Created
August 7, 2020 00:03
-
-
Save Spartan322/a0d45fde37ccef4ad4f2acc5cdbbc0cb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class SL | |
{ | |
public static readonly Harmony Harmony = new Harmony("com.spartan322.spartanslib"); | |
[HarmonyReversePatch] | |
public static void ReadyPrefix(Node __instance) | |
{ | |
__instance.Wire(); | |
} | |
public static void CtorPrefix(Node __instance) | |
{ | |
__instance.Connect(Signal.Ready, dummyRef, nameof(DummyRef.OnReady), __instance); | |
__instance.Connect(Signal.ScriptChanged, dummyRef, nameof(DummyRef.TryCallReadyAgain), __instance); | |
} | |
public static void CtorPrefixForGlobal(Node __instance) | |
{ | |
var type = __instance.GetType(); | |
type.GetFirstAttribute<GlobalAttribute>().HandleFor(__instance, type); | |
} | |
// needed for ctor, can't inject into non-existent methods | |
private class DummyRef : Reference | |
{ | |
public void OnReady(Node instance) | |
=> ReadyPrefix(instance); | |
public void TryCallReadyAgain(Node instance) | |
=> instance.Notification(Node.NotificationReady); | |
} | |
private static readonly DummyRef dummyRef = new DummyRef(); | |
[MethodImpl(MethodImplOptions.NoInlining)] | |
public static void Init() | |
{ | |
var nodeType = typeof(Node); | |
var readyPrefix = new HarmonyMethod(typeof(SL).GetMethod(nameof(ReadyPrefix))); | |
var ctorPrefix = new HarmonyMethod(typeof(SL).GetMethod(nameof(CtorPrefix))); | |
var ctorForGlobal = new HarmonyMethod(typeof(SL).GetMethod(nameof(CtorPrefixForGlobal))); | |
foreach (var type in Assembly.GetCallingAssembly().GetExportedTypes()) | |
{ | |
if (type.IsSealed || type.IsAbstract || type.IsInterface || !nodeType.IsAssignableFrom(type)) continue; | |
if (type.HasAttribute<GlobalAttribute>()) | |
Harmony.Patch(type.GetConstructor(new Type[0]), ctorForGlobal); | |
if (!type.HasSLAttribute()) continue; | |
//Harmony.PatchAll(); | |
var readyMethInfo = type.GetMethod(nameof(Node._Ready), new Type[0]); | |
if(!readyMethInfo.IsDeclaredMember()) | |
{ | |
Harmony.Patch(type.GetConstructor(new Type[0]), ctorPrefix); | |
continue; | |
} | |
//if (readyMethInfo == null) continue; | |
Harmony.Patch(readyMethInfo, readyPrefix); | |
} | |
} | |
public static void Wire<T>(this T node) | |
where T : Node | |
{ | |
var type = node.GetType(); | |
foreach (var attribType in Assembly.GetExecutingAssembly().GetExportedTypes()) | |
{ | |
if (NotValidAttributeType(attribType)) continue; | |
var validOn = attribType.GetFirstAttribute<AttributeUsageAttribute>().ValidOn; | |
if (validOn == 0) | |
validOn = AttributeTargets.All; | |
if (validOn.TargetsTypes()) | |
type.GetFirstAttributeFor<SpartansLibAttribute>(attribType, false)?.HandleFor(node, type); | |
if(validOn.TargetsMembers()) | |
foreach (var member in type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) | |
{ | |
if ((member is PropertyInfo && validOn.HasFlagOf(AttributeTargets.Property)) | |
|| (member is FieldInfo && validOn.HasFlagOf(AttributeTargets.Field)) | |
|| (member is MethodInfo && validOn.HasFlagOf(AttributeTargets.Method)) | |
|| (member is EventInfo && validOn.HasFlagOf(AttributeTargets.Event)) | |
|| member is ConstructorInfo && validOn.HasFlagOf(AttributeTargets.Constructor)) | |
member.GetFirstAttributeFor<SpartansLibAttribute>(attribType)?.HandleFor(node, member); | |
} | |
} | |
} | |
public static bool HasSLAttribute(this Type nodeType) | |
{ | |
foreach (var attribType in Assembly.GetExecutingAssembly().GetExportedTypes()) | |
{ | |
if (NotValidAttributeType(attribType)) continue; | |
var validOn = attribType.GetFirstAttribute<AttributeUsageAttribute>()?.ValidOn ?? AttributeTargets.All; | |
if (validOn == 0) | |
validOn = AttributeTargets.All; | |
if (validOn.TargetsTypes()) | |
{ | |
var has = nodeType.HasAttribute(attribType); | |
if (has) | |
return has; | |
} | |
if (validOn.TargetsMembers()) | |
{ | |
var has = nodeType.GetMembers( | |
BindingFlags.Public | |
| BindingFlags.NonPublic | |
| BindingFlags.Instance) | |
.Any(m => | |
{ | |
if ((m is PropertyInfo && validOn.HasFlagOf(AttributeTargets.Property)) | |
|| (m is FieldInfo && validOn.HasFlagOf(AttributeTargets.Field)) | |
|| (m is MethodInfo && validOn.HasFlagOf(AttributeTargets.Method)) | |
|| (m is EventInfo && validOn.HasFlagOf(AttributeTargets.Event)) | |
|| m is ConstructorInfo && validOn.HasFlagOf(AttributeTargets.Constructor)) | |
return m.HasAttribute(attribType); | |
return false; | |
}); | |
if (has) | |
return has; | |
} | |
} | |
return false; | |
} | |
// GlobalAttribute is a special attribute that allows me to access globals by type or name without explict singletons, I am trying to reduce their usage | |
// SpartanLibsAttribute is the base attribute type that handles everything else | |
private static bool NotValidAttributeType(Type type) | |
=> type == typeof(SpartansLibAttribute) || type == typeof(GlobalAttribute) || !typeof(SpartansLibAttribute).IsAssignableFrom(type); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment