Access Transformer

基本信息

属性 值
文件 src/main/resources/META-INF/chunkapi_at.cfg
构建声明 build.gradle.kts → core { accessTransformerFile = "chunkapi_at.cfg" }
规则数 4 条(3 条 public-f + 1 条 public)
getAccessTransformerClass() 返回 null
目标类数 2 个:NibbleArray、PlayerManager$PlayerInstance

⚠️ AT 确实生效,但它不经 IFMLLoadingPlugin。CoreLoadingPlugin.getAccessTransformerClass() 返回 null(见 核心 mod 与读档闸门),AT 由 build.gradle.kts 的 accessTransformerFile 交给构建插件注册。只看 getAccessTransformerClass() 会误判本 mod 没有 AT。

全部 4 条规则(verbatim)

public-f net.minecraft.world.chunk.NibbleArray field_76585_a # data
public-f net.minecraft.world.chunk.NibbleArray field_76583_b # depthBits
public-f net.minecraft.world.chunk.NibbleArray field_76584_c # depthBitsPlusFour
public net.minecraft.server.management.PlayerManager$PlayerInstance
# 修饰 目标 SRG 字段 注释给出的可读名
1 public-f(public + final) net.minecraft.world.chunk.NibbleArray field_76585_a data
2 public-f net.minecraft.world.chunk.NibbleArray field_76583_b depthBits
3 public-f net.minecraft.world.chunk.NibbleArray field_76584_c depthBitsPlusFour
4 public(整个类) net.minecraft.server.management.PlayerManager$PlayerInstance — —

⚠️ 前 3 条用 -f 保留 final。源码里 NibbleArray.depthBitsPlusFour 与 depthBits 都被重新赋值(ArrayUtil.copyArray 的 NibbleArray 重载),因此 AT 只公开化而不取消 final——赋值靠的是 mixin 之外另一条路径或 decompiler 产物,属源码层面的不一致(见下)。

用途一:NibbleArray 的三个字段

ArrayUtil.copyArray(NibbleArray, NibbleArray) 直接写 dstArray.depthBits = ... / dstArray.depthBitsPlusFour = ... / dstArray.data = ...,并读 srcArray.data / srcArray.depthBits。这 3 个字段在 1.7.10 原版是 private,没有 AT 就无法编译。见 数组工具类。

⚠️ 源码层面确实与 -f 冲突:ArrayUtil 里两行 dstArray.depthBits = srcArray.depthBits; 与 dstArray.depthBitsPlusFour = srcArray.depthBitsPlusFour; 是对 final 字段的写入。编译器会对 final 实例字段的外部写入报错,除非通过其他手段(如字段可变性的编译期宽松处理)绕过。原仓库能构建通过,说明实际编译环境允许——但 AT 的 -f 与源码写法不一致是客观事实。

对应地,NibbleManager(以及继承它的 4 个 manager)也读 getNibbleArray(subChunk).data,见 内置原版数据管理器。

用途二:PlayerManager$PlayerInstance

第 4 条把整个内部类 PlayerManager$PlayerInstance 公开化,供 PlayerInstanceMixin 混入:

@Mixin(PlayerManager.PlayerInstance.class)
public abstract class PlayerInstanceMixin {
    @Redirect(method = "sendChunkUpdate",
              at = @At(value = "NEW",
                       target = "(I[SLnet/minecraft/world/chunk/Chunk;)Lnet/minecraft/network/play/server/S22PacketMultiBlockChange;"),
              require = 1)
    private S22PacketMultiBlockChange hijackPacket(int count, short[] positions, Chunk chunk) {
        val packet = new S22PacketMultiBlockChange();
        ((CustomPacketMultiBlockChange) packet).chunkapi$init(count, positions, chunk);
        return packet;
    }
}

见 Mixin 清单。该 mixin 没有任何 @Shadow,全部依赖这条 AT。

⚠️ 这是本 mod 唯一的 public(非 public-f)规则,也是唯一针对内部类($PlayerInstance)的规则。

不经 AT 获取的字段

以下字段访问不由 AT 提供,而是靠 mixin 的 @Shadow 或包内可见性:

字段 所属类 获取方式
chunk.heightMap Chunk ChunkMixin 的 @Shadow(但 LightingManager 直接在自己的类里读写 chunk.heightMap —— 依赖 Chunk 的字段可见性,见下)
chunk.isLightPopulated Chunk ChunkMixin 的 @Shadow
packet.field_148883_d、field_148884_e S23PacketBlockChange S23PacketBlockChangeMixin 的 @Shadow public

⚠️ LightingManager.writeChunkToNBT / readChunkFromNBT / cloneChunk 在 internal.vanilla 包里直接读写 chunk.heightMap 与 chunk.isLightPopulated,而 ChunkMixin 又把这些字段 @Shadow 了出来。两条路径的可见性需求不同,源码未加注释说明 LightingManager 是靠什么拿到访问权的(既无 AT 规则、也不同包)。

规则数与构建配置的一致性

来源 内容
src/main/resources/META-INF/chunkapi_at.cfg 4 条规则
build.gradle.kts 的 core { accessTransformerFile } 指向 "chunkapi_at.cfg",位于 META-INF/ 下
CoreLoadingPlugin.getAccessTransformerClass() null
build.gradle.kts 的 core { coreModClass } "internal.core.CoreLoadingPlugin"

见 随包资源清单。

相关条目