/** * Socket Client Module * Manages WebSocket communication with the game server. */ class SocketClient { constructor(serverUrl) { this.socket = null; this.serverUrl = serverUrl || window.location.origin; // Default to current origin this.eventListeners = { connect: [], disconnect: [], connect_error: [], gameIntroduction: [], narrativeResponse: [], gameSaved: [], gameLoaded: [], error: [], }; } /** * Connects to the WebSocket server. */ connect() { if (this.socket && this.socket.connected) { console.log('SocketClient: Already connected.'); return; } console.log(`SocketClient: Connecting to ${this.serverUrl}...`); // Ensure io is available (it should be loaded globally) if (typeof io === 'undefined') { console.error('Socket.IO client library (io) not found. Make sure it is loaded.'); this.triggerEvent('error', { message: 'Socket.IO library not loaded.' }); return; } this.socket = io(this.serverUrl, { reconnectionAttempts: 5, timeout: 10000, }); this.initializeSocketEventHandlers(); } /** * Disconnects from the server. */ disconnect() { if (this.socket) { console.log('SocketClient: Disconnecting...'); this.socket.disconnect(); } } /** * Checks if the client is currently connected. * @returns {boolean} True if connected, false otherwise. */ isConnected() { return this.socket && this.socket.connected; } /** * Sets up the listeners for standard socket events. */ initializeSocketEventHandlers() { if (!this.socket) return; this.socket.on('connect', () => { console.log('SocketClient: Connected to server.'); this.triggerEvent('connect'); }); this.socket.on('disconnect', (reason) => { console.log(`SocketClient: Disconnected from server. Reason: ${reason}`); this.triggerEvent('disconnect', reason); }); this.socket.on('connect_error', (error) => { console.error('SocketClient: Connection error:', error); this.triggerEvent('connect_error', error); }); // --- Game-specific events --- this.socket.on('gameIntroduction', (data) => { console.log('SocketClient: Received gameIntroduction'); this.triggerEvent('gameIntroduction', data); }); this.socket.on('narrativeResponse', (data) => { console.log('SocketClient: Received narrativeResponse'); this.triggerEvent('narrativeResponse', data); }); this.socket.on('gameSaved', (data) => { console.log('SocketClient: Received gameSaved confirmation'); this.triggerEvent('gameSaved', data); // Pass data if any }); this.socket.on('gameLoaded', (data) => { console.log('SocketClient: Received gameLoaded confirmation'); this.triggerEvent('gameLoaded', data); }); this.socket.on('error', (data) => { console.error('SocketClient: Received error from server:', data); this.triggerEvent('error', data); }); } /** * Registers a listener for a specific event. * @param {string} eventName - The name of the event. * @param {function} callback - The function to call when the event occurs. */ on(eventName, callback) { if (this.eventListeners[eventName]) { this.eventListeners[eventName].push(callback); } else { console.warn(`SocketClient: Attempted to register listener for unknown event "${eventName}"`); } } /** * Triggers a specific event, calling all registered listeners. * @param {string} eventName - The name of the event. * @param {*} data - Data to pass to the listeners. */ triggerEvent(eventName, data) { if (this.eventListeners[eventName]) { this.eventListeners[eventName].forEach(callback => { try { callback(data); } catch (error) { console.error(`SocketClient: Error in event listener for "${eventName}":`, error); } }); } } /** * Emits an event to the server. * @param {string} eventName - The name of the event to emit. * @param {object} data - The data to send with the event. */ emit(eventName, data) { if (this.socket && this.socket.connected) { console.log(`SocketClient: Emitting "${eventName}"`, data || ''); this.socket.emit(eventName, data); } else { console.error(`SocketClient: Cannot emit "${eventName}", not connected.`); // Optionally trigger an error event or queue the message this.triggerEvent('error', { message: `Cannot send command "${eventName}", not connected.` }); } } // --- Convenience methods for game actions --- requestStartGame() { this.emit('startGame'); } sendCommand(command) { this.emit('playerCommand', { command }); } requestSaveGame() { this.emit('saveGame'); } requestLoadGame() { this.emit('loadGame'); } } // Export the class if using modules // export default SocketClient;