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