code wiki / _hdl_build / nx_voxchunk.nx

nx_voxchunk.nx source

↩ module page · 313 lines · 11657 B

1// nx_voxchunk.nx -- the SOVEREIGN CHUNKED VOXEL WORLD PART. nx_gamebench gap-queue rank 1 2// (voxel-world-chunked, PARTIAL): the craft organ's world is a FIXED 64x32x64 volume -- honest 3// evidence for rendering and editing, but NOT for the Minecraft-class bar the capability names: 4// an unbounded world streamed through a BOUNDED resident set of chunks. This part is that bar, 5// built on the certified parts (persistence = nx_gamesave, so a dirty chunk survives eviction 6// with atomicity/corruption-refusal/additive-only inherited, not re-derived). 7// 8// THE INVARIANTS THIS PART EXISTS TO HOLD (each a gate tooth in nx_voxchunk_gate): 9// 1. DETERMINISTIC GENERATION. A virgin chunk is a pure function of (world_seed, cx, cz): 10// load -> evict -> reload is bit-identical. No stored world is needed for unvisited land. 11// 2. BOUNDED RESIDENCY BY CONSTRUCTION. At most `cap` chunks are ever resident; walking 12// 10,000 chunks costs the same memory as walking 10. The eviction is the design, not an 13// afterthought. 14// 3. EDITS SURVIVE EVICTION. A modified (dirty) chunk is flushed through gs_save before its 15// slot is reused, and reload prefers the saved file over regeneration -- the Minecraft law: 16// virgin land from the seed, touched land from disk. Losing edits on eviction is the classic 17// chunk-corruption bug class; here it is structurally impossible while the flush path holds. 18// 4. SEAM-CONSISTENT WORLD ACCESS. Callers address blocks in WORLD coordinates; chunk math is 19// internal. A query at a chunk border resolves identically whether the neighbour is resident, 20// evicted, or never visited. 21// 5. WHOLE-SESSION DURABILITY. The session arena is flat i64 words -> gs_save; dirty state is 22// flushed first, so a saved world reopens exactly where it left off. 23// Chunk geometry: 16 x 32 x 16 blocks (8192 bytes), packed 8 blocks/word in the arena tail. 24// license_tier: ORIGINAL expect_exit: 0 25import "nx_syscalls.nx" 26import "nx_gamesave.nx" 27const VXC_MAGIC_374761393: i64 = 374761393 28const VXC_MAGIC_668265263: i64 = 668265263 29const VXC_MAGIC_2246822519: i64 = 2246822519 30const VXC_MAGIC_1274126177: i64 = 1274126177 31const VXC_MAGIC_60630: i64 = 60630 32const VXC_MAGIC_1785430000: i64 = 1785430000 33const VXC_MAGIC_52711: i64 = 52711 34const VXC_MAGIC_8191: i64 = 8191 35 36const VXC_CW: i64 = 16 37const VXC_CH: i64 = 32 38const VXC_CD: i64 = 16 39const VXC_CBYTES: i64 = 8192 40const VXC_CWORDS: i64 = 1024 41const VXC_HDR: i64 = 8 42const VXC_SLOTW: i64 = 4 43const VXC_SEA: i64 = 12 44 45// slot states 46const VXC_S_EMPTY: i64 = 0 47const VXC_S_CLEAN: i64 = 1 48const VXC_S_DIRTY: i64 = 2 49 50// error codes -- distinct, loud 51const VXC_E_RANGE: i64 = 0-1 52const VXC_E_FLUSH: i64 = 0-2 53 54// hdr: [0]=cap [1]=seed [2]=access_tick [3]=resident [4]=loads [5]=evictions [6]=dirty_flushes [7]=disk_loads 55func vxc_cap(a: *i64) -> i64 { return a[0] } 56func vxc_seed(a: *i64) -> i64 { return a[1] } 57func vxc_resident(a: *i64) -> i64 { return a[3] } 58func vxc_loads(a: *i64) -> i64 { return a[4] } 59func vxc_evictions(a: *i64) -> i64 { return a[5] } 60func vxc_flushes(a: *i64) -> i64 { return a[6] } 61func vxc_diskloads(a: *i64) -> i64 { return a[7] } 62 63func vxc_words(cap: i64) -> i64 { return VXC_HDR + cap*VXC_SLOTW + cap*VXC_CWORDS } 64func vxc_bytes(cap: i64) -> i64 { return vxc_words(cap) * 8 } 65func vxc_o_slot(a: *i64, s: i64) -> i64 { return VXC_HDR + s*VXC_SLOTW } 66// chunk block bytes for slot s, as *u8 67func vxc_blocks(a: *i64, s: i64) -> *u8 { 68 return (a as i64 + (VXC_HDR + vxc_cap(a)*VXC_SLOTW + s*VXC_CWORDS)*8) as *u8 69} 70 71func vxc_init(a: *i64, cap: i64, seed: i64) -> i64 { 72 a[0] = cap 73 a[1] = seed 74 if a[1] == 0 { a[1] = 1 } 75 a[2] = 0 76 a[3] = 0 77 a[4] = 0 78 a[5] = 0 79 a[6] = 0 80 a[7] = 0 81 var s: i64 = 0 82 while s < cap { 83 let o: i64 = vxc_o_slot(a, s) 84 a[o] = 0 85 a[o+1] = 0 86 a[o+2] = 0 87 a[o+3] = VXC_S_EMPTY 88 s = s + 1 89 } 90 return 0 91} 92 93// deterministic integer hash -> positive 94func vxc_hash(x: i64, z: i64, seed: i64) -> i64 { 95 var h: i64 = (x * VXC_MAGIC_374761393) + (z * VXC_MAGIC_668265263) + (seed * VXC_MAGIC_2246822519) 96 h = h ^ (h >> 13) 97 h = (h * VXC_MAGIC_1274126177) & 0x7FFFFFFFFFFFFFFF 98 h = h ^ (h >> 16) 99 return h & 0x7FFFFFFFFFFFFFFF 100} 101// lattice height at world column (wx,wz): bilinear blend of hashed corners on an 8-block lattice 102func vxc_height(wx: i64, wz: i64, seed: i64) -> i64 { 103 let lx: i64 = wx >> 3 104 let lz: i64 = wz >> 3 105 let fx: i64 = wx & 7 106 let fz: i64 = wz & 7 107 let h00: i64 = 6 + (vxc_hash(lx, lz, seed) % 18) 108 let h10: i64 = 6 + (vxc_hash(lx+1, lz, seed) % 18) 109 let h01: i64 = 6 + (vxc_hash(lx, lz+1, seed) % 18) 110 let h11: i64 = 6 + (vxc_hash(lx+1, lz+1, seed) % 18) 111 let top: i64 = h00*(8-fx) + h10*fx 112 let bot: i64 = h01*(8-fx) + h11*fx 113 return (top*(8-fz) + bot*fz) / 64 114} 115// generate a virgin chunk into blocks: bands stone/dirt/grass, water to sea level, ore flecks 116func vxc_gen(blocks: *u8, cx: i64, cz: i64, seed: i64) -> i64 { 117 var z: i64 = 0 118 while z < VXC_CD { 119 var x: i64 = 0 120 while x < VXC_CW { 121 let wx: i64 = cx*VXC_CW + x 122 let wz: i64 = cz*VXC_CD + z 123 let h: i64 = vxc_height(wx, wz, seed) 124 var y: i64 = 0 125 while y < VXC_CH { 126 var b: i64 = 0 127 if y < h - 3 { b = 3 } 128 if y >= h - 3 { if y < h { b = 2 } } 129 if y == h { b = 1 } 130 if b == 0 { if y <= VXC_SEA { if y > h { b = 4 } } } 131 if b == 3 { if vxc_hash(wx*32+y, wz*32+y, seed) % 97 == 0 { b = 5 } } 132 blocks[(y*VXC_CD + z)*VXC_CW + x] = b as u8 133 y = y + 1 134 } 135 x = x + 1 136 } 137 z = z + 1 138 } 139 return 0 140} 141 142// ---- dirty-chunk persistence: path "knowledge/nx_vxc_<cx>_<cz>.sav" (small coords; gate range) ---- 143func vxc_itoa_at(p: *u8, at: i64, v: i64) -> i64 { 144 var a: i64 = at 145 var m: i64 = v 146 if m < 0 { p[a] = 110 as u8; a = a + 1; m = 0 - m } 147 let t: *u8 = sys_mmap(24) 148 var k: i64 = 0 149 if m == 0 { t[0] = 48 as u8; k = 1 } 150 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 151 var q: i64 = k - 1 152 while q >= 0 { p[a] = t[q]; a = a + 1; q = q - 1 } 153 return a 154} 155func vxc_path(p: *u8, cx: i64, cz: i64) -> i64 { 156 let pre: *u8 = "knowledge/nx_vxc_" as *u8 157 var i: i64 = 0 158 while pre[i] != (0 as u8) { p[i] = pre[i]; i = i + 1 } 159 i = vxc_itoa_at(p, i, cx) 160 p[i] = 95 as u8; i = i + 1 161 i = vxc_itoa_at(p, i, cz) 162 p[i] = 46 as u8; p[i+1] = 115 as u8; p[i+2] = 97 as u8; p[i+3] = 118 as u8; p[i+4] = 0 as u8 163 return 0 164} 165// flush one dirty slot through the certified save part. Returns >0 on success. 166func vxc_flush_slot(a: *i64, s: i64) -> i64 { 167 let o: i64 = vxc_o_slot(a, s) 168 let pbuf: *u8 = sys_mmap(96) 169 vxc_path(pbuf, a[o], a[o+1]) 170 let cw: *i64 = (vxc_blocks(a, s)) as *i64 171 let wrote: i64 = gs_save(pbuf, VXC_MAGIC_60630, cw, VXC_CWORDS, VXC_MAGIC_1785430000) 172 if wrote > 0 { a[6] = a[6] + 1 } 173 return wrote 174} 175// try to load a previously-saved (touched) chunk from disk. 1 = loaded, 0 = absent/invalid. 176func vxc_disk_load(a: *i64, s: i64, cx: i64, cz: i64) -> i64 { 177 let pbuf: *u8 = sys_mmap(96) 178 vxc_path(pbuf, cx, cz) 179 let cw: *i64 = (vxc_blocks(a, s)) as *i64 180 let got: i64 = gs_load(pbuf, cw, VXC_CWORDS, 0 as *i64) 181 if got == VXC_CWORDS { a[7] = a[7] + 1; return 1 } 182 return 0 183} 184 185// find the slot holding (cx,cz), else -1 186func vxc_find(a: *i64, cx: i64, cz: i64) -> i64 { 187 let cap: i64 = vxc_cap(a) 188 var s: i64 = 0 189 while s < cap { 190 let o: i64 = vxc_o_slot(a, s) 191 if a[o+3] != VXC_S_EMPTY { if a[o] == cx { if a[o+1] == cz { return s } } } 192 s = s + 1 193 } 194 return 0-1 195} 196// pick a victim slot: an EMPTY one if any, else the least-recently-used. Flushes a dirty victim 197// BEFORE reuse -- the invariant-3 choke point. 198func vxc_victim(a: *i64) -> i64 { 199 let cap: i64 = vxc_cap(a) 200 var best: i64 = 0 201 var bestu: i64 = 0x7FFFFFFFFFFFFFFF 202 var s: i64 = 0 203 while s < cap { 204 let o: i64 = vxc_o_slot(a, s) 205 if a[o+3] == VXC_S_EMPTY { return s } 206 if a[o+2] < bestu { bestu = a[o+2]; best = s } 207 s = s + 1 208 } 209 let bo: i64 = vxc_o_slot(a, best) 210 if a[bo+3] == VXC_S_DIRTY { 211 if vxc_flush_slot(a, best) <= 0 { return VXC_E_FLUSH } 212 } 213 a[5] = a[5] + 1 214 a[3] = a[3] - 1 215 a[bo+3] = VXC_S_EMPTY 216 return best 217} 218// ensure chunk (cx,cz) is resident; returns its slot (or VXC_E_FLUSH if a dirty victim failed to save). 219func vxc_ensure(a: *i64, cx: i64, cz: i64) -> i64 { 220 var s: i64 = vxc_find(a, cx, cz) 221 a[2] = a[2] + 1 222 if s >= 0 { 223 a[vxc_o_slot(a, s)+2] = a[2] 224 return s 225 } 226 s = vxc_victim(a) 227 if s < 0 { return s } 228 let o: i64 = vxc_o_slot(a, s) 229 a[o] = cx 230 a[o+1] = cz 231 a[o+2] = a[2] 232 a[o+3] = VXC_S_CLEAN 233 a[3] = a[3] + 1 234 a[4] = a[4] + 1 235 if vxc_disk_load(a, s, cx, cz) == 0 { 236 vxc_gen(vxc_blocks(a, s), cx, cz, vxc_seed(a)) 237 } 238 return s 239} 240 241// floor-division chunk coords for possibly-negative world coords 242func vxc_cdiv(v: i64, d: i64) -> i64 { 243 if v >= 0 { return v / d } 244 return 0 - (((0 - v) + d - 1) / d) 245} 246// ---- WORLD-COORDINATE access: the caller never sees chunk math ---- 247func vxc_get(a: *i64, wx: i64, wy: i64, wz: i64) -> i64 { 248 if wy < 0 { return VXC_E_RANGE } 249 if wy >= VXC_CH { return VXC_E_RANGE } 250 let cx: i64 = vxc_cdiv(wx, VXC_CW) 251 let cz: i64 = vxc_cdiv(wz, VXC_CD) 252 let s: i64 = vxc_ensure(a, cx, cz) 253 if s < 0 { return s } 254 let x: i64 = wx - cx*VXC_CW 255 let z: i64 = wz - cz*VXC_CD 256 let bl: *u8 = vxc_blocks(a, s) 257 return bl[(wy*VXC_CD + z)*VXC_CW + x] as i64 258} 259func vxc_set(a: *i64, wx: i64, wy: i64, wz: i64, b: i64) -> i64 { 260 if wy < 0 { return VXC_E_RANGE } 261 if wy >= VXC_CH { return VXC_E_RANGE } 262 if b < 0 { return VXC_E_RANGE } 263 if b > 255 { return VXC_E_RANGE } 264 let cx: i64 = vxc_cdiv(wx, VXC_CW) 265 let cz: i64 = vxc_cdiv(wz, VXC_CD) 266 let s: i64 = vxc_ensure(a, cx, cz) 267 if s < 0 { return s } 268 let x: i64 = wx - cx*VXC_CW 269 let z: i64 = wz - cz*VXC_CD 270 let bl: *u8 = vxc_blocks(a, s) 271 bl[(wy*VXC_CD + z)*VXC_CW + x] = b as u8 272 let so: i64 = vxc_o_slot(a, s) 273 a[so+3] = VXC_S_DIRTY 274 return 0 275} 276// flush every dirty resident chunk (session save prelude). Returns flush count, or VXC_E_FLUSH. 277func vxc_flush_all(a: *i64) -> i64 { 278 let cap: i64 = vxc_cap(a) 279 var n: i64 = 0 280 var s: i64 = 0 281 while s < cap { 282 let o: i64 = vxc_o_slot(a, s) 283 if a[o+3] == VXC_S_DIRTY { 284 if vxc_flush_slot(a, s) <= 0 { return VXC_E_FLUSH } 285 a[o+3] = VXC_S_CLEAN 286 n = n + 1 287 } 288 s = s + 1 289 } 290 return n 291} 292// independent live-state checksum (world truth walker; excludes access ticks so timing-neutral state 293// compares equal). Walks resident chunk CONTENT keyed by coords, order-independent via per-chunk fold. 294func vxc_checksum(a: *i64) -> i64 { 295 var h: i64 = VXC_MAGIC_52711 296 let cap: i64 = vxc_cap(a) 297 var s: i64 = 0 298 while s < cap { 299 let o: i64 = vxc_o_slot(a, s) 300 if a[o+3] != VXC_S_EMPTY { 301 var ch: i64 = ((a[o] * VXC_MAGIC_8191) + a[o+1]) & 0x7FFFFFFFFFFFFFFF 302 let bl: *u8 = vxc_blocks(a, s) 303 var i: i64 = 0 304 while i < VXC_CBYTES { 305 ch = ((ch * 131) + (bl[i] as i64)) & 0x7FFFFFFFFFFFFFFF 306 i = i + 1 307 } 308 h = (h + ch) & 0x7FFFFFFFFFFFFFFF 309 } 310 s = s + 1 311 } 312 return h 313}