@mml-io/3d-web-user-networking 0.22.0 → 0.23.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/DeltaNetComponentMapping.d.ts +58 -0
- package/build/UserData.d.ts +3 -2
- package/build/UserNetworkingClient.d.ts +40 -14
- package/build/UserNetworkingLogger.d.ts +15 -0
- package/build/UserNetworkingMessages.d.ts +14 -53
- package/build/UserNetworkingServer.d.ts +31 -20
- package/build/index.d.ts +5 -2
- package/build/index.js +1338 -391
- package/build/index.js.map +4 -4
- package/build/legacy/LegacyAdapter.d.ts +29 -0
- package/build/legacy/LegacyUserNetworkingCodec.d.ts +17 -0
- package/build/legacy/LegacyUserNetworkingMessages.d.ts +76 -0
- package/build/{UserNetworkingCodec.d.ts → types.d.ts} +7 -5
- package/package.json +15 -5
- package/build/ReconnectingWebSocket.d.ts +0 -27
- package/build/user-networking-settings.d.ts +0 -3
package/build/index.js
CHANGED
@@ -1,5 +1,272 @@
|
|
1
|
-
// src/
|
2
|
-
|
1
|
+
// src/UserNetworkingServer.ts
|
2
|
+
import { encodeError, DeltaNetV01ServerErrors } from "@mml-io/delta-net-protocol";
|
3
|
+
import {
|
4
|
+
DeltaNetServer,
|
5
|
+
DeltaNetServerError as DeltaNetServerError2
|
6
|
+
} from "@mml-io/delta-net-server";
|
7
|
+
|
8
|
+
// src/DeltaNetComponentMapping.ts
|
9
|
+
import { BufferReader, BufferWriter } from "@mml-io/delta-net-protocol";
|
10
|
+
var COMPONENT_POSITION_X = 1;
|
11
|
+
var COMPONENT_POSITION_Y = 2;
|
12
|
+
var COMPONENT_POSITION_Z = 3;
|
13
|
+
var COMPONENT_ROTATION_Y = 4;
|
14
|
+
var COMPONENT_ROTATION_W = 5;
|
15
|
+
var COMPONENT_STATE = 6;
|
16
|
+
var STATE_INTERNAL_CONNECTION_ID = 0;
|
17
|
+
var STATE_CHARACTER_DESCRIPTION = 1;
|
18
|
+
var STATE_USERNAME = 2;
|
19
|
+
var STATE_COLORS = 3;
|
20
|
+
var rotationMultiplier = 360;
|
21
|
+
var positionMultiplier = 100;
|
22
|
+
var textDecoder = new TextDecoder();
|
23
|
+
var DeltaNetComponentMapping = class _DeltaNetComponentMapping {
|
24
|
+
/**
|
25
|
+
* Convert UserNetworkingClientUpdate to deltanet components
|
26
|
+
*/
|
27
|
+
static toComponents(update) {
|
28
|
+
const components = /* @__PURE__ */ new Map();
|
29
|
+
components.set(
|
30
|
+
COMPONENT_POSITION_X,
|
31
|
+
BigInt(Math.round(update.position.x * positionMultiplier))
|
32
|
+
);
|
33
|
+
components.set(
|
34
|
+
COMPONENT_POSITION_Y,
|
35
|
+
BigInt(Math.round(update.position.y * positionMultiplier))
|
36
|
+
);
|
37
|
+
components.set(
|
38
|
+
COMPONENT_POSITION_Z,
|
39
|
+
BigInt(Math.round(update.position.z * positionMultiplier))
|
40
|
+
);
|
41
|
+
components.set(
|
42
|
+
COMPONENT_ROTATION_Y,
|
43
|
+
BigInt(Math.round(update.rotation.quaternionY * rotationMultiplier))
|
44
|
+
);
|
45
|
+
components.set(
|
46
|
+
COMPONENT_ROTATION_W,
|
47
|
+
BigInt(Math.round(update.rotation.quaternionW * rotationMultiplier))
|
48
|
+
);
|
49
|
+
components.set(COMPONENT_STATE, BigInt(update.state));
|
50
|
+
return components;
|
51
|
+
}
|
52
|
+
/**
|
53
|
+
* Convert deltanet components back to UserNetworkingClientUpdate
|
54
|
+
*/
|
55
|
+
static fromComponents(components) {
|
56
|
+
const positionX = Number(components.get(COMPONENT_POSITION_X) || BigInt(0)) / positionMultiplier;
|
57
|
+
const positionY = Number(components.get(COMPONENT_POSITION_Y) || BigInt(0)) / positionMultiplier;
|
58
|
+
const positionZ = Number(components.get(COMPONENT_POSITION_Z) || BigInt(0)) / positionMultiplier;
|
59
|
+
const rotationY = Number(components.get(COMPONENT_ROTATION_Y) || BigInt(0)) / rotationMultiplier;
|
60
|
+
const rotationW = Number(components.get(COMPONENT_ROTATION_W) || BigInt(0)) / rotationMultiplier;
|
61
|
+
const state = Number(components.get(COMPONENT_STATE) || BigInt(0));
|
62
|
+
return {
|
63
|
+
position: { x: positionX, y: positionY, z: positionZ },
|
64
|
+
rotation: { quaternionY: rotationY, quaternionW: rotationW },
|
65
|
+
state
|
66
|
+
};
|
67
|
+
}
|
68
|
+
/**
|
69
|
+
* Encode character description and username to binary states
|
70
|
+
*/
|
71
|
+
static toStates(userIdentity) {
|
72
|
+
const states = /* @__PURE__ */ new Map();
|
73
|
+
const textEncoder = new TextEncoder();
|
74
|
+
if (userIdentity.username) {
|
75
|
+
states.set(STATE_USERNAME, textEncoder.encode(userIdentity.username));
|
76
|
+
}
|
77
|
+
if (userIdentity.characterDescription) {
|
78
|
+
states.set(
|
79
|
+
STATE_CHARACTER_DESCRIPTION,
|
80
|
+
textEncoder.encode(JSON.stringify(userIdentity.characterDescription))
|
81
|
+
);
|
82
|
+
}
|
83
|
+
if (userIdentity.colors) {
|
84
|
+
states.set(STATE_COLORS, _DeltaNetComponentMapping.encodeColors(userIdentity.colors));
|
85
|
+
}
|
86
|
+
return states;
|
87
|
+
}
|
88
|
+
/**
|
89
|
+
* Encode username to binary state
|
90
|
+
*/
|
91
|
+
static toUsernameState(username) {
|
92
|
+
const states = /* @__PURE__ */ new Map();
|
93
|
+
const textEncoder = new TextEncoder();
|
94
|
+
states.set(STATE_USERNAME, textEncoder.encode(username));
|
95
|
+
return states;
|
96
|
+
}
|
97
|
+
/**
|
98
|
+
* Encode character description to binary state
|
99
|
+
*/
|
100
|
+
static toCharacterDescriptionState(characterDescription) {
|
101
|
+
const states = /* @__PURE__ */ new Map();
|
102
|
+
const textEncoder = new TextEncoder();
|
103
|
+
states.set(
|
104
|
+
STATE_CHARACTER_DESCRIPTION,
|
105
|
+
textEncoder.encode(JSON.stringify(characterDescription))
|
106
|
+
);
|
107
|
+
return states;
|
108
|
+
}
|
109
|
+
/**
|
110
|
+
* Encode colors to binary state
|
111
|
+
*/
|
112
|
+
static toColorsState(colors) {
|
113
|
+
const states = /* @__PURE__ */ new Map();
|
114
|
+
states.set(STATE_COLORS, _DeltaNetComponentMapping.encodeColors(colors));
|
115
|
+
return states;
|
116
|
+
}
|
117
|
+
/**
|
118
|
+
* Encode single state value
|
119
|
+
*/
|
120
|
+
static toSingleState(stateId, value) {
|
121
|
+
const states = /* @__PURE__ */ new Map();
|
122
|
+
const textEncoder = new TextEncoder();
|
123
|
+
switch (stateId) {
|
124
|
+
case STATE_USERNAME:
|
125
|
+
if (typeof value === "string") {
|
126
|
+
states.set(stateId, textEncoder.encode(value));
|
127
|
+
}
|
128
|
+
break;
|
129
|
+
case STATE_CHARACTER_DESCRIPTION:
|
130
|
+
if (typeof value === "object" && value !== null) {
|
131
|
+
states.set(stateId, textEncoder.encode(JSON.stringify(value)));
|
132
|
+
}
|
133
|
+
break;
|
134
|
+
case STATE_COLORS:
|
135
|
+
if (Array.isArray(value)) {
|
136
|
+
states.set(stateId, _DeltaNetComponentMapping.encodeColors(value));
|
137
|
+
}
|
138
|
+
break;
|
139
|
+
}
|
140
|
+
return states;
|
141
|
+
}
|
142
|
+
static encodeColors(colors) {
|
143
|
+
const bufferWriter = new BufferWriter(3 * colors.length + 1);
|
144
|
+
bufferWriter.writeUVarint(colors.length);
|
145
|
+
for (const color of colors) {
|
146
|
+
bufferWriter.writeUVarint(color[0]);
|
147
|
+
bufferWriter.writeUVarint(color[1]);
|
148
|
+
bufferWriter.writeUVarint(color[2]);
|
149
|
+
}
|
150
|
+
return bufferWriter.getBuffer();
|
151
|
+
}
|
152
|
+
static decodeColors(colors, logger) {
|
153
|
+
if (colors.byteLength === 0) {
|
154
|
+
return [];
|
155
|
+
}
|
156
|
+
try {
|
157
|
+
const bufferReader = new BufferReader(colors);
|
158
|
+
const colorsArray = [];
|
159
|
+
const count = bufferReader.readUVarint();
|
160
|
+
for (let i = 0; i < count; i++) {
|
161
|
+
colorsArray.push([
|
162
|
+
bufferReader.readUVarint(),
|
163
|
+
bufferReader.readUVarint(),
|
164
|
+
bufferReader.readUVarint()
|
165
|
+
]);
|
166
|
+
}
|
167
|
+
return colorsArray;
|
168
|
+
} catch (e) {
|
169
|
+
logger.error("Error decoding colors", colors, e);
|
170
|
+
return [];
|
171
|
+
}
|
172
|
+
}
|
173
|
+
static fromUserStates(states, logger) {
|
174
|
+
const usernameBytes = states.get(STATE_USERNAME);
|
175
|
+
const username = usernameBytes ? _DeltaNetComponentMapping.usernameFromBytes(usernameBytes) : null;
|
176
|
+
const characterDescBytes = states.get(STATE_CHARACTER_DESCRIPTION);
|
177
|
+
const characterDescription = characterDescBytes ? _DeltaNetComponentMapping.characterDescriptionFromBytes(characterDescBytes) : null;
|
178
|
+
const colorsBytes = states.get(STATE_COLORS);
|
179
|
+
const colorsArray = colorsBytes ? _DeltaNetComponentMapping.decodeColors(colorsBytes, logger) : [];
|
180
|
+
return { username, characterDescription, colors: colorsArray };
|
181
|
+
}
|
182
|
+
static userIdFromBytes(bytes) {
|
183
|
+
if (bytes.length === 0) {
|
184
|
+
return null;
|
185
|
+
}
|
186
|
+
const reader = new BufferReader(bytes);
|
187
|
+
return reader.readUVarint(false);
|
188
|
+
}
|
189
|
+
static usernameFromBytes(bytes) {
|
190
|
+
if (bytes.length === 0) {
|
191
|
+
return null;
|
192
|
+
}
|
193
|
+
return textDecoder.decode(bytes);
|
194
|
+
}
|
195
|
+
static characterDescriptionFromBytes(bytes) {
|
196
|
+
if (bytes.length === 0) {
|
197
|
+
return null;
|
198
|
+
}
|
199
|
+
return JSON.parse(textDecoder.decode(bytes));
|
200
|
+
}
|
201
|
+
/**
|
202
|
+
* Decode binary states back to username and character description
|
203
|
+
*/
|
204
|
+
static fromStates(states, logger) {
|
205
|
+
const userIdBytes = states.get(STATE_INTERNAL_CONNECTION_ID);
|
206
|
+
let userId = null;
|
207
|
+
if (userIdBytes) {
|
208
|
+
const reader = new BufferReader(userIdBytes);
|
209
|
+
userId = reader.readUVarint(false);
|
210
|
+
}
|
211
|
+
const userStates = _DeltaNetComponentMapping.fromUserStates(states, logger);
|
212
|
+
return { userId, ...userStates };
|
213
|
+
}
|
214
|
+
};
|
215
|
+
|
216
|
+
// src/UserNetworkingMessages.ts
|
217
|
+
import { DeltaNetServerError } from "@mml-io/delta-net-server";
|
218
|
+
var UserNetworkingServerError = class extends DeltaNetServerError {
|
219
|
+
};
|
220
|
+
var SERVER_BROADCAST_MESSAGE_TYPE = 1;
|
221
|
+
var FROM_CLIENT_CHAT_MESSAGE_TYPE = 2;
|
222
|
+
var FROM_SERVER_CHAT_MESSAGE_TYPE = 3;
|
223
|
+
function parseClientChatMessage(contents) {
|
224
|
+
try {
|
225
|
+
const parsed = JSON.parse(contents);
|
226
|
+
if (typeof parsed === "object" && parsed !== null && "message" in parsed && typeof parsed.message === "string") {
|
227
|
+
return {
|
228
|
+
message: parsed.message
|
229
|
+
};
|
230
|
+
} else {
|
231
|
+
throw new Error("Invalid chat message");
|
232
|
+
}
|
233
|
+
} catch (error) {
|
234
|
+
return new Error(`Invalid chat message: ${error}`);
|
235
|
+
}
|
236
|
+
}
|
237
|
+
function parseServerChatMessage(contents) {
|
238
|
+
try {
|
239
|
+
const parsed = JSON.parse(contents);
|
240
|
+
if (typeof parsed === "object" && parsed !== null && "fromUserId" in parsed && typeof parsed.fromUserId === "number" && "message" in parsed && typeof parsed.message === "string") {
|
241
|
+
return {
|
242
|
+
fromUserId: parsed.fromUserId,
|
243
|
+
message: parsed.message
|
244
|
+
};
|
245
|
+
} else {
|
246
|
+
throw new Error("Invalid server chat message");
|
247
|
+
}
|
248
|
+
} catch (error) {
|
249
|
+
return new Error(`Invalid server chat message: ${error}`);
|
250
|
+
}
|
251
|
+
}
|
252
|
+
function parseServerBroadcastMessage(contents) {
|
253
|
+
try {
|
254
|
+
const parsed = JSON.parse(contents);
|
255
|
+
if (typeof parsed === "object" && parsed !== null && "broadcastType" in parsed && typeof parsed.broadcastType === "string" && "payload" in parsed && typeof parsed.payload === "object") {
|
256
|
+
return {
|
257
|
+
broadcastType: parsed.broadcastType,
|
258
|
+
payload: parsed.payload
|
259
|
+
};
|
260
|
+
} else {
|
261
|
+
throw new Error("Invalid server broadcast message");
|
262
|
+
}
|
263
|
+
} catch (error) {
|
264
|
+
return new Error(`Invalid server broadcast message: ${error}`);
|
265
|
+
}
|
266
|
+
}
|
267
|
+
|
268
|
+
// src/legacy/LegacyUserNetworkingCodec.ts
|
269
|
+
var LegacyUserNetworkingCodec = class {
|
3
270
|
static encodeUpdate(update) {
|
4
271
|
const buffer = new ArrayBuffer(19);
|
5
272
|
const dataView = new DataView(buffer);
|
@@ -27,78 +294,63 @@ var UserNetworkingCodec = class {
|
|
27
294
|
}
|
28
295
|
};
|
29
296
|
|
30
|
-
// src/
|
31
|
-
var
|
32
|
-
var
|
33
|
-
var
|
34
|
-
|
35
|
-
|
36
|
-
var
|
37
|
-
var
|
38
|
-
var
|
39
|
-
var
|
40
|
-
var
|
41
|
-
var
|
42
|
-
var
|
43
|
-
var
|
44
|
-
var USER_NETWORKING_PONG_MESSAGE_TYPE = "pong";
|
45
|
-
var USER_NETWORKING_CONNECTION_LIMIT_REACHED_ERROR_TYPE = "CONNECTION_LIMIT_REACHED";
|
46
|
-
var USER_NETWORKING_AUTHENTICATION_FAILED_ERROR_TYPE = "AUTHENTICATION_FAILED";
|
47
|
-
var USER_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE = "SERVER_SHUTDOWN";
|
48
|
-
var USER_NETWORKING_UNKNOWN_ERROR = "UNKNOWN_ERROR";
|
297
|
+
// src/legacy/LegacyUserNetworkingMessages.ts
|
298
|
+
var LEGACY_USER_NETWORKING_DISCONNECTED_MESSAGE_TYPE = "disconnected";
|
299
|
+
var LEGACY_USER_NETWORKING_IDENTITY_MESSAGE_TYPE = "identity";
|
300
|
+
var LEGACY_USER_NETWORKING_USER_AUTHENTICATE_MESSAGE_TYPE = "user_auth";
|
301
|
+
var LEGACY_USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE = "user_profile";
|
302
|
+
var LEGACY_USER_NETWORKING_USER_UPDATE_MESSAGE_TYPE = "user_update";
|
303
|
+
var LEGACY_USER_NETWORKING_SERVER_BROADCAST_MESSAGE_TYPE = "broadcast";
|
304
|
+
var LEGACY_USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE = "error";
|
305
|
+
var LEGACY_USER_NETWORKING_PING_MESSAGE_TYPE = "ping";
|
306
|
+
var LEGACY_USER_NETWORKING_PONG_MESSAGE_TYPE = "pong";
|
307
|
+
var LEGACY_USER_NETWORKING_CONNECTION_LIMIT_REACHED_ERROR_TYPE = "CONNECTION_LIMIT_REACHED";
|
308
|
+
var LEGACY_USER_NETWORKING_AUTHENTICATION_FAILED_ERROR_TYPE = "AUTHENTICATION_FAILED";
|
309
|
+
var LEGACY_USER_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE = "SERVER_SHUTDOWN";
|
310
|
+
var LEGACY_USER_NETWORKING_UNKNOWN_ERROR = "UNKNOWN_ERROR";
|
49
311
|
|
50
|
-
// src/
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
this.authenticatedClientsById = /* @__PURE__ */ new Map();
|
57
|
-
this.sendUpdatesIntervalTimer = setInterval(this.sendUpdates.bind(this), packetsUpdateRate);
|
58
|
-
this.pingClientsIntervalTimer = setInterval(this.pingClients.bind(this), pingPongRate);
|
59
|
-
this.heartbeatIntervalTimer = setInterval(this.heartBeat.bind(this), heartBeatRate);
|
60
|
-
}
|
61
|
-
heartBeat() {
|
62
|
-
const now = Date.now();
|
63
|
-
this.allClientsById.forEach((client) => {
|
64
|
-
if (now - client.lastPong > heartBeatRate) {
|
65
|
-
client.socket.close();
|
66
|
-
this.handleDisconnectedClient(client);
|
67
|
-
}
|
68
|
-
});
|
69
|
-
}
|
70
|
-
pingClients() {
|
71
|
-
const message = { type: "ping" };
|
72
|
-
const messageString = JSON.stringify(message);
|
73
|
-
this.authenticatedClientsById.forEach((client) => {
|
74
|
-
if (client.socket.readyState === WebSocketOpenStatus) {
|
75
|
-
client.socket.send(messageString);
|
76
|
-
}
|
77
|
-
});
|
312
|
+
// src/legacy/LegacyAdapter.ts
|
313
|
+
function toArrayBuffer(buffer) {
|
314
|
+
const arrayBuffer = new ArrayBuffer(buffer.length);
|
315
|
+
const view = new Uint8Array(arrayBuffer);
|
316
|
+
for (let i = 0; i < buffer.length; ++i) {
|
317
|
+
view[i] = buffer[i];
|
78
318
|
}
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
319
|
+
return arrayBuffer;
|
320
|
+
}
|
321
|
+
var WebSocketOpenStatus = 1;
|
322
|
+
var LegacyAdapter = class {
|
323
|
+
constructor(userNetworkingServer, deltaNetServer, logger) {
|
324
|
+
this.userNetworkingServer = userNetworkingServer;
|
325
|
+
this.deltaNetServer = deltaNetServer;
|
326
|
+
this.logger = logger;
|
85
327
|
}
|
328
|
+
allClientsById = /* @__PURE__ */ new Map();
|
329
|
+
legacyAuthenticatedClientsById = /* @__PURE__ */ new Map();
|
86
330
|
broadcastMessage(broadcastType, broadcastPayload) {
|
331
|
+
if (broadcastType !== SERVER_BROADCAST_MESSAGE_TYPE) {
|
332
|
+
return;
|
333
|
+
}
|
334
|
+
const parsedPayload = parseServerBroadcastMessage(broadcastPayload);
|
335
|
+
if (parsedPayload instanceof Error) {
|
336
|
+
this.logger.error("Error parsing server broadcast message", parsedPayload);
|
337
|
+
return;
|
338
|
+
}
|
339
|
+
const { broadcastType: broadcastTypeString, payload } = parsedPayload;
|
87
340
|
const message = {
|
88
341
|
type: "broadcast",
|
89
|
-
broadcastType,
|
90
|
-
payload
|
342
|
+
broadcastType: broadcastTypeString,
|
343
|
+
payload
|
91
344
|
};
|
92
345
|
const messageString = JSON.stringify(message);
|
93
|
-
for (const [, client] of this.
|
346
|
+
for (const [, client] of this.legacyAuthenticatedClientsById) {
|
94
347
|
if (client.socket.readyState === WebSocketOpenStatus) {
|
95
348
|
client.socket.send(messageString);
|
96
349
|
}
|
97
350
|
}
|
98
351
|
}
|
99
|
-
|
100
|
-
const id = this.
|
101
|
-
console.log(`Client ID: ${id} joined, waiting for user-identification`);
|
352
|
+
addWebSocket(socket) {
|
353
|
+
const id = this.userNetworkingServer.getLegacyClientId();
|
102
354
|
const client = {
|
103
355
|
id,
|
104
356
|
lastPong: Date.now(),
|
@@ -112,100 +364,185 @@ var UserNetworkingServer = class {
|
|
112
364
|
}
|
113
365
|
};
|
114
366
|
this.allClientsById.set(id, client);
|
115
|
-
socket.
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
367
|
+
socket.addEventListener("message", (message) => {
|
368
|
+
try {
|
369
|
+
if (message.data instanceof ArrayBuffer || message.data instanceof Buffer) {
|
370
|
+
if (client.authenticatedUser) {
|
371
|
+
const arrayBuffer = message.data instanceof ArrayBuffer ? message.data : toArrayBuffer(message.data);
|
372
|
+
const update = LegacyUserNetworkingCodec.decodeUpdate(arrayBuffer);
|
373
|
+
update.id = id;
|
374
|
+
const index = this.deltaNetServer.dangerouslyGetConnectionsToComponentIndex().get(id);
|
375
|
+
client.update = update;
|
376
|
+
if (index !== void 0) {
|
377
|
+
this.deltaNetServer.setComponentValue(
|
378
|
+
COMPONENT_POSITION_X,
|
379
|
+
index,
|
380
|
+
BigInt(Math.round(update.position.x * positionMultiplier))
|
381
|
+
);
|
382
|
+
this.deltaNetServer.setComponentValue(
|
383
|
+
COMPONENT_POSITION_Y,
|
384
|
+
index,
|
385
|
+
BigInt(Math.round(update.position.y * positionMultiplier))
|
386
|
+
);
|
387
|
+
this.deltaNetServer.setComponentValue(
|
388
|
+
COMPONENT_POSITION_Z,
|
389
|
+
index,
|
390
|
+
BigInt(Math.round(update.position.z * positionMultiplier))
|
391
|
+
);
|
392
|
+
this.deltaNetServer.setComponentValue(
|
393
|
+
COMPONENT_ROTATION_Y,
|
394
|
+
index,
|
395
|
+
BigInt(Math.round(update.rotation.quaternionY * rotationMultiplier))
|
396
|
+
);
|
397
|
+
this.deltaNetServer.setComponentValue(
|
398
|
+
COMPONENT_ROTATION_W,
|
399
|
+
index,
|
400
|
+
BigInt(Math.round(update.rotation.quaternionW * rotationMultiplier))
|
401
|
+
);
|
402
|
+
this.deltaNetServer.setComponentValue(
|
403
|
+
COMPONENT_STATE,
|
404
|
+
index,
|
405
|
+
BigInt(Math.round(update.state))
|
406
|
+
);
|
407
|
+
}
|
408
|
+
}
|
409
|
+
} else {
|
410
|
+
let parsed;
|
411
|
+
try {
|
412
|
+
parsed = JSON.parse(message.data);
|
413
|
+
} catch (e) {
|
414
|
+
this.logger.error("Error parsing JSON message", message, e);
|
415
|
+
return;
|
416
|
+
}
|
417
|
+
if (!client.authenticatedUser) {
|
418
|
+
if (parsed.type === LEGACY_USER_NETWORKING_USER_AUTHENTICATE_MESSAGE_TYPE) {
|
419
|
+
this.handleUserAuth(client, parsed).then((authResult) => {
|
420
|
+
if (client.socket.readyState !== WebSocketOpenStatus) {
|
421
|
+
return;
|
422
|
+
}
|
423
|
+
if (!authResult) {
|
424
|
+
this.logger.error(`Client-id ${client.id} user_auth failed`, authResult);
|
146
425
|
const serverError = JSON.stringify({
|
147
|
-
type:
|
148
|
-
errorType:
|
149
|
-
message: "
|
426
|
+
type: LEGACY_USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE,
|
427
|
+
errorType: LEGACY_USER_NETWORKING_AUTHENTICATION_FAILED_ERROR_TYPE,
|
428
|
+
message: "Authentication failed"
|
150
429
|
});
|
151
430
|
socket.send(serverError);
|
152
431
|
socket.close();
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
client.socket.send(userProfileMessage);
|
164
|
-
const identityMessage = JSON.stringify({
|
165
|
-
id: client.id,
|
166
|
-
type: USER_NETWORKING_IDENTITY_MESSAGE_TYPE
|
167
|
-
});
|
168
|
-
client.socket.send(identityMessage);
|
169
|
-
const userUpdateMessage = UserNetworkingCodec.encodeUpdate(client.update);
|
170
|
-
for (const [, otherClient] of this.authenticatedClientsById) {
|
171
|
-
if (otherClient.socket.readyState !== WebSocketOpenStatus || otherClient === client) {
|
172
|
-
continue;
|
432
|
+
} else {
|
433
|
+
if (!this.userNetworkingServer.hasCapacityForLegacyClient()) {
|
434
|
+
const serverError = JSON.stringify({
|
435
|
+
type: LEGACY_USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE,
|
436
|
+
errorType: LEGACY_USER_NETWORKING_CONNECTION_LIMIT_REACHED_ERROR_TYPE,
|
437
|
+
message: "Connection limit reached"
|
438
|
+
});
|
439
|
+
socket.send(serverError);
|
440
|
+
socket.close();
|
441
|
+
return;
|
173
442
|
}
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
443
|
+
const userData = authResult;
|
444
|
+
this.deltaNetServer.dangerouslyAddNewJoinerCallback((index) => {
|
445
|
+
if (client.socket.readyState !== WebSocketOpenStatus) {
|
446
|
+
return null;
|
447
|
+
}
|
448
|
+
client.authenticatedUser = userData;
|
449
|
+
this.deltaNetServer.setComponentValue(COMPONENT_POSITION_X, index, BigInt(0));
|
450
|
+
this.deltaNetServer.setComponentValue(COMPONENT_POSITION_Y, index, BigInt(0));
|
451
|
+
this.deltaNetServer.setComponentValue(COMPONENT_POSITION_Z, index, BigInt(0));
|
452
|
+
this.deltaNetServer.setComponentValue(COMPONENT_ROTATION_Y, index, BigInt(0));
|
453
|
+
this.deltaNetServer.setComponentValue(COMPONENT_ROTATION_W, index, BigInt(0));
|
454
|
+
this.deltaNetServer.setComponentValue(COMPONENT_STATE, index, BigInt(0));
|
455
|
+
const asUserData = {
|
456
|
+
...userData,
|
457
|
+
colors: []
|
458
|
+
};
|
459
|
+
return {
|
460
|
+
id: client.id,
|
461
|
+
afterAddCallback: () => {
|
462
|
+
this.userNetworkingServer.setAuthenticatedLegacyClientConnection(
|
463
|
+
client.id,
|
464
|
+
client.socket,
|
465
|
+
asUserData
|
466
|
+
);
|
467
|
+
this.userNetworkingServer.updateUserCharacter(client.id, asUserData);
|
468
|
+
}
|
469
|
+
};
|
470
|
+
});
|
471
|
+
const userProfileMessage = JSON.stringify({
|
472
|
+
id: client.id,
|
473
|
+
type: LEGACY_USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE,
|
474
|
+
username: userData.username,
|
475
|
+
characterDescription: userData.characterDescription
|
476
|
+
});
|
477
|
+
client.socket.send(userProfileMessage);
|
478
|
+
const identityMessage = JSON.stringify({
|
479
|
+
id: client.id,
|
480
|
+
type: LEGACY_USER_NETWORKING_IDENTITY_MESSAGE_TYPE
|
481
|
+
});
|
482
|
+
client.socket.send(identityMessage);
|
483
|
+
const allUsers = this.deltaNetServer.dangerouslyGetConnectionsToComponentIndex();
|
484
|
+
for (const [connectionId, componentIndex] of allUsers) {
|
485
|
+
if (connectionId === client.id) {
|
486
|
+
continue;
|
487
|
+
}
|
488
|
+
const x = this.deltaNetServer.getComponentValue(COMPONENT_POSITION_X, componentIndex) / positionMultiplier;
|
489
|
+
const y = this.deltaNetServer.getComponentValue(COMPONENT_POSITION_Y, componentIndex) / positionMultiplier;
|
490
|
+
const z = this.deltaNetServer.getComponentValue(COMPONENT_POSITION_Z, componentIndex) / positionMultiplier;
|
491
|
+
const quaternionY = this.deltaNetServer.getComponentValue(COMPONENT_ROTATION_Y, componentIndex) / rotationMultiplier;
|
492
|
+
const quaternionW = this.deltaNetServer.getComponentValue(COMPONENT_ROTATION_W, componentIndex) / rotationMultiplier;
|
493
|
+
const state = this.deltaNetServer.getComponentValue(
|
494
|
+
COMPONENT_STATE,
|
495
|
+
componentIndex
|
496
|
+
);
|
497
|
+
const update = LegacyUserNetworkingCodec.encodeUpdate({
|
498
|
+
id: connectionId,
|
499
|
+
position: { x, y, z },
|
500
|
+
rotation: { quaternionY, quaternionW },
|
501
|
+
state
|
502
|
+
});
|
503
|
+
client.socket.send(
|
504
|
+
JSON.stringify({
|
505
|
+
id: connectionId,
|
506
|
+
type: LEGACY_USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE,
|
507
|
+
username: this.userNetworkingServer.getUsername(connectionId),
|
508
|
+
characterDescription: this.userNetworkingServer.getCharacterDescription(connectionId)
|
509
|
+
})
|
510
|
+
);
|
511
|
+
client.socket.send(update);
|
512
|
+
}
|
513
|
+
this.legacyAuthenticatedClientsById.set(id, client);
|
185
514
|
}
|
186
|
-
|
187
|
-
|
188
|
-
|
515
|
+
});
|
516
|
+
} else {
|
517
|
+
this.logger.error(`Unhandled message pre-auth: ${JSON.stringify(parsed)}`);
|
518
|
+
socket.close();
|
519
|
+
}
|
189
520
|
} else {
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
break;
|
201
|
-
default:
|
202
|
-
console.error(`Unhandled message: ${JSON.stringify(parsed)}`);
|
521
|
+
switch (parsed.type) {
|
522
|
+
case LEGACY_USER_NETWORKING_PONG_MESSAGE_TYPE:
|
523
|
+
client.lastPong = Date.now();
|
524
|
+
break;
|
525
|
+
case LEGACY_USER_NETWORKING_USER_UPDATE_MESSAGE_TYPE:
|
526
|
+
this.handleUserUpdate(id, parsed);
|
527
|
+
break;
|
528
|
+
default:
|
529
|
+
this.logger.error(`Unhandled message: ${JSON.stringify(parsed)}`);
|
530
|
+
}
|
203
531
|
}
|
204
532
|
}
|
533
|
+
} catch (e) {
|
534
|
+
this.logger.error("Error handling message", message, e);
|
535
|
+
socket.send(
|
536
|
+
JSON.stringify({
|
537
|
+
type: LEGACY_USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE,
|
538
|
+
errorType: LEGACY_USER_NETWORKING_UNKNOWN_ERROR,
|
539
|
+
message: "Error handling message"
|
540
|
+
})
|
541
|
+
);
|
542
|
+
socket.close();
|
205
543
|
}
|
206
544
|
});
|
207
|
-
socket.
|
208
|
-
console.log("Client disconnected", id);
|
545
|
+
socket.addEventListener("close", () => {
|
209
546
|
this.handleDisconnectedClient(client);
|
210
547
|
});
|
211
548
|
}
|
@@ -215,21 +552,13 @@ var UserNetworkingServer = class {
|
|
215
552
|
}
|
216
553
|
this.allClientsById.delete(client.id);
|
217
554
|
if (client.authenticatedUser !== null) {
|
218
|
-
this.
|
219
|
-
this.
|
220
|
-
|
221
|
-
id: client.id,
|
222
|
-
type: USER_NETWORKING_DISCONNECTED_MESSAGE_TYPE
|
223
|
-
});
|
224
|
-
for (const [, otherClient] of this.authenticatedClientsById) {
|
225
|
-
if (otherClient.socket.readyState === WebSocketOpenStatus) {
|
226
|
-
otherClient.socket.send(disconnectMessage);
|
227
|
-
}
|
228
|
-
}
|
555
|
+
this.userNetworkingServer.onLegacyClientDisconnect(client.id);
|
556
|
+
this.legacyAuthenticatedClientsById.delete(client.id);
|
557
|
+
this.deltaNetServer.clearInternalConnectionId(client.id);
|
229
558
|
}
|
230
559
|
}
|
231
560
|
async handleUserAuth(client, credentials) {
|
232
|
-
const userData = this.
|
561
|
+
const userData = this.userNetworkingServer.onLegacyClientConnect(
|
233
562
|
client.id,
|
234
563
|
credentials.sessionToken,
|
235
564
|
credentials.userIdentity
|
@@ -240,42 +569,37 @@ var UserNetworkingServer = class {
|
|
240
569
|
} else {
|
241
570
|
resolvedUserData = userData;
|
242
571
|
}
|
572
|
+
if (resolvedUserData instanceof Error) {
|
573
|
+
this.logger.error(`Client-id ${client.id} user_auth failed`, resolvedUserData);
|
574
|
+
return false;
|
575
|
+
} else if (resolvedUserData === true) {
|
576
|
+
this.logger.error(`Client-id ${client.id} user_auth failed`, resolvedUserData);
|
577
|
+
resolvedUserData = credentials.userIdentity;
|
578
|
+
} else {
|
579
|
+
resolvedUserData = resolvedUserData;
|
580
|
+
}
|
243
581
|
if (resolvedUserData === null) {
|
244
|
-
|
582
|
+
this.logger.error(`Client-id ${client.id} user_auth unauthorized and ignored`);
|
245
583
|
return false;
|
246
584
|
}
|
247
|
-
console.log("Client authenticated", client.id, resolvedUserData);
|
248
585
|
return resolvedUserData;
|
249
586
|
}
|
250
587
|
updateUserCharacter(clientId, userData) {
|
251
588
|
this.internalUpdateUser(clientId, userData);
|
252
589
|
}
|
253
590
|
internalUpdateUser(clientId, userData) {
|
254
|
-
const client = this.
|
591
|
+
const client = this.legacyAuthenticatedClientsById.get(clientId);
|
255
592
|
client.authenticatedUser = userData;
|
256
|
-
this.
|
257
|
-
|
258
|
-
id: clientId,
|
259
|
-
type: USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE,
|
260
|
-
username: userData.username,
|
261
|
-
characterDescription: userData.characterDescription
|
262
|
-
});
|
263
|
-
for (const [otherClientId, otherClient] of this.authenticatedClientsById) {
|
264
|
-
if (otherClient.socket.readyState === WebSocketOpenStatus) {
|
265
|
-
otherClient.socket.send(newUserData);
|
266
|
-
}
|
267
|
-
}
|
593
|
+
this.legacyAuthenticatedClientsById.set(clientId, client);
|
594
|
+
this.userNetworkingServer.updateUserCharacter(client.id, { ...userData, colors: [] });
|
268
595
|
}
|
269
596
|
async handleUserUpdate(clientId, message) {
|
270
|
-
const client = this.
|
597
|
+
const client = this.legacyAuthenticatedClientsById.get(clientId);
|
271
598
|
if (!client) {
|
272
|
-
|
599
|
+
this.logger.error(`Client-id ${clientId} user_update ignored, client not found`);
|
273
600
|
return;
|
274
601
|
}
|
275
|
-
const authorizedUserData =
|
276
|
-
clientId,
|
277
|
-
message.userIdentity
|
278
|
-
);
|
602
|
+
const authorizedUserData = message.userIdentity;
|
279
603
|
let resolvedAuthorizedUserData;
|
280
604
|
if (authorizedUserData instanceof Promise) {
|
281
605
|
resolvedAuthorizedUserData = await authorizedUserData;
|
@@ -283,28 +607,73 @@ var UserNetworkingServer = class {
|
|
283
607
|
resolvedAuthorizedUserData = authorizedUserData;
|
284
608
|
}
|
285
609
|
if (!resolvedAuthorizedUserData) {
|
286
|
-
|
610
|
+
this.logger.warn(`Client-id ${clientId} user_update unauthorized and ignored`);
|
287
611
|
return;
|
288
612
|
}
|
289
613
|
this.internalUpdateUser(clientId, resolvedAuthorizedUserData);
|
290
614
|
}
|
291
|
-
sendUpdates() {
|
292
|
-
for (const
|
293
|
-
const
|
294
|
-
|
295
|
-
|
296
|
-
|
615
|
+
sendUpdates(removedIds, addedIds, updateUserProfilesInTick) {
|
616
|
+
for (const id of removedIds) {
|
617
|
+
const disconnectMessage = JSON.stringify({
|
618
|
+
id,
|
619
|
+
type: LEGACY_USER_NETWORKING_DISCONNECTED_MESSAGE_TYPE
|
620
|
+
});
|
621
|
+
for (const [, otherClient] of this.legacyAuthenticatedClientsById) {
|
622
|
+
if (otherClient.socket.readyState === WebSocketOpenStatus) {
|
623
|
+
otherClient.socket.send(disconnectMessage);
|
624
|
+
}
|
625
|
+
}
|
626
|
+
}
|
627
|
+
for (const id of addedIds) {
|
628
|
+
const identityMessage = JSON.stringify({
|
629
|
+
id,
|
630
|
+
type: LEGACY_USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE,
|
631
|
+
username: this.userNetworkingServer.getUsername(id),
|
632
|
+
characterDescription: this.userNetworkingServer.getCharacterDescription(id)
|
633
|
+
});
|
634
|
+
for (const [, otherClient] of this.legacyAuthenticatedClientsById) {
|
635
|
+
if (otherClient.socket.readyState === WebSocketOpenStatus) {
|
636
|
+
otherClient.socket.send(identityMessage);
|
637
|
+
}
|
638
|
+
}
|
639
|
+
}
|
640
|
+
for (const id of updateUserProfilesInTick) {
|
641
|
+
const identityMessage = JSON.stringify({
|
642
|
+
id,
|
643
|
+
type: LEGACY_USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE,
|
644
|
+
username: this.userNetworkingServer.getUsername(id),
|
645
|
+
characterDescription: this.userNetworkingServer.getCharacterDescription(id)
|
646
|
+
});
|
647
|
+
for (const [, otherClient] of this.legacyAuthenticatedClientsById) {
|
648
|
+
if (otherClient.socket.readyState === WebSocketOpenStatus) {
|
649
|
+
otherClient.socket.send(identityMessage);
|
650
|
+
}
|
651
|
+
}
|
652
|
+
}
|
653
|
+
const allUsers = this.deltaNetServer.dangerouslyGetConnectionsToComponentIndex();
|
654
|
+
for (const [connectionId, componentIndex] of allUsers) {
|
655
|
+
const x = this.deltaNetServer.getComponentValue(COMPONENT_POSITION_X, componentIndex) / positionMultiplier;
|
656
|
+
const y = this.deltaNetServer.getComponentValue(COMPONENT_POSITION_Y, componentIndex) / positionMultiplier;
|
657
|
+
const z = this.deltaNetServer.getComponentValue(COMPONENT_POSITION_Z, componentIndex) / positionMultiplier;
|
658
|
+
const quaternionY = this.deltaNetServer.getComponentValue(COMPONENT_ROTATION_Y, componentIndex) / rotationMultiplier;
|
659
|
+
const quaternionW = this.deltaNetServer.getComponentValue(COMPONENT_ROTATION_W, componentIndex) / rotationMultiplier;
|
660
|
+
const state = this.deltaNetServer.getComponentValue(COMPONENT_STATE, componentIndex);
|
661
|
+
const encodedUpdate = LegacyUserNetworkingCodec.encodeUpdate({
|
662
|
+
id: connectionId,
|
663
|
+
position: { x, y, z },
|
664
|
+
rotation: { quaternionY, quaternionW },
|
665
|
+
state
|
666
|
+
});
|
667
|
+
for (const [otherClientId, otherClient] of this.legacyAuthenticatedClientsById) {
|
668
|
+
if (otherClientId !== connectionId && otherClient.socket.readyState === WebSocketOpenStatus) {
|
297
669
|
otherClient.socket.send(encodedUpdate);
|
298
670
|
}
|
299
671
|
}
|
300
672
|
}
|
301
673
|
}
|
302
674
|
dispose(clientCloseError) {
|
303
|
-
clearInterval(this.sendUpdatesIntervalTimer);
|
304
|
-
clearInterval(this.pingClientsIntervalTimer);
|
305
|
-
clearInterval(this.heartbeatIntervalTimer);
|
306
675
|
const stringifiedError = clientCloseError ? JSON.stringify(clientCloseError) : void 0;
|
307
|
-
for (const [, client] of this.
|
676
|
+
for (const [, client] of this.legacyAuthenticatedClientsById) {
|
308
677
|
if (stringifiedError) {
|
309
678
|
client.socket.send(stringifiedError);
|
310
679
|
}
|
@@ -313,239 +682,817 @@ var UserNetworkingServer = class {
|
|
313
682
|
}
|
314
683
|
};
|
315
684
|
|
316
|
-
// src/
|
317
|
-
var
|
318
|
-
|
319
|
-
|
320
|
-
WebsocketStatus2[WebsocketStatus2["Reconnecting"] = 2] = "Reconnecting";
|
321
|
-
WebsocketStatus2[WebsocketStatus2["Disconnected"] = 3] = "Disconnected";
|
322
|
-
return WebsocketStatus2;
|
323
|
-
})(WebsocketStatus || {});
|
324
|
-
var startingBackoffTimeMilliseconds = 100;
|
325
|
-
var maximumBackoffTimeMilliseconds = 1e4;
|
326
|
-
var maximumWebsocketConnectionTimeout = 5e3;
|
327
|
-
var ReconnectingWebSocket = class {
|
328
|
-
constructor(url, websocketFactory, statusUpdateCallback) {
|
329
|
-
this.url = url;
|
330
|
-
this.websocketFactory = websocketFactory;
|
331
|
-
this.statusUpdateCallback = statusUpdateCallback;
|
332
|
-
this.websocket = null;
|
333
|
-
this.status = null;
|
334
|
-
this.receivedMessageSinceOpen = false;
|
335
|
-
this.backoffTime = startingBackoffTimeMilliseconds;
|
336
|
-
this.stopped = false;
|
337
|
-
this.setStatus(0 /* Connecting */);
|
338
|
-
this.startWebSocketConnectionAttempt();
|
339
|
-
}
|
340
|
-
setStatus(status) {
|
341
|
-
if (this.status !== status) {
|
342
|
-
this.status = status;
|
343
|
-
this.statusUpdateCallback(status);
|
344
|
-
}
|
685
|
+
// src/UserNetworkingLogger.ts
|
686
|
+
var UserNetworkingConsoleLogger = class {
|
687
|
+
trace(...args) {
|
688
|
+
console.trace(...args);
|
345
689
|
}
|
346
|
-
|
347
|
-
|
348
|
-
console.error("Not connected to the server");
|
349
|
-
return;
|
350
|
-
}
|
351
|
-
const encodedUpdate = UserNetworkingCodec.encodeUpdate(update);
|
352
|
-
this.send(encodedUpdate);
|
690
|
+
debug(...args) {
|
691
|
+
console.debug(...args);
|
353
692
|
}
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
693
|
+
info(...args) {
|
694
|
+
console.info(...args);
|
695
|
+
}
|
696
|
+
warn(...args) {
|
697
|
+
console.warn(...args);
|
698
|
+
}
|
699
|
+
error(...args) {
|
700
|
+
console.error(...args);
|
701
|
+
}
|
702
|
+
};
|
703
|
+
|
704
|
+
// src/UserNetworkingServer.ts
|
705
|
+
var UserNetworkingServer = class {
|
706
|
+
constructor(options, logger = new UserNetworkingConsoleLogger()) {
|
707
|
+
this.options = options;
|
708
|
+
this.logger = logger;
|
709
|
+
this.deltaNetServer = new DeltaNetServer({
|
710
|
+
serverConnectionIdStateId: 0,
|
711
|
+
onJoiner: (joiner) => {
|
712
|
+
return this.handleJoiner(joiner);
|
713
|
+
},
|
714
|
+
onLeave: (leave) => {
|
715
|
+
this.handleLeave(leave);
|
716
|
+
},
|
717
|
+
onComponentsUpdate: (update) => {
|
360
718
|
return;
|
719
|
+
},
|
720
|
+
onStatesUpdate: (update) => {
|
721
|
+
return this.handleStatesUpdate(update);
|
722
|
+
},
|
723
|
+
onCustomMessage: (customMessage) => {
|
724
|
+
this.handleCustomMessage(customMessage);
|
361
725
|
}
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
} catch (e) {
|
366
|
-
this.setStatus(2 /* Reconnecting */);
|
367
|
-
await this.waitBackoffTime();
|
368
|
-
}
|
726
|
+
});
|
727
|
+
if (this.options.legacyAdapterEnabled) {
|
728
|
+
this.legacyAdapter = new LegacyAdapter(this, this.deltaNetServer, this.logger);
|
369
729
|
}
|
730
|
+
this.tickInterval = setInterval(() => {
|
731
|
+
const { removedIds, addedIds } = this.deltaNetServer.tick();
|
732
|
+
if (this.legacyAdapter) {
|
733
|
+
this.legacyAdapter.sendUpdates(removedIds, addedIds, this.updatedUserProfilesInTick);
|
734
|
+
this.updatedUserProfilesInTick.clear();
|
735
|
+
}
|
736
|
+
}, 50);
|
370
737
|
}
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
);
|
738
|
+
deltaNetServer;
|
739
|
+
authenticatedClientsById = /* @__PURE__ */ new Map();
|
740
|
+
tickInterval;
|
741
|
+
legacyAdapter = null;
|
742
|
+
updatedUserProfilesInTick = /* @__PURE__ */ new Set();
|
743
|
+
getCharacterDescription(connectionId) {
|
744
|
+
var _a;
|
745
|
+
const client = this.authenticatedClientsById.get(connectionId);
|
746
|
+
return ((_a = client == null ? void 0 : client.authenticatedUser) == null ? void 0 : _a.characterDescription) ?? { mmlCharacterUrl: "" };
|
379
747
|
}
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
748
|
+
getUsername(connectionId) {
|
749
|
+
var _a, _b;
|
750
|
+
const client = this.authenticatedClientsById.get(connectionId);
|
751
|
+
this.logger.info("getUsername", connectionId, (_a = client == null ? void 0 : client.authenticatedUser) == null ? void 0 : _a.username);
|
752
|
+
return ((_b = client == null ? void 0 : client.authenticatedUser) == null ? void 0 : _b.username) ?? "";
|
753
|
+
}
|
754
|
+
getLegacyClientId() {
|
755
|
+
return this.deltaNetServer.getNextConnectionId();
|
756
|
+
}
|
757
|
+
hasCapacityForLegacyClient() {
|
758
|
+
return true;
|
759
|
+
}
|
760
|
+
onLegacyClientConnect(id, sessionToken, userIdentity) {
|
761
|
+
return this.options.onClientConnect(id, sessionToken, {
|
762
|
+
username: (userIdentity == null ? void 0 : userIdentity.username) ?? null,
|
763
|
+
characterDescription: (userIdentity == null ? void 0 : userIdentity.characterDescription) ?? null,
|
764
|
+
colors: null
|
765
|
+
});
|
766
|
+
}
|
767
|
+
setAuthenticatedLegacyClientConnection(clientId, webSocket, userData) {
|
768
|
+
this.logger.info("setAuthenticatedLegacyClientConnection", clientId, userData);
|
769
|
+
const authenticatedClient = {
|
770
|
+
id: clientId,
|
771
|
+
socket: webSocket,
|
772
|
+
lastPong: Date.now(),
|
773
|
+
authenticatedUser: userData,
|
774
|
+
deltaNetConnection: null
|
775
|
+
};
|
776
|
+
this.authenticatedClientsById.set(clientId, authenticatedClient);
|
777
|
+
}
|
778
|
+
onLegacyClientDisconnect(id) {
|
779
|
+
this.options.onClientDisconnect(id);
|
780
|
+
}
|
781
|
+
handleStatesUpdate(update) {
|
782
|
+
const deltaNetConnection = update.deltaNetV01Connection;
|
783
|
+
const clientId = deltaNetConnection.internalConnectionId;
|
784
|
+
const updatedStates = update.states;
|
785
|
+
const updatedStatesMap = new Map(updatedStates);
|
786
|
+
const updatedUserData = DeltaNetComponentMapping.fromUserStates(
|
787
|
+
updatedStatesMap,
|
788
|
+
this.logger
|
789
|
+
);
|
790
|
+
const existingClient = this.authenticatedClientsById.get(clientId);
|
791
|
+
if (!existingClient) {
|
792
|
+
return new DeltaNetServerError2(
|
793
|
+
DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
|
794
|
+
"User not authenticated - no client found",
|
795
|
+
false
|
796
|
+
);
|
384
797
|
}
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
}
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
798
|
+
const existingUserData = existingClient.authenticatedUser ?? {};
|
799
|
+
const userData = {
|
800
|
+
...existingUserData,
|
801
|
+
...updatedUserData
|
802
|
+
};
|
803
|
+
const res = this.options.onClientUserIdentityUpdate(clientId, userData);
|
804
|
+
if (res instanceof Promise) {
|
805
|
+
return res.then((res2) => {
|
806
|
+
if (!this.authenticatedClientsById.get(clientId)) {
|
807
|
+
return new DeltaNetServerError2(
|
808
|
+
DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
|
809
|
+
"User not authenticated - client disconnected",
|
810
|
+
false
|
811
|
+
);
|
812
|
+
}
|
813
|
+
if (res2 instanceof DeltaNetServerError2) {
|
814
|
+
return res2;
|
815
|
+
}
|
816
|
+
if (res2 instanceof Error) {
|
817
|
+
return new DeltaNetServerError2(
|
818
|
+
DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
|
819
|
+
"User identity update failed",
|
820
|
+
false
|
821
|
+
);
|
822
|
+
}
|
823
|
+
if (res2 === null) {
|
824
|
+
return new DeltaNetServerError2(
|
825
|
+
DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
|
826
|
+
"User identity update failed",
|
827
|
+
false
|
828
|
+
);
|
829
|
+
}
|
830
|
+
if (res2 === false) {
|
831
|
+
return new DeltaNetServerError2(
|
832
|
+
DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
|
833
|
+
"User identity update failed",
|
834
|
+
false
|
835
|
+
);
|
836
|
+
}
|
837
|
+
if (!res2 || typeof res2 !== "object") {
|
838
|
+
return new DeltaNetServerError2(
|
839
|
+
DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
|
840
|
+
"User identity update failed",
|
841
|
+
false
|
842
|
+
);
|
843
|
+
}
|
844
|
+
this.updatedUserProfilesInTick.add(clientId);
|
845
|
+
existingClient.authenticatedUser = {
|
846
|
+
...existingClient.authenticatedUser,
|
847
|
+
...res2
|
848
|
+
};
|
849
|
+
return {
|
850
|
+
success: true,
|
851
|
+
stateOverrides: Array.from(DeltaNetComponentMapping.toStates(res2).entries())
|
430
852
|
};
|
431
|
-
websocket.addEventListener("close", (e) => {
|
432
|
-
if (websocket !== this.websocket) {
|
433
|
-
console.warn("Ignoring websocket close event because it is no longer current");
|
434
|
-
return;
|
435
|
-
}
|
436
|
-
console.log("ReconnectingWebSocket close", e);
|
437
|
-
onWebsocketClose();
|
438
|
-
});
|
439
|
-
websocket.addEventListener("error", (e) => {
|
440
|
-
if (websocket !== this.websocket) {
|
441
|
-
console.log("Ignoring websocket error event because it is no longer current");
|
442
|
-
return;
|
443
|
-
}
|
444
|
-
console.error("ReconnectingWebSocket error", e);
|
445
|
-
onWebsocketClose();
|
446
|
-
});
|
447
|
-
resolve(websocket);
|
448
|
-
});
|
449
|
-
websocket.addEventListener("error", (e) => {
|
450
|
-
clearTimeout(timeoutId);
|
451
|
-
reject(e);
|
452
853
|
});
|
854
|
+
}
|
855
|
+
if (res instanceof DeltaNetServerError2) {
|
856
|
+
return res;
|
857
|
+
}
|
858
|
+
if (res instanceof Error) {
|
859
|
+
return new DeltaNetServerError2(
|
860
|
+
DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
|
861
|
+
"User identity update failed",
|
862
|
+
false
|
863
|
+
);
|
864
|
+
}
|
865
|
+
if (res === null) {
|
866
|
+
return new DeltaNetServerError2(
|
867
|
+
DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
|
868
|
+
"User identity update failed",
|
869
|
+
false
|
870
|
+
);
|
871
|
+
}
|
872
|
+
if (res === false) {
|
873
|
+
return new DeltaNetServerError2(
|
874
|
+
DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
|
875
|
+
"User identity update failed",
|
876
|
+
false
|
877
|
+
);
|
878
|
+
}
|
879
|
+
if (!res || typeof res !== "object") {
|
880
|
+
return new DeltaNetServerError2(
|
881
|
+
DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
|
882
|
+
"User identity update failed",
|
883
|
+
false
|
884
|
+
);
|
885
|
+
}
|
886
|
+
this.updatedUserProfilesInTick.add(clientId);
|
887
|
+
existingClient.authenticatedUser = {
|
888
|
+
...existingClient.authenticatedUser,
|
889
|
+
...res
|
890
|
+
};
|
891
|
+
return {
|
892
|
+
success: true,
|
893
|
+
stateOverrides: Array.from(DeltaNetComponentMapping.toStates(res).entries())
|
894
|
+
};
|
895
|
+
}
|
896
|
+
handleJoiner(joiner) {
|
897
|
+
const deltaNetConnection = joiner.deltaNetV01Connection;
|
898
|
+
const webSocket = deltaNetConnection.webSocket;
|
899
|
+
const states = joiner.states;
|
900
|
+
const clientId = joiner.internalConnectionId;
|
901
|
+
const statesMap = new Map(states);
|
902
|
+
const userData = DeltaNetComponentMapping.fromUserStates(statesMap, this.logger);
|
903
|
+
return this.handleDeltaNetAuthentication(
|
904
|
+
clientId,
|
905
|
+
webSocket,
|
906
|
+
deltaNetConnection,
|
907
|
+
joiner.token,
|
908
|
+
userData
|
909
|
+
).then((authResult) => {
|
910
|
+
var _a;
|
911
|
+
if (!authResult.success) {
|
912
|
+
this.logger.warn(`Authentication failed for client ID: ${clientId}`, authResult.error);
|
913
|
+
return new DeltaNetServerError2(
|
914
|
+
DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
|
915
|
+
((_a = authResult.error) == null ? void 0 : _a.message) || "Authentication failed",
|
916
|
+
false
|
917
|
+
);
|
918
|
+
} else {
|
919
|
+
return {
|
920
|
+
success: true,
|
921
|
+
stateOverrides: authResult.stateOverrides
|
922
|
+
};
|
923
|
+
}
|
924
|
+
}).catch((error) => {
|
925
|
+
this.logger.error(`Authentication error for client ID: ${clientId}:`, error);
|
926
|
+
return new DeltaNetServerError2(
|
927
|
+
DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
|
928
|
+
"Authentication error",
|
929
|
+
false
|
930
|
+
);
|
453
931
|
});
|
454
932
|
}
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
933
|
+
handleLeave(leave) {
|
934
|
+
const deltaNetConnection = leave.deltaNetV01Connection;
|
935
|
+
const clientId = deltaNetConnection.internalConnectionId;
|
936
|
+
if (clientId !== void 0) {
|
937
|
+
const client = this.authenticatedClientsById.get(clientId);
|
938
|
+
if (client) {
|
939
|
+
this.options.onClientDisconnect(clientId);
|
940
|
+
this.authenticatedClientsById.delete(clientId);
|
941
|
+
}
|
942
|
+
}
|
943
|
+
}
|
944
|
+
handleCustomMessage(customMessage) {
|
945
|
+
const deltaNetConnection = customMessage.deltaNetV01Connection;
|
946
|
+
const clientId = deltaNetConnection.internalConnectionId;
|
947
|
+
const client = this.authenticatedClientsById.get(clientId);
|
948
|
+
if (client && client.authenticatedUser) {
|
949
|
+
if (customMessage.customType === FROM_CLIENT_CHAT_MESSAGE_TYPE) {
|
950
|
+
const chatMessage = parseClientChatMessage(customMessage.contents);
|
951
|
+
if (chatMessage instanceof Error) {
|
952
|
+
this.logger.error(`Invalid chat message from client ${clientId}:`, chatMessage);
|
953
|
+
} else {
|
954
|
+
const serverChatMessage = {
|
955
|
+
fromUserId: clientId,
|
956
|
+
message: chatMessage.message
|
957
|
+
};
|
958
|
+
this.deltaNetServer.broadcastCustomMessage(
|
959
|
+
FROM_SERVER_CHAT_MESSAGE_TYPE,
|
960
|
+
JSON.stringify(serverChatMessage)
|
961
|
+
);
|
962
|
+
}
|
963
|
+
}
|
964
|
+
} else {
|
965
|
+
this.logger.warn(`Custom message from unauthenticated client ${clientId} - ignoring`);
|
966
|
+
}
|
967
|
+
}
|
968
|
+
async handleDeltaNetAuthentication(clientId, webSocket, deltaNetConnection, sessionToken, userIdentity) {
|
969
|
+
try {
|
970
|
+
let onClientConnectReturn = deltaNetConnection.isObserver ? null : await this.options.onClientConnect(clientId, sessionToken, userIdentity);
|
971
|
+
if (!deltaNetConnection.isObserver && !onClientConnectReturn) {
|
972
|
+
this.logger.warn(`Authentication failed for client ${clientId} - no user data returned`);
|
973
|
+
return { success: false };
|
974
|
+
}
|
975
|
+
if (onClientConnectReturn instanceof Error) {
|
976
|
+
return { success: false, error: onClientConnectReturn };
|
977
|
+
}
|
978
|
+
if (onClientConnectReturn === true) {
|
979
|
+
onClientConnectReturn = userIdentity;
|
980
|
+
}
|
981
|
+
const authenticatedUser = onClientConnectReturn;
|
982
|
+
const authenticatedClient = {
|
983
|
+
id: clientId,
|
984
|
+
socket: webSocket,
|
985
|
+
lastPong: Date.now(),
|
986
|
+
authenticatedUser,
|
987
|
+
deltaNetConnection
|
988
|
+
};
|
989
|
+
this.authenticatedClientsById.set(clientId, authenticatedClient);
|
990
|
+
let stateOverrides = [];
|
991
|
+
if (onClientConnectReturn) {
|
992
|
+
const officialStates = DeltaNetComponentMapping.toStates(onClientConnectReturn);
|
993
|
+
stateOverrides = Array.from(officialStates.entries());
|
994
|
+
}
|
995
|
+
return {
|
996
|
+
success: true,
|
997
|
+
stateOverrides
|
998
|
+
};
|
999
|
+
} catch (error) {
|
1000
|
+
this.logger.error("Authentication error:", error);
|
1001
|
+
return { success: false };
|
1002
|
+
}
|
1003
|
+
}
|
1004
|
+
connectClient(socket) {
|
1005
|
+
if (socket.protocol === "") {
|
1006
|
+
if (this.legacyAdapter) {
|
1007
|
+
this.legacyAdapter.addWebSocket(socket);
|
1008
|
+
return;
|
1009
|
+
} else {
|
1010
|
+
socket.close(1e3, "Legacy client detected (no subprotocol) - not supported");
|
1011
|
+
return;
|
1012
|
+
}
|
1013
|
+
}
|
1014
|
+
this.deltaNetServer.addWebSocket(socket);
|
1015
|
+
socket.addEventListener("close", () => {
|
1016
|
+
this.deltaNetServer.removeWebSocket(socket);
|
1017
|
+
});
|
1018
|
+
}
|
1019
|
+
broadcastMessage(broadcastType, broadcastPayload) {
|
1020
|
+
this.deltaNetServer.broadcastCustomMessage(broadcastType, broadcastPayload);
|
1021
|
+
if (this.legacyAdapter) {
|
1022
|
+
this.legacyAdapter.broadcastMessage(broadcastType, broadcastPayload);
|
460
1023
|
}
|
461
1024
|
}
|
1025
|
+
updateUserCharacter(clientId, userData) {
|
1026
|
+
this.logger.info("updateUserCharacter", clientId, userData);
|
1027
|
+
this.internalUpdateUser(clientId, userData);
|
1028
|
+
}
|
1029
|
+
updateUserUsername(clientId, username) {
|
1030
|
+
const client = this.authenticatedClientsById.get(clientId);
|
1031
|
+
if (!client || !client.authenticatedUser) return;
|
1032
|
+
client.authenticatedUser = {
|
1033
|
+
...client.authenticatedUser,
|
1034
|
+
username
|
1035
|
+
};
|
1036
|
+
this.updatedUserProfilesInTick.add(clientId);
|
1037
|
+
const states = DeltaNetComponentMapping.toUsernameState(username);
|
1038
|
+
const asArray = Array.from(states.entries());
|
1039
|
+
this.deltaNetServer.overrideUserStates(client.deltaNetConnection, clientId, asArray);
|
1040
|
+
}
|
1041
|
+
updateUserCharacterDescription(clientId, characterDescription) {
|
1042
|
+
const client = this.authenticatedClientsById.get(clientId);
|
1043
|
+
if (!client || !client.authenticatedUser) return;
|
1044
|
+
client.authenticatedUser = {
|
1045
|
+
...client.authenticatedUser,
|
1046
|
+
characterDescription
|
1047
|
+
};
|
1048
|
+
this.updatedUserProfilesInTick.add(clientId);
|
1049
|
+
const states = DeltaNetComponentMapping.toCharacterDescriptionState(characterDescription);
|
1050
|
+
const asArray = Array.from(states.entries());
|
1051
|
+
this.deltaNetServer.overrideUserStates(client.deltaNetConnection, clientId, asArray);
|
1052
|
+
}
|
1053
|
+
updateUserColors(clientId, colors) {
|
1054
|
+
const client = this.authenticatedClientsById.get(clientId);
|
1055
|
+
if (!client || !client.authenticatedUser) return;
|
1056
|
+
client.authenticatedUser = {
|
1057
|
+
...client.authenticatedUser,
|
1058
|
+
colors
|
1059
|
+
};
|
1060
|
+
this.updatedUserProfilesInTick.add(clientId);
|
1061
|
+
const states = DeltaNetComponentMapping.toColorsState(colors);
|
1062
|
+
const asArray = Array.from(states.entries());
|
1063
|
+
this.deltaNetServer.overrideUserStates(client.deltaNetConnection, clientId, asArray);
|
1064
|
+
}
|
1065
|
+
updateUserStates(clientId, updates) {
|
1066
|
+
const client = this.authenticatedClientsById.get(clientId);
|
1067
|
+
if (!client || !client.authenticatedUser) return;
|
1068
|
+
const states = /* @__PURE__ */ new Map();
|
1069
|
+
let hasUpdates = false;
|
1070
|
+
let updatedUserData = client.authenticatedUser;
|
1071
|
+
this.updatedUserProfilesInTick.add(clientId);
|
1072
|
+
if (updates.username !== null) {
|
1073
|
+
updatedUserData = {
|
1074
|
+
...updatedUserData,
|
1075
|
+
username: updates.username
|
1076
|
+
};
|
1077
|
+
const usernameStates = DeltaNetComponentMapping.toUsernameState(updates.username);
|
1078
|
+
for (const [stateId, stateValue] of usernameStates) {
|
1079
|
+
states.set(stateId, stateValue);
|
1080
|
+
}
|
1081
|
+
hasUpdates = true;
|
1082
|
+
}
|
1083
|
+
if (updates.characterDescription !== null) {
|
1084
|
+
updatedUserData = {
|
1085
|
+
...updatedUserData,
|
1086
|
+
characterDescription: updates.characterDescription
|
1087
|
+
};
|
1088
|
+
const characterDescStates = DeltaNetComponentMapping.toCharacterDescriptionState(
|
1089
|
+
updates.characterDescription
|
1090
|
+
);
|
1091
|
+
for (const [stateId, stateValue] of characterDescStates) {
|
1092
|
+
states.set(stateId, stateValue);
|
1093
|
+
}
|
1094
|
+
hasUpdates = true;
|
1095
|
+
}
|
1096
|
+
if (updates.colors !== null) {
|
1097
|
+
updatedUserData = {
|
1098
|
+
...updatedUserData,
|
1099
|
+
colors: updates.colors
|
1100
|
+
};
|
1101
|
+
const colorsStates = DeltaNetComponentMapping.toColorsState(updates.colors);
|
1102
|
+
for (const [stateId, stateValue] of colorsStates) {
|
1103
|
+
states.set(stateId, stateValue);
|
1104
|
+
}
|
1105
|
+
hasUpdates = true;
|
1106
|
+
}
|
1107
|
+
if (hasUpdates) {
|
1108
|
+
client.authenticatedUser = updatedUserData;
|
1109
|
+
const asArray = Array.from(states.entries());
|
1110
|
+
this.deltaNetServer.overrideUserStates(client.deltaNetConnection, clientId, asArray);
|
1111
|
+
}
|
1112
|
+
}
|
1113
|
+
internalUpdateUser(clientId, userData) {
|
1114
|
+
const client = this.authenticatedClientsById.get(clientId);
|
1115
|
+
if (!client) {
|
1116
|
+
throw new Error(`internalUpdateUser - client not found for clientId ${clientId}`);
|
1117
|
+
}
|
1118
|
+
this.logger.info("internalUpdateUser", clientId, userData);
|
1119
|
+
this.updatedUserProfilesInTick.add(clientId);
|
1120
|
+
client.authenticatedUser = {
|
1121
|
+
...client.authenticatedUser,
|
1122
|
+
...userData
|
1123
|
+
};
|
1124
|
+
const states = DeltaNetComponentMapping.toStates(userData);
|
1125
|
+
const asArray = Array.from(states.entries());
|
1126
|
+
this.deltaNetServer.overrideUserStates(client.deltaNetConnection, clientId, asArray);
|
1127
|
+
}
|
1128
|
+
dispose(clientCloseError) {
|
1129
|
+
if (this.tickInterval) {
|
1130
|
+
clearInterval(this.tickInterval);
|
1131
|
+
}
|
1132
|
+
let errorMessage = null;
|
1133
|
+
if (clientCloseError) {
|
1134
|
+
errorMessage = encodeError({
|
1135
|
+
type: "error",
|
1136
|
+
errorType: clientCloseError.errorType,
|
1137
|
+
message: clientCloseError.message,
|
1138
|
+
retryable: clientCloseError.retryable
|
1139
|
+
}).getBuffer();
|
1140
|
+
}
|
1141
|
+
for (const [, client] of this.authenticatedClientsById) {
|
1142
|
+
if (errorMessage) {
|
1143
|
+
client.socket.send(errorMessage);
|
1144
|
+
}
|
1145
|
+
client.socket.close();
|
1146
|
+
}
|
1147
|
+
this.authenticatedClientsById.clear();
|
1148
|
+
}
|
462
1149
|
};
|
463
1150
|
|
464
1151
|
// src/UserNetworkingClient.ts
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
1152
|
+
import {
|
1153
|
+
DeltaNetClientState,
|
1154
|
+
DeltaNetClientWebsocket,
|
1155
|
+
DeltaNetClientWebsocketStatus
|
1156
|
+
} from "@mml-io/delta-net-web";
|
1157
|
+
|
1158
|
+
// src/types.ts
|
1159
|
+
var WebsocketStatus = /* @__PURE__ */ ((WebsocketStatus2) => {
|
1160
|
+
WebsocketStatus2[WebsocketStatus2["Connecting"] = 0] = "Connecting";
|
1161
|
+
WebsocketStatus2[WebsocketStatus2["Connected"] = 1] = "Connected";
|
1162
|
+
WebsocketStatus2[WebsocketStatus2["Reconnecting"] = 2] = "Reconnecting";
|
1163
|
+
WebsocketStatus2[WebsocketStatus2["Disconnected"] = 3] = "Disconnected";
|
1164
|
+
return WebsocketStatus2;
|
1165
|
+
})(WebsocketStatus || {});
|
1166
|
+
|
1167
|
+
// src/UserNetworkingClient.ts
|
1168
|
+
var UserNetworkingClient = class {
|
1169
|
+
constructor(config, initialUserState, initialUpdate, logger = new UserNetworkingConsoleLogger()) {
|
476
1170
|
this.config = config;
|
1171
|
+
this.logger = logger;
|
1172
|
+
this.pendingUpdate = initialUpdate ?? {
|
1173
|
+
position: { x: 0, y: 0, z: 0 },
|
1174
|
+
rotation: { quaternionY: 0, quaternionW: 1 },
|
1175
|
+
state: 0
|
1176
|
+
};
|
1177
|
+
this.userState = initialUserState ?? {
|
1178
|
+
username: null,
|
1179
|
+
characterDescription: null,
|
1180
|
+
colors: null
|
1181
|
+
};
|
1182
|
+
this.deltaNetState = new DeltaNetClientState();
|
1183
|
+
this.deltaNetClient = new DeltaNetClientWebsocket(
|
1184
|
+
config.url,
|
1185
|
+
(url) => {
|
1186
|
+
const ws = config.websocketFactory(url);
|
1187
|
+
return ws;
|
1188
|
+
},
|
1189
|
+
config.sessionToken,
|
1190
|
+
{
|
1191
|
+
ignoreData: false,
|
1192
|
+
onInitialCheckout: (initialCheckout) => {
|
1193
|
+
const { addedStableIds } = this.deltaNetState.handleInitialCheckout(initialCheckout);
|
1194
|
+
const networkUpdate = this.processNetworkUpdate([], addedStableIds, []);
|
1195
|
+
this.config.onUpdate(networkUpdate);
|
1196
|
+
if (this.userIndex !== null) {
|
1197
|
+
const userIds = this.deltaNetState.getStableIds();
|
1198
|
+
if (this.userIndex < userIds.length) {
|
1199
|
+
const stableId = userIds[this.userIndex];
|
1200
|
+
const userId = this.stableIdToUserId.get(stableId);
|
1201
|
+
if (!userId) {
|
1202
|
+
throw new Error(`No userId found for stableId ${stableId}`);
|
1203
|
+
}
|
1204
|
+
this.userId = userId;
|
1205
|
+
this.isAuthenticated = true;
|
1206
|
+
this.config.assignedIdentity(this.userId);
|
1207
|
+
} else {
|
1208
|
+
this.logger.error(
|
1209
|
+
`Invalid userIndex ${this.userIndex}, userIds length: ${userIds.length}`
|
1210
|
+
);
|
1211
|
+
}
|
1212
|
+
}
|
1213
|
+
},
|
1214
|
+
onTick: (tick) => {
|
1215
|
+
const { stateUpdates, removedStableIds, addedStableIds } = this.deltaNetState.handleTick(tick);
|
1216
|
+
const networkUpdate = this.processNetworkUpdate(
|
1217
|
+
removedStableIds,
|
1218
|
+
addedStableIds,
|
1219
|
+
stateUpdates
|
1220
|
+
);
|
1221
|
+
this.config.onUpdate(networkUpdate);
|
1222
|
+
},
|
1223
|
+
onUserIndex: (userIndex) => {
|
1224
|
+
this.userIndex = userIndex.userIndex;
|
1225
|
+
this.deltaNetState.setLocalIndex(userIndex.userIndex);
|
1226
|
+
},
|
1227
|
+
onError: (errorType, errorMessage, retryable) => {
|
1228
|
+
this.logger.error(
|
1229
|
+
"DeltaNet error:",
|
1230
|
+
errorType,
|
1231
|
+
"errorMessage:",
|
1232
|
+
errorMessage,
|
1233
|
+
"retryable:",
|
1234
|
+
retryable
|
1235
|
+
);
|
1236
|
+
this.config.onServerError({
|
1237
|
+
message: errorMessage,
|
1238
|
+
errorType
|
1239
|
+
});
|
1240
|
+
},
|
1241
|
+
onWarning: (warning) => {
|
1242
|
+
this.logger.warn("DeltaNet warning:", warning);
|
1243
|
+
},
|
1244
|
+
onServerCustom: (customType, contents) => {
|
1245
|
+
var _a, _b;
|
1246
|
+
(_b = (_a = this.config).onCustomMessage) == null ? void 0 : _b.call(_a, customType, contents);
|
1247
|
+
}
|
1248
|
+
},
|
1249
|
+
void 0,
|
1250
|
+
// timeCallback is optional
|
1251
|
+
(status) => {
|
1252
|
+
let mappedStatus;
|
1253
|
+
switch (status) {
|
1254
|
+
case DeltaNetClientWebsocketStatus.Connected:
|
1255
|
+
mappedStatus = 1 /* Connected */;
|
1256
|
+
break;
|
1257
|
+
case DeltaNetClientWebsocketStatus.ConnectionOpen:
|
1258
|
+
this.sendInitialAuthentication();
|
1259
|
+
mappedStatus = 1 /* Connected */;
|
1260
|
+
break;
|
1261
|
+
case DeltaNetClientWebsocketStatus.Disconnected:
|
1262
|
+
mappedStatus = 3 /* Disconnected */;
|
1263
|
+
this.reset();
|
1264
|
+
break;
|
1265
|
+
case DeltaNetClientWebsocketStatus.Reconnecting:
|
1266
|
+
mappedStatus = 2 /* Reconnecting */;
|
1267
|
+
this.reset();
|
1268
|
+
break;
|
1269
|
+
default:
|
1270
|
+
mappedStatus = 3 /* Disconnected */;
|
1271
|
+
}
|
1272
|
+
this.config.statusUpdateCallback(mappedStatus);
|
1273
|
+
}
|
1274
|
+
);
|
477
1275
|
}
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
1276
|
+
deltaNetClient;
|
1277
|
+
deltaNetState;
|
1278
|
+
userId = null;
|
1279
|
+
userIndex = null;
|
1280
|
+
userState = {
|
1281
|
+
username: null,
|
1282
|
+
characterDescription: null,
|
1283
|
+
colors: null
|
1284
|
+
};
|
1285
|
+
stableIdToUserId = /* @__PURE__ */ new Map();
|
1286
|
+
userProfiles = /* @__PURE__ */ new Map();
|
1287
|
+
isAuthenticated = false;
|
1288
|
+
pendingUpdate;
|
1289
|
+
reset() {
|
1290
|
+
this.deltaNetState.reset();
|
1291
|
+
this.userProfiles.clear();
|
1292
|
+
this.stableIdToUserId.clear();
|
1293
|
+
this.isAuthenticated = false;
|
1294
|
+
this.userId = null;
|
1295
|
+
this.userIndex = null;
|
1296
|
+
}
|
1297
|
+
sendInitialAuthentication() {
|
1298
|
+
const components = DeltaNetComponentMapping.toComponents(this.pendingUpdate);
|
1299
|
+
const states = DeltaNetComponentMapping.toStates(this.userState);
|
1300
|
+
this.deltaNetClient.setUserComponents(components, states);
|
1301
|
+
}
|
1302
|
+
processNetworkUpdate(removedStableIds, addedStableIdsArray, stateUpdates) {
|
1303
|
+
const addedUserIds = /* @__PURE__ */ new Map();
|
1304
|
+
const removedUserIds = /* @__PURE__ */ new Set();
|
1305
|
+
for (const stableId of removedStableIds) {
|
1306
|
+
const userId = this.stableIdToUserId.get(stableId);
|
1307
|
+
if (userId) {
|
1308
|
+
removedUserIds.add(userId);
|
1309
|
+
this.userProfiles.delete(userId);
|
1310
|
+
this.stableIdToUserId.delete(stableId);
|
1311
|
+
} else {
|
1312
|
+
throw new Error(`No userId found for stableId ${stableId}`);
|
1313
|
+
}
|
1314
|
+
}
|
1315
|
+
for (const stableId of addedStableIdsArray) {
|
1316
|
+
const stableUserData = this.deltaNetState.byStableId.get(stableId);
|
1317
|
+
if (!stableUserData) {
|
1318
|
+
throw new Error(`No stableUserData found for stableId ${stableId}`);
|
1319
|
+
}
|
1320
|
+
const userIdState = stableUserData.states.get(STATE_INTERNAL_CONNECTION_ID);
|
1321
|
+
if (!userIdState) {
|
1322
|
+
throw new Error(`No userIdState found for stableId ${stableId}`);
|
1323
|
+
}
|
1324
|
+
const userId = DeltaNetComponentMapping.userIdFromBytes(userIdState);
|
1325
|
+
if (!userId) {
|
1326
|
+
throw new Error(`Failed to extract userId from bytes for stableId ${stableId}`);
|
1327
|
+
}
|
1328
|
+
this.stableIdToUserId.set(stableId, userId);
|
1329
|
+
const newProfile = DeltaNetComponentMapping.fromStates(stableUserData.states, this.logger);
|
1330
|
+
this.userProfiles.set(userId, newProfile);
|
1331
|
+
const clientUpdate = DeltaNetComponentMapping.fromComponents(stableUserData.components);
|
1332
|
+
addedUserIds.set(userId, {
|
1333
|
+
userState: newProfile,
|
1334
|
+
components: clientUpdate
|
1335
|
+
});
|
1336
|
+
}
|
1337
|
+
const updatedUsers = /* @__PURE__ */ new Map();
|
1338
|
+
for (const [stableUserId, userInfo] of this.deltaNetState.byStableId) {
|
1339
|
+
const userId = this.stableIdToUserId.get(stableUserId);
|
1340
|
+
if (!userId) {
|
1341
|
+
throw new Error(`No userId found for stableUserId ${stableUserId}`);
|
1342
|
+
}
|
1343
|
+
if (!addedUserIds.has(userId)) {
|
1344
|
+
if (userInfo.components.size > 0) {
|
1345
|
+
const clientUpdate = DeltaNetComponentMapping.fromComponents(userInfo.components);
|
1346
|
+
updatedUsers.set(userId, {
|
1347
|
+
components: clientUpdate
|
1348
|
+
});
|
1349
|
+
}
|
1350
|
+
}
|
1351
|
+
}
|
1352
|
+
for (const update of stateUpdates) {
|
1353
|
+
const stableUserId = update.stableId;
|
1354
|
+
const userId = this.stableIdToUserId.get(stableUserId);
|
1355
|
+
if (!userId) {
|
1356
|
+
throw new Error(`No userId found for stableUserId ${stableUserId}`);
|
1357
|
+
}
|
1358
|
+
if (addedUserIds.has(userId)) {
|
1359
|
+
continue;
|
1360
|
+
}
|
1361
|
+
const profile = this.userProfiles.get(userId);
|
1362
|
+
if (!profile) {
|
1363
|
+
this.logger.warn(`No profile found for user ${userId}, skipping update`);
|
1364
|
+
continue;
|
1365
|
+
}
|
1366
|
+
const existingUpdate = updatedUsers.get(userId);
|
1367
|
+
let existingUserStateUpdate = existingUpdate.userState;
|
1368
|
+
if (!existingUserStateUpdate) {
|
1369
|
+
existingUserStateUpdate = {};
|
1370
|
+
existingUpdate.userState = existingUserStateUpdate;
|
1371
|
+
}
|
1372
|
+
switch (update.stateId) {
|
1373
|
+
case STATE_INTERNAL_CONNECTION_ID:
|
1374
|
+
this.logger.error(
|
1375
|
+
"STATE_INTERNAL_CONNECTION_ID is not expected to change in state updates"
|
1376
|
+
);
|
500
1377
|
break;
|
501
|
-
case
|
502
|
-
|
503
|
-
|
1378
|
+
case STATE_USERNAME:
|
1379
|
+
const username = DeltaNetComponentMapping.usernameFromBytes(update.state);
|
1380
|
+
if (username) {
|
1381
|
+
profile.username = username;
|
1382
|
+
existingUserStateUpdate.username = username;
|
1383
|
+
}
|
504
1384
|
break;
|
505
|
-
case
|
506
|
-
|
1385
|
+
case STATE_CHARACTER_DESCRIPTION:
|
1386
|
+
const characterDescription = DeltaNetComponentMapping.characterDescriptionFromBytes(
|
1387
|
+
update.state
|
1388
|
+
);
|
1389
|
+
profile.characterDescription = characterDescription;
|
1390
|
+
existingUserStateUpdate.characterDescription = characterDescription;
|
507
1391
|
break;
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
broadcastType: parsed.broadcastType,
|
513
|
-
payload: parsed.payload
|
514
|
-
});
|
515
|
-
} else {
|
516
|
-
console.warn("Unhandled broadcast", parsed);
|
517
|
-
}
|
1392
|
+
case STATE_COLORS:
|
1393
|
+
const colors = DeltaNetComponentMapping.decodeColors(update.state, this.logger);
|
1394
|
+
profile.colors = colors;
|
1395
|
+
existingUserStateUpdate.colors = colors;
|
518
1396
|
break;
|
519
|
-
}
|
520
1397
|
default:
|
521
|
-
|
1398
|
+
this.logger.warn(`Unknown state ID: ${update.stateId}`);
|
522
1399
|
}
|
523
|
-
} else if (message.data instanceof ArrayBuffer) {
|
524
|
-
const userNetworkingClientUpdate = UserNetworkingCodec.decodeUpdate(message.data);
|
525
|
-
this.config.clientUpdate(userNetworkingClientUpdate.id, userNetworkingClientUpdate);
|
526
|
-
} else {
|
527
|
-
console.error("Unhandled message type", message.data);
|
528
1400
|
}
|
1401
|
+
return {
|
1402
|
+
removedUserIds,
|
1403
|
+
addedUserIds,
|
1404
|
+
updatedUsers
|
1405
|
+
};
|
1406
|
+
}
|
1407
|
+
sendUpdate(update) {
|
1408
|
+
if (!this.isAuthenticated || this.userId === null) {
|
1409
|
+
this.pendingUpdate = update;
|
1410
|
+
return;
|
1411
|
+
}
|
1412
|
+
const components = DeltaNetComponentMapping.toComponents(update);
|
1413
|
+
this.deltaNetClient.setUserComponents(components, /* @__PURE__ */ new Map());
|
1414
|
+
}
|
1415
|
+
sendCustomMessage(customType, contents) {
|
1416
|
+
if (!this.isAuthenticated || this.userId === null) {
|
1417
|
+
this.logger.warn("Cannot send custom message before authentication");
|
1418
|
+
return;
|
1419
|
+
}
|
1420
|
+
this.deltaNetClient.sendCustomMessage(customType, contents);
|
1421
|
+
}
|
1422
|
+
updateUsername(username) {
|
1423
|
+
if (!this.isAuthenticated || this.userId === null) {
|
1424
|
+
return;
|
1425
|
+
}
|
1426
|
+
this.userState.username = username;
|
1427
|
+
const states = DeltaNetComponentMapping.toUsernameState(username);
|
1428
|
+
this.deltaNetClient.setUserComponents(/* @__PURE__ */ new Map(), states);
|
1429
|
+
}
|
1430
|
+
updateCharacterDescription(characterDescription) {
|
1431
|
+
if (!this.isAuthenticated || this.userId === null) {
|
1432
|
+
return;
|
1433
|
+
}
|
1434
|
+
this.userState.characterDescription = characterDescription;
|
1435
|
+
const states = DeltaNetComponentMapping.toCharacterDescriptionState(characterDescription);
|
1436
|
+
this.deltaNetClient.setUserComponents(/* @__PURE__ */ new Map(), states);
|
1437
|
+
}
|
1438
|
+
updateColors(colors) {
|
1439
|
+
if (!this.isAuthenticated || this.userId === null) {
|
1440
|
+
return;
|
1441
|
+
}
|
1442
|
+
this.userState.colors = colors;
|
1443
|
+
const states = DeltaNetComponentMapping.toColorsState(colors);
|
1444
|
+
this.deltaNetClient.setUserComponents(/* @__PURE__ */ new Map(), states);
|
1445
|
+
}
|
1446
|
+
stop() {
|
1447
|
+
this.deltaNetClient.stop();
|
1448
|
+
this.reset();
|
529
1449
|
}
|
530
1450
|
};
|
1451
|
+
|
1452
|
+
// src/index.ts
|
1453
|
+
import {
|
1454
|
+
DeltaNetV01ServerErrors as DeltaNetV01ServerErrors2,
|
1455
|
+
deltaNetProtocolSubProtocol_v0_1
|
1456
|
+
} from "@mml-io/delta-net-protocol";
|
531
1457
|
export {
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
1458
|
+
COMPONENT_POSITION_X,
|
1459
|
+
COMPONENT_POSITION_Y,
|
1460
|
+
COMPONENT_POSITION_Z,
|
1461
|
+
COMPONENT_ROTATION_W,
|
1462
|
+
COMPONENT_ROTATION_Y,
|
1463
|
+
COMPONENT_STATE,
|
1464
|
+
DeltaNetComponentMapping,
|
1465
|
+
DeltaNetV01ServerErrors2 as DeltaNetV01ServerErrors,
|
1466
|
+
FROM_CLIENT_CHAT_MESSAGE_TYPE,
|
1467
|
+
FROM_SERVER_CHAT_MESSAGE_TYPE,
|
1468
|
+
LEGACY_USER_NETWORKING_AUTHENTICATION_FAILED_ERROR_TYPE,
|
1469
|
+
LEGACY_USER_NETWORKING_CONNECTION_LIMIT_REACHED_ERROR_TYPE,
|
1470
|
+
LEGACY_USER_NETWORKING_DISCONNECTED_MESSAGE_TYPE,
|
1471
|
+
LEGACY_USER_NETWORKING_IDENTITY_MESSAGE_TYPE,
|
1472
|
+
LEGACY_USER_NETWORKING_PING_MESSAGE_TYPE,
|
1473
|
+
LEGACY_USER_NETWORKING_PONG_MESSAGE_TYPE,
|
1474
|
+
LEGACY_USER_NETWORKING_SERVER_BROADCAST_MESSAGE_TYPE,
|
1475
|
+
LEGACY_USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE,
|
1476
|
+
LEGACY_USER_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE,
|
1477
|
+
LEGACY_USER_NETWORKING_UNKNOWN_ERROR,
|
1478
|
+
LEGACY_USER_NETWORKING_USER_AUTHENTICATE_MESSAGE_TYPE,
|
1479
|
+
LEGACY_USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE,
|
1480
|
+
LEGACY_USER_NETWORKING_USER_UPDATE_MESSAGE_TYPE,
|
1481
|
+
SERVER_BROADCAST_MESSAGE_TYPE,
|
1482
|
+
STATE_CHARACTER_DESCRIPTION,
|
1483
|
+
STATE_COLORS,
|
1484
|
+
STATE_INTERNAL_CONNECTION_ID,
|
1485
|
+
STATE_USERNAME,
|
546
1486
|
UserNetworkingClient,
|
547
|
-
|
1487
|
+
UserNetworkingConsoleLogger,
|
548
1488
|
UserNetworkingServer,
|
549
|
-
|
1489
|
+
UserNetworkingServerError,
|
1490
|
+
WebsocketStatus,
|
1491
|
+
deltaNetProtocolSubProtocol_v0_1,
|
1492
|
+
parseClientChatMessage,
|
1493
|
+
parseServerBroadcastMessage,
|
1494
|
+
parseServerChatMessage,
|
1495
|
+
positionMultiplier,
|
1496
|
+
rotationMultiplier
|
550
1497
|
};
|
551
1498
|
//# sourceMappingURL=index.js.map
|