Configuration
Basic configuration handling
Introduction
Plugin configuration files are in the umod/config directory. By default, a plugin configuration file is named after the plugin that created them with a .json file extension.
Why use configuration files?
A configuration file gives server administrators a convenient way to configure plugin behavior.
Server administrators are typically unfamiliar with programming and should not modify C# code in a production environment.
Developers should implement configuration options for values that non-developers would want to modify. Offer as much flexibility as possible by ensuring that variables are configurable (with sensible defaults).
Configuration schematic
Define a class schematic to implement a serializable contract that represents the shape of the configuration file...
umod/plugins/EpicStuff.cs
namespace uMod.Plugins
{
[Info("Epic Stuff", "Calytic", "1.0.0")]
[Description("Does epic stuff of course")]
class EpicStuff : Plugin
{
[Config]
class DefaultConfig
{
public bool Feature = true;
public int Distance = 10;
}
}
}
When the above plugin loads for the first time, a file with the shape defined above by the DefaultConfig class will be formatted as JSON and written to the filesystem...
umod/config/EpicStuff.json
{
"Feature" : true,
"Distance" : 10
}
TOML
To use the TOML file format instead of JSON, simply specify a [Toml] attribute in addition to the existing [Config] attribute.
[Config, Toml]
class DefaultConfig
{
public bool Feature = true;
public int Distance = 10;
}
umod/config/EpicStuff.toml
Feature = true
Distance = 10
Resolved schematics
For performance reasons, configuration files will be loaded and deserialized asynchronously in a background thread.
The Loaded hook is invoked after all of the configuration files are finished loading in the background thread. Additionally, any number of parameters may be injected to give the plugin an opportunity to store the schematics locally.
DefaultConfig config;
void Loaded(DefaultConfig defaultConfig)
{
config = defaultConfig;
}
Populating schematics
Specify default configuration values with field or property initializers.
[Config]
class DefaultConfig
{
public bool Feature = true;
public int Distance = 10;
}
Sometimes it may be necessary to have additional configuration values that cannot be specified using field or property initializers.
void OnConfigCreate(DefaultConfig defaultConfig)
{
#if UNITY
defaultConfig.Distance = 20;
#endif
}
Saving schematics
Configuration files are loaded automatically when a plugin starts. Make changes directly to the file using the Files.GetDataFile method.
[Command("test")]
void MyTestCommand(IPlayer player)
{
IDataFile<DefaultConfig> file = Files.GetDataFile<DefaultConfig>();
file.SaveAsync()
.Done(delegate()
{
Logger.Info("Config file saved!");
},
delegate(Exception ex)
{
Logger.Error($"File save failed: {ex.Message}");
});
}
The Files abstraction is a simple way to perform filesystem operations on Configuration and Localization data files. For more information on how to use data files, read the Data Files documentation.
Multiple configurations
Define secondary configurations by specifying a name in the Config annotation. Named configurations will be placed in a sub-directory.
umod/config/EpicStuff/separate.json
[Config("separate")]
class MyOtherConfig
{
public bool OtherFeature = true;
}
Upgrade path
To automate an upgrade between configuration schematics where backwards compatibility is broken (e.g. modifying a data type, or adding/removing nesting), the Config annotation supports the Version option.
Take for example the first version a configuration schematic as such:
umod/plugins/EpicStuff.cs
[Config(Version="1.0.0")]
class DefaultConfig1
{
public bool FeatureEnabled = true;
public float Distance = 10.01f;
}
The resulting file..
umod/config/EpicStuff.json
{
"FeatureEnabled" : true,
"Distance" : "10.01"
}
Next, copy the DefaultConfig1 schematic and change it's shape in a way that makes the previous schematic invalid. For example, changing the type of the Distance field from float to int.
[Config(Version="2.0.0")]
class DefaultConfig2
{
public bool FeatureEnabled = true;
public int Distance = 10;
}
A fresh plugin installation would use the DefaultConfig2 schematic...
umod/config/EpicStuff.json
{
"FeatureEnabled" : true,
"Distance" : 10
}
But, if updating from the first version to the second, the configuration file will not load (yielding a serialization exception).
To avoid that, handle the transformation between versions:
void OnConfigUpgrade(DefaultConfig1 oldConfig, DefaultConfig2 newConfig)
{
newConfig.Distance = (int)oldConfig.Distance;
}
Skipping versions
When skipping versions, the upgrade path will sequentially iterate through all of the available upgrade hooks.
Again, implement a new schematic (version 3.0.0)...
umod/plugins/EpicStuff.cs
[Config(Version="3.0.0")]
class DefaultConfig3
{
public class MyFeature
{
public bool Enabled = true;
public int OtherDistance = 20;
}
public MyFeature Feature = new MyFeature();
public int Distance = 10;
}
A fresh installation would look like...
umod/config/EpicStuff.json
{
"Feature" : {
"Enabled" : true,
"OtherDistance" : 20
},
"Distance" : 10
}
Implement another upgrade hook for the final transformation.
void OnConfigUpgrade(DefaultConfig2 oldConfig, DefaultConfig3 newConfig)
{
newConfig.Feature.Enabled = oldConfig.FeatureEnabled;
}
Finally, when upgrading from version 1.0.0 to 3.0.0 (skipping 2.0.0) the upgrade hooks are invoked sequentially...
OnConfigUpgrade(DefaultConfig1, DefaultConfig2);
OnConfigUpgrade(DefaultConfig2, DefaultConfig3);
Configuration monitor
Configuration monitoring, if enabled in the global plugin configuration, will automatically reload a plugin when it's configuration file is modified.
Listen for configuration changes with the OnConfigChanged hook. Implementing OnConfigChanged will prevent a plugin from being reloaded automatically and allow a plugin to handle changes locally.
void OnConfigChanged(DefaultConfig newConfig)
{
config = newConfig;
}