From d5dbb8289a66ab88fcb0974bb5a007d0f8820e20 Mon Sep 17 00:00:00 2001 From: Jage9 Date: Sun, 22 Feb 2026 18:40:26 -0500 Subject: [PATCH] net: auto-reload on server version mismatch and announce connected version on welcome --- client/public/version.js | 2 +- client/src/main.ts | 25 ++++++++++++++++++++++++- docs/protocol-notes.md | 5 +++-- docs/runtime-flow.md | 4 +++- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/client/public/version.js b/client/public/version.js index 41f9ebe..fd02007 100644 --- a/client/public/version.js +++ b/client/public/version.js @@ -1,5 +1,5 @@ // Maintainer-controlled web client version. // Format: YYYY.MM.DD Rn (example: 2026.02.20 R2) -window.CHGRID_WEB_VERSION = "2026.02.22 R164"; +window.CHGRID_WEB_VERSION = "2026.02.22 R165"; // Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid. window.CHGRID_TIME_ZONE = "America/Detroit"; diff --git a/client/src/main.ts b/client/src/main.ts index 96e2968..a4301ae 100644 --- a/client/src/main.ts +++ b/client/src/main.ts @@ -201,6 +201,7 @@ let heartbeatNextPingId = -1; let heartbeatAwaitingPong = false; let reconnectInFlight = false; let activeServerInstanceId: string | null = null; +let reloadScheduledForVersionMismatch = false; let audioLayers: AudioLayerState = { voice: true, item: true, @@ -508,6 +509,9 @@ function toggleAudioLayer(layer: keyof AudioLayerState): void { /** Routes signaling transport status messages through chat buffer + status output. */ function handleSignalingStatus(message: string): void { + if (message === 'Connected.') { + return; + } pushChatMessage(message); } @@ -1221,16 +1225,35 @@ async function onSignalingMessage(message: IncomingMessage): Promise { return; } let restartAnnouncement: string | null = null; + let connectedAnnouncement: string | null = null; if (message.type === 'welcome') { const incomingInstanceId = String(message.serverInfo?.instanceId ?? '').trim() || null; const incomingVersion = String(message.serverInfo?.version ?? '').trim() || 'unknown'; + connectedAnnouncement = `Connected. Version ${incomingVersion}.`; + if ( + !reloadScheduledForVersionMismatch && + APP_VERSION && + incomingVersion && + incomingVersion !== 'unknown' && + incomingVersion !== APP_VERSION + ) { + reloadScheduledForVersionMismatch = true; + pushChatMessage(`Server version ${incomingVersion} detected. Reloading client...`); + window.setTimeout(() => { + window.location.reload(); + }, 50); + return; + } if (activeServerInstanceId && incomingInstanceId && activeServerInstanceId !== incomingInstanceId) { - restartAnnouncement = `Server restarted, version ${incomingVersion}.`; + restartAnnouncement = 'Server restarted.'; } activeServerInstanceId = incomingInstanceId; startHeartbeat(); } await onAppMessage(message); + if (connectedAnnouncement) { + pushChatMessage(connectedAnnouncement); + } if (restartAnnouncement) { pushChatMessage(restartAnnouncement); audio.sfxUiConfirm(); diff --git a/docs/protocol-notes.md b/docs/protocol-notes.md index cebaea6..251f6ee 100644 --- a/docs/protocol-notes.md +++ b/docs/protocol-notes.md @@ -62,5 +62,6 @@ This is a behavior guide for packet semantics beyond raw schemas. - Client sends automatic heartbeat `ping` packets every 10 seconds while connected. - Heartbeat pings use negative `clientSentAt` ids and are internal (not user-visible ping status). - If a heartbeat `pong` is missed for one interval (10 seconds), client force-disconnects and reconnects. -- After reconnect, if `welcome.serverInfo.instanceId` changed, client announces: - `Server restarted, version .` +- After reconnect, if `welcome.serverInfo.instanceId` changed, client announces `Server restarted.` +- Client emits `Connected. Version .` after each `welcome`. +- If `welcome.serverInfo.version` differs from running client version, client auto-reloads. diff --git a/docs/runtime-flow.md b/docs/runtime-flow.md index 8d602f5..50ce0ad 100644 --- a/docs/runtime-flow.md +++ b/docs/runtime-flow.md @@ -9,6 +9,7 @@ 5. Client: - applies `welcome.worldConfig.gridSize` for authoritative grid bounds/rendering - records `welcome.serverInfo` (`instanceId`, `version`) for restart detection + - if `welcome.serverInfo.version` differs from running client version, auto-reloads the page - applies `welcome.uiDefinitions` for item menus/properties/options - sends initial `update_position` - sends initial `update_nickname` @@ -48,7 +49,8 @@ Core incoming message effects: - While running, client sends heartbeat `ping` every 10 seconds. - If one heartbeat `pong` is missed (10-second interval), client auto-reconnects. -- If reconnect lands on a different `welcome.serverInfo.instanceId`, client announces server restart/version. +- If reconnect lands on a different `welcome.serverInfo.instanceId`, client announces server restart. +- Connect/reconnect status message is emitted from `welcome` and includes server version. ## Disconnect/Cleanup