Decompiled source of NetworkQualityTracker v1.2.2

BepInEx/plugins/Hikaria.NetworkQualityTracker/Hikaria.NetworkQualityTracker.dll

Decompiled 6 months 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.Unity.IL2CPP.Utils;
using CellMenu;
using Hikaria.Core;
using Hikaria.Core.SNetworkExt;
using Hikaria.NetworkQualityTracker.Features;
using Hikaria.NetworkQualityTracker.Handlers;
using Hikaria.NetworkQualityTracker.Managers;
using Hikaria.NetworkQualityTracker.Utils;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using SNetwork;
using TMPro;
using TheArchive.Core;
using TheArchive.Core.Attributes;
using TheArchive.Core.Attributes.Feature.Settings;
using TheArchive.Core.FeaturesAPI;
using TheArchive.Core.Localization;
using TheArchive.Interfaces;
using TheArchive.Loader;
using TheArchive.Utilities;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("Hikaria.NetworkQualityTracker")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+667d7e207b4647f000accf81d10f4298d493ff08")]
[assembly: AssemblyProduct("Hikaria.NetworkQualityTracker")]
[assembly: AssemblyTitle("Hikaria.NetworkQualityTracker")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Hikaria.NetworkQualityTracker
{
	[ArchiveDependency(/*Could not decode attribute arguments.*/)]
	[ArchiveModule("Hikaria.NetworkQualityTracker", "NetworkQualityTracker", "1.2.2")]
	public class EntryPoint : IArchiveModule
	{
		public static EntryPoint Instance { get; private set; }

		public bool ApplyHarmonyPatches => false;

		public bool UsesLegacyPatches => false;

		public ArchiveLegacyPatcher Patcher { get; set; }

		public string ModuleGroup => "Network Quality Tracker";

		public Dictionary<Language, string> ModuleGroupLanguages => new Dictionary<Language, string>
		{
			{
				(Language)1,
				"网络质量跟踪器"
			},
			{
				(Language)0,
				"Network Quality Tracker"
			}
		};

		public void Init()
		{
			Instance = this;
			Logs.LogMessage("OK");
		}

		public void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
		}

		public void OnLateUpdate()
		{
		}

		public void OnExit()
		{
		}
	}
	internal static class Logs
	{
		private static IArchiveLogger _logger;

		private static IArchiveLogger Logger => _logger ?? (_logger = LoaderWrapper.CreateLoggerInstance("Hikaria.NetworkQualityTracker", ConsoleColor.White));

		public static void LogDebug(object data)
		{
			Logger.Debug(data.ToString());
		}

		public static void LogError(object data)
		{
			Logger.Error(data.ToString());
		}

		public static void LogInfo(object data)
		{
			Logger.Info(data.ToString());
		}

		public static void LogMessage(object data)
		{
			Logger.Msg(ConsoleColor.White, data.ToString());
		}

		public static void LogWarning(object data)
		{
			Logger.Warning(data.ToString());
		}

		public static void LogNotice(object data)
		{
			Logger.Notice(data.ToString());
		}

		public static void LogSuccess(object data)
		{
			Logger.Success(data.ToString());
		}

		public static void LogException(Exception ex)
		{
			Logger.Exception(ex);
		}
	}
	public static class PluginInfo
	{
		public const string GUID = "Hikaria.NetworkQualityTracker";

		public const string NAME = "NetworkQualityTracker";

		public const string VERSION = "1.2.2";
	}
}
namespace Hikaria.NetworkQualityTracker.Utils
{
	internal class Utils
	{
		public static void RGBToHSL(Color color, out float h, out float s, out float l)
		{
			//IL_0000: 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_000e: Unknown result type (might be due to invalid IL or missing references)
			float r = color.r;
			float g = color.g;
			float b = color.b;
			float num = Mathf.Max(r, Mathf.Max(g, b));
			float num2 = Mathf.Min(r, Mathf.Min(g, b));
			float num3 = num - num2;
			l = (num + num2) / 2f;
			if (num3 == 0f)
			{
				h = 0f;
			}
			else if (num == r)
			{
				h = (g - b) / num3;
				if (g < b)
				{
					h += 6f;
				}
			}
			else if (num == g)
			{
				h = 2f + (b - r) / num3;
			}
			else
			{
				h = 4f + (r - g) / num3;
			}
			h /= 6f;
			if (num3 == 0f)
			{
				s = 0f;
			}
			else
			{
				s = num3 / (1f - Math.Abs(2f * l - 1f));
			}
		}

		public static Color HSLToRGB(float h, float s, float l)
		{
			//IL_000b: 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)
			if (s == 0f)
			{
				return new Color(l, l, l);
			}
			float num = ((l < 0.5f) ? (l * (1f + s)) : (l + s - l * s));
			float p = 2f * l - num;
			float num2 = HueToRGB(p, num, h + 1f / 3f);
			float num3 = HueToRGB(p, num, h);
			float num4 = HueToRGB(p, num, h - 1f / 3f);
			return new Color(num2, num3, num4);
		}

		public static float HueToRGB(float p, float q, float t)
		{
			if (t < 0f)
			{
				t += 1f;
			}
			else if (t > 1f)
			{
				t -= 1f;
			}
			if (t < 1f / 6f)
			{
				return p + (q - p) * 6f * t;
			}
			if (t < 0.5f)
			{
				return q;
			}
			if (t < 2f / 3f)
			{
				return p + (q - p) * (2f / 3f - t) * 6f;
			}
			return p;
		}
	}
}
namespace Hikaria.NetworkQualityTracker.Managers
{
	public static class NetworkQualityManager
	{
		public struct pHeartbeat
		{
			public long Index;

			public pHeartbeat()
			{
				Index = 0L;
			}
		}

		public struct pToMasterNetworkQualityReport
		{
			public int ToMasterLatency;

			public int ToMasterPacketLoss;

			public int ToMasterNetworkJitter;

			public bool IsToMasterAlive;

			public pToMasterNetworkQualityReport(int toMasterLatency, int toMasterNetworkJitter, int toMasterPacketLossRate, bool isToMasterAlive)
			{
				ToMasterLatency = toMasterLatency;
				ToMasterPacketLoss = toMasterPacketLossRate;
				ToMasterNetworkJitter = toMasterNetworkJitter;
				IsToMasterAlive = isToMasterAlive;
			}
		}

		public struct pHeartbeatAck
		{
			public long Index;

			public pHeartbeatAck()
			{
				Index = 0L;
			}
		}

		public class NetworkQualityData
		{
			private Queue<int> NetworkJitterQueue = new Queue<int>(20);

			private Queue<bool> PacketReceiveQueue = new Queue<bool>(100);

			private Dictionary<long, bool> PacketReceiveLookup = new Dictionary<long, bool>(100);

			private static pHeartbeatAck heatbeatAck = new pHeartbeatAck();

			private bool HeartbeatStarted;

			private long HeartbeatStartIndex = -1L;

			private int PacketLossCount;

			private long LastReceivedTime = -1L;

			private const int NetworkJitterQueueMaxCap = 20;

			private const int PacketReceiveQueueMaxCap = 100;

			public string ToLocalLatencyColorHexString => GetColorHexString(60f, 150f, ToLocalLatency);

			public string ToLocalNetworkJitterColorHexString => GetColorHexString(20f, 100f, ToLocalNetworkJitter);

			public string ToLocalPacketLossColorHexString => GetColorHexString(0f, 10f, ToLocalPacketLossRate);

			public string ToMasterLatencyColorHexString => GetColorHexString(60f, 150f, ToMasterLatency);

			public string ToMasterNetworkJitterColorHexString => GetColorHexString(20f, 100f, ToMasterNetworkJitter);

			public string ToMasterPacketLossColorHexString => GetColorHexString(0f, 10f, ToMasterPacketLossRate);

			public int ToLocalPacketLossRate
			{
				get
				{
					if (PacketReceiveQueue.Count != 0)
					{
						return (int)((float)PacketLossCount * 100f / (float)PacketReceiveQueue.Count);
					}
					return 0;
				}
			}

			public int ToLocalLatency { get; private set; }

			public int ToLocalNetworkJitter { get; private set; }

			public int ToMasterLatency { get; internal set; }

			public int ToMasterNetworkJitter { get; internal set; }

			public int ToMasterPacketLossRate { get; internal set; }

			public SNet_Player Owner { get; private set; }

			public bool IsAlive { get; private set; }

			public bool IsToMasterAlive { get; internal set; }

			public NetworkQualityData(SNet_Player player)
			{
				Owner = player;
			}

			public void EnqueuePacket(long index, bool received)
			{
				PacketReceiveLookup.Remove(index - 100);
				if (PacketReceiveLookup.TryAdd(index, received))
				{
					if (PacketReceiveQueue.Count == 100 && !PacketReceiveQueue.Dequeue())
					{
						PacketLossCount--;
					}
					PacketReceiveQueue.Enqueue(received);
					if (!received)
					{
						PacketLossCount++;
					}
				}
			}

			public void ReceiveHeartbeat(pHeartbeat data)
			{
				SendHeartbeatAck(data);
			}

			public void ReceiveHeartbeatAck(pHeartbeatAck data)
			{
				if (!HeartbeatStarted)
				{
					LastReceivedTime = CurrentTime;
					PacketReceiveQueue.Clear();
					PacketReceiveLookup.Clear();
					PacketLossCount = 0;
					NetworkJitterQueue.Clear();
					HeartbeatStartIndex = data.Index;
					HeartbeatStarted = true;
				}
				long index = data.Index;
				if (HeartbeatSendTimeLookup.TryGetValue(index, out var value))
				{
					int toLocalLatency = ToLocalLatency;
					ToLocalLatency = (int)(CurrentTime - value);
					if (NetworkJitterQueue.Count == 20)
					{
						NetworkJitterQueue.Dequeue();
					}
					NetworkJitterQueue.Enqueue(Math.Abs(ToLocalLatency - toLocalLatency));
					ToLocalNetworkJitter = NetworkJitterQueue.Max();
					EnqueuePacket(index, received: true);
					LastReceivedTime = CurrentTime;
				}
			}

			public void ReceiveNetworkQualityReport(pToMasterNetworkQualityReport data)
			{
				ToMasterLatency = data.ToMasterLatency;
				ToMasterPacketLossRate = data.ToMasterPacketLoss;
				ToMasterNetworkJitter = data.ToMasterNetworkJitter;
				IsToMasterAlive = data.IsToMasterAlive;
			}

			public void CheckConnection()
			{
				IsAlive = (float)(CurrentTime - LastReceivedTime) <= 2000f;
				if (Owner.IsMaster && NetworkQualityDataLookup.TryGetValue(SNet.LocalPlayer.Lookup, out var value))
				{
					value.IsToMasterAlive = IsAlive;
				}
				long num = HeartbeatSendIndex - 4;
				if (num >= HeartbeatStartIndex && HeartbeatStarted && HeartbeatSendTimeLookup.TryGetValue(num, out var value2) && !PacketReceiveLookup.ContainsKey(num) && (float)(CurrentTime - value2) > 2000f)
				{
					EnqueuePacket(num, received: false);
				}
			}

			public void SendHeartbeatAck(pHeartbeat data)
			{
				heatbeatAck.Index = data.Index;
				s_HeartbeatAckPacket.Send(heatbeatAck, Owner);
			}

			public void OnMasterChanged()
			{
				ToMasterLatency = 0;
				ToMasterPacketLossRate = 0;
				ToMasterNetworkJitter = 0;
				IsToMasterAlive = SNet.IsMaster;
			}

			public void GetToLocalReportText(out string toLocalLatencyText, out string toLocalJitterText, out string toLocalPacketLossRateText)
			{
				toLocalLatencyText = Hikaria.NetworkQualityTracker.Features.NetworkQualityTracker.LocalizationService.Format(2u, new object[1] { $"<{ToLocalLatencyColorHexString}>{$"{ToLocalLatency}ms"}</color>" });
				toLocalJitterText = Hikaria.NetworkQualityTracker.Features.NetworkQualityTracker.LocalizationService.Format(3u, new object[1] { $"<{ToLocalNetworkJitterColorHexString}>{$"{ToLocalNetworkJitter}ms"}</color>" });
				toLocalPacketLossRateText = Hikaria.NetworkQualityTracker.Features.NetworkQualityTracker.LocalizationService.Format(4u, new object[1] { $"<{ToLocalPacketLossColorHexString}>{$"{ToLocalPacketLossRate}%"}</color>" });
			}

			public void GetToMasterReportText(out string toMasterLatencyText, out string toMasterJitterText, out string toMasterPacketLossRateText)
			{
				toMasterLatencyText = Hikaria.NetworkQualityTracker.Features.NetworkQualityTracker.LocalizationService.Format(2u, new object[1] { $"<{ToMasterLatencyColorHexString}>{((!IsMasterHasHeartbeat) ? Hikaria.NetworkQualityTracker.Features.NetworkQualityTracker.LocalizationService.Get(1u) : $"{ToMasterLatency}ms")}</color>" });
				toMasterJitterText = Hikaria.NetworkQualityTracker.Features.NetworkQualityTracker.LocalizationService.Format(3u, new object[1] { $"<{ToMasterNetworkJitterColorHexString}>{((!IsMasterHasHeartbeat) ? Hikaria.NetworkQualityTracker.Features.NetworkQualityTracker.LocalizationService.Get(1u) : $"{ToMasterNetworkJitter}ms")}</color>" });
				toMasterPacketLossRateText = Hikaria.NetworkQualityTracker.Features.NetworkQualityTracker.LocalizationService.Format(4u, new object[1] { $"<{ToMasterPacketLossColorHexString}>{((!IsMasterHasHeartbeat) ? Hikaria.NetworkQualityTracker.Features.NetworkQualityTracker.LocalizationService.Get(1u) : $"{ToMasterPacketLossRate}%")}</color>" });
			}

			public pToMasterNetworkQualityReport GetToMasterReportData()
			{
				return new pToMasterNetworkQualityReport(ToMasterLatency, ToMasterNetworkJitter, ToMasterPacketLossRate, IsToMasterAlive);
			}

			private static string GetColorHexString(float min, float max, float value)
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_004b: Unknown result type (might be due to invalid IL or missing references)
				float num = (value - min) / (max - min);
				Hikaria.NetworkQualityTracker.Utils.Utils.RGBToHSL(COLOR_GREEN, out var h, out var s, out var l);
				Hikaria.NetworkQualityTracker.Utils.Utils.RGBToHSL(COLOR_RED, out var h2, out var s2, out var l2);
				float h3 = Mathf.Lerp(h, h2, num);
				float s3 = Mathf.Lerp(s, s2, num);
				float l3 = Mathf.Lerp(l, l2, num);
				return SColorExtensions.ToHexString(Hikaria.NetworkQualityTracker.Utils.Utils.HSLToRGB(h3, s3, l3));
			}
		}

		private static readonly Version Miniver = new Version("1.2.0", false);

		public static Dictionary<ulong, NetworkQualityData> NetworkQualityDataLookup = new Dictionary<ulong, NetworkQualityData>();

		public static Dictionary<ulong, int> PlayerSlotIndexLookup = new Dictionary<ulong, int>();

		public static SNetExt_BroadcastAction<pHeartbeat> s_HeartbeatAction;

		public static SNetExt_Packet<pHeartbeatAck> s_HeartbeatAckPacket;

		public static SNetExt_BroadcastAction<pToMasterNetworkQualityReport> s_ToMasterNetworkQualityReportAction;

		private static pHeartbeat heatbeat = new pHeartbeat();

		internal static Dictionary<long, long> HeartbeatSendTimeLookup = new Dictionary<long, long>();

		internal static long HeartbeatSendIndex = 0L;

		public static bool IsMasterHasHeartbeat { get; private set; }

		public static long CurrentTime => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();

		public static TextMeshPro WatermarkQualityTextMesh { get; set; }

		public static Dictionary<int, TextMeshPro> PageLoadoutQualityTextMeshes { get; } = new Dictionary<int, TextMeshPro>();


		public static TextMeshPro WatermarkTextPrefab => GuiManager.WatermarkLayer.m_watermark.m_watermarkText;

		public static Color COLOR_GREEN { get; private set; } = new Color(0f, 0.7206f, 0f, 0.3137f);


		public static Color COLOR_RED { get; private set; } = new Color(0.7206f, 0f, 0f, 0.3137f);


		public static void Setup()
		{
			CoreAPI.OnPlayerModsSynced += OnPlayerModsSynced;
			GameEventAPI.OnMasterChanged += OnMasterChanged;
			GameEventAPI.OnPlayerSlotChanged += OnPlayerSlotChanged;
			s_HeartbeatAction = SNetExt_BroadcastAction<pHeartbeat>.Create(typeof(pHeartbeat).FullName, (Action<ulong, pHeartbeat>)OnReceiveHeartbeat, (Func<SNet_Player, bool>)HeartbeatListenerFilter, (SNet_ChannelType)4);
			s_ToMasterNetworkQualityReportAction = SNetExt_BroadcastAction<pToMasterNetworkQualityReport>.Create(typeof(pToMasterNetworkQualityReport).FullName, (Action<ulong, pToMasterNetworkQualityReport>)OnReceiveNetworkQualityReport, (Func<SNet_Player, bool>)HeartbeatListenerFilter, (SNet_ChannelType)4);
			s_HeartbeatAckPacket = SNetExt_Packet<pHeartbeatAck>.Create(typeof(pHeartbeatAck).FullName, (Action<ulong, pHeartbeatAck>)OnReceiveHeartbeatAck, (Action<pHeartbeatAck>)null, false, (SNet_ChannelType)4);
			((SNetExt_SyncedAction<pHeartbeat>)(object)s_HeartbeatAction).OnPlayerAddedToListeners += RegisterListener;
			((SNetExt_SyncedAction<pHeartbeat>)(object)s_HeartbeatAction).OnPlayerRemovedFromListeners += UnregisterListener;
		}

		private static void OnPlayerSlotChanged(SNet_Player player, SNet_SlotType type, SNet_SlotHandleType handle, int index)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Invalid comparison between Unknown and I4
			if ((int)handle == 0 || (int)handle == 1)
			{
				Dictionary<ulong, int> playerSlotIndexLookup = PlayerSlotIndexLookup;
				ulong lookup = player.Lookup;
				SNet_Slot playerSlot = player.PlayerSlot;
				playerSlotIndexLookup[lookup] = ((playerSlot != null) ? playerSlot.index : (-1));
			}
		}

		private static void OnPlayerModsSynced(SNet_Player player, IEnumerable<pModInfo> mods)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (player.IsMaster)
			{
				IsMasterHasHeartbeat = CoreAPI.IsPlayerInstalledMod(player, "Hikaria.NetworkQualityTracker", Miniver);
				if (!IsMasterHasHeartbeat)
				{
					((TMP_Text)WatermarkQualityTextMesh).SetText(string.Empty, true);
					((TMP_Text)WatermarkQualityTextMesh).ForceMeshUpdate(false, false);
				}
				TextMeshPro watermarkQualityTextMesh = WatermarkQualityTextMesh;
				if (watermarkQualityTextMesh != null)
				{
					((Component)watermarkQualityTextMesh).gameObject.SetActive(Hikaria.NetworkQualityTracker.Features.NetworkQualityTracker.s_ShowInWatermark && !SNet.IsMaster && IsMasterHasHeartbeat);
				}
			}
		}

		private static void OnMasterChanged()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			IsMasterHasHeartbeat = CoreAPI.IsPlayerInstalledMod(SNet.Master, "Hikaria.NetworkQualityTracker", Miniver);
			if (!IsMasterHasHeartbeat)
			{
				((TMP_Text)WatermarkQualityTextMesh).SetText(string.Empty, true);
				((TMP_Text)WatermarkQualityTextMesh).ForceMeshUpdate(false, false);
			}
			TextMeshPro watermarkQualityTextMesh = WatermarkQualityTextMesh;
			if (watermarkQualityTextMesh != null)
			{
				((Component)watermarkQualityTextMesh).gameObject.SetActive(Hikaria.NetworkQualityTracker.Features.NetworkQualityTracker.s_ShowInWatermark && !SNet.IsMaster && IsMasterHasHeartbeat);
			}
			foreach (NetworkQualityData value in NetworkQualityDataLookup.Values)
			{
				value.OnMasterChanged();
			}
		}

		private static bool HeartbeatListenerFilter(SNet_Player player)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return CoreAPI.IsPlayerInstalledMod(player, "Hikaria.NetworkQualityTracker", Miniver);
		}

		private static void OnReceiveHeartbeat(ulong senderID, pHeartbeat data)
		{
			if (NetworkQualityDataLookup.TryGetValue(senderID, out var value) && !value.Owner.IsLocal)
			{
				value.ReceiveHeartbeat(data);
			}
		}

		private static void OnReceiveHeartbeatAck(ulong senderID, pHeartbeatAck data)
		{
			if (NetworkQualityDataLookup.TryGetValue(senderID, out var value) && !value.Owner.IsLocal)
			{
				value.ReceiveHeartbeatAck(data);
			}
		}

		private static void OnReceiveNetworkQualityReport(ulong senderID, pToMasterNetworkQualityReport data)
		{
			if (NetworkQualityDataLookup.TryGetValue(senderID, out var value) && !value.Owner.IsLocal)
			{
				value.ReceiveNetworkQualityReport(data);
			}
		}

		public static void RegisterListener(SNet_Player player)
		{
			NetworkQualityDataLookup.TryAdd(player.Lookup, new NetworkQualityData(player));
		}

		public static void UnregisterListener(SNet_Player player)
		{
			if (PlayerSlotIndexLookup.TryGetValue(player.Lookup, out var value) && PageLoadoutQualityTextMeshes.TryGetValue(value, out var value2))
			{
				((TMP_Text)value2).SetText(string.Empty, true);
				((TMP_Text)value2).ForceMeshUpdate(false, false);
			}
			if (player.IsLocal)
			{
				((TMP_Text)WatermarkQualityTextMesh).SetText(string.Empty, true);
				((TMP_Text)WatermarkQualityTextMesh).ForceMeshUpdate(false, false);
				NetworkQualityDataLookup.Clear();
				HeartbeatSendTimeLookup.Clear();
				HeartbeatSendIndex = 0L;
			}
			else
			{
				NetworkQualityDataLookup.Remove(player.Lookup);
			}
		}

		public static void SendHeartbeats()
		{
			HeartbeatSendIndex++;
			HeartbeatSendTimeLookup.Remove(HeartbeatSendIndex - 10);
			HeartbeatSendTimeLookup[HeartbeatSendIndex] = CurrentTime;
			heatbeat.Index = HeartbeatSendIndex;
			s_HeartbeatAction.Do(heatbeat);
			foreach (ulong key in NetworkQualityDataLookup.Keys)
			{
				NetworkQualityData networkQualityData = NetworkQualityDataLookup[key];
				if (!networkQualityData.Owner.IsLocal)
				{
					networkQualityData.CheckConnection();
					if (networkQualityData.Owner.IsMaster)
					{
						NetworkQualityData networkQualityData2 = NetworkQualityDataLookup[SNet.LocalPlayer.Lookup];
						networkQualityData2.ToMasterLatency = networkQualityData.ToLocalLatency;
						networkQualityData2.ToMasterNetworkJitter = networkQualityData.ToLocalNetworkJitter;
						networkQualityData2.ToMasterPacketLossRate = networkQualityData.ToLocalPacketLossRate;
						networkQualityData2.IsToMasterAlive = networkQualityData.IsAlive;
						s_ToMasterNetworkQualityReportAction.Do(networkQualityData2.GetToMasterReportData());
					}
				}
			}
		}
	}
}
namespace Hikaria.NetworkQualityTracker.Handlers
{
	public class NetworkQualityUpdater : MonoBehaviour
	{
		public const float TextUpdateInterval = 0.5f;

		public const float HeartbeatSendInterval = 0.5f;

		public const float ToMasterQualityReportSendInterval = 0.5f;

		public const float ConnectionWatchdogCheckInterval = 1f;

		public static bool ShowToLocalLatency = true;

		public static bool ShowToLocalNetworkJitter = true;

		public static bool ShowToLocalPacketLoss = true;

		public static bool ShowToMasterLatency = true;

		public static bool ShowToMasterNetworkJitter = true;

		public static bool ShowToMasterPacketLoss = true;

		public static NetworkQualityUpdater Instance { get; private set; }

		private static bool AnyShowToLocal
		{
			get
			{
				if (!ShowToLocalLatency && !ShowToLocalNetworkJitter)
				{
					return ShowToLocalPacketLoss;
				}
				return true;
			}
		}

		private static bool AnyShowToMaster
		{
			get
			{
				if (!ShowToMasterLatency && !ShowToMasterNetworkJitter)
				{
					return ShowToMasterPacketLoss;
				}
				return true;
			}
		}

		private void Awake()
		{
			Instance = this;
		}

		public static void StartAllCoroutines()
		{
			MonoBehaviourExtensions.StartCoroutine((MonoBehaviour)(object)Instance, StartAllCoroutinesCoroutine());
		}

		private static IEnumerator StartAllCoroutinesCoroutine()
		{
			MonoBehaviourExtensions.StartCoroutine((MonoBehaviour)(object)Instance, SendHeartbeatCoroutine());
			yield return (object)new WaitForFixedUpdate();
			MonoBehaviourExtensions.StartCoroutine((MonoBehaviour)(object)Instance, TextUpdateCoroutine());
		}

		private static IEnumerator SendHeartbeatCoroutine()
		{
			WaitForSecondsRealtime yielder = new WaitForSecondsRealtime(0.5f);
			while (true)
			{
				if (SNet.IsInLobby)
				{
					NetworkQualityManager.SendHeartbeats();
				}
				yield return yielder;
			}
		}

		private static IEnumerator TextUpdateCoroutine()
		{
			WaitForSecondsRealtime yielder = new WaitForSecondsRealtime(0.5f);
			StringBuilder sb = new StringBuilder(300);
			while (true)
			{
				foreach (NetworkQualityManager.NetworkQualityData value3 in NetworkQualityManager.NetworkQualityDataLookup.Values)
				{
					value3.GetToMasterReportText(out var toMasterLatencyText, out var toMasterJitterText, out var toMasterPacketLossRateText);
					if (Hikaria.NetworkQualityTracker.Features.NetworkQualityTracker.s_ShowInWatermark && value3.Owner.IsLocal && (Object)(object)NetworkQualityManager.WatermarkQualityTextMesh != (Object)null)
					{
						((TMP_Text)NetworkQualityManager.WatermarkQualityTextMesh).SetText($"{toMasterLatencyText}, {toMasterJitterText}, {toMasterPacketLossRateText}", true);
						((TMP_Text)NetworkQualityManager.WatermarkQualityTextMesh).ForceMeshUpdate(false, false);
					}
					if (!Hikaria.NetworkQualityTracker.Features.NetworkQualityTracker.s_ShowInPageLoadout || !NetworkQualityManager.PlayerSlotIndexLookup.TryGetValue(value3.Owner.Lookup, out var value) || !NetworkQualityManager.PageLoadoutQualityTextMeshes.TryGetValue(value, out var value2))
					{
						continue;
					}
					if (!value3.Owner.IsLocal && AnyShowToLocal)
					{
						value3.GetToLocalReportText(out var toLocalLatencyText, out var toLocalJitterText, out var toLocalPacketLossRateText);
						sb.AppendLine(Hikaria.NetworkQualityTracker.Features.NetworkQualityTracker.LocalizationService.Get(7u));
						if (!value3.IsAlive)
						{
							StringBuilder stringBuilder = sb;
							StringBuilder stringBuilder2 = stringBuilder;
							StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(10, 2, stringBuilder);
							handler.AppendLiteral("<");
							handler.AppendFormatted(SColorExtensions.ToHexString(NetworkQualityManager.COLOR_RED));
							handler.AppendLiteral(">");
							handler.AppendFormatted(Hikaria.NetworkQualityTracker.Features.NetworkQualityTracker.LocalizationService.Get(5u));
							handler.AppendLiteral("</color>");
							stringBuilder2.AppendLine(ref handler);
						}
						if (ShowToLocalLatency)
						{
							StringBuilder stringBuilder = sb;
							StringBuilder stringBuilder3 = stringBuilder;
							StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(0, 1, stringBuilder);
							handler.AppendFormatted(toLocalLatencyText);
							stringBuilder3.AppendLine(ref handler);
						}
						if (ShowToLocalNetworkJitter)
						{
							StringBuilder stringBuilder = sb;
							StringBuilder stringBuilder4 = stringBuilder;
							StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(0, 1, stringBuilder);
							handler.AppendFormatted(toLocalJitterText);
							stringBuilder4.AppendLine(ref handler);
						}
						if (ShowToLocalPacketLoss)
						{
							StringBuilder stringBuilder = sb;
							StringBuilder stringBuilder5 = stringBuilder;
							StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(0, 1, stringBuilder);
							handler.AppendFormatted(toLocalPacketLossRateText);
							stringBuilder5.AppendLine(ref handler);
						}
						sb.AppendLine();
					}
					if (NetworkQualityManager.IsMasterHasHeartbeat && !SNet.IsMaster && !value3.Owner.IsMaster && AnyShowToMaster)
					{
						sb.AppendLine(Hikaria.NetworkQualityTracker.Features.NetworkQualityTracker.LocalizationService.Get(8u));
						if (!value3.IsToMasterAlive)
						{
							StringBuilder stringBuilder = sb;
							StringBuilder stringBuilder6 = stringBuilder;
							StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(10, 2, stringBuilder);
							handler.AppendLiteral("<");
							handler.AppendFormatted(SColorExtensions.ToHexString(NetworkQualityManager.COLOR_RED));
							handler.AppendLiteral(">");
							handler.AppendFormatted(Hikaria.NetworkQualityTracker.Features.NetworkQualityTracker.LocalizationService.Get(6u));
							handler.AppendLiteral("</color>");
							stringBuilder6.AppendLine(ref handler);
						}
						if (ShowToMasterLatency)
						{
							StringBuilder stringBuilder = sb;
							StringBuilder stringBuilder7 = stringBuilder;
							StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(0, 1, stringBuilder);
							handler.AppendFormatted(toMasterLatencyText);
							stringBuilder7.AppendLine(ref handler);
						}
						if (ShowToMasterNetworkJitter)
						{
							StringBuilder stringBuilder = sb;
							StringBuilder stringBuilder8 = stringBuilder;
							StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(0, 1, stringBuilder);
							handler.AppendFormatted(toMasterJitterText);
							stringBuilder8.AppendLine(ref handler);
						}
						if (ShowToMasterPacketLoss)
						{
							StringBuilder stringBuilder = sb;
							StringBuilder stringBuilder9 = stringBuilder;
							StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(0, 1, stringBuilder);
							handler.AppendFormatted(toMasterPacketLossRateText);
							stringBuilder9.AppendLine(ref handler);
						}
					}
					((TMP_Text)value2).SetText(sb.ToString(), true);
					((TMP_Text)value2).ForceMeshUpdate(false, false);
					sb.Clear();
				}
				yield return yielder;
			}
		}
	}
}
namespace Hikaria.NetworkQualityTracker.Features
{
	[DisallowInGameToggle]
	[EnableFeatureByDefault]
	public class NetworkQualityTracker : Feature
	{
		public class NetworkLatencySetting
		{
			[FSDisplayName("在水印中显示")]
			public bool ShowInWatermark
			{
				get
				{
					return s_ShowInWatermark;
				}
				set
				{
					s_ShowInWatermark = value;
					TextMeshPro watermarkQualityTextMesh = NetworkQualityManager.WatermarkQualityTextMesh;
					if (watermarkQualityTextMesh != null)
					{
						((Component)watermarkQualityTextMesh).gameObject.SetActive(value);
					}
				}
			}

			[FSDisplayName("在大厅详情界面显示")]
			public bool ShowInPageLoadout
			{
				get
				{
					return s_ShowInPageLoadout;
				}
				set
				{
					s_ShowInPageLoadout = value;
					foreach (TextMeshPro value2 in NetworkQualityManager.PageLoadoutQualityTextMeshes.Values)
					{
						((Component)value2).gameObject.SetActive(value);
					}
				}
			}

			[FSInline]
			public ShowInfoSetting InfoSettings { get; set; } = new ShowInfoSetting();


			[FSInline]
			public PositionSetting Position { get; set; } = new PositionSetting();

		}

		public class ShowInfoSetting
		{
			[FSHeader("信息设置", true)]
			[FSDisplayName("与本地延迟")]
			public bool ShowToLocalLatency
			{
				get
				{
					return NetworkQualityUpdater.ShowToLocalLatency;
				}
				set
				{
					NetworkQualityUpdater.ShowToLocalLatency = value;
				}
			}

			[FSDisplayName("与本地网络抖动")]
			public bool ShowToLocalNetworkJitter
			{
				get
				{
					return NetworkQualityUpdater.ShowToLocalNetworkJitter;
				}
				set
				{
					NetworkQualityUpdater.ShowToLocalNetworkJitter = value;
				}
			}

			[FSDisplayName("与本地丢包率")]
			public bool ShowToLocalPacketLoss
			{
				get
				{
					return NetworkQualityUpdater.ShowToLocalPacketLoss;
				}
				set
				{
					NetworkQualityUpdater.ShowToLocalPacketLoss = value;
				}
			}

			[FSDisplayName("与主机延迟")]
			public bool ShowToMasterLatency
			{
				get
				{
					return NetworkQualityUpdater.ShowToMasterLatency;
				}
				set
				{
					NetworkQualityUpdater.ShowToMasterLatency = value;
				}
			}

			[FSDisplayName("与主机网络抖动")]
			public bool ShowToMasterNetworkJitter
			{
				get
				{
					return NetworkQualityUpdater.ShowToMasterNetworkJitter;
				}
				set
				{
					NetworkQualityUpdater.ShowToMasterNetworkJitter = value;
				}
			}

			[FSDisplayName("与主机丢包率")]
			public bool ShowToMasterPacketLoss
			{
				get
				{
					return NetworkQualityUpdater.ShowToMasterPacketLoss;
				}
				set
				{
					NetworkQualityUpdater.ShowToMasterPacketLoss = value;
				}
			}
		}

		public class PositionSetting
		{
			[FSHeader("水印位置", true)]
			[FSDisplayName("横向偏移量")]
			[FSDescription("单位: 像素")]
			public int WatermarkOffsetX
			{
				get
				{
					return s_WatermarkOffsetX;
				}
				set
				{
					//IL_002f: Unknown result type (might be due to invalid IL or missing references)
					s_WatermarkOffsetX = value;
					if ((Object)(object)NetworkQualityManager.WatermarkQualityTextMesh != (Object)null)
					{
						NetworkQualityManager.WatermarkQualityTextMesh.transform.localPosition = new Vector3((float)s_WatermarkOffsetX, 17.5f + (float)s_WatermarkOffsetY);
					}
				}
			}

			[FSDisplayName("纵向向偏移量")]
			[FSDescription("单位: 像素")]
			public int WatermarkOffsetY
			{
				get
				{
					return s_WatermarkOffsetY;
				}
				set
				{
					//IL_002f: Unknown result type (might be due to invalid IL or missing references)
					s_WatermarkOffsetY = value;
					if ((Object)(object)NetworkQualityManager.WatermarkQualityTextMesh != (Object)null)
					{
						NetworkQualityManager.WatermarkQualityTextMesh.transform.localPosition = new Vector3((float)s_WatermarkOffsetX, 17.5f + (float)s_WatermarkOffsetY);
					}
				}
			}

			[FSHeader("大厅详情页位置", true)]
			[FSDisplayName("横向偏移量")]
			[FSDescription("单位: 像素")]
			public int PageLoadoutOffsetX
			{
				get
				{
					return s_PageLoadoutOffsetX;
				}
				set
				{
					//IL_003c: Unknown result type (might be due to invalid IL or missing references)
					s_PageLoadoutOffsetX = value;
					foreach (TextMeshPro value2 in NetworkQualityManager.PageLoadoutQualityTextMeshes.Values)
					{
						value2.transform.localPosition = new Vector3(1525f + (float)s_PageLoadoutOffsetX, -515f + (float)s_PageLoadoutOffsetY);
					}
				}
			}

			[FSDisplayName("纵向向偏移量")]
			[FSDescription("单位: 像素")]
			public int PageLoadoutOffsetY
			{
				get
				{
					return s_PageLoadoutOffsetY;
				}
				set
				{
					//IL_003c: Unknown result type (might be due to invalid IL or missing references)
					s_PageLoadoutOffsetY = value;
					foreach (TextMeshPro value2 in NetworkQualityManager.PageLoadoutQualityTextMeshes.Values)
					{
						value2.transform.localPosition = new Vector3(1525f + (float)s_PageLoadoutOffsetX, -515f + (float)s_PageLoadoutOffsetY);
					}
				}
			}
		}

		[ArchivePatch(/*Could not decode attribute arguments.*/)]
		private class SNet_Core_STEAM__CreateLocalPlayer__Patch
		{
			private static bool IsSetup;

			private static void Postfix()
			{
				if (!IsSetup)
				{
					NetworkQualityUpdater.StartAllCoroutines();
					IsSetup = true;
				}
			}
		}

		[ArchivePatch(/*Could not decode attribute arguments.*/)]
		private class PUI_Watermark__UpdateWatermark__Patch
		{
			private static bool IsSetup;

			private static void Postfix()
			{
				//IL_0054: Unknown result type (might be due to invalid IL or missing references)
				//IL_0087: Unknown result type (might be due to invalid IL or missing references)
				//IL_0097: 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_00bd: 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_014a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0156: Unknown result type (might be due to invalid IL or missing references)
				//IL_015d: Unknown result type (might be due to invalid IL or missing references)
				//IL_018a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0194: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
				//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
				//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
				//IL_01fc: Expected O, but got Unknown
				if (IsSetup)
				{
					return;
				}
				NetworkQualityManager.WatermarkQualityTextMesh = Object.Instantiate<TextMeshPro>(NetworkQualityManager.WatermarkTextPrefab);
				NetworkQualityManager.WatermarkQualityTextMesh.transform.SetParent(NetworkQualityManager.WatermarkTextPrefab.transform.parent, false);
				NetworkQualityManager.WatermarkQualityTextMesh.transform.localPosition = new Vector3((float)s_WatermarkOffsetX, 17.5f + (float)s_WatermarkOffsetY);
				((TMP_Text)NetworkQualityManager.WatermarkQualityTextMesh).SetText(string.Empty, true);
				((Graphic)NetworkQualityManager.WatermarkQualityTextMesh).color = new Color(0.7075f, 0.7075f, 0.7075f, 0.4706f);
				TextMeshPro watermarkQualityTextMesh = NetworkQualityManager.WatermarkQualityTextMesh;
				((TMP_Text)watermarkQualityTextMesh).fontStyle = (FontStyles)(((TMP_Text)watermarkQualityTextMesh).fontStyle & -17);
				((TMP_Text)NetworkQualityManager.WatermarkQualityTextMesh).rectTransform.sizeDelta = new Vector2(1000f, ((TMP_Text)NetworkQualityManager.WatermarkQualityTextMesh).rectTransform.sizeDelta.y);
				((TMP_Text)NetworkQualityManager.WatermarkQualityTextMesh).ForceMeshUpdate(false, false);
				foreach (CM_PlayerLobbyBar item in (Il2CppArrayBase<CM_PlayerLobbyBar>)(object)CM_PageLoadout.Current.m_playerLobbyBars)
				{
					int playerSlotIndex = item.PlayerSlotIndex;
					if (!NetworkQualityManager.PageLoadoutQualityTextMeshes.ContainsKey(playerSlotIndex))
					{
						TextMeshPro val = Object.Instantiate<TextMeshPro>(NetworkQualityManager.WatermarkTextPrefab, item.m_hasPlayerRoot.transform, false);
						val.transform.localPosition = new Vector3(1525f + (float)s_PageLoadoutOffsetX, -515f + (float)s_PageLoadoutOffsetY, 0f);
						((TMP_Text)val).fontStyle = (FontStyles)(((TMP_Text)val).fontStyle & -17);
						((TMP_Text)val).alignment = (TextAlignmentOptions)257;
						((TMP_Text)val).fontSize = 25f;
						((TMP_Text)val).rectTransform.sizeDelta = new Vector2(1000f, ((TMP_Text)val).rectTransform.sizeDelta.y);
						((Graphic)val).color = new Color(1f, 1f, 1f, 0.7059f);
						((TMP_Text)val).SetText(string.Empty, true);
						NetworkQualityManager.PageLoadoutQualityTextMeshes[playerSlotIndex] = val;
					}
				}
				GameObject val2 = new GameObject("NetworkQualityUpdater");
				Object.DontDestroyOnLoad((Object)val2);
				val2.AddComponent<NetworkQualityUpdater>();
				IsSetup = true;
			}
		}

		private static int s_WatermarkOffsetX = 0;

		private static int s_WatermarkOffsetY = 0;

		private static int s_PageLoadoutOffsetX = 0;

		private static int s_PageLoadoutOffsetY = 0;

		public override string Name => "Network Quality Tracker";

		public override FeatureGroup Group => ((Feature)this).ModuleGroup;

		public override bool InlineSettingsIntoParentMenu => true;

		public static ILocalizationService LocalizationService { get; private set; }

		[FeatureConfig]
		public static NetworkLatencySetting Settings { get; set; }

		public static bool s_ShowInWatermark { get; private set; } = true;


		public static bool s_ShowInPageLoadout { get; private set; } = true;


		public override void Init()
		{
			LocalizationService = ((Feature)this).Localization;
			ClassInjector.RegisterTypeInIl2Cpp<NetworkQualityUpdater>(false);
			NetworkQualityManager.Setup();
		}
	}
}