@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/index.js CHANGED
@@ -1,5 +1,272 @@
1
- // src/UserNetworkingCodec.ts
2
- var UserNetworkingCodec = class {
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/user-networking-settings.ts
31
- var pingPongRate = 1500;
32
- var heartBeatRate = 15e3;
33
- var packetsUpdateRate = 1 / 30 * 1e3;
34
-
35
- // src/UserNetworkingMessages.ts
36
- var USER_NETWORKING_DISCONNECTED_MESSAGE_TYPE = "disconnected";
37
- var USER_NETWORKING_IDENTITY_MESSAGE_TYPE = "identity";
38
- var USER_NETWORKING_USER_AUTHENTICATE_MESSAGE_TYPE = "user_auth";
39
- var USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE = "user_profile";
40
- var USER_NETWORKING_USER_UPDATE_MESSAGE_TYPE = "user_update";
41
- var USER_NETWORKING_SERVER_BROADCAST_MESSAGE_TYPE = "broadcast";
42
- var USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE = "error";
43
- var USER_NETWORKING_PING_MESSAGE_TYPE = "ping";
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/UserNetworkingServer.ts
51
- var WebSocketOpenStatus = 1;
52
- var UserNetworkingServer = class {
53
- constructor(options) {
54
- this.options = options;
55
- this.allClientsById = /* @__PURE__ */ new Map();
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
- getId() {
80
- let id = 1;
81
- while (this.allClientsById.has(id)) {
82
- id++;
83
- }
84
- return id;
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: broadcastPayload
342
+ broadcastType: broadcastTypeString,
343
+ payload
91
344
  };
92
345
  const messageString = JSON.stringify(message);
93
- for (const [, client] of this.authenticatedClientsById) {
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
- connectClient(socket) {
100
- const id = this.getId();
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.on("message", (message, _isBinary) => {
116
- if (message instanceof Buffer) {
117
- const arrayBuffer = new Uint8Array(message).buffer;
118
- const update = UserNetworkingCodec.decodeUpdate(arrayBuffer);
119
- update.id = id;
120
- client.update = update;
121
- } else {
122
- let parsed;
123
- try {
124
- parsed = JSON.parse(message);
125
- } catch (e) {
126
- console.error("Error parsing JSON message", message, e);
127
- return;
128
- }
129
- if (!client.authenticatedUser) {
130
- if (parsed.type === USER_NETWORKING_USER_AUTHENTICATE_MESSAGE_TYPE) {
131
- this.handleUserAuth(client, parsed).then((authResult) => {
132
- var _a, _b;
133
- if (client.socket.readyState !== WebSocketOpenStatus) {
134
- return;
135
- }
136
- if (!authResult) {
137
- const serverError = JSON.stringify({
138
- type: USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE,
139
- errorType: USER_NETWORKING_AUTHENTICATION_FAILED_ERROR_TYPE,
140
- message: "Authentication failed"
141
- });
142
- socket.send(serverError);
143
- socket.close();
144
- } else {
145
- if (this.options.connectionLimit !== void 0 && this.authenticatedClientsById.size >= this.options.connectionLimit) {
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: USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE,
148
- errorType: USER_NETWORKING_CONNECTION_LIMIT_REACHED_ERROR_TYPE,
149
- message: "Connection limit reached"
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
- return;
154
- }
155
- const userData = authResult;
156
- client.authenticatedUser = userData;
157
- const userProfileMessage = JSON.stringify({
158
- id: client.id,
159
- type: USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE,
160
- username: userData.username,
161
- characterDescription: userData.characterDescription
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
- client.socket.send(
175
- JSON.stringify({
176
- id: otherClient.update.id,
177
- type: USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE,
178
- username: (_a = otherClient.authenticatedUser) == null ? void 0 : _a.username,
179
- characterDescription: (_b = otherClient.authenticatedUser) == null ? void 0 : _b.characterDescription
180
- })
181
- );
182
- client.socket.send(UserNetworkingCodec.encodeUpdate(otherClient.update));
183
- otherClient.socket.send(userProfileMessage);
184
- otherClient.socket.send(userUpdateMessage);
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
- this.authenticatedClientsById.set(id, client);
187
- }
188
- });
515
+ });
516
+ } else {
517
+ this.logger.error(`Unhandled message pre-auth: ${JSON.stringify(parsed)}`);
518
+ socket.close();
519
+ }
189
520
  } else {
190
- console.error(`Unhandled message pre-auth: ${JSON.stringify(parsed)}`);
191
- socket.close();
192
- }
193
- } else {
194
- switch (parsed.type) {
195
- case USER_NETWORKING_PONG_MESSAGE_TYPE:
196
- client.lastPong = Date.now();
197
- break;
198
- case USER_NETWORKING_USER_UPDATE_MESSAGE_TYPE:
199
- this.handleUserUpdate(id, parsed);
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.on("close", () => {
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.options.onClientDisconnect(client.id);
219
- this.authenticatedClientsById.delete(client.id);
220
- const disconnectMessage = JSON.stringify({
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.options.onClientConnect(
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
- console.error(`Client-id ${client.id} user_auth unauthorized and ignored`);
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.authenticatedClientsById.get(clientId);
591
+ const client = this.legacyAuthenticatedClientsById.get(clientId);
255
592
  client.authenticatedUser = userData;
256
- this.authenticatedClientsById.set(clientId, client);
257
- const newUserData = JSON.stringify({
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.authenticatedClientsById.get(clientId);
597
+ const client = this.legacyAuthenticatedClientsById.get(clientId);
271
598
  if (!client) {
272
- console.error(`Client-id ${clientId} user_update ignored, client not found`);
599
+ this.logger.error(`Client-id ${clientId} user_update ignored, client not found`);
273
600
  return;
274
601
  }
275
- const authorizedUserData = this.options.onClientUserIdentityUpdate(
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
- console.warn(`Client-id ${clientId} user_update unauthorized and ignored`);
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 [clientId, client] of this.authenticatedClientsById) {
293
- const update = client.update;
294
- const encodedUpdate = UserNetworkingCodec.encodeUpdate(update);
295
- for (const [otherClientId, otherClient] of this.authenticatedClientsById) {
296
- if (otherClientId !== clientId && otherClient.socket.readyState === WebSocketOpenStatus) {
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.authenticatedClientsById) {
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/ReconnectingWebSocket.ts
317
- var WebsocketStatus = /* @__PURE__ */ ((WebsocketStatus2) => {
318
- WebsocketStatus2[WebsocketStatus2["Connecting"] = 0] = "Connecting";
319
- WebsocketStatus2[WebsocketStatus2["Connected"] = 1] = "Connected";
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
- sendUpdate(update) {
347
- if (!this.websocket) {
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
- async startWebSocketConnectionAttempt() {
355
- if (this.stopped) {
356
- return;
357
- }
358
- while (true) {
359
- if (this.stopped) {
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
- try {
363
- await this.createWebsocketWithTimeout(maximumWebsocketConnectionTimeout);
364
- break;
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
- async waitBackoffTime() {
372
- console.warn(`Websocket connection to '${this.url}' failed: retrying in ${this.backoffTime}ms`);
373
- await new Promise((resolve) => setTimeout(resolve, this.backoffTime));
374
- this.backoffTime = Math.min(
375
- // Introduce a small amount of randomness to prevent clients from retrying in lockstep
376
- this.backoffTime * (1.5 + Math.random() * 0.5),
377
- maximumBackoffTimeMilliseconds
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
- send(message) {
381
- if (!this.websocket) {
382
- console.error("Not connected to the server");
383
- return;
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
- if (message instanceof Uint8Array) {
386
- this.websocket.send(message);
387
- } else {
388
- this.websocket.send(JSON.stringify(message));
389
- }
390
- }
391
- createWebsocketWithTimeout(timeout) {
392
- return new Promise((resolve, reject) => {
393
- const websocket = this.websocketFactory(this.url);
394
- const timeoutId = setTimeout(() => {
395
- reject(new Error("websocket connection timed out"));
396
- websocket.close();
397
- }, timeout);
398
- websocket.binaryType = "arraybuffer";
399
- websocket.addEventListener("open", () => {
400
- clearTimeout(timeoutId);
401
- this.receivedMessageSinceOpen = false;
402
- this.websocket = websocket;
403
- this.setStatus(1 /* Connected */);
404
- websocket.addEventListener("message", (event) => {
405
- if (websocket !== this.websocket) {
406
- console.log("Ignoring websocket message event because it is no longer current");
407
- websocket.close();
408
- return;
409
- }
410
- if (!this.receivedMessageSinceOpen) {
411
- this.receivedMessageSinceOpen = true;
412
- }
413
- this.handleIncomingWebsocketMessage(event);
414
- });
415
- const onWebsocketClose = async () => {
416
- if (websocket !== this.websocket) {
417
- console.log("Ignoring websocket close event because it is no longer current");
418
- return;
419
- }
420
- this.websocket = null;
421
- if (this.stopped) {
422
- this.setStatus(3 /* Disconnected */);
423
- return;
424
- }
425
- if (!this.receivedMessageSinceOpen) {
426
- await this.waitBackoffTime();
427
- }
428
- this.setStatus(2 /* Reconnecting */);
429
- this.startWebSocketConnectionAttempt();
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
- stop() {
456
- this.stopped = true;
457
- if (this.websocket !== null) {
458
- this.websocket.close();
459
- this.websocket = null;
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
- var UserNetworkingClient = class extends ReconnectingWebSocket {
466
- constructor(config) {
467
- super(config.url, config.websocketFactory, (status) => {
468
- if (status === 1 /* Connected */) {
469
- this.sendMessage({
470
- type: USER_NETWORKING_USER_AUTHENTICATE_MESSAGE_TYPE,
471
- sessionToken: config.sessionToken
472
- });
473
- }
474
- config.statusUpdateCallback(status);
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
- sendUpdate(update) {
479
- const encodedUpdate = UserNetworkingCodec.encodeUpdate(update);
480
- this.send(encodedUpdate);
481
- }
482
- sendMessage(message) {
483
- this.send(message);
484
- }
485
- handleIncomingWebsocketMessage(message) {
486
- if (typeof message.data === "string") {
487
- const parsed = JSON.parse(message.data);
488
- switch (parsed.type) {
489
- case USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE:
490
- console.error(`Server error: ${parsed.message}. errorType: ${parsed.errorType}`);
491
- this.config.onServerError(parsed);
492
- break;
493
- case USER_NETWORKING_DISCONNECTED_MESSAGE_TYPE:
494
- console.log(`Client ID: ${parsed.id} left`);
495
- this.config.clientUpdate(parsed.id, null);
496
- break;
497
- case USER_NETWORKING_IDENTITY_MESSAGE_TYPE:
498
- console.log(`Client ID: ${parsed.id} assigned to self`);
499
- this.config.assignedIdentity(parsed.id);
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 USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE:
502
- console.log(`Client ID: ${parsed.id} updated profile`);
503
- this.config.clientProfileUpdated(parsed.id, parsed.username, parsed.characterDescription);
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 USER_NETWORKING_PING_MESSAGE_TYPE: {
506
- this.sendMessage({ type: "pong" });
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
- case USER_NETWORKING_SERVER_BROADCAST_MESSAGE_TYPE: {
510
- if (this.config.onServerBroadcast) {
511
- this.config.onServerBroadcast({
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
- console.error("Unhandled message", parsed);
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
- ReconnectingWebSocket,
533
- USER_NETWORKING_AUTHENTICATION_FAILED_ERROR_TYPE,
534
- USER_NETWORKING_CONNECTION_LIMIT_REACHED_ERROR_TYPE,
535
- USER_NETWORKING_DISCONNECTED_MESSAGE_TYPE,
536
- USER_NETWORKING_IDENTITY_MESSAGE_TYPE,
537
- USER_NETWORKING_PING_MESSAGE_TYPE,
538
- USER_NETWORKING_PONG_MESSAGE_TYPE,
539
- USER_NETWORKING_SERVER_BROADCAST_MESSAGE_TYPE,
540
- USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE,
541
- USER_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE,
542
- USER_NETWORKING_UNKNOWN_ERROR,
543
- USER_NETWORKING_USER_AUTHENTICATE_MESSAGE_TYPE,
544
- USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE,
545
- USER_NETWORKING_USER_UPDATE_MESSAGE_TYPE,
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
- UserNetworkingCodec,
1487
+ UserNetworkingConsoleLogger,
548
1488
  UserNetworkingServer,
549
- WebsocketStatus
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