shuffle players implementado correctamente

This commit is contained in:
2025-08-15 20:03:26 -06:00
parent 9b84008f19
commit f214174bab
4 changed files with 234 additions and 40 deletions

View File

@@ -6,6 +6,26 @@ import { broadcastDashboardUpdate } from "../adminApi";
export class GameRoom extends Room<GameState> {
maxClients = 2;
getAvailableData() {
// If waiting for shuffled players, report as available regardless of current client count
if (this.isWaitingForShuffledPlayers) {
return {
clients: 0,
maxClients: this.maxClients,
metadata: this.metadata
};
}
return super.getAvailableData();
}
hasReachedMaxClients() {
// If waiting for shuffled players, allow up to 2 new clients regardless of current count
if (this.isWaitingForShuffledPlayers) {
return false;
}
return super.hasReachedMaxClients();
}
private gameInterval?: NodeJS.Timeout;
private recentSystemMessage: { text: string; kind: string; timestamp: number } | null = null;
@@ -242,7 +262,7 @@ export class GameRoom extends Room<GameState> {
}
onJoin(client: Client, options: any) {
console.log(`[GameRoom] ${client.sessionId} joined room ${this.roomId} with name: ${options.playerName}`);
console.log(`[GameRoom] ${client.sessionId} joined room ${this.roomId} with name: ${options.playerName}, isShuffleJoin: ${options.isShuffleJoin}, isWaitingForShuffledPlayers: ${this.isWaitingForShuffledPlayers}, playersCount: ${this.state.players.size}`);
const uuid = options?.uuid as string | undefined;
// UUID-based reconnection: if game already started or room is full, allow join if UUID matches a participant
@@ -283,12 +303,13 @@ export class GameRoom extends Room<GameState> {
}
// Special handling for shuffled players
if (this.isWaitingForShuffledPlayers && options.uuid) {
if ((this.isWaitingForShuffledPlayers || options.isShuffleJoin) && options.uuid) {
return this.handleShuffledPlayerJoin(client, options);
}
// Prevent new joins if game already started or two players are registered
if (this.state.gameStatus !== GameStatus.WAITING || this.state.players.size >= 2) {
// BUT allow if waiting for shuffled players (they should go through shuffle handler above)
if ((this.state.gameStatus !== GameStatus.WAITING || this.state.players.size >= 2) && !this.isWaitingForShuffledPlayers) {
try { client.leave(1000); } catch {}
return;
}
@@ -685,10 +706,18 @@ export class GameRoom extends Room<GameState> {
}
});
// Clear all players from state completely
this.state.players.clear();
this.state.p1Id = "";
this.state.p2Id = "";
this.sessionToUuid.clear();
// Reset room state but keep variant
const currentVariant = this.state.currentVariant;
this.state.restartGame();
this.state.currentVariant = currentVariant;
// Ensure room is accepting new joins after shuffle
try { (this as any).unlock?.(); } catch {}
// Prepare for new players
this.isWaitingForShuffledPlayers = true;
@@ -704,42 +733,75 @@ export class GameRoom extends Room<GameState> {
this.expectedShuffledPlayers = assignments;
this.isWaitingForShuffledPlayers = true;
// Update metadata
// Update metadata to reflect that room is waiting for shuffled players
this.setMetadata({
gameStatus: 'waiting',
currentRound: 1,
currentVariant: this.state.currentVariant
currentVariant: this.state.currentVariant,
playersCount: 0, // Force reset player count
maxClients: 2
});
// Make sure the room is unlocked and can accept new connections
try {
(this as any).unlock?.();
// Force Colyseus to recalculate available slots
(this as any).maxClients = 2;
} catch {}
}
private handleShuffledPlayerJoin(client: Client, options: any) {
const uuid = options.uuid;
console.log(`[GameRoom] Shuffled player ${uuid} trying to join room ${this.roomId}`);
console.log(`[GameRoom] Shuffled player ${uuid} trying to join room ${this.roomId} with options:`, options);
console.log(`[GameRoom] Room state - isWaitingForShuffledPlayers: ${this.isWaitingForShuffledPlayers}, playersCount: ${this.state.players.size}, expectedPlayers:`, this.expectedShuffledPlayers);
// Check if this player is expected in this room
let expectedRole: 'P1' | 'P2' | null = null;
let expectedRole: 'P1' | 'P2' | null = options.role || null;
if (this.expectedShuffledPlayers.p1?.uuid === uuid) {
expectedRole = 'P1';
} else if (this.expectedShuffledPlayers.p2?.uuid === uuid) {
expectedRole = 'P2';
// If role not provided, determine from expected players
if (!expectedRole) {
if (this.expectedShuffledPlayers.p1?.uuid === uuid) {
expectedRole = 'P1';
} else if (this.expectedShuffledPlayers.p2?.uuid === uuid) {
expectedRole = 'P2';
}
}
if (!expectedRole) {
// Fallback: consult NameManager assignment if not set yet in room
try {
const { NameManager } = require("../utils/nameManager");
const assign = NameManager.getInstance().getPlayerRoomAssignment(uuid);
if (assign && assign.roomId === this.roomId) {
expectedRole = assign.role;
if (assign.role === 'P1' && !this.expectedShuffledPlayers.p1) {
this.expectedShuffledPlayers.p1 = { uuid, name: options.playerName || 'player', color: options.playerColor };
} else if (assign.role === 'P2' && !this.expectedShuffledPlayers.p2) {
this.expectedShuffledPlayers.p2 = { uuid, name: options.playerName || 'player', color: options.playerColor };
}
}
} catch {}
}
if (!expectedRole) {
console.log(`[GameRoom] Player ${uuid} not expected in room ${this.roomId}, rejecting`);
try { client.leave(1000); } catch {}
return;
}
// Get player info from expected data
// Get player info from expected data or use provided options as fallback
const expectedPlayerData = expectedRole === 'P1' ? this.expectedShuffledPlayers.p1 : this.expectedShuffledPlayers.p2;
const playerName = expectedPlayerData?.name || options.playerName || 'player';
const playerColor = expectedPlayerData?.color || options.playerColor || "#667eea";
console.log(`[GameRoom] Adding shuffled player ${uuid} as ${expectedRole} in room ${this.roomId}`);
// Add player with the expected role
const player = this.state.addPlayer(client.sessionId, expectedPlayerData.name);
const player = this.state.addPlayer(client.sessionId, playerName);
player.role = expectedRole;
player.color = expectedPlayerData.color || "#667eea";
player.color = playerColor;
(player as any).uuid = uuid;
this.sessionToUuid.set(client.sessionId, uuid);
// Set role IDs
if (expectedRole === 'P1') {
@@ -750,12 +812,16 @@ export class GameRoom extends Room<GameState> {
client.send("playerInfo", {
sessionId: client.sessionId,
name: expectedPlayerData.name,
name: playerName,
roomId: this.roomId
});
// Remove from NameManager assignment once joined
NameManager.getInstance().removePlayerRoomAssignment(uuid);
// Update mappings in NameManager
try {
const { NameManager } = require("../utils/nameManager");
NameManager.getInstance().setCurrentRoom(uuid, this.roomId);
NameManager.getInstance().removePlayerRoomAssignment(uuid);
} catch {}
// Check if room is complete
if (this.state.players.size === 2) {

View File

@@ -44,6 +44,52 @@ export class LobbyRoom extends Room<LobbyState> {
this.handleJoinRoom(client, roomId);
});
// Client asks server to suggest a resume target (after listeners are ready)
this.onMessage("resumeMe", async (client) => {
const uuid = this.sessionToUuid.get(client.sessionId);
if (!uuid) return;
// If shuffle in progress and an assignment exists, prefer it
if (NameManager.getInstance().isShuffleInProgress()) {
const assignment = NameManager.getInstance().getPlayerRoomAssignment(uuid);
if (assignment) {
try {
const delay = assignment.role === 'P1' ? 150 : 750; // stagger joins to avoid full race
const playerName = NameManager.getInstance().getPlayerName(uuid) || "";
const playerColor = NameManager.getInstance().getPlayerColor(uuid) || "#667eea";
setTimeout(() => {
try {
client.send("shuffleRedirect", {
roomId: assignment.roomId,
role: assignment.role,
playerName,
playerColor,
isShuffleJoin: true
});
} catch {}
}, delay);
} catch {}
return;
}
}
// Otherwise, try current room mapping
try {
const currentRoomId = NameManager.getInstance().getCurrentRoom(uuid);
if (currentRoomId) {
const rooms = await matchMaker.query({ roomId: currentRoomId });
const room = rooms[0];
const status = room?.metadata?.gameStatus || "waiting";
if (room && status !== "finished") {
const token = NameManager.getInstance().getReconnectToken(uuid);
if (token) { client.send("resumeReconnection", { token }); }
else { client.send("resumeGame", { roomId: currentRoomId }); }
}
}
} catch {}
});
this.updateInterval = setInterval(() => {
this.updateAvailableRooms();
}, 2000);
@@ -88,12 +134,8 @@ export class LobbyRoom extends Room<LobbyState> {
name: existingName || "",
color: this.state.players.get(client.sessionId)?.color || "#667eea"
});
// Then immediately redirect to assigned room
setTimeout(() => {
this.handleJoinRoom(client, assignment.roomId);
}, 500);
// Do not push immediate redirect here; client will send "resumeMe" after handlers are ready
return;
}
}
@@ -237,7 +279,7 @@ export class LobbyRoom extends Room<LobbyState> {
}
}
private async handleJoinRoom(client: Client, roomId: string) {
private async handleJoinRoom(client: Client, roomId: string, opts?: { force?: boolean }) {
const player = this.state.players.get(client.sessionId);
if (!player || player.inGame) return;
if (!player.name || !player.name.trim()) {
@@ -246,12 +288,13 @@ export class LobbyRoom extends Room<LobbyState> {
}
try {
// Verify the room exists and is available
const rooms = await matchMaker.query({ roomId });
const status = rooms[0]?.metadata?.gameStatus || "waiting";
if (rooms.length === 0 || rooms[0].clients >= 2 || status !== "waiting") {
throw new Error("Room not available");
if (!opts?.force) {
// Verify the room exists and is available
const rooms = await matchMaker.query({ roomId });
const status = rooms[0]?.metadata?.gameStatus || "waiting";
if (rooms.length === 0 || rooms[0].clients >= 2 || status !== "waiting") {
throw new Error("Room not available");
}
}
this.state.setPlayerInGame(client.sessionId, true);