From 3f5b981f8eb8cfe9ba8779eb9a6513a75c5e7599 Mon Sep 17 00:00:00 2001 From: MaZiFAU <marco.alexander.zimmer@fau.de> Date: Thu, 18 Aug 2022 19:17:24 +0200 Subject: [PATCH] +adjusted IJSONsavable & included auto pre-/ postprocessing unsing attributes; --- Assets/Scripts/IJSONsavable.cs | 405 ++++++++++++++++++ Assets/Scripts/IJSONsavable.cs.meta | 11 + .../FactHandling/FactOrganizer.cs | 2 +- .../FactHandling/SolutionOrganizer.cs | 3 + Assets/Scripts/JSONManager.cs | 320 +------------- Assets/Scripts/Loading/Stage.cs | 34 +- Assets/Scripts/StageStatic.cs | 2 +- .../UI/MainMenue/PageLoader/CreateLoader.cs | 2 +- 8 files changed, 440 insertions(+), 339 deletions(-) create mode 100644 Assets/Scripts/IJSONsavable.cs create mode 100644 Assets/Scripts/IJSONsavable.cs.meta diff --git a/Assets/Scripts/IJSONsavable.cs b/Assets/Scripts/IJSONsavable.cs new file mode 100644 index 00000000..83571ab2 --- /dev/null +++ b/Assets/Scripts/IJSONsavable.cs @@ -0,0 +1,405 @@ +using System.Collections.Generic; +using System.Reflection; +using System.Linq; +using System.IO; +using System; +using Newtonsoft.Json; +using UnityEngine; +using static CommunicationEvents; + + +// I would go for static virtual methods, but C#9 does not allow me... +// static methods cannot be overwritten -> virtual +public interface IJSONsavable<T> where T : IJSONsavable<T>, new() +{ + // stand-in for non static methods + public static readonly IJSONsavable<T> Instance = new T(); + + public static readonly FieldInfo[] JsonSaveableFields = + #region one-time-initialisation + typeof(T) + .GetFields( + BindingFlags.Instance | + BindingFlags.Public | + BindingFlags.NonPublic | + BindingFlags.Static) + .Where((field) + => !field.GetCustomAttributes().Any((attribute) + => attribute.GetType() == typeof(JsonIgnoreAttribute)) + && field.FieldType.GetInterfaces().Any((inter) + => inter.IsGenericType && inter.GetGenericTypeDefinition() == typeof(IJSONsavable<>))) + .ToArray(); + #endregion one-time-initialisation + + public static readonly FieldInfo[] JsonAutoPreProcessFields = + #region one-time-initialisation + typeof(T) + .GetFields( + BindingFlags.Instance | + BindingFlags.Public | + BindingFlags.NonPublic | + BindingFlags.Static) + .Where((field) + => !field.GetCustomAttributes().Any((attribute) + => attribute.GetType() == typeof(JsonIgnoreAttribute)) + && field.GetCustomAttributes().Any((attribute) + => attribute.GetType() == typeof(JSONsavable.JsonAutoPreProcessAttribute)) + && field.FieldType.GetInterfaces().Any((inter) + => inter.IsGenericType && inter.GetGenericTypeDefinition() == typeof(IJSONsavable<>))) + .ToArray(); + #endregion one-time-initialisation + + public static readonly FieldInfo[] JsonAutoPostProcessFields = + #region one-time-initialisation + typeof(T) + .GetFields( + BindingFlags.Instance | + BindingFlags.Public | + BindingFlags.NonPublic | + BindingFlags.Static) + .Where((field) + => !field.GetCustomAttributes().Any((attribute) + => attribute.GetType() == typeof(JsonIgnoreAttribute)) + && field.GetCustomAttributes().Any((attribute) + => attribute.GetType() == typeof(JSONsavable.JsonAutoPostProcessAttribute)) + && field.FieldType.GetInterfaces().Any((inter) + => inter.IsGenericType && inter.GetGenericTypeDefinition() == typeof(IJSONsavable<>))) + .ToArray(); + #endregion one-time-initialisation + + public static readonly FieldInfo[] JsonSeperateFields = + #region one-time-initialisation + typeof(T) + .GetFields( + BindingFlags.Instance | + BindingFlags.Public | + BindingFlags.NonPublic | + BindingFlags.Static) + .Where((field) + => field.GetCustomAttributes().Any((attribute) + => attribute.GetType() == typeof(JSONsavable.JsonSeparateAttribute)) + && field.FieldType.GetInterfaces().Any((inter) + => inter.IsGenericType && inter.GetGenericTypeDefinition() == typeof(IJSONsavable<>))) + .ToArray(); + #endregion one-time-initialisation + + + // TODO: this? + public string name { get; set; } + public string path { get; set; } + protected static List<Directories> + hierarchie = new List<Directories> { Directories.misc }; + + #region OverridableMethods + + public virtual string _IJGetName(string name) => name; + public virtual List<Directories> _IJGetHierarchie(List<Directories> hierarchie_base) + { + hierarchie_base ??= new List<Directories>(); + return hierarchie_base.Concat(hierarchie).ToList(); + } + public virtual bool _IJGetRawObject(out T payload, string path) => JSONsavable.ReadFromJsonFile<T>(out payload, path); + public virtual T _IJPreProcess(T payload) => payload; + public virtual T _IJPostProcess(T payload) => payload; + + #endregion OverridableMethods + + + #region MethodTemplates + + public bool store(List<Directories> hierarchie, string name, bool use_install_folder = false, bool overwrite = true, bool deep_store = true) + => store(hierarchie, name, (T)this, use_install_folder, overwrite, deep_store); + + public static bool store(List<Directories> hierarchie, string name, T payload, bool use_install_folder = false, bool overwrite = true, bool deep_store = true) + { + var new_hierarchie = + Instance._IJGetHierarchie(hierarchie); + var new_name = + Instance._IJGetName(name); + + string path = CreatePathToFile(out bool exists, new_name, "JSON", new_hierarchie, use_install_folder); + if (exists && !overwrite) + return false; + + // store fields decorated with JsonSeparateAttribute and implementing IJSONsavable<> separately + if (deep_store + && !store_children(hierarchie, name, payload, use_install_folder: false, overwrite, deep_store: true)) + return false; + + // store this + string path_o = payload.path; + payload.path = path; + + var new_payload = + preprocess(payload); + + payload.path = path_o; + + JSONsavable.WriteToJsonFile(path, new_payload); + return true; + } + + public bool store_children(List<Directories> hierarchie, string name, bool use_install_folder = false, bool overwrite = true, bool deep_store = true) + => store_children(hierarchie, name, (T)this, use_install_folder, overwrite, deep_store); + + public static bool store_children(List<Directories> hierarchie, string name, T payload, bool use_install_folder = false, bool overwrite = true, bool deep_store = true) + { + var new_hierarchie = + Instance._IJGetHierarchie(hierarchie); + var new_name = + Instance._IJGetName(name); + + for ((int max_i, bool success) = (0, true); max_i < JsonSeperateFields.Count(); max_i++) + { + var field = JsonSeperateFields[max_i]; + dynamic save_me = field.GetValue(payload); // is S:IJSONsavable<S> + + Type interface_type = typeof(IJSONsavable<>).MakeGenericType(field.FieldType); + Type[] store_args_type = new Type[] { typeof(List<Directories>), typeof(string), field.FieldType, typeof(bool), typeof(bool), typeof(bool) }; + object[] store_args = new object[] { new_hierarchie, new_name, save_me, use_install_folder, overwrite, deep_store }; + + var method = interface_type.GetMethod("store", store_args_type); + success &= (bool)method.Invoke(null, store_args); + + // in case of no success: delete it again + if (!success) + { + delete_children(hierarchie, name, use_install_folder, JsonSeperateFields.Count() - max_i); + return false; + } + } + + return true; + } + + public static bool load_children(List<Directories> hierarchie, string name, ref T raw_payload, bool use_install_folder = false, bool deep_load = true, bool post_process = true) + { + var new_hierarchie = + Instance._IJGetHierarchie(hierarchie); + var new_name = + Instance._IJGetName(name); + + bool success = true; + for (int max_i = 0; max_i < JsonSeperateFields.Count(); max_i++) + { + var field = JsonSeperateFields[max_i]; + + Type interface_type = typeof(IJSONsavable<>).MakeGenericType(field.FieldType); + Type[] load_args_type = new Type[] { typeof(List<Directories>), typeof(string), field.FieldType.MakeByRefType(), typeof(bool), typeof(bool), typeof(bool) }; + object[] load_args = new object[] { new_hierarchie, new_name, null, use_install_folder, deep_load, post_process }; + + var method = interface_type.GetMethod("load", BindingFlags.Public | BindingFlags.Static, null, load_args_type, null); + bool success_i = (bool)method.Invoke(null, load_args); + + field.SetValue(raw_payload, success_i ? load_args[2] : Activator.CreateInstance(field.FieldType)); + success &= success_i; + } + + return success; + } + + public static bool load(List<Directories> hierarchie, string name, out T payload, bool use_install_folder = false, bool deep_load = true, bool post_process = true) + { + payload = default(T); + bool success = true; + + var new_hierarchie = + Instance._IJGetHierarchie(hierarchie); + var new_name = + Instance._IJGetName(name); + + string path = CreatePathToFile(out bool loadable, new_name, "JSON", new_hierarchie, use_install_folder); + if (!loadable) + return false; + + if (!Instance._IJGetRawObject(out T raw_payload, path)) + return false; + raw_payload.name = new_name; + + // load fields decorated with JsonSeparateAttribute and implementing IJSONsavable<> separately + if (deep_load + && !load_children(hierarchie, name, ref raw_payload, false /*use_install_folder*/)) + success = false; + + payload = post_process + ? postprocess(raw_payload) + : raw_payload; + + return success; + } + + public static T postprocess(T payload) + { + if (payload == null) + return Instance._IJPostProcess(payload); + + for (int i = 0; i < JsonAutoPostProcessFields.Count(); i++) + { + var field = JsonAutoPostProcessFields[i]; + dynamic process_me = field.GetValue(payload); // is S:IJSONsavable<S> + + Type interface_type = typeof(IJSONsavable<>).MakeGenericType(field.FieldType); + Type[] process_args_type = new Type[] { field.FieldType }; + object[] process_args = new object[] { process_me }; + + var method = interface_type.GetMethod("postprocess", process_args_type); + var processed = method.Invoke(null, process_args); + + field.SetValue(payload, processed); + } + + return Instance._IJPostProcess(payload); + } + + public static T preprocess(T payload) + { + if (payload == null) + return Instance._IJPreProcess(payload); + + for (int i = 0; i < JsonAutoPreProcessFields.Count(); i++) + { + var field = JsonAutoPreProcessFields[i]; + dynamic process_me = field.GetValue(payload); // is S:IJSONsavable<S> + + Type interface_type = typeof(IJSONsavable<>).MakeGenericType(field.FieldType); + Type[] process_args_type = new Type[] { field.FieldType }; + object[] process_args = new object[] { process_me }; + + var method = interface_type.GetMethod("preprocess", process_args_type); + var processed = method.Invoke(null, process_args); + + field.SetValue(payload, processed); + } + + return Instance._IJPreProcess(payload); + } + + public static void delete_children(List<Directories> hierarchie, string name, bool use_install_folder = false, int skip_last_children = 0) + { + var new_hierarchie = + Instance._IJGetHierarchie(hierarchie); + var new_name = + Instance._IJGetName(name); + + for (int i = 0; i < JsonSeperateFields.Count() - skip_last_children; i++) + { + var field = JsonSeperateFields[i]; + + Type interface_type = typeof(IJSONsavable<>).MakeGenericType(field.FieldType); + Type[] delete_args_type = new Type[] { typeof(List<Directories>), typeof(string), typeof(bool) }; + object[] delete_args = new object[] { new_hierarchie, new_name, use_install_folder }; + + var method = interface_type.GetMethod("delete", delete_args_type); + method.Invoke(null, delete_args); + } + } + + public static bool delete(List<Directories> hierarchie, string name, bool use_install_folder = false) + { + var new_hierarchie = + Instance._IJGetHierarchie(hierarchie); + var new_name = + Instance._IJGetName(name); + + string path = CreatePathToFile(out bool _, new_name, "JSON", new_hierarchie, use_install_folder); + if (!delete(path)) + return false; + + delete_children(hierarchie, name, use_install_folder); + return true; + } + + // does not delete children! + private static bool delete(string path) + { + if (!File.Exists(path)) + return false; + + File.Delete(path); + return true; + } + + // public bool delete() => delete(hierarchie, name); + + #endregion MethodTemplates + +} + +public static class JSONsavable +{ + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] + public sealed class JsonSeparateAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] + public sealed class JsonAutoPostProcessAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] + public sealed class JsonAutoPreProcessAttribute : Attribute { } + + /// <summary> + /// Writes the given object instance to a Json file, recursively, including public members, excluding [JsonIgnore]. + /// <para>Object type must have a parameterless constructor.</para> + /// <para>Only public properties and variables will be written to the file. These can be any type though, even other non-abstract classes.</para> + /// </summary> + /// <param name="filePath">The file path to write the object instance to.</param> + /// <param name="objectToWrite">The object instance to write to the file.</param> + public static bool WriteToJsonFile(string filePath, object objectToWrite) + { + // This tells your serializer that multiple references are okay. + var settings = new JsonSerializerSettings + { + ReferenceLoopHandling = ReferenceLoopHandling.Ignore + }; + + TextWriter writer = null; + try + { + string payload = JsonConvert.SerializeObject(objectToWrite, settings); + writer = new StreamWriter(filePath); + writer.Write(payload); + return true; + } + catch (Exception e) + { + Debug.LogError(e); + return false; + } + finally + { + if (writer != null) + writer.Close(); + } + } + + /// <summary> + /// Reads an object instance from an Json file. + /// <para>Object type must have a parameterless constructor.</para> + /// </summary> + /// <typeparam name="T">The type of object to read from the file.</typeparam> + /// <param name="filePath">The file path to read the object instance from.</param> + /// <returns>Returns a new instance of the object read from the Json file.</returns> + public static bool ReadFromJsonFile<T>(out T payload, string filePath) where T : new() + { + payload = default(T); + TextReader reader = null; + + try + { + reader = new StreamReader(filePath); + var fileContents = reader.ReadToEnd(); + payload = JsonConvert.DeserializeObject<T>(fileContents); + return true; + } + catch (Exception e) + { + Debug.LogError(e); + return false; + } + finally + { + if (reader != null) + reader.Close(); + } + } + +} \ No newline at end of file diff --git a/Assets/Scripts/IJSONsavable.cs.meta b/Assets/Scripts/IJSONsavable.cs.meta new file mode 100644 index 00000000..35b3b75b --- /dev/null +++ b/Assets/Scripts/IJSONsavable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 515d546c824caf241ac2fe28cb89ebf9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/InteractionEngine/FactHandling/FactOrganizer.cs b/Assets/Scripts/InteractionEngine/FactHandling/FactOrganizer.cs index 9e72d4b5..afe14cb7 100644 --- a/Assets/Scripts/InteractionEngine/FactHandling/FactOrganizer.cs +++ b/Assets/Scripts/InteractionEngine/FactHandling/FactOrganizer.cs @@ -679,7 +679,7 @@ public void fastforward() } FactOrganizer IJSONsavable<FactOrganizer>._IJPostProcess(FactOrganizer raw_payload) - => ReInitializeFactOrganizer<FactOrganizer>(raw_payload, false, out _); + => raw_payload == null ? raw_payload : ReInitializeFactOrganizer<FactOrganizer>(raw_payload, false, out _); /// <summary> /// Call this after assigning a stored instance in an empty world, that was not drawn. diff --git a/Assets/Scripts/InteractionEngine/FactHandling/SolutionOrganizer.cs b/Assets/Scripts/InteractionEngine/FactHandling/SolutionOrganizer.cs index c2882c8d..7faca0f6 100644 --- a/Assets/Scripts/InteractionEngine/FactHandling/SolutionOrganizer.cs +++ b/Assets/Scripts/InteractionEngine/FactHandling/SolutionOrganizer.cs @@ -144,6 +144,9 @@ public List<Fact> getMasterFactsByIndex (int i) string IJSONsavable<SolutionOrganizer>._IJGetName(string name) => name + endingVal; SolutionOrganizer IJSONsavable<SolutionOrganizer>._IJPostProcess(SolutionOrganizer raw_payload) { + if (raw_payload == null) + return raw_payload; + SolutionOrganizer payload = ReInitializeFactOrganizer<SolutionOrganizer> (raw_payload, false, out Dictionary<string, string> old_to_new); diff --git a/Assets/Scripts/JSONManager.cs b/Assets/Scripts/JSONManager.cs index afb7adfd..a0df7e51 100644 --- a/Assets/Scripts/JSONManager.cs +++ b/Assets/Scripts/JSONManager.cs @@ -1,244 +1,21 @@ using System.Collections.Generic; -using System.Reflection; -using System.IO; -using System; -using System.Linq; using Newtonsoft.Json; using JsonSubTypes; -using System.Collections; -using UnityEngine; -using Newtonsoft.Json.Linq; -using static CommunicationEvents; -// I would go for static virtual methods, but C#9 does not allow me... -// static methods cannot be overwritten -> virtual -public interface IJSONsavable<T> where T : IJSONsavable<T>, new() -{ - // stand-in for non static methods - public static readonly IJSONsavable<T> Instance = new T(); - public static readonly FieldInfo[] JsonSeperateFields = - typeof(T) - .GetFields( - BindingFlags.Instance | - BindingFlags.Public | - BindingFlags.NonPublic | - BindingFlags.Static ) - .Where((field) - => field.GetCustomAttributes().Any((attribute) - => attribute.GetType() == typeof(JSONManager.JsonSeparateAttribute)) - && field.FieldType.GetInterfaces().Any((inter) - => inter.IsGenericType && inter.GetGenericTypeDefinition() == typeof(IJSONsavable<>))) - .ToArray(); - - - // TODO: this? - public string name { get; set; } - public string path { get; set; } - protected static List<Directories> - hierarchie = new List<Directories> { Directories.misc }; - - #region OverridableMethods - - public virtual string _IJGetName(string name) => name; - public virtual List<Directories> _IJGetHierarchie(List<Directories> hierarchie_base) - { - hierarchie_base ??= new List<Directories>(); - return hierarchie_base.Concat(hierarchie).ToList(); - } - public virtual bool _IJGetRawObject(out T payload, string path) => JSONManager.ReadFromJsonFile<T>(out payload, path); - public virtual T _IJPreProcess(T payload) => payload; - public virtual T _IJPostProcess(T payload) => payload; - - #endregion OverridableMethods - - - #region MethodTemplates - - public bool store(List<Directories> hierarchie, string name, bool use_install_folder = false, bool overwrite = true, bool deep_store = true) - => store(hierarchie, name, (T) this, use_install_folder, overwrite, deep_store); - - public static bool store(List<Directories> hierarchie, string name, T payload, bool use_install_folder = false, bool overwrite = true, bool deep_store = true) - { - var new_hierarchie = - Instance._IJGetHierarchie(hierarchie); - var new_name = - Instance._IJGetName(name); - - string path = CreatePathToFile(out bool exists, new_name, "JSON", new_hierarchie, use_install_folder); - if (exists && !overwrite) - return false; - - // store fields decorated with JsonSeparateAttribute and implementing IJSONsavable<> separately - if (deep_store - && !store_children(hierarchie, name, payload, use_install_folder: false, overwrite, deep_store: true)) - return false; - - // store this - string path_o = payload.path; - payload.path = path; - - var new_payload = - Instance._IJPreProcess(payload); - - payload.path = path_o; - - JSONManager.WriteToJsonFile(path, new_payload); - return true; - } - - public bool store_children(List<Directories> hierarchie, string name, bool use_install_folder = false, bool overwrite = true, bool deep_store = true) - => store_children(hierarchie, name, (T) this, use_install_folder, overwrite, deep_store); - - public static bool store_children(List<Directories> hierarchie, string name, T payload, bool use_install_folder = false, bool overwrite = true, bool deep_store = true) - { - var new_hierarchie = - Instance._IJGetHierarchie(hierarchie); - var new_name = - Instance._IJGetName(name); - - for ((int max_i, bool success) = (0, true); max_i < JsonSeperateFields.Count(); max_i++) - { - var field = JsonSeperateFields[max_i]; - dynamic save_me = field.GetValue(payload); // is S:IJSONsavable<S> - - Type interface_type = typeof(IJSONsavable<>).MakeGenericType(field.FieldType); - Type[] store_args_type = new Type[] { typeof(List<Directories>), typeof(string), field.FieldType, typeof(bool), typeof(bool), typeof(bool) }; - object[] store_args = new object[] { new_hierarchie, new_name, save_me, use_install_folder, overwrite, deep_store }; - - var method = interface_type.GetMethod("store", store_args_type); - success &= (bool)method.Invoke(null, store_args); - - // in case of no success: delete it again - if (!success) - { - delete_children(hierarchie, name, use_install_folder, JsonSeperateFields.Count() - max_i); - return false; - } - } - - return true; - } - - public static bool load_children(List<Directories> hierarchie, string name, ref T raw_payload, bool use_install_folder = false, bool deep_load = true, bool post_process = true) - { - var new_hierarchie = - Instance._IJGetHierarchie(hierarchie); - var new_name = - Instance._IJGetName(name); - - bool success = true; - for (int max_i = 0; max_i < JsonSeperateFields.Count(); max_i++) - { - var field = JsonSeperateFields[max_i]; - - Type interface_type = typeof(IJSONsavable<>).MakeGenericType(field.FieldType); - Type[] load_args_type = new Type[] { typeof(List<Directories>), typeof(string), field.FieldType.MakeByRefType(), typeof(bool), typeof(bool), typeof(bool) }; - object[] load_args = new object[] { new_hierarchie, new_name, null, use_install_folder, deep_load, post_process }; - - var method = interface_type.GetMethod("load", BindingFlags.Public | BindingFlags.Static, null, load_args_type, null); - bool success_i = (bool)method.Invoke(null, load_args); - - field.SetValue(raw_payload, success_i ? load_args[2] : Activator.CreateInstance(field.FieldType)); - success &= success_i; - } - - return success; - } - - public static bool load(List<Directories> hierarchie, string name, out T payload, bool use_install_folder = false, bool deep_load = true, bool post_process = true) - { - payload = default(T); - bool success = true; - - var new_hierarchie = - Instance._IJGetHierarchie(hierarchie); - var new_name = - Instance._IJGetName(name); - - string path = CreatePathToFile(out bool loadable, new_name, "JSON", new_hierarchie, use_install_folder); - if (!loadable) - return false; - - if (!Instance._IJGetRawObject(out T raw_payload, path)) - return false; - raw_payload.name = new_name; - - // load fields decorated with JsonSeparateAttribute and implementing IJSONsavable<> separately - if (deep_load - && !load_children(hierarchie, name, ref raw_payload, false /*use_install_folder*/)) - success = false; - - payload = post_process - ? Instance._IJPostProcess(raw_payload) - : raw_payload; - - return success; - } - - public static void delete_children(List<Directories> hierarchie, string name, bool use_install_folder = false, int skip_last_children = 0) - { - var new_hierarchie = - Instance._IJGetHierarchie(hierarchie); - var new_name = - Instance._IJGetName(name); - - for(int i = 0; i < JsonSeperateFields.Count() - skip_last_children; i++) - { - var field = JsonSeperateFields[i]; - - Type interface_type = typeof(IJSONsavable<>).MakeGenericType(field.FieldType); - Type[] delete_args_type = new Type[] { typeof(List<Directories>), typeof(string), typeof(bool) }; - object[] delete_args = new object[] { new_hierarchie, new_name, use_install_folder }; - - var method = interface_type.GetMethod("delete", delete_args_type); - method.Invoke(null, delete_args); - } - } - - public static bool delete(List<Directories> hierarchie, string name, bool use_install_folder = false) - { - var new_hierarchie = - Instance._IJGetHierarchie(hierarchie); - var new_name = - Instance._IJGetName(name); - - string path = CreatePathToFile(out bool _, new_name, "JSON", new_hierarchie, use_install_folder); - if (!delete(path)) - return false; - - delete_children(hierarchie, name, use_install_folder); - return true; - } - - // does not delete children! - private static bool delete(string path) - { - if (!File.Exists(path)) - return false; - - File.Delete(path); - return true; - } - - // public bool delete() => delete(hierarchie, name); - - #endregion MethodTemplates - -} public class MMTURICollection { - public string Point = "http://mathhub.info/MitM/core/geometry?3DGeometry?point"; - public string Tuple = "http://gl.mathhub.info/MMT/LFX/Sigma?Symbols?Tuple"; - public string LineType = "http://mathhub.info/MitM/core/geometry?Geometry/Common?line_type"; - public string LineOf = "http://mathhub.info/MitM/core/geometry?Geometry/Common?lineOf"; - public string OnLine = "http://mathhub.info/MitM/core/geometry?Geometry/Common?onLine"; - public string Ded = "http://mathhub.info/MitM/Foundation?Logic?ded"; - public string Eq = "http://mathhub.info/MitM/Foundation?Logic?eq"; - public string Metric = "http://mathhub.info/MitM/core/geometry?Geometry/Common?metric"; - public string Angle = "http://mathhub.info/MitM/core/geometry?Geometry/Common?angle_between"; - public string Sketch = "http://mathhub.info/MitM/Foundation?InformalProofs?proofsketch"; - public string RealLit = "http://mathhub.info/MitM/Foundation?RealLiterals?real_lit"; + public string Tuple = "http://gl.mathhub.info/MMT/LFX/Sigma?Symbols?Tuple"; + public string Point = "http://mathhub.info/MitM/core/geometry?3DGeometry?point"; + public string LineType = "http://mathhub.info/MitM/core/geometry?Geometry/Common?line_type"; + public string LineOf = "http://mathhub.info/MitM/core/geometry?Geometry/Common?lineOf"; + public string OnLine = "http://mathhub.info/MitM/core/geometry?Geometry/Common?onLine"; + public string Metric = "http://mathhub.info/MitM/core/geometry?Geometry/Common?metric"; + public string Angle = "http://mathhub.info/MitM/core/geometry?Geometry/Common?angle_between"; + public string Ded = "http://mathhub.info/MitM/Foundation?Logic?ded"; + public string Eq = "http://mathhub.info/MitM/Foundation?Logic?eq"; + public string Sketch = "http://mathhub.info/MitM/Foundation?InformalProofs?proofsketch"; + public string RealLit = "http://mathhub.info/MitM/Foundation?RealLiterals?real_lit"; } public static class JSONManager @@ -371,79 +148,4 @@ public MMTValueDeclaration(string label, MMTTerm lhs, MMTTerm valueTp, MMTTerm v this.value = value; } } - - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] - public sealed class JsonSeparateAttribute : Attribute - { - public JsonSeparateAttribute() { } - } - - // TODO? /// <para>If there are public properties/variables that you do not want written to the file, decorate them with the [JsonIgnore] attribute.</para> - - /// <summary> - /// Writes the given object instance to a Json file, recursively to set depth, including all members. - /// <para>Object type must have a parameterless constructor.</para> - /// <para>Only All properties and variables will be written to the file. These can be any type though, even other non-abstract classes.</para> - /// </summary> - /// <param name="filePath">The file path to write the object instance to.</param> - /// <param name="objectToWrite">The object instance to write to the file.</param> - /// <param name="max_depth">The depth recursion will occur. Default = 0.</param> - public static bool WriteToJsonFile(string filePath, object objectToWrite, int max_depth = 0) - { - // This tells your serializer that multiple references are okay. - var settings = new JsonSerializerSettings - { - ReferenceLoopHandling = ReferenceLoopHandling.Ignore - }; - - TextWriter writer = null; - try - { - string payload = JsonConvert.SerializeObject(objectToWrite, settings); - writer = new StreamWriter(filePath); - writer.Write(payload); - return true; - } - catch (Exception e) - { - Debug.LogError(e); - return false; - } - finally - { - if (writer != null) - writer.Close(); - } - } - - /// <summary> - /// Reads an object instance from an Json file. - /// <para>Object type must have a parameterless constructor.</para> - /// </summary> - /// <typeparam name="T">The type of object to read from the file.</typeparam> - /// <param name="filePath">The file path to read the object instance from.</param> - /// <returns>Returns a new instance of the object read from the Json file.</returns> - public static bool ReadFromJsonFile<T>(out T payload, string filePath) where T : new() - { - payload = default(T); - TextReader reader = null; - - try - { - reader = new StreamReader(filePath); - var fileContents = reader.ReadToEnd(); - payload = JsonConvert.DeserializeObject<T>(fileContents); - return true; - } - catch (Exception e) - { - Debug.LogError(e); - return false; - } - finally - { - if (reader != null) - reader.Close(); - } - } } diff --git a/Assets/Scripts/Loading/Stage.cs b/Assets/Scripts/Loading/Stage.cs index 4da3ed34..e89eb914 100644 --- a/Assets/Scripts/Loading/Stage.cs +++ b/Assets/Scripts/Loading/Stage.cs @@ -32,7 +32,7 @@ public class Stage: IJSONsavable<Stage> /// <summary> Wether this <see cref="Stage"/> is located in installation folder or user data (a.k.a. !local).</summary> public bool use_install_folder = false; - [JsonIgnore, JSONManager.JsonSeparate] + [JsonIgnore, JSONsavable.JsonSeparate] public SaveGame savegame = null; /// <summary> @@ -57,6 +57,7 @@ public Dictionary<string, PlayerRecord> player_record_list { /// Defining when this <see cref="Stage.player_record"/> is considered as solved. /// <seealso cref="FactOrganizer.DynamiclySolved(SolutionOrganizer, out List{List{string}}, out List{List{string}})"/> /// </summary> + [JSONsavable.JsonAutoPreProcess, JSONsavable.JsonAutoPostProcess] public SolutionOrganizer solution = null; /// <summary> @@ -122,15 +123,6 @@ public Stage(string category, int number, string name, string description, strin } - Stage IJSONsavable<Stage>._IJPostProcess(Stage payload) - { - payload.solution = IJSONsavable<SolutionOrganizer>.Instance._IJPostProcess(payload.solution ?? new()); - // // savegame is saved separately - // payload.savegame = IJSONsavable<SaveGame>.Instance._IJPostProcess(payload.savegame ?? new()); - - return payload; - } - /// <summary> /// Sets members which are primitives. /// </summary> @@ -344,7 +336,7 @@ public static bool ShallowLoad(out Stage set, string path) if (!IJSONsavable<Stage>.Instance._IJGetRawObject(out set, path)) return false; - set = IJSONsavable<Stage>.Instance._IJPostProcess(set); + set = IJSONsavable<Stage>.postprocess(set); set.path = path; IJSONsavable<Stage>.load_children(null /*hierarchie*/, set.name, ref set, post_process: false); @@ -446,8 +438,10 @@ public class SaveGame : IJSONsavable<SaveGame> public string name { get; set; } public string path { get; set; } + [JSONsavable.JsonAutoPreProcess, JSONsavable.JsonAutoPostProcess] public PlayerRecord player_record = new(); - public Dictionary<string, PlayerRecord> player_record_list = new(); + + public Dictionary<string, PlayerRecord> player_record_list = new(); //entries are "PostProcess"ed when accessed/Cloned static SaveGame() { @@ -456,16 +450,6 @@ static SaveGame() public SaveGame() { } string IJSONsavable<SaveGame>._IJGetName(string name) => name + "_save"; - SaveGame IJSONsavable<SaveGame>._IJPostProcess(SaveGame payload) - { - payload.player_record = IJSONsavable<PlayerRecord>.Instance._IJPostProcess(payload.player_record ?? new()); - - // // player_record_list entries are "PostProcess"ed when accessed/Cloned - //payload.player_record_list = new (payload.player_record_list.Select((v) => new KeyValuePair<string, PlayerRecord> - // (v.Key, IJSONsavable<PlayerRecord>.Instance._IJPostProcess(v.Value)))); - - return payload; - } } @@ -482,6 +466,7 @@ public class PlayerRecord: IJSONsavable<PlayerRecord> public double seconds = 0; /// <summary> Stage progress. </summary> + [JSONsavable.JsonAutoPreProcess, JSONsavable.JsonAutoPostProcess] public FactOrganizer factState = new(); /// <summary> save game file name </summary> public string name { get; set; } = null; @@ -507,11 +492,6 @@ public PlayerRecord(string name) { factState = new FactOrganizer(); } - PlayerRecord IJSONsavable<PlayerRecord>._IJPostProcess(PlayerRecord payload) - { - payload.factState = IJSONsavable<FactOrganizer>.Instance._IJPostProcess(payload.factState); - return payload; - } /// <summary> /// Copies a specified <see cref="PlayerRecord"/> diff --git a/Assets/Scripts/StageStatic.cs b/Assets/Scripts/StageStatic.cs index 1af54911..17087d00 100644 --- a/Assets/Scripts/StageStatic.cs +++ b/Assets/Scripts/StageStatic.cs @@ -218,7 +218,7 @@ public static StageErrorStruct Validate(string category, int id, string name, st return new StageErrorStruct( category.Length == 0, ContainsNumber(category, id, true), - name.Length == 0 || ContainsKey(name, true) || ContainsKey(name, false), + name.Length == 0 || ContainsKey(name, local), false, !Worlds.Contains(scene), local == false, diff --git a/Assets/Scripts/UI/MainMenue/PageLoader/CreateLoader.cs b/Assets/Scripts/UI/MainMenue/PageLoader/CreateLoader.cs index 3341ff9f..2eb0a497 100644 --- a/Assets/Scripts/UI/MainMenue/PageLoader/CreateLoader.cs +++ b/Assets/Scripts/UI/MainMenue/PageLoader/CreateLoader.cs @@ -84,7 +84,7 @@ protected void Error(StageStatic.StageErrorStruct error) { throw new System.NotImplementedException("Id must be unique within a category"); // technichal a lie } - if (error.name) // for savegame identification (could be chained with local/category/id) + if (error.name) // for savegame identification (could be chained with local/category/id => beware StageStatic.GetStage(name, local)) { throw new System.NotImplementedException("Name must be unique"); } -- GitLab