@woosh/meep-engine 2.138.0 → 2.138.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/bundle-worker-image-decoder.js +1 -1
- package/build/bundle-worker-terrain.js +1 -1
- package/package.json +1 -1
- package/src/core/assert.d.ts +6 -0
- package/src/core/assert.d.ts.map +1 -1
- package/src/core/assert.js +16 -3
- package/src/core/binary/half_to_float_uint16.js +1 -1
- package/src/core/binary/to_half_float_uint16.d.ts.map +1 -1
- package/src/core/binary/to_half_float_uint16.js +9 -4
- package/src/core/collection/table/RowFirstTableSpec.js +1 -1
- package/src/core/events/signal/Signal.d.ts.map +1 -1
- package/src/core/events/signal/Signal.js +53 -0
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.d.ts +3 -3
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.js +3 -3
- package/src/engine/Clock.d.ts +2 -2
- package/src/engine/Clock.js +2 -2
- package/src/engine/graphics/ecs/highlight/system/RenderableHighlightSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/highlight/system/RenderableHighlightSystem.js +17 -29
- package/src/engine/graphics/ecs/highlight/system/ShadedGeometryHighlightSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/highlight/system/ShadedGeometryHighlightSystem.js +18 -31
- package/src/engine/network/NetworkSession.d.ts +386 -0
- package/src/engine/network/NetworkSession.d.ts.map +1 -0
- package/src/engine/network/NetworkSession.js +1841 -0
- package/src/engine/network/PriorityFetch.d.ts.map +1 -1
- package/src/engine/network/PriorityFetch.js +3 -2
- package/src/engine/network/adapters/QuaternionInterpolationAdapter.d.ts +14 -0
- package/src/engine/network/adapters/QuaternionInterpolationAdapter.d.ts.map +1 -0
- package/src/engine/network/adapters/QuaternionInterpolationAdapter.js +44 -0
- package/src/engine/network/adapters/TransformInterpolationAdapter.d.ts +18 -0
- package/src/engine/network/adapters/TransformInterpolationAdapter.d.ts.map +1 -0
- package/src/engine/network/adapters/TransformInterpolationAdapter.js +79 -0
- package/src/engine/network/adapters/TransformReplicationAdapter.d.ts +37 -0
- package/src/engine/network/adapters/TransformReplicationAdapter.d.ts.map +1 -0
- package/src/engine/network/adapters/TransformReplicationAdapter.js +87 -0
- package/src/engine/network/adapters/Vector3InterpolationAdapter.d.ts +18 -0
- package/src/engine/network/adapters/Vector3InterpolationAdapter.d.ts.map +1 -0
- package/src/engine/network/adapters/Vector3InterpolationAdapter.js +46 -0
- package/src/engine/network/convertPathToURL.js +107 -107
- package/src/engine/network/core/quantize/quantize_float.d.ts +54 -0
- package/src/engine/network/core/quantize/quantize_float.d.ts.map +1 -0
- package/src/engine/network/core/quantize/quantize_float.js +66 -0
- package/src/engine/network/core/quantize/quantize_position.d.ts +44 -0
- package/src/engine/network/core/quantize/quantize_position.d.ts.map +1 -0
- package/src/engine/network/core/quantize/quantize_position.js +54 -0
- package/src/engine/network/core/sequence/ack_bitfield.d.ts +47 -0
- package/src/engine/network/core/sequence/ack_bitfield.d.ts.map +1 -0
- package/src/engine/network/core/sequence/ack_bitfield.js +77 -0
- package/src/engine/network/core/sequence/seq16.d.ts +53 -0
- package/src/engine/network/core/sequence/seq16.d.ts.map +1 -0
- package/src/engine/network/core/sequence/seq16.js +69 -0
- package/src/engine/network/core/sequence/seq32.d.ts +55 -0
- package/src/engine/network/core/sequence/seq32.d.ts.map +1 -0
- package/src/engine/network/core/sequence/seq32.js +73 -0
- package/src/engine/network/diagnostics/BandwidthMeter.d.ts +76 -0
- package/src/engine/network/diagnostics/BandwidthMeter.d.ts.map +1 -0
- package/src/engine/network/diagnostics/BandwidthMeter.js +155 -0
- package/src/engine/network/diagnostics/ReplayLog.d.ts +74 -0
- package/src/engine/network/diagnostics/ReplayLog.d.ts.map +1 -0
- package/src/engine/network/diagnostics/ReplayLog.js +137 -0
- package/src/engine/network/diagnostics/SyncTest.d.ts +74 -0
- package/src/engine/network/diagnostics/SyncTest.d.ts.map +1 -0
- package/src/engine/network/diagnostics/SyncTest.js +151 -0
- package/src/engine/network/ecs/NetworkSystem.d.ts +57 -0
- package/src/engine/network/ecs/NetworkSystem.d.ts.map +1 -0
- package/src/engine/network/ecs/NetworkSystem.js +84 -0
- package/src/engine/network/ecs/components/NetworkIdentity.d.ts +58 -0
- package/src/engine/network/ecs/components/NetworkIdentity.d.ts.map +1 -0
- package/src/engine/network/ecs/components/NetworkIdentity.js +73 -0
- package/src/engine/network/ecs/serialization/NetworkIdentitySerializationAdapter.d.ts +40 -0
- package/src/engine/network/ecs/serialization/NetworkIdentitySerializationAdapter.d.ts.map +1 -0
- package/src/engine/network/ecs/serialization/NetworkIdentitySerializationAdapter.js +64 -0
- package/src/engine/network/orchestrator/NetworkPeer.d.ts +389 -0
- package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -0
- package/src/engine/network/orchestrator/NetworkPeer.js +1107 -0
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts +260 -0
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -0
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +425 -0
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts +217 -0
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts.map +1 -0
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.js +562 -0
- package/src/engine/network/replication/Replicator.d.ts +134 -0
- package/src/engine/network/replication/Replicator.d.ts.map +1 -0
- package/src/engine/network/replication/Replicator.js +334 -0
- package/src/engine/network/replication/ScopeFilter.d.ts +64 -0
- package/src/engine/network/replication/ScopeFilter.d.ts.map +1 -0
- package/src/engine/network/replication/ScopeFilter.js +71 -0
- package/src/engine/network/sim/ActionLog.d.ts +94 -0
- package/src/engine/network/sim/ActionLog.d.ts.map +1 -0
- package/src/engine/network/sim/ActionLog.js +189 -0
- package/src/engine/network/sim/BinaryInterpolationAdapter.d.ts +58 -0
- package/src/engine/network/sim/BinaryInterpolationAdapter.d.ts.map +1 -0
- package/src/engine/network/sim/BinaryInterpolationAdapter.js +56 -0
- package/src/engine/network/sim/InterpolationLog.d.ts +165 -0
- package/src/engine/network/sim/InterpolationLog.d.ts.map +1 -0
- package/src/engine/network/sim/InterpolationLog.js +583 -0
- package/src/engine/network/sim/ReplicatedComponentRegistry.d.ts +59 -0
- package/src/engine/network/sim/ReplicatedComponentRegistry.d.ts.map +1 -0
- package/src/engine/network/sim/ReplicatedComponentRegistry.js +140 -0
- package/src/engine/network/sim/RewindEngine.d.ts +66 -0
- package/src/engine/network/sim/RewindEngine.d.ts.map +1 -0
- package/src/engine/network/sim/RewindEngine.js +182 -0
- package/src/engine/network/sim/SimAction.d.ts +133 -0
- package/src/engine/network/sim/SimAction.d.ts.map +1 -0
- package/src/engine/network/sim/SimAction.js +273 -0
- package/src/engine/network/sim/SimActionExecutor.d.ts +109 -0
- package/src/engine/network/sim/SimActionExecutor.d.ts.map +1 -0
- package/src/engine/network/sim/SimActionExecutor.js +238 -0
- package/src/engine/network/sim/SimActionRegistry.d.ts +60 -0
- package/src/engine/network/sim/SimActionRegistry.d.ts.map +1 -0
- package/src/engine/network/sim/SimActionRegistry.js +128 -0
- package/src/engine/network/sim/SmoothingState.d.ts +87 -0
- package/src/engine/network/sim/SmoothingState.d.ts.map +1 -0
- package/src/engine/network/sim/SmoothingState.js +223 -0
- package/src/engine/network/sim/Snapshotter.d.ts +98 -0
- package/src/engine/network/sim/Snapshotter.d.ts.map +1 -0
- package/src/engine/network/sim/Snapshotter.js +206 -0
- package/src/engine/network/sim/SpeculationLog.d.ts +53 -0
- package/src/engine/network/sim/SpeculationLog.d.ts.map +1 -0
- package/src/engine/network/sim/SpeculationLog.js +84 -0
- package/src/engine/network/state/Baseline.d.ts +48 -0
- package/src/engine/network/state/Baseline.d.ts.map +1 -0
- package/src/engine/network/state/Baseline.js +83 -0
- package/src/engine/network/state/ChangedEntitySet.d.ts +94 -0
- package/src/engine/network/state/ChangedEntitySet.d.ts.map +1 -0
- package/src/engine/network/state/ChangedEntitySet.js +256 -0
- package/src/engine/network/state/InputRing.d.ts +90 -0
- package/src/engine/network/state/InputRing.d.ts.map +1 -0
- package/src/engine/network/state/InputRing.js +173 -0
- package/src/engine/network/state/MutationLedger.d.ts +82 -0
- package/src/engine/network/state/MutationLedger.d.ts.map +1 -0
- package/src/engine/network/state/MutationLedger.js +182 -0
- package/src/engine/network/state/PriorityAccumulator.d.ts +104 -0
- package/src/engine/network/state/PriorityAccumulator.d.ts.map +1 -0
- package/src/engine/network/state/PriorityAccumulator.js +180 -0
- package/src/engine/network/state/ReplicationSlotTable.d.ts +78 -0
- package/src/engine/network/state/ReplicationSlotTable.d.ts.map +1 -0
- package/src/engine/network/state/ReplicationSlotTable.js +211 -0
- package/src/engine/network/time/AdaptiveRenderDelay.d.ts +128 -0
- package/src/engine/network/time/AdaptiveRenderDelay.d.ts.map +1 -0
- package/src/engine/network/time/AdaptiveRenderDelay.js +258 -0
- package/src/engine/network/time/JitterBuffer.d.ts +58 -0
- package/src/engine/network/time/JitterBuffer.d.ts.map +1 -0
- package/src/engine/network/time/JitterBuffer.js +116 -0
- package/src/engine/network/time/TimeDilation.d.ts +49 -0
- package/src/engine/network/time/TimeDilation.d.ts.map +1 -0
- package/src/engine/network/time/TimeDilation.js +62 -0
- package/src/engine/network/time/TimeSync.d.ts +68 -0
- package/src/engine/network/time/TimeSync.d.ts.map +1 -0
- package/src/engine/network/time/TimeSync.js +153 -0
- package/src/engine/network/transport/Channel.d.ts +74 -0
- package/src/engine/network/transport/Channel.d.ts.map +1 -0
- package/src/engine/network/transport/Channel.js +272 -0
- package/src/engine/network/transport/LoopbackTransport.d.ts +59 -0
- package/src/engine/network/transport/LoopbackTransport.d.ts.map +1 -0
- package/src/engine/network/transport/LoopbackTransport.js +194 -0
- package/src/engine/network/transport/ReliableCommandPipeline.d.ts +139 -0
- package/src/engine/network/transport/ReliableCommandPipeline.d.ts.map +1 -0
- package/src/engine/network/transport/ReliableCommandPipeline.js +291 -0
- package/src/engine/network/transport/Transport.d.ts +109 -0
- package/src/engine/network/transport/Transport.d.ts.map +1 -0
- package/src/engine/network/transport/Transport.js +119 -0
- package/src/engine/network/transport/adapters/NodeUDPTransport.d.ts +60 -0
- package/src/engine/network/transport/adapters/NodeUDPTransport.d.ts.map +1 -0
- package/src/engine/network/transport/adapters/NodeUDPTransport.js +206 -0
- package/src/engine/network/transport/adapters/SimulatedTransport.d.ts +110 -0
- package/src/engine/network/transport/adapters/SimulatedTransport.d.ts.map +1 -0
- package/src/engine/network/transport/adapters/SimulatedTransport.js +252 -0
- package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.d.ts +33 -0
- package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.d.ts.map +1 -0
- package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.js +131 -0
- package/src/engine/network/transport/adapters/WebSocketTransport.d.ts +49 -0
- package/src/engine/network/transport/adapters/WebSocketTransport.d.ts.map +1 -0
- package/src/engine/network/transport/adapters/WebSocketTransport.js +180 -0
- package/src/engine/network/transport/adapters/WebTransportTransport.d.ts +73 -0
- package/src/engine/network/transport/adapters/WebTransportTransport.d.ts.map +1 -0
- package/src/engine/network/transport/adapters/WebTransportTransport.js +210 -0
- package/src/engine/network/transport/fragments/FragmentAssembler.d.ts +104 -0
- package/src/engine/network/transport/fragments/FragmentAssembler.d.ts.map +1 -0
- package/src/engine/network/transport/fragments/FragmentAssembler.js +291 -0
- package/src/engine/network/transport/fragments/FragmentRetention.d.ts +103 -0
- package/src/engine/network/transport/fragments/FragmentRetention.d.ts.map +1 -0
- package/src/engine/network/transport/fragments/FragmentRetention.js +194 -0
- package/src/engine/network/transport/fragments/fragment_send.d.ts +53 -0
- package/src/engine/network/transport/fragments/fragment_send.d.ts.map +1 -0
- package/src/engine/network/transport/fragments/fragment_send.js +147 -0
- package/src/engine/network/transport/fragments/packet_size.d.ts +93 -0
- package/src/engine/network/transport/fragments/packet_size.d.ts.map +1 -0
- package/src/engine/network/transport/fragments/packet_size.js +101 -0
- package/src/engine/network/xhr.js +23 -23
- package/src/engine/simulation/Ticker.d.ts +7 -0
- package/src/engine/simulation/Ticker.d.ts.map +1 -1
- package/src/engine/simulation/Ticker.js +15 -4
- package/src/engine/network/DataChannel.js +0 -1210
- package/src/engine/network/RemoteController.d.ts +0 -23
- package/src/engine/network/RemoteController.d.ts.map +0 -1
- package/src/engine/network/RemoteController.js +0 -114
- package/src/engine/network/remoteEditor.d.ts +0 -2
- package/src/engine/network/remoteEditor.d.ts.map +0 -1
- package/src/engine/network/remoteEditor.js +0 -142
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { assert } from "../../../core/assert.js";
|
|
2
|
+
import { BinaryBuffer } from "../../../core/binary/BinaryBuffer.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Per-frame ring of action records.
|
|
6
|
+
*
|
|
7
|
+
* Each frame's `BinaryBuffer` holds a sequence of action records. One record per
|
|
8
|
+
* `SimActionExecutor.execute` call:
|
|
9
|
+
*
|
|
10
|
+
* ```
|
|
11
|
+
* varint: prior_state_count
|
|
12
|
+
* loop:
|
|
13
|
+
* varint: entity_id
|
|
14
|
+
* uint8: component_type_id (assigned by the ReplicatedComponentRegistry)
|
|
15
|
+
* uint32: prior_payload_len
|
|
16
|
+
* bytes: prior_payload (adapter.serialize of the component's prior state)
|
|
17
|
+
* uint8: action_type_id
|
|
18
|
+
* uint8: sender_id (peer that originated the action; local-only,
|
|
19
|
+
* STRIPPED by Replicator before send)
|
|
20
|
+
* uint32: action_payload_len
|
|
21
|
+
* bytes: action_payload (the action's own serialize output)
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* Self-describing: records can be skipped without consulting the action registry,
|
|
25
|
+
* which means rewind code can walk records forward to find boundaries, then
|
|
26
|
+
* iterate backward to apply prior states — no need to instantiate any actions.
|
|
27
|
+
*
|
|
28
|
+
* Note that `sender_id` is recorded in-buffer for local rollback orchestrators
|
|
29
|
+
* (stable-sort tie-breaking by sender on replay) but never crosses the wire —
|
|
30
|
+
* {@link Replicator#pack_for_peer} strips it and the receiver derives sender
|
|
31
|
+
* from the inbound packet's peer_id, so a hostile peer cannot impersonate.
|
|
32
|
+
*
|
|
33
|
+
* Ring depth is set at construction. When the ring fills, the oldest frame's
|
|
34
|
+
* buffer is recycled for the new frame.
|
|
35
|
+
*
|
|
36
|
+
* @author Alex Goldring
|
|
37
|
+
* @copyright Company Named Limited (c) 2025
|
|
38
|
+
*/
|
|
39
|
+
export class ActionLog {
|
|
40
|
+
#buffers;
|
|
41
|
+
#frame_numbers;
|
|
42
|
+
#write_ends;
|
|
43
|
+
#current_frame;
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {{ frame_capacity: number, initial_buffer_size?: number }} options
|
|
48
|
+
*/
|
|
49
|
+
constructor({ frame_capacity, initial_buffer_size = 4096 }) {
|
|
50
|
+
if (!Number.isInteger(frame_capacity) || frame_capacity <= 0) {
|
|
51
|
+
throw new Error(`ActionLog: frame_capacity must be a positive integer, got ${frame_capacity}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @readonly
|
|
56
|
+
* @type {number}
|
|
57
|
+
*/
|
|
58
|
+
this.frame_capacity = frame_capacity;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Per-frame buffers. Index by `frame % frame_capacity`.
|
|
62
|
+
* @type {BinaryBuffer[]}
|
|
63
|
+
* @private
|
|
64
|
+
*/
|
|
65
|
+
this.#buffers = new Array(frame_capacity);
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Per-frame absolute frame numbers (so we can detect "this slot is stale").
|
|
69
|
+
* `-1` means the slot has not yet been used.
|
|
70
|
+
* @type {Int32Array}
|
|
71
|
+
* @private
|
|
72
|
+
*/
|
|
73
|
+
this.#frame_numbers = new Int32Array(frame_capacity).fill(-1);
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Per-frame "write end" position. The buffer's own `position` is reused
|
|
77
|
+
* when actively writing; this is captured at frame_end so readers know
|
|
78
|
+
* where the meaningful data ends.
|
|
79
|
+
* @type {Int32Array}
|
|
80
|
+
* @private
|
|
81
|
+
*/
|
|
82
|
+
this.#write_ends = new Int32Array(frame_capacity);
|
|
83
|
+
|
|
84
|
+
for (let i = 0; i < frame_capacity; i++) {
|
|
85
|
+
this.#buffers[i] = new BinaryBuffer();
|
|
86
|
+
this.#buffers[i].setCapacity(initial_buffer_size);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Frame number currently being written to, or -1 if no frame is open.
|
|
91
|
+
* @type {number}
|
|
92
|
+
* @private
|
|
93
|
+
*/
|
|
94
|
+
this.#current_frame = -1;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Open a new frame for writing. Recycles the oldest slot if the ring is full.
|
|
99
|
+
*
|
|
100
|
+
* @param {number} frame absolute frame number
|
|
101
|
+
*/
|
|
102
|
+
begin_frame(frame) {
|
|
103
|
+
assert.isNonNegativeInteger(frame, 'frame');
|
|
104
|
+
|
|
105
|
+
if (this.#current_frame !== -1) {
|
|
106
|
+
throw new Error(`ActionLog.begin_frame: previous frame ${this.#current_frame} still open; call end_frame first`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const slot = frame % this.frame_capacity;
|
|
110
|
+
const buffer = this.#buffers[slot];
|
|
111
|
+
|
|
112
|
+
buffer.position = 0;
|
|
113
|
+
this.#frame_numbers[slot] = frame;
|
|
114
|
+
this.#current_frame = frame;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Close the currently-open frame.
|
|
119
|
+
*/
|
|
120
|
+
end_frame() {
|
|
121
|
+
if (this.#current_frame === -1) {
|
|
122
|
+
throw new Error(`ActionLog.end_frame: no frame is open`);
|
|
123
|
+
}
|
|
124
|
+
const slot = this.#current_frame % this.frame_capacity;
|
|
125
|
+
this.#write_ends[slot] = this.#buffers[slot].position;
|
|
126
|
+
this.#current_frame = -1;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get the buffer for the currently-open frame for direct writing.
|
|
131
|
+
*
|
|
132
|
+
* @returns {BinaryBuffer}
|
|
133
|
+
*/
|
|
134
|
+
current_buffer() {
|
|
135
|
+
if (this.#current_frame === -1) {
|
|
136
|
+
throw new Error(`ActionLog.current_buffer: no frame is open`);
|
|
137
|
+
}
|
|
138
|
+
return this.#buffers[this.#current_frame % this.frame_capacity];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Has the given frame number ever been written, and is it still in the ring?
|
|
143
|
+
*
|
|
144
|
+
* @param {number} frame
|
|
145
|
+
* @returns {boolean}
|
|
146
|
+
*/
|
|
147
|
+
has_frame(frame) {
|
|
148
|
+
assert.isNonNegativeInteger(frame, 'frame');
|
|
149
|
+
const slot = frame % this.frame_capacity;
|
|
150
|
+
return this.#frame_numbers[slot] === frame;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Read-only access to a closed frame's buffer. Throws if the frame is not in the ring.
|
|
155
|
+
* The buffer's `position` will be set to 0 and its valid byte length is `write_end_for(frame)`.
|
|
156
|
+
*
|
|
157
|
+
* **Only valid for frames that have been closed via {@link end_frame}.** While a
|
|
158
|
+
* frame is open, `__write_ends[slot]` still holds the previous occupant's
|
|
159
|
+
* length — `buffer_for`/`write_end_for` would return mismatched
|
|
160
|
+
* buffer-contents-vs-length and any reader would walk garbage.
|
|
161
|
+
*
|
|
162
|
+
* @param {number} frame
|
|
163
|
+
* @returns {BinaryBuffer}
|
|
164
|
+
*/
|
|
165
|
+
buffer_for(frame) {
|
|
166
|
+
assert.isNonNegativeInteger(frame, 'frame');
|
|
167
|
+
if (!this.has_frame(frame)) {
|
|
168
|
+
throw new Error(`ActionLog.buffer_for: frame ${frame} is not present in the ring`);
|
|
169
|
+
}
|
|
170
|
+
const slot = frame % this.frame_capacity;
|
|
171
|
+
const buffer = this.#buffers[slot];
|
|
172
|
+
buffer.position = 0;
|
|
173
|
+
return buffer;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Number of valid bytes written to the given frame's buffer.
|
|
178
|
+
* @param {number} frame
|
|
179
|
+
* @returns {number}
|
|
180
|
+
*/
|
|
181
|
+
write_end_for(frame) {
|
|
182
|
+
assert.isNonNegativeInteger(frame, 'frame');
|
|
183
|
+
if (!this.has_frame(frame)) {
|
|
184
|
+
throw new Error(`ActionLog.write_end_for: frame ${frame} is not present in the ring`);
|
|
185
|
+
}
|
|
186
|
+
const slot = frame % this.frame_capacity;
|
|
187
|
+
return this.#write_ends[slot];
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The blend math an adapter performs. Read by higher-level wiring to
|
|
3
|
+
* decide whether the adapter is compatible with the current rendering
|
|
4
|
+
* pipeline.
|
|
5
|
+
*/
|
|
6
|
+
export type InterpolationKind = number;
|
|
7
|
+
/**
|
|
8
|
+
* The blend math an adapter performs. Read by higher-level wiring to
|
|
9
|
+
* decide whether the adapter is compatible with the current rendering
|
|
10
|
+
* pipeline.
|
|
11
|
+
*
|
|
12
|
+
* @readonly
|
|
13
|
+
* @enum {number}
|
|
14
|
+
*/
|
|
15
|
+
export const InterpolationKind: Readonly<{
|
|
16
|
+
/** No blending — output is one of the two snapshots exactly. */
|
|
17
|
+
Discrete: 0;
|
|
18
|
+
/** Linear blend at fractional `t ∈ [0, 1]` between two snapshots. */
|
|
19
|
+
Linear: 1;
|
|
20
|
+
/** Cubic between four samples. Reserved; not yet supported. */
|
|
21
|
+
Cubic: 2;
|
|
22
|
+
}>;
|
|
23
|
+
/**
|
|
24
|
+
* Per-component-type interpolator: reads two encoded snapshots of one
|
|
25
|
+
* component from `source`, blends by `t`, writes the result to
|
|
26
|
+
* `out_buffer` in the SAME wire format so the receiver can deserialize
|
|
27
|
+
* directly via the corresponding `BinaryClassSerializationAdapter`.
|
|
28
|
+
*
|
|
29
|
+
* Pair with {@link InterpolationLog}, which resolves
|
|
30
|
+
* `(network_id, type_id, tick) → byte offset` so the adapter only sees
|
|
31
|
+
* offsets.
|
|
32
|
+
*
|
|
33
|
+
* Subclasses override `kind` if their blend math is non-linear; the
|
|
34
|
+
* default is `Linear`.
|
|
35
|
+
*
|
|
36
|
+
* @author Alex Goldring
|
|
37
|
+
* @copyright Company Named Limited (c) 2025
|
|
38
|
+
*/
|
|
39
|
+
export class BinaryInterpolationAdapter {
|
|
40
|
+
/** @type {number} one of {@link InterpolationKind} */
|
|
41
|
+
kind: number;
|
|
42
|
+
/**
|
|
43
|
+
* Advance `out_buffer.position` past the bytes written. `source.position`
|
|
44
|
+
* may be mutated freely (the log restores it).
|
|
45
|
+
*
|
|
46
|
+
* `first_offset === second_offset` is the degenerate "snap to single
|
|
47
|
+
* snapshot" case — implementations don't need to special-case it; the
|
|
48
|
+
* blend at any `t` reduces to the input.
|
|
49
|
+
*
|
|
50
|
+
* @param {BinaryBuffer} out_buffer
|
|
51
|
+
* @param {BinaryBuffer} source
|
|
52
|
+
* @param {number} first_offset byte position of snapshot A
|
|
53
|
+
* @param {number} second_offset byte position of snapshot B
|
|
54
|
+
* @param {number} t blend fraction in [0, 1]; 0 ⇒ A, 1 ⇒ B
|
|
55
|
+
*/
|
|
56
|
+
interpolate(out_buffer: BinaryBuffer, source: BinaryBuffer, first_offset: number, second_offset: number, t: number): void;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=BinaryInterpolationAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BinaryInterpolationAdapter.d.ts","sourceRoot":"","sources":["../../../../../src/engine/network/sim/BinaryInterpolationAdapter.js"],"names":[],"mappings":";;;;;gCAMU,MAAM;AANhB;;;;;;;GAOG;AACH;IACI,gEAAgE;;IAEhE,qEAAqE;;IAErE,+DAA+D;;GAEhE;AAEH;;;;;;;;;;;;;;;GAeG;AACH;IAEI,sDAAsD;IACtD,MADW,MAAM,CACe;IAEhC;;;;;;;;;;;;;OAaG;IACH,0EAJW,MAAM,iBACN,MAAM,KACN,MAAM,QAIhB;CACJ"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The blend math an adapter performs. Read by higher-level wiring to
|
|
3
|
+
* decide whether the adapter is compatible with the current rendering
|
|
4
|
+
* pipeline.
|
|
5
|
+
*
|
|
6
|
+
* @readonly
|
|
7
|
+
* @enum {number}
|
|
8
|
+
*/
|
|
9
|
+
export const InterpolationKind = Object.freeze({
|
|
10
|
+
/** No blending — output is one of the two snapshots exactly. */
|
|
11
|
+
Discrete: 0,
|
|
12
|
+
/** Linear blend at fractional `t ∈ [0, 1]` between two snapshots. */
|
|
13
|
+
Linear: 1,
|
|
14
|
+
/** Cubic between four samples. Reserved; not yet supported. */
|
|
15
|
+
Cubic: 2,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Per-component-type interpolator: reads two encoded snapshots of one
|
|
20
|
+
* component from `source`, blends by `t`, writes the result to
|
|
21
|
+
* `out_buffer` in the SAME wire format so the receiver can deserialize
|
|
22
|
+
* directly via the corresponding `BinaryClassSerializationAdapter`.
|
|
23
|
+
*
|
|
24
|
+
* Pair with {@link InterpolationLog}, which resolves
|
|
25
|
+
* `(network_id, type_id, tick) → byte offset` so the adapter only sees
|
|
26
|
+
* offsets.
|
|
27
|
+
*
|
|
28
|
+
* Subclasses override `kind` if their blend math is non-linear; the
|
|
29
|
+
* default is `Linear`.
|
|
30
|
+
*
|
|
31
|
+
* @author Alex Goldring
|
|
32
|
+
* @copyright Company Named Limited (c) 2025
|
|
33
|
+
*/
|
|
34
|
+
export class BinaryInterpolationAdapter {
|
|
35
|
+
|
|
36
|
+
/** @type {number} one of {@link InterpolationKind} */
|
|
37
|
+
kind = InterpolationKind.Linear;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Advance `out_buffer.position` past the bytes written. `source.position`
|
|
41
|
+
* may be mutated freely (the log restores it).
|
|
42
|
+
*
|
|
43
|
+
* `first_offset === second_offset` is the degenerate "snap to single
|
|
44
|
+
* snapshot" case — implementations don't need to special-case it; the
|
|
45
|
+
* blend at any `t` reduces to the input.
|
|
46
|
+
*
|
|
47
|
+
* @param {BinaryBuffer} out_buffer
|
|
48
|
+
* @param {BinaryBuffer} source
|
|
49
|
+
* @param {number} first_offset byte position of snapshot A
|
|
50
|
+
* @param {number} second_offset byte position of snapshot B
|
|
51
|
+
* @param {number} t blend fraction in [0, 1]; 0 ⇒ A, 1 ⇒ B
|
|
52
|
+
*/
|
|
53
|
+
interpolate(out_buffer, source, first_offset, second_offset, t) {
|
|
54
|
+
throw new Error('BinaryInterpolationAdapter subclasses must override interpolate(out, src, off_a, off_b, t).');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-tick recording of replicated component state for snapshot interpolation.
|
|
3
|
+
*
|
|
4
|
+
* Two ring buffers — both wrap at tick boundaries only:
|
|
5
|
+
*
|
|
6
|
+
* - `__buffer` (BinaryBuffer): raw component bytes, written via `begin_record`
|
|
7
|
+
* / `end_record`. Variable size per record.
|
|
8
|
+
*
|
|
9
|
+
* - `__records` (Uint32Array): the metadata table, holding one row per
|
|
10
|
+
* `(network_id, type_id, byte_offset, byte_length)` plus a bloom-filter
|
|
11
|
+
* header every {@link METADATA_PAGE_SIZE} records. The structure:
|
|
12
|
+
*
|
|
13
|
+
* page = [bloom_0..7] [rec_0_n, _t, _o, _l] ... [rec_38_n, _t, _o, _l]
|
|
14
|
+
*
|
|
15
|
+
* A new page header is written at `begin_tick` and then again every 39
|
|
16
|
+
* records within a tick. The bloom filter is keyed by `network_id`, so a
|
|
17
|
+
* `locate(network_id, type_id)` can skip whole pages with a single 256-bit
|
|
18
|
+
* check before falling back to a per-record scan inside the page. Pages
|
|
19
|
+
* never straddle tick boundaries.
|
|
20
|
+
*
|
|
21
|
+
* Per-tick metadata is small: byte range + word range in `__records` + record
|
|
22
|
+
* count. The oldest tick is tracked explicitly (`__oldest_tick`) so the squash
|
|
23
|
+
* check at each {@link begin_tick} is amortized O(1) — squash the oldest while
|
|
24
|
+
* it overlaps the new write region, advance the pointer, repeat.
|
|
25
|
+
*
|
|
26
|
+
* Pair with {@link BinaryInterpolationAdapter} concrete subclasses (e.g.
|
|
27
|
+
* {@link Vector3InterpolationAdapter}) which know each component type's
|
|
28
|
+
* encoding and blend math.
|
|
29
|
+
*
|
|
30
|
+
* Typical receive-side use:
|
|
31
|
+
* ```
|
|
32
|
+
* onFrameApplied(_, frame_number) {
|
|
33
|
+
* log.begin_tick(frame_number);
|
|
34
|
+
* const buf = log.begin_record(network_id, type_id);
|
|
35
|
+
* adapter.serialize(buf, value);
|
|
36
|
+
* log.end_record();
|
|
37
|
+
* log.end_tick();
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* Typical render-time use:
|
|
42
|
+
* ```
|
|
43
|
+
* const ok = log.interpolate(out_buffer, network_id, type_id, tick_a, tick_b, t, adapter);
|
|
44
|
+
* if (ok) { /* deserialize from out_buffer into the live component }
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @author Alex Goldring
|
|
48
|
+
* @copyright Company Named Limited (c) 2025
|
|
49
|
+
*/
|
|
50
|
+
export class InterpolationLog {
|
|
51
|
+
/**
|
|
52
|
+
* @param {{ buffer_capacity_bytes?: number, records_capacity?: number }} [options]
|
|
53
|
+
* `records_capacity` is in *records* (not bytes); the underlying word
|
|
54
|
+
* array is sized large enough to fit that many records plus their pages'
|
|
55
|
+
* bloom-filter headers.
|
|
56
|
+
*/
|
|
57
|
+
constructor({ buffer_capacity_bytes, records_capacity }?: {
|
|
58
|
+
buffer_capacity_bytes?: number;
|
|
59
|
+
records_capacity?: number;
|
|
60
|
+
});
|
|
61
|
+
/** @readonly @type {number} */
|
|
62
|
+
readonly buffer_capacity: number;
|
|
63
|
+
/** @readonly @type {number} */
|
|
64
|
+
readonly records_capacity: number;
|
|
65
|
+
/**
|
|
66
|
+
* Open a new tick for recording. May wrap either ring at this point — but
|
|
67
|
+
* never mid-tick, so a single tick's data is always contiguous in both
|
|
68
|
+
* the byte buffer and the metadata table. After wrapping, any old tick
|
|
69
|
+
* whose data overlaps the new write region is squashed (in oldest-first
|
|
70
|
+
* order) and `__oldest_tick` advances.
|
|
71
|
+
*
|
|
72
|
+
* @param {number} tick non-negative integer
|
|
73
|
+
*/
|
|
74
|
+
begin_tick(tick: number): void;
|
|
75
|
+
/**
|
|
76
|
+
* Begin recording one component. Returns the underlying byte buffer
|
|
77
|
+
* positioned at the next write slot — the caller writes the component
|
|
78
|
+
* payload directly (e.g. via a `BinaryClassSerializationAdapter.serialize`
|
|
79
|
+
* call), then calls {@link end_record} to finalize the (offset, length).
|
|
80
|
+
*
|
|
81
|
+
* Splitting begin/end (vs. taking a write callback) avoids the per-record
|
|
82
|
+
* closure allocation and lets the caller pass the buffer straight to an
|
|
83
|
+
* adapter without wrapping.
|
|
84
|
+
*
|
|
85
|
+
* @param {number} network_id
|
|
86
|
+
* @param {number} type_id component type id (small integer; matches the registry)
|
|
87
|
+
* @returns {BinaryBuffer} the log's byte buffer, positioned for writing the payload
|
|
88
|
+
*/
|
|
89
|
+
begin_record(network_id: number, type_id: number): BinaryBuffer;
|
|
90
|
+
/**
|
|
91
|
+
* Finalize the in-progress record opened by {@link begin_record}. Reads
|
|
92
|
+
* the buffer's current position to compute the payload length, inserts
|
|
93
|
+
* the network_id into the current page's bloom filter, writes the row to
|
|
94
|
+
* the metadata table, and advances the cursors.
|
|
95
|
+
*/
|
|
96
|
+
end_record(): void;
|
|
97
|
+
/**
|
|
98
|
+
* Close the currently-open tick. Updates the max-observed-tick sizes so
|
|
99
|
+
* the next {@link begin_tick} can size its wrap heuristic correctly.
|
|
100
|
+
*
|
|
101
|
+
* Throws if a record is still open — silently dropping the in-progress
|
|
102
|
+
* record's metadata (offset/length) would leak buffer space and produce
|
|
103
|
+
* stale bytes that no one can locate.
|
|
104
|
+
*/
|
|
105
|
+
end_tick(): void;
|
|
106
|
+
/**
|
|
107
|
+
* @param {number} tick
|
|
108
|
+
* @returns {boolean} true if the tick has live (non-squashed) data
|
|
109
|
+
*/
|
|
110
|
+
has_tick(tick: number): boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Find a component's stored slice within a tick. Walks the tick's pages,
|
|
113
|
+
* bloom-filter-skipping pages that don't contain `network_id`, and
|
|
114
|
+
* scanning records inside any page where the bloom hits.
|
|
115
|
+
*
|
|
116
|
+
* On success, writes `out[out_offset+0] = byte_offset` and
|
|
117
|
+
* `out[out_offset+1] = byte_length`.
|
|
118
|
+
*
|
|
119
|
+
* @param {number[]|Uint32Array} out
|
|
120
|
+
* @param {number} out_offset
|
|
121
|
+
* @param {number} tick
|
|
122
|
+
* @param {number} network_id
|
|
123
|
+
* @param {number} type_id
|
|
124
|
+
* @returns {boolean} false if the tick is missing/squashed or doesn't carry this component
|
|
125
|
+
*/
|
|
126
|
+
locate(out: number[] | Uint32Array, out_offset: number, tick: number, network_id: number, type_id: number): boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Interpolate one component between two ticks via `adapter` and write the
|
|
129
|
+
* result to `out_buffer` at its current position.
|
|
130
|
+
*
|
|
131
|
+
* Outcome by available data:
|
|
132
|
+
* - Both ticks carry the component → adapter blends them at `t`.
|
|
133
|
+
* - Only one carries it → adapter is called with the same offset twice
|
|
134
|
+
* (snap to the surviving snapshot; `t` is irrelevant).
|
|
135
|
+
* - Neither carries it → returns false; `out_buffer` is unchanged.
|
|
136
|
+
*
|
|
137
|
+
* @param {BinaryBuffer} out_buffer
|
|
138
|
+
* @param {number} network_id
|
|
139
|
+
* @param {number} type_id
|
|
140
|
+
* @param {number} tick_a
|
|
141
|
+
* @param {number} tick_b
|
|
142
|
+
* @param {number} t
|
|
143
|
+
* @param {BinaryInterpolationAdapter} adapter
|
|
144
|
+
* @returns {boolean} true if a payload was written; false if neither tick has the component
|
|
145
|
+
*/
|
|
146
|
+
interpolate(out_buffer: BinaryBuffer, network_id: number, type_id: number, tick_a: number, tick_b: number, t: number, adapter: BinaryInterpolationAdapter): boolean;
|
|
147
|
+
/**
|
|
148
|
+
* Number of ticks currently held. Squashed ticks are excluded.
|
|
149
|
+
* @returns {number}
|
|
150
|
+
*/
|
|
151
|
+
size(): number;
|
|
152
|
+
/**
|
|
153
|
+
* Tick number of the oldest live tick, or -1 if empty.
|
|
154
|
+
* @returns {number}
|
|
155
|
+
*/
|
|
156
|
+
oldest_tick(): number;
|
|
157
|
+
/**
|
|
158
|
+
* Drop all recorded ticks. Resets both ring cursors to 0. Useful on
|
|
159
|
+
* reconnect or level transition.
|
|
160
|
+
*/
|
|
161
|
+
clear(): void;
|
|
162
|
+
#private;
|
|
163
|
+
}
|
|
164
|
+
import { BinaryBuffer } from "../../../core/binary/BinaryBuffer.js";
|
|
165
|
+
//# sourceMappingURL=InterpolationLog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InterpolationLog.d.ts","sourceRoot":"","sources":["../../../../../src/engine/network/sim/InterpolationLog.js"],"names":[],"mappings":"AA6EA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH;IAkBI;;;;;OAKG;IACH,0DALW;QAAE,qBAAqB,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,EAkGvE;IA9EG,+BAA+B;IAC/B,0BADqB,MAAM,CACiB;IAe5C,+BAA+B;IAC/B,2BADqB,MAAM,CACa;IA+D5C;;;;;;;;OAQG;IACH,iBAFW,MAAM,QAkEhB;IAED;;;;;;;;;;;;;OAaG;IACH,yBAJW,MAAM,WACN,MAAM,GACJ,YAAY,CAmCxB;IAED;;;;;OAKG;IACH,mBAsDC;IAED;;;;;;;OAOG;IACH,iBAeC;IAED;;;OAGG;IACH,eAHW,MAAM,GACJ,OAAO,CAInB;IAED;;;;;;;;;;;;;;OAcG;IACH,YAPW,MAAM,EAAE,GAAC,WAAW,cACpB,MAAM,QACN,MAAM,cACN,MAAM,WACN,MAAM,GACJ,OAAO,CA+BnB;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH,wBATW,YAAY,cACZ,MAAM,WACN,MAAM,UACN,MAAM,UACN,MAAM,KACN,MAAM,wCAEJ,OAAO,CAuBnB;IAED;;;OAGG;IACH,QAFa,MAAM,CAIlB;IAED;;;OAGG;IACH,eAFa,MAAM,CAIlB;IAED;;;OAGG;IACH,cASC;;CACJ;6BArkB4B,sCAAsC"}
|