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

Commit

Permalink
Integrate subversion
Browse files Browse the repository at this point in the history
  • Loading branch information
misandrie authored Dec 26, 2023
2 parents 824ab0e + f23f0f3 commit bfc22dd
Show file tree
Hide file tree
Showing 10 changed files with 62 additions and 145 deletions.
12 changes: 4 additions & 8 deletions Marsey/MarseyPatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public static void CreateInstance(Assembly? robClientAssembly)

_instance = new MarseyPatcher(robClientAssembly);
}


/// <exception cref="Exception">Excepts if Robust.Client assembly is null</exception>
private MarseyPatcher(Assembly? robClientAssembly)
{
if (robClientAssembly == null) throw new Exception("Robust.Client was null.");
Expand All @@ -62,18 +63,13 @@ private MarseyPatcher(Assembly? robClientAssembly)
///
/// Executed by the loader.
/// </summary>
/// <exception cref="Exception">Excepts if Robust.Client assembly is null</exception>
public void Boot()
{
// Preload marseypatches, if available
Marsyfier.Preload();

// Initialize subverter if enabled and present
if (MarseyVars.Subverter && Subverse.InitSubverter())
{
// Side-load custom code
Subverse.PatchSubverter();
}
// Side-load custom code
Subverse.PatchSubverter();

// Manage game assemblies
GameAssemblyManager.GetGameAssemblies(out Assembly? clientAss, out Assembly? robustSharedAss, out Assembly? clientSharedAss);
Expand Down
5 changes: 1 addition & 4 deletions Marsey/Misc/FileHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,7 @@ public static void LoadExactAssembly(string file)
}
catch (FileNotFoundException)
{
if (!file.EndsWith(Subverse.SubverterFile))
{
MarseyLogger.Log(MarseyLogger.LogType.DEBG, $"{file} could not be found");
}
MarseyLogger.Log(MarseyLogger.LogType.DEBG, $"{file} could not be found");
}
catch (PatchAssemblyException ex)
{
Expand Down
2 changes: 1 addition & 1 deletion Marsey/PatchAssembly/AssemblyFieldHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ private static void SetupLogger(Assembly patch)
/// <summary>
/// Get methodhandle for the entrypoint function in patch
/// </summary>
private static MethodInfo? GetEntry(Assembly patch, Type entryType)
public static MethodInfo? GetEntry(Assembly patch, Type entryType)
{
MethodInfo? entry = entryType.GetMethod("Entry", BindingFlags.Public | BindingFlags.Static);
return entry;
Expand Down
4 changes: 0 additions & 4 deletions Marsey/PatchAssembly/AssemblyInitializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,6 @@ private static void ProcessDataType(Assembly assembly, Type dataType)

break;
}
case "SubverterPatch" when Subverse.CheckSubverterPresent() && Subverse.CheckSubverterDuplicate(assembly):
MarseyLogger.Log(MarseyLogger.LogType.FATL,
$"{Path.GetFileName(assembly.Location)}: Tried to create a SubverterPatch with the same assembly as Subverter!");
return;
}

AssemblyFieldHandler.GetFields(dataType, out string name, out string description);
Expand Down
134 changes: 49 additions & 85 deletions Marsey/Subversion/Subverse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
using System.IO;
using System.Linq;
using System.Reflection;
using HarmonyLib;
using Marsey.Config;
using Marsey.GameAssembly;
using Marsey.Handbrake;
using Marsey.Stealthsey;
using Marsey.Misc;
using Marsey.PatchAssembly;
using Marsey.Serializer;
using Marsey.Stealthsey.Reflection;

namespace Marsey.Subversion;
Expand All @@ -17,63 +21,6 @@ namespace Marsey.Subversion;
/// </summary>
public static class Subverse
{
public static string SubverterFile = "Subverter.dll";
private static SubverterPatch? _subverter = null;

/// <summary>
/// Initializes the subverter library.
/// </summary>
/// <returns>True if the library was initialized successfully.</returns>
public static bool InitSubverter()
{
string path = Path.Combine(Directory.GetCurrentDirectory(), SubverterFile);
FileHandler.LoadExactAssembly(path);

List<SubverterPatch> patches = Subverter.GetSubverterPatches();
SubverterPatch? subverterPatch = patches.FirstOrDefault(p => p.Name == "Subverter");

if (subverterPatch == null) return false;

AssignSubverter(subverterPatch);
SetHidesey();
patches.Clear();

return true;
}

private static void SetHidesey()
{
if (_subverter == null) return;

Type? subverterPatchType = _subverter.Asm.GetType("SubverterPatch");
MethodInfo? hideMethod = typeof(Subverter).GetMethod("Hide", BindingFlags.NonPublic | BindingFlags.Static);
FieldInfo? hideDelegateField = subverterPatchType?.GetField("hideDelegate", BindingFlags.Public | BindingFlags.Static);

if (subverterPatchType == null || hideMethod == null || hideDelegateField == null)
{
List<string> missingComps = new List<string>();
if (subverterPatchType == null) missingComps.Add("subverterPatchType");
if (hideMethod == null) missingComps.Add("hideMethod");
if (hideDelegateField == null) missingComps.Add("hideDelegateField");

string missingCompStr = string.Join(", ", missingComps);

MarseyLogger.Log(MarseyLogger.LogType.FATL, $"Failed to connect patch to subverter. Missing components: {missingCompStr}.");
return;
}

try
{
Delegate logDelegate = Delegate.CreateDelegate(hideDelegateField.FieldType, hideMethod);
hideDelegateField.SetValue(null, logDelegate);
}
catch (Exception e)
{
MarseyLogger.Log(MarseyLogger.LogType.FATL, $"Failed to to assign hide delegate: {e.Message}");
}
}


/// <summary>
/// Enables subverter if any of the of the subverter patches are enabled
/// Used by the launcher to determine if it should load subversions
Expand All @@ -91,44 +38,61 @@ public static void CheckEnabled()

MarseyVars.Subverter = false;
}

/// <summary>
/// Check if a patch is loaded from the same place subverter is
/// Patches subverter ahead of everything else
/// This is done as we attach to the assembly loading function
/// </summary>
/// <returns></returns>
public static bool CheckSubverterDuplicate(SubverterPatch subverter)
{
if (CheckSubverterPresent())
return CheckSubverterDuplicate(subverter.Asm);

return false;
}

public static bool CheckSubverterDuplicate(Assembly assembly)
public static void PatchSubverter()
{
return assembly == _subverter?.Asm;
MethodInfo Target = AccessTools.Method(AccessTools.TypeByName("Robust.Shared.ContentPack.ModLoader"), "TryLoadModules");
MethodInfo Prefix = typeof(Subverse).GetMethod("Prefix", BindingFlags.NonPublic | BindingFlags.Static)!;

MarseyLogger.Log(MarseyLogger.LogType.DEBG, "Subversion", $"Hooking {Target.Name} with {Prefix.Name}");

Manual.Patch(Target, Prefix, HarmonyPatchType.Prefix);
}

/// <summary>
/// Check if subverter is already defined.
/// Used by the launcher in the plugins/patches tab.
/// </summary>
public static bool CheckSubverterPresent()
private static bool Prefix(object __instance)
{
return _subverter != null;
MarseyLogger.Log(MarseyLogger.LogType.DEBG, "Subversion", "Detour");
MethodInfo? loadGameAssemblyMethod = AccessTools.Method(AccessTools.TypeByName("Robust.Shared.ContentPack.BaseModLoader"), "InitMod");

foreach (string path in GetSubverters())
{
Assembly subverterAssembly = Assembly.LoadFrom(path);
MarseyLogger.Log(MarseyLogger.LogType.DEBG, "Subversion", $"Sideloading {path}");
loadGameAssemblyMethod.Invoke(__instance, new object[] { subverterAssembly });

MethodInfo? Entry = CheckEntry(subverterAssembly);
if (Entry != null)
Doorbreak.Enter(Entry);

Hidesey.HidePatch(subverterAssembly);
}

return true;
}

/// <summary>
/// Patches subverter ahead of everything else
/// This is done as we attach to the assembly loading function
/// </summary>
public static void PatchSubverter()
private static IEnumerable<string> GetSubverters()
{
if (_subverter != null) GamePatcher.Patch(new List<SubverterPatch>() { _subverter });
string directoryPath = Path.Combine(Directory.GetCurrentDirectory(), "Marsey");
MarseyLogger.Log(MarseyLogger.LogType.DEBG, "Subversion", $"Loading from {directoryPath}");

List<string> patches = Marserializer.Deserialize(new string[] { directoryPath }, Subverter.MarserializerFile) ?? new List<string>();

foreach (string filePath in patches)
{
yield return filePath;
}
}
private static void AssignSubverter(SubverterPatch subverter)

private static MethodInfo? CheckEntry(Assembly assembly)
{
_subverter = subverter;
Type? entryType = assembly.GetType("MarseyEntry");
if (entryType == null) return null;

MethodInfo? entryMethod = AssemblyFieldHandler.GetEntry(assembly, entryType);
return entryMethod != null ? entryMethod : null;
}
}
6 changes: 0 additions & 6 deletions Marsey/Subversion/Subverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,5 @@ public SubverterPatch(string asmpath, Assembly asm, string name, string desc)
Name = name;
Desc = desc;
Asm = asm;

if (Subverse.CheckSubverterDuplicate(this))
{
throw new PatchAssemblyException(
"Tried to create a SubverterPatch that is the same as Subverter! Remove Subverter.dll from the patches folder!");
}
}
}
14 changes: 6 additions & 8 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ Space Station 14 launcher with client-side modding/patching support.

* **Integration with the Harmony patching library.**
* * Full functionality regarding methods in client/shared content/engine assemblies.
* * Sideloading custom code using [Subverter](https://github.com/Subversionary/Subverter)
* * Sideloading custom code as part of the game
* * Win/Mac/Linux support
* * No injectors used, entirely based on reflection
* * Patches are hidden from game
* Enabled multiaccount
* Privacy changes
* * Tokens are updated only on connect or account switch to evade alt detection
* Locally change username for screenshots (This doesn't change your username in-game)
* * HWId spoofing
* * Forcibly disable Discord RPC
* * Wizden hub mirror set as default hub
* * Locally change username for screenshots (This doesn't change your username in-game)
* Marsey.

### Contributing
Expand All @@ -36,20 +40,14 @@ Marseyloader uses the [Harmony](https://github.com/pardeike/Harmony) patching li

Example patches can be found in the [ExampleMarseyPatch](https://github.com/ValidHunters/ExampleMarseyPatch) repository.

Additionally, custom code can be loaded to the game using the [Subverter](https://github.com/Subversionary/Subverter) helper patch.

### FAQ

#### Where do I ask for help?

Github issues or on the [discord server](https://discord.gg/5RjbK7EzEm).

#### How do I make a patch?
[Example Marseypatches](https://github.com/ValidHunters/ExampleMarseyPatch)

#### How do I enable subversion?
Compile [Subverter](https://github.com/Subversionary/Subverter), put "Subverter.dll" in the directory with the executable.

#### What is subversion for?
Subversion is used for adding your custom code (and not patching existing code) to the game, like custom commands and what not that can fully interact with the game as if they were part of the original code.

Expand Down
26 changes: 0 additions & 26 deletions SS14.Launcher/ViewModels/MainWindowTabs/PatchesTabViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,13 @@ namespace SS14.Launcher.ViewModels.MainWindowTabs
public class PatchesTabViewModel : MainWindowTabViewModel
{
public override string Name => "Plugins";
public bool SubverterPresent { get; set; }
public ObservableCollection<MarseyPatch> MarseyPatches { get; } = new ObservableCollection<MarseyPatch>();
public ObservableCollection<SubverterPatch> SubverterPatches { get; } = new ObservableCollection<SubverterPatch>();

public ICommand OpenPatchDirectoryCommand { get; }

public PatchesTabViewModel()
{
SubverterPresent = Subverse.CheckSubverterPresent();
OpenPatchDirectoryCommand = new RelayCommand(() => OpenPatchDirectory(MarseyVars.MarseyPatchFolder));
LoadPatches();
}
Expand All @@ -38,8 +36,6 @@ private void LoadPatches()
List<MarseyPatch> marseys = Marsyfier.GetMarseyPatches();
LoadPatchList(marseys, MarseyPatches, "marseypatches");

if (!SubverterPresent) return;

List<SubverterPatch> subverters = Subverter.GetSubverterPatches();
LoadPatchList(subverters, SubverterPatches, "subverterpatches");
}
Expand Down Expand Up @@ -79,28 +75,6 @@ public class PathToFileNameConverter : IValueConverter
}
}

public class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is bool booleanValue)
{
return booleanValue;
}
throw new InvalidOperationException("Invalid boolean value");
}

public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value == null) throw new ArgumentNullException(nameof(value));
if (value is bool visibilityValue)
{
return visibilityValue;
}
throw new InvalidOperationException("Invalid visibility value");
}
}

public class BooleanToPreloadConverter : IValueConverter
{
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
Expand Down
1 change: 0 additions & 1 deletion SS14.Launcher/ViewModels/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ public MainWindowViewModel()

HarmonyManager.Init(new Harmony(MarseyVars.Identifier));
Hidesey.Initialize();
Subverse.InitSubverter();

ServersTab = new ServerListTabViewModel(this);
NewsTab = new NewsTabViewModel();
Expand Down
3 changes: 1 addition & 2 deletions SS14.Launcher/Views/MainWindowTabs/PatchesTabView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
x:Class="SS14.Launcher.Views.MainWindowTabs.PatchesTabView"
Name="PatchesTab">
<UserControl.Resources>
<global:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<global:PathToFileNameConverter x:Key="PathToFileNameConverter"/>
<global:BooleanToPreloadConverter x:Key="BooleanToPreloadConverter"/>
</UserControl.Resources>
Expand Down Expand Up @@ -46,7 +45,7 @@
</ScrollViewer>
</TabItem>

<TabItem Header="Subverter" IsVisible="{Binding SubverterPresent, Converter={StaticResource BooleanToVisibilityConverter}}">
<TabItem Header="Subverter">
<ScrollViewer HorizontalScrollBarVisibility="Disabled">
<StackPanel>
<TextBlock Margin="4, 0" Text="Sideloading" Classes="NanoHeadingMedium" />
Expand Down

0 comments on commit bfc22dd

Please sign in to comment.