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