pike/val Public Public: anyone can read this; nobody can change it.

forked from benjivers/val

workbenchthis machine

Merge pull request #34 from look-around-always

Record where a player is looking in every phase, alive or not
benjivers committed 916ab8f merge Browse files
2 files changed +62 −1
src/match.test.ts +43
@@ -414,3 +414,46 @@ test("the deal is the seeded shuffle, so the same match replays the same", () =>
414414 expect(newMatch(77).players.map((p) => p.name)).toEqual(newMatch(77).players.map((p) => p.name));
415415 expect(newMatch(77).players.map((p) => p.name)).not.toEqual(newMatch(78).players.map((p) => p.name));
416416 });
417
418 const round1 = (n: number) => Math.round(n * 1000) / 1000;
419
420 test("where you are looking is recorded in every phase, alive or not", () => {
421 // The bug this pins: `player.angle = input.angle` lived inside movePlayer,
422 // which the end and over phases return before reaching and which a dead
423 // player never enters. The server's copy of your angle froze, the browser
424 // kept its own, and once the angles the client had recently sent aged out
425 // of its memory the frozen one read as a correction — so the view snapped
426 // back, about 0.8 times a second, in first person only. Looking around is
427 // not moving, and no phase and no death stops you doing it.
428 const state = newMatch(5);
429 const me = state.players[0]!;
430 me.bot = false;
431
432 const looksFollow = (label: string) => {
433 for (const want of [0.4, -1.2, 2.9]) {
434 setInput(me, { angle: want });
435 tick(state, 1 / 30);
436 expect(`${label}: ${round1(me.angle)}`).toBe(`${label}: ${round1(want)}`);
437 }
438 };
439
440 looksFollow("live");
441
442 me.alive = false;
443 me.hp = 0;
444 looksFollow("dead");
445 me.alive = true;
446 me.hp = 100;
447
448 state.phase = "end";
449 state.clock = 99;
450 looksFollow("between rounds");
451
452 state.phase = "over";
453 state.clock = 99;
454 looksFollow("match over");
455
456 state.phase = "buy";
457 state.clock = 99;
458 looksFollow("buy");
459 });
src/match.ts +19 −1
@@ -314,6 +314,24 @@ export function tick(state: MatchState, dt: number): void {
314314 state.shots = state.shots.filter((s) => s.age < 0.12);
315315 for (const event of state.events) event.age += dt;
316316
317 // Where you are LOOKING is recorded every tick, in every phase, alive or
318 // dead. Moving is what the phases and dying gate; turning your head is not.
319 //
320 // This used to live inside movePlayer, which the round-end and match-over
321 // phases return before reaching and which a dead player never enters. The
322 // server's copy of your angle therefore froze between rounds and while you
323 // were down — and the browser, which owns your view and only takes the
324 // server's angle when the server disagrees, kept looking where the mouse
325 // said. Once the angles a client had recently sent aged out of its memory
326 // (yaw.js SENT_MEMORY), the frozen one read as a genuine correction and the
327 // view snapped back to it. Measured: about 0.8 snaps a second while dead or
328 // between rounds, against 0.0 in a live round. It was invisible in the
329 // top-down view, which draws the server's angle, and violent in first
330 // person, which draws the browser's — and that asymmetry is what made it
331 // look like the renderer rather than the rules.
332 for (const player of state.players)
333 if (Number.isFinite(player.input.angle)) player.angle = player.input.angle;
334
317335 if (state.phase === "over") {
318336 state.clock -= dt;
319337 if (state.clock <= 0) resetMatch(state);
@@ -385,7 +403,7 @@ export function speedOf(state: MatchState, player: Player): number {
385403 function movePlayer(state: MatchState, player: Player, dt: number, penned: boolean): void {
386404 if (!player.alive) return;
387405 const input = player.input;
388 if (Number.isFinite(input.angle)) player.angle = input.angle;
406 // The angle is applied for every player in tick(), whatever the phase.
389407 let mx = input.mx;
390408 let my = input.my;
391409 const len = Math.hypot(mx, my);
↑↓ navigate ↵ open esc close

Keyboard shortcuts