@crowdedkingdoms/crowdyjs 18.10.0 → 18.12.0-dev.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.
- package/MIGRATION.md +69 -0
- package/README.md +91 -30
- package/dist/crowdy-client.d.ts +11 -3
- package/dist/crowdy-client.d.ts.map +1 -1
- package/dist/crowdy-client.js +25 -4
- package/dist/default-origin.d.ts +6 -6
- package/dist/default-origin.d.ts.map +1 -1
- package/dist/default-origin.js +6 -6
- package/dist/domains/exec.d.ts +6 -0
- package/dist/domains/exec.d.ts.map +1 -1
- package/dist/domains/exec.js +28 -2
- package/dist/generated/graphql-schema.d.ts +1 -1
- package/dist/generated/graphql-schema.d.ts.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/kit/index.d.ts +22 -2
- package/dist/kit/index.d.ts.map +1 -1
- package/dist/kit/index.js +22 -2
- package/dist/kit/kit.d.ts +141 -7
- package/dist/kit/kit.d.ts.map +1 -1
- package/dist/kit/kit.js +138 -4
- package/dist/kit/mirror.d.ts +111 -0
- package/dist/kit/mirror.d.ts.map +1 -0
- package/dist/kit/mirror.js +295 -0
- package/dist/kit/modules/arenas.d.ts +87 -0
- package/dist/kit/modules/arenas.d.ts.map +1 -0
- package/dist/kit/modules/arenas.js +66 -0
- package/dist/kit/modules/encounters.d.ts +75 -0
- package/dist/kit/modules/encounters.d.ts.map +1 -0
- package/dist/kit/modules/encounters.js +57 -0
- package/dist/kit/modules/instances.d.ts +78 -0
- package/dist/kit/modules/instances.d.ts.map +1 -0
- package/dist/kit/modules/instances.js +72 -0
- package/dist/kit/modules/leaderboards.d.ts +79 -0
- package/dist/kit/modules/leaderboards.d.ts.map +1 -0
- package/dist/kit/modules/leaderboards.js +69 -0
- package/dist/kit/modules/matchmaking.d.ts +107 -0
- package/dist/kit/modules/matchmaking.d.ts.map +1 -0
- package/dist/kit/modules/matchmaking.js +109 -0
- package/dist/kit/modules/minigames.d.ts +59 -0
- package/dist/kit/modules/minigames.d.ts.map +1 -0
- package/dist/kit/modules/minigames.js +55 -0
- package/dist/kit/modules/npcs.d.ts +80 -0
- package/dist/kit/modules/npcs.d.ts.map +1 -0
- package/dist/kit/modules/npcs.js +102 -0
- package/dist/kit/modules/presence.d.ts +81 -0
- package/dist/kit/modules/presence.d.ts.map +1 -0
- package/dist/kit/modules/presence.js +99 -0
- package/dist/kit/modules/racing.d.ts +131 -0
- package/dist/kit/modules/racing.d.ts.map +1 -0
- package/dist/kit/modules/racing.js +113 -0
- package/dist/kit/modules/sessions.d.ts +91 -0
- package/dist/kit/modules/sessions.d.ts.map +1 -0
- package/dist/kit/modules/sessions.js +84 -0
- package/dist/kit/modules/tables.d.ts +76 -0
- package/dist/kit/modules/tables.d.ts.map +1 -0
- package/dist/kit/modules/tables.js +73 -0
- package/dist/kit/modules/tactics.d.ts +79 -0
- package/dist/kit/modules/tactics.d.ts.map +1 -0
- package/dist/kit/modules/tactics.js +67 -0
- package/dist/kit/modules/territory.d.ts +92 -0
- package/dist/kit/modules/territory.d.ts.map +1 -0
- package/dist/kit/modules/territory.js +84 -0
- package/dist/kit/modules/world.d.ts +69 -0
- package/dist/kit/modules/world.d.ts.map +1 -0
- package/dist/kit/modules/world.js +35 -0
- package/dist/kit/protocol.d.ts +128 -0
- package/dist/kit/protocol.d.ts.map +1 -0
- package/dist/kit/protocol.js +171 -0
- package/dist/kit/realtime.d.ts +72 -0
- package/dist/kit/realtime.d.ts.map +1 -0
- package/dist/kit/realtime.js +108 -0
- package/dist/kit/runtime.d.ts +90 -0
- package/dist/kit/runtime.d.ts.map +1 -0
- package/dist/kit/runtime.js +194 -0
- package/dist/kit/store.d.ts +20 -0
- package/dist/kit/store.d.ts.map +1 -0
- package/dist/kit/store.js +25 -0
- package/dist/kit/topology.d.ts +69 -0
- package/dist/kit/topology.d.ts.map +1 -0
- package/dist/kit/topology.js +156 -0
- package/dist/kit/wire.d.ts +45 -5
- package/dist/kit/wire.d.ts.map +1 -1
- package/dist/kit/wire.js +51 -5
- package/dist/live-coding/assets/browser-authoring-index.json +1 -1
- package/dist/live-coding/assets/manifest.json +1 -1
- package/dist/live-coding/browser-authoring-index.generated.d.ts.map +1 -1
- package/dist/live-coding/browser-authoring-index.generated.js +5092 -45
- package/package.json +1 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { HubState } from '../mirror.js';
|
|
2
|
+
import type { KitRuntime } from '../runtime.js';
|
|
3
|
+
export interface TerritoryPoint {
|
|
4
|
+
id: string;
|
|
5
|
+
owner: string | null;
|
|
6
|
+
/** The faction pressing a capture, and how far it is, 0 to 1. */
|
|
7
|
+
capturing: string | null;
|
|
8
|
+
progress: number;
|
|
9
|
+
/** Members on the point at the last scan, by faction. */
|
|
10
|
+
present: Record<string, number>;
|
|
11
|
+
}
|
|
12
|
+
/** What the `territory` topic carries. */
|
|
13
|
+
export interface TerritoryView {
|
|
14
|
+
phase: 'open' | 'won';
|
|
15
|
+
points: TerritoryPoint[];
|
|
16
|
+
scores: Record<string, number>;
|
|
17
|
+
/** Members by faction. */
|
|
18
|
+
members: Record<string, number>;
|
|
19
|
+
winner: string | null;
|
|
20
|
+
}
|
|
21
|
+
export interface ZoneContender {
|
|
22
|
+
player: number;
|
|
23
|
+
hp: number;
|
|
24
|
+
/** Outside the circle at the last scan. */
|
|
25
|
+
outside: boolean;
|
|
26
|
+
/** 1 for the winner; set as each player is eliminated. */
|
|
27
|
+
place: number | null;
|
|
28
|
+
}
|
|
29
|
+
/** What the `zone` topic carries. */
|
|
30
|
+
export interface ZoneView {
|
|
31
|
+
phase: 'lobby' | 'running' | 'finished';
|
|
32
|
+
/** The circle's centre, in chunks. */
|
|
33
|
+
center: [number, number];
|
|
34
|
+
radius: number;
|
|
35
|
+
/** The phase under way, and the radius it shrinks to. */
|
|
36
|
+
stage: number | null;
|
|
37
|
+
next_radius: number | null;
|
|
38
|
+
players: ZoneContender[];
|
|
39
|
+
alive: number;
|
|
40
|
+
winner: number | null;
|
|
41
|
+
}
|
|
42
|
+
/** Control points and shrinking circles (the `territory` starter, types `territory` and `zone`). */
|
|
43
|
+
export declare class TerritoryKit {
|
|
44
|
+
private readonly runtime;
|
|
45
|
+
private readonly territoryType;
|
|
46
|
+
private readonly zoneType;
|
|
47
|
+
private readonly maps;
|
|
48
|
+
private readonly zones;
|
|
49
|
+
constructor(runtime: KitRuntime, territoryType?: string, zoneType?: string);
|
|
50
|
+
/** The war on map `key`. */
|
|
51
|
+
map(key: string): TerritoryMap;
|
|
52
|
+
/** The circle of match `key`. */
|
|
53
|
+
zone(key: string): ZoneMatch;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* One war over a map's control points: a faction captures a point by having more members standing
|
|
57
|
+
* on it than any other (the hub reads where players are; a client never says it is on a point),
|
|
58
|
+
* a capture decays when nobody presses it, each point held pays its faction, and the first to the
|
|
59
|
+
* winning score wins. `view.onEvent` hears `captured`, `income` and `won`.
|
|
60
|
+
*/
|
|
61
|
+
export declare class TerritoryMap {
|
|
62
|
+
private readonly runtime;
|
|
63
|
+
readonly nodeType: string;
|
|
64
|
+
readonly key: string;
|
|
65
|
+
readonly view: HubState<TerritoryView>;
|
|
66
|
+
constructor(runtime: KitRuntime, nodeType: string, key: string);
|
|
67
|
+
watch(): Promise<TerritoryView>;
|
|
68
|
+
unwatch(): Promise<void>;
|
|
69
|
+
/** Joins a faction, once: a player keeps theirs. @throws {KitError} `conflict`, `bad_request`, `full`. */
|
|
70
|
+
join(faction: string): Promise<boolean>;
|
|
71
|
+
/** A new war. Developers only. */
|
|
72
|
+
reset(): Promise<boolean>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* One match's circle: it holds, then shrinks, phase by phase, and wears down every player outside
|
|
76
|
+
* it (the hub reads where players are) until one is left. `view.onEvent` hears `warning`,
|
|
77
|
+
* `shrinking`, `settled`, `final`, `eliminated` and `won`.
|
|
78
|
+
*/
|
|
79
|
+
export declare class ZoneMatch {
|
|
80
|
+
private readonly runtime;
|
|
81
|
+
readonly nodeType: string;
|
|
82
|
+
readonly key: string;
|
|
83
|
+
readonly view: HubState<ZoneView>;
|
|
84
|
+
constructor(runtime: KitRuntime, nodeType: string, key: string);
|
|
85
|
+
watch(): Promise<ZoneView>;
|
|
86
|
+
unwatch(): Promise<void>;
|
|
87
|
+
/** @throws {KitError} `started`, `full`. */
|
|
88
|
+
join(): Promise<boolean>;
|
|
89
|
+
/** The first to join, or a developer. @throws {KitError} `forbidden`, `not_ready`, `started`. */
|
|
90
|
+
start(): Promise<boolean>;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=territory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"territory.d.ts","sourceRoot":"","sources":["../../../src/kit/modules/territory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { HubState } from '../mirror.js';
|
|
2
|
+
/** Control points and shrinking circles (the `territory` starter, types `territory` and `zone`). */
|
|
3
|
+
export class TerritoryKit {
|
|
4
|
+
constructor(runtime, territoryType = 'territory', zoneType = 'zone') {
|
|
5
|
+
this.runtime = runtime;
|
|
6
|
+
this.territoryType = territoryType;
|
|
7
|
+
this.zoneType = zoneType;
|
|
8
|
+
this.maps = new Map();
|
|
9
|
+
this.zones = new Map();
|
|
10
|
+
}
|
|
11
|
+
/** The war on map `key`. */
|
|
12
|
+
map(key) {
|
|
13
|
+
let m = this.maps.get(key);
|
|
14
|
+
if (!m) {
|
|
15
|
+
m = new TerritoryMap(this.runtime, this.territoryType, key);
|
|
16
|
+
this.maps.set(key, m);
|
|
17
|
+
}
|
|
18
|
+
return m;
|
|
19
|
+
}
|
|
20
|
+
/** The circle of match `key`. */
|
|
21
|
+
zone(key) {
|
|
22
|
+
let z = this.zones.get(key);
|
|
23
|
+
if (!z) {
|
|
24
|
+
z = new ZoneMatch(this.runtime, this.zoneType, key);
|
|
25
|
+
this.zones.set(key, z);
|
|
26
|
+
}
|
|
27
|
+
return z;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* One war over a map's control points: a faction captures a point by having more members standing
|
|
32
|
+
* on it than any other (the hub reads where players are; a client never says it is on a point),
|
|
33
|
+
* a capture decays when nobody presses it, each point held pays its faction, and the first to the
|
|
34
|
+
* winning score wins. `view.onEvent` hears `captured`, `income` and `won`.
|
|
35
|
+
*/
|
|
36
|
+
export class TerritoryMap {
|
|
37
|
+
constructor(runtime, nodeType, key) {
|
|
38
|
+
this.runtime = runtime;
|
|
39
|
+
this.nodeType = nodeType;
|
|
40
|
+
this.key = key;
|
|
41
|
+
this.view = new HubState(runtime, nodeType, key, 'territory');
|
|
42
|
+
}
|
|
43
|
+
watch() {
|
|
44
|
+
return this.view.start();
|
|
45
|
+
}
|
|
46
|
+
unwatch() {
|
|
47
|
+
return this.view.stop();
|
|
48
|
+
}
|
|
49
|
+
/** Joins a faction, once: a player keeps theirs. @throws {KitError} `conflict`, `bad_request`, `full`. */
|
|
50
|
+
join(faction) {
|
|
51
|
+
return this.runtime.call(this.nodeType, this.key, 'join', { faction }, { action: true });
|
|
52
|
+
}
|
|
53
|
+
/** A new war. Developers only. */
|
|
54
|
+
reset() {
|
|
55
|
+
return this.runtime.call(this.nodeType, this.key, 'reset', null, { action: true });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* One match's circle: it holds, then shrinks, phase by phase, and wears down every player outside
|
|
60
|
+
* it (the hub reads where players are) until one is left. `view.onEvent` hears `warning`,
|
|
61
|
+
* `shrinking`, `settled`, `final`, `eliminated` and `won`.
|
|
62
|
+
*/
|
|
63
|
+
export class ZoneMatch {
|
|
64
|
+
constructor(runtime, nodeType, key) {
|
|
65
|
+
this.runtime = runtime;
|
|
66
|
+
this.nodeType = nodeType;
|
|
67
|
+
this.key = key;
|
|
68
|
+
this.view = new HubState(runtime, nodeType, key, 'zone');
|
|
69
|
+
}
|
|
70
|
+
watch() {
|
|
71
|
+
return this.view.start();
|
|
72
|
+
}
|
|
73
|
+
unwatch() {
|
|
74
|
+
return this.view.stop();
|
|
75
|
+
}
|
|
76
|
+
/** @throws {KitError} `started`, `full`. */
|
|
77
|
+
join() {
|
|
78
|
+
return this.runtime.call(this.nodeType, this.key, 'join', null, { action: true });
|
|
79
|
+
}
|
|
80
|
+
/** The first to join, or a developer. @throws {KitError} `forbidden`, `not_ready`, `started`. */
|
|
81
|
+
start() {
|
|
82
|
+
return this.runtime.call(this.nodeType, this.key, 'start', null, { action: true });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { CollectionMirror, HubState } from '../mirror.js';
|
|
2
|
+
import type { KitRuntime } from '../runtime.js';
|
|
3
|
+
/** What the `world` topic carries: the world's conditions now. */
|
|
4
|
+
export interface WorldConditions {
|
|
5
|
+
/** 0 at midnight, 0.5 at noon. */
|
|
6
|
+
time_of_day: number;
|
|
7
|
+
night: boolean;
|
|
8
|
+
/** `clear`, `rain`, `storm` or `fog` in the starter. */
|
|
9
|
+
weather: string;
|
|
10
|
+
/** The active liveops events' modifiers; where events overlap they multiply. */
|
|
11
|
+
modifiers: Record<string, number>;
|
|
12
|
+
}
|
|
13
|
+
/** A resource node, as `state` lists it. */
|
|
14
|
+
export interface WorldResourceNode {
|
|
15
|
+
id: number;
|
|
16
|
+
amount: number;
|
|
17
|
+
/** When it grows back (Unix ms), while it is empty. */
|
|
18
|
+
regrow_at: number | null;
|
|
19
|
+
}
|
|
20
|
+
/** A liveops event: between `start_ms` and `end_ms` its modifiers apply. */
|
|
21
|
+
export interface WorldLiveopsEvent {
|
|
22
|
+
name: string;
|
|
23
|
+
start_ms: number;
|
|
24
|
+
end_ms: number;
|
|
25
|
+
modifiers?: Record<string, number>;
|
|
26
|
+
}
|
|
27
|
+
/** What the world hub's `state` answers. */
|
|
28
|
+
export interface WorldView {
|
|
29
|
+
conditions: WorldConditions;
|
|
30
|
+
/** Game time since the world began, advanced only while it runs. */
|
|
31
|
+
clock_ms: number;
|
|
32
|
+
nodes: WorldResourceNode[];
|
|
33
|
+
events: WorldLiveopsEvent[];
|
|
34
|
+
/** Players in the app. */
|
|
35
|
+
online: number;
|
|
36
|
+
}
|
|
37
|
+
/** Where the world hub is: the root hub, `world` with an empty key, in the starter pack. */
|
|
38
|
+
export interface WorldKitOptions {
|
|
39
|
+
nodeType?: string;
|
|
40
|
+
key?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The root world hub (the `world-tick` starter): its clock, weather and liveops modifiers as a
|
|
44
|
+
* mirrored {@link conditions} topic, its resource nodes as a mirrored {@link nodes} collection,
|
|
45
|
+
* harvesting, and for developers the liveops schedule. The root hub takes 50 calls a second for
|
|
46
|
+
* the whole app, so read it from the mirrors (`conditions.start()`), not by polling `state`.
|
|
47
|
+
*/
|
|
48
|
+
export declare class WorldKit {
|
|
49
|
+
private readonly runtime;
|
|
50
|
+
/** The `world` topic: conditions now. `start()` it to keep it current. */
|
|
51
|
+
readonly conditions: HubState<WorldConditions>;
|
|
52
|
+
/** The `nodes` topic: each resource node's `amount` and `regrow_at`, by node id. */
|
|
53
|
+
readonly nodes: CollectionMirror;
|
|
54
|
+
private readonly nodeType;
|
|
55
|
+
private readonly key;
|
|
56
|
+
constructor(runtime: KitRuntime, options?: WorldKitOptions);
|
|
57
|
+
/** Everything the world shows: conditions, clock, nodes, events, players online. */
|
|
58
|
+
state(): Promise<WorldView>;
|
|
59
|
+
/**
|
|
60
|
+
* Takes one from resource node `node`; resolves with what it holds now.
|
|
61
|
+
* @throws {KitError} `cooldown`, `empty` (it grows back), `not_found`.
|
|
62
|
+
*/
|
|
63
|
+
harvest(node: number): Promise<number>;
|
|
64
|
+
/** Schedules (or replaces) a liveops event. Developers only (a developer kit). */
|
|
65
|
+
schedule(event: WorldLiveopsEvent): Promise<boolean>;
|
|
66
|
+
/** Cancels a liveops event; resolves with whether there was one. Developers only. */
|
|
67
|
+
cancel(name: string): Promise<boolean>;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=world.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"world.d.ts","sourceRoot":"","sources":["../../../src/kit/modules/world.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,kEAAkE;AAClE,MAAM,WAAW,eAAe;IAC9B,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,4EAA4E;AAC5E,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED,4CAA4C;AAC5C,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,eAAe,CAAC;IAC5B,oEAAoE;IACpE,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,0BAA0B;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,4FAA4F;AAC5F,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,qBAAa,QAAQ;IASjB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAR1B,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IAC/C,oFAAoF;IACpF,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;gBAGV,OAAO,EAAE,UAAU,EACpC,OAAO,GAAE,eAAoB;IAQ/B,oFAAoF;IACpF,KAAK,IAAI,OAAO,CAAC,SAAS,CAAC;IAI3B;;;OAGG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAItC,kFAAkF;IAClF,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC;IAIpD,qFAAqF;IACrF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAGvC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { CollectionMirror, HubState } from '../mirror.js';
|
|
2
|
+
/**
|
|
3
|
+
* The root world hub (the `world-tick` starter): its clock, weather and liveops modifiers as a
|
|
4
|
+
* mirrored {@link conditions} topic, its resource nodes as a mirrored {@link nodes} collection,
|
|
5
|
+
* harvesting, and for developers the liveops schedule. The root hub takes 50 calls a second for
|
|
6
|
+
* the whole app, so read it from the mirrors (`conditions.start()`), not by polling `state`.
|
|
7
|
+
*/
|
|
8
|
+
export class WorldKit {
|
|
9
|
+
constructor(runtime, options = {}) {
|
|
10
|
+
this.runtime = runtime;
|
|
11
|
+
this.nodeType = options.nodeType ?? 'world';
|
|
12
|
+
this.key = options.key ?? '';
|
|
13
|
+
this.conditions = new HubState(runtime, this.nodeType, this.key, 'world');
|
|
14
|
+
this.nodes = new CollectionMirror(runtime, this.nodeType, this.key, 'nodes');
|
|
15
|
+
}
|
|
16
|
+
/** Everything the world shows: conditions, clock, nodes, events, players online. */
|
|
17
|
+
state() {
|
|
18
|
+
return this.runtime.call(this.nodeType, this.key, 'state');
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Takes one from resource node `node`; resolves with what it holds now.
|
|
22
|
+
* @throws {KitError} `cooldown`, `empty` (it grows back), `not_found`.
|
|
23
|
+
*/
|
|
24
|
+
harvest(node) {
|
|
25
|
+
return this.runtime.call(this.nodeType, this.key, 'harvest', { node }, { action: true });
|
|
26
|
+
}
|
|
27
|
+
/** Schedules (or replaces) a liveops event. Developers only (a developer kit). */
|
|
28
|
+
schedule(event) {
|
|
29
|
+
return this.runtime.call(this.nodeType, this.key, 'schedule', event, { action: true });
|
|
30
|
+
}
|
|
31
|
+
/** Cancels a liveops event; resolves with whether there was one. Developers only. */
|
|
32
|
+
cancel(name) {
|
|
33
|
+
return this.runtime.call(this.nodeType, this.key, 'cancel', { name }, { action: true });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The game kit protocol: what kit hubs (ck-exec's starter packs, built on `ckx_sdk::kit`)
|
|
3
|
+
* send the kit's clients, read without I/O.
|
|
4
|
+
*
|
|
5
|
+
* A kit hub's topics carry **kit messages**: MessagePack maps with the hub's data and four
|
|
6
|
+
* reserved fields beside it: `v` (the protocol version, {@link KIT_PROTOCOL}), `epoch` (the
|
|
7
|
+
* publishing instance's epoch, which changes whenever it is placed again: a move, a restart or
|
|
8
|
+
* a crash), `seq` (one more than the topic's last message) and `kind`:
|
|
9
|
+
*
|
|
10
|
+
* - `snapshot`: the topic's whole public state; it replaces what the client holds.
|
|
11
|
+
* - `delta`: one change to a collection (`id`, then `type_name` and `props`, or `removed`),
|
|
12
|
+
* applied in `seq` order. A gap in `seq`, or another `epoch`, means a resync.
|
|
13
|
+
* - `changed`: something private changed for the players in `who` (`what` names it); they read
|
|
14
|
+
* it back with a call the hub answers for its caller alone. Topics have no access control,
|
|
15
|
+
* so kit hubs never publish anything private.
|
|
16
|
+
* - `event`: something that happened, with nothing to keep.
|
|
17
|
+
*
|
|
18
|
+
* Every kit hub answers `snapshot { topic }` with a `snapshot` message whose `seq` is the
|
|
19
|
+
* topic's last. A call that changes something may carry an `action_id`; the hub answers a call
|
|
20
|
+
* sent again with the first answer. A refusal's message is `kit:<code>: <text>`.
|
|
21
|
+
*/
|
|
22
|
+
import { CrowdyExecError } from '../domains/exec.js';
|
|
23
|
+
/** The protocol version kit messages carry in `v`. */
|
|
24
|
+
export declare const KIT_PROTOCOL = 1;
|
|
25
|
+
/** The method every kit hub answers with a topic's snapshot. */
|
|
26
|
+
export declare const KIT_SNAPSHOT_METHOD = "snapshot";
|
|
27
|
+
/** The longest `action_id` a hub takes, in bytes. */
|
|
28
|
+
export declare const KIT_ACTION_ID_MAX = 64;
|
|
29
|
+
export type KitMessageKind = 'snapshot' | 'delta' | 'changed' | 'event';
|
|
30
|
+
/** The reserved fields of a kit message. */
|
|
31
|
+
export interface KitHeader {
|
|
32
|
+
v: number;
|
|
33
|
+
epoch: number;
|
|
34
|
+
seq: number;
|
|
35
|
+
kind: KitMessageKind;
|
|
36
|
+
}
|
|
37
|
+
/** The body of a `changed` message. */
|
|
38
|
+
export interface KitChanged {
|
|
39
|
+
/** The players who should read again (player ids). */
|
|
40
|
+
who: number[];
|
|
41
|
+
/** What changed for them, as the hub's endpoints name it. */
|
|
42
|
+
what: string;
|
|
43
|
+
}
|
|
44
|
+
/** One member of a collection, as a `delta` or a collection snapshot carries it. */
|
|
45
|
+
export interface KitContainer {
|
|
46
|
+
id: number;
|
|
47
|
+
type_name: string;
|
|
48
|
+
/** Its public properties, all of them: a delta replaces what the client held. */
|
|
49
|
+
props: Record<string, unknown>;
|
|
50
|
+
}
|
|
51
|
+
/** A collection delta's body. */
|
|
52
|
+
export interface KitDelta {
|
|
53
|
+
id: number;
|
|
54
|
+
type_name?: string;
|
|
55
|
+
props?: Record<string, unknown>;
|
|
56
|
+
removed?: boolean;
|
|
57
|
+
}
|
|
58
|
+
/** Whether `value` is a kit message: a map with the four reserved fields. */
|
|
59
|
+
export declare function isKitMessage(value: unknown): value is KitHeader & Record<string, unknown>;
|
|
60
|
+
/**
|
|
61
|
+
* A kit message split into its header and its body (every other field), or null when
|
|
62
|
+
* `value` is not one.
|
|
63
|
+
*/
|
|
64
|
+
export declare function splitKitMessage<T = Record<string, unknown>>(value: unknown): {
|
|
65
|
+
header: KitHeader;
|
|
66
|
+
body: T;
|
|
67
|
+
} | null;
|
|
68
|
+
/** The code and text of a kit refusal's message, or null for any other message. */
|
|
69
|
+
export declare function parseKitRefusal(message: string): {
|
|
70
|
+
code: string;
|
|
71
|
+
text: string;
|
|
72
|
+
} | null;
|
|
73
|
+
/** The refusal codes kit hubs share; a hub may add its own (lowercase, digits, `_`). */
|
|
74
|
+
export declare const KIT_CODES: {
|
|
75
|
+
readonly BAD_REQUEST: "bad_request";
|
|
76
|
+
readonly NOT_FOUND: "not_found";
|
|
77
|
+
readonly FORBIDDEN: "forbidden";
|
|
78
|
+
readonly CONFLICT: "conflict";
|
|
79
|
+
readonly FULL: "full";
|
|
80
|
+
readonly LIMIT: "limit";
|
|
81
|
+
readonly COOLDOWN: "cooldown";
|
|
82
|
+
readonly NOT_YOUR_TURN: "not_your_turn";
|
|
83
|
+
readonly NOT_MEMBER: "not_member";
|
|
84
|
+
readonly STARTED: "started";
|
|
85
|
+
readonly FINISHED: "finished";
|
|
86
|
+
readonly NOT_READY: "not_ready";
|
|
87
|
+
readonly RESERVED: "reserved";
|
|
88
|
+
readonly EMPTY: "empty";
|
|
89
|
+
readonly OUT_OF_REACH: "out_of_reach";
|
|
90
|
+
readonly EXPIRED: "expired";
|
|
91
|
+
readonly INSUFFICIENT: "insufficient";
|
|
92
|
+
readonly ALREADY_DONE: "already_done";
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* A kit hub refused the call (`AppError` with a `kit:<code>:` message): `code` is what to
|
|
96
|
+
* branch on, `text` what to show. It is a {@link CrowdyExecError} with status `AppError`, so
|
|
97
|
+
* code that handles exec errors handles it.
|
|
98
|
+
*/
|
|
99
|
+
export declare class KitError extends CrowdyExecError {
|
|
100
|
+
readonly code: string;
|
|
101
|
+
readonly text: string;
|
|
102
|
+
constructor(code: string, text: string, cause?: unknown);
|
|
103
|
+
/** `error` as a {@link KitError} when it is a coded refusal; otherwise `error` itself. */
|
|
104
|
+
static from(error: unknown): unknown;
|
|
105
|
+
}
|
|
106
|
+
/** The refusal code of `error`, when it is a kit refusal. */
|
|
107
|
+
export declare function kitErrorCode(error: unknown): string | null;
|
|
108
|
+
/** A fresh `action_id`: unique per attempt, the same for every resend of that attempt. */
|
|
109
|
+
export declare function newActionId(): string;
|
|
110
|
+
/**
|
|
111
|
+
* `args` with `action_id` beside them: a plain object gains the field, `undefined` or `null`
|
|
112
|
+
* becomes `{ action_id }`. Anything else (an array, a number) cannot carry one and is
|
|
113
|
+
* returned as it is.
|
|
114
|
+
*/
|
|
115
|
+
export declare function withActionId(args: unknown, actionId: string): unknown;
|
|
116
|
+
/**
|
|
117
|
+
* The centre chunk of the shard holding chunk `(x, z)`: shards are `size` by `size` chunks,
|
|
118
|
+
* keyed by their centre. The same function as `ckx_sdk::kit::shard`, so a client finds the
|
|
119
|
+
* hub for where it stands without asking. `x` and `z` are chunk coordinates (integers).
|
|
120
|
+
*/
|
|
121
|
+
export declare function shardCentre(x: number, z: number, size: number): [number, number];
|
|
122
|
+
/** The key of the shard holding chunk `(x, z)`: `"<x>,<z>"` of its centre. */
|
|
123
|
+
export declare function shardKey(x: number, z: number, size: number): string;
|
|
124
|
+
/** The centre chunk a shard key names, or null when it is not `"<x>,<z>"`. */
|
|
125
|
+
export declare function parseShardKey(key: string): [number, number] | null;
|
|
126
|
+
/** The keys of the shards within `radius` shards of the one holding `(x, z)`, its own first. */
|
|
127
|
+
export declare function shardKeysAround(x: number, z: number, size: number, radius: number): string[];
|
|
128
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/kit/protocol.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The game kit protocol: what kit hubs (ck-exec's starter packs, built on `ckx_sdk::kit`)
|
|
3
|
+
* send the kit's clients, read without I/O.
|
|
4
|
+
*
|
|
5
|
+
* A kit hub's topics carry **kit messages**: MessagePack maps with the hub's data and four
|
|
6
|
+
* reserved fields beside it: `v` (the protocol version, {@link KIT_PROTOCOL}), `epoch` (the
|
|
7
|
+
* publishing instance's epoch, which changes whenever it is placed again: a move, a restart or
|
|
8
|
+
* a crash), `seq` (one more than the topic's last message) and `kind`:
|
|
9
|
+
*
|
|
10
|
+
* - `snapshot`: the topic's whole public state; it replaces what the client holds.
|
|
11
|
+
* - `delta`: one change to a collection (`id`, then `type_name` and `props`, or `removed`),
|
|
12
|
+
* applied in `seq` order. A gap in `seq`, or another `epoch`, means a resync.
|
|
13
|
+
* - `changed`: something private changed for the players in `who` (`what` names it); they read
|
|
14
|
+
* it back with a call the hub answers for its caller alone. Topics have no access control,
|
|
15
|
+
* so kit hubs never publish anything private.
|
|
16
|
+
* - `event`: something that happened, with nothing to keep.
|
|
17
|
+
*
|
|
18
|
+
* Every kit hub answers `snapshot { topic }` with a `snapshot` message whose `seq` is the
|
|
19
|
+
* topic's last. A call that changes something may carry an `action_id`; the hub answers a call
|
|
20
|
+
* sent again with the first answer. A refusal's message is `kit:<code>: <text>`.
|
|
21
|
+
*/
|
|
22
|
+
import { CrowdyExecError } from '../domains/exec.js';
|
|
23
|
+
/** The protocol version kit messages carry in `v`. */
|
|
24
|
+
export const KIT_PROTOCOL = 1;
|
|
25
|
+
/** The method every kit hub answers with a topic's snapshot. */
|
|
26
|
+
export const KIT_SNAPSHOT_METHOD = 'snapshot';
|
|
27
|
+
/** The longest `action_id` a hub takes, in bytes. */
|
|
28
|
+
export const KIT_ACTION_ID_MAX = 64;
|
|
29
|
+
const KINDS = new Set(['snapshot', 'delta', 'changed', 'event']);
|
|
30
|
+
const RESERVED = ['v', 'epoch', 'seq', 'kind'];
|
|
31
|
+
/** Whether `value` is a kit message: a map with the four reserved fields. */
|
|
32
|
+
export function isKitMessage(value) {
|
|
33
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
34
|
+
return false;
|
|
35
|
+
const v = value;
|
|
36
|
+
return (typeof v.v === 'number' &&
|
|
37
|
+
typeof v.epoch === 'number' &&
|
|
38
|
+
typeof v.seq === 'number' &&
|
|
39
|
+
typeof v.kind === 'string' &&
|
|
40
|
+
KINDS.has(v.kind));
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* A kit message split into its header and its body (every other field), or null when
|
|
44
|
+
* `value` is not one.
|
|
45
|
+
*/
|
|
46
|
+
export function splitKitMessage(value) {
|
|
47
|
+
if (!isKitMessage(value))
|
|
48
|
+
return null;
|
|
49
|
+
const body = { ...value };
|
|
50
|
+
for (const k of RESERVED)
|
|
51
|
+
delete body[k];
|
|
52
|
+
return {
|
|
53
|
+
header: { v: value.v, epoch: value.epoch, seq: value.seq, kind: value.kind },
|
|
54
|
+
body: body,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// ---- refusals ----
|
|
58
|
+
const CODED = /^kit:([a-z0-9_]{1,64}): ([\s\S]*)$/;
|
|
59
|
+
/** The code and text of a kit refusal's message, or null for any other message. */
|
|
60
|
+
export function parseKitRefusal(message) {
|
|
61
|
+
const m = CODED.exec(message);
|
|
62
|
+
return m ? { code: m[1], text: m[2] } : null;
|
|
63
|
+
}
|
|
64
|
+
/** The refusal codes kit hubs share; a hub may add its own (lowercase, digits, `_`). */
|
|
65
|
+
export const KIT_CODES = {
|
|
66
|
+
BAD_REQUEST: 'bad_request',
|
|
67
|
+
NOT_FOUND: 'not_found',
|
|
68
|
+
FORBIDDEN: 'forbidden',
|
|
69
|
+
CONFLICT: 'conflict',
|
|
70
|
+
FULL: 'full',
|
|
71
|
+
LIMIT: 'limit',
|
|
72
|
+
COOLDOWN: 'cooldown',
|
|
73
|
+
NOT_YOUR_TURN: 'not_your_turn',
|
|
74
|
+
NOT_MEMBER: 'not_member',
|
|
75
|
+
STARTED: 'started',
|
|
76
|
+
FINISHED: 'finished',
|
|
77
|
+
NOT_READY: 'not_ready',
|
|
78
|
+
RESERVED: 'reserved',
|
|
79
|
+
EMPTY: 'empty',
|
|
80
|
+
OUT_OF_REACH: 'out_of_reach',
|
|
81
|
+
EXPIRED: 'expired',
|
|
82
|
+
INSUFFICIENT: 'insufficient',
|
|
83
|
+
ALREADY_DONE: 'already_done',
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* A kit hub refused the call (`AppError` with a `kit:<code>:` message): `code` is what to
|
|
87
|
+
* branch on, `text` what to show. It is a {@link CrowdyExecError} with status `AppError`, so
|
|
88
|
+
* code that handles exec errors handles it.
|
|
89
|
+
*/
|
|
90
|
+
export class KitError extends CrowdyExecError {
|
|
91
|
+
constructor(code, text, cause) {
|
|
92
|
+
super('AppError', `kit:${code}: ${text}`, cause);
|
|
93
|
+
this.name = 'KitError';
|
|
94
|
+
this.code = code;
|
|
95
|
+
this.text = text;
|
|
96
|
+
}
|
|
97
|
+
/** `error` as a {@link KitError} when it is a coded refusal; otherwise `error` itself. */
|
|
98
|
+
static from(error) {
|
|
99
|
+
if (error instanceof KitError)
|
|
100
|
+
return error;
|
|
101
|
+
if (error instanceof CrowdyExecError && error.status === 'AppError') {
|
|
102
|
+
// A CrowdyExecError's message leads with its status.
|
|
103
|
+
const prefix = `${error.status}: `;
|
|
104
|
+
const raw = error.message.startsWith(prefix) ? error.message.slice(prefix.length) : error.message;
|
|
105
|
+
const coded = parseKitRefusal(raw);
|
|
106
|
+
if (coded)
|
|
107
|
+
return new KitError(coded.code, coded.text, error);
|
|
108
|
+
}
|
|
109
|
+
return error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/** The refusal code of `error`, when it is a kit refusal. */
|
|
113
|
+
export function kitErrorCode(error) {
|
|
114
|
+
const e = KitError.from(error);
|
|
115
|
+
return e instanceof KitError ? e.code : null;
|
|
116
|
+
}
|
|
117
|
+
// ---- action ids ----
|
|
118
|
+
/** A fresh `action_id`: unique per attempt, the same for every resend of that attempt. */
|
|
119
|
+
export function newActionId() {
|
|
120
|
+
const c = globalThis.crypto;
|
|
121
|
+
if (c?.randomUUID)
|
|
122
|
+
return c.randomUUID();
|
|
123
|
+
return `a-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* `args` with `action_id` beside them: a plain object gains the field, `undefined` or `null`
|
|
127
|
+
* becomes `{ action_id }`. Anything else (an array, a number) cannot carry one and is
|
|
128
|
+
* returned as it is.
|
|
129
|
+
*/
|
|
130
|
+
export function withActionId(args, actionId) {
|
|
131
|
+
if (args === undefined || args === null)
|
|
132
|
+
return { action_id: actionId };
|
|
133
|
+
if (typeof args === 'object' && !Array.isArray(args) && !(args instanceof Uint8Array)) {
|
|
134
|
+
return { ...args, action_id: actionId };
|
|
135
|
+
}
|
|
136
|
+
return args;
|
|
137
|
+
}
|
|
138
|
+
// ---- shard keys ----
|
|
139
|
+
/**
|
|
140
|
+
* The centre chunk of the shard holding chunk `(x, z)`: shards are `size` by `size` chunks,
|
|
141
|
+
* keyed by their centre. The same function as `ckx_sdk::kit::shard`, so a client finds the
|
|
142
|
+
* hub for where it stands without asking. `x` and `z` are chunk coordinates (integers).
|
|
143
|
+
*/
|
|
144
|
+
export function shardCentre(x, z, size) {
|
|
145
|
+
const s = Math.max(1, Math.trunc(size));
|
|
146
|
+
const half = Math.floor(s / 2);
|
|
147
|
+
return [Math.floor(Math.trunc(x) / s) * s + half, Math.floor(Math.trunc(z) / s) * s + half];
|
|
148
|
+
}
|
|
149
|
+
/** The key of the shard holding chunk `(x, z)`: `"<x>,<z>"` of its centre. */
|
|
150
|
+
export function shardKey(x, z, size) {
|
|
151
|
+
const [cx, cz] = shardCentre(x, z, size);
|
|
152
|
+
return `${cx},${cz}`;
|
|
153
|
+
}
|
|
154
|
+
/** The centre chunk a shard key names, or null when it is not `"<x>,<z>"`. */
|
|
155
|
+
export function parseShardKey(key) {
|
|
156
|
+
const m = /^\s*(-?\d+)\s*,\s*(-?\d+)\s*$/.exec(key);
|
|
157
|
+
return m ? [Number(m[1]), Number(m[2])] : null;
|
|
158
|
+
}
|
|
159
|
+
/** The keys of the shards within `radius` shards of the one holding `(x, z)`, its own first. */
|
|
160
|
+
export function shardKeysAround(x, z, size, radius) {
|
|
161
|
+
const s = Math.max(1, Math.trunc(size));
|
|
162
|
+
const [cx, cz] = shardCentre(x, z, s);
|
|
163
|
+
const keys = [`${cx},${cz}`];
|
|
164
|
+
for (let dz = -radius; dz <= radius; dz++) {
|
|
165
|
+
for (let dx = -radius; dx <= radius; dx++) {
|
|
166
|
+
if (dx !== 0 || dz !== 0)
|
|
167
|
+
keys.push(`${cx + dx * s},${cz + dz * s}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return keys;
|
|
171
|
+
}
|