code wiki / _hdl_build / nx_mp_daemon_gate.nx

nx_mp_daemon_gate.nx source

↩ module page · 105 lines · 5285 B

1// nx_mp_daemon_gate.nx -- proves the sovereign multiplayer daemon request-handler (nx_mp_daemon) over a relay. 2// NO sockets, NO js, NO curl: crafts real HTTP request bytes and drives mpd_pstate / mpd_roster directly. 3// 1) routing: /pstate->1, /roster->2, a file path->0 ; query ints parsed 4// 2) POST /pstate puts the body snapshot into the room (two players) 5// 3) GET /roster returns an HTTP response whose body = the OTHER players' [len][frame] records (byte-exact) 6// 4) room isolation (neg): /roster for a different room returns an empty body 7// Built nx_cc_sovereign -> nxasm_x86 (no gcc, no .sh). license_tier: ORIGINAL 8import "nx_syscalls.nx" 9import "nx_gate_emit_lib.nx" 10import "nx_mp_daemon.nx" // brings nx_mp_relay + nx_room_relay transitively 11import "nx_gate_verdict.nx" 12 13func mk_post(req: *u8, rm: i64, pr: i64, frame: *u8, fl: i64) -> i64 { 14 var o: i64 = mpd_wstr(req, 0, "POST /pstate?r=" as *u8) 15 o = mpd_wn(req, o, rm) 16 o = mpd_wstr(req, o, "&p=" as *u8) 17 o = mpd_wn(req, o, pr) 18 o = mpd_wstr(req, o, " HTTP/1.1\r\nContent-Length: " as *u8) 19 o = mpd_wn(req, o, fl) 20 o = mpd_wstr(req, o, "\r\n\r\n" as *u8) 21 var k: i64 = 0 22 while k < fl { req[o + k] = frame[k]; k = k + 1 } 23 return o + fl 24} 25func rec_eq(body: *u8, o: i64, expect: *u8, n: i64) -> i64 { 26 if (body[o] as i64) != n { return 0 } 27 var k: i64 = 0 28 while k < n { if body[o + 1 + k] != expect[k] { return 0 } k = k + 1 } 29 return 1 30} 31 32func main() -> i64 { 33 g_puts("nx_mp_daemon gate (sovereign /pstate + /roster handler, NO sockets/js/curl)\n" as *u8) 34 var pass: i64 = 0; var total: i64 = 0 35 36 let N: i64 = 8 37 let SB: i64 = 64 38 let st: *i64 = sys_mmap((2 + 5*N) * 8) as *i64 39 let arena: *u8 = sys_mmap(N * SB) 40 rr_init(st, N, SB) 41 let path: *u8 = sys_mmap(1024) 42 let out: *u8 = sys_mmap(8192) 43 let req: *u8 = sys_mmap(8192) 44 let RM: i64 = 32368 45 let NOW: i64 = 1000 46 47 // 1) routing + query int parse 48 var r1: i64 = 1 49 mpd_path("POST /pstate?r=32368&p=100 HTTP/1.1\r\n\r\n" as *u8, 40, path) 50 if mpd_route(path) != 1 { r1 = 0 } 51 if mpd_qint(path, "r=" as *u8) != 32368 { r1 = 0 } 52 if mpd_qint(path, "p=" as *u8) != 100 { r1 = 0 } 53 mpd_path("GET /roster?r=5&p=9 HTTP/1.1\r\n\r\n" as *u8, 33, path) 54 if mpd_route(path) != 2 { r1 = 0 } 55 mpd_path("GET /pets3d.wasm HTTP/1.1\r\n\r\n" as *u8, 29, path) 56 if mpd_route(path) != 0 { r1 = 0 } 57 pass = pass + g_check("routing + query-int parse" as *u8, r1); total=total+1 58 59 // 2) POST /pstate for two players 60 let fa: *u8 = sys_mmap(64); var i: i64=0; while i<11 { fa[i]=(100+i) as u8; i=i+1 } 61 let fb: *u8 = sys_mmap(64); i=0; while i<11 { fb[i]=(40+i) as u8; i=i+1 } 62 let na: i64 = mk_post(req, RM, 100, fa, 11) 63 mpd_path(req, na, path); mpd_pstate(req, na, path, st, arena, SB, NOW, out) 64 let nb: i64 = mk_post(req, RM, 101, fb, 11) 65 mpd_path(req, nb, path); mpd_pstate(req, nb, path, st, arena, SB, NOW, out) 66 var r2: i64 = 1 67 if rr_live(st, RM, 100, NOW, 8000) < 0 { r2 = 0 } 68 if rr_live(st, RM, 101, NOW, 8000) < 0 { r2 = 0 } 69 pass = pass + g_check("POST /pstate stored both players in the room" as *u8, r2); total=total+1 70 71 // 3) GET /roster -> HTTP response whose body = the other players' records 72 var go: i64 = mpd_wstr(req, 0, "GET /roster?r=" as *u8) 73 go = mpd_wn(req, go, RM); go = mpd_wstr(req, go, "&p=999 HTTP/1.1\r\n\r\n" as *u8) 74 mpd_path(req, go, path) 75 let rlen: i64 = mpd_roster(path, st, arena, SB, NOW, 8000, out) 76 let bo: i64 = mpd_body_off(out, rlen) // skip the HTTP header 77 var r3: i64 = 1 78 if bo < 0 { r3 = 0 } else { 79 let body: *u8 = (out + bo) as *u8 80 let blen: i64 = rlen - bo 81 if mp_roster_count(body, blen) != 2 { r3 = 0 } 82 if rec_eq(body, 0, fa, 11) != 1 { r3 = 0 } 83 if rec_eq(body, 1 + 11, fb, 11) != 1 { r3 = 0 } 84 g_puts(" [measure] /roster response = " as *u8); g_pn(rlen); g_puts(" bytes (header + " as *u8); g_pn(blen); g_puts("-byte body, 2 records)\n" as *u8) 85 } 86 pass = pass + g_check("GET /roster body = other players' [len][frame] records, byte-exact" as *u8, r3); total=total+1 87 88 // 4) room isolation (neg): /roster for a different room -> empty body 89 var gx: i64 = mpd_wstr(req, 0, "GET /roster?r=11111&p=1 HTTP/1.1\r\n\r\n" as *u8) 90 mpd_path(req, gx, path) 91 let rl2: i64 = mpd_roster(path, st, arena, SB, NOW, 8000, out) 92 let bo2: i64 = mpd_body_off(out, rl2) 93 pass = pass + g_check("room isolation: empty roster body for another room (neg)" as *u8, (rl2 - bo2) == 0); total=total+1 94 95 g_puts("---- mp-daemon gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8) 96 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 97 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 98 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 99 let ctr__dry: *i64 = gv_ctr() 100 ctr__dry[0] = pass 101 ctr__dry[1] = total 102 let rc__dry: i64 = gv_verdict("MP-DAEMON-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 103 sys_exit(rc__dry) 104 return rc__dry 105}