Skip to content
Header background
posted by Bubbafett on Friday, July 4, 2025

tutorialconfigurationmanagementdeveloper
Configuration Management
Learn to create, save, load, and update Rust plugin config files using C# classes for Oxide and Carbon!

Introduction

If you ever want to add a configurable option to your plugin, it might be tempting to simply define a field at the top of your code and call it done. However, if you plan to share your plugin publicly or expect others to modify its settings, it's best to use a configuration file instead.

In this article, we will cover how to create, save, load, and update configuration files!

WARNING

This tutorial assumes you know the following:

  • How to create basic Carbon or Oxide plugins
  • Basic C# syntax and structure (classes, methods, fields)
  • How to run and test your plugin on a Rust server

What are Configuration Files?

Configuration files for Carbon and Oxide are saved in JSON format at carbon/configs/plugin_name.json or oxide/config/plugin_name.json. These files allow server owners to customize plugin behavior by adjusting various settings specific to each plugin.

Example Config:

json
// Config of the WhiteList Module from Carbon
{
  "Enabled": false,
  "Config": {
    "BypassPermission": "whitelist.bypass",
    "BypassGroup": "whitelisted"
  },
  "Version": "781308331"
}

Creating your Class!

Let’s say we want to create a plugin that sends a message when a player with a specific permission connects, but we also want the server owner to be able to configure that permission. That’s easy to do!

First, we need to create a configuration class inside our plugin. I like to keep this organized by placing it inside a #region, which helps separate it from the rest of the code.

cs
#region Config

    public class Configuration { }

#endregion

Next, we'll add our first property to the class. I usually start with a version number. This is easily done using the VersionNumber struct from Oxide.Core. We’ll also use [JsonProperty()] from Newtonsoft.Json to control how the field appears in the JSON file, specifically setting PropertyName = "string" and ordering it last using Order = int.MaxValue.

cs
using Oxide.Core;
using Newtonsoft.Json;

#region Config

    public class Configuration
    {
        // Version numbers follow this format: Major.Minor.Patch
        [JsonProperty(PropertyName = "Version (DO NOT CHANGE)", Order = int.MaxValue)]
        public VersionNumber Version = new(1, 0, 0);
    }

#endregion

The version number will be useful later if we add new options after the plugin has been released publicly or deployed on a server. It allows us to safely update the configuration file as needed without breaking compatibility.

Now, let's add the permission string we'll check for when a player connects. We'll do that by adding a new property to our Configuration class:

cs
using Oxide.Core;
using Newtonsoft.Json;

#region Config

    public class Configuration
    {
        [JsonProperty(PropertyName = "Version (DO NOT CHANGE)", Order = int.MaxValue)]
        public VersionNumber Version = new(1, 0, 0);

        [JsonProperty(PropertyName = "Permission")]
        public string UsePermission = "CoolPlugin.use";
    }

#endregion

This gives server owners the flexibility to change the required permission via the configuration file, without modifying the plugin code directly.

Next, we will create a public instance of the Configuration class:

cs
using Oxide.Core;
using Newtonsoft.Json;

#region Config

    public Configuration PluginConfig;

    public class Configuration
    {
        [JsonProperty(PropertyName = "Version (DO NOT CHANGE)", Order = int.MaxValue)]
        public VersionNumber Version = new(1, 0, 0);

        [JsonProperty(PropertyName = "Permission")]
        public string UsePermission = "CoolPlugin.use";
    }

#endregion

We create this instance so that we can store and access the loaded configuration data within our plugin. When the plugin is loaded, we’ll read the configuration file from disk and assign its values to this PluginConfig object. This way, any part of our plugin can reference PluginConfig.UsePermission (or other future options) without needing to parse the file every time.

Config File Management

The first thing you’ll want to do when managing your config file is ensure that a new one is created if none exists. This is especially important when your plugin is first loaded or if the config has been deleted or corrupted. To handle this, we override the LoadDefaultConfig() method. This method is called automatically by the plugin system when a config file is missing. In our implementation, we simply return a new instance of the Configuration class using a lambda expression.

cs
using Oxide.Core;
using Newtonsoft.Json;

#region Config

    public Configuration PluginConfig;

    public class Configuration
    {
        [JsonProperty(PropertyName = "Version (DO NOT CHANGE)", Order = int.MaxValue)]
        public VersionNumber Version = new(1, 0, 0);

        [JsonProperty(PropertyName = "Permission")]
        public string UsePermission = "CoolPlugin.use";
    }

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

#endregion

Next, we need to make sure the configuration can be saved whenever it changes, whether those changes come from the plugin or from server owners. We do this by overriding the SaveConfig() method. This method uses the Config object (a DynamicConfigFile provided by the base plugin class) and calls WriteObject() to serialize our PluginConfig instance into a readable JSON format.

cs
using Oxide.Core;
using Newtonsoft.Json;

#region Config

    public Configuration PluginConfig;

    public class Configuration
    {
        [JsonProperty(PropertyName = "Version (DO NOT CHANGE)", Order = int.MaxValue)]
        public VersionNumber Version = new(1, 0, 0);

        [JsonProperty(PropertyName = "Permission")]
        public string UsePermission = "CoolPlugin.use";
    }

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

    protected override void SaveConfig() => Config.WriteObject(PluginConfig, true);

#endregion

After that, we override the LoadConfig() method. In this override, we first call base.LoadConfig() to ensure the base functionality is executed. Then we set our public instance of the configuration class by calling Config.ReadObject<Configuration>(). If the result is null, we create a new config using our class and save it immediately. This guarantees that the plugin always has a valid configuration available.

cs
using Oxide.Core;
using Newtonsoft.Json;

#region Config

    public Configuration PluginConfig;

    public class Configuration
    {
        [JsonProperty(PropertyName = "Version (DO NOT CHANGE)", Order = int.MaxValue)]
        public VersionNumber Version = new(1, 0, 0);

        [JsonProperty(PropertyName = "Permission")]
        public string UsePermission = "CoolPlugin.use";
    }

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

    protected override void SaveConfig() => Config.WriteObject(PluginConfig, true);

    protected override void LoadConfig()
    {
        base.LoadConfig();
        PluginConfig = Config.ReadObject<Configuration>();
        if (PluginConfig == null)
        {
            PluginConfig = new Configuration();
            SaveConfig();
            return;
        }
    }
#endregion

And that's it! You now have a fully functional configuration system. But how do you actually use it? How do you safely read values from the config, and what happens if you add new options to the configuration file later?

Let’s walk through how to read from your config and add values later when the plugin updates!

Reading the Config

Let’s start by listening to the Init() hook, which runs when the plugin is initialized. In this method, we’ll register the permission defined in the config so it can be used elsewhere in the plugin. To keep things organized, we’ll wrap this logic in a #region called Hooks.

We reference the configuration by accessing the public Configuration instance we created earlier, PluginConfig, and using its UsePermission field.

cs
#region Hooks

    private void Init()
    {
        permission.RegisterPermission(PluginConfig.UsePermission, this);
    }

#endregion

Next, we’ll listen for when a player connects using the OnPlayerConnected() hook. If the player has the required permission, we’ll send a message to everyone in chat welcoming them to the server.

We do this by calling permission.UserHasPermission() inside an if statement, and broadcasting the appropriate message using server.Broadcast().

cs
#region Hooks

    private void Init()
    {
        permission.RegisterPermission(PluginConfig.UsePermission, this);
    }

    private void OnPlayerConnected(BasePlayer player)
    {
        if (permission.UserHasPermission(player.UserIDString, PluginConfig.UsePermission))
        {
            server.Broadcast("Someone has connected with the Cool Plugin features enabled!");
        }
        else
        {
            server.Broadcast("Someone has connected without the Cool Plugin features enabled.");
        }
    }

#endregion

Great! At this point, the server owner can change the permission string in the config, and the plugin will register and use that updated permission when players connect.

But what happens if you want to add more configuration options after the config file has already been generated?

Updating the Config

Let's go through updating the config, but first we need to add the new option into the Configuration class. I am going to create an IsEnabled bool field that will be used to see if the plugin even checks the permission or not. I will also update the version number to show that the plugin was updated.

cs
    public class Configuration
    {
        [JsonProperty(PropertyName = "Version (DO NOT CHANGE)", Order = int.MaxValue)]
        public VersionNumber Version = new(1, 0, 1);
        
        [JsonProperty(PropertyName = "Permission")]
        public string UsePermission = "CoolPlugin.use";
        
        [JsonProperty(PropertyName = "Permission Enabled")]
        public bool IsEnabled = true;
    }

Next, we add a new UpdateConfig() helper method that compares the plugin version with the version number found in the config file, and if it is greater or equal to the plugin version it will exit the method. I will also add a warning here using PrintWarning() to tell the server owner that the config was outdated and that it was updated. Then, I am going to call that method in the LoadConfig() override we created earlier.

cs
    protected override void LoadConfig()
    {
        base.LoadConfig();
        PluginConfig = Config.ReadObject<Configuration>();
        if (PluginConfig == null)
        {
            PluginConfig = new Configuration();
            SaveConfig();
            return;
        }
        
        UpdateConfig();
    }

    private void UpdateConfig()
    {
        if (PluginConfig.Version >= Version) return;
        PrintWarning("Outdated configuration file detected. Updating...");
    }

Now, to add the new config option we need to set it, update the version, and save the config. So we will set the PluginConfig.IsEnabled field to true, and set the PluginConfig.Version to Version which is a public instance of the actual plugin version from the metadata header required for the plugin. Once that is done, we will call the SaveConfig() override we created to ensure the config file gets updated correctly.

cs
    private void UpdateConfig()
    {
        if (PluginConfig.Version >= Version) return;
        PrintWarning("Outdated configuration file detected. Updating...");
        PluginConfig.IsEnabled = true;
        PluginConfig.Version = Version;
        SaveConfig();
    }

So now, lets implement this new config in the OnPlayerConnected() hook we are listening to by adding an if statement at the top of the hook that returns if the new field is not true.

cs
    private void OnPlayerConnected(BasePlayer player)
    {
        if (!PluginConfig.IsEnabled) return;
        if(permission.UserHasPermission(player.UserIDString,PluginConfig.UsePermission))
        {
            server.Broadcast("Someone has connected with the Cool Plugin features enabled!");
        }
        else
        {
            server.Broadcast("Someone has connected without the Cool Plugin features enabled.");
        }
    }

Awesome! You did it! Now, lets put it together in a full plugin!

Putting it all together!

Now let's wrap this up in a nice neat little bow! In this tutorial, we created a config class, learned how to create, save, and load the config, and how to update the config as the plugin grows!

cs
using Newtonsoft.Json;
using Oxide.Core;

namespace Oxide.Plugins;

[Info("CoolPlugin", "Bubbafett", "1.0.1")]
[Description("Cool plugin that tells players about cool permissions.")]
public class ConfigExample : RustPlugin
{
    #region Config
    
    public Configuration PluginConfig;

    public class Configuration
    {
        [JsonProperty(PropertyName = "Version (DO NOT CHANGE)", Order = int.MaxValue)]
        public VersionNumber Version = new(1, 0, 1);
        
        [JsonProperty(PropertyName = "Permission")]
        public string UsePermission = "CoolPlugin.use";
        
        [JsonProperty(PropertyName = "Permission Enabled")]
        public bool IsEnabled = true;
    }
    
    protected override void LoadDefaultConfig() => PluginConfig = new Configuration();
    
    protected override void SaveConfig() => Config.WriteObject(PluginConfig, true);
    
    protected override void LoadConfig()
    {
        base.LoadConfig();
        PluginConfig = Config.ReadObject<Configuration>();
        if (PluginConfig == null)
        {
            PluginConfig = new Configuration();
            SaveConfig();
            return;
        }
        
        UpdateConfig();
    }

    private void UpdateConfig()
    {
        if (PluginConfig.Version >= Version) return;
        PrintWarning("Outdated configuration file detected. Updating...");
        PluginConfig.IsEnabled = true;
        PluginConfig.Version = Version;
        SaveConfig();
    }
    
    #endregion
    #region Hooks
    
    private void Init()
    {
        permission.RegisterPermission(PluginConfig.UsePermission, this);
    }
    
    private void OnPlayerConnected(BasePlayer player)
    {
        if (!PluginConfig.IsEnabled) return;
        if(permission.UserHasPermission(player.UserIDString,PluginConfig.UsePermission))
        {
            server.Broadcast("Someone has connected with the Cool Plugin features enabled!");
        }
        else
        {
            server.Broadcast("Someone has connected without the Cool Plugin features enabled.");
        }
    }
    
    #endregion
}

Thanks!

This tutorial was made possible thanks to awesome community members who offered help and support!

Special thanks to:

  • ViolationHandler.exe - How to update the config as the plugin updates
  • Raul - Setting the PluginConfig field on LoadDefaultConfig()
  • Whispers88 - General Config handling

Join us!

Feel free to join us on our official Discord server if you have any other questions!

Read More

There's more where that came from! Choose what you wanna learn about next.

Released under the MIT License. Feel free to help us improve!