M1+M2:1.21.1 构建跑通 + 协议客户端进 play 阶段

M1(构建):
- build.sh 编译 CP 加入 $OUT/fabric/*.jar,解决 net.fabricmc.* 找不到
- gen_1_21 内联 Sodium 通知改为委托共用 RenderCompat,补 reportRenderCompat
- 1.21.1 全量七步构建产物齐备(classes-intermediary.jar / names*.json / srg / inheritance.map)

M2(协议):
- login_start 按协议数据发 UUID(1.21 必填;1.20 可选)
- login_acknowledged / serverbound 包 id 改为查表,不再写死 0x00
- send() 实现压缩帧格式(varint(dataLen)+数据,小包不压)
- configuration 双向协商:发 settings、回 select_known_packs、拒 resource_pack
- protodef 补 bitflags / entityMetadataLoop / count(ctx引用) / compareTo 路径表达式
- NBT 读取器由占位符改为真实现(anonymousNbt 支撑 registry_data)
- 坏包容错:长度前缀是帧层权威,解不开整包丢弃并布告,不断流

实测:login→configuration(11 注册表)→play,位置 (5.5,-60.0,-0.5),map_chunk×9
判据:protodef 6→13 项(含线上真实 entity_metadata 字节回归);全量 77 项通过
This commit is contained in:
JianFeeeee
2026-09-26 12:15:48 +08:00
parent b21b869e09
commit 547f4beb7e
8 changed files with 1122 additions and 240 deletions

View File

@ -60,6 +60,7 @@ import java.util.Optional;
import java.util.OptionalInt;
import java.util.UUID;
import mcfold.McfoldNames;
import mcfold.RenderCompat;
/**
* mcfold 渲染服务:常驻进程,NDJSON(行分隔 JSON) 协议,由 Python 侧驱动。
@ -667,6 +668,7 @@ public class McfoldRenderServer {
}
// viewCenter
r.addProperty("fogStart", String.valueOf(mc.options.renderDistance().get()));
reportRenderCompat(r);
emit(id, r);
break;
}
@ -743,33 +745,37 @@ public class McfoldRenderServer {
} catch (Throwable t) {
System.err.println("[mcfold] onChunkLoaded(" + cx + "," + cz + ") 失败: " + t);
}
// ② 渲染替换件的 tracker(完整说明见 gen_1_20 同处)
try {
Class<?> holder = Class.forName(
"me.jellysquid.mods.sodium.client.render.chunk.map.ChunkTrackerHolder");
// ★ 参数类型必须是 ClientLevel(签名 get(class_638)),传 Level.class 会 NoSuchMethodException
Object tracker = holder.getMethod("get",
net.minecraft.client.multiplayer.ClientLevel.class).invoke(null, level);
if (tracker == null) return;
// ★ 第三参数是位掩码 int(实测签名 (III)V):
// FLAG_HAS_BLOCK_DATA=1 / FLAG_HAS_LIGHT_DATA=2 / FLAG_ALL=3
// 而 updateMerged 的 2×2 判定要求 == 3 ⇒ 必须报 FLAG_ALL,
// 否则所有区块判成"凑不齐 2×2" ⇒ chunkReady 恒 0、无地形
// (实测:传 1 → chunkStatus=49 但 chunkReady=0)
int flagAll;
try {
Class<?> cs = sodiumClass().getClassLoader().loadClass(
"me.jellysquid.mods.sodium.client.render.chunk.map.ChunkStatus");
flagAll = cs.getField("FLAG_ALL").getInt(null);
} catch (Throwable t) { flagAll = 3; }
tracker.getClass()
.getMethod("onChunkStatusAdded", int.class, int.class, int.class)
.invoke(tracker, cx, cz, flagAll);
} catch (ClassNotFoundException noTracker) {
// 没装该渲染器 ⇒ 正常,静默
} catch (Throwable t) {
System.err.println("[mcfold] notifyRenderTracker(" + cx + "," + cz + "): " + t);
// ② 渲染替换件的 tracker —— 走共用桥(完整根因说明见 RenderCompat)
RenderCompat.notifyChunkLoaded(level, cx, cz);
}
/** 渲染替换件在本次渲染期间的变化([chunkReady 增量, loadQueue 增量])。 */
static final long[] SODIUM_DELTA = new long[2];
/** 诊断:地形渲染替换件(Sodium 等)的真实状态。根因说明见 RenderCompat。 */
static void reportRenderCompat(JsonObject r) {
if (!RenderCompat.available()) { r.addProperty("renderCompat", "absent"); return; }
r.addProperty("renderCompat", "present");
long[] snap = RenderCompat.trackerSnapshot(level);
if (snap != null) {
r.addProperty("rc_chunkStatus", snap[0]);
r.addProperty("rc_chunkReady", snap[1]);
r.addProperty("rc_loadQueue", snap[2]);
}
Integer vis = RenderCompat.visibleChunks(level);
if (vis != null) r.addProperty("rc_visibleChunks", vis);
String dbg = RenderCompat.chunksDebugString(level);
if (dbg != null) r.addProperty("rc_chunksDebug", dbg);
int[] q = RenderCompat.rebuildQueue(level);
if (q != null) {
JsonObject qo = new JsonObject();
qo.addProperty("INITIAL_BUILD", q[0]);
qo.addProperty("REBUILD", q[1]);
qo.addProperty("IMPORTANT_REBUILD", q[2]);
r.add("rc_rebuildQueue", qo);
}
r.addProperty("rc_deltaChunkReady", SODIUM_DELTA[0]);
r.addProperty("rc_deltaLoadQueue", SODIUM_DELTA[1]);
}
static void levelRendererAllChanged() {