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

Commit

Permalink
Patchless
Browse files Browse the repository at this point in the history
  • Loading branch information
misandrie committed Oct 5, 2024
1 parent 4e4b9db commit 8eeea65
Show file tree
Hide file tree
Showing 14 changed files with 119 additions and 33 deletions.
8 changes: 7 additions & 1 deletion Marsey/Config/MarseyConf.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ public static class MarseyConf
/// </summary>
public static bool DisableAnyBackports;

/// <summary>
/// Don't run any patches
/// </summary>
public static bool Patchless;

/// <summary>
/// Reflect changes made here to the Dictionary in the launcher's Connector.cs
/// </summary>
Expand All @@ -97,7 +102,8 @@ public static class MarseyConf
{ "MARSEY_NO_ANY_BACKPORTS", value => DisableAnyBackports = value == "true" },
{ "MARSEY_FORKID", MarseyPortMan.SetForkID },
{ "MARSEY_ENGINE", MarseyPortMan.SetEngineVer },
{ "MARSEY_HIDE_LEVEL", value => MarseyHide = (HideLevel)Enum.Parse(typeof(HideLevel), value) }
{ "MARSEY_HIDE_LEVEL", value => MarseyHide = (HideLevel)Enum.Parse(typeof(HideLevel), value) },
{ "MARSEY_PATCHLESS", value => Patchless = value == "true"}
};

// Conf variables that do not go into the EnvVarMap go here
Expand Down
8 changes: 5 additions & 3 deletions Marsey/Game/Patcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Marsey.Game.Misc;
using Marsey.Patches;
using Marsey.Misc;
using Marsey.Stealthsey.Reflection;

namespace Marsey.Game;

Expand All @@ -25,9 +26,10 @@ public static void Patch<T>(List<T> patchlist) where T : IPatch
PatchAssembly(harmony, patch);
}
}

/// <inheritdoc cref="Patcher.Patch"/>
private static void PatchAssembly<T>(Harmony harmony, T patch) where T : IPatch
[Patching]
private static void PatchAssembly(Harmony harmony, IPatch patch)
{
AssemblyName assemblyName = patch.Asm.GetName();
MarseyLogger.Log(MarseyLogger.LogType.INFO, $"Patching {assemblyName}");
Expand Down Expand Up @@ -56,4 +58,4 @@ private static void HandlePatchException(string assemblyName, Exception e)

MarseyLogger.Log(MarseyLogger.LogType.FATL, errorMessage);
}
}
}
2 changes: 2 additions & 0 deletions Marsey/Game/Patches/Marseyports/MarseyPortMan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Marsey.Game.Patches.Marseyports.Attributes;
using Marsey.Misc;
using Marsey.Stealthsey;
using Marsey.Stealthsey.Reflection;

namespace Marsey.Game.Patches.Marseyports;

Expand Down Expand Up @@ -66,6 +67,7 @@ private static bool ValidateBackport(Type backport)
return true;
}

[Patching]
public static void PatchBackports(bool Content = false)
{
if (_backports == null) return;
Expand Down
51 changes: 38 additions & 13 deletions Marsey/Handbreak/Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public static class Helpers
Type? t = TypeFromQualifiedName(FullyQualifiedTypeName);

if (t != null) return GetMethod(t, MethodName, parameters);

MarseyLogger.Log(MarseyLogger.LogType.ERRO, $"{FullyQualifiedTypeName} not found.");
return null;
}
Expand All @@ -30,7 +30,7 @@ public static class Helpers
{
return AccessTools.Method(type, MethodName, parameters);
}

/// <summary>
/// Patches a method from the given pointers
/// </summary>
Expand All @@ -44,14 +44,15 @@ public static class Helpers
public static void PatchMethod(Type? targetType, string targetMethodName, Type? patchType, string patchMethodName, HarmonyPatchType patchingType, Type[]? targetMethodParameters = null, Type[]? patchMethodParameters = null)
{
ValidateTypes(targetType, patchType);

MethodInfo? targetMethod = GetAndValidateMethod(targetType, targetMethodName, targetMethodParameters, "target");
MethodInfo? patchMethod = GetAndValidateMethod(patchType, patchMethodName, patchMethodParameters, "patch");

if (Manual.Patch(targetMethod, patchMethod, patchingType))
LogPatchSuccess(patchingType, targetMethodName, patchMethodName);
}

#region Generics
public static void PatchGenericMethod(Type? targetType, string targetMethodName, Type? patchType, string patchMethodName, Type returnType, HarmonyPatchType patchingType)
{
ValidateTypes(targetType, patchType);
Expand All @@ -60,26 +61,50 @@ public static void PatchGenericMethod(Type? targetType, string targetMethodName,
MethodInfo? patchMethod = GetAndValidateMethod(patchType, patchMethodName, null, "patch");

MethodInfo? genericMethod = MakeGenericMethod(patchMethod, returnType);

if (Manual.Patch(targetMethod, genericMethod, patchingType))
LogPatchSuccess(patchingType, targetMethodName, patchMethodName);
}

public static void PatchGenericMethod(MethodInfo? target, MethodInfo? patch, Type returnType, HarmonyPatchType patchType)
public static void PatchGenericMethod(MethodInfo? target, Type targetReturnType, MethodInfo? patch, Type patchReturnType, HarmonyPatchType patchType)
{
target = MakeGenericMethod(target, targetReturnType);
patch = MakeGenericMethod(patch, patchReturnType);

if (Manual.Patch(target, patch, patchType))
LogPatchSuccess(patchType, target!.Name, patch!.Name);
}

public static void PatchGenericMethod(MethodInfo? target, MethodInfo? patch, Type patchReturnType, HarmonyPatchType patchType)
{
patch = MakeGenericMethod(patch, patchReturnType);

if (Manual.Patch(target, patch, patchType))
LogPatchSuccess(patchType, target!.Name, patch!.Name);
}

public static void PatchGenericMethod(MethodInfo target, Type targetReturnType, MethodInfo? patch, HarmonyPatchType patchType)
{
MethodInfo? generic = MakeGenericMethod(patch, returnType);

if (Manual.Patch(target, generic, patchType))
target = target.MakeGenericMethod([]);

if (Manual.Patch(target, patch, patchType))
LogPatchSuccess(patchType, target.Name, patch!.Name);
}

public static void PatchGenericMethod(MethodInfo? target, MethodInfo? patch, HarmonyPatchType patchType)
{
if (Manual.Patch(target, patch, patchType))
LogPatchSuccess(patchType, target!.Name, patch!.Name);

}


#endregion

private static void ValidateTypes(Type? targetType, Type? patchType)
{
if (targetType == null || patchType == null)
throw new HandBreakException($"Passed type is null. Target: {targetType}, patch: {patchType}");
}

private static void ValidateMethods(MethodInfo? target, MethodInfo? patch)
{
if (target == null || patch == null)
Expand All @@ -97,7 +122,7 @@ private static void ValidateMethods(MethodInfo? target, MethodInfo? patch)
private static MethodInfo? MakeGenericMethod(MethodInfo? method, Type returnType)
{
if (method != null) return method.MakeGenericMethod(returnType);

MarseyLogger.Log(MarseyLogger.LogType.ERRO, "Handbreak", $"Error making generic method");
return null;
}
Expand All @@ -106,4 +131,4 @@ private static void LogPatchSuccess(HarmonyPatchType patchingType, string target
{
MarseyLogger.Log(MarseyLogger.LogType.DEBG, "Handbreak", $"{patchingType}: Patched {targetMethodName} with {patchMethodName}.");
}
}
}
6 changes: 4 additions & 2 deletions Marsey/MarseyPatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using Marsey.Stealthsey;
using Marsey.Subversion;
using Marsey.Misc;
using Marsey.Stealthsey.Reflection;

namespace Marsey;

Expand Down Expand Up @@ -63,7 +64,7 @@ private MarseyPatcher(Assembly? robClientAssembly, ManualResetEvent mre)
Utility.ReadConf();
HarmonyManager.Init(new Harmony(MarseyVars.Identifier));

MarseyLogger.Log(MarseyLogger.LogType.INFO, $"Marseyloader started, version {MarseyVars.MarseyVersion}");
MarseyLogger.Log(MarseyLogger.LogType.INFO, $"Marseyloader started{(MarseyConf.Patchless ? " in patchless mode" : "")}, version {MarseyVars.MarseyVersion}");

// Init backport manager
MarseyPortMan.Initialize();
Expand All @@ -78,7 +79,8 @@ private MarseyPatcher(Assembly? robClientAssembly, ManualResetEvent mre)
}

// We might want to patch things before the loader has even a chance to execute anything
public void Preload()
[Patching]
private void Preload()
{
Sentry.Patch();

Expand Down
4 changes: 2 additions & 2 deletions Marsey/Stealthsey/Hidesey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public static void Initialize() // Finally, a patch loader that loads with a pat

_initialized = true;

HideLevelExec.Initialize();
HideseyAttributeManager.Initialize();

Load();
}
Expand Down Expand Up @@ -203,7 +203,7 @@ private static void Perjurize()
Helpers.PatchGenericMethod(
target: targetMethod,
patch: Lie,
returnType: returnType,
patchReturnType: returnType,
patchType: HarmonyPatchType.Postfix
);
}
Expand Down
2 changes: 2 additions & 0 deletions Marsey/Stealthsey/HideseyPatches.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public static void Lie<T>(ref T __result)
/// </summary>
public static bool Skip() => false;

public static bool SkipPatchless() => !MarseyConf.Patchless;

/// <summary>
/// Prefix patch that checks if MarseyHide matches or above the attributed HideLevelRequirement
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Marsey.Stealthsey.Reflection;
/// <summary>
/// Manages methods with the HideLevelRequirement attribute, patching them with a prefix.
/// </summary>
public static class HideLevelExec
public static class HideseyAttributeManager
{
/// <summary>
/// Initializes the HideLevelExec by finding and patching methods with HideLevelRequirement attributes.
Expand All @@ -21,40 +21,55 @@ public static void Initialize()
{
Assembly assembly = Assembly.GetExecutingAssembly();
IEnumerable<Type> types = assembly.GetTypes();
IEnumerable<Type> marseyTypes = types.Where(t => t.Namespace != null && t.Namespace.StartsWith("Marsey"));

IEnumerable<Type> marseyTypes = Assembly.GetExecutingAssembly().ExportedTypes;

foreach (Type type in marseyTypes)
{
CheckAndExecute(type);
}
}

/// <summary>
/// Checks each type for methods with HideLevelRequirement attributes and executes them if the hide level is met.
/// </summary>
private static void CheckAndExecute(Type type)
{
// Get all methods from the given type
IEnumerable<MethodInfo> methods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance);
IEnumerable<MethodInfo> methods = AccessTools.GetDeclaredMethods(type);

foreach (MethodInfo method in methods)
{
ExecIfLevelMet(method);
SetExecLevels(method);
SetPatchless(method);
}
}

/// <summary>
/// Executes the method if the current hide level meets or exceeds the required level specified by the HideLevelRequirement attribute.
/// </summary>
private static void ExecIfLevelMet(MethodInfo method)
private static void SetExecLevels(MethodInfo method)
{
HideLevelRequirement? hideLevelRequirement = method.GetCustomAttribute<HideLevelRequirement>();
HideLevelRestriction? hideLevelRestriction = method.GetCustomAttribute<HideLevelRestriction>();

if (hideLevelRequirement == null && hideLevelRestriction == null) return;

MethodInfo? prefix = typeof(HideseyPatches).GetMethod("LevelCheck", BindingFlags.Public | BindingFlags.Static);
Manual.Patch(method, prefix, HarmonyPatchType.Prefix);
}
}

private static void SetPatchless(MethodInfo method)
{
if (method.GetCustomAttribute<Patching>() is null) return;
if (method.IsGenericMethod) throw new InvalidOperationException("Patching attribute not allowed on generic methods.");

Console.WriteLine($"Trying to patch {method.Name}");

MethodInfo? prefix = AccessTools.Method(typeof(HideseyPatches), nameof(HideseyPatches.SkipPatchless));

Manual.Patch(method, prefix, HarmonyPatchType.Prefix);

Console.WriteLine($"Patched {method.Name} fine");
}
}
10 changes: 10 additions & 0 deletions Marsey/Stealthsey/Reflection/PatchingAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Reflection;

namespace Marsey.Stealthsey.Reflection;

/// <summary>
/// Method is patching the game in some form
/// <remarks> Does not execute if <see cref="Patching"/> is true </remarks>
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public sealed class Patching : Attribute;
1 change: 1 addition & 0 deletions Marsey/Subversion/Subverse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public static bool CheckSubversions()
/// This is done as we attach to the assembly loading function
/// </summary>
[HideLevelRestriction(HideLevel.Unconditional)]
[Patching]
public static void PatchSubverter()
{

Expand Down
3 changes: 2 additions & 1 deletion SS14.Launcher/Models/Connector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,8 @@ private void ConfigureMarsey()
{ "MARSEY_BACKPORTS", _cfg.GetCVar(CVars.Backports) ? "true" : null },
{ "MARSEY_NO_ANY_BACKPORTS", _cfg.GetCVar(CVars.DisableAnyEngineBackports) ? "true" : null },
{ "MARSEY_DISABLE_STRICT", _cfg.GetCVar(CVars.DisableStrict) ? "true" : null },
{ "MARSEY_DUMP_ASSEMBLIES", MarseyConf.Dumper ? "true" : null }
{ "MARSEY_DUMP_ASSEMBLIES", MarseyConf.Dumper ? "true" : null },
{ "MARSEY_PATCHLESS", _cfg.GetCVar(CVars.Patchless) ? "true" : null }
};

// Serialize environment variables
Expand Down
5 changes: 5 additions & 0 deletions SS14.Launcher/Models/Data/CVars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,11 @@ public static readonly CVarDef<bool> HasDismissedEarlyAccessWarning
/// Username used in guest mode
/// </summary>
public static readonly CVarDef<string> GuestUsername = CVarDef.Create("GuestUsername", "Guest");

/// <summary>
/// Do not patch anything in the game modules
/// </summary>
public static readonly CVarDef<bool> Patchless = CVarDef.Create("Patchless", false);
}

/// <summary>
Expand Down
10 changes: 10 additions & 0 deletions SS14.Launcher/ViewModels/MainWindowTabs/OptionsTabViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,16 @@ public bool DumpAssemblies
set => MarseyConf.Dumper = value;
}

public bool Patchless
{
get => Cfg.GetCVar(CVars.Patchless);
set
{
Cfg.SetCVar(CVars.Patchless, value);
Cfg.CommitConfig();
}
}

public bool ResourceOverride
{
get => Cfg.GetCVar(CVars.DisableStrict);
Expand Down
5 changes: 5 additions & 0 deletions SS14.Launcher/Views/MainWindowTabs/OptionsTabView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,11 @@
<ScrollViewer HorizontalScrollBarVisibility="Disabled">
<DockPanel>
<StackPanel Orientation="Vertical">
<CheckBox VerticalAlignment="Center" Margin="4" IsChecked="{Binding Patchless}">Run patchless</CheckBox>
<TextBlock VerticalAlignment="Center" TextWrapping="Wrap"
Text="Disables any patching except hiding harmony, essentially acting like a killswitch. Useful when game breaks due to the launcher itself."
Margin="8" />

<TextBlock Margin="4, 0" Text="Patches" Classes="NanoHeadingMedium" />
<CheckBox VerticalAlignment="Center" Margin="4" IsChecked="{Binding DumpAssemblies}">Dump Resources</CheckBox>
<TextBlock VerticalAlignment="Center" TextWrapping="Wrap"
Expand Down

0 comments on commit 8eeea65

Please sign in to comment.