Plugin Tutorial: Managing Settings

Most plugins will need to save and load settings. Melon provides IStorageAPI for this purpose. It allows you to define an object as your settings class and save/load it in one line, along with built in security with encrypted fields. This saves the object to the Melon/Configs folder as <name>.json.

Step-by-Step Guide

1. Create a Configuration Class

Define a class to hold your plugin's settings. This can support any values json supports.


    public class MyPluginConfig
    {
        public string GreetingMessage { get; set; }
        public bool EnableFeatureX { get; set; }
    }
        

2. Load and Save Settings Using IStorageAPI

a. Load Settings


    public MyPluginConfig Config { get; set; }
    private void LoadConfig()
    {
        Config = Host.Storage.LoadConfigFile<MyPluginConfig>("MyPluginConfig", null, out bool converted);
        if (Config == null)
        {
            Config = new MyPluginConfig
            {
                GreetingMessage = "Hello from My First Plugin!",
                EnableFeatureX = true
            };
            Host.Storage.SaveConfigFile("MyPluginConfig", Config, null);
        }
    }
        

b. Save Settings


    private void SaveConfig()
    {
        Host.Storage.SaveConfigFile("MyPluginConfig", Config, null);
    }
        

3. Use Settings in Your Plugin

Modify your plugin main menu code to use the greeting setting.


    private void MyPluginMenu()
    {
        while (true)
        {
            Host.MelonUI.BreadCrumbBar(new List<string> { "Melon", "My First Plugin" });
            var choice = Host.MelonUI.OptionPicker(new List<string>
            {
                "Back",
                "Greet",
                "Show Date",
                "Perform Calculation"
            });
    
            switch (choice)
            {
                case "Back":
                    return;
                case "Greet":
                    // Use the new config set greeting 
                    Console.WriteLine(Config.GreetingMessage);
                    break;
                case "Show Date":
                    Console.WriteLine($"Today's date is {DateTime.Now.ToShortDateString()}");
                    break;
                case "Perform Calculation":
                    PerformCalculation();
                    break;
            }
    
            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();
        }
    }
        

4. Create a Settings Menu

Allow users to modify settings.


    private void SettingsMenu()
    {
        while (true)
        {
            Host.MelonUI.BreadCrumbBar(new List<string> { "Melon", "My First Plugin Settings" });
            var choice = Host.MelonUI.OptionPicker(new List<string>
            {
                "Back",
                "Change Greeting Message",
                Config.EnableFeatureX ? "Disable Feature X" : "Enable Feature X"
            });
    
            switch (choice)
            {
                case "Back":
                    return;
                case "Change Greeting Message":
                    Console.Write("Enter new greeting message: ");
                    var input = Console.ReadLine();
                    if (!string.IsNullOrWhiteSpace(input))
                    {
                        Config.GreetingMessage = input;
                        SaveConfig();
                    }
                    break;
                case "Disable Feature X":
                case "Enable Feature X":
                    Config.EnableFeatureX = !Config.EnableFeatureX;
                    SaveConfig();
                    break;
            }
        }
    }
        

5. Update LoadUI Method to Include Settings Menu


    public int LoadUI()
    {
        Host.DisplayManager.MenuOptions.Add("My First Plugin", MyPluginMenu);
        Host.SettingsUI.MenuOptions.Add("My First Plugin Settings", SettingsMenu); // Add new Settings Menu Option to the SettinsUI.MenuOptions
        return 0;
    }
        

6. Initialize Config

Ensure you load the config when the plugin is initialized. Plugins get initialized twice, once for UI and once for Server processes, but you'll want to load your config both times.


    public void LoadMelonCommands(IHost host)
    {
        Host = host;
        LoadConfig();
    }
    public void LoadMelonServerCommands(IWebApi webapi)
    {
        WebApi = webapi;
        LoadConfig();
    }
        

7. Watching for changes

When you update the config file through the console, or with a text editor, the server wont have a way to know the config has changed unless you tell it.


    watcher = new FileSystemWatcher();
    watcher.Path = $"{Host.StateManager.melonPath}/Configs/";
    
    watcher.NotifyFilter = NotifyFilters.LastWrite
                            | NotifyFilters.FileName
                            | NotifyFilters.DirectoryName;
    
    watcher.Filter = "*.json";
    
    FileSystemEventHandler func = (sender, args) =>
    {
        if(args.Name == "MyPluginConfig.json")
        {
            // Check if settings have actually changed
            var temp = Storage.LoadConfigFile<Settings>("MelonSettings", null, out _);
            if (StateManager.MelonSettings == null || temp == null || 
                Storage.PropertiesEqual(StateManager.MelonSettings, temp))
            {
                return;
            }
            StateManager.MelonSettings = temp;
        }
    };
    
    // Add event handlers.
    watcher.Changed += func;
    watcher.Created += func;
    
    watcher.EnableRaisingEvents = true;
        

Make sure to make the watcher variable global, if it is disposed it will stop watching.

8. Rebuild and Test

Last Tutorial: First UI <--- ---> Next Tutorial: Custom Endpoints