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();
}
}
}