@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,111 @@
|
|
|
1
|
+
import { type KitChanged, type KitContainer, type KitDelta, type KitHeader } from './protocol.js';
|
|
2
|
+
import type { KitRuntime } from './runtime.js';
|
|
3
|
+
import type { KitStore } from './store.js';
|
|
4
|
+
/**
|
|
5
|
+
* A hub topic mirrored on the client: it subscribes, asks the hub for a snapshot, then applies
|
|
6
|
+
* what arrives after the snapshot's `seq`. It asks again when the publishing instance's
|
|
7
|
+
* `epoch` changes, when a delta topic skips a `seq`, and after the connection reconnects.
|
|
8
|
+
*/
|
|
9
|
+
export declare abstract class TopicMirror<S> implements KitStore<S | undefined> {
|
|
10
|
+
protected readonly runtime: KitRuntime;
|
|
11
|
+
readonly nodeType: string;
|
|
12
|
+
readonly key: string;
|
|
13
|
+
readonly topic: string;
|
|
14
|
+
private state;
|
|
15
|
+
private epoch;
|
|
16
|
+
private seq;
|
|
17
|
+
private buffer;
|
|
18
|
+
private syncing;
|
|
19
|
+
private stopSub;
|
|
20
|
+
private offReconnect;
|
|
21
|
+
private readonly listeners;
|
|
22
|
+
private readonly eventListeners;
|
|
23
|
+
private readonly changedListeners;
|
|
24
|
+
private watching;
|
|
25
|
+
constructor(runtime: KitRuntime, nodeType: string, key: string, topic: string);
|
|
26
|
+
/** The state a snapshot's body stands for. */
|
|
27
|
+
protected abstract fromSnapshot(body: Record<string, unknown>): S;
|
|
28
|
+
/** The state after one delta, or null when a delta is not this topic's (resync). */
|
|
29
|
+
protected abstract applyDelta(state: S, delta: KitDelta): S | null;
|
|
30
|
+
/** Whether a skipped `seq` loses state (a delta topic) or not (snapshot topics). */
|
|
31
|
+
protected abstract readonly gapsLoseState: boolean;
|
|
32
|
+
/** Subscribes and takes the first snapshot; resolves with the state. Idempotent. */
|
|
33
|
+
start(): Promise<S>;
|
|
34
|
+
/** Stops the subscription; the last state stays readable. */
|
|
35
|
+
stop(): Promise<void>;
|
|
36
|
+
/** Takes a fresh snapshot (shared by concurrent callers). */
|
|
37
|
+
resync(): Promise<S>;
|
|
38
|
+
get(): S | undefined;
|
|
39
|
+
subscribe(listener: (value: S | undefined) => void): () => void;
|
|
40
|
+
/** `event` messages on the topic. */
|
|
41
|
+
onEvent(listener: (body: Record<string, unknown>, header: KitHeader) => void): () => void;
|
|
42
|
+
/** `changed` messages on the topic: someone's private state to read again. */
|
|
43
|
+
onChanged(listener: (changed: KitChanged, header: KitHeader) => void): () => void;
|
|
44
|
+
/** The epoch and seq the mirror is at; `-1` before its first snapshot. */
|
|
45
|
+
get position(): {
|
|
46
|
+
epoch: number;
|
|
47
|
+
seq: number;
|
|
48
|
+
};
|
|
49
|
+
private emit;
|
|
50
|
+
private onPush;
|
|
51
|
+
private apply;
|
|
52
|
+
}
|
|
53
|
+
/** A topic whose messages are its hub's whole public state (`snapshot` messages). */
|
|
54
|
+
export declare class HubState<T = Record<string, unknown>> extends TopicMirror<T> {
|
|
55
|
+
protected readonly gapsLoseState = false;
|
|
56
|
+
protected fromSnapshot(body: Record<string, unknown>): T;
|
|
57
|
+
protected applyDelta(): T | null;
|
|
58
|
+
}
|
|
59
|
+
/** A collection's members by id: what a model topic's snapshot and deltas describe. */
|
|
60
|
+
export type KitCollection = ReadonlyMap<number, KitContainer>;
|
|
61
|
+
/**
|
|
62
|
+
* A topic carrying a collection (`delta` per member; the snapshot's `containers`): the
|
|
63
|
+
* starters' model topics, such as an area's mobs, NPCs and pets.
|
|
64
|
+
*/
|
|
65
|
+
export declare class CollectionMirror extends TopicMirror<KitCollection> {
|
|
66
|
+
protected readonly gapsLoseState = true;
|
|
67
|
+
protected fromSnapshot(body: Record<string, unknown>): KitCollection;
|
|
68
|
+
protected applyDelta(state: KitCollection, d: KitDelta): KitCollection | null;
|
|
69
|
+
/** The members, oldest first, of one type or all. */
|
|
70
|
+
list(typeName?: string): KitContainer[];
|
|
71
|
+
/** One member by id. */
|
|
72
|
+
member(id: number): KitContainer | undefined;
|
|
73
|
+
}
|
|
74
|
+
/** How a {@link PrivateState} reads its value and hears that it changed. */
|
|
75
|
+
export interface PrivateStateSource<T> {
|
|
76
|
+
nodeType: string;
|
|
77
|
+
key: string;
|
|
78
|
+
/** The topic whose `changed` messages name this player. */
|
|
79
|
+
topic: string;
|
|
80
|
+
/** The `what` (or whats) that concern this value. */
|
|
81
|
+
what: string | readonly string[];
|
|
82
|
+
/** Reads the value with a call the hub answers for its caller alone. */
|
|
83
|
+
read: () => Promise<T>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* State only its player may read (a hand of cards, an inventory, a pet's bond): read with a
|
|
87
|
+
* caller-scoped call, read again when the hub's `changed` message names this player, and after
|
|
88
|
+
* a reconnect. Nothing private is ever on a topic.
|
|
89
|
+
*/
|
|
90
|
+
export declare class PrivateState<T> implements KitStore<T | undefined> {
|
|
91
|
+
private readonly runtime;
|
|
92
|
+
private readonly source;
|
|
93
|
+
private value;
|
|
94
|
+
private reading;
|
|
95
|
+
private again;
|
|
96
|
+
private stopSub;
|
|
97
|
+
private offReconnect;
|
|
98
|
+
private readonly listeners;
|
|
99
|
+
private readonly whats;
|
|
100
|
+
private watching;
|
|
101
|
+
constructor(runtime: KitRuntime, source: PrivateStateSource<T>);
|
|
102
|
+
/** Subscribes and reads the first value. Idempotent. */
|
|
103
|
+
start(): Promise<T>;
|
|
104
|
+
stop(): Promise<void>;
|
|
105
|
+
/** Reads the value again (one read at a time; a request during one runs after it). */
|
|
106
|
+
refresh(): Promise<T>;
|
|
107
|
+
get(): T | undefined;
|
|
108
|
+
subscribe(listener: (value: T | undefined) => void): () => void;
|
|
109
|
+
private onPush;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=mirror.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mirror.d.ts","sourceRoot":"","sources":["../../src/kit/mirror.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { KIT_SNAPSHOT_METHOD, KitError, splitKitMessage, } from './protocol.js';
|
|
2
|
+
/**
|
|
3
|
+
* A hub topic mirrored on the client: it subscribes, asks the hub for a snapshot, then applies
|
|
4
|
+
* what arrives after the snapshot's `seq`. It asks again when the publishing instance's
|
|
5
|
+
* `epoch` changes, when a delta topic skips a `seq`, and after the connection reconnects.
|
|
6
|
+
*/
|
|
7
|
+
export class TopicMirror {
|
|
8
|
+
constructor(runtime, nodeType, key, topic) {
|
|
9
|
+
this.runtime = runtime;
|
|
10
|
+
this.nodeType = nodeType;
|
|
11
|
+
this.key = key;
|
|
12
|
+
this.topic = topic;
|
|
13
|
+
this.epoch = -1;
|
|
14
|
+
this.seq = 0;
|
|
15
|
+
this.buffer = null;
|
|
16
|
+
this.syncing = null;
|
|
17
|
+
this.stopSub = null;
|
|
18
|
+
this.offReconnect = null;
|
|
19
|
+
this.listeners = new Set();
|
|
20
|
+
this.eventListeners = new Set();
|
|
21
|
+
this.changedListeners = new Set();
|
|
22
|
+
this.watching = null;
|
|
23
|
+
}
|
|
24
|
+
/** Subscribes and takes the first snapshot; resolves with the state. Idempotent. */
|
|
25
|
+
start() {
|
|
26
|
+
if (!this.watching) {
|
|
27
|
+
this.watching = (async () => {
|
|
28
|
+
this.offReconnect = this.runtime.onReconnect(() => void this.resync().catch(() => undefined));
|
|
29
|
+
this.stopSub = await this.runtime.subscribe(this.nodeType, this.key, this.topic, (p) => this.onPush(p));
|
|
30
|
+
return this.resync();
|
|
31
|
+
})().catch((e) => {
|
|
32
|
+
this.watching = null;
|
|
33
|
+
throw e;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return this.watching;
|
|
37
|
+
}
|
|
38
|
+
/** Stops the subscription; the last state stays readable. */
|
|
39
|
+
async stop() {
|
|
40
|
+
this.offReconnect?.();
|
|
41
|
+
this.offReconnect = null;
|
|
42
|
+
const stop = this.stopSub;
|
|
43
|
+
this.stopSub = null;
|
|
44
|
+
this.watching = null;
|
|
45
|
+
await stop?.();
|
|
46
|
+
}
|
|
47
|
+
/** Takes a fresh snapshot (shared by concurrent callers). */
|
|
48
|
+
resync() {
|
|
49
|
+
if (!this.syncing) {
|
|
50
|
+
this.buffer = [];
|
|
51
|
+
this.syncing = (async () => {
|
|
52
|
+
try {
|
|
53
|
+
const reply = await this.runtime.call(this.nodeType, this.key, KIT_SNAPSHOT_METHOD, { topic: this.topic });
|
|
54
|
+
const msg = splitKitMessage(reply);
|
|
55
|
+
if (!msg)
|
|
56
|
+
throw new KitError('bad_request', `${this.nodeType} answered snapshot with something other than a kit message`);
|
|
57
|
+
this.epoch = msg.header.epoch;
|
|
58
|
+
this.seq = msg.header.seq;
|
|
59
|
+
this.state = this.fromSnapshot(msg.body);
|
|
60
|
+
const pending = this.buffer ?? [];
|
|
61
|
+
this.buffer = null;
|
|
62
|
+
this.syncing = null;
|
|
63
|
+
for (const m of pending.sort((a, b) => a.header.seq - b.header.seq))
|
|
64
|
+
this.apply(m);
|
|
65
|
+
this.emit();
|
|
66
|
+
return this.state;
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
this.buffer = null;
|
|
70
|
+
this.syncing = null;
|
|
71
|
+
throw e;
|
|
72
|
+
}
|
|
73
|
+
})();
|
|
74
|
+
}
|
|
75
|
+
return this.syncing;
|
|
76
|
+
}
|
|
77
|
+
get() {
|
|
78
|
+
return this.state;
|
|
79
|
+
}
|
|
80
|
+
subscribe(listener) {
|
|
81
|
+
this.listeners.add(listener);
|
|
82
|
+
return () => this.listeners.delete(listener);
|
|
83
|
+
}
|
|
84
|
+
/** `event` messages on the topic. */
|
|
85
|
+
onEvent(listener) {
|
|
86
|
+
this.eventListeners.add(listener);
|
|
87
|
+
return () => this.eventListeners.delete(listener);
|
|
88
|
+
}
|
|
89
|
+
/** `changed` messages on the topic: someone's private state to read again. */
|
|
90
|
+
onChanged(listener) {
|
|
91
|
+
this.changedListeners.add(listener);
|
|
92
|
+
return () => this.changedListeners.delete(listener);
|
|
93
|
+
}
|
|
94
|
+
/** The epoch and seq the mirror is at; `-1` before its first snapshot. */
|
|
95
|
+
get position() {
|
|
96
|
+
return { epoch: this.epoch, seq: this.seq };
|
|
97
|
+
}
|
|
98
|
+
emit() {
|
|
99
|
+
for (const l of [...this.listeners]) {
|
|
100
|
+
try {
|
|
101
|
+
l(this.state);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
/* a listener's exception is its own */
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
onPush(push) {
|
|
109
|
+
const msg = splitKitMessage(push.value);
|
|
110
|
+
if (!msg)
|
|
111
|
+
return;
|
|
112
|
+
if (this.buffer) {
|
|
113
|
+
this.buffer.push(msg);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
this.apply(msg);
|
|
117
|
+
}
|
|
118
|
+
apply({ header, body }) {
|
|
119
|
+
if (header.epoch !== this.epoch) {
|
|
120
|
+
if (header.kind === 'snapshot') {
|
|
121
|
+
this.epoch = header.epoch;
|
|
122
|
+
this.seq = header.seq;
|
|
123
|
+
this.state = this.fromSnapshot(body);
|
|
124
|
+
this.emit();
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
void this.resync().catch(() => undefined);
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (header.seq <= this.seq)
|
|
132
|
+
return;
|
|
133
|
+
const gap = header.seq !== this.seq + 1;
|
|
134
|
+
this.seq = header.seq;
|
|
135
|
+
switch (header.kind) {
|
|
136
|
+
case 'snapshot':
|
|
137
|
+
this.state = this.fromSnapshot(body);
|
|
138
|
+
this.emit();
|
|
139
|
+
return;
|
|
140
|
+
case 'delta': {
|
|
141
|
+
const next = gap || this.state === undefined ? null : this.applyDelta(this.state, body);
|
|
142
|
+
if (next === null) {
|
|
143
|
+
void this.resync().catch(() => undefined);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
this.state = next;
|
|
147
|
+
this.emit();
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
case 'changed':
|
|
151
|
+
for (const l of [...this.changedListeners])
|
|
152
|
+
l(body, header);
|
|
153
|
+
break;
|
|
154
|
+
case 'event':
|
|
155
|
+
for (const l of [...this.eventListeners])
|
|
156
|
+
l(body, header);
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
if (gap && this.gapsLoseState)
|
|
160
|
+
void this.resync().catch(() => undefined);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/** A topic whose messages are its hub's whole public state (`snapshot` messages). */
|
|
164
|
+
export class HubState extends TopicMirror {
|
|
165
|
+
constructor() {
|
|
166
|
+
super(...arguments);
|
|
167
|
+
this.gapsLoseState = false;
|
|
168
|
+
}
|
|
169
|
+
fromSnapshot(body) {
|
|
170
|
+
return body;
|
|
171
|
+
}
|
|
172
|
+
applyDelta() {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* A topic carrying a collection (`delta` per member; the snapshot's `containers`): the
|
|
178
|
+
* starters' model topics, such as an area's mobs, NPCs and pets.
|
|
179
|
+
*/
|
|
180
|
+
export class CollectionMirror extends TopicMirror {
|
|
181
|
+
constructor() {
|
|
182
|
+
super(...arguments);
|
|
183
|
+
this.gapsLoseState = true;
|
|
184
|
+
}
|
|
185
|
+
fromSnapshot(body) {
|
|
186
|
+
const out = new Map();
|
|
187
|
+
for (const c of body.containers ?? []) {
|
|
188
|
+
if (!c.removed)
|
|
189
|
+
out.set(c.id, { id: c.id, type_name: c.type_name ?? '', props: c.props ?? {} });
|
|
190
|
+
}
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
193
|
+
applyDelta(state, d) {
|
|
194
|
+
if (typeof d.id !== 'number')
|
|
195
|
+
return null;
|
|
196
|
+
const next = new Map(state);
|
|
197
|
+
if (d.removed) {
|
|
198
|
+
next.delete(d.id);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
next.set(d.id, { id: d.id, type_name: d.type_name ?? state.get(d.id)?.type_name ?? '', props: d.props ?? {} });
|
|
202
|
+
}
|
|
203
|
+
return next;
|
|
204
|
+
}
|
|
205
|
+
/** The members, oldest first, of one type or all. */
|
|
206
|
+
list(typeName) {
|
|
207
|
+
const all = [...(this.get()?.values() ?? [])].sort((a, b) => a.id - b.id);
|
|
208
|
+
return typeName ? all.filter((c) => c.type_name === typeName) : all;
|
|
209
|
+
}
|
|
210
|
+
/** One member by id. */
|
|
211
|
+
member(id) {
|
|
212
|
+
return this.get()?.get(id);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* State only its player may read (a hand of cards, an inventory, a pet's bond): read with a
|
|
217
|
+
* caller-scoped call, read again when the hub's `changed` message names this player, and after
|
|
218
|
+
* a reconnect. Nothing private is ever on a topic.
|
|
219
|
+
*/
|
|
220
|
+
export class PrivateState {
|
|
221
|
+
constructor(runtime, source) {
|
|
222
|
+
this.runtime = runtime;
|
|
223
|
+
this.source = source;
|
|
224
|
+
this.reading = null;
|
|
225
|
+
this.again = false;
|
|
226
|
+
this.stopSub = null;
|
|
227
|
+
this.offReconnect = null;
|
|
228
|
+
this.listeners = new Set();
|
|
229
|
+
this.watching = null;
|
|
230
|
+
this.whats = new Set(typeof source.what === 'string' ? [source.what] : source.what);
|
|
231
|
+
}
|
|
232
|
+
/** Subscribes and reads the first value. Idempotent. */
|
|
233
|
+
start() {
|
|
234
|
+
if (!this.watching) {
|
|
235
|
+
this.watching = (async () => {
|
|
236
|
+
this.offReconnect = this.runtime.onReconnect(() => void this.refresh().catch(() => undefined));
|
|
237
|
+
this.stopSub = await this.runtime.subscribe(this.source.nodeType, this.source.key, this.source.topic, (p) => this.onPush(p));
|
|
238
|
+
return this.refresh();
|
|
239
|
+
})().catch((e) => {
|
|
240
|
+
this.watching = null;
|
|
241
|
+
throw e;
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
return this.watching;
|
|
245
|
+
}
|
|
246
|
+
async stop() {
|
|
247
|
+
this.offReconnect?.();
|
|
248
|
+
this.offReconnect = null;
|
|
249
|
+
const stop = this.stopSub;
|
|
250
|
+
this.stopSub = null;
|
|
251
|
+
this.watching = null;
|
|
252
|
+
await stop?.();
|
|
253
|
+
}
|
|
254
|
+
/** Reads the value again (one read at a time; a request during one runs after it). */
|
|
255
|
+
refresh() {
|
|
256
|
+
if (this.reading) {
|
|
257
|
+
this.again = true;
|
|
258
|
+
return this.reading;
|
|
259
|
+
}
|
|
260
|
+
this.reading = (async () => {
|
|
261
|
+
try {
|
|
262
|
+
let v;
|
|
263
|
+
do {
|
|
264
|
+
this.again = false;
|
|
265
|
+
v = await this.source.read();
|
|
266
|
+
} while (this.again);
|
|
267
|
+
this.value = v;
|
|
268
|
+
for (const l of [...this.listeners])
|
|
269
|
+
l(v);
|
|
270
|
+
return v;
|
|
271
|
+
}
|
|
272
|
+
finally {
|
|
273
|
+
this.reading = null;
|
|
274
|
+
}
|
|
275
|
+
})();
|
|
276
|
+
return this.reading;
|
|
277
|
+
}
|
|
278
|
+
get() {
|
|
279
|
+
return this.value;
|
|
280
|
+
}
|
|
281
|
+
subscribe(listener) {
|
|
282
|
+
this.listeners.add(listener);
|
|
283
|
+
return () => this.listeners.delete(listener);
|
|
284
|
+
}
|
|
285
|
+
onPush(push) {
|
|
286
|
+
const msg = splitKitMessage(push.value);
|
|
287
|
+
if (!msg || msg.header.kind !== 'changed' || !this.whats.has(msg.body.what))
|
|
288
|
+
return;
|
|
289
|
+
const who = Array.isArray(msg.body.who) ? msg.body.who.map(String) : [];
|
|
290
|
+
void this.runtime.playerId().then((me) => {
|
|
291
|
+
if (me === null || who.includes(me))
|
|
292
|
+
void this.refresh().catch(() => undefined);
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { HubState } from '../mirror.js';
|
|
2
|
+
import type { KitRuntime } from '../runtime.js';
|
|
3
|
+
export interface ArenaFighter {
|
|
4
|
+
player: number;
|
|
5
|
+
class: string;
|
|
6
|
+
x: number;
|
|
7
|
+
z: number;
|
|
8
|
+
hp: number;
|
|
9
|
+
max_hp: number;
|
|
10
|
+
/** What casts spend; it regenerates. */
|
|
11
|
+
resource: number;
|
|
12
|
+
statuses: string[];
|
|
13
|
+
kills: number;
|
|
14
|
+
deaths: number;
|
|
15
|
+
/** Down, until the respawn. */
|
|
16
|
+
down: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface ArenaShot {
|
|
19
|
+
ability: string;
|
|
20
|
+
caster: number;
|
|
21
|
+
x: number;
|
|
22
|
+
z: number;
|
|
23
|
+
}
|
|
24
|
+
export interface ArenaBlast {
|
|
25
|
+
ability: string;
|
|
26
|
+
caster: number;
|
|
27
|
+
x: number;
|
|
28
|
+
z: number;
|
|
29
|
+
radius: number;
|
|
30
|
+
/** When it goes off (Unix ms). */
|
|
31
|
+
at: number;
|
|
32
|
+
}
|
|
33
|
+
/** What the `arena` topic's snapshots carry. */
|
|
34
|
+
export interface ArenaView {
|
|
35
|
+
phase: 'waiting' | 'running' | 'finished';
|
|
36
|
+
width: number;
|
|
37
|
+
depth: number;
|
|
38
|
+
fighters: ArenaFighter[];
|
|
39
|
+
projectiles: ArenaShot[];
|
|
40
|
+
areas: ArenaBlast[];
|
|
41
|
+
kill_target: number;
|
|
42
|
+
ends_at: number | null;
|
|
43
|
+
winner: number | null;
|
|
44
|
+
}
|
|
45
|
+
/** Real-time arenas (the `arena` starter): {@link arena} by key. */
|
|
46
|
+
export declare class ArenasKit {
|
|
47
|
+
private readonly runtime;
|
|
48
|
+
private readonly nodeType;
|
|
49
|
+
private readonly arenas;
|
|
50
|
+
constructor(runtime: KitRuntime, nodeType?: string);
|
|
51
|
+
arena(key: string): ArenaMatch;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* One arena: players fight with their class's abilities and the hub referees every hit. A
|
|
55
|
+
* client reports where its player is ({@link move}, a few times a second) and the hub keeps what
|
|
56
|
+
* the class's speed allows; casts are checked for cooldown, resource and range, and land as the hub
|
|
57
|
+
* decides. {@link view} mirrors the `arena` topic (up to five snapshots a second); its `event`
|
|
58
|
+
* messages (`type`: `cast`, `impact`, `kill`, `respawn`, `violation`, `removed`, `finished`) come
|
|
59
|
+
* through `view.onEvent`.
|
|
60
|
+
*/
|
|
61
|
+
export declare class ArenaMatch {
|
|
62
|
+
private readonly runtime;
|
|
63
|
+
readonly nodeType: string;
|
|
64
|
+
readonly key: string;
|
|
65
|
+
readonly view: HubState<ArenaView>;
|
|
66
|
+
constructor(runtime: KitRuntime, nodeType: string, key: string);
|
|
67
|
+
watch(): Promise<ArenaView>;
|
|
68
|
+
unwatch(): Promise<void>;
|
|
69
|
+
/** Joins as `heroClass` (the starter's are `warrior` and `mage`). @throws {KitError} `bad_request`, `full`, `started`. */
|
|
70
|
+
join(heroClass: string): Promise<boolean>;
|
|
71
|
+
leave(): Promise<boolean>;
|
|
72
|
+
/** The first to join, or a developer. @throws {KitError} `forbidden`, `not_ready` (one fighter), `started`. */
|
|
73
|
+
start(): Promise<boolean>;
|
|
74
|
+
/**
|
|
75
|
+
* Reports where the player is; resolves with the position the hub kept (a move too fast is cut
|
|
76
|
+
* short, a jump refused). Not sent with an action id: the next report replaces it.
|
|
77
|
+
* @throws {KitError} `forbidden` (removed for movement the warden refused), `conflict` (down).
|
|
78
|
+
*/
|
|
79
|
+
move(x: number, z: number): Promise<[number, number]>;
|
|
80
|
+
/**
|
|
81
|
+
* Casts `ability` aimed at `(x, z)`; resolves with whether a hit-scan strike hit someone at once
|
|
82
|
+
* (a projectile or an area lands later, as an `impact` event).
|
|
83
|
+
* @throws {KitError} `cooldown`, `insufficient` (resource), `out_of_reach`, `not_found` (not the class's).
|
|
84
|
+
*/
|
|
85
|
+
cast(ability: string, x: number, z: number): Promise<boolean>;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=arenas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arenas.d.ts","sourceRoot":"","sources":["../../../src/kit/modules/arenas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,gDAAgD;AAChD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,WAAW,EAAE,SAAS,EAAE,CAAC;IACzB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,oEAAoE;AACpE,qBAAa,SAAS;IAIlB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJ3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;gBAGrC,OAAO,EAAE,UAAU,EACnB,QAAQ,SAAU;IAGrC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU;CAQ/B;AAED;;;;;;;GAOG;AACH,qBAAa,UAAU;IAInB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM;IALtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;gBAGhB,OAAO,EAAE,UAAU,EAC3B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM;IAKtB,KAAK,IAAI,OAAO,CAAC,SAAS,CAAC;IAI3B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,0HAA0H;IAC1H,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIzC,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;IAIzB,+GAA+G;IAC/G,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;IAIzB;;;;OAIG;IACH,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAIrD;;;;OAIG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAG9D"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { HubState } from '../mirror.js';
|
|
2
|
+
/** Real-time arenas (the `arena` starter): {@link arena} by key. */
|
|
3
|
+
export class ArenasKit {
|
|
4
|
+
constructor(runtime, nodeType = 'arena') {
|
|
5
|
+
this.runtime = runtime;
|
|
6
|
+
this.nodeType = nodeType;
|
|
7
|
+
this.arenas = new Map();
|
|
8
|
+
}
|
|
9
|
+
arena(key) {
|
|
10
|
+
let a = this.arenas.get(key);
|
|
11
|
+
if (!a) {
|
|
12
|
+
a = new ArenaMatch(this.runtime, this.nodeType, key);
|
|
13
|
+
this.arenas.set(key, a);
|
|
14
|
+
}
|
|
15
|
+
return a;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* One arena: players fight with their class's abilities and the hub referees every hit. A
|
|
20
|
+
* client reports where its player is ({@link move}, a few times a second) and the hub keeps what
|
|
21
|
+
* the class's speed allows; casts are checked for cooldown, resource and range, and land as the hub
|
|
22
|
+
* decides. {@link view} mirrors the `arena` topic (up to five snapshots a second); its `event`
|
|
23
|
+
* messages (`type`: `cast`, `impact`, `kill`, `respawn`, `violation`, `removed`, `finished`) come
|
|
24
|
+
* through `view.onEvent`.
|
|
25
|
+
*/
|
|
26
|
+
export class ArenaMatch {
|
|
27
|
+
constructor(runtime, nodeType, key) {
|
|
28
|
+
this.runtime = runtime;
|
|
29
|
+
this.nodeType = nodeType;
|
|
30
|
+
this.key = key;
|
|
31
|
+
this.view = new HubState(runtime, nodeType, key, 'arena');
|
|
32
|
+
}
|
|
33
|
+
watch() {
|
|
34
|
+
return this.view.start();
|
|
35
|
+
}
|
|
36
|
+
unwatch() {
|
|
37
|
+
return this.view.stop();
|
|
38
|
+
}
|
|
39
|
+
/** Joins as `heroClass` (the starter's are `warrior` and `mage`). @throws {KitError} `bad_request`, `full`, `started`. */
|
|
40
|
+
join(heroClass) {
|
|
41
|
+
return this.runtime.call(this.nodeType, this.key, 'join', { class: heroClass }, { action: true });
|
|
42
|
+
}
|
|
43
|
+
leave() {
|
|
44
|
+
return this.runtime.call(this.nodeType, this.key, 'leave', null, { action: true });
|
|
45
|
+
}
|
|
46
|
+
/** The first to join, or a developer. @throws {KitError} `forbidden`, `not_ready` (one fighter), `started`. */
|
|
47
|
+
start() {
|
|
48
|
+
return this.runtime.call(this.nodeType, this.key, 'start', null, { action: true });
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Reports where the player is; resolves with the position the hub kept (a move too fast is cut
|
|
52
|
+
* short, a jump refused). Not sent with an action id: the next report replaces it.
|
|
53
|
+
* @throws {KitError} `forbidden` (removed for movement the warden refused), `conflict` (down).
|
|
54
|
+
*/
|
|
55
|
+
move(x, z) {
|
|
56
|
+
return this.runtime.call(this.nodeType, this.key, 'move', { x, z });
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Casts `ability` aimed at `(x, z)`; resolves with whether a hit-scan strike hit someone at once
|
|
60
|
+
* (a projectile or an area lands later, as an `impact` event).
|
|
61
|
+
* @throws {KitError} `cooldown`, `insufficient` (resource), `out_of_reach`, `not_found` (not the class's).
|
|
62
|
+
*/
|
|
63
|
+
cast(ability, x, z) {
|
|
64
|
+
return this.runtime.call(this.nodeType, this.key, 'cast', { ability, x, z }, { action: true });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { HubState } from '../mirror.js';
|
|
2
|
+
import type { KitRuntime } from '../runtime.js';
|
|
3
|
+
export interface EncounterEnemy {
|
|
4
|
+
id: number;
|
|
5
|
+
kind: string;
|
|
6
|
+
x: number;
|
|
7
|
+
z: number;
|
|
8
|
+
hp: number;
|
|
9
|
+
max_hp: number;
|
|
10
|
+
boss: boolean;
|
|
11
|
+
/** Ticks until its next step. */
|
|
12
|
+
wait: number;
|
|
13
|
+
}
|
|
14
|
+
export interface EncounterTower {
|
|
15
|
+
x: number;
|
|
16
|
+
z: number;
|
|
17
|
+
owner: number;
|
|
18
|
+
}
|
|
19
|
+
/** What the `encounter` topic's snapshots carry. */
|
|
20
|
+
export interface EncounterView {
|
|
21
|
+
phase: 'lobby' | 'running' | 'won' | 'lost';
|
|
22
|
+
players: number[];
|
|
23
|
+
width: number;
|
|
24
|
+
height: number;
|
|
25
|
+
spawn: [number, number];
|
|
26
|
+
base: [number, number];
|
|
27
|
+
base_hp: number;
|
|
28
|
+
gold: number;
|
|
29
|
+
/** The wave under way. */
|
|
30
|
+
wave: number | null;
|
|
31
|
+
waves: number;
|
|
32
|
+
/** When the next wave starts, while one counts down (Unix ms). */
|
|
33
|
+
next_wave_at: number | null;
|
|
34
|
+
enemies: EncounterEnemy[];
|
|
35
|
+
towers: EncounterTower[];
|
|
36
|
+
boss_phase: number;
|
|
37
|
+
/** Kills by player id (the keys are the ids as text). */
|
|
38
|
+
kills: Record<string, number>;
|
|
39
|
+
}
|
|
40
|
+
/** Waves, towers and bosses (the `director` starter): {@link encounter} by key. */
|
|
41
|
+
export declare class EncountersKit {
|
|
42
|
+
private readonly runtime;
|
|
43
|
+
private readonly nodeType;
|
|
44
|
+
private readonly encounters;
|
|
45
|
+
constructor(runtime: KitRuntime, nodeType?: string);
|
|
46
|
+
encounter(key: string): Encounter;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* One encounter: waves of enemies down a lane to the players' base, towers the players build (a
|
|
50
|
+
* tower may not cut the lane), strikes on a cooldown, kills that pay gold, and a boss that changes
|
|
51
|
+
* phase as it weakens. {@link view} mirrors the `encounter` topic: snapshots once a second while it
|
|
52
|
+
* runs, and `event` messages (`type`: `wave_started`, `wave_cleared`, `boss_phase`, `won`, `lost`)
|
|
53
|
+
* through `view.onEvent`.
|
|
54
|
+
*/
|
|
55
|
+
export declare class Encounter {
|
|
56
|
+
private readonly runtime;
|
|
57
|
+
readonly nodeType: string;
|
|
58
|
+
readonly key: string;
|
|
59
|
+
readonly view: HubState<EncounterView>;
|
|
60
|
+
constructor(runtime: KitRuntime, nodeType: string, key: string);
|
|
61
|
+
watch(): Promise<EncounterView>;
|
|
62
|
+
unwatch(): Promise<void>;
|
|
63
|
+
/** @throws {KitError} `full`. */
|
|
64
|
+
join(): Promise<boolean>;
|
|
65
|
+
/** The first to join, or a developer. @throws {KitError} `forbidden`, `started`, `bad_request` (no way to the base). */
|
|
66
|
+
start(): Promise<boolean>;
|
|
67
|
+
/**
|
|
68
|
+
* Builds a tower at `(x, z)`; resolves with the gold left.
|
|
69
|
+
* @throws {KitError} `blocks_lane`, `insufficient` (gold), `conflict` (taken), `bad_request`, `not_member`.
|
|
70
|
+
*/
|
|
71
|
+
build(x: number, z: number): Promise<number>;
|
|
72
|
+
/** Strikes enemy `enemy`; resolves with its hp left. @throws {KitError} `cooldown`, `not_found`, `not_member`. */
|
|
73
|
+
hit(enemy: number): Promise<number>;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=encounters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encounters.d.ts","sourceRoot":"","sources":["../../../src/kit/modules/encounters.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACf;AAED,oDAAoD;AACpD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,KAAK,GAAG,MAAM,CAAC;IAC5C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxB,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,mFAAmF;AACnF,qBAAa,aAAa;IAItB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJ3B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgC;gBAGxC,OAAO,EAAE,UAAU,EACnB,QAAQ,SAAa;IAGxC,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS;CAQlC;AAED;;;;;;GAMG;AACH,qBAAa,SAAS;IAIlB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM;IALtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC;gBAGpB,OAAO,EAAE,UAAU,EAC3B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM;IAKtB,KAAK,IAAI,OAAO,CAAC,aAAa,CAAC;IAI/B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,iCAAiC;IACjC,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAIxB,wHAAwH;IACxH,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;IAIzB;;;OAGG;IACH,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI5C,kHAAkH;IAClH,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAGpC"}
|