Esempio n. 1
0
        private static async Task StartDebugHost(string executableOutputPath, ProjectWatcher projectWatcher, TaskCompletionSource <bool> projectCouldFirstCompile, TimeSpan recompilationDelay, Process debuggerProcess, LoggerResult logger)
        {
            // Clear logger, so we don't fail because of a previous debug session
            logger.Clear();
            logger.HasErrors = false;

            var assemblyRecompiler = new AssemblyRecompiler();

            AssemblyRecompiler.UpdateResult updateResult;

            // TODO: When should we do the NuGet restore? Should we do it only once, or every change?

            try
            {
                updateResult = await assemblyRecompiler.Recompile(projectWatcher.CurrentGameLibrary, logger);

                if (updateResult.HasErrors)
                {
                    // Failure during initial compilation
                    updateResult.Error("Initial LiveScripting compilation failed, can't start live scripting");
                    projectCouldFirstCompile.TrySetResult(false);
                    return;
                }
            }
            catch (Exception e)
            {
                projectCouldFirstCompile.TrySetException(e);
                throw;
            }

            // Notify project could compile succesfully
            projectCouldFirstCompile.TrySetResult(true);

            using (var debugHost = new DebugHost())
            {
                // Start the debug host and wait for it to be available
                debugHost.Start(executableOutputPath, debuggerProcess, logger);
                var debugTarget = await debugHost.GameHost.Target;

                bool firstLoad = true;

                // List of currently loaded assemblies
                var loadedAssemblies = new Dictionary <AssemblyRecompiler.SourceGroup, DebugAssembly>(AssemblyRecompiler.SourceGroupComparer.Default);

                // Listen for game exited event
                var gameExited = new CancellationTokenSource();
                debugHost.GameHost.GameExited += gameExited.Cancel;

                while (!gameExited.IsCancellationRequested)
                {
                    if (!updateResult.HasErrors)
                    {
                        // Assemblies to unload, based on currently loaded ones
                        var assembliesToUnload = updateResult.UnloadedProjects.Select(x => loadedAssemblies[x]).ToList();

                        // Load new assemblies
                        foreach (var loadedProject in updateResult.LoadedProjects)
                        {
                            var assembly = debugTarget.AssemblyLoadRaw(loadedProject.PE, loadedProject.PDB);
                            loadedAssemblies[loadedProject] = assembly;
                        }

                        // Assemblies to load, based on the updated SourceGroup mapping
                        var assembliesToLoad = updateResult.LoadedProjects.Select(x => loadedAssemblies[x]).ToList();

                        // Update runtime game to use new assemblies
                        debugTarget.AssemblyUpdate(assembliesToUnload, assembliesToLoad);

                        // Start game on first load
                        if (firstLoad)
                        {
                            // Arbitrarily launch first game (should be only one anyway?)
                            // TODO: Maybe game is not even necessary anymore and we should just instantiate a "DefaultSceneGame"?
                            var games = debugTarget.GameEnumerateTypeNames();
                            debugTarget.GameLaunch(games.First());

                            firstLoad = false;
                        }
                    }

                    // Wait for any file change that would trigger a recompilation (or a game exit event)
                    await projectWatcher.ReceiveAndDiscardChanges(recompilationDelay, gameExited.Token);

                    // Check if live session exited to avoid recompiling for nothing
                    if (gameExited.IsCancellationRequested)
                    {
                        break;
                    }

                    // Update result for next loop
                    updateResult = await assemblyRecompiler.Recompile(projectWatcher.CurrentGameLibrary, logger);
                }
            }
        }