From 0fda28526fcfce0751b8979400e4a16f6a77df8d Mon Sep 17 00:00:00 2001 From: Cj <161484149+CJ-SPT@users.noreply.github.com> Date: Sun, 11 May 2025 15:52:14 -0400 Subject: [PATCH] Implement module patch abstraction and patch loader (#250) * Implement patch abstractions and patch loader using an interface * remove patch loader * rename patch class --- .../SPTarkov.Reflection/CodeWrapper/Code.cs | 46 ++++++ .../CodeWrapper/CodeGenerator.cs | 76 ++++++++++ .../CodeWrapper/CodeWithLabel.cs | 33 +++++ .../Patching/AbstractPatch.cs | 132 ++++++++++++++++++ .../Patching/Attributes.cs | 27 ++++ .../SPTarkov.Reflection.csproj | 26 ++++ .../SPTarkov.Server.Core.csproj | 1 + .../Modding/HarmonyBootstrapper.cs | 29 ---- SPTarkov.Server/Program.cs | 2 - SPTarkov.Server/SPTarkov.Server.csproj | 1 - server-csharp.sln | 7 + 11 files changed, 348 insertions(+), 32 deletions(-) create mode 100644 Libraries/SPTarkov.Reflection/CodeWrapper/Code.cs create mode 100644 Libraries/SPTarkov.Reflection/CodeWrapper/CodeGenerator.cs create mode 100644 Libraries/SPTarkov.Reflection/CodeWrapper/CodeWithLabel.cs create mode 100644 Libraries/SPTarkov.Reflection/Patching/AbstractPatch.cs create mode 100644 Libraries/SPTarkov.Reflection/Patching/Attributes.cs create mode 100644 Libraries/SPTarkov.Reflection/SPTarkov.Reflection.csproj delete mode 100644 SPTarkov.Server/Modding/HarmonyBootstrapper.cs diff --git a/Libraries/SPTarkov.Reflection/CodeWrapper/Code.cs b/Libraries/SPTarkov.Reflection/CodeWrapper/Code.cs new file mode 100644 index 00000000..87c8d832 --- /dev/null +++ b/Libraries/SPTarkov.Reflection/CodeWrapper/Code.cs @@ -0,0 +1,46 @@ +using System.Reflection.Emit; + +namespace SPTarkov.Reflection.CodeWrapper; + +public class Code +{ + public OpCode OpCode { get; } + public Type? CallerType { get; } + public object? OperandTarget { get; } + public Type[]? Parameters { get; } + public bool HasOperand { get; } + + public Code(OpCode opCode) + { + OpCode = opCode; + HasOperand = false; + } + + public Code(OpCode opCode, object operandTarget) + { + OpCode = opCode; + OperandTarget = operandTarget; + HasOperand = true; + } + + public Code(OpCode opCode, Type callerType) + { + OpCode = opCode; + CallerType = callerType; + HasOperand = true; + } + + public Code(OpCode opCode, Type callerType, object operandTarget, Type[] parameters = null) + { + OpCode = opCode; + CallerType = callerType; + OperandTarget = operandTarget; + Parameters = parameters; + HasOperand = true; + } + + public virtual Label? GetLabel() + { + return null; + } +} diff --git a/Libraries/SPTarkov.Reflection/CodeWrapper/CodeGenerator.cs b/Libraries/SPTarkov.Reflection/CodeWrapper/CodeGenerator.cs new file mode 100644 index 00000000..2c68965d --- /dev/null +++ b/Libraries/SPTarkov.Reflection/CodeWrapper/CodeGenerator.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection.Emit; +using HarmonyLib; + +namespace SPTarkov.Reflection.CodeWrapper; + +/// +/// Helper class to generate IL code for transpilers +/// +public class CodeGenerator +{ + public static List GenerateInstructions(List codes) + { + var list = new List(); + + foreach (Code code in codes) + { + list.Add(ParseCode(code)); + } + + return list; + } + + private static CodeInstruction ParseCode(Code code) + { + if (!code.HasOperand) + { + return new CodeInstruction(code.OpCode) { labels = GetLabelList(code) }; + } + + if (code.OpCode == OpCodes.Ldfld || code.OpCode == OpCodes.Ldflda || code.OpCode == OpCodes.Stfld) + { + return new CodeInstruction(code.OpCode, AccessTools.Field(code.CallerType, code.OperandTarget as string)) { labels = GetLabelList(code) }; + } + + if (code.OpCode == OpCodes.Call || code.OpCode == OpCodes.Callvirt) + { + return new CodeInstruction(code.OpCode, AccessTools.Method(code.CallerType, code.OperandTarget as string, code.Parameters)) { labels = GetLabelList(code) }; + } + + if (code.OpCode == OpCodes.Box) + { + return new CodeInstruction(code.OpCode, code.CallerType) { labels = GetLabelList(code) }; + } + + if (code.OpCode == OpCodes.Br || code.OpCode == OpCodes.Brfalse || code.OpCode == OpCodes.Brtrue || code.OpCode == OpCodes.Brtrue_S + || code.OpCode == OpCodes.Brfalse_S || code.OpCode == OpCodes.Br_S) + { + return new CodeInstruction(code.OpCode, code.OperandTarget) { labels = GetLabelList(code) }; + } + + if (code.OpCode == OpCodes.Ldftn) + { + return new CodeInstruction(code.OpCode, AccessTools.Method(code.CallerType, code.OperandTarget as string, code.Parameters)) { labels = GetLabelList(code) }; + } + + if (code.OpCode == OpCodes.Newobj) + { + return new CodeInstruction(code.OpCode, code.CallerType.GetConstructors().FirstOrDefault(x => x.GetParameters().Length == code.Parameters.Length)) { labels = GetLabelList(code) }; + } + + throw new ArgumentException($"Code with OpCode {code.OpCode.ToString()} is not supported."); + } + + private static List