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); } }
/// <summary> /// Performs hot-reload of the currently played script. /// </summary> public static async UniTask ReloadPlayedScriptAsync() { if (player?.Playlist is null || player.Playlist.Count == 0 || !player.PlayedScript) { Debug.LogError("Failed to perform hot reload: script player is not available or no script is currently played."); return; } var requireReload = string.IsNullOrEmpty(AssetDatabase.GetAssetPath(player.PlayedScript)); if (requireReload) // In case the played script is stored outside of Unity project, force reload it. { var prevLineHashes = playedLineHashes; // Otherwise they're overridden when playing the loaded script below. var scriptName = player.PlayedScript.Name; scriptManager.UnloadScript(scriptName); var script = await scriptManager.LoadScriptAsync(scriptName); player.Play(script, player.PlaybackSpot.LineIndex, player.PlaybackSpot.InlineIndex); playedLineHashes = prevLineHashes; } var lastPlayedLineIndex = (player.PlayedCommand ?? player.Playlist.Last()).PlaybackSpot.LineIndex; // Find the first modified line in the updated script (before the played line). var rollbackIndex = -1; for (int i = 0; i < lastPlayedLineIndex; i++) { if (!player.PlayedScript.Lines.IsIndexValid(i)) // The updated script ends before the currently played line. { rollbackIndex = player.Playlist.GetCommandBeforeLine(i - 1, 0)?.PlaybackSpot.LineIndex ?? 0; break; } if (playedLineHashes?.IsIndexValid(i) ?? false) { var oldLineHash = playedLineHashes[i]; var newLine = player.PlayedScript.Lines[i]; if (oldLineHash.EqualsFast(newLine.LineHash)) { continue; } } rollbackIndex = player.Playlist.GetCommandBeforeLine(i, 0)?.PlaybackSpot.LineIndex ?? 0; break; } if (rollbackIndex > -1) // Script has changed before the played line. // Rollback to the line before the first modified one. { await stateManager.RollbackAsync(s => s.PlaybackSpot.LineIndex == rollbackIndex); } // Update the playlist and play. var resumeLineIndex = rollbackIndex > -1 ? rollbackIndex : lastPlayedLineIndex; var playlist = new ScriptPlaylist(player.PlayedScript, scriptManager); var playlistIndex = player.Playlist.FindIndex(c => c.PlaybackSpot.LineIndex == resumeLineIndex); if (playlistIndex < 0) { playlistIndex = 0; } await playlist.PreloadResourcesAsync(playlistIndex, playlist.Count - 1); player.Play(playlist, playlistIndex); if (player.WaitingForInput) { player.SetWaitingForInputEnabled(false); } }