ChunkAPI
[!INFO] Git Commit:
d7b4afe| Updated: 2026-05-17
ChunkAPI(modid chunkapi)不是一个内容 mod,而是一个区块数据序列化库 + core mod。它不注册任何方块、物品、实体、维度、群系或结构,玩家在游戏里看不到它。它的全部作用是:
- 提供一套 API,让其他 mod 把自己的一小段数据挂进区块(存盘、读盘、区块克隆、网络同步四件事统一由它分发);
- 用 core mod + 10 个 mixin + 4 条 Access Transformer 把原版区块读写与封包代码的挂钩点接管掉;
- 把原版自己的区块字段(
Biomes、Blocks、Add、Data、SkyLight、BlockLight、HeightMap、LightPopulated)重新实现成 6 个内建数据管理器,从而对区块数据做统一的分片打包。
mcmod.info 里唯一的描述是仓库 README 的第一句:“Chunk data, simplified.”
⚠️ 这个 mod 不是什么(对任务简报的核实)
任务简报把它描述为"被许多世界生成 mod 使用的区块/世界操作库",并预设了"世界生成钩子、人口生成事件、方块替换/扫描、结构/特征放置、群系、配置项、事件类型"等维度。源码核实后,这些绝大部分不存在:
- 本 mod 不做任何世界生成或人口生成。
IWorldGenerator、WorldGen、populate(、PopulateChunk、addGenerator、MapGenStructure、Feature、BiomeProvider、WorldProvider、registerDimension在src/下全部零命中。它是读写已有区块,不是生成区块。 BiomeManager不是群系。它只是把原版Chunk.getBiomeArray()的 256 字节搬进打包框架,不定义任何群系。因此本 wiki 没有biome/目录。- 没有方块、物品、实体、TileEntity、多方块结构、附魔、Buff/Debuff、成就、维度、热键、指令、事件。
- 不写任何配置文件。
- 不调用矿词典、不注册配方、无任何液体/能量接口。
完整的 grep 证据见页面底部"本 mod 没有的东西"。
基本信息
| 属性 | 值 |
|---|---|
| 仓库目录 | ChunkAPI(FalsePattern 原始,LegacyModdingMC 维护的 GTNH fork) |
| modid | chunkapi |
| 显示名 | ChunkAPI |
@Mod 类 |
com.falsepattern.chunk.internal.ChunkAPI(internal 包,不是根包!) |
| Minecraft | 1.7.10(acceptedMinecraftVersions = "[1.7.10]") |
| 源码规模 | 47 个 .java 文件(其中 7 个是 package-info.java) |
| 根包 | com.falsepattern.chunk(group = "com.falsepattern") |
| 作者 | FalsePattern(mcmod.info 的 authorList),图标 Houstonruss |
| 许可 | LGPLv3 + api 包的"additional permissions"(见 随包资源清单) |
| core mod | coreModClass = "internal.core.CoreLoadingPlugin" |
| AT | accessTransformerFile = "chunkapi_at.cfg"(4 条规则) |
| mixin | mixin.pkg = "internal.mixin.mixins",10 个类,框架 com.gtnewhorizon.gtnhmixins |
| API 包 | api.packages = listOf("api") |
| 内容维度 | 仅 setting(9 个条目) |
| 配置文件 | 无(不生成任何 .cfg) |
| 构建 | com.falsepattern.fpgradle-mc 2.1.0(Kotlin DSL);gradle.properties 仅 2 行,无 mod 侧配置键 |
| 发布 | CurseForge 844484 / Modrinth y0vBUOla / mvn.falsepattern.com |
⚠️ @Mod 注解类在 internal 包里:com.falsepattern.chunk.internal.ChunkAPI。类名与 mod 名同名,但从源码树位置看不出 modid——modid 实际定义在 build.gradle.kts 的 mod { modid = "chunkapi" },@Mod 里引用的是构建期生成的 Tags.MOD_ID。
⚠️ gradle.properties 里没有 coreModClass / usesMixins / accessTransformersFile / mixinPlugin——这些键全部在 build.gradle.kts 的 minecraft_fp { } 块内,用 Kotlin DSL 写法(core { coreModClass = ...; accessTransformerFile = ... }、mixin { pkg = ... })。gradle.properties 全文只有两行:
org.gradle.caching=true
org.gradle.configuration-cache=true
全部 9 个条目
机制与设定(9 个)
| 条目 | 内容 |
|---|---|
| API 接口 | 核心:DataManager 根接口 + 6 个嵌套接口(PacketDataManager / CubicPacketDataManager / BlockPacketDataManager / StorageDataManager / ChunkDataManager / SubChunkDataManager)的完整方法表;DataRegistry 门面;OrderedManager 排序 |
| 数据注册表 | DataRegistryImpl(558 行)的注册/排序/停用规则;ordering 约定;maxPacketSize 累加公式;特权 NBT 命名空间;未知 manager 的降级处理 |
| 内置原版数据管理器 | 6 个内建 manager(blockid/metadata/skylight/blocklight/lighting/biome)各自实现的接口、NBT 键、包体积;基类 VanillaManager 与 NibbleManager |
| 序列化流水线 | 6 条独立路径:存盘、读档、区块包 S→C、单方块包、多方块包、level.dat;每步的调用顺序与 verbatim 注入点 |
| 核心 mod 与读档闸门 | CoreLoadingPlugin(IFMLLoadingPlugin + IEarlyMixinLoader)与 ChunkAPICoreModContainer(WorldAccessContainer);读档兼容闸门:manager 增删/版本变更 → 弹窗确认 → 自动世界备份 |
| Mixin 清单 | 10 个 mixin 类的 @Mixin 目标、手法、verbatim 注入点与 SRG 别名;Mixin 枚举 5 个分组;VanillaCore/ThermosCore 互斥;Thermos 与 Spool 条件 |
| Access Transformer | META-INF/chunkapi_at.cfg 的 4 条规则逐条说明;为何 getAccessTransformerClass() 返回 null 却仍然生效 |
| 数组工具类 | ArrayUtil 的 11 个 copyArray 重载与统一的 null/长度契约;NibbleArray 特例为何依赖 AT |
| 随包资源清单 | src/main/resources 下 8 个文件逐个列出(含字节数);mcmod.info 全文;两份内容相同的 mixin JSON |
API 表面总览
对外 API 只有 4 个类型,全部在 com.falsepattern.chunk.api 包:
| 类型 | 种类 | 消费者做什么 |
|---|---|---|
DataManager |
接口(含 6 个嵌套接口) | 声明"我的数据要存/读/发/收/克隆" |
DataRegistry |
@ApiStatus.NonExtendable 静态门面 |
注册、停用、查询、广播克隆 |
OrderedManager |
普通类(Comparable) |
排序键载体 |
ArrayUtil |
@ApiStatus.NonExtendable 工具类 |
11 个重载的原地数组复制 |
DataManager 的 6 个嵌套接口构成"一个类可以同时是多种身份"的设计,注册表用 instanceof 逐个分流:
| 接口 | 父接口 | 粒度 | 内建实现者 |
|---|---|---|---|
PacketDataManager |
DataManager |
整区块(随区块包) | BlockIDManager、MetadataManager、BlocklightManager、SkylightManager、BiomeManager(5 个) |
CubicPacketDataManager |
DataManager |
单 cube(仅 CubicChunks,@ApiStatus.Experimental) |
BlockIDManager、MetadataManager、BlocklightManager、SkylightManager(4 个) |
BlockPacketDataManager |
DataManager |
单方块(随 S22/S23 包) | BlockIDManager、MetadataManager(2 个) |
StorageDataManager |
DataManager |
版本与提示文案 | 全部 6 个 |
ChunkDataManager |
StorageDataManager |
每区块 1 次 | LightingManager、BiomeManager(2 个) |
SubChunkDataManager |
StorageDataManager |
每区块 16 次 | BlockIDManager、MetadataManager、BlocklightManager、SkylightManager(4 个) |
⚠️ PacketDataManager 的 5 个内建实现者中有 4 个是"隐式"的——MetadataManager / BlocklightManager / SkylightManager 的源码里没写 implements PacketDataManager,它们从 NibbleManager 父类继承了这个身份。LightingManager 则是唯一不参与任何网络同步的内建 manager。
6 条序列化路径
| # | 路径 | 服务端挂钩点 | 客户端挂钩点 |
|---|---|---|---|
| 1 | 存盘 | AnvilChunkLoader.writeChunkToNBT @Inject(HEAD, cancellable) |
— |
| 2 | 读档 | AnvilChunkLoader.readChunkFromNBT @Inject(HEAD) + CallbackInfoReturnable |
— |
| 3 | 区块包 S→C | S21PacketChunkData.func_149269_a @Overwrite + func_149275_c() @Overwrite |
Chunk.fillChunk @Overwrite(@SideOnly(CLIENT)) |
| 4 | 单方块包 | S23PacketBlockChange.<init> @Inject(RETURN) 或 chunkapi$init |
NetHandlerPlayClient.handleBlockChange @Inject(RETURN) |
| 5 | 多方块包 | PlayerInstance$PlayerInstance.sendChunkUpdate @Redirect(NEW) → 拆成 S23PacketBlockChange[] |
NetHandlerPlayClient.handleMultiBlockChange @Overwrite |
| 6 | level.dat | ChunkAPICoreModContainer.getDataForWriting / readData(WorldAccessContainer) |
— |
全部靠 mixin 挂载,没有任何 Forge 事件。 每条路径的详细顺序见 序列化流水线。
数据包格式
区块包体(DataRegistryImpl.writeToBuffer / readFromBuffer,小端序):
putInt(manager 数量)
for each manager(按 ordering 升序):
putInt(id 的 UTF-8 字节长度) + id 字节
putInt(该段实际长度) ← 预留 4 字节槽后回填
manager.writeToBuffer(chunk, subChunkMask, forceUpdate, slice)
单方块包体(writeBlockPacketToBuffer)没有长度前缀,靠注册键精确查找:
writeInt(blockPacketManagers 数量)
for each manager(按 ordering 升序):
writeStringToBuffer(ord.id)
manager.writeBlockPacketToBuffer(packet, buffer)
⚠️ 两条路径的容错性不同:区块包遇到未注册的 manager 会打 error 并跳过该段(可降级),单方块包遇到未注册的 manager 直接 NPE。
核心机制速览
包体积预算
S21PacketChunkData.func_149275_c() 被 @Overwrite 成 DataRegistryImpl.maxPacketSize(),原版返回固定 12288,ChunkAPI 换成按注册表累加的动态值:
maxPacketSize 初值 4
每注册一个 PacketDataManager: maxPacketSize += 4 + id.length + 4 + maxPacketSize()
| 内建 manager | 注册键 | maxPacketSize() |
|---|---|---|
BlockIDManager |
minecraft:blockid |
98306 |
MetadataManager |
minecraft:metadata |
32768 |
BlocklightManager |
minecraft:blocklight |
32768 |
SkylightManager |
minecraft:skylight |
32768 |
BiomeManager |
minecraft:biome |
256 |
⚠️ BlockIDManager 一家就占了预算的一半(2 + 16 × (4096 LSB + 2048 MSB)),且这 5 个 manager 全部 ordering 传 0(ChunkAPI.init),实际遍历序退化成按 id 字典序。全部累加后 maxPacketSize = 197018(按公式推算)。
读档兼容闸门
这是本 mod 唯一有用户可见行为的部分。level.dat 里记录每个第三方 manager 的 version 与 uninstallMessage(跳过所有 minecraft: 前缀)。读档时 DataRegistryImpl.verifyManagerCompatibility 比对三类差异:
| 差异 | 报告内容 |
|---|---|
| 已移除的 manager | id + 存档版本 + "Uninstall information: " |
| 新增的 manager | id + 当前版本 + "Install information: "(newInstallDescription() 非 null 时) |
| 不兼容版本变更 | "<id> has changed versions from <旧> to <新>" + "Upgrade information: " |
任一命中 → 拼风险警告 → StartupQuery.confirm(...)(用户不确认就 abort()) → ZipperUtil.backupWorld()(备份失败也 abort())。
⚠️ 内建 6 个 manager 继承 VanillaManager 的默认实现(version() 返回 ""、三个提示方法分别返回 null/""/null),因此永远不会触发任何一类警告。
Mixin 分组与加载时机
Mixin 枚举 5 个分组,4 个 EARLY + 1 个 LATE:
| 分组 | Phase | 条件 | mixin |
|---|---|---|---|
VanillaCore |
EARLY | !hasThermos() |
common/vanilla/S26PacketMapChunkBulkMixin |
ThermosCore |
EARLY | hasThermos() |
common/thermos/S26PacketMapChunkBulkMixin |
CommonCore |
EARLY | 无条件 | base/ 4 个 + client/vanilla/NetHandlerPlayClientMixin |
Core_NoSpool |
EARLY | avoid(Spool) |
base/AnvilChunkLoaderMixin + client/vanilla/ChunkMixin |
Compat_LookingGlass |
LATE | require(LookingGlass) |
common/lookingglass/PacketChunkInfoMixin |
⚠️ 只有 Core_NoSpool 带 avoid(Spool)——Spool 存在时被跳过的只有 AnvilChunkLoaderMixin 与 ChunkMixin,CommonCore 的 5 个 mixin 照常应用。
⚠️ hasThermos() 的实现是 Class.forName("thermos.Thermos")。
⚠️ 两份 mixin JSON 都没有 "mixins" 数组,类清单运行时注入。
Access Transformer(4 条)
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
⚠️ CoreLoadingPlugin.getAccessTransformerClass() 返回 null——AT 走 build.gradle.kts 的 accessTransformerFile,不经 IFMLLoadingPlugin。
⚠️ 前 3 条用 -f 保留 final,但 ArrayUtil.copyArray(NibbleArray, NibbleArray) 里确实对这两个字段做了赋值(源码与 AT 不一致)。
源码中的不一致(均为原文,未修正)
| 位置 | 现象 |
|---|---|
BlocklightManager.cloneSubChunk |
from.setBlocklightArray(...) 应为 to.(对照 SkylightManager / MetadataManager 同名方法)——克隆时 blocklight 数据留在源 subChunk,目标不被替换 |
ChunkAPI.init 的注册顺序 |
6 个 manager 全传 ordering 0,实际遍历序按 id 字典序而非源码书写顺序 |
readLevelDat 第 392 行 |
val managerCompat = verifyManagerCompatibility(tag) 命名与语义相反(名为 compat 实为差异报告) |
S22PacketMultiBlockChangeMixin.writePacketData |
subPackets == null 分支写 writeInt(0),正常分支写 writeVarIntToBuffer(length)——两分支编码方式不一致,读侧按 varInt 解析 |
readBlockPacketFromBuffer |
blockPacketManagers.get(id) 返回 null 时直接 NPE,无降级(区块包路径有) |
ChunkMixin.fillChunk |
形参 subChunkMSBMask 被完全忽略(MSB 信息由 BlockIDManager 自己的 2 字节头承载) |
两份 S26PacketMapChunkBulkMixin.readPacketData |
subChunkMSBMasks 分配为 new int[chunkCount] 但从不写入也从不读取 |
SkylightManager.writeSubChunkToNBT |
无天空维度下写的全 0 数组长度取自 getBlocklightArray().data.length 而非 getSkylightArray() |
NibbleManager 两个 Chunk 级方法 |
只判 subChunk != null,不判 getNibbleArray(subChunk).data != null |
ArrayUtil 的 @Contract |
声明只有 "!null, null -> new",但实现里 src.length != dst.length 也返回新数组 |
ArrayUtil 与 AT 的 -f |
对 final 的 depthBits / depthBitsPlusFour 赋值 |
LightingManager |
在 internal.vanilla 包直接读写 chunk.heightMap / chunk.isLightPopulated,既无 AT 规则也不同包,源码未说明可见性来源 |
PacketChunkInfoMixin.handle |
解压失败重请求时把 subChunkMask 传给 PacketRequestChunk.createPacket(xPos, subChunkMask, zPos, dim);转发调用把 yMSBPos 硬编码 (short)0 |
MixinHelper.server(...) / Mixin.server(...) |
无调用点,mixins/server/ 目录不存在(只有 client/ 与 common/) |
plugin.fplib.TargetMod |
与 plugin.TargetMod 枚举重名结构,无任何引用 |
mcmod.info 的 dependencies |
[] 空数组,但实际硬依赖 gtnhmixins / lombok / org.jetbrains:annotations,可选 lookingglass |
ChunkAPICoreModContainer 的 dependencies |
加了一条 DefaultArtifactVersion(Tags.MOD_ID, Tags.MOD_VERSION),core 容器依赖自己的父 mod |
本 mod 没有的东西(grep 证据)
以下断言均已在 /Users/evlos/a/mirror/ChunkAPI/src/main/java/ 下全量检索核实:
| 维度 | 检索命令 | 结果 |
|---|---|---|
| 方块注册 | grep -rn "registerBlock|new Block(|Block.register" |
0 命中 |
| 物品注册 | grep -rn "registerItem|new Item(|Item.register" |
0 命中 |
| 实体 / TileEntity 注册 | grep -rn "registerEntity|registerTileEntity|registerModEntity|EntityRegistry" |
0 命中 |
| 世界生成 / 人口生成 / 结构 / 特征 | grep -rn "IWorldGenerator|WorldGen|populate(|PopulateChunk|addGenerator|MapGenStructure|Feature|BiomeProvider|WorldProvider|registerDimension" |
0 命中 ⚠️ 这是对任务简报最关键的一条修正 |
| Forge 事件 | grep -rn "@SubscribeEvent|net.minecraftforge.event|Event(" |
0 命中(本 mod 不声明也不监听任何事件) |
| 指令 | grep -rn "CommandHandler|ICommand|addChatCommand|CommandBase|getCommandName" |
0 命中 |
| 热键 | grep -rn "KeyBinding|keybinding|registerKeyBinding" |
0 命中 |
| 配置 | grep -rn "Configuration|@Config|ForgeConfigSpec|config.get|Configuration.getConfig" |
0 命中,且 src/main/resources 下无任何 *.cfg 模板——不生成配置文件 |
| 附魔 / Buff / 成就 / 群系注册 | grep -rn "Enchantment|PotionEffect|Achievement|BiomeGen|registerBiome" |
0 命中 |
| 多方块 | grep -rn "extends TileEntity|Multiblock|IMultiblock" |
0 命中 |
IClassTransformer |
grep -rn "IClassTransformer" |
0 命中;getASMTransformerClass() 返回 new String[0] |
| 服务端专属 mixin | ls src/.../mixin/mixins/ |
只有 client/ 与 common/,无 server/ |
assets/ 目录 |
ls src/main/resources/assets |
不存在——无 zh_CN.lang、无模型、无贴图、无配方 |
| 矿词典 / 配方 / 液体 / 能量 | grep -rn "OreDictionary|addMapping|GameRegistry.addRecipe|IFluidHandler|IEnergyHandler" |
0 命中 |
唯一 @Mod 注解 |
grep -rn "@Mod(" |
仅 1 处:internal/ChunkAPI.java:36 |
@Mod.EventHandler 方法体 |
读 internal/ChunkAPI.java:43-50 |
只有 6 行 DataRegistry.registerDataManager(...),无代理、无渲染、无网络注册 |
⚠️ 关于"实体"的唯一两处 net.minecraft.entity import 都与实体内容无关:AnvilChunkLoaderMixin:35 的 net.minecraft.entity.Entity(用于写盘时遍历实体列表)与 PacketChunkInfoMixin:35 的 net.minecraft.entity.player.EntityPlayer(LookingGlass 封包的接收者参数)。
因此只建了 setting/ 一个目录;block、item、entity、multiblock、enchantment、dimension、keybinding、potioneffect、biome、structure、achievement、item-effect 共 12 个维度无内容,未建目录、未在正文里留占位。
相关条目
- API 接口 — 从这里开始读
- 序列化流水线 — 6 条路径的详细顺序
- 核心 mod 与读档闸门 — 唯一有用户可见行为的部分
- Mixin 清单 — 10 个 mixin 与 verbatim SRG 别名