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}