code wiki / _hdl_build / nx_entity_store.nx

nx_entity_store.nx

buildroot/runtime/_hdl_build/nx_entity_store.nx

6453 B151 linesdepth 2pulls 2 transitivereach 22 importersview sourcekind librarytopic entity
docsdependenciesstructsconstsfunctions

about

nx_entity_store.nx -- the SOVEREIGN ENTITY STORE. nx_gamebench gap-queue rank 1 after save/load and rpg-stats landed: entity-component-sim was PARTIAL and blocks 7 of 12 benchmarked titles (Doom actors, OpenXcom units, Diablo monsters, Cataclysm, Veloren, Freeciv, NetHack). THE HAZARD THIS PART EXISTS TO KILL -- the stale handle. Every naive entity store hands out a raw slot index. An entity dies, its slot is recycled for a new entity, and any handle still held by old code now silently addresses a DIFFERENT entity: a homing missile retargets onto a random unit, a dead unit's buff lands on its replacement. It is one of the nastiest bug classes in game code because it is invisible until it is a bug report. Here every handle carries a GENERATION stamped into it, bumped on destroy, so a stale handle is DETECTABLY invalid -- rejection is by construction, not by discipline. Other certified properties (each a gate tooth): - O(1) spawn and destroy (free-list + swap-remove), no scanning - DENSE iteration: visiting the alive set never walks holes - REPLAY-DETERMINISTIC: the same operation sequence yields the same handles and the same iteration order, so recorded input replays identically (the determinism exceed carried into simulation) - CAPACITY-BOUNDED: spawning past capacity returns the null handle, never corrupts the arena - Component values are plain i64 so entity state drops straight into gs_save/gs_load (nx_gamesave). Arena layout follows the nx_swgpu base+offset idiom: one mmap block, all offsets derived from cap/ncomp. LIB ONLY -- no main() by ecosystem convention. license_tier: ORIGINAL expect_exit: 0

dependencies 1 imports · 7 importers

nx_syscalls.nx nx_entity_store.nx nx_entity_store_gate.nx nx_game_actor.nx nx_game_companion.nx nx_wasm_craft.nx nx_wire_diablo2_gate.nx nx_wire_nethack_gate.nx nx_wire_probe.nx

imports: nx_syscalls.nx

imported by: nx_entity_store_gate.nxnx_game_actor.nxnx_game_companion.nxnx_wasm_craft.nxnx_wire_diablo2_gate.nxnx_wire_nethack_gate.nxnx_wire_probe.nx

structs

none

consts

25const EN_GENMOD: i64 = 1048576 // 2^20 generations per slot before wrap
26const EN_NULL: i64 = 0 // the null handle; a real handle is always > 0
27const EN_HDR: i64 = 4 // cap, ncomp, alive_count, fresh_next

functions

30func en_cap(a: *i64) -> i64 { return a[0] }
31func en_ncomp(a: *i64) -> i64 { return a[1] }
32func en_count(a: *i64) -> i64 { return a[2] }
35func en_o_gen(a: *i64) -> i64 { return EN_HDR }
36func en_o_alive(a: *i64) -> i64 { return EN_HDR + en_cap(a) }
37func en_o_free(a: *i64) -> i64 { return EN_HDR + 2*en_cap(a) }
38func en_o_dense(a: *i64) -> i64 { return EN_HDR + 3*en_cap(a) }
39func en_o_pos(a: *i64) -> i64 { return EN_HDR + 4*en_cap(a) }
called by 3: en_initen_spawnen_destroy calls 1: en_cap
40func en_o_comp(a: *i64) -> i64 { return EN_HDR + 5*en_cap(a) }
called by 3: en_spawnen_seten_get calls 1: en_cap
43func en_words(cap: i64, ncomp: i64) -> i64 { return EN_HDR + 5*cap + ncomp*cap + 8 }
44func en_bytes(cap: i64, ncomp: i64) -> i64 { return en_words(cap, ncomp) * 8 }
called by 8: ba_runmainmainmainmainmain+2 calls 1: en_words
48func en_init(a: *i64, cap: i64, ncomp: i64) -> i64
65func en_freelen(a: *i64) -> i64 { return a[en_o_free(a)] }
called by 2: en_spawnen_destroy calls 1: en_o_free
67func en_make_handle(idx: i64, gen: i64) -> i64 { return (idx + 1) * EN_GENMOD + gen }
called by 2: en_spawnen_nth
68func en_handle_idx(h: i64) -> i64 { return (h / EN_GENMOD) - 1 }
69func en_handle_gen(h: i64) -> i64 { return h % EN_GENMOD }
called by 1: en_valid
72func en_valid(a: *i64, h: i64) -> i64
83func en_spawn(a: *i64) -> i64
108func en_destroy(a: *i64, h: i64) -> i64
131func en_set(a: *i64, h: i64, comp: i64, val: i64) -> i64
138func en_get(a: *i64, h: i64, comp: i64) -> i64
146func en_nth(a: *i64, k: i64) -> i64