UNPKG

@rlqd/minecraft-server-util

Version:

A Node.js library for Minecraft servers that can retrieve status, perform queries, and RCON into servers.

170 lines 17.3 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RCON = void 0; const assert_1 = __importDefault(require("assert")); const events_1 = require("events"); const util_1 = require("util"); const TCPClient_1 = __importDefault(require("./TCPClient")); const encoder = new util_1.TextEncoder(); class RCON extends events_1.EventEmitter { constructor() { super(); this.isLoggedIn = false; this.socket = null; this.requestID = 0; } get isConnected() { return this.socket && this.socket.isConnected || false; } connect(host, port = 25575, options = {}) { (0, assert_1.default)(typeof host === 'string', `Expected 'host' to be a 'string', got '${typeof host}'`); (0, assert_1.default)(host.length > 1, `Expected 'host' to have a length greater than 0, got ${host.length}`); (0, assert_1.default)(typeof port === 'number', `Expected 'port' to be a 'number', got '${typeof port}'`); (0, assert_1.default)(Number.isInteger(port), `Expected 'port' to be an integer, got '${port}'`); (0, assert_1.default)(port >= 0, `Expected 'port' to be greater than or equal to 0, got '${port}'`); (0, assert_1.default)(port <= 65535, `Expected 'port' to be less than or equal to 65535, got '${port}'`); (0, assert_1.default)(typeof options === 'object', `Expected 'options' to be an 'object', got '${typeof options}'`); return new Promise((resolve, reject) => { var _a; this.socket = new TCPClient_1.default(); const timeout = setTimeout(() => { var _a; reject(new Error('Server is offline or unreachable')); (_a = this.socket) === null || _a === void 0 ? void 0 : _a.close(); }, (_a = options === null || options === void 0 ? void 0 : options.timeout) !== null && _a !== void 0 ? _a : 1000 * 5); this.socket.connect(Object.assign({ host, port }, options)) .then(() => { clearTimeout(timeout); resolve(); }) .catch((error) => { clearTimeout(timeout); reject(error); }); }); } login(password, options = {}) { (0, assert_1.default)(typeof password === 'string', `Expected 'password' to be a 'string', got '${typeof password}'`); (0, assert_1.default)(password.length > 1, `Expected 'password' to have a length greater than 0, got ${password.length}`); (0, assert_1.default)(typeof options === 'object', `Expected 'options' to be an 'object', got '${typeof options}'`); return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { var _a; if (this.socket === null || !this.socket.isConnected) return reject(new Error('login() attempted before RCON has connected')); const timeout = setTimeout(() => { var _a; reject(new Error('Server is offline or unreachable')); (_a = this.socket) === null || _a === void 0 ? void 0 : _a.close(); }, (_a = options === null || options === void 0 ? void 0 : options.timeout) !== null && _a !== void 0 ? _a : 1000 * 5); this.isLoggedIn = false; const passwordBytes = encoder.encode(password); // Login packet // https://minecraft.wiki/w/Java_Edition_protocol/RCON#3:_Login { this.socket.writeInt32LE(10 + passwordBytes.byteLength); this.socket.writeInt32LE(this.requestID++); this.socket.writeInt32LE(3); this.socket.writeBytes(passwordBytes); this.socket.writeBytes(Uint8Array.from([0x00, 0x00])); yield this.socket.flush(false); } // Login response packet // https://minecraft.wiki/w/Java_Edition_protocol/RCON#3:_Login { const packetLength = yield this.socket.readInt32LE(); this.socket.ensureBufferedData(packetLength); const requestID = yield this.socket.readInt32LE(); if (requestID === -1) reject(new Error('Invalid RCON password')); const packetType = yield this.socket.readInt32LE(); if (packetType !== 2) reject(new Error('Expected server to send packet type 2, received ' + packetType)); yield this.socket.readBytes(2); } this.isLoggedIn = true; clearTimeout(timeout); resolve(); process.nextTick(() => __awaiter(this, void 0, void 0, function* () { while (this.socket !== null && this.socket.isConnected && this.isLoggedIn) { try { yield this._readPacket(); } catch (e) { this.emit('error', e); } } })); })); } run(command) { return __awaiter(this, void 0, void 0, function* () { (0, assert_1.default)(typeof command === 'string', `Expected 'command' to be a 'string', got '${typeof command}'`); (0, assert_1.default)(command.length > 0, `Expected 'command' to have a length greater than 0, got ${command.length}`); if (this.socket === null || !this.socket.isConnected) throw new Error('run() attempted before RCON has connected'); if (!this.isLoggedIn) throw new Error('run() attempted before RCON has successfully logged in'); const commandBytes = encoder.encode(command); const requestID = this.requestID++; this.socket.writeInt32LE(10 + commandBytes.byteLength); this.socket.writeInt32LE(requestID); this.socket.writeInt32LE(2); this.socket.writeBytes(commandBytes); this.socket.writeBytes(Uint8Array.from([0x00, 0x00])); yield this.socket.flush(false); return requestID; }); } execute(command) { return __awaiter(this, void 0, void 0, function* () { (0, assert_1.default)(typeof command === 'string', `Expected 'command' to be a 'string', got '${typeof command}'`); (0, assert_1.default)(command.length > 1, `Expected 'command' to have a length greater than 0, got ${command.length}`); const requestID = yield this.run(command); return new Promise((resolve) => { const listenerFunc = (data) => { if (data.requestID !== requestID) return; this.removeListener('message', listenerFunc); resolve(data.message); }; this.on('message', listenerFunc); }); }); } _readPacket() { return __awaiter(this, void 0, void 0, function* () { if (this.socket === null || !this.socket.isConnected || !this.isLoggedIn) return Promise.reject(new Error('Attempted to read packet when socket was disconnected or RCON was not logged in')); const packetLength = yield this.socket.readInt32LE(); yield this.socket.ensureBufferedData(packetLength); const requestID = yield this.socket.readInt32LE(); const packetType = yield this.socket.readInt32LE(); if (packetType === 0) { const message = yield this.socket.readStringNT(); yield this.socket.readBytes(1); this.emit('message', { requestID, message }); } else { yield this.socket.readBytes(packetLength - 8); } }); } close() { var _a; (_a = this.socket) === null || _a === void 0 ? void 0 : _a.close(); } } exports.RCON = RCON; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUkNPTi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zdHJ1Y3R1cmUvUkNPTi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7QUFBQSxvREFBNEI7QUFDNUIsbUNBQXNDO0FBRXRDLCtCQUFtQztBQUNuQyw0REFBb0M7QUFFcEMsTUFBTSxPQUFPLEdBQUcsSUFBSSxrQkFBVyxFQUFFLENBQUM7QUFpQmxDLE1BQU0sSUFBSyxTQUFRLHFCQUFZO0lBSzlCO1FBQ0MsS0FBSyxFQUFFLENBQUM7UUFMRixlQUFVLEdBQUcsS0FBSyxDQUFDO1FBQ2xCLFdBQU0sR0FBcUIsSUFBSSxDQUFDO1FBQ2hDLGNBQVMsR0FBRyxDQUFDLENBQUM7SUFJdEIsQ0FBQztJQUVELElBQUksV0FBVztRQUNkLE9BQU8sSUFBSSxDQUFDLE1BQU0sSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsSUFBSSxLQUFLLENBQUM7SUFDeEQsQ0FBQztJQUVELE9BQU8sQ0FBQyxJQUFZLEVBQUUsSUFBSSxHQUFHLEtBQUssRUFBRSxVQUFtQyxFQUFFO1FBQ3hFLElBQUEsZ0JBQU0sRUFBQyxPQUFPLElBQUksS0FBSyxRQUFRLEVBQUUsMENBQTBDLE9BQU8sSUFBSSxHQUFHLENBQUMsQ0FBQztRQUMzRixJQUFBLGdCQUFNLEVBQUMsSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsd0RBQXdELElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBQy9GLElBQUEsZ0JBQU0sRUFBQyxPQUFPLElBQUksS0FBSyxRQUFRLEVBQUUsMENBQTBDLE9BQU8sSUFBSSxHQUFHLENBQUMsQ0FBQztRQUMzRixJQUFBLGdCQUFNLEVBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsRUFBRSwwQ0FBMEMsSUFBSSxHQUFHLENBQUMsQ0FBQztRQUNsRixJQUFBLGdCQUFNLEVBQUMsSUFBSSxJQUFJLENBQUMsRUFBRSwwREFBMEQsSUFBSSxH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