Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commitd93fccdb21

README.md

voxbin

Binary encode/decode for voxel records. The on-disk format for shapes, world chunks, templates, and captures. Mirrors the in-memory record shape used by Voxel.Grid so a round-trip through encode / decode is lossless.

Container layout (in order on disk): fixed header (12 bytes — magic VBIN, version, kind byte, flags) → geometry header (16 bytes — size, blockSize) → palette section (count + entries) → grid section (dense or chunked, with optional RLE compression and u8/u16 index width) → optional custom-data trailer → CRC32 trailer. All multi-byte fields are little- endian. See the source for the per-byte breakdown.

Strict policies:

  • CRC32 trailer is always present. Decoder verifies it before parsing — corrupt files reject loudly.
  • Custom-data trailer is dense-only. Chunked records with hasCustomData = true raise (issue #1269 tracks the follow-up).
  • Encoder picks RLE vs raw bytes per grid section by emitting whichever is shorter; RLE flag is set only when strictly smaller.
  • Palette width auto-selects u8 (≤255 entries) vs u16 (≥256 entries).

Exports

  • M.encode(record: VoxbinRecord) -> string — encode an in-memory record to bytes. Raises on shape mismatch.
  • M.decode(bytes: string) -> VoxbinRecord — decode bytes back to a record. Verifies CRC32 first.
  • M.write(path: string, record: VoxbinRecord) -> boolean — encode + vfs.write in one step.
  • M.read(path: string) -> VoxbinRecord — vfs.read + decode in one step.

Types:

  • Vec3 = { x: number, y: number, z: number }
  • PaletteEntry = { name?, color?, atlasName?, faceTiles?, material? }
  • ChunkKey = { number } (i32 triple { x, y, z })
  • ChunkEntry = { chunkKey: ChunkKey, grid: { number } }
  • VoxbinRecord = { kind, chunked?, hasCustomData?, size, blockSize, palette?, grid?, chunks?, chunkSize?, customData? }

Usage

local Voxbin = require("@builtin::systems.voxelEngine.voxelEngine.voxbin")

-- Round-trip in memory:
local bytes  = Voxbin.encode(record)
local record = Voxbin.decode(bytes)

-- VFS wrappers:
Voxbin.write("/source/voxel/shapes/my.vbin", record)
local record = Voxbin.read("/source/voxel/shapes/my.vbin")

Notes

  • Format version is currently 1. Unknown versions raise on decode — there is no implicit migration.
  • The encoder never hardcodes a path; callers supply it. M.write / M.read are thin VFS wrappers.
  • Custom-data payload bytes are produced by a minimal Luau-native table serializer because the engine runtime exposes neither msgpack nor json at the time of writing. Issue #1270 tracks the migration to msgpack once it lands. Supported payload types are nil / boolean / number / string / nested tables with string or 1-based integer keys.
  • RLE markers are sentinel-prefixed (0xFF u8 / 0xFFFF u16) and literal payload bytes that collide with the sentinel are escaped as run-of-1 markers. Decoder always treats a sentinel as a marker start.
  • The CRC32 polynomial is the standard IEEE 802.3 / PKZIP variant (0xEDB88320 reflected) — any external CRC32 tool computes the same value over the same bytes.