Skip to content
RustPluginGenerator

Oxide Chat Commands vs Console Commands: [ChatCommand], [ConsoleCommand] and [Command]

An Oxide chat command is a method marked [ChatCommand("name")] that players run by typing /name in chat. A console command is marked [ConsoleCommand("plugin.action")], receives a ConsoleSystem.Arg, and also works from the F1 console, RCON, the server console and CUI buttons. Covalence [Command] is a third, framework-neutral style that uses IPlayer. This guide shows when to use each, with argument validation, permission checks and a working example.

5 min readUpdated Oct 26, 2026

An Oxide chat command is a method with the [ChatCommand("name")] attribute that players run by typing /name in chat, and it always has a BasePlayer caller. A console command uses [ConsoleCommand("plugin.action")] and receives a ConsoleSystem.Arg. It works from the F1 console, RCON, the server console and CUI buttons, so the caller can be missing. Use chat commands for player-facing features and console commands for admin tooling, automation and UI buttons.

The three command styles

Chat commandConsole commandCovalence command
Attribute[ChatCommand("gift")][ConsoleCommand("giftcommand.give")][Command("gift")]
Signature(BasePlayer player, string command, string[] args)(ConsoleSystem.Arg arg)(IPlayer player, string command, string[] args)
Typed as/gift in chatgiftcommand.give in F1, RCON, server console/gift in chat, and from the console
CallerAlways an in-game playerarg.Player(), null from RCON or server consoleIPlayer, which can be the server
Reply withSendReply(player, text)arg.ReplyWith(text)player.Reply(text)
Works as CUI targetNoYesNo

Covalence commands need using Oxide.Core.Libraries.Covalence;. They are useful when you want one code path for several games or for the server console, but most Rust-only plugins stick with the first two styles. For the wider trade-offs see covalence vs RustPlugin.

Chat commands

C#
[ChatCommand("gift")]
private void CmdGift(BasePlayer player, string command, string[] args)
{
    // args holds the words after the command name
}

Rules to remember:

  • The attribute value has no slash. Players type the slash.
  • args is never null for chat commands, but it can be empty. Check args.Length before indexing, otherwise you get IndexOutOfRangeException in a command.
  • command is the name the player typed.
  • Chat command names share one namespace across all plugins on the server.

You can also register a chat command at runtime with cmd.AddChatCommand("gift", this, "CmdGift"), passing the name of the method as a string. This is handy when the command name comes from the config, for example a configurable alias. The attribute is simpler and should be your default.

Console commands

C#
[ConsoleCommand("giftcommand.give")]
private void CcmdGive(ConsoleSystem.Arg arg)
{
    var caller = arg.Player(); // null from server console or RCON
}

ConsoleSystem.Arg gives you:

  • arg.Player(): the calling BasePlayer, or null when the command comes from the server console or RCON.
  • arg.Args: the raw arguments, null when no arguments were given. In current Rust this is a Facepunch.StringView[], not a string[]: call .ToString() on an element, or use the typed getters below.
  • arg.GetString(index, default) and arg.GetInt(index, default): safe readers that return the default when the argument is missing or unparsable.
  • arg.IsAdmin: true for server console, RCON and admin players.
  • arg.ReplyWith(text): sends the reply back to whoever ran the command, including RCON.

You can register at runtime with cmd.AddConsoleCommand("giftcommand.give", this, "CcmdGive").

Naming conventions

Console commands should be pluginname.action in lowercase, for example giftcommand.give. The prefix prevents collisions with vanilla commands and other plugins. Use the same lowercase plugin name as your permission prefix. Keep chat commands short and human-friendly (/gift), because players type them by hand.

Parsing and validating arguments

Treat every argument as untrusted input, particularly for console commands that players can type themselves.

  1. Check the count first (args.Length, or arg.Args == null).
  2. Parse numbers with int.TryParse, never int.Parse.
  3. Clamp the result to a sane range.
  4. Look up players with BasePlayer.FindAwakeOrSleeping(nameOrId) and null-check the result.
  5. Look up items with ItemManager.CreateByName, which returns null for an unknown shortname.
  6. Reply with a usage message when anything fails instead of throwing.

An unhandled exception in a command is logged as "Failed to run command ..." with a stack trace, and the player sees nothing. A clear usage reply is better.

Permission checks

Neither attribute checks permissions for you. Register the permission in Init and check it at the top of the command:

  • Chat command: permission.UserHasPermission(player.UserIDString, PermUse).
  • Console command: only check when arg.Player() is not null. Server console and RCON callers are already trusted, so skipping the check for them is the normal pattern.

For admin-only commands, check a plugin permission rather than IsAdmin, so server owners can delegate it with oxide.grant. Details are in Oxide permissions and groups.

Replying to the caller

Send text through the lang API so server owners can translate it, as described in plugin localization. Pass the player's UserIDString to lang.GetMessage so the right language is chosen. For console replies from RCON there is no player, so pass null as the user id and the default language is used.

Running from RCON and the server console

When arg.Player() is null, there is no position, inventory or UserIDString. Do not touch the caller without a null check. Design admin commands so every target is named in the arguments, such as giftcommand.give <player> <item> [amount]. Then they work identically from chat-less contexts like scheduled command plugins and RCON tools.

Chat commands cannot be run from RCON directly, which is one more reason to put the real logic in a shared private method and call it from both a chat and a console command.

Console commands as CUI button targets

A CUI button runs a console command on the server when clicked:

C#
c.Add(new CuiButton
{
    Button = { Command = "giftcommand.give", Color = "0.2 0.6 0.2 1" },
    RectTransform = { AnchorMin = "0.4 0.05", AnchorMax = "0.6 0.2" },
    Text = { Text = "Claim", Align = TextAnchor.MiddleCenter }
}, panel);

Points that matter:

  • Only console commands work as button targets. Chat commands do not.
  • The click runs as the player, so arg.Player() is that player.
  • Arguments go after the name in the same string, for example myplugin.page 2.
  • Players can type any button command into F1 themselves, so always validate arguments and check permissions. A hidden button is not access control.
  • Destroy the panel in your close command and in Unload. See CUI basics.

Complete example

This plugin lets permitted players gift themselves an item with a chat command, and lets admins or RCON give items to any player with a console command. Both share one method.

C#
using System.Linq;
using UnityEngine;
using System;
using System.Collections.Generic;

namespace Oxide.Plugins
{
    [Info("Gift Command", "RustPluginGenerator", "1.0.0")]
    [Description("Gives items through a chat command and an admin console command")]
    public class GiftCommand : RustPlugin
    {
        private const string PermUse = "giftcommand.use";
        private const string PermAdmin = "giftcommand.admin";
        private const int MaxAmount = 1000;

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

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["NoPermission"] = "You do not have permission to use this command.",
                ["UsageChat"] = "Usage: /gift <shortname> [amount]",
                ["UsageConsole"] = "Usage: giftcommand.give <player> <shortname> [amount]",
                ["UnknownItem"] = "Unknown item: {0}",
                ["PlayerNotFound"] = "Player not found: {0}",
                ["Given"] = "Gave {0}x {1} to {2}."
            }, this);
        }

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

        [ChatCommand("gift")]
        private void CmdGift(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;
            if (!permission.UserHasPermission(player.UserIDString, PermUse))
            {
                SendReply(player, Msg("NoPermission", player.UserIDString));
                return;
            }
            if (args.Length < 1)
            {
                SendReply(player, Msg("UsageChat", player.UserIDString));
                return;
            }
            int amount = ParseAmount(args, 1);
            SendReply(player, GiveTo(player, args[0], amount, player.UserIDString));
        }

        [ConsoleCommand("giftcommand.give")]
        private void CcmdGive(ConsoleSystem.Arg arg)
        {
            var caller = arg.Player(); // null from server console or RCON
            string callerId = caller?.UserIDString;

            if (caller != null && !permission.UserHasPermission(caller.UserIDString, PermAdmin))
            {
                arg.ReplyWith(Msg("NoPermission", callerId));
                return;
            }
            if (arg.Args == null || arg.Args.Length < 2)
            {
                arg.ReplyWith(Msg("UsageConsole", callerId));
                return;
            }

            var args = new string[arg.Args.Length];
            for (int i = 0; i < arg.Args.Length; i++)
                args[i] = arg.Args[i].ToString();

            var target = BasePlayer.FindAwakeOrSleeping(args[0]);
            if (target == null)
            {
                arg.ReplyWith(Msg("PlayerNotFound", callerId, args[0]));
                return;
            }
            int amount = ParseAmount(args, 2);
            arg.ReplyWith(GiveTo(target, args[1], amount, callerId));
        }

        private int ParseAmount(string[] args, int index)
        {
            int amount;
            if (args.Length <= index || !int.TryParse(args[index], out amount)) return 1;
            return Math.Max(1, Math.Min(amount, MaxAmount));
        }

        private string GiveTo(BasePlayer target, string shortname, int amount, string replyUserId)
        {
            var item = ItemManager.CreateByName(shortname, amount);
            if (item == null) return Msg("UnknownItem", replyUserId, shortname);
            target.GiveItem(item);
            return Msg("Given", replyUserId, amount, shortname, target.displayName);
        }
    }
}

Both commands call GiveTo, so the logic exists once. The chat command trusts player because chat always has one, while the console command handles the null caller and requires a permission only for in-game callers.

Common mistakes

  • Using a chat command as a button target. Switch to a console command.
  • Dereferencing arg.Player() without a null check, which breaks RCON use.
  • Indexing args[0] before checking the length.
  • Choosing a generic name that another plugin already owns. See tried to register command, already exists.
  • Hard-coding reply text instead of using lang.

FAQ

Why does my chat command work in chat but not in the F1 console?

A [ChatCommand] is only dispatched from chat messages that start with a slash. Typing the same name in the F1 console does nothing. If players or admins need to run it from the console, RCON or a UI button, add a [ConsoleCommand] with a dotted name, or use a covalence [Command].

Can two plugins register the same command name?

Oxide refuses to register a command that already exists and logs a warning. Chat commands are a shared namespace across plugins, so generic names like home or kit collide easily. See tried to register command, already exists. Rename one command or use a prefixed console command.

Does a chat command need the leading slash in the attribute?

No. Write [ChatCommand("gift")] and players type /gift. Including the slash in the attribute is a common mistake and the command will not match. Console commands are written without a slash as well, for example giftcommand.give.

How do I give a CUI button an argument?

Put the argument after the command in the button's Command string, for example myplugin.page 2. In the console command, read it with arg.GetInt(0, 1) or arg.GetString(0, ""). Remember that the clicking player runs this command, so validate the value and check permissions.

Can I stop another plugin's or a vanilla chat command from running?

For chat input, OnPlayerChat and OnPlayerCommand let a plugin inspect the text before dispatch. Check the hook page for the return behavior before relying on it. This is how command alias and anti-spam plugins work.

Skip the C#: describe your plugin

Open generator