Decompiled source of InteropFixes v1.0.1

InteropFixes.dll

Decompiled 3 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("InteropFixes")]
[assembly: AssemblyDescription("Runtime compatibility fixes for Valheim mods")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyProduct("InteropFixes")]
[assembly: ComVisible(false)]
[assembly: Guid("D710223A-7033-4535-B9A8-C3E2D98D20DF")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace InteropFixes
{
	internal static class CharacterLifecycleCompatibility
	{
		private static bool _installed;

		internal static void TryInstall(Harmony harmony)
		{
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Expected O, but got Unknown
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Expected O, but got Unknown
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Expected O, but got Unknown
			//IL_0217: Expected O, but got Unknown
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Expected O, but got Unknown
			if (_installed)
			{
				return;
			}
			MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(ZNetScene), "Destroy", new Type[1] { typeof(GameObject) }, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(StatusEffect), "RemoveStartEffects", (Type[])null, (Type[])null);
			MethodInfo methodInfo3 = AccessTools.DeclaredMethod(typeof(Character), "OnDestroy", (Type[])null, (Type[])null);
			MethodInfo methodInfo4 = AccessTools.DeclaredMethod(typeof(ZNetScene), "OnDestroy", (Type[])null, (Type[])null);
			FieldInfo fieldInfo = AccessTools.Field(typeof(StatusEffect), "m_character");
			FieldInfo fieldInfo2 = AccessTools.Field(typeof(StatusEffect), "m_startEffectInstances");
			FieldInfo fieldInfo3 = AccessTools.Field(typeof(Character), "m_seman");
			MethodInfo methodInfo5 = AccessTools.DeclaredMethod(typeof(CharacterLifecycleCompatibility), "TrackDestroyPrefix", (Type[])null, (Type[])null);
			MethodInfo methodInfo6 = AccessTools.DeclaredMethod(typeof(CharacterLifecycleCompatibility), "RemoveStartEffectsPrefix", (Type[])null, (Type[])null);
			MethodInfo methodInfo7 = AccessTools.DeclaredMethod(typeof(CharacterLifecycleCompatibility), "CharacterOnDestroyPrefix", (Type[])null, (Type[])null);
			MethodInfo methodInfo8 = AccessTools.DeclaredMethod(typeof(CharacterLifecycleCompatibility), "CharacterOnDestroyFinalizer", (Type[])null, (Type[])null);
			MethodInfo methodInfo9 = AccessTools.DeclaredMethod(typeof(CharacterLifecycleCompatibility), "SceneOnDestroyPostfix", (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null || methodInfo3 == null || methodInfo4 == null || fieldInfo == null || fieldInfo2 == null || fieldInfo3 == null || methodInfo5 == null || methodInfo6 == null || methodInfo7 == null || methodInfo8 == null || methodInfo9 == null)
			{
				InteropFixesPlugin.ModLogger.LogWarning((object)"Character lifecycle compatibility skipped: one or more required Valheim methods or fields were not found.");
				return;
			}
			try
			{
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo5)
				{
					priority = 800
				}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(methodInfo6)
				{
					priority = 800
				}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(methodInfo7)
				{
					priority = 0
				}, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(methodInfo8)
				{
					priority = 0
				}, (HarmonyMethod)null);
				harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(methodInfo9), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_installed = true;
				InteropFixesPlugin.ModLogger.LogInfo((object)"Installed Character removal tracking, StatusEffect teardown guard, and Character.OnDestroy repair.");
			}
			catch (Exception ex)
			{
				SafeUnpatch(harmony, methodInfo, methodInfo5);
				SafeUnpatch(harmony, methodInfo2, methodInfo6);
				SafeUnpatch(harmony, methodInfo3, methodInfo7);
				SafeUnpatch(harmony, methodInfo3, methodInfo8);
				SafeUnpatch(harmony, methodInfo4, methodInfo9);
				CreatureRemovalTracker.Clear();
				bool flag = !IsPatchInstalled(methodInfo, methodInfo5) && !IsPatchInstalled(methodInfo2, methodInfo6) && !IsPatchInstalled(methodInfo3, methodInfo7) && !IsPatchInstalled(methodInfo3, methodInfo8) && !IsPatchInstalled(methodInfo4, methodInfo9);
				if (!flag)
				{
					try
					{
						harmony.UnpatchSelf();
					}
					catch
					{
					}
					flag = !IsPatchInstalled(methodInfo, methodInfo5) && !IsPatchInstalled(methodInfo2, methodInfo6) && !IsPatchInstalled(methodInfo3, methodInfo7) && !IsPatchInstalled(methodInfo3, methodInfo8) && !IsPatchInstalled(methodInfo4, methodInfo9);
				}
				if (flag)
				{
					InteropFixesPlugin.ModLogger.LogWarning((object)("Character lifecycle compatibility was rolled back after an installation failure: " + ex.Message));
				}
				else
				{
					InteropFixesPlugin.ModLogger.LogError((object)$"Character lifecycle compatibility installation failed and rollback was incomplete: {ex}");
				}
			}
		}

		internal static void ResetInstallationState()
		{
			_installed = false;
		}

		private static void TrackDestroyPrefix(GameObject __0)
		{
			CreatureRemovalTracker.Track(__0);
		}

		private static bool RemoveStartEffectsPrefix(Character ___m_character, ref GameObject[]? ___m_startEffectInstances)
		{
			if (!CreatureRemovalTracker.IsPending(___m_character))
			{
				return true;
			}
			GameObject[] array = ___m_startEffectInstances;
			if (array == null || !Object.op_Implicit((Object)(object)ZNetScene.instance))
			{
				return false;
			}
			GameObject[] array2 = array;
			foreach (GameObject val in array2)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					ZNetView component = val.GetComponent<ZNetView>();
					if (Object.op_Implicit((Object)(object)component) && component.IsValid())
					{
						component.ClaimOwnership();
						component.Destroy();
					}
					else
					{
						Object.Destroy((Object)(object)val);
					}
				}
			}
			___m_startEffectInstances = null;
			return false;
		}

		private static bool CharacterOnDestroyPrefix(Character __instance, SEMan? ___m_seman)
		{
			if (___m_seman != null)
			{
				return true;
			}
			try
			{
				CreatureRemovalTracker.RepairCharacterReferences(__instance);
			}
			catch (Exception arg)
			{
				InteropFixesPlugin.ModLogger.LogWarning((object)$"Failed to repair an uninitialized Character.OnDestroy: {arg}");
			}
			finally
			{
				CreatureRemovalTracker.Complete(__instance);
			}
			return false;
		}

		private static Exception? CharacterOnDestroyFinalizer(Character __instance, Exception? __exception)
		{
			bool flag = CreatureRemovalTracker.IsPending(__instance);
			try
			{
				if (flag || __exception != null)
				{
					CreatureRemovalTracker.RepairCharacterReferences(__instance);
				}
			}
			catch (Exception arg)
			{
				InteropFixesPlugin.ModLogger.LogWarning((object)$"Failed to repair Character.OnDestroy bookkeeping: {arg}");
			}
			finally
			{
				CreatureRemovalTracker.Complete(__instance);
			}
			return __exception;
		}

		private static void SceneOnDestroyPostfix()
		{
			CreatureRemovalTracker.Clear();
		}

		private static void SafeUnpatch(Harmony harmony, MethodBase original, MethodInfo patch)
		{
			try
			{
				harmony.Unpatch(original, patch);
			}
			catch
			{
			}
		}

		private static bool IsPatchInstalled(MethodBase original, MethodInfo expectedPatch)
		{
			Patches patchInfo = Harmony.GetPatchInfo(original);
			if (patchInfo == null)
			{
				return false;
			}
			if (!ContainsPatch(patchInfo.Prefixes, expectedPatch) && !ContainsPatch(patchInfo.Postfixes, expectedPatch))
			{
				return ContainsPatch(patchInfo.Finalizers, expectedPatch);
			}
			return true;
		}

		private static bool ContainsPatch(IEnumerable<Patch> patches, MethodInfo expectedPatch)
		{
			foreach (Patch patch in patches)
			{
				MethodInfo patchMethod = patch.PatchMethod;
				if ((object)patchMethod == expectedPatch || patchMethod.Equals(expectedPatch))
				{
					return true;
				}
			}
			return false;
		}
	}
	internal static class CreatureRemovalTracker
	{
		private sealed class PendingRemoval
		{
			internal Character Character { get; }

			internal float ExpiresAt { get; set; }

			internal PendingRemoval(Character character, float expiresAt)
			{
				Character = character;
				ExpiresAt = expiresAt;
			}
		}

		private const float PendingLifetimeSeconds = 15f;

		private const float PruneIntervalSeconds = 1f;

		private static readonly Dictionary<int, PendingRemoval> PendingByInstanceId = new Dictionary<int, PendingRemoval>();

		private static readonly List<int> ExpiredInstanceIds = new List<int>();

		private static float _nextPruneAt;

		internal static void Track(GameObject gameObject)
		{
			if (!Object.op_Implicit((Object)(object)gameObject))
			{
				return;
			}
			Character component = gameObject.GetComponent<Character>();
			if (Object.op_Implicit((Object)(object)component))
			{
				int instanceID = ((Object)component).GetInstanceID();
				float expiresAt = Time.realtimeSinceStartup + 15f;
				if (PendingByInstanceId.TryGetValue(instanceID, out PendingRemoval value) && value.Character == component)
				{
					value.ExpiresAt = expiresAt;
				}
				else
				{
					PendingByInstanceId[instanceID] = new PendingRemoval(component, expiresAt);
				}
			}
		}

		internal static bool IsPending(Character character)
		{
			if (character == null)
			{
				return false;
			}
			int instanceID = ((Object)character).GetInstanceID();
			if (PendingByInstanceId.TryGetValue(instanceID, out PendingRemoval value))
			{
				return value.Character == character;
			}
			return false;
		}

		internal static void Complete(Character character)
		{
			if (character != null)
			{
				int instanceID = ((Object)character).GetInstanceID();
				if (PendingByInstanceId.TryGetValue(instanceID, out PendingRemoval value) && value.Character == character)
				{
					PendingByInstanceId.Remove(instanceID);
				}
			}
		}

		internal static void Update()
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (PendingByInstanceId.Count == 0 || realtimeSinceStartup < _nextPruneAt)
			{
				return;
			}
			_nextPruneAt = realtimeSinceStartup + 1f;
			ExpiredInstanceIds.Clear();
			foreach (KeyValuePair<int, PendingRemoval> item in PendingByInstanceId)
			{
				if (realtimeSinceStartup >= item.Value.ExpiresAt)
				{
					ExpiredInstanceIds.Add(item.Key);
				}
			}
			foreach (int expiredInstanceId in ExpiredInstanceIds)
			{
				PendingByInstanceId.Remove(expiredInstanceId);
			}
			ExpiredInstanceIds.Clear();
		}

		internal static void RepairCharacterReferences(Character character)
		{
			if (character == null)
			{
				return;
			}
			List<Character> allCharacters = Character.GetAllCharacters();
			for (int num = allCharacters.Count - 1; num >= 0; num--)
			{
				if (allCharacters[num] == character)
				{
					allCharacters.RemoveAt(num);
				}
			}
			TryRemoveCharacterHud(character);
		}

		internal static void Clear()
		{
			PendingByInstanceId.Clear();
			ExpiredInstanceIds.Clear();
			_nextPruneAt = 0f;
		}

		private static void TryRemoveCharacterHud(Character character)
		{
			try
			{
				if (Object.op_Implicit((Object)(object)EnemyHud.instance))
				{
					EnemyHud.instance.RemoveCharacterHud(character);
				}
			}
			catch (Exception ex)
			{
				InteropFixesPlugin.ModLogger.LogDebug((object)("Failed to repair a removed Character HUD reference: " + ex.Message));
			}
		}
	}
	[BepInPlugin("sighsorry.InteropFixes", "InteropFixes", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class InteropFixesPlugin : BaseUnityPlugin
	{
		internal const string ModGuid = "sighsorry.InteropFixes";

		internal const string ModName = "InteropFixes";

		internal const string ModVersion = "1.0.1";

		internal const string MagicPluginGuid = "blacks7ar.MagicPlugin";

		private Harmony? _harmony;

		internal static ManualLogSource ModLogger { get; private set; }

		private void Awake()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			ModLogger = ((BaseUnityPlugin)this).Logger;
			_harmony = new Harmony("sighsorry.InteropFixes");
			try
			{
				CharacterLifecycleCompatibility.TryInstall(_harmony);
			}
			catch (Exception arg)
			{
				ModLogger.LogError((object)$"Character lifecycle compatibility initialization failed safely: {arg}");
			}
			try
			{
				MagicPluginCompatibility.TryInstall(_harmony);
			}
			catch (Exception arg2)
			{
				ModLogger.LogError((object)$"MagicPlugin compatibility initialization failed safely: {arg2}");
			}
		}

		private void Update()
		{
			CreatureRemovalTracker.Update();
		}

		private void OnDestroy()
		{
			CreatureRemovalTracker.Clear();
			if (_harmony == null)
			{
				return;
			}
			try
			{
				_harmony.UnpatchSelf();
				MagicPluginCompatibility.RestoreExternalPatchesAndReset();
				CharacterLifecycleCompatibility.ResetInstallationState();
			}
			catch (Exception arg)
			{
				ModLogger.LogError((object)$"InteropFixes shutdown could not fully restore its patch state: {arg}");
			}
			finally
			{
				_harmony = null;
			}
		}
	}
	internal static class MagicPluginCompatibility
	{
		private sealed class ProjectileConfiguration
		{
			internal ConfigEntry<float> MagicVelocity { get; }

			internal ConfigEntry<float> MagicAccuracy { get; }

			internal ConfigEntry<float> SurtlingStaffAoe { get; }

			internal ConfigEntry<float> EikthyrStaffAoe { get; }

			internal ConfigEntry<float> LightningStaffAoe { get; }

			internal ConfigEntry<float> PoisonStaffAoe { get; }

			internal ConfigEntry<float> ArcticStaffAoe { get; }

			internal ConfigEntry<float> ModersAoeRange { get; }

			internal ConfigEntry<float> ModersChopDamage { get; }

			internal ConfigEntry<float> ModersFrostDamage { get; }

			internal ConfigEntry<float> ModersPickaxeDamage { get; }

			internal ConfigEntry<float> ModersPierceDamage { get; }

			internal ConfigEntry<float> YagluthAoeRange { get; }

			internal ConfigEntry<float> YagluthBluntDamage { get; }

			internal ConfigEntry<float> YagluthChopDamage { get; }

			internal ConfigEntry<float> YagluthFireDamage { get; }

			internal ConfigEntry<float> YagluthPickaxeDamage { get; }

			private ProjectileConfiguration(ConfigEntry<float> magicVelocity, ConfigEntry<float> magicAccuracy, ConfigEntry<float> surtlingStaffAoe, ConfigEntry<float> eikthyrStaffAoe, ConfigEntry<float> lightningStaffAoe, ConfigEntry<float> poisonStaffAoe, ConfigEntry<float> arcticStaffAoe, ConfigEntry<float> modersAoeRange, ConfigEntry<float> modersChopDamage, ConfigEntry<float> modersFrostDamage, ConfigEntry<float> modersPickaxeDamage, ConfigEntry<float> modersPierceDamage, ConfigEntry<float> yagluthAoeRange, ConfigEntry<float> yagluthBluntDamage, ConfigEntry<float> yagluthChopDamage, ConfigEntry<float> yagluthFireDamage, ConfigEntry<float> yagluthPickaxeDamage)
			{
				MagicVelocity = magicVelocity;
				MagicAccuracy = magicAccuracy;
				SurtlingStaffAoe = surtlingStaffAoe;
				EikthyrStaffAoe = eikthyrStaffAoe;
				LightningStaffAoe = lightningStaffAoe;
				PoisonStaffAoe = poisonStaffAoe;
				ArcticStaffAoe = arcticStaffAoe;
				ModersAoeRange = modersAoeRange;
				ModersChopDamage = modersChopDamage;
				ModersFrostDamage = modersFrostDamage;
				ModersPickaxeDamage = modersPickaxeDamage;
				ModersPierceDamage = modersPierceDamage;
				YagluthAoeRange = yagluthAoeRange;
				YagluthBluntDamage = yagluthBluntDamage;
				YagluthChopDamage = yagluthChopDamage;
				YagluthFireDamage = yagluthFireDamage;
				YagluthPickaxeDamage = yagluthPickaxeDamage;
			}

			internal static bool TryCreate(Type? pluginType, Type? configType, out ProjectileConfiguration? configuration, out string missingField)
			{
				configuration = null;
				missingField = "MagicPlugin.Plugin";
				if (pluginType == null)
				{
					return false;
				}
				missingField = "MagicPlugin.Functions.ConfigSetup";
				if (configType == null)
				{
					return false;
				}
				if (!TryGetFloatConfig(pluginType, "_magicVelocity", out ConfigEntry<float> entry, out missingField) || !TryGetFloatConfig(pluginType, "_magciAccuracy", out ConfigEntry<float> entry2, out missingField) || !TryGetFloatConfig(configType, "_surtlingStaffAOE", out ConfigEntry<float> entry3, out missingField) || !TryGetFloatConfig(configType, "_eikthyrStaffAOE", out ConfigEntry<float> entry4, out missingField) || !TryGetFloatConfig(configType, "_lightningStaffAOE", out ConfigEntry<float> entry5, out missingField) || !TryGetFloatConfig(configType, "_poisonStaffAOE", out ConfigEntry<float> entry6, out missingField) || !TryGetFloatConfig(configType, "_arcticStaffAOE", out ConfigEntry<float> entry7, out missingField) || !TryGetFloatConfig(configType, "_modersAoeRange", out ConfigEntry<float> entry8, out missingField) || !TryGetFloatConfig(configType, "_modersChopDamage", out ConfigEntry<float> entry9, out missingField) || !TryGetFloatConfig(configType, "_modersFrostDamage", out ConfigEntry<float> entry10, out missingField) || !TryGetFloatConfig(configType, "_modersPickaxeDamage", out ConfigEntry<float> entry11, out missingField) || !TryGetFloatConfig(configType, "_modersPierceDamage", out ConfigEntry<float> entry12, out missingField) || !TryGetFloatConfig(configType, "_yagluthAoeRange", out ConfigEntry<float> entry13, out missingField) || !TryGetFloatConfig(configType, "_yagluthBluntDamage", out ConfigEntry<float> entry14, out missingField) || !TryGetFloatConfig(configType, "_yagluthChopDamage", out ConfigEntry<float> entry15, out missingField) || !TryGetFloatConfig(configType, "_yagluthFireDamage", out ConfigEntry<float> entry16, out missingField) || !TryGetFloatConfig(configType, "_yagluthPickaxeDamage", out ConfigEntry<float> entry17, out missingField))
				{
					return false;
				}
				configuration = new ProjectileConfiguration(entry, entry2, entry3, entry4, entry5, entry6, entry7, entry8, entry9, entry10, entry11, entry12, entry13, entry14, entry15, entry16, entry17);
				return true;
			}

			private static bool TryGetFloatConfig(Type ownerType, string fieldName, out ConfigEntry<float> entry, out string missingField)
			{
				missingField = ownerType.FullName + "." + fieldName;
				FieldInfo fieldInfo = AccessTools.Field(ownerType, fieldName);
				if (fieldInfo == null || !fieldInfo.IsStatic || fieldInfo.FieldType != typeof(ConfigEntry<float>) || !(fieldInfo.GetValue(null) is ConfigEntry<float> val))
				{
					entry = null;
					return false;
				}
				entry = val;
				return true;
			}
		}

		private sealed class PatchReplacement
		{
			internal MethodInfo Original { get; }

			internal Patch ExternalPatch { get; }

			internal HarmonyPatchType PatchType { get; }

			internal PatchReplacement(MethodInfo original, Patch externalPatch, HarmonyPatchType patchType)
			{
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				Original = original;
				ExternalPatch = externalPatch;
				PatchType = patchType;
			}
		}

		private const string PluginTypeName = "MagicPlugin.Plugin";

		private const string ConfigTypeName = "MagicPlugin.Functions.ConfigSetup";

		private const string AttackPatchTypeName = "MagicPlugin.Patches.AttackPatch";

		private const string PlayerPatchTypeName = "MagicPlugin.Patches.PlayerPatch";

		private static ProjectileConfiguration? _projectileConfiguration;

		private static bool _projectilePatchInstalled;

		private static bool _teleportPatchInstalled;

		private static bool _registryGuardInstalled;

		private static bool _reportedProjectileRuntimeError;

		private static bool _reportedTeleportRuntimeError;

		private static bool _reportedRegistryRuntimeError;

		private static PatchReplacement? _projectileReplacement;

		private static PatchReplacement? _teleportReplacement;

		internal static void TryInstall(Harmony harmony)
		{
			if (!Chainloader.PluginInfos.TryGetValue("blacks7ar.MagicPlugin", out var value))
			{
				InteropFixesPlugin.ModLogger.LogDebug((object)"MagicPlugin is not loaded; MagicPlugin-specific compatibility fixes are inactive.");
				return;
			}
			Assembly magicAssembly = ((object)value.Instance)?.GetType().Assembly ?? FindAssemblyWithType("MagicPlugin.Plugin");
			if (magicAssembly == null)
			{
				InteropFixesPlugin.ModLogger.LogWarning((object)"MagicPlugin compatibility skipped: the plugin assembly could not be resolved.");
				return;
			}
			string version = value.Metadata.Version?.ToString() ?? "unknown";
			TryInstallCapability("projectile compatibility", delegate
			{
				TryInstallProjectilePatch(harmony, magicAssembly, version);
			});
			TryInstallCapability("teleport compatibility", delegate
			{
				TryInstallTeleportPatch(harmony, magicAssembly, version);
			});
			TryInstallCapability("Character registry guard", delegate
			{
				TryInstallRegistryGuard(harmony, magicAssembly, version);
			});
		}

		internal static void RestoreExternalPatchesAndReset()
		{
			RestoreExternalPatch(_projectileReplacement);
			RestoreExternalPatch(_teleportReplacement);
			_projectileConfiguration = null;
			_projectileReplacement = null;
			_teleportReplacement = null;
			_projectilePatchInstalled = false;
			_teleportPatchInstalled = false;
			_registryGuardInstalled = false;
			_reportedProjectileRuntimeError = false;
			_reportedTeleportRuntimeError = false;
			_reportedRegistryRuntimeError = false;
		}

		private static void TryInstallProjectilePatch(Harmony harmony, Assembly magicAssembly, string version)
		{
			if (_projectilePatchInstalled)
			{
				return;
			}
			MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(Attack), "FireProjectileBurst", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = magicAssembly.GetType("MagicPlugin.Patches.AttackPatch", throwOnError: false)?.GetMethod("FireProjectileBurst_Prefix", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Attack) }, null);
			if (methodInfo == null || methodInfo2 == null || methodInfo2.ReturnType != typeof(bool))
			{
				InteropFixesPlugin.ModLogger.LogWarning((object)"MagicPlugin projectile compatibility skipped: the expected FireProjectileBurst patch structure was not found.");
				return;
			}
			Patch val = FindOwnedPatch(methodInfo, (HarmonyPatchType)1, methodInfo2);
			if (val == null)
			{
				InteropFixesPlugin.ModLogger.LogWarning((object)"MagicPlugin projectile compatibility skipped: the exact MagicPlugin FireProjectileBurst prefix is not installed.");
				return;
			}
			Type? type = magicAssembly.GetType("MagicPlugin.Plugin", throwOnError: false);
			Type type2 = magicAssembly.GetType("MagicPlugin.Functions.ConfigSetup", throwOnError: false);
			if (!ProjectileConfiguration.TryCreate(type, type2, out ProjectileConfiguration configuration, out string missingField))
			{
				InteropFixesPlugin.ModLogger.LogWarning((object)("MagicPlugin projectile compatibility skipped: required float config '" + missingField + "' was not found."));
				return;
			}
			MethodInfo methodInfo3 = AccessTools.DeclaredMethod(typeof(MagicPluginCompatibility), "FireProjectileBurstPrefix", (Type[])null, (Type[])null);
			string failure = "the InteropFixes replacement prefix was not found.";
			if (methodInfo3 == null || !TryReplacePatch(harmony, methodInfo, val, methodInfo3, (HarmonyPatchType)1, out failure))
			{
				InteropFixesPlugin.ModLogger.LogWarning((object)("MagicPlugin projectile compatibility skipped: " + failure));
				return;
			}
			_projectileConfiguration = configuration;
			_projectileReplacement = new PatchReplacement(methodInfo, val, (HarmonyPatchType)1);
			_projectilePatchInstalled = true;
			InteropFixesPlugin.ModLogger.LogInfo((object)("Installed MagicPlugin FireProjectileBurst compatibility for MagicPlugin " + version + "."));
		}

		private static void TryInstallTeleportPatch(Harmony harmony, Assembly magicAssembly, string version)
		{
			if (_teleportPatchInstalled)
			{
				return;
			}
			MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(Player), "TeleportTo", new Type[3]
			{
				typeof(Vector3),
				typeof(Quaternion),
				typeof(bool)
			}, (Type[])null);
			MethodInfo methodInfo2 = magicAssembly.GetType("MagicPlugin.Patches.PlayerPatch", throwOnError: false)?.GetMethod("TeleportTo_Postfix", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[3]
			{
				typeof(Player),
				typeof(Vector3).MakeByRefType(),
				typeof(Quaternion).MakeByRefType()
			}, null);
			if (methodInfo == null || methodInfo2 == null || methodInfo2.ReturnType != typeof(void))
			{
				InteropFixesPlugin.ModLogger.LogWarning((object)"MagicPlugin teleport compatibility skipped: the expected Player.TeleportTo patch structure was not found.");
				return;
			}
			Patch val = FindOwnedPatch(methodInfo, (HarmonyPatchType)2, methodInfo2);
			if (val == null)
			{
				InteropFixesPlugin.ModLogger.LogWarning((object)"MagicPlugin teleport compatibility skipped: the exact MagicPlugin TeleportTo postfix is not installed.");
				return;
			}
			MethodInfo methodInfo3 = AccessTools.DeclaredMethod(typeof(MagicPluginCompatibility), "TeleportToPostfix", (Type[])null, (Type[])null);
			string failure = "the InteropFixes replacement postfix was not found.";
			if (methodInfo3 == null || !TryReplacePatch(harmony, methodInfo, val, methodInfo3, (HarmonyPatchType)2, out failure))
			{
				InteropFixesPlugin.ModLogger.LogWarning((object)("MagicPlugin teleport compatibility skipped: " + failure));
				return;
			}
			_teleportReplacement = new PatchReplacement(methodInfo, val, (HarmonyPatchType)2);
			_teleportPatchInstalled = true;
			InteropFixesPlugin.ModLogger.LogInfo((object)("Installed MagicPlugin safe teleport compatibility for MagicPlugin " + version + "."));
		}

		private static void TryInstallRegistryGuard(Harmony harmony, Assembly magicAssembly, string version)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected O, but got Unknown
			if (_registryGuardInstalled)
			{
				return;
			}
			MethodInfo methodInfo = magicAssembly.GetType("MagicPlugin.Plugin", throwOnError: false)?.GetMethod("Update", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
			MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(MagicPluginCompatibility), "MagicPluginUpdatePrefix", (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo.ReturnType != typeof(void) || methodInfo2 == null)
			{
				InteropFixesPlugin.ModLogger.LogWarning((object)"MagicPlugin Character registry guard skipped: MagicPlugin.Plugin.Update did not match the expected structure.");
				return;
			}
			try
			{
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2)
				{
					priority = 800
				}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_registryGuardInstalled = true;
				InteropFixesPlugin.ModLogger.LogInfo((object)("Installed MagicPlugin Character registry guard for MagicPlugin " + version + "."));
			}
			catch (Exception ex)
			{
				harmony.Unpatch((MethodBase)methodInfo, methodInfo2);
				InteropFixesPlugin.ModLogger.LogWarning((object)("MagicPlugin Character registry guard skipped after a patching failure: " + ex.Message));
			}
		}

		private static bool FireProjectileBurstPrefix(Attack __instance)
		{
			try
			{
				ProjectileConfiguration projectileConfiguration = _projectileConfiguration;
				if (projectileConfiguration != null)
				{
					ApplyFireProjectileBurstAdjustments(__instance, projectileConfiguration);
				}
			}
			catch (Exception ex)
			{
				if (!_reportedProjectileRuntimeError)
				{
					_reportedProjectileRuntimeError = true;
					InteropFixesPlugin.ModLogger.LogWarning((object)("MagicPlugin projectile compatibility failed while applying an adjustment: " + ex.Message));
				}
			}
			return true;
		}

		private static void ApplyFireProjectileBurstAdjustments(Attack attack, ProjectileConfiguration configuration)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Invalid comparison between Unknown and I4
			if (attack == null)
			{
				return;
			}
			Humanoid character = attack.m_character;
			Player val = (Player)(object)((character is Player) ? character : null);
			if (val == null)
			{
				return;
			}
			SharedData val2 = attack.m_weapon?.m_shared;
			if (val2 == null || (int)val2.m_skillType != 9)
			{
				return;
			}
			string text = val2.m_name ?? "";
			if (text.EndsWith("scepter", StringComparison.Ordinal) || text.Contains("flamestaff") || text.Contains("thunderstaff"))
			{
				return;
			}
			string ammoType = val2.m_ammoType;
			if ((ammoType != null && ammoType.Contains("ammo_spells")) || ((Object)(object)val2.m_secondaryAttack?.m_attackProjectile != (Object)null && ((Object)val2.m_secondaryAttack.m_attackProjectile).name == "BDS_DvergerStaffFire_clusterbomb_projectile"))
			{
				return;
			}
			if (text == "$bmp_arcticstaff" || text == "$item_stafficeshards")
			{
				attack.m_projectileVel *= 1.2f;
				attack.m_projectileVelMin *= 1.2f;
				attack.m_projectileAccuracy *= 0f;
				attack.m_projectileAccuracyMin *= 0f;
			}
			else
			{
				attack.m_projectileVel *= configuration.MagicVelocity.Value;
				attack.m_projectileVelMin *= configuration.MagicVelocity.Value;
				attack.m_projectileAccuracy *= configuration.MagicAccuracy.Value;
				attack.m_projectileAccuracyMin *= configuration.MagicAccuracy.Value;
			}
			GameObject attackProjectile = attack.m_attackProjectile;
			if (Object.op_Implicit((Object)(object)attackProjectile))
			{
				string obj = ((Object)attackProjectile).name ?? "";
				string text2 = obj.ToLowerInvariant();
				float skillFactor = ((Character)val).GetSkillFactor((SkillType)9);
				if (text2.Contains("surtlingstaff"))
				{
					ApplyProjectileAoe(attackProjectile, configuration.SurtlingStaffAoe, skillFactor);
				}
				if (text2.Contains("eikthyrsstaff"))
				{
					ApplyProjectileAoe(attackProjectile, configuration.EikthyrStaffAoe, skillFactor);
				}
				if (text2.Contains("lightningstaff"))
				{
					ApplyProjectileAoe(attackProjectile, configuration.LightningStaffAoe, skillFactor);
				}
				if (text2.Contains("poisonstaff"))
				{
					ApplyProjectileAoe(attackProjectile, configuration.PoisonStaffAoe, skillFactor);
				}
				if (text2.Contains("arcticstaff"))
				{
					ApplyProjectileAoe(attackProjectile, configuration.ArcticStaffAoe, skillFactor);
				}
				if (obj == "bmp_modersheritage_projectile")
				{
					ApplyModersHeritage(attackProjectile, skillFactor, configuration);
				}
				if (obj == "bmp_yagluthsheritage_projectile")
				{
					ApplyYagluthsHeritage(attackProjectile, skillFactor, configuration);
				}
			}
		}

		private static void ApplyProjectileAoe(GameObject projectilePrefab, ConfigEntry<float> aoeConfig, float elementalSkillFactor)
		{
			Projectile component = projectilePrefab.GetComponent<Projectile>();
			if (Object.op_Implicit((Object)(object)component))
			{
				component.m_aoe = aoeConfig.Value + 2f * elementalSkillFactor;
			}
		}

		private static void ApplyModersHeritage(GameObject projectilePrefab, float elementalSkillFactor, ProjectileConfiguration configuration)
		{
			Projectile val = TryGetSpawnedProjectile(projectilePrefab);
			if (Object.op_Implicit((Object)(object)val))
			{
				val.m_aoe = configuration.ModersAoeRange.Value + 2f * elementalSkillFactor;
				val.m_damage.m_chop = configuration.ModersChopDamage.Value;
				val.m_damage.m_frost = configuration.ModersFrostDamage.Value * (1f + elementalSkillFactor);
				val.m_damage.m_pickaxe = configuration.ModersPickaxeDamage.Value;
				val.m_damage.m_pierce = configuration.ModersPierceDamage.Value;
			}
		}

		private static void ApplyYagluthsHeritage(GameObject projectilePrefab, float elementalSkillFactor, ProjectileConfiguration configuration)
		{
			Projectile val = TryGetSpawnedProjectile(projectilePrefab);
			if (Object.op_Implicit((Object)(object)val))
			{
				val.m_aoe = configuration.YagluthAoeRange.Value + 2f * elementalSkillFactor;
				val.m_damage.m_blunt = configuration.YagluthBluntDamage.Value;
				val.m_damage.m_chop = configuration.YagluthChopDamage.Value;
				val.m_damage.m_fire = configuration.YagluthFireDamage.Value * (1f + elementalSkillFactor);
				val.m_damage.m_pickaxe = configuration.YagluthPickaxeDamage.Value;
			}
		}

		private static Projectile? TryGetSpawnedProjectile(GameObject projectilePrefab)
		{
			GameObject obj = projectilePrefab.GetComponent<Projectile>()?.m_spawnOnHit;
			SpawnAbility val = ((obj != null) ? obj.GetComponent<SpawnAbility>() : null);
			if (val?.m_spawnPrefab == null || val.m_spawnPrefab.Length == 0)
			{
				return null;
			}
			GameObject obj2 = val.m_spawnPrefab[0];
			if (obj2 == null)
			{
				return null;
			}
			return obj2.GetComponent<Projectile>();
		}

		private static void TeleportToPostfix(Player __instance, ref Vector3 pos, ref Quaternion rot)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				TeleportFollowingMagicSummons(__instance, pos, rot);
			}
			catch (Exception ex)
			{
				if (!_reportedTeleportRuntimeError)
				{
					_reportedTeleportRuntimeError = true;
					InteropFixesPlugin.ModLogger.LogWarning((object)("MagicPlugin teleport compatibility failed while moving followed summons: " + ex.Message));
				}
			}
		}

		private static void TeleportFollowingMagicSummons(Player player, Vector3 position, Quaternion rotation)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)player))
			{
				return;
			}
			GameObject gameObject = ((Component)player).gameObject;
			if (!Object.op_Implicit((Object)(object)gameObject))
			{
				return;
			}
			Vector3 position2 = position + ((Component)player).transform.forward;
			List<Character> allCharacters = Character.GetAllCharacters();
			for (int i = 0; i < allCharacters.Count; i++)
			{
				Character val = allCharacters[i];
				if (IsFollowedMagicSummon(val, gameObject))
				{
					Transform transform = ((Component)val).transform;
					transform.position = position2;
					transform.rotation = rotation;
				}
			}
		}

		private static bool IsFollowedMagicSummon(Character character, GameObject owner)
		{
			if (!Object.op_Implicit((Object)(object)character) || !Object.op_Implicit((Object)(object)((Component)character).gameObject))
			{
				return false;
			}
			ZNetView nview = character.m_nview;
			if (!Object.op_Implicit((Object)(object)nview) || !nview.IsValid())
			{
				return false;
			}
			if (!(((Object)((Component)character).gameObject).name ?? "").StartsWith("BMP_", StringComparison.Ordinal) || !character.IsTamed())
			{
				return false;
			}
			Tameable component = ((Component)character).GetComponent<Tameable>();
			MonsterAI val = (Object.op_Implicit((Object)(object)component) ? component.m_monsterAI : null);
			return (Object)(object)(Object.op_Implicit((Object)(object)val) ? val.GetFollowTarget() : null) == (Object)(object)owner;
		}

		private static void MagicPluginUpdatePrefix()
		{
			try
			{
				List<Character> allCharacters = Character.GetAllCharacters();
				for (int num = allCharacters.Count - 1; num >= 0; num--)
				{
					Character val = allCharacters[num];
					if (val == null || !Object.op_Implicit((Object)(object)val))
					{
						allCharacters.RemoveAt(num);
						if (val != null)
						{
							TryRemoveCharacterHud(val);
						}
					}
				}
			}
			catch (Exception ex)
			{
				ReportRegistryRuntimeError(ex);
			}
		}

		private static void TryRemoveCharacterHud(Character character)
		{
			try
			{
				if (Object.op_Implicit((Object)(object)EnemyHud.instance))
				{
					EnemyHud.instance.RemoveCharacterHud(character);
				}
			}
			catch (Exception ex)
			{
				ReportRegistryRuntimeError(ex);
			}
		}

		private static void ReportRegistryRuntimeError(Exception ex)
		{
			if (!_reportedRegistryRuntimeError)
			{
				_reportedRegistryRuntimeError = true;
				InteropFixesPlugin.ModLogger.LogWarning((object)("MagicPlugin Character registry guard failed while pruning stale references: " + ex.Message));
			}
		}

		private static Patch? FindOwnedPatch(MethodBase original, HarmonyPatchType patchType, MethodInfo expectedMethod)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Invalid comparison between Unknown and I4
			Patches patchInfo = Harmony.GetPatchInfo(original);
			if (patchInfo == null)
			{
				return null;
			}
			foreach (Patch item in (IEnumerable<Patch>)(((int)patchType == 1) ? patchInfo.Prefixes : patchInfo.Postfixes))
			{
				if (item.owner == "blacks7ar.MagicPlugin" && IsSameMethod(item.PatchMethod, expectedMethod))
				{
					return item;
				}
			}
			return null;
		}

		private static bool TryReplacePatch(Harmony harmony, MethodInfo original, Patch externalPatch, MethodInfo replacement, HarmonyPatchType patchType, out string failure)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Expected O, but got Unknown
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Invalid comparison between Unknown and I4
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			try
			{
				harmony.Unpatch((MethodBase)original, externalPatch.PatchMethod);
				flag = FindOwnedPatch(original, patchType, externalPatch.PatchMethod) == null;
				if (!flag)
				{
					failure = $"the exact external {patchType} could not be removed.";
					return false;
				}
				HarmonyMethod val = new HarmonyMethod(replacement)
				{
					priority = externalPatch.priority,
					before = externalPatch.before,
					after = externalPatch.after
				};
				if ((int)patchType == 1)
				{
					harmony.Patch((MethodBase)original, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				else
				{
					harmony.Patch((MethodBase)original, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				failure = "";
				return true;
			}
			catch (Exception ex)
			{
				try
				{
					harmony.Unpatch((MethodBase)original, replacement);
				}
				catch
				{
				}
				if (flag)
				{
					TryRestoreExternalPatch(original, externalPatch, patchType);
				}
				failure = "patch replacement failed (" + ex.Message + ").";
				return false;
			}
		}

		private static void TryRestoreExternalPatch(MethodInfo original, Patch externalPatch, HarmonyPatchType patchType)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Invalid comparison between Unknown and I4
			try
			{
				HarmonyMethod val = new HarmonyMethod(externalPatch.PatchMethod)
				{
					priority = externalPatch.priority,
					before = externalPatch.before,
					after = externalPatch.after
				};
				Harmony val2 = new Harmony(externalPatch.owner);
				if ((int)patchType == 1)
				{
					val2.Patch((MethodBase)original, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				else
				{
					val2.Patch((MethodBase)original, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
			catch (Exception arg)
			{
				InteropFixesPlugin.ModLogger.LogError((object)$"Failed to restore a MagicPlugin patch after compatibility installation failed: {arg}");
			}
		}

		private static void RestoreExternalPatch(PatchReplacement? replacement)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if (replacement == null)
			{
				return;
			}
			try
			{
				if (FindOwnedPatch(replacement.Original, replacement.PatchType, replacement.ExternalPatch.PatchMethod) == null)
				{
					TryRestoreExternalPatch(replacement.Original, replacement.ExternalPatch, replacement.PatchType);
				}
			}
			catch (Exception arg)
			{
				InteropFixesPlugin.ModLogger.LogError((object)$"Failed to restore a replaced MagicPlugin patch while InteropFixes was shutting down: {arg}");
			}
		}

		private static void TryInstallCapability(string capability, Action install)
		{
			try
			{
				install();
			}
			catch (Exception ex)
			{
				InteropFixesPlugin.ModLogger.LogWarning((object)("MagicPlugin " + capability + " skipped after a structural probe failed: " + ex.Message));
			}
		}

		private static bool IsSameMethod(MethodInfo left, MethodInfo right)
		{
			if ((object)left == right || left.Equals(right))
			{
				return true;
			}
			try
			{
				return left.Module.Equals(right.Module) && left.MetadataToken == right.MetadataToken;
			}
			catch
			{
				return false;
			}
		}

		private static Assembly? FindAssemblyWithType(string fullTypeName)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if (assembly.GetType(fullTypeName, throwOnError: false) != null)
				{
					return assembly;
				}
			}
			return null;
		}
	}
}