体素气体模拟

模拟粒度:EBS

pV=nRT 不在方块级模拟气体,而是把世界切成 EBS(Extended Block Storage) 粒度——即 Minecraft 1.7.10 区块的 16×16×16 存储段。每个 EBS 对应原生层的一个句柄,由 EBSGasTracker(pvnrt/core/world/EBSGasTracker.java)跟踪。

类 职责
GasManager 世界级入口,持有 NativeGasSimulator,提供 getVoxelStatus / getVoxelGas / getVoxelVolume / addGas / populate
EBSGasTracker 单个 EBS 的占用状态、遮挡掩码、patch 生成、存档读写
GasDataManager 实现 ChunkAPI 的 DataManager + SubChunkDataManager,负责把气体数据写进区块 NBT
EBSExt / WorldExt 挂在原版 ExtendedBlockStorage / World 上的 @Unique 字段接口
HeightMap 辅助结构,记录列高供 flood-fill 使用

GasManager 的常量 TICK_RATE = 20(pvnrt/core/world/GasManager.java:20)是固定的 20 tick 心跳。

Voxel 位打包

Voxel(pvnrt/rust/Voxel.java)把每个体素的状态压进一个 int,位布局定义在 VoxelLike(pvnrt/rust/VoxelLike.java:5-11):

位 掩码 含义
0-11 0b1111_1111_1111 气体数据指针(12 bit)
12 0b1 << 12 是否持有气体指针
13 0b1 << 13 是否为世界大气体素
14-19 0b11_1111 << 14 遮挡掩码(6 bit,对应 6 个 ForgeDirection 面)
20-23 0b1111 << 20 打包体积(4 bit)

⚠️ 仓库 CLAUDE.md 称体素数据「packed into 16-bit integers(status + occlusion + pointer fields)」,与源码不符——Voxel.data 是 32 位 int,且没有 OCCLUDED/ACTIVE/IDLE/WORLD_ATMOSPHERE 状态枚举。GasManager.getVoxelStatus() 返回的是原生层 format!("Voxel @ {x},{y},{z}: {voxel}")(gas-sim/src/jni.rs:156)拼出的调试字符串。

遮挡

EBSGasTracker 用 6 位掩码记录每个体素哪些面可传导气体。遮挡计算见 EBSGasTracker.java:266 起:原版 block.isOpaqueCube() 为真即判 FULLY_OCCLUDED(常量 0b11_1111,EBSGasTracker.java:29);FilledBlock 类方块按填充比例 <= 0.00001 也判全遮挡。GasAwareBlock(pvnrt/block/GasAwareBlock.java)是让方块参与气体遮挡判定的接口。

populate 流程

EBSGasTracker.populate(...) 逐体素判断是否需要把气体数据分配进原生模拟器:全遮挡的体素直接 patches.addPatch(x, y, z, false, FULLY_OCCLUDED, 0) 跳过(EBSGasTracker.java:203-204)。patch 通过 VoxelPatchBuffer 以 ByteBuffer 直接内存块传给 Rust 侧。另有 save() / load(byte[]) 用于 NBT 序列化。

相关条目