Skip to content

Commit cd53384

Browse files
committed
Add AutoPatch Harmony attribute
1 parent 9e6f3d9 commit cd53384

File tree

2 files changed

+66
-1
lines changed

2 files changed

+66
-1
lines changed

src/Oxide.Core.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<Project>
33
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
44
<Import Project="..\netfx.props" />
@@ -17,6 +17,7 @@
1717
<NoWarn>NU1701</NoWarn>
1818
</PropertyGroup>
1919
<ItemGroup>
20+
<PackageReference Include="Lib.Harmony" Version="2.3.3" />
2021
<PackageReference Include="Oxide.Common" Version="2.0.21-develop" />
2122
<PackageReference Include="Oxide.References" Version="2.0.*">
2223
<PrivateAssets>contentfiles;analyzers;build</PrivateAssets>

src/Plugins/CSPlugin.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections.Generic;
44
using System.Reflection;
55
using Oxide.Pooling;
6+
using HarmonyLib;
67

78
namespace Oxide.Core.Plugins
89
{
@@ -27,6 +28,17 @@ public HookMethodAttribute(string name)
2728
}
2829
}
2930

31+
/// <summary>
32+
/// Indicates that the specified class should automatically apply it's harmony patches
33+
/// </summary>
34+
[AttributeUsage(AttributeTargets.Class)]
35+
public class AutoPatchAttribute : Attribute
36+
{
37+
public AutoPatchAttribute()
38+
{
39+
}
40+
}
41+
3042
/// <summary>
3143
/// Represents a plugin implemented in .NET
3244
/// </summary>
@@ -42,6 +54,22 @@ public abstract class CSPlugin : Plugin
4254
// All hooked methods
4355
protected Dictionary<string, List<HookMethod>> Hooks = new Dictionary<string, List<HookMethod>>();
4456

57+
// Harmony
58+
private Harmony _harmonyInstance;
59+
protected string HarmonyId => $"com.oxidemod.{Name}";
60+
protected Harmony HarmonyInstance
61+
{
62+
get
63+
{
64+
if (_harmonyInstance == null)
65+
{
66+
_harmonyInstance = new Harmony(HarmonyId);
67+
}
68+
69+
return _harmonyInstance;
70+
}
71+
}
72+
4573
// All matched hooked methods
4674
protected HookCache HooksCache = new HookCache();
4775

@@ -97,6 +125,29 @@ public override void HandleAddedToManager(PluginManager manager)
97125
Subscribe(hookname);
98126
}
99127

128+
// Find all classes with the AutoPatch attribute and apply the patches
129+
foreach (Type nestedType in GetType().GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
130+
{
131+
object[] attr = nestedType.GetCustomAttributes(typeof(AutoPatchAttribute), false);
132+
if (attr.Length < 1)
133+
{
134+
continue;
135+
}
136+
137+
List<MethodInfo> harmonyMethods = HarmonyInstance.CreateClassProcessor(nestedType)?.Patch();
138+
139+
if (harmonyMethods == null || harmonyMethods.Count == 0)
140+
{
141+
Interface.Oxide.LogWarning($"[{Title}] AutoPatch attribute found on '{nestedType.Name}' but no HarmonyPatch methods found. Skipping.");
142+
continue;
143+
}
144+
145+
foreach (MethodInfo method in harmonyMethods)
146+
{
147+
Interface.Oxide.LogInfo($"[{Title}] Automatically Harmony patched '{method.Name}' method. ({nestedType.Name})");
148+
}
149+
}
150+
100151
try
101152
{
102153
// Let the plugin know that it is loading
@@ -112,6 +163,19 @@ public override void HandleAddedToManager(PluginManager manager)
112163
}
113164
}
114165

166+
/// <summary>
167+
/// Called when this plugin has been removed from a manager
168+
/// </summary>
169+
/// <param name="manager"></param>
170+
public override void HandleRemovedFromManager(PluginManager manager)
171+
{
172+
// Unpatch all automatically patched Harmony patches
173+
_harmonyInstance?.UnpatchAll(HarmonyId);
174+
175+
// Let base work
176+
base.HandleRemovedFromManager(manager);
177+
}
178+
115179
protected void AddHookMethod(string name, MethodInfo method)
116180
{
117181
if (!Hooks.TryGetValue(name, out List<HookMethod> hookMethods))

0 commit comments

Comments
 (0)