Please disclose if your mod was created primarily using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ProfilerValheim Zeta v1.1.2
BepInEx/plugins/ProfilerValheim.Zeta.dll
Decompiled a week agousing 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 UnityEngine.Events; using UnityEngine.UI; 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); } } } public static class WelcomeMessage { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static UnityAction <>9__1_0; internal void <ShowWelcomePanel>b__1_0() { Application.OpenURL("https://steamcommunity.com/id/Skarif_W/"); } } private static bool _shown; [HarmonyPatch(typeof(FejdStartup), "Start")] [HarmonyPostfix] public static void ShowWelcomePanel() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_00a2: 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_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00df: 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_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: 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_0176: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02bc: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_030e: Unknown result type (might be due to invalid IL or missing references) //IL_0389: Unknown result type (might be due to invalid IL or missing references) //IL_03a3: Unknown result type (might be due to invalid IL or missing references) //IL_03b2: Unknown result type (might be due to invalid IL or missing references) //IL_03c1: Unknown result type (might be due to invalid IL or missing references) //IL_03ea: Unknown result type (might be due to invalid IL or missing references) //IL_0402: Unknown result type (might be due to invalid IL or missing references) //IL_0411: Unknown result type (might be due to invalid IL or missing references) //IL_0420: Unknown result type (might be due to invalid IL or missing references) //IL_044d: Unknown result type (might be due to invalid IL or missing references) //IL_0479: Unknown result type (might be due to invalid IL or missing references) //IL_048e: Unknown result type (might be due to invalid IL or missing references) //IL_049d: Unknown result type (might be due to invalid IL or missing references) //IL_04ac: Unknown result type (might be due to invalid IL or missing references) //IL_04dd: Unknown result type (might be due to invalid IL or missing references) //IL_04f6: Unknown result type (might be due to invalid IL or missing references) //IL_0502: Unknown result type (might be due to invalid IL or missing references) //IL_050c: Expected O, but got Unknown //IL_052b: Unknown result type (might be due to invalid IL or missing references) //IL_0543: Unknown result type (might be due to invalid IL or missing references) //IL_0552: Unknown result type (might be due to invalid IL or missing references) //IL_0561: Unknown result type (might be due to invalid IL or missing references) //IL_057e: Unknown result type (might be due to invalid IL or missing references) //IL_05a7: Unknown result type (might be due to invalid IL or missing references) //IL_05c0: Unknown result type (might be due to invalid IL or missing references) //IL_05cc: Unknown result type (might be due to invalid IL or missing references) //IL_05d6: Expected O, but got Unknown //IL_0327: Unknown result type (might be due to invalid IL or missing references) //IL_032c: Unknown result type (might be due to invalid IL or missing references) //IL_0332: Expected O, but got Unknown if (_shown || PlayerPrefs.GetInt("ProfilerZeta_HideWelcome", 0) == 1) { return; } _shown = true; GameObject canvasGO = new GameObject("WelcomeCanvas_ProfilerZeta"); canvasGO.layer = 5; Canvas obj = canvasGO.AddComponent<Canvas>(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 9999; canvasGO.AddComponent<CanvasScaler>(); canvasGO.AddComponent<GraphicRaycaster>(); Object.DontDestroyOnLoad((Object)(object)canvasGO); GameObject val = MakeRect("Background", canvasGO.transform, new Color(0.05f, 0.05f, 0.05f, 0.96f)); RectTransform component = val.GetComponent<RectTransform>(); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0.5f, 0.5f); component.anchorMax = val2; component.anchorMin = val2; component.sizeDelta = new Vector2(550f, 480f); component.anchoredPosition = Vector2.zero; GameObject val3 = MakeRect("Header", val.transform, new Color(0.1f, 0.1f, 0.1f, 1f)); Stretch(val3, new Vector2(0f, 0.86f), Vector2.one); MakeText("Title", val3.transform, "ProfilerValheim.Zeta", 22, new Color(1f, 0.85f, 0.4f), (FontStyle)1); Text obj2 = MakeText("Body", val.transform, "Hi! Thank you for using my mod.\nThe mod is in beta, so I'd love to hear any feedback:\nbugs, suggestions, logs - anything.\n\nПривет! Спасибо за использование моего мода.\nМод находится в бете, поэтому я буду рад любым отзывам:\nбагам, предложениям, логам - чему угодно.\n\nMy Steam account / Мой аккаунт Steam:", 15, Color.white, (FontStyle)0); obj2.lineSpacing = 1.2f; obj2.alignment = (TextAnchor)1; SetAnchors(((Component)obj2).gameObject, new Vector2(0f, 0.35f), new Vector2(1f, 0.86f), new Vector2(20f, 0f), new Vector2(-20f, -15f)); GameObject val4 = MakeRect("Link", val.transform, Color.clear); SetAnchors(val4, new Vector2(0f, 0.25f), new Vector2(1f, 0.35f), new Vector2(20f, 0f), new Vector2(-20f, 0f)); MakeText("LinkText", val4.transform, "https://steamcommunity.com/id/Skarif_W/", 15, new Color(0.35f, 0.7f, 1f), (FontStyle)0); RectTransform component2 = MakeRect("Underline", val4.transform, new Color(0.35f, 0.7f, 1f)).GetComponent<RectTransform>(); component2.anchorMin = new Vector2(0.05f, 0f); component2.anchorMax = new Vector2(0.95f, 0f); component2.sizeDelta = new Vector2(0f, 1.5f); component2.anchoredPosition = new Vector2(0f, 3f); Image component3 = val4.GetComponent<Image>(); Color highlight = new Color(0.35f, 0.7f, 1f, 0.15f); Color pressed = new Color(0.35f, 0.7f, 1f, 0.3f); object obj3 = <>c.<>9__1_0; if (obj3 == null) { UnityAction val5 = delegate { Application.OpenURL("https://steamcommunity.com/id/Skarif_W/"); }; <>c.<>9__1_0 = val5; obj3 = (object)val5; } MakeButton(val4, component3, highlight, pressed, (UnityAction)obj3); MakeSeparator("Sep1", val.transform, 0.22f); MakeSeparator("Sep2", val.transform, 0.14f); bool dontShow = false; SetPivotAnchor(((Component)MakeText("Label", val.transform, "Don't show anymore / Больше не показывать", 13, new Color(0.75f, 0.75f, 0.75f), (FontStyle)0)).gameObject, new Vector2(0.5f, 0f), new Vector2(280f, 30f), new Vector2(20f, 85f)); GameObject val6 = MakeRect("Box", val.transform, new Color(0.25f, 0.25f, 0.25f, 1f)); SetPivotAnchor(val6, new Vector2(0.5f, 0f), new Vector2(18f, 18f), new Vector2(-135f, 85f)); GameObject tickGO = ((Component)MakeText("Tick", val6.transform, "v", 13, new Color(0.35f, 0.7f, 1f), (FontStyle)1)).gameObject; tickGO.SetActive(false); GameObject obj4 = MakeRect("CheckHit", val.transform, Color.clear); SetPivotAnchor(obj4, new Vector2(0.5f, 0f), new Vector2(320f, 30f), new Vector2(0f, 85f)); Image boxImg = val6.GetComponent<Image>(); MakeButton(obj4, obj4.GetComponent<Image>(), new Color(1f, 1f, 1f, 0.05f), new Color(1f, 1f, 1f, 0.1f), (UnityAction)delegate { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) dontShow = !dontShow; tickGO.SetActive(dontShow); ((Graphic)boxImg).color = (dontShow ? new Color(0.1f, 0.25f, 0.45f, 1f) : new Color(0.25f, 0.25f, 0.25f, 1f)); }); GameObject val7 = MakeRect("Close", val.transform, new Color(0.65f, 0.15f, 0.15f, 1f)); SetPivotAnchor(val7, new Vector2(0.5f, 0f), new Vector2(140f, 34f), new Vector2(0f, 20f)); MakeText("CloseText", val7.transform, "Close / Закрыть", 15, Color.white, (FontStyle)0); MakeButton(val7, val7.GetComponent<Image>(), new Color(0.85f, 0.25f, 0.25f, 1f), new Color(0.45f, 0.08f, 0.08f, 1f), (UnityAction)delegate { if (dontShow) { PlayerPrefs.SetInt("ProfilerZeta_HideWelcome", 1); } Object.Destroy((Object)(object)canvasGO); }); } private static GameObject MakeRect(string name, Transform parent, Color color) { //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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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_002e: Expected O, but got Unknown GameObject val = new GameObject(name) { layer = 5 }; val.transform.SetParent(parent, false); val.AddComponent<RectTransform>(); ((Graphic)val.AddComponent<Image>()).color = color; return val; } private static Text MakeText(string name, Transform parent, string text, int size, Color color, FontStyle style = 0) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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) GameObject val = new GameObject(name); val.layer = 5; val.transform.SetParent(parent, false); val.AddComponent<RectTransform>(); Text obj = val.AddComponent<Text>(); obj.text = text; obj.font = GetFont(); obj.fontSize = size; ((Graphic)obj).color = color; obj.fontStyle = style; obj.alignment = (TextAnchor)4; Stretch(val, Vector2.zero, Vector2.one); return obj; } private static void MakeButton(GameObject go, Image target, Color highlight, Color pressed, UnityAction action) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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_0032: Unknown result type (might be due to invalid IL or missing references) Button obj = go.AddComponent<Button>(); ((Selectable)obj).targetGraphic = (Graphic)(object)target; ColorBlock colors = ((Selectable)obj).colors; ((ColorBlock)(ref colors)).normalColor = ((Graphic)target).color; ((ColorBlock)(ref colors)).highlightedColor = highlight; ((ColorBlock)(ref colors)).pressedColor = pressed; ((Selectable)obj).colors = colors; ((UnityEvent)obj.onClick).AddListener(action); } private static void MakeSeparator(string name, Transform parent, float anchorY) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) RectTransform component = MakeRect(name, parent, new Color(1f, 1f, 1f, 0.12f)).GetComponent<RectTransform>(); component.anchorMin = new Vector2(0f, anchorY); component.anchorMax = new Vector2(1f, anchorY); component.sizeDelta = new Vector2(0f, 1f); component.anchoredPosition = Vector2.zero; } private static void Stretch(GameObject go, Vector2 min, Vector2 max) { //IL_0007: 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_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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: Unknown result type (might be due to invalid IL or missing references) RectTransform component = go.GetComponent<RectTransform>(); component.anchorMin = min; component.anchorMax = max; Vector2 offsetMin = (component.offsetMax = Vector2.zero); component.offsetMin = offsetMin; } private static void SetAnchors(GameObject go, Vector2 min, Vector2 max, Vector2 offsetMin, Vector2 offsetMax) { //IL_0007: 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_0015: 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) RectTransform component = go.GetComponent<RectTransform>(); component.anchorMin = min; component.anchorMax = max; component.offsetMin = offsetMin; component.offsetMax = offsetMax; } private static void SetPivotAnchor(GameObject go, Vector2 anchor, Vector2 size, Vector2 pos) { //IL_0008: 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_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) RectTransform component = go.GetComponent<RectTransform>(); Vector2 anchorMin = (component.anchorMax = anchor); component.anchorMin = anchorMin; component.sizeDelta = size; component.anchoredPosition = pos; } private static Font GetFont() { Font[] array = Resources.FindObjectsOfTypeAll<Font>(); foreach (Font val in array) { if ((Object)(object)val != (Object)null && ((Object)val).name.Contains("Averia")) { return val; } } return Resources.GetBuiltinResource<Font>("Arial.ttf"); } } } 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() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) Instance = this; Logger = ((BaseUnityPlugin)this).Logger; ProfilerDataManager.Initialize(); new Harmony("com.debug.valheim_profiler.welcome").PatchAll(typeof(WelcomeMessage)); } 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(); } } }