@crowdedkingdoms/crowdyjs 8.2.0 → 8.4.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/README.md +81 -3
- package/dist/crowdy-client.d.ts.map +1 -1
- package/dist/crowdy-client.js +5 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/kit/blueprints/combat.d.ts +75 -0
- package/dist/kit/blueprints/combat.d.ts.map +1 -0
- package/dist/kit/blueprints/combat.js +351 -0
- package/dist/kit/blueprints/core.d.ts +222 -0
- package/dist/kit/blueprints/core.d.ts.map +1 -0
- package/dist/kit/blueprints/core.js +185 -0
- package/dist/kit/blueprints/decks.d.ts +50 -0
- package/dist/kit/blueprints/decks.d.ts.map +1 -0
- package/dist/kit/blueprints/decks.js +163 -0
- package/dist/kit/blueprints/economy.d.ts +88 -0
- package/dist/kit/blueprints/economy.d.ts.map +1 -0
- package/dist/kit/blueprints/economy.js +533 -0
- package/dist/kit/blueprints/guild.d.ts +51 -0
- package/dist/kit/blueprints/guild.d.ts.map +1 -0
- package/dist/kit/blueprints/guild.js +50 -0
- package/dist/kit/blueprints/index.d.ts +21 -0
- package/dist/kit/blueprints/index.d.ts.map +1 -0
- package/dist/kit/blueprints/index.js +20 -0
- package/dist/kit/blueprints/inventory.d.ts +38 -0
- package/dist/kit/blueprints/inventory.d.ts.map +1 -0
- package/dist/kit/blueprints/inventory.js +157 -0
- package/dist/kit/blueprints/leaderboards.d.ts +49 -0
- package/dist/kit/blueprints/leaderboards.d.ts.map +1 -0
- package/dist/kit/blueprints/leaderboards.js +126 -0
- package/dist/kit/blueprints/locks.d.ts +82 -0
- package/dist/kit/blueprints/locks.d.ts.map +1 -0
- package/dist/kit/blueprints/locks.js +155 -0
- package/dist/kit/blueprints/loot.d.ts +84 -0
- package/dist/kit/blueprints/loot.d.ts.map +1 -0
- package/dist/kit/blueprints/loot.js +228 -0
- package/dist/kit/blueprints/matches.d.ts +59 -0
- package/dist/kit/blueprints/matches.d.ts.map +1 -0
- package/dist/kit/blueprints/matches.js +246 -0
- package/dist/kit/blueprints/npcs.d.ts +71 -0
- package/dist/kit/blueprints/npcs.d.ts.map +1 -0
- package/dist/kit/blueprints/npcs.js +122 -0
- package/dist/kit/blueprints/plots.d.ts +54 -0
- package/dist/kit/blueprints/plots.d.ts.map +1 -0
- package/dist/kit/blueprints/plots.js +146 -0
- package/dist/kit/blueprints/progression.d.ts +68 -0
- package/dist/kit/blueprints/progression.d.ts.map +1 -0
- package/dist/kit/blueprints/progression.js +324 -0
- package/dist/kit/blueprints/quests.d.ts +65 -0
- package/dist/kit/blueprints/quests.d.ts.map +1 -0
- package/dist/kit/blueprints/quests.js +284 -0
- package/dist/kit/blueprints/worldsim.d.ts +84 -0
- package/dist/kit/blueprints/worldsim.d.ts.map +1 -0
- package/dist/kit/blueprints/worldsim.js +461 -0
- package/dist/kit/combat.d.ts +103 -0
- package/dist/kit/combat.d.ts.map +1 -0
- package/dist/kit/combat.js +151 -0
- package/dist/kit/decks.d.ts +123 -0
- package/dist/kit/decks.d.ts.map +1 -0
- package/dist/kit/decks.js +138 -0
- package/dist/kit/economy.d.ts +244 -0
- package/dist/kit/economy.d.ts.map +1 -0
- package/dist/kit/economy.js +365 -0
- package/dist/kit/features.d.ts +60 -0
- package/dist/kit/features.d.ts.map +1 -0
- package/dist/kit/features.js +65 -0
- package/dist/kit/index.d.ts +13 -2
- package/dist/kit/index.d.ts.map +1 -1
- package/dist/kit/index.js +12 -1
- package/dist/kit/inventory.d.ts +5 -0
- package/dist/kit/inventory.d.ts.map +1 -1
- package/dist/kit/inventory.js +14 -1
- package/dist/kit/kit.d.ts +58 -2
- package/dist/kit/kit.d.ts.map +1 -1
- package/dist/kit/kit.js +24 -2
- package/dist/kit/leaderboards.d.ts +66 -0
- package/dist/kit/leaderboards.d.ts.map +1 -0
- package/dist/kit/leaderboards.js +94 -0
- package/dist/kit/loot.d.ts +106 -0
- package/dist/kit/loot.d.ts.map +1 -0
- package/dist/kit/loot.js +120 -0
- package/dist/kit/matches.d.ts +140 -0
- package/dist/kit/matches.d.ts.map +1 -0
- package/dist/kit/matches.js +253 -0
- package/dist/kit/objects.js +1 -1
- package/dist/kit/plots.js +1 -1
- package/dist/kit/progression.d.ts +181 -0
- package/dist/kit/progression.d.ts.map +1 -0
- package/dist/kit/progression.js +265 -0
- package/dist/kit/quests.d.ts +110 -0
- package/dist/kit/quests.d.ts.map +1 -0
- package/dist/kit/quests.js +158 -0
- package/dist/kit/social.d.ts +267 -0
- package/dist/kit/social.d.ts.map +1 -0
- package/dist/kit/social.js +230 -0
- package/dist/kit/worldsim.d.ts +225 -0
- package/dist/kit/worldsim.d.ts.map +1 -0
- package/dist/kit/worldsim.js +231 -0
- package/dist/stores/actors.d.ts +316 -0
- package/dist/stores/actors.d.ts.map +1 -0
- package/dist/stores/actors.js +506 -0
- package/dist/stores/chunks.d.ts +180 -0
- package/dist/stores/chunks.d.ts.map +1 -0
- package/dist/stores/chunks.js +378 -0
- package/dist/stores/codec.d.ts +109 -0
- package/dist/stores/codec.d.ts.map +1 -0
- package/dist/stores/codec.js +186 -0
- package/dist/stores/durable.d.ts +144 -0
- package/dist/stores/durable.d.ts.map +1 -0
- package/dist/stores/durable.js +246 -0
- package/dist/stores/errors.d.ts +69 -0
- package/dist/stores/errors.d.ts.map +1 -0
- package/dist/stores/errors.js +87 -0
- package/dist/stores/inbox.d.ts +184 -0
- package/dist/stores/inbox.d.ts.map +1 -0
- package/dist/stores/inbox.js +326 -0
- package/dist/stores/index.d.ts +186 -0
- package/dist/stores/index.d.ts.map +1 -0
- package/dist/stores/index.js +109 -0
- package/dist/stores/keys.d.ts +52 -0
- package/dist/stores/keys.d.ts.map +1 -0
- package/dist/stores/keys.js +76 -0
- package/dist/stores/model.d.ts +81 -0
- package/dist/stores/model.d.ts.map +1 -0
- package/dist/stores/model.js +163 -0
- package/dist/stores/session.d.ts +119 -0
- package/dist/stores/session.d.ts.map +1 -0
- package/dist/stores/session.js +116 -0
- package/dist/stores/ticker.d.ts +44 -0
- package/dist/stores/ticker.d.ts.map +1 -0
- package/dist/stores/ticker.js +127 -0
- package/package.json +6 -1
- package/dist/kit/blueprints.d.ts +0 -370
- package/dist/kit/blueprints.d.ts.map +0 -1
- package/dist/kit/blueprints.js +0 -643
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* World Stores — opt-in, SDK-managed game state for CrowdyJS
|
|
3
|
+
* (`@crowdedkingdoms/crowdyjs/stores`).
|
|
4
|
+
*
|
|
5
|
+
* The core client is a thin transport; this layer adds the source-of-truth
|
|
6
|
+
* data structures every game otherwise hand-writes: typed codecs, actor
|
|
7
|
+
* registries, chunk/voxel caches, error attribution, message inboxes, host
|
|
8
|
+
* tracking, and durable-state wrappers. Import only what you use — the core
|
|
9
|
+
* bundle never includes this module, and unused stores tree-shake away
|
|
10
|
+
* (`"sideEffects": false`).
|
|
11
|
+
*
|
|
12
|
+
* Entry point: {@link createWorldSession}.
|
|
13
|
+
*/
|
|
14
|
+
export { bool8, bytes, f32, f64, i8, i16, i32, jsonCodec, rawCodec, reserved, structCodec, textCodec, u8, u16, u32, type StateCodec, type StructCodec, type StructField, type StructSpec, type StructValue, } from './codec.js';
|
|
15
|
+
export { CHUNK_SIZE, CHUNK_VOLUME, chunkDistance, chunkKey, chunksAround, fromChunkInput, parseChunkKey, toChunkInput, voxelCoordFromIndex, voxelIndex, worldToChunk, worldToLocalVoxel, type ChunkCoord, } from './keys.js';
|
|
16
|
+
export { intervalTicker, manualTicker, workerTicker, type ManualTicker, type Ticker, } from './ticker.js';
|
|
17
|
+
export { WorldSessionCore, type BusKey, type SentPacketKind, type SentPacketRecord, type WorldSessionBaseConfig, type WorldSessionContext, type WorldStoresClient, } from './session.js';
|
|
18
|
+
export { LocalActorStore, RemoteActorLane, RemoteActorStore, attachLocalActor, attachRemoteActors, localStorageUuidStore, memoryUuidStore, type AckedActorUpdate, type ActorSendError, type ActorUpdateEcho, type GenericErrorEcho, type LocalActorConfig, type LocalActorStatus, type RemoteActor, type RemoteActorSample, type RemoteActorsConfig, type SendReason, type SentActorUpdate, type UuidStore, } from './actors.js';
|
|
19
|
+
export { ErrorStore, attachErrorStore, type AttributedError, type ErrorStoreConfig, } from './errors.js';
|
|
20
|
+
export { ChunkStore, attachChunkStore, type CachedChunk, type ChunkLoadState, type ChunkStoreConfig, type SetVoxelInput, } from './chunks.js';
|
|
21
|
+
export { ActorInbox, ChannelInbox, EventRouter, attachActorInbox, attachChannelInbox, attachEventRouter, type ActorInboxConfig, type ChannelInboxConfig, type EventRouterConfig, type InboxMessage, type TypedEvent, } from './inbox.js';
|
|
22
|
+
export { ContainerMirror, attachContainerMirror, type ContainerMirrorConfig, type MirroredContainer, } from './model.js';
|
|
23
|
+
export { AvatarStateStore, HostTracker, SaveStateStore, attachAvatarState, attachHostTracker, attachSaveState, type AvatarStateConfig, type HostTrackerConfig, type SaveStateConfig, } from './durable.js';
|
|
24
|
+
import { LocalActorStore, RemoteActorStore, type LocalActorConfig, type RemoteActorsConfig } from './actors.js';
|
|
25
|
+
import type { StateCodec } from './codec.js';
|
|
26
|
+
import { ChunkStore, type ChunkStoreConfig } from './chunks.js';
|
|
27
|
+
import { AvatarStateStore, HostTracker, SaveStateStore, type AvatarStateConfig, type HostTrackerConfig, type SaveStateConfig } from './durable.js';
|
|
28
|
+
import { ContainerMirror, type ContainerMirrorConfig } from './model.js';
|
|
29
|
+
import { ErrorStore, type ErrorStoreConfig } from './errors.js';
|
|
30
|
+
import { ActorInbox, ChannelInbox, EventRouter, type ActorInboxConfig, type ChannelInboxConfig, type EventRouterConfig } from './inbox.js';
|
|
31
|
+
import { WorldSessionCore, type WorldSessionBaseConfig, type WorldStoresClient } from './session.js';
|
|
32
|
+
/**
|
|
33
|
+
* Configuration for {@link createWorldSession}: one optional key per store.
|
|
34
|
+
* Only configured stores are constructed — and, via {@link WorldSession}'s
|
|
35
|
+
* conditional typing, only configured stores exist on the returned session's
|
|
36
|
+
* TYPE, so touching an unconfigured store is a compile error, not a runtime
|
|
37
|
+
* surprise. Keys documented on each store's config type; `true` selects a
|
|
38
|
+
* store's defaults.
|
|
39
|
+
*/
|
|
40
|
+
export interface WorldSessionConfig extends WorldSessionBaseConfig {
|
|
41
|
+
/** Your own actor: identity, typed state, 5 Hz send loop ({@link LocalActorStore}). */
|
|
42
|
+
self?: LocalActorConfig<any>;
|
|
43
|
+
/**
|
|
44
|
+
* Remote actors: typed registry with lanes, history, staleness
|
|
45
|
+
* ({@link RemoteActorStore}). The self-echo filter is wired from `self`
|
|
46
|
+
* automatically; set `selfUuid` yourself otherwise.
|
|
47
|
+
*/
|
|
48
|
+
actors?: RemoteActorsConfig<any>;
|
|
49
|
+
/**
|
|
50
|
+
* Send-error log: attributes `GenericErrorResponse`s to the sends that
|
|
51
|
+
* caused them ({@link ErrorStore}).
|
|
52
|
+
*/
|
|
53
|
+
errors?: ErrorStoreConfig | true;
|
|
54
|
+
/**
|
|
55
|
+
* Chunk/voxel cache: bulk loading, typed states, realtime merge,
|
|
56
|
+
* optimistic edits, worldgen write-back ({@link ChunkStore}). The outbound
|
|
57
|
+
* sender uuid is wired from `self` automatically.
|
|
58
|
+
*/
|
|
59
|
+
chunks?: ChunkStoreConfig<any, any> | true;
|
|
60
|
+
/** Channel message inbox: per-channel typed history + send ({@link ChannelInbox}). */
|
|
61
|
+
channelInbox?: ChannelInboxConfig<any> | true;
|
|
62
|
+
/** Direct actor-to-actor message inbox ({@link ActorInbox}). */
|
|
63
|
+
actorInbox?: ActorInboxConfig<any> | true;
|
|
64
|
+
/**
|
|
65
|
+
* App-defined event router: per-eventType codecs + handlers, lastEvent
|
|
66
|
+
* cache ({@link EventRouter}).
|
|
67
|
+
*/
|
|
68
|
+
events?: EventRouterConfig | true;
|
|
69
|
+
/** Host election tracking: heartbeat loop + isHost ({@link HostTracker}). */
|
|
70
|
+
host?: HostTrackerConfig | true;
|
|
71
|
+
/** Typed per-user app save blob with debounced autosave ({@link SaveStateStore}). */
|
|
72
|
+
save?: SaveStateConfig<any> | true;
|
|
73
|
+
/** Typed avatar public/private/app state ({@link AvatarStateStore}). */
|
|
74
|
+
avatar?: AvatarStateConfig<any, any, any> | true;
|
|
75
|
+
/**
|
|
76
|
+
* Game-model mirror: typed cached container snapshots with coalesced
|
|
77
|
+
* refresh + notify-to-pull channel binding ({@link ContainerMirror}).
|
|
78
|
+
*/
|
|
79
|
+
model?: ContainerMirrorConfig | true;
|
|
80
|
+
}
|
|
81
|
+
/** Infer the payload type of a `codec` config field (else the default `D`). */
|
|
82
|
+
type CodecType<CC, D> = CC extends {
|
|
83
|
+
codec: StateCodec<infer T>;
|
|
84
|
+
} ? T : D;
|
|
85
|
+
type VoxelStateType<CC> = CC extends {
|
|
86
|
+
voxelStateCodec: StateCodec<infer T>;
|
|
87
|
+
} ? T : string;
|
|
88
|
+
type ChunkStateType<CC> = CC extends {
|
|
89
|
+
chunkStateCodec: StateCodec<infer T>;
|
|
90
|
+
} ? T : string;
|
|
91
|
+
type AvatarPublicType<CC> = CC extends {
|
|
92
|
+
publicCodec: StateCodec<infer T>;
|
|
93
|
+
} ? T : unknown;
|
|
94
|
+
type AvatarPrivateType<CC> = CC extends {
|
|
95
|
+
privateCodec: StateCodec<infer T>;
|
|
96
|
+
} ? T : unknown;
|
|
97
|
+
type AvatarAppType<CC> = CC extends {
|
|
98
|
+
appCodec: StateCodec<infer T>;
|
|
99
|
+
} ? T : unknown;
|
|
100
|
+
/** The parts of a {@link WorldSession} that exist regardless of config. */
|
|
101
|
+
export interface WorldSessionBase {
|
|
102
|
+
/** The app this session is scoped to. */
|
|
103
|
+
readonly appId: string;
|
|
104
|
+
/** Close the shared subscription, cancel timers, and release the stores. */
|
|
105
|
+
dispose(): void;
|
|
106
|
+
/** The wiring context (for custom store implementations). */
|
|
107
|
+
readonly context: WorldSessionCore;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The session returned by {@link createWorldSession}: the base surface plus
|
|
111
|
+
* exactly the stores your config declared, with every store's value types
|
|
112
|
+
* inferred from the codecs you passed. Annotate variables as
|
|
113
|
+
* `WorldSession<typeof config>` when you need a name for it.
|
|
114
|
+
*/
|
|
115
|
+
export type WorldSession<C extends WorldSessionConfig = WorldSessionConfig> = WorldSessionBase & (C extends {
|
|
116
|
+
self: LocalActorConfig<infer T>;
|
|
117
|
+
} ? {
|
|
118
|
+
readonly self: LocalActorStore<T>;
|
|
119
|
+
} : unknown) & (C extends {
|
|
120
|
+
actors: RemoteActorsConfig<infer T>;
|
|
121
|
+
} ? {
|
|
122
|
+
readonly actors: RemoteActorStore<T>;
|
|
123
|
+
} : unknown) & (C extends {
|
|
124
|
+
errors: ErrorStoreConfig | true;
|
|
125
|
+
} ? {
|
|
126
|
+
readonly errors: ErrorStore;
|
|
127
|
+
} : unknown) & (C extends {
|
|
128
|
+
chunks: infer CC;
|
|
129
|
+
} ? {
|
|
130
|
+
readonly chunks: ChunkStore<VoxelStateType<CC>, ChunkStateType<CC>>;
|
|
131
|
+
} : unknown) & (C extends {
|
|
132
|
+
channelInbox: infer CC;
|
|
133
|
+
} ? {
|
|
134
|
+
readonly channelInbox: ChannelInbox<CodecType<CC, string>>;
|
|
135
|
+
} : unknown) & (C extends {
|
|
136
|
+
actorInbox: infer CC;
|
|
137
|
+
} ? {
|
|
138
|
+
readonly actorInbox: ActorInbox<CodecType<CC, string>>;
|
|
139
|
+
} : unknown) & (C extends {
|
|
140
|
+
events: EventRouterConfig | true;
|
|
141
|
+
} ? {
|
|
142
|
+
readonly events: EventRouter;
|
|
143
|
+
} : unknown) & (C extends {
|
|
144
|
+
host: HostTrackerConfig | true;
|
|
145
|
+
} ? {
|
|
146
|
+
readonly host: HostTracker;
|
|
147
|
+
} : unknown) & (C extends {
|
|
148
|
+
save: infer CC;
|
|
149
|
+
} ? {
|
|
150
|
+
readonly save: SaveStateStore<CodecType<CC, unknown>>;
|
|
151
|
+
} : unknown) & (C extends {
|
|
152
|
+
avatar: infer CC;
|
|
153
|
+
} ? {
|
|
154
|
+
readonly avatar: AvatarStateStore<AvatarPublicType<CC>, AvatarPrivateType<CC>, AvatarAppType<CC>>;
|
|
155
|
+
} : unknown) & (C extends {
|
|
156
|
+
model: ContainerMirrorConfig | true;
|
|
157
|
+
} ? {
|
|
158
|
+
readonly model: ContainerMirror;
|
|
159
|
+
} : unknown);
|
|
160
|
+
/**
|
|
161
|
+
* Create a **world session**: one shared `udpNotifications` subscription
|
|
162
|
+
* fanned out to the stores you configure, one shared {@link Ticker}, and a
|
|
163
|
+
* single `dispose()`. Pass your `CrowdyClient` (it satisfies
|
|
164
|
+
* {@link WorldStoresClient} structurally) and the app id.
|
|
165
|
+
*
|
|
166
|
+
* ```ts
|
|
167
|
+
* import { createWorldSession, structCodec, f32, u8, jsonCodec, workerTicker } from '@crowdedkingdoms/crowdyjs/stores';
|
|
168
|
+
*
|
|
169
|
+
* const poseCodec = structCodec({ x: f32(), y: f32(), z: f32(), flags: u8() });
|
|
170
|
+
* const session = createWorldSession(client, appId, {
|
|
171
|
+
* ticker: workerTicker(), // hold 5 Hz in backgrounded tabs
|
|
172
|
+
* self: { codec: poseCodec, initialState: { x: 0, y: 0, z: 0, flags: 0 } },
|
|
173
|
+
* actors: { codec: poseCodec },
|
|
174
|
+
* errors: true,
|
|
175
|
+
* save: { codec: jsonCodec<{ level: number }>(), autosaveMs: 5000 },
|
|
176
|
+
* });
|
|
177
|
+
* await session.self.join({ x: '0', y: '0', z: '0' });
|
|
178
|
+
* session.self.patchState({ x: 12.5 }); // next tick replicates it
|
|
179
|
+
* for (const other of session.actors.list()) {} // typed poses, self filtered
|
|
180
|
+
* session.save.set({ level: 2 }); // autosaves, typed
|
|
181
|
+
* // session.chunks — compile error: not configured.
|
|
182
|
+
* session.dispose();
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
export declare function createWorldSession<const C extends WorldSessionConfig = Record<never, never>>(client: WorldStoresClient, appId: string, config?: C): WorldSession<C>;
|
|
186
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stores/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* World Stores — opt-in, SDK-managed game state for CrowdyJS
|
|
3
|
+
* (`@crowdedkingdoms/crowdyjs/stores`).
|
|
4
|
+
*
|
|
5
|
+
* The core client is a thin transport; this layer adds the source-of-truth
|
|
6
|
+
* data structures every game otherwise hand-writes: typed codecs, actor
|
|
7
|
+
* registries, chunk/voxel caches, error attribution, message inboxes, host
|
|
8
|
+
* tracking, and durable-state wrappers. Import only what you use — the core
|
|
9
|
+
* bundle never includes this module, and unused stores tree-shake away
|
|
10
|
+
* (`"sideEffects": false`).
|
|
11
|
+
*
|
|
12
|
+
* Entry point: {@link createWorldSession}.
|
|
13
|
+
*/
|
|
14
|
+
export { bool8, bytes, f32, f64, i8, i16, i32, jsonCodec, rawCodec, reserved, structCodec, textCodec, u8, u16, u32, } from './codec.js';
|
|
15
|
+
export { CHUNK_SIZE, CHUNK_VOLUME, chunkDistance, chunkKey, chunksAround, fromChunkInput, parseChunkKey, toChunkInput, voxelCoordFromIndex, voxelIndex, worldToChunk, worldToLocalVoxel, } from './keys.js';
|
|
16
|
+
export { intervalTicker, manualTicker, workerTicker, } from './ticker.js';
|
|
17
|
+
export { WorldSessionCore, } from './session.js';
|
|
18
|
+
export { LocalActorStore, RemoteActorLane, RemoteActorStore, attachLocalActor, attachRemoteActors, localStorageUuidStore, memoryUuidStore, } from './actors.js';
|
|
19
|
+
export { ErrorStore, attachErrorStore, } from './errors.js';
|
|
20
|
+
export { ChunkStore, attachChunkStore, } from './chunks.js';
|
|
21
|
+
export { ActorInbox, ChannelInbox, EventRouter, attachActorInbox, attachChannelInbox, attachEventRouter, } from './inbox.js';
|
|
22
|
+
export { ContainerMirror, attachContainerMirror, } from './model.js';
|
|
23
|
+
export { AvatarStateStore, HostTracker, SaveStateStore, attachAvatarState, attachHostTracker, attachSaveState, } from './durable.js';
|
|
24
|
+
import { LocalActorStore, RemoteActorStore, } from './actors.js';
|
|
25
|
+
import { ChunkStore } from './chunks.js';
|
|
26
|
+
import { AvatarStateStore, HostTracker, SaveStateStore, } from './durable.js';
|
|
27
|
+
import { ContainerMirror } from './model.js';
|
|
28
|
+
import { ErrorStore } from './errors.js';
|
|
29
|
+
import { ActorInbox, ChannelInbox, EventRouter, } from './inbox.js';
|
|
30
|
+
import { WorldSessionCore, } from './session.js';
|
|
31
|
+
/**
|
|
32
|
+
* Create a **world session**: one shared `udpNotifications` subscription
|
|
33
|
+
* fanned out to the stores you configure, one shared {@link Ticker}, and a
|
|
34
|
+
* single `dispose()`. Pass your `CrowdyClient` (it satisfies
|
|
35
|
+
* {@link WorldStoresClient} structurally) and the app id.
|
|
36
|
+
*
|
|
37
|
+
* ```ts
|
|
38
|
+
* import { createWorldSession, structCodec, f32, u8, jsonCodec, workerTicker } from '@crowdedkingdoms/crowdyjs/stores';
|
|
39
|
+
*
|
|
40
|
+
* const poseCodec = structCodec({ x: f32(), y: f32(), z: f32(), flags: u8() });
|
|
41
|
+
* const session = createWorldSession(client, appId, {
|
|
42
|
+
* ticker: workerTicker(), // hold 5 Hz in backgrounded tabs
|
|
43
|
+
* self: { codec: poseCodec, initialState: { x: 0, y: 0, z: 0, flags: 0 } },
|
|
44
|
+
* actors: { codec: poseCodec },
|
|
45
|
+
* errors: true,
|
|
46
|
+
* save: { codec: jsonCodec<{ level: number }>(), autosaveMs: 5000 },
|
|
47
|
+
* });
|
|
48
|
+
* await session.self.join({ x: '0', y: '0', z: '0' });
|
|
49
|
+
* session.self.patchState({ x: 12.5 }); // next tick replicates it
|
|
50
|
+
* for (const other of session.actors.list()) {} // typed poses, self filtered
|
|
51
|
+
* session.save.set({ level: 2 }); // autosaves, typed
|
|
52
|
+
* // session.chunks — compile error: not configured.
|
|
53
|
+
* session.dispose();
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export function createWorldSession(client, appId, config) {
|
|
57
|
+
const cfg = config ?? {};
|
|
58
|
+
const opt = (value) => value === true ? {} : value;
|
|
59
|
+
const core = new WorldSessionCore(client, appId, cfg.ticker);
|
|
60
|
+
// The error store registers the send-tracking sink — construct it first so
|
|
61
|
+
// the very first actor/chunk sends are already attributable.
|
|
62
|
+
const errors = cfg.errors ? new ErrorStore(core, opt(cfg.errors)) : undefined;
|
|
63
|
+
const self = cfg.self ? new LocalActorStore(core, cfg.self) : undefined;
|
|
64
|
+
const selfUuid = self ? () => self.uuid : undefined;
|
|
65
|
+
const actors = cfg.actors
|
|
66
|
+
? new RemoteActorStore(core, {
|
|
67
|
+
// Filter the local echo automatically when a self store exists.
|
|
68
|
+
selfUuid,
|
|
69
|
+
...cfg.actors,
|
|
70
|
+
})
|
|
71
|
+
: undefined;
|
|
72
|
+
const chunks = cfg.chunks
|
|
73
|
+
? new ChunkStore(core, {
|
|
74
|
+
// Stamp outbound voxel edits with the local actor's uuid.
|
|
75
|
+
actorUuid: selfUuid,
|
|
76
|
+
...opt(cfg.chunks),
|
|
77
|
+
})
|
|
78
|
+
: undefined;
|
|
79
|
+
const channelInbox = cfg.channelInbox
|
|
80
|
+
? new ChannelInbox(core, { senderUuid: selfUuid, ...opt(cfg.channelInbox) })
|
|
81
|
+
: undefined;
|
|
82
|
+
const actorInbox = cfg.actorInbox
|
|
83
|
+
? new ActorInbox(core, opt(cfg.actorInbox))
|
|
84
|
+
: undefined;
|
|
85
|
+
const events = cfg.events
|
|
86
|
+
? new EventRouter(core, { senderUuid: selfUuid, ...opt(cfg.events) })
|
|
87
|
+
: undefined;
|
|
88
|
+
const host = cfg.host ? new HostTracker(core, opt(cfg.host)) : undefined;
|
|
89
|
+
const save = cfg.save ? new SaveStateStore(core, opt(cfg.save)) : undefined;
|
|
90
|
+
const avatar = cfg.avatar ? new AvatarStateStore(core, opt(cfg.avatar)) : undefined;
|
|
91
|
+
const model = cfg.model ? new ContainerMirror(core, opt(cfg.model)) : undefined;
|
|
92
|
+
const session = {
|
|
93
|
+
appId,
|
|
94
|
+
context: core,
|
|
95
|
+
...(self ? { self } : {}),
|
|
96
|
+
...(actors ? { actors } : {}),
|
|
97
|
+
...(errors ? { errors } : {}),
|
|
98
|
+
...(chunks ? { chunks } : {}),
|
|
99
|
+
...(channelInbox ? { channelInbox } : {}),
|
|
100
|
+
...(actorInbox ? { actorInbox } : {}),
|
|
101
|
+
...(events ? { events } : {}),
|
|
102
|
+
...(host ? { host } : {}),
|
|
103
|
+
...(save ? { save } : {}),
|
|
104
|
+
...(avatar ? { avatar } : {}),
|
|
105
|
+
...(model ? { model } : {}),
|
|
106
|
+
dispose: () => core.dispose(),
|
|
107
|
+
};
|
|
108
|
+
return session;
|
|
109
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chunk/voxel coordinate math shared by the World Stores — the helpers every
|
|
3
|
+
* voxel game re-derives (chunk keys, world→chunk mapping, the 4096-slot voxel
|
|
4
|
+
* index). A chunk is a 16×16×16 voxel cube addressed by signed-int64 chunk
|
|
5
|
+
* coordinates (decimal strings on the wire).
|
|
6
|
+
*/
|
|
7
|
+
import type { ChunkCoordinatesInput } from '../generated/graphql.js';
|
|
8
|
+
/** Voxels per chunk axis. */
|
|
9
|
+
export declare const CHUNK_SIZE = 16;
|
|
10
|
+
/** Voxels per chunk (16³) — the length of a dense voxel array. */
|
|
11
|
+
export declare const CHUNK_VOLUME: number;
|
|
12
|
+
/** A chunk coordinate with numeric axes (convenient client-side form). */
|
|
13
|
+
export interface ChunkCoord {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
z: number;
|
|
17
|
+
}
|
|
18
|
+
/** The stable string key a chunk is cached under: `"x:y:z"`. */
|
|
19
|
+
export declare function chunkKey(coord: ChunkCoord | ChunkCoordinatesInput): string;
|
|
20
|
+
/** Parse a {@link chunkKey} back into numeric coordinates. */
|
|
21
|
+
export declare function parseChunkKey(key: string): ChunkCoord;
|
|
22
|
+
/** Convert a numeric chunk coordinate to the wire form (decimal strings). */
|
|
23
|
+
export declare function toChunkInput(coord: ChunkCoord): ChunkCoordinatesInput;
|
|
24
|
+
/** Convert a wire chunk coordinate (decimal strings) to numeric form. */
|
|
25
|
+
export declare function fromChunkInput(coord: ChunkCoordinatesInput): ChunkCoord;
|
|
26
|
+
/** The chunk containing a world-space position. */
|
|
27
|
+
export declare function worldToChunk(x: number, y: number, z: number): ChunkCoord;
|
|
28
|
+
/** The within-chunk voxel coordinate (0-15 per axis) of a world position. */
|
|
29
|
+
export declare function worldToLocalVoxel(x: number, y: number, z: number): {
|
|
30
|
+
x: number;
|
|
31
|
+
y: number;
|
|
32
|
+
z: number;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* The dense-array index of a within-chunk voxel coordinate:
|
|
36
|
+
* `x + y*16 + z*256` (the platform's chunk `voxels` layout).
|
|
37
|
+
*/
|
|
38
|
+
export declare function voxelIndex(x: number, y: number, z: number): number;
|
|
39
|
+
/** Invert {@link voxelIndex} back to within-chunk coordinates. */
|
|
40
|
+
export declare function voxelCoordFromIndex(index: number): {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
z: number;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Every chunk coordinate within a Chebyshev radius of `center` (the cube
|
|
47
|
+
* `(2r+1)³`, matching `chunks.byDistance` semantics), center first.
|
|
48
|
+
*/
|
|
49
|
+
export declare function chunksAround(center: ChunkCoord, radius: number): ChunkCoord[];
|
|
50
|
+
/** Chebyshev (chunk-grid) distance between two chunk coordinates. */
|
|
51
|
+
export declare function chunkDistance(a: ChunkCoord, b: ChunkCoord): number;
|
|
52
|
+
//# sourceMappingURL=keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/stores/keys.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAErE,6BAA6B;AAC7B,eAAO,MAAM,UAAU,KAAK,CAAC;AAC7B,kEAAkE;AAClE,eAAO,MAAM,YAAY,QAAuC,CAAC;AAEjE,0EAA0E;AAC1E,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,gEAAgE;AAChE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,qBAAqB,GAAG,MAAM,CAE1E;AAED,8DAA8D;AAC9D,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAGrD;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,qBAAqB,CAErE;AAED,yEAAyE;AACzE,wBAAgB,cAAc,CAAC,KAAK,EAAE,qBAAqB,GAAG,UAAU,CAEvE;AAED,mDAAmD;AACnD,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,UAAU,CAMxE;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAC/B,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACR;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAGrC;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,kEAAkE;AAClE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAMtF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,EAAE,CAW7E;AAED,qEAAqE;AACrE,wBAAgB,aAAa,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,MAAM,CAElE"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chunk/voxel coordinate math shared by the World Stores — the helpers every
|
|
3
|
+
* voxel game re-derives (chunk keys, world→chunk mapping, the 4096-slot voxel
|
|
4
|
+
* index). A chunk is a 16×16×16 voxel cube addressed by signed-int64 chunk
|
|
5
|
+
* coordinates (decimal strings on the wire).
|
|
6
|
+
*/
|
|
7
|
+
/** Voxels per chunk axis. */
|
|
8
|
+
export const CHUNK_SIZE = 16;
|
|
9
|
+
/** Voxels per chunk (16³) — the length of a dense voxel array. */
|
|
10
|
+
export const CHUNK_VOLUME = CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE;
|
|
11
|
+
/** The stable string key a chunk is cached under: `"x:y:z"`. */
|
|
12
|
+
export function chunkKey(coord) {
|
|
13
|
+
return `${coord.x}:${coord.y}:${coord.z}`;
|
|
14
|
+
}
|
|
15
|
+
/** Parse a {@link chunkKey} back into numeric coordinates. */
|
|
16
|
+
export function parseChunkKey(key) {
|
|
17
|
+
const [x, y, z] = key.split(':').map(Number);
|
|
18
|
+
return { x, y, z };
|
|
19
|
+
}
|
|
20
|
+
/** Convert a numeric chunk coordinate to the wire form (decimal strings). */
|
|
21
|
+
export function toChunkInput(coord) {
|
|
22
|
+
return { x: String(coord.x), y: String(coord.y), z: String(coord.z) };
|
|
23
|
+
}
|
|
24
|
+
/** Convert a wire chunk coordinate (decimal strings) to numeric form. */
|
|
25
|
+
export function fromChunkInput(coord) {
|
|
26
|
+
return { x: Number(coord.x), y: Number(coord.y), z: Number(coord.z) };
|
|
27
|
+
}
|
|
28
|
+
/** The chunk containing a world-space position. */
|
|
29
|
+
export function worldToChunk(x, y, z) {
|
|
30
|
+
return {
|
|
31
|
+
x: Math.floor(x / CHUNK_SIZE),
|
|
32
|
+
y: Math.floor(y / CHUNK_SIZE),
|
|
33
|
+
z: Math.floor(z / CHUNK_SIZE),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** The within-chunk voxel coordinate (0-15 per axis) of a world position. */
|
|
37
|
+
export function worldToLocalVoxel(x, y, z) {
|
|
38
|
+
const mod = (n) => ((Math.floor(n) % CHUNK_SIZE) + CHUNK_SIZE) % CHUNK_SIZE;
|
|
39
|
+
return { x: mod(x), y: mod(y), z: mod(z) };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The dense-array index of a within-chunk voxel coordinate:
|
|
43
|
+
* `x + y*16 + z*256` (the platform's chunk `voxels` layout).
|
|
44
|
+
*/
|
|
45
|
+
export function voxelIndex(x, y, z) {
|
|
46
|
+
return x + y * CHUNK_SIZE + z * CHUNK_SIZE * CHUNK_SIZE;
|
|
47
|
+
}
|
|
48
|
+
/** Invert {@link voxelIndex} back to within-chunk coordinates. */
|
|
49
|
+
export function voxelCoordFromIndex(index) {
|
|
50
|
+
return {
|
|
51
|
+
x: index % CHUNK_SIZE,
|
|
52
|
+
y: Math.floor(index / CHUNK_SIZE) % CHUNK_SIZE,
|
|
53
|
+
z: Math.floor(index / (CHUNK_SIZE * CHUNK_SIZE)),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Every chunk coordinate within a Chebyshev radius of `center` (the cube
|
|
58
|
+
* `(2r+1)³`, matching `chunks.byDistance` semantics), center first.
|
|
59
|
+
*/
|
|
60
|
+
export function chunksAround(center, radius) {
|
|
61
|
+
const out = [center];
|
|
62
|
+
for (let dx = -radius; dx <= radius; dx++) {
|
|
63
|
+
for (let dy = -radius; dy <= radius; dy++) {
|
|
64
|
+
for (let dz = -radius; dz <= radius; dz++) {
|
|
65
|
+
if (dx === 0 && dy === 0 && dz === 0)
|
|
66
|
+
continue;
|
|
67
|
+
out.push({ x: center.x + dx, y: center.y + dy, z: center.z + dz });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
/** Chebyshev (chunk-grid) distance between two chunk coordinates. */
|
|
74
|
+
export function chunkDistance(a, b) {
|
|
75
|
+
return Math.max(Math.abs(a.x - b.x), Math.abs(a.y - b.y), Math.abs(a.z - b.z));
|
|
76
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContainerMirror — the client half of the platform's **notify-to-pull**
|
|
3
|
+
* pattern for game-model state: keep typed snapshots of the containers you
|
|
4
|
+
* care about, re-pull them on demand or whenever a bound channel pings
|
|
5
|
+
* ("state changed"), and render straight from the cache.
|
|
6
|
+
*
|
|
7
|
+
* Model changes are pull-based on this platform (there is no model
|
|
8
|
+
* subscription); functions declare channel/spatial notifications and clients
|
|
9
|
+
* re-read. The Game Kit's match layer pings a per-match channel — bind the
|
|
10
|
+
* mirror to that channel and every watched container refreshes itself.
|
|
11
|
+
*/
|
|
12
|
+
import type { WorldSessionContext } from './session.js';
|
|
13
|
+
/** A typed snapshot of one watched container. */
|
|
14
|
+
export interface MirroredContainer<T = Record<string, unknown>> {
|
|
15
|
+
readonly containerId: string;
|
|
16
|
+
typeName: string;
|
|
17
|
+
displayName: string;
|
|
18
|
+
ownerUserId: string | null;
|
|
19
|
+
/** The parsed, caller-visible properties. */
|
|
20
|
+
value: T;
|
|
21
|
+
/** Bumped on every refresh that changed the snapshot. */
|
|
22
|
+
revision: number;
|
|
23
|
+
/** Local time of the last refresh. */
|
|
24
|
+
refreshedAt: number;
|
|
25
|
+
}
|
|
26
|
+
/** Options for {@link attachContainerMirror}. */
|
|
27
|
+
export interface ContainerMirrorConfig {
|
|
28
|
+
/** Clock override for tests. Defaults to `Date.now`. */
|
|
29
|
+
now?: () => number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The SDK-managed **model mirror**: typed, cached snapshots of watched
|
|
33
|
+
* game-model containers with coalesced refresh and channel-ping binding.
|
|
34
|
+
* Reads are synchronous ({@link get}); {@link onChange} fires only when a
|
|
35
|
+
* refresh actually changed the visible state.
|
|
36
|
+
*/
|
|
37
|
+
export declare class ContainerMirror {
|
|
38
|
+
private readonly ctx;
|
|
39
|
+
private readonly watches;
|
|
40
|
+
private readonly listeners;
|
|
41
|
+
private readonly boundChannels;
|
|
42
|
+
private readonly now;
|
|
43
|
+
private refreshing;
|
|
44
|
+
private refreshQueued;
|
|
45
|
+
constructor(ctx: WorldSessionContext, config?: ContainerMirrorConfig);
|
|
46
|
+
/**
|
|
47
|
+
* Watch a container: fetches the initial snapshot and keeps it refreshable.
|
|
48
|
+
* `parse` maps the visible properties object to your type (defaults to the
|
|
49
|
+
* raw object).
|
|
50
|
+
*/
|
|
51
|
+
watch<T = Record<string, unknown>>(containerId: string, parse?: (properties: Record<string, unknown>) => T): Promise<MirroredContainer<T>>;
|
|
52
|
+
/** Stop watching a container (its snapshot is dropped). */
|
|
53
|
+
unwatch(containerId: string): void;
|
|
54
|
+
/** The current snapshot of a watched container (undefined before watch resolves). */
|
|
55
|
+
get<T = Record<string, unknown>>(containerId: string): MirroredContainer<T> | undefined;
|
|
56
|
+
/** Every watched snapshot. */
|
|
57
|
+
list(): Array<MirroredContainer<unknown>>;
|
|
58
|
+
/**
|
|
59
|
+
* Subscribe to snapshot changes — every watched container, or one
|
|
60
|
+
* `containerId`. Fires only when a refresh changed the visible state.
|
|
61
|
+
* @returns off.
|
|
62
|
+
*/
|
|
63
|
+
onChange(handler: (container: MirroredContainer<unknown>) => void, containerId?: string): () => void;
|
|
64
|
+
/**
|
|
65
|
+
* Bind a channel: any message on it triggers a coalesced {@link refreshAll}
|
|
66
|
+
* — pair with model functions that declare channel notifications (e.g. the
|
|
67
|
+
* Game Kit's `match_changed` pings).
|
|
68
|
+
*/
|
|
69
|
+
bindToChannel(channelId: string): () => void;
|
|
70
|
+
/** Re-pull one watched container now. */
|
|
71
|
+
refresh(containerId: string): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Re-pull every watched container. Concurrent calls coalesce: a refresh
|
|
74
|
+
* requested while one is running queues exactly one follow-up pass (pings
|
|
75
|
+
* can burst; state converges without stampeding the API).
|
|
76
|
+
*/
|
|
77
|
+
refreshAll(): Promise<void>;
|
|
78
|
+
}
|
|
79
|
+
/** Attach a {@link ContainerMirror}. Prefer the `model` config key. */
|
|
80
|
+
export declare function attachContainerMirror(ctx: WorldSessionContext, config?: ContainerMirrorConfig): ContainerMirror;
|
|
81
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/stores/model.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAExD,iDAAiD;AACjD,MAAM,WAAW,iBAAiB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC5D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,6CAA6C;IAC7C,KAAK,EAAE,CAAC,CAAC;IACT,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,iDAAiD;AACjD,MAAM,WAAW,qBAAqB;IACpC,wDAAwD;IACxD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AASD;;;;;GAKG;AACH,qBAAa,eAAe;IAYxB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAXtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;IACzD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAGrB;IACL,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAS;gBAGX,GAAG,EAAE,mBAAmB,EACzC,MAAM,GAAE,qBAA0B;IAcpC;;;;OAIG;IACG,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,GACjD,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAWhC,2DAA2D;IAC3D,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAIlC,qFAAqF;IACrF,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,iBAAiB,CAAC,CAAC,CAAC,GAAG,SAAS;IAMvF,8BAA8B;IAC9B,IAAI,IAAI,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAQzC;;;;OAIG;IACH,QAAQ,CACN,OAAO,EAAE,CAAC,SAAS,EAAE,iBAAiB,CAAC,OAAO,CAAC,KAAK,IAAI,EACxD,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,IAAI;IAMb;;;;OAIG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,IAAI;IAK5C,yCAAyC;IACnC,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiDjD;;;;OAIG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAqBlC;AAED,uEAAuE;AACvE,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,mBAAmB,EACxB,MAAM,GAAE,qBAA0B,GACjC,eAAe,CAEjB"}
|