Decompiled source of LagMeter v1.1.0

LagMeter.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("LagMeter")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("LagMeter")]
[assembly: AssemblyTitle("LagMeter")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace LagMeter
{
	[BepInPlugin("pieter.valheim.lagmeter", "Lag Meter", "1.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "pieter.valheim.lagmeter";

		public const string Name = "Lag Meter";

		public const string Version = "1.1.0";

		private static ConfigEntry<bool> _enabled;

		private static ConfigEntry<int> _interval;

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

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

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

		private static readonly List<string> Order = new List<string>();

		private static FieldInfo _sceneInstances;

		private float _start;

		private int _frames;

		private float _worst;

		private int _gc0;

		private int _reports;

		private void Awake()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Measure and log. Turn off (or remove the mod) when done.");
			_interval = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Interval (seconds)", 10, new ConfigDescription("Seconds per log line.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 120), Array.Empty<object>()));
			_sceneInstances = AccessTools.Field(typeof(ZNetScene), "m_instances");
			Harmony harmony = new Harmony("pieter.valheim.lagmeter");
			Measure(harmony, "Scripts (Update)", typeof(MonoUpdaters), "Update");
			Measure(harmony, "Scripts (FixedUpdate)", typeof(MonoUpdaters), "FixedUpdate");
			Measure(harmony, "Scripts (LateUpdate)", typeof(MonoUpdaters), "LateUpdate");
			Measure(harmony, "Object streaming", typeof(ZNetScene), "Update");
			Measure(harmony, "Network", typeof(ZNet), "Update");
			Measure(harmony, "Network (late)", typeof(ZNet), "LateUpdate");
			Measure(harmony, "Building wear/support", typeof(WearNTear), "UpdateWear");
			Measure(harmony, "Plants", typeof(Plant), "SUpdate");
			Measure(harmony, "Fires", typeof(Fireplace), "UpdateFireplace");
			Measure(harmony, "Smoke render", typeof(SmokeRenderer), "LateUpdate");
			Measure(harmony, "Grass", typeof(ClutterSystem), "LateUpdate");
			Measure(harmony, "Grass (update)", typeof(ClutterSystem), "Update");
			Measure(harmony, "Light LOD", typeof(LightLod), "FixedUpdate");
			Measure(harmony, "Light LOD (update)", typeof(LightLod), "Update");
			Measure(harmony, "Cooking stations", typeof(CookingStation), "Update");
			Measure(harmony, "Terrain", typeof(TerrainComp), "Update");
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Timing {Order.Count} game systems; a [Lag] line is written every {_interval.Value}s in a world.");
		}

		private void Measure(Harmony harmony, string label, Type type, string method)
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Expected O, but got Unknown
			//IL_00d4: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.DeclaredMethod(type, method, (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Not timed, " + type.Name + "." + method + " not found."));
				return;
			}
			Labels[methodInfo] = label;
			if (!Ticks.ContainsKey(label))
			{
				Ticks[label] = 0L;
				Calls[label] = 0;
				Order.Add(label);
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), "StartTimer", (Type[])null)
			{
				priority = 800
			}, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "StopTimer", (Type[])null)
			{
				priority = 0
			}, (HarmonyMethod)null);
		}

		private static void StartTimer(out long __state)
		{
			__state = Stopwatch.GetTimestamp();
		}

		private static Exception StopTimer(Exception __exception, MethodBase __originalMethod, long __state)
		{
			if (_enabled.Value && Labels.TryGetValue(__originalMethod, out var value))
			{
				Ticks[value] += Stopwatch.GetTimestamp() - __state;
				Calls[value]++;
			}
			return __exception;
		}

		private void Update()
		{
			if (!_enabled.Value || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				_start = Time.realtimeSinceStartup;
				_frames = 0;
				_worst = 0f;
				_gc0 = GC.CollectionCount(0);
				Reset();
				return;
			}
			_frames++;
			_worst = Mathf.Max(_worst, Time.unscaledDeltaTime);
			float num = Time.realtimeSinceStartup - _start;
			if (!(num < (float)_interval.Value))
			{
				try
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)Report(num));
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Report failed: " + ex.Message));
				}
				_start = Time.realtimeSinceStartup;
				_frames = 0;
				_worst = 0f;
				_gc0 = GC.CollectionCount(0);
				Reset();
			}
		}

		private static void Reset()
		{
			foreach (string item in Order)
			{
				Ticks[item] = 0L;
				Calls[item] = 0;
			}
		}

		private string Report(float elapsed)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: 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_0053: Unknown result type (might be due to invalid IL or missing references)
			int frames = Mathf.Max(1, _frames);
			StringBuilder stringBuilder = new StringBuilder();
			Vector3 position = ((Component)Player.m_localPlayer).transform.position;
			stringBuilder.Append($"[Lag] {elapsed:0}s at ({position.x:0}, {position.z:0}): {(float)_frames / elapsed:0.0} fps avg ({1000f * elapsed / (float)frames:0.0} ms/frame), " + $"worst frame {_worst * 1000f:0} ms, GCs {GC.CollectionCount(0) - _gc0}");
			Light[] source = Object.FindObjectsOfType<Light>();
			int num = source.Count((Light l) => ((Behaviour)l).enabled && (int)l.type != 1);
			int num2 = source.Count((Light l) => ((Behaviour)l).enabled && (int)l.type != 1 && (int)l.shadows > 0);
			Fireplace[] array = Object.FindObjectsOfType<Fireplace>();
			int num3 = array.Count((Fireplace f) => f.IsBurning());
			Rigidbody[] array2 = Object.FindObjectsOfType<Rigidbody>();
			int num4 = array2.Count((Rigidbody b) => !b.IsSleeping() && !b.isKinematic);
			int num5 = SlowUpdate.GetAllInstaces().Count((SlowUpdate s) => s is Plant);
			int num6 = (_sceneInstances?.GetValue(ZNetScene.instance) as ICollection)?.Count ?? (-1);
			stringBuilder.Append($"\n      loaded: point lights {num} on ({num2} with shadows), smoke puffs {Smoke.Instances.Count}, " + $"fires {num3}/{array.Length} burning, building pieces {WearNTear.GetAllInstances().Count}, plants {num5}, " + $"objects {num6}, physics bodies {array2.Length} ({num4} moving)");
			double freq = Stopwatch.Frequency;
			IEnumerable<string> values = from l in Order
				where Calls[l] > 0
				orderby Ticks[l] descending
				select $"{l} {(double)Ticks[l] * 1000.0 / freq / (double)frames:0.00}";
			stringBuilder.Append("\n      ms per frame: " + string.Join(", ", values));
			if (++_reports % Math.Max(1, 60 / _interval.Value) == 1 && _sceneInstances?.GetValue(ZNetScene.instance) is IDictionary dictionary)
			{
				Dictionary<string, int> dictionary2 = new Dictionary<string, int>();
				foreach (object value2 in dictionary.Values)
				{
					ZNetView val = (ZNetView)((value2 is ZNetView) ? value2 : null);
					if (!((Object)(object)val == (Object)null))
					{
						string prefabName = Utils.GetPrefabName(((Component)val).gameObject);
						dictionary2.TryGetValue(prefabName, out var value);
						dictionary2[prefabName] = value + 1;
					}
				}
				IEnumerable<string> values2 = from p in dictionary2.OrderByDescending((KeyValuePair<string, int> p) => p.Value).Take(25)
					select $"{p.Key} {p.Value}";
				stringBuilder.Append($"\n      top objects ({dictionary2.Count} types): " + string.Join(", ", values2));
				IEnumerable<string> values3 = from g in (from l in source
						where ((Behaviour)l).enabled && (int)l.type != 1
						group l by Utils.GetPrefabName(((Component)((Component)l).transform.root).gameObject) into g
						orderby g.Count() descending
						select g).Take(15)
					select $"{g.Key} {g.Count()}" + (g.Any((Light l) => (int)l.shadows > 0) ? " (shadows)" : "");
				stringBuilder.Append("\n      lights by object: " + string.Join(", ", values3));
			}
			return stringBuilder.ToString();
		}
	}
}