murow 0.0.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/README.md +61 -0
- package/dist/core/binary-codec/binary-codec.d.ts +159 -0
- package/dist/core/binary-codec/binary-codec.js +336 -0
- package/dist/core/binary-codec/index.d.ts +1 -0
- package/dist/core/binary-codec/index.js +1 -0
- package/dist/core/events/event-system.d.ts +71 -0
- package/dist/core/events/event-system.js +88 -0
- package/dist/core/events/index.d.ts +1 -0
- package/dist/core/events/index.js +1 -0
- package/dist/core/fixed-ticker/fixed-ticker.d.ts +105 -0
- package/dist/core/fixed-ticker/fixed-ticker.js +91 -0
- package/dist/core/fixed-ticker/index.d.ts +1 -0
- package/dist/core/fixed-ticker/index.js +1 -0
- package/dist/core/generate-id/generate-id.d.ts +21 -0
- package/dist/core/generate-id/generate-id.js +25 -0
- package/dist/core/generate-id/index.d.ts +1 -0
- package/dist/core/generate-id/index.js +1 -0
- package/dist/core/index.d.ts +8 -0
- package/dist/core/index.js +8 -0
- package/dist/core/lerp/index.d.ts +1 -0
- package/dist/core/lerp/index.js +1 -0
- package/dist/core/lerp/lerp.d.ts +40 -0
- package/dist/core/lerp/lerp.js +42 -0
- package/dist/core/navmesh/index.d.ts +1 -0
- package/dist/core/navmesh/index.js +1 -0
- package/dist/core/navmesh/navmesh.d.ts +116 -0
- package/dist/core/navmesh/navmesh.js +666 -0
- package/dist/core/pooled-codec/index.d.ts +1 -0
- package/dist/core/pooled-codec/index.js +1 -0
- package/dist/core/pooled-codec/pooled-codec.d.ts +140 -0
- package/dist/core/pooled-codec/pooled-codec.js +213 -0
- package/dist/core/prediction/index.d.ts +1 -0
- package/dist/core/prediction/index.js +1 -0
- package/dist/core/prediction/prediction.d.ts +64 -0
- package/dist/core/prediction/prediction.js +90 -0
- package/dist/core.esm.js +1 -0
- package/dist/core.js +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +18 -0
- package/dist/protocol/index.d.ts +43 -0
- package/dist/protocol/index.js +43 -0
- package/dist/protocol/intent/index.d.ts +39 -0
- package/dist/protocol/intent/index.js +38 -0
- package/dist/protocol/intent/intent-registry.d.ts +54 -0
- package/dist/protocol/intent/intent-registry.js +73 -0
- package/dist/protocol/intent/intent.d.ts +12 -0
- package/dist/protocol/intent/intent.js +1 -0
- package/dist/protocol/snapshot/index.d.ts +44 -0
- package/dist/protocol/snapshot/index.js +43 -0
- package/dist/protocol/snapshot/snapshot-codec.d.ts +48 -0
- package/dist/protocol/snapshot/snapshot-codec.js +56 -0
- package/dist/protocol/snapshot/snapshot-registry.d.ts +100 -0
- package/dist/protocol/snapshot/snapshot-registry.js +136 -0
- package/dist/protocol/snapshot/snapshot.d.ts +19 -0
- package/dist/protocol/snapshot/snapshot.js +30 -0
- package/package.json +54 -0
- package/src/core/binary-codec/README.md +60 -0
- package/src/core/binary-codec/binary-codec.test.ts +300 -0
- package/src/core/binary-codec/binary-codec.ts +430 -0
- package/src/core/binary-codec/index.ts +1 -0
- package/src/core/events/README.md +47 -0
- package/src/core/events/event-system.test.ts +243 -0
- package/src/core/events/event-system.ts +140 -0
- package/src/core/events/index.ts +1 -0
- package/src/core/fixed-ticker/README.md +77 -0
- package/src/core/fixed-ticker/fixed-ticker.test.ts +151 -0
- package/src/core/fixed-ticker/fixed-ticker.ts +158 -0
- package/src/core/fixed-ticker/index.ts +1 -0
- package/src/core/generate-id/README.md +18 -0
- package/src/core/generate-id/generate-id.test.ts +79 -0
- package/src/core/generate-id/generate-id.ts +37 -0
- package/src/core/generate-id/index.ts +1 -0
- package/src/core/index.ts +8 -0
- package/src/core/lerp/README.md +79 -0
- package/src/core/lerp/index.ts +1 -0
- package/src/core/lerp/lerp.test.ts +90 -0
- package/src/core/lerp/lerp.ts +42 -0
- package/src/core/navmesh/README.md +124 -0
- package/src/core/navmesh/index.ts +1 -0
- package/src/core/navmesh/navmesh.test.ts +344 -0
- package/src/core/navmesh/navmesh.ts +850 -0
- package/src/core/pooled-codec/README.md +70 -0
- package/src/core/pooled-codec/index.ts +1 -0
- package/src/core/pooled-codec/pooled-codec.test.ts +349 -0
- package/src/core/pooled-codec/pooled-codec.ts +239 -0
- package/src/core/prediction/README.md +64 -0
- package/src/core/prediction/index.ts +1 -0
- package/src/core/prediction/prediction.test.ts +422 -0
- package/src/core/prediction/prediction.ts +101 -0
- package/src/index.ts +20 -0
- package/src/protocol/README.md +310 -0
- package/src/protocol/index.ts +44 -0
- package/src/protocol/intent/index.ts +40 -0
- package/src/protocol/intent/intent-registry.test.ts +237 -0
- package/src/protocol/intent/intent-registry.ts +88 -0
- package/src/protocol/intent/intent.ts +12 -0
- package/src/protocol/snapshot/index.ts +45 -0
- package/src/protocol/snapshot/snapshot-codec.test.ts +138 -0
- package/src/protocol/snapshot/snapshot-codec.ts +71 -0
- package/src/protocol/snapshot/snapshot-registry.test.ts +302 -0
- package/src/protocol/snapshot/snapshot-registry.ts +162 -0
- package/src/protocol/snapshot/snapshot.test.ts +76 -0
- package/src/protocol/snapshot/snapshot.ts +41 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-authoritative snapshot with delta updates.
|
|
3
|
+
*
|
|
4
|
+
* @template T The full state type
|
|
5
|
+
*/
|
|
6
|
+
export interface Snapshot<T> {
|
|
7
|
+
/** Server tick when this snapshot was created */
|
|
8
|
+
tick: number;
|
|
9
|
+
/** Partial state updates (only what changed) */
|
|
10
|
+
updates: Partial<T>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Apply snapshot updates to state (mutating).
|
|
14
|
+
* Deep merges objects, replaces arrays.
|
|
15
|
+
*
|
|
16
|
+
* @param state State to update
|
|
17
|
+
* @param snapshot Snapshot to apply
|
|
18
|
+
*/
|
|
19
|
+
export declare function applySnapshot<T>(state: T, snapshot: Snapshot<T>): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apply snapshot updates to state (mutating).
|
|
3
|
+
* Deep merges objects, replaces arrays.
|
|
4
|
+
*
|
|
5
|
+
* @param state State to update
|
|
6
|
+
* @param snapshot Snapshot to apply
|
|
7
|
+
*/
|
|
8
|
+
export function applySnapshot(state, snapshot) {
|
|
9
|
+
deepMerge(state, snapshot.updates);
|
|
10
|
+
}
|
|
11
|
+
function deepMerge(target, update) {
|
|
12
|
+
for (const key in update) {
|
|
13
|
+
if (!Object.prototype.hasOwnProperty.call(update, key))
|
|
14
|
+
continue;
|
|
15
|
+
const updateValue = update[key];
|
|
16
|
+
const targetValue = target[key];
|
|
17
|
+
if (updateValue === null || updateValue === undefined) {
|
|
18
|
+
target[key] = updateValue;
|
|
19
|
+
}
|
|
20
|
+
else if (Array.isArray(updateValue)) {
|
|
21
|
+
target[key] = updateValue;
|
|
22
|
+
}
|
|
23
|
+
else if (typeof updateValue === "object" && typeof targetValue === "object") {
|
|
24
|
+
deepMerge(targetValue, updateValue);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
target[key] = updateValue;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "murow",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A lightweight TypeScript game engine for server-authoritative multiplayer games",
|
|
5
|
+
"main": "dist/core.js",
|
|
6
|
+
"module": "dist/core.esm.js",
|
|
7
|
+
"types": "dist/core.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/core.esm.js",
|
|
11
|
+
"require": "./dist/core.js",
|
|
12
|
+
"types": "./dist/core.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./core": {
|
|
15
|
+
"import": "./dist/core.esm.js",
|
|
16
|
+
"require": "./dist/core.js",
|
|
17
|
+
"types": "./dist/core.d.ts"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"src"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "npm run build:types && npm run build:js",
|
|
26
|
+
"build:js": "esbuild src/core/index.ts --bundle --outfile=dist/core.js --format=cjs --platform=neutral --minify && esbuild src/core/index.ts --bundle --outfile=dist/core.esm.js --format=esm --platform=neutral --minify",
|
|
27
|
+
"build:types": "tsc --project tsconfig.json",
|
|
28
|
+
"dev": "npm run build:js -- --watch",
|
|
29
|
+
"prepublishOnly": "npm run build",
|
|
30
|
+
"test": "bun test ./src/**/**/*.test.ts"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"game-engine",
|
|
34
|
+
"multiplayer",
|
|
35
|
+
"typescript",
|
|
36
|
+
"server-authoritative",
|
|
37
|
+
"networking",
|
|
38
|
+
"game-development"
|
|
39
|
+
],
|
|
40
|
+
"author": "Luiz Felipe Moureau",
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "https://github.com/mococa/murow.git"
|
|
45
|
+
},
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/mococa/murow/issues"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/bun": "^1.1.16",
|
|
51
|
+
"esbuild": "^0.19.0",
|
|
52
|
+
"typescript": "^5.0.0"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# BinaryCodec
|
|
2
|
+
|
|
3
|
+
A minimal TypeScript library for **schema-driven binary encoding and decoding**.
|
|
4
|
+
Supports fixed-size numeric fields with automatic buffer sizing.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Usage
|
|
9
|
+
|
|
10
|
+
### Define a schema
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { BinaryCodec } from ".";
|
|
14
|
+
|
|
15
|
+
type Player = {
|
|
16
|
+
id: number;
|
|
17
|
+
score: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const playerSchema = {
|
|
21
|
+
id: BinaryCodec.u8,
|
|
22
|
+
score: BinaryCodec.u16,
|
|
23
|
+
};
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Encode an object
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
const data: Player = { id: 1, score: 420 };
|
|
30
|
+
const buffer = BinaryCodec.encode(playerSchema, data);
|
|
31
|
+
|
|
32
|
+
console.log(buffer); // Uint8Array [1, 1, 164] (example)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Decode into an object
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
const target: Player = { id: 0, score: 0 };
|
|
39
|
+
BinaryCodec.decode(playerSchema, buffer, target);
|
|
40
|
+
|
|
41
|
+
console.log(target); // { id: 1, score: 420 }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Features
|
|
47
|
+
|
|
48
|
+
* Fixed-size fields (`u8`, `u16`, `f32`)
|
|
49
|
+
* Automatic buffer sizing
|
|
50
|
+
* Explicit big-endian encoding
|
|
51
|
+
* Safe, re-entrant, and concurrent-friendly
|
|
52
|
+
* Schema-driven and type-safe
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Notes
|
|
57
|
+
|
|
58
|
+
* Object keys define **field order** in the binary layout.
|
|
59
|
+
* Buffer sizes are validated on decode to avoid overflow.
|
|
60
|
+
* Works in browsers and Node.js.
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { BinaryCodec, BinaryPrimitives } from "./binary-codec";
|
|
3
|
+
|
|
4
|
+
describe("BinaryCodec - Primitives", () => {
|
|
5
|
+
test("should encode and decode u8", () => {
|
|
6
|
+
const schema = { value: BinaryPrimitives.u8 };
|
|
7
|
+
const data = { value: 255 };
|
|
8
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
9
|
+
const decoded = BinaryCodec.decode(schema, encoded, { value: 0 });
|
|
10
|
+
expect(decoded.value).toBe(255);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("should encode and decode u16", () => {
|
|
14
|
+
const schema = { value: BinaryPrimitives.u16 };
|
|
15
|
+
const data = { value: 65535 };
|
|
16
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
17
|
+
const decoded = BinaryCodec.decode(schema, encoded, { value: 0 });
|
|
18
|
+
expect(decoded.value).toBe(65535);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("should encode and decode u32", () => {
|
|
22
|
+
const schema = { value: BinaryPrimitives.u32 };
|
|
23
|
+
const data = { value: 4294967295 };
|
|
24
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
25
|
+
const decoded = BinaryCodec.decode(schema, encoded, { value: 0 });
|
|
26
|
+
expect(decoded.value).toBe(4294967295);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("should encode and decode i8", () => {
|
|
30
|
+
const schema = { value: BinaryPrimitives.i8 };
|
|
31
|
+
const data = { value: -128 };
|
|
32
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
33
|
+
const decoded = BinaryCodec.decode(schema, encoded, { value: 0 });
|
|
34
|
+
expect(decoded.value).toBe(-128);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("should encode and decode i16", () => {
|
|
38
|
+
const schema = { value: BinaryPrimitives.i16 };
|
|
39
|
+
const data = { value: -32768 };
|
|
40
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
41
|
+
const decoded = BinaryCodec.decode(schema, encoded, { value: 0 });
|
|
42
|
+
expect(decoded.value).toBe(-32768);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("should encode and decode i32", () => {
|
|
46
|
+
const schema = { value: BinaryPrimitives.i32 };
|
|
47
|
+
const data = { value: -2147483648 };
|
|
48
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
49
|
+
const decoded = BinaryCodec.decode(schema, encoded, { value: 0 });
|
|
50
|
+
expect(decoded.value).toBe(-2147483648);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("should encode and decode f32", () => {
|
|
54
|
+
const schema = { value: BinaryPrimitives.f32 };
|
|
55
|
+
const data = { value: 3.14159 };
|
|
56
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
57
|
+
const decoded = BinaryCodec.decode(schema, encoded, { value: 0 });
|
|
58
|
+
expect(decoded.value).toBeCloseTo(3.14159, 5);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("should encode and decode f64", () => {
|
|
62
|
+
const schema = { value: BinaryPrimitives.f64 };
|
|
63
|
+
const data = { value: Math.PI };
|
|
64
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
65
|
+
const decoded = BinaryCodec.decode(schema, encoded, { value: 0 });
|
|
66
|
+
expect(decoded.value).toBeCloseTo(Math.PI, 15);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("should encode and decode bool", () => {
|
|
70
|
+
const schema = { flag: BinaryPrimitives.bool };
|
|
71
|
+
const data1 = { flag: true };
|
|
72
|
+
const encoded1 = BinaryCodec.encode(schema, data1);
|
|
73
|
+
const decoded1 = BinaryCodec.decode(schema, encoded1, { flag: false });
|
|
74
|
+
expect(decoded1.flag).toBe(true);
|
|
75
|
+
|
|
76
|
+
const data2 = { flag: false };
|
|
77
|
+
const encoded2 = BinaryCodec.encode(schema, data2);
|
|
78
|
+
const decoded2 = BinaryCodec.decode(schema, encoded2, { flag: true });
|
|
79
|
+
expect(decoded2.flag).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("should encode and decode string", () => {
|
|
83
|
+
const schema = { name: BinaryPrimitives.string(20) };
|
|
84
|
+
const data = { name: "Player1" };
|
|
85
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
86
|
+
const decoded = BinaryCodec.decode(schema, encoded, { name: "" });
|
|
87
|
+
expect(decoded.name).toBe("Player1");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("should throw on string too long", () => {
|
|
91
|
+
const schema = { name: BinaryPrimitives.string(5) };
|
|
92
|
+
const data = { name: "TooLongString" };
|
|
93
|
+
expect(() => BinaryCodec.encode(schema, data)).toThrow();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("should encode and decode vec2", () => {
|
|
97
|
+
const schema = { pos: BinaryPrimitives.vec2 };
|
|
98
|
+
const data = { pos: { x: 10.5, y: 20.7 } };
|
|
99
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
100
|
+
const decoded = BinaryCodec.decode(schema, encoded, { pos: { x: 0, y: 0 } });
|
|
101
|
+
expect(decoded.pos.x).toBeCloseTo(10.5, 5);
|
|
102
|
+
expect(decoded.pos.y).toBeCloseTo(20.7, 5);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("should encode and decode vec3", () => {
|
|
106
|
+
const schema = { pos: BinaryPrimitives.vec3 };
|
|
107
|
+
const data = { pos: { x: 1.1, y: 2.2, z: 3.3 } };
|
|
108
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
109
|
+
const decoded = BinaryCodec.decode(schema, encoded, {
|
|
110
|
+
pos: { x: 0, y: 0, z: 0 },
|
|
111
|
+
});
|
|
112
|
+
expect(decoded.pos.x).toBeCloseTo(1.1, 5);
|
|
113
|
+
expect(decoded.pos.y).toBeCloseTo(2.2, 5);
|
|
114
|
+
expect(decoded.pos.z).toBeCloseTo(3.3, 5);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("should encode and decode color", () => {
|
|
118
|
+
const schema = { color: BinaryPrimitives.color };
|
|
119
|
+
const data = { color: { r: 255, g: 128, b: 64, a: 32 } };
|
|
120
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
121
|
+
const decoded = BinaryCodec.decode(schema, encoded, {
|
|
122
|
+
color: { r: 0, g: 0, b: 0, a: 0 },
|
|
123
|
+
});
|
|
124
|
+
expect(decoded.color.r).toBe(255);
|
|
125
|
+
expect(decoded.color.g).toBe(128);
|
|
126
|
+
expect(decoded.color.b).toBe(64);
|
|
127
|
+
expect(decoded.color.a).toBe(32);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe("BinaryCodec - Complex Schemas", () => {
|
|
132
|
+
test("should encode and decode multiple fields", () => {
|
|
133
|
+
const schema = {
|
|
134
|
+
id: BinaryPrimitives.u32,
|
|
135
|
+
name: BinaryPrimitives.string(10),
|
|
136
|
+
health: BinaryPrimitives.f32,
|
|
137
|
+
alive: BinaryPrimitives.bool,
|
|
138
|
+
};
|
|
139
|
+
const data = {
|
|
140
|
+
id: 12345,
|
|
141
|
+
name: "Hero",
|
|
142
|
+
health: 85.5,
|
|
143
|
+
alive: true,
|
|
144
|
+
};
|
|
145
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
146
|
+
const decoded = BinaryCodec.decode(schema, encoded, {
|
|
147
|
+
id: 0,
|
|
148
|
+
name: "",
|
|
149
|
+
health: 0,
|
|
150
|
+
alive: false,
|
|
151
|
+
});
|
|
152
|
+
expect(decoded.id).toBe(12345);
|
|
153
|
+
expect(decoded.name).toBe("Hero");
|
|
154
|
+
expect(decoded.health).toBeCloseTo(85.5, 5);
|
|
155
|
+
expect(decoded.alive).toBe(true);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("should maintain field order", () => {
|
|
159
|
+
const schema = {
|
|
160
|
+
a: BinaryPrimitives.u8,
|
|
161
|
+
b: BinaryPrimitives.u8,
|
|
162
|
+
c: BinaryPrimitives.u8,
|
|
163
|
+
};
|
|
164
|
+
const data = { a: 1, b: 2, c: 3 };
|
|
165
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
166
|
+
expect(Array.from(encoded)).toEqual([1, 2, 3]);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("should throw on buffer too small", () => {
|
|
170
|
+
const schema = { value: BinaryPrimitives.u32 };
|
|
171
|
+
const smallBuffer = new Uint8Array(2); // Only 2 bytes, need 4
|
|
172
|
+
expect(() =>
|
|
173
|
+
BinaryCodec.decode(schema, smallBuffer, { value: 0 })
|
|
174
|
+
).toThrow();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("should handle empty string", () => {
|
|
178
|
+
const schema = { name: BinaryPrimitives.string(10) };
|
|
179
|
+
const data = { name: "" };
|
|
180
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
181
|
+
const decoded = BinaryCodec.decode(schema, encoded, { name: "default" });
|
|
182
|
+
expect(decoded.name).toBe("");
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test("should handle unicode strings", () => {
|
|
186
|
+
const schema = { text: BinaryPrimitives.string(20) };
|
|
187
|
+
const data = { text: "Hello 世界" };
|
|
188
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
189
|
+
const decoded = BinaryCodec.decode(schema, encoded, { text: "" });
|
|
190
|
+
expect(decoded.text).toBe("Hello 世界");
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe("BinaryCodec - Little Endian Variants", () => {
|
|
195
|
+
test("should encode and decode f32_le", () => {
|
|
196
|
+
const schema = { value: BinaryPrimitives.f32_le };
|
|
197
|
+
const data = { value: 1.234 };
|
|
198
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
199
|
+
const decoded = BinaryCodec.decode(schema, encoded, { value: 0 });
|
|
200
|
+
expect(decoded.value).toBeCloseTo(1.234, 5);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("should encode and decode f64_le", () => {
|
|
204
|
+
const schema = { value: BinaryPrimitives.f64_le };
|
|
205
|
+
const data = { value: Math.E };
|
|
206
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
207
|
+
const decoded = BinaryCodec.decode(schema, encoded, { value: 0 });
|
|
208
|
+
expect(decoded.value).toBeCloseTo(Math.E, 15);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("should encode and decode vec2_le", () => {
|
|
212
|
+
const schema = { pos: BinaryPrimitives.vec2_le };
|
|
213
|
+
const data = { pos: [5.5, 6.6] as [number, number] };
|
|
214
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
215
|
+
const decoded = BinaryCodec.decode(schema, encoded, { pos: [0, 0] as [number, number] });
|
|
216
|
+
expect(decoded.pos[0]).toBeCloseTo(5.5, 5);
|
|
217
|
+
expect(decoded.pos[1]).toBeCloseTo(6.6, 5);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("should encode and decode vec3_le", () => {
|
|
221
|
+
const schema = { pos: BinaryPrimitives.vec3_le };
|
|
222
|
+
const data = { pos: [1.1, 2.2, 3.3] as [number, number, number] };
|
|
223
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
224
|
+
const decoded = BinaryCodec.decode(schema, encoded, {
|
|
225
|
+
pos: [0, 0, 0] as [number, number, number],
|
|
226
|
+
});
|
|
227
|
+
expect(decoded.pos[0]).toBeCloseTo(1.1, 5);
|
|
228
|
+
expect(decoded.pos[1]).toBeCloseTo(2.2, 5);
|
|
229
|
+
expect(decoded.pos[2]).toBeCloseTo(3.3, 5);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test("should encode and decode vec4_le", () => {
|
|
233
|
+
const schema = { quat: BinaryPrimitives.vec4_le };
|
|
234
|
+
const data = { quat: [0.1, 0.2, 0.3, 0.4] as [number, number, number, number] };
|
|
235
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
236
|
+
const decoded = BinaryCodec.decode(schema, encoded, {
|
|
237
|
+
quat: [0, 0, 0, 0] as [number, number, number, number],
|
|
238
|
+
});
|
|
239
|
+
expect(decoded.quat[0]).toBeCloseTo(0.1, 5);
|
|
240
|
+
expect(decoded.quat[1]).toBeCloseTo(0.2, 5);
|
|
241
|
+
expect(decoded.quat[2]).toBeCloseTo(0.3, 5);
|
|
242
|
+
expect(decoded.quat[3]).toBeCloseTo(0.4, 5);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
describe("BinaryCodec - Edge Cases", () => {
|
|
247
|
+
test("should handle zero values", () => {
|
|
248
|
+
const schema = {
|
|
249
|
+
u8: BinaryPrimitives.u8,
|
|
250
|
+
i32: BinaryPrimitives.i32,
|
|
251
|
+
f32: BinaryPrimitives.f32,
|
|
252
|
+
};
|
|
253
|
+
const data = { u8: 0, i32: 0, f32: 0 };
|
|
254
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
255
|
+
const decoded = BinaryCodec.decode(schema, encoded, {
|
|
256
|
+
u8: 1,
|
|
257
|
+
i32: 1,
|
|
258
|
+
f32: 1,
|
|
259
|
+
});
|
|
260
|
+
expect(decoded.u8).toBe(0);
|
|
261
|
+
expect(decoded.i32).toBe(0);
|
|
262
|
+
expect(decoded.f32).toBe(0);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test("should handle max values", () => {
|
|
266
|
+
const schema = {
|
|
267
|
+
u8: BinaryPrimitives.u8,
|
|
268
|
+
u16: BinaryPrimitives.u16,
|
|
269
|
+
u32: BinaryPrimitives.u32,
|
|
270
|
+
};
|
|
271
|
+
const data = { u8: 255, u16: 65535, u32: 4294967295 };
|
|
272
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
273
|
+
const decoded = BinaryCodec.decode(schema, encoded, {
|
|
274
|
+
u8: 0,
|
|
275
|
+
u16: 0,
|
|
276
|
+
u32: 0,
|
|
277
|
+
});
|
|
278
|
+
expect(decoded.u8).toBe(255);
|
|
279
|
+
expect(decoded.u16).toBe(65535);
|
|
280
|
+
expect(decoded.u32).toBe(4294967295);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("should handle min signed values", () => {
|
|
284
|
+
const schema = {
|
|
285
|
+
i8: BinaryPrimitives.i8,
|
|
286
|
+
i16: BinaryPrimitives.i16,
|
|
287
|
+
i32: BinaryPrimitives.i32,
|
|
288
|
+
};
|
|
289
|
+
const data = { i8: -128, i16: -32768, i32: -2147483648 };
|
|
290
|
+
const encoded = BinaryCodec.encode(schema, data);
|
|
291
|
+
const decoded = BinaryCodec.decode(schema, encoded, {
|
|
292
|
+
i8: 0,
|
|
293
|
+
i16: 0,
|
|
294
|
+
i32: 0,
|
|
295
|
+
});
|
|
296
|
+
expect(decoded.i8).toBe(-128);
|
|
297
|
+
expect(decoded.i16).toBe(-32768);
|
|
298
|
+
expect(decoded.i32).toBe(-2147483648);
|
|
299
|
+
});
|
|
300
|
+
});
|