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.
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.