openvibers/openvibe.games Commits

Client: the first connect gives up after 10 s, and a refused or timed-out connect waits for the server and reloads, as a dropped connection already did (WS-P task 3 game-shard proof: an upgrade sent during a restart can wait with no answer, so a page opened in that 2 s gap stayed on "connecting…")

OP OpenVibers committed 26 Sept 2026 · b207e78909 · 2 files, +23 −9


Changes

ddiff --git a/apps/client/src/main.ts b/apps/client/src/main.ts
iindex 4f6251a..e844c70 100644
---- a/apps/client/src/main.ts
++++ b/apps/client/src/main.ts
@@@ -369,12 +369,8 @@ async function start(): Promise<void> {
}
}
}
-- connection.onClose = () => {
-- if (rejected) return
-- // Deploys restart the server; come back on our own instead of
-- // freezing on a dead socket. Reload = full clean resync.
-- hud.setStatus('connection lost — reconnecting automatically…')
-- hud.toast('Disconnected — reconnecting…', true)
++ // Deploys restart the server: wait until it answers /healthz, then reload (a full clean resync).
++ const reloadWhenServerIsBack = (): void => {
const poll = setInterval(() => {
fetch('/healthz')
.then((r) => {
@@@ -386,12 +382,21 @@ async function start(): Promise<void> {
.catch(() => {})
}, 2000)
}
++ connection.onClose = () => {
++ if (rejected) return
++ // Come back on our own instead of freezing on a dead socket.
++ hud.setStatus('connection lost — reconnecting automatically…')
++ hud.toast('Disconnected — reconnecting…', true)
++ reloadWhenServerIsBack()
++ }
hud.setStatus('connecting…')
try {
await connection.connect(gameSocketUrl(), identity.token, name, appearance, slot, sso)
} catch {
-- hud.setStatus('could not reach server — is it running?')
++ // Refused or timed out (a page opened during a restart): wait for the server, then reload.
++ hud.setStatus('could not reach the server — retrying automatically…')
++ reloadWhenServerIsBack()
return
}
ddiff --git a/apps/client/src/net/connection.ts b/apps/client/src/net/connection.ts
iindex 6008271..7566759 100644
---- a/apps/client/src/net/connection.ts
++++ b/apps/client/src/net/connection.ts
@@@ -7,6 +7,9 @@ import {
type ServerMessage,
} from '@openvibe/protocol'
++/** How long the first WebSocket connect may take before the page falls back to waiting for the server. */
++export const CONNECT_TIMEOUT_MS = 10_000
++
/**
* WebSocket connection to the dedicated server. Message handling is a
* callback so the network layer stays independent of game/state code.
@@@ -27,8 +30,14 @@ export class Connection {
const ws = new WebSocket(url)
this.ws = ws
await new Promise<void>((resolve, reject) => {
-- ws.onopen = () => resolve()
-- ws.onerror = () => reject(new Error('connection failed'))
++ // An upgrade sent while the server restarts can wait with no answer (the proxy holds it):
++ // give up after CONNECT_TIMEOUT_MS so the page can wait for the server and reload instead.
++ const timer = setTimeout(() => {
++ try { ws.close() } catch { /* already closing */ }
++ reject(new Error('connection timed out'))
++ }, CONNECT_TIMEOUT_MS)
++ ws.onopen = () => { clearTimeout(timer); resolve() }
++ ws.onerror = () => { clearTimeout(timer); reject(new Error('connection failed')) }
})
ws.onmessage = (event) => {
const msg = decodeServerMessage(String(event.data))