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}