#!/usr/bin/env bash # mcfold 构建脚本 —— 把官方客户端 jar 变成「可直接调用的渲染库」。 # # ★ 版本隔离策略(用户 2026-09-25 强调) # 产物写入**内容根** store/-/.mcfold/,不写 /tmp: # 内容根/.mcfold/client-named.jar 反混淆后的客户端(可只读共享) # 内容根/.mcfold/classes/ 我们的渲染服务 # 内容根/.mcfold/natives/ LWJGL .so # 理由:这些都由该版本的 client jar 派生,属「可共享内容」; # /tmp 是 tmpfs,重启即丢(实测踩过)。实例独占的东西(mods/config/options) # 一律不进这里。 # # 用法: ./build.sh <内容根|实例名> [java模式目录] # ./build.sh /home/newqqagent/mcview/store/vanilla-1.20.1 1.20.1 # ./build.sh /home/newqqagent/mcview/store/vanilla-1.21.1 1.21.1 java/gen_1_21 set -euo pipefail STORE="${1:?用法: build.sh <内容根> [java目录]}" MCVER="${2:?需要 MC 版本}" JAVA_SUB="${3:-}" HERE="$(cd "$(dirname "$0")" && pwd)" # 按版本挑世代目录(除非显式指定) if [ -z "$JAVA_SUB" ]; then case "$MCVER" in 1.21*) JAVA_SUB="java/gen_1_21" ;; *) JAVA_SUB="java/gen_1_20" ;; esac fi OUT="$STORE/.mcfold" WORK="${MCFOLD_BUILD_WORK:-$(mktemp -d /tmp/mcfold-build-XXXXXX)}" CLIENT_JAR="$STORE/versions/$MCVER/$MCVER.jar" [ -f "$CLIENT_JAR" ] || { echo "找不到客户端 jar: $CLIENT_JAR"; exit 1; } mkdir -p "$OUT/classes" "$OUT/natives" "$WORK" echo "[build] 版本 $MCVER 世代 $JAVA_SUB" echo "[build] 输出 → $OUT" # ── 1. 官方映射 ──────────────────────────────────────────────── if [ ! -s "$WORK/client_mappings.txt" ]; then URL=$(python3 - "$STORE/versions/$MCVER/$MCVER.json" <<'PY' import json,sys print(json.load(open(sys.argv[1]))['downloads']['client_mappings']['url']) PY ) echo "[1/5] 下载官方映射..." curl -sL -o "$WORK/client_mappings.txt" "$URL" fi echo "[1/5] 映射 $(wc -l < "$WORK/client_mappings.txt") 行" # ── 2. ProGuard → SRG ───────────────────────────────────────── echo "[2/5] 转换映射格式..." python3 "$HERE/pg2srg.py" "$WORK/client_mappings.txt" "$WORK/mappings.srg" # ── 3. 重映射客户端 jar ─────────────────────────────────────── NAMED="$OUT/client-named.jar" if [ ! -f "$NAMED" ] || [ "$CLIENT_JAR" -nt "$NAMED" ]; then echo "[3/5] 重映射(1-3 分钟)..." SS=$(find /home/minecraft/libraries -name "SpecialSource-*.jar" 2>/dev/null | head -1) GUAVA=$(find /home/minecraft/libraries -name "guava-*.jar" ! -name "*sources*" 2>/dev/null | head -1) [ -n "$SS" ] || { echo "找不到 SpecialSource"; exit 1; } for a in asm asm-commons asm-tree; do [ -f "$WORK/$a.jar" ] || curl -sL -o "$WORK/$a.jar" \ "https://repo1.maven.org/maven2/org/ow2/asm/$a/9.7/$a-9.7.jar" done [ -f "$WORK/joptsimple.jar" ] || curl -sL -o "$WORK/joptsimple.jar" \ "https://repo1.maven.org/maven2/net/sf/jopt-simple/jopt-simple/5.0.4/joptsimple-5.0.4.jar" java -Xmx3G -cp "$SS:$WORK/joptsimple.jar:$WORK/asm.jar:$WORK/asm-commons.jar:$WORK/asm-tree.jar:$GUAVA" \ net.md_5.specialsource.SpecialSource \ -i "$CLIENT_JAR" -o "$NAMED" -m "$WORK/mappings.srg" else echo "[3/5] client-named.jar 已是最新" fi # ── 4. classpath(去 asm 重复,Fabric 需要) + natives ──────── # classpath.txt **只含第三方 libraries** —— client-named.jar 与 classes/ 由使用方拼接 # (build.sh 自己在下面拼,测试/后端则从各自路径取)。 # ★ 别在这里把 named/classes 也写进去:那会让「libraries 清单」与「运行 classpath」 # 两个概念混成一列,而 Fabric 路线需要把 asm 从 libraries 里剔掉后**另行**拼 8 个库。 find "$STORE/libraries" -name "*.jar" ! -name "*-sources.jar" ! -name "*natives*" 2>/dev/null \ | grep -vE "minecraftforge|neoforge" > "$OUT/classpath.txt" NATLIST=$(find "$STORE/libraries" -name "*natives-linux*.jar" 2>/dev/null) if [ -n "$NATLIST" ]; then rm -rf "$OUT/natives"; mkdir -p "$OUT/natives" # ★ 必须**平铺**。natives jar 里的路径是 linux/x64/org/lwjgl//lib*.so, # 而 LWJGL 是经 java.library.path/LD_LIBRARY_PATH 按**文件名**找库的 —— # 嵌套一层的目录它**找不到**(实测 UnsatisfiedLinkError: Failed to locate library: liblwjgl.so)。 while read -r j; do [ -f "$j" ] || continue unzip -o -q "$j" -d "$OUT/natives.tmp" 2>/dev/null || true done <<< "$NATLIST" find "$OUT/natives.tmp" -name '*.so' -exec cp {} "$OUT/natives/" \; 2>/dev/null rm -rf "$OUT/natives.tmp" fi echo "[4/5] 库 $(wc -l < "$OUT/classpath.txt") 个, natives $(find "$OUT/natives" -name '*.so' | wc -l) 个" # ── 5. 编译渲染服务 ─────────────────────────────────────────── echo "[5/5] 编译 $JAVA_SUB ..." CP="$(paste -sd: "$OUT/classpath.txt"):$NAMED:$OUT/classes" find "$HERE/$JAVA_SUB" -name "*.java" > "$WORK/srcs.txt" javac -nowarn -proc:none -cp "$CP" -d "$OUT/classes" @"$WORK/srcs.txt" echo echo "✅ 构建完成(产物在内容根,不是 /tmp)" echo " 内容根 : $STORE" echo " 产物 : $OUT" echo " named : $OUT/client-named.jar" echo " classes: $OUT/classes" echo echo "运行(渲染服务从 stdin 读 NDJSON):" echo " DISPLAY=:98 java -cp \"\$(paste -sd: $OUT/classpath.txt):$NAMED:$OUT/classes\" \\" echo " -Djava.library.path=$OUT/natives McfoldRenderServer \\" echo " --gameDir <实例目录> --assets $STORE/assets --assetIndex $MCVER" echo echo "★ 实例目录必须是**实例独占**的那个(mods/config/options 的边界)," echo " 不要指向别的版本或别的加载器的实例 —— 那正是「多实例共用 mods」的崩因。"