code wiki / _hdl_build / nx_mineworld_mp_up.nx
nx_mineworld_mp_up.nx source
↩ module page · 102 lines · 4996 B
1// nx_mineworld_mp_up.nx -- bring the MULTIPLAYER MineWorld relay LIVE locally: build nx_mp_serve (--build-only,
2// the runner leaves /tmp/nx_mp_serve.sov.elf in THIS shell session), daemon_spawn it detached (nx_daemon =
3// fork+setsid+execve, gate-proven; survives the launcher), then LOOPBACK-PROBE 127.0.0.1:7702 GET /mineworld
4// until 200 + a full self-contained page (>20000B) or an 8s deadline. Play = open http://localhost:7702/mineworld
5// in two tabs (same room #hash) -- peers see each other. A 404 here = an OLD daemon holds :7702 (restart it).
6// license_tier: ORIGINAL expect_exit: 0
7import "nx_syscalls.nx"
8import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
9import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
10import "nx_daemon.nx"
11const K_MAGIC_7702: i64 = 7702
12const K_MAGIC_65536: i64 = 65536
13const K_MAGIC_8000: i64 = 8000
14const K_MAGIC_20000: i64 = 20000
15
16func up_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
17// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
18// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
19// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
20// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
21func up_n(v: i64) -> i64 { nxi_out(v); return 0 }
22func up_spawn_wait(path: *u8, a0: *u8, a1: *u8) -> i64 {
23 let pid: i64 = sys_fork()
24 if pid == 0 {
25 let dn: i64 = sys_openat_wr("/dev/null\x00" as *u8, 420)
26 if dn >= 0 { sys_dup3(dn, 1, 0); sys_dup3(dn, 2, 0) }
27 let argv: *i64 = sys_mmap(32) as *i64
28 argv[0] = path as i64
29 var n: i64 = 1
30 if (a0 as i64) != 0 { argv[n] = a0 as i64; n = n + 1 }
31 if (a1 as i64) != 0 { argv[n] = a1 as i64; n = n + 1 }
32 argv[n] = 0
33 let envp: *i64 = sys_mmap(16) as *i64
34 envp[0] = "PATH=/usr/bin:/bin\x00" as *u8 as i64
35 envp[1] = 0
36 sys_execve(path, argv, envp)
37 sys_exit(127)
38 }
39 let st: *i64 = sys_mmap(16) as *i64
40 sys_wait4(pid, st, 0)
41 return (st[0] >> 8) & 0xff
42}
43// one GET /mineworld probe; returns total bytes read (0 = connect/read failed), status code hint via first bytes
44func up_probe(resp: *u8, cap: i64) -> i64 {
45 let addr: *u8 = sys_mmap(16)
46 addr[0] = 2 as u8; addr[1] = 0 as u8
47 addr[2] = ((K_MAGIC_7702 >> 8) & 0xff) as u8; addr[3] = (K_MAGIC_7702 & 0xff) as u8
48 addr[4] = 127 as u8; addr[5] = 0 as u8; addr[6] = 0 as u8; addr[7] = 1 as u8
49 var z: i64 = 8
50 while z < 16 { addr[z] = 0 as u8; z = z + 1 }
51 let fd: i64 = sys_socket(2, 1, 0)
52 if fd < 0 { return 0 }
53 if nx_connect_bounded(fd, addr, 16, NX_CONN_DEFAULT_MS) < 0 { sys_close(fd); return 0 }
54 let req: *u8 = "GET /mineworld HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n\x00" as *u8
55 var rl: i64 = 0
56 while req[rl] != (0 as u8) { rl = rl + 1 }
57 sys_write(fd, req, rl)
58 var total: i64 = 0
59 var go: i64 = 1
60 while go == 1 {
61 let k: i64 = sys_read(fd, (resp as i64 + total) as *u8, cap - total)
62 if k <= 0 { go = 0 } else { total = total + k; if total >= cap { go = 0 } }
63 }
64 sys_close(fd)
65 return total
66}
67
68func main() -> i64 {
69 up_w("[1] build nx_mp_serve (runner, --build-only)\n\x00" as *u8)
70 up_spawn_wait("_offc/nx_sov_build_run.elf\x00" as *u8, "nx_mp_serve\x00" as *u8, "--build-only\x00" as *u8)
71 up_w("[2] daemon_spawn /tmp/nx_mp_serve.sov.elf (detached, setsid)\n\x00" as *u8)
72 let argv: *i64 = sys_mmap(16) as *i64
73 daemon_argv0("/tmp/nx_mp_serve.sov.elf\x00" as *u8, argv)
74 daemon_spawn("/tmp/nx_mp_serve.sov.elf\x00" as *u8, argv)
75 up_w("[3] loopback probe GET 127.0.0.1:7702/mineworld (8s window)\n\x00" as *u8)
76 let cap: i64 = K_MAGIC_65536
77 let resp: *u8 = sys_mmap(cap)
78 let deadline: i64 = sys_now_ms() + K_MAGIC_8000
79 var total: i64 = 0
80 var go: i64 = 1
81 while go == 1 {
82 total = up_probe(resp, cap)
83 if total > 0 { go = 0 } else {
84 if sys_now_ms() >= deadline { go = 0 } else {
85 let until: i64 = sys_now_ms() + 250
86 while sys_now_ms() < until { }
87 }
88 }
89 }
90 // require "200" at resp[9..11] (HTTP/1.1 200) + a full page body
91 var ok: i64 = 0
92 if total > K_MAGIC_20000 { if (resp[9] as i64) == 50 { if (resp[10] as i64) == 48 { if (resp[11] as i64) == 48 { ok = 1 } } } }
93 up_w(" bytes=\x00" as *u8); up_n(total)
94 if total >= 12 { up_w(" status=\x00" as *u8); sys_write(1, (resp as i64 + 9) as *u8, 3) }
95 up_w("\n\x00" as *u8)
96 if ok == 1 {
97 up_w("MINEWORLD-MP UP: http://localhost:7702/mineworld (two tabs = shared world; #<n> picks a room)\n\x00" as *u8)
98 return 0
99 }
100 up_w("MINEWORLD-MP NOT SERVING (0 bytes = daemon didn't bind; 404 = an OLD nx_mp_serve holds :7702 -- restart it)\n\x00" as *u8)
101 return 1
102}