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";
}
}