@bloopjs/engine 0.0.88 → 0.0.89
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/dist/codegen/offsets.d.ts +40 -0
- package/dist/codegen/offsets.d.ts.map +1 -0
- package/dist/contexts/inputContext.d.ts +1 -0
- package/dist/contexts/inputContext.d.ts.map +1 -1
- package/dist/contexts/netContext.d.ts +2 -8
- package/dist/contexts/netContext.d.ts.map +1 -1
- package/dist/contexts/timeContext.d.ts.map +1 -1
- package/dist/engine.js +102 -77
- package/dist/engine.js.map +9 -8
- package/dist/inputs.d.ts +2 -7
- package/dist/inputs.d.ts.map +1 -1
- package/js/codegen/offsets.ts +48 -0
- package/js/contexts/inputContext.ts +20 -12
- package/js/contexts/netContext.ts +50 -51
- package/js/contexts/timeContext.ts +9 -3
- package/js/defaultUrl.ts +1 -1
- package/js/inputs.ts +18 -14
- package/package.json +1 -1
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MAX_PLAYERS,
|
|
3
|
+
NET_CTX_PEERS_OFFSET,
|
|
4
|
+
NET_CTX_LAST_ROLLBACK_DEPTH_OFFSET,
|
|
5
|
+
NET_CTX_TOTAL_ROLLBACKS_OFFSET,
|
|
6
|
+
NET_CTX_FRAMES_RESIMULATED_OFFSET,
|
|
7
|
+
NET_CTX_PEER_COUNT_OFFSET,
|
|
8
|
+
NET_CTX_LOCAL_PEER_ID_OFFSET,
|
|
9
|
+
NET_CTX_IN_SESSION_OFFSET,
|
|
10
|
+
NET_CTX_STATUS_OFFSET,
|
|
11
|
+
NET_CTX_MATCH_FRAME_OFFSET,
|
|
12
|
+
NET_CTX_SESSION_START_FRAME_OFFSET,
|
|
13
|
+
NET_CTX_ROOM_CODE_OFFSET,
|
|
14
|
+
NET_CTX_WANTS_ROOM_CODE_OFFSET,
|
|
15
|
+
NET_CTX_WANTS_DISCONNECT_OFFSET,
|
|
16
|
+
PEER_CTX_SIZE,
|
|
17
|
+
PEER_CTX_CONNECTED_OFFSET,
|
|
18
|
+
PEER_CTX_SEQ_OFFSET,
|
|
19
|
+
PEER_CTX_ACK_OFFSET,
|
|
20
|
+
} from "../codegen/offsets";
|
|
21
|
+
|
|
1
22
|
/**
|
|
2
23
|
* Network status values matching Zig NetStatus enum
|
|
3
24
|
*/
|
|
@@ -28,22 +49,6 @@ export type PeerInfo = {
|
|
|
28
49
|
ack: number; // -1 if no packets received
|
|
29
50
|
};
|
|
30
51
|
|
|
31
|
-
const MAX_PEERS = 12;
|
|
32
|
-
const PEERS_ARRAY_OFFSET = 32; // After _pad at offset 29-31
|
|
33
|
-
const PEER_CTX_SIZE = 8; // connected(1) + packet_count(1) + seq(2) + ack(2) + ack_count(1) + pad(1)
|
|
34
|
-
|
|
35
|
-
// Rollback stats offsets (after peers array at 32 + 12*8 = 128)
|
|
36
|
-
const LAST_ROLLBACK_DEPTH_OFFSET = 128;
|
|
37
|
-
const TOTAL_ROLLBACKS_OFFSET = 132;
|
|
38
|
-
const FRAMES_RESIMULATED_OFFSET = 136;
|
|
39
|
-
|
|
40
|
-
// Offsets within PeerCtx struct
|
|
41
|
-
const PEER_CONNECTED_OFFSET = 0;
|
|
42
|
-
const PEER_PACKET_COUNT_OFFSET = 1;
|
|
43
|
-
const PEER_SEQ_OFFSET = 2;
|
|
44
|
-
const PEER_ACK_OFFSET = 4;
|
|
45
|
-
const PEER_ACK_COUNT_OFFSET = 6;
|
|
46
|
-
|
|
47
52
|
const STATUS_MAP: Record<number, NetStatus> = {
|
|
48
53
|
0: "offline",
|
|
49
54
|
1: "local",
|
|
@@ -53,14 +58,8 @@ const STATUS_MAP: Record<number, NetStatus> = {
|
|
|
53
58
|
};
|
|
54
59
|
|
|
55
60
|
/**
|
|
56
|
-
* NetCtx struct layout
|
|
57
|
-
*
|
|
58
|
-
* - local_peer_id: u8 (offset 1)
|
|
59
|
-
* - in_session: u8 (offset 2)
|
|
60
|
-
* - status: u8 (offset 3)
|
|
61
|
-
* - match_frame: u32 (offset 4)
|
|
62
|
-
* - session_start_frame: u32 (offset 8)
|
|
63
|
-
* - room_code: [8]u8 (offset 12)
|
|
61
|
+
* NetCtx struct layout is defined in context.zig.
|
|
62
|
+
* Field offsets are generated in codegen/offsets.ts.
|
|
64
63
|
*
|
|
65
64
|
* All getters read directly from the engine's memory via DataView.
|
|
66
65
|
* State is managed by the Zig engine, not TypeScript.
|
|
@@ -69,7 +68,7 @@ export class NetContext {
|
|
|
69
68
|
dataView?: DataView;
|
|
70
69
|
|
|
71
70
|
// Pre-allocated peer objects to avoid GC pressure
|
|
72
|
-
#peers: PeerInfo[] = Array.from({ length:
|
|
71
|
+
#peers: PeerInfo[] = Array.from({ length: MAX_PLAYERS }, () => ({
|
|
73
72
|
isLocal: false,
|
|
74
73
|
seq: -1,
|
|
75
74
|
ack: -1,
|
|
@@ -92,7 +91,7 @@ export class NetContext {
|
|
|
92
91
|
if (!this.#hasValidBuffer()) {
|
|
93
92
|
throw new Error("NetContext dataView is not valid");
|
|
94
93
|
}
|
|
95
|
-
return this.dataView!.getUint8(
|
|
94
|
+
return this.dataView!.getUint8(NET_CTX_PEER_COUNT_OFFSET);
|
|
96
95
|
}
|
|
97
96
|
|
|
98
97
|
/** Local peer ID in the session */
|
|
@@ -100,7 +99,7 @@ export class NetContext {
|
|
|
100
99
|
if (!this.#hasValidBuffer()) {
|
|
101
100
|
throw new Error("NetContext dataView is not valid");
|
|
102
101
|
}
|
|
103
|
-
return this.dataView!.getUint8(
|
|
102
|
+
return this.dataView!.getUint8(NET_CTX_LOCAL_PEER_ID_OFFSET);
|
|
104
103
|
}
|
|
105
104
|
|
|
106
105
|
/** Whether we're in an active multiplayer session */
|
|
@@ -108,7 +107,7 @@ export class NetContext {
|
|
|
108
107
|
if (!this.#hasValidBuffer()) {
|
|
109
108
|
throw new Error("NetContext dataView is not valid");
|
|
110
109
|
}
|
|
111
|
-
return this.dataView!.getUint8(
|
|
110
|
+
return this.dataView!.getUint8(NET_CTX_IN_SESSION_OFFSET) !== 0;
|
|
112
111
|
}
|
|
113
112
|
|
|
114
113
|
/** Current network status */
|
|
@@ -116,7 +115,7 @@ export class NetContext {
|
|
|
116
115
|
if (!this.#hasValidBuffer()) {
|
|
117
116
|
throw new Error("NetContext dataView is not valid");
|
|
118
117
|
}
|
|
119
|
-
const statusByte = this.dataView!.getUint8(
|
|
118
|
+
const statusByte = this.dataView!.getUint8(NET_CTX_STATUS_OFFSET);
|
|
120
119
|
return STATUS_MAP[statusByte] ?? "local";
|
|
121
120
|
}
|
|
122
121
|
|
|
@@ -125,7 +124,7 @@ export class NetContext {
|
|
|
125
124
|
if (!this.#hasValidBuffer()) {
|
|
126
125
|
throw new Error("NetContext dataView is not valid");
|
|
127
126
|
}
|
|
128
|
-
return this.dataView!.getUint32(
|
|
127
|
+
return this.dataView!.getUint32(NET_CTX_MATCH_FRAME_OFFSET, true);
|
|
129
128
|
}
|
|
130
129
|
|
|
131
130
|
/**
|
|
@@ -136,7 +135,7 @@ export class NetContext {
|
|
|
136
135
|
if (!this.#hasValidBuffer()) {
|
|
137
136
|
throw new Error("NetContext dataView is not valid");
|
|
138
137
|
}
|
|
139
|
-
return this.dataView!.getUint32(
|
|
138
|
+
return this.dataView!.getUint32(NET_CTX_SESSION_START_FRAME_OFFSET, true);
|
|
140
139
|
}
|
|
141
140
|
|
|
142
141
|
/** Current room code (empty string if not in a room) */
|
|
@@ -144,10 +143,10 @@ export class NetContext {
|
|
|
144
143
|
if (!this.#hasValidBuffer()) {
|
|
145
144
|
throw new Error("NetContext dataView is not valid");
|
|
146
145
|
}
|
|
147
|
-
// Read 8 bytes starting at offset
|
|
146
|
+
// Read 8 bytes starting at room_code offset, convert to string until null terminator
|
|
148
147
|
const bytes: number[] = [];
|
|
149
148
|
for (let i = 0; i < 8; i++) {
|
|
150
|
-
const byte = this.dataView!.getUint8(
|
|
149
|
+
const byte = this.dataView!.getUint8(NET_CTX_ROOM_CODE_OFFSET + i);
|
|
151
150
|
if (byte === 0) break;
|
|
152
151
|
bytes.push(byte);
|
|
153
152
|
}
|
|
@@ -159,10 +158,10 @@ export class NetContext {
|
|
|
159
158
|
if (!this.#hasValidBuffer()) {
|
|
160
159
|
return undefined;
|
|
161
160
|
}
|
|
162
|
-
// Read 8 bytes starting at offset
|
|
161
|
+
// Read 8 bytes starting at wants_room_code offset, convert to string until null terminator
|
|
163
162
|
const bytes: number[] = [];
|
|
164
163
|
for (let i = 0; i < 8; i++) {
|
|
165
|
-
const byte = this.dataView!.getUint8(
|
|
164
|
+
const byte = this.dataView!.getUint8(NET_CTX_WANTS_ROOM_CODE_OFFSET + i);
|
|
166
165
|
if (byte === 0) break;
|
|
167
166
|
bytes.push(byte);
|
|
168
167
|
}
|
|
@@ -175,12 +174,12 @@ export class NetContext {
|
|
|
175
174
|
}
|
|
176
175
|
// Clear first
|
|
177
176
|
for (let i = 0; i < 8; i++) {
|
|
178
|
-
this.dataView!.setUint8(
|
|
177
|
+
this.dataView!.setUint8(NET_CTX_WANTS_ROOM_CODE_OFFSET + i, 0);
|
|
179
178
|
}
|
|
180
179
|
if (code) {
|
|
181
180
|
// Write up to 7 chars (leave room for null terminator)
|
|
182
181
|
for (let i = 0; i < Math.min(code.length, 7); i++) {
|
|
183
|
-
this.dataView!.setUint8(
|
|
182
|
+
this.dataView!.setUint8(NET_CTX_WANTS_ROOM_CODE_OFFSET + i, code.charCodeAt(i));
|
|
184
183
|
}
|
|
185
184
|
}
|
|
186
185
|
}
|
|
@@ -190,14 +189,14 @@ export class NetContext {
|
|
|
190
189
|
if (!this.#hasValidBuffer()) {
|
|
191
190
|
return false;
|
|
192
191
|
}
|
|
193
|
-
return this.dataView!.getUint8(
|
|
192
|
+
return this.dataView!.getUint8(NET_CTX_WANTS_DISCONNECT_OFFSET) !== 0;
|
|
194
193
|
}
|
|
195
194
|
|
|
196
195
|
set wantsDisconnect(value: boolean) {
|
|
197
196
|
if (!this.#hasValidBuffer()) {
|
|
198
197
|
throw new Error("NetContext dataView is not valid");
|
|
199
198
|
}
|
|
200
|
-
this.dataView!.setUint8(
|
|
199
|
+
this.dataView!.setUint8(NET_CTX_WANTS_DISCONNECT_OFFSET, value ? 1 : 0);
|
|
201
200
|
}
|
|
202
201
|
|
|
203
202
|
/**
|
|
@@ -223,12 +222,12 @@ export class NetContext {
|
|
|
223
222
|
|
|
224
223
|
// Calculate local peer ack = min(seq) across connected remotes with data
|
|
225
224
|
let minRemoteSeq = -1;
|
|
226
|
-
for (let i = 0; i <
|
|
225
|
+
for (let i = 0; i < MAX_PLAYERS; i++) {
|
|
227
226
|
if (i === localPeerId) continue;
|
|
228
|
-
const peerOffset =
|
|
229
|
-
if (dv.getUint8(peerOffset +
|
|
227
|
+
const peerOffset = NET_CTX_PEERS_OFFSET + i * PEER_CTX_SIZE;
|
|
228
|
+
if (dv.getUint8(peerOffset + PEER_CTX_CONNECTED_OFFSET) !== 1) continue;
|
|
230
229
|
// seq is now i16 with -1 meaning "no data yet"
|
|
231
|
-
const seq = dv.getInt16(peerOffset +
|
|
230
|
+
const seq = dv.getInt16(peerOffset + PEER_CTX_SEQ_OFFSET, true);
|
|
232
231
|
if (seq < 0) continue; // No data from this peer yet
|
|
233
232
|
if (minRemoteSeq === -1 || seq < minRemoteSeq) {
|
|
234
233
|
minRemoteSeq = seq;
|
|
@@ -237,9 +236,9 @@ export class NetContext {
|
|
|
237
236
|
|
|
238
237
|
// Update pre-allocated peer objects and build result array
|
|
239
238
|
this.#peersResult.length = 0;
|
|
240
|
-
for (let i = 0; i <
|
|
241
|
-
const peerOffset =
|
|
242
|
-
if (dv.getUint8(peerOffset +
|
|
239
|
+
for (let i = 0; i < MAX_PLAYERS; i++) {
|
|
240
|
+
const peerOffset = NET_CTX_PEERS_OFFSET + i * PEER_CTX_SIZE;
|
|
241
|
+
if (dv.getUint8(peerOffset + PEER_CTX_CONNECTED_OFFSET) !== 1) continue;
|
|
243
242
|
|
|
244
243
|
const peer = this.#peers[i];
|
|
245
244
|
if (!peer) {
|
|
@@ -253,8 +252,8 @@ export class NetContext {
|
|
|
253
252
|
peer.ack = minRemoteSeq;
|
|
254
253
|
} else {
|
|
255
254
|
// seq and ack are now i16 with -1 meaning "no data yet"
|
|
256
|
-
peer.seq = dv.getInt16(peerOffset +
|
|
257
|
-
peer.ack = dv.getInt16(peerOffset +
|
|
255
|
+
peer.seq = dv.getInt16(peerOffset + PEER_CTX_SEQ_OFFSET, true);
|
|
256
|
+
peer.ack = dv.getInt16(peerOffset + PEER_CTX_ACK_OFFSET, true);
|
|
258
257
|
}
|
|
259
258
|
this.#peersResult.push(peer);
|
|
260
259
|
}
|
|
@@ -266,7 +265,7 @@ export class NetContext {
|
|
|
266
265
|
if (!this.#hasValidBuffer()) {
|
|
267
266
|
throw new Error("NetContext dataView is not valid");
|
|
268
267
|
}
|
|
269
|
-
return this.dataView!.getUint32(
|
|
268
|
+
return this.dataView!.getUint32(NET_CTX_LAST_ROLLBACK_DEPTH_OFFSET, true);
|
|
270
269
|
}
|
|
271
270
|
|
|
272
271
|
/** Total number of rollbacks during this session */
|
|
@@ -274,7 +273,7 @@ export class NetContext {
|
|
|
274
273
|
if (!this.#hasValidBuffer()) {
|
|
275
274
|
throw new Error("NetContext dataView is not valid");
|
|
276
275
|
}
|
|
277
|
-
return this.dataView!.getUint32(
|
|
276
|
+
return this.dataView!.getUint32(NET_CTX_TOTAL_ROLLBACKS_OFFSET, true);
|
|
278
277
|
}
|
|
279
278
|
|
|
280
279
|
/** Total frames resimulated during this session */
|
|
@@ -283,6 +282,6 @@ export class NetContext {
|
|
|
283
282
|
throw new Error("NetContext dataView is not valid");
|
|
284
283
|
}
|
|
285
284
|
// Read u64 as BigInt then convert to number (safe for reasonable frame counts)
|
|
286
|
-
return Number(this.dataView!.getBigUint64(
|
|
285
|
+
return Number(this.dataView!.getBigUint64(NET_CTX_FRAMES_RESIMULATED_OFFSET, true));
|
|
287
286
|
}
|
|
288
287
|
}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TIME_CTX_FRAME_OFFSET,
|
|
3
|
+
TIME_CTX_DT_MS_OFFSET,
|
|
4
|
+
TIME_CTX_TOTAL_MS_OFFSET,
|
|
5
|
+
} from "../codegen/offsets";
|
|
6
|
+
|
|
1
7
|
export class TimeContext {
|
|
2
8
|
dataView?: DataView;
|
|
3
9
|
|
|
@@ -10,7 +16,7 @@ export class TimeContext {
|
|
|
10
16
|
if (!this.dataView) {
|
|
11
17
|
throw new Error("TimeContext DataView is not initialized");
|
|
12
18
|
}
|
|
13
|
-
return this.dataView.getUint32(
|
|
19
|
+
return this.dataView.getUint32(TIME_CTX_FRAME_OFFSET, true);
|
|
14
20
|
}
|
|
15
21
|
|
|
16
22
|
/** The number of seconds since the last frame */
|
|
@@ -18,7 +24,7 @@ export class TimeContext {
|
|
|
18
24
|
if (!this.dataView) {
|
|
19
25
|
throw new Error("TimeContext DataView is not initialized");
|
|
20
26
|
}
|
|
21
|
-
return this.dataView.getUint32(
|
|
27
|
+
return this.dataView.getUint32(TIME_CTX_DT_MS_OFFSET, true) / 1000;
|
|
22
28
|
}
|
|
23
29
|
|
|
24
30
|
/** The total number of seconds since the engine started */
|
|
@@ -26,6 +32,6 @@ export class TimeContext {
|
|
|
26
32
|
if (!this.dataView) {
|
|
27
33
|
throw new Error("TimeContext DataView is not initialized");
|
|
28
34
|
}
|
|
29
|
-
return this.dataView.getUint32(
|
|
35
|
+
return this.dataView.getUint32(TIME_CTX_TOTAL_MS_OFFSET, true) / 1000;
|
|
30
36
|
}
|
|
31
37
|
}
|
package/js/defaultUrl.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const DEFAULT_WASM_URL: URL = new URL("https://unpkg.com/@bloopjs/engine@0.0.
|
|
1
|
+
export const DEFAULT_WASM_URL: URL = new URL("https://unpkg.com/@bloopjs/engine@0.0.89/wasm/bloop.wasm");
|
package/js/inputs.ts
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import * as Enums from "./codegen/enums";
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
// Re-export layout constants from generated offsets
|
|
4
|
+
export {
|
|
5
|
+
MAX_PLAYERS,
|
|
6
|
+
KEY_CTX_SIZE,
|
|
7
|
+
MOUSE_CTX_SIZE,
|
|
8
|
+
PLAYER_INPUTS_SIZE,
|
|
9
|
+
INPUT_CTX_SIZE,
|
|
10
|
+
PLAYER_INPUTS_KEY_CTX_OFFSET,
|
|
11
|
+
PLAYER_INPUTS_MOUSE_CTX_OFFSET,
|
|
12
|
+
MOUSE_CTX_BUTTON_STATES_OFFSET,
|
|
13
|
+
} from "./codegen/offsets";
|
|
6
14
|
|
|
7
|
-
//
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export const PLAYER_INPUTS_SIZE = 280;
|
|
15
|
-
|
|
16
|
-
// InputCtx layout: players[12] = 12 * 280 = 3360 bytes
|
|
17
|
-
export const INPUT_CTX_SIZE = MAX_PLAYERS * PLAYER_INPUTS_SIZE;
|
|
15
|
+
// Backwards compatibility aliases
|
|
16
|
+
export {
|
|
17
|
+
PLAYER_INPUTS_KEY_CTX_OFFSET as KEYBOARD_OFFSET,
|
|
18
|
+
KEY_CTX_SIZE as KEYBOARD_SIZE,
|
|
19
|
+
PLAYER_INPUTS_MOUSE_CTX_OFFSET as MOUSE_OFFSET,
|
|
20
|
+
MOUSE_CTX_BUTTON_STATES_OFFSET as MOUSE_BUTTONS_OFFSET,
|
|
21
|
+
} from "./codegen/offsets";
|
|
18
22
|
|
|
19
23
|
export const EVENT_PAYLOAD_SIZE = 8;
|
|
20
24
|
export const EVENT_PAYLOAD_ALIGN = 4;
|