const logger = require('../utils/logger'); const { getGameSystem } = require('../systems/GameSystem'); const Player = require('../models/Player'); class SocketHandlers { constructor(io) { this.io = io; this.connectedUsers = new Map(); // userId -> socket.id this.userSockets = new Map(); // socket.id -> userId } handleConnection(socket) { logger.info(`Client connected: ${socket.id}`); // Authentication socket.on('authenticate', async (token) => { try { const jwt = require('jsonwebtoken'); const decoded = jwt.verify(token, process.env.JWT_SECRET || 'fallback_secret'); const player = await Player.findOne({ userId: decoded.userId }); if (!player) { socket.emit('auth_error', { error: 'Player not found' }); return; } // Store user connection this.connectedUsers.set(decoded.userId, socket.id); this.userSockets.set(socket.id, decoded.userId); socket.userId = decoded.userId; socket.emit('authenticated', { userId: decoded.userId }); logger.info(`User authenticated: ${decoded.userId}`); // Join user to their current server if any if (player.currentServer) { socket.join(player.currentServer); this.broadcastToServer(player.currentServer, 'user_joined', { userId: decoded.userId, username: player.username }); } } catch (error) { logger.error('Authentication error:', error); socket.emit('auth_error', { error: 'Invalid token' }); } }); // Server management socket.on('join_server', async (data) => { try { if (!socket.userId) { socket.emit('error', { error: 'Not authenticated' }); return; } const gameSystem = getGameSystem(); const server = await gameSystem.joinServer(data.serverId, socket.userId); // Update player's current server await Player.findOneAndUpdate( { userId: socket.userId }, { currentServer: data.serverId } ); // Join socket room socket.join(data.serverId); socket.emit('server_joined', { server }); this.broadcastToServer(data.serverId, 'user_joined', { userId: socket.userId, serverId: data.serverId }); logger.info(`User ${socket.userId} joined server ${data.serverId}`); } catch (error) { logger.error('Error joining server:', error); socket.emit('error', { error: error.message }); } }); socket.on('leave_server', async (data) => { try { if (!socket.userId) { socket.emit('error', { error: 'Not authenticated' }); return; } const gameSystem = getGameSystem(); const server = await gameSystem.leaveServer(data.serverId, socket.userId); // Update player's current server await Player.findOneAndUpdate( { userId: socket.userId }, { currentServer: null } ); // Leave socket room socket.leave(data.serverId); socket.emit('server_left', { server }); this.broadcastToServer(data.serverId, 'user_left', { userId: socket.userId, serverId: data.serverId }); logger.info(`User ${socket.userId} left server ${data.serverId}`); } catch (error) { logger.error('Error leaving server:', error); socket.emit('error', { error: error.message }); } }); // Game actions socket.on('game_action', async (data) => { try { if (!socket.userId) { socket.emit('error', { error: 'Not authenticated' }); return; } const gameSystem = getGameSystem(); const result = await gameSystem.processGameAction(socket.userId, data); socket.emit('action_result', { action: data.type, result }); // Broadcast relevant actions to server if (data.broadcast && socket.userId) { const player = await Player.findOne({ userId: socket.userId }); if (player && player.currentServer) { this.broadcastToServer(player.currentServer, 'user_action', { userId: socket.userId, username: player.username, action: data.type, result }); } } } catch (error) { logger.error('Error processing game action:', error); socket.emit('error', { error: error.message }); } }); // Chat functionality socket.on('send_message', async (data) => { try { if (!socket.userId) { socket.emit('error', { error: 'Not authenticated' }); return; } const player = await Player.findOne({ userId: socket.userId }); if (!player || !player.currentServer) { socket.emit('error', { error: 'Not in a server' }); return; } const message = { userId: socket.userId, username: player.username, message: data.message, timestamp: new Date(), type: data.type || 'chat' }; // Broadcast to server this.broadcastToServer(player.currentServer, 'new_message', message); logger.info(`Chat message from ${socket.userId} in server ${player.currentServer}`); } catch (error) { logger.error('Error sending message:', error); socket.emit('error', { error: error.message }); } }); // Real-time updates socket.on('request_server_status', async () => { try { if (!socket.userId) { socket.emit('error', { error: 'Not authenticated' }); return; } const player = await Player.findOne({ userId: socket.userId }); if (!player || !player.currentServer) { socket.emit('server_status', { server: null }); return; } const gameSystem = getGameSystem(); const server = gameSystem.servers.get(player.currentServer); if (server) { const players = await Player.find({ userId: { $in: server.players } }).select('userId username info.stats.level'); socket.emit('server_status', { server: { id: server.id, name: server.name, currentPlayers: server.players.length, maxPlayers: server.maxPlayers, players: players.map(p => ({ userId: p.userId, username: p.username, level: p.info.stats.level })) } }); } } catch (error) { logger.error('Error getting server status:', error); socket.emit('error', { error: error.message }); } }); // Disconnection socket.on('disconnect', async () => { logger.info(`Client disconnected: ${socket.id}`); const userId = this.userSockets.get(socket.id); if (userId) { // Remove from tracking this.connectedUsers.delete(userId); this.userSockets.delete(socket.id); // Notify server if user was in one const player = await Player.findOne({ userId }); if (player && player.currentServer) { this.broadcastToServer(player.currentServer, 'user_disconnected', { userId, username: player.username }); } } }); } broadcastToServer(serverId, event, data) { this.io.to(serverId).emit(event, data); } sendToUser(userId, event, data) { const socketId = this.connectedUsers.get(userId); if (socketId) { this.io.to(socketId).emit(event, data); } } broadcastToAll(event, data) { this.io.emit(event, data); } getConnectedUsers() { return Array.from(this.connectedUsers.keys()); } getUserCount() { return this.connectedUsers.size; } } module.exports = SocketHandlers;