UNPKG

tsrcon-client

Version:

A TypeScript RCON client for communicating with a RCON Server.

196 lines (194 loc) 6.35 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { RCONClient: () => RCONClient }); module.exports = __toCommonJS(index_exports); var import_net = require("net"); var import_crypto = require("crypto"); var RCONClient = class { constructor(host, port, password, opts) { this.host = host; this.port = port; this.password = password; this.buffer = Buffer.alloc(0); this.isConnected = false; this.reconnectAttempts = 0; this.authenticated = false; this.callbacks = /* @__PURE__ */ new Map(); this.requestQueue = []; this.processingQueue = false; this.lastRequestTime = 0; this.socket = new import_net.Socket(); this.options = { timeout: opts?.timeout ?? 3e3, retries: opts?.retries ?? 1, reconnect: opts?.reconnect ?? true, reconnectDelay: opts?.reconnectDelay ?? 2e3, maxReconnectAttempts: opts?.maxReconnectAttempts ?? 5, minDelayBetweenRequests: opts?.minDelayBetweenRequests ?? 0 }; this.socket.on("data", this.handleData.bind(this)); this.socket.on("error", (err) => { for (const cb of this.callbacks.values()) cb.reject(err); this.callbacks.clear(); }); this.socket.on("close", () => { this.isConnected = false; this.authenticated = false; if (this.options.reconnect) { this.tryReconnect(); } }); } async connect() { return new Promise((resolve, reject) => { this.socket.connect(this.port, this.host, async () => { try { await this.authenticate(); this.isConnected = true; this.reconnectAttempts = 0; resolve(); } catch (e) { reject(e); } }); }); } disconnect() { this.socket.end(); this.socket.destroy(); this.isConnected = false; this.authenticated = false; } async tryReconnect() { if (this.reconnectAttempts >= this.options.maxReconnectAttempts) return; this.reconnectAttempts++; setTimeout(async () => { try { await this.connect(); console.log("RCON: Reconnected successfully"); } catch (err) { console.error("RCON: Reconnect failed:", err); this.tryReconnect(); } }, this.options.reconnectDelay); } handleData(data) { this.buffer = Buffer.concat([this.buffer, data]); while (this.buffer.length >= 4) { const packetLength = this.buffer.readInt32LE(0); if (this.buffer.length < packetLength + 4) return; const packet = this.buffer.subarray(4, 4 + packetLength); const requestId = packet.readInt32LE(0); const type = packet.readInt32LE(4); const body = packet.toString("utf8", 8, packet.length - 2); this.buffer = this.buffer.subarray(4 + packetLength); const cb = this.callbacks.get(requestId); if (!cb) continue; if (type === 2 /* AUTH_RESPONSE */ && requestId === -1) { cb.reject(new Error("Authentication failed")); this.callbacks.delete(requestId); continue; } cb.buffer.push(body); clearTimeout(cb.timeout); cb.timeout = setTimeout(() => { cb.resolve(cb.buffer.join("")); this.callbacks.delete(requestId); }, 10); } } sendPacket(type, body) { const requestId = (0, import_crypto.randomInt)(1, 2147483647); const payload = Buffer.from(body + "\0", "utf8"); const size = 4 + 4 + payload.length + 1; const buf = Buffer.alloc(4 + size); buf.writeInt32LE(size, 0); buf.writeInt32LE(requestId, 4); buf.writeInt32LE(type, 8); payload.copy(buf, 12); buf.writeUInt8(0, 12 + payload.length); return new Promise((resolve, reject) => { const timeout = setTimeout(() => { this.callbacks.delete(requestId); reject(new Error("RCON request timed out")); }, this.options.timeout); this.callbacks.set(requestId, { resolve, reject, buffer: [], timeout }); this.socket.write(buf); }); } async authenticate() { for (let attempt = 1; attempt <= this.options.retries + 1; attempt++) { try { await this.sendPacket(3 /* AUTH */, this.password); this.authenticated = true; return; } catch (err) { if (attempt > this.options.retries) throw err; } } } async sendCommand(command) { return new Promise((resolve, reject) => { const task = async () => { const now = Date.now(); const delay = Math.max(this.options.minDelayBetweenRequests - (now - this.lastRequestTime), 0); if (delay > 0) await new Promise((r) => setTimeout(r, delay)); try { if (!this.authenticated) throw new Error("Not authenticated"); const result = await this.sendPacket(2 /* COMMAND */, command); this.lastRequestTime = Date.now(); resolve(result); } catch (err) { reject(err); } this.processingQueue = false; this.processQueue(); }; this.requestQueue.push(task); this.processQueue(); }); } async processQueue() { if (this.processingQueue || this.requestQueue.length === 0) return; this.processingQueue = true; const next = this.requestQueue.shift(); if (next) await next(); } async ping() { try { const result = await this.sendCommand("list"); return !!result; } catch { return false; } } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { RCONClient });