Skip to content

Instantly share code, notes, and snippets.

@Spartan322
Created August 7, 2020 00:03
Show Gist options
  • Save Spartan322/a0d45fde37ccef4ad4f2acc5cdbbc0cb to your computer and use it in GitHub Desktop.
Save Spartan322/a0d45fde37ccef4ad4f2acc5cdbbc0cb to your computer and use it in GitHub Desktop.
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