import { createServer, Server as SocketServer, Socket } from 'node:net'; import Connection from '@/core/interface/networking/Connection'; import Logger from '@/core/infra/logger/Logger'; import { PacketMapValue } from '@/core/interface/networking/packets/Packets'; import { GameConfig } from '@/game/infra/config/GameConfig'; import { ConnectionStateEnum } from '@/core/enum/ConnectionStateEnum'; export default abstract class Server { protected server!: SocketServer; protected readonly connections = new Map(); protected readonly logger: Logger; protected readonly config: GameConfig; protected readonly container: any; protected readonly packets: Map>; protected isShuttingDown = false; constructor(container: { logger: Logger; config: GameConfig; packets: Map> }) { this.logger = container.logger; this.config = container.config; this.container = container; this.packets = container.packets; } setup() { this.server = createServer(this.onListener.bind(this)); return this; } abstract createConnection(socket: Socket): Connection; abstract onData(connection: Connection, data: Buffer): Promise; onListener(socket: Socket) { socket.setNoDelay(true); const connection = this.createConnection(socket); this.connections.set(connection.getId(), connection); this.logger.debug(`[IN][CONNECT SOCKET EVENT] New connection: ID: ${connection.getId()}`); connection.startHandShake(); socket.on('close', this.onClose.bind(this, connection)); socket.on('data', this.onData.bind(this, connection)); socket.on('error', this.onError.bind(this, connection)); } async onError(connection: Connection, err: Error) { this.logger.debug(`[IN][ERROR SOCKET EVENT] Closing connection: ID: ${connection.getId()}`); this.logger.debug(`[IN][ERROR SOCKET EVENT] Error ${err.message || err}`); connection.setState(ConnectionStateEnum.CLOSE); } async onClose(connection: Connection) { this.logger.debug(`[IN][CLOSE SOCKET EVENT] Closing connection: ID: ${connection.getId()}`); this.connections.delete(connection.getId()); } start(): Promise { return new Promise((resolve) => { this.server.listen(this.config.SERVER_PORT, Number(this.config.SERVER_ADDRESS), () => { this.logger.info(`Server running on: ${this.config.SERVER_ADDRESS}:${this.config.SERVER_PORT} 🔥 `); resolve(); }); }); } async close(): Promise { if (!this.server.listening || this.isShuttingDown) return; this.isShuttingDown = true; return new Promise((resolve, reject) => { this.server.close((err: Error | undefined) => { if (err) { this.logger.error('[SERVER] Error when try to close server:', err); reject(err); } else { this.logger.info('[SERVER] Server closed with success.'); resolve(); } }); }); } }