Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

12 Commits

Repository files navigation

Bedrock Level —— C# 存档解析

把原 C++ 项目 bedrock-level 的“解析存档”部分用 C# 重写。解析层不依赖任何第三方 NuGet 库,全部从字节流层面手写实现;区块解析完成后用 .NET 内置的 Brotli(质量 11,最高压缩率) 压缩并存入内存磁盘缓存文件,避免常驻大量未压缩数据。


1. 环境要求

  • .NET SDK 8.0 或更高(构建本仓库使用的是 .NET 10 SDK,目标框架为 net8.0
  • 操作系统:Windows / Linux / macOS 均可(纯托管代码)

2. 目录结构

csharp/
├── BedrockLevel.sln
└── src/
├── BedrockLevel/ # 类库(可复用)
│ ├── IO/ # ByteReader / ByteWriter(小端)
│ ├── Nbt/ # NBT 解析器(小端,完整 Tag 类型)
│ ├── LevelDb/ # 自研 LevelDB 读取器
│ │ ├── Varint.cs # 变长整数
│ │ ├── ByteString.cs # 字典键(字节串)
│ │ ├── BlockHandle.cs # Block 句柄
│ │ ├── Block.cs # Block 解析(前缀压缩)
│ │ ├── ZlibDecompressor.cs # zlib / raw-deflate 解压(内置)
│ │ ├── SstFile.cs # SSTable 读取
│ │ ├── LogReader.cs # WAL / MANIFEST 记录帧 + WriteBatch
│ │ ├── Manifest.cs # VersionEdit 解析
│ │ └── LevelDbStore.cs # 合并所有 entry
│ ├── Keys/ # chunk_key / actor_key / digest / village 解析
│ ├── Chunk/ # SubChunk / Biome3D / RawChunk / Chunk / Actor
│ ├── Cache/ # ChunkCache(Brotli 内存 + 文件)
│ └── Level/ # LevelDat / BedrockLevel 顶层入口
├── BedrockRender/ # 渲染库(生成区块剖面图)
│ ├── Resources/ # biome_color.json / block_color.json(嵌入资源)
│ ├── ColorPalette.cs # 生物群系 / 方块调色板 + 生物群系着色
│ ├── ChunkRenderer.cs # 区块 → 像素图(Surface / Biome / Height)
│ └── BedrockRender.csproj
└── BedrockRender.Demo/ # Avalonia 示例(读全部区块并拼接到画布 / 导出 PNG)

3. 编译

在项目根目录下执行:

cd csharp
dotnet build -c Release

仅编译类库:

dotnet build src/BedrockLevel/BedrockLevel.csproj -c Release

4. 运行控制台示例

4.1 解析一个真实存档

把一个 Minecraft 基岩版存档目录(里面应有 level.datdb/ 子目录)传给程序:

dotnet run --project src/BedrockLevel.Demo/BedrockLevel.Demo.csproj -- <存档目录> [缓存目录]

示例输出:

Opening save: D:\Saves\MyWorld
Level name : MyWorld
Spawn : (12, 64, -8)
DB keys : 12345
Chunks : 1024
Cached 1024 raw chunks in 123 ms.
Cache dir : C:\Users\...\bedrock-chunk-cache
Raw bytes : 12,345,678
Compressed bytes : 1,234,567
Compression ratio: 10.00% (lower = smaller)
Process memory : 45,678,912 bytes
Sample chunk [0, 0, 0]:
version : New
entities : 3
block entities : 2
top biome : 1
surface block : minecraft:grass

4.2 自检测试(无需真实存档)

程序内置 --selftest,会现场合成一个合法的 LevelDB SST 文件 + level.dat,然后跑完整“解析 → 压缩缓存 → 回读”链路,验证自研读取器、NBT、区块解析与缓存往返是否正确:

dotnet run --project src/BedrockLevel.Demo/BedrockLevel.Demo.csproj -- --selftest

输出 [selftest] PASS 即通过。

5. 作为类库使用

引用 BedrockLevel 项目后,典型流程如下:

usingBedrockLevel.Keys;usingBedrockLevel.Level;usingBedrockLevel.Cache;usingBedrockLevel.Chunk;// 1) 打开存档(读取 level.dat + db/)varlevel=newBedrockLevel();if(!level.Open(@"D:\Saves\MyWorld"))return;// 2) 元数据stringname=level.Dat.LevelName;varspawn=level.Dat.Spawn;// 3) 枚举所有区块坐标foreach(varcpinlevel.ChunkPositions()){// 4) 读取原始区块(未解析的键值集合)varraw=level.GetRawChunk(cp);// 5) 完整解析为结构化区块varchunk=level.GetChunk(cp);if(chunk==null)continue;stringtopBlock=chunk.GetBlockName(0,chunk.GetHeight(0,0),0);bytetopBiome=chunk.GetTopBiome(0,0);}// 6) 压缩缓存:内存 + 磁盘文件(Brotli 质量 11)varcache=newChunkCache(@"D:\cache");// 不传路径则仅内存foreach(varcpinlevel.ChunkPositions()){varraw=level.GetRawChunk(cp);if(raw.Loaded())cache.Store(cp,raw);// 自动压缩后写入}// 回读(从压缩缓存还原 RawChunk)varrestored=cache.Load(newChunkPos(0,0,0));doubleratio=cache.CompressionRatio;// 压缩后 / 压缩前

6. 压缩与缓存说明

  • 压缩算法:.NET 内置 System.IO.Compression.BrotliStreamCompressionLevel.SmallestSize 对应质量 11(最高压缩率),不涉及任何 NuGet 依赖
  • 缓存后端(ChunkCache):
    • 内存Dictionary<(x,z,dim), byte[]> 存放压缩后的字节,原 RawChunk 不常驻,回读时再解压。
    • 文件:每个区块写成 <x>.<z>.<dim>.blkcacheBrotli 压缩流),几乎不占内存。
  • 缓存内容是 RawChunk.ToRaw() 的自定义二进制格式(魔数 BCHK + 普通键 + 子区块 + 实体摘要 + 实体),因此可无损还原并经 Chunk.LoadFromRawChunk 重新解析。

7. 实现要点与约束

  • LevelDB 读取范围:完整只读实现 —— 读取 CURRENT 定位 MANIFEST,解析 MANIFEST 得到存活 SST 集合与 last_sequence;读取所有 *.sst / *.ldb 数据文件;读取除 MANIFEST 外的所有 *.log(WAL)回放 WriteBatch。按 (user key, sequence, type) 合并,删除标记(type=0)隐藏对应键。
  • Block 解压:Block 尾部 1 字节为压缩 id:0=无、1=Snappy(Snappy.cs 自研解压,Google Snappy block 格式)、2=zlib(RFC1950)、4=raw-deflate(RFC1951)。Snappy 为 Minecraft 基岩版 LevelDB 的默认压缩,已完整实现,无需任何 NuGet 依赖。
  • NBT:基岩版 NBT 为小端,已与 palette.cpp 对齐。
  • 均匀方块层bits==0 的 uniform 层直接跟单个调色板 Compound,没有 palette 长度前缀(与 sub_chunk.cpp 行为一致)。

8. 已知限制

  • 没有真实基岩版存档样本的情况下,SST / MANIFEST / WAL / zlib block 的真实路径已按规范实现并通过合成数据自测,但未经真实存档端到端验证。若提供真实存档目录,可据此校准压缩 id 映射或任何字节偏移。
  • 归档压缩支持 bedrock LevelDB 使用的 None / Snappy / zlib / raw-deflate;若遇到其它压缩类型(如 zstd),该 block 会回退为原样并被判为无效,跳过该块而非崩溃。
  • Chunk 目前解析了地形(子区块)、生物群系/高度图、实体、方块实体、计划刻、HSA;color.cpp 的方块颜色着色与图片导出已移植到 BedrockRender(见第 9 节)。

9. 区块渲染(BedrockRender + Avalonia)

BedrockRender 把解析后的 Chunk 渲染成每列一个像素的剖面图;BedrockRender.Demo 用 Avalonia 读取一个存档的全部区块(可按维度筛选),逐区块渲染后拼接到一张统一画布,既可在窗口中查看,也可直接导出 PNG。

9.1 三种视图模式

模式含义颜色来源
Surface顶面地图:取每列地表最高非空气方块block_color.json + 生物群系对水/草/叶的着色(color.cppblend_color_with_biome
Biome生物群系地图:取每列顶层生物群系biome_color.json
Height高度地图:按维度 Y 范围归一化着色(蓝→绿→黄→红)列顶实心方块高度

9.2 交互式界面(默认窗口模式)

直接运行即可打开窗口;无需预先在命令行指定存档目录,可在界面里用「选择存档文件夹」按钮挑选存档。

dotnet run --project src/BedrockRender.Demo/BedrockRender.Demo.csproj
# 也可直接带上存档目录,启动后自动加载:
dotnet run --project src/BedrockRender.Demo/BedrockRender.Demo.csproj -- <存档目录>

窗口功能:

  • 选择存档文件夹:点击按钮用系统文件夹选择器定位 Bedrock 存档(db/ + level.dat)。
  • 维度:主世界 / 下界 / 末地(对应 0/1/2),切换后重新渲染。
  • 模式:表面 / 生物群系 / 高度(ViewMode.Surface/Biome/Height)。
  • 精度:每区块像素(1/2/4/8),即区块底图的渲染分辨率。
  • 适应窗口:一键把视角缩放到刚好容纳整个世界。
  • 平移:在地图上按住左键拖拽移动预览视角。
  • 缩放鼠标滚轮以光标为中心放大 / 缩小。
  • 网格分割线:缩放较大(每区块 ≥ 6 px)时绘制每个区块一条分割线;缩放较小(8 区块间隔 ≥ 6 px)时改为每 8 个区块一条分割线,避免过密。
  • 坐标显示:状态栏实时显示光标处的世界坐标 (x,z)区块坐标 (cx,cz);地图边缘会对齐 8 区块网格线标注对应的世界 X / Z 坐标。
  • 渐进式多线程渲染:区块按 8×8 区块一组(区块块)在后台多线程渲染,渲染完一组立即上屏,无需等待全部区块加载完毕。
  • 非阻塞加载与进度:打开窗口后立刻显示,读取 / 枚举 / 缓存区块以及渲染过程都在后台进行并显示进度条与状态文字;无需在命令行传参。
  • 缓存目录(边渲染边缓存):渲染每个区块块时,会把它以 Brotli(质量 11)压缩写入存档目录下的 .cache/(文件名 <x>.<z>.<dim>.blkcache)。缓存是写入即落盘、不驻留内存,压缩用的字节缓冲立刻可被 GC 回收,内存占用低。
  • 低内存瓦片:屏幕上显示的瓦片位图保存在有上限的 LRU 缓存中(默认最多 2000 张),滚出视野或超出上限的瓦片会被 Dispose 及时释放;多线程渲染时 CPU 占用高无妨。

9.3 导出 PNG(headless)

dotnet run --project src/BedrockRender.Demo/BedrockRender.Demo.csproj -- <存档目录> --save world.png
# 完整参数
dotnet run --project src/BedrockRender.Demo/BedrockRender.Demo.csproj -- <存档目录> \
--dim 1 \ # 0=主世界(默认) 1=下界 2=末地
--mode biome \ # surface(默认) | biome | height
--scale 8 \ # 每列像素数(默认 4)
--save out.png # 启用 headless 导出

9.3 作为类库使用

usingBedrockLevel.Level;usingBedrockRender;varlevel=newBedrockLevel.Level.BedrockLevel();level.Open(@"D:\Saves\MyWorld");varpalette=ColorPalette.LoadEmbedded();// 或从文件 LoadFiles(...)varrenderer=newChunkRenderer(palette);foreach(varcpinlevel.ChunkPositions(dimension:0))// 按维度筛选{varchunk=level.GetChunk(cp);if(chunk==null)continue;// 渲染成 16*scale × 16*scale 的 RGBA 缓冲varrc=renderer.Render(chunk,ViewMode.Surface,scale:4);// 直接把 RGBA 贴到任意 BGRA 帧缓冲上rc.BlitTo(bgraSpan,canvasWidth,canvasHeight,destX,destY);}

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages