TileEntityMarkerTransform
基本信息
| 属性 | 值 |
|---|---|
| 类 | com.gtnewhorizons.angelica.loading.shared.transformers.TileEntityMarkerTransform |
| 路径 | src/main/java/com/gtnewhorizons/angelica/loading/shared/transformers/TileEntityMarkerTransform.java |
| 行数 | 137 |
| 类型 | public final class(:20) |
| 公开常量 | 3 个 marker 位掩码(:22-24) |
| 内部类型 | private record Marker(int bit, String method, String desc, String iface)(:43) |
| 内部类 | private final class MarkerScanner extends ClassVisitor(:116-136) |
| 驱动者 | CeleritasBlockTransformer 与 RFBCeleritasBlockTransformer |
类级 Javadoc 说明的问题(:15-18)
Marks TileEntity subclasses that declare getDescriptionPacket, shouldRenderInPass or getDistanceFrom with a corresponding interface, so runtime code can test with instanceof instead of reflection.
即:把「这个 TileEntity 是否覆写了某方法」从运行期反射变成类加载期打标记 + 运行期 instanceof。这是典型的反射性能优化。
3 个 marker(:22-24)
| 位常量 | 值 | 生成的接口 |
|---|---|---|
MARK_DESCRIPTION_PACKET |
1 << 0 |
SendsDescriptionPacket |
MARK_SHOULD_RENDER_IN_PASS |
1 << 1 |
OverridesShouldRenderInPass |
MARK_GET_DISTANCE_FROM |
1 << 2 |
OverridesGetDistanceFrom |
目标方法表(:47-50)
| marker | 方法名 | 混淆名 | 描述符 | 接口 FQN |
|---|---|---|---|---|
| DESCRIPTION_PACKET | getDescriptionPacket |
func_145844_m |
()Lnet/minecraft/network/Packet; |
com/gtnewhorizons/angelica/mixins/interfaces/SendsDescriptionPacket |
| SHOULD_RENDER_IN_PASS | shouldRenderInPass |
(无混淆名) | (I)Z |
.../OverridesShouldRenderInPass |
| GET_DISTANCE_FROM | getDistanceFrom |
func_145835_a |
(DDD)D |
.../OverridesGetDistanceFrom |
只有 2 个方法需要混淆名分支 —— shouldRenderInPass 是 Forge 添加的方法,不存在混淆名,构造时直接写字面量。
构造参数 asmApi 的必要性(:36-42)
/**
* A visitor refuses to visit attributes newer than its api level, such as the NestMember one javac emits for
* classes with nested classes, so the caller passes the highest level its class loader supports.
*/
private final int asmApi;
这是本类唯一有解释性注释的字段,也是两条加载路径行为差异的根因:
| Wrapper | asmApi 取值 |
后果 |
|---|---|---|
CeleritasBlockTransformer :22 |
Opcodes.ASM5(硬编码) |
带嵌套类的 TileEntity 的 NestMember 属性被拒绝访问 |
RFBCeleritasBlockTransformer :25 |
RetroFuturaBootstrap.API.newestAsmVersion() |
正确处理 |
4 个公开/包级方法
| 方法 | 行 | 语义 |
|---|---|---|
track(className, superClassName) |
:62-64 |
父类是 TileEntity 则记录子类 |
markersFor(className, classBytes) |
:66-72 |
返回缺失的 marker 位掩码(declared & ~alreadyPresent) |
addMarkers(byte[], int) |
:74-92 |
字节码版:用 ClassVisitor 覆写 visit 追加接口 |
addMarkers(ClassNode, int) |
:94-96 |
AST 版:Collections.addAll(cn.interfaces, ...) |
isTileEntity(:58-60)
className != null && (className.startsWith(TILE_ENTITY) || tileEntities.contains(className))
TILE_ENTITY = "net/minecraft/tileentity/TileEntity"(:35)。用 startsWith 前缀识别原版(与 CeleritasBlockTransform.isVanillaBlockSubclass 同一手法),用 tileEntities 集合识别 mod 的。
markersFor 的两级早退(:67)
if (TILE_ENTITY.equals(className) || !isTileEntity(className) || !prefilter.find(classBytes)) return 0;
| 条件 | 理由 |
|---|---|
类就是 TileEntity 本身 |
跳过基类自身 |
| 不是 TileEntity 子类 | 跳过无关类 |
| 常量池预筛选未命中 | 无需建 ClassReader |
预筛选用 SCAN_FLAGS = SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES(:36)—— 只读方法声明,跳过字节码。
幂等性设计
MarkerScanner(:116-136)用两个累加器:
| 字段 | 行 | 含义 |
|---|---|---|
declared |
:118、:133 |
类里声明了哪些 marker 方法(bitOf(access, name, desc)) |
alreadyPresent |
:119、:128 |
类已实现哪些 marker 接口(bitOf(ifaceName)) |
:71 的 return scanner.declared & ~scanner.alreadyPresent; —— 返回「已声明但未标记」的部分,天然幂等:重复处理不会重复加接口。
bitOf(int access, String name, String desc)(:98-102)有过滤:(access & ACC_PUBLIC) == 0 || (access & ACC_STATIC) != 0 直接返回 0 —— 即只认 public 非 static 的方法。私有/静态的同名方法不算覆写。
已知问题
- FML 侧
ASM5硬编码的后果(见上表):含嵌套类的 TileEntity 会被ClassVisitor拒访NestMember属性。这是两条加载路径间可观测的行为差异,且只在 FML 路径出现。 shouldRenderInPass无混淆名(:48):若某 mod 在混淆环境下把 Forge 方法也重命名,本 marker 静默失效。构造器对 3 个 marker 用了 2 种不同处理方式,没有统一规则。startsWith(TILE_ENTITY)误识别:任何类名以net/minecraft/tileentity/TileEntity开头的类(无论是否真的继承)都被当作 TileEntity。与 CeleritasBlockTransform:65的同类手法共享这个风险。markersFor被重复调用:shouldTransformClass(RFB:69)与transformClassIfNeeded(RFB:81)各调一次,每次都new ClassReader(classBytes).accept(scanner, SCAN_FLAGS)(:70)。FML 侧靠markers局部变量只算一次(CeleritasBlockTransformer:42),RFB 路径白白扫两遍。- 接口定义在
mixins/interfaces/包(:26-28):本类生成的是运行期可见接口,而这些接口定义在 mixin 源集相关的包里。物理位置与「面向外部 mod 的标记 API」的语义不太匹配,且仓库内无文档说明这 3 个接口是否为公开 API。
相关条目
- CeleritasBlockTransformer - FML 侧唯一驱动者(
:40、:42-43、:50) - RFBCeleritasBlockTransformer - RFB 侧驱动者(
:25、:68-69、:81-85) - CeleritasBlockTransform - 同一 wrapper 驱动的另一个变换
- AngelicaClassDump - dump 出口
- FML Transformers -
shared/映射图的说明 - shared/package-info - 本包的类加载约束