@colyseus/core 0.16.0-preview.3 → 0.16.0-preview.31
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/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 +30 -22
- package/build/MatchMaker.js +110 -87
- package/build/MatchMaker.js.map +2 -2
- package/build/MatchMaker.mjs +114 -94
- package/build/MatchMaker.mjs.map +2 -2
- package/build/Protocol.d.ts +3 -4
- package/build/Protocol.js +28 -19
- package/build/Protocol.js.map +2 -2
- package/build/Protocol.mjs +31 -21
- package/build/Protocol.mjs.map +2 -2
- package/build/Room.d.ts +36 -35
- package/build/Room.js +270 -109
- package/build/Room.js.map +2 -2
- package/build/Room.mjs +274 -116
- package/build/Room.mjs.map +2 -2
- package/build/Server.d.ts +6 -7
- package/build/Server.js +40 -12
- package/build/Server.js.map +2 -2
- package/build/Server.mjs +40 -15
- package/build/Server.mjs.map +3 -3
- package/build/Stats.js +1 -1
- package/build/Stats.js.map +2 -2
- package/build/Stats.mjs +6 -5
- package/build/Stats.mjs.map +2 -2
- package/build/Transport.d.ts +24 -11
- 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/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 +20 -19
- package/build/index.js +28 -20
- package/build/index.js.map +2 -2
- package/build/index.mjs +21 -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 +3 -4
- package/build/matchmaker/controller.js +22 -5
- package/build/matchmaker/controller.js.map +2 -2
- package/build/matchmaker/controller.mjs +19 -3
- package/build/matchmaker/controller.mjs.map +2 -2
- package/build/matchmaker/driver/RoomData.d.ts +3 -3
- package/build/matchmaker/driver/RoomData.js +3 -3
- package/build/matchmaker/driver/RoomData.js.map +2 -2
- package/build/matchmaker/driver/RoomData.mjs +2 -2
- package/build/matchmaker/driver/RoomData.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 +7 -7
- package/build/matchmaker/driver/index.js +1 -1
- package/build/matchmaker/driver/index.js.map +2 -2
- package/build/matchmaker/driver/index.mjs +2 -2
- package/build/matchmaker/driver/index.mjs.map +2 -2
- package/build/matchmaker/driver/interfaces.d.ts +7 -11
- package/build/matchmaker/driver/interfaces.js.map +1 -1
- 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 -3
- package/build/presence/LocalPresence.js +83 -5
- package/build/presence/LocalPresence.js.map +3 -3
- package/build/presence/LocalPresence.mjs +83 -8
- 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 +2 -2
- 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 +13 -11
- package/build/serializer/SchemaSerializer.js +13 -10
- package/build/serializer/SchemaSerializer.js.map +2 -2
- package/build/serializer/SchemaSerializer.mjs +17 -13
- package/build/serializer/SchemaSerializer.mjs.map +2 -2
- 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 +4 -2
- package/build/utils/Utils.js +16 -15
- package/build/utils/Utils.js.map +2 -2
- package/build/utils/Utils.mjs +18 -21
- package/build/utils/Utils.mjs.map +2 -2
- package/package.json +9 -3
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,19 @@ __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");
|
|
38
|
-
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_Transport = require("./Transport");
|
|
39
|
+
var import_Logger = require("./Logger.js");
|
|
40
|
+
var import_NoneSerializer = require("./serializer/NoneSerializer.js");
|
|
41
|
+
var import_SchemaSerializer = require("./serializer/SchemaSerializer.js");
|
|
42
|
+
var import_Protocol = require("./Protocol.js");
|
|
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");
|
|
47
|
+
var import_Transport = require("./Transport.js");
|
|
44
48
|
const DEFAULT_PATCH_RATE = 1e3 / 20;
|
|
45
49
|
const DEFAULT_SIMULATION_INTERVAL = 1e3 / 60;
|
|
46
50
|
const noneSerializer = new import_NoneSerializer.NoneSerializer();
|
|
@@ -53,35 +57,73 @@ var RoomInternalState = /* @__PURE__ */ ((RoomInternalState2) => {
|
|
|
53
57
|
})(RoomInternalState || {});
|
|
54
58
|
class Room {
|
|
55
59
|
constructor() {
|
|
60
|
+
/**
|
|
61
|
+
* Timing events tied to the room instance.
|
|
62
|
+
* Intervals and timeouts are cleared when the room is disposed.
|
|
63
|
+
*/
|
|
56
64
|
this.clock = new import_timer.default();
|
|
57
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Maximum number of clients allowed to connect into the room. When room reaches this limit,
|
|
67
|
+
* it is locked automatically. Unless the room was explicitly locked by you via `lock()` method,
|
|
68
|
+
* the room will be unlocked as soon as a client disconnects from it.
|
|
69
|
+
*/
|
|
58
70
|
this.maxClients = Infinity;
|
|
71
|
+
/**
|
|
72
|
+
* Automatically dispose the room when last client disconnects.
|
|
73
|
+
*
|
|
74
|
+
* @default true
|
|
75
|
+
*/
|
|
76
|
+
this.autoDispose = true;
|
|
77
|
+
/**
|
|
78
|
+
* Frequency to send the room state to connected clients, in milliseconds.
|
|
79
|
+
*
|
|
80
|
+
* @default 50ms (20fps)
|
|
81
|
+
*/
|
|
59
82
|
this.patchRate = DEFAULT_PATCH_RATE;
|
|
83
|
+
/**
|
|
84
|
+
* The array of connected clients.
|
|
85
|
+
*
|
|
86
|
+
* @see {@link https://docs.colyseus.io/colyseus/server/room/#client|Client instance}
|
|
87
|
+
*/
|
|
60
88
|
this.clients = new import_Transport.ClientArray();
|
|
89
|
+
/** @internal */
|
|
61
90
|
this._events = new import_events.EventEmitter();
|
|
91
|
+
// seat reservation & reconnection
|
|
62
92
|
this.seatReservationTime = DEFAULT_SEAT_RESERVATION_TIME;
|
|
63
93
|
this.reservedSeats = {};
|
|
64
94
|
this.reservedSeatTimeouts = {};
|
|
65
95
|
this._reconnections = {};
|
|
66
96
|
this._reconnectingSessionId = /* @__PURE__ */ new Map();
|
|
67
|
-
this.onMessageHandlers = {
|
|
97
|
+
this.onMessageHandlers = {
|
|
98
|
+
"__no_message_handler": {
|
|
99
|
+
callback: (client, messageType, _) => {
|
|
100
|
+
const errorMessage = `onMessage for "${messageType}" not registered.`;
|
|
101
|
+
(0, import_Debug.debugAndPrintError)(errorMessage);
|
|
102
|
+
if (import_DevMode.isDevMode) {
|
|
103
|
+
client.error(import_Protocol.ErrorCode.INVALID_PAYLOAD, errorMessage);
|
|
104
|
+
} else {
|
|
105
|
+
client.leave(import_Protocol.Protocol.WS_CLOSE_WITH_ERROR, errorMessage);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
};
|
|
68
110
|
this._serializer = noneSerializer;
|
|
69
111
|
this._afterNextPatchQueue = [];
|
|
70
112
|
this._internalState = 0 /* CREATING */;
|
|
71
113
|
this._locked = false;
|
|
72
114
|
this._lockedExplicitly = false;
|
|
73
115
|
this._maxClientsReached = false;
|
|
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");
|
|
116
|
+
this._events.once("dispose", () => {
|
|
117
|
+
this._dispose().catch((e) => (0, import_Debug.debugAndPrintError)(`onDispose error: ${e && e.message || e || "promise rejected"}`)).finally(() => this._events.emit("disconnect"));
|
|
81
118
|
});
|
|
82
|
-
this.setPatchRate(this.patchRate);
|
|
83
|
-
this.resetAutoDisposeTimeout(this.seatReservationTime);
|
|
84
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* This property will change on these situations:
|
|
122
|
+
* - The maximum number of allowed clients has been reached (`maxClients`)
|
|
123
|
+
* - You manually locked, or unlocked the room using lock() or `unlock()`.
|
|
124
|
+
*
|
|
125
|
+
* @readonly
|
|
126
|
+
*/
|
|
85
127
|
get locked() {
|
|
86
128
|
return this._locked;
|
|
87
129
|
}
|
|
@@ -91,64 +133,149 @@ class Room {
|
|
|
91
133
|
#_roomId;
|
|
92
134
|
#_roomName;
|
|
93
135
|
#_autoDispose;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
136
|
+
#_patchRate;
|
|
137
|
+
#_patchInterval;
|
|
138
|
+
__init() {
|
|
139
|
+
if (this.state) {
|
|
140
|
+
this.setState(this.state);
|
|
141
|
+
}
|
|
142
|
+
this.#_autoDispose = this.autoDispose;
|
|
143
|
+
this.#_patchRate = this.patchRate;
|
|
144
|
+
Object.defineProperties(this, {
|
|
145
|
+
autoDispose: {
|
|
146
|
+
enumerable: true,
|
|
147
|
+
get: () => this.#_autoDispose,
|
|
148
|
+
set: (value) => {
|
|
149
|
+
if (value !== this.#_autoDispose && this._internalState !== 2 /* DISPOSING */) {
|
|
150
|
+
this.#_autoDispose = value;
|
|
151
|
+
this.resetAutoDisposeTimeout();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
patchRate: {
|
|
156
|
+
enumerable: true,
|
|
157
|
+
get: () => this.#_patchRate,
|
|
158
|
+
set: (milliseconds) => {
|
|
159
|
+
this.#_patchRate = milliseconds;
|
|
160
|
+
if (this.#_patchInterval) {
|
|
161
|
+
clearInterval(this.#_patchInterval);
|
|
162
|
+
this.#_patchInterval = void 0;
|
|
163
|
+
}
|
|
164
|
+
if (milliseconds !== null && milliseconds !== 0) {
|
|
165
|
+
this.#_patchInterval = setInterval(() => this.broadcastPatch(), milliseconds);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
this.patchRate = this.#_patchRate;
|
|
171
|
+
this.resetAutoDisposeTimeout(this.seatReservationTime);
|
|
102
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* The name of the room you provided as first argument for `gameServer.define()`.
|
|
175
|
+
*
|
|
176
|
+
* @returns roomName string
|
|
177
|
+
*/
|
|
103
178
|
get roomName() {
|
|
104
179
|
return this.#_roomName;
|
|
105
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* Setting the name of the room. Overwriting this property is restricted.
|
|
183
|
+
*
|
|
184
|
+
* @param roomName
|
|
185
|
+
*/
|
|
106
186
|
set roomName(roomName) {
|
|
107
187
|
if (this.#_roomName) {
|
|
108
188
|
throw new import_ServerError.ServerError(import_Protocol.ErrorCode.APPLICATION_ERROR, "'roomName' cannot be overwritten.");
|
|
109
189
|
}
|
|
110
190
|
this.#_roomName = roomName;
|
|
111
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* A unique, auto-generated, 9-character-long id of the room.
|
|
194
|
+
* You may replace `this.roomId` during `onCreate()`.
|
|
195
|
+
*
|
|
196
|
+
* @returns roomId string
|
|
197
|
+
*/
|
|
112
198
|
get roomId() {
|
|
113
199
|
return this.#_roomId;
|
|
114
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* Setting the roomId, is restricted in room lifetime except upon room creation.
|
|
203
|
+
*
|
|
204
|
+
* @param roomId
|
|
205
|
+
* @returns roomId string
|
|
206
|
+
*/
|
|
115
207
|
set roomId(roomId) {
|
|
116
208
|
if (this._internalState !== 0 /* CREATING */ && !import_DevMode.isDevMode) {
|
|
117
209
|
throw new import_ServerError.ServerError(import_Protocol.ErrorCode.APPLICATION_ERROR, "'roomId' can only be overridden upon room creation.");
|
|
118
210
|
}
|
|
119
211
|
this.#_roomId = roomId;
|
|
120
212
|
}
|
|
213
|
+
// TODO: flag as @deprecated on v0.16
|
|
214
|
+
// TOOD: remove instance level `onAuth` on 1.0
|
|
215
|
+
/**
|
|
216
|
+
* onAuth at the instance level will be deprecated in the future.
|
|
217
|
+
* Please use "static onAuth(token, req) instead
|
|
218
|
+
*/
|
|
121
219
|
onAuth(client, options, request) {
|
|
122
220
|
return true;
|
|
123
221
|
}
|
|
124
222
|
static async onAuth(token, req) {
|
|
125
223
|
return true;
|
|
126
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Returns whether the sum of connected clients and reserved seats exceeds maximum number of clients.
|
|
227
|
+
*
|
|
228
|
+
* @returns boolean
|
|
229
|
+
*/
|
|
127
230
|
hasReachedMaxClients() {
|
|
128
231
|
return this.clients.length + Object.keys(this.reservedSeats).length >= this.maxClients || this._internalState === 2 /* DISPOSING */;
|
|
129
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* Set the number of seconds a room can wait for a client to effectively join the room.
|
|
235
|
+
* You should consider how long your `onAuth()` will have to wait for setting a different seat reservation time.
|
|
236
|
+
* The default value is 15 seconds. You may set the `COLYSEUS_SEAT_RESERVATION_TIME`
|
|
237
|
+
* environment variable if you'd like to change the seat reservation time globally.
|
|
238
|
+
*
|
|
239
|
+
* @default 15 seconds
|
|
240
|
+
*
|
|
241
|
+
* @param seconds - number of seconds.
|
|
242
|
+
* @returns The modified Room object.
|
|
243
|
+
*/
|
|
130
244
|
setSeatReservationTime(seconds) {
|
|
131
245
|
this.seatReservationTime = seconds;
|
|
132
246
|
return this;
|
|
133
247
|
}
|
|
134
248
|
hasReservedSeat(sessionId, reconnectionToken) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
return
|
|
249
|
+
const reservedSeat = this.reservedSeats[sessionId];
|
|
250
|
+
if (reservedSeat === void 0) {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
if (reservedSeat[3]) {
|
|
254
|
+
return reconnectionToken && this._reconnections[reconnectionToken]?.[0] === sessionId && this._reconnectingSessionId.has(sessionId);
|
|
138
255
|
} else {
|
|
139
|
-
return
|
|
256
|
+
return reservedSeat[2] === false;
|
|
140
257
|
}
|
|
141
258
|
}
|
|
142
259
|
checkReconnectionToken(reconnectionToken) {
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
if (
|
|
260
|
+
const sessionId = this._reconnections[reconnectionToken]?.[0];
|
|
261
|
+
const reservedSeat = this.reservedSeats[sessionId];
|
|
262
|
+
if (reservedSeat && reservedSeat[3]) {
|
|
146
263
|
this._reconnectingSessionId.set(sessionId, reconnectionToken);
|
|
147
264
|
return sessionId;
|
|
148
265
|
} else {
|
|
149
266
|
return void 0;
|
|
150
267
|
}
|
|
151
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* (Optional) Set a simulation interval that can change the state of the game.
|
|
271
|
+
* The simulation interval is your game loop.
|
|
272
|
+
*
|
|
273
|
+
* @default 16.6ms (60fps)
|
|
274
|
+
*
|
|
275
|
+
* @param onTickCallback - You can implement your physics or world updates here!
|
|
276
|
+
* This is a good place to update the room state.
|
|
277
|
+
* @param delay - Interval delay on executing `onTickCallback` in milliseconds.
|
|
278
|
+
*/
|
|
152
279
|
setSimulationInterval(onTickCallback, delay = DEFAULT_SIMULATION_INTERVAL) {
|
|
153
280
|
if (this._simulationInterval) {
|
|
154
281
|
clearInterval(this._simulationInterval);
|
|
@@ -160,15 +287,11 @@ class Room {
|
|
|
160
287
|
}, delay);
|
|
161
288
|
}
|
|
162
289
|
}
|
|
290
|
+
/**
|
|
291
|
+
* @deprecated Use `.patchRate=` instead.
|
|
292
|
+
*/
|
|
163
293
|
setPatchRate(milliseconds) {
|
|
164
294
|
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
295
|
}
|
|
173
296
|
setState(newState) {
|
|
174
297
|
this.clock.start();
|
|
@@ -202,14 +325,16 @@ class Room {
|
|
|
202
325
|
}
|
|
203
326
|
}
|
|
204
327
|
async setPrivate(bool = true) {
|
|
205
|
-
if (this.listing.private === bool)
|
|
206
|
-
return;
|
|
328
|
+
if (this.listing.private === bool) return;
|
|
207
329
|
this.listing.private = bool;
|
|
208
330
|
if (this._internalState === 1 /* CREATED */) {
|
|
209
331
|
await this.listing.save();
|
|
210
332
|
}
|
|
211
333
|
this._events.emit("visibility-change", bool);
|
|
212
334
|
}
|
|
335
|
+
/**
|
|
336
|
+
* Locking the room will remove it from the pool of available rooms for new clients to connect to.
|
|
337
|
+
*/
|
|
213
338
|
async lock() {
|
|
214
339
|
this._lockedExplicitly = arguments[0] === void 0;
|
|
215
340
|
if (this._locked) {
|
|
@@ -221,6 +346,9 @@ class Room {
|
|
|
221
346
|
});
|
|
222
347
|
this._events.emit("lock");
|
|
223
348
|
}
|
|
349
|
+
/**
|
|
350
|
+
* Unlocking the room returns it to the pool of available rooms for new clients to connect to.
|
|
351
|
+
*/
|
|
224
352
|
async unlock() {
|
|
225
353
|
if (arguments[0] === void 0) {
|
|
226
354
|
this._lockedExplicitly = false;
|
|
@@ -238,16 +366,28 @@ class Room {
|
|
|
238
366
|
import_Logger.logger.warn("DEPRECATION WARNING: use client.send(...) instead of this.send(client, ...)");
|
|
239
367
|
client.send(messageOrType, messageOrOptions, options);
|
|
240
368
|
}
|
|
241
|
-
broadcast(
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
if (opts && opts.afterNextPatch) {
|
|
245
|
-
delete opts.afterNextPatch;
|
|
369
|
+
broadcast(type, message, options) {
|
|
370
|
+
if (options && options.afterNextPatch) {
|
|
371
|
+
delete options.afterNextPatch;
|
|
246
372
|
this._afterNextPatchQueue.push(["broadcast", arguments]);
|
|
247
373
|
return;
|
|
248
374
|
}
|
|
249
|
-
this.broadcastMessageType(
|
|
375
|
+
this.broadcastMessageType(type, message, options);
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Broadcast bytes (UInt8Arrays) to a particular room
|
|
379
|
+
*/
|
|
380
|
+
broadcastBytes(type, message, options) {
|
|
381
|
+
if (options && options.afterNextPatch) {
|
|
382
|
+
delete options.afterNextPatch;
|
|
383
|
+
this._afterNextPatchQueue.push(["broadcastBytes", arguments]);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
this.broadcastMessageType(type, message, options);
|
|
250
387
|
}
|
|
388
|
+
/**
|
|
389
|
+
* Checks whether mutations have occurred in the state, and broadcast them to all connected clients.
|
|
390
|
+
*/
|
|
251
391
|
broadcastPatch() {
|
|
252
392
|
if (this.onBeforePatch) {
|
|
253
393
|
this.onBeforePatch(this.state);
|
|
@@ -262,10 +402,16 @@ class Room {
|
|
|
262
402
|
this._dequeueAfterPatchMessages();
|
|
263
403
|
return hasChanges;
|
|
264
404
|
}
|
|
265
|
-
onMessage(messageType, callback) {
|
|
266
|
-
this.onMessageHandlers[messageType] = callback;
|
|
405
|
+
onMessage(messageType, callback, validate) {
|
|
406
|
+
this.onMessageHandlers[messageType] = { callback, validate };
|
|
267
407
|
return () => delete this.onMessageHandlers[messageType];
|
|
268
408
|
}
|
|
409
|
+
/**
|
|
410
|
+
* Disconnect all connected clients, and then dispose the room.
|
|
411
|
+
*
|
|
412
|
+
* @param closeCode WebSocket close code (default = 4000, which is a "consented leave")
|
|
413
|
+
* @returns Promise<void>
|
|
414
|
+
*/
|
|
269
415
|
disconnect(closeCode = import_Protocol.Protocol.WS_CLOSE_CONSENTED) {
|
|
270
416
|
if (this._internalState === 2 /* DISPOSING */) {
|
|
271
417
|
return Promise.resolve(`disconnect() ignored: room (${this.roomId}) is already disposing.`);
|
|
@@ -291,7 +437,7 @@ class Room {
|
|
|
291
437
|
}
|
|
292
438
|
async ["_onJoin"](client, req) {
|
|
293
439
|
const sessionId = client.sessionId;
|
|
294
|
-
client.
|
|
440
|
+
client.reconnectionToken = (0, import_Utils.generateId)();
|
|
295
441
|
if (this.reservedSeatTimeouts[sessionId]) {
|
|
296
442
|
clearTimeout(this.reservedSeatTimeouts[sessionId]);
|
|
297
443
|
delete this.reservedSeatTimeouts[sessionId];
|
|
@@ -300,18 +446,23 @@ class Room {
|
|
|
300
446
|
clearTimeout(this._autoDisposeTimeout);
|
|
301
447
|
this._autoDisposeTimeout = void 0;
|
|
302
448
|
}
|
|
303
|
-
const [joinOptions, authData] = this.reservedSeats[sessionId];
|
|
304
|
-
if (
|
|
449
|
+
const [joinOptions, authData, isConsumed, isWaitingReconnection] = this.reservedSeats[sessionId];
|
|
450
|
+
if (isConsumed) {
|
|
305
451
|
throw new import_ServerError.ServerError(import_Protocol.ErrorCode.MATCHMAKE_EXPIRED, "already consumed");
|
|
306
452
|
}
|
|
307
|
-
this.reservedSeats[sessionId]
|
|
453
|
+
this.reservedSeats[sessionId][2] = true;
|
|
308
454
|
client._afterNextPatchQueue = this._afterNextPatchQueue;
|
|
309
455
|
client.ref["onleave"] = (_) => client.state = import_Transport.ClientState.LEAVING;
|
|
310
456
|
client.ref.once("close", client.ref["onleave"]);
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
457
|
+
if (isWaitingReconnection) {
|
|
458
|
+
const previousReconnectionToken = this._reconnectingSessionId.get(sessionId);
|
|
459
|
+
if (previousReconnectionToken) {
|
|
460
|
+
this.clients.push(client);
|
|
461
|
+
await this._reconnections[previousReconnectionToken]?.[1].resolve(client);
|
|
462
|
+
} else {
|
|
463
|
+
const errorMessage = process.env.NODE_ENV === "production" ? "already consumed" : "bad reconnection token";
|
|
464
|
+
throw new import_ServerError.ServerError(import_Protocol.ErrorCode.MATCHMAKE_EXPIRED, errorMessage);
|
|
465
|
+
}
|
|
315
466
|
} else {
|
|
316
467
|
try {
|
|
317
468
|
if (authData) {
|
|
@@ -354,15 +505,25 @@ class Room {
|
|
|
354
505
|
client.ref.once("close", client.ref["onleave"]);
|
|
355
506
|
client.ref.on("message", this._onMessage.bind(this, client));
|
|
356
507
|
client.raw(import_Protocol.getMessageBytes[import_Protocol.Protocol.JOIN_ROOM](
|
|
357
|
-
client.
|
|
508
|
+
client.reconnectionToken,
|
|
358
509
|
this._serializer.id,
|
|
359
510
|
this._serializer.handshake && this._serializer.handshake()
|
|
360
511
|
));
|
|
361
512
|
}
|
|
362
513
|
}
|
|
514
|
+
/**
|
|
515
|
+
* Allow the specified client to reconnect into the room. Must be used inside `onLeave()` method.
|
|
516
|
+
* If seconds is provided, the reconnection is going to be cancelled after the provided amount of seconds.
|
|
517
|
+
*
|
|
518
|
+
* @param previousClient - The client which is to be waiting until re-connection happens.
|
|
519
|
+
* @param seconds - Timeout period on re-connection in seconds.
|
|
520
|
+
*
|
|
521
|
+
* @returns Deferred<Client> - The differed is a promise like type.
|
|
522
|
+
* This type can forcibly reject the promise by calling `.reject()`.
|
|
523
|
+
*/
|
|
363
524
|
allowReconnection(previousClient, seconds) {
|
|
364
525
|
if (previousClient._enqueuedMessages !== void 0) {
|
|
365
|
-
return;
|
|
526
|
+
return import_Utils.Deferred.reject("client not joined");
|
|
366
527
|
}
|
|
367
528
|
if (seconds === void 0) {
|
|
368
529
|
console.warn('DEPRECATED: allowReconnection() requires a second argument. Using "manual" mode.');
|
|
@@ -373,10 +534,10 @@ class Room {
|
|
|
373
534
|
}
|
|
374
535
|
if (this._internalState === 2 /* DISPOSING */) {
|
|
375
536
|
this._disposeIfEmpty();
|
|
376
|
-
|
|
537
|
+
return import_Utils.Deferred.reject("disconnecting");
|
|
377
538
|
}
|
|
378
539
|
const sessionId = previousClient.sessionId;
|
|
379
|
-
const reconnectionToken = previousClient.
|
|
540
|
+
const reconnectionToken = previousClient.reconnectionToken;
|
|
380
541
|
this._reserveSeat(sessionId, true, previousClient.auth, seconds, true);
|
|
381
542
|
const reconnection = new import_Utils.Deferred();
|
|
382
543
|
this._reconnections[reconnectionToken] = [sessionId, reconnection];
|
|
@@ -414,7 +575,7 @@ class Room {
|
|
|
414
575
|
}
|
|
415
576
|
broadcastMessageType(type, message, options = {}) {
|
|
416
577
|
(0, import_Debug.debugMessage)("broadcast: %O", message);
|
|
417
|
-
const encodedMessage = import_Protocol.getMessageBytes.raw(import_Protocol.Protocol.ROOM_DATA, type, message);
|
|
578
|
+
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
579
|
const except = typeof options.except !== "undefined" ? Array.isArray(options.except) ? options.except : [options.except] : void 0;
|
|
419
580
|
let numClients = this.clients.length;
|
|
420
581
|
while (numClients--) {
|
|
@@ -425,7 +586,7 @@ class Room {
|
|
|
425
586
|
}
|
|
426
587
|
}
|
|
427
588
|
sendFullState(client) {
|
|
428
|
-
client.
|
|
589
|
+
client.raw(this._serializer.getFullState(client));
|
|
429
590
|
}
|
|
430
591
|
_dequeueAfterPatchMessages() {
|
|
431
592
|
const length = this._afterNextPatchQueue.length;
|
|
@@ -445,7 +606,7 @@ class Room {
|
|
|
445
606
|
if (!allowReconnection && this.hasReachedMaxClients()) {
|
|
446
607
|
return false;
|
|
447
608
|
}
|
|
448
|
-
this.reservedSeats[sessionId] = [joinOptions, authData];
|
|
609
|
+
this.reservedSeats[sessionId] = [joinOptions, authData, false, allowReconnection];
|
|
449
610
|
if (!allowReconnection) {
|
|
450
611
|
await this._incrementClientCount();
|
|
451
612
|
this.reservedSeatTimeouts[sessionId] = setTimeout(async () => {
|
|
@@ -469,14 +630,14 @@ class Room {
|
|
|
469
630
|
}
|
|
470
631
|
async _dispose() {
|
|
471
632
|
this._internalState = 2 /* DISPOSING */;
|
|
472
|
-
|
|
633
|
+
this.listing.remove();
|
|
473
634
|
let userReturnData;
|
|
474
635
|
if (this.onDispose) {
|
|
475
636
|
userReturnData = this.onDispose();
|
|
476
637
|
}
|
|
477
|
-
if (this
|
|
478
|
-
clearInterval(this
|
|
479
|
-
this
|
|
638
|
+
if (this.#_patchInterval) {
|
|
639
|
+
clearInterval(this.#_patchInterval);
|
|
640
|
+
this.#_patchInterval = void 0;
|
|
480
641
|
}
|
|
481
642
|
if (this._simulationInterval) {
|
|
482
643
|
clearInterval(this._simulationInterval);
|
|
@@ -490,59 +651,52 @@ class Room {
|
|
|
490
651
|
this.clock.stop();
|
|
491
652
|
return await (userReturnData || Promise.resolve());
|
|
492
653
|
}
|
|
493
|
-
_onMessage(client,
|
|
654
|
+
_onMessage(client, buffer) {
|
|
494
655
|
if (client.state === import_Transport.ClientState.LEAVING) {
|
|
495
656
|
return;
|
|
496
657
|
}
|
|
497
|
-
const it = { offset:
|
|
498
|
-
const code =
|
|
499
|
-
if (!
|
|
500
|
-
(0, import_Debug.debugAndPrintError)(`${this.roomName} (${this.roomId}), couldn't decode message: ${
|
|
658
|
+
const it = { offset: 1 };
|
|
659
|
+
const code = buffer[0];
|
|
660
|
+
if (!buffer) {
|
|
661
|
+
(0, import_Debug.debugAndPrintError)(`${this.roomName} (${this.roomId}), couldn't decode message: ${buffer}`);
|
|
501
662
|
return;
|
|
502
663
|
}
|
|
503
664
|
if (code === import_Protocol.Protocol.ROOM_DATA) {
|
|
504
|
-
const messageType = import_schema.decode.stringCheck(
|
|
665
|
+
const messageType = import_schema.decode.stringCheck(buffer, it) ? import_schema.decode.string(buffer, it) : import_schema.decode.number(buffer, it);
|
|
666
|
+
const messageTypeHandler = this.onMessageHandlers[messageType];
|
|
505
667
|
let message;
|
|
506
668
|
try {
|
|
507
|
-
message =
|
|
669
|
+
message = buffer.byteLength > it.offset ? (0, import_msgpackr.unpack)(buffer.subarray(it.offset, buffer.byteLength)) : void 0;
|
|
508
670
|
(0, import_Debug.debugMessage)("received: '%s' -> %j", messageType, message);
|
|
671
|
+
if (messageTypeHandler?.validate !== void 0) {
|
|
672
|
+
message = messageTypeHandler.validate(message);
|
|
673
|
+
}
|
|
509
674
|
} catch (e) {
|
|
510
675
|
(0, import_Debug.debugAndPrintError)(e);
|
|
511
676
|
client.leave(import_Protocol.Protocol.WS_CLOSE_WITH_ERROR);
|
|
512
677
|
return;
|
|
513
678
|
}
|
|
514
|
-
if (
|
|
515
|
-
|
|
516
|
-
} else if (this.onMessageHandlers["*"]) {
|
|
517
|
-
this.onMessageHandlers["*"](client, messageType, message);
|
|
679
|
+
if (messageTypeHandler) {
|
|
680
|
+
messageTypeHandler.callback(client, message);
|
|
518
681
|
} 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
|
-
}
|
|
682
|
+
(this.onMessageHandlers["*"] || this.onMessageHandlers["__no_message_handler"]).callback(client, messageType, message);
|
|
526
683
|
}
|
|
527
684
|
} else if (code === import_Protocol.Protocol.ROOM_DATA_BYTES) {
|
|
528
|
-
const messageType = import_schema.decode.stringCheck(
|
|
529
|
-
const
|
|
685
|
+
const messageType = import_schema.decode.stringCheck(buffer, it) ? import_schema.decode.string(buffer, it) : import_schema.decode.number(buffer, it);
|
|
686
|
+
const messageTypeHandler = this.onMessageHandlers[messageType];
|
|
687
|
+
let message = buffer.subarray(it.offset, buffer.byteLength);
|
|
530
688
|
(0, import_Debug.debugMessage)("received: '%s' -> %j", messageType, message);
|
|
531
|
-
if (
|
|
532
|
-
|
|
533
|
-
}
|
|
534
|
-
|
|
689
|
+
if (messageTypeHandler?.validate !== void 0) {
|
|
690
|
+
message = messageTypeHandler.validate(message);
|
|
691
|
+
}
|
|
692
|
+
if (messageTypeHandler) {
|
|
693
|
+
messageTypeHandler.callback(client, message);
|
|
535
694
|
} 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
|
-
}
|
|
695
|
+
(this.onMessageHandlers["*"] || this.onMessageHandlers["__no_message_handler"]).callback(client, messageType, message);
|
|
543
696
|
}
|
|
544
697
|
} else if (code === import_Protocol.Protocol.JOIN_ROOM && client.state === import_Transport.ClientState.JOINING) {
|
|
545
698
|
client.state = import_Transport.ClientState.JOINED;
|
|
699
|
+
client._joinedAt = this.clock.elapsedTime;
|
|
546
700
|
if (this.state) {
|
|
547
701
|
this.sendFullState(client);
|
|
548
702
|
}
|
|
@@ -571,11 +725,18 @@ class Room {
|
|
|
571
725
|
}
|
|
572
726
|
}
|
|
573
727
|
}
|
|
574
|
-
if (client.
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
728
|
+
if (this._reconnections[client.reconnectionToken]) {
|
|
729
|
+
this._reconnections[client.reconnectionToken][1].catch(async () => {
|
|
730
|
+
await this._onAfterLeave(client);
|
|
731
|
+
});
|
|
732
|
+
} else if (client.state !== import_Transport.ClientState.RECONNECTED) {
|
|
733
|
+
await this._onAfterLeave(client);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
async _onAfterLeave(client) {
|
|
737
|
+
const willDispose = await this._decrementClientCount();
|
|
738
|
+
if (this.reservedSeats[client.sessionId] === void 0) {
|
|
739
|
+
this._events.emit("leave", client, willDispose);
|
|
579
740
|
}
|
|
580
741
|
}
|
|
581
742
|
async _incrementClientCount() {
|