/// <summary> /// Creates a new .yarnproject asset in the same directory as the /// Yarn script represented by serializedObject, and configures the /// script's importer to use the new Yarn Project. /// </summary> /// <param name="serializedObject">A serialized object that /// represents a <see cref="YarnImporter"/>.</param> /// <returns>The path to the created asset.</returns> internal static string CreateYarnProject(YarnImporter initialSourceAsset) { // Figure out where on disk this asset is var path = initialSourceAsset.assetPath; var directory = Path.GetDirectoryName(path); // Figure out a new, unique path for the localization we're // creating var databaseFileName = $"Project.yarnproject"; var destinationPath = Path.Combine(directory, databaseFileName); destinationPath = AssetDatabase.GenerateUniqueAssetPath(destinationPath); // Create the program YarnEditorUtility.CreateYarnAsset(destinationPath); AssetDatabase.ImportAsset(destinationPath); AssetDatabase.SaveAssets(); var programImporter = AssetImporter.GetAtPath(destinationPath) as YarnProjectImporter; programImporter.sourceScripts.Add(AssetDatabase.LoadAssetAtPath <TextAsset>(path)); EditorUtility.SetDirty(programImporter); // Reimport the program to make it generate its default string // table, if needed programImporter.SaveAndReimport(); return(destinationPath); }
private void ImportCompiledYarn(AssetImportContext ctx) { var bytes = File.ReadAllBytes(ctx.assetPath); try { // Validate that this can be parsed as a Program protobuf var _ = Program.Parser.ParseFrom(bytes); } catch (Google.Protobuf.InvalidProtocolBufferException) { ctx.LogImportError("Invalid compiled yarn file. Please re-compile the source code."); return; } isSuccessfullyParsed = true; // Create a container for storing the bytes var programContainer = new TextAsset("<pre-compiled Yarn script>"); // Add this container to the imported asset; it will be what // the user interacts with in Unity ctx.AddObjectToAsset("Program", programContainer, YarnEditorUtility.GetYarnDocumentIconTexture()); ctx.SetMainObject(programContainer); }
private void ImportYarn(AssetImportContext ctx) { var sourceText = File.ReadAllText(ctx.assetPath); string fileName = System.IO.Path.GetFileNameWithoutExtension(ctx.assetPath); var text = new TextAsset(File.ReadAllText(ctx.assetPath)); // Add this container to the imported asset; it will be what // the user interacts with in Unity ctx.AddObjectToAsset("Program", text, YarnEditorUtility.GetYarnDocumentIconTexture()); ctx.SetMainObject(text); Yarn.Program compiledProgram = null; IDictionary <string, Yarn.Compiler.StringInfo> stringTable = null; parseErrorMessage = null; try { // Compile the source code into a compiled Yarn program (or // generate a parse error) var compilationJob = CompilationJob.CreateFromString(fileName, sourceText, null); compilationJob.CompilationType = CompilationJob.Type.StringsOnly; var result = Yarn.Compiler.Compiler.Compile(compilationJob); LastImportHadImplicitStringIDs = result.ContainsImplicitStringTags; LastImportHadAnyStrings = result.StringTable.Count > 0; stringTable = result.StringTable; compiledProgram = result.Program; isSuccessfullyParsed = true; parseErrorMessage = string.Empty; } catch (Yarn.Compiler.ParseException e) { isSuccessfullyParsed = false; parseErrorMessage = e.Message; ctx.LogImportError($"Error importing {ctx.assetPath}: {e.Message}"); return; } }
// Sets up a YarnProject, and as many yarn scripts as there are // parameters. All files will have random filenames. public YarnProject SetUpProject(params string[] yarnScriptText) { // Disable errors causing failures, in case the yarn script // text contains deliberately invalid code var wasIgnoringFailingMessages = LogAssert.ignoreFailingMessages; LogAssert.ignoreFailingMessages = true; string yarnProjectName = Path.GetRandomFileName(); string yarnProjectPath = $"Assets/{yarnProjectName}.yarnproject"; createdFilePaths.Add(yarnProjectPath); var project = YarnEditorUtility.CreateYarnProject(yarnProjectPath) as YarnProject; var yarnProjectImporter = AssetImporter.GetAtPath(yarnProjectPath) as YarnProjectImporter; var pathsToAdd = new List <string>(); foreach (var scriptText in yarnScriptText) { string yarnScriptName = Path.GetRandomFileName(); string yarnScriptPath = $"Assets/{yarnScriptName}.yarn"; createdFilePaths.Add(yarnScriptPath); pathsToAdd.Add(yarnScriptPath); string textToWrite; if (string.IsNullOrEmpty(scriptText)) { textToWrite = $"title: {yarnScriptName.Replace(".", "_")}\n---\n===\n"; } else { textToWrite = scriptText; } File.WriteAllText(yarnScriptPath, textToWrite); } // Import all these files AssetDatabase.Refresh(); foreach (var path in pathsToAdd) { var textAsset = AssetDatabase.LoadAssetAtPath <TextAsset>(path); // We should have a text asset, imported by a YarnImporter Assert.IsNotNull(textAsset); Assert.IsInstanceOf <YarnImporter>(AssetImporter.GetAtPath(path)); // Make the yarn project use this script yarnProjectImporter.sourceScripts.Add(textAsset); } // Reimport the project to make it set up the links EditorUtility.SetDirty(yarnProjectImporter); yarnProjectImporter.SaveAndReimport(); foreach (var path in pathsToAdd) { var scriptImporter = AssetImporter.GetAtPath(path) as YarnImporter; Assert.AreSame(project, scriptImporter.DestinationProject); } // As a final check, make sure the project is referencing the // right number of scripts Assert.AreEqual(yarnScriptText.Length, yarnProjectImporter.sourceScripts.Count); LogAssert.ignoreFailingMessages = wasIgnoringFailingMessages; return(project); }
private void ImportYarn(AssetImportContext ctx) { var sourceText = File.ReadAllText(ctx.assetPath); string fileName = System.IO.Path.GetFileNameWithoutExtension(ctx.assetPath); try { // Compile the source code into a compiled Yarn program (or // generate a parse error) compilationStatus = Compiler.CompileString(sourceText, fileName, out var compiledProgram, out var stringTable); // Create a container for storing the bytes var programContainer = ScriptableObject.CreateInstance <YarnProgram>(); using (var memoryStream = new MemoryStream()) using (var outputStream = new Google.Protobuf.CodedOutputStream(memoryStream)) { // Serialize the compiled program to memory compiledProgram.WriteTo(outputStream); outputStream.Flush(); byte[] compiledBytes = memoryStream.ToArray(); programContainer.compiledProgram = compiledBytes; // Add this container to the imported asset; it will be // what the user interacts with in Unity ctx.AddObjectToAsset("Program", programContainer, YarnEditorUtility.GetYarnDocumentIconTexture()); ctx.SetMainObject(programContainer); isSuccesfullyCompiled = true; // var outPath = Path.ChangeExtension(ctx.assetPath, ".yarnc"); // File.WriteAllBytes(outPath, compiledBytes); } if (stringTable.Count > 0) { using (var memoryStream = new MemoryStream()) using (var textWriter = new StreamWriter(memoryStream)) { // Generate the localised .csv file // Use the invariant culture when writing the CSV var configuration = new CsvHelper.Configuration.Configuration( System.Globalization.CultureInfo.InvariantCulture ); var csv = new CsvHelper.CsvWriter( textWriter, // write into this stream configuration // use this configuration ); var lines = stringTable.Select(x => new { id = x.Key, text = x.Value.text, file = x.Value.fileName, node = x.Value.nodeName, lineNumber = x.Value.lineNumber }); csv.WriteRecords(lines); textWriter.Flush(); memoryStream.Position = 0; using (var reader = new StreamReader(memoryStream)) { var textAsset = new TextAsset(reader.ReadToEnd()); textAsset.name = $"{fileName} ({baseLanguageID})"; ctx.AddObjectToAsset("Strings", textAsset); programContainer.baseLocalisationStringTable = textAsset; baseLanguage = textAsset; programContainer.localizations = localizations; } stringIDs = lines.Select(l => l.id).ToArray(); } } } catch (Yarn.Compiler.ParseException e) { isSuccesfullyCompiled = false; compilationErrorMessage = e.Message; var message = FormatError(sourceText, compilationErrorMessage); Debug.LogError(message); return; } }
public void YarnEditorUtility_HasValidEditorResources() { // Test that YarnEditorUtility can locate the editor assets Assert.IsNotNull(YarnEditorUtility.GetYarnDocumentIconTexture()); Assert.IsNotNull(YarnEditorUtility.GetTemplateYarnScriptPath()); }
private void ImportYarn(AssetImportContext ctx) { var sourceText = File.ReadAllText(ctx.assetPath); string fileName = System.IO.Path.GetFileNameWithoutExtension(ctx.assetPath); Yarn.Program compiledProgram = null; IDictionary <string, Yarn.Compiler.StringInfo> stringTable = null; compilationErrorMessage = null; try { // Compile the source code into a compiled Yarn program (or // generate a parse error) compilationStatus = Yarn.Compiler.Compiler.CompileString(sourceText, fileName, out compiledProgram, out stringTable); isSuccesfullyCompiled = true; compilationErrorMessage = string.Empty; } catch (Yarn.Compiler.ParseException e) { isSuccesfullyCompiled = false; compilationErrorMessage = e.Message; ctx.LogImportError(e.Message); } // Create a container for storing the bytes if (programContainer == null) { programContainer = ScriptableObject.CreateInstance <YarnProgram>(); } byte[] compiledBytes = null; if (compiledProgram != null) { using (var memoryStream = new MemoryStream()) using (var outputStream = new Google.Protobuf.CodedOutputStream(memoryStream)) { // Serialize the compiled program to memory compiledProgram.WriteTo(outputStream); outputStream.Flush(); compiledBytes = memoryStream.ToArray(); } } programContainer.compiledProgram = compiledBytes; // Add this container to the imported asset; it will be // what the user interacts with in Unity ctx.AddObjectToAsset("Program", programContainer, YarnEditorUtility.GetYarnDocumentIconTexture()); ctx.SetMainObject(programContainer); if (stringTable?.Count > 0) { var lines = stringTable.Select(x => new StringTableEntry { ID = x.Key, Language = baseLanguageID, Text = x.Value.text, File = x.Value.fileName, Node = x.Value.nodeName, LineNumber = x.Value.lineNumber.ToString(), Lock = GetHashString(x.Value.text, 8), }).OrderBy(entry => int.Parse(entry.LineNumber)); var stringTableCSV = StringTableEntry.CreateCSV(lines); var textAsset = new TextAsset(stringTableCSV); textAsset.name = $"{fileName} ({baseLanguageID})"; ctx.AddObjectToAsset("Strings", textAsset); programContainer.baseLocalizationId = baseLanguageID; baseLanguage = textAsset; programContainer.localizations = localizations.Append(new YarnProgram.YarnTranslation(baseLanguageID, textAsset)).ToArray(); programContainer.baseLocalizationId = baseLanguageID; stringIDs = lines.Select(l => l.ID).ToArray(); } }