@couch-kit/host 1.2.5 → 1.2.7
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/lib/provider.d.ts.map +1 -1
- package/lib/websocket.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/provider.tsx +32 -19
- package/src/websocket.ts +5 -1
package/lib/provider.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AAGf,OAAO,EAML,KAAK,UAAU,EACf,KAAK,OAAO,EAGb,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,OAAO;IACrE,YAAY,EAAE,CAAC,CAAC;IAChB,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,+CAA+C;IAC/C,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,wCAAwC;IACxC,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,yCAAyC;IACzC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,UAAU,oBAAoB,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,OAAO;IACpE,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,CAAC;IAC9B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,KAAK,GAAG,IAAI,CAAC;CAC3B;AA4CD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,OAAO,EAAE,EACxE,QAAQ,EACR,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CAC9B,
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AAGf,OAAO,EAML,KAAK,UAAU,EACf,KAAK,OAAO,EAGb,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,OAAO;IACrE,YAAY,EAAE,CAAC,CAAC;IAChB,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,+CAA+C;IAC/C,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,wCAAwC;IACxC,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,yCAAyC;IACzC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,UAAU,oBAAoB,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,OAAO;IACpE,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,CAAC;IAC9B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,KAAK,GAAG,IAAI,CAAC;CAC3B;AA4CD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,OAAO,EAAE,EACxE,QAAQ,EACR,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CAC9B,qBA4OA;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,OAAO,KAK/C,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAC7C"}
|
package/lib/websocket.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAY/C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oFAAoF;IACpF,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,8CAA8C;AAC9C,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC/B,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC9C,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC/B,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1B,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;CACvB,CAAC;AAwBF,qBAAa,mBAAoB,SAAQ,YAAY,CAAC,qBAAqB,CAAC;IAC1E,OAAO,CAAC,MAAM,CAA0D;IACxE,OAAO,CAAC,OAAO,CAAyC;IACxD,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,KAAK,CAAU;IACvB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,cAAc,CAA+C;gBAEzD,MAAM,EAAE,eAAe;IASnC,OAAO,CAAC,GAAG;IAMJ,KAAK;IAkFZ,OAAO,CAAC,cAAc;IAkCtB,OAAO,CAAC,aAAa;
|
|
1
|
+
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAY/C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oFAAoF;IACpF,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,8CAA8C;AAC9C,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC/B,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC9C,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC/B,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1B,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;CACvB,CAAC;AAwBF,qBAAa,mBAAoB,SAAQ,YAAY,CAAC,qBAAqB,CAAC;IAC1E,OAAO,CAAC,MAAM,CAA0D;IACxE,OAAO,CAAC,OAAO,CAAyC;IACxD,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,KAAK,CAAU;IACvB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,cAAc,CAA+C;gBAEzD,MAAM,EAAE,eAAe;IASnC,OAAO,CAAC,GAAG;IAMJ,KAAK;IAkFZ,OAAO,CAAC,cAAc;IAkCtB,OAAO,CAAC,aAAa;IA6GrB;;;OAGG;IACI,IAAI;IAqCX;;;OAGG;IACI,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO;IAgB3C;;;OAGG;IACI,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM;IAclD,yDAAyD;IACzD,IAAW,WAAW,IAAI,MAAM,CAE/B;IAID,OAAO,CAAC,eAAe;IAkDvB,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,WAAW;IA4DnB,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,UAAU;CA2BnB"}
|
package/package.json
CHANGED
package/src/provider.tsx
CHANGED
|
@@ -123,6 +123,27 @@ export function GameHostProvider<S extends IGameState, A extends IAction>({
|
|
|
123
123
|
stateRef.current = state;
|
|
124
124
|
}, [state]);
|
|
125
125
|
|
|
126
|
+
// Send WELCOME messages after state has settled (post-render).
|
|
127
|
+
// This guarantees the joining player is included in the state snapshot.
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
if (pendingWelcome.current.size === 0) return;
|
|
130
|
+
if (!wsServer.current) return;
|
|
131
|
+
|
|
132
|
+
const server = wsServer.current;
|
|
133
|
+
for (const [socketId] of pendingWelcome.current) {
|
|
134
|
+
welcomedClients.current.add(socketId);
|
|
135
|
+
server.send(socketId, {
|
|
136
|
+
type: MessageTypes.WELCOME,
|
|
137
|
+
payload: {
|
|
138
|
+
playerId: socketId,
|
|
139
|
+
state,
|
|
140
|
+
serverTime: Date.now(),
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
pendingWelcome.current.clear();
|
|
145
|
+
}, [state]);
|
|
146
|
+
|
|
126
147
|
// Keep refs for callback props to avoid stale closures
|
|
127
148
|
const configRef = useRef(config);
|
|
128
149
|
useEffect(() => {
|
|
@@ -147,6 +168,9 @@ export function GameHostProvider<S extends IGameState, A extends IAction>({
|
|
|
147
168
|
// Track socket IDs that have received their WELCOME message
|
|
148
169
|
const welcomedClients = useRef<Set<string>>(new Set());
|
|
149
170
|
|
|
171
|
+
// Track socket IDs that need a WELCOME message after state settles
|
|
172
|
+
const pendingWelcome = useRef<Map<string, string>>(new Map()); // socketId -> playerId
|
|
173
|
+
|
|
150
174
|
useEffect(() => {
|
|
151
175
|
const port = config.wsPort || httpPort + DEFAULT_WS_PORT_OFFSET;
|
|
152
176
|
const server = new GameWebSocketServer({ port, debug: config.debug });
|
|
@@ -199,20 +223,9 @@ export function GameHostProvider<S extends IGameState, A extends IAction>({
|
|
|
199
223
|
payload: { id: socketId, ...payload },
|
|
200
224
|
} as InternalAction<S>);
|
|
201
225
|
|
|
202
|
-
//
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
queueMicrotask(() => {
|
|
206
|
-
welcomedClients.current.add(socketId);
|
|
207
|
-
server.send(socketId, {
|
|
208
|
-
type: MessageTypes.WELCOME,
|
|
209
|
-
payload: {
|
|
210
|
-
playerId: socketId,
|
|
211
|
-
state: stateRef.current,
|
|
212
|
-
serverTime: Date.now(),
|
|
213
|
-
},
|
|
214
|
-
});
|
|
215
|
-
});
|
|
226
|
+
// Queue WELCOME to be sent after React re-renders and stateRef is updated.
|
|
227
|
+
// A useEffect watches for pending welcomes and sends them with fresh state.
|
|
228
|
+
pendingWelcome.current.set(socketId, socketId);
|
|
216
229
|
|
|
217
230
|
configRef.current.onPlayerJoined?.(socketId, payload.name);
|
|
218
231
|
break;
|
|
@@ -290,7 +303,6 @@ export function GameHostProvider<S extends IGameState, A extends IAction>({
|
|
|
290
303
|
// 3. Throttled State Broadcasts (~30fps)
|
|
291
304
|
// Batches rapid state changes so at most one broadcast is sent per ~33ms frame,
|
|
292
305
|
// reducing serialization overhead and network traffic for fast-updating games.
|
|
293
|
-
const broadcastPending = useRef(false);
|
|
294
306
|
const broadcastTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
295
307
|
|
|
296
308
|
const broadcastState = useCallback(() => {
|
|
@@ -303,14 +315,15 @@ export function GameHostProvider<S extends IGameState, A extends IAction>({
|
|
|
303
315
|
},
|
|
304
316
|
});
|
|
305
317
|
}
|
|
306
|
-
broadcastPending.current = false;
|
|
307
318
|
}, []);
|
|
308
319
|
|
|
309
320
|
useEffect(() => {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
321
|
+
// Cancel any pending broadcast and schedule a fresh one.
|
|
322
|
+
// This ensures the broadcast always uses the latest stateRef.
|
|
323
|
+
if (broadcastTimer.current) {
|
|
324
|
+
clearTimeout(broadcastTimer.current);
|
|
313
325
|
}
|
|
326
|
+
broadcastTimer.current = setTimeout(broadcastState, 33); // ~30fps
|
|
314
327
|
|
|
315
328
|
return () => {
|
|
316
329
|
if (broadcastTimer.current) {
|
package/src/websocket.ts
CHANGED
|
@@ -208,7 +208,11 @@ export class GameWebSocketServer extends EventEmitter<WebSocketServerEvents> {
|
|
|
208
208
|
|
|
209
209
|
while (offset < managed.bufferLength) {
|
|
210
210
|
// Create a view of the unconsumed portion for decoding
|
|
211
|
-
const view =
|
|
211
|
+
const view = Buffer.from(
|
|
212
|
+
managed.buffer.buffer,
|
|
213
|
+
managed.buffer.byteOffset + offset,
|
|
214
|
+
managed.bufferLength - offset,
|
|
215
|
+
);
|
|
212
216
|
let frame: DecodedFrame | null;
|
|
213
217
|
try {
|
|
214
218
|
frame = this.decodeFrame(view);
|