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
JavaScript
// 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