@jsprismarine/raknet
Version:
Basic RakNet implementation written in TypeScript
137 lines (130 loc) • 18.1 kB
JavaScript
'use strict';
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
const events = require('events');
const Dgram = require('node:dgram');
const Constants = require('./Constants.cjs.cjs');
const Session = require('./Session.cjs.cjs');
const protocol_BitFlags = require('./protocol/BitFlags.cjs.cjs');
const protocol_OfflineHandler = require('./protocol/OfflineHandler.cjs.cjs');
const _interopDefault = e => e && e.__esModule ? e : { default: e };
const Dgram__default = /*#__PURE__*/_interopDefault(Dgram);
class ServerSocket extends events.EventEmitter {
constructor(maxConnections, onlineMode, serverName, logger) {
super();
this.maxConnections = maxConnections;
this.onlineMode = onlineMode;
this.serverName = serverName;
this.logger = logger;
this.socket = Dgram__default.default.createSocket("udp4").unref();
this.guid = Buffer.allocUnsafe(8).readBigInt64BE();
if (this.onlineMode) {
this.logger.warn("Online mode is currently not supported.", "RakNet/ServerSocket");
}
}
socket;
guid;
sessions = /* @__PURE__ */ new Set();
ticker;
offlineHandler = new protocol_OfflineHandler.OfflineHandler(this);
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();
}, Constants.RAKNET_TPS);
this.ticker = tick();
this.ticker.unref();
this.socket.on("message", this.handleMessage.bind(this));
}
handleMessage(msg, rinfo) {
if ((msg[0] & protocol_BitFlags.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.default(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);
}
}
exports.default = ServerSocket;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2VydmVyU29ja2V0LmNqcy5janMiLCJzb3VyY2VzIjpbIi4uL3NyYy9TZXJ2ZXJTb2NrZXQudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRXZlbnRFbWl0dGVyIH0gZnJvbSAnZXZlbnRzJztcbmltcG9ydCBEZ3JhbSwgeyB0eXBlIFJlbW90ZUluZm8gfSBmcm9tICdub2RlOmRncmFtJztcbmltcG9ydCB7IFJBS05FVF9UUFMgfSBmcm9tICcuL0NvbnN0YW50cyc7XG5pbXBvcnQgUmFrTmV0U2Vzc2lvbiBmcm9tICcuL1Nlc3Npb24nO1xuaW1wb3J0IEJpdEZsYWdzIGZyb20gJy4vcHJvdG9jb2wvQml0RmxhZ3MnO1xuaW1wb3J0IHsgT2ZmbGluZUhhbmRsZXIgfSBmcm9tICcuL3Byb3RvY29sL09mZmxpbmVIYW5kbGVyJztcbmltcG9ydCB0eXBlIFBhY2tldCBmcm9tICcuL3Byb3RvY29sL1BhY2tldCc7XG5pbXBvcnQgdHlwZSB7IFNlcnZlck5hbWUgfSBmcm9tICcuL3V0aWxzL1NlcnZlck5hbWUnO1xuXG50eXBlIExvZ2dlciA9IHtcbiAgICBpbmZvOiBGdW5jdGlvbjtcbiAgICB3YXJuOiBGdW5jdGlvbjtcbiAgICBlcnJvcjogRnVuY3Rpb247XG4gICAgdmVyYm9zZTogRnVuY3Rpb247XG4gICAgZGVidWc6IEZ1bmN0aW9uO1xuICAgIHNpbGx5OiBGdW5jdGlvbjtcbn07XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFNlcnZlclNvY2tldCBleHRlbmRzIEV2ZW50RW1pdHRlciB7XG4gICAgcHJpdmF0ZSByZWFkb25seSBzb2NrZXQ6IERncmFtLlNvY2tldDtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGd1aWQ6IGJpZ2ludDtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHNlc3Npb25zOiBTZXQ8UmFrTmV0U2Vzc2lvbj4gPSBuZXcgU2V0KCk7XG4gICAgcHJpdmF0ZSB0aWNrZXI6IE5vZGVKUy5UaW1lb3V0IHwgdW5kZWZpbmVkO1xuXG4gICAgcHJpdmF0ZSByZWFkb25seSBvZmZsaW5lSGFuZGxlciA9IG5ldyBPZmZsaW5lSGFuZGxlcih0aGlzKTtcblxuICAgIHB1YmxpYyBjb25zdHJ1Y3RvcihcbiAgICAgICAgcHJpdmF0ZSBtYXhDb25uZWN0aW9uczogbnVtYmVyLFxuICAgICAgICBwcml2YXRlIHJlYWRvbmx5IG9ubGluZU1vZGU6IGJvb2xlYW4sXG4gICAgICAgIHB1YmxpYyByZWFkb25seSBzZXJ2ZXJOYW1lOiBTZXJ2ZXJOYW1lLFxuICAgICAgICBwcml2YXRlIHJlYWRvbmx5IGxvZ2dlcjogTG9nZ2VyXG4gICAgKSB7XG4gICAgICAgIHN1cGVyKCk7XG4gICAgICAgIHRoaXMuc29ja2V0ID0gRGdyYW0uY3JlYXRlU29ja2V0KCd1ZHA0JykudW5yZWYoKTtcbiAgICAgICAgdGhpcy5ndWlkID0gQnVmZmVyLmFsbG9jVW5zYWZlKDgpLnJlYWRCaWdJbnQ2NEJFKCk7XG5cbiAgICAgICAgaWYgKHRoaXMub25saW5lTW9kZSkge1xuICAgICAgICAgICAgdGhpcy5sb2dnZXIud2FybignT25saW5lIG1vZGUgaXMgY3VycmVudGx5IG5vdCBzdXBwb3J0ZWQuJywgJ1Jha05ldC9TZXJ2ZXJTb2NrZXQnKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBzdGFydChhZGRyZXNzOiBzdHJpbmcsIHBvcnQ6IG51bWJlcik6IHZvaWQge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgdGhpcy5zb2NrZXQuYmluZChwb3J0LCBhZGRyZXNzKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IHVua25vd24pIHtcbiAgICAgICAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIEVycm9yKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoJ0ZhaWxlZCB0byBiaW5kIHNvY2tldCwgZXJyb3IgbWVzc2FnZT0lcycsICdSYWtOZXQvU2VydmVyU29ja2V0L1N0YXJ0Jyk7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoZXJyb3IsICdSYWtOZXQvU2VydmVyU29ja2V0L1N0YXJ0Jyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCB0aWNrID0gKCkgPT5cbiAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgICAgIGZvciAoY29uc3Qgc2Vzc2lvbiBvZiB0aGlzLnNlc3Npb25zLnZhbHVlcygpKSB7XG4gICAgICAgICAgICAgICAgICAgIHNlc3Npb24udXBkYXRlKERhdGUubm93KCkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB0aWNrKCk7XG4gICAgICAgICAgICB9LCBSQUtORVRfVFBTKTtcblxuICAgICAgICAvLyBTdGFydCB0aWNraW5nXG4gICAgICAgIHRoaXMudGlja2VyID0gdGljaygpO1xuICAgICAgICB0aGlzLnRpY2tlci51bnJlZigpO1xuXG4gICAgICAgIHRoaXMuc29ja2V0Lm9uKCdtZXNzYWdlJywgdGhpcy5oYW5kbGVNZXNzYWdlLmJpbmQodGhpcykpO1xuICAgIH1cblxuICAgIHByaXZhdGUgaGFuZGxlTWVzc2FnZShtc2c6IEJ1ZmZlciwgcmluZm86IFJlbW90ZUluZm8pOiB2b2lkIHtcbiAgICAgICAgLy8gRGlyZWN0bHkgY2hlY2sgaWYgaXQncyBhIG9mZmxpbmUgbWVzc2FnZVxuICAgICAgICBpZiAoKG1zZ1swXSEgJiBCaXRGbGFncy5WQUxJRCkgPT09IDApIHtcbiAgICAgICAgICAgIHRoaXMub2ZmbGluZUhhbmRsZXIucHJvY2Vzcyhtc2csIHJpbmZvKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIE5vcm1hbGx5IFJha05ldCBpZ25vcmVzIHVuaGFuZGxlZCBwYWNrZXRzLCBidXQgd2Ugc3RpbGwgd2FudCBzb21lIGxvZ3MuLi5cbiAgICAgICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9mYWNlYm9va2FyY2hpdmUvUmFrTmV0L2Jsb2IvbWFzdGVyL1NvdXJjZS9SYWtQZWVyLmNwcCNMNTQ2NVxuICAgICAgICAgICAgbGV0IHNlc3Npb247XG4gICAgICAgICAgICBpZiAoKHNlc3Npb24gPSB0aGlzLmdldFNlc3Npb25CeUFkZHJlc3MocmluZm8pKSAhPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIHNlc3Npb24uaGFuZGxlKG1zZyk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIC8vIE1heSBiZSB0cmlnZ2VyZWQgYnkgdGhlIEFDSyBvZiBkaXNjb25uZWN0IHBhY2tldCB0aGF0IGlzIHJlY2VpdmVkIGFmdGVyIHNlc3Npb24gaXMgY2xvc2VkXG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZGVidWcoXG4gICAgICAgICAgICAgICAgICAgIGBDYW5ub3QgaGFuZGxlIERhdGFncmFtIGZvciB1bmNvbm5lY3RlZCBjbGllbnQ9JHtyaW5mby5hZGRyZXNzfToke3JpbmZvLnBvcnR9LCBidWZmZXI9JHttc2d9YCxcbiAgICAgICAgICAgICAgICAgICAgJ1Jha05ldC9TZXJ2ZXJTb2NrZXQvaGFuZGxlTWVzc2FnZSdcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIGtpbGwoKTogdm9pZCB7XG4gICAgICAgIC8vIFNlbmQgbGFzdCByZW1haW5pbmcgcGFja2V0cyB0byBhbGwgcGxheWVyc1xuICAgICAgICBmb3IgKGNvbnN0IHNlc3Npb24gb2YgdGhpcy5nZXRTZXNzaW9ucygpKSB7XG4gICAgICAgICAgICAvLyBGSVhNRTogVGhpcyBzaG91bGQgYmUgYXdhaXRhYmxlLlxuICAgICAgICAgICAgc2Vzc2lvbi5zZW5kRnJhbWVRdWV1ZSgpO1xuICAgICAgICB9XG5cbiAgICAgICAgY2xlYXJUaW1lb3V0KHRoaXMudGlja2VyKTtcblxuICAgICAgICAvLyBNYWtlIHN1cmUgd2UgZG9uJ3Qgc2VuZCBhbnkgbW9yZSBldmVudHMuXG4gICAgICAgIHRoaXMucmVtb3ZlQWxsTGlzdGVuZXJzKCk7XG5cbiAgICAgICAgLy8gRmluYWxseSwgY2xvc2UgdGhlIHNvY2tldC5cbiAgICAgICAgdGhpcy5zb2NrZXQuY2xvc2UoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBVc2VkIHRvIHJldHJpZXZlIGlmIHdlIGFyZSBvdmVyZmxvd2luZyB0aGUgbWF4aW11bVxuICAgICAqIGNvbm5lY3Rpb25zIHdlIGNhbiBhbGxvdywgdmFsdWUgZ2l2ZW4gaW4gdGhlIGNvbnN0cnVjdG9yLlxuICAgICAqIEByZXR1cm5zIHtib29sZWFufSBpZiB3ZSBjYW4gaG9sZCBuZXcgY29ubmVjdGlvbnMuXG4gICAgICovXG4gICAgcHVibGljIGFsbG93SW5jb21pbmdDb25uZWN0aW9ucygpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc2Vzc2lvbnMuc2l6ZSA8IHRoaXMubWF4Q29ubmVjdGlvbnM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgbWF4aW11bSBudW1iZXIgb2YgaW5jb21pbmcgY29ubmVjdGlvbnMuXG4gICAgICogQHJldHVybnMge251bWJlcn0gdGhlIG1heGltdW0gY29ubmVjdGlvbnMgd2UgY2FuIGFsbG93LlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRNYXhDb25uZWN0aW9ucygpOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5tYXhDb25uZWN0aW9ucztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBtYXhpbXVtIG51bWJlciBvZiBhbGxvd2VkIGluY29taW5nIGNvbm5lY3Rpb25zLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBhbGxvd2VkIC0gbWF4aW11bSBudW1iZXIgb2YgY29ubmVjdGlvbnMuXG4gICAgICovXG4gICAgcHVibGljIHNldE1heENvbm5lY3Rpb25zKGFsbG93ZWQ6IG51bWJlcik6IHZvaWQge1xuICAgICAgICB0aGlzLm1heENvbm5lY3Rpb25zID0gYWxsb3dlZDtcbiAgICB9XG5cbiAgICBwdWJsaWMgYWRkU2Vzc2lvbihyaW5mbzogUmVtb3RlSW5mbywgbXR1U2l6ZTogbnVtYmVyLCBpbmNvbWluZ0d1aWQ6IGJpZ2ludCk6IHZvaWQge1xuICAgICAgICB0aGlzLnNlc3Npb25zLmFkZChuZXcgUmFrTmV0U2Vzc2lvbih0aGlzLCBtdHVTaXplLCByaW5mbywgaW5jb21pbmdHdWlkKSk7XG4gICAgICAgIHRoaXMubG9nZ2VyLnZlcmJvc2UoYFNlc3Npb24gY3JlYXRlZCBmb3IgY2xpZW50PSR7cmluZm8uYWRkcmVzc306JHtyaW5mby5wb3J0fSwgbXR1PSR7bXR1U2l6ZX1gKTtcbiAgICAgICAgdGhpcy5zZXJ2ZXJOYW1lLnNldE9ubGluZVBsYXllckNvdW50KHRoaXMuc2Vzc2lvbnMuc2l6ZSk7XG4gICAgfVxuXG4gICAgcHVibGljIHJlbW92ZVNlc3Npb24oc2Vzc2lvbjogUmFrTmV0U2Vzc2lvbiwgcmVhc29uPzogc3RyaW5nKTogdm9pZCB7XG4gICAgICAgIGlmICghdGhpcy5zZXNzaW9ucy5kZWxldGUoc2Vzc2lvbikpIHJldHVybjtcbiAgICAgICAgdGhpcy5lbWl0KCdjbG9zZUNvbm5lY3Rpb24nLCBzZXNzaW9uLmdldEFkZHJlc3MoKSwgcmVhc29uKTtcbiAgICAgICAgdGhpcy5sb2dnZXIudmVyYm9zZShgQ2xvc2VkIHNlc3Npb24gZm9yIGNsaWVudD0ke3Nlc3Npb24uZ2V0QWRkcmVzcygpfSwgcmVhc29uPSR7cmVhc29ufWApO1xuICAgICAgICB0aGlzLnNlcnZlck5hbWUuc2V0T25saW5lUGxheWVyQ291bnQodGhpcy5zZXNzaW9ucy5zaXplKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0TG9nZ2VyKCk6IExvZ2dlciB7XG4gICAgICAgIHJldHVybiB0aGlzLmxvZ2dlcjtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0U2Vzc2lvbnMoKTogUmFrTmV0U2Vzc2lvbltdIHtcbiAgICAgICAgcmV0dXJuIEFycmF5LmZyb20odGhpcy5zZXNzaW9ucy52YWx1ZXMoKSk7XG4gICAgfVxuXG4gICAgcHVibGljIGdldFNlc3Npb25CeUFkZHJlc3MocmluZm86IFJlbW90ZUluZm8pOiBSYWtOZXRTZXNzaW9uIHwgbnVsbCB7XG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICB0aGlzLmdldFNlc3Npb25zKCkuZmluZChcbiAgICAgICAgICAgICAgICAoc2Vzc2lvbikgPT4gc2Vzc2lvbi5yaW5mby5hZGRyZXNzID09PSByaW5mby5hZGRyZXNzICYmIHNlc3Npb24ucmluZm8ucG9ydCA9PT0gcmluZm8ucG9ydFxuICAgICAgICAgICAgKSA/PyBudWxsXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHVibGljIGdldFNlc3Npb25CeUdVSUQoZ3VpZDogYmlnaW50KTogUmFrTmV0U2Vzc2lvbiB8IG51bGwge1xuICAgICAgICByZXR1cm4gdGhpcy5nZXRTZXNzaW9ucygpLmZpbmQoKHNlc3Npb24pID0+IHNlc3Npb24uZ3VpZCA9PT0gZ3VpZCkgPz8gbnVsbDtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0U2VydmVyR3VpZCgpOiBiaWdpbnQge1xuICAgICAgICByZXR1cm4gdGhpcy5ndWlkO1xuICAgIH1cblxuICAgIHB1YmxpYyBzZW5kUGFja2V0PFQgZXh0ZW5kcyBQYWNrZXQ+KHBhY2tldDogVCwgcmluZm86IFJlbW90ZUluZm8pOiB2b2lkIHtcbiAgICAgICAgcGFja2V0LmVuY29kZSgpO1xuICAgICAgICB0aGlzLnNlbmRCdWZmZXIocGFja2V0LmdldEJ1ZmZlcigpLCByaW5mbyk7XG4gICAgfVxuXG4gICAgcHVibGljIHNlbmRCdWZmZXIoYnVmZmVyOiBCdWZmZXIsIHJpbmZvOiBSZW1vdGVJbmZvKTogdm9pZCB7XG4gICAgICAgIHRoaXMuc29ja2V0LnNlbmQoYnVmZmVyLCByaW5mby5wb3J0LCByaW5mby5hZGRyZXNzKTtcbiAgICB9XG59XG4iXSwibmFtZXMiOlsiRXZlbnRFbWl0dGVyIiwiRGdyYW0iLCJPZmZsaW5lSGFuZGxlciIsIlJBS05FVF9UUFMiLCJCaXRGbGFncyIsIlJha05ldFNlc3Np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