Decompiled source of ServX v1.1.1

ServX.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using ServerX;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ServerExtras")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.1.1.0")]
[assembly: AssemblyInformationalVersion("1.1.1")]
[assembly: AssemblyProduct("ServX")]
[assembly: AssemblyTitle("ServerExtras")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
public class PingGraph : MonoBehaviour
{
	private GameObject graphCanvas;

	private RectTransform graphContainer;

	private Text maxPingText;

	private Text avgPingText;

	private Text minPingText;

	private Text changeText;

	private Text timerText;

	private bool isInitialized = false;

	private bool sessionSummaryDisplayed = false;

	private float tickTimer = 0f;

	private List<GameObject> linePool = new List<GameObject>();

	private void Update()
	{
		PingTracker.Ticks++;
		if (PingTracker.DisableGraph)
		{
			if ((Object)(object)graphCanvas != (Object)null && graphCanvas.activeSelf)
			{
				graphCanvas.SetActive(false);
			}
			if (isInitialized)
			{
				ResetGraph();
			}
		}
		else if ((Object)(object)TabMenu._current != (Object)null && TabMenu._current._isOpen && (Object)(object)TabMenu._current._whoCell != (Object)null)
		{
			if (!isInitialized)
			{
				InitializeGraph();
			}
			if (isInitialized)
			{
				UpdateGraph();
				UpdateTextValues();
				PingTracker.Timer += Time.deltaTime;
				tickTimer += Time.deltaTime;
				if (tickTimer >= 1f)
				{
					PingTracker.LastTPS = PingTracker.Ticks;
					PingTracker.TPSHistory.Add(PingTracker.LastTPS);
					PingTracker.Ticks = 0;
					tickTimer = 0f;
				}
			}
		}
		else
		{
			if ((Object)(object)graphCanvas != (Object)null && graphCanvas.activeSelf)
			{
				graphCanvas.SetActive(false);
			}
			if (isInitialized && !sessionSummaryDisplayed)
			{
				DisplaySessionSummary();
				sessionSummaryDisplayed = true;
			}
			if (isInitialized)
			{
				ResetGraph();
			}
		}
	}

	private void InitializeGraph()
	{
		ResetGraph();
		CreateGraphCanvas();
		CreateTextElements();
		isInitialized = true;
	}

	private void CreateGraphCanvas()
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Expected O, but got Unknown
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Expected O, but got Unknown
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Expected O, but got Unknown
		//IL_0148: Unknown result type (might be due to invalid IL or missing references)
		//IL_0167: Unknown result type (might be due to invalid IL or missing references)
		//IL_017e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0195: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ac: 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)
		if (!((Object)(object)graphCanvas != (Object)null))
		{
			graphCanvas = new GameObject("PingGraphCanvas");
			Canvas val = graphCanvas.AddComponent<Canvas>();
			val.renderMode = (RenderMode)0;
			graphCanvas.AddComponent<CanvasScaler>();
			graphCanvas.AddComponent<GraphicRaycaster>();
			GameObject val2 = new GameObject("GraphContainer");
			val2.transform.SetParent(graphCanvas.transform);
			graphContainer = val2.AddComponent<RectTransform>();
			graphContainer.sizeDelta = new Vector2(300f, 150f);
			graphContainer.anchorMin = new Vector2(1f, 1f);
			graphContainer.anchorMax = new Vector2(1f, 1f);
			graphContainer.pivot = new Vector2(1f, 1f);
			graphContainer.anchoredPosition = new Vector2(-10f, -20f);
			GameObject val3 = new GameObject("GraphBackground");
			val3.transform.SetParent(((Component)graphContainer).transform, false);
			Image val4 = val3.AddComponent<Image>();
			((Graphic)val4).color = new Color(0.1f, 0.1f, 0.1f, 0.8f);
			RectTransform rectTransform = ((Graphic)val4).rectTransform;
			rectTransform.sizeDelta = new Vector2(500f, 150f);
			rectTransform.anchorMin = new Vector2(0f, 0f);
			rectTransform.anchorMax = new Vector2(1f, 1f);
			rectTransform.offsetMin = new Vector2(-120f, 0f);
			rectTransform.offsetMax = Vector2.zero;
			graphCanvas.SetActive(false);
		}
	}

	private void CreateTextElements()
	{
		//IL_0012: 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_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		maxPingText = CreateText("MaxPingText", new Vector2(-320f, -10f), Color.red);
		avgPingText = CreateText("AvgPingText", new Vector2(-320f, -30f), Color.green);
		minPingText = CreateText("MinPingText", new Vector2(-320f, -50f), Color.cyan);
		changeText = CreateText("ChangePingText", new Vector2(-320f, -70f), Color.yellow);
		timerText = CreateText("TimerText", new Vector2(-320f, -90f), Color.white);
	}

	private Text CreateText(string name, Vector2 position, Color color)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Expected O, but got Unknown
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: 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_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject(name);
		val.transform.SetParent(graphCanvas.transform);
		Text val2 = val.AddComponent<Text>();
		val2.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
		val2.fontSize = 14;
		((Graphic)val2).color = color;
		val2.alignment = (TextAnchor)3;
		val2.horizontalOverflow = (HorizontalWrapMode)1;
		val2.verticalOverflow = (VerticalWrapMode)1;
		RectTransform rectTransform = ((Graphic)val2).rectTransform;
		rectTransform.anchorMin = new Vector2(1f, 1f);
		rectTransform.anchorMax = new Vector2(1f, 1f);
		rectTransform.pivot = new Vector2(1f, 1f);
		rectTransform.anchoredPosition = position;
		return val2;
	}

	public void UpdateGraph()
	{
		//IL_0059: 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_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)graphCanvas == (Object)null || (Object)(object)graphContainer == (Object)null)
		{
			Plugin.Logger.LogWarning((object)"PingGraph: Graph canvas or container not initialized.");
		}
		else
		{
			if (PingTracker.AveragePings.Count == 0)
			{
				return;
			}
			float y = graphContainer.sizeDelta.y;
			float x = graphContainer.sizeDelta.x;
			float step = x / (float)Mathf.Max(PingTracker.AveragePings.Count - 1, 1);
			int num = 0;
			for (int i = 1; i < PingTracker.AveragePings.Count; i++)
			{
				CreateGraphLine(PingTracker.AveragePings[i - 1], PingTracker.AveragePings[i], i - 1, step, y, Color.green, num++);
				CreateGraphLine(PingTracker.MaxPings[i - 1], PingTracker.MaxPings[i], i - 1, step, y, Color.red, num++);
				CreateGraphLine(PingTracker.MinPings[i - 1], PingTracker.MinPings[i], i - 1, step, y, Color.cyan, num++);
			}
			for (int j = num; j < linePool.Count; j++)
			{
				if ((Object)(object)linePool[j] != (Object)null)
				{
					linePool[j].SetActive(false);
				}
			}
			graphCanvas.SetActive(true);
		}
	}

	private void CreateGraphLine(float value1, float value2, int index, float step, float graphHeight, Color color, int poolIndex)
	{
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: Expected O, but got Unknown
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_011d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0134: Unknown result type (might be due to invalid IL or missing references)
		//IL_014b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0162: Unknown result type (might be due to invalid IL or missing references)
		//IL_0172: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
		float num = Mathf.Clamp(value1, 0f, 500f);
		float num2 = Mathf.Clamp(value2, 0f, 500f);
		float num3 = (float)index * step;
		float num4 = (float)(index + 1) * step;
		float num5 = num / 500f * graphHeight;
		float num6 = num2 / 500f * graphHeight;
		GameObject val;
		if (poolIndex < linePool.Count && (Object)(object)linePool[poolIndex] != (Object)null)
		{
			val = linePool[poolIndex];
			val.SetActive(true);
		}
		else
		{
			val = new GameObject("GraphLine");
			val.transform.SetParent((Transform)(object)graphContainer);
			Image val2 = val.AddComponent<Image>();
			linePool.Add(val);
		}
		Image component = val.GetComponent<Image>();
		((Graphic)component).color = color;
		RectTransform val3 = val.GetComponent<RectTransform>();
		if ((Object)(object)val3 == (Object)null)
		{
			val3 = val.AddComponent<RectTransform>();
		}
		float num7 = Vector2.Distance(new Vector2(num3, num5), new Vector2(num4, num6));
		val3.sizeDelta = new Vector2(num7, 2f);
		val3.anchorMin = new Vector2(0f, 0f);
		val3.anchorMax = new Vector2(0f, 0f);
		val3.pivot = new Vector2(0f, 0.5f);
		val3.anchoredPosition = new Vector2(num3, num5);
		float num8 = Mathf.Atan2(num6 - num5, num4 - num3) * 57.29578f;
		((Transform)val3).localRotation = Quaternion.Euler(0f, 0f, num8);
	}

	public void UpdateTextValues()
	{
		if (!((Object)(object)maxPingText == (Object)null) && !((Object)(object)avgPingText == (Object)null) && !((Object)(object)minPingText == (Object)null) && !((Object)(object)changeText == (Object)null) && !((Object)(object)timerText == (Object)null) && PingTracker.AveragePings.Count > 0)
		{
			avgPingText.text = $"Avg: {PingTracker.AveragePings.Last():F1} ms";
			maxPingText.text = $"Max: {PingTracker.MaxPings.Last():F1} ms";
			minPingText.text = $"Min: {PingTracker.MinPings.Last():F1} ms";
			float num = PingTracker.FirstAveragePing ?? PingTracker.AveragePings.First();
			float num2 = PingTracker.AveragePings.Last();
			float num3 = num2 - num;
			changeText.text = $"ΔAvg: {num3:+0.0;-0.0} ms";
			TimeSpan timeSpan = TimeSpan.FromSeconds(PingTracker.Timer);
			timerText.text = $"Time: {timeSpan:hh\\:mm\\:ss}";
		}
	}

	private void DisplaySessionSummary()
	{
		if (PingTracker.AveragePings.Count > 0 && !PingTracker.DisableSummary)
		{
			float num = PingTracker.AveragePings.Average();
			float num2 = ((PingTracker.PlayerPings.Count > 0) ? ((float)PingTracker.PlayerPings.Average()) : 0f);
			float num3 = ((PingTracker.TPSHistory.Count > 0) ? ((float)PingTracker.TPSHistory.Average()) : 0f);
			float num4 = PingTracker.AveragePings.Last() - (PingTracker.FirstAveragePing ?? PingTracker.AveragePings.First());
			TimeSpan timeSpan = TimeSpan.FromSeconds(PingTracker.Timer);
			Plugin.Logger.LogInfo((object)"\r\n");
			Plugin.Logger.LogInfo((object)"======= Session Summary =======");
			Plugin.Logger.LogInfo((object)$"   • Average Ping:   {num:F1} ms");
			Plugin.Logger.LogInfo((object)$"   • Delta Avg Ping: {num4:+0.0;-0.0} ms");
			Plugin.Logger.LogInfo((object)$"   • Highest Ping:   {PingTracker.HighestPing:F1} ms");
			Plugin.Logger.LogInfo((object)$"   • Lowest Ping:    {PingTracker.LowestPing:F1} ms");
			Plugin.Logger.LogInfo((object)$"   • Player Ping:    {num2:F1} ms (average)");
			Plugin.Logger.LogInfo((object)$"   • TPS:            {num3:F1} (average)");
			Plugin.Logger.LogInfo((object)"--------------------------------");
			Plugin.Logger.LogInfo((object)$"   Total Time Elapsed: {timeSpan:hh\\:mm\\:ss}");
			Plugin.Logger.LogInfo((object)"================================\r\n\r\n");
		}
	}

	private void ResetGraph()
	{
		if ((Object)(object)graphCanvas != (Object)null)
		{
			Object.Destroy((Object)(object)graphCanvas);
		}
		maxPingText = null;
		avgPingText = null;
		minPingText = null;
		changeText = null;
		timerText = null;
		isInitialized = false;
		PingTracker.AveragePings.Clear();
		PingTracker.MaxPings.Clear();
		PingTracker.MinPings.Clear();
		PingTracker.HighestPing = null;
		PingTracker.LowestPing = null;
		PingTracker.FirstAveragePing = null;
		PingTracker.Timer = 0f;
		PingTracker.Ticks = 0;
		PingTracker.LastTPS = 0;
		sessionSummaryDisplayed = false;
		foreach (GameObject item in linePool)
		{
			Object.Destroy((Object)(object)item);
		}
		linePool.Clear();
	}
}
namespace ServerX
{
	[BepInPlugin("ServerExtras", "ServX", "1.1.1")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Logger;

		private void Awake()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			Logger = ((BaseUnityPlugin)this).Logger;
			Logger.LogInfo((object)"Plugin ServerExtras is loaded!");
			Harmony val = new Harmony("ServerExtras");
			val.PatchAll();
			((Component)this).gameObject.AddComponent<PingGraph>();
		}
	}
	public static class PingTracker
	{
		public static List<float> AveragePings = new List<float>();

		public static List<float> MaxPings = new List<float>();

		public static List<float> MinPings = new List<float>();

		public static List<int> PlayerPings = new List<int>();

		public static List<int> TPSHistory = new List<int>();

		public static bool ShowGraph = false;

		public static bool DisableGraph = false;

		public static bool DisableSummary = false;

		public static float? FirstAveragePing = null;

		public static float? HighestPing = null;

		public static float? LowestPing = null;

		public static float Timer = 0f;

		public static int Ticks = 0;

		public static int LastTPS = 0;

		public static float PingUpdateTimer = 0f;
	}
	[HarmonyPatch(typeof(WhoMenuCell), "Handle_CellUpdate")]
	public static class WhoMenuCellPatch
	{
		private static float lastPingUpdate;

		private static void Postfix(WhoMenuCell __instance)
		{
			try
			{
				PingTracker.ShowGraph = true;
				WhoListDataEntry[] array = Object.FindObjectsOfType<WhoListDataEntry>();
				if (array.Length == 0)
				{
					return;
				}
				List<int> list = (from entry in array
					where (Object)(object)entry._player != (Object)null && entry._player._latency > 0
					select entry._player._latency).ToList();
				int num = ((Player._mainPlayer._latency > 0) ? Player._mainPlayer._latency : 0);
				if (num > 0)
				{
					list.Add(num);
				}
				if (list.Count <= 0)
				{
					return;
				}
				float num2 = (float)list.Average();
				float num3 = list.Max();
				float num4 = list.Min();
				if (!PingTracker.HighestPing.HasValue || num3 > PingTracker.HighestPing)
				{
					PingTracker.HighestPing = num3;
				}
				if (!PingTracker.LowestPing.HasValue || num4 < PingTracker.LowestPing)
				{
					PingTracker.LowestPing = num4;
				}
				if (!PingTracker.FirstAveragePing.HasValue)
				{
					PingTracker.FirstAveragePing = num2;
				}
				if (Time.time - lastPingUpdate >= 1f)
				{
					PingTracker.AveragePings.Add(num2);
					PingTracker.MaxPings.Add(num3);
					PingTracker.MinPings.Add(num4);
					PingTracker.PlayerPings.Add(num);
					if (PingTracker.AveragePings.Count > 200)
					{
						PingTracker.AveragePings.RemoveAt(0);
						PingTracker.MaxPings.RemoveAt(0);
						PingTracker.MinPings.RemoveAt(0);
					}
					lastPingUpdate = Time.time;
				}
				UpdatePingUI(__instance);
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"Error tracking ping data: {arg}");
			}
		}

		private static void UpdatePingUI(WhoMenuCell whoMenuCell)
		{
			try
			{
				FieldInfo fieldInfo = AccessTools.Field(typeof(WhoMenuCell), "_localPingCounterText");
				object? value = fieldInfo.GetValue(whoMenuCell);
				Text val = (Text)((value is Text) ? value : null);
				if ((Object)(object)val != (Object)null)
				{
					string text = val.text;
					string arg = ((PingTracker.LastTPS < 10) ? "#FF0000" : ((PingTracker.LastTPS < 20) ? "#FFFF00" : ((PingTracker.LastTPS >= 30) ? "#FF69B4" : "#00FF00")));
					string text2 = $"<color={arg}>TPS: {PingTracker.LastTPS}</color>";
					val.text = text + " " + text2;
				}
			}
			catch (Exception arg2)
			{
				Plugin.Logger.LogError((object)$"Error updating Ping UI: {arg2}");
			}
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "ServerExtras";

		public const string PLUGIN_NAME = "ServX";

		public const string PLUGIN_VERSION = "1.1.1";
	}
}