UNPKG

rustcon-js

Version:

RustCON JS is a JavaScript library for interacting with Rust servers via WebSockets (Web RCON)

502 lines (495 loc) 11.4 kB
// src/client/Client.ts import { WebSocket } from "unws"; import { EventEmitter } from "events"; // src/structures/ServerInfo.ts var ServerInfo2 = class { /** * Server hostname * @type {string} */ hostname; /** * * @type {number} */ players; /** * * @type {number} */ maxPlayers; /** * * @type {number} */ queuedPlayers; /** * * @type {number} */ joiningPlayers; /** * Server entity count * @type {number} */ entityCount; /** * Server game time * @type {string} */ gameTime; /** * Server uptime (seconds) * @type {number} */ uptime; /** * Server map type * @type {string} */ map; /** * Server framerate * @type {number} */ framerate; /** * Server memory usage * @type {number} */ memory; /** * System memory usage * @type {number} */ memoryUsageSystem; /** * * @type {number} */ collections; /** * Server network in (bytes) * @type {number} */ networkIn; /** * Server network out (bytes) * @type {number} */ networkOut; /** * Is the server restarting * @type {boolean} */ restarting; /** * Server save created time * @type {string} */ saveCreatedTime; /** * Server version * @type {number} */ version; /** * Server protocol * @type {string} */ protocol; constructor(serverInfo) { this.hostname = serverInfo.Hostname; this.players = serverInfo.Players; this.maxPlayers = serverInfo.MaxPlayers; this.queuedPlayers = serverInfo.Queued; this.joiningPlayers = serverInfo.Joining; this.entityCount = serverInfo.EntityCount; this.gameTime = serverInfo.GameTime; this.uptime = serverInfo.Uptime; this.map = serverInfo.Map; this.framerate = serverInfo.Framerate; this.memory = serverInfo.Memory; this.memoryUsageSystem = serverInfo.MemoryUsageSystem; this.collections = serverInfo.Collections; this.networkIn = serverInfo.NetworkIn; this.networkOut = serverInfo.NetworkOut; this.restarting = serverInfo.Restarting; this.saveCreatedTime = serverInfo.SaveCreatedTime; this.version = serverInfo.Version; this.protocol = serverInfo.Protocol; } /** * Check if the server is full * @returns {boolean} */ isFull() { return this.players >= this.maxPlayers; } /** * Check if the server has a queue * @returns {boolean} */ hasQueue() { return this.queuedPlayers > 0; } /** * Check if the server has players joining * @returns {boolean} */ hasPlayersJoining() { return this.joiningPlayers > 0; } }; // src/utils/validators.ts function validateConnectionOptions(options) { if (!options.ip) { return "IP is required"; } if (!/^(\d{1,3}\.){3}\d{1,3}$/.test(options.ip)) { return "Invalid IP Address"; } if (options.port && (isNaN(options.port) || options.port < 1 || options.port > 65535)) { return "Invalid Port"; } if (!options.password || options.password === "") { return "Password is required"; } return null; } function isValidSteamId(steamId) { if (!steamId) { return false; } if (typeof steamId === "string") { steamId = Number(steamId); } if (isNaN(steamId)) { return false; } return steamId > 7656119e10; } // src/managers/Server.ts var ServerManager = class { client; constructor(client) { this.client = client; } /** * Get the server info * @returns {Promise<ServerInfo>} */ async getInfo() { const response = await this.client.sendCommand("serverinfo"); const serverInfo = new ServerInfo2(JSON.parse(response.content)); return serverInfo; } /** * Save the server * @returns {Promise<void>} */ async save() { await this.client.sendCommand("save"); } /** * Save the server configuration * @returns {Promise<void>} */ async writeCFG() { await this.client.sendCommand("writecfg"); } /** * Restart the server * @param {string} reason * @param {number} duration * @returns {Promise<void>} */ async restart(reason, duration) { await this.client.sendCommand(`restart ${reason} ${duration}`); } /** * Shutdown the server * @param {string} reason * @param {number} duration * @returns {Promise<void>} */ async shutdown(reason, duration) { await this.client.sendCommand(`shutdown ${reason} ${duration}`); } /** * Add a moderator to the server * @param {(number|string)} steamId * @param {string} name * @param {string} reason * @param {boolean} save * @returns {Promise<void>} */ async addModerator(steamId, name, reason, save = true) { if (!isValidSteamId(steamId)) { throw new Error("Invalid Steam ID"); } await this.client.sendCommand(`moderatorid ${steamId} ${name} ${reason}`); if (save) { await this.writeCFG(); } } /** * Add an owner to the server * @param {(number|string)} steamId * @param {string} name * @param {string} reason * @param {boolean} save * @returns {Promise<void>} */ async addOwner(steamId, name, reason, save = true) { if (!isValidSteamId(steamId)) { throw new Error("Invalid Steam ID"); } await this.client.sendCommand(`ownerid ${steamId} ${name} ${reason}`); if (save) { await this.writeCFG(); } } }; // src/structures/Player.ts var Player2 = class { /** * Player name * @type {string} */ name; /** * Player Steam ID * @type {string} */ steamId; /** * Player IP address * @type {string} */ ipAddress; /** * Player ping * @type {number} */ ping; /** * Player health * @type {number} */ health; /** * Player connected time (seconds) * @type {number} */ connectedTime; playersManager; constructor(player, playersManager) { this.name = player.DisplayName; this.steamId = player.SteamID; this.ipAddress = player.Address; this.ping = player.Ping; this.health = player.Health; this.connectedTime = player.ConnectedSeconds; this.playersManager = playersManager; } /** * Kick player * @param {string} reason * @example * player.kick(); * player.kick("You're a bad player!"); */ async kick(reason) { await this.playersManager.kickPlayer(this.steamId, reason); } /** * Ban player * @param {string} reason * @example * player.ban(); * player.ban("You're a bad player!"); */ async ban(reason) { await this.playersManager.banPlayer(this.steamId, reason); } }; // src/managers/Players.ts var PlayersManager = class { client; constructor(client) { this.client = client; } /** * Get all connected players * @returns {Promise<Player[]>} */ async getAll() { const response = await this.client.sendCommand("playerlist"); const playerList = JSON.parse(response.content); return playerList.map((player) => new Player2(player, this)); } /** * Get a player by their Steam ID * @param {(number|string)} steamId * @returns {Promise<(Player|null)>} */ async get(steamId) { if (!isValidSteamId(steamId)) { throw new Error("Invalid Steam ID"); } const allPlayers = await this.getAll(); return allPlayers.find((player) => player.steamId === steamId) || null; } /** * Kick a player * @param {(number|string)} steamId * @param {string} reason * @returns {Promise<void>} */ async kickPlayer(steamId, reason) { if (!isValidSteamId(steamId)) { throw new Error("Invalid Steam ID"); } await this.client.sendCommand(`kick ${steamId} ${reason}`); } /** * Ban a player * @param {(number|string)} steamId * @param {string} reason * @returns {Promise<void>} */ async banPlayer(steamId, reason) { if (!isValidSteamId(steamId)) { throw new Error("Invalid Steam ID"); } await this.client.sendCommand(`ban ${steamId} ${reason}`); } /** * Unban a player * @param {(number|string)} steamId * @returns {Promise<void>} */ async unbanPlayer(steamId) { if (!isValidSteamId(steamId)) { throw new Error("Invalid Steam ID"); } await this.client.sendCommand(`unban ${steamId}`); } }; // src/structures/Message.ts var Message2 = class { content; identifier; type; stacktrace; constructor(messageData) { this.content = messageData.Message; this.identifier = messageData.Identifier; this.type = messageData.Type; this.stacktrace = messageData.Stacktrace; } }; // src/client/Client.ts var Client = class extends EventEmitter { ws; server; players; connectionOptions; pendingCommands; constructor(options) { super(); const validationResult = validateConnectionOptions(options); if (validationResult) { throw new Error(`Invalid Connection Options: ${validationResult}`); } this.ws = null; this.server = new ServerManager(this); this.players = new PlayersManager(this); this.connectionOptions = options; this.pendingCommands = /* @__PURE__ */ new Map(); } on(e, listener) { return super.on(e, listener); } /** * Connects to the server * @returns {void} */ connect() { this.ws = new WebSocket(`ws://${this.connectionOptions.ip}:${this.connectionOptions.port ?? 28016}/${this.connectionOptions.password}`); this.ws.onopen = this.onConnection.bind(this); this.ws.onmessage = (data) => this.onMessage(data.data); this.ws.onerror = this.onError.bind(this); this.ws.onclose = this.onClose.bind(this); } /** * Sends a command to the server * @param {string} command - The command to be sent * @returns {Promise<Message>} */ sendCommand(command) { return new Promise((resolve, reject) => { if (this.ws && this.ws.readyState === WebSocket.OPEN) { const identifier = this.generateIdentifier(); this.ws.send(JSON.stringify({ Identifier: identifier, Message: command, Name: "RCON" })); this.pendingCommands.set(identifier, resolve); setTimeout(() => { if (this.pendingCommands.has(identifier)) { this.pendingCommands.delete(identifier); reject("Command Timed Out"); } }, 5e3); } else { reject("Not connected to server"); } }); } /** * Disconnects from the server * @returns {void} */ disconnect() { if (this.ws) { this.ws.close(); } else { throw new Error("Not connected to server"); } } onConnection() { this.emit("connected"); } onMessage(data) { const message = new Message2(JSON.parse(data)); const pendingCommand = this.pendingCommands.get(message.identifier); if (pendingCommand) { pendingCommand(message); this.pendingCommands.delete(message.identifier); } else { this.emit("message", message); } } onError(error) { this.emit("error", error); } onClose() { this.ws = null; this.pendingCommands.clear(); this.emit("disconnected"); } generateIdentifier() { const randomNumber = Math.floor(Math.random() * (1e3 - 10 + 1)) + 10; if (this.pendingCommands.has(randomNumber)) { return this.generateIdentifier(); } return randomNumber; } }; export { Client as RconConnection }; //# sourceMappingURL=index.mjs.map