/**
* Represents a message to be sent to the server.
* @class
*/
class RequestMessage {
/** @type {number} */
id;
/** @type {number} */
version = 2;
/** @type {string} */
name;
/** @type {string} */
contentBody;
/** @type {RCONConnection} */
connection;
/**
* @param {RCONConnection} connection - The connection this message belongs to.
* @param {Object} options - The message configuration.
* @param {string} options.name - The RCON command name.
* @param {string|Object} [options.contentBody] - The RCON command body if applicable.
* @param {number} options.id - Internal request id.
*/
constructor(connection, options) {
this.connection = connection;
this.name = options.name;
this.id = options.id;
// Stringify the payload if it is an object, otherwise keep it as a string
this.contentBody = typeof options.contentBody === 'object' && options.contentBody !== null
? JSON.stringify(options.contentBody)
: options.contentBody;
}
/**
* Wraps this RequestMessage in an unencrypted buffer to be directly sent through the socket.
* @returns {Buffer} The formatted unencrypted buffer.
*/
toUnencryptedBuffer() {
const body = {
AuthToken: this.connection.authToken || "",
Version: this.version,
Name: this.name,
ContentBody: this.contentBody
};
const magicNumber = 0xDE450508; // Defined in API docs
const stringified = JSON.stringify(body);
// MN MN MN MN ID ID ID ID SL SL SL SL
const headerBuffer = Buffer.alloc(12);
headerBuffer.writeUInt32LE(magicNumber, 0);
headerBuffer.writeUInt32LE(this.id, 4);
headerBuffer.writeUInt32LE(stringified.length, 8);
return Buffer.concat([headerBuffer, Buffer.from(stringified)]);
}
/**
* Wraps this RequestMessage in an encrypted buffer to be directly sent through the socket.
* @returns {Buffer} The encrypted buffer ready for transmission.
* @throws {Error} Throws if the XOR key is missing.
*/
toBuffer() {
const unencryptedBuffer = this.toUnencryptedBuffer();
const encryptedBuffer = Buffer.alloc(unencryptedBuffer.length);
const xorKey = this.connection.xorKey;
// Ensure the key exists to prevent division by zero or undefined errors
if (!xorKey || xorKey.length === 0) {
throw new Error("XOR key is missing or empty.");
}
// Copy header over, unencrypted
for (let i = 0; i < 12; i++) {
encryptedBuffer[i] = unencryptedBuffer[i];
}
// Apply the XOR cipher to the content only
for (let i = 12; i < unencryptedBuffer.length; i++) {
encryptedBuffer[i] = unencryptedBuffer[i] ^ xorKey[(i - 12) % xorKey.length];
}
return encryptedBuffer;
}
}
module.exports = RequestMessage;