Skip to content
RustPluginGenerator

OnUserCommand

OnUserCommand is called when a connected player types a chat command that the server does not recognize as a registered chat command. Returning a non-null value blocks the default handling of the command.

Signature
object OnUserCommand(IPlayer iplayer, string cmd, string[] args)

Parameters

Hook parameters
ParameterTypeDescription
iplayerIPlayerThe Oxide IPlayer wrapper of the connected player who typed the command in chat. Use iplayer.Id, iplayer.Name, or iplayer.Object to reach the BasePlayer.
cmdstringThe command name without the leading slash, e.g. "kit" for /kit. Parsing happens before the hook, so this is already the first token only.
argsstring[]The remaining tokens split on whitespace, or an empty array when the player typed only the command. Null-check before indexing.

Return value

Return null to let the command proceed through the normal path (registered chat commands, console fallback). Return any non-null object (a string, true, an empty string) to signal that the command was handled and should not continue. If OnPlayerCommand already returned non-null for the same input, its value takes precedence and this hook's return is ignored.

How it works

This is an Oxide-level hook, not a patched game method. It fires from RustCore.TryRunPlayerCommand after the message is sanitized, the prefix is stripped, and the remaining text is split into cmd and args. It only runs for connected players (IsConnected); players using the Rust+ app go through OnApplicationCommand instead.

The hook runs next to OnPlayerCommand, and OnPlayerCommand has priority: if that hook returns something non-null, Oxide uses it as the block result and the value from OnUserCommand is not consulted. Either way, a non-null result stops the vanilla processing of the input for that method.

iplayer is a Covalence/Oxide IPlayer, which is stable across framework versions but requires casting to BasePlayer for game APIs (iplayer.Object as BasePlayer). cmd is already lowercase as typed, so compare with string.Equals(cmd, "name", StringComparison.OrdinalIgnoreCase). args can be empty, never null in practice, but guard it anyway. Use this hook for custom command routers, blocked-command lists, or cross-plugin command bridging; the hook fires for every typed command, so keep the body cheap and avoid logging inside it.

Example plugin: Route custom chat commands to a configurable set of shortnames

CommandRouter.cs
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Command Router", "RustPluginGenerator", "1.0.0")]
    [Description("Routes typed chat commands to console commands or replies based on a configurable table, and blocks others.")]
    public class CommandRouter : RustPlugin
    {
        private const string PermUse = "commandrouter.use";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Routes (command => target)")]
            public Dictionary<string, Route> Routes = new Dictionary<string, Route>
            {
                ["discord"] = new Route { Reply = "Join us at discord.gg/example" },
                ["starter"] = new Route { Console = "kit givetoplayer {id} starter", Permission = "kit.starter" },
                ["where"] = new Route { Console = "printpos" }
            };

            [JsonProperty("Blocked routes reply")]
            public string BlockedReply = "You do not have permission to use /{0}.";
        }

        public class Route
        {
            [JsonProperty("Console command ({id} = player id)")]
            public string Console;

            [JsonProperty("Chat reply")]
            public string Reply;

            [JsonProperty("Permission")]
            public string Permission;
        }

        private void Init()
        {
            permission.RegisterPermission(PermUse, this);
        }

        protected override void LoadDefaultConfig() => config = new PluginConfig();

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<PluginConfig>();
                if (config == null) throw new Exception();
            }
            catch
            {
                PrintWarning("Configuration file is invalid, using defaults");
                LoadDefaultConfig();
            }
            SaveConfig();
        }

        protected override void SaveConfig() => Config.WriteObject(config);

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["Unknown"] = "Unknown command '/{0}'. Try /help."
            }, this);
        }

        private string Msg(string key, string userId = null, params object[] args)
            => string.Format(lang.GetMessage(key, this, userId), args);

        private object OnUserCommand(IPlayer iplayer, string cmd, string[] args)
        {
            if (iplayer == null || !iplayer.IsConnected) return null;

            Route route;
            if (cmd == null || !config.Routes.TryGetValue(cmd.ToLowerInvariant(), out route) || route == null)
                return null;

            var player = iplayer.Object as BasePlayer;
            if (player == null) return null;

            if (!string.IsNullOrEmpty(route.Permission) && !permission.UserHasPermission(player.UserIDString, route.Permission))
            {
                player.ChatMessage(string.Format(config.BlockedReply, cmd));
                return true;
            }

            if (!string.IsNullOrEmpty(route.Console))
            {
                string line = route.Console.Replace("{id}", player.UserIDString);
                player.SendConsoleCommand(line);
            }

            if (!string.IsNullOrEmpty(route.Reply))
                player.ChatMessage(route.Reply);

            return true;
        }
    }
}

The plugin gives admins a single hook point for typed chat commands. A config maps a command word to a target: either a console command string (run as the player) or a permission-gated message. When OnUserCommand fires, the plugin looks the command up case-insensitively, refuses it if a permission field is set and the player lacks it, then either runs SendConsoleCommand as the player or replies through the language file. Returning a non-null value tells the server the command was handled, so vanilla has no chance to interpret it. Unknown commands return null, so normal /kit, /tpr and similar plugin commands keep working untouched.

Common use cases

  • Block rude or abusable commands per group

    Maintain a short list of blocked command words in config and return a non-null value for players without a bypass permission, with a chat reply telling them why.

  • Bridge chat commands to console commands

    Let staff run SendConsoleCommand targets through a short alias typed in chat, while still letting the player see replies through normal console output.

  • Custom usage tips

    Detect typo'd commands and return a suggested alternative, so players typing /kits are pointed at /kit instead of seeing a generic unknown-command message.

Pitfalls

  • OnPlayerCommand wins

    If another plugin returns non-null from OnPlayerCommand for the same input, its result decides. Do not rely on this hook alone to enforce blocking when both hooks are in use.

  • Only connected players

    The hook never fires for Rust+ app usage (IsConnected false); that path goes through OnApplicationCommand. If you need to cover mobile, hook that separately.

  • Fires for every command

    Even trivial ones like /kit typed by a player with 20 plugins are dispatched through here. Avoid expensive work (database reads, full player list scans) in the body; cache what you can at load time.

Build a plugin with OnUserCommand

Describe what should happen and get a compiled plugin that uses this hook.

Prompt: Block rude or abusable commands per group using the OnUserCommand hook, with a config file and a permission
Generate this plugin

Edit the prompt on the next screen. 2 free plugins a month, no card.

Last updated Oct 26, 2026. Found a mistake? Email support@rustplugingenerator.com.

Build a plugin with OnUserCommand

Generate