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();
                }
            });
        });
    }
}