Decompiled source of PEAKNetworkingLibrary v1.0.7

off_grid.NetworkingLibrary.dll

Decompiled 2 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using NetworkingLibrary;
using NetworkingLibrary.Features;
using NetworkingLibrary.Modules;
using NetworkingLibrary.Services;
using Photon.Pun;
using Photon.Realtime;
using Steamworks;
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("DAa")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Networking library for mods using Steam instead of PUN.")]
[assembly: AssemblyFileVersion("1.0.7.0")]
[assembly: AssemblyInformationalVersion("1.0.7")]
[assembly: AssemblyProduct("NetworkingLibrary")]
[assembly: AssemblyTitle("off_grid.NetworkingLibrary")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.7.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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;
		}
	}
}
public class SteamCallbackPump : MonoBehaviour
{
	private void Update()
	{
		try
		{
			SteamAPI.RunCallbacks();
		}
		catch (Exception arg)
		{
			try
			{
				Net.Logger.LogError((object)$"SteamAPI.RunCallbacks error: {arg}");
			}
			catch
			{
			}
		}
	}
}
public class UnityMainThreadDispatcher : MonoBehaviour
{
	[CompilerGenerated]
	private sealed class <EnqueueDelayed>d__4 : IEnumerator<object>, IEnumerator, IDisposable
	{
		private int <>1__state;

		private object <>2__current;

		public float d;

		public Action a;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <EnqueueDelayed>d__4(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				<>2__current = (object)new WaitForSeconds(d);
				<>1__state = 1;
				return true;
			case 1:
				<>1__state = -1;
				lock (queue)
				{
					queue.Enqueue(a);
				}
				return false;
			}
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	private static UnityMainThreadDispatcher? instance;

	private static readonly Queue<Action> queue = new Queue<Action>();

	public static UnityMainThreadDispatcher Instance()
	{
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Expected O, but got Unknown
		if ((Object)(object)instance == (Object)null)
		{
			GameObject val = GameObject.Find("UnityMainThreadDispatcher");
			if ((Object)(object)val == (Object)null)
			{
				val = new GameObject("UnityMainThreadDispatcher");
				Object.DontDestroyOnLoad((Object)(object)val);
				instance = val.AddComponent<UnityMainThreadDispatcher>();
			}
			else
			{
				instance = val.GetComponent<UnityMainThreadDispatcher>() ?? val.AddComponent<UnityMainThreadDispatcher>();
			}
		}
		return instance;
	}

	public void Enqueue(Action a, float delaySeconds = 0f)
	{
		if (delaySeconds <= 0f)
		{
			lock (queue)
			{
				queue.Enqueue(a);
				return;
			}
		}
		((MonoBehaviour)this).StartCoroutine(EnqueueDelayed(a, delaySeconds));
	}

	[IteratorStateMachine(typeof(<EnqueueDelayed>d__4))]
	private IEnumerator EnqueueDelayed(Action a, float d)
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <EnqueueDelayed>d__4(0)
		{
			a = a,
			d = d
		};
	}

	private void Update()
	{
		while (true)
		{
			Action action = null;
			lock (queue)
			{
				if (queue.Count <= 0)
				{
					break;
				}
				action = queue.Dequeue();
			}
			try
			{
				action?.Invoke();
			}
			catch (Exception arg)
			{
				Debug.LogError((object)$"Dispatcher action error: {arg}");
			}
		}
	}
}
namespace NetworkingLibrary
{
	[BepInPlugin("off_grid.NetworkingLibrary", "NetworkingLibrary", "1.0.7")]
	public class Net : BaseUnityPlugin
	{
		internal static Harmony Harmony = new Harmony("off_grid.NetworkingLibrary");

		public ConfigFile config;

		public static Net Instance { get; private set; } = null;


		internal static ManualLogSource Logger { get; private set; } = null;


		public static INetworkingService? Service { get; private set; }

		private void OnDestroy()
		{
			Service?.Shutdown();
		}

		private void Awake()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			Logger = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			FileManager.InitializeConfig();
			Harmony.PatchAll();
			Service = NetworkingServiceFactory.CreateDefaultService();
			Service.Initialize();
			GameObject val = new GameObject("NetworkingLibrary.Poller");
			((Object)val.AddComponent<NetworkingPoller>()).hideFlags = (HideFlags)61;
			Object.DontDestroyOnLoad((Object)(object)val);
			Logger.LogInfo((object)"NetworkingLibrary v1.0.7 has fully loaded!");
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "off_grid.NetworkingLibrary";

		public const string PLUGIN_NAME = "NetworkingLibrary";

		public const string PLUGIN_VERSION = "1.0.7";
	}
}
namespace NetworkingLibrary.Services
{
	public enum ReliableType
	{
		Unreliable,
		Reliable,
		UnreliableNoDelay
	}
	public interface INetworkingService
	{
		bool IsInitialized { get; }

		bool InLobby { get; }

		ulong HostSteamId64 { get; }

		string HostIdString { get; }

		bool IsHost { get; }

		Func<Message, ulong, bool>? IncomingValidator { get; set; }

		event Action? LobbyCreated;

		event Action? LobbyEntered;

		event Action? LobbyLeft;

		event Action<ulong>? PlayerEntered;

		event Action<ulong>? PlayerLeft;

		event Action<string[]>? LobbyDataChanged;

		event Action<ulong, string[]>? PlayerDataChanged;

		ulong GetLocalSteam64();

		ulong[] GetLobbyMemberSteamIds();

		void Initialize();

		void Shutdown();

		void CreateLobby(int maxPlayers = 8);

		void JoinLobby(ulong lobbySteamId64);

		void LeaveLobby();

		void InviteToLobby(ulong steamId64);

		IDisposable RegisterNetworkObject(object instance, uint modId, int mask = 0);

		IDisposable RegisterNetworkType(Type type, uint modId, int mask = 0);

		void DeregisterNetworkObject(object instance, uint modId, int mask = 0);

		void DeregisterNetworkType(Type type, uint modId, int mask = 0);

		void RPC(uint modId, string methodName, ReliableType reliable, params object[] parameters);

		void RPCTarget(uint modId, string methodName, ulong targetSteamId64, ReliableType reliable, params object[] parameters);

		void RPCToHost(uint modId, string methodName, ReliableType reliable, params object[] parameters);

		void RegisterLobbyDataKey(string key);

		void SetLobbyData(string key, object value);

		T GetLobbyData<T>(string key);

		void RegisterPlayerDataKey(string key);

		void SetPlayerData(string key, object value);

		T GetPlayerData<T>(ulong steamId64, string key);

		void PollReceive();

		void RegisterModSigner(uint modId, Func<byte[], byte[]> signerDelegate);

		void RegisterModPublicKey(uint modId, RSAParameters pub);
	}
	public class NetworkingPoller : MonoBehaviour
	{
		private void Update()
		{
			try
			{
				Net.Service?.PollReceive();
			}
			catch (Exception arg)
			{
				Net.Logger.LogError((object)$"NetworkingPollerDebug PollReceive error: {arg}");
			}
		}

		private void Awake()
		{
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
		}

		private void OnDestroy()
		{
		}
	}
	public static class NetworkingServiceFactory
	{
		public static INetworkingService CreateDefaultService()
		{
			if (SteamAPI.IsSteamRunning())
			{
				Net.Logger.LogInfo((object)"Steam type present. Creating SteamNetworkingService.");
				return new SteamNetworkingService();
			}
			Net.Logger.LogInfo((object)"Steam not available. Creating OfflineNetworkingService.");
			return new OfflineNetworkingService();
		}
	}
	public class OfflineNetworkingService : INetworkingService
	{
		private class Token : IDisposable
		{
			private Action on;

			public Token(Action on)
			{
				this.on = on;
			}

			public void Dispose()
			{
				on();
			}
		}

		private class SlidingWindowRateLimiter
		{
			private readonly int limit;

			private readonly TimeSpan window;

			private readonly Queue<DateTime> q = new Queue<DateTime>();

			public SlidingWindowRateLimiter(int limit, TimeSpan window)
			{
				this.limit = limit;
				this.window = window;
			}

			public bool IncomingAllowed()
			{
				DateTime utcNow = DateTime.UtcNow;
				while (q.Count > 0 && utcNow - q.Peek() > window)
				{
					q.Dequeue();
				}
				if (q.Count >= limit)
				{
					return false;
				}
				q.Enqueue(utcNow);
				return true;
			}
		}

		private class MessageHandler
		{
			public object Target;

			public MethodInfo Method;

			public ParameterInfo[] Parameters;

			public bool TakesInfo;

			public int Mask;
		}

		private readonly Dictionary<uint, Dictionary<string, List<MessageHandler>>> rpcs = new Dictionary<uint, Dictionary<string, List<MessageHandler>>>();

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

		private readonly Dictionary<ulong, Dictionary<string, string>> perPlayerData = new Dictionary<ulong, Dictionary<string, string>>();

		private readonly HashSet<string> lobbyKeys = new HashSet<string>();

		private readonly HashSet<string> playerKeys = new HashSet<string>();

		private readonly SlidingWindowRateLimiter rateLimiter = new SlidingWindowRateLimiter(100, TimeSpan.FromSeconds(1.0));

		private readonly Dictionary<ulong, byte[]> perPeerSymmetricKey = new Dictionary<ulong, byte[]>();

		private byte[]? globalSharedSecret;

		private HMACSHA256? globalHmac;

		private long _nextMessageId;

		private bool offlineIsHost;

		public bool IsInitialized { get; private set; }

		public bool InLobby { get; private set; }

		public ulong HostSteamId64 { get; private set; } = 1uL;


		public string HostIdString => HostSteamId64.ToString();

		public Func<Message, ulong, bool>? IncomingValidator { get; set; }

		public ulong LocalSteamId { get; private set; } = 1000uL;


		public bool IsHost => offlineIsHost;

		public event Action? LobbyCreated;

		public event Action? LobbyEntered;

		public event Action? LobbyLeft;

		public event Action<ulong>? PlayerEntered;

		public event Action<ulong>? PlayerLeft;

		public event Action<string[]>? LobbyDataChanged;

		public event Action<ulong, string[]>? PlayerDataChanged;

		public ulong GetLocalSteam64()
		{
			return 0uL;
		}

		public ulong[] GetLobbyMemberSteamIds()
		{
			return Array.Empty<ulong>();
		}

		public void RegisterModSigner(uint modId, Func<byte[], byte[]> signerDelegate)
		{
			Net.Logger.LogWarning((object)"This feature is currently not setup in the offline system");
		}

		public void RegisterModPublicKey(uint modId, RSAParameters pub)
		{
			Net.Logger.LogWarning((object)"This feature is currently not setup in the offline system");
		}

		private ulong NextMessageId()
		{
			return (ulong)Interlocked.Increment(ref _nextMessageId);
		}

		public void Initialize()
		{
			if (!IsInitialized)
			{
				RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create();
				byte[] array = new byte[32];
				randomNumberGenerator.GetBytes(array);
				perPeerSymmetricKey[LocalSteamId] = array;
				IsInitialized = true;
			}
		}

		public void Shutdown()
		{
			IsInitialized = false;
			rpcs.Clear();
			perPeerSymmetricKey.Clear();
			globalHmac?.Dispose();
			globalHmac = null;
		}

		public void CreateLobby(int maxPlayers = 8)
		{
			InLobby = true;
			HostSteamId64 = LocalSteamId;
			perPlayerData[LocalSteamId] = new Dictionary<string, string>();
			this.LobbyCreated?.Invoke();
			this.LobbyEntered?.Invoke();
			offlineIsHost = true;
		}

		public void JoinLobby(ulong lobbySteamId64)
		{
			InLobby = true;
			HostSteamId64 = lobbySteamId64;
			if (!perPlayerData.ContainsKey(LocalSteamId))
			{
				perPlayerData[LocalSteamId] = new Dictionary<string, string>();
			}
			this.LobbyEntered?.Invoke();
			this.PlayerEntered?.Invoke(LocalSteamId);
		}

		public void LeaveLobby()
		{
			InLobby = false;
			this.LobbyLeft?.Invoke();
			offlineIsHost = false;
		}

		public void InviteToLobby(ulong steamId64)
		{
			this.PlayerEntered?.Invoke(steamId64);
		}

		public IDisposable RegisterNetworkObject(object instance, uint modId, int mask = 0)
		{
			object instance2 = instance;
			if (instance2 == null)
			{
				throw new ArgumentNullException("instance");
			}
			Type type = instance2.GetType();
			int num = 0;
			MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				CustomRPCAttribute[] array = methodInfo.GetCustomAttributes(inherit: false).OfType<CustomRPCAttribute>().ToArray();
				if (array.Length != 0)
				{
					if (!rpcs.ContainsKey(modId))
					{
						rpcs[modId] = new Dictionary<string, List<MessageHandler>>();
					}
					if (!rpcs[modId].ContainsKey(methodInfo.Name))
					{
						rpcs[modId][methodInfo.Name] = new List<MessageHandler>();
					}
					rpcs[modId][methodInfo.Name].Add(new MessageHandler
					{
						Target = instance2,
						Method = methodInfo,
						Parameters = methodInfo.GetParameters(),
						TakesInfo = (methodInfo.GetParameters().Length != 0 && methodInfo.GetParameters().Last().ParameterType.Name == "RPCInfo"),
						Mask = mask
					});
					num++;
				}
			}
			return new Token(delegate
			{
				DeregisterNetworkObject(instance2, modId, mask);
			});
		}

		public IDisposable RegisterNetworkType(Type type, uint modId, int mask = 0)
		{
			Net.Logger.LogWarning((object)"This feature is currently not setup in the offline system");
			return new Token(delegate
			{
				DeregisterNetworkObject(null, modId, mask);
			});
		}

		public void DeregisterNetworkObject(object instance, uint modId, int mask = 0)
		{
			if (!rpcs.TryGetValue(modId, out Dictionary<string, List<MessageHandler>> value))
			{
				return;
			}
			MethodInfo[] methods = instance.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				CustomRPCAttribute[] array = methodInfo.GetCustomAttributes(inherit: false).OfType<CustomRPCAttribute>().ToArray();
				if (array.Length == 0 || !value.TryGetValue(methodInfo.Name, out var value2))
				{
					continue;
				}
				for (int num = value2.Count - 1; num >= 0; num--)
				{
					if (value2[num].Target == instance && value2[num].Mask == mask)
					{
						value2.RemoveAt(num);
					}
				}
			}
		}

		public void DeregisterNetworkType(Type type, uint modId, int mask = 0)
		{
			Net.Logger.LogWarning((object)"This feature is currently not setup in the offline system");
		}

		public void RPC(uint modId, string methodName, ReliableType reliable, params object[] parameters)
		{
			Message message = BuildMessage(modId, methodName, 0, parameters);
			if (message != null)
			{
				DispatchIncoming(message, LocalSteamId);
			}
		}

		public void RPCTarget(uint modId, string methodName, ulong targetSteamId64, ReliableType reliable, params object[] parameters)
		{
			Message message = BuildMessage(modId, methodName, 0, parameters);
			if (message != null && targetSteamId64 == LocalSteamId)
			{
				DispatchIncoming(message, LocalSteamId);
			}
		}

		public void RPCToHost(uint modId, string methodName, ReliableType reliable, params object[] parameters)
		{
			RPCTarget(modId, methodName, HostSteamId64, reliable, parameters);
		}

		public void RegisterLobbyDataKey(string key)
		{
			lobbyKeys.Add(key);
		}

		public void SetLobbyData(string key, object value)
		{
			if (!InLobby)
			{
				Debug.LogError((object)"Cannot set lobby data when not in lobby.");
				return;
			}
			if (!lobbyKeys.Contains(key))
			{
				Debug.LogWarning((object)("Accessing unregistered lobby key " + key));
			}
			lobbyData[key] = Convert.ToString(value, CultureInfo.InvariantCulture);
			this.LobbyDataChanged?.Invoke(new string[1] { key });
		}

		public T GetLobbyData<T>(string key)
		{
			if (!InLobby)
			{
				Debug.LogError((object)"Cannot get lobby data when not in lobby.");
				return default(T);
			}
			if (!lobbyKeys.Contains(key))
			{
				Debug.LogWarning((object)("Accessing unregistered lobby key " + key));
			}
			if (!lobbyData.TryGetValue(key, out string value))
			{
				return default(T);
			}
			try
			{
				return (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
			}
			catch
			{
				Debug.LogError((object)("Could not parse lobby data [" + key + "," + value + "]"));
				return default(T);
			}
		}

		public void RegisterPlayerDataKey(string key)
		{
			playerKeys.Add(key);
		}

		public void SetPlayerData(string key, object value)
		{
			if (!InLobby)
			{
				Debug.LogError((object)"Cannot set player data when not in lobby.");
				return;
			}
			if (!playerKeys.Contains(key))
			{
				Debug.LogWarning((object)("Accessing unregistered player key " + key));
			}
			perPlayerData[LocalSteamId][key] = Convert.ToString(value, CultureInfo.InvariantCulture);
			this.PlayerDataChanged?.Invoke(LocalSteamId, new string[1] { key });
		}

		public T GetPlayerData<T>(ulong steamId64, string key)
		{
			if (!InLobby)
			{
				Debug.LogError((object)"Cannot get player data when not in lobby.");
				return default(T);
			}
			if (!playerKeys.Contains(key))
			{
				Debug.LogWarning((object)("Accessing unregistered player key " + key));
			}
			if (!perPlayerData.TryGetValue(steamId64, out Dictionary<string, string> value))
			{
				return default(T);
			}
			if (!value.TryGetValue(key, out var value2))
			{
				return default(T);
			}
			try
			{
				return (T)Convert.ChangeType(value2, typeof(T), CultureInfo.InvariantCulture);
			}
			catch
			{
				Debug.LogError((object)("Could not parse player data [" + key + "," + value2 + "]"));
				return default(T);
			}
		}

		public void PollReceive()
		{
		}

		private Message? BuildMessage(uint modId, string methodName, int mask, object[] parameters)
		{
			try
			{
				Message message = new Message(modId, methodName, mask);
				if (rpcs.TryGetValue(modId, out Dictionary<string, List<MessageHandler>> value) && value.TryGetValue(methodName, out var value2) && value2.Count > 0)
				{
					MessageHandler messageHandler = value2[0];
					ParameterInfo[] parameters2 = messageHandler.Parameters;
					int num = (messageHandler.TakesInfo ? (parameters2.Length - 1) : parameters2.Length);
					if (num != parameters.Length)
					{
						throw new Exception($"Parameter count mismatch: expected {num}, got {parameters.Length}");
					}
					for (int i = 0; i < num; i++)
					{
						Type parameterType = parameters2[i].ParameterType;
						object obj = parameters[i] ?? throw new Exception($"Null parameter {i}");
						if (!parameterType.IsAssignableFrom(obj.GetType()))
						{
							throw new Exception($"Type mismatch {parameterType} vs {obj.GetType()}");
						}
						message.WriteObject(parameterType, obj);
					}
				}
				else
				{
					foreach (object obj2 in parameters)
					{
						if (obj2 == null)
						{
							throw new Exception("Null parameter");
						}
						message.WriteObject(obj2.GetType(), obj2);
					}
				}
				return message;
			}
			catch (Exception arg)
			{
				Debug.LogError((object)$"BuildMessage failed: {arg}");
				return null;
			}
		}

		private void DispatchIncoming(Message message, ulong from)
		{
			if ((IncomingValidator != null && !IncomingValidator(message, from)) || !rateLimiter.IncomingAllowed())
			{
				return;
			}
			if (!rpcs.TryGetValue(message.ModID, out Dictionary<string, List<MessageHandler>> value))
			{
				Debug.LogWarning((object)$"No mod {message.ModID}");
				return;
			}
			if (!value.TryGetValue(message.MethodName, out var value2))
			{
				Debug.LogWarning((object)("No method " + message.MethodName));
				return;
			}
			MessageHandler[] array = value2.ToArray();
			foreach (MessageHandler messageHandler in array)
			{
				if (messageHandler.Mask == message.Mask)
				{
					ParameterInfo[] parameters = messageHandler.Parameters;
					int num = (messageHandler.TakesInfo ? (parameters.Length - 1) : parameters.Length);
					object[] array2 = new object[parameters.Length];
					for (int j = 0; j < num; j++)
					{
						array2[j] = message.ReadObject(parameters[j].ParameterType);
					}
					if (messageHandler.TakesInfo)
					{
						Type parameterType = parameters[^1].ParameterType;
						array2[parameters.Length - 1] = CreateRpcInfoInstance(parameterType, from);
					}
					try
					{
						messageHandler.Method.Invoke(messageHandler.Target, array2);
					}
					catch (Exception arg)
					{
						Debug.LogError((object)$"Invoke RPC error: {arg}");
					}
				}
			}
		}

		private object CreateRpcInfoInstance(Type infoType, ulong from)
		{
			try
			{
				ConstructorInfo constructor = infoType.GetConstructor(new Type[1] { typeof(ulong) });
				if (constructor != null)
				{
					return constructor.Invoke(new object[1] { from });
				}
				object obj = Activator.CreateInstance(infoType);
				FieldInfo field = infoType.GetField("SenderSteamID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					field.SetValue(obj, from);
				}
				return obj;
			}
			catch
			{
				return null;
			}
		}
	}
	public class SteamNetworkingService : INetworkingService
	{
		private class FragmentBuffer
		{
			public int Total;

			public DateTime FirstSeen = DateTime.UtcNow;

			public Dictionary<int, byte[]> Fragments = new Dictionary<int, byte[]>();
		}

		private sealed class RegistrationToken : IDisposable
		{
			private readonly SteamNetworkingService svc;

			private readonly object? instance;

			private readonly Type? registeredType;

			private readonly uint modId;

			private readonly int mask;

			private bool disposed;

			public RegistrationToken(SteamNetworkingService svc, object? instance, Type? registeredType, uint modId, int mask)
			{
				this.svc = svc;
				this.instance = instance;
				this.registeredType = registeredType;
				this.modId = modId;
				this.mask = mask;
				disposed = false;
			}

			public void Dispose()
			{
				if (!disposed)
				{
					disposed = true;
					if (instance != null)
					{
						svc.DeregisterNetworkObject(instance, modId, mask);
					}
					else if (registeredType != null)
					{
						svc.DeregisterNetworkType(registeredType, modId, mask);
					}
				}
			}
		}

		private class HandshakeState
		{
			public string? PeerPub;

			public string LocalNonce = string.Empty;

			public byte[]? Sym;

			public bool Completed;
		}

		private class SlidingWindowRateLimiter
		{
			private readonly int limit;

			private readonly TimeSpan window;

			private readonly Queue<DateTime> q = new Queue<DateTime>();

			public SlidingWindowRateLimiter(int limit, TimeSpan window)
			{
				this.limit = limit;
				this.window = window;
			}

			public bool Allowed()
			{
				DateTime utcNow = DateTime.UtcNow;
				while (q.Count > 0 && utcNow - q.Peek() > window)
				{
					q.Dequeue();
				}
				if (q.Count >= limit)
				{
					return false;
				}
				q.Enqueue(utcNow);
				return true;
			}

			public bool IncomingAllowed()
			{
				return Allowed();
			}
		}

		private enum Priority
		{
			High,
			Normal,
			Low
		}

		private class QueuedSend
		{
			public byte[] Framed;

			public CSteamID Target;

			public ReliableType Reliable;

			public DateTime Enqueued;
		}

		private class UnackedMessage
		{
			public byte[] Framed;

			public CSteamID Target;

			public ReliableType Reliable;

			public DateTime LastSent;

			public int Attempts;

			public ulong msgId => BitConverter.ToUInt64(Framed, 1);
		}

		private class MessageHandler
		{
			public object Target;

			public MethodInfo Method;

			public ParameterInfo[] Parameters;

			public bool TakesInfo;

			public int Mask;
		}

		private const int CHANNEL = 120;

		private const int MAX_IN_MESSAGES = 500;

		private static IntPtr[] inMessages = new IntPtr[500];

		private readonly object rpcLock = new object();

		private CSteamID[] players = Array.Empty<CSteamID>();

		private static readonly List<string> lobbyDataKeys = new List<string>();

		private static readonly List<string> playerDataKeys = new List<string>();

		private static readonly Dictionary<CSteamID, Dictionary<string, string>> lastPlayerData = new Dictionary<CSteamID, Dictionary<string, string>>();

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

		private Callback<LobbyEnter_t>? cbLobbyEnter;

		private Callback<LobbyCreated_t>? cbLobbyCreated;

		private Callback<LobbyChatUpdate_t>? cbLobbyChatUpdate;

		private Callback<LobbyDataUpdate_t>? cbLobbyDataUpdate;

		private readonly Dictionary<uint, Dictionary<string, List<MessageHandler>>> rpcs = new Dictionary<uint, Dictionary<string, List<MessageHandler>>>();

		private readonly Queue<QueuedSend> highQueue = new Queue<QueuedSend>();

		private readonly Queue<QueuedSend> normalQueue = new Queue<QueuedSend>();

		private readonly Queue<QueuedSend> lowQueue = new Queue<QueuedSend>();

		private readonly object queueLock = new object();

		private readonly Dictionary<(ulong target, ulong msgId), UnackedMessage> unacked = new Dictionary<(ulong, ulong), UnackedMessage>();

		private readonly object unackedLock = new object();

		private TimeSpan ackTimeout = TimeSpan.FromSeconds(1.2);

		private int maxRetransmitAttempts = 5;

		private long _nextMessageId;

		private readonly Dictionary<uint, ulong> outgoingSequencePerMod = new Dictionary<uint, ulong>();

		private readonly Dictionary<ulong, Dictionary<uint, ulong>> lastSeenSequence = new Dictionary<ulong, Dictionary<uint, ulong>>();

		private readonly Dictionary<ulong, SlidingWindowRateLimiter> rateLimiters = new Dictionary<ulong, SlidingWindowRateLimiter>();

		private readonly Dictionary<ulong, byte[]> perPeerSymmetricKey = new Dictionary<ulong, byte[]>();

		private byte[]? globalSharedSecret;

		private HMACSHA256? globalHmac;

		private readonly Dictionary<uint, Func<byte[], byte[]>> modSigners = new Dictionary<uint, Func<byte[], byte[]>>();

		private readonly Dictionary<uint, RSAParameters> modPublicKeys = new Dictionary<uint, RSAParameters>();

		private readonly Dictionary<ulong, HandshakeState> handshakeStates = new Dictionary<ulong, HandshakeState>();

		private const byte FRAG_FLAG = 1;

		private const byte COMPRESSED_FLAG = 2;

		private const byte HMAC_FLAG = 4;

		private const byte SIGN_FLAG = 8;

		private const byte ACK_FLAG = 16;

		private RSACryptoServiceProvider? LocalRsa;

		private readonly Dictionary<(ulong sender, ulong msgId), FragmentBuffer> fragmentBuffers = new Dictionary<(ulong, ulong), FragmentBuffer>();

		private readonly object fragmentLock = new object();

		private readonly TimeSpan FragmentTimeout = TimeSpan.FromSeconds(30.0);

		public bool IsInitialized { get; private set; }

		public bool InLobby { get; private set; }

		public ulong HostSteamId64
		{
			get
			{
				//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_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)
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Unknown result type (might be due to invalid IL or missing references)
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				if (Lobby == CSteamID.Nil)
				{
					return 0uL;
				}
				CSteamID lobbyOwner = SteamMatchmaking.GetLobbyOwner(Lobby);
				if (lobbyOwner == CSteamID.Nil)
				{
					return 0uL;
				}
				return lobbyOwner.m_SteamID;
			}
		}

		public string HostIdString
		{
			get
			{
				//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_0019: 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_0023: 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_0025: Unknown result type (might be due to invalid IL or missing references)
				if (Lobby == CSteamID.Nil)
				{
					return string.Empty;
				}
				CSteamID lobbyOwner = SteamMatchmaking.GetLobbyOwner(Lobby);
				if (!(lobbyOwner == CSteamID.Nil))
				{
					return ((object)(CSteamID)(ref lobbyOwner)).ToString();
				}
				return string.Empty;
			}
		}

		public CSteamID Lobby { get; private set; } = CSteamID.Nil;


		public bool IsHost
		{
			get
			{
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_0029: 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)
				try
				{
					if (!InLobby)
					{
						return false;
					}
					CSteamID lobbyOwner = SteamMatchmaking.GetLobbyOwner(Lobby);
					if (lobbyOwner == CSteamID.Nil)
					{
						return false;
					}
					return lobbyOwner == SteamUser.GetSteamID();
				}
				catch
				{
					return false;
				}
			}
		}

		public Func<Message, ulong, bool>? IncomingValidator { get; set; }

		public event Action? LobbyCreated;

		public event Action? LobbyEntered;

		public event Action? LobbyLeft;

		public event Action<ulong>? PlayerEntered;

		public event Action<ulong>? PlayerLeft;

		public event Action<string[]>? LobbyDataChanged;

		public event Action<ulong, string[]>? PlayerDataChanged;

		public ulong GetLocalSteam64()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return SteamUser.GetSteamID().m_SteamID;
			}
			catch
			{
				return 0uL;
			}
		}

		public ulong[] GetLobbyMemberSteamIds()
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: 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_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: 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)
			//IL_0056: 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)
			if (!InLobby || Lobby == CSteamID.Nil)
			{
				return Array.Empty<ulong>();
			}
			try
			{
				int numLobbyMembers = SteamMatchmaking.GetNumLobbyMembers(Lobby);
				if (numLobbyMembers <= 0)
				{
					return Array.Empty<ulong>();
				}
				ulong[] array = new ulong[numLobbyMembers];
				for (int i = 0; i < numLobbyMembers; i++)
				{
					CSteamID lobbyMemberByIndex = SteamMatchmaking.GetLobbyMemberByIndex(Lobby, i);
					array[i] = ((lobbyMemberByIndex == CSteamID.Nil) ? 0 : lobbyMemberByIndex.m_SteamID);
				}
				return array;
			}
			catch (Exception arg)
			{
				Net.Logger.LogError((object)$"GetLobbyMemberSteamIds error: {arg}");
				return Array.Empty<ulong>();
			}
		}

		private ulong NextMessageId()
		{
			return (ulong)Interlocked.Increment(ref _nextMessageId);
		}

		public void Initialize()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			if (IsInitialized)
			{
				return;
			}
			if (Application.isPlaying)
			{
				try
				{
					GameObject val = GameObject.Find("SteamCallbackPump");
					if ((Object)(object)val == (Object)null)
					{
						val = new GameObject("SteamCallbackPump");
						Object.DontDestroyOnLoad((Object)(object)val);
						val.AddComponent<SteamCallbackPump>();
						Net.Logger.LogInfo((object)"Created SteamCallbackPump GameObject.");
					}
				}
				catch (Exception arg)
				{
					Net.Logger.LogError((object)$"Failed to create SteamCallbackPump: {arg}");
				}
			}
			try
			{
				Message.MaxSize = 524288;
			}
			catch
			{
			}
			cbLobbyEnter = Callback<LobbyEnter_t>.Create((DispatchDelegate<LobbyEnter_t>)OnLobbyEnter);
			cbLobbyCreated = Callback<LobbyCreated_t>.Create((DispatchDelegate<LobbyCreated_t>)OnLobbyCreated);
			cbLobbyChatUpdate = Callback<LobbyChatUpdate_t>.Create((DispatchDelegate<LobbyChatUpdate_t>)OnLobbyChatUpdate);
			cbLobbyDataUpdate = Callback<LobbyDataUpdate_t>.Create((DispatchDelegate<LobbyDataUpdate_t>)OnLobbyDataUpdate);
			LocalRsa = new RSACryptoServiceProvider(2048);
			IsInitialized = true;
			Net.Logger.LogInfo((object)"SteamNetworkingService initialized");
		}

		public void Shutdown()
		{
			cbLobbyEnter = null;
			cbLobbyCreated = null;
			cbLobbyChatUpdate = null;
			cbLobbyDataUpdate = null;
			rpcs.Clear();
			unacked.Clear();
			handshakeStates.Clear();
			perPeerSymmetricKey.Clear();
			globalHmac?.Dispose();
			globalHmac = null;
			LocalRsa?.Dispose();
			LocalRsa = null;
			IsInitialized = false;
			lock (fragmentLock)
			{
				fragmentBuffers.Clear();
			}
			Net.Logger.LogInfo((object)"SteamNetworkingService shutdown");
		}

		public void CreateLobby(int maxPlayers = 8)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			SteamMatchmaking.CreateLobby((ELobbyType)0, maxPlayers);
		}

		public void JoinLobby(ulong lobbySteamId64)
		{
			//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)
			SteamMatchmaking.JoinLobby(new CSteamID(lobbySteamId64));
		}

		public void LeaveLobby()
		{
			OnLobbyLeftInternal();
		}

		public void InviteToLobby(ulong steamId64)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			SteamMatchmaking.InviteUserToLobby(Lobby, new CSteamID(steamId64));
		}

		private void OnLobbyEnter(LobbyEnter_t param)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			Net.Logger.LogDebug((object)$"LobbyEnter {param.m_ulSteamIDLobby}");
			Lobby = new CSteamID(param.m_ulSteamIDLobby);
			InLobby = true;
			RefreshPlayerList();
			this.LobbyEntered?.Invoke();
		}

		private void OnLobbyCreated(LobbyCreated_t param)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			//IL_0061: 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_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			Net.Logger.LogDebug((object)$"LobbyCreated: {param.m_eResult}");
			if ((int)param.m_eResult == 1)
			{
				Lobby = new CSteamID(param.m_ulSteamIDLobby);
				InLobby = true;
				RefreshPlayerList();
				this.LobbyCreated?.Invoke();
			}
			else
			{
				Net.Logger.LogError((object)$"Lobby creation failed: {param.m_eResult}");
			}
		}

		private void OnLobbyChatUpdate(LobbyChatUpdate_t param)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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_004a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				RefreshPlayerList();
				CSteamID val = default(CSteamID);
				((CSteamID)(ref val))..ctor(param.m_ulSteamIDUserChanged);
				EChatMemberStateChange val2 = (EChatMemberStateChange)param.m_rgfChatMemberStateChange;
				if ((val2 & 1) != 0)
				{
					this.PlayerEntered?.Invoke(val.m_SteamID);
				}
				EChatMemberStateChange val3 = (EChatMemberStateChange)30;
				if ((val2 & val3) != 0)
				{
					this.PlayerLeft?.Invoke(val.m_SteamID);
				}
			}
			catch (Exception arg)
			{
				Net.Logger.LogError((object)$"OnLobbyChatUpdate error: {arg}");
			}
		}

		internal void OnLobbyLeftInternal()
		{
			RefreshPlayerList();
			lastLobbyData.Clear();
			lastPlayerData.Clear();
			InLobby = false;
			this.LobbyLeft?.Invoke();
		}

		private void RefreshPlayerList()
		{
			//IL_0001: 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_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				_ = Lobby;
				if (Lobby == CSteamID.Nil)
				{
					players = Array.Empty<CSteamID>();
					return;
				}
				int numLobbyMembers = SteamMatchmaking.GetNumLobbyMembers(Lobby);
				players = (CSteamID[])(object)new CSteamID[numLobbyMembers];
				for (int i = 0; i < players.Length; i++)
				{
					players[i] = SteamMatchmaking.GetLobbyMemberByIndex(Lobby, i);
				}
				Net.Logger.LogDebug((object)$"RefreshPlayerList: total members = {players.Length}");
			}
			catch (Exception arg)
			{
				Net.Logger.LogError((object)$"RefreshPlayerList error: {arg}");
				players = Array.Empty<CSteamID>();
			}
		}

		private void OnLobbyDataUpdate(LobbyDataUpdate_t param)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: 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_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			if (!InLobby || param.m_ulSteamIDLobby != Lobby.m_SteamID)
			{
				return;
			}
			if (param.m_ulSteamIDLobby == param.m_ulSteamIDMember)
			{
				List<string> list = new List<string>();
				foreach (string lobbyDataKey in lobbyDataKeys)
				{
					string lobbyData = SteamMatchmaking.GetLobbyData(Lobby, lobbyDataKey);
					if (!lastLobbyData.TryGetValue(lobbyDataKey, out string value) || value != lobbyData)
					{
						list.Add(lobbyDataKey);
						lastLobbyData[lobbyDataKey] = lobbyData;
					}
				}
				if (list.Count > 0)
				{
					this.LobbyDataChanged?.Invoke(list.ToArray());
				}
				return;
			}
			CSteamID val = default(CSteamID);
			((CSteamID)(ref val))..ctor(param.m_ulSteamIDMember);
			if (!lastPlayerData.ContainsKey(val))
			{
				lastPlayerData[val] = new Dictionary<string, string>();
			}
			List<string> list2 = new List<string>();
			foreach (string playerDataKey in playerDataKeys)
			{
				string lobbyMemberData = SteamMatchmaking.GetLobbyMemberData(Lobby, val, playerDataKey);
				if (!lastPlayerData[val].TryGetValue(playerDataKey, out string value2) || value2 != lobbyMemberData)
				{
					list2.Add(playerDataKey);
					lastPlayerData[val][playerDataKey] = lobbyMemberData;
				}
			}
			if (list2.Count > 0)
			{
				this.PlayerDataChanged?.Invoke(val.m_SteamID, list2.ToArray());
			}
		}

		public void RegisterLobbyDataKey(string key)
		{
			if (lobbyDataKeys.Contains(key))
			{
				Net.Logger.LogWarning((object)("Lobby key " + key + " already registered"));
			}
			else
			{
				lobbyDataKeys.Add(key);
			}
		}

		public void SetLobbyData(string key, object value)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			if (!InLobby)
			{
				Net.Logger.LogError((object)"Cannot set lobby data when not in lobby.");
				return;
			}
			if (!lobbyDataKeys.Contains(key))
			{
				Net.Logger.LogWarning((object)("Accessing unregistered lobby key '" + key + "'."));
			}
			SteamMatchmaking.SetLobbyData(Lobby, key, Convert.ToString(value, CultureInfo.InvariantCulture));
		}

		public T GetLobbyData<T>(string key)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			if (!InLobby)
			{
				Net.Logger.LogError((object)"Cannot get lobby data when not in lobby.");
				return default(T);
			}
			if (!lobbyDataKeys.Contains(key))
			{
				Net.Logger.LogWarning((object)("Accessing unregistered lobby key '" + key + "'."));
			}
			string lobbyData = SteamMatchmaking.GetLobbyData(Lobby, key);
			if (string.IsNullOrEmpty(lobbyData))
			{
				return default(T);
			}
			try
			{
				return (T)Convert.ChangeType(lobbyData, typeof(T), CultureInfo.InvariantCulture);
			}
			catch
			{
				Net.Logger.LogError((object)("Could not parse lobby data [" + key + "," + lobbyData + "] as " + typeof(T).Name));
				return default(T);
			}
		}

		public void RegisterPlayerDataKey(string key)
		{
			if (playerDataKeys.Contains(key))
			{
				Net.Logger.LogWarning((object)("Player key " + key + " already registered"));
			}
			else
			{
				playerDataKeys.Add(key);
			}
		}

		public void SetPlayerData(string key, object value)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			if (!InLobby)
			{
				Net.Logger.LogError((object)"Cannot set player data when not in lobby.");
				return;
			}
			if (!playerDataKeys.Contains(key))
			{
				Net.Logger.LogWarning((object)("Accessing unregistered player key '" + key + "'."));
			}
			SteamMatchmaking.SetLobbyMemberData(Lobby, key, Convert.ToString(value, CultureInfo.InvariantCulture));
		}

		public T GetPlayerData<T>(ulong steamId64, string key)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			if (!InLobby)
			{
				Net.Logger.LogError((object)"Cannot get player data when not in lobby.");
				return default(T);
			}
			if (!playerDataKeys.Contains(key))
			{
				Net.Logger.LogWarning((object)("Accessing unregistered player key '" + key + "'."));
			}
			CSteamID val = default(CSteamID);
			((CSteamID)(ref val))..ctor(steamId64);
			string lobbyMemberData = SteamMatchmaking.GetLobbyMemberData(Lobby, val, key);
			if (string.IsNullOrEmpty(lobbyMemberData))
			{
				return default(T);
			}
			try
			{
				return (T)Convert.ChangeType(lobbyMemberData, typeof(T), CultureInfo.InvariantCulture);
			}
			catch
			{
				Net.Logger.LogError((object)("Could not parse player data [" + key + "," + lobbyMemberData + "] as " + typeof(T).Name));
				return default(T);
			}
		}

		public IDisposable RegisterNetworkObject(object instance, uint modId, int mask = 0)
		{
			if (instance == null)
			{
				throw new ArgumentNullException("instance");
			}
			return RegisterNetworkTypeInternal(instance.GetType(), instance, modId, mask);
		}

		public IDisposable RegisterNetworkType(Type type, uint modId, int mask = 0)
		{
			if (type == null)
			{
				throw new ArgumentNullException("type");
			}
			return RegisterNetworkTypeInternal(type, null, modId, mask);
		}

		private IDisposable RegisterNetworkTypeInternal(Type type, object? instance, uint modId, int mask)
		{
			int num = 0;
			lock (rpcLock)
			{
				MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				MethodInfo[] array = methods;
				foreach (MethodInfo methodInfo in array)
				{
					CustomRPCAttribute[] array2 = methodInfo.GetCustomAttributes(inherit: false).OfType<CustomRPCAttribute>().ToArray();
					if (array2.Length != 0)
					{
						if (!rpcs.ContainsKey(modId))
						{
							rpcs[modId] = new Dictionary<string, List<MessageHandler>>();
						}
						if (!rpcs[modId].ContainsKey(methodInfo.Name))
						{
							rpcs[modId][methodInfo.Name] = new List<MessageHandler>();
						}
						MessageHandler item = new MessageHandler
						{
							Target = (methodInfo.IsStatic ? null : instance),
							Method = methodInfo,
							Parameters = methodInfo.GetParameters(),
							TakesInfo = (methodInfo.GetParameters().Length != 0 && methodInfo.GetParameters().Last().ParameterType.Name == "RPCInfo"),
							Mask = mask
						};
						rpcs[modId][methodInfo.Name].Add(item);
						num++;
					}
				}
			}
			if (instance != null)
			{
				Net.Logger.LogInfo((object)$"Registered {num} RPCs for mod {modId} on {instance} ({instance.GetType().FullName})");
			}
			else
			{
				Net.Logger.LogInfo((object)$"Registered {num} static RPCs for mod {modId} on type {type.FullName}");
			}
			return new RegistrationToken(this, instance, type, modId, mask);
		}

		public void DeregisterNetworkObject(object instance, uint modId, int mask = 0)
		{
			if (instance == null)
			{
				throw new ArgumentNullException("instance");
			}
			DeregisterNetworkObjectInternal(instance.GetType(), instance, modId, mask);
		}

		public void DeregisterNetworkType(Type type, uint modId, int mask = 0)
		{
			if (type == null)
			{
				throw new ArgumentNullException("type");
			}
			DeregisterNetworkObjectInternal(type, null, modId, mask);
		}

		private void DeregisterNetworkObjectInternal(Type type, object? instanceOrNull, uint modId, int mask)
		{
			lock (rpcLock)
			{
				if (!rpcs.TryGetValue(modId, out Dictionary<string, List<MessageHandler>> value))
				{
					Net.Logger.LogWarning((object)$"No RPCs for mod {modId}");
					return;
				}
				int num = 0;
				string[] array = value.Keys.ToArray();
				string[] array2 = array;
				foreach (string key in array2)
				{
					if (!value.TryGetValue(key, out var value2))
					{
						continue;
					}
					for (int num2 = value2.Count - 1; num2 >= 0; num2--)
					{
						MessageHandler messageHandler = value2[num2];
						if (instanceOrNull != null && messageHandler.Target != null && messageHandler.Target == instanceOrNull && messageHandler.Mask == mask)
						{
							value2.RemoveAt(num2);
							num++;
						}
						else if (instanceOrNull == null && messageHandler.Target == null && messageHandler.Method.DeclaringType == type && messageHandler.Mask == mask)
						{
							value2.RemoveAt(num2);
							num++;
						}
					}
					if (value2.Count == 0)
					{
						value.Remove(key);
					}
				}
				if (value.Count == 0)
				{
					rpcs.Remove(modId);
				}
				Net.Logger.LogInfo((object)$"Deregistered {num} RPCs for mod {modId} (type/instance {type.FullName})");
			}
		}

		public void RPC(uint modId, string methodName, ReliableType reliable, params object[] parameters)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			if (!InLobby)
			{
				Net.Logger.LogError((object)"RPC called while not in lobby");
				return;
			}
			Message message = BuildMessage(modId, methodName, 0, parameters);
			if (message == null)
			{
				return;
			}
			CSteamID[] array = players;
			foreach (CSteamID val in array)
			{
				if (!(val == SteamUser.GetSteamID()))
				{
					EnqueueOrSend(BuildFramedBytesWithMeta(message, modId, reliable), val, reliable, DeterminePriority(modId, methodName));
				}
			}
			InvokeLocalMessage(new Message(message.ToArray()), SteamUser.GetSteamID());
		}

		public void RPCTarget(uint modId, string methodName, ulong targetSteamId64, ReliableType reliable, params object[] parameters)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			RPCTarget(modId, methodName, new CSteamID(targetSteamId64), reliable, parameters);
		}

		public void RPCTarget(uint modId, string methodName, CSteamID target, ReliableType reliable, params object[] parameters)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (!InLobby)
			{
				Net.Logger.LogError((object)"Cannot RPC target when not in lobby");
				return;
			}
			Message message = BuildMessage(modId, methodName, 0, parameters);
			if (message != null)
			{
				byte[] framed = BuildFramedBytesWithMeta(message, modId, reliable);
				EnqueueOrSend(framed, target, reliable, DeterminePriority(modId, methodName));
			}
		}

		public void RPCToHost(uint modId, string methodName, ReliableType reliable, params object[] parameters)
		{
			//IL_0019: 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_0023: 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_0025: 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)
			if (!InLobby)
			{
				Net.Logger.LogError((object)"Not in lobby");
				return;
			}
			CSteamID lobbyOwner = SteamMatchmaking.GetLobbyOwner(Lobby);
			if (lobbyOwner == CSteamID.Nil)
			{
				Net.Logger.LogError((object)"No host set");
			}
			else
			{
				RPCTarget(modId, methodName, lobbyOwner, reliable, parameters);
			}
		}

		private Priority DeterminePriority(uint modId, string methodName)
		{
			string text = methodName.ToLowerInvariant();
			if (text.Contains("admin") || text.Contains("control") || text.Contains("critical") || text.Contains("sync"))
			{
				return Priority.High;
			}
			return Priority.Normal;
		}

		private void EnqueueOrSend(byte[] framed, CSteamID target, ReliableType reliable, Priority p)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			SlidingWindowRateLimiter orCreateRateLimiter = GetOrCreateRateLimiter(target.m_SteamID);
			if (!orCreateRateLimiter.Allowed())
			{
				return;
			}
			if (p == Priority.High)
			{
				SendWithPossibleAck(framed, target, reliable);
				return;
			}
			lock (queueLock)
			{
				Queue<QueuedSend> queue = ((p == Priority.Normal) ? normalQueue : lowQueue);
				queue.Enqueue(new QueuedSend
				{
					Framed = framed,
					Target = target,
					Reliable = reliable,
					Enqueued = DateTime.UtcNow
				});
			}
		}

		private void FlushQueues(int maxPerFrame = 8)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			while (num < maxPerFrame)
			{
				QueuedSend queuedSend = null;
				lock (queueLock)
				{
					if (normalQueue.Count > 0)
					{
						queuedSend = normalQueue.Dequeue();
					}
					else
					{
						if (lowQueue.Count <= 0)
						{
							break;
						}
						queuedSend = lowQueue.Dequeue();
					}
				}
				if (queuedSend != null)
				{
					SendWithPossibleAck(queuedSend.Framed, queuedSend.Target, queuedSend.Reliable);
					num++;
				}
			}
		}

		private byte[] BuildFramedBytesWithMeta(Message msg, uint modId, ReliableType reliable)
		{
			byte[] array = msg.ToArray();
			bool flag = array.Length > 1024;
			if (flag)
			{
				array = msg.CompressPayload();
			}
			byte b = 0;
			if (flag)
			{
				b = (byte)(b | 2u);
			}
			if (globalHmac != null)
			{
				b = (byte)(b | 4u);
			}
			if (modSigners.ContainsKey(modId))
			{
				b = (byte)(b | 8u);
			}
			ulong value2;
			lock (outgoingSequencePerMod)
			{
				if (!outgoingSequencePerMod.TryGetValue(modId, out var value))
				{
					value = 0uL;
				}
				value2 = ++value;
				outgoingSequencePerMod[modId] = value;
			}
			ulong value3 = NextMessageId();
			using MemoryStream memoryStream = new MemoryStream();
			memoryStream.WriteByte(b);
			memoryStream.Write(BitConverter.GetBytes(value3), 0, 8);
			memoryStream.Write(BitConverter.GetBytes(value2), 0, 8);
			memoryStream.Write(BitConverter.GetBytes(1), 0, 4);
			memoryStream.Write(BitConverter.GetBytes(0), 0, 4);
			memoryStream.Write(array, 0, array.Length);
			byte[] array2 = memoryStream.ToArray();
			if (modSigners.TryGetValue(modId, out Func<byte[], byte[]> value4))
			{
				byte[] array3 = value4(array2);
				using MemoryStream memoryStream2 = new MemoryStream();
				memoryStream2.Write(array2, 0, array2.Length);
				ushort value5 = (ushort)array3.Length;
				memoryStream2.Write(BitConverter.GetBytes(value5), 0, 2);
				memoryStream2.Write(array3, 0, array3.Length);
				array2 = memoryStream2.ToArray();
			}
			if (globalHmac != null)
			{
				byte[] array4 = globalHmac.ComputeHash(array2);
				using MemoryStream memoryStream3 = new MemoryStream();
				memoryStream3.Write(array2, 0, array2.Length);
				memoryStream3.Write(array4, 0, array4.Length);
				array2 = memoryStream3.ToArray();
			}
			if (reliable == ReliableType.Reliable)
			{
				array2[0] = (byte)(array2[0] | 0x10u);
			}
			return array2;
		}

		private void SendWithPossibleAck(byte[] framed, CSteamID target, ReliableType reliable)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			if (perPeerSymmetricKey.TryGetValue(target.m_SteamID, out byte[] value))
			{
				using HMACSHA256 hMACSHA = new HMACSHA256(value);
				byte[] array = hMACSHA.ComputeHash(framed);
				using MemoryStream memoryStream = new MemoryStream();
				memoryStream.Write(framed, 0, framed.Length);
				memoryStream.Write(array, 0, array.Length);
				framed = memoryStream.ToArray();
				framed[0] = (byte)(framed[0] | 4u);
			}
			if ((framed[0] & 0x10u) != 0)
			{
				ulong item = BitConverter.ToUInt64(framed, 1);
				(ulong, ulong) key = (target.m_SteamID, item);
				lock (unackedLock)
				{
					unacked[key] = new UnackedMessage
					{
						Framed = framed,
						Target = target,
						Reliable = reliable,
						LastSent = DateTime.UtcNow,
						Attempts = 1
					};
				}
			}
			SendBytes(framed, target, reliable);
		}

		private void SendBytes(byte[] data, CSteamID target, ReliableType reliable)
		{
			//IL_0031: 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_0054: 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_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Invalid comparison between Unknown and I4
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			if (data.Length > Message.MaxSize)
			{
				Net.Logger.LogError((object)$"Send length {data.Length} exceeds Message.MaxSize {Message.MaxSize}");
				return;
			}
			if (target == SteamUser.GetSteamID())
			{
				Message message = new Message(data);
				InvokeLocalMessage(message, SteamUser.GetSteamID());
				return;
			}
			SteamNetworkingIdentity val = default(SteamNetworkingIdentity);
			((SteamNetworkingIdentity)(ref val)).SetSteamID(target);
			GCHandle gCHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
			try
			{
				IntPtr intPtr = gCHandle.AddrOfPinnedObject();
				int num = 32;
				switch (reliable)
				{
				case ReliableType.Unreliable:
					num |= 0;
					break;
				case ReliableType.Reliable:
					num |= 8;
					break;
				case ReliableType.UnreliableNoDelay:
					num |= 5;
					break;
				}
				EResult val2 = SteamNetworkingMessages.SendMessageToUser(ref val, intPtr, (uint)data.Length, num, 120);
				if ((int)val2 != 1)
				{
					Net.Logger.LogError((object)$"SendMessageToUser failed: {val2} to {target}");
				}
			}
			catch (Exception arg)
			{
				Net.Logger.LogError((object)$"SendBytes exception: {arg}");
			}
			finally
			{
				if (gCHandle.IsAllocated)
				{
					gCHandle.Free();
				}
			}
		}

		public void PollReceive()
		{
			try
			{
				SteamAPI.RunCallbacks();
			}
			catch (Exception arg)
			{
				Net.Logger.LogError((object)$"SteamAPI.RunCallbacks error: {arg}");
			}
			FlushQueues();
			RetransmitUnacked();
			ReceiveMessages();
		}

		private void RetransmitUnacked()
		{
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			List<UnackedMessage> list = new List<UnackedMessage>();
			lock (unackedLock)
			{
				DateTime utcNow = DateTime.UtcNow;
				(ulong, ulong)[] array = unacked.Keys.ToArray();
				(ulong, ulong)[] array2 = array;
				foreach ((ulong, ulong) key in array2)
				{
					UnackedMessage unackedMessage = unacked[key];
					if (utcNow - unackedMessage.LastSent > ackTimeout)
					{
						if (unackedMessage.Attempts >= maxRetransmitAttempts)
						{
							unacked.Remove(key);
							continue;
						}
						unackedMessage.Attempts++;
						unackedMessage.LastSent = utcNow;
						unacked[key] = unackedMessage;
						list.Add(unackedMessage);
					}
				}
			}
			foreach (UnackedMessage item in list)
			{
				try
				{
					SendBytes(item.Framed, item.Target, item.Reliable);
				}
				catch (Exception arg)
				{
					Net.Logger.LogError((object)$"RetransmitUnacked: retransmit SendBytes exception: {arg}");
				}
			}
		}

		private void ReceiveMessages()
		{
			//IL_002b: 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_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: 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)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				int num = SteamNetworkingMessages.ReceiveMessagesOnChannel(120, inMessages, 500);
				if (num <= 0)
				{
					return;
				}
				for (int i = 0; i < num; i++)
				{
					IntPtr intPtr = inMessages[i];
					SteamNetworkingMessage_t val = Marshal.PtrToStructure<SteamNetworkingMessage_t>(intPtr);
					int cbSize = val.m_cbSize;
					if (cbSize <= 0)
					{
						SteamNetworkingMessage_t.Release(intPtr);
						continue;
					}
					if (cbSize > Message.MaxSize)
					{
						SteamNetworkingMessage_t.Release(intPtr);
						continue;
					}
					CSteamID steamID = ((SteamNetworkingIdentity)(ref val.m_identityPeer)).GetSteamID();
					if (steamID == CSteamID.Nil)
					{
						SteamNetworkingMessage_t.Release(intPtr);
						continue;
					}
					byte[] array = new byte[cbSize];
					Marshal.Copy(val.m_pData, array, 0, cbSize);
					try
					{
						ProcessIncomingFrame(array, steamID);
					}
					catch (Exception arg)
					{
						Net.Logger.LogError((object)$"ProcessIncomingFrame exception: {arg}");
					}
					SteamNetworkingMessage_t.Release(intPtr);
				}
			}
			catch (Exception arg2)
			{
				Net.Logger.LogError((object)$"ReceiveMessages outer exception: {arg2}");
			}
		}

		private void ProcessIncomingFrame(byte[] frame, CSteamID sender)
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_026f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0478: Unknown result type (might be due to invalid IL or missing references)
			//IL_045a: Unknown result type (might be due to invalid IL or missing references)
			//IL_046b: Unknown result type (might be due to invalid IL or missing references)
			//IL_04af: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d5: Unknown result type (might be due to invalid IL or missing references)
			using MemoryStream memoryStream = new MemoryStream(frame);
			int num = memoryStream.ReadByte();
			if (num < 0)
			{
				return;
			}
			bool flag = (num & 2) != 0;
			bool flag2 = (num & 4) != 0;
			bool flag3 = (num & 8) != 0;
			bool flag4 = (num & 0x10) != 0;
			try
			{
				ulong num2 = ReadU64(memoryStream);
				ulong seq = ReadU64(memoryStream);
				int num3 = ReadI32(memoryStream);
				int key = ReadI32(memoryStream);
				int num4 = (int)(memoryStream.Length - memoryStream.Position);
				if (num4 <= 0)
				{
					return;
				}
				byte[] array = new byte[num4];
				memoryStream.Read(array, 0, num4);
				byte[] array2;
				if (num3 > 1)
				{
					(ulong, ulong) key2 = (sender.m_SteamID, num2);
					lock (fragmentLock)
					{
						if (!fragmentBuffers.TryGetValue(key2, out FragmentBuffer value))
						{
							value = new FragmentBuffer
							{
								Total = num3,
								FirstSeen = DateTime.UtcNow
							};
							fragmentBuffers[key2] = value;
						}
						value.Fragments[key] = array;
						List<(ulong, ulong)> list = (from kv in fragmentBuffers
							where DateTime.UtcNow - kv.Value.FirstSeen > FragmentTimeout
							select kv.Key).ToList();
						foreach (var item in list)
						{
							fragmentBuffers.Remove(item);
						}
						if (value.Fragments.Count != value.Total)
						{
							return;
						}
						using MemoryStream memoryStream2 = new MemoryStream();
						for (int i = 0; i < value.Total; i++)
						{
							if (!value.Fragments.TryGetValue(i, out byte[] value2))
							{
								fragmentBuffers.Remove(key2);
								return;
							}
							memoryStream2.Write(value2, 0, value2.Length);
						}
						array2 = memoryStream2.ToArray();
						fragmentBuffers.Remove(key2);
					}
				}
				else
				{
					array2 = array;
				}
				byte[] array3 = array2;
				byte[] array4 = array3;
				if (flag2)
				{
					if (array3.Length < 32)
					{
						return;
					}
					int num5 = array3.Length - 32;
					byte[] array5 = new byte[num5];
					Array.Copy(array3, 0, array5, 0, num5);
					byte[] array6 = new byte[32];
					Array.Copy(array3, num5, array6, 0, 32);
					if (perPeerSymmetricKey.TryGetValue(sender.m_SteamID, out byte[] value3))
					{
						using HMACSHA256 hMACSHA = new HMACSHA256(value3);
						byte[] array7 = hMACSHA.ComputeHash(array5);
						if (!((ReadOnlySpan<byte>)array7).SequenceEqual((ReadOnlySpan<byte>)array6))
						{
							return;
						}
						array4 = array5;
					}
					else
					{
						if (globalHmac == null)
						{
							return;
						}
						byte[] array8 = globalHmac.ComputeHash(array5);
						if (!((ReadOnlySpan<byte>)array8).SequenceEqual((ReadOnlySpan<byte>)array6))
						{
							return;
						}
						array4 = array5;
					}
				}
				if (flag3)
				{
					if (array4.Length < 5 || array4.Length < 5)
					{
						return;
					}
					uint key3 = BitConverter.ToUInt32(array4, 1);
					if (!modPublicKeys.TryGetValue(key3, out var value4))
					{
						return;
					}
					byte[]? modulus = value4.Modulus;
					int num6 = ((modulus != null) ? modulus.Length : 0);
					if (num6 <= 0 || array4.Length < num6 + 2)
					{
						return;
					}
					int num7 = array4.Length - num6 - 2;
					if (num7 < 0)
					{
						return;
					}
					ushort num8 = BitConverter.ToUInt16(array4, num7);
					if (num8 != num6)
					{
						return;
					}
					byte[] array9 = new byte[num6];
					Array.Copy(array4, num7 + 2, array9, 0, num6);
					byte[] array10 = new byte[num7];
					Array.Copy(array4, 0, array10, 0, num7);
					try
					{
						using RSACryptoServiceProvider rSACryptoServiceProvider = new RSACryptoServiceProvider();
						rSACryptoServiceProvider.ImportParameters(value4);
						if (!rSACryptoServiceProvider.VerifyData(array10, CryptoConfig.MapNameToOID("SHA256"), array9))
						{
							return;
						}
						array4 = array10;
					}
					catch (Exception arg)
					{
						Net.Logger.LogError((object)$"ProcessIncomingFrame: Signature verification error: {arg}");
						return;
					}
				}
				if (flag)
				{
					try
					{
						array4 = Message.DecompressPayload(array4);
					}
					catch (Exception arg2)
					{
						Net.Logger.LogError((object)$"ProcessIncomingFrame: Decompression failed: {arg2}");
						return;
					}
				}
				Message message = new Message(array4);
				if (message.ModID == 0)
				{
					HandleInternalMessage(message, sender, num2, seq, flag4);
					if (flag4)
					{
						SendAckToSender(sender, num2);
					}
					return;
				}
				ulong steamID = sender.m_SteamID;
				SlidingWindowRateLimiter orCreateRateLimiter = GetOrCreateRateLimiter(steamID);
				if (orCreateRateLimiter.IncomingAllowed() && CheckAndUpdateSequence(steamID, message.ModID, seq))
				{
					if (flag4)
					{
						SendAckToSender(sender, num2);
					}
					if (IncomingValidator == null || IncomingValidator(message, steamID))
					{
						DispatchIncoming(message, sender);
					}
				}
			}
			catch (Exception arg3)
			{
				Net.Logger.LogError((object)$"ProcessIncomingFrame top-level exception: {arg3}");
			}
		}

		private static ulong ReadU64(Stream s)
		{
			byte[] array = new byte[8];
			s.Read(array, 0, 8);
			return BitConverter.ToUInt64(array, 0);
		}

		private static int ReadI32(Stream s)
		{
			byte[] array = new byte[4];
			s.Read(array, 0, 4);
			return BitConverter.ToInt32(array, 0);
		}

		private void SendAckToSender(CSteamID sender, ulong msgId)
		{
			//IL_0021: 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)
			Message message = new Message(0u, "NETWORK_INTERNAL_ACK", 0);
			message.WriteULong(msgId);
			byte[] data = BuildFramedBytesWithMeta(message, 0u, ReliableType.Reliable);
			SendBytes(data, sender, ReliableType.Reliable);
			(ulong, ulong) key = (sender.m_SteamID, msgId);
			lock (unackedLock)
			{
				if (unacked.ContainsKey(key))
				{
					unacked.Remove(key);
				}
			}
		}

		private void HandleInternalMessage(Message message, CSteamID sender, ulong msgId, ulong seq, bool requiresAck)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				switch (message.MethodName)
				{
				case "NETWORK_INTERNAL_HANDSHAKE_PUBKEY":
				{
					string peerPubKeySerialized = message.ReadString();
					string peerNonce = message.ReadString();
					StartHandshakeReply(sender, peerPubKeySerialized, peerNonce);
					break;
				}
				case "NETWORK_INTERNAL_HANDSHAKE_SECRET":
				{
					byte[] encSecret = (byte[])message.ReadObject(typeof(byte[]));
					string initiatorNonce2 = message.ReadString();
					CompleteHandshakeReceiver(sender, encSecret, initiatorNonce2);
					break;
				}
				case "NETWORK_INTERNAL_HANDSHAKE_CONFIRM":
				{
					string initiatorNonce = message.ReadString();
					byte[] confirmHmac = (byte[])message.ReadObject(typeof(byte[]));
					CompleteHandshakeInitiator(sender, initiatorNonce, confirmHmac);
					break;
				}
				case "NETWORK_INTERNAL_ACK":
				{
					ulong item = message.ReadULong();
					(ulong, ulong) key = (sender.m_SteamID, item);
					lock (unackedLock)
					{
						if (unacked.ContainsKey(key))
						{
							unacked.Remove(key);
						}
						break;
					}
				}
				default:
					Net.Logger.LogWarning((object)("Unknown internal method " + message.MethodName));
					break;
				}
			}
			catch (Exception arg)
			{
				Net.Logger.LogError((object)$"HandleInternalMessage error: {arg}");
			}
		}

		private bool CheckAndUpdateSequence(ulong sender64, uint modId, ulong seq)
		{
			lock (lastSeenSequence)
			{
				if (!lastSeenSequence.TryGetValue(sender64, out Dictionary<uint, ulong> value))
				{
					Dictionary<uint, ulong> dictionary2 = (lastSeenSequence[sender64] = new Dictionary<uint, ulong>());
					value = dictionary2;
				}
				if (!value.TryGetValue(modId, out var value2))
				{
					value2 = 0uL;
				}
				if (seq <= value2)
				{
					return false;
				}
				value[modId] = seq;
				return true;
			}
		}

		private void DispatchIncoming(Message message, CSteamID sender)
		{
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			Message message2 = message;
			if (!rpcs.TryGetValue(message2.ModID, out Dictionary<string, List<MessageHandler>> value))
			{
				Net.Logger.LogWarning((object)$"Dropping message for unknown mod {message2.ModID}");
				return;
			}
			if (!value.TryGetValue(message2.MethodName, out var value2))
			{
				Net.Logger.LogWarning((object)$"Dropping message for method {message2.MethodName} not registered for {message2.ModID}");
				return;
			}
			bool flag = false;
			List<MessageHandler> list = value2.Where((MessageHandler h) => h.Mask == message2.Mask).ToList();
			foreach (MessageHandler item in list)
			{
				Message message3 = new Message(message2.ToArray());
				try
				{
					ParameterInfo[] parameters = item.Parameters;
					int num = (item.TakesInfo ? (parameters.Length - 1) : parameters.Length);
					object[] array = new object[parameters.Length];
					for (int i = 0; i < num; i++)
					{
						Type parameterType = parameters[i].ParameterType;
						array[i] = message3.ReadObject(parameterType);
					}
					if (item.TakesInfo)
					{
						Type parameterType2 = parameters[^1].ParameterType;
						array[parameters.Length - 1] = CreateRpcInfoInstance(parameterType2, sender);
					}
					item.Method.Invoke(item.Target, array);
					flag = true;
				}
				catch
				{
					continue;
				}
				break;
			}
			if (!flag)
			{
				Net.Logger.LogWarning((object)$"No handler matched for {message2.ModID}:{message2.MethodName}");
			}
			if (!flag)
			{
				Net.Logger.LogWarning((object)$"No handler matched for {message2.ModID}:{message2.MethodName}");
			}
		}

		private object CreateRpcInfoInstance(Type infoType, CSteamID sender)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ConstructorInfo constructor = infoType.GetConstructor(new Type[3]
				{
					typeof(ulong),
					typeof(string),
					typeof(bool)
				});
				if (constructor != null)
				{
					bool flag = false;
					try
					{
						flag = sender == SteamUser.GetSteamID();
					}
					catch
					{
						flag = false;
					}
					return constructor.Invoke(new object[3]
					{
						sender.m_SteamID,
						((object)(CSteamID)(ref sender)).ToString(),
						flag
					});
				}
				ConstructorInfo constructor2 = infoType.GetConstructor(new Type[1] { typeof(CSteamID) });
				if (constructor2 != null)
				{
					return constructor2.Invoke(new object[1] { sender });
				}
				ConstructorInfo constructor3 = infoType.GetConstructor(new Type[1] { typeof(ulong) });
				if (constructor3 != null)
				{
					return constructor3.Invoke(new object[1] { sender.m_SteamID });
				}
				ConstructorInfo constructor4 = infoType.GetConstructor(Type.EmptyTypes);
				if (constructor4 != null)
				{
					object obj2 = constructor4.Invoke(null);
					FieldInfo field = infoType.GetField("SenderSteamID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null)
					{
						field.SetValue(obj2, sender);
					}
					FieldInfo field2 = infoType.GetField("Sender", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field2 != null)
					{
						field2.SetValue(obj2, sender);
					}
					return obj2;
				}
			}
			catch
			{
			}
			return null;
		}

		public void StartHandshake(CSteamID target)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			if (LocalRsa != null)
			{
				string v = SerializeRsaPublicKey(LocalRsa);
				RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create();
				byte[] array = new byte[16];
				randomNumberGenerator.GetBytes(array);
				string text = Convert.ToBase64String(array);
				handshakeStates[target.m_SteamID] = new HandshakeState
				{
					PeerPub = null,
					LocalNonce = text,
					Completed = false
				};
				Message message = new Message(0u, "NETWORK_INTERNAL_HANDSHAKE_PUBKEY", 0);
				message.WriteString(v);
				message.WriteString(text);
				byte[] data = BuildFramedBytesWithMeta(message, 0u, ReliableType.Reliable);
				SendBytes(data, target, ReliableType.Reliable);
			}
		}

		private void StartHandshakeReply(CSteamID sender, string peerPubKeySerialized, string peerNonce)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			if (LocalRsa != null)
			{
				HandshakeState handshakeState = (handshakeStates.ContainsKey(sender.m_SteamID) ? handshakeStates[sender.m_SteamID] : new HandshakeState());
				handshakeState.PeerPub = peerPubKeySerialized;
				if (string.IsNullOrEmpty(handshakeState.LocalNonce))
				{
					RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create();
					byte[] array = new byte[16];
					randomNumberGenerator.GetBytes(array);
					handshakeState.LocalNonce = Convert.ToBase64String(array);
					handshakeStates[sender.m_SteamID] = handshakeState;
					string v = SerializeRsaPublicKey(LocalRsa);
					Message message = new Message(0u, "NETWORK_INTERNAL_HANDSHAKE_PUBKEY", 0);
					message.WriteString(v);
					message.WriteString(handshakeState.LocalNonce);
					byte[] data = BuildFramedBytesWithMeta(message, 0u, ReliableType.Reliable);
					SendBytes(data, sender, ReliableType.Reliable);
				}
				else if (!handshakeState.Completed && !string.IsNullOrEmpty(handshakeState.LocalNonce) && !string.IsNullOrEmpty(handshakeState.PeerPub))
				{
					RandomNumberGenerator randomNumberGenerator2 = RandomNumberGenerator.Create();
					byte[] array2 = new byte[32];
					randomNumberGenerator2.GetBytes(array2);
					RSACryptoServiceProvider rSACryptoServiceProvider = new RSACryptoServiceProvider();
					RSAParameters parameters = DeserializeRsaPublicKey(handshakeState.PeerPub);
					rSACryptoServiceProvider.ImportParameters(parameters);
					byte[] v2 = rSACryptoServiceProvider.Encrypt(array2, fOAEP: false);
					Message message2 = new Message(0u, "NETWORK_INTERNAL_HANDSHAKE_SECRET", 0);
					message2.WriteBytes(v2);
					message2.WriteString(handshakeState.LocalNonce);
					byte[] data2 = BuildFramedBytesWithMeta(message2, 0u, ReliableType.Reliable);
					SendBytes(data2, sender, ReliableType.Reliable);
					handshakeState.Sym = array2;
					handshakeState.Completed = true;
					handshakeStates[sender.m_SteamID] = handshakeState;
					perPeerSymmetricKey[sender.m_SteamID] = array2;
				}
			}
		}

		private void CompleteHandshakeReceiver(CSteamID sender, byte[] encSecret, string initiatorNonce)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			if (LocalRsa == null)
			{
				return;
			}
			try
			{
				byte[] array = LocalRsa.Decrypt(encSecret, fOAEP: false);
				HandshakeState handshakeState = (handshakeStates.ContainsKey(sender.m_SteamID) ? handshakeStates[sender.m_SteamID] : new HandshakeState());
				handshakeState.Sym = array;
				handshakeState.Completed = true;
				handshakeStates[sender.m_SteamID] = handshakeState;
				perPeerSymmetricKey[sender.m_SteamID] = array;
				byte[] v = HmacSha256Raw(array, Encoding.UTF8.GetBytes(initiatorNonce));
				Message message = new Message(0u, "NETWORK_INTERNAL_HANDSHAKE_CONFIRM", 0);
				message.WriteString(initiatorNonce);
				message.WriteBytes(v);
				byte[] data = BuildFramedBytesWithMeta(message, 0u, ReliableType.Reliable);
				SendBytes(data, sender, ReliableType.Reliable);
			}
			catch (Exception arg)
			{
				Net.Logger.LogError((object)$"Handshake decryption error: {arg}");
			}
		}

		private void CompleteHandshakeInitiator(CSteamID sender, string initiatorNonce, byte[] confirmHmac)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			if (!handshakeStates.TryGetValue(sender.m_SteamID, out HandshakeState value) || value.Sym == null)
			{
				Net.Logger.LogWarning((object)"Handshake confirm received but no state");
				return;
			}
			byte[] array = HmacSha256Raw(value.Sym, Encoding.UTF8.GetBytes(initiatorNonce));
			if (!((ReadOnlySpan<byte>)array).SequenceEqual((ReadOnlySpan<byte>)confirmHmac))
			{
				Net.Logger.LogWarning((object)"Handshake confirm HMAC mismatch");
				return;
			}
			value.Completed = true;
			handshakeStates[sender.m_SteamID] = value;
			perPeerSymmetricKey[sender.m_SteamID] = value.Sym;
		}

		private static byte[] HmacSha256Raw(byte[] key, byte[] payload)
		{
			using HMACSHA256 hMACSHA = new HMACSHA256(key);
			return hMACSHA.ComputeHash(payload);
		}

		private static string SerializeRsaPublicKey(RSACryptoServiceProvider rsa)
		{
			RSAParameters rSAParameters = rsa.ExportParameters(includePrivateParameters: false);
			string text = Convert.ToBase64String(rSAParameters.Modulus ?? Array.Empty<byte>());
			string text2 = Convert.ToBase64String(rSAParameters.Exponent ?? Array.Empty<byte>());
			return text + ":" + text2;
		}

		private static RSAParameters DeserializeRsaPublicKey(string s)
		{
			string[] array = s.Split(':');
			byte[] modulus = Convert.FromBase64String(array[0]);
			byte[] exponent = Convert.FromBase64String(array[1]);
			RSAParameters result = default(RSAParameters);
			result.Modulus = modulus;
			result.Exponent = exponent;
			return result;
		}

		public void SetSharedSecret(byte[]? secret)
		{
			if (secret == null)
			{
				globalSharedSecret = null;
				globalHmac?.Dispose();
				globalHmac = null;
			}
			else
			{
				globalSharedSecret = (byte[])secret.Clone();
				globalHmac?.Dispose();
				globalHmac = new HMACSHA256(globalSharedSecret);
			}
		}

		public void RegisterModSigner(uint modId, Func<byte[], byte[]> signerDelegate)
		{
			modSigners[modId] = signerDelegate;
		}

		public void RegisterModPublicKey(uint modId, RSAParameters pub)
		{
			modPublicKeys[modId] = pub;
		}

		private void InvokeLocalMessage(Message message, CSteamID localSender)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			ulong steamID = localSender.m_SteamID;
			if ((IncomingValidator != null && !IncomingValidator(message, steamID)) || !rpcs.TryGetValue(message.ModID, out Dictionary<string, List<MessageHandler>> value) || !value.TryGetValue(message.MethodName, out var value2))
			{
				return;
			}
			foreach (MessageHandler item in value2)
			{
				if (item.Mask == message.Mask)
				{
					ParameterInfo[] parameters = item.Parameters;
					int num = (item.TakesInfo ? (parameters.Length - 1) : parameters.Length);
					object[] array = new object[parameters.Length];
					for (int i = 0; i < num; i++)
					{
						array[i] = message.ReadObject(parameters[i].ParameterType);
					}
					if (item.TakesInfo)
					{
						Type parameterType = parameters[^1].ParameterType;
						array[parameters.Length - 1] = CreateRpcInfoInstance(parameterType, localSender);
					}
					try
					{
						item.Method.Invoke(item.Target, array);
					}
					catch (Exception arg)
					{
						Net.Logger.LogError((object)$"Local invoke error: {arg}");
					}
				}
			}
		}

		private Message? BuildMessage(uint modId, string methodName, int mask, object[] parameters)
		{
			object[] parameters2 = parameters;
			try
			{
				Message message = new Message(modId, methodName, mask);
				if (rpcs.TryGetValue(modId, out Dictionary<string, List<MessageHandler>> value) && value.TryGetValue(methodName, out var value2) && value2.Count > 0)
				{
					MessageHandler messageHandler = null;
					foreach (MessageHandler item in value2)
					{
						if (item.Mask != mask)
						{
							continue;
						}
						ParameterInfo[] parameters3 = item.Parameters;
						int num = (item.TakesInfo ? (parameters3.Length - 1) : parameters3.Length);
						if (num != parameters2.Length)
						{
							continue;
						}
						bool flag = true;
						for (int i = 0; i < num; i++)
						{
							if (!parameters3[i].ParameterType.IsAssignableFrom(parameters2[i].GetType()))
							{
								flag = false;
								break;
							}
						}
						if (flag)
						{
							messageHandler = item;
							break;
						}
					}
					if (messageHandler == null)
					{
						messageHandler = value2.FirstOrDefault(delegate(MessageHandler h)
						{
							int num3 = (h.TakesInfo ? (h.Parameters.Length - 1) : h.Parameters.Length);
							return num3 == parameters2.Length && h.Mask == mask;
						}) ?? value2[0];
					}
					ParameterInfo[] parameters4 = messageHandler.Parameters;
					int num2 = (messageHandler.TakesInfo ? (parameters4.Length - 1) : parameters4.Length);
					for (int j = 0; j < num2; j++)
					{
						Type parameterType = parameters4[j].ParameterType;
						object obj = parameters2[j] ?? throw new Exception($"Null parameter {j}");
						if (!parameterType.IsAssignableFrom(obj.GetType()))
						{
							throw new Exception($"Parameter {j} type mismatch: expected {parameterType}, got {obj.GetType()}");
						}
						message.WriteObject(parameterType, obj);
					}
				}
				if (message.Length() > Message.MaxSize * 16)
				{
					Net.Logger.LogError((object)"Message exceeds maximum allowed overall size.");
					return null;
				}
				return message;
			}
			catch (Exception arg)
			{
				Net.Logger.LogError((object)$"BuildMessage failed: {arg}");
				return null;
			}
		}

		private SlidingWindowRateLimiter GetOrCreateRateLimiter(ulong steam64)
		{
			lock (rateLimiters)
			{
				if (!rateLimiters.TryGetValue(steam64, out SlidingWindowRateLimiter value))
				{
					value = new SlidingWindowRateLimiter(100, TimeSpan.FromSeconds(1.0));
					rateLimiters[steam64] = value;
				}
				return value;
			}
		}

		private static byte[] HmacSha256RawStatic(byte[] key, byte[] payload)
		{
			using HMACSHA256 hMACSHA = new HMACSHA256(key);
			return hMACSHA.ComputeHash(payload);
		}
	}
}
namespace NetworkingLibrary.Patches
{
	internal class NetworkPatch
	{
	}
}
namespace NetworkingLibrary.Modules
{
	[AttributeUsage(AttributeTargets.Method)]
	public class CustomRPCAttribute : Attribute
	{
	}
	public class Message : IDisposable
	{
		public const byte PROTOCOL_VERSION = 1;

		public static int MaxSize;

		public byte ProtocolVersion;

		public uint ModID;

		public string MethodName = string.Empty;

		public int Mask;

		private List<byte> buffer = new List<byte>();

		internal byte[] readableBuffer = Array.Empty<byte>();

		internal int readPos;

		private static readonly Dictionary<Type, Action<Message, object>> writeCasters;

		private static readonly Dictionary<Type, Func<Message, object>> readCasters;

		public bool Compressed { get; private set; }

		public Message(uint modId, string methodName, int mask)
		{
			ProtocolVersion = 1;
			ModID = modId;
			MethodName = methodName;
			Mask = mask;
			WriteByte(ProtocolVersion);
			WriteUInt(ModID);
			WriteString(MethodName);
			WriteInt(Mask);
		}

		public Message(byte[] data)
		{
			SetBytes(data);
			ProtocolVersion = ReadByte();
			ModID = ReadUInt();
			MethodName = ReadString();
			Mask = ReadInt();
		}

		public void SetBytes(byte[] data)
		{
			buffer.Clear();
			buffer.AddRange(data);
			readableBuffer = buffer.ToArray();
			readPos = 0;
		}

		public byte[] ToArray()
		{
			readableBuffer = buffer.ToArray();
			return readableBuffer;
		}

		public int Length()
		{
			return buffer.Count;
		}

		public int UnreadLength()
		{
			return Length() - readPos;
		}

		public void Reset(bool zero = true)
		{
			if (zero)
			{
				buffer.Clear();
				readableBuffer = Array.Empty<byte>();
				readPos = 0;
			}
			else
			{
				readPos -= 4;
			}
		}

		public Message WriteByte(byte v)
		{
			buffer.Add(v);
			return this;
		}

		public Message WriteBytes(byte[] v)
		{
			WriteInt(v.Length);
			buffer.AddRange(v);
			return this;
		}

		public Message WriteInt(int v)
		{
			buffer.AddRange(BitConverter.GetBytes(v));
			return this;
		}

		public Message WriteUInt(uint v)
		{
			buffer.AddRange(BitConverter.GetBytes(v));
			return this;
		}

		public Message WriteLong(long v)
		{
			buffer.AddRange(BitConverter.GetBytes(v));
			return this;
		}

		public Message WriteULong(ulong v)
		{
			buffer.AddRange(BitConverter.GetBytes(v));
			return this;
		}

		public Message WriteFloat(float v)
		{
			buffer.AddRange(BitConverter.GetBytes(v));
			return this;
		}

		public Message WriteBool(bool v)
		{
			buffer.AddRange(BitConverter.GetBytes(v));
			return this;
		}

		public Message WriteString(string v)
		{
			byte[] bytes = Encoding.UTF8.GetBytes(v ?? "");
			WriteInt(bytes.Length);
			buffer.AddRange(bytes);
			return this;
		}

		public Message WriteVector3(Vector3 v)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: 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)
			WriteFloat(v.x);
			WriteFloat(v.y);
			WriteFloat(v.z);
			return this;
		}

		public Message WriteQuaternion(Quaternion q)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: 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)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			WriteFloat(q.x);
			WriteFloat(q.y);
			WriteFloat(q.z);
			WriteFloat(q.w);
			return this;
		}

		public void WriteObject(Type type, object value)
		{
			if (type == null)
			{
				throw new ArgumentNullException("type");
			}
			if (writeCasters.TryGetValue(type, out Action<Message, object> value2))
			{
				value2(this, value);
				return;
			}
			Type underlyingType = Nullable.GetUnderlyingType(type);
			if (underlyingType != null)
			{
				bool flag = value != null;
				WriteBool(flag);
				if (flag)
				{
					WriteObject(underlyingType, value);
				}
				return;
			}
			if (type.IsEnum)
			{
				Type underlyingType2 = Enum.GetUnderlyingType(type);
				WriteObject(underlyingType2, Convert.ChangeType(value, underlyingType2));
				return;
			}
			if (type.IsArray)
			{
				Type elementType = type.GetElementType();
				Array array = (value as Array) ?? Array.CreateInstance(elementType, 0);
				WriteInt(array.Length);
				for (int i = 0; i < array.Length; i++)
				{
					WriteObject(elementType, array.GetValue(i));
				}
				return;
			}
			if (typeof(IList).IsAssignableFrom(type))
			{
				Type type2 = null;
				if (type.IsGenericType)
				{
					Type[] genericArguments = type.GetGenericArguments();
					if (genericArguments.Length == 1)
					{
						type2 = genericArguments[0];
					}
				}
				IList list = (IList)value;
				if (type2 != null)
				{
					WriteInt(list.Count);
					for (int j = 0; j < list.Count; j++)
					{
						WriteObject(type2, list[j]);
					}
					return;
				}
				throw new Exception("Cannot serialize non-generic IList (heterogeneous lists) without explicit serializer registration.");
			}
			throw new Exception("Unsupported type for WriteObject: " + type.FullName + ". Register a serializer using Message.RegisterSerializer.");
		}

		public static void RegisterSerializer<T>(Action<Message, T> writer, Func<Message, T> reader)
		{
			Action<Message, T> writer2 = writer;
			Func<Message, T> reader2 = reader;
			if (writer2 == null || reader2 == null)
			{
				throw new ArgumentNullException();
			}
			writeCasters[typeof(T)] = delegate(Message m, object o)
			{
				writer2(m, (T)o);
			};
			readCasters[typeof(T)] = (Message m) => reader2(m);
		}

		public static void UnregisterSerializer<T>()
		{
			writeCasters.Remove(typeof(T));
			readCasters.Remove(typeof(T));
		}

		static Message()
		{
			MaxSize = 65536;
			writeCasters = new Dictionary<Type, Action<Message, object>>();
			readCasters = new Dictionary<Type, Func<Message, object>>();
			writeCasters[typeof(byte)] = delegate(Message m, object o)
			{
				m.WriteByte((byte)o);
			};
			writeCasters[typeof(byte[])] = delegate(Message m, object o)
			{
				m.WriteBytes((byte[])o);
			};
			writeCasters[typeof(int)] = delegate(Message m, object o)
			{
				m.WriteInt((int)o);
			};
			writeCasters[typeof(uint)] = delegate(Message m, object o)
			{
				m.WriteUInt((uint)o);
			};
			writeCasters[typeof(long)] = delegate(Message m, object o)
			{
				m.WriteLong((long)o);
			};
			writeCasters[typeof(ulong)] = delegate(Message m, object o)
			{
				m.WriteULong((ulong)o);
			};
			writeCasters[typeof(float)] = delegate(Message m, object o)
			{
				m.WriteFloat((float)o);
			};
			writeCasters[typeof(bool)] = delegate(Message m, object o)
			{
				m.WriteBool((bool)o);
			};
			writeCasters[typeof(string)] = delegate(Message m, object o)
			{
				m.WriteString((string)o);
			};
			writeCasters[typeof(Vector3)] = delegate(Message m, object o)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				m.WriteVector3((Vector3)o);
			};
			writeCasters[typeof(Quaternion)] = delegate(Message m, object o)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				m.WriteQuaternion((Quaternion)o);
			};
			writeCasters[typeof(CSteamID)] = delegate(Message m, object o)
			{
				m.WriteULong(((CSteamID)o).m_SteamID);
			};
			writeCasters[typeof(int[])] = delegate(Message m, object o)
			{
				int[] array5 = ((int[])o) ?? Array.Empty<int>();
				m.WriteInt(array5.Length);
				for (int l = 0; l < array5.Length; l++)
				{
					m.WriteInt(array5[l]);
				}
			};
			writeCasters[typeof(string[])] = delegate(Message m, object o)
			{
				string[] array4 = ((string[])o) ?? Array.Empty<string>();
				m.WriteInt(array4.Length);
				for (int k = 0; k < array4.Length; k++)
				{
					m.WriteString(array4[k]);
				}
			};
			readCasters[typeof(byte)] = (Message m) => m.ReadByte();
			readCasters[typeof(byte[])] = delegate(Message m)
			{
				int num3 = m.ReadInt();
				if (num3 == 0)
				{
					return new byte[0];
				}
				byte[] array3 = new byte[num3];
				Array.Copy(m.readableBuffer, m.readPos, array3, 0, num3);
				m.readPos += num3;
				return array3;
			};
			readCasters[typeof(int)] = (Message m) => m.ReadInt();
			readCasters[typeof(uint)] = (Message m) => m.ReadUInt();
			readCasters[typeof(long)] = (Message m) => m.ReadLong();
			readCasters[typeof(ulong)] = (Message m) => m.ReadULong();
			readCasters[typeof(float)] = (Message m) => m.ReadFloat();
			readCasters[typeof(bool)] = (Message m) => m.ReadBool();
			readCasters[typeof(string)] = (Message m) => m.ReadString();
			readCasters[typeof(Vector3)] = (Message m) => m.ReadVector3();
			readCasters[typeof(Quaternion)] = (Message m) => m.ReadQuaternion();
			readCasters[typeof(CSteamID)] = (Message m) => (object)new CSteamID(m.ReadULong());
			readCasters[typeof(int[])] = delegate(Message m)
			{
				int num2 = m.ReadInt();
				if (num2 == 0)
				{
					return new int[0];
				}
				int[] array2 = new int[num2];
				for (int j = 0; j < num2; j++)
				{
					array2[j] = m.ReadInt();
				}
				return array2;
			};
			readCasters[typeof(string[])] = delegate(Message m)
			{
				int num = m.ReadInt();
				if (num == 0)
				{
					return new string[0];
				}
				string[] array = new string[num];
				for (int i = 0; i < num; i++)
				{
					array[i] = m.ReadString();
				}
				return array;
			};
		}

		public byte ReadByte()
		{
			if (buffer.Count > readPos)
			{
				byte result = readableBuffer[readPos];
				readPos++;
				return result;
			}
			throw new Exception("ReadByte out of range");
		}

		public int ReadInt()
		{
			if (buffer.Count > readPos)
			{
				int result = BitConverter.ToInt32(readableBuffer, readPos);
				readPos += 4;
				return result;
			}
			throw new Exception("ReadInt out of range");
		}

		public uint ReadUInt()
		{
			if (buffer.Count > readPos)
			{
				uint result = BitConverter.ToUInt32(readableBuffer, readPos);
				readPos += 4;
				return result;
			}
			throw new Exception("ReadUInt out of range");
		}

		public long ReadLong()
		{
			if (buffer.Count > readPos)
			{
				long result = BitConverter.ToInt64(readableBuffer, readPos);
				readPos += 8;
				return result;
			}
			throw new Exception("ReadLong out of range");
		}

		public ulong ReadULong()
		{
			if (buffer.Count > readPos)
			{
				ulong result = BitConverter.ToUInt64(readableBuffer, readPos);
				readPos += 8;
				return result;
			}
			throw new Exception("ReadULong out of range");
		}

		public float ReadFloat()
		{
			if (buffer.Count > readPos)
			{
				float result = BitConverter.ToSingle(readableBuffer, readPos);
				readPos += 4;
				return result;
			}
			throw new Exception("ReadFloat out of range");
		}

		public bool ReadBool()
		{
			if (buffer.Count > readPos)
			{
				bool result = BitConverter.ToBoolean(readableBuffer, readPos);
				readPos++;
				return result;
			}
			throw new Exception("ReadBool out of range");
		}

		public string ReadString()
		{
			int num = ReadInt();
			if (num == 0)
			{
				return string.Empty;
			}
			if (buffer.Count > readPos)
			{
				string @string = Encoding.UTF8.GetString(readableBuffer, readPos, num);
				readPos += num;
				return @string;
			}
			throw new Exception("ReadString out of range");
		}

		public Vector3 ReadVector3()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3(ReadFloat(), ReadFloat(), ReadFloat());
		}

		public Quaternion ReadQuaternion()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			return new Quaternion(ReadFloat(), ReadFloat(), ReadFloat(), ReadFloat());
		}

		public object ReadObject(Type type)
		{
			if (type == null)
			{
				throw new ArgumentNullException("type");
			}
			if (readCasters.TryGetValue(type, out Func<Message, object> value))
			{
				return value(this);
			}
			Type underlyingType = Nullable.GetUnderlyingType(type);
			if (underlyingType != null)
			{
				if (!ReadBool())
				{
					return null;
				}
				return ReadObject(underlyingType);
			}
			if (type.IsEnum)
			{
				Type underlyingType2 = Enum.GetUnderlyingType(type);
				object value2 = ReadObject(underlyingType2);
				return Enum.ToObject(type, value2);
			}
			if (type.IsArray)
			{
				Type elementType = type.GetElementType();
				int num = ReadInt();
				Array array = Array.CreateInstance(elementType, num);
				for (int i = 0; i < num; i++)
				{
					object value3 = ReadObject(elementType);
					array.SetValue(value3, i);
				}
				return array;
			}