This repository has been archived by the owner on Oct 16, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add proper backport support, make debug logging even more verbose
- Loading branch information
Showing
18 changed files
with
297 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}."); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.