162 lines
5.0 KiB
C#
162 lines
5.0 KiB
C#
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.");
|
|
}
|
|
}
|