@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
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type DeterministicMockNetworkReplay } from './mock-network-adapter.js';
|
|
2
|
+
import type { ProtocolRollbackLabOptions } from './protocol-lab.js';
|
|
3
|
+
export interface LocalWebSocketServer {
|
|
4
|
+
readonly port: number;
|
|
5
|
+
readonly healthUrl: string;
|
|
6
|
+
readonly url: string;
|
|
7
|
+
close(): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
export interface LocalWebSocketAdapterTestOptions {
|
|
10
|
+
readonly url: string;
|
|
11
|
+
readonly protocolOptions: ProtocolRollbackLabOptions;
|
|
12
|
+
}
|
|
13
|
+
export declare function startLocalWebSocketProtocolServer(port: number): Promise<LocalWebSocketServer>;
|
|
14
|
+
export declare function runLocalWebSocketAdapterTest(options: LocalWebSocketAdapterTestOptions): Promise<DeterministicMockNetworkReplay>;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { createDeterministicMockNetworkAdapter } from './mock-network-adapter.js';
|
|
3
|
+
import { encodeWebSocketCloseFrame, encodeWebSocketTextFrame, readWebSocketFrame, webSocketAcceptKey } from './ws-frame.js';
|
|
4
|
+
export async function startLocalWebSocketProtocolServer(port) {
|
|
5
|
+
const server = createServer((request, response) => {
|
|
6
|
+
if (request.url === '/health') {
|
|
7
|
+
response.statusCode = 200;
|
|
8
|
+
response.setHeader('content-type', 'text/plain');
|
|
9
|
+
response.end('ok');
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
response.statusCode = 404;
|
|
13
|
+
response.end('not found');
|
|
14
|
+
});
|
|
15
|
+
server.on('upgrade', (request, socket, head) => {
|
|
16
|
+
if (new URL(request.url ?? '/', 'http://127.0.0.1').pathname !== '/protocol') {
|
|
17
|
+
socket.end('HTTP/1.1 404 Not Found\r\n\r\n');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
acceptWebSocket(request, socket);
|
|
21
|
+
const connection = createLocalProtocolConnection(socket);
|
|
22
|
+
if (head.byteLength > 0) {
|
|
23
|
+
connection.receive(head);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
await new Promise((resolveListen) => {
|
|
27
|
+
server.listen(port, '127.0.0.1', resolveListen);
|
|
28
|
+
});
|
|
29
|
+
const actualPort = serverPort(server);
|
|
30
|
+
return {
|
|
31
|
+
port: actualPort,
|
|
32
|
+
healthUrl: `http://127.0.0.1:${actualPort}/health`,
|
|
33
|
+
url: `ws://127.0.0.1:${actualPort}/protocol`,
|
|
34
|
+
close() {
|
|
35
|
+
return new Promise((resolveClose, rejectClose) => {
|
|
36
|
+
server.close((error) => {
|
|
37
|
+
if (error !== undefined) {
|
|
38
|
+
rejectClose(error);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
resolveClose();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export async function runLocalWebSocketAdapterTest(options) {
|
|
48
|
+
const socket = new WebSocket(options.url);
|
|
49
|
+
const request = {
|
|
50
|
+
kind: 'run-protocol',
|
|
51
|
+
options: options.protocolOptions,
|
|
52
|
+
};
|
|
53
|
+
await new Promise((resolveOpen, rejectOpen) => {
|
|
54
|
+
socket.addEventListener('open', () => resolveOpen(), { once: true });
|
|
55
|
+
socket.addEventListener('error', () => rejectOpen(new Error(`Could not connect to ${options.url}`)), { once: true });
|
|
56
|
+
});
|
|
57
|
+
const response = new Promise((resolveMessage, rejectMessage) => {
|
|
58
|
+
socket.addEventListener('message', (event) => {
|
|
59
|
+
try {
|
|
60
|
+
resolveMessage(JSON.parse(String(event.data)));
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
rejectMessage(error);
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
socket.close();
|
|
67
|
+
}
|
|
68
|
+
}, { once: true });
|
|
69
|
+
socket.addEventListener('error', () => rejectMessage(new Error(`WebSocket error from ${options.url}`)), { once: true });
|
|
70
|
+
});
|
|
71
|
+
socket.send(JSON.stringify(request));
|
|
72
|
+
return response;
|
|
73
|
+
}
|
|
74
|
+
function acceptWebSocket(request, socket) {
|
|
75
|
+
const key = request.headers['sec-websocket-key'];
|
|
76
|
+
if (typeof key !== 'string') {
|
|
77
|
+
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
socket.write([
|
|
81
|
+
'HTTP/1.1 101 Switching Protocols',
|
|
82
|
+
'Upgrade: websocket',
|
|
83
|
+
'Connection: Upgrade',
|
|
84
|
+
`Sec-WebSocket-Accept: ${webSocketAcceptKey(key)}`,
|
|
85
|
+
'\r\n',
|
|
86
|
+
].join('\r\n'));
|
|
87
|
+
}
|
|
88
|
+
function createLocalProtocolConnection(socket) {
|
|
89
|
+
let pending = new Uint8Array();
|
|
90
|
+
socket.on('data', (chunk) => {
|
|
91
|
+
receive(chunk);
|
|
92
|
+
});
|
|
93
|
+
socket.on('error', () => {
|
|
94
|
+
socket.destroy();
|
|
95
|
+
});
|
|
96
|
+
function receive(chunk) {
|
|
97
|
+
pending = concatBytes(pending, chunk);
|
|
98
|
+
let result = readWebSocketFrame(pending);
|
|
99
|
+
while (result.kind === 'frame') {
|
|
100
|
+
const frame = result.frame;
|
|
101
|
+
pending = pending.slice(frame.bytesRead);
|
|
102
|
+
if (frame.opcode === 8) {
|
|
103
|
+
socket.end(encodeWebSocketCloseFrame());
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (frame.opcode === 1) {
|
|
107
|
+
handleProtocolText(socket, frame.text);
|
|
108
|
+
}
|
|
109
|
+
result = readWebSocketFrame(pending);
|
|
110
|
+
}
|
|
111
|
+
if (result.kind === 'invalid') {
|
|
112
|
+
// Malformed frame: close the connection instead of crashing the process.
|
|
113
|
+
socket.end(encodeWebSocketCloseFrame());
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return { receive };
|
|
117
|
+
}
|
|
118
|
+
function handleProtocolText(socket, text) {
|
|
119
|
+
try {
|
|
120
|
+
const request = JSON.parse(text);
|
|
121
|
+
if (request.kind !== 'run-protocol') {
|
|
122
|
+
throw new Error('Unsupported local WebSocket request');
|
|
123
|
+
}
|
|
124
|
+
const replay = createDeterministicMockNetworkAdapter(request.options).exportReplay();
|
|
125
|
+
socket.write(encodeWebSocketTextFrame(JSON.stringify(replay)));
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
socket.write(encodeWebSocketTextFrame(JSON.stringify({
|
|
129
|
+
kind: 'error',
|
|
130
|
+
message: error instanceof Error ? error.message : String(error),
|
|
131
|
+
})));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function concatBytes(left, right) {
|
|
135
|
+
const output = new Uint8Array(left.byteLength + right.byteLength);
|
|
136
|
+
output.set(left, 0);
|
|
137
|
+
output.set(right, left.byteLength);
|
|
138
|
+
return output;
|
|
139
|
+
}
|
|
140
|
+
function serverPort(server) {
|
|
141
|
+
const address = server.address();
|
|
142
|
+
if (address === null || typeof address === 'string') {
|
|
143
|
+
throw new Error('Could not determine local WebSocket server port');
|
|
144
|
+
}
|
|
145
|
+
return address.port;
|
|
146
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { ConfirmedInputFrame, EncodedInput } from './input-authority.js';
|
|
2
|
+
import type { DeterministicSessionConfig, OfflineSession } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Server-ordered match logs + offline re-simulation verification (G3).
|
|
5
|
+
*
|
|
6
|
+
* The authority sees and orders every input; with `preserveFullHistory` it can
|
|
7
|
+
* export the COMPLETE confirmed-input log from tick 0 ({@link SyncplayMatchLog}
|
|
8
|
+
* via `DeterministicAuthorityRoom.matchLog()`). Re-simulating that log through
|
|
9
|
+
* the game's deterministic session is THE anti-cheat for deterministic systems
|
|
10
|
+
* (Quantum doctrine; Rune's server re-execution): a client cannot forge a
|
|
11
|
+
* score without forging inputs the server itself ordered. This is strictly
|
|
12
|
+
* stronger than `verifyScoredReplay`, which trusts a CLIENT-exported
|
|
13
|
+
* ReplayFile — a fully self-consistent fabricated replay passes that; a match
|
|
14
|
+
* log cannot be fabricated by a client at all.
|
|
15
|
+
*
|
|
16
|
+
* The verifier is transport/storage-agnostic: the submission pipeline persists
|
|
17
|
+
* logs (mp-room-server persists them in the restorable room record) and runs
|
|
18
|
+
* this wherever the game bundle can execute (worker, verification service).
|
|
19
|
+
*/
|
|
20
|
+
export interface SyncplayMatchLog {
|
|
21
|
+
readonly v: 1;
|
|
22
|
+
readonly seed: number;
|
|
23
|
+
readonly tickRateHz: number;
|
|
24
|
+
readonly playerCount: number;
|
|
25
|
+
readonly hardToleranceTicks: number;
|
|
26
|
+
readonly confirmedThrough: number;
|
|
27
|
+
/** Every playerId ever seated, by slot — score attribution. */
|
|
28
|
+
readonly everSlots: Readonly<Record<string, number>>;
|
|
29
|
+
/** The complete server-ordered confirmed frames, tick 0 .. confirmedThrough. */
|
|
30
|
+
readonly frames: readonly ConfirmedInputFrame[];
|
|
31
|
+
}
|
|
32
|
+
export interface ScoredMatchLogVerificationConfig<State, Input> {
|
|
33
|
+
readonly log: SyncplayMatchLog;
|
|
34
|
+
/**
|
|
35
|
+
* The game's session factory — the same shape `createNetworkedSyncplayClient`
|
|
36
|
+
* takes, minus seed/playerCount (supplied from the log so the verifier can
|
|
37
|
+
* never be pointed at the wrong descriptor).
|
|
38
|
+
*/
|
|
39
|
+
readonly createSession: (params: {
|
|
40
|
+
seed: number;
|
|
41
|
+
playerCount: number;
|
|
42
|
+
}) => OfflineSession<State, Input>;
|
|
43
|
+
/** Decode a wire input; MUST map the neutral substitute (canonical null) to the default. */
|
|
44
|
+
readonly decodeInput: (encoded: EncodedInput) => Input;
|
|
45
|
+
readonly submittedScore: number;
|
|
46
|
+
/** Game-supplied: read the canonical score out of the final state. */
|
|
47
|
+
readonly extractScore: (finalState: State) => number;
|
|
48
|
+
/**
|
|
49
|
+
* Optional cross-check against the quorum: the state checksum clients
|
|
50
|
+
* reported at `confirmedThrough` (from checksum-reports / the validated
|
|
51
|
+
* snapshot). A mismatch means the server log and the clients' sim disagree —
|
|
52
|
+
* either a desynced match or a non-deterministic game build.
|
|
53
|
+
*/
|
|
54
|
+
readonly expectedFinalChecksum?: string;
|
|
55
|
+
}
|
|
56
|
+
export type ScoredMatchLogRejection = 'log-not-contiguous' | 'checksum-mismatch' | 'score-mismatch';
|
|
57
|
+
export interface ScoredMatchLogVerdict {
|
|
58
|
+
readonly ok: boolean;
|
|
59
|
+
readonly reason?: ScoredMatchLogRejection;
|
|
60
|
+
readonly replayScore?: number;
|
|
61
|
+
readonly submittedScore: number;
|
|
62
|
+
readonly framesReplayed: number;
|
|
63
|
+
readonly finalChecksum?: string;
|
|
64
|
+
}
|
|
65
|
+
export declare function verifyScoredMatchLog<State, Input>(config: ScoredMatchLogVerificationConfig<State, Input>): ScoredMatchLogVerdict;
|
|
66
|
+
/**
|
|
67
|
+
* Convenience: build the verifier's session factory from a full
|
|
68
|
+
* DeterministicSessionConfig (creators usually have one of these), overriding
|
|
69
|
+
* seed/playerCount from the log.
|
|
70
|
+
*/
|
|
71
|
+
export declare function matchLogSessionFactory<State, Input>(base: Omit<DeterministicSessionConfig<State, Input>, 'randomSeed' | 'playerCount'>): (params: {
|
|
72
|
+
seed: number;
|
|
73
|
+
playerCount: number;
|
|
74
|
+
}) => OfflineSession<State, Input>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createOfflineSession } from './offline-session.js';
|
|
2
|
+
import { defaultChecksum } from './canonical.js';
|
|
3
|
+
export function verifyScoredMatchLog(config) {
|
|
4
|
+
const { log } = config;
|
|
5
|
+
// The log must be the complete, gap-free tick 0..confirmedThrough sequence —
|
|
6
|
+
// a truncated or reordered log must never verify a score.
|
|
7
|
+
if (log.frames.length !== log.confirmedThrough + 1 || log.frames.some((frame, index) => frame.tick !== index)) {
|
|
8
|
+
return { ok: false, reason: 'log-not-contiguous', submittedScore: config.submittedScore, framesReplayed: 0 };
|
|
9
|
+
}
|
|
10
|
+
const session = config.createSession({ seed: log.seed, playerCount: log.playerCount });
|
|
11
|
+
for (const frame of log.frames) {
|
|
12
|
+
for (let slot = 0; slot < log.playerCount; slot += 1) {
|
|
13
|
+
session.setInput(slot, config.decodeInput(frame.inputs[slot]));
|
|
14
|
+
}
|
|
15
|
+
session.stepFrames(1);
|
|
16
|
+
}
|
|
17
|
+
const finalChecksum = String(defaultChecksum(session.getState()));
|
|
18
|
+
if (config.expectedFinalChecksum !== undefined && finalChecksum !== config.expectedFinalChecksum) {
|
|
19
|
+
return {
|
|
20
|
+
ok: false,
|
|
21
|
+
reason: 'checksum-mismatch',
|
|
22
|
+
submittedScore: config.submittedScore,
|
|
23
|
+
framesReplayed: log.frames.length,
|
|
24
|
+
finalChecksum,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const replayScore = config.extractScore(session.getState());
|
|
28
|
+
if (replayScore !== config.submittedScore) {
|
|
29
|
+
return {
|
|
30
|
+
ok: false,
|
|
31
|
+
reason: 'score-mismatch',
|
|
32
|
+
replayScore,
|
|
33
|
+
submittedScore: config.submittedScore,
|
|
34
|
+
framesReplayed: log.frames.length,
|
|
35
|
+
finalChecksum,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
ok: true,
|
|
40
|
+
replayScore,
|
|
41
|
+
submittedScore: config.submittedScore,
|
|
42
|
+
framesReplayed: log.frames.length,
|
|
43
|
+
finalChecksum,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Convenience: build the verifier's session factory from a full
|
|
48
|
+
* DeterministicSessionConfig (creators usually have one of these), overriding
|
|
49
|
+
* seed/playerCount from the log.
|
|
50
|
+
*/
|
|
51
|
+
export function matchLogSessionFactory(base) {
|
|
52
|
+
return ({ seed, playerCount }) => createOfflineSession({
|
|
53
|
+
...base,
|
|
54
|
+
randomSeed: seed,
|
|
55
|
+
playerCount,
|
|
56
|
+
});
|
|
57
|
+
}
|
package/dist/math.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface DeterministicMath {
|
|
2
|
+
readonly fixedScale: number;
|
|
3
|
+
readonly minFixed: number;
|
|
4
|
+
readonly maxFixed: number;
|
|
5
|
+
toFixed(value: number): number;
|
|
6
|
+
fromFixed(value: number): number;
|
|
7
|
+
add(a: number, b: number): number;
|
|
8
|
+
sub(a: number, b: number): number;
|
|
9
|
+
mul(a: number, b: number): number;
|
|
10
|
+
div(a: number, b: number): number;
|
|
11
|
+
saturatingAdd(a: number, b: number): number;
|
|
12
|
+
saturatingSub(a: number, b: number): number;
|
|
13
|
+
quantize(value: number, engine: number): number;
|
|
14
|
+
/**
|
|
15
|
+
* CHEAP DETERMINISTIC APPROXIMATION, not true sine: piecewise-LINEAR ramp
|
|
16
|
+
* per quadrant (a triangle wave) — ~29% below true sine at 1/8 turn. Right
|
|
17
|
+
* for gameplay oscillation/steering where cross-peer identity matters more
|
|
18
|
+
* than curve shape; wrong for trajectories that must LOOK sinusoidal. A
|
|
19
|
+
* true polynomial approximation is a candidate for the API freeze pass.
|
|
20
|
+
*/
|
|
21
|
+
sinTurns(turns: number): number;
|
|
22
|
+
/** See sinTurns — same triangle-wave approximation, quarter-turn shifted. */
|
|
23
|
+
cosTurns(turns: number): number;
|
|
24
|
+
vec2(x: number, y: number): DeterministicFixedVec2;
|
|
25
|
+
vec2Add(a: DeterministicFixedVec2, b: DeterministicFixedVec2): DeterministicFixedVec2;
|
|
26
|
+
vec2Lerp(a: DeterministicFixedVec2, b: DeterministicFixedVec2, t: number): DeterministicFixedVec2;
|
|
27
|
+
quatFromYawTurns(turns: number): DeterministicFixedQuaternion;
|
|
28
|
+
quatLerp(a: DeterministicFixedQuaternion, b: DeterministicFixedQuaternion, t: number): DeterministicFixedQuaternion;
|
|
29
|
+
clamp(value: number, min: number, max: number): number;
|
|
30
|
+
}
|
|
31
|
+
export interface DeterministicFixedVec2 {
|
|
32
|
+
readonly x: number;
|
|
33
|
+
readonly y: number;
|
|
34
|
+
}
|
|
35
|
+
export interface DeterministicFixedQuaternion {
|
|
36
|
+
readonly x: number;
|
|
37
|
+
readonly y: number;
|
|
38
|
+
readonly z: number;
|
|
39
|
+
readonly w: number;
|
|
40
|
+
}
|
|
41
|
+
export declare function createDeterministicMath(fixedScale?: number, minFixed?: number, maxFixed?: number): DeterministicMath;
|
package/dist/math.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
const DEFAULT_MIN_FIXED = -2_147_483_648;
|
|
2
|
+
const DEFAULT_MAX_FIXED = 2_147_483_647;
|
|
3
|
+
export function createDeterministicMath(fixedScale = 1_000, minFixed = DEFAULT_MIN_FIXED, maxFixed = DEFAULT_MAX_FIXED) {
|
|
4
|
+
if (!Number.isInteger(fixedScale) || fixedScale <= 0) {
|
|
5
|
+
throw new Error('fixedScale must be a positive integer');
|
|
6
|
+
}
|
|
7
|
+
if (!Number.isInteger(minFixed) || !Number.isInteger(maxFixed) || minFixed >= maxFixed) {
|
|
8
|
+
throw new Error('fixed range must be integer min < max');
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
fixedScale,
|
|
12
|
+
minFixed,
|
|
13
|
+
maxFixed,
|
|
14
|
+
toFixed(value) {
|
|
15
|
+
if (!Number.isFinite(value)) {
|
|
16
|
+
throw new Error('Cannot convert a non-finite number to fixed point');
|
|
17
|
+
}
|
|
18
|
+
// normalizeZero: Math.round of a tiny negative yields -0, which the
|
|
19
|
+
// canonical layer tags as {$rdm:'negative-zero'} — keep -0 out of state.
|
|
20
|
+
return normalizeZero(saturate(Math.round(value * fixedScale), minFixed, maxFixed));
|
|
21
|
+
},
|
|
22
|
+
fromFixed(value) {
|
|
23
|
+
if (!Number.isInteger(value)) {
|
|
24
|
+
throw new Error('Fixed point values must be integers');
|
|
25
|
+
}
|
|
26
|
+
return value / fixedScale;
|
|
27
|
+
},
|
|
28
|
+
add(a, b) {
|
|
29
|
+
assertFixed(a);
|
|
30
|
+
assertFixed(b);
|
|
31
|
+
const result = a + b;
|
|
32
|
+
if (result < minFixed || result > maxFixed) {
|
|
33
|
+
throw new Error('Fixed point addition overflow');
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
},
|
|
37
|
+
sub(a, b) {
|
|
38
|
+
assertFixed(a);
|
|
39
|
+
assertFixed(b);
|
|
40
|
+
const result = a - b;
|
|
41
|
+
if (result < minFixed || result > maxFixed) {
|
|
42
|
+
throw new Error('Fixed point subtraction overflow');
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
},
|
|
46
|
+
// Exactness bound: the a*b intermediate is an IEEE double, so results are
|
|
47
|
+
// deterministic everywhere (correctly-rounded *), but INTEGER-EXACT only
|
|
48
|
+
// while |a*b| <= 2^53 — at the default fixedScale 1000 that is operands up
|
|
49
|
+
// to ~95k real units each. Beyond that the product quietly loses low bits
|
|
50
|
+
// (identically on every peer). Keep gameplay magnitudes inside the bound
|
|
51
|
+
// or use a larger fixedScale with smaller ranges. div has the same bound
|
|
52
|
+
// via a*fixedScale.
|
|
53
|
+
mul(a, b) {
|
|
54
|
+
assertFixed(a);
|
|
55
|
+
assertFixed(b);
|
|
56
|
+
return normalizeZero(saturate(Math.trunc((a * b) / fixedScale), minFixed, maxFixed));
|
|
57
|
+
},
|
|
58
|
+
div(a, b) {
|
|
59
|
+
assertFixed(a);
|
|
60
|
+
assertFixed(b);
|
|
61
|
+
if (b === 0) {
|
|
62
|
+
throw new Error('Fixed point division by zero');
|
|
63
|
+
}
|
|
64
|
+
return normalizeZero(saturate(Math.trunc((a * fixedScale) / b), minFixed, maxFixed));
|
|
65
|
+
},
|
|
66
|
+
saturatingAdd(a, b) {
|
|
67
|
+
assertFixed(a);
|
|
68
|
+
assertFixed(b);
|
|
69
|
+
return saturate(a + b, minFixed, maxFixed);
|
|
70
|
+
},
|
|
71
|
+
saturatingSub(a, b) {
|
|
72
|
+
assertFixed(a);
|
|
73
|
+
assertFixed(b);
|
|
74
|
+
return saturate(a - b, minFixed, maxFixed);
|
|
75
|
+
},
|
|
76
|
+
quantize(value, engine) {
|
|
77
|
+
assertFixed(value);
|
|
78
|
+
if (!Number.isInteger(engine) || engine <= 0) {
|
|
79
|
+
throw new Error('engine must be a positive integer');
|
|
80
|
+
}
|
|
81
|
+
return Math.trunc(value / engine) * engine;
|
|
82
|
+
},
|
|
83
|
+
sinTurns(turns) {
|
|
84
|
+
assertFixed(turns);
|
|
85
|
+
return trigLookup(turns, fixedScale, 'sin');
|
|
86
|
+
},
|
|
87
|
+
cosTurns(turns) {
|
|
88
|
+
assertFixed(turns);
|
|
89
|
+
return trigLookup(turns, fixedScale, 'cos');
|
|
90
|
+
},
|
|
91
|
+
vec2(x, y) {
|
|
92
|
+
assertFixed(x);
|
|
93
|
+
assertFixed(y);
|
|
94
|
+
return { x, y };
|
|
95
|
+
},
|
|
96
|
+
vec2Add(a, b) {
|
|
97
|
+
return {
|
|
98
|
+
x: saturate(a.x + b.x, minFixed, maxFixed),
|
|
99
|
+
y: saturate(a.y + b.y, minFixed, maxFixed),
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
vec2Lerp(a, b, t) {
|
|
103
|
+
assertFixed(t);
|
|
104
|
+
return {
|
|
105
|
+
x: normalizeZero(a.x + Math.trunc(((b.x - a.x) * t) / fixedScale)),
|
|
106
|
+
y: normalizeZero(a.y + Math.trunc(((b.y - a.y) * t) / fixedScale)),
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
quatFromYawTurns(turns) {
|
|
110
|
+
assertFixed(turns);
|
|
111
|
+
const halfTurns = Math.trunc(turns / 2);
|
|
112
|
+
return {
|
|
113
|
+
x: 0,
|
|
114
|
+
y: 0,
|
|
115
|
+
z: normalizeZero(trigLookup(halfTurns, fixedScale, 'sin')),
|
|
116
|
+
w: normalizeZero(trigLookup(halfTurns, fixedScale, 'cos')),
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
quatLerp(a, b, t) {
|
|
120
|
+
assertFixed(t);
|
|
121
|
+
return {
|
|
122
|
+
x: a.x + Math.trunc(((b.x - a.x) * t) / fixedScale),
|
|
123
|
+
y: a.y + Math.trunc(((b.y - a.y) * t) / fixedScale),
|
|
124
|
+
z: a.z + Math.trunc(((b.z - a.z) * t) / fixedScale),
|
|
125
|
+
w: a.w + Math.trunc(((b.w - a.w) * t) / fixedScale),
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
clamp(value, min, max) {
|
|
129
|
+
return Math.min(max, Math.max(min, value));
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function assertFixed(value) {
|
|
134
|
+
if (!Number.isInteger(value)) {
|
|
135
|
+
throw new Error('Fixed point values must be integers');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function saturate(value, min, max) {
|
|
139
|
+
return Math.min(max, Math.max(min, value));
|
|
140
|
+
}
|
|
141
|
+
function trigLookup(turns, fixedScale, op) {
|
|
142
|
+
const normalized = ((turns % fixedScale) + fixedScale) % fixedScale;
|
|
143
|
+
const quadrant = Math.trunc((normalized * 4) / fixedScale) % 4;
|
|
144
|
+
const offset = normalized - Math.trunc((quadrant * fixedScale) / 4);
|
|
145
|
+
const ramp = Math.trunc((offset * fixedScale * 4) / fixedScale);
|
|
146
|
+
const sin = quadrant === 0 ? ramp : quadrant === 1 ? fixedScale - ramp : quadrant === 2 ? -ramp : -fixedScale + ramp;
|
|
147
|
+
if (op === 'sin') {
|
|
148
|
+
return normalizeZero(sin);
|
|
149
|
+
}
|
|
150
|
+
return trigLookup(normalized + Math.trunc(fixedScale / 4), fixedScale, 'sin');
|
|
151
|
+
}
|
|
152
|
+
function normalizeZero(value) {
|
|
153
|
+
return Object.is(value, -0) ? 0 : value;
|
|
154
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ProtocolRollbackLabOptions, type ProtocolRollbackLabResult } from './protocol-lab.js';
|
|
2
|
+
export interface DeterministicMockNetworkAdapter {
|
|
3
|
+
readonly options: ProtocolRollbackLabOptions;
|
|
4
|
+
run(): ProtocolRollbackLabResult;
|
|
5
|
+
exportReplay(): DeterministicMockNetworkReplay;
|
|
6
|
+
}
|
|
7
|
+
export interface DeterministicMockNetworkReplay {
|
|
8
|
+
readonly formatVersion: 1;
|
|
9
|
+
readonly kind: 'deterministic-mock-network';
|
|
10
|
+
readonly options: ProtocolRollbackLabOptions;
|
|
11
|
+
readonly result: ProtocolRollbackLabResult;
|
|
12
|
+
}
|
|
13
|
+
export declare function createDeterministicMockNetworkAdapter(options: ProtocolRollbackLabOptions): DeterministicMockNetworkAdapter;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { cloneCanonical } from './canonical.js';
|
|
2
|
+
import { runProtocolRollbackLab } from './protocol-lab.js';
|
|
3
|
+
export function createDeterministicMockNetworkAdapter(options) {
|
|
4
|
+
const stableOptions = cloneCanonical(options);
|
|
5
|
+
let result;
|
|
6
|
+
return {
|
|
7
|
+
options: stableOptions,
|
|
8
|
+
run() {
|
|
9
|
+
result = runProtocolRollbackLab(stableOptions);
|
|
10
|
+
return cloneCanonical(result);
|
|
11
|
+
},
|
|
12
|
+
exportReplay() {
|
|
13
|
+
const replayResult = result ?? runProtocolRollbackLab(stableOptions);
|
|
14
|
+
result = replayResult;
|
|
15
|
+
return {
|
|
16
|
+
formatVersion: 1,
|
|
17
|
+
kind: 'deterministic-mock-network',
|
|
18
|
+
options: cloneCanonical(stableOptions),
|
|
19
|
+
result: cloneCanonical(replayResult),
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { type QuantizedFpsAim } from './deterministic-primitives.js';
|
|
2
|
+
export interface DeterministicKccBody {
|
|
3
|
+
readonly x: number;
|
|
4
|
+
readonly y: number;
|
|
5
|
+
readonly vx: number;
|
|
6
|
+
readonly vy: number;
|
|
7
|
+
readonly grounded: boolean;
|
|
8
|
+
readonly coyoteFrames: number;
|
|
9
|
+
readonly jumpBufferFrames: number;
|
|
10
|
+
readonly wallSlideSide: -1 | 0 | 1;
|
|
11
|
+
readonly platformId?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface DeterministicKccInput {
|
|
14
|
+
readonly moveX: number;
|
|
15
|
+
readonly jumpPressed?: boolean;
|
|
16
|
+
readonly dropPressed?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface DeterministicSlope {
|
|
19
|
+
readonly id: string;
|
|
20
|
+
readonly minX: number;
|
|
21
|
+
readonly maxX: number;
|
|
22
|
+
readonly baseY: number;
|
|
23
|
+
readonly risePerUnit: number;
|
|
24
|
+
}
|
|
25
|
+
export interface DeterministicMovementStep {
|
|
26
|
+
readonly id: string;
|
|
27
|
+
readonly x: number;
|
|
28
|
+
readonly height: number;
|
|
29
|
+
}
|
|
30
|
+
export interface DeterministicMovingPlatform {
|
|
31
|
+
readonly id: string;
|
|
32
|
+
readonly minX: number;
|
|
33
|
+
readonly maxX: number;
|
|
34
|
+
readonly y: number;
|
|
35
|
+
readonly dxPerFrame: number;
|
|
36
|
+
readonly dyPerFrame?: number;
|
|
37
|
+
}
|
|
38
|
+
export interface DeterministicOneWayPlatform {
|
|
39
|
+
readonly id: string;
|
|
40
|
+
readonly minX: number;
|
|
41
|
+
readonly maxX: number;
|
|
42
|
+
readonly y: number;
|
|
43
|
+
}
|
|
44
|
+
export interface DeterministicMovementWorld {
|
|
45
|
+
readonly minX: number;
|
|
46
|
+
readonly maxX: number;
|
|
47
|
+
readonly minY: number;
|
|
48
|
+
readonly maxY: number;
|
|
49
|
+
readonly walls: readonly number[];
|
|
50
|
+
readonly slopes: readonly DeterministicSlope[];
|
|
51
|
+
readonly steps: readonly DeterministicMovementStep[];
|
|
52
|
+
readonly movingPlatforms: readonly DeterministicMovingPlatform[];
|
|
53
|
+
readonly oneWayPlatforms: readonly DeterministicOneWayPlatform[];
|
|
54
|
+
}
|
|
55
|
+
export interface DeterministicKccStepResult {
|
|
56
|
+
readonly body: DeterministicKccBody;
|
|
57
|
+
readonly events: readonly string[];
|
|
58
|
+
}
|
|
59
|
+
export interface DeterministicTopDownBody {
|
|
60
|
+
readonly x: number;
|
|
61
|
+
readonly y: number;
|
|
62
|
+
readonly vx: number;
|
|
63
|
+
readonly vy: number;
|
|
64
|
+
}
|
|
65
|
+
export interface DeterministicTopDownInput {
|
|
66
|
+
readonly dx: number;
|
|
67
|
+
readonly dy: number;
|
|
68
|
+
readonly navDx?: number;
|
|
69
|
+
readonly navDy?: number;
|
|
70
|
+
}
|
|
71
|
+
export interface DeterministicTopDownBounds {
|
|
72
|
+
readonly minX: number;
|
|
73
|
+
readonly maxX: number;
|
|
74
|
+
readonly minY: number;
|
|
75
|
+
readonly maxY: number;
|
|
76
|
+
}
|
|
77
|
+
export interface DeterministicFpsAimState extends QuantizedFpsAim {
|
|
78
|
+
readonly recoil: number;
|
|
79
|
+
}
|
|
80
|
+
export interface DeterministicFpsInput {
|
|
81
|
+
readonly yawDelta: number;
|
|
82
|
+
readonly pitchDelta: number;
|
|
83
|
+
readonly fire?: boolean;
|
|
84
|
+
}
|
|
85
|
+
export interface DeterministicTargetSnapshot {
|
|
86
|
+
readonly frame: number;
|
|
87
|
+
readonly targetId: string;
|
|
88
|
+
readonly x: number;
|
|
89
|
+
readonly y: number;
|
|
90
|
+
readonly radius?: number;
|
|
91
|
+
}
|
|
92
|
+
export interface DeterministicLagCompensatedShot {
|
|
93
|
+
readonly shotFrame: number;
|
|
94
|
+
readonly latencyFrames: number;
|
|
95
|
+
readonly shooterX: number;
|
|
96
|
+
readonly shooterY: number;
|
|
97
|
+
readonly directionX: number;
|
|
98
|
+
readonly directionY: number;
|
|
99
|
+
readonly maxDistance: number;
|
|
100
|
+
readonly targetId?: string;
|
|
101
|
+
readonly maxHistoryFrames?: number;
|
|
102
|
+
}
|
|
103
|
+
export interface DeterministicLagCompensatedHitValidation {
|
|
104
|
+
readonly accepted: boolean;
|
|
105
|
+
readonly reason: 'hit' | 'miss' | 'stale-frame' | 'future-frame' | 'no-target' | 'invalid-shot';
|
|
106
|
+
readonly rewindFrame: number;
|
|
107
|
+
readonly targetId?: string;
|
|
108
|
+
readonly hitTargetId?: string;
|
|
109
|
+
readonly interpolated: boolean;
|
|
110
|
+
readonly distance: number;
|
|
111
|
+
readonly checksum: string;
|
|
112
|
+
}
|
|
113
|
+
export interface DeterministicMovementSimulationMetrics {
|
|
114
|
+
readonly checksum: string;
|
|
115
|
+
readonly slopes: number;
|
|
116
|
+
readonly steps: number;
|
|
117
|
+
readonly ledges: number;
|
|
118
|
+
readonly platformRides: number;
|
|
119
|
+
readonly elevatorRides: number;
|
|
120
|
+
readonly oneWayDrops: number;
|
|
121
|
+
readonly wallSlides: number;
|
|
122
|
+
readonly bufferedJumps: number;
|
|
123
|
+
readonly coyoteJumps: number;
|
|
124
|
+
readonly topDownSlides: number;
|
|
125
|
+
readonly navSteeringFrames: number;
|
|
126
|
+
readonly hitscanOriginsStable: boolean;
|
|
127
|
+
readonly recoilFrames: number;
|
|
128
|
+
readonly rewindHits: number;
|
|
129
|
+
readonly rewindValidatedHits: number;
|
|
130
|
+
readonly rewindMisses: number;
|
|
131
|
+
readonly rewindRejectedStale: number;
|
|
132
|
+
readonly rewindRejectedFuture: number;
|
|
133
|
+
readonly rewindInterpolated: number;
|
|
134
|
+
readonly rewindValidationChecksum: string;
|
|
135
|
+
}
|
|
136
|
+
export interface DeterministicMovementCrowdMetrics {
|
|
137
|
+
readonly checksum: string;
|
|
138
|
+
readonly players: number;
|
|
139
|
+
readonly frames: number;
|
|
140
|
+
readonly humanInputSlots: number;
|
|
141
|
+
readonly botInputSlots: number;
|
|
142
|
+
readonly predictionCorrections: number;
|
|
143
|
+
readonly rollbackEquivalent: boolean;
|
|
144
|
+
}
|
|
145
|
+
export declare function stepDeterministicKcc2D(body: DeterministicKccBody, input: DeterministicKccInput, world: DeterministicMovementWorld, frame: number): DeterministicKccStepResult;
|
|
146
|
+
export declare function stepDeterministicTopDownMover(body: DeterministicTopDownBody, input: DeterministicTopDownInput, bounds: DeterministicTopDownBounds): {
|
|
147
|
+
readonly body: DeterministicTopDownBody;
|
|
148
|
+
readonly slid: boolean;
|
|
149
|
+
readonly navSteered: boolean;
|
|
150
|
+
};
|
|
151
|
+
export declare function stepDeterministicFpsAim(previous: DeterministicFpsAimState, input: DeterministicFpsInput, seed: number, frame: number): DeterministicFpsAimState;
|
|
152
|
+
export declare function rewindDeterministicTargets(snapshots: readonly DeterministicTargetSnapshot[], targetFrame: number): readonly DeterministicTargetSnapshot[];
|
|
153
|
+
export declare function validateDeterministicLagCompensatedHit(snapshots: readonly DeterministicTargetSnapshot[], shot: DeterministicLagCompensatedShot): DeterministicLagCompensatedHitValidation;
|
|
154
|
+
export declare function runDeterministicMovementSimulation(frames: number, seed: number, restoreFrame?: number): DeterministicMovementSimulationMetrics;
|
|
155
|
+
export declare function runDeterministicMovementCrowdSimulation(players: number, frames: number, seed: number, restoreFrame?: number): DeterministicMovementCrowdMetrics;
|