feat(shared): pin block handles to a per-world id table in level.dat #10
No reviewers
Labels
No labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
scope/assets
scope/client
scope/networking
scope/renderer
scope/scripting
scope/server
scope/shared
scope/workspace
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Synvael/synvael!10
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/level-id-table"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What does this PR do?
I gave every world a block ID table in
level.datand made block handles come from it instead of from registration order.WorldMetadataincrates/shared/src/save/level.rsnow carriesblock_ids: Vec<ContentId>, where the ID at indexiownsBlockId(i)for the life of the world. I bumpedWORLD_FORMAT_VERSIONfrom 2 to 3.RegistryBuilderincrates/shared/src/registry.rsno longer assigns handles.registerreturns(), and I replacedfreezewithbind(&mut Vec<ContentId>). An ID already in the table keeps its index, a new ID is appended, and a table entry no registered ID matches stays as an orphan whose slot is never reused.bindreturns aBoundholding theRegistry, the number of appended IDs, and alive: Vec<bool>indexed by handle.Registryentries are nowOptionso orphaned handles resolve toNone.WorldMetadata::bind_blocksadds the block-only rule thatsyn:airsits at index 0 and reportsRegistryError::MisplacedAirotherwise.SaveError::Registrywraps registry errors.save::load_or_createincrates/server/src/save/level_file.rsseeds the table from registration order for a new world, binds on every open, logs each orphan withwarn!, and writes the table back with the timestamp.main.rsregisterssyn:airplus the three placeholder blocks throughregister_blocks.Chunk::replace_orphans_with_airturns any voxel with a non-live handle into air, including a handle past the end of the table.load_chunkinworld_server.rscalls it on saved chunks only, since the generator emits registered blocks alone.ServerWorld::newtakes thelivevector and shares it with the workers.docs/save_format.md.Why is this change necessary?
ChunkDatastores rawBlockIdhandles, and handles followed registration order. Adding, removing, or reordering a block pack would shift every handle after it and silently turn saved edits into different blocks. ADR-0005 requires content IDs to be interned to handles at runtime, and that only holds across sessions if each world records which ID owns which handle.Scope of Changes
server, shared, workspace
Testing
Automated:
cargo check -p shared -p server: passed.cargo test -p shared: 277 passed.cargo test -p server: 59 passed.cargo clippy --all-targets --all-features -- -D warnings: clean.cargo fmt --all -- --check: clean.New tests cover rebinding with the same registration, appending a new ID, reordered registration resolving to the original handles, a removed ID keeping its slot, refusing a table without air first or past the last
u16handle, refusing version 1 and version 2level.datrecords, seeding the table and keeping it across reloads, persisting an appended block,replace_orphans_with_airon orphaned and out-of-table handles, and an orphaned handle loading as air and leaving the record on write-back.Manual: no manual verification.
Additional Context
level.datmoved from format version 2 to 3 with no migrator, so existing dev worlds are refused withUnsupportedVersionand have to be recreated.RegistryBuilder::freezeis gone andregisterno longer returns a handle. Handles only exist after binding against a table.BLOCK_IDSinmain.rsis a placeholder until the data loader builds the registry. The table only covers blocks for now. Anitem_idstable follows the same rules once something persists anItemId.Checklist
dev(or a feature branch offdev) and my PR targetsdev.///doc comments for Rust) where necessary.