rollback-netcode 0.0.4
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/LICENSE +21 -0
- package/README.md +140 -0
- package/dist/debug.d.ts +29 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/debug.js +56 -0
- package/dist/debug.js.map +1 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/encoding.d.ts +80 -0
- package/dist/protocol/encoding.d.ts.map +1 -0
- package/dist/protocol/encoding.js +992 -0
- package/dist/protocol/encoding.js.map +1 -0
- package/dist/protocol/messages.d.ts +271 -0
- package/dist/protocol/messages.d.ts.map +1 -0
- package/dist/protocol/messages.js +114 -0
- package/dist/protocol/messages.js.map +1 -0
- package/dist/rollback/engine.d.ts +261 -0
- package/dist/rollback/engine.d.ts.map +1 -0
- package/dist/rollback/engine.js +543 -0
- package/dist/rollback/engine.js.map +1 -0
- package/dist/rollback/input-buffer.d.ts +225 -0
- package/dist/rollback/input-buffer.d.ts.map +1 -0
- package/dist/rollback/input-buffer.js +483 -0
- package/dist/rollback/input-buffer.js.map +1 -0
- package/dist/rollback/snapshot-buffer.d.ts +119 -0
- package/dist/rollback/snapshot-buffer.d.ts.map +1 -0
- package/dist/rollback/snapshot-buffer.js +256 -0
- package/dist/rollback/snapshot-buffer.js.map +1 -0
- package/dist/session/desync-manager.d.ts +106 -0
- package/dist/session/desync-manager.d.ts.map +1 -0
- package/dist/session/desync-manager.js +136 -0
- package/dist/session/desync-manager.js.map +1 -0
- package/dist/session/lag-monitor.d.ts +69 -0
- package/dist/session/lag-monitor.d.ts.map +1 -0
- package/dist/session/lag-monitor.js +74 -0
- package/dist/session/lag-monitor.js.map +1 -0
- package/dist/session/message-builders.d.ts +86 -0
- package/dist/session/message-builders.d.ts.map +1 -0
- package/dist/session/message-builders.js +199 -0
- package/dist/session/message-builders.js.map +1 -0
- package/dist/session/message-router.d.ts +61 -0
- package/dist/session/message-router.d.ts.map +1 -0
- package/dist/session/message-router.js +105 -0
- package/dist/session/message-router.js.map +1 -0
- package/dist/session/player-manager.d.ts +100 -0
- package/dist/session/player-manager.d.ts.map +1 -0
- package/dist/session/player-manager.js +160 -0
- package/dist/session/player-manager.js.map +1 -0
- package/dist/session/session.d.ts +379 -0
- package/dist/session/session.d.ts.map +1 -0
- package/dist/session/session.js +1294 -0
- package/dist/session/session.js.map +1 -0
- package/dist/session/topology.d.ts +66 -0
- package/dist/session/topology.d.ts.map +1 -0
- package/dist/session/topology.js +72 -0
- package/dist/session/topology.js.map +1 -0
- package/dist/transport/adapter.d.ts +99 -0
- package/dist/transport/adapter.d.ts.map +1 -0
- package/dist/transport/adapter.js +8 -0
- package/dist/transport/adapter.js.map +1 -0
- package/dist/transport/local.d.ts +192 -0
- package/dist/transport/local.d.ts.map +1 -0
- package/dist/transport/local.js +435 -0
- package/dist/transport/local.js.map +1 -0
- package/dist/transport/transforming.d.ts +177 -0
- package/dist/transport/transforming.d.ts.map +1 -0
- package/dist/transport/transforming.js +407 -0
- package/dist/transport/transforming.js.map +1 -0
- package/dist/transport/webrtc.d.ts +285 -0
- package/dist/transport/webrtc.d.ts.map +1 -0
- package/dist/transport/webrtc.js +734 -0
- package/dist/transport/webrtc.js.map +1 -0
- package/dist/types.d.ts +394 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +256 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +59 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +93 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core rollback netcode engine.
|
|
3
|
+
*
|
|
4
|
+
* Handles the rollback algorithm: detecting mispredictions,
|
|
5
|
+
* restoring state, and resimulating forward.
|
|
6
|
+
*/
|
|
7
|
+
import type { Game, InputPredictor, PlayerId, PlayerTimeline, Tick, TickResult } from "../types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Callback for player lifecycle events during resimulation.
|
|
10
|
+
*/
|
|
11
|
+
export type PlayerLifecycleCallback = (playerId: PlayerId, tick: Tick) => void;
|
|
12
|
+
/**
|
|
13
|
+
* Configuration for the rollback engine.
|
|
14
|
+
*/
|
|
15
|
+
export interface RollbackEngineConfig {
|
|
16
|
+
/** The game instance to control */
|
|
17
|
+
game: Game;
|
|
18
|
+
/** The local player's ID */
|
|
19
|
+
localPlayerId: PlayerId;
|
|
20
|
+
/** Number of snapshots to keep in history */
|
|
21
|
+
snapshotHistorySize?: number;
|
|
22
|
+
/** Maximum ticks to speculate ahead without confirmed inputs */
|
|
23
|
+
maxSpeculationTicks?: number;
|
|
24
|
+
/** Input predictor for remote players */
|
|
25
|
+
inputPredictor?: InputPredictor<Uint8Array>;
|
|
26
|
+
/**
|
|
27
|
+
* Number of ticks to keep before the confirmed tick when pruning.
|
|
28
|
+
* Higher values use more memory but allow rollback further into the past.
|
|
29
|
+
* @default 10
|
|
30
|
+
*/
|
|
31
|
+
pruneBufferTicks?: number;
|
|
32
|
+
/**
|
|
33
|
+
* Callback invoked when a player should be added during resimulation.
|
|
34
|
+
* Called when resimulating past a player's joinTick.
|
|
35
|
+
*/
|
|
36
|
+
onPlayerAddDuringResimulation?: PlayerLifecycleCallback;
|
|
37
|
+
/**
|
|
38
|
+
* Callback invoked when a player should be removed during resimulation.
|
|
39
|
+
* Called when resimulating past a player's leaveTick.
|
|
40
|
+
*/
|
|
41
|
+
onPlayerRemoveDuringResimulation?: PlayerLifecycleCallback;
|
|
42
|
+
/**
|
|
43
|
+
* Callback invoked when a rollback occurs, before resimulation.
|
|
44
|
+
* Receives the tick that we're rolling back to (the restore tick).
|
|
45
|
+
* Use this to clear state that needs to be re-applied during resimulation.
|
|
46
|
+
*/
|
|
47
|
+
onRollback?: (restoreTick: Tick) => void;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Core rollback engine that handles prediction, rollback, and resimulation.
|
|
51
|
+
*/
|
|
52
|
+
export declare class RollbackEngine {
|
|
53
|
+
private readonly game;
|
|
54
|
+
private readonly localPlayerId;
|
|
55
|
+
private readonly snapshotBuffer;
|
|
56
|
+
private readonly inputBuffer;
|
|
57
|
+
private readonly inputPredictor;
|
|
58
|
+
private readonly maxSpeculationTicks;
|
|
59
|
+
private readonly pruneBufferTicks;
|
|
60
|
+
private readonly onPlayerAddDuringResimulation;
|
|
61
|
+
private readonly onPlayerRemoveDuringResimulation;
|
|
62
|
+
private readonly onRollback;
|
|
63
|
+
private _currentTick;
|
|
64
|
+
private _confirmedTick;
|
|
65
|
+
private localInputs;
|
|
66
|
+
/**
|
|
67
|
+
* Create a new rollback engine.
|
|
68
|
+
*/
|
|
69
|
+
constructor(config: RollbackEngineConfig);
|
|
70
|
+
/**
|
|
71
|
+
* The current simulation tick.
|
|
72
|
+
*/
|
|
73
|
+
get currentTick(): Tick;
|
|
74
|
+
/**
|
|
75
|
+
* The lowest confirmed tick across all active players.
|
|
76
|
+
* All inputs up to and including this tick are confirmed.
|
|
77
|
+
*/
|
|
78
|
+
get confirmedTick(): Tick;
|
|
79
|
+
/**
|
|
80
|
+
* Add a player to the simulation.
|
|
81
|
+
*
|
|
82
|
+
* @param playerId - The player's ID
|
|
83
|
+
* @param joinTick - The tick at which they join
|
|
84
|
+
*/
|
|
85
|
+
addPlayer(playerId: PlayerId, joinTick: Tick): void;
|
|
86
|
+
/**
|
|
87
|
+
* Remove a player from the simulation.
|
|
88
|
+
*
|
|
89
|
+
* @param playerId - The player's ID
|
|
90
|
+
* @param leaveTick - The tick at which they leave
|
|
91
|
+
*/
|
|
92
|
+
removePlayer(playerId: PlayerId, leaveTick: Tick): void;
|
|
93
|
+
/**
|
|
94
|
+
* Get the confirmed tick for a specific player.
|
|
95
|
+
* Returns the highest tick for which we have received confirmed input from this player.
|
|
96
|
+
*
|
|
97
|
+
* @param playerId - The player's ID
|
|
98
|
+
* @returns The confirmed tick, or undefined if the player is not tracked
|
|
99
|
+
*/
|
|
100
|
+
getConfirmedTickForPlayer(playerId: PlayerId): Tick | undefined;
|
|
101
|
+
/**
|
|
102
|
+
* Set the local player's input for the current tick.
|
|
103
|
+
* Call this before tick() to set what input the local player uses.
|
|
104
|
+
*
|
|
105
|
+
* @param tick - The tick the input is for
|
|
106
|
+
* @param input - The input data
|
|
107
|
+
*/
|
|
108
|
+
setLocalInput(tick: Tick, input: Uint8Array): void;
|
|
109
|
+
/**
|
|
110
|
+
* Receive a remote player's input.
|
|
111
|
+
*
|
|
112
|
+
* @param playerId - The player's ID
|
|
113
|
+
* @param tick - The tick the input is for
|
|
114
|
+
* @param input - The input data
|
|
115
|
+
*/
|
|
116
|
+
receiveRemoteInput(playerId: PlayerId, tick: Tick, input: Uint8Array): void;
|
|
117
|
+
/**
|
|
118
|
+
* Get the local player's input for a tick.
|
|
119
|
+
*
|
|
120
|
+
* @param tick - The tick to get input for
|
|
121
|
+
* @returns The input, or undefined if not set
|
|
122
|
+
*/
|
|
123
|
+
getLocalInput(tick: Tick): Uint8Array | undefined;
|
|
124
|
+
/**
|
|
125
|
+
* Save the initial snapshot at tick -1.
|
|
126
|
+
* This allows rollback of tick 0 if there's a misprediction.
|
|
127
|
+
* Call this before the first tick() if the engine wasn't initialized via setState().
|
|
128
|
+
*/
|
|
129
|
+
saveInitialSnapshot(): void;
|
|
130
|
+
/**
|
|
131
|
+
* Advance the simulation by one tick.
|
|
132
|
+
*
|
|
133
|
+
* This is the core rollback algorithm:
|
|
134
|
+
* 1. Check for mispredictions among remote players
|
|
135
|
+
* 2. If misprediction found, rollback and resimulate
|
|
136
|
+
* 3. Gather inputs (real or predicted) for all players
|
|
137
|
+
* 4. Step the simulation
|
|
138
|
+
* 5. Save snapshot
|
|
139
|
+
*
|
|
140
|
+
* @returns Result containing tick number and rollback info
|
|
141
|
+
*/
|
|
142
|
+
tick(): TickResult;
|
|
143
|
+
/**
|
|
144
|
+
* Check for mispredictions and rollback if found.
|
|
145
|
+
*/
|
|
146
|
+
private checkAndRollback;
|
|
147
|
+
/**
|
|
148
|
+
* Handle player add/remove lifecycle events at a specific tick during resimulation.
|
|
149
|
+
* This ensures the game layer knows about player joins/leaves when replaying history.
|
|
150
|
+
* Uses O(1) tick-indexed lookups instead of iterating all players.
|
|
151
|
+
*/
|
|
152
|
+
private handlePlayerLifecycleAtTick;
|
|
153
|
+
/**
|
|
154
|
+
* Gather inputs for all active players at a given tick.
|
|
155
|
+
* Uses real inputs when available, predicts otherwise.
|
|
156
|
+
*/
|
|
157
|
+
private gatherInputs;
|
|
158
|
+
/**
|
|
159
|
+
* Update the confirmed tick based on all players' confirmed ticks.
|
|
160
|
+
*/
|
|
161
|
+
private updateConfirmedTick;
|
|
162
|
+
/**
|
|
163
|
+
* Get the hash at a specific tick.
|
|
164
|
+
*
|
|
165
|
+
* @param tick - The tick to get hash for
|
|
166
|
+
* @returns The hash, or undefined if not available
|
|
167
|
+
*/
|
|
168
|
+
getHash(tick: Tick): number | undefined;
|
|
169
|
+
/**
|
|
170
|
+
* Get the current game hash.
|
|
171
|
+
*/
|
|
172
|
+
getCurrentHash(): number;
|
|
173
|
+
/**
|
|
174
|
+
* Get the current game state and player timeline for sync.
|
|
175
|
+
*/
|
|
176
|
+
getState(): {
|
|
177
|
+
tick: Tick;
|
|
178
|
+
state: Uint8Array;
|
|
179
|
+
playerTimeline: PlayerTimeline;
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Set the game state from a sync message.
|
|
183
|
+
* Used for late join or desync recovery.
|
|
184
|
+
*
|
|
185
|
+
* @param tick - The tick the state is from
|
|
186
|
+
* @param state - The serialized game state
|
|
187
|
+
* @param playerTimeline - Timeline of player join/leave events
|
|
188
|
+
*/
|
|
189
|
+
setState(tick: Tick, state: Uint8Array, playerTimeline: PlayerTimeline): void;
|
|
190
|
+
/**
|
|
191
|
+
* Reset buffers and tick counters for sync without changing game state.
|
|
192
|
+
*
|
|
193
|
+
* Used by the host when broadcasting sync to all players. The host's game
|
|
194
|
+
* state is already correct, but it needs to reset buffers and tick counters
|
|
195
|
+
* to match the synced state being sent to clients.
|
|
196
|
+
*
|
|
197
|
+
* This is more efficient than setState() when the game state doesn't need
|
|
198
|
+
* to be restored (avoids unnecessary serialize/deserialize round-trip).
|
|
199
|
+
*
|
|
200
|
+
* @param tick - The tick to reset to
|
|
201
|
+
* @param playerTimeline - Timeline of player join/leave events
|
|
202
|
+
*/
|
|
203
|
+
resetForSync(tick: Tick, playerTimeline: PlayerTimeline): void;
|
|
204
|
+
/**
|
|
205
|
+
* Check if we have all inputs for a given tick.
|
|
206
|
+
*
|
|
207
|
+
* @param tick - The tick to check
|
|
208
|
+
* @returns true if all inputs are available
|
|
209
|
+
*/
|
|
210
|
+
hasAllInputsForTick(tick: Tick): boolean;
|
|
211
|
+
/**
|
|
212
|
+
* Check if a tick is "settled" - meaning we've simulated past it
|
|
213
|
+
* and have all confirmed inputs for it.
|
|
214
|
+
*
|
|
215
|
+
* A settled tick's state is stable and won't change from future rollbacks,
|
|
216
|
+
* making it safe to compare hashes for desync detection.
|
|
217
|
+
*
|
|
218
|
+
* @param tick - The tick to check
|
|
219
|
+
* @param currentTick - The current simulation tick
|
|
220
|
+
* @returns true if the tick is settled
|
|
221
|
+
*/
|
|
222
|
+
isTickSettled(tick: Tick, currentTick: Tick): boolean;
|
|
223
|
+
/**
|
|
224
|
+
* Get the number of ticks we're speculating ahead.
|
|
225
|
+
*/
|
|
226
|
+
getSpeculationDistance(): number;
|
|
227
|
+
/**
|
|
228
|
+
* Get all active player IDs at the current tick.
|
|
229
|
+
*/
|
|
230
|
+
getActivePlayers(): PlayerId[];
|
|
231
|
+
/**
|
|
232
|
+
* Get all player IDs.
|
|
233
|
+
*/
|
|
234
|
+
getAllPlayers(): PlayerId[];
|
|
235
|
+
/**
|
|
236
|
+
* Reset the engine to initial state.
|
|
237
|
+
*/
|
|
238
|
+
reset(): void;
|
|
239
|
+
/**
|
|
240
|
+
* Wrap a game operation with error handling.
|
|
241
|
+
* Catches any error and re-throws it wrapped in a GameError with context.
|
|
242
|
+
*/
|
|
243
|
+
private wrapGameOperation;
|
|
244
|
+
/**
|
|
245
|
+
* Call game.step() with error wrapping.
|
|
246
|
+
*/
|
|
247
|
+
private gameStep;
|
|
248
|
+
/**
|
|
249
|
+
* Call game.serialize() with error wrapping.
|
|
250
|
+
*/
|
|
251
|
+
private gameSerialize;
|
|
252
|
+
/**
|
|
253
|
+
* Call game.deserialize() with error wrapping.
|
|
254
|
+
*/
|
|
255
|
+
private gameDeserialize;
|
|
256
|
+
/**
|
|
257
|
+
* Call game.hash() with error wrapping.
|
|
258
|
+
*/
|
|
259
|
+
private gameHash;
|
|
260
|
+
}
|
|
261
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/rollback/engine.ts"],"names":[],"mappings":"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"}
|