@bloopjs/bloop 0.0.11 → 0.0.13

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bloop/bloop",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "license": "MIT",
5
5
  "exports": "./src/mod.ts"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bloopjs/bloop",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
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.11"
26
+ "@bloopjs/engine": "0.0.13"
27
27
  },
28
28
  "peerDependencies": {
29
29
  "typescript": "^5"
package/src/context.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { InputSnapshot } from "@bloopjs/engine";
1
+ import type { EngineTiming, InputSnapshot } from "@bloopjs/engine";
2
2
  import type { BloopSchema } from "./data/schema";
3
3
 
4
4
  export type Context<
@@ -21,21 +21,5 @@ export type Context<
21
21
  /** The input snapshot */
22
22
  inputs: InputSnapshot;
23
23
  /** The timing information for the current frame */
24
- time: TimingSnapshot;
25
- };
26
-
27
- export type TimingSnapshot = {
28
- /** The number of seconds (usually fractional) since the last frame */
29
- dt: number;
30
- /** The total number of seconds since the engine started */
31
- time: number;
32
- /** The number of frames rendered since the engine started */
33
- frame: number;
34
-
35
- /** The current frame rate of the engine in frames per second */
36
- // fps: number;
37
- /** The number of frames rendered since the engine started */
38
- highResFrame: bigint;
39
- /** The total number of milliseconds since the engine started */
40
- highResTime: bigint;
24
+ time: EngineTiming.TimingSnapshot;
41
25
  };
package/src/mod.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import type { BloopSchema } from "./data/schema";
2
2
  import type { Bag } from "./data/bag";
3
3
  import type { System } from "./system";
4
- import { EngineInputs, type PlatformEvent } from "@bloopjs/engine";
5
- import type { Context } from "./context";
4
+ import { EngineTiming, EngineInputs, type PlatformEvent } from "@bloopjs/engine";
5
+ import { type Context } from "./context";
6
+ import { TIMING_SNAPSHOT_SIZE } from "../../engine/js/timing";
6
7
 
7
8
  export type BloopOpts<B extends Bag> = {
8
9
  /** defaults to "Game" */
@@ -53,6 +54,7 @@ export class Bloop<GS extends BloopSchema> {
53
54
 
54
55
  this.#context = {
55
56
  bag: opts.bag ?? {},
57
+ // todo: this should be a pointer to memory managed by the engine
56
58
  inputs: new EngineInputs.InputSnapshot(new DataView(new ArrayBuffer(100))),
57
59
  time: {
58
60
  dt: 0,
@@ -64,10 +66,14 @@ export class Bloop<GS extends BloopSchema> {
64
66
  };
65
67
  }
66
68
 
67
- get bag() {
69
+ get bag(): GS["B"] {
68
70
  return this.#context.bag;
69
71
  }
70
72
 
73
+ get context() : Readonly<Context<GS>> {
74
+ return this.#context;
75
+ }
76
+
71
77
  /**
72
78
  * Take a snapshot of the game state
73
79
  * @returns linear memory representation of the game state
@@ -77,20 +83,24 @@ export class Bloop<GS extends BloopSchema> {
77
83
  const encoder = new TextEncoder();
78
84
  const textBytes = encoder.encode(str);
79
85
 
80
- const buffer = new Uint8Array(1024*1024);
86
+ const size = EngineTiming.TIMING_SNAPSHOT_SIZE + 4 + textBytes.length;
87
+
88
+ const buffer = new Uint8Array(size);
81
89
  const view = new DataView(buffer.buffer);
82
- let offset = 0;
83
- view.setUint32(0, textBytes.length, true);
90
+ let offset = EngineTiming.encodeTimingSnapshot(this.#context.time, buffer.subarray(0, EngineTiming.TIMING_SNAPSHOT_SIZE));
91
+ view.setUint32(offset, textBytes.length, true);
84
92
  offset += 4;
93
+
85
94
  buffer.set(textBytes, offset);
86
95
  offset += textBytes.length;
87
96
  return buffer;
88
97
  }
89
98
 
90
99
  restore(snapshot: Uint8Array) {
100
+ const size = EngineTiming.decodeTimingSnapshot(new Uint8Array(snapshot.buffer, 0, EngineTiming.TIMING_SNAPSHOT_SIZE), this.#context.time);
91
101
  const view = new DataView(snapshot.buffer);
92
- let offset = 0;
93
- const length = view.getUint32(0, true);
102
+ let offset = size;
103
+ const length = view.getUint32(offset, true);
94
104
  offset += 4;
95
105
  const bagBytes = snapshot.slice(offset, offset + length);
96
106
  const decoder = new TextDecoder();
@@ -108,8 +118,14 @@ export class Bloop<GS extends BloopSchema> {
108
118
  return this.#systems.length;
109
119
  }
110
120
 
111
- systemsCallback(events: PlatformEvent[]) {
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,99 @@ describe("tapes", () => {
29
29
  bloop.restore(snapshot);
30
30
  expect(bloop.bag.cool).toEqual(43);
31
31
  });
32
+
33
+ it("can snapshot the bag on frame 0", async () => {
34
+ const bloop = Bloop.create({
35
+ bag: {
36
+ score: 10,
37
+ },
38
+ });
39
+
40
+ await mount(bloop);
41
+ const snapshot = bloop.snapshot();
42
+ bloop.restore(snapshot);
43
+ expect(bloop.bag.score).toEqual(10);
44
+ });
45
+
46
+ it("snapshots time", async () => {
47
+ const bloop = Bloop.create();
48
+
49
+ const timeCheck = {
50
+ dt: 0,
51
+ time: 0,
52
+ frame: 0,
53
+ };
54
+
55
+ bloop.system("timeCheck", {
56
+ update({ time }) {
57
+ timeCheck.dt = time.dt;
58
+ timeCheck.time = time.time;
59
+ timeCheck.frame = time.frame;
60
+ },
61
+ });
62
+
63
+ const { runtime } = await mount(bloop);
64
+
65
+ runtime.step();
66
+
67
+ expect(timeCheck.dt).toBeCloseTo(0.016);
68
+ expect(timeCheck.time).toBeCloseTo(0.016);
69
+ expect(timeCheck.frame).toEqual(0);
70
+ expect(bloop.context.time.frame).toEqual(1);
71
+
72
+ const snapshot = bloop.snapshot();
73
+
74
+ runtime.step();
75
+ expect(timeCheck.dt).toBeCloseTo(0.016);
76
+ expect(timeCheck.time).toBeCloseTo(0.016 * 2);
77
+ expect(timeCheck.frame).toEqual(1);
78
+ expect(bloop.context.time.frame).toEqual(2);
79
+
80
+ bloop.restore(snapshot);
81
+ expect(bloop.context.time.frame).toEqual(1);
82
+ runtime.step();
83
+
84
+ expect(timeCheck.dt).toBeCloseTo(0.016);
85
+ expect(timeCheck.time).toBeCloseTo(0.016 * 2);
86
+ expect(bloop.context.time.frame).toEqual(2);
87
+ });
88
+ });
89
+
90
+ describe("playback", () => {
91
+ it("can play back inputs", async () => {
92
+ const bloop = Bloop.create({
93
+ bag: {
94
+ clicks: 0,
95
+ },
96
+ });
97
+
98
+ bloop.system("countClicks", {
99
+ update({ bag, inputs }) {
100
+ if (inputs.mouse.left.down) {
101
+ bag.clicks++;
102
+ }
103
+ },
104
+ });
105
+
106
+ const { runtime, emitter } = await mount(bloop);
107
+
108
+ runtime.record();
109
+ emitter.mousedown("Left");
110
+ runtime.step();
111
+ expect(bloop.context.time.frame).toEqual(1);
112
+ expect(bloop.bag.clicks).toEqual(1);
113
+ emitter.mouseup("Left");
114
+ runtime.step();
115
+ expect(bloop.context.time.frame).toEqual(2);
116
+ expect(bloop.bag.clicks).toEqual(1);
117
+
118
+ runtime.stepBack();
119
+ expect(bloop.context.time.frame).toEqual(1);
120
+ expect(bloop.bag.clicks).toEqual(1);
121
+
122
+ runtime.stepBack();
123
+ expect(bloop.context.time.frame).toEqual(0);
124
+ expect(bloop.bag.clicks).toEqual(0);
125
+ });
32
126
  });
33
127
  });