Decompiled source of FairTrophies v1.0.2

FairTrophies.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Hampus Toft")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © Hampus Toft")]
[assembly: AssemblyDescription("Fair rare drops for Valheim: one bad-luck counter per character, shared by every star level.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+74e989517cd6d4ebb71acc8f8845e4c6e3d4857b")]
[assembly: AssemblyProduct("FairTrophies")]
[assembly: AssemblyTitle("FairTrophies")]
[assembly: AssemblyVersion("1.0.0.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 FairTrophies
{
	internal static class CharacterCounters
	{
		internal const string CustomDataKey = "FairTrophies_counters";

		internal static List<KeyValuePair<int, int>> RegisterKill(Player player, string creatureName, CharacterDrop table, int level)
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Invalid comparison between Unknown and I4
			player.m_customData.TryGetValue("FairTrophies_counters", out var value);
			SharedDropCounter sharedDropCounter = SharedDropCounter.Parse(value, () => Random.value);
			List<KeyValuePair<int, int>> list = new List<KeyValuePair<int, int>>();
			foreach (Drop drop in table.m_drops)
			{
				if ((Object)(object)drop.m_prefab == (Object)null || !SharedDropCounter.IsGoverned(drop.m_chance))
				{
					continue;
				}
				string name = ((Object)drop.m_prefab).name;
				ItemDrop component = drop.m_prefab.GetComponent<ItemDrop>();
				bool isTrophy = (Object)(object)component != (Object)null && (int)component.m_itemData.m_shared.m_itemType == 13;
				StarScaling starScaling = StarScaling.For(level, drop.m_levelMultiplier, name, isTrophy);
				bool flag = sharedDropCounter.RegisterKill(name, drop.m_chance, starScaling.Weight);
				float? remaining = sharedDropCounter.GetRemaining(name);
				Log.Counter($"{player.GetPlayerName()} killed {creatureName} (level {level}) -> {name} " + $"chance {drop.m_chance:0.###} rate x{starScaling.Weight} amount x{starScaling.AmountMultiplier}: " + (flag ? "DROP" : "no drop") + ", " + (remaining.HasValue ? (remaining.Value / drop.m_chance).ToString("0.0") : "-") + " base kills to next");
				if (flag)
				{
					int num = VanillaAmount(drop, starScaling.AmountMultiplier);
					if (num > 0)
					{
						list.Add(new KeyValuePair<int, int>(StringExtensionMethods.GetStableHashCode(name), num));
					}
				}
			}
			player.m_customData["FairTrophies_counters"] = sharedDropCounter.Serialize();
			return list;
		}

		private static int VanillaAmount(Drop drop, int amountMultiplier)
		{
			int num = (drop.m_dontScale ? Random.Range(drop.m_amountMin, drop.m_amountMax) : Game.instance.ScaleDrops(drop.m_prefab, drop.m_amountMin, drop.m_amountMax));
			num *= amountMultiplier;
			if (drop.m_onePerPlayer)
			{
				num = ZNet.instance.GetNrOfPlayers();
			}
			return Mathf.Min(num, 100);
		}
	}
	[HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")]
	internal static class CharacterDropPatch
	{
		[HarmonyPrefix]
		private static void Prefix(CharacterDrop __instance, out List<Drop> __state)
		{
			__state = null;
			if ((Object)(object)ZNet.instance == (Object)null || ZRoutedRpc.instance == null || ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey((GlobalKeys)36)))
			{
				return;
			}
			List<Drop> list = null;
			foreach (Drop drop in __instance.m_drops)
			{
				if (!((Object)(object)drop?.m_prefab != (Object)null) || !SharedDropCounter.IsGoverned(drop.m_chance) || list != null)
				{
					continue;
				}
				list = new List<Drop>(__instance.m_drops.Count);
				foreach (Drop drop2 in __instance.m_drops)
				{
					if ((Object)(object)drop2?.m_prefab == (Object)null || !SharedDropCounter.IsGoverned(drop2.m_chance))
					{
						list.Add(drop2);
					}
				}
			}
			if (list != null)
			{
				__state = __instance.m_drops;
				__instance.m_drops = list;
			}
		}

		[HarmonyPostfix]
		private static void Postfix(CharacterDrop __instance, List<Drop> __state)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			if (__state != null)
			{
				Character component = ((Component)__instance).GetComponent<Character>();
				int level = ((!Object.op_Implicit((Object)(object)component)) ? 1 : component.GetLevel());
				Vector3 dropPoint = (Object.op_Implicit((Object)(object)component) ? (component.GetCenterPoint() + ((Component)__instance).transform.TransformVector(__instance.m_spawnOffset)) : ((Component)__instance).transform.position);
				KillRouting.ReportKill(StringExtensionMethods.GetStableHashCode(Utils.GetPrefabName(((Component)__instance).gameObject)), level, KillAttribution.LastPlayer(component), dropPoint, RagdollContext.Current, __instance.m_cheated);
			}
		}

		[HarmonyFinalizer]
		private static void Finalizer(CharacterDrop __instance, List<Drop> __state)
		{
			if (__state != null)
			{
				__instance.m_drops = __state;
			}
		}
	}
	[HarmonyPatch(typeof(Ragdoll), "SaveLootList")]
	internal static class RagdollContext
	{
		internal static ZDOID Current = ZDOID.None;

		private static readonly List<int> DeferredHashes = new List<int>();

		private static readonly List<int> DeferredAmounts = new List<int>();

		internal static void Defer(IList<int> hashes, IList<int> amounts)
		{
			DeferredHashes.AddRange(hashes);
			DeferredAmounts.AddRange(amounts);
		}

		[HarmonyPrefix]
		private static void Prefix(Ragdoll __instance)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
			Current = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO().m_uid : ZDOID.None);
		}

		[HarmonyPostfix]
		private static void Postfix(Ragdoll __instance)
		{
			if (DeferredHashes.Count != 0)
			{
				ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
				if ((Object)(object)component != (Object)null && component.IsValid())
				{
					KillRouting.AppendRagdollLoot(component.GetZDO(), DeferredHashes, DeferredAmounts);
				}
			}
		}

		[HarmonyFinalizer]
		private static void Finalizer()
		{
			//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)
			Current = ZDOID.None;
			DeferredHashes.Clear();
			DeferredAmounts.Clear();
		}
	}
	internal static class KillAttribution
	{
		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		private static class DamagePatch
		{
			[HarmonyPostfix]
			private static void Postfix(Character __instance, HitData hit)
			{
				if (hit == null || __instance.IsPlayer())
				{
					return;
				}
				Character attacker = hit.GetAttacker();
				Player val = (Player)(object)((attacker is Player) ? attacker : null);
				if (val == null)
				{
					return;
				}
				ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
				if (!((Object)(object)component == (Object)null) && component.IsValid() && component.IsOwner())
				{
					long playerID = val.GetPlayerID();
					if (playerID != 0L)
					{
						component.GetZDO().Set(LastPlayerKey, playerID);
					}
				}
			}
		}

		private static readonly int LastPlayerKey = StringExtensionMethods.GetStableHashCode("FairTrophies_lastPlayer");

		internal static long LastPlayer(Character character)
		{
			ZNetView val = (Object.op_Implicit((Object)(object)character) ? ((Component)character).GetComponent<ZNetView>() : null);
			if (!((Object)(object)val != (Object)null) || !val.IsValid())
			{
				return 0L;
			}
			return val.GetZDO().GetLong(LastPlayerKey, 0L);
		}
	}
	public sealed class SharedDropCounter
	{
		public const float Spread = 2f;

		private readonly Dictionary<string, float> remaining = new Dictionary<string, float>();

		private readonly Func<float> random01;

		private const string FormatPrefix = "1|";

		public const float PseudoDropThreshold = 0.3f;

		public int Count => remaining.Count;

		public SharedDropCounter(Func<float> random01)
		{
			this.random01 = random01 ?? throw new ArgumentNullException("random01");
		}

		public bool RegisterKill(string item, float baseChance, float weight)
		{
			float num = baseChance * weight;
			if (num <= 0f)
			{
				return false;
			}
			if (num >= 1f)
			{
				return true;
			}
			if (!remaining.TryGetValue(item, out var value))
			{
				value = RollInterval();
			}
			value -= num;
			bool num2 = value <= 0f;
			if (num2)
			{
				value += RollInterval();
			}
			remaining[item] = value;
			return num2;
		}

		public float? GetRemaining(string item)
		{
			if (!remaining.TryGetValue(item, out var value))
			{
				return null;
			}
			return value;
		}

		public void Set(string item, float left)
		{
			remaining[item] = left;
		}

		public IReadOnlyDictionary<string, float> Snapshot()
		{
			return remaining;
		}

		private float RollInterval()
		{
			return random01() * 2f;
		}

		public string Serialize()
		{
			StringBuilder stringBuilder = new StringBuilder("1|");
			bool flag = true;
			foreach (KeyValuePair<string, float> item in remaining)
			{
				if (!flag)
				{
					stringBuilder.Append(';');
				}
				flag = false;
				stringBuilder.Append(item.Key).Append('=').Append(item.Value.ToString("R", CultureInfo.InvariantCulture));
			}
			return stringBuilder.ToString();
		}

		public static SharedDropCounter Parse(string text, Func<float> random01)
		{
			SharedDropCounter sharedDropCounter = new SharedDropCounter(random01);
			if (string.IsNullOrEmpty(text) || !text.StartsWith("1|", StringComparison.Ordinal))
			{
				return sharedDropCounter;
			}
			string[] array = text.Substring("1|".Length).Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text2 in array)
			{
				int num = text2.LastIndexOf('=');
				if (num > 0 && float.TryParse(text2.Substring(num + 1), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && !float.IsNaN(result) && !float.IsInfinity(result))
				{
					sharedDropCounter.remaining[text2.Substring(0, num)] = result;
				}
			}
			return sharedDropCounter;
		}

		public static bool IsGoverned(float baseChance)
		{
			if (baseChance > 0f)
			{
				return baseChance <= 0.3f;
			}
			return false;
		}
	}
	public readonly struct StarScaling
	{
		public float Weight { get; }

		public int AmountMultiplier { get; }

		private StarScaling(float weight, int amountMultiplier)
		{
			Weight = weight;
			AmountMultiplier = amountMultiplier;
		}

		public static StarScaling For(int level, bool levelMultiplier, string itemName, bool isTrophy)
		{
			if (!levelMultiplier || level <= 1)
			{
				return new StarScaling(1f, 1);
			}
			int num = (int)Math.Pow(2.0, level - 1);
			if (!IsUniqueRareItem(itemName, isTrophy))
			{
				return new StarScaling(1f, num);
			}
			return new StarScaling(num, 1);
		}

		public static bool IsUniqueRareItem(string itemName, bool isTrophy)
		{
			if (isTrophy)
			{
				return true;
			}
			if (string.IsNullOrEmpty(itemName))
			{
				return false;
			}
			if (!itemName.StartsWith("AncientGemstone", StringComparison.Ordinal))
			{
				return itemName.StartsWith("MoldArmor", StringComparison.Ordinal);
			}
			return true;
		}
	}
	internal static class FairTrophiesConfig
	{
		public static ConfigEntry<bool> LogCounters;

		public static void Initialize(ConfigFile config)
		{
			LogCounters = config.Bind<bool>("Debug", "LogCounters", false, "Log every counted kill: who was credited, the item, whether it dropped and how many kills are left.");
		}
	}
	[BepInPlugin("HampusToft.FairTrophies", "FairTrophies", "1.0.2")]
	public class FairTrophiesPlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "HampusToft.FairTrophies";

		public const string PluginName = "FairTrophies";

		public const string PluginVersion = "1.0.2";

		private Harmony harmony;

		private void Awake()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			Log.Source = ((BaseUnityPlugin)this).Logger;
			FairTrophiesConfig.Initialize(((BaseUnityPlugin)this).Config);
			harmony = new Harmony("HampusToft.FairTrophies");
			harmony.PatchAll(typeof(FairTrophiesPlugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"FairTrophies 1.0.2 initialized");
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}
	}
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	internal static class RegisterRpcsPatch
	{
		[HarmonyPostfix]
		private static void Postfix(ZRoutedRpc __instance)
		{
			KillRouting.Register(__instance);
		}
	}
	internal static class Log
	{
		internal static ManualLogSource Source;

		internal static void Counter(string message)
		{
			if (FairTrophiesConfig.LogCounters != null && FairTrophiesConfig.LogCounters.Value)
			{
				ManualLogSource source = Source;
				if (source != null)
				{
					source.LogInfo((object)message);
				}
			}
		}

		internal static void Info(string message)
		{
			ManualLogSource source = Source;
			if (source != null)
			{
				source.LogInfo((object)message);
			}
		}

		internal static void Warning(string message)
		{
			ManualLogSource source = Source;
			if (source != null)
			{
				source.LogWarning((object)message);
			}
		}
	}
	internal static class KillRouting
	{
		private const string KillRpc = "FairTrophies_Kill";

		private const string CountRpc = "FairTrophies_Count";

		private const string DropRpc = "FairTrophies_Drop";

		internal static void Register(ZRoutedRpc rpc)
		{
			rpc.Register<ZPackage>("FairTrophies_Kill", (Action<long, ZPackage>)RPC_Kill);
			rpc.Register<ZPackage>("FairTrophies_Count", (Action<long, ZPackage>)RPC_Count);
			rpc.Register<ZPackage>("FairTrophies_Drop", (Action<long, ZPackage>)RPC_Drop);
		}

		internal static void ReportKill(int creatureHash, int level, long creditedPlayer, Vector3 dropPoint, ZDOID ragdoll, bool cheated)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			long num = (((Object)(object)Player.m_localPlayer != (Object)null) ? Player.m_localPlayer.GetPlayerID() : 0);
			ZPackage val = new ZPackage();
			val.Write(creatureHash);
			val.Write(level);
			val.Write(creditedPlayer);
			val.Write(num);
			val.Write(dropPoint);
			val.Write(ragdoll);
			val.Write(cheated);
			ZRoutedRpc.instance.InvokeRoutedRPC("FairTrophies_Kill", new object[1] { val });
		}

		private static void RPC_Kill(long sender, ZPackage pkg)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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)
			if (ZNet.instance.IsServer())
			{
				int num = pkg.ReadInt();
				int num2 = pkg.ReadInt();
				long playerId = pkg.ReadLong();
				long playerId2 = pkg.ReadLong();
				Vector3 val = pkg.ReadVector3();
				ZDOID val2 = pkg.ReadZDOID();
				bool flag = pkg.ReadBool();
				ZDO val3 = FindCharacter(playerId) ?? FindCharacter(playerId2) ?? NearestCharacter(val);
				if (val3 == null)
				{
					Log.Counter($"Kill of {num} at {val} has no online player to credit; rare drops skipped");
					return;
				}
				ZPackage val4 = new ZPackage();
				val4.Write(num);
				val4.Write(num2);
				val4.Write(val3.GetLong(ZDOVars.s_playerID, 0L));
				val4.Write(sender);
				val4.Write(val);
				val4.Write(val2);
				val4.Write(flag);
				ZRoutedRpc.instance.InvokeRoutedRPC(val3.GetOwner(), "FairTrophies_Count", new object[1] { val4 });
			}
		}

		private static void RPC_Count(long sender, ZPackage pkg)
		{
			//IL_0026: 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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			if (!IsFromServer(sender))
			{
				return;
			}
			int num = pkg.ReadInt();
			int level = pkg.ReadInt();
			long num2 = pkg.ReadLong();
			long num3 = pkg.ReadLong();
			Vector3 val = pkg.ReadVector3();
			ZDOID val2 = pkg.ReadZDOID();
			bool flag = pkg.ReadBool();
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || localPlayer.GetPlayerID() != num2)
			{
				return;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(num);
			CharacterDrop val3 = (Object.op_Implicit((Object)(object)prefab) ? prefab.GetComponent<CharacterDrop>() : null);
			if ((Object)(object)val3 == (Object)null)
			{
				return;
			}
			List<KeyValuePair<int, int>> list = CharacterCounters.RegisterKill(localPlayer, ((Object)prefab).name, val3, level);
			if (list.Count == 0)
			{
				return;
			}
			ZPackage val4 = new ZPackage();
			val4.Write(val);
			val4.Write(val2);
			val4.Write(flag);
			val4.Write(list.Count);
			foreach (KeyValuePair<int, int> item in list)
			{
				val4.Write(item.Key);
				val4.Write(item.Value);
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(num3, "FairTrophies_Drop", new object[1] { val4 });
		}

		private static void RPC_Drop(long sender, ZPackage pkg)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: 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_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: 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)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = pkg.ReadVector3();
			ZDOID val2 = pkg.ReadZDOID();
			bool flag = pkg.ReadBool();
			int num = pkg.ReadInt();
			int[] array = new int[num];
			int[] array2 = new int[num];
			for (int i = 0; i < num; i++)
			{
				array[i] = pkg.ReadInt();
				array2[i] = pkg.ReadInt();
			}
			if (!((ZDOID)(ref val2)).IsNone())
			{
				if (val2 == RagdollContext.Current)
				{
					RagdollContext.Defer(array, array2);
					return;
				}
				ZDO zDO = ZDOMan.instance.GetZDO(val2);
				if (zDO != null && zDO.IsOwner())
				{
					AppendRagdollLoot(zDO, array, array2);
					return;
				}
			}
			List<KeyValuePair<GameObject, int>> list = new List<KeyValuePair<GameObject, int>>(num);
			for (int j = 0; j < num; j++)
			{
				GameObject prefab = ZNetScene.instance.GetPrefab(array[j]);
				if ((Object)(object)prefab != (Object)null)
				{
					list.Add(new KeyValuePair<GameObject, int>(prefab, array2[j]));
				}
			}
			CharacterDrop.DropItems(list, val, 0.5f, flag);
		}

		internal static void AppendRagdollLoot(ZDO ragdollZdo, IList<int> hashes, IList<int> amounts)
		{
			int num = ragdollZdo.GetInt(ZDOVars.s_drops, 0);
			int num2 = 0;
			while (num2 < hashes.Count)
			{
				ragdollZdo.Set("drop_hash" + num, hashes[num2]);
				ragdollZdo.Set("drop_amount" + num, amounts[num2]);
				num2++;
				num++;
			}
			ragdollZdo.Set(ZDOVars.s_drops, num, false);
		}

		private static bool IsFromServer(long sender)
		{
			if (ZNet.instance.IsServer())
			{
				return sender == ZDOMan.GetSessionID();
			}
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer != null)
			{
				return sender == serverPeer.m_uid;
			}
			return false;
		}

		private static ZDO FindCharacter(long playerId)
		{
			if (playerId == 0L)
			{
				return null;
			}
			foreach (ZDO allCharacterZDO in ZNet.instance.GetAllCharacterZDOS())
			{
				if (allCharacterZDO.GetLong(ZDOVars.s_playerID, 0L) == playerId)
				{
					return allCharacterZDO;
				}
			}
			return null;
		}

		private static ZDO NearestCharacter(Vector3 point)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			ZDO result = null;
			float num = float.MaxValue;
			foreach (ZDO allCharacterZDO in ZNet.instance.GetAllCharacterZDOS())
			{
				float num2 = Vector3.Distance(allCharacterZDO.GetPosition(), point);
				if (allCharacterZDO.GetLong(ZDOVars.s_playerID, 0L) != 0L && num2 < num)
				{
					result = allCharacterZDO;
					num = num2;
				}
			}
			return result;
		}
	}
	internal static class VersionCheck
	{
		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		private static class OnNewConnectionPatch
		{
			[HarmonyPostfix]
			private static void Postfix(ZNet __instance, ZNetPeer peer)
			{
				if (!__instance.IsServer())
				{
					serverVersion = null;
					failureMessage = null;
				}
				peer.m_rpc.Register<string>("FairTrophies_Version", (Action<ZRpc, string>)delegate(ZRpc rpc, string version)
				{
					RPC_Version(__instance, rpc, version);
				});
				peer.m_rpc.Invoke("FairTrophies_Version", new object[1] { "1.0.2" });
			}
		}

		[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
		private static class PeerInfoPatch
		{
			[HarmonyPrefix]
			private static bool Prefix(ZNet __instance, ZRpc rpc)
			{
				if (__instance.IsServer())
				{
					if (ValidatedClients.Contains(rpc))
					{
						return true;
					}
					Log.Warning("Rejecting " + rpc.GetSocket().GetEndPointString() + ": FairTrophies 1.0.2 is required on clients");
					rpc.Invoke("Error", new object[1] { 3 });
					return false;
				}
				if (serverVersion == "1.0.2")
				{
					return true;
				}
				failureMessage = ((serverVersion == null) ? "This server does not run FairTrophies.\nRemove FairTrophies 1.0.2 to join it." : ("FairTrophies version mismatch.\nServer: " + serverVersion + ", you: 1.0.2."));
				Log.Warning(failureMessage.Replace('\n', ' '));
				ConnectionStatus.Invoke() = (ConnectionStatus)3;
				return false;
			}
		}

		[HarmonyPatch(typeof(ZNet), "Disconnect")]
		private static class DisconnectPatch
		{
			[HarmonyPrefix]
			private static void Prefix(ZNetPeer peer)
			{
				if (peer?.m_rpc != null)
				{
					ValidatedClients.Remove(peer.m_rpc);
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "OnDestroy")]
		private static class ZNetDestroyPatch
		{
			[HarmonyPostfix]
			private static void Postfix()
			{
				ValidatedClients.Clear();
			}
		}

		[HarmonyPatch(typeof(FejdStartup), "ShowConnectError")]
		private static class ShowConnectErrorPatch
		{
			[HarmonyPostfix]
			private static void Postfix(FejdStartup __instance)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_000d: Invalid comparison between Unknown and I4
				if (failureMessage != null && (int)ZNet.GetConnectionStatus() == 3)
				{
					__instance.m_connectionFailedError.text = failureMessage;
				}
			}
		}

		private const string VersionRpc = "FairTrophies_Version";

		private static readonly HashSet<ZRpc> ValidatedClients = new HashSet<ZRpc>();

		private static string serverVersion;

		private static string failureMessage;

		private static readonly FieldRef<ConnectionStatus> ConnectionStatus = AccessTools.StaticFieldRefAccess<ConnectionStatus>(AccessTools.Field(typeof(ZNet), "m_connectionStatus"));

		private static void RPC_Version(ZNet znet, ZRpc rpc, string version)
		{
			if (znet.IsServer())
			{
				if (version == "1.0.2")
				{
					ValidatedClients.Add(rpc);
					return;
				}
				Log.Warning("Client " + rpc.GetSocket().GetEndPointString() + " runs FairTrophies " + version + ", server runs 1.0.2");
			}
			else
			{
				serverVersion = version;
			}
		}
	}
}