feat(shared): intern namespaced content ids in a freezable registry #9
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!9
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/content-ids"
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 added a
registrymodule tocrates/sharedthat implements the content ID format from ADR-0005 and interns those IDs to handles.ContentIdincrates/shared/src/registry.rsis a validatednamespace:namestring. Both halves must match[a-z0-9_-]+with exactly one:between them. A bare name returnsContentIdError::MissingNamespaceand is never coerced tosyn:. Validation runs inTryFrom<String>,TryFrom<&str>andFromStr, and serde goes throughtry_from = "String", so a deserialised ID is validated too. It serialises as its canonical string in both JSON and postcard.ItemIdis a new#[repr(transparent)]u16handle derivingPodandZeroable, matchingBlockId. It lives inregistrybecause items are not voxels;BlockIdstays inworld.RegistryBuilder<H, D>assigns denseu16handles in registration order and keeps aBTreeMap<ContentId, H>for reverse lookup.registerreturnsRegistryError::Duplicatefor an ID already present andRegistryError::Exhaustedonce all 65536 handles are taken.freezeconsumes the builder and returns aRegistry<H, D>, which only hasget,idandhandle, so nothing can register after load.RegistryBuilder::<BlockId, _>::with_airpre-registerssyn:airatBlockId::AIR, since chunks treat handle 0 as empty space. Block handles therefore start at 1 and item handles (RegistryBuilder::<ItemId, _>::new) start at 0.ContentHandleis a sealed trait implemented forBlockIdandItemIdonly, so the builder is generic over the two handle types without being open to arbitrary ones.thiserrorand live incrates/shared/src/registry/error.rs.Why is this change necessary?
ADR-0005 fixed the
namespace:idformat and the interning step, but none of it existed in code.BlockIdvalues are still hand-assigned and nothing validates an identifier. Worldgen data, the Lua API and save files all need a single place that parses IDs and maps them to handles before any of them can move off raw integers.Scope of Changes
shared
Testing
Automated:
cargo check -p shared: passes.cargo test -p shared: 269 passed, 0 failed.cargo clippy --all-targets --all-features -- -D warnings: passes.cargo fmt --all -- --check: passes.New tests in
crates/shared/src/tests/registry.rscover well-formed IDs, the namespace and name split, rejection of bare names, empty halves, a second:, and characters outside the charset (uppercase, spaces, dots, slashes, non-ASCII). They also check JSON and postcard round trips, that deserialising an invalid string fails, air at handle 0, dense handle assignment for blocks and items, lookups in both directions on a frozen registry, duplicate rejection, and exhaustion afterBlockId(u16::MAX).Manual: none. Nothing outside the tests uses the registry yet, so there is no in-game behaviour to check.
Additional Context
This PR only adds the types. Nothing is wired to them yet: worldgen config still holds raw
BlockIdvalues, the save format is unchanged, and there is no Lua binding. Those move over in follow-up PRs.Handles depend on registration order, and
ChunkDatastill storesBlockIdraw. Until a world carries its ownnamespace:idto handle table, changing the registered content set would reinterpret saved blocks. ADR-0005 already records this.Registering the same ID twice is an error even when the definitions match, because there is no rule yet for when two definitions count as equal.
Related Issues
None.
Checklist
dev(or a feature branch offdev) and my PR targetsdev.///doc comments for Rust) where necessary.