Mixin 清单
基本信息
| 属性 | 值 |
|---|---|
| 类型 | 早期(EARLY)字节码注入 |
| 源包 | com.gtnewhorizons.foundation.mixins(Gradle mixinsPackage = mixins) |
| mixin 类总数 | 6 个 |
| EARLY 阶段 | 6 个(全部) |
| LATE 阶段 | 0 个 |
| 注册方式 | Mixins 枚举 + GTNHLib MixinBuilder |
| 挂载配置 | mixins.foundation.early.json |
行为
Foundation 的全部 6 个 mixin 都被声明为 Phase.EARLY——grep "Phase\." 在全仓库只命中
Mixins.java 的两处,且两处都是 Phase.EARLY,没有任何 Phase.LATE。原因是这些
mixin 要在原版网络封包类被别的 coremod / mixin 触碰之前拿到 @Overwrite 权限:S21/S22/S23
都是协议类,晚一步就可能与其他模组的注入冲突。
Mixins 枚举把 6 个类分成两组(Side 决定物理加载,Phase 决定时序):
| 枚举常量 | 显示名 | 目标模组 | Side | Phase | applyIf |
包含的 mixin |
|---|---|---|---|---|---|---|
PACKETS |
Packet Mixins |
TargetedMod.VANILLA |
Side.BOTH |
Phase.EARLY |
() -> true |
MixinS21PacketChunkData、MixinS22PacketMultiBlockChange、MixinS23PacketBlockChange、MixinS26PacketMapChunkBulk |
CLIENT_ONLY |
Client Only Mixins |
TargetedMod.VANILLA |
Side.CLIENT |
Phase.EARLY |
() -> true |
client.MixinChunk、client.MixinNetHandlerPlayClient |
枚举构造函数有两处防御性校验:targetedMods 为空抛
RuntimeException("No targeted mods specified for " + name()),applyIf 为 null 抛
RuntimeException("No ApplyIf function specified for " + name())。
excludedMods 字段存在但从未被填充(无 addExcludedMod 调用),getExcludedMods()
恒返回空列表。
Mixin 全表
| # | Mixin 类 | @Mixin 目标 |
注入方法签名(源码原样) | 注入类型 | 阶段 | Side | 修补的原版行为 |
|---|---|---|---|---|---|---|---|
| 1 | early/MixinS21PacketChunkData |
S21PacketChunkData |
public static int func_149275_c() |
@Overwrite |
EARLY | BOTH | 把「整个区块数据的字节长度」常量替换为 HandlerRegistry.getChunkPacketBytes(),即所有已注册 chunk handler 的 maxBytesPerChunk() 之和 |
| 2 | early/MixinS21PacketChunkData |
S21PacketChunkData |
public static S21PacketChunkData.Extracted func_149269_a(Chunk chunk, boolean sendUpdates, int flagSubChunks) |
@Overwrite |
EARLY | BOTH | 保留子区块掩码 extracted.field_150280_b 与 chunk.sendUpdates 的原版语义,主体改为 HandlerRegistry.writeChunkPackets(...) |
| 3 | early/MixinS22PacketMultiBlockChange |
S22PacketMultiBlockChange |
public void newConstructor(int count, short[] positions, Chunk chunk) |
普通方法(接口 IMixinS22PacketMultiBlockChange 提供) |
EARLY | BOTH | 新增能力:原版构造丢弃 Chunk 引用,此处逐项 chunk.getBlock / getBlockMetadata 填充 BlockPacketInfo[] |
| 4 | early/MixinS22PacketMultiBlockChange |
S22PacketMultiBlockChange |
public void readPacketData(PacketBuffer data) |
@Overwrite |
EARLY | BOTH | 解析出每个方块后交 HandlerRegistry.readS22Packets(info, data) 读取 block + metadata |
| 5 | early/MixinS22PacketMultiBlockChange |
S22PacketMultiBlockChange |
public void writePacketData(PacketBuffer data) |
@Overwrite |
EARLY | BOTH | 逐项交 HandlerRegistry.writeS22Packets(info, data) 写出;blockPackets == null 时写 0 长度 |
| 6 | early/MixinS23PacketBlockChange |
S23PacketBlockChange |
public void writePacketData(PacketBuffer data) |
@Overwrite |
EARLY | BOTH | x/y/z 三字段写完后交 HandlerRegistry.writeS23Packets(...) |
| 7 | early/MixinS23PacketBlockChange |
S23PacketBlockChange |
public void readPacketData(PacketBuffer data) |
@Overwrite |
EARLY | BOTH | x/y/z 三字段读完后交 HandlerRegistry.readS23Packets(...) |
| 8 | early/MixinS26PacketMapChunkBulk |
S26PacketMapChunkBulk |
private void foundation$injectS21SizeReads(PacketBuffer data, CallbackInfo ci, @Local(ordinal = 0) short chunks) |
@Inject @At(INVOKE PacketBuffer.readInt()I ordinal = 0) |
EARLY | BOTH | 批量封包里每个内嵌 S21 前面多出的「长度 int」需要先吃掉,否则后续按原版偏移读会错位 |
| 9 | early/MixinS26PacketMapChunkBulk |
S26PacketMapChunkBulk |
private int foundation$modifyI1Value1(int original, @Local(ordinal = 1) int j) |
@ModifyVariable @At(LOAD ordinal = 5) ordinal = 4 |
EARLY | BOTH | 用 s21Sizes[j] 替换原版算出的内嵌 S21 长度 |
| 10 | early/MixinS26PacketMapChunkBulk |
S26PacketMapChunkBulk |
private int foundation$modifyI1Value2(int original, @Local(ordinal = 1) int j) |
@ModifyVariable @At(LOAD ordinal = 6) ordinal = 4 |
EARLY | BOTH | 同上,第二个长度读取点 |
| 11 | early/MixinS26PacketMapChunkBulk |
S26PacketMapChunkBulk |
private int foundation$modifyI1Value3(int original, @Local(ordinal = 1) int j) |
@ModifyVariable @At(LOAD ordinal = 7) ordinal = 4 |
EARLY | BOTH | 同上,第三个长度读取点 |
| 12 | early/MixinS26PacketMapChunkBulk |
S26PacketMapChunkBulk |
private void foundation$injectSizeWrites(PacketBuffer data, CallbackInfo ci) |
@Inject @At(INVOKE PacketBuffer.writeInt(I)Lio/netty/buffer/ByteBuf; ordinal = 0) |
EARLY | BOTH | 写出前为每个内嵌 chunk 补写 field_149260_f[i].length |
| 13 | early/client/MixinChunk |
net.minecraft.world.chunk.Chunk |
public void fillChunk(byte[] data, int flagSubChunks, int flagMsb, boolean sendUpdates) |
@Overwrite |
EARLY | CLIENT | 整段原版「压缩块 + nibble 数组」解包逻辑改为 HandlerRegistry.readChunkPackets(thisObject, sendUpdates, flagSubChunks, data);flagMsb 参数保留但不再被读取 |
| 14 | early/client/MixinNetHandlerPlayClient |
NetHandlerPlayClient |
public void handleMultiBlockChange(S22PacketMultiBlockChange packetIn) |
@Overwrite |
EARLY | CLIENT | 不再逐条解析位置,改为遍历 getInfos() 并调 clientWorldController.func_147492_c(x + chunkX, y, z + chunkZ, block, metadata) |
字段注入(@Shadow / @Unique)
| Mixin | 字段 | 注解 | 源码声明 |
|---|---|---|---|
MixinS21PacketChunkData |
private static byte[] field_149286_i = null; |
@Shadow |
整个区块数据的字节数组 |
MixinS22PacketMultiBlockChange |
private ChunkCoordIntPair field_148925_b; |
@Shadow |
|
MixinS22PacketMultiBlockChange |
private BlockPacketInfo[] blockPackets; |
@Unique |
|
MixinS23PacketBlockChange |
private int field_148887_a; |
@Shadow |
x |
MixinS23PacketBlockChange |
private int field_148885_b; |
@Shadow |
y |
MixinS23PacketBlockChange |
private int field_148886_c; |
@Shadow |
z |
MixinS23PacketBlockChange |
public Block field_148883_d; |
@Shadow |
block |
MixinS23PacketBlockChange |
public int field_148884_e; |
@Shadow |
metadata |
MixinS26PacketMapChunkBulk |
private byte[][] field_149260_f; |
@Shadow |
每个内嵌 chunk 的原始数据 |
MixinS26PacketMapChunkBulk |
private int[] s21Sizes; |
无注解(既非 @Shadow 也非 @Unique) |
mixin 私有字段,但按 Mixin 规范应加 @Unique;此处靠 foundation$ 命名的方法前缀区分 |
client/MixinChunk |
public Map<ChunkPosition, TileEntity> chunkTileEntityMap; |
@Shadow |
|
client/MixinChunk |
public World worldObj; |
@Shadow |
|
client/MixinChunk |
private ExtendedBlockStorage[] storageArrays; |
@Shadow |
|
client/MixinChunk |
public boolean isLightPopulated; |
@Shadow |
|
client/MixinChunk |
public boolean isTerrainPopulated; |
@Shadow |
|
client/MixinNetHandlerPlayClient |
private WorldClient clientWorldController; |
@Shadow |
注入点接口
mixins/interfaces/ 下有 2 个接口,把 mixin 新增/改写的能力暴露给非 mixin 代码调用:
| 接口 | 方法 | 实现方 |
|---|---|---|
IMixinS22PacketMultiBlockChange |
void newConstructor(int count, short[] positions, Chunk chunk) |
MixinS22PacketMultiBlockChange |
IMixinS22PacketMultiBlockChange |
BlockPacketInfo[] getInfos() |
MixinS22PacketMultiBlockChange |
IMixinS23PacketBlockChange |
void syncBlockPacketInfo(BlockPacketInfo info) |
MixinS23PacketBlockChange |
IMixinS23PacketBlockChange |
BlockPacketInfo createBlockPacketInfo() |
MixinS23PacketBlockChange |
newConstructor 的声明是唯一走 ASM 转换器链 而非 mixin
本身调用路径的方法——S22ConstructorTransformer 生成 INVOKEINTERFACE 指向它。
数值
| 数值 | 值 |
|---|---|
| mixin 类总数 | 6 |
| EARLY 阶段数 | 6 |
| LATE 阶段数 | 0 |
@Overwrite 数量 |
9(#1、2、4、5、6、7、13、14 共 8 处 + 构造重写由 ASM 承担) |
@Inject 数量 |
2(#8、#12) |
@ModifyVariable 数量 |
3(#9、#10、#11) |
Side.CLIENT 组内 mixin 数 |
2 |
Side.BOTH 组内 mixin 数 |
4 |