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);
        }
Beispiel #2
0
        public virtual async UniTask LoadServiceStateAsync(GameStateMap stateMap)
        {
            var state = stateMap.GetState <GameState>();

            if (state is null)
            {
                RemoveAllActors();
                return;
            }

            // Remove actors that doesn't exist in the serialized state.
            if (ManagedActors.Count > 0)
            {
                foreach (var actorId in ManagedActors.Keys.ToList())
                {
                    if (!state.ActorsMap.ContainsKey(actorId))
                    {
                        RemoveActor(actorId);
                    }
                }
            }

            foreach (var kv in state.ActorsMap)
            {
                var actor = await GetOrAddActorAsync(kv.Key);

                kv.Value.ApplyToActor(actor);
            }
        }
Beispiel #3
0
        /// <summary>
        /// Saves current game state to the specified save slot.
        /// </summary>
        public async Task <GameStateMap> SaveGameAsync(string slotId)
        {
            if (rollbackStateStack.Count == 0 || rollbackStateStack.Peek() is null)
            {
                Debug.LogError("Failed to save game state: rollback stack is empty.");
                return(null);
            }

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

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

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

            state.SaveDateTime = DateTime.Now;
            state.Thumbnail    = Engine.GetService <CameraManager>().CaptureThumbnail();
            if (config.StateRollbackMode == StateRollbackMode.Full && config.SavedRollbackSteps > 0)
            {
                state.RollbackStackJson = rollbackStateStack.ToJson(config.SavedRollbackSteps);
            }

            await GameStateSlotManager.SaveAsync(slotId, state);

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

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

            return(state);
        }
Beispiel #4
0
 protected virtual async UniTask PerformOnGameDeserializeTasksAsync(GameStateMap state)
 {
     for (int i = onGameDeserializeTasks.Count - 1; i >= 0; i--)
     {
         await onGameDeserializeTasks[i](state);
     }
 }
Beispiel #5
0
 protected virtual void PerformOnGameSerializeTasks(GameStateMap state)
 {
     for (int i = onGameSerializeTasks.Count - 1; i >= 0; i--)
     {
         onGameSerializeTasks[i](state);
     }
 }
Beispiel #6
0
        public override async Task LoadServiceStateAsync(GameStateMap stateMap)
        {
            await base.LoadServiceStateAsync(stateMap);

            var state = stateMap.GetState <GameState>();

            if (state is null)
            {
                if (charIdToAvatarPathMap.Count > 0)
                {
                    foreach (var charId in charIdToAvatarPathMap.Keys.ToList())
                    {
                        RemoveAvatarTextureFor(charId);
                    }
                }
                return;
            }

            // Remove non-existing avatar mappings.
            if (charIdToAvatarPathMap.Count > 0)
            {
                foreach (var charId in charIdToAvatarPathMap.Keys.ToList())
                {
                    if (!state.CharIdToAvatarPathMap.ContainsKey(charId))
                    {
                        RemoveAvatarTextureFor(charId);
                    }
                }
            }
            // Add new or changed avatar mappings.
            foreach (var kv in state.CharIdToAvatarPathMap)
            {
                SetAvatarTexturePathFor(kv.Key, kv.Value);
            }
        }
Beispiel #7
0
        public virtual async Task LoadServiceStateAsync(GameStateMap stateMap)
        {
            var state = stateMap.GetState <GameState>();

            if (state is null)
            {
                RemoveAllActors();
                return;
            }

            // Remove actors that doesn't exist in the serialized state.
            if (ManagedActors.Count > 0)
            {
                foreach (var actorId in ManagedActors.Keys.ToList())
                {
                    if (!state.ActorState.Exists(s => s.Id.EqualsFast(actorId)))
                    {
                        RemoveActor(actorId);
                    }
                }
            }

            foreach (var actorState in state.ActorState)
            {
                var actor = await GetOrAddActorAsync(actorState.Id);

                actorState.ApplyToActor(actor);
            }
        }
Beispiel #8
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);
                PerformOnGameSerializeTasks(state);
                state.RollbackStackJson = SerializeRollbackStack();

                await GameSlotManager.SaveAsync(slotId, state);

                // Also save global state on every game save.
                await SaveGlobalAsync();
            }
        }
Beispiel #9
0
        public override async UniTask LoadServiceStateAsync(GameStateMap stateMap)
        {
            await base.LoadServiceStateAsync(stateMap);

            var state = stateMap.GetState <GameState>() ?? new GameState();

            DefaultPrinterId = state.DefaultPrinterId ?? Configuration.DefaultPrinterId;
        }
        public void Push(GameStateMap item)
        {
            rollbackList.AddFirst(item);

            if (rollbackList.Count > Capacity)
            {
                rollbackList.RemoveLast();
            }
        }
Beispiel #11
0
        public override void SaveServiceState(GameStateMap stateMap)
        {
            base.SaveServiceState(stateMap);

            var gameState = new GameState {
                DefaultPrinterId = DefaultPrinterId ?? Configuration.DefaultPrinterId
            };

            stateMap.SetState(gameState);
        }
Beispiel #12
0
        public override void SaveServiceState(GameStateMap stateMap)
        {
            base.SaveServiceState(stateMap);

            var gameState = new GameState {
                CharIdToAvatarPathMap = new SerializableLiteralStringMap(charIdToAvatarPathMap)
            };

            stateMap.SetState(gameState);
        }
        public override async Task SaveServiceStateAsync(GameStateMap stateMap)
        {
            await base.SaveServiceStateAsync(stateMap);

            var gameState = new GameState()
            {
                DefaultPrinterId = DefaultPrinterId ?? Configuration.DefaultPrinterId
            };

            stateMap.SetState(gameState);
        }
Beispiel #14
0
        public override async Task SaveServiceStateAsync(GameStateMap stateMap)
        {
            await base.SaveServiceStateAsync(stateMap);

            var gameState = new GameState()
            {
                CharIdToAvatarPathMap = new SerializableLiteralStringMap(charIdToAvatarPathMap)
            };

            stateMap.SetState(gameState);
        }
Beispiel #15
0
        public virtual async UniTask LoadServiceStateAsync(GameStateMap stateMap)
        {
            var state = stateMap.GetState <GameState>();

            if (state is null)
            {
                ResetService();
                return;
            }

            // Force stop and cancel all running commands to prevent state mutation while loading other services.
            Stop(); CancelCommands();

            executedPlayedCommand = state.ExecutedPlayedCommand;

            if (state.Playing) // The playback is resumed (when necessary) after other services are loaded.
            {
                if (stateManager.RollbackInProgress)
                {
                    stateManager.OnRollbackFinished += PlayAfterRollback;
                }
                else
                {
                    stateManager.OnGameLoadFinished += PlayAfterLoad;
                }
            }

            if (state.GosubReturnSpots != null && state.GosubReturnSpots.Count > 0)
            {
                GosubReturnSpots = new Stack <PlaybackSpot>(state.GosubReturnSpots);
            }
            else
            {
                GosubReturnSpots.Clear();
            }

            if (!string.IsNullOrEmpty(stateMap.PlaybackSpot.ScriptName))
            {
                if (PlayedScript is null || !stateMap.PlaybackSpot.ScriptName.EqualsFast(PlayedScript.Name))
                {
                    PlayedScript = await scriptManager.LoadScriptAsync(stateMap.PlaybackSpot.ScriptName);

                    Playlist    = new ScriptPlaylist(PlayedScript, scriptManager);
                    PlayedIndex = Playlist.IndexOf(stateMap.PlaybackSpot);
                    Debug.Assert(PlayedIndex >= 0, $"Failed to load script player state: `{stateMap.PlaybackSpot}` doesn't exist in the current playlist.");
                    var endIndex = providerConfig.ResourcePolicy == ResourcePolicy.Static ? Playlist.Count - 1 :
                                   Mathf.Min(PlayedIndex + providerConfig.DynamicPolicySteps, Playlist.Count - 1);
                    await Playlist.PreloadResourcesAsync(PlayedIndex, endIndex);
                }
                else
                {
                    PlayedIndex = Playlist.IndexOf(stateMap.PlaybackSpot);
                }
            }
        public void SaveServiceState(GameStateMap stateMap)
        {
            var gameState = new GameState()
            {
                Playing          = Playing,
                WaitingForInput  = WaitingForInput,
                GosubReturnSpots = GosubReturnSpots.Count > 0 ? GosubReturnSpots.Reverse().ToList() : null // Stack is reversed on enum.
            };

            stateMap.PlaybackSpot = PlaybackSpot;
            stateMap.SetState(gameState);
        }
Beispiel #17
0
        public virtual void SaveServiceState(GameStateMap stateMap)
        {
            var state = new GameState();

            foreach (var kv in ManagedActors)
            {
                var actorState = new TState();
                actorState.OverwriteFromActor(kv.Value);
                state.ActorsMap.Add(kv.Key, actorState);
            }
            stateMap.SetState(state);
        }
Beispiel #18
0
        public virtual Task SaveServiceStateAsync(GameStateMap stateMap)
        {
            var state = new GameState();

            foreach (var kv in ManagedActors)
            {
                var actorState = new TState();
                actorState.OverwriteFromActor(kv.Value);
                state.ActorState.Add(actorState);
            }
            stateMap.SetState(state);
            return(Task.CompletedTask);
        }
Beispiel #19
0
        public Task SaveServiceStateAsync(GameStateMap stateMap)
        {
            var gameState = new GameState()
            {
                PlayedScriptName     = PlayedScript?.Name,
                PlayedIndex          = PlayedIndex,
                IsWaitingForInput    = IsWaitingForInput,
                LastGosubReturnSpots = LastGosubReturnSpots.Count > 0 ? LastGosubReturnSpots.Reverse().ToList() : null // Stack is reversed on enum.
            };

            stateMap.PlaybackSpot = PlaybackSpot;
            stateMap.SetState(gameState);
            return(Task.CompletedTask);
        }
Beispiel #20
0
        private async Task PushRollbackSnapshotAsync(Commands.Command executedCommand)
        {
            var state = new GameStateMap();

            state.SaveDateTime = DateTime.Now;

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

            foreach (var task in onGameSerializeTasks)
            {
                await task(state);
            }

            rollbackStateStack.Push(state);
        }
Beispiel #21
0
        public async Task LoadServiceStateAsync(GameStateMap stateMap)
        {
            var state = stateMap.GetState <GameState>();

            if (state is null)
            {
                ResetService();
                return;
            }

            Stop(true);

            PlayedIndex = state.PlayedIndex;
            SetWaitingForInputActive(state.IsWaitingForInput);
            if (state.LastGosubReturnSpots != null && state.LastGosubReturnSpots.Count > 0)
            {
                LastGosubReturnSpots = new Stack <PlaybackSpot>(state.LastGosubReturnSpots);
            }
            else
            {
                LastGosubReturnSpots.Clear();
            }

            if (!string.IsNullOrEmpty(state.PlayedScriptName))
            {
                if (PlayedScript is null || !state.PlayedScriptName.EqualsFast(PlayedScript.Name))
                {
                    PlayedScript = await scriptManager.LoadScriptAsync(state.PlayedScriptName);

                    Playlist = new ScriptPlaylist(PlayedScript);
                    var endIndex = providerManager.ResourcePolicy == ResourcePolicy.Static ? Playlist.Count - 1 :
                                   Mathf.Min(PlayedIndex + providerManager.DynamicPolicySteps, Playlist.Count - 1);
                    await Playlist.HoldResourcesAsync(PlayedIndex, endIndex);
                }

                // Start playback and force waiting for input to prevent looping same command when performing state rollback.
                if (stateManager.RollbackInProgress)
                {
                    SetWaitingForInputActive(true);
                    Play();
                }
            }
            else
            {
                Playlist.Clear();
                PlayedScript = null;
            }
        }
        public void PushRollbackSnapshot(bool allowPlayerRollback)
        {
            var state = new GameStateMap();

            state.SaveDateTime          = DateTime.Now;
            state.PlayerRollbackAllowed = allowPlayerRollback;

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

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

            rollbackStack.Push(state);
        }
Beispiel #23
0
        public virtual void PushRollbackSnapshot(bool allowPlayerRollback)
        {
            if (RollbackStack is null)
            {
                return;
            }

            var state = new GameStateMap();

            state.SaveDateTime          = DateTime.Now;
            state.PlayerRollbackAllowed = allowPlayerRollback;

            SaveAllServicesToState <IStatefulService <GameStateMap>, GameStateMap>(state);
            PerformOnGameSerializeTasks(state);
            RollbackStack.Push(state);
        }
Beispiel #24
0
        protected virtual async UniTask RollbackToStateAsync(GameStateMap state)
        {
            rollbackTaskQueue.Enqueue(state);
            OnRollbackStarted?.Invoke();

            while (rollbackTaskQueue.Peek() != state)
            {
                await AsyncUtils.WaitEndOfFrame;
            }

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

            await PerformOnGameDeserializeTasksAsync(state);

            rollbackTaskQueue.Dequeue();
            OnRollbackFinished?.Invoke();
        }
        private async UniTask RollbackToStateAsync(GameStateMap state)
        {
            rollbackTaskQueue.Enqueue(state);
            OnRollbackStarted?.Invoke();

            while (rollbackTaskQueue.Peek() != state)
            {
                await AsyncUtils.WaitEndOfFrame;
            }

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

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

            rollbackTaskQueue.Dequeue();
            OnRollbackFinished?.Invoke();
        }
Beispiel #26
0
        private async Task RollbackToStateAsync(GameStateMap state)
        {
            rollbackTaskQueue.Enqueue(state);
            OnRollbackStarted?.Invoke();

            while (rollbackTaskQueue.Peek() != state)
            {
                await waitForFrame;
            }

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

            foreach (var task in onGameDeserializeTasks)
            {
                await task(state);
            }

            rollbackTaskQueue.Dequeue();
            OnRollbackFinished?.Invoke();
        }
Beispiel #27
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();
            }
        }