Back to Poly Dogfight
SOURCE / PINNED RELEASE

Made of little things.

Poly Dogfight

Release
b8511ee374e3…
Author-recorded commit
5579434b9d8b…
License
LICENSE
Author’s source reference
nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.napplet.soy%2F/n-44f63e5422b

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

tests/gamepad.test.mjs
import assert from 'node:assert/strict';
import test from 'node:test';
import { createGamepadInput } from '../src/gamepad.ts';
import { createInput } from '../src/input.ts';
import { planRoster } from '../src/players.ts';
const sample = (index, id = `pad-${index}`, mapping = 'standard') => ({ index, id, mapping, connected: true, axes: [0, 0, 0, 0], buttons: Array.from({ length: 17 }, () => ({ value: 0, pressed: false })) });
const bindings = { turn: [{ axis: 0 }], start: [{ button: 0 }] };

test('polling discovers idle pads, sparse slots, unplugging and reconnection without events', () => {
  let pads = [];
  const input = createGamepadInput(bindings, { read: () => pads, active: () => true });
  assert.equal(input.poll().status, 'waiting');
  pads = [sample(0), null, sample(3)];
  assert.deepEqual(input.poll().connected, [0, 3]);
  assert.deepEqual(input.poll().players.map(p => p.index), [0, 3]);
  pads = [sample(0)]; assert.deepEqual(input.poll().disconnected, [3]);
  pads.push(sample(3)); assert.deepEqual(input.poll().connected, [3]);
  input.dispose(); assert.equal(input.poll().status, 'closed');
});

test('temporary read errors neutralize controls without losing seats or explicit mappings', () => {
  const pad = sample(2, 'custom', ''); let error = null;
  const input = createGamepadInput(bindings, { read: () => { if (error) throw error; return [pad]; }, active: () => true });
  input.poll(); input.remap(2, bindings); input.poll();
  pad.axes[0] = 1; pad.buttons[0].value = 1;
  assert.equal(input.poll().players[0].actions.start.pressed, true);
  for (const name of ['Error', 'SecurityError']) {
    error = new Error('temporary access failure'); error.name = name;
    const frame = input.poll();
    assert.equal(frame.status, name === 'SecurityError' ? 'blocked' : 'unavailable');
    assert.deepEqual(frame.disconnected, []); assert.deepEqual(frame.connected, []);
    assert.equal(frame.players[0].mapped, true);
    assert.equal(frame.players[0].actions.turn.value, 0); assert.equal(frame.players[0].actions.start.down, false);
    error = null;
    const recovered = input.poll();
    assert.deepEqual(recovered.connected, []);
    assert.equal(recovered.players[0].actions.start.pressed, false);
    assert.equal(recovered.players[0].actions.turn.value, 1);
  }
  input.dispose();
});

test('focus recovery keeps devices and suppresses held start, pause and mode presses', () => {
  let active = true; const pad = sample(0);
  const input = createGamepadInput(bindings, { read: () => [pad], active: () => active });
  input.poll(); pad.buttons[0].value = 1; assert.equal(input.poll().players[0].actions.start.pressed, true);
  active = false; const blurred = input.poll();
  assert.equal(blurred.status, 'inactive'); assert.equal(blurred.players[0].actions.start.down, false);
  assert.deepEqual(blurred.disconnected, []);
  active = true; assert.equal(input.poll().players[0].actions.start.pressed, false);
  pad.buttons[0].value = 0; input.poll(); pad.buttons[0].value = 1;
  assert.equal(input.poll().players[0].actions.start.pressed, true);
  input.dispose();
});

test('replacement resets an explicit mapping instead of assigning it to a different device', () => {
  let pad = sample(1, 'old', '');
  const input = createGamepadInput(bindings, { read: () => [pad], active: () => true });
  input.poll(); input.remap(1, bindings); assert.equal(input.poll().players[0].mapped, true);
  pad = sample(1, 'new', ''); const frame = input.poll();
  assert.deepEqual(frame.connected, [1]); assert.deepEqual(frame.disconnected, [1]);
  assert.equal(frame.players[0].mapped, false); input.dispose();
});

test('game input assigns idle controllers immediately and preserves roster across focus/read failures', () => {
  const originals = ['window', 'document', 'navigator'].map(key => [key, Object.getOwnPropertyDescriptor(globalThis, key)]);
  class Element extends EventTarget { style = {}; }
  const doc = new Element(); doc.hidden = false; doc.hasFocus = () => focused;
  const win = new Element(); win.document = doc;
  let pads = [], focused = true, error = false;
  Object.defineProperty(globalThis, 'window', { configurable: true, value: win });
  Object.defineProperty(globalThis, 'document', { configurable: true, value: doc });
  Object.defineProperty(globalThis, 'navigator', { configurable: true, value: { getGamepads: () => { if(error) throw new Error('temporary');return pads; } } });
  let input;
  try {
    input = createInput(Object.fromEntries(['stick','stickKnob','throttle','boost','yawLeft','yawRight','fire','rocket'].map(key => [key, new Element()])));
    let roster = [null];
    const poll = () => { const frame = input.poll(1/60);roster = planRoster(roster, frame.activePads).map(p => p.pad);return frame; };
    assert.deepEqual(poll().activePads, []);
    pads = [sample(0), null, sample(3), sample(4, 'unsupported', '')];
    assert.deepEqual(poll().activePads, [0,3]); assert.deepEqual(roster, [0,3]);
    focused = false; assert.deepEqual(poll().activePads, [0,3]); assert.deepEqual(roster, [0,3]);
    error = true; assert.deepEqual(poll().activePads, [0,3]); assert.deepEqual(roster, [0,3]);
    error = false; focused = true; assert.deepEqual(poll().activePads, [0,3]);
    pads = [sample(0)];poll();assert.deepEqual(roster, [0]);
    pads.push(sample(3));poll();assert.deepEqual(roster, [0,3]);
  } finally {
    input?.dispose();
    for (const [key, descriptor] of originals) { if(descriptor)Object.defineProperty(globalThis,key,descriptor);else delete globalThis[key]; }
  }
});