Decompiled source of WhySoLaggy v1.0.1

WhySoLaggy.dll

Decompiled 18 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("WhySoLaggy")]
[assembly: AssemblyDescription("BepInEx performance monitor: FPS tracking, plugin Update profiling, Harmony patch profiling.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("WhySoLaggy")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("7a0d965a-ed09-40ae-9680-c8917710a150")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 WhySoLaggy
{
	internal static class AbuseLogger
	{
		private static StreamWriter _writer;

		private static readonly object _lock = new object();

		public static void Initialize(string bepInExDir)
		{
			try
			{
				string text = Path.Combine(bepInExDir, "WhySoLaggy_Abuse.log");
				_writer = new StreamWriter(text, append: false, Encoding.UTF8)
				{
					AutoFlush = true
				};
				_writer.WriteLine("[" + Timestamp() + "] WhySoLaggy Abuse Detection Monitor started");
				_writer.WriteLine("[" + Timestamp() + "] Log file: " + text);
				_writer.WriteLine(new string('=', 60));
			}
			catch (Exception ex)
			{
				Logger.CreateLogSource("WhySoLaggy").LogError((object)("Failed to create abuse log file: " + ex.Message));
			}
		}

		public static void Write(string message)
		{
			if (_writer == null)
			{
				return;
			}
			lock (_lock)
			{
				try
				{
					_writer.WriteLine("[" + Timestamp() + "] " + message);
				}
				catch
				{
				}
			}
		}

		public static void Alert(string message)
		{
			string text = "⚠ ABUSE ALERT: " + message;
			Write(text);
			ManualLogSource log = WhySoLaggyPlugin.Log;
			if (log != null)
			{
				log.LogWarning((object)("[WHY_LAG] " + text));
			}
		}

		public static void WriteRaw(string line)
		{
			if (_writer == null)
			{
				return;
			}
			lock (_lock)
			{
				try
				{
					_writer.WriteLine(line);
				}
				catch
				{
				}
			}
		}

		public static void Shutdown()
		{
			if (_writer == null)
			{
				return;
			}
			lock (_lock)
			{
				try
				{
					_writer.WriteLine("[" + Timestamp() + "] WhySoLaggy Abuse Detection shutting down");
					_writer.Flush();
					_writer.Close();
					_writer = null;
				}
				catch
				{
				}
			}
		}

		private static string Timestamp()
		{
			return DateTime.Now.ToString("HH:mm:ss.fff");
		}
	}
	internal static class AbuseNotificationUI
	{
		private struct Notification
		{
			public string Message;

			public float ExpireTime;
		}

		private const float DisplayDuration = 12f;

		private const int MaxVisibleMessages = 8;

		private const float BoxWidth = 520f;

		private const float LineHeight = 22f;

		private const float Padding = 8f;

		private const float TopMargin = 10f;

		private const float LeftMargin = 10f;

		private static readonly List<Notification> _notifications = new List<Notification>();

		private static GUIStyle _boxStyle;

		private static GUIStyle _textStyle;

		public static void Show(string message)
		{
			_notifications.Add(new Notification
			{
				Message = message,
				ExpireTime = Time.unscaledTime + 12f
			});
			while (_notifications.Count > 8)
			{
				_notifications.RemoveAt(0);
			}
		}

		public static void DrawGUI()
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			float now = Time.unscaledTime;
			_notifications.RemoveAll((Notification n) => now >= n.ExpireTime);
			if (_notifications.Count == 0)
			{
				return;
			}
			EnsureStyles();
			float num = (float)_notifications.Count * 22f + 16f;
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(10f, 10f, 520f, num);
			GUI.Box(val, GUIContent.none, _boxStyle);
			float num2 = 18f;
			Rect val2 = default(Rect);
			foreach (Notification notification in _notifications)
			{
				float num3 = notification.ExpireTime - now;
				float a = ((num3 < 3f) ? (num3 / 3f) : 1f);
				Color textColor = _textStyle.normal.textColor;
				textColor.a = a;
				_textStyle.normal.textColor = textColor;
				((Rect)(ref val2))..ctor(18f, num2, 504f, 22f);
				GUI.Label(val2, notification.Message, _textStyle);
				num2 += 22f;
			}
		}

		private static void EnsureStyles()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			if (_boxStyle == null)
			{
				Texture2D val = new Texture2D(1, 1);
				val.SetPixel(0, 0, new Color(0f, 0f, 0f, 0.75f));
				val.Apply();
				_boxStyle = new GUIStyle(GUI.skin.box);
				_boxStyle.normal.background = val;
				_boxStyle.border = new RectOffset(0, 0, 0, 0);
				_textStyle = new GUIStyle(GUI.skin.label);
				_textStyle.fontSize = 14;
				_textStyle.normal.textColor = new Color(1f, 0.35f, 0.3f, 1f);
				_textStyle.fontStyle = (FontStyle)1;
				_textStyle.wordWrap = true;
			}
		}
	}
	internal static class FpsTracker
	{
		public static int SpikeThresholdMs = 50;

		public static int ReportIntervalSeconds = 10;

		private const int WindowSize = 600;

		private static readonly float[] _frameTimes = new float[600];

		private static int _writeIndex;

		private static int _sampleCount;

		private static float _periodTimer;

		private static int _periodFrames;

		private static float _periodSumMs;

		private static float _periodMinFps = float.MaxValue;

		private static float _periodMaxFps;

		private static int _periodSpikeCount;

		public static bool IsSpikeFrame { get; private set; }

		public static float CurrentFrameMs { get; private set; }

		public static void Tick()
		{
			float unscaledDeltaTime = Time.unscaledDeltaTime;
			float num2 = (CurrentFrameMs = unscaledDeltaTime * 1000f);
			_frameTimes[_writeIndex] = num2;
			_writeIndex = (_writeIndex + 1) % 600;
			if (_sampleCount < 600)
			{
				_sampleCount++;
			}
			IsSpikeFrame = num2 > (float)SpikeThresholdMs;
			if (IsSpikeFrame)
			{
				_periodSpikeCount++;
			}
			_periodFrames++;
			_periodSumMs += num2;
			float num3 = ((unscaledDeltaTime > 0f) ? (1f / unscaledDeltaTime) : 0f);
			if (num3 < _periodMinFps)
			{
				_periodMinFps = num3;
			}
			if (num3 > _periodMaxFps)
			{
				_periodMaxFps = num3;
			}
			_periodTimer += unscaledDeltaTime;
			if (_periodTimer >= (float)ReportIntervalSeconds)
			{
				WriteReport();
				ResetPeriod();
			}
		}

		public static float GetWindowAvgMs()
		{
			if (_sampleCount == 0)
			{
				return 0f;
			}
			float num = 0f;
			for (int i = 0; i < _sampleCount; i++)
			{
				num += _frameTimes[i];
			}
			return num / (float)_sampleCount;
		}

		private static void WriteReport()
		{
			if (_periodFrames != 0)
			{
				float num = ((_periodSumMs > 0f) ? ((float)_periodFrames / (_periodSumMs / 1000f)) : 0f);
				float num2 = _periodSumMs / (float)_periodFrames;
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine($"[WHY_LAG] === FPS Report ({_periodTimer:F1}s, {_periodFrames} frames) ===");
				stringBuilder.AppendLine($"[WHY_LAG] FPS: avg={num:F1} | min={_periodMinFps:F1} | max={_periodMaxFps:F1} | avgFrame={num2:F1}ms");
				stringBuilder.Append($"[WHY_LAG] Spikes(>{SpikeThresholdMs}ms): {_periodSpikeCount}");
				LagLogger.Write(stringBuilder.ToString());
			}
		}

		private static void ResetPeriod()
		{
			_periodTimer = 0f;
			_periodFrames = 0;
			_periodSumMs = 0f;
			_periodMinFps = float.MaxValue;
			_periodMaxFps = 0f;
			_periodSpikeCount = 0;
		}
	}
	internal static class LagLogger
	{
		private static StreamWriter _writer;

		private static readonly object _lock = new object();

		public static void Initialize(string bepInExDir)
		{
			try
			{
				string text = Path.Combine(bepInExDir, "WhySoLaggy.log");
				_writer = new StreamWriter(text, append: false, Encoding.UTF8)
				{
					AutoFlush = true
				};
				_writer.WriteLine("[" + Timestamp() + "] WhySoLaggy Performance Monitor started");
				_writer.WriteLine("[" + Timestamp() + "] Log file: " + text);
				_writer.WriteLine(new string('=', 60));
			}
			catch (Exception ex)
			{
				Logger.CreateLogSource("WhySoLaggy").LogError((object)("Failed to create log file: " + ex.Message));
			}
		}

		public static void Write(string message)
		{
			if (_writer == null)
			{
				return;
			}
			lock (_lock)
			{
				try
				{
					_writer.WriteLine("[" + Timestamp() + "] " + message);
				}
				catch
				{
				}
			}
		}

		public static void WriteRaw(string line)
		{
			if (_writer == null)
			{
				return;
			}
			lock (_lock)
			{
				try
				{
					_writer.WriteLine(line);
				}
				catch
				{
				}
			}
		}

		public static void Flush()
		{
			if (_writer == null)
			{
				return;
			}
			lock (_lock)
			{
				try
				{
					_writer.Flush();
				}
				catch
				{
				}
			}
		}

		public static void Shutdown()
		{
			if (_writer == null)
			{
				return;
			}
			lock (_lock)
			{
				try
				{
					_writer.WriteLine("[" + Timestamp() + "] WhySoLaggy shutting down");
					_writer.Flush();
					_writer.Close();
					_writer = null;
				}
				catch
				{
				}
			}
		}

		private static string Timestamp()
		{
			return DateTime.Now.ToString("HH:mm:ss.fff");
		}
	}
	internal static class NetworkAbuseDetector
	{
		public static int InstantiateRateThreshold = 15;

		public static int DestroyRateThreshold = 20;

		public static int RpcRateThreshold = 50;

		public static int ObjectSpikeThreshold = 30;

		public static float CheckIntervalSeconds = 1f;

		public static float ReportIntervalSeconds = 30f;

		private const byte EventInstantiate = 202;

		private const byte EventRpc = 200;

		private const byte EventDestroy = 204;

		private const byte EventDestroyPlayer = 207;

		private static readonly object _lock = new object();

		private static int _localInstantiateCount;

		private static int _localDestroyCount;

		private static int _localRpcCount;

		private static int _remoteInstantiateCount;

		private static int _remoteDestroyCount;

		private static int _remoteRpcCount;

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

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

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

		private static readonly Dictionary<string, int> _prefabCount = new Dictionary<string, int>();

		private static int _lastPhotonViewCount;

		private static int _lastZombieCount;

		private static Type _zombieType;

		private static float _checkTimer;

		private static float _reportTimer;

		private static bool _initialized;

		private static int _totalInstantiates;

		private static int _totalDestroys;

		private static int _totalRpcs;

		private static int _alertCount;

		private static bool IsChinese => (int)Application.systemLanguage == 6 || (int)Application.systemLanguage == 40 || (int)Application.systemLanguage == 41;

		public static void Initialize(Harmony harmony)
		{
			if (_initialized)
			{
				return;
			}
			try
			{
				PatchPhotonMethods(harmony);
				FindGameTypes();
				_lastPhotonViewCount = CountPhotonViews();
				_lastZombieCount = CountZombies();
				_initialized = true;
				AbuseLogger.Write("[ABUSE] NetworkAbuseDetector initialized (observation-only mode)");
				AbuseLogger.Write($"[ABUSE] Thresholds: Instantiate={InstantiateRateThreshold}/s, Destroy={DestroyRateThreshold}/s, RPC={RpcRateThreshold}/s, ObjectSpike={ObjectSpikeThreshold}");
				AbuseLogger.Write($"[ABUSE] Initial counts: PhotonViews={_lastPhotonViewCount}, Zombies={_lastZombieCount}");
			}
			catch (Exception arg)
			{
				ManualLogSource log = WhySoLaggyPlugin.Log;
				if (log != null)
				{
					log.LogError((object)$"[WHY_LAG] NetworkAbuseDetector init failed: {arg}");
				}
			}
		}

		public static void OnNetworkEvent(byte eventCode, int senderActorNumber)
		{
			if (!_initialized || (PhotonNetwork.LocalPlayer != null && senderActorNumber == PhotonNetwork.LocalPlayer.ActorNumber))
			{
				return;
			}
			lock (_lock)
			{
				switch (eventCode)
				{
				case 202:
					_remoteInstantiateCount++;
					_totalInstantiates++;
					IncrementActor(_instantiateByActor, senderActorNumber);
					break;
				case 200:
					_remoteRpcCount++;
					_totalRpcs++;
					IncrementActor(_rpcByActor, senderActorNumber);
					break;
				case 204:
				case 207:
					_remoteDestroyCount++;
					_totalDestroys++;
					IncrementActor(_destroyByActor, senderActorNumber);
					break;
				}
			}
		}

		public static void Tick()
		{
			if (_initialized)
			{
				float unscaledDeltaTime = Time.unscaledDeltaTime;
				_checkTimer += unscaledDeltaTime;
				_reportTimer += unscaledDeltaTime;
				if (_checkTimer >= CheckIntervalSeconds)
				{
					CheckRates();
					CheckObjectSpike();
					ResetCounters();
					_checkTimer = 0f;
				}
				if (_reportTimer >= ReportIntervalSeconds)
				{
					WritePeriodicReport();
					ResetReportStats();
					_reportTimer = 0f;
				}
			}
		}

		private static void PatchPhotonMethods(Harmony harmony)
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Expected O, but got Unknown
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Expected O, but got Unknown
			MethodInfo[] methods = typeof(PhotonNetwork).GetMethods(BindingFlags.Static | BindingFlags.Public);
			MethodInfo[] array = methods;
			foreach (MethodInfo methodInfo in array)
			{
				if (methodInfo.Name == "Instantiate" || methodInfo.Name == "InstantiateRoomObject")
				{
					try
					{
						harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(NetworkAbuseDetector), "OnInstantiatePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
					catch (Exception ex)
					{
						AbuseLogger.Write("[ABUSE] Failed to patch " + methodInfo.Name + ": " + ex.Message);
					}
				}
			}
			MethodInfo[] methods2 = typeof(PhotonNetwork).GetMethods(BindingFlags.Static | BindingFlags.Public);
			MethodInfo[] array2 = methods2;
			foreach (MethodInfo methodInfo2 in array2)
			{
				if (methodInfo2.Name == "Destroy")
				{
					try
					{
						harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(NetworkAbuseDetector), "OnDestroyPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
					catch (Exception ex2)
					{
						AbuseLogger.Write("[ABUSE] Failed to patch Destroy: " + ex2.Message);
					}
				}
			}
			MethodInfo[] methods3 = typeof(PhotonView).GetMethods(BindingFlags.Instance | BindingFlags.Public);
			MethodInfo[] array3 = methods3;
			foreach (MethodInfo methodInfo3 in array3)
			{
				if (methodInfo3.Name == "RPC")
				{
					try
					{
						harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(NetworkAbuseDetector), "OnRpcPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
					catch (Exception ex3)
					{
						AbuseLogger.Write("[ABUSE] Failed to patch RPC: " + ex3.Message);
					}
				}
			}
			AbuseLogger.Write("[ABUSE] Photon method hooks registered (local API tracking)");
		}

		private static void FindGameTypes()
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if (_zombieType != null)
				{
					break;
				}
				try
				{
					Type[] types = assembly.GetTypes();
					foreach (Type type in types)
					{
						if (type.Name == "MushroomZombie")
						{
							_zombieType = type;
							AbuseLogger.Write("[ABUSE] Found zombie type: " + type.FullName);
							break;
						}
					}
				}
				catch
				{
				}
			}
			if (_zombieType == null)
			{
				AbuseLogger.Write("[ABUSE] MushroomZombie type not found (zombie counting disabled)");
			}
		}

		private static void OnInstantiatePrefix(string prefabName)
		{
			lock (_lock)
			{
				_localInstantiateCount++;
				_totalInstantiates++;
				if (!string.IsNullOrEmpty(prefabName))
				{
					if (!_prefabCount.ContainsKey(prefabName))
					{
						_prefabCount[prefabName] = 0;
					}
					_prefabCount[prefabName]++;
				}
			}
		}

		private static void OnDestroyPrefix()
		{
			lock (_lock)
			{
				_localDestroyCount++;
				_totalDestroys++;
			}
		}

		private static void OnRpcPrefix(PhotonView __instance, string methodName)
		{
			lock (_lock)
			{
				_localRpcCount++;
				_totalRpcs++;
			}
		}

		private static void CheckRates()
		{
			float checkIntervalSeconds = CheckIntervalSeconds;
			int num;
			int num2;
			int num3;
			Dictionary<int, int> dict;
			Dictionary<int, int> dict2;
			Dictionary<int, int> dict3;
			lock (_lock)
			{
				num = _localInstantiateCount + _remoteInstantiateCount;
				num2 = _localDestroyCount + _remoteDestroyCount;
				num3 = _localRpcCount + _remoteRpcCount;
				dict = new Dictionary<int, int>(_instantiateByActor);
				dict2 = new Dictionary<int, int>(_rpcByActor);
				dict3 = new Dictionary<int, int>(_destroyByActor);
			}
			float num4 = (float)num / checkIntervalSeconds;
			float num5 = (float)num2 / checkIntervalSeconds;
			float num6 = (float)num3 / checkIntervalSeconds;
			if (num4 >= (float)InstantiateRateThreshold)
			{
				_alertCount++;
				string message = $"Instantiate flood! Rate: {num4:F1}/s (threshold: {InstantiateRateThreshold}/s)";
				AbuseLogger.Alert(message);
				string message2 = (IsChinese ? $"⚠ 刷物体洪水!速率: {num4:F1}/秒(阈值: {InstantiateRateThreshold}/秒)" : $"⚠ Instantiate flood! Rate: {num4:F1}/s (threshold: {InstantiateRateThreshold}/s)");
				AbuseNotificationUI.Show(message2);
				LogTopActors(dict, "Instantiate");
				LogTopPrefabs();
			}
			if (num5 >= (float)DestroyRateThreshold)
			{
				_alertCount++;
				string message3 = $"Destroy flood! Rate: {num5:F1}/s (threshold: {DestroyRateThreshold}/s)";
				AbuseLogger.Alert(message3);
				string message4 = (IsChinese ? $"⚠ 大量销毁!速率: {num5:F1}/秒(阈值: {DestroyRateThreshold}/秒)" : $"⚠ Destroy flood! Rate: {num5:F1}/s (threshold: {DestroyRateThreshold}/s)");
				AbuseNotificationUI.Show(message4);
				LogTopActors(dict3, "Destroy");
			}
			if (num6 >= (float)RpcRateThreshold)
			{
				_alertCount++;
				string message5 = $"RPC flood! Rate: {num6:F1}/s (threshold: {RpcRateThreshold}/s)";
				AbuseLogger.Alert(message5);
				string message6 = (IsChinese ? $"⚠ RPC洪水!速率: {num6:F1}/秒(阈值: {RpcRateThreshold}/秒)" : $"⚠ RPC flood! Rate: {num6:F1}/s (threshold: {RpcRateThreshold}/s)");
				AbuseNotificationUI.Show(message6);
				LogTopActors(dict2, "RPC");
			}
		}

		private static void CheckObjectSpike()
		{
			int num = CountPhotonViews();
			int num2 = CountZombies();
			int num3 = num - _lastPhotonViewCount;
			int num4 = num2 - _lastZombieCount;
			if (num3 >= ObjectSpikeThreshold)
			{
				_alertCount++;
				string message = $"PhotonView spike! +{num3} in {CheckIntervalSeconds}s (now: {num})";
				AbuseLogger.Alert(message);
				string message2 = (IsChinese ? $"⚠ 对象突增!+{num3} 个/{CheckIntervalSeconds}秒(当前: {num})" : $"⚠ PhotonView spike! +{num3} in {CheckIntervalSeconds}s (now: {num})");
				AbuseNotificationUI.Show(message2);
				InvestigatePhotonViewOwners();
			}
			if (num4 >= ObjectSpikeThreshold)
			{
				_alertCount++;
				string message3 = $"Zombie spike! +{num4} in {CheckIntervalSeconds}s (now: {num2})";
				AbuseLogger.Alert(message3);
				string message4 = (IsChinese ? $"⚠ 僵尸突增!+{num4} 个/{CheckIntervalSeconds}秒(当前: {num2})" : $"⚠ Zombie spike! +{num4} in {CheckIntervalSeconds}s (now: {num2})");
				AbuseNotificationUI.Show(message4);
			}
			_lastPhotonViewCount = num;
			_lastZombieCount = num2;
		}

		private static int CountPhotonViews()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				int num = 0;
				ValueIterator<int, PhotonView> enumerator = PhotonNetwork.PhotonViewCollection.GetEnumerator();
				try
				{
					while (enumerator.MoveNext())
					{
						PhotonView current = enumerator.Current;
						num++;
					}
				}
				finally
				{
					((IDisposable)enumerator).Dispose();
				}
				return num;
			}
			catch
			{
				return 0;
			}
		}

		private static int CountZombies()
		{
			if (_zombieType == null)
			{
				return 0;
			}
			try
			{
				Object[] array = Object.FindObjectsByType(_zombieType, (FindObjectsSortMode)0);
				return (array != null) ? array.Length : 0;
			}
			catch
			{
				return 0;
			}
		}

		private static void InvestigatePhotonViewOwners()
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Dictionary<string, int> dictionary = new Dictionary<string, int>();
				ValueIterator<int, PhotonView> enumerator = PhotonNetwork.PhotonViewCollection.GetEnumerator();
				try
				{
					while (enumerator.MoveNext())
					{
						PhotonView current = enumerator.Current;
						string key = "Scene";
						if ((Object)(object)current != (Object)null && current.Owner != null)
						{
							key = string.Format("{0}#{1}", current.Owner.NickName ?? "?", current.Owner.ActorNumber);
						}
						if (!dictionary.ContainsKey(key))
						{
							dictionary[key] = 0;
						}
						dictionary[key]++;
					}
				}
				finally
				{
					((IDisposable)enumerator).Dispose();
				}
				AbuseLogger.Write("[ABUSE]   PhotonView owner distribution:");
				List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>(dictionary);
				list.Sort((KeyValuePair<string, int> a, KeyValuePair<string, int> b) => b.Value.CompareTo(a.Value));
				foreach (KeyValuePair<string, int> item in list)
				{
					AbuseLogger.WriteRaw($"          {item.Key}: {item.Value} objects");
				}
				if (list.Count > 0 && list[0].Value > ObjectSpikeThreshold)
				{
					string message = (IsChinese ? $"  → 最大持有: {list[0].Key}({list[0].Value} 个对象)" : $"  → Top owner: {list[0].Key} ({list[0].Value} objs)");
					AbuseNotificationUI.Show(message);
				}
			}
			catch (Exception ex)
			{
				AbuseLogger.Write("[ABUSE]   Failed to investigate owners: " + ex.Message);
			}
		}

		private static void WritePeriodicReport()
		{
			AbuseLogger.Write(new string('─', 50));
			AbuseLogger.Write("[ABUSE] ═══ Periodic Abuse Report ═══");
			if (PhotonNetwork.InRoom)
			{
				Room currentRoom = PhotonNetwork.CurrentRoom;
				AbuseLogger.Write(string.Format("[ABUSE]   Room: {0}, Players: {1}/{2}", ((currentRoom != null) ? currentRoom.Name : null) ?? "?", (currentRoom != null) ? currentRoom.PlayerCount : 0, (currentRoom != null) ? currentRoom.MaxPlayers : 0));
			}
			else
			{
				AbuseLogger.Write("[ABUSE]   Not in room");
			}
			AbuseLogger.Write($"[ABUSE]   Current objects: PhotonViews={_lastPhotonViewCount}, Zombies={_lastZombieCount}");
			float reportIntervalSeconds = ReportIntervalSeconds;
			AbuseLogger.Write($"[ABUSE]   Period totals ({reportIntervalSeconds:F0}s): Instantiates={_totalInstantiates}, Destroys={_totalDestroys}, RPCs={_totalRpcs}");
			AbuseLogger.Write($"[ABUSE]   Alerts triggered: {_alertCount}");
			lock (_lock)
			{
				if (_prefabCount.Count > 0)
				{
					AbuseLogger.Write("[ABUSE]   Top spawned prefabs:");
					List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>(_prefabCount);
					list.Sort((KeyValuePair<string, int> a, KeyValuePair<string, int> b) => b.Value.CompareTo(a.Value));
					int num = 0;
					foreach (KeyValuePair<string, int> item in list)
					{
						AbuseLogger.WriteRaw($"          {item.Key}: {item.Value}x");
						if (++num >= 5)
						{
							break;
						}
					}
				}
			}
			if (PhotonNetwork.InRoom)
			{
				AbuseLogger.Write("[ABUSE]   Players in room:");
				Player[] playerList = PhotonNetwork.PlayerList;
				foreach (Player val in playerList)
				{
					string arg = (val.IsMasterClient ? " [Master]" : "");
					AbuseLogger.WriteRaw($"          #{val.ActorNumber} {val.NickName}{arg}");
				}
			}
			AbuseLogger.Write(new string('─', 50));
		}

		private static void ResetCounters()
		{
			lock (_lock)
			{
				_localInstantiateCount = 0;
				_localDestroyCount = 0;
				_localRpcCount = 0;
				_remoteInstantiateCount = 0;
				_remoteDestroyCount = 0;
				_remoteRpcCount = 0;
				_instantiateByActor.Clear();
				_rpcByActor.Clear();
				_destroyByActor.Clear();
			}
		}

		private static void ResetReportStats()
		{
			lock (_lock)
			{
				_totalInstantiates = 0;
				_totalDestroys = 0;
				_totalRpcs = 0;
				_alertCount = 0;
				_prefabCount.Clear();
			}
		}

		private static void IncrementActor(Dictionary<int, int> dict, int actorNumber)
		{
			if (!dict.ContainsKey(actorNumber))
			{
				dict[actorNumber] = 0;
			}
			dict[actorNumber]++;
		}

		private static void LogTopActors(Dictionary<int, int> dict, string action)
		{
			if (dict.Count == 0)
			{
				return;
			}
			List<KeyValuePair<int, int>> list = new List<KeyValuePair<int, int>>(dict);
			list.Sort((KeyValuePair<int, int> a, KeyValuePair<int, int> b) => b.Value.CompareTo(a.Value));
			AbuseLogger.Write("[ABUSE]   Top " + action + " sources (by ActorNumber):");
			int num = 0;
			foreach (KeyValuePair<int, int> item in list)
			{
				string arg = ResolvePlayerName(item.Key);
				AbuseLogger.WriteRaw($"          {arg}#{item.Key}: {item.Value}x");
				if (++num >= 5)
				{
					break;
				}
			}
			if (list.Count > 0)
			{
				string text = ResolvePlayerName(list[0].Key);
				string message = (IsChinese ? $"  → 嫌疑人: {text}#{list[0].Key}({list[0].Value}次 {action})" : $"  → Suspect: {text}#{list[0].Key} ({list[0].Value}x {action})");
				AbuseNotificationUI.Show(message);
			}
		}

		private static void LogTopPrefabs()
		{
			lock (_lock)
			{
				if (_prefabCount.Count == 0)
				{
					return;
				}
				List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>(_prefabCount);
				list.Sort((KeyValuePair<string, int> a, KeyValuePair<string, int> b) => b.Value.CompareTo(a.Value));
				AbuseLogger.Write("[ABUSE]   Top prefabs this interval:");
				int num = 0;
				foreach (KeyValuePair<string, int> item in list)
				{
					AbuseLogger.WriteRaw($"          {item.Key}: {item.Value}x");
					if (++num >= 5)
					{
						break;
					}
				}
			}
		}

		private static string ResolvePlayerName(int actorNumber)
		{
			try
			{
				if (!PhotonNetwork.InRoom)
				{
					return "?";
				}
				Player[] playerList = PhotonNetwork.PlayerList;
				foreach (Player val in playerList)
				{
					if (val.ActorNumber == actorNumber)
					{
						return val.NickName ?? "?";
					}
				}
			}
			catch
			{
			}
			return "?";
		}
	}
	internal static class PatchProfiler
	{
		private class MethodTimingData
		{
			public long TotalTicks;

			public int CallCount;
		}

		private class FrameMethodData
		{
			public long Ticks;

			public int Calls;
		}

		public static bool Enabled = true;

		public static int TopMethodCount = 10;

		public static string OwnHarmonyId;

		private static readonly Dictionary<string, MethodTimingData> _timings = new Dictionary<string, MethodTimingData>();

		private static readonly Dictionary<string, string> _ownerMap = new Dictionary<string, string>();

		private static readonly Dictionary<string, FrameMethodData> _frameTimers = new Dictionary<string, FrameMethodData>();

		private static bool _initialized;

		private static int _patchedCount;

		public static void Initialize(Harmony harmony)
		{
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Expected O, but got Unknown
			//IL_018a: Expected O, but got Unknown
			if (_initialized || !Enabled)
			{
				return;
			}
			_initialized = true;
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("[WHY_LAG] -- PatchProfiler: scanning patched methods --");
			List<MethodBase> list = Harmony.GetAllPatchedMethods().ToList();
			stringBuilder.AppendLine($"[WHY_LAG]   Found {list.Count} patched methods in total");
			int num = 0;
			int num2 = 0;
			foreach (MethodBase item in list)
			{
				if (item == null)
				{
					continue;
				}
				try
				{
					Patches patchInfo = Harmony.GetPatchInfo(item);
					if (patchInfo == null)
					{
						continue;
					}
					List<string> list2 = patchInfo.Owners?.ToList() ?? new List<string>();
					if (list2.Count != 0)
					{
						List<string> list3 = list2.Where((string o) => o != OwnHarmonyId).ToList();
						if (list3.Count == 0)
						{
							num++;
							continue;
						}
						string methodKey = GetMethodKey(item);
						_ownerMap[methodKey] = string.Join(", ", list3);
						harmony.Patch(item, new HarmonyMethod(typeof(PatchProfiler), "TimingPrefix", (Type[])null)
						{
							priority = 800
						}, new HarmonyMethod(typeof(PatchProfiler), "TimingPostfix", (Type[])null)
						{
							priority = 0
						}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						_patchedCount++;
					}
				}
				catch (Exception ex)
				{
					num2++;
					string text = item.DeclaringType?.Name + "." + item.Name;
					stringBuilder.AppendLine("[WHY_LAG]   FAILED to hook " + text + ": " + ex.Message);
				}
			}
			stringBuilder.AppendLine($"[WHY_LAG]   Hooked: {_patchedCount} | Skipped(self-only): {num} | Failed: {num2}");
			stringBuilder.Append("[WHY_LAG] PatchProfiler initialized");
			LagLogger.Write(stringBuilder.ToString());
		}

		public static void TimingPrefix(MethodBase __originalMethod, out long __state)
		{
			__state = Stopwatch.GetTimestamp();
		}

		public static void TimingPostfix(MethodBase __originalMethod, long __state)
		{
			long elapsedTicks = Stopwatch.GetTimestamp() - __state;
			string methodKey = GetMethodKey(__originalMethod);
			Record(methodKey, elapsedTicks);
		}

		private static void Record(string methodKey, long elapsedTicks)
		{
			if (!_timings.TryGetValue(methodKey, out var value))
			{
				value = new MethodTimingData();
				_timings[methodKey] = value;
			}
			value.TotalTicks += elapsedTicks;
			value.CallCount++;
			if (!_frameTimers.TryGetValue(methodKey, out var value2))
			{
				value2 = new FrameMethodData();
				_frameTimers[methodKey] = value2;
			}
			value2.Ticks += elapsedTicks;
			value2.Calls++;
		}

		public static void WriteReport(int totalFrames)
		{
			if (!_initialized || _timings.Count == 0)
			{
				return;
			}
			double num = (double)Stopwatch.Frequency / 1000.0;
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("[WHY_LAG] -- Top Slow Patched Methods (per-frame avg) --");
			List<KeyValuePair<string, MethodTimingData>> list = (from kv in _timings
				where kv.Value.CallCount > 0
				orderby kv.Value.TotalTicks descending
				select kv).Take(TopMethodCount).ToList();
			foreach (KeyValuePair<string, MethodTimingData> item in list)
			{
				double num2 = (double)item.Value.TotalTicks / num;
				double num3 = ((totalFrames > 0) ? (num2 / (double)totalFrames) : num2);
				int num4 = ((totalFrames > 0) ? (item.Value.CallCount / totalFrames) : item.Value.CallCount);
				string value;
				string text = (_ownerMap.TryGetValue(item.Key, out value) ? value : "?");
				string text2 = ((num3 > 2.0) ? " !!!" : ((num3 > 0.5) ? " !" : ""));
				stringBuilder.AppendLine($"[WHY_LAG]   {item.Key}: {num3:F2}ms x{num4} [{text}]{text2}");
			}
			LagLogger.Write(stringBuilder.ToString().TrimEnd(Array.Empty<char>()));
			foreach (MethodTimingData value2 in _timings.Values)
			{
				value2.TotalTicks = 0L;
				value2.CallCount = 0;
			}
		}

		public static void WriteSpikeDetail()
		{
			if (!_initialized || _frameTimers.Count == 0)
			{
				return;
			}
			double tickFreq = (double)Stopwatch.Frequency / 1000.0;
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("[WHY_LAG]   Methods: ");
			List<KeyValuePair<string, FrameMethodData>> list = (from kv in _frameTimers
				where kv.Value.Ticks > 0
				orderby kv.Value.Ticks descending
				select kv).Take(5).ToList();
			foreach (KeyValuePair<string, FrameMethodData> item in list)
			{
				double num = (double)item.Value.Ticks / tickFreq;
				if (!(num < 0.5))
				{
					string value;
					string text = (_ownerMap.TryGetValue(item.Key, out value) ? value : "?");
					stringBuilder.Append($"{item.Key}={num:F1}ms x{item.Value.Calls} [{text}] | ");
				}
			}
			if (list.Any((KeyValuePair<string, FrameMethodData> kv) => (double)kv.Value.Ticks / tickFreq >= 0.5))
			{
				LagLogger.Write(stringBuilder.ToString());
			}
		}

		public static void ResetFrameTimers()
		{
			foreach (FrameMethodData value in _frameTimers.Values)
			{
				value.Ticks = 0L;
				value.Calls = 0;
			}
		}

		private static string GetMethodKey(MethodBase method)
		{
			if (method == null)
			{
				return "(null)";
			}
			string text = method.DeclaringType?.Name ?? "?";
			return text + "." + method.Name;
		}
	}
	internal static class PluginProfiler
	{
		private class PluginTimingData
		{
			public long TotalTicks;

			public int CallCount;
		}

		public static bool Enabled = true;

		private static readonly Dictionary<string, PluginTimingData> _timings = new Dictionary<string, PluginTimingData>();

		private static readonly Dictionary<string, string> _displayNames = new Dictionary<string, string>();

		private static readonly Dictionary<string, long> _frameTimers = new Dictionary<string, long>();

		private static bool _initialized;

		private static int _patchedCount;

		public static void Initialize(Harmony harmony)
		{
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Expected O, but got Unknown
			//IL_017a: Expected O, but got Unknown
			if (_initialized || !Enabled)
			{
				return;
			}
			_initialized = true;
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("[WHY_LAG] -- PluginProfiler: scanning plugins --");
			foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
			{
				PluginInfo value = pluginInfo.Value;
				if ((Object)(object)value.Instance == (Object)null)
				{
					continue;
				}
				Type type = ((object)value.Instance).GetType();
				string key = type.FullName ?? type.Name;
				BepInPlugin metadata = value.Metadata;
				string text = ((metadata != null) ? metadata.Name : null) ?? pluginInfo.Key;
				if (type == typeof(WhySoLaggyPlugin))
				{
					continue;
				}
				_displayNames[key] = text;
				string[] array = new string[3] { "Update", "LateUpdate", "FixedUpdate" };
				string[] array2 = array;
				foreach (string text2 in array2)
				{
					MethodInfo method = type.GetMethod(text2, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (!(method == null))
					{
						try
						{
							harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(PluginProfiler), "TimingPrefix", (Type[])null)
							{
								priority = 800
							}, new HarmonyMethod(typeof(PluginProfiler), "TimingPostfix", (Type[])null)
							{
								priority = 0
							}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
							_patchedCount++;
							stringBuilder.AppendLine("[WHY_LAG]   Hooked " + text + "." + text2);
						}
						catch (Exception ex)
						{
							stringBuilder.AppendLine("[WHY_LAG]   FAILED " + text + "." + text2 + ": " + ex.Message);
						}
					}
				}
			}
			stringBuilder.Append($"[WHY_LAG] PluginProfiler: {_patchedCount} methods hooked across {_displayNames.Count} plugins");
			LagLogger.Write(stringBuilder.ToString());
		}

		public static void TimingPrefix(MonoBehaviour __instance, out long __state)
		{
			__state = Stopwatch.GetTimestamp();
		}

		public static void TimingPostfix(MonoBehaviour __instance, long __state)
		{
			long elapsedTicks = Stopwatch.GetTimestamp() - __state;
			string typeName = ((object)__instance).GetType().FullName ?? ((object)__instance).GetType().Name;
			Record(typeName, elapsedTicks);
		}

		private static void Record(string typeName, long elapsedTicks)
		{
			if (!_timings.TryGetValue(typeName, out var value))
			{
				value = new PluginTimingData();
				_timings[typeName] = value;
			}
			value.TotalTicks += elapsedTicks;
			value.CallCount++;
			if (!_frameTimers.ContainsKey(typeName))
			{
				_frameTimers[typeName] = 0L;
			}
			_frameTimers[typeName] += elapsedTicks;
		}

		public static void WriteReport(int totalFrames)
		{
			if (!_initialized || _timings.Count == 0)
			{
				return;
			}
			double num = (double)Stopwatch.Frequency / 1000.0;
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("[WHY_LAG] -- Plugin Update Time (per-frame avg) --");
			List<KeyValuePair<string, PluginTimingData>> list = _timings.OrderByDescending((KeyValuePair<string, PluginTimingData> kv) => kv.Value.TotalTicks).ToList();
			double num2 = 0.0;
			foreach (KeyValuePair<string, PluginTimingData> item in list)
			{
				string value;
				string text = (_displayNames.TryGetValue(item.Key, out value) ? value : item.Key);
				double num3 = (double)item.Value.TotalTicks / num;
				double num4 = ((totalFrames > 0) ? (num3 / (double)totalFrames) : num3);
				num2 += num3;
				string text2 = ((num4 > 1.0) ? " !!!" : ((num4 > 0.5) ? " !" : ""));
				stringBuilder.AppendLine($"[WHY_LAG]   {text}: {num4:F2}ms/frame (total={num3:F1}ms, calls={item.Value.CallCount}){text2}");
			}
			double num5 = ((totalFrames > 0) ? (num2 / (double)totalFrames) : num2);
			stringBuilder.Append($"[WHY_LAG]   Total MOD overhead: {num5:F2}ms/frame");
			LagLogger.Write(stringBuilder.ToString());
			foreach (PluginTimingData value2 in _timings.Values)
			{
				value2.TotalTicks = 0L;
				value2.CallCount = 0;
			}
		}

		public static void WriteSpikeDetail(float frameMs)
		{
			if (!_initialized || _frameTimers.Count == 0)
			{
				return;
			}
			double num = (double)Stopwatch.Frequency / 1000.0;
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append($"[WHY_LAG] !!! SPIKE {frameMs:F0}ms ({((frameMs > 0f) ? (1000f / frameMs) : 0f):F1} FPS) !!! Plugins: ");
			List<KeyValuePair<string, long>> list = (from kv in _frameTimers
				where kv.Value > 0
				orderby kv.Value descending
				select kv).ToList();
			foreach (KeyValuePair<string, long> item in list)
			{
				string value;
				string arg = (_displayNames.TryGetValue(item.Key, out value) ? value : item.Key);
				double num2 = (double)item.Value / num;
				if (!(num2 < 0.1))
				{
					stringBuilder.Append($"{arg}={num2:F1}ms | ");
				}
			}
			LagLogger.Write(stringBuilder.ToString());
		}

		public static void ResetFrameTimers()
		{
			_frameTimers.Clear();
		}
	}
	[BepInPlugin("com.wuyachiyu.WhySoLaggy", "WhySoLaggy", "1.0.1")]
	public class WhySoLaggyPlugin : BaseUnityPlugin, IOnEventCallback
	{
		[CompilerGenerated]
		private sealed class <Start>d__21 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public WhySoLaggyPlugin <>4__this;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <Start>d__21(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0041: Unknown result type (might be due to invalid IL or missing references)
				//IL_004b: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					Log.LogInfo((object)"[WHY_LAG] Waiting 5s for all plugins to finish loading...");
					LagLogger.Write("[WHY_LAG] Waiting 5s for all plugins to finish loading...");
					<>2__current = (object)new WaitForSeconds(5f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if (EnablePluginProfiling.Value)
					{
						PluginProfiler.Initialize(<>4__this._harmony);
					}
					if (EnablePatchProfiling.Value)
					{
						PatchProfiler.Initialize(<>4__this._harmony);
					}
					if (EnableAbuseDetection.Value)
					{
						NetworkAbuseDetector.InstantiateRateThreshold = InstantiateRateThreshold.Value;
						NetworkAbuseDetector.DestroyRateThreshold = DestroyRateThreshold.Value;
						NetworkAbuseDetector.RpcRateThreshold = RpcRateThreshold.Value;
						NetworkAbuseDetector.ObjectSpikeThreshold = ObjectSpikeThreshold.Value;
						NetworkAbuseDetector.CheckIntervalSeconds = AbuseCheckInterval.Value;
						NetworkAbuseDetector.ReportIntervalSeconds = AbuseReportInterval.Value;
						NetworkAbuseDetector.Initialize(<>4__this._harmony);
					}
					<>4__this._profilingActive = true;
					Log.LogInfo((object)"[WHY_LAG] Profiling active!");
					LagLogger.Write("[WHY_LAG] === Profiling active ===");
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		public const string PluginGuid = "com.wuyachiyu.WhySoLaggy";

		public const string PluginName = "WhySoLaggy";

		public const string PluginVersion = "1.0.1";

		public static ConfigEntry<int> SpikeThresholdMs;

		public static ConfigEntry<int> ReportIntervalSeconds;

		public static ConfigEntry<bool> EnablePluginProfiling;

		public static ConfigEntry<bool> EnablePatchProfiling;

		public static ConfigEntry<int> TopMethodCount;

		public static ConfigEntry<bool> EnableAbuseDetection;

		public static ConfigEntry<float> AbuseCheckInterval;

		public static ConfigEntry<float> AbuseReportInterval;

		public static ConfigEntry<int> InstantiateRateThreshold;

		public static ConfigEntry<int> DestroyRateThreshold;

		public static ConfigEntry<int> RpcRateThreshold;

		public static ConfigEntry<int> ObjectSpikeThreshold;

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private int _reportFrameCount;

		private float _reportTimer;

		private bool _profilingActive;

		private void Awake()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Expected O, but got Unknown
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Expected O, but got Unknown
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Expected O, but got Unknown
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Expected O, but got Unknown
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Expected O, but got Unknown
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Expected O, but got Unknown
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Expected O, but got Unknown
			//IL_02ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f4: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"[WHY_LAG] WhySoLaggy Awake");
			SpikeThresholdMs = ((BaseUnityPlugin)this).Config.Bind<int>("General", "SpikeThresholdMs", 50, new ConfigDescription("Frame time threshold for spike detection (ms). Frames exceeding this are logged.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(16, 200), Array.Empty<object>()));
			ReportIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<int>("General", "ReportIntervalSeconds", 10, new ConfigDescription("Seconds between periodic performance reports.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 60), Array.Empty<object>()));
			EnablePluginProfiling = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnablePluginProfiling", true, "Profile each BepInEx plugin's Update/LateUpdate/FixedUpdate callbacks.");
			EnablePatchProfiling = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnablePatchProfiling", true, "Profile all Harmony-patched game methods.");
			TopMethodCount = ((BaseUnityPlugin)this).Config.Bind<int>("General", "TopMethodCount", 10, new ConfigDescription("Number of top slow methods to show in reports.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 30), Array.Empty<object>()));
			EnableAbuseDetection = ((BaseUnityPlugin)this).Config.Bind<bool>("AbuseDetection", "EnableAbuseDetection", true, "Enable network abuse / room bombing detection.");
			AbuseCheckInterval = ((BaseUnityPlugin)this).Config.Bind<float>("AbuseDetection", "CheckIntervalSeconds", 1f, new ConfigDescription("Seconds between each abuse rate check.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 5f), Array.Empty<object>()));
			AbuseReportInterval = ((BaseUnityPlugin)this).Config.Bind<float>("AbuseDetection", "ReportIntervalSeconds", 30f, new ConfigDescription("Seconds between periodic abuse summary reports.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 120f), Array.Empty<object>()));
			InstantiateRateThreshold = ((BaseUnityPlugin)this).Config.Bind<int>("AbuseDetection", "InstantiateRateThreshold", 15, new ConfigDescription("Max Instantiate calls per second before triggering alert.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 100), Array.Empty<object>()));
			DestroyRateThreshold = ((BaseUnityPlugin)this).Config.Bind<int>("AbuseDetection", "DestroyRateThreshold", 20, new ConfigDescription("Max Destroy calls per second before triggering alert.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 100), Array.Empty<object>()));
			RpcRateThreshold = ((BaseUnityPlugin)this).Config.Bind<int>("AbuseDetection", "RpcRateThreshold", 50, new ConfigDescription("Max RPC calls per second before triggering alert.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 200), Array.Empty<object>()));
			ObjectSpikeThreshold = ((BaseUnityPlugin)this).Config.Bind<int>("AbuseDetection", "ObjectSpikeThreshold", 30, new ConfigDescription("Object count increase per check interval to trigger spike alert.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 100), Array.Empty<object>()));
			string bepInExDir = Path.GetDirectoryName(Path.GetDirectoryName(typeof(BaseUnityPlugin).Assembly.Location)) ?? Paths.BepInExRootPath;
			LagLogger.Initialize(bepInExDir);
			AbuseLogger.Initialize(bepInExDir);
			FpsTracker.SpikeThresholdMs = SpikeThresholdMs.Value;
			FpsTracker.ReportIntervalSeconds = ReportIntervalSeconds.Value;
			PluginProfiler.Enabled = EnablePluginProfiling.Value;
			PatchProfiler.Enabled = EnablePatchProfiling.Value;
			PatchProfiler.TopMethodCount = TopMethodCount.Value;
			_harmony = new Harmony("com.wuyachiyu.WhySoLaggy");
			PatchProfiler.OwnHarmonyId = "com.wuyachiyu.WhySoLaggy";
			Log.LogInfo((object)"[WHY_LAG] Config bound, loggers initialized");
		}

		[IteratorStateMachine(typeof(<Start>d__21))]
		private IEnumerator Start()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <Start>d__21(0)
			{
				<>4__this = this
			};
		}

		private void Update()
		{
			FpsTracker.Tick();
			if (EnableAbuseDetection.Value)
			{
				NetworkAbuseDetector.Tick();
			}
			if (_profilingActive)
			{
				_reportFrameCount++;
				_reportTimer += Time.unscaledDeltaTime;
				if (FpsTracker.IsSpikeFrame)
				{
					PluginProfiler.WriteSpikeDetail(FpsTracker.CurrentFrameMs);
					PatchProfiler.WriteSpikeDetail();
				}
				if (_reportTimer >= (float)ReportIntervalSeconds.Value)
				{
					LagLogger.Write(new string('-', 60));
					PluginProfiler.WriteReport(_reportFrameCount);
					PatchProfiler.WriteReport(_reportFrameCount);
					_reportFrameCount = 0;
					_reportTimer = 0f;
				}
				PluginProfiler.ResetFrameTimers();
				PatchProfiler.ResetFrameTimers();
			}
		}

		private void OnDestroy()
		{
			PhotonNetwork.RemoveCallbackTarget((object)this);
			LagLogger.Shutdown();
			AbuseLogger.Shutdown();
		}

		private void OnEnable()
		{
			PhotonNetwork.AddCallbackTarget((object)this);
		}

		private void OnDisable()
		{
			PhotonNetwork.RemoveCallbackTarget((object)this);
		}

		public void OnEvent(EventData photonEvent)
		{
			if (EnableAbuseDetection.Value)
			{
				NetworkAbuseDetector.OnNetworkEvent(photonEvent.Code, photonEvent.Sender);
			}
		}

		private void OnGUI()
		{
			if (EnableAbuseDetection.Value)
			{
				AbuseNotificationUI.DrawGUI();
			}
		}
	}
}