static void InjectUnityEventInterceptors()
        {
            List <System.Type> udonSharpBehaviourTypes = new List <Type>();

            foreach (Assembly assembly in UdonSharpUtils.GetLoadedEditorAssemblies())
            {
                foreach (System.Type type in assembly.GetTypes())
                {
                    if (type != typeof(UdonSharpBehaviour) && type.IsSubclassOf(typeof(UdonSharpBehaviour)))
                    {
                        udonSharpBehaviourTypes.Add(type);
                    }
                }
            }

            const string harmonyID = "UdonSharp.Editor.EventPatch";
            Harmony      harmony   = new Harmony(harmonyID);

            harmony.UnpatchAll(harmonyID);

            MethodInfo    injectedEvent  = typeof(InjectedMethods).GetMethod(nameof(InjectedMethods.EventInterceptor), BindingFlags.Static | BindingFlags.Public);
            HarmonyMethod injectedMethod = new HarmonyMethod(injectedEvent);

            void InjectEvent(System.Type behaviourType, string eventName)
            {
                const BindingFlags eventBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;

                MethodInfo eventInfo = behaviourType.GetMethods(eventBindingFlags).FirstOrDefault(e => e.Name == eventName && e.ReturnType == typeof(void));

                try
                {
                    if (eventInfo != null)
                    {
                        harmony.Patch(eventInfo, injectedMethod);
                    }
                }
                catch (System.Exception)
                {
                    Debug.LogWarning($"Failed to patch event {eventInfo} on {behaviourType}");
                }
            }

            foreach (System.Type udonSharpBehaviourType in udonSharpBehaviourTypes)
            {
                // Trigger events
                InjectEvent(udonSharpBehaviourType, "OnTriggerEnter");
                InjectEvent(udonSharpBehaviourType, "OnTriggerExit");
                InjectEvent(udonSharpBehaviourType, "OnTriggerStay");
                InjectEvent(udonSharpBehaviourType, "OnTriggerEnter2D");
                InjectEvent(udonSharpBehaviourType, "OnTriggerExit2D");
                InjectEvent(udonSharpBehaviourType, "OnTriggerStay2D");

                // Collision events
                InjectEvent(udonSharpBehaviourType, "OnCollisionEnter");
                InjectEvent(udonSharpBehaviourType, "OnCollisionExit");
                InjectEvent(udonSharpBehaviourType, "OnCollisionStay");
                InjectEvent(udonSharpBehaviourType, "OnCollisionEnter2D");
                InjectEvent(udonSharpBehaviourType, "OnCollisionExit2D");
                InjectEvent(udonSharpBehaviourType, "OnCollisionStay2D");

                // Controller
                InjectEvent(udonSharpBehaviourType, "OnControllerColliderHit");

                // Animator events
                InjectEvent(udonSharpBehaviourType, "OnAnimatorIK");
                InjectEvent(udonSharpBehaviourType, "OnAnimatorMove");

                // Mouse events
                InjectEvent(udonSharpBehaviourType, "OnMouseDown");
                InjectEvent(udonSharpBehaviourType, "OnMouseDrag");
                InjectEvent(udonSharpBehaviourType, "OnMouseEnter");
                InjectEvent(udonSharpBehaviourType, "OnMouseExit");
                InjectEvent(udonSharpBehaviourType, "OnMouseOver");
                InjectEvent(udonSharpBehaviourType, "OnMouseUp");
                InjectEvent(udonSharpBehaviourType, "OnMouseUpAsButton");

                // Particle events
                InjectEvent(udonSharpBehaviourType, "OnParticleCollision");
                InjectEvent(udonSharpBehaviourType, "OnParticleSystemStopped");
                InjectEvent(udonSharpBehaviourType, "OnParticleTrigger");
                InjectEvent(udonSharpBehaviourType, "OnParticleUpdateJobScheduled");

                // Rendering events
                InjectEvent(udonSharpBehaviourType, "OnPostRender");
                InjectEvent(udonSharpBehaviourType, "OnPreCull");
                InjectEvent(udonSharpBehaviourType, "OnPreRender");
                InjectEvent(udonSharpBehaviourType, "OnRenderImage");
                InjectEvent(udonSharpBehaviourType, "OnRenderObject");
                InjectEvent(udonSharpBehaviourType, "OnWillRenderObject");

                // Joint events
                InjectEvent(udonSharpBehaviourType, "OnJointBreak");
                InjectEvent(udonSharpBehaviourType, "OnJointBreak2D");

                // Audio
                InjectEvent(udonSharpBehaviourType, "OnAudioFilterRead");

                // Transforms
                InjectEvent(udonSharpBehaviourType, "OnTransformChildrenChanged");
                InjectEvent(udonSharpBehaviourType, "OnTransformParentChanged");

                // Object state, OnDisable and OnDestroy will get called regardless of the enabled state of the component, include OnEnable for consistency
                InjectEvent(udonSharpBehaviourType, "OnEnable");
                InjectEvent(udonSharpBehaviourType, "OnDisable");
                InjectEvent(udonSharpBehaviourType, "OnDestroy");
            }

            // Add method for checking if events need to be skipped
            InjectedMethods.shouldSkipEventsMethod = (Func <bool>)Delegate.CreateDelegate(typeof(Func <bool>), typeof(UdonSharpBehaviour).GetMethod("ShouldSkipEvents", BindingFlags.Static | BindingFlags.NonPublic));

            // Patch GUI object field drawer
            MethodInfo doObjectFieldMethod = typeof(EditorGUI).GetMethods(BindingFlags.Static | BindingFlags.NonPublic).FirstOrDefault(e => e.Name == "DoObjectField" && e.GetParameters().Length == 9);

            HarmonyMethod objectFieldProxy = new HarmonyMethod(typeof(InjectedMethods).GetMethod(nameof(InjectedMethods.DoObjectFieldProxy)));

            harmony.Patch(doObjectFieldMethod, objectFieldProxy);

            System.Type validatorDelegateType = typeof(EditorGUI).GetNestedType("ObjectFieldValidator", BindingFlags.Static | BindingFlags.NonPublic);
            InjectedMethods.validationDelegate = Delegate.CreateDelegate(validatorDelegateType, typeof(InjectedMethods).GetMethod(nameof(InjectedMethods.ValidateObjectReference)));

            InjectedMethods.objectValidatorMethod = typeof(EditorGUI).GetMethod("ValidateObjectReferenceValue", BindingFlags.NonPublic | BindingFlags.Static);

            MethodInfo crossSceneRefCheckMethod = typeof(EditorGUI).GetMethod("CheckForCrossSceneReferencing", BindingFlags.NonPublic | BindingFlags.Static);

            InjectedMethods.crossSceneRefCheckMethod = (Func <UnityEngine.Object, UnityEngine.Object, bool>)Delegate.CreateDelegate(typeof(Func <UnityEngine.Object, UnityEngine.Object, bool>), crossSceneRefCheckMethod);

            // Patch post BuildAssetBundles fixup function
            MethodInfo buildAssetbundlesMethod = typeof(BuildPipeline).GetMethods(BindingFlags.NonPublic | BindingFlags.Static).First(e => e.Name == "BuildAssetBundles" && e.GetParameters().Length == 5);

            MethodInfo    postBuildMethod        = typeof(InjectedMethods).GetMethod(nameof(InjectedMethods.PostBuildAssetBundles), BindingFlags.Public | BindingFlags.Static);
            HarmonyMethod postBuildHarmonyMethod = new HarmonyMethod(postBuildMethod);

            MethodInfo    preBuildMethod        = typeof(InjectedMethods).GetMethod(nameof(InjectedMethods.PreBuildAssetBundles), BindingFlags.Public | BindingFlags.Static);
            HarmonyMethod preBuildHarmonyMethod = new HarmonyMethod(preBuildMethod);

            harmony.Patch(buildAssetbundlesMethod, preBuildHarmonyMethod, postBuildHarmonyMethod);
        }
Ejemplo n.º 2
0
        static void InjectUnityEventInterceptors()
        {
            List <System.Type> udonSharpBehaviourTypes = new List <Type>();

            foreach (Assembly assembly in UdonSharpUtils.GetLoadedEditorAssemblies())
            {
                foreach (System.Type type in assembly.GetTypes())
                {
                    if (type != typeof(UdonSharpBehaviour) && type.IsSubclassOf(typeof(UdonSharpBehaviour)))
                    {
                        udonSharpBehaviourTypes.Add(type);
                    }
                }
            }

            Harmony harmony = new Harmony(HARMONY_ID);

            using (var patchScope = new UdonSharpUtils.UdonSharpAssemblyLoadStripScope())
                harmony.UnpatchAll(HARMONY_ID);

            MethodInfo    injectedEvent  = typeof(InjectedMethods).GetMethod(nameof(InjectedMethods.EventInterceptor), BindingFlags.Static | BindingFlags.Public);
            HarmonyMethod injectedMethod = new HarmonyMethod(injectedEvent);

            void InjectEvent(System.Type behaviourType, string eventName)
            {
                const BindingFlags eventBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;

                MethodInfo eventInfo = behaviourType.GetMethods(eventBindingFlags).FirstOrDefault(e => e.Name == eventName && e.ReturnType == typeof(void));

                try
                {
                    if (eventInfo != null)
                    {
                        harmony.Patch(eventInfo, injectedMethod);
                    }
                }
                catch (System.Exception e)
                {
                    Debug.LogWarning($"Failed to patch event {eventInfo} on {behaviourType}\nException:\n{e}");
                }
            }

            using (var loadScope = new UdonSharpUtils.UdonSharpAssemblyLoadStripScope())
            {
                foreach (System.Type udonSharpBehaviourType in udonSharpBehaviourTypes)
                {
                    // Trigger events
                    InjectEvent(udonSharpBehaviourType, "OnTriggerEnter");
                    InjectEvent(udonSharpBehaviourType, "OnTriggerExit");
                    InjectEvent(udonSharpBehaviourType, "OnTriggerStay");
                    InjectEvent(udonSharpBehaviourType, "OnTriggerEnter2D");
                    InjectEvent(udonSharpBehaviourType, "OnTriggerExit2D");
                    InjectEvent(udonSharpBehaviourType, "OnTriggerStay2D");

                    // Collision events
                    InjectEvent(udonSharpBehaviourType, "OnCollisionEnter");
                    InjectEvent(udonSharpBehaviourType, "OnCollisionExit");
                    InjectEvent(udonSharpBehaviourType, "OnCollisionStay");
                    InjectEvent(udonSharpBehaviourType, "OnCollisionEnter2D");
                    InjectEvent(udonSharpBehaviourType, "OnCollisionExit2D");
                    InjectEvent(udonSharpBehaviourType, "OnCollisionStay2D");

                    // Controller
                    InjectEvent(udonSharpBehaviourType, "OnControllerColliderHit");

                    // Animator events
                    InjectEvent(udonSharpBehaviourType, "OnAnimatorIK");
                    InjectEvent(udonSharpBehaviourType, "OnAnimatorMove");

                    // Mouse events
                    InjectEvent(udonSharpBehaviourType, "OnMouseDown");
                    InjectEvent(udonSharpBehaviourType, "OnMouseDrag");
                    InjectEvent(udonSharpBehaviourType, "OnMouseEnter");
                    InjectEvent(udonSharpBehaviourType, "OnMouseExit");
                    InjectEvent(udonSharpBehaviourType, "OnMouseOver");
                    InjectEvent(udonSharpBehaviourType, "OnMouseUp");
                    InjectEvent(udonSharpBehaviourType, "OnMouseUpAsButton");

                    // Particle events
                    InjectEvent(udonSharpBehaviourType, "OnParticleCollision");
                    InjectEvent(udonSharpBehaviourType, "OnParticleSystemStopped");
                    InjectEvent(udonSharpBehaviourType, "OnParticleTrigger");
                    InjectEvent(udonSharpBehaviourType, "OnParticleUpdateJobScheduled");

                    // Rendering events
                    InjectEvent(udonSharpBehaviourType, "OnPostRender");
                    InjectEvent(udonSharpBehaviourType, "OnPreCull");
                    InjectEvent(udonSharpBehaviourType, "OnPreRender");
                    InjectEvent(udonSharpBehaviourType, "OnRenderImage");
                    InjectEvent(udonSharpBehaviourType, "OnRenderObject");
                    InjectEvent(udonSharpBehaviourType, "OnWillRenderObject");

                    // Joint events
                    InjectEvent(udonSharpBehaviourType, "OnJointBreak");
                    InjectEvent(udonSharpBehaviourType, "OnJointBreak2D");

                    // Audio
                    InjectEvent(udonSharpBehaviourType, "OnAudioFilterRead");

                    // Transforms
                    InjectEvent(udonSharpBehaviourType, "OnTransformChildrenChanged");
                    InjectEvent(udonSharpBehaviourType, "OnTransformParentChanged");

                    // Object state, OnDisable and OnDestroy will get called regardless of the enabled state of the component, include OnEnable for consistency
                    InjectEvent(udonSharpBehaviourType, "OnEnable");
                    InjectEvent(udonSharpBehaviourType, "OnDisable");
                    InjectEvent(udonSharpBehaviourType, "OnDestroy");
                }

                // Add method for checking if events need to be skipped
                InjectedMethods.shouldSkipEventsMethod = (Func <bool>)Delegate.CreateDelegate(typeof(Func <bool>), typeof(UdonSharpBehaviour).GetMethod("ShouldSkipEvents", BindingFlags.Static | BindingFlags.NonPublic));

                // Patch GUI object field drawer
                MethodInfo doObjectFieldMethod = typeof(EditorGUI).GetMethods(BindingFlags.Static | BindingFlags.NonPublic).FirstOrDefault(e => e.Name == "DoObjectField" && e.GetParameters().Length == 9);

                HarmonyMethod objectFieldProxy = new HarmonyMethod(typeof(InjectedMethods).GetMethod(nameof(InjectedMethods.DoObjectFieldProxy)));
                harmony.Patch(doObjectFieldMethod, objectFieldProxy);

                System.Type validatorDelegateType = typeof(EditorGUI).GetNestedType("ObjectFieldValidator", BindingFlags.Static | BindingFlags.NonPublic);
                InjectedMethods.validationDelegate = Delegate.CreateDelegate(validatorDelegateType, typeof(InjectedMethods).GetMethod(nameof(InjectedMethods.ValidateObjectReference)));

                InjectedMethods.objectValidatorMethod = typeof(EditorGUI).GetMethod("ValidateObjectReferenceValue", BindingFlags.NonPublic | BindingFlags.Static);

                MethodInfo crossSceneRefCheckMethod = typeof(EditorGUI).GetMethod("CheckForCrossSceneReferencing", BindingFlags.NonPublic | BindingFlags.Static);
                InjectedMethods.crossSceneRefCheckMethod = (Func <UnityEngine.Object, UnityEngine.Object, bool>)Delegate.CreateDelegate(typeof(Func <UnityEngine.Object, UnityEngine.Object, bool>), crossSceneRefCheckMethod);

                // Patch post BuildAssetBundles fixup function
                MethodInfo buildAssetbundlesMethod = typeof(BuildPipeline).GetMethods(BindingFlags.NonPublic | BindingFlags.Static).First(e => e.Name == "BuildAssetBundles" && e.GetParameters().Length == 5);

                MethodInfo    postBuildMethod        = typeof(InjectedMethods).GetMethod(nameof(InjectedMethods.PostBuildAssetBundles), BindingFlags.Public | BindingFlags.Static);
                HarmonyMethod postBuildHarmonyMethod = new HarmonyMethod(postBuildMethod);

                MethodInfo    preBuildMethod        = typeof(InjectedMethods).GetMethod(nameof(InjectedMethods.PreBuildAssetBundles), BindingFlags.Public | BindingFlags.Static);
                HarmonyMethod preBuildHarmonyMethod = new HarmonyMethod(preBuildMethod);

                harmony.Patch(buildAssetbundlesMethod, preBuildHarmonyMethod, postBuildHarmonyMethod);

                // Patch a workaround for errors in Unity's APIUpdaterHelper when in a Japanese locale
                MethodInfo    findTypeInLoadedAssemblies    = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.Scripting.Compilers.APIUpdaterHelper").GetMethod("FindTypeInLoadedAssemblies", BindingFlags.Static | BindingFlags.NonPublic);
                MethodInfo    injectedFindType              = typeof(InjectedMethods).GetMethod(nameof(InjectedMethods.FindTypeInLoadedAssembliesPrefix), BindingFlags.Public | BindingFlags.Static);
                HarmonyMethod injectedFindTypeHarmonyMethod = new HarmonyMethod(injectedFindType);

                harmony.Patch(findTypeInLoadedAssemblies, injectedFindTypeHarmonyMethod);

#if ODIN_INSPECTOR_3
                try
                {
                    Assembly odinEditorAssembly = UdonSharpUtils.GetLoadedEditorAssemblies().FirstOrDefault(assembly => assembly.GetName().Name == "Sirenix.OdinInspector.Editor");

                    System.Type editorUtilityType = odinEditorAssembly.GetType("Sirenix.OdinInspector.Editor.CustomEditorUtility");

                    MethodInfo resetCustomEditorsMethod = editorUtilityType.GetMethod("ResetCustomEditors");

                    MethodInfo    odinInspectorOverrideMethod        = typeof(InjectedMethods).GetMethod(nameof(InjectedMethods.OdinInspectorOverride), BindingFlags.Public | BindingFlags.Static);
                    HarmonyMethod odinInspectorOverrideHarmonyMethod = new HarmonyMethod(odinInspectorOverrideMethod);

                    harmony.Patch(resetCustomEditorsMethod, null, odinInspectorOverrideHarmonyMethod);
                }
                catch (Exception e)
                {
                    Debug.LogWarning($"Failed to patch Odin inspector fix for U#\nException: {e}");
                }
#endif
            }
        }