@colyseus/core 0.17.43 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/MatchMaker.cjs +19 -6
- package/build/MatchMaker.cjs.map +2 -2
- package/build/MatchMaker.d.ts +10 -0
- package/build/MatchMaker.mjs +18 -6
- package/build/MatchMaker.mjs.map +2 -2
- package/build/Protocol.cjs +102 -37
- package/build/Protocol.cjs.map +2 -2
- package/build/Protocol.d.ts +33 -2
- package/build/Protocol.mjs +102 -37
- package/build/Protocol.mjs.map +2 -2
- package/build/Room.cjs +296 -19
- package/build/Room.cjs.map +3 -3
- package/build/Room.d.ts +186 -3
- package/build/Room.mjs +303 -21
- package/build/Room.mjs.map +3 -3
- package/build/RoomPlugin.cjs +252 -0
- package/build/RoomPlugin.cjs.map +7 -0
- package/build/RoomPlugin.d.ts +271 -0
- package/build/RoomPlugin.mjs +220 -0
- package/build/RoomPlugin.mjs.map +7 -0
- package/build/Server.cjs +49 -15
- package/build/Server.cjs.map +2 -2
- package/build/Server.d.ts +25 -0
- package/build/Server.mjs +50 -16
- package/build/Server.mjs.map +2 -2
- package/build/Transport.cjs +38 -2
- package/build/Transport.cjs.map +2 -2
- package/build/Transport.d.ts +40 -4
- package/build/Transport.mjs +38 -2
- package/build/Transport.mjs.map +2 -2
- package/build/index.cjs +11 -2
- package/build/index.cjs.map +2 -2
- package/build/index.d.ts +2 -1
- package/build/index.mjs +12 -2
- package/build/index.mjs.map +2 -2
- package/build/input/InputBuffer.cjs +113 -0
- package/build/input/InputBuffer.cjs.map +7 -0
- package/build/input/InputBuffer.d.ts +136 -0
- package/build/input/InputBuffer.mjs +86 -0
- package/build/input/InputBuffer.mjs.map +7 -0
- package/build/internal.cjs +61 -0
- package/build/internal.cjs.map +7 -0
- package/build/internal.d.ts +9 -0
- package/build/internal.mjs +29 -0
- package/build/internal.mjs.map +7 -0
- package/build/matchmaker/LocalDriver/LocalDriver.cjs +13 -0
- package/build/matchmaker/LocalDriver/LocalDriver.cjs.map +2 -2
- package/build/matchmaker/LocalDriver/LocalDriver.d.ts +1 -0
- package/build/matchmaker/LocalDriver/LocalDriver.mjs +13 -0
- package/build/matchmaker/LocalDriver/LocalDriver.mjs.map +2 -2
- package/build/matchmaker/driver.cjs.map +1 -1
- package/build/matchmaker/driver.d.ts +12 -0
- package/build/matchmaker/driver.mjs.map +1 -1
- package/build/presence/LocalPresence.d.ts +1 -1
- package/build/rooms/LobbyRoom.cjs +8 -10
- package/build/rooms/LobbyRoom.cjs.map +2 -2
- package/build/rooms/LobbyRoom.d.ts +4 -3
- package/build/rooms/LobbyRoom.mjs +8 -10
- package/build/rooms/LobbyRoom.mjs.map +2 -2
- package/build/rooms/RelayRoom.cjs +12 -16
- package/build/rooms/RelayRoom.cjs.map +2 -2
- package/build/rooms/RelayRoom.d.ts +32 -11
- package/build/rooms/RelayRoom.mjs +10 -16
- package/build/rooms/RelayRoom.mjs.map +2 -2
- package/build/router/index.cjs +65 -4
- package/build/router/index.cjs.map +2 -2
- package/build/router/index.d.ts +30 -6
- package/build/router/index.mjs +66 -6
- package/build/router/index.mjs.map +3 -3
- package/build/utils/Env.cjs +4 -8
- package/build/utils/Env.cjs.map +3 -3
- package/build/utils/Env.mjs +4 -8
- package/build/utils/Env.mjs.map +2 -2
- package/build/utils/UserSessionIndex.cjs +162 -0
- package/build/utils/UserSessionIndex.cjs.map +7 -0
- package/build/utils/UserSessionIndex.d.ts +166 -0
- package/build/utils/UserSessionIndex.mjs +130 -0
- package/build/utils/UserSessionIndex.mjs.map +7 -0
- package/package.json +20 -15
- package/src/MatchMaker.ts +40 -6
- package/src/Protocol.ts +130 -59
- package/src/Room.ts +475 -22
- package/src/RoomPlugin.ts +563 -0
- package/src/Server.ts +81 -22
- package/src/Transport.ts +76 -8
- package/src/index.ts +10 -1
- package/src/input/InputBuffer.ts +192 -0
- package/src/internal.ts +46 -0
- package/src/matchmaker/LocalDriver/LocalDriver.ts +10 -0
- package/src/matchmaker/driver.ts +13 -0
- package/src/rooms/LobbyRoom.ts +12 -8
- package/src/rooms/RelayRoom.ts +9 -15
- package/src/router/index.ts +112 -11
- package/src/utils/Env.ts +4 -12
- package/src/utils/UserSessionIndex.ts +311 -0
package/src/Protocol.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Packr } from '
|
|
1
|
+
import { Packr, RESERVE_START_SPACE } from 'msgpackr';
|
|
2
2
|
import { encode, type Iterator } from '@colyseus/schema';
|
|
3
3
|
import { Protocol } from '@colyseus/shared-types';
|
|
4
4
|
|
|
@@ -11,101 +11,172 @@ export const IpcProtocol = {
|
|
|
11
11
|
export type IpcProtocol = typeof IpcProtocol[keyof typeof IpcProtocol];
|
|
12
12
|
|
|
13
13
|
const packr = new Packr({
|
|
14
|
-
useRecords: false, //
|
|
14
|
+
useRecords: false, // interop with non-msgpackr decoders
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
-
//
|
|
18
|
-
|
|
17
|
+
// Buffer for assembling outgoing frames; keeps us off msgpackr's internal
|
|
18
|
+
// buffer so `core` can use the upstream package directly.
|
|
19
|
+
let frameBuffer = Buffer.allocUnsafe(8192);
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
// Grow to `capacity`, preserving written bytes (`rawMessage` writes the header
|
|
22
|
+
// before the payload size is known).
|
|
23
|
+
function ensureFrameCapacity(capacity: number) {
|
|
24
|
+
if (capacity > frameBuffer.byteLength) {
|
|
25
|
+
const next = Buffer.allocUnsafe(capacity);
|
|
26
|
+
frameBuffer.copy(next);
|
|
27
|
+
frameBuffer = next;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
// Shared across calls to avoid a per-call allocation on the hot send path.
|
|
32
|
+
// Safe: encode.* mutate `.offset` in place and never re-enter this module.
|
|
33
|
+
const it: Iterator = { offset: 0 };
|
|
30
34
|
|
|
35
|
+
export const getMessageBytes = {
|
|
36
|
+
/**
|
|
37
|
+
* Build the JOIN_ROOM payload.
|
|
38
|
+
*
|
|
39
|
+
* Wire layout:
|
|
40
|
+
* [JOIN_ROOM byte][rt-len][rt][sid-len][sid][stateReflectionLen varint][stateReflection][...sections]
|
|
41
|
+
*
|
|
42
|
+
* The varint length prefix on `stateReflection` is required because the
|
|
43
|
+
* schema decoder on the SDK side runs `while (offset < bytes.byteLength)`
|
|
44
|
+
* — without a boundary it consumes past the state reflection into the
|
|
45
|
+
* trailing tagged-section bytes, producing "definition mismatch" warnings.
|
|
46
|
+
* Length is `0` when no state reflection is present.
|
|
47
|
+
*
|
|
48
|
+
* Each trailing section is `[tag (uint8)][length (varint)][payload]`. The
|
|
49
|
+
* SDK skips unknown tags via `length`, so new sections can be added without
|
|
50
|
+
* breaking older clients. See {@link HandshakeSection} in shared-types.
|
|
51
|
+
*/
|
|
52
|
+
[Protocol.JOIN_ROOM]: (
|
|
53
|
+
reconnectionToken: string,
|
|
54
|
+
serializerId: string,
|
|
55
|
+
handshake?: Uint8Array,
|
|
56
|
+
extraSections?: Array<{ tag: number; bytes: Uint8Array }>,
|
|
57
|
+
) => {
|
|
58
|
+
const reconnectionTokenLength = Buffer.byteLength(reconnectionToken, "utf8");
|
|
59
|
+
const serializerIdLength = Buffer.byteLength(serializerId, "utf8");
|
|
31
60
|
let handshakeLength = handshake?.byteLength || 0;
|
|
32
61
|
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
62
|
+
// per section: 1 tag byte + 9 (max varint) + payload
|
|
63
|
+
let extraLength = 0;
|
|
64
|
+
if (extraSections !== undefined) {
|
|
65
|
+
for (let i = 0; i < extraSections.length; i++) {
|
|
66
|
+
extraLength += 1 + 9 + extraSections[i].bytes.byteLength;
|
|
67
|
+
}
|
|
36
68
|
}
|
|
37
69
|
|
|
70
|
+
// capacity: header + 9 (handshake-len varint) + handshake + sections
|
|
71
|
+
ensureFrameCapacity(1 + 1 + reconnectionTokenLength + 1 + serializerIdLength + 9 + handshakeLength + extraLength);
|
|
72
|
+
|
|
73
|
+
it.offset = 1;
|
|
74
|
+
frameBuffer[0] = Protocol.JOIN_ROOM;
|
|
75
|
+
|
|
76
|
+
frameBuffer[it.offset++] = reconnectionTokenLength;
|
|
77
|
+
encode.utf8Write(frameBuffer, reconnectionToken, it);
|
|
78
|
+
|
|
79
|
+
frameBuffer[it.offset++] = serializerIdLength;
|
|
80
|
+
encode.utf8Write(frameBuffer, serializerId, it);
|
|
81
|
+
|
|
82
|
+
encode.number(frameBuffer, handshakeLength, it);
|
|
38
83
|
if (handshakeLength > 0) {
|
|
39
|
-
|
|
84
|
+
frameBuffer.set(handshake, it.offset);
|
|
85
|
+
it.offset += handshakeLength;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (extraSections !== undefined) {
|
|
89
|
+
for (let i = 0; i < extraSections.length; i++) {
|
|
90
|
+
const section = extraSections[i];
|
|
91
|
+
frameBuffer[it.offset++] = section.tag;
|
|
92
|
+
encode.number(frameBuffer, section.bytes.byteLength, it);
|
|
93
|
+
frameBuffer.set(section.bytes, it.offset);
|
|
94
|
+
it.offset += section.bytes.byteLength;
|
|
95
|
+
}
|
|
40
96
|
}
|
|
41
97
|
|
|
42
|
-
return Buffer.from(
|
|
98
|
+
return Buffer.from(frameBuffer.subarray(0, it.offset));
|
|
43
99
|
},
|
|
44
100
|
|
|
45
101
|
[Protocol.ERROR]: (code: number, message: string = '') => {
|
|
46
|
-
|
|
47
|
-
|
|
102
|
+
// capacity: 1 + code varint + length-prefixed message
|
|
103
|
+
ensureFrameCapacity(1 + 9 + 9 + Buffer.byteLength(message, "utf8"));
|
|
104
|
+
|
|
105
|
+
it.offset = 1;
|
|
106
|
+
frameBuffer[0] = Protocol.ERROR;
|
|
48
107
|
|
|
49
|
-
encode.number(
|
|
50
|
-
encode.string(
|
|
108
|
+
encode.number(frameBuffer, code, it);
|
|
109
|
+
encode.string(frameBuffer, message, it);
|
|
51
110
|
|
|
52
|
-
return Buffer.from(
|
|
111
|
+
return Buffer.from(frameBuffer.subarray(0, it.offset));
|
|
53
112
|
},
|
|
54
113
|
|
|
55
114
|
[Protocol.ROOM_STATE]: (bytes: number[]) => {
|
|
56
115
|
return [Protocol.ROOM_STATE, ...bytes];
|
|
57
116
|
},
|
|
58
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Reply to a client {@link Protocol.ROOM_REQUEST}.
|
|
120
|
+
*
|
|
121
|
+
* Wire layout: `[ROOM_RESPONSE byte][requestId varint][status uint8][msgpack payload?]`
|
|
122
|
+
*
|
|
123
|
+
* `requestId` is opaque — echoed back exactly as the SDK sent it so the
|
|
124
|
+
* pending callback/promise can be correlated. `status` is a
|
|
125
|
+
* {@link ResponseStatus} (0 = OK, 1 = ERROR). The payload is omitted when a
|
|
126
|
+
* handler resolves with `undefined`. Returns a fresh Buffer copy for the
|
|
127
|
+
* same back-pressure reason documented on `raw` below.
|
|
128
|
+
*/
|
|
129
|
+
[Protocol.ROOM_RESPONSE]: (requestId: number, status: number, message?: any): Buffer => {
|
|
130
|
+
it.offset = 1;
|
|
131
|
+
frameBuffer[0] = Protocol.ROOM_RESPONSE;
|
|
132
|
+
|
|
133
|
+
encode.number(frameBuffer, requestId, it);
|
|
134
|
+
frameBuffer[it.offset++] = status;
|
|
135
|
+
const headerLength = it.offset;
|
|
136
|
+
|
|
137
|
+
if (message !== undefined) {
|
|
138
|
+
// reserve `headerLength` bytes up front in the pack output for the header
|
|
139
|
+
const packed = packr.pack(message, RESERVE_START_SPACE | headerLength);
|
|
140
|
+
packed.set(frameBuffer.subarray(0, headerLength), 0);
|
|
141
|
+
return Buffer.from(packed);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return Buffer.from(frameBuffer.subarray(0, headerLength));
|
|
145
|
+
},
|
|
146
|
+
|
|
59
147
|
[Protocol.PING]: () => {
|
|
60
|
-
|
|
61
|
-
return Buffer.from(
|
|
148
|
+
frameBuffer[0] = Protocol.PING;
|
|
149
|
+
return Buffer.from(frameBuffer.subarray(0, 1));
|
|
62
150
|
},
|
|
63
151
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
152
|
+
// Returns a fresh copy: frameBuffer/packr are reused next call, but callers pass
|
|
153
|
+
// the result to async consumers (Node retains it for libuv writev across ticks).
|
|
154
|
+
// See benchmark/test-buffer-corruption.ts for the back-pressure repro.
|
|
155
|
+
raw: (code: Protocol, type: string | number, message?: any, rawMessage?: Uint8Array | Buffer): Buffer => {
|
|
156
|
+
it.offset = 1;
|
|
157
|
+
frameBuffer[0] = code;
|
|
67
158
|
|
|
68
159
|
if (typeof (type) === 'string') {
|
|
69
|
-
encode.string(
|
|
160
|
+
encode.string(frameBuffer, type, it);
|
|
70
161
|
|
|
71
162
|
} else {
|
|
72
|
-
encode.number(
|
|
163
|
+
encode.number(frameBuffer, type, it);
|
|
73
164
|
}
|
|
165
|
+
const headerLength = it.offset;
|
|
74
166
|
|
|
75
167
|
if (message !== undefined) {
|
|
76
|
-
//
|
|
77
|
-
packr.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
// TODO: remove this after issue is fixed https://github.com/kriszyp/msgpackr/issues/139
|
|
81
|
-
//
|
|
82
|
-
// - This check is only required when running integration tests.
|
|
83
|
-
// (colyseus.js' usage of msgpackr/buffer is conflicting)
|
|
84
|
-
//
|
|
85
|
-
if (process.env.NODE_ENV !== "production") {
|
|
86
|
-
packr.useBuffer(packr.buffer);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// pack message into the same packr.buffer
|
|
90
|
-
const endOfBufferOffset = packr.pack(message, 2048 + it.offset).byteLength;
|
|
91
|
-
// 2048 = RESERVE_START_SPACE
|
|
92
|
-
return Buffer.from(packr.buffer.subarray(0, endOfBufferOffset));
|
|
168
|
+
// reserve `headerLength` bytes up front in the pack output for the header
|
|
169
|
+
const packed = packr.pack(message, RESERVE_START_SPACE | headerLength);
|
|
170
|
+
packed.set(frameBuffer.subarray(0, headerLength), 0);
|
|
171
|
+
return Buffer.from(packed);
|
|
93
172
|
|
|
94
173
|
} else if (rawMessage !== undefined) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (rawMessage.length + it.offset > packr.buffer.byteLength) {
|
|
99
|
-
packr.useBuffer(Buffer.alloc(it.offset + rawMessage.length, packr.buffer));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// copy raw message into packr.buffer
|
|
103
|
-
packr.buffer.set(rawMessage, it.offset);
|
|
104
|
-
|
|
105
|
-
return Buffer.from(packr.buffer.subarray(0, it.offset + rawMessage.byteLength));
|
|
174
|
+
ensureFrameCapacity(headerLength + rawMessage.byteLength);
|
|
175
|
+
frameBuffer.set(rawMessage, headerLength);
|
|
176
|
+
return Buffer.from(frameBuffer.subarray(0, headerLength + rawMessage.byteLength));
|
|
106
177
|
|
|
107
178
|
} else {
|
|
108
|
-
return Buffer.from(
|
|
179
|
+
return Buffer.from(frameBuffer.subarray(0, headerLength));
|
|
109
180
|
}
|
|
110
181
|
},
|
|
111
182
|
|