#!/usr/bin/env node /** * Reconnect 测试脚本 * 1. 连接玩家 * 2. 加入匹配 * 3. 等待匹配完成后,模拟断线并重新连接,使用相同的 game_token 重新加入同一场比赛 */ const WebSocket = require('ws'); // 请根据实际情况修改以下配置 const NAKAMA_HOST = 'ws://127.0.0.1:7350/ws'; // 本地 Nakama 地址 const NUM_PLAYERS = 2; // 只测试 2 人即可 // 这里需要填入真实的 Nakama 认证 token 和 game_token const NAKAMA_TOKENS = [ 'YOUR_NAKAMA_TOKEN_1', 'YOUR_NAKAMA_TOKEN_2', ]; const GAME_TOKENS = [ 'YOUR_GAME_TOKEN_1', 'YOUR_GAME_TOKEN_2', ]; class TestPlayer { constructor(id, nakamaToken, gameToken) { this.id = id; this.nakamaToken = nakamaToken; this.gameToken = gameToken; this.ws = null; this.matchId = null; } connect() { return new Promise((resolve, reject) => { const url = `${NAKAMA_HOST}?token=${this.nakamaToken}&format=json`; this.ws = new WebSocket(url); this.ws.on('open', () => { console.log(`[${this.id}] ✅ WS connected`); resolve(); }); this.ws.on('message', (data) => this.handleMessage(JSON.parse(data))); this.ws.on('error', (e) => reject(e)); }); } handleMessage(msg) { if (msg.matchmaker_matched) { this.matchId = msg.matchmaker_matched.match_id; console.log(`[${this.id}] 🎮 Matched, matchId=${this.matchId}`); // 自动加入 setTimeout(() => this.joinMatch(this.matchId), 100); } if (msg.match) { console.log(`[${this.id}] ✅ Joined match ${msg.match.match_id}`); } if (msg.error) { console.error(`[${this.id}] ❌ Error: ${msg.error.message}`); } } addToMatchmaker() { const req = { cid: `${this.id}_mm`, matchmaker_add: { min_count: NUM_PLAYERS, max_count: NUM_PLAYERS, query: '*', string_properties: { game_token: this.gameToken }, }, }; this.ws.send(JSON.stringify(req)); console.log(`[${this.id}] 🔍 Added to matchmaker`); } joinMatch(matchId) { const req = { cid: `${this.id}_join`, match_join: { match_id: matchId, metadata: { game_token: this.gameToken }, }, }; this.ws.send(JSON.stringify(req)); console.log(`[${this.id}] 🚪 Joining match ${matchId}`); } disconnect() { if (this.ws) { this.ws.close(); this.ws = null; } console.log(`[${this.id}] 🔌 Disconnected`); } } async function sleep(ms) { return new Promise(r => setTimeout(r, ms)); } (async () => { const players = []; for (let i = 0; i < NUM_PLAYERS; i++) { players.push(new TestPlayer(i + 1, NAKAMA_TOKENS[i], GAME_TOKENS[i])); } // 1. 连接 await Promise.all(players.map(p => p.connect())); // 2. 加入匹配队列 players.forEach(p => p.addToMatchmaker()); // 等待匹配完成 await sleep(15000); // 3. 模拟断线并重连(只对第一个玩家演示) const p = players[0]; console.log(`\n🔌 模拟玩家 ${p.id} 断线`); const oldMatchId = p.matchId; p.disconnect(); await sleep(2000); console.log(`🔁 重新连接玩家 ${p.id}`); await p.connect(); // 使用相同的 game_token 重新加入同一场比赛 if (oldMatchId) { p.joinMatch(oldMatchId); } // 等待几秒观察结果 await sleep(10000); // 清理 players.forEach(p => p.disconnect()); console.log('✅ 测试结束'); })();