Modding:ModConfig
This page was last verified for Vintage Story version 1.20.3.
Mod Config
When developing code mods, it is often important to allow the user to change some values to their liking. To do this, you can create mod config files. The game provides a number of functions that allow you to save and load your mod config in a JSON format.
Creating Config Class
The first step of creating a mod config is to create a class that is JSON serializable. These classes often offer little functionality, but are simply used as definitions for JSON files.
A config class merely consists of a set of serializable data.
public class MyConfigData
{
public bool ShouldModDoAThing = true;
public int DoThingTimes = 3;
public string CustomModString = "Hello World!";
}
When the mod config is stored, the following file will be created:
{
"ShouldModDoAThing": true,
"DoThingTimes": 3,
"CustomModString": "Hello World!"
}
Serializable Objects
Only objects that can be serialized should be included in a mod config class. These include, but are not limited to:
- String
- Bool
- Int
- Float
- Vectors (Vec2f, Vec3f, Vec2i, etc.)
- Other custom classes
Store & Load Mod Config in Api
Mod Config files need to me stored and loaded using the appropriate functions.
The following code sample will load and store a mod config.
public static MyConfigData config;
public override void Start(ICoreAPI api)
{
TryToLoadConfig(api);
}
private void TryToLoadConfig(ICoreAPI api)
{
//It is important to surround the LoadModConfig function in a try-catch.
//If loading the file goes wrong, then the 'catch' block is run.
try
{
config = api.LoadModConfig<MyConfigData>("MyConfigData.json");
if (config == null) //if the 'MyConfigData.json' file isn't found...
{
config = new MyConfigData();
}
//Save a copy of the mod config.
api.StoreModConfig<MyConfigData>(config, "MyConfigData.json");
}
catch (Exception e)
{
//Couldn't load the mod config... Create a new one with default settings, but don't save it.
Mod.Logger.Error("Could not load config! Loading default settings instead.");
Mod.Logger.Error(e);
config = new MyConfigData();
}
}
The loaded config settings can now be used by accessing the 'config' object anywhere in your code.
Seperating Config Files
When creating config files, it is important to remember that the server and client are often logically and physically seperated. Due to this, it is a good idea for mods to have a config file for the client, and a config file for a server.
public static MyClientConfigData clientConfig;
public static MyServerConfigData serverConfig;
public override void StartClientSide(ICoreClientAPI api)
{
TryToLoadClientConfig(api);
}
public override void StartServerSide(ICoreServerAPI api)
{
TryToLoadServerConfig(api);
}
private void TryToLoadClientConfig(ICoreAPI api)
{
//It is important to surround the LoadModConfig function in a try-catch.
//If loading the file goes wrong, then the 'catch' block is run.
try
{
clientConfig = api.LoadModConfig<MyClientConfigData>("MyClientConfigData.json");
if (clientConfig == null) //if the 'MyClientConfigData.json' file isn't found...
{
clientConfig = new MyClientConfigData();
}
//Save a copy of the mod config.
api.StoreModConfig<MyClientConfigData>(clientConfig, "MyClientConfigData.json");
}
catch (Exception e)
{
//Couldn't load the mod config... Create a new one with default settings, but don't save it.
Mod.Logger.Error("Could not load client config! Loading default settings instead.");
Mod.Logger.Error(e);
clientConfig = new MyClientConfigData();
}
}
private void TryToLoadServerConfig(ICoreAPI api)
{
//It is important to surround the LoadModConfig function in a try-catch.
//If loading the file goes wrong, then the 'catch' block is run.
try
{
serverConfig = api.LoadModConfig<MyServerConfigData>("MyServerConfigData.json");
if (serverConfig == null) //if the 'MyServerConfigData.json' file isn't found...
{
serverConfig = new MyServerConfigData();
}
//Save a copy of the mod config.
api.StoreModConfig<MyServerConfigData>(serverConfig, "MyServerConfigData.json");
}
catch (Exception e)
{
//Couldn't load the mod config... Create a new one with default settings, but don't save it.
Mod.Logger.Error("Could not load server config! Loading default settings instead.");
Mod.Logger.Error(e);
serverConfig = new MyServerConfigData();
}
}
|
When accessing a config file in this manner, always make sure to access the appropriate file based on the current client/server side. This can usually be retrieved by checking:
if (api.Side == EnumAppSide.Server)
{
//Server side...
}
else
{
//Client side...
}
Sending Server Config to Clients via World Config
If you need to send configuration values from the server to the client, or client to server, you will likely need to use one of the networking APIs in the game. However, there is an easier method that involves saving data to the world config. The following code snippet will load the config values on the server, and adds them to the World Config (which is a TreeAttribute). The world config is retrieved from clients whenever they join.
public override void StartClientSide(ICoreClientAPI api)
{
TryToLoadClientConfig(api);
int valueFromServer = api.World.Config.GetInt("ServerSideValue", 0);
}
public override void StartServerSide(ICoreServerAPI api)
{
TryToLoadServerConfig(api);
api.World.Config.SetInt("ServerSideValue", serverConfig.ServerSideValueOnly);
}
Further Notes
- When loading, if a property exists in the C# class, but is not found in the JSON file, it will be set to a default value.
- When loading, if a property exists in the JSON file, but is not found in the C# file, it will be ignored.
- When loading, if a config file does not exist at the specified path, it will return null.
- Config files should be stored after they are loaded, to ensure any new properties are added. This will not change player-modified data.
- StoreModConfig will always overwrite an existing file.
- You should not store a new mod config if loading an existing one causes an error. This will remove any player-modified data.
- JSON5 is valid for config files.
- Currently, the config system does not allow specific JsonSerializerSettings, and will always use the default of these.
- Standard JSON.NET features are applied for the class. You can, for example, use the [JsonIgnore] attribute to not save a property.