@vibemancer/core 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './types.js';
2
2
  export * from './rules.js';
3
3
  export * from './engine-version.js';
4
+ export * from './replay-compat.js';
4
5
  export * from './engine/simulation.js';
5
6
  export {DEFAULT_BUDGET, DEFAULT_FIGHT_BACKSTOP_MS, createBudgetState, createFightBudget, recordSpend, mayAct} from './engine/bot-compute-budget.js';
6
7
  export type {BudgetLimits, BotBudgetState, FightBudget} from './engine/bot-compute-budget.js';
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Whether a stored match can still be replayed by the running engine.
3
+ *
4
+ * Spectate re-simulates a recorded match from its seed, so it may only do that when the
5
+ * engine is the one that produced it. The check used to be `match.engineVersion ===
6
+ * ENGINE_VERSION`, which is right in spirit and slightly too strict in fact.
7
+ *
8
+ * ENGINE_VERSION is a CONTENT HASH, so it answers "is this the same source?" rather than
9
+ * "is this the same behaviour?". Those come apart when the hashing itself is corrected. On
10
+ * 2026-09-09 the comment stripper was fixed — it had been hashing most comments as if they
11
+ * were code — and the value moved from 1688330189011034 to 3884810833238487 without
12
+ * packages/core/src changing by a byte. Under an exact comparison that would have stranded
13
+ * 26,948 of the 42,896 stored matches, 63% of them, for no reason a player could act on.
14
+ *
15
+ * So the gate consults a short list of historical values known to describe the same engine.
16
+ * See scripts/engine-version-equivalents.json for what may go in it, and for why a wrong
17
+ * entry is the dangerous kind of wrong: it fails OPEN.
18
+ */
19
+
20
+ import {ENGINE_VERSION, EQUIVALENT_ENGINE_VERSIONS} from './engine-version.js';
21
+
22
+ /**
23
+ * @param matchEngineVersion the `engineVersion` field as read from a stored match.
24
+ * Deliberately typed to admit the junk a database can hand back: a document written
25
+ * before the field existed returns undefined, and a missing version is never a match.
26
+ */
27
+ export function canReplayMatch(matchEngineVersion: number | null | undefined): boolean
28
+ {
29
+ if (typeof matchEngineVersion !== 'number') return false;
30
+ // NaN would fail `===` against everything, so an exact gate happened to reject it. Being
31
+ // explicit keeps that true now that the comparison is a lookup.
32
+ if (!Number.isSafeInteger(matchEngineVersion)) return false;
33
+ if (matchEngineVersion === ENGINE_VERSION) return true;
34
+ return EQUIVALENT_ENGINE_VERSIONS.includes(matchEngineVersion);
35
+ }
@@ -43,8 +43,20 @@ export function directionAway(from: Position, to: Position): Position
43
43
  }
44
44
 
45
45
  /**
46
- * Clamp a position to valid arena bounds.
47
- * Note: For wizard-specific clamping with radius, use clampToArena from physics.ts
46
+ * Clamp a position into the arena rectangle — [0, ARENA_SIZE] on both axes.
47
+ *
48
+ * READ THIS BEFORE USING IT FOR SAFETY. It does NOT keep a wizard alive. The arena includes
49
+ * the 30-unit lava border, so clamping to it can hand you back a coordinate that is deep
50
+ * inside the lava — for example (0, 0), which is lethal. It used to be documented as
51
+ * clamping to "valid arena bounds", which reads like exactly the safety helper it is not:
52
+ * a player sanitising a blink target with this will blink into the fire.
53
+ *
54
+ * To stay ALIVE you want the safe box for a wizard's CENTRE, which is
55
+ * [ARENA_MIN + WIZARD_RADIUS, ARENA_MAX - WIZARD_RADIUS] — currently [35, 825], because
56
+ * `isInLava` tests your edge rather than your centre.
57
+ *
58
+ * For the engine's own wizard clamp (arena bounds minus the radius, still not lava-safe),
59
+ * see clampToArena in physics.ts.
48
60
  */
49
61
  export function clampPositionToArena(position: Position): Position
50
62
  {