@kradle/challenges-sdk 0.19.0 → 0.20.0

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/CHANGELOG.md CHANGED
@@ -17,6 +17,26 @@ also hand-edit this file or the [Unreleased] section at any time.
17
17
 
18
18
  ## [Unreleased]
19
19
 
20
+ ## [0.20.0] - 2026-10-08
21
+
22
+ ### Added
23
+
24
+ - `g.clone(from, to, dest, { mode? })`: copies a box so that its low corner lands at `dest`, with the vanilla clone. `mode` is `replace` (the default), `masked` (the air of the source is skipped, cave air and void air included) or `move` (the source is left as air). The mirror copies the states it holds from the source to the destination, and `on.blocksChanged` gets one entry with the destination `box`, the source `from` and `cause: "clone"` (a move adds the source box as air, first, as the vanilla command clears it first). When a source block changed in the same tick, or the mirror does not hold the source, the change arrives block by block with `cause: "effect"` instead. The volume cap of a fill applies (32,768 blocks), and a move counts both boxes. `replace` and `masked` refuse an overlap of the two boxes; `move` accepts it and ends with the moved blocks.
25
+ - The clone entry of a `blocksChanged` payload declares `block` as `undefined`, so a handler that reads `c.block` on every entry still compiles.
26
+ - `g.command(cmd)` may run `fill`, `setblock` and `clone` with absolute coordinates (the engine reads the blocks a command names before it runs, so the mirror sees the change, one entry per block with `cause: "command"`; a relative coordinate the engine cannot resolve comes back as `on.effectFailed`, and an `execute` clause other than `in`, `as` or `positioned` loses the position for `~`), `forceload` (a loaded chunk arrives as a fact) and `fillbiome` (the mirror holds no biome). `place`, `data` and `item ... block` stay refused.
27
+ - In a test, `game.cloneBlocks(from, to, dest, mode?)` reports a clone to the challenge.
28
+
29
+ ### Changed
30
+
31
+ - `LLM_README.md` names `g.clone` among the effects, records which commands `g.command` accepts, and says how a clone reaches `blocksChanged`; `README.md` names a clone beside a fill in the handler list.
32
+
33
+ ## [0.19.1] - 2026-10-08
34
+
35
+ ### Changed
36
+
37
+ - A hook your challenge does not handle now costs nothing: the reducer keeps its state up to date and returns without building the event payload. A large block flush is the case that mattered: 357,166 changes went from 740 ms to 164 ms, under the time one event is allowed, so a challenge that reads no `on.blocksChanged` is no longer charged for the churn of a busy world. A block read after the flush gives the same block as before.
38
+ - A hook payload now reaches your handler as the reducer built it, with no copy in between. Every player in a payload (`player`, `attacker`, `killer`, `by`) is the same read-only view as `g.player(name)`, and every other field is still built for the call or copied out of the engine event, so nothing in a payload is shared with the engine. With an `on.blocksChanged` handler the same 357,166-change flush reduces in 290 ms, from 606 ms.
39
+
20
40
  ## [0.19.0] - 2026-10-01
21
41
 
22
42
  ### Added
@@ -294,7 +314,9 @@ also hand-edit this file or the [Unreleased] section at any time.
294
314
  - Voting primitives (`votedFor`, `promptVote`, `allVotesIn`, `assignDefaultVotes`) for challenges that use discuss-then-vote-then-act phases as an alternative to pad-based voting.
295
315
  - Build-time validation of vote IDs and options against `KRADLE_CHALLENGE_VOTING_OPTIONS` (when set), matching the existing `getLocation`/`setEndState` pattern. `promptVote` also validates each announced option against the declared `votingOptions`.
296
316
 
297
- [Unreleased]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.19.0...HEAD
317
+ [Unreleased]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.20.0...HEAD
318
+ [0.20.0]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.19.1...v0.20.0
319
+ [0.19.1]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.19.0...v0.19.1
298
320
  [0.19.0]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.18.0...v0.19.0
299
321
  [0.18.0]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.17.0...v0.18.0
300
322
  [0.17.0]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.16.0...v0.17.0
package/LLM_README.md CHANGED
@@ -157,7 +157,7 @@ The steps register behavior and return the same challenge object. Use one chain
157
157
  | `pickup` | `{ player, item, count, at }` |
158
158
  | `inventory` | `{ player, item, count }`: a count changed. |
159
159
  | `blockPlaced`, `blockBroken`, `blockUsed` | `{ player, at, block: { id, props } }`; the mirror already shows the new state. |
160
- | `blocksChanged` | `{ changes }`: every block that changed this tick; a fill is one entry with its `box`. |
160
+ | `blocksChanged` | `{ changes }`: every block that changed this tick; a fill is one entry with its `box` and `block`, a clone one entry with its destination `box` and the source `from`. |
161
161
  | `container` | `{ at, block, items, changes, by?, removed }`: a container's contents changed. |
162
162
  | `zoneEntered`, `zoneLeft` | `{ player, zone }` |
163
163
  | `chat` | `{ player, text }` |
@@ -210,7 +210,7 @@ not in `say` or `tell`.
210
210
 
211
211
  **Scores and roster.** `g.setScore(player, n)`, `g.addScore(player, delta)` (refused when `outcome.scores` exists), `g.eliminate(player)`, `g.random.int(lo, hi)`, `g.random.pick(items)`, `g.random.shuffle(items)`, `g.random.weighted(items)`.
212
212
 
213
- **Effects.** `g.give(player, item, count?)` (`item` is an id or `{ id, count?, name?, lore?, enchantments?, damage?, components? }`; `components` is the raw item argument suffix such as `[enchantments={sharpness:1}]`, and a metadata field wins over the same component), `g.clear(player, item?, count?)`, `g.effect(player, { id, duration, amplifier? })`, `g.teleport(player, to)`, `g.kill(player)`, `g.gamemode(player, mode)`, `g.attribute(player, attribute, value)` (e.g. `minecraft:max_health`; no `generic.` prefix since Minecraft 1.21.2), `g.fill(from, to, block)`, `g.setBlock(at, block)` (`block` is an id or `{ id, props?, blockEntity? }`), `g.summon(entity, at, { nbt? })`, `g.killEntities(types?)`, `g.time(value)`, `g.gamerule(rule, value)` (`rule` is a Minecraft 1.21.11+ id such as `minecraft:keep_inventory`; the old camelCase names are refused), `g.setSpawn(at)`, `g.worldBorder({ center?, size?, over? })`, `g.container(at, items)`, `g.command(cmd)` (the escape hatch: gameplay commands only).
213
+ **Effects.** `g.give(player, item, count?)` (`item` is an id or `{ id, count?, name?, lore?, enchantments?, damage?, components? }`; `components` is the raw item argument suffix such as `[enchantments={sharpness:1}]`, and a metadata field wins over the same component), `g.clear(player, item?, count?)`, `g.effect(player, { id, duration, amplifier? })`, `g.teleport(player, to)`, `g.kill(player)`, `g.gamemode(player, mode)`, `g.attribute(player, attribute, value)` (e.g. `minecraft:max_health`; no `generic.` prefix since Minecraft 1.21.2), `g.fill(from, to, block)`, `g.setBlock(at, block)` (`block` is an id or `{ id, props?, blockEntity? }`), `g.clone(from, to, dest, { mode? })` (copies the box so that its low corner lands at `dest`; `mode` is `replace`, `masked` or `move`; at most 32,768 blocks; `replace` and `masked` refuse an overlap, `move` accepts it), `g.summon(entity, at, { nbt? })`, `g.killEntities(types?)`, `g.time(value)`, `g.gamerule(rule, value)` (`rule` is a Minecraft 1.21.11+ id such as `minecraft:keep_inventory`; the old camelCase names are refused), `g.setSpawn(at)`, `g.worldBorder({ center?, size?, over? })`, `g.container(at, items)`, `g.command(cmd)` (the escape hatch: gameplay commands, and `fill`, `setblock`, `clone` with absolute coordinates, `forceload`, `fillbiome`; the mirror reports what a world command changed, one entry per block).
214
214
 
215
215
  **Flow.** `g.startPhase(name)`, `g.endPhase()`, `g.endGame(endState?)`, `g.schedule(timer, after)`, `g.cancel(timer)`, `g.walk(player, to, { fallback? })`, `g.batch(name, fn)` (the effects inside `fn` go out as one named batch; `on.batchDone` reports).
216
216
 
package/README.md CHANGED
@@ -149,7 +149,7 @@ the state; the SDK answers from that mirror:
149
149
  - `p.pos`, `p.inventory`, `p.zones`; `g.inZone(p, zone)`, `g.countItems(p, item)`
150
150
 
151
151
  The handlers report the changes: `blockPlaced`, `blockBroken`, `blocksChanged`
152
- (every cause, a fill as one entry), `container`, `inventory`, `move`,
152
+ (every cause, a fill or a clone as one entry), `container`, `inventory`, `move`,
153
153
  `zoneEntered`, `zoneLeft`, `entitySpawned`, `entityChanged`, `entityRemoved`.
154
154
 
155
155
  ### Structures
@@ -1 +1 @@
1
- {"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../../src/runtime/reducer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../../src/runtime/reducer.ts"],"names":[],"mappings":"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"}
@@ -171,11 +171,13 @@ function countsOf(slots) {
171
171
  counts[st.id] = (counts[st.id] ?? 0) + st.count;
172
172
  return counts;
173
173
  }
174
+ /** The blocks a fill or a clone changes (a move changes both boxes); 0 for any other effect. */
174
175
  function fillVolume(e) {
175
- if (e.kind !== "fill")
176
+ if (e.kind !== "fill" && e.kind !== "clone")
176
177
  return 0;
177
178
  const { lo, hi } = (0, world_js_1.bounds)({ from: e.from, to: e.to });
178
- return (hi.x - lo.x + 1) * (hi.y - lo.y + 1) * (hi.z - lo.z + 1);
179
+ const volume = (hi.x - lo.x + 1) * (hi.y - lo.y + 1) * (hi.z - lo.z + 1);
180
+ return e.kind === "clone" && e.mode === "move" ? volume * 2 : volume;
179
181
  }
180
182
  /** Splits the setup effects into batches under the engine's limits, in order. */
181
183
  function splitBatches(effects) {
@@ -185,7 +187,7 @@ function splitBatches(effects) {
185
187
  for (const e of effects) {
186
188
  const v = fillVolume(e);
187
189
  if (v > exports.MAX_FILL_VOLUME) {
188
- throw (0, errors_js_1.apiError)("fill_too_large", `a fill of ${v} blocks exceeds ${exports.MAX_FILL_VOLUME}; split it`, { path: "setup" });
190
+ throw (0, errors_js_1.apiError)("fill_too_large", `a ${e.kind} of ${v} blocks exceeds ${exports.MAX_FILL_VOLUME}; split it`, { path: "setup" });
189
191
  }
190
192
  if (current.length >= exports.MAX_BATCH_EFFECTS || volume + v > exports.MAX_FILL_VOLUME) {
191
193
  out.push(current);
@@ -313,19 +315,13 @@ function snapshot(value, seen = new WeakMap()) {
313
315
  out[k] = snapshot(v, seen);
314
316
  return out;
315
317
  }
316
- /** An event payload with every player replaced by its handler view. Frozen observations pass through. */
317
- function viewPayload(r, value) {
318
- if (value === null || typeof value !== "object" || Object.isFrozen(value))
319
- return value;
320
- const asPlayer = value;
321
- if (typeof asPlayer.name === "string" && r.s.players[asPlayer.name] === value)
322
- return playerView(r, asPlayer, true);
323
- if (Array.isArray(value))
324
- return value.map((x) => viewPayload(r, x));
325
- const out = {};
326
- for (const [k, v] of Object.entries(value))
327
- out[k] = viewPayload(r, v);
328
- return out;
318
+ /**
319
+ * A player in a hook payload: the handler view, or `undefined` for none. Every other payload field is
320
+ * built fresh for the fire, copied out of the event (the harness logs and replays the event object),
321
+ * or frozen (an entity), so the payload goes to the handler as it is: no walk.
322
+ */
323
+ function viewOf(r, p) {
324
+ return p === undefined ? undefined : playerView(r, p, true);
329
325
  }
330
326
  function createReducer(def) {
331
327
  const config = def.config;
@@ -771,6 +767,10 @@ function createReducer(def) {
771
767
  write("fill");
772
768
  push({ kind: "fill", from: { ...from }, to: { ...to }, block: blockSpecString(block) });
773
769
  },
770
+ clone(from, to, dest, opts) {
771
+ write("clone");
772
+ push({ kind: "clone", from: { ...from }, to: { ...to }, dest: { ...dest }, mode: opts?.mode });
773
+ },
774
774
  setBlock(at, block) {
775
775
  write("setBlock");
776
776
  const nbt = typeof block === "string" ? undefined : block.blockEntity;
@@ -932,14 +932,27 @@ function createReducer(def) {
932
932
  return g;
933
933
  }
934
934
  // ------------------------------------------------------------ dispatch
935
- /** One event to the ordered global group, then, in the same entry, to the phase's handler. */
935
+ /** Whether a global `.on()` set or the current phase handles `key`. */
936
+ function hasHandler(s, key) {
937
+ for (const set of def.on)
938
+ if (set[key])
939
+ return true;
940
+ return Boolean(phaseSpec(s)?.on?.[key]);
941
+ }
942
+ /**
943
+ * One event to the ordered global group, then, in the same entry, to the phase's handler.
944
+ * Without a handler nothing runs and the payload is not built: a function payload is called only
945
+ * then (a block flush can carry hundreds of thousands of changes).
946
+ */
936
947
  function fire(r, key, payload) {
937
948
  const s = r.s;
938
949
  if (r.req.end || s.status !== "PLAYING")
939
950
  return;
951
+ if (!hasHandler(s, key))
952
+ return;
940
953
  const entry = s.phaseEntries;
941
954
  const g = ctxOf(r, "full");
942
- const e = viewPayload(r, payload);
955
+ const e = typeof payload === "function" ? payload() : payload;
943
956
  for (const set of def.on) {
944
957
  if (r.req.end)
945
958
  break;
@@ -1464,11 +1477,11 @@ function createReducer(def) {
1464
1477
  for (const { m, before, after } of edges) {
1465
1478
  for (const zone of after)
1466
1479
  if (!before.includes(zone))
1467
- fire(r, "zoneEntered", { player: m.p, zone });
1480
+ fire(r, "zoneEntered", { player: viewOf(r, m.p), zone });
1468
1481
  for (const zone of before)
1469
1482
  if (!after.includes(zone))
1470
- fire(r, "zoneLeft", { player: m.p, zone });
1471
- fire(r, "move", { player: m.p, from: m.from, to: { ...m.to } });
1483
+ fire(r, "zoneLeft", { player: viewOf(r, m.p), zone });
1484
+ fire(r, "move", { player: viewOf(r, m.p), from: m.from, to: { ...m.to } });
1472
1485
  }
1473
1486
  }
1474
1487
  function sampleOther(s, d) {
@@ -1523,7 +1536,7 @@ function createReducer(def) {
1523
1536
  if (changes.length === 0)
1524
1537
  return;
1525
1538
  const by = e.by === undefined ? undefined : s.players[e.by];
1526
- fire(r, "container", { at: { ...e.at }, block: e.block, items: after, changes, by, removed: false });
1539
+ fire(r, "container", { at: { ...e.at }, block: e.block, items: after, changes, by: viewOf(r, by), removed: false });
1527
1540
  }
1528
1541
  /** Applies one event and dispatches its handlers. */
1529
1542
  function step(r, e) {
@@ -1596,7 +1609,7 @@ function createReducer(def) {
1596
1609
  if (e.gamemode !== undefined)
1597
1610
  p.gamemode = e.gamemode;
1598
1611
  if (playing())
1599
- fire(r, "joined", { player: p });
1612
+ fire(r, "joined", { player: viewOf(r, p) });
1600
1613
  break;
1601
1614
  }
1602
1615
  case "gamemode": {
@@ -1609,7 +1622,7 @@ function createReducer(def) {
1609
1622
  }
1610
1623
  p.gamemode = e.gamemode;
1611
1624
  if (playing())
1612
- fire(r, "gamemode", { player: p, gamemode: e.gamemode });
1625
+ fire(r, "gamemode", { player: viewOf(r, p), gamemode: e.gamemode });
1613
1626
  break;
1614
1627
  }
1615
1628
  case "player_left": {
@@ -1620,7 +1633,7 @@ function createReducer(def) {
1620
1633
  }
1621
1634
  p.connected = false;
1622
1635
  if (playing())
1623
- fire(r, "left", { player: p });
1636
+ fire(r, "left", { player: viewOf(r, p) });
1624
1637
  break;
1625
1638
  }
1626
1639
  case "tick": {
@@ -1674,7 +1687,7 @@ function createReducer(def) {
1674
1687
  if (batchName !== undefined) {
1675
1688
  delete s.batches[e.id];
1676
1689
  if (playing())
1677
- fire(r, "batchDone", { name: batchName, ok: e.ok, failed: e.failed ?? [] });
1690
+ fire(r, "batchDone", { name: batchName, ok: e.ok, failed: snapshot(e.failed ?? []) });
1678
1691
  return;
1679
1692
  }
1680
1693
  const w = s.walks[e.id];
@@ -1688,7 +1701,7 @@ function createReducer(def) {
1688
1701
  }
1689
1702
  const p = resolve(w.player);
1690
1703
  if (p && playing())
1691
- fire(r, "walkDone", { player: p, ok: e.ok });
1704
+ fire(r, "walkDone", { player: viewOf(r, p), ok: e.ok });
1692
1705
  }
1693
1706
  break;
1694
1707
  }
@@ -1698,7 +1711,7 @@ function createReducer(def) {
1698
1711
  fire(r, "effectFailed", {
1699
1712
  kind: e.kind,
1700
1713
  detail: e.detail,
1701
- effect: e.effect,
1714
+ effect: snapshot(e.effect),
1702
1715
  batch: e.batch && s.batches[e.batch.id] !== undefined
1703
1716
  ? { name: s.batches[e.batch.id], index: e.batch.index }
1704
1717
  : undefined,
@@ -1720,7 +1733,7 @@ function createReducer(def) {
1720
1733
  const p = resolve(e.name);
1721
1734
  if (!p || !playing())
1722
1735
  return;
1723
- fire(r, "damaged", { player: p, amount: e.amount, cause: e.cause, attacker: resolve(e.attacker) });
1736
+ fire(r, "damaged", { player: viewOf(r, p), amount: e.amount, cause: e.cause, attacker: viewOf(r, resolve(e.attacker)) });
1724
1737
  break;
1725
1738
  }
1726
1739
  case "player_died": {
@@ -1738,7 +1751,7 @@ function createReducer(def) {
1738
1751
  p.eliminated = true;
1739
1752
  r.actions.push({ type: "effect", effect: { kind: "eliminate", player: p.name } });
1740
1753
  }
1741
- fire(r, "died", { player: p, cause: e.cause, killer });
1754
+ fire(r, "died", { player: viewOf(r, p), cause: e.cause, killer: viewOf(r, killer) });
1742
1755
  break;
1743
1756
  }
1744
1757
  case "player_respawned": {
@@ -1748,14 +1761,14 @@ function createReducer(def) {
1748
1761
  if (!p.eliminated)
1749
1762
  p.alive = true;
1750
1763
  if (playing())
1751
- fire(r, "respawned", { player: p });
1764
+ fire(r, "respawned", { player: viewOf(r, p) });
1752
1765
  break;
1753
1766
  }
1754
1767
  case "item_picked_up": {
1755
1768
  const p = resolve(e.name);
1756
1769
  if (!p || !playing())
1757
1770
  return;
1758
- fire(r, "pickup", { player: p, item: e.item, count: e.count, at: e.at });
1771
+ fire(r, "pickup", { player: viewOf(r, p), item: e.item, count: e.count, at: { ...e.at } });
1759
1772
  break;
1760
1773
  }
1761
1774
  case "inventory": {
@@ -1776,7 +1789,7 @@ function createReducer(def) {
1776
1789
  return;
1777
1790
  for (const item of new Set([...Object.keys(before), ...Object.keys(after)])) {
1778
1791
  if ((before[item] ?? 0) !== (after[item] ?? 0)) {
1779
- fire(r, "inventory", { player: p, item: item, count: after[item] ?? 0 });
1792
+ fire(r, "inventory", { player: viewOf(r, p), item: item, count: after[item] ?? 0 });
1780
1793
  }
1781
1794
  }
1782
1795
  break;
@@ -1839,7 +1852,7 @@ function createReducer(def) {
1839
1852
  return;
1840
1853
  s.world = (0, world_js_1.removeEntity)(s.world, e.id);
1841
1854
  if (playing())
1842
- fire(r, "entityRemoved", { entity: (0, world_js_1.freezeObservation)(prev), cause: e.cause, killer: resolve(e.killer) });
1855
+ fire(r, "entityRemoved", { entity: (0, world_js_1.freezeObservation)(prev), cause: e.cause, killer: viewOf(r, resolve(e.killer)) });
1843
1856
  break;
1844
1857
  }
1845
1858
  case "chunk":
@@ -1858,9 +1871,9 @@ function createReducer(def) {
1858
1871
  case "blocks_changed": {
1859
1872
  s.world = (0, world_js_1.setBlocks)(s.world, e.changes);
1860
1873
  if (playing()) {
1861
- fire(r, "blocksChanged", {
1862
- changes: e.changes.map((c) => compact({ x: c.x, y: c.y, z: c.z, block: c.block, was: c.was, cause: c.cause, by: resolve(c.by) })),
1863
- });
1874
+ fire(r, "blocksChanged", () => ({
1875
+ changes: e.changes.map((c) => compact({ x: c.x, y: c.y, z: c.z, block: c.block, was: c.was, cause: c.cause, by: viewOf(r, resolve(c.by)) })),
1876
+ }));
1864
1877
  }
1865
1878
  break;
1866
1879
  }
@@ -1872,11 +1885,31 @@ function createReducer(def) {
1872
1885
  });
1873
1886
  }
1874
1887
  break;
1888
+ case "blocks_cloned": {
1889
+ const from = { ...e.from };
1890
+ const to = { ...e.to };
1891
+ const { lo, hi } = (0, world_js_1.bounds)({ from, to });
1892
+ const dest = { x: Math.floor(e.dest.x), y: Math.floor(e.dest.y), z: Math.floor(e.dest.z) };
1893
+ s.world = (0, world_js_1.cloneBlocks)(s.world, { from, to }, dest, e.mode);
1894
+ if (playing()) {
1895
+ const box = { from: dest, to: { x: dest.x + hi.x - lo.x, y: dest.y + hi.y - lo.y, z: dest.z + hi.z - lo.z } };
1896
+ // A move clears the source first: an author who applies the entries in order gets the vanilla result on an overlap.
1897
+ fire(r, "blocksChanged", {
1898
+ changes: e.mode === "move"
1899
+ ? [
1900
+ { box: { from, to }, block: world_js_1.AIR, cause: "clone" },
1901
+ { box, from: { from, to }, cause: "clone" },
1902
+ ]
1903
+ : [{ box, from: { from, to }, cause: "clone" }],
1904
+ });
1905
+ }
1906
+ break;
1907
+ }
1875
1908
  case "block_broken": {
1876
1909
  s.world = (0, world_js_1.setBlocks)(s.world, [{ ...e.at, block: e.after ?? world_js_1.AIR }]);
1877
1910
  const p = resolve(e.name);
1878
1911
  if (p && playing())
1879
- fire(r, "blockBroken", { player: p, at: { ...e.at }, block: parseBlockState(e.block) });
1912
+ fire(r, "blockBroken", { player: viewOf(r, p), at: { ...e.at }, block: parseBlockState(e.block) });
1880
1913
  break;
1881
1914
  }
1882
1915
  case "block_placed": {
@@ -1884,25 +1917,25 @@ function createReducer(def) {
1884
1917
  s.world = (0, world_js_1.setBlocks)(s.world, changes);
1885
1918
  const p = resolve(e.name);
1886
1919
  if (p && playing())
1887
- fire(r, "blockPlaced", { player: p, at: { ...e.at }, block: parseBlockState(e.block) });
1920
+ fire(r, "blockPlaced", { player: viewOf(r, p), at: { ...e.at }, block: parseBlockState(e.block) });
1888
1921
  break;
1889
1922
  }
1890
1923
  case "block_used": {
1891
1924
  const p = resolve(e.name);
1892
1925
  if (p && playing())
1893
- fire(r, "blockUsed", { player: p, at: { ...e.at }, block: parseBlockState(e.block), item: e.item });
1926
+ fire(r, "blockUsed", { player: viewOf(r, p), at: { ...e.at }, block: parseBlockState(e.block), item: e.item });
1894
1927
  break;
1895
1928
  }
1896
1929
  case "chat": {
1897
1930
  const p = resolve(e.from);
1898
1931
  if (p && playing())
1899
- fire(r, "chat", { player: p, text: e.text });
1932
+ fire(r, "chat", { player: viewOf(r, p), text: e.text });
1900
1933
  break;
1901
1934
  }
1902
1935
  case "animation": {
1903
1936
  const p = resolve(e.name);
1904
1937
  if (p && playing())
1905
- fire(r, "animation", { player: p, animation: e.animation });
1938
+ fire(r, "animation", { player: viewOf(r, p), animation: e.animation });
1906
1939
  break;
1907
1940
  }
1908
1941
  case "vote_attempt":
@@ -1911,7 +1944,7 @@ function createReducer(def) {
1911
1944
  case "custom": {
1912
1945
  if (!playing())
1913
1946
  return;
1914
- fire(r, "custom", { name: e.name, player: resolve(e.player), data: e.data });
1947
+ fire(r, "custom", { name: e.name, player: viewOf(r, resolve(e.player)), data: snapshot(e.data) });
1915
1948
  break;
1916
1949
  }
1917
1950
  }
@@ -1984,7 +2017,7 @@ function createReducer(def) {
1984
2017
  }
1985
2018
  v.cast[p.name] = e.option;
1986
2019
  r.actions.push({ type: "vote_decision", attemptId: e.attemptId, accepted: true, round: v.round });
1987
- fire(r, "vote", { player: p, voteId: e.voteId, option: e.option });
2020
+ fire(r, "vote", { player: viewOf(r, p), voteId: e.voteId, option: e.option });
1988
2021
  }
1989
2022
  // ------------------------------------------------------------ reduce
1990
2023
  const reduce = (input, event) => {