Back to Crossfire
SOURCE / PINNED RELEASE

Made of little things.

Crossfire

Release
9614abe48623
Author-recorded commit
6f6c8299e2fb…
License
LICENSE
Author’s source reference
nostr://npub1n8ga89w8h6tvwamxusfyzexw8gjy84yxu9rxgnmk955cxtml4ujswzxydd/wss%3A%2F%2Fgit.napplet.soy%2F/n-347ab685aef

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

tests/netcode.test.mjs
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import ts from 'typescript';
import vm from 'node:vm';
const modules={};
for(const name of ['game','netcode']){const module={exports:{}};vm.runInNewContext(ts.transpileModule(fs.readFileSync(new URL('../src/'+name+'.ts',import.meta.url),'utf8'),{compilerOptions:{module:ts.ModuleKind.CommonJS,target:ts.ScriptTarget.ES2022}}).outputText,{module,exports:module.exports,structuredClone,require:()=>modules.game});modules[name]=module.exports;}
const {createWorld,movePlayer,idle,step}=modules.game;
const {Prediction,Interpolation,Encoder,Decoder,validCommands,TICK}=modules.netcode;
test('local move, aim and shot feedback happen before any host response; ack replay converges',()=>{
 const w=createWorld(),p=new Prediction();p.reconcile(w.p[0],0);const u={x:1,y:0,a:1,fire:true};
 p.advance(u,-1);assert.ok(p.local.x>w.p[0].x);assert.equal(p.local.a,1);assert.equal(p.fx.length,1);
 for(let i=0;i<29;i++)p.advance(u,-1);
 for(let i=0;i<15;i++)movePlayer(w.p[0],u,TICK);
 p.reconcile(w.p[0],15);assert.ok(Math.abs(p.local.x-210)<.001);assert.equal(p.pending.length,15);
 for(let i=0;i<15;i++)movePlayer(w.p[0],u,TICK);p.reconcile(w.p[0],30);assert.equal(p.pending.length,0);assert.ok(Math.abs(p.local.x-w.p[0].x)<.001);
});
test('prediction shares wall collision, bounds history and resets death/respawn',()=>{
 const w=createWorld(),p=new Prediction();w.p[0].x=190;w.p[0].y=200;p.reconcile(w.p[0],0);
 for(let i=0;i<180;i++)p.advance({...idle(),x:1},-1);assert.ok(p.local.x<=195);assert.equal(p.pending.length,120);
 w.p[0].dead=2;w.p[0].hp=0;p.reconcile(w.p[0],120);p.advance({...idle(),fire:true},-1);assert.equal(p.fx.length,0);
 w.p[0].dead=0;w.p[0].hp=100;w.p[0].spawn++;w.p[0].x=100;p.reconcile(w.p[0],121);assert.equal(p.local.x,100);assert.equal(p.pending.length,0);
});
test('remote interpolation fills 20Hz gaps and does not blend respawns',()=>{
 const w=createWorld(),i=new Interpolation();for(let n=0;n<6;n++){w.p[1].x=400+n*11;i.push(n*50, w);}
 for(let n=0;n<6;n++)i.render(TICK); // Initial buffer fills before steady motion.
 const positions=[];for(let n=0;n<6;n++)positions.push(i.render(TICK).p[1].x);
 assert.ok(positions.every((x,n)=>!n||x>positions[n-1]));assert.ok(positions.slice(1).every((x,n)=>x-positions[n]<5));
 w.p[1].spawn++;w.p[1].x=860;i.push(300,w);for(let n=0;n<30;n++)i.render(TICK);assert.equal(i.render(TICK).p[1].x,860);
});
test('delta snapshots omit unchanged players and existing bullet trajectories, recover on keyframes',()=>{
 const w=createWorld(),e=new Encoder(),d=new Decoder();const first=e.encode(w,0,[0,0]);assert.ok(d.decode(first));
 step(w,[{...idle(),fire:true},idle()],TICK);const shot=e.encode(w,50,[0,0]);assert.equal(shot.b.length,1);assert.ok(d.decode(shot));
 step(w,[idle(),idle()],TICK);const next=e.encode(w,100,[0,0]);assert.equal(next.b.length,0);assert.equal(next.roster,undefined);assert.ok(d.decode(next));
 const late=new Decoder();assert.equal(late.decode(next),undefined);const key=e.encode(w,1100,[0,0]);assert.ok(late.decode(key));
 const gap=e.encode(w,1150,[0,0]);e.encode(w,1200,[0,0]);assert.ok(d.decode(key));assert.ok(d.decode(gap));assert.equal(d.decode(e.encode(w,1250,[0,0])),undefined);assert.ok(d.decode(e.encode(w,1300,[0,0],true)));
});
test('malformed input batches and invalid delta slots cannot alter state',()=>{
 assert.equal(validCommands([[1,0,0,0,1]]),true);for(const v of [[[1,NaN,0,0,1]],[[1,2,0,0,1]],[[1,0,0,0,2]],Array(13).fill([1,0,0,0,0])])assert.equal(validCommands(v),false);
 const d=new Decoder(),e=new Encoder(),m=e.encode(createWorld(),0,[0,0]);m.p[0][0]=9;assert.equal(d.decode(m),undefined);
});