From 5bf3888e27c5c7482844b98fe2623b4afdfbf168 Mon Sep 17 00:00:00 2001 From: key44 Date: Sun, 20 Apr 2025 18:37:48 +0200 Subject: [PATCH 1/4] Feature: Add extension methods for storing custom data based on JSON in BaseSystemData --- general/game/extensions/DataExtension.cs | 50 ++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 general/game/extensions/DataExtension.cs diff --git a/general/game/extensions/DataExtension.cs b/general/game/extensions/DataExtension.cs new file mode 100644 index 0000000..b20d189 --- /dev/null +++ b/general/game/extensions/DataExtension.cs @@ -0,0 +1,50 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +namespace NeoModLoader.General.Game.extensions; + +public static class DataExtension +{ + public static bool TryGet(this BaseSystemData data, string key, out TCustomData result) where TCustomData : ICustomData, new() + { + result = new TCustomData(); + data.get(key, out string json); + if (json == null) + { + return false; + } + JObject json_obj; + try + { + json_obj = JObject.Parse(json); + } + catch (JsonReaderException) + { + return false; + } + var serialized_data = json_obj.ToObject(); + if (serialized_data == null) + { + return false; + } + result.Deserialize(serialized_data); + return true; + } + + public static void Set(this BaseSystemData data, string key, TCustomData value) where TCustomData : ICustomData + { + data.set(key, JsonConvert.SerializeObject(value.Serialize())); + } +} + +public class SerializedCustomData +{ + public string ModId; + public string DataVersion; + public JObject Data; +} + +public interface ICustomData +{ + public SerializedCustomData Serialize(); + public void Deserialize(SerializedCustomData data); +} \ No newline at end of file From 1940d340d79b468e324f9c29ad61748304564449 Mon Sep 17 00:00:00 2001 From: key44 Date: Sun, 20 Apr 2025 19:13:28 +0200 Subject: [PATCH 2/4] Repo: Add documentation for all DataExtension classes --- general/game/extensions/DataExtension.cs | 53 ++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/general/game/extensions/DataExtension.cs b/general/game/extensions/DataExtension.cs index b20d189..d776490 100644 --- a/general/game/extensions/DataExtension.cs +++ b/general/game/extensions/DataExtension.cs @@ -2,8 +2,19 @@ using Newtonsoft.Json.Linq; namespace NeoModLoader.General.Game.extensions; +/// +/// Extension helper for using to persistently store data objects via JSON serialization +/// public static class DataExtension { + /// + /// Try to get the custom string data at a specific key as an object of type TCustomData + /// + /// The to access the storage of + /// The key the serialized custom data is stored at + /// A custom data object, either with the serialized custom data from key deserialized if true is returned or in its default state if false is returned + /// The expected data format at the specified key, has to implement + /// Whether a valid JSON string was stored at the specified key public static bool TryGet(this BaseSystemData data, string key, out TCustomData result) where TCustomData : ICustomData, new() { result = new TCustomData(); @@ -30,21 +41,63 @@ public static class DataExtension return true; } + /// + /// Set the custom string data at a specific key as the serialized JSON string of an object of type TCustomData + /// + /// The to access the storage of + /// The key the serialized custom data should be stored at + /// The custom data object that should be serialized and stored at the specified key + /// The expected data format at the specified key, has to implement public static void Set(this BaseSystemData data, string key, TCustomData value) where TCustomData : ICustomData { data.set(key, JsonConvert.SerializeObject(value.Serialize())); } } +/// +/// A storage wrapper for representing the serialized data of an implementation +/// public class SerializedCustomData { + /// + /// The constructor of + /// + /// + /// + /// + public SerializedCustomData(string modId, string dataVersion, JObject data) + { + ModId = modId; + DataVersion = dataVersion; + Data = data; + } + /// + /// A mod ID provided by the implementation on serialization for being able to ensure that the mod serializing the data is also the mod deserializing the data + /// public string ModId; + /// + /// A version string provided by the implementation on serialization for being able to detect after potential data model changes by the mod if a given piece of data is serialized in an old format by the mod or an up to date one + /// public string DataVersion; + /// + /// A of the actual variable custom data that the implementation has to serialize/deserialize + /// public JObject Data; } +/// +/// An interface for representing classes by mods that can serialize/deserialize themselves as persistent custom data for a object +/// public interface ICustomData { + /// + /// Method that's supposed to serialize custom data and return it as a object with a and to allow to determine whether it can properly deserialize a given piece of data and as storage of the actual custom data that can deserialize + /// + /// The with all fields filled in correctly public SerializedCustomData Serialize(); + /// + /// Method that's supposed to deserialize custom data generated by onto itself if it can verify based on and that it is capable of it + /// + /// The custom data by to deserialize public void Deserialize(SerializedCustomData data); } \ No newline at end of file From 10fef1074f309585cfaf39b420804cdf952c28d2 Mon Sep 17 00:00:00 2001 From: key44 Date: Sun, 20 Apr 2025 19:52:25 +0200 Subject: [PATCH 3/4] Feature: Add very simplistic BasicCustomData and according documentation for basic experimentation with the custom data extensions system --- general/game/extensions/DataExtension.cs | 50 ++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/general/game/extensions/DataExtension.cs b/general/game/extensions/DataExtension.cs index d776490..ee80424 100644 --- a/general/game/extensions/DataExtension.cs +++ b/general/game/extensions/DataExtension.cs @@ -100,4 +100,54 @@ public interface ICustomData /// /// The custom data by to deserialize public void Deserialize(SerializedCustomData data); +} + +/// +/// A very basic implementation of to use with that does not have support for versioning +/// +/// A class that contains whatever properties zou would like to be able to store as persistent custom data +/// Due to the complete lack of versioning support, we recommend making a custom implementation of instead of using this. This class is sealed to make it clear that trying to bodge on advanced functionality onto it is a bad idea. +public sealed class BasicCustomData : ICustomData where TDataClass : class, new() +{ + /// + /// The actual data object that is serialized and deserialized + /// + public TDataClass Data { get; private set; } = new TDataClass(); + + /// + /// An ID provided by the mod that instantiated this + /// + private string ModId { get; } + + /// + /// The constructor of + /// + /// + /// , not passing an argument simply creates an empty default data entry + public BasicCustomData(string modID, TDataClass data = null) + { + ModId = modID; + if (data != null) + { + Data = data; + } + } + + /// + /// This basic implementation simply uses the default reflection based JSON object serializer and does not make a proper mod/version distinction, we recommend against trying to use it for advanced use cases. + public SerializedCustomData Serialize() + { + return new SerializedCustomData(ModId, "NO-VERSIONING-SUPPORT", JObject.FromObject(Data)); + } + + /// + /// This basic implementation simply uses the default reflection based JSON object serializer and does not make a proper mod/version distinction, we recommend against trying to use it for advanced use cases. + public void Deserialize(SerializedCustomData data) + { + if (data.ModId != ModId || data.DataVersion != "NO-VERSIONING-SUPPORT") + { + throw new Exception("Supplied data object is not compatible with the basic custom data serializer, mod ID or version mismatch"); + } + Data = data.Data.ToObject(); + } } \ No newline at end of file From 1cb0a9486b1676b8b13f24c7a70faa2b327a1c97 Mon Sep 17 00:00:00 2001 From: key44 Date: Sun, 20 Apr 2025 20:36:32 +0200 Subject: [PATCH 4/4] Bugfix: Make BasicCustomData fully compatible with Get/Set methods --- general/game/extensions/DataExtension.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/general/game/extensions/DataExtension.cs b/general/game/extensions/DataExtension.cs index ee80424..78bcf9f 100644 --- a/general/game/extensions/DataExtension.cs +++ b/general/game/extensions/DataExtension.cs @@ -114,37 +114,35 @@ public interface ICustomData /// public TDataClass Data { get; private set; } = new TDataClass(); - /// - /// An ID provided by the mod that instantiated this - /// - private string ModId { get; } - /// /// The constructor of /// - /// /// , not passing an argument simply creates an empty default data entry - public BasicCustomData(string modID, TDataClass data = null) + public BasicCustomData(TDataClass data) { - ModId = modID; if (data != null) { Data = data; } } + /// + /// The constructor of + /// + public BasicCustomData() : this(null) {} + /// /// This basic implementation simply uses the default reflection based JSON object serializer and does not make a proper mod/version distinction, we recommend against trying to use it for advanced use cases. public SerializedCustomData Serialize() { - return new SerializedCustomData(ModId, "NO-VERSIONING-SUPPORT", JObject.FromObject(Data)); + return new SerializedCustomData("UNKNOWN", "NO-VERSIONING-SUPPORT", JObject.FromObject(Data)); } /// /// This basic implementation simply uses the default reflection based JSON object serializer and does not make a proper mod/version distinction, we recommend against trying to use it for advanced use cases. public void Deserialize(SerializedCustomData data) { - if (data.ModId != ModId || data.DataVersion != "NO-VERSIONING-SUPPORT") + if (data.ModId != "UNKNOWN" || data.DataVersion != "NO-VERSIONING-SUPPORT") { throw new Exception("Supplied data object is not compatible with the basic custom data serializer, mod ID or version mismatch"); }