Jump to content
RAGE Multiplayer Community

1 Screenshot

About This File

spacer.png

This resource adds a very flexible command handler for serverside JavaScript.

 

Installing

Drag and drop the improved-commands folder to your server-files/packages/ folder.

NOTE: The archive also contains a folder named improved-commands-example, it's an example resource so don't install it along with the library.

 

Features

  • Command aliases
  • Capture command requests (and cancel them if you want to)
  • Somewhat customizable command not found message (like serverside C#)
  • beforeRun property that lets you do final checks before a command actually runs (see example)

 

API

Instead of extending the global mp object, I've decided to export CommandEvents and CommandRegistry objects. You can access to the library by doing:

const { CommandEvents, CommandRegistry } = require("../improved-commands");

 

API Properties

/**
 * This property is used to toggle the "command not found" message feature.
 * Default value: false
 */
CommandRegistry.notFoundMessageEnabled; // get
CommandRegistry.notFoundMessageEnabled = bool; // set

/**
 * This property is used to set the "command not found" message.
 * Default value: "SERVER: Command not found."
 */
CommandRegistry.notFoundMessage; // get
CommandRegistry.notFoundMessage = string; // set

 

API Functions

/**
 * Adds a command to the registry.
 * The command object must contain "name" (string) and "run" (function) properties at the very least.
 * Optionally, the command object can have "aliases" (array of strings) and "beforeRun" (function, needs to return true for further execution) properties.
 * Other properties will be collected into an "extra" (object) property, which is useful for storing data on commands.
 */
CommandRegistry.add(command);

/**
 * Returns all command names, aliases not included.
 * @return {string[]}
 */
CommandRegistry.getNames();

/**
 * Returns all command names with aliases.
 * @return {string[]}
 */
CommandRegistry.getNamesWithAliases();

/**
 * Returns the command object for the specified command name/alias.
 * @param  {string} commandName
 * @return {Object|undefined}   Will be undefined if there is no command with the given name/alias.
 */
CommandRegistry.find(commandName);

 

Events

/**
 * receive
 * This event is emitted when a player tries to run a command.
 * @param  {mp.Player}  player      The player who is trying to use the command.
 * @param  {Object}     command     The command object.
 * @param  {string}     fullText    All arguments that will be passed to the command, as one string.
 * @param  {string[]}   commandArgs All arguments that will be passed to the command.
 * @param  {Object}     cancel      Whether the execution should be cancelled. Set this object's cancel property to true to stop further processing of the command.
 */
CommandEvents.on("receive", function (player, command, fullText, commandArgs, cancel) {
    // your code
});

/**
 * fail
 * This event is emitted when an error is thrown during command execution.
 * @param  {mp.Player}  player      The player whose command execution failed.
 * @param  {Object}     command     The command object.
 * @param  {string}     fullText    All arguments that were be passed to the command, as one string.
 * @param  {string[]}   commandArgs All arguments that were be passed to the command.
 * @param  {Error}      error       The error that was thrown during command execution.
 */
CommandEvents.on("fail", function (player, command, fullText, commandArgs, error) {
    // your code
});

 

Example

const { CommandEvents, CommandRegistry } = require("../improved-commands");

// Should we inform the player when they enter an invalid command? Probably...
// Note that commands added with mp.events.addCommand aren't known by this resource so they'll trigger the not found message
// This is disabled by default
CommandRegistry.notFoundMessageEnabled = true;

// Events
// Example: Players can't use commands in a vehicle
CommandEvents.on("receive", function (player, command, fullText, commandArgs, cancel) {
    if (player.vehicle) {
        player.outputChatBox("You cannot use commands in a vehicle.");
        cancel.cancel = true;
    }
});

// Example: Send a message to the player and print the error to the console on execution failure
CommandEvents.on("fail", function (player, command, fullText, commandArgs, error) {
    player.outputChatBox(`Failed to run command "${command.name}".`);
    console.error(error.stack || error);
});

// Commands
// Example: /argtest lorem ipsum dolor sit amet -> results in "You wrote: lorem ipsum dolor sit amet"
CommandRegistry.add({
    name: "argtest",
    aliases: ["echo", "combineargs"],
    beforeRun: function (player, fullText) {
        if (fullText.length === 0) {
            player.outputChatBox("No arguments provided.");
            return false;
        }

        return true;
    },
    run: function (player, fullText) {
        player.outputChatBox(`You wrote: ${fullText}`);
    }
});

// Example: /freemode_male_only -> will only work when player's model is mp_m_freemode_01
CommandRegistry.add({
    name: "freemode_male_only",
    beforeRun: function (player) {
        return player.model === mp.joaat("mp_m_freemode_01");
    },
    run: function (player) {
        player.outputChatBox("Yes, only freemode male can run this command.");
    }
});

// Example: /boom -> will emit "fail" event
CommandRegistry.add({
    name: "boom",
    run: function (player) {
        throw new Error("error thrown");
    }
});

// Properties that aren't named "name", "aliases", "beforeRun" or "run" will be collected into the "extra" property
// Example: /getweapon weapon_carbinerifle 500 -> will only work when player's adminLevel property value is equal to or higher than cmdAdminLevel extra property
CommandRegistry.add({
    name: "getweapon",
    aliases: ["giveweapon"],

    // You can access this property in handlers by using "this.extra.cmdAdminLevel" if the handlers are regular functions (meaning it doesn't work with arrow functions!)
    cmdAdminLevel: 5,

    beforeRun: function (player) {
        return player.adminLevel >= this.extra.cmdAdminLevel;
    },
    run: function (player, fullText, weaponName, ammo = 9999) {
        // You can do this in beforeRun as well (see argtest example)
        if (!weaponName || weaponName.length === 0) {
            player.outputChatBox("Syntax: /getweapon [name]");
            return;
        }

        player.giveWeapon(mp.joaat(weaponName), Number(ammo));
        player.outputChatBox(`Gave yourself ${weaponName} with ${ammo} ammo.`);
    }
});

// Example: Extra property #2
CommandRegistry.add({
    name: "count_runs",

    // You can access this property in handlers by using "this.extra.timesRan" if the handlers are regular functions (meaning it doesn't work with arrow functions!)
    timesRan: 0,

    beforeRun: function (player) {
        player.outputChatBox(`This command was used ${this.extra.timesRan} time(s).`);
        return true;
    },
    run: function (player) {
        this.extra.timesRan++;
        player.outputChatBox(`Now it's used ${this.extra.timesRan} time(s).`);
    }
});

// Example: List all commands
CommandRegistry.add({
    name: "commands",
    aliases: ["cmds"],
    run: function (player) {
        const commands = CommandRegistry.getNamesWithAliases();
        commands.sort();

        player.outputChatBox(`Commands: ${commands.join(", ")}`);
    }
});

// Example: Async beforeRun (v1.1 and above)
// Important: You should check if player object is still valid by mp.players.exists(player) after awaiting
// sleep function can be found here: https://stackoverflow.com/a/39914235
CommandRegistry.add({
    name: "async",
    beforeRun: async function (player) {
        // Getting data from slow API
        await sleep(5000);

        const result = Math.random() < 0.5;
        if (result) {
            player.outputChatBox("You're allowed...");
        } else {
            player.outputChatBox("You're not allowed...");
        }

        return result;
    },
    run: async function (player) {
        // Getting data from slow API again
        await sleep(2000);

        if (Math.random() < 0.5) {
            player.outputChatBox("You waited for nothing!");
        } else {
            throw new Error("Failed so bad it caused an error"); // should emit fail
        }
    }
});

 

Notes

  • This resource does not know about commands added with mp.events.addCommand or C# commands. Meaning if you're using the command not found message feature, addCommand and C# commands will also result in command not found message.
  • Commands are case insensitive.
  • Also on GitHub: https://github.com/root-cause/ragemp-improved-commands

Edited by rootcause


What's New in Version 1.1.1   See changelog

Released

  • Improved fullText performance while processing a command.

As a downside, additional spaces in the fullText argument of command handlers and events is no longer removed by the library and needs to be handled by scripters.

  • Like 3
  • Sad 1

User Feedback

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest

Captien

   3 of 3 members found this review helpful 3 / 3 members

I heard it bans you from the server when you type /me

Response from the author:

L16JXlK.png

  • Like 1
Link to comment
×
×
  • Create New...