Skip to content
This repository has been archived by the owner on Oct 16, 2024. It is now read-only.

Commit

Permalink
Add proper backport support, make debug logging even more verbose
Browse files Browse the repository at this point in the history
  • Loading branch information
misandrie committed Feb 18, 2024
1 parent 5770ec2 commit 5473500
Show file tree
Hide file tree
Showing 18 changed files with 297 additions and 58 deletions.
9 changes: 8 additions & 1 deletion Marsey/Config/MarseyConf.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ public static class MarseyConf

/// <see cref="Blackhole"/>
public static bool DisableREC;

/// <summary>
/// Enables backports and fixes for the game
/// </summary>
/// <see cref="Marsey.Game.Patches.Marseyports.MarseyPortMan"/>
public static bool Backports;

public static readonly Dictionary<string, Action<bool>> EnvVarMap = new Dictionary<string, Action<bool>>
{
Expand All @@ -68,6 +74,7 @@ public static class MarseyConf
{ "MARSEY_DISABLE_PRESENCE", value => KillRPC = value },
{ "MARSEY_DUMP_ASSEMBLIES", value => Dumper = value },
{ "MARSEY_JAMMER", value => JamDials = value },
{ "MARSEY_DISABLE_REC", value => DisableREC = value }
{ "MARSEY_DISABLE_REC", value => DisableREC = value },
{ "MARSEY_BACKPORTS", value => Backports = value}
};
}
42 changes: 0 additions & 42 deletions Marsey/Game/Patches/BindFix.cs

This file was deleted.

3 changes: 1 addition & 2 deletions Marsey/Game/Patches/HWID.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ public static void Force()

private static string GetForcedHWId()
{
string? hwid = Environment.GetEnvironmentVariable(HWIDEnv);
Envsey.CleanFlag(HWIDEnv);
string? hwid = Envsey.CleanFlag(HWIDEnv);
return hwid ?? string.Empty;
}

Expand Down
70 changes: 70 additions & 0 deletions Marsey/Game/Patches/Marseyports/Attributes/Targets.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
namespace Marsey.Game.Patches.Marseyports.Attributes;

/// <summary>
/// Target specific engine version
/// </summary>
/// <remarks>Mutually exclusive with BackportTargetEngineUntil and BackportTargetEngineAfter</remarks>
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class BackportTargetEngine : Attribute
{
public Version Ver { get; }

public BackportTargetEngine(string ver)
{
Ver = new Version(ver);

Type targetType = GetType();
if (IsDefined(targetType, typeof(BackportTargetEngineBefore)) || IsDefined(targetType, typeof(BackportTargetEngineAfter)))
throw new InvalidOperationException("Cannot apply BackportTargetEngine with BackportTargetEngineBefore or BackportTargetEngineAfter attributes.");
}
}

/// <summary>
/// Target engine versions below and including this value
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class BackportTargetEngineBefore : Attribute
{
public Version Ver { get; }

public BackportTargetEngineBefore(string ver)
{
Ver = new Version(ver);

Type targetType = GetType();
if (GetCustomAttribute(targetType, typeof(BackportTargetEngineAfter)) is BackportTargetEngineAfter afterAttr && afterAttr.Ver >= Ver)
throw new InvalidOperationException("The version in BackportTargetEngineBefore must be greater than the version in BackportTargetEngineAfter.");
}
}

/// <summary>
/// Target engine versions above and including this value
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class BackportTargetEngineAfter : Attribute
{
public Version Ver { get; }

public BackportTargetEngineAfter(string ver)
{
Ver = new Version(ver);

Type targetType = GetType();
if (GetCustomAttribute(targetType, typeof(BackportTargetEngineBefore)) is BackportTargetEngineBefore untilAttr && untilAttr.Ver <= Ver)
throw new InvalidOperationException("The version in BackportTargetEngineAfter must be less than the version in BackportTargetEngineBefore.");
}
}

/// <summary>
/// Target specific content packs with ID matching this
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class BackportTargetFork : Attribute
{
public string ForkID { get; }

public BackportTargetFork(string fork)
{
ForkID = fork;
}
}
50 changes: 50 additions & 0 deletions Marsey/Game/Patches/Marseyports/Fixes/BindFix.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System.Reflection;
using System.Reflection.Emit;
using HarmonyLib;
using Marsey.Game.Patches.Marseyports;
using Marsey.Game.Patches.Marseyports.Attributes;
using Marsey.Handbreak;
using Marsey.Stealthsey;

namespace Marsey.Game.Patches.Marseyports.Fixes;

// Fixes custom keybind saving issue causing the default keybind set to be mashed over the custom one
// Affects engines 210.0 to 210.1.0, fixed with 210.1.1
// https://media.discordapp.net/attachments/1132440600478756879/1208211732980301964/MalPR.png?ex=65e275dc&is=65d000dc&hm=d0e99b62cca1151e13a8e4af65cbe7b0e3c4f0d35e74965ab07640f87276df93&=&format=webp&quality=lossless
// Cred: https://github.com/space-wizards/RobustToolbox/pull/4903
[BackportTargetEngineAfter("210.0.0")]
[BackportTargetEngineBefore("210.1.0")]
public class BindFix : IBackport
{
public string TargetType { get; set; } = "Robust.Client.Input.InputManager";
public string TargetMethod { get; set; } = "LoadKeyFile";
public bool Content { get; set; } = false;
public MethodInfo? PatchMethodInfo { get; set; } = AccessTools.Method(typeof(BindFix), "Transpiler");
public HarmonyPatchType? PatchType { get; set; } = HarmonyPatchType.Transpiler;

public bool Patch()
{
Type Input = AccessTools.TypeByName("Robust.Client.Input.InputManager");
MethodInfo LKFmi = AccessTools.Method(Input, "LoadKeyFile");
MethodInfo BindFixTranspiler = AccessTools.Method(typeof(BindFix), "Transpiler");

return Manual.Patch(LKFmi, BindFixTranspiler, HarmonyPatchType.Transpiler);
}

public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
List<CodeInstruction> codes = [..instructions];

for (int i = 0; i < codes.Count - 1; i++)
{
if (codes[i].opcode != OpCodes.Ldarg_2 || codes[i + 1].opcode != OpCodes.Call ||
((MethodInfo)codes[i + 1].operand).Name != "RegisterBinding") continue;

codes.Insert(i + 1, new CodeInstruction(OpCodes.Ldc_I4_0)); // Erm its akshually false
codes.Insert(i + 2, new CodeInstruction(OpCodes.Ceq));
i += 2; // Skip the inserted instructions
}

return codes.AsEnumerable();
}
}
24 changes: 24 additions & 0 deletions Marsey/Game/Patches/Marseyports/IBackport.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Reflection;
using HarmonyLib;

namespace Marsey.Game.Patches.Marseyports;

/// <summary>
/// Base class for backport patches or fixes on certain engine versions or content packs
/// Marsey uses backports in case of breaking changes or some inconvenience caused by the game developers
/// Backports use two identifiers - fork id and engine version, as attributes
/// <seealso cref="Marsey.Game.Patches.Marseyports.Attributes"/>
/// </summary>
public interface IBackport
{
string TargetType { get; set; }
string TargetMethod { get; set; }
/// <summary>
/// Does this backport target the content pack
/// </summary>
bool Content { get; set; }
MethodInfo? PatchMethodInfo { get; set; }
HarmonyPatchType? PatchType { get; set; }

bool Patch();
}
92 changes: 92 additions & 0 deletions Marsey/Game/Patches/Marseyports/MarseyPortMan.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using System.Collections;
using System.Reflection;
using HarmonyLib;
using Marsey.Config;
using Marsey.Game.Patches.Marseyports.Attributes;
using Marsey.Misc;
using Marsey.Stealthsey;

namespace Marsey.Game.Patches.Marseyports;

/// <summary>
/// Manages code backports, fixes and patches
/// </summary>
public static class MarseyPortMan
{
private static readonly string _forkenvvar = "MARSEY_FORKID";
private static readonly string _engineenvvar = "MARSEY_ENGINE";
public static string fork = "";
public static Version engine = new Version();
private static IEnumerable<Type>? _backports;

public static void Initialize()
{
// https://www.youtube.com/watch?v=vmUGxXrlRmE
if (!MarseyConf.Backports) return;

fork = Envsey.CleanFlag(_forkenvvar)!;
engine = new Version(Envsey.CleanFlag(_engineenvvar)!);
MarseyLogger.Log(MarseyLogger.LogType.DEBG, "Backporter", $"Starting backporter against fork \"{fork}\", engine {engine}.");

IEnumerable<Type> backports = GetBackports();
MarseyLogger.Log(MarseyLogger.LogType.DEBG, "Backporter",$"Found {backports.Count()} available backports.");

_backports = backports.Where(ValidateBackport);
MarseyLogger.Log(MarseyLogger.LogType.DEBG, "Backporter",$"Found {_backports.Count()} valid backports.");
}

private static IEnumerable<Type> GetBackports()
{
Assembly assembly = Assembly.GetExecutingAssembly();
IEnumerable<Type> types = assembly.GetTypes();
return types.Where(t => t.Namespace != null && t.Namespace.StartsWith("Marsey.Game.Patches.Marseyports.Fixes"));
}

/// <summary>
/// Determines if this backport should be applied
/// </summary>
private static bool ValidateBackport(Type backport)
{
BackportTargetFork? BTF = backport.GetCustomAttribute<BackportTargetFork>();
BackportTargetEngine? BTE = backport.GetCustomAttribute<BackportTargetEngine>();
BackportTargetEngineAfter? BTEA = backport.GetCustomAttribute<BackportTargetEngineAfter>();
BackportTargetEngineBefore? BTEB = backport.GetCustomAttribute<BackportTargetEngineBefore>();

// Discard if fork id is set and does not match
if (BTF != null && BTF.ForkID != fork) return false;
// Discard if target engine is set and does not match
if (BTE != null && BTE.Ver.CompareTo(engine) != 0) return false;
// Discard if target engine after is set and version is below
if (BTEA != null && BTEA.Ver.CompareTo(engine) > 0) return false;
// Discard if target engine before is set and version is above
if (BTEB != null && BTEB.Ver.CompareTo(engine) < 0) return false;

return true;
}

public static void PatchBackports(bool Content = false)
{
if (_backports == null) return;

foreach (Type backport in _backports)
{
object instance = AccessTools.CreateInstance(backport);

// We are backporting fixes to engine for now
PropertyInfo contentProperty = AccessTools.Property(backport, "Content");
bool content = contentProperty != null && (bool)(contentProperty.GetValue(instance) ?? false);

switch (Content)
{
case false when content:
case true when !content:
continue;
}

MethodInfo patchMethod = AccessTools.Method(backport, "Patch");
patchMethod.Invoke(instance, null);

MarseyLogger.Log(MarseyLogger.LogType.DEBG, "Backporter", $"Backported {backport.Name}.");
}
}
}
5 changes: 2 additions & 3 deletions Marsey/Game/Resources/ResMan.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Marsey.Config;
using Marsey.Game.Patches.Marseyports;
using Marsey.Game.Resources.Dumper;
using Marsey.Game.Resources.Reflection;
using Marsey.Misc;
Expand All @@ -12,7 +13,6 @@ public static class ResMan
public static readonly string MarserializerFile = "rpacks.marsey";
private static readonly List<ResourcePack> _resourcePacks = [];
private static string? _fork;
private static readonly string _forkEnvVar = "MARSEY_FORKID";

/// <summary>
/// Executed by the loader
Expand All @@ -21,8 +21,7 @@ public static void Initialize()
{
ResourceTypes.Initialize();

_fork = Environment.GetEnvironmentVariable(_forkEnvVar) ?? "marsey";
Envsey.CleanFlag(_forkEnvVar);
_fork = MarseyPortMan.fork; // Peak spaghet moment

// If were dumping the game we dont want to dump our own respack now would we
if (MarseyConf.Dumper)
Expand Down
15 changes: 12 additions & 3 deletions Marsey/MarseyPatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Marsey.Game.Managers;
using Marsey.Game.Misc;
using Marsey.Game.Patches;
using Marsey.Game.Patches.Marseyports;
using Marsey.Game.Resources;
using Marsey.Game.Resources.Dumper;
using Marsey.PatchAssembly;
Expand Down Expand Up @@ -61,6 +62,11 @@ private MarseyPatcher(Assembly? robClientAssembly, ManualResetEvent mre)
Utility.SetupFlags();
HarmonyManager.Init(new Harmony(MarseyVars.Identifier));

MarseyLogger.Log(MarseyLogger.LogType.INFO, $"Marseyloader started, version {MarseyVars.MarseyVersion}");

// Init backport manager
MarseyPortMan.Initialize();

// Hide the loader
Hidesey.Initialize();

Expand All @@ -82,8 +88,8 @@ public void Preload()
Jammer.Patch();
Blackhole.Patch();

// Fix keybinds lol
BindFix.Patch();
// Apply engine backports
MarseyPortMan.PatchBackports();

// Start Resource Manager
ResMan.Initialize();
Expand Down Expand Up @@ -129,7 +135,7 @@ private void ExecPatcher()
private void Afterparty()
{
// TODO: Test if GameAssemblies.ClientInitialized works here
while (!Sentry.State) // Wait until EntryPoint is just about to start
while (!GameAssemblies.ClientInitialized()) // Wait until EntryPoint is just about to start
{
Thread.Sleep(125);
}
Expand All @@ -138,6 +144,9 @@ private void Afterparty()
if (Preclusion.State)
Preclusion.Fire();

// Apply content-related backports
MarseyPortMan.PatchBackports(true);

// Post-Load hidesey methods
Hidesey.Cleanup();
}
Expand Down
Loading

0 comments on commit 5473500

Please sign in to comment.