feat: 增强 Nakama 认证支持外部用户 ID,并实现扫雷游戏对局恢复与发现功能

This commit is contained in:
邹方成 2026-01-04 12:40:24 +08:00
parent 1d1c4f29d6
commit d507122f2f
2 changed files with 51 additions and 15 deletions

View File

@ -311,6 +311,7 @@ export default {
isMatching: false, isMatching: false,
matchId: null, matchId: null,
gameToken: null, gameToken: null,
stableUserId: null,
myUserId: null, myUserId: null,
floatingLabels: [], floatingLabels: [],
showGuide: false, showGuide: false,
@ -413,12 +414,13 @@ export default {
}, },
onLoad(options) { onLoad(options) {
this.fetchGameConfig(); 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 (is_spectator) this.isSpectator = true;
if (match_id) this.matchId = match_id; if (match_id) this.matchId = match_id;
if (uid) this.stableUserId = uid;
if (game_token) { 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 { } else {
uni.showToast({ title: '参数错误', icon: 'none' }); uni.showToast({ title: '参数错误', icon: 'none' });
} }
@ -467,30 +469,56 @@ export default {
this.floatingLabels = this.floatingLabels.filter(l => l.id !== id); this.floatingLabels = this.floatingLabels.filter(l => l.id !== id);
}, 1000); }, 1000);
}, },
async initNakama(token, server, key) { async initNakama(token, server, key, stableUid = null) {
try { try {
const serverUrl = server || 'wss://game.1024tool.vip'; const serverUrl = server || 'wss://game.1024tool.vip';
const serverKey = key || 'defaultkey'; const serverKey = key || 'defaultkey';
nakamaManager.initClient(serverUrl, serverKey); nakamaManager.initClient(serverUrl, serverKey);
this.gameToken = token; this.gameToken = token;
const session = await nakamaManager.authenticateWithGameToken(token); const session = await nakamaManager.authenticateWithGameToken(token, stableUid);
this.myUserId = session.user_id; this.myUserId = session.user_id;
this.isConnected = true; this.isConnected = true;
this.addLog('system', '✅ 已连接到远程节点'); this.addLog('system', '✅ 已连接到远程节点');
//
this.setupSocketListeners(); 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) { if (this.matchId) {
this.addLog('system', this.isSpectator ? '🔭 正在切入观察视角...' : '🚪 正在进入指定战局...'); this.addLog('system', this.isSpectator ? '🔭 正在切入观察视角...' : '🚪 正在恢复战局...');
try { try {
await nakamaManager.joinMatch(this.matchId); await nakamaManager.joinMatch(this.matchId);
//
uni.setStorageSync('minesweeper_last_match_id', this.matchId);
this.addLog('system', '✅ 接入成功'); this.addLog('system', '✅ 接入成功');
setTimeout(() => { setTimeout(() => {
nakamaManager.sendMatchState(this.matchId, 100, JSON.stringify({ action: 'getState' })); nakamaManager.sendMatchState(this.matchId, 100, JSON.stringify({ action: 'getState' }));
}, 100); }, 100);
} catch(err) { } catch(err) {
this.addLog('system', '❌ 接入失败: ' + err.message); console.error('[自愈] 重连失败:', err);
this.addLog('system', '❌ 自动恢复失败 (对局可能已结束)');
this.matchId = null;
uni.removeStorageSync('minesweeper_last_match_id');
} }
} }
} catch (e) { } catch (e) {
@ -531,6 +559,7 @@ export default {
console.log('======================'); console.log('======================');
this.matchId = match.match_id; this.matchId = match.match_id;
uni.setStorageSync('minesweeper_last_match_id', match.match_id);
this.addLog('system', `成功接入战局`); this.addLog('system', `成功接入战局`);
setTimeout(() => { setTimeout(() => {
console.log('[发送状态请求] 请求初始游戏状态...'); console.log('[发送状态请求] 请求初始游戏状态...');
@ -670,6 +699,7 @@ export default {
const winnerId = data.winnerId || (data.gameState && data.gameState.winnerId) || ''; const winnerId = data.winnerId || (data.gameState && data.gameState.winnerId) || '';
this.settlementWinnerId = winnerId; this.settlementWinnerId = winnerId;
this.showSettlement = !!winnerId; this.showSettlement = !!winnerId;
uni.removeStorageSync('minesweeper_last_match_id');
if (data.gameState) { if (data.gameState) {
// //
@ -787,7 +817,8 @@ export default {
nakamaManager.sendMatchState(this.matchId, 3, JSON.stringify({ index: idx })); nakamaManager.sendMatchState(this.matchId, 3, JSON.stringify({ index: idx }));
}, },
refreshAndPlayAgain() { refreshAndPlayAgain() {
uni.navigateBack(); uni.removeStorageSync('minesweeper_last_match_id');
uni.navigateBack();
}, },
resetTurnTimer() { resetTurnTimer() {
this.turnTimer = 15; this.turnTimer = 15;

View File

@ -74,16 +74,21 @@ class NakamaManager {
/** /**
* 使用 game_token 认证 * 使用 game_token 认证
*/ */
async authenticateWithGameToken(gameToken) { async authenticateWithGameToken(gameToken, externalUserId = null) {
this.gameToken = gameToken; this.gameToken = gameToken;
// 获取或生成持久化的 custom ID let customId = externalUserId;
let customId = uni.getStorageSync('nakama_custom_id');
if (!customId) { if (!customId) {
customId = `game_${Date.now()}_${Math.random().toString(36).substring(7)}`; // 获取或生成持久化的 custom ID
uni.setStorageSync('nakama_custom_id', customId); 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 认证请求 // HTTP 认证请求
const scheme = this.useSSL ? 'https://' : 'http://'; const scheme = this.useSSL ? 'https://' : 'http://';