内置区块处理器

基本信息

属性 值
类型 内置封包数据序列化器
所在包 com.gtnewhorizons.foundation.builtin
处理器总数 6 个
同时实现两个接口的 2 个(BlockLSBHandler、MetadataHandler)
分配缓冲区总量 196,896 字节(固定,按注册累加)

行为

6 个内置处理器在 Foundation.preInit(FMLPreInitializationEvent) 中被 new 出来并 注册到 处理器注册表。注册顺序即下表顺序,且客户端与服务端必须一致。

注册代码(Foundation.java:29-44):

HandlerRegistry.registerChunkPacketHandler(blockLSBHandler);
HandlerRegistry.registerChunkPacketHandler(metadataHandler);
HandlerRegistry.registerChunkPacketHandler(blockLightHandler);
HandlerRegistry.registerChunkPacketHandler(skylightHandler);
HandlerRegistry.registerChunkPacketHandler(blockMSBHandler);
HandlerRegistry.registerChunkPacketHandler(biomeHandler);

HandlerRegistry.registerBlockPacketHandler(blockLSBHandler);
HandlerRegistry.registerBlockPacketHandler(metadataHandler);

处理器全表

# 处理器 实现的接口 maxBytesPerChunk() 源码注释 编解码的数据
1 BlockLSBHandler ChunkPacketHandler, BlockPacketHandler 4096 * 16 = 65,536 16 * 16 * 16 bytes per section * 16 sections ExtendedBlockStorage.getBlockLSBArray()(方块 ID 低 8 位,每子区块 4096 字节)
2 MetadataHandler ChunkPacketHandler, BlockPacketHandler 2048 * 16 = 32,768 16 * 16 * 16 / 2 bytes per chunk section getMetadataArray()(metadata 半字节数组,4 位/方块)
3 BlockLightHandler ChunkPacketHandler 2048 * 16 = 32,768 同上 getBlocklightArray()(方块光照半字节数组)
4 SkylightHandler ChunkPacketHandler 2048 * 16 = 32,768 同上 getSkylightArray()(天空光照半字节数组)
5 BlockMSBHandler ChunkPacketHandler (2048 + 2) * 16 = 32,800 (16 * 16 * 16 / 2) bytes per section * 16 sections + 2 bytes for flags getBlockMSBArray()(方块 ID 高位)+ 每子区块 2 字节 flagMsb
6 BiomeHandler ChunkPacketHandler 256 16 * 16 bytes for biome data chunk.getBiomeArray()(每列 1 字节群系 ID)

累加:65536 + 32768 + 32768 + 32768 + 32800 + 256 = 196,896 字节, 即 HandlerRegistry.getChunkPacketBytes() 的返回值,也是 MixinS21PacketChunkData 分配的静态缓冲区大小。

实际写入量通常远低于该上限:注册表为每个 handler 分配 maxBytesPerChunk() 的定长切片,但只回填真实长度(slice.position()),未用的部分不进入网络数据。

逐个处理器的行为差异

BlockLSBHandler(唯一同时写 block 字段的处理器)

单方块封包路径上,它负责方块 ID 本身:

方向 实现
写 data.writeVarIntToBuffer(Block.getIdFromBlock(info.getBlock()))
读 info.setBlock(Block.getBlockById(data.readVarIntFromBuffer()))

用的是 VarInt 而非固定 short,因此 > 32767 的扩展方块 ID 也可传输。

源码 builtin/BlockLSBHandler.java:24 有一个未使用的局部变量 int start = buffer.position();(后续无引用)——不影响输出,因为 处理器注册表 用的是切片返回的 slice.position()。

MetadataHandler(唯一同时写 metadata 字段的处理器)

方向 实现
写 data.writeByte(info.getMetadata())
读 info.setMetadata(data.readUnsignedByte())

读写不对称:写用 writeByte(有符号,metadata > 127 会变负), 读用 readUnsignedByte(0~255),往返后 metadata 高 8 位的符号位信息不保留。

BlockLightHandler / MetadataHandler(chunk 路径)

逐子区块 get*Array().data 直接 buffer.put / buffer.get 整段拷贝, 写入条件统一为 storageArrays[l] != null && (!sendUpdates || !isEmpty()) && (flagSubChunks & 1 << l) != 0。

SkylightHandler(读写不对称)

方向 是否检查 hasNoSky
写 是 —— 整个方法体包在 if (!chunk.worldObj.provider.hasNoSky) 内,无天空维度不写任何数据
读 否 —— 直接按 flagSubChunks 读入

由于注册表按真实长度切片,读取侧即使遇到零长分片也能正确跳过,故此不对称不会造成 错位。代价是无天空维度仍占用 32,768 字节预算。

BlockMSBHandler(唯一两趟写、唯一会改动区块数组结构)

写路径:先 buffer.position(start + 2) 预留 flagMsb 的 2 字节 → 第一趟扫描统计 哪些子区块的 getBlockMSBArray() 非 null 并置位 → 第二趟写入 MSB 数据 → 回退到 start 处 putShort((short) (flagMsb & 65535)) → 恢复 position。

读路径:先读 flagMsb;对置位的子区块,MSB 数组为 null 时先调 createBlockMSBArray() 再读;未置位但 sendUpdates 且 MSB 数组已存在时调 clearMSBArray() 清理。

即 MSB 数组是按需创建的:绝大多数方块 ID < 32768 时 flagMsb 恒为 0,不传输 任何 MSB 数据,但那 32 字节(2 × 16 子区块)仍计入预算。

BiomeHandler(只搬运已有群系 ID)

writeChunkPacket / readChunkPacket 都先判 if (sendUpdates),然后整体 buffer.put(chunk.getBiomeArray()) / buffer.get(biomeArray),共 256 字节。

需要强调:本处理器不定义、不注册、不生成任何群系,它只是把 Chunk 里已存在的 群系 ID 数组搬进封包。因此 Foundation 没有群系维度的内容。

数值

数值 值
内置 chunk handler 数 6
内置 block handler 数 2(与上表 #1、#2 为同一批对象)
chunkPacketBytes 合计 196,896
其中 MSB 标志字节 32(2 × 16 子区块)
其中群系字节 256
子区块数(getBlockStorageArray().length) 16
单方块封包中 block 字段编码 VarInt
单方块封包中 metadata 字段编码 1 字节(写有符号 / 读无符号)

相关条目