refactor: unify build scripts to Python 3
This commit is contained in:
@@ -3,3 +3,4 @@ themes/hugo-book/**
|
|||||||
resources/_gen
|
resources/_gen
|
||||||
*.log
|
*.log
|
||||||
assets/items/*.png
|
assets/items/*.png
|
||||||
|
__pycache__/
|
||||||
|
|||||||
@@ -1,23 +1,18 @@
|
|||||||
# SFCraft 文档 · 构建 / 开发入口 (跨平台)
|
# SFCraft 文档 · 构建 / 开发入口 (跨平台, 脚本为 Python 3)
|
||||||
#
|
#
|
||||||
# Linux / macOS: make build / make dev / make sync
|
# make build / make dev / make sync / make help
|
||||||
# Windows(有 make 时): 同样可用, 自动调用 PowerShell 脚本
|
|
||||||
#
|
#
|
||||||
# 可附加 hugo 参数: make dev HUGO_ARGS="--port 1314"
|
# 可附加 hugo 参数: make dev HUGO_ARGS="--port 1314"
|
||||||
|
# 可指定解释器: make PY=python3 build
|
||||||
|
|
||||||
.PHONY: help sync build dev
|
.PHONY: help sync build dev
|
||||||
|
|
||||||
HUGO_ARGS ?=
|
HUGO_ARGS ?=
|
||||||
|
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
PS := powershell -NoProfile -ExecutionPolicy Bypass -File
|
PY ?= python
|
||||||
BUILD_CMD := $(PS) scripts/build.ps1
|
|
||||||
DEV_CMD := $(PS) scripts/dev.ps1
|
|
||||||
SYNC_CMD := $(PS) scripts/sync-items.ps1
|
|
||||||
else
|
else
|
||||||
BUILD_CMD := ./scripts/build.sh
|
PY ?= python3
|
||||||
DEV_CMD := ./scripts/dev.sh
|
|
||||||
SYNC_CMD := ./scripts/sync-items.sh
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
help:
|
help:
|
||||||
@@ -30,10 +25,10 @@ help:
|
|||||||
@echo 可附加 hugo 参数: make dev HUGO_ARGS="--port 1314"
|
@echo 可附加 hugo 参数: make dev HUGO_ARGS="--port 1314"
|
||||||
|
|
||||||
sync:
|
sync:
|
||||||
$(SYNC_CMD)
|
$(PY) scripts/sync-items.py
|
||||||
|
|
||||||
build:
|
build:
|
||||||
$(BUILD_CMD) $(HUGO_ARGS)
|
$(PY) scripts/build.py $(HUGO_ARGS)
|
||||||
|
|
||||||
dev:
|
dev:
|
||||||
$(DEV_CMD) $(HUGO_ARGS)
|
$(PY) scripts/dev.py $(HUGO_ARGS)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
## 自动同步(推荐)
|
## 自动同步(推荐)
|
||||||
|
|
||||||
构建 / 开发时由 `scripts/sync-items.ps1` 自动维护本目录:
|
构建 / 开发时由 `scripts/sync-items.py` 自动维护本目录:
|
||||||
|
|
||||||
- sfcraft 自定义贴图(`amethyst_cauldron_blank`、`exp_totem`、`lunch_box`、`pearl_token` 等)
|
- 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)
|
从 [sfcraft 仓库](https://github.com/saltedfishclub/sfcraft/tree/rev/26.2/src/main/resources/assets/sfcraft/textures/item)
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
- 原版物品(`diamond`、`stick` 等)在本地缺失时从 Minecraft wiki 自动获取;
|
- 原版物品(`diamond`、`stick` 等)在本地缺失时从 Minecraft wiki 自动获取;
|
||||||
- 手动放入的贴图不会被覆盖;找不到的贴图会报错并列出物品 id,阻止构建 / 开发。
|
- 手动放入的贴图不会被覆盖;找不到的贴图会报错并列出物品 id,阻止构建 / 开发。
|
||||||
|
|
||||||
请使用 `scripts\build.ps1` / `scripts\dev.ps1` 而非直接运行 `hugo`,详见 `scripts/README.md`。
|
请使用 `python scripts\build.py` / `python scripts\dev.py` 而非直接运行 `hugo`,详见 `scripts/README.md`。
|
||||||
|
|
||||||
## 组件用法
|
## 组件用法
|
||||||
|
|
||||||
|
|||||||
+22
-22
@@ -2,50 +2,50 @@
|
|||||||
|
|
||||||
## 用途
|
## 用途
|
||||||
|
|
||||||
`sync-items.ps1` 会在构建和开发前自动保证物品贴图齐全:
|
`scripts/` 下全部为 Python 3 脚本(无其他语言):
|
||||||
|
|
||||||
|
- `sync-items.py` —— 物品贴图自动同步(核心);
|
||||||
|
- `build.py` —— 同步贴图 + hugo 构建;
|
||||||
|
- `dev.py` —— 同步贴图 + hugo 开发服务器。
|
||||||
|
|
||||||
|
同步流程:
|
||||||
|
|
||||||
1. 下载 [sfcraft 仓库](https://github.com/saltedfishclub/sfcraft/tree/rev/26.2/src/main/resources/assets/sfcraft/textures/item) 的
|
1. 下载 [sfcraft 仓库](https://github.com/saltedfishclub/sfcraft/tree/rev/26.2/src/main/resources/assets/sfcraft/textures/item) 的
|
||||||
全部自定义贴图到 `assets/items/`;
|
全部自定义贴图到 `assets/items/`;
|
||||||
2. 扫描 `content/` 中所有 `crafting` 组件用到的物品 id;
|
2. 扫描 `content/` 中所有 `crafting` 组件用到的物品 id(支持九格直填与 pattern 别名两种写法);
|
||||||
3. 本地缺失的原版物品从 Minecraft wiki 自动获取(自动挑选最新版本渲染);
|
3. 本地缺失的原版物品从 Minecraft wiki 自动获取(自动挑选最新版本渲染);
|
||||||
4. 有任何物品无法找到时, 打印缺失清单并以非零码退出, 阻断构建 / 开发启动。
|
4. 有任何物品无法找到时, 打印缺失清单并以非零码退出, 阻断构建 / 开发启动。
|
||||||
|
|
||||||
## 使用
|
## 使用
|
||||||
|
|
||||||
```powershell
|
Windows:
|
||||||
.\scripts\build.ps1 # 同步贴图 + hugo 构建
|
|
||||||
.\scripts\dev.ps1 # 同步贴图 + hugo server
|
|
||||||
.\scripts\dev.ps1 -NoSync # 仅启动开发服务器, 不重新同步
|
|
||||||
```
|
|
||||||
|
|
||||||
若 PowerShell 执行策略阻止脚本, 请用:
|
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
powershell -ExecutionPolicy Bypass -File scripts\dev.ps1
|
python scripts\build.py # 同步贴图 + hugo 构建
|
||||||
|
python scripts\dev.py # 同步贴图 + hugo server
|
||||||
|
python scripts\dev.py --no-sync # 仅启动开发服务器, 不重新同步
|
||||||
|
python scripts\sync-items.py -f # 仅同步贴图(强制刷新 sfcraft 贴图)
|
||||||
```
|
```
|
||||||
|
|
||||||
脚本通过 `$env:HUGO` 或 PATH 查找 hugo, 找不到时也会探测 winget 安装位置。
|
Linux / macOS:
|
||||||
|
|
||||||
## Linux / macOS 使用
|
|
||||||
|
|
||||||
Unix 环境下使用同名的 bash 脚本(同步逻辑为 Python 3 实现, 需要 `python3` 与 `hugo`):
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./scripts/build.sh # 同步贴图 + hugo 构建
|
python3 scripts/build.py
|
||||||
./scripts/dev.sh # 同步贴图 + hugo server
|
python3 scripts/dev.py
|
||||||
./scripts/dev.sh -NoSync # 仅启动开发服务器
|
python3 scripts/dev.py --no-sync
|
||||||
```
|
```
|
||||||
|
|
||||||
也可以使用 Makefile(Windows / Unix 通用, 自动选择对应脚本):
|
也可以用 Makefile(Windows / Unix 通用):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make build # 同步贴图 + hugo 构建
|
make build # 同步贴图 + hugo 构建
|
||||||
make dev # 同步贴图 + hugo server
|
make dev # 同步贴图 + hugo server
|
||||||
make sync # 仅同步贴图
|
make sync # 仅同步贴图
|
||||||
make dev HUGO_ARGS="--port 1314"
|
make dev HUGO_ARGS="--port 1314"
|
||||||
|
make PY=python build # 指定 Python 解释器
|
||||||
```
|
```
|
||||||
|
|
||||||
首次使用前请确认脚本有执行权限: `chmod +x scripts/*.sh`。
|
脚本按 `$HUGO` 环境变量、PATH、winget 安装目录的顺序查找 hugo。
|
||||||
|
|
||||||
## 组件写法
|
## 组件写法
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ make dev HUGO_ARGS="--port 1314"
|
|||||||
- 原版贴图: 只在本地缺失时下载, **不会覆盖**手动放入 `assets/items/` 的贴图;
|
- 原版贴图: 只在本地缺失时下载, **不会覆盖**手动放入 `assets/items/` 的贴图;
|
||||||
- 物品 id 即 `assets/items/` 下的文件名(不含 `.png`);
|
- 物品 id 即 `assets/items/` 下的文件名(不含 `.png`);
|
||||||
- wiki 命名规则: `<英文名>_JE<版本>[_BE<版本>].png`, 特殊命名(如 `TNT`、`Flint_and_Steel`)
|
- wiki 命名规则: `<英文名>_JE<版本>[_BE<版本>].png`, 特殊命名(如 `TNT`、`Flint_and_Steel`)
|
||||||
由 `sync-items.ps1` / `sync-items.py` 中的变体生成与别名表处理。
|
由 `sync-items.py` 中的变体生成与别名表处理。
|
||||||
|
|
||||||
## 网络要求
|
## 网络要求
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
<#
|
|
||||||
SFCraft 文档构建入口: 先同步物品贴图, 再执行 hugo 构建。
|
|
||||||
贴图同步失败(有物品找不到)时终止构建。
|
|
||||||
用法: powershell -ExecutionPolicy Bypass -File scripts\build.ps1 [hugo 参数...]
|
|
||||||
#>
|
|
||||||
[CmdletBinding()]
|
|
||||||
param(
|
|
||||||
[Parameter(ValueFromRemainingArguments = $true)]
|
|
||||||
[string[]]$HugoArgs
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = 'Stop'
|
|
||||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
||||||
|
|
||||||
function Get-HugoPath {
|
|
||||||
if ($env:HUGO) { return $env:HUGO }
|
|
||||||
$cmd = Get-Command hugo -ErrorAction SilentlyContinue
|
|
||||||
if ($cmd) { return $cmd.Source }
|
|
||||||
$p = Get-ChildItem -Path (Join-Path $env:LOCALAPPDATA 'Microsoft\WinGet\Packages') -Recurse -Filter hugo.exe -ErrorAction SilentlyContinue |
|
|
||||||
Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
|
||||||
if ($p) { return $p.FullName }
|
|
||||||
throw '未找到 Hugo。请安装 Hugo, 或将 hugo 可执行文件路径写入环境变量 HUGO。'
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "`n[1/2] 同步物品贴图" -ForegroundColor Cyan
|
|
||||||
& (Join-Path $scriptDir 'sync-items.ps1')
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
Write-Host "[构建终止] 物品贴图同步失败, 未执行 hugo 构建。" -ForegroundColor Red
|
|
||||||
exit $LASTEXITCODE
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "`n[2/2] hugo 构建" -ForegroundColor Cyan
|
|
||||||
$hugo = Get-HugoPath
|
|
||||||
& $hugo @HugoArgs
|
|
||||||
exit $LASTEXITCODE
|
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""SFCraft 文档构建入口: 先同步物品贴图, 再执行 hugo 构建。
|
||||||
|
|
||||||
|
贴图同步失败(有物品找不到)时终止构建。
|
||||||
|
|
||||||
|
用法:
|
||||||
|
python scripts/build.py [hugo 参数...]
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
SCRIPTS = Path(__file__).resolve().parent
|
||||||
|
|
||||||
|
|
||||||
|
def find_hugo():
|
||||||
|
"""按 HUGO 环境变量、PATH、winget 安装目录依次查找 hugo。"""
|
||||||
|
env = os.environ.get("HUGO")
|
||||||
|
if env and Path(env).is_file():
|
||||||
|
return env
|
||||||
|
|
||||||
|
name = "hugo.exe" if os.name == "nt" else "hugo"
|
||||||
|
for d in os.environ.get("PATH", "").split(os.pathsep):
|
||||||
|
if d:
|
||||||
|
cand = Path(d) / name
|
||||||
|
if cand.is_file():
|
||||||
|
return str(cand)
|
||||||
|
|
||||||
|
local = os.environ.get("LOCALAPPDATA")
|
||||||
|
if local:
|
||||||
|
base = Path(local) / "Microsoft" / "WinGet" / "Packages"
|
||||||
|
if base.is_dir():
|
||||||
|
for p in sorted(base.glob("*/hugo.exe")):
|
||||||
|
return str(p)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("\n[1/2] 同步物品贴图")
|
||||||
|
r = subprocess.run([sys.executable, str(SCRIPTS / "sync-items.py")])
|
||||||
|
if r.returncode != 0:
|
||||||
|
print("[构建终止] 物品贴图同步失败, 未执行 hugo 构建。", file=sys.stderr)
|
||||||
|
sys.exit(r.returncode)
|
||||||
|
|
||||||
|
hugo = find_hugo()
|
||||||
|
if not hugo:
|
||||||
|
print(
|
||||||
|
"[错误] 未找到 hugo, 请安装 Hugo、将其加入 PATH, 或设置环境变量 HUGO。",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print("\n[2/2] hugo 构建")
|
||||||
|
sys.exit(subprocess.run([hugo, *sys.argv[1:]]).returncode)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# SFCraft 文档构建入口 (Linux / macOS / Unix): 先同步贴图, 再执行 hugo 构建。
|
|
||||||
# 贴图同步失败(有物品找不到)时终止构建。
|
|
||||||
# 用法: ./scripts/build.sh [hugo 参数...]
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "[1/2] 同步物品贴图"
|
|
||||||
"$DIR/sync-items.sh"
|
|
||||||
|
|
||||||
if ! command -v hugo >/dev/null 2>&1; then
|
|
||||||
HUGO_BIN=""
|
|
||||||
if [ -n "${HUGO:-}" ] && [ -x "$HUGO" ]; then
|
|
||||||
HUGO_BIN="$HUGO"
|
|
||||||
elif [ -n "${LOCALAPPDATA:-}" ]; then
|
|
||||||
HUGO_BIN="$(find "$LOCALAPPDATA/Microsoft/WinGet/Packages" -maxdepth 3 -name hugo.exe 2>/dev/null | head -n 1)"
|
|
||||||
fi
|
|
||||||
if [ -z "$HUGO_BIN" ]; then
|
|
||||||
echo "[错误] 未找到 hugo, 请安装 Hugo、将其加入 PATH, 或设置环境变量 HUGO。" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
HUGO_BIN="$(command -v hugo)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "[2/2] hugo 构建"
|
|
||||||
exec "$HUGO_BIN" "$@"
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
<#
|
|
||||||
SFCraft 文档开发服务器入口: 先同步物品贴图, 再启动 hugo server。
|
|
||||||
贴图同步失败(有物品找不到)时终止启动。
|
|
||||||
用法: powershell -ExecutionPolicy Bypass -File scripts\dev.ps1 [-NoSync] [hugo server 参数...]
|
|
||||||
#>
|
|
||||||
[CmdletBinding()]
|
|
||||||
param(
|
|
||||||
[switch]$NoSync,
|
|
||||||
[Parameter(ValueFromRemainingArguments = $true)]
|
|
||||||
[string[]]$HugoArgs
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = 'Stop'
|
|
||||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
||||||
|
|
||||||
function Get-HugoPath {
|
|
||||||
if ($env:HUGO) { return $env:HUGO }
|
|
||||||
$cmd = Get-Command hugo -ErrorAction SilentlyContinue
|
|
||||||
if ($cmd) { return $cmd.Source }
|
|
||||||
$p = Get-ChildItem -Path (Join-Path $env:LOCALAPPDATA 'Microsoft\WinGet\Packages') -Recurse -Filter hugo.exe -ErrorAction SilentlyContinue |
|
|
||||||
Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
|
||||||
if ($p) { return $p.FullName }
|
|
||||||
throw '未找到 Hugo。请安装 Hugo, 或将 hugo 可执行文件路径写入环境变量 HUGO。'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not $NoSync) {
|
|
||||||
Write-Host "`n[1/2] 同步物品贴图" -ForegroundColor Cyan
|
|
||||||
& (Join-Path $scriptDir 'sync-items.ps1')
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
Write-Host "[启动终止] 物品贴图同步失败, 未启动 hugo server。" -ForegroundColor Red
|
|
||||||
exit $LASTEXITCODE
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Write-Host "[跳过] 已指定 -NoSync, 不执行贴图同步" -ForegroundColor Yellow
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "`n[2/2] 启动 hugo server" -ForegroundColor Cyan
|
|
||||||
$hugo = Get-HugoPath
|
|
||||||
$serverArgs = @('server')
|
|
||||||
if ($HugoArgs.Count -gt 0) { $serverArgs += $HugoArgs }
|
|
||||||
& $hugo @serverArgs
|
|
||||||
exit $LASTEXITCODE
|
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""SFCraft 文档开发服务器入口: 先同步物品贴图, 再启动 hugo server。
|
||||||
|
|
||||||
|
贴图同步失败(有物品找不到)时终止启动。
|
||||||
|
|
||||||
|
用法:
|
||||||
|
python scripts/dev.py [--no-sync] [hugo server 参数...]
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
SCRIPTS = Path(__file__).resolve().parent
|
||||||
|
|
||||||
|
|
||||||
|
def find_hugo():
|
||||||
|
"""按 HUGO 环境变量、PATH、winget 安装目录依次查找 hugo。"""
|
||||||
|
env = os.environ.get("HUGO")
|
||||||
|
if env and Path(env).is_file():
|
||||||
|
return env
|
||||||
|
|
||||||
|
name = "hugo.exe" if os.name == "nt" else "hugo"
|
||||||
|
for d in os.environ.get("PATH", "").split(os.pathsep):
|
||||||
|
if d:
|
||||||
|
cand = Path(d) / name
|
||||||
|
if cand.is_file():
|
||||||
|
return str(cand)
|
||||||
|
|
||||||
|
local = os.environ.get("LOCALAPPDATA")
|
||||||
|
if local:
|
||||||
|
base = Path(local) / "Microsoft" / "WinGet" / "Packages"
|
||||||
|
if base.is_dir():
|
||||||
|
for p in sorted(base.glob("*/hugo.exe")):
|
||||||
|
return str(p)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="SFCraft 开发服务器")
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-sync", "-NoSync", action="store_true",
|
||||||
|
help="跳过贴图同步",
|
||||||
|
)
|
||||||
|
args, rest = parser.parse_known_args()
|
||||||
|
|
||||||
|
if not args.no_sync:
|
||||||
|
print("\n[1/2] 同步物品贴图")
|
||||||
|
r = subprocess.run([sys.executable, str(SCRIPTS / "sync-items.py")])
|
||||||
|
if r.returncode != 0:
|
||||||
|
print("[启动终止] 物品贴图同步失败, 未启动 hugo server。", file=sys.stderr)
|
||||||
|
sys.exit(r.returncode)
|
||||||
|
else:
|
||||||
|
print("[跳过] 已指定 --no-sync, 不执行贴图同步")
|
||||||
|
|
||||||
|
hugo = find_hugo()
|
||||||
|
if not hugo:
|
||||||
|
print(
|
||||||
|
"[错误] 未找到 hugo, 请安装 Hugo、将其加入 PATH, 或设置环境变量 HUGO。",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print("\n[2/2] 启动 hugo server")
|
||||||
|
sys.exit(subprocess.run([hugo, "server", *rest]).returncode)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# SFCraft 文档开发服务器入口 (Linux / macOS / Unix): 先同步贴图, 再启动 hugo server。
|
|
||||||
# 贴图同步失败(有物品找不到)时终止启动。
|
|
||||||
# 用法: ./scripts/dev.sh [-NoSync] [hugo server 参数...]
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
|
|
||||||
NOSYNC=0
|
|
||||||
HUGO_ARGS=()
|
|
||||||
for arg in "$@"; do
|
|
||||||
case "$arg" in
|
|
||||||
-NoSync|--no-sync|-nosync)
|
|
||||||
NOSYNC=1
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
HUGO_ARGS+=("$arg")
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$NOSYNC" -eq 0 ]; then
|
|
||||||
echo
|
|
||||||
echo "[1/2] 同步物品贴图"
|
|
||||||
"$DIR/sync-items.sh"
|
|
||||||
else
|
|
||||||
echo "[跳过] 已指定 -NoSync, 不执行贴图同步"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v hugo >/dev/null 2>&1; then
|
|
||||||
HUGO_BIN=""
|
|
||||||
if [ -n "${HUGO:-}" ] && [ -x "$HUGO" ]; then
|
|
||||||
HUGO_BIN="$HUGO"
|
|
||||||
elif [ -n "${LOCALAPPDATA:-}" ]; then
|
|
||||||
HUGO_BIN="$(find "$LOCALAPPDATA/Microsoft/WinGet/Packages" -maxdepth 3 -name hugo.exe 2>/dev/null | head -n 1)"
|
|
||||||
fi
|
|
||||||
if [ -z "$HUGO_BIN" ]; then
|
|
||||||
echo "[错误] 未找到 hugo, 请安装 Hugo、将其加入 PATH, 或设置环境变量 HUGO。" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
HUGO_BIN="$(command -v hugo)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "[2/2] 启动 hugo server"
|
|
||||||
exec "$HUGO_BIN" server "${HUGO_ARGS[@]}"
|
|
||||||
@@ -1,248 +0,0 @@
|
|||||||
<#
|
|
||||||
SFCraft 文档 · 物品贴图自动同步
|
|
||||||
|
|
||||||
流程:
|
|
||||||
1. 将 sfcraft 仓库 textures/item 目录下所有贴图同步到 assets/items
|
|
||||||
2. 扫描 content 中所有 crafting 组件, 收集用到的物品 id
|
|
||||||
3. 对本地缺失的 id, 从 Minecraft wiki 自动获取(按命名规则挑选最新版本)
|
|
||||||
4. 仍有无法获取的 id 时, 列出并退出(返回码 1), 阻断 build / dev
|
|
||||||
|
|
||||||
用法:
|
|
||||||
powershell -ExecutionPolicy Bypass -File scripts\sync-items.ps1 [-Force]
|
|
||||||
|
|
||||||
说明:
|
|
||||||
- sfcraft 自定义贴图每次与远端比对大小, 缺失或变更时更新
|
|
||||||
- 原版贴图只在本地缺失时下载, 不会覆盖手动放入 assets/items 的贴图
|
|
||||||
- 需要联网访问 api.github.com / zh.minecraft.wiki
|
|
||||||
#>
|
|
||||||
[CmdletBinding()]
|
|
||||||
param(
|
|
||||||
[switch]$Force
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = 'Stop'
|
|
||||||
|
|
||||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
|
||||||
$itemsDir = Join-Path $repoRoot 'assets\items'
|
|
||||||
$contentDir = Join-Path $repoRoot 'content'
|
|
||||||
$ua = 'sfcraft-docs-sync/1.0 (https://github.com/saltedfishclub/sfcraft-docs)'
|
|
||||||
|
|
||||||
New-Item -ItemType Directory -Path $itemsDir -Force | Out-Null
|
|
||||||
|
|
||||||
# ---------- 数据源 ----------
|
|
||||||
$sfOwner = 'saltedfishclub'
|
|
||||||
$sfRepo = 'sfcraft'
|
|
||||||
$sfRef = 'rev/26.2'
|
|
||||||
$sfDir = 'src/main/resources/assets/sfcraft/textures/item'
|
|
||||||
$sfListUrl = "https://api.github.com/repos/$sfOwner/$sfRepo/contents/$sfDir`?ref=$sfRef"
|
|
||||||
$wikiApi = 'https://zh.minecraft.wiki/api.php'
|
|
||||||
$wikiImg = 'https://zh.minecraft.wiki/images/'
|
|
||||||
|
|
||||||
function Write-Step([string]$s) { Write-Host "`n== $s" -ForegroundColor Cyan }
|
|
||||||
|
|
||||||
function Invoke-Get([string]$url, [int]$maxRetry = 2) {
|
|
||||||
for ($i = 0; ; $i++) {
|
|
||||||
try {
|
|
||||||
return Invoke-RestMethod -Uri $url -Headers @{ 'User-Agent' = $ua } -TimeoutSec 30
|
|
||||||
} catch {
|
|
||||||
if ($i -ge $maxRetry) { throw }
|
|
||||||
Start-Sleep -Milliseconds 800
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------- 1. sfcraft 自定义贴图 ----------
|
|
||||||
Write-Step '1/4 同步 sfcraft 自定义贴图'
|
|
||||||
$sfCount = 0
|
|
||||||
$sfNew = 0
|
|
||||||
try {
|
|
||||||
$list = Invoke-Get $sfListUrl
|
|
||||||
foreach ($f in $list) {
|
|
||||||
$target = Join-Path $itemsDir $f.name
|
|
||||||
$need = $Force -or
|
|
||||||
-not (Test-Path -LiteralPath $target) -or
|
|
||||||
((Get-Item -LiteralPath $target).Length -ne [int64]$f.size)
|
|
||||||
if ($need) {
|
|
||||||
Invoke-WebRequest -Uri $f.download_url -Headers @{ 'User-Agent' = $ua } -OutFile $target -TimeoutSec 60
|
|
||||||
$sfNew++
|
|
||||||
}
|
|
||||||
$sfCount++
|
|
||||||
}
|
|
||||||
Write-Host " $sfCount 个文件, 本次更新 $sfNew 个"
|
|
||||||
} catch {
|
|
||||||
Write-Host " [警告] 无法访问 GitHub, 跳过 sfcraft 贴图同步: $($_.Exception.Message)" -ForegroundColor Yellow
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------- 2. 扫描 crafting 组件 ----------
|
|
||||||
Write-Step '2/4 扫描组件物品 id'
|
|
||||||
$ids = @{}
|
|
||||||
$shortcodePat = [regex]'(?s){{<[ ]*crafting\b(.*?)}}'
|
|
||||||
$pairPat = [regex]'(\w+)="([^"]*)"'
|
|
||||||
$slotKeys = @('a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3', 'out')
|
|
||||||
|
|
||||||
function Add-ItemId([string]$raw) {
|
|
||||||
$v = $raw.Trim()
|
|
||||||
if ($v.Length -gt 0) {
|
|
||||||
$id = ($v -split ':')[0].Trim()
|
|
||||||
if ($id.Length -gt 0) { $ids[$id] = $true }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Get-ChildItem -Path $contentDir -Recurse -Filter '*.md' -File | ForEach-Object {
|
|
||||||
$text = Get-Content -LiteralPath $_.FullName -Raw -Encoding UTF8
|
|
||||||
foreach ($m in $shortcodePat.Matches($text)) {
|
|
||||||
$params = @{}
|
|
||||||
foreach ($p in $pairPat.Matches($m.Groups[1].Value)) { $params[$p.Groups[1].Value] = $p.Groups[2].Value }
|
|
||||||
foreach ($key in $slotKeys) {
|
|
||||||
if ($params.ContainsKey($key)) { Add-ItemId $params[$key] }
|
|
||||||
}
|
|
||||||
if ($params.ContainsKey('pattern')) {
|
|
||||||
$pat = $params['pattern'] -replace '\\n|;|\r?\n', ' '
|
|
||||||
foreach ($tok in @($pat -split '\s+' | Where-Object { $_ })) {
|
|
||||||
if ($tok -eq '.') { continue }
|
|
||||||
if ($params.ContainsKey($tok)) { Add-ItemId $params[$tok] }
|
|
||||||
elseif ($tok -match '^[a-z0-9_]+$') { Add-ItemId $tok }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$all = @($ids.Keys | Sort-Object)
|
|
||||||
$need = @($all | Where-Object { -not (Test-Path -LiteralPath (Join-Path $itemsDir "${_}.png")) })
|
|
||||||
Write-Host " 使用 $($all.Count) 个物品 id, 本地已有 $($all.Count - $need.Count) 个"
|
|
||||||
|
|
||||||
# ---------- 3. 从 Minecraft wiki 获取缺失贴图 ----------
|
|
||||||
Write-Step '3/4 从 Minecraft wiki 获取缺失贴图'
|
|
||||||
|
|
||||||
function Get-WikiCandidates([string]$name) {
|
|
||||||
$regex = '^' + [regex]::Escape($name) + '(_\(item\))?_JE\d+(\.\d+)*(_BE\d+)?\.png$'
|
|
||||||
# 两个查询互补:
|
|
||||||
# <name>_ 宽前缀, 覆盖 (item) 等变体, 如 Diamond_Boots_(item)_JE3_BE2.png
|
|
||||||
# <name>_JE 窄前缀, 在宽前缀因数量被截断时兜底(裸物品如 diamond 的 JE 渲染排序靠前)
|
|
||||||
$urls = @(
|
|
||||||
"$wikiApi`?action=query&list=allimages&aiprefix=$([uri]::EscapeDataString($name + '_'))&ailimit=500&format=json",
|
|
||||||
"$wikiApi`?action=query&list=allimages&aiprefix=$([uri]::EscapeDataString($name + '_JE'))&ailimit=200&format=json"
|
|
||||||
)
|
|
||||||
$seen = @{}
|
|
||||||
$all = New-Object System.Collections.ArrayList
|
|
||||||
foreach ($u in $urls) {
|
|
||||||
try {
|
|
||||||
$r = Invoke-Get $u
|
|
||||||
if ($null -ne $r.query.allimages) {
|
|
||||||
foreach ($n in $r.query.allimages) {
|
|
||||||
if (-not $seen.ContainsKey($n.name)) {
|
|
||||||
[void]$seen.Add($n.name, $true)
|
|
||||||
[void]$all.Add($n.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
Write-Host " [警告] wiki 查询失败 ($name): $($_.Exception.Message)" -ForegroundColor Yellow
|
|
||||||
}
|
|
||||||
Start-Sleep -Milliseconds 250
|
|
||||||
}
|
|
||||||
return @($all | Where-Object { $_ -match $regex })
|
|
||||||
}
|
|
||||||
|
|
||||||
function Select-BestCandidate([string]$name, [string[]]$candidates) {
|
|
||||||
$regex = '^' + [regex]::Escape($name) + '(_\(item\))?_JE(\d+)(\.\d+)*(_BE(\d+))?\.png$'
|
|
||||||
$best = $null
|
|
||||||
$bestKey = $null
|
|
||||||
foreach ($c in $candidates) {
|
|
||||||
$m = [regex]::Match($c, $regex)
|
|
||||||
if (-not $m.Success) { continue }
|
|
||||||
$key = @(
|
|
||||||
$(if ($m.Groups[1].Success) { 1 } else { 0 }), # 优先物品图标 (item)
|
|
||||||
[int]$m.Groups[2].Value, # JE 版本
|
|
||||||
$(if ($m.Groups[5].Success) { [int]$m.Groups[5].Value } else { -1 }) # BE 版本
|
|
||||||
)
|
|
||||||
if ($null -eq $bestKey) { $best = $c; $bestKey = $key; continue }
|
|
||||||
for ($i = 0; $i -lt 3; $i++) {
|
|
||||||
if ($key[$i] -ne $bestKey[$i]) {
|
|
||||||
if ($key[$i] -gt $bestKey[$i]) { $best = $c; $bestKey = $key }
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $best
|
|
||||||
}
|
|
||||||
|
|
||||||
function Get-NameVariants([string]$id) {
|
|
||||||
$words = @($id -split '_' | Where-Object { $_ })
|
|
||||||
$title = @($words | ForEach-Object { $_.Substring(0, 1).ToUpper() + $_.Substring(1) }) -join '_'
|
|
||||||
|
|
||||||
$connectors = @('and', 'of', 'the', 'for', 'with', 'to', 'from', 'by', 'in', 'on', 'at')
|
|
||||||
$mixed = @($words | ForEach-Object {
|
|
||||||
if ($connectors -contains $_) { $_ } else { $_.Substring(0, 1).ToUpper() + $_.Substring(1) }
|
|
||||||
}) -join '_'
|
|
||||||
|
|
||||||
$variants = New-Object System.Collections.ArrayList
|
|
||||||
[void]$variants.Add($title)
|
|
||||||
if ($mixed -cne $title) { [void]$variants.Add($mixed) }
|
|
||||||
if ($id.Length -le 4 -and $id -notmatch '[aeiou]') { [void]$variants.Add($id.ToUpper()) }
|
|
||||||
$oneWord = @($words | ForEach-Object { $_.Substring(0, 1).ToUpper() + $_.Substring(1) }) -join ''
|
|
||||||
if ($oneWord -cne $title) { [void]$variants.Add($oneWord) }
|
|
||||||
|
|
||||||
$aliases = @{
|
|
||||||
'tnt' = 'TNT'
|
|
||||||
'tnt_minecart' = 'TNT_Minecart'
|
|
||||||
'dragon_breath' = "Dragon's_Breath"
|
|
||||||
'slime_ball' = 'Slime'
|
|
||||||
'snow_golem' = 'Snow_Golem'
|
|
||||||
'ocelot' = 'Ocelot'
|
|
||||||
}
|
|
||||||
if ($aliases.ContainsKey($id)) { [void]$variants.Insert(0, $aliases[$id]) }
|
|
||||||
return @($variants | Select-Object -Unique)
|
|
||||||
}
|
|
||||||
|
|
||||||
$failed = @()
|
|
||||||
foreach ($id in $need) {
|
|
||||||
$file = $null
|
|
||||||
foreach ($name in Get-NameVariants $id) {
|
|
||||||
$cands = Get-WikiCandidates $name
|
|
||||||
if ($cands.Count -gt 0) { $file = Select-BestCandidate $name $cands; break }
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($file) {
|
|
||||||
$target = Join-Path $itemsDir "$id.png"
|
|
||||||
try {
|
|
||||||
Invoke-WebRequest -Uri ($wikiImg + [uri]::EscapeDataString($file)) -Headers @{ 'User-Agent' = $ua } -OutFile $target -TimeoutSec 60
|
|
||||||
$fs = [System.IO.File]::OpenRead($target)
|
|
||||||
$sig = New-Object byte[] 8
|
|
||||||
[void]$fs.Read($sig, 0, 8)
|
|
||||||
$fs.Close()
|
|
||||||
if (-not ($sig[0] -eq 137 -and $sig[1] -eq 80 -and $sig[2] -eq 78 -and $sig[3] -eq 71)) {
|
|
||||||
throw '下载内容不是有效 PNG'
|
|
||||||
}
|
|
||||||
Write-Host " ok $id <- $file" -ForegroundColor Green
|
|
||||||
continue
|
|
||||||
} catch {
|
|
||||||
Write-Host " fail $id 下载失败: $($_.Exception.Message)" -ForegroundColor Yellow
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Write-Host " fail $id 在 wiki 上未找到" -ForegroundColor Yellow
|
|
||||||
}
|
|
||||||
$failed += $id
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------- 4. 汇总错误 ----------
|
|
||||||
if ($failed.Count -gt 0) {
|
|
||||||
Write-Host "`n[错误] 以下物品贴图无法自动获取:" -ForegroundColor Red
|
|
||||||
$failed | Sort-Object | ForEach-Object { Write-Host " - $_" -ForegroundColor Red }
|
|
||||||
Write-Host @"
|
|
||||||
|
|
||||||
可能原因:
|
|
||||||
1. 物品 id 拼写错误(组件中填写的是 assets/items 下的文件名)
|
|
||||||
2. 该物品是 sfcraft 自定义物品, 但 textures/item 目录中没有同名贴图
|
|
||||||
3. 原版物品在 Minecraft wiki 上的命名特殊, 未被自动规则覆盖
|
|
||||||
|
|
||||||
解决办法:
|
|
||||||
- 将贴图手动放入 assets/items/<id>.png 后重新运行
|
|
||||||
- 修正组件中的物品 id
|
|
||||||
- 若是 wiki 命名特例, 可在 scripts/sync-items.ps1 的 Get-NameVariants 别名表中补充
|
|
||||||
"@ -ForegroundColor Yellow
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "`n全部贴图就绪 ✓" -ForegroundColor Green
|
|
||||||
exit 0
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""SFCraft 文档 · 物品贴图自动同步 (Linux / macOS / Unix 版)
|
"""SFCraft 文档 · 物品贴图自动同步 (Python 3)
|
||||||
|
|
||||||
与 scripts/sync-items.ps1 逻辑一致:
|
|
||||||
1. 下载 sfcraft 仓库 textures/item 全部贴图到 assets/items
|
1. 下载 sfcraft 仓库 textures/item 全部贴图到 assets/items
|
||||||
2. 扫描 content 中 crafting 组件用到的物品 id (支持九格直填与 pattern 别名两种写法)
|
2. 扫描 content 中 crafting 组件用到的物品 id (支持九格直填与 pattern 别名两种写法)
|
||||||
3. 本地缺失的 id 从 Minecraft wiki 自动获取(自动挑选最新版本渲染)
|
3. 本地缺失的 id 从 Minecraft wiki 自动获取(自动挑选最新版本渲染)
|
||||||
@@ -50,18 +49,19 @@ ALIASES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def http_get_json(url, retries=2):
|
def http_get_json(url: str, retries: int = 2) -> dict:
|
||||||
last = None
|
|
||||||
for i in range(retries + 1):
|
for i in range(retries + 1):
|
||||||
try:
|
try:
|
||||||
req = urllib.request.Request(url, headers={"User-Agent": UA})
|
req = urllib.request.Request(url, headers={"User-Agent": UA})
|
||||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||||
return json.load(resp)
|
return json.load(resp)
|
||||||
except Exception as e: # noqa: BLE001
|
except Exception: # noqa: BLE001
|
||||||
last = e
|
|
||||||
if i < retries:
|
if i < retries:
|
||||||
time.sleep(0.8)
|
time.sleep(0.8)
|
||||||
raise last
|
else:
|
||||||
|
raise
|
||||||
|
# 理论上不可达(retries 为负时 range 为空才可能走到), 兜底避免隐式返回 None
|
||||||
|
raise RuntimeError(f"请求失败(无可用异常信息): {url}")
|
||||||
|
|
||||||
|
|
||||||
def http_download(url, target):
|
def http_download(url, target):
|
||||||
@@ -84,7 +84,7 @@ def add_item_id(ids, raw):
|
|||||||
|
|
||||||
def scan_item_ids():
|
def scan_item_ids():
|
||||||
"""扫描 content 下所有 crafting 组件, 收集物品 id。"""
|
"""扫描 content 下所有 crafting 组件, 收集物品 id。"""
|
||||||
ids = set()
|
ids: set[str] = set()
|
||||||
for path in sorted(CONTENT_DIR.rglob("*.md")):
|
for path in sorted(CONTENT_DIR.rglob("*.md")):
|
||||||
try:
|
try:
|
||||||
text = path.read_text(encoding="utf-8")
|
text = path.read_text(encoding="utf-8")
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# SFCraft 文档 · 物品贴图自动同步 (Linux / macOS / Unix)
|
|
||||||
# 用法: ./scripts/sync-items.sh [-f]
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
|
|
||||||
PY=""
|
|
||||||
if command -v python3 >/dev/null 2>&1 && python3 -c "import sys" >/dev/null 2>&1; then
|
|
||||||
PY="python3"
|
|
||||||
elif command -v python >/dev/null 2>&1 && python -c "import sys" >/dev/null 2>&1; then
|
|
||||||
PY="python"
|
|
||||||
else
|
|
||||||
echo "[错误] 未找到可用的 python3 / python, 请先安装 Python 3。" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec "$PY" "$DIR/sync-items.py" "$@"
|
|
||||||
Reference in New Issue
Block a user