UNPKG

@jsprismarine/raknet

Version:
118 lines (117 loc) 15.1 kB
import { BitFlags } from "./protocol/BitFlags.es.js"; import "./Constants.es.js"; import Session from "./Session.es.js"; import { OfflineHandler } from "./protocol/OfflineHandler.es.js"; import { EventEmitter } from "events"; import Dgram from "node:dgram"; //#region src/ServerSocket.ts var ServerSocket = class extends EventEmitter { maxConnections; onlineMode; serverName; logger; socket; guid; sessions = /* @__PURE__ */ new Set(); ticker; offlineHandler = new OfflineHandler(this); constructor(maxConnections, onlineMode, serverName, logger) { super(); this.maxConnections = maxConnections; this.onlineMode = onlineMode; this.serverName = serverName; this.logger = logger; this.socket = Dgram.createSocket("udp4").unref(); this.guid = Buffer.allocUnsafe(8).readBigInt64BE(); if (this.onlineMode) this.logger.warn("Online mode is currently not supported.", "RakNet/ServerSocket"); } start(address, port) { try { this.socket.bind(port, address); } catch (error) { if (error instanceof Error) { this.logger.error("Failed to bind socket, error message=%s", "RakNet/ServerSocket/Start"); this.logger.error(error, "RakNet/ServerSocket/Start"); } } const tick = () => setTimeout(() => { for (const session of this.sessions.values()) session.update(Date.now()); tick(); }, 10); this.ticker = tick(); this.ticker.unref(); this.socket.on("message", this.handleMessage.bind(this)); } handleMessage(msg, rinfo) { if ((msg[0] & BitFlags.VALID) === 0) this.offlineHandler.process(msg, rinfo); else { let session; if ((session = this.getSessionByAddress(rinfo)) !== null) session.handle(msg); else this.logger.debug(`Cannot handle Datagram for unconnected client=${rinfo.address}:${rinfo.port}, buffer=${msg}`, "RakNet/ServerSocket/handleMessage"); } } kill() { for (const session of this.getSessions()) session.sendFrameQueue(); clearTimeout(this.ticker); this.removeAllListeners(); this.socket.close(); } /** * Used to retrieve if we are overflowing the maximum * connections we can allow, value given in the constructor. * @returns {boolean} if we can hold new connections. */ allowIncomingConnections() { return this.sessions.size < this.maxConnections; } /** * Returns the maximum number of incoming connections. * @returns {number} the maximum connections we can allow. */ getMaxConnections() { return this.maxConnections; } /** * Sets the maximum number of allowed incoming connections. * @param {number} allowed - maximum number of connections. */ setMaxConnections(allowed) { this.maxConnections = allowed; } addSession(rinfo, mtuSize, incomingGuid) { this.sessions.add(new Session(this, mtuSize, rinfo, incomingGuid)); this.logger.verbose(`Session created for client=${rinfo.address}:${rinfo.port}, mtu=${mtuSize}`); this.serverName.setOnlinePlayerCount(this.sessions.size); } removeSession(session, reason) { if (!this.sessions.delete(session)) return; this.emit("closeConnection", session.getAddress(), reason); this.logger.verbose(`Closed session for client=${session.getAddress()}, reason=${reason}`); this.serverName.setOnlinePlayerCount(this.sessions.size); } getLogger() { return this.logger; } getSessions() { return Array.from(this.sessions.values()); } getSessionByAddress(rinfo) { return this.getSessions().find((session) => session.rinfo.address === rinfo.address && session.rinfo.port === rinfo.port) ?? null; } getSessionByGUID(guid) { return this.getSessions().find((session) => session.guid === guid) ?? null; } getServerGuid() { return this.guid; } sendPacket(packet, rinfo) { packet.encode(); this.sendBuffer(packet.getBuffer(), rinfo); } sendBuffer(buffer, rinfo) { this.socket.send(buffer, rinfo.port, rinfo.address); } }; //#endregion export { ServerSocket as default }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2VydmVyU29ja2V0LmVzLmpzIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uL3NyYy9TZXJ2ZXJTb2NrZXQudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRXZlbnRFbWl0dGVyIH0gZnJvbSAnZXZlbnRzJztcbmltcG9ydCBEZ3JhbSwgeyB0eXBlIFJlbW90ZUluZm8gfSBmcm9tICdub2RlOmRncmFtJztcbmltcG9ydCB7IFJBS05FVF9UUFMgfSBmcm9tICcuL0NvbnN0YW50cyc7XG5pbXBvcnQgUmFrTmV0U2Vzc2lvbiBmcm9tICcuL1Nlc3Npb24nO1xuaW1wb3J0IEJpdEZsYWdzIGZyb20gJy4vcHJvdG9jb2wvQml0RmxhZ3MnO1xuaW1wb3J0IHsgT2ZmbGluZUhhbmRsZXIgfSBmcm9tICcuL3Byb3RvY29sL09mZmxpbmVIYW5kbGVyJztcbmltcG9ydCB0eXBlIFBhY2tldCBmcm9tICcuL3Byb3RvY29sL1BhY2tldCc7XG5pbXBvcnQgdHlwZSB7IFNlcnZlck5hbWUgfSBmcm9tICcuL3V0aWxzL1NlcnZlck5hbWUnO1xuXG50eXBlIExvZ2dlciA9IHtcbiAgICBpbmZvOiBGdW5jdGlvbjtcbiAgICB3YXJuOiBGdW5jdGlvbjtcbiAgICBlcnJvcjogRnVuY3Rpb247XG4gICAgdmVyYm9zZTogRnVuY3Rpb247XG4gICAgZGVidWc6IEZ1bmN0aW9uO1xuICAgIHNpbGx5OiBGdW5jdGlvbjtcbn07XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFNlcnZlclNvY2tldCBleHRlbmRzIEV2ZW50RW1pdHRlciB7XG4gICAgcHJpdmF0ZSByZWFkb25seSBzb2NrZXQ6IERncmFtLlNvY2tldDtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGd1aWQ6IGJpZ2ludDtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHNlc3Npb25zOiBTZXQ8UmFrTmV0U2Vzc2lvbj4gPSBuZXcgU2V0KCk7XG4gICAgcHJpdmF0ZSB0aWNrZXI6IE5vZGVKUy5UaW1lb3V0IHwgdW5kZWZpbmVkO1xuXG4gICAgcHJpdmF0ZSByZWFkb25seSBvZmZsaW5lSGFuZGxlciA9IG5ldyBPZmZsaW5lSGFuZGxlcih0aGlzKTtcblxuICAgIHB1YmxpYyBjb25zdHJ1Y3RvcihcbiAgICAgICAgcHJpdmF0ZSBtYXhDb25uZWN0aW9uczogbnVtYmVyLFxuICAgICAgICBwcml2YXRlIHJlYWRvbmx5IG9ubGluZU1vZGU6IGJvb2xlYW4sXG4gICAgICAgIHB1YmxpYyByZWFkb25seSBzZXJ2ZXJOYW1lOiBTZXJ2ZXJOYW1lLFxuICAgICAgICBwcml2YXRlIHJlYWRvbmx5IGxvZ2dlcjogTG9nZ2VyXG4gICAgKSB7XG4gICAgICAgIHN1cGVyKCk7XG4gICAgICAgIHRoaXMuc29ja2V0ID0gRGdyYW0uY3JlYXRlU29ja2V0KCd1ZHA0JykudW5yZWYoKTtcbiAgICAgICAgdGhpcy5ndWlkID0gQnVmZmVyLmFsbG9jVW5zYWZlKDgpLnJlYWRCaWdJbnQ2NEJFKCk7XG5cbiAgICAgICAgaWYgKHRoaXMub25saW5lTW9kZSkge1xuICAgICAgICAgICAgdGhpcy5sb2dnZXIud2FybignT25saW5lIG1vZGUgaXMgY3VycmVudGx5IG5vdCBzdXBwb3J0ZWQuJywgJ1Jha05ldC9TZXJ2ZXJTb2NrZXQnKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBzdGFydChhZGRyZXNzOiBzdHJpbmcsIHBvcnQ6IG51bWJlcik6IHZvaWQge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgdGhpcy5zb2NrZXQuYmluZChwb3J0LCBhZGRyZXNzKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IHVua25vd24pIHtcbiAgICAgICAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIEVycm9yKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoJ0ZhaWxlZCB0byBiaW5kIHNvY2tldCwgZXJyb3IgbWVzc2FnZT0lcycsICdSYWtOZXQvU2VydmVyU29ja2V0L1N0YXJ0Jyk7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoZXJyb3IsICdSYWtOZXQvU2VydmVyU29ja2V0L1N0YXJ0Jyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCB0aWNrID0gKCkgPT5cbiAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgICAgIGZvciAoY29uc3Qgc2Vzc2lvbiBvZiB0aGlzLnNlc3Npb25zLnZhbHVlcygpKSB7XG4gICAgICAgICAgICAgICAgICAgIHNlc3Npb24udXBkYXRlKERhdGUubm93KCkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB0aWNrKCk7XG4gICAgICAgICAgICB9LCBSQUtORVRfVFBTKTtcblxuICAgICAgICAvLyBTdGFydCB0aWNraW5nXG4gICAgICAgIHRoaXMudGlja2VyID0gdGljaygpO1xuICAgICAgICB0aGlzLnRpY2tlci51bnJlZigpO1xuXG4gICAgICAgIHRoaXMuc29ja2V0Lm9uKCdtZXNzYWdlJywgdGhpcy5oYW5kbGVNZXNzYWdlLmJpbmQodGhpcykpO1xuICAgIH1cblxuICAgIHByaXZhdGUgaGFuZGxlTWVzc2FnZShtc2c6IEJ1ZmZlciwgcmluZm86IFJlbW90ZUluZm8pOiB2b2lkIHtcbiAgICAgICAgLy8gRGlyZWN0bHkgY2hlY2sgaWYgaXQncyBhIG9mZmxpbmUgbWVzc2FnZVxuICAgICAgICBpZiAoKG1zZ1swXSEgJiBCaXRGbGFncy5WQUxJRCkgPT09IDApIHtcbiAgICAgICAgICAgIHRoaXMub2ZmbGluZUhhbmRsZXIucHJvY2Vzcyhtc2csIHJpbmZvKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIE5vcm1hbGx5IFJha05ldCBpZ25vcmVzIHVuaGFuZGxlZCBwYWNrZXRzLCBidXQgd2Ugc3RpbGwgd2FudCBzb21lIGxvZ3MuLi5cbiAgICAgICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9mYWNlYm9va2FyY2hpdmUvUmFrTmV0L2Jsb2IvbWFzdGVyL1NvdXJjZS9SYWtQZWVyLmNwcCNMNTQ2NVxuICAgICAgICAgICAgbGV0IHNlc3Npb247XG4gICAgICAgICAgICBpZiAoKHNlc3Npb24gPSB0aGlzLmdldFNlc3Npb25CeUFkZHJlc3MocmluZm8pKSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIHNlc3Npb24uaGFuZGxlKG1zZyk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIC8vIE1heSBiZSB0cmlnZ2VyZWQgYnkgdGhlIEFDSyBvZiBkaXNjb25uZWN0IHBhY2tldCB0aGF0IGlzIHJlY2VpdmVkIGFmdGVyIHNlc3Npb24gaXMgY2xvc2VkXG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZGVidWcoXG4gICAgICAgICAgICAgICAgICAgIGBDYW5ub3QgaGFuZGxlIERhdGFncmFtIGZvciB1bmNvbm5lY3RlZCBjbGllbnQ9JHtyaW5mby5hZGRyZXNzfToke3JpbmZvLnBvcnR9LCBidWZmZXI9JHttc2d9YCxcbiAgICAgICAgICAgICAgICAgICAgJ1Jha05ldC9TZXJ2ZXJTb2NrZXQvaGFuZGxlTWVzc2FnZSdcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIGtpbGwoKTogdm9pZCB7XG4gICAgICAgIC8vIFNlbmQgbGFzdCByZW1haW5pbmcgcGFja2V0cyB0byBhbGwgcGxheWVyc1xuICAgICAgICBmb3IgKGNvbnN0IHNlc3Npb24gb2YgdGhpcy5nZXRTZXNzaW9ucygpKSB7XG4gICAgICAgICAgICAvLyBGSVhNRTogVGhpcyBzaG91bGQgYmUgYXdhaXRhYmxlLlxuICAgICAgICAgICAgc2Vzc2lvbi5zZW5kRnJhbWVRdWV1ZSgpO1xuICAgICAgICB9XG5cbiAgICAgICAgY2xlYXJUaW1lb3V0KHRoaXMudGlja2VyKTtcblxuICAgICAgICAvLyBNYWtlIHN1cmUgd2UgZG9uJ3Qgc2VuZCBhbnkgbW9yZSBldmVudHMuXG4gICAgICAgIHRoaXMucmVtb3ZlQWxsTGlzdGVuZXJzKCk7XG5cbiAgICAgICAgLy8gRmluYWxseSwgY2xvc2UgdGhlIHNvY2tldC5cbiAgICAgICAgdGhpcy5zb2NrZXQuY2xvc2UoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBVc2VkIHRvIHJldHJpZXZlIGlmIHdlIGFyZSBvdmVyZmxvd2luZyB0aGUgbWF4aW11bVxuICAgICAqIGNvbm5lY3Rpb25zIHdlIGNhbiBhbGxvdywgdmFsdWUgZ2l2ZW4gaW4gdGhlIGNvbnN0cnVjdG9yLlxuICAgICAqIEByZXR1cm5zIHtib29sZWFufSBpZiB3ZSBjYW4gaG9sZCBuZXcgY29ubmVjdGlvbnMuXG4gICAgICovXG4gICAgcHVibGljIGFsbG93SW5jb21pbmdDb25uZWN0aW9ucygpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc2Vzc2lvbnMuc2l6ZSA8IHRoaXMubWF4Q29ubmVjdGlvbnM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgbWF4aW11bSBudW1iZXIgb2YgaW5jb21pbmcgY29ubmVjdGlvbnMuXG4gICAgICogQHJldHVybnMge251bWJlcn0gdGhlIG1heGltdW0gY29ubmVjdGlvbnMgd2UgY2FuIGFsbG93LlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRNYXhDb25uZWN0aW9ucygpOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5tYXhDb25uZWN0aW9ucztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBtYXhpbXVtIG51bWJlciBvZiBhbGxvd2VkIGluY29taW5nIGNvbm5lY3Rpb25zLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBhbGxvd2VkIC0gbWF4aW11bSBudW1iZXIgb2YgY29ubmVjdGlvbnMuXG4gICAgICovXG4gICAgcHVibGljIHNldE1heENvbm5lY3Rpb25zKGFsbG93ZWQ6IG51bWJlcik6IHZvaWQge1xuICAgICAgICB0aGlzLm1heENvbm5lY3Rpb25zID0gYWxsb3dlZDtcbiAgICB9XG5cbiAgICBwdWJsaWMgYWRkU2Vzc2lvbihyaW5mbzogUmVtb3RlSW5mbywgbXR1U2l6ZTogbnVtYmVyLCBpbmNvbWluZ0d1aWQ6IGJpZ2ludCk6IHZvaWQge1xuICAgICAgICB0aGlzLnNlc3Npb25zLmFkZChuZXcgUmFrTmV0U2Vzc2lvbih0aGlzLCBtdHVTaXplLCByaW5mbywgaW5jb21pbmdHdWlkKSk7XG4gICAgICAgIHRoaXMubG9nZ2VyLnZlcmJvc2UoYFNlc3Npb24gY3JlYXRlZCBmb3IgY2xpZW50PSR7cmluZm8uYWRkcmVzc306JHtyaW5mby5wb3J0fSwgbXR1PSR7bXR1U2l6ZX1gKTtcbiAgICAgICAgdGhpcy5zZXJ2ZXJOYW1lLnNldE9ubGluZVBsYXllckNvdW50KHRoaXMuc2Vzc2lvbnMuc2l6ZSk7XG4gICAgfVxuXG4gICAgcHVibGljIHJlbW92ZVNlc3Npb24oc2Vzc2lvbjogUmFrTmV0U2Vzc2lvbiwgcmVhc29uPzogc3RyaW5nKTogdm9pZCB7XG4gICAgICAgIGlmICghdGhpcy5zZXNzaW9ucy5kZWxldGUoc2Vzc2lvbikpIHJldHVybjtcbiAgICAgICAgdGhpcy5lbWl0KCdjbG9zZUNvbm5lY3Rpb24nLCBzZXNzaW9uLmdldEFkZHJlc3MoKSwgcmVhc29uKTtcbiAgICAgICAgdGhpcy5sb2dnZXIudmVyYm9zZShgQ2xvc2VkIHNlc3Npb24gZm9yIGNsaWVudD0ke3Nlc3Npb24uZ2V0QWRkcmVzcygpfSwgcmVhc29uPSR7cmVhc29ufWApO1xuICAgICAgICB0aGlzLnNlcnZlck5hbWUuc2V0T25saW5lUGxheWVyQ291bnQodGhpcy5zZXNzaW9ucy5zaXplKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0TG9nZ2VyKCk6IExvZ2dlciB7XG4gICAgICAgIHJldHVybiB0aGlzLmxvZ2dlcjtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0U2Vzc2lvbnMoKTogUmFrTmV0U2Vzc2lvbltdIHtcbiAgICAgICAgcmV0dXJuIEFycmF5LmZyb20odGhpcy5zZXNzaW9ucy52YWx1ZXMoKSk7XG4gICAgfVxuXG4gICAgcHVibGljIGdldFNlc3Npb25CeUFkZHJlc3MocmluZm86IFJlbW90ZUluZm8pOiBSYWtOZXRTZXNzaW9uIHwgbnVsbCB7XG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICB0aGlzLmdldFNlc3Npb25zKCkuZmluZChcbiAgICAgICAgICAgICAgICAoc2Vzc2lvbikgPT4gc2Vzc2lvbi5yaW5mby5hZGRyZXNzID09PSByaW5mby5hZGRyZXNzICYmIHNlc3Npb24ucmluZm8ucG9ydCA9PT0gcmluZm8ucG9ydFxuICAgICAgICAgICAgKSA/PyBudWxsXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHVibGljIGdldFNlc3Npb25CeUdVSUQoZ3VpZDogYmlnaW50KTogUmFrTmV0U2Vzc2lvbiB8IG51bGwge1xuICAgICAgICByZXR1cm4gdGhpcy5nZXRTZXNzaW9ucygpLmZpbmQoKHNlc3Npb24pID0+IHNlc3Npb24uZ3VpZCA9PT0gZ3VpZCkgPz8gbnVsbDtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0U2VydmVyR3VpZCgpOiBiaWdpbnQge1xuICAgICAgICByZXR1cm4gdGhpcy5ndWlkO1xuICAgIH1cblxuICAgIHB1YmxpYyBzZW5kUGFja2V0PFQgZXh0ZW5kcyBQYWNrZXQ+KHBhY2tldDogVCwgcmluZm86IFJlbW90ZUluZm8pOiB2b2lkIHtcbiAgICAgICAgcGFja2V0LmVuY29kZSgpO1xuICAgICAgICB0aGlzLnNlbmRCdWZmZXIocGFja2V0LmdldEJ1ZmZlcigpLCByaW5mbyk7XG4gICAgfVxuXG4gICAgcHVibGljIHNlbmRCdWZmZXIoYnVmZmVyOiBCdWZmZXIsIHJpbmZvOiBSZW1vdGVJbmZvKTogdm9pZCB7XG4gICAgICAgIHRoaXMuc29ja2V0LnNlbmQoYnVmZmVyLCByaW5mby5wb3J0LCByaW5mby5hZGRyZXNzKTtcbiAgICB9XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7QUFrQkEsSUFBcUIsZUFBckIsY0FBMEMsYUFBYTtDQVN2QztDQUNTO0NBQ0Q7Q0FDQztDQVhyQjtD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