/** * Where this person is looking. * * The view angle is the browser's to move: the mouse arrives at frame rate, * and a snapshot is a description of a moment already gone. The client used * to take `snap.me.angle` on every snapshot, which dragged the camera back * to a stale angle thirty times a second — worst exactly when you turned * fastest, because that is when the stale angle is furthest away. * * So the local angle is the camera's, and the server's angle is taken only * when it is telling us something we did not say: there is no view yet, the * body under us changed, we respawned, or the angle that came back is one we * never sent. That last one is what the sent log is for — the server echoes * our own angle back, rounded, so an angle matching nothing we sent in the * last second came from the match (a round start recentring everybody at the * site) rather than from us, and is worth taking. * * Nothing here is sent anywhere: the angle still leaves through the input * timer exactly as it always did. */ /** How long a sent angle is remembered, in seconds. */ export const SENT_MEMORY = 1.2; /** How close an echo must be to count as ours. The server rounds to 0.1. */ export const SENT_TOLERANCE = 0.08; export const makeYaw = (angle = 0) => ({ angle, id: "", alive: false, ready: false, sent: [] }); /** The short way round from one angle to another, in (-π, π]. */ export const shortWay = (from, to) => ((to - from + Math.PI * 3) % (Math.PI * 2)) - Math.PI; /** A mouse delta, or an arrow key's share of a second. Ours, always taken. */ export function turn(yaw, delta) { yaw.angle += delta; return yaw.angle; } /** Point it somewhere outright: the top-down view's pointer aim. */ export function aim(yaw, angle) { yaw.angle = angle; return yaw.angle; } /** Remember an angle we told the server about, at a time in seconds. */ export function told(yaw, angle, at) { yaw.sent.push({ at, angle }); while (yaw.sent.length && yaw.sent[0].at < at - SENT_MEMORY) yaw.sent.shift(); return yaw; } /** Is this angle one we sent recently? An echo, not a correction. */ const ours = (yaw, angle, at) => yaw.sent.some((s) => s.at >= at - SENT_MEMORY && Math.abs(shortWay(s.angle, angle)) <= SENT_TOLERANCE); /** * A snapshot landed. Returns the angle to look along, having decided whether * the server's word replaces ours. * * `me` is the snapshot's own body: `{ id, angle, alive }`. `at` is now, in * seconds. An empty sent log is not evidence of anything — a client that has * not spoken yet keeps what it has. */ export function settle(yaw, me, at) { const seatChanged = !yaw.ready || me.id !== yaw.id; const respawned = me.alive && !yaw.alive; const corrected = yaw.sent.length > 0 && !ours(yaw, me.angle, at); if (seatChanged || respawned || corrected) { yaw.angle = me.angle; yaw.sent.length = 0; } yaw.id = me.id; yaw.alive = me.alive; yaw.ready = true; return yaw.angle; }