@kradle/challenges-sdk 0.11.0 → 0.13.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 +71 -1
- package/LLM_README.md +286 -193
- package/MIGRATION-0.13.md +109 -0
- package/README.md +98 -90
- package/dist/engine.d.ts +8 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +17 -0
- package/dist/engine.js.map +1 -0
- package/dist/index.d.ts +19 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +42 -16
- package/dist/index.js.map +1 -1
- package/dist/runtime/builder.d.ts +33 -0
- package/dist/runtime/builder.d.ts.map +1 -0
- package/dist/runtime/builder.js +357 -0
- package/dist/runtime/builder.js.map +1 -0
- package/dist/runtime/errors.d.ts +61 -0
- package/dist/runtime/errors.d.ts.map +1 -0
- package/dist/runtime/errors.js +71 -0
- package/dist/runtime/errors.js.map +1 -0
- package/dist/runtime/reducer.d.ts +205 -0
- package/dist/runtime/reducer.d.ts.map +1 -0
- package/dist/runtime/reducer.js +2025 -0
- package/dist/runtime/reducer.js.map +1 -0
- package/dist/runtime/rules.d.ts +29 -0
- package/dist/runtime/rules.d.ts.map +1 -0
- package/dist/runtime/rules.js +23 -0
- package/dist/runtime/rules.js.map +1 -0
- package/dist/runtime/sandbox.d.ts +2 -1
- package/dist/runtime/sandbox.d.ts.map +1 -1
- package/dist/runtime/sandbox.js +7 -4
- package/dist/runtime/sandbox.js.map +1 -1
- package/dist/runtime/time.d.ts +33 -0
- package/dist/runtime/time.d.ts.map +1 -0
- package/dist/runtime/time.js +53 -0
- package/dist/runtime/time.js.map +1 -0
- package/dist/runtime/types.d.ts +963 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +25 -0
- package/dist/runtime/types.js.map +1 -0
- package/dist/runtime/wire.d.ts +858 -0
- package/dist/runtime/wire.d.ts.map +1 -0
- package/dist/runtime/wire.js +28 -0
- package/dist/runtime/wire.js.map +1 -0
- package/dist/runtime/world.d.ts +58 -3
- package/dist/runtime/world.d.ts.map +1 -1
- package/dist/runtime/world.js +12 -0
- package/dist/runtime/world.js.map +1 -1
- package/dist/structures.d.ts +119 -0
- package/dist/structures.d.ts.map +1 -0
- package/dist/structures.js +279 -0
- package/dist/structures.js.map +1 -0
- package/dist/test/harness.d.ts +280 -0
- package/dist/test/harness.d.ts.map +1 -0
- package/dist/{runtime → test}/harness.js +193 -68
- package/dist/test/harness.js.map +1 -0
- package/dist/test/helpers.d.ts +8 -0
- package/dist/test/helpers.d.ts.map +1 -0
- package/dist/test/helpers.js +12 -0
- package/dist/test/helpers.js.map +1 -0
- package/dist/test/index.d.ts +3 -0
- package/dist/test/index.d.ts.map +1 -0
- package/dist/{runtime → test}/index.js +3 -1
- package/dist/test/index.js.map +1 -0
- package/package.json +31 -16
- package/dist/runtime/examples/capture-the-flag.d.ts +0 -33
- package/dist/runtime/examples/capture-the-flag.d.ts.map +0 -1
- package/dist/runtime/examples/capture-the-flag.js +0 -79
- package/dist/runtime/examples/capture-the-flag.js.map +0 -1
- package/dist/runtime/examples/climb.d.ts +0 -34
- package/dist/runtime/examples/climb.d.ts.map +0 -1
- package/dist/runtime/examples/climb.js +0 -54
- package/dist/runtime/examples/climb.js.map +0 -1
- package/dist/runtime/examples/four-bridges.d.ts +0 -107
- package/dist/runtime/examples/four-bridges.d.ts.map +0 -1
- package/dist/runtime/examples/four-bridges.js +0 -294
- package/dist/runtime/examples/four-bridges.js.map +0 -1
- package/dist/runtime/examples/herding.d.ts +0 -59
- package/dist/runtime/examples/herding.d.ts.map +0 -1
- package/dist/runtime/examples/herding.js +0 -181
- package/dist/runtime/examples/herding.js.map +0 -1
- package/dist/runtime/examples/skywars.d.ts +0 -116
- package/dist/runtime/examples/skywars.d.ts.map +0 -1
- package/dist/runtime/examples/skywars.js +0 -253
- package/dist/runtime/examples/skywars.js.map +0 -1
- package/dist/runtime/harness.d.ts +0 -176
- package/dist/runtime/harness.d.ts.map +0 -1
- package/dist/runtime/harness.js.map +0 -1
- package/dist/runtime/index.d.ts +0 -2
- package/dist/runtime/index.d.ts.map +0 -1
- package/dist/runtime/index.js.map +0 -1
- package/dist/runtime/sdk.d.ts +0 -1038
- package/dist/runtime/sdk.d.ts.map +0 -1
- package/dist/runtime/sdk.js +0 -1271
- package/dist/runtime/sdk.js.map +0 -1
|
@@ -0,0 +1,963 @@
|
|
|
1
|
+
import type { Duration, Ticks } from "./time.js";
|
|
2
|
+
import type { BlockId, Box, EffectId, EndReason, EntityTypeId, ItemId, ItemMeta, ItemStack, Json, GameMode, Marker, SdkEndState, Status, Vec3 } from "./wire.js";
|
|
3
|
+
import type { Entity, EntityFilter, Equipment } from "./world.js";
|
|
4
|
+
/** A per-run parameter the configuration declares; a job may override its default. */
|
|
5
|
+
export type ParamSpec =
|
|
6
|
+
/** A number, with its default. */
|
|
7
|
+
{
|
|
8
|
+
type: "number";
|
|
9
|
+
/** The value when the job sets none. */
|
|
10
|
+
default: number;
|
|
11
|
+
}
|
|
12
|
+
/** A string, with its default. */
|
|
13
|
+
| {
|
|
14
|
+
type: "string";
|
|
15
|
+
/** The value when the job sets none. */
|
|
16
|
+
default: string;
|
|
17
|
+
}
|
|
18
|
+
/** A boolean, with its default. */
|
|
19
|
+
| {
|
|
20
|
+
type: "boolean";
|
|
21
|
+
/** The value when the job sets none. */
|
|
22
|
+
default: boolean;
|
|
23
|
+
};
|
|
24
|
+
/** The shape of config.ts that the runtime reads. Roles, end states, locations and params become literal types. */
|
|
25
|
+
export interface ChallengeConfig {
|
|
26
|
+
/** The challenge's display name. */
|
|
27
|
+
name: string;
|
|
28
|
+
/** The roles by id. The keys become the `role` literal type. */
|
|
29
|
+
roles: Record<string, {
|
|
30
|
+
/** What the role does, shown to the player. */
|
|
31
|
+
description?: string;
|
|
32
|
+
/** The role's task text, shown to the player. */
|
|
33
|
+
specificTask?: string;
|
|
34
|
+
/** The seats the run must fill for this role. */
|
|
35
|
+
minParticipants?: number;
|
|
36
|
+
}>;
|
|
37
|
+
/** The execution configuration. The `locations` names become a literal type. */
|
|
38
|
+
challengeConfig: {
|
|
39
|
+
/** The named locations, by name; the run supplies their coordinates. */
|
|
40
|
+
locations?: Record<string, unknown>;
|
|
41
|
+
[key: string]: unknown;
|
|
42
|
+
};
|
|
43
|
+
/** The end states a challenge may report: a list, or a record of id to description. */
|
|
44
|
+
endStates?: readonly string[] | Record<string, string>;
|
|
45
|
+
/** The per-run parameters, with their defaults. */
|
|
46
|
+
params?: Record<string, ParamSpec>;
|
|
47
|
+
}
|
|
48
|
+
/** The role ids of a configuration, as a literal type. */
|
|
49
|
+
export type RoleOf<C extends ChallengeConfig> = keyof C["roles"] & string;
|
|
50
|
+
/** The end state ids of a configuration, as a literal type; `string` when none are declared. */
|
|
51
|
+
export type EndStateOf<C extends ChallengeConfig> = C["endStates"] extends readonly (infer E)[] ? E & string : C["endStates"] extends Record<infer K, string> ? K & string : string;
|
|
52
|
+
/** The location names of a configuration, as a literal type; `never` without locations. */
|
|
53
|
+
export type LocationOf<C extends ChallengeConfig> = C["challengeConfig"] extends {
|
|
54
|
+
locations: infer L;
|
|
55
|
+
} ? keyof L & string : never;
|
|
56
|
+
/** The resolved parameter values of a configuration: each `type` becomes its primitive type. */
|
|
57
|
+
export type ParamsOf<C extends ChallengeConfig> = C["params"] extends Record<string, ParamSpec> ? {
|
|
58
|
+
[K in keyof C["params"]]: C["params"][K] extends {
|
|
59
|
+
type: "number";
|
|
60
|
+
} ? number : C["params"][K] extends {
|
|
61
|
+
type: "string";
|
|
62
|
+
} ? string : boolean;
|
|
63
|
+
} : Record<string, never>;
|
|
64
|
+
/**
|
|
65
|
+
* The vote `skills.voteForPlayer` casts in. Its options are the other participants' names, so it
|
|
66
|
+
* declares no `options`: `votes: { player: { final: true } }`.
|
|
67
|
+
*/
|
|
68
|
+
export declare const PLAYER_VOTE = "player";
|
|
69
|
+
/** The part of a vote declaration that makes types: the options. */
|
|
70
|
+
export type VoteOptionsDecl = {
|
|
71
|
+
readonly options?: readonly string[] | undefined;
|
|
72
|
+
};
|
|
73
|
+
/** The votes of a declaration, by id. */
|
|
74
|
+
export type VotesDecl = Record<string, VoteOptionsDecl>;
|
|
75
|
+
/** The vote ids of a declaration, as a literal type. */
|
|
76
|
+
export type VoteIdOf<V extends VotesDecl> = keyof V & string;
|
|
77
|
+
/** The option type of one vote: a participant name for the player vote, that vote's declared options otherwise. */
|
|
78
|
+
export type OptionFor<V extends VotesDecl, Id> = Id extends typeof PLAYER_VOTE ? string : Id extends keyof V ? V[Id] extends {
|
|
79
|
+
readonly options: readonly (infer O)[];
|
|
80
|
+
} ? O & string : never : never;
|
|
81
|
+
/** One vote's rules. `options` are the choices; the `player` vote has none. */
|
|
82
|
+
export interface VoteRule<R, Pl, O extends string> {
|
|
83
|
+
/** `true` (the default): one vote per open round. `false`: a new vote replaces the old one until the round closes. */
|
|
84
|
+
final?: boolean;
|
|
85
|
+
/** `false` (the default): an eliminated player's vote is refused. */
|
|
86
|
+
allowEliminated?: boolean;
|
|
87
|
+
/** The `player` vote only. `false` (the default): a player cannot vote for themself. */
|
|
88
|
+
allowSelf?: boolean;
|
|
89
|
+
/** The challenge's veto, a pure predicate. `true` accepts; a string refuses with that feedback to the voter. */
|
|
90
|
+
accept?: (g: R, voter: Pl, option: O) => true | string;
|
|
91
|
+
}
|
|
92
|
+
/** Read-only through every level: the engine writes the observations, a rule reads them. */
|
|
93
|
+
export type DeepReadonly<X> = X extends (infer E)[] ? ReadonlyArray<DeepReadonly<E>> : X extends (...args: never[]) => unknown ? X : X extends object ? {
|
|
94
|
+
readonly [K in keyof X]: DeepReadonly<X[K]>;
|
|
95
|
+
} : X;
|
|
96
|
+
/** The roster facts of a participant: fixed for the run. */
|
|
97
|
+
export interface Roster<C extends ChallengeConfig, R extends RoleOf<C> = RoleOf<C>> {
|
|
98
|
+
/** The player's name, unique in the run. */
|
|
99
|
+
readonly name: string;
|
|
100
|
+
/** The role the run assigned. */
|
|
101
|
+
readonly role: R;
|
|
102
|
+
/** One-based, from the run's participant list; stable for the run. */
|
|
103
|
+
readonly seatNumber: number;
|
|
104
|
+
/** `true` for a human participant, `false` for an agent. */
|
|
105
|
+
readonly human: boolean;
|
|
106
|
+
}
|
|
107
|
+
/** What the engine reports about a participant. A rule reads these; the SDK writes them. */
|
|
108
|
+
export interface Observed {
|
|
109
|
+
/** Whether the player is online. */
|
|
110
|
+
readonly connected: boolean;
|
|
111
|
+
/** Whether the player is alive; `false` between a death and the respawn. */
|
|
112
|
+
readonly alive: boolean;
|
|
113
|
+
/** Whether the player left the game: a death under `life: "single"`, or `g.eliminate`. */
|
|
114
|
+
readonly eliminated: boolean;
|
|
115
|
+
/** `true` while the player is in the game: not eliminated. */
|
|
116
|
+
readonly isActive: boolean;
|
|
117
|
+
/** How many times the player died. */
|
|
118
|
+
readonly deaths: number;
|
|
119
|
+
/** How many participants the player killed. */
|
|
120
|
+
readonly kills: number;
|
|
121
|
+
/** The reported score: derived by `outcome.scores`, or set with `g.setScore` / `g.addScore`. */
|
|
122
|
+
readonly score: number;
|
|
123
|
+
/** The position, in blocks. Set from the first sample onward; a disconnected player keeps the last one. */
|
|
124
|
+
readonly pos: Readonly<Vec3>;
|
|
125
|
+
/** Degrees, Minecraft convention. */
|
|
126
|
+
readonly yaw: number;
|
|
127
|
+
/** Degrees, Minecraft convention. */
|
|
128
|
+
readonly pitch: number;
|
|
129
|
+
/** Degrees, Minecraft convention. */
|
|
130
|
+
readonly headYaw: number;
|
|
131
|
+
/** The server's entity id; `null` before the first sample. */
|
|
132
|
+
readonly entityId: number | null;
|
|
133
|
+
/** The player's uuid; `null` before the first sample. */
|
|
134
|
+
readonly uuid: string | null;
|
|
135
|
+
/** Worn and held items by slot (main, off, head, chest, legs, feet). */
|
|
136
|
+
readonly equipment: DeepReadonly<Equipment>;
|
|
137
|
+
/** Whether the player is invisible. */
|
|
138
|
+
readonly invisible: boolean;
|
|
139
|
+
/** Half-hearts. */
|
|
140
|
+
readonly health: number;
|
|
141
|
+
/** The food level, 0..20. */
|
|
142
|
+
readonly food: number;
|
|
143
|
+
/** The game mode. */
|
|
144
|
+
readonly gamemode: GameMode;
|
|
145
|
+
/** The item in the main hand; `null` for an empty hand. */
|
|
146
|
+
readonly held: ItemId | null;
|
|
147
|
+
/** Inventory by slot: 0-35 main (0-8 hotbar), 36-39 armor, 40 offhand. */
|
|
148
|
+
readonly slots: DeepReadonly<Record<number, ItemStack>>;
|
|
149
|
+
/** Item counts over every slot, by item id. */
|
|
150
|
+
readonly inventory: DeepReadonly<Record<string, number>>;
|
|
151
|
+
/** The declared zones the player stands in. */
|
|
152
|
+
readonly zones: readonly string[];
|
|
153
|
+
}
|
|
154
|
+
/** A participant as the state initializers see it: the roster and the observations, no state yet. */
|
|
155
|
+
export type ObservedPlayer<C extends ChallengeConfig, R extends RoleOf<C> = RoleOf<C>> = Roster<C, R> & Observed;
|
|
156
|
+
/** The challenge's per-player state, resolved for every role of the config. */
|
|
157
|
+
export type PlayerMap<C extends ChallengeConfig> = {
|
|
158
|
+
[R in RoleOf<C>]: object;
|
|
159
|
+
};
|
|
160
|
+
/** The map `player` (shared) and `players` (per role) declare: a role without its own entry gets the shared state. */
|
|
161
|
+
export type ResolvePlayers<C extends ChallengeConfig, P extends object, PS> = {
|
|
162
|
+
[R in RoleOf<C>]: R extends keyof PS ? (PS[R] extends object ? PS[R] : P) : P;
|
|
163
|
+
};
|
|
164
|
+
/** One participant of role `R` in a handler: the observations plus its own writable `state`. */
|
|
165
|
+
export type PlayerOf<C extends ChallengeConfig, PM extends PlayerMap<C>, R extends RoleOf<C>> = ObservedPlayer<C, R> & {
|
|
166
|
+
/** The challenge's own data for this player, from the initializer; a handler writes it. */
|
|
167
|
+
readonly state: PM[R];
|
|
168
|
+
};
|
|
169
|
+
/** The same participant in a pure callback: its `state` is read-only too. */
|
|
170
|
+
export type ReadPlayerOf<C extends ChallengeConfig, PM extends PlayerMap<C>, R extends RoleOf<C>> = ObservedPlayer<C, R> & {
|
|
171
|
+
/** The challenge's own data for this player, read-only here. */
|
|
172
|
+
readonly state: DeepReadonly<PM[R]>;
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* A participant: one member per role, discriminated by `role`, so `p.role === "p1"`
|
|
176
|
+
* narrows to that role's state (an inferred type predicate does the same in `filter`).
|
|
177
|
+
*/
|
|
178
|
+
export type Player<C extends ChallengeConfig, PM extends PlayerMap<C> = PlayerMap<C>> = {
|
|
179
|
+
[R in RoleOf<C>]: PlayerOf<C, PM, R>;
|
|
180
|
+
}[RoleOf<C>];
|
|
181
|
+
/** A participant in a pure callback: its `state` is read-only. */
|
|
182
|
+
export type ReadPlayer<C extends ChallengeConfig, PM extends PlayerMap<C> = PlayerMap<C>> = {
|
|
183
|
+
[R in RoleOf<C>]: ReadPlayerOf<C, PM, R>;
|
|
184
|
+
}[RoleOf<C>];
|
|
185
|
+
/** A player, or anything named like one: what a message target, a log line or a winner check takes. */
|
|
186
|
+
export type Named = {
|
|
187
|
+
/** The player's name. */
|
|
188
|
+
readonly name: string;
|
|
189
|
+
};
|
|
190
|
+
/** The player type a context works with: `type Pl = PlayerIn<G>`. */
|
|
191
|
+
export type PlayerIn<Cx> = Cx extends {
|
|
192
|
+
readonly players: ReadonlyArray<infer Pl>;
|
|
193
|
+
} ? Pl : never;
|
|
194
|
+
/** An observed block: the id and its state properties, e.g. `{ id: "minecraft:oak_stairs", props: { facing: "east" } }`. */
|
|
195
|
+
export interface BlockState {
|
|
196
|
+
/** The block id, without the state: `minecraft:oak_stairs`. */
|
|
197
|
+
readonly id: BlockId;
|
|
198
|
+
/** The state properties, e.g. `{ facing: "east" }`; empty for a block without state. */
|
|
199
|
+
readonly props: Readonly<Record<string, string>>;
|
|
200
|
+
}
|
|
201
|
+
/** A block to place: an id, or an id with state properties and block entity data (SNBT, for a banner or a sign). */
|
|
202
|
+
export type BlockSpec = BlockId | {
|
|
203
|
+
/** The block id. */
|
|
204
|
+
id: BlockId;
|
|
205
|
+
/** The state properties to set, e.g. `{ facing: "east" }`. */
|
|
206
|
+
props?: Record<string, string>;
|
|
207
|
+
/** The block entity data, as SNBT (a sign's text, a banner's patterns). */
|
|
208
|
+
blockEntity?: string;
|
|
209
|
+
};
|
|
210
|
+
/** A block predicate: an id (any state), an id with the properties that must match, or a function over the state. */
|
|
211
|
+
export type BlockMatch = BlockId | {
|
|
212
|
+
/** The block id. */
|
|
213
|
+
id: BlockId;
|
|
214
|
+
/** The properties that must match; the block may have more. */
|
|
215
|
+
props?: Record<string, string>;
|
|
216
|
+
} | ((state: BlockState) => boolean);
|
|
217
|
+
/** An item to give or put in a container: an id, or an id with a count and metadata. */
|
|
218
|
+
export type ItemRequest = ItemId | ({
|
|
219
|
+
/** The item id. */
|
|
220
|
+
id: ItemId;
|
|
221
|
+
/** How many; default 1. */
|
|
222
|
+
count?: number;
|
|
223
|
+
/** Raw item data (SNBT), merged with the metadata fields. */
|
|
224
|
+
snbt?: string;
|
|
225
|
+
} & ItemMeta);
|
|
226
|
+
/** An item predicate for counts: an id, or an id with the metadata that must match. */
|
|
227
|
+
export type ItemMatch = ItemId | ({
|
|
228
|
+
/** The item id. */
|
|
229
|
+
id: ItemId;
|
|
230
|
+
} & ItemMeta);
|
|
231
|
+
/** Integer block coordinates. `toBlockPos` floors a position. */
|
|
232
|
+
export type BlockPos = {
|
|
233
|
+
/** East. */
|
|
234
|
+
x: number;
|
|
235
|
+
/** Up. */
|
|
236
|
+
y: number;
|
|
237
|
+
/** South. */
|
|
238
|
+
z: number;
|
|
239
|
+
};
|
|
240
|
+
/** The run's seeded generator. Every draw advances the state in the reducer, so a replay draws the same values. */
|
|
241
|
+
export interface Random {
|
|
242
|
+
/** A whole number between `lo` and `hi`, both inclusive. */
|
|
243
|
+
int(lo: number, hi: number): number;
|
|
244
|
+
/** One element; `undefined` for an empty list. */
|
|
245
|
+
pick<X>(items: readonly [X, ...X[]]): X;
|
|
246
|
+
/** One element; `undefined` for an empty list. */
|
|
247
|
+
pick<X>(items: readonly X[]): X | undefined;
|
|
248
|
+
/** A new array with the elements in random order. */
|
|
249
|
+
shuffle<X>(items: readonly X[]): X[];
|
|
250
|
+
/** One element, weighted; `undefined` when the weights sum to zero. */
|
|
251
|
+
weighted<X>(items: ReadonlyArray<{
|
|
252
|
+
/** The relative weight; 0 never picks the element. */
|
|
253
|
+
weight: number;
|
|
254
|
+
/** The element. */
|
|
255
|
+
value: X;
|
|
256
|
+
}>): X | undefined;
|
|
257
|
+
}
|
|
258
|
+
/** What the state initializers receive. Not a context: the state does not exist yet. */
|
|
259
|
+
export interface Init<C extends ChallengeConfig> {
|
|
260
|
+
/** The run's parameter values: the job's overrides over the configuration's defaults. */
|
|
261
|
+
readonly params: ParamsOf<C>;
|
|
262
|
+
/** The seeded generator. Closed after the initializers: keep the values, not the generator. */
|
|
263
|
+
readonly random: Random;
|
|
264
|
+
/** The observed roster, positions included, no state yet. */
|
|
265
|
+
readonly players: ReadonlyArray<ObservedPlayer<C>>;
|
|
266
|
+
/** The declared locations of the configuration, by name. */
|
|
267
|
+
readonly locations: Readonly<Record<LocationOf<C>, Vec3>>;
|
|
268
|
+
}
|
|
269
|
+
/** What the game-over hook and the judge data receive: frozen when the game ended. */
|
|
270
|
+
export interface Result<C extends ChallengeConfig> {
|
|
271
|
+
/** The winners' names, as `game_over` carries them. */
|
|
272
|
+
readonly winners: readonly string[];
|
|
273
|
+
/** A declared end state, an SDK error state, or "" when no end state was classified. */
|
|
274
|
+
readonly endState: EndStateOf<C> | SdkEndState | "";
|
|
275
|
+
/** Why the game ended: a rule, the time, a phase's `"END"`, `endGame`, or an error. */
|
|
276
|
+
readonly reason: EndReason;
|
|
277
|
+
/** The phase the game ended in; `null` without phases. */
|
|
278
|
+
readonly phase: string | null;
|
|
279
|
+
/** Ticks from game start to the end. */
|
|
280
|
+
readonly elapsed: Ticks;
|
|
281
|
+
/** Ticks from the last phase entry to the end. */
|
|
282
|
+
readonly phaseElapsed: Ticks;
|
|
283
|
+
/** Whether the player is in `winners`. */
|
|
284
|
+
isWinner(player: Named): boolean;
|
|
285
|
+
}
|
|
286
|
+
/** Who receives a message: one player, several, or `"all"`. */
|
|
287
|
+
export type To<Pl> = Pl | readonly Pl[] | "all";
|
|
288
|
+
/** What `g.title` shows. */
|
|
289
|
+
export type TitleOptions = {
|
|
290
|
+
/** The main line. */
|
|
291
|
+
title: string;
|
|
292
|
+
/** The smaller line under the title. */
|
|
293
|
+
subtitle?: string | undefined;
|
|
294
|
+
/** Fade-in time. Default 10 ticks. */
|
|
295
|
+
fadeIn?: Duration | undefined;
|
|
296
|
+
/** Time on the screen. Default 70 ticks. */
|
|
297
|
+
stay?: Duration | undefined;
|
|
298
|
+
/** Fade-out time. Default 20 ticks. */
|
|
299
|
+
fadeOut?: Duration | undefined;
|
|
300
|
+
};
|
|
301
|
+
/** The state type a context exposes: writable in a handler, read-only in a pure callback. */
|
|
302
|
+
type StateFor<G, W extends boolean> = W extends true ? G : DeepReadonly<G>;
|
|
303
|
+
type PlayerFor<C extends ChallengeConfig, PM extends PlayerMap<C>, W extends boolean> = W extends true ? Player<C, PM> : ReadPlayer<C, PM>;
|
|
304
|
+
type PlayerOfFor<C extends ChallengeConfig, PM extends PlayerMap<C>, R extends RoleOf<C>, W extends boolean> = W extends true ? PlayerOf<C, PM, R> : ReadPlayerOf<C, PM, R>;
|
|
305
|
+
/**
|
|
306
|
+
* The reads every context has. `W` says whether the state and the players are writable
|
|
307
|
+
* (a handler) or read-only (a pure callback).
|
|
308
|
+
*/
|
|
309
|
+
export interface Reads<C extends ChallengeConfig, G, PM extends PlayerMap<C>, Ph extends string, Z extends string, T extends string, V extends VotesDecl, W extends boolean> {
|
|
310
|
+
/** The shared challenge data. Read-only in a pure callback; a handler writes it. */
|
|
311
|
+
readonly state: StateFor<G, W>;
|
|
312
|
+
/** The run's parameter values. */
|
|
313
|
+
readonly params: ParamsOf<C>;
|
|
314
|
+
/** Every participant, in roster order. */
|
|
315
|
+
readonly players: ReadonlyArray<PlayerFor<C, PM, W>>;
|
|
316
|
+
/** The participants still in the game: not eliminated. */
|
|
317
|
+
readonly activePlayers: ReadonlyArray<PlayerFor<C, PM, W>>;
|
|
318
|
+
/** A participant by name; `undefined` for a name that is not a participant. */
|
|
319
|
+
player(name: string): PlayerFor<C, PM, W> | undefined;
|
|
320
|
+
/** The participants of one role, typed with that role's state. */
|
|
321
|
+
playersWithRole<R extends RoleOf<C>>(role: R): PlayerOfFor<C, PM, R, W>[];
|
|
322
|
+
/** The one participant of a role. Throws unless exactly one participant has it. */
|
|
323
|
+
playerWithRole<R extends RoleOf<C>>(role: R): PlayerOfFor<C, PM, R, W>;
|
|
324
|
+
/** The server tick, absolute. */
|
|
325
|
+
readonly tick: number;
|
|
326
|
+
/** Ticks since the game started. */
|
|
327
|
+
readonly elapsed: Ticks;
|
|
328
|
+
/** The stage: CONNECTING, SETUP, PLAYING, OVER. */
|
|
329
|
+
readonly status: Status;
|
|
330
|
+
/** The current phase; `null` before the first entry and without phases. */
|
|
331
|
+
readonly phase: Ph | null;
|
|
332
|
+
/** The entry number of the current phase: it grows at every transition (1 for the first phase), across all phases; 0 without a phase. */
|
|
333
|
+
readonly phaseEntry: number;
|
|
334
|
+
/** Ticks since the current phase was entered; 0 without a phase. */
|
|
335
|
+
readonly phaseElapsed: Ticks;
|
|
336
|
+
/** Ticks left in the current phase; `null` without a duration. */
|
|
337
|
+
readonly phaseRemaining: Ticks | null;
|
|
338
|
+
/** A declared location of the configuration. */
|
|
339
|
+
location(name: LocationOf<C>): Vec3;
|
|
340
|
+
/** Marker entities from `markers`: all of them, or those carrying `tag` exactly. */
|
|
341
|
+
markers(tag?: string): Marker[];
|
|
342
|
+
/** A declared zone by name, or any box: whether the block at the player's feet is inside. */
|
|
343
|
+
inZone(player: PlayerFor<C, PM, W>, zone: NoInfer<Z> | Box): boolean;
|
|
344
|
+
/** How many of an item a player holds over every slot. */
|
|
345
|
+
countItems(player: PlayerFor<C, PM, W>, item: ItemMatch): number;
|
|
346
|
+
/** The block id at `at` (`minecraft:oak_fence`); `undefined` outside the loaded chunks. */
|
|
347
|
+
blockAt(at: Vec3): BlockId | undefined;
|
|
348
|
+
/** The block with its state properties; `undefined` outside the loaded chunks. */
|
|
349
|
+
blockStateAt(at: Vec3): BlockState | undefined;
|
|
350
|
+
/** Whether the block at `at` matches. Throws `block_unknown` outside the loaded chunks. */
|
|
351
|
+
blockMatches(at: Vec3, match: BlockMatch): boolean;
|
|
352
|
+
/** The count of matching blocks in a box. Throws `block_unknown` when a chunk of the box is not loaded, unless `partial`. */
|
|
353
|
+
blockCount(box: Box, match: BlockMatch, opts?: {
|
|
354
|
+
/** `true`: count the loaded chunks only instead of throwing on an unloaded one. */
|
|
355
|
+
partial?: boolean;
|
|
356
|
+
}): number;
|
|
357
|
+
/** The positions of matching blocks in a box, up to `limit`. Throws like `blockCount`. */
|
|
358
|
+
findBlocks(box: Box, match: BlockMatch, opts?: {
|
|
359
|
+
/** At most this many positions. */
|
|
360
|
+
limit?: number;
|
|
361
|
+
/** `true`: search the loaded chunks only instead of throwing on an unloaded one. */
|
|
362
|
+
partial?: boolean;
|
|
363
|
+
}): BlockPos[];
|
|
364
|
+
/** Whether every chunk of the box is loaded in the mirror. */
|
|
365
|
+
regionLoaded(box: Box): boolean;
|
|
366
|
+
/** The world time of day, 0..24000. */
|
|
367
|
+
readonly worldTime: number;
|
|
368
|
+
/** Non-player entities: all, or those in a box, of a type, or with a tag (`"kradle.*"` matches a prefix). */
|
|
369
|
+
entities(filter?: EntityFilter): ReadonlyArray<DeepReadonly<Entity>>;
|
|
370
|
+
/** One entity by id; `undefined` when the mirror does not hold it. */
|
|
371
|
+
entity(id: string): DeepReadonly<Entity> | undefined;
|
|
372
|
+
/** The contents of a container the mirror holds, by item id; `undefined` for a block the mirror does not know. */
|
|
373
|
+
containerContents(at: Vec3): Readonly<Record<string, number>> | undefined;
|
|
374
|
+
/** The accepted votes of the current round: voter name to option. */
|
|
375
|
+
votes<Id extends VoteIdOf<V>>(voteId: Id): Readonly<Record<string, OptionFor<V, Id>>>;
|
|
376
|
+
/** One voter's option in the current round, or `undefined`. */
|
|
377
|
+
vote<Id extends VoteIdOf<V>>(player: Named, voteId: Id): OptionFor<V, Id> | undefined;
|
|
378
|
+
/** Whether a round of the vote is open. */
|
|
379
|
+
voteOpen(voteId: VoteIdOf<V>): boolean;
|
|
380
|
+
/** The counts per option, the leading options, and the active players who did not vote. */
|
|
381
|
+
tally<Id extends VoteIdOf<V>>(voteId: Id): {
|
|
382
|
+
/** Votes per option, for the options that got any. */
|
|
383
|
+
counts: Readonly<Record<string, number>>;
|
|
384
|
+
/** The options with the most votes; empty without a vote. */
|
|
385
|
+
leaders: OptionFor<V, Id>[];
|
|
386
|
+
/** The active players who did not vote in this round. */
|
|
387
|
+
abstaining: PlayerFor<C, PM, W>[];
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* The read-only context: every read, no write, no effect. The pure callbacks get it:
|
|
392
|
+
* `outcome.scores`, `outcome.endWhen`, `outcome.winners`, `outcome.endState`, `judgeContext`,
|
|
393
|
+
* a phase's `until`, a functional `duration` or `next`, a vote's `accept`.
|
|
394
|
+
*/
|
|
395
|
+
export interface ReadCtx<C extends ChallengeConfig, G, PM extends PlayerMap<C>, Ph extends string, Z extends string, T extends string, V extends VotesDecl> extends Reads<C, G, PM, Ph, Z, T, V, false> {
|
|
396
|
+
}
|
|
397
|
+
/** The messages and metrics: what a notification-only callback may do. */
|
|
398
|
+
export interface NotifyOps {
|
|
399
|
+
/** A public chat line. */
|
|
400
|
+
say(message: string): void;
|
|
401
|
+
/** A private chat line to one player, several, or all. */
|
|
402
|
+
tell(to: To<Named>, message: string): void;
|
|
403
|
+
/** A title on the screen. Durations default to the server's (10 ticks in, 70 stay, 20 out). */
|
|
404
|
+
title(to: To<Named>, options: TitleOptions): void;
|
|
405
|
+
/** A line in the run log; `player` attributes it, `data` goes along as JSON. */
|
|
406
|
+
log(message: string, opts?: {
|
|
407
|
+
/** The player the line is about; the run log files it under that participant. */
|
|
408
|
+
player?: Named | undefined;
|
|
409
|
+
/** JSON data along with the line. */
|
|
410
|
+
data?: Json | undefined;
|
|
411
|
+
}): void;
|
|
412
|
+
/** A metric of the run: the last value per name reaches the run result. Per player with `player`. */
|
|
413
|
+
setMetric(name: string, value: number, opts?: {
|
|
414
|
+
/** The player the metric belongs to; global without one. */
|
|
415
|
+
player?: Named | undefined;
|
|
416
|
+
}): void;
|
|
417
|
+
/** An achievement line in the run log. */
|
|
418
|
+
achievement(player: Named, id: string): void;
|
|
419
|
+
}
|
|
420
|
+
/** The notification context: reads plus messages and metrics. The game-over hook gets it. */
|
|
421
|
+
export interface NotifyCtx<C extends ChallengeConfig, G, PM extends PlayerMap<C>, Ph extends string, Z extends string, T extends string, V extends VotesDecl> extends ReadCtx<C, G, PM, Ph, Z, T, V>, NotifyOps {
|
|
422
|
+
}
|
|
423
|
+
/** The world effects: queued for the engine, applied on the next tick (in the setup batches during setup). */
|
|
424
|
+
export interface EffectOps<C extends ChallengeConfig, PM extends PlayerMap<C>> {
|
|
425
|
+
/** Gives items to a player. `count` overrides the request's count. */
|
|
426
|
+
give(player: Player<C, PM>, item: ItemRequest, count?: number): void;
|
|
427
|
+
/** Removes items from a player's inventory: one item (up to `count`), or everything. */
|
|
428
|
+
clear(player: Player<C, PM>, item?: ItemId, count?: number): void;
|
|
429
|
+
/** Applies a status effect for `duration`. */
|
|
430
|
+
effect(player: Player<C, PM>, options: {
|
|
431
|
+
/** The status effect id, e.g. `minecraft:speed`. */
|
|
432
|
+
id: EffectId;
|
|
433
|
+
/** How long it lasts. */
|
|
434
|
+
duration: Duration;
|
|
435
|
+
/** The level minus one; default 0. */
|
|
436
|
+
amplifier?: number;
|
|
437
|
+
}): void;
|
|
438
|
+
/** Moves a player to `to`. */
|
|
439
|
+
teleport(player: Player<C, PM>, to: Vec3): void;
|
|
440
|
+
/** Kills a player. A death event follows. */
|
|
441
|
+
kill(player: Player<C, PM>): void;
|
|
442
|
+
/** Sets a player's game mode. */
|
|
443
|
+
gamemode(player: Player<C, PM>, mode: GameMode): void;
|
|
444
|
+
/** Sets a player attribute, e.g. `minecraft:generic.max_health`. */
|
|
445
|
+
attribute(player: Player<C, PM>, attribute: string, value: number): void;
|
|
446
|
+
/** Fills a box with one block. The mirror updates when the engine reports the fill. */
|
|
447
|
+
fill(from: Vec3, to: Vec3, block: BlockSpec): void;
|
|
448
|
+
/** Sets one block. The mirror updates when the engine reports the change. */
|
|
449
|
+
setBlock(at: Vec3, block: BlockSpec): void;
|
|
450
|
+
/** Summons an entity; `nbt` is SNBT for its data. */
|
|
451
|
+
summon(entity: EntityTypeId, at: Vec3, opts?: {
|
|
452
|
+
/** The entity data, as SNBT (a name, tags, equipment). */
|
|
453
|
+
nbt?: string;
|
|
454
|
+
}): void;
|
|
455
|
+
/** Removes entities in the run's world; players are never removed. */
|
|
456
|
+
killEntities(entities?: EntityTypeId[]): void;
|
|
457
|
+
/** Sets the time of day. */
|
|
458
|
+
time(value: "day" | "noon" | "night" | "midnight"): void;
|
|
459
|
+
/** Sets a game rule, e.g. `doDaylightCycle`. */
|
|
460
|
+
gamerule(rule: string, value: boolean | number): void;
|
|
461
|
+
/** Sets the world spawn: participants respawn there, the watcher and spectators are moved there. */
|
|
462
|
+
setSpawn(at: Vec3): void;
|
|
463
|
+
/** Moves and resizes the world border; `over` animates the size change. */
|
|
464
|
+
worldBorder(opts: {
|
|
465
|
+
/** The new center. */
|
|
466
|
+
center?: {
|
|
467
|
+
/** East. */
|
|
468
|
+
x: number;
|
|
469
|
+
/** South. */
|
|
470
|
+
z: number;
|
|
471
|
+
};
|
|
472
|
+
/** The new diameter, in blocks. */
|
|
473
|
+
size?: number;
|
|
474
|
+
/** The time the size change takes; at once without it. */
|
|
475
|
+
over?: Duration;
|
|
476
|
+
}): void;
|
|
477
|
+
/** Replaces the contents of a chest or other container block. */
|
|
478
|
+
container(at: Vec3, items: ItemRequest[]): void;
|
|
479
|
+
/**
|
|
480
|
+
* The escape hatch. Logged. Prefer a typed method. The arena runs only the gameplay commands on its
|
|
481
|
+
* allowlist (no `fill`, `setblock`, `clone`, `place`, `data`, `item`, `forceload`: a command fires no
|
|
482
|
+
* block event, so the mirror would miss it); a refused command comes back as `effectFailed`.
|
|
483
|
+
*/
|
|
484
|
+
command(cmd: string): void;
|
|
485
|
+
}
|
|
486
|
+
/** The setup context: every read, every message, every world effect, the state writes. No phase, end, timer, vote or walk. */
|
|
487
|
+
export interface SetupCtx<C extends ChallengeConfig, G, PM extends PlayerMap<C>, Ph extends string, Z extends string, T extends string, V extends VotesDecl> extends Reads<C, G, PM, Ph, Z, T, V, true>, NotifyOps, EffectOps<C, PM> {
|
|
488
|
+
/** Sets a player's score. Throws `score_derived` when `outcome.scores` computes the scores. */
|
|
489
|
+
setScore(player: Player<C, PM>, score: number): void;
|
|
490
|
+
/** Adds to a player's score. Throws `score_derived` when `outcome.scores` computes the scores. */
|
|
491
|
+
addScore(player: Player<C, PM>, delta: number): void;
|
|
492
|
+
/** Removes a player from the game (spectator). `isActive` becomes false. */
|
|
493
|
+
eliminate(player: Player<C, PM>): void;
|
|
494
|
+
/** The run's seeded generator. Every draw advances the state, so a replay draws the same values. */
|
|
495
|
+
readonly random: Random;
|
|
496
|
+
}
|
|
497
|
+
/** The gameplay context: everything, including the phase and end requests, timers, votes, walks and batches. */
|
|
498
|
+
export interface Ctx<C extends ChallengeConfig, G, PM extends PlayerMap<C>, Ph extends string, Z extends string, T extends string, V extends VotesDecl> extends SetupCtx<C, G, PM, Ph, Z, T, V> {
|
|
499
|
+
/**
|
|
500
|
+
* Requests a phase: exit the current phase, enter `phase`. An event's handlers form one group
|
|
501
|
+
* (every global `on` in order, then the phase's): the request applies once the group returned,
|
|
502
|
+
* so a later handler of the same event still sees the current phase, and a second request in
|
|
503
|
+
* the group throws `phase_twice`. A request from `enter` applies after `enter` (a chain, up to
|
|
504
|
+
* the budget of 8 per event). Not available in `exit` or in a pure callback.
|
|
505
|
+
*/
|
|
506
|
+
startPhase(phase: Ph): void;
|
|
507
|
+
/** Ends the current phase through its `next`. Throws without a `next`. */
|
|
508
|
+
endPhase(): void;
|
|
509
|
+
/** Requests the end of the game. Later callbacks of this event are skipped; this callback continues. */
|
|
510
|
+
endGame(endState?: EndStateOf<C>): void;
|
|
511
|
+
/** A named timer. `on.timer({ name })` fires when it is due; scheduling again resets it. */
|
|
512
|
+
schedule(name: NoInfer<T>, after: Duration): void;
|
|
513
|
+
/** Cancels a scheduled timer. No effect when the timer is not scheduled. */
|
|
514
|
+
cancel(name: NoInfer<T>): void;
|
|
515
|
+
/** Opens a round of the vote and clears the previous votes. Throws `vote_open` when a round is open. */
|
|
516
|
+
openVote(voteId: VoteIdOf<V>): void;
|
|
517
|
+
/** Closes the round; the votes stay readable. */
|
|
518
|
+
closeVote(voteId: VoteIdOf<V>): void;
|
|
519
|
+
/** Clears the votes and starts a new round number, open or closed as before: a request stamped with the old round is refused. */
|
|
520
|
+
resetVote(voteId: VoteIdOf<V>): void;
|
|
521
|
+
/** The arena walks the bot; a failed walk teleports unless fallback is "none". `on.walkDone` reports. */
|
|
522
|
+
walk(player: Player<C, PM>, to: Vec3, opts?: {
|
|
523
|
+
/** What happens when the walk fails: a teleport to `to` (the default), or nothing. */
|
|
524
|
+
fallback?: "teleport" | "none";
|
|
525
|
+
}): void;
|
|
526
|
+
/** Effects queued inside `fn` go out as one named batch; `on.batchDone` reports. Throws `batch_pending` on a duplicate name. */
|
|
527
|
+
batch(name: string, fn: () => void): void;
|
|
528
|
+
}
|
|
529
|
+
type H<Cx, E> = (g: Cx, e: E) => void;
|
|
530
|
+
/** Handlers receive resolved players. Events for names that are not participants never reach them. */
|
|
531
|
+
export interface Handlers<C extends ChallengeConfig, G, PM extends PlayerMap<C>, Ph extends string, Z extends string, T extends string, V extends VotesDecl> {
|
|
532
|
+
/** Every server tick (20 per second). */
|
|
533
|
+
tick?: (g: Ctx<C, G, PM, Ph, Z, T, V>) => void;
|
|
534
|
+
/** A participant connected. */
|
|
535
|
+
joined?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
536
|
+
/** The participant. */
|
|
537
|
+
player: Player<C, PM>;
|
|
538
|
+
}>;
|
|
539
|
+
/** A participant disconnected. */
|
|
540
|
+
left?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
541
|
+
/** The participant. */
|
|
542
|
+
player: Player<C, PM>;
|
|
543
|
+
}>;
|
|
544
|
+
/** One event per tick in which the player moved. */
|
|
545
|
+
move?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
546
|
+
/** The participant. */
|
|
547
|
+
player: Player<C, PM>;
|
|
548
|
+
/** The position before. */
|
|
549
|
+
from: Vec3;
|
|
550
|
+
/** The position now. */
|
|
551
|
+
to: Vec3;
|
|
552
|
+
}>;
|
|
553
|
+
/** A participant took damage. `attacker` is set when another participant caused it. */
|
|
554
|
+
damaged?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
555
|
+
/** The participant. */
|
|
556
|
+
player: Player<C, PM>;
|
|
557
|
+
/** The damage, in half-hearts. */
|
|
558
|
+
amount: number;
|
|
559
|
+
/** The damage cause, as the server names it. */
|
|
560
|
+
cause: string;
|
|
561
|
+
/** The participant who caused it, when one did. */
|
|
562
|
+
attacker?: Player<C, PM> | undefined;
|
|
563
|
+
}>;
|
|
564
|
+
/** A participant died. `killer` is set when another participant caused it. */
|
|
565
|
+
died?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
566
|
+
/** The participant. */
|
|
567
|
+
player: Player<C, PM>;
|
|
568
|
+
/** The death cause, as the server names it. */
|
|
569
|
+
cause: string;
|
|
570
|
+
/** The participant who caused it, when one did. */
|
|
571
|
+
killer?: Player<C, PM> | undefined;
|
|
572
|
+
}>;
|
|
573
|
+
/** A participant respawned (`life: "respawn"`). */
|
|
574
|
+
respawned?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
575
|
+
/** The participant. */
|
|
576
|
+
player: Player<C, PM>;
|
|
577
|
+
}>;
|
|
578
|
+
/** A non-player entity appeared in the mirror. */
|
|
579
|
+
entitySpawned?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
580
|
+
/** The entity, as observed. */
|
|
581
|
+
entity: DeepReadonly<Entity>;
|
|
582
|
+
}>;
|
|
583
|
+
/** An entity's reported fields changed (health, tags, item, equipment). */
|
|
584
|
+
entityChanged?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
585
|
+
/** The entity now. */
|
|
586
|
+
entity: DeepReadonly<Entity>;
|
|
587
|
+
/** The entity before the change. */
|
|
588
|
+
was: DeepReadonly<Entity>;
|
|
589
|
+
}>;
|
|
590
|
+
/** A non-player entity left the mirror: it died, or the server removed it. */
|
|
591
|
+
entityRemoved?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
592
|
+
/** The entity's last observation. */
|
|
593
|
+
entity: DeepReadonly<Entity>;
|
|
594
|
+
/** Why: it died, or the server removed it. */
|
|
595
|
+
cause: "died" | "removed";
|
|
596
|
+
/** The participant who killed it, when one did. */
|
|
597
|
+
killer?: Player<C, PM> | undefined;
|
|
598
|
+
}>;
|
|
599
|
+
/** A participant picked an item up from the ground. */
|
|
600
|
+
pickup?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
601
|
+
/** The participant. */
|
|
602
|
+
player: Player<C, PM>;
|
|
603
|
+
/** The item id. */
|
|
604
|
+
item: ItemId;
|
|
605
|
+
/** How many. */
|
|
606
|
+
count: number;
|
|
607
|
+
/** Where the item was. */
|
|
608
|
+
at: Vec3;
|
|
609
|
+
}>;
|
|
610
|
+
/** A participant's count of one item changed. */
|
|
611
|
+
inventory?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
612
|
+
/** The participant. */
|
|
613
|
+
player: Player<C, PM>;
|
|
614
|
+
/** The item id. */
|
|
615
|
+
item: ItemId;
|
|
616
|
+
/** The count now. */
|
|
617
|
+
count: number;
|
|
618
|
+
}>;
|
|
619
|
+
/** A participant broke a block. The mirror already shows the block after the break. */
|
|
620
|
+
blockBroken?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
621
|
+
/** The participant. */
|
|
622
|
+
player: Player<C, PM>;
|
|
623
|
+
/** The block position. */
|
|
624
|
+
at: Vec3;
|
|
625
|
+
/** The block state that was there. */
|
|
626
|
+
block: BlockState;
|
|
627
|
+
}>;
|
|
628
|
+
/** A participant placed a block. The mirror already shows the placed state. */
|
|
629
|
+
blockPlaced?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
630
|
+
/** The participant. */
|
|
631
|
+
player: Player<C, PM>;
|
|
632
|
+
/** The block position. */
|
|
633
|
+
at: Vec3;
|
|
634
|
+
/** The placed block state. */
|
|
635
|
+
block: BlockState;
|
|
636
|
+
}>;
|
|
637
|
+
/** A participant right-clicked a block; `item` is what the hand held. */
|
|
638
|
+
blockUsed?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
639
|
+
/** The participant. */
|
|
640
|
+
player: Player<C, PM>;
|
|
641
|
+
/** The block position. */
|
|
642
|
+
at: Vec3;
|
|
643
|
+
/** The block state. */
|
|
644
|
+
block: BlockState;
|
|
645
|
+
/** The item in the hand, when one. */
|
|
646
|
+
item?: ItemId | undefined;
|
|
647
|
+
}>;
|
|
648
|
+
/** A container's contents changed: the counts after the change and the deltas. `by` only when the engine is certain. */
|
|
649
|
+
container?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
650
|
+
/** The container block. */
|
|
651
|
+
at: Vec3;
|
|
652
|
+
/** The container's block id. */
|
|
653
|
+
block: BlockId;
|
|
654
|
+
/** The counts per item id after the change. */
|
|
655
|
+
items: Readonly<Record<string, number>>;
|
|
656
|
+
/** The count deltas, per item. */
|
|
657
|
+
changes: Array<{
|
|
658
|
+
/** The item id. */
|
|
659
|
+
item: ItemId;
|
|
660
|
+
/** The count change; positive for items added. */
|
|
661
|
+
delta: number;
|
|
662
|
+
}>;
|
|
663
|
+
/** The participant who moved the items, when the engine is certain. */
|
|
664
|
+
by?: Player<C, PM> | undefined;
|
|
665
|
+
/** `true` when the container block is gone. */
|
|
666
|
+
removed: boolean;
|
|
667
|
+
}>;
|
|
668
|
+
/**
|
|
669
|
+
* Every block that changed this tick, whatever the cause. A fill is one entry with its `box`.
|
|
670
|
+
* The mirror already shows the new states.
|
|
671
|
+
*/
|
|
672
|
+
blocksChanged?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
673
|
+
/** The changes: one per block, or one per fill. */
|
|
674
|
+
changes: Array<(Vec3 & {
|
|
675
|
+
/** The block id now. */
|
|
676
|
+
block: BlockId;
|
|
677
|
+
/** The block id before, when known. */
|
|
678
|
+
was?: BlockId | undefined;
|
|
679
|
+
/** What changed it (break, place, burn, ...). */
|
|
680
|
+
cause?: string | undefined;
|
|
681
|
+
/** The participant who did it, when one did. */
|
|
682
|
+
by?: Player<C, PM> | undefined;
|
|
683
|
+
}) | {
|
|
684
|
+
/** The filled box. */
|
|
685
|
+
box: Box;
|
|
686
|
+
/** The block id now. */
|
|
687
|
+
block: BlockId;
|
|
688
|
+
cause: "fill";
|
|
689
|
+
}>;
|
|
690
|
+
}>;
|
|
691
|
+
/** A participant's feet entered a declared zone. */
|
|
692
|
+
zoneEntered?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
693
|
+
/** The participant. */
|
|
694
|
+
player: Player<C, PM>;
|
|
695
|
+
/** The zone name. */
|
|
696
|
+
zone: Z;
|
|
697
|
+
}>;
|
|
698
|
+
/** A participant's feet left a declared zone. */
|
|
699
|
+
zoneLeft?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
700
|
+
/** The participant. */
|
|
701
|
+
player: Player<C, PM>;
|
|
702
|
+
/** The zone name. */
|
|
703
|
+
zone: Z;
|
|
704
|
+
}>;
|
|
705
|
+
/** A participant sent a public chat line. */
|
|
706
|
+
chat?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
707
|
+
/** The participant. */
|
|
708
|
+
player: Player<C, PM>;
|
|
709
|
+
/** The line. */
|
|
710
|
+
text: string;
|
|
711
|
+
}>;
|
|
712
|
+
/** A participant swung an arm: a mining or attack attempt (`swing`), or the off hand (`swing_off`). */
|
|
713
|
+
animation?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
714
|
+
/** The participant. */
|
|
715
|
+
player: Player<C, PM>;
|
|
716
|
+
/** The arm: the main hand, or the off hand. */
|
|
717
|
+
animation: "swing" | "swing_off";
|
|
718
|
+
}>;
|
|
719
|
+
/** A participant's game mode changed. */
|
|
720
|
+
gamemode?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
721
|
+
/** The participant. */
|
|
722
|
+
player: Player<C, PM>;
|
|
723
|
+
/** The game mode now. */
|
|
724
|
+
gamemode: GameMode;
|
|
725
|
+
}>;
|
|
726
|
+
/** Accepted votes only. The SDK answered the rejected ones. */
|
|
727
|
+
vote?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
728
|
+
[Id in VoteIdOf<V>]: {
|
|
729
|
+
/** The voter. */
|
|
730
|
+
player: Player<C, PM>;
|
|
731
|
+
/** The vote. */
|
|
732
|
+
voteId: Id;
|
|
733
|
+
/** The accepted option. */
|
|
734
|
+
option: OptionFor<V, Id>;
|
|
735
|
+
};
|
|
736
|
+
}[VoteIdOf<V>]>;
|
|
737
|
+
/** A walk requested with `g.walk` ended. `ok` is `false` when the bot did not arrive. */
|
|
738
|
+
walkDone?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
739
|
+
/** The participant. */
|
|
740
|
+
player: Player<C, PM>;
|
|
741
|
+
/** `true` when the bot arrived. */
|
|
742
|
+
ok: boolean;
|
|
743
|
+
}>;
|
|
744
|
+
/** A timer scheduled with `g.schedule` is due. */
|
|
745
|
+
timer?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
746
|
+
/** The timer name. */
|
|
747
|
+
name: T;
|
|
748
|
+
}>;
|
|
749
|
+
/** A named batch ran. `failed` lists the effects the engine refused. */
|
|
750
|
+
batchDone?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
751
|
+
/** The batch name. */
|
|
752
|
+
name: string;
|
|
753
|
+
/** Whether every effect applied. */
|
|
754
|
+
ok: boolean;
|
|
755
|
+
/** The effects the engine refused. */
|
|
756
|
+
failed: Array<{
|
|
757
|
+
/** The effect's index in the batch. */
|
|
758
|
+
index: number;
|
|
759
|
+
/** Why it failed. */
|
|
760
|
+
detail: string;
|
|
761
|
+
}>;
|
|
762
|
+
}>;
|
|
763
|
+
/** The engine refused one effect. `effect` echoes it; `batch` names the batch when it is still pending. */
|
|
764
|
+
effectFailed?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
765
|
+
/** The effect kind. */
|
|
766
|
+
kind: string;
|
|
767
|
+
/** Why. */
|
|
768
|
+
detail: string;
|
|
769
|
+
/** The refused effect, as sent. */
|
|
770
|
+
effect?: unknown;
|
|
771
|
+
/** The batch it was in, when that batch is still pending. */
|
|
772
|
+
batch?: {
|
|
773
|
+
/** The batch name. */
|
|
774
|
+
name: string;
|
|
775
|
+
/** The effect's index in the batch. */
|
|
776
|
+
index: number;
|
|
777
|
+
} | undefined;
|
|
778
|
+
}>;
|
|
779
|
+
/** A server event the engine forwards by name, with its JSON data. */
|
|
780
|
+
custom?: H<Ctx<C, G, PM, Ph, Z, T, V>, {
|
|
781
|
+
/** The event name. */
|
|
782
|
+
name: string;
|
|
783
|
+
/** The participant it is about, when one. */
|
|
784
|
+
player?: Player<C, PM> | undefined;
|
|
785
|
+
/** The event's JSON data. */
|
|
786
|
+
data: Json;
|
|
787
|
+
}>;
|
|
788
|
+
}
|
|
789
|
+
/** One timed step of a phase: at `elapsed` since entry, or `remaining` before its end (needs a `duration`). */
|
|
790
|
+
export type PhaseStep<Cx> =
|
|
791
|
+
/** Runs once, `elapsed` after the phase entry. */
|
|
792
|
+
{
|
|
793
|
+
/** How long after the phase entry. */
|
|
794
|
+
elapsed: Duration;
|
|
795
|
+
/** Not with `elapsed`. */
|
|
796
|
+
remaining?: never;
|
|
797
|
+
/** What runs. */
|
|
798
|
+
run: (g: Cx) => void;
|
|
799
|
+
}
|
|
800
|
+
/** Runs once, `remaining` before the phase's timeout (the phase needs a `duration`). */
|
|
801
|
+
| {
|
|
802
|
+
/** How long before the phase's timeout. */
|
|
803
|
+
remaining: Duration;
|
|
804
|
+
/** Not with `remaining`. */
|
|
805
|
+
elapsed?: never;
|
|
806
|
+
/** What runs. */
|
|
807
|
+
run: (g: Cx) => void;
|
|
808
|
+
};
|
|
809
|
+
/** The behavior of one declared phase: how long it lasts, where it goes, and its handlers. */
|
|
810
|
+
export interface PhaseSpec<C extends ChallengeConfig, G, PM extends PlayerMap<C>, Ph extends string, Z extends string, T extends string, V extends VotesDecl> {
|
|
811
|
+
/**
|
|
812
|
+
* The phase's length. Without it and without `until`, the phase lasts one tick (a pass-through).
|
|
813
|
+
* A function is evaluated once, when the phase is entered.
|
|
814
|
+
*/
|
|
815
|
+
duration?: Duration | ((g: ReadCtx<C, G, PM, Ph, Z, T, V>) => Duration);
|
|
816
|
+
/** Where the phase goes when it ends: a phase name, `"END"`, or a function of the read-only context. */
|
|
817
|
+
next: Ph | "END" | ((g: ReadCtx<C, G, PM, Ph, Z, T, V>) => Ph | "END");
|
|
818
|
+
/** The phase ends as soon as this holds. Evaluated after the handlers of every event. */
|
|
819
|
+
until?: (g: ReadCtx<C, G, PM, Ph, Z, T, V>) => boolean;
|
|
820
|
+
/** Runs when the phase is entered, after `duration` is resolved. */
|
|
821
|
+
enter?: (g: Ctx<C, G, PM, Ph, Z, T, V>) => void;
|
|
822
|
+
/** Runs when the phase is left, also when the game ends. No phase or end request inside it. */
|
|
823
|
+
exit?: (g: Ctx<C, G, PM, Ph, Z, T, V>) => void;
|
|
824
|
+
/** Once per phase entry. */
|
|
825
|
+
at?: Array<PhaseStep<Ctx<C, G, PM, Ph, Z, T, V>>>;
|
|
826
|
+
/** Repeating steps, counted from the phase entry. */
|
|
827
|
+
every?: Array<{
|
|
828
|
+
/** The time between two runs. */
|
|
829
|
+
interval: Duration;
|
|
830
|
+
/** What runs. */
|
|
831
|
+
run: (g: Ctx<C, G, PM, Ph, Z, T, V>) => void;
|
|
832
|
+
}>;
|
|
833
|
+
/** Handlers that run only in this phase, after the global `on` for the same event. */
|
|
834
|
+
on?: Handlers<C, G, PM, Ph, Z, T, V>;
|
|
835
|
+
}
|
|
836
|
+
/** How the game ends and who wins. The SDK computes all of it after every reduction; nothing here has effects. */
|
|
837
|
+
export interface Outcome<C extends ChallengeConfig, G, PM extends PlayerMap<C>, Ph extends string, Z extends string, T extends string, V extends VotesDecl> {
|
|
838
|
+
/**
|
|
839
|
+
* Derived scores: computed after every reduction, not only at the end. When present, `setScore`
|
|
840
|
+
* and `addScore` are refused. Pure: every evaluation reads the previous committed scores.
|
|
841
|
+
*/
|
|
842
|
+
scores?: (g: ReadCtx<C, G, PM, Ph, Z, T, V>, player: ReadPlayer<C, PM>) => number;
|
|
843
|
+
/** The end rule, checked after every reduction. The game duration ends the game on its own. */
|
|
844
|
+
endWhen?: (g: ReadCtx<C, G, PM, Ph, Z, T, V>) => boolean;
|
|
845
|
+
/** The winners at the end. Default: no winner. */
|
|
846
|
+
winners?: (g: ReadCtx<C, G, PM, Ph, Z, T, V>) => ReadonlyArray<{
|
|
847
|
+
/** The player's name. */
|
|
848
|
+
name: string;
|
|
849
|
+
}>;
|
|
850
|
+
/** The end state at the end. Default: `""`. */
|
|
851
|
+
endState?: (g: ReadCtx<C, G, PM, Ph, Z, T, V>) => EndStateOf<C>;
|
|
852
|
+
}
|
|
853
|
+
/** World setup the SDK applies before the game. Every field but `spawn` has today's default; `false` skips all of it. */
|
|
854
|
+
export interface World {
|
|
855
|
+
/** The world spawn: participants respawn there, the watcher and spectators are moved there. */
|
|
856
|
+
spawn?: Vec3;
|
|
857
|
+
/** The time of day set before the game. `false` leaves the world's time. Default `"day"`. */
|
|
858
|
+
time?: "day" | "noon" | "night" | "midnight" | false;
|
|
859
|
+
/** Whether the time of day advances. Default `false`. */
|
|
860
|
+
daylightCycle?: boolean;
|
|
861
|
+
/** Whether mobs spawn on their own. Default `false`. */
|
|
862
|
+
mobSpawning?: boolean;
|
|
863
|
+
/** Whether health regenerates from food. Default `true`. */
|
|
864
|
+
naturalRegeneration?: boolean;
|
|
865
|
+
/** The participants' maximum health, in half-hearts. `false` leaves the attribute. Default 20. */
|
|
866
|
+
maxHealth?: number | false;
|
|
867
|
+
}
|
|
868
|
+
/** The values `world` uses for the fields a declaration omits. */
|
|
869
|
+
export declare const WORLD_DEFAULTS: Required<Omit<World, "spawn">>;
|
|
870
|
+
/** The state initializers. `player` is the state of every role; `players` overrides it per role. */
|
|
871
|
+
export interface StateDecl<C extends ChallengeConfig, G, P, PS> {
|
|
872
|
+
/** The shared data. Default: `{}`. */
|
|
873
|
+
game?: (init: Init<C>) => G;
|
|
874
|
+
/** The state of every participant, unless `players` names the role. Default: `{}`. */
|
|
875
|
+
player?: (player: ObservedPlayer<C>, init: Init<C>) => P;
|
|
876
|
+
/** The state per role; a role listed here does not use `player`. */
|
|
877
|
+
players?: {
|
|
878
|
+
[R in keyof PS]: (player: ObservedPlayer<C, R & RoleOf<C>>, init: Init<C>) => PS[R];
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* The root declaration of `defineChallenge(config, decl)`: data, the state initializers and pure
|
|
883
|
+
* predicates. No event handler lives here; the steps carry them.
|
|
884
|
+
*/
|
|
885
|
+
export interface Decl<C extends ChallengeConfig, G, P, PS, Ph extends string, Z extends string, T extends string, V extends VotesDecl> {
|
|
886
|
+
/** The state initializers. They run once, at the end of CONNECTING, with the observed roster. */
|
|
887
|
+
state?: StateDecl<C, G, P, PS>;
|
|
888
|
+
/** "single": one death eliminates (spectator). "respawn": deaths count, the player plays on. */
|
|
889
|
+
life: "single" | "respawn";
|
|
890
|
+
/** Time from game start to the end. Omit for no time limit. */
|
|
891
|
+
duration?: Duration;
|
|
892
|
+
/** World setup before the game (time, game rules, spawn). `false` skips it; omit for the defaults. */
|
|
893
|
+
world?: World | false;
|
|
894
|
+
/** Named boxes, evaluated every tick from the players' positions: `on.zoneEntered/zoneLeft`, `g.inZone`, `player.zones`. */
|
|
895
|
+
zones?: Record<Z, Box>;
|
|
896
|
+
/** Timer names for `g.schedule` / `on.timer`. */
|
|
897
|
+
timers?: readonly T[];
|
|
898
|
+
/** Tagged entities to read from the world before the game (`g.markers`). Chunks in `within` are loaded first. */
|
|
899
|
+
markers?: {
|
|
900
|
+
/** The scoreboard tags that mark an entity, matched exactly; a tag that ends with `*` is a prefix (`kradle.island.*` matches `kradle.island.north`). */
|
|
901
|
+
tags: string[];
|
|
902
|
+
/** The box the engine loads first, so far markers are found too. */
|
|
903
|
+
within?: Box;
|
|
904
|
+
};
|
|
905
|
+
/** The votes, with their options. The `player` vote declares none. */
|
|
906
|
+
votes?: V & {
|
|
907
|
+
[Id in keyof V]: VoteRule<ReadCtx<C, G, ResolvePlayers<C, P & object, PS>, Ph, Z, T, V>, ReadPlayer<C, ResolvePlayers<C, P & object, PS>>, OptionFor<V, Id>>;
|
|
908
|
+
};
|
|
909
|
+
/** The phase names. Each gets its behavior in a `.phase(name, {...})` step. */
|
|
910
|
+
phases?: readonly Ph[];
|
|
911
|
+
/** The phase the game starts in. Default: the first name of `phases`. */
|
|
912
|
+
initialPhase?: NoInfer<Ph>;
|
|
913
|
+
}
|
|
914
|
+
/** A callback registered from a rule: `(g, ...) => void`. */
|
|
915
|
+
export type Step<Cx> = (g: Cx) => void;
|
|
916
|
+
/** The context types of a challenge, for helpers: `type G = CtxOf<typeof challenge>`. */
|
|
917
|
+
export type CtxOf<Ch> = Ch extends ChallengeApi<infer C, infer G, infer PM, infer Ph, infer Z, infer T, infer V> ? Ctx<C, G, PM, Ph, Z, T, V> : never;
|
|
918
|
+
/** The read-only context of a challenge, for helpers used in pure callbacks. */
|
|
919
|
+
export type ReadCtxOf<Ch> = Ch extends ChallengeApi<infer C, infer G, infer PM, infer Ph, infer Z, infer T, infer V> ? ReadCtx<C, G, PM, Ph, Z, T, V> : never;
|
|
920
|
+
/** The setup context of a challenge, for helpers used in `.setup()`. */
|
|
921
|
+
export type SetupCtxOf<Ch> = Ch extends ChallengeApi<infer C, infer G, infer PM, infer Ph, infer Z, infer T, infer V> ? SetupCtx<C, G, PM, Ph, Z, T, V> : never;
|
|
922
|
+
/** The notification context of a challenge, for helpers used in `.onOver()`. */
|
|
923
|
+
export type NotifyCtxOf<Ch> = Ch extends ChallengeApi<infer C, infer G, infer PM, infer Ph, infer Z, infer T, infer V> ? NotifyCtx<C, G, PM, Ph, Z, T, V> : never;
|
|
924
|
+
/**
|
|
925
|
+
* The challenge object `defineChallenge` returns. Every step registers behavior and returns the
|
|
926
|
+
* same object, so a definition reads as one chain or as separate statements. `init()` locks it.
|
|
927
|
+
*/
|
|
928
|
+
export interface ChallengeApi<C extends ChallengeConfig, G, PM extends PlayerMap<C>, Ph extends string, Z extends string, T extends string, V extends VotesDecl> {
|
|
929
|
+
/** The configuration the challenge was defined with. */
|
|
930
|
+
readonly config: C;
|
|
931
|
+
/** The declared zones, for tests. */
|
|
932
|
+
readonly zones: Readonly<Record<string, Box>>;
|
|
933
|
+
/** World build. Its effects go out as batches; the game starts after the engine confirms them all. */
|
|
934
|
+
setup(fn: Step<SetupCtx<C, G, PM, Ph, Z, T, V>>): this;
|
|
935
|
+
/** Runs on the first tick of PLAYING, after the setup batches are confirmed. */
|
|
936
|
+
start(fn: Step<Ctx<C, G, PM, Ph, Z, T, V>>): this;
|
|
937
|
+
/** The behavior of a declared phase. One step per phase name. */
|
|
938
|
+
phase(name: Ph, spec: PhaseSpec<C, G, PM, Ph, Z, T, V>): this;
|
|
939
|
+
/** One-shot steps after game start, `>=` semantics. */
|
|
940
|
+
at(steps: Array<{
|
|
941
|
+
/** When, after the game start. */
|
|
942
|
+
at: Duration;
|
|
943
|
+
/** What runs. */
|
|
944
|
+
run: Step<Ctx<C, G, PM, Ph, Z, T, V>>;
|
|
945
|
+
}>): this;
|
|
946
|
+
/** Repeating steps. After a gap they run once, not once per missed interval. */
|
|
947
|
+
every(steps: Array<{
|
|
948
|
+
/** The time between two runs. */
|
|
949
|
+
interval: Duration;
|
|
950
|
+
/** What runs. */
|
|
951
|
+
run: Step<Ctx<C, G, PM, Ph, Z, T, V>>;
|
|
952
|
+
}>): this;
|
|
953
|
+
/** Event handlers. Several calls add up, in call order. */
|
|
954
|
+
on(handlers: Handlers<C, G, PM, Ph, Z, T, V>): this;
|
|
955
|
+
/** The result rules. Required: `init()` refuses a challenge without it. */
|
|
956
|
+
outcome(outcome: Outcome<C, G, PM, Ph, Z, T, V>): this;
|
|
957
|
+
/** Runs once the result is frozen: messages and titles, no state writes. */
|
|
958
|
+
onOver(fn: (g: NotifyCtx<C, G, PM, Ph, Z, T, V>, result: Result<C>) => void): this;
|
|
959
|
+
/** An object sent with `game_over` for the LLM judge; the backend takes an object, not another JSON value. */
|
|
960
|
+
judgeContext(fn: (g: ReadCtx<C, G, PM, Ph, Z, T, V>, result: Result<C>) => Record<string, Json>): this;
|
|
961
|
+
}
|
|
962
|
+
export {};
|
|
963
|
+
//# sourceMappingURL=types.d.ts.map
|