Back to Napplet Machines V8
SOURCE / PINNED RELEASE

Made of little things.

Napplet Machines V8

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

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

tests/network-benchmark.mjs
export async function benchmark(
  { players, check, measure, diagnostics, network },
  conditions = {},
) {
  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' });
  }
  try {
    await Promise.all(
      players.map((p) =>
        p.frame.waitForFunction(
          () => document.querySelector('#connectionState').textContent === 'Connected',
          undefined,
          { timeout: 35000 },
        ),
      ),
    );
  } catch (e) {
    console.log(
      'CONNECT_FAILURE',
      await Promise.all(players.map((p) => p.frame.locator('body').innerText())),
    );
    throw e;
  }
  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',
    new Set(routes[0].filter((p) => p.state === 'connected').map((p) => p.peer)).size ===
      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 p of players)
    await p.frame.evaluate(() => {
      window.netMeter = { bytes: 0, messages: 0, binary: 0, sizes: [], sent: new Map(), ackMs: [] };
      const remember = (seq) => {
        if (!window.netMeter.sent.has(seq)) window.netMeter.sent.set(seq, performance.now());
      };
      window.netMeterTimer = setInterval(() => {
        const m = window.netMeter,
          ack = Number(document.querySelector('#scene').dataset.ack);
        if (m.sent.has(ack)) m.ackMs.push(performance.now() - m.sent.get(ack));
        for (const seq of m.sent.keys()) if (seq <= ack || m.sent.size > 240) m.sent.delete(seq);
      }, 5);
      const api = window.napplet.webrtc,
        original = api.send.bind(api);
      api.send = (id, payload) => {
        if (payload.type === 'input') for (const c of payload.commands) remember(c.seq);
        const n = new TextEncoder().encode(JSON.stringify(payload)).length;
        window.netMeter.bytes += n;
        window.netMeter.messages++;
        window.netMeter.sizes.push(n);
        return original(id, payload);
      };
      if (api.sendBytes) {
        const bytes = api.sendBytes.bind(api);
        api.sendBytes = (id, value) => {
          const raw = Uint8Array.from(atob(value), (c) => c.charCodeAt(0)),
            view = new DataView(raw.buffer);
          if (raw[3] === 2)
            for (let i = 0; i < raw[20]; i++) remember(view.getUint32(21 + i * 9, true));
          const n = raw.length;
          window.netMeter.bytes += n;
          window.netMeter.messages++;
          window.netMeter.binary++;
          window.netMeter.sizes.push(n);
          return bytes(id, value);
        };
      }
    });
  const guest = players[1];
  await guest.frame.locator('#scene').focus();
  await guest.page.keyboard.down('w');
  const phases = [];
  for (const latencyMs of [50, 200, 50]) {
    await network({ latencyMs, jitterMs: 15, ...conditions });
    const pending = [],
      positions = [];
    for (let i = 0; i < 50; i++) {
      const row = await guest.frame.evaluate(() => ({
        ...document.querySelector('#scene').dataset,
      }));
      pending.push(Number(row.pending));
      positions.push(row.remoteX + ',' + row.remoteY);
      await guest.page.waitForTimeout(100);
    }
    pending.sort((a, b) => a - b);
    phases.push({
      latencyMs,
      pendingP50: pending[25],
      pendingP95: pending[47],
      pendingMax: pending[49],
    });
  }
  await guest.page.keyboard.up('w');
  const traffic = await Promise.all(
    players.map((p) =>
      p.frame.evaluate(() => {
        clearInterval(window.netMeterTimer);
        const m = window.netMeter;
        m.ackMs.sort((a, b) => a - b);
        m.sizes.sort((a, b) => a - b);
        return {
          ackMsP50: m.ackMs[Math.floor(m.ackMs.length * 0.5)],
          ackMsP95: m.ackMs[Math.floor(m.ackMs.length * 0.95)],
          bytes: m.bytes,
          messages: m.messages,
          binary: m.binary,
          p95Bytes: m.sizes[Math.floor(m.sizes.length * 0.95)],
        };
      }),
    ),
  );
  console.log(
    'NETWORK_BENCHMARK ' + JSON.stringify({ phases, traffic, diagnostics: await diagnostics() }),
  );
  check(
    'benchmark completes with bounded prediction history',
    phases.every((p) => p.pendingMax < 120),
  );
}

export default (context) => benchmark(context);