Defining Commands with IRocketCommand
Commands are how players and administrators interact with RocketMod plugins. Every command in RocketMod is a class that implements the IRocketCommand interface. RocketMod's command manager automatically discovers these classes in the plugin's assembly and registers them when the plugin loads. No manual registration step is needed.
This article covers the IRocketCommand interface in detail — every property, every method, every option for controlling who can run a command, what arguments it accepts, how it responds to errors, and how it interacts with RocketMod's command infrastructure. The 57 Studios™ team has written dozens of RocketMod commands across our plugin suite, and the patterns documented here are drawn from production code that handles real player traffic.

Prerequisites
- A working RocketMod plugin. See Creating Your First RocketMod Plugin.
- Familiarity with C# interfaces and class implementation.
- A test server to deploy and test commands.
The IRocketCommand interface
The interface defines six properties and one method. Every command must implement all of them.
csharp
public interface IRocketCommand
{
string Name { get; }
string Help { get; }
string Syntax { get; }
List<string> Aliases { get; }
string[] Permissions { get; }
AllowedCaller AllowedCaller { get; }
void Execute(IRocketPlayer caller, string[] command);
}Property reference
| Property | Required | Type | Purpose |
|---|---|---|---|
Name | Yes | string | The primary command keyword — what players type after / |
Help | Yes | string | Short description shown in /help listings |
Syntax | Yes | string | Usage format shown in /help <command> |
Aliases | Yes | List<string> | Alternative keywords that also trigger this command |
Permissions | Yes | string[] | Permission strings required to run the command |
AllowedCaller | Yes | AllowedCaller | Restricts who can execute the command |
The AllowedCaller enum
The AllowedCaller enum controls whether a command can be used by players in-game, from the server console, or both. RocketMod defines four values in the enum:
| Value | Behavior | Use case |
|---|---|---|
Player | Command only executes when invoked by a connected player from in-game chat | Player-facing commands: teleport, heal, give |
Console | Command only executes from the server console | Administrative commands: shutdown, ban, plugin management |
Both | Command executes from both in-game chat and server console | Commands safe in both contexts: status, help, time |
Server | Command executes from the server process itself, outside of player or console context | Internal automation and cross-plugin communication |
The Server value is useful when a command needs to be invoked programmatically by another plugin or by the server's internal systems without requiring a player or console actor. When AllowedCaller is set to Server, the command does not appear in /help listings and cannot be typed by a player or administrator. It is registered solely for programmatic invocation through R.Commands.Execute().
csharp
public AllowedCaller AllowedCaller => AllowedCaller.Server;Choosing the right AllowedCaller
The rule of thumb is to restrict as much as possible. If a command modifies player inventory or teleports a player, it should only work for Player callers, because the console has no meaningful player context. If a command shuts down the server, it should only work from Console so a malicious player cannot trigger it. Only use Both when the command is purely informational and does not depend on player context.
| Command type | Recommended AllowedCaller |
|---|---|
| Teleport, heal, give, kill | Player |
| Kick, ban, shutdown | Console |
| Help, status, ping, online | Both |
| Internal programmatic trigger | Server |
AllowedCaller and the caller parameter
The Execute method receives an IRocketPlayer parameter. The concrete implementation differs based on who called the command:
- When called by a player:
calleris anUnturnedPlayerinstance with access to.SteamId,.DisplayName,.CharacterName,.IsAdmin, and player-specific methods like.Heal(),.Teleport(),.GiveItem(). - When called by the console:
calleris aConsolePlayerinstance. It has.DisplayName(returns "Console") and a limited set of properties. Calling player-specific methods throwsNotSupportedException. - When called by Server context:
calleris aServerActorinstance with minimal properties. It represents the server process itself and has no player or console context.
Always check the AllowedCaller configuration, but also guard against invalid assumptions in Execute:
csharp
public void Execute(IRocketPlayer caller, string[] command)
{
// Safe type check for player-specific operations
if (caller is UnturnedPlayer player)
{
player.Heal(100);
UnturnedChat.Say(caller, "You have been healed.", Color.green);
}
else if (caller is ConsolePlayer)
{
UnturnedChat.Say(caller, "This command can only be used by players.", Color.red);
}
}The Name property
The Name property defines the primary keyword that triggers the command. Players type / followed by this name to invoke the command.
csharp
public string Name => "heal";Naming conventions
- Use lowercase letters only. RocketMod performs a case-insensitive comparison, so
/Heal,/HEAL, and/healall work. However, using mixed case in the property value is confusing when the command appears in/helplistings. - Use single words where possible. Multi-word names like
giveitemare harder to remember than single-word aliases. - Avoid special characters. RocketMod's command parser splits on spaces, so a name with a space never matches the full string.
- Avoid names that conflict with Unturned's built-in commands. Unturned's
/give,/teleport, and/vare reserved. If your plugin registers a command with the same name as an Unturned built-in, RocketMod's command manager overrides the built-in behavior, which may confuse server administrators.
Name collision between plugins
If two plugins register a command with the same Name, RocketMod uses a priority system to determine which plugin's implementation runs. By default, the first-loaded plugin wins. Command priority is covered in detail in Command Mapping and Priority.
The Help property
The Help property provides a one-line description of the command. It appears when a player types /help or /help <commandname>.
csharp
public string Help => "Heals a player to full health.";Guidelines for Help text:
- Keep it under 80 characters. The
/helpoutput formats commands in columns, and long help text wraps awkwardly. - Start with a capital letter and end without a period. RocketMod's help formatter adds punctuation styling.
- Describe what the command DOES, not what it is. "Heals a player to full health" is better than "This command is for healing."
- Do not repeat the command name. "Heals a player" not "Heal heals a player."
The Syntax property
The Syntax property shows the expected argument format. It appears when a player types /help <commandname> or when the command is used with missing or invalid arguments.
csharp
public string Syntax => "/heal <player>";Syntax notation
The 57 Studios™ team follows a standard notation for syntax strings:
- Required arguments:
<argument>— angle brackets, no brackets inside. - Optional arguments:
[argument]— square brackets. - Literal text: plain text like the command name.
- Choices:
<player|console>— pipe separated. - Repeatable:
[args...]— ellipsis suffix.
Examples:
/teleport <player> [destination]
/give <player> <item> <amount>
/kick <player> [reason...]
/announce <message>The syntax string should match what the Execute method expects in its command string array. If the command expects exactly two arguments, the syntax should show two required parameters.
The Aliases property
Aliases are alternative names that trigger the same command. If a player types an alias instead of the primary name, RocketMod redirects to the same Execute method.
csharp
private readonly List<string> _aliases = new List<string> { "health", "hp", "revive" };
public List<string> Aliases => _aliases;Alias guidelines
- Provide memorable short forms. If the primary name is
teleport, aliases oftpandtpoare helpful. - Do not create aliases that overlap with other commands on the server. Check with the server administrator if you are unsure which commands are already registered.
- Three to five aliases is a reasonable maximum. Too many aliases clutter the command namespace and increase the chance of collision.
Aliases and permissions
Aliases share the same permission requirements as the primary command. If myplugin.heal is required, it is required whether the user types /heal, /health, or /revive. There is no way to give an alias different permissions within the same command class — if you need different permissions for different names, create separate command classes.
The Permissions property
The Permissions property lists the permission strings required to run the command. If the array is empty, anyone can use the command. If it has one or more entries, the caller must have at least one of the listed permissions.
csharp
public string[] Permissions => new string[] { "myplugin.heal" };Empty permissions = no restriction
csharp
public string[] Permissions => new string[] { };Commands with empty permissions are visible in /help and executable by any player or console operator. Use this for informational commands like /help, /ping, /online.
Permission naming
The 57 Studios™ convention is to use <pluginname>.<commandname> — all lowercase, dot-separated. This convention prevents naming collisions and makes permissions readable in audits.
csharp
public string[] Permissions => new string[] { "myplugin.heal" };Permissions are checked in permissions.xml and assigned through groups. The permission system is covered in detail in Command Permissions and Cooldowns.
The Execute method
The Execute method is the command's body — the code that runs when a player or console operator invokes the command.
csharp
public void Execute(IRocketPlayer caller, string[] command)
{
// command[0] is the first argument after the command name
// If the user typed "/heal Notch", command = ["Notch"]
if (command.Length < 1)
{
UnturnedChat.Say(caller, "Usage: /heal <player>", Color.red);
return;
}
UnturnedPlayer target = U.Instance.Players.FindPlayer(command[0]);
if (target == null)
{
UnturnedChat.Say(caller, "Player not found.", Color.red);
return;
}
target.Heal(100);
UnturnedChat.Say(caller, $"Healed {target.DisplayName}.", Color.green);
}The command parameter
The command parameter is a string[] containing the arguments after the command name, split by spaces. If a user types /give Notch 5 10, the array is ["Notch", "5", "10"].
| Input | command array |
|---|---|
/heal | [] — empty array, no arguments |
/heal Notch | ["Notch"] |
/give Notch 5 10 | ["Notch", "5", "10"] |
/say Hello world! | ["Hello", "world!"] — note: no concatenation |
Quotation mark handling is limited. RocketMod does NOT support quoted strings as a single argument. /say "Hello world!" passes ["\"Hello", "world!\""] with the quotes included as part of the strings. If your command needs multi-word arguments, the caller must use an alternative syntax (e.g., -m "Hello world" flags) or you must concatenate the remaining arguments manually:
csharp
public void Execute(IRocketPlayer caller, string[] command)
{
if (command.Length < 1)
{
UnturnedChat.Say(caller, "Usage: /say <message>", Color.red);
return;
}
// Concatenate all arguments into one message
string message = string.Join(" ", command);
UnturnedChat.Say(message, Color.white);
}Return value is void
Execute returns void. There is no return code or error reporting mechanism through the interface. Errors are communicated to the caller by sending a chat message. Use Color.red for errors and Color.green for success messages to provide visual feedback.
Exception handling in Execute
Unhandled exceptions in Execute are caught by RocketMod's command manager and logged to Rocket.log. The player sees no error message unless the command explicitly sends one. Always wrap command logic in a try-catch:
csharp
public void Execute(IRocketPlayer caller, string[] command)
{
try
{
// Command logic
}
catch (Exception ex)
{
Rocket.Core.Logging.Logger.LogError(
$"[MyPlugin] Error in heal command: {ex.Message}");
UnturnedChat.Say(caller,
"An error occurred while executing the command.", Color.red);
}
}Complete command example
The following is a complete command that teleports a player to another player, with proper argument validation, caller type checking, and error handling:
csharp
using Rocket.API;
using Rocket.Unturned.Chat;
using Rocket.Unturned.Player;
using System.Collections.Generic;
using UnityEngine;
namespace MyPlugin.Commands
{
public class TeleportToPlayerCommand : IRocketCommand
{
public string Name => "tpto";
public string Help => "Teleport to another player.";
public string Syntax => "/tpto <player>";
public List<string> Aliases => new List<string> { "tp", "goto" };
public string[] Permissions => new string[]
{
"myplugin.tpto",
"myplugin.tp"
};
public AllowedCaller AllowedCaller => AllowedCaller.Player;
public void Execute(IRocketPlayer caller, string[] command)
{
try
{
// Validate arguments
if (command.Length < 1)
{
UnturnedChat.Say(caller,
"Usage: /tpto <player>", Color.red);
return;
}
// Ensure the caller is a player
if (!(caller is UnturnedPlayer player))
{
UnturnedChat.Say(caller,
"This command can only be used by players.", Color.red);
return;
}
// Find the target player
UnturnedPlayer target = U.Instance.Players.FindPlayer(command[0]);
if (target == null)
{
UnturnedChat.Say(caller,
$"Player '{command[0]}' not found.", Color.red);
return;
}
// Teleport
player.Teleport(target.Position, target.Rotation);
UnturnedChat.Say(caller,
$"Teleported to {target.DisplayName}.", Color.green);
}
catch (System.Exception ex)
{
Rocket.Core.Logging.Logger.LogError(
$"[MyPlugin] Teleport error: {ex.Message}");
UnturnedChat.Say(caller,
"An error occurred during teleport.", Color.red);
}
}
}
}Command discovery and registration
RocketMod discovers commands through assembly scanning. When a plugin loads, RocketMod's command manager scans every type in the plugin's assembly for classes that implement IRocketCommand. Each discovered class is instantiated and registered with the command name from the Name property.
Automatic registration
csharp
// RocketMod does this for you — no manual registration needed
// When MyPlugin loads, all IRocketCommand implementations are found
// and registered automatically.When commands are registered
Command registration happens during Load(). If the plugin's Load() method throws an exception, no commands from that plugin are registered. Commands are deregistered when the plugin unloads.
Logging command registration
RocketMod logs each discovered command at startup:
[Managed] Loaded command /heal from MyPlugin v1.0.0
[Managed] Loaded command /tpto from MyPlugin v1.0.0If a command does not appear in the logs, check that the class is public and implements IRocketCommand with the correct namespace. The interface is in Rocket.API.
Best practices
Validate arguments early
Check command.Length at the top of Execute and return immediately with a usage message if the required arguments are missing. This keeps the rest of the method unindented and readable.
Use the Syntax property for error messages
The Syntax string is the canonical usage format. When returning a usage error, include the Syntax value so the player sees the correct format even if they typed the command wrong.
csharp
if (command.Length < 1)
{
UnturnedChat.Say(caller,
$"Usage: {Syntax}", Color.red);
return;
}Log execution for administrative commands
Commands that have side effects (kick, ban, teleport, give) should log the action for the server operator's audit trail:
csharp
Rocket.Core.Logging.Logger.Log(
$"[MyPlugin] {caller.DisplayName} healed {target.DisplayName}.");Do not block the server
Execute runs on the server's main thread. Avoid long-running operations like web requests, heavy file I/O, or database queries inside Execute. If you need to perform an async operation, spawn a background task, but be aware that IRocketPlayer is not thread-safe and should not be accessed from a background thread.
Handle the ConsolePlayer case
Even if AllowedCaller is set to Both, guard against console callers calling player-specific operations:
csharp
if (caller is ConsolePlayer)
{
UnturnedChat.Say(caller, "This command requires a player context.", Color.red);
return;
}Common mistakes
Null reference on caller
If AllowedCaller is Both and a console user calls a command that accesses UnturnedPlayer-specific properties without a type check, a NullReferenceException is thrown. Always type-check caller before casting.
Arguments treated as one string
New plugin developers sometimes expect command to be a single string. It is a string[]. Access command[0] for the first argument, not the entire string.
Case-sensitive comparison on arguments
RocketMod's command matching is case-insensitive, but the arguments in the command array preserve the original casing. If you need to compare an argument to a literal, use StringComparison.OrdinalIgnoreCase:
csharp
if (command[0].Equals("all", StringComparison.OrdinalIgnoreCase))
{
// Heal all players
}Assuming command.Length checks pass
command.Length is zero when no arguments are supplied. Accessing command[0] without checking command.Length >= 1 throws an IndexOutOfRangeException.
Subcommand patterns
Some commands benefit from a subcommand structure where the first argument determines the operation. For example, /player might have subcommands like /player info, /player kick, /player warn.
Subcommand dispatch pattern
csharp
public void Execute(IRocketPlayer caller, string[] command)
{
if (command.Length < 1)
{
UnturnedChat.Say(caller, $"Usage: {Syntax}", Color.red);
return;
}
string subcommand = command[0].ToLowerInvariant();
string[] subArgs = command.Length > 1
? command.Skip(1).ToArray()
: new string[0];
switch (subcommand)
{
case "info":
HandleInfo(caller, subArgs);
break;
case "kick":
HandleKick(caller, subArgs);
break;
case "warn":
HandleWarn(caller, subArgs);
break;
default:
UnturnedChat.Say(caller,
$"Unknown subcommand '{command[0]}'. Use /player info, " +
"/player kick, or /player warn.",
Color.red);
break;
}
}
private void HandleInfo(IRocketPlayer caller, string[] args) { }
private void HandleKick(IRocketPlayer caller, string[] args) { }
private void HandleWarn(IRocketPlayer caller, string[] args) { }Subcommand permission grouping
When using subcommands, check permissions per-subcommand rather than at the command level. This lets server administrators grant or deny specific operations without exposing the entire command.
csharp
public void Execute(IRocketPlayer caller, string[] command)
{
if (command.Length < 1)
{
UnturnedChat.Say(caller, $"Usage: {Syntax}", Color.red);
return;
}
string subcommand = command[0].ToLowerInvariant();
// Check subcommand-specific permission
string subPerm = $"myplugin.player.{subcommand}";
if (!R.Permissions.HasPermission(caller, subPerm))
{
UnturnedChat.Say(caller,
$"You do not have permission for the '{subcommand}' subcommand.",
Color.red);
return;
}
// Dispatch to handler
}The corresponding permissions in permissions.xml:
xml
<Permission Cooldown="0">myplugin.player.info</Permission>
<Permission Cooldown="0">myplugin.player.kick</Permission>
<Permission Cooldown="0">myplugin.player.warn</Permission>When subcommands beat separate command classes
Use subcommands when the operations are conceptually related and share significant infrastructure (database connections, shared validation logic, common output formatting). Use separate command classes when the operations are distinct enough to warrant their own /help entries and individual permission management.
Argument validation patterns
Proper argument validation prevents runtime errors and gives clear feedback to the command user. The following patterns cover the most common validation scenarios.
Enum argument parsing
csharp
public void Execute(IRocketPlayer caller, string[] command)
{
if (command.Length < 1)
{
UnturnedChat.Say(caller, "Usage: /settime <day|night|dawn|dusk>", Color.red);
return;
}
if (!Enum.TryParse(typeof(EDayMode), command[0], true, out object mode))
{
UnturnedChat.Say(caller,
$"Invalid time mode '{command[0]}'. Use: day, night, dawn, or dusk.",
Color.red);
return;
}
EDayMode dayMode = (EDayMode)mode;
// Apply the time mode
}Numeric range validation
csharp
private int ParseAmount(string input, IRocketPlayer caller)
{
if (!int.TryParse(input, out int amount))
{
UnturnedChat.Say(caller, $"'{input}' is not a valid number.", Color.red);
return -1;
}
if (amount < 1 || amount > 100)
{
UnturnedChat.Say(caller, "Amount must be between 1 and 100.", Color.red);
return -1;
}
return amount;
}Player existence validation
csharp
private UnturnedPlayer FindTarget(string name, IRocketPlayer caller)
{
UnturnedPlayer target = U.Instance.Players.FindPlayer(name);
if (target == null)
{
UnturnedChat.Say(caller,
$"Player '{name}' is not online.", Color.red);
return null;
}
return target;
}Combined validation in a production command
csharp
public void Execute(IRocketPlayer caller, string[] command)
{
// Validate argument count
if (command.Length < 2)
{
UnturnedChat.Say(caller, $"Usage: {Syntax}", Color.red);
return;
}
// Validate target player
UnturnedPlayer target = FindTarget(command[0], caller);
if (target == null) return;
// Validate numeric argument
int amount = ParseAmount(command[1], caller);
if (amount < 0) return;
// All validation passed — execute
target.GiveItem(1001, amount);
UnturnedChat.Say(caller,
$"Gave {amount}x item 1001 to {target.DisplayName}.", Color.green);
}Command result reporting
RocketMod's IRocketCommand.Execute returns void, which means there is no built-in mechanism for reporting success, failure, or error codes to the caller. The convention is to communicate results through chat messages.
Result color conventions
The 57 Studios™ team follows a standardized color scheme for command feedback:
| Outcome | Color | Hex | Example |
|---|---|---|---|
| Success | Green | #00FF00 | "Healed Notch." |
| Error / failure | Red | #FF0000 | "Player not found." |
| Warning / cooldown | Yellow | #FFFF00 | "Please wait 30 seconds." |
| Informational | White | #FFFFFF | "5 players online." |
| System message | Magenta | #FF00FF | Configuration change notification |
Standardized message helper
csharp
public static class CommandResult
{
public static void Success(IRocketPlayer target, string message)
{
UnturnedChat.Say(target, message, Color.green);
}
public static void Error(IRocketPlayer target, string message)
{
UnturnedChat.Say(target, message, Color.red);
}
public static void Warning(IRocketPlayer target, string message)
{
UnturnedChat.Say(target, message, Color.yellow);
}
public static void Info(IRocketPlayer target, string message)
{
UnturnedChat.Say(target, message, Color.white);
}
public static void AdminAlert(string message)
{
foreach (var player in U.Instance.Players)
{
if (player.IsAdmin)
{
UnturnedChat.Say(player, message, Color.magenta);
}
}
}
}Logging results for audit
Administrative commands should log their results for the server's audit trail. The log entry should include who ran the command, what it did, and the target:
csharp
Rocket.Core.Logging.Logger.Log(
$"[Audit] {caller.DisplayName} ({caller.Id}) healed " +
$"{target.DisplayName} ({target.SteamId}) for {amount} HP.");Frequently asked questions
How do I make a command that works from both chat and console?
Set AllowedCaller to AllowedCaller.Both. Inside Execute, check whether caller is UnturnedPlayer or ConsolePlayer and branch accordingly. Do not assume player-specific methods are available.
What is the AllowedCaller.Server value used for?
AllowedCaller.Server is used for commands that are invoked programmatically by the server process itself or by cross-plugin automation systems. It is not accessible from in-game chat or the console. It is useful for scheduled tasks, internal event handlers, and plugins that need to expose an internal API without exposing a player-facing command.
Do I need to register my command class anywhere?
No. RocketMod automatically discovers all IRocketCommand implementations in the plugin's assembly. Class accessibility must be public, and the class must be in the same assembly as the plugin class.
Can one command class handle multiple command names?
The primary name is defined by Name. Alternate names go in Aliases. All names trigger the same Execute method. If you need different behavior for different names, create separate command classes.
How do I parse integer arguments?
Use int.TryParse() and return an error message if parsing fails:
csharp
if (!int.TryParse(command[1], out int amount))
{
UnturnedChat.Say(caller, "Amount must be a number.", Color.red);
return;
}Can I prevent a command from appearing in /help?
Set the Permissions array to non-empty and do not assign the permission to most players. Commands with required permissions still appear in /help but show as restricted. There is no built-in way to hide a command entirely from /help in RocketMod. For truly hidden commands, you can work around this by not implementing IRocketCommand and instead parsing chat messages manually.
Document history
| Version | Date | Author | Notes |
|---|---|---|---|
| 1.0 | 2026-07-27 | 57 Studios | Initial publication. Full IRocketCommand reference. |
