Decompiled source of ProfilerValheim Zeta v1.1.0

BepInEx/plugins/ProfilerValheim.Zeta.dll

Decompiled 8 hours ago
using System;
using System.Collections;
using System.Collections.Concurrent;
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 System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using ValheimModProfiler.Core;
using ValheimModProfiler.Data;
using ValheimModProfiler.Instrumentation;
using ValheimModProfiler.Overlay;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ProfilerValheim.Zeta")]
[assembly: AssemblyDescription("In-game CPU profiler for Valheim mods")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ProfilerValheim.Zeta")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("78df1c3c-4bc4-4d62-8123-7d5be105a321")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.1.0.0")]
namespace ValheimModProfiler.Overlay
{
	public class IngameProfiler : MonoBehaviour
	{
		private Rect _windowRect = new Rect(40f, 40f, 970f, 720f);

		private bool _showWindow = true;

		private Vector2 _cachedMousePos;

		private bool _hookedBefore;

		private MetricSnapshot _currentSnapshot;

		private float _updateTimer;

		public static string StatusText = "Ready.";

		private int _currentTab;

		private Vector2 _overviewScroll;

		private string _searchFilter = "";

		private bool _hideSmall;

		private HashSet<string> _expandedMods = new HashSet<string>();

		private Vector2 _alertsScroll;

		private Vector2 _compareScroll;

		private List<ModDelta> _cachedDeltas;

		private string _exportStatus = "";

		private static Texture2D _whiteTex;

		private GUIStyle _modBtnStyle;

		private GUIStyle _methodLabelStyle;

		private bool _stylesReady;

		private const float GRAPH_CEILING_MS = 66f;

		private void Start()
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			_whiteTex = new Texture2D(1, 1);
			_whiteTex.SetPixel(0, 0, Color.white);
			_whiteTex.Apply();
		}

		private void Update()
		{
			if (Input.GetKeyDown((KeyCode)289))
			{
				_showWindow = !_showWindow;
			}
			ProfilerDataManager.EndFrame();
			if (ProfilerBootstrapper.IsMonitoring)
			{
				_hookedBefore = true;
			}
			_updateTimer += Time.unscaledDeltaTime;
			if (_updateTimer >= 0.5f)
			{
				_updateTimer = 0f;
				_currentSnapshot = ProfilerDataManager.GetSnapshot();
				_cachedDeltas = null;
			}
		}

		private void OnGUI()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Invalid comparison between Unknown and I4
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Invalid comparison between Unknown and I4
			//IL_004d: 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_0068: Expected O, but got Unknown
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: 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: Invalid comparison between Unknown and I4
			if (_showWindow)
			{
				if ((int)Event.current.type == 2 || (int)Event.current.type == 3 || (int)Event.current.type == 7)
				{
					_cachedMousePos = Event.current.mousePosition;
				}
				EnsureStyles();
				_windowRect = GUI.Window(12345, _windowRect, new WindowFunction(DrawWindow), "Valheim Mod Profiler  [F8]");
			}
		}

		private void DrawWindow(int id)
		{
			//IL_01b1: 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_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label($"<b>FPS: {_currentSnapshot?.Fps ?? 0.0:0.0}</b>", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) });
			GUILayout.Label(StatusText, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			DrawScanButton();
			GUI.backgroundColor = Color.white;
			if (GUILayout.Button("CLEAR GRAPH", (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Height(28f),
				GUILayout.Width(100f)
			}))
			{
				Array.Clear(ProfilerDataManager.GlobalHistory, 0, ProfilerDataManager.GlobalHistory.Length);
				Array.Clear(ProfilerDataManager.WorstFrameHistory, 0, ProfilerDataManager.WorstFrameHistory.Length);
				Array.Clear(ProfilerDataManager.EventMarkerHistory, 0, ProfilerDataManager.EventMarkerHistory.Length);
			}
			GUI.backgroundColor = new Color(0.5f, 0.8f, 1f);
			if (GUILayout.Button("MARK EVENT", (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Height(28f),
				GUILayout.Width(100f)
			}))
			{
				ProfilerDataManager.MarkEvent();
			}
			GUI.backgroundColor = new Color(0.9f, 0.85f, 0.5f);
			if (GUILayout.Button("EXPORT", (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Height(28f),
				GUILayout.Width(80f)
			}))
			{
				try
				{
					string path = ReportExporter.Export(_currentSnapshot);
					_exportStatus = "Saved: " + Path.GetFileName(path);
				}
				catch (Exception ex)
				{
					_exportStatus = "Export error: " + ex.Message;
				}
			}
			GUI.backgroundColor = Color.white;
			GUILayout.EndHorizontal();
			if (!string.IsNullOrEmpty(_exportStatus))
			{
				GUILayout.Label(_exportStatus, Array.Empty<GUILayoutOption>());
			}
			int alertCount = ProfilerDataManager.AlertCount;
			string[] array = new string[3]
			{
				"  Overview  ",
				$"  Alerts ({alertCount})  ",
				"  A/B Compare  "
			};
			GUI.backgroundColor = Color.white;
			_currentTab = GUILayout.Toolbar(_currentTab, array, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) });
			GUILayout.Space(4f);
			switch (_currentTab)
			{
			case 0:
				DrawOverviewTab();
				break;
			case 1:
				DrawAlertsTab();
				break;
			case 2:
				DrawCompareTab();
				break;
			}
			GUI.DragWindow();
		}

		private void DrawScanButton()
		{
			//IL_0072: 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)
			bool isScanning = ProfilerBootstrapper.IsScanning;
			bool isMonitoring = ProfilerBootstrapper.IsMonitoring;
			if (isScanning || isMonitoring)
			{
				GUI.backgroundColor = new Color(1f, 0.25f, 0.25f);
				if (GUILayout.Button(isScanning ? "STOP SCAN" : "STOP MONITORING", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) }))
				{
					ModEntry.Instance?.StopMonitoring();
				}
				return;
			}
			GUI.backgroundColor = new Color(0.3f, 0.85f, 0.3f);
			if (GUILayout.Button(_hookedBefore ? "RESUME" : "SCAN MODS", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) }))
			{
				if (_hookedBefore)
				{
					ProfilerBootstrapper.ResumeMonitoring();
				}
				else
				{
					ModEntry.Instance?.StartScanning();
				}
			}
		}

		private void DrawOverviewTab()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0239: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Invalid comparison between Unknown and I4
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.Label("CPU Load (0 – 66ms)   белый: total   оранжевый: worst frame   зелёный: событие", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
			Rect rect = GUILayoutUtility.GetRect(0f, 150f, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
			GUI.Box(rect, "");
			if (((Rect)(ref rect)).width > 1f && ((Rect)(ref rect)).height > 1f && (int)Event.current.type == 7)
			{
				DrawGrid(rect);
				if (_currentSnapshot != null)
				{
					DrawWorstFrameLine(rect, _currentSnapshot);
					DrawMultiGraph(rect, _currentSnapshot);
					DrawEventMarkers(rect);
					DrawTooltip(rect, _currentSnapshot);
				}
			}
			GUILayout.Space(6f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Filter:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(42f) });
			_searchFilter = GUILayout.TextField(_searchFilter, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(200f) });
			if (GUILayout.Button("x", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(22f) }))
			{
				_searchFilter = "";
			}
			GUILayout.Space(10f);
			_hideSmall = GUILayout.Toggle(_hideSmall, " Hide < 0.1ms", Array.Empty<GUILayoutOption>());
			GUILayout.FlexibleSpace();
			GUILayout.Label("[click row = show methods]", Array.Empty<GUILayoutOption>());
			GUILayout.EndHorizontal();
			GUILayout.Space(4f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(16f) });
			GUILayout.Label("", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(18f) });
			GUILayout.Label("Mod Name", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(326f) });
			GUILayout.Label("Avg CPU", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(75f) });
			GUILayout.Label("Impact", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(75f) });
			GUILayout.Label("Calls/s", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(75f) });
			GUILayout.EndHorizontal();
			_overviewScroll = GUILayout.BeginScrollView(_overviewScroll, Array.Empty<GUILayoutOption>());
			if (_currentSnapshot?.Mods != null)
			{
				foreach (ModMetric item in FilteredMods(_currentSnapshot.Mods))
				{
					DrawModRow(item);
				}
			}
			GUILayout.EndScrollView();
		}

		private IEnumerable<ModMetric> FilteredMods(List<ModMetric> mods)
		{
			return from m in mods
				where !_hideSmall || m.AvgMs >= 0.1
				where string.IsNullOrEmpty(_searchFilter) || m.Name.IndexOf(_searchFilter, StringComparison.OrdinalIgnoreCase) >= 0
				select m;
		}

		private void DrawModRow(ModMetric mod)
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: 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_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			bool flag = _expandedMods.Contains(mod.Name);
			bool flag2 = mod.TopMethods != null && mod.TopMethods.Count > 0;
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label((!flag2) ? " " : (flag ? "v" : ">"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(16f) });
			Rect rect = GUILayoutUtility.GetRect(16f, 16f, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) });
			Color color = GUI.color;
			GUI.color = mod.Color;
			GUI.DrawTexture(rect, (Texture)(object)_whiteTex);
			GUI.color = color;
			if (GUILayout.Button(mod.Name, _modBtnStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(326f) }) && !_expandedMods.Remove(mod.Name))
			{
				_expandedMods.Add(mod.Name);
			}
			GUILayout.Label(mod.CachedAvgMsString + "ms", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(75f) });
			GUI.contentColor = (Color)((mod.ImpactPercent > 5.0) ? new Color(1f, 0.4f, 0.4f) : ((mod.ImpactPercent > 1.0) ? Color.yellow : Color.white));
			GUILayout.Label(mod.CachedImpactString, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(75f) });
			GUI.contentColor = Color.white;
			GUILayout.Label(mod.CachedCallsString, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(75f) });
			GUILayout.EndHorizontal();
			if (!(flag && flag2))
			{
				return;
			}
			foreach (MethodDetail topMethod in mod.TopMethods)
			{
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				GUILayout.Space(34f);
				GUILayout.Label("|_", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(18f) });
				GUILayout.Label(topMethod.MethodName, _methodLabelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(308f) });
				GUILayout.Label(topMethod.CachedAvgMs + "ms", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(75f) });
				GUILayout.Label("", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(75f) });
				GUILayout.Label(topMethod.CachedCalls, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(75f) });
				GUILayout.EndHorizontal();
			}
			GUILayout.Space(3f);
		}

		private void DrawAlertsTab()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label($"Spikes > {16.6f:0.0}ms per frame:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
			GUI.backgroundColor = new Color(0.8f, 0.3f, 0.3f);
			if (GUILayout.Button("Clear", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }))
			{
				ProfilerDataManager.ClearAlerts();
			}
			GUI.backgroundColor = Color.white;
			GUILayout.EndHorizontal();
			GUILayout.Space(4f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Time", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(65f) });
			GUILayout.Label("Mod", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) });
			GUILayout.Label("Peak", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) });
			GUILayout.EndHorizontal();
			_alertsScroll = GUILayout.BeginScrollView(_alertsScroll, Array.Empty<GUILayoutOption>());
			List<AlertEntry> alerts = ProfilerDataManager.GetAlerts();
			if (alerts.Count == 0)
			{
				GUILayout.Label("No spikes detected yet. Keep profiling!", Array.Empty<GUILayoutOption>());
			}
			else
			{
				foreach (AlertEntry item in alerts)
				{
					GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
					GUILayout.Label(item.Timestamp, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(65f) });
					GUI.contentColor = new Color(1f, 0.5f, 0.5f);
					GUILayout.Label(item.ModName, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) });
					GUI.contentColor = Color.white;
					GUILayout.Label(item.PeakMs.ToString("0.0") + " ms", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) });
					GUILayout.EndHorizontal();
				}
			}
			GUILayout.EndScrollView();
		}

		private void DrawCompareTab()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: 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_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_028b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0295: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0322: Unknown result type (might be due to invalid IL or missing references)
			//IL_034b: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUI.backgroundColor = new Color(0.4f, 0.6f, 1f);
			if (GUILayout.Button("Save as Snapshot A", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) }))
			{
				ProfilerDataManager.SaveSnapshotA();
				_cachedDeltas = null;
			}
			GUI.backgroundColor = new Color(1f, 0.65f, 0.3f);
			if (GUILayout.Button("Save as Snapshot B", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) }))
			{
				ProfilerDataManager.SaveSnapshotB();
				_cachedDeltas = null;
			}
			GUI.backgroundColor = Color.white;
			GUILayout.EndHorizontal();
			GUILayout.Space(4f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			bool flag = ProfilerDataManager.SnapshotA != null;
			bool flag2 = ProfilerDataManager.SnapshotB != null;
			GUI.color = (flag ? Color.green : Color.gray);
			GUILayout.Label(flag ? $"A: {ProfilerDataManager.SnapshotA.Fps:0.0} FPS  ({ProfilerDataManager.SnapshotA.Mods?.Count} mods)" : "A: not saved", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(230f) });
			GUI.color = (Color)(flag2 ? new Color(1f, 0.65f, 0.3f) : Color.gray);
			GUILayout.Label(flag2 ? $"B: {ProfilerDataManager.SnapshotB.Fps:0.0} FPS  ({ProfilerDataManager.SnapshotB.Mods?.Count} mods)" : "B: not saved", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(230f) });
			GUI.color = Color.white;
			GUILayout.EndHorizontal();
			GUILayout.Space(6f);
			if (!flag || !flag2)
			{
				GUILayout.Label("Workflow:\n1. Profile your mods in state A  ->  Save as Snapshot A\n2. Make changes  ->  Save as Snapshot B\n3. See the delta here.", Array.Empty<GUILayoutOption>());
				return;
			}
			if (_cachedDeltas == null)
			{
				_cachedDeltas = ProfilerDataManager.ComputeDeltas();
			}
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Mod", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(330f) });
			GUILayout.Label("A (ms)", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(85f) });
			GUILayout.Label("B (ms)", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(85f) });
			GUILayout.Label("Delta", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(95f) });
			GUILayout.EndHorizontal();
			_compareScroll = GUILayout.BeginScrollView(_compareScroll, Array.Empty<GUILayoutOption>());
			foreach (ModDelta cachedDelta in _cachedDeltas)
			{
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				GUILayout.Label(cachedDelta.ModName, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(330f) });
				GUILayout.Label(cachedDelta.CachedA, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(85f) });
				GUILayout.Label(cachedDelta.CachedB, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(85f) });
				GUI.contentColor = cachedDelta.DeltaColor;
				GUILayout.Label(cachedDelta.CachedDelta, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(95f) });
				GUI.contentColor = Color.white;
				GUILayout.EndHorizontal();
			}
			GUILayout.EndScrollView();
		}

		private void DrawGrid(Rect r)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			Color color = GUI.color;
			DrawHLine(r, 66f, new Color(1f, 0.2f, 0.2f, 0.12f));
			DrawHLine(r, 33.3f, new Color(1f, 0.8f, 0.2f, 0.2f));
			DrawHLine(r, 16.6f, new Color(0.3f, 1f, 0.3f, 0.28f));
			GUI.color = new Color(0.3f, 1f, 0.3f, 0.6f);
			float num = ((Rect)(ref r)).y + ((Rect)(ref r)).height * 0.74848485f;
			GUI.Label(new Rect(((Rect)(ref r)).x + 4f, num - 14f, 45f, 14f), "60fps");
			GUI.color = new Color(1f, 0.8f, 0.2f, 0.6f);
			float num2 = ((Rect)(ref r)).y + ((Rect)(ref r)).height * 0.49545455f;
			GUI.Label(new Rect(((Rect)(ref r)).x + 4f, num2 - 14f, 45f, 14f), "30fps");
			GUI.color = color;
		}

		private void DrawHLine(Rect r, float ms, Color col)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			float num = ((Rect)(ref r)).y + ((Rect)(ref r)).height * (1f - ms / 66f);
			if (!(num < ((Rect)(ref r)).y) && !(num > ((Rect)(ref r)).yMax))
			{
				Color color = GUI.color;
				GUI.color = col;
				GUI.DrawTexture(new Rect(((Rect)(ref r)).x, num, ((Rect)(ref r)).width, 1f), (Texture)(object)_whiteTex);
				GUI.color = color;
			}
		}

		private void DrawMultiGraph(Rect r, MetricSnapshot snap)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			if (snap.TotalHistory == null)
			{
				return;
			}
			float num = 0f;
			float[] totalHistory = snap.TotalHistory;
			foreach (float num2 in totalHistory)
			{
				if (num2 > num)
				{
					num = num2;
				}
			}
			float scaleMax = Mathf.Clamp(num, 16f, 66f);
			DrawLine(r, snap.TotalHistory, new Color(1f, 1f, 1f, 0.35f), scaleMax, 3f);
			foreach (ModMetric item in snap.Mods.Where((ModMetric m) => m.History != null && m.ImpactPercent > 0.5).Take(5).Reverse())
			{
				DrawLine(r, item.History, item.Color, scaleMax, 2f);
			}
		}

		private void DrawWorstFrameLine(Rect r, MetricSnapshot snap)
		{
			//IL_004b: 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)
			if (snap.WorstFrameHistory == null || snap.TotalHistory == null)
			{
				return;
			}
			float num = 0f;
			float[] totalHistory = snap.TotalHistory;
			foreach (float num2 in totalHistory)
			{
				if (num2 > num)
				{
					num = num2;
				}
			}
			float scaleMax = Mathf.Clamp(num, 16f, 66f);
			DrawLine(r, snap.WorstFrameHistory, new Color(1f, 0.55f, 0.1f, 0.45f), scaleMax, 1f);
		}

		private void DrawEventMarkers(Rect r)
		{
			//IL_0019: 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_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: 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)
			float[] eventMarkerHistory = ProfilerDataManager.EventMarkerHistory;
			if (eventMarkerHistory == null)
			{
				return;
			}
			float num = ((Rect)(ref r)).width / (float)(eventMarkerHistory.Length - 1);
			Color color = GUI.color;
			for (int i = 0; i < eventMarkerHistory.Length; i++)
			{
				if (!(eventMarkerHistory[i] <= 0f))
				{
					float num2 = ((Rect)(ref r)).x + (float)i * num;
					GUI.color = new Color(0.3f, 1f, 0.5f, 0.75f);
					GUI.DrawTexture(new Rect(num2, ((Rect)(ref r)).y, 2f, ((Rect)(ref r)).height), (Texture)(object)_whiteTex);
					GUI.color = new Color(0.3f, 1f, 0.5f, 1f);
					GUI.DrawTexture(new Rect(num2 - 3f, ((Rect)(ref r)).y + 2f, 8f, 8f), (Texture)(object)_whiteTex);
				}
			}
			GUI.color = color;
		}

		private void DrawTooltip(Rect r, MetricSnapshot snap)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: 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)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			Vector2 cachedMousePos = _cachedMousePos;
			if (!((Rect)(ref r)).Contains(cachedMousePos))
			{
				return;
			}
			int num = Mathf.Clamp((int)((cachedMousePos.x - ((Rect)(ref r)).x) / ((Rect)(ref r)).width * (float)(snap.TotalHistory.Length - 1)), 0, snap.TotalHistory.Length - 1);
			Color color = GUI.color;
			GUI.color = new Color(1f, 1f, 1f, 0.35f);
			GUI.DrawTexture(new Rect(cachedMousePos.x, ((Rect)(ref r)).y, 1f, ((Rect)(ref r)).height), (Texture)(object)_whiteTex);
			GUI.color = color;
			float num2 = snap.TotalHistory[num];
			float num3 = ((snap.WorstFrameHistory != null && num < snap.WorstFrameHistory.Length) ? snap.WorstFrameHistory[num] : 0f);
			string arg = "None";
			float num4 = 0f;
			foreach (ModMetric mod in snap.Mods)
			{
				if (mod.History != null && num < mod.History.Length && mod.History[num] > num4)
				{
					num4 = mod.History[num];
					arg = mod.Name;
				}
			}
			bool flag = ProfilerDataManager.EventMarkerHistory != null && num < ProfilerDataManager.EventMarkerHistory.Length && ProfilerDataManager.EventMarkerHistory[num] > 0f;
			string text = $"Total: {num2:0.0} ms" + $"\nWorst: {num3:0.0} ms" + $"\nTop:   {arg} ({num4:0.0} ms)" + (flag ? "\n[EVENT MARKER]" : "");
			float num5 = 240f;
			float num6 = (flag ? 72 : 58);
			float num7 = cachedMousePos.x + 15f;
			if (num7 + num5 > ((Rect)(ref r)).xMax)
			{
				num7 = cachedMousePos.x - num5 - 15f;
			}
			GUI.Box(new Rect(num7, cachedMousePos.y, num5, num6), text);
		}

		private void DrawLine(Rect r, float[] data, Color col, float scaleMax, float thickness)
		{
			//IL_0019: 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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if (data != null && data.Length >= 2)
			{
				float xStep = ((Rect)(ref r)).width / (float)(data.Length - 1);
				Color color = GUI.color;
				GUI.color = col;
				Vector2 a = Pt(r, 0, data[0], xStep, scaleMax);
				for (int i = 1; i < data.Length; i++)
				{
					Vector2 val = Pt(r, i, data[i], xStep, scaleMax);
					DrawSeg(a, val, thickness);
					a = val;
				}
				GUI.color = color;
			}
		}

		private Vector2 Pt(Rect r, int i, float v, float xStep, float maxV)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			v = Mathf.Min(v, maxV);
			return new Vector2(((Rect)(ref r)).x + (float)i * xStep, ((Rect)(ref r)).y + ((Rect)(ref r)).height - v / maxV * ((Rect)(ref r)).height);
		}

		public static void DrawSeg(Vector2 a, Vector2 b, float w)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = b - a;
			if (!(((Vector2)(ref val)).sqrMagnitude < 0.01f))
			{
				float num = 57.29578f * Mathf.Atan2(b.y - a.y, b.x - a.x);
				GUIUtility.RotateAroundPivot(num, a);
				float x = a.x;
				float num2 = a.y - w * 0.5f;
				val = b - a;
				GUI.DrawTexture(new Rect(x, num2, ((Vector2)(ref val)).magnitude, w), (Texture)(object)_whiteTex);
				GUIUtility.RotateAroundPivot(0f - num, a);
			}
		}

		private void EnsureStyles()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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)
			//IL_003e: Expected O, but got Unknown
			//IL_0043: Expected O, but got Unknown
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: 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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			if (!_stylesReady)
			{
				_stylesReady = true;
				_modBtnStyle = new GUIStyle(GUI.skin.button)
				{
					alignment = (TextAnchor)3,
					fontSize = 12,
					padding = new RectOffset(4, 4, 2, 2)
				};
				_modBtnStyle.normal.textColor = Color.white;
				_modBtnStyle.hover.textColor = Color.white;
				_methodLabelStyle = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)3,
					fontSize = 11
				};
				_methodLabelStyle.normal.textColor = new Color(0.75f, 0.75f, 0.75f);
			}
		}
	}
}
namespace ValheimModProfiler.Instrumentation
{
	public static class ProfilerBootstrapper
	{
		[CompilerGenerated]
		private sealed class <ApplyHooksCoroutine>d__13 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			private Harmony <harmony>5__2;

			private MethodInfo <myPrefix>5__3;

			private MethodInfo <myPostfix>5__4;

			private List<PluginInfo> <plugins>5__5;

			private int <totalHooks>5__6;

			private int <processedPlugins>5__7;

			private List<PluginInfo>.Enumerator <>7__wrap7;

			private PluginInfo <plugin>5__9;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 2)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<harmony>5__2 = null;
				<myPrefix>5__3 = null;
				<myPostfix>5__4 = null;
				<plugins>5__5 = null;
				<>7__wrap7 = default(List<PluginInfo>.Enumerator);
				<plugin>5__9 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0067: Unknown result type (might be due to invalid IL or missing references)
				//IL_0071: Expected O, but got Unknown
				bool result;
				try
				{
					switch (<>1__state)
					{
					default:
						result = false;
						goto end_IL_0000;
					case 0:
						<>1__state = -1;
						_cancelRequested = false;
						IsScanning = true;
						IsMonitoring = true;
						IngameProfiler.StatusText = "Initializing Smart Scan...";
						<>2__current = null;
						<>1__state = 1;
						result = true;
						goto end_IL_0000;
					case 1:
						<>1__state = -1;
						<harmony>5__2 = new Harmony("com.debug.valheim_profiler");
						<myPrefix>5__3 = typeof(ProfilerRecorder).GetMethod("Prefix");
						<myPostfix>5__4 = typeof(ProfilerRecorder).GetMethod("Postfix");
						<plugins>5__5 = Chainloader.PluginInfos.Values.ToList();
						Logger.LogInfo((object)$"Found {<plugins>5__5.Count} BepInEx plugins to scan.");
						<totalHooks>5__6 = 0;
						<processedPlugins>5__7 = 0;
						<>7__wrap7 = <plugins>5__5.GetEnumerator();
						<>1__state = -3;
						break;
					case 2:
					{
						<>1__state = -3;
						if ((Object)(object)<plugin>5__9.Instance == (Object)null)
						{
							break;
						}
						Assembly assembly = ((object)<plugin>5__9.Instance).GetType().Assembly;
						if (assembly == null)
						{
							break;
						}
						Type[] array;
						try
						{
							array = assembly.GetTypes();
						}
						catch (ReflectionTypeLoadException ex)
						{
							array = ex.Types.Where((Type t) => t != null).ToArray();
							Logger.LogWarning((object)("[Profiler] Partial type load for " + <plugin>5__9.Metadata.Name + ": " + ex.LoaderExceptions.FirstOrDefault()?.Message));
						}
						catch (Exception ex2)
						{
							Logger.LogWarning((object)("[Profiler] Cannot load types from " + <plugin>5__9.Metadata.Name + ": " + ex2.Message));
							break;
						}
						Type[] array2 = array;
						foreach (Type type in array2)
						{
							if (typeof(MonoBehaviour).IsAssignableFrom(type))
							{
								HookUnityMethod(<harmony>5__2, type, "Update", <myPrefix>5__3, <myPostfix>5__4, ref <totalHooks>5__6);
								HookUnityMethod(<harmony>5__2, type, "FixedUpdate", <myPrefix>5__3, <myPostfix>5__4, ref <totalHooks>5__6);
								HookUnityMethod(<harmony>5__2, type, "LateUpdate", <myPrefix>5__3, <myPostfix>5__4, ref <totalHooks>5__6);
							}
							if (!type.GetCustomAttributes(inherit: false).Any((object x) => x.GetType().Name.Contains("Harmony")))
							{
								continue;
							}
							MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
							foreach (MethodInfo methodInfo in methods)
							{
								if (methodInfo.Name == "Prefix" || methodInfo.Name == "Postfix")
								{
									SafePatch(<harmony>5__2, methodInfo, <myPrefix>5__3, <myPostfix>5__4, ref <totalHooks>5__6);
								}
							}
						}
						<plugin>5__9 = null;
						break;
					}
					}
					while (true)
					{
						if (<>7__wrap7.MoveNext())
						{
							<plugin>5__9 = <>7__wrap7.Current;
							if (_cancelRequested)
							{
								Logger.LogInfo((object)$"[Profiler] Scan cancelled by user. Hooks applied: {<totalHooks>5__6}");
								IngameProfiler.StatusText = $"Monitoring stopped. Hooks were: {<totalHooks>5__6}.";
								IsScanning = false;
								result = false;
								<>m__Finally1();
								break;
							}
							<processedPlugins>5__7++;
							if (!(<plugin>5__9.Metadata.GUID == "com.debug.valheim_profiler"))
							{
								IngameProfiler.StatusText = $"Scanning ({<processedPlugins>5__7}/{<plugins>5__5.Count}): {<plugin>5__9.Metadata.Name}";
								<>2__current = null;
								<>1__state = 2;
								result = true;
								break;
							}
							continue;
						}
						<>m__Finally1();
						<>7__wrap7 = default(List<PluginInfo>.Enumerator);
						Logger.LogInfo((object)$"Smart Scan Complete. Total Hooks: {<totalHooks>5__6}");
						IngameProfiler.StatusText = $"Monitoring {<totalHooks>5__6} methods. Press Stop to pause.";
						IsScanning = false;
						result = false;
						break;
					}
					end_IL_0000:;
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
				return result;
			}

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

			private void <>m__Finally1()
			{
				<>1__state = -1;
				((IDisposable)<>7__wrap7).Dispose();
			}

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

		private static readonly ManualLogSource Logger = Logger.CreateLogSource("ProfilerHooks");

		private static readonly HashSet<MethodBase> _hookedMethods = new HashSet<MethodBase>();

		private static volatile bool _cancelRequested = false;

		public static bool IsScanning { get; private set; } = false;


		public static bool IsMonitoring { get; private set; } = false;


		public static void StopMonitoring()
		{
			_cancelRequested = true;
			IsMonitoring = false;
		}

		public static void ResumeMonitoring()
		{
			_cancelRequested = false;
			IsMonitoring = true;
			IngameProfiler.StatusText = "Monitoring resumed.";
		}

		[IteratorStateMachine(typeof(<ApplyHooksCoroutine>d__13))]
		public static IEnumerator ApplyHooksCoroutine()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ApplyHooksCoroutine>d__13(0);
		}

		private static void HookUnityMethod(Harmony harmony, Type type, string methodName, MethodInfo pre, MethodInfo post, ref int counter)
		{
			try
			{
				MethodInfo method = type.GetMethod(methodName, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (method != null)
				{
					SafePatch(harmony, method, pre, post, ref counter);
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("[Profiler] HookUnityMethod(" + methodName + ") on " + type.FullName + ": " + ex.Message));
			}
		}

		private static void SafePatch(Harmony harmony, MethodBase target, MethodInfo pre, MethodInfo post, ref int counter)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			//IL_0040: Expected O, but got Unknown
			if (_hookedMethods.Contains(target) || target.IsGenericMethod || target.ContainsGenericParameters || target.IsAbstract)
			{
				return;
			}
			try
			{
				harmony.Patch(target, new HarmonyMethod(pre), new HarmonyMethod(post), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_hookedMethods.Add(target);
				counter++;
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("[Profiler] Cannot patch " + target.DeclaringType?.Name + "." + target.Name + ": " + ex.Message));
			}
		}
	}
	public static class ProfilerRecorder
	{
		private struct FrameData
		{
			public long StartTick;

			public long ChildrenTicks;

			public int OwnerHash;
		}

		[ThreadStatic]
		private static Stack<FrameData> _stack;

		[ThreadStatic]
		private static bool _isRecording;

		public static void Prefix(MethodBase __originalMethod, out long __state)
		{
			if (!ProfilerBootstrapper.IsMonitoring)
			{
				__state = -1L;
				return;
			}
			if (_isRecording)
			{
				__state = -1L;
				return;
			}
			_isRecording = true;
			try
			{
				if (_stack == null)
				{
					_stack = new Stack<FrameData>();
				}
				int ownerHash = 0;
				string name = "Unknown";
				string displayName = "Unknown";
				if (__originalMethod.DeclaringType != null)
				{
					ownerHash = __originalMethod.DeclaringType.Assembly.FullName.GetHashCode();
					name = __originalMethod.DeclaringType.Assembly.GetName().Name;
					displayName = __originalMethod.DeclaringType.Name + "." + __originalMethod.Name;
				}
				ProfilerDataManager.RegisterModName(ownerHash, name);
				ProfilerDataManager.RegisterMethodName(__originalMethod.MetadataToken, displayName);
				_stack.Push(new FrameData
				{
					StartTick = Stopwatch.GetTimestamp(),
					ChildrenTicks = 0L,
					OwnerHash = ownerHash
				});
				__state = 1L;
			}
			catch (Exception ex)
			{
				ManualLogSource logger = ModEntry.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)("[Profiler] Prefix error in " + __originalMethod?.Name + ": " + ex.Message));
				}
				__state = -1L;
			}
			finally
			{
				_isRecording = false;
			}
		}

		public static void Postfix(MethodBase __originalMethod, long __state)
		{
			if (__state == -1 || _isRecording)
			{
				return;
			}
			_isRecording = true;
			try
			{
				if (_stack != null && _stack.Count != 0)
				{
					long timestamp = Stopwatch.GetTimestamp();
					FrameData frameData = _stack.Pop();
					long num = timestamp - frameData.StartTick;
					long num2 = num - frameData.ChildrenTicks;
					if (num2 < 0)
					{
						num2 = 0L;
					}
					if (_stack.Count > 0)
					{
						FrameData item = _stack.Pop();
						item.ChildrenTicks += num;
						_stack.Push(item);
					}
					ProfilerDataManager.RecordSample(__originalMethod.MetadataToken, frameData.OwnerHash, num2);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = ModEntry.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)("[Profiler] Postfix error in " + __originalMethod?.Name + ": " + ex.Message));
				}
			}
			finally
			{
				_isRecording = false;
			}
		}
	}
}
namespace ValheimModProfiler.Data
{
	public class MetricSnapshot
	{
		public double Fps { get; set; }

		public List<ModMetric> Mods { get; set; } = new List<ModMetric>();


		public float[] TotalHistory { get; set; }

		public float[] WorstFrameHistory { get; set; }
	}
	public class ModMetric
	{
		public string Name { get; set; }

		public double TotalMs { get; set; }

		public double AvgMs { get; set; }

		public int Calls { get; set; }

		public double ImpactPercent { get; set; }

		public Color Color { get; set; }

		public float[] History { get; set; }

		public List<MethodDetail> TopMethods { get; set; } = new List<MethodDetail>();


		public string CachedAvgMsString { get; set; }

		public string CachedImpactString { get; set; }

		public string CachedCallsString { get; set; }
	}
	public class MethodDetail
	{
		public string MethodName { get; set; }

		public double AvgMs { get; set; }

		public int Calls { get; set; }

		public string CachedAvgMs { get; set; }

		public string CachedCalls { get; set; }
	}
	public class AlertEntry
	{
		public string ModName { get; set; }

		public float PeakMs { get; set; }

		public string Timestamp { get; set; }
	}
	public class ModDelta
	{
		public string ModName { get; set; }

		public double AvgMsA { get; set; }

		public double AvgMsB { get; set; }

		public double DeltaMs => AvgMsB - AvgMsA;

		public Color DeltaColor { get; set; }

		public string CachedA { get; set; }

		public string CachedB { get; set; }

		public string CachedDelta { get; set; }
	}
	public class MethodData
	{
		public long Ticks;

		public int Calls;

		public int OwnerHash;
	}
	public static class ProfilerDataManager
	{
		private class MethodAccumEntry
		{
			public string DisplayName;

			public int OwnerHash;

			public double TotalMs;

			public int Calls;
		}

		private static ConcurrentDictionary<int, MethodData> _methodRegistry = new ConcurrentDictionary<int, MethodData>();

		private static ConcurrentDictionary<int, string> _modNames = new ConcurrentDictionary<int, string>();

		private static ConcurrentDictionary<int, string> _methodDisplayNames = new ConcurrentDictionary<int, string>();

		private static readonly int _historySize = 300;

		public static float[] GlobalHistory = new float[300];

		public static float[] WorstFrameHistory = new float[300];

		public static float[] EventMarkerHistory = new float[300];

		private static float _sessionWorst = 0f;

		private static bool _pendingMark = false;

		private static Dictionary<string, float[]> _modHistories = new Dictionary<string, float[]>();

		private static Dictionary<int, MethodAccumEntry> _methodAccum = new Dictionary<int, MethodAccumEntry>();

		private static Dictionary<string, ModMetric> _snapshotAccumulator = new Dictionary<string, ModMetric>();

		private static MetricSnapshot _currentSnapshot = new MetricSnapshot();

		private static readonly object _snapshotLock = new object();

		private static Stopwatch _snapshotTimer = new Stopwatch();

		private static int _frameCount = 0;

		public const float SpikeThresholdMs = 16.6f;

		private static Dictionary<string, float> _spikeTracking = new Dictionary<string, float>();

		private static readonly List<AlertEntry> _alerts = new List<AlertEntry>();

		private static readonly object _alertsLock = new object();

		private const int MaxAlerts = 100;

		public static MetricSnapshot SnapshotA { get; private set; }

		public static MetricSnapshot SnapshotB { get; private set; }

		public static int AlertCount
		{
			get
			{
				lock (_alertsLock)
				{
					return _alerts.Count;
				}
			}
		}

		public static void Initialize()
		{
			_snapshotTimer.Start();
		}

		public static void MarkEvent()
		{
			_pendingMark = true;
		}

		public static void SaveSnapshotA()
		{
			SnapshotA = GetSnapshot();
		}

		public static void SaveSnapshotB()
		{
			SnapshotB = GetSnapshot();
		}

		public static void ClearAlerts()
		{
			lock (_alertsLock)
			{
				_alerts.Clear();
			}
		}

		public static void RegisterModName(int ownerHash, string name)
		{
			_modNames.TryAdd(ownerHash, name);
		}

		public static void RegisterMethodName(int methodToken, string displayName)
		{
			_methodDisplayNames.TryAdd(methodToken, displayName);
		}

		public static void RecordSample(int methodToken, int ownerHash, long selfTicks)
		{
			if (!_methodRegistry.TryGetValue(methodToken, out var value))
			{
				value = new MethodData
				{
					OwnerHash = ownerHash
				};
				_methodRegistry.TryAdd(methodToken, value);
			}
			Interlocked.Add(ref value.Ticks, selfTicks);
			Interlocked.Increment(ref value.Calls);
		}

		public static List<AlertEntry> GetAlerts()
		{
			lock (_alertsLock)
			{
				return new List<AlertEntry>(_alerts);
			}
		}

		public static List<ModDelta> ComputeDeltas()
		{
			MetricSnapshot snapshotA = SnapshotA;
			MetricSnapshot snapshotB = SnapshotB;
			if (snapshotA == null || snapshotB == null)
			{
				return null;
			}
			Dictionary<string, double> dictA = snapshotA.Mods.ToDictionary((ModMetric m) => m.Name, (ModMetric m) => m.AvgMs);
			Dictionary<string, double> dictB = snapshotB.Mods.ToDictionary((ModMetric m) => m.Name, (ModMetric m) => m.AvgMs);
			HashSet<string> hashSet = new HashSet<string>(dictA.Keys);
			hashSet.UnionWith(dictB.Keys);
			return (from d in hashSet.Select(delegate(string name)
				{
					//IL_00a0: 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_0074: Unknown result type (might be due to invalid IL or missing references)
					double value;
					double num = (dictA.TryGetValue(name, out value) ? value : 0.0);
					double value2;
					double num2 = (dictB.TryGetValue(name, out value2) ? value2 : 0.0);
					double num3 = num2 - num;
					return new ModDelta
					{
						ModName = name,
						AvgMsA = num,
						AvgMsB = num2,
						DeltaColor = (Color)((num3 > 0.5) ? new Color(1f, 0.4f, 0.4f) : ((num3 < -0.5) ? new Color(0.4f, 1f, 0.4f) : Color.white)),
						CachedA = num.ToString("0.00") + "ms",
						CachedB = num2.ToString("0.00") + "ms",
						CachedDelta = ((num3 >= 0.0) ? "+" : "") + num3.ToString("0.00") + "ms"
					};
				})
				orderby Math.Abs(d.DeltaMs) descending
				select d).ToList();
		}

		public static void EndFrame()
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			if (Time.timeScale <= 0.0001f)
			{
				return;
			}
			_frameCount++;
			double num = 0.0;
			Dictionary<string, float> dictionary = new Dictionary<string, float>();
			foreach (KeyValuePair<int, MethodData> item in _methodRegistry)
			{
				MethodData value = item.Value;
				long num2 = Interlocked.Exchange(ref value.Ticks, 0L);
				int num3 = Interlocked.Exchange(ref value.Calls, 0);
				if (num2 != 0L)
				{
					num += (double)num2;
					double num4 = (double)num2 / (double)Stopwatch.Frequency * 1000.0;
					string value2;
					string text = (_modNames.TryGetValue(value.OwnerHash, out value2) ? value2 : "Unknown");
					if (!_snapshotAccumulator.TryGetValue(text, out var value3))
					{
						value3 = new ModMetric
						{
							Name = text,
							Color = GenerateColor(text)
						};
						_snapshotAccumulator[text] = value3;
					}
					value3.TotalMs += num4;
					value3.Calls += num3;
					if (!dictionary.ContainsKey(text))
					{
						dictionary[text] = 0f;
					}
					dictionary[text] += (float)num4;
					if (!_methodAccum.TryGetValue(item.Key, out var value4))
					{
						value4 = new MethodAccumEntry
						{
							DisplayName = (_methodDisplayNames.TryGetValue(item.Key, out var value5) ? value5 : $"token_{item.Key}"),
							OwnerHash = value.OwnerHash
						};
						_methodAccum[item.Key] = value4;
					}
					value4.TotalMs += num4;
					value4.Calls += num3;
				}
			}
			foreach (KeyValuePair<string, float> item2 in dictionary)
			{
				float value6;
				float num5 = (_spikeTracking.TryGetValue(item2.Key, out value6) ? value6 : 0f);
				if (item2.Value > num5)
				{
					_spikeTracking[item2.Key] = item2.Value;
				}
			}
			float num6 = (float)(num / (double)Stopwatch.Frequency * 1000.0);
			ShiftAndSet(GlobalHistory, num6);
			if (num6 > _sessionWorst)
			{
				_sessionWorst = num6;
			}
			ShiftAndSet(WorstFrameHistory, _sessionWorst);
			_sessionWorst *= 0.999f;
			foreach (string key in _snapshotAccumulator.Keys)
			{
				if (!_modHistories.ContainsKey(key))
				{
					_modHistories[key] = new float[_historySize];
				}
				ShiftAndSet(_modHistories[key], dictionary.TryGetValue(key, out var value7) ? value7 : 0f);
			}
			ShiftAndSet(EventMarkerHistory, _pendingMark ? 1f : 0f);
			_pendingMark = false;
			if (_snapshotTimer.ElapsedMilliseconds >= 500)
			{
				PublishSnapshot();
			}
		}

		private static void PublishSnapshot()
		{
			double elapsedSec = _snapshotTimer.Elapsed.TotalSeconds;
			double num = (double)_frameCount / elapsedSec;
			if (num <= 0.0)
			{
				num = 1.0;
			}
			double num2 = 1000.0 / num;
			Dictionary<string, List<MethodAccumEntry>> dictionary = new Dictionary<string, List<MethodAccumEntry>>();
			foreach (KeyValuePair<int, MethodAccumEntry> item in _methodAccum)
			{
				if (!(item.Value.TotalMs < 0.001))
				{
					string value;
					string key = (_modNames.TryGetValue(item.Value.OwnerHash, out value) ? value : "Unknown");
					if (!dictionary.ContainsKey(key))
					{
						dictionary[key] = new List<MethodAccumEntry>();
					}
					dictionary[key].Add(item.Value);
				}
			}
			MetricSnapshot metricSnapshot = new MetricSnapshot
			{
				Fps = Math.Round(num, 1),
				Mods = new List<ModMetric>(),
				TotalHistory = (float[])GlobalHistory.Clone(),
				WorstFrameHistory = (float[])WorstFrameHistory.Clone()
			};
			foreach (KeyValuePair<string, ModMetric> item2 in _snapshotAccumulator)
			{
				ModMetric value2 = item2.Value;
				value2.AvgMs = value2.TotalMs / (double)_frameCount;
				value2.ImpactPercent = value2.AvgMs / num2 * 100.0;
				value2.CachedAvgMsString = value2.AvgMs.ToString("0.00");
				value2.CachedImpactString = value2.ImpactPercent.ToString("0.0") + "%";
				value2.CachedCallsString = ((double)value2.Calls / elapsedSec).ToString("0") + "/s";
				if (_modHistories.TryGetValue(value2.Name, out var value3))
				{
					value2.History = (float[])value3.Clone();
				}
				value2.TopMethods = new List<MethodDetail>();
				if (dictionary.TryGetValue(value2.Name, out var value4))
				{
					value2.TopMethods = (from m in value4.OrderByDescending((MethodAccumEntry m) => m.TotalMs).Take(10)
						select new MethodDetail
						{
							MethodName = m.DisplayName,
							AvgMs = m.TotalMs / (double)_frameCount,
							Calls = m.Calls,
							CachedAvgMs = (m.TotalMs / (double)_frameCount).ToString("0.00"),
							CachedCalls = ((double)m.Calls / elapsedSec).ToString("0") + "/s"
						}).ToList();
				}
				metricSnapshot.Mods.Add(value2);
			}
			metricSnapshot.Mods = metricSnapshot.Mods.OrderByDescending((ModMetric m) => m.TotalMs).ToList();
			foreach (KeyValuePair<string, float> item3 in _spikeTracking)
			{
				if (!(item3.Value >= 16.6f))
				{
					continue;
				}
				lock (_alertsLock)
				{
					_alerts.Insert(0, new AlertEntry
					{
						ModName = item3.Key,
						PeakMs = item3.Value,
						Timestamp = DateTime.Now.ToString("HH:mm:ss")
					});
					if (_alerts.Count > 100)
					{
						_alerts.RemoveAt(_alerts.Count - 1);
					}
				}
			}
			_spikeTracking.Clear();
			lock (_snapshotLock)
			{
				_currentSnapshot = metricSnapshot;
			}
			foreach (ModMetric value5 in _snapshotAccumulator.Values)
			{
				value5.TotalMs = 0.0;
				value5.Calls = 0;
			}
			foreach (MethodAccumEntry value6 in _methodAccum.Values)
			{
				value6.TotalMs = 0.0;
				value6.Calls = 0;
			}
			_frameCount = 0;
			_snapshotTimer.Restart();
		}

		public static MetricSnapshot GetSnapshot()
		{
			lock (_snapshotLock)
			{
				return _currentSnapshot;
			}
		}

		private static void ShiftAndSet(float[] arr, float newVal)
		{
			Array.Copy(arr, 1, arr, 0, arr.Length - 1);
			arr[^1] = newVal;
		}

		private static Color GenerateColor(string name)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			return Color.HSVToRGB((float)Mathf.Abs(name.GetHashCode() % 360) / 360f, 0.85f, 1f);
		}
	}
	public static class ReportExporter
	{
		public static string Export(MetricSnapshot snapshot)
		{
			if (snapshot == null)
			{
				throw new InvalidOperationException("No snapshot available yet — wait a moment after starting the scan.");
			}
			string text = Path.Combine(Paths.BepInExRootPath, "profiler_reports");
			Directory.CreateDirectory(text);
			string path = $"profiler_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.txt";
			string text2 = Path.Combine(text, path);
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("=================================================");
			stringBuilder.AppendLine("     Valheim Mod Profiler -- Performance Report   ");
			stringBuilder.AppendLine("=================================================");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine($"  Date    : {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
			stringBuilder.AppendLine($"  FPS     : {snapshot.Fps:0.0}");
			stringBuilder.AppendLine($"  Mods    : {snapshot.Mods?.Count ?? 0}");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine(string.Format("  {0,-38} {1,9} {2,9} {3,9}", "Mod", "Avg CPU", "Impact", "Calls/s"));
			stringBuilder.AppendLine("  " + new string('-', 68));
			if (snapshot.Mods != null)
			{
				foreach (ModMetric mod in snapshot.Mods)
				{
					stringBuilder.AppendLine($"  {mod.Name,-38} " + string.Format("{0,9} ", mod.CachedAvgMsString + "ms") + $"{mod.CachedImpactString,9} " + $"{mod.CachedCallsString,9}");
					if (mod.TopMethods == null || mod.TopMethods.Count <= 0)
					{
						continue;
					}
					foreach (MethodDetail topMethod in mod.TopMethods)
					{
						stringBuilder.AppendLine($"    └ {topMethod.MethodName,-36} " + string.Format("{0,9} ", topMethod.CachedAvgMs + "ms") + string.Format("{0,9} ", "") + $"{topMethod.CachedCalls,9}");
					}
					stringBuilder.AppendLine();
				}
			}
			List<AlertEntry> alerts = ProfilerDataManager.GetAlerts();
			if (alerts.Count > 0)
			{
				stringBuilder.AppendLine();
				stringBuilder.AppendLine($"  Spikes (>{16.6f:0.0}ms detected):");
				stringBuilder.AppendLine("  " + new string('-', 50));
				foreach (AlertEntry item in alerts)
				{
					stringBuilder.AppendLine($"  [{item.Timestamp}]  {item.ModName,-38}  peak {item.PeakMs:0.0} ms");
				}
			}
			File.WriteAllText(text2, stringBuilder.ToString(), Encoding.UTF8);
			return text2;
		}
	}
}
namespace ValheimModProfiler.Core
{
	[BepInPlugin("com.debug.valheim_profiler", "Valheim Mod Profiler", "1.0.0")]
	[BepInProcess("valheim.exe")]
	public class ModEntry : BaseUnityPlugin
	{
		public static ManualLogSource Logger;

		public static ModEntry Instance { get; private set; }

		private void Awake()
		{
			Instance = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			ProfilerDataManager.Initialize();
		}

		private void Start()
		{
			//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_0010: Expected O, but got Unknown
			GameObject val = new GameObject("ValheimProfilerOverlay");
			Object.DontDestroyOnLoad((Object)val);
			val.AddComponent<IngameProfiler>();
			Logger.LogInfo((object)"Profiler UI Ready. Press F8 in game.");
		}

		public void StartScanning()
		{
			if (!ProfilerBootstrapper.IsScanning)
			{
				((MonoBehaviour)this).StartCoroutine(ProfilerBootstrapper.ApplyHooksCoroutine());
			}
		}

		public void StopMonitoring()
		{
			ProfilerBootstrapper.StopMonitoring();
		}
	}
}