refactor: resolve item textures in Hugo instead of sync scripts
The crafting shortcode previously depended on scripts/sync-items.py to pre-populate assets/items/ before every build: the script regex-scanned content/ for shortcode usages, listed the sfcraft repo via the GitHub API, downloaded missing vanilla textures from the wiki, and had to be driven through scripts/build.py, scripts/dev.py or a Makefile so that plain `hugo` never ran on its own. Resolution now happens inside the render pipeline, so `hugo` and `hugo server` work directly and nothing has to be kept in sync: - layouts/_partials/sfcraft/item-texture.html resolves an item id to an image Resource, trying assets/items/<id>.png, then the sfcraft repo texture, then the Minecraft wiki. - layouts/_partials/sfcraft/wiki-lookup.html derives the wiki English name from the id, lists candidates via the allimages API and picks the newest JE/BE render, replacing the script's name-variant logic. - Callers go through partialCached keyed on the item id, so each id is resolved once per build no matter how many slots reference it. Because usages are discovered by rendering, the content scanner is gone and the two syntaxes can no longer drift apart from what the scanner understood. Enumerating the sfcraft repo is also unnecessary: a texture is fetched by its raw URL and a 404 simply means "not a custom item". Caching is Hugo's getresource file cache, pinned to maxAge -1, so every URL is downloaded once globally, later builds hit the cache, and offline builds succeed. `hugo --ignoreCache` refreshes upstream changes, which replaces the script's per-build file-size comparison. Error reporting distinguishes cases the script could not tell apart. A 404 from every source means the id is wrong and fails the build (configurable via params.itemTextures.onMissing), while a transport error, rate limit or 5xx only warns and falls back to the `?` placeholder, so a missing network no longer looks like a typo. Also in this change: - wiki name special cases move from a dict in the script to data/sfcraft/wiki_aliases.yaml - textures publish to /sfc/items/<id>.png regardless of source, so switching an item to a hand-placed texture keeps its URL - component CSS moves to assets/css/sfcraft-crafting.css, minified and inlined once per page, instead of a heredoc inside the shortcode - a `.` used as an alias value now means "empty slot", matching what it already meant inside pattern; it previously resolved as an item id and reported a missing texture - assets/items/*.png is no longer gitignored, since that directory now only holds intentional overrides that should be committed Verified against Hugo 0.164.0: custom items resolve from the repo, vanilla items from the wiki, TNT / Flint_and_Steel / Dragon's_Breath exercise the alias and connector rules, hand-placed textures win over both, a typo fails the build, and a cold cache with no network degrades to placeholders while a warm cache builds fully offline. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
.sfc-crafting {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 540px;
|
||||
aspect-ratio: 320 / 160;
|
||||
margin: 1rem 0 1.25rem;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
container-type: inline-size;
|
||||
}
|
||||
|
||||
.sfc-slot {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.sfc-grid {
|
||||
width: 10.625%;
|
||||
height: 21.25%;
|
||||
}
|
||||
|
||||
.sfc-out {
|
||||
width: 15.625%;
|
||||
height: 31.25%;
|
||||
}
|
||||
|
||||
.sfc-item {
|
||||
width: 88%;
|
||||
height: 88%;
|
||||
object-fit: contain;
|
||||
image-rendering: pixelated;
|
||||
filter: drop-shadow(0 2px 2px rgba(0, 0, 0, 0.35));
|
||||
}
|
||||
|
||||
.sfc-count {
|
||||
position: absolute;
|
||||
right: 5%;
|
||||
bottom: 3%;
|
||||
font-family: "Minecraft", ui-monospace, Consolas, monospace;
|
||||
font-size: 12px;
|
||||
font-size: 2.8cqw;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
color: #fff;
|
||||
text-shadow:
|
||||
0 1px 0 #3f3f3f,
|
||||
1px 0 0 #3f3f3f,
|
||||
0 -1px 0 #3f3f3f,
|
||||
-1px 0 0 #3f3f3f,
|
||||
0 2px 3px rgba(0, 0, 0, 0.55);
|
||||
}
|
||||
|
||||
/* 贴图缺失时的占位符 */
|
||||
.sfc-missing {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 86%;
|
||||
height: 86%;
|
||||
font-size: 4cqw;
|
||||
font-weight: 700;
|
||||
color: rgba(0, 0, 0, 0.28);
|
||||
border: 2px dashed rgba(0, 0, 0, 0.18);
|
||||
border-radius: 4px;
|
||||
background: repeating-linear-gradient(
|
||||
45deg,
|
||||
rgba(255, 255, 255, 0.22),
|
||||
rgba(255, 255, 255, 0.22) 4px,
|
||||
rgba(0, 0, 0, 0.04) 4px,
|
||||
rgba(0, 0, 0, 0.04) 8px
|
||||
);
|
||||
}
|
||||
|
||||
/* 悬停显示物品 id */
|
||||
.sfc-slot[data-name]::after {
|
||||
content: attr(data-name);
|
||||
position: absolute;
|
||||
bottom: calc(100% + 6px);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
padding: 3px 9px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.16);
|
||||
border-radius: 5px;
|
||||
background: rgba(18, 18, 18, 0.92);
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.35);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.12s ease;
|
||||
z-index: 30;
|
||||
}
|
||||
|
||||
.sfc-slot[data-name]:hover::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.sfc-caption {
|
||||
margin: 0.35rem 0 0;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
color: var(--gray-600, #6c757d);
|
||||
}
|
||||
|
||||
.sfc-error {
|
||||
max-width: 540px;
|
||||
margin: 1rem 0;
|
||||
padding: 0.6rem 1rem;
|
||||
border: 1px solid #f5c2c7;
|
||||
border-radius: 6px;
|
||||
background: #f8d7da;
|
||||
color: #842029;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
+59
-18
@@ -1,26 +1,68 @@
|
||||
# 物品贴图库
|
||||
# 物品贴图
|
||||
|
||||
本目录存放 `crafting` 合成组件使用的物品贴图(PNG)。
|
||||
`crafting` 合成组件里的物品贴图**不需要手工准备**:构建时由 Hugo 自己解析、下载并缓存。
|
||||
写 `a1="diamond"` 就会显示钻石,不用先把 `diamond.png` 放进仓库,也不用跑任何同步脚本。
|
||||
|
||||
## 命名规则
|
||||
直接 `hugo` / `hugo server` 即可。
|
||||
|
||||
一个物品对应一个文件:`<名称>.png`,文件名(不含 `.png`)就是引用时的名字。
|
||||
## 解析顺序
|
||||
|
||||
例如放入 `diamond.png` 后,在合成组件中写 `a1="diamond"` 即可显示钻石。
|
||||
每个物品 id 依次尝试,命中即止(实现见 `layouts/_partials/sfcraft/item-texture.html`):
|
||||
|
||||
> 推荐使用 16×16(或 32×32)的 Minecraft 像素风贴图,`image-rendering: pixelated` 会保证缩放后依然清晰锐利。
|
||||
1. **本目录** `assets/items/<id>.png` —— 手动放入的贴图,优先级最高;
|
||||
2. **sfcraft 仓库** `textures/item/<id>.png` —— 自定义物品,版本由 `hugo.yaml`
|
||||
中 `params.itemTextures.repo.baseURL` 的 ref 决定;
|
||||
3. **Minecraft wiki** —— 原版物品,按 `<英文名>_JE<版本>[_BE<版本>].png` 的命名
|
||||
列出候选并自动挑选最新版本渲染。
|
||||
|
||||
## 自动同步(推荐)
|
||||
无论来自哪一层,贴图最终都发布到 `/sfc/items/<id>.png`,
|
||||
所以把某个物品从 wiki 换成手动贴图不会改变页面里的 URL。
|
||||
|
||||
构建 / 开发时由 `scripts/sync-items.py` 自动维护本目录:
|
||||
## 缓存
|
||||
|
||||
- sfcraft 自定义贴图(`amethyst_cauldron_blank`、`exp_totem`、`lunch_box`、`pearl_token` 等)
|
||||
从 [sfcraft 仓库](https://github.com/saltedfishclub/sfcraft/tree/rev/26.2/src/main/resources/assets/sfcraft/textures/item)
|
||||
自动下载,缺失或远端变更时更新;
|
||||
- 原版物品(`diamond`、`stick` 等)在本地缺失时从 Minecraft wiki 自动获取;
|
||||
- 手动放入的贴图不会被覆盖;找不到的贴图会报错并列出物品 id,阻止构建 / 开发。
|
||||
抓取结果写进 Hugo 的 `getresource` 文件缓存,`hugo.yaml` 里设为永不过期,
|
||||
因此每个 URL 全局只下载一次:
|
||||
|
||||
请使用 `python scripts\build.py` / `python scripts\dev.py` 而非直接运行 `hugo`,详见 `scripts/README.md`。
|
||||
- 首次构建需要联网(本仓库当前的配方约需数秒);
|
||||
- 之后的构建全部命中缓存,**离线也能完整构建**;
|
||||
- 需要拉取上游更新(sfcraft 换了贴图、wiki 出了新版本渲染)时执行 `hugo --ignoreCache`。
|
||||
|
||||
缓存默认在 `hugo config | grep cachedir` 指向的目录。CI 里建议把它固定下来并缓存该目录:
|
||||
|
||||
```bash
|
||||
hugo --cacheDir "$PWD/.hugo-cache"
|
||||
```
|
||||
|
||||
这样只有第一次构建需要访问 `raw.githubusercontent.com` 与 `zh.minecraft.wiki`。
|
||||
若 CI 完全不允许联网,把需要的贴图提交到本目录即可(第 1 层优先级最高)。
|
||||
|
||||
## 手动放入贴图
|
||||
|
||||
需要覆盖上游贴图,或某个物品自动解析不到时,把文件放到 `assets/items/<id>.png`,
|
||||
文件名(不含 `.png`)就是引用时的 id。这些文件会提交进仓库。
|
||||
|
||||
> 推荐 16×16 或 32×32 的像素风贴图,`image-rendering: pixelated` 会保证放大后依然锐利。
|
||||
|
||||
## 解析不到怎么办
|
||||
|
||||
构建会报错并指出页面与物品 id:
|
||||
|
||||
```
|
||||
ERROR /vanilla/items/xxx 引用的物品 "diamnod" 没有对应贴图。请检查 id 拼写, 或把贴图放进 assets/items/diamnod.png
|
||||
```
|
||||
|
||||
常见原因与处理:
|
||||
|
||||
- **id 拼错** —— 改正 id;
|
||||
- **wiki 上的英文名不符合自动推导规则**(如 `TNT`、`Dragon's_Breath`)
|
||||
—— 在 `data/sfcraft/wiki_aliases.yaml` 里补一条 id → 英文名的映射;
|
||||
- **确实没有现成贴图** —— 手动放进 `assets/items/`。
|
||||
|
||||
网络故障、被限流或上游异常时不会被当成「id 写错」:这类情况只告警并显示 `?` 占位符,
|
||||
不阻断构建,方便离线时继续写文档。
|
||||
|
||||
想让缺失贴图也不阻断构建,可把 `hugo.yaml` 中 `params.itemTextures.onMissing`
|
||||
从 `error` 改成 `warn` 或 `ignore`。
|
||||
|
||||
## 组件用法
|
||||
|
||||
@@ -35,10 +77,9 @@
|
||||
```
|
||||
|
||||
- `a1`~`c3`:3×3 合成格的九个格子,`out`:输出格;
|
||||
- 值 = 贴图文件名(不含 `.png`),**留空 / 省略 = 空格子**;
|
||||
- 值 = 物品 id,**留空 / 省略 / `.` = 空格子**;
|
||||
- 需要显示堆叠数量时写 `名称:数量`,例如 `b2="stick:2"`;
|
||||
- `caption` 可选,显示在组件下方居中说明文字;
|
||||
- 未运行自动同步且贴图缺失时,格子内会显示一个 `?` 占位,方便发现写错的名字。
|
||||
- `caption` 可选,显示在组件下方居中说明文字。
|
||||
|
||||
### pattern 别名模式(另一种写法)
|
||||
|
||||
@@ -58,5 +99,5 @@
|
||||
- `pattern` 共 9 个 token(3 行 × 3 列),**必须写在一行内**,行间用 `\n` 表示;
|
||||
- 每个 token 对应一个同名参数(`a`、`b`、`c`…),参数值就是物品 id;
|
||||
- token 也可以直接写物品 id:`pattern="diamond stick ."`;
|
||||
- `.` 表示空格子;
|
||||
- `.` 表示空格子(写在 pattern 里,或作为别名的值,都算空格子);
|
||||
- 同时给出 `pattern` 和 `a1`~`c3` 时,以 `pattern` 为准(`out` 两者通用)。
|
||||
|
||||
Reference in New Issue
Block a user