Guide14 min read·updated

How to Make Your First Rust Plugin in C# (Carbon)

A Carbon plugin is just one C# file the server compiles for you. This walks through a real, working first plugin — the skeleton, a hook, a chat command, a permission, and saved config — with copy-paste code at every step.

Most "how to make a Rust plugin" results are either ancient Oxide forum threads or a single terse docs page. This is the modern Carbon + C# version, written so you end up with something that actually runs. By the end you'll have a plugin that greets players, responds to a chat command, gates that command behind a permission, and remembers a setting across restarts.

What you need

  • A Rust server with Carbon installed.
  • A text editor — VS Code with the C# extension is ideal but not required.
  • Access to carbon/plugins/ on the server (FTP, file manager, or local).

1. The plugin skeleton

Every plugin is a class that extends RustPlugin (Carbon) and is tagged with an [Info] attribute. Create carbon/plugins/Welcomer.cs:

using Carbon.Plugins;

namespace Carbon.Plugins
{
    [Info("Welcomer", "RGZ", "1.0.0")]
    [Description("Greets players and adds a simple command.")]
    public class Welcomer : RustPlugin
    {
        void OnServerInitialized()
        {
            Puts("Welcomer loaded.");
        }
    }
}

Save it. Watch the console — you should see Loaded plugin Welcomer v1.0.0 and then Welcomer loaded. That's a live plugin. Puts() writes to the server console; OnServerInitialized is a hook that fires once when the server is ready.

carbon — hot reload
[Carbon] Compiling Welcomer.cs ...
[Carbon] Loaded plugin Welcomer v1.0.0 by RGZ
Welcomer loaded.
What a successful hot-reload looks like. A compile error here names the file and line — that's your debugger.

2. React to an event with a hook

Hooks are methods the game calls automatically. Name the method after the hook and it just works. Let's greet players when they spawn:

void OnPlayerConnected(BasePlayer player)
{
    player.ChatMessage("Welcome to the server. Type /info for the rules.");
}

Save — Carbon hot-reloads — connect a player, and they get the message. There are hundreds of hooks (OnEntityDeath, OnItemCraft, OnPlayerChat…); the hook name tells you when it fires and the parameters tell you what you get.

3. Add a chat command

Commands use the [ChatCommand] attribute. Add an /info command:

[ChatCommand("info")]
void CmdInfo(BasePlayer player, string command, string[] args)
{
    player.ChatMessage("This is an RGZ-modded server. No griefing. Have fun.");
}

args is everything typed after the command, already split on spaces — so /info rules gives you args[0] == "rules".

4. Gate it behind a permission

You don't want every command open to everyone. Register a permission, then check it. Permissions are granted with o.grant in the console.

private const string PermUse = "welcomer.admin";

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

[ChatCommand("reloadinfo")]
void CmdReloadInfo(BasePlayer player, string command, string[] args)
{
    if (!permission.UserHasPermission(player.UserIDString, PermUse))
    {
        player.ChatMessage("You don't have permission to use that.");
        return;
    }
    player.ChatMessage("Info reloaded.");
}

Then in the server console: o.grant user <steamid> welcomer.admin — or grant it to a group. (See adding admins for finding a SteamID64.)

5. Save a setting in config

Hardcoded strings are fine for a demo, but real plugins read a config file so admins can edit behaviour without touching code. Carbon generates and loads carbon/configs/Welcomer.json for you:

private ConfigData config;

private class ConfigData
{
    public string WelcomeMessage = "Welcome to the server!";
}

protected override void LoadDefaultConfig()
{
    config = new ConfigData();
}

protected override void LoadConfig()
{
    base.LoadConfig();
    config = Config.ReadObject<ConfigData>();
    SaveConfig();
}

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

Now use config.WelcomeMessage instead of the hardcoded string in OnPlayerConnected. Admins edit the JSON, reload the plugin, and the message changes — no recompile. If a config ever won't parse, our config validator will point at the broken line.

The development loop

That's the whole cycle: edit the .cs file, save it into carbon/plugins/, watch the console for the reload (or a compile error), test in-game. Keep the console visible while you work — it's your compiler output and your debugger. Puts() and player.ChatMessage()are your print-debugging until you're comfortable.

Where to go next

  • Browse real plugins in the directory and read their config examples — seeing how shipped plugins structure things teaches fast.
  • Learn the framework trade-offs in Carbon vs Oxide.
  • The plugin dev hub collects the deeper topics — data files, timers, the hooks reference, and publishing.
Frequently asked

Questions & answers

Do I need Visual Studio to make a Rust plugin?
No. A Carbon plugin is a single .cs file — you can write it in any text editor and Carbon compiles it on the server. Visual Studio or VS Code with the C# extension just gives you autocomplete and error highlighting, which helps a lot once plugins grow.
What language are Rust plugins written in?
C#. Both Carbon and Oxide compile C# source files at runtime. This is unrelated to the Rust programming language — the game Rust is built in Unity/C#, and so are its plugins.
How do I test a plugin without restarting the server?
Carbon hot-reloads. Save the .cs file into carbon/plugins/ and Carbon recompiles and reloads it instantly. Watch the console for compile errors. This edit-save-watch loop is the whole development cycle.
Will a plugin I write for Carbon also run on Oxide?
Mostly yes, if you stick to the standard Oxide API (the [Info] attribute, hooks, permission system, config). Carbon implements that API. You only diverge if you call Carbon-specific or Oxide-specific internals.
Ad Unit · inline