diff --git a/pages-game/game/minesweeper/play.vue b/pages-game/game/minesweeper/play.vue index b0037f2..959c817 100644 --- a/pages-game/game/minesweeper/play.vue +++ b/pages-game/game/minesweeper/play.vue @@ -311,6 +311,7 @@ export default { isMatching: false, matchId: null, gameToken: null, + stableUserId: null, myUserId: null, floatingLabels: [], showGuide: false, @@ -413,12 +414,13 @@ export default { }, onLoad(options) { this.fetchGameConfig(); - const { game_token, nakama_server, nakama_key, match_id, is_spectator } = options; + const { game_token, nakama_server, nakama_key, match_id, is_spectator, uid } = options; if (is_spectator) this.isSpectator = true; if (match_id) this.matchId = match_id; + if (uid) this.stableUserId = uid; if (game_token) { - this.initNakama(game_token, decodeURIComponent(nakama_server || ''), decodeURIComponent(nakama_key || '')); + this.initNakama(game_token, decodeURIComponent(nakama_server || ''), decodeURIComponent(nakama_key || ''), uid); } else { uni.showToast({ title: '参数错误', icon: 'none' }); } @@ -467,30 +469,56 @@ export default { this.floatingLabels = this.floatingLabels.filter(l => l.id !== id); }, 1000); }, - async initNakama(token, server, key) { + async initNakama(token, server, key, stableUid = null) { try { const serverUrl = server || 'wss://game.1024tool.vip'; const serverKey = key || 'defaultkey'; nakamaManager.initClient(serverUrl, serverKey); this.gameToken = token; - const session = await nakamaManager.authenticateWithGameToken(token); + const session = await nakamaManager.authenticateWithGameToken(token, stableUid); this.myUserId = session.user_id; this.isConnected = true; this.addLog('system', '✅ 已连接到远程节点'); - // 先设置监听器,再继续后续操作 this.setupSocketListeners(); - // 如果是直连模式(加入指定房间或围观) + // 跨设备对局发现逻辑:调用 RPC 询问服务器我当前是否有正在进行的战局 + if (!this.matchId) { + try { + const activeMatch = await nakamaManager.rpc('find_my_match', {}); + if (activeMatch && activeMatch.match_id) { + console.log('[RPC发现] 发现服务器端活跃对局:', activeMatch.match_id); + this.matchId = activeMatch.match_id; + } + } catch (rpcErr) { + console.warn('[RPC发现] 检索活跃对局失败:', rpcErr); + } + } + + // 跨页面对局自愈逻辑:如果 RPC 没找到,尝试从本地缓存读取(作为兜底) + if (!this.matchId) { + const lastMatchId = uni.getStorageSync('minesweeper_last_match_id'); + if (lastMatchId) { + console.log('[自愈] 发现未完成对局缓存:', lastMatchId); + this.matchId = lastMatchId; + } + } + + // 如果有比赛 ID(来自 URL 或 缓存),尝试加入 if (this.matchId) { - this.addLog('system', this.isSpectator ? '🔭 正在切入观察视角...' : '🚪 正在进入指定战局...'); + this.addLog('system', this.isSpectator ? '🔭 正在切入观察视角...' : '🚪 正在恢复战局...'); try { await nakamaManager.joinMatch(this.matchId); + // 加入成功,确保缓存是最新的 + uni.setStorageSync('minesweeper_last_match_id', this.matchId); this.addLog('system', '✅ 接入成功'); setTimeout(() => { nakamaManager.sendMatchState(this.matchId, 100, JSON.stringify({ action: 'getState' })); }, 100); } catch(err) { - this.addLog('system', '❌ 接入失败: ' + err.message); + console.error('[自愈] 重连失败:', err); + this.addLog('system', '❌ 自动恢复失败 (对局可能已结束)'); + this.matchId = null; + uni.removeStorageSync('minesweeper_last_match_id'); } } } catch (e) { @@ -531,6 +559,7 @@ export default { console.log('======================'); this.matchId = match.match_id; + uni.setStorageSync('minesweeper_last_match_id', match.match_id); this.addLog('system', `成功接入战局`); setTimeout(() => { console.log('[发送状态请求] 请求初始游戏状态...'); @@ -670,6 +699,7 @@ export default { const winnerId = data.winnerId || (data.gameState && data.gameState.winnerId) || ''; this.settlementWinnerId = winnerId; this.showSettlement = !!winnerId; + uni.removeStorageSync('minesweeper_last_match_id'); if (data.gameState) { // 更新游戏状态并标记为已结束 @@ -787,7 +817,8 @@ export default { nakamaManager.sendMatchState(this.matchId, 3, JSON.stringify({ index: idx })); }, refreshAndPlayAgain() { - uni.navigateBack(); + uni.removeStorageSync('minesweeper_last_match_id'); + uni.navigateBack(); }, resetTurnTimer() { this.turnTimer = 15; diff --git a/utils/nakamaManager.js b/utils/nakamaManager.js index 8beac15..5074387 100644 --- a/utils/nakamaManager.js +++ b/utils/nakamaManager.js @@ -74,16 +74,21 @@ class NakamaManager { /** * 使用 game_token 认证 */ - async authenticateWithGameToken(gameToken) { + async authenticateWithGameToken(gameToken, externalUserId = null) { this.gameToken = gameToken; - // 获取或生成持久化的 custom ID - let customId = uni.getStorageSync('nakama_custom_id'); + let customId = externalUserId; + if (!customId) { - customId = `game_${Date.now()}_${Math.random().toString(36).substring(7)}`; - uni.setStorageSync('nakama_custom_id', customId); + // 获取或生成持久化的 custom ID + customId = uni.getStorageSync('nakama_custom_id'); + if (!customId) { + customId = `game_${Date.now()}_${Math.random().toString(36).substring(7)}`; + uni.setStorageSync('nakama_custom_id', customId); + } } - console.log('[Nakama] Authenticating with Persistent ID:', customId); + + console.log('[Nakama] Authenticating with ID:', customId, externalUserId ? '(Account-based)' : '(Device-based)'); // HTTP 认证请求 const scheme = this.useSSL ? 'https://' : 'http://';