diff --git a/pages-game/game/minesweeper/play.vue b/pages-game/game/minesweeper/play.vue index f500481..9a7dcb1 100644 --- a/pages-game/game/minesweeper/play.vue +++ b/pages-game/game/minesweeper/play.vue @@ -774,7 +774,8 @@ export default { }, handleCellClick(idx) { if (this.isSpectator) return; - if (!this.gameState || !this.gameState.gameStarted) return; + // 增加 showResultModal || showSettlement 的拦截,防止结算后的“瞬时点击” + if (!this.gameState || !this.gameState.gameStarted || this.showResultModal || this.showSettlement) return; if (!this.isMyTurn) return; if (this.gameState.grid[idx].revealed) return; nakamaManager.sendMatchState(this.matchId, 3, JSON.stringify({ index: idx })); diff --git a/utils/nakamaManager.js b/utils/nakamaManager.js index b04bc70..fbc2895 100644 --- a/utils/nakamaManager.js +++ b/utils/nakamaManager.js @@ -32,6 +32,7 @@ class NakamaManager { // 心跳定时器 this.heartbeatTimer = null; this.heartbeatInterval = 10000; // 10秒 + this.heartbeatId = 0; // 用于识别心跳版本的 ID } /** @@ -375,10 +376,24 @@ class NakamaManager { _startHeartbeat() { this._stopHeartbeat(); + const currentHeartbeatId = ++this.heartbeatId; + console.log('[Nakama] Starting heartbeat version:', currentHeartbeatId); + this.heartbeatTimer = setInterval(() => { + // 如果此心跳 ID 不再是当前活跃 ID,立即停止 + if (this.heartbeatId !== currentHeartbeatId) { + console.log('[Nakama] Zombie heartbeat detected and stopped:', currentHeartbeatId); + clearInterval(this.heartbeatTimer); + return; + } + if (this.isConnected) { - this._send({ ping: {} }, 5000).catch(() => { - console.warn('[Nakama] Heartbeat failed'); + this._send({ ping: {} }, 5000).catch((err) => { + console.warn('[Nakama] Heartbeat failed:', err.message); + // 如果发送失败且 socketTask 已断开,触发清理 + if (!this.socketTask || (err.message && err.message.includes('not connected'))) { + this.disconnect(); + } }); } }, this.heartbeatInterval);