public void OnEnable() { data = (InkLibrary) target; list = new ReorderableList(data.inkLibrary, typeof(InkFile), false, true, false, false); // list.elementHeight = 60; list.drawHeaderCallback = (Rect rect) => { EditorGUI.LabelField(rect, "Ink Library"); }; list.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => { InkFile inkFile = ((List<InkFile>)list.list)[index]; Rect objectFieldRect = new Rect(rect.x, rect.y, rect.width - 80, rect.height-4); if(!inkFile.isMaster) { objectFieldRect.x += 14; objectFieldRect.width -= 14; } Rect selectRect = new Rect(objectFieldRect.xMax, rect.y, 80, rect.height-4); // Rect titleRect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight); // Rect inkAssetRect = new Rect(rect.x, rect.y + EditorGUIUtility.singleLineHeight * 1, rect.width, EditorGUIUtility.singleLineHeight); // Rect jsonAssetRect = new Rect(rect.x, rect.y + EditorGUIUtility.singleLineHeight * 2, rect.width, EditorGUIUtility.singleLineHeight); // // inkFile.compileAutomatically = EditorGUI.Toggle(titleRect, "Compile Automatically", inkFile.compileAutomatically); EditorGUI.BeginDisabledGroup(true); inkFile.inkAsset = EditorGUI.ObjectField(objectFieldRect, inkFile.inkAsset, typeof(DefaultAsset)) as DefaultAsset; EditorGUI.EndDisabledGroup(); if(GUI.Button(selectRect, "Select")) { Selection.activeObject = inkFile.inkAsset; } // inkFile.jsonAsset = EditorGUI.ObjectField(jsonAssetRect, new GUIContent("JSON File"), inkFile.jsonAsset, typeof(TextAsset)) as TextAsset; }; }
/// <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> internal static void CompileInkInternal(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 = inkFile.absoluteJSONPath; 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) { foreach (var path in InkSettings.Instance.customInklecateOptions.monoPaths) { if (File.Exists(path)) { process.StartInfo.FileName = path; } } if (process.StartInfo.FileName == null) { Debug.LogError("Mono was not found on machine, please edit the mono paths in settings to include a valid one for your 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. }
public override void OnInspectorGUI() { editor.Repaint(); serializedObject.Update(); if (inkFile == null) { EditorGUILayout.HelpBox("Ink File is not in library.", MessageType.Warning); if (GUILayout.Button("Rebuild Library")) { InkLibrary.Rebuild(); Rebuild(); } return; } if (InkLibrary.GetCompilationStackItem(inkFile) != null) { EditorGUILayout.HelpBox("File is compiling...", MessageType.Info); return; } InkFile masterInkFile = inkFile; if (inkFile.metaInfo.isMaster) { DrawMasterFileHeader(); } else { masterInkFile = inkFile.metaInfo.masterInkFile; DrawSubFileHeader(masterInkFile); } DrawEditAndCompileDates(masterInkFile); if (masterInkFile.metaInfo.hasCompileErrors) { EditorGUILayout.HelpBox("Last compiled failed", MessageType.Error); } if (masterInkFile.metaInfo.hasErrors) { EditorGUILayout.HelpBox("Last compiled had errors", MessageType.Error); } else if (masterInkFile.metaInfo.hasWarnings) { EditorGUILayout.HelpBox("Last compile had warnings", MessageType.Warning); } else if (masterInkFile.jsonAsset == null) { EditorGUILayout.HelpBox("Ink file has not been compiled", MessageType.Warning); } if (inkFile.metaInfo.requiresCompile && GUILayout.Button("Compile")) { InkCompiler.CompileInk(masterInkFile); } DrawIncludedFiles(); DrawCompileErrors(); DrawErrors(); DrawWarnings(); DrawTODOList(); DrawFileContents(); serializedObject.ApplyModifiedProperties(); }
static void OnOpenUnityEditor() { InkLibrary.Rebuild(); }
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."); } }
public override void OnInspectorGUI() { serializedObject.Update(); EditorGUI.BeginDisabledGroup(InkCompiler.compiling); if (GUILayout.Button(new GUIContent("Rebuild Library", "Rebuilds the ink library. Do this if you're getting unusual errors"), EditorStyles.miniButton)) { InkLibrary.Rebuild(); } EditorGUI.EndDisabledGroup(); EditorGUILayout.Toggle("HasLockedUnityCompilation", InkCompiler.hasLockedUnityCompilation); if (GUILayout.Button("Unlock")) { EditorApplication.UnlockReloadAssemblies(); } #if UNITY_2019_4_OR_NEWER if (GUILayout.Button("AllowAutoRefresh")) { AssetDatabase.AllowAutoRefresh(); } #endif if (InkCompiler.compiling) { Rect r = EditorGUILayout.BeginVertical(); EditorGUI.ProgressBar(r, InkCompiler.GetEstimatedCompilationProgress(), "Compiling..."); GUILayout.Space(EditorGUIUtility.singleLineHeight); EditorGUILayout.EndVertical(); GUILayout.Space(EditorGUIUtility.standardVerticalSpacing); } else { var filesRequiringRecompile = InkLibrary.GetFilesRequiringRecompile(); if (filesRequiringRecompile.Any()) { var files = string.Join("\n", filesRequiringRecompile.Select(x => x.filePath).ToArray()); if (EditorApplication.isPlaying && InkSettings.instance.delayInPlayMode) { EditorGUILayout.HelpBox("Some Ink files marked to compile on exiting play mode.\n" + files, MessageType.Info); } else { EditorGUILayout.HelpBox("Some Ink files marked to compile automatically are not compiled! Check they don't have compile errors, or else try compiling now.\n" + files, MessageType.Warning); } } else { EditorGUILayout.HelpBox("All Ink files marked to compile automatically are compiled", MessageType.Info); } } EditorGUI.BeginDisabledGroup(InkCompiler.compiling); if (GUILayout.Button(new GUIContent("Recompile All", "Recompiles all files marked to compile automatically."))) { InkEditorUtils.RecompileAll(); } if (EditorApplication.isPlaying && InkSettings.instance.delayInPlayMode) { var filesRequiringRecompile = InkLibrary.GetFilesRequiringRecompile(); if (filesRequiringRecompile.Any()) { var files = string.Join("\n", filesRequiringRecompile.Select(x => x.filePath).ToArray()); if (GUILayout.Button(new GUIContent("Recompile Pending", "Recompiles all files marked to compile on exiting play mode."))) { InkEditorUtils.RecompileAll(); } } } EditorGUI.EndDisabledGroup(); EditorGUI.BeginDisabledGroup(true); EditorGUILayout.PropertyField(serializedObject.FindProperty("inkLibrary"), true); EditorGUILayout.PropertyField(serializedObject.FindProperty("pendingCompilationStack"), true); EditorGUILayout.PropertyField(serializedObject.FindProperty("compilationStack"), true); EditorGUI.EndDisabledGroup(); if (GUILayout.Button(new GUIContent("Clear Compilation Stacks"))) { InkCompiler.ClearCompilationStacks(); } if (GUI.changed && target != null) { EditorUtility.SetDirty(target); } serializedObject.ApplyModifiedProperties(); }
private static void OnMoveAssets(string[] movedAssets) { if (!InkSettings.Instance.handleJSONFilesAutomatically) return; List<string> validMovedAssets = new List<string>(); for (var i = 0; i < movedAssets.Length; i++) { if(Path.GetExtension(movedAssets[i]) != InkEditorUtils.inkFileExtension) continue; validMovedAssets.Add(movedAssets[i]); queuedMovedAssets.Add(movedAssets[i]); } // Move compiled JSON files. // This can cause Unity to postprocess assets again. bool assetMoved = false; foreach(var inkFilePath in validMovedAssets) { InkFile inkFile = InkLibrary.GetInkFileWithPath(inkFilePath); if(inkFile == null) continue; if(inkFile.jsonAsset == null) continue; string jsonAssetPath = AssetDatabase.GetAssetPath(inkFile.jsonAsset); string movedAssetDir = Path.GetDirectoryName(inkFilePath); string movedAssetFile = Path.GetFileName(inkFilePath); string newPath = InkEditorUtils.CombinePaths(movedAssetDir, Path.GetFileNameWithoutExtension(movedAssetFile)) + ".json"; AssetDatabase.MoveAsset(jsonAssetPath, newPath); assetMoved = true; } // Check if no JSON assets were moved (as a result of none needing to move, or this function being called as a result of JSON files being moved) if(!assetMoved && queuedMovedAssets.Count > 0) { List<InkFile> filesToCompile = new List<InkFile>(); // Add the old master file to the files to be recompiled foreach(var inkFilePath in queuedMovedAssets) { InkFile inkFile = InkLibrary.GetInkFileWithPath(inkFilePath); if(inkFile == null) continue; InkFile masterInkFile = inkFile; if(!inkFile.metaInfo.isMaster) masterInkFile = inkFile.metaInfo.masterInkFile; if(!filesToCompile.Contains(masterInkFile)) filesToCompile.Add(masterInkFile); } InkMetaLibrary.RebuildInkFileConnections(); // Add the new file to be recompiled foreach(var inkFilePath in queuedMovedAssets) { InkFile inkFile = InkLibrary.GetInkFileWithPath(inkFilePath); if(inkFile == null) continue; InkFile masterInkFile = inkFile; if(!inkFile.metaInfo.isMaster) masterInkFile = inkFile.metaInfo.masterInkFile; if(!filesToCompile.Contains(masterInkFile)) filesToCompile.Add(masterInkFile); } queuedMovedAssets.Clear(); // Compile any ink files that are deemed master files a rebuild foreach(var inkFile in filesToCompile) { if(inkFile.metaInfo.isMaster) InkCompiler.CompileInk(inkFile); } } }
public static void RebuildLibrary() { InkLibrary.Rebuild(); }
void CreateMastersList() { List <DefaultAsset> mastersTextAssets = inkFile.masterInkAssets; mastersFileList = new ReorderableList(mastersTextAssets, typeof(DefaultAsset), false, false, false, false); mastersFileList.drawHeaderCallback = (Rect rect) => { EditorGUI.LabelField(rect, "Master Files"); }; mastersFileList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => { DefaultAsset masterAssetFile = ((List <DefaultAsset>)mastersFileList.list)[index]; if (masterAssetFile == null) { Debug.LogError("Ink file in masters list is null. This should never occur. Use Assets > Recompile Ink to fix this issue."); EditorGUI.LabelField(rect, new GUIContent("Warning: Ink File in masters list is null. Use Assets > Recompile Ink to fix this issue.")); return; } InkFile masterInkFile = InkLibrary.GetInkFileWithFile(masterAssetFile); if (masterInkFile == null) { Debug.LogError("Ink File for master file " + masterAssetFile + " not found. This should never occur. Use Assets > Recompile Ink to fix this issue."); EditorGUI.LabelField(rect, new GUIContent("Warning: Ink File for master file " + masterAssetFile + " not found. Use Assets > Recompile Ink to fix this issue.")); return; } Rect iconRect = new Rect(rect.x, rect.y, 0, 16); if (masterInkFile.hasErrors || masterInkFile.hasWarnings) { iconRect.width = 20; } Rect objectFieldRect = new Rect(iconRect.xMax, rect.y, rect.width - iconRect.width - 80, 16); Rect selectRect = new Rect(objectFieldRect.xMax, rect.y, 80, 16); if (masterInkFile.hasErrors) { EditorGUI.LabelField(iconRect, new GUIContent(InkBrowserIcons.errorIcon)); } else if (masterInkFile.hasWarnings) { EditorGUI.LabelField(iconRect, new GUIContent(InkBrowserIcons.warningIcon)); } EditorGUI.BeginDisabledGroup(true); EditorGUI.ObjectField(objectFieldRect, masterAssetFile, typeof(Object), false); EditorGUI.EndDisabledGroup(); if (GUI.Button(selectRect, "Select")) { Selection.activeObject = masterAssetFile; } // foreach(var masterInkFile in inkFile.masterInkFiles) { // EditorGUILayout.BeginHorizontal(); // if(masterInkFile.hasErrors) { // GUILayout.Label(new GUIContent(InkBrowserIcons.errorIcon), GUILayout.Width(20)); // } else if(masterInkFile.hasWarnings) { // GUILayout.Label(new GUIContent(InkBrowserIcons.warningIcon), GUILayout.Width(20)); // } // EditorGUI.BeginDisabledGroup(true); // EditorGUILayout.ObjectField("Master Ink File", masterInkFile.inkAsset, typeof(Object), false); // EditorGUI.EndDisabledGroup(); // if(GUILayout.Button("Select", GUILayout.Width(80))) { // Selection.activeObject = masterInkFile.inkAsset; // } // EditorGUILayout.EndHorizontal(); // } }; }
public static void ClearCompilationStacks() { InkLibrary.Instance.compilationStack.Clear(); InkLibrary.Instance.pendingCompilationStack.Clear(); InkLibrary.SaveToFile(); }
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); } }
public void OnEnable() { data = (InkLibrary)target; }
private void OnDisable() { _Instance = null; }
private void OnEnable() { _Instance = this; }
public override void OnInspectorGUI() { editor.Repaint(); serializedObject.Update(); if (inkFile == null) { EditorGUILayout.HelpBox("Ink File is not in library.", MessageType.Warning); if (GUILayout.Button("Rebuild Library")) { InkLibrary.Rebuild(); Rebuild(); } return; } if (InkCompiler.IsInkFileOnCompilationStack(inkFile)) { EditorGUILayout.HelpBox("File is compiling...", MessageType.Info); return; } if (!inkFile.isMaster) { EditorGUI.BeginChangeCheck(); var newCompileAsIfMaster = EditorGUILayout.Toggle(new GUIContent("Compile As If Master File", "This file is included by another ink file. Typically, these files don't want to be compiled, but this option enables them to be for special purposes."), InkSettings.instance.includeFilesToCompileAsMasterFiles.Contains(inkFile.inkAsset)); if (EditorGUI.EndChangeCheck()) { if (newCompileAsIfMaster) { InkSettings.instance.includeFilesToCompileAsMasterFiles.Add(inkFile.inkAsset); EditorUtility.SetDirty(InkSettings.instance); } else { InkSettings.instance.includeFilesToCompileAsMasterFiles.Remove(inkFile.inkAsset); EditorUtility.SetDirty(InkSettings.instance); } } EditorApplication.RepaintProjectWindow(); } if (inkFile.compileAsMasterFile) { DrawMasterFileHeader(); DrawEditAndCompileDates(inkFile); if (inkFile.hasUnhandledCompileErrors) { EditorGUILayout.HelpBox("Last compiled failed", MessageType.Error); } if (inkFile.hasErrors) { EditorGUILayout.HelpBox("Last compiled had errors", MessageType.Error); } else if (inkFile.hasWarnings) { EditorGUILayout.HelpBox("Last compile had warnings", MessageType.Warning); } else if (inkFile.jsonAsset == null) { EditorGUILayout.HelpBox("Ink file has not been compiled", MessageType.Warning); } if (inkFile.requiresCompile && GUILayout.Button("Compile")) { InkCompiler.CompileInk(inkFile); } DrawCompileErrors(); DrawErrors(); DrawWarnings(); DrawTODOList(); } else { EditorGUILayout.LabelField("Include File", EditorStyles.boldLabel); } DrawListOfMasterFiles(); DrawIncludedFiles(); DrawFileContents(); serializedObject.ApplyModifiedProperties(); }
// 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) { 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(); #if !UNITY_EDITOR_LINUX EditorUtility.ClearProgressBar(); #endif if (EditorApplication.isPlayingOrWillChangePlaymode) { Debug.LogWarning("Ink just finished recompiling while in play mode. Your runtime story may not be up to date."); } if (playModeBlocked) { 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."); } } }
private static void SetOutputLog(CompilationStackItem pendingFile) { pendingFile.inkFile.metaInfo.errors.Clear(); pendingFile.inkFile.metaInfo.warnings.Clear(); pendingFile.inkFile.metaInfo.todos.Clear(); foreach (var childInkFile in pendingFile.inkFile.metaInfo.inkFilesInIncludeHierarchy) { childInkFile.metaInfo.compileErrors.Clear(); childInkFile.metaInfo.errors.Clear(); childInkFile.metaInfo.warnings.Clear(); childInkFile.metaInfo.todos.Clear(); } foreach (string output in pendingFile.output) { var match = _errorRegex.Match(output); if (match.Success) { string errorType = null; string filename = null; int lineNo = -1; string message = null; var errorTypeCapture = match.Groups["errorType"]; if (errorTypeCapture != null) { errorType = errorTypeCapture.Value; } var filenameCapture = match.Groups["filename"]; if (filenameCapture != null) { filename = filenameCapture.Value; } var lineNoCapture = match.Groups["lineNo"]; if (lineNoCapture != null) { lineNo = int.Parse(lineNoCapture.Value); } var messageCapture = match.Groups["message"]; if (messageCapture != null) { message = messageCapture.Value.Trim(); } string logFilePath = InkEditorUtils.CombinePaths(Path.GetDirectoryName(pendingFile.inkFile.filePath), filename); InkFile inkFile = InkLibrary.GetInkFileWithPath(logFilePath); if (inkFile == null) { inkFile = pendingFile.inkFile; } string pathAndLineNumberString = "\n" + inkFile.filePath + ":" + lineNo; if (errorType == "ERROR") { inkFile.metaInfo.errors.Add(new InkMetaFile.InkFileLog(message, lineNo)); Debug.LogError("INK " + errorType + ": " + message + pathAndLineNumberString); } else if (errorType == "WARNING") { inkFile.metaInfo.warnings.Add(new InkMetaFile.InkFileLog(message, lineNo)); Debug.LogWarning("INK " + errorType + ": " + message + pathAndLineNumberString); } else if (errorType == "TODO") { inkFile.metaInfo.todos.Add(new InkMetaFile.InkFileLog(message, lineNo)); Debug.Log("INK " + errorType + ": " + message + pathAndLineNumberString); } } } }
private static void Update() { // If we're not compiling but have locked C# compilation then now is the time to reset if ((!InkLibrary.created || !compiling) && hasLockedUnityCompilation) { hasLockedUnityCompilation = false; EditorApplication.UnlockReloadAssemblies(); } if (!InkLibrary.created) { return; } // When all files have compiled, run the complete function. if (compiling && InkLibrary.NumFilesInCompilingStackInState(CompilationStackItem.State.Queued) == 0 && InkLibrary.NumFilesInCompilingStackInState(CompilationStackItem.State.Compiling) == 0) { DelayedComplete(); } for (int i = InkLibrary.Instance.compilationStack.Count - 1; i >= 0; i--) { var compilingFile = InkLibrary.Instance.compilationStack [i]; if (compilingFile.state == CompilationStackItem.State.Compiling) { if (compilingFile.timeTaken > InkSettings.Instance.compileTimeout) { // TODO - Cancel the thread if it's still going. Not critical, since its kinda fine if it compiles a bit later, but it's not clear. RemoveCompilingFile(i); Debug.LogError("Ink Compiler timed out for " + compilingFile.inkAbsoluteFilePath + ".\nCompilation 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" + string.Join("\n", compilingFile.unhandledErrorOutput.ToArray())); TryCompileNextFileInStack(); } } } // If we're not showing a progress bar in Linux this whole step is superfluous #if !UNITY_EDITOR_LINUX UpdateProgressBar(); #endif }
// 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."); } } }
/// <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.EnableRaisingEvents = true; process.StartInfo.EnvironmentVariables["inkAbsoluteFilePath"] = inputPath; process.ErrorDataReceived += OnProcessError; process.Exited += OnCompileProcessComplete; process.Start(); }
// 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(); }
private static void SetOutputLog(CompilationStackItem pendingFile) { pendingFile.inkFile.errors.Clear(); pendingFile.inkFile.warnings.Clear(); pendingFile.inkFile.todos.Clear(); foreach (var child in pendingFile.inkFile.includes) { if (child == null) { Debug.LogError("Error compiling ink: Ink file include in " + pendingFile.inkFile.filePath + " is null."); continue; } InkFile childInkFile = InkLibrary.GetInkFileWithFile((DefaultAsset)child); childInkFile.errors.Clear(); childInkFile.warnings.Clear(); childInkFile.todos.Clear(); } string[] splitOutput = pendingFile.output.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries); foreach (string output in splitOutput) { var match = _errorRegex.Match(output); if (match.Success) { string errorType = null; string filename = null; int lineNo = -1; string message = null; var errorTypeCapture = match.Groups["errorType"]; if (errorTypeCapture != null) { errorType = errorTypeCapture.Value; } var filenameCapture = match.Groups["filename"]; if (filenameCapture != null) { filename = filenameCapture.Value; } var lineNoCapture = match.Groups["lineNo"]; if (lineNoCapture != null) { lineNo = int.Parse(lineNoCapture.Value); } var messageCapture = match.Groups["message"]; if (messageCapture != null) { message = messageCapture.Value.Trim(); } string logFilePath = Path.Combine(Path.GetDirectoryName(pendingFile.inkFile.filePath), filename); logFilePath = logFilePath.Replace('\\', '/'); InkFile inkFile = InkLibrary.GetInkFileWithPath(logFilePath); if (inkFile == null) { inkFile = pendingFile.inkFile; } string pathAndLineNumberString = "\n" + inkFile.filePath + "(" + lineNo + ")"; if (errorType == "ERROR") { inkFile.errors.Add(new InkFile.InkFileLog(message, lineNo)); Debug.LogError("INK " + errorType + ": " + message + pathAndLineNumberString); } else if (errorType == "WARNING") { inkFile.warnings.Add(new InkFile.InkFileLog(message, lineNo)); Debug.LogWarning("INK " + errorType + ": " + message + pathAndLineNumberString); } else if (errorType == "TODO") { inkFile.todos.Add(new InkFile.InkFileLog(message, lineNo)); Debug.Log("INK " + errorType + ": " + message + pathAndLineNumberString); } } } }