diff --git a/components/chat/ChatLobby.vue b/components/chat/ChatLobby.vue index 2c0c73cc..38c7884b 100644 --- a/components/chat/ChatLobby.vue +++ b/components/chat/ChatLobby.vue @@ -535,6 +535,7 @@ export default { lobbyId: { immediate: true, handler() { + this.lobbyListener?.stop(); this.lobby?.leave(); this.lobby = socket.joinLobby( this.instance, diff --git a/components/matchmaking/MatchmakingConfirm.vue b/components/matchmaking/MatchmakingConfirm.vue index 7b3625aa..6d4217d8 100644 --- a/components/matchmaking/MatchmakingConfirm.vue +++ b/components/matchmaking/MatchmakingConfirm.vue @@ -80,6 +80,9 @@ export default { } if (!oldConfirmation) { + if (this.countdownInterval) { + clearInterval(this.countdownInterval); + } this.playMatchFoundSound(); this.updateCountdown(); this.countdownInterval = setInterval(this.updateCountdown, 1000); @@ -122,7 +125,7 @@ export default { }, }, beforeUnmount() { - if (this.countdownInterval !== null) { + if (this.countdownInterval) { clearInterval(this.countdownInterval); } }, diff --git a/web-sockets/Socket.ts b/web-sockets/Socket.ts index 03d61ad6..a7758e30 100644 --- a/web-sockets/Socket.ts +++ b/web-sockets/Socket.ts @@ -29,10 +29,26 @@ class Socket extends EventEmitter { event: string; data: Record; }> = []; + private retryCount = 0; + private static readonly MAX_RETRIES = 50; + private static readonly BASE_DELAY_MS = 1000; + private static readonly MAX_DELAY_MS = 30000; private lobbies: Map = new Map(); public connect() { + // Clean up any existing connection before creating a new one + if (this.connection) { + try { + this.connection.onclose = null; + this.connection.onerror = null; + this.connection.close(); + } catch { + // Ignore errors when closing stale connections + } + this.connection = undefined; + } + const wsHost = `wss://${useRuntimeConfig().public.wsDomain}/web`; console.info(`[ws] connecting to ws: ${wsHost}`); const webSocket = new WebSocket(wsHost); @@ -47,6 +63,7 @@ class Socket extends EventEmitter { webSocket.addEventListener("open", () => { this.emit("online"); this.connected = true; + this.retryCount = 0; clearInterval(this.heartBeat); @@ -88,9 +105,28 @@ class Socket extends EventEmitter { this.emit("offline"); this.connected = false; console.warn("[ws] lost connection to websocket server", closeEvent); + + if (this.retryCount >= Socket.MAX_RETRIES) { + console.warn( + `[ws] max reconnection attempts (${Socket.MAX_RETRIES}) reached, giving up`, + ); + return; + } + + const delay = Math.min( + Socket.BASE_DELAY_MS * Math.pow(2, this.retryCount), + Socket.MAX_DELAY_MS, + ); + const jitter = Math.random() * 1000; + this.retryCount++; + + console.info( + `[ws] reconnecting in ${Math.round(delay + jitter)}ms (attempt ${this.retryCount}/${Socket.MAX_RETRIES})`, + ); + setTimeout(() => { this.connect(); - }, 1000); + }, delay + jitter); }; webSocket.onerror = (error) => {