Skip to content
RustPluginGenerator

How to make a Rust help command plugin (paged /help list from config)

A paged /help command that lists server commands and short descriptions stored in the plugin config, 8 entries per page, with /help 2 for the next page. It registers a chat command in Init and needs no game hooks.

Players join a Rust server and have no idea which chat commands you run. Vanilla Rust has no central command list, and hard-coding one into every plugin gets stale fast. This plugin keeps the whole list in oxide/config/HelpCommand.json, so you can add or remove entries without recompiling anything. /help prints the first eight entries in chat, /help 2 prints the second eight, and so on; entries beyond the page count are never lost, and one config key lets you set a footer line shown on every page. The README-style header message, page number, and the "no page there" feedback all go through the language file, so non-English servers can translate them. The implementation only uses Oxide's permission, lang, chat-command and config APIs; there is nothing to patch, no hook, and no per-tick cost.

The plugin code

HelpCommand.cs
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;

namespace Oxide.Plugins
{
    [Info("Help Command", "RustPluginGenerator", "1.0.0")]
    [Description("Shows a paged list of server commands and descriptions stored in the config.")]
    public class HelpCommand : RustPlugin
    {
        private const int PageSize = 8;

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Header (use {0} for page, {1} for total pages)")]
            public string Header = "Server commands - page {0}/{1}";

            [JsonProperty("Footer")]
            public string Footer = "Type /help <page> for more.";

            [JsonProperty("Commands")]
            public List<CommandEntry> Commands = new List<CommandEntry>
            {
                new CommandEntry { Command = "/kit", Description = "Claim your starter kit", ShowInHelp = true },
                new CommandEntry { Command = "/trade <player>", Description = "Send a trade request", ShowInHelp = true },
                new CommandEntry { Command = "/tp", Description = "Teleport request", ShowInHelp = true },
                new CommandEntry { Command = "/report <player> <reason>", Description = "Report a player", ShowInHelp = true }
            };
        }

        private class CommandEntry
        {
            [JsonProperty("Command")]
            public string Command = "/command";

            [JsonProperty("Description")]
            public string Description = "What it does";

            [JsonProperty("Show in help")]
            public bool ShowInHelp = true;
        }

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

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<PluginConfig>();
                if (config == null || config.Commands == 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>
            {
                ["Header"] = "Server commands - page {0}/{1}",
                ["Line"] = "{0} - {1}",
                ["Footer"] = "Type /help <page> for more.",
                ["NoSuchPage"] = "There is no page {0}. Use /help 1.",
                ["NoCommands"] = "No commands are listed right now.",
                ["WrongUsage"] = "Usage: /help or /help <page>"
            }, this);
        }

        private void Init()
        {
            cmd.AddChatCommand("help", this, nameof(CmdHelp));
        }

        private void CmdHelp(BasePlayer player, string command, string[] args)
        {
            if (player == null || config == null || config.Commands == null)
                return;

            var visible = new List<CommandEntry>();
            foreach (var entry in config.Commands)
            {
                if (entry != null && entry.ShowInHelp && !string.IsNullOrEmpty(entry.Command))
                    visible.Add(entry);
            }

            if (visible.Count == 0)
            {
                SendReply(player, Msg("NoCommands", player.UserIDString));
                return;
            }

            int page = 1;
            if (args != null && args.Length > 0)
            {
                if (!int.TryParse(args[0], out page))
                    page = 1;
            }
            if (page < 1)
                page = 1;

            int pageCount = (int)Math.Ceiling(visible.Count / (double)PageSize);
            if (page > pageCount)
            {
                SendReply(player, Msg("NoSuchPage", player.UserIDString, page));
                return;
            }

            SendReply(player, Msg("Header", player.UserIDString, page, pageCount));

            int start = (page - 1) * PageSize;
            int end = Math.Min(start + PageSize, visible.Count);
            for (int i = start; i < end; i++)
            {
                var entry = visible[i];
                SendReply(player, Msg("Line", player.UserIDString, entry.Command, entry.Description));
            }

            if (!string.IsNullOrEmpty(config.Footer))
                SendReply(player, Msg("Footer", player.UserIDString));
        }

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

Config file

Created automatically on first load at oxide/config/HelpCommand.json.

HelpCommand.json
{
  "Header (use {0} for page, {1} for total pages)": "Server commands - page {0}/{1}",
  "Footer": "Type /help <page> for more.",
  "Commands": [
    {
      "Command": "/kit",
      "Description": "Claim your starter kit",
      "Show in help": true
    },
    {
      "Command": "/trade <player>",
      "Description": "Send a trade request",
      "Show in help": true
    },
    {
      "Command": "/tp",
      "Description": "Teleport request",
      "Show in help": true
    },
    {
      "Command": "/report <player> <reason>",
      "Description": "Report a player",
      "Show in help": true
    }
  ]
}

Commands and permissions

Chat and console commands
CommandWhat it doesPermission
/helpShows page 1 of the command list (8 entries).Everyone
/help <page>Shows the given page number, e.g. /help 2 for entries 9 to 16.Everyone

How the code works

  1. Store the command list in the config

    The nested PluginConfig class holds a List<CommandEntry>. Each CommandEntry has Command (the text to display, e.g. /kit), Description (shown on the same line) and ShowInHelp (a quick way to park an entry without deleting it). The Header and Footer strings wrap every page. LoadDefaultConfig seeds the list with a couple of realistic entries, matching the default config file, so the plugin works out of the box.

  2. Register the chat command

    Init is the only startup hook used here. It calls cmd.AddChatCommand with the name help, so the command name can be changed without touching the attribute. The callback receives the player, the raw command string and the string[] args. The method returns early when player is null, so a stale command from a disconnected player cannot throw. No permission is registered, every connected player can run it.

  3. Resolve the requested page

    The handler counts the visible entries first, then builds pageCount with Math.Ceiling(count / (double)PageSize), where PageSize is a const int of 8. A missing argument means page 1. int.TryParse guards against /help abc, which falls back to page 1 instead of failing. The value is clamped to at least 1. If the requested page exceeds the page count you send the NoSuchPage message through the Msg helper and stop.

  4. Print only the entries for that page

    The header is sent with {0} as the current page and {1} as the total. The loop starts at (page - 1) * PageSize and stops at start + PageSize or the end of the filtered list. Each entry is printed as one chat line built from Command and Description. Entries with ShowInHelp set to false are skipped before paging, so they do not create empty slots. The footer is appended last, also through the language file.

  5. Keep player-facing text in lang

    LoadDefaultMessages registers Header, Line, Footer, NoSuchPage, NoCommands and WrongUsage in English. Msg formats them with lang.GetMessage(key, this, player.UserIDString) and string.Format. Every one of those messages can be overridden in oxide/lang/en/HelpCommand.json; you do not need to edit the plugin to reword them.

  6. Config load and save

    LoadConfig calls base.LoadConfig, tries Config.ReadObject<PluginConfig>() and, on any exception or a null result, prints a warning and calls LoadDefaultConfig. SaveConfig writes the object back. That means an admin who deletes a key or breaks the JSON gets a working default file the next time the plugin loads, rather than a crash. There is no data file and no timer, so Unload needs no cleanup.

Install it on your server

  1. Save HelpCommand.cs

    Keep the file name exactly as the class name. Oxide matches them.
  2. Upload it to the plugins folder

    Oxide: oxide/plugins/. Carbon: carbon/plugins/. The server compiles and loads it within a few seconds, no restart needed.
  3. Edit the config if you want

    On first load the default config is written to oxide/config/HelpCommand.json (Carbon: carbon/configs/HelpCommand.json). After editing, reload it:oxide.reload HelpCommand c.reload HelpCommand # Carbon

Variations

Each one opens the generator with a ready prompt you can tweak.

  • Show the command list in a CUI panel

    Render the page as a draggable Oxide UI panel with a Prev/Next button pair instead of chat lines.

    Generate this version
  • Search instead of paging

    Let players type /help kit to filter entries by keyword across all pages.

    Generate this version
  • Per-permission help

    Hide entries the player cannot use by checking a permission string stored per entry.

    Generate this version

FAQ

How do I add or remove commands from the list?

Edit oxide/config/HelpCommand.json, add an object to Commands with Command, Description and ShowInHelp, then reload the plugin. Set ShowInHelp to false to hide an entry without removing it. Pages recalculate automatically because the count is taken at runtime.

Why does /help collide with another plugin's command?

Oxide lets one plugin own a chat command name; the last plugin loaded registers it and the others log a conflict. Rename the command with cmd.AddChatCommand("commands", this, ...) in Init and update the Command strings in config if you also want them to match.

Can I translate the header and footer?

Yes. Edit oxide/lang/en/HelpCommand.json (or add a language folder for your locale) and change Header, Line, Footer, NoSuchPage, NoCommands and WrongUsage. The placeholders {0} and {1} in Header are the current and total page numbers.

How do I install it on Oxide or Carbon?

Drop HelpCommand.cs into oxide/plugins and let it compile, or upload it through the admin panel. Carbon loads the same file. Then edit oxide/config/HelpCommand.json and run oxide.reload HelpCommand. No permissions are registered and no hooks are patched.

Does this cost performance on a full server?

No. The plugin only does work when a player types /help; it has no OnTick, no timers and no entity subscriptions. Building one page is a single loop over at most eight list entries.

Customize it for your server

Change the reward, add a cooldown, restrict it to VIPs. Start from this prompt and edit anything.

Prompt: Write a Rust Oxide plugin with a paged /help chat command that lists commands and descriptions from config, 8 per page, with header and footer messages in lang.
Customize for my server

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

Last updated Oct 26, 2026.

Customize help command

Customize