Decompiled source of RoundsLanPing v0.2.5

BepInEx/plugins/RoundsLanPing.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("Tiago Al Tabaa")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Shows every player's ping in a ROUNDS match, read from RoundsWithFriends' synced Ping property.")]
[assembly: AssemblyFileVersion("0.2.5.0")]
[assembly: AssemblyInformationalVersion("0.2.5+9d65a916ebce4200d81904a0105afe537e662ce4")]
[assembly: AssemblyProduct("ROUNDS LAN Ping")]
[assembly: AssemblyTitle("RoundsLanPing")]
[assembly: AssemblyVersion("0.2.5.0")]
namespace RoundsLanPing;

public enum PingMode
{
	Hold,
	Toggle
}
public enum PingDesign
{
	Compact,
	Detailed,
	Minimal
}
public enum PingCorner
{
	TopLeft,
	TopRight,
	BottomLeft,
	BottomRight,
	Custom
}
[BepInPlugin("tiago.rounds.lanping", "ROUNDS LAN Ping", "0.2.3")]
public class PingPlugin : BaseUnityPlugin
{
	private sealed class Row
	{
		public string Name;

		public int Ping;

		public int Actor;

		public bool IsLocal;

		public string FoundKey;

		public string KeysSeen;
	}

	internal static ConfigEntry<KeyCode> ShowKey;

	internal static ConfigEntry<PingMode> Mode;

	internal static ConfigEntry<bool> AlwaysShow;

	internal static ConfigEntry<PingCorner> Position;

	internal static ConfigEntry<float> OffsetX;

	internal static ConfigEntry<float> OffsetY;

	internal static ConfigEntry<PingDesign> Design;

	internal static ConfigEntry<int> FontSize;

	internal static ConfigEntry<float> LogEverySeconds;

	internal static ConfigEntry<bool> Diagnostics;

	private static Type _photonType;

	private static PropertyInfo _currentRoom;

	private static PropertyInfo _localPlayer;

	private static PropertyInfo _playerList;

	private static MethodInfo _getPing;

	private static PropertyInfo _players;

	private static PropertyInfo _roomName;

	private static PropertyInfo _nickName;

	private static PropertyInfo _customProps;

	private static PropertyInfo _actorNumber;

	private static bool _resolved;

	private float _nextResolve;

	private float _nextLog;

	private bool _visible;

	private Texture2D _pixel;

	private GUIStyle _row;

	private GUIStyle _head;

	private GUIStyle _note;

	private bool _stylesBuilt;

	private void Awake()
	{
		//IL_0154: Unknown result type (might be due to invalid IL or missing references)
		//IL_015e: Expected O, but got Unknown
		//IL_0166: Unknown result type (might be due to invalid IL or missing references)
		//IL_018b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0190: Unknown result type (might be due to invalid IL or missing references)
		ShowKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "ShowKey", (KeyCode)288, "Key that shows the panel.");
		Mode = ((BaseUnityPlugin)this).Config.Bind<PingMode>("General", "Mode", PingMode.Hold, "Hold: the panel shows while the key is held. Toggle: press once to show, press again to hide.");
		AlwaysShow = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AlwaysShow", false, "Show the panel all the time and ignore the key.");
		Position = ((BaseUnityPlugin)this).Config.Bind<PingCorner>("Layout", "Position", PingCorner.TopLeft, "TopLeft, TopRight, BottomLeft, BottomRight, or Custom to use OffsetX and OffsetY.");
		OffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("Layout", "OffsetX", 20f, "Distance from the left edge, for Position = Custom.");
		OffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Layout", "OffsetY", 20f, "Distance from the top edge, for Position = Custom.");
		Design = ((BaseUnityPlugin)this).Config.Bind<PingDesign>("Layout", "Design", PingDesign.Compact, "Compact: one panel with name and ping. Detailed: adds the room, the player count and player numbers. Minimal: text only, no panel.");
		FontSize = ((BaseUnityPlugin)this).Config.Bind<int>("Layout", "FontSize", 14, "Text size.");
		LogEverySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "LogEverySeconds", 10f, "Write the ping list into the BepInEx log this often. 0 turns it off.");
		Diagnostics = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Diagnostics", false, "Write what was found for every player (name, number, every property key) into the BepInEx log. Turn this on when someone shows ? and send the log.");
		_pixel = new Texture2D(1, 1);
		_pixel.SetPixel(0, 0, Color.white);
		_pixel.Apply();
		((BaseUnityPlugin)this).Logger.LogInfo((object)("[PING] loaded 0.2.3: " + ((object)ShowKey.Value/*cast due to .constrained prefix*/).ToString() + " " + Mode.Value));
	}

	private void Update()
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		bool flag = false;
		try
		{
			flag = Input.GetKeyDown(ShowKey.Value);
		}
		catch
		{
		}
		if (Mode.Value == PingMode.Toggle && flag)
		{
			_visible = !_visible;
		}
		if (!_resolved && Time.unscaledTime >= _nextResolve)
		{
			_nextResolve = Time.unscaledTime + 2f;
			TryResolve();
		}
		if (!_resolved || !(LogEverySeconds.Value > 0f) || !(Time.unscaledTime >= _nextLog))
		{
			return;
		}
		_nextLog = Time.unscaledTime + LogEverySeconds.Value;
		List<Row> list = Collect();
		if (list.Count == 0)
		{
			return;
		}
		StringBuilder stringBuilder = new StringBuilder("[PING] ");
		for (int i = 0; i < list.Count; i++)
		{
			if (i > 0)
			{
				stringBuilder.Append("  |  ");
			}
			stringBuilder.Append(list[i].Name).Append(" ").Append((list[i].Ping < 0) ? "?" : (list[i].Ping + "ms"));
			if (Diagnostics.Value)
			{
				stringBuilder.Append(" [").Append(list[i].FoundKey ?? "no key").Append("]");
			}
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)stringBuilder.ToString());
		if (Diagnostics.Value)
		{
			DumpPlayers(list);
		}
	}

	private static void TryResolve()
	{
		try
		{
			if (_photonType == null)
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				for (int i = 0; i < assemblies.Length; i++)
				{
					Type type = null;
					try
					{
						type = assemblies[i].GetType("Photon.Pun.PhotonNetwork", throwOnError: false);
					}
					catch
					{
					}
					if (type == null)
					{
						try
						{
							type = assemblies[i].GetType("PhotonNetwork", throwOnError: false);
						}
						catch
						{
						}
					}
					if (type != null)
					{
						_photonType = type;
						break;
					}
				}
			}
			if (!(_photonType == null))
			{
				_currentRoom = _photonType.GetProperty("CurrentRoom", BindingFlags.Static | BindingFlags.Public);
				_localPlayer = _photonType.GetProperty("LocalPlayer", BindingFlags.Static | BindingFlags.Public);
				_playerList = _photonType.GetProperty("playerList", BindingFlags.Static | BindingFlags.Public);
				_getPing = _photonType.GetMethod("GetPing", BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null);
				Type type2 = ((_currentRoom != null) ? _currentRoom.PropertyType : null);
				if (type2 != null)
				{
					_players = type2.GetProperty("Players");
					_roomName = type2.GetProperty("Name");
				}
				Type type3 = ((_localPlayer != null) ? _localPlayer.PropertyType : null);
				if (type3 != null)
				{
					_nickName = type3.GetProperty("NickName");
					_customProps = type3.GetProperty("CustomProperties");
					_actorNumber = type3.GetProperty("ActorNumber");
				}
				_resolved = _customProps != null && _nickName != null && ((_players != null && type2 != null) || _playerList != null);
				if (_resolved)
				{
					Debug.Log((object)("[PING] photon members resolved: " + _photonType.FullName));
				}
			}
		}
		catch
		{
		}
	}

	private List<Row> Collect()
	{
		List<Row> list = new List<Row>();
		if (!_resolved)
		{
			return list;
		}
		object obj = null;
		try
		{
			if (_localPlayer != null)
			{
				obj = _localPlayer.GetValue(null, null);
			}
		}
		catch
		{
		}
		int num = -1;
		try
		{
			if (_getPing != null)
			{
				num = Convert.ToInt32(_getPing.Invoke(null, null));
			}
		}
		catch
		{
		}
		List<object> list2 = new List<object>();
		object obj4 = null;
		try
		{
			if (_currentRoom != null)
			{
				obj4 = _currentRoom.GetValue(null, null);
			}
		}
		catch
		{
		}
		if (obj4 != null && _players != null)
		{
			IDictionary dictionary = null;
			try
			{
				dictionary = _players.GetValue(obj4, null) as IDictionary;
			}
			catch
			{
			}
			if (dictionary != null)
			{
				foreach (DictionaryEntry item in dictionary)
				{
					if (item.Value != null)
					{
						list2.Add(item.Value);
					}
				}
			}
		}
		if (list2.Count == 0 && _playerList != null)
		{
			try
			{
				if (_playerList.GetValue(null, null) is Array array)
				{
					foreach (object item2 in array)
					{
						if (item2 != null)
						{
							list2.Add(item2);
						}
					}
				}
			}
			catch
			{
			}
		}
		foreach (object item3 in list2)
		{
			string text = "";
			try
			{
				text = _nickName.GetValue(item3, null) as string;
			}
			catch
			{
			}
			if (string.IsNullOrEmpty(text))
			{
				text = "player";
			}
			int actor = -1;
			try
			{
				if (_actorNumber != null)
				{
					actor = Convert.ToInt32(_actorNumber.GetValue(item3, null));
				}
			}
			catch
			{
			}
			string foundKey = null;
			string keysSeen = "";
			int num2 = -1;
			try
			{
				num2 = PingParser.Find(_customProps.GetValue(item3, null) as IDictionary, out foundKey, out keysSeen);
			}
			catch
			{
			}
			bool flag = item3 == obj;
			if (flag && num2 <= 0 && num > 0)
			{
				num2 = num;
				if (foundKey == null)
				{
					foundKey = "GetPing()";
				}
			}
			Row row = new Row();
			row.Name = text;
			row.Ping = num2;
			row.Actor = actor;
			row.IsLocal = flag;
			row.FoundKey = foundKey;
			row.KeysSeen = keysSeen;
			list.Add(row);
		}
		list.Sort((Row a, Row b) => b.Ping.CompareTo(a.Ping));
		return list;
	}

	private string RoomLabel()
	{
		try
		{
			object obj = ((_currentRoom != null) ? _currentRoom.GetValue(null, null) : null);
			if (obj == null || _roomName == null)
			{
				return "";
			}
			return _roomName.GetValue(obj, null) as string;
		}
		catch
		{
			return "";
		}
	}

	private void DumpPlayers(List<Row> rows)
	{
		try
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[PING] diagnostics: room='" + RoomLabel() + "' players=" + rows.Count));
			for (int i = 0; i < rows.Count; i++)
			{
				Row row = rows[i];
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[PING]   #" + row.Actor + " '" + row.Name + "' ping=" + row.Ping + " key=" + (row.FoundKey ?? "none") + " keys=[" + row.KeysSeen + "]"));
			}
		}
		catch
		{
		}
	}

	private void BuildStyles()
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Expected O, but got Unknown
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Expected O, but got Unknown
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Expected O, but got Unknown
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Expected O, but got Unknown
		if (!_stylesBuilt)
		{
			int value = FontSize.Value;
			_row = new GUIStyle(GUI.skin.label);
			_row.fontSize = value;
			_row.alignment = (TextAnchor)3;
			_row.padding = new RectOffset(0, 0, 0, 0);
			_head = new GUIStyle(_row);
			_head.fontStyle = (FontStyle)1;
			_head.fontSize = value + 1;
			_note = new GUIStyle(_row);
			_note.fontSize = Mathf.Max(10, value - 2);
			_note.wordWrap = true;
			_stylesBuilt = true;
		}
	}

	private static Color PingColor(int ping)
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		if (ping < 0)
		{
			return new Color(0.75f, 0.75f, 0.75f);
		}
		if (ping <= 60)
		{
			return new Color(0.45f, 0.95f, 0.45f);
		}
		if (ping <= 120)
		{
			return new Color(0.95f, 0.9f, 0.35f);
		}
		if (ping <= 200)
		{
			return new Color(0.98f, 0.62f, 0.25f);
		}
		return new Color(0.98f, 0.35f, 0.35f);
	}

	private void DrawBox(float x, float y, float w, float h, Color color)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		Color color2 = GUI.color;
		GUI.color = color;
		GUI.DrawTexture(new Rect(x, y, w, h), (Texture)(object)_pixel);
		GUI.color = color2;
	}

	private Vector2 Anchor(float w, float h, float pad)
	{
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		float num = Screen.width;
		float num2 = Screen.height;
		return (Vector2)(Position.Value switch
		{
			PingCorner.TopRight => new Vector2(num - w - pad, pad), 
			PingCorner.BottomLeft => new Vector2(pad, num2 - h - pad), 
			PingCorner.BottomRight => new Vector2(num - w - pad, num2 - h - pad), 
			PingCorner.Custom => new Vector2(OffsetX.Value, OffsetY.Value), 
			_ => new Vector2(pad, pad), 
		});
	}

	private void OnGUI()
	{
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_019b: 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)
		//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0257: Unknown result type (might be due to invalid IL or missing references)
		//IL_026e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0224: Unknown result type (might be due to invalid IL or missing references)
		//IL_0229: Unknown result type (might be due to invalid IL or missing references)
		//IL_029b: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_036a: Unknown result type (might be due to invalid IL or missing references)
		//IL_036f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0379: Unknown result type (might be due to invalid IL or missing references)
		//IL_0396: Unknown result type (might be due to invalid IL or missing references)
		//IL_03af: Unknown result type (might be due to invalid IL or missing references)
		//IL_03b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d6: Unknown result type (might be due to invalid IL or missing references)
		bool flag;
		if (AlwaysShow.Value)
		{
			flag = true;
		}
		else if (Mode.Value == PingMode.Toggle)
		{
			flag = _visible;
		}
		else
		{
			try
			{
				flag = Input.GetKey(ShowKey.Value);
			}
			catch
			{
				flag = false;
			}
		}
		if (!flag)
		{
			return;
		}
		BuildStyles();
		float num = (float)FontSize.Value + 8f;
		float num2 = ((Design.Value == PingDesign.Detailed) ? 380f : 320f);
		bool flag2 = Design.Value != PingDesign.Minimal;
		if (!_resolved)
		{
			float h = num * 2f + 12f;
			Vector2 val = Anchor(num2, h, 20f);
			if (flag2)
			{
				DrawBox(val.x, val.y, num2, h, new Color(0f, 0f, 0f, 0.68f));
			}
			GUI.Label(new Rect(val.x + 12f, val.y + 6f, num2 - 24f, num), "LAN PING", _head);
			GUI.Label(new Rect(val.x + 12f, val.y + 6f + num, num2 - 24f, num), "waiting for the game's networking (not in a room yet)", _note);
			return;
		}
		List<Row> list = Collect();
		if (list.Count == 0)
		{
			return;
		}
		int num3 = ((Design.Value != PingDesign.Detailed) ? 1 : 2);
		float h2 = 10.799999f + num * (float)(list.Count + num3) + 4.8f;
		Vector2 val2 = Anchor(num2, h2, 20f);
		if (flag2)
		{
			DrawBox(val2.x, val2.y, num2, h2, new Color(0f, 0f, 0f, 0.68f));
		}
		float num4 = val2.y + 5.3999996f;
		string text = ((Mode.Value == PingMode.Toggle) ? "toggle " : "hold ");
		string text2 = ((Design.Value == PingDesign.Minimal) ? "PING" : ("PING  (" + (AlwaysShow.Value ? "always on" : (text + ((object)ShowKey.Value/*cast due to .constrained prefix*/).ToString())) + ")"));
		GUI.Label(new Rect(val2.x + 12f, num4, num2 - 24f, num), text2, _head);
		num4 += num;
		if (Design.Value == PingDesign.Detailed)
		{
			string text3 = RoomLabel();
			GUI.Label(new Rect(val2.x + 12f, num4, num2 - 24f, num), (string.IsNullOrEmpty(text3) ? "room ?" : ("room " + text3)) + "   players " + list.Count, _note);
			num4 += num;
		}
		for (int i = 0; i < list.Count; i++)
		{
			Row row = list[i];
			string text4 = row.Name + (row.IsLocal ? "  (you)" : "");
			if (Design.Value == PingDesign.Detailed && row.Actor > 0)
			{
				text4 = "#" + row.Actor + "  " + text4;
			}
			Color contentColor = GUI.contentColor;
			GUI.contentColor = Color.white;
			GUI.Label(new Rect(val2.x + 12f, num4, num2 - 24f - 70f, num), text4, _row);
			GUI.contentColor = PingColor(row.Ping);
			GUI.Label(new Rect(val2.x + num2 - 12f - 66f, num4, 66f, num), (row.Ping < 0) ? "?" : (row.Ping + " ms"), _row);
			GUI.contentColor = contentColor;
			num4 += num;
		}
	}
}
public static class PingParser
{
	public static int Find(IDictionary props, out string foundKey, out string keysSeen)
	{
		foundKey = null;
		keysSeen = "";
		if (props == null)
		{
			return -1;
		}
		int ping = -1;
		Walk(props, 0, exactOnly: true, collectKeys: true, ref ping, ref foundKey, ref keysSeen);
		if (foundKey == null)
		{
			Walk(props, 0, exactOnly: false, collectKeys: false, ref ping, ref foundKey, ref keysSeen);
		}
		return ping;
	}

	private static void Walk(IDictionary props, int depth, bool exactOnly, bool collectKeys, ref int ping, ref string foundKey, ref string keysSeen)
	{
		if (props == null || depth > 2)
		{
			return;
		}
		foreach (DictionaryEntry prop in props)
		{
			string text = ((prop.Key == null) ? null : prop.Key.ToString());
			if (text != null && collectKeys && keysSeen.Length < 400)
			{
				keysSeen = ((keysSeen.Length == 0) ? text : (keysSeen + ", " + text));
			}
			if (text != null && foundKey == null && (exactOnly ? (text.ToLowerInvariant() == "ping") : IsSuffixedPing(text)) && TryNumber(prop.Value, out var result))
			{
				ping = result;
				foundKey = text;
			}
			if (prop.Value is IDictionary props2 && depth < 2)
			{
				Walk(props2, depth + 1, exactOnly, collectKeys, ref ping, ref foundKey, ref keysSeen);
			}
		}
	}

	private static bool IsSuffixedPing(string key)
	{
		string text = key.ToLowerInvariant();
		if (!text.EndsWith("/ping") && !text.EndsWith(".ping"))
		{
			return text.EndsWith("_ping");
		}
		return true;
	}

	private static bool TryNumber(object value, out int result)
	{
		result = 0;
		if (value == null)
		{
			return false;
		}
		if (value is int)
		{
			result = (int)value;
			return true;
		}
		if (value is short)
		{
			result = (short)value;
			return true;
		}
		if (value is byte)
		{
			result = (byte)value;
			return true;
		}
		if (value is long)
		{
			result = (int)(long)value;
			return true;
		}
		if (value is float)
		{
			return false;
		}
		if (value is double)
		{
			return false;
		}
		return int.TryParse(value.ToString(), out result);
	}
}