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,
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;

View File

@ -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://';