Command System and Registration
Unturned's command system is built on the abstract Command class and the Commander static registry. Commander.init() registers approximately 60 built-in commands in a sorted list. Each command has a _command name string, an _info description, and a _help detailed usage string — all loaded from localization .dat files. Commands execute with a CSteamID executorID parameter that identifies the caller for permission checks. The system supports runtime registration/deregistration for mods and a Unity event execution path with permission callbacks.
This article covers the Command base class, the Commander sorted-list registration pattern, permission checks (server-only, admin-only, owner-only), the help system localization, the Unity event permission model, and cataloging of all built-in command categories.
Source code location: Unturned/Command/Command.cs, Unturned/Command/Commander.cs, Unturned/Command/Command*.cs
Command Base Class
csharp
public class Command : IComparable<Command>
{
protected Local localization;
protected string _command;
public string command => _command;
protected string _info;
public string info => _info;
protected string _help;
public string help => _help;
protected virtual void execute(CSteamID executorID, string parameter) { }
public virtual bool check(CSteamID executorID, string method, string parameter)
{
if (method.ToLower() == command.ToLower())
{
execute(executorID, parameter);
return true;
}
return false;
}
public int CompareTo(Command other)
{
return command.CompareTo(other.command);
}
}check() performs case-insensitive name matching. When matched, it calls the virtual execute() method. The CompareTo implementation alphabetizes by command name for the sorted list insertion.
Subclasses override execute() to implement their logic. The executorID parameter is:
CSteamID.Nil— executed from the server console.- Valid Steam ID — executed via in-game chat by a player.
This duality allows commands to check who called them and behave differently — for example, CommandHelp outputs to CommandWindow for console calls and to ChatManager.say() for in-game calls.
Constructor Pattern
Every command subclass follows the same constructor pattern:
csharp
public CommandGameMode(Local newLocalization)
{
localization = newLocalization;
_command = localization.format("GameModeCommandText");
_info = localization.format("GameModeInfoText");
_help = localization.format("GameModeHelpText");
}Three localization keys are always used:
{Name}CommandText— the command name (invocation string).{Name}InfoText— the short description shown in the help listing.{Name}HelpText— the detailed usage help shown onhelp {command}.
Commander Registry
Commander is a static class managing the command list.
Registration
csharp
public static void register(Command command)
{
int insert = commands.BinarySearch(command);
if (insert < 0)
insert = ~insert;
commands.Insert(insert, command);
}register() uses BinarySearch to find the insertion index, maintaining alphabetical order. This allows O(log n) lookup potential, though the current execute() iterates linearly — the sorted list is primarily for the help display to show commands alphabetically.
deregister() removes a command by reference:
csharp
public static void deregister(Command command)
{
commands.Remove(command);
}This is the extension point for mods that want to replace or remove built-in commands.
Execution
csharp
public static bool execute(CSteamID executorID, string command)
{
try
{
string method = command;
string parameter = "";
int split = command.IndexOf(' ');
if (split != -1)
{
method = command.Substring(0, split);
parameter = command.Substring(split + 1, command.Length - split - 1);
}
for (int index = 0; index < commands.Count; index++)
{
if (commands[index].check(executorID, method, parameter))
{
return true;
}
}
}
catch (Exception e)
{
UnturnedLog.exception(e, "Caught exception while executing command string \"{0}\"", command);
}
return false;
}- Splits the input string into
method(first token) andparameter(everything after the first space). - Iterates through all registered commands, calling
check(). - Returns
trueon first match, orfalseif no command matched. - All exceptions are caught and logged to prevent a crashing command from bringing down the server.
Unity Event Execution
csharp
public static void execute_UnityEvent(string command, ServerTextChatMessenger messenger)
{
if (Dedicator.IsDedicatedServer && !Provider.configData.UnityEvents.Allow_Server_Commands)
{
UnturnedLog.info("Blocking UnityEvent command \"{0}\" because Allow_Server_Commands is off");
return;
}
bool shouldAllow = true;
onCheckUnityEventPermissions?.Invoke(messenger, command, ref shouldAllow);
if (shouldAllow)
{
execute(CSteamID.Nil, command);
}
}Unity events (from asset dialogs, triggers, or NPC interactions) can execute commands. The permission model:
- Server config
UnityEvents.Allow_Server_Commandsmust be enabled. - The
onCheckUnityEventPermissionsevent allows external permission handlers to veto. - The messenger and command string are logged for auditability.
Initialization
Commander.init() creates the commands list and registers all built-in commands:
csharp
public static void init()
{
commands = new List<Command>();
Local emptyPlaceholder = new Local();
register(new CommandModules(Localization.read("/Server/ServerCommandModules.dat")));
register(new CommandReload(Localization.read("/Server/ServerCommandReload.dat")));
register(new CommandHelp(Localization.read("/Server/ServerCommandHelp.dat")));
register(new CommandName(Localization.read("/Server/ServerCommandName.dat")));
register(new CommandPort(Localization.read("/Server/ServerCommandPort.dat")));
register(new CommandPassword(Localization.read("/Server/ServerCommandPassword.dat")));
register(new CommandMaxPlayers(Localization.read("/Server/ServerCommandMaxPlayers.dat")));
// ... ~60 registrations total
}Commands are fully registered in init() before the server starts accepting connections. The emptyPlaceholder is used for debug-only or localization-free commands.
Server Configuration Commands
| Command | Description | File |
|---|---|---|
CommandModules | List/load server modules | ServerCommandModules.dat |
CommandReload | Reload server configuration | ServerCommandReload.dat |
CommandHelp | Display command help | ServerCommandHelp.dat |
CommandName | Set server name | ServerCommandName.dat |
CommandPort | Set server port | ServerCommandPort.dat |
CommandPassword | Set server password | ServerCommandPassword.dat |
CommandMaxPlayers | Set max player count | ServerCommandMaxPlayers.dat |
CommandQueue | Set queue size | ServerCommandQueue.dat |
CommandMap | Set/change current map | ServerCommandMap.dat |
CommandPvE | Toggle PvE mode | ServerCommandPvE.dat |
CommandWhitelisted | Toggle whitelist | ServerCommandWhitelisted.dat |
CommandCheats | Toggle cheat mode | ServerCommandCheats.dat |
CommandHideAdmins | Hide admin status from players | ServerCommandHideAdmins.dat |
CommandEffectUI | Toggle effect UI | ServerCommandEffectUI.dat |
CommandSync | Toggle server sync settings | ServerCommandSync.dat |
CommandFilter | Manage chat filter | ServerCommandFilter.dat |
CommandVotify | Toggle voting | ServerCommandVotify.dat |
CommandMode | Set game mode category | ServerCommandMode.dat |
CommandGameMode | Set game mode (deprecated) | ServerCommandGameMode.dat |
CommandGold | Toggle gold mode | ServerCommandGold.dat |
CommandCamera | Set camera mode (first/third) | ServerCommandCamera.dat |
CommandCycle | Set day/night cycle speed | ServerCommandCycle.dat |
CommandTime | Set current time of day | ServerCommandTime.dat |
CommandDay | Set to daytime | ServerCommandDay.dat |
CommandNight | Set to nighttime | ServerCommandNight.dat |
CommandWeather | Override weather | ServerCommandWeather.dat |
CommandAirdrop | Trigger an airdrop | ServerCommandAirdrop.dat |
Player Management Commands
| Command | Description | File |
|---|---|---|
CommandKick | Kick a player | ServerCommandKick.dat |
CommandBan | Ban a player | ServerCommandBan.dat |
CommandUnban | Unban a player | ServerCommandUnban.dat |
CommandBans | List active bans | ServerCommandBans.dat |
CommandAdmin | Grant admin to a player | ServerCommandAdmin.dat |
CommandUnadmin | Revoke admin from a player | ServerCommandUnadmin.dat |
CommandAdmins | List admins | ServerCommandAdmins.dat |
CommandOwner | Set server owner | ServerCommandOwner.dat |
CommandPermit | Whitelist a player | ServerCommandPermit.dat |
CommandUnpermit | Remove from whitelist | ServerCommandUnpermit.dat |
CommandPermits | List whitelisted players | ServerCommandPermits.dat |
CommandSpy | Toggle admin spy mode | ServerCommandSpy.dat |
Player Interaction Commands
| Command | Description | File |
|---|---|---|
CommandPlayers | List connected players | ServerCommandPlayers.dat |
CommandSay | Broadcast message to all | ServerCommandSay.dat |
CommandWelcome | Set welcome message | ServerCommandWelcome.dat |
CommandSlay | Kill and ban a player | ServerCommandSlay.dat |
CommandKill | Kill a player | ServerCommandKill.dat |
CommandGive | Give item to player | ServerCommandGive.dat |
CommandExperience | Set/give XP | ServerCommandExperience.dat |
CommandReputation | Set reputation | ServerCommandReputation.dat |
CommandFlag | Set quest flag value | ServerCommandFlag.dat |
CommandQuest | Set quest status | ServerCommandQuest.dat |
CommandVehicle | Spawn a vehicle | ServerCommandVehicle.dat |
CommandAnimal | Spawn an animal | ServerCommandAnimal.dat |
CommandTeleport | Teleport player | ServerCommandTeleport.dat |
CommandLoadout | Set default spawn loadout | ServerCommandLoadout.dat |
Debug and Utility Commands
| Command | Description | File |
|---|---|---|
CommandDebug | Toggle debug mode | ServerCommandDebug.dat |
CommandBind | Bind server IP | ServerCommandBind.dat |
CommandLog | Toggle server logging | ServerCommandLog.dat |
CommandTimeout | Set connection timeout | ServerCommandTimeout.dat |
CommandChatrate | Set chat rate limit | ServerCommandChatrate.dat |
CommandSave | Save world state | ServerCommandSave.dat |
CommandShutdown | Shutdown server | ServerCommandShutdown.dat |
CommandGSLT | Set Game Server Login Token | ServerCommandGSLT.dat |
CommandLogMemoryUsage | Log memory stats | None (emptyPlaceholder) |
CommandLogTransportConnections | Log transport connections | None |
CommandCopyServerCode | Copy server code | None |
CommandCopyFakeIP | Copy fake IP | None |
CommandDestroyDrivenVehicle | Destroy driven vehicle | None |
CommandExitAndDestroyDrivenVehicle | Exit and destroy vehicle | None |
CommandEnterAndDestroyNearestVehicle | Enter and destroy nearest vehicle | None |
CommandRewardList | List NPC rewards | None |
CommandDialogue | Trigger NPC dialogue | None |
CommandScheduledShutdownInfo | Show scheduled shutdown time | None |
CommandSetNpcSpawnId | Set NPC spawn ID | None |
CommandToggleNpcCutsceneMode | Toggle cutscene mode | None |
CommandNpcEvent | Trigger NPC event | None |
Development-Only Commands
Registered only in UNITY_EDITOR || DEVELOPMENT_BUILD:
CommandLogAssetOrigins— log all loaded asset origins.CommandSpawnAllBarricades— spawn barricades from all assets.CommandSpawnAllVehicles— spawn vehicles from all assets.CommandSteamClearAchievement— clear a Steam achievement.
Permission System
The command system has no explicit permission attribute on Command — each subclass implements its own permission check inside execute().
Server-only Guard
csharp
if (!Dedicator.IsDedicatedServer) return;Commands that only make sense on dedicated servers use this guard. Examples: CommandPort, CommandPassword, CommandGameMode, CommandMode.
Server Running Guard
csharp
if (!Provider.isServer)
{
CommandWindow.LogError(localization.format("NotRunningErrorText"));
return;
}Commands that require the server to be actively running. Examples: CommandKick, CommandBan, CommandSlay, CommandAdmin, CommandGive, CommandTeleport.
Admin Permission
Commands like CommandAdmin, CommandBan, CommandKick, CommandSlay, CommandGive, CommandTeleport implicitly require admin. When executed from the server console (CSteamID.Nil), they are always allowed. When executed in-game, the Steam ID resolves through PlayerTool.tryGetSteamPlayer() which checks admin status.
CommandAdmin grants admin status:
csharp
protected override void execute(CSteamID executorID, string parameter)
{
if (!Dedicator.IsDedicatedServer) return;
if (!Provider.isServer) { /* NotRunningError */ return; }
CSteamID steamID;
if (!PlayerTool.tryGetSteamID(parameter, out steamID))
{
CommandWindow.LogError(localization.format("NoPlayerErrorText", parameter));
return;
}
SteamAdminlist.admin(steamID, executorID);
CommandWindow.Log(localization.format("AdminText", steamID));
}Owner Permission
CommandOwner sets the server owner. It doesn't enforce a permission gate in the command itself — the server owner is configured in the server config file and checked by other systems (e.g., SteamAdminlist).
Help System
CommandHelp implements the help command:
csharp
protected override void execute(CSteamID executorID, string parameter)
{
if (string.IsNullOrEmpty(parameter))
{
// List all commands (console only)
if (!Dedicator.IsDedicatedServer) return;
CommandWindow.Log(localization.format("HelpText"));
string commands = "";
for (int index = 0; index < Commander.commands.Count; index++)
{
if (string.IsNullOrEmpty(Commander.commands[index].info)) continue;
commands += Commander.commands[index].info;
if (index < Commander.commands.Count - 1) commands += "\n";
}
CommandWindow.Log(commands);
}
else
{
// Show help for specific command
for (int index = 0; index < Commander.commands.Count; index++)
{
if (parameter.ToLower() == Commander.commands[index].command.ToLower())
{
if (executorID == CSteamID.Nil)
{
CommandWindow.Log(Commander.commands[index].info);
CommandWindow.Log(Commander.commands[index].help);
}
else
{
ChatManager.say(executorID, Commander.commands[index].info, ...);
ChatManager.say(executorID, Commander.commands[index].help, ...);
}
return;
}
}
// Command not found
if (executorID == CSteamID.Nil)
CommandWindow.Log(localization.format("NoCommandErrorText", parameter));
else
ChatManager.say(executorID, localization.format("NoCommandErrorText", parameter), ...);
}
}Without parameters: lists all registered commands' _info strings (console only). With a command name: searches for the command and outputs both _info and _help via CommandWindow (console) or ChatManager.say() (in-game).
Localization
Each command loads its strings from a .dat file via Localization.read(). The file path follows the pattern /Server/ServerCommand{Name}.dat. Each .dat file contains key-value pairs:
CommandText "help"
InfoText "Displays a list of commands"
HelpText "help [commandname]"Commands that don't need localization pass an empty Local placeholder via new Local().
Command Examples
CommandAdmin — Granting Admin
csharp
public class CommandAdmin : Command
{
protected override void execute(CSteamID executorID, string parameter)
{
if (!Dedicator.IsDedicatedServer) return;
if (!Provider.isServer) { CommandWindow.LogError("Not running"); return; }
CSteamID steamID;
if (!PlayerTool.tryGetSteamID(parameter, out steamID))
{
CommandWindow.LogError("No player: " + parameter);
return;
}
SteamAdminlist.admin(steamID, executorID);
CommandWindow.Log("Admin: " + steamID);
}
}CommandSlay — Kill and Ban
csharp
public class CommandSlay : Command
{
protected override void execute(CSteamID executorID, string parameter)
{
if (!Dedicator.IsDedicatedServer) return;
if (!Provider.isServer) { CommandWindow.LogError("Not running"); return; }
string[] components = Parser.getComponentsFromSerial(parameter, '/');
SteamPlayer player;
if (!PlayerTool.tryGetSteamPlayer(components[0], out player))
{
CommandWindow.LogError("No player: " + components[0]);
return;
}
uint ip = player.getIPv4AddressOrZero();
// Ban the player
Provider.requestBanPlayer(executorID, player.playerID.steamID, ip,
player.playerID.GetHwids(), components.Length == 2 ? components[1] : "Slay",
SteamBlacklist.PERMANENT);
// Kill the player
player.player.life.askDamage(101, Vector3.up * 101, EDeathCause.KILL, ELimb.SKULL, executorID, out kill);
CommandWindow.Log("Slayed: " + player.playerID.playerName);
}
}CommandGameMode — Deprecated
csharp
public class CommandGameMode : Command
{
protected override void execute(CSteamID executorID, string parameter)
{
if (!Dedicator.IsDedicatedServer) return;
if (Provider.isServer) { CommandWindow.LogError("RunningErrorText"); return; }
CommandWindow.Log("GameModeText: " + parameter);
}
}This command is essentially deprecated — the // Provider.selectedGameModeName = parameter; line is commented out.
Error Handling
Commands communicate results through CommandWindow:
CommandWindow.Log(string)— informational (green in default console).CommandWindow.LogError(string)— error (red).CommandWindow.LogWarning(string)— warning (yellow).
The Commander execute loop wraps execution in try/catch:
csharp
try { /* execution */ }
catch (Exception e)
{
UnturnedLog.exception(e, "Exception while executing command \"{0}\"", command);
}This prevents a crashing command from bringing down the server.
Mod Integration
Mods can create custom commands:
csharp
public class MyCommand : Command
{
protected override void execute(CSteamID executorID, string parameter)
{
CommandWindow.Log("My command executed with: " + parameter);
}
public MyCommand() : base() { }
}
// Registration
Commander.register(new MyCommand());
// Deregistration (on mod unload)
Commander.deregister(myCommandInstance);The deregister() method removes by reference equality, so mods must keep a reference to the registered instance. Mods can also replace built-in commands by deregistering the original and registering their own.
Command Flow Walkthrough
Console Execution
When a server admin types a command in the console:
CommandWindow.input.onInputTextfires with the raw input string.Commander.execute(CSteamID.Nil, inputString)is called.- The input is split into method and parameter at the first space.
- Each registered command's
check()is called in registration order. - The first matching command executes and returns
true. - The command's
execute()runs, callingCommandWindow.Log()for output. - If no command matches,
CommandWindow.Log()shows "unknown command" (handled by the caller, not by Commander).
Chat Execution
When a player types /command in chat:
ChatManagerintercepts messages starting with/.Commander.execute(steamID, message.Substring(1))is called.- Same flow as console, but
executorIDis the player'sCSteamID. - The command can distinguish console vs. chat via
executorID == CSteamID.Nil. - Output goes to
ChatManager.say()for in-game display instead ofCommandWindow.Log().
Permission Resolution
Player types "/slay Bob"
→ ChatManager detects '/'
→ Commander.execute(steamID, "slay Bob")
→ CommandSlay.check("slay", "Bob")
→ CommandSlay.execute(steamID, "Bob")
→ Dedicator.IsDedicatedServer? Yes
→ Provider.isServer? Yes
→ PlayerTool.tryGetSteamPlayer("Bob")? Found
→ Provider.requestBanPlayer(...) // Ban Bob
→ player.life.askDamage(101, ...) // Kill BobMod Command Registration Walkthrough
Mod initializes:
var myCmd = new MyCustomCommand(localization)
Commander.register(myCmd)
→ BinarySearch finds alphabetical insert index
→ Inserted into sorted list
Later, mod unloads:
Commander.deregister(myCmd)
→ commands.Remove(myCmd) removes by referenceCommand Localization File Format
Each command loads its strings from a .dat file in the /{Server} directory. The format is key-value pairs:
CommandText "help"
InfoText "Displays a list of commands"
HelpText "!help [commandname]"
NoCommandErrorText "Could not find command: {0}"The {0} placeholder in error messages is replaced with the user's input when the command is not found. This localization system allows server owners to translate or customize command messages per-language.
When localization is not needed (debug commands or commands with only hardcoded strings), the constructor passes new Local() (empty localization).
Command Naming Conventions
Built-in commands follow consistent naming:
- Lowercase: All commands are lowercase (
help,ban,give). - No prefixes: Commands don't include
/or!— those are handled by the chat system. - Single word: Command names are single words with no hyphens or underscores.
- Unique: No two commands share the same name. The linear search returns the first match.
Extending the Command System
Plugin frameworks like RocketMod and OpenMod can extend the command system by:
- Registering their own
Commandsubclasses viaCommander.register(). - Hooking
Commander.execute()via prefix delegates (not directly supported — requires wrappingCommander.execute()or replacing the text input handler). - Using
CommandWindow.input.onInputTextto intercept console input before Commander processes it.
The command system's sorted-list registration makes it predictable (alphabetical order in help), but the linear search for execution means performance is O(n) for n registered commands. For the ~60 built-in commands, this is negligible. For plugins adding hundreds of commands, this could become noticeable in the chat input path.
