@bloopjs/web 0.0.88 → 0.0.90
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/App.d.ts.map +1 -1
- package/dist/mod.js +467 -251
- package/dist/mod.js.map +8 -8
- package/dist/netcode/mod.d.ts +0 -2
- package/dist/netcode/mod.d.ts.map +1 -1
- package/dist/netcode/reconcile.d.ts +3 -0
- package/dist/netcode/reconcile.d.ts.map +1 -0
- package/package.json +3 -3
- package/src/App.ts +68 -0
- package/src/netcode/mod.ts +0 -2
- package/src/netcode/reconcile.ts +171 -0
- package/dist/netcode/scaffold.d.ts +0 -13
- package/dist/netcode/scaffold.d.ts.map +0 -1
- package/src/netcode/scaffold.ts +0 -168
package/dist/netcode/mod.d.ts
CHANGED
|
@@ -2,8 +2,6 @@ export type { PeerId, BrokerMessage, PeerMessage } from "./protocol.ts";
|
|
|
2
2
|
export type { Log, LogOpts, LogDirection, LogSeverity, OnLogCallback, } from "./logs.ts";
|
|
3
3
|
export type { WebRtcPipe } from "./transport.ts";
|
|
4
4
|
export type { RoomEvents } from "./broker.ts";
|
|
5
|
-
export type { JoinRollbackRoomOptions } from "./scaffold.ts";
|
|
6
5
|
export { PacketType } from "./protocol.ts";
|
|
7
6
|
export { logger } from "./logs.ts";
|
|
8
|
-
export { joinRollbackRoom } from "./scaffold.ts";
|
|
9
7
|
//# sourceMappingURL=mod.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../src/netcode/mod.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACxE,YAAY,EACV,GAAG,EACH,OAAO,EACP,YAAY,EACZ,WAAW,EACX,aAAa,GACd,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../src/netcode/mod.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACxE,YAAY,EACV,GAAG,EACH,OAAO,EACP,YAAY,EACZ,WAAW,EACX,aAAa,GACd,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../src/netcode/reconcile.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAiBrC,wBAAsB,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAiC5E"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bloopjs/web",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.90",
|
|
4
4
|
"author": "Neil Sarkar",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"typescript": "^5"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@bloopjs/bloop": "0.0.
|
|
37
|
-
"@bloopjs/engine": "0.0.
|
|
36
|
+
"@bloopjs/bloop": "0.0.90",
|
|
37
|
+
"@bloopjs/engine": "0.0.90",
|
|
38
38
|
"@preact/signals": "^1.3.1",
|
|
39
39
|
"partysocket": "^1.1.6",
|
|
40
40
|
"preact": "^10.25.4"
|
package/src/App.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
type RoomEvents,
|
|
20
20
|
} from "./netcode/broker";
|
|
21
21
|
import { logger } from "./netcode/logs.ts";
|
|
22
|
+
import { reconcile } from "./netcode/reconcile.ts";
|
|
22
23
|
|
|
23
24
|
export type StartOptions = {
|
|
24
25
|
/** A bloop game instance */
|
|
@@ -85,6 +86,8 @@ export class App {
|
|
|
85
86
|
#now: number = performance.now();
|
|
86
87
|
#debugUi: DebugUi | null = null;
|
|
87
88
|
|
|
89
|
+
#abortController: AbortController = new AbortController();
|
|
90
|
+
|
|
88
91
|
constructor(
|
|
89
92
|
sim: Sim,
|
|
90
93
|
game: Bloop<any>,
|
|
@@ -106,6 +109,10 @@ export class App {
|
|
|
106
109
|
};
|
|
107
110
|
|
|
108
111
|
this.subscribe();
|
|
112
|
+
|
|
113
|
+
reconcile(this, this.#abortController.signal).catch((err) => {
|
|
114
|
+
console.error("Error in lemmyloop:", err);
|
|
115
|
+
});
|
|
109
116
|
}
|
|
110
117
|
|
|
111
118
|
/** The simulation instance associated with this app */
|
|
@@ -172,6 +179,63 @@ export class App {
|
|
|
172
179
|
// Skip emitting input events during replay to avoid filling the event buffer
|
|
173
180
|
const shouldEmitInputs = () => !this.sim.isReplaying;
|
|
174
181
|
|
|
182
|
+
// Resize handling - emits resize events when viewport changes
|
|
183
|
+
let resizeObserver: ResizeObserver | null = null;
|
|
184
|
+
let cleanupPixelRatio: (() => void) | null = null;
|
|
185
|
+
|
|
186
|
+
const emitResize = () => {
|
|
187
|
+
const canvas = this.canvas;
|
|
188
|
+
const pixelRatio = window.devicePixelRatio || 1;
|
|
189
|
+
|
|
190
|
+
let width: number;
|
|
191
|
+
let height: number;
|
|
192
|
+
|
|
193
|
+
if (canvas) {
|
|
194
|
+
const rect = canvas.getBoundingClientRect();
|
|
195
|
+
width = Math.round(rect.width);
|
|
196
|
+
height = Math.round(rect.height);
|
|
197
|
+
} else {
|
|
198
|
+
// Fallback to window dimensions if no canvas
|
|
199
|
+
width = window.innerWidth;
|
|
200
|
+
height = window.innerHeight;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
this.sim.emit.resize(
|
|
204
|
+
width,
|
|
205
|
+
height,
|
|
206
|
+
Math.round(width * pixelRatio),
|
|
207
|
+
Math.round(height * pixelRatio),
|
|
208
|
+
pixelRatio,
|
|
209
|
+
);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// Observe canvas size changes
|
|
213
|
+
const canvas = this.canvas;
|
|
214
|
+
if (canvas) {
|
|
215
|
+
resizeObserver = new ResizeObserver(() => emitResize());
|
|
216
|
+
resizeObserver.observe(canvas);
|
|
217
|
+
} else {
|
|
218
|
+
// Fallback: observe window resize if no canvas
|
|
219
|
+
window.addEventListener("resize", emitResize);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Detect devicePixelRatio changes (monitor switch, browser zoom)
|
|
223
|
+
const updatePixelRatioListener = () => {
|
|
224
|
+
const mediaQuery = window.matchMedia(
|
|
225
|
+
`(resolution: ${window.devicePixelRatio}dppx)`,
|
|
226
|
+
);
|
|
227
|
+
const handler = () => {
|
|
228
|
+
emitResize();
|
|
229
|
+
updatePixelRatioListener(); // Re-register for new ratio
|
|
230
|
+
};
|
|
231
|
+
mediaQuery.addEventListener("change", handler, { once: true });
|
|
232
|
+
return () => mediaQuery.removeEventListener("change", handler);
|
|
233
|
+
};
|
|
234
|
+
cleanupPixelRatio = updatePixelRatioListener();
|
|
235
|
+
|
|
236
|
+
// Emit initial resize event
|
|
237
|
+
emitResize();
|
|
238
|
+
|
|
175
239
|
const handleKeydown = (event: KeyboardEvent) => {
|
|
176
240
|
if (shouldEmitInputs()) this.sim.emit.keydown(event.code as Key);
|
|
177
241
|
};
|
|
@@ -353,6 +417,9 @@ export class App {
|
|
|
353
417
|
window.removeEventListener("touchstart", handleTouchstart);
|
|
354
418
|
window.removeEventListener("touchend", handleTouchend);
|
|
355
419
|
window.removeEventListener("touchmove", handleTouchmove);
|
|
420
|
+
window.removeEventListener("resize", emitResize);
|
|
421
|
+
resizeObserver?.disconnect();
|
|
422
|
+
cleanupPixelRatio?.();
|
|
356
423
|
if (this.#rafHandle != null) {
|
|
357
424
|
cancelAnimationFrame(this.#rafHandle);
|
|
358
425
|
}
|
|
@@ -367,6 +434,7 @@ export class App {
|
|
|
367
434
|
this.afterFrame.unsubscribeAll();
|
|
368
435
|
this.onHmr.unsubscribeAll();
|
|
369
436
|
this.#debugUi?.unmount();
|
|
437
|
+
this.#abortController.abort();
|
|
370
438
|
}
|
|
371
439
|
|
|
372
440
|
/**
|
package/src/netcode/mod.ts
CHANGED
|
@@ -9,8 +9,6 @@ export type {
|
|
|
9
9
|
} from "./logs.ts";
|
|
10
10
|
export type { WebRtcPipe } from "./transport.ts";
|
|
11
11
|
export type { RoomEvents } from "./broker.ts";
|
|
12
|
-
export type { JoinRollbackRoomOptions } from "./scaffold.ts";
|
|
13
12
|
|
|
14
13
|
export { PacketType } from "./protocol.ts";
|
|
15
14
|
export { logger } from "./logs.ts";
|
|
16
|
-
export { joinRollbackRoom } from "./scaffold.ts";
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { unwrap } from "@bloopjs/bloop";
|
|
2
|
+
import type { App } from "../App.ts";
|
|
3
|
+
import * as Debug from "../debugui/mod.ts";
|
|
4
|
+
import { logger } from "./logs.ts";
|
|
5
|
+
|
|
6
|
+
// State
|
|
7
|
+
let udp: RTCDataChannel | null = null;
|
|
8
|
+
let localPeerId: number | null = null;
|
|
9
|
+
let remotePeerId: number | null = null;
|
|
10
|
+
let localStringPeerId: string | null = null;
|
|
11
|
+
let remoteStringPeerId: string | null = null;
|
|
12
|
+
const incomingPackets: Uint8Array[] = [];
|
|
13
|
+
|
|
14
|
+
// actual netcode state (vs desired)
|
|
15
|
+
const actual = {
|
|
16
|
+
roomCode: "",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export async function reconcile(app: App, signal: AbortSignal): Promise<void> {
|
|
20
|
+
// Process packets and send our state each frame
|
|
21
|
+
app.beforeFrame.subscribe((_frame) => {
|
|
22
|
+
if (!app.game.context.net.isInSession) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
receivePackets(app);
|
|
28
|
+
sendPacket(app);
|
|
29
|
+
} catch (e) {
|
|
30
|
+
console.error("Error in beforeFrame:", e);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Wire up logger to debug state
|
|
35
|
+
logger.onLog = (log) => {
|
|
36
|
+
Debug.addLog(log);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
while (!signal.aborted) {
|
|
40
|
+
const { net } = app.game.context;
|
|
41
|
+
if (net.wantsRoomCode && actual.roomCode !== net.wantsRoomCode) {
|
|
42
|
+
console.log("[netcode] wants a room code", {
|
|
43
|
+
actual: actual.roomCode,
|
|
44
|
+
wants: net.wantsRoomCode,
|
|
45
|
+
});
|
|
46
|
+
actual.roomCode = net.wantsRoomCode;
|
|
47
|
+
joinRollbackRoom(net.wantsRoomCode, app);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
await sleep(150);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function sleep(ms: number) {
|
|
55
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function joinRollbackRoom(roomId: string, app: App): void {
|
|
59
|
+
app.joinRoom(roomId, {
|
|
60
|
+
onPeerIdAssign: (peerId) => {
|
|
61
|
+
localStringPeerId = peerId;
|
|
62
|
+
},
|
|
63
|
+
onBrokerMessage: (_message) => {},
|
|
64
|
+
onMessage(_peerId, data, _reliable) {
|
|
65
|
+
incomingPackets.push(new Uint8Array(data));
|
|
66
|
+
},
|
|
67
|
+
onDataChannelClose(_peerId, reliable) {
|
|
68
|
+
if (!reliable && remotePeerId !== null) {
|
|
69
|
+
app.sim.emit.network("peer:leave", { peerId: remotePeerId });
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
onDataChannelOpen(peerId, reliable, channel) {
|
|
73
|
+
if (!reliable) {
|
|
74
|
+
udp = channel;
|
|
75
|
+
|
|
76
|
+
if (localStringPeerId === null) {
|
|
77
|
+
console.error("[netcode] Local peer ID not assigned yet!");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const ids = assignPeerIds(localStringPeerId, peerId);
|
|
82
|
+
localPeerId = ids.local;
|
|
83
|
+
Debug.setLocalId(localPeerId);
|
|
84
|
+
remotePeerId = ids.remote;
|
|
85
|
+
remoteStringPeerId = peerId;
|
|
86
|
+
Debug.setRemoteId(remotePeerId);
|
|
87
|
+
|
|
88
|
+
// Set up local and remote peers in net state
|
|
89
|
+
app.sim.emit.network("peer:join", { peerId: localPeerId });
|
|
90
|
+
app.sim.emit.network("peer:join", { peerId: remotePeerId });
|
|
91
|
+
app.sim.emit.network("peer:assign_local_id", { peerId: localPeerId });
|
|
92
|
+
app.sim.emit.network("session:start", {});
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
onPeerConnected(peerId) {
|
|
96
|
+
Debug.addPeer({
|
|
97
|
+
id: peerId,
|
|
98
|
+
nickname: peerId.substring(0, 6),
|
|
99
|
+
ack: -1,
|
|
100
|
+
seq: -1,
|
|
101
|
+
lastPacketTime: performance.now(),
|
|
102
|
+
});
|
|
103
|
+
console.log(
|
|
104
|
+
`[netcode] Peer connected: ${peerId}. Total peers: ${Debug.debugState.netStatus.value.peers.length}`,
|
|
105
|
+
);
|
|
106
|
+
},
|
|
107
|
+
onPeerDisconnected(peerId) {
|
|
108
|
+
Debug.removePeer(peerId);
|
|
109
|
+
if (remotePeerId !== null && peerId === remoteStringPeerId) {
|
|
110
|
+
app.sim.emit.network("peer:leave", { peerId: remotePeerId });
|
|
111
|
+
app.sim.emit.network("session:end", {});
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function assignPeerIds(
|
|
118
|
+
localId: string,
|
|
119
|
+
remoteId: string,
|
|
120
|
+
): { local: number; remote: number } {
|
|
121
|
+
if (localId < remoteId) {
|
|
122
|
+
return { local: 0, remote: 1 };
|
|
123
|
+
} else {
|
|
124
|
+
return { local: 1, remote: 0 };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function receivePackets(app: App) {
|
|
129
|
+
for (const packetData of incomingPackets) {
|
|
130
|
+
app.sim.emit.packet(packetData);
|
|
131
|
+
|
|
132
|
+
if (remotePeerId == null) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const peerState = unwrap(
|
|
137
|
+
app.sim.net.peers[remotePeerId],
|
|
138
|
+
`Remote peer state not found for peerId ${remotePeerId}`,
|
|
139
|
+
);
|
|
140
|
+
Debug.updatePeer(remoteStringPeerId!, {
|
|
141
|
+
ack: peerState.ack,
|
|
142
|
+
seq: peerState.seq,
|
|
143
|
+
lastPacketTime: performance.now(),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
incomingPackets.length = 0;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function sendPacket(app: App) {
|
|
150
|
+
if (!udp || remotePeerId === null) {
|
|
151
|
+
console.warn("[netcode] Cannot send packet, udp or remotePeerId is null");
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (udp.readyState !== "open") {
|
|
156
|
+
console.warn(
|
|
157
|
+
"[netcode] Data channel not open, cannot send packet. readyState=",
|
|
158
|
+
udp.readyState,
|
|
159
|
+
);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const packet = app.sim.getOutboundPacket(remotePeerId);
|
|
164
|
+
|
|
165
|
+
if (!packet) {
|
|
166
|
+
console.warn("[netcode] No packet to send");
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
udp.send(packet);
|
|
171
|
+
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { App } from "../App.ts";
|
|
2
|
-
export type JoinRollbackRoomOptions = {
|
|
3
|
-
/** Called when session becomes active */
|
|
4
|
-
onSessionStart?: () => void;
|
|
5
|
-
/** Called when session ends */
|
|
6
|
-
onSessionEnd?: () => void;
|
|
7
|
-
};
|
|
8
|
-
/**
|
|
9
|
-
* Join a rollback netcode room and wire up packet processing.
|
|
10
|
-
* This is a scaffold/stopgap - not the final architecture.
|
|
11
|
-
*/
|
|
12
|
-
export declare function joinRollbackRoom(roomId: string, app: App, opts?: JoinRollbackRoomOptions): void;
|
|
13
|
-
//# sourceMappingURL=scaffold.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../src/netcode/scaffold.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAIrC,MAAM,MAAM,uBAAuB,GAAG;IACpC,yCAAyC;IACzC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,+BAA+B;IAC/B,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,GAAG,EACR,IAAI,CAAC,EAAE,uBAAuB,GAC7B,IAAI,CAmJN"}
|
package/src/netcode/scaffold.ts
DELETED
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
import { unwrap } from "@bloopjs/bloop";
|
|
2
|
-
import type { App } from "../App.ts";
|
|
3
|
-
import * as Debug from "../debugui/mod.ts";
|
|
4
|
-
import { logger } from "./logs.ts";
|
|
5
|
-
|
|
6
|
-
export type JoinRollbackRoomOptions = {
|
|
7
|
-
/** Called when session becomes active */
|
|
8
|
-
onSessionStart?: () => void;
|
|
9
|
-
/** Called when session ends */
|
|
10
|
-
onSessionEnd?: () => void;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Join a rollback netcode room and wire up packet processing.
|
|
15
|
-
* This is a scaffold/stopgap - not the final architecture.
|
|
16
|
-
*/
|
|
17
|
-
export function joinRollbackRoom(
|
|
18
|
-
roomId: string,
|
|
19
|
-
app: App,
|
|
20
|
-
opts?: JoinRollbackRoomOptions,
|
|
21
|
-
): void {
|
|
22
|
-
// State
|
|
23
|
-
let udp: RTCDataChannel | null = null;
|
|
24
|
-
let sessionActive = false;
|
|
25
|
-
let localPeerId: number | null = null;
|
|
26
|
-
let remotePeerId: number | null = null;
|
|
27
|
-
let localStringPeerId: string | null = null;
|
|
28
|
-
let remoteStringPeerId: string | null = null;
|
|
29
|
-
const incomingPackets: Uint8Array[] = [];
|
|
30
|
-
|
|
31
|
-
function assignPeerIds(
|
|
32
|
-
localId: string,
|
|
33
|
-
remoteId: string,
|
|
34
|
-
): { local: number; remote: number } {
|
|
35
|
-
if (localId < remoteId) {
|
|
36
|
-
return { local: 0, remote: 1 };
|
|
37
|
-
} else {
|
|
38
|
-
return { local: 1, remote: 0 };
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function receivePackets() {
|
|
43
|
-
for (const packetData of incomingPackets) {
|
|
44
|
-
app.sim.emit.packet(packetData);
|
|
45
|
-
|
|
46
|
-
if (remotePeerId == null) {
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const peerState = unwrap(
|
|
51
|
-
app.sim.net.peers[remotePeerId],
|
|
52
|
-
`Remote peer state not found for peerId ${remotePeerId}`,
|
|
53
|
-
);
|
|
54
|
-
Debug.updatePeer(remoteStringPeerId!, {
|
|
55
|
-
ack: peerState.ack,
|
|
56
|
-
seq: peerState.seq,
|
|
57
|
-
lastPacketTime: performance.now(),
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
incomingPackets.length = 0;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function sendPacket() {
|
|
64
|
-
if (!udp || remotePeerId === null) {
|
|
65
|
-
console.warn("[netcode] Cannot send packet, udp or remotePeerId is null");
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (udp.readyState !== "open") {
|
|
70
|
-
console.warn(
|
|
71
|
-
"[netcode] Data channel not open, cannot send packet. readyState=",
|
|
72
|
-
udp.readyState,
|
|
73
|
-
);
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const packet = app.sim.getOutboundPacket(remotePeerId);
|
|
78
|
-
|
|
79
|
-
if (!packet) {
|
|
80
|
-
console.warn("[netcode] No packet to send");
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
udp.send(packet);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Wire up logger to debug state
|
|
88
|
-
logger.onLog = (log) => {
|
|
89
|
-
Debug.addLog(log);
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
app.joinRoom(roomId, {
|
|
93
|
-
onPeerIdAssign: (peerId) => {
|
|
94
|
-
localStringPeerId = peerId;
|
|
95
|
-
},
|
|
96
|
-
onBrokerMessage: (_message) => {},
|
|
97
|
-
onMessage(_peerId, data, _reliable) {
|
|
98
|
-
incomingPackets.push(new Uint8Array(data));
|
|
99
|
-
},
|
|
100
|
-
onDataChannelClose(peerId, reliable) {
|
|
101
|
-
if (!reliable && remotePeerId !== null) {
|
|
102
|
-
app.sim.emit.network("peer:leave", { peerId: remotePeerId });
|
|
103
|
-
sessionActive = false;
|
|
104
|
-
opts?.onSessionEnd?.();
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
onDataChannelOpen(peerId, reliable, channel) {
|
|
108
|
-
if (!reliable) {
|
|
109
|
-
udp = channel;
|
|
110
|
-
|
|
111
|
-
if (localStringPeerId === null) {
|
|
112
|
-
console.error("[netcode] Local peer ID not assigned yet!");
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const ids = assignPeerIds(localStringPeerId, peerId);
|
|
117
|
-
localPeerId = ids.local;
|
|
118
|
-
Debug.setLocalId(localPeerId);
|
|
119
|
-
remotePeerId = ids.remote;
|
|
120
|
-
remoteStringPeerId = peerId;
|
|
121
|
-
Debug.setRemoteId(remotePeerId);
|
|
122
|
-
|
|
123
|
-
// Set up local and remote peers in net state
|
|
124
|
-
app.sim.emit.network("peer:join", { peerId: localPeerId });
|
|
125
|
-
app.sim.emit.network("peer:join", { peerId: remotePeerId });
|
|
126
|
-
app.sim.emit.network("peer:assign_local_id", { peerId: localPeerId });
|
|
127
|
-
app.sim.emit.network("session:start", {});
|
|
128
|
-
|
|
129
|
-
sessionActive = true;
|
|
130
|
-
opts?.onSessionStart?.();
|
|
131
|
-
}
|
|
132
|
-
},
|
|
133
|
-
onPeerConnected(peerId) {
|
|
134
|
-
Debug.addPeer({
|
|
135
|
-
id: peerId,
|
|
136
|
-
nickname: peerId.substring(0, 6),
|
|
137
|
-
ack: -1,
|
|
138
|
-
seq: -1,
|
|
139
|
-
lastPacketTime: performance.now(),
|
|
140
|
-
});
|
|
141
|
-
console.log(
|
|
142
|
-
`[netcode] Peer connected: ${peerId}. Total peers: ${Debug.debugState.netStatus.value.peers.length}`,
|
|
143
|
-
);
|
|
144
|
-
},
|
|
145
|
-
onPeerDisconnected(peerId) {
|
|
146
|
-
Debug.removePeer(peerId);
|
|
147
|
-
if (remotePeerId !== null && peerId === remoteStringPeerId) {
|
|
148
|
-
app.sim.emit.network("peer:leave", { peerId: remotePeerId });
|
|
149
|
-
sessionActive = false;
|
|
150
|
-
opts?.onSessionEnd?.();
|
|
151
|
-
}
|
|
152
|
-
},
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
// Process packets and send our state each frame
|
|
156
|
-
app.beforeFrame.subscribe((_frame) => {
|
|
157
|
-
if (!sessionActive || !udp || remotePeerId === null) {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
try {
|
|
162
|
-
receivePackets();
|
|
163
|
-
sendPacket();
|
|
164
|
-
} catch (e) {
|
|
165
|
-
console.error("Error in beforeFrame:", e);
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
}
|