@colyseus/core 0.17.43 → 0.18.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/build/MatchMaker.cjs +19 -6
- package/build/MatchMaker.cjs.map +2 -2
- package/build/MatchMaker.d.ts +10 -0
- package/build/MatchMaker.mjs +18 -6
- package/build/MatchMaker.mjs.map +2 -2
- package/build/Protocol.cjs +102 -37
- package/build/Protocol.cjs.map +2 -2
- package/build/Protocol.d.ts +33 -2
- package/build/Protocol.mjs +102 -37
- package/build/Protocol.mjs.map +2 -2
- package/build/Room.cjs +296 -19
- package/build/Room.cjs.map +3 -3
- package/build/Room.d.ts +186 -3
- package/build/Room.mjs +303 -21
- package/build/Room.mjs.map +3 -3
- package/build/RoomPlugin.cjs +252 -0
- package/build/RoomPlugin.cjs.map +7 -0
- package/build/RoomPlugin.d.ts +271 -0
- package/build/RoomPlugin.mjs +220 -0
- package/build/RoomPlugin.mjs.map +7 -0
- package/build/Server.cjs +49 -15
- package/build/Server.cjs.map +2 -2
- package/build/Server.d.ts +25 -0
- package/build/Server.mjs +50 -16
- package/build/Server.mjs.map +2 -2
- package/build/Transport.cjs +38 -2
- package/build/Transport.cjs.map +2 -2
- package/build/Transport.d.ts +40 -4
- package/build/Transport.mjs +38 -2
- package/build/Transport.mjs.map +2 -2
- package/build/index.cjs +11 -2
- package/build/index.cjs.map +2 -2
- package/build/index.d.ts +2 -1
- package/build/index.mjs +12 -2
- package/build/index.mjs.map +2 -2
- package/build/input/InputBuffer.cjs +113 -0
- package/build/input/InputBuffer.cjs.map +7 -0
- package/build/input/InputBuffer.d.ts +136 -0
- package/build/input/InputBuffer.mjs +86 -0
- package/build/input/InputBuffer.mjs.map +7 -0
- package/build/internal.cjs +61 -0
- package/build/internal.cjs.map +7 -0
- package/build/internal.d.ts +9 -0
- package/build/internal.mjs +29 -0
- package/build/internal.mjs.map +7 -0
- package/build/matchmaker/LocalDriver/LocalDriver.cjs +13 -0
- package/build/matchmaker/LocalDriver/LocalDriver.cjs.map +2 -2
- package/build/matchmaker/LocalDriver/LocalDriver.d.ts +1 -0
- package/build/matchmaker/LocalDriver/LocalDriver.mjs +13 -0
- package/build/matchmaker/LocalDriver/LocalDriver.mjs.map +2 -2
- package/build/matchmaker/driver.cjs.map +1 -1
- package/build/matchmaker/driver.d.ts +12 -0
- package/build/matchmaker/driver.mjs.map +1 -1
- package/build/presence/LocalPresence.d.ts +1 -1
- package/build/rooms/LobbyRoom.cjs +8 -10
- package/build/rooms/LobbyRoom.cjs.map +2 -2
- package/build/rooms/LobbyRoom.d.ts +4 -3
- package/build/rooms/LobbyRoom.mjs +8 -10
- package/build/rooms/LobbyRoom.mjs.map +2 -2
- package/build/rooms/RelayRoom.cjs +12 -16
- package/build/rooms/RelayRoom.cjs.map +2 -2
- package/build/rooms/RelayRoom.d.ts +32 -11
- package/build/rooms/RelayRoom.mjs +10 -16
- package/build/rooms/RelayRoom.mjs.map +2 -2
- package/build/router/index.cjs +65 -4
- package/build/router/index.cjs.map +2 -2
- package/build/router/index.d.ts +30 -6
- package/build/router/index.mjs +66 -6
- package/build/router/index.mjs.map +3 -3
- package/build/utils/Env.cjs +4 -8
- package/build/utils/Env.cjs.map +3 -3
- package/build/utils/Env.mjs +4 -8
- package/build/utils/Env.mjs.map +2 -2
- package/build/utils/UserSessionIndex.cjs +162 -0
- package/build/utils/UserSessionIndex.cjs.map +7 -0
- package/build/utils/UserSessionIndex.d.ts +166 -0
- package/build/utils/UserSessionIndex.mjs +130 -0
- package/build/utils/UserSessionIndex.mjs.map +7 -0
- package/package.json +20 -15
- package/src/MatchMaker.ts +40 -6
- package/src/Protocol.ts +130 -59
- package/src/Room.ts +475 -22
- package/src/RoomPlugin.ts +563 -0
- package/src/Server.ts +81 -22
- package/src/Transport.ts +76 -8
- package/src/index.ts +10 -1
- package/src/input/InputBuffer.ts +192 -0
- package/src/internal.ts +46 -0
- package/src/matchmaker/LocalDriver/LocalDriver.ts +10 -0
- package/src/matchmaker/driver.ts +13 -0
- package/src/rooms/LobbyRoom.ts +12 -8
- package/src/rooms/RelayRoom.ts +9 -15
- package/src/router/index.ts +112 -11
- package/src/utils/Env.ts +4 -12
- package/src/utils/UserSessionIndex.ts +311 -0
package/build/Room.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type InputAPI, type NumericFieldsOf } from './input/InputBuffer.ts';
|
|
2
|
+
export { type InputAccessor, type InputAPI, type InputOptions, type NumericFieldsOf } from './input/InputBuffer.ts';
|
|
1
3
|
import { ClockTimer as Clock } from '@colyseus/timer';
|
|
2
4
|
import type { Presence } from './presence/Presence.ts';
|
|
3
5
|
import type { Serializer } from './serializer/Serializer.ts';
|
|
@@ -6,12 +8,24 @@ import { type AuthContext, type Client, ClientArray, type ISendOptions, type Mes
|
|
|
6
8
|
import { type RoomMethodName, type RoomException } from './errors/RoomExceptions.ts';
|
|
7
9
|
import { type StandardSchemaV1 } from './utils/StandardSchema.ts';
|
|
8
10
|
import { type MessageHandlerWithFormat as SharedMessageHandlerWithFormat, type MessageHandler as SharedMessageHandler, type Messages as SharedMessages } from '@colyseus/shared-types';
|
|
11
|
+
import { RoomPlugin, type PluginLayout } from './RoomPlugin.ts';
|
|
12
|
+
export { RoomPlugin, definePlugins, attachToTestRoom, type RoomPluginOrder, } from './RoomPlugin.ts';
|
|
9
13
|
export declare const DEFAULT_SEAT_RESERVATION_TIME: number;
|
|
10
14
|
export type SimulationCallback = (deltaTime: number) => void;
|
|
11
15
|
export interface RoomOptions {
|
|
12
16
|
state?: object;
|
|
13
17
|
metadata?: any;
|
|
14
18
|
client?: Client;
|
|
19
|
+
/**
|
|
20
|
+
* Schema class for client→server input packets. When set, the Room
|
|
21
|
+
* allocates one instance per joining client and binds an InputDecoder.
|
|
22
|
+
* Must be a flat Schema (primitive fields only — see InputEncoder docs).
|
|
23
|
+
*
|
|
24
|
+
* Typed loosely (no `Schema` constraint) to avoid type-identity clashes
|
|
25
|
+
* when the user's app loads a different copy of `@colyseus/schema` than
|
|
26
|
+
* `@colyseus/core` does. Runtime validation happens via the encoder.
|
|
27
|
+
*/
|
|
28
|
+
input?: any;
|
|
15
29
|
}
|
|
16
30
|
export type ExtractRoomState<T> = T extends {
|
|
17
31
|
state?: infer S extends object;
|
|
@@ -22,6 +36,9 @@ export type ExtractRoomMetadata<T> = T extends {
|
|
|
22
36
|
export type ExtractRoomClient<T> = T extends {
|
|
23
37
|
client?: infer C extends Client;
|
|
24
38
|
} ? C : Client;
|
|
39
|
+
export type ExtractRoomInput<T> = T extends {
|
|
40
|
+
input?: infer I;
|
|
41
|
+
} ? I : never;
|
|
25
42
|
export interface IBroadcastOptions extends ISendOptions {
|
|
26
43
|
except?: Client | Client[];
|
|
27
44
|
}
|
|
@@ -172,6 +189,44 @@ export declare class Room<T extends RoomOptions = RoomOptions> {
|
|
|
172
189
|
private _reconnections;
|
|
173
190
|
private _reconnectionAttempts;
|
|
174
191
|
messages?: Messages<any>;
|
|
192
|
+
/**
|
|
193
|
+
* Room plugins, keyed by an operator-chosen handle. Each plugin
|
|
194
|
+
* contributes any subset of: declarative message handlers (merged
|
|
195
|
+
* into `this.messages`), lifecycle hooks (composed with the room's
|
|
196
|
+
* own), and public methods callable via `this.plugins.<key>.X()`.
|
|
197
|
+
*
|
|
198
|
+
* The framework walks this record once per Room subclass to compute
|
|
199
|
+
* the lifecycle/message layout and install hook wrappers on the
|
|
200
|
+
* class prototype; subsequent constructs reuse the cached layout
|
|
201
|
+
* and just inject `.room` + merge messages.
|
|
202
|
+
*
|
|
203
|
+
* Use `definePlugins({...})` so TypeScript preserves each plugin's
|
|
204
|
+
* literal instance type. Frozen after `__init`.
|
|
205
|
+
*/
|
|
206
|
+
plugins?: any;
|
|
207
|
+
/**
|
|
208
|
+
* Auto-included plugin instances pulled in via `static
|
|
209
|
+
* dependencies` declarations on user-registered plugins. Kept
|
|
210
|
+
* separate from `this.plugins` so the user's typed view doesn't
|
|
211
|
+
* gain framework-managed keys. Sentinel-keyed (`__dep:<ClassName>`)
|
|
212
|
+
* so the hook wrappers can route lookups to the right map.
|
|
213
|
+
*
|
|
214
|
+
* @internal
|
|
215
|
+
*/
|
|
216
|
+
_autoPlugins?: Record<string, RoomPlugin<any>>;
|
|
217
|
+
/**
|
|
218
|
+
* Layout cache populated on the FIRST construction of each Room
|
|
219
|
+
* subclass. Holds the precomputed hook participation order + message
|
|
220
|
+
* key → plugin key mapping. Stored on the constructor (a static field)
|
|
221
|
+
* so all instances of the same class share it.
|
|
222
|
+
*
|
|
223
|
+
* `null` is a sentinel meaning "no plugins on this class" — distinct
|
|
224
|
+
* from `undefined` ("not yet computed") so we don't re-walk an empty
|
|
225
|
+
* plugin record on every construct.
|
|
226
|
+
*
|
|
227
|
+
* @internal
|
|
228
|
+
*/
|
|
229
|
+
static __pluginLayout?: PluginLayout | null;
|
|
175
230
|
private onMessageEvents;
|
|
176
231
|
private onMessageValidators;
|
|
177
232
|
private onMessageFallbacks;
|
|
@@ -248,6 +303,64 @@ export declare class Room<T extends RoomOptions = RoomOptions> {
|
|
|
248
303
|
* @param code - The close code of the leave event.
|
|
249
304
|
*/
|
|
250
305
|
onLeave?(client: ExtractRoomClient<T>, code?: number): void | Promise<any>;
|
|
306
|
+
/**
|
|
307
|
+
* Per-client input accessor. Set by `defineInput()`. Call `room.input(sessionId)`
|
|
308
|
+
* each tick to read the latest decoded input and/or the buffered snapshot ring
|
|
309
|
+
* for that client.
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* ```typescript
|
|
313
|
+
* class FpsRoom extends Room<{ input: MoveInput }> {
|
|
314
|
+
* input = this.defineInput(MoveInput);
|
|
315
|
+
*
|
|
316
|
+
* onCreate() {
|
|
317
|
+
* this.setSimulationInterval(() => {
|
|
318
|
+
* for (const c of this.clients) {
|
|
319
|
+
* const input = this.input(c.sessionId);
|
|
320
|
+
* if (input.latest) this.apply(c, input.latest);
|
|
321
|
+
* // for rollback / lockstep:
|
|
322
|
+
* // const snapshot = input.at(this.clock.ticks);
|
|
323
|
+
* // for (const snapshot of input.drain()) ...
|
|
324
|
+
* }
|
|
325
|
+
* }, 1000 / 30);
|
|
326
|
+
* }
|
|
327
|
+
* }
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
330
|
+
input?: InputAPI<ExtractRoomInput<T>>;
|
|
331
|
+
/**
|
|
332
|
+
* Input configuration. Set via {@link defineInput} only.
|
|
333
|
+
* @internal
|
|
334
|
+
*/
|
|
335
|
+
private inputOptions?;
|
|
336
|
+
/**
|
|
337
|
+
* Declare the input schema and configuration in a single line. Returns the
|
|
338
|
+
* callable accessor that gets assigned to `this.input` — call
|
|
339
|
+
* `this.input(sessionId)` per tick to consume.
|
|
340
|
+
*
|
|
341
|
+
* ```typescript
|
|
342
|
+
* class FpsRoom extends Room<{ input: MoveInput }> {
|
|
343
|
+
* input = this.defineInput(MoveInput, {
|
|
344
|
+
* seqField: "tick", // typed: only numeric fields of MoveInput
|
|
345
|
+
* bufferMaxSize: 64,
|
|
346
|
+
* });
|
|
347
|
+
*
|
|
348
|
+
* // …or without options — defaults to seqField: "seq", bufferMaxSize: 32:
|
|
349
|
+
* // input = this.defineInput(MoveInput);
|
|
350
|
+
* }
|
|
351
|
+
* ```
|
|
352
|
+
*
|
|
353
|
+
* **Defaults** when `opts` (or individual fields) are omitted:
|
|
354
|
+
* - `seqField`: `"seq"` — framework dedupes by `input.seq` if the schema has
|
|
355
|
+
* such a field. Schemas without it gracefully skip dedupe.
|
|
356
|
+
* - `bufferMaxSize`: `32` — enables per-client snapshot buffering for
|
|
357
|
+
* `room.input(sessionId).drain() / .peek() / .at()`. Set to `0` to disable
|
|
358
|
+
* buffering (the `.latest` read still works).
|
|
359
|
+
*/
|
|
360
|
+
protected defineInput<C extends new () => any>(type: C, opts?: {
|
|
361
|
+
seqField?: NumericFieldsOf<InstanceType<C>>;
|
|
362
|
+
bufferMaxSize?: number;
|
|
363
|
+
}): InputAPI<InstanceType<C>>;
|
|
251
364
|
/**
|
|
252
365
|
* This method is called when the room is disposed.
|
|
253
366
|
*/
|
|
@@ -326,6 +439,38 @@ export declare class Room<T extends RoomOptions = RoomOptions> {
|
|
|
326
439
|
* @param delay - Interval delay on executing `onTickCallback` in milliseconds.
|
|
327
440
|
*/
|
|
328
441
|
setSimulationInterval(onTickCallback?: SimulationCallback, delay?: number): void;
|
|
442
|
+
/**
|
|
443
|
+
* Run a fixed-rate simulation tagged with a monotonic server tick number.
|
|
444
|
+
* Combine with `room.input(sessionId).at(tick)` to retrieve each client's
|
|
445
|
+
* input *for a specific tick* — the building block for lockstep / rollback
|
|
446
|
+
* netcode.
|
|
447
|
+
*
|
|
448
|
+
* Replaces any previous {@link setSimulationInterval}. The current tick is
|
|
449
|
+
* exposed via {@link tick}.
|
|
450
|
+
*
|
|
451
|
+
* @example
|
|
452
|
+
* ```typescript
|
|
453
|
+
* class LockstepRoom extends Room<{ input: MoveInput }> {
|
|
454
|
+
* input = this.defineInput(MoveInput, { seqField: "tick", bufferMaxSize: 64 });
|
|
455
|
+
*
|
|
456
|
+
* onCreate() {
|
|
457
|
+
* this.setTickedSimulation((tick, dt) => {
|
|
458
|
+
* for (const c of this.clients) {
|
|
459
|
+
* const snapshot = this.input(c.sessionId).at(tick);
|
|
460
|
+
* if (snapshot) this.apply(c, snapshot);
|
|
461
|
+
* // else: predict, freeze, etc. — game-level decision
|
|
462
|
+
* }
|
|
463
|
+
* }, 1000 / 60);
|
|
464
|
+
* }
|
|
465
|
+
* }
|
|
466
|
+
* ```
|
|
467
|
+
*/
|
|
468
|
+
setTickedSimulation(onTickCallback: (tick: number, deltaTime: number) => void, delay?: number, startTick?: number): void;
|
|
469
|
+
/**
|
|
470
|
+
* Current server tick. Incremented by {@link setTickedSimulation} after each
|
|
471
|
+
* tick callback returns. Returns 0 when no ticked simulation is running.
|
|
472
|
+
*/
|
|
473
|
+
get tick(): number;
|
|
329
474
|
/**
|
|
330
475
|
* @deprecated Use `.patchRate=` instead.
|
|
331
476
|
*/
|
|
@@ -335,7 +480,7 @@ export declare class Room<T extends RoomOptions = RoomOptions> {
|
|
|
335
480
|
*/
|
|
336
481
|
setState(newState: ExtractRoomState<T>): void;
|
|
337
482
|
setSerializer(serializer: Serializer<ExtractRoomState<T>>): void;
|
|
338
|
-
setMetadata(meta:
|
|
483
|
+
setMetadata(meta: ExtractRoomMetadata<T>, persist?: boolean): Promise<void>;
|
|
339
484
|
setPrivate(bool?: boolean, persist?: boolean): Promise<void>;
|
|
340
485
|
/**
|
|
341
486
|
* Update multiple matchmaking/listing properties at once with a single persist operation.
|
|
@@ -364,7 +509,8 @@ export declare class Room<T extends RoomOptions = RoomOptions> {
|
|
|
364
509
|
*
|
|
365
510
|
* @example
|
|
366
511
|
* ```typescript
|
|
367
|
-
* //
|
|
512
|
+
* // Merging with existing metadata: spread `this.metadata` yourself.
|
|
513
|
+
* // `metadata` is always REPLACED (not merged) by setMatchmaking()/setMetadata().
|
|
368
514
|
* await this.setMatchmaking({
|
|
369
515
|
* metadata: { ...this.metadata, round: this.metadata.round + 1 }
|
|
370
516
|
* });
|
|
@@ -439,6 +585,44 @@ export declare class Room<T extends RoomOptions = RoomOptions> {
|
|
|
439
585
|
onMessage<T = any, C extends Client = ExtractRoomClient<T>>(messageType: string | number, validationSchema: StandardSchemaV1<T>, callback: (client: C, message: T) => void): any;
|
|
440
586
|
onMessageBytes<T = any, C extends Client = ExtractRoomClient<T>>(messageType: string | number, callback: (client: C, message: T) => void): any;
|
|
441
587
|
onMessageBytes<T = any, C extends Client = ExtractRoomClient<T>>(messageType: string | number, validationSchema: StandardSchemaV1<T>, callback: (client: C, message: T) => void): any;
|
|
588
|
+
/**
|
|
589
|
+
* Snapshot the room's live state for an inspector / admin UI. Includes:
|
|
590
|
+
*
|
|
591
|
+
* roomId, name, maxClients, locked, elapsedTime (ms),
|
|
592
|
+
* metadata, clients (sessionId + per-client elapsed + userId when set),
|
|
593
|
+
* state (the schema/json the SDK would see),
|
|
594
|
+
* stateSize (bytes of the encoded full state, or 0 when no serializer).
|
|
595
|
+
*
|
|
596
|
+
* The payload is intentionally plain JSON — `remoteRoomCall` serializes
|
|
597
|
+
* the return value across process boundaries.
|
|
598
|
+
*
|
|
599
|
+
* @internal Operator-only. Game code should not call this.
|
|
600
|
+
*/
|
|
601
|
+
getInspectorView(): {
|
|
602
|
+
roomId: string;
|
|
603
|
+
name: string;
|
|
604
|
+
clients: number;
|
|
605
|
+
maxClients: number;
|
|
606
|
+
locked: boolean;
|
|
607
|
+
elapsedTime: number;
|
|
608
|
+
metadata: any;
|
|
609
|
+
clientList: Array<{
|
|
610
|
+
sessionId: string;
|
|
611
|
+
userId: string | null;
|
|
612
|
+
userEmail: string | null;
|
|
613
|
+
elapsedTime: number;
|
|
614
|
+
}>;
|
|
615
|
+
state: any;
|
|
616
|
+
stateSize: number;
|
|
617
|
+
};
|
|
618
|
+
/**
|
|
619
|
+
* Force-disconnect a single client by sessionId. No-op when the client
|
|
620
|
+
* isn't connected (idempotent — the caller doesn't need to race-check).
|
|
621
|
+
*
|
|
622
|
+
* @internal Operator-only. Game code disconnects clients by calling
|
|
623
|
+
* `.leave()` on the Client object directly.
|
|
624
|
+
*/
|
|
625
|
+
kickClient(sessionId: string, closeCode?: number, reason?: string): void;
|
|
442
626
|
/**
|
|
443
627
|
* Disconnect all connected clients, and then dispose the room.
|
|
444
628
|
*
|
|
@@ -485,4 +669,3 @@ type DefineRoomOptions<T extends RoomOptions = RoomOptions> = Partial<Pick<Room<
|
|
|
485
669
|
state?: ExtractRoomState<T> | (() => ExtractRoomState<T>);
|
|
486
670
|
} & ThisType<Exclude<Room<T>, RoomLifecycleMethods>> & ThisType<Room<T>>;
|
|
487
671
|
export declare function room<T>(options: DefineRoomOptions<T>): typeof Room<T>;
|
|
488
|
-
export {};
|