Ejemplo n.º 1
0
        /// <summary>
        /// Starts playback of the provided script at the provided line and inline indexes.
        /// </summary>
        /// <param name="script">The script to play.</param>
        /// <param name="startLineIndex">Line index to start playback from.</param>
        /// <param name="startInlineIndex">Command inline index to start playback from.</param>
        public void Play(Script script, int startLineIndex = 0, int startInlineIndex = 0)
        {
            PlayedScript = script;

            if (Playlist is null || Playlist.ScriptName != script.Name)
            {
                Playlist = new ScriptPlaylist(script);
            }

            if (startLineIndex > 0 || startInlineIndex > 0)
            {
                var startAction = Playlist.GetFirstCommandAfterLine(startLineIndex, startInlineIndex);
                if (startAction is null)
                {
                    Debug.LogError($"Script player failed to start: no commands found in script `{PlayedScript.Name}` at line #{startLineIndex}.{startInlineIndex}."); return;
                }
                PlayedIndex = Playlist.IndexOf(startAction);
            }
            else
            {
                PlayedIndex = 0;
            }

            Play();
        }
        private async void HandleButtonClicked()
        {
            button.interactable = false;

            if (!string.IsNullOrEmpty(scriptName))
            {
                var script = await scriptManager.LoadScriptAsync(scriptName);

                if (script is null)
                {
                    Debug.LogError($"Failed to play `{scriptName}` for the on-click script of button `{name}`. Make sure the specified script exists.");
                    button.interactable = true;
                    return;
                }
                await scriptPlayer.PreloadAndPlayAsync(script);
            }
            else if (!string.IsNullOrWhiteSpace(scriptText))
            {
                var script   = new Script(name, scriptText);
                var playlist = new ScriptPlaylist(script);
                foreach (var command in playlist)
                {
                    await command.ExecuteAsync();
                }
            }

            button.interactable = true;
        }
Ejemplo n.º 3
0
        private async void PlayScriptAsync()
        {
            if (!string.IsNullOrEmpty(scriptName))
            {
                var player = Engine.GetService <IScriptPlayer>();
                if (player is null)
                {
                    throw new Exception($"Failed to play a script via `{nameof(PlayScript)}` component attached to `{gameObject.name}` game object: script player service is not available.");
                }
                await player.PreloadAndPlayAsync(scriptName);

                return;
            }

            if (!string.IsNullOrWhiteSpace(scriptText))
            {
                var text     = string.IsNullOrEmpty(argument) ? scriptText : scriptText.Replace("{arg}", argument);
                var script   = Script.FromScriptText($"`{name}` generated script", text);
                var playlist = new ScriptPlaylist(script);
                foreach (var command in playlist)
                {
                    if (command.ShouldExecute)
                    {
                        if (!command.Wait && !(command is Commands.Command.IForceWait))
                        {
                            command.ExecuteAsync().Forget();
                        }
                        else
                        {
                            await command.ExecuteAsync();
                        }
                    }
                }
            }
        }
Ejemplo n.º 4
0
        private static async void HandleScriptModifiedAsync(string assetPath)
        {
            if (!Engine.Initialized || !(Engine.Behaviour is RuntimeBehaviour) || !configuration.HotReloadScripts ||
                !ObjectUtils.IsValid(player.PlayedScript) || player.Playlist?.Count == 0)
            {
                return;
            }

            var scriptGuid = AssetDatabase.AssetPathToGUID(assetPath);
            var scriptName = editorResources.GetRecordByGuid(scriptGuid)?.Name;

            if (player.PlayedScript.Name != scriptName)
            {
                return;
            }

            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;
                }

                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);

            player.Play(playlist, playlistIndex);

            if (player.WaitingForInput)
            {
                player.SetWaitingForInputEnabled(false);
            }
        }
Ejemplo n.º 5
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);
                }
            }
Ejemplo n.º 6
0
        /// <summary>
        /// Preloads the script's commands and starts playing.
        /// </summary>
        public async Task PreloadAndPlayAsync(Script script, int startLineIndex = 0, int startInlineIndex = 0)
        {
            Playlist = new ScriptPlaylist(script);
            var startAction = Playlist.GetFirstCommandAfterLine(startLineIndex, startInlineIndex);
            var startIndex  = startAction != null?Playlist.IndexOf(startAction) : 0;

            var endIndex = providerManager.ResourcePolicy == ResourcePolicy.Static ? Playlist.Count - 1 :
                           Mathf.Min(startIndex + providerManager.DynamicPolicySteps, Playlist.Count - 1);
            await Playlist.HoldResourcesAsync(startIndex, endIndex);

            Play(script, startLineIndex, startInlineIndex);
        }
Ejemplo n.º 7
0
        private async UniTask <int> CountTotalCommandsAsync()
        {
            var result = 0;

            var scripts = await LoadAllScriptsAsync();

            foreach (var script in scripts)
            {
                var playlist = new ScriptPlaylist(script.Name, script.ExtractCommands());
                result += playlist.Count;
            }

            return(result);
        }
Ejemplo n.º 8
0
        public async Task <int> UpdateTotalActionCountAsync()
        {
            TotalCommandsCount = 0;

            var scripts = await scriptManager.LoadAllScriptsAsync();

            foreach (var script in scripts)
            {
                var playlist = new ScriptPlaylist(script);
                TotalCommandsCount += playlist.Count;
            }

            return(TotalCommandsCount);
        }
Ejemplo n.º 9
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;
            }
        }
Ejemplo n.º 10
0
        /// <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;
                }

                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);

            player.Play(playlist, playlistIndex);

            if (player.WaitingForInput)
            {
                player.SetWaitingForInputEnabled(false);
            }
        }