File size: 3,255 Bytes
eee16fe | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | 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<string, Connection>();
protected readonly logger: Logger;
protected readonly config: GameConfig;
protected readonly container: any;
protected readonly packets: Map<number, PacketMapValue<any>>;
protected isShuttingDown = false;
constructor(container: { logger: Logger; config: GameConfig; packets: Map<number, PacketMapValue<any>> }) {
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<void>;
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<void> {
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<void> {
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();
}
});
});
}
}
|