123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- #if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR)
- #pragma warning disable CS0618 // obsolete warnings (stay warning-free also in newer unity versions)
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using System.Threading;
- using System.Threading.Tasks;
- using UnityEngine;
- using Object = UnityEngine.Object;
- #if UNITY_EDITOR
- using UnityEditor;
- #endif
- namespace SingularityGroup.HotReload {
- static class Dispatch {
- // DispatchOnHotReload is called every time a patch is applied (1x per batch of filechanges)
- public static async Task OnHotReload(List<MethodPatch> patchedMethods) {
- var methods = await Task.Run(() => GetOrFillMethodsCacheThreaded());
- foreach (var m in methods) {
- if (m.IsStatic) {
- InvokeStaticMethod(m, nameof(InvokeOnHotReload), patchedMethods);
- } else {
- foreach (var go in GameObject.FindObjectsOfType(m.DeclaringType)) {
- InvokeInstanceMethod(m, go, patchedMethods);
- }
- }
- }
- }
- public static void OnHotReloadLocal(MethodBase originalMethod, MethodBase patchMethod) {
- if (!Attribute.IsDefined(originalMethod, typeof(InvokeOnHotReloadLocal))) {
- return;
- }
- var attrib = Attribute.GetCustomAttribute(originalMethod, typeof(InvokeOnHotReloadLocal)) as InvokeOnHotReloadLocal;
- if (!string.IsNullOrEmpty(attrib?.methodToInvoke)) {
- OnHotReloadLocalCustom(originalMethod, attrib);
- return;
- }
- var patchMethodParams = patchMethod.GetParameters();
- if (patchMethodParams.Length == 0) {
- InvokeStaticMethod(patchMethod, nameof(InvokeOnHotReloadLocal), null);
- } else if (typeof(MonoBehaviour).IsAssignableFrom(patchMethodParams[0].ParameterType)) {
- foreach (var go in GameObject.FindObjectsOfType(patchMethodParams[0].ParameterType)) {
- InvokeInstanceMethodStatic(patchMethod, go);
- }
- } else {
- Log.Warning($"[{nameof(InvokeOnHotReloadLocal)}] {patchMethod.DeclaringType?.Name} {patchMethod.Name} failed. Make sure it's a method with 0 parameters either static or defined on MonoBehaviour.");
- }
- }
- public static void OnHotReloadLocalCustom(MethodBase origianlMethod, InvokeOnHotReloadLocal attrib) {
- var reloadForType = origianlMethod.DeclaringType;
- var reloadMethod = reloadForType?.GetMethod(attrib.methodToInvoke, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
- if (reloadMethod == null) {
- Log.Warning($"[{nameof(InvokeOnHotReloadLocal)}] failed to find method {attrib.methodToInvoke}. Make sure it exists within the same class.");
- return;
- }
- if (reloadMethod.IsStatic) {
- InvokeStaticMethod(reloadMethod, nameof(InvokeOnHotReloadLocal), null);
- } else if (typeof(MonoBehaviour).IsAssignableFrom(reloadForType)) {
- foreach (var go in GameObject.FindObjectsOfType(reloadForType)) {
- InvokeInstanceMethod(reloadMethod, go, null);
- }
- } else {
- Log.Warning($"[{nameof(InvokeOnHotReloadLocal)}] {reloadMethod.DeclaringType?.Name} {reloadMethod.Name} failed. Make sure it's a method with 0 parameters either static or defined on MonoBehaviour.");
- }
- }
- private static List<MethodInfo> methodsCache;
- private static List<MethodInfo> GetOrFillMethodsCacheThreaded() {
- if (methodsCache != null) {
- return methodsCache;
- }
- #if UNITY_2019_1_OR_NEWER && UNITY_EDITOR
- var methodCollection = UnityEditor.TypeCache.GetMethodsWithAttribute(typeof(InvokeOnHotReload));
- var methods = new List<MethodInfo>();
- foreach (var m in methodCollection) {
- methods.Add(m);
- }
- #else
- var methods = GetMethodsReflection();
- #endif
- methodsCache = methods;
- return methods;
- }
- private static List<MethodInfo> GetMethodsReflection() {
- var methods = new List<MethodInfo>();
- try {
- foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) {
- if (asm.FullName == "System" || asm.FullName.StartsWith("System.", StringComparison.Ordinal)) {
- continue; // big performance optimization
- }
- try {
- foreach (var type in asm.GetTypes()) {
- try {
- foreach (var m in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) {
- try {
- if (Attribute.IsDefined(m, typeof(InvokeOnHotReload))) {
- methods.Add(m);
- }
- } catch (BadImageFormatException) {
- // silently ignore (can happen, is very annoying if it spams)
- /*
- BadImageFormatException: VAR 3 (TOutput) cannot be expanded in this context with 3 instantiations
- System.Reflection.MonoMethod.GetBaseMethod () (at <c8d0d7b9135640958bff528a1e374758>:0)
- System.MonoCustomAttrs.GetBase (System.Reflection.ICustomAttributeProvider obj) (at <c8d0d7b9135640958bff528a1e374758>:0)
- System.MonoCustomAttrs.IsDefined (System.Reflection.ICustomAttributeProvider obj, System.Type attributeType, System.Boolean inherit) (at <c8d0d7b9135640958bff528a1e374758>:0)
- */
- } catch (TypeLoadException) {
- // silently ignore (can happen, is very annoying if it spams)
- } catch (Exception e) {
- ThreadUtility.LogException(new AggregateException(type.Name + "." + m.Name, e));
- }
- }
- } catch (BadImageFormatException) {
- // silently ignore (can happen, is very annoying if it spams)
- } catch (TypeLoadException) {
- // silently ignore (can happen, is very annoying if it spams)
- } catch (Exception e) {
- ThreadUtility.LogException(new AggregateException(type.Name, e));
- }
- }
- } catch (BadImageFormatException) {
- // silently ignore (can happen, is very annoying if it spams)
- } catch (TypeLoadException) {
- // silently ignore (can happen, is very annoying if it spams)
- } catch (Exception e) {
- ThreadUtility.LogException(new AggregateException(asm.FullName, e));
- }
- }
- } catch (Exception e) {
- ThreadUtility.LogException(e);
- }
- return methods;
- }
- private static void InvokeStaticMethod(MethodBase m, string attrName, List<MethodPatch> patchedMethods) {
- try {
- if (patchedMethods != null && m.GetParameters().Length == 1) {
- m.Invoke(null, new object[] { patchedMethods });
- } else {
- m.Invoke(null, new object[] { });
- }
- } catch (Exception e) {
- if (m.GetParameters().Length != 0) {
- Log.Warning($"[{attrName}] {m.DeclaringType?.Name} {m.Name} failed. Make sure it has 0 parameters, or 1 parameter with type List<MethodPatch>. Exception:\n{e}");
- } else {
- Log.Warning($"[{attrName}] {m.DeclaringType?.Name} {m.Name} failed. Exception\n{e}");
- }
- }
- }
- private static void InvokeInstanceMethod(MethodBase m, Object go, List<MethodPatch> patchedMethods) {
- try {
- if (patchedMethods != null && m.GetParameters().Length == 1) {
- m.Invoke(go, new object[] { patchedMethods });
- } else {
- m.Invoke(go, new object[] { });
- }
- } catch (Exception e) {
- if (m.GetParameters().Length != 0) {
- Log.Warning($"[InvokeOnHotReload] {m.DeclaringType?.Name} {m.Name} failed. Make sure it has 0 parameters, or 1 parameter with type List<MethodPatch>. Exception:\n{e}");
- } else {
- Log.Warning($"[InvokeOnHotReload] {m.DeclaringType?.Name} {m.Name} failed. Exception:\n{e}");
- }
- }
- }
-
- private static void InvokeInstanceMethodStatic(MethodBase m, Object go) {
- try {
- m.Invoke(null, new object[] { go });
- } catch (Exception e) {
- if (m.GetParameters().Length != 0) {
- Log.Warning($"[InvokeOnHotReloadLocal] {m.DeclaringType?.Name} {m.Name} failed. Make sure it has 0 parameters. Exception:\n{e}");
- } else {
- Log.Warning($"[InvokeOnHotReloadLocal] {m.DeclaringType?.Name} {m.Name} failed. Exception:\n{e}");
- }
- }
- }
- }
- }
- #endif
|