Example #1
0
        public virtual async UniTask <GameStateMap> LoadGameAsync(string slotId)
        {
            if (string.IsNullOrEmpty(slotId) || !GameStateSlotManager.SaveSlotExists(slotId))
            {
                throw new Exception($"Slot '{slotId}' not found when loading '{typeof(GameStateMap)}' data.");
            }

            var quick = slotId.EqualsFast(LastQuickSaveSlotId);

            OnGameLoadStarted?.Invoke(new GameSaveLoadArgs(slotId, quick));

            if (Configuration.LoadStartDelay > 0)
            {
                await UniTask.Delay(TimeSpan.FromSeconds(Configuration.LoadStartDelay));
            }

            Engine.Reset();
            await Resources.UnloadUnusedAssets();

            var state = await GameStateSlotManager.LoadAsync(slotId);

            await LoadAllServicesFromStateAsync <IStatefulService <GameStateMap>, GameStateMap>(state);

            // All the serialized snapshots are expected to allow player rollback.
            RollbackStack?.OverrideFromJson(state.RollbackStackJson, s => s.AllowPlayerRollback());

            for (int i = onGameDeserializeTasks.Count - 1; i >= 0; i--)
            {
                await onGameDeserializeTasks[i](state);
            }

            OnGameLoadFinished?.Invoke(new GameSaveLoadArgs(slotId, quick));

            return(state);
        }
        public async UniTask <GameStateMap> SaveGameAsync(string slotId)
        {
            if (rollbackStack.Count == 0 || rollbackStack.Peek() is null)
            {
                PushRollbackSnapshot(false);
            }

            var quick = slotId.StartsWithFast(Configuration.QuickSaveSlotMask.GetBefore("{"));

            OnGameSaveStarted?.Invoke(new GameSaveLoadArgs(slotId, quick));

            var state = new GameStateMap(rollbackStack.Peek());

            state.SaveDateTime = DateTime.Now;
            state.Thumbnail    = Engine.GetService <ICameraManager>().CaptureThumbnail();

            var lastZero = rollbackStack.FirstOrDefault(s => s.PlaybackSpot.InlineIndex == 0); // Required when changing locale.

            bool filter(GameStateMap s) => (Configuration.EnableStateRollback && Configuration.SavedRollbackSteps > 0 && s.PlayerRollbackAllowed) || s == lastZero;

            state.RollbackStackJson = rollbackStack.ToJson(Configuration.SavedRollbackSteps, filter);

            await GameStateSlotManager.SaveAsync(slotId, state);

            // Also save global state on every game save.
            await SaveGlobalStateAsync();

            OnGameSaveFinished?.Invoke(new GameSaveLoadArgs(slotId, quick));

            return(state);
        }
Example #3
0
        public StateManager(StateConfiguration config, EngineConfiguration engineConfig, InputManager inputManager)
        {
            this.config       = config;
            this.inputManager = inputManager;

            var allowUserRollback = config.StateRollbackMode == StateRollbackMode.Full || (config.StateRollbackMode == StateRollbackMode.Debug && Debug.isDebugBuild);
            var rollbackCapacity  = allowUserRollback ? Mathf.Max(1, config.StateRollbackSteps) : 1; // One step is reserved for game save operations.

            rollbackStateStack = new StateRollbackStack(rollbackCapacity);

            var savesFolderPath = PathUtils.Combine(engineConfig.GeneratedDataPath, config.SaveFolderName);

            GameStateSlotManager   = new GameStateSlotManager(savesFolderPath, config.SaveSlotMask, config.QuickSaveSlotMask, config.SaveSlotLimit, config.QuickSaveSlotLimit, config.BinarySaveFiles);
            GlobalStateSlotManager = new GlobalStateSlotManager(savesFolderPath, config.DefaultGlobalSlotId, config.BinarySaveFiles);
            SettingsSlotManager    = new SettingsSlotManager(engineConfig.GeneratedDataPath, config.DefaultSettingsSlotId, false);
        }
Example #4
0
        public virtual async UniTask <GameStateMap> SaveGameAsync(string slotId)
        {
            var quick = slotId.StartsWithFast(Configuration.QuickSaveSlotMask.GetBefore("{"));

            OnGameSaveStarted?.Invoke(new GameSaveLoadArgs(slotId, quick));

            var state = new GameStateMap();
            await scriptPlayer.SynchronizeAndDoAsync(DoSaveAfterSync);

            OnGameSaveFinished?.Invoke(new GameSaveLoadArgs(slotId, quick));

            return(state);

            async UniTask DoSaveAfterSync()
            {
                state.SaveDateTime = DateTime.Now;
                state.Thumbnail    = cameraManager.CaptureThumbnail();

                SaveAllServicesToState <IStatefulService <GameStateMap>, GameStateMap>(state);

                for (int i = onGameSerializeTasks.Count - 1; i >= 0; i--)
                {
                    onGameSerializeTasks[i](state);
                }

                if (RollbackStack != null)
                {
                    // Closest spot with zero inline index is used when changing locale (UI/GameSettings/LanguageDropdown.cs).
                    var nearestStartLineSpot = RollbackStack.FirstOrDefault(s => s.PlaybackSpot.InlineIndex == 0);
                    bool SelectSerializedSnapshots(GameStateMap s) => s.PlayerRollbackAllowed || s == nearestStartLineSpot;

                    state.RollbackStackJson = RollbackStack.ToJson(Configuration.SavedRollbackSteps, SelectSerializedSnapshots);
                }

                await GameStateSlotManager.SaveAsync(slotId, state);

                // Also save global state on every game save.
                await SaveGlobalStateAsync();
            }
        }
        public async UniTask <GameStateMap> QuickSaveAsync()
        {
            // Free first quick save slot by shifting existing ones by one.
            for (int i = Configuration.QuickSaveSlotLimit; i > 0; i--)
            {
                var curSlotId  = Configuration.IndexToQuickSaveSlotId(i);
                var prevSlotId = Configuration.IndexToQuickSaveSlotId(i + 1);
                GameStateSlotManager.RenameSaveSlot(curSlotId, prevSlotId);
            }

            // Delete the last slot in case it's out of the limit.
            var outOfLimitSlotId = Configuration.IndexToQuickSaveSlotId(Configuration.QuickSaveSlotLimit + 1);

            if (GameStateSlotManager.SaveSlotExists(outOfLimitSlotId))
            {
                GameStateSlotManager.DeleteSaveSlot(outOfLimitSlotId);
            }

            var firstSlotId = string.Format(Configuration.QuickSaveSlotMask, 1);

            return(await SaveGameAsync(firstSlotId));
        }