first commit
This commit is contained in:
161
NitroxPatcher/Patcher.cs
Normal file
161
NitroxPatcher/Patcher.cs
Normal file
@@ -0,0 +1,161 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Autofac;
|
||||
using HarmonyLib;
|
||||
using HarmonyLib.Tools;
|
||||
using NitroxClient;
|
||||
using NitroxClient.MonoBehaviours;
|
||||
using NitroxModel.Core;
|
||||
using NitroxModel.DataStructures.Util;
|
||||
using NitroxModel.Helper;
|
||||
using NitroxPatcher.Modules;
|
||||
using NitroxPatcher.Patches;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NitroxPatcher;
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "DIMA001:Dependency Injection container is used directly")]
|
||||
internal static class Patcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Dependency Injection container used by NitroxPatcher only.
|
||||
/// </summary>
|
||||
private static IContainer container;
|
||||
|
||||
private static readonly Harmony harmony = new("com.nitroxmod.harmony");
|
||||
private static bool isApplied;
|
||||
|
||||
/// <summary>
|
||||
/// Applies all the dynamic patches defined in Patches namespace.
|
||||
/// Persistent patches are applied when the game initializes (i.e. before main menu).
|
||||
/// See <see cref="Patcher.Initialize"/>.
|
||||
/// </summary>
|
||||
public static void Apply()
|
||||
{
|
||||
Validate.NotNull(container, "No patches have been discovered yet! Run Execute() first.");
|
||||
if (isApplied)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (IDynamicPatch patch in container.Resolve<IDynamicPatch[]>())
|
||||
{
|
||||
Log.Debug($"Applying dynamic patch {patch.GetType().Name}");
|
||||
try
|
||||
{
|
||||
patch.Patch(harmony);
|
||||
}
|
||||
catch (HarmonyException e)
|
||||
{
|
||||
Exception innerMost = e;
|
||||
while (innerMost.InnerException != null)
|
||||
{
|
||||
innerMost = innerMost.InnerException;
|
||||
}
|
||||
Log.Error($"Error patching {patch.GetType().Name}{Environment.NewLine}{innerMost}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"Error patching {patch.GetType().Name}{Environment.NewLine}{e}");
|
||||
}
|
||||
}
|
||||
|
||||
isApplied = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all the dynamic patches defined by <see cref="NitroxPatcher"/>.
|
||||
/// <p/>
|
||||
/// If the player starts the main menu for the first time, or returns from a (multiplayer) session, get rid of all the
|
||||
/// patches if applicable.
|
||||
/// </summary>
|
||||
public static void Restore()
|
||||
{
|
||||
Validate.NotNull(container, "No patches have been discovered yet! Run Execute() first.");
|
||||
if (!isApplied)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (IDynamicPatch patch in container.Resolve<IDynamicPatch[]>())
|
||||
{
|
||||
Log.Debug($"Restoring dynamic patch {patch.GetType().Name}");
|
||||
patch.Restore(harmony);
|
||||
}
|
||||
|
||||
isApplied = false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void Initialize()
|
||||
{
|
||||
Optional.ApplyHasValueCondition<UnityEngine.Object>(o => (bool)o);
|
||||
|
||||
if (container != null)
|
||||
{
|
||||
throw new Exception($"Patches have already been detected! Call {nameof(Apply)} or {nameof(Restore)} instead.");
|
||||
}
|
||||
Log.Info("Registering dependencies");
|
||||
container = CreatePatchingContainer();
|
||||
try
|
||||
{
|
||||
NitroxServiceLocator.InitializeDependencyContainer(new ClientAutoFacRegistrar());
|
||||
}
|
||||
catch (ReflectionTypeLoadException ex)
|
||||
{
|
||||
Log.Error($"Failed to load one or more dependency types for Nitrox. Assembly: {ex.Types.FirstOrDefault()?.Assembly.FullName ?? "unknown"}");
|
||||
foreach (Exception loaderEx in ex.LoaderExceptions)
|
||||
{
|
||||
Log.Error(loaderEx);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error while initializing and loading dependencies.");
|
||||
throw;
|
||||
}
|
||||
|
||||
InitPatches();
|
||||
ApplyNitroxBehaviours();
|
||||
}
|
||||
|
||||
private static void InitPatches()
|
||||
{
|
||||
Log.Info("Patching Subnautica...");
|
||||
|
||||
// Enabling this creates a log file on your desktop (why there?), showing the emitted IL instructions.
|
||||
HarmonyFileLog.Enabled = false;
|
||||
|
||||
foreach (IPersistentPatch patch in container.Resolve<IEnumerable<IPersistentPatch>>())
|
||||
{
|
||||
Log.Debug($"Applying persistent patch {patch.GetType().Name}");
|
||||
patch.Patch(harmony);
|
||||
}
|
||||
|
||||
Multiplayer.OnBeforeMultiplayerStart += Apply;
|
||||
Multiplayer.OnAfterMultiplayerEnd += Restore;
|
||||
Log.Info("Completed patching");
|
||||
}
|
||||
|
||||
private static IContainer CreatePatchingContainer()
|
||||
{
|
||||
ContainerBuilder builder = new();
|
||||
builder.RegisterModule(new NitroxPatchesModule());
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
private static void ApplyNitroxBehaviours()
|
||||
{
|
||||
Log.Info("Applying Nitrox behaviours..");
|
||||
|
||||
GameObject nitroxRoot = new();
|
||||
nitroxRoot.name = "Nitrox";
|
||||
nitroxRoot.AddComponent<NitroxBootstrapper>();
|
||||
|
||||
Log.Info("Behaviours applied.");
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user