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:
2025-08-06 02:58:29 -06:00
parent a28bc286a1
commit 1392e5a652
5 changed files with 192 additions and 93 deletions

View File

@@ -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;

View File

@@ -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>

View File

@@ -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);