@mml-io/3d-web-user-networking 0.0.0-experimental-c32c620-20250626 → 0.0.0-experimental-3a2278c-20250715

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 "/Users/marcuslongmuir/mml/3d-web-experience/packages/deltanet/delta-net-protocol/build/index.js";
3
+ import {
4
+ DeltaNetServer,
5
+ DeltaNetServerError as DeltaNetServerError2
6
+ } from "/Users/marcuslongmuir/mml/3d-web-experience/packages/deltanet/delta-net-server/build/index.js";
7
+
8
+ // src/DeltaNetComponentMapping.ts
9
+ import { BufferReader, BufferWriter } from "/Users/marcuslongmuir/mml/3d-web-experience/packages/deltanet/delta-net-protocol/build/index.js";
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) {
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
+ console.error("Error decoding colors", colors, e);
170
+ return [];
171
+ }
172
+ }
173
+ static fromUserStates(states) {
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) : [];
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) {
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);
212
+ return { userId, ...userStates };
213
+ }
214
+ };
215
+
216
+ // src/UserNetworkingMessages.ts
217
+ import { DeltaNetServerError } from "/Users/marcuslongmuir/mml/3d-web-experience/packages/deltanet/delta-net-server/build/index.js";
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,77 +294,62 @@ 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
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];
318
+ }
319
+ return arrayBuffer;
320
+ }
51
321
  var WebSocketOpenStatus = 1;
52
- var UserNetworkingServer = class {
53
- constructor(options) {
54
- this.options = options;
322
+ var LegacyAdapter = class {
323
+ constructor(userNetworkingServer, deltaNetServer) {
324
+ this.userNetworkingServer = userNetworkingServer;
325
+ this.deltaNetServer = deltaNetServer;
55
326
  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
- });
78
- }
79
- getId() {
80
- let id = 1;
81
- while (this.allClientsById.has(id)) {
82
- id++;
83
- }
84
- return id;
327
+ this.legacyAuthenticatedClientsById = /* @__PURE__ */ new Map();
85
328
  }
86
329
  broadcastMessage(broadcastType, broadcastPayload) {
330
+ if (broadcastType !== SERVER_BROADCAST_MESSAGE_TYPE) {
331
+ return;
332
+ }
333
+ const parsedPayload = parseServerBroadcastMessage(broadcastPayload);
334
+ if (parsedPayload instanceof Error) {
335
+ console.error("Error parsing server broadcast message", parsedPayload);
336
+ return;
337
+ }
338
+ const { broadcastType: broadcastTypeString, payload } = parsedPayload;
87
339
  const message = {
88
340
  type: "broadcast",
89
- broadcastType,
90
- payload: broadcastPayload
341
+ broadcastType: broadcastTypeString,
342
+ payload
91
343
  };
92
344
  const messageString = JSON.stringify(message);
93
- for (const [, client] of this.authenticatedClientsById) {
345
+ for (const [, client] of this.legacyAuthenticatedClientsById) {
94
346
  if (client.socket.readyState === WebSocketOpenStatus) {
95
347
  client.socket.send(messageString);
96
348
  }
97
349
  }
98
350
  }
99
- connectClient(socket) {
100
- const id = this.getId();
351
+ addWebSocket(socket) {
352
+ const id = this.userNetworkingServer.getLegacyClientId();
101
353
  console.log(`Client ID: ${id} joined, waiting for user-identification`);
102
354
  const client = {
103
355
  id,
@@ -112,99 +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
+ console.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
+ console.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
+ console.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
+ console.error(`Unhandled message: ${JSON.stringify(parsed)}`);
530
+ }
203
531
  }
204
532
  }
533
+ } catch (e) {
534
+ console.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", () => {
545
+ socket.addEventListener("close", () => {
208
546
  console.log("Client disconnected", id);
209
547
  this.handleDisconnectedClient(client);
210
548
  });
@@ -215,21 +553,13 @@ var UserNetworkingServer = class {
215
553
  }
216
554
  this.allClientsById.delete(client.id);
217
555
  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
- }
556
+ this.userNetworkingServer.onLegacyClientDisconnect(client.id);
557
+ this.legacyAuthenticatedClientsById.delete(client.id);
558
+ this.deltaNetServer.clearInternalConnectionId(client.id);
229
559
  }
230
560
  }
231
561
  async handleUserAuth(client, credentials) {
232
- const userData = this.options.onClientConnect(
562
+ const userData = this.userNetworkingServer.onLegacyClientConnect(
233
563
  client.id,
234
564
  credentials.sessionToken,
235
565
  credentials.userIdentity
@@ -240,6 +570,15 @@ var UserNetworkingServer = class {
240
570
  } else {
241
571
  resolvedUserData = userData;
242
572
  }
573
+ if (resolvedUserData instanceof Error) {
574
+ console.error(`Client-id ${client.id} user_auth failed`, resolvedUserData);
575
+ return false;
576
+ } else if (resolvedUserData === true) {
577
+ console.error(`Client-id ${client.id} user_auth failed`, resolvedUserData);
578
+ resolvedUserData = credentials.userIdentity;
579
+ } else {
580
+ resolvedUserData = resolvedUserData;
581
+ }
243
582
  if (resolvedUserData === null) {
244
583
  console.error(`Client-id ${client.id} user_auth unauthorized and ignored`);
245
584
  return false;
@@ -251,31 +590,18 @@ var UserNetworkingServer = class {
251
590
  this.internalUpdateUser(clientId, userData);
252
591
  }
253
592
  internalUpdateUser(clientId, userData) {
254
- const client = this.authenticatedClientsById.get(clientId);
593
+ const client = this.legacyAuthenticatedClientsById.get(clientId);
255
594
  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
- }
595
+ this.legacyAuthenticatedClientsById.set(clientId, client);
596
+ this.userNetworkingServer.updateUserCharacter(client.id, { ...userData, colors: [] });
268
597
  }
269
598
  async handleUserUpdate(clientId, message) {
270
- const client = this.authenticatedClientsById.get(clientId);
599
+ const client = this.legacyAuthenticatedClientsById.get(clientId);
271
600
  if (!client) {
272
601
  console.error(`Client-id ${clientId} user_update ignored, client not found`);
273
602
  return;
274
603
  }
275
- const authorizedUserData = this.options.onClientUserIdentityUpdate(
276
- clientId,
277
- message.userIdentity
278
- );
604
+ const authorizedUserData = message.userIdentity;
279
605
  let resolvedAuthorizedUserData;
280
606
  if (authorizedUserData instanceof Promise) {
281
607
  resolvedAuthorizedUserData = await authorizedUserData;
@@ -288,23 +614,76 @@ var UserNetworkingServer = class {
288
614
  }
289
615
  this.internalUpdateUser(clientId, resolvedAuthorizedUserData);
290
616
  }
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) {
617
+ sendUpdates(removedIds, addedIds, updateUserProfilesInTick) {
618
+ for (const id of removedIds) {
619
+ const disconnectMessage = JSON.stringify({
620
+ id,
621
+ type: LEGACY_USER_NETWORKING_DISCONNECTED_MESSAGE_TYPE
622
+ });
623
+ for (const [, otherClient] of this.legacyAuthenticatedClientsById) {
624
+ if (otherClient.socket.readyState === WebSocketOpenStatus) {
625
+ otherClient.socket.send(disconnectMessage);
626
+ }
627
+ }
628
+ }
629
+ for (const id of addedIds) {
630
+ const identityMessage = JSON.stringify({
631
+ id,
632
+ type: LEGACY_USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE,
633
+ username: this.userNetworkingServer.getUsername(id),
634
+ characterDescription: this.userNetworkingServer.getCharacterDescription(id)
635
+ });
636
+ for (const [, otherClient] of this.legacyAuthenticatedClientsById) {
637
+ if (otherClient.socket.readyState === WebSocketOpenStatus) {
638
+ otherClient.socket.send(identityMessage);
639
+ }
640
+ }
641
+ }
642
+ for (const id of updateUserProfilesInTick) {
643
+ const identityMessage = JSON.stringify({
644
+ id,
645
+ type: LEGACY_USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE,
646
+ username: this.userNetworkingServer.getUsername(id),
647
+ characterDescription: this.userNetworkingServer.getCharacterDescription(id)
648
+ });
649
+ for (const [, otherClient] of this.legacyAuthenticatedClientsById) {
650
+ if (otherClient.socket.readyState === WebSocketOpenStatus) {
651
+ otherClient.socket.send(identityMessage);
652
+ }
653
+ }
654
+ }
655
+ for (const [clientId, client] of this.legacyAuthenticatedClientsById) {
656
+ const encodedUpdate = LegacyUserNetworkingCodec.encodeUpdate(client.update);
657
+ for (const [otherClientId, otherClient] of this.legacyAuthenticatedClientsById) {
296
658
  if (otherClientId !== clientId && otherClient.socket.readyState === WebSocketOpenStatus) {
297
659
  otherClient.socket.send(encodedUpdate);
298
660
  }
299
661
  }
300
662
  }
663
+ const allUsers = this.deltaNetServer.dangerouslyGetConnectionsToComponentIndex();
664
+ for (const [connectionId, componentIndex] of allUsers) {
665
+ const x = this.deltaNetServer.getComponentValue(COMPONENT_POSITION_X, componentIndex) / positionMultiplier;
666
+ const y = this.deltaNetServer.getComponentValue(COMPONENT_POSITION_Y, componentIndex) / positionMultiplier;
667
+ const z = this.deltaNetServer.getComponentValue(COMPONENT_POSITION_Z, componentIndex) / positionMultiplier;
668
+ const quaternionY = this.deltaNetServer.getComponentValue(COMPONENT_ROTATION_Y, componentIndex) / rotationMultiplier;
669
+ const quaternionW = this.deltaNetServer.getComponentValue(COMPONENT_ROTATION_W, componentIndex) / rotationMultiplier;
670
+ const state = this.deltaNetServer.getComponentValue(COMPONENT_STATE, componentIndex);
671
+ const encodedUpdate = LegacyUserNetworkingCodec.encodeUpdate({
672
+ id: connectionId,
673
+ position: { x, y, z },
674
+ rotation: { quaternionY, quaternionW },
675
+ state
676
+ });
677
+ for (const [otherClientId, otherClient] of this.legacyAuthenticatedClientsById) {
678
+ if (otherClientId !== connectionId && otherClient.socket.readyState === WebSocketOpenStatus) {
679
+ otherClient.socket.send(encodedUpdate);
680
+ }
681
+ }
682
+ }
301
683
  }
302
684
  dispose(clientCloseError) {
303
- clearInterval(this.sendUpdatesIntervalTimer);
304
- clearInterval(this.pingClientsIntervalTimer);
305
- clearInterval(this.heartbeatIntervalTimer);
306
685
  const stringifiedError = clientCloseError ? JSON.stringify(clientCloseError) : void 0;
307
- for (const [, client] of this.authenticatedClientsById) {
686
+ for (const [, client] of this.legacyAuthenticatedClientsById) {
308
687
  if (stringifiedError) {
309
688
  client.socket.send(stringifiedError);
310
689
  }
@@ -313,239 +692,795 @@ var UserNetworkingServer = class {
313
692
  }
314
693
  };
315
694
 
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);
695
+ // src/UserNetworkingServer.ts
696
+ var UserNetworkingServer = class {
697
+ constructor(options) {
698
+ this.options = options;
699
+ this.authenticatedClientsById = /* @__PURE__ */ new Map();
700
+ this.legacyAdapter = null;
701
+ this.updatedUserProfilesInTick = /* @__PURE__ */ new Set();
702
+ this.deltaNetServer = new DeltaNetServer({
703
+ serverConnectionIdStateId: 0,
704
+ onJoiner: (joiner) => {
705
+ return this.handleJoiner(joiner);
706
+ },
707
+ onLeave: (leave) => {
708
+ this.handleLeave(leave);
709
+ },
710
+ onComponentsUpdate: (update) => {
711
+ return;
712
+ },
713
+ onStatesUpdate: (update) => {
714
+ return this.handleStatesUpdate(update);
715
+ },
716
+ onCustomMessage: (customMessage) => {
717
+ this.handleCustomMessage(customMessage);
718
+ }
719
+ });
720
+ if (this.options.legacyAdapterEnabled) {
721
+ this.legacyAdapter = new LegacyAdapter(this, this.deltaNetServer);
344
722
  }
723
+ this.tickInterval = setInterval(() => {
724
+ const { removedIds, addedIds } = this.deltaNetServer.tick();
725
+ if (this.legacyAdapter) {
726
+ this.legacyAdapter.sendUpdates(removedIds, addedIds, this.updatedUserProfilesInTick);
727
+ this.updatedUserProfilesInTick.clear();
728
+ }
729
+ }, 50);
345
730
  }
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);
731
+ getCharacterDescription(connectionId) {
732
+ var _a, _b;
733
+ const client = this.authenticatedClientsById.get(connectionId);
734
+ console.log(
735
+ "getCharacterDescription",
736
+ connectionId,
737
+ (_a = client == null ? void 0 : client.authenticatedUser) == null ? void 0 : _a.characterDescription
738
+ );
739
+ return ((_b = client == null ? void 0 : client.authenticatedUser) == null ? void 0 : _b.characterDescription) ?? { mmlCharacterUrl: "" };
353
740
  }
354
- async startWebSocketConnectionAttempt() {
355
- if (this.stopped) {
356
- return;
741
+ getUsername(connectionId) {
742
+ var _a, _b;
743
+ const client = this.authenticatedClientsById.get(connectionId);
744
+ console.log("getUsername", connectionId, (_a = client == null ? void 0 : client.authenticatedUser) == null ? void 0 : _a.username);
745
+ return ((_b = client == null ? void 0 : client.authenticatedUser) == null ? void 0 : _b.username) ?? "";
746
+ }
747
+ getLegacyClientId() {
748
+ return this.deltaNetServer.getNextConnectionId();
749
+ }
750
+ hasCapacityForLegacyClient() {
751
+ return true;
752
+ }
753
+ onLegacyClientConnect(id, sessionToken, userIdentity) {
754
+ return this.options.onClientConnect(id, sessionToken, {
755
+ username: (userIdentity == null ? void 0 : userIdentity.username) ?? null,
756
+ characterDescription: (userIdentity == null ? void 0 : userIdentity.characterDescription) ?? null,
757
+ colors: null
758
+ });
759
+ }
760
+ setAuthenticatedLegacyClientConnection(clientId, webSocket, userData) {
761
+ console.log("setAuthenticatedLegacyClientConnection", clientId, userData);
762
+ const authenticatedClient = {
763
+ id: clientId,
764
+ socket: webSocket,
765
+ lastPong: Date.now(),
766
+ authenticatedUser: userData,
767
+ deltaNetConnection: null
768
+ };
769
+ this.authenticatedClientsById.set(clientId, authenticatedClient);
770
+ }
771
+ onLegacyClientDisconnect(id) {
772
+ this.options.onClientDisconnect(id);
773
+ }
774
+ handleStatesUpdate(update) {
775
+ const deltaNetConnection = update.deltaNetV01Connection;
776
+ const clientId = deltaNetConnection.internalConnectionId;
777
+ const updatedStates = update.states;
778
+ const updatedStatesMap = new Map(updatedStates);
779
+ const updatedUserData = DeltaNetComponentMapping.fromUserStates(updatedStatesMap);
780
+ const existingClient = this.authenticatedClientsById.get(clientId);
781
+ if (!existingClient) {
782
+ return new DeltaNetServerError2(
783
+ DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
784
+ "User not authenticated - no client found",
785
+ false
786
+ );
357
787
  }
358
- while (true) {
359
- if (this.stopped) {
360
- return;
788
+ const existingUserData = existingClient.authenticatedUser ?? {};
789
+ const userData = {
790
+ ...existingUserData,
791
+ ...updatedUserData
792
+ };
793
+ const res = this.options.onClientUserIdentityUpdate(clientId, userData);
794
+ if (res instanceof Promise) {
795
+ return res.then((res2) => {
796
+ if (!this.authenticatedClientsById.get(clientId)) {
797
+ return new DeltaNetServerError2(
798
+ DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
799
+ "User not authenticated - client disconnected",
800
+ false
801
+ );
802
+ }
803
+ if (res2 instanceof DeltaNetServerError2) {
804
+ return res2;
805
+ }
806
+ if (res2 instanceof Error) {
807
+ return new DeltaNetServerError2(
808
+ DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
809
+ "User identity update failed",
810
+ false
811
+ );
812
+ }
813
+ if (res2 === null) {
814
+ return new DeltaNetServerError2(
815
+ DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
816
+ "User identity update failed",
817
+ false
818
+ );
819
+ }
820
+ if (res2 === false) {
821
+ return new DeltaNetServerError2(
822
+ DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
823
+ "User identity update failed",
824
+ false
825
+ );
826
+ }
827
+ if (!res2 || typeof res2 !== "object") {
828
+ return new DeltaNetServerError2(
829
+ DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
830
+ "User identity update failed",
831
+ false
832
+ );
833
+ }
834
+ this.updatedUserProfilesInTick.add(clientId);
835
+ existingClient.authenticatedUser = {
836
+ ...existingClient.authenticatedUser,
837
+ ...res2
838
+ };
839
+ return {
840
+ success: true,
841
+ stateOverrides: Array.from(DeltaNetComponentMapping.toStates(res2).entries())
842
+ };
843
+ });
844
+ }
845
+ if (res instanceof DeltaNetServerError2) {
846
+ return res;
847
+ }
848
+ if (res instanceof Error) {
849
+ return new DeltaNetServerError2(
850
+ DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
851
+ "User identity update failed",
852
+ false
853
+ );
854
+ }
855
+ if (res === null) {
856
+ return new DeltaNetServerError2(
857
+ DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
858
+ "User identity update failed",
859
+ false
860
+ );
861
+ }
862
+ if (res === false) {
863
+ return new DeltaNetServerError2(
864
+ DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
865
+ "User identity update failed",
866
+ false
867
+ );
868
+ }
869
+ if (!res || typeof res !== "object") {
870
+ return new DeltaNetServerError2(
871
+ DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
872
+ "User identity update failed",
873
+ false
874
+ );
875
+ }
876
+ this.updatedUserProfilesInTick.add(clientId);
877
+ existingClient.authenticatedUser = {
878
+ ...existingClient.authenticatedUser,
879
+ ...res
880
+ };
881
+ return {
882
+ success: true,
883
+ stateOverrides: Array.from(DeltaNetComponentMapping.toStates(res).entries())
884
+ };
885
+ }
886
+ handleJoiner(joiner) {
887
+ const deltaNetConnection = joiner.deltaNetV01Connection;
888
+ const webSocket = deltaNetConnection.webSocket;
889
+ const states = joiner.states;
890
+ const clientId = joiner.internalConnectionId;
891
+ const statesMap = new Map(states);
892
+ const userData = DeltaNetComponentMapping.fromUserStates(statesMap);
893
+ return this.handleDeltaNetAuthentication(
894
+ clientId,
895
+ webSocket,
896
+ deltaNetConnection,
897
+ joiner.token,
898
+ userData
899
+ ).then((authResult) => {
900
+ if (!authResult.success) {
901
+ console.warn(`Authentication failed for client ID: ${clientId}`);
902
+ return new DeltaNetServerError2(
903
+ DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
904
+ "Authentication failed",
905
+ false
906
+ );
907
+ } else {
908
+ return {
909
+ success: true,
910
+ stateOverrides: authResult.stateOverrides
911
+ };
361
912
  }
362
- try {
363
- await this.createWebsocketWithTimeout(maximumWebsocketConnectionTimeout);
364
- break;
365
- } catch (e) {
366
- this.setStatus(2 /* Reconnecting */);
367
- await this.waitBackoffTime();
913
+ }).catch((error) => {
914
+ console.error(`Authentication error for client ID: ${clientId}:`, error);
915
+ return new DeltaNetServerError2(
916
+ DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE,
917
+ "Authentication error",
918
+ false
919
+ );
920
+ });
921
+ }
922
+ handleLeave(leave) {
923
+ const deltaNetConnection = leave.deltaNetV01Connection;
924
+ const clientId = deltaNetConnection.internalConnectionId;
925
+ if (clientId !== void 0) {
926
+ const client = this.authenticatedClientsById.get(clientId);
927
+ if (client) {
928
+ this.options.onClientDisconnect(clientId);
929
+ this.authenticatedClientsById.delete(clientId);
368
930
  }
369
931
  }
370
932
  }
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
- );
933
+ handleCustomMessage(customMessage) {
934
+ const deltaNetConnection = customMessage.deltaNetV01Connection;
935
+ const clientId = deltaNetConnection.internalConnectionId;
936
+ const client = this.authenticatedClientsById.get(clientId);
937
+ if (client && client.authenticatedUser) {
938
+ if (customMessage.customType === FROM_CLIENT_CHAT_MESSAGE_TYPE) {
939
+ const chatMessage = parseClientChatMessage(customMessage.contents);
940
+ if (chatMessage instanceof Error) {
941
+ console.error(`Invalid chat message from client ${clientId}:`, chatMessage);
942
+ } else {
943
+ const serverChatMessage = {
944
+ fromUserId: clientId,
945
+ message: chatMessage.message
946
+ };
947
+ this.deltaNetServer.broadcastCustomMessage(
948
+ FROM_SERVER_CHAT_MESSAGE_TYPE,
949
+ JSON.stringify(serverChatMessage)
950
+ );
951
+ }
952
+ }
953
+ } else {
954
+ console.warn(`Custom message from unauthenticated client ${clientId} - ignoring`);
955
+ }
379
956
  }
380
- send(message) {
381
- if (!this.websocket) {
382
- console.error("Not connected to the server");
383
- return;
957
+ async handleDeltaNetAuthentication(clientId, webSocket, deltaNetConnection, sessionToken, userIdentity) {
958
+ try {
959
+ let userData = deltaNetConnection.isObserver ? null : await this.options.onClientConnect(clientId, sessionToken, userIdentity);
960
+ if (!deltaNetConnection.isObserver && !userData) {
961
+ console.warn(`Authentication failed for client ${clientId} - no user data returned`);
962
+ return { success: false };
963
+ }
964
+ if (this.options.connectionLimit !== void 0 && this.authenticatedClientsById.size >= this.options.connectionLimit) {
965
+ return { success: false };
966
+ }
967
+ if (userData instanceof Error) {
968
+ return { success: false, error: userData };
969
+ }
970
+ if (userData === true) {
971
+ userData = userIdentity;
972
+ }
973
+ const authenticatedClient = {
974
+ id: clientId,
975
+ socket: webSocket,
976
+ lastPong: Date.now(),
977
+ authenticatedUser: userData,
978
+ deltaNetConnection
979
+ };
980
+ this.authenticatedClientsById.set(clientId, authenticatedClient);
981
+ let stateOverrides = [];
982
+ if (userData) {
983
+ const officialStates = DeltaNetComponentMapping.toStates(userData);
984
+ stateOverrides = Array.from(officialStates.entries());
985
+ }
986
+ return {
987
+ success: true,
988
+ stateOverrides
989
+ };
990
+ } catch (error) {
991
+ console.error("Authentication error:", error);
992
+ return { success: false };
384
993
  }
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();
430
- };
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
- });
994
+ }
995
+ connectClient(socket) {
996
+ if (socket.protocol === "") {
997
+ if (this.legacyAdapter) {
998
+ console.log("Legacy client detected - using legacy adapter");
999
+ this.legacyAdapter.addWebSocket(socket);
1000
+ return;
1001
+ } else {
1002
+ socket.close(1e3, "Legacy client detected (no subprotocol) - not supported");
1003
+ return;
1004
+ }
1005
+ }
1006
+ this.deltaNetServer.addWebSocket(socket);
1007
+ socket.addEventListener("close", () => {
1008
+ this.deltaNetServer.removeWebSocket(socket);
453
1009
  });
454
1010
  }
455
- stop() {
456
- this.stopped = true;
457
- if (this.websocket !== null) {
458
- this.websocket.close();
459
- this.websocket = null;
1011
+ broadcastMessage(broadcastType, broadcastPayload) {
1012
+ this.deltaNetServer.broadcastCustomMessage(broadcastType, broadcastPayload);
1013
+ if (this.legacyAdapter) {
1014
+ this.legacyAdapter.broadcastMessage(broadcastType, broadcastPayload);
460
1015
  }
461
1016
  }
1017
+ updateUserCharacter(clientId, userData) {
1018
+ console.log("updateUserCharacter", clientId, userData);
1019
+ this.internalUpdateUser(clientId, userData);
1020
+ }
1021
+ updateUserUsername(clientId, username) {
1022
+ const client = this.authenticatedClientsById.get(clientId);
1023
+ if (!client || !client.authenticatedUser) return;
1024
+ client.authenticatedUser = {
1025
+ ...client.authenticatedUser,
1026
+ username
1027
+ };
1028
+ this.updatedUserProfilesInTick.add(clientId);
1029
+ const states = DeltaNetComponentMapping.toUsernameState(username);
1030
+ const asArray = Array.from(states.entries());
1031
+ this.deltaNetServer.overrideUserStates(client.deltaNetConnection, clientId, asArray);
1032
+ }
1033
+ updateUserCharacterDescription(clientId, characterDescription) {
1034
+ const client = this.authenticatedClientsById.get(clientId);
1035
+ if (!client || !client.authenticatedUser) return;
1036
+ client.authenticatedUser = {
1037
+ ...client.authenticatedUser,
1038
+ characterDescription
1039
+ };
1040
+ this.updatedUserProfilesInTick.add(clientId);
1041
+ const states = DeltaNetComponentMapping.toCharacterDescriptionState(characterDescription);
1042
+ const asArray = Array.from(states.entries());
1043
+ this.deltaNetServer.overrideUserStates(client.deltaNetConnection, clientId, asArray);
1044
+ }
1045
+ updateUserColors(clientId, colors) {
1046
+ const client = this.authenticatedClientsById.get(clientId);
1047
+ if (!client || !client.authenticatedUser) return;
1048
+ client.authenticatedUser = {
1049
+ ...client.authenticatedUser,
1050
+ colors
1051
+ };
1052
+ this.updatedUserProfilesInTick.add(clientId);
1053
+ const states = DeltaNetComponentMapping.toColorsState(colors);
1054
+ const asArray = Array.from(states.entries());
1055
+ this.deltaNetServer.overrideUserStates(client.deltaNetConnection, clientId, asArray);
1056
+ }
1057
+ updateUserStates(clientId, updates) {
1058
+ const client = this.authenticatedClientsById.get(clientId);
1059
+ if (!client || !client.authenticatedUser) return;
1060
+ const states = /* @__PURE__ */ new Map();
1061
+ let hasUpdates = false;
1062
+ let updatedUserData = client.authenticatedUser;
1063
+ this.updatedUserProfilesInTick.add(clientId);
1064
+ if (updates.username !== null) {
1065
+ updatedUserData = {
1066
+ ...updatedUserData,
1067
+ username: updates.username
1068
+ };
1069
+ const usernameStates = DeltaNetComponentMapping.toUsernameState(updates.username);
1070
+ for (const [stateId, stateValue] of usernameStates) {
1071
+ states.set(stateId, stateValue);
1072
+ }
1073
+ hasUpdates = true;
1074
+ }
1075
+ if (updates.characterDescription !== null) {
1076
+ updatedUserData = {
1077
+ ...updatedUserData,
1078
+ characterDescription: updates.characterDescription
1079
+ };
1080
+ const characterDescStates = DeltaNetComponentMapping.toCharacterDescriptionState(
1081
+ updates.characterDescription
1082
+ );
1083
+ for (const [stateId, stateValue] of characterDescStates) {
1084
+ states.set(stateId, stateValue);
1085
+ }
1086
+ hasUpdates = true;
1087
+ }
1088
+ if (updates.colors !== null) {
1089
+ updatedUserData = {
1090
+ ...updatedUserData,
1091
+ colors: updates.colors
1092
+ };
1093
+ const colorsStates = DeltaNetComponentMapping.toColorsState(updates.colors);
1094
+ for (const [stateId, stateValue] of colorsStates) {
1095
+ states.set(stateId, stateValue);
1096
+ }
1097
+ hasUpdates = true;
1098
+ }
1099
+ if (hasUpdates) {
1100
+ client.authenticatedUser = updatedUserData;
1101
+ const asArray = Array.from(states.entries());
1102
+ this.deltaNetServer.overrideUserStates(client.deltaNetConnection, clientId, asArray);
1103
+ }
1104
+ }
1105
+ internalUpdateUser(clientId, userData) {
1106
+ const client = this.authenticatedClientsById.get(clientId);
1107
+ if (!client) {
1108
+ throw new Error(`internalUpdateUser - client not found for clientId ${clientId}`);
1109
+ }
1110
+ console.log("internalUpdateUser", clientId, userData);
1111
+ this.updatedUserProfilesInTick.add(clientId);
1112
+ client.authenticatedUser = {
1113
+ ...client.authenticatedUser,
1114
+ ...userData
1115
+ };
1116
+ const states = DeltaNetComponentMapping.toStates(userData);
1117
+ const asArray = Array.from(states.entries());
1118
+ this.deltaNetServer.overrideUserStates(client.deltaNetConnection, clientId, asArray);
1119
+ }
1120
+ dispose(clientCloseError) {
1121
+ if (this.tickInterval) {
1122
+ clearInterval(this.tickInterval);
1123
+ }
1124
+ let errorMessage = null;
1125
+ if (clientCloseError) {
1126
+ errorMessage = encodeError({
1127
+ type: "error",
1128
+ errorType: clientCloseError.errorType,
1129
+ message: clientCloseError.message,
1130
+ retryable: clientCloseError.retryable
1131
+ }).getBuffer();
1132
+ }
1133
+ for (const [, client] of this.authenticatedClientsById) {
1134
+ if (errorMessage) {
1135
+ client.socket.send(errorMessage);
1136
+ }
1137
+ client.socket.close();
1138
+ }
1139
+ this.authenticatedClientsById.clear();
1140
+ }
462
1141
  };
463
1142
 
464
1143
  // 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
- });
1144
+ import {
1145
+ DeltaNetClientState,
1146
+ DeltaNetClientWebsocket,
1147
+ DeltaNetClientWebsocketStatus
1148
+ } from "/Users/marcuslongmuir/mml/3d-web-experience/packages/deltanet/delta-net-web/build/index.js";
1149
+
1150
+ // src/types.ts
1151
+ var WebsocketStatus = /* @__PURE__ */ ((WebsocketStatus2) => {
1152
+ WebsocketStatus2[WebsocketStatus2["Connecting"] = 0] = "Connecting";
1153
+ WebsocketStatus2[WebsocketStatus2["Connected"] = 1] = "Connected";
1154
+ WebsocketStatus2[WebsocketStatus2["Reconnecting"] = 2] = "Reconnecting";
1155
+ WebsocketStatus2[WebsocketStatus2["Disconnected"] = 3] = "Disconnected";
1156
+ return WebsocketStatus2;
1157
+ })(WebsocketStatus || {});
1158
+
1159
+ // src/UserNetworkingClient.ts
1160
+ var UserNetworkingClient = class {
1161
+ constructor(config, initialUserState, initialUpdate) {
476
1162
  this.config = config;
1163
+ this.userId = null;
1164
+ this.userIndex = null;
1165
+ this.userState = {
1166
+ username: null,
1167
+ characterDescription: null,
1168
+ colors: null
1169
+ };
1170
+ this.stableIdToUserId = /* @__PURE__ */ new Map();
1171
+ this.userProfiles = /* @__PURE__ */ new Map();
1172
+ this.isAuthenticated = false;
1173
+ this.pendingUpdate = initialUpdate ?? {
1174
+ position: { x: 0, y: 0, z: 0 },
1175
+ rotation: { quaternionY: 0, quaternionW: 1 },
1176
+ state: 0
1177
+ };
1178
+ this.userState = initialUserState ?? {
1179
+ username: null,
1180
+ characterDescription: null,
1181
+ colors: null
1182
+ };
1183
+ this.deltaNetState = new DeltaNetClientState();
1184
+ this.deltaNetClient = new DeltaNetClientWebsocket(
1185
+ config.url,
1186
+ (url) => {
1187
+ const ws = config.websocketFactory(url);
1188
+ return ws;
1189
+ },
1190
+ config.sessionToken,
1191
+ {
1192
+ ignoreData: false,
1193
+ onInitialCheckout: (initialCheckout) => {
1194
+ const { addedStableIds } = this.deltaNetState.handleInitialCheckout(initialCheckout);
1195
+ const networkUpdate = this.processNetworkUpdate([], addedStableIds, []);
1196
+ this.config.onUpdate(networkUpdate);
1197
+ if (this.userIndex !== null) {
1198
+ const userIds = this.deltaNetState.getStableIds();
1199
+ if (this.userIndex < userIds.length) {
1200
+ const stableId = userIds[this.userIndex];
1201
+ const userId = this.stableIdToUserId.get(stableId);
1202
+ if (!userId) {
1203
+ throw new Error(`No userId found for stableId ${stableId}`);
1204
+ }
1205
+ this.userId = userId;
1206
+ this.isAuthenticated = true;
1207
+ this.config.assignedIdentity(this.userId);
1208
+ } else {
1209
+ console.error(
1210
+ `Invalid userIndex ${this.userIndex}, userIds length: ${userIds.length}`
1211
+ );
1212
+ }
1213
+ }
1214
+ },
1215
+ onTick: (tick) => {
1216
+ const { stateUpdates, removedStableIds, addedStableIds } = this.deltaNetState.handleTick(tick);
1217
+ const networkUpdate = this.processNetworkUpdate(
1218
+ removedStableIds,
1219
+ addedStableIds,
1220
+ stateUpdates
1221
+ );
1222
+ this.config.onUpdate(networkUpdate);
1223
+ },
1224
+ onUserIndex: (userIndex) => {
1225
+ this.userIndex = userIndex.userIndex;
1226
+ this.deltaNetState.setLocalIndex(userIndex.userIndex);
1227
+ console.log(
1228
+ `Received userIndex: ${userIndex.userIndex}, waiting for initial checkout to resolve stable userId...`
1229
+ );
1230
+ },
1231
+ onError: (errorType, errorMessage, retryable) => {
1232
+ console.error(
1233
+ "DeltaNet error:",
1234
+ errorType,
1235
+ "errorMessage:",
1236
+ errorMessage,
1237
+ "retryable:",
1238
+ retryable
1239
+ );
1240
+ this.config.onServerError({
1241
+ message: errorMessage,
1242
+ errorType
1243
+ });
1244
+ },
1245
+ onWarning: (warning) => {
1246
+ console.warn("DeltaNet warning:", warning);
1247
+ },
1248
+ onServerCustom: (customType, contents) => {
1249
+ var _a, _b;
1250
+ (_b = (_a = this.config).onCustomMessage) == null ? void 0 : _b.call(_a, customType, contents);
1251
+ }
1252
+ },
1253
+ void 0,
1254
+ // timeCallback is optional
1255
+ (status) => {
1256
+ let mappedStatus;
1257
+ switch (status) {
1258
+ case DeltaNetClientWebsocketStatus.Connected:
1259
+ mappedStatus = 1 /* Connected */;
1260
+ break;
1261
+ case DeltaNetClientWebsocketStatus.ConnectionOpen:
1262
+ this.sendInitialAuthentication();
1263
+ mappedStatus = 1 /* Connected */;
1264
+ break;
1265
+ case DeltaNetClientWebsocketStatus.Disconnected:
1266
+ mappedStatus = 3 /* Disconnected */;
1267
+ this.reset();
1268
+ break;
1269
+ case DeltaNetClientWebsocketStatus.Reconnecting:
1270
+ mappedStatus = 2 /* Reconnecting */;
1271
+ this.reset();
1272
+ break;
1273
+ default:
1274
+ mappedStatus = 3 /* Disconnected */;
1275
+ }
1276
+ this.config.statusUpdateCallback(mappedStatus);
1277
+ }
1278
+ );
477
1279
  }
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);
1280
+ reset() {
1281
+ this.deltaNetState.reset();
1282
+ this.userProfiles.clear();
1283
+ this.stableIdToUserId.clear();
1284
+ this.isAuthenticated = false;
1285
+ this.userId = null;
1286
+ this.userIndex = null;
1287
+ }
1288
+ sendInitialAuthentication() {
1289
+ const components = DeltaNetComponentMapping.toComponents(this.pendingUpdate);
1290
+ const states = DeltaNetComponentMapping.toStates(this.userState);
1291
+ this.deltaNetClient.setUserComponents(components, states);
1292
+ }
1293
+ processNetworkUpdate(removedStableIds, addedStableIdsArray, stateUpdates) {
1294
+ const addedUserIds = /* @__PURE__ */ new Map();
1295
+ const removedUserIds = /* @__PURE__ */ new Set();
1296
+ for (const stableId of removedStableIds) {
1297
+ const userId = this.stableIdToUserId.get(stableId);
1298
+ if (userId) {
1299
+ removedUserIds.add(userId);
1300
+ this.userProfiles.delete(userId);
1301
+ this.stableIdToUserId.delete(stableId);
1302
+ } else {
1303
+ throw new Error(`No userId found for stableId ${stableId}`);
1304
+ }
1305
+ }
1306
+ for (const stableId of addedStableIdsArray) {
1307
+ const stableUserData = this.deltaNetState.byStableId.get(stableId);
1308
+ if (!stableUserData) {
1309
+ throw new Error(`No stableUserData found for stableId ${stableId}`);
1310
+ }
1311
+ const userIdState = stableUserData.states.get(STATE_INTERNAL_CONNECTION_ID);
1312
+ if (!userIdState) {
1313
+ throw new Error(`No userIdState found for stableId ${stableId}`);
1314
+ }
1315
+ const userId = DeltaNetComponentMapping.userIdFromBytes(userIdState);
1316
+ if (!userId) {
1317
+ throw new Error(`Failed to extract userId from bytes for stableId ${stableId}`);
1318
+ }
1319
+ this.stableIdToUserId.set(stableId, userId);
1320
+ const newProfile = DeltaNetComponentMapping.fromStates(stableUserData.states);
1321
+ this.userProfiles.set(userId, newProfile);
1322
+ const clientUpdate = DeltaNetComponentMapping.fromComponents(stableUserData.components);
1323
+ addedUserIds.set(userId, {
1324
+ userState: newProfile,
1325
+ components: clientUpdate
1326
+ });
1327
+ }
1328
+ const updatedUsers = /* @__PURE__ */ new Map();
1329
+ for (const [stableUserId, userInfo] of this.deltaNetState.byStableId) {
1330
+ const userId = this.stableIdToUserId.get(stableUserId);
1331
+ if (!userId) {
1332
+ throw new Error(`No userId found for stableUserId ${stableUserId}`);
1333
+ }
1334
+ if (!addedUserIds.has(userId)) {
1335
+ if (userInfo.components.size > 0) {
1336
+ const clientUpdate = DeltaNetComponentMapping.fromComponents(userInfo.components);
1337
+ updatedUsers.set(userId, {
1338
+ components: clientUpdate
1339
+ });
1340
+ }
1341
+ }
1342
+ }
1343
+ for (const update of stateUpdates) {
1344
+ const stableUserId = update.stableId;
1345
+ const userId = this.stableIdToUserId.get(stableUserId);
1346
+ if (!userId) {
1347
+ throw new Error(`No userId found for stableUserId ${stableUserId}`);
1348
+ }
1349
+ if (addedUserIds.has(userId)) {
1350
+ continue;
1351
+ }
1352
+ const profile = this.userProfiles.get(userId);
1353
+ if (!profile) {
1354
+ console.warn(`No profile found for user ${userId}, skipping update`);
1355
+ continue;
1356
+ }
1357
+ const existingUpdate = updatedUsers.get(userId);
1358
+ let existingUserStateUpdate = existingUpdate.userState;
1359
+ if (!existingUserStateUpdate) {
1360
+ existingUserStateUpdate = {};
1361
+ existingUpdate.userState = existingUserStateUpdate;
1362
+ }
1363
+ switch (update.stateId) {
1364
+ case STATE_INTERNAL_CONNECTION_ID:
1365
+ console.error("STATE_INTERNAL_CONNECTION_ID is not expected to change in state updates");
500
1366
  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);
1367
+ case STATE_USERNAME:
1368
+ const username = DeltaNetComponentMapping.usernameFromBytes(update.state);
1369
+ if (username) {
1370
+ profile.username = username;
1371
+ existingUserStateUpdate.username = username;
1372
+ }
504
1373
  break;
505
- case USER_NETWORKING_PING_MESSAGE_TYPE: {
506
- this.sendMessage({ type: "pong" });
1374
+ case STATE_CHARACTER_DESCRIPTION:
1375
+ const characterDescription = DeltaNetComponentMapping.characterDescriptionFromBytes(
1376
+ update.state
1377
+ );
1378
+ profile.characterDescription = characterDescription;
1379
+ existingUserStateUpdate.characterDescription = characterDescription;
507
1380
  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
- }
1381
+ case STATE_COLORS:
1382
+ const colors = DeltaNetComponentMapping.decodeColors(update.state);
1383
+ profile.colors = colors;
1384
+ existingUserStateUpdate.colors = colors;
518
1385
  break;
519
- }
520
1386
  default:
521
- console.error("Unhandled message", parsed);
1387
+ console.warn(`Unknown state ID: ${update.stateId}`);
522
1388
  }
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
1389
  }
1390
+ return {
1391
+ removedUserIds,
1392
+ addedUserIds,
1393
+ updatedUsers
1394
+ };
1395
+ }
1396
+ sendUpdate(update) {
1397
+ if (!this.isAuthenticated || this.userId === null) {
1398
+ this.pendingUpdate = update;
1399
+ return;
1400
+ }
1401
+ const components = DeltaNetComponentMapping.toComponents(update);
1402
+ this.deltaNetClient.setUserComponents(components, /* @__PURE__ */ new Map());
1403
+ }
1404
+ sendCustomMessage(customType, contents) {
1405
+ if (!this.isAuthenticated || this.userId === null) {
1406
+ console.warn("Cannot send custom message before authentication");
1407
+ return;
1408
+ }
1409
+ this.deltaNetClient.sendCustomMessage(customType, contents);
1410
+ }
1411
+ updateUsername(username) {
1412
+ if (!this.isAuthenticated || this.userId === null) {
1413
+ return;
1414
+ }
1415
+ this.userState.username = username;
1416
+ const states = DeltaNetComponentMapping.toUsernameState(username);
1417
+ this.deltaNetClient.setUserComponents(/* @__PURE__ */ new Map(), states);
1418
+ }
1419
+ updateCharacterDescription(characterDescription) {
1420
+ if (!this.isAuthenticated || this.userId === null) {
1421
+ return;
1422
+ }
1423
+ this.userState.characterDescription = characterDescription;
1424
+ const states = DeltaNetComponentMapping.toCharacterDescriptionState(characterDescription);
1425
+ this.deltaNetClient.setUserComponents(/* @__PURE__ */ new Map(), states);
1426
+ }
1427
+ updateColors(colors) {
1428
+ if (!this.isAuthenticated || this.userId === null) {
1429
+ return;
1430
+ }
1431
+ this.userState.colors = colors;
1432
+ const states = DeltaNetComponentMapping.toColorsState(colors);
1433
+ this.deltaNetClient.setUserComponents(/* @__PURE__ */ new Map(), states);
1434
+ }
1435
+ stop() {
1436
+ this.deltaNetClient.stop();
1437
+ this.reset();
529
1438
  }
530
1439
  };
1440
+
1441
+ // src/index.ts
1442
+ import {
1443
+ DeltaNetV01ServerErrors as DeltaNetV01ServerErrors2,
1444
+ deltaNetProtocolSubProtocol_v0_1
1445
+ } from "/Users/marcuslongmuir/mml/3d-web-experience/packages/deltanet/delta-net-protocol/build/index.js";
531
1446
  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,
1447
+ COMPONENT_POSITION_X,
1448
+ COMPONENT_POSITION_Y,
1449
+ COMPONENT_POSITION_Z,
1450
+ COMPONENT_ROTATION_W,
1451
+ COMPONENT_ROTATION_Y,
1452
+ COMPONENT_STATE,
1453
+ DeltaNetComponentMapping,
1454
+ DeltaNetV01ServerErrors2 as DeltaNetV01ServerErrors,
1455
+ FROM_CLIENT_CHAT_MESSAGE_TYPE,
1456
+ FROM_SERVER_CHAT_MESSAGE_TYPE,
1457
+ LEGACY_USER_NETWORKING_AUTHENTICATION_FAILED_ERROR_TYPE,
1458
+ LEGACY_USER_NETWORKING_CONNECTION_LIMIT_REACHED_ERROR_TYPE,
1459
+ LEGACY_USER_NETWORKING_DISCONNECTED_MESSAGE_TYPE,
1460
+ LEGACY_USER_NETWORKING_IDENTITY_MESSAGE_TYPE,
1461
+ LEGACY_USER_NETWORKING_PING_MESSAGE_TYPE,
1462
+ LEGACY_USER_NETWORKING_PONG_MESSAGE_TYPE,
1463
+ LEGACY_USER_NETWORKING_SERVER_BROADCAST_MESSAGE_TYPE,
1464
+ LEGACY_USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE,
1465
+ LEGACY_USER_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE,
1466
+ LEGACY_USER_NETWORKING_UNKNOWN_ERROR,
1467
+ LEGACY_USER_NETWORKING_USER_AUTHENTICATE_MESSAGE_TYPE,
1468
+ LEGACY_USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE,
1469
+ LEGACY_USER_NETWORKING_USER_UPDATE_MESSAGE_TYPE,
1470
+ SERVER_BROADCAST_MESSAGE_TYPE,
1471
+ STATE_CHARACTER_DESCRIPTION,
1472
+ STATE_COLORS,
1473
+ STATE_INTERNAL_CONNECTION_ID,
1474
+ STATE_USERNAME,
546
1475
  UserNetworkingClient,
547
- UserNetworkingCodec,
548
1476
  UserNetworkingServer,
549
- WebsocketStatus
1477
+ UserNetworkingServerError,
1478
+ WebsocketStatus,
1479
+ deltaNetProtocolSubProtocol_v0_1,
1480
+ parseClientChatMessage,
1481
+ parseServerBroadcastMessage,
1482
+ parseServerChatMessage,
1483
+ positionMultiplier,
1484
+ rotationMultiplier
550
1485
  };
551
1486
  //# sourceMappingURL=index.js.map