Advertisement
Guest User

Untitled

a guest
Jun 21st, 2018
118
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 14.48 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using Assets.Scripts.Data;
  6. using UnityEngine;
  7.  
  8. public class DataManager : Singleton<DataManager>
  9. {
  10.     public enum EState
  11.     {
  12.         Idle,
  13.         Saving,
  14.         Loading
  15.     }
  16.  
  17.     public event Action SaveStartedEventHandler;
  18.     public event Action SaveEndedEventHandler;
  19.  
  20.     const string LevelSaveFileName = "level.json";
  21.     const string BackupFolder = "backups";
  22.     const int BackupCount = 3;
  23.  
  24.     [SerializeField] float _autoSavePeriod = 60f;
  25.     float _lastAutoSaveTime;
  26.     bool _saveScheduled;
  27.     EState _state = EState.Idle;
  28.     Queue<Action> _callbackQueue = new Queue<Action>();
  29.  
  30.     public bool CanSave
  31.     {
  32.         get
  33.         {
  34.             return (GameManager.Instance.State == GameManager.EState.Playing ||
  35.                     GameManager.Instance.State == GameManager.EState.Paused)
  36.                    // game manager is in the playing or paused state
  37.                    && GuiStateMachine.Instance.CurrentState == GuiStateMachine.State.Normal
  38.                    // gui is in the normal state
  39.                    && CameraController.Instance.State == CameraController.EState.Playing;
  40.             // camera controller is in the playing state
  41.         }
  42.     }
  43.  
  44.     public string BackupFolderPath
  45.     {
  46.         get { return Application.persistentDataPath + "/" + BackupFolder; }
  47.     }
  48.  
  49.     public string SaveFilePath
  50.     {
  51.         get { return Application.persistentDataPath + "/" + LevelSaveFileName; }
  52.     }
  53.  
  54.     void Start()
  55.     {
  56.         // register to events that cause autosave to be triggered.
  57.         // NOTE: This may incur a memory leak (should really do some investigation on this)
  58.         PlacableSystem.ItemPlacedEventHandler += _ => _saveScheduled = true;
  59.         InventorySystem.ItemPickedUpEventHandler += _ => _saveScheduled = true;
  60.         InventorySystem.ItemDroppedEventHandler += _ => _saveScheduled = true;
  61.         DoorSystem.Instance.DoorOperationOccurredEventHandler += ( _, __ ) => _saveScheduled = true;
  62.         UsableSystem.UsableUsedEventHandler += _ => _saveScheduled = true;
  63.  
  64.         LevelManager.LevelLoadedEventHandler += OnLevelLoaded;
  65.     }
  66.  
  67.     void OnLevelLoaded( string levelName )
  68.     {
  69.         AsgardEventSystem.EventInvocationEndedEventHandler += _ => _saveScheduled = true;
  70.     }
  71.  
  72.     void EnsureBackupFolderExists()
  73.     {
  74.         if ( !Directory.Exists( BackupFolderPath ) )
  75.         {
  76.             Directory.CreateDirectory( BackupFolderPath );
  77.         }
  78.     }
  79.  
  80.     void SaveLevelFile( LevelSaveData levelSaveData )
  81.     {
  82.         var json = levelSaveData.SerializeToJson();
  83.  
  84.         // If it already exists then back it up
  85.         BackupCurrentSave();
  86.  
  87.         // write the save data to the file.
  88.         File.WriteAllText( SaveFilePath, json );
  89.  
  90.         Debug.Log( string.Format( "Saved level state to {0}", SaveFilePath ) );
  91.  
  92.         _state = EState.Idle;
  93.  
  94.         Debug.Log( "Save ended" );
  95.  
  96.         if ( SaveEndedEventHandler != null )
  97.         {
  98.             SaveEndedEventHandler();
  99.         }
  100.     }
  101.  
  102.     LevelSaveData LoadLevelFile()
  103.     {
  104.         if ( !DoesLevelSaveFileExists() )
  105.         {
  106.             Debug.LogError( "Tried to load save file when the file does not exist" );
  107.             return null;
  108.         }
  109.  
  110.         var json = GetLevelSaveFileContent();
  111.         return LevelSaveData.DeserializeFromJson( json );
  112.     }
  113.  
  114.     void BackupCurrentSave()
  115.     {
  116.         Debug.Log( "Started backup..." );
  117.         // check to see if a save currently exists
  118.         if ( !DoesLevelSaveFileExists() )
  119.         {
  120.             Debug.Log( "No save file exists. No backup created." );
  121.             return;
  122.         }
  123.  
  124.         // ensure the backup folder exists
  125.         EnsureBackupFolderExists();
  126.  
  127.         // delete the oldest backups until the backup count is equal to the backup limit constant - 1
  128.         var backups = Directory.GetFiles( BackupFolderPath );
  129.         var orderedBackups = backups.OrderBy( File.GetCreationTime ).ToList();
  130.         while ( orderedBackups.Count >= BackupCount )
  131.         {
  132.             Debug.Log( string.Format( "Backup count is >= {0}. Deleting backup '{1}'", orderedBackups.Count,
  133.                 orderedBackups[ 0 ] ) );
  134.             File.Delete( orderedBackups[ 0 ] );
  135.             orderedBackups.RemoveAt( 0 );
  136.         }
  137.  
  138.         // copy the current save file to the backup folder
  139.         var fileName = BackupFolderPath + "/level_backup_" + DateTime.Now.Ticks;
  140.         File.WriteAllText( fileName , File.ReadAllText( SaveFilePath ) );
  141.         Debug.Log( "Created new backup with name " + fileName + ". Backup complete.");
  142.     }
  143.  
  144.     [ContextMenu( "Load Level State" )]
  145.     public void LoadSaveFile()
  146.     {
  147.         if ( _state != EState.Idle )
  148.         {
  149.             // do not perform loads during non-idle states
  150.             Debug.LogError( string.Format( "Tried to perform a load operation when in the {0} state", _state ) );
  151.             return;
  152.         }
  153.  
  154.         _state = EState.Loading;
  155.  
  156.         Debug.Log( "Load occurring" );
  157.  
  158.         LoadSaveFile( LoadLevelFile() );
  159.     }
  160.  
  161.     void LoadSaveFile( LevelSaveData levelSaveData )
  162.     {
  163.         // define what should happen once the level is loaded.
  164.         Action setupLevelStateCallback = () =>
  165.         {
  166.             // restore event states (this must be done first so the placable system doesn't invoke events)
  167.             foreach ( var savedEventState in levelSaveData.Events )
  168.             {
  169.                 var asgardEvent = AsgardEventSystem.Instance.GetAsgardEvent( savedEventState.EventName );
  170.                 if ( asgardEvent == null )
  171.                 {
  172.                     Debug.LogError(
  173.                         string.Format(
  174.                             "Tried to restore event state for event with name '{0}' but it was not returned by the event system query.",
  175.                             savedEventState.EventName ) );
  176.                     continue;
  177.                 }
  178.  
  179.                 // restore the "has been invoked" state.
  180.                 asgardEvent.HasBeenInvoked = savedEventState.HasBeenInvoked;
  181.             }
  182.  
  183.             // restore all door states
  184.             foreach ( var doorData in levelSaveData.Doors )
  185.             {
  186.                 if ( doorData.IsInOpenState )
  187.                 {
  188.                     DoorSystem.Instance.DoDoorOperationInstantly( doorData.UniqueId, DoorSystem.EDoorOperaton.Open );
  189.                 }
  190.                 else
  191.                 {
  192.                     DoorSystem.Instance.DoDoorOperationInstantly( doorData.UniqueId, DoorSystem.EDoorOperaton.Close );
  193.                 }
  194.             }
  195.  
  196.             //TODO: Add saving of usables back in if needed.
  197.             //// restore all the usable states
  198.             //foreach ( var usableData in levelSaveData.Usables )
  199.             //{
  200.             //    var usable = UsableSystem.Instance.FindUsable( usableData.UniqueId );
  201.             //    if ( usable == null )
  202.             //    {
  203.             //        Debug.LogError( string.Format( "Tried to restore saved data of usable with id {0} but it could not be found!", usableData.UniqueId ) );
  204.             //        continue;
  205.             //    }
  206.  
  207.             //    UsableSystem.Instance.SetupUsable( usable, usableData.IsUsed );
  208.             //}
  209.  
  210.             // delete all items in the scene
  211.             var worldItems = WorldItemSystem.Instance.Components;
  212.  
  213.             foreach ( var worldItem in worldItems )
  214.             {
  215.                 WorldItemSystem.Instance.DestroyWorldItem( worldItem );
  216.             }
  217.  
  218.             // place items from save file in the scene, ensuring to set placed state and z
  219.             foreach ( var savedItem in levelSaveData.Items )
  220.             {
  221.                 var isPlaced = !string.IsNullOrEmpty( savedItem.IsPlaced );
  222.  
  223.                 // if the item is placed, then call the placable system api otherwise just place the item through the world item system
  224.                 if ( isPlaced )
  225.                 {
  226.                     PlacableSystem.Instance.PlaceItem( savedItem.IsPlaced, savedItem.Name );
  227.                 }
  228.                 else
  229.                 {
  230.                     WorldItemSystem.Instance.InstantiateOnItemLayer( savedItem.Name, savedItem.Position );
  231.                 }
  232.             }
  233.  
  234.             // clear player inventory objects
  235.             InventorySystem.Instance.RemoveAllItemsFromInventory();
  236.  
  237.             // restore player inventory objects
  238.             foreach ( var inventorySlotData in levelSaveData.InventorySlots )
  239.             {
  240.                 InventorySystem.Instance.AddItemToInventory( inventorySlotData.ItemName, inventorySlotData.Index );
  241.             }
  242.  
  243.             // restore player character position
  244.             CharacterController.Instance.SetPosition( levelSaveData.Character.Position );
  245.             CharacterController.Instance.SetRotation( levelSaveData.Character.Rotation );
  246.  
  247.             // snap camera position to player character (this might be a bad idea as i'm taking over control...)
  248.             CameraController.Instance.SnapToTarget();
  249.  
  250.             Debug.Log( "Loaded level state from " + Application.dataPath + "/" + LevelSaveFileName );
  251.  
  252.             Debug.Log( "Load ended" );
  253.  
  254.             _state = EState.Idle;
  255.         };
  256.  
  257.         // load the relevant level
  258.         if ( LevelManager.Instance.CurrentSceneName != levelSaveData.LevelName )
  259.         {
  260.             // if the level is not loaded, we want to load it and execute setup of the level as the callback
  261.             GameManager.Instance.LoadLevel( levelSaveData.LevelName, setupLevelStateCallback );
  262.         }
  263.         else
  264.         {
  265.             // otherwise just call the callback right now
  266.             setupLevelStateCallback();
  267.         }
  268.     }
  269.  
  270.     [ContextMenu( "Save Level State" )]
  271.     void SaveLevelState()
  272.     {
  273.         if ( _state != EState.Idle )
  274.         {
  275.             // don't peform saves during non-idle states
  276.             return;
  277.         }
  278.  
  279.         Debug.Log( "Save occurring" );
  280.  
  281.         _state = EState.Saving;
  282.  
  283.         if ( SaveStartedEventHandler != null )
  284.         {
  285.             SaveStartedEventHandler();
  286.         }
  287.  
  288.         var levelSaveData = new LevelSaveData();
  289.  
  290.         Debug.Log( "saving " + WorldItemSystem.Instance.Components.Count );
  291.         // add all items in the world, not forgetting to determine their placed state
  292.         foreach ( var worldItem in WorldItemSystem.Instance.Components )
  293.         {
  294.             // skip items that have been marked for destruction.
  295.             if ( worldItem.IsMarkedForDestruction )
  296.             {
  297.                 continue;
  298.             }
  299.  
  300.             var placed = PlacableSystem.Instance.GetPlacableItemIsPlacedOn( worldItem );
  301.             var uniquePlacableId = placed != null ? placed.PlacableUniqueName : string.Empty;
  302.  
  303.             levelSaveData.Items.Add( new ItemData
  304.             {
  305.                 Name = worldItem.Model.Name,
  306.                 Position = worldItem.Transform.position,
  307.                 IsPlaced = uniquePlacableId
  308.             } );
  309.         }
  310.  
  311.         // add player character position
  312.         levelSaveData.Character = new CharacterData
  313.         {
  314.             Position = CharacterController.Instance.Position,
  315.             Rotation = CharacterController.Instance.Rotation
  316.         };
  317.  
  318.         // add player inventory
  319.         for ( var i = 0; i < InventorySystem.Instance.PlayerInventorySize; i++ )
  320.         {
  321.             var inventorySlot = InventorySystem.Instance.GetInventorySlotAtIndex( i );
  322.             if ( inventorySlot.Model.IsSet )
  323.             {
  324.                 levelSaveData.InventorySlots.Add( new InventorySlotData
  325.                 {
  326.                     Index = i,
  327.                     ItemName = inventorySlot.Model.Name
  328.                 } );
  329.             }
  330.         }
  331.  
  332.         // add event states
  333.         foreach ( var asgardEvent in AsgardEventSystem.Instance.AsgardEvents )
  334.         {
  335.             // get the name which should be unique (as we use this to restore the state back into the array on load)
  336.             var eventName = asgardEvent.EventName;
  337.             // get the invoked state
  338.             var hasBeenInvoked = asgardEvent.HasBeenInvoked;
  339.  
  340.             levelSaveData.Events.Add( new EventData
  341.             {
  342.                 EventName = eventName,
  343.                 HasBeenInvoked = hasBeenInvoked
  344.             } );
  345.         }
  346.  
  347.         // add level name
  348.         levelSaveData.LevelName = LevelManager.Instance.CurrentSceneName;
  349.  
  350.         // add door states
  351.         foreach ( var door in DoorSystem.Instance.Components )
  352.         {
  353.             var isInOpenState = door.IsInOpenState;
  354.             var uniqueId = door.UniqueId;
  355.  
  356.             levelSaveData.Doors.Add( new DoorData
  357.             {
  358.                 IsInOpenState = isInOpenState,
  359.                 UniqueId = uniqueId
  360.             } );
  361.         }
  362.  
  363.         // add the usable's states
  364.         foreach ( var usable in UsableSystem.Instance.Components )
  365.         {
  366.             var uniqueId = usable.UniqueId;
  367.             var isUsed = usable.IsUsed;
  368.  
  369.             levelSaveData.Usables.Add( new UsableData
  370.             {
  371.                 IsUsed = isUsed,
  372.                 UniqueId = uniqueId
  373.             } );
  374.         }
  375.  
  376.         // save file
  377.         SaveLevelFile( levelSaveData );
  378.  
  379.         // if any interested objects want a callback on save, we do it now after everything is complete. This is used for quitting the game.
  380.         ExecuteCallbacks();
  381.     }
  382.  
  383.     public bool DoesLevelSaveFileExists()
  384.     {
  385.         return File.Exists( SaveFilePath );
  386.     }
  387.  
  388.     string GetLevelSaveFileContent()
  389.     {
  390.         return File.ReadAllText( SaveFilePath );
  391.     }
  392.  
  393.     void LateUpdate()
  394.     {
  395.         // check to see if we should autosave.
  396.         if ( Time.time >= _lastAutoSaveTime + _autoSavePeriod || _saveScheduled )
  397.         {
  398.             // we need to be in the appropriate state
  399.             if ( CanSave )
  400.             {
  401.                 // save the game state and reset the timer.
  402.                 _lastAutoSaveTime = Time.time;
  403.                 _saveScheduled = false;
  404.                 SaveLevelState();
  405.                 Debug.Log( "Autosave occurred at " + DateTime.Now );
  406.             }
  407.         }
  408.     }
  409.  
  410.     public void ScheduleSave( Action callback = null )
  411.     {
  412.         if ( callback != null )
  413.         {
  414.             _callbackQueue.Enqueue( callback );
  415.         }
  416.  
  417.         _saveScheduled = true;
  418.     }
  419.  
  420.     void ExecuteCallbacks()
  421.     {
  422.         while ( _callbackQueue.Count > 0 )
  423.         {
  424.             var callback = _callbackQueue.Dequeue();
  425.             callback();
  426.         }
  427.     }
  428. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement