Files
snatchgame/server/src/rooms/LobbyRoom.ts
josedario87 1392e5a652 fix: resolve room state synchronization and player display issues
- Fix room.state.players undefined error on component mount
- Wait for initial state sync before accessing room data
- Move message handlers from service to Game component
- Fix player count display in waiting screen (was showing 0/2)
- Prevent lobby component from clearing game room on unmount
- Separate leaveLobby() and leaveGame() methods for proper cleanup
- Ensure player names persist when moving from lobby to game
- Add proper error handling for state initialization
2025-08-06 02:58:29 -06:00

169 lines
4.8 KiB
TypeScript

import { Room, Client, matchMaker } from "colyseus";
import { LobbyState, AvailableRoom } from "./schemas/LobbyState";
import { NameManager } from "../utils/nameManager";
export class LobbyRoom extends Room<LobbyState> {
private updateInterval?: NodeJS.Timeout;
onCreate(options: any) {
this.setState(new LobbyState());
this.setPrivate(false);
this.onMessage("setName", (client, playerName: string) => {
this.handleSetName(client, playerName);
});
this.onMessage("quickPlay", (client) => {
this.handleQuickPlay(client);
});
this.onMessage("joinRoom", (client, roomId: string) => {
this.handleJoinRoom(client, roomId);
});
this.updateInterval = setInterval(() => {
this.updateAvailableRooms();
}, 2000);
}
onJoin(client: Client, options: any) {
console.log(`[LobbyRoom] ${client.sessionId} joined lobby`);
const defaultName = `guest`;
const uniqueName = NameManager.getInstance().generateUniquePlayerName(defaultName, client.sessionId);
this.state.addPlayer(client.sessionId, uniqueName);
client.send("welcome", {
sessionId: client.sessionId,
assignedName: uniqueName
});
this.updateAvailableRooms();
}
onLeave(client: Client, consented: boolean) {
console.log(`[LobbyRoom] ${client.sessionId} left lobby`);
const player = this.state.players.get(client.sessionId);
if (player) {
NameManager.getInstance().releasePlayerName(client.sessionId);
}
this.state.removePlayer(client.sessionId);
}
onDispose() {
console.log("[LobbyRoom] Disposing lobby room");
if (this.updateInterval) {
clearInterval(this.updateInterval);
}
this.state.players.forEach(player => {
NameManager.getInstance().releasePlayerName(player.sessionId);
});
}
private handleSetName(client: Client, playerName: string) {
const currentPlayer = this.state.players.get(client.sessionId);
if (!currentPlayer) return;
NameManager.getInstance().releasePlayerName(client.sessionId);
const uniqueName = NameManager.getInstance().generateUniquePlayerName(playerName, client.sessionId);
currentPlayer.name = uniqueName;
client.send("nameUpdated", {
name: uniqueName
});
}
private async handleQuickPlay(client: Client) {
const player = this.state.players.get(client.sessionId);
if (!player || player.inGame) return;
try {
// First try to find an available room
const rooms = await matchMaker.query({ name: "game", locked: false });
let targetRoomId: string;
// Find a room with less than 2 players
const availableRoom = rooms.find(room => room.clients < 2);
if (availableRoom) {
targetRoomId = availableRoom.roomId;
} else {
// If no room available, create a new one
const newRoom = await matchMaker.createRoom("game", {});
targetRoomId = newRoom.roomId;
}
this.state.setPlayerInGame(client.sessionId, true);
// Send the roomId to the client
client.send("gameJoined", {
roomId: targetRoomId
});
// Don't auto-leave, let the client handle it
// The client will leave the lobby after successfully joining the game room
} catch (error) {
console.error("[LobbyRoom] Error in quick play:", error);
client.send("error", {
message: "Could not find or create a game room"
});
}
}
private async handleJoinRoom(client: Client, roomId: string) {
const player = this.state.players.get(client.sessionId);
if (!player || player.inGame) return;
try {
// Verify the room exists and is available
const rooms = await matchMaker.query({ roomId });
if (rooms.length === 0 || rooms[0].clients >= 2) {
throw new Error("Room not available");
}
this.state.setPlayerInGame(client.sessionId, true);
// Send the roomId to the client
client.send("gameJoined", {
roomId: roomId
});
// Don't auto-leave, let the client handle it
// The client will leave the lobby after successfully joining the game room
} catch (error) {
console.error("[LobbyRoom] Error joining room:", error);
client.send("error", {
message: "Could not join the selected room"
});
}
}
private async updateAvailableRooms() {
try {
const rooms = await matchMaker.query({ name: "game" });
const availableRooms = rooms
.filter(room => !room.locked && room.clients < 2)
.map(room => new AvailableRoom(
room.roomId,
room.clients,
room.metadata?.gameStatus || "waiting"
));
this.state.updateAvailableRooms(availableRooms);
} catch (error) {
console.error("[LobbyRoom] Error updating available rooms:", error);
}
}
}