Back to Supersonic RC Revive
SOURCE / PINNED RELEASE

Made of little things.

Supersonic RC Revive

Release
bb8b570f3600…
Author-recorded commit
073e2d2c0e63…
License
LICENSE
Author’s source reference
nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.shakespeare.diy%2F/SuperSonicRCRevive

Archive hash verified: 857599401cc78951…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

tests/online-multiplayer.test.mjs
import assert from 'node:assert/strict';
import { before, after, test } from 'node:test';
import { createServer } from 'vite';

let server, protocol, OnlineSession, privateAvailable, publicAvailable;
before(async () => {
  server = await createServer({ logLevel: 'error', server: { middlewareMode: true }, appType: 'custom', optimizeDeps: { noDiscovery: true, entries: [] } });
  protocol = await server.ssrLoadModule('/src/game/onlineProtocol.ts');
  ({ OnlineSession, privateAvailable, publicAvailable } = await server.ssrLoadModule('/src/game/onlineSession.ts'));
});
after(() => server?.close());
const me = 'a'.repeat(64), peer = 'b'.repeat(64), other = 'c'.repeat(64);
const roomId = '12345678-1234-4234-8234-123456789012', roomId2 = '12345678-1234-4234-8234-123456789013', newRoom = '12345678-1234-4234-8234-123456789099';
const descriptor = { pack: 'd'.repeat(64), level: null, physics: 'ssrc-rapier-v1' };
const roomOf = (over = {}) => ({ ...descriptor, founded: 1000, nonce: '0'.repeat(32), ...over });
const pose = { p: [0,0,40], q: [0,0,0,1], w: Array.from({length:4},()=>[0,0,-5,0,0]) };
const snapshot = (seq, time, x = 0) => ({...pose, type:'car', seq, time, p:[x,0,40]});
const tick = () => new Promise(r => setImmediate(r));
const wait = ms => new Promise(r => setTimeout(r, ms));

test('private rooms need only WebRTC; public rooms also need CVM', () => {
  const saved=Object.getOwnPropertyDescriptor(globalThis,'window');
  try {
    for(const [napplet,priv,pub] of [[undefined,false,false],[{},false,false],[{cvm:{}},false,false],[{webrtc:{}},true,false],[{cvm:{},webrtc:{}},true,true]]) {
      Object.defineProperty(globalThis,'window',{configurable:true,value:{napplet}});
      assert.equal(privateAvailable(),priv);assert.equal(publicAvailable(),pub);
    }
  } finally {if(saved)Object.defineProperty(globalThis,'window',saved);else delete globalThis.window;}
});

/** Fake transport: CVM room directory plus one WebRTC session per open. */
function fixture(mode = {kind:'private',code:'ABCDEF'}, {actor = me, rooms = []} = {}) {
  const sent = [], calls = [], closed = [], opened = [];
  const state = {rooms: rooms.map(r => ({...r, peers:[...r.peers]}))};
  let handler, unsubscribed = false;
  const snap = r => ({room:r.room, name:r.name, protocol:protocol.ONLINE_PROTOCOL, capacity:8, peers:[...r.peers]});
  const io = {
    async call(tool, args) {
      calls.push([tool,args]);
      if (tool === 'soy_session') return {actor};
      if (tool === 'soy_room_list') return {rooms: state.rooms.map(snap)};
      if (tool === 'soy_room_join') {
        const r = state.rooms.find(r => r.room === args.room);
        if (!r || r.peers.length >= 8) throw new Error('Room full');
        r.peers.push(actor); return snap(r);
      }
      if (tool === 'soy_room_create') { const r = {room:newRoom, name:args.name, peers:[actor]}; state.rooms.push(r); return snap(r); }
      if (tool === 'soy_room_status') return snap(state.rooms.find(r => r.room === args.room));
      if (tool === 'soy_room_leave') return {left:true};
      throw new Error(`unexpected ${tool}`);
    },
    async open(request) { opened.push(request); return {session:{id:`rtc${opened.length}`}}; },
    async send(id, message) { sent.push([id,message]); },
    async close(id) { closed.push(id); },
    events(fn) {handler=fn;return {close(){unsubscribed=true;}};},
  };
  const session = new OnlineSession(descriptor, mode, io, 'test-namespace', 20);
  const id = () => `rtc${opened.length}`;
  const emit = (from, payload) => handler({type:'message',sessionId:id(),from,payload});
  const join = key => handler({type:'peer',sessionId:id(),pubkey:key,state:'joined'});
  const leave = key => handler({type:'peer',sessionId:id(),pubkey:key,state:'left'});
  const beacons = () => sent.filter(([,m]) => m.type === 'room').map(([,m]) => m);
  return {session,io,state,sent,calls,closed,opened,emit,join,leave,beacons,get unsubscribed(){return unsubscribed;}};
}

test('private codes are six unambiguous characters and parse leniently', () => {
  const code = protocol.privateCode(b => b.map((_, i) => i * 37));
  assert.match(code,/^[A-HJKMNP-Z2-9]{6}$/);
  assert.equal(protocol.parsePrivateCode(' abc-def '),'ABCDEF');
  assert.equal(protocol.formatCode('ABCDEF'),'ABC-DEF');
  assert.equal(protocol.privateRoom('ABCDEF'),'ssrc-private:ABCDEF');
  for (const bad of ['','ABCDE','ABCDEFG','ABC-DE1','O0OOOO','ABC_DEF']) assert.throws(()=>protocol.parsePrivateCode(bad));
});
test('asset identity covers pack, custom level and physics version', () => {
  assert.equal(protocol.mismatch(descriptor,{...descriptor}),null);
  assert.match(protocol.mismatch(descriptor,{...descriptor,pack:'e'.repeat(64)}),/packs differ/);
  assert.match(protocol.mismatch(descriptor,{...descriptor,level:'e'.repeat(64)}),/Levels differ/);
  assert.match(protocol.mismatch(descriptor,{...descriptor,physics:'next'}),/versions differ/);
});
test('peer payload validation bounds coordinates, quaternions, wheels and room beacons', () => {
  assert.ok(protocol.decodeMessage(snapshot(1,50)));
  for (const value of [NaN,Infinity,1e8]) assert.equal(protocol.decodeMessage({...snapshot(1,50),p:[value,0,0]}),null);
  assert.equal(protocol.decodeMessage({...snapshot(1,50),q:[0,0,0,0]}),null);
  assert.equal(protocol.decodeMessage({...snapshot(1,50),w:[]}),null);
  assert.equal(protocol.decodeMessage({...snapshot(-1,50)}),null);
  assert.ok(protocol.decodeMessage({type:'room',descriptor:roomOf(),count:2}));
  for (const bad of [{nonce:'xyz'},{founded:-1},{founded:1.5},{pack:'nope'}]) assert.equal(protocol.decodeMessage({type:'room',descriptor:roomOf(bad),count:2}),null);
  assert.equal(protocol.decodeMessage({type:'room',descriptor:roomOf(),count:0}),null);
});
test('the earliest founded room wins, with the nonce breaking ties', () => {
  assert.ok(protocol.olderRoom(roomOf({founded:1}),roomOf({founded:2})));
  assert.ok(!protocol.olderRoom(roomOf({founded:2}),roomOf({founded:1})));
  assert.ok(protocol.olderRoom(roomOf({nonce:'0'.repeat(32)}),roomOf({nonce:'1'.repeat(32)})));
});
test('remote interpolation ignores stale sequences and clocks, handles teleports and expires', () => {
  const car = new protocol.RemoteCar();
  car.push(snapshot(0,100000,0),100);
  car.push(snapshot(1,100050,10),150);
  assert.equal(car.sample(225).p[0],5);
  assert.equal(car.push(snapshot(0,100100,100),200),false);
  assert.equal(car.push(snapshot(2,90000,100),200),false);
  car.push(snapshot(3,100150,1000),250);
  assert.equal(car.sample(250).p[0],1000);
  assert.equal(car.sample(3300),null);
});
test('an empty private room is founded over WebRTC alone, without any CVM call', async () => {
  const f = fixture();let ready=0;
  f.session.onReady=()=>ready++;
  try {
    await f.session.start();
    assert.deepEqual(f.opened[0].scope,{type:'room',room:'ssrc-private:ABCDEF'});
    assert.equal(f.opened[0].protocol,protocol.ONLINE_PROTOCOL);
    assert.equal(ready,0);
    await wait(40);
    assert.equal(ready,1);assert.equal(f.session.lane,0);
    assert.match(f.session.status,/Room ABC-DEF · 1\/8/);
    f.join(peer);
    assert.equal(f.beacons().at(-1).descriptor.pack,descriptor.pack);
    f.emit(peer,snapshot(0,1));
    assert.equal(f.session.remote.size,0,'cars count only after the peer confirms the room');
    f.emit(peer,{type:'room',descriptor:f.session.roomDescriptor,count:2});
    f.emit(peer,snapshot(0,1));
    assert.equal(f.session.remote.size,1);assert.equal(f.session.players,2);
    f.emit(other,snapshot(0,1));
    assert.equal(f.session.remote.size,1,'unconnected senders are ignored');
    f.emit(peer,{type:'bye'});
    assert.equal(f.session.remote.size,0);assert.equal(f.session.players,1);
  } finally {await f.session.close();}
  assert.equal(f.calls.length,0);assert.ok(f.unsubscribed);assert.deepEqual(f.closed,['rtc1']);
});
test('a private newcomer adopts the room it hears and spawns beside existing players', async () => {
  const f = fixture();let ready=0;
  f.session.onReady=()=>ready++;
  try {
    await f.session.start();f.join(peer);
    assert.ok(f.sent.some(([,m])=>m.type==='hello'));
    f.emit(peer,{type:'room',descriptor:roomOf(),count:3});
    assert.equal(ready,1);assert.equal(f.session.lane,3);
    assert.deepEqual(f.session.roomDescriptor,roomOf());
    await wait(40);
    assert.equal(f.session.roomDescriptor.founded,1000,'the founding timer never overrides an adopted room');
  } finally {await f.session.close();}
});
test('rooms stop at eight players', async () => {
  const f = fixture();let reason='';
  f.session.onEnd=r=>reason=r;
  await f.session.start();f.join(peer);
  f.emit(peer,{type:'room',descriptor:roomOf(),count:8});
  assert.match(reason,/full \(8 players\)/);assert.equal(f.session.ready,false);
  const g = fixture();let reason2='';
  g.session.onEnd=r=>reason2=r;
  await g.session.start();g.join(peer);g.emit(peer,{type:'full'});
  assert.match(reason2,/full/);
  const h = fixture();
  try {
    await h.session.start();await wait(40);
    const peers=Array.from({length:7},(_,i)=>String(i+1).repeat(64));
    for(const key of peers){h.join(key);h.emit(key,{type:'room',descriptor:h.session.roomDescriptor,count:2});}
    assert.equal(h.session.players,8);
    h.join(other);h.emit(other,{type:'hello'});
    assert.equal(h.sent.at(-1)[1].type,'full');
  } finally {await h.session.close();}
});
test('when two groups founded the same code, everyone joins the older room', async () => {
  const f = fixture();let reason='';
  f.session.onEnd=r=>reason=r;
  try {
    await f.session.start();await wait(40);
    const mine=f.session.roomDescriptor;
    f.join(peer);
    const newer={...mine,founded:mine.founded+1};
    f.emit(peer,{type:'room',descriptor:newer,count:1});
    assert.deepEqual(f.session.roomDescriptor,mine);assert.equal(f.beacons().at(-1).descriptor.founded,mine.founded);
    const older=roomOf({founded:2});
    f.emit(peer,{type:'room',descriptor:older,count:2});
    assert.deepEqual(f.session.roomDescriptor,older);assert.equal(f.session.players,2);
    f.join(other);
    f.emit(other,{type:'room',descriptor:roomOf({founded:1,pack:'e'.repeat(64)}),count:1});
    assert.match(reason,/different game data/);
  } finally {await f.session.close();}
});
test('a newcomer loads the room assets once before admission', async () => {
  const f=fixture();let resolve,loads=0,ready=0;
  const target=roomOf({pack:'e'.repeat(64),level:'f'.repeat(64),packServers:['https://blossom.example']});
  f.session.onAssetsRequired=async d=>{loads++;assert.deepEqual(d,target);return new Promise(r=>resolve=r);};
  f.session.onReady=()=>ready++;
  try {
    await f.session.start();f.join(peer);
    f.emit(peer,{type:'room',descriptor:target,count:2});f.emit(peer,{type:'room',descriptor:target,count:2});
    await wait(40);
    assert.equal(loads,1);assert.equal(ready,0);assert.match(f.session.status,/Downloading/);
    resolve({...descriptor,pack:target.pack,level:target.level});await tick();
    assert.equal(ready,1);assert.equal(f.session.lane,2);assert.equal(f.session.players,2);
    assert.equal(f.session.roomDescriptor,target);
  } finally {await f.session.close();}
});
test('asset load failure and cancellation never admit the player', async () => {
  const f=fixture();let reason='';
  f.session.onAssetsRequired=async()=>{throw new Error('Could not download that asset pack');};
  f.session.onEnd=text=>reason=text;
  await f.session.start();f.join(peer);
  f.emit(peer,{type:'room',descriptor:roomOf({pack:'e'.repeat(64)}),count:2});
  await tick();
  assert.equal(f.session.ready,false);assert.match(reason,/Could not download/);
  const g=fixture();let resolve,active;
  g.session.onAssetsRequired=async(d,isActive)=>{active=isActive;return new Promise(r=>resolve=r);};
  await g.session.start();g.join(peer);
  g.emit(peer,{type:'room',descriptor:roomOf({pack:'e'.repeat(64)}),count:2});
  await g.session.close();assert.equal(active(),false);
  const sent=g.sent.length;resolve({...descriptor,pack:'e'.repeat(64)});await tick();
  assert.equal(g.sent.length,sent);assert.equal(g.session.ready,false);
});
test('incompatible physics ends joining before any download', async () => {
  const f=fixture();let downloads=0,reason='';
  f.session.onAssetsRequired=async()=>{downloads++;return descriptor;};
  f.session.onEnd=text=>reason=text;
  await f.session.start();f.join(peer);
  f.emit(peer,{type:'room',descriptor:roomOf({pack:'e'.repeat(64),physics:'next'}),count:2});
  assert.equal(downloads,0);assert.match(reason,/versions differ/);
});

test('profile names are single-line, control-free and short; display name wins', async () => {
  const {tagName}=await server.ssrLoadModule('/src/game/onlineNames.ts');
  assert.equal(tagName({displayName:'  Robert‮  ',name:'rob'}),'Robert');
  assert.equal(tagName({displayName:'   ',name:'line\nbreak'}),'line break');
  assert.equal(tagName({name:'a'.repeat(30)}),'a'.repeat(15)+'…');
  assert.equal(tagName({name:'🏎️'.repeat(20)}).length<=32,true);
  for (const missing of [null,undefined,{},{name:42},{name:'\u0000'}]) assert.equal(tagName(missing),null);
});
test('beacons share this player\'s Nostr key; other players resolve names once through NAP-COMMON', async () => {
  const {ProfileNames}=await server.ssrLoadModule('/src/game/onlineNames.ts');
  const nostr='9'.repeat(64),lookups=[];
  const f=fixture();
  f.session.profile=me;
  f.session.names=new ProfileNames(async key=>{lookups.push(key);return key===nostr?{name:'Speedy'}:null;});
  try {
    await f.session.start();await wait(40);f.join(peer);
    assert.equal(f.beacons().at(-1).profile,me);
    assert.equal(protocol.decodeMessage({type:'room',descriptor:roomOf(),count:2,profile:'not-hex'}),null);
    f.emit(peer,{type:'room',descriptor:f.session.roomDescriptor,count:2,profile:nostr});
    assert.equal(f.session.label(peer),'BBBB','the key tag shows until the profile loads');
    await tick();
    assert.equal(f.session.label(peer),'Speedy');f.session.label(peer);
    assert.deepEqual(lookups,[nostr]);
    f.join(other);f.emit(other,{type:'room',descriptor:f.session.roomDescriptor,count:3});
    assert.equal(f.session.label(other),'CCCC','anonymous players keep their tag');
  } finally {await f.session.close();}
  const g=fixture();
  g.session.names=new ProfileNames(async()=>{throw new Error('common unavailable');});
  try {
    await g.session.start();await wait(40);g.join(peer);
    assert.equal(g.beacons().at(-1).profile,undefined,'guests share no key');
    g.emit(peer,{type:'room',descriptor:g.session.roomDescriptor,count:2,profile:'9'.repeat(64)});
    g.session.label(peer);await tick();
    assert.equal(g.session.label(peer),'BBBB');
  } finally {await g.session.close();}
});
test('public play creates a listed eight-player room when none has space', async () => {
  const full={room:roomId,name:'Public · x',peers:Array.from({length:8},(_,i)=>String(i+1).repeat(64))};
  const f=fixture({kind:'public'},{rooms:[full]});let ready=0;
  f.session.onReady=()=>ready++;
  try {
    await f.session.start();
    const create=f.calls.find(([tool])=>tool==='soy_room_create')[1];
    assert.deepEqual({capacity:create.capacity,listed:create.listed,protocol:create.protocol},{capacity:8,listed:true,protocol:protocol.ONLINE_PROTOCOL});
    assert.equal(create.name,`Public · ${protocol.assetTag(descriptor)}`);
    assert.equal(ready,1,'the creator founds immediately');
    assert.deepEqual(f.opened[0].scope,{type:'room',room:newRoom});
    assert.match(f.session.status,/^Public · 1\/8/);
  } finally {await f.session.close();}
  assert.ok(f.calls.some(([tool,args])=>tool==='soy_room_leave'&&args.room===newRoom));
});
test('public play prefers rooms with matching assets, then the fullest, and retries a lost seat', async () => {
  const tag=protocol.assetTag(descriptor);
  const rooms=[{room:roomId,name:'Public · other',peers:[peer,other]},{room:roomId2,name:`Public · ${tag}`,peers:[peer]}];
  const f=fixture({kind:'public'},{rooms});
  try {
    await f.session.start();
    assert.equal(f.calls.find(([tool])=>tool==='soy_room_join')[1].room,roomId2);
    assert.deepEqual(f.opened[0].scope,{type:'room',room:roomId2});
    assert.equal(f.session.ready,false,'joiners wait to hear the room');
  } finally {await f.session.close();}
  const g=fixture({kind:'public'},{rooms:[{room:roomId,name:'Public · x',peers:[peer]},{room:roomId2,name:'Public · y',peers:[other]}]});
  const join=g.io.call.bind(g.io);const attempts=[];
  g.io.call=async(tool,args)=>{if(tool==='soy_room_join'){attempts.push(args.room);if(attempts.length===1)throw new Error('Room full');}return join(tool,args);};
  try {
    await g.session.start();
    assert.deepEqual(attempts,[roomId,roomId2]);
    assert.equal(g.session.room.room,roomId2);
  } finally {await g.session.close();}
});
test('public play lists again when every seat was taken, and when the directory fails', async () => {
  const f=fixture({kind:'public'},{rooms:[{room:roomId,name:'Public · x',peers:[peer]}]});
  const call=f.io.call.bind(f.io);let lists=0;
  f.io.call=async(tool,args)=>{
    if(tool==='soy_room_list'&&++lists===2)f.state.rooms.push({room:roomId2,name:'Public · y',peers:[other]});
    if(tool==='soy_room_join'&&args.room===roomId)throw new Error('Room full');
    return call(tool,args);
  };
  try {
    await f.session.start();
    assert.equal(lists,2);assert.equal(f.session.room.room,roomId2);
    assert.ok(!f.calls.some(([tool])=>tool==='soy_room_create'));
  } finally {await f.session.close();}
  const g=fixture({kind:'public'},{rooms:[{room:roomId,name:'Public · x',peers:[peer]}]});
  const gcall=g.io.call.bind(g.io);let failed=false;
  g.io.call=async(tool,args)=>{if(tool==='soy_room_list'&&!failed){failed=true;throw new Error('Request timed out');}return gcall(tool,args);};
  try {
    await g.session.start();
    assert.equal(g.session.room.room,roomId);
  } finally {await g.session.close();}
});
test('a lone public player moves into a room with the same assets; of two lone rooms only one side moves', async () => {
  const tag=protocol.assetTag(descriptor);
  const f=fixture({kind:'public'});let reason='';
  f.session.onEnd=r=>reason=r;
  try {
    await f.session.start();
    assert.equal(f.session.room.room,newRoom);
    f.state.rooms.push({room:roomId,name:'Public · other',peers:[peer,other]});
    for(let i=0;i<3;++i)await f.session.poll();
    assert.equal(f.session.room.room,newRoom,'rooms needing other assets are left alone');
    const higher='12345678-1234-4234-8234-123456789999';
    f.state.rooms.push({room:higher,name:`Public · ${tag}`,peers:[peer]});
    for(let i=0;i<3;++i)await f.session.poll();
    assert.equal(f.session.room.room,newRoom,'the lower lone room stays put');
    f.state.rooms.push({room:roomId2,name:`Public · ${tag}`,peers:[peer]});
    for(let i=0;i<3;++i)await f.session.poll();
    assert.equal(f.session.room.room,roomId2);
    assert.ok(f.calls.some(([tool,args])=>tool==='soy_room_leave'&&args.room===newRoom));
    assert.deepEqual(f.closed,['rtc1']);assert.deepEqual(f.opened[1].scope,{type:'room',room:roomId2});
    assert.equal(f.session.ready,true);assert.equal(reason,'');
    f.join(peer);f.emit(peer,{type:'room',descriptor:roomOf(),count:2});
    assert.equal(f.session.players,2,'the moved player meets the room on its new session');
    for(let i=0;i<3;++i)await f.session.poll();
    assert.equal(f.opened.length,2,'connected players never move');
  } finally {await f.session.close();}
});
test('a public room continues after its creator leaves and admits only current CVM members', async () => {
  const f=fixture({kind:'public'},{rooms:[{room:roomId,name:'Public · x',peers:[peer,other]}]});let reason='';
  f.session.onEnd=r=>reason=r;
  try {
    await f.session.start();f.join(peer);f.join(other);
    f.emit(peer,{type:'room',descriptor:roomOf(),count:3});
    f.emit(other,{type:'room',descriptor:roomOf(),count:3});
    f.emit(peer,snapshot(0,0));
    assert.equal(f.session.players,3);
    f.state.rooms[0].peers=f.state.rooms[0].peers.filter(p=>p!==peer);
    await f.session.poll();
    assert.equal(reason,'');assert.equal(f.session.remote.size,0);
    assert.equal(f.opened.length,1,'membership changes never reopen the mesh');
    assert.equal(f.session.players,2);assert.equal(f.session.ready,true);
    f.emit(peer,{type:'room',descriptor:roomOf(),count:3});f.emit(peer,snapshot(1,1));
    assert.equal(f.session.players,2,'a departed member is not readmitted');assert.equal(f.session.remote.size,0);
    const late='e'.repeat(64);
    f.join(late);f.emit(late,{type:'room',descriptor:roomOf(),count:3});
    assert.equal(f.session.players,2,'unknown peers wait for the next roster');
    f.state.rooms[0].peers.push(late);await f.session.poll();
    f.emit(late,{type:'room',descriptor:roomOf(),count:3});
    assert.equal(f.session.players,3);
  } finally {await f.session.close();}
});
test('a transient room renewal failure keeps driving until membership would lapse', async () => {
  const f=fixture({kind:'public'});let reason='';
  f.session.onEnd=r=>reason=r;
  try {
    await f.session.start();
    const call=f.io.call.bind(f.io);
    f.io.call=async(tool,args)=>tool==='soy_room_status'?Promise.reject(new Error('Request timed out')):call(tool,args);
    await f.session.poll();
    assert.equal(reason,'');assert.equal(f.session.ready,true);
    f.io.call=async(tool,args)=>tool==='soy_room_status'?Promise.reject(new Error('Room expired')):call(tool,args);
    await f.session.poll();
    assert.match(reason,/Room expired/);
  } finally {await f.session.close();}
});
test('cancelling an in-flight public room creation leaves the late room without opening RTC', async () => {
  const f=fixture({kind:'public'});let resolve;
  const call=f.io.call.bind(f.io);
  f.io.call=async(tool,args)=>tool==='soy_room_create'?new Promise(r=>resolve=r):call(tool,args);
  const pending=f.session.start();
  await wait(5);
  await f.session.close();
  resolve({room:newRoom,protocol:protocol.ONLINE_PROTOCOL,capacity:8,peers:[me]});
  await pending;
  assert.equal(f.opened.length,0);
  assert.ok(f.calls.some(([tool])=>tool==='soy_room_leave'));
});
test('RTC permission denial ends the session and leaves the public room', async () => {
  const f=fixture({kind:'public'});let reason='';
  f.io.open=async()=>{throw new Error('Peer connections blocked');};
  f.session.onEnd=text=>reason=text;
  await assert.rejects(f.session.start(),/blocked/);
  await tick();
  assert.match(reason,/blocked/);assert.ok(f.unsubscribed);
  assert.ok(f.calls.some(([tool])=>tool==='soy_room_leave'));
});
test('snapshot sending never queues behind a pending send', async () => {
  const f=fixture();
  try {
    await f.session.start();f.join(peer);f.emit(peer,{type:'room',descriptor:roomOf(),count:2});
    let resolve, calls=0;
    f.io.send=async()=>{calls++;return new Promise(r=>resolve=r);};
    f.session.update(pose,100);
    for(let now=150;now<1000;now+=50) f.session.update(pose,now);
    assert.equal(calls,1);
    resolve();await tick();
    f.session.update(pose,1000);assert.equal(calls,2);
    resolve();await tick();
    f.io.send=async()=>{};
  } finally {await f.session.close();}
});
test('a late RTC open after cancellation is immediately closed', async () => {
  const f=fixture();let resolve;
  f.io.open=()=>new Promise(r=>resolve=r);
  const pending=f.session.start();await tick();
  await f.session.close();resolve({session:{id:'late'}});await pending;
  assert.deepEqual(f.closed,['late']);assert.equal(f.session.ready,false);
});
test('departure cleanup still closes RTC when a send never settles', async () => {
  const f=fixture();await f.session.start();f.join(peer);
  f.io.send=()=>new Promise(()=>{});
  await f.session.close();
  assert.deepEqual(f.closed,['rtc1']);assert.ok(f.unsubscribed);
});

test('descriptor download hints accept bounded HTTPS origins only', () => {
  assert.ok(protocol.validDescriptor({...descriptor,packServers:['https://blossom.example']}));
  for(const packServers of [['http://example.com'],['https://example.com/path'],['https://user:password@example.com'],Array(9).fill('https://example.com'),[42]])
    assert.equal(protocol.validDescriptor({...descriptor,packServers}),false);
});

function assetFixture() {
  const oldPack=new ArrayBuffer(1),newPack=new ArrayBuffer(2),oldLevel=new ArrayBuffer(3),newLevel=new ArrayBuffer(4);
  let pack=oldPack,level=oldLevel;
  const downloads=[],installs=[];
  const hash=bytes=>bytes===oldPack?descriptor.pack:bytes===newPack?'e'.repeat(64):bytes===oldLevel?'1'.repeat(64):'f'.repeat(64);
  const target={...descriptor,pack:hash(newPack),level:hash(newLevel),packServers:['https://pack.example'],levelServers:['https://level.example']};
  const io={
    descriptor:async()=>({...descriptor,pack:hash(pack),level:level?hash(level):null}),
    pack:()=>pack,level:()=>level,
    download:async url=>{downloads.push(url);return url.includes(target.pack)?newPack:newLevel;},
    installPack:bytes=>{installs.push(bytes);pack=bytes;level=null;},
    installLevel:bytes=>{installs.push(bytes);level=bytes;},
    isGlb:bytes=>bytes===oldLevel||bytes===newLevel,
  };
  return {io,target,downloads,installs,oldPack,oldLevel,newPack,newLevel};
}

test('downloads host pack and custom level with hints, verifies hashes and rebuilds once', async () => {
  const {loadHostAssets}=await server.ssrLoadModule('/src/game/onlineAssets.ts');
  const f=assetFixture();let rebuilds=0;
  const result=await loadHostAssets(f.target,()=>true,async()=>{rebuilds++;},f.io);
  assert.equal(protocol.mismatch(f.target,result),null);assert.equal(rebuilds,1);
  assert.equal(f.io.pack(),f.newPack);assert.equal(f.io.level(),f.newLevel);
  assert.ok(f.downloads[0].includes(encodeURIComponent('https://pack.example')));
  assert.ok(f.downloads[1].includes(encodeURIComponent('https://level.example')));
});

test('matching bytes are reused and the guest overlay is removed when the host has none', async () => {
  const {loadHostAssets}=await server.ssrLoadModule('/src/game/onlineAssets.ts');
  const f=assetFixture();const target={...descriptor,level:null};
  await loadHostAssets(target,()=>true,async()=>{},f.io);
  assert.equal(f.downloads.length,0);assert.equal(f.io.pack(),f.oldPack);assert.equal(f.io.level(),null);
});

test('failed downloads and cancellation before install leave the previous assets untouched', async () => {
  const {loadHostAssets}=await server.ssrLoadModule('/src/game/onlineAssets.ts');
  for(const cancel of [false,true]) {
    const f=assetFixture();let rebuilds=0;
    if(!cancel) f.io.download=async()=>{throw new Error('Resource unavailable');};
    await assert.rejects(loadHostAssets(f.target,()=>!cancel,async()=>{rebuilds++;},f.io),cancel?/cancelled/:/unavailable/);
    assert.equal(f.installs.length,0);assert.equal(rebuilds,0);
    if(cancel) assert.equal(f.downloads.length,0);
    assert.equal(f.io.pack(),f.oldPack);assert.equal(f.io.level(),f.oldLevel);
  }
});

test('hash mismatch, failed rebuild and cancellation during rebuild restore the previous scene', async () => {
  const {loadHostAssets}=await server.ssrLoadModule('/src/game/onlineAssets.ts');
  for(const failure of ['hash','build','cancel']) {
    const f=assetFixture();let active=true,rebuilds=0;
    if(failure==='hash') f.target.pack='0'.repeat(64);
    const reload=async()=>{rebuilds++;if(rebuilds===1){if(failure==='build')throw new Error('Invalid track');if(failure==='cancel')active=false;}};
    await assert.rejects(loadHostAssets(f.target,()=>active,reload,f.io));
    assert.equal(f.io.pack(),f.oldPack);assert.equal(f.io.level(),f.oldLevel);
    assert.equal(rebuilds,failure==='hash'?1:2);
  }
});

test('download provenance retains Blossom hints for the next player', async () => {
  const {fetchPackLink,assetServers}=await server.ssrLoadModule('/src/game/assets.ts');
  const saved=Object.getOwnPropertyDescriptor(globalThis,'window');
  const buffer=new Uint8Array([1,2,3]).buffer;
  const hash=[...new Uint8Array(await crypto.subtle.digest('SHA-256',buffer))].map(b=>b.toString(16).padStart(2,'0')).join('');
  const requests=[];
  const resource={bytes:async(url,options)=>{requests.push({url,options});return new Blob([buffer]);}};
  Object.defineProperty(globalThis,'window',{configurable:true,value:{napplet:{resource}}});
  try {
    const downloaded=await fetchPackLink(`https://pack.example/${hash}.ssrcpack?xs=mirror.example`);
    assert.deepEqual(assetServers(downloaded),['https://pack.example','https://mirror.example']);
    assert.equal(requests[0].url,`blossom:sha256:${hash}`);
    assert.deepEqual(requests[0].options.servers,assetServers(downloaded));
  } finally {if(saved)Object.defineProperty(globalThis,'window',saved);else delete globalThis.window;}
});