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> {
|
async quickPlay(): Promise<Room> {
|
||||||
if (this.lobbyRoom.value) {
|
if (!this.lobbyRoom.value) {
|
||||||
|
throw new Error("Not in lobby");
|
||||||
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const room = this.lobbyRoom.value!;
|
const room = this.lobbyRoom.value!;
|
||||||
|
console.log('Sending quickPlay message to lobby...');
|
||||||
|
|
||||||
room.onMessage("roomReservation", async (reservation) => {
|
const handleGameJoined = async (data: any) => {
|
||||||
|
console.log('Received gameJoined with roomId:', data.roomId);
|
||||||
try {
|
try {
|
||||||
await this.joinGameByReservation(reservation);
|
// Join the game room directly using the roomId
|
||||||
resolve();
|
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) {
|
} catch (error) {
|
||||||
|
console.error('Error joining game room:', error);
|
||||||
reject(error);
|
reject(error);
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
room.onMessage("error", (error) => {
|
const handleError = (error: any) => {
|
||||||
|
console.error('Received error from lobby:', error);
|
||||||
reject(new Error(error.message));
|
reject(new Error(error.message));
|
||||||
});
|
};
|
||||||
|
|
||||||
|
room.onMessage("gameJoined", handleGameJoined);
|
||||||
|
room.onMessage("error", handleError);
|
||||||
|
|
||||||
room.send("quickPlay");
|
room.send("quickPlay");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async joinGameRoom(roomId: string): Promise<void> {
|
async joinGameRoom(roomId: string): Promise<Room> {
|
||||||
if (this.lobbyRoom.value) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const room = this.lobbyRoom.value!;
|
|
||||||
|
|
||||||
room.onMessage("roomReservation", async (reservation) => {
|
|
||||||
try {
|
try {
|
||||||
await this.joinGameByReservation(reservation);
|
const gameRoom = await this.client.joinById(roomId, {
|
||||||
resolve();
|
playerName: this.playerName.value
|
||||||
} catch (error) {
|
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
room.onMessage("error", (error) => {
|
this.gameRoom.value = gameRoom;
|
||||||
reject(new Error(error.message));
|
this.currentRoom = gameRoom;
|
||||||
});
|
|
||||||
|
|
||||||
room.send("joinRoom", roomId);
|
// Don't register message handlers here - let the Game component handle them
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async joinGameByReservation(reservation: any): Promise<void> {
|
return gameRoom;
|
||||||
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;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.lobbyRoom.value) {
|
|
||||||
this.lobbyRoom.value.leave();
|
|
||||||
this.lobbyRoom.value = null;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to join game room:", error);
|
console.error("Failed to join game room:", error);
|
||||||
throw 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 {
|
leaveCurrentRoom(): void {
|
||||||
|
console.log('leaveCurrentRoom called - THIS SHOULD NOT BE USED');
|
||||||
|
// This method is deprecated - use leaveLobby() or leaveGame() instead
|
||||||
if (this.currentRoom) {
|
if (this.currentRoom) {
|
||||||
this.currentRoom.leave();
|
this.currentRoom.leave();
|
||||||
this.currentRoom = null;
|
this.currentRoom = null;
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
<div class="waiting-message">
|
<div class="waiting-message">
|
||||||
<div class="spinner"></div>
|
<div class="spinner"></div>
|
||||||
<h2>Waiting for opponent...</h2>
|
<h2>Waiting for opponent...</h2>
|
||||||
|
<p>Players in room: {{ players.length }}/2</p>
|
||||||
<p>Game will start when another player joins</p>
|
<p>Game will start when another player joins</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -90,15 +91,45 @@ const sessionId = computed(() => colyseusService.sessionId.value);
|
|||||||
let clickTimeout: NodeJS.Timeout;
|
let clickTimeout: NodeJS.Timeout;
|
||||||
|
|
||||||
onMounted(() => {
|
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;
|
const room = colyseusService.gameRoom.value;
|
||||||
|
console.log('Current game room:', room);
|
||||||
|
|
||||||
if (!room) {
|
if (!room) {
|
||||||
|
console.error('No game room found, redirecting to lobby...');
|
||||||
router.push('/');
|
router.push('/');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('Setting up game room listeners...');
|
||||||
|
|
||||||
const $ = getStateCallbacks(room);
|
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) => {
|
$(room.state).listen("gameStatus", (value: string) => {
|
||||||
gameStatus.value = value;
|
gameStatus.value = value;
|
||||||
});
|
});
|
||||||
@@ -112,12 +143,16 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
$(room.state).players.onAdd((player: any) => {
|
$(room.state).players.onAdd((player: any) => {
|
||||||
|
// Check if player already exists before adding
|
||||||
|
const exists = players.value.find(p => p.sessionId === player.sessionId);
|
||||||
|
if (!exists) {
|
||||||
players.value.push({
|
players.value.push({
|
||||||
sessionId: player.sessionId,
|
sessionId: player.sessionId,
|
||||||
name: player.name,
|
name: player.name,
|
||||||
clicks: player.clicks,
|
clicks: player.clicks,
|
||||||
connected: player.connected
|
connected: player.connected
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$(player).listen("clicks", (value: number) => {
|
$(player).listen("clicks", (value: number) => {
|
||||||
const p = players.value.find(p => p.sessionId === player.sessionId);
|
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", () => {
|
room.onMessage("gameStart", () => {
|
||||||
console.log("Game started!");
|
console.log("Game started!");
|
||||||
|
gameStatus.value = 'playing';
|
||||||
});
|
});
|
||||||
|
|
||||||
room.onMessage("gameEnd", (data) => {
|
room.onMessage("gameEnd", (data) => {
|
||||||
console.log("Game ended!", data);
|
console.log("Game ended!", data);
|
||||||
|
gameStatus.value = 'finished';
|
||||||
});
|
});
|
||||||
|
|
||||||
room.onMessage("gamePaused", () => {
|
room.onMessage("gamePaused", () => {
|
||||||
console.log("Game paused");
|
console.log("Game paused");
|
||||||
|
gameStatus.value = 'paused';
|
||||||
});
|
});
|
||||||
|
|
||||||
room.onMessage("gameRestart", () => {
|
room.onMessage("gameRestart", () => {
|
||||||
console.log("Game restarting");
|
console.log("Game restarting");
|
||||||
players.value.forEach(p => p.clicks = 0);
|
players.value.forEach(p => p.clicks = 0);
|
||||||
|
gameStatus.value = 'waiting';
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -180,7 +225,7 @@ function formatTime(seconds: number): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function leaveGame() {
|
function leaveGame() {
|
||||||
colyseusService.leaveCurrentRoom();
|
colyseusService.leaveGame();
|
||||||
router.push('/');
|
router.push('/');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -131,7 +131,8 @@ onMounted(async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
colyseusService.leaveCurrentRoom();
|
// Don't leave the current room - it might be a game room now
|
||||||
|
console.log('Lobby component unmounting...');
|
||||||
});
|
});
|
||||||
|
|
||||||
async function updateName() {
|
async function updateName() {
|
||||||
@@ -143,9 +144,21 @@ async function updateName() {
|
|||||||
|
|
||||||
async function handleQuickPlay() {
|
async function handleQuickPlay() {
|
||||||
isJoining.value = true;
|
isJoining.value = true;
|
||||||
|
console.log('Starting quickPlay...');
|
||||||
try {
|
try {
|
||||||
await colyseusService.quickPlay();
|
const gameRoom = await colyseusService.quickPlay();
|
||||||
router.push('/game');
|
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) {
|
} catch (error) {
|
||||||
console.error('Failed to join game:', error);
|
console.error('Failed to join game:', error);
|
||||||
isJoining.value = false;
|
isJoining.value = false;
|
||||||
@@ -155,7 +168,13 @@ async function handleQuickPlay() {
|
|||||||
async function joinRoom(roomId: string) {
|
async function joinRoom(roomId: string) {
|
||||||
isJoining.value = true;
|
isJoining.value = true;
|
||||||
try {
|
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');
|
router.push('/game');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to join room:', error);
|
console.error('Failed to join room:', error);
|
||||||
|
|||||||
@@ -34,16 +34,16 @@ export class GameRoom extends Room<GameState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onJoin(client: Client, options: any) {
|
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 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", {
|
client.send("playerInfo", {
|
||||||
sessionId: client.sessionId,
|
sessionId: client.sessionId,
|
||||||
name: uniqueName,
|
name: playerName,
|
||||||
roomId: this.roomId
|
roomId: this.roomId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ export class GameRoom extends Room<GameState> {
|
|||||||
const player = this.state.players.get(client.sessionId);
|
const player = this.state.players.get(client.sessionId);
|
||||||
if (player) {
|
if (player) {
|
||||||
player.connected = false;
|
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) {
|
if (this.state.gameStatus === GameStatus.PLAYING) {
|
||||||
@@ -90,9 +90,7 @@ export class GameRoom extends Room<GameState> {
|
|||||||
clearInterval(this.gameInterval);
|
clearInterval(this.gameInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state.players.forEach(player => {
|
// Don't release names here - they're managed by the LobbyRoom
|
||||||
NameManager.getInstance().releasePlayerName(player.sessionId);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private startGame() {
|
private startGame() {
|
||||||
|
|||||||
@@ -85,20 +85,30 @@ export class LobbyRoom extends Room<LobbyState> {
|
|||||||
if (!player || player.inGame) return;
|
if (!player || player.inGame) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const reservation = await matchMaker.joinOrCreate("game", {
|
// First try to find an available room
|
||||||
playerName: player.name
|
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);
|
this.state.setPlayerInGame(client.sessionId, true);
|
||||||
|
|
||||||
client.send("roomReservation", {
|
// Send the roomId to the client
|
||||||
sessionId: reservation.sessionId,
|
client.send("gameJoined", {
|
||||||
room: reservation.room
|
roomId: targetRoomId
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
// Don't auto-leave, let the client handle it
|
||||||
client.leave();
|
// The client will leave the lobby after successfully joining the game room
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[LobbyRoom] Error in quick play:", error);
|
console.error("[LobbyRoom] Error in quick play:", error);
|
||||||
@@ -113,20 +123,22 @@ export class LobbyRoom extends Room<LobbyState> {
|
|||||||
if (!player || player.inGame) return;
|
if (!player || player.inGame) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const reservation = await matchMaker.joinById(roomId, {
|
// Verify the room exists and is available
|
||||||
playerName: player.name
|
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);
|
this.state.setPlayerInGame(client.sessionId, true);
|
||||||
|
|
||||||
client.send("roomReservation", {
|
// Send the roomId to the client
|
||||||
sessionId: reservation.sessionId,
|
client.send("gameJoined", {
|
||||||
room: reservation.room
|
roomId: roomId
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
// Don't auto-leave, let the client handle it
|
||||||
client.leave();
|
// The client will leave the lobby after successfully joining the game room
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[LobbyRoom] Error joining room:", error);
|
console.error("[LobbyRoom] Error joining room:", error);
|
||||||
|
|||||||
Reference in New Issue
Block a user