Decompiled source of FriendlyFireDetector v1.0.1

plugins\FriendlyFireDetector\FriendlyFireDetector.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using Agents;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using FriendlyFireDetector.Config;
using FriendlyFireDetector.Managers;
using FriendlyFireDetector.UI;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes;
using Microsoft.CodeAnalysis;
using Player;
using SNetwork;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("FriendlyFireDetector")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("FriendlyFireDetector")]
[assembly: AssemblyTitle("FriendlyFireDetector")]
[assembly: AssemblyVersion("1.0.0.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 FriendlyFireDetector
{
	[BepInPlugin("com.gtfo.friendlyfiredetector", "FriendlyFireDetector", "1.0.1")]
	public sealed class Plugin : BasePlugin
	{
		public const string PluginGuid = "com.gtfo.friendlyfiredetector";

		public const string PluginName = "FriendlyFireDetector";

		public const string PluginVersion = "1.0.1";

		private Harmony? _harmony;

		internal static ManualLogSource Logger { get; private set; }

		public override void Load()
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			Logger = ((BasePlugin)this).Log;
			try
			{
				ConfigManager.Bind(new ConfigFile(Path.Combine(Paths.ConfigPath, "FriendlyFireDetector.cfg"), true));
				_harmony = new Harmony("com.gtfo.friendlyfiredetector");
				_harmony.PatchAll(typeof(Plugin).Assembly);
				Logger.LogInfo((object)"FriendlyFireDetector loaded.");
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(37, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("FriendlyFireDetector failed to load: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val);
				throw;
			}
		}
	}
}
namespace FriendlyFireDetector.UI
{
	internal static class MessageDisplay
	{
		public static string BuildFriendlyFireMessage(string attackerName, string victimName, float damage, float attackerTotalDamage)
		{
			string value = (ConfigManager.ShowAttackerName.Value ? attackerName : "Unknown");
			string value2 = (ConfigManager.ShowVictimName.Value ? victimName : "Unknown");
			if (ConfigManager.ShowDamageAmount.Value)
			{
				return $"<color=#FF3333><b>⚠ 内鬼播报:</b></color>\n{value}对{value2}造成了{damage:0.#}点伤害\n{value}累计造成{attackerTotalDamage:0.#}点伤害。";
			}
			return $"<color=#FF3333><b>⚠ 内鬼播报:</b></color>\n{value}对{value2}造成了伤害\n{value}累计造成友伤。";
		}

		public static void Show(string message)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			try
			{
				if (GuiManager.InteractionLayer != null)
				{
					GuiManager.InteractionLayer.SetTimedMessage(message, ConfigManager.MessageDuration.Value, (ePUIMessageStyle)3, 10);
					return;
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(34, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Native UI message display failed: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val);
			}
			TryShowChatFallback(message);
		}

		private static void TryShowChatFallback(string message)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			try
			{
				if (!((Object)(object)PlayerChatManager.Current == (Object)null))
				{
					PlayerChatManager.PostChatMessageLocaly(PlayerManager.GetLocalPlayerAgent().Owner, message, (SNet_Player)null);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(30, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Chat fallback display failed: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val);
			}
		}
	}
}
namespace FriendlyFireDetector.Patches
{
	[HarmonyPatch(typeof(Dam_PlayerDamageBase), "ReceiveBulletDamage")]
	internal static class ReceiveBulletDamagePatch
	{
		private static void Prefix(Dam_PlayerDamageBase __instance, out DamageSnapshot __state)
		{
			__state = FriendlyFireManager.Capture(__instance);
		}

		private static void Postfix(Dam_PlayerDamageBase __instance, pBulletDamageData data, DamageSnapshot __state)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			FriendlyFireManager.HandleSyncedDamage(__instance, data.source, __state, DamageKind.Bullet);
		}
	}
	[HarmonyPatch(typeof(Dam_PlayerDamageBase), "ReceiveMeleeDamage")]
	internal static class ReceiveMeleeDamagePatch
	{
		private static void Prefix(Dam_PlayerDamageBase __instance, out DamageSnapshot __state)
		{
			__state = FriendlyFireManager.Capture(__instance);
		}

		private static void Postfix(Dam_PlayerDamageBase __instance, pFullDamageData data, DamageSnapshot __state)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			FriendlyFireManager.HandleSyncedDamage(__instance, data.source, __state, DamageKind.Melee);
		}
	}
	[HarmonyPatch(typeof(Dam_PlayerDamageBase), "ReceiveFireDamage")]
	internal static class ReceiveFireDamagePatch
	{
		private static void Prefix(Dam_PlayerDamageBase __instance, out DamageSnapshot __state)
		{
			__state = FriendlyFireManager.Capture(__instance);
		}

		private static void Postfix(Dam_PlayerDamageBase __instance, pSmallDamageData data, DamageSnapshot __state)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			FriendlyFireManager.HandleSyncedDamage(__instance, data.source, __state, DamageKind.Fire);
		}
	}
	[HarmonyPatch(typeof(Dam_PlayerDamageBase), "ReceiveExplosionDamage")]
	internal static class ReceiveExplosionDamagePatch
	{
		private static void Prefix(Dam_PlayerDamageBase __instance, out DamageSnapshot __state)
		{
			__state = FriendlyFireManager.Capture(__instance);
		}

		private static void Postfix(Dam_PlayerDamageBase __instance, pExplosionDamageData data, DamageSnapshot __state)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			try
			{
				FriendlyFireManager.HandleExplosionDamageWithoutSource(__instance, __state);
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(30, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Explosion damage hook failed: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val);
			}
		}
	}
}
namespace FriendlyFireDetector.Managers
{
	internal enum DamageKind
	{
		Bullet,
		Melee,
		Fire,
		Explosion
	}
	internal readonly struct DamageSnapshot
	{
		public float HealthBefore { get; }

		public DamageSnapshot(float healthBefore)
		{
			HealthBefore = healthBefore;
		}
	}
	internal static class FriendlyFireManager
	{
		private const float MinimumReportableDamage = 0.01f;

		private static readonly Dictionary<ulong, float> s_friendlyDamageByAttacker = new Dictionary<ulong, float>();

		public static DamageSnapshot Capture(Dam_PlayerDamageBase damageBase)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			try
			{
				return new DamageSnapshot(((Dam_SyncedDamageBase)damageBase).Health);
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(35, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Failed to capture damage snapshot: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val);
				return new DamageSnapshot(0f);
			}
		}

		public static void HandleSyncedDamage(Dam_PlayerDamageBase victimDamageBase, pAgent source, DamageSnapshot snapshot, DamageKind kind)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			if (!ConfigManager.EnableMod.Value)
			{
				return;
			}
			try
			{
				float num = CalculateActualDamage(victimDamageBase, snapshot);
				Agent agent = default(Agent);
				if (!(num <= 0.01f) && ((pAgent)(ref source)).TryGet(ref agent))
				{
					PlayerAgent val = PlayerManager.TryAsPlayerAgent(agent);
					PlayerAgent val2 = PlayerManager.TryAsPlayerAgent(((Dam_SyncedDamageBase)victimDamageBase).GetBaseAgent());
					if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null) && !PlayerManager.IsSamePlayer(val, val2))
					{
						ReportFriendlyFire(val, val2, num, kind);
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val3 = new BepInExErrorLogInterpolatedStringHandler(32, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Friendly fire detection failed: ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val3);
			}
		}

		public static void HandleExplosionDamageWithoutSource(Dam_PlayerDamageBase victimDamageBase, DamageSnapshot snapshot)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			if (!ConfigManager.EnableMod.Value)
			{
				return;
			}
			float num = CalculateActualDamage(victimDamageBase, snapshot);
			if (!(num <= 0.01f))
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(91, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Explosion damage received by player, but pExplosionDamageData has no source agent. Damage: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(num, "0.#");
				}
				logger.LogInfo(val);
			}
		}

		private static float CalculateActualDamage(Dam_PlayerDamageBase damageBase, DamageSnapshot snapshot)
		{
			float health = ((Dam_SyncedDamageBase)damageBase).Health;
			return Math.Max(0f, snapshot.HealthBefore - health);
		}

		private static void ReportFriendlyFire(PlayerAgent attacker, PlayerAgent victim, float damage, DamageKind kind)
		{
			string displayName = PlayerManager.GetDisplayName(attacker);
			string displayName2 = PlayerManager.GetDisplayName(victim);
			string text = kind.ToString();
			float num = AddAttackerDamage(attacker, damage);
			Plugin.Logger.LogInfo((object)("Friendly damage detected:\nAttacker: " + displayName + "\nVictim: " + displayName2 + "\n" + $"Damage: {damage:0.#}\n" + $"TotalDamageByAttacker: {num:0.#}\n" + "Type: " + text));
			if (ConfigManager.EnableBroadcast.Value)
			{
				MessageDisplay.Show(MessageDisplay.BuildFriendlyFireMessage(displayName, displayName2, damage, num));
			}
		}

		private static float AddAttackerDamage(PlayerAgent attacker, float damage)
		{
			ulong stablePlayerKey = PlayerManager.GetStablePlayerKey(attacker);
			s_friendlyDamageByAttacker.TryGetValue(stablePlayerKey, out var value);
			value += damage;
			s_friendlyDamageByAttacker[stablePlayerKey] = value;
			return value;
		}
	}
	internal static class PlayerManager
	{
		public static ulong GetStablePlayerKey(PlayerAgent agent)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			try
			{
				SNet_Player owner = agent.Owner;
				if ((Object)(object)owner != (Object)null && owner.Lookup != 0L)
				{
					return owner.Lookup;
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(37, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Failed to resolve stable player key: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val);
			}
			return ((Agent)agent).GlobalID;
		}

		public static PlayerAgent? TryAsPlayerAgent(Agent? agent)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)agent == (Object)null || (int)agent.Type != 0)
			{
				return null;
			}
			try
			{
				return ((Il2CppObjectBase)agent).Cast<PlayerAgent>();
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(37, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Failed to cast Agent to PlayerAgent: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val);
				return null;
			}
		}

		public static bool IsSamePlayer(PlayerAgent left, PlayerAgent right)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			try
			{
				SNet_Player owner = left.Owner;
				SNet_Player owner2 = right.Owner;
				if ((Object)(object)owner != (Object)null && (Object)(object)owner2 != (Object)null)
				{
					return owner.Lookup == owner2.Lookup;
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(33, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Failed to compare player owners: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val);
			}
			if (left != right)
			{
				return ((Agent)left).GlobalID == ((Agent)right).GlobalID;
			}
			return true;
		}

		public static string GetDisplayName(PlayerAgent agent)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			try
			{
				SNet_Player owner = agent.Owner;
				if ((Object)(object)owner != (Object)null)
				{
					string nickName = owner.NickName;
					if (!string.IsNullOrWhiteSpace(nickName))
					{
						return nickName;
					}
					string name = owner.GetName();
					if (!string.IsNullOrWhiteSpace(name))
					{
						return name;
					}
				}
				string playerName = agent.PlayerName;
				if (!string.IsNullOrWhiteSpace(playerName))
				{
					return playerName;
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(31, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Failed to resolve player name: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				logger.LogError(val);
			}
			return $"Player_{((Agent)agent).GlobalID}";
		}
	}
}
namespace FriendlyFireDetector.Config
{
	internal static class ConfigManager
	{
		public static ConfigEntry<bool> EnableMod { get; private set; }

		public static ConfigEntry<bool> EnableBroadcast { get; private set; }

		public static ConfigEntry<bool> ShowDamageAmount { get; private set; }

		public static ConfigEntry<bool> ShowAttackerName { get; private set; }

		public static ConfigEntry<bool> ShowVictimName { get; private set; }

		public static ConfigEntry<float> MessageDuration { get; private set; }

		public static void Bind(ConfigFile config)
		{
			EnableMod = config.Bind<bool>("General", "EnableMod", true, "Enable or disable FriendlyFireDetector.");
			EnableBroadcast = config.Bind<bool>("General", "EnableBroadcast", true, "Show friendly fire alerts in game UI.");
			ShowDamageAmount = config.Bind<bool>("General", "ShowDamageAmount", true, "Include the actual health deducted in alerts.");
			ShowAttackerName = config.Bind<bool>("General", "ShowAttackerName", true, "Include attacker name in alerts.");
			ShowVictimName = config.Bind<bool>("General", "ShowVictimName", true, "Include victim name in alerts.");
			MessageDuration = config.Bind<float>("General", "MessageDuration", 5f, "Message display duration in seconds.");
		}
	}
}