/// <summary> /// Deletes an UdonSharp program asset and the serialized program asset associated with it /// </summary> /// <param name="programAsset"></param> public static void DeleteProgramAsset(UdonSharpProgramAsset programAsset) { if (programAsset == null) { return; } AbstractSerializedUdonProgramAsset serializedAsset = programAsset.GetSerializedUdonProgramAsset(); if (serializedAsset != null) { string assetPath = AssetDatabase.GetAssetPath(serializedAsset); serializedAsset = AssetDatabase.LoadAssetAtPath <AbstractSerializedUdonProgramAsset>(assetPath); if (serializedAsset != null) { AssetDatabase.DeleteAsset(assetPath); } } string programAssetPath = AssetDatabase.GetAssetPath(programAsset); programAsset = AssetDatabase.LoadAssetAtPath <UdonSharpProgramAsset>(programAssetPath); if (programAsset != null) { AssetDatabase.DeleteAsset(programAssetPath); } }
public bool IsSourceFileDirty(UdonSharpProgramAsset programAsset) { if (programAsset?.sourceCsScript == null) { return(false); } if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(programAsset, out string programAssetGuid, out long _)) { return(false); } // We haven't seen the source file before, so it needs to be compiled if (!sourceFileHashLookup.TryGetValue(programAssetGuid, out string sourceFileHash)) { return(true); } string currentHash = HashSourceFile(programAsset.sourceCsScript); if (currentHash != sourceFileHash) { return(true); } return(false); }
/// <summary> /// Creates a new UdonAssemblyProgramAsset from an UdonSharpProgramAsset for the sake of portability. Most info used for the inspector gets stripped so this isn't a great solution for remotely complex assets. /// </summary> /// <param name="udonSharpProgramAsset">The source program asset</param> /// <param name="savePath">The save path for the asset file. Save path is only needed here because Udon needs a GUID for saving the serialized program asset and it'd be a pain to break that requirement at the moment</param> /// <returns>The exported UdonAssemblyProgramAsset</returns> public static UdonAssemblyProgramAsset UdonSharpProgramToAssemblyProgram(UdonSharpProgramAsset udonSharpProgramAsset, string savePath) { if (EditorApplication.isPlaying) { throw new System.NotSupportedException("UdonSharpEditorUtility.UdonSharpProgramToAssemblyProgram() cannot be called in play mode"); } UdonAssemblyProgramAsset newProgramAsset = ScriptableObject.CreateInstance <UdonAssemblyProgramAsset>(); AssetDatabase.CreateAsset(newProgramAsset, savePath); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); newProgramAsset = AssetDatabase.LoadAssetAtPath <UdonAssemblyProgramAsset>(savePath); FieldInfo assemblyField = typeof(UdonAssemblyProgramAsset).GetField("udonAssembly", BindingFlags.NonPublic | BindingFlags.Instance); udonSharpProgramAsset.CompileCsProgram(); assemblyField.SetValue(newProgramAsset, assemblyField.GetValue(udonSharpProgramAsset)); MethodInfo assembleMethod = typeof(UdonAssemblyProgramAsset).GetMethod("AssembleProgram", BindingFlags.NonPublic | BindingFlags.Instance); assembleMethod.Invoke(newProgramAsset, new object[] { }); IUdonProgram uSharpProgram = udonSharpProgramAsset.GetRealProgram(); FieldInfo assemblyProgramGetter = typeof(UdonProgramAsset).GetField("program", BindingFlags.NonPublic | BindingFlags.Instance); IUdonProgram assemblyProgram = (IUdonProgram)assemblyProgramGetter.GetValue(newProgramAsset); if (uSharpProgram == null || assemblyProgram == null) { return(null); } string[] symbols = uSharpProgram.SymbolTable.GetSymbols(); foreach (string symbol in symbols) { uint symbolAddress = uSharpProgram.SymbolTable.GetAddressFromSymbol(symbol); System.Type symbolType = uSharpProgram.Heap.GetHeapVariableType(symbolAddress); object symbolValue = uSharpProgram.Heap.GetHeapVariable(symbolAddress); assemblyProgram.Heap.SetHeapVariable(assemblyProgram.SymbolTable.GetAddressFromSymbol(symbol), symbolValue, symbolType); } EditorUtility.SetDirty(newProgramAsset); newProgramAsset.SerializedProgramAsset.StoreProgram(assemblyProgram); EditorUtility.SetDirty(newProgramAsset.SerializedProgramAsset); AssetDatabase.SaveAssets(); // This doesn't work unfortunately due to how Udon tries to locate the serialized asset when importing an assembly //string serializedAssetPath = $"{Path.GetDirectoryName(savePath)}/{Path.GetFileNameWithoutExtension(savePath)}_serialized.asset"; //AssetDatabase.MoveAsset(AssetDatabase.GetAssetPath(newProgramAsset.SerializedProgramAsset), serializedAssetPath); //AssetDatabase.SaveAssets(); return(newProgramAsset); }
public void UpdateSourceHash(UdonSharpProgramAsset programAsset, string sourceText) { if (programAsset?.sourceCsScript == null) { return; } if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(programAsset, out string programAssetGuid, out long _)) { return; } string newHash = UdonSharpUtils.HashString(sourceText); if (sourceFileHashLookup.ContainsKey(programAssetGuid)) { if (sourceFileHashLookup[programAssetGuid] != newHash) { _sourceDirty = true; } sourceFileHashLookup[programAssetGuid] = newHash; } else { sourceFileHashLookup.Add(programAssetGuid, newHash); _sourceDirty = true; } }
/// <summary> /// Clears the source hash, this is used when a script hits a compile error in order to allow an undo to compile the scripts. /// </summary> /// <param name="programAsset"></param> public void ClearSourceHash(UdonSharpProgramAsset programAsset) { if (programAsset?.sourceCsScript == null) { return; } if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(programAsset, out string programAssetGuid, out long _)) { return; } if (sourceFileHashLookup.ContainsKey(programAssetGuid)) { if (sourceFileHashLookup[programAssetGuid] != "") { _sourceDirty = true; } sourceFileHashLookup[programAssetGuid] = ""; } else { sourceFileHashLookup.Add(programAssetGuid, ""); _sourceDirty = true; } }
public static AssetDeleteResult OnWillDeleteAsset(string assetPath, RemoveAssetOptions options) { UdonSharpProgramAsset programAsset = AssetDatabase.LoadAssetAtPath <UdonSharpProgramAsset>(assetPath); if (programAsset) { Instance.ClearSourceHash(programAsset); } else if (AssetDatabase.IsValidFolder(assetPath)) { string[] assetGuids = AssetDatabase.FindAssets($"t:{nameof(UdonSharpProgramAsset)}", new string[] { assetPath }); foreach (string guid in assetGuids) { programAsset = AssetDatabase.LoadAssetAtPath <UdonSharpProgramAsset>(AssetDatabase.GUIDToAssetPath(guid)); if (programAsset) { Instance.ClearSourceHash(programAsset); } } } return(AssetDeleteResult.DidNotDelete); }
static void PlayModeErrorCheck(PlayModeStateChange state) { // Prevent people from entering play mode when there are compile errors, like normal Unity C# if (state == PlayModeStateChange.EnteredPlayMode || state == PlayModeStateChange.ExitingEditMode) { string[] udonSharpDataAssets = AssetDatabase.FindAssets($"t:{typeof(UdonSharpProgramAsset).Name}"); bool foundCompileErrors = false; foreach (string dataGuid in udonSharpDataAssets) { UdonSharpProgramAsset programAsset = AssetDatabase.LoadAssetAtPath <UdonSharpProgramAsset>(AssetDatabase.GUIDToAssetPath(dataGuid)); if (programAsset.sourceCsScript != null && programAsset.compileErrors.Count > 0) { foundCompileErrors = true; break; } } if (foundCompileErrors) { EditorApplication.isPlaying = false; typeof(SceneView).GetMethod("ShowNotification", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).Invoke(null, new object[] { "All U# compile errors have to be fixed before you can enter playmode!" }); } } }
static void PlayModeErrorCheck(PlayModeStateChange state) { // Prevent people from entering play mode when there are compile errors, like normal Unity C# // READ ME // -------- // If you think you know better and are about to edit this out, be aware that you gain nothing by doing so. // If a script hits a compile error, it will not update until the compile errors are resolved. // You will just be left wondering "why aren't my scripts changing when I edit them?" since the old copy of the script will be used until the compile errors are resolved. // -------- if (state == PlayModeStateChange.EnteredPlayMode || state == PlayModeStateChange.ExitingEditMode) { string[] udonSharpDataAssets = AssetDatabase.FindAssets($"t:{typeof(UdonSharpProgramAsset).Name}"); bool foundCompileErrors = false; foreach (string dataGuid in udonSharpDataAssets) { UdonSharpProgramAsset programAsset = AssetDatabase.LoadAssetAtPath <UdonSharpProgramAsset>(AssetDatabase.GUIDToAssetPath(dataGuid)); if (programAsset.sourceCsScript != null && programAsset.compileErrors.Count > 0) { foundCompileErrors = true; break; } } if (foundCompileErrors) { EditorApplication.isPlaying = false; typeof(SceneView).GetMethod("ShowNotification", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).Invoke(null, new object[] { "All U# compile errors have to be fixed before you can enter playmode!" }); } } }
public CompilationModule(UdonSharpProgramAsset sourceAsset) { programAsset = sourceAsset; resolver = new ResolverContext(); moduleSymbols = new SymbolTable(resolver, null); moduleLabels = new LabelTable(); fieldsWithInitializers = new HashSet <FieldDeclarationSyntax>(); }
static void HandleScriptModifications() { UdonSharpSettings settings = UdonSharpSettings.GetSettings(); if (settings != null) { if (!settings.autoCompileOnModify) { modifiedScripts.Clear(); return; } if (settings.waitForFocus && !UnityEditorInternal.InternalEditorUtility.isApplicationActive) { return; } } if (modifiedScripts.Count == 0) { return; } UdonSharpProgramAsset[] udonSharpPrograms = UdonSharpProgramAsset.GetAllUdonSharpPrograms(); HashSet <UdonSharpProgramAsset> assetsToUpdate = new HashSet <UdonSharpProgramAsset>(); foreach (MonoScript script in modifiedScripts) { foreach (UdonSharpProgramAsset programAsset in udonSharpPrograms) { if (programAsset.sourceCsScript == script) { assetsToUpdate.Add(programAsset); } } } try { if (assetsToUpdate.Count > 0) { if (settings == null || settings.compileAllScripts) { UdonSharpProgramAsset.CompileAllCsPrograms(); } else { UdonSharpCompiler compiler = new UdonSharpCompiler(assetsToUpdate.ToArray()); compiler.Compile(); } } } finally { modifiedScripts.Clear(); } }
public override void OnInspectorGUI() { EditorGUILayout.HelpBox("Udon Sharp Behaviours need to be converted to Udon Behaviours to work in game. Click the convert button below to automatically convert the script.", MessageType.Warning); if (GUILayout.Button("Convert to UdonBehaviour", GUILayout.Height(25))) { Undo.RegisterCompleteObjectUndo(targets.Select(e => (e as MonoBehaviour)).Distinct().ToArray(), "Convert to UdonBehaviour"); foreach (Object targetObject in targets.Distinct()) { MonoScript behaviourScript = MonoScript.FromMonoBehaviour(targetObject as MonoBehaviour); UdonSharpProgramAsset programAsset = GetUdonSharpProgram(behaviourScript); if (programAsset == null) { string scriptPath = AssetDatabase.GetAssetPath(behaviourScript); string scriptDirectory = Path.GetDirectoryName(scriptPath); string scriptFileName = Path.GetFileNameWithoutExtension(scriptPath); string assetPath = Path.Combine(scriptDirectory, $"{scriptFileName}.asset").Replace('\\', '/'); if (AssetDatabase.LoadAssetAtPath <UdonSharpProgramAsset>(assetPath) != null) { if (!EditorUtility.DisplayDialog("Existing file found", $"Asset file {assetPath} already exists, do you want to overwrite it?", "Ok", "Cancel")) { continue; } } programAsset = ScriptableObject.CreateInstance <UdonSharpProgramAsset>(); programAsset.sourceCsScript = behaviourScript; programAsset.CompileCsProgram(); AssetDatabase.CreateAsset(programAsset, assetPath); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); } GameObject targetGameObject = (targetObject as MonoBehaviour).gameObject; Undo.DestroyObjectImmediate(targetObject); UdonBehaviour udonBehaviour = targetGameObject.AddComponent <UdonBehaviour>(); udonBehaviour.programSource = programAsset; Undo.RegisterCreatedObjectUndo(udonBehaviour, "Convert to UdonBehaviour"); } return; } EditorGUILayout.Space(); base.OnInspectorGUI(); }
protected override void RefreshProgramImpl() { if (sourceCsScript != null && !EditorApplication.isCompiling && !EditorApplication.isUpdating && !UdonSharpProgramAsset.AnyUdonSharpScriptHasError()) { CompileAllCsPrograms(true); } }
private static void CreateUSharpScript() { string folderPath = "Assets/"; if (Selection.activeObject != null) { folderPath = AssetDatabase.GetAssetPath(Selection.activeObject); if (Selection.activeObject.GetType() != typeof(UnityEditor.DefaultAsset)) { folderPath = Path.GetDirectoryName(folderPath); } } else if (Selection.assetGUIDs.Length > 0) { folderPath = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]); } folderPath = folderPath.Replace('\\', '/'); string chosenFilePath = EditorUtility.SaveFilePanelInProject("Save UdonSharp File", "", "cs", "Save UdonSharp file", folderPath); if (chosenFilePath.Length > 0) { string chosenFileName = Path.GetFileNameWithoutExtension(chosenFilePath).Replace(" ", "").Replace("#", "Sharp"); string assetFilePath = Path.Combine(Path.GetDirectoryName(chosenFilePath), $"{chosenFileName}.asset"); if (AssetDatabase.LoadAssetAtPath <UdonSharpProgramAsset>(assetFilePath) != null) { if (!EditorUtility.DisplayDialog("File already exists", $"Corresponding asset file '{assetFilePath}' already found for new UdonSharp script. Overwrite?", "Ok", "Cancel")) { return; } } string fileContents = UdonSharpSettings.GetProgramTemplateString(chosenFileName); File.WriteAllText(chosenFilePath, fileContents); AssetDatabase.ImportAsset(chosenFilePath, ImportAssetOptions.ForceSynchronousImport); MonoScript newScript = AssetDatabase.LoadAssetAtPath <MonoScript>(chosenFilePath); UdonSharpProgramAsset newProgramAsset = ScriptableObject.CreateInstance <UdonSharpProgramAsset>(); newProgramAsset.sourceCsScript = newScript; AssetDatabase.CreateAsset(newProgramAsset, assetFilePath); AssetDatabase.Refresh(); } }
private static UdonSharpProgramAsset GetUdonSharpProgram(MonoScript programScript) { string[] udonSharpDataAssets = AssetDatabase.FindAssets($"t:{typeof(UdonSharpProgramAsset).Name}"); foreach (string dataGuid in udonSharpDataAssets) { UdonSharpProgramAsset programAsset = AssetDatabase.LoadAssetAtPath <UdonSharpProgramAsset>(AssetDatabase.GUIDToAssetPath(dataGuid)); if (programAsset && programAsset.sourceCsScript == programScript) { return(programAsset); } } return(null); }
static void OnEditorUpdate() { SetupWatchers(); // Prevent people from entering play mode when there are compile errors, like normal Unity C# if (EditorApplication.isPlayingOrWillChangePlaymode && !EditorApplication.isPlaying) { string[] udonSharpDataAssets = AssetDatabase.FindAssets($"t:{typeof(UdonSharpProgramAsset).Name}"); bool foundCompileErrors = false; foreach (string dataGuid in udonSharpDataAssets) { UdonSharpProgramAsset programAsset = AssetDatabase.LoadAssetAtPath <UdonSharpProgramAsset>(AssetDatabase.GUIDToAssetPath(dataGuid)); if (programAsset.sourceCsScript != null && programAsset.compileErrors.Count > 0) { foundCompileErrors = true; break; } } if (foundCompileErrors) { EditorApplication.isPlaying = false; typeof(SceneView).GetMethod("ShowNotification", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).Invoke(null, new object[] { "All U# compile errors have to be fixed before you can enter playmode!" }); } } lock (modifiedFileLock) { if (modifiedFilePaths.Count > 0) { foreach (string filePath in modifiedFilePaths) { MonoScript asset = AssetDatabase.LoadAssetAtPath <MonoScript>(filePath.Replace(Application.dataPath.Replace("/", "\\"), "Assets")); modifiedScripts.Add(asset); } modifiedFilePaths.Clear(); } } HandleScriptModifications(); }
public CompilationModule(UdonSharpProgramAsset sourceAsset) { programAsset = sourceAsset; resolver = new ResolverContext(); moduleSymbols = new SymbolTable(resolver, null); moduleLabels = new LabelTable(); fieldsWithInitializers = new HashSet <FieldDeclarationSyntax>(); if (programAsset.sourceCsScript == null) { throw new System.ArgumentException($"Asset '{AssetDatabase.GetAssetPath(programAsset)}' does not have a valid program source to compile from"); } sourceCode = UdonSharpUtils.ReadFileTextSync(AssetDatabase.GetAssetPath(programAsset.sourceCsScript)); settings = UdonSharpSettings.GetSettings(); }
static void HandleScriptModifications(List <MonoScript> scripts) { UdonSharpSettings settings = UdonSharpSettings.GetSettings(); if (settings != null && !settings.autoCompileOnModify) { return; } string[] udonSharpDataAssets = AssetDatabase.FindAssets($"t:{typeof(UdonSharpProgramAsset).Name}"); List <UdonSharpProgramAsset> udonSharpPrograms = new List <UdonSharpProgramAsset>(); foreach (string dataGuid in udonSharpDataAssets) { udonSharpPrograms.Add(AssetDatabase.LoadAssetAtPath <UdonSharpProgramAsset>(AssetDatabase.GUIDToAssetPath(dataGuid))); } HashSet <UdonSharpProgramAsset> assetsToUpdate = new HashSet <UdonSharpProgramAsset>(); foreach (MonoScript script in scripts) { foreach (UdonSharpProgramAsset programAsset in udonSharpPrograms) { if (programAsset.sourceCsScript == script) { assetsToUpdate.Add(programAsset); } } } if (assetsToUpdate.Count > 0) { if (settings == null || settings.compileAllScripts) { UdonSharpProgramAsset.CompileAllCsPrograms(); } else { UdonSharpCompiler compiler = new UdonSharpCompiler(assetsToUpdate.ToArray()); compiler.Compile(); } } }
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { bool importedUdonSharpAsset = false; foreach (string importedAssetPath in importedAssets) { UdonSharpProgramAsset importedAsset = AssetDatabase.LoadAssetAtPath <UdonSharpProgramAsset>(importedAssetPath); if (importedAsset != null) { importedUdonSharpAsset = true; break; } } UdonSharpProgramAsset.ClearProgramAssetCache(); if (importedUdonSharpAsset) { UdonSharpEditorManager.QueueScriptCompile(); } }
public bool OnBuildRequested(VRCSDKRequestedBuildType requestedBuildType) { if (requestedBuildType == VRCSDKRequestedBuildType.Avatar) { return(true); } if (UdonSharpSettings.GetSettings()?.disableUploadCompile ?? false) { return(true); } UdonSharpProgramAsset.CompileAllCsPrograms(true, false); UdonSharpEditorCache.SaveAllCache(); if (UdonSharpProgramAsset.AnyUdonSharpScriptHasError()) { Debug.LogError("[<color=#FF00FF>UdonSharp</color>] Failed to compile UdonSharp scripts for build, check error log for details."); UdonSharpUtils.ShowEditorNotification("Failed to compile UdonSharp scripts for build, check error log for details."); return(false); } return(true); }
static void HandleScriptModifications() { UdonSharpSettings settings = UdonSharpSettings.GetSettings(); if (settings != null) { if (!settings.autoCompileOnModify) { modifiedScripts.Clear(); return; } if (settings.waitForFocus && !UnityEditorInternal.InternalEditorUtility.isApplicationActive) { return; } } if (modifiedScripts.Count == 0) { return; } string[] udonSharpDataAssets = AssetDatabase.FindAssets($"t:{typeof(UdonSharpProgramAsset).Name}"); List <UdonSharpProgramAsset> udonSharpPrograms = new List <UdonSharpProgramAsset>(); foreach (string dataGuid in udonSharpDataAssets) { udonSharpPrograms.Add(AssetDatabase.LoadAssetAtPath <UdonSharpProgramAsset>(AssetDatabase.GUIDToAssetPath(dataGuid))); } HashSet <UdonSharpProgramAsset> assetsToUpdate = new HashSet <UdonSharpProgramAsset>(); foreach (MonoScript script in modifiedScripts) { foreach (UdonSharpProgramAsset programAsset in udonSharpPrograms) { if (programAsset.sourceCsScript == script) { assetsToUpdate.Add(programAsset); } } } try { if (assetsToUpdate.Count > 0) { if (settings == null || settings.compileAllScripts) { UdonSharpProgramAsset.CompileAllCsPrograms(); } else { UdonSharpCompiler compiler = new UdonSharpCompiler(assetsToUpdate.ToArray()); compiler.Compile(); } } } finally { modifiedScripts.Clear(); } modifiedScripts.Clear(); }
public UdonSharpCompiler(UdonSharpProgramAsset programAsset) { modules = new CompilationModule[] { new CompilationModule(programAsset) }; }