227 lines
6.6 KiB
JavaScript
Executable File
227 lines
6.6 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
||
/**
|
||
* 扫雷游戏匹配测试脚本
|
||
* 测试多个玩家同时匹配和加入游戏的流程
|
||
*/
|
||
|
||
const WebSocket = require('ws');
|
||
|
||
const NAKAMA_HOST = 'wss://nakama.bindbox.cn'; // 修改为你的服务器地址
|
||
const NUM_PLAYERS = 4;
|
||
|
||
// 模拟游戏token(实际应该从后端获取)
|
||
const TEST_TOKENS = [
|
||
// 这些是mock token,需要替换为真实的game_token
|
||
'test_token_player_1',
|
||
'test_token_player_2',
|
||
'test_token_player_3',
|
||
'test_token_player_4',
|
||
];
|
||
|
||
class TestPlayer {
|
||
constructor(index, nakamaToken, gameToken) {
|
||
this.index = index;
|
||
this.nakamaToken = nakamaToken;
|
||
this.gameToken = gameToken;
|
||
this.ws = null;
|
||
this.matchId = null;
|
||
this.connected = false;
|
||
}
|
||
|
||
async connect() {
|
||
return new Promise((resolve, reject) => {
|
||
const url = `${NAKAMA_HOST}/ws?token=${this.nakamaToken}&format=json`;
|
||
console.log(`[Player ${this.index}] Connecting to ${url.substring(0, 80)}...`);
|
||
|
||
this.ws = new WebSocket(url);
|
||
|
||
this.ws.on('open', () => {
|
||
console.log(`[Player ${this.index}] ✅ WebSocket connected`);
|
||
this.connected = true;
|
||
resolve();
|
||
});
|
||
|
||
this.ws.on('message', (data) => {
|
||
try {
|
||
const msg = JSON.parse(data.toString());
|
||
this.handleMessage(msg);
|
||
} catch (e) {
|
||
console.error(`[Player ${this.index}] Failed to parse message:`, e);
|
||
}
|
||
});
|
||
|
||
this.ws.on('error', (err) => {
|
||
console.error(`[Player ${this.index}] ❌ WebSocket error:`, err.message);
|
||
reject(err);
|
||
});
|
||
|
||
this.ws.on('close', (code, reason) => {
|
||
console.log(`[Player ${this.index}] WebSocket closed: ${code} - ${reason}`);
|
||
this.connected = false;
|
||
});
|
||
|
||
setTimeout(() => {
|
||
if (!this.connected) {
|
||
reject(new Error('Connection timeout'));
|
||
}
|
||
}, 10000);
|
||
});
|
||
}
|
||
|
||
handleMessage(msg) {
|
||
console.log(`[Player ${this.index}] Received:`, JSON.stringify(msg).substring(0, 200));
|
||
|
||
// 匹配成功
|
||
if (msg.matchmaker_matched) {
|
||
const matchId = msg.matchmaker_matched.match_id;
|
||
console.log(`[Player ${this.index}] 🎮 Matchmaker matched! Match ID: ${matchId}`);
|
||
this.matchId = matchId;
|
||
|
||
// 自动加入比赛
|
||
setTimeout(() => {
|
||
this.joinMatch(matchId);
|
||
}, 100);
|
||
}
|
||
|
||
// 比赛加入结果
|
||
if (msg.match) {
|
||
console.log(`[Player ${this.index}] ✅ Successfully joined match!`);
|
||
}
|
||
|
||
// 错误信息
|
||
if (msg.error) {
|
||
console.error(`[Player ${this.index}] ❌ Error:`, msg.error.message);
|
||
}
|
||
|
||
// 游戏状态更新
|
||
if (msg.match_data) {
|
||
const opCode = msg.match_data.op_code;
|
||
console.log(`[Player ${this.index}] 📦 Match data received, op_code: ${opCode}`);
|
||
}
|
||
}
|
||
|
||
addToMatchmaker() {
|
||
if (!this.connected) {
|
||
console.error(`[Player ${this.index}] Not connected!`);
|
||
return;
|
||
}
|
||
|
||
const request = {
|
||
cid: `${this.index}_matchmaker`,
|
||
matchmaker_add: {
|
||
min_count: NUM_PLAYERS,
|
||
max_count: NUM_PLAYERS,
|
||
query: '*',
|
||
string_properties: {
|
||
game_token: this.gameToken
|
||
}
|
||
}
|
||
};
|
||
|
||
console.log(`[Player ${this.index}] 🔍 Adding to matchmaker...`);
|
||
this.ws.send(JSON.stringify(request));
|
||
}
|
||
|
||
joinMatch(matchId) {
|
||
if (!this.connected) {
|
||
console.error(`[Player ${this.index}] Not connected!`);
|
||
return;
|
||
}
|
||
|
||
const request = {
|
||
cid: `${this.index}_join`,
|
||
match_join: {
|
||
match_id: matchId,
|
||
metadata: {
|
||
game_token: this.gameToken // 关键:加入时必须带game_token
|
||
}
|
||
}
|
||
};
|
||
|
||
console.log(`[Player ${this.index}] 🚪 Joining match with game_token...`);
|
||
this.ws.send(JSON.stringify(request));
|
||
}
|
||
|
||
// 测试:不带game_token加入
|
||
joinMatchWithoutToken(matchId) {
|
||
const request = {
|
||
cid: `${this.index}_join_no_token`,
|
||
match_join: {
|
||
match_id: matchId
|
||
// 没有 metadata.game_token
|
||
}
|
||
};
|
||
|
||
console.log(`[Player ${this.index}] 🚪 Joining match WITHOUT game_token (should fail)...`);
|
||
this.ws.send(JSON.stringify(request));
|
||
}
|
||
|
||
disconnect() {
|
||
if (this.ws) {
|
||
this.ws.close();
|
||
}
|
||
}
|
||
}
|
||
|
||
async function main() {
|
||
console.log('=== 扫雷游戏匹配测试 ===\n');
|
||
console.log(`测试玩家数: ${NUM_PLAYERS}`);
|
||
console.log(`服务器地址: ${NAKAMA_HOST}\n`);
|
||
|
||
// 步骤1: 获取Nakama认证token(需要替换为实际的认证逻辑)
|
||
console.log('⚠️ 注意: 此脚本需要有效的Nakama认证token和game_token');
|
||
console.log('请确保已配置正确的token后运行\n');
|
||
|
||
// 创建测试玩家
|
||
const players = [];
|
||
|
||
// 这里需要替换为实际的Nakama token
|
||
// 可以通过调用后端API获取
|
||
const nakamaTokens = [
|
||
// 示例token格式,需要替换
|
||
'YOUR_NAKAMA_TOKEN_1',
|
||
'YOUR_NAKAMA_TOKEN_2',
|
||
'YOUR_NAKAMA_TOKEN_3',
|
||
'YOUR_NAKAMA_TOKEN_4',
|
||
];
|
||
|
||
for (let i = 0; i < NUM_PLAYERS; i++) {
|
||
players.push(new TestPlayer(i + 1, nakamaTokens[i], TEST_TOKENS[i]));
|
||
}
|
||
|
||
try {
|
||
// 步骤2: 连接所有玩家
|
||
console.log('--- 步骤1: 连接玩家 ---');
|
||
await Promise.all(players.map(p => p.connect()));
|
||
console.log('✅ 所有玩家已连接\n');
|
||
|
||
// 步骤3: 添加到匹配队列
|
||
console.log('--- 步骤2: 添加到匹配队列 ---');
|
||
for (const player of players) {
|
||
player.addToMatchmaker();
|
||
await sleep(200); // 稍微错开请求
|
||
}
|
||
|
||
// 等待匹配完成
|
||
console.log('\n⏳ 等待匹配完成...');
|
||
await sleep(15000);
|
||
|
||
console.log('\n--- 测试完成 ---');
|
||
|
||
} catch (error) {
|
||
console.error('测试失败:', error);
|
||
} finally {
|
||
// 断开所有连接
|
||
for (const player of players) {
|
||
player.disconnect();
|
||
}
|
||
}
|
||
}
|
||
|
||
function sleep(ms) {
|
||
return new Promise(resolve => setTimeout(resolve, ms));
|
||
}
|
||
|
||
// 运行测试
|
||
main().catch(console.error);
|