@kradle/challenges-sdk 0.25.0 → 0.26.1

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.
@@ -6,6 +6,7 @@ exports.parseBlockState = parseBlockState;
6
6
  exports.blockSpecString = blockSpecString;
7
7
  exports.splitBatches = splitBatches;
8
8
  exports.createReducer = createReducer;
9
+ const commands_js_1 = require("./commands.js");
9
10
  const dimensions_js_1 = require("./dimensions.js");
10
11
  // The reducer: one locked definition, one `init()` per run, one `reduce(state,
11
12
  // event)` per engine event. Everything a rule sees comes through a context
@@ -375,6 +376,7 @@ function createReducer(def) {
375
376
  every: {},
376
377
  timers: {},
377
378
  nextGen: 0,
379
+ commandAccess: {},
378
380
  players: {},
379
381
  game: {},
380
382
  votes: Object.fromEntries(voteIds.map((id) => [id, { open: false, round: 0, cast: {} }])),
@@ -1887,6 +1889,10 @@ function createReducer(def) {
1887
1889
  return;
1888
1890
  }
1889
1891
  const missing = missingCapabilities(e.capabilities);
1892
+ if (def.customCommands.length)
1893
+ for (const c of ["custom_commands_v1", "agent_commands_v1"])
1894
+ if (!e.capabilities.includes(c))
1895
+ missing.push(c);
1890
1896
  if (dimensions.length > 1 && !e.capabilities.includes("dimensions"))
1891
1897
  missing.push("dimensions");
1892
1898
  if (def.setup && !e.capabilities.includes("batch"))
@@ -2349,6 +2355,9 @@ function createReducer(def) {
2349
2355
  });
2350
2356
  break;
2351
2357
  }
2358
+ case "challenge_command":
2359
+ customCommand(r, e);
2360
+ break;
2352
2361
  case "custom": {
2353
2362
  if (!playing())
2354
2363
  return;
@@ -2357,6 +2366,67 @@ function createReducer(def) {
2357
2366
  }
2358
2367
  }
2359
2368
  }
2369
+ function commandAvailable(r, command, player) {
2370
+ return (r.s.status === "PLAYING" &&
2371
+ player.connected &&
2372
+ (!command.available ||
2373
+ run(r, "pure", command.available, ctxOf(r, "read"), playerView(r, player, false)) === true));
2374
+ }
2375
+ function publishCommandAccess(r) {
2376
+ if (!def.customCommands.length)
2377
+ return;
2378
+ const access = {};
2379
+ if (r.s.status === "PLAYING")
2380
+ for (const p of Object.values(r.s.players)) {
2381
+ if (p.connected)
2382
+ access[p.name] = def.customCommands.filter((c) => commandAvailable(r, c, p)).map((c) => c.schema.name);
2383
+ }
2384
+ if (JSON.stringify(access) !== JSON.stringify(r.s.commandAccess)) {
2385
+ r.s.commandAccess = access;
2386
+ r.actions.push({ type: "command_access", players: snapshot(access) });
2387
+ }
2388
+ }
2389
+ function customCommand(r, e) {
2390
+ const reject = (reason) => r.actions.push({ type: "command_decision", requestId: e.requestId, origin: e.origin, accepted: false, reason });
2391
+ const p = r.s.players[e.player];
2392
+ const command = def.customCommands.find((c) => c.schema.name === e.command);
2393
+ if (!p || (e.uuid !== undefined && e.uuid !== p.uuid) || !command || !commandAvailable(r, command, p)) {
2394
+ reject("The command is not available.");
2395
+ return;
2396
+ }
2397
+ if (!e.args ||
2398
+ typeof e.args !== "object" ||
2399
+ Array.isArray(e.args) ||
2400
+ Object.keys(e.args).some((k) => !command.schema.args.some((a) => a.name === k))) {
2401
+ reject("Invalid command arguments.");
2402
+ return;
2403
+ }
2404
+ const args = {};
2405
+ for (const a of command.schema.args) {
2406
+ const value = Object.hasOwn(e.args, a.name) ? e.args[a.name] : a.default;
2407
+ if (value === undefined && a.optional) {
2408
+ args[a.name] = undefined;
2409
+ continue;
2410
+ }
2411
+ if (!(0, commands_js_1.validCommandValue)(a, value)) {
2412
+ reject("Invalid command arguments.");
2413
+ return;
2414
+ }
2415
+ if (a.type === "player") {
2416
+ const target = r.s.players[value];
2417
+ if (!target?.connected) {
2418
+ reject("Select a connected participant.");
2419
+ return;
2420
+ }
2421
+ args[a.name] = viewOf(r, target);
2422
+ }
2423
+ else
2424
+ args[a.name] = value;
2425
+ }
2426
+ run(r, "handler", command.run, ctxOf(r, "full"), { player: viewOf(r, p), args: Object.freeze(args) });
2427
+ resolvePhaseRequest(r);
2428
+ r.actions.push({ type: "command_decision", requestId: e.requestId, origin: e.origin, accepted: true });
2429
+ }
2360
2430
  function voteAttempt(r, e) {
2361
2431
  const s = r.s;
2362
2432
  const p = s.players[e.name];
@@ -2450,6 +2520,7 @@ function createReducer(def) {
2450
2520
  checkpoint(r);
2451
2521
  if (r.req.end)
2452
2522
  finalize(r);
2523
+ publishCommandAccess(r);
2453
2524
  assertJson({ game: r.s.game, players: Object.fromEntries(Object.values(r.s.players).map((p) => [p.name, p.state])) }, "state");
2454
2525
  }
2455
2526
  catch (err) {
@@ -2459,6 +2530,10 @@ function createReducer(def) {
2459
2530
  r = { ...r, s: clone(input), actions: [], batch: null, req: { phase: null, end: null, budget: 0 } };
2460
2531
  if (event.type === "tick")
2461
2532
  r.s.tick = event.serverTick;
2533
+ if (def.customCommands.length) {
2534
+ r.s.commandAccess = {};
2535
+ r.actions.push({ type: "command_access", players: {} });
2536
+ }
2462
2537
  const detail = err instanceof errors_js_1.ChallengeError ? err.toJSON() : { message: String(err?.message ?? err) };
2463
2538
  failRun(r.s, r.actions, "error_challenge", `challenge error on ${event.type}: ${err.message}`, {
2464
2539
  event: event,