@bloopjs/bloop 0.0.13 → 0.0.15

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.
@@ -0,0 +1,298 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import {
3
+ KeyboardContext,
4
+ type KeyState,
5
+ MOUSE_OFFSET,
6
+ MouseContext,
7
+ } from "@bloopjs/engine";
8
+ import { mount } from "../src/runtime";
9
+ import { assert } from "../src/util";
10
+
11
+ it("hello wasm", async () => {
12
+ let count = 0;
13
+ const { wasm } = await mount({
14
+ systemsCallback() {
15
+ count++;
16
+ },
17
+ setBuffer() {},
18
+ });
19
+
20
+ wasm.step(16);
21
+ expect(count).toBe(1);
22
+ });
23
+
24
+ describe("time", () => {
25
+ it("injects frame and dt", async () => {
26
+ const { runtime } = await mount({
27
+ systemsCallback() {},
28
+ setBuffer() {},
29
+ });
30
+
31
+ runtime.step(16);
32
+ expect(runtime.time.frame).toEqual(1);
33
+ expect(runtime.time.dt).toEqual(0.016);
34
+ });
35
+
36
+ it("exposes time context pointer in system callback", async () => {
37
+ let called = false;
38
+ const { runtime } = await mount({
39
+ systemsCallback(_handle, ptr) {
40
+ called = true;
41
+ const dataView = new DataView(runtime.buffer, ptr);
42
+ const timeCtxPtr = dataView.getUint32(0, true);
43
+ const timeDataView = new DataView(runtime.buffer, timeCtxPtr);
44
+ const frame = timeDataView.getUint32(0, true);
45
+ const dt = timeDataView.getUint32(4, true);
46
+ expect(frame).toEqual(0);
47
+ expect(dt).toEqual(16);
48
+ },
49
+ setBuffer() {},
50
+ });
51
+ runtime.step(16);
52
+
53
+ expect(called).toEqual(true);
54
+ });
55
+ });
56
+
57
+ describe("snapshots", () => {
58
+ it("can capture time to a snapshot", async () => {
59
+ const { runtime } = await mount({
60
+ systemsCallback() {},
61
+ setBuffer() {},
62
+ });
63
+
64
+ runtime.step(16);
65
+ runtime.step(16);
66
+ expect(runtime.time.frame).toEqual(2);
67
+ expect(runtime.time.dt).toEqual(0.016);
68
+
69
+ const snapshot = runtime.snapshot();
70
+
71
+ runtime.step(16);
72
+ expect(runtime.time.frame).toEqual(3);
73
+
74
+ runtime.restore(snapshot);
75
+ expect(runtime.time.frame).toEqual(2);
76
+
77
+ runtime.step(16);
78
+ expect(runtime.time.frame).toEqual(3);
79
+ });
80
+ });
81
+
82
+ describe("inputs", () => {
83
+ it("updates input context in response to keyboard events", async () => {
84
+ let called = false;
85
+ const states: KeyState[] = [];
86
+
87
+ const { runtime } = await mount({
88
+ systemsCallback(_handle, ptr) {
89
+ const dataView = new DataView(runtime.buffer, ptr);
90
+ const inputCtxPtr = dataView.getUint32(4, true);
91
+ const inputDataView = new DataView(runtime.buffer, inputCtxPtr);
92
+
93
+ const keyboardContext = new KeyboardContext(inputDataView);
94
+ states.push(keyboardContext.digit8);
95
+
96
+ called = true;
97
+ },
98
+ setBuffer() {},
99
+ });
100
+
101
+ runtime.emit.keydown("Digit8");
102
+ runtime.step();
103
+ runtime.step();
104
+ runtime.emit.keyup("Digit8");
105
+ runtime.step();
106
+ expect(called).toEqual(true);
107
+
108
+ expect(states[0]).toEqual({
109
+ down: true,
110
+ held: true,
111
+ up: false,
112
+ });
113
+
114
+ expect(states[1]).toEqual({
115
+ down: false,
116
+ held: true,
117
+ up: false,
118
+ });
119
+
120
+ expect(states[2]).toEqual({
121
+ down: false,
122
+ held: false,
123
+ up: true,
124
+ });
125
+ });
126
+
127
+ it("updates input context in response to mouse events", async () => {
128
+ let called = false;
129
+ type MouseState = {
130
+ x: number;
131
+ y: number;
132
+ wheelX: number;
133
+ wheelY: number;
134
+ left: KeyState;
135
+ };
136
+ const states: MouseState[] = [];
137
+
138
+ const { runtime } = await mount({
139
+ systemsCallback(_handle, ptr) {
140
+ const dataView = new DataView(runtime.buffer, ptr);
141
+ const inputCtxPtr = dataView.getUint32(4, true);
142
+ const inputDataView = new DataView(runtime.buffer, inputCtxPtr);
143
+
144
+ const dv = new DataView(
145
+ inputDataView.buffer,
146
+ inputDataView.byteOffset + MOUSE_OFFSET,
147
+ );
148
+ const mouseContext = new MouseContext(dv);
149
+
150
+ states.push({
151
+ x: mouseContext.x,
152
+ y: mouseContext.y,
153
+ left: mouseContext.left,
154
+ wheelX: mouseContext.wheelX,
155
+ wheelY: mouseContext.wheelY,
156
+ });
157
+
158
+ called = true;
159
+ },
160
+ setBuffer() {},
161
+ });
162
+
163
+ runtime.emit.mousedown("Left");
164
+ runtime.step();
165
+
166
+ runtime.emit.mousemove(123, 456);
167
+ runtime.emit.mousewheel(789, 101);
168
+ runtime.step();
169
+
170
+ runtime.emit.mouseup("Left");
171
+ runtime.step();
172
+
173
+ expect(called).toEqual(true);
174
+
175
+ expect(states[0]).toMatchObject({
176
+ left: {
177
+ down: true,
178
+ held: true,
179
+ up: false,
180
+ },
181
+ });
182
+
183
+ expect(states[1]).toMatchObject({
184
+ left: {
185
+ down: false,
186
+ held: true,
187
+ up: false,
188
+ },
189
+ x: 123,
190
+ y: 456,
191
+ wheelX: 789,
192
+ wheelY: 101,
193
+ });
194
+
195
+ expect(states[2]).toMatchObject({
196
+ left: {
197
+ down: false,
198
+ held: false,
199
+ up: true,
200
+ },
201
+ });
202
+ });
203
+
204
+ it("updates platform events with input events", async () => {
205
+ let called = false;
206
+ const { runtime } = await mount({
207
+ systemsCallback(_handle, ptr) {
208
+ const dataView = new DataView(runtime.buffer, ptr);
209
+ const eventsPtr = dataView.getUint32(8, true);
210
+ const eventsDataView = new DataView(runtime.buffer, eventsPtr);
211
+ const eventCount = eventsDataView.getUint32(0, true);
212
+ expect(eventCount).toEqual(1);
213
+ // kind = 1 byte + 3 bytes padding
214
+ // payload = 8 bytes
215
+ const typeOffset = 4;
216
+ const payloadOffset = 7;
217
+ const eventType = eventsDataView.getUint8(typeOffset + 0);
218
+ const eventPayload = eventsDataView.getUint8(payloadOffset + 1);
219
+ expect(eventCount).toEqual(1);
220
+ expect(eventType).toEqual(1);
221
+ expect(eventPayload).toEqual(3); // KeyCode for BracketLeft
222
+ called = true;
223
+ },
224
+ setBuffer() {},
225
+ });
226
+
227
+ runtime.emit.keydown("BracketLeft");
228
+ runtime.step();
229
+ expect(called).toEqual(true);
230
+ });
231
+ });
232
+
233
+ describe("tapes", () => {
234
+ describe("engine snapshot", () => {
235
+ it("saves and restores time context", async () => {
236
+ const { runtime } = await mount({
237
+ systemsCallback() {},
238
+ setBuffer() {},
239
+ });
240
+
241
+ const snapshot = runtime.snapshot();
242
+ expect(runtime.time.frame).toEqual(0);
243
+ runtime.step();
244
+ expect(runtime.time.frame).toEqual(1);
245
+ runtime.step();
246
+ expect(runtime.time.frame).toEqual(2);
247
+ runtime.restore(snapshot);
248
+
249
+ expect(runtime.time.frame).toEqual(0);
250
+ });
251
+ });
252
+
253
+ describe("caller payload", () => {
254
+ it("can capture and restore arbitrary payloads", async () => {
255
+ let called = false;
256
+ const { runtime } = await mount({
257
+ startRecording: false,
258
+ systemsCallback() {},
259
+ setBuffer() {},
260
+ serialize() {
261
+ return {
262
+ write(buffer, ptr) {
263
+ const data = new Uint8Array(buffer, ptr, 1);
264
+ data[0] = 66;
265
+ },
266
+ size: 1,
267
+ };
268
+ },
269
+ deserialize(buffer, ptr, length) {
270
+ called = true;
271
+ const data = new Uint8Array(buffer, ptr, length);
272
+ expect(data.byteLength).toBe(1);
273
+ expect(length).toEqual(1);
274
+ expect(data[0]).toBe(66);
275
+ },
276
+ });
277
+
278
+ const snapshot = runtime.snapshot();
279
+ runtime.restore(snapshot);
280
+ expect(called).toEqual(true);
281
+ });
282
+ });
283
+ });
284
+
285
+ function toHexString(bytes: DataView | Uint8Array, length?: number): string {
286
+ const dv =
287
+ bytes instanceof DataView
288
+ ? bytes
289
+ : new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
290
+ length ??= dv.byteLength;
291
+
292
+ let hexString = "";
293
+ for (let i = 0; i < length; i++) {
294
+ const byte = dv.getUint8(i);
295
+ hexString += `${byte.toString(16).padStart(2, "0")} `;
296
+ }
297
+ return hexString.trim();
298
+ }
package/test/tape.test.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { it, expect, describe } from "bun:test";
2
- import { mount } from "@bloopjs/engine";
3
- import { Bloop } from "../src/mod";
1
+ import { describe, expect, it } from "bun:test";
2
+ import { Bloop } from "../src/bloop";
3
+ import { mount } from "../src/runtime";
4
4
 
5
5
  describe("tapes", () => {
6
6
  describe("snapshots", () => {
@@ -21,12 +21,12 @@ describe("tapes", () => {
21
21
  runtime.step();
22
22
 
23
23
  expect(bloop.bag.cool).toEqual(43);
24
- const snapshot = bloop.snapshot();
24
+ const snapshot = runtime.snapshot();
25
25
 
26
26
  runtime.step();
27
27
  expect(bloop.bag.cool).toEqual(44);
28
28
 
29
- bloop.restore(snapshot);
29
+ runtime.restore(snapshot);
30
30
  expect(bloop.bag.cool).toEqual(43);
31
31
  });
32
32
 
@@ -37,9 +37,9 @@ describe("tapes", () => {
37
37
  },
38
38
  });
39
39
 
40
- await mount(bloop);
41
- const snapshot = bloop.snapshot();
42
- bloop.restore(snapshot);
40
+ const { runtime } = await mount(bloop);
41
+ const snapshot = runtime.snapshot();
42
+ runtime.restore(snapshot);
43
43
  expect(bloop.bag.score).toEqual(10);
44
44
  });
45
45
 
@@ -69,7 +69,8 @@ describe("tapes", () => {
69
69
  expect(timeCheck.frame).toEqual(0);
70
70
  expect(bloop.context.time.frame).toEqual(1);
71
71
 
72
- const snapshot = bloop.snapshot();
72
+ // const snapshot = bloop.snapshot();
73
+ const snapshot = runtime.snapshot();
73
74
 
74
75
  runtime.step();
75
76
  expect(timeCheck.dt).toBeCloseTo(0.016);
@@ -77,7 +78,7 @@ describe("tapes", () => {
77
78
  expect(timeCheck.frame).toEqual(1);
78
79
  expect(bloop.context.time.frame).toEqual(2);
79
80
 
80
- bloop.restore(snapshot);
81
+ runtime.restore(snapshot);
81
82
  expect(bloop.context.time.frame).toEqual(1);
82
83
  runtime.step();
83
84
 
@@ -88,7 +89,7 @@ describe("tapes", () => {
88
89
  });
89
90
 
90
91
  describe("playback", () => {
91
- it("can play back inputs", async () => {
92
+ it("can step back", async () => {
92
93
  const bloop = Bloop.create({
93
94
  bag: {
94
95
  clicks: 0,
@@ -103,14 +104,14 @@ describe("tapes", () => {
103
104
  },
104
105
  });
105
106
 
106
- const { runtime, emitter } = await mount(bloop);
107
+ const { runtime } = await mount(bloop);
107
108
 
108
- runtime.record();
109
- emitter.mousedown("Left");
109
+ runtime.emit.mousedown("Left");
110
110
  runtime.step();
111
111
  expect(bloop.context.time.frame).toEqual(1);
112
112
  expect(bloop.bag.clicks).toEqual(1);
113
- emitter.mouseup("Left");
113
+
114
+ runtime.emit.mouseup("Left");
114
115
  runtime.step();
115
116
  expect(bloop.context.time.frame).toEqual(2);
116
117
  expect(bloop.bag.clicks).toEqual(1);