Skip to content
Snippets Groups Projects
Commit 1505a793 authored by Marco Zimmer's avatar Marco Zimmer
Browse files

Merge branch 'MaZiFAU_TopSort'

+ minor adjustments
parents 1903af66 bd6fc83e
Branches
No related tags found
No related merge requests found
Showing
with 456 additions and 627 deletions
......@@ -5,8 +5,12 @@
public class GenerateDemoFiles
{
static bool firstcall = true;
public static void GenerateAll()
{
if (!firstcall) return;
firstcall = false;
Debug.LogWarning("Generating and Overwriting Stage Files");
GenerateDemoA();
GenerateDemoB();
......@@ -21,13 +25,13 @@ public static void GenerateDemoA()
// TODO? use constructor
Stage demo = new Stage
{
number = 0,
number = 1,
category = "Demo Category",
name = "TechDemo A",
scene = "RiverWorld",
description = "Tree Stage",
use_install_folder = true,
hierarchie = new List<Directories> { /*Directories.Stages*/ }
//hierarchie = new List<Directories> { /*Directories.Stages*/ }
};
// needed to generate facts
......@@ -54,6 +58,7 @@ public static void GenerateDemoA()
{ new SolutionOrganizer.SubSolution(new HashSet<string> { target_Id }, null, null, new LineFactHightDirectionComparer()) };
// Save
StageStatic.SetMode(StageStatic.Mode.Create);
StageStatic.stage.store();
}
......@@ -66,13 +71,13 @@ public static void GenerateDemoB()
// TODO? use constructor
Stage demo = new Stage
{
number = 0,
number = 2,
category = "Demo Category",
name = "TechDemo B",
scene = "RiverWorld",
description = "River Stage",
use_install_folder = true,
hierarchie = new List<Directories> { /*Directories.Stages*/ }
//hierarchie = new List<Directories> { /*Directories.Stages*/ }
};
// needed to generate facts
......@@ -102,6 +107,7 @@ public static void GenerateDemoB()
};
// Save
StageStatic.SetMode(StageStatic.Mode.Create);
StageStatic.stage.store();
}
}
......@@ -36,7 +36,7 @@ public class GlobalBehaviour : MonoBehaviour
void Awake()
{
GenerateDemoFiles.GenerateAll();
// GenerateDemoFiles.GenerateAll();
hintAnimationStartColor = _hintAnimationStartColor;
hintAnimationEndColor = _hintAnimationEndColor;
......
......@@ -100,17 +100,15 @@ public enum OperationSystem
public static bool GadgetCanBeUsed = false;
// Configs
public static bool VerboseURI = false;
public enum Directories
{
misc,
Stages,
SaveGames,
ValidationSets,
FactStateMachines,
}
......@@ -138,41 +136,19 @@ public static string CreatePathToFile(out bool file_exists, string name, string
break;
}
//int Opsys =1 Android;
//int Opsys =0 Windows;
//is set above;
//OperationSystem Opsys is set above;
switch (Opsys)
{
default:
case OperationSystem.Windows:
path = use_install_folder ? Application.dataPath : Application.persistentDataPath;
if (hierarchie != null)
{
path = CreateHierarchiePath(hierarchie, path);
System.IO.Directory.CreateDirectory(path);
}
path = System.IO.Path.Combine(path, name + ending);
file_exists = System.IO.File.Exists(path);
return path;
break;
case OperationSystem.Android:
path = Application.persistentDataPath;
if (hierarchie != null)
{
path = CreateHierarchiePath(hierarchie, path);
System.IO.Directory.CreateDirectory(path);
break;
}
path = System.IO.Path.Combine(path, name + ending);
file_exists = System.IO.File.Exists(path);
return path;
default:
path = use_install_folder ? Application.dataPath : Application.persistentDataPath;
if (hierarchie != null)
{
path = CreateHierarchiePath(hierarchie, path);
......@@ -183,9 +159,5 @@ public static string CreatePathToFile(out bool file_exists, string name, string
file_exists = System.IO.File.Exists(path);
return path;
}
}
}
......@@ -99,9 +99,12 @@ public abstract class Fact
public GameObject Representation;
/// <value>
/// [ClassName] for JSON de-/serialization
/// [ClassName] for JSON de-/serialization.
/// Set in every non-abstract subclass of Fact.
/// Also add JsonSubtypes.KnownSubType decorator for deserialization to Fact!
/// </value>
public new string s_type;
[JsonProperty]
protected static /*new*/ string s_type = "ERROR: set s_type in T:Fact"; // In the subtype! NOT here!
/// <value>
/// Unique Id. e.g.: MMT URI
......@@ -122,9 +125,10 @@ public string Id {
/// </value>
public string Label {
get { // in case of renamed dependables
return hasCustomLabel && _CustomLabel != null ?
_CustomLabel :
generateLabel();
return _Facts.FactDict.Count == 0
|| (hasCustomLabel && _CustomLabel != null)
? _CustomLabel
: generateLabel();
}
set { rename(value); }
}
......@@ -482,7 +486,8 @@ protected override bool EquivalentWrapped(AbstractLineFact f1, AbstractLineFact
public class PointFact : FactWrappedCRTP<PointFact>
{
/// \copydoc Fact.s_type
public new string s_type = "PointFact";
[JsonProperty]
protected static new string s_type = "PointFact";
/// <summary> Position </summary>
public Vector3 Point;
......@@ -615,7 +620,8 @@ protected override bool EquivalentWrapped(PointFact f1, PointFact f2)
public class LineFact : AbstractLineFactWrappedCRTP<LineFact>
{
/// \copydoc Fact.s_type
public new string s_type = "LineFact";
[JsonProperty]
protected static new string s_type = "LineFact";
/// <summary> Distance between <see cref="AbstractLineFact.Pid1"/> and <see cref="AbstractLineFact.Pid2"/></summary>
public float Distance;
......@@ -740,7 +746,8 @@ private void SetDistance()
public class RayFact : AbstractLineFactWrappedCRTP<RayFact>
{
/// \copydoc Fact.s_type
public new string s_type = "RayFact";
[JsonProperty]
protected static new string s_type = "RayFact";
/// <summary> \copydoc Fact.Fact </summary>
public RayFact() : base() { }
......@@ -843,7 +850,8 @@ protected override bool EquivalentWrapped(RayFact f1, RayFact f2)
public class OnLineFact : FactWrappedCRTP<OnLineFact>
{
/// \copydoc Fact.s_type
public new string s_type = "OnLineFact";
[JsonProperty]
protected static new string s_type = "OnLineFact";
public string
/// <summary> <see cref="PointFact"/>.<see cref="Fact.Id">Id</see> </summary>
......@@ -1000,7 +1008,8 @@ protected override bool EquivalentWrapped(OnLineFact f1, OnLineFact f2)
public class AngleFact : FactWrappedCRTP<AngleFact>
{
/// \copydoc Fact.s_type
public new string s_type = "AngleFact";
[JsonProperty]
protected static new string s_type = "AngleFact";
/// @{ <summary>
/// One <see cref="Fact.Id">Id</see> of three <see cref="PointFact">PointFacts</see> defining Angle [<see cref="Pid1"/>, <see cref="Pid2"/>, <see cref="Pid3"/>].
......
......@@ -18,7 +18,7 @@
/// Organizes (insertion/ deletion / etc. operations) and sepperates <see cref="Fact">Fact</see> spaces.
/// Keeps track of insertion/ deletion actions for <see cref="undo"/> and <see cref="redo"/>.
/// </summary>
public class FactOrganizer
public class FactOrganizer : IJSONsavable<FactOrganizer>
{
/// <summary>
/// - <c>Key</c>: <see cref="Fact.Id"/>
......@@ -91,9 +91,8 @@ public class FactOrganizer
/// @{ <summary>
/// For <see cref="store(string, List<Directories>, bool, bool)"/> and <see cref="load(ref FactOrganizer, bool, string, List<Directories>, bool, out Dictionary<string, string>)"/>
/// </summary>
protected internal string path = null;
private static List<Directories>
hierState = new List<Directories> { Directories.FactStateMachines };
public string name { get; set; } = null;
public string path { get; set; } = null;
/// @}
......@@ -184,6 +183,12 @@ public meta(int workflow_id, bool active = true)
}
}
static FactOrganizer()
{
IJSONsavable<FactOrganizer>.hierarchie = new List<Directories> { Directories.FactStateMachines };
}
/// <summary>
/// Only used by <see cref="JsonConverter"/> to initiate empty instance.
/// </summary>
......@@ -211,7 +216,7 @@ public FactOrganizer(bool invoke = false)
/// <param name="source">instance to be parsed</param>
/// <param name="invoke">see <see cref="invoke"/></param>
/// <param name="old_to_new">generated to map <c>Key</c> <see cref="Fact.Id"/> of <paramref name="source"/> to corresponding <c>Value</c> <see cref="Fact.Id"/> of <paramref name="target"/></param>.
protected static T ReInitializeFactOrganizer<T>
public static T ReInitializeFactOrganizer<T>
(FactOrganizer source, bool invoke, out Dictionary<string, string> old_to_new)
where T : FactOrganizer, new()
{
......@@ -673,65 +678,8 @@ public void fastforward()
redo();
}
/// @{
/// TODO? move to interface?
/// TODO: document
public void store(string name, List<Directories> hierarchie = null, bool use_install_folder = false, bool overwrite = true)
{
hierarchie ??= new List<Directories>();
hierarchie.AddRange(hierState.AsEnumerable());
string path_o = path;
path = CreatePathToFile(out bool exists, name, "JSON", hierarchie, use_install_folder);
hierarchie.RemoveRange(hierarchie.Count - hierState.Count, hierState.Count);
if (!exists || overwrite)
JSONManager.WriteToJsonFile(path, this);
path = path_o;
}
public static bool load(ref FactOrganizer set, bool draw, string name, List<Directories> hierarchie, bool use_install_folder, out Dictionary<string, string> old_to_new)
{
old_to_new = null;
hierarchie ??= new List<Directories>();
hierarchie.AddRange(hierState.AsEnumerable());
string path = CreatePathToFile(out bool loadable, name, "JSON", hierarchie, use_install_folder);
hierarchie.RemoveRange(hierarchie.Count - hierState.Count, hierState.Count);
if (!loadable)
return false;
FactOrganizer de_json = JSONManager.ReadFromJsonFile<FactOrganizer>(path);
set = ReInitializeFactOrganizer<FactOrganizer>(de_json, draw, out old_to_new);
set.path = path;
return true;
}
public static void delete(string name, List<Directories> hierarchie, bool use_install_folder)
{
hierarchie ??= new List<Directories>();
hierarchie.AddRange(hierState.AsEnumerable());
string path = CreatePathToFile(out bool _, name, "JSON", hierarchie, use_install_folder);
hierarchie.RemoveRange(hierarchie.Count - hierState.Count, hierState.Count);
delete(path);
}
public static void delete(string path)
{
if (File.Exists(path))
File.Delete(path);
}
public void delete()
{
delete(path);
}
/// @}
FactOrganizer IJSONsavable<FactOrganizer>._IJPostProcess(FactOrganizer raw_payload)
=> ReInitializeFactOrganizer<FactOrganizer>(raw_payload, false, out _);
/// <summary>
/// Call this after assigning a stored instance in an empty world, that was not drawn.
......
......@@ -10,22 +10,14 @@
/// <summary>
/// Solution of a <see cref="Stage"/>
/// </summary>
public class SolutionOrganizer : FactOrganizer
public class SolutionOrganizer : FactOrganizer, IJSONsavable<SolutionOrganizer>
{
/// @{ <summary> adds to the end of the file name of a </summary>
private const string
/// <summary> SolutionFile (stores <see cref="SolutionOrganizer">this</see>) </summary>
endingSol = "_sol",
/// <summary> ValidationFile (stores <see cref="ValidationSet"/>) </summary>
endingVal = "_val";
/// @}
/// <summary>
/// \copydoc FactOrganizer.hierState
/// Additional value for <see cref="ValidationSet"/>
/// </summary>
private static List<Directories>
hierVal = new List<Directories> { Directories.ValidationSets };
/// <summary>
/// A collection of constrains of which *all* have to be <see langword="true"/>
......@@ -125,6 +117,11 @@ public bool IsEmpty()
}
}
static SolutionOrganizer()
{
IJSONsavable<SolutionOrganizer>.hierarchie = new List<Directories> { Directories.ValidationSets };
}
/// \copydoc FactOrganizer.FactOrganizer()
public SolutionOrganizer(): base()
{
......@@ -144,58 +141,20 @@ public List<Fact> getMasterFactsByIndex (int i)
}
*/
/// @{
/// TODO? move to interface?
/// TODO: document
public new void store(string name, List<Directories> hierarchie = null, bool use_install_folder = false, bool overwrite = true)
{
hierarchie ??= new List<Directories>();
hierarchie.AddRange(hierVal.AsEnumerable());
string path_o = path;
path = CreatePathToFile(out bool exists, name + endingVal, "JSON", hierarchie, use_install_folder);
hierarchie.RemoveRange(hierarchie.Count - hierVal.Count, hierVal.Count);
if (exists && !overwrite)
{
path = path_o;
return;
}
JSONManager.WriteToJsonFile(path, this);
path = path_o;
}
public static bool load(ref SolutionOrganizer set, bool draw, string name, List<Directories> hierarchie = null, bool use_install_folder = false)
string IJSONsavable<SolutionOrganizer>._IJGetName(string name) => name + endingVal;
SolutionOrganizer IJSONsavable<SolutionOrganizer>._IJPostProcess(SolutionOrganizer raw_payload)
{
hierarchie ??= new List<Directories>();
hierarchie.AddRange(hierVal.AsEnumerable());
string path = CreatePathToFile(out bool loadable, name + endingVal, "JSON", hierarchie, use_install_folder);
hierarchie.RemoveRange(hierarchie.Count - hierVal.Count, hierVal.Count);
if (!loadable)
return false;
SolutionOrganizer payload =
ReInitializeFactOrganizer<SolutionOrganizer>
(raw_payload, false, out Dictionary<string, string> old_to_new);
SolutionOrganizer JsonTmp = JSONManager.ReadFromJsonFile <SolutionOrganizer> (path);
set = ReInitializeFactOrganizer<SolutionOrganizer>(JsonTmp, draw, out Dictionary<string, string> old_to_new);
set.path = path;
foreach (var element in JsonTmp.ValidationSet)
foreach (var element in raw_payload.ValidationSet)
// Parse and add
{
element.MasterIDs = new HashSet<string>(element.MasterIDs.Select(k => old_to_new[k]));
set.ValidationSet.Add(element);
payload.ValidationSet.Add(element);
}
return true;
return payload;
}
public new void delete()
{
base.delete();
if (System.IO.File.Exists(path))
System.IO.File.Delete(path);
}
/// @}
}
......@@ -8,6 +8,223 @@
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
{
......@@ -155,6 +372,11 @@ public MMTValueDeclaration(string label, MMTTerm lhs, MMTTerm valueTp, MMTTerm v
}
}
[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>
......@@ -166,104 +388,32 @@ public MMTValueDeclaration(string label, MMTTerm lhs, MMTTerm valueTp, MMTTerm v
/// <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 void WriteToJsonFile(string filePath, object objectToWrite, int max_depth = 0)
public static bool WriteToJsonFile(string filePath, object objectToWrite, int max_depth = 0)
{
int current_depth = 0;
// This tells your serializer that multiple references are okay.
var settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
BindingFlags bindFlags =
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Static;
TextWriter writer = null;
try
{
string payload = RecursiveStep(objectToWrite);
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();
}
// ======= local methods =======
// TODO? more stable depths (see next todo)
// TODO? respect IgnoreJson tags
string RecursiveStep<S>(S objectToWrite) where S : new()
{
string json;
if (current_depth >= max_depth
|| Type.GetTypeCode(objectToWrite.GetType()) != TypeCode.Object
|| objectToWrite == null)
json = JsonConvert.SerializeObject(objectToWrite, settings/*, new JsonInheritenceConverter<object>()*/);
else
{
current_depth++;
json = IntrusiveRecursiveJsonGenerator(objectToWrite);
current_depth--;
}
return json;
}
string IntrusiveRecursiveJsonGenerator<S>(S objectToWrite) where S : new()
// private convention? more like private suggestion!
{
bool is_enum = IsEnumerableType(objectToWrite.GetType());
string json = is_enum ? "[" : "{";
foreach (object field in is_enum ? (objectToWrite as IEnumerable) : objectToWrite.GetType().GetFields(bindFlags))
{
object not_so_private;
if (is_enum)
{
not_so_private = field;
}
else
{
not_so_private = ((FieldInfo)field).GetValue(objectToWrite);
json += ((FieldInfo)field).Name + ":";
}
json += RecursiveStep(not_so_private);
json += ",";
}
json = json.TrimEnd(',') + (is_enum ? "]" : "}");
return json;
// ======= local methods =======
bool IsEnumerableType(Type type)
{
if (type.IsInterface && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
return true;
foreach (Type intType in type.GetInterfaces())
{
if (intType.IsGenericType
&& intType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
return true;
}
}
return false;
}
}
}
/// <summary>
......@@ -273,14 +423,22 @@ bool IsEnumerableType(Type type)
/// <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 T ReadFromJsonFile<T>(string filePath) where T : new()
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();
return JsonConvert.DeserializeObject<T>(fileContents/*, new JsonInheritenceConverter<object>()*/);
payload = JsonConvert.DeserializeObject<T>(fileContents);
return true;
}
catch (Exception e)
{
Debug.LogError(e);
return false;
}
finally
{
......@@ -288,37 +446,4 @@ bool IsEnumerableType(Type type)
reader.Close();
}
}
// tutorial @https://www.codeproject.com/Articles/1201466/Working-with-JSON-in-Csharp-VB#data_structure_types
// unused
// TODO: check for actual type in ReadJson
// TODO: avoid self-referencing-loop-error in WriteJson
public sealed class JsonInheritenceConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
var element = jo.Properties().First();
return element.Value.ToObject(Type.GetType(element.Name));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value == null)
{
serializer.Serialize(writer, null);
return;
}
writer.WriteStartObject();
writer.WritePropertyName(value.GetType().FullName);
serializer.Serialize(writer, value);
writer.WriteEndObject();
}
}
}
......@@ -5,9 +5,13 @@
using UnityEngine;
using Newtonsoft.Json;
using static CommunicationEvents;
using System.Reflection;
public class Stage
//using static IJSONsavable<Stage>;
public class Stage: IJSONsavable<Stage>
{
/// <summary> Which category this <see cref="Stage"/> should be displayed in. </summary>
public string category = null;
/// <summary> Where to display this <see cref="Stage"/> within a <see cref="category"/> relative to others. </summary>
......@@ -16,7 +20,9 @@ public class Stage
/// The name this <see cref="Stage"/> will be displayed with.
/// Also defines names of save files of <see cref="player_record_list">stage progress</see>, <see cref="solution"/>
/// </summary>
public string name = null;
public string name { set; get; } = null;
public string path { set; get; } = null;
/// <summary> The description this <see cref="Stage"/> will be displayed with.</summary>
public string description = null;
......@@ -25,8 +31,9 @@ public class 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;
/// <summary> TODO? interface </summary>
public List<Directories> hierarchie = null;
[JsonIgnore, JSONManager.JsonSeparate]
public SaveGame savegame = null;
/// <summary>
/// <see langword="true"/> iff there is at least one element in <see cref="player_record_list"/> where <see cref="PlayerRecord.solved"/> == <see langword="true"/>.
......@@ -39,13 +46,17 @@ public class Stage
/// - Key: name of file
/// - Value: <see cref="PlayerRecord"/>
/// </summary>
public Dictionary<string, PlayerRecord> player_record_list = null;
[JsonIgnore]
public Dictionary<string, PlayerRecord> player_record_list {
get => savegame?.player_record_list;
set => (savegame ??= new())
.player_record_list = value;
}
/// <summary>
/// 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>
[JsonIgnore]
public SolutionOrganizer solution = null;
/// <summary>
......@@ -56,19 +67,17 @@ public class Stage
/// </summary>
[JsonIgnore]
public FactOrganizer factState {
get {
if (player_record == null)
return null;
return player_record.factState;
}
set {
if (player_record == null)
player_record = new PlayerRecord(record_name);
player_record.factState = value;
}
get => player_record?.factState;
set => (player_record ??= new(record_name))
.factState = value;
}
/// <summary> Current Stage progress.</summary>
public PlayerRecord player_record = null;
[JsonIgnore]
public PlayerRecord player_record {
get => savegame?.player_record;
set => (savegame ??= new())
.player_record = value;
}
/// <summary> Returns a name for <see cref="player_record.name"/> which needs to be uniquified once put into <see cref="player_record_list"/> (e.g. by <see cref="push_record(double, bool) adding '_i'"/>).</summary>
private string record_name { get { return name + "_save"; } }
......@@ -76,17 +85,14 @@ public FactOrganizer factState {
[JsonIgnore]
public bool creatorMode = false;
/// @{ <summary>
/// TODO? interafce
/// </summary>
private string path = null;
private static List<Directories>
hierStage = new List<Directories> { Directories.Stages };
/// @}
/// <summary> Tempory variable storing <see cref="factState"/> when <see cref="creatorMode"/> == <see langword="true"/>. </summary>
private FactOrganizer hiddenState;
static Stage()
{
IJSONsavable<Stage>.hierarchie = new List<Directories> { Directories.Stages };
}
/// <summary>
/// Initiates all parameterless members. <br/>
/// Used by <see cref="JsonConverter"/> to initate empty <c>class</c>.
......@@ -115,48 +121,14 @@ public Stage(string category, int number, string name, string description, strin
InitOOP();
}
/// <summary>
/// Copy Constructor. <br/>
/// <seealso cref="InitOOP"/>
/// <seealso cref="InitFields(string, int, string, string, string, bool)"/>
/// </summary>
/// <param name="get">to be copied</param>
/// <param name="category">sets <see cref="category"/></param>
/// <param name="number">sets <see cref="number"/></param>
/// <param name="name">sets <see cref="name"/></param>
/// <param name="description">sets <see cref="description"/></param>
/// <param name="scene">sets <see cref="scene"/></param>
/// <param name="local">sets !<see cref="use_install_folder"/></param>
public Stage(Stage get, string category, int number, string name, string description, string scene, bool local = true)
{
InitOOP();
Stage cpy = new Stage();
// "DeepCopy" of ref-types, 'cause screw c# and ICloneable
load(ref cpy, get.name, null, get.use_install_folder);
this.hierarchie = cpy.hierarchie;
this.solution = cpy.solution;
this.player_record = cpy.player_record;
InitFields(category, number, name, description, scene, local);
hierarchie ??= new List<Directories>();
hierarchie.AddRange(hierStage.AsEnumerable());
player_record.load(hierarchie);
player_record.name = player_record.name.Replace(get.record_name, record_name);
player_record.store(hierarchie, false);
//this.player_record_list = cpy.player_record_list;
foreach (var record in cpy.player_record_list.Values)
Stage IJSONsavable<Stage>._IJPostProcess(Stage payload)
{
record.load(hierarchie);
record.name = record.name.Replace(get.record_name, record_name);
record.store(hierarchie, false);
player_record_list.Add(record.name, record);
}
payload.solution = IJSONsavable<SolutionOrganizer>.Instance._IJPostProcess(payload.solution ?? new());
// // savegame is saved separately
// payload.savegame = IJSONsavable<SaveGame>.Instance._IJPostProcess(payload.savegame ?? new());
hierarchie.RemoveRange(hierarchie.Count - hierStage.Count, hierStage.Count);
store(false);
return payload;
}
/// <summary>
......@@ -184,8 +156,8 @@ public void InitFields(string category, int number, string name, string descript
private void InitOOP()
{
solution = new SolutionOrganizer();
savegame = new();
player_record = new PlayerRecord(record_name);
player_record_list = new Dictionary<string, PlayerRecord>();
}
/// <summary>
......@@ -227,7 +199,7 @@ public void ClearPlay()
/// </summary>
public void ClearALLRecords()
{
foreach (var record in player_record_list.Values.ToList())
foreach (var record in player_record_list.Values)
deletet_record(record, false);
player_record_list = new Dictionary<string, PlayerRecord>();
......@@ -242,18 +214,13 @@ public void ClearALLRecords()
/// <param name="b_store">iff <see langword="true"/> <see cref="store(bool)">stores</see> changes made to this <see cref="Stage"/></param>
public void deletet_record(PlayerRecord record, bool b_store = true)
{
hierarchie ??= new List<Directories>();
hierarchie.AddRange(hierStage.AsEnumerable());
if (record.factState != null)
record.factState.hardreset();
record.delete(hierarchie);
player_record_list.Remove(record.name);
hierarchie.RemoveRange(hierarchie.Count - hierStage.Count, hierStage.Count);
if(b_store)
if (b_store) {
player_record_list.Remove(record.name);
store();
}
}
/// <summary>
/// Clones <paramref name="record"/> to <see cref="player_record"/> iff found in <see cref="player_record_list"/> <br/>
......@@ -262,39 +229,24 @@ public void deletet_record(PlayerRecord record, bool b_store = true)
/// <param name="record">to be set or <see langword="null"/></param>
/// <returns><see langword="false"/> iff <paramref name="record"/> not found in <see cref="player_record_list"/> <br/>
/// or <see cref="PlayerRecord.load(List<Directories>)"/> fails.</returns>
public bool set_record(PlayerRecord record)
{
hierarchie ??= new List<Directories>();
hierarchie.AddRange(hierStage.AsEnumerable());
if (record != null)
{
if (!player_record_list.ContainsKey(record.name))
public bool set_record(string record_index)
{
hierarchie.RemoveRange(hierarchie.Count - hierStage.Count, hierStage.Count);
if (record_index == null)
return false;
}
else if (!record.load(hierarchie))
{
deletet_record(record);
hierarchie.RemoveRange(hierarchie.Count - hierStage.Count, hierStage.Count);
if (!player_record_list.ContainsKey(record_index))
return false;
}
}
player_record = record == null ? new PlayerRecord(record_name) : record.Clone(hierarchie);
player_record = player_record_list[record_index].Clone();
player_record.name = record_name;
hierarchie.RemoveRange(hierarchie.Count - hierStage.Count, hierStage.Count);
store(false);
return true;
}
/// <summary>
/// Adds current <see cref="player_record"/> to <see cref="player_record_list"/> incrementing <see cref="PlayerRecord.seconds"/> beforehand.
/// <remarks> IMPACTS SERVER PERFORMANCE: Increases number of <see cref="Fact"/>s at the Server for every <see cref="Fact"/> in current <see cref="player_record"/> </remarks>
/// </summary>
/// <param name="seconds_s">time in seconds to be added to <see cref="player_record.seconds"/> before pushing. <br/>
/// Iff set to <c>-1</c> <see cref="Time.timeSinceLevelLoadAsDouble"/> will be used.</param>
......@@ -305,25 +257,14 @@ public void push_record(double seconds_s = -1, bool force_push = false)
if(!force_push && StageStatic.mode == StageStatic.Mode.Create && creatorMode)
// store solution space
{
SetMode(false);
store(false);
//push_record(seconds_s, false);
SetMode(true);
return;
}
hierarchie ??= new List<Directories>();
hierarchie.AddRange(hierStage.AsEnumerable());
if (seconds_s == -1)
seconds_s = Time.timeSinceLevelLoadAsDouble;
player_record.seconds += seconds_s;
var push = player_record.Clone(hierarchie);
player_record.seconds += seconds_s != -1 ? seconds_s : Time.timeSinceLevelLoadAsDouble;
var push = player_record.Clone();
hierarchie.RemoveRange(hierarchie.Count - hierStage.Count, hierStage.Count);
int i = 0;
int i = 1;
push.name = record_name + "_" + i.ToString();
for (; player_record_list.ContainsKey(push.name); i++)
push.name = record_name + "_" + i.ToString();
......@@ -369,84 +310,26 @@ public void SetMode(bool create)
/// Clears and deletes all files associated with this <see cref="Stage"/>.
/// </summary>
/// <param name="player_record_list_too">iff set <see langword="false"/>, all files regarding <see cref="player_record_list"/> will be spared.</param>
public void delete(bool player_record_list_too)
public void delete()
{
hierarchie ??= new List<Directories>();
hierarchie.AddRange(hierStage.AsEnumerable());
ClearSolution();
ClearPlay();
solution.delete();
player_record.delete(hierarchie);
foreach (var record in player_record_list.Values.ToList())
record.factState.hardreset(false);
if (player_record_list_too)
ClearALLRecords();
if (File.Exists(path))
File.Delete(path);
hierarchie.RemoveRange(hierarchie.Count - hierStage.Count, hierStage.Count);
ClearAll();
IJSONsavable<Stage>.delete(null, name);
}
/// <summary>
/// Stores and overwrites this <see cref="Stage"/>, <see cref="player_record"/>, every element in <see cref="player_record_list"/> and <see cref="solution"/> (no overwrite for latter if empty).
/// </summary>
/// <param name="reset_player">wether to clear current <see cref="player_record"/></param>
public void store(bool reset_player = false)
public void store(bool reset_player = false, bool force_stage_file = false)
{
hierarchie ??= new List<Directories>();
hierarchie.AddRange(hierStage.AsEnumerable());
player_record.name = record_name;
if (reset_player)
player_record = new PlayerRecord(record_name);
//if (creatorMode || StageStatic.devel)
{
string path_o = path;
path = CreatePathToFile(out bool exists, name, "JSON", hierarchie, use_install_folder);
hierarchie.RemoveRange(hierarchie.Count - hierStage.Count, hierStage.Count);
JSONManager.WriteToJsonFile(path, this, 0);
path = path_o;
hierarchie.AddRange(hierStage.AsEnumerable());
if(solution != null)
solution.store(name, hierarchie, use_install_folder,
overwrite: solution.ValidationSet.Count > 0 && !solution.ValidationSet.Aggregate(true, (last, next) => last && next.IsEmpty()));
}
if (player_record != null)
player_record.store(hierarchie, true);
foreach (var track in player_record_list)
track.Value.store(hierarchie, false);
hierarchie.RemoveRange(hierarchie.Count - hierStage.Count, hierStage.Count);
}
/// <summary>
/// Loads a Stage complete using:
/// - <see cref="ShallowLoad(ref Stage, string, List<Directories>, bool)"/>
/// - <see cref="DeepLoad"/>
/// </summary>
/// \copydetails ShallowLoad(ref Stage, string, List<Directories>, bool)
public static bool load(ref Stage set, string name, List<Directories> hierarchie = null, bool use_install_folder = false)
{
Stage ret = new Stage();
bool loadable = ShallowLoad(ref ret, name, hierarchie, use_install_folder);
if (!loadable)
return false;
loadable = ret.DeepLoad();
if (!loadable)
return false;
set = ret;
return true;
if (force_stage_file || StageStatic.mode == StageStatic.Mode.Create)
(this as IJSONsavable<Stage>).store (null, name, use_install_folder, deep_store: false);
else
(this as IJSONsavable<Stage>).store_children(null, name, false , deep_store: true );
}
/// <summary>
......@@ -456,42 +339,14 @@ public static bool load(ref Stage set, string name, List<Directories> hierarchie
/// <param name="set">to be written in</param>
/// <param name="path">file location</param>
/// <returns><see langword="true"/> iff succeeded</returns>
public static bool ShallowLoad(ref Stage set, string path)
public static bool ShallowLoad(out Stage set, string path)
{
if (!System.IO.File.Exists(path))
if (!IJSONsavable<Stage>.Instance._IJGetRawObject(out set, path))
return false;
set = JSONManager.ReadFromJsonFile<Stage>(path);
set = IJSONsavable<Stage>.Instance._IJPostProcess(set);
set.path = path;
set.hierarchie ??= new List<Directories>();
set.hierarchie.AddRange(hierStage.AsEnumerable());
//if (!set.player_record.load(set.hierarchie))
// set.player_record = new PlayerRecord(set.record_name);
set.hierarchie.RemoveRange(set.hierarchie.Count - hierStage.Count, hierStage.Count);
return true;
}
/// <summary>
/// Determines path via <paramref name="hierarchie"/> and <paramref name="use_install_folder"/> and calls <see cref="ShallowLoad(ref Stage, string)"/>.
/// <seealso cref="ShallowLoad(ref Stage, string)"/>
/// </summary>
/// <param name="set">see <see cref="ShallowLoad(ref Stage, string)"/></param>
/// <param name="name">see <see cref="ShallowLoad(ref Stage, string)"/></param>
/// <param name="hierarchie">see <see cref="hierarchie"/> // TODO? Interface</param>
/// <param name="use_install_folder">see <see cref="use_install_folder"/></param>
/// <returns><see langword="true"/> iff succeeded</returns>
public static bool ShallowLoad(ref Stage set, string name, List<Directories> hierarchie = null, bool use_install_folder = false)
{
hierarchie ??= new List<Directories>();
hierarchie.AddRange(hierStage.AsEnumerable());
string path = CreatePathToFile(out bool loadable, name, "JSON", hierarchie, use_install_folder);
hierarchie.RemoveRange(hierarchie.Count - hierStage.Count, hierStage.Count);
if (!loadable || !ShallowLoad(ref set, path))
return false;
IJSONsavable<Stage>.load_children(null /*hierarchie*/, set.name, ref set, post_process: false);
return true;
}
......@@ -502,24 +357,13 @@ public static bool ShallowLoad(ref Stage set, string name, List<Directories> hie
/// <returns><see langword="false"/> iff <see cref="solution"/> could not be <see cref="SolutionOrganizer.load(ref SolutionOrganizer, bool, string, List<Directories>, bool)">loaded</see>.</returns>
public bool DeepLoad()
{
hierarchie ??= new List<Directories>();
hierarchie.AddRange(hierStage.AsEnumerable());
bool loadable;
solution ??= new SolutionOrganizer(false);
loadable = SolutionOrganizer.load(ref solution, false, name, hierarchie, use_install_folder);
if (!loadable)
return false;
player_record.load(hierarchie);
hierarchie.RemoveRange(hierarchie.Count - hierStage.Count, hierStage.Count);
Stage cpy = this;
IJSONsavable<Stage>.load_children(null /*hierarchie*/, name, ref cpy);
return true;
}
/// <summary>
/// Looks for saved <see cref="Stage">Stages</see> in parametised directories and calls on them <see cref="ShallowLoad(ref Stage, string)"/>.
/// Looks for saved <see cref="Stage">Stages</see> in parametised directories and calls on them <see cref="ShallowLoad(out Stage, string)"/>.
/// </summary>
/// <param name="hierarchie">see <see cref="hierarchie"/> //TODO? Interface</param>
/// <param name="use_install_folder">see <see cref="use_install_folder"/></param>
......@@ -528,25 +372,16 @@ public static Dictionary<string, Stage> Grup(List<Directories> hierarchie = null
{
Dictionary<string, Stage> ret = new Dictionary<string, Stage>();
hierarchie ??= new List<Directories>();
hierarchie.AddRange(hierStage.AsEnumerable());
var new_hierarchie = IJSONsavable<Stage>.Instance._IJGetHierarchie(hierarchie);
string path = CreatePathToFile(out _, "", "", new_hierarchie, use_install_folder);
string path = CreatePathToFile(out _, "", "", hierarchie, use_install_folder);
hierarchie.RemoveRange(hierarchie.Count - hierStage.Count, hierStage.Count);
string ending = ".JSON";
var info = new DirectoryInfo(path);
var fileInfo = info.GetFiles();
foreach(var file in fileInfo)
foreach(var file in new DirectoryInfo(path).GetFiles())
{
if (0 != string.Compare(ending, 0, file.Name, file.Name.Length - ending.Length, ending.Length))
if (file.Extension != ".JSON")
continue;
Stage tmp = new Stage();
if (ShallowLoad(ref tmp, file.FullName))
if (ShallowLoad(out Stage tmp, file.FullName))
ret.Add(tmp.name, tmp);
}
return ret;
......@@ -606,10 +441,38 @@ public bool CheckSolved()
}
public class SaveGame : IJSONsavable<SaveGame>
{
public string name { get; set; }
public string path { get; set; }
public PlayerRecord player_record = new();
public Dictionary<string, PlayerRecord> player_record_list = new();
static SaveGame()
{
IJSONsavable<SaveGame>.hierarchie = new() { Directories.SaveGames };
}
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;
}
}
/// <summary>
/// Represents a save slot.
/// </summary>
public class PlayerRecord
public class PlayerRecord: IJSONsavable<PlayerRecord>
{
/// <summary> Wether this save has solved the <see cref="Stage"/> which contains it. </summary>
public bool solved = false;
......@@ -619,13 +482,16 @@ public class PlayerRecord
public double seconds = 0;
/// <summary> Stage progress. </summary>
[JsonIgnore]
public FactOrganizer factState = null;
public FactOrganizer factState = new();
/// <summary> save game file name </summary>
public string name = null;
public string name { get; set; } = null;
private static List<Directories>
hierStage = new List<Directories> { /*Directories.FactStateMachines*/ };
public string path { get; set; }
static PlayerRecord(/*static!*/)
{
IJSONsavable<PlayerRecord>.hierarchie = new List<Directories> { /*Directories.FactStateMachines*/ };
}
/// <summary>
/// Empty constructor for <see cref="JsonConverter"/>
......@@ -641,41 +507,10 @@ public PlayerRecord(string name) {
factState = new FactOrganizer();
}
public void store(List<Directories> hierarchie, bool force_write)
{
hierarchie ??= new List<Directories>();
hierarchie.AddRange(hierStage.AsEnumerable());
if (factState != null)
factState.store(name, hierarchie, false, force_write);
hierarchie.RemoveRange(hierarchie.Count - hierStage.Count, hierStage.Count);
}
public bool load(List<Directories> hierarchie)
PlayerRecord IJSONsavable<PlayerRecord>._IJPostProcess(PlayerRecord payload)
{
hierarchie ??= new List<Directories>();
hierarchie.AddRange(hierStage.AsEnumerable());
factState = new FactOrganizer(false);
bool loadable = FactOrganizer.load(ref factState, false, name, hierarchie, false, out _);
hierarchie.RemoveRange(hierarchie.Count - hierStage.Count, hierStage.Count);
if (!loadable) {
return false;
}
return true;
}
public void delete(List<Directories> hierarchie)
{
hierarchie ??= new List<Directories>();
hierarchie.AddRange(hierStage.AsEnumerable());
FactOrganizer.delete(name, hierarchie, false);
hierarchie.RemoveRange(hierarchie.Count - hierStage.Count, hierStage.Count);
payload.factState = IJSONsavable<FactOrganizer>.Instance._IJPostProcess(payload.factState);
return payload;
}
/// <summary>
......@@ -683,21 +518,15 @@ public void delete(List<Directories> hierarchie)
/// </summary>
/// <param name="hierarchie">// TODO:</param>
/// <returns>a copied <see cref="PlayerRecord"/></returns>
public PlayerRecord Clone(List<Directories> hierarchie)
public PlayerRecord Clone()
{
this.store(hierarchie, true);
hierarchie ??= new List<Directories>();
hierarchie.AddRange(hierStage.AsEnumerable());
var ret = new PlayerRecord(this.name)
{
solved = this.solved,
seconds = this.seconds
};
ret.load(hierarchie);
ret.factState = FactOrganizer.ReInitializeFactOrganizer<FactOrganizer>(this.factState, false, out _);
hierarchie.RemoveRange(hierarchie.Count - hierStage.Count, hierStage.Count);
return ret;
}
}
\ No newline at end of file
......@@ -22,8 +22,8 @@ void Start()
private void OnDestroy()
{
StageStatic.SetMode(Mode.Play); // no Mode.Create
StageStatic.stage.solution.hardreset();
StageStatic.stage.factState.hardreset();
StageStatic.stage.solution.hardreset(invoke_event: false);
StageStatic.stage.factState.hardreset(invoke_event: false);
}
/// <summary>
......
......@@ -64,8 +64,6 @@ public static Stage stage {
(local_stage ? StageLocal : StageOfficial).Remove(current_name);
(local_stage ? StageLocal : StageOfficial).Add(current_name, value);
value.store();
}
}
......@@ -234,11 +232,8 @@ public static StageErrorStruct LoadNewStage(string category, int id, string name
if (!ret.pass)
return ret;
stage = new Stage(category, id, name, description, scene, true)
{
creatorMode = true
};
stage.store();
stage = new Stage(category, id, name, description, scene, true);
stage.store(force_stage_file: true);
LoadCreate();
return ret;
......@@ -294,7 +289,7 @@ public static bool ContainsNumber(string category, int i, bool local)
}
/// <summary>
/// Looks for and initial loads (see <see cref="Stage.ShallowLoad(ref Stage, string)"/>) <see cref="Stage">Stages</see> in <see cref="local_stage"/> and !<see cref="local_stage"/>.
/// Looks for and initial loads (see <see cref="Stage.ShallowLoad(out Stage, string)"/>) <see cref="Stage">Stages</see> in <see cref="local_stage"/> and !<see cref="local_stage"/>.
/// </summary>
public static void ShallowLoadStages()
{
......@@ -326,12 +321,12 @@ public static Stage GetStage(string name, bool local)
/// <summary>
/// Deletes a <see cref="Stage"/> and all its associated files (including save games).
/// <seealso cref="Stage.delete(bool)"/>
/// <seealso cref="Stage.delete()"/>
/// </summary>
/// <param name="stage">to be deleted</param>
public static void Delete(Stage stage)
{
GetStage(stage.name, !stage.use_install_folder).delete(true);
GetStage(stage.name, !stage.use_install_folder).delete();
(!stage.use_install_folder ? StageLocal : StageOfficial).Remove(stage.name);
}
......@@ -379,13 +374,13 @@ public static bool LoadInitStage(bool restore_session, GameObject gameObject = n
else
{
stage.ResetPlay();
if(mode == Mode.Create) // block saving "player" progress
stage.player_record.seconds = -1;
//if(mode == Mode.Create) // block saving "player" progress
// stage.player_record.seconds = -1;
}
if(gameObject != null)
gameObject.UpdateTagActive("DevelopingMode", mode == Mode.Create);
SetMode(stage.creatorMode ? Mode.Create : Mode.Play);
SetMode(mode);
return true;
}
......
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;
using static UIToolBox;
public class CollapsableStage : MonoBehaviour
......@@ -69,17 +69,23 @@ public void DrawChildren()
this.Init();
});
// set clone button
time_entry.GetNthChild(new List<int> { 4, 0 }).GetComponent<UnityEngine.UI.Button>().onClick.AddListener(delegate {
if (!stage.set_record(stage.player_record_list[index]))
{
// button Action to load a record
UnityAction loadRecord = delegate () {
// redraw this, when unable to find record
if (!stage.set_record(index)) {
this.Init();
return;
}
StageStatic.SetMode(StageStatic.Mode.Play);
// TODO: handle unable to load
Loader.LoadStage(stage.name, !stage.use_install_folder, true);
});
};
// set implicit load button (whole header)
time_entry.GetComponent<UnityEngine.UI.Button>().onClick.AddListener(loadRecord);
// set clone button
time_entry.GetNthChild(new List<int> { 4, 0 }).GetComponent<UnityEngine.UI.Button>().onClick.AddListener(loadRecord);
}
}
}
......@@ -63,11 +63,15 @@ private bool _Clone(bool overwrite)
return false;
}
Stage new_stage = new Stage(original_stage, category, id, name, description, scene, true);
IJSONsavable<Stage>.load(null /*original_stage.hierarchie*/, original_stage.name, out Stage new_stage, original_stage.use_install_folder, true);
new_stage.InitFields(category, id, name, description, scene, true);
if (!overwrite)
new_stage.ResetSaves();
StageStatic.stage = new_stage;
new_stage.store(force_stage_file: true );
new_stage.store(force_stage_file: false);
return true;
}
......
{"category":"Demo Category","number":0,"name":"TechDemo A","description":"Tree Stage","scene":"RiverWorld","use_install_folder":true,"hierarchie":[],"player_record_list":{},"player_record":{"solved":false,"date":-8585431120627090841,"seconds":0.0,"name":"TechDemo A_save"}}
\ No newline at end of file
{"category":"Demo Category","number":1,"description":"Tree Stage","scene":"RiverWorld","use_install_folder":true,"solution":{"ValidationSet":[{"MasterIDs":["http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2885"],"SolutionIndex":[],"RelationIndex":[],"ComparerString":"LineFactHightDirectionComparer"}],"FactDict":{"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2883":{"s_type":"PointFact","Point":{"x":0.0,"y":0.0,"z":0.0,"magnitude":0.0,"sqrMagnitude":0.0},"Normal":{"x":0.0,"y":1.0,"z":0.0,"magnitude":1.0,"sqrMagnitude":1.0},"Id":"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2883","Label":"A","hasCustomLabel":false,"LabelId":1},"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2884":{"s_type":"PointFact","Point":{"x":0.0,"y":6.0,"z":0.0,"normalized":{"x":0.0,"y":1.0,"z":0.0,"magnitude":1.0,"sqrMagnitude":1.0},"magnitude":6.0,"sqrMagnitude":36.0},"Normal":{"x":0.0,"y":1.0,"z":0.0,"magnitude":1.0,"sqrMagnitude":1.0},"Id":"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2884","Label":"B","hasCustomLabel":false,"LabelId":2},"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2885":{"s_type":"LineFact","Distance":6.0,"Pid1":"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2883","Pid2":"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2884","Dir":{"x":0.0,"y":1.0,"z":0.0,"magnitude":1.0,"sqrMagnitude":1.0},"Id":"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2885","Label":"[AB]","hasCustomLabel":false,"LabelId":0}},"MetaInf":{"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2883":{"workflow_id":0,"active":true},"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2884":{"workflow_id":1,"active":true},"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2885":{"workflow_id":2,"active":true}},"Workflow":[{"Id":"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2883","samestep":false,"steplink":3,"creation":true},{"Id":"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2884","samestep":true,"steplink":0,"creation":true},{"Id":"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2885","samestep":true,"steplink":0,"creation":true}],"marker":3,"worksteps":1,"backlog":0,"soft_resetted":false,"invoke":true,"MaxLabelId":2,"UnusedLabelIds":[],"name":null,"path":null},"name":"TechDemo A","path":null}
\ No newline at end of file
{"category":"Demo Category","number":0,"name":"TechDemo B","description":"River Stage","scene":"RiverWorld","use_install_folder":true,"hierarchie":[],"player_record_list":{},"player_record":{"solved":false,"date":-8585431120621442457,"seconds":0.0,"name":"TechDemo B_save"}}
\ No newline at end of file
{"category":"Demo Category","number":2,"description":"River Stage","scene":"RiverWorld","use_install_folder":true,"solution":{"ValidationSet":[{"MasterIDs":["http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2888"],"SolutionIndex":[],"RelationIndex":[],"ComparerString":"LineFactHightDirectionComparer"},{"MasterIDs":["http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2888"],"SolutionIndex":[],"RelationIndex":[],"ComparerString":"LineSpanningOverRiverWorldComparer"},{"MasterIDs":[],"SolutionIndex":[1],"RelationIndex":[0],"ComparerString":"LineFactHightComparer"}],"FactDict":{"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2886":{"s_type":"PointFact","Point":{"x":0.0,"y":0.0,"z":0.0,"magnitude":0.0,"sqrMagnitude":0.0},"Normal":{"x":0.0,"y":1.0,"z":0.0,"magnitude":1.0,"sqrMagnitude":1.0},"Id":"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2886","Label":"A","hasCustomLabel":false,"LabelId":1},"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2887":{"s_type":"PointFact","Point":{"x":0.0,"y":6.0,"z":0.0,"normalized":{"x":0.0,"y":1.0,"z":0.0,"magnitude":1.0,"sqrMagnitude":1.0},"magnitude":6.0,"sqrMagnitude":36.0},"Normal":{"x":0.0,"y":1.0,"z":0.0,"magnitude":1.0,"sqrMagnitude":1.0},"Id":"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2887","Label":"B","hasCustomLabel":false,"LabelId":2},"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2888":{"s_type":"LineFact","Distance":6.0,"Pid1":"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2886","Pid2":"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2887","Dir":{"x":0.0,"y":1.0,"z":0.0,"magnitude":1.0,"sqrMagnitude":1.0},"Id":"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2888","Label":"[AB]","hasCustomLabel":false,"LabelId":0}},"MetaInf":{"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2886":{"workflow_id":0,"active":true},"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2887":{"workflow_id":1,"active":true},"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2888":{"workflow_id":2,"active":true}},"Workflow":[{"Id":"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2886","samestep":false,"steplink":3,"creation":true},{"Id":"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2887","samestep":true,"steplink":0,"creation":true},{"Id":"http://mathhub.info/FrameIT/frameworld/integrationtests?SampleSituationSpace/Root?fact2888","samestep":true,"steplink":0,"creation":true}],"marker":3,"worksteps":1,"backlog":0,"soft_resetted":false,"invoke":true,"MaxLabelId":2,"UnusedLabelIds":[],"name":null,"path":null},"name":"TechDemo B","path":null}
\ No newline at end of file
fileFormatVersion: 2
guid: adcbcdc21be4b2a4a85d96580503e38b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
{"ValidationSet":[{"MasterIDs":["http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact354"],"SolutionIndex":[],"RelationIndex":[],"ComparerString":"LineFactHightDirectionComparer"}],"FactDict":{"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact352":{"s_type":"PointFact","Point":{"x":0.0,"y":0.0,"z":0.0,"magnitude":0.0,"sqrMagnitude":0.0},"Normal":{"x":0.0,"y":1.0,"z":0.0,"magnitude":1.0,"sqrMagnitude":1.0},"Id":"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact352","Label":"A","hasCustomLabel":false,"LabelId":1},"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact353":{"s_type":"PointFact","Point":{"x":0.0,"y":6.0,"z":0.0,"normalized":{"x":0.0,"y":1.0,"z":0.0,"magnitude":1.0,"sqrMagnitude":1.0},"magnitude":6.0,"sqrMagnitude":36.0},"Normal":{"x":0.0,"y":1.0,"z":0.0,"magnitude":1.0,"sqrMagnitude":1.0},"Id":"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact353","Label":"B","hasCustomLabel":false,"LabelId":2},"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact354":{"s_type":"LineFact","Distance":6.0,"Pid1":"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact352","Pid2":"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact353","Dir":{"x":0.0,"y":1.0,"z":0.0,"magnitude":1.0,"sqrMagnitude":1.0},"Id":"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact354","Label":"[AB]","hasCustomLabel":false,"LabelId":0}},"MetaInf":{"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact352":{"workflow_id":0,"active":true},"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact353":{"workflow_id":1,"active":true},"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact354":{"workflow_id":2,"active":true}},"Workflow":[{"Id":"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact352","samestep":false,"steplink":3,"creation":true},{"Id":"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact353","samestep":true,"steplink":0,"creation":true},{"Id":"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact354","samestep":true,"steplink":0,"creation":true}],"marker":3,"worksteps":1,"backlog":0,"soft_resetted":false,"invoke":false,"MaxLabelId":2,"UnusedLabelIds":[]}
\ No newline at end of file
fileFormatVersion: 2
guid: c1f88002b210dd4449cbaf8b9bb46003
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
{"ValidationSet":[{"MasterIDs":["http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact357"],"SolutionIndex":[],"RelationIndex":[],"ComparerString":"LineFactHightDirectionComparer"},{"MasterIDs":["http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact357"],"SolutionIndex":[],"RelationIndex":[],"ComparerString":"LineSpanningOverRiverWorldComparer"},{"MasterIDs":[],"SolutionIndex":[1],"RelationIndex":[0],"ComparerString":"LineFactHightComparer"}],"FactDict":{"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact355":{"s_type":"PointFact","Point":{"x":0.0,"y":0.0,"z":0.0,"magnitude":0.0,"sqrMagnitude":0.0},"Normal":{"x":0.0,"y":1.0,"z":0.0,"magnitude":1.0,"sqrMagnitude":1.0},"Id":"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact355","Label":"A","hasCustomLabel":false,"LabelId":1},"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact356":{"s_type":"PointFact","Point":{"x":0.0,"y":6.0,"z":0.0,"normalized":{"x":0.0,"y":1.0,"z":0.0,"magnitude":1.0,"sqrMagnitude":1.0},"magnitude":6.0,"sqrMagnitude":36.0},"Normal":{"x":0.0,"y":1.0,"z":0.0,"magnitude":1.0,"sqrMagnitude":1.0},"Id":"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact356","Label":"B","hasCustomLabel":false,"LabelId":2},"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact357":{"s_type":"LineFact","Distance":6.0,"Pid1":"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact355","Pid2":"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact356","Dir":{"x":0.0,"y":1.0,"z":0.0,"magnitude":1.0,"sqrMagnitude":1.0},"Id":"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact357","Label":"[AB]","hasCustomLabel":false,"LabelId":0}},"MetaInf":{"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact355":{"workflow_id":0,"active":true},"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact356":{"workflow_id":1,"active":true},"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact357":{"workflow_id":2,"active":true}},"Workflow":[{"Id":"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact355","samestep":false,"steplink":3,"creation":true},{"Id":"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact356","samestep":true,"steplink":0,"creation":true},{"Id":"http://mathhub.info/FrameIT/frameworld?DefaultSituationSpace/SituationTheory1?fact357","samestep":true,"steplink":0,"creation":true}],"marker":3,"worksteps":1,"backlog":0,"soft_resetted":false,"invoke":false,"MaxLabelId":2,"UnusedLabelIds":[]}
\ No newline at end of file
fileFormatVersion: 2
guid: 8fe9258a5917e3c4f8935875a478ed20
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
......@@ -13,7 +13,7 @@ void Awake()
//Instantiate(eventSystem);
GameObject obj = new GameObject("EventSystem");
obj.AddComponent<EventSystem>();
obj.AddComponent<StandaloneInputModule>().forceModuleActive = true;
// obsolete?: obj.AddComponent<StandaloneInputModule>().forceModuleActive = true;
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment