using EscapeRoomEngine.Engine.Runtime.Modules; using EscapeRoomEngine.Engine.Runtime.UI; using Realms; using UnityEngine; using Logger = EscapeRoomEngine.Engine.Runtime.Utilities.Logger; using LogType = EscapeRoomEngine.Engine.Runtime.Utilities.LogType; namespace EscapeRoomEngine.Engine.Runtime.Measurements { /// /// The storage engine that manages the data in a realm database. /// public class PuzzleStorage : MonoBehaviour { /// /// The schema version must be updated whenever the schema in the database changed. /// private const int SchemaVersion = 2; /// /// The active instance of the storage engine. /// public static PuzzleStorage Instance { get; private set; } [Tooltip("The path of the database relative to where the engine stores persistent data.")] [SerializeField] private string databasePath = "measurements.realm"; private Realm _realm; private void OnEnable() { // configure special actions during specific migrations of the database var config = new RealmConfiguration { SchemaVersion = SchemaVersion, MigrationCallback = (migration, oldSchemaVersion) => { if (oldSchemaVersion < 1) { // migration from version 0 to 1 // nothing to do } if (oldSchemaVersion < 2) { // migration from version 1 to 2 // nothing to do } Logger.Log($"Migrated database to version {SchemaVersion}", LogType.Measuring); } }; _realm = Realm.GetInstance(config.ConfigWithPath(databasePath)); } private void Awake() { Instance = this; } private void Start() { Logger.Log($"Using realm database at {_realm.Config.DatabasePath}", LogType.Measuring); } private void OnDisable() { _realm?.Dispose(); } #region Session /// /// End a session and store it to the database. /// public void EndSession(Session session, float time) { session.Time = time; _realm.Write(() => _realm.Add(session)); } #endregion #region Puzzles /// /// Create a new puzzle for a specific description and store it in the database. /// /// This requires that the puzzle does not yet exist in the database. private Puzzle New(PuzzleModuleDescription puzzle) { Puzzle created = null; _realm.Write(() => created = _realm.Add(new Puzzle(puzzle))); return created; } /// /// Load a specific puzzle from the database or create it if it wasn't found. /// private Puzzle LoadOrNew(PuzzleModuleDescription puzzle) => _realm.Find(puzzle.Id) ?? New(puzzle); /// /// Load a specific puzzle from the database. /// /// The puzzle or null if it wasn't found in the database. public Puzzle Load(PuzzleModuleDescription puzzle) => _realm.Find(puzzle.Id); /// /// Delete all records concerning a specific puzzle from the database irreversibly. /// public void Delete(PuzzleModuleDescription puzzle) { var found = Load(puzzle); if (found != null) { _realm.Write(() => _realm.Remove(found)); } } /// /// End the measurement for a specific puzzle and store it in the database. Also stores the results of the current puzzle plan. /// public void EndMeasurement(Session session, PuzzleModuleDescription puzzle, PuzzleMeasurement measurement) { var found = LoadOrNew(puzzle); _realm.Write(() => { measurement.TimeSolved = Time.time; found.Measurements.Add(measurement); // add solved puzzle to session session.PuzzlesSolved.Add(found); // add plan result to session session.PlanResults.Add(new PlanResult( GameControl.Instance.TargetTime, found.Distribution.Cumulative(measurement.Time), GameControl.Instance.EstimatedTime)); }); } #endregion } }