Files
agentmc/scaffold/gen_intermediary_srg.py
JianFeeeee e6f8c0e596 build.sh 七步:加 remap 与名字表(加载器路线所需)
## 新增两步(原五步 → 七步)

**[6/7] 反射名次表** —— 调 gen_names.py 生成
    names.json(named 形态)与 names-intermediary.json(intermediary 形态)

**[7/7] remap named → intermediary** —— 三步:
    7a. 复合出 named→intermediary 的 SRG(gen_intermediary_srg.py)
    7b. 造一个 **intermediary 形态的 client jar** ← 继承查找的输入,缺它不行
    7c. 从它生成继承映射(-H),再用 -h 把我们的 classes remap 过去

## 为什么必须 remap

实 mod(Iris/Sodium 等)的 mixin 注解写的是 **intermediary** 名且不带 refmap。
跑 named 时 42 个 mod 的 464 处 mixin 全部找不到目标(实测)。
⇒「mod 要 intermediary」与「我们的产物是 named」在同一 JVM 互斥,
  解法是把**我们的产物** remap 过去(非通用数据由 extern 名次表解决)。

## gen_intermediary_srg.py 的两个实测坑(不处理会出错且症状误导)

① **描述符必须转**。mappings.srg 的 MD 行两侧 desc **都是 official 形式**:
       MD: enn/Z ()Laki; net/.../Minecraft/getResourcePackRepository ()Laki;
   左 desc 必须转成 named、右转成 intermediary。
   只改类名不改 desc 的症状:**类改名成功了、方法却没改** → NoSuchMethodError。

② **必须生成继承映射**。SpecialSource 按 owner+name+desc 精确匹配、
   **不查继承链**;而 MC 里大量方法继承自父类/接口
   (如 ClientPacketCache.registryAccess 实际声明在 RegistryAccess)。
   不带它漏 ~60 处(实测:60 → 12,且剩余 12 全是 JDK 继承方法、
   record component、和我们自己那个不该改名的桥类)。

## 另修三处

- **SS_CP 提到全局**:原来在第 3 步的 if 里定义,而第 7 步也要用它 ——
  若 client-named.jar 已是最新,第 3 步不进入 if ⇒ 变量为空。
- **LoaderBoot 纳入编译**:launcher_fabric 从没被 build.sh 编译过,
  运行期报 ClassNotFoundException: LoaderBoot。
- **common/ 纳入编译**:mcfold/McfoldNames.java 在 common 下,
  漏了它 ⇒ remap 后产物里没有这个类 ⇒ NoSuchFieldException(实测踩过)。
- **natives 平铺**(见上一批提交):jar 内是 linux/x64/org/lwjgl/<mod>/lib*.so,
  而 LWJGL 按**文件名**找库 ⇒ 保留包路径会 UnsatisfiedLinkError。

## 产物清单(落在内容根 .mcfold/)

    classes/                    裸启动 / named
    classes-intermediary.jar    加载器 / intermediary
    client-named.jar · client-intermediary.jar
    names.json · names-intermediary.json
    named2intermediary.srg · inheritance.map
    classpath.txt · natives/(平铺)· fabric/(8 库)
2026-09-26 10:18:08 +08:00

131 lines
5.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""复合两份映射 → named→intermediary 的 SRG(供 SpecialSource 把我们的产物 remap 过去)。
## 为什么需要
实 mod(Iris/Sodium 等)的 mixin 注解写的是 **intermediary** 名,且不带 refmap。
我们跑 named 时,42 个 mod 的 464 处 mixin 全部找不到目标。
⇒ 「mod 要 intermediary」与「我们的产物是 named」在同一 JVM 互斥,
解法:把**我们的产物** remap 到 intermediary。
## 输入与复合
mappings.srg official → named (ProGuard;我们做 client-named.jar 用的)
intermediary mappings.tiny official → intermediary (Fabric)
两者键在**同一套 official 名**上(实测交集 7388/7413),所以:
named ──(srg 反查)──> official ──(tiny)──> intermediary
## ★ 两个实测坑(不处理就会出错,且症状具有误导性)
① **描述符必须转**。mappings.srg 的 MD 行两侧 desc **都是 official 形式**:
MD: enn/Z ()Laki; net/minecraft/client/Minecraft/getResourcePackRepository ()Laki;
⇒ 左(from)desc 必须转成 named,右(to)desc 转成 intermediary。
只改类名不改 desc 的症状:**类改名成功了,方法却没改** → 运行期 NoSuchMethodError。
② **必须另生成继承映射**(build.sh 里用 -H/-h)。SpecialSource 按 owner+name+desc
精确匹配,**不查继承链**;而 MC 里大量方法继承自父类/接口
(如 ClientPacketConnection.registryAccess 实际声明在 RegistryAccess)。
不带继承映射会漏 ~60 处(实测)。
"""
from __future__ import annotations
import argparse
import re
import sys
import zipfile
from pathlib import Path
def load_srg(path: Path):
"""official → named。返回 (类表, 方法行, 字段行)。"""
off2named, mds, fds = {}, [], []
for line in path.read_text(errors="replace").splitlines():
p = line.split()
if not p:
continue
if p[0] == "CL:" and len(p) == 3:
off2named[p[1]] = p[2]
elif p[0] == "MD:" and len(p) == 5:
mds.append(p)
elif p[0] == "FD:" and len(p) == 3:
fds.append(p)
return off2named, mds, fds
def load_tiny(jar: Path):
"""official → intermediary。返回 (类表, (kind,owner,desc,name)→intName, (owner,name)→intName)。"""
off2int_c, mem, fld_by_name = {}, {}, {}
with zipfile.ZipFile(jar) as z:
text = z.read("mappings/mappings.tiny").decode("utf-8", "replace")
for line in text.splitlines():
p = line.split("\t")
if not p:
continue
if p[0] == "CLASS" and len(p) >= 3:
off2int_c[p[1]] = p[2]
elif p[0] in ("METHOD", "FIELD") and len(p) >= 5:
# METHOD/FIELD <offOwner> <desc> <name> <intName>
mem[(p[0], p[1], p[2], p[3])] = p[4]
if p[0] == "FIELD":
fld_by_name[(p[1], p[3])] = p[4]
return off2int_c, mem, fld_by_name
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--srg", required=True)
ap.add_argument("--intermediary", required=True,
help="含 mappings/mappings.tiny 的 jar")
ap.add_argument("--out", required=True)
args = ap.parse_args()
off2named, mds, fds = load_srg(Path(args.srg))
off2int_c, tiny_mem, tiny_fld = load_tiny(Path(args.intermediary))
print(f" official→named cls={len(off2named)} md={len(mds)} fd={len(fds)}")
print(f" official→int cls={len(off2int_c)} mem={len(tiny_mem)}")
def to_named(desc: str) -> str:
return re.sub(r"L([^;]+);",
lambda m: "L" + off2named.get(m.group(1), m.group(1)) + ";", desc)
def to_inter(desc: str) -> str:
return re.sub(r"L([^;]+);",
lambda m: "L" + off2int_c.get(m.group(1), m.group(1)) + ";", desc)
out, n_cls, n_mth, n_fld = [], 0, 0, 0
for off, named in off2named.items():
if off in off2int_c:
out.append(f"CL: {named} {off2int_c[off]}")
n_cls += 1
for p in mds:
o = p[1].rsplit("/", 1)
n = p[3].rsplit("/", 1)
if len(o) != 2 or len(n) != 2:
continue
k = ("METHOD", o[0], p[2], o[1])
if k in tiny_mem and o[0] in off2int_c:
out.append("MD: %s/%s %s %s/%s %s" % (
n[0], n[1], to_named(p[4]), # ← 左 desc 转 named(坑 ①)
off2int_c[o[0]], tiny_mem[k], to_inter(p[2])))
n_mth += 1
for p in fds:
o = p[1].rsplit("/", 1)
n = p[2].rsplit("/", 1)
if len(o) != 2 or len(n) != 2 or o[0] not in off2int_c:
continue
hit = tiny_fld.get((o[0], o[1]))
if hit:
out.append(f"FD: {n[0]}/{n[1]} {off2int_c[o[0]]}/{hit}")
n_fld += 1
Path(args.out).write_text("\n".join(out) + "\n")
print(f" ✅ named→intermediary: cls={n_cls} mth={n_mth} fld={n_fld} → {args.out}")
return 0
if __name__ == "__main__":
sys.exit(main())