@bloopjs/bloop 0.0.20 → 0.0.22

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.
@@ -1,227 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import type { Key, MouseButton } from "@bloopjs/engine";
3
- import { Bloop } from "../src/bloop";
4
- import { mount } from "../src/runtime";
5
-
6
- describe("inputs", () => {
7
- it("runs a single system", async () => {
8
- const bloop = Bloop.create();
9
- let count = 0;
10
-
11
- bloop.system("test", {
12
- update() {
13
- count++;
14
- },
15
- });
16
-
17
- const { runtime } = await mount(bloop);
18
- runtime.step();
19
-
20
- expect(count).toEqual(1);
21
- });
22
-
23
- it("routes keydown event", async () => {
24
- const bloop = Bloop.create();
25
- let receivedKey: Key | null = null;
26
- let called = false;
27
- bloop.system("input", {
28
- keydown({ event }) {
29
- receivedKey = event.key;
30
- called = true;
31
- },
32
- });
33
-
34
- const { runtime } = await mount(bloop);
35
- runtime.emit.keydown("Space");
36
- runtime.step();
37
-
38
- expect(called).toBe(true);
39
- expect(receivedKey!).toEqual("Space");
40
- });
41
-
42
- it("routes all input events", async () => {
43
- const bloop = Bloop.create();
44
-
45
- const events = {
46
- keydown: null as Key | null,
47
- keyup: null as Key | null,
48
-
49
- mousemove: null as { x: number; y: number } | null,
50
- mousedown: null as MouseButton | null,
51
- mouseup: null as MouseButton | null,
52
- mousewheel: null as { x: number; y: number } | null,
53
- };
54
-
55
- bloop.system("input", {
56
- keydown({ event }) {
57
- events.keydown = event.key;
58
- },
59
- keyup({ event }) {
60
- events.keyup = event.key;
61
- },
62
- mousemove({ event }) {
63
- events.mousemove = { x: event.x, y: event.y };
64
- },
65
- mousedown({ event }) {
66
- events.mousedown = event.button;
67
- },
68
- mouseup({ event }) {
69
- events.mouseup = event.button;
70
- },
71
- mousewheel({ event }) {
72
- events.mousewheel = event;
73
- },
74
- });
75
-
76
- const { runtime } = await mount(bloop);
77
-
78
- runtime.emit.keydown("Space");
79
- runtime.emit.mousedown("Left");
80
- runtime.emit.mousemove(100, 150);
81
- runtime.emit.mousewheel(1, 2);
82
-
83
- runtime.step();
84
-
85
- expect(events.keydown).toEqual("Space");
86
- expect(events.mousemove).toEqual({ x: 100, y: 150 });
87
- expect(events.mousedown).toEqual("Left");
88
- expect(events.mousewheel).toEqual({ x: 1, y: 2 });
89
-
90
- runtime.emit.keyup("Space");
91
- runtime.emit.mouseup("Left");
92
- runtime.emit.mousemove(3, 4);
93
-
94
- runtime.step();
95
-
96
- expect(events.keyup).toEqual("Space");
97
- expect(events.mouseup).toEqual("Left");
98
- expect(events.mousemove).toEqual({ x: 3, y: 4 });
99
- });
100
-
101
- it("exposes keyboard context", async () => {
102
- const bloop = Bloop.create({
103
- bag: {
104
- down: null as boolean | null,
105
- held: null as boolean | null,
106
- up: null as boolean | null,
107
- },
108
- });
109
-
110
- bloop.system("key state", {
111
- update({ inputs, bag }) {
112
- bag.down = inputs.keys.backquote.down;
113
- bag.held = inputs.keys.backquote.held;
114
- bag.up = inputs.keys.backquote.up;
115
- },
116
- });
117
-
118
- const { runtime } = await mount(bloop);
119
-
120
- // Initial state
121
- runtime.step();
122
- expect(bloop.bag).toEqual({
123
- down: false,
124
- held: false,
125
- up: false,
126
- });
127
-
128
- // down and held are both true on the first frame of a key down
129
- runtime.emit.keydown("Backquote");
130
- runtime.step();
131
- expect(bloop.bag).toEqual({
132
- down: true,
133
- held: true,
134
- up: false,
135
- });
136
-
137
- // held remains true, down goes false
138
- runtime.step();
139
- expect(bloop.bag).toEqual({
140
- down: false,
141
- held: true,
142
- up: false,
143
- });
144
-
145
- // on key up, up is true, held and down are false
146
- runtime.emit.keyup("Backquote");
147
- runtime.step();
148
- expect(bloop.bag).toEqual({
149
- down: false,
150
- held: false,
151
- up: true,
152
- });
153
- });
154
-
155
- it("exposes mouse context", async () => {
156
- const bloop = Bloop.create({
157
- bag: {
158
- down: null as boolean | null,
159
- held: null as boolean | null,
160
- up: null as boolean | null,
161
- position: null as { x: number; y: number } | null,
162
- wheel: null as { x: number; y: number } | null,
163
- },
164
- });
165
-
166
- bloop.system("mouse state", {
167
- update({ inputs, bag }) {
168
- bag.down = inputs.mouse.left.down;
169
- bag.held = inputs.mouse.left.held;
170
- bag.up = inputs.mouse.left.up;
171
- bag.position = { x: inputs.mouse.x, y: inputs.mouse.y };
172
- bag.wheel = inputs.mouse.wheel;
173
- },
174
- });
175
-
176
- const { runtime } = await mount(bloop);
177
-
178
- // Initial state
179
- runtime.step();
180
- expect(bloop.bag).toEqual({
181
- down: false,
182
- held: false,
183
- up: false,
184
- position: { x: 0, y: 0 },
185
- wheel: { x: 0, y: 0 },
186
- });
187
-
188
- // down and held are both true on the first frame of a key down
189
- runtime.emit.mousedown("Left");
190
- runtime.step();
191
- expect(bloop.bag).toMatchObject({
192
- down: true,
193
- held: true,
194
- up: false,
195
- });
196
-
197
- runtime.emit.mousemove(123, 456);
198
- runtime.step();
199
- expect(bloop.bag).toMatchObject({
200
- down: false,
201
- held: true,
202
- up: false,
203
- position: { x: 123, y: 456 },
204
- });
205
-
206
- runtime.emit.mousewheel(5, -3);
207
- runtime.step();
208
- expect(bloop.bag).toMatchObject({
209
- down: false,
210
- held: true,
211
- up: false,
212
- position: { x: 123, y: 456 },
213
- wheel: { x: 5, y: -3 },
214
- });
215
-
216
- runtime.emit.mouseup("Left");
217
- runtime.step();
218
- expect(bloop.bag).toMatchObject({
219
- down: false,
220
- held: false,
221
- up: true,
222
- });
223
- });
224
-
225
- it.skip("handles multiple frames between accumulated inputs", async () => {});
226
- it.skip("handles down and up between frames", async () => {});
227
- });
@@ -1,298 +0,0 @@
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 DELETED
@@ -1,128 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { Bloop } from "../src/bloop";
3
- import { mount } from "../src/runtime";
4
-
5
- describe("tapes", () => {
6
- describe("snapshots", () => {
7
- it("can snapshot the bag", async () => {
8
- const bloop = Bloop.create({
9
- bag: {
10
- cool: 42,
11
- },
12
- });
13
-
14
- bloop.system("inc", {
15
- update({ bag }) {
16
- bag.cool++;
17
- },
18
- });
19
-
20
- const { runtime } = await mount(bloop);
21
- runtime.step();
22
-
23
- expect(bloop.bag.cool).toEqual(43);
24
- const snapshot = runtime.snapshot();
25
-
26
- runtime.step();
27
- expect(bloop.bag.cool).toEqual(44);
28
-
29
- runtime.restore(snapshot);
30
- expect(bloop.bag.cool).toEqual(43);
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
- const { runtime } = await mount(bloop);
41
- const snapshot = runtime.snapshot();
42
- runtime.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
- const snapshot = runtime.snapshot();
74
-
75
- runtime.step();
76
- expect(timeCheck.dt).toBeCloseTo(0.016);
77
- expect(timeCheck.time).toBeCloseTo(0.016 * 2);
78
- expect(timeCheck.frame).toEqual(1);
79
- expect(bloop.context.time.frame).toEqual(2);
80
-
81
- runtime.restore(snapshot);
82
- expect(bloop.context.time.frame).toEqual(1);
83
- runtime.step();
84
-
85
- expect(timeCheck.dt).toBeCloseTo(0.016);
86
- expect(timeCheck.time).toBeCloseTo(0.016 * 2);
87
- expect(bloop.context.time.frame).toEqual(2);
88
- });
89
- });
90
-
91
- describe("playback", () => {
92
- it("can step back", async () => {
93
- const bloop = Bloop.create({
94
- bag: {
95
- clicks: 0,
96
- },
97
- });
98
-
99
- bloop.system("countClicks", {
100
- update({ bag, inputs }) {
101
- if (inputs.mouse.left.down) {
102
- bag.clicks++;
103
- }
104
- },
105
- });
106
-
107
- const { runtime } = await mount(bloop);
108
-
109
- runtime.emit.mousedown("Left");
110
- runtime.step();
111
- expect(bloop.context.time.frame).toEqual(1);
112
- expect(bloop.bag.clicks).toEqual(1);
113
-
114
- runtime.emit.mouseup("Left");
115
- runtime.step();
116
- expect(bloop.context.time.frame).toEqual(2);
117
- expect(bloop.bag.clicks).toEqual(1);
118
-
119
- runtime.stepBack();
120
- expect(bloop.context.time.frame).toEqual(1);
121
- expect(bloop.bag.clicks).toEqual(1);
122
-
123
- runtime.stepBack();
124
- expect(bloop.context.time.frame).toEqual(0);
125
- expect(bloop.bag.clicks).toEqual(0);
126
- });
127
- });
128
- });
package/tsconfig.json DELETED
@@ -1,34 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- // Environment setup & latest features
4
- "lib": ["ESNext"],
5
- "target": "ESNext",
6
- "module": "Preserve",
7
- "moduleDetection": "force",
8
- "jsx": "react-jsx",
9
- "allowJs": true,
10
-
11
- // Bundler mode
12
- "moduleResolution": "bundler",
13
- "allowImportingTsExtensions": true,
14
- "verbatimModuleSyntax": true,
15
-
16
- // Best practices
17
- "strict": true,
18
- "skipLibCheck": true,
19
- "noFallthroughCasesInSwitch": true,
20
- "noUncheckedIndexedAccess": true,
21
- "noImplicitOverride": true,
22
-
23
- // Some stricter flags (disabled by default)
24
- "noUnusedLocals": false,
25
- "noUnusedParameters": false,
26
- "noPropertyAccessFromIndexSignature": false,
27
-
28
- // Declarations
29
- "emitDeclarationOnly": true,
30
- "declaration": true,
31
- "outDir": "dist",
32
- },
33
- "include": ["src", "test"]
34
- }