Пример #1
0
        /// <summary>
        /// Starts a System.Process that compiles a master ink file, creating a playable JSON file that can be parsed by the Ink.Story class
        /// </summary>
        /// <param name="inkFile">Ink file.</param>
        private static void CompileInkInternal(InkFile inkFile, bool immediate)
        {
            // If we've not yet locked C# compilation do so now
            if (!hasLockedUnityCompilation)
            {
                hasLockedUnityCompilation = true;
                EditorApplication.LockReloadAssemblies();
            }

            RemoveFromPendingCompilationStack(inkFile);

            if (inkFile == null)
            {
                Debug.LogError("Tried to compile ink file but input was null.");
                return;
            }
            if (!inkFile.metaInfo.isMaster)
            {
                Debug.LogWarning("Compiling InkFile which is an include. Any file created is likely to be invalid. Did you mean to call CompileInk on inkFile.master?");
            }
            if (InkLibrary.GetCompilationStackItem(inkFile) != null)
            {
                UnityEngine.Debug.LogWarning("Tried compiling ink file, but file is already compiling. " + inkFile.filePath);
                return;
            }

            string inputPath = InkEditorUtils.CombinePaths(inkFile.absoluteFolderPath, Path.GetFileName(inkFile.filePath));

            Debug.Assert(inkFile.absoluteFilePath == inputPath);

            CompilationStackItem pendingFile = new CompilationStackItem
            {
                inkFile              = InkLibrary.GetInkFileWithAbsolutePath(inputPath),
                inkAbsoluteFilePath  = inputPath,
                jsonAbsoluteFilePath = inkFile.jsonPath,
                state = CompilationStackItem.State.Compiling
            };

            InkLibrary.Instance.compilationStack.Add(pendingFile);
            InkLibrary.Save();
            if (immediate)
            {
                CompileInkThreaded(pendingFile);
                Update();
            }
            else
            {
                if (EditorApplication.isCompiling)
                {
                    Debug.LogWarning("Was compiling scripts when ink compilation started! This seems to cause the thread to cancel and complete, but the work isn't done. It may cause a timeout.");
                }
                ThreadPool.QueueUserWorkItem(CompileInkThreaded, pendingFile);
            }
        }
Пример #2
0
        static void RemoveCompilingFile(int index)
        {
            InkLibrary.Instance.compilationStack.RemoveAt(index);
            InkLibrary.Save();
            // Progress bar prevents delayCall callback from firing in Linux Editor, locking the
            // compilation until it times out. Let's just not show progress bars in Linux Editor
#if !UNITY_EDITOR_LINUX
            if (InkLibrary.Instance.compilationStack.Count == 0)
            {
                EditorUtility.ClearProgressBar();
            }
#endif
        }
Пример #3
0
 public static void LoadOrCreateInstance()
 {
     InternalEditorUtility.LoadSerializedFileAndForget(absoluteSavePath);
     if (created)
     {
         if (InkEditorUtils.isFirstCompile)
         {
             Validate();
         }
     }
     else
     {
         instance           = ScriptableObject.CreateInstance <InkLibrary>();
         instance.hideFlags = HideFlags.HideAndDontSave;
         Rebuild();
         instance.Save(true);
     }
 }
        /// <summary>
        /// Starts a System.Process that compiles a master ink file, creating a playable JSON file that can be parsed by the Ink.Story class
        /// </summary>
        /// <param name="inkFile">Ink file.</param>
        private static void CompileInkInternal(InkFile inkFile, bool immediate)
        {
            if (inkFile == null)
            {
                Debug.LogError("Tried to compile ink file but input was null.");
                return;
            }
            if (!inkFile.metaInfo.isMaster)
            {
                Debug.LogWarning("Compiling InkFile which is an include. Any file created is likely to be invalid. Did you mean to call CompileInk on inkFile.master?");
            }

            // If we've not yet locked C# compilation do so now
            if (!hasLockedUnityCompilation)
            {
                hasLockedUnityCompilation = true;
                EditorApplication.LockReloadAssemblies();
            }

            RemoveFromPendingCompilationStack(inkFile);
            if (InkLibrary.GetCompilationStackItem(inkFile) != null)
            {
                UnityEngine.Debug.LogWarning("Tried compiling ink file, but file is already compiling. " + inkFile.filePath);
                return;
            }

            string inputPath = InkEditorUtils.CombinePaths(inkFile.absoluteFolderPath, Path.GetFileName(inkFile.filePath));

            Debug.Assert(inkFile.absoluteFilePath == inputPath);

            CompilationStackItem pendingFile = new CompilationStackItem
            {
                inkFile              = InkLibrary.GetInkFileWithAbsolutePath(inputPath),
                inkAbsoluteFilePath  = inputPath,
                jsonAbsoluteFilePath = inkFile.absoluteJSONPath,
                state     = CompilationStackItem.State.Queued,
                immediate = immediate
            };

            InkLibrary.Instance.compilationStack.Add(pendingFile);
            InkLibrary.Save();

            TryCompileNextFileInStack();
        }
        public static void CompileInk(params InkFile[] inkFiles)
        {
            _postAssetDataBaseRefreshActions.Clear();
            AssetDatabase.DisallowAutoRefresh();
            try
            {
                CompileInk(inkFiles, true, null);                //use immediate=true to avoid thread access issues.
            }
            finally
            {
                AssetDatabase.AllowAutoRefresh();
            }
            AssetDatabase.Refresh();

            foreach (var action in _postAssetDataBaseRefreshActions)
            {
                action();
            }
            _postAssetDataBaseRefreshActions.Clear();

            InkLibrary.Save();
            InkMetaLibrary.Save();
        }
Пример #6
0
        private static void Update()
        {
            if (!InkLibrary.created)
            {
                return;
            }

            for (int i = InkLibrary.Instance.compilationStack.Count - 1; i >= 0; i--)
            {
                var compilingFile = InkLibrary.Instance.compilationStack [i];
                if (compilingFile.state == CompilationStackItem.State.Compiling)
                {
                    compilingFile.timeTaken = (float)((DateTime.Now - compilingFile.startTime).TotalSeconds);
                    if (compilingFile.timeTaken > InkSettings.Instance.compileTimeout)
                    {
                        if (compilingFile.process != null)
                        {
                            compilingFile.process.Exited -= OnCompileProcessComplete;
                            compilingFile.process.Kill();
                        }
                        InkLibrary.Instance.compilationStack.RemoveAt(i);
                        InkLibrary.Save();
                        if (InkLibrary.Instance.compilationStack.Count == 0)
                        {
                            EditorUtility.ClearProgressBar();
                        }
                        Debug.LogError("Ink Compiler timed out for " + compilingFile.inkAbsoluteFilePath + ".\n. Compilation should never take more than a few seconds, but for large projects or slow computers you may want to increase the timeout time in the InkSettings file.\nIf this persists there may be another issue; or else check an ink file exists at this path and try Assets/Recompile Ink, else please report as a bug with the following error log at this address: https://github.com/inkle/ink/issues\nError log:\n" + compilingFile.errorOutput);
                    }
                }
                else if (compilingFile.state == CompilationStackItem.State.Importing)
                {
                    // This covers a rare bug that I've not pinned down
                    var timeTaken = (float)((DateTime.Now - compilingFile.startTime).TotalSeconds);
                    if (timeTaken > InkSettings.Instance.compileTimeout + 2)
                    {
                        if (compilingFile.process != null && !compilingFile.process.HasExited)
                        {
                            compilingFile.process.Exited -= OnCompileProcessComplete;
                            compilingFile.process.Kill();
                        }
                        InkLibrary.Instance.compilationStack.RemoveAt(i);
                        InkLibrary.Save();
                        if (InkLibrary.Instance.compilationStack.Count == 0)
                        {
                            EditorUtility.ClearProgressBar();
                        }
                        Debug.LogError("Ink Compiler timed out for " + compilingFile.inkAbsoluteFilePath + " while the file was importing.\n. Please report as a bug with the following error log at this address: https://github.com/inkle/ink/issues\nError log:\n" + compilingFile.errorOutput);
                    }
                }
            }
            if (InkLibrary.Instance.compilationStack.Count > 0)
            {
                int    numCompiling = InkLibrary.FilesInCompilingStackInState(CompilationStackItem.State.Compiling).Count;
                string message      = "Compiling .Ink File " + (InkLibrary.Instance.compilationStack.Count - numCompiling) + " of " + InkLibrary.Instance.compilationStack.Count;
                float  progress     = 0;
                foreach (var compilingFile in InkLibrary.Instance.compilationStack)
                {
                    if (compilingFile.state == CompilationStackItem.State.Compiling)
                    {
                        progress += compilingFile.timeTaken / InkSettings.Instance.compileTimeout;
                    }
                    if (compilingFile.state == CompilationStackItem.State.Importing)
                    {
                        progress += 1;
                    }
                }
                progress /= InkLibrary.Instance.compilationStack.Count;
                EditorUtility.DisplayProgressBar("Compiling Ink...", message, progress);
            }
        }
Пример #7
0
        private static void Delay()
        {
            if (InkLibrary.FilesInCompilingStackInState(CompilationStackItem.State.Compiling).Count > 0)
            {
                Debug.LogWarning("Delayed, but a file is now compiling! You can ignore this warning.");
                return;
            }
            float         longestTimeTaken = 0;
            bool          errorsFound      = false;
            StringBuilder filesCompiledLog = new StringBuilder("Files compiled:");

            foreach (var compilingFile in InkLibrary.Instance.compilationStack)
            {
                longestTimeTaken = Mathf.Max(compilingFile.timeTaken);
                filesCompiledLog.AppendLine().Append(compilingFile.inkFile.filePath);
                if (compilingFile.errorOutput.Count > 0)
                {
                    filesCompiledLog.Append(" (With unhandled error)");
                    StringBuilder errorLog = new StringBuilder();
                    errorLog.Append("Unhandled error(s) occurred compiling Ink file ");
                    errorLog.Append("'");
                    errorLog.Append(compilingFile.inkFile.filePath);
                    errorLog.Append("'");
                    errorLog.AppendLine("! Please report following error(s) as a bug:");
                    foreach (var error in compilingFile.errorOutput)
                    {
                        errorLog.AppendLine(error);
                    }
                    Debug.LogError(errorLog);
                    compilingFile.inkFile.metaInfo.compileErrors = compilingFile.errorOutput;
                    errorsFound = true;
                }
                else
                {
                    SetOutputLog(compilingFile);
                    bool errorsInEntireStory   = false;
                    bool warningsInEntireStory = false;
                    foreach (var inkFile in compilingFile.inkFile.metaInfo.inkFilesInIncludeHierarchy)
                    {
                        if (inkFile.metaInfo.hasErrors)
                        {
                            errorsInEntireStory = true;
                        }
                        if (inkFile.metaInfo.hasWarnings)
                        {
                            warningsInEntireStory = true;
                        }
                    }
                    if (errorsInEntireStory)
                    {
                        filesCompiledLog.Append(" (With error)");
                        errorsFound = true;
                    }
                    else
                    {
                        string localJSONAssetPath = InkEditorUtils.AbsoluteToUnityRelativePath(compilingFile.jsonAbsoluteFilePath);
                        AssetDatabase.ImportAsset(localJSONAssetPath);
                        compilingFile.inkFile.jsonAsset = AssetDatabase.LoadAssetAtPath <TextAsset> (localJSONAssetPath);
                    }
                    if (warningsInEntireStory)
                    {
                        filesCompiledLog.Append(" (With warning)");
                    }
                }
            }

            if (longestTimeTaken > InkSettings.Instance.compileTimeout * 0.6f)
            {
                Debug.LogWarning("Compilation took over 60% of the time required to timeout the compiler. Consider increasing the compile timeout on the InkSettings file.");
            }

            foreach (var compilingFile in InkLibrary.Instance.compilationStack)
            {
                if (OnCompileInk != null)
                {
                    OnCompileInk(compilingFile.inkFile);
                }
            }

            StringBuilder outputLog = new StringBuilder();

            if (errorsFound)
            {
                outputLog.Append("Ink compilation completed with errors at ");
                outputLog.AppendLine(DateTime.Now.ToLongTimeString());
                outputLog.Append(filesCompiledLog.ToString());
                Debug.LogWarning(outputLog);
            }
            else
            {
                outputLog.Append("Ink compilation completed at ");
                outputLog.AppendLine(DateTime.Now.ToLongTimeString());
                outputLog.Append(filesCompiledLog.ToString());
                Debug.Log(outputLog);
            }

            InkLibrary.Instance.compilationStack.Clear();
            InkLibrary.Save();
            InkMetaLibrary.Save();

            EditorUtility.ClearProgressBar();
            if (EditorApplication.isPlayingOrWillChangePlaymode)
            {
                Debug.LogWarning("Ink just finished recompiling while in play mode. Your runtime story may not be up to date.");
            }
        }
Пример #8
0
        /// <summary>
        /// Starts a System.Process that compiles a master ink file, creating a playable JSON file that can be parsed by the Ink.Story class
        /// </summary>
        /// <param name="inkFile">Ink file.</param>
        public static void CompileInk(InkFile inkFile)
        {
            if (inkFile == null)
            {
                Debug.LogError("Tried to compile ink file " + inkFile.filePath + ", but input was null.");
                return;
            }
            if (!inkFile.metaInfo.isMaster)
            {
                Debug.LogWarning("Compiling InkFile which is an include. Any file created is likely to be invalid. Did you mean to call CompileInk on inkFile.master?");
            }
            if (InkLibrary.GetCompilationStackItem(inkFile) != null)
            {
                UnityEngine.Debug.LogWarning("Tried compiling ink file, but file is already compiling. " + inkFile.filePath);
                return;
            }

            string inklecatePath = InkEditorUtils.GetInklecateFilePath();

            if (inklecatePath == null)
            {
                UnityEngine.Debug.LogWarning("Inklecate (the ink compiler) not found in assets. This will prevent automatic building of JSON TextAsset files from ink story files.");
                return;
            }
            if (Application.platform == RuntimePlatform.OSXEditor)
            {
                SetInklecateFilePermissions(inklecatePath);
            }
            if (inklecatePath.Contains("'"))
            {
                Debug.LogError("Due to a Unity bug, Inklecate path cannot contain an apostrophe. Ink will not compile until this is resolved. Path is '" + inklecatePath + "'");
                return;
            }
            // This hasn't been affecting us lately. Left it in so we can easily restore it in case of future bugs.

            /* else if(inklecatePath.Contains(" ")){
             *      Debug.LogWarning("Inklecate path should not contain a space. This might lead to compilation failing. Path is '"+inklecatePath+"'. If you don't see any compilation errors, you can ignore this warning.");
             * }*/
            string inputPath    = InkEditorUtils.CombinePaths(inkFile.absoluteFolderPath, Path.GetFileName(inkFile.filePath));
            string outputPath   = InkEditorUtils.CombinePaths(inkFile.absoluteFolderPath, Path.GetFileNameWithoutExtension(Path.GetFileName(inkFile.filePath))) + ".json";
            string inkArguments = InkSettings.Instance.customInklecateOptions.additionalCompilerOptions + " -c -o " + "\"" + outputPath + "\" \"" + inputPath + "\"";

            CompilationStackItem pendingFile = new CompilationStackItem();

            pendingFile.inkFile              = InkLibrary.GetInkFileWithAbsolutePath(inputPath);
            pendingFile.inkAbsoluteFilePath  = inputPath;
            pendingFile.jsonAbsoluteFilePath = outputPath;
            pendingFile.state = CompilationStackItem.State.Compiling;
            InkLibrary.Instance.compilationStack.Add(pendingFile);
            InkLibrary.Save();

            Process process = new Process();

            if (InkSettings.Instance.customInklecateOptions.runInklecateWithMono && Application.platform != RuntimePlatform.WindowsEditor)
            {
                if (File.Exists(_libraryMono))
                {
                    process.StartInfo.FileName = _libraryMono;
                }
                else if (File.Exists(_usrMono))
                {
                    process.StartInfo.FileName = _usrMono;
                }
                else
                {
                    Debug.LogError("Mono was not found on machine");
                    return;
                }
                process.StartInfo.Arguments = inklecatePath + " " + inkArguments;
            }
            else
            {
                process.StartInfo.FileName  = inklecatePath;
                process.StartInfo.Arguments = inkArguments;
            }

            process.StartInfo.RedirectStandardError  = true;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.UseShellExecute        = false;
            process.StartInfo.CreateNoWindow         = true;
            process.EnableRaisingEvents = true;
            process.OutputDataReceived += OnProcessOutput;
            // For some reason having this line enabled spams the output and error streams with null and "???" (only on OSX?)
            // Rather than removing unhandled error detection I thought it'd be best to just catch those messages and ignore them instead.
            process.ErrorDataReceived += OnProcessError;
            process.Exited            += OnCompileProcessComplete;
            process.Start();
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();
            pendingFile.process = process;
            // If you'd like to run this command outside of unity, you could instead run process.StartInfo.Arguments in the command line.
        }
Пример #9
0
        private static void Delay()
        {
            if (InkLibrary.FilesInCompilingStackInState(CompilationStackItem.State.Compiling).Count > 0)
            {
                Debug.LogWarning("Delayed, but a file is now compiling! You can ignore this warning.");
                return;
            }
            bool   errorsFound = false;
            string listOfFiles = "\nFiles compiled:";

            foreach (var compilingFile in InkLibrary.Instance.compilationStack)
            {
                listOfFiles += "\n";
                listOfFiles += compilingFile.inkFile.filePath;
                if (compilingFile.errorOutput != "")
                {
                    listOfFiles += " (With unhandled error)";
                    Debug.LogError("Unhandled error occurred compiling Ink file " + compilingFile.inkFile + "! Please report following error as a bug:\n" + compilingFile.errorOutput);
                    compilingFile.inkFile.compileErrors.Clear();
                    compilingFile.inkFile.compileErrors.Add(compilingFile.errorOutput);
                    errorsFound = true;
                }
                else
                {
                    SetOutputLog(compilingFile);
                    bool errorsInEntireStory   = false;
                    bool warningsInEntireStory = false;
                    foreach (var inkFile in compilingFile.inkFile.inkFilesInIncludeHierarchy)
                    {
                        if (inkFile.hasErrors)
                        {
                            errorsInEntireStory = true;
                        }
                        if (inkFile.hasWarnings)
                        {
                            warningsInEntireStory = true;
                        }
                    }
                    if (errorsInEntireStory)
                    {
                        listOfFiles += " (With error)";
                        errorsFound  = true;
                    }
                    else
                    {
                        string localJSONAssetPath = compilingFile.jsonAbsoluteFilePath.Substring(Application.dataPath.Length - 6);
                        AssetDatabase.ImportAsset(localJSONAssetPath);
                        compilingFile.inkFile.jsonAsset = AssetDatabase.LoadAssetAtPath <TextAsset> (localJSONAssetPath);
                    }
                    if (warningsInEntireStory)
                    {
                        listOfFiles += " (With warning)";
                    }
                }
            }

            foreach (var compilingFile in InkLibrary.Instance.compilationStack)
            {
                if (OnCompileInk != null)
                {
                    OnCompileInk(compilingFile.inkFile);
                }
            }

            if (errorsFound)
            {
                Debug.LogWarning("Ink compilation completed with errors at " + DateTime.Now.ToLongTimeString() + listOfFiles);
            }
            else
            {
                Debug.Log("Ink compilation completed at " + DateTime.Now.ToLongTimeString() + listOfFiles);
            }
            InkLibrary.Instance.compilationStack.Clear();
            EditorUtility.ClearProgressBar();
            if (EditorApplication.isPlayingOrWillChangePlaymode)
            {
                Debug.LogWarning("Ink just finished recompiling while in play mode. Your runtime story may not be up to date.");
            }
            InkLibrary.Save();
        }
        // When all files in stack have been compiled. This is called via update because Process events run in another thread.
        private static void DelayedComplete()
        {
            if (InkLibrary.NumFilesInCompilingStackInState(CompilationStackItem.State.Compiling) > 0)
            {
                Debug.LogWarning("Delayed, but a file is now compiling! You can ignore this warning.");
                return;
            }
            bool          errorsFound      = false;
            StringBuilder filesCompiledLog = new StringBuilder("Files compiled:");

            // Create and import compiled files
            AssetDatabase.StartAssetEditing();
            foreach (var compilingFile in InkLibrary.Instance.compilationStack)
            {
                // Complete status is also set when an error occured, in these cases 'compiledJson' will be null so there's no import to process
                if (compilingFile.compiledJson == null)
                {
                    continue;
                }

                // Write new compiled data to the file system
                File.WriteAllText(compilingFile.jsonAbsoluteFilePath, compilingFile.compiledJson, Encoding.UTF8);
                AssetDatabase.ImportAsset(compilingFile.inkFile.jsonPath);
            }
            AssetDatabase.StopAssetEditing();

            foreach (var compilingFile in InkLibrary.Instance.compilationStack)
            {
                // Load and store a reference to the compiled file
                compilingFile.inkFile.FindCompiledJSONAsset();

                filesCompiledLog.AppendLine().Append(compilingFile.inkFile.filePath);
                filesCompiledLog.Append(string.Format(" ({0}s)", compilingFile.timeTaken));
                if (compilingFile.unhandledErrorOutput.Count > 0)
                {
                    filesCompiledLog.Append(" (With unhandled error)");
                    StringBuilder errorLog = new StringBuilder();
                    errorLog.Append("Unhandled error(s) occurred compiling Ink file ");
                    errorLog.Append("'");
                    errorLog.Append(compilingFile.inkFile.filePath);
                    errorLog.Append("'");
                    errorLog.AppendLine("! Please report following error(s) as a bug:");
                    foreach (var error in compilingFile.unhandledErrorOutput)
                    {
                        errorLog.AppendLine(error);
                    }
                    Debug.LogError(errorLog);
                    compilingFile.inkFile.metaInfo.unhandledCompileErrors = compilingFile.unhandledErrorOutput;
                    errorsFound = true;
                }
                else
                {
                    SetOutputLog(compilingFile);
                    bool errorsInEntireStory   = false;
                    bool warningsInEntireStory = false;
                    foreach (var inkFile in compilingFile.inkFile.metaInfo.inkFilesInIncludeHierarchy)
                    {
                        if (inkFile.metaInfo.hasErrors)
                        {
                            errorsInEntireStory = true;
                        }
                        if (inkFile.metaInfo.hasWarnings)
                        {
                            warningsInEntireStory = true;
                        }
                    }
                    if (errorsInEntireStory)
                    {
                        filesCompiledLog.Append(" (With error)");
                        errorsFound = true;
                    }
                    if (warningsInEntireStory)
                    {
                        filesCompiledLog.Append(" (With warning)");
                    }
                }
            }


            foreach (var compilingFile in InkLibrary.Instance.compilationStack)
            {
                if (OnCompileInk != null)
                {
                    OnCompileInk(compilingFile.inkFile);
                }
            }

            StringBuilder outputLog = new StringBuilder();

            if (errorsFound)
            {
                outputLog.Append("Ink compilation completed with errors at ");
                outputLog.AppendLine(DateTime.Now.ToLongTimeString());
                outputLog.Append(filesCompiledLog.ToString());
                Debug.LogError(outputLog);
            }
            else
            {
                outputLog.Append("Ink compilation completed at ");
                outputLog.AppendLine(DateTime.Now.ToLongTimeString());
                outputLog.Append(filesCompiledLog.ToString());
                Debug.Log(outputLog);
            }

            InkLibrary.Instance.compilationStack.Clear();
            InkLibrary.Save();
            InkMetaLibrary.Save();

                        #if !UNITY_EDITOR_LINUX
            EditorUtility.ClearProgressBar();
                        #endif

                        #if UNITY_2019_4_OR_NEWER
            AssetDatabase.AllowAutoRefresh();
                        #endif

            // This is now allowed, if compiled manually. I've left this code commented out because at some point we might want to track what caused a file to compile.
            // if(EditorApplication.isPlayingOrWillChangePlaymode && InkSettings.Instance.delayInPlayMode) {
            //  Debug.LogError("Ink just finished recompiling while in play mode. This should never happen when InkSettings.Instance.delayInPlayMode is true!");
            // }

            buildBlocked = false;

            if (playModeBlocked)
            {
                playModeBlocked = false;
                if (!errorsFound)
                {
                    // Delaying gives the editor a frame to clear the progress bar.
                    EditorApplication.delayCall += () => {
                        Debug.Log("Compilation completed, entering play mode.");
                        EditorApplication.isPlaying = true;
                    };
                }
                else
                {
                    Debug.LogWarning("Play mode not entered after ink compilation because ink had errors.");
                }
            }

            foreach (var onCompleteAction in onCompleteActions)
            {
                if (onCompleteAction != null)
                {
                    onCompleteAction();
                }
            }
            onCompleteActions.Clear();
        }
Пример #11
0
        // When all files in stack have been compiled. This is called via update because Process events run in another thread.
        private static void DelayedComplete()
        {
            if (InkLibrary.FilesInCompilingStackInState(CompilationStackItem.State.Compiling).Count > 0)
            {
                Debug.LogWarning("Delayed, but a file is now compiling! You can ignore this warning.");
                return;
            }
            float         longestTimeTaken = 0;
            bool          errorsFound      = false;
            StringBuilder filesCompiledLog = new StringBuilder("Files compiled:");

            foreach (var compilingFile in InkLibrary.Instance.compilationStack)
            {
                // Complete status is also set when an error occured, in these cases 'compiledJson' will be null so there's no import to process
                if (compilingFile.compiledJson != null)
                {
                    // Write new compiled data to the file system
                    File.WriteAllText(compilingFile.jsonAbsoluteFilePath, compilingFile.compiledJson, Encoding.UTF8);
                    AssetDatabase.ImportAsset(compilingFile.jsonAbsoluteFilePath);
                    var jsonObject = AssetDatabase.LoadAssetAtPath <TextAsset>(compilingFile.inkFile.jsonPath);

                    // Update the jsonAsset reference
                    compilingFile.inkFile.jsonAsset = jsonObject;
                }

                longestTimeTaken = Mathf.Max(compilingFile.timeTaken);
                filesCompiledLog.AppendLine().Append(compilingFile.inkFile.filePath);
                filesCompiledLog.Append(string.Format(" ({0}s)", compilingFile.timeTaken));
                if (compilingFile.errorOutput.Count > 0)
                {
                    filesCompiledLog.Append(" (With unhandled error)");
                    StringBuilder errorLog = new StringBuilder();
                    errorLog.Append("Unhandled error(s) occurred compiling Ink file ");
                    errorLog.Append("'");
                    errorLog.Append(compilingFile.inkFile.filePath);
                    errorLog.Append("'");
                    errorLog.AppendLine("! Please report following error(s) as a bug:");
                    foreach (var error in compilingFile.errorOutput)
                    {
                        errorLog.AppendLine(error);
                    }
                    Debug.LogError(errorLog);
                    compilingFile.inkFile.metaInfo.compileErrors = compilingFile.errorOutput;
                    errorsFound = true;
                }
                else
                {
                    SetOutputLog(compilingFile);
                    bool errorsInEntireStory   = false;
                    bool warningsInEntireStory = false;
                    foreach (var inkFile in compilingFile.inkFile.metaInfo.inkFilesInIncludeHierarchy)
                    {
                        if (inkFile.metaInfo.hasErrors)
                        {
                            errorsInEntireStory = true;
                        }
                        if (inkFile.metaInfo.hasWarnings)
                        {
                            warningsInEntireStory = true;
                        }
                    }
                    if (errorsInEntireStory)
                    {
                        filesCompiledLog.Append(" (With error)");
                        errorsFound = true;
                    }
                    if (warningsInEntireStory)
                    {
                        filesCompiledLog.Append(" (With warning)");
                    }
                }
            }

            if (longestTimeTaken > InkSettings.Instance.compileTimeout * 0.6f)
            {
                Debug.LogWarning("Compilation took over 60% of the time required to timeout the compiler. Consider increasing the compile timeout on the InkSettings file.");
            }

            foreach (var compilingFile in InkLibrary.Instance.compilationStack)
            {
                if (OnCompileInk != null)
                {
                    OnCompileInk(compilingFile.inkFile);
                }
            }

            StringBuilder outputLog = new StringBuilder();

            if (errorsFound)
            {
                outputLog.Append("Ink compilation completed with errors at ");
                outputLog.AppendLine(DateTime.Now.ToLongTimeString());
                outputLog.Append(filesCompiledLog.ToString());
                Debug.LogError(outputLog);
            }
            else
            {
                outputLog.Append("Ink compilation completed at ");
                outputLog.AppendLine(DateTime.Now.ToLongTimeString());
                outputLog.Append(filesCompiledLog.ToString());
                Debug.Log(outputLog);
            }

            InkLibrary.Instance.compilationStack.Clear();
            InkLibrary.Save();
            InkMetaLibrary.Save();

                        #if !UNITY_EDITOR_LINUX
            EditorUtility.ClearProgressBar();
                        #endif
            if (EditorApplication.isPlayingOrWillChangePlaymode && InkSettings.Instance.delayInPlayMode)
            {
                Debug.LogError("Ink just finished recompiling while in play mode. This should never happen when InkSettings.Instance.delayInPlayMode is true!");
            }

            buildBlocked = false;

            if (playModeBlocked)
            {
                playModeBlocked = false;
                if (!errorsFound)
                {
                    // Delaying gives the editor a frame to clear the progress bar.
                    EditorApplication.delayCall += () => {
                        Debug.Log("Compilation completed, entering play mode.");
                        EditorApplication.isPlaying = true;
                    };
                }
                else
                {
                    Debug.LogWarning("Play mode not entered after ink compilation because ink had errors.");
                }
            }
        }