@bloopjs/bloop 0.0.11 → 0.0.12
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/jsr.json +1 -1
- package/package.json +2 -2
- package/src/context.ts +46 -0
- package/src/mod.ts +27 -6
- package/test/tape.test.ts +47 -0
package/jsr.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bloopjs/bloop",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"@types/bun": "latest"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@bloopjs/engine": "0.0.
|
|
26
|
+
"@bloopjs/engine": "0.0.12"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"typescript": "^5"
|
package/src/context.ts
CHANGED
|
@@ -39,3 +39,49 @@ export type TimingSnapshot = {
|
|
|
39
39
|
/** The total number of milliseconds since the engine started */
|
|
40
40
|
highResTime: bigint;
|
|
41
41
|
};
|
|
42
|
+
|
|
43
|
+
export const TIMING_SNAPSHOT_SIZE = 32;
|
|
44
|
+
|
|
45
|
+
// these functions should go in engine
|
|
46
|
+
export function encodeTimingSnapshot(
|
|
47
|
+
ts: TimingSnapshot,
|
|
48
|
+
target: Uint8Array,
|
|
49
|
+
): number {
|
|
50
|
+
if (target.byteLength !== TIMING_SNAPSHOT_SIZE) {
|
|
51
|
+
throw new Error("Target buffer must be exactly 32 bytes");
|
|
52
|
+
}
|
|
53
|
+
const view = new DataView(
|
|
54
|
+
target.buffer,
|
|
55
|
+
target.byteOffset,
|
|
56
|
+
target.byteLength,
|
|
57
|
+
);
|
|
58
|
+
view.setFloat32(0, ts.dt, true);
|
|
59
|
+
view.setFloat32(4, ts.time, true);
|
|
60
|
+
view.setBigUint64(8, ts.highResFrame, true);
|
|
61
|
+
view.setBigUint64(16, ts.highResTime, true);
|
|
62
|
+
view.setUint32(24, ts.frame, true);
|
|
63
|
+
return TIMING_SNAPSHOT_SIZE;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function decodeTimingSnapshot(
|
|
67
|
+
source: Uint8Array,
|
|
68
|
+
target: TimingSnapshot,
|
|
69
|
+
): number {
|
|
70
|
+
if (source.byteLength !== TIMING_SNAPSHOT_SIZE) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
`Source buffer must be exactly ${TIMING_SNAPSHOT_SIZE} bytes`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
const view = new DataView(
|
|
76
|
+
source.buffer,
|
|
77
|
+
source.byteOffset,
|
|
78
|
+
source.byteLength,
|
|
79
|
+
);
|
|
80
|
+
target.dt = view.getFloat32(0, true);
|
|
81
|
+
target.time = view.getFloat32(4, true);
|
|
82
|
+
target.highResFrame = view.getBigUint64(8, true);
|
|
83
|
+
target.highResTime = view.getBigUint64(16, true);
|
|
84
|
+
target.frame = view.getUint32(24, true);
|
|
85
|
+
|
|
86
|
+
return TIMING_SNAPSHOT_SIZE;
|
|
87
|
+
}
|
package/src/mod.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { BloopSchema } from "./data/schema";
|
|
|
2
2
|
import type { Bag } from "./data/bag";
|
|
3
3
|
import type { System } from "./system";
|
|
4
4
|
import { EngineInputs, type PlatformEvent } from "@bloopjs/engine";
|
|
5
|
-
import type
|
|
5
|
+
import { decodeTimingSnapshot, encodeTimingSnapshot, TIMING_SNAPSHOT_SIZE, type Context } from "./context";
|
|
6
6
|
|
|
7
7
|
export type BloopOpts<B extends Bag> = {
|
|
8
8
|
/** defaults to "Game" */
|
|
@@ -53,6 +53,7 @@ export class Bloop<GS extends BloopSchema> {
|
|
|
53
53
|
|
|
54
54
|
this.#context = {
|
|
55
55
|
bag: opts.bag ?? {},
|
|
56
|
+
// todo: this should be a pointer to memory managed by the engine
|
|
56
57
|
inputs: new EngineInputs.InputSnapshot(new DataView(new ArrayBuffer(100))),
|
|
57
58
|
time: {
|
|
58
59
|
dt: 0,
|
|
@@ -68,6 +69,10 @@ export class Bloop<GS extends BloopSchema> {
|
|
|
68
69
|
return this.#context.bag;
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
get context() : Readonly<Context<GS>> {
|
|
73
|
+
return this.#context;
|
|
74
|
+
}
|
|
75
|
+
|
|
71
76
|
/**
|
|
72
77
|
* Take a snapshot of the game state
|
|
73
78
|
* @returns linear memory representation of the game state
|
|
@@ -77,25 +82,30 @@ export class Bloop<GS extends BloopSchema> {
|
|
|
77
82
|
const encoder = new TextEncoder();
|
|
78
83
|
const textBytes = encoder.encode(str);
|
|
79
84
|
|
|
80
|
-
const
|
|
85
|
+
const size = TIMING_SNAPSHOT_SIZE + 4 + textBytes.length;
|
|
86
|
+
|
|
87
|
+
const buffer = new Uint8Array(size);
|
|
81
88
|
const view = new DataView(buffer.buffer);
|
|
82
|
-
let offset = 0;
|
|
83
|
-
view.setUint32(
|
|
89
|
+
let offset = encodeTimingSnapshot(this.#context.time, buffer.subarray(0, TIMING_SNAPSHOT_SIZE));
|
|
90
|
+
view.setUint32(offset, textBytes.length, true);
|
|
84
91
|
offset += 4;
|
|
92
|
+
|
|
85
93
|
buffer.set(textBytes, offset);
|
|
86
94
|
offset += textBytes.length;
|
|
87
95
|
return buffer;
|
|
88
96
|
}
|
|
89
97
|
|
|
90
98
|
restore(snapshot: Uint8Array) {
|
|
99
|
+
const size = decodeTimingSnapshot(new Uint8Array(snapshot.buffer, 0, 32), this.#context.time);
|
|
91
100
|
const view = new DataView(snapshot.buffer);
|
|
92
|
-
let offset =
|
|
101
|
+
let offset = size;
|
|
93
102
|
const length = view.getUint32(0, true);
|
|
94
103
|
offset += 4;
|
|
95
104
|
const bagBytes = snapshot.slice(offset, offset + length);
|
|
96
105
|
const decoder = new TextDecoder();
|
|
97
106
|
const str = decoder.decode(bagBytes);
|
|
98
107
|
this.#context.bag = JSON.parse(str);
|
|
108
|
+
|
|
99
109
|
}
|
|
100
110
|
|
|
101
111
|
/**
|
|
@@ -108,8 +118,14 @@ export class Bloop<GS extends BloopSchema> {
|
|
|
108
118
|
return this.#systems.length;
|
|
109
119
|
}
|
|
110
120
|
|
|
111
|
-
|
|
121
|
+
// todo - get data from ptr
|
|
122
|
+
systemsCallback(ptr: number, events: PlatformEvent[], dt: number) {
|
|
112
123
|
this.#context.inputs.update(events);
|
|
124
|
+
const dtSeconds = dt / 1000;
|
|
125
|
+
this.#context.time.dt = dtSeconds;
|
|
126
|
+
this.#context.time.time += dtSeconds;
|
|
127
|
+
this.#context.time.highResTime += BigInt(dt);
|
|
128
|
+
|
|
113
129
|
for (const system of this.#systems) {
|
|
114
130
|
system.update?.(this.#context);
|
|
115
131
|
|
|
@@ -174,6 +190,11 @@ export class Bloop<GS extends BloopSchema> {
|
|
|
174
190
|
}
|
|
175
191
|
}
|
|
176
192
|
}
|
|
193
|
+
|
|
194
|
+
// do this in the engine snapshot
|
|
195
|
+
this.#context.time.frame++;
|
|
196
|
+
this.#context.time.highResFrame += 1n;
|
|
197
|
+
|
|
177
198
|
this.#context.inputs.flush();
|
|
178
199
|
}
|
|
179
200
|
}
|
package/test/tape.test.ts
CHANGED
|
@@ -29,5 +29,52 @@ describe("tapes", () => {
|
|
|
29
29
|
bloop.restore(snapshot);
|
|
30
30
|
expect(bloop.bag.cool).toEqual(43);
|
|
31
31
|
});
|
|
32
|
+
|
|
33
|
+
it("snapshots time", async () => {
|
|
34
|
+
const bloop = Bloop.create({
|
|
35
|
+
bag: {
|
|
36
|
+
nope: 10,
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const timeCheck = {
|
|
41
|
+
dt: 0,
|
|
42
|
+
time: 0,
|
|
43
|
+
frame: 0,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
bloop.system("timeCheck", {
|
|
47
|
+
update({ time }) {
|
|
48
|
+
timeCheck.dt = time.dt;
|
|
49
|
+
timeCheck.time = time.time;
|
|
50
|
+
timeCheck.frame = time.frame;
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const { runtime } = await mount(bloop);
|
|
55
|
+
|
|
56
|
+
runtime.step();
|
|
57
|
+
|
|
58
|
+
expect(timeCheck.dt).toBeCloseTo(0.016);
|
|
59
|
+
expect(timeCheck.time).toBeCloseTo(0.016);
|
|
60
|
+
expect(timeCheck.frame).toEqual(0);
|
|
61
|
+
expect(bloop.context.time.frame).toEqual(1);
|
|
62
|
+
|
|
63
|
+
const snapshot = bloop.snapshot();
|
|
64
|
+
|
|
65
|
+
runtime.step();
|
|
66
|
+
expect(timeCheck.dt).toBeCloseTo(0.016);
|
|
67
|
+
expect(timeCheck.time).toBeCloseTo(0.016 * 2);
|
|
68
|
+
expect(timeCheck.frame).toEqual(1);
|
|
69
|
+
expect(bloop.context.time.frame).toEqual(2);
|
|
70
|
+
|
|
71
|
+
bloop.restore(snapshot);
|
|
72
|
+
expect(bloop.context.time.frame).toEqual(1);
|
|
73
|
+
runtime.step();
|
|
74
|
+
|
|
75
|
+
expect(timeCheck.dt).toBeCloseTo(0.016);
|
|
76
|
+
expect(timeCheck.time).toBeCloseTo(0.016 * 2);
|
|
77
|
+
expect(bloop.context.time.frame).toEqual(2);
|
|
78
|
+
});
|
|
32
79
|
});
|
|
33
80
|
});
|