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.");
}
}
}