@colyseus/core 0.16.0-preview.21 → 0.16.0-preview.28

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.
Files changed (87) hide show
  1. package/build/MatchMaker.d.ts +15 -14
  2. package/build/MatchMaker.js +81 -65
  3. package/build/MatchMaker.js.map +2 -2
  4. package/build/MatchMaker.mjs +80 -64
  5. package/build/MatchMaker.mjs.map +2 -2
  6. package/build/Protocol.d.ts +3 -4
  7. package/build/Protocol.js +22 -20
  8. package/build/Protocol.js.map +2 -2
  9. package/build/Protocol.mjs +22 -20
  10. package/build/Protocol.mjs.map +2 -2
  11. package/build/Room.d.ts +30 -29
  12. package/build/Room.js +123 -84
  13. package/build/Room.js.map +2 -2
  14. package/build/Room.mjs +123 -84
  15. package/build/Room.mjs.map +2 -2
  16. package/build/Server.d.ts +0 -1
  17. package/build/Server.js +2 -2
  18. package/build/Server.js.map +2 -2
  19. package/build/Server.mjs +1 -1
  20. package/build/Server.mjs.map +2 -2
  21. package/build/Transport.d.ts +0 -5
  22. package/build/index.d.ts +1 -1
  23. package/build/index.js +1 -1
  24. package/build/index.js.map +2 -2
  25. package/build/index.mjs +1 -1
  26. package/build/index.mjs.map +1 -1
  27. package/build/matchmaker/Lobby.d.ts +2 -2
  28. package/build/matchmaker/Lobby.js.map +2 -2
  29. package/build/matchmaker/Lobby.mjs.map +2 -2
  30. package/build/matchmaker/RegisteredHandler.d.ts +4 -5
  31. package/build/matchmaker/RegisteredHandler.js +4 -3
  32. package/build/matchmaker/RegisteredHandler.js.map +2 -2
  33. package/build/matchmaker/RegisteredHandler.mjs +4 -3
  34. package/build/matchmaker/RegisteredHandler.mjs.map +2 -2
  35. package/build/matchmaker/controller.d.ts +1 -2
  36. package/build/matchmaker/driver/RoomData.d.ts +3 -3
  37. package/build/matchmaker/driver/RoomData.js +3 -3
  38. package/build/matchmaker/driver/RoomData.js.map +2 -2
  39. package/build/matchmaker/driver/RoomData.mjs +2 -2
  40. package/build/matchmaker/driver/RoomData.mjs.map +2 -2
  41. package/build/matchmaker/driver/api.d.ts +104 -0
  42. package/build/matchmaker/driver/api.js +29 -0
  43. package/build/matchmaker/driver/api.js.map +7 -0
  44. package/build/matchmaker/driver/api.mjs +6 -0
  45. package/build/matchmaker/driver/api.mjs.map +7 -0
  46. package/build/matchmaker/driver/index.d.ts +7 -7
  47. package/build/matchmaker/driver/index.js +1 -1
  48. package/build/matchmaker/driver/index.js.map +2 -2
  49. package/build/matchmaker/driver/index.mjs +2 -2
  50. package/build/matchmaker/driver/index.mjs.map +2 -2
  51. package/build/matchmaker/driver/interfaces.d.ts +7 -11
  52. package/build/matchmaker/driver/interfaces.js.map +1 -1
  53. package/build/matchmaker/driver/local/LocalDriver.d.ts +13 -0
  54. package/build/matchmaker/driver/local/LocalDriver.js +65 -0
  55. package/build/matchmaker/driver/local/LocalDriver.js.map +7 -0
  56. package/build/matchmaker/driver/local/LocalDriver.mjs +42 -0
  57. package/build/matchmaker/driver/local/LocalDriver.mjs.map +7 -0
  58. package/build/matchmaker/driver/local/Query.d.ts +9 -0
  59. package/build/matchmaker/driver/local/Query.js +78 -0
  60. package/build/matchmaker/driver/local/Query.js.map +7 -0
  61. package/build/matchmaker/driver/local/Query.mjs +55 -0
  62. package/build/matchmaker/driver/local/Query.mjs.map +7 -0
  63. package/build/matchmaker/driver/local/RoomData.d.ts +19 -0
  64. package/build/matchmaker/driver/local/RoomData.js +79 -0
  65. package/build/matchmaker/driver/local/RoomData.js.map +7 -0
  66. package/build/matchmaker/driver/local/RoomData.mjs +56 -0
  67. package/build/matchmaker/driver/local/RoomData.mjs.map +7 -0
  68. package/build/presence/LocalPresence.d.ts +9 -2
  69. package/build/presence/LocalPresence.js +77 -3
  70. package/build/presence/LocalPresence.js.map +3 -3
  71. package/build/presence/LocalPresence.mjs +77 -3
  72. package/build/presence/LocalPresence.mjs.map +3 -3
  73. package/build/presence/Presence.d.ts +38 -2
  74. package/build/presence/Presence.js.map +1 -1
  75. package/build/rooms/LobbyRoom.d.ts +4 -4
  76. package/build/rooms/LobbyRoom.js.map +2 -2
  77. package/build/rooms/LobbyRoom.mjs.map +2 -2
  78. package/build/serializer/SchemaSerializer.d.ts +11 -10
  79. package/build/serializer/SchemaSerializer.js.map +2 -2
  80. package/build/serializer/SchemaSerializer.mjs.map +2 -2
  81. package/build/serializer/Serializer.d.ts +0 -1
  82. package/build/utils/Utils.d.ts +4 -2
  83. package/build/utils/Utils.js +11 -2
  84. package/build/utils/Utils.js.map +2 -2
  85. package/build/utils/Utils.mjs +10 -2
  86. package/build/utils/Utils.mjs.map +2 -2
  87. package/package.json +1 -1
@@ -34,50 +34,52 @@ var IpcProtocol = /* @__PURE__ */ ((IpcProtocol2) => {
34
34
  IpcProtocol2[IpcProtocol2["TIMEOUT"] = 2] = "TIMEOUT";
35
35
  return IpcProtocol2;
36
36
  })(IpcProtocol || {});
37
- const sendBuffer = Buffer.allocUnsafe(8192);
38
37
  const packr = new Packr();
39
- packr.useBuffer(sendBuffer);
38
+ packr.encode(void 0);
40
39
  const getMessageBytes = {
41
40
  [10 /* JOIN_ROOM */]: (reconnectionToken, serializerId, handshake) => {
42
41
  const it = { offset: 1 };
43
- sendBuffer[0] = 10 /* JOIN_ROOM */;
44
- sendBuffer[it.offset++] = Buffer.byteLength(reconnectionToken, "utf8");
45
- encode.utf8Write(sendBuffer, reconnectionToken, it);
46
- sendBuffer[it.offset++] = Buffer.byteLength(serializerId, "utf8");
47
- encode.utf8Write(sendBuffer, serializerId, it);
42
+ packr.buffer[0] = 10 /* JOIN_ROOM */;
43
+ packr.buffer[it.offset++] = Buffer.byteLength(reconnectionToken, "utf8");
44
+ encode.utf8Write(packr.buffer, reconnectionToken, it);
45
+ packr.buffer[it.offset++] = Buffer.byteLength(serializerId, "utf8");
46
+ encode.utf8Write(packr.buffer, serializerId, it);
48
47
  let handshakeLength = handshake?.byteLength || 0;
49
48
  if (handshakeLength > 0) {
50
- handshake.copy(sendBuffer, it.offset, 0, handshakeLength);
49
+ handshake.copy(packr.buffer, it.offset, 0, handshakeLength);
51
50
  }
52
- return sendBuffer.subarray(0, it.offset + handshakeLength);
51
+ return packr.buffer.subarray(0, it.offset + handshakeLength);
53
52
  },
54
53
  [11 /* ERROR */]: (code, message = "") => {
55
54
  const it = { offset: 1 };
56
- sendBuffer[0] = 11 /* ERROR */;
57
- encode.number(sendBuffer, code, it);
58
- encode.string(sendBuffer, message, it);
59
- return sendBuffer.subarray(0, it.offset);
55
+ packr.buffer[0] = 11 /* ERROR */;
56
+ encode.number(packr.buffer, code, it);
57
+ encode.string(packr.buffer, message, it);
58
+ return packr.buffer.subarray(0, it.offset);
60
59
  },
61
60
  [14 /* ROOM_STATE */]: (bytes) => {
62
61
  return [14 /* ROOM_STATE */, ...bytes];
63
62
  },
64
63
  raw: (code, type, message, rawMessage) => {
65
64
  const it = { offset: 1 };
66
- sendBuffer[0] = code;
65
+ packr.buffer[0] = code;
67
66
  if (typeof type === "string") {
68
- encode.string(sendBuffer, type, it);
67
+ encode.string(packr.buffer, type, it);
69
68
  } else {
70
- encode.number(sendBuffer, type, it);
69
+ encode.number(packr.buffer, type, it);
71
70
  }
72
71
  if (message !== void 0) {
73
72
  packr.position = 0;
73
+ if (process.env.NODE_ENV !== "production") {
74
+ packr.useBuffer(packr.buffer);
75
+ }
74
76
  const endOfBufferOffset = packr.pack(message, 2048 + it.offset).byteLength;
75
- return sendBuffer.subarray(0, endOfBufferOffset);
77
+ return packr.buffer.subarray(0, endOfBufferOffset);
76
78
  } else if (rawMessage !== void 0) {
77
- sendBuffer.set(rawMessage, it.offset);
78
- return sendBuffer.subarray(0, it.offset + rawMessage.byteLength);
79
+ packr.buffer.set(rawMessage, it.offset);
80
+ return packr.buffer.subarray(0, it.offset + rawMessage.byteLength);
79
81
  } else {
80
- return sendBuffer.subarray(0, it.offset);
82
+ return packr.buffer.subarray(0, it.offset);
81
83
  }
82
84
  }
83
85
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/Protocol.ts"],
4
- "sourcesContent": ["import { pack, Packr } from '@colyseus/msgpackr';\nimport { encode, Iterator } from '@colyseus/schema';\n\n// Colyseus protocol codes range between 0~100\nexport enum Protocol {\n // Room-related (10~19)\n JOIN_ROOM = 10,\n ERROR = 11,\n LEAVE_ROOM = 12,\n ROOM_DATA = 13,\n ROOM_STATE = 14,\n ROOM_STATE_PATCH = 15,\n // ROOM_DATA_SCHEMA = 16, // DEPRECATED: used to send schema instances via room.send()\n ROOM_DATA_BYTES = 17,\n\n // WebSocket close codes (https://github.com/Luka967/websocket-close-codes)\n WS_CLOSE_NORMAL = 1000,\n WS_CLOSE_GOING_AWAY = 1001,\n\n // WebSocket error codes\n WS_CLOSE_CONSENTED = 4000,\n WS_CLOSE_WITH_ERROR = 4002,\n WS_CLOSE_DEVMODE_RESTART = 4010,\n\n WS_SERVER_DISCONNECT = 4201,\n WS_TOO_MANY_CLIENTS = 4202,\n}\n\nexport enum ErrorCode {\n // MatchMaking Error Codes\n MATCHMAKE_NO_HANDLER = 4210,\n MATCHMAKE_INVALID_CRITERIA = 4211,\n MATCHMAKE_INVALID_ROOM_ID = 4212,\n MATCHMAKE_UNHANDLED = 4213, // generic exception during onCreate/onJoin\n MATCHMAKE_EXPIRED = 4214, // generic exception during onCreate/onJoin\n\n AUTH_FAILED = 4215,\n APPLICATION_ERROR = 4216,\n\n INVALID_PAYLOAD = 4217,\n}\n\n// Inter-process communication protocol\nexport enum IpcProtocol {\n SUCCESS = 0,\n ERROR = 1,\n TIMEOUT = 2,\n}\n\nconst sendBuffer = Buffer.allocUnsafe(8192);\n\nconst packr = new Packr();\npackr.useBuffer(sendBuffer);\n\nexport const getMessageBytes = {\n [Protocol.JOIN_ROOM]: (reconnectionToken: string, serializerId: string, handshake?: Buffer) => {\n const it: Iterator = { offset: 1 };\n sendBuffer[0] = Protocol.JOIN_ROOM;\n\n sendBuffer[it.offset++] = Buffer.byteLength(reconnectionToken, \"utf8\");\n encode.utf8Write(sendBuffer, reconnectionToken, it);\n\n sendBuffer[it.offset++] = Buffer.byteLength(serializerId, \"utf8\");\n encode.utf8Write(sendBuffer, serializerId, it);\n\n let handshakeLength = handshake?.byteLength || 0;\n if (handshakeLength > 0) {\n handshake.copy(sendBuffer, it.offset, 0, handshakeLength);\n }\n\n return sendBuffer.subarray(0, it.offset + handshakeLength);\n },\n\n [Protocol.ERROR]: (code: number, message: string = '') => {\n const it: Iterator = { offset: 1 };\n sendBuffer[0] = Protocol.ERROR;\n\n encode.number(sendBuffer, code, it);\n encode.string(sendBuffer, message, it);\n\n return sendBuffer.subarray(0, it.offset);\n },\n\n [Protocol.ROOM_STATE]: (bytes: number[]) => {\n return [Protocol.ROOM_STATE, ...bytes];\n },\n\n raw: (code: Protocol, type: string | number, message?: any, rawMessage?: Uint8Array | Buffer) => {\n\n const it: Iterator = { offset: 1 };\n sendBuffer[0] = code;\n\n if (typeof (type) === 'string') {\n encode.string(sendBuffer, type as string, it);\n\n } else {\n encode.number(sendBuffer, type, it);\n }\n\n if (message !== undefined) {\n // pack message into the same sendBuffer\n packr.position = 0; // force to encode from the beginning\n const endOfBufferOffset = packr.pack(message, 2048 + it.offset).byteLength;\n // 2048 = RESERVE_START_SPACE\n return sendBuffer.subarray(0, endOfBufferOffset);\n\n } else if (rawMessage !== undefined) {\n\n // copy raw message into sendBuffer\n sendBuffer.set(rawMessage, it.offset);\n return sendBuffer.subarray(0, it.offset + rawMessage.byteLength);\n\n } else {\n return sendBuffer.subarray(0, it.offset);\n }\n },\n\n};\n\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { pack, Packr } from '@colyseus/msgpackr';\nimport { encode, Iterator } from '@colyseus/schema';\n\n// Colyseus protocol codes range between 0~100\nexport enum Protocol {\n // Room-related (10~19)\n JOIN_ROOM = 10,\n ERROR = 11,\n LEAVE_ROOM = 12,\n ROOM_DATA = 13,\n ROOM_STATE = 14,\n ROOM_STATE_PATCH = 15,\n // ROOM_DATA_SCHEMA = 16, // DEPRECATED: used to send schema instances via room.send()\n ROOM_DATA_BYTES = 17,\n\n // WebSocket close codes (https://github.com/Luka967/websocket-close-codes)\n WS_CLOSE_NORMAL = 1000,\n WS_CLOSE_GOING_AWAY = 1001,\n\n // WebSocket error codes\n WS_CLOSE_CONSENTED = 4000,\n WS_CLOSE_WITH_ERROR = 4002,\n WS_CLOSE_DEVMODE_RESTART = 4010,\n\n WS_SERVER_DISCONNECT = 4201,\n WS_TOO_MANY_CLIENTS = 4202,\n}\n\nexport enum ErrorCode {\n // MatchMaking Error Codes\n MATCHMAKE_NO_HANDLER = 4210,\n MATCHMAKE_INVALID_CRITERIA = 4211,\n MATCHMAKE_INVALID_ROOM_ID = 4212,\n MATCHMAKE_UNHANDLED = 4213, // generic exception during onCreate/onJoin\n MATCHMAKE_EXPIRED = 4214, // generic exception during onCreate/onJoin\n\n AUTH_FAILED = 4215,\n APPLICATION_ERROR = 4216,\n\n INVALID_PAYLOAD = 4217,\n}\n\n// Inter-process communication protocol\nexport enum IpcProtocol {\n SUCCESS = 0,\n ERROR = 1,\n TIMEOUT = 2,\n}\n\n\nconst packr = new Packr();\n\n// msgpackr workaround: initialize buffer\npackr.encode(undefined);\n\nexport const getMessageBytes = {\n [Protocol.JOIN_ROOM]: (reconnectionToken: string, serializerId: string, handshake?: Buffer) => {\n const it: Iterator = { offset: 1 };\n packr.buffer[0] = Protocol.JOIN_ROOM;\n\n packr.buffer[it.offset++] = Buffer.byteLength(reconnectionToken, \"utf8\");\n encode.utf8Write(packr.buffer, reconnectionToken, it);\n\n packr.buffer[it.offset++] = Buffer.byteLength(serializerId, \"utf8\");\n encode.utf8Write(packr.buffer, serializerId, it);\n\n let handshakeLength = handshake?.byteLength || 0;\n if (handshakeLength > 0) {\n handshake.copy(packr.buffer, it.offset, 0, handshakeLength);\n }\n\n return packr.buffer.subarray(0, it.offset + handshakeLength);\n },\n\n [Protocol.ERROR]: (code: number, message: string = '') => {\n const it: Iterator = { offset: 1 };\n packr.buffer[0] = Protocol.ERROR;\n\n encode.number(packr.buffer, code, it);\n encode.string(packr.buffer, message, it);\n\n return packr.buffer.subarray(0, it.offset);\n },\n\n [Protocol.ROOM_STATE]: (bytes: number[]) => {\n return [Protocol.ROOM_STATE, ...bytes];\n },\n\n raw: (code: Protocol, type: string | number, message?: any, rawMessage?: Uint8Array | Buffer) => {\n const it: Iterator = { offset: 1 };\n packr.buffer[0] = code;\n\n if (typeof (type) === 'string') {\n encode.string(packr.buffer, type as string, it);\n\n } else {\n encode.number(packr.buffer, type, it);\n }\n\n if (message !== undefined) {\n // force to encode from offset\n packr.position = 0;\n\n //\n // TODO: remove this after issue is fixed https://github.com/kriszyp/msgpackr/issues/139\n //\n // - This check is only required when running integration tests.\n // (colyseus.js' usage of msgpackr/buffer is conflicting)\n //\n if (process.env.NODE_ENV !== \"production\") {\n packr.useBuffer(packr.buffer);\n }\n\n // pack message into the same packr.buffer\n const endOfBufferOffset = packr.pack(message, 2048 + it.offset).byteLength;\n // 2048 = RESERVE_START_SPACE\n return packr.buffer.subarray(0, endOfBufferOffset);\n\n } else if (rawMessage !== undefined) {\n\n // copy raw message into packr.buffer\n packr.buffer.set(rawMessage, it.offset);\n return packr.buffer.subarray(0, it.offset + rawMessage.byteLength);\n\n } else {\n return packr.buffer.subarray(0, it.offset);\n }\n },\n\n};\n\n"],
5
+ "mappings": "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",
6
6
  "names": ["Protocol", "ErrorCode", "IpcProtocol"]
7
7
  }
package/build/Room.d.ts CHANGED
@@ -1,14 +1,10 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
- /// <reference types="node" />
4
1
  import http, { IncomingMessage } from 'http';
5
- import { Schema } from '@colyseus/schema';
6
2
  import Clock from '@gamestdio/timer';
7
3
  import { EventEmitter } from 'events';
8
4
  import { Presence } from './presence/Presence';
9
5
  import { Serializer } from './serializer/Serializer';
10
6
  import { Deferred } from './utils/Utils';
11
- import { RoomListingData } from './matchmaker/driver';
7
+ import { RoomCache } from './matchmaker/driver/local/LocalDriver';
12
8
  import { Client, ClientArray, ClientPrivate, ISendOptions } from './Transport';
13
9
  export declare const DEFAULT_SEAT_RESERVATION_TIME: number;
14
10
  export type SimulationCallback = (deltaTime: number) => void;
@@ -38,7 +34,7 @@ export declare abstract class Room<State extends object = any, Metadata = any, U
38
34
  */
39
35
  get locked(): boolean;
40
36
  get metadata(): Metadata;
41
- listing: RoomListingData<Metadata>;
37
+ listing: RoomCache<Metadata>;
42
38
  /**
43
39
  * Timing events tied to the room instance.
44
40
  * Intervals and timeouts are cleared when the room is disposed.
@@ -50,6 +46,12 @@ export declare abstract class Room<State extends object = any, Metadata = any, U
50
46
  * the room will be unlocked as soon as a client disconnects from it.
51
47
  */
52
48
  maxClients: number;
49
+ /**
50
+ * Automatically dispose the room when last client disconnects.
51
+ *
52
+ * @default true
53
+ */
54
+ autoDispose: boolean;
53
55
  /**
54
56
  * Frequency to send the room state to connected clients, in milliseconds.
55
57
  *
@@ -76,7 +78,7 @@ export declare abstract class Room<State extends object = any, Metadata = any, U
76
78
  _events: EventEmitter<[never]>;
77
79
  protected seatReservationTime: number;
78
80
  protected reservedSeats: {
79
- [sessionId: string]: [any, any];
81
+ [sessionId: string]: [any, any, boolean?, boolean?];
80
82
  };
81
83
  protected reservedSeatTimeouts: {
82
84
  [sessionId: string]: NodeJS.Timeout;
@@ -89,20 +91,13 @@ export declare abstract class Room<State extends object = any, Metadata = any, U
89
91
  private _serializer;
90
92
  private _afterNextPatchQueue;
91
93
  private _simulationInterval;
92
- private _patchInterval;
93
94
  private _internalState;
94
95
  private _locked;
95
96
  private _lockedExplicitly;
96
97
  private _maxClientsReached;
97
98
  private _autoDisposeTimeout;
98
99
  constructor();
99
- /**
100
- * Automatically dispose the room when last client disconnects.
101
- *
102
- * @default true
103
- */
104
- get autoDispose(): boolean;
105
- set autoDispose(value: boolean);
100
+ protected __init(): void;
106
101
  /**
107
102
  * The name of the room you provided as first argument for `gameServer.define()`.
108
103
  *
@@ -184,6 +179,9 @@ export declare abstract class Room<State extends object = any, Metadata = any, U
184
179
  * @param delay - Interval delay on executing `onTickCallback` in milliseconds.
185
180
  */
186
181
  setSimulationInterval(onTickCallback?: SimulationCallback, delay?: number): void;
182
+ /**
183
+ * @deprecated Use `.patchRate=` instead.
184
+ */
187
185
  setPatchRate(milliseconds: number | null): void;
188
186
  setState(newState: State): void;
189
187
  setSerializer(serializer: Serializer<State>): void;
@@ -198,15 +196,17 @@ export declare abstract class Room<State extends object = any, Metadata = any, U
198
196
  */
199
197
  unlock(): Promise<void>;
200
198
  send(client: Client, type: string | number, message: any, options?: ISendOptions): void;
201
- send(client: Client, message: Schema, options?: ISendOptions): void;
202
- broadcast(type: string | number, message?: any, options?: IBroadcastOptions): any;
203
- broadcast<T extends Schema>(message: T, options?: IBroadcastOptions): any;
199
+ broadcast(type: string | number, message?: any, options?: IBroadcastOptions): void;
200
+ /**
201
+ * Broadcast bytes (UInt8Arrays) to a particular room
202
+ */
203
+ broadcastBytes(type: string | number, message: Uint8Array, options: IBroadcastOptions): void;
204
204
  /**
205
205
  * Checks whether mutations have occurred in the state, and broadcast them to all connected clients.
206
206
  */
207
207
  broadcastPatch(): boolean;
208
208
  onMessage<T = any>(messageType: '*', callback: (client: Client<UserData, AuthData>, type: string | number, message: T) => void): any;
209
- onMessage<T = any>(messageType: string | number, callback: (client: Client<UserData, AuthData>, message: T) => void): any;
209
+ onMessage<T = any>(messageType: string | number, callback: (client: Client<UserData, AuthData>, message: T) => void, validate?: (message: unknown) => T): any;
210
210
  /**
211
211
  * Disconnect all connected clients, and then dispose the room.
212
212
  *
@@ -228,14 +228,15 @@ export declare abstract class Room<State extends object = any, Metadata = any, U
228
228
  allowReconnection(previousClient: Client, seconds: number | "manual"): Deferred<Client>;
229
229
  protected resetAutoDisposeTimeout(timeoutInSeconds?: number): void;
230
230
  private broadcastMessageType;
231
- private sendFullState;
232
- private _dequeueAfterPatchMessages;
233
- private _reserveSeat;
234
- private _disposeIfEmpty;
235
- private _dispose;
236
- private _onMessage;
237
- private _forciblyCloseClient;
238
- private _onLeave;
239
- private _incrementClientCount;
240
- private _decrementClientCount;
231
+ protected sendFullState(client: Client): void;
232
+ protected _dequeueAfterPatchMessages(): void;
233
+ protected _reserveSeat(sessionId: string, joinOptions?: any, authData?: any, seconds?: number, allowReconnection?: boolean, devModeReconnection?: boolean): Promise<boolean>;
234
+ protected _disposeIfEmpty(): boolean;
235
+ protected _dispose(): Promise<any>;
236
+ protected _onMessage(client: Client & ClientPrivate, buffer: Buffer): void;
237
+ protected _forciblyCloseClient(client: Client & ClientPrivate, closeCode: number): void;
238
+ protected _onLeave(client: Client, code?: number): Promise<any>;
239
+ protected _onAfterLeave(client: Client): Promise<void>;
240
+ protected _incrementClientCount(): Promise<void>;
241
+ protected _decrementClientCount(): Promise<boolean>;
241
242
  }
package/build/Room.js CHANGED
@@ -54,8 +54,8 @@ var RoomInternalState = /* @__PURE__ */ ((RoomInternalState2) => {
54
54
  class Room {
55
55
  constructor() {
56
56
  this.clock = new import_timer.default();
57
- this.#_autoDispose = true;
58
57
  this.maxClients = Infinity;
58
+ this.autoDispose = true;
59
59
  this.patchRate = DEFAULT_PATCH_RATE;
60
60
  this.clients = new import_Transport.ClientArray();
61
61
  this._events = new import_events.EventEmitter();
@@ -64,23 +64,28 @@ class Room {
64
64
  this.reservedSeatTimeouts = {};
65
65
  this._reconnections = {};
66
66
  this._reconnectingSessionId = /* @__PURE__ */ new Map();
67
- this.onMessageHandlers = {};
67
+ this.onMessageHandlers = {
68
+ "__no_message_handler": {
69
+ callback: (client, messageType, _) => {
70
+ const errorMessage = `onMessage for "${messageType}" not registered.`;
71
+ (0, import_Debug.debugAndPrintError)(errorMessage);
72
+ if (import_DevMode.isDevMode) {
73
+ client.error(import_Protocol.ErrorCode.INVALID_PAYLOAD, errorMessage);
74
+ } else {
75
+ client.leave(import_Protocol.Protocol.WS_CLOSE_WITH_ERROR, errorMessage);
76
+ }
77
+ }
78
+ }
79
+ };
68
80
  this._serializer = noneSerializer;
69
81
  this._afterNextPatchQueue = [];
70
82
  this._internalState = 0 /* CREATING */;
71
83
  this._locked = false;
72
84
  this._lockedExplicitly = false;
73
85
  this._maxClientsReached = false;
74
- this._events.once("dispose", async () => {
75
- try {
76
- await this._dispose();
77
- } catch (e) {
78
- (0, import_Debug.debugAndPrintError)(`onDispose error: ${e && e.message || e || "promise rejected"}`);
79
- }
80
- this._events.emit("disconnect");
86
+ this._events.once("dispose", () => {
87
+ this._dispose().catch((e) => (0, import_Debug.debugAndPrintError)(`onDispose error: ${e && e.message || e || "promise rejected"}`)).finally(() => this._events.emit("disconnect"));
81
88
  });
82
- this.setPatchRate(this.patchRate);
83
- this.resetAutoDisposeTimeout(this.seatReservationTime);
84
89
  }
85
90
  get locked() {
86
91
  return this._locked;
@@ -91,14 +96,42 @@ class Room {
91
96
  #_roomId;
92
97
  #_roomName;
93
98
  #_autoDispose;
94
- get autoDispose() {
95
- return this.#_autoDispose;
96
- }
97
- set autoDispose(value) {
98
- if (value !== this.#_autoDispose && this._internalState !== 2 /* DISPOSING */) {
99
- this.#_autoDispose = value;
100
- this.resetAutoDisposeTimeout();
101
- }
99
+ #_patchRate;
100
+ #_patchInterval;
101
+ __init() {
102
+ if (this.state) {
103
+ this.setState(this.state);
104
+ }
105
+ this.#_autoDispose = this.autoDispose;
106
+ this.#_patchRate = this.patchRate;
107
+ Object.defineProperties(this, {
108
+ autoDispose: {
109
+ enumerable: true,
110
+ get: () => this.#_autoDispose,
111
+ set: (value) => {
112
+ if (value !== this.#_autoDispose && this._internalState !== 2 /* DISPOSING */) {
113
+ this.#_autoDispose = value;
114
+ this.resetAutoDisposeTimeout();
115
+ }
116
+ }
117
+ },
118
+ patchRate: {
119
+ enumerable: true,
120
+ get: () => this.#_patchRate,
121
+ set: (milliseconds) => {
122
+ this.#_patchRate = milliseconds;
123
+ if (this.#_patchInterval) {
124
+ clearInterval(this.#_patchInterval);
125
+ this.#_patchInterval = void 0;
126
+ }
127
+ if (milliseconds !== null && milliseconds !== 0) {
128
+ this.#_patchInterval = setInterval(() => this.broadcastPatch(), milliseconds);
129
+ }
130
+ }
131
+ }
132
+ });
133
+ this.patchRate = this.#_patchRate;
134
+ this.resetAutoDisposeTimeout(this.seatReservationTime);
102
135
  }
103
136
  get roomName() {
104
137
  return this.#_roomName;
@@ -132,17 +165,20 @@ class Room {
132
165
  return this;
133
166
  }
134
167
  hasReservedSeat(sessionId, reconnectionToken) {
135
- if (reconnectionToken) {
136
- const reconnection = this._reconnections[reconnectionToken];
137
- return reconnection && reconnection[0] === sessionId && this.reservedSeats[sessionId] !== void 0 && this._reconnectingSessionId.has(sessionId);
168
+ const reservedSeat = this.reservedSeats[sessionId];
169
+ if (reservedSeat === void 0) {
170
+ return false;
171
+ }
172
+ if (reservedSeat[3]) {
173
+ return reconnectionToken && this._reconnections[reconnectionToken]?.[0] === sessionId && this._reconnectingSessionId.has(sessionId);
138
174
  } else {
139
- return this.reservedSeats[sessionId] !== void 0 && (!this._reconnectingSessionId.has(sessionId) || this._reconnectingSessionId.get(sessionId) === sessionId);
175
+ return reservedSeat[2] === false;
140
176
  }
141
177
  }
142
178
  checkReconnectionToken(reconnectionToken) {
143
- const reconnection = this._reconnections[reconnectionToken];
144
- const sessionId = reconnection && reconnection[0];
145
- if (this.hasReservedSeat(sessionId)) {
179
+ const sessionId = this._reconnections[reconnectionToken]?.[0];
180
+ const reservedSeat = this.reservedSeats[sessionId];
181
+ if (reservedSeat && reservedSeat[3]) {
146
182
  this._reconnectingSessionId.set(sessionId, reconnectionToken);
147
183
  return sessionId;
148
184
  } else {
@@ -162,13 +198,6 @@ class Room {
162
198
  }
163
199
  setPatchRate(milliseconds) {
164
200
  this.patchRate = milliseconds;
165
- if (this._patchInterval) {
166
- clearInterval(this._patchInterval);
167
- this._patchInterval = void 0;
168
- }
169
- if (milliseconds !== null && milliseconds !== 0) {
170
- this._patchInterval = setInterval(() => this.broadcastPatch(), milliseconds);
171
- }
172
201
  }
173
202
  setState(newState) {
174
203
  this.clock.start();
@@ -238,15 +267,21 @@ class Room {
238
267
  import_Logger.logger.warn("DEPRECATION WARNING: use client.send(...) instead of this.send(client, ...)");
239
268
  client.send(messageOrType, messageOrOptions, options);
240
269
  }
241
- broadcast(typeOrSchema, messageOrOptions, options) {
242
- const isSchema = typeof typeOrSchema === "object";
243
- const opts = isSchema ? messageOrOptions : options;
244
- if (opts && opts.afterNextPatch) {
245
- delete opts.afterNextPatch;
270
+ broadcast(type, message, options) {
271
+ if (options && options.afterNextPatch) {
272
+ delete options.afterNextPatch;
246
273
  this._afterNextPatchQueue.push(["broadcast", arguments]);
247
274
  return;
248
275
  }
249
- this.broadcastMessageType(typeOrSchema, messageOrOptions, opts);
276
+ this.broadcastMessageType(type, message, options);
277
+ }
278
+ broadcastBytes(type, message, options) {
279
+ if (options && options.afterNextPatch) {
280
+ delete options.afterNextPatch;
281
+ this._afterNextPatchQueue.push(["broadcastBytes", arguments]);
282
+ return;
283
+ }
284
+ this.broadcastMessageType(type, message, options);
250
285
  }
251
286
  broadcastPatch() {
252
287
  if (this.onBeforePatch) {
@@ -262,8 +297,8 @@ class Room {
262
297
  this._dequeueAfterPatchMessages();
263
298
  return hasChanges;
264
299
  }
265
- onMessage(messageType, callback) {
266
- this.onMessageHandlers[messageType] = callback;
300
+ onMessage(messageType, callback, validate) {
301
+ this.onMessageHandlers[messageType] = { callback, validate };
267
302
  return () => delete this.onMessageHandlers[messageType];
268
303
  }
269
304
  disconnect(closeCode = import_Protocol.Protocol.WS_CLOSE_CONSENTED) {
@@ -300,18 +335,23 @@ class Room {
300
335
  clearTimeout(this._autoDisposeTimeout);
301
336
  this._autoDisposeTimeout = void 0;
302
337
  }
303
- const [joinOptions, authData] = this.reservedSeats[sessionId];
304
- if (this.reservedSeats[sessionId].length > 2) {
338
+ const [joinOptions, authData, isConsumed, isWaitingReconnection] = this.reservedSeats[sessionId];
339
+ if (isConsumed) {
305
340
  throw new import_ServerError.ServerError(import_Protocol.ErrorCode.MATCHMAKE_EXPIRED, "already consumed");
306
341
  }
307
- this.reservedSeats[sessionId].push(true);
342
+ this.reservedSeats[sessionId][2] = true;
308
343
  client._afterNextPatchQueue = this._afterNextPatchQueue;
309
344
  client.ref["onleave"] = (_) => client.state = import_Transport.ClientState.LEAVING;
310
345
  client.ref.once("close", client.ref["onleave"]);
311
- const previousReconnectionToken = this._reconnectingSessionId.get(sessionId);
312
- if (previousReconnectionToken) {
313
- this.clients.push(client);
314
- this._reconnections[previousReconnectionToken]?.[1].resolve(client);
346
+ if (isWaitingReconnection) {
347
+ const previousReconnectionToken = this._reconnectingSessionId.get(sessionId);
348
+ if (previousReconnectionToken) {
349
+ this.clients.push(client);
350
+ await this._reconnections[previousReconnectionToken]?.[1].resolve(client);
351
+ } else {
352
+ const errorMessage = process.env.NODE_ENV === "production" ? "already consumed" : "bad reconnection token";
353
+ throw new import_ServerError.ServerError(import_Protocol.ErrorCode.MATCHMAKE_EXPIRED, errorMessage);
354
+ }
315
355
  } else {
316
356
  try {
317
357
  if (authData) {
@@ -362,7 +402,7 @@ class Room {
362
402
  }
363
403
  allowReconnection(previousClient, seconds) {
364
404
  if (previousClient._enqueuedMessages !== void 0) {
365
- return;
405
+ return import_Utils.Deferred.reject("client not joined");
366
406
  }
367
407
  if (seconds === void 0) {
368
408
  console.warn('DEPRECATED: allowReconnection() requires a second argument. Using "manual" mode.');
@@ -373,7 +413,7 @@ class Room {
373
413
  }
374
414
  if (this._internalState === 2 /* DISPOSING */) {
375
415
  this._disposeIfEmpty();
376
- throw new Error("disconnecting");
416
+ return import_Utils.Deferred.reject("disconnecting");
377
417
  }
378
418
  const sessionId = previousClient.sessionId;
379
419
  const reconnectionToken = previousClient.reconnectionToken;
@@ -414,7 +454,7 @@ class Room {
414
454
  }
415
455
  broadcastMessageType(type, message, options = {}) {
416
456
  (0, import_Debug.debugMessage)("broadcast: %O", message);
417
- const encodedMessage = import_Protocol.getMessageBytes.raw(import_Protocol.Protocol.ROOM_DATA, type, message);
457
+ const encodedMessage = message instanceof Uint8Array ? import_Protocol.getMessageBytes.raw(import_Protocol.Protocol.ROOM_DATA_BYTES, type, void 0, message) : import_Protocol.getMessageBytes.raw(import_Protocol.Protocol.ROOM_DATA, type, message);
418
458
  const except = typeof options.except !== "undefined" ? Array.isArray(options.except) ? options.except : [options.except] : void 0;
419
459
  let numClients = this.clients.length;
420
460
  while (numClients--) {
@@ -445,7 +485,7 @@ class Room {
445
485
  if (!allowReconnection && this.hasReachedMaxClients()) {
446
486
  return false;
447
487
  }
448
- this.reservedSeats[sessionId] = [joinOptions, authData];
488
+ this.reservedSeats[sessionId] = [joinOptions, authData, false, allowReconnection];
449
489
  if (!allowReconnection) {
450
490
  await this._incrementClientCount();
451
491
  this.reservedSeatTimeouts[sessionId] = setTimeout(async () => {
@@ -469,14 +509,14 @@ class Room {
469
509
  }
470
510
  async _dispose() {
471
511
  this._internalState = 2 /* DISPOSING */;
472
- await this.listing.remove();
512
+ this.listing.remove();
473
513
  let userReturnData;
474
514
  if (this.onDispose) {
475
515
  userReturnData = this.onDispose();
476
516
  }
477
- if (this._patchInterval) {
478
- clearInterval(this._patchInterval);
479
- this._patchInterval = void 0;
517
+ if (this.#_patchInterval) {
518
+ clearInterval(this.#_patchInterval);
519
+ this.#_patchInterval = void 0;
480
520
  }
481
521
  if (this._simulationInterval) {
482
522
  clearInterval(this._simulationInterval);
@@ -502,44 +542,36 @@ class Room {
502
542
  }
503
543
  if (code === import_Protocol.Protocol.ROOM_DATA) {
504
544
  const messageType = import_schema.decode.stringCheck(buffer, it) ? import_schema.decode.string(buffer, it) : import_schema.decode.number(buffer, it);
545
+ const messageTypeHandler = this.onMessageHandlers[messageType];
505
546
  let message;
506
547
  try {
507
548
  message = buffer.byteLength > it.offset ? (0, import_msgpackr.unpack)(buffer.subarray(it.offset, buffer.byteLength)) : void 0;
508
549
  (0, import_Debug.debugMessage)("received: '%s' -> %j", messageType, message);
550
+ if (messageTypeHandler?.validate !== void 0) {
551
+ message = messageTypeHandler.validate(message);
552
+ }
509
553
  } catch (e) {
510
554
  (0, import_Debug.debugAndPrintError)(e);
511
555
  client.leave(import_Protocol.Protocol.WS_CLOSE_WITH_ERROR);
512
556
  return;
513
557
  }
514
- if (this.onMessageHandlers[messageType]) {
515
- this.onMessageHandlers[messageType](client, message);
516
- } else if (this.onMessageHandlers["*"]) {
517
- this.onMessageHandlers["*"](client, messageType, message);
558
+ if (messageTypeHandler) {
559
+ messageTypeHandler.callback(client, message);
518
560
  } else {
519
- const errorMessage = `onMessage for "${messageType}" not registered.`;
520
- (0, import_Debug.debugAndPrintError)(errorMessage);
521
- if (import_DevMode.isDevMode) {
522
- client.error(import_Protocol.ErrorCode.INVALID_PAYLOAD, errorMessage);
523
- } else {
524
- client.leave(import_Protocol.Protocol.WS_CLOSE_WITH_ERROR, errorMessage);
525
- }
561
+ (this.onMessageHandlers["*"] || this.onMessageHandlers["__no_message_handler"]).callback(client, messageType, message);
526
562
  }
527
563
  } else if (code === import_Protocol.Protocol.ROOM_DATA_BYTES) {
528
564
  const messageType = import_schema.decode.stringCheck(buffer, it) ? import_schema.decode.string(buffer, it) : import_schema.decode.number(buffer, it);
529
- const message = buffer.subarray(it.offset, buffer.byteLength);
565
+ const messageTypeHandler = this.onMessageHandlers[messageType];
566
+ let message = buffer.subarray(it.offset, buffer.byteLength);
530
567
  (0, import_Debug.debugMessage)("received: '%s' -> %j", messageType, message);
531
- if (this.onMessageHandlers[messageType]) {
532
- this.onMessageHandlers[messageType](client, message);
533
- } else if (this.onMessageHandlers["*"]) {
534
- this.onMessageHandlers["*"](client, messageType, message);
568
+ if (messageTypeHandler?.validate !== void 0) {
569
+ message = messageTypeHandler.validate(message);
570
+ }
571
+ if (messageTypeHandler) {
572
+ messageTypeHandler.callback(client, message);
535
573
  } else {
536
- const errorMessage = `onMessage for "${messageType}" not registered.`;
537
- (0, import_Debug.debugAndPrintError)(errorMessage);
538
- if (import_DevMode.isDevMode) {
539
- client.error(import_Protocol.ErrorCode.INVALID_PAYLOAD, errorMessage);
540
- } else {
541
- client.leave(import_Protocol.Protocol.WS_CLOSE_WITH_ERROR, errorMessage);
542
- }
574
+ (this.onMessageHandlers["*"] || this.onMessageHandlers["__no_message_handler"]).callback(client, messageType, message);
543
575
  }
544
576
  } else if (code === import_Protocol.Protocol.JOIN_ROOM && client.state === import_Transport.ClientState.JOINING) {
545
577
  client.state = import_Transport.ClientState.JOINED;
@@ -572,11 +604,18 @@ class Room {
572
604
  }
573
605
  }
574
606
  }
575
- if (client.state !== import_Transport.ClientState.RECONNECTED) {
576
- const willDispose = await this._decrementClientCount();
577
- if (this.reservedSeats[client.sessionId] === void 0) {
578
- this._events.emit("leave", client, willDispose);
579
- }
607
+ if (this._reconnections[client.reconnectionToken]) {
608
+ this._reconnections[client.reconnectionToken][1].catch(async () => {
609
+ await this._onAfterLeave(client);
610
+ });
611
+ } else if (client.state !== import_Transport.ClientState.RECONNECTED) {
612
+ await this._onAfterLeave(client);
613
+ }
614
+ }
615
+ async _onAfterLeave(client) {
616
+ const willDispose = await this._decrementClientCount();
617
+ if (this.reservedSeats[client.sessionId] === void 0) {
618
+ this._events.emit("leave", client, willDispose);
580
619
  }
581
620
  }
582
621
  async _incrementClientCount() {