UNPKG

jsp-raknet

Version:

Basic RakNet implementation written in Javascript

182 lines (181 loc) 7.43 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Server = void 0; const dgram_1 = __importDefault(require("dgram")); const crypto_1 = __importDefault(require("crypto")); const events_1 = __importDefault(require("events")); const Connection_1 = require("./Connection"); const Identifiers_1 = __importDefault(require("./protocol/Identifiers")); const UnconnectedPong_1 = __importDefault(require("./protocol/UnconnectedPong")); const OpenConnectionRequest1_1 = __importDefault(require("./protocol/OpenConnectionRequest1")); const OpenConnectionRequest2_1 = __importDefault(require("./protocol/OpenConnectionRequest2")); const IncompatibleProtocolVersion_1 = __importDefault(require("./protocol/IncompatibleProtocolVersion")); const OpenConnectionReply1_1 = __importDefault(require("./protocol/OpenConnectionReply1")); const OpenConnectionReply2_1 = __importDefault(require("./protocol/OpenConnectionReply2")); const UnconnectedPing_1 = __importDefault(require("./protocol/UnconnectedPing")); const InetAddress_1 = __importDefault(require("./utils/InetAddress")); const debug = require('debug')('raknet'); // RakNet protocol versions const RAKNET_PROTOCOL = 10; const RAKNET_TPS = 100; const RAKNET_TICK_LENGTH = 1 / RAKNET_TPS; class Server extends events_1.default { constructor(hostname, port, serverName) { super(); this.server = true; this.client = false; this.running = true; this.connections = new Map(); this.serverName = serverName; this.serverId = crypto_1.default.randomBytes(8).readBigInt64BE(0); this.hostname = hostname; this.port = port; this.inLog = (...args) => debug('S -> ', hostname, ...args); this.outLog = (...args) => debug('S <- ', hostname, ...args); } async listen() { this.socket = dgram_1.default.createSocket({ type: 'udp4', recvBufferSize: 1024 * 256 * 2, sendBufferSize: 1024 * 16 }); this.serverName.serverId = this.serverId.toString(); this.socket.on('message', (buffer, rinfo) => { this.inLog('[C->S]', buffer, rinfo); const sender = new InetAddress_1.default(rinfo.address, rinfo.port); this.handle(buffer, sender); }); await new Promise((resolve, reject) => { const failFn = e => reject(e); this.socket.once('error', failFn); this.socket.bind(this.port, this.hostname, () => { this.socket.removeListener('error', failFn); resolve(true); }); }); this.startTicking(); // tick sessions return this; } handle(buffer, sender) { const header = buffer.readUInt8(); // Read packet header to recognize packet type if (this.connections.has(sender.hash)) { const connection = this.connections.get(sender.hash); connection.recieve(buffer); } else { // Offline switch (header) { case Identifiers_1.default.UnconnectedPing: this.sendBuffer(this.handleUnconnectedPing(buffer), sender); break; case Identifiers_1.default.OpenConnectionRequest1: this.sendBuffer(this.handleOpenConnectionRequest1(buffer), sender); break; case Identifiers_1.default.OpenConnectionRequest2: this.sendBuffer(this.handleOpenConnectionRequest2(buffer, sender), sender); break; } } } sendBuffer(sendBuffer, client) { this.outLog('<- ', sendBuffer, client); this.socket.send(sendBuffer, 0, sendBuffer.length, client.port, client.address); } setPongAdvertisement(ad) { this.serverName = ad; } handleUnconnectedPing(buffer) { // Decode server packet const decodedPacket = new UnconnectedPing_1.default(); decodedPacket.buffer = buffer; decodedPacket.decode(); // Check packet validity // To refactor if (!decodedPacket.isValid()) { throw new Error('Received an invalid offline message'); } // Encode response const packet = new UnconnectedPong_1.default(); packet.sendTimestamp = decodedPacket.sendTimestamp; packet.serverGUID = this.serverId; let serverQuery = this.serverName; this.emit('unconnectedPong', serverQuery); packet.serverName = serverQuery.toString(); packet.encode(); return packet.buffer; } handleOpenConnectionRequest1(buffer) { // Decode server packet const decodedPacket = new OpenConnectionRequest1_1.default(); decodedPacket.buffer = buffer; decodedPacket.decode(); // Check packet validity // To refactor if (!decodedPacket.isValid()) { throw new Error('Received an invalid offline message'); } if (decodedPacket.protocol !== RAKNET_PROTOCOL) { const packet = new IncompatibleProtocolVersion_1.default(); packet.protocol = RAKNET_PROTOCOL; packet.serverGUID = this.serverId; packet.encode(); return packet.buffer; } // Encode response const packet = new OpenConnectionReply1_1.default(); packet.serverGUID = this.serverId; packet.mtuSize = Math.min(decodedPacket.mtuSize, 1400); packet.encode(); return packet.buffer; } handleOpenConnectionRequest2(buffer, address) { // Decode server packet const decodedPacket = new OpenConnectionRequest2_1.default(); decodedPacket.buffer = buffer; decodedPacket.decode(); // Check packet validity // To refactor if (!decodedPacket.isValid()) { throw new Error('Received an invalid offline message'); } // Encode response const packet = new OpenConnectionReply2_1.default(); packet.serverGUID = this.serverId; packet.mtuSize = Math.min(decodedPacket.mtuSize, 1400); packet.clientAddress = address; packet.encode(); // Create a session const conn = new Connection_1.Connection(this, decodedPacket.mtuSize, address); conn.inLog = this.inLog; conn.outLog = this.outLog; this.connections.set(address.hash, conn); return packet.buffer; } startTicking() { const int = setInterval(() => { if (this.running) { for (const [_, connection] of this.connections) { connection.update(Date.now()); } } else { this.emit('closing'); clearInterval(int); } }, RAKNET_TICK_LENGTH * 1000); } close() { if (!this.running) return; this.running = false; // Wait some time for final packets to go through setTimeout(() => { for (const [k, v] of this.connections) { v.close(); } this.socket.close(() => { this.emit('close'); this.removeAllListeners(); }); }, 100); } } exports.Server = Server;