@kradle/challenges-sdk 0.25.0 → 0.26.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.
@@ -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,66 @@ 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
+ r.actions.push({ type: "command_decision", requestId: e.requestId, origin: e.origin, accepted: true });
2428
+ }
2360
2429
  function voteAttempt(r, e) {
2361
2430
  const s = r.s;
2362
2431
  const p = s.players[e.name];
@@ -2450,6 +2519,7 @@ function createReducer(def) {
2450
2519
  checkpoint(r);
2451
2520
  if (r.req.end)
2452
2521
  finalize(r);
2522
+ publishCommandAccess(r);
2453
2523
  assertJson({ game: r.s.game, players: Object.fromEntries(Object.values(r.s.players).map((p) => [p.name, p.state])) }, "state");
2454
2524
  }
2455
2525
  catch (err) {
@@ -2459,6 +2529,10 @@ function createReducer(def) {
2459
2529
  r = { ...r, s: clone(input), actions: [], batch: null, req: { phase: null, end: null, budget: 0 } };
2460
2530
  if (event.type === "tick")
2461
2531
  r.s.tick = event.serverTick;
2532
+ if (def.customCommands.length) {
2533
+ r.s.commandAccess = {};
2534
+ r.actions.push({ type: "command_access", players: {} });
2535
+ }
2462
2536
  const detail = err instanceof errors_js_1.ChallengeError ? err.toJSON() : { message: String(err?.message ?? err) };
2463
2537
  failRun(r.s, r.actions, "error_challenge", `challenge error on ${event.type}: ${err.message}`, {
2464
2538
  event: event,