SOURCE / PINNED RELEASE
Made of little things.
Black Site Map Editor
- Release
- 32a57733b7e9…
- Author-recorded commit
- 3f54890f542f…
- License
- LICENSE
- Author’s source reference
- nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.shakespeare.diy%2F/black-site-map-editor
Archive hash verified: 7a4b1abbac7db5b0…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
/** Application-side examples. Adapt to your physics and validate peer messages before calling. */
export function inputPrediction<State, Input>(
initial: State,
step: (state: State, input: Input) => State,
limit = 120,
) {
let state = structuredClone(initial),
sequence = 0,
acknowledged = 0,
tick = -1;
let pending: { sequence: number; input: Input }[] = [];
return {
state: () => structuredClone(state),
advance(input: Input) {
if (pending.length >= limit)
throw new Error(
'Prediction backlog full; request a fresh state and resynchronize.',
);
const entry = { sequence: ++sequence, input: structuredClone(input) };
pending.push(entry);
state = step(structuredClone(state), structuredClone(input));
return structuredClone(entry);
},
reconcile(snapshot: { tick: number; acknowledged: number; state: State }) {
if (
!Number.isSafeInteger(snapshot.tick) ||
snapshot.tick <= tick ||
!Number.isSafeInteger(snapshot.acknowledged) ||
snapshot.acknowledged < acknowledged ||
snapshot.acknowledged > sequence
)
return false;
tick = snapshot.tick;
acknowledged = snapshot.acknowledged;
pending = pending.filter((entry) => entry.sequence > acknowledged);
state = structuredClone(snapshot.state);
for (const entry of pending)
state = step(structuredClone(state), structuredClone(entry.input));
return true;
},
};
}
/** Use host simulation ticks, not wall clocks from different machines. No extrapolation by default. */
export function snapshotBuffer<State>(
interpolate: (a: State, b: State, fraction: number) => State,
limit = 32,
) {
const frames: { tick: number; state: State }[] = [];
return {
push(tick: number, state: State) {
if (!Number.isFinite(tick) || tick < 0 || tick <= (frames.at(-1)?.tick ?? -1))
return false;
frames.push({ tick, state: structuredClone(state) });
if (frames.length > limit) frames.shift();
return true;
},
sample(renderTick: number): State | undefined {
if (!frames.length || !Number.isFinite(renderTick)) return undefined;
for (let i = 1; i < frames.length; i++) {
const a = frames[i - 1],
b = frames[i];
if (renderTick <= b.tick)
return interpolate(
structuredClone(a.state),
structuredClone(b.state),
Math.max(0, Math.min(1, (renderTick - a.tick) / (b.tick - a.tick))),
);
}
return structuredClone(frames.at(-1)!.state);
},
};
}
