@kradle/challenges-sdk 0.19.1 → 0.21.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 +29 -1
- package/LLM_README.md +4 -4
- package/README.md +1 -1
- package/dist/runtime/reducer.d.ts +12 -0
- package/dist/runtime/reducer.d.ts.map +1 -1
- package/dist/runtime/reducer.js +189 -9
- package/dist/runtime/reducer.js.map +1 -1
- package/dist/runtime/types.d.ts +62 -9
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/runtime/wire.d.ts +84 -8
- package/dist/runtime/wire.d.ts.map +1 -1
- package/dist/runtime/wire.js.map +1 -1
- package/dist/runtime/world.d.ts +52 -4
- package/dist/runtime/world.d.ts.map +1 -1
- package/dist/runtime/world.js +50 -4
- 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,32 @@ also hand-edit this file or the [Unreleased] section at any time.
|
|
|
17
17
|
|
|
18
18
|
## [Unreleased]
|
|
19
19
|
|
|
20
|
+
## [0.21.0] - 2026-10-09
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- Status facts on a player and an entity. A player (`g.players[i]`) carries `effects` (the effect id to `{ level, duration }`, the duration in ticks, `-1` with no end), `fire`, `absorption`, `pose` (`sneaking`, `swimming`, `fall_flying`, `sleeping`...), `xp`, `air`, `sprinting` and `blocking`; an entity (`g.entities()`) carries `effects`, `fire`, `absorption`, `pose` and, for a mob that has them, `baby`, `tamed`, `owner`, `sitting` and `color`. Each field is present only while away from its default, and leaves the record when it is back at it (no effect, no fire, no absorption, `pose: "standing"`, no experience level, full air, a wild or untamed mob), so a rule reads `p.fire === true` or `"pose" in p`. No declaration is needed: the fields ride the samples the engine already sends.
|
|
25
|
+
- An inventory slot (`p.slots[i]`) and an equipped item (`p.equipment.main`) carry `components`: the command syntax of every component the stack changes from its item's defaults (`[minecraft:enchantments={minecraft:fire_aspect:2},minecraft:damage=12]`), the same string `g.give` accepts. An equipped item now carries the metadata of an inventory slot too (`name`, `lore`, `enchantments`, `damage`) beside `item`, `count` and the dyed `color`.
|
|
26
|
+
- In a test, `game.sample`, `game.other`, `game.spawnEntity` and `game.changeEntity` take the status fields of a player or a mob, and the metadata of `player.setInventory` takes `components`.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- `LLM_README.md` lists the status fields of a player and of an entity, and the metadata an inventory slot and an equipped item carry.
|
|
31
|
+
- The reference of `g.command` says that a relative coordinate (`~`) resolves from the server console's position, not from a player, so absolute coordinates stay the advice, and that under `execute as <player>` the `by` of a block change names that player. The 0.20.0 entry below says the same.
|
|
32
|
+
|
|
33
|
+
## [0.20.0] - 2026-10-08
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- `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.
|
|
38
|
+
- The clone entry of a `blocksChanged` payload declares `block` as `undefined`, so a handler that reads `c.block` on every entry still compiles.
|
|
39
|
+
- `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"`; use absolute coordinates, because a `~` resolves from the server console's position, not from a player; under `execute as <player>`, the change's `by` names that player), `forceload` (a loaded chunk arrives as a fact) and `fillbiome` (the mirror holds no biome). `place`, `data` and `item ... block` stay refused.
|
|
40
|
+
- In a test, `game.cloneBlocks(from, to, dest, mode?)` reports a clone to the challenge.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- `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.
|
|
45
|
+
|
|
20
46
|
## [0.19.1] - 2026-10-08
|
|
21
47
|
|
|
22
48
|
### Changed
|
|
@@ -301,7 +327,9 @@ also hand-edit this file or the [Unreleased] section at any time.
|
|
|
301
327
|
- Voting primitives (`votedFor`, `promptVote`, `allVotesIn`, `assignDefaultVotes`) for challenges that use discuss-then-vote-then-act phases as an alternative to pad-based voting.
|
|
302
328
|
- 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`.
|
|
303
329
|
|
|
304
|
-
[Unreleased]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.
|
|
330
|
+
[Unreleased]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.21.0...HEAD
|
|
331
|
+
[0.21.0]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.20.0...v0.21.0
|
|
332
|
+
[0.20.0]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.19.1...v0.20.0
|
|
305
333
|
[0.19.1]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.19.0...v0.19.1
|
|
306
334
|
[0.19.0]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.18.0...v0.19.0
|
|
307
335
|
[0.18.0]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.17.0...v0.18.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
|
|
|
@@ -220,13 +220,13 @@ Durations are `"35s"`, `"2m"`, `"40t"` or `seconds(35)` / `minutes(2)` / `ticks(
|
|
|
220
220
|
|
|
221
221
|
## Players and Entities
|
|
222
222
|
|
|
223
|
-
A player (`g.players[i]`) carries the roster (`name`, `role`, `seatNumber`, `human`), the observations (`connected`, `alive`, `eliminated`, `isActive`, `deaths`, `kills`, `score`, `achievements`, `pos`, `yaw`, `pitch`, `headYaw`, `health`, `food`, `gamemode`, `held`, `slots`, `inventory`, `equipment`, `invisible`, `zones`) and its own `state`. The observations are read-only for a rule; `p.state` is the challenge's; the score changes through `g.setScore` / `g.addScore` or `outcome.scores`. `p.pos` is set from the initializers onward: a disconnected player keeps the last position.
|
|
223
|
+
A player (`g.players[i]`) carries the roster (`name`, `role`, `seatNumber`, `human`), the observations (`connected`, `alive`, `eliminated`, `isActive`, `deaths`, `kills`, `score`, `achievements`, `pos`, `yaw`, `pitch`, `headYaw`, `health`, `food`, `gamemode`, `held`, `slots`, `inventory`, `equipment`, `invisible`, `zones`, and the status fields that are present only while not at their default: `effects` (id to `{ level, duration }`, ticks, -1 no end), `fire`, `absorption`, `pose`, `xp`, `air`, `sprinting`, `blocking`) and its own `state`. A slot (`p.slots[i]`) and an equipped item (`p.equipment.main`) carry `id`/`item`, `count`, and the metadata that is set: `name`, `lore`, `enchantments`, `damage`, and `components`, the command syntax of every component the stack changes from its item's defaults (`[minecraft:potion_contents={potion:"minecraft:speed"}]`), the same string `g.give` accepts. The observations are read-only for a rule; `p.state` is the challenge's; the score changes through `g.setScore` / `g.addScore` or `outcome.scores`. `p.pos` is set from the initializers onward: a disconnected player keeps the last position.
|
|
224
224
|
|
|
225
225
|
`p.role === "seeker"` narrows the player to that role's state. `g.playersWithRole("seeker")` is typed the same way.
|
|
226
226
|
|
|
227
227
|
All non-player entities in the chunks Minecraft loads are available before setup. No tracking declaration is needed. Use `g.entities({ tag: "kradle.spawn" })` to find tagged entities and read their `pos`. The SDK does not load chunks for observation. Minecraft controls when entities enter and leave the mirror. Keep entities needed for setup in spawn chunks or near the players' initial positions. Distant entities appear only after Minecraft loads their chunks.
|
|
228
228
|
|
|
229
|
-
Entities (`g.entities()`) carry `id`, `type`, `pos`, `tags`, `name?`, `health?`, `item?`, `count?`, `uuid?`, `yaw?`, `pitch?`, `headYaw?`, `equipment?`, `invisible?`, `villager?`. They are read-only through every level; copy what you keep (`{ ...e.pos }`).
|
|
229
|
+
Entities (`g.entities()`) carry `id`, `type`, `pos`, `tags`, `name?`, `health?`, `item?`, `count?`, `uuid?`, `yaw?`, `pitch?`, `headYaw?`, `equipment?`, `invisible?`, `villager?`, and, only while not at their default, `effects?`, `fire?`, `absorption?`, `pose?`, `baby?`, `tamed?`, `owner?`, `sitting?`, `color?`. They are read-only through every level; copy what you keep (`{ ...e.pos }`).
|
|
230
230
|
|
|
231
231
|
---
|
|
232
232
|
|
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
|
|
@@ -79,6 +79,18 @@ interface StatePlayer {
|
|
|
79
79
|
slots: Record<number, ItemStack>;
|
|
80
80
|
inventory: Record<string, number>;
|
|
81
81
|
zones: string[];
|
|
82
|
+
/** The status fields the engine reports; absent while at their default (`fire: false`, no effects, standing). */
|
|
83
|
+
effects?: Record<string, {
|
|
84
|
+
level: number;
|
|
85
|
+
duration: number;
|
|
86
|
+
}>;
|
|
87
|
+
fire?: boolean;
|
|
88
|
+
absorption?: number;
|
|
89
|
+
pose?: string;
|
|
90
|
+
xp?: number;
|
|
91
|
+
air?: number;
|
|
92
|
+
sprinting?: boolean;
|
|
93
|
+
blocking?: boolean;
|
|
82
94
|
/** Achievement ids granted so far, in the order earned, each once. */
|
|
83
95
|
achievements: string[];
|
|
84
96
|
state: Record<string, unknown>;
|
|
@@ -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
|
@@ -38,6 +38,15 @@ const SETUP_BATCH_TIMEOUT_TICKS = 30 * 20;
|
|
|
38
38
|
/** The bound Kradle applies to a result's achievements; mirrored here so the harness reports it. */
|
|
39
39
|
exports.MAX_ACHIEVEMENTS = 64;
|
|
40
40
|
// ---------------------------------------------------------------- small helpers
|
|
41
|
+
/** The air a player has at the surface, in seconds. */
|
|
42
|
+
const FULL_AIR = 15;
|
|
43
|
+
/** A copy of an effects map, entry by entry: the mirror never shares an object with the event. */
|
|
44
|
+
function copyEffects(effects) {
|
|
45
|
+
const out = {};
|
|
46
|
+
for (const [id, e] of Object.entries(effects))
|
|
47
|
+
out[id] = { level: e.level, duration: e.duration };
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
41
50
|
/** Drops undefined-valued keys: wire objects carry only what JSON would carry. */
|
|
42
51
|
function compact(o) {
|
|
43
52
|
for (const k of Object.keys(o))
|
|
@@ -163,6 +172,7 @@ function stackOf(sl) {
|
|
|
163
172
|
lore: sl.lore ? [...sl.lore] : undefined,
|
|
164
173
|
enchantments: sl.enchantments ? { ...sl.enchantments } : undefined,
|
|
165
174
|
damage: sl.damage,
|
|
175
|
+
components: sl.components,
|
|
166
176
|
});
|
|
167
177
|
}
|
|
168
178
|
function countsOf(slots) {
|
|
@@ -171,11 +181,13 @@ function countsOf(slots) {
|
|
|
171
181
|
counts[st.id] = (counts[st.id] ?? 0) + st.count;
|
|
172
182
|
return counts;
|
|
173
183
|
}
|
|
184
|
+
/** The blocks a fill or a clone changes (a move changes both boxes); 0 for any other effect. */
|
|
174
185
|
function fillVolume(e) {
|
|
175
|
-
if (e.kind !== "fill")
|
|
186
|
+
if (e.kind !== "fill" && e.kind !== "clone")
|
|
176
187
|
return 0;
|
|
177
188
|
const { lo, hi } = (0, world_js_1.bounds)({ from: e.from, to: e.to });
|
|
178
|
-
|
|
189
|
+
const volume = (hi.x - lo.x + 1) * (hi.y - lo.y + 1) * (hi.z - lo.z + 1);
|
|
190
|
+
return e.kind === "clone" && e.mode === "move" ? volume * 2 : volume;
|
|
179
191
|
}
|
|
180
192
|
/** Splits the setup effects into batches under the engine's limits, in order. */
|
|
181
193
|
function splitBatches(effects) {
|
|
@@ -185,7 +197,7 @@ function splitBatches(effects) {
|
|
|
185
197
|
for (const e of effects) {
|
|
186
198
|
const v = fillVolume(e);
|
|
187
199
|
if (v > exports.MAX_FILL_VOLUME) {
|
|
188
|
-
throw (0, errors_js_1.apiError)("fill_too_large", `a
|
|
200
|
+
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
201
|
}
|
|
190
202
|
if (current.length >= exports.MAX_BATCH_EFFECTS || volume + v > exports.MAX_FILL_VOLUME) {
|
|
191
203
|
out.push(current);
|
|
@@ -765,6 +777,10 @@ function createReducer(def) {
|
|
|
765
777
|
write("fill");
|
|
766
778
|
push({ kind: "fill", from: { ...from }, to: { ...to }, block: blockSpecString(block) });
|
|
767
779
|
},
|
|
780
|
+
clone(from, to, dest, opts) {
|
|
781
|
+
write("clone");
|
|
782
|
+
push({ kind: "clone", from: { ...from }, to: { ...to }, dest: { ...dest }, mode: opts?.mode });
|
|
783
|
+
},
|
|
768
784
|
setBlock(at, block) {
|
|
769
785
|
write("setBlock");
|
|
770
786
|
const nbt = typeof block === "string" ? undefined : block.blockEntity;
|
|
@@ -1449,8 +1465,117 @@ function createReducer(def) {
|
|
|
1449
1465
|
p.gamemode = d.gamemode;
|
|
1450
1466
|
if (d.held !== undefined)
|
|
1451
1467
|
p.held = d.held;
|
|
1468
|
+
applyStatus(p, d);
|
|
1452
1469
|
return move;
|
|
1453
1470
|
}
|
|
1471
|
+
/** Folds the status fields of a sample into a player: a field at its default leaves the record. */
|
|
1472
|
+
function applyStatus(p, d) {
|
|
1473
|
+
if (d.effects !== undefined) {
|
|
1474
|
+
if (Object.keys(d.effects).length)
|
|
1475
|
+
p.effects = copyEffects(d.effects);
|
|
1476
|
+
else
|
|
1477
|
+
delete p.effects;
|
|
1478
|
+
}
|
|
1479
|
+
if (d.fire !== undefined) {
|
|
1480
|
+
if (d.fire)
|
|
1481
|
+
p.fire = true;
|
|
1482
|
+
else
|
|
1483
|
+
delete p.fire;
|
|
1484
|
+
}
|
|
1485
|
+
if (d.absorption !== undefined) {
|
|
1486
|
+
if (d.absorption > 0)
|
|
1487
|
+
p.absorption = d.absorption;
|
|
1488
|
+
else
|
|
1489
|
+
delete p.absorption;
|
|
1490
|
+
}
|
|
1491
|
+
if (d.pose !== undefined) {
|
|
1492
|
+
if (d.pose !== "standing")
|
|
1493
|
+
p.pose = d.pose;
|
|
1494
|
+
else
|
|
1495
|
+
delete p.pose;
|
|
1496
|
+
}
|
|
1497
|
+
if (d.xp !== undefined) {
|
|
1498
|
+
if (d.xp > 0)
|
|
1499
|
+
p.xp = d.xp;
|
|
1500
|
+
else
|
|
1501
|
+
delete p.xp;
|
|
1502
|
+
}
|
|
1503
|
+
if (d.air !== undefined) {
|
|
1504
|
+
if (d.air < FULL_AIR)
|
|
1505
|
+
p.air = d.air;
|
|
1506
|
+
else
|
|
1507
|
+
delete p.air;
|
|
1508
|
+
}
|
|
1509
|
+
if (d.sprinting !== undefined) {
|
|
1510
|
+
if (d.sprinting)
|
|
1511
|
+
p.sprinting = true;
|
|
1512
|
+
else
|
|
1513
|
+
delete p.sprinting;
|
|
1514
|
+
}
|
|
1515
|
+
if (d.blocking !== undefined) {
|
|
1516
|
+
if (d.blocking)
|
|
1517
|
+
p.blocking = true;
|
|
1518
|
+
else
|
|
1519
|
+
delete p.blocking;
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
/** Folds a mob's status fields into an entity: a field at its default leaves the record. */
|
|
1523
|
+
function applyMobStatus(entity, d) {
|
|
1524
|
+
if (d.effects !== undefined) {
|
|
1525
|
+
if (Object.keys(d.effects).length)
|
|
1526
|
+
entity.effects = copyEffects(d.effects);
|
|
1527
|
+
else
|
|
1528
|
+
delete entity.effects;
|
|
1529
|
+
}
|
|
1530
|
+
if (d.fire !== undefined) {
|
|
1531
|
+
if (d.fire)
|
|
1532
|
+
entity.fire = true;
|
|
1533
|
+
else
|
|
1534
|
+
delete entity.fire;
|
|
1535
|
+
}
|
|
1536
|
+
if (d.absorption !== undefined) {
|
|
1537
|
+
if (d.absorption > 0)
|
|
1538
|
+
entity.absorption = d.absorption;
|
|
1539
|
+
else
|
|
1540
|
+
delete entity.absorption;
|
|
1541
|
+
}
|
|
1542
|
+
if (d.pose !== undefined) {
|
|
1543
|
+
if (d.pose !== "standing")
|
|
1544
|
+
entity.pose = d.pose;
|
|
1545
|
+
else
|
|
1546
|
+
delete entity.pose;
|
|
1547
|
+
}
|
|
1548
|
+
if (d.baby !== undefined) {
|
|
1549
|
+
if (d.baby)
|
|
1550
|
+
entity.baby = true;
|
|
1551
|
+
else
|
|
1552
|
+
delete entity.baby;
|
|
1553
|
+
}
|
|
1554
|
+
if (d.tamed !== undefined) {
|
|
1555
|
+
if (d.tamed)
|
|
1556
|
+
entity.tamed = true;
|
|
1557
|
+
else
|
|
1558
|
+
delete entity.tamed;
|
|
1559
|
+
}
|
|
1560
|
+
if (d.owner !== undefined) {
|
|
1561
|
+
if (d.owner)
|
|
1562
|
+
entity.owner = d.owner;
|
|
1563
|
+
else
|
|
1564
|
+
delete entity.owner;
|
|
1565
|
+
}
|
|
1566
|
+
if (d.sitting !== undefined) {
|
|
1567
|
+
if (d.sitting)
|
|
1568
|
+
entity.sitting = true;
|
|
1569
|
+
else
|
|
1570
|
+
delete entity.sitting;
|
|
1571
|
+
}
|
|
1572
|
+
if (d.color !== undefined) {
|
|
1573
|
+
if (d.color)
|
|
1574
|
+
entity.color = d.color;
|
|
1575
|
+
else
|
|
1576
|
+
delete entity.color;
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1454
1579
|
function zonesAt(pos) {
|
|
1455
1580
|
if (!pos)
|
|
1456
1581
|
return [];
|
|
@@ -1480,7 +1605,7 @@ function createReducer(def) {
|
|
|
1480
1605
|
}
|
|
1481
1606
|
function sampleOther(s, d) {
|
|
1482
1607
|
const prev = s.world.others[d.name];
|
|
1483
|
-
|
|
1608
|
+
const next = compact({
|
|
1484
1609
|
name: d.name,
|
|
1485
1610
|
pos: d.pos ? { ...d.pos } : (prev?.pos ?? null),
|
|
1486
1611
|
health: d.health ?? prev?.health ?? 20,
|
|
@@ -1494,7 +1619,18 @@ function createReducer(def) {
|
|
|
1494
1619
|
headYaw: d.headYaw ?? prev?.headYaw,
|
|
1495
1620
|
equipment: d.equipment ? (0, world_js_1.applyEquipment)(prev?.equipment, d.equipment) : prev?.equipment,
|
|
1496
1621
|
invisible: d.invisible ?? prev?.invisible,
|
|
1497
|
-
|
|
1622
|
+
effects: prev?.effects,
|
|
1623
|
+
fire: prev?.fire,
|
|
1624
|
+
absorption: prev?.absorption,
|
|
1625
|
+
pose: prev?.pose,
|
|
1626
|
+
xp: prev?.xp,
|
|
1627
|
+
air: prev?.air,
|
|
1628
|
+
sprinting: prev?.sprinting,
|
|
1629
|
+
blocking: prev?.blocking,
|
|
1630
|
+
});
|
|
1631
|
+
// Before the store freezes the record: a status write on a frozen object throws.
|
|
1632
|
+
applyStatus(next, d);
|
|
1633
|
+
s.world = (0, world_js_1.putOther)(s.world, next);
|
|
1498
1634
|
}
|
|
1499
1635
|
function applyContainer(r, e) {
|
|
1500
1636
|
const s = r.s;
|
|
@@ -1727,7 +1863,12 @@ function createReducer(def) {
|
|
|
1727
1863
|
const p = resolve(e.name);
|
|
1728
1864
|
if (!p || !playing())
|
|
1729
1865
|
return;
|
|
1730
|
-
fire(r, "damaged", {
|
|
1866
|
+
fire(r, "damaged", {
|
|
1867
|
+
player: viewOf(r, p),
|
|
1868
|
+
amount: e.amount,
|
|
1869
|
+
cause: e.cause,
|
|
1870
|
+
attacker: viewOf(r, resolve(e.attacker)),
|
|
1871
|
+
});
|
|
1731
1872
|
break;
|
|
1732
1873
|
}
|
|
1733
1874
|
case "player_died": {
|
|
@@ -1809,6 +1950,7 @@ function createReducer(def) {
|
|
|
1809
1950
|
invisible: e.invisible,
|
|
1810
1951
|
villager: e.villager ? { ...e.villager } : undefined,
|
|
1811
1952
|
});
|
|
1953
|
+
applyMobStatus(entity, e);
|
|
1812
1954
|
s.world = (0, world_js_1.putEntity)(s.world, entity);
|
|
1813
1955
|
if (playing())
|
|
1814
1956
|
fire(r, "entitySpawned", { entity: (0, world_js_1.freezeObservation)(entity) });
|
|
@@ -1835,6 +1977,7 @@ function createReducer(def) {
|
|
|
1835
1977
|
next.invisible = e.invisible;
|
|
1836
1978
|
if (e.villager)
|
|
1837
1979
|
next.villager = { ...e.villager };
|
|
1980
|
+
applyMobStatus(next, e);
|
|
1838
1981
|
s.world = (0, world_js_1.putEntity)(s.world, next);
|
|
1839
1982
|
if (playing())
|
|
1840
1983
|
fire(r, "entityChanged", { entity: (0, world_js_1.freezeObservation)(next), was: (0, world_js_1.freezeObservation)(prev) });
|
|
@@ -1846,7 +1989,11 @@ function createReducer(def) {
|
|
|
1846
1989
|
return;
|
|
1847
1990
|
s.world = (0, world_js_1.removeEntity)(s.world, e.id);
|
|
1848
1991
|
if (playing())
|
|
1849
|
-
fire(r, "entityRemoved", {
|
|
1992
|
+
fire(r, "entityRemoved", {
|
|
1993
|
+
entity: (0, world_js_1.freezeObservation)(prev),
|
|
1994
|
+
cause: e.cause,
|
|
1995
|
+
killer: viewOf(r, resolve(e.killer)),
|
|
1996
|
+
});
|
|
1850
1997
|
break;
|
|
1851
1998
|
}
|
|
1852
1999
|
case "chunk":
|
|
@@ -1866,7 +2013,15 @@ function createReducer(def) {
|
|
|
1866
2013
|
s.world = (0, world_js_1.setBlocks)(s.world, e.changes);
|
|
1867
2014
|
if (playing()) {
|
|
1868
2015
|
fire(r, "blocksChanged", () => ({
|
|
1869
|
-
changes: e.changes.map((c) => compact({
|
|
2016
|
+
changes: e.changes.map((c) => compact({
|
|
2017
|
+
x: c.x,
|
|
2018
|
+
y: c.y,
|
|
2019
|
+
z: c.z,
|
|
2020
|
+
block: c.block,
|
|
2021
|
+
was: c.was,
|
|
2022
|
+
cause: c.cause,
|
|
2023
|
+
by: viewOf(r, resolve(c.by)),
|
|
2024
|
+
})),
|
|
1870
2025
|
}));
|
|
1871
2026
|
}
|
|
1872
2027
|
break;
|
|
@@ -1879,6 +2034,26 @@ function createReducer(def) {
|
|
|
1879
2034
|
});
|
|
1880
2035
|
}
|
|
1881
2036
|
break;
|
|
2037
|
+
case "blocks_cloned": {
|
|
2038
|
+
const from = { ...e.from };
|
|
2039
|
+
const to = { ...e.to };
|
|
2040
|
+
const { lo, hi } = (0, world_js_1.bounds)({ from, to });
|
|
2041
|
+
const dest = { x: Math.floor(e.dest.x), y: Math.floor(e.dest.y), z: Math.floor(e.dest.z) };
|
|
2042
|
+
s.world = (0, world_js_1.cloneBlocks)(s.world, { from, to }, dest, e.mode);
|
|
2043
|
+
if (playing()) {
|
|
2044
|
+
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 } };
|
|
2045
|
+
// A move clears the source first: an author who applies the entries in order gets the vanilla result on an overlap.
|
|
2046
|
+
fire(r, "blocksChanged", {
|
|
2047
|
+
changes: e.mode === "move"
|
|
2048
|
+
? [
|
|
2049
|
+
{ box: { from, to }, block: world_js_1.AIR, cause: "clone" },
|
|
2050
|
+
{ box, from: { from, to }, cause: "clone" },
|
|
2051
|
+
]
|
|
2052
|
+
: [{ box, from: { from, to }, cause: "clone" }],
|
|
2053
|
+
});
|
|
2054
|
+
}
|
|
2055
|
+
break;
|
|
2056
|
+
}
|
|
1882
2057
|
case "block_broken": {
|
|
1883
2058
|
s.world = (0, world_js_1.setBlocks)(s.world, [{ ...e.at, block: e.after ?? world_js_1.AIR }]);
|
|
1884
2059
|
const p = resolve(e.name);
|
|
@@ -1897,7 +2072,12 @@ function createReducer(def) {
|
|
|
1897
2072
|
case "block_used": {
|
|
1898
2073
|
const p = resolve(e.name);
|
|
1899
2074
|
if (p && playing())
|
|
1900
|
-
fire(r, "blockUsed", {
|
|
2075
|
+
fire(r, "blockUsed", {
|
|
2076
|
+
player: viewOf(r, p),
|
|
2077
|
+
at: { ...e.at },
|
|
2078
|
+
block: parseBlockState(e.block),
|
|
2079
|
+
item: e.item,
|
|
2080
|
+
});
|
|
1901
2081
|
break;
|
|
1902
2082
|
}
|
|
1903
2083
|
case "chat": {
|