UNPKG

rustplus-ts

Version:

Rust+ API Wrapper written in TypeScript for the game Rust.

1,012 lines (1,007 loc) 94.7 kB
/* Copyright (C) 2025 Alexander Emanuelsson (alexemanuelol) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. https://github.com/alexemanuelol/rustplus-ts */ 'use strict'; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RustPlus = exports.ConsumeTokensError = exports.AppResponseError = exports.EmitErrorType = void 0; const ws_1 = __importDefault(require("ws")); const events_1 = require("events"); const rpi = __importStar(require("./interfaces/rustplus")); const validation = __importStar(require("./validation")); var EmitErrorType; (function (EmitErrorType) { EmitErrorType[EmitErrorType["WebSocket"] = 0] = "WebSocket"; EmitErrorType[EmitErrorType["Callback"] = 1] = "Callback"; })(EmitErrorType || (exports.EmitErrorType = EmitErrorType = {})); var AppResponseError; (function (AppResponseError) { /** * No error was found. */ AppResponseError[AppResponseError["NoError"] = 0] = "NoError"; /** * unknown occurences: * - unknown */ AppResponseError[AppResponseError["Unknown"] = 1] = "Unknown"; /** * server_error occurences: * - If there was an error during the executing of request handler on server side. * - If CameraRendererManager instance is null. */ AppResponseError[AppResponseError["ServerError"] = 2] = "ServerError"; /** * banned occurences: * - Trying to run any request while being Rust+ banned on the rust server. */ AppResponseError[AppResponseError["Banned"] = 3] = "Banned"; /** * rate_limit occurences: * - Too many requests too fast, tokens ran out. */ AppResponseError[AppResponseError["RateLimit"] = 4] = "RateLimit"; /** * not_found occurences: * - playerToken might be invalid, rust server might have reset Rust+ data. * - smart device can't be found. * - Trying to run promoteToLeader on someone that is not in the team. * - Trying to run cameraSubscribe on a cameraId that could not be found. */ AppResponseError[AppResponseError["NotFound"] = 5] = "NotFound"; /** * wrong_type occurences: * - Trying to run setEntityValue on Alarm or StorageMonitor. * - Trying to run checkSubscription on Switch or StorageMonitor. * - Trying to run setSubscription on Switch or StorageMonitor. */ AppResponseError[AppResponseError["WrongType"] = 6] = "WrongType"; /** * no_team occurences: * - Trying to run promoteToLeader when you're not in a team. * - Trying to run getTeamChat when you're not in a team. */ AppResponseError[AppResponseError["NoTeam"] = 7] = "NoTeam"; /** * no_clan occurences: * - Trying to run getClanInfo when you're not in a clan. * - Trying to run setClanMotd when you're not in a clan. * - Trying to run getClanChat when you're not in a clan. * - Trying to run sendClanMessage when you're not in a clan. */ AppResponseError[AppResponseError["NoClan"] = 8] = "NoClan"; /** * no_map occurences: * - Trying to run getMap but imageData is null. */ AppResponseError[AppResponseError["NoMap"] = 9] = "NoMap"; /** * no_camera occurences: * - Trying to run cameraInput when there is no camera subscribed to. */ AppResponseError[AppResponseError["NoCamera"] = 10] = "NoCamera"; /** * no_player occurences: * - Trying to run cameraSubscribe while player token is invalid or player caller dead. */ AppResponseError[AppResponseError["NoPlayer"] = 11] = "NoPlayer"; /** * access_denied occurences: * - Trying to run cameraSubscribe with a cameraId but access was denied. * - Trying to run getNexusAuth but access was denied. * - Trying to run promoteToLeader when you're not the leader. */ AppResponseError[AppResponseError["AccessDenied"] = 12] = "AccessDenied"; /** * player_online occurences: * - Trying to run cameraSubscribe when requester online. */ AppResponseError[AppResponseError["PlayerOnline"] = 13] = "PlayerOnline"; /** * invalid_playerid occurences: * - Trying to run getNexusAuth but playerid is invalid. */ AppResponseError[AppResponseError["InvalidPlayerid"] = 14] = "InvalidPlayerid"; /** * invalid_id occurences: * - Trying to run cameraSubscribe with a cameraId that is an empty string. */ AppResponseError[AppResponseError["InvalidId"] = 15] = "InvalidId"; /** * invalid_motd occurences: * - Trying to run setClanMotd but motd was invalid. */ AppResponseError[AppResponseError["InvalidMotd"] = 16] = "InvalidMotd"; /** * too_many_subscribers occurences: * - Trying to run setSubscription on a Alarm that have too many subscribers already. */ AppResponseError[AppResponseError["TooManySubscribers"] = 17] = "TooManySubscribers"; /** * not_enabled occurences: * - Trying to run cameraInput when cameras are disabled on the rust server. * - Trying to run cameraSubscribe when cameras are disabled on the rust server. * - Trying to run cameraUnsubscribe when cameras are disabled on the rust server. */ AppResponseError[AppResponseError["NotEnabled"] = 18] = "NotEnabled"; /** * message_not_sent occurences: * - Trying to run sendClanMessage but something went wrong on server side. * - Trying to run sendTeamMessage but something went wrong on server side. */ AppResponseError[AppResponseError["MessageNotSent"] = 19] = "MessageNotSent"; })(AppResponseError || (exports.AppResponseError = AppResponseError = {})); var ConsumeTokensError; (function (ConsumeTokensError) { ConsumeTokensError[ConsumeTokensError["NoError"] = 0] = "NoError"; ConsumeTokensError[ConsumeTokensError["Unknown"] = 1] = "Unknown"; ConsumeTokensError[ConsumeTokensError["NotEnoughConnectionTokens"] = 2] = "NotEnoughConnectionTokens"; ConsumeTokensError[ConsumeTokensError["NotEnoughPlayerIdTokens"] = 3] = "NotEnoughPlayerIdTokens"; ConsumeTokensError[ConsumeTokensError["WaitReplenishTimeout"] = 4] = "WaitReplenishTimeout"; })(ConsumeTokensError || (exports.ConsumeTokensError = ConsumeTokensError = {})); function getAddress(useFacepunchProxy, ip, port) { return useFacepunchProxy ? `wss://companion-rust.facepunch.com/game/${ip}/${port}` : `ws://${ip}:${port}`; } class RustPlus extends events_1.EventEmitter { static MAX_REQUESTS_PER_IP_ADDRESS = 50; static REQUESTS_PER_IP_REPLENISH_RATE = 15; /* Tokens per second. */ static MAX_REQUESTS_PER_PLAYER_ID = 25; static REQUESTS_PER_PLAYER_ID_REPLENISH_RATE = 3; /* Tokens per second. */ static MAX_REQUESTS_FOR_SERVER_PAIRING = 5; static REQUESTS_FOR_SERVER_PAIRING_REPLENISH_RATE = 0.1; /* Tokens per second. */ static REQUEST_GET_INFO_TOKEN_COST = 1; static REQUEST_GET_TIME_TOKEN_COST = 1; static REQUEST_GET_MAP_TOKEN_COST = 5; static REQUEST_GET_TEAM_INFO_TOKEN_COST = 1; static REQUEST_GET_TEAM_CHAT_TOKEN_COST = 1; static REQUEST_SEND_TEAM_MESSAGE_TOKEN_COST = 2; static REQUEST_GET_ENTITY_INFO_TOKEN_COST = 1; static REQUEST_SET_ENTITY_VALUE_TOKEN_COST = 1; static REQUEST_CHECK_SUBSCRIPTION_TOKEN_COST = 1; static REQUEST_SET_SUBSCRIPTION_TOKEN_COST = 1; static REQUEST_GET_MAP_MARKERS_TOKEN_COST = 1; static REQUEST_PROMOTE_TO_LEADER_TOKEN_COST = 1; static REQUEST_GET_CLAN_INFO_TOKEN_COST = 1; static REQUEST_SET_CLAN_MOTD_TOKEN_COST = 1; static REQUEST_GET_CLAN_CHAT_TOKEN_COST = 1; static REQUEST_SEND_CLAN_MESSAGE_TOKEN_COST = 2; static REQUEST_GET_NEXUS_AUTH_TOKEN_COST = 1; static REQUEST_CAMERA_SUBSCRIBE_TOKEN_COST = 1; static REQUEST_CAMERA_UNSUBSCRIBE_TOKEN_COST = 1; static REQUEST_CAMERA_INPUT_TOKEN_COST = 0.01; static REQUEST_GET_INFO_TIMEOUT_MS = 10000; static REQUEST_GET_TIME_TIMEOUT_MS = 10000; static REQUEST_GET_MAP_TIMEOUT_MS = 30000; static REQUEST_GET_TEAM_INFO_TIMEOUT_MS = 10000; static REQUEST_GET_TEAM_CHAT_TIMEOUT_MS = 10000; static REQUEST_SEND_TEAM_MESSAGE_TIMEOUT_MS = 10000; static REQUEST_GET_ENTITY_INFO_TIMEOUT_MS = 10000; static REQUEST_SET_ENTITY_VALUE_TIMEOUT_MS = 10000; static REQUEST_CHECK_SUBSCRIPTION_TIMEOUT_MS = 10000; static REQUEST_SET_SUBSCRIPTION_TIMEOUT_MS = 10000; static REQUEST_GET_MAP_MARKERS_TIMEOUT_MS = 10000; static REQUEST_PROMOTE_TO_LEADER_TIMEOUT_MS = 10000; static REQUEST_GET_CLAN_INFO_TIMEOUT_MS = 10000; static REQUEST_SET_CLAN_MOTD_TIMEOUT_MS = 10000; static REQUEST_GET_CLAN_CHAT_TIMEOUT_MS = 10000; static REQUEST_SEND_CLAN_MESSAGE_TIMEOUT_MS = 10000; static REQUEST_GET_NEXUS_AUTH_TIMEOUT_MS = 10000; static REQUEST_CAMERA_SUBSCRIBE_TIMEOUT_MS = 10000; static REQUEST_CAMERA_UNSUBSCRIBE_TIMEOUT_MS = 10000; static REQUEST_CAMERA_INPUT_TIMEOUT_MS = 10000; ip; port; useFacepunchProxy; logger; seq; seqCallbacks; ws; tokens; replenishInterval; /** * @param {string} ip The ip address or hostname of the Rust Server. * @param {string} port The port of the Rust Server (app.port in server.cfg) * @param useFacepunchProxy True to use secure websocket via Facepunch's proxy, or false to directly connect * to Rust Server. * * Events emitted by the RustPlus class instance * - connecting: When we are connecting to the Rust Server. * - connected: When we are connected to the Rust Server. * - message: When an AppMessage has been received from the Rust Server. * - request: When an AppRequest has been sent to the Rust Server. * - disconnected: When we are disconnected from the Rust Server. * - error: When something goes wrong with the WebSocket, incoming message or the request callback function. */ constructor(ip, port, useFacepunchProxy = false, logger = null) { super(); this.ip = ip; this.port = port; this.useFacepunchProxy = useFacepunchProxy; this.logger = logger; this.seq = 0; this.seqCallbacks = []; this.ws = null; this.tokens = { connection: RustPlus.MAX_REQUESTS_PER_IP_ADDRESS, playerId: {}, serverPairing: RustPlus.MAX_REQUESTS_FOR_SERVER_PAIRING }; this.replenishInterval = null; } /** * Function that replenish the connection, playerId and server pairing tokens. */ replenishTask() { this.replenishConnectionTokens(); this.replenishPlayerIdTokens(); this.replenishServerPairingTokens(); } /** * Function that replenish the connection tokens. */ replenishConnectionTokens() { /* Replenish tokens for requests per ip. */ if (this.tokens.connection < RustPlus.MAX_REQUESTS_PER_IP_ADDRESS) { this.tokens.connection = Math.min(this.tokens.connection + RustPlus.REQUESTS_PER_IP_REPLENISH_RATE, RustPlus.MAX_REQUESTS_PER_IP_ADDRESS); } } /** * Function that replenish the playerId tokens. */ replenishPlayerIdTokens() { /* Replenish tokens for requests per player id. */ for (const playerId in this.tokens.playerId) { if (this.tokens.playerId[playerId] < RustPlus.MAX_REQUESTS_PER_PLAYER_ID) { this.tokens.playerId[playerId] = Math.min(this.tokens.playerId[playerId] + RustPlus.REQUESTS_PER_PLAYER_ID_REPLENISH_RATE, RustPlus.MAX_REQUESTS_PER_PLAYER_ID); } } } /** * Function that replenish the server pairing tokens. */ replenishServerPairingTokens() { /* Replenish tokens for requests for server pairing. */ if (this.tokens.serverPairing < RustPlus.MAX_REQUESTS_FOR_SERVER_PAIRING) { this.tokens.serverPairing = Math.min(this.tokens.serverPairing + RustPlus.REQUESTS_FOR_SERVER_PAIRING_REPLENISH_RATE, RustPlus.MAX_REQUESTS_FOR_SERVER_PAIRING); } } /** * Consume tokens amount of tokens from connection and playerId. * @param {string} playerId - The steamId of the player consuming the tokens. * @param {number} tokens - The tokens to consume. * @param {boolean} waitForReplenish - If true, it will wait till there are available tokens to consume. * @param {number} timeoutMs - Timeout if tokens were not replenished in timeout milliseconds. * @returns {Promise<ConsumeTokensError>} Resolves with a `ConsumeTokensError` enum value indicating success or * the type of error encountered. */ async consumeTokens(playerId, tokens, waitForReplenish, timeoutMs = 10000) { /* Check if playerId exists, if not, initialize with max tokens. */ if (!(playerId in this.tokens.playerId)) { this.tokens.playerId[playerId] = RustPlus.MAX_REQUESTS_PER_PLAYER_ID; } const startTime = Date.now(); /* Function to check whether there are enough tokens available from connection and playerId. */ const hasEnoughTokens = () => { return this.tokens.connection >= tokens && this.tokens.playerId[playerId] >= tokens; }; if (!waitForReplenish && !hasEnoughTokens()) { if (this.tokens.connection >= tokens) { return ConsumeTokensError.NotEnoughConnectionTokens; } else if (this.tokens.playerId[playerId] >= tokens) { return ConsumeTokensError.NotEnoughPlayerIdTokens; } else { return ConsumeTokensError.Unknown; } } /* Wait until there are enough tokens or the timeout is reached */ while (!hasEnoughTokens()) { const elapsedTime = Date.now() - startTime; if (elapsedTime >= timeoutMs) { return ConsumeTokensError.WaitReplenishTimeout; } await this.delay(100); } this.tokens.connection -= tokens; this.tokens.playerId[playerId] -= tokens; return ConsumeTokensError.NoError; } /** * Delays the execution of the next operation by a specified number of milliseconds. * * @param {number} ms - The amount of time, in milliseconds, to delay the execution. * * @returns {Promise<void>} A promise that resolves after the specified delay. */ delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } /** * Retrieves the next available sequence number for a request. * * Increments the sequence number and ensures that it is unique by checking for any existing callbacks * associated with the current sequence number. If a callback exists, it continues to increment the * sequence until a unique number is found. * * @returns {number} The next available sequence number. */ getNextSeq() { let nextSeq = this.seq + 1; /* Find the next available sequence number. */ while (this.seqCallbacks[nextSeq]) { nextSeq++; } this.seq = nextSeq; return nextSeq; } /** * Setup and connect to the Rust Server via WebSocket. * @returns {Promise<boolean>} Promise of True if successful, else false. */ async connect() { /* Make sure existing connection is closed before connecting again. */ if (this.ws !== null) { this.disconnect(); } this.emit('connecting'); this.ws = new ws_1.default(getAddress(this.useFacepunchProxy, this.ip, this.port)); this.ws.on('open', () => { this.emit('connected'); }); this.ws.on('close', () => { this.emit('disconnected'); }); this.ws.on('error', (e) => { this.emit('error', EmitErrorType.WebSocket, e); }); this.ws.on('message', (data) => { try { if (!data) { throw new Error('Received empty or invalid message data.'); } let handled = false; const appMessage = rpi.AppMessage.fromBinary(data); if (appMessage.response && this.seqCallbacks[appMessage.response.seq]) { const callback = this.seqCallbacks[appMessage.response.seq]; try { callback(appMessage); handled = true; } catch (callbackError) { const errorMessage = callbackError instanceof Error ? callbackError.message : 'Unknown error'; this.emit('error', EmitErrorType.Callback, new Error(`ERROR on.message: ${errorMessage}`)); } finally { delete this.seqCallbacks[appMessage.response.seq]; } } this.emit('message', appMessage, handled); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; this.emit('error', EmitErrorType.Callback, new Error(`ERROR on.message: ${errorMessage}`)); } }); /* Start interval to replenish tokens every second. */ this.replenishInterval = setInterval(() => this.replenishTask(), 1000); return true; } /** * Disconnect from the Rust Server and clear WebSocket variable. * @returns {boolean} True if successful, else false. */ async disconnect() { if (this.replenishInterval) { clearInterval(this.replenishInterval); } if (this.ws !== null) { this.ws.terminate(); await this.delay(100); this.ws.removeAllListeners(); this.ws = null; return true; } else { return false; } } /** * Check if RustPlus is connected to the server. * @returns {boolean} True if connected, else false. */ isConnected() { return this.ws !== null && this.ws.readyState === ws_1.default.OPEN; } /** * Sends a request to the Rust server with a specified callback that will be executed upon receiving a response. * The request is created using the `AppRequest` protocol, and it ensures that the WebSocket connection is open * before sending the request. The function also handles the sequence number for requests and stores the callback * for when the response arrives. * * @param {Omit<rpi.AppRequest, 'seq' | 'playerId' | 'playerToken'>} data - The data for the request, * excluding the `seq`, `playerId`, * and `playerToken`, which are * provided separately. * @param {string} playerId - The steamId of the player making the request. * @param {number} playerToken - The authentication token of the player making the request. * @param {CallbackFunction} callback - The callback function that will be executed when the server responds. * @param {number | null} [seq=null] - The sequence number for the request. If not provided, a new sequence number * is generated. * * @returns {Error | void} Returns an `Error` if there was an issue sending the request or formatting the data. * Returns `void` if the request is successfully sent. */ sendRequest(data, playerId, playerToken, callback, seq = null) { /* Is the WebSocket present? */ if (this.ws === null || this.ws.readyState !== ws_1.default.OPEN) { return new Error('ERROR sendRequest: WebSocket is not open.'); } if (seq === null) { seq = this.getNextSeq(); } /* Save callback function. */ this.seqCallbacks[seq] = callback; /* Create AppRequest object. */ let appRequestData; try { appRequestData = { seq: seq, playerId: playerId, playerToken: playerToken, ...data }; if (!validation.isValidAppRequest(appRequestData, this.logger)) { throw new Error('AppRequest data is invalid.'); } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; delete this.seqCallbacks[seq]; return new Error(`ERROR sendRequest: ${errorMessage}`); } /* Convert AppRequest object to binary format. */ let appRequest; try { appRequest = rpi.AppRequest.toBinary(appRequestData); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; delete this.seqCallbacks[seq]; return new Error(`ERROR sendRequest AppRequest.toBinary: ${errorMessage}`); } /* Send AppRequest packet to the rust server. */ try { this.ws.send(appRequest); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; delete this.seqCallbacks[seq]; return new Error(`ERROR sendRequest ws.send: ${errorMessage}`); } this.emit('request', appRequestData); } /** * Sends an asynchronous request to the Rust server and returns a Promise that resolves when a response is received * or rejects when a timeout is reached. The request is formatted using the `AppRequest` protocol, and the server's * response is handled within the promise. If no response is received within the provided timeout, an error is * returned. * * @param {Omit<rpi.AppRequest, 'seq' | 'playerId' | 'playerToken'>} data - The data for the request, * excluding the `seq`, `playerId`, * and `playerToken`, which are * passed separately. * @param {string} playerId - The steamId of the player making the request. * @param {number} playerToken - The authentication token of the player making the request. * @param {number} [timeoutMs=10000] - The timeout in milliseconds to wait for a response before returning an error. * * @returns {Promise<rpi.AppResponse | Error>} A Promise that resolves to the server's response if * successful, or an Error if the request times out or encounters * an issue. */ sendRequestAsync(data, playerId, playerToken, timeoutMs = 10000) { return new Promise((resolve) => { const seq = this.getNextSeq(); const timeout = setTimeout(() => { delete this.seqCallbacks[seq]; resolve(new Error('Timeout reached while waiting for response.')); }, timeoutMs); const result = this.sendRequest(data, playerId, playerToken, (appMessage) => { clearTimeout(timeout); try { if (!validation.isValidAppMessage(appMessage, this.logger)) { throw new Error('appMessage is invalid format.'); } if (!appMessage.response) { throw new Error('appMessage is missing response.'); } const response = appMessage.response; if (response.error) { resolve(response); } else { resolve(response); } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; resolve(new Error(`ERROR sendRequestAsync: ${errorMessage}`)); } }, seq); if (result instanceof Error) { clearTimeout(timeout); resolve(result); } }); } /** * Translates an `AppResponse` error object into a corresponding `AppResponseError` enum value. * If the `appResponse` contains no error, it returns `AppResponseError.NoError`. * If the `appResponse.error` matches any known error string, the function maps it to the appropriate * `AppResponseError` enum. If no matching error string is found, it returns `AppResponseError.Unknown`. * * @param {rpi.AppResponse} appResponse - The response object from the Rust server which may contain an error. * * @returns {AppResponseError} The corresponding `AppResponseError` enum value based on the error string in the * `appResponse.error` object. Returns `AppResponseError.NoError` if no error is * present. */ getAppResponseError(appResponse) { if (!appResponse.error) { return AppResponseError.NoError; } switch (appResponse.error.error) { case 'server_error': { return AppResponseError.ServerError; } case 'banned': { return AppResponseError.Banned; } case 'rate_limit': { return AppResponseError.RateLimit; } case 'not_found': { return AppResponseError.NotFound; } case 'wrong_type': { return AppResponseError.WrongType; } case 'no_team': { return AppResponseError.NoTeam; } case 'no_clan': { return AppResponseError.NoClan; } case 'no_map': { return AppResponseError.NoMap; } case 'no_camera': { return AppResponseError.NoCamera; } case 'no_player': { return AppResponseError.NoPlayer; } case 'access_denied': { return AppResponseError.AccessDenied; } case 'player_online': { return AppResponseError.PlayerOnline; } case 'invalid_playerid': { return AppResponseError.InvalidPlayerid; } case 'invalid_id': { return AppResponseError.InvalidId; } case 'invalid_motd': { return AppResponseError.InvalidMotd; } case 'too_many_subscribers': { return AppResponseError.TooManySubscribers; } case 'not_enabled': { return AppResponseError.NotEnabled; } case 'message_not_sent': { return AppResponseError.MessageNotSent; } default: { return AppResponseError.Unknown; } } } /** * Rust+ API requests. */ /** * Get the server info (callback version). * Consumes tokens and then sends a request to retrieve server information. * * @param {string} playerId - The steamId of the player making the request. * @param {number} playerToken - The authentication token of the player making the request. * @param {CallbackFunction} callback - The callback function that will be executed when a response is received. * @param {boolean} [waitForReplenish=true] - If true, wait until tokens are available before proceeding with the * request. * * @returns {Promise<ConsumeTokensError | Error | void>} Returns an error if token consumption fails or if there is * an issue sending the request, otherwise returns void. */ async getInfo(playerId, playerToken, callback, waitForReplenish = true) { const tokenCost = RustPlus.REQUEST_GET_INFO_TOKEN_COST; const result = await this.consumeTokens(playerId, tokenCost, waitForReplenish); if (result !== ConsumeTokensError.NoError) return result; return this.sendRequest({ getInfo: {} }, playerId, playerToken, callback); } /** * Get the server info (async version). * Consumes tokens and then sends a request to retrieve server information, returning a promise. * * @param {string} playerId - The steamId of the player making the request. * @param {number} playerToken - The authentication token of the player making the request. * @param {boolean} [waitForReplenish=true] - If true, wait until tokens are available before proceeding with the * request. * @param {number} [timeoutMs=10000] - Time (in milliseconds) to wait for a response before timing out. * * @returns {Promise<ConsumeTokensError | Error | rpi.AppResponse>} Returns a promise that resolves to the * server info (AppResponse), or an error if * token consumption fails or there is an * issue with the request. */ async getInfoAsync(playerId, playerToken, waitForReplenish = true, timeoutMs = RustPlus.REQUEST_GET_INFO_TIMEOUT_MS) { const tokenCost = RustPlus.REQUEST_GET_INFO_TOKEN_COST; const result = await this.consumeTokens(playerId, tokenCost, waitForReplenish); if (result !== ConsumeTokensError.NoError) return result; const appResponse = await this.sendRequestAsync({ getInfo: {} }, playerId, playerToken, timeoutMs); if (validation.isValidAppResponse(appResponse)) { return appResponse; } else { return appResponse; } } /** * Get the server time (callback version). * Consumes tokens and then sends a request to retrieve server time. * * @param {string} playerId - The steamId of the player making the request. * @param {number} playerToken - The authentication token of the player making the request. * @param {CallbackFunction} callback - The callback function that will be executed when a response is received. * @param {boolean} [waitForReplenish=true] - If true, wait until tokens are available before proceeding with the * request. * * @returns {Promise<ConsumeTokensError | Error | void>} Returns an error if token consumption fails or if there is * an issue sending the request, otherwise returns void. */ async getTime(playerId, playerToken, callback, waitForReplenish = true) { const tokenCost = RustPlus.REQUEST_GET_TIME_TOKEN_COST; const result = await this.consumeTokens(playerId, tokenCost, waitForReplenish); if (result !== ConsumeTokensError.NoError) return result; return this.sendRequest({ getTime: {} }, playerId, playerToken, callback); } /** * Get the server time (async version). * Consumes tokens and then sends a request to retrieve server time, returning a promise. * * @param {string} playerId - The steamId of the player making the request. * @param {number} playerToken - The authentication token of the player making the request. * @param {boolean} [waitForReplenish=true] - If true, wait until tokens are available before proceeding with the * request. * @param {number} [timeoutMs=10000] - Time (in milliseconds) to wait for a response before timing out. * * @returns {Promise<ConsumeTokensError | Error | rpi.AppResponse>} Returns a promise that resolves to the * server time (AppResponse), or an error if * token consumption fails or there is an * issue with the request. */ async getTimeAsync(playerId, playerToken, waitForReplenish = true, timeoutMs = RustPlus.REQUEST_GET_TIME_TIMEOUT_MS) { const tokenCost = RustPlus.REQUEST_GET_TIME_TOKEN_COST; const result = await this.consumeTokens(playerId, tokenCost, waitForReplenish); if (result !== ConsumeTokensError.NoError) return result; const appResponse = await this.sendRequestAsync({ getTime: {} }, playerId, playerToken, timeoutMs); if (validation.isValidAppResponse(appResponse)) { return appResponse; } else { return appResponse; } } /** * Get the server map (callback version). * Consumes tokens and then sends a request to retrieve server map. * * @param {string} playerId - The steamId of the player making the request. * @param {number} playerToken - The authentication token of the player making the request. * @param {CallbackFunction} callback - The callback function that will be executed when a response is received. * @param {boolean} [waitForReplenish=true] - If true, wait until tokens are available before proceeding with the * request. * * @returns {Promise<ConsumeTokensError | Error | void>} Returns an error if token consumption fails or if there is * an issue sending the request, otherwise returns void. */ async getMap(playerId, playerToken, callback, waitForReplenish = true) { const tokenCost = RustPlus.REQUEST_GET_MAP_TOKEN_COST; const result = await this.consumeTokens(playerId, tokenCost, waitForReplenish); if (result !== ConsumeTokensError.NoError) return result; return this.sendRequest({ getMap: {} }, playerId, playerToken, callback); } /** * Get the server map (async version). * Consumes tokens and then sends a request to retrieve server map, returning a promise. * * @param {string} playerId - The steamId of the player making the request. * @param {number} playerToken - The authentication token of the player making the request. * @param {boolean} [waitForReplenish=true] - If true, wait until tokens are available before proceeding with the * request. * @param {number} [timeoutMs=10000] - Time (in milliseconds) to wait for a response before timing out. * * @returns {Promise<ConsumeTokensError | Error | rpi.AppResponse>} Returns a promise that resolves to the * server map (AppResponse), or an error if * token consumption fails or there is an * issue with the request. */ async getMapAsync(playerId, playerToken, waitForReplenish = true, timeoutMs = RustPlus.REQUEST_GET_MAP_TIMEOUT_MS) { const tokenCost = RustPlus.REQUEST_GET_MAP_TOKEN_COST; const result = await this.consumeTokens(playerId, tokenCost, waitForReplenish); if (result !== ConsumeTokensError.NoError) return result; const appResponse = await this.sendRequestAsync({ getMap: {} }, playerId, playerToken, timeoutMs); if (validation.isValidAppResponse(appResponse)) { return appResponse; } else { return appResponse; } } /** * Get the team info (callback version). * Consumes tokens and then sends a request to retrieve team information. * * @param {string} playerId - The steamId of the player making the request. * @param {number} playerToken - The authentication token of the player making the request. * @param {CallbackFunction} callback - The callback function that will be executed when a response is received. * @param {boolean} [waitForReplenish=true] - If true, wait until tokens are available before proceeding with the * request. * * @returns {Promise<ConsumeTokensError | Error | void>} Returns an error if token consumption fails or if there is * an issue sending the request, otherwise returns void. */ async getTeamInfo(playerId, playerToken, callback, waitForReplenish = true) { const tokenCost = RustPlus.REQUEST_GET_TEAM_INFO_TOKEN_COST; const result = await this.consumeTokens(playerId, tokenCost, waitForReplenish); if (result !== ConsumeTokensError.NoError) return result; return this.sendRequest({ getTeamInfo: {} }, playerId, playerToken, callback); } /** * Get the team info (async version). * Consumes tokens and then sends a request to retrieve team information, returning a promise. * * @param {string} playerId - The steamId of the player making the request. * @param {number} playerToken - The authentication token of the player making the request. * @param {boolean} [waitForReplenish=true] - If true, wait until tokens are available before proceeding with the * request. * @param {number} [timeoutMs=10000] - Time (in milliseconds) to wait for a response before timing out. * * @returns {Promise<ConsumeTokensError | Error | rpi.AppResponse>} Returns a promise that resolves to the * team info (AppResponse), or an error if * token consumption fails or there is an * issue with the request. */ async getTeamInfoAsync(playerId, playerToken, waitForReplenish = true, timeoutMs = RustPlus.REQUEST_GET_TEAM_INFO_TIMEOUT_MS) { const tokenCost = RustPlus.REQUEST_GET_TEAM_INFO_TOKEN_COST; const result = await this.consumeTokens(playerId, tokenCost, waitForReplenish); if (result !== ConsumeTokensError.NoError) return result; const appResponse = await this.sendRequestAsync({ getTeamInfo: {} }, playerId, playerToken, timeoutMs); if (validation.isValidAppResponse(appResponse)) { return appResponse; } else { return appResponse; } } /** * Get the team chat (callback version). * Consumes tokens and then sends a request to retrieve team chat. * * @param {string} playerId - The steamId of the player making the request. * @param {number} playerToken - The authentication token of the player making the request. * @param {CallbackFunction} callback - The callback function that will be executed when a response is received. * @param {boolean} [waitForReplenish=true] - If true, wait until tokens are available before proceeding with the * request. * * @returns {Promise<ConsumeTokensError | Error | void>} Returns an error if token consumption fails or if there is * an issue sending the request, otherwise returns void. */ async getTeamChat(playerId, playerToken, callback, waitForReplenish = true) { const tokenCost = RustPlus.REQUEST_GET_TEAM_CHAT_TOKEN_COST; const result = await this.consumeTokens(playerId, tokenCost, waitForReplenish); if (result !== ConsumeTokensError.NoError) return result; return this.sendRequest({ getTeamChat: {} }, playerId, playerToken, callback); } /** * Get the team chat (async version). * Consumes tokens and then sends a request to retrieve team chat, returning a promise. * * @param {string} playerId - The steamId of the player making the request. * @param {number} playerToken - The authentication token of the player making the request. * @param {boolean} [waitForReplenish=true] - If true, wait until tokens are available before proceeding with the * request. * @param {number} [timeoutMs=10000] - Time (in milliseconds) to wait for a response before timing out. * * @returns {Promise<ConsumeTokensError | Error | rpi.AppResponse>} Returns a promise that resolves to the * team chat (AppResponse), or an error if * token consumption fails or there is an * issue with the request. */ async getTeamChatAsync(playerId, playerToken, waitForReplenish = true, timeoutMs = RustPlus.REQUEST_GET_TEAM_CHAT_TIMEOUT_MS) { const tokenCost = RustPlus.REQUEST_GET_TEAM_CHAT_TOKEN_COST; const result = await this.consumeTokens(playerId, tokenCost, waitForReplenish); if (result !== ConsumeTokensError.NoError) return result; const appResponse = await this.sendRequestAsync({ getTeamChat: {} }, playerId, playerToken, timeoutMs); if (validation.isValidAppResponse(appResponse)) { return appResponse; } else { return appResponse; } } /** * Send a team message (callback version). * Consumes tokens and then sends a request to send team message. * * @param {string} playerId - The steamId of the player making the request. * @param {number} playerToken - The authentication token of the player making the request. * @param {string} message - The message to be sent in the team chat. * @param {CallbackFunction} callback - The callback function that will be executed when a response is received. * @param {boolean} [waitForReplenish=true] - If true, wait until tokens are available before proceeding with the * request. * * @returns {Promise<ConsumeTokensError | Error | void>} Returns an error if token consumption fails or if there is * an issue sending the request, otherwise returns void. */ async sendTeamMessage(playerId, playerToken, message, callback, waitForReplenish = true) { const tokenCost = RustPlus.REQUEST_SEND_TEAM_MESSAGE_TOKEN_COST; const result = await this.consumeTokens(playerId, tokenCost, waitForReplenish); if (result !== ConsumeTokensError.NoError) return result; return this.sendRequest({ sendTeamMessage: { message: message } }, playerId, playerToken, callback); } /** * Send a team message (async version). * Consumes tokens and then sends a request to send team message, returning a promise. * * @param {string} playerId - The steamId of the player making the request. * @param {number} playerToken - The authentication token of the player making the request. * @param {string} message - The message to be sent in the team chat. * @param {boolean} [waitForReplenish=true] - If true, wait until tokens are available before proceeding with the * request. * @param {number} [timeoutMs=10000] - Time (in milliseconds) to wait for a response before timing out. * * @returns {Promise<ConsumeTokensError | Error | rpi.AppResponse>} Returns a promise that resolves to the * send team message (AppResponse), or an * error if token consumption fails or there * is an issue with the request. */ async sendTeamMessageAsync(playerId, playerToken, message, waitForReplenish = true, timeoutMs = RustPlus.REQUEST_SEND_TEAM_MESSAGE_TIMEOUT_MS) { const tokenCost = RustPlus.REQUEST_SEND_TEAM_MESSAGE_TOKEN_COST; const result = await this.consumeTokens(playerId, tokenCost, waitForReplenish); if (result !== ConsumeTokensError.NoError) return result; const appResponse = await this.sendRequestAsync({ sendTeamMessage: { message: message } }, playerId, playerToken, timeoutMs); if (validation.isValidAppResponse(appResponse)) { return appResponse; } else { return appResponse; } } /** * Get entity info (callback version). * Consumes tokens and then sends a request to retrieve entity information. * * @param {string} playerId - The steamId of the player making the request. * @param {number} playerToken - The authentication token of the player making the request. * @param {number} entityId - The ID of the entity to retrieve information about. * @param {CallbackFunction} callback - The callback function that will be executed when a response is received. * @param {boolean} [waitForReplenish=true] - If true, wait until tokens are available before proceeding with the * request. * * @returns {Promise<ConsumeTokensError | Error | void>} Returns an error if token consumption fails or if there is * an issue sending the request, otherwise returns void. */ async getEntityInfo(playerId, playerToken, entityId, callback, waitForReplenish = true) { const tokenCost = RustPlus.REQUEST_GET_ENTITY_INFO_TOKEN_COST; const result = await this.consumeTokens(playerId, tokenCost, waitForReplenish); if (result !== ConsumeTokensError.NoError) return result; return this.sendRequest({ entityId: entityId, getEntityInfo: {} }, playerId, playerToken, callback); } /** * Get entity info (async version). * Consumes tokens and then sends a request to retrieve entity information, returning a promise. * * @param {string} playerId - The steamId of the player making the request. * @param {number} playerToken - The authentication token of the player making the request. * @param {number} entityId - The ID of the entity to retrieve information about. * @param {boolean} [waitForReplenish=true] - If true, wait until tokens are available before proceeding with the * request. * @param {number} [timeoutMs=10000] - Time (in milliseconds) to wait for a response before