using System;
using System.Collections.Generic;
using UnityEngine;
using Framework.Services;
using Framework.Storage.Containers;
namespace Framework.Storage {
/// Manages all persistent data of the game
[
ServiceScope(Scope.Session)
]
public class PersistentGameStateManager : Service, IPersistentGameStateStore {
/// Filename for the saved game state file in each save slot folder
public static readonly string GameStateFilename = "GameState.xml";
/// Called when the persistent state store has been initialized
public event Action Initialized {
add {
if(IsInitialized) {
value();
} else {
if(this.initializedEventSubscribers == null) {
this.initializedEventSubscribers = new List();
this.initializedEventSubscribers.Add(value);
} else if(!this.initializedEventSubscribers.Contains(value)) {
this.initializedEventSubscribers.Add(value);
}
}
}
remove {
this.initializedEventSubscribers.Remove(value);
}
}
/// Initialized a new persistent game state manager
public PersistentGameStateManager() {
this.activeSaveSlotIndex = -1;
}
/// Injects the instance's dependencies
/// Save slot manager used to look up save slot paths
///
/// This method is called automatically by the services framework. Any parameters
/// it requires will be filled out by looking up the respective services and creating
/// them as needed.
///
protected void Inject(ISaveSlotManager saveSlotManager) {
this.saveSlotManager = saveSlotManager;
}
/// Initializes the types that will be used by the persistent data store
///
/// Type that holds the current state of the game as it is stored in a save slot
///
///
/// The default implementation of the persistent store requires this method to be called
/// before any of the other properties and methods of the persistent store will work.
/// It will load the game's current settings from their default locations if available
/// and create new default instances of no saved settings exist.
///
public void Initialize(
IPersistentStateSerializer gameStateSerializer = null
)
where TGameState : class, IGameState, new() {
if(IsInitialized) {
Debug.LogError("Persistent game state manager was initialized a second time.");
return;
}
{
var gameStateContainer = new XmlBackedContainer(
StandardDirectories.SaveGamePath, GameStateFilename
);
gameStateContainer.CustomSerializer = gameStateSerializer;
this.gameState = gameStateContainer;
this.changeStateDirectoryDelegate = new Action(
gameStateContainer.ChangeDirectory
);
}
Debug.Log("Persistent game state manager initialized");
OnInitialized();
}
/// Whether the persistent data store has been initialized already
public bool IsInitialized {
get {
return (this.gameState != null);
}
}
/// Container for the current state of the game
public IVerifiedPersistentContainer GameState {
get {
if(!IsInitialized) {
throw new InvalidOperationException(
"Can't access game state; persistent game state manager not initialized uet"
);
}
if(this.activeSaveSlotIndex == -1) {
throw new InvalidOperationException(
"Can't access game state; no active save slot yet"
);
}
return this.gameState;
}
}
/// Save slot that is currently being played, -1 if none
public int ActiveSaveSlot { get { return this.activeSaveSlotIndex; } }
/// Starts a new game state in the specified slot
/// Index of the slot a new game will be started in
public void StartNewGame(int slotIndex) {
if(!IsInitialized) {
throw new InvalidOperationException(
"Can't start new game; persistent game state manager not initialized uet"
);
}
if(this.saveSlotManager == null) {
throw new InvalidOperationException(
"Can't start new game; save slot manager has not been created yet"
);
}
this.activeSaveSlotIndex = slotIndex;
//this.saveSlotManager.DeleteSaveSlot(slotIndex);
this.changeStateDirectoryDelegate(this.saveSlotManager.GetSaveSlotPath(slotIndex));
this.gameState.Reset();
Debug.Log(
"New game started in slot " + slotIndex.ToString() +
" (home in '" + this.saveSlotManager.GetSaveSlotPath(slotIndex) + "')"
);
}
/// Loads the game state from the specified slot
/// Index of hte slot from which the game will be loaded
public void LoadGame(int slotIndex) {
if(!IsInitialized) {
throw new InvalidOperationException(
"Can't load game; persistent game state manager not initialized yet"
);
}
if(this.saveSlotManager == null) {
throw new InvalidOperationException(
"Can't load game; save slot manager has not been created yet"
);
}
this.activeSaveSlotIndex = slotIndex;
this.changeStateDirectoryDelegate(this.saveSlotManager.GetSaveSlotPath(slotIndex));
this.gameState.Load();
if(this.gameState.IsGenuine) {
Debug.Log("Game loaded from slot " + slotIndex.ToString() + " (game state is genuine)");
} else {
Debug.Log("Game loaded from slot " + slotIndex.ToString() + " (tampering was detected)");
}
}
/// Saves the game in the active slot
public void SaveGame() {
if(this.activeSaveSlotIndex == -1) {
throw new InvalidOperationException("Can't save game; no save slot active yet");
}
this.gameState.Save();
Debug.Log("Game saved to slot " + this.activeSaveSlotIndex.ToString());
}
/// Deletes the game state in the specified slot
/// Index of the slot in which the game will be deleted
///
/// True if a saved state existed in the specified slot and was deleted, false otherwise
///
public bool DeleteGame(int slotIndex) {
if(this.saveSlotManager == null) {
throw new InvalidOperationException(
"Can't delete game; save slot manager has not been created yet"
);
}
Debug.Log("Deleting saved game in slot " + slotIndex.ToString());
if(this.saveSlotManager.IsSlotFilled(slotIndex)) {
this.saveSlotManager.DeleteSaveSlot(slotIndex);
return true;
} else {
return false;
}
}
/// Whether a saved game exists in the specified slot
/// Index of the slot that will be checked
/// True if a saved game exists in the specified slot
public bool HasSavedGame(int slotIndex) {
return this.saveSlotManager.IsSlotFilled(slotIndex);
}
/// Fires the OnInitialized() event
/// >
/// We auto-clear the subscribers after firing since the event is supposed to
/// only happen once per game and we don't want to keep a bunch of objects hanging
/// by their subscription to the event list.
///
protected virtual void OnInitialized() {
if(this.initializedEventSubscribers != null) {
IList subscribers = this.initializedEventSubscribers;
this.initializedEventSubscribers = null;
int subscriberCount = subscribers.Count;
for(int index = 0; index < subscriberCount; ++index) {
subscribers[index]();
}
}
}
/// Container for the current state of the game
private IVerifiedPersistentContainer gameState;
/// List of subscribers for the event
private IList initializedEventSubscribers;
/// Index of the active save slot
private int activeSaveSlotIndex;
/// Delegate for the XmlBacked container's ChangeDirectory() method
private Action changeStateDirectoryDelegate;
/// Manages the save slots into which game states can be saved
private ISaveSlotManager saveSlotManager;
}
} // namespace Framework.Storage