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

forked from benjivers/val

workbenchthis machine
Files 967e12d2fa288842a7c42ddda720f0c8a28efc34
1
/**
2
 * The body a snapshot must carry, stated once.
3
 *
4
 * The client renderer (`scene.js`, `topdown.js`, `presence.js`, `gun.js`)
5
 * reads fixed fields off every body in `snap.players` and off `snap.me`. A
6
 * game that renames one — `down` for `alive` — is not refused by anything at
7
 * runtime: every enemy simply draws as a corpse. So the shape is a contract
8
 * here, and each game tests its real snapshot against it.
9
 *
10
 * Every body in `snap.players` has:
11
 *   id: string        who; `me` is skipped in the scene by this id
12
 *   name: string      the tag over the body
13
 *   x, y: number      where, in world units
14
 *   angle: number     which way it faces, radians
15
 *   alive: boolean    false draws a corpse
16
 *   hp: number        0..100; a bar shows under 100
17
 *   mate: boolean     on the viewer's side
18
 *
19
 * `snap.me` has, besides id, x, y, angle, alive and hp:
20
 *   ammo: number        rounds in the magazine
21
 *   reloading: boolean  mid-reload
22
 */
23
24
type Kind = "string" | "number" | "boolean";
25
26
const BODY: Record<string, Kind> = {
27
  id: "string",
28
  name: "string",
29
  x: "number",
30
  y: "number",
31
  angle: "number",
32
  alive: "boolean",
33
  hp: "number",
34
  mate: "boolean",
35
};
36
37
const ME: Record<string, Kind> = {
38
  id: "string",
39
  x: "number",
40
  y: "number",
41
  angle: "number",
42
  alive: "boolean",
43
  hp: "number",
44
  ammo: "number",
45
  reloading: "boolean",
46
};
47
48
function problems(value: unknown, shape: Record<string, Kind>, label: string): string[] {
49
  if (typeof value !== "object" || value === null) return [`${label} is not an object`];
50
  const out: string[] = [];
51
  for (const [field, kind] of Object.entries(shape)) {
52
    const got = (value as Record<string, unknown>)[field];
53
    if (typeof got !== kind || (kind === "number" && !Number.isFinite(got))) out.push(`${label}.${field} should be a ${kind}, is ${String(got)}`);
54
  }
55
  return out;
56
}
57
58
/** What is wrong with one body of `snap.players`; empty when it is fit to draw. */
59
export const bodyProblems = (body: unknown, label = "body"): string[] => problems(body, BODY, label);
60
61
/** What is wrong with `snap.me`; empty when the gun and the camera can read it. */
62
export const meProblems = (me: unknown): string[] => problems(me, ME, "me");
63
64
/** Every problem in a snapshot's bodies and `me`. */
65
export function snapshotProblems(snap: { me?: unknown; players?: unknown }): string[] {
66
  const players = Array.isArray(snap.players) ? snap.players : null;
67
  return [
68
    ...meProblems(snap.me),
69
    ...(players ? players.flatMap((p, i) => bodyProblems(p, `players[${i}]`)) : ["players is not an array"]),
70
  ];
71
}
↑↓ navigate ↵ open esc close
↑↓ navigate ↵ open esc close

Keyboard shortcuts