Skip to content

Commit

Permalink
tweak schema generation code & update installer
Browse files Browse the repository at this point in the history
  • Loading branch information
Pathoschild committed Jul 6, 2024
1 parent 4fd8a9f commit a7a9a74
Show file tree
Hide file tree
Showing 9 changed files with 52 additions and 47 deletions.
1 change: 1 addition & 0 deletions build/deploy-local-smapi.targets
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ This assumes `find-game-folder.targets` has already been imported and validated.
<Copy SourceFiles="$(TargetDir)\SMAPI.config.json" DestinationFiles="$(GamePath)\smapi-internal\config.json" />
<Copy SourceFiles="$(TargetDir)\SMAPI.metadata.json" DestinationFiles="$(GamePath)\smapi-internal\metadata.json" />
<Copy SourceFiles="$(TargetDir)\Newtonsoft.Json.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\Newtonsoft.Json.Schema.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\TMXTile.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="$(TargetDir)\Pintail.dll" DestinationFolder="$(GamePath)\smapi-internal" />
<Copy SourceFiles="@(TranslationFiles)" DestinationFolder="$(GamePath)\smapi-internal\i18n" />
Expand Down
2 changes: 1 addition & 1 deletion build/unix/prepare-install-package.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion build/windows/prepare-install-package.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}

Expand Down
37 changes: 20 additions & 17 deletions src/SMAPI.Toolkit/Serialization/JsonHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,26 @@ namespace StardewModdingAPI.Toolkit.Serialization
public class JsonHelper
{
/*********
** Accessors
** Fields
*********/
/// <summary>The JSON settings to use when serializing and deserializing files.</summary>
public JsonSerializerSettings JsonSettings { get; } = JsonHelper.CreateDefaultSettings();

private readonly JSchemaGenerator _schemaGenerator = new();
/// <summary>The JSON schema generator to use when creating a schema file.</summary>
private readonly JSchemaGenerator SchemaGenerator = new();

private readonly JSchemaWriterSettings _schemaWriterSettings = new()
/// <summary>The JSON settings to use when creating a schema file.</summary>
private readonly JSchemaWriterSettings SchemaWriterSettings = new()
{
Version = SchemaVersion.Draft2019_09,
ReferenceHandling = JSchemaWriterReferenceHandling.Never
};


/*********
** Accessors
*********/
/// <summary>The JSON settings to use when serializing and deserializing files.</summary>
public JsonSerializerSettings JsonSettings { get; } = JsonHelper.CreateDefaultSettings();


/*********
** Public methods
*********/
Expand Down Expand Up @@ -109,9 +116,7 @@ public void WriteJsonFile<TModel>(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);

Expand All @@ -120,7 +125,7 @@ public void WriteJsonFile<TModel>(string fullPath, TModel model)
File.WriteAllText(fullPath, json);
}

/// <summary>Generate a schema and save to a JSON file.</summary>
/// <summary>Save a data model schema to a JSON file.</summary>
/// <typeparam name="TModel">The model type.</typeparam>
/// <param name="fullPath">The absolute file path.</param>
/// <exception cref="InvalidOperationException">The given path is empty or invalid.</exception>
Expand All @@ -132,16 +137,14 @@ public void WriteJsonSchemaFile<TModel>(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);
}

/// <summary>Deserialize JSON text if possible.</summary>
Expand Down
14 changes: 8 additions & 6 deletions src/SMAPI/Framework/ModHelpers/DataHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ internal class DataHelper : BaseHelper, IDataHelper
/// <summary>Construct an instance.</summary>
/// <param name="mod">The mod using this instance.</param>
/// <param name="modFolderPath">The absolute path to the mod folder.</param>
/// <param name="jsonHelper">The absolute path to the mod folder.</param>
/// <param name="jsonHelper">Encapsulates SMAPI's JSON file parsing.</param>
public DataHelper(IModMetadata mod, string modFolderPath, JsonHelper jsonHelper)
: base(mod)
{
Expand Down Expand Up @@ -67,19 +67,21 @@ public void WriteJsonFile<TModel>(string path, TModel? data)
File.Delete(path);
}

/// <inheritdoc/>
public void WriteJsonSchemaFile<TModel>(string path, TModel data) where TModel : class
/// <inheritdoc />
public void WriteJsonSchemaFile<TModel>(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<TModel>(path);
}

/****
** Save file
****/
** Save file
****/
/// <inheritdoc />
public TModel? ReadSaveData<TModel>(string key)
where TModel : class
Expand Down
24 changes: 8 additions & 16 deletions src/SMAPI/Framework/ModHelpers/ModHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ internal class ModHelper : BaseHelper, IModHelper, IDisposable
/*********
** Fields
*********/
/// <summary>Whether to generate config.schema.json files.</summary>
private readonly bool _generateConfigSchemas;
/// <summary>Whether to generate a <c>config.schema.json</c> file based on the mod's config model when it's loaded or saved.</summary>
private readonly bool GenerateConfigSchema;


/*********
** Accessors
Expand Down Expand Up @@ -71,10 +72,10 @@ internal class ModHelper : BaseHelper, IModHelper, IDisposable
/// <param name="reflectionHelper">An API for accessing private game code.</param>
/// <param name="multiplayer">Provides multiplayer utilities.</param>
/// <param name="translationHelper">An API for reading translations stored in the mod's <c>i18n</c> folder.</param>
/// <param name="generateConfigSchemas">Whether to generate config.schema.json files.</param>
/// <param name="generateConfigSchema">Whether to generate a <c>config.schema.json</c> file based on the mod's config model when it's loaded or saved.</param>
/// <exception cref="ArgumentNullException">An argument is null or empty.</exception>
/// <exception cref="InvalidOperationException">The <paramref name="modDirectory"/> path does not exist on disk.</exception>
public ModHelper(IModMetadata mod, string modDirectory, Func<SInputState> 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<SInputState> 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
Expand All @@ -96,7 +97,7 @@ public ModHelper(IModMetadata mod, string modDirectory, Func<SInputState> 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;
}

/****
Expand All @@ -108,12 +109,6 @@ public TConfig ReadConfig<TConfig>()
{
TConfig config = this.Data.ReadJsonFile<TConfig>("config.json") ?? new TConfig();
this.WriteConfig(config); // create file or fill in missing fields

if (this._generateConfigSchemas)
{
this.WriteConfigSchema(config);
}

return config;
}

Expand All @@ -122,12 +117,9 @@ public void WriteConfig<TConfig>(TConfig config)
where TConfig : class, new()
{
this.Data.WriteJsonFile("config.json", config);
}

private void WriteConfigSchema<TConfig>(TConfig config)
where TConfig : class, new()
{
this.Data.WriteJsonSchemaFile("config.schema.json", config);
if (this.GenerateConfigSchema)
this.Data.WriteJsonSchemaFile<TConfig>("config.schema.json");
}

/****
Expand Down
5 changes: 3 additions & 2 deletions src/SMAPI/Framework/Models/SConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

/// <summary>The default values for <see cref="SuppressUpdateChecks"/>, to log changes if different.</summary>
Expand Down Expand Up @@ -92,9 +92,10 @@ internal class SConfig
/// <summary>The mod IDs SMAPI should load after any other mods.</summary>
public HashSet<string> ModsToLoadLate { get; set; }

/// <summary>Whether to generate config.schema.json files for external tools like mod managers.</summary>
/// <summary>Whether to generate <c>config.schema.json</c> files for external tools like mod managers.</summary>
public bool GenerateConfigSchemas { get; set; }


/********
** Public methods
********/
Expand Down
5 changes: 2 additions & 3 deletions src/SMAPI/IDataHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,11 @@ public interface IDataHelper
void WriteJsonFile<TModel>(string path, TModel? data)
where TModel : class;

/// <summary>Save the schema of the data to a JSON file in the mod's folder.</summary>
/// <summary>Save a data model schema to a JSON file in the mod's folder.</summary>
/// <typeparam name="TModel">The model type. This should be a plain class that has public properties for the data you want. The properties can be complex types.</typeparam>
/// <param name="path">The file path relative to the mod folder.</param>
/// <param name="data">The arbitrary data to save.</param>
/// <exception cref="InvalidOperationException">The <paramref name="path"/> is not relative or contains directory climbing (../).</exception>
void WriteJsonSchemaFile<TModel>(string path, TModel data)
void WriteJsonSchemaFile<TModel>(string path)
where TModel : class;

/****
Expand Down
9 changes: 8 additions & 1 deletion src/SMAPI/SMAPI.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

0 comments on commit a7a9a74

Please sign in to comment.