-
-
Notifications
You must be signed in to change notification settings - Fork 311
Simplify how mods use config.json #159
Description
Consider simplifying how mods deal with config.json.
Current system
Here's how mods use config.json now:
-
The mod creates a subclass of
StardewModdingAPI.Configwith an overridden method:internal class SampleConfig : StardewModdingAPI.Config { public bool ExampleBoolean { get; set; } public float ExampleFloat { get; set; } public override T GenerateDefaultConfig<T>() { this.ExampleBoolean = true; this.ExampleFloat = 0.5; return this as T; } }
-
The mod loads it by calling a method on the subclass with the mod's config path:
var config = new SampleConfig().InitializeConfig(this.BaseConfigPath);
If the mod wants to use a separate JSON file (e.g. Lookup Anything's data.json):
-
Reference the
Newtonsoft.Jsonpackage. (Make sure to use the same version as SMAPI to avoid issues.) -
Read the file:
string path = Path.Combine(this.PathOnDisk, "data.json"); string data = File.ReadAllText(path); var customModel = JsonConvert.DeserialiseObject<Model>(data);
-
Optionally resave the file if you want to use SMAPI's auto-create logic:
string data = JsonConvert.SerialiseObject(customModel); File.WriteAllText(path, data);
Pros & cons of current system
-
Advantages:
- ✓ Existing mods already use it.
- ✓ It's well-tested, since it's been in use for seven months now.
-
Disadvantages:
- ✘ It ignores C# conventions.
This makes it harder for new mod developers. For example, it's easy to forget to overrideConfig.GenerateDefaultConfig<T>, which will break. It's also not clear how it should be used — should you set the defaults in the constructor and return the instance, or set the defaults in that method and return the instance, or return a new object? Why not use a constructor? - ✘ The interface is confusing and exposes SMAPI internals.
For example, what's the difference betweenConfig.InitializeConfig<T>(),Config.LoadConfig<T>(),Config.ReloadConfig<T>(), andConfig.Instance<T>()? How about betweenConfig.UpdateConfig<T>()andConfig.WriteConfig<T>()? - ✘ It adds a lot of boilerplate.
Ideally the mod author shouldn't need to care how the config system works (e.g. implementing an internal base class or explicitly calling an initialiser). - ✘ If the mod wants to use custom JSON files, it needs to reference the underlying library and do it manually.
- ✘ The internal implementation is a bit complicated, since it reinvents some things that C# and the JSON library do out of the box (like merging defaults).
- ✘ It ignores C# conventions.
Proposed system
Here's one idea for a new config system:
-
The mod creates a plain class and sets defaults in the usual C# way:
internal class SampleConfig { public bool ExampleBoolean { get; set; } = true; public float ExampleFloat { get; set; } = 0.5; }
(A constructor would work too.)
-
The mod loads it using a mod method:
var config = this.Helper.ReadConfig<SampleConfig>();
If the mod wants to use a separate JSON file (e.g. Lookup Anything's data.json):
-
The mod loads it the same way:
var model = this.Helper.ReadJson<CustomModel>("data.json");
Pros & cons of proposed system
-
Advantages:
- ✓ It uses C# conventions.
This reduces the learning curve, since mod developers can apply their existing knowledge. They already know how to set default values in C#; why reinvent the wheel? - ✓ The interface is discoverable.
The mod's base class only has two properties:this.Manifestandthis.Helper. It's easy to discover what features are available without needing to look up separate documentation. The SMAPI internals are hidden away to avoid confusion. - ✓ It eliminates boilerplate.
The mod author just creates a simple class — no base class, overridden methods, or passing paths into a certain method. - ✓ If the mod wants to use custom JSON files, that's easy to do.
- ✓ The
ModHelperis easy to extend with more methods in the future. - ✓ It's easy to implement internally, since that's how the Json.NET library is meant to be used.
- ✓ It uses C# conventions.
-
Disadvantages:
- ✘ For backwards compatibility with existing mods, SMAPI would need to support the old system too (and mark it deprecated).