code wiki / _hdl_build / nx_mp_daemon.nx

nx_mp_daemon.nx source

↩ module page · 119 lines · 5422 B

1// nx_mp_daemon.nx -- sovereign request-handler for the multiplayer player-state relay (the /pstate POST + 2// /roster GET routes). A LIBRARY (no main) so it is gate-testable without sockets and then dropped into the 3// socket accept/fork loop (mirrors nx_arcade_serve). The relay lives in SHARED mmap so every forked handler 4// sees it. Composes nx_room_relay (rr_post) + nx_mp_relay (mp_roster_body). Pure parsing + buffer ops. 5// /pstate?r=<room>&p=<peer> (POST, body = the wasm's packed snapshot bytes) -> rr_post into the room 6// /roster?r=<room>&p=<self> (GET) -> mp_roster_body of the OTHER live peers, as an octet-stream body 7// The browser host does ONLY: snapshot->POST, GET->memcpy->gi_net_ingest_roster. Zero net logic in JS. 8// license_tier: ORIGINAL 9import "nx_syscalls.nx" 10import "nx_mp_relay.nx" // brings nx_room_relay (rr_*) transitively 11const K_MAGIC_4096: i64 = 4096 12 13// extract the request-target (the path) from "METHOD <target> HTTP/1.1" into out; returns its length. 14func mpd_path(req: *u8, n: i64, out: *u8) -> i64 { 15 var i: i64 = 0 16 var sp: i64 = 0 - 1 17 while i < n { if req[i] == (32 as u8) { sp = i; i = n } else { i = i + 1 } } // first space = end of METHOD 18 if sp < 0 { out[0] = 0 as u8; return 0 } 19 var p: i64 = sp + 1 20 var w: i64 = 0 21 var go: i64 = 1 22 while go == 1 { 23 if p >= n { go = 0 } else { 24 if req[p] == (32 as u8) { go = 0 } else { out[w] = req[p]; w = w + 1; p = p + 1 } 25 } 26 } 27 out[w] = 0 as u8 28 return w 29} 30func mpd_starts(s: *u8, pre: *u8) -> i64 { 31 var i: i64 = 0 32 while pre[i] != (0 as u8) { if s[i] != pre[i] { return 0 } i = i + 1 } 33 return 1 34} 35// route a path: 1 = /pstate, 2 = /roster, 0 = neither (caller serves files / 404) 36func mpd_route(path: *u8) -> i64 { 37 if mpd_starts(path, "/pstate" as *u8) == 1 { return 1 } 38 if mpd_starts(path, "/roster" as *u8) == 1 { return 2 } 39 return 0 40} 41func mpd_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 42// parse the unsigned integer following `key` in `path` (e.g. "r="); returns the value, or -1 if absent. 43func mpd_qint(path: *u8, key: *u8) -> i64 { 44 let n: i64 = mpd_slen(path) 45 let kl: i64 = mpd_slen(key) 46 var st: i64 = 0 - 1 47 var i: i64 = 0 48 while i + kl <= n { 49 var j: i64 = 0 50 var m: i64 = 1 51 while j < kl { if path[i + j] != key[j] { m = 0; j = kl } else { j = j + 1 } } 52 if m == 1 { st = i + kl; i = n + kl } else { i = i + 1 } 53 } 54 if st < 0 { return 0 - 1 } 55 var v: i64 = 0 56 var p: i64 = st 57 var any: i64 = 0 58 var go: i64 = 1 59 while go == 1 { 60 if p >= n { go = 0 } else { 61 let c: i64 = path[p] as i64 62 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); any = 1; p = p + 1 } else { go = 0 } } else { go = 0 } 63 } 64 } 65 if any == 0 { return 0 - 1 } 66 return v 67} 68// byte offset of the POST body (just past the "\r\n\r\n" header terminator), or -1 if none. 69func mpd_body_off(req: *u8, n: i64) -> i64 { 70 var i: i64 = 0 71 while i + 3 < n { 72 if req[i] == (13 as u8) { if req[i+1] == (10 as u8) { if req[i+2] == (13 as u8) { if req[i+3] == (10 as u8) { return i + 4 } } } } 73 i = i + 1 74 } 75 return 0 - 1 76} 77func mpd_wstr(buf: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { buf[off + i] = s[i]; i = i + 1 } return off + i } 78func mpd_wn(buf: *u8, off: i64, v: i64) -> i64 { 79 var m: i64 = v 80 let tmp: *u8 = sys_mmap(32) 81 var k: i64 = 0 82 if m == 0 { tmp[0] = 48 as u8; k = 1 } 83 while m > 0 { tmp[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 84 var i: i64 = 0 85 while i < k { buf[off + i] = tmp[k - 1 - i]; i = i + 1 } 86 return off + k 87} 88 89// HANDLE /pstate: parse room+peer, take the POST body as the snapshot, rr_post it. Writes a tiny 200 ack to 90// `out`; returns response length. (st/arena = the SHARED relay; slotb = its slot bytes; now = wall ms.) 91func mpd_pstate(req: *u8, n: i64, path: *u8, st: *i64, arena: *u8, slotb: i64, now: i64, out: *u8) -> i64 { 92 let rm: i64 = mpd_qint(path, "r=" as *u8) 93 let pr: i64 = mpd_qint(path, "p=" as *u8) 94 let bo: i64 = mpd_body_off(req, n) 95 if rm >= 0 { if pr >= 0 { if bo >= 0 { 96 var bl: i64 = n - bo 97 if bl > slotb { bl = slotb } 98 rr_post(st, arena, rm, pr, now, now, (req + bo) as *u8, bl) 99 } } } 100 var o: i64 = 0 101 o = mpd_wstr(out, o, "HTTP/1.1 200 OK\r\nContent-Length: 2\r\nConnection: close\r\nAccess-Control-Allow-Origin: *\r\n\r\nok" as *u8) 102 return o 103} 104// HANDLE /roster: serialize OTHER live peers (mp_roster_body) as an octet-stream body. Writes the full HTTP 105// response (header + binary body) to `out`; returns total length. 106func mpd_roster(path: *u8, st: *i64, arena: *u8, slotb: i64, now: i64, ttl: i64, out: *u8) -> i64 { 107 let rm: i64 = mpd_qint(path, "r=" as *u8) 108 let self_pr: i64 = mpd_qint(path, "p=" as *u8) 109 let body: *u8 = sys_mmap(K_MAGIC_4096) 110 var bl: i64 = 0 111 if rm >= 0 { bl = mp_roster_body(st, arena, slotb, rm, self_pr, now, ttl, body) } 112 var o: i64 = 0 113 o = mpd_wstr(out, o, "HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\nContent-Length: " as *u8) 114 o = mpd_wn(out, o, bl) 115 o = mpd_wstr(out, o, "\r\nConnection: close\r\nAccess-Control-Allow-Origin: *\r\n\r\n" as *u8) 116 var k: i64 = 0 117 while k < bl { out[o + k] = body[k]; k = k + 1 } 118 return o + bl 119}