Skip to content

Commit 6b430ef

Browse files
committed
feat: more experimenting
1 parent 3587fc3 commit 6b430ef

File tree

6 files changed

+248
-76
lines changed

6 files changed

+248
-76
lines changed

COTL_API/CustomInventory/CustomItemManager.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Reflection;
22
using COTL_API.Guid;
3+
using COTL_API.HarmonyUtils;
34
using Random = UnityEngine.Random;
45

56
namespace COTL_API.CustomInventory;
@@ -27,6 +28,7 @@ public static InventoryItem.ITEM_TYPE Add(CustomInventoryItem item)
2728
item.InternalObjectName = $"CustomItem_{item.InternalName}";
2829

2930
CustomItemList.Add(itemType, item);
31+
RegistryManager.GetRegistry(typeof(InventoryItem.ITEM_TYPE)).Add(itemType, item);
3032

3133
return itemType;
3234
}

COTL_API/CustomInventory/Patches/CustomInventoryPatches.cs

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -86,50 +86,50 @@ private static bool InventoryIconMapping_GetImage(InventoryItem.ITEM_TYPE type,
8686
return false;
8787
}
8888

89-
[HarmonyPatch(typeof(InventoryItem), nameof(InventoryItem.Name))]
90-
[HarmonyPrefix]
91-
private static bool InventoryItem_Name(InventoryItem.ITEM_TYPE Type, ref string __result)
92-
{
93-
if (!CustomItemList.TryGetValue(Type, out var value)) return true;
94-
__result = value.Name();
95-
return false;
96-
}
97-
98-
[HarmonyPatch(typeof(InventoryItem), nameof(InventoryItem.LocalizedName))]
99-
[HarmonyPrefix]
100-
private static bool InventoryItem_LocalizedName(InventoryItem.ITEM_TYPE Type, ref string __result)
101-
{
102-
if (!CustomItemList.TryGetValue(Type, out var value)) return true;
103-
__result = value.LocalizedName();
104-
return false;
105-
}
106-
107-
[HarmonyPatch(typeof(InventoryItem), nameof(InventoryItem.Description))]
108-
[HarmonyPrefix]
109-
private static bool InventoryItem_Description(InventoryItem.ITEM_TYPE Type, ref string __result)
110-
{
111-
if (!CustomItemList.TryGetValue(Type, out var value)) return true;
112-
__result = value.Description();
113-
return false;
114-
}
115-
116-
[HarmonyPatch(typeof(InventoryItem), nameof(InventoryItem.LocalizedDescription))]
117-
[HarmonyPrefix]
118-
private static bool InventoryItem_LocalizedDescription(InventoryItem.ITEM_TYPE Type, ref string __result)
119-
{
120-
if (!CustomItemList.TryGetValue(Type, out var value)) return true;
121-
__result = value.LocalizedDescription();
122-
return false;
123-
}
124-
125-
[HarmonyPatch(typeof(InventoryItem), nameof(InventoryItem.Lore))]
126-
[HarmonyPrefix]
127-
private static bool InventoryItem_Lore(InventoryItem.ITEM_TYPE Type, ref string __result)
128-
{
129-
if (!CustomItemList.TryGetValue(Type, out var value)) return true;
130-
__result = value.Lore();
131-
return false;
132-
}
89+
// [HarmonyPatch(typeof(InventoryItem), nameof(InventoryItem.Name))]
90+
// [HarmonyPrefix]
91+
// private static bool InventoryItem_Name(InventoryItem.ITEM_TYPE Type, ref string __result)
92+
// {
93+
// if (!CustomItemList.TryGetValue(Type, out var value)) return true;
94+
// __result = value.Name();
95+
// return false;
96+
// }
97+
//
98+
// [HarmonyPatch(typeof(InventoryItem), nameof(InventoryItem.LocalizedName))]
99+
// [HarmonyPrefix]
100+
// private static bool InventoryItem_LocalizedName(InventoryItem.ITEM_TYPE Type, ref string __result)
101+
// {
102+
// if (!CustomItemList.TryGetValue(Type, out var value)) return true;
103+
// __result = value.LocalizedName();
104+
// return false;
105+
// }
106+
//
107+
// [HarmonyPatch(typeof(InventoryItem), nameof(InventoryItem.Description))]
108+
// [HarmonyPrefix]
109+
// private static bool InventoryItem_Description(InventoryItem.ITEM_TYPE Type, ref string __result)
110+
// {
111+
// if (!CustomItemList.TryGetValue(Type, out var value)) return true;
112+
// __result = value.Description();
113+
// return false;
114+
// }
115+
//
116+
// [HarmonyPatch(typeof(InventoryItem), nameof(InventoryItem.LocalizedDescription))]
117+
// [HarmonyPrefix]
118+
// private static bool InventoryItem_LocalizedDescription(InventoryItem.ITEM_TYPE Type, ref string __result)
119+
// {
120+
// if (!CustomItemList.TryGetValue(Type, out var value)) return true;
121+
// __result = value.LocalizedDescription();
122+
// return false;
123+
// }
124+
//
125+
// [HarmonyPatch(typeof(InventoryItem), nameof(InventoryItem.Lore))]
126+
// [HarmonyPrefix]
127+
// private static bool InventoryItem_Lore(InventoryItem.ITEM_TYPE Type, ref string __result)
128+
// {
129+
// if (!CustomItemList.TryGetValue(Type, out var value)) return true;
130+
// __result = value.Lore();
131+
// return false;
132+
// }
133133

134134
[HarmonyPatch(typeof(InventoryItem), nameof(InventoryItem.GetItemCategory))]
135135
[HarmonyPrefix]

COTL_API/HarmonyUtils/Patcher.cs

Lines changed: 178 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,194 @@
1-
using COTL_API.CustomInventory;
1+
using System.Reflection;
22
using HarmonyLib;
3-
using Sirenix.Utilities;
3+
using UnityEngine.UIElements.Collections;
44

55
namespace COTL_API.HarmonyUtils;
66

77
public class Patcher
88
{
9-
private List<PatchProcessor> xyz = new();
9+
private readonly HashSet<MethodBase> _patchedMethods = new();
1010

11-
public void PatchAll()
11+
public void PatchAll(Type targetType)
1212
{
13-
CreatePatchClass(typeof(InventoryItem), typeof(CustomInventoryItem));
13+
if (targetType == null)
14+
throw new ArgumentNullException(nameof(targetType));
15+
16+
var candidateMethods = targetType
17+
.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic)
18+
.Where(IsEnumMethod)
19+
.Where(m => !_patchedMethods.Contains(m));
20+
21+
foreach (var method in candidateMethods)
22+
try
23+
{
24+
HarmonyMethod patchMethod;
25+
if (method.ReturnType == typeof(void))
26+
patchMethod =
27+
new HarmonyMethod(typeof(GenericEnumPatch).GetMethod(nameof(GenericEnumPatch.PrefixVoid)));
28+
else
29+
patchMethod =
30+
new HarmonyMethod(
31+
typeof(GenericEnumPatch).GetMethod(nameof(GenericEnumPatch.PrefixWithReturn)));
32+
33+
Plugin.Instance!._harmony.Patch(method, patchMethod);
34+
_patchedMethods.Add(method);
35+
36+
LogDebug($"Successfully patched method: {method.DeclaringType?.Name}.{method.Name} " +
37+
$"(Enum: {method.GetParameters()[0].ParameterType.Name})");
38+
}
39+
catch (Exception ex)
40+
{
41+
LogError($"Failed to patch method {method.Name}: {ex.Message}");
42+
}
43+
}
44+
45+
public void UnpatchAll(Type targetType)
46+
{
47+
if (targetType == null)
48+
throw new ArgumentNullException(nameof(targetType));
49+
50+
var candidateMethods = targetType
51+
.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic)
52+
.Where(IsEnumMethod)
53+
.Where(m => _patchedMethods.Contains(m));
54+
55+
foreach (var method in candidateMethods)
56+
{
57+
HarmonyMethod patchMethod;
58+
if (method.ReturnType == typeof(void))
59+
patchMethod =
60+
new HarmonyMethod(typeof(GenericEnumPatch).GetMethod(nameof(GenericEnumPatch.PrefixVoid)));
61+
else
62+
patchMethod =
63+
new HarmonyMethod(
64+
typeof(GenericEnumPatch).GetMethod(nameof(GenericEnumPatch.PrefixWithReturn))
65+
);
66+
67+
Plugin.Instance!._harmony.Unpatch(method, patchMethod.method);
68+
}
1469
}
15-
16-
public void CreatePatchClass(Type orig, Type dest)
70+
71+
private static bool IsEnumMethod(MethodInfo method)
1772
{
18-
var destMethods = dest.GetMethods();
19-
20-
foreach (var x in orig.GetMethods())
73+
var parameters = method.GetParameters();
74+
return parameters.Length > 0 && parameters[0].ParameterType.IsEnum;
75+
}
76+
}
77+
78+
public static class GenericEnumPatch
79+
{
80+
[HarmonyPriority(Priority.High)]
81+
public static bool PrefixVoid(
82+
MethodBase __originalMethod,
83+
object __instance,
84+
object[] __args)
85+
{
86+
try
2187
{
22-
LogDebug("Trying to patch " + x.Name + "AAAAA");
23-
24-
var method = destMethods.FirstOrDefault(info =>
88+
if (__args == null || __args.Length == 0 || !(__args[0] is Enum enumValue))
89+
return true;
90+
91+
var registry = RegistryManager.GetRegistry(enumValue.GetType());
92+
93+
var customClass = registry.Get(enumValue);
94+
if (customClass == null)
95+
return true;
96+
97+
var customType = customClass.GetType();
98+
var customMethods = customType.GetMethods(
99+
BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic);
100+
101+
var originalParams = __originalMethod.GetParameters().Skip(1).ToArray();
102+
103+
var customMethod = customMethods.FirstOrDefault(m =>
104+
m.Name == __originalMethod.Name &&
105+
IsParameterCompatible(originalParams, m.GetParameters()));
106+
107+
if (customMethod == null)
25108
{
26-
if (info.Name != x.Name) return false;
27-
if (info.ReturnType != dest.GetReturnType()) return false;
28-
return info.GetParameters().Length == x.GetParameters().Length &&
29-
x.GetParameters().All(parameter => parameter.GetType() == info.GetParameters()[parameter.Position].ParameterType);
30-
});
31-
32-
if (method is null)
33-
return;
34-
35-
var processor = new PatchProcessor(Plugin.Instance!._harmony, x);
36-
processor.AddPrefix(new HarmonyMethod
109+
LogDebug($"No matching custom method found for {__originalMethod.Name} in {customType.Name}");
110+
return true;
111+
}
112+
113+
LogDebug($"Invoking custom method: {customMethod.Name}");
114+
115+
var customArgs = __args.Skip(1).ToArray();
116+
customMethod.Invoke(customClass, customArgs);
117+
118+
return false;
119+
}
120+
catch (Exception ex)
121+
{
122+
LogError($"Error in PrefixVoid: {ex.Message}");
123+
return true;
124+
}
125+
}
126+
127+
[HarmonyPriority(Priority.High)]
128+
public static bool PrefixWithReturn(
129+
MethodBase __originalMethod,
130+
object __instance,
131+
ref object? __result,
132+
object[] __args)
133+
{
134+
try
135+
{
136+
if (__args == null || __args.Length == 0 || __args[0] is not Enum enumValue)
137+
return true;
138+
139+
var registry = RegistryManager.GetRegistry(enumValue.GetType());
140+
141+
var customClass = registry.Get(enumValue);
142+
if (customClass == null)
143+
return true;
144+
145+
var customType = customClass.GetType();
146+
var customMethods = customType.GetMethods(
147+
BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic);
148+
149+
var originalParams = __originalMethod.GetParameters().Skip(1).ToArray();
150+
151+
var customMethod = customMethods.FirstOrDefault(m =>
152+
m.Name == __originalMethod.Name &&
153+
IsParameterCompatible(originalParams, m.GetParameters()));
154+
155+
if (customMethod == null)
37156
{
38-
method = method,
39-
methodName = method.Name,
40-
argumentTypes = method.GetParameters().Select(p => p.ParameterType).ToArray(),
41-
42-
});
43-
processor.Patch();
44-
45-
xyz.Add(processor);
157+
LogDebug($"No matching custom method found for {__originalMethod.Name} in {customType.Name}");
158+
return true;
159+
}
160+
161+
LogDebug($"Invoking custom method: {customMethod.Name}");
162+
163+
var customArgs = __args.Skip(1).ToArray();
164+
__result = customMethod.Invoke(customClass, customArgs);
165+
166+
return false;
167+
}
168+
catch (Exception ex)
169+
{
170+
LogError($"Error in PrefixWithReturn: {ex.Message}");
171+
return true;
46172
}
47173
}
174+
175+
private static bool IsCompatible(ParameterInfo info1, ParameterInfo info2)
176+
{
177+
if (info1 == null || info2 == null)
178+
return false;
179+
180+
return info1.ParameterType.IsAssignableFrom(info2.ParameterType) ||
181+
info2.ParameterType.IsAssignableFrom(info1.ParameterType);
182+
}
183+
184+
private static bool IsParameterCompatible(ParameterInfo[] originalParams, ParameterInfo[] customParams)
185+
{
186+
if (originalParams == null || customParams == null)
187+
return false;
188+
189+
if (originalParams.Length != customParams.Length)
190+
return false;
191+
192+
return !originalParams.Where((t, i) => !IsCompatible(t, customParams[i])).Any();
193+
}
48194
}

COTL_API/HarmonyUtils/Registry.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
namespace COTL_API.HarmonyUtils;
2+
3+
public class Registry<T, V> : Dictionary<T, V>
4+
where T : Enum
5+
where V : class;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace COTL_API.HarmonyUtils;
2+
3+
public class RegistryManager
4+
{
5+
public static Dictionary<Type, Registry<Enum, object>> Registries = new();
6+
7+
public static Registry<Enum, object> GetRegistry(Type t)
8+
{
9+
if (Registries.TryGetValue(t, out var registry))
10+
return registry;
11+
12+
Registries[t] = new Registry<Enum, object>();
13+
return Registries[t];
14+
}
15+
}

COTL_API/Plugin.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using COTL_API.CustomSettings.Elements;
99
using COTL_API.CustomSkins;
1010
using COTL_API.Debug;
11+
using COTL_API.HarmonyUtils;
1112
using COTL_API.Saves;
1213
using HarmonyLib;
1314
using I2.Loc;
@@ -27,6 +28,7 @@ public class Plugin : BaseUnityPlugin
2728
internal static Dropdown? GoatFleeceBleatSettings;
2829

2930
internal static Dropdown? CustomPlayerSpineSettings;
31+
internal static Patcher _patcher = new();
3032
internal readonly Harmony _harmony = new(MyPluginInfo.PLUGIN_GUID);
3133

3234
internal readonly ModdedSaveData<ApiData> APIData = new(MyPluginInfo.PLUGIN_GUID)
@@ -73,6 +75,8 @@ private void Awake()
7375
Instance = this;
7476
Logger = base.Logger;
7577

78+
_patcher.PatchAll(typeof(InventoryItem));
79+
7680
PluginPath = Path.GetDirectoryName(Info.Location) ?? string.Empty;
7781

7882
ModdedSaveManager.RegisterModdedSave(ModdedSettingsData);

0 commit comments

Comments
 (0)