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.
import type { World, Body, Input } from './physics.js';
// Application format MC7/1. Roster/name/car arrive reliably in the race start.
// Binary frames never carry an identity; the authenticated event.from owns input.
export const stateFields = [
'x',
'y',
'z',
'vx',
'vy',
'vz',
'angle',
'spin',
'steering',
'handbrake',
'progress',
'lastDistance',
'safeDistance',
'lap',
'finished',
'falls',
'respawn',
'boost',
'pitch',
'roll',
'ack',
'nextGate',
'stuck',
] as const;
type Command = { seq: number; input: Input };
export type StatePacket =
| { type: 'world'; race: string; world: World }
| { type: 'input'; race: string; commands: Command[] };
const header = 20;
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
export function encodeState(packet: StatePacket): Uint8Array | undefined {
if (!uuid.test(packet.race)) return;
const world = packet.type === 'world' ? packet.world : undefined;
const commands = packet.type === 'input' ? packet.commands : [];
const length = world
? header + 14 + world.bodies.length * (1 + stateFields.length * 4)
: header + 1 + commands.length * 9;
if (length > 16384 || commands.length > 120) return;
const bytes = new Uint8Array(length),
view = new DataView(bytes.buffer);
bytes.set([77, 67, 1, world ? 1 : 2]);
bytes.set(
packet.race
.replaceAll('-', '')
.match(/../g)!
.map((h) => parseInt(h, 16)),
4,
);
let offset = header;
const u32 = (n: number) => {
view.setUint32(offset, n, true);
offset += 4;
};
const f32 = (n: number) => {
view.setFloat32(offset, n, true);
offset += 4;
};
if (world) {
u32(world.tick);
f32(world.time);
f32(world.countdown);
bytes[offset++] = world.ended ? 1 : 0;
bytes[offset++] = world.bodies.length;
for (const b of world.bodies) {
bytes[offset++] = (b.grounded ? 1 : 0) | (b.boosting ? 2 : 0) | (b.ai ? 4 : 0);
for (const field of stateFields) field === 'ack' ? u32(b[field]) : f32(b[field]);
}
} else {
bytes[offset++] = commands.length;
for (const c of commands) {
u32(c.seq);
view.setInt16(offset, Math.round(c.input.throttle * 32767), true);
offset += 2;
view.setInt16(offset, Math.round(c.input.steer * 32767), true);
offset += 2;
bytes[offset++] = (c.input.brake ? 1 : 0) | (c.input.boost ? 2 : 0) | (c.input.reset ? 4 : 0);
}
}
return bytes;
}
export function decodeState(
bytes: Uint8Array,
race: string,
roster: Body[],
): StatePacket | undefined {
if (
bytes.length < header + 1 ||
bytes.length > 16384 ||
bytes[0] !== 77 ||
bytes[1] !== 67 ||
bytes[2] !== 1
)
return;
const hex = Array.from(bytes.slice(4, 20), (n) => n.toString(16).padStart(2, '0')).join('');
if (hex !== race.replaceAll('-', '')) return;
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
let offset = header;
const u32 = () => {
const n = view.getUint32(offset, true);
offset += 4;
return n;
};
const f32 = () => {
const n = view.getFloat32(offset, true);
offset += 4;
return n;
};
if (bytes[3] === 1) {
if (bytes.length !== header + 14 + roster.length * (1 + stateFields.length * 4)) return;
const tick = u32(),
time = f32(),
countdown = f32(),
ended = bytes[offset++],
count = bytes[offset++];
if (ended > 1 || count !== roster.length || count < 1 || count > 6) return;
const bodies: Body[] = [];
for (const original of roster) {
const flags = bytes[offset++];
if (flags > 7) return;
const b = {
...original,
grounded: !!(flags & 1),
boosting: !!(flags & 2),
ai: !!(flags & 4),
name: flags & 4 && !original.ai ? original.name.slice(0, 12) + ' · AI' : original.name,
};
for (const field of stateFields) b[field] = field === 'ack' ? u32() : f32();
bodies.push(b);
}
return { type: 'world', race, world: { tick, time, countdown, ended: !!ended, bodies } };
}
if (bytes[3] === 2) {
const count = bytes[offset++];
if (count < 1 || count > 120 || bytes.length !== header + 1 + count * 9) return;
const commands: Command[] = [];
for (let i = 0; i < count; i++) {
const seq = u32(),
throttle = view.getInt16(offset, true) / 32767,
steer = view.getInt16(offset + 2, true) / 32767;
offset += 4;
const flags = bytes[offset++];
if (flags > 7) return;
commands.push({
seq,
input: {
throttle,
steer,
brake: !!(flags & 1),
boost: !!(flags & 2),
reset: !!(flags & 4),
},
});
}
return { type: 'input', race, commands };
}
}
export const stateBase64 = (bytes: Uint8Array) => btoa(String.fromCharCode(...bytes));
export function stateBytes(text: string): Uint8Array | undefined {
if (
text.length > 21848 ||
!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(text)
)
return;
const raw = atob(text);
if (btoa(raw) !== text) return;
return Uint8Array.from(raw, (c) => c.charCodeAt(0));
}
