murow 0.0.1 → 0.0.3
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 +15 -1
- package/dist/core/binary-codec/binary-codec.d.ts +30 -0
- package/dist/core/binary-codec/binary-codec.js +18 -0
- package/dist/core/pooled-codec/pooled-codec.d.ts +88 -16
- package/dist/core/pooled-codec/pooled-codec.js +207 -10
- package/dist/core/prediction/prediction.d.ts +2 -2
- package/dist/core/prediction/prediction.js +13 -7
- package/dist/core.esm.js +1 -1
- package/dist/core.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/net/adapters/browser-websocket.d.ts +24 -0
- package/dist/net/adapters/browser-websocket.js +66 -0
- package/dist/net/adapters/bun-websocket.d.ts +80 -0
- package/dist/net/adapters/bun-websocket.js +232 -0
- package/dist/net/buffer-pool.d.ts +49 -0
- package/dist/net/buffer-pool.js +89 -0
- package/dist/net/client.d.ts +221 -0
- package/dist/net/client.js +533 -0
- package/dist/net/index.d.ts +57 -0
- package/dist/net/index.js +58 -0
- package/dist/net/server.d.ts +376 -0
- package/dist/net/server.js +937 -0
- package/dist/net/types.d.ts +169 -0
- package/dist/net/types.js +31 -0
- package/dist/net/validators.d.ts +54 -0
- package/dist/net/validators.js +88 -0
- package/dist/protocol/index.d.ts +54 -5
- package/dist/protocol/index.js +54 -5
- package/dist/protocol/intent/define-intent.d.ts +132 -0
- package/dist/protocol/intent/define-intent.js +125 -0
- package/dist/protocol/intent/index.d.ts +56 -2
- package/dist/protocol/intent/index.js +55 -2
- package/dist/protocol/intent/intent-registry.d.ts +13 -3
- package/dist/protocol/intent/intent-registry.js +24 -6
- package/dist/protocol/rpc/define-rpc.d.ts +72 -0
- package/dist/protocol/rpc/define-rpc.js +84 -0
- package/dist/protocol/rpc/index.d.ts +3 -0
- package/dist/protocol/rpc/index.js +3 -0
- package/dist/protocol/rpc/rpc-registry.d.ts +105 -0
- package/dist/protocol/rpc/rpc-registry.js +142 -0
- package/dist/protocol/rpc/rpc.d.ts +34 -0
- package/dist/protocol/rpc/rpc.js +12 -0
- package/dist/protocol/snapshot/snapshot-codec.d.ts +4 -0
- package/dist/protocol/snapshot/snapshot-codec.js +13 -2
- package/dist/protocol/snapshot/snapshot-registry.d.ts +18 -10
- package/dist/protocol/snapshot/snapshot-registry.js +27 -12
- package/package.json +2 -2
- package/src/core/binary-codec/binary-codec.ts +18 -0
- package/src/core/pooled-codec/pooled-codec.test.ts +337 -0
- package/src/core/pooled-codec/pooled-codec.ts +286 -21
- package/src/core/prediction/prediction.test.ts +8 -7
- package/src/core/prediction/prediction.ts +18 -11
- package/src/index.ts +4 -0
- package/src/net/README.md +470 -0
- package/src/net/adapters/browser-websocket.ts +78 -0
- package/src/net/adapters/bun-websocket.ts +277 -0
- package/src/net/buffer-pool.ts +106 -0
- package/src/net/client.test.ts +526 -0
- package/src/net/client.ts +634 -0
- package/src/net/index.ts +60 -0
- package/src/net/server.test.ts +799 -0
- package/src/net/server.ts +1115 -0
- package/src/net/types.ts +201 -0
- package/src/net/validators.ts +104 -0
- package/src/protocol/README.md +237 -78
- package/src/protocol/index.ts +54 -5
- package/src/protocol/intent/define-intent.test.ts +397 -0
- package/src/protocol/intent/define-intent.ts +182 -0
- package/src/protocol/intent/index.ts +56 -2
- package/src/protocol/intent/intent-registry.test.ts +56 -95
- package/src/protocol/intent/intent-registry.ts +30 -6
- package/src/protocol/rpc/define-rpc.test.ts +141 -0
- package/src/protocol/rpc/define-rpc.ts +113 -0
- package/src/protocol/rpc/index.ts +3 -0
- package/src/protocol/rpc/rpc-registry.test.ts +168 -0
- package/src/protocol/rpc/rpc-registry.ts +156 -0
- package/src/protocol/rpc/rpc.ts +37 -0
- package/src/protocol/snapshot/snapshot-codec.ts +19 -3
- package/src/protocol/snapshot/snapshot-registry.test.ts +15 -7
- package/src/protocol/snapshot/snapshot-registry.ts +37 -18
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ import { FixedTicker } from 'murow/core';
|
|
|
35
35
|
- `generateId`: Cryptographically secure ID generation
|
|
36
36
|
- `lerp`: Linear interpolation utility
|
|
37
37
|
- `NavMesh`: Pathfinding with dynamic obstacles
|
|
38
|
-
- `PooledCodec`: Object-pooled binary codec
|
|
38
|
+
- `PooledCodec`: Object-pooled binary codec with array support (via `PooledCodec.array()`) for efficient snapshot encoding. Supports zero-copy encoding with `encodeInto()` for minimal allocations
|
|
39
39
|
- `IntentTracker` & `Reconciliator`: Client-side prediction
|
|
40
40
|
|
|
41
41
|
### Protocol Layer
|
|
@@ -49,6 +49,20 @@ Works harmoniously with core utilities (`FixedTicker`, `IntentTracker`, `Reconci
|
|
|
49
49
|
|
|
50
50
|
See [Protocol Layer Documentation](./src/protocol/README.md) for usage.
|
|
51
51
|
|
|
52
|
+
### Network Layer
|
|
53
|
+
Transport-agnostic client/server abstractions:
|
|
54
|
+
- `ServerNetwork`: Multiplayer game server with per-peer snapshot registries
|
|
55
|
+
- `ClientNetwork`: Game client with intent/snapshot handling
|
|
56
|
+
- `TransportAdapter`: Pluggable transport interface
|
|
57
|
+
- `BunWebSocketTransport`: Bun WebSocket implementation (reference)
|
|
58
|
+
|
|
59
|
+
Key features:
|
|
60
|
+
- **Per-peer snapshot registries** for fog of war and interest management
|
|
61
|
+
- **Transport agnostic** - works with WebSocket, WebRTC, UDP, etc.
|
|
62
|
+
- **Type-safe** protocol integration with `IntentRegistry` and `SnapshotRegistry`
|
|
63
|
+
|
|
64
|
+
See [Network Layer Documentation](./src/net/README.md) for usage and [examples/multiplayer-game.ts](./examples/multiplayer-game.ts) for a complete example.
|
|
65
|
+
|
|
52
66
|
## Building
|
|
53
67
|
|
|
54
68
|
```bash
|
|
@@ -146,8 +146,38 @@ export declare class BinaryCodec extends BaseBinaryCodec {
|
|
|
146
146
|
static readonly u8: Field<number>;
|
|
147
147
|
/** Unsigned 16-bit integer field */
|
|
148
148
|
static readonly u16: Field<number>;
|
|
149
|
+
/** Unsigned 32-bit integer field */
|
|
150
|
+
static readonly u32: Field<number>;
|
|
151
|
+
/** Signed 8-bit integer field */
|
|
152
|
+
static readonly i8: Field<number>;
|
|
153
|
+
/** Signed 16-bit integer field */
|
|
154
|
+
static readonly i16: Field<number>;
|
|
155
|
+
/** Signed 32-bit integer field */
|
|
156
|
+
static readonly i32: Field<number>;
|
|
149
157
|
/** 32-bit floating point field */
|
|
150
158
|
static readonly f32: Field<number>;
|
|
159
|
+
/** Boolean field */
|
|
160
|
+
static readonly bool: Field<boolean>;
|
|
161
|
+
/** String field with length prefix */
|
|
162
|
+
static string: typeof BinaryPrimitives.string;
|
|
163
|
+
/** 2D vector field */
|
|
164
|
+
static readonly vec2: Field<{
|
|
165
|
+
x: number;
|
|
166
|
+
y: number;
|
|
167
|
+
}>;
|
|
168
|
+
/** 3D vector field */
|
|
169
|
+
static readonly vec3: Field<{
|
|
170
|
+
x: number;
|
|
171
|
+
y: number;
|
|
172
|
+
z: number;
|
|
173
|
+
}>;
|
|
174
|
+
/** RGBA color field */
|
|
175
|
+
static readonly color: Field<{
|
|
176
|
+
r: number;
|
|
177
|
+
g: number;
|
|
178
|
+
b: number;
|
|
179
|
+
a: number;
|
|
180
|
+
}>;
|
|
151
181
|
/**
|
|
152
182
|
* Encodes an object into a binary buffer.
|
|
153
183
|
*/
|
|
@@ -332,5 +332,23 @@ export class BinaryCodec extends BaseBinaryCodec {
|
|
|
332
332
|
BinaryCodec.u8 = BinaryPrimitives.u8;
|
|
333
333
|
/** Unsigned 16-bit integer field */
|
|
334
334
|
BinaryCodec.u16 = BinaryPrimitives.u16;
|
|
335
|
+
/** Unsigned 32-bit integer field */
|
|
336
|
+
BinaryCodec.u32 = BinaryPrimitives.u32;
|
|
337
|
+
/** Signed 8-bit integer field */
|
|
338
|
+
BinaryCodec.i8 = BinaryPrimitives.i8;
|
|
339
|
+
/** Signed 16-bit integer field */
|
|
340
|
+
BinaryCodec.i16 = BinaryPrimitives.i16;
|
|
341
|
+
/** Signed 32-bit integer field */
|
|
342
|
+
BinaryCodec.i32 = BinaryPrimitives.i32;
|
|
335
343
|
/** 32-bit floating point field */
|
|
336
344
|
BinaryCodec.f32 = BinaryPrimitives.f32;
|
|
345
|
+
/** Boolean field */
|
|
346
|
+
BinaryCodec.bool = BinaryPrimitives.bool;
|
|
347
|
+
/** String field with length prefix */
|
|
348
|
+
BinaryCodec.string = BinaryPrimitives.string;
|
|
349
|
+
/** 2D vector field */
|
|
350
|
+
BinaryCodec.vec2 = BinaryPrimitives.vec2;
|
|
351
|
+
/** 3D vector field */
|
|
352
|
+
BinaryCodec.vec3 = BinaryPrimitives.vec3;
|
|
353
|
+
/** RGBA color field */
|
|
354
|
+
BinaryCodec.color = BinaryPrimitives.color;
|
|
@@ -1,4 +1,35 @@
|
|
|
1
|
-
import { Schema } from "../binary-codec";
|
|
1
|
+
import { Schema, Field } from "../binary-codec";
|
|
2
|
+
/**
|
|
3
|
+
* Type marker for array fields in schemas.
|
|
4
|
+
*
|
|
5
|
+
* ArrayField now implements both the Field interface (for use in schemas)
|
|
6
|
+
* and the Codec interface (for standalone use), making it versatile enough
|
|
7
|
+
* to be used directly with SnapshotRegistry or as a field in PooledCodec schemas.
|
|
8
|
+
*/
|
|
9
|
+
export type ArrayField<T> = {
|
|
10
|
+
__arrayType?: T[];
|
|
11
|
+
encode(items: T[]): Uint8Array;
|
|
12
|
+
decode(buf: Uint8Array): T[];
|
|
13
|
+
decodeField(buf: Uint8Array): {
|
|
14
|
+
value: T[];
|
|
15
|
+
bytesRead: number;
|
|
16
|
+
};
|
|
17
|
+
toNil(): T[];
|
|
18
|
+
calculateSize(items: T[]): number;
|
|
19
|
+
encodeInto(items: T[], buffer: Uint8Array, offset: number): number;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Extended schema type that supports both regular fields and array fields
|
|
23
|
+
*/
|
|
24
|
+
export type ExtendedSchema<T> = {
|
|
25
|
+
[K in keyof T]: T[K] extends any[] ? ArrayField<T[K][number]> : Field<T[K]>;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Infer the type from a schema definition
|
|
29
|
+
*/
|
|
30
|
+
type InferSchemaType<S> = {
|
|
31
|
+
[K in keyof S]: S[K] extends ArrayField<infer U> ? U[] : S[K] extends Field<infer V> ? V : unknown;
|
|
32
|
+
};
|
|
2
33
|
/**
|
|
3
34
|
* Generic object pool for reusing objects and minimizing allocations.
|
|
4
35
|
* @template T Type of objects stored in the pool.
|
|
@@ -108,33 +139,74 @@ export declare class PooledEncoder<T extends object> {
|
|
|
108
139
|
/**
|
|
109
140
|
* Combined pooled encoder and decoder for a single schema.
|
|
110
141
|
* Provides a convenient wrapper around PooledEncoder and PooledDecoder.
|
|
111
|
-
* @template
|
|
142
|
+
* @template S Schema type
|
|
112
143
|
*/
|
|
113
|
-
export declare class PooledCodec<
|
|
144
|
+
export declare class PooledCodec<S extends Record<string, any>> {
|
|
145
|
+
schema: S;
|
|
114
146
|
/** Pooled encoder for the schema */
|
|
115
|
-
encoder: PooledEncoder<
|
|
147
|
+
encoder: PooledEncoder<any>;
|
|
116
148
|
/** Pooled decoder for the schema */
|
|
117
|
-
decoder: PooledDecoder<
|
|
149
|
+
decoder: PooledDecoder<any>;
|
|
118
150
|
/**
|
|
119
151
|
* @param schema Schema describing the object structure.
|
|
120
|
-
* @param initial Initial object used as a template for pooling decoded objects.
|
|
121
152
|
*/
|
|
122
|
-
constructor(schema:
|
|
153
|
+
constructor(schema: S);
|
|
154
|
+
/**
|
|
155
|
+
* Calculate the size in bytes needed to encode the data.
|
|
156
|
+
* @param data Object to calculate size for.
|
|
157
|
+
* @returns Size in bytes.
|
|
158
|
+
*/
|
|
159
|
+
calculateSize(data: InferSchemaType<S>): number;
|
|
160
|
+
/**
|
|
161
|
+
* Encode an object directly into a target buffer at the given offset.
|
|
162
|
+
* This is a zero-copy operation - no intermediate buffers are allocated.
|
|
163
|
+
* @param data Object to encode.
|
|
164
|
+
* @param buffer Target buffer to write into.
|
|
165
|
+
* @param offset Byte offset in the buffer to start writing.
|
|
166
|
+
* @returns Number of bytes written.
|
|
167
|
+
*/
|
|
168
|
+
encodeInto(data: InferSchemaType<S>, buffer: Uint8Array, offset: number): number;
|
|
123
169
|
/**
|
|
124
170
|
* Encode an object into a pooled buffer.
|
|
125
|
-
* @param
|
|
126
|
-
* @returns
|
|
171
|
+
* @param data Object to encode.
|
|
172
|
+
* @returns Encoded buffer.
|
|
127
173
|
*/
|
|
128
|
-
encode(data:
|
|
174
|
+
encode(data: InferSchemaType<S>): Uint8Array;
|
|
129
175
|
/**
|
|
130
176
|
* Decode a buffer into a pooled object.
|
|
131
|
-
* @param
|
|
132
|
-
* @returns
|
|
177
|
+
* @param buf Buffer to decode.
|
|
178
|
+
* @returns Decoded object.
|
|
133
179
|
*/
|
|
134
|
-
decode(buf: Uint8Array):
|
|
180
|
+
decode(buf: Uint8Array): InferSchemaType<S>;
|
|
135
181
|
/**
|
|
136
182
|
* Release a decoded object back to the pool.
|
|
137
|
-
* @param
|
|
138
|
-
*/
|
|
139
|
-
release(obj:
|
|
183
|
+
* @param obj Object to release.
|
|
184
|
+
*/
|
|
185
|
+
release(obj: InferSchemaType<S>): void;
|
|
186
|
+
/**
|
|
187
|
+
* Creates an array field descriptor for use in schemas.
|
|
188
|
+
* Encodes array length as u16 followed by each item.
|
|
189
|
+
*
|
|
190
|
+
* @template U Type of items in the array
|
|
191
|
+
* @param itemSchema Schema for individual array items
|
|
192
|
+
* @returns An array field descriptor that can be used in PooledCodec schemas
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* ```ts
|
|
196
|
+
* const PlayerSchema = {
|
|
197
|
+
* entityId: BinaryPrimitives.u32,
|
|
198
|
+
* x: BinaryPrimitives.f32,
|
|
199
|
+
* y: BinaryPrimitives.f32,
|
|
200
|
+
* };
|
|
201
|
+
*
|
|
202
|
+
* const UpdateSchema = {
|
|
203
|
+
* tick: BinaryPrimitives.u32,
|
|
204
|
+
* players: PooledCodec.array(PlayerSchema),
|
|
205
|
+
* };
|
|
206
|
+
*
|
|
207
|
+
* const codec = new PooledCodec(UpdateSchema);
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
static array<U extends object>(itemSchema: Schema<U> | Record<string, any>): ArrayField<U>;
|
|
140
211
|
}
|
|
212
|
+
export {};
|
|
@@ -70,16 +70,31 @@ export class PooledDecoder {
|
|
|
70
70
|
* @param {T} target Object to write decoded data into.
|
|
71
71
|
*/
|
|
72
72
|
decodeInto(buf, target) {
|
|
73
|
+
let offset = 0;
|
|
73
74
|
for (const key of Object.keys(this.schema)) {
|
|
74
75
|
const field = this.schema[key];
|
|
75
76
|
if ("decodeAll" in field) {
|
|
76
|
-
|
|
77
|
+
const result = field.decodeAll(buf.subarray(offset));
|
|
78
|
+
target[key] = result.value;
|
|
79
|
+
offset += result.bytesRead;
|
|
80
|
+
}
|
|
81
|
+
else if ("decodeField" in field) {
|
|
82
|
+
// Use decodeField for ArrayField (returns { value, bytesRead })
|
|
83
|
+
const result = field.decodeField(buf.subarray(offset));
|
|
84
|
+
target[key] = result.value;
|
|
85
|
+
offset += result.bytesRead;
|
|
77
86
|
}
|
|
78
87
|
else if ("decode" in field) {
|
|
79
|
-
|
|
88
|
+
const result = field.decode(buf.subarray(offset));
|
|
89
|
+
target[key] = result.value;
|
|
90
|
+
offset += result.bytesRead;
|
|
80
91
|
}
|
|
81
92
|
else {
|
|
82
|
-
|
|
93
|
+
// For primitive fields, calculate size and decode
|
|
94
|
+
const fieldSize = field.size || 0;
|
|
95
|
+
const fieldBuf = buf.subarray(offset, offset + fieldSize);
|
|
96
|
+
BinaryCodec.decodeInto({ [key]: field }, fieldBuf, target);
|
|
97
|
+
offset += fieldSize;
|
|
83
98
|
}
|
|
84
99
|
}
|
|
85
100
|
}
|
|
@@ -176,38 +191,220 @@ export class PooledEncoder {
|
|
|
176
191
|
/**
|
|
177
192
|
* Combined pooled encoder and decoder for a single schema.
|
|
178
193
|
* Provides a convenient wrapper around PooledEncoder and PooledDecoder.
|
|
179
|
-
* @template
|
|
194
|
+
* @template S Schema type
|
|
180
195
|
*/
|
|
181
196
|
export class PooledCodec {
|
|
182
197
|
/**
|
|
183
198
|
* @param schema Schema describing the object structure.
|
|
184
|
-
* @param initial Initial object used as a template for pooling decoded objects.
|
|
185
199
|
*/
|
|
186
200
|
constructor(schema) {
|
|
201
|
+
this.schema = schema;
|
|
187
202
|
this.encoder = new PooledEncoder(schema);
|
|
188
203
|
this.decoder = new PooledDecoder(schema);
|
|
189
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* Calculate the size in bytes needed to encode the data.
|
|
207
|
+
* @param data Object to calculate size for.
|
|
208
|
+
* @returns Size in bytes.
|
|
209
|
+
*/
|
|
210
|
+
calculateSize(data) {
|
|
211
|
+
let size = 0;
|
|
212
|
+
for (const key of Object.keys(this.schema)) {
|
|
213
|
+
const field = this.schema[key];
|
|
214
|
+
if ("size" in field) {
|
|
215
|
+
// Fixed-size primitive field
|
|
216
|
+
size += field.size;
|
|
217
|
+
}
|
|
218
|
+
else if ("calculateSize" in field) {
|
|
219
|
+
// Variable-size field (like arrays)
|
|
220
|
+
size += field.calculateSize(data[key]);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return size;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Encode an object directly into a target buffer at the given offset.
|
|
227
|
+
* This is a zero-copy operation - no intermediate buffers are allocated.
|
|
228
|
+
* @param data Object to encode.
|
|
229
|
+
* @param buffer Target buffer to write into.
|
|
230
|
+
* @param offset Byte offset in the buffer to start writing.
|
|
231
|
+
* @returns Number of bytes written.
|
|
232
|
+
*/
|
|
233
|
+
encodeInto(data, buffer, offset) {
|
|
234
|
+
const view = new DataView(buffer.buffer, buffer.byteOffset);
|
|
235
|
+
let currentOffset = offset;
|
|
236
|
+
for (const key of Object.keys(this.schema)) {
|
|
237
|
+
const field = this.schema[key];
|
|
238
|
+
if ("write" in field) {
|
|
239
|
+
// Primitive field - direct write
|
|
240
|
+
field.write(view, currentOffset, data[key]);
|
|
241
|
+
currentOffset += field.size;
|
|
242
|
+
}
|
|
243
|
+
else if ("encodeInto" in field) {
|
|
244
|
+
// Array or nested field - delegate to its encodeInto
|
|
245
|
+
const bytesWritten = field.encodeInto(data[key], buffer, currentOffset);
|
|
246
|
+
currentOffset += bytesWritten;
|
|
247
|
+
}
|
|
248
|
+
else if ("encode" in field) {
|
|
249
|
+
// Fallback for fields that only have encode()
|
|
250
|
+
const nested = field.encode(data[key]);
|
|
251
|
+
buffer.set(nested, currentOffset);
|
|
252
|
+
currentOffset += nested.length;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return currentOffset - offset; // bytes written
|
|
256
|
+
}
|
|
190
257
|
/**
|
|
191
258
|
* Encode an object into a pooled buffer.
|
|
192
|
-
* @param
|
|
193
|
-
* @returns
|
|
259
|
+
* @param data Object to encode.
|
|
260
|
+
* @returns Encoded buffer.
|
|
194
261
|
*/
|
|
195
262
|
encode(data) {
|
|
196
263
|
return this.encoder.encode(data);
|
|
197
264
|
}
|
|
198
265
|
/**
|
|
199
266
|
* Decode a buffer into a pooled object.
|
|
200
|
-
* @param
|
|
201
|
-
* @returns
|
|
267
|
+
* @param buf Buffer to decode.
|
|
268
|
+
* @returns Decoded object.
|
|
202
269
|
*/
|
|
203
270
|
decode(buf) {
|
|
204
271
|
return this.decoder.decode(buf);
|
|
205
272
|
}
|
|
206
273
|
/**
|
|
207
274
|
* Release a decoded object back to the pool.
|
|
208
|
-
* @param
|
|
275
|
+
* @param obj Object to release.
|
|
209
276
|
*/
|
|
210
277
|
release(obj) {
|
|
211
278
|
this.decoder.release(obj);
|
|
212
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* Creates an array field descriptor for use in schemas.
|
|
282
|
+
* Encodes array length as u16 followed by each item.
|
|
283
|
+
*
|
|
284
|
+
* @template U Type of items in the array
|
|
285
|
+
* @param itemSchema Schema for individual array items
|
|
286
|
+
* @returns An array field descriptor that can be used in PooledCodec schemas
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* ```ts
|
|
290
|
+
* const PlayerSchema = {
|
|
291
|
+
* entityId: BinaryPrimitives.u32,
|
|
292
|
+
* x: BinaryPrimitives.f32,
|
|
293
|
+
* y: BinaryPrimitives.f32,
|
|
294
|
+
* };
|
|
295
|
+
*
|
|
296
|
+
* const UpdateSchema = {
|
|
297
|
+
* tick: BinaryPrimitives.u32,
|
|
298
|
+
* players: PooledCodec.array(PlayerSchema),
|
|
299
|
+
* };
|
|
300
|
+
*
|
|
301
|
+
* const codec = new PooledCodec(UpdateSchema);
|
|
302
|
+
* ```
|
|
303
|
+
*/
|
|
304
|
+
static array(itemSchema) {
|
|
305
|
+
// Calculate item size once
|
|
306
|
+
let itemSize = 0;
|
|
307
|
+
for (const key of Object.keys(itemSchema)) {
|
|
308
|
+
const field = itemSchema[key];
|
|
309
|
+
itemSize += field.size || 0;
|
|
310
|
+
}
|
|
311
|
+
// Pool for encoding buffers (larger initial size to avoid allocations)
|
|
312
|
+
const bufferPool = new ObjectPool(() => new Uint8Array(16384));
|
|
313
|
+
// Pool for decoded objects to avoid allocations
|
|
314
|
+
const objectPool = new ObjectPool(() => {
|
|
315
|
+
const obj = {};
|
|
316
|
+
for (const key of Object.keys(itemSchema)) {
|
|
317
|
+
const field = itemSchema[key];
|
|
318
|
+
obj[key] = ("toNil" in field ? field.toNil() : undefined);
|
|
319
|
+
}
|
|
320
|
+
return obj;
|
|
321
|
+
});
|
|
322
|
+
// Pool for result arrays to avoid allocations
|
|
323
|
+
const arrayPool = new ObjectPool(() => []);
|
|
324
|
+
// Reusable DataView for encoding
|
|
325
|
+
let encodeView = null;
|
|
326
|
+
// Reusable DataView for decoding
|
|
327
|
+
let decodeView = null;
|
|
328
|
+
return {
|
|
329
|
+
__arrayType: undefined,
|
|
330
|
+
calculateSize(items) {
|
|
331
|
+
return 2 + (items.length * itemSize);
|
|
332
|
+
},
|
|
333
|
+
encodeInto(items, buffer, offset) {
|
|
334
|
+
const view = new DataView(buffer.buffer, buffer.byteOffset);
|
|
335
|
+
// Write array length
|
|
336
|
+
view.setUint16(offset, items.length, false);
|
|
337
|
+
let currentOffset = offset + 2;
|
|
338
|
+
// Write each item directly into buffer
|
|
339
|
+
for (const item of items) {
|
|
340
|
+
for (const key of Object.keys(itemSchema)) {
|
|
341
|
+
const field = itemSchema[key];
|
|
342
|
+
field.write(view, currentOffset, item[key]);
|
|
343
|
+
currentOffset += field.size;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return currentOffset - offset; // bytes written
|
|
347
|
+
},
|
|
348
|
+
encode(items) {
|
|
349
|
+
const totalSize = 2 + (items.length * itemSize);
|
|
350
|
+
let buffer = bufferPool.acquire();
|
|
351
|
+
// Grow buffer pool if needed (but keep the undersized buffer for next time)
|
|
352
|
+
if (buffer.length < totalSize) {
|
|
353
|
+
bufferPool.release(buffer);
|
|
354
|
+
buffer = new Uint8Array(Math.max(totalSize, buffer.length * 2));
|
|
355
|
+
}
|
|
356
|
+
// Create or reuse DataView for this buffer
|
|
357
|
+
if (!encodeView || encodeView.buffer !== buffer.buffer) {
|
|
358
|
+
encodeView = new DataView(buffer.buffer, buffer.byteOffset);
|
|
359
|
+
}
|
|
360
|
+
// Write array length
|
|
361
|
+
encodeView.setUint16(0, items.length, false);
|
|
362
|
+
// Write each item directly into buffer (zero intermediate allocations)
|
|
363
|
+
let offset = 2;
|
|
364
|
+
for (const item of items) {
|
|
365
|
+
for (const key of Object.keys(itemSchema)) {
|
|
366
|
+
const field = itemSchema[key];
|
|
367
|
+
field.write(encodeView, offset, item[key]);
|
|
368
|
+
offset += field.size;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
// Create a copy to return (caller owns this memory)
|
|
372
|
+
const result = new Uint8Array(offset);
|
|
373
|
+
result.set(buffer.subarray(0, offset));
|
|
374
|
+
// Return buffer to pool
|
|
375
|
+
bufferPool.release(buffer);
|
|
376
|
+
return result;
|
|
377
|
+
},
|
|
378
|
+
decodeField(buf) {
|
|
379
|
+
// Read array length directly from buffer
|
|
380
|
+
const length = (buf[0] << 8) | buf[1];
|
|
381
|
+
// Acquire pooled array and resize if needed
|
|
382
|
+
const items = arrayPool.acquire();
|
|
383
|
+
items.length = length;
|
|
384
|
+
// Create or reuse DataView for reading
|
|
385
|
+
if (!decodeView || decodeView.buffer !== buf.buffer || decodeView.byteOffset !== buf.byteOffset) {
|
|
386
|
+
decodeView = new DataView(buf.buffer, buf.byteOffset);
|
|
387
|
+
}
|
|
388
|
+
// Read each item using pooled objects
|
|
389
|
+
let offset = 2;
|
|
390
|
+
for (let i = 0; i < length; i++) {
|
|
391
|
+
const item = objectPool.acquire();
|
|
392
|
+
// Decode directly into pooled object
|
|
393
|
+
for (const key of Object.keys(itemSchema)) {
|
|
394
|
+
const field = itemSchema[key];
|
|
395
|
+
item[key] = field.read(decodeView, offset);
|
|
396
|
+
offset += field.size;
|
|
397
|
+
}
|
|
398
|
+
items[i] = item;
|
|
399
|
+
}
|
|
400
|
+
return { value: items, bytesRead: offset };
|
|
401
|
+
},
|
|
402
|
+
decode(buf) {
|
|
403
|
+
return this.decodeField(buf).value;
|
|
404
|
+
},
|
|
405
|
+
toNil() {
|
|
406
|
+
return [];
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
}
|
|
213
410
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Used for prediction and reconciliation in a server-authoritative architecture.
|
|
6
6
|
*/
|
|
7
7
|
export declare class IntentTracker<T> {
|
|
8
|
-
|
|
8
|
+
tracker: Map<number, T[]>;
|
|
9
9
|
get size(): number;
|
|
10
10
|
/**
|
|
11
11
|
* Adds a new intent for a specific tick.
|
|
@@ -34,7 +34,7 @@ export declare class IntentTracker<T> {
|
|
|
34
34
|
*/
|
|
35
35
|
export declare class Reconciliator<T, U> {
|
|
36
36
|
private options;
|
|
37
|
-
|
|
37
|
+
tracker: IntentTracker<T>;
|
|
38
38
|
/**
|
|
39
39
|
* @param {Object} options - Callbacks for applying snapshot state and replaying intents.
|
|
40
40
|
* @param {(snapshotState: U) => void} options.onLoadState - Called to load authoritative snapshot state.
|
|
@@ -17,7 +17,10 @@ export class IntentTracker {
|
|
|
17
17
|
* @param {T} intent - The intent data.
|
|
18
18
|
*/
|
|
19
19
|
track(tick, intent) {
|
|
20
|
-
this.tracker.
|
|
20
|
+
if (!this.tracker.has(tick)) {
|
|
21
|
+
this.tracker.set(tick, []);
|
|
22
|
+
}
|
|
23
|
+
this.tracker.get(tick).push(intent);
|
|
21
24
|
return intent;
|
|
22
25
|
}
|
|
23
26
|
/**
|
|
@@ -28,15 +31,15 @@ export class IntentTracker {
|
|
|
28
31
|
*/
|
|
29
32
|
dropUpTo(tick) {
|
|
30
33
|
const remaining = [];
|
|
31
|
-
for (const [t,
|
|
34
|
+
for (const [t, intents] of this.tracker) {
|
|
32
35
|
if (t <= tick)
|
|
33
36
|
this.tracker.delete(t);
|
|
34
37
|
else
|
|
35
|
-
remaining.push([t,
|
|
38
|
+
remaining.push([t, intents]);
|
|
36
39
|
}
|
|
37
40
|
// sort by tick ascending
|
|
38
41
|
remaining.sort(([a], [b]) => a - b);
|
|
39
|
-
return remaining.map(([_,
|
|
42
|
+
return remaining.map(([_, intents]) => intents).flat();
|
|
40
43
|
}
|
|
41
44
|
/**
|
|
42
45
|
* Returns all currently tracked intents in ascending tick order.
|
|
@@ -45,7 +48,8 @@ export class IntentTracker {
|
|
|
45
48
|
values() {
|
|
46
49
|
return Array.from(this.tracker.entries())
|
|
47
50
|
.sort(([a], [b]) => a - b)
|
|
48
|
-
.map(([_,
|
|
51
|
+
.map(([_, intents]) => intents)
|
|
52
|
+
.flat();
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
55
|
/**
|
|
@@ -84,7 +88,9 @@ export class Reconciliator {
|
|
|
84
88
|
this.options.onLoadState(snapshot.state);
|
|
85
89
|
// 2. Remove confirmed intents and get remaining
|
|
86
90
|
const remainingIntents = this.tracker.dropUpTo(snapshot.tick);
|
|
87
|
-
// 3.
|
|
88
|
-
|
|
91
|
+
// 3. Only replay if there are actually remaining intents
|
|
92
|
+
if (remainingIntents.length > 0) {
|
|
93
|
+
this.options.onReplay(remainingIntents);
|
|
94
|
+
}
|
|
89
95
|
}
|
|
90
96
|
}
|
package/dist/core.esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var U=Symbol("schemaSize");function V(i){let e=i[U];if(e!==void 0)return e;let t=0;for(let s of Object.keys(i))t+=i[s].size;return i[U]=t,t}var w=class{static encodeInto(e,t){let s=V(e),n=new ArrayBuffer(s),c=new DataView(n),r=0;for(let o of Object.keys(e)){let a=e[o];a.write(c,r,t[o]),r+=a.size}return new Uint8Array(n)}static decodeInto(e,t,s){let n=V(e);if(t.byteLength<n)throw new RangeError(`Buffer too small: expected ${n} bytes, got ${t.byteLength}`);let c=new DataView(t.buffer,t.byteOffset,t.byteLength),r=0;for(let o of Object.keys(e)){let a=e[o];s[o]=a.read(c,r),r+=a.size}return s}},y=class{static{this.u8={size:1,write:(e,t,s)=>e.setUint8(t,s),read:(e,t)=>e.getUint8(t),toNil:()=>0}}static{this.u16={size:2,write:(e,t,s)=>e.setUint16(t,s,!1),read:(e,t)=>e.getUint16(t,!1),toNil:()=>0}}static{this.u32={size:4,write:(e,t,s)=>e.setUint32(t,s,!1),read:(e,t)=>e.getUint32(t,!1),toNil:()=>0}}static{this.i8={size:1,write:(e,t,s)=>e.setInt8(t,s),read:(e,t)=>e.getInt8(t),toNil:()=>0}}static{this.i16={size:2,write:(e,t,s)=>e.setInt16(t,s,!1),read:(e,t)=>e.getInt16(t,!1),toNil:()=>0}}static{this.i32={size:4,write:(e,t,s)=>e.setInt32(t,s,!1),read:(e,t)=>e.getInt32(t,!1),toNil:()=>0}}static{this.f32={size:4,write:(e,t,s)=>e.setFloat32(t,s,!1),read:(e,t)=>e.getFloat32(t,!1),toNil:()=>0}}static{this.f64={size:8,write:(e,t,s)=>e.setFloat64(t,s,!1),read:(e,t)=>e.getFloat64(t,!1),toNil:()=>0}}static{this.bool={size:1,write:(e,t,s)=>e.setUint8(t,s?1:0),read:(e,t)=>e.getUint8(t)!==0,toNil:()=>!1}}static string(e){return{size:e+2,write(t,s,n){let r=new TextEncoder().encode(n);if(r.length>e)throw new RangeError(`String too long, max ${e} bytes`);t.setUint16(s,r.length,!1);for(let o=0;o<r.length;o++)t.setUint8(s+2+o,r[o]);for(let o=r.length;o<e;o++)t.setUint8(s+2+o,0)},read(t,s){let n=t.getUint16(s,!1),c=new Uint8Array(n);for(let r=0;r<n;r++)c[r]=t.getUint8(s+2+r);return new TextDecoder().decode(c)},toNil:()=>""}}static{this.vec2={size:8,write(e,t,s){e.setFloat32(t,s.x,!1),e.setFloat32(t+4,s.y,!1)},read(e,t){return{x:e.getFloat32(t,!1),y:e.getFloat32(t+4,!1)}},toNil:()=>({x:0,y:0})}}static{this.vec3={size:12,write(e,t,s){e.setFloat32(t,s.x,!1),e.setFloat32(t+4,s.y,!1),e.setFloat32(t+8,s.z,!1)},read(e,t){return{x:e.getFloat32(t,!1),y:e.getFloat32(t+4,!1),z:e.getFloat32(t+8,!1)}},toNil:()=>({x:0,y:0,z:0})}}static{this.color={size:4,write(e,t,s){e.setUint8(t,s.r),e.setUint8(t+1,s.g),e.setUint8(t+2,s.b),e.setUint8(t+3,s.a)},read(e,t){return{r:e.getUint8(t),g:e.getUint8(t+1),b:e.getUint8(t+2),a:e.getUint8(t+3)}},toNil:()=>({r:0,g:0,b:0,a:0})}}static{this.f32_le={size:4,write:(e,t,s)=>e.setFloat32(t,s,!0),read:(e,t)=>e.getFloat32(t,!0),toNil:()=>0}}static{this.f64_le={size:8,write:(e,t,s)=>e.setFloat64(t,s,!0),read:(e,t)=>e.getFloat64(t,!0),toNil:()=>0}}static{this.u16_le={size:2,write:(e,t,s)=>e.setUint16(t,s,!0),read:(e,t)=>e.getUint16(t,!0),toNil:()=>0}}static{this.u32_le={size:4,write:(e,t,s)=>e.setUint32(t,s,!0),read:(e,t)=>e.getUint32(t,!0),toNil:()=>0}}static{this.i16_le={size:2,write:(e,t,s)=>e.setInt16(t,s,!0),read:(e,t)=>e.getInt16(t,!0),toNil:()=>0}}static{this.i32_le={size:4,write:(e,t,s)=>e.setInt32(t,s,!0),read:(e,t)=>e.getInt32(t,!0),toNil:()=>0}}static{this.vec2_le={size:8,write:(e,t,s)=>{e.setFloat32(t,s[0],!0),e.setFloat32(t+4,s[1],!0)},read:(e,t)=>[e.getFloat32(t,!0),e.getFloat32(t+4,!0)],toNil:()=>[0,0]}}static{this.vec3_le={size:12,write:(e,t,s)=>{e.setFloat32(t,s[0],!0),e.setFloat32(t+4,s[1],!0),e.setFloat32(t+8,s[2],!0)},read:(e,t)=>[e.getFloat32(t,!0),e.getFloat32(t+4,!0),e.getFloat32(t+8,!0)],toNil:()=>[0,0,0]}}static{this.vec4_le={size:16,write:(e,t,s)=>{e.setFloat32(t,s[0],!0),e.setFloat32(t+4,s[1],!0),e.setFloat32(t+8,s[2],!0),e.setFloat32(t+12,s[3],!0)},read:(e,t)=>[e.getFloat32(t,!0),e.getFloat32(t+4,!0),e.getFloat32(t+8,!0),e.getFloat32(t+12,!0)],toNil:()=>[0,0,0,0]}}},x=class extends w{static{this.u8=y.u8}static{this.u16=y.u16}static{this.f32=y.f32}static encode(e,t){return this.encodeInto(e,t)}static decode(e,t,s){return this.decodeInto(e,t,s)}};var C=class{constructor({events:e}){this.callbacks=new Map,this.events=e;for(let t of this.events)this.callbacks.set(t,new Set)}on(e,t){let s=this.callbacks.get(e);if(!s)return console.warn(`Event "${e}" does not exist.`);s.add(t)}once(e,t){let s=n=>{t(n),this.off(e,s)};this.on(e,s)}emit(e,t){let s=this.callbacks.get(e);if(!s)return console.warn(`Event "${e}" does not exist.`);for(let n of s)n(t)}off(e,t){let s=this.callbacks.get(e);if(!s)return console.warn(`Event "${e}" does not exist.`);s.delete(t)}clear(e){if(!e){this.callbacks.clear();for(let s of this.events)this.callbacks.set(s,new Set);return}let t=this.callbacks.get(e);if(!t)return console.warn(`Event "${e}" does not exist.`);t.clear()}};var A=class{constructor({rate:e,onTick:t}){this.accumulator=0;this._tickCount=0;this.rate=e,this.intervalMs=1e3/this.rate,this.onTick=t,this.maxTicksPerFrame=Math.max(1,Math.floor(e/2))}getTicks(e){this.accumulator+=e*1e3;let t=0;for(;this.accumulator>=this.intervalMs&&t<this.maxTicksPerFrame;)this.accumulator-=this.intervalMs,t++;let s=Math.floor(this.accumulator/this.intervalMs);return s>0&&this.onTickSkipped&&this.onTickSkipped(s),t}tick(e){let t=this.getTicks(e);for(let s=0;s<t;s++)this.onTick(1/this.rate,this._tickCount++)}get tickCount(){return this._tickCount}resetTickCount(){this._tickCount=0}get accumulatedTime(){return this.accumulator/1e3}};function re(i={}){let{prefix:e="",size:t=16}=i,s=Math.max(t-e.length,8),n=Math.ceil(s/8),r=crypto.getRandomValues(new Uint32Array(n)).reduce((o,a)=>o+a.toString(16).padStart(8,"0"),"");return r=r.slice(0,s).padStart(s,"0"),`${e}${r}`}function ce(i,e,t){return i+(e-i)*t}var B=[{x:1,y:0},{x:-1,y:0},{x:0,y:1},{x:0,y:-1}],T=i=>({x:Math.floor(i.x),y:Math.floor(i.y)}),X=i=>({x:i.x+.5,y:i.y+.5}),L=(()=>{let i=1;return()=>i++})(),m=(i,e)=>i&65535|(e&65535)<<16,F=i=>({x:i<<16>>16,y:i>>16}),M=class{constructor(e){this.scoreFn=e;this.heap=[]}push(e){this.heap.push(e),this.bubbleUp(this.heap.length-1)}pop(){let e=this.heap[0],t=this.heap.pop();return this.heap.length>0&&t!==void 0&&(this.heap[0]=t,this.sinkDown(0)),e}get size(){return this.heap.length}bubbleUp(e){let t=this.heap[e],s=this.scoreFn(t);for(;e>0;){let n=(e+1>>1)-1,c=this.heap[n];if(s>=this.scoreFn(c))break;this.heap[n]=t,this.heap[e]=c,e=n}}sinkDown(e){let t=this.heap.length,s=this.heap[e],n=this.scoreFn(s);for(;;){let c=e+1<<1,r=c-1,o=null,a;if(r<t){let l=this.heap[r];a=this.scoreFn(l),a<n&&(o=r)}if(c<t){let l=this.heap[c];this.scoreFn(l)<(o===null?n:a)&&(o=c)}if(o===null)break;this.heap[e]=this.heap[o],this.heap[o]=s,e=o}}},z=class{constructor(e=1){this.grid=new Map;this.obstacleCells=new Map;this.cellSize=e}hash(e,t){let s=Math.floor(e/this.cellSize),n=Math.floor(t/this.cellSize);return m(s,n)}add(e,t){let s=this.getCellsForObstacle(t);for(let n of s)this.grid.has(n)||this.grid.set(n,new Set),this.grid.get(n).add(e);this.obstacleCells.set(e,s)}remove(e){let t=this.obstacleCells.get(e);if(t){for(let s of t){let n=this.grid.get(s);n&&(n.delete(e),n.size===0&&this.grid.delete(s))}this.obstacleCells.delete(e)}}query(e){let t=this.hash(e.x,e.y);return this.grid.get(t)||new Set}clear(){this.grid.clear(),this.obstacleCells.clear()}getCellsForObstacle(e){let t=new Set;if(e.type==="circle"){let s=e.radius,n=Math.floor((e.pos.x-s)/this.cellSize),c=Math.floor((e.pos.x+s)/this.cellSize),r=Math.floor((e.pos.y-s)/this.cellSize),o=Math.floor((e.pos.y+s)/this.cellSize);for(let a=n;a<=c;a++)for(let l=r;l<=o;l++)t.add(m(a,l))}else if(e.type==="rect"){let s=e.pos.x+e.size.x/2,n=e.pos.y+e.size.y/2,c=e.size.x/2,r=e.size.y/2,o=Math.sqrt(c*c+r*r),a=Math.floor((s-o)/this.cellSize),l=Math.floor((s+o)/this.cellSize),h=Math.floor((n-o)/this.cellSize),u=Math.floor((n+o)/this.cellSize);for(let d=a;d<=l;d++)for(let p=h;p<=u;p++)t.add(m(d,p))}else if(e.type==="polygon"){let s=R(e),n=Math.floor(s.minX/this.cellSize),c=Math.floor(s.maxX/this.cellSize),r=Math.floor(s.minY/this.cellSize),o=Math.floor(s.maxY/this.cellSize);for(let a=n;a<=c;a++)for(let l=r;l<=o;l++)t.add(m(a,l))}return t}};function Y(i,e){let t=i.x-e.pos.x,s=i.y-e.pos.y;return t*t+s*s<=e.radius*e.radius}function j(i,e){let t=e.pos.x+e.size.x/2,s=e.pos.y+e.size.y/2;if(e.rotation){let n=Math.cos(-e.rotation),c=Math.sin(-e.rotation),r=i.x-t,o=i.y-s,a=r*n-o*c,l=r*c+o*n;return Math.abs(a)<=e.size.x/2&&Math.abs(l)<=e.size.y/2}return i.x>=e.pos.x&&i.y>=e.pos.y&&i.x<=e.pos.x+e.size.x&&i.y<=e.pos.y+e.size.y}function _(i,e){let t=!1,s=e.points,n=e.rotation?Math.cos(e.rotation):1,c=e.rotation?Math.sin(e.rotation):0;for(let r=0,o=s.length-1;r<s.length;o=r++){let a=s[r].x,l=s[r].y,h=s[o].x,u=s[o].y;if(e.rotation){let p=a*n-l*c,f=a*c+l*n,b=h*n-u*c,g=h*c+u*n;a=p,l=f,h=b,u=g}a+=e.pos.x,l+=e.pos.y,h+=e.pos.x,u+=e.pos.y,l>i.y!=u>i.y&&i.x<(h-a)*(i.y-l)/(u-l)+a&&(t=!t)}return t}function R(i){let e=1/0,t=1/0,s=-1/0,n=-1/0,c=i.rotation?Math.cos(i.rotation):1,r=i.rotation?Math.sin(i.rotation):0;for(let o of i.points){let a=o.x,l=o.y;if(i.rotation){let h=a*c-l*r,u=a*r+l*c;a=h,l=u}a+=i.pos.x,l+=i.pos.y,e=Math.min(e,a),t=Math.min(t,l),s=Math.max(s,a),n=Math.max(n,l)}return{minX:e,minY:t,maxX:s,maxY:n}}var O=class{constructor(){this.items=new Map;this.spatial=new z(1);this._cachedItems=[];this.dirty=!0;this.version=0}add(e){let t=L(),s={...e,id:t};return this.items.set(t,s),this.spatial.add(t,s),this.dirty=!0,this.version++,t}move(e,t){let s=this.items.get(e);if(!s)return;this.spatial.remove(e);let n={...s,pos:{...t}};this.items.set(e,n),this.spatial.add(e,n),this.dirty=!0,this.version++}remove(e){this.spatial.remove(e),this.items.delete(e),this.dirty=!0,this.version++}at(e){let t=this.spatial.query(e);for(let s of t){let n=this.items.get(s);if(!(!n||n.solid===!1)&&(n.type==="circle"&&Y(e,n)||n.type==="rect"&&j(e,n)||n.type==="polygon"&&_(e,n)))return n}}get values(){return this.dirty?(this._cachedItems=[...this.items.values()],this.dirty=!1,this._cachedItems):this._cachedItems}},S=class{constructor(e){this.obstacles=e;this.blocked=new Set}rebuild(){this.blocked.clear();for(let e of this.obstacles.values)if(e.solid!==!1){if(e.type==="circle"){let t=Math.ceil(e.radius),s=Math.floor(e.pos.x),n=Math.floor(e.pos.y);for(let c=-t;c<=t;c++)for(let r=-t;r<=t;r++){let o=s+c,a=n+r,l={x:o+.5,y:a+.5};Y(l,e)&&this.blocked.add(m(o,a))}}else if(e.type==="rect"){let t=e.pos.x+e.size.x/2,s=e.pos.y+e.size.y/2,n=e.size.x/2,c=e.size.y/2,r=Math.sqrt(n*n+c*c),o=Math.floor(t-r),a=Math.ceil(t+r),l=Math.floor(s-r),h=Math.ceil(s+r);for(let u=o;u<=a;u++)for(let d=l;d<=h;d++){let p={x:u+.5,y:d+.5};j(p,e)&&this.blocked.add(m(u,d))}}else if(e.type==="polygon"){let t=R(e),s=Math.floor(t.minX),n=Math.ceil(t.maxX),c=Math.floor(t.minY),r=Math.ceil(t.maxY);for(let o=s;o<=n;o++)for(let a=c;a<=r;a++){let l={x:o+.5,y:a+.5};_(l,e)&&this.blocked.add(m(o,a))}}}}findPath(e,t){return D(T(e),T(t),(s,n)=>!this.blocked.has(m(s,n))).map(X)}},E=class{constructor(e){this.obstacles=e}rebuild(){}findPath(e,t){let s=Math.ceil(Math.hypot(t.x-e.x,t.y-e.y)*2),n=!1;for(let r=1;r<=s;r++){let o=r/s,a={x:e.x+(t.x-e.x)*o,y:e.y+(t.y-e.y)*o};if(this.obstacles.at(a)){n=!0;break}}return n?D(T(e),T(t),(r,o)=>{let a={x:r+.5,y:o+.5};return!this.obstacles.at(a)}).map(X):[e,t]}},P=class{constructor(e){this.type=e;this.lastVersion=-1;this.obstacles=new O,e==="grid"&&(this.grid=new S(this.obstacles)),e==="graph"&&(this.graph=new E(this.obstacles))}addObstacle(e){return this.obstacles.add(e)}moveObstacle(e,t){this.obstacles.move(e,t)}removeObstacle(e){this.obstacles.remove(e)}getObstacles(){return this.obstacles.values}findPath({from:e,to:t}){return this.rebuild(),this.type==="grid"?this.grid.findPath(e,t):this.graph.findPath(e,t)}rebuild(){this.lastVersion!==this.obstacles.version&&(this.grid?.rebuild(),this.graph?.rebuild(),this.lastVersion=this.obstacles.version)}};function D(i,e,t){let s=new Map,n=new Map,c=new Set,r=new Set,o=u=>m(u.x,u.y),a=(u,d)=>Math.abs(u.x-d.x)+Math.abs(u.y-d.y),l=new M(u=>{let d=F(u);return n.get(u)+a(d,e)}),h=o(i);for(n.set(h,0),l.push(h),r.add(h);l.size>0;){let u=l.pop();r.delete(u);let d=F(u);if(d.x===e.x&&d.y===e.y)return G(s,d);c.add(u);for(let p of B){let f={x:d.x+p.x,y:d.y+p.y};if(!t(f.x,f.y))continue;let b=o(f);if(c.has(b))continue;let g=n.get(u)+1;g<(n.get(b)??1/0)&&(n.set(b,g),s.set(b,u),r.has(b)||(l.push(b),r.add(b)))}}return[]}function G(i,e){let t=[e],s=m(e.x,e.y);for(;i.has(s);)s=i.get(s),t.push(F(s));return t.reverse()}var k=class{constructor(e){this.factory=e;this.pool=[]}acquire(){return this.pool.pop()??this.factory()}release(e){this.pool.push(e)}releaseAll(e){this.pool.push(...e)}},v=class{constructor(e){this.schema=e;this.pool=new k(()=>this.createNil())}createNil(){let e={};for(let t of Object.keys(this.schema)){let s=this.schema[t];e[t]="toNil"in s?s.toNil():void 0}return e}decode(e){let t=this.pool.acquire();return this.decodeInto(e,t),t}decodeInto(e,t){for(let s of Object.keys(this.schema)){let n=this.schema[s];"decodeAll"in n?t[s]=n.decodeAll(e):"decode"in n?t[s]=n.decode(e):x.decodeInto({[s]:n},e,t)}}release(e){this.pool.release(e)}},$=class{constructor(e){this.pooledDecoder=new v(e)}decodeAll(e){return e.map(t=>this.pooledDecoder.decode(t))}releaseAll(e){e.forEach(t=>this.pooledDecoder.release(t))}},I=class{constructor(e,t=1024){this.schema=e;this.bufferSize=t;this.pool=new k(()=>new Uint8Array(t))}encode(e){let t=this.pool.acquire(),s=0;for(let n of Object.keys(this.schema)){let c=this.schema[n];if("encode"in c){let r=c.encode(e[n]);t.set(r,s),s+=r.length}else if("encodeAll"in c){let r=c.encodeAll(e[n]),o=0;for(let a of r)t.set(a,s+o),o+=a.length;s+=o}else{let r=x.encode({[n]:c},{[n]:e[n]});t.set(r,s),s+=r.length}}return t.subarray(0,s)}release(e){this.pool.release(e)}},q=class{constructor(e){this.encoder=new I(e),this.decoder=new v(e)}encode(e){return this.encoder.encode(e)}decode(e){return this.decoder.decode(e)}release(e){this.decoder.release(e)}};var N=class{constructor(){this.tracker=new Map}get size(){return this.tracker.size}track(e,t){return this.tracker.set(e,t),t}dropUpTo(e){let t=[];for(let[s,n]of this.tracker)s<=e?this.tracker.delete(s):t.push([s,n]);return t.sort(([s],[n])=>s-n),t.map(([s,n])=>n)}values(){return Array.from(this.tracker.entries()).sort(([e],[t])=>e-t).map(([e,t])=>t)}},K=class{constructor(e){this.options=e;this.tracker=new N}trackIntent(e,t){this.tracker.track(e,t)}onSnapshot(e){this.options.onLoadState(e.state);let t=this.tracker.dropUpTo(e.tick);this.options.onReplay(t)}};export{w as BaseBinaryCodec,x as BinaryCodec,y as BinaryPrimitives,C as EventSystem,A as FixedTicker,N as IntentTracker,P as NavMesh,k as ObjectPool,$ as PooledArrayDecoder,q as PooledCodec,v as PooledDecoder,I as PooledEncoder,K as Reconciliator,re as generateId,ce as lerp};
|
|
1
|
+
var N=Symbol("schemaSize");function V(c){let e=c[N];if(e!==void 0)return e;let t=0;for(let s of Object.keys(c))t+=c[s].size;return c[N]=t,t}var w=class{static encodeInto(e,t){let s=V(e),n=new ArrayBuffer(s),a=new DataView(n),r=0;for(let o of Object.keys(e)){let i=e[o];i.write(a,r,t[o]),r+=i.size}return new Uint8Array(n)}static decodeInto(e,t,s){let n=V(e);if(t.byteLength<n)throw new RangeError(`Buffer too small: expected ${n} bytes, got ${t.byteLength}`);let a=new DataView(t.buffer,t.byteOffset,t.byteLength),r=0;for(let o of Object.keys(e)){let i=e[o];s[o]=i.read(a,r),r+=i.size}return s}},y=class{static{this.u8={size:1,write:(e,t,s)=>e.setUint8(t,s),read:(e,t)=>e.getUint8(t),toNil:()=>0}}static{this.u16={size:2,write:(e,t,s)=>e.setUint16(t,s,!1),read:(e,t)=>e.getUint16(t,!1),toNil:()=>0}}static{this.u32={size:4,write:(e,t,s)=>e.setUint32(t,s,!1),read:(e,t)=>e.getUint32(t,!1),toNil:()=>0}}static{this.i8={size:1,write:(e,t,s)=>e.setInt8(t,s),read:(e,t)=>e.getInt8(t),toNil:()=>0}}static{this.i16={size:2,write:(e,t,s)=>e.setInt16(t,s,!1),read:(e,t)=>e.getInt16(t,!1),toNil:()=>0}}static{this.i32={size:4,write:(e,t,s)=>e.setInt32(t,s,!1),read:(e,t)=>e.getInt32(t,!1),toNil:()=>0}}static{this.f32={size:4,write:(e,t,s)=>e.setFloat32(t,s,!1),read:(e,t)=>e.getFloat32(t,!1),toNil:()=>0}}static{this.f64={size:8,write:(e,t,s)=>e.setFloat64(t,s,!1),read:(e,t)=>e.getFloat64(t,!1),toNil:()=>0}}static{this.bool={size:1,write:(e,t,s)=>e.setUint8(t,s?1:0),read:(e,t)=>e.getUint8(t)!==0,toNil:()=>!1}}static string(e){return{size:e+2,write(t,s,n){let r=new TextEncoder().encode(n);if(r.length>e)throw new RangeError(`String too long, max ${e} bytes`);t.setUint16(s,r.length,!1);for(let o=0;o<r.length;o++)t.setUint8(s+2+o,r[o]);for(let o=r.length;o<e;o++)t.setUint8(s+2+o,0)},read(t,s){let n=t.getUint16(s,!1),a=new Uint8Array(n);for(let r=0;r<n;r++)a[r]=t.getUint8(s+2+r);return new TextDecoder().decode(a)},toNil:()=>""}}static{this.vec2={size:8,write(e,t,s){e.setFloat32(t,s.x,!1),e.setFloat32(t+4,s.y,!1)},read(e,t){return{x:e.getFloat32(t,!1),y:e.getFloat32(t+4,!1)}},toNil:()=>({x:0,y:0})}}static{this.vec3={size:12,write(e,t,s){e.setFloat32(t,s.x,!1),e.setFloat32(t+4,s.y,!1),e.setFloat32(t+8,s.z,!1)},read(e,t){return{x:e.getFloat32(t,!1),y:e.getFloat32(t+4,!1),z:e.getFloat32(t+8,!1)}},toNil:()=>({x:0,y:0,z:0})}}static{this.color={size:4,write(e,t,s){e.setUint8(t,s.r),e.setUint8(t+1,s.g),e.setUint8(t+2,s.b),e.setUint8(t+3,s.a)},read(e,t){return{r:e.getUint8(t),g:e.getUint8(t+1),b:e.getUint8(t+2),a:e.getUint8(t+3)}},toNil:()=>({r:0,g:0,b:0,a:0})}}static{this.f32_le={size:4,write:(e,t,s)=>e.setFloat32(t,s,!0),read:(e,t)=>e.getFloat32(t,!0),toNil:()=>0}}static{this.f64_le={size:8,write:(e,t,s)=>e.setFloat64(t,s,!0),read:(e,t)=>e.getFloat64(t,!0),toNil:()=>0}}static{this.u16_le={size:2,write:(e,t,s)=>e.setUint16(t,s,!0),read:(e,t)=>e.getUint16(t,!0),toNil:()=>0}}static{this.u32_le={size:4,write:(e,t,s)=>e.setUint32(t,s,!0),read:(e,t)=>e.getUint32(t,!0),toNil:()=>0}}static{this.i16_le={size:2,write:(e,t,s)=>e.setInt16(t,s,!0),read:(e,t)=>e.getInt16(t,!0),toNil:()=>0}}static{this.i32_le={size:4,write:(e,t,s)=>e.setInt32(t,s,!0),read:(e,t)=>e.getInt32(t,!0),toNil:()=>0}}static{this.vec2_le={size:8,write:(e,t,s)=>{e.setFloat32(t,s[0],!0),e.setFloat32(t+4,s[1],!0)},read:(e,t)=>[e.getFloat32(t,!0),e.getFloat32(t+4,!0)],toNil:()=>[0,0]}}static{this.vec3_le={size:12,write:(e,t,s)=>{e.setFloat32(t,s[0],!0),e.setFloat32(t+4,s[1],!0),e.setFloat32(t+8,s[2],!0)},read:(e,t)=>[e.getFloat32(t,!0),e.getFloat32(t+4,!0),e.getFloat32(t+8,!0)],toNil:()=>[0,0,0]}}static{this.vec4_le={size:16,write:(e,t,s)=>{e.setFloat32(t,s[0],!0),e.setFloat32(t+4,s[1],!0),e.setFloat32(t+8,s[2],!0),e.setFloat32(t+12,s[3],!0)},read:(e,t)=>[e.getFloat32(t,!0),e.getFloat32(t+4,!0),e.getFloat32(t+8,!0),e.getFloat32(t+12,!0)],toNil:()=>[0,0,0,0]}}},g=class extends w{static{this.u8=y.u8}static{this.u16=y.u16}static{this.u32=y.u32}static{this.i8=y.i8}static{this.i16=y.i16}static{this.i32=y.i32}static{this.f32=y.f32}static{this.bool=y.bool}static{this.string=y.string}static{this.vec2=y.vec2}static{this.vec3=y.vec3}static{this.color=y.color}static encode(e,t){return this.encodeInto(e,t)}static decode(e,t,s){return this.decodeInto(e,t,s)}};var A=class{constructor({events:e}){this.callbacks=new Map,this.events=e;for(let t of this.events)this.callbacks.set(t,new Set)}on(e,t){let s=this.callbacks.get(e);if(!s)return console.warn(`Event "${e}" does not exist.`);s.add(t)}once(e,t){let s=n=>{t(n),this.off(e,s)};this.on(e,s)}emit(e,t){let s=this.callbacks.get(e);if(!s)return console.warn(`Event "${e}" does not exist.`);for(let n of s)n(t)}off(e,t){let s=this.callbacks.get(e);if(!s)return console.warn(`Event "${e}" does not exist.`);s.delete(t)}clear(e){if(!e){this.callbacks.clear();for(let s of this.events)this.callbacks.set(s,new Set);return}let t=this.callbacks.get(e);if(!t)return console.warn(`Event "${e}" does not exist.`);t.clear()}};var C=class{constructor({rate:e,onTick:t}){this.accumulator=0;this._tickCount=0;this.rate=e,this.intervalMs=1e3/this.rate,this.onTick=t,this.maxTicksPerFrame=Math.max(1,Math.floor(e/2))}getTicks(e){this.accumulator+=e*1e3;let t=0;for(;this.accumulator>=this.intervalMs&&t<this.maxTicksPerFrame;)this.accumulator-=this.intervalMs,t++;let s=Math.floor(this.accumulator/this.intervalMs);return s>0&&this.onTickSkipped&&this.onTickSkipped(s),t}tick(e){let t=this.getTicks(e);for(let s=0;s<t;s++)this.onTick(1/this.rate,this._tickCount++)}get tickCount(){return this._tickCount}resetTickCount(){this._tickCount=0}get accumulatedTime(){return this.accumulator/1e3}};function re(c={}){let{prefix:e="",size:t=16}=c,s=Math.max(t-e.length,8),n=Math.ceil(s/8),r=crypto.getRandomValues(new Uint32Array(n)).reduce((o,i)=>o+i.toString(16).padStart(8,"0"),"");return r=r.slice(0,s).padStart(s,"0"),`${e}${r}`}function ce(c,e,t){return c+(e-c)*t}var B=[{x:1,y:0},{x:-1,y:0},{x:0,y:1},{x:0,y:-1}],k=c=>({x:Math.floor(c.x),y:Math.floor(c.y)}),P=c=>({x:c.x+.5,y:c.y+.5}),L=(()=>{let c=1;return()=>c++})(),m=(c,e)=>c&65535|(e&65535)<<16,F=c=>({x:c<<16>>16,y:c>>16}),z=class{constructor(e){this.scoreFn=e;this.heap=[]}push(e){this.heap.push(e),this.bubbleUp(this.heap.length-1)}pop(){let e=this.heap[0],t=this.heap.pop();return this.heap.length>0&&t!==void 0&&(this.heap[0]=t,this.sinkDown(0)),e}get size(){return this.heap.length}bubbleUp(e){let t=this.heap[e],s=this.scoreFn(t);for(;e>0;){let n=(e+1>>1)-1,a=this.heap[n];if(s>=this.scoreFn(a))break;this.heap[n]=t,this.heap[e]=a,e=n}}sinkDown(e){let t=this.heap.length,s=this.heap[e],n=this.scoreFn(s);for(;;){let a=e+1<<1,r=a-1,o=null,i;if(r<t){let l=this.heap[r];i=this.scoreFn(l),i<n&&(o=r)}if(a<t){let l=this.heap[a];this.scoreFn(l)<(o===null?n:i)&&(o=a)}if(o===null)break;this.heap[e]=this.heap[o],this.heap[o]=s,e=o}}},S=class{constructor(e=1){this.grid=new Map;this.obstacleCells=new Map;this.cellSize=e}hash(e,t){let s=Math.floor(e/this.cellSize),n=Math.floor(t/this.cellSize);return m(s,n)}add(e,t){let s=this.getCellsForObstacle(t);for(let n of s)this.grid.has(n)||this.grid.set(n,new Set),this.grid.get(n).add(e);this.obstacleCells.set(e,s)}remove(e){let t=this.obstacleCells.get(e);if(t){for(let s of t){let n=this.grid.get(s);n&&(n.delete(e),n.size===0&&this.grid.delete(s))}this.obstacleCells.delete(e)}}query(e){let t=this.hash(e.x,e.y);return this.grid.get(t)||new Set}clear(){this.grid.clear(),this.obstacleCells.clear()}getCellsForObstacle(e){let t=new Set;if(e.type==="circle"){let s=e.radius,n=Math.floor((e.pos.x-s)/this.cellSize),a=Math.floor((e.pos.x+s)/this.cellSize),r=Math.floor((e.pos.y-s)/this.cellSize),o=Math.floor((e.pos.y+s)/this.cellSize);for(let i=n;i<=a;i++)for(let l=r;l<=o;l++)t.add(m(i,l))}else if(e.type==="rect"){let s=e.pos.x+e.size.x/2,n=e.pos.y+e.size.y/2,a=e.size.x/2,r=e.size.y/2,o=Math.sqrt(a*a+r*r),i=Math.floor((s-o)/this.cellSize),l=Math.floor((s+o)/this.cellSize),u=Math.floor((n-o)/this.cellSize),d=Math.floor((n+o)/this.cellSize);for(let h=i;h<=l;h++)for(let p=u;p<=d;p++)t.add(m(h,p))}else if(e.type==="polygon"){let s=R(e),n=Math.floor(s.minX/this.cellSize),a=Math.floor(s.maxX/this.cellSize),r=Math.floor(s.minY/this.cellSize),o=Math.floor(s.maxY/this.cellSize);for(let i=n;i<=a;i++)for(let l=r;l<=o;l++)t.add(m(i,l))}return t}};function _(c,e){let t=c.x-e.pos.x,s=c.y-e.pos.y;return t*t+s*s<=e.radius*e.radius}function X(c,e){let t=e.pos.x+e.size.x/2,s=e.pos.y+e.size.y/2;if(e.rotation){let n=Math.cos(-e.rotation),a=Math.sin(-e.rotation),r=c.x-t,o=c.y-s,i=r*n-o*a,l=r*a+o*n;return Math.abs(i)<=e.size.x/2&&Math.abs(l)<=e.size.y/2}return c.x>=e.pos.x&&c.y>=e.pos.y&&c.x<=e.pos.x+e.size.x&&c.y<=e.pos.y+e.size.y}function Y(c,e){let t=!1,s=e.points,n=e.rotation?Math.cos(e.rotation):1,a=e.rotation?Math.sin(e.rotation):0;for(let r=0,o=s.length-1;r<s.length;o=r++){let i=s[r].x,l=s[r].y,u=s[o].x,d=s[o].y;if(e.rotation){let p=i*n-l*a,b=i*a+l*n,f=u*n-d*a,T=u*a+d*n;i=p,l=b,u=f,d=T}i+=e.pos.x,l+=e.pos.y,u+=e.pos.x,d+=e.pos.y,l>c.y!=d>c.y&&c.x<(u-i)*(c.y-l)/(d-l)+i&&(t=!t)}return t}function R(c){let e=1/0,t=1/0,s=-1/0,n=-1/0,a=c.rotation?Math.cos(c.rotation):1,r=c.rotation?Math.sin(c.rotation):0;for(let o of c.points){let i=o.x,l=o.y;if(c.rotation){let u=i*a-l*r,d=i*r+l*a;i=u,l=d}i+=c.pos.x,l+=c.pos.y,e=Math.min(e,i),t=Math.min(t,l),s=Math.max(s,i),n=Math.max(n,l)}return{minX:e,minY:t,maxX:s,maxY:n}}var O=class{constructor(){this.items=new Map;this.spatial=new S(1);this._cachedItems=[];this.dirty=!0;this.version=0}add(e){let t=L(),s={...e,id:t};return this.items.set(t,s),this.spatial.add(t,s),this.dirty=!0,this.version++,t}move(e,t){let s=this.items.get(e);if(!s)return;this.spatial.remove(e);let n={...s,pos:{...t}};this.items.set(e,n),this.spatial.add(e,n),this.dirty=!0,this.version++}remove(e){this.spatial.remove(e),this.items.delete(e),this.dirty=!0,this.version++}at(e){let t=this.spatial.query(e);for(let s of t){let n=this.items.get(s);if(!(!n||n.solid===!1)&&(n.type==="circle"&&_(e,n)||n.type==="rect"&&X(e,n)||n.type==="polygon"&&Y(e,n)))return n}}get values(){return this.dirty?(this._cachedItems=[...this.items.values()],this.dirty=!1,this._cachedItems):this._cachedItems}},U=class{constructor(e){this.obstacles=e;this.blocked=new Set}rebuild(){this.blocked.clear();for(let e of this.obstacles.values)if(e.solid!==!1){if(e.type==="circle"){let t=Math.ceil(e.radius),s=Math.floor(e.pos.x),n=Math.floor(e.pos.y);for(let a=-t;a<=t;a++)for(let r=-t;r<=t;r++){let o=s+a,i=n+r,l={x:o+.5,y:i+.5};_(l,e)&&this.blocked.add(m(o,i))}}else if(e.type==="rect"){let t=e.pos.x+e.size.x/2,s=e.pos.y+e.size.y/2,n=e.size.x/2,a=e.size.y/2,r=Math.sqrt(n*n+a*a),o=Math.floor(t-r),i=Math.ceil(t+r),l=Math.floor(s-r),u=Math.ceil(s+r);for(let d=o;d<=i;d++)for(let h=l;h<=u;h++){let p={x:d+.5,y:h+.5};X(p,e)&&this.blocked.add(m(d,h))}}else if(e.type==="polygon"){let t=R(e),s=Math.floor(t.minX),n=Math.ceil(t.maxX),a=Math.floor(t.minY),r=Math.ceil(t.maxY);for(let o=s;o<=n;o++)for(let i=a;i<=r;i++){let l={x:o+.5,y:i+.5};Y(l,e)&&this.blocked.add(m(o,i))}}}}findPath(e,t){return D(k(e),k(t),(s,n)=>!this.blocked.has(m(s,n))).map(P)}},M=class{constructor(e){this.obstacles=e}rebuild(){}findPath(e,t){let s=Math.ceil(Math.hypot(t.x-e.x,t.y-e.y)*2),n=!1;for(let r=1;r<=s;r++){let o=r/s,i={x:e.x+(t.x-e.x)*o,y:e.y+(t.y-e.y)*o};if(this.obstacles.at(i)){n=!0;break}}return n?D(k(e),k(t),(r,o)=>{let i={x:r+.5,y:o+.5};return!this.obstacles.at(i)}).map(P):[e,t]}},j=class{constructor(e){this.type=e;this.lastVersion=-1;this.obstacles=new O,e==="grid"&&(this.grid=new U(this.obstacles)),e==="graph"&&(this.graph=new M(this.obstacles))}addObstacle(e){return this.obstacles.add(e)}moveObstacle(e,t){this.obstacles.move(e,t)}removeObstacle(e){this.obstacles.remove(e)}getObstacles(){return this.obstacles.values}findPath({from:e,to:t}){return this.rebuild(),this.type==="grid"?this.grid.findPath(e,t):this.graph.findPath(e,t)}rebuild(){this.lastVersion!==this.obstacles.version&&(this.grid?.rebuild(),this.graph?.rebuild(),this.lastVersion=this.obstacles.version)}};function D(c,e,t){let s=new Map,n=new Map,a=new Set,r=new Set,o=d=>m(d.x,d.y),i=(d,h)=>Math.abs(d.x-h.x)+Math.abs(d.y-h.y),l=new z(d=>{let h=F(d);return n.get(d)+i(h,e)}),u=o(c);for(n.set(u,0),l.push(u),r.add(u);l.size>0;){let d=l.pop();r.delete(d);let h=F(d);if(h.x===e.x&&h.y===e.y)return G(s,h);a.add(d);for(let p of B){let b={x:h.x+p.x,y:h.y+p.y};if(!t(b.x,b.y))continue;let f=o(b);if(a.has(f))continue;let T=n.get(d)+1;T<(n.get(f)??1/0)&&(n.set(f,T),s.set(f,d),r.has(f)||(l.push(f),r.add(f)))}}return[]}function G(c,e){let t=[e],s=m(e.x,e.y);for(;c.has(s);)s=c.get(s),t.push(F(s));return t.reverse()}var x=class{constructor(e){this.factory=e;this.pool=[]}acquire(){return this.pool.pop()??this.factory()}release(e){this.pool.push(e)}releaseAll(e){this.pool.push(...e)}},v=class{constructor(e){this.schema=e;this.pool=new x(()=>this.createNil())}createNil(){let e={};for(let t of Object.keys(this.schema)){let s=this.schema[t];e[t]="toNil"in s?s.toNil():void 0}return e}decode(e){let t=this.pool.acquire();return this.decodeInto(e,t),t}decodeInto(e,t){let s=0;for(let n of Object.keys(this.schema)){let a=this.schema[n];if("decodeAll"in a){let r=a.decodeAll(e.subarray(s));t[n]=r.value,s+=r.bytesRead}else if("decodeField"in a){let r=a.decodeField(e.subarray(s));t[n]=r.value,s+=r.bytesRead}else if("decode"in a){let r=a.decode(e.subarray(s));t[n]=r.value,s+=r.bytesRead}else{let r=a.size||0,o=e.subarray(s,s+r);g.decodeInto({[n]:a},o,t),s+=r}}}release(e){this.pool.release(e)}},K=class{constructor(e){this.pooledDecoder=new v(e)}decodeAll(e){return e.map(t=>this.pooledDecoder.decode(t))}releaseAll(e){e.forEach(t=>this.pooledDecoder.release(t))}},I=class{constructor(e,t=1024){this.schema=e;this.bufferSize=t;this.pool=new x(()=>new Uint8Array(t))}encode(e){let t=this.pool.acquire(),s=0;for(let n of Object.keys(this.schema)){let a=this.schema[n];if("encode"in a){let r=a.encode(e[n]);t.set(r,s),s+=r.length}else if("encodeAll"in a){let r=a.encodeAll(e[n]),o=0;for(let i of r)t.set(i,s+o),o+=i.length;s+=o}else{let r=g.encode({[n]:a},{[n]:e[n]});t.set(r,s),s+=r.length}}return t.subarray(0,s)}release(e){this.pool.release(e)}},q=class{constructor(e){this.schema=e;this.encoder=new I(e),this.decoder=new v(e)}calculateSize(e){let t=0;for(let s of Object.keys(this.schema)){let n=this.schema[s];"size"in n?t+=n.size:"calculateSize"in n&&(t+=n.calculateSize(e[s]))}return t}encodeInto(e,t,s){let n=new DataView(t.buffer,t.byteOffset),a=s;for(let r of Object.keys(this.schema)){let o=this.schema[r];if("write"in o)o.write(n,a,e[r]),a+=o.size;else if("encodeInto"in o){let i=o.encodeInto(e[r],t,a);a+=i}else if("encode"in o){let i=o.encode(e[r]);t.set(i,a),a+=i.length}}return a-s}encode(e){return this.encoder.encode(e)}decode(e){return this.decoder.decode(e)}release(e){this.decoder.release(e)}static array(e){let t=0;for(let i of Object.keys(e)){let l=e[i];t+=l.size||0}let s=new x(()=>new Uint8Array(16384)),n=new x(()=>{let i={};for(let l of Object.keys(e)){let u=e[l];i[l]="toNil"in u?u.toNil():void 0}return i}),a=new x(()=>[]),r=null,o=null;return{__arrayType:void 0,calculateSize(i){return 2+i.length*t},encodeInto(i,l,u){let d=new DataView(l.buffer,l.byteOffset);d.setUint16(u,i.length,!1);let h=u+2;for(let p of i)for(let b of Object.keys(e)){let f=e[b];f.write(d,h,p[b]),h+=f.size}return h-u},encode(i){let l=2+i.length*t,u=s.acquire();u.length<l&&(s.release(u),u=new Uint8Array(Math.max(l,u.length*2))),(!r||r.buffer!==u.buffer)&&(r=new DataView(u.buffer,u.byteOffset)),r.setUint16(0,i.length,!1);let d=2;for(let p of i)for(let b of Object.keys(e)){let f=e[b];f.write(r,d,p[b]),d+=f.size}let h=new Uint8Array(d);return h.set(u.subarray(0,d)),s.release(u),h},decodeField(i){let l=i[0]<<8|i[1],u=a.acquire();u.length=l,(!o||o.buffer!==i.buffer||o.byteOffset!==i.byteOffset)&&(o=new DataView(i.buffer,i.byteOffset));let d=2;for(let h=0;h<l;h++){let p=n.acquire();for(let b of Object.keys(e)){let f=e[b];p[b]=f.read(o,d),d+=f.size}u[h]=p}return{value:u,bytesRead:d}},decode(i){return this.decodeField(i).value},toNil(){return[]}}}};var E=class{constructor(){this.tracker=new Map}get size(){return this.tracker.size}track(e,t){return this.tracker.has(e)||this.tracker.set(e,[]),this.tracker.get(e).push(t),t}dropUpTo(e){let t=[];for(let[s,n]of this.tracker)s<=e?this.tracker.delete(s):t.push([s,n]);return t.sort(([s],[n])=>s-n),t.map(([s,n])=>n).flat()}values(){return Array.from(this.tracker.entries()).sort(([e],[t])=>e-t).map(([e,t])=>t).flat()}},$=class{constructor(e){this.options=e;this.tracker=new E}trackIntent(e,t){this.tracker.track(e,t)}onSnapshot(e){this.options.onLoadState(e.state);let t=this.tracker.dropUpTo(e.tick);t.length>0&&this.options.onReplay(t)}};export{w as BaseBinaryCodec,g as BinaryCodec,y as BinaryPrimitives,A as EventSystem,C as FixedTicker,E as IntentTracker,j as NavMesh,x as ObjectPool,K as PooledArrayDecoder,q as PooledCodec,v as PooledDecoder,I as PooledEncoder,$ as Reconciliator,re as generateId,ce as lerp};
|