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
This commit is contained in:
@@ -61,67 +61,68 @@ class ColyseusService {
|
||||
}
|
||||
}
|
||||
|
||||
async quickPlay(): Promise<void> {
|
||||
if (this.lobbyRoom.value) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const room = this.lobbyRoom.value!;
|
||||
|
||||
room.onMessage("roomReservation", async (reservation) => {
|
||||
try {
|
||||
await this.joinGameByReservation(reservation);
|
||||
resolve();
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
|
||||
room.onMessage("error", (error) => {
|
||||
reject(new Error(error.message));
|
||||
});
|
||||
|
||||
room.send("quickPlay");
|
||||
});
|
||||
async quickPlay(): Promise<Room> {
|
||||
if (!this.lobbyRoom.value) {
|
||||
throw new Error("Not in lobby");
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const room = this.lobbyRoom.value!;
|
||||
console.log('Sending quickPlay message to lobby...');
|
||||
|
||||
const handleGameJoined = async (data: any) => {
|
||||
console.log('Received gameJoined with roomId:', data.roomId);
|
||||
try {
|
||||
// Join the game room directly using the roomId
|
||||
console.log('Joining game room with name:', this.playerName.value);
|
||||
const gameRoom = await this.client.joinById(data.roomId, {
|
||||
playerName: this.playerName.value
|
||||
});
|
||||
|
||||
// Ensure the room id is set
|
||||
if (!gameRoom.id) {
|
||||
gameRoom.id = data.roomId;
|
||||
}
|
||||
|
||||
console.log('Successfully joined game room:', gameRoom.id, gameRoom);
|
||||
console.log('Setting gameRoom.value...');
|
||||
this.gameRoom.value = gameRoom;
|
||||
this.currentRoom = gameRoom;
|
||||
console.log('gameRoom.value is now:', this.gameRoom.value);
|
||||
|
||||
// Don't register message handlers here - let the Game component handle them
|
||||
|
||||
resolve(gameRoom);
|
||||
} catch (error) {
|
||||
console.error('Error joining game room:', error);
|
||||
reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleError = (error: any) => {
|
||||
console.error('Received error from lobby:', error);
|
||||
reject(new Error(error.message));
|
||||
};
|
||||
|
||||
room.onMessage("gameJoined", handleGameJoined);
|
||||
room.onMessage("error", handleError);
|
||||
|
||||
room.send("quickPlay");
|
||||
});
|
||||
}
|
||||
|
||||
async joinGameRoom(roomId: string): Promise<void> {
|
||||
if (this.lobbyRoom.value) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const room = this.lobbyRoom.value!;
|
||||
|
||||
room.onMessage("roomReservation", async (reservation) => {
|
||||
try {
|
||||
await this.joinGameByReservation(reservation);
|
||||
resolve();
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
|
||||
room.onMessage("error", (error) => {
|
||||
reject(new Error(error.message));
|
||||
});
|
||||
|
||||
room.send("joinRoom", roomId);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async joinGameByReservation(reservation: any): Promise<void> {
|
||||
async joinGameRoom(roomId: string): Promise<Room> {
|
||||
try {
|
||||
const room = await this.client.consumeSeatReservation(reservation);
|
||||
this.gameRoom.value = room;
|
||||
this.currentRoom = room;
|
||||
|
||||
room.onMessage("playerInfo", (data) => {
|
||||
this.sessionId.value = data.sessionId;
|
||||
this.playerName.value = data.name;
|
||||
const gameRoom = await this.client.joinById(roomId, {
|
||||
playerName: this.playerName.value
|
||||
});
|
||||
|
||||
if (this.lobbyRoom.value) {
|
||||
this.lobbyRoom.value.leave();
|
||||
this.lobbyRoom.value = null;
|
||||
}
|
||||
this.gameRoom.value = gameRoom;
|
||||
this.currentRoom = gameRoom;
|
||||
|
||||
// Don't register message handlers here - let the Game component handle them
|
||||
|
||||
return gameRoom;
|
||||
} catch (error) {
|
||||
console.error("Failed to join game room:", error);
|
||||
throw error;
|
||||
@@ -134,7 +135,31 @@ class ColyseusService {
|
||||
}
|
||||
}
|
||||
|
||||
leaveLobby(): void {
|
||||
console.log('leaveLobby called');
|
||||
if (this.lobbyRoom.value) {
|
||||
this.lobbyRoom.value.leave();
|
||||
this.lobbyRoom.value = null;
|
||||
if (this.currentRoom === this.lobbyRoom.value) {
|
||||
this.currentRoom = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
leaveGame(): void {
|
||||
console.log('leaveGame called');
|
||||
if (this.gameRoom.value) {
|
||||
this.gameRoom.value.leave();
|
||||
this.gameRoom.value = null;
|
||||
if (this.currentRoom === this.gameRoom.value) {
|
||||
this.currentRoom = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
leaveCurrentRoom(): void {
|
||||
console.log('leaveCurrentRoom called - THIS SHOULD NOT BE USED');
|
||||
// This method is deprecated - use leaveLobby() or leaveGame() instead
|
||||
if (this.currentRoom) {
|
||||
this.currentRoom.leave();
|
||||
this.currentRoom = null;
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
<div class="waiting-message">
|
||||
<div class="spinner"></div>
|
||||
<h2>Waiting for opponent...</h2>
|
||||
<p>Players in room: {{ players.length }}/2</p>
|
||||
<p>Game will start when another player joins</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -90,15 +91,45 @@ const sessionId = computed(() => colyseusService.sessionId.value);
|
||||
let clickTimeout: NodeJS.Timeout;
|
||||
|
||||
onMounted(() => {
|
||||
console.log('Game component mounted');
|
||||
console.log('colyseusService.gameRoom:', colyseusService.gameRoom);
|
||||
console.log('colyseusService.gameRoom.value:', colyseusService.gameRoom.value);
|
||||
const room = colyseusService.gameRoom.value;
|
||||
console.log('Current game room:', room);
|
||||
|
||||
if (!room) {
|
||||
console.error('No game room found, redirecting to lobby...');
|
||||
router.push('/');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Setting up game room listeners...');
|
||||
|
||||
const $ = getStateCallbacks(room);
|
||||
|
||||
// Wait for the initial state sync
|
||||
room.onStateChange.once((state) => {
|
||||
console.log('Initial state received:', state);
|
||||
gameStatus.value = state.gameStatus || 'waiting';
|
||||
timeRemaining.value = state.timeRemaining || 600;
|
||||
winner.value = state.winner || '';
|
||||
|
||||
// Load existing players if they exist
|
||||
if (state.players) {
|
||||
console.log('Players map exists in initial state, loading players...');
|
||||
state.players.forEach((player: any, sessionId: string) => {
|
||||
console.log('Loading initial player:', player);
|
||||
players.value.push({
|
||||
sessionId: player.sessionId,
|
||||
name: player.name,
|
||||
clicks: player.clicks,
|
||||
connected: player.connected
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Listen for future changes
|
||||
$(room.state).listen("gameStatus", (value: string) => {
|
||||
gameStatus.value = value;
|
||||
});
|
||||
@@ -112,12 +143,16 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
$(room.state).players.onAdd((player: any) => {
|
||||
players.value.push({
|
||||
sessionId: player.sessionId,
|
||||
name: player.name,
|
||||
clicks: player.clicks,
|
||||
connected: player.connected
|
||||
});
|
||||
// Check if player already exists before adding
|
||||
const exists = players.value.find(p => p.sessionId === player.sessionId);
|
||||
if (!exists) {
|
||||
players.value.push({
|
||||
sessionId: player.sessionId,
|
||||
name: player.name,
|
||||
clicks: player.clicks,
|
||||
connected: player.connected
|
||||
});
|
||||
}
|
||||
|
||||
$(player).listen("clicks", (value: number) => {
|
||||
const p = players.value.find(p => p.sessionId === player.sessionId);
|
||||
@@ -137,21 +172,31 @@ onMounted(() => {
|
||||
}
|
||||
});
|
||||
|
||||
room.onMessage("playerInfo", (info) => {
|
||||
console.log('Received playerInfo:', info);
|
||||
colyseusService.sessionId.value = info.sessionId;
|
||||
colyseusService.playerName.value = info.name;
|
||||
});
|
||||
|
||||
room.onMessage("gameStart", () => {
|
||||
console.log("Game started!");
|
||||
gameStatus.value = 'playing';
|
||||
});
|
||||
|
||||
room.onMessage("gameEnd", (data) => {
|
||||
console.log("Game ended!", data);
|
||||
gameStatus.value = 'finished';
|
||||
});
|
||||
|
||||
room.onMessage("gamePaused", () => {
|
||||
console.log("Game paused");
|
||||
gameStatus.value = 'paused';
|
||||
});
|
||||
|
||||
room.onMessage("gameRestart", () => {
|
||||
console.log("Game restarting");
|
||||
players.value.forEach(p => p.clicks = 0);
|
||||
gameStatus.value = 'waiting';
|
||||
});
|
||||
});
|
||||
|
||||
@@ -180,7 +225,7 @@ function formatTime(seconds: number): string {
|
||||
}
|
||||
|
||||
function leaveGame() {
|
||||
colyseusService.leaveCurrentRoom();
|
||||
colyseusService.leaveGame();
|
||||
router.push('/');
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -131,7 +131,8 @@ onMounted(async () => {
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
colyseusService.leaveCurrentRoom();
|
||||
// Don't leave the current room - it might be a game room now
|
||||
console.log('Lobby component unmounting...');
|
||||
});
|
||||
|
||||
async function updateName() {
|
||||
@@ -143,9 +144,21 @@ async function updateName() {
|
||||
|
||||
async function handleQuickPlay() {
|
||||
isJoining.value = true;
|
||||
console.log('Starting quickPlay...');
|
||||
try {
|
||||
await colyseusService.quickPlay();
|
||||
router.push('/game');
|
||||
const gameRoom = await colyseusService.quickPlay();
|
||||
console.log('Game room joined:', gameRoom?.id, 'Full room object:', gameRoom);
|
||||
|
||||
// Leave the lobby room before navigating
|
||||
if (colyseusService.lobbyRoom.value) {
|
||||
console.log('Leaving lobby room...');
|
||||
colyseusService.lobbyRoom.value.leave();
|
||||
colyseusService.lobbyRoom.value = null;
|
||||
}
|
||||
|
||||
console.log('Navigating to /game...');
|
||||
await router.push('/game');
|
||||
console.log('Navigation complete');
|
||||
} catch (error) {
|
||||
console.error('Failed to join game:', error);
|
||||
isJoining.value = false;
|
||||
@@ -155,7 +168,13 @@ async function handleQuickPlay() {
|
||||
async function joinRoom(roomId: string) {
|
||||
isJoining.value = true;
|
||||
try {
|
||||
await colyseusService.joinGameRoom(roomId);
|
||||
// For direct room joining, we can use joinGameRoom directly
|
||||
const gameRoom = await colyseusService.joinGameRoom(roomId);
|
||||
// Leave the lobby room before navigating
|
||||
if (colyseusService.lobbyRoom.value) {
|
||||
colyseusService.lobbyRoom.value.leave();
|
||||
colyseusService.lobbyRoom.value = null;
|
||||
}
|
||||
router.push('/game');
|
||||
} catch (error) {
|
||||
console.error('Failed to join room:', error);
|
||||
|
||||
@@ -34,16 +34,16 @@ export class GameRoom extends Room<GameState> {
|
||||
}
|
||||
|
||||
onJoin(client: Client, options: any) {
|
||||
console.log(`[GameRoom] ${client.sessionId} joined room ${this.roomId}`);
|
||||
console.log(`[GameRoom] ${client.sessionId} joined room ${this.roomId} with name: ${options.playerName}`);
|
||||
|
||||
// Use the playerName passed from the lobby - don't generate a new one!
|
||||
const playerName = options.playerName || "player";
|
||||
const uniqueName = NameManager.getInstance().generateUniquePlayerName(playerName, client.sessionId);
|
||||
|
||||
this.state.addPlayer(client.sessionId, uniqueName);
|
||||
this.state.addPlayer(client.sessionId, playerName);
|
||||
|
||||
client.send("playerInfo", {
|
||||
sessionId: client.sessionId,
|
||||
name: uniqueName,
|
||||
name: playerName,
|
||||
roomId: this.roomId
|
||||
});
|
||||
|
||||
@@ -58,7 +58,7 @@ export class GameRoom extends Room<GameState> {
|
||||
const player = this.state.players.get(client.sessionId);
|
||||
if (player) {
|
||||
player.connected = false;
|
||||
NameManager.getInstance().releasePlayerName(client.sessionId);
|
||||
// Don't release the name here - it's managed by the LobbyRoom
|
||||
}
|
||||
|
||||
if (this.state.gameStatus === GameStatus.PLAYING) {
|
||||
@@ -90,9 +90,7 @@ export class GameRoom extends Room<GameState> {
|
||||
clearInterval(this.gameInterval);
|
||||
}
|
||||
|
||||
this.state.players.forEach(player => {
|
||||
NameManager.getInstance().releasePlayerName(player.sessionId);
|
||||
});
|
||||
// Don't release names here - they're managed by the LobbyRoom
|
||||
}
|
||||
|
||||
private startGame() {
|
||||
|
||||
@@ -85,20 +85,30 @@ export class LobbyRoom extends Room<LobbyState> {
|
||||
if (!player || player.inGame) return;
|
||||
|
||||
try {
|
||||
const reservation = await matchMaker.joinOrCreate("game", {
|
||||
playerName: player.name
|
||||
});
|
||||
// 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);
|
||||
|
||||
client.send("roomReservation", {
|
||||
sessionId: reservation.sessionId,
|
||||
room: reservation.room
|
||||
// Send the roomId to the client
|
||||
client.send("gameJoined", {
|
||||
roomId: targetRoomId
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
client.leave();
|
||||
}, 1000);
|
||||
// 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);
|
||||
@@ -113,20 +123,22 @@ export class LobbyRoom extends Room<LobbyState> {
|
||||
if (!player || player.inGame) return;
|
||||
|
||||
try {
|
||||
const reservation = await matchMaker.joinById(roomId, {
|
||||
playerName: player.name
|
||||
});
|
||||
// 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);
|
||||
|
||||
client.send("roomReservation", {
|
||||
sessionId: reservation.sessionId,
|
||||
room: reservation.room
|
||||
// Send the roomId to the client
|
||||
client.send("gameJoined", {
|
||||
roomId: roomId
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
client.leave();
|
||||
}, 1000);
|
||||
// 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);
|
||||
|
||||
Reference in New Issue
Block a user