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
{
///
/// Dependency Injection container used by NitroxPatcher only.
///
private static IContainer container;
private static readonly Harmony harmony = new("com.nitroxmod.harmony");
private static bool isApplied;
///
/// Applies all the dynamic patches defined in Patches namespace.
/// Persistent patches are applied when the game initializes (i.e. before main menu).
/// See .
///
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())
{
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;
}
///
/// Removes all the dynamic patches defined by .
///
/// 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.
///
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())
{
Log.Debug($"Restoring dynamic patch {patch.GetType().Name}");
patch.Restore(harmony);
}
isApplied = false;
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Initialize()
{
Optional.ApplyHasValueCondition(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>())
{
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();
Log.Info("Behaviours applied.");
}
}