Back to Napplet Machines V8
SOURCE / PINNED RELEASE

Made of little things.

Napplet Machines V8

Release
b3f614e739f0…
Author-recorded commit
534f19acb4ba…
License
LICENSE
Author’s source reference
nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-b5572362d4a

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

tests/multiplayer.mjs
export default async ({ players, check, measure, diagnostics }) => {
  const host = players[0];
  await host.frame.locator('#trackSelect').selectOption('4');
  await host.frame
    .getByRole('button', { name: 'Workshop', exact: false })
    .click();
  await host.frame.locator('#widthInput').fill('20.8');
  await host.frame.locator('#widthInput').dispatchEvent('input');
  await host.frame.locator('#wallThicknessInput').fill('1.7');
  await host.frame.locator('#wallThicknessInput').dispatchEvent('input');
  await Promise.all(
    players.map(async (p, i) => {
      await p.frame
        .getByRole('button', { name: 'Rooms', exact: false })
        .click();
      await p.frame.locator('#playerName').fill(`Driver ${i + 1}`);
    }),
  );
  await host.frame
    .getByRole('button', { name: 'Host game', exact: false })
    .click();
  await host.frame.locator('#activeRoom').waitFor({ state: 'visible' });
  const room = await host.frame.locator('#activeCode').inputValue();
  check('host receives a room code', room.length > 10);
  for (const p of players.slice(1)) {
    await p.frame.locator('#roomCode').fill(room);
    await p.frame
      .getByRole('button', { name: 'Join room', exact: true })
      .click();
    await p.frame.locator('#activeRoom').waitFor({ state: 'visible' });
  }
  await Promise.all(
    players.map((p) =>
      p.frame.waitForFunction(
        () =>
          document.querySelector('#connectionState').textContent ===
          'Connected',
        undefined,
        { timeout: 35000 },
      ),
    ),
  );
  await Promise.all(
    players
      .slice(1)
      .map((p) =>
        p.frame.getByRole('button', { name: 'Ready up', exact: true }).click(),
      ),
  );
  await host.frame.waitForFunction(
    () => !document.querySelector('#startRoom').disabled,
  );
  check('all members ready', true);
  const routes = await diagnostics();
  check(
    'host star connects every guest',
    routes[0].filter((p) => p.state === 'connected').length ===
      players.length - 1 &&
      routes
        .slice(1)
        .every((rows) => rows.some((p) => p.state === 'connected')),
  );
  await host.frame
    .getByRole('button', { name: 'Start race', exact: true })
    .click();
  await Promise.all(
    players.map((p) =>
      p.frame.waitForFunction(
        () => document.querySelector('#scene').dataset.countdown === '0',
      ),
    ),
  );
  check('race starts on all players', true);
  check(
    'large track and positions beyond the old world boundary reach all peers',
    (
      await Promise.all(
        players.map(
          async (p) =>
            (await p.frame.locator('#sceneTitle').textContent()) ===
              'Switchback Summit' &&
            Math.abs(
              Number(
                await p.frame.locator('#scene').getAttribute('data-local-x'),
              ),
            ) > 100,
        ),
      )
    ).every(Boolean),
  );
  check(
    '20.8 m point width and section thickness reach every guest',
    (
      await Promise.all(
        players.map(
          async (p) =>
            (await p.frame.locator('#widthInput').inputValue()) === '20.8' &&
            (await p.frame.locator('#wallThicknessInput').inputValue()) ===
              '1.7',
        ),
      )
    ).every(Boolean),
  );
  for (const [index, p] of players.entries()) {
    const ms = await p.frame.evaluate(async () => {
      const canvas = document.querySelector('#scene'),
        beforeSpeed = Number(canvas.dataset.localSpeed),
        start = performance.now();
      canvas.dispatchEvent(
        new KeyboardEvent('keydown', { code: 'KeyW', bubbles: true }),
      );
      try {
        await new Promise((resolve, reject) => {
          const poll = () => {
            if (Number(canvas.dataset.localSpeed) > beforeSpeed + 0.12)
              resolve();
            else if (performance.now() - start > 1500)
              reject(new Error('No visible motion'));
            else requestAnimationFrame(poll);
          };
          requestAnimationFrame(poll);
        });
        return performance.now() - start;
      } finally {
        canvas.dispatchEvent(
          new KeyboardEvent('keyup', { code: 'KeyW', bubbles: true }),
        );
      }
    });
    measure(`player ${index + 1} input to rendered motion`, ms, 50);
  }
  const guest = players[1];
  await guest.frame.locator('#scene').focus();
  await guest.page.keyboard.down('w');
  await guest.page.waitForTimeout(1000);
  await guest.page.keyboard.up('w');
  await guest.frame.waitForFunction(
    () => Number(document.querySelector('#scene').dataset.ack) > 20,
  );
  check('host acknowledges simulated guest inputs', true);
  check(
    'prediction history bounded',
    Number(await guest.frame.locator('#scene').getAttribute('data-pending')) <
      120,
  );
  await guest.frame.locator('#scene').focus();
  const padInput = await guest.frame.evaluate(async () => {
    const original = navigator.getGamepads.bind(navigator);
    const pad = {
      index: 4,
      id: 'Network controller',
      mapping: 'standard',
      connected: true,
      axes: [0, 0],
      buttons: Array.from({ length: 17 }, () => ({ value: 0, pressed: false })),
    };
    Object.defineProperty(navigator, 'getGamepads', {
      configurable: true,
      value: () => [null, null, null, null, pad],
    });
    const frames = async (n) => {
      for (let i = 0; i < n; i++) await new Promise(requestAnimationFrame);
    };
    const before = Number(document.querySelector('#scene').dataset.ack);
    try {
      await frames(4);
      pad.buttons[0].value = 1;
      await frames(4);
      pad.buttons[0].value = 0;
      pad.buttons[7].value = 0.6;
      pad.axes[0] = -0.4;
      await frames(30);
      const d = document.querySelector('#scene').dataset;
      return {
        throttle: Number(d.inputThrottle),
        steer: Number(d.inputSteer),
        ack: Number(d.ack),
        before,
      };
    } finally {
      Object.defineProperty(navigator, 'getGamepads', {
        configurable: true,
        value: original,
      });
    }
  });
  check(
    'guest analog controller input enters prediction and receives host acknowledgements',
    Math.abs(padInput.throttle - 0.6) < 0.01 &&
      padInput.steer > 0.2 &&
      padInput.steer < 0.4 &&
      padInput.ack > padInput.before,
  );
  await host.frame.locator('#scene').focus();
  await host.page.keyboard.down('w');
  const continuity = await guest.frame.evaluate(async () => {
    const samples = [];
    for (let i = 0; i < 30; i++) {
      await new Promise(requestAnimationFrame);
      const s = document.querySelector('#scene').dataset;
      samples.push(s.remoteX + ',' + s.remoteY);
    }
    return new Set(samples).size;
  });
  await host.page.keyboard.up('w');
  check('remote cars interpolate between network snapshots', continuity > 12);
  const buildings = await Promise.all(
    players.map((p) =>
      p.frame.locator('#scene').getAttribute('data-buildings'),
    ),
  );
  check(
    'every peer regenerates the same trackside building count',
    Number(buildings[0]) >= 5 && new Set(buildings).size === 1,
  );
  await host.frame.evaluate(() => {
    const scene = document.querySelector('#scene');
    for (const code of ['KeyW', 'KeyA', 'Space'])
      scene.dispatchEvent(
        new KeyboardEvent('keydown', { code, bubbles: true }),
      );
  });
  try {
    await Promise.all(
      players.slice(1).map((p) =>
        p.frame.waitForFunction(
          () => {
            const d = document.querySelector('#scene').dataset;
            return (
              Number(d.remoteHandbrake) > 0.6 &&
              Math.abs(Number(d.remoteSteering)) > 0.05
            );
          },
          undefined,
          { timeout: 10000 },
        ),
      ),
    );
    check('steering and handbrake state reaches all five opponents', true);
    const poses = await Promise.all(
      players.map((p) =>
        p.frame.locator('#scene').getAttribute('data-local-pitch'),
      ),
    );
    check(
      'slope pose remains finite on every client',
      poses.every((p) => Number.isFinite(Number(p))),
    );
  } finally {
    await host.frame.evaluate(() => {
      const scene = document.querySelector('#scene');
      for (const code of ['KeyW', 'KeyA', 'Space'])
        scene.dispatchEvent(
          new KeyboardEvent('keyup', { code, bubbles: true }),
        );
    });
  }
  await guest.frame.waitForFunction(
    () =>
      Number(document.querySelector('#scene').dataset.remoteHandbrake) < 0.1,
  );
  check('handbrake release is synchronized', true);
  const beforeReconnect = Number(
    await guest.frame.locator('#scene').getAttribute('data-ack'),
  );
  await guest.frame.locator('#reconnectRace').click();
  await guest.frame.waitForFunction(
    (ack) => Number(document.querySelector('#scene').dataset.ack) > ack + 10,
    beforeReconnect,
    { timeout: 20000 },
  );
  check('guest transport reconnect resumes acknowledged simulation', true);
  await guest.frame
    .getByRole('button', { name: 'Exit race', exact: true })
    .click();
  await guest.frame.waitForFunction(
    () => document.querySelector('#scene').dataset.racing === 'false',
  );
  check('guest leaves race cleanly', true);
  await host.frame
    .getByRole('button', { name: 'Exit race', exact: true })
    .click();
  if (players.length > 2)
    await players[2].frame.waitForFunction(
      () => document.querySelector('#scene').dataset.racing === 'false',
      undefined,
      { timeout: 10000 },
    );
  check('host departure ends the remaining race', true);
};