提交 c8fd4e62 authored 作者: lidongxu's avatar lidongxu

修复:先2v2以后,复用同一个房间号进入1v1发现游戏结束2/4等待

上级 f537fb4b
...@@ -2,6 +2,7 @@ const express = require('express'); ...@@ -2,6 +2,7 @@ const express = require('express');
const router = express.Router(); const router = express.Router();
const prisma = require('../prisma/client'); const prisma = require('../prisma/client');
const { getWaitingRoomTeamInfo } = require('../socket/roomHandler'); const { getWaitingRoomTeamInfo } = require('../socket/roomHandler');
const { getActiveRoomSubscriberCount } = require('../socket');
// GET /api/rooms — 获取所有房间列表(进行中房间含当前分数) // GET /api/rooms — 获取所有房间列表(进行中房间含当前分数)
router.get('/', async (_req, res, next) => { router.get('/', async (_req, res, next) => {
...@@ -91,20 +92,23 @@ router.post('/allocate', async (_req, res, next) => { ...@@ -91,20 +92,23 @@ router.post('/allocate', async (_req, res, next) => {
/** /**
* 分配房间号: * 分配房间号:
* 1. 优先复用已结束(finished)的最小房间号 * 1. 优先复用已结束(finished)的最小房间号
* 2. 没有可复用的,则取最大房间号 +1 递增 * 2. 复用前要求该房间当前没有任何活跃订阅连接
* 3. 从 1 开始,无上限 * 3. 没有可复用的,则取最大房间号 +1 递增
* 4. 从 1 开始,无上限
* @returns {Promise<string>} 房间号字符串 * @returns {Promise<string>} 房间号字符串
*/ */
async function allocateRoomId() { async function allocateRoomId() {
// 优先找一个已结束的房间号复用(取最小的) // 优先找一个已结束且当前无人订阅的房间号复用(取最小的)
const finished = await prisma.room.findFirst({ const finishedRooms = await prisma.room.findMany({
where: { status: 'finished' }, where: { status: 'finished' },
orderBy: { roomId: 'asc' }, orderBy: { roomId: 'asc' },
select: { roomId: true }, select: { roomId: true },
}); });
if (finished) { for (const room of finishedRooms) {
return finished.roomId; if (getActiveRoomSubscriberCount(room.roomId) === 0) {
return room.roomId;
}
} }
// 没有可复用的,取最大房间号 +1 // 没有可复用的,取最大房间号 +1
......
...@@ -70,6 +70,21 @@ function leaveAllRooms(ws) { ...@@ -70,6 +70,21 @@ function leaveAllRooms(ws) {
} }
} }
/**
* 获取房间内当前活跃订阅连接数。
* role 为空时统计全部角色,传入后只统计指定角色。
*/
function getActiveRoomSubscriberCount(roomId, role = null) {
const subs = roomSubscribers.get(String(roomId));
if (!subs) return 0;
return [...subs].filter(ws => {
if (ws.readyState !== ws.OPEN) return false;
if (!role) return true;
return ws.ctx?.role === role;
}).length;
}
/** /**
* 初始化原生 WebSocket 服务,挂载到 /ws 路径 * 初始化原生 WebSocket 服务,挂载到 /ws 路径
*/ */
...@@ -147,4 +162,12 @@ function initSocket(httpServer) { ...@@ -147,4 +162,12 @@ function initSocket(httpServer) {
return wss; return wss;
} }
module.exports = { initSocket, broadcastToRoom, sendToScreen, joinRoom, leaveAllRooms, screenSockets }; module.exports = {
initSocket,
broadcastToRoom,
sendToScreen,
joinRoom,
leaveAllRooms,
getActiveRoomSubscriberCount,
screenSockets,
};
...@@ -68,6 +68,9 @@ function registerRoomHandlers(ws, { broadcastToRoom, joinRoom, leaveAllRooms, ro ...@@ -68,6 +68,9 @@ function registerRoomHandlers(ws, { broadcastToRoom, joinRoom, leaveAllRooms, ro
const duration = Number(gameDuration) || 0; // 0 表示不限时 const duration = Number(gameDuration) || 0; // 0 表示不限时
try { try {
// 同一个连接重新建房前,先退出旧房间,避免旧订阅残留到新局。
leaveAllRooms(ws);
// 写库:waiting 状态 // 写库:waiting 状态
await prisma.room.upsert({ await prisma.room.upsert({
where: { roomId: rid }, where: { roomId: rid },
...@@ -127,6 +130,9 @@ function registerRoomHandlers(ws, { broadcastToRoom, joinRoom, leaveAllRooms, ro ...@@ -127,6 +130,9 @@ function registerRoomHandlers(ws, { broadcastToRoom, joinRoom, leaveAllRooms, ro
// 如果等待表里没有,说明是旧流程(游戏中直接 join),兼容处理 // 如果等待表里没有,说明是旧流程(游戏中直接 join),兼容处理
if (!waiting) { if (!waiting) {
try { try {
// 同一个连接重新进房前,先退出旧房间,避免旧订阅残留到新局。
leaveAllRooms(ws);
await prisma.room.upsert({ await prisma.room.upsert({
where: { roomId: rid }, where: { roomId: rid },
update: { status: 'playing' }, update: { status: 'playing' },
...@@ -150,6 +156,9 @@ function registerRoomHandlers(ws, { broadcastToRoom, joinRoom, leaveAllRooms, ro ...@@ -150,6 +156,9 @@ function registerRoomHandlers(ws, { broadcastToRoom, joinRoom, leaveAllRooms, ro
return; return;
} }
// 同一个连接重新进房前,先退出旧房间,避免旧订阅残留到新局。
leaveAllRooms(ws);
// 校验:房间是否已满 // 校验:房间是否已满
if (waiting.joined.size >= waiting.totalSeats) { if (waiting.joined.size >= waiting.totalSeats) {
ws.sendEvent('error', { message: '房间已满' }); ws.sendEvent('error', { message: '房间已满' });
...@@ -260,10 +269,12 @@ function registerRoomHandlers(ws, { broadcastToRoom, joinRoom, leaveAllRooms, ro ...@@ -260,10 +269,12 @@ function registerRoomHandlers(ws, { broadcastToRoom, joinRoom, leaveAllRooms, ro
const roomScores = gameOverScores.get(roomId); const roomScores = gameOverScores.get(roomId);
roomScores.set(playerId, { score: score ?? 0, nickname, team: ws.ctx.team }); roomScores.set(playerId, { score: score ?? 0, nickname, team: ws.ctx.team });
// 获取房间内所有连接的 minigame 玩家数量 // 完成总人数以本局房间座位数为准,避免旧连接残留导致 1v1 被算成 4 人局。
const subs = roomSubscribers.get(roomId); const room = await prisma.room.findUnique({
const minigamePlayers = subs ? [...subs].filter(s => s.ctx?.role === 'minigame') : []; where: { roomId },
const totalPlayers = minigamePlayers.length; select: { totalSeats: true },
});
const totalPlayers = Number(room?.totalSeats) || roomScores.size;
const reportedPlayers = roomScores.size; const reportedPlayers = roomScores.size;
console.log(`[Room] 游戏结束分数收集 roomId=${roomId} playerId=${playerId} score=${score ?? 0} (${reportedPlayers}/${totalPlayers})`); console.log(`[Room] 游戏结束分数收集 roomId=${roomId} playerId=${playerId} score=${score ?? 0} (${reportedPlayers}/${totalPlayers})`);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论