@kradle/challenges-sdk 0.14.1 → 0.16.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 +42 -1
- package/LLM_README.md +21 -20
- package/MIGRATION-0.13.md +7 -3
- package/MIGRATION-0.16.md +16 -0
- package/README.md +23 -19
- package/dist/engine.d.ts +1 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +2 -1
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime/builder.d.ts +3 -3
- package/dist/runtime/builder.d.ts.map +1 -1
- package/dist/runtime/builder.js +18 -0
- package/dist/runtime/builder.js.map +1 -1
- package/dist/runtime/reducer.d.ts +5 -0
- package/dist/runtime/reducer.d.ts.map +1 -1
- package/dist/runtime/reducer.js +56 -39
- package/dist/runtime/reducer.js.map +1 -1
- package/dist/runtime/types.d.ts +24 -9
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/runtime/wire.d.ts +21 -11
- package/dist/runtime/wire.d.ts.map +1 -1
- package/dist/runtime/wire.js +3 -1
- package/dist/runtime/wire.js.map +1 -1
- package/dist/test/harness.d.ts +19 -18
- package/dist/test/harness.d.ts.map +1 -1
- package/dist/test/harness.js +54 -47
- package/dist/test/harness.js.map +1 -1
- package/dist/test/helpers.js +1 -1
- package/dist/test/helpers.js.map +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -17,6 +17,45 @@ also hand-edit this file or the [Unreleased] section at any time.
|
|
|
17
17
|
|
|
18
18
|
## [Unreleased]
|
|
19
19
|
|
|
20
|
+
## [0.16.0] - 2026-09-24
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- `outcome.points(g, player, result)`: the integer a participant earns, evaluated once per player at the end with the frozen result (so `result.isWinner(player)` and the roster are available). Without it a winner earns 1 and anyone else 0. `outcome.pointsDescription` is one line for the menus that offer the challenge ("1 point for a win, plus 10 per extra opponent"), carried by the manifest. A non-integer, or more than 1,000,000 in either direction, fails the run (`bad_points`).
|
|
25
|
+
- `game_over` carries `points` and `achievements` per player (by name). `g.achievement(player, id)` now records the id once per player, readable as `p.achievements` (the log line still goes out on every call); an id is lowercase letters, digits, `_` and `-`, up to 64 characters; a malformed one, or a 65th achievement for one player, fails the run (`bad_achievement`). An achievement granted in `onOver` is included, so "won without a death" works from the frozen result. A failed run pays nobody and keeps the achievements earned before the failure.
|
|
26
|
+
- `MINECRAFT_TARGET` (`"26.1"`), the Minecraft version the SDK builds for, is exported from `@kradle/challenges-sdk` and `@kradle/challenges-sdk/engine`, and the manifest carries it as `minecraft`.
|
|
27
|
+
- `GameRuleId`, exported from `@kradle/challenges-sdk`: the game rule ids Minecraft 1.21.11 and later know.
|
|
28
|
+
- `MIGRATION-0.16.md`, in the package, maps the 0.15 forms to the 26.1 ones row by row.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- **Breaking:** The SDK builds for Minecraft 26.1 (Paper 26.1.2). Rebuild every challenge with this SDK and activate it again: Kradle refuses a bundle built for another Minecraft version, in both directions.
|
|
33
|
+
- **Breaking:** `g.gamerule(rule, value)` takes the Minecraft 1.21.11 ids (`GameRuleId`), e.g. `minecraft:keep_inventory` instead of `keepInventory`; the old camelCase names are refused. The world defaults (`daylightCycle`, `mobSpawning`, `naturalRegeneration`, `maxHealth`) need no change: the SDK emits the new ids.
|
|
34
|
+
- **Breaking:** An item request carries its metadata as fields (`name`, `lore`, `enchantments`, `damage`) and anything else as `components`, the raw item argument suffix (`"[unbreakable={}]"`). A metadata field wins over the same component.
|
|
35
|
+
- **Breaking:** Attribute ids lost the `generic.` prefix in Minecraft 1.21.2: use `g.attribute(p, "minecraft:max_health", 30)`.
|
|
36
|
+
- **Breaking:** Command strings and the SNBT of `g.summon` and `g.setBlock` follow the 1.20.5+ forms: item components (`give @s stick[custom_name="..."]`, `{Item:{id:"...",count:1}}`), lowercase banner `patterns`, sign text as text components, and `click_event` in a `tellraw`. `MIGRATION-0.16.md` lists them.
|
|
37
|
+
- Releases publish under the npm tag `latest` again: install the SDK with `npm install @kradle/challenges-sdk`, without `@next`. `README.md` says so.
|
|
38
|
+
- `LLM_README.md` names the item `components` field, the game rule ids and the attribute ids without the `generic.` prefix.
|
|
39
|
+
|
|
40
|
+
### Removed
|
|
41
|
+
|
|
42
|
+
- **Breaking:** `snbt` on an item request, and the `nbt` field of the `give` effect and of a container item on the wire. Use the metadata fields (`name`, `lore`, `enchantments`, `damage`) or `components` for anything else.
|
|
43
|
+
|
|
44
|
+
## [0.15.0] - 2026-09-18
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
|
|
48
|
+
- Two reads on the test kit's `Game`: `game.participants` gives every participant's record by name (the observations, `state`, `score`) and `game.votes` gives every vote's record by id (`open`, `round`, `cast`). Both are read-only. `scores(game)` is unchanged.
|
|
49
|
+
|
|
50
|
+
### Changed
|
|
51
|
+
|
|
52
|
+
- **Breaking:** `game.participant(name)` returns a read-only record. A test that wrote through the record no longer compiles; drive the state through the fake player and the events.
|
|
53
|
+
- The guides name what happens, not the machinery that does it: `README.md`, `LLM_README.md` and `MIGRATION-0.13.md`, all three in the package, no longer speak of the arena, the reducer or the sandbox, and the JSDoc on the four entries says the same. A challenge is a set of pure rules that Kradle runs, and `kradle challenge test` runs them against the built bundle, with the limits of a run: no clock, no random, no network. In `LLM_README.md` the "Rules of the Reducer" section is now "Constraints".
|
|
54
|
+
|
|
55
|
+
### Removed
|
|
56
|
+
|
|
57
|
+
- **Breaking:** The test kit no longer exposes `game.reducerState` or the `ReducerState` type. Read the players through `game.participants` and the votes through `game.votes`; `JSON.parse(game.checkpoint())` gives the whole state. See "The test kit after 0.14.1" in `MIGRATION-0.13.md`.
|
|
58
|
+
|
|
20
59
|
## [0.14.1] - 2026-09-18
|
|
21
60
|
|
|
22
61
|
### Changed
|
|
@@ -214,7 +253,9 @@ also hand-edit this file or the [Unreleased] section at any time.
|
|
|
214
253
|
- Voting primitives (`votedFor`, `promptVote`, `allVotesIn`, `assignDefaultVotes`) for challenges that use discuss-then-vote-then-act phases as an alternative to pad-based voting.
|
|
215
254
|
- 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`.
|
|
216
255
|
|
|
217
|
-
[Unreleased]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.
|
|
256
|
+
[Unreleased]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.16.0...HEAD
|
|
257
|
+
[0.16.0]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.15.0...v0.16.0
|
|
258
|
+
[0.15.0]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.14.1...v0.15.0
|
|
218
259
|
[0.14.1]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.14.0...v0.14.1
|
|
219
260
|
[0.14.0]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.13.1...v0.14.0
|
|
220
261
|
[0.13.1]: https://github.com/Kradle-ai/challenges-sdk/compare/v0.13.0...v0.13.1
|
package/LLM_README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# LLM_README.md - @kradle/challenges-sdk API Reference
|
|
2
2
|
|
|
3
|
-
This document is the API reference for AI agents that write Kradle challenges with `@kradle/challenges-sdk`. A challenge is a
|
|
3
|
+
This document is the API reference for AI agents that write Kradle challenges with `@kradle/challenges-sdk`. A challenge is a set of pure rules that Kradle runs during the game. Read this file before you write a challenge. The declarations (`.d.ts`) carry the full JSDoc of every member; this file is the tutorial and the index.
|
|
4
4
|
|
|
5
5
|
## Table of Contents
|
|
6
6
|
|
|
@@ -13,7 +13,7 @@ This document is the API reference for AI agents that write Kradle challenges wi
|
|
|
13
13
|
7. [The Context](#the-context)
|
|
14
14
|
8. [Players and Entities](#players-and-entities)
|
|
15
15
|
9. [Structures](#structures)
|
|
16
|
-
10. [
|
|
16
|
+
10. [Constraints](#constraints)
|
|
17
17
|
11. [Testing](#testing)
|
|
18
18
|
12. [Build and Run](#build-and-run)
|
|
19
19
|
13. [Complete Examples](#complete-examples)
|
|
@@ -25,11 +25,11 @@ This document is the API reference for AI agents that write Kradle challenges wi
|
|
|
25
25
|
|
|
26
26
|
## Overview
|
|
27
27
|
|
|
28
|
-
`@kradle/challenges-sdk` is a TypeScript SDK for Minecraft challenges that AI agents play on Kradle. A challenge is one root call, `defineChallenge(config, decl)`, plus steps that register the behavior: `.setup(...)`, `.start(...)`, `.phase(name, {...})`, `.on({...})`, `.outcome({...})`. The
|
|
28
|
+
`@kradle/challenges-sdk` is a TypeScript SDK for Minecraft challenges that AI agents play on Kradle. A challenge is one root call, `defineChallenge(config, decl)`, plus steps that register the behavior: `.setup(...)`, `.start(...)`, `.phase(name, {...})`, `.on({...})`, `.outcome({...})`. The game server reports the facts (players, entities, loaded chunks, block changes, containers, chat, deaths, pickups, votes) every server tick, and applies the effects the rules ask for (give, fill, teleport, titles, votes, the end of the game). The state is plain JSON, and a run replays from its event log.
|
|
29
29
|
|
|
30
|
-
There is no datapack and no mcfunction. The rules are TypeScript, the tests run without Minecraft, and `kradle challenge test` runs them
|
|
30
|
+
There is no datapack and no mcfunction. The rules are TypeScript, the tests run without Minecraft, and `kradle challenge test` runs them against the built bundle, with the limits of a run.
|
|
31
31
|
|
|
32
|
-
The package's entries: `@kradle/challenges-sdk` (the author API), `@kradle/challenges-sdk/test` (the test kit), `@kradle/challenges-sdk/engine` (the wire protocol, for
|
|
32
|
+
The package's entries: `@kradle/challenges-sdk` (the author API), `@kradle/challenges-sdk/test` (the test kit), `@kradle/challenges-sdk/engine` (the wire protocol, for tools).
|
|
33
33
|
|
|
34
34
|
---
|
|
35
35
|
|
|
@@ -40,12 +40,12 @@ Use [Kradle's CLI](https://github.com/kradle-ai/cli):
|
|
|
40
40
|
```bash
|
|
41
41
|
kradle init # a project with the SDK installed
|
|
42
42
|
kradle challenge create my-challenge # challenges/my-challenge/{challenge.ts,config.ts,challenge.test.ts}
|
|
43
|
-
kradle challenge test my-challenge # the tests,
|
|
43
|
+
kradle challenge test my-challenge # the tests, against the built bundle
|
|
44
44
|
kradle challenge build my-challenge # the bundle, uploaded and activated
|
|
45
45
|
kradle challenge run my-challenge # a run in the cloud
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
A manual installation is `npm install @kradle/challenges-sdk
|
|
48
|
+
A manual installation is `npm install @kradle/challenges-sdk` with `"type": "module"` in `package.json`. Node 22 or later.
|
|
49
49
|
|
|
50
50
|
A challenge folder holds `challenge.ts` (the rules), `config.ts` (the metadata and the prompts, `export const config`), and `challenge.test.ts`. `kradle ai-docs challenge-config` documents `config.ts`.
|
|
51
51
|
|
|
@@ -53,7 +53,7 @@ A challenge folder holds `challenge.ts` (the rules), `config.ts` (the metadata a
|
|
|
53
53
|
|
|
54
54
|
## Core Concepts
|
|
55
55
|
|
|
56
|
-
**The
|
|
56
|
+
**The rules.** Every event from the game server goes through the challenge's rules, which return the next state and a list of actions. The rules never keep state outside the returned state, never mutate their input, and never touch the clock, the network or the file system: during a run, `Date.now()`, `Math.random()`, timers, `process` and `fetch` do not exist.
|
|
57
57
|
|
|
58
58
|
**The state.** `g.state` (shared) and `p.state` (per player). Both come from the initializers in the root declaration (`state: { game: (init) => ({...}), player: (p, init) => ({...}) }`). The SDK keeps its own fields beside them: `p.score`, `p.pos`, `p.inventory`, `p.isActive`... The whole state is JSON.
|
|
59
59
|
|
|
@@ -126,7 +126,7 @@ The steps register behavior and return the same challenge object. Use one chain
|
|
|
126
126
|
| `.at([{ at, run }])` | gameplay | One-shot steps after game start, `>=` semantics. |
|
|
127
127
|
| `.every([{ interval, run }])` | gameplay | Repeating steps; after a gap they run once. |
|
|
128
128
|
| `.on({...})` | gameplay | Event handlers. Several calls add up, in call order. |
|
|
129
|
-
| `.outcome({ scores?, endWhen?, winners, endState? })` | read-only | The result rules. Required. |
|
|
129
|
+
| `.outcome({ scores?, endWhen?, winners, endState?, points?, pointsDescription? })` | read-only | The result rules. Required. |
|
|
130
130
|
| `.onOver(fn)` | notification: messages and metrics | Once the result is frozen. |
|
|
131
131
|
| `.judgeContext(fn)` | read-only | An object sent with `game_over` for the LLM judge. |
|
|
132
132
|
|
|
@@ -136,7 +136,9 @@ The steps register behavior and return the same challenge object. Use one chain
|
|
|
136
136
|
|
|
137
137
|
**The checkpoint.** After the handlers of every event: the derived scores, the phase's `until`, the phase timeout, the game duration, `outcome.endWhen`. A game end wins over a staged phase change.
|
|
138
138
|
|
|
139
|
-
**Finalization.** The phase's `exit` (full context), the final scores, `outcome.winners`, the end state (`endGame(endState)` or `outcome.endState`), the frozen result, `onOver`, `judgeContext`, `game_over`.
|
|
139
|
+
**Finalization.** The phase's `exit` (full context), the final scores, `outcome.winners`, the end state (`endGame(endState)` or `outcome.endState`), the frozen result, the points, `onOver`, `judgeContext`, `game_over`.
|
|
140
|
+
|
|
141
|
+
**Points.** `outcome.points(g, player, result)` returns the integer a participant earns; it runs once per player with the frozen result, so `result.isWinner(player)` and the other players' roles are available. Without it a winner earns 1 and anyone else 0. `outcome.pointsDescription` is one line for the menus that offer the challenge ("1 point for a win, plus 10 per extra opponent"), carried by the manifest. Kradle records the points and the achievements (`g.achievement`) per participant; a judged run may override the points.
|
|
140
142
|
|
|
141
143
|
---
|
|
142
144
|
|
|
@@ -181,11 +183,11 @@ The steps register behavior and return the same challenge object. Use one chain
|
|
|
181
183
|
|
|
182
184
|
**Votes.** `g.votes(id)` (voter to option), `g.vote(player, id)`, `g.voteOpen(id)`, `g.tally(id)` (`{ counts, leaders, abstaining }`); `g.openVote(id)` (a new round; throws `vote_open` on an open round), `g.closeVote(id)`, `g.resetVote(id)`. A round is closed until opened. An eliminated player's vote is refused unless `allowEliminated`; the `player` vote (`skills.voteForPlayer`) excludes the voter unless `allowSelf`.
|
|
183
185
|
|
|
184
|
-
**Messages and metrics.** `g.say(message)`, `g.tell(to, message)`, `g.title(to, { title, subtitle?, fadeIn?, stay?, fadeOut? })`, `g.log(message, { player?, data? })`, `g.setMetric(name, value, { player? })` (the last value per name reaches the run result), `g.achievement(player, id)
|
|
186
|
+
**Messages and metrics.** `g.say(message)`, `g.tell(to, message)`, `g.title(to, { title, subtitle?, fadeIn?, stay?, fadeOut? })`, `g.log(message, { player?, data? })`, `g.setMetric(name, value, { player? })` (the last value per name reaches the run result), `g.achievement(player, id)` (an id is lowercase letters, digits, `_` and `-`; recorded once per player, listed on `game_over` and readable as `p.achievements`).
|
|
185
187
|
|
|
186
188
|
**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)`.
|
|
187
189
|
|
|
188
|
-
**Effects.** `g.give(player, item, count?)` (`item` is an id or `{ id, count?, name?, lore?, enchantments?, damage?,
|
|
190
|
+
**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).
|
|
189
191
|
|
|
190
192
|
**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).
|
|
191
193
|
|
|
@@ -195,7 +197,7 @@ Durations are `"35s"`, `"2m"`, `"40t"` or `seconds(35)` / `minutes(2)` / `ticks(
|
|
|
195
197
|
|
|
196
198
|
## Players and Entities
|
|
197
199
|
|
|
198
|
-
A player (`g.players[i]`) carries the roster (`name`, `role`, `seatNumber`, `human`), the observations (`connected`, `alive`, `eliminated`, `isActive`, `deaths`, `kills`, `score`, `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.
|
|
200
|
+
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.
|
|
199
201
|
|
|
200
202
|
`p.role === "seeker"` narrows the player to that role's state. `g.playersWithRole("seeker")` is typed the same way.
|
|
201
203
|
|
|
@@ -249,7 +251,7 @@ export default challenge;
|
|
|
249
251
|
|
|
250
252
|
---
|
|
251
253
|
|
|
252
|
-
##
|
|
254
|
+
## Constraints
|
|
253
255
|
|
|
254
256
|
- Every callback is synchronous. A callback that returns a Promise is an error that ends the run.
|
|
255
257
|
- A phase request (`g.startPhase`, `g.endPhase`) applies once the event's handler group returned: every global `on` in order, then the phase's. A later handler of the same event still sees the current phase; a second request in the group throws `phase_twice`. `g.endGame` skips the later handlers of the event; the current callback continues.
|
|
@@ -260,21 +262,20 @@ export default challenge;
|
|
|
260
262
|
- The observations are read-only everywhere: `p.pos.y = 1`, `p.score = 3` or `g.params.x = 1` throws `read_only` (the state is written through `p.state` and `g.state`; the score through `g.setScore` / `g.addScore`).
|
|
261
263
|
- The pure callbacks (`outcome.*`, `judgeContext`, `until`, a functional `duration` or `next`, a vote's `accept`) read only: a write or an effect does not compile, and throws `read_only` at run time; so does a write in `onOver`.
|
|
262
264
|
- Derived scores (`outcome.scores`) commit once per reduction, after the handlers and after the transition the checkpoint made; every evaluation reads the previous committed scores, so `p.score + 1` adds one. `until` and `endWhen` read the vector under evaluation; `exit` and `enter` read the committed one.
|
|
263
|
-
- A vote request carries the round
|
|
265
|
+
- A vote request carries the round it saw (`vote_round`); a request without a round, or with an earlier one, is refused with feedback. The harness's `Fake.vote` stamps the current round.
|
|
264
266
|
- The engine confirms the setup batches in order; an acknowledgement that is not for the pending batch is logged and ignored.
|
|
265
267
|
|
|
266
268
|
---
|
|
267
269
|
|
|
268
270
|
## Testing
|
|
269
271
|
|
|
270
|
-
`@kradle/challenges-sdk/test` is a stand-in
|
|
272
|
+
`@kradle/challenges-sdk/test` is a stand-in for the run. No test framework dependency: it throws plain errors.
|
|
271
273
|
|
|
272
274
|
```ts
|
|
273
275
|
import { describe, expect, test } from "bun:test";
|
|
274
276
|
import { Game, sandboxed, scores } from "@kradle/challenges-sdk/test";
|
|
275
277
|
import source from "./challenge";
|
|
276
278
|
|
|
277
|
-
// Under `kradle challenge test`, the built bundle inside the arena's sandbox; the source otherwise.
|
|
278
279
|
const challenge = await sandboxed(source);
|
|
279
280
|
|
|
280
281
|
test("a kill scores", () => {
|
|
@@ -291,13 +292,13 @@ test("a kill scores", () => {
|
|
|
291
292
|
|
|
292
293
|
`Game.begin(seed, roster, opts)` sends `run_started`, the joins, one tick with every participant's position (a default row unless `at`), and confirms the setup batches. `game.player(name)` is a scripted player: `moveTo`, `says`, `vote(id, option)`, `hurt`, `dies`, `respawns`, `picksUp`, `setInventory`, `enters(zone)`, `exits(zone)`, `uses`, `breaks`, `places`, `joins`, `leaves`. The game feeds the mirror: `terrain`, `setBlocks`, `fillBlocks`, `spawnEntity`, `changeEntity`, `moveEntities`, `removeEntity`, `container`, `sample`, `other`; and answers the engine's side: `walkResult`, `batchResult`, `effectFailed`, `confirmSetup`. Time: `tick(n)`, `after(duration)`, `at(duration)`.
|
|
293
294
|
|
|
294
|
-
Reads: `game.state` (the shared data), `game.phase`, `game.status`, `game.participant(name)`, `game.
|
|
295
|
+
Reads: `game.state` (the shared data), `game.phase`, `game.status`, `game.participant(name)`, `game.participants`, `game.votes`, `game.of(type)` / `game.all(type)` (actions), `game.effects(kind)` / `game.effectsAll(kind)`, `game.said()`, `game.announcements(to?)`, `game.result()`, `game.logs()`, `game.metrics()`; `scores(g)` and `near(a, b, blocks)` from the same entry. `game.replay()` re-runs the log and compares; `game.checkpoint()` / `Game.resume(challenge, json)` cross a JSON round trip.
|
|
295
296
|
|
|
296
297
|
---
|
|
297
298
|
|
|
298
299
|
## Build and Run
|
|
299
300
|
|
|
300
|
-
`kradle challenge build <slug>` type-checks the project, bundles the challenge, loads the bundle
|
|
301
|
+
`kradle challenge build <slug>` type-checks the project, bundles the challenge, loads the bundle the way a run loads it, reads its manifest and runs one synthetic start (a definition or API error fails the build; a throw from the challenge's own code that needs the world is a warning), then uploads and activates it. `kradle challenge run <slug>` starts a run in the cloud.
|
|
301
302
|
|
|
302
303
|
The manifest the bundle exports (`runtime: 2`) carries the name, the life, the roles, the locations, the votes and the params. The backend checks the roles against the challenge's configuration at activation and stores the votes with the artifact.
|
|
303
304
|
|
|
@@ -439,4 +440,4 @@ export default challenge
|
|
|
439
440
|
- Use one chain by default, followed by `export default challenge;`. Split only to resolve a circular type; do not copy a split into every challenge.
|
|
440
441
|
- Prefer `outcome.scores` over manual score writes: the score is then a function of the state, computed after every event.
|
|
441
442
|
- Keep `p.state` small and JSON. Copy what you keep from an event (`{ ...e.pos }`).
|
|
442
|
-
- Test with `Game` before you build: a test is faster than a run, and `kradle challenge test` runs the same file
|
|
443
|
+
- Test with `Game` before you build: a test is faster than a run, and `kradle challenge test` runs the same file against the built bundle.
|
package/MIGRATION-0.13.md
CHANGED
|
@@ -61,7 +61,7 @@ The root declaration holds data and the state initializers: `state`, `life`, `du
|
|
|
61
61
|
| `g.contents(name \| at)`, `containers: {...}` in the spec | `g.containerContents(at)` for any container; no declaration |
|
|
62
62
|
| `g.rand(lo, hi)`, `g.pick`, `g.shuffle` | `g.random.int(lo, hi)`, `g.random.pick`, `g.random.shuffle`, `g.random.weighted`; `init.random` in the initializers |
|
|
63
63
|
| `g.setblock(at, block, nbt?)` | `g.setBlock(at, block)` with `block` an id or `{ id, props?, blockEntity? }` |
|
|
64
|
-
| `g.give(p, item, count, nbt)` | `g.give(p, item, count?)` with `item` an id or `{ id, count?, name?, lore?, enchantments?, damage?,
|
|
64
|
+
| `g.give(p, item, count, nbt)` | `g.give(p, item, count?)` with `item` an id or `{ id, count?, name?, lore?, enchantments?, damage?, components? }` |
|
|
65
65
|
| `g.effect(p, id, seconds, amplifier)` | `g.effect(p, { id, duration, amplifier? })` |
|
|
66
66
|
| `g.title(to, title, subtitle, times)` | `g.title(to, { title, subtitle?, fadeIn?, stay?, fadeOut? })` |
|
|
67
67
|
| `g.log(player \| null, message, data)` | `g.log(message, { player?, data? })` |
|
|
@@ -70,7 +70,7 @@ The root declaration holds data and the state initializers: `state`, `life`, `du
|
|
|
70
70
|
| `g.findBlocks(box, match, limit)` | `g.findBlocks(box, match, { limit })` |
|
|
71
71
|
| `g.summon(type, at, nbt)` | `g.summon(type, at, { nbt })` (a string third argument is ignored) |
|
|
72
72
|
| `g.worldBorder({ seconds: n })` | `g.worldBorder({ over: seconds(n) })` or `{ over: "10s" }` (`seconds` is ignored) |
|
|
73
|
-
| `g.container(at, [{ item, count, nbt, slot? }])` | `g.container(at, [{ id, count,
|
|
73
|
+
| `g.container(at, [{ item, count, nbt, slot? }])` | `g.container(at, [{ id, count, components? }])`: the items fill the slots in order; there is no slot selection |
|
|
74
74
|
| `p.slots[i].item` | `p.slots[i].id` |
|
|
75
75
|
| `onOver(g, { winnerPlayers })` | `result.isWinner(p)` over `g.players` |
|
|
76
76
|
| `g.dt` | removed: keep the last tick in the state and use `g.tick` differences |
|
|
@@ -85,7 +85,7 @@ The root declaration holds data and the state initializers: `state`, `life`, `du
|
|
|
85
85
|
| `effectFailed { kind, detail }` | `{ kind, detail, effect?, batch? }`; `g.batch(name, fn)` + `on.batchDone` |
|
|
86
86
|
| `ContextOf<typeof config, typeof state>`, `typeof challenge.ctx` | `CtxOf<typeof challenge>`, `ReadCtxOf`, `SetupCtxOf`, `PlayerIn<G>`; split the root call only to resolve a circular type |
|
|
87
87
|
| `game.state.status === "LOBBY"` (in a test) | `game.status === "CONNECTING"` (the author context has no `status` field in 0.12; a rule reads `g.status` now) |
|
|
88
|
-
| `Game.begin(seed, roster)` without positions | `begin` supplies a position per participant (`at` to choose); `g.state` is the shared data, `g.
|
|
88
|
+
| `Game.begin(seed, roster)` without positions | `begin` supplies a position per participant (`at` to choose); `g.state` is the shared data, `g.participants` every player's record; `Fake.has` is `setInventory`; `g.all("store")` is `g.all("metric")` |
|
|
89
89
|
|
|
90
90
|
## Helpers
|
|
91
91
|
|
|
@@ -126,3 +126,7 @@ A root predicate (`votes.room.accept`) cannot call a helper typed from the chall
|
|
|
126
126
|
Remove the `markers` declaration. Replace `g.markers(tag)` with `g.entities({ tag })` and `marker.at` with `entity.pos`.
|
|
127
127
|
All non-player entities in loaded chunks are available before setup. Minecraft controls chunk loading; the SDK does not preload a box. Keep entities needed for setup in spawn chunks or near the players' initial positions. Distant entities appear only after Minecraft loads their chunks.
|
|
128
128
|
In tests, use `game.spawnEntity(id, type, pos, { tags })` before `game.begin(...)` instead of `begin(..., { markers })`.
|
|
129
|
+
|
|
130
|
+
## The test kit after 0.14.1
|
|
131
|
+
|
|
132
|
+
`game.reducerState` and the `ReducerState` type are gone. Read the players through `game.participants` (every record, by name) or `game.participant(name)`, the votes through `game.votes` (by id), and the whole state, when a test needs it, through `JSON.parse(game.checkpoint())`. `scores(game)` is unchanged.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Migration to the Minecraft 26.1 SDK
|
|
2
|
+
|
|
3
|
+
The SDK builds for Minecraft 26.1 (Paper 26.1.2). A bundle from an older SDK does not run on a 26.1 arena, and a bundle from this SDK does not run on a 1.20.4 arena: the manifest carries the target (`minecraft: "26.1"`) and the arena refuses another version. Rebuild every challenge with this SDK and activate it again.
|
|
4
|
+
|
|
5
|
+
| Before | Now |
|
|
6
|
+
|---|---|
|
|
7
|
+
| `g.give(p, { id, snbt: "{Enchantments:[{id:\"minecraft:sharpness\",lvl:1s}]}" })` | `g.give(p, { id, enchantments: { "minecraft:sharpness": 1 } })` |
|
|
8
|
+
| `snbt` on an item request (SNBT compound) | `components` on an item request: the item argument suffix, e.g. `"[unbreakable={}]"`. A metadata field (`name`, `lore`, `enchantments`, `damage`) wins over the same component. |
|
|
9
|
+
| `g.gamerule("keepInventory", true)` | `g.gamerule("minecraft:keep_inventory", true)` (`GameRuleId`; Minecraft 1.21.11 renamed every rule, e.g. `doDaylightCycle` = `minecraft:advance_time`, `doMobSpawning` = `minecraft:spawn_mobs`, `doTileDrops` = `minecraft:block_drops`, `doMobLoot` = `minecraft:mob_drops`, `doWeatherCycle` = `minecraft:advance_weather`, `mobGriefing` = `minecraft:mob_griefing`, `naturalRegeneration` = `minecraft:natural_health_regeneration`) |
|
|
10
|
+
| `g.attribute(p, "minecraft:generic.max_health", 30)` | `g.attribute(p, "minecraft:max_health", 30)` (no `generic.` prefix since 1.21.2) |
|
|
11
|
+
| Item NBT in `g.command` strings: `give @s stick{display:{Name:'...'}}`, `{Item:{id:"minecraft:diamond",Count:1b}}` | Item components: `give @s stick[custom_name="..."]`, `{Item:{id:"minecraft:diamond",count:1}}` (1.20.5) |
|
|
12
|
+
| `blockEntity: '{Patterns:[{Pattern:"cs",Color:4}]}'` on a banner | `blockEntity: '{patterns:[{pattern:"minecraft:stripe_center",color:"yellow"}]}'` (1.20.5) |
|
|
13
|
+
| Sign text as JSON strings: `messages:['{"text":"go"}']` | Sign text as text components: `messages:[{text:"go"}]` or `messages:["go"]` (1.21.5: a JSON string shows literally) |
|
|
14
|
+
| `clickEvent:{action:"run_command",value:"/x"}` in a `tellraw` | `click_event:{action:"run_command",command:"/x"}` (1.21.5) |
|
|
15
|
+
|
|
16
|
+
The world defaults (`daylightCycle`, `mobSpawning`, `naturalRegeneration`, `maxHealth`) need no change: the SDK emits the new ids.
|
package/README.md
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
# @kradle/challenges-sdk
|
|
4
4
|
|
|
5
|
-
Write a Minecraft challenge for Kradle as a
|
|
6
|
-
that
|
|
7
|
-
for effects. The
|
|
8
|
-
|
|
5
|
+
Write a Minecraft challenge for Kradle as a set of pure rules: TypeScript
|
|
6
|
+
functions that read the world and the players, update a JSON state, and ask
|
|
7
|
+
the game server for effects. The server reports the facts and applies the
|
|
8
|
+
effects; the rules run with no clock, no `Math.random()` and no network.
|
|
9
9
|
|
|
10
10
|
## Getting started
|
|
11
11
|
|
|
@@ -15,13 +15,12 @@ the CLI's README first.
|
|
|
15
15
|
```bash
|
|
16
16
|
kradle init
|
|
17
17
|
kradle challenge create my-challenge # challenges/my-challenge/{challenge.ts,config.ts,challenge.test.ts}
|
|
18
|
-
kradle challenge test my-challenge # the tests,
|
|
18
|
+
kradle challenge test my-challenge # the tests, against the built bundle
|
|
19
19
|
kradle challenge build my-challenge # the bundle, uploaded and activated
|
|
20
20
|
kradle challenge run my-challenge # a run in the cloud
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
A manual installation is one dependency: `npm install @kradle/challenges-sdk
|
|
24
|
-
(the reducer SDK stays under the npm tag `next` until the CLI release installs it).
|
|
23
|
+
A manual installation is one dependency: `npm install @kradle/challenges-sdk`.
|
|
25
24
|
Add `"type": "module"` to the project's `package.json`.
|
|
26
25
|
|
|
27
26
|
## Local development
|
|
@@ -92,6 +91,13 @@ const challenge = defineChallenge(config, {
|
|
|
92
91
|
export default challenge;
|
|
93
92
|
```
|
|
94
93
|
|
|
94
|
+
The outcome can also price the game: `points: (g, p, result) => number` is
|
|
95
|
+
what a participant earns (an integer, evaluated at the end with the frozen
|
|
96
|
+
result; without it a winner earns 1 and anyone else 0), and
|
|
97
|
+
`pointsDescription` is the one line the menus show for it. `g.achievement(player, id)`
|
|
98
|
+
records an achievement once per player (`p.achievements`); Kradle records
|
|
99
|
+
both with the run.
|
|
100
|
+
|
|
95
101
|
Helpers can use `CtxOf<typeof challenge>` for their context type.
|
|
96
102
|
Split the root call from the steps only when TypeScript requires it to resolve
|
|
97
103
|
a circular type. This can occur when a step receives a helper typed from the
|
|
@@ -144,15 +150,15 @@ export const pillarDone = (g: StructureReads, at: { x: number; y: number; z: num
|
|
|
144
150
|
|
|
145
151
|
## Testing
|
|
146
152
|
|
|
147
|
-
`@kradle/challenges-sdk/test` is a stand-in
|
|
148
|
-
the actions, and checks the state; `replay()` proves determinism.
|
|
153
|
+
`@kradle/challenges-sdk/test` is a stand-in for the run. A test feeds events,
|
|
154
|
+
reads the actions, and checks the state; `replay()` proves determinism.
|
|
149
155
|
|
|
150
156
|
```ts
|
|
151
157
|
import { describe, expect, test } from "bun:test";
|
|
152
158
|
import { Game, sandboxed, scores } from "@kradle/challenges-sdk/test";
|
|
153
159
|
import source from "./challenge";
|
|
154
160
|
|
|
155
|
-
const challenge = await sandboxed(source);
|
|
161
|
+
const challenge = await sandboxed(source);
|
|
156
162
|
|
|
157
163
|
test("a tag scores", () => {
|
|
158
164
|
const g = new Game(challenge).begin(1, { Alice: "seeker", Bob: "hider" });
|
|
@@ -169,16 +175,14 @@ joins and confirms the setup batches. `g.player(name)` scripts a player
|
|
|
169
175
|
(`moveTo`, `says`, `vote`, `hurt`, `dies`, `setInventory`, `places`, ...); the
|
|
170
176
|
game feeds the mirror (`terrain`, `setBlocks`, `spawnEntity`, `container`) and
|
|
171
177
|
answers the engine's side (`walkResult`, `batchResult`). `kradle challenge test
|
|
172
|
-
<slug>` runs the same file against the built bundle
|
|
178
|
+
<slug>` runs the same file against the built bundle, with the limits of a run.
|
|
173
179
|
|
|
174
180
|
## Build and run
|
|
175
181
|
|
|
176
182
|
`kradle challenge build <slug>` bundles `challenge.ts` into one file, loads it
|
|
177
|
-
once
|
|
178
|
-
configuration. The
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
types catch. The engine contract (`Event`, `Effect`, `Action`) is in
|
|
184
|
-
`src/runtime/sdk.ts`; the server side is `backend/engine-plugin`.
|
|
183
|
+
once the way a run loads it, uploads it and activates it with the challenge's
|
|
184
|
+
configuration. The run's event log is uploaded as the `runtime` recording.
|
|
185
|
+
|
|
186
|
+
The wire contract (`Event`, `Effect`, `Action`) is the
|
|
187
|
+
`@kradle/challenges-sdk/engine` entry; `test/types/` shows what the types
|
|
188
|
+
catch.
|
package/dist/engine.d.ts
CHANGED
|
@@ -3,6 +3,6 @@ export { ChallengeError, type ErrorCategory, categoryOf } from "./runtime/errors
|
|
|
3
3
|
export { MAX_BATCH_EFFECTS, MAX_FILL_VOLUME, type State, assertJson, parseBlockState, splitBatches } from "./runtime/reducer.js";
|
|
4
4
|
export { type Duration, type Ticks, toTicks } from "./runtime/time.js";
|
|
5
5
|
export type { Action, BlockId, Box, Capability, ContainerItem, Effect, EffectId, EndReason, EngineChallenge, EntityTypeId, Event, GameMode, ItemId, ItemMeta, ItemStack, Json, ManifestVote, Participant, PlayerSample, RuntimeManifest, SdkEndState, SlotChange, Status, TitleTimes, Vec3, } from "./runtime/wire.js";
|
|
6
|
-
export { ALL_CAPABILITIES } from "./runtime/wire.js";
|
|
6
|
+
export { ALL_CAPABILITIES, MINECRAFT_TARGET } from "./runtime/wire.js";
|
|
7
7
|
export type { ChunkMessage, Entity, EntityMove, Equipment, EquipmentChange, EquipmentSlot, Equipped, WorldState } from "./runtime/world.js";
|
|
8
8
|
//# sourceMappingURL=engine.d.ts.map
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAEA,YAAY,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,KAAK,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACjI,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACvE,YAAY,EACX,MAAM,EACN,OAAO,EACP,GAAG,EACH,UAAU,EACV,aAAa,EACb,MAAM,EACN,QAAQ,EACR,SAAS,EACT,eAAe,EACf,YAAY,EACZ,KAAK,EACL,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,SAAS,EACT,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,eAAe,EACf,WAAW,EACX,UAAU,EACV,MAAM,EACN,UAAU,EACV,IAAI,GACJ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAEA,YAAY,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,KAAK,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACjI,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACvE,YAAY,EACX,MAAM,EACN,OAAO,EACP,GAAG,EACH,UAAU,EACV,aAAa,EACb,MAAM,EACN,QAAQ,EACR,SAAS,EACT,eAAe,EACf,YAAY,EACZ,KAAK,EACL,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,SAAS,EACT,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,eAAe,EACf,WAAW,EACX,UAAU,EACV,MAAM,EACN,UAAU,EACV,IAAI,GACJ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACvE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/engine.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ALL_CAPABILITIES = exports.toTicks = exports.splitBatches = exports.parseBlockState = exports.assertJson = exports.MAX_FILL_VOLUME = exports.MAX_BATCH_EFFECTS = exports.categoryOf = exports.ChallengeError = void 0;
|
|
3
|
+
exports.MINECRAFT_TARGET = exports.ALL_CAPABILITIES = exports.toTicks = exports.splitBatches = exports.parseBlockState = exports.assertJson = exports.MAX_FILL_VOLUME = exports.MAX_BATCH_EFFECTS = exports.categoryOf = exports.ChallengeError = void 0;
|
|
4
4
|
var errors_js_1 = require("./runtime/errors.js");
|
|
5
5
|
Object.defineProperty(exports, "ChallengeError", { enumerable: true, get: function () { return errors_js_1.ChallengeError; } });
|
|
6
6
|
Object.defineProperty(exports, "categoryOf", { enumerable: true, get: function () { return errors_js_1.categoryOf; } });
|
|
@@ -14,4 +14,5 @@ var time_js_1 = require("./runtime/time.js");
|
|
|
14
14
|
Object.defineProperty(exports, "toTicks", { enumerable: true, get: function () { return time_js_1.toTicks; } });
|
|
15
15
|
var wire_js_1 = require("./runtime/wire.js");
|
|
16
16
|
Object.defineProperty(exports, "ALL_CAPABILITIES", { enumerable: true, get: function () { return wire_js_1.ALL_CAPABILITIES; } });
|
|
17
|
+
Object.defineProperty(exports, "MINECRAFT_TARGET", { enumerable: true, get: function () { return wire_js_1.MINECRAFT_TARGET; } });
|
|
17
18
|
//# sourceMappingURL=engine.js.map
|
package/dist/engine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":";;;AAGA,iDAAqF;AAA5E,2GAAA,cAAc,OAAA;AAAsB,uGAAA,UAAU,OAAA;AACvD,mDAAiI;AAAxH,+GAAA,iBAAiB,OAAA;AAAE,6GAAA,eAAe,OAAA;AAAc,wGAAA,UAAU,OAAA;AAAE,6GAAA,eAAe,OAAA;AAAE,0GAAA,YAAY,OAAA;AAClG,6CAAuE;AAAnC,kGAAA,OAAO,OAAA;AA4B3C,
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":";;;AAGA,iDAAqF;AAA5E,2GAAA,cAAc,OAAA;AAAsB,uGAAA,UAAU,OAAA;AACvD,mDAAiI;AAAxH,+GAAA,iBAAiB,OAAA;AAAE,6GAAA,eAAe,OAAA;AAAc,wGAAA,UAAU,OAAA;AAAE,6GAAA,eAAe,OAAA;AAAE,0GAAA,YAAY,OAAA;AAClG,6CAAuE;AAAnC,kGAAA,OAAO,OAAA;AA4B3C,6CAAuE;AAA9D,2GAAA,gBAAgB,OAAA;AAAE,2GAAA,gBAAgB,OAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,8 +3,9 @@ export { ChallengeError, type ErrorCategory } from "./runtime/errors.js";
|
|
|
3
3
|
export { MAX_BATCH_EFFECTS, MAX_FILL_VOLUME, MAX_TRANSITIONS_PER_EVENT } from "./runtime/reducer.js";
|
|
4
4
|
export { winBy } from "./runtime/rules.js";
|
|
5
5
|
export { addDuration, asMinutes, asSeconds, type Duration, minutes, seconds, subtractDuration, TICKS_PER_SECOND, type Ticks, ticks, toTicks, } from "./runtime/time.js";
|
|
6
|
-
export type { BlockMatch, BlockPos, BlockSpec, BlockState, ChallengeApi, ChallengeConfig, Ctx, CtxOf, Decl, DeepReadonly, EndStateOf, Handlers, Init, ItemMatch, ItemRequest, LocationOf, NotifyCtx, NotifyCtxOf, ObservedPlayer, OptionFor, Outcome, ParamSpec, ParamsOf, PhaseSpec, PhaseStep, Player, PlayerIn, PlayerOf, Random, ReadCtx, ReadCtxOf, ReadPlayer, ReadPlayerOf, Result, RoleOf, Roster, SetupCtx, SetupCtxOf, StateDecl, TitleOptions, To, VoteIdOf, VoteRule, World, } from "./runtime/types.js";
|
|
6
|
+
export type { BlockMatch, BlockPos, BlockSpec, BlockState, ChallengeApi, ChallengeConfig, Ctx, CtxOf, Decl, DeepReadonly, EndStateOf, GameRuleId, Handlers, Init, ItemMatch, ItemRequest, LocationOf, NotifyCtx, NotifyCtxOf, ObservedPlayer, OptionFor, Outcome, ParamSpec, ParamsOf, PhaseSpec, PhaseStep, Player, PlayerIn, PlayerOf, Random, ReadCtx, ReadCtxOf, ReadPlayer, ReadPlayerOf, Result, RoleOf, Roster, SetupCtx, SetupCtxOf, StateDecl, TitleOptions, To, VoteIdOf, VoteRule, World, } from "./runtime/types.js";
|
|
7
7
|
export { PLAYER_VOTE, WORLD_DEFAULTS } from "./runtime/types.js";
|
|
8
|
+
export { MINECRAFT_TARGET } from "./runtime/wire.js";
|
|
8
9
|
export type { BlockId, Box, EffectId, EndReason, EntityTypeId, GameMode, ItemId, ItemMeta, ItemStack, Json, SdkEndState, Status, Vec3, } from "./runtime/wire.js";
|
|
9
10
|
export { blockIdOf, bounds, type Entity, type EntityFilter, type Equipment, inBox } from "./runtime/world.js";
|
|
10
11
|
import type { BlockPos } from "./runtime/types.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACrG,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EACN,WAAW,EACX,SAAS,EACT,SAAS,EACT,KAAK,QAAQ,EACb,OAAO,EACP,OAAO,EACP,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,KAAK,EACV,KAAK,EACL,OAAO,GACP,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACX,UAAU,EACV,QAAQ,EACR,SAAS,EACT,UAAU,EACV,YAAY,EACZ,eAAe,EACf,GAAG,EACH,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,IAAI,EACJ,SAAS,EACT,WAAW,EACX,UAAU,EACV,SAAS,EACT,WAAW,EACX,cAAc,EACd,SAAS,EACT,OAAO,EACP,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,OAAO,EACP,SAAS,EACT,UAAU,EACV,YAAY,EACZ,MAAM,EACN,MAAM,EACN,MAAM,EACN,QAAQ,EACR,UAAU,EACV,SAAS,EACT,YAAY,EACZ,EAAE,EACF,QAAQ,EACR,QAAQ,EACR,KAAK,GACL,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACjE,YAAY,EACX,OAAO,EACP,GAAG,EACH,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,SAAS,EACT,IAAI,EACJ,WAAW,EACX,MAAM,EACN,IAAI,GACJ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE9G,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC9C,8EAA8E;AAC9E,eAAO,MAAM,UAAU,GAAI,GAAG,IAAI,KAAG,QAA4E,CAAC;AAClH,iGAAiG;AACjG,eAAO,MAAM,KAAK,GAAI,IAAI,IAAI,KAAG,QAAmF,CAAC;AACrH,wCAAwC;AACxC,eAAO,MAAM,KAAK,GAAI,IAAI,IAAI,KAAG,QAAmF,CAAC;AACrH,sCAAsC;AACtC,eAAO,MAAM,MAAM,GAAI,IAAI,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,KAAG,IAA8E,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACrG,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EACN,WAAW,EACX,SAAS,EACT,SAAS,EACT,KAAK,QAAQ,EACb,OAAO,EACP,OAAO,EACP,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,KAAK,EACV,KAAK,EACL,OAAO,GACP,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACX,UAAU,EACV,QAAQ,EACR,SAAS,EACT,UAAU,EACV,YAAY,EACZ,eAAe,EACf,GAAG,EACH,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,UAAU,EACV,QAAQ,EACR,IAAI,EACJ,SAAS,EACT,WAAW,EACX,UAAU,EACV,SAAS,EACT,WAAW,EACX,cAAc,EACd,SAAS,EACT,OAAO,EACP,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,OAAO,EACP,SAAS,EACT,UAAU,EACV,YAAY,EACZ,MAAM,EACN,MAAM,EACN,MAAM,EACN,QAAQ,EACR,UAAU,EACV,SAAS,EACT,YAAY,EACZ,EAAE,EACF,QAAQ,EACR,QAAQ,EACR,KAAK,GACL,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,YAAY,EACX,OAAO,EACP,GAAG,EACH,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,SAAS,EACT,IAAI,EACJ,WAAW,EACX,MAAM,EACN,IAAI,GACJ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE9G,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC9C,8EAA8E;AAC9E,eAAO,MAAM,UAAU,GAAI,GAAG,IAAI,KAAG,QAA4E,CAAC;AAClH,iGAAiG;AACjG,eAAO,MAAM,KAAK,GAAI,IAAI,IAAI,KAAG,QAAmF,CAAC;AACrH,wCAAwC;AACxC,eAAO,MAAM,KAAK,GAAI,IAAI,IAAI,KAAG,QAAmF,CAAC;AACrH,sCAAsC;AACtC,eAAO,MAAM,MAAM,GAAI,IAAI,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,KAAG,IAA8E,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.offset = exports.above = exports.below = exports.toBlockPos = exports.inBox = exports.bounds = exports.blockIdOf = exports.WORLD_DEFAULTS = exports.PLAYER_VOTE = exports.toTicks = exports.ticks = exports.TICKS_PER_SECOND = exports.subtractDuration = exports.seconds = exports.minutes = exports.asSeconds = exports.asMinutes = exports.addDuration = exports.winBy = exports.MAX_TRANSITIONS_PER_EVENT = exports.MAX_FILL_VOLUME = exports.MAX_BATCH_EFFECTS = exports.ChallengeError = exports.defineChallenge = void 0;
|
|
3
|
+
exports.offset = exports.above = exports.below = exports.toBlockPos = exports.inBox = exports.bounds = exports.blockIdOf = exports.MINECRAFT_TARGET = exports.WORLD_DEFAULTS = exports.PLAYER_VOTE = exports.toTicks = exports.ticks = exports.TICKS_PER_SECOND = exports.subtractDuration = exports.seconds = exports.minutes = exports.asSeconds = exports.asMinutes = exports.addDuration = exports.winBy = exports.MAX_TRANSITIONS_PER_EVENT = exports.MAX_FILL_VOLUME = exports.MAX_BATCH_EFFECTS = exports.ChallengeError = exports.defineChallenge = void 0;
|
|
4
4
|
// The author API: `@kradle/challenges-sdk`. The engine protocol is `@kradle/challenges-sdk/engine`,
|
|
5
5
|
// the test kit `@kradle/challenges-sdk/test`.
|
|
6
6
|
var builder_js_1 = require("./runtime/builder.js");
|
|
@@ -26,6 +26,8 @@ Object.defineProperty(exports, "toTicks", { enumerable: true, get: function () {
|
|
|
26
26
|
var types_js_1 = require("./runtime/types.js");
|
|
27
27
|
Object.defineProperty(exports, "PLAYER_VOTE", { enumerable: true, get: function () { return types_js_1.PLAYER_VOTE; } });
|
|
28
28
|
Object.defineProperty(exports, "WORLD_DEFAULTS", { enumerable: true, get: function () { return types_js_1.WORLD_DEFAULTS; } });
|
|
29
|
+
var wire_js_1 = require("./runtime/wire.js");
|
|
30
|
+
Object.defineProperty(exports, "MINECRAFT_TARGET", { enumerable: true, get: function () { return wire_js_1.MINECRAFT_TARGET; } });
|
|
29
31
|
var world_js_1 = require("./runtime/world.js");
|
|
30
32
|
Object.defineProperty(exports, "blockIdOf", { enumerable: true, get: function () { return world_js_1.blockIdOf; } });
|
|
31
33
|
Object.defineProperty(exports, "bounds", { enumerable: true, get: function () { return world_js_1.bounds; } });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,oGAAoG;AACpG,8CAA8C;AAC9C,mDAA0F;AAA9C,6GAAA,eAAe,OAAA;AAC3D,iDAAyE;AAAhE,2GAAA,cAAc,OAAA;AACvB,mDAAqG;AAA5F,+GAAA,iBAAiB,OAAA;AAAE,6GAAA,eAAe,OAAA;AAAE,uHAAA,yBAAyB,OAAA;AACtE,+CAA2C;AAAlC,iGAAA,KAAK,OAAA;AACd,6CAY2B;AAX1B,sGAAA,WAAW,OAAA;AACX,oGAAA,SAAS,OAAA;AACT,oGAAA,SAAS,OAAA;AAET,kGAAA,OAAO,OAAA;AACP,kGAAA,OAAO,OAAA;AACP,2GAAA,gBAAgB,OAAA;AAChB,2GAAA,gBAAgB,OAAA;AAEhB,gGAAA,KAAK,OAAA;AACL,kGAAA,OAAO,OAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,oGAAoG;AACpG,8CAA8C;AAC9C,mDAA0F;AAA9C,6GAAA,eAAe,OAAA;AAC3D,iDAAyE;AAAhE,2GAAA,cAAc,OAAA;AACvB,mDAAqG;AAA5F,+GAAA,iBAAiB,OAAA;AAAE,6GAAA,eAAe,OAAA;AAAE,uHAAA,yBAAyB,OAAA;AACtE,+CAA2C;AAAlC,iGAAA,KAAK,OAAA;AACd,6CAY2B;AAX1B,sGAAA,WAAW,OAAA;AACX,oGAAA,SAAS,OAAA;AACT,oGAAA,SAAS,OAAA;AAET,kGAAA,OAAO,OAAA;AACP,kGAAA,OAAO,OAAA;AACP,2GAAA,gBAAgB,OAAA;AAChB,2GAAA,gBAAgB,OAAA;AAEhB,gGAAA,KAAK,OAAA;AACL,kGAAA,OAAO,OAAA;AAiDR,+CAAiE;AAAxD,uGAAA,WAAW,OAAA;AAAE,0GAAA,cAAc,OAAA;AACpC,6CAAqD;AAA5C,2GAAA,gBAAgB,OAAA;AAgBzB,+CAA8G;AAArG,qGAAA,SAAS,OAAA;AAAE,kGAAA,MAAM,OAAA;AAAkD,iGAAA,KAAK,OAAA;AAIjF,8EAA8E;AACvE,MAAM,UAAU,GAAG,CAAC,CAAO,EAAY,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAArG,QAAA,UAAU,cAA2F;AAClH,iGAAiG;AAC1F,MAAM,KAAK,GAAG,CAAC,EAAQ,EAAY,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAAxG,QAAA,KAAK,SAAmG;AACrH,wCAAwC;AACjC,MAAM,KAAK,GAAG,CAAC,EAAQ,EAAY,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAAxG,QAAA,KAAK,SAAmG;AACrH,sCAAsC;AAC/B,MAAM,MAAM,GAAG,CAAC,EAAQ,EAAE,CAAgB,EAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;AAAtH,QAAA,MAAM,UAAgH"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { type State } from "./reducer.js";
|
|
2
2
|
import type { ChallengeApi, ChallengeConfig, Decl, Handlers, Outcome, PhaseSpec, PlayerMap, ResolvePlayers, VotesDecl } from "./types.js";
|
|
3
3
|
import type { Action, Event, RuntimeManifest } from "./wire.js";
|
|
4
|
-
/** A challenge as
|
|
4
|
+
/** A challenge as a run and the harness use it: the API plus `init`, `reduce` and the manifest. */
|
|
5
5
|
export interface Challenge<C extends ChallengeConfig, G, PM extends PlayerMap<C>, Ph extends string, Z extends string, T extends string, V extends VotesDecl> extends ChallengeApi<C, G, PM, Ph, Z, T, V> {
|
|
6
|
-
/** The manifest
|
|
6
|
+
/** The manifest Kradle reads before a run. Reading it locks the definition. */
|
|
7
7
|
readonly manifest: RuntimeManifest;
|
|
8
8
|
/** True once the definition is locked (`init()` or `manifest` was used). */
|
|
9
9
|
readonly locked: boolean;
|
|
@@ -13,7 +13,7 @@ export interface Challenge<C extends ChallengeConfig, G, PM extends PlayerMap<C>
|
|
|
13
13
|
reduce(state: State, event: Event): {
|
|
14
14
|
/** The next state, frozen; the input is never changed. */
|
|
15
15
|
state: State;
|
|
16
|
-
/** What the
|
|
16
|
+
/** What the game server applies, in order. */
|
|
17
17
|
actions: Action[];
|
|
18
18
|
};
|
|
19
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/runtime/builder.ts"],"names":[],"mappings":"AAYA,OAAO,EAAwF,KAAK,KAAK,EAAE,MAAM,cAAc,CAAC;AAEhI,OAAO,KAAK,EACX,YAAY,EACZ,eAAe,EACf,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,SAAS,EACT,SAAS,EACT,cAAc,EACd,SAAS,EAET,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,MAAM,EAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/runtime/builder.ts"],"names":[],"mappings":"AAYA,OAAO,EAAwF,KAAK,KAAK,EAAE,MAAM,cAAc,CAAC;AAEhI,OAAO,KAAK,EACX,YAAY,EACZ,eAAe,EACf,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,SAAS,EACT,SAAS,EACT,cAAc,EACd,SAAS,EAET,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,MAAM,EAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAKrE,mGAAmG;AACnG,MAAM,WAAW,SAAS,CACzB,CAAC,SAAS,eAAe,EACzB,CAAC,EACD,EAAE,SAAS,SAAS,CAAC,CAAC,CAAC,EACvB,EAAE,SAAS,MAAM,EACjB,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,SAAS,CAClB,SAAQ,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAC5C,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,iEAAiE;IACjE,IAAI,IAAI,KAAK,CAAC;IACd,2FAA2F;IAC3F,MAAM,CACL,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,GACV;QACF,0DAA0D;QAC1D,KAAK,EAAE,KAAK,CAAC;QACb,8CAA8C;QAC9C,OAAO,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC,CAAC;AA4H9I;;;GAGG;AACH,wBAAgB,eAAe,CAC9B,KAAK,CAAC,CAAC,SAAS,eAAe,EAC/B,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EACxC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EACxC,EAAE,SAAS,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EACrE,KAAK,CAAC,EAAE,SAAS,MAAM,GAAG,KAAK,EAC/B,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,KAAK,EAC9B,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,KAAK,EAC9B,KAAK,CAAC,CAAC,SAAS,SAAS,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAC/C,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAmNzG;AAED,KAAK,OAAO,CAAC,CAAC,SAAS,eAAe,IAAI,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC;AAGpE,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC"}
|
package/dist/runtime/builder.js
CHANGED
|
@@ -15,6 +15,7 @@ const errors_js_1 = require("./errors.js");
|
|
|
15
15
|
const reducer_js_1 = require("./reducer.js");
|
|
16
16
|
const time_js_1 = require("./time.js");
|
|
17
17
|
const types_js_1 = require("./types.js");
|
|
18
|
+
const wire_js_1 = require("./wire.js");
|
|
18
19
|
const TAG_SAFE = /^[a-z0-9_.-]+$/;
|
|
19
20
|
/** Deep copy of plain data (objects, arrays, primitives); functions stay by reference. */
|
|
20
21
|
function copyData(value) {
|
|
@@ -143,6 +144,8 @@ function validatePhaseSpec(name, spec, phases) {
|
|
|
143
144
|
});
|
|
144
145
|
(spec.every ?? []).forEach((ev, i) => checkDuration(ev.interval, `phases.${name}.every.${i}.interval`));
|
|
145
146
|
}
|
|
147
|
+
/** The cap on `outcome.pointsDescription`: one line for a menu. */
|
|
148
|
+
const POINTS_DESCRIPTION_MAX = 200;
|
|
146
149
|
/**
|
|
147
150
|
* Defines a challenge. The root declaration carries the data and the state initializers; the
|
|
148
151
|
* steps carry the behavior. `init()` or the first `manifest` read locks the definition.
|
|
@@ -207,6 +210,7 @@ function defineChallenge(config, decl) {
|
|
|
207
210
|
const declaredLocations = Object.keys(ownConfig.challengeConfig?.locations ?? {});
|
|
208
211
|
const manifest = {
|
|
209
212
|
runtime: 2,
|
|
213
|
+
minecraft: wire_js_1.MINECRAFT_TARGET,
|
|
210
214
|
name: ownConfig.name,
|
|
211
215
|
life,
|
|
212
216
|
roles: Object.keys(ownConfig.roles),
|
|
@@ -228,6 +232,8 @@ function defineChallenge(config, decl) {
|
|
|
228
232
|
}
|
|
229
233
|
if (declaredLocations.length > 0)
|
|
230
234
|
manifest.locations = declaredLocations;
|
|
235
|
+
if (outcome.pointsDescription !== undefined)
|
|
236
|
+
manifest.pointsDescription = outcome.pointsDescription.trim();
|
|
231
237
|
locked = {
|
|
232
238
|
config: ownConfig,
|
|
233
239
|
state: ownState,
|
|
@@ -319,6 +325,18 @@ function defineChallenge(config, decl) {
|
|
|
319
325
|
assertOpen("outcome");
|
|
320
326
|
if (outcome)
|
|
321
327
|
duplicates.push("outcome");
|
|
328
|
+
if (o.points !== undefined && typeof o.points !== "function") {
|
|
329
|
+
throw (0, errors_js_1.definitionError)("bad_outcome", "outcome.points must be a function (g, player, result) => integer", {
|
|
330
|
+
path: "outcome.points",
|
|
331
|
+
value: o.points,
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
if (o.pointsDescription !== undefined) {
|
|
335
|
+
const d = o.pointsDescription;
|
|
336
|
+
if (typeof d !== "string" || d.trim().length === 0 || d.length > POINTS_DESCRIPTION_MAX) {
|
|
337
|
+
throw (0, errors_js_1.definitionError)("bad_outcome", `outcome.pointsDescription must be a non-empty string of at most ${POINTS_DESCRIPTION_MAX} characters`, { path: "outcome.pointsDescription", value: d });
|
|
338
|
+
}
|
|
339
|
+
}
|
|
322
340
|
outcome = { ...o };
|
|
323
341
|
return challenge;
|
|
324
342
|
},
|