@mml-io/delta-net-server 0.0.0-experimental-bcf0b7c-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/README.md +10 -0
- package/build/ComponentCollection.d.ts +22 -0
- package/build/DeltaNetServer.d.ts +126 -0
- package/build/DeltaNetV01Connection.d.ts +25 -0
- package/build/StateCollection.d.ts +8 -0
- package/build/createDeltaNetServerConnectionForWebsocket.d.ts +4 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +1238 -0
- package/build/index.js.map +7 -0
- package/package.json +34 -0
package/build/index.js
ADDED
@@ -0,0 +1,1238 @@
|
|
1
|
+
// src/DeltaNetServer.ts
|
2
|
+
import {
|
3
|
+
BufferWriter,
|
4
|
+
encodeInitialCheckout,
|
5
|
+
encodePing,
|
6
|
+
encodeTick,
|
7
|
+
encodeServerMessage as encodeServerMessage2
|
8
|
+
} from "/Users/marcuslongmuir/mml/3d-web-experience/packages/deltanet/delta-net-protocol/build/index.js";
|
9
|
+
|
10
|
+
// src/ComponentCollection.ts
|
11
|
+
var ComponentCollection = class _ComponentCollection {
|
12
|
+
// Internal storage using bigint arrays for unlimited precision
|
13
|
+
targetValues;
|
14
|
+
pendingDeltas;
|
15
|
+
previousEmittedDeltas;
|
16
|
+
// Track the current observable value (what existing connections see)
|
17
|
+
currentObservableValues;
|
18
|
+
// Constants for int64 range
|
19
|
+
static MIN_INT64 = -9223372036854775808n;
|
20
|
+
static MAX_INT64 = 9223372036854775807n;
|
21
|
+
constructor(initialLength = 128) {
|
22
|
+
this.targetValues = new Array(initialLength).fill(BigInt(0));
|
23
|
+
this.pendingDeltas = new Array(initialLength).fill(BigInt(0));
|
24
|
+
this.previousEmittedDeltas = new Array(initialLength).fill(BigInt(0));
|
25
|
+
this.currentObservableValues = new Array(initialLength).fill(BigInt(0));
|
26
|
+
}
|
27
|
+
setLength(length) {
|
28
|
+
if (length > this.targetValues.length) {
|
29
|
+
while (this.targetValues.length < length) {
|
30
|
+
this.targetValues.push(BigInt(0));
|
31
|
+
this.pendingDeltas.push(BigInt(0));
|
32
|
+
this.previousEmittedDeltas.push(BigInt(0));
|
33
|
+
this.currentObservableValues.push(BigInt(0));
|
34
|
+
}
|
35
|
+
} else if (length < this.targetValues.length) {
|
36
|
+
this.targetValues = this.targetValues.slice(0, length);
|
37
|
+
this.pendingDeltas = this.pendingDeltas.slice(0, length);
|
38
|
+
this.previousEmittedDeltas = this.previousEmittedDeltas.slice(0, length);
|
39
|
+
this.currentObservableValues = this.currentObservableValues.slice(0, length);
|
40
|
+
}
|
41
|
+
}
|
42
|
+
setValue(index, value) {
|
43
|
+
if (index >= this.targetValues.length) {
|
44
|
+
const newLength = Math.pow(2, Math.ceil(Math.log2(index + 1)));
|
45
|
+
this.setLength(newLength);
|
46
|
+
}
|
47
|
+
const clampedValue = this.clampToInt64(value);
|
48
|
+
const currentValue = this.targetValues[index];
|
49
|
+
const totalDelta = clampedValue - currentValue;
|
50
|
+
this.pendingDeltas[index] += totalDelta;
|
51
|
+
this.targetValues[index] = clampedValue;
|
52
|
+
}
|
53
|
+
clampToInt64(value) {
|
54
|
+
if (value > _ComponentCollection.MAX_INT64) {
|
55
|
+
return _ComponentCollection.MAX_INT64;
|
56
|
+
}
|
57
|
+
if (value < _ComponentCollection.MIN_INT64) {
|
58
|
+
return _ComponentCollection.MIN_INT64;
|
59
|
+
}
|
60
|
+
return value;
|
61
|
+
}
|
62
|
+
tick() {
|
63
|
+
const length = this.targetValues.length;
|
64
|
+
const delta = new BigInt64Array(length);
|
65
|
+
const deltaDeltas = new BigInt64Array(length);
|
66
|
+
for (let i = 0; i < length; i++) {
|
67
|
+
const pendingDelta = this.pendingDeltas[i];
|
68
|
+
const emittableDelta = this.clampToInt64(pendingDelta);
|
69
|
+
delta[i] = emittableDelta;
|
70
|
+
const deltaDelta = emittableDelta - this.previousEmittedDeltas[i];
|
71
|
+
const clampedDeltaDelta = this.clampToInt64(deltaDelta);
|
72
|
+
deltaDeltas[i] = clampedDeltaDelta;
|
73
|
+
this.previousEmittedDeltas[i] = emittableDelta;
|
74
|
+
this.currentObservableValues[i] = this.clampToInt64(
|
75
|
+
this.currentObservableValues[i] + emittableDelta
|
76
|
+
);
|
77
|
+
this.pendingDeltas[i] -= emittableDelta;
|
78
|
+
}
|
79
|
+
return { delta, deltaDeltas };
|
80
|
+
}
|
81
|
+
removeIndices(sortedUnoccupyingIndices) {
|
82
|
+
if (sortedUnoccupyingIndices.length === 0) {
|
83
|
+
return;
|
84
|
+
}
|
85
|
+
let writeIndex = 0;
|
86
|
+
let skipIndex = 0;
|
87
|
+
for (let readIndex = 0; readIndex < this.targetValues.length; readIndex++) {
|
88
|
+
if (skipIndex < sortedUnoccupyingIndices.length && readIndex === sortedUnoccupyingIndices[skipIndex]) {
|
89
|
+
skipIndex++;
|
90
|
+
continue;
|
91
|
+
}
|
92
|
+
if (writeIndex !== readIndex) {
|
93
|
+
this.targetValues[writeIndex] = this.targetValues[readIndex];
|
94
|
+
this.pendingDeltas[writeIndex] = this.pendingDeltas[readIndex];
|
95
|
+
this.previousEmittedDeltas[writeIndex] = this.previousEmittedDeltas[readIndex];
|
96
|
+
this.currentObservableValues[writeIndex] = this.currentObservableValues[readIndex];
|
97
|
+
}
|
98
|
+
writeIndex++;
|
99
|
+
}
|
100
|
+
for (let i = writeIndex; i < this.targetValues.length; i++) {
|
101
|
+
this.targetValues[i] = BigInt(0);
|
102
|
+
this.pendingDeltas[i] = BigInt(0);
|
103
|
+
this.previousEmittedDeltas[i] = BigInt(0);
|
104
|
+
this.currentObservableValues[i] = BigInt(0);
|
105
|
+
}
|
106
|
+
}
|
107
|
+
// Getter for current target values (for testing)
|
108
|
+
getTargetValue(index) {
|
109
|
+
return this.targetValues[index] || BigInt(0);
|
110
|
+
}
|
111
|
+
// Getter for pending deltas (for testing)
|
112
|
+
getPendingDelta(index) {
|
113
|
+
return this.pendingDeltas[index] || BigInt(0);
|
114
|
+
}
|
115
|
+
// Getter for length (for testing)
|
116
|
+
get length() {
|
117
|
+
return this.targetValues.length;
|
118
|
+
}
|
119
|
+
// Get target values as BigInt64Array for serialization
|
120
|
+
getCurrentValuesArray() {
|
121
|
+
const result = new BigInt64Array(this.targetValues.length);
|
122
|
+
for (let i = 0; i < this.targetValues.length; i++) {
|
123
|
+
result[i] = this.currentObservableValues[i];
|
124
|
+
}
|
125
|
+
return result;
|
126
|
+
}
|
127
|
+
// Get previous emitted deltas as BigInt64Array for serialization
|
128
|
+
getPreviousEmittedDeltasArray() {
|
129
|
+
const result = new BigInt64Array(this.previousEmittedDeltas.length);
|
130
|
+
for (let i = 0; i < this.previousEmittedDeltas.length; i++) {
|
131
|
+
result[i] = this.previousEmittedDeltas[i];
|
132
|
+
}
|
133
|
+
return result;
|
134
|
+
}
|
135
|
+
};
|
136
|
+
|
137
|
+
// src/createDeltaNetServerConnectionForWebsocket.ts
|
138
|
+
import {
|
139
|
+
deltaNetProtocolSubProtocol_v0_1
|
140
|
+
} from "/Users/marcuslongmuir/mml/3d-web-experience/packages/deltanet/delta-net-protocol/build/index.js";
|
141
|
+
|
142
|
+
// src/DeltaNetV01Connection.ts
|
143
|
+
import {
|
144
|
+
DeltaNetV01ServerErrors,
|
145
|
+
BufferReader,
|
146
|
+
decodeClientMessages,
|
147
|
+
encodeServerMessage
|
148
|
+
} from "/Users/marcuslongmuir/mml/3d-web-experience/packages/deltanet/delta-net-protocol/build/index.js";
|
149
|
+
var DeltaNetV01Connection = class {
|
150
|
+
constructor(webSocket, deltaNetServer) {
|
151
|
+
this.webSocket = webSocket;
|
152
|
+
this.deltaNetServer = deltaNetServer;
|
153
|
+
this.internalConnectionId = deltaNetServer.getNextConnectionId();
|
154
|
+
this.websocketListener = (messageEvent) => {
|
155
|
+
const buffer = new Uint8Array(messageEvent.data);
|
156
|
+
const maxMessageSize = this.deltaNetServer.getMaxMessageSize();
|
157
|
+
if (buffer.length > maxMessageSize) {
|
158
|
+
this.disconnectWithError(
|
159
|
+
new Error(
|
160
|
+
`Message size ${buffer.length} bytes exceeds maximum allowed size of ${maxMessageSize} bytes`
|
161
|
+
),
|
162
|
+
DeltaNetV01ServerErrors.USER_NETWORKING_UNKNOWN_ERROR_TYPE,
|
163
|
+
false
|
164
|
+
);
|
165
|
+
return;
|
166
|
+
}
|
167
|
+
try {
|
168
|
+
const messages = decodeClientMessages(new BufferReader(buffer));
|
169
|
+
for (const parsed of messages) {
|
170
|
+
this.handleClientMessage(parsed);
|
171
|
+
}
|
172
|
+
} catch (error) {
|
173
|
+
this.disconnectWithError(
|
174
|
+
new Error(`Failed to decode client messages: ${error instanceof Error ? error.message : error}`),
|
175
|
+
DeltaNetV01ServerErrors.USER_NETWORKING_UNKNOWN_ERROR_TYPE,
|
176
|
+
false
|
177
|
+
);
|
178
|
+
return;
|
179
|
+
}
|
180
|
+
};
|
181
|
+
webSocket.addEventListener("message", this.websocketListener);
|
182
|
+
this.deltaNetServer = deltaNetServer;
|
183
|
+
}
|
184
|
+
websocketListener;
|
185
|
+
internalConnectionId;
|
186
|
+
isObserver = false;
|
187
|
+
// Track observer mode
|
188
|
+
// Pending state for new joiners (before they're authenticated)
|
189
|
+
components = /* @__PURE__ */ new Map();
|
190
|
+
states = /* @__PURE__ */ new Map();
|
191
|
+
// Track pending async state validations per state
|
192
|
+
pendingStateValidations = /* @__PURE__ */ new Map();
|
193
|
+
nextValidationId = 1;
|
194
|
+
// Track authentication state
|
195
|
+
isAuthenticated = false;
|
196
|
+
isAuthenticating = false;
|
197
|
+
authenticationAbortController = null;
|
198
|
+
sendMessage(message) {
|
199
|
+
this.sendEncodedBytes(encodeServerMessage(message).getBuffer());
|
200
|
+
}
|
201
|
+
sendEncodedBytes(bytes) {
|
202
|
+
this.webSocket.send(bytes);
|
203
|
+
}
|
204
|
+
dispose() {
|
205
|
+
this.webSocket.removeEventListener("message", this.websocketListener);
|
206
|
+
for (const [, validation] of this.pendingStateValidations) {
|
207
|
+
validation.abortController.abort();
|
208
|
+
}
|
209
|
+
this.pendingStateValidations.clear();
|
210
|
+
if (this.authenticationAbortController) {
|
211
|
+
this.authenticationAbortController.abort();
|
212
|
+
this.authenticationAbortController = null;
|
213
|
+
}
|
214
|
+
}
|
215
|
+
setAuthenticated() {
|
216
|
+
this.isAuthenticated = true;
|
217
|
+
}
|
218
|
+
disconnectWithError(error, errorType, retryable = true) {
|
219
|
+
try {
|
220
|
+
this.sendMessage({
|
221
|
+
type: "error",
|
222
|
+
errorType,
|
223
|
+
message: error.message,
|
224
|
+
retryable
|
225
|
+
});
|
226
|
+
} catch (sendError) {
|
227
|
+
console.warn("Failed to send error message to client:", sendError);
|
228
|
+
}
|
229
|
+
try {
|
230
|
+
this.webSocket.close(1008, error.message);
|
231
|
+
} catch (closeError) {
|
232
|
+
console.warn("Failed to close websocket connection:", closeError);
|
233
|
+
}
|
234
|
+
}
|
235
|
+
async handleConnectUser(token, observer, components, states) {
|
236
|
+
var _a, _b;
|
237
|
+
if (this.isAuthenticated) {
|
238
|
+
this.disconnectWithError(
|
239
|
+
new Error("User is already authenticated"),
|
240
|
+
DeltaNetV01ServerErrors.USER_ALREADY_AUTHENTICATED_ERROR_TYPE,
|
241
|
+
false
|
242
|
+
);
|
243
|
+
return;
|
244
|
+
}
|
245
|
+
if (this.isAuthenticating) {
|
246
|
+
this.disconnectWithError(
|
247
|
+
new Error("Authentication already in progress"),
|
248
|
+
DeltaNetV01ServerErrors.AUTHENTICATION_IN_PROGRESS_ERROR_TYPE,
|
249
|
+
false
|
250
|
+
);
|
251
|
+
return;
|
252
|
+
}
|
253
|
+
this.isAuthenticating = true;
|
254
|
+
this.authenticationAbortController = new AbortController();
|
255
|
+
this.isObserver = observer;
|
256
|
+
this.components = new Map(components);
|
257
|
+
this.states = new Map(states);
|
258
|
+
let result;
|
259
|
+
try {
|
260
|
+
const rawResult = this.deltaNetServer.validateJoiner(this, token, components, states);
|
261
|
+
if (rawResult instanceof Promise) {
|
262
|
+
result = await rawResult;
|
263
|
+
if ((_a = this.authenticationAbortController) == null ? void 0 : _a.signal.aborted) {
|
264
|
+
return;
|
265
|
+
}
|
266
|
+
if (!this.deltaNetServer.hasWebSocket(this.webSocket)) {
|
267
|
+
return;
|
268
|
+
}
|
269
|
+
} else {
|
270
|
+
result = rawResult;
|
271
|
+
}
|
272
|
+
} catch (error) {
|
273
|
+
if ((_b = this.authenticationAbortController) == null ? void 0 : _b.signal.aborted) {
|
274
|
+
return;
|
275
|
+
}
|
276
|
+
result = error;
|
277
|
+
}
|
278
|
+
this.authenticationAbortController = null;
|
279
|
+
if (result instanceof DeltaNetServerError) {
|
280
|
+
this.disconnectWithError(result, result.errorType, result.retryable);
|
281
|
+
} else if (result instanceof Error) {
|
282
|
+
this.disconnectWithError(
|
283
|
+
result,
|
284
|
+
DeltaNetV01ServerErrors.USER_NETWORKING_UNKNOWN_ERROR_TYPE,
|
285
|
+
false
|
286
|
+
);
|
287
|
+
} else if (typeof result !== "object") {
|
288
|
+
this.disconnectWithError(
|
289
|
+
new Error("Invalid authentication result"),
|
290
|
+
DeltaNetV01ServerErrors.USER_NETWORKING_UNKNOWN_ERROR_TYPE,
|
291
|
+
false
|
292
|
+
);
|
293
|
+
} else {
|
294
|
+
if (result.success) {
|
295
|
+
if (result.stateOverrides) {
|
296
|
+
for (const [stateId, stateValue] of result.stateOverrides) {
|
297
|
+
this.states.set(stateId, stateValue);
|
298
|
+
}
|
299
|
+
}
|
300
|
+
this.deltaNetServer.addAuthenticatedConnection(this);
|
301
|
+
} else {
|
302
|
+
this.disconnectWithError(
|
303
|
+
new Error(result.error || "Authentication failed"),
|
304
|
+
DeltaNetV01ServerErrors.USER_NETWORKING_UNKNOWN_ERROR_TYPE,
|
305
|
+
false
|
306
|
+
);
|
307
|
+
}
|
308
|
+
}
|
309
|
+
this.isAuthenticating = false;
|
310
|
+
}
|
311
|
+
async handleStateUpdate(stateId, stateValue) {
|
312
|
+
if (!this.isAuthenticated) {
|
313
|
+
console.error("State update received before authentication completed");
|
314
|
+
return;
|
315
|
+
}
|
316
|
+
const existingValidation = this.pendingStateValidations.get(stateId);
|
317
|
+
if (existingValidation) {
|
318
|
+
existingValidation.abortController.abort();
|
319
|
+
this.pendingStateValidations.delete(stateId);
|
320
|
+
}
|
321
|
+
const result = this.deltaNetServer.validateAndApplyStateUpdate(
|
322
|
+
this,
|
323
|
+
this.internalConnectionId,
|
324
|
+
stateId,
|
325
|
+
stateValue
|
326
|
+
);
|
327
|
+
if (result instanceof Promise) {
|
328
|
+
const validationId = this.nextValidationId++;
|
329
|
+
const abortController = new AbortController();
|
330
|
+
this.pendingStateValidations.set(stateId, { validationId, abortController });
|
331
|
+
try {
|
332
|
+
const asyncResult = await result;
|
333
|
+
const currentValidation = this.pendingStateValidations.get(stateId);
|
334
|
+
if (!currentValidation || currentValidation.validationId !== validationId) {
|
335
|
+
return;
|
336
|
+
}
|
337
|
+
this.pendingStateValidations.delete(stateId);
|
338
|
+
if (asyncResult instanceof DeltaNetServerError) {
|
339
|
+
this.disconnectWithError(asyncResult, asyncResult.errorType, asyncResult.retryable);
|
340
|
+
return;
|
341
|
+
}
|
342
|
+
if (asyncResult instanceof Error) {
|
343
|
+
this.disconnectWithError(
|
344
|
+
asyncResult,
|
345
|
+
DeltaNetV01ServerErrors.USER_NETWORKING_UNKNOWN_ERROR_TYPE,
|
346
|
+
false
|
347
|
+
);
|
348
|
+
return;
|
349
|
+
}
|
350
|
+
return;
|
351
|
+
} catch (error) {
|
352
|
+
const currentValidation = this.pendingStateValidations.get(stateId);
|
353
|
+
if (currentValidation && currentValidation.validationId === validationId) {
|
354
|
+
this.pendingStateValidations.delete(stateId);
|
355
|
+
if (error instanceof DeltaNetServerError) {
|
356
|
+
this.disconnectWithError(error, error.errorType, error.retryable);
|
357
|
+
} else if (error instanceof Error) {
|
358
|
+
this.disconnectWithError(
|
359
|
+
error,
|
360
|
+
DeltaNetV01ServerErrors.USER_NETWORKING_UNKNOWN_ERROR_TYPE,
|
361
|
+
false
|
362
|
+
);
|
363
|
+
} else {
|
364
|
+
this.disconnectWithError(
|
365
|
+
new Error("State validation failed"),
|
366
|
+
DeltaNetV01ServerErrors.USER_NETWORKING_UNKNOWN_ERROR_TYPE,
|
367
|
+
false
|
368
|
+
);
|
369
|
+
}
|
370
|
+
}
|
371
|
+
return;
|
372
|
+
}
|
373
|
+
} else {
|
374
|
+
if (result instanceof DeltaNetServerError) {
|
375
|
+
this.disconnectWithError(result, result.errorType, result.retryable);
|
376
|
+
return;
|
377
|
+
}
|
378
|
+
if (result instanceof Error) {
|
379
|
+
this.disconnectWithError(
|
380
|
+
result,
|
381
|
+
DeltaNetV01ServerErrors.USER_NETWORKING_UNKNOWN_ERROR_TYPE,
|
382
|
+
false
|
383
|
+
);
|
384
|
+
return;
|
385
|
+
}
|
386
|
+
return;
|
387
|
+
}
|
388
|
+
}
|
389
|
+
handleClientMessage(parsed) {
|
390
|
+
switch (parsed.type) {
|
391
|
+
case "connectUser": {
|
392
|
+
if (this.deltaNetServer !== null) {
|
393
|
+
this.handleConnectUser(parsed.token, parsed.observer, parsed.components, parsed.states);
|
394
|
+
}
|
395
|
+
return;
|
396
|
+
}
|
397
|
+
case "pong":
|
398
|
+
return;
|
399
|
+
case "setUserComponents": {
|
400
|
+
if (!this.deltaNetServer) {
|
401
|
+
console.error("DeltaNetServer not set on connection that received event", this);
|
402
|
+
return;
|
403
|
+
}
|
404
|
+
if (!this.isAuthenticated) {
|
405
|
+
this.sendMessage({
|
406
|
+
type: "error",
|
407
|
+
errorType: "USER_NOT_AUTHENTICATED",
|
408
|
+
message: `Event sent, but user has not been authenticated yet.`,
|
409
|
+
retryable: false
|
410
|
+
});
|
411
|
+
console.error("Event sent, but user has not been authenticated yet.");
|
412
|
+
this.webSocket.close(1e3, "User has not been authenticated yet");
|
413
|
+
return;
|
414
|
+
}
|
415
|
+
const result = this.deltaNetServer.setUserComponents(
|
416
|
+
this,
|
417
|
+
this.internalConnectionId,
|
418
|
+
parsed.components
|
419
|
+
);
|
420
|
+
if (!result.success) {
|
421
|
+
this.disconnectWithError(
|
422
|
+
new Error(result.error),
|
423
|
+
DeltaNetV01ServerErrors.USER_NETWORKING_UNKNOWN_ERROR_TYPE,
|
424
|
+
true
|
425
|
+
);
|
426
|
+
return;
|
427
|
+
}
|
428
|
+
for (const [stateId, stateValue] of parsed.states) {
|
429
|
+
this.handleStateUpdate(stateId, stateValue);
|
430
|
+
}
|
431
|
+
return;
|
432
|
+
}
|
433
|
+
case "clientCustom": {
|
434
|
+
if (!this.deltaNetServer) {
|
435
|
+
console.error("DeltaNetServer not set on connection that received custom message", this);
|
436
|
+
return;
|
437
|
+
}
|
438
|
+
if (!this.isAuthenticated) {
|
439
|
+
this.sendMessage({
|
440
|
+
type: "error",
|
441
|
+
errorType: "USER_NOT_AUTHENTICATED",
|
442
|
+
message: `Custom message sent, but user has not been authenticated yet.`,
|
443
|
+
retryable: false
|
444
|
+
});
|
445
|
+
console.error("Custom message sent, but user has not been authenticated yet.");
|
446
|
+
this.webSocket.close(1e3, "User has not been authenticated yet");
|
447
|
+
return;
|
448
|
+
}
|
449
|
+
this.deltaNetServer.handleCustomMessage(
|
450
|
+
this,
|
451
|
+
this.internalConnectionId,
|
452
|
+
parsed.customType,
|
453
|
+
parsed.contents
|
454
|
+
);
|
455
|
+
return;
|
456
|
+
}
|
457
|
+
default:
|
458
|
+
console.error("Unknown message type from client", parsed);
|
459
|
+
}
|
460
|
+
}
|
461
|
+
};
|
462
|
+
|
463
|
+
// src/createDeltaNetServerConnectionForWebsocket.ts
|
464
|
+
var SupportedWebsocketSubProtocolsPreferenceOrder = [
|
465
|
+
deltaNetProtocolSubProtocol_v0_1
|
466
|
+
];
|
467
|
+
function IsRecognizedWebsocketSubProtocol(protocol) {
|
468
|
+
return SupportedWebsocketSubProtocolsPreferenceOrder.includes(protocol);
|
469
|
+
}
|
470
|
+
function createDeltaNetServerConnectionForWebsocket(webSocket, deltaNetServer) {
|
471
|
+
if (!webSocket.protocol || !IsRecognizedWebsocketSubProtocol(webSocket.protocol)) {
|
472
|
+
const errorMessageString = `Unsupported websocket subprotocol: ${webSocket.protocol}`;
|
473
|
+
const errorMessage = [
|
474
|
+
{
|
475
|
+
type: "error",
|
476
|
+
errorType: "UNSUPPORTED_WEBSOCKET_SUBPROTOCOL",
|
477
|
+
message: errorMessageString,
|
478
|
+
retryable: false
|
479
|
+
}
|
480
|
+
];
|
481
|
+
webSocket.send(JSON.stringify(errorMessage));
|
482
|
+
webSocket.close();
|
483
|
+
return null;
|
484
|
+
}
|
485
|
+
return new DeltaNetV01Connection(webSocket, deltaNetServer);
|
486
|
+
}
|
487
|
+
|
488
|
+
// src/StateCollection.ts
|
489
|
+
var emptyUint8Array = new Uint8Array(0);
|
490
|
+
var StateCollection = class {
|
491
|
+
values;
|
492
|
+
modifiedIndices = /* @__PURE__ */ new Set();
|
493
|
+
constructor() {
|
494
|
+
this.values = new Array();
|
495
|
+
}
|
496
|
+
setValue(index, value) {
|
497
|
+
if (value === null) {
|
498
|
+
value = emptyUint8Array;
|
499
|
+
}
|
500
|
+
this.modifiedIndices.add(index);
|
501
|
+
this.values[index] = value;
|
502
|
+
}
|
503
|
+
tick() {
|
504
|
+
const states = [];
|
505
|
+
for (const index of this.modifiedIndices) {
|
506
|
+
const value = this.values[index];
|
507
|
+
if (value === null) {
|
508
|
+
states.push([index, new Uint8Array(0)]);
|
509
|
+
} else {
|
510
|
+
states.push([index, value]);
|
511
|
+
}
|
512
|
+
}
|
513
|
+
this.modifiedIndices.clear();
|
514
|
+
return states;
|
515
|
+
}
|
516
|
+
removeIndices(sortedUnoccupyingIndices) {
|
517
|
+
if (sortedUnoccupyingIndices.length === 0) {
|
518
|
+
return;
|
519
|
+
}
|
520
|
+
let writeIndex = 0;
|
521
|
+
let skipIndex = 0;
|
522
|
+
for (let readIndex = 0; readIndex < this.values.length; readIndex++) {
|
523
|
+
if (skipIndex < sortedUnoccupyingIndices.length && readIndex === sortedUnoccupyingIndices[skipIndex]) {
|
524
|
+
this.modifiedIndices.delete(readIndex);
|
525
|
+
skipIndex++;
|
526
|
+
continue;
|
527
|
+
}
|
528
|
+
if (writeIndex !== readIndex && this.modifiedIndices.has(readIndex)) {
|
529
|
+
this.modifiedIndices.delete(readIndex);
|
530
|
+
this.modifiedIndices.add(writeIndex);
|
531
|
+
}
|
532
|
+
if (writeIndex !== readIndex) {
|
533
|
+
this.values[writeIndex] = this.values[readIndex];
|
534
|
+
}
|
535
|
+
writeIndex++;
|
536
|
+
}
|
537
|
+
for (let i = writeIndex; i < this.values.length; i++) {
|
538
|
+
this.values[i] = emptyUint8Array;
|
539
|
+
this.modifiedIndices.delete(i);
|
540
|
+
}
|
541
|
+
}
|
542
|
+
};
|
543
|
+
|
544
|
+
// src/DeltaNetServer.ts
|
545
|
+
var DeltaNetServerError = class extends Error {
|
546
|
+
constructor(errorType, message, retryable) {
|
547
|
+
super(message);
|
548
|
+
this.errorType = errorType;
|
549
|
+
this.retryable = retryable;
|
550
|
+
}
|
551
|
+
};
|
552
|
+
var DeltaNetServer2 = class {
|
553
|
+
constructor(opts = {}) {
|
554
|
+
this.opts = opts;
|
555
|
+
if (opts.serverConnectionIdStateId !== void 0) {
|
556
|
+
this.states.set(opts.serverConnectionIdStateId, new StateCollection());
|
557
|
+
}
|
558
|
+
this.maxStateValueSize = opts.maxStateValueSize ?? 1024 * 1024;
|
559
|
+
this.maxMessageSize = opts.maxMessageSize ?? 10 * 1024 * 1024;
|
560
|
+
}
|
561
|
+
currentConnectionId = 1;
|
562
|
+
nextIndex = 0;
|
563
|
+
preTickData = {
|
564
|
+
// This is state that will be flushed to clients in the next tick, but messages handled before the tick could change it
|
565
|
+
unoccupyingIndices: /* @__PURE__ */ new Set(),
|
566
|
+
newJoinerConnections: /* @__PURE__ */ new Set(),
|
567
|
+
// Allows for arbitrary processes to be run that can imitate a new joiner connection (currently used for legacy adapter)
|
568
|
+
newJoinerCallbacks: /* @__PURE__ */ new Set(),
|
569
|
+
componentsUpdated: 0
|
570
|
+
};
|
571
|
+
connectionIdToComponentIndex = /* @__PURE__ */ new Map();
|
572
|
+
componentIndexToConnectionId = /* @__PURE__ */ new Map();
|
573
|
+
connectionIdToDeltaNetServerConnection = /* @__PURE__ */ new Map();
|
574
|
+
allDeltaNetV01Connections = /* @__PURE__ */ new Set();
|
575
|
+
authenticatedDeltaNetV01Connections = /* @__PURE__ */ new Set();
|
576
|
+
observerConnections = /* @__PURE__ */ new Set();
|
577
|
+
// Track observer connections separately
|
578
|
+
webSocketToDeltaNetServerConnection = /* @__PURE__ */ new Map();
|
579
|
+
components = /* @__PURE__ */ new Map();
|
580
|
+
states = /* @__PURE__ */ new Map();
|
581
|
+
documentEffectiveStartTime = Date.now();
|
582
|
+
pingCounter = 1;
|
583
|
+
disposed = false;
|
584
|
+
maxStateValueSize;
|
585
|
+
maxMessageSize;
|
586
|
+
static handleWebsocketSubprotocol(protocols) {
|
587
|
+
const protocolsSet = new Set(protocols);
|
588
|
+
for (const protocol of SupportedWebsocketSubProtocolsPreferenceOrder) {
|
589
|
+
if (protocolsSet.has(protocol)) {
|
590
|
+
return protocol;
|
591
|
+
}
|
592
|
+
}
|
593
|
+
return false;
|
594
|
+
}
|
595
|
+
addWebSocket(webSocket) {
|
596
|
+
if (this.disposed) {
|
597
|
+
throw new Error("This DeltaNetServer has been disposed");
|
598
|
+
}
|
599
|
+
const deltaNetV01Connection = createDeltaNetServerConnectionForWebsocket(webSocket, this);
|
600
|
+
if (deltaNetV01Connection === null) {
|
601
|
+
return;
|
602
|
+
}
|
603
|
+
this.allDeltaNetV01Connections.add(deltaNetV01Connection);
|
604
|
+
this.webSocketToDeltaNetServerConnection.set(
|
605
|
+
deltaNetV01Connection.webSocket,
|
606
|
+
deltaNetV01Connection
|
607
|
+
);
|
608
|
+
}
|
609
|
+
removeWebSocket(webSocket) {
|
610
|
+
const deltaNetV01Connection = this.webSocketToDeltaNetServerConnection.get(webSocket);
|
611
|
+
if (deltaNetV01Connection === void 0) {
|
612
|
+
return;
|
613
|
+
}
|
614
|
+
if (!this.allDeltaNetV01Connections.has(deltaNetV01Connection)) {
|
615
|
+
return;
|
616
|
+
}
|
617
|
+
deltaNetV01Connection.dispose();
|
618
|
+
if (!this.disposed && this.opts.onLeave) {
|
619
|
+
const internalConnectionId2 = deltaNetV01Connection.internalConnectionId;
|
620
|
+
const index = this.connectionIdToComponentIndex.get(internalConnectionId2);
|
621
|
+
if (index !== void 0) {
|
622
|
+
const components = [];
|
623
|
+
for (const [componentId, collection] of this.components) {
|
624
|
+
const value = collection.getTargetValue(index);
|
625
|
+
if (value !== 0n) {
|
626
|
+
components.push([componentId, Number(value)]);
|
627
|
+
}
|
628
|
+
}
|
629
|
+
const states = [];
|
630
|
+
for (const [stateId, collection] of this.states) {
|
631
|
+
const value = collection.values[index];
|
632
|
+
if (value !== void 0 && value.length > 0) {
|
633
|
+
states.push([stateId, value]);
|
634
|
+
}
|
635
|
+
}
|
636
|
+
try {
|
637
|
+
this.opts.onLeave({
|
638
|
+
deltaNetV01Connection,
|
639
|
+
internalConnectionId: internalConnectionId2,
|
640
|
+
components,
|
641
|
+
states
|
642
|
+
});
|
643
|
+
} catch (error) {
|
644
|
+
console.warn("Error in onLeave callback:", error);
|
645
|
+
}
|
646
|
+
}
|
647
|
+
}
|
648
|
+
const internalConnectionId = deltaNetV01Connection.internalConnectionId;
|
649
|
+
this.connectionIdToDeltaNetServerConnection.delete(internalConnectionId);
|
650
|
+
if (this.preTickData.newJoinerConnections.has(deltaNetV01Connection)) {
|
651
|
+
this.preTickData.newJoinerConnections.delete(deltaNetV01Connection);
|
652
|
+
} else {
|
653
|
+
const index = this.connectionIdToComponentIndex.get(internalConnectionId);
|
654
|
+
if (index !== void 0) {
|
655
|
+
this.clearInternalConnectionId(internalConnectionId);
|
656
|
+
}
|
657
|
+
}
|
658
|
+
this.authenticatedDeltaNetV01Connections.delete(deltaNetV01Connection);
|
659
|
+
this.observerConnections.delete(deltaNetV01Connection);
|
660
|
+
this.allDeltaNetV01Connections.delete(deltaNetV01Connection);
|
661
|
+
this.webSocketToDeltaNetServerConnection.delete(deltaNetV01Connection.webSocket);
|
662
|
+
}
|
663
|
+
hasWebSocket(webSocket) {
|
664
|
+
return this.webSocketToDeltaNetServerConnection.has(webSocket);
|
665
|
+
}
|
666
|
+
dangerouslyGetConnectionsToComponentIndex() {
|
667
|
+
return this.connectionIdToComponentIndex;
|
668
|
+
}
|
669
|
+
dangerouslyAddNewJoinerCallback(callback) {
|
670
|
+
this.preTickData.newJoinerCallbacks.add(callback);
|
671
|
+
}
|
672
|
+
disconnectWithError(deltaNetV01Connection, error, errorType, retryable = true) {
|
673
|
+
try {
|
674
|
+
deltaNetV01Connection.sendMessage({
|
675
|
+
type: "error",
|
676
|
+
errorType,
|
677
|
+
message: error.message,
|
678
|
+
retryable
|
679
|
+
});
|
680
|
+
} catch (sendError) {
|
681
|
+
console.warn("Failed to send error message to client:", sendError);
|
682
|
+
}
|
683
|
+
try {
|
684
|
+
deltaNetV01Connection.webSocket.close(1008, error.message);
|
685
|
+
} catch (closeError) {
|
686
|
+
console.warn("Failed to close websocket connection:", closeError);
|
687
|
+
}
|
688
|
+
try {
|
689
|
+
this.removeWebSocket(deltaNetV01Connection.webSocket);
|
690
|
+
} catch (cleanupError) {
|
691
|
+
console.warn("Failed to clean up connection state:", cleanupError);
|
692
|
+
}
|
693
|
+
}
|
694
|
+
getComponentValue(componentId, componentIndex) {
|
695
|
+
return Number(this.components.get(componentId).getTargetValue(componentIndex));
|
696
|
+
}
|
697
|
+
getNextConnectionId() {
|
698
|
+
return this.currentConnectionId++;
|
699
|
+
}
|
700
|
+
getMaxMessageSize() {
|
701
|
+
return this.maxMessageSize;
|
702
|
+
}
|
703
|
+
validateJoiner(deltaNetV01Connection, token, components, states) {
|
704
|
+
if (this.disposed) {
|
705
|
+
return { success: false, error: "This DeltaNetServer has been disposed" };
|
706
|
+
}
|
707
|
+
for (const [stateId, stateValue] of states) {
|
708
|
+
if (stateValue.length > this.maxStateValueSize) {
|
709
|
+
return {
|
710
|
+
success: false,
|
711
|
+
error: `State value for state ${stateId} has size ${stateValue.length} bytes which exceeds maximum allowed size of ${this.maxStateValueSize} bytes`
|
712
|
+
};
|
713
|
+
}
|
714
|
+
}
|
715
|
+
function resultToReturn(result) {
|
716
|
+
if (result instanceof DeltaNetServerError) {
|
717
|
+
return { success: false, error: result.message };
|
718
|
+
}
|
719
|
+
if (result instanceof Error) {
|
720
|
+
return { success: false, error: result.message };
|
721
|
+
}
|
722
|
+
if (result === true || result === void 0) {
|
723
|
+
return { success: true };
|
724
|
+
}
|
725
|
+
if (typeof result === "object" && result.success === true) {
|
726
|
+
return result;
|
727
|
+
}
|
728
|
+
return { success: false, error: "Joiner validation failed" };
|
729
|
+
}
|
730
|
+
if (this.opts.onJoiner) {
|
731
|
+
const rawResult = this.opts.onJoiner({
|
732
|
+
deltaNetV01Connection,
|
733
|
+
components,
|
734
|
+
states,
|
735
|
+
token,
|
736
|
+
internalConnectionId: deltaNetV01Connection.internalConnectionId
|
737
|
+
});
|
738
|
+
if (rawResult instanceof Promise) {
|
739
|
+
return rawResult.then((resolvedResult) => {
|
740
|
+
return resultToReturn(resolvedResult);
|
741
|
+
}).catch((error) => {
|
742
|
+
console.warn("Error in async onJoiner callback:", error);
|
743
|
+
return resultToReturn(error);
|
744
|
+
});
|
745
|
+
} else {
|
746
|
+
return resultToReturn(rawResult);
|
747
|
+
}
|
748
|
+
}
|
749
|
+
return { success: true };
|
750
|
+
}
|
751
|
+
addAuthenticatedConnection(deltaNetV01Connection) {
|
752
|
+
if (deltaNetV01Connection.internalConnectionId === null) {
|
753
|
+
throw new Error("Connection ID must be set before adding to authenticated connections");
|
754
|
+
}
|
755
|
+
this.connectionIdToDeltaNetServerConnection.set(
|
756
|
+
deltaNetV01Connection.internalConnectionId,
|
757
|
+
deltaNetV01Connection
|
758
|
+
);
|
759
|
+
if (deltaNetV01Connection.isObserver) {
|
760
|
+
this.observerConnections.add(deltaNetV01Connection);
|
761
|
+
this.preTickData.newJoinerConnections.add(deltaNetV01Connection);
|
762
|
+
} else {
|
763
|
+
this.preTickData.newJoinerConnections.add(deltaNetV01Connection);
|
764
|
+
}
|
765
|
+
}
|
766
|
+
validateAndApplyStateUpdate(deltaNetV01Connection, internalConnectionId, stateId, stateValue) {
|
767
|
+
if (this.disposed) {
|
768
|
+
return new Error("This DeltaNetServer has been disposed");
|
769
|
+
}
|
770
|
+
if (stateValue.length > this.maxStateValueSize) {
|
771
|
+
return new DeltaNetServerError(
|
772
|
+
"USER_NETWORKING_UNKNOWN_ERROR",
|
773
|
+
`State value for state ${stateId} has size ${stateValue.length} bytes which exceeds maximum allowed size of ${this.maxStateValueSize} bytes`,
|
774
|
+
false
|
775
|
+
);
|
776
|
+
}
|
777
|
+
if (deltaNetV01Connection.isObserver) {
|
778
|
+
return new DeltaNetServerError(
|
779
|
+
"OBSERVER_CANNOT_SEND_STATE_UPDATES",
|
780
|
+
"Observers cannot send state updates",
|
781
|
+
false
|
782
|
+
);
|
783
|
+
}
|
784
|
+
if (this.opts.onStatesUpdate) {
|
785
|
+
try {
|
786
|
+
const result = this.opts.onStatesUpdate({
|
787
|
+
deltaNetV01Connection,
|
788
|
+
internalConnectionId,
|
789
|
+
states: [[stateId, stateValue]]
|
790
|
+
});
|
791
|
+
if (result instanceof Promise) {
|
792
|
+
return result.then((asyncResult) => {
|
793
|
+
if (!this.connectionIdToDeltaNetServerConnection.has(internalConnectionId)) {
|
794
|
+
return;
|
795
|
+
}
|
796
|
+
if (asyncResult instanceof DeltaNetServerError || asyncResult instanceof Error) {
|
797
|
+
return asyncResult;
|
798
|
+
}
|
799
|
+
if (asyncResult === true || asyncResult === void 0) {
|
800
|
+
this.applyStateUpdates(deltaNetV01Connection, internalConnectionId, [
|
801
|
+
[stateId, stateValue]
|
802
|
+
]);
|
803
|
+
return true;
|
804
|
+
}
|
805
|
+
if (asyncResult.success) {
|
806
|
+
if (asyncResult.stateOverrides) {
|
807
|
+
this.applyStateUpdates(
|
808
|
+
deltaNetV01Connection,
|
809
|
+
internalConnectionId,
|
810
|
+
asyncResult.stateOverrides
|
811
|
+
);
|
812
|
+
}
|
813
|
+
return true;
|
814
|
+
} else {
|
815
|
+
return new DeltaNetServerError(
|
816
|
+
"USER_NETWORKING_UNKNOWN_ERROR",
|
817
|
+
"State validation failed",
|
818
|
+
false
|
819
|
+
);
|
820
|
+
}
|
821
|
+
}).catch((error) => {
|
822
|
+
console.warn("Error in async onStatesUpdate callback:", error);
|
823
|
+
if (error instanceof DeltaNetServerError) {
|
824
|
+
return error;
|
825
|
+
}
|
826
|
+
if (error instanceof Error) {
|
827
|
+
return error;
|
828
|
+
}
|
829
|
+
return new Error("State validation failed");
|
830
|
+
});
|
831
|
+
} else {
|
832
|
+
if (result instanceof DeltaNetServerError || result instanceof Error) {
|
833
|
+
return result;
|
834
|
+
}
|
835
|
+
if (result === true || result === void 0) {
|
836
|
+
this.applyStateUpdates(deltaNetV01Connection, internalConnectionId, [
|
837
|
+
[stateId, stateValue]
|
838
|
+
]);
|
839
|
+
return true;
|
840
|
+
}
|
841
|
+
if (result.success) {
|
842
|
+
if (result.stateOverrides) {
|
843
|
+
this.applyStateUpdates(
|
844
|
+
deltaNetV01Connection,
|
845
|
+
internalConnectionId,
|
846
|
+
result.stateOverrides
|
847
|
+
);
|
848
|
+
}
|
849
|
+
return true;
|
850
|
+
} else {
|
851
|
+
return new DeltaNetServerError(
|
852
|
+
"USER_NETWORKING_UNKNOWN_ERROR",
|
853
|
+
"State validation failed",
|
854
|
+
false
|
855
|
+
);
|
856
|
+
}
|
857
|
+
}
|
858
|
+
} catch (error) {
|
859
|
+
console.warn("Error in onStatesUpdate callback:", error);
|
860
|
+
if (error instanceof DeltaNetServerError) {
|
861
|
+
return error;
|
862
|
+
}
|
863
|
+
if (error instanceof Error) {
|
864
|
+
return error;
|
865
|
+
}
|
866
|
+
return new Error("State validation failed");
|
867
|
+
}
|
868
|
+
} else {
|
869
|
+
this.applyStateUpdates(deltaNetV01Connection, internalConnectionId, [[stateId, stateValue]]);
|
870
|
+
return true;
|
871
|
+
}
|
872
|
+
}
|
873
|
+
clearInternalConnectionId(internalConnectionId) {
|
874
|
+
const index = this.connectionIdToComponentIndex.get(internalConnectionId);
|
875
|
+
if (index === void 0) {
|
876
|
+
throw new Error("Index for removing user is undefined");
|
877
|
+
}
|
878
|
+
for (const [, collection] of this.components) {
|
879
|
+
collection.setValue(index, 0n);
|
880
|
+
}
|
881
|
+
for (const [, collection] of this.states) {
|
882
|
+
collection.setValue(index, null);
|
883
|
+
}
|
884
|
+
this.preTickData.unoccupyingIndices.add(index);
|
885
|
+
}
|
886
|
+
sendPings() {
|
887
|
+
const ping = this.pingCounter++;
|
888
|
+
if (this.pingCounter > 1e3) {
|
889
|
+
this.pingCounter = 1;
|
890
|
+
}
|
891
|
+
const v01PingMessage = {
|
892
|
+
type: "ping",
|
893
|
+
ping
|
894
|
+
};
|
895
|
+
const writer = new BufferWriter(8);
|
896
|
+
encodePing(v01PingMessage, writer);
|
897
|
+
const v01Encoded = writer.getBuffer();
|
898
|
+
this.allDeltaNetV01Connections.forEach((deltaNetV01Connection) => {
|
899
|
+
deltaNetV01Connection.sendEncodedBytes(v01Encoded);
|
900
|
+
});
|
901
|
+
}
|
902
|
+
tick() {
|
903
|
+
if (this.disposed) {
|
904
|
+
return { removedIds: /* @__PURE__ */ new Set(), addedIds: /* @__PURE__ */ new Set() };
|
905
|
+
}
|
906
|
+
this.preTickData.componentsUpdated = 0;
|
907
|
+
const removedIds = /* @__PURE__ */ new Set();
|
908
|
+
const addedIds = /* @__PURE__ */ new Set();
|
909
|
+
const sortedUnoccupyingIndices = Array.from(this.preTickData.unoccupyingIndices);
|
910
|
+
sortedUnoccupyingIndices.sort((a, b) => a - b);
|
911
|
+
for (const componentCollection of this.components.values()) {
|
912
|
+
componentCollection.removeIndices(sortedUnoccupyingIndices);
|
913
|
+
}
|
914
|
+
for (const stateCollection of this.states.values()) {
|
915
|
+
stateCollection.removeIndices(sortedUnoccupyingIndices);
|
916
|
+
}
|
917
|
+
for (const index of sortedUnoccupyingIndices) {
|
918
|
+
const connectionId = this.componentIndexToConnectionId.get(index);
|
919
|
+
if (connectionId === void 0) {
|
920
|
+
throw new Error("Connection id not found for index " + index);
|
921
|
+
}
|
922
|
+
removedIds.add(connectionId);
|
923
|
+
this.connectionIdToComponentIndex.delete(connectionId);
|
924
|
+
this.componentIndexToConnectionId.delete(index);
|
925
|
+
}
|
926
|
+
let writeIndex = 0;
|
927
|
+
let skipIndex = 0;
|
928
|
+
for (let i = 0; i < this.nextIndex; i++) {
|
929
|
+
const connectionId = this.componentIndexToConnectionId.get(i);
|
930
|
+
if (skipIndex < sortedUnoccupyingIndices.length && i === sortedUnoccupyingIndices[skipIndex]) {
|
931
|
+
skipIndex++;
|
932
|
+
continue;
|
933
|
+
}
|
934
|
+
if (writeIndex !== i) {
|
935
|
+
if (connectionId === void 0) {
|
936
|
+
throw new Error("Connection id not found for index " + i);
|
937
|
+
}
|
938
|
+
this.componentIndexToConnectionId.set(writeIndex, connectionId);
|
939
|
+
this.connectionIdToComponentIndex.set(connectionId, writeIndex);
|
940
|
+
}
|
941
|
+
writeIndex++;
|
942
|
+
}
|
943
|
+
this.nextIndex -= sortedUnoccupyingIndices.length;
|
944
|
+
for (const deltaNetV01Connection of this.preTickData.newJoinerConnections) {
|
945
|
+
const internalConnectionId = deltaNetV01Connection.internalConnectionId;
|
946
|
+
if (deltaNetV01Connection.isObserver) {
|
947
|
+
} else {
|
948
|
+
const index = this.nextIndex++;
|
949
|
+
this.connectionIdToComponentIndex.set(internalConnectionId, index);
|
950
|
+
this.componentIndexToConnectionId.set(index, internalConnectionId);
|
951
|
+
addedIds.add(internalConnectionId);
|
952
|
+
for (const [componentId] of deltaNetV01Connection.components) {
|
953
|
+
if (!this.components.has(componentId)) {
|
954
|
+
this.components.set(componentId, new ComponentCollection());
|
955
|
+
}
|
956
|
+
}
|
957
|
+
for (const [stateId] of deltaNetV01Connection.states) {
|
958
|
+
if (!this.states.has(stateId)) {
|
959
|
+
this.states.set(stateId, new StateCollection());
|
960
|
+
}
|
961
|
+
}
|
962
|
+
for (const [componentId, collection] of this.components) {
|
963
|
+
const value = deltaNetV01Connection.components.get(componentId);
|
964
|
+
if (value === void 0) {
|
965
|
+
collection.setValue(index, 0n);
|
966
|
+
} else {
|
967
|
+
collection.setValue(index, BigInt(value));
|
968
|
+
}
|
969
|
+
}
|
970
|
+
for (const [stateId, collection] of this.states) {
|
971
|
+
const value = deltaNetV01Connection.states.get(stateId);
|
972
|
+
if (this.opts.serverConnectionIdStateId !== void 0 && stateId === this.opts.serverConnectionIdStateId) {
|
973
|
+
const writer2 = new BufferWriter(8);
|
974
|
+
writer2.writeUVarint(internalConnectionId);
|
975
|
+
const buffer = writer2.getBuffer();
|
976
|
+
collection.setValue(index, buffer);
|
977
|
+
} else {
|
978
|
+
if (value === void 0) {
|
979
|
+
collection.setValue(index, null);
|
980
|
+
} else {
|
981
|
+
collection.setValue(index, value);
|
982
|
+
}
|
983
|
+
}
|
984
|
+
}
|
985
|
+
deltaNetV01Connection.sendMessage({
|
986
|
+
type: "userIndex",
|
987
|
+
index
|
988
|
+
});
|
989
|
+
}
|
990
|
+
}
|
991
|
+
for (const callback of this.preTickData.newJoinerCallbacks) {
|
992
|
+
const index = this.nextIndex++;
|
993
|
+
const result = callback(index);
|
994
|
+
if (result === null) {
|
995
|
+
this.nextIndex--;
|
996
|
+
} else {
|
997
|
+
const { id, afterAddCallback } = result;
|
998
|
+
this.connectionIdToComponentIndex.set(id, index);
|
999
|
+
this.componentIndexToConnectionId.set(index, id);
|
1000
|
+
addedIds.add(id);
|
1001
|
+
if (this.opts.serverConnectionIdStateId !== void 0) {
|
1002
|
+
const writer2 = new BufferWriter(8);
|
1003
|
+
writer2.writeUVarint(id);
|
1004
|
+
const buffer = writer2.getBuffer();
|
1005
|
+
this.setUserState(index, this.opts.serverConnectionIdStateId, buffer);
|
1006
|
+
}
|
1007
|
+
if (afterAddCallback) {
|
1008
|
+
afterAddCallback();
|
1009
|
+
}
|
1010
|
+
}
|
1011
|
+
}
|
1012
|
+
const componentDeltas = [];
|
1013
|
+
for (const [componentId, collection] of this.components) {
|
1014
|
+
const { deltaDeltas } = collection.tick();
|
1015
|
+
componentDeltas.push({ componentId, deltaDeltas });
|
1016
|
+
}
|
1017
|
+
const stateDeltas = [];
|
1018
|
+
for (const [stateId, collection] of this.states) {
|
1019
|
+
const updatedStates = collection.tick();
|
1020
|
+
if (updatedStates.length === 0) {
|
1021
|
+
continue;
|
1022
|
+
}
|
1023
|
+
stateDeltas.push({
|
1024
|
+
stateId,
|
1025
|
+
updatedStates
|
1026
|
+
});
|
1027
|
+
}
|
1028
|
+
const tickMessage = {
|
1029
|
+
type: "tick",
|
1030
|
+
serverTime: this.getServerTime(),
|
1031
|
+
removedIndices: sortedUnoccupyingIndices,
|
1032
|
+
indicesCount: this.nextIndex,
|
1033
|
+
componentDeltaDeltas: componentDeltas,
|
1034
|
+
states: stateDeltas
|
1035
|
+
};
|
1036
|
+
const writer = new BufferWriter(this.nextIndex * this.components.size + 128);
|
1037
|
+
encodeTick(tickMessage, writer);
|
1038
|
+
const v01EncodedComponentsChanged = writer.getBuffer();
|
1039
|
+
this.authenticatedDeltaNetV01Connections.forEach((deltaNetV01Connection) => {
|
1040
|
+
deltaNetV01Connection.sendEncodedBytes(v01EncodedComponentsChanged);
|
1041
|
+
});
|
1042
|
+
if (this.preTickData.newJoinerConnections.size > 0) {
|
1043
|
+
const components = [];
|
1044
|
+
for (const [componentId, collection] of this.components) {
|
1045
|
+
components.push({
|
1046
|
+
componentId,
|
1047
|
+
values: collection.getCurrentValuesArray(),
|
1048
|
+
deltas: collection.getPreviousEmittedDeltasArray()
|
1049
|
+
});
|
1050
|
+
}
|
1051
|
+
const states = [];
|
1052
|
+
for (const [stateId, collection] of this.states) {
|
1053
|
+
states.push({
|
1054
|
+
stateId,
|
1055
|
+
values: collection.values
|
1056
|
+
});
|
1057
|
+
}
|
1058
|
+
const initialCheckout = {
|
1059
|
+
type: "initialCheckout",
|
1060
|
+
components,
|
1061
|
+
states,
|
1062
|
+
indicesCount: this.nextIndex,
|
1063
|
+
serverTime: this.getServerTime()
|
1064
|
+
};
|
1065
|
+
const writer2 = new BufferWriter(this.nextIndex * this.components.size + 128);
|
1066
|
+
encodeInitialCheckout(initialCheckout, writer2);
|
1067
|
+
const v01EncodedInitialCheckout = writer2.getBuffer();
|
1068
|
+
for (const deltaNetV01Connection of this.preTickData.newJoinerConnections) {
|
1069
|
+
deltaNetV01Connection.sendEncodedBytes(v01EncodedInitialCheckout);
|
1070
|
+
}
|
1071
|
+
}
|
1072
|
+
for (const deltaNetV01Connection of this.preTickData.newJoinerConnections) {
|
1073
|
+
this.authenticatedDeltaNetV01Connections.add(deltaNetV01Connection);
|
1074
|
+
deltaNetV01Connection.setAuthenticated();
|
1075
|
+
}
|
1076
|
+
this.preTickData.unoccupyingIndices.clear();
|
1077
|
+
this.preTickData.newJoinerConnections.clear();
|
1078
|
+
this.preTickData.newJoinerCallbacks.clear();
|
1079
|
+
return {
|
1080
|
+
removedIds,
|
1081
|
+
addedIds
|
1082
|
+
};
|
1083
|
+
}
|
1084
|
+
getServerTime() {
|
1085
|
+
return Date.now() - this.documentEffectiveStartTime;
|
1086
|
+
}
|
1087
|
+
setUserComponents(deltaNetV01Connection, internalConnectionId, components) {
|
1088
|
+
if (this.disposed) {
|
1089
|
+
console.error("Cannot dispatch remote event after dispose");
|
1090
|
+
return { success: false, error: "This DeltaNetServer has been disposed" };
|
1091
|
+
}
|
1092
|
+
if (deltaNetV01Connection.isObserver) {
|
1093
|
+
return { success: false, error: "Observers cannot send component updates" };
|
1094
|
+
}
|
1095
|
+
if (this.opts.onComponentsUpdate) {
|
1096
|
+
try {
|
1097
|
+
const result = this.opts.onComponentsUpdate({
|
1098
|
+
deltaNetV01Connection,
|
1099
|
+
internalConnectionId,
|
1100
|
+
components
|
1101
|
+
});
|
1102
|
+
if (result instanceof DeltaNetServerError) {
|
1103
|
+
return { success: false, error: result.message };
|
1104
|
+
}
|
1105
|
+
if (result instanceof Error) {
|
1106
|
+
return { success: false, error: result.message };
|
1107
|
+
}
|
1108
|
+
} catch (error) {
|
1109
|
+
console.warn("Error in onComponentsUpdate callback:", error);
|
1110
|
+
if (error instanceof DeltaNetServerError) {
|
1111
|
+
return { success: false, error: error.message };
|
1112
|
+
}
|
1113
|
+
if (error instanceof Error) {
|
1114
|
+
return { success: false, error: error.message };
|
1115
|
+
}
|
1116
|
+
return { success: false, error: "Component update failed" };
|
1117
|
+
}
|
1118
|
+
}
|
1119
|
+
this.applyComponentUpdates(deltaNetV01Connection, internalConnectionId, components);
|
1120
|
+
return { success: true };
|
1121
|
+
}
|
1122
|
+
setComponentValue(componentId, index, value) {
|
1123
|
+
this.preTickData.componentsUpdated++;
|
1124
|
+
let collection = this.components.get(componentId);
|
1125
|
+
if (!collection) {
|
1126
|
+
collection = new ComponentCollection();
|
1127
|
+
this.components.set(componentId, collection);
|
1128
|
+
}
|
1129
|
+
collection.setValue(index, value);
|
1130
|
+
}
|
1131
|
+
applyComponentUpdates(deltaNetV01Connection, internalConnectionId, components) {
|
1132
|
+
if (this.preTickData.newJoinerConnections.has(deltaNetV01Connection)) {
|
1133
|
+
for (const [componentId, componentValue] of components) {
|
1134
|
+
deltaNetV01Connection.components.set(componentId, componentValue);
|
1135
|
+
}
|
1136
|
+
return;
|
1137
|
+
}
|
1138
|
+
const index = this.connectionIdToComponentIndex.get(internalConnectionId);
|
1139
|
+
if (index === void 0) {
|
1140
|
+
return;
|
1141
|
+
}
|
1142
|
+
for (const [componentId, componentValue] of components) {
|
1143
|
+
this.setComponentValue(componentId, index, componentValue);
|
1144
|
+
}
|
1145
|
+
}
|
1146
|
+
overrideUserStates(deltaNetV01Connection, internalConnectionId, states) {
|
1147
|
+
this.applyStateUpdates(deltaNetV01Connection, internalConnectionId, states);
|
1148
|
+
}
|
1149
|
+
setUserState(index, stateId, stateValue) {
|
1150
|
+
let collection = this.states.get(stateId);
|
1151
|
+
if (!collection) {
|
1152
|
+
collection = new StateCollection();
|
1153
|
+
this.states.set(stateId, collection);
|
1154
|
+
}
|
1155
|
+
collection.setValue(index, stateValue);
|
1156
|
+
}
|
1157
|
+
applyStateUpdates(deltaNetV01Connection, internalConnectionId, states) {
|
1158
|
+
if (deltaNetV01Connection !== null && this.preTickData.newJoinerConnections.has(deltaNetV01Connection)) {
|
1159
|
+
for (const [stateId, stateValue] of states) {
|
1160
|
+
deltaNetV01Connection.states.set(stateId, stateValue);
|
1161
|
+
}
|
1162
|
+
return;
|
1163
|
+
}
|
1164
|
+
const index = this.connectionIdToComponentIndex.get(internalConnectionId);
|
1165
|
+
if (index === void 0) {
|
1166
|
+
return;
|
1167
|
+
}
|
1168
|
+
for (const [stateId, stateValue] of states) {
|
1169
|
+
this.setUserState(index, stateId, stateValue);
|
1170
|
+
}
|
1171
|
+
}
|
1172
|
+
handleCustomMessage(deltaNetV01Connection, internalConnectionId, customType, contents) {
|
1173
|
+
if (this.disposed) {
|
1174
|
+
return;
|
1175
|
+
}
|
1176
|
+
if (this.opts.onCustomMessage) {
|
1177
|
+
try {
|
1178
|
+
this.opts.onCustomMessage({
|
1179
|
+
deltaNetV01Connection,
|
1180
|
+
internalConnectionId,
|
1181
|
+
customType,
|
1182
|
+
contents
|
1183
|
+
});
|
1184
|
+
} catch (error) {
|
1185
|
+
console.warn("Error in onCustomMessage callback:", error);
|
1186
|
+
}
|
1187
|
+
}
|
1188
|
+
}
|
1189
|
+
broadcastCustomMessage(customType, contents) {
|
1190
|
+
console.log("Broadcasting custom message", customType, contents);
|
1191
|
+
if (this.disposed) {
|
1192
|
+
return;
|
1193
|
+
}
|
1194
|
+
const writer = new BufferWriter(contents.length + 16);
|
1195
|
+
const message = {
|
1196
|
+
type: "serverCustom",
|
1197
|
+
customType,
|
1198
|
+
contents
|
1199
|
+
};
|
1200
|
+
const encodedMessage = encodeServerMessage2(message, writer);
|
1201
|
+
const messageBytes = encodedMessage.getBuffer();
|
1202
|
+
this.authenticatedDeltaNetV01Connections.forEach((connection) => {
|
1203
|
+
try {
|
1204
|
+
connection.sendEncodedBytes(messageBytes);
|
1205
|
+
} catch (error) {
|
1206
|
+
console.warn("Failed to send custom message to connection:", error);
|
1207
|
+
}
|
1208
|
+
});
|
1209
|
+
}
|
1210
|
+
dispose() {
|
1211
|
+
this.disposed = true;
|
1212
|
+
const connectionsToClose = Array.from(this.allDeltaNetV01Connections);
|
1213
|
+
for (const connection of connectionsToClose) {
|
1214
|
+
try {
|
1215
|
+
connection.webSocket.close(1001, "Server shutting down");
|
1216
|
+
} catch (error) {
|
1217
|
+
console.warn("Failed to close connection during disposal:", error);
|
1218
|
+
}
|
1219
|
+
}
|
1220
|
+
this.allDeltaNetV01Connections.clear();
|
1221
|
+
this.authenticatedDeltaNetV01Connections.clear();
|
1222
|
+
this.observerConnections.clear();
|
1223
|
+
this.webSocketToDeltaNetServerConnection.clear();
|
1224
|
+
this.connectionIdToDeltaNetServerConnection.clear();
|
1225
|
+
this.connectionIdToComponentIndex.clear();
|
1226
|
+
this.componentIndexToConnectionId.clear();
|
1227
|
+
this.components.clear();
|
1228
|
+
this.states.clear();
|
1229
|
+
this.preTickData.newJoinerConnections.clear();
|
1230
|
+
this.preTickData.unoccupyingIndices.clear();
|
1231
|
+
}
|
1232
|
+
};
|
1233
|
+
export {
|
1234
|
+
DeltaNetServer2 as DeltaNetServer,
|
1235
|
+
DeltaNetServerError,
|
1236
|
+
DeltaNetV01Connection
|
1237
|
+
};
|
1238
|
+
//# sourceMappingURL=index.js.map
|