diff --git a/build/deploy-local-smapi.targets b/build/deploy-local-smapi.targets
index d30967fe5..bc97dedf8 100644
--- a/build/deploy-local-smapi.targets
+++ b/build/deploy-local-smapi.targets
@@ -22,6 +22,7 @@ This assumes `find-game-folder.targets` has already been imported and validated.
+
diff --git a/build/unix/prepare-install-package.sh b/build/unix/prepare-install-package.sh
index cfd24afd4..b70fbca56 100755
--- a/build/unix/prepare-install-package.sh
+++ b/build/unix/prepare-install-package.sh
@@ -134,7 +134,7 @@ for folder in ${folders[@]}; do
cp -r "$smapiBin/i18n" "$bundlePath/smapi-internal"
# bundle smapi-internal
- for name in "0Harmony.dll" "0Harmony.xml" "Mono.Cecil.dll" "Mono.Cecil.Mdb.dll" "Mono.Cecil.Pdb.dll" "MonoMod.Common.dll" "Newtonsoft.Json.dll" "Pathoschild.Http.Client.dll" "Pintail.dll" "TMXTile.dll" "SMAPI.Toolkit.dll" "SMAPI.Toolkit.xml" "SMAPI.Toolkit.CoreInterfaces.dll" "SMAPI.Toolkit.CoreInterfaces.xml" "System.Net.Http.Formatting.dll"; do
+ for name in "0Harmony.dll" "0Harmony.xml" "Mono.Cecil.dll" "Mono.Cecil.Mdb.dll" "Mono.Cecil.Pdb.dll" "MonoMod.Common.dll" "Newtonsoft.Json.dll" "Newtonsoft.Json.Schema.dll" "Pathoschild.Http.Client.dll" "Pintail.dll" "TMXTile.dll" "SMAPI.Toolkit.dll" "SMAPI.Toolkit.xml" "SMAPI.Toolkit.CoreInterfaces.dll" "SMAPI.Toolkit.CoreInterfaces.xml" "System.Net.Http.Formatting.dll"; do
cp "$smapiBin/$name" "$bundlePath/smapi-internal"
done
diff --git a/build/windows/prepare-install-package.ps1 b/build/windows/prepare-install-package.ps1
index 48c013ff0..00a719efe 100644
--- a/build/windows/prepare-install-package.ps1
+++ b/build/windows/prepare-install-package.ps1
@@ -155,7 +155,7 @@ foreach ($folder in $folders) {
cp -Recurse "$smapiBin/i18n" "$bundlePath/smapi-internal"
# bundle smapi-internal
- foreach ($name in @("0Harmony.dll", "0Harmony.xml", "Mono.Cecil.dll", "Mono.Cecil.Mdb.dll", "Mono.Cecil.Pdb.dll", "MonoMod.Common.dll", "Newtonsoft.Json.dll", "Pathoschild.Http.Client.dll", "Pintail.dll", "TMXTile.dll", "SMAPI.Toolkit.dll", "SMAPI.Toolkit.xml", "SMAPI.Toolkit.CoreInterfaces.dll", "SMAPI.Toolkit.CoreInterfaces.xml", "System.Net.Http.Formatting.dll")) {
+ foreach ($name in @("0Harmony.dll", "0Harmony.xml", "Mono.Cecil.dll", "Mono.Cecil.Mdb.dll", "Mono.Cecil.Pdb.dll", "MonoMod.Common.dll", "Newtonsoft.Json.dll", "Newtonsoft.Json.Schema.dll", "Pathoschild.Http.Client.dll", "Pintail.dll", "TMXTile.dll", "SMAPI.Toolkit.dll", "SMAPI.Toolkit.xml", "SMAPI.Toolkit.CoreInterfaces.dll", "SMAPI.Toolkit.CoreInterfaces.xml", "System.Net.Http.Formatting.dll")) {
cp "$smapiBin/$name" "$bundlePath/smapi-internal"
}
diff --git a/src/SMAPI.Toolkit/Serialization/JsonHelper.cs b/src/SMAPI.Toolkit/Serialization/JsonHelper.cs
index 0552a23e1..a1ae915af 100644
--- a/src/SMAPI.Toolkit/Serialization/JsonHelper.cs
+++ b/src/SMAPI.Toolkit/Serialization/JsonHelper.cs
@@ -14,19 +14,26 @@ namespace StardewModdingAPI.Toolkit.Serialization
public class JsonHelper
{
/*********
- ** Accessors
+ ** Fields
*********/
- /// The JSON settings to use when serializing and deserializing files.
- public JsonSerializerSettings JsonSettings { get; } = JsonHelper.CreateDefaultSettings();
-
- private readonly JSchemaGenerator _schemaGenerator = new();
+ /// The JSON schema generator to use when creating a schema file.
+ private readonly JSchemaGenerator SchemaGenerator = new();
- private readonly JSchemaWriterSettings _schemaWriterSettings = new()
+ /// The JSON settings to use when creating a schema file.
+ private readonly JSchemaWriterSettings SchemaWriterSettings = new()
{
Version = SchemaVersion.Draft2019_09,
ReferenceHandling = JSchemaWriterReferenceHandling.Never
};
+
+ /*********
+ ** Accessors
+ *********/
+ /// The JSON settings to use when serializing and deserializing files.
+ public JsonSerializerSettings JsonSettings { get; } = JsonHelper.CreateDefaultSettings();
+
+
/*********
** Public methods
*********/
@@ -109,9 +116,7 @@ public void WriteJsonFile(string fullPath, TModel model)
throw new ArgumentException("The file path is empty or invalid.", nameof(fullPath));
// create directory if needed
- string dir = Path.GetDirectoryName(fullPath)!;
- if (dir == null)
- throw new ArgumentException("The file path is invalid.", nameof(fullPath));
+ string dir = Path.GetDirectoryName(fullPath) ?? throw new ArgumentException("The file path is invalid.", nameof(fullPath));
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
@@ -120,7 +125,7 @@ public void WriteJsonFile(string fullPath, TModel model)
File.WriteAllText(fullPath, json);
}
- /// Generate a schema and save to a JSON file.
+ /// Save a data model schema to a JSON file.
/// The model type.
/// The absolute file path.
/// The given path is empty or invalid.
@@ -132,16 +137,14 @@ public void WriteJsonSchemaFile(string fullPath)
throw new ArgumentException("The file path is empty or invalid.", nameof(fullPath));
// create directory if needed
- string dir = Path.GetDirectoryName(fullPath)!;
- if (dir == null)
- throw new ArgumentException("The file path is invalid.", nameof(fullPath));
+ string dir = Path.GetDirectoryName(fullPath) ?? throw new ArgumentException("The file path is invalid.", nameof(fullPath));
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
- JSchema schema = this._schemaGenerator.Generate(typeof(TModel));
- string output = schema.ToString(this._schemaWriterSettings);
-
- File.WriteAllText(fullPath, output);
+ // write file
+ JSchema schema = this.SchemaGenerator.Generate(typeof(TModel));
+ string json = schema.ToString(this.SchemaWriterSettings);
+ File.WriteAllText(fullPath, json);
}
/// Deserialize JSON text if possible.
diff --git a/src/SMAPI/Framework/ModHelpers/DataHelper.cs b/src/SMAPI/Framework/ModHelpers/DataHelper.cs
index dd8cd373e..69b20b1ac 100644
--- a/src/SMAPI/Framework/ModHelpers/DataHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/DataHelper.cs
@@ -28,7 +28,7 @@ internal class DataHelper : BaseHelper, IDataHelper
/// Construct an instance.
/// The mod using this instance.
/// The absolute path to the mod folder.
- /// The absolute path to the mod folder.
+ /// Encapsulates SMAPI's JSON file parsing.
public DataHelper(IModMetadata mod, string modFolderPath, JsonHelper jsonHelper)
: base(mod)
{
@@ -67,19 +67,21 @@ public void WriteJsonFile(string path, TModel? data)
File.Delete(path);
}
- ///
- public void WriteJsonSchemaFile(string path, TModel data) where TModel : class
+ ///
+ public void WriteJsonSchemaFile(string path)
+ where TModel : class
{
if (!PathUtilities.IsSafeRelativePath(path))
- throw new InvalidOperationException($"You must call {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.WriteJsonFile)} with a relative path (without directory climbing).");
+ throw new InvalidOperationException($"You must call {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.WriteJsonSchemaFile)} with a relative path (without directory climbing).");
path = Path.Combine(this.ModFolderPath, PathUtilities.NormalizePath(path));
+
this.JsonHelper.WriteJsonSchemaFile(path);
}
/****
- ** Save file
- ****/
+ ** Save file
+ ****/
///
public TModel? ReadSaveData(string key)
where TModel : class
diff --git a/src/SMAPI/Framework/ModHelpers/ModHelper.cs b/src/SMAPI/Framework/ModHelpers/ModHelper.cs
index 13efbd683..e48052aab 100644
--- a/src/SMAPI/Framework/ModHelpers/ModHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ModHelper.cs
@@ -11,8 +11,9 @@ internal class ModHelper : BaseHelper, IModHelper, IDisposable
/*********
** Fields
*********/
- /// Whether to generate config.schema.json files.
- private readonly bool _generateConfigSchemas;
+ /// Whether to generate a config.schema.json file based on the mod's config model when it's loaded or saved.
+ private readonly bool GenerateConfigSchema;
+
/*********
** Accessors
@@ -71,10 +72,10 @@ internal class ModHelper : BaseHelper, IModHelper, IDisposable
/// An API for accessing private game code.
/// Provides multiplayer utilities.
/// An API for reading translations stored in the mod's i18n folder.
- /// Whether to generate config.schema.json files.
+ /// Whether to generate a config.schema.json file based on the mod's config model when it's loaded or saved.
/// An argument is null or empty.
/// The path does not exist on disk.
- public ModHelper(IModMetadata mod, string modDirectory, Func currentInputState, IModEvents events, IGameContentHelper gameContentHelper, IModContentHelper modContentHelper, IContentPackHelper contentPackHelper, ICommandHelper commandHelper, IDataHelper dataHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, IMultiplayerHelper multiplayer, ITranslationHelper translationHelper, bool generateConfigSchemas)
+ public ModHelper(IModMetadata mod, string modDirectory, Func currentInputState, IModEvents events, IGameContentHelper gameContentHelper, IModContentHelper modContentHelper, IContentPackHelper contentPackHelper, ICommandHelper commandHelper, IDataHelper dataHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, IMultiplayerHelper multiplayer, ITranslationHelper translationHelper, bool generateConfigSchema)
: base(mod)
{
// validate directory
@@ -96,7 +97,7 @@ public ModHelper(IModMetadata mod, string modDirectory, Func curren
this.Multiplayer = multiplayer ?? throw new ArgumentNullException(nameof(multiplayer));
this.Translation = translationHelper ?? throw new ArgumentNullException(nameof(translationHelper));
this.Events = events;
- this._generateConfigSchemas = generateConfigSchemas;
+ this.GenerateConfigSchema = generateConfigSchema;
}
/****
@@ -108,12 +109,6 @@ public TConfig ReadConfig()
{
TConfig config = this.Data.ReadJsonFile("config.json") ?? new TConfig();
this.WriteConfig(config); // create file or fill in missing fields
-
- if (this._generateConfigSchemas)
- {
- this.WriteConfigSchema(config);
- }
-
return config;
}
@@ -122,12 +117,9 @@ public void WriteConfig(TConfig config)
where TConfig : class, new()
{
this.Data.WriteJsonFile("config.json", config);
- }
- private void WriteConfigSchema(TConfig config)
- where TConfig : class, new()
- {
- this.Data.WriteJsonSchemaFile("config.schema.json", config);
+ if (this.GenerateConfigSchema)
+ this.Data.WriteJsonSchemaFile("config.schema.json");
}
/****
diff --git a/src/SMAPI/Framework/Models/SConfig.cs b/src/SMAPI/Framework/Models/SConfig.cs
index 42c9e0b52..4c439dab3 100644
--- a/src/SMAPI/Framework/Models/SConfig.cs
+++ b/src/SMAPI/Framework/Models/SConfig.cs
@@ -25,7 +25,7 @@ internal class SConfig
[nameof(RewriteMods)] = true,
[nameof(UseCaseInsensitivePaths)] = Constants.Platform is Platform.Android or Platform.Linux,
[nameof(SuppressHarmonyDebugMode)] = true,
- [nameof(GenerateConfigSchemas)] = false,
+ [nameof(GenerateConfigSchemas)] = false
};
/// The default values for , to log changes if different.
@@ -92,9 +92,10 @@ internal class SConfig
/// The mod IDs SMAPI should load after any other mods.
public HashSet ModsToLoadLate { get; set; }
- /// Whether to generate config.schema.json files for external tools like mod managers.
+ /// Whether to generate config.schema.json files for external tools like mod managers.
public bool GenerateConfigSchemas { get; set; }
+
/********
** Public methods
********/
diff --git a/src/SMAPI/IDataHelper.cs b/src/SMAPI/IDataHelper.cs
index 23d026e31..ff778bc24 100644
--- a/src/SMAPI/IDataHelper.cs
+++ b/src/SMAPI/IDataHelper.cs
@@ -27,12 +27,11 @@ public interface IDataHelper
void WriteJsonFile(string path, TModel? data)
where TModel : class;
- /// Save the schema of the data to a JSON file in the mod's folder.
+ /// Save a data model schema to a JSON file in the mod's folder.
/// The model type. This should be a plain class that has public properties for the data you want. The properties can be complex types.
/// The file path relative to the mod folder.
- /// The arbitrary data to save.
/// The is not relative or contains directory climbing (../).
- void WriteJsonSchemaFile(string path, TModel data)
+ void WriteJsonSchemaFile(string path)
where TModel : class;
/****
diff --git a/src/SMAPI/SMAPI.config.json b/src/SMAPI/SMAPI.config.json
index 1bd467d97..4e64707cb 100644
--- a/src/SMAPI/SMAPI.config.json
+++ b/src/SMAPI/SMAPI.config.json
@@ -158,5 +158,12 @@ in future SMAPI versions.
* the mod author.
*/
"ModsToLoadEarly": [],
- "ModsToLoadLate": []
+ "ModsToLoadLate": [],
+
+ /**
+ * Whether to generate a `config.schema.json` file next to each mod's `config.json` file.
+ *
+ * This can be used by separate tools like mod managers to enable config editing features.
+ */
+ "GenerateConfigSchemas": false
}