Beispiel #1
0
        /// <summary>
        /// If you're considering commenting any section of this out, try enabling the force compile in the U# settings first.
        /// This is here to prevent you from corrupting your project files.
        /// If scripts are left uncompiled from Unity's side when uploading, there is a chance to corrupt your assemblies which can cause all of your UdonBehaviours to lose their variables if handled wrong.
        /// </summary>
        /// <param name="requestedBuildType"></param>
        /// <returns></returns>
        public bool OnBuildRequested(VRCSDKRequestedBuildType requestedBuildType)
        {
            UdonSharpSettings settings = UdonSharpSettings.GetSettings();
            bool shouldForceCompile    = settings != null && settings.shouldForceCompile;

            // Unity doesn't like this and will throw errors if it ends up compiling scripts. But it seems to work.
            // This is marked experimental for now since I don't know if it will break horribly in some case.
            if (shouldForceCompile)
            {
                AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport);
            }
            else
            {
                AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);

                if (EditorApplication.isCompiling)
                {
                    //EditorUtility.DisplayDialog("Udon# build error", "Scripts are in the process of compiling, please retry build after scripts have compiled.", "OK");
                    Debug.LogError("[UdonSharp] Scripts are in the process of compiling, please retry build after scripts have compiled.");
                    typeof(SceneView).GetMethod("ShowNotification", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).Invoke(null, new object[] { "Scripts are in the process of compiling, please retry build after scripts have compiled." });
                    return(false);
                }
            }

            return(true);
        }
        /// <summary>
        /// If you're considering commenting any section of this out, try enabling the force compile in the U# settings first.
        /// This is here to prevent you from corrupting your project files.
        /// If scripts are left uncompiled from Unity's side when uploading, there is a chance to corrupt your assemblies which can cause all of your UdonBehaviours to lose their variables if handled wrong.
        /// </summary>
        /// <param name="requestedBuildType"></param>
        /// <returns></returns>
        public bool OnBuildRequested(VRCSDKRequestedBuildType requestedBuildType)
        {
            UdonSharpSettings settings = UdonSharpSettings.GetSettings();
            bool shouldForceCompile    = settings != null && settings.shouldForceCompile;

            // Unity doesn't like this and will throw errors if it ends up compiling scripts. But it seems to work.
            // This is marked experimental for now since I don't know if it will break horribly in some case.
            if (shouldForceCompile)
            {
                AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport);
            }
            else
            {
                AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);

                if (EditorApplication.isCompiling)
                {
                    Debug.LogError("[<color=#FF00FF>UdonSharp</color>] Scripts are in the process of compiling, please retry build after scripts have compiled.");
                    UdonSharpUtils.ShowEditorNotification("Scripts are in the process of compiling, please retry build after scripts have compiled.");
                    return(false);
                }
            }

            return(true);
        }
Beispiel #3
0
        private bool DrawCreateScriptButton()
        {
            if (GUILayout.Button("Create Script"))
            {
                string thisPath = AssetDatabase.GetAssetPath(this);
                //string initialPath = Path.GetDirectoryName(thisPath);
                string fileName = Path.GetFileNameWithoutExtension(thisPath).Replace(" Udon C# Program Asset", "").Replace(" ", "").Replace("#", "Sharp");

                string chosenFilePath = EditorUtility.SaveFilePanelInProject("Save UdonSharp File", fileName, "cs", "Save UdonSharp file");

                if (chosenFilePath.Length > 0)
                {
                    string fileContents = UdonSharpSettings.GetProgramTemplateString(Path.GetFileNameWithoutExtension(chosenFilePath));

                    File.WriteAllText(chosenFilePath, fileContents);

                    AssetDatabase.ImportAsset(chosenFilePath, ImportAssetOptions.ForceSynchronousImport);
                    AssetDatabase.Refresh();

                    sourceCsScript = AssetDatabase.LoadAssetAtPath <MonoScript>(chosenFilePath);

                    return(true);
                }
            }

            return(false);
        }
Beispiel #4
0
        public static string GetProgramTemplateString(string scriptName)
        {
            scriptName = scriptName.Replace(" ", "")
                         .Replace("#", "Sharp")
                         .Replace("(", "")
                         .Replace(")", "")
                         .Replace("*", "")
                         .Replace("<", "")
                         .Replace(">", "")
                         .Replace("-", "_");

            UdonSharpSettings settings = GetSettings();

            string templateStr;

            if (settings != null && settings.newScriptTemplateOverride != null)
            {
                templateStr = settings.newScriptTemplateOverride.ToString();
            }
            else
            {
                templateStr = DefaultProgramTemplate;
            }

            templateStr = templateStr.Replace("<TemplateClassName>", scriptName);

            return(templateStr);
        }
        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();
            }
        }
        static void SetupWatchers()
        {
            if (fileSystemWatchers != null)
            {
                UdonSharpSettings settings = UdonSharpSettings.GetSettings();

                bool watcherEnabled = settings == null || settings.autoCompileOnModify;

                if (watcherEnabled != lastEnabledState)
                {
                    lastEnabledState = watcherEnabled;
                    foreach (FileSystemWatcher watcher in fileSystemWatchers)
                    {
                        if (watcher != null)
                        {
                            watcher.EnableRaisingEvents = watcherEnabled;
                        }
                    }
                }

                return;
            }

            AssemblyReloadEvents.beforeAssemblyReload += CleanupWatchers;

            string[] blacklistedDirectories = UdonSharpSettings.GetScannerBlacklist();

            string[] directories = Directory.GetDirectories("Assets/", "*", SearchOption.AllDirectories).Append("Assets/")
                                   .Select(e => e.Replace('\\', '/'))
                                   .Where(e => !blacklistedDirectories.Any(name => name.TrimEnd('/') == e.TrimEnd('/') || e.StartsWith(name)))
                                   .ToArray();

            List <string> sourceDirectories = new List <string>();

            foreach (string directory in directories)
            {
                if (Directory.GetFiles(directory, "*.cs").Length > 0)
                {
                    sourceDirectories.Add(directory);
                }
            }

            fileSystemWatchers = new FileSystemWatcher[sourceDirectories.Count];

            for (int i = 0; i < sourceDirectories.Count; ++i)
            {
                FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(sourceDirectories[i], "*.cs");
                fileSystemWatcher.IncludeSubdirectories = false;
                fileSystemWatcher.InternalBufferSize    = 1024; // Someone would need to modify 64 files in a single directory at once to hit this

                fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
                fileSystemWatcher.Changed     += OnSourceFileChanged;

                fileSystemWatchers[i] = fileSystemWatcher;
            }
        }
Beispiel #7
0
        public static SettingsProvider CreateSettingsProvider()
        {
            SettingsProvider provider = new SettingsProvider("Project/Udon Sharp", SettingsScope.Project)
            {
                label      = "Udon Sharp",
                keywords   = new HashSet <string>(new string[] { "Udon", "Sharp", "U#", "VRC", "VRChat" }),
                guiHandler = (searchContext) =>
                {
                    UdonSharpSettings settings       = UdonSharpSettings.GetOrCreateSettings();
                    SerializedObject  settingsObject = UdonSharpSettings.GetSerializedSettings();

                    EditorGUI.BeginChangeCheck();
                    EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.autoCompileOnModify)), autoCompileLabel);

                    if (settings.autoCompileOnModify)
                    {
                        EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.compileAllScripts)), compileAllLabel);
                        if (!settings.compileAllScripts)
                        {
                            EditorGUILayout.HelpBox("Only compiling the script that has been modified can cause issues if you have multiple scripts communicating via methods.", MessageType.Warning);
                        }
                    }

                    EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.waitForFocus)), waitForFocusLabel);

                    EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.newScriptTemplateOverride)), templateOverrideLabel);

                    EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.scanningDirectoryBlacklist)), scanningBlackListLabel, true);

                    EditorGUILayout.Space();
                    EditorGUILayout.LabelField("Debugging", EditorStyles.boldLabel);

                    EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.buildDebugInfo)), includeDebugInfoLabel);

                    if (settings.buildDebugInfo)
                    {
                        EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.includeInlineCode)), includeInlineCodeLabel);
                        EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.listenForVRCExceptions)), listenForVRCExceptionsLabel);
                    }

                    EditorGUILayout.Space();
                    EditorGUILayout.LabelField("Experimental", EditorStyles.boldLabel);

                    EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.shouldForceCompile)), forceCompileLabel);

                    if (EditorGUI.EndChangeCheck())
                    {
                        settingsObject.ApplyModifiedProperties();
                        EditorUtility.SetDirty(UdonSharpSettings.GetSettings());
                    }
                },
            };

            return(provider);
        }
        public static string GetProgramTemplateString()
        {
            UdonSharpSettings settings = GetSettings();

            if (settings != null && settings.newScriptTemplateOverride != null)
            {
                return(settings.newScriptTemplateOverride.ToString());
            }

            return(DefaultProgramTemplate);
        }
Beispiel #9
0
        public static string[] GetScannerBlacklist()
        {
            UdonSharpSettings settings = GetSettings();

            if (settings != null)
            {
                return(BuiltinScanningBlacklist.Concat(settings.scanningDirectoryBlacklist).ToArray());
            }

            return(BuiltinScanningBlacklist);
        }
Beispiel #10
0
        internal static UdonSharpSettings GetOrCreateSettings()
        {
            UdonSharpSettings settings = AssetDatabase.LoadAssetAtPath <UdonSharpSettings>(SettingsSavePath);

            if (settings == null)
            {
                settings = ScriptableObject.CreateInstance <UdonSharpSettings>();
                AssetDatabase.CreateAsset(settings, SettingsSavePath);
                AssetDatabase.SaveAssets();
            }

            return(settings);
        }
Beispiel #11
0
        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();
            }
        }
        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 OnEditorUpdate()
        {
            SetupWatchers();

            UdonSharpSettings settings = UdonSharpSettings.GetSettings();

            bool watcherEnabled = settings == null || settings.autoCompileOnModify;

            if (watcherEnabled != lastEnabledState && fileSystemWatchers != null)
            {
                lastEnabledState = watcherEnabled;
                foreach (FileSystemWatcher watcher in fileSystemWatchers)
                {
                    if (watcher != null)
                    {
                        watcher.EnableRaisingEvents = watcherEnabled;
                    }
                }
            }

            List <MonoScript> modifiedScripts = null;

            lock (modifiedFileLock)
            {
                if (modifiedFilePaths.Count > 0)
                {
                    modifiedScripts = new List <MonoScript>();

                    foreach (string filePath in modifiedFilePaths)
                    {
                        MonoScript asset = AssetDatabase.LoadAssetAtPath <MonoScript>(filePath.Replace(Application.dataPath.Replace("/", "\\"), "Assets"));
                        modifiedScripts.Add(asset);
                    }

                    modifiedFilePaths.Clear();
                }
            }

            if (modifiedScripts != null && modifiedScripts.Count > 0)
            {
                HandleScriptModifications(modifiedScripts);
            }
        }
Beispiel #15
0
        public static string GetProgramTemplateString(string scriptName)
        {
            scriptName = SanitizeName(scriptName);

            UdonSharpSettings settings = GetSettings();

            string templateStr;

            if (settings != null && settings.newScriptTemplateOverride != null)
            {
                templateStr = settings.newScriptTemplateOverride.ToString();
            }
            else
            {
                templateStr = DefaultProgramTemplate;
            }

            templateStr = templateStr.Replace("<TemplateClassName>", scriptName);

            return(templateStr);
        }
        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);
        }
Beispiel #17
0
        public static UdonSharpSettings GetSettings()
        {
            UdonSharpSettings settings = AssetDatabase.LoadAssetAtPath <UdonSharpSettings>(SettingsSavePath);

            return(settings);
        }
        public int Compile(List <ClassDefinition> classDefinitions)
        {
            if (programAsset.sourceCsScript == null)
            {
                throw new System.ArgumentException($"Asset '{AssetDatabase.GetAssetPath(programAsset)}' does not have a valid program source to compile from");
            }

            Profiler.BeginSample("Compile Module");

            programAsset.compileErrors.Clear();

            sourceCode = File.ReadAllText(AssetDatabase.GetAssetPath(programAsset.sourceCsScript));

            Profiler.BeginSample("Parse AST");
            SyntaxTree tree = CSharpSyntaxTree.ParseText(sourceCode);

            Profiler.EndSample();

            int errorCount = 0;

            string errorString = "";

            foreach (Diagnostic diagnostic in tree.GetDiagnostics())
            {
                if (diagnostic.Severity == DiagnosticSeverity.Error)
                {
                    errorCount++;

                    LinePosition linePosition = diagnostic.Location.GetLineSpan().StartLinePosition;

                    errorString = UdonSharpUtils.LogBuildError($"error {diagnostic.Descriptor.Id}: {diagnostic.GetMessage()}",
                                                               AssetDatabase.GetAssetPath(programAsset.sourceCsScript).Replace("/", "\\"),
                                                               linePosition.Line,
                                                               linePosition.Character);

                    programAsset.compileErrors.Add(errorString);
                }
            }

            if (errorCount > 0)
            {
                ErrorCount = errorCount;
                Profiler.EndSample();
                return(errorCount);
            }

            Profiler.BeginSample("Visit");
            UdonSharpFieldVisitor fieldVisitor = new UdonSharpFieldVisitor(fieldsWithInitializers);

            fieldVisitor.Visit(tree.GetRoot());

            MethodVisitor methodVisitor = new MethodVisitor(resolver, moduleSymbols, moduleLabels);

            methodVisitor.Visit(tree.GetRoot());

            UdonSharpSettings settings = UdonSharpSettings.GetSettings();

            ClassDebugInfo debugInfo = null;

            if (settings == null || settings.buildDebugInfo)
            {
                debugInfo = new ClassDebugInfo(sourceCode, settings == null || settings.includeInlineCode);
            }

            ASTVisitor visitor = new ASTVisitor(resolver, moduleSymbols, moduleLabels, methodVisitor.definedMethods, classDefinitions, debugInfo);

            try
            {
                visitor.Visit(tree.GetRoot());
                visitor.VerifyIntegrity();
            }
            catch (System.Exception e)
            {
                SyntaxNode currentNode = visitor.visitorContext.currentNode;

                string logMessage = "";

                if (currentNode != null)
                {
                    FileLinePositionSpan lineSpan = currentNode.GetLocation().GetLineSpan();

                    logMessage = UdonSharpUtils.LogBuildError($"{e.GetType()}: {e.Message}",
                                                              AssetDatabase.GetAssetPath(programAsset.sourceCsScript).Replace("/", "\\"),
                                                              lineSpan.StartLinePosition.Line,
                                                              lineSpan.StartLinePosition.Character);
                }
                else
                {
                    logMessage = e.ToString();
                    Debug.LogException(e);
                }

                programAsset.compileErrors.Add(logMessage);

                errorCount++;
            }
            Profiler.EndSample();

            if (errorCount == 0)
            {
                Profiler.BeginSample("Build assembly");
                string dataBlock = BuildHeapDataBlock();
                string codeBlock = visitor.GetCompiledUasm();

                programAsset.SetUdonAssembly(dataBlock + codeBlock);
                Profiler.EndSample();

                Profiler.BeginSample("Assemble Program");
                programAsset.AssembleCsProgram((uint)(moduleSymbols.GetAllUniqueChildSymbols().Count + visitor.GetExternStrCount()));
                Profiler.EndSample();
                programAsset.behaviourIDHeapVarName = visitor.GetIDHeapVarName();

                programAsset.fieldDefinitions = visitor.visitorContext.localFieldDefinitions;

                if (debugInfo != null)
                {
                    debugInfo.FinalizeDebugInfo();
                }

                programAsset.debugInfo = debugInfo;
            }

            Profiler.EndSample();

            return(errorCount);
        }
Beispiel #19
0
        public static SettingsProvider CreateSettingsProvider()
        {
            SettingsProvider provider = new SettingsProvider("Project/Udon Sharp", SettingsScope.Project)
            {
                label      = "Udon Sharp",
                keywords   = new HashSet <string>(new string[] { "Udon", "Sharp", "U#", "VRC", "VRChat" }),
                guiHandler = (searchContext) =>
                {
                    UdonSharpSettings settings       = UdonSharpSettings.GetOrCreateSettings();
                    SerializedObject  settingsObject = UdonSharpSettings.GetSerializedSettings();

                    // Compiler settings
                    EditorGUILayout.LabelField("Compiler", EditorStyles.boldLabel);

                    EditorGUI.BeginChangeCheck();
                    EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.autoCompileOnModify)), autoCompileLabel);

                    if (settings.autoCompileOnModify)
                    {
                        EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.compileAllScripts)), compileAllLabel);
                        if (!settings.compileAllScripts)
                        {
                            EditorGUILayout.HelpBox("Only compiling the script that has been modified can cause issues if you have multiple scripts communicating via methods.", MessageType.Warning);
                        }
                    }

                    EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.waitForFocus)), waitForFocusLabel);

                    EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.disableUploadCompile)), disableUploadCompileLabel);

                    if (settings.disableUploadCompile)
                    {
                        EditorGUILayout.HelpBox(@"Do not disable this setting unless it is not viable to wait for the compile on upload process. 
Disabling this setting will make the UNITY_EDITOR define not work as expected and will break prefabs that depend on the define being accurate between game and editor builds.", MessageType.Warning);
                    }

                    EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.newScriptTemplateOverride)), templateOverrideLabel);

                    EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.scanningDirectoryBlacklist)), scanningBlackListLabel, true);

                    EditorGUILayout.Space();

                    // Interface settings
                    EditorGUILayout.LabelField("Interface", EditorStyles.boldLabel);

                    SerializedProperty defaultDrawerProperty = settingsObject.FindProperty(nameof(UdonSharpSettings.defaultBehaviourInterfaceType));

                    defaultDrawerProperty.stringValue = DrawCustomEditorSelection(defaultDrawerProperty.stringValue);

                    EditorGUILayout.Space();

                    // Debugging settings
                    EditorGUILayout.LabelField("Debugging", EditorStyles.boldLabel);

                    EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.buildDebugInfo)), includeDebugInfoLabel);

                    if (settings.buildDebugInfo)
                    {
                        EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.includeInlineCode)), includeInlineCodeLabel);
                        EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.listenForVRCExceptions)), listenForVRCExceptionsLabel);
                    }

                    EditorGUILayout.Space();
                    SerializedProperty watcherModeProperty = settingsObject.FindProperty(nameof(UdonSharpSettings.watcherMode));
                    EditorGUILayout.PropertyField(watcherModeProperty, outputLogWatcherModeLabel);

                    if (watcherModeProperty.enumValueIndex == (int)UdonSharpSettings.LogWatcherMode.Prefix)
                    {
                        EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.logWatcherMatchStrings)), prefixArrayLabel, true);
                    }

                    EditorGUILayout.Space();

                    // Experimental settings
                    EditorGUILayout.LabelField("Experimental", EditorStyles.boldLabel);

                    EditorGUILayout.PropertyField(settingsObject.FindProperty(nameof(UdonSharpSettings.shouldForceCompile)), forceCompileLabel);

                    if (EditorGUI.EndChangeCheck())
                    {
                        settingsObject.ApplyModifiedProperties();
                        EditorUtility.SetDirty(UdonSharpSettings.GetSettings());
                    }
                },
            };

            return(provider);
        }
        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();
        }