first commit
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user