ToneGuessr/server/handler/connection.js
2025-03-01 17:17:00 +01:00

379 lines
14 KiB
JavaScript

const roomController = require("../controller/room");
const gameController = require("../controller/game");
const youtubeService = require('../services/youtubeService');
module.exports = (io) => (socket) => {
let currentRoomId = null;
let currentUser = null;
let phaseTimers = {};
const clearRoomTimers = (roomId) => {
if (phaseTimers[roomId]) {
console.log(`Clearing timer for room ${roomId}`);
clearTimeout(phaseTimers[roomId]);
delete phaseTimers[roomId];
}
};
const startPhaseTimer = (roomId) => {
clearRoomTimers(roomId);
const gameState = gameController.getGameState(roomId);
if (!gameState) {
console.error(`Cannot start timer: no game state for room ${roomId}`);
return;
}
const timeRemaining = gameController.getTimeRemaining(roomId) * 1000;
console.log(`Starting ${gameState.phase} phase timer for room ${roomId} with ${timeRemaining}ms`);
phaseTimers[roomId] = setTimeout(() => {
console.log(`Timer expired for room ${roomId}, advancing phase from ${gameState.phase}`);
const advanced = gameController.advancePhase(roomId);
if (!advanced) {
console.log(`Failed to advance phase for room ${roomId}`);
return;
}
if (typeof advanced === 'boolean') {
handleRoundStart(roomId);
} else {
const newPhase = gameController.getGameState(roomId).phase;
console.log(`Advanced to ${newPhase} phase in room ${roomId}`);
if (newPhase === 'guessing') {
handleGuessingPhaseStart(roomId);
} else if (newPhase === 'results') {
handleResultsPhaseStart(roomId);
}
}
}, timeRemaining);
};
const handleRoundStart = (roomId) => {
const roles = gameController.getRoles(roomId);
const selectedSong = gameController.getSelectedSong(roomId);
const timeLeft = gameController.getTimeRemaining(roomId);
const songOptions = gameController.getSongOptions(roomId);
io.to(roomId).emit('roles-assigned', roles);
Object.entries(roles).forEach(([userId, role]) => {
if (role === 'composer') {
io.to(userId).emit('song-selected', selectedSong);
} else {
io.to(userId).emit('song-options', { songOptions });
}
});
io.to(roomId).emit('round-started', {
round: gameController.getRoundResults(roomId).round,
timeRemaining: timeLeft,
phase: 'composing'
});
startPhaseTimer(roomId);
};
const handleGuessingPhaseStart = (roomId) => {
const gameState = gameController.getGameState(roomId);
if (!gameState) return;
const roles = gameController.getRoles(roomId);
const songOptions = gameController.getSongOptions(roomId);
const timeLeft = gameController.getTimeRemaining(roomId);
console.log(`Starting guessing phase for room ${roomId} with ${Object.keys(roles).length} players`);
io.to(roomId).emit('phase-changed', {
phase: 'guessing',
timeRemaining: timeLeft,
message: 'Time to submit your final answer!'
});
Object.entries(roles).forEach(([userId, role]) => {
if (role === 'guesser') {
io.to(userId).emit('guessing-phase-started', {
timeRemaining: timeLeft,
songOptions
});
}
});
startPhaseTimer(roomId);
};
const handleResultsPhaseStart = (roomId) => {
const results = gameController.getRoundResults(roomId);
io.to(roomId).emit('round-results', results);
};
socket.on("disconnect", () => {
const roomId = roomController.getUserRoom(socket.id);
if (roomId) {
socket.to(roomId).emit("user-disconnected", socket.id);
roomController.disconnectUser(socket.id);
}
});
socket.on("join-room", async ({roomId, name}) => {
if (currentRoomId) return socket.emit("already-in-room", currentRoomId);
roomId = roomId.toString().toUpperCase();
if (roomController.roomExists(roomId)) {
if (!roomController.isRoomOpen(roomId)) {
return socket.emit("room-closed", roomId);
}
currentUser = {id: socket.id, name: name.toString()};
await roomController.connectUserToRoom(roomId, currentUser);
socket.join(roomId);
const users = roomController.getRoomUsers(roomId);
const votes = roomController.getPlaylistVotes(roomId);
const availablePlaylists = roomController.getAvailablePlaylists(roomId);
socket.emit("room-joined", roomId);
socket.emit("room-users", users);
socket.emit("playlist-votes-updated", votes);
try {
const details = await youtubeService.getPlaylistDetails(availablePlaylists);
Object.keys(details).forEach(genre => {
const playlistId = details[genre].id;
details[genre].votes = votes[playlistId]?.length || 0;
});
socket.emit("playlist-options", details);
} catch (error) {
console.error("Error sending playlist details to new user:", error);
socket.emit("error", {
message: "Failed to load playlists",
details: error.message
});
}
socket.to(roomId).emit("user-connected", currentUser);
io.to(roomId).emit("room-users-update", users);
currentRoomId = roomId;
} else {
socket.emit("room-not-found", roomId);
}
});
socket.on("create-room", ({name}) => {
if (!name) return socket.emit("room-name-required");
const roomId = Math.random().toString(36).substring(7).toUpperCase();
currentUser = {id: socket.id, name: name?.toString(), creator: true};
roomController.connectUserToRoom(roomId, currentUser);
socket.join(roomId);
socket.emit("room-created", roomId);
currentRoomId = roomId;
});
socket.on("start-game", async () => {
const roomId = roomController.getUserRoom(socket.id);
if (!roomId || !roomController.isUserHost(socket.id)) {
return socket.emit("not-authorized");
}
roomController.validateRoomMembers(io, roomId);
const users = roomController.getRoomUsers(roomId);
if (users.length < 2) {
return socket.emit("error", { message: "At least 2 players are required" });
}
if (!roomController.startGame(roomId)) return;
gameController.initializeGameState(roomId);
try {
const success = await gameController.startNewRound(roomId);
if (!success) {
return socket.emit("error", { message: "Failed to start game - could not load songs" });
}
io.to(roomId).emit("game-started");
handleRoundStart(roomId);
} catch (error) {
console.error("Error starting game:", error);
socket.emit("error", { message: "Failed to start game due to an error" });
}
});
socket.on("send-message", (messageData) => {
const roomId = roomController.getUserRoom(socket.id);
if (!roomId) return;
const serverUsername = roomController.getUserName(socket.id);
if (!messageData.sender || messageData.sender === "Player" || messageData.sender === "Anonymous") {
if (serverUsername) {
console.log(`Fixing missing username for ${socket.id}: using "${serverUsername}" instead of "${messageData.sender || 'none'}"`);
messageData.sender = serverUsername;
} else {
console.warn(`Could not find username for user ${socket.id}`);
}
}
socket.to(roomId).emit("chat-message", messageData);
socket.emit("chat-message-confirmation", {
...messageData,
sender: messageData.sender
});
});
socket.on("get-user-info", () => {
if (currentUser) socket.emit("user-info", currentUser);
});
socket.on("get-room-users", () => {
const roomId = roomController.getUserRoom(socket.id);
if (roomId) {
const users = roomController.getRoomUsers(roomId);
socket.emit("room-users", users);
}
});
socket.on("check-host-status", () => {
socket.emit("host-status", { isHost: roomController.isUserHost(socket.id) });
});
socket.on("get-room-code", () => {
if (currentRoomId) socket.emit("room-code", currentRoomId);
});
socket.on("submit-frequency", ({ frequency, isPlaying }) => {
const roomId = roomController.getUserRoom(socket.id);
if (!roomId) return;
const userRole = gameController.getUserRole(roomId, socket.id);
if (userRole !== 'composer') return;
if (gameController.updateFrequency(roomId, frequency)) {
socket.to(roomId).emit("frequency-update", { frequency, isPlaying });
}
});
socket.on("submit-guess", ({ songId }) => {
const roomId = roomController.getUserRoom(socket.id);
if (!roomId) return;
console.log(`User ${socket.id} submitted guess: Song ID ${songId}`);
const gamePhase = gameController.getGameState(roomId)?.phase;
if (gamePhase !== 'guessing') {
console.log(`Ignoring guess: room ${roomId} is in ${gamePhase} phase, not guessing`);
return;
}
const result = gameController.submitGuess(roomId, socket.id, songId);
if (result) {
console.log(`Guess result for ${socket.id}:`, result);
socket.emit("guess-result", result);
const currentComposer = gameController.getCurrentComposer(roomId);
if (currentComposer) {
const guesserName = roomController.getUserName(socket.id) || "Someone";
io.to(currentComposer).emit("player-guessed", {
guesserName,
isCorrect: result.isCorrect
});
}
}
});
socket.on("next-round", async () => {
const roomId = roomController.getUserRoom(socket.id);
if (!roomId || !roomController.isUserHost(socket.id)) return;
roomController.validateRoomMembers(io, roomId);
const users = roomController.getRoomUsers(roomId);
if (users.length < 2) {
return socket.emit("error", { message: "At least 2 players are required" });
}
try {
const success = await gameController.startNewRound(roomId);
if (success) {
handleRoundStart(roomId);
} else {
socket.emit("error", { message: "Failed to start next round" });
}
} catch (error) {
console.error("Error starting next round:", error);
socket.emit("error", { message: "Failed to start next round due to an error" });
}
});
socket.on("get-current-frequency", () => {
const roomId = roomController.getUserRoom(socket.id);
if (roomId) {
socket.emit("current-frequency", {
frequency: gameController.getCurrentFrequency(roomId)
});
}
});
socket.on("get-playlist-songs", async () => {
try {
const roomId = roomController.getUserRoom(socket.id);
if (!roomId) {
throw new Error("User not in a room");
}
const gameState = gameController.getGameState(roomId);
const playlistId = gameState?.selectedPlaylist || null;
console.log(`Fetching songs for playlist ${playlistId}`);
const songs = await youtubeService.fetchPlaylistSongs(playlistId);
socket.emit("playlist-songs", { songs });
} catch (error) {
console.error("Error sending playlist songs:", error);
socket.emit("playlist-songs", { songs: [] });
}
});
socket.on("get-playlist-options", async () => {
try {
const roomId = roomController.getUserRoom(socket.id);
if (!roomId) {
throw new Error("User not in a room");
}
const availablePlaylists = roomController.getAvailablePlaylists(roomId);
const details = await youtubeService.getPlaylistDetails(availablePlaylists);
if (Object.keys(details).length === 0) {
const newPlaylists = await youtubeService.getRandomPlaylists(3);
const newDetails = await youtubeService.getPlaylistDetails(newPlaylists);
roomController.updateAvailablePlaylists(roomId, newPlaylists);
socket.emit("playlist-options", newDetails);
} else {
socket.emit("playlist-options", details);
}
} catch (error) {
console.error("Error fetching playlist options:", error);
socket.emit("error", {
message: "Failed to load playlists",
details: error.message
});
}
});
socket.on("vote-playlist", ({ playlistId }) => {
const roomId = roomController.getUserRoom(socket.id);
if (!roomId) return;
if (roomController.voteForPlaylist(roomId, socket.id, playlistId)) {
const votes = roomController.getPlaylistVotes(roomId);
io.to(roomId).emit("playlist-votes-updated", votes);
}
});
};