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); }
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 } }