Decompiled source of DPSChecker v0.2.2

plugins/DpsChecker/DpsChecker.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using MycopunkShared;
using Pigeon.Movement;
using TMPro;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyFileVersion("0.2.0.0")]
[assembly: AssemblyInformationalVersion("0.2.2")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.2.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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;
		}
	}
}
internal static class AttackerResolver
{
	public static Player Resolve(IDamageSource source)
	{
		IDamageSource obj = source.GetBase();
		Player val = (Player)(object)((obj is Player) ? obj : null);
		if ((Object)(object)val != (Object)null)
		{
			return val;
		}
		Gun val2 = (Gun)(object)((source is Gun) ? source : null);
		if ((Object)(object)val2 != (Object)null && (Object)(object)val2.Player != (Object)null)
		{
			return val2.Player;
		}
		Throwable val3 = (Throwable)(object)((source is Throwable) ? source : null);
		if ((Object)(object)val3 != (Object)null && (Object)(object)val3.Player != (Object)null)
		{
			return val3.Player;
		}
		NetworkBehaviour val4 = (NetworkBehaviour)(object)((source is IGear) ? ((source is NetworkBehaviour) ? source : null) : null);
		if ((Object)(object)val4 == (Object)null || !val4.IsSpawned || GameManager.players == null)
		{
			return null;
		}
		foreach (Player player in GameManager.players)
		{
			if ((Object)(object)player != (Object)null && ((NetworkBehaviour)player).OwnerClientId == val4.OwnerClientId)
			{
				return player;
			}
		}
		return null;
	}
}
internal sealed class BurstLabel
{
	private TextMeshProUGUI label;

	private Canvas canvas;

	private string shown;

	public void Refresh(TextMeshProUGUI template, double damage, float remaining)
	{
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_0117: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_0141: Unknown result type (might be due to invalid IL or missing references)
		//IL_0196: Unknown result type (might be due to invalid IL or missing references)
		//IL_019b: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
		if (damage <= 0.0 || (Object)(object)template == (Object)null || (Object)(object)((Graphic)template).canvas == (Object)null || !((Behaviour)((Graphic)template).canvas.rootCanvas).isActiveAndEnabled || HudVisibility.ResultsOpen())
		{
			if ((Object)(object)label != (Object)null)
			{
				((Component)label).gameObject.SetActive(false);
			}
			return;
		}
		Canvas rootCanvas = ((Graphic)template).canvas.rootCanvas;
		if ((Object)(object)label == (Object)null || (Object)(object)canvas != (Object)(object)rootCanvas)
		{
			Clear();
			canvas = rootCanvas;
			label = GameHudText.Create(template, ((Component)rootCanvas).transform, "DPS Checker Burst Damage");
			((TMP_Text)label).alignment = (TextAlignmentOptions)4097;
			((TMP_Text)label).fontSize = 20f;
			((TMP_Text)label).fontStyle = (FontStyles)1;
			RectTransform rectTransform = ((TMP_Text)label).rectTransform;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0.5f, 0.5f);
			rectTransform.anchorMax = val;
			rectTransform.anchorMin = val;
			rectTransform.pivot = Vector2.zero;
			rectTransform.anchoredPosition = new Vector2(32f, -28f);
			rectTransform.sizeDelta = new Vector2(220f, 28f);
			((Transform)rectTransform).localScale = Vector3.one;
			((Transform)rectTransform).localRotation = Quaternion.identity;
			((TMP_Text)label).transform.SetAsLastSibling();
		}
		string text = Math.Ceiling(damage).ToString("0", CultureInfo.InvariantCulture);
		if (shown != text)
		{
			shown = text;
			((TMP_Text)label).text = text;
		}
		Color white = Color.white;
		white.a = Mathf.Clamp01(remaining / 0.8f);
		((Graphic)label).color = white;
		if (!((Component)label).gameObject.activeSelf)
		{
			((Component)label).gameObject.SetActive(true);
		}
	}

	public void Clear()
	{
		if ((Object)(object)label != (Object)null)
		{
			Object.Destroy((Object)(object)((Component)label).gameObject);
		}
		label = null;
		canvas = null;
		shown = null;
	}
}
internal struct CombatStats
{
	public float Dps;

	public float Max;

	public float TotalDamage;

	public int Kills;

	public bool TotalsKnown;

	public bool DamageKnown;

	public static string Compact(float value)
	{
		if (value < 1000f)
		{
			return value.ToString("0", CultureInfo.InvariantCulture);
		}
		if (value < 999950f)
		{
			return (value / 1000f).ToString("0.#", CultureInfo.InvariantCulture) + "k";
		}
		return (value / 1000000f).ToString("0.#", CultureInfo.InvariantCulture) + "m";
	}

	public string Format(bool dpsEstimated, bool totalsEstimated, bool local = false)
	{
		string text = (dpsEstimated ? "~" : "");
		string text2 = (totalsEstimated ? "~" : "");
		string text3 = ((totalsEstimated && (dpsEstimated || !TotalsKnown)) ? "~" : "");
		return "DPS " + text + Compact(Dps) + "  T " + text2 + Compact(TotalDamage) + "  K " + text3 + Kills;
	}
}
internal sealed class StatsText
{
	private string text;

	private float dps;

	private float totalDamage;

	private int kills;

	private int flags = -1;

	public string Get(CombatStats stats, bool dpsEstimated, bool totalsEstimated, bool local = false)
	{
		int num = (int)((dpsEstimated ? 1u : 0u) | (uint)(totalsEstimated ? 2 : 0)) | (local ? 4 : 0);
		if (text == null || !stats.Dps.Equals(dps) || !stats.TotalDamage.Equals(totalDamage) || stats.Kills != kills || num != flags)
		{
			text = stats.Format(dpsEstimated, totalsEstimated, local);
			dps = stats.Dps;
			totalDamage = stats.TotalDamage;
			kills = stats.Kills;
			flags = num;
		}
		return text;
	}
}
internal sealed class DamageRpcHooks : IDisposable
{
	private sealed class Hook
	{
		public RpcReceiveHandler Original;

		public RpcReceiveHandler Wrapper;

		public uint RpcId;
	}

	private const uint EnemyDamageRpcId = 3500258196u;

	private const uint TrainingDamageRpcId = 4264469793u;

	private const uint EnemyKillRpcId = 3954280513u;

	private readonly Dictionary<Type, Dictionary<uint, RpcReceiveHandler>> table;

	private readonly Dictionary<Type, Dictionary<uint, Hook>> hooks = new Dictionary<Type, Dictionary<uint, Hook>>();

	private readonly Action<NetworkBehaviour, FastBufferReader> observeDamage;

	private readonly Action<NetworkBehaviour, FastBufferReader> observeKill;

	private readonly ManualLogSource log;

	private bool installedLogged;

	private bool skippedLogged;

	private bool errorLogged;

	public DamageRpcHooks(ManualLogSource log, Action<NetworkBehaviour, FastBufferReader> observeDamage, Action<NetworkBehaviour, FastBufferReader> observeKill)
	{
		this.log = log;
		this.observeDamage = observeDamage;
		this.observeKill = observeKill;
		FieldInfo field = typeof(NetworkBehaviour).GetField("__rpc_func_table", BindingFlags.Static | BindingFlags.NonPublic);
		if (field == null)
		{
			throw new MissingFieldException("NetworkBehaviour.__rpc_func_table");
		}
		table = field.GetValue(null) as Dictionary<Type, Dictionary<uint, RpcReceiveHandler>>;
		if (table == null)
		{
			throw new InvalidOperationException("Unsupported RPC table type.");
		}
	}

	public void InstallExisting()
	{
		foreach (Type key in table.Keys)
		{
			Install(key);
		}
	}

	public void Install(Type type)
	{
		try
		{
			bool flag = typeof(EnemyPart).IsAssignableFrom(type);
			bool flag2 = typeof(DynamicTrainingTarget).IsAssignableFrom(type);
			if (!flag && !flag2)
			{
				return;
			}
			uint rpcId = (flag ? 3500258196u : 4264469793u);
			Type declaringType = (flag ? typeof(EnemyPart) : typeof(ObjectTarget));
			if (table.TryGetValue(type, out var value))
			{
				InstallHandler(type, value, rpcId, declaringType, observeDamage);
				if (typeof(EnemyCore).IsAssignableFrom(type))
				{
					InstallHandler(type, value, 3954280513u, typeof(EnemyPart), observeKill);
				}
			}
		}
		catch (Exception ex)
		{
			if (!errorLogged)
			{
				log.LogWarning((object)("DPS estimate: could not observe an enemy RPC: " + ex));
			}
			errorLogged = true;
		}
	}

	private void InstallHandler(Type type, Dictionary<uint, RpcReceiveHandler> handlers, uint rpcId, Type declaringType, Action<NetworkBehaviour, FastBufferReader> observe)
	{
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Expected O, but got Unknown
		if (!hooks.TryGetValue(type, out var value))
		{
			value = (hooks[type] = new Dictionary<uint, Hook>());
		}
		if (value.ContainsKey(rpcId) || !handlers.TryGetValue(rpcId, out var original))
		{
			return;
		}
		if (original == null || ((Delegate)(object)original).Method.DeclaringType != declaringType || ((Delegate)(object)original).Method.Name != "__rpc_handler_" + rpcId)
		{
			if (!skippedLogged)
			{
				log.LogWarning((object)"DPS estimate: unsupported or modified target RPC handler; estimation skipped for that handler.");
			}
			skippedLogged = true;
			return;
		}
		RpcReceiveHandler val = (RpcReceiveHandler)delegate(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				observe(target, reader);
			}
			catch (Exception ex)
			{
				if (!errorLogged)
				{
					log.LogWarning((object)("DPS estimate: RPC observation failed: " + ex));
				}
				errorLogged = true;
			}
			original.Invoke(target, reader, rpcParams);
		};
		value.Add(rpcId, new Hook
		{
			Original = original,
			Wrapper = val,
			RpcId = rpcId
		});
		handlers[rpcId] = val;
		if (!installedLogged)
		{
			log.LogInfo((object)"DPS estimate: registered target RPC observer.");
		}
		installedLogged = true;
	}

	public void Dispose()
	{
		foreach (KeyValuePair<Type, Dictionary<uint, Hook>> hook in hooks)
		{
			if (!table.TryGetValue(hook.Key, out var value))
			{
				continue;
			}
			foreach (Hook value3 in hook.Value.Values)
			{
				if (value.TryGetValue(value3.RpcId, out var value2) && (Delegate?)(object)value2 == (Delegate?)(object)value3.Wrapper)
				{
					value[value3.RpcId] = value3.Original;
				}
			}
		}
		hooks.Clear();
	}
}
internal static class DamageRpcReader
{
	public unsafe static void Read(FastBufferReader original, out DamageData damage, out NetworkBehaviourReference source)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: 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_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		FastBufferReader val = default(FastBufferReader);
		((FastBufferReader)(ref val))..ctor(original, (Allocator)1, ((FastBufferReader)(ref original)).Length, ((FastBufferReader)(ref original)).Position, (Allocator)2);
		try
		{
			((FastBufferReader)(ref val)).ReadValueSafe<DamageData>(ref damage, default(ForStructs));
			((FastBufferReader)(ref val)).ReadValueSafe<NetworkBehaviourReference>(ref source, default(ForNetworkSerializable));
		}
		finally
		{
			((IDisposable)(*(FastBufferReader*)(&val))/*cast due to .constrained prefix*/).Dispose();
		}
	}

	public unsafe static NetworkBehaviourReference ReadKillSource(FastBufferReader original)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: 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_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		FastBufferReader val = default(FastBufferReader);
		((FastBufferReader)(ref val))..ctor(original, (Allocator)1, ((FastBufferReader)(ref original)).Length, ((FastBufferReader)(ref original)).Position, (Allocator)2);
		try
		{
			NetworkBehaviourReference result = default(NetworkBehaviourReference);
			((FastBufferReader)(ref val)).ReadValueSafe<NetworkBehaviourReference>(ref result, default(ForNetworkSerializable));
			return result;
		}
		finally
		{
			((IDisposable)(*(FastBufferReader*)(&val))/*cast due to .constrained prefix*/).Dispose();
		}
	}
}
internal sealed class DpsNetwork : IDisposable
{
	private const string Message = "mycopunk.dpschecker.v1";

	private readonly ManualLogSource log;

	private readonly RemoteDps values = new RemoteDps();

	private readonly HashSet<ulong> peers = new HashSet<ulong>();

	private NetworkManager manager;

	private float nextSend;

	private bool failed;

	private bool registered;

	public DpsNetwork(ManualLogSource log)
	{
		this.log = log;
	}

	public bool TryGet(ulong id, out CombatStats value)
	{
		return values.TryGet(id, Time.unscaledTime, out value);
	}

	public void ClearValues()
	{
		values.Clear();
		nextSend = 0f;
	}

	public void Tick(CombatStats localStats)
	{
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d1: Expected O, but got Unknown
		NetworkManager singleton = NetworkManager.Singleton;
		if ((Object)(object)singleton != (Object)(object)manager || ((Object)(object)manager != (Object)null && !manager.IsListening))
		{
			Dispose();
			manager = (((Object)(object)singleton != (Object)null && singleton.IsListening) ? singleton : null);
			failed = false;
		}
		if ((Object)(object)manager == (Object)null || failed || !manager.IsListening || (!manager.IsServer && !manager.IsConnectedClient))
		{
			return;
		}
		try
		{
			if (manager.CustomMessagingManager == null)
			{
				return;
			}
			if (!registered)
			{
				manager.CustomMessagingManager.RegisterNamedMessageHandler("mycopunk.dpschecker.v1", new HandleNamedMessageDelegate(OnMessage));
				manager.OnClientDisconnectCallback += OnDisconnect;
				registered = true;
			}
			if (Time.unscaledTime < nextSend)
			{
				return;
			}
			if (!manager.IsServer)
			{
				nextSend = Time.unscaledTime + (values.TryGet(0uL, Time.unscaledTime, out var _) ? 0.25f : 1f);
				Send(0uL, manager.LocalClientId, localStats);
				return;
			}
			nextSend = Time.unscaledTime + 0.25f;
			foreach (ulong peer in peers)
			{
				Send(peer, manager.LocalClientId, localStats);
				foreach (ulong peer2 in peers)
				{
					if (peer2 != peer && values.TryGet(peer2, Time.unscaledTime, out var value2))
					{
						Send(peer, peer2, value2);
					}
				}
			}
		}
		catch (Exception ex)
		{
			log.LogWarning((object)("DPS sharing disabled for this session: " + ex));
			failed = true;
		}
	}

	private unsafe void Send(ulong recipient, ulong source, CombatStats value)
	{
		//IL_0011: 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_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: 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_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		FastBufferWriter val = default(FastBufferWriter);
		((FastBufferWriter)(ref val))..ctor(26, (Allocator)2, -1);
		try
		{
			((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref source, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe<float>(ref value.Dps, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe<float>(ref value.Max, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref value.Kills, default(ForPrimitives));
			byte b = (value.TotalsKnown ? ((byte)1) : ((byte)0));
			((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe<float>(ref value.TotalDamage, default(ForPrimitives));
			b = (value.DamageKnown ? ((byte)1) : ((byte)0));
			((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
			manager.CustomMessagingManager.SendNamedMessage("mycopunk.dpschecker.v1", recipient, val, (NetworkDelivery)1);
		}
		finally
		{
			((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
		}
	}

	private void OnMessage(ulong sender, FastBufferReader reader)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: 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_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			CombatStats value = default(CombatStats);
			ulong num = default(ulong);
			((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives));
			((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref value.Dps, default(ForPrimitives));
			if (((FastBufferReader)(ref reader)).Length - ((FastBufferReader)(ref reader)).Position >= 9)
			{
				((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref value.Max, default(ForPrimitives));
				((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref value.Kills, default(ForPrimitives));
				byte b = default(byte);
				((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives));
				if (b > 1)
				{
					return;
				}
				value.TotalsKnown = b == 1;
			}
			if (((FastBufferReader)(ref reader)).Length - ((FastBufferReader)(ref reader)).Position >= 5)
			{
				((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref value.TotalDamage, default(ForPrimitives));
				byte b2 = default(byte);
				((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b2, default(ForPrimitives));
				if (b2 > 1)
				{
					return;
				}
				value.DamageKnown = b2 == 1;
			}
			if (manager.IsServer)
			{
				num = sender;
				if (sender != manager.LocalClientId && manager.ConnectedClients.ContainsKey(sender) && values.Set(num, value, Time.unscaledTime))
				{
					peers.Add(sender);
				}
			}
			else if (sender == 0L && num != manager.LocalClientId)
			{
				values.Set(num, value, Time.unscaledTime);
			}
		}
		catch (Exception ex)
		{
			log.LogWarning((object)("Invalid DPS message: " + ex.Message));
		}
	}

	private void OnDisconnect(ulong id)
	{
		peers.Remove(id);
		values.Remove(id);
	}

	public void Dispose()
	{
		if ((Object)(object)manager != (Object)null && registered)
		{
			manager.OnClientDisconnectCallback -= OnDisconnect;
			if (manager.CustomMessagingManager != null)
			{
				manager.CustomMessagingManager.UnregisterNamedMessageHandler("mycopunk.dpschecker.v1");
			}
		}
		registered = false;
		peers.Clear();
		values.Clear();
		manager = null;
		nextSend = 0f;
	}
}
internal sealed class DpsWindow
{
	private readonly Queue<KeyValuePair<float, float>> hits = new Queue<KeyValuePair<float, float>>();

	private float total;

	private double burstDamage;

	private float lastBurstTime;

	private bool burstActive;

	public float Peak { get; private set; }

	public float TotalDamage { get; private set; }

	public int Kills { get; private set; }

	public void Add(float time, float damage)
	{
		if (!(damage <= 0f) && !float.IsNaN(damage) && !float.IsInfinity(damage))
		{
			Current(time);
			hits.Enqueue(new KeyValuePair<float, float>(time, damage));
			total += damage;
			TotalDamage += damage;
			if (total > Peak)
			{
				Peak = total;
			}
		}
	}

	public void AddKill()
	{
		Kills++;
	}

	public void AddBurst(float time, float damage)
	{
		if (!(damage <= 0f) && !float.IsNaN(damage) && !float.IsInfinity(damage))
		{
			if (!burstActive || time - lastBurstTime >= 4.5f)
			{
				burstDamage = 0.0;
			}
			burstDamage += damage;
			lastBurstTime = time;
			burstActive = true;
		}
	}

	public CombatStats Snapshot(float time)
	{
		return new CombatStats
		{
			Dps = Current(time),
			Max = Peak,
			TotalDamage = TotalDamage,
			Kills = Kills,
			TotalsKnown = true,
			DamageKnown = true
		};
	}

	public double Burst(float time, out float remaining)
	{
		remaining = (burstActive ? (4.5f - (time - lastBurstTime)) : 0f);
		if (remaining <= 0f)
		{
			burstDamage = 0.0;
			burstActive = false;
			return 0.0;
		}
		return burstDamage;
	}

	public float Current(float time)
	{
		while (hits.Count > 0 && time - hits.Peek().Key >= 1f)
		{
			total -= hits.Dequeue().Value;
		}
		if (hits.Count == 0)
		{
			total = 0f;
		}
		return total;
	}

	public void Clear()
	{
		hits.Clear();
		total = 0f;
		Peak = 0f;
		TotalDamage = 0f;
		Kills = 0;
		burstDamage = 0.0;
		lastBurstTime = 0f;
		burstActive = false;
	}
}
internal sealed class EstimatedDps
{
	private sealed class PlayerDamage
	{
		public readonly DpsWindow Window = new DpsWindow();
	}

	private readonly Dictionary<ulong, PlayerDamage> players = new Dictionary<ulong, PlayerDamage>();

	public void Add(ulong id, float time, float damage)
	{
		if (!(damage <= 0f) && !float.IsNaN(damage) && !float.IsInfinity(damage))
		{
			if (!players.TryGetValue(id, out var value))
			{
				value = (players[id] = new PlayerDamage());
			}
			value.Window.Add(time, damage);
		}
	}

	public void AddKill(ulong id)
	{
		if (!players.TryGetValue(id, out var value))
		{
			value = (players[id] = new PlayerDamage());
		}
		value.Window.AddKill();
	}

	public bool TryGet(ulong id, float time, out CombatStats stats)
	{
		if (players.TryGetValue(id, out var value))
		{
			stats = value.Window.Snapshot(time);
			return true;
		}
		stats = default(CombatStats);
		return false;
	}

	public void Clear()
	{
		players.Clear();
	}
}
internal sealed class LocalDpsLabel
{
	private TextMeshProUGUI template;

	private TextMeshProUGUI label;

	private readonly Vector3[] corners = (Vector3[])(object)new Vector3[4];

	private readonly StatsText text = new StatsText();

	public bool Visible
	{
		get
		{
			if ((Object)(object)label != (Object)null)
			{
				return ((Component)label).gameObject.activeInHierarchy;
			}
			return false;
		}
	}

	public void Refresh(TextMeshProUGUI title, EquippableHUDOverlay[] overlays, CombatStats stats)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_0202: Unknown result type (might be due to invalid IL or missing references)
		//IL_0233: Unknown result type (might be due to invalid IL or missing references)
		//IL_023a: Unknown result type (might be due to invalid IL or missing references)
		//IL_024c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0277: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02de: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Expected O, but got Unknown
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		RectTransform val = null;
		Vector3 val2 = Vector3.zero;
		bool flag = false;
		if ((Object)(object)title != (Object)null && overlays != null)
		{
			foreach (EquippableHUDOverlay val3 in overlays)
			{
				if ((Object)(object)val3 == (Object)null || !((Component)val3).gameObject.activeInHierarchy)
				{
					continue;
				}
				Transform transform = ((Component)val3).transform;
				RectTransform val4 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
				if (!((Object)(object)val4 == (Object)null) && ((Transform)val4).parent is RectTransform)
				{
					if ((Object)(object)val == (Object)null)
					{
						val = (RectTransform)((Transform)val4).parent;
					}
					val4.GetWorldCorners(corners);
					Vector3 val5 = ((Transform)val).InverseTransformPoint(corners[1]);
					if (!flag || val5.y > val2.y)
					{
						val2 = val5;
					}
					flag = true;
				}
			}
		}
		if ((Object)(object)val == (Object)null)
		{
			Clear();
			return;
		}
		if ((Object)(object)template != (Object)(object)title || (Object)(object)label == (Object)null || (Object)(object)((TMP_Text)label).transform.parent != (Object)(object)val)
		{
			Clear();
			template = title;
			label = Object.Instantiate<TextMeshProUGUI>(title, (Transform)(object)val, false);
			((Object)label).name = "DPS Checker Local";
			((Graphic)label).raycastTarget = false;
			((TMP_Text)label).richText = false;
			((TMP_Text)label).textWrappingMode = (TextWrappingModes)0;
			((TMP_Text)label).overflowMode = (TextOverflowModes)0;
			((TMP_Text)label).alignment = (TextAlignmentOptions)1025;
			((TMP_Text)label).enableAutoSizing = false;
			((TMP_Text)label).fontSize = Mathf.Max(((TMP_Text)title).fontSize * 1.2f, 20f);
			Outline val6 = ((Component)label).GetComponent<Outline>();
			if ((Object)(object)val6 == (Object)null)
			{
				val6 = ((Component)label).gameObject.AddComponent<Outline>();
			}
			((Shadow)val6).effectColor = new Color(0f, 0f, 0f, 0.9f);
			((Shadow)val6).effectDistance = new Vector2(1f, -1f);
			RectTransform rectTransform = ((TMP_Text)label).rectTransform;
			RectTransform rectTransform2 = ((TMP_Text)label).rectTransform;
			Vector2 val7 = default(Vector2);
			((Vector2)(ref val7))..ctor(0.5f, 0.5f);
			rectTransform2.anchorMax = val7;
			rectTransform.anchorMin = val7;
			((TMP_Text)label).rectTransform.pivot = Vector2.zero;
			((TMP_Text)label).rectTransform.sizeDelta = new Vector2(420f, ((TMP_Text)label).fontSize * 1.5f);
			((Component)label).gameObject.SetActive(true);
		}
		string text = this.text.Get(stats, dpsEstimated: false, totalsEstimated: false, local: true);
		GameHudText.SetText(label, text);
		((Graphic)label).color = Color.white;
		((Transform)((TMP_Text)label).rectTransform).localPosition = val2 + new Vector3(0f, 4f, 0f);
	}

	public void Clear()
	{
		if ((Object)(object)label != (Object)null)
		{
			Object.Destroy((Object)(object)((Component)label).gameObject);
		}
		label = null;
		template = null;
	}
}
[BepInPlugin("f1w3.mycopunk.dpschecker", "DPS Checker", "0.2.2")]
[BepInDependency("doku_macaron.mycopunk.macaroncore", "1.1.0")]
[BepInProcess("Mycopunk.exe")]
[MycoMod(/*Could not decode attribute arguments.*/)]
public sealed class Plugin : BaseUnityPlugin
{
	[HarmonyPatch(typeof(PlayerData), "OnLocalPlayerDamageTarget")]
	private static class LocalDamagePatch
	{
		[HarmonyPostfix]
		private static void Postfix(ref DamageCallbackData data)
		{
			OnDamage(ref data);
		}
	}

	[HarmonyPatch(typeof(PlayerData), "OnLocalPlayerKillTarget")]
	private static class LocalKillPatch
	{
		[HarmonyPostfix]
		private static void Postfix(ref KillCallbackData data)
		{
			OnLocalKill(ref data);
		}
	}

	[HarmonyPatch(typeof(TeammateHUD), "Initialize")]
	private static class TeammateInitializePatch
	{
		[HarmonyPostfix]
		private static void Postfix(TeammateHUD __instance, Player player)
		{
			OnTeammateInitialize(__instance, player);
		}
	}

	[HarmonyPatch(typeof(Highlighter), "OnDamageTarget")]
	private static class BurstDamagePatch
	{
		[HarmonyPrefix]
		private static void Prefix(ref DamageCallbackData data)
		{
			OnBurstDamage(ref data);
		}
	}

	[HarmonyPatch(typeof(MissionManager), "OnMissionStarted_Client")]
	private static class MissionStartedPatch
	{
		[HarmonyPostfix]
		private static void Postfix(MissionManager __instance)
		{
			OnMissionBoundary(__instance);
		}
	}

	[HarmonyPatch(typeof(MissionManager), "OnDestroy")]
	private static class MissionDestroyPatch
	{
		[HarmonyPrefix]
		private static void Prefix(MissionManager __instance)
		{
			OnMissionBoundary(__instance);
		}
	}

	[HarmonyPatch(typeof(EnemyPart), "ApplyDamage_Server")]
	private static class EnemyDamageServerPatch
	{
		[HarmonyPrefix]
		private static void Prefix(EnemyPart __instance, DamageData data, IDamageSource source)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			OnEnemyDamageServer(__instance, data, source);
		}
	}

	[HarmonyPatch(typeof(ObjectTarget), "ApplyDamage_Server")]
	private static class ObjectDamageServerPatch
	{
		[HarmonyPrefix]
		private static void Prefix(ObjectTarget __instance, DamageData data, IDamageSource source)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			OnObjectDamageServer(__instance, data, source);
		}
	}

	[HarmonyPatch(typeof(EnemyPart), "Kill_Server")]
	private static class EnemyKillServerPatch
	{
		[HarmonyPrefix]
		private static void Prefix(EnemyPart __instance, IDamageSource source)
		{
			OnEnemyKillServer(__instance, source);
		}
	}

	[HarmonyPatch(typeof(NetworkBehaviour), "InitializeVariables")]
	private static class NetworkVariablesPatch
	{
		[HarmonyPostfix]
		private static void Postfix(NetworkBehaviour __instance)
		{
			OnNetworkVariablesInitialized(__instance);
		}
	}

	private static readonly DpsWindow Window = new DpsWindow();

	private static readonly HashSet<EnemyCore> EstimatedKills = new HashSet<EnemyCore>();

	private static readonly EstimatedDps Estimates = new EstimatedDps();

	private readonly LocalDpsLabel localLabel = new LocalDpsLabel();

	private readonly BurstLabel burstLabel = new BurstLabel();

	private readonly StatsText fallbackText = new StatsText();

	private Harmony harmony;

	private bool ready;

	private bool localHudFailed;

	private bool burstHudFailed;

	private bool missionHooksReady;

	private DpsNetwork network;

	private DamageRpcHooks damageRpcHooks;

	private static bool estimateErrorLogged;

	private static bool estimateRpcLogged;

	private static bool estimateEventLogged;

	private static bool estimateMissingSourceLogged;

	private static bool estimateUnknownSourceLogged;

	private static bool estimateSuccessLogged;

	private bool estimateDisplayLogged;

	internal static Plugin Instance { get; private set; }

	private void Awake()
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Expected O, but got Unknown
		try
		{
			harmony = new Harmony("mycopunk.dpschecker");
			harmony.CreateClassProcessor(typeof(LocalDamagePatch)).Patch();
			harmony.CreateClassProcessor(typeof(LocalKillPatch)).Patch();
			SceneManager.sceneLoaded += OnSceneLoaded;
			network = new DpsNetwork(((BaseUnityPlugin)this).Logger);
			Instance = this;
			ModChannels.Publish("MycopunkShared.DpsChecker.Stats.v1", (object)new Func<ulong, string>(StatsFor));
			ready = true;
			Hotkeys.ReportConflicts((BaseUnityPlugin)(object)this);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"DPS Checker loaded.");
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("DPS Checker could not hook damage events: " + ex));
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			return;
		}
		try
		{
			harmony.CreateClassProcessor(typeof(TeammateInitializePatch)).Patch();
			TeammateHUD[] array = Object.FindObjectsByType<TeammateHUD>((FindObjectsInactive)1);
			foreach (TeammateHUD val in array)
			{
				Player player = val.player;
				if (player != null)
				{
					OnTeammateInitialize(val, player);
				}
			}
		}
		catch (Exception ex2)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("DPS teammate HUD could not be hooked: " + ex2));
		}
		try
		{
			harmony.CreateClassProcessor(typeof(BurstDamagePatch)).Patch();
		}
		catch (Exception ex3)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("DPS burst damage could not be hooked: " + ex3));
		}
		try
		{
			harmony.CreateClassProcessor(typeof(MissionStartedPatch)).Patch();
			harmony.CreateClassProcessor(typeof(MissionDestroyPatch)).Patch();
			missionHooksReady = true;
		}
		catch (Exception ex4)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("DPS mission reset hooks could not be installed: " + ex4));
		}
		try
		{
			harmony.CreateClassProcessor(typeof(EnemyDamageServerPatch)).Patch();
			harmony.CreateClassProcessor(typeof(ObjectDamageServerPatch)).Patch();
			harmony.CreateClassProcessor(typeof(EnemyKillServerPatch)).Patch();
			damageRpcHooks = new DamageRpcHooks(((BaseUnityPlugin)this).Logger, OnEnemyDamageRpc, OnEnemyKillRpc);
			harmony.CreateClassProcessor(typeof(NetworkVariablesPatch)).Patch();
			damageRpcHooks.InstallExisting();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"DPS estimate: damage hooks installed.");
		}
		catch (Exception ex5)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("DPS estimation could not be hooked: " + ex5));
		}
	}

	private void OnDestroy()
	{
		SceneManager.sceneLoaded -= OnSceneLoaded;
		try
		{
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("DPS hook cleanup failed: " + ex));
		}
		try
		{
			if (damageRpcHooks != null)
			{
				damageRpcHooks.Dispose();
			}
		}
		catch (Exception ex2)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("DPS RPC cleanup failed: " + ex2));
		}
		try
		{
			if (network != null)
			{
				network.Dispose();
			}
		}
		catch (Exception ex3)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("DPS network cleanup failed: " + ex3));
		}
		localLabel.Clear();
		burstLabel.Clear();
		Instance = null;
		ModChannels.Publish("MycopunkShared.DpsChecker.Stats.v1", (object)null);
		TeammateDpsLabel[] array = Object.FindObjectsByType<TeammateDpsLabel>((FindObjectsInactive)1);
		for (int i = 0; i < array.Length; i++)
		{
			Object.Destroy((Object)(object)array[i]);
		}
		Window.Clear();
		Estimates.Clear();
		EstimatedKills.Clear();
	}

	private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
	{
		if (!missionHooksReady)
		{
			ResetStats();
		}
		localLabel.Clear();
		burstLabel.Clear();
		localHudFailed = false;
		burstHudFailed = false;
	}

	private static void OnMissionBoundary(MissionManager __instance)
	{
		if ((Object)(object)Instance == (Object)null || (Object)(object)__instance != (Object)(object)MissionManager.Instance)
		{
			return;
		}
		try
		{
			Instance.ResetStats();
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)Instance).Logger.LogWarning((object)("DPS mission reset failed: " + ex));
		}
	}

	private void ResetStats()
	{
		Window.Clear();
		Estimates.Clear();
		EstimatedKills.Clear();
		if (network != null)
		{
			network.ClearValues();
		}
	}

	private static void OnDamage(ref DamageCallbackData data)
	{
		//IL_001a: 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)
		float damage = data.damageData.damage;
		if (data.target != null && ((((IFollowable)data.target).Type & 1) != 0 || data.target is DynamicTrainingTarget) && !(damage <= 0f) && !float.IsNaN(damage) && !float.IsInfinity(damage))
		{
			Window.Add(Time.unscaledTime, damage);
		}
	}

	private static void OnBurstDamage(ref DamageCallbackData data)
	{
		if ((Object)(object)Instance == (Object)null || !Instance.ready)
		{
			return;
		}
		float damage = data.damageData.damage;
		if (damage <= 0f || float.IsNaN(damage) || float.IsInfinity(damage))
		{
			return;
		}
		try
		{
			if ((object)data.target != Player.LocalPlayer || data.source == null || (object)data.source.GetBase() != data.target)
			{
				Window.AddBurst(Time.unscaledTime, damage);
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)Instance).Logger.LogWarning((object)("DPS burst damage could not be recorded: " + ex.Message));
		}
	}

	private void Update()
	{
		if (ready)
		{
			network.Tick(Window.Snapshot(Time.unscaledTime));
		}
	}

	private void LateUpdate()
	{
		if (!ready)
		{
			return;
		}
		if (!burstHudFailed)
		{
			try
			{
				PlayerLook instance = PlayerLook.Instance;
				float remaining;
				double damage = Window.Burst(Time.unscaledTime, out remaining);
				burstLabel.Refresh(((Object)(object)instance != (Object)null) ? instance.PlayerTitleText : null, damage, remaining);
			}
			catch (Exception ex)
			{
				burstHudFailed = true;
				burstLabel.Clear();
				((BaseUnityPlugin)this).Logger.LogWarning((object)("DPS burst HUD could not be created: " + ex));
			}
		}
		if (localHudFailed)
		{
			return;
		}
		try
		{
			PlayerLook instance2 = PlayerLook.Instance;
			Player localPlayer = Player.LocalPlayer;
			EquippableHUDOverlay[] overlays = (((Object)(object)localPlayer != (Object)null) ? localPlayer.gearHUDs : null);
			localLabel.Refresh(((Object)(object)instance2 != (Object)null) ? instance2.PlayerTitleText : null, overlays, Window.Snapshot(Time.unscaledTime));
		}
		catch (Exception ex2)
		{
			localHudFailed = true;
			localLabel.Clear();
			((BaseUnityPlugin)this).Logger.LogWarning((object)("DPS local HUD could not be created: " + ex2));
		}
	}

	private static void OnLocalKill(ref KillCallbackData data)
	{
		if (data.target is EnemyCore)
		{
			Window.AddKill();
		}
	}

	private static string StatsFor(ulong clientId)
	{
		try
		{
			if ((Object)(object)Instance == (Object)null)
			{
				return null;
			}
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton != (Object)null && clientId == singleton.LocalClientId)
			{
				CombatStats combatStats = Window.Snapshot(Time.unscaledTime);
				return ModChannels.FormatDps(combatStats.Max, combatStats.Kills, false);
			}
			if (!Instance.TryGetRemoteStats(clientId, out var stats, out var dpsEstimated, out var _))
			{
				return null;
			}
			return ModChannels.FormatDps(stats.Max, stats.Kills, dpsEstimated || !stats.TotalsKnown);
		}
		catch
		{
			return null;
		}
	}

	internal bool TryGetRemoteStats(ulong clientId, out CombatStats stats, out bool dpsEstimated, out bool totalsEstimated)
	{
		CombatStats stats2;
		bool flag = Estimates.TryGet(clientId, Time.unscaledTime, out stats2);
		if (network.TryGet(clientId, out stats))
		{
			dpsEstimated = false;
			totalsEstimated = !stats.TotalsKnown || !stats.DamageKnown;
			if (totalsEstimated && flag)
			{
				stats.Max = Math.Max(stats.Max, stats2.Max);
				if (!stats.TotalsKnown)
				{
					stats.Kills = stats2.Kills;
				}
				if (!stats.DamageKnown)
				{
					stats.TotalDamage = stats2.TotalDamage;
				}
			}
			return true;
		}
		dpsEstimated = true;
		totalsEstimated = true;
		stats = stats2;
		return flag;
	}

	private static void OnEnemyDamageServer(EnemyPart __instance, DamageData data, IDamageSource source)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		RecordEstimate((NetworkBehaviour)(object)__instance, data, source, client: false);
	}

	private static void OnObjectDamageServer(ObjectTarget __instance, DamageData data, IDamageSource source)
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		if (__instance is DynamicTrainingTarget)
		{
			RecordEstimate((NetworkBehaviour)(object)__instance, data, source, client: false);
		}
	}

	private static void OnEnemyKillServer(EnemyPart __instance, IDamageSource source)
	{
		EnemyCore val = (EnemyCore)(object)((__instance is EnemyCore) ? __instance : null);
		if ((Object)(object)val != (Object)null && ((EnemyPart)val).IsAlive)
		{
			RecordEstimatedKill(val, source);
		}
	}

	private static void OnEnemyKillRpc(NetworkBehaviour target, FastBufferReader reader)
	{
		//IL_0022: 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_0028: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			EnemyCore val = (EnemyCore)(object)((target is EnemyCore) ? target : null);
			if (!((Object)(object)val == (Object)null) && !((NetworkBehaviour)val).IsServer && ((NetworkBehaviour)val).IsClient)
			{
				NetworkBehaviourReference val2 = DamageRpcReader.ReadKillSource(reader);
				NetworkBehaviour val3 = default(NetworkBehaviour);
				RecordEstimatedKill(val, (IDamageSource)(object)(((NetworkBehaviourReference)(ref val2)).TryGet(ref val3, (NetworkManager)null) ? ((val3 is IDamageSource) ? val3 : null) : null));
			}
		}
		catch (Exception e)
		{
			LogEstimateError(e);
		}
	}

	private static void RecordEstimatedKill(EnemyCore core, IDamageSource source)
	{
		try
		{
			if (!((Object)(object)Instance == (Object)null) && source != null && !EstimatedKills.Contains(core))
			{
				Player val = AttackerResolver.Resolve(source);
				if (!((Object)(object)val == (Object)null) && !((NetworkBehaviour)val).IsLocalPlayer)
				{
					EstimatedKills.Add(core);
					Estimates.AddKill(((NetworkBehaviour)val).OwnerClientId);
				}
			}
		}
		catch (Exception e)
		{
			LogEstimateError(e);
		}
	}

	private static void OnNetworkVariablesInitialized(NetworkBehaviour __instance)
	{
		if ((Object)(object)Instance != (Object)null && Instance.damageRpcHooks != null && (__instance is EnemyPart || __instance is DynamicTrainingTarget))
		{
			Instance.damageRpcHooks.Install(((object)__instance).GetType());
		}
	}

	private static void OnEnemyDamageRpc(NetworkBehaviour __0, FastBufferReader __1)
	{
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if ((__0 is EnemyPart || __0 is DynamicTrainingTarget) && !__0.IsServer && __0.IsClient)
			{
				if (!estimateRpcLogged && (Object)(object)Instance != (Object)null)
				{
					estimateRpcLogged = true;
					((BaseUnityPlugin)Instance).Logger.LogInfo((object)"DPS estimate: received a target damage RPC.");
				}
				DamageRpcReader.Read(__1, out var damage, out var source);
				NetworkBehaviour val = default(NetworkBehaviour);
				IDamageSource source2 = (IDamageSource)(object)(((NetworkBehaviourReference)(ref source)).TryGet(ref val, (NetworkManager)null) ? ((val is IDamageSource) ? val : null) : null);
				RecordEstimate(__0, damage, source2, client: true);
			}
		}
		catch (Exception e)
		{
			LogEstimateError(e);
		}
	}

	private static void RecordEstimate(NetworkBehaviour target, DamageData data, IDamageSource source, bool client)
	{
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			ITarget val = (ITarget)(object)((target is ITarget) ? target : null);
			if ((Object)(object)Instance == (Object)null || (Object)(object)target == (Object)null || val == null || !val.IsAlive || (client && target.IsHost))
			{
				return;
			}
			if (!estimateEventLogged)
			{
				estimateEventLogged = true;
				((BaseUnityPlugin)Instance).Logger.LogInfo((object)"DPS estimate: received a target damage event.");
			}
			if (source == null)
			{
				if (!estimateMissingSourceLogged)
				{
					((BaseUnityPlugin)Instance).Logger.LogInfo((object)"DPS estimate: an event had no networked damage source.");
				}
				estimateMissingSourceLogged = true;
				return;
			}
			Player val2 = AttackerResolver.Resolve(source);
			if ((Object)(object)val2 == (Object)null)
			{
				if (!estimateUnknownSourceLogged)
				{
					((BaseUnityPlugin)Instance).Logger.LogInfo((object)("DPS estimate: could not match a damage source to a player (" + ((object)source).GetType().Name + ")."));
				}
				estimateUnknownSourceLogged = true;
			}
			else if (!((NetworkBehaviour)val2).IsLocalPlayer)
			{
				Estimates.Add(((NetworkBehaviour)val2).OwnerClientId, Time.unscaledTime, data.damage);
				if (data.damage > 0f && !float.IsNaN(data.damage) && !float.IsInfinity(data.damage) && !estimateSuccessLogged)
				{
					estimateSuccessLogged = true;
					((BaseUnityPlugin)Instance).Logger.LogInfo((object)"DPS estimate: recorded teammate damage.");
				}
			}
		}
		catch (Exception e)
		{
			LogEstimateError(e);
		}
	}

	internal void LogEstimateDisplay()
	{
		if (!estimateDisplayLogged)
		{
			estimateDisplayLogged = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"DPS estimate: displayed a teammate estimate on the HUD.");
		}
	}

	private static void LogEstimateError(Exception e)
	{
		if (!estimateErrorLogged && (Object)(object)Instance != (Object)null)
		{
			estimateErrorLogged = true;
			((BaseUnityPlugin)Instance).Logger.LogWarning((object)("DPS estimation missed a damage event: " + e));
		}
	}

	private static void OnTeammateInitialize(TeammateHUD __instance, Player player)
	{
		TeammateDpsLabel teammateDpsLabel = null;
		try
		{
			teammateDpsLabel = ((Component)__instance).GetComponent<TeammateDpsLabel>();
			if ((Object)(object)teammateDpsLabel == (Object)null)
			{
				teammateDpsLabel = ((Component)__instance).gameObject.AddComponent<TeammateDpsLabel>();
			}
			if (!teammateDpsLabel.Initialize(__instance, player))
			{
				Object.Destroy((Object)(object)teammateDpsLabel);
				if ((Object)(object)Instance != (Object)null)
				{
					((BaseUnityPlugin)Instance).Logger.LogWarning((object)"DPS teammate label could not be created (TeammateHUD fields not found).");
				}
			}
		}
		catch (Exception ex)
		{
			if ((Object)(object)teammateDpsLabel != (Object)null)
			{
				Object.Destroy((Object)(object)teammateDpsLabel);
			}
			if ((Object)(object)Instance != (Object)null)
			{
				((BaseUnityPlugin)Instance).Logger.LogWarning((object)("DPS teammate label could not be created: " + ex));
			}
		}
	}

	private void OnGUI()
	{
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.LocalPlayer;
		if (ready && !((Object)(object)localPlayer == (Object)null) && !localLabel.Visible)
		{
			GUI.Box(new Rect(20f, 20f, 420f, 44f), GUIContent.none);
			GUI.Label(new Rect(30f, 28f, 400f, 24f), fallbackText.Get(Window.Snapshot(Time.unscaledTime), dpsEstimated: false, totalsEstimated: false, local: true));
		}
	}
}
internal sealed class RemoteDps
{
	private struct Sample
	{
		public CombatStats Value;

		public float Time;
	}

	private readonly Dictionary<ulong, Sample> values = new Dictionary<ulong, Sample>();

	public bool Set(ulong id, CombatStats value, float time)
	{
		if (value.Dps < 0f || float.IsNaN(value.Dps) || float.IsInfinity(value.Dps) || value.Max < 0f || float.IsNaN(value.Max) || float.IsInfinity(value.Max) || value.TotalDamage < 0f || float.IsNaN(value.TotalDamage) || float.IsInfinity(value.TotalDamage) || value.Kills < 0)
		{
			return false;
		}
		if (!value.TotalsKnown && values.TryGetValue(id, out var value2))
		{
			value.Max = Math.Max(value.Max, Math.Max(value2.Value.Max, value.Dps));
		}
		else if (!value.TotalsKnown)
		{
			value.Max = Math.Max(value.Max, value.Dps);
		}
		values[id] = new Sample
		{
			Value = value,
			Time = time
		};
		return true;
	}

	public bool TryGet(ulong id, float time, out CombatStats value)
	{
		if (values.TryGetValue(id, out var value2) && time - value2.Time < 3f)
		{
			value = value2.Value;
			return true;
		}
		value = default(CombatStats);
		return false;
	}

	public void Remove(ulong id)
	{
		values.Remove(id);
	}

	public void Clear()
	{
		values.Clear();
	}
}
public sealed class TeammateDpsLabel : MonoBehaviour
{
	internal const string LineId = "f1w3.dpschecker";

	internal const int LineOrder = 100;

	private static readonly TeammateDpsText.StatsSource FromPlugin = delegate(ulong id, out CombatStats stats, out bool dpsEstimated, out bool totalsEstimated)
	{
		stats = default(CombatStats);
		dpsEstimated = (totalsEstimated = true);
		return (Object)(object)Plugin.Instance != (Object)null && Plugin.Instance.TryGetRemoteStats(id, out stats, out dpsEstimated, out totalsEstimated);
	};

	private Player player;

	private TeammateHUD owner;

	private TextMeshProUGUI nameText;

	private TextMeshProUGUI label;

	private float nextUpdate;

	private readonly TeammateDpsText text = new TeammateDpsText(FromPlugin, delegate
	{
		if ((Object)(object)Plugin.Instance != (Object)null)
		{
			Plugin.Instance.LogEstimateDisplay();
		}
	});

	internal bool Initialize(TeammateHUD hud, Player teammate)
	{
		owner = hud;
		player = teammate;
		nameText = hud.nameText;
		label = UnityTeammateHud.AddText(hud, "f1w3.dpschecker", 100);
		if ((Object)(object)label == (Object)null || (Object)(object)nameText == (Object)null)
		{
			return false;
		}
		nextUpdate = 0f;
		Refresh();
		return true;
	}

	private void Update()
	{
		if (!(Time.unscaledTime < nextUpdate))
		{
			nextUpdate = Time.unscaledTime + 0.25f;
			Refresh();
		}
	}

	private void Refresh()
	{
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)label == (Object)null) && !((Object)(object)nameText == (Object)null) && !((Object)(object)player == (Object)null))
		{
			string text = this.text.Get(((NetworkBehaviour)player).OwnerClientId);
			GameHudText.SetText(label, text);
			((Graphic)label).color = ((Graphic)nameText).color;
			((TMP_Text)label).fontSize = ((TMP_Text)nameText).fontSize;
			UnityTeammateHud.Place(owner);
		}
	}

	private void OnDestroy()
	{
		if ((Object)(object)owner != (Object)null)
		{
			UnityTeammateHud.Remove(owner, "f1w3.dpschecker");
		}
	}
}
internal sealed class TeammateDpsText
{
	internal delegate bool StatsSource(ulong clientId, out CombatStats stats, out bool dpsEstimated, out bool totalsEstimated);

	private readonly StatsSource source;

	private readonly Action estimateShown;

	private readonly StatsText text = new StatsText();

	public TeammateDpsText(StatsSource source, Action estimateShown)
	{
		this.source = source;
		this.estimateShown = estimateShown;
	}

	public string Get(ulong clientId)
	{
		if (!source(clientId, out var stats, out var dpsEstimated, out var totalsEstimated))
		{
			stats = default(CombatStats);
			dpsEstimated = (totalsEstimated = true);
		}
		else if (dpsEstimated && estimateShown != null)
		{
			estimateShown();
		}
		return text.Get(stats, dpsEstimated, totalsEstimated);
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}