code wiki / _hdl_build / nx_wasm_mineworld_mp_html.nx
nx_wasm_mineworld_mp_html.nx source
↩ module page · 29 lines · 5790 B
1// nx_wasm_mineworld_mp_html.nx -- SOVEREIGN packager for MULTIPLAYER MineWorld (GTA-online rung 1) with
2// LOGIC-FREE JS (operator law): the wasm core owns the sim (start/tick/render_cam/dig/put, integer trig) AND
3// the whole net protocol (net_push packs the frame; net_ingest_roster parses peers). The page only: captures
4// raw input, blits, and memcpy's bytes to/from /pstate + /roster every 150ms. Peer id = browser crypto entropy
5// (transport identity, not game logic). Room = URL #hash (default 42). Serve via nx_mp_serve :7702 /mineworld.
6// usage: nx_wasm_mineworld_mp_html <in.wasm> <out.html>. license_tier: ORIGINAL
7import "nx_syscalls.nx"
8import "nx_base64.nx"
9
10func hw(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 }
11
12func main(argc: i64, argv: *i64) -> i64 {
13 if argc < 3 { hw(2, "usage: nx_wasm_mineworld_mp_html <in.wasm> <out.html>\n" as *u8); return 2 }
14 let wlen_p: *i64 = sys_mmap(8) as *i64
15 let wbytes: *u8 = sys_read_file(argv[1] as *u8, wlen_p)
16 if (wbytes as i64) == 0 { hw(2, "read wasm failed\n" as *u8); return 3 }
17 let wlen: i64 = wlen_p[0]
18 let b64: *u8 = sys_mmap(wlen * 2 + 64) as *u8
19 let b64len: i64 = b64_encode(wbytes, wlen, b64)
20 let fd: i64 = sys_openat_wr(argv[2] as *u8, 0x1a4)
21 if fd < 0 { hw(2, "open html failed\n" as *u8); return 4 }
22 hw(fd, "<!doctype html><html><head><meta charset='utf-8'><title>Nishi MineWorld ONLINE -- shared infinite world</title><style>html,body{background:#000;margin:0;height:100%;overflow:hidden;font-family:sans-serif;color:#cdd}#wrap{display:flex;align-items:center;justify-content:center;height:100vh;width:100vw}canvas{image-rendering:pixelated;width:100vw;height:100vh;object-fit:contain;background:#9ec3e6;cursor:pointer}#hud{position:fixed;top:10px;left:0;right:0;text-align:center;font-size:13px;color:#aef;text-shadow:0 1px 2px #000;pointer-events:none}</style></head><body><div id='wrap'><canvas id='c'></canvas></div><div id='hud'>SHARED WORLD — open in another tab/device to meet · room = url #number · WASD · mouse look · L-dig R-place · Space jump · F fly · G FULLSCREEN — the ENTIRE game + net protocol runs in sovereign wasm</div><script>\n" as *u8)
23 hw(fd, "const B='" as *u8)
24 sys_write(fd, b64, b64len)
25 hw(fd, "';\nconst hud=document.getElementById('hud');function diag(m){hud.textContent=m;}window.onerror=(m,s,l)=>{diag('JS ERROR: '+m+' @line '+l);};\nconst bytes=Uint8Array.from(atob(B),c=>c.charCodeAt(0));\nWebAssembly.instantiate(bytes).then(r=>{const ex=r.instance.exports;const W=Number(ex.ww()),H=Number(ex.hh()),off=Number(ex.fb_off());const cv=document.getElementById('c');cv.width=W;cv.height=H;const ctx=cv.getContext('2d');const img=ctx.createImageData(W,H);const mem=new Uint8Array(ex.memory.buffer);ex.start();let modn=0;{const mi=location.hash.indexOf('mod=');if(mi>=0){for(const rec of location.hash.slice(mi+4).split(';')){const p=rec.split(',');if(p.length===4){const pk=v=>((v>>16)&255)|(((v>>8)&255)<<8)|((v&255)<<16);const h1=parseInt(p[1],16),h2=parseInt(p[2],16);if(!isNaN(h1)&&!isNaN(h2)&&Number(ex.pack_block(BigInt(p[0]|0),BigInt(pk(h1)),BigInt(pk(h2)),BigInt(p[3]|0)))===0)modn++;}}}}let keys=0,adx=0,ady=0,frames=0,peers=0;const t0=performance.now();const KM={KeyW:1,KeyS:2,KeyA:4,KeyD:8,Space:16,ShiftLeft:32,ShiftRight:32,KeyF:64,KeyI:128,KeyO:256};addEventListener('keydown',e=>{keys|=(KM[e.code]||0);if(e.code=='Space')e.preventDefault();if(e.code=='KeyG'){if(document.fullscreenElement)document.exitFullscreen();else document.documentElement.requestFullscreen();}});addEventListener('keyup',e=>{keys&=~(KM[e.code]||0);});addEventListener('mousemove',e=>{if(document.pointerLockElement===cv){adx+=e.movementX;ady+=e.movementY;}});cv.addEventListener('mousedown',e=>{if(document.pointerLockElement!==cv){cv.requestPointerLock();return;}e.preventDefault();if(e.button===0)ex.dig();else if(e.button===2)ex.put();});cv.addEventListener('contextmenu',e=>e.preventDefault());function inv(){const s=Number(ex.invert_state());return 'Y:'+((s>=10)?'inv':'norm')+' X:'+((s%10)?'inv':'norm');}const ROOM=(parseInt(location.hash.slice(1))||42),PEER=(crypto.getRandomValues(new Uint32Array(1))[0]%999999)+1,OUT=Number(ex.net_out_off()),IN=Number(ex.net_in_off());setInterval(async()=>{try{const len=Number(ex.net_push());const outb=new Uint8Array(ex.memory.buffer,OUT,len).slice();fetch('/pstate?r='+ROOM+'&p='+PEER,{method:'POST',body:outb}).catch(()=>{});const rr=await fetch('/roster?r='+ROOM+'&p='+PEER);const rb=new Uint8Array(await rr.arrayBuffer());new Uint8Array(ex.memory.buffer).set(rb.subarray(0,4096),IN);peers=Number(ex.net_ingest_roster(BigInt(rb.length)));}catch(e){}},150);function frame(){try{ex.tick(BigInt(keys),BigInt(adx),BigInt(ady));adx=0;ady=0;ex.render_cam();for(let i=0;i<W*H;i++){const b=off+i*8;img.data[i*4]=mem[b];img.data[i*4+1]=mem[b+1];img.data[i*4+2]=mem[b+2];img.data[i*4+3]=255;}ctx.putImageData(img,0,0);ctx.fillStyle='rgba(255,255,255,0.8)';ctx.fillRect((W>>1)-1,(H>>1)-5,2,11);ctx.fillRect((W>>1)-5,(H>>1)-1,11,2);frames++;if(frames===1){const ns=Number(ex.drawn_px());diag('MP BUILD R10-VISUAL ok, room '+ROOM+(modn?' MOD:'+modn:'')+', frame1 terrain-px='+ns+(ns<6000?' MOSTLY SKY - REPORT THIS':' - open another tab to meet'));}if(frames%240===0){diag('MP R10 room '+ROOM+' peers '+peers+' '+Math.round(frames*1000/(performance.now()-t0))+' fps - '+inv()+' (I/O flip) - WASD, L-dig R-place, F fly');}}catch(e){diag('GAME ERROR: '+e);return;}requestAnimationFrame(frame);}frame();}).catch(e=>{diag('WASM LOAD ERROR: '+e);});\n</script></body></html>\n" as *u8)
26 sys_close(fd)
27 hw(1, "nx_wasm_mineworld_mp_html: wrote html (logic-free JS)\n" as *u8)
28 return 0
29}