private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
        {
            foreach (string importedAssetPath in importedAssets)
            {
                UdonSharpProgramAsset importedAsset = AssetDatabase.LoadAssetAtPath <UdonSharpProgramAsset>(importedAssetPath);

                if (importedAsset && (importedAsset.CompiledVersion < UdonSharpProgramVersion.CurrentVersion || importedAsset.ScriptVersion < UdonSharpProgramVersion.CurrentVersion))
                {
                    UdonSharpUpgrader.QueueUpgrade(importedAsset);
                    UdonSharpEditorCache.Instance.QueueUpgradePass();
                }

                if (importedAsset)
                {
                    UdonSharpProgramAsset.ClearProgramAssetCache();
                }
            }
        }
        internal static UdonBehaviour[] ConvertToUdonBehavioursInternal(UdonSharpBehaviour[] components, bool shouldUndo, bool showPrompts, bool convertChildren)
        {
            components = components.Distinct().ToArray();

            if (showPrompts)
            {
                HashSet <UdonSharpBehaviour> allReferencedBehaviours = new HashSet <UdonSharpBehaviour>();

                // Check if any of these need child component conversion
                foreach (UdonSharpBehaviour targetObject in components)
                {
                    HashSet <UdonSharpBehaviour> referencedBehaviours = new HashSet <UdonSharpBehaviour>();

                    CollectUdonSharpBehaviourReferencesInternal(targetObject, referencedBehaviours);

                    if (referencedBehaviours.Count > 1)
                    {
                        foreach (UdonSharpBehaviour referencedBehaviour in referencedBehaviours)
                        {
                            if (referencedBehaviour != targetObject)
                            {
                                allReferencedBehaviours.Add(referencedBehaviour);
                            }
                        }
                    }
                }

                if (allReferencedBehaviours.Count > 0)
                {
                    // This is an absolute mess, it should probably just be simplified to counting the number of affected behaviours
                    string referencedBehaviourStr;
                    if (allReferencedBehaviours.Count <= 2)
                    {
                        referencedBehaviourStr = string.Join(", ", allReferencedBehaviours.Select(e => $"'{e.ToString()}'"));
                    }
                    else
                    {
                        referencedBehaviourStr = $"{allReferencedBehaviours.Count} behaviours";
                    }

                    string rootBehaviourStr;

                    if (components.Length <= 2)
                    {
                        rootBehaviourStr = $"{string.Join(", ", components.Select(e => $"'{e.ToString()}'"))} reference{(components.Length == 1 ? "s" : "")} ";
                    }
                    else
                    {
                        rootBehaviourStr = $"{components.Length} behaviours to convert reference ";
                    }

                    string messageStr = $"{rootBehaviourStr}{referencedBehaviourStr}. Do you want to convert all referenced behaviours as well? If no, references to these behaviours will be set to null.";

                    int result = EditorUtility.DisplayDialogComplex("Dependent behaviours found", messageStr, "Yes", "Cancel", "No");

                    if (result == 2) // No
                    {
                        convertChildren = false;
                    }
                    else if (result == 1) // Cancel
                    {
                        return(null);
                    }
                }
            }

            if (shouldUndo)
            {
                Undo.RegisterCompleteObjectUndo(components, "Convert to UdonBehaviour");
            }

            List <UdonBehaviour> createdComponents = new List <UdonBehaviour>();

            foreach (UdonSharpBehaviour targetObject in components)
            {
                MonoScript            behaviourScript = MonoScript.FromMonoBehaviour(targetObject);
                UdonSharpProgramAsset programAsset    = GetUdonSharpProgramAsset(behaviourScript);

                if (programAsset == null)
                {
                    if (showPrompts)
                    {
                        string scriptPath      = AssetDatabase.GetAssetPath(behaviourScript);
                        string scriptDirectory = Path.GetDirectoryName(scriptPath);
                        string scriptFileName  = Path.GetFileNameWithoutExtension(scriptPath);

                        string assetPath = Path.Combine(scriptDirectory, $"{scriptFileName}.asset").Replace('\\', '/');

                        if (EditorUtility.DisplayDialog("No linked program asset", $"There was no UdonSharpProgramAsset found for '{behaviourScript.GetClass()}', do you want to create one?", "Ok", "Cancel"))
                        {
                            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;
                                }
                            }
                        }
                        else
                        {
                            continue;
                        }

                        programAsset = ScriptableObject.CreateInstance <UdonSharpProgramAsset>();
                        programAsset.sourceCsScript = behaviourScript;
                        AssetDatabase.CreateAsset(programAsset, assetPath);
                        AssetDatabase.SaveAssets();

                        UdonSharpProgramAsset.ClearProgramAssetCache();

                        programAsset.CompileCsProgram();

                        AssetDatabase.SaveAssets();

                        AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
                    }
                    else
                    {
                        Debug.LogWarning($"Could not convert U# behaviour '{behaviourScript.GetClass()}' on '{targetObject.gameObject}' because it does not have a corresponding UdonSharpProgramAsset");
                        continue;
                    }
                }

                GameObject targetGameObject = targetObject.gameObject;

                UdonBehaviour udonBehaviour = null;

                if (shouldUndo)
                {
                    udonBehaviour = Undo.AddComponent <UdonBehaviour>(targetGameObject);
                }
                else
                {
                    udonBehaviour = targetGameObject.AddComponent <UdonBehaviour>();
                }

                udonBehaviour.programSource = programAsset;
#pragma warning disable CS0618 // Type or member is obsolete
                udonBehaviour.SynchronizePosition             = false;
                udonBehaviour.AllowCollisionOwnershipTransfer = false;
#pragma warning restore CS0618 // Type or member is obsolete

                udonBehaviour.Reliable = programAsset.behaviourSyncMode == BehaviourSyncMode.Manual;


                //if (shouldUndo)
                //    Undo.RegisterCompleteObjectUndo(targetObject, "Convert C# to U# behaviour");

                UdonSharpEditorUtility.SetBackingUdonBehaviour(targetObject, udonBehaviour);

                try
                {
                    if (convertChildren)
                    {
                        UdonSharpEditorUtility.CopyProxyToUdon(targetObject, shouldUndo ? ProxySerializationPolicy.AllWithCreateUndo : ProxySerializationPolicy.AllWithCreate);
                    }
                    else
                    {
                        UdonSharpEditorUtility.CopyProxyToUdon(targetObject, ProxySerializationPolicy.RootOnly);
                    }
                }
                catch (System.Exception e)
                {
                    Debug.LogError(e);
                }

                UdonSharpEditorUtility.SetBackingUdonBehaviour(targetObject, null);

                System.Type behaviourType = targetObject.GetType();

                UdonSharpBehaviour newProxy;

                SetIgnoreEvents(true);

                try
                {
                    if (shouldUndo)
                    {
                        newProxy = (UdonSharpBehaviour)Undo.AddComponent(targetObject.gameObject, behaviourType);
                    }
                    else
                    {
                        newProxy = (UdonSharpBehaviour)targetObject.gameObject.AddComponent(behaviourType);
                    }

                    UdonSharpEditorUtility.SetBackingUdonBehaviour(newProxy, udonBehaviour);
                    try
                    {
                        UdonSharpEditorUtility.CopyUdonToProxy(newProxy);
                    }
                    catch (System.Exception e)
                    {
                        Debug.LogError(e);
                    }

                    if (shouldUndo)
                    {
                        Undo.DestroyObjectImmediate(targetObject);
                    }
                    else
                    {
                        Object.DestroyImmediate(targetObject);
                    }

                    newProxy.hideFlags = HideFlags.DontSaveInBuild |
#if !UDONSHARP_DEBUG
                                         HideFlags.HideInInspector |
#endif
                                         HideFlags.DontSaveInEditor;

                    newProxy.enabled = false;
                }
                finally
                {
                    SetIgnoreEvents(false);
                }

                createdComponents.Add(udonBehaviour);
            }

            return(createdComponents.ToArray());
        }