Decompiled source of OptionalNetworkAPI v1.1.0

OptionalNetworkAPI.dll

Decompiled 3 days ago
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using GTFO.API;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using OptionalNetworking.Hashing;
using OptionalNetworking.Managers;
using Player;
using SNetwork;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("OptionalNetworkAPI")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+11d78cfdb4cea7dbfa759ea055d91fb7aa24f102")]
[assembly: AssemblyProduct("OptionalNetworkAPI")]
[assembly: AssemblyTitle("OptionalNetworkAPI")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsUnmanagedAttribute : Attribute
	{
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace OptionalNetworking
{
	internal interface ICustomDataManager : ICustomDataSender
	{
		void OnPlayerModRemoved(SNet_Player player);
	}
	public class CustomDataManager<T> : CustomDataSender<T>, ICustomDataManager, ICustomDataSender where T : unmanaged
	{
		private readonly Dictionary<ulong, T> _data = new Dictionary<ulong, T>();

		public T this[SNet_Player player]
		{
			get
			{
				return _data[player.Lookup];
			}
			set
			{
				SetData(player, value);
			}
		}

		public event Action<SNet_Player, T>? PreDataSet;

		public event Action<SNet_Player, T>? OnDataSet;

		public event Action<SNet_Player, T>? OnDataRemoved;

		private CustomDataManager(ModInfo parent, string id, Func<SNet_Player, T> defaultProvider)
			: base(parent, id, defaultProvider, SenderMode.All)
		{
			ReceiveEvent = SetData;
		}

		internal static CustomDataManager<T> CreateDataManager(ModInfo parent, int index, Func<SNet_Player, T> defaultProvider)
		{
			return new CustomDataManager<T>(parent, $"{parent.Hash:x}_{index}", defaultProvider);
		}

		public bool TryGetData(SNet_Player player, out T data)
		{
			return _data.TryGetValue(player.Lookup, out data);
		}

		private void SetData(SNet_Player player, T data)
		{
			this.PreDataSet?.Invoke(player, data);
			_data[player.Lookup] = data;
			this.OnDataSet?.Invoke(player, data);
		}

		void ICustomDataManager.OnPlayerModRemoved(SNet_Player player)
		{
			if (player.IsBot)
			{
				RemoveData(player);
			}
		}

		private protected override void OnPlayerLeft(SNet_Player player)
		{
			base.OnPlayerLeft(player);
			RemoveData(player);
		}

		public void RemoveData(SNet_Player player)
		{
			if (_data.Remove(player.Lookup, out var value))
			{
				this.OnDataRemoved?.Invoke(player, value);
			}
		}

		private protected override void OnLobbyLeft()
		{
			base.OnLobbyLeft();
			_data.Clear();
		}
	}
	internal interface ICustomDataSender
	{
		void SendDefaultData(SNet_Player player);

		void OnPlayerLeft(SNet_Player player);

		void OnLobbyLeft();
	}
	[Flags]
	public enum SenderMode
	{
		OtherPlayers = 1,
		Bots = 4,
		Self = 8,
		All = 0xD
	}
	public class CustomDataSender<T> : ICustomDataSender where T : unmanaged
	{
		private struct CustomPacket
		{
			public pPlayer Player;

			public T Data;

			public CustomPacket(SNet_Player player, T data)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				Player = default(pPlayer);
				((pPlayer)(ref Player)).SetPlayer(player);
				Data = data;
			}
		}

		private const string EventPrefix = "OptionalData";

		private static readonly byte[] s_packetBuffer = new byte[Marshal.SizeOf<pPlayer>() + Marshal.SizeOf<T>()];

		private readonly ModInfo _parent;

		private readonly string _eventName;

		private readonly Func<SNet_Player, T>? _defaultProvider;

		private protected Action<SNet_Player, T> ReceiveEvent;

		private readonly Dictionary<ulong, SNet_Player> _sentBots = new Dictionary<ulong, SNet_Player>();

		private readonly SenderMode _mode;

		private CustomDataSender(ModInfo parent, string id, Action<SNet_Player, T> receiveEvent, Func<SNet_Player, T>? defaultProvider, SenderMode senderMode)
			: this(parent, id, defaultProvider, senderMode)
		{
			ReceiveEvent = receiveEvent;
		}

		private protected CustomDataSender(ModInfo parent, string id, Func<SNet_Player, T>? defaultProvider, SenderMode senderMode)
		{
			_parent = parent;
			_eventName = "OptionalData" + id;
			_defaultProvider = defaultProvider;
			ReceiveEvent = null;
			_mode = senderMode;
			NetworkAPI.RegisterFreeSizedEvent(_eventName, (Action<ulong, byte[]>)ReceiveDataRaw);
		}

		internal static CustomDataSender<T> CreateDataSender(ModInfo parent, int index, Action<SNet_Player, T> receiveEvent, Func<SNet_Player, T>? defaultProvider, SenderMode senderMode)
		{
			return new CustomDataSender<T>(parent, $"{parent.Hash:x}_{index}", receiveEvent, defaultProvider, senderMode);
		}

		public void SendDefaultData(SNet_Player player)
		{
			if (_defaultProvider == null)
			{
				return;
			}
			if (!player.IsLocal && !player.IsBot)
			{
				if (!_mode.HasFlag(SenderMode.OtherPlayers))
				{
					return;
				}
				SendData(SNet.LocalPlayer, _defaultProvider(SNet.LocalPlayer), player, (SNet_ChannelType)0);
				if (!SNet.IsMaster)
				{
					return;
				}
				{
					foreach (SNet_Player value in _sentBots.Values)
					{
						SendData(value, _defaultProvider(value), player, (SNet_ChannelType)0);
					}
					return;
				}
			}
			if (_mode.HasFlag(SenderMode.Self) && player.IsLocal)
			{
				ReceiveEvent(player, _defaultProvider(player));
			}
			else if (_mode.HasFlag(SenderMode.Bots) && player.IsBot && SNet.IsMaster && _sentBots.TryAdd(player.Lookup, player))
			{
				SendData(player, _defaultProvider(player), null, (SNet_ChannelType)0);
			}
		}

		public void SendData(T data, SNet_Player? target = null, SNet_ChannelType channelType = (SNet_ChannelType)0)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			SendData(SNet.LocalPlayer, data, target, channelType);
		}

		public void SendData(SNet_Player player, T data, SNet_Player? target = null, SNet_ChannelType channelType = (SNet_ChannelType)0)
		{
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			if (!_mode.HasFlag(SenderMode.Bots) && player.IsBot)
			{
				return;
			}
			CustomPacket packet = new CustomPacket(player, data);
			if ((Object)(object)target == (Object)null)
			{
				if (_mode.HasFlag(SenderMode.Self) && player.IsLocal)
				{
					ReceiveEvent(player, packet.Data);
				}
				if (player.IsBot)
				{
					ReceiveEvent(player, packet.Data);
				}
				if (!_mode.HasFlag(SenderMode.OtherPlayers))
				{
					return;
				}
				if (!player.IsLocal && !player.IsBot)
				{
					ReceiveEvent(player, packet.Data);
				}
				{
					foreach (SNet_Player otherPlayer in _parent.OtherPlayers)
					{
						NetworkAPI.InvokeFreeSizedEvent(_eventName, ConvertPacket(packet), otherPlayer, channelType);
					}
					return;
				}
			}
			if (target.IsLocal)
			{
				if (_mode.HasFlag(SenderMode.Self))
				{
					ReceiveEvent(player, packet.Data);
				}
				return;
			}
			if (target.IsBot)
			{
				if (SNet.IsMaster)
				{
					ReceiveEvent(player, packet.Data);
					return;
				}
				target = SNet.Master;
			}
			if (ModManager.HasMod(target, _parent))
			{
				NetworkAPI.InvokeFreeSizedEvent(_eventName, ConvertPacket(packet), target, channelType);
			}
		}

		private static byte[] ConvertPacket(CustomPacket packet)
		{
			MemoryMarshal.Write(s_packetBuffer, in packet);
			return s_packetBuffer;
		}

		private void ReceiveDataRaw(ulong _, byte[] bytes)
		{
			ReceiveData(MemoryMarshal.Read<CustomPacket>(bytes));
		}

		private void ReceiveData(CustomPacket packet)
		{
			SNet_Player arg = default(SNet_Player);
			if (((pPlayer)(ref packet.Player)).TryGetPlayer(ref arg))
			{
				ReceiveEvent(arg, packet.Data);
			}
		}

		void ICustomDataSender.OnPlayerLeft(SNet_Player player)
		{
			OnPlayerLeft(player);
		}

		private protected virtual void OnPlayerLeft(SNet_Player player)
		{
			_sentBots.Remove(player.Lookup);
		}

		void ICustomDataSender.OnLobbyLeft()
		{
			OnLobbyLeft();
		}

		private protected virtual void OnLobbyLeft()
		{
			_sentBots.Clear();
		}
	}
	internal static class DinoLogger
	{
		private static ManualLogSource logger = Logger.CreateLogSource("OptionalNetworkAPI");

		public static void Log(string format, params object[] args)
		{
			Log(string.Format(format, args));
		}

		public static void Log(string str)
		{
			if (logger != null)
			{
				logger.Log((LogLevel)8, (object)str);
			}
		}

		public static void Warning(string format, params object[] args)
		{
			Warning(string.Format(format, args));
		}

		public static void Warning(string str)
		{
			if (logger != null)
			{
				logger.Log((LogLevel)4, (object)str);
			}
		}

		public static void Error(string format, params object[] args)
		{
			Error(string.Format(format, args));
		}

		public static void Error(string str)
		{
			if (logger != null)
			{
				logger.Log((LogLevel)2, (object)str);
			}
		}

		public static void Debug(string format, params object[] args)
		{
			Debug(string.Format(format, args));
		}

		public static void Debug(string str)
		{
			if (logger != null)
			{
				logger.Log((LogLevel)32, (object)str);
			}
		}
	}
	[BepInPlugin("Dinorush.OptionalNetworkAPI", "OptionalNetworkAPI", "1.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	internal sealed class EntryPoint : BasePlugin
	{
		public const string MODNAME = "OptionalNetworkAPI";

		public override void Load()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			ModSetNetworkManager.Init();
			new Harmony("OptionalNetworkAPI").PatchAll();
			((BasePlugin)this).Log.LogMessage((object)"Loaded OptionalNetworkAPI");
		}
	}
	public class ModInfo
	{
		public readonly string Name;

		internal readonly ulong Hash;

		public readonly long Mask;

		private readonly List<ICustomDataSender> _customDataSenders = new List<ICustomDataSender>();

		private readonly List<ICustomDataManager> _customDataManagers = new List<ICustomDataManager>();

		private readonly Dictionary<ulong, SNet_Player> _bots = new Dictionary<ulong, SNet_Player>(3);

		private readonly Dictionary<ulong, SNet_Player> _others = new Dictionary<ulong, SNet_Player>(3);

		private readonly Dictionary<ulong, SNet_Player> _players = new Dictionary<ulong, SNet_Player>(4);

		private static long s_nextMask = 2L;

		public bool MasterHasMod { get; private set; }

		public IReadOnlyCollection<SNet_Player> Bots => _bots.Values;

		public IReadOnlyCollection<SNet_Player> OtherPlayers => _others.Values;

		public IReadOnlyCollection<SNet_Player> Players => _players.Values;

		public event Action<SNet_Player>? OnPlayerJoined;

		public event Action<SNet_Player, bool>? OnPlayerModSet;

		public event Action<SNet_Player>? OnPlayerLeft;

		public event Action<PlayerAgent>? OnPlayerSpawned;

		public event Action<PlayerAgent>? OnPlayerDespawned;

		public event Action<bool>? OnMasterModSet;

		public event Action? OnLobbyLeft;

		private ModInfo(string name, ulong hash, long mask)
		{
			Name = name;
			Hash = hash;
			Mask = mask;
		}

		public CustomDataSender<T> AddCustomDataSender<T>(Action<SNet_Player, T> receiveEvent, Func<SNet_Player, T>? defaultProvider = null, SenderMode mode = SenderMode.OtherPlayers) where T : unmanaged
		{
			CustomDataSender<T> customDataSender = CustomDataSender<T>.CreateDataSender(this, _customDataSenders.Count, receiveEvent, defaultProvider, mode);
			_customDataSenders.Add(customDataSender);
			return customDataSender;
		}

		public CustomDataManager<T> AddCustomDataManager<T>(Func<SNet_Player, T> defaultProvider) where T : unmanaged
		{
			CustomDataManager<T> customDataManager = CustomDataManager<T>.CreateDataManager(this, _customDataSenders.Count, defaultProvider);
			_customDataSenders.Add(customDataManager);
			_customDataManagers.Add(customDataManager);
			return customDataManager;
		}

		internal void InvokePlayerJoined(PlayerInfo info)
		{
			this.OnPlayerJoined?.Invoke(info.Owner);
		}

		internal void InvokePlayerModSet(PlayerInfo info)
		{
			bool flag = info.HasMod(Mask);
			if ((flag && !_players.TryAdd(info.Lookup, info.Owner)) || (!flag && !_players.Remove(info.Lookup)))
			{
				return;
			}
			if (!info.IsLocal)
			{
				Dictionary<ulong, SNet_Player> dictionary = (info.IsBot ? _bots : _others);
				if (flag)
				{
					dictionary.Add(info.Lookup, info.Owner);
				}
				else
				{
					dictionary.Remove(info.Lookup);
				}
			}
			this.OnPlayerModSet?.Invoke(info.Owner, flag);
			if (flag)
			{
				foreach (ICustomDataSender customDataSender in _customDataSenders)
				{
					customDataSender.SendDefaultData(info.Owner);
				}
				return;
			}
			foreach (ICustomDataManager customDataManager in _customDataManagers)
			{
				customDataManager.OnPlayerModRemoved(info.Owner);
			}
		}

		internal void InvokePlayerLeft(PlayerInfo info)
		{
			(info.IsBot ? _bots : _others).Remove(info.Lookup);
			this.OnPlayerLeft?.Invoke(info.Owner);
			foreach (ICustomDataSender customDataSender in _customDataSenders)
			{
				customDataSender.OnPlayerLeft(info.Owner);
			}
		}

		internal void InvokePlayerSpawned(PlayerInfo info)
		{
			this.OnPlayerSpawned?.Invoke(info.Player);
		}

		internal void InvokePlayerDespawned(PlayerInfo info)
		{
			this.OnPlayerDespawned?.Invoke(info.Player);
		}

		internal void InvokeMasterSet(PlayerInfo info)
		{
			MasterHasMod = info.HasMod(Mask);
			this.OnMasterModSet?.Invoke(MasterHasMod);
		}

		internal void InvokeLobbyLeft()
		{
			_bots.Clear();
			_others.Clear();
			_players.Clear();
			MasterHasMod = false;
			this.OnLobbyLeft?.Invoke();
			foreach (ICustomDataSender customDataSender in _customDataSenders)
			{
				customDataSender.OnLobbyLeft();
			}
		}

		private static long GetNextMask()
		{
			if (s_nextMask == 0L)
			{
				return 0L;
			}
			long num = s_nextMask & -2;
			if ((s_nextMask & 1) != 0L)
			{
				if (num == long.MinValue)
				{
					s_nextMask = 0L;
					return num | 1;
				}
				s_nextMask = (num << 1) | 1;
				return num | 1;
			}
			if (num == long.MinValue)
			{
				s_nextMask = 3L;
				return num;
			}
			s_nextMask = num << 1;
			return num;
		}

		internal static ModInfo CreateModInfo(string name, ulong hash)
		{
			return new ModInfo(name, hash, GetNextMask());
		}
	}
	public static class OptionalNetworkAPI
	{
		public static PlayerInfo? MasterInfo => ModManager.MasterInfo;

		public static PlayerInfo? LocalInfo => ModManager.LocalInfo;

		public static IReadOnlyCollection<ModInfo> Mods => ModManager.Mods;

		public static ModInfo RegisterMod(string name)
		{
			return ModManager.AddMod(name);
		}

		public static bool HasMod(string name)
		{
			return ModManager.HasMod(name);
		}

		public static bool HasMod(SNet_Player player, string name)
		{
			return ModManager.HasMod(player, name);
		}

		public static bool HasMod(SNet_Player player, ModInfo modInfo)
		{
			return ModManager.HasMod(player, modInfo);
		}

		public static bool TryGetModInfo(string name, [MaybeNullWhen(false)] out ModInfo modInfo)
		{
			return ModManager.TryGetModInfo(name, out modInfo);
		}

		public static bool TryGetPlayerInfo(SNet_Player player, [MaybeNullWhen(false)] out PlayerInfo playerInfo)
		{
			return ModManager.TryGetPlayerInfo(player, out playerInfo);
		}
	}
	public class PlayerInfo
	{
		public (long mask1, long mask2) ModMask { get; internal set; } = (mask1: 0L, mask2: 1L);

		public ulong Lookup { get; }

		public SNet_Player Owner { get; }

		public PlayerAgent? Player { get; internal set; }

		public bool IsLocal { get; }

		public bool IsBot { get; }

		public bool IsRemote { get; }

		internal PlayerInfo(SNet_Player owner)
		{
			Owner = owner;
			Lookup = Owner.Lookup;
			IsLocal = Owner.IsLocal;
			IsBot = Owner.IsBot;
			IsRemote = !Owner.IsBot && !Owner.IsLocal;
		}

		public bool HasMod(ModInfo modInfo)
		{
			return HasMod(modInfo.Mask);
		}

		public bool HasMod(long modMask)
		{
			if ((modMask & 1) == 0L)
			{
				return (ModMask.mask1 & modMask) != 0;
			}
			return (ModMask.mask2 & modMask) != 1;
		}
	}
}
namespace OptionalNetworking.Patches
{
	[HarmonyPatch]
	internal static class NetworkPatches
	{
		[HarmonyPatch(typeof(SNet_SessionHub), "AddPlayerToSession")]
		[HarmonyWrapSafe]
		[HarmonyPostfix]
		private static void Post_AddPlayer(SNet_Player player)
		{
			ModManager.OnAddPlayer(player);
			ModSetNetworkManager.OnAddPlayer(player);
		}

		[HarmonyPatch(typeof(SNet_SessionHub), "OnLeftLobby")]
		[HarmonyWrapSafe]
		[HarmonyPostfix]
		private static void Post_RemovePlayer(SNet_Player player)
		{
			ModManager.OnRemovePlayer(player);
		}

		[HarmonyPatch(typeof(SNet_SyncManager), "OnFoundMaster")]
		[HarmonyPatch(typeof(SNet_SyncManager), "OnFoundNewMasterDuringMigration")]
		[HarmonyWrapSafe]
		[HarmonyPostfix]
		private static void Post_FoundMaster()
		{
			ModManager.OnMasterSet();
		}

		[HarmonyPatch(typeof(PlayerSync), "OnSpawn")]
		[HarmonyWrapSafe]
		[HarmonyPostfix]
		private static void Post_PlayerSpawn(PlayerSync __instance)
		{
			ModManager.OnPlayerSpawned(__instance.m_agent);
		}

		[HarmonyPatch(typeof(PlayerAgent), "OnDespawn")]
		[HarmonyWrapSafe]
		[HarmonyPrefix]
		private static void Pre_PlayerDespawn(PlayerAgent __instance)
		{
			ModManager.OnPlayerDespawned(__instance);
		}

		[HarmonyPatch(typeof(SNet_SessionHub), "LeaveHub")]
		[HarmonyWrapSafe]
		[HarmonyPostfix]
		private static void Post_LeaveHub()
		{
			ModManager.OnLobbyLeft();
		}
	}
}
namespace OptionalNetworking.Managers
{
	internal static class ModManager
	{
		private static readonly Dictionary<ulong, PlayerInfo> _bots = new Dictionary<ulong, PlayerInfo>();

		private static readonly Dictionary<ulong, PlayerInfo> _players = new Dictionary<ulong, PlayerInfo>();

		private static readonly Dictionary<string, ModInfo> _modInfos = new Dictionary<string, ModInfo>();

		private static readonly Dictionary<ulong, ModInfo> _hashToMod = new Dictionary<ulong, ModInfo>();

		private static (long mask1, long mask2) _localMask = (mask1: 0L, mask2: 1L);

		public static PlayerInfo? MasterInfo { get; private set; }

		public static PlayerInfo? LocalInfo { get; private set; }

		public static IReadOnlyCollection<ModInfo> Mods => _modInfos.Values;

		internal static IReadOnlyCollection<ulong> ModHashes => _hashToMod.Keys;

		public static bool HasMod(string name)
		{
			return _modInfos.ContainsKey(name);
		}

		public static bool HasMod(SNet_Player player, string name)
		{
			if (!_players.TryGetValue(player.Lookup, out PlayerInfo value) || !_modInfos.TryGetValue(name, out ModInfo value2))
			{
				return false;
			}
			return value.HasMod(value2);
		}

		public static bool TryGetModInfo(string name, [MaybeNullWhen(false)] out ModInfo modInfo)
		{
			return _modInfos.TryGetValue(name, out modInfo);
		}

		public static bool TryGetPlayerInfo(SNet_Player player, [MaybeNullWhen(false)] out PlayerInfo playerInfo)
		{
			return _players.TryGetValue(player.Lookup, out playerInfo);
		}

		private static void CombineMask(ref (long mask1, long mask2) mask, long maskToAdd)
		{
			if ((maskToAdd & 1) == 0L)
			{
				mask.mask1 |= maskToAdd;
			}
			else
			{
				mask.mask2 |= maskToAdd;
			}
		}

		internal static (long mask1, long mask2) HashesToMask(ulong[] hashes)
		{
			(long, long) mask = (0L, 1L);
			foreach (ulong key in hashes)
			{
				if (_hashToMod.TryGetValue(key, out ModInfo value))
				{
					CombineMask(ref mask, value.Mask);
				}
			}
			return mask;
		}

		internal static ModInfo AddMod(string name)
		{
			if (_modInfos.ContainsKey(name))
			{
				throw new ArgumentException("Cannot register duplicate mod name " + name + ".");
			}
			ulong num = MurmurHash2.Hash(name);
			if (_hashToMod.TryGetValue(num, out ModInfo value))
			{
				throw new ArgumentException($"Hash for mod name {name} collides with {value.Name}, can't register!");
			}
			ModInfo modInfo = ModInfo.CreateModInfo(name, num);
			if (modInfo.Mask == 0L)
			{
				throw new IndexOutOfRangeException("Out of internal space (126 mods registered), unable to register mod " + name + ".");
			}
			_modInfos.Add(name, modInfo);
			_hashToMod.Add(num, modInfo);
			CombineMask(ref _localMask, modInfo.Mask);
			return modInfo;
		}

		internal static bool HasMod(SNet_Player? player, ModInfo modInfo)
		{
			if ((Object)(object)player == (Object)null || !_players.TryGetValue(player.Lookup, out PlayerInfo value))
			{
				return false;
			}
			return value.HasMod(modInfo);
		}

		internal static void OnAddPlayer(SNet_Player player)
		{
			if (_players.ContainsKey(player.Lookup))
			{
				return;
			}
			PlayerInfo playerInfo = new PlayerInfo(player);
			_players.Add(player.Lookup, playerInfo);
			foreach (ModInfo value in _modInfos.Values)
			{
				value.InvokePlayerJoined(playerInfo);
			}
			if (playerInfo.IsLocal)
			{
				LocalInfo = playerInfo;
				OnPlayerModSet(player, _localMask);
			}
			else if (playerInfo.IsBot)
			{
				_bots.Add(playerInfo.Lookup, playerInfo);
				if (MasterInfo != null)
				{
					OnPlayerModSet(player, MasterInfo.ModMask);
				}
			}
		}

		internal static void OnRemovePlayer(SNet_Player player)
		{
			if (!_players.Remove(player.Lookup, out PlayerInfo value))
			{
				return;
			}
			_bots.Remove(player.Lookup);
			if (value.IsLocal)
			{
				LocalInfo = null;
			}
			foreach (ModInfo value2 in _modInfos.Values)
			{
				value2.InvokePlayerLeft(value);
			}
		}

		internal static void OnPlayerSpawned(PlayerAgent agent)
		{
			if (!_players.TryGetValue(agent.Owner.Lookup, out PlayerInfo value))
			{
				return;
			}
			value.Player = agent;
			foreach (ModInfo value2 in _modInfos.Values)
			{
				value2.InvokePlayerSpawned(value);
			}
		}

		internal static void OnPlayerDespawned(PlayerAgent agent)
		{
			if (!_players.TryGetValue(agent.Owner.Lookup, out PlayerInfo value) || (Object)(object)value.Player == (Object)null)
			{
				return;
			}
			foreach (ModInfo value2 in _modInfos.Values)
			{
				value2.InvokePlayerDespawned(value);
			}
			value.Player = null;
		}

		internal static void OnPlayerModSet(SNet_Player player, (long mask1, long mask2) modMask)
		{
			if (!_players.TryGetValue(player.Lookup, out PlayerInfo value))
			{
				OnAddPlayer(player);
				value = _players[player.Lookup];
			}
			value.ModMask = modMask;
			foreach (ModInfo value2 in _modInfos.Values)
			{
				value2.InvokePlayerModSet(value);
			}
		}

		internal static void OnLobbyLeft()
		{
			foreach (ModInfo value in _modInfos.Values)
			{
				value.InvokeLobbyLeft();
			}
			_players.Clear();
			MasterInfo = null;
			LocalInfo = null;
		}

		internal static void OnMasterSet()
		{
			if (!SNet.HasMaster)
			{
				MasterInfo = null;
				return;
			}
			if (!_players.TryGetValue(SNet.Master.Lookup, out PlayerInfo value))
			{
				OnAddPlayer(SNet.Master);
				value = _players[SNet.Master.Lookup];
			}
			else if (MasterInfo == value)
			{
				return;
			}
			MasterInfo = value;
			foreach (ModInfo value2 in _modInfos.Values)
			{
				value2.InvokeMasterSet(value);
			}
			foreach (PlayerInfo value3 in _bots.Values)
			{
				OnPlayerModSet(value3.Owner, MasterInfo.ModMask);
			}
		}
	}
	internal static class ModSetNetworkManager
	{
		private const string ModSetEvent = "OptionalNetworkAPIModSet";

		public static void Init()
		{
			NetworkAPI.RegisterFreeSizedEvent("OptionalNetworkAPIModSet", (Action<ulong, byte[]>)ReceiveModSet);
		}

		internal static void OnAddPlayer(SNet_Player player)
		{
			if (player.IsBot || player.IsLocal)
			{
				return;
			}
			byte[] array = new byte[ModManager.ModHashes.Count * 8];
			int num = 0;
			foreach (ulong modHash in ModManager.ModHashes)
			{
				BinaryPrimitives.WriteUInt64BigEndian(array.AsSpan(num++ * 8, 8), modHash);
			}
			NetworkAPI.InvokeFreeSizedEvent("OptionalNetworkAPIModSet", array, player, (SNet_ChannelType)0);
		}

		private static void ReceiveModSet(ulong lookup, byte[] data)
		{
			SNet_Player player = default(SNet_Player);
			if (SNet.TryGetPlayer(lookup, ref player))
			{
				ulong[] array = new ulong[data.Length / 8];
				int num = 0;
				for (int i = 0; i < data.Length; i += 8)
				{
					array[num++] = BinaryPrimitives.ReadUInt64BigEndian(data.AsSpan(i, 8));
				}
				ModManager.OnPlayerModSet(player, ModManager.HashesToMask(array));
			}
		}
	}
}
namespace OptionalNetworking.Hashing
{
	internal static class MurmurHash2
	{
		private const ulong seed = 3314489978uL;

		private const ulong m = 14313749767032793493uL;

		private const int r = 47;

		public static ulong Hash(string data)
		{
			return Hash(Encoding.UTF8.GetBytes(data));
		}

		public static ulong Hash(byte[] data)
		{
			return Hash(data, 3314489978uL);
		}

		public static ulong Hash(byte[] data, ulong seed)
		{
			int num = data.Length;
			if (num == 0)
			{
				return 0uL;
			}
			ulong num2 = seed ^ (ulong)(num * -4132994306676758123L);
			int num3 = 0;
			while (num >= 8)
			{
				ulong num4 = (ulong)(data[num3++] | (data[num3++] << 8) | (data[num3++] << 16) | (data[num3++] << 24) | data[num3++] | (data[num3++] << 8) | (data[num3++] << 16) | (data[num3++] << 24));
				num4 *= 14313749767032793493uL;
				num4 ^= num4 >> 47;
				num4 *= 14313749767032793493uL;
				num2 ^= num4;
				num2 *= 14313749767032793493uL;
				num -= 8;
			}
			switch (num)
			{
			case 7:
				num2 ^= (ulong)(data[num3++] | (data[num3++] << 8) | (data[num3++] << 16) | (data[num3++] << 24) | data[num3++] | (data[num3++] << 8) | (data[num3] << 16));
				num2 *= 14313749767032793493uL;
				break;
			case 6:
				num2 ^= (ulong)(data[num3++] | (data[num3++] << 8) | (data[num3++] << 16) | (data[num3++] << 24) | data[num3++] | (data[num3++] << 8));
				num2 *= 14313749767032793493uL;
				break;
			case 5:
				num2 ^= (ulong)(data[num3++] | (data[num3++] << 8) | (data[num3++] << 16) | (data[num3++] << 24) | data[num3++]);
				num2 *= 14313749767032793493uL;
				break;
			case 4:
				num2 ^= (ulong)(data[num3++] | (data[num3++] << 8) | (data[num3++] << 16) | (data[num3++] << 24));
				num2 *= 14313749767032793493uL;
				break;
			case 3:
				num2 ^= (ulong)(data[num3++] | (data[num3++] << 8) | (data[num3++] << 16));
				num2 *= 14313749767032793493uL;
				break;
			case 2:
				num2 ^= (ulong)(data[num3++] | (data[num3++] << 8));
				num2 *= 14313749767032793493uL;
				break;
			case 1:
				num2 ^= data[num3++];
				num2 *= 14313749767032793493uL;
				break;
			}
			num2 ^= num2 >> 47;
			num2 *= 14313749767032793493uL;
			return num2 ^ (num2 >> 47);
		}
	}
}