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 /// /// /// This service remain in existence for as long as the game runs and manages /// settings that apply to the whole game. Before accessing it for the first /// time, the specific classes used to store the game's settings need to be /// made known to the service by calling the method. /// /// /// It is recommended that you access the service through a game-specific /// wrapper which makes sure that the method is /// always called before accessing the settings, even when a scene is started /// inside the editor, skipping the startup, main menu and other scenes. /// /// /// Shared modules which do not have access to the game specific classes used /// to store settings can wait for the game to initialize the settings manager /// by subscribing to the event. If the settings /// manager has already been initialized at the time of subscription, /// the event handler will be called while subscribing. /// /// [ ServiceScope(Scope.Global) ] public class PersistentSettingsManager : Service, IPersistentSettingsStore { /// Filename for the machine settings file public static readonly string MachineSettingsFilename = "Settings.xml"; /// Filename for the user settings file public static readonly string UserSettingsFilename = "UserSettings.xml"; /// Filename for the achievements and global unlocks file public static readonly string AchievementsFilename = "Achievements.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); } } /// Initializes the types that will be used by the persistent data store /// /// Type that will be used to store user-specific settings such as key bindings, /// mouse speed or language. /// /// /// Type that will be used to store machine-specific settings such as resolution, /// monitor and detail levels. /// /// /// Type that will be used to store global unlocks and achievements /// /// /// 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< TMachineSettings, TUserSettings, TAchievements >( IPersistentStateSerializer machineSettingsSerializer = null, IPersistentStateSerializer userSettingsSerializer = null, IPersistentStateSerializer achievementSerializer = null ) where TMachineSettings : class, IMachineSettings, new() where TUserSettings : class, IUserSettings, new() where TAchievements : class, IAchievements, new() { if(IsInitialized) { Debug.LogError("Persistent data manager was initialized a second time."); return; } { var machineSettingsContainer = new XmlBackedContainer( StandardDirectories.UserDataPath, MachineSettingsFilename ); machineSettingsContainer.CustomSerializer = machineSettingsSerializer; this.machineSettings = machineSettingsContainer; } { var userSettingsContainer = new XmlBackedContainer( StandardDirectories.UserDataPath, UserSettingsFilename ); userSettingsContainer.CustomSerializer = userSettingsSerializer; this.userSettings = userSettingsContainer; } { var achievementContainer = new XmlBackedContainer( StandardDirectories.UserDataPath, AchievementsFilename ); achievementContainer.CustomSerializer = achievementSerializer; this.achievements = achievementContainer; } Debug.Log("Persistent settings manager initialized"); OnInitialized(); } /// Whether the persistent data store has been initialized already public bool IsInitialized { get { return ( (this.userSettings != null) && (this.machineSettings != null) && (this.achievements != null) ); } } /// Container for machine-specific settings such as display parameters public IPersistentContainer MachineSettings { get { if(!IsInitialized) { throw new InvalidOperationException( "Can't access machine settings; persistent settings manager not initialized uet" ); } return this.machineSettings; } } /// Container for user-specific settings such as key bindings public IPersistentContainer UserSettings { get { if(!IsInitialized) { throw new InvalidOperationException( "Can't access user settings; persistent settings manager not initialized uet" ); } return this.userSettings; } } /// Container for global unlocks and achievements public IVerifiedPersistentContainer Achievements { get { if(!IsInitialized) { throw new InvalidOperationException( "Can't access achievements; persistent settings manager not initialized uet" ); } return this.achievements; } } /// Loads all persistent settings from the current serialized state public void LoadAllSettings() { if(!IsInitialized) { throw new InvalidOperationException( "Can't load settings; persistent game state manager not initialized uet" ); } this.machineSettings.Load(); this.userSettings.Load(); } /// Saves all persistent settings into their respective files public void SaveAllSettings() { if(!IsInitialized) { throw new InvalidOperationException( "Can't save settings; persistent game state manager not initialized uet" ); } this.machineSettings.Save(); this.userSettings.Save(); } /// 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 machine-specific settings such as display parameters private IPersistentContainer machineSettings; /// Container for user-specific settings such as key bindings private IPersistentContainer userSettings; /// Container for global unlocks and achievements private IVerifiedPersistentContainer achievements; /// List of subscribers for the event private IList initializedEventSubscribers; } } // namespace Framework.Storage