Add screens and components for Song Battle game, including Home, Lobby, Voting, Results, and Song Submission screens; implement YouTube video embedding and styles
Some checks failed
Publish Docker image / Push Docker image to Docker Hub (push) Failing after 7m12s
Some checks failed
Publish Docker image / Push Docker image to Docker Hub (push) Failing after 7m12s
This commit is contained in:
325
server/index.js
325
server/index.js
@ -3,11 +3,12 @@ const { Server } = require("socket.io");
|
||||
const http = require("http");
|
||||
const app = express();
|
||||
const path = require("path");
|
||||
const GameManager = require("./game");
|
||||
|
||||
app.use(express.static(path.join(__dirname, './dist')));
|
||||
app.use(express.static(path.join(__dirname, '../client/dist')));
|
||||
app.disable("x-powered-by");
|
||||
|
||||
app.get('*', (req, res) => res.sendFile(path.join(__dirname, './dist', 'index.html')));
|
||||
app.get('*', (req, res) => res.sendFile(path.join(__dirname, '../client/dist', 'index.html')));
|
||||
|
||||
const server = http.createServer(app);
|
||||
|
||||
@ -17,6 +18,326 @@ const io = new Server(server, {
|
||||
pingInterval: 10000
|
||||
});
|
||||
|
||||
// Initialize game manager
|
||||
const gameManager = new GameManager();
|
||||
|
||||
// Socket.IO event handlers
|
||||
io.on('connection', (socket) => {
|
||||
console.log(`User connected: ${socket.id}`);
|
||||
|
||||
// Create a new game lobby
|
||||
socket.on('create_lobby', ({ playerName }, callback) => {
|
||||
try {
|
||||
const result = gameManager.createLobby(socket.id, playerName);
|
||||
|
||||
// Join the socket room for this lobby
|
||||
socket.join(result.lobbyId);
|
||||
|
||||
// Send response to client
|
||||
if (callback) callback(result);
|
||||
|
||||
console.log(`Lobby created: ${result.lobbyId} by ${playerName}`);
|
||||
} catch (error) {
|
||||
console.error('Error creating lobby:', error);
|
||||
if (callback) callback({ error: 'Failed to create lobby' });
|
||||
}
|
||||
});
|
||||
|
||||
// Join an existing lobby
|
||||
socket.on('join_lobby', ({ lobbyId, playerName }, callback) => {
|
||||
try {
|
||||
const result = gameManager.joinLobby(socket.id, playerName, lobbyId);
|
||||
|
||||
if (result.error) {
|
||||
if (callback) callback(result);
|
||||
return;
|
||||
}
|
||||
|
||||
// Join the socket room for this lobby
|
||||
socket.join(lobbyId);
|
||||
|
||||
// Notify all players in the lobby
|
||||
socket.to(lobbyId).emit('player_joined', {
|
||||
playerId: socket.id,
|
||||
playerName
|
||||
});
|
||||
|
||||
// Send response to client
|
||||
if (callback) callback(result);
|
||||
|
||||
console.log(`Player ${playerName} joined lobby ${lobbyId}`);
|
||||
} catch (error) {
|
||||
console.error('Error joining lobby:', error);
|
||||
if (callback) callback({ error: 'Failed to join lobby' });
|
||||
}
|
||||
});
|
||||
|
||||
// Attempt to reconnect to a lobby
|
||||
socket.on('reconnect_to_lobby', ({ lobbyId, playerName }, callback) => {
|
||||
try {
|
||||
const result = gameManager.handleReconnect(socket.id, lobbyId, playerName);
|
||||
|
||||
if (result.error) {
|
||||
if (callback) callback(result);
|
||||
return;
|
||||
}
|
||||
|
||||
// Join the socket room for this lobby
|
||||
socket.join(lobbyId);
|
||||
|
||||
// Notify all players in the lobby
|
||||
socket.to(lobbyId).emit('player_reconnected', {
|
||||
playerId: socket.id,
|
||||
playerName
|
||||
});
|
||||
|
||||
// Send response to client
|
||||
if (callback) callback(result);
|
||||
|
||||
console.log(`Player ${playerName} reconnected to lobby ${lobbyId}`);
|
||||
} catch (error) {
|
||||
console.error('Error reconnecting to lobby:', error);
|
||||
if (callback) callback({ error: 'Failed to reconnect to lobby' });
|
||||
}
|
||||
});
|
||||
|
||||
// Update lobby settings
|
||||
socket.on('update_settings', ({ settings }, callback) => {
|
||||
try {
|
||||
const result = gameManager.updateSettings(socket.id, settings);
|
||||
|
||||
if (result.error) {
|
||||
if (callback) callback(result);
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify all players in the lobby
|
||||
io.to(result.lobbyId).emit('settings_updated', {
|
||||
settings: result.lobby.settings
|
||||
});
|
||||
|
||||
// Send response to client
|
||||
if (callback) callback(result);
|
||||
} catch (error) {
|
||||
console.error('Error updating settings:', error);
|
||||
if (callback) callback({ error: 'Failed to update settings' });
|
||||
}
|
||||
});
|
||||
|
||||
// Start the game from lobby
|
||||
socket.on('start_game', (_, callback) => {
|
||||
try {
|
||||
const result = gameManager.startGame(socket.id);
|
||||
|
||||
if (result.error) {
|
||||
if (callback) callback(result);
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify all players in the lobby
|
||||
io.to(result.lobbyId).emit('game_started', {
|
||||
state: result.lobby.state
|
||||
});
|
||||
|
||||
// Send response to client
|
||||
if (callback) callback(result);
|
||||
|
||||
console.log(`Game started in lobby ${result.lobbyId}`);
|
||||
} catch (error) {
|
||||
console.error('Error starting game:', error);
|
||||
if (callback) callback({ error: 'Failed to start game' });
|
||||
}
|
||||
});
|
||||
|
||||
// Add a song
|
||||
socket.on('add_song', ({ song }, callback) => {
|
||||
try {
|
||||
console.log(`[DEBUG] Attempting to add song: ${JSON.stringify(song)}`);
|
||||
const result = gameManager.addSong(socket.id, song);
|
||||
|
||||
if (result.error) {
|
||||
console.log(`[DEBUG] Error adding song: ${result.error}`);
|
||||
if (callback) callback(result);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add better error handling to prevent crash if lobby is not defined
|
||||
if (!result.lobby || !result.lobbyId) {
|
||||
console.log(`[DEBUG] Warning: Song added but lobby information is missing`);
|
||||
if (callback) callback({ error: 'Failed to associate song with lobby' });
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`[DEBUG] Song added successfully, notifying lobby ${result.lobbyId}`);
|
||||
console.log(`[DEBUG] Lobby songs: ${JSON.stringify(result.lobby.songs.map(s => s.title))}`);
|
||||
|
||||
// Notify all players in the lobby about updated song count
|
||||
io.to(result.lobbyId).emit('songs_updated', {
|
||||
lobby: result.lobby
|
||||
});
|
||||
|
||||
// Send response to client
|
||||
if (callback) callback(result);
|
||||
} catch (error) {
|
||||
console.error('Error adding song:', error);
|
||||
if (callback) callback({ error: 'Failed to add song' });
|
||||
}
|
||||
});
|
||||
|
||||
// Remove a song
|
||||
socket.on('remove_song', ({ songId }, callback) => {
|
||||
try {
|
||||
const result = gameManager.removeSong(socket.id, songId);
|
||||
|
||||
if (result.error) {
|
||||
if (callback) callback(result);
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify all players in the lobby
|
||||
io.to(result.lobbyId).emit('songs_updated', {
|
||||
lobby: result.lobby
|
||||
});
|
||||
|
||||
// Send response to client
|
||||
if (callback) callback(result);
|
||||
} catch (error) {
|
||||
console.error('Error removing song:', error);
|
||||
if (callback) callback({ error: 'Failed to remove song' });
|
||||
}
|
||||
});
|
||||
|
||||
// Search YouTube for songs
|
||||
socket.on('search_youtube', async ({ query }, callback) => {
|
||||
try {
|
||||
if (!query || typeof query !== 'string') {
|
||||
if (callback) callback({ error: 'Invalid search query' });
|
||||
return;
|
||||
}
|
||||
|
||||
const results = await gameManager.searchYouTube(query);
|
||||
|
||||
// Send response to client
|
||||
if (callback) callback({ results });
|
||||
} catch (error) {
|
||||
console.error('Error searching YouTube:', error);
|
||||
if (callback) callback({ error: 'Failed to search YouTube' });
|
||||
}
|
||||
});
|
||||
|
||||
// Mark player as ready
|
||||
socket.on('player_ready', (_, callback) => {
|
||||
try {
|
||||
const result = gameManager.setPlayerReady(socket.id);
|
||||
|
||||
if (result.error) {
|
||||
if (callback) callback(result);
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify all players in the lobby
|
||||
io.to(result.lobbyId).emit('player_status_changed', {
|
||||
lobby: result.lobby
|
||||
});
|
||||
|
||||
// If the game state just changed to VOTING, notify about the first battle
|
||||
if (result.lobby.state === 'VOTING' && result.lobby.currentBattle) {
|
||||
console.log('Sending new_battle event to clients');
|
||||
io.to(result.lobbyId).emit('new_battle', {
|
||||
battle: result.lobby.currentBattle
|
||||
});
|
||||
}
|
||||
|
||||
// If tournament just started, explicitly notify about the state change and first battle
|
||||
if (result.tournamentStarted && result.lobby.currentBattle) {
|
||||
console.log('Tournament started, sending battle data');
|
||||
// First send state change
|
||||
io.to(result.lobbyId).emit('tournament_started', {
|
||||
state: 'VOTING'
|
||||
});
|
||||
|
||||
// Then send battle data with a slight delay to ensure clients process in the right order
|
||||
setTimeout(() => {
|
||||
io.to(result.lobbyId).emit('new_battle', {
|
||||
battle: result.lobby.currentBattle
|
||||
});
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// If game finished (edge case where only one song was submitted)
|
||||
if (result.lobby.state === 'FINISHED') {
|
||||
io.to(result.lobbyId).emit('game_finished', {
|
||||
winner: result.lobby.finalWinner,
|
||||
battles: result.lobby.battles
|
||||
});
|
||||
}
|
||||
|
||||
// Send response to client
|
||||
if (callback) callback(result);
|
||||
} catch (error) {
|
||||
console.error('Error setting player ready:', error);
|
||||
if (callback) callback({ error: 'Failed to set player status' });
|
||||
}
|
||||
});
|
||||
|
||||
// Submit a vote in a battle
|
||||
socket.on('submit_vote', ({ songId }, callback) => {
|
||||
try {
|
||||
const result = gameManager.submitVote(socket.id, songId);
|
||||
|
||||
if (result.error) {
|
||||
if (callback) callback(result);
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify all players about vote count
|
||||
io.to(result.lobbyId).emit('vote_submitted', {
|
||||
lobby: result.lobby
|
||||
});
|
||||
|
||||
// If battle is finished, notify about new battle
|
||||
if (result.lobby.currentBattle) {
|
||||
io.to(result.lobbyId).emit('new_battle', {
|
||||
battle: result.lobby.currentBattle
|
||||
});
|
||||
}
|
||||
|
||||
// If game is finished, notify about the winner
|
||||
if (result.lobby.state === 'FINISHED') {
|
||||
io.to(result.lobbyId).emit('game_finished', {
|
||||
winner: result.lobby.finalWinner,
|
||||
battles: result.lobby.battles
|
||||
});
|
||||
}
|
||||
|
||||
// Send response to client
|
||||
if (callback) callback(result);
|
||||
} catch (error) {
|
||||
console.error('Error submitting vote:', error);
|
||||
if (callback) callback({ error: 'Failed to submit vote' });
|
||||
}
|
||||
});
|
||||
|
||||
// Handle disconnection
|
||||
socket.on('disconnect', () => {
|
||||
try {
|
||||
const result = gameManager.handleDisconnect(socket.id);
|
||||
|
||||
if (result) {
|
||||
// Notify remaining players about the disconnection
|
||||
io.to(result.lobbyId).emit('player_disconnected', {
|
||||
playerId: socket.id,
|
||||
lobby: result.lobby
|
||||
});
|
||||
|
||||
console.log(`Player ${socket.id} disconnected from lobby ${result.lobbyId}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error handling disconnect:', error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
server.on('error', (error) => {
|
||||
console.error('Server error:', error);
|
||||
});
|
||||
|
Reference in New Issue
Block a user