331 lines
12 KiB
JavaScript
331 lines
12 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", ({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()};
|
|
roomController.connectUserToRoom(roomId, currentUser);
|
|
socket.join(roomId);
|
|
|
|
const users = roomController.getRoomUsers(roomId);
|
|
io.to(roomId).emit("room-users-update", users);
|
|
socket.to(roomId).emit("user-connected", currentUser);
|
|
|
|
socket.emit("room-joined", roomId);
|
|
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 songs = await youtubeService.fetchPlaylistSongs();
|
|
socket.emit("playlist-songs", { songs });
|
|
} catch (error) {
|
|
console.error("Error sending playlist songs:", error);
|
|
socket.emit("playlist-songs", { songs: youtubeService.getDefaultSongs() });
|
|
}
|
|
});
|
|
|
|
socket.on("get-playlist-options", async () => {
|
|
try {
|
|
const playlists = await youtubeService.getPlaylistDetails();
|
|
socket.emit("playlist-options", playlists);
|
|
} catch (error) {
|
|
console.error("Error fetching playlist options:", error);
|
|
socket.emit("error", { message: "Failed to load playlists" });
|
|
}
|
|
});
|
|
|
|
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);
|
|
}
|
|
});
|
|
}; |