@series-inc/rundot-syncplay 5.23.0-beta.7
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 +610 -0
- package/dist/animation.d.ts +119 -0
- package/dist/animation.js +488 -0
- package/dist/asset-cooking.d.ts +82 -0
- package/dist/asset-cooking.js +364 -0
- package/dist/authority-room.d.ts +217 -0
- package/dist/authority-room.js +701 -0
- package/dist/bot-documents.d.ts +115 -0
- package/dist/bot-documents.js +549 -0
- package/dist/bot-primitives.d.ts +152 -0
- package/dist/bot-primitives.js +472 -0
- package/dist/browser.d.ts +57 -0
- package/dist/browser.js +53 -0
- package/dist/canonical.d.ts +12 -0
- package/dist/canonical.js +288 -0
- package/dist/certification.d.ts +455 -0
- package/dist/certification.js +1855 -0
- package/dist/chaos-certification.d.ts +56 -0
- package/dist/chaos-certification.js +1397 -0
- package/dist/cjs/animation.js +495 -0
- package/dist/cjs/asset-cooking.js +372 -0
- package/dist/cjs/authority-room.js +704 -0
- package/dist/cjs/bot-documents.js +556 -0
- package/dist/cjs/bot-primitives.js +482 -0
- package/dist/cjs/browser.js +96 -0
- package/dist/cjs/canonical.js +299 -0
- package/dist/cjs/certification.js +1860 -0
- package/dist/cjs/chaos-certification.js +1402 -0
- package/dist/cjs/collections.js +36 -0
- package/dist/cjs/collision.js +96 -0
- package/dist/cjs/commands.js +987 -0
- package/dist/cjs/config-bundle.js +491 -0
- package/dist/cjs/creator.js +30 -0
- package/dist/cjs/deterministic-primitives.js +134 -0
- package/dist/cjs/deterministic-session.js +1410 -0
- package/dist/cjs/ecs-lite.js +155 -0
- package/dist/cjs/effects-adapter.js +97 -0
- package/dist/cjs/engine-complete.js +2297 -0
- package/dist/cjs/engine-core-proof.js +70 -0
- package/dist/cjs/engine-parity-matrix.js +874 -0
- package/dist/cjs/engine-parity.js +3183 -0
- package/dist/cjs/engine-runtime.js +3141 -0
- package/dist/cjs/errors.js +22 -0
- package/dist/cjs/events.js +247 -0
- package/dist/cjs/examples/counter-duel.js +157 -0
- package/dist/cjs/examples/fixed-arena.js +368 -0
- package/dist/cjs/index.js +229 -0
- package/dist/cjs/input-authority.js +150 -0
- package/dist/cjs/input-button.js +256 -0
- package/dist/cjs/input-timeline.js +418 -0
- package/dist/cjs/instant-replay.js +66 -0
- package/dist/cjs/lag-compensation3d.js +837 -0
- package/dist/cjs/local-authority-server-runner.js +1121 -0
- package/dist/cjs/local-authority-wire.js +1497 -0
- package/dist/cjs/local-authority.js +817 -0
- package/dist/cjs/local-ws-adapter.js +150 -0
- package/dist/cjs/match-log.js +61 -0
- package/dist/cjs/math.js +157 -0
- package/dist/cjs/mock-network-adapter.js +26 -0
- package/dist/cjs/movement.js +703 -0
- package/dist/cjs/movement3d.js +1246 -0
- package/dist/cjs/multiclient-authority.js +308 -0
- package/dist/cjs/multiplayer-service.js +434 -0
- package/dist/cjs/navigation.js +347 -0
- package/dist/cjs/navmesh.js +1437 -0
- package/dist/cjs/networked-client.js +691 -0
- package/dist/cjs/networked-input-authority.js +443 -0
- package/dist/cjs/node.js +47 -0
- package/dist/cjs/number-lab.js +78 -0
- package/dist/cjs/offline-session.js +370 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/pause-resume.js +43 -0
- package/dist/cjs/physics-cert.js +426 -0
- package/dist/cjs/physics2d.js +751 -0
- package/dist/cjs/physics3d.js +1996 -0
- package/dist/cjs/prediction-culling.js +203 -0
- package/dist/cjs/protocol-lab.js +1299 -0
- package/dist/cjs/r3f-render-adapter.js +209 -0
- package/dist/cjs/random.js +59 -0
- package/dist/cjs/replay-bundle.js +1598 -0
- package/dist/cjs/replay-inspect.js +236 -0
- package/dist/cjs/replay-scoring.js +68 -0
- package/dist/cjs/replay.js +356 -0
- package/dist/cjs/reports.js +181 -0
- package/dist/cjs/rollback-history.js +251 -0
- package/dist/cjs/runtime-guards.js +111 -0
- package/dist/cjs/sample-lab.js +145 -0
- package/dist/cjs/sample-scenes.js +752 -0
- package/dist/cjs/schema-artifacts.js +359 -0
- package/dist/cjs/schema-codegen.js +544 -0
- package/dist/cjs/schema-dsl-binary.js +655 -0
- package/dist/cjs/schema.js +319 -0
- package/dist/cjs/sdk-offline.js +99 -0
- package/dist/cjs/session-promotion.js +74 -0
- package/dist/cjs/session-snapshot.js +265 -0
- package/dist/cjs/session-wire.js +216 -0
- package/dist/cjs/signals.js +103 -0
- package/dist/cjs/sparse-set.js +169 -0
- package/dist/cjs/synctest.js +136 -0
- package/dist/cjs/system-lifecycle.js +286 -0
- package/dist/cjs/timers.js +61 -0
- package/dist/cjs/tools/static-checker.js +1468 -0
- package/dist/cjs/types.js +2 -0
- package/dist/cjs/venus-mp-budget.js +125 -0
- package/dist/cjs/ws-frame.js +255 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +11379 -0
- package/dist/collections.d.ts +9 -0
- package/dist/collections.js +30 -0
- package/dist/collision.d.ts +25 -0
- package/dist/collision.js +85 -0
- package/dist/commands.d.ts +137 -0
- package/dist/commands.js +977 -0
- package/dist/config-bundle.d.ts +89 -0
- package/dist/config-bundle.js +483 -0
- package/dist/creator.d.ts +20 -0
- package/dist/creator.js +11 -0
- package/dist/deterministic-primitives.d.ts +59 -0
- package/dist/deterministic-primitives.js +126 -0
- package/dist/deterministic-session.d.ts +197 -0
- package/dist/deterministic-session.js +1401 -0
- package/dist/ecs-lite.d.ts +26 -0
- package/dist/ecs-lite.js +152 -0
- package/dist/effects-adapter.d.ts +21 -0
- package/dist/effects-adapter.js +94 -0
- package/dist/engine-complete.d.ts +45 -0
- package/dist/engine-complete.js +2292 -0
- package/dist/engine-core-proof.d.ts +26 -0
- package/dist/engine-core-proof.js +66 -0
- package/dist/engine-parity-matrix.d.ts +300 -0
- package/dist/engine-parity-matrix.js +869 -0
- package/dist/engine-parity.d.ts +74 -0
- package/dist/engine-parity.js +3178 -0
- package/dist/engine-runtime.d.ts +351 -0
- package/dist/engine-runtime.js +3123 -0
- package/dist/errors.d.ts +11 -0
- package/dist/errors.js +17 -0
- package/dist/events.d.ts +45 -0
- package/dist/events.js +242 -0
- package/dist/examples/counter-duel.d.ts +43 -0
- package/dist/examples/counter-duel.js +150 -0
- package/dist/examples/fixed-arena.d.ts +110 -0
- package/dist/examples/fixed-arena.js +356 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.js +66 -0
- package/dist/input-authority.d.ts +82 -0
- package/dist/input-authority.js +147 -0
- package/dist/input-button.d.ts +36 -0
- package/dist/input-button.js +249 -0
- package/dist/input-timeline.d.ts +65 -0
- package/dist/input-timeline.js +412 -0
- package/dist/instant-replay.d.ts +59 -0
- package/dist/instant-replay.js +62 -0
- package/dist/lag-compensation3d.d.ts +146 -0
- package/dist/lag-compensation3d.js +827 -0
- package/dist/local-authority-server-runner.d.ts +79 -0
- package/dist/local-authority-server-runner.js +1118 -0
- package/dist/local-authority-wire.d.ts +142 -0
- package/dist/local-authority-wire.js +1477 -0
- package/dist/local-authority.d.ts +160 -0
- package/dist/local-authority.js +810 -0
- package/dist/local-ws-adapter.d.ts +14 -0
- package/dist/local-ws-adapter.js +146 -0
- package/dist/match-log.d.ts +74 -0
- package/dist/match-log.js +57 -0
- package/dist/math.d.ts +41 -0
- package/dist/math.js +154 -0
- package/dist/mock-network-adapter.d.ts +13 -0
- package/dist/mock-network-adapter.js +23 -0
- package/dist/movement.d.ts +155 -0
- package/dist/movement.js +694 -0
- package/dist/movement3d.d.ts +195 -0
- package/dist/movement3d.js +1238 -0
- package/dist/multiclient-authority.d.ts +30 -0
- package/dist/multiclient-authority.js +305 -0
- package/dist/multiplayer-service.d.ts +153 -0
- package/dist/multiplayer-service.js +429 -0
- package/dist/navigation.d.ts +91 -0
- package/dist/navigation.js +338 -0
- package/dist/navmesh.d.ts +236 -0
- package/dist/navmesh.js +1423 -0
- package/dist/networked-client.d.ts +124 -0
- package/dist/networked-client.js +688 -0
- package/dist/networked-input-authority.d.ts +51 -0
- package/dist/networked-input-authority.js +439 -0
- package/dist/node.d.ts +22 -0
- package/dist/node.js +19 -0
- package/dist/number-lab.d.ts +15 -0
- package/dist/number-lab.js +75 -0
- package/dist/offline-session.d.ts +2 -0
- package/dist/offline-session.js +367 -0
- package/dist/pause-resume.d.ts +48 -0
- package/dist/pause-resume.js +40 -0
- package/dist/physics-cert.d.ts +40 -0
- package/dist/physics-cert.js +389 -0
- package/dist/physics2d.d.ts +142 -0
- package/dist/physics2d.js +741 -0
- package/dist/physics3d.d.ts +285 -0
- package/dist/physics3d.js +1979 -0
- package/dist/prediction-culling.d.ts +34 -0
- package/dist/prediction-culling.js +200 -0
- package/dist/protocol-lab.d.ts +115 -0
- package/dist/protocol-lab.js +1295 -0
- package/dist/r3f-render-adapter.d.ts +61 -0
- package/dist/r3f-render-adapter.js +203 -0
- package/dist/random.d.ts +13 -0
- package/dist/random.js +55 -0
- package/dist/replay-bundle.d.ts +150 -0
- package/dist/replay-bundle.js +1586 -0
- package/dist/replay-inspect.d.ts +60 -0
- package/dist/replay-inspect.js +228 -0
- package/dist/replay-scoring.d.ts +28 -0
- package/dist/replay-scoring.js +65 -0
- package/dist/replay.d.ts +17 -0
- package/dist/replay.js +351 -0
- package/dist/reports.d.ts +176 -0
- package/dist/reports.js +174 -0
- package/dist/rollback-history.d.ts +46 -0
- package/dist/rollback-history.js +247 -0
- package/dist/runtime-guards.d.ts +1 -0
- package/dist/runtime-guards.js +108 -0
- package/dist/sample-lab.d.ts +32 -0
- package/dist/sample-lab.js +134 -0
- package/dist/sample-scenes.d.ts +86 -0
- package/dist/sample-scenes.js +745 -0
- package/dist/schema-artifacts.d.ts +49 -0
- package/dist/schema-artifacts.js +356 -0
- package/dist/schema-codegen.d.ts +21 -0
- package/dist/schema-codegen.js +538 -0
- package/dist/schema-dsl-binary.d.ts +75 -0
- package/dist/schema-dsl-binary.js +648 -0
- package/dist/schema.d.ts +87 -0
- package/dist/schema.js +315 -0
- package/dist/sdk-offline.d.ts +34 -0
- package/dist/sdk-offline.js +96 -0
- package/dist/session-promotion.d.ts +26 -0
- package/dist/session-promotion.js +70 -0
- package/dist/session-snapshot.d.ts +95 -0
- package/dist/session-snapshot.js +255 -0
- package/dist/session-wire.d.ts +89 -0
- package/dist/session-wire.js +211 -0
- package/dist/signals.d.ts +31 -0
- package/dist/signals.js +99 -0
- package/dist/sparse-set.d.ts +34 -0
- package/dist/sparse-set.js +165 -0
- package/dist/synctest.d.ts +31 -0
- package/dist/synctest.js +133 -0
- package/dist/system-lifecycle.d.ts +72 -0
- package/dist/system-lifecycle.js +281 -0
- package/dist/timers.d.ts +17 -0
- package/dist/timers.js +53 -0
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.js +4 -0
- package/dist/tools/static-checker.d.ts +31 -0
- package/dist/tools/static-checker.js +1462 -0
- package/dist/tools/vite-plugin.d.ts +69 -0
- package/dist/tools/vite-plugin.js +203 -0
- package/dist/types.d.ts +148 -0
- package/dist/types.js +1 -0
- package/dist/venus-mp-budget.d.ts +29 -0
- package/dist/venus-mp-budget.js +122 -0
- package/dist/ws-frame.d.ts +49 -0
- package/dist/ws-frame.js +247 -0
- package/package.json +169 -0
package/dist/ws-frame.js
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { createServer } from 'node:http';
|
|
3
|
+
import { decodeUtf8, encodeUtf8 } from './canonical.js';
|
|
4
|
+
/**
|
|
5
|
+
* Minimal RFC 6455 text-frame codec plus a connection-oriented server/client.
|
|
6
|
+
*
|
|
7
|
+
* The engine ships with zero runtime dependencies, so this hand-rolls just enough of the
|
|
8
|
+
* WebSocket wire format to carry small JSON control messages over a real socket. It supports
|
|
9
|
+
* unfragmented text frames up to 65535 bytes — sufficient for input/confirmed-input traffic.
|
|
10
|
+
* `local-ws-adapter.ts` and the M0 networked-authority spike share this codec.
|
|
11
|
+
*/
|
|
12
|
+
const webSocketGuid = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
|
|
13
|
+
export function webSocketAcceptKey(secWebSocketKey) {
|
|
14
|
+
return createHash('sha1').update(`${secWebSocketKey}${webSocketGuid}`).digest('base64');
|
|
15
|
+
}
|
|
16
|
+
export function encodeWebSocketTextFrame(text) {
|
|
17
|
+
const payload = encodeUtf8(text);
|
|
18
|
+
if (payload.byteLength > 65535) {
|
|
19
|
+
throw new Error('WebSocket text frame exceeds the 65535-byte codec limit');
|
|
20
|
+
}
|
|
21
|
+
const headerLength = payload.byteLength < 126 ? 2 : 4;
|
|
22
|
+
const frame = new Uint8Array(headerLength + payload.byteLength);
|
|
23
|
+
frame[0] = 0x81;
|
|
24
|
+
if (payload.byteLength < 126) {
|
|
25
|
+
frame[1] = payload.byteLength;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
frame[1] = 126;
|
|
29
|
+
frame[2] = Math.floor(payload.byteLength / 256);
|
|
30
|
+
frame[3] = payload.byteLength % 256;
|
|
31
|
+
}
|
|
32
|
+
frame.set(payload, headerLength);
|
|
33
|
+
return frame;
|
|
34
|
+
}
|
|
35
|
+
/** A minimal, bodyless close frame (opcode 0x8, FIN set). */
|
|
36
|
+
export function encodeWebSocketCloseFrame() {
|
|
37
|
+
return new Uint8Array([0x88, 0x00]);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* NEVER throws: this runs inside raw `socket.on('data')` handlers, where an
|
|
41
|
+
* uncaught throw is not routed to the 'error' listener and takes the process
|
|
42
|
+
* down — a handful of malformed bytes from any client would be an
|
|
43
|
+
* unauthenticated crash. Malformed input is reported as `kind: 'invalid'` so
|
|
44
|
+
* the receive loop can close the connection instead.
|
|
45
|
+
*/
|
|
46
|
+
export function readWebSocketFrame(bytes) {
|
|
47
|
+
if (bytes.byteLength < 2) {
|
|
48
|
+
return { kind: 'incomplete' };
|
|
49
|
+
}
|
|
50
|
+
const opcode = bytes[0] & 0x0f;
|
|
51
|
+
const masked = (bytes[1] & 0x80) !== 0;
|
|
52
|
+
// Capture the 7-bit tag BEFORE the 126 branch reassigns `length`: a 127-byte
|
|
53
|
+
// payload arrives in the 16-bit form and must not trip the 64-bit guard.
|
|
54
|
+
const lengthTag = bytes[1] & 0x7f;
|
|
55
|
+
let length = lengthTag;
|
|
56
|
+
let offset = 2;
|
|
57
|
+
if (lengthTag === 127) {
|
|
58
|
+
return { kind: 'invalid', reason: 'unsupported-64-bit-frame-length' };
|
|
59
|
+
}
|
|
60
|
+
if (lengthTag === 126) {
|
|
61
|
+
if (bytes.byteLength < 4) {
|
|
62
|
+
return { kind: 'incomplete' };
|
|
63
|
+
}
|
|
64
|
+
length = bytes[2] * 256 + bytes[3];
|
|
65
|
+
offset = 4;
|
|
66
|
+
}
|
|
67
|
+
const maskBytes = masked ? bytes.slice(offset, offset + 4) : undefined;
|
|
68
|
+
offset += masked ? 4 : 0;
|
|
69
|
+
if (bytes.byteLength < offset + length) {
|
|
70
|
+
return { kind: 'incomplete' };
|
|
71
|
+
}
|
|
72
|
+
const payload = bytes.slice(offset, offset + length);
|
|
73
|
+
if (maskBytes !== undefined) {
|
|
74
|
+
for (let index = 0; index < payload.byteLength; index += 1) {
|
|
75
|
+
payload[index] ^= maskBytes[index % 4];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
let text;
|
|
79
|
+
try {
|
|
80
|
+
text = decodeUtf8(payload);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return { kind: 'invalid', reason: 'malformed-utf8-payload' };
|
|
84
|
+
}
|
|
85
|
+
return { kind: 'frame', frame: { opcode, text, bytesRead: offset + length } };
|
|
86
|
+
}
|
|
87
|
+
export async function startWebSocketServer(options) {
|
|
88
|
+
const path = options.path ?? '/';
|
|
89
|
+
const server = createServer((request, response) => {
|
|
90
|
+
if (request.url === '/health') {
|
|
91
|
+
response.statusCode = 200;
|
|
92
|
+
response.setHeader('content-type', 'text/plain');
|
|
93
|
+
response.end('ok');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
response.statusCode = 404;
|
|
97
|
+
response.end('not found');
|
|
98
|
+
});
|
|
99
|
+
server.on('upgrade', (request, socket, head) => {
|
|
100
|
+
if (new URL(request.url ?? '/', 'http://127.0.0.1').pathname !== path) {
|
|
101
|
+
socket.end('HTTP/1.1 404 Not Found\r\n\r\n');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const key = request.headers['sec-websocket-key'];
|
|
105
|
+
if (typeof key !== 'string') {
|
|
106
|
+
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
socket.write([
|
|
110
|
+
'HTTP/1.1 101 Switching Protocols',
|
|
111
|
+
'Upgrade: websocket',
|
|
112
|
+
'Connection: Upgrade',
|
|
113
|
+
`Sec-WebSocket-Accept: ${webSocketAcceptKey(key)}`,
|
|
114
|
+
'\r\n',
|
|
115
|
+
].join('\r\n'));
|
|
116
|
+
// The upgraded socket is a net.Socket at runtime; @types/node's upgrade signature omits a
|
|
117
|
+
// 'close' overload, so narrow it to the small surface this codec actually uses.
|
|
118
|
+
const connection = createServerConnection(socket);
|
|
119
|
+
options.onConnection(connection.handle, request);
|
|
120
|
+
if (head.byteLength > 0) {
|
|
121
|
+
connection.receive(head);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
await new Promise((resolveListen) => {
|
|
125
|
+
server.listen(options.port ?? 0, '127.0.0.1', resolveListen);
|
|
126
|
+
});
|
|
127
|
+
const address = server.address();
|
|
128
|
+
if (address === null || typeof address === 'string') {
|
|
129
|
+
throw new Error('Could not determine WebSocket server port');
|
|
130
|
+
}
|
|
131
|
+
const port = address.port;
|
|
132
|
+
return {
|
|
133
|
+
port,
|
|
134
|
+
url: `ws://127.0.0.1:${port}${path}`,
|
|
135
|
+
close() {
|
|
136
|
+
return new Promise((resolveClose, rejectClose) => {
|
|
137
|
+
server.close((error) => (error === undefined ? resolveClose() : rejectClose(error)));
|
|
138
|
+
});
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function createServerConnection(socket) {
|
|
143
|
+
let pending = new Uint8Array();
|
|
144
|
+
const messageHandlers = [];
|
|
145
|
+
const closeHandlers = [];
|
|
146
|
+
let closed = false;
|
|
147
|
+
function emitClose() {
|
|
148
|
+
if (closed) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
closed = true;
|
|
152
|
+
for (const handler of closeHandlers) {
|
|
153
|
+
handler();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function receive(chunk) {
|
|
157
|
+
pending = concatBytes(pending, chunk);
|
|
158
|
+
let result = readWebSocketFrame(pending);
|
|
159
|
+
while (result.kind === 'frame') {
|
|
160
|
+
const frame = result.frame;
|
|
161
|
+
pending = pending.slice(frame.bytesRead);
|
|
162
|
+
if (frame.opcode === 0x8) {
|
|
163
|
+
socket.end(encodeWebSocketCloseFrame());
|
|
164
|
+
emitClose();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (frame.opcode === 0x1) {
|
|
168
|
+
const text = frame.text;
|
|
169
|
+
for (const handler of messageHandlers) {
|
|
170
|
+
handler(text);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
result = readWebSocketFrame(pending);
|
|
174
|
+
}
|
|
175
|
+
if (result.kind === 'invalid') {
|
|
176
|
+
// Malformed frame: close the connection instead of crashing the process.
|
|
177
|
+
socket.end(encodeWebSocketCloseFrame());
|
|
178
|
+
emitClose();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
socket.on('data', (chunk) => receive(chunk));
|
|
182
|
+
socket.on('error', () => {
|
|
183
|
+
socket.destroy();
|
|
184
|
+
emitClose();
|
|
185
|
+
});
|
|
186
|
+
socket.on('close', emitClose);
|
|
187
|
+
const handle = {
|
|
188
|
+
send(text) {
|
|
189
|
+
if (!closed) {
|
|
190
|
+
socket.write(encodeWebSocketTextFrame(text));
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
onMessage(handler) {
|
|
194
|
+
messageHandlers.push(handler);
|
|
195
|
+
},
|
|
196
|
+
onClose(handler) {
|
|
197
|
+
closeHandlers.push(handler);
|
|
198
|
+
},
|
|
199
|
+
close() {
|
|
200
|
+
if (!closed) {
|
|
201
|
+
socket.end(encodeWebSocketCloseFrame());
|
|
202
|
+
emitClose();
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
return { handle, receive };
|
|
207
|
+
}
|
|
208
|
+
export async function connectWebSocketClient(url) {
|
|
209
|
+
const socket = new WebSocket(url);
|
|
210
|
+
const messageHandlers = [];
|
|
211
|
+
const closeHandlers = [];
|
|
212
|
+
socket.addEventListener('message', (event) => {
|
|
213
|
+
const text = String(event.data);
|
|
214
|
+
for (const handler of messageHandlers) {
|
|
215
|
+
handler(text);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
socket.addEventListener('close', () => {
|
|
219
|
+
for (const handler of closeHandlers) {
|
|
220
|
+
handler();
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
await new Promise((resolveOpen, rejectOpen) => {
|
|
224
|
+
socket.addEventListener('open', () => resolveOpen(), { once: true });
|
|
225
|
+
socket.addEventListener('error', () => rejectOpen(new Error(`Could not connect to ${url}`)), { once: true });
|
|
226
|
+
});
|
|
227
|
+
return {
|
|
228
|
+
send(text) {
|
|
229
|
+
socket.send(text);
|
|
230
|
+
},
|
|
231
|
+
onMessage(handler) {
|
|
232
|
+
messageHandlers.push(handler);
|
|
233
|
+
},
|
|
234
|
+
onClose(handler) {
|
|
235
|
+
closeHandlers.push(handler);
|
|
236
|
+
},
|
|
237
|
+
close() {
|
|
238
|
+
socket.close();
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function concatBytes(left, right) {
|
|
243
|
+
const output = new Uint8Array(left.byteLength + right.byteLength);
|
|
244
|
+
output.set(left, 0);
|
|
245
|
+
output.set(right, left.byteLength);
|
|
246
|
+
return output;
|
|
247
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@series-inc/rundot-syncplay",
|
|
3
|
+
"version": "5.23.0-beta.7",
|
|
4
|
+
"description": "Offline-first deterministic multiplayer core for RUN.game",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/cjs/index.js",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./creator": {
|
|
17
|
+
"types": "./dist/creator.d.ts",
|
|
18
|
+
"import": "./dist/creator.js",
|
|
19
|
+
"require": "./dist/cjs/creator.js",
|
|
20
|
+
"default": "./dist/creator.js"
|
|
21
|
+
},
|
|
22
|
+
"./node": {
|
|
23
|
+
"types": "./dist/node.d.ts",
|
|
24
|
+
"import": "./dist/node.js",
|
|
25
|
+
"require": "./dist/cjs/node.js",
|
|
26
|
+
"default": "./dist/node.js"
|
|
27
|
+
},
|
|
28
|
+
"./browser": {
|
|
29
|
+
"types": "./dist/browser.d.ts",
|
|
30
|
+
"import": "./dist/browser.js",
|
|
31
|
+
"require": "./dist/cjs/browser.js",
|
|
32
|
+
"default": "./dist/browser.js"
|
|
33
|
+
},
|
|
34
|
+
"./tools": {
|
|
35
|
+
"types": "./dist/tools/index.d.ts",
|
|
36
|
+
"import": "./dist/tools/index.js",
|
|
37
|
+
"default": "./dist/tools/index.js"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"bin": {
|
|
41
|
+
"rundot-syncplay": "dist/cli.js"
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist"
|
|
45
|
+
],
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json && tsc -p tsconfig.cjs.json && node -e \"require('node:fs').writeFileSync('dist/cjs/package.json', JSON.stringify({type:'commonjs'}))\"",
|
|
48
|
+
"test": "tsx --test tests/**/*.test.ts",
|
|
49
|
+
"test:ci": "tsx --test tests/canonical.test.ts tests/entrypoints.test.ts && npm run static:determinism -- --paths tests/fixtures/pass/simulation.ts --source-only --json artifacts/certification/static-ci-smoke.json",
|
|
50
|
+
"test:core": "tsx --test tests/canonical.test.ts tests/offline-session.test.ts tests/input-authority.test.ts tests/session-wire.test.ts tests/session-snapshot.test.ts tests/authority-room.test.ts tests/networked-client-snapshot.test.ts tests/local-authority-wire.test.ts tests/replay-scoring.test.ts tests/bot-backfill.test.ts tests/latency-simulation.test.ts tests/time-sync.test.ts tests/match-log.test.ts tests/spectator.test.ts tests/synctest.test.ts tests/instant-replay.test.ts tests/session-promotion.test.ts tests/pause-resume.test.ts",
|
|
51
|
+
"test:certification": "npm run test:core && tsx --test tests/entrypoints.test.ts tests/multiplayer-service.test.ts tests/deterministic-session.test.ts tests/multiclient-authority.test.ts tests/networked-input-authority.test.ts tests/rollback-history.test.ts",
|
|
52
|
+
"pack:dry-run": "npm pack --dry-run",
|
|
53
|
+
"lab:offline-replay": "tsx src/cli.ts lab:offline-replay",
|
|
54
|
+
"lab:replay-verify": "tsx src/cli.ts lab:replay-verify",
|
|
55
|
+
"lab:replay-inspect": "tsx src/cli.ts lab:replay-inspect",
|
|
56
|
+
"lab:replay-diff": "tsx src/cli.ts lab:replay-diff",
|
|
57
|
+
"lab:replay-verify-browser": "tsx src/cli.ts lab:replay-verify-browser",
|
|
58
|
+
"lab:buffer-bench": "tsx src/cli.ts lab:buffer-bench",
|
|
59
|
+
"lab:protocol-rollback": "tsx src/cli.ts lab:protocol-rollback",
|
|
60
|
+
"lab:netcode": "tsx src/cli.ts lab:netcode",
|
|
61
|
+
"lab:prediction-culling": "tsx src/cli.ts lab:prediction-culling",
|
|
62
|
+
"lab:local-ws-server": "tsx src/cli.ts lab:local-ws-server",
|
|
63
|
+
"lab:local-authority-server": "tsx src/cli.ts lab:local-authority-server",
|
|
64
|
+
"lab:networked-authority": "tsx src/cli.ts lab:networked-authority",
|
|
65
|
+
"lab:wait-for-url": "tsx src/cli.ts lab:wait-for-url",
|
|
66
|
+
"lab:ws-adapter-test": "tsx src/cli.ts lab:ws-adapter-test",
|
|
67
|
+
"lab:r3f-render-adapter": "tsx src/cli.ts lab:r3f-render-adapter",
|
|
68
|
+
"lab:venus-mp-budget": "tsx src/cli.ts lab:venus-mp-budget",
|
|
69
|
+
"lab:physics-cert": "tsx src/cli.ts lab:physics-cert",
|
|
70
|
+
"lab:physics3d": "tsx src/cli.ts lab:physics3d",
|
|
71
|
+
"lab:kcc3d": "tsx src/cli.ts lab:kcc3d",
|
|
72
|
+
"lab:lag-compensation3d": "tsx src/cli.ts lab:lag-compensation3d",
|
|
73
|
+
"lab:input-timeline": "tsx src/cli.ts lab:input-timeline",
|
|
74
|
+
"lab:command-timeline": "tsx src/cli.ts lab:command-timeline",
|
|
75
|
+
"lab:number": "tsx src/cli.ts lab:number",
|
|
76
|
+
"lab:number-browser": "tsx src/cli.ts lab:number-browser",
|
|
77
|
+
"lab:engine-core-browser": "tsx src/cli.ts lab:engine-core-browser",
|
|
78
|
+
"certify:v1": "tsx src/cli.ts certify",
|
|
79
|
+
"certify:v2": "tsx src/cli.ts certify:v2",
|
|
80
|
+
"certify:chaos": "tsx src/cli.ts certify:v2 --profile chaos",
|
|
81
|
+
"certify:engine-parity": "tsx src/cli.ts certify:engine-parity",
|
|
82
|
+
"benchmark:engine-parity": "tsx src/cli.ts benchmark:engine-parity",
|
|
83
|
+
"certify:engine-complete": "tsx src/cli.ts certify:engine-complete",
|
|
84
|
+
"benchmark:engine-complete": "tsx src/cli.ts benchmark:engine-complete",
|
|
85
|
+
"certify:deterministic-stress": "npm run benchmark:engine-complete -- --players 128 --frames 100000 --seed 71 --enforce-thresholds --json artifacts/certification/engine-complete-stress.json --junit artifacts/certification/engine-complete-stress.xml && npm run benchmark:engine-parity -- --profile stress --players 128 --frames 100000 --seed 71 --json artifacts/certification/engine-parity-stress-100k.json --junit artifacts/certification/engine-parity-stress-100k.xml",
|
|
86
|
+
"certify:standalone-complete": "npm run certify:engine-complete -- --players 128 --frames 5000 --seed 71 --json artifacts/certification/engine-complete.json --junit artifacts/certification/engine-complete.xml && npm run benchmark:engine-complete -- --players 128 --frames 100000 --seed 71 --enforce-thresholds --json artifacts/certification/engine-complete-stress.json --junit artifacts/certification/engine-complete-stress.xml && npm run certify:v2 -- --profile required-full --players 128 --frames 1000 --seed 71 --json artifacts/certification/engine-parity-required-full.json --junit artifacts/certification/engine-parity-required-full.xml && npm run benchmark:engine-parity -- --profile stress --players 128 --frames 100000 --seed 71 --json artifacts/certification/engine-parity-stress-100k.json --junit artifacts/certification/engine-parity-stress-100k.xml && npm run lab:local-authority-server -- --players 128 --frames 1000 --seed 71 --json artifacts/certification/local-authority-server-p128.json && npm run lab:offline-replay -- --players 4 --frames 120 --seed 17 --out artifacts/replays/browser-proof-current.json && npm run lab:replay-verify-browser -- artifacts/replays/browser-proof-current.json --runtime chromium --json artifacts/certification/browser-replay-chromium-current.json && npm run lab:engine-core-browser -- --runtime chromium --players 128 --frames 1000 --seed 71 --json artifacts/certification/engine-core-numeric-chromium-current.json && SDK_TEST_APP_PROOF=\"$PWD/artifacts/certification/sdk-test-app-current.json\" && cd ../sdk && npm run build && npm run build:test-app && npm run verify:syncplay-test-app -- --json \"$SDK_TEST_APP_PROOF\" && cd ../syncplay && npm run report:engine-parity-matrix:certified && npm run static:determinism -- --json artifacts/certification/static-engine-complete.json",
|
|
87
|
+
"report:diff": "tsx src/cli.ts report:diff",
|
|
88
|
+
"report:engine-parity-matrix": "tsx src/cli.ts report:engine-parity-matrix",
|
|
89
|
+
"report:engine-parity-matrix:certified": "tsx src/cli.ts report:engine-parity-matrix --artifact-root artifacts/certification --enforce --json artifacts/certification/engine-parity-matrix.json",
|
|
90
|
+
"replay:verify": "tsx src/cli.ts replay:verify",
|
|
91
|
+
"replay-bundle:verify": "tsx src/cli.ts replay-bundle:verify",
|
|
92
|
+
"replay-bundle:verify-runtime": "tsx src/cli.ts replay-bundle:verify-runtime",
|
|
93
|
+
"replay-bundle:slice": "tsx src/cli.ts replay-bundle:slice",
|
|
94
|
+
"replay-bundle:debug-tables": "tsx src/cli.ts replay-bundle:debug-tables",
|
|
95
|
+
"replay-bundle:diff": "tsx src/cli.ts replay-bundle:diff",
|
|
96
|
+
"static:determinism": "tsx src/cli.ts static:determinism",
|
|
97
|
+
"cook:syncplay-assets": "tsx src/cli.ts cook:syncplay-assets",
|
|
98
|
+
"config:bundle": "tsx src/cli.ts config:bundle",
|
|
99
|
+
"config:verify": "tsx src/cli.ts config:verify",
|
|
100
|
+
"navmesh:cook": "tsx src/cli.ts navmesh:cook",
|
|
101
|
+
"navmesh:validate": "tsx src/cli.ts navmesh:validate",
|
|
102
|
+
"navmesh:trace": "tsx src/cli.ts navmesh:trace",
|
|
103
|
+
"animation:compile": "tsx src/cli.ts animation:compile",
|
|
104
|
+
"animation:trace": "tsx src/cli.ts animation:trace",
|
|
105
|
+
"bot:compile": "tsx src/cli.ts bot:compile",
|
|
106
|
+
"bot:trace": "tsx src/cli.ts bot:trace",
|
|
107
|
+
"project:scaffold": "tsx src/cli.ts project:scaffold",
|
|
108
|
+
"project:certify": "tsx src/cli.ts project:certify",
|
|
109
|
+
"project:summary": "tsx src/cli.ts project:summary",
|
|
110
|
+
"project:preflight": "tsx src/cli.ts project:preflight",
|
|
111
|
+
"project:check": "tsx src/cli.ts project:check",
|
|
112
|
+
"project:release-verify": "tsx src/cli.ts project:release-verify",
|
|
113
|
+
"project:release-bundle": "tsx src/cli.ts project:release-bundle",
|
|
114
|
+
"project:release-bundle-verify": "tsx src/cli.ts project:release-bundle-verify",
|
|
115
|
+
"project:release-bundle-schema": "tsx src/cli.ts project:release-bundle-schema",
|
|
116
|
+
"project:doctor": "tsx src/cli.ts project:doctor",
|
|
117
|
+
"project:deploy-check": "tsx src/cli.ts project:deploy-check",
|
|
118
|
+
"project:deploy-check-verify": "tsx src/cli.ts project:deploy-check-verify",
|
|
119
|
+
"project:ci-workflow": "tsx src/cli.ts project:ci-workflow",
|
|
120
|
+
"example:counter-duel": "tsx src/cli.ts example:counter-duel",
|
|
121
|
+
"example:fixed-arena": "tsx src/cli.ts example:fixed-arena",
|
|
122
|
+
"schema:validate": "tsx src/cli.ts schema:validate",
|
|
123
|
+
"schema:dump": "tsx src/cli.ts schema:dump",
|
|
124
|
+
"schema:types": "tsx src/cli.ts schema:types",
|
|
125
|
+
"schema:runtime": "tsx src/cli.ts schema:runtime",
|
|
126
|
+
"schema:binary": "tsx src/cli.ts schema:binary",
|
|
127
|
+
"check": "tsx src/cli.ts check",
|
|
128
|
+
"coverage:package:core-fast": "node scripts/run-package-istanbul-coverage-shard.mjs --id core-fast --include src/collision.ts --include src/deterministic-primitives.ts --include src/index.ts --include src/math.ts --include src/random.ts --test tests/canonical.test.ts --test tests/collision.test.ts --test tests/deterministic-primitives.test.ts --test tests/math.test.ts --test tests/random.test.ts",
|
|
129
|
+
"coverage:package:commands": "node scripts/run-package-source-istanbul-coverage-shard.mjs --id commands --include src/commands.ts --test tests/command-timeline.test.ts",
|
|
130
|
+
"coverage:package:input-timeline": "node scripts/run-package-source-istanbul-coverage-shard.mjs --id input-timeline --include src/input-timeline.ts --test tests/input-timeline.test.ts",
|
|
131
|
+
"coverage:package:multiclient-authority": "node scripts/run-package-istanbul-coverage-shard.mjs --id multiclient-authority --include src/multiclient-authority.ts --test tests/multiclient-authority.test.ts",
|
|
132
|
+
"coverage:package:local-authority": "node scripts/run-package-source-istanbul-coverage-shard.mjs --id local-authority --include src/local-authority.ts --test tests/local-authority.test.ts",
|
|
133
|
+
"coverage:package:local-authority-wire": "node scripts/run-package-istanbul-coverage-shard.mjs --id local-authority-wire --include src/local-authority-wire.ts --test tests/local-authority-wire.test.ts",
|
|
134
|
+
"coverage:package:input-button": "node scripts/run-package-istanbul-coverage-shard.mjs --id input-button --include src/input-button.ts --test tests/input-button.test.ts",
|
|
135
|
+
"coverage:package:ecs-lite": "node scripts/run-package-istanbul-coverage-shard.mjs --id ecs-lite --include src/ecs-lite.ts --test tests/ecs-lite.test.ts",
|
|
136
|
+
"coverage:package:collision": "node scripts/run-package-istanbul-coverage-shard.mjs --id collision --include src/collision.ts --test tests/collision.test.ts",
|
|
137
|
+
"coverage:package:random": "node scripts/run-package-istanbul-coverage-shard.mjs --id random --include src/random.ts --test tests/random.test.ts",
|
|
138
|
+
"coverage:package:deterministic-primitives": "node scripts/run-package-istanbul-coverage-shard.mjs --id deterministic-primitives --include src/deterministic-primitives.ts --test tests/deterministic-primitives.test.ts",
|
|
139
|
+
"coverage:package:deterministic-session": "node scripts/run-package-istanbul-coverage-shard.mjs --id deterministic-session --include src/deterministic-session.ts --test tests/deterministic-session.test.ts",
|
|
140
|
+
"coverage:package:canonical": "node scripts/run-package-istanbul-coverage-shard.mjs --id canonical --include src/canonical.ts --test tests/canonical.test.ts",
|
|
141
|
+
"coverage:package:math": "node scripts/run-package-istanbul-coverage-shard.mjs --id math --include src/math.ts --test tests/math.test.ts",
|
|
142
|
+
"coverage:package:errors": "node scripts/run-package-istanbul-coverage-shard.mjs --id errors --include src/errors.ts --test tests/errors.test.ts",
|
|
143
|
+
"coverage:package:entrypoints": "node scripts/run-package-istanbul-coverage-shard.mjs --id entrypoints --include src/browser.ts --include src/node.ts --include src/creator.ts --include src/tools/index.ts --test tests/entrypoints.test.ts",
|
|
144
|
+
"coverage:package:multiplayer-service": "node scripts/run-package-istanbul-coverage-shard.mjs --id multiplayer-service --include src/multiplayer-service.ts --test tests/multiplayer-service.test.ts",
|
|
145
|
+
"coverage:package:prediction-culling": "node scripts/run-package-istanbul-coverage-shard.mjs --id prediction-culling --include src/prediction-culling.ts --test tests/prediction-culling.test.ts",
|
|
146
|
+
"coverage:package:rollback-history": "node scripts/run-package-istanbul-coverage-shard.mjs --id rollback-history --include src/rollback-history.ts --test tests/rollback-history.test.ts",
|
|
147
|
+
"coverage:package:sparse-set": "node scripts/run-package-istanbul-coverage-shard.mjs --id sparse-set --include src/sparse-set.ts --test tests/sparse-set.test.ts",
|
|
148
|
+
"coverage:package:offline-session": "node scripts/run-package-istanbul-coverage-shard.mjs --id offline-session --include src/offline-session.ts --test tests/offline-session.test.ts",
|
|
149
|
+
"coverage:package:schema-dsl-binary": "node scripts/run-package-istanbul-coverage-shard.mjs --id schema-dsl-binary --include src/schema-dsl-binary.ts --test tests/schema-dsl-binary.test.ts",
|
|
150
|
+
"coverage:deterministic:strict": "node --import tsx --test --experimental-test-coverage --test-coverage-include='src/**/*.ts' --test-coverage-lines=100 --test-coverage-branches=100 --test-coverage-functions=100 tests/**/*.test.ts"
|
|
151
|
+
},
|
|
152
|
+
"license": "MIT",
|
|
153
|
+
"keywords": [
|
|
154
|
+
"RUN.game",
|
|
155
|
+
"deterministic",
|
|
156
|
+
"multiplayer",
|
|
157
|
+
"rollback",
|
|
158
|
+
"replay"
|
|
159
|
+
],
|
|
160
|
+
"devDependencies": {
|
|
161
|
+
"@dimforge/rapier2d-compat": "0.19.3",
|
|
162
|
+
"@types/node": "^22.0.0",
|
|
163
|
+
"istanbul-lib-coverage": "^3.2.2",
|
|
164
|
+
"istanbul-lib-instrument": "^6.0.3",
|
|
165
|
+
"playwright": "^1.60.0",
|
|
166
|
+
"tsx": "^4.0.0",
|
|
167
|
+
"typescript": "^5.9.2"
|
|
168
|
+
}
|
|
169
|
+
}
|