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

forked from benjivers/val

workbenchthis machine

The camera shook at spawn because it was drawn like somebody else

Reported from play: 'when i spawn in the screen is all shakey'.

The buy phase turns prediction off on purpose — the spawn pen is a match rule
the browser does not know, and a guess that ignored it would spend the phase
being dragged back through a door. But with prediction off, your own body fell
through to the path written for OTHER people, and collected both of its
treatments: drawn at now-minus-the-interpolation-delay, so the camera sat
behind your own mouse, and run through the arrival slide, which treats a body
it has not seen before as someone walking into view and slides it in from
where it last saw them. On a respawn that is the camera sliding across the
room, every round, while the mouse says otherwise.

Your own body is now drawn from the newest word about it and nothing else
whenever it is not predicted. Other bodies keep both treatments, which is
where they belong.
benjivers committed b6659f7 Browse files
2 files changed +26 −0
src/client.test.ts +18
@@ -451,3 +451,21 @@ test("a snapshot with no seat in it draws the watching view instead of throwing"
451451 runFrame();
452452 expect(String(nodes.get("prompt")!.textContent)).toContain("cookie");
453453 });
454
455 test("your own camera is never drawn as a stranger arriving", async () => {
456 // At spawn the buy phase turns prediction off, and the frame used to fall
457 // through to the treatments meant for other people: drawn in the past, and
458 // slid in from where it was last seen. On a respawn that slides the CAMERA
459 // across the room — the shake somebody reported the first time they played
460 // a round from the start.
461 const { spectatorSnapshot } = await import("./arena.ts");
462 expect(typeof spectatorSnapshot).toBe("function");
463 const bundle = await Bun.file(new URL("../static/val.js", import.meta.url)).text();
464 // The rule is small enough to state: own id short-circuits before both.
465 const look = bundle.slice(bundle.indexOf("const look = (p) =>"));
466 const body = look.slice(0, look.indexOf("};") + 2);
467 const ownReturn = body.indexOf("if (p.id === snap.me.id) return p;");
468 const slideCall = body.indexOf("slide(presence");
469 expect(ownReturn).toBeGreaterThan(-1);
470 expect(slideCall).toBeGreaterThan(ownReturn);
471 });
static/val.js +8
@@ -347,6 +347,14 @@ const worldToScreen = (p) => ({ x: p.x * scale(), y: p.y * scale() });
347347 */
348348 const look = (p) => {
349349 if (predicting() && p.id === snap.me.id) return drawnMe;
350 // Your own body, when it is not being predicted — the buy phase, or dead —
351 // is drawn from the newest word about it and nothing else. It must not go
352 // through the two treatments below, which exist for OTHER people: drawing
353 // it in the past puts the camera behind your own mouse, and the arriving
354 // slide treats a respawn as somebody walking into view and slides the
355 // camera in from the last place it saw you. Together they are the shake at
356 // spawn.
357 if (p.id === snap.me.id) return p;
350358 // A body still arriving is drawn part of the way in from where it came,
351359 // rather than at the position it had already reached when we first saw it.
352360 return slide(presence, p.id, bodyAt(timeline, p, drawAt), nowSec, slid);
↑↓ navigate ↵ open esc close

Keyboard shortcuts