// Network module - Socket.io client handling class NetworkManager { constructor() { this.socket = null; this.currentPlayerId = null; this.isSpectator = false; this.players = {}; this.spectators = {}; this.gameState = {}; this.listeners = { onPlayerJoined: null, onPlayerLeft: null, onGameStarted: null, onStateUpdate: null, onGameOver: null, onSpectatorJoined: null, onSpectatorLeft: null, onForcedSpectator: null }; } connect() { this.socket = io(); this.socket.on('connect', () => { console.log('Connected to server'); this.currentPlayerId = this.socket.id; }); this.socket.on('disconnect', () => { console.log('Disconnected from server'); }); this.socket.on('player-joined', ({ player, players }) => { this.updatePlayers(players); if (this.listeners.onPlayerJoined) { this.listeners.onPlayerJoined(player); } }); this.socket.on('player-left', ({ playerId, players }) => { delete this.players[playerId]; this.updatePlayers(players); if (this.listeners.onPlayerLeft) { this.listeners.onPlayerLeft(playerId); } }); this.socket.on('game-started', ({ players, states }) => { this.updatePlayers(players); this.updateStates(states); if (this.listeners.onGameStarted) { this.listeners.onGameStarted(players, states); } }); this.socket.on('state-update', (states) => { this.updateStates(states); if (this.listeners.onStateUpdate) { this.listeners.onStateUpdate(states); } }); this.socket.on('game-over', (data) => { if (this.listeners.onGameOver) { this.listeners.onGameOver(data); } }); // Forced spectator mode - game already in progress this.socket.on('forced-spectator', ({ spectatorId, states, players }) => { this.isSpectator = true; this.updateStates(states); this.updatePlayers(players); if (this.listeners.onForcedSpectator) { this.listeners.onForcedSpectator({ states, players }); } }); // Spectator joined this.socket.on('spectator-joined', ({ spectator, spectators }) => { this.updateSpectators(spectators); if (this.listeners.onSpectatorJoined) { this.listeners.onSpectatorJoined(spectator); } }); // Spectator left this.socket.on('spectator-left', ({ spectatorId, spectators }) => { delete this.spectators[spectatorId]; this.updateSpectators(spectators); if (this.listeners.onSpectatorLeft) { this.listeners.onSpectatorLeft(spectatorId); } }); } joinLobby(playerName) { if (!this.socket) return; this.socket.emit('join-lobby', { playerName }); } leaveLobby() { if (!this.socket) return; } ready() { if (!this.socket) return; this.socket.emit('ready'); } unready() { if (!this.socket) return; this.socket.emit('unready'); } sendMove(direction) { if (!this.socket || !this.currentPlayerId) return; this.socket.emit('player-move', { playerId: this.currentPlayerId, direction }); } sendRotate() { if (!this.socket || !this.currentPlayerId) return; this.socket.emit('player-rotate', { playerId: this.currentPlayerId }); } sendDrop() { if (!this.socket || !this.currentPlayerId) return; this.socket.emit('player-drop', { playerId: this.currentPlayerId, hard: false }); } sendHardDrop() { if (!this.socket || !this.currentPlayerId) return; this.socket.emit('player-drop', { playerId: this.currentPlayerId, hard: true }); } sendHold() { if (!this.socket || !this.currentPlayerId) return; this.socket.emit('player-hold', { playerId: this.currentPlayerId }); } sendZoneActivate() { if (!this.socket || !this.currentPlayerId) return; this.socket.emit('zone-activate', { playerId: this.currentPlayerId }); } updatePlayers(players) { this.players = {}; players.forEach(p => { this.players[p.id] = p; }); } updateStates(states) { states.forEach(s => { this.gameState[s.playerId] = s; }); } updateSpectators(spectators) { this.spectators = {}; spectators.forEach(s => { this.spectators[s.id] = s; }); } getPlayer(playerId) { return this.players[playerId]; } getGameState(playerId) { return this.gameState[playerId]; } getAllPlayers() { return this.players; } getAllGameStates() { return this.gameState; } getAllSpectators() { return this.spectators; } setListener(event, callback) { const listenerMap = { 'player-joined': 'onPlayerJoined', 'player-left': 'onPlayerLeft', 'game-started': 'onGameStarted', 'state-update': 'onStateUpdate', 'game-over': 'onGameOver', 'forced-spectator': 'onForcedSpectator', 'spectator-joined': 'onSpectatorJoined', 'spectator-left': 'onSpectatorLeft' }; if (callback) { this.listeners[listenerMap[event]] = callback; } } } const network = new NetworkManager();