net: auto-reload on server version mismatch and announce connected version on welcome

This commit is contained in:
Jage9
2026-02-22 18:40:26 -05:00
parent ee0b98290e
commit d5dbb8289a
4 changed files with 31 additions and 5 deletions

View File

@@ -1,5 +1,5 @@
// Maintainer-controlled web client version. // Maintainer-controlled web client version.
// Format: YYYY.MM.DD Rn (example: 2026.02.20 R2) // 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. // Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid.
window.CHGRID_TIME_ZONE = "America/Detroit"; window.CHGRID_TIME_ZONE = "America/Detroit";

View File

@@ -201,6 +201,7 @@ let heartbeatNextPingId = -1;
let heartbeatAwaitingPong = false; let heartbeatAwaitingPong = false;
let reconnectInFlight = false; let reconnectInFlight = false;
let activeServerInstanceId: string | null = null; let activeServerInstanceId: string | null = null;
let reloadScheduledForVersionMismatch = false;
let audioLayers: AudioLayerState = { let audioLayers: AudioLayerState = {
voice: true, voice: true,
item: true, item: true,
@@ -508,6 +509,9 @@ function toggleAudioLayer(layer: keyof AudioLayerState): void {
/** Routes signaling transport status messages through chat buffer + status output. */ /** Routes signaling transport status messages through chat buffer + status output. */
function handleSignalingStatus(message: string): void { function handleSignalingStatus(message: string): void {
if (message === 'Connected.') {
return;
}
pushChatMessage(message); pushChatMessage(message);
} }
@@ -1221,16 +1225,35 @@ async function onSignalingMessage(message: IncomingMessage): Promise<void> {
return; return;
} }
let restartAnnouncement: string | null = null; let restartAnnouncement: string | null = null;
let connectedAnnouncement: string | null = null;
if (message.type === 'welcome') { if (message.type === 'welcome') {
const incomingInstanceId = String(message.serverInfo?.instanceId ?? '').trim() || null; const incomingInstanceId = String(message.serverInfo?.instanceId ?? '').trim() || null;
const incomingVersion = String(message.serverInfo?.version ?? '').trim() || 'unknown'; 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) { if (activeServerInstanceId && incomingInstanceId && activeServerInstanceId !== incomingInstanceId) {
restartAnnouncement = `Server restarted, version ${incomingVersion}.`; restartAnnouncement = 'Server restarted.';
} }
activeServerInstanceId = incomingInstanceId; activeServerInstanceId = incomingInstanceId;
startHeartbeat(); startHeartbeat();
} }
await onAppMessage(message); await onAppMessage(message);
if (connectedAnnouncement) {
pushChatMessage(connectedAnnouncement);
}
if (restartAnnouncement) { if (restartAnnouncement) {
pushChatMessage(restartAnnouncement); pushChatMessage(restartAnnouncement);
audio.sfxUiConfirm(); audio.sfxUiConfirm();

View File

@@ -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. - 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). - 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. - 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: - After reconnect, if `welcome.serverInfo.instanceId` changed, client announces `Server restarted.`
`Server restarted, version <version>.` - Client emits `Connected. Version <version>.` after each `welcome`.
- If `welcome.serverInfo.version` differs from running client version, client auto-reloads.

View File

@@ -9,6 +9,7 @@
5. Client: 5. Client:
- applies `welcome.worldConfig.gridSize` for authoritative grid bounds/rendering - applies `welcome.worldConfig.gridSize` for authoritative grid bounds/rendering
- records `welcome.serverInfo` (`instanceId`, `version`) for restart detection - 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 - applies `welcome.uiDefinitions` for item menus/properties/options
- sends initial `update_position` - sends initial `update_position`
- sends initial `update_nickname` - sends initial `update_nickname`
@@ -48,7 +49,8 @@ Core incoming message effects:
- While running, client sends heartbeat `ping` every 10 seconds. - While running, client sends heartbeat `ping` every 10 seconds.
- If one heartbeat `pong` is missed (10-second interval), client auto-reconnects. - 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 ## Disconnect/Cleanup