@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 +23 -1
- package/LLM_README.md +2 -2
- package/README.md +1 -1
- package/dist/runtime/reducer.d.ts.map +1 -1
- package/dist/runtime/reducer.js +77 -44
- package/dist/runtime/reducer.js.map +1 -1
- package/dist/runtime/types.d.ts +44 -9
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/runtime/wire.d.ts +30 -0
- package/dist/runtime/wire.d.ts.map +1 -1
- package/dist/runtime/wire.js.map +1 -1
- package/dist/runtime/world.d.ts +9 -0
- package/dist/runtime/world.d.ts.map +1 -1
- package/dist/runtime/world.js +29 -1
- package/dist/runtime/world.js.map +1 -1
- package/dist/test/harness.d.ts +3 -1
- package/dist/test/harness.d.ts.map +1 -1
- package/dist/test/harness.js +4 -0
- package/dist/test/harness.js.map +1 -1
- package/package.json +1 -1
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.
|
|
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
|
|
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":"AAmBA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAW,MAAM,WAAW,CAAC;AAC/D,OAAO,KAAK,EAEX,SAAS,EACT,UAAU,EACV,eAAe,EAGf,QAAQ,EAIR,OAAO,EACP,SAAS,EAGT,SAAS,EAET,KAAK,EACL,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EACX,MAAM,EACN,OAAO,EACP,GAAG,EACH,UAAU,
|
|
1
|
+
{"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../../src/runtime/reducer.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime/reducer.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
/**
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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
|
-
/**
|
|
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 =
|
|
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) => {
|