Decompiled source of Character Level v1.0.1

plugins/CharacterLevel.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("BenjSammy")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Character level from the sum of skill levels and a shared server ranking.")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("Character Level by BenjSammy")]
[assembly: AssemblyTitle("CharacterLevel")]
[assembly: AssemblyVersion("1.0.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BenjSammy.CharacterLevel
{
	[BepInPlugin("benjsammy.characterlevel", "Character Level by BenjSammy", "1.0.1")]
	public sealed class CharacterLevelPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "benjsammy.characterlevel";

		public const string PluginName = "Character Level by BenjSammy";

		public const string PluginVersion = "1.0.1";

		private GameApi _api;

		private RankingEngine _engine;

		private RankingPanel _panel;

		private ConfigEntry<bool> _visible;

		private ConfigEntry<KeyCode> _toggleKey;

		private ConfigEntry<float> _scale;

		private ConfigEntry<float> _width;

		private ConfigEntry<float> _top;

		private ConfigEntry<float> _margin;

		private ConfigEntry<int> _rows;

		private ConfigEntry<float> _backgroundOpacity;

		private float _nextError;

		private void Awake()
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Expected O, but got Unknown
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Expected O, but got Unknown
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Expected O, but got Unknown
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Expected O, but got Unknown
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Expected O, but got Unknown
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Expected O, but got Unknown
			_visible = ((BaseUnityPlugin)this).Config.Bind<bool>("Affichage", "Afficher", true, "Afficher le classement à droite du jeu.");
			_toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Affichage", "Touche", (KeyCode)289, "Touche pour afficher ou masquer le cadre.");
			_scale = ((BaseUnityPlugin)this).Config.Bind<float>("Affichage", "Echelle", 1f, new ConfigDescription("Taille du cadre, adaptée automatiquement à la hauteur de l'écran.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.7f, 2f), Array.Empty<object>()));
			_width = ((BaseUnityPlugin)this).Config.Bind<float>("Affichage", "Largeur", 330f, new ConfigDescription("Largeur maximale à 1080 pixels de haut. Le cadre se resserre autour de la liste.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(260f, 600f), Array.Empty<object>()));
			_top = ((BaseUnityPlugin)this).Config.Bind<float>("Affichage", "PositionVerticale", 0.33f, new ConfigDescription("Position depuis le haut de l'écran : 0,33 = 33 %, sous la mini-carte.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 0.7f), Array.Empty<object>()));
			_margin = ((BaseUnityPlugin)this).Config.Bind<float>("Affichage", "MargeDroite", 20f, new ConfigDescription("Marge depuis le bord droit.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 300f), Array.Empty<object>()));
			_rows = ((BaseUnityPlugin)this).Config.Bind<int>("Affichage", "LignesVisibles", 10, new ConfigDescription("Nombre de lignes visibles avant défilement.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 20), Array.Empty<object>()));
			_backgroundOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("Affichage", "OpaciteFond", 0.25f, new ConfigDescription("Opacité du fond : 0.25 correspond à 75 % de transparence.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			try
			{
				_api = new GameApi();
				_engine = new RankingEngine();
				_panel = new RankingPanel();
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Character Level 1.0.1 by BenjSammy active. Simple Connecté panel, 75% transparent background. Level = sum of current integer skill levels.");
			}
			catch (Exception ex)
			{
				((Behaviour)this).enabled = false;
				((BaseUnityPlugin)this).Logger.LogError((object)("Character Level could not start: incompatible Valheim API. " + ex));
			}
		}

		private void Update()
		{
			if (_engine == null)
			{
				return;
			}
			try
			{
				_engine.Tick(_api, Time.realtimeSinceStartup);
			}
			catch (Exception exception)
			{
				Report(exception);
			}
		}

		private void OnGUI()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			if (_engine == null || _api.NetworkToken == null || _api.IsDedicated || !_api.HasLocalPlayer)
			{
				return;
			}
			try
			{
				if (RankingPanel.TogglePressed(_toggleKey.Value))
				{
					_visible.Value = !_visible.Value;
				}
				if (_visible.Value)
				{
					_panel.Draw(_engine, _scale.Value, _width.Value, _top.Value, _margin.Value, _rows.Value, _backgroundOpacity.Value);
				}
			}
			catch (Exception exception)
			{
				Report(exception);
			}
		}

		private void Report(Exception exception)
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (!(realtimeSinceStartup < _nextError))
			{
				_nextError = realtimeSinceStartup + 15f;
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Character Level: " + exception));
			}
		}

		private void OnDestroy()
		{
			_engine?.Close();
		}
	}
	internal sealed class OnlinePeer
	{
		internal long Id;

		internal string Name;
	}
	internal interface IGameApi
	{
		object NetworkToken { get; }

		bool IsServer { get; }

		bool IsDedicated { get; }

		bool HasLocalPlayer { get; }

		long LocalPeerId { get; }

		long ServerPeerId { get; }

		bool TryGetLocalSkills(out float[] levels, out string name);

		List<OnlinePeer> GetPeers();

		void Register(string method, Action<long, string> handler);

		void Send(long destination, string method, string payload);
	}
	internal static class LevelMath
	{
		internal const int MaxSkills = 512;

		internal const int MaxTotal = 1000000;

		internal static bool TrySum(float[] levels, out int total)
		{
			total = 0;
			if (levels == null || levels.Length > 512)
			{
				return false;
			}
			foreach (float num in levels)
			{
				if (float.IsNaN(num) || float.IsInfinity(num) || num < 0f || num > 10000f)
				{
					return false;
				}
				int num2 = (int)Math.Floor(num);
				if (total > 1000000 - num2)
				{
					return false;
				}
				total += num2;
			}
			return true;
		}
	}
	[DataContract]
	internal sealed class SkillReport
	{
		[DataMember(Name = "v", IsRequired = true)]
		public int Version = 1;

		[DataMember(Name = "s", IsRequired = true)]
		public float[] Skills;
	}
	[DataContract]
	internal sealed class RankRow
	{
		[DataMember(Name = "id", IsRequired = true)]
		public long PeerId;

		[DataMember(Name = "n", IsRequired = true)]
		public string Name;

		[DataMember(Name = "l", IsRequired = true)]
		public int? Level;
	}
	[DataContract]
	internal sealed class RankingPacket
	{
		[DataMember(Name = "v", IsRequired = true)]
		public int Version = 1;

		[DataMember(Name = "p", IsRequired = true)]
		public RankRow[] Rows;
	}
	internal static class PacketCodec
	{
		private const int MaximumBytes = 48000;

		internal const int MaximumPlayers = 256;

		internal static string Encode<T>(T packet)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			using MemoryStream memoryStream = new MemoryStream();
			((XmlObjectSerializer)new DataContractJsonSerializer(typeof(T))).WriteObject((Stream)memoryStream, (object)packet);
			if (memoryStream.Length > 48000)
			{
				throw new SerializationException("Ranking packet too large.");
			}
			return Encoding.UTF8.GetString(memoryStream.ToArray());
		}

		internal static bool TryDecode<T>(string payload, out T packet) where T : class
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			packet = null;
			if (string.IsNullOrEmpty(payload) || payload.Length > 48000 || Encoding.UTF8.GetByteCount(payload) > 48000)
			{
				return false;
			}
			try
			{
				using (MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(payload)))
				{
					packet = ((XmlObjectSerializer)new DataContractJsonSerializer(typeof(T), new DataContractJsonSerializerSettings
					{
						MaxItemsInObjectGraph = 5000
					})).ReadObject((Stream)memoryStream) as T;
				}
				return packet != null;
			}
			catch (Exception)
			{
				return false;
			}
		}

		internal static string CleanName(string name)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				return "Personnage";
			}
			StringBuilder stringBuilder = new StringBuilder();
			foreach (char c in name)
			{
				stringBuilder.Append(char.IsControl(c) ? ' ' : c);
			}
			string text = stringBuilder.ToString().Trim();
			if (text.Length > 48)
			{
				text = text[..(char.IsHighSurrogate(text[47]) ? 47 : 48)];
			}
			if (text.Length != 0)
			{
				return text;
			}
			return "Personnage";
		}

		internal static RankRow[] Sort(IEnumerable<RankRow> rows)
		{
			return rows.OrderByDescending((RankRow row) => row.Level ?? (-1)).ThenBy<RankRow, string>((RankRow row) => row.Name, StringComparer.OrdinalIgnoreCase).ThenBy((RankRow row) => row.PeerId)
				.ToArray();
		}

		internal static bool TryReadRanking(string payload, out RankRow[] rows)
		{
			rows = null;
			if (!TryDecode<RankingPacket>(payload, out var packet) || packet.Version != 1 || packet.Rows == null || packet.Rows.Length > 256)
			{
				return false;
			}
			HashSet<long> hashSet = new HashSet<long>();
			RankRow[] rows2 = packet.Rows;
			foreach (RankRow rankRow in rows2)
			{
				if (rankRow == null || rankRow.PeerId == 0L || !hashSet.Add(rankRow.PeerId) || (rankRow.Level.HasValue && (rankRow.Level.Value < 0 || rankRow.Level.Value > 1000000)))
				{
					return false;
				}
				rankRow.Name = CleanName(rankRow.Name);
			}
			rows = Sort(packet.Rows);
			return true;
		}
	}
	internal sealed class RankingEngine
	{
		internal const string ReportRpc = "benjsammy.characterlevel.v1.skills";

		internal const string RankingRpc = "benjsammy.characterlevel.v1.ranking";

		private const double RefreshSeconds = 5.0;

		private readonly Dictionary<long, int> _totals = new Dictionary<long, int>();

		private readonly Dictionary<long, double> _lastReport = new Dictionary<long, double>();

		private IGameApi _game;

		private object _networkToken;

		private double _now;

		private double _nextReport;

		private double _nextSnapshot;

		private double _lastSnapshot = -1.0;

		private double _lastBroadcast = -1.0;

		private bool _dirty;

		private bool _closed;

		internal RankRow[] Rows { get; private set; } = new RankRow[0];

		internal int LocalLevel { get; private set; }

		internal string LocalName { get; private set; } = "";

		internal bool HasLocalLevel { get; private set; }

		internal bool Synchronized
		{
			get
			{
				if (_lastSnapshot >= 0.0)
				{
					return _now - _lastSnapshot <= 20.0;
				}
				return false;
			}
		}

		internal void Tick(IGameApi game, double now)
		{
			if (_closed)
			{
				return;
			}
			_now = now;
			object token = game.NetworkToken;
			if (token == null)
			{
				if (_networkToken != null)
				{
					ResetSession();
				}
				return;
			}
			if (token != _networkToken)
			{
				ResetSession();
				_game = game;
				_networkToken = token;
				game.Register("benjsammy.characterlevel.v1.skills", delegate(long sender, string text)
				{
					if (_networkToken == token && !_closed)
					{
						ReceiveReport(sender, text);
					}
				});
				game.Register("benjsammy.characterlevel.v1.ranking", delegate(long sender, string text)
				{
					if (_networkToken == token && !_closed)
					{
						ReceiveRanking(sender, text);
					}
				});
			}
			if (now >= _nextReport && game.HasLocalPlayer)
			{
				_nextReport = now + 5.0;
				if (game.TryGetLocalSkills(out var levels, out var name) && LevelMath.TrySum(levels, out var total))
				{
					LocalLevel = total;
					LocalName = PacketCodec.CleanName(name);
					HasLocalLevel = true;
					if (game.IsServer)
					{
						_totals[game.LocalPeerId] = total;
						_dirty = true;
					}
					else if (game.ServerPeerId != 0L)
					{
						game.Send(game.ServerPeerId, "benjsammy.characterlevel.v1.skills", PacketCodec.Encode(new SkillReport
						{
							Skills = levels
						}));
					}
				}
			}
			if (game.IsServer && (now >= _nextSnapshot || (_dirty && now - _lastBroadcast >= 0.5)))
			{
				PublishRanking();
			}
		}

		private void ReceiveReport(long sender, string payload)
		{
			if (_game != null && _game.IsServer && _game.GetPeers().FirstOrDefault((OnlinePeer player) => player.Id == sender) != null && (!_lastReport.TryGetValue(sender, out var value) || !(_now - value < 0.5)) && PacketCodec.TryDecode<SkillReport>(payload, out var packet) && packet.Version == 1 && LevelMath.TrySum(packet.Skills, out var total))
			{
				_totals[sender] = total;
				_lastReport[sender] = _now;
				_dirty = true;
			}
		}

		private void PublishRanking()
		{
			List<OnlinePeer> list = (from peer in _game.GetPeers()
				where peer.Id != 0
				group peer by peer.Id into @group
				select @group.First()).ToList();
			if (!_game.IsDedicated && _game.HasLocalPlayer && _game.TryGetLocalSkills(out var _, out var name))
			{
				list.Add(new OnlinePeer
				{
					Id = _game.LocalPeerId,
					Name = name
				});
			}
			list = (from peer in list
				where peer.Id != 0
				group peer by peer.Id into @group
				select @group.First()).Take(256).ToList();
			HashSet<long> activeIds = new HashSet<long>(list.Select((OnlinePeer peer) => peer.Id));
			long[] array = _totals.Keys.Where((long id) => !activeIds.Contains(id)).ToArray();
			foreach (long key in array)
			{
				_totals.Remove(key);
			}
			array = _lastReport.Keys.Where((long id) => !activeIds.Contains(id)).ToArray();
			foreach (long key2 in array)
			{
				_lastReport.Remove(key2);
			}
			Rows = PacketCodec.Sort(list.Select((OnlinePeer peer) => new RankRow
			{
				PeerId = peer.Id,
				Name = PacketCodec.CleanName(peer.Name),
				Level = (_totals.TryGetValue(peer.Id, out var value) ? new int?(value) : ((int?)null))
			}));
			_lastSnapshot = _now;
			_lastBroadcast = _now;
			_nextSnapshot = _now + 5.0;
			_dirty = false;
			string payload = PacketCodec.Encode(new RankingPacket
			{
				Rows = Rows
			});
			foreach (OnlinePeer item in list)
			{
				if (item.Id != _game.LocalPeerId && _totals.ContainsKey(item.Id))
				{
					_game.Send(item.Id, "benjsammy.characterlevel.v1.ranking", payload);
				}
			}
		}

		private void ReceiveRanking(long sender, string payload)
		{
			if (_game != null && !_game.IsServer && sender == _game.ServerPeerId && PacketCodec.TryReadRanking(payload, out var rows))
			{
				Rows = rows;
				_lastSnapshot = _now;
			}
		}

		internal RankRow[] DisplayRows()
		{
			if (Synchronized)
			{
				return Rows;
			}
			if (HasLocalLevel && _game != null && _game.HasLocalPlayer)
			{
				return new RankRow[1]
				{
					new RankRow
					{
						PeerId = _game.LocalPeerId,
						Name = LocalName,
						Level = LocalLevel
					}
				};
			}
			return new RankRow[0];
		}

		private void ResetSession()
		{
			_game = null;
			_networkToken = null;
			_totals.Clear();
			_lastReport.Clear();
			Rows = new RankRow[0];
			LocalLevel = 0;
			LocalName = "";
			HasLocalLevel = false;
			_nextReport = 0.0;
			_nextSnapshot = 0.0;
			_lastSnapshot = -1.0;
			_lastBroadcast = -1.0;
			_dirty = false;
		}

		internal void Close()
		{
			ResetSession();
			_closed = true;
		}
	}
	internal sealed class GameApi : IGameApi
	{
		private const BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private readonly MemberInfo _netInstance;

		private readonly MemberInfo _rpcInstance;

		private readonly MemberInfo _localPlayer;

		private readonly MethodInfo _isServer;

		private readonly MethodInfo _isDedicated;

		private readonly MethodInfo _getPeers;

		private readonly MethodInfo _getServerPeer;

		private readonly MethodInfo _serverPeerId;

		private readonly FieldInfo _routerId;

		private readonly FieldInfo _peerId;

		private readonly FieldInfo _peerName;

		private readonly MethodInfo _peerReady;

		private readonly MethodInfo _getSkills;

		private readonly MethodInfo _getSkillList;

		private readonly FieldInfo _skillLevel;

		private readonly MethodInfo _getPlayerName;

		private readonly MethodInfo _register;

		private readonly MethodInfo _send;

		private object Net => Read(_netInstance, null);

		private object Router => Read(_rpcInstance, null);

		private object LocalPlayer => Read(_localPlayer, null);

		public object NetworkToken
		{
			get
			{
				if (Net != null)
				{
					return Router;
				}
				return null;
			}
		}

		public bool IsServer
		{
			get
			{
				if (Net != null)
				{
					return (bool)_isServer.Invoke(Net, null);
				}
				return false;
			}
		}

		public bool IsDedicated
		{
			get
			{
				if (Net != null)
				{
					return (bool)_isDedicated.Invoke(Net, null);
				}
				return false;
			}
		}

		public bool HasLocalPlayer => LocalPlayer != null;

		public long LocalPeerId
		{
			get
			{
				if (Router != null)
				{
					return (long)_routerId.GetValue(Router);
				}
				return 0L;
			}
		}

		public long ServerPeerId
		{
			get
			{
				if (Net == null || Router == null)
				{
					return 0L;
				}
				if (IsServer)
				{
					return LocalPeerId;
				}
				object obj = _getServerPeer.Invoke(Net, null);
				if (obj != null)
				{
					return (long)_peerId.GetValue(obj);
				}
				return (long)_serverPeerId.Invoke(Router, null);
			}
		}

		internal GameApi()
		{
			Type type = RequireType("ZNet");
			Type type2 = RequireType("ZRoutedRpc");
			Type type3 = RequireType("ZNetPeer");
			Type type4 = RequireType("Player");
			Type type5 = RequireType("Skills");
			Type nestedType = type5.GetNestedType("Skill", BindingFlags.Public | BindingFlags.NonPublic);
			if (nestedType == null)
			{
				throw new TypeLoadException("Skills.Skill was not found.");
			}
			_netInstance = RequireMember(type, "instance");
			_rpcInstance = RequireMember(type2, "instance");
			_localPlayer = RequireMember(type4, "m_localPlayer");
			_isServer = RequireMethod(type, "IsServer", Type.EmptyTypes, typeof(bool));
			_isDedicated = RequireMethod(type, "IsDedicated", Type.EmptyTypes, typeof(bool));
			_getPeers = RequireMethod(type, "GetPeers", Type.EmptyTypes);
			_getServerPeer = RequireMethod(type, "GetServerPeer", Type.EmptyTypes);
			_serverPeerId = RequireMethod(type2, "GetServerPeerID", Type.EmptyTypes, typeof(long));
			_routerId = RequireField(type2, "m_id", typeof(long));
			_peerId = RequireField(type3, "m_uid", typeof(long));
			_peerName = RequireField(type3, "m_playerName", typeof(string));
			_peerReady = type3.GetMethod("IsReady", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
			_getSkills = RequireMethod(type4, "GetSkills", Type.EmptyTypes);
			_getSkillList = RequireMethod(type5, "GetSkillList", Type.EmptyTypes);
			_skillLevel = RequireField(nestedType, "m_level", typeof(float));
			_getPlayerName = RequireMethod(type4, "GetPlayerName", Type.EmptyTypes, typeof(string));
			_send = RequireMethod(type2, "InvokeRoutedRPC", new Type[3]
			{
				typeof(long),
				typeof(string),
				typeof(object[])
			});
			_register = (from method in type2.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
				where method.Name == "Register" && method.IsGenericMethodDefinition && method.GetGenericArguments().Length == 1
				select method.MakeGenericMethod(typeof(string))).FirstOrDefault(delegate(MethodInfo method)
			{
				ParameterInfo[] parameters = method.GetParameters();
				return parameters.Length == 2 && parameters[0].ParameterType == typeof(string) && parameters[1].ParameterType == typeof(Action<long, string>);
			});
			if (_register == null)
			{
				throw new MissingMethodException("ZRoutedRpc.Register<string>(string, Action<long,string>)");
			}
		}

		public bool TryGetLocalSkills(out float[] levels, out string name)
		{
			levels = null;
			name = "";
			object localPlayer = LocalPlayer;
			if (localPlayer == null)
			{
				return false;
			}
			object obj = _getSkills.Invoke(localPlayer, null);
			if (obj == null)
			{
				return false;
			}
			if (!(_getSkillList.Invoke(obj, null) is IEnumerable enumerable))
			{
				return false;
			}
			List<float> list = new List<float>();
			foreach (object item in enumerable)
			{
				if (item != null)
				{
					if (list.Count >= 512)
					{
						return false;
					}
					list.Add((float)_skillLevel.GetValue(item));
				}
			}
			levels = list.ToArray();
			name = (string)_getPlayerName.Invoke(localPlayer, null);
			return true;
		}

		public List<OnlinePeer> GetPeers()
		{
			List<OnlinePeer> list = new List<OnlinePeer>();
			if (Net == null || !IsServer)
			{
				return list;
			}
			if (!(_getPeers.Invoke(Net, null) is IEnumerable enumerable))
			{
				return list;
			}
			foreach (object item in enumerable)
			{
				if (item != null && (!(_peerReady != null) || !(_peerReady.ReturnType == typeof(bool)) || (bool)_peerReady.Invoke(item, null)))
				{
					string text = (string)_peerName.GetValue(item);
					long num = (long)_peerId.GetValue(item);
					if (num != 0L && !string.IsNullOrWhiteSpace(text))
					{
						list.Add(new OnlinePeer
						{
							Id = num,
							Name = text
						});
					}
				}
			}
			return list;
		}

		public void Register(string method, Action<long, string> handler)
		{
			object router = Router;
			if (router == null)
			{
				throw new InvalidOperationException("Valheim network is not ready.");
			}
			_register.Invoke(router, new object[2] { method, handler });
		}

		public void Send(long destination, string method, string payload)
		{
			object router = Router;
			if (router != null)
			{
				_send.Invoke(router, new object[3]
				{
					destination,
					method,
					new object[1] { payload }
				});
			}
		}

		private static Type RequireType(string name)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			for (int i = 0; i < assemblies.Length; i++)
			{
				Type type = assemblies[i].GetType(name, throwOnError: false);
				if (type != null)
				{
					return type;
				}
			}
			throw new TypeLoadException("Valheim type was not found: " + name);
		}

		private static MemberInfo RequireMember(Type type, string name)
		{
			object obj = ((object)type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) ?? ((object)type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic));
			if (obj == null)
			{
				throw new MissingMemberException(type.FullName, name);
			}
			return (MemberInfo)obj;
		}

		private static FieldInfo RequireField(Type type, string name, Type expected)
		{
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (field == null || field.FieldType != expected)
			{
				throw new MissingFieldException(type.FullName, name);
			}
			return field;
		}

		private static MethodInfo RequireMethod(Type type, string name, Type[] parameters, Type expected = null)
		{
			MethodInfo method = type.GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, parameters, null);
			if (method == null || (expected != null && method.ReturnType != expected))
			{
				throw new MissingMethodException(type.FullName, name);
			}
			return method;
		}

		private static object Read(MemberInfo member, object instance)
		{
			FieldInfo fieldInfo = member as FieldInfo;
			if (!(fieldInfo != null))
			{
				return ((PropertyInfo)member).GetValue(instance, null);
			}
			return fieldInfo.GetValue(instance);
		}
	}
	internal sealed class RankingPanel
	{
		private Vector2 _scroll;

		private GUIStyle _line;

		private GUIStyle _title;

		private int _fontSize;

		internal static bool TogglePressed(KeyCode key)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			Event current = Event.current;
			if ((int)key != 0 && current != null && (int)current.type == 4 && current.keyCode == key)
			{
				current.Use();
				return true;
			}
			return false;
		}

		internal void Draw(RankingEngine engine, float customScale, float referenceWidth, float topFraction, float marginRight, int maximumRows, float backgroundOpacity)
		{
			//IL_0328: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0310: Unknown result type (might be due to invalid IL or missing references)
			//IL_0315: Unknown result type (might be due to invalid IL or missing references)
			//IL_031c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02db: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fa: Unknown result type (might be due to invalid IL or missing references)
			if (Screen.height < 240)
			{
				return;
			}
			RankRow[] array = engine.DisplayRows();
			float num = Mathf.Clamp((float)Screen.height / 1080f, 0.65f, 1.8f) * customScale;
			EnsureStyles(num);
			float num2 = 12f * num;
			float num3 = Mathf.Min(marginRight * num, (float)Screen.width * 0.2f);
			float num4 = _title.CalcSize(new GUIContent("Connecté")).x;
			RankRow[] array2 = array;
			foreach (RankRow row in array2)
			{
				num4 = Mathf.Max(num4, _line.CalcSize(new GUIContent(RowLabel(row))).x);
			}
			float num5 = Mathf.Min(Mathf.Clamp(num4 + num2 * 2f + 18f * num, 160f * num, referenceWidth * num), (float)Screen.width - num3 - 12f);
			if (num5 < 100f)
			{
				return;
			}
			float num6 = 34f * num;
			float num7 = 8f * num;
			float num8 = 28f * num;
			float num9 = Mathf.Clamp((float)Screen.height * topFraction, 8f, (float)Screen.height - 120f);
			int num10 = Mathf.Max(1, Mathf.FloorToInt(((float)Screen.height - num9 - 16f - num6 - num7) / num8));
			int num11 = Mathf.Min(Mathf.Max(1, array.Length), Mathf.Min(maximumRows, num10));
			float num12 = (float)num11 * num8;
			float num13 = num6 + num12 + num7;
			if (num13 > (float)Screen.height - 16f)
			{
				num12 = Mathf.Max(16f, (float)Screen.height - 16f - num6 - num7);
				num13 = num6 + num12 + num7;
			}
			num9 = Mathf.Min(num9, Mathf.Max(8f, (float)Screen.height - num13 - 8f));
			float num14 = Mathf.Max(6f, (float)Screen.width - num5 - num3);
			Color color = GUI.color;
			int depth = GUI.depth;
			try
			{
				GUI.depth = -100;
				GUI.color = new Color(0.035f, 0.04f, 0.05f, Mathf.Clamp01(backgroundOpacity));
				GUI.DrawTexture(new Rect(num14, num9, num5, num13), (Texture)(object)Texture2D.whiteTexture);
				Label(new Rect(num14 + num2, num9 + 5f * num, num5 - num2 * 2f, 25f * num), "Connecté", _title, num);
				Rect val = default(Rect);
				((Rect)(ref val))..ctor(num14 + num2, num9 + num6, num5 - num2 * 2f, num12);
				if (array.Length > num11)
				{
					Rect val2 = default(Rect);
					((Rect)(ref val2))..ctor(0f, 0f, Mathf.Max(1f, ((Rect)(ref val)).width - 18f * num), (float)array.Length * num8);
					_scroll = GUI.BeginScrollView(val, _scroll, val2, false, false);
					try
					{
						DrawRows(array, new Rect(0f, 0f, ((Rect)(ref val2)).width, ((Rect)(ref val2)).height), num8, num);
						return;
					}
					finally
					{
						GUI.EndScrollView();
					}
				}
				_scroll = Vector2.zero;
				DrawRows(array, val, num8, num);
			}
			finally
			{
				GUI.color = color;
				GUI.depth = depth;
			}
		}

		private void DrawRows(RankRow[] rows, Rect area, float rowHeight, float scale)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < rows.Length; i++)
			{
				Label(new Rect(((Rect)(ref area)).x, ((Rect)(ref area)).y + (float)i * rowHeight, ((Rect)(ref area)).width, rowHeight), RowLabel(rows[i]), _line, scale);
			}
			if (rows.Length == 0)
			{
				Label(new Rect(((Rect)(ref area)).x, ((Rect)(ref area)).y, ((Rect)(ref area)).width, rowHeight), "…", _line, scale);
			}
		}

		private static string RowLabel(RankRow row)
		{
			return row.Name + " " + (row.Level.HasValue ? row.Level.Value.ToString() : "…") + " lvl.";
		}

		private void EnsureStyles(float scale)
		{
			int num = Mathf.RoundToInt(17f * scale);
			if (_line == null || _fontSize != num)
			{
				_fontSize = num;
				_line = Style(num, (FontStyle)0);
				_title = Style(Mathf.RoundToInt(18f * scale), (FontStyle)1);
			}
		}

		private static GUIStyle Style(int size, FontStyle fontStyle)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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_004f: Expected O, but got Unknown
			//IL_004f: 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_0060: Expected O, but got Unknown
			GUIStyle val = new GUIStyle(GUI.skin.label)
			{
				fontSize = Mathf.Max(9, size),
				fontStyle = fontStyle,
				alignment = (TextAnchor)3,
				richText = false,
				wordWrap = false,
				clipping = (TextClipping)1,
				padding = new RectOffset(0, 0, 0, 0)
			};
			val.normal.textColor = Color.white;
			return val;
		}

		private static void Label(Rect rectangle, string text, GUIStyle style, float scale)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			GUI.color = new Color(0f, 0f, 0f, 0.65f);
			GUI.Label(new Rect(((Rect)(ref rectangle)).x + scale, ((Rect)(ref rectangle)).y + scale, ((Rect)(ref rectangle)).width, ((Rect)(ref rectangle)).height), text, style);
			GUI.color = Color.white;
			GUI.Label(rectangle, text, style);
		}
	}
}