@colyseus/core 0.16.0-preview.9 → 0.16.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 +5 -5
- package/build/Debug.js +6 -2
- package/build/Debug.js.map +2 -2
- package/build/Debug.mjs +11 -10
- package/build/Debug.mjs.map +2 -2
- package/build/IPC.d.ts +1 -1
- package/build/IPC.js +3 -3
- package/build/IPC.js.map +2 -2
- package/build/IPC.mjs +4 -3
- package/build/IPC.mjs.map +2 -2
- package/build/Logger.mjs +4 -3
- package/build/Logger.mjs.map +1 -1
- package/build/MatchMaker.d.ts +35 -30
- package/build/MatchMaker.js +150 -100
- package/build/MatchMaker.js.map +2 -2
- package/build/MatchMaker.mjs +154 -107
- package/build/MatchMaker.mjs.map +2 -2
- package/build/Protocol.d.ts +3 -4
- package/build/Protocol.js +33 -19
- package/build/Protocol.js.map +2 -2
- package/build/Protocol.mjs +36 -21
- package/build/Protocol.mjs.map +2 -2
- package/build/Room.d.ts +64 -40
- package/build/Room.js +408 -151
- package/build/Room.js.map +2 -2
- package/build/Room.mjs +412 -158
- package/build/Room.mjs.map +2 -2
- package/build/Server.d.ts +8 -7
- package/build/Server.js +51 -18
- package/build/Server.js.map +2 -2
- package/build/Server.mjs +51 -21
- package/build/Server.mjs.map +3 -3
- package/build/Stats.d.ts +2 -0
- package/build/Stats.js +38 -3
- package/build/Stats.js.map +2 -2
- package/build/Stats.mjs +30 -6
- package/build/Stats.mjs.map +2 -2
- package/build/Transport.d.ts +8 -7
- package/build/Transport.js +1 -1
- package/build/Transport.js.map +2 -2
- package/build/Transport.mjs +6 -5
- package/build/Transport.mjs.map +2 -2
- package/build/discovery/index.d.ts +1 -1
- package/build/discovery/index.js.map +2 -2
- package/build/discovery/index.mjs +3 -2
- package/build/discovery/index.mjs.map +2 -2
- package/build/errors/RoomExceptions.d.ts +39 -0
- package/build/errors/RoomExceptions.js +100 -0
- package/build/errors/RoomExceptions.js.map +7 -0
- package/build/errors/RoomExceptions.mjs +71 -0
- package/build/errors/RoomExceptions.mjs.map +7 -0
- package/build/errors/SeatReservationError.mjs +3 -2
- package/build/errors/SeatReservationError.mjs.map +1 -1
- package/build/errors/ServerError.js +1 -1
- package/build/errors/ServerError.js.map +1 -1
- package/build/errors/ServerError.mjs +5 -4
- package/build/errors/ServerError.mjs.map +2 -2
- package/build/index.d.ts +21 -19
- package/build/index.js +47 -20
- package/build/index.js.map +2 -2
- package/build/index.mjs +41 -19
- package/build/index.mjs.map +2 -2
- package/build/matchmaker/Lobby.d.ts +3 -3
- package/build/matchmaker/Lobby.js +6 -3
- package/build/matchmaker/Lobby.js.map +2 -2
- package/build/matchmaker/Lobby.mjs +4 -4
- package/build/matchmaker/Lobby.mjs.map +2 -2
- package/build/matchmaker/RegisteredHandler.d.ts +6 -7
- package/build/matchmaker/RegisteredHandler.js +7 -10
- package/build/matchmaker/RegisteredHandler.js.map +2 -2
- package/build/matchmaker/RegisteredHandler.mjs +11 -13
- package/build/matchmaker/RegisteredHandler.mjs.map +2 -2
- package/build/matchmaker/controller.d.ts +4 -5
- package/build/matchmaker/controller.js +22 -15
- package/build/matchmaker/controller.js.map +2 -2
- package/build/matchmaker/controller.mjs +19 -13
- package/build/matchmaker/controller.mjs.map +2 -2
- package/build/matchmaker/driver/api.d.ts +104 -0
- package/build/matchmaker/driver/api.js +29 -0
- package/build/matchmaker/driver/api.js.map +7 -0
- package/build/matchmaker/driver/api.mjs +7 -0
- package/build/matchmaker/driver/api.mjs.map +7 -0
- package/build/matchmaker/driver/index.d.ts +2 -2
- package/build/matchmaker/driver/index.js +2 -2
- package/build/matchmaker/driver/index.js.map +2 -2
- package/build/matchmaker/driver/index.mjs +5 -4
- package/build/matchmaker/driver/index.mjs.map +2 -2
- package/build/matchmaker/driver/local/LocalDriver.d.ts +13 -0
- package/build/matchmaker/driver/local/LocalDriver.js +65 -0
- package/build/matchmaker/driver/local/LocalDriver.js.map +7 -0
- package/build/matchmaker/driver/local/LocalDriver.mjs +43 -0
- package/build/matchmaker/driver/local/LocalDriver.mjs.map +7 -0
- package/build/matchmaker/driver/local/Query.d.ts +9 -0
- package/build/matchmaker/driver/local/Query.js +78 -0
- package/build/matchmaker/driver/local/Query.js.map +7 -0
- package/build/matchmaker/driver/local/Query.mjs +56 -0
- package/build/matchmaker/driver/local/Query.mjs.map +7 -0
- package/build/matchmaker/driver/local/RoomData.d.ts +19 -0
- package/build/matchmaker/driver/local/RoomData.js +79 -0
- package/build/matchmaker/driver/local/RoomData.js.map +7 -0
- package/build/matchmaker/driver/local/RoomData.mjs +57 -0
- package/build/matchmaker/driver/local/RoomData.mjs.map +7 -0
- package/build/presence/LocalPresence.d.ts +10 -6
- package/build/presence/LocalPresence.js +85 -24
- package/build/presence/LocalPresence.js.map +3 -3
- package/build/presence/LocalPresence.mjs +85 -27
- package/build/presence/LocalPresence.mjs.map +3 -3
- package/build/presence/Presence.d.ts +38 -2
- package/build/presence/Presence.js.map +1 -1
- package/build/rooms/LobbyRoom.d.ts +6 -6
- package/build/rooms/LobbyRoom.js +8 -3
- package/build/rooms/LobbyRoom.js.map +2 -2
- package/build/rooms/LobbyRoom.mjs +7 -5
- package/build/rooms/LobbyRoom.mjs.map +2 -2
- package/build/rooms/RelayRoom.d.ts +3 -3
- package/build/rooms/RelayRoom.js +3 -1
- package/build/rooms/RelayRoom.js.map +2 -2
- package/build/rooms/RelayRoom.mjs +10 -7
- package/build/rooms/RelayRoom.mjs.map +2 -2
- package/build/serializer/NoneSerializer.d.ts +2 -2
- package/build/serializer/NoneSerializer.js.map +1 -1
- package/build/serializer/NoneSerializer.mjs +3 -2
- package/build/serializer/NoneSerializer.mjs.map +2 -2
- package/build/serializer/SchemaSerializer.d.ts +16 -15
- package/build/serializer/SchemaSerializer.js +12 -10
- package/build/serializer/SchemaSerializer.js.map +2 -2
- package/build/serializer/SchemaSerializer.mjs +16 -13
- package/build/serializer/SchemaSerializer.mjs.map +2 -2
- package/build/serializer/SchemaSerializerDebug.d.ts +7 -0
- package/build/serializer/SchemaSerializerDebug.js +0 -0
- package/build/serializer/SchemaSerializerDebug.js.map +7 -0
- package/build/serializer/SchemaSerializerDebug.mjs +0 -0
- package/build/serializer/SchemaSerializerDebug.mjs.map +7 -0
- package/build/serializer/Serializer.d.ts +1 -2
- package/build/serializer/Serializer.js.map +1 -1
- package/build/utils/DevMode.d.ts +2 -2
- package/build/utils/DevMode.js +8 -4
- package/build/utils/DevMode.js.map +2 -2
- package/build/utils/DevMode.mjs +7 -6
- package/build/utils/DevMode.mjs.map +2 -2
- package/build/utils/Utils.d.ts +8 -3
- package/build/utils/Utils.js +41 -17
- package/build/utils/Utils.js.map +2 -2
- package/build/utils/Utils.mjs +40 -21
- package/build/utils/Utils.mjs.map +2 -2
- package/package.json +17 -6
package/build/Room.js
CHANGED
|
@@ -17,6 +17,10 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
17
|
return to;
|
|
18
18
|
};
|
|
19
19
|
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
20
24
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
21
25
|
mod
|
|
22
26
|
));
|
|
@@ -28,19 +32,20 @@ __export(Room_exports, {
|
|
|
28
32
|
RoomInternalState: () => RoomInternalState
|
|
29
33
|
});
|
|
30
34
|
module.exports = __toCommonJS(Room_exports);
|
|
31
|
-
var import_msgpackr = require("msgpackr");
|
|
35
|
+
var import_msgpackr = require("@colyseus/msgpackr");
|
|
32
36
|
var import_schema = require("@colyseus/schema");
|
|
33
|
-
var import_timer = __toESM(require("@
|
|
37
|
+
var import_timer = __toESM(require("@colyseus/timer"));
|
|
34
38
|
var import_events = require("events");
|
|
35
|
-
var import_Logger = require("./Logger");
|
|
36
|
-
var import_NoneSerializer = require("./serializer/NoneSerializer");
|
|
37
|
-
var import_SchemaSerializer = require("./serializer/SchemaSerializer");
|
|
39
|
+
var import_Logger = require("./Logger.js");
|
|
40
|
+
var import_NoneSerializer = require("./serializer/NoneSerializer.js");
|
|
41
|
+
var import_SchemaSerializer = require("./serializer/SchemaSerializer.js");
|
|
38
42
|
var import_Protocol = require("./Protocol");
|
|
39
|
-
var import_Utils = require("./utils/Utils");
|
|
40
|
-
var import_DevMode = require("./utils/DevMode");
|
|
41
|
-
var import_Debug = require("./Debug");
|
|
42
|
-
var import_ServerError = require("./errors/ServerError");
|
|
43
|
+
var import_Utils = require("./utils/Utils.js");
|
|
44
|
+
var import_DevMode = require("./utils/DevMode.js");
|
|
45
|
+
var import_Debug = require("./Debug.js");
|
|
46
|
+
var import_ServerError = require("./errors/ServerError.js");
|
|
43
47
|
var import_Transport = require("./Transport");
|
|
48
|
+
var import_RoomExceptions = require("./errors/RoomExceptions.js");
|
|
44
49
|
const DEFAULT_PATCH_RATE = 1e3 / 20;
|
|
45
50
|
const DEFAULT_SIMULATION_INTERVAL = 1e3 / 60;
|
|
46
51
|
const noneSerializer = new import_NoneSerializer.NoneSerializer();
|
|
@@ -53,131 +58,308 @@ var RoomInternalState = /* @__PURE__ */ ((RoomInternalState2) => {
|
|
|
53
58
|
})(RoomInternalState || {});
|
|
54
59
|
class Room {
|
|
55
60
|
constructor() {
|
|
61
|
+
/**
|
|
62
|
+
* Timing events tied to the room instance.
|
|
63
|
+
* Intervals and timeouts are cleared when the room is disposed.
|
|
64
|
+
*/
|
|
56
65
|
this.clock = new import_timer.default();
|
|
57
|
-
this.#
|
|
66
|
+
this.#_onLeaveConcurrent = 0;
|
|
67
|
+
// number of onLeave calls in progress
|
|
68
|
+
/**
|
|
69
|
+
* Maximum number of clients allowed to connect into the room. When room reaches this limit,
|
|
70
|
+
* it is locked automatically. Unless the room was explicitly locked by you via `lock()` method,
|
|
71
|
+
* the room will be unlocked as soon as a client disconnects from it.
|
|
72
|
+
*/
|
|
58
73
|
this.maxClients = Infinity;
|
|
74
|
+
this.#_maxClientsReached = false;
|
|
75
|
+
/**
|
|
76
|
+
* Automatically dispose the room when last client disconnects.
|
|
77
|
+
*
|
|
78
|
+
* @default true
|
|
79
|
+
*/
|
|
80
|
+
this.autoDispose = true;
|
|
81
|
+
/**
|
|
82
|
+
* Frequency to send the room state to connected clients, in milliseconds.
|
|
83
|
+
*
|
|
84
|
+
* @default 50ms (20fps)
|
|
85
|
+
*/
|
|
59
86
|
this.patchRate = DEFAULT_PATCH_RATE;
|
|
87
|
+
/**
|
|
88
|
+
* The array of connected clients.
|
|
89
|
+
*
|
|
90
|
+
* @see {@link https://docs.colyseus.io/colyseus/server/room/#client|Client instance}
|
|
91
|
+
*/
|
|
60
92
|
this.clients = new import_Transport.ClientArray();
|
|
93
|
+
/** @internal */
|
|
61
94
|
this._events = new import_events.EventEmitter();
|
|
95
|
+
// seat reservation & reconnection
|
|
62
96
|
this.seatReservationTime = DEFAULT_SEAT_RESERVATION_TIME;
|
|
63
97
|
this.reservedSeats = {};
|
|
64
98
|
this.reservedSeatTimeouts = {};
|
|
65
99
|
this._reconnections = {};
|
|
66
100
|
this._reconnectingSessionId = /* @__PURE__ */ new Map();
|
|
67
|
-
this.onMessageHandlers = {
|
|
101
|
+
this.onMessageHandlers = {
|
|
102
|
+
"__no_message_handler": {
|
|
103
|
+
callback: (client, messageType, _) => {
|
|
104
|
+
const errorMessage = `room onMessage for "${messageType}" not registered.`;
|
|
105
|
+
(0, import_Debug.debugAndPrintError)(`${errorMessage} (roomId: ${this.roomId})`);
|
|
106
|
+
if (import_DevMode.isDevMode) {
|
|
107
|
+
client.error(import_Protocol.ErrorCode.INVALID_PAYLOAD, errorMessage);
|
|
108
|
+
} else {
|
|
109
|
+
client.leave(import_Protocol.Protocol.WS_CLOSE_WITH_ERROR, errorMessage);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
68
114
|
this._serializer = noneSerializer;
|
|
69
115
|
this._afterNextPatchQueue = [];
|
|
70
116
|
this._internalState = 0 /* CREATING */;
|
|
71
|
-
this._locked = false;
|
|
72
117
|
this._lockedExplicitly = false;
|
|
73
|
-
this
|
|
74
|
-
this._events.once("dispose",
|
|
75
|
-
|
|
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");
|
|
118
|
+
this.#_locked = false;
|
|
119
|
+
this._events.once("dispose", () => {
|
|
120
|
+
this._dispose().catch((e) => (0, import_Debug.debugAndPrintError)(`onDispose error: ${e && e.stack || e.message || e || "promise rejected"} (roomId: ${this.roomId})`)).finally(() => this._events.emit("disconnect"));
|
|
81
121
|
});
|
|
82
|
-
|
|
83
|
-
|
|
122
|
+
if (this.onUncaughtException !== void 0) {
|
|
123
|
+
this.#registerUncaughtExceptionHandlers();
|
|
124
|
+
}
|
|
84
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* This property will change on these situations:
|
|
128
|
+
* - The maximum number of allowed clients has been reached (`maxClients`)
|
|
129
|
+
* - You manually locked, or unlocked the room using lock() or `unlock()`.
|
|
130
|
+
*
|
|
131
|
+
* @readonly
|
|
132
|
+
*/
|
|
85
133
|
get locked() {
|
|
86
|
-
return this
|
|
134
|
+
return this.#_locked;
|
|
87
135
|
}
|
|
88
136
|
get metadata() {
|
|
89
137
|
return this.listing.metadata;
|
|
90
138
|
}
|
|
91
139
|
#_roomId;
|
|
92
140
|
#_roomName;
|
|
141
|
+
#_onLeaveConcurrent;
|
|
142
|
+
#_maxClientsReached;
|
|
143
|
+
#_maxClients;
|
|
93
144
|
#_autoDispose;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
145
|
+
#_patchRate;
|
|
146
|
+
#_patchInterval;
|
|
147
|
+
#_state;
|
|
148
|
+
#_locked;
|
|
149
|
+
/**
|
|
150
|
+
* This method is called by the MatchMaker before onCreate()
|
|
151
|
+
* @internal
|
|
152
|
+
*/
|
|
153
|
+
__init() {
|
|
154
|
+
this.#_state = this.state;
|
|
155
|
+
this.#_autoDispose = this.autoDispose;
|
|
156
|
+
this.#_patchRate = this.patchRate;
|
|
157
|
+
this.#_maxClients = this.maxClients;
|
|
158
|
+
Object.defineProperties(this, {
|
|
159
|
+
state: {
|
|
160
|
+
enumerable: true,
|
|
161
|
+
get: () => this.#_state,
|
|
162
|
+
set: (newState) => {
|
|
163
|
+
if (newState[import_schema.$changes] !== void 0) {
|
|
164
|
+
this.setSerializer(new import_SchemaSerializer.SchemaSerializer());
|
|
165
|
+
} else if ("_definition" in newState) {
|
|
166
|
+
throw new Error("@colyseus/schema v2 compatibility currently missing (reach out if you need it)");
|
|
167
|
+
} else if (import_schema.$changes === void 0) {
|
|
168
|
+
throw new Error("Multiple @colyseus/schema versions detected. Please make sure you don't have multiple versions of @colyseus/schema installed.");
|
|
169
|
+
}
|
|
170
|
+
this._serializer.reset(newState);
|
|
171
|
+
this.#_state = newState;
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
maxClients: {
|
|
175
|
+
enumerable: true,
|
|
176
|
+
get: () => this.#_maxClients,
|
|
177
|
+
set: (value) => {
|
|
178
|
+
this.#_maxClients = value;
|
|
179
|
+
if (this._internalState === 1 /* CREATED */) {
|
|
180
|
+
const hasReachedMaxClients = this.hasReachedMaxClients();
|
|
181
|
+
if (!this._lockedExplicitly && this.#_maxClientsReached && !hasReachedMaxClients) {
|
|
182
|
+
this.#_maxClientsReached = false;
|
|
183
|
+
this.#_locked = false;
|
|
184
|
+
this.listing.locked = false;
|
|
185
|
+
}
|
|
186
|
+
if (hasReachedMaxClients) {
|
|
187
|
+
this.#_maxClientsReached = true;
|
|
188
|
+
this.#_locked = true;
|
|
189
|
+
this.listing.locked = true;
|
|
190
|
+
}
|
|
191
|
+
this.listing.maxClients = value;
|
|
192
|
+
this.listing.save();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
autoDispose: {
|
|
197
|
+
enumerable: true,
|
|
198
|
+
get: () => this.#_autoDispose,
|
|
199
|
+
set: (value) => {
|
|
200
|
+
if (value !== this.#_autoDispose && this._internalState !== 2 /* DISPOSING */) {
|
|
201
|
+
this.#_autoDispose = value;
|
|
202
|
+
this.resetAutoDisposeTimeout();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
patchRate: {
|
|
207
|
+
enumerable: true,
|
|
208
|
+
get: () => this.#_patchRate,
|
|
209
|
+
set: (milliseconds) => {
|
|
210
|
+
this.#_patchRate = milliseconds;
|
|
211
|
+
if (this.#_patchInterval) {
|
|
212
|
+
clearInterval(this.#_patchInterval);
|
|
213
|
+
this.#_patchInterval = void 0;
|
|
214
|
+
}
|
|
215
|
+
if (milliseconds !== null && milliseconds !== 0) {
|
|
216
|
+
this.#_patchInterval = setInterval(() => this.broadcastPatch(), milliseconds);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
this.patchRate = this.#_patchRate;
|
|
222
|
+
if (this.#_state) {
|
|
223
|
+
this.state = this.#_state;
|
|
101
224
|
}
|
|
225
|
+
this.resetAutoDisposeTimeout(this.seatReservationTime);
|
|
226
|
+
this.clock.start();
|
|
102
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* The name of the room you provided as first argument for `gameServer.define()`.
|
|
230
|
+
*
|
|
231
|
+
* @returns roomName string
|
|
232
|
+
*/
|
|
103
233
|
get roomName() {
|
|
104
234
|
return this.#_roomName;
|
|
105
235
|
}
|
|
236
|
+
/**
|
|
237
|
+
* Setting the name of the room. Overwriting this property is restricted.
|
|
238
|
+
*
|
|
239
|
+
* @param roomName
|
|
240
|
+
*/
|
|
106
241
|
set roomName(roomName) {
|
|
107
242
|
if (this.#_roomName) {
|
|
108
243
|
throw new import_ServerError.ServerError(import_Protocol.ErrorCode.APPLICATION_ERROR, "'roomName' cannot be overwritten.");
|
|
109
244
|
}
|
|
110
245
|
this.#_roomName = roomName;
|
|
111
246
|
}
|
|
247
|
+
/**
|
|
248
|
+
* A unique, auto-generated, 9-character-long id of the room.
|
|
249
|
+
* You may replace `this.roomId` during `onCreate()`.
|
|
250
|
+
*
|
|
251
|
+
* @returns roomId string
|
|
252
|
+
*/
|
|
112
253
|
get roomId() {
|
|
113
254
|
return this.#_roomId;
|
|
114
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Setting the roomId, is restricted in room lifetime except upon room creation.
|
|
258
|
+
*
|
|
259
|
+
* @param roomId
|
|
260
|
+
* @returns roomId string
|
|
261
|
+
*/
|
|
115
262
|
set roomId(roomId) {
|
|
116
263
|
if (this._internalState !== 0 /* CREATING */ && !import_DevMode.isDevMode) {
|
|
117
264
|
throw new import_ServerError.ServerError(import_Protocol.ErrorCode.APPLICATION_ERROR, "'roomId' can only be overridden upon room creation.");
|
|
118
265
|
}
|
|
119
266
|
this.#_roomId = roomId;
|
|
120
267
|
}
|
|
121
|
-
onAuth(client, options,
|
|
268
|
+
onAuth(client, options, context) {
|
|
122
269
|
return true;
|
|
123
270
|
}
|
|
124
|
-
static async onAuth(token,
|
|
271
|
+
static async onAuth(token, options, context) {
|
|
125
272
|
return true;
|
|
126
273
|
}
|
|
274
|
+
/**
|
|
275
|
+
* This method is called during graceful shutdown of the server process
|
|
276
|
+
* You may override this method to dispose the room in your own way.
|
|
277
|
+
*
|
|
278
|
+
* Once process reaches room count of 0, the room process will be terminated.
|
|
279
|
+
*/
|
|
280
|
+
onBeforeShutdown() {
|
|
281
|
+
this.disconnect(
|
|
282
|
+
import_DevMode.isDevMode ? import_Protocol.Protocol.WS_CLOSE_DEVMODE_RESTART : import_Protocol.Protocol.WS_CLOSE_CONSENTED
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Returns whether the sum of connected clients and reserved seats exceeds maximum number of clients.
|
|
287
|
+
*
|
|
288
|
+
* @returns boolean
|
|
289
|
+
*/
|
|
127
290
|
hasReachedMaxClients() {
|
|
128
291
|
return this.clients.length + Object.keys(this.reservedSeats).length >= this.maxClients || this._internalState === 2 /* DISPOSING */;
|
|
129
292
|
}
|
|
293
|
+
/**
|
|
294
|
+
* Set the number of seconds a room can wait for a client to effectively join the room.
|
|
295
|
+
* You should consider how long your `onAuth()` will have to wait for setting a different seat reservation time.
|
|
296
|
+
* The default value is 15 seconds. You may set the `COLYSEUS_SEAT_RESERVATION_TIME`
|
|
297
|
+
* environment variable if you'd like to change the seat reservation time globally.
|
|
298
|
+
*
|
|
299
|
+
* @default 15 seconds
|
|
300
|
+
*
|
|
301
|
+
* @param seconds - number of seconds.
|
|
302
|
+
* @returns The modified Room object.
|
|
303
|
+
*/
|
|
130
304
|
setSeatReservationTime(seconds) {
|
|
131
305
|
this.seatReservationTime = seconds;
|
|
132
306
|
return this;
|
|
133
307
|
}
|
|
134
308
|
hasReservedSeat(sessionId, reconnectionToken) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
return
|
|
309
|
+
const reservedSeat = this.reservedSeats[sessionId];
|
|
310
|
+
if (reservedSeat === void 0) {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
if (reservedSeat[3]) {
|
|
314
|
+
return reconnectionToken && this._reconnections[reconnectionToken]?.[0] === sessionId && this._reconnectingSessionId.has(sessionId);
|
|
138
315
|
} else {
|
|
139
|
-
return
|
|
316
|
+
return reservedSeat[2] === false;
|
|
140
317
|
}
|
|
141
318
|
}
|
|
142
319
|
checkReconnectionToken(reconnectionToken) {
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
if (
|
|
320
|
+
const sessionId = this._reconnections[reconnectionToken]?.[0];
|
|
321
|
+
const reservedSeat = this.reservedSeats[sessionId];
|
|
322
|
+
if (reservedSeat && reservedSeat[3]) {
|
|
146
323
|
this._reconnectingSessionId.set(sessionId, reconnectionToken);
|
|
147
324
|
return sessionId;
|
|
148
325
|
} else {
|
|
149
326
|
return void 0;
|
|
150
327
|
}
|
|
151
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* (Optional) Set a simulation interval that can change the state of the game.
|
|
331
|
+
* The simulation interval is your game loop.
|
|
332
|
+
*
|
|
333
|
+
* @default 16.6ms (60fps)
|
|
334
|
+
*
|
|
335
|
+
* @param onTickCallback - You can implement your physics or world updates here!
|
|
336
|
+
* This is a good place to update the room state.
|
|
337
|
+
* @param delay - Interval delay on executing `onTickCallback` in milliseconds.
|
|
338
|
+
*/
|
|
152
339
|
setSimulationInterval(onTickCallback, delay = DEFAULT_SIMULATION_INTERVAL) {
|
|
153
340
|
if (this._simulationInterval) {
|
|
154
341
|
clearInterval(this._simulationInterval);
|
|
155
342
|
}
|
|
156
343
|
if (onTickCallback) {
|
|
344
|
+
if (this.onUncaughtException !== void 0) {
|
|
345
|
+
onTickCallback = (0, import_Utils.wrapTryCatch)(onTickCallback, this.onUncaughtException.bind(this), import_RoomExceptions.SimulationIntervalException, "setSimulationInterval");
|
|
346
|
+
}
|
|
157
347
|
this._simulationInterval = setInterval(() => {
|
|
158
348
|
this.clock.tick();
|
|
159
349
|
onTickCallback(this.clock.deltaTime);
|
|
160
350
|
}, delay);
|
|
161
351
|
}
|
|
162
352
|
}
|
|
353
|
+
/**
|
|
354
|
+
* @deprecated Use `.patchRate=` instead.
|
|
355
|
+
*/
|
|
163
356
|
setPatchRate(milliseconds) {
|
|
164
357
|
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
358
|
}
|
|
359
|
+
/**
|
|
360
|
+
* @deprecated Use `.state =` instead.
|
|
361
|
+
*/
|
|
173
362
|
setState(newState) {
|
|
174
|
-
this.clock.start();
|
|
175
|
-
if (newState[import_schema.$changes] !== void 0) {
|
|
176
|
-
this.setSerializer(new import_SchemaSerializer.SchemaSerializer());
|
|
177
|
-
} else if (import_schema.$changes === void 0) {
|
|
178
|
-
throw new Error("@colyseus/schema v2 compatibility currently missing (reach out if you need it)");
|
|
179
|
-
}
|
|
180
|
-
this._serializer.reset(newState);
|
|
181
363
|
this.state = newState;
|
|
182
364
|
}
|
|
183
365
|
setSerializer(serializer) {
|
|
@@ -202,35 +384,40 @@ class Room {
|
|
|
202
384
|
}
|
|
203
385
|
}
|
|
204
386
|
async setPrivate(bool = true) {
|
|
205
|
-
if (this.listing.private === bool)
|
|
206
|
-
return;
|
|
387
|
+
if (this.listing.private === bool) return;
|
|
207
388
|
this.listing.private = bool;
|
|
208
389
|
if (this._internalState === 1 /* CREATED */) {
|
|
209
390
|
await this.listing.save();
|
|
210
391
|
}
|
|
211
392
|
this._events.emit("visibility-change", bool);
|
|
212
393
|
}
|
|
394
|
+
/**
|
|
395
|
+
* Locking the room will remove it from the pool of available rooms for new clients to connect to.
|
|
396
|
+
*/
|
|
213
397
|
async lock() {
|
|
214
398
|
this._lockedExplicitly = arguments[0] === void 0;
|
|
215
|
-
if (this
|
|
399
|
+
if (this.#_locked) {
|
|
216
400
|
return;
|
|
217
401
|
}
|
|
218
|
-
this
|
|
402
|
+
this.#_locked = true;
|
|
219
403
|
await this.listing.updateOne({
|
|
220
|
-
$set: { locked: this
|
|
404
|
+
$set: { locked: this.#_locked }
|
|
221
405
|
});
|
|
222
406
|
this._events.emit("lock");
|
|
223
407
|
}
|
|
408
|
+
/**
|
|
409
|
+
* Unlocking the room returns it to the pool of available rooms for new clients to connect to.
|
|
410
|
+
*/
|
|
224
411
|
async unlock() {
|
|
225
412
|
if (arguments[0] === void 0) {
|
|
226
413
|
this._lockedExplicitly = false;
|
|
227
414
|
}
|
|
228
|
-
if (!this
|
|
415
|
+
if (!this.#_locked) {
|
|
229
416
|
return;
|
|
230
417
|
}
|
|
231
|
-
this
|
|
418
|
+
this.#_locked = false;
|
|
232
419
|
await this.listing.updateOne({
|
|
233
|
-
$set: { locked: this
|
|
420
|
+
$set: { locked: this.#_locked }
|
|
234
421
|
});
|
|
235
422
|
this._events.emit("unlock");
|
|
236
423
|
}
|
|
@@ -238,16 +425,28 @@ class Room {
|
|
|
238
425
|
import_Logger.logger.warn("DEPRECATION WARNING: use client.send(...) instead of this.send(client, ...)");
|
|
239
426
|
client.send(messageOrType, messageOrOptions, options);
|
|
240
427
|
}
|
|
241
|
-
broadcast(
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
if (opts && opts.afterNextPatch) {
|
|
245
|
-
delete opts.afterNextPatch;
|
|
428
|
+
broadcast(type, message, options) {
|
|
429
|
+
if (options && options.afterNextPatch) {
|
|
430
|
+
delete options.afterNextPatch;
|
|
246
431
|
this._afterNextPatchQueue.push(["broadcast", arguments]);
|
|
247
432
|
return;
|
|
248
433
|
}
|
|
249
|
-
this.broadcastMessageType(
|
|
434
|
+
this.broadcastMessageType(type, message, options);
|
|
250
435
|
}
|
|
436
|
+
/**
|
|
437
|
+
* Broadcast bytes (UInt8Arrays) to a particular room
|
|
438
|
+
*/
|
|
439
|
+
broadcastBytes(type, message, options) {
|
|
440
|
+
if (options && options.afterNextPatch) {
|
|
441
|
+
delete options.afterNextPatch;
|
|
442
|
+
this._afterNextPatchQueue.push(["broadcastBytes", arguments]);
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
this.broadcastMessageType(type, message, options);
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Checks whether mutations have occurred in the state, and broadcast them to all connected clients.
|
|
449
|
+
*/
|
|
251
450
|
broadcastPatch() {
|
|
252
451
|
if (this.onBeforePatch) {
|
|
253
452
|
this.onBeforePatch(this.state);
|
|
@@ -262,10 +461,16 @@ class Room {
|
|
|
262
461
|
this._dequeueAfterPatchMessages();
|
|
263
462
|
return hasChanges;
|
|
264
463
|
}
|
|
265
|
-
onMessage(messageType, callback) {
|
|
266
|
-
this.onMessageHandlers[messageType] = callback;
|
|
464
|
+
onMessage(messageType, callback, validate) {
|
|
465
|
+
this.onMessageHandlers[messageType] = this.onUncaughtException !== void 0 ? { validate, callback: (0, import_Utils.wrapTryCatch)(callback, this.onUncaughtException.bind(this), import_RoomExceptions.OnMessageException, "onMessage", false, messageType) } : { validate, callback };
|
|
267
466
|
return () => delete this.onMessageHandlers[messageType];
|
|
268
467
|
}
|
|
468
|
+
/**
|
|
469
|
+
* Disconnect all connected clients, and then dispose the room.
|
|
470
|
+
*
|
|
471
|
+
* @param closeCode WebSocket close code (default = 4000, which is a "consented leave")
|
|
472
|
+
* @returns Promise<void>
|
|
473
|
+
*/
|
|
269
474
|
disconnect(closeCode = import_Protocol.Protocol.WS_CLOSE_CONSENTED) {
|
|
270
475
|
if (this._internalState === 2 /* DISPOSING */) {
|
|
271
476
|
return Promise.resolve(`disconnect() ignored: room (${this.roomId}) is already disposing.`);
|
|
@@ -277,7 +482,7 @@ class Room {
|
|
|
277
482
|
this.#_autoDispose = true;
|
|
278
483
|
const delayedDisconnection = new Promise((resolve) => this._events.once("disconnect", () => resolve()));
|
|
279
484
|
for (const [_, reconnection] of Object.values(this._reconnections)) {
|
|
280
|
-
reconnection.reject();
|
|
485
|
+
reconnection.reject(new Error("disconnecting"));
|
|
281
486
|
}
|
|
282
487
|
let numClients = this.clients.length;
|
|
283
488
|
if (numClients > 0) {
|
|
@@ -289,7 +494,7 @@ class Room {
|
|
|
289
494
|
}
|
|
290
495
|
return delayedDisconnection;
|
|
291
496
|
}
|
|
292
|
-
async ["_onJoin"](client,
|
|
497
|
+
async ["_onJoin"](client, authContext) {
|
|
293
498
|
const sessionId = client.sessionId;
|
|
294
499
|
client.reconnectionToken = (0, import_Utils.generateId)();
|
|
295
500
|
if (this.reservedSeatTimeouts[sessionId]) {
|
|
@@ -300,26 +505,38 @@ class Room {
|
|
|
300
505
|
clearTimeout(this._autoDisposeTimeout);
|
|
301
506
|
this._autoDisposeTimeout = void 0;
|
|
302
507
|
}
|
|
303
|
-
const [joinOptions, authData] = this.reservedSeats[sessionId];
|
|
304
|
-
if (
|
|
508
|
+
const [joinOptions, authData, isConsumed, isWaitingReconnection] = this.reservedSeats[sessionId];
|
|
509
|
+
if (isConsumed) {
|
|
305
510
|
throw new import_ServerError.ServerError(import_Protocol.ErrorCode.MATCHMAKE_EXPIRED, "already consumed");
|
|
306
511
|
}
|
|
307
|
-
this.reservedSeats[sessionId]
|
|
512
|
+
this.reservedSeats[sessionId][2] = true;
|
|
513
|
+
(0, import_Debug.debugMatchMaking)("consuming seat reservation, sessionId: '%s' (roomId: %s)", client.sessionId, this.roomId);
|
|
308
514
|
client._afterNextPatchQueue = this._afterNextPatchQueue;
|
|
309
515
|
client.ref["onleave"] = (_) => client.state = import_Transport.ClientState.LEAVING;
|
|
310
516
|
client.ref.once("close", client.ref["onleave"]);
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
517
|
+
if (isWaitingReconnection) {
|
|
518
|
+
const previousReconnectionToken = this._reconnectingSessionId.get(sessionId);
|
|
519
|
+
if (previousReconnectionToken) {
|
|
520
|
+
this.clients.push(client);
|
|
521
|
+
await this._reconnections[previousReconnectionToken]?.[1].resolve(client);
|
|
522
|
+
} else {
|
|
523
|
+
const errorMessage = process.env.NODE_ENV === "production" ? "already consumed" : "bad reconnection token";
|
|
524
|
+
throw new import_ServerError.ServerError(import_Protocol.ErrorCode.MATCHMAKE_EXPIRED, errorMessage);
|
|
525
|
+
}
|
|
315
526
|
} else {
|
|
316
527
|
try {
|
|
317
528
|
if (authData) {
|
|
318
529
|
client.auth = authData;
|
|
319
530
|
} else if (this.onAuth !== Room.prototype.onAuth) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
531
|
+
try {
|
|
532
|
+
client.auth = await this.onAuth(client, joinOptions, authContext);
|
|
533
|
+
if (!client.auth) {
|
|
534
|
+
throw new import_ServerError.ServerError(import_Protocol.ErrorCode.AUTH_FAILED, "onAuth failed");
|
|
535
|
+
}
|
|
536
|
+
} catch (e) {
|
|
537
|
+
delete this.reservedSeats[sessionId];
|
|
538
|
+
await this._decrementClientCount();
|
|
539
|
+
throw e;
|
|
323
540
|
}
|
|
324
541
|
}
|
|
325
542
|
if (client.state === import_Transport.ClientState.LEAVING) {
|
|
@@ -333,15 +550,15 @@ class Room {
|
|
|
333
550
|
if (this.onJoin) {
|
|
334
551
|
await this.onJoin(client, joinOptions, client.auth);
|
|
335
552
|
}
|
|
336
|
-
this._events.emit("join", client);
|
|
337
|
-
delete this.reservedSeats[sessionId];
|
|
338
553
|
if (client.state === import_Transport.ClientState.LEAVING) {
|
|
339
|
-
|
|
554
|
+
throw new Error("early_leave");
|
|
555
|
+
} else {
|
|
556
|
+
delete this.reservedSeats[sessionId];
|
|
557
|
+
this._events.emit("join", client);
|
|
340
558
|
}
|
|
341
559
|
} catch (e) {
|
|
342
|
-
this.
|
|
560
|
+
await this._onLeave(client, import_Protocol.Protocol.WS_CLOSE_GOING_AWAY);
|
|
343
561
|
delete this.reservedSeats[sessionId];
|
|
344
|
-
this._decrementClientCount();
|
|
345
562
|
if (!e.code) {
|
|
346
563
|
e.code = import_Protocol.ErrorCode.APPLICATION_ERROR;
|
|
347
564
|
}
|
|
@@ -360,9 +577,19 @@ class Room {
|
|
|
360
577
|
));
|
|
361
578
|
}
|
|
362
579
|
}
|
|
580
|
+
/**
|
|
581
|
+
* Allow the specified client to reconnect into the room. Must be used inside `onLeave()` method.
|
|
582
|
+
* If seconds is provided, the reconnection is going to be cancelled after the provided amount of seconds.
|
|
583
|
+
*
|
|
584
|
+
* @param previousClient - The client which is to be waiting until re-connection happens.
|
|
585
|
+
* @param seconds - Timeout period on re-connection in seconds.
|
|
586
|
+
*
|
|
587
|
+
* @returns Deferred<Client> - The differed is a promise like type.
|
|
588
|
+
* This type can forcibly reject the promise by calling `.reject()`.
|
|
589
|
+
*/
|
|
363
590
|
allowReconnection(previousClient, seconds) {
|
|
364
591
|
if (previousClient._enqueuedMessages !== void 0) {
|
|
365
|
-
return;
|
|
592
|
+
return Promise.reject(new Error("not joined"));
|
|
366
593
|
}
|
|
367
594
|
if (seconds === void 0) {
|
|
368
595
|
console.warn('DEPRECATED: allowReconnection() requires a second argument. Using "manual" mode.');
|
|
@@ -372,8 +599,7 @@ class Room {
|
|
|
372
599
|
seconds = Infinity;
|
|
373
600
|
}
|
|
374
601
|
if (this._internalState === 2 /* DISPOSING */) {
|
|
375
|
-
|
|
376
|
-
throw new Error("disconnecting");
|
|
602
|
+
return Promise.reject(new Error("disposing"));
|
|
377
603
|
}
|
|
378
604
|
const sessionId = previousClient.sessionId;
|
|
379
605
|
const reconnectionToken = previousClient.reconnectionToken;
|
|
@@ -392,8 +618,9 @@ class Room {
|
|
|
392
618
|
reconnection.then((newClient) => {
|
|
393
619
|
newClient.auth = previousClient.auth;
|
|
394
620
|
newClient.userData = previousClient.userData;
|
|
395
|
-
previousClient.ref = newClient.ref;
|
|
396
621
|
previousClient.state = import_Transport.ClientState.RECONNECTED;
|
|
622
|
+
previousClient.ref = newClient.ref;
|
|
623
|
+
previousClient.reconnectionToken = newClient.reconnectionToken;
|
|
397
624
|
clearTimeout(this.reservedSeatTimeouts[sessionId]);
|
|
398
625
|
cleanup();
|
|
399
626
|
}).catch(() => {
|
|
@@ -413,8 +640,8 @@ class Room {
|
|
|
413
640
|
}, timeoutInSeconds * 1e3);
|
|
414
641
|
}
|
|
415
642
|
broadcastMessageType(type, message, options = {}) {
|
|
416
|
-
(0, import_Debug.debugMessage)("broadcast: %O", message);
|
|
417
|
-
const encodedMessage = import_Protocol.getMessageBytes.raw(import_Protocol.Protocol.ROOM_DATA, type, message);
|
|
643
|
+
(0, import_Debug.debugMessage)("broadcast: %O (roomId: %s)", message, this.roomId);
|
|
644
|
+
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
645
|
const except = typeof options.except !== "undefined" ? Array.isArray(options.except) ? options.except : [options.except] : void 0;
|
|
419
646
|
let numClients = this.clients.length;
|
|
420
647
|
while (numClients--) {
|
|
@@ -445,7 +672,7 @@ class Room {
|
|
|
445
672
|
if (!allowReconnection && this.hasReachedMaxClients()) {
|
|
446
673
|
return false;
|
|
447
674
|
}
|
|
448
|
-
this.reservedSeats[sessionId] = [joinOptions, authData];
|
|
675
|
+
this.reservedSeats[sessionId] = [joinOptions, authData, false, allowReconnection];
|
|
449
676
|
if (!allowReconnection) {
|
|
450
677
|
await this._incrementClientCount();
|
|
451
678
|
this.reservedSeatTimeouts[sessionId] = setTimeout(async () => {
|
|
@@ -461,7 +688,8 @@ class Room {
|
|
|
461
688
|
return true;
|
|
462
689
|
}
|
|
463
690
|
_disposeIfEmpty() {
|
|
464
|
-
const willDispose = this.#
|
|
691
|
+
const willDispose = this.#_onLeaveConcurrent === 0 && // no "onLeave" calls in progress
|
|
692
|
+
this.#_autoDispose && this._autoDisposeTimeout === void 0 && this.clients.length === 0 && Object.keys(this.reservedSeats).length === 0;
|
|
465
693
|
if (willDispose) {
|
|
466
694
|
this._events.emit("dispose");
|
|
467
695
|
}
|
|
@@ -469,14 +697,14 @@ class Room {
|
|
|
469
697
|
}
|
|
470
698
|
async _dispose() {
|
|
471
699
|
this._internalState = 2 /* DISPOSING */;
|
|
472
|
-
|
|
700
|
+
this.listing.remove();
|
|
473
701
|
let userReturnData;
|
|
474
702
|
if (this.onDispose) {
|
|
475
703
|
userReturnData = this.onDispose();
|
|
476
704
|
}
|
|
477
|
-
if (this
|
|
478
|
-
clearInterval(this
|
|
479
|
-
this
|
|
705
|
+
if (this.#_patchInterval) {
|
|
706
|
+
clearInterval(this.#_patchInterval);
|
|
707
|
+
this.#_patchInterval = void 0;
|
|
480
708
|
}
|
|
481
709
|
if (this._simulationInterval) {
|
|
482
710
|
clearInterval(this._simulationInterval);
|
|
@@ -490,56 +718,48 @@ class Room {
|
|
|
490
718
|
this.clock.stop();
|
|
491
719
|
return await (userReturnData || Promise.resolve());
|
|
492
720
|
}
|
|
493
|
-
_onMessage(client,
|
|
721
|
+
_onMessage(client, buffer) {
|
|
494
722
|
if (client.state === import_Transport.ClientState.LEAVING) {
|
|
495
723
|
return;
|
|
496
724
|
}
|
|
497
|
-
const it = { offset:
|
|
498
|
-
const code =
|
|
499
|
-
if (!
|
|
500
|
-
(0, import_Debug.debugAndPrintError)(`${this.roomName} (${this.roomId}), couldn't decode message: ${
|
|
725
|
+
const it = { offset: 1 };
|
|
726
|
+
const code = buffer[0];
|
|
727
|
+
if (!buffer) {
|
|
728
|
+
(0, import_Debug.debugAndPrintError)(`${this.roomName} (roomId: ${this.roomId}), couldn't decode message: ${buffer}`);
|
|
501
729
|
return;
|
|
502
730
|
}
|
|
503
731
|
if (code === import_Protocol.Protocol.ROOM_DATA) {
|
|
504
|
-
const messageType = import_schema.decode.stringCheck(
|
|
732
|
+
const messageType = import_schema.decode.stringCheck(buffer, it) ? import_schema.decode.string(buffer, it) : import_schema.decode.number(buffer, it);
|
|
733
|
+
const messageTypeHandler = this.onMessageHandlers[messageType];
|
|
505
734
|
let message;
|
|
506
735
|
try {
|
|
507
|
-
message =
|
|
508
|
-
(0, import_Debug.debugMessage)("received: '%s' -> %j", messageType, message);
|
|
736
|
+
message = buffer.byteLength > it.offset ? (0, import_msgpackr.unpack)(buffer.subarray(it.offset, buffer.byteLength)) : void 0;
|
|
737
|
+
(0, import_Debug.debugMessage)("received: '%s' -> %j (roomId: %s)", messageType, message, this.roomId);
|
|
738
|
+
if (messageTypeHandler?.validate !== void 0) {
|
|
739
|
+
message = messageTypeHandler.validate(message);
|
|
740
|
+
}
|
|
509
741
|
} catch (e) {
|
|
510
742
|
(0, import_Debug.debugAndPrintError)(e);
|
|
511
743
|
client.leave(import_Protocol.Protocol.WS_CLOSE_WITH_ERROR);
|
|
512
744
|
return;
|
|
513
745
|
}
|
|
514
|
-
if (
|
|
515
|
-
|
|
516
|
-
} else if (this.onMessageHandlers["*"]) {
|
|
517
|
-
this.onMessageHandlers["*"](client, messageType, message);
|
|
746
|
+
if (messageTypeHandler) {
|
|
747
|
+
messageTypeHandler.callback(client, message);
|
|
518
748
|
} else {
|
|
519
|
-
|
|
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
|
-
}
|
|
749
|
+
(this.onMessageHandlers["*"] || this.onMessageHandlers["__no_message_handler"]).callback(client, messageType, message);
|
|
526
750
|
}
|
|
527
751
|
} else if (code === import_Protocol.Protocol.ROOM_DATA_BYTES) {
|
|
528
|
-
const messageType = import_schema.decode.stringCheck(
|
|
529
|
-
const
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
752
|
+
const messageType = import_schema.decode.stringCheck(buffer, it) ? import_schema.decode.string(buffer, it) : import_schema.decode.number(buffer, it);
|
|
753
|
+
const messageTypeHandler = this.onMessageHandlers[messageType];
|
|
754
|
+
let message = buffer.subarray(it.offset, buffer.byteLength);
|
|
755
|
+
(0, import_Debug.debugMessage)("received: '%s' -> %j (roomId: %s)", messageType, message, this.roomId);
|
|
756
|
+
if (messageTypeHandler?.validate !== void 0) {
|
|
757
|
+
message = messageTypeHandler.validate(message);
|
|
758
|
+
}
|
|
759
|
+
if (messageTypeHandler) {
|
|
760
|
+
messageTypeHandler.callback(client, message);
|
|
535
761
|
} else {
|
|
536
|
-
|
|
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
|
-
}
|
|
762
|
+
(this.onMessageHandlers["*"] || this.onMessageHandlers["__no_message_handler"]).callback(client, messageType, message);
|
|
543
763
|
}
|
|
544
764
|
} else if (code === import_Protocol.Protocol.JOIN_ROOM && client.state === import_Transport.ClientState.JOINING) {
|
|
545
765
|
client.state = import_Transport.ClientState.JOINED;
|
|
@@ -561,32 +781,43 @@ class Room {
|
|
|
561
781
|
this._onLeave(client, closeCode).then(() => client.leave(closeCode));
|
|
562
782
|
}
|
|
563
783
|
async _onLeave(client, code) {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
try {
|
|
569
|
-
await this.onLeave(client, code === import_Protocol.Protocol.WS_CLOSE_CONSENTED);
|
|
570
|
-
} catch (e) {
|
|
571
|
-
(0, import_Debug.debugAndPrintError)(`onLeave error: ${e && e.message || e || "promise rejected"}`);
|
|
572
|
-
}
|
|
573
|
-
}
|
|
784
|
+
(0, import_Debug.debugMatchMaking)("onLeave, sessionId: '%s' (close code: %d, roomId: %s)", client.sessionId, code, this.roomId);
|
|
785
|
+
client.state = import_Transport.ClientState.LEAVING;
|
|
786
|
+
if (!this.clients.delete(client)) {
|
|
787
|
+
return;
|
|
574
788
|
}
|
|
575
|
-
if (
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
this.
|
|
789
|
+
if (this.onLeave) {
|
|
790
|
+
try {
|
|
791
|
+
this.#_onLeaveConcurrent++;
|
|
792
|
+
await this.onLeave(client, code === import_Protocol.Protocol.WS_CLOSE_CONSENTED);
|
|
793
|
+
} catch (e) {
|
|
794
|
+
(0, import_Debug.debugAndPrintError)(`onLeave error: ${e && e.message || e || "promise rejected"} (roomId: ${this.roomId})`);
|
|
795
|
+
} finally {
|
|
796
|
+
this.#_onLeaveConcurrent--;
|
|
579
797
|
}
|
|
580
798
|
}
|
|
799
|
+
if (this._reconnections[client.reconnectionToken]) {
|
|
800
|
+
this._reconnections[client.reconnectionToken][1].catch(async () => {
|
|
801
|
+
await this._onAfterLeave(client);
|
|
802
|
+
});
|
|
803
|
+
} else if (client.state !== import_Transport.ClientState.RECONNECTED) {
|
|
804
|
+
await this._onAfterLeave(client);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
async _onAfterLeave(client) {
|
|
808
|
+
const willDispose = await this._decrementClientCount();
|
|
809
|
+
if (this.reservedSeats[client.sessionId] === void 0) {
|
|
810
|
+
this._events.emit("leave", client, willDispose);
|
|
811
|
+
}
|
|
581
812
|
}
|
|
582
813
|
async _incrementClientCount() {
|
|
583
|
-
if (!this
|
|
584
|
-
this
|
|
814
|
+
if (!this.#_locked && this.hasReachedMaxClients()) {
|
|
815
|
+
this.#_maxClientsReached = true;
|
|
585
816
|
this.lock.call(this, true);
|
|
586
817
|
}
|
|
587
818
|
await this.listing.updateOne({
|
|
588
819
|
$inc: { clients: 1 },
|
|
589
|
-
$set: { locked: this
|
|
820
|
+
$set: { locked: this.#_locked }
|
|
590
821
|
});
|
|
591
822
|
}
|
|
592
823
|
async _decrementClientCount() {
|
|
@@ -595,17 +826,43 @@ class Room {
|
|
|
595
826
|
return true;
|
|
596
827
|
}
|
|
597
828
|
if (!willDispose) {
|
|
598
|
-
if (this
|
|
599
|
-
this
|
|
829
|
+
if (this.#_maxClientsReached && !this._lockedExplicitly) {
|
|
830
|
+
this.#_maxClientsReached = false;
|
|
600
831
|
this.unlock.call(this, true);
|
|
601
832
|
}
|
|
602
833
|
await this.listing.updateOne({
|
|
603
834
|
$inc: { clients: -1 },
|
|
604
|
-
$set: { locked: this
|
|
835
|
+
$set: { locked: this.#_locked }
|
|
605
836
|
});
|
|
606
837
|
}
|
|
607
838
|
return willDispose;
|
|
608
839
|
}
|
|
840
|
+
#registerUncaughtExceptionHandlers() {
|
|
841
|
+
const onUncaughtException = this.onUncaughtException.bind(this);
|
|
842
|
+
const originalSetTimeout = this.clock.setTimeout;
|
|
843
|
+
this.clock.setTimeout = (cb, timeout, ...args) => {
|
|
844
|
+
return originalSetTimeout.call(this.clock, (0, import_Utils.wrapTryCatch)(cb, onUncaughtException, import_RoomExceptions.TimedEventException, "setTimeout"), timeout, ...args);
|
|
845
|
+
};
|
|
846
|
+
const originalSetInterval = this.clock.setInterval;
|
|
847
|
+
this.clock.setInterval = (cb, timeout, ...args) => {
|
|
848
|
+
return originalSetInterval.call(this.clock, (0, import_Utils.wrapTryCatch)(cb, onUncaughtException, import_RoomExceptions.TimedEventException, "setInterval"), timeout, ...args);
|
|
849
|
+
};
|
|
850
|
+
if (this.onCreate !== void 0) {
|
|
851
|
+
this.onCreate = (0, import_Utils.wrapTryCatch)(this.onCreate.bind(this), onUncaughtException, import_RoomExceptions.OnCreateException, "onCreate", true);
|
|
852
|
+
}
|
|
853
|
+
if (this.onAuth !== void 0) {
|
|
854
|
+
this.onAuth = (0, import_Utils.wrapTryCatch)(this.onAuth.bind(this), onUncaughtException, import_RoomExceptions.OnAuthException, "onAuth", true);
|
|
855
|
+
}
|
|
856
|
+
if (this.onJoin !== void 0) {
|
|
857
|
+
this.onJoin = (0, import_Utils.wrapTryCatch)(this.onJoin.bind(this), onUncaughtException, import_RoomExceptions.OnJoinException, "onJoin", true);
|
|
858
|
+
}
|
|
859
|
+
if (this.onLeave !== void 0) {
|
|
860
|
+
this.onLeave = (0, import_Utils.wrapTryCatch)(this.onLeave.bind(this), onUncaughtException, import_RoomExceptions.OnLeaveException, "onLeave", true);
|
|
861
|
+
}
|
|
862
|
+
if (this.onDispose !== void 0) {
|
|
863
|
+
this.onDispose = (0, import_Utils.wrapTryCatch)(this.onDispose.bind(this), onUncaughtException, import_RoomExceptions.OnDisposeException, "onDispose");
|
|
864
|
+
}
|
|
865
|
+
}
|
|
609
866
|
}
|
|
610
867
|
// Annotate the CommonJS export names for ESM import in node:
|
|
611
868
|
0 && (module.exports = {
|