Decompiled source of PEAKNetworkingLibrary v1.0.9

AeralisFoundation.NetworkingLibrary.dll

Decompiled 2 weeks ago
#define TRACE
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
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 ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using NetworkingLibrary.Features;
using NetworkingLibrary.Modules;
using NetworkingLibrary.Services;
using Photon.Pun;
using Photon.Realtime;
using Steamworks;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("NetworkingLibrary.Tests")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("AeralisFoundation")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Networking library for mods using Steam instead of PUN.")]
[assembly: AssemblyFileVersion("1.0.9.0")]
[assembly: AssemblyInformationalVersion("1.0.9+0f1c2d7274cf8f619512eace85de587430684362")]
[assembly: AssemblyProduct("NetworkingLibrary")]
[assembly: AssemblyTitle("AeralisFoundation.NetworkingLibrary")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.9.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;
		}
	}
}
namespace NetworkingLibrary
{
	[BepInPlugin("AeralisFoundation.NetworkingLibrary", "NetworkingLibrary", "1.0.9")]
	public class Net : BaseUnityPlugin
	{
		internal static Harmony? Harmony;

		public ConfigFile config;

		internal static Func<(INetworkingService service, DefaultServiceSelectionReason reason)> CreateDefaultNetworkingServiceWithReason = delegate
		{
			DefaultServiceSelectionReason reason;
			INetworkingService item = NetworkingServiceFactory.CreateDefaultServiceWithReason(out reason);
			return (item, reason);
		};

		internal static Func<INetworkingService> CreateOfflineNetworkingService = () => new OfflineNetworkingService();

		internal static Func<bool> IsApplicationPlaying = () => Application.isPlaying;

		internal static Action<Object> DestroyObject = Object.Destroy;

		internal static Action<Object> DestroyObjectImmediate = Object.DestroyImmediate;

		internal static Func<float> RealtimeSinceStartupProvider = () => Time.realtimeSinceStartup;

		internal static Func<float, object?> WaitForSecondsRealtimeFactory = (float seconds) => (object?)new WaitForSecondsRealtime(seconds);

		private const string StartupRetryLogSource = "Net.StartupRetry";

		private const string StartupRetryTransitionLogKey = "Net.StartupRetry.Transition";

		private const float StartupRetryTransitionLogCooldownSeconds = 1.5f;

		private const float StartupRetryWindowSeconds = 10f;

		private const float StartupRetryInitialDelaySeconds = 0.5f;

		private const float StartupRetryMaxDelaySeconds = 3f;

		private const float StartupRetryBackoffMultiplier = 1.8f;

		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()
		{
			try
			{
				Service?.Shutdown();
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Logger;
				if (logger != null)
				{
					logger.LogWarning((object)("Failed to shutdown networking service during plugin teardown: " + ex.Message));
				}
			}
			finally
			{
				Service = null;
				ResetNetworkingStartupHooks();
				Message.ResetSerializersForTests();
			}
			try
			{
				Harmony harmony = Harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
					ManualLogSource logger2 = Logger;
					if (logger2 != null)
					{
						logger2.LogDebug((object)"Removed Harmony patches during plugin teardown.");
					}
				}
			}
			catch (Exception ex2)
			{
				ManualLogSource logger3 = Logger;
				if (logger3 != null)
				{
					logger3.LogWarning((object)("Failed to unpatch Harmony during plugin teardown: " + ex2.Message));
				}
			}
			finally
			{
				Harmony = null;
				Instance = null;
			}
		}

		private void Awake()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Expected O, but got Unknown
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			Logger = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			FileManager.InitializeConfig();
			try
			{
				if (Harmony == null)
				{
					Harmony = new Harmony("AeralisFoundation.NetworkingLibrary");
				}
				Harmony.PatchAll();
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"Failed to apply Harmony patches: {arg}");
			}
			Service = null;
			TryInitializeNetworkingService(Logger, out INetworkingService service, out DefaultServiceSelectionReason defaultSelectionReason);
			Service = service;
			if (defaultSelectionReason == DefaultServiceSelectionReason.SteamApiNotReady && Service is OfflineNetworkingService)
			{
				((MonoBehaviour)this).StartCoroutine(RetrySteamInitializationForStartupWindow(10f));
			}
			string text = "NetworkingLibrary.Poller";
			List<NetworkingPoller> list = (from poller in Object.FindObjectsByType<NetworkingPoller>((FindObjectsInactive)1, (FindObjectsSortMode)0)
				orderby ((Behaviour)poller).isActiveAndEnabled descending, ((Component)poller).gameObject.activeInHierarchy descending, ((Object)poller).GetInstanceID()
				select poller).ToList();
			NetworkingPoller networkingPoller = list.FirstOrDefault();
			if ((Object)(object)networkingPoller == (Object)null)
			{
				GameObject val = new GameObject(text);
				networkingPoller = val.AddComponent<NetworkingPoller>();
				((Object)networkingPoller).hideFlags = (HideFlags)61;
			}
			CleanupDuplicatePollers(networkingPoller, list.Skip(1), DestroyPollerDuringStartup);
			GameObject gameObject = ((Component)networkingPoller).gameObject;
			((Object)gameObject).name = text;
			if (list.Count > 0)
			{
				Scene scene = gameObject.scene;
				if (((Scene)(ref scene)).IsValid())
				{
					scene = gameObject.scene;
					if (((Scene)(ref scene)).name != "DontDestroyOnLoad")
					{
						ManualLogSource logger = Logger;
						string[] obj = new string[5]
						{
							"Promoting existing poller object '",
							((Object)gameObject).name,
							"' from scene '",
							null,
							null
						};
						scene = gameObject.scene;
						obj[3] = ((Scene)(ref scene)).name;
						obj[4] = "' to DontDestroyOnLoad lifecycle.";
						logger.LogDebug((object)string.Concat(obj));
					}
				}
			}
			Object.DontDestroyOnLoad((Object)(object)gameObject);
			Logger.LogInfo((object)"NetworkingLibrary v1.0.9 has fully loaded!");
		}

		private static void CleanupDuplicatePollers(NetworkingPoller canonicalPoller, IEnumerable<NetworkingPoller> duplicatePollers, Action<Object> destroyAction)
		{
			foreach (NetworkingPoller duplicatePoller in duplicatePollers)
			{
				if (!((Object)(object)duplicatePoller == (Object)null))
				{
					GameObject gameObject = ((Component)duplicatePoller).gameObject;
					if (!((Object)(object)gameObject == (Object)null))
					{
						bool flag = HasOnlyTransformAndPollerComponents(gameObject);
						bool flag2 = flag && (Object)(object)gameObject.transform != (Object)null && ((Component)canonicalPoller).transform.IsChildOf(gameObject.transform);
						destroyAction((Object)(object)((flag && !flag2) ? ((NetworkingPoller)(object)gameObject) : duplicatePoller));
					}
				}
			}
		}

		private static bool HasOnlyTransformAndPollerComponents(GameObject pollerObject)
		{
			Component[] components = pollerObject.GetComponents<Component>();
			bool flag = false;
			bool flag2 = false;
			int num = 0;
			foreach (Component val in components)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				num++;
				if (val is Transform)
				{
					flag = true;
					continue;
				}
				if (val is NetworkingPoller)
				{
					flag2 = true;
					continue;
				}
				return false;
			}
			return num == 2 && flag && flag2;
		}

		private static void DestroyPollerDuringStartup(Object target)
		{
			if (target is NetworkingPoller networkingPoller)
			{
				((Behaviour)networkingPoller).enabled = false;
			}
			else
			{
				GameObject val = (GameObject)(object)((target is GameObject) ? target : null);
				if (val != null)
				{
					val.SetActive(false);
				}
			}
			if (IsApplicationPlaying())
			{
				DestroyObject(target);
			}
			else
			{
				DestroyObjectImmediate(target);
			}
		}

		internal static bool TryInitializeNetworkingService(ManualLogSource? logger, out INetworkingService? service)
		{
			DefaultServiceSelectionReason defaultSelectionReason;
			return TryInitializeNetworkingService(logger, out service, out defaultSelectionReason);
		}

		internal static bool TryInitializeNetworkingService(ManualLogSource? logger, out INetworkingService? service, out DefaultServiceSelectionReason defaultSelectionReason)
		{
			service = null;
			defaultSelectionReason = DefaultServiceSelectionReason.ProbeFailed;
			try
			{
				(INetworkingService, DefaultServiceSelectionReason) tuple = CreateDefaultNetworkingServiceWithReason();
				defaultSelectionReason = tuple.Item2;
				(service, _) = tuple;
				service.Initialize();
				if (!service.IsInitialized)
				{
					if (logger != null)
					{
						logger.LogError((object)("Default networking service '" + service.GetType().Name + "' initialization returned without becoming active. Attempting OfflineNetworkingService fallback."));
					}
					try
					{
						service.Shutdown();
					}
					catch (Exception arg)
					{
						if (logger != null)
						{
							logger.LogWarning((object)$"Failed to shutdown uninitialized default networking service '{service.GetType().Name}' during fallback: {arg}");
						}
					}
					service = null;
					throw new InvalidOperationException("Default networking service initialization completed without becoming active.");
				}
				return true;
			}
			catch (Exception arg2)
			{
				SafeShutdown(service, "failed default networking service initialization");
				service = null;
				if (logger != null)
				{
					logger.LogError((object)$"Failed to initialize default networking service. Attempting OfflineNetworkingService fallback. Exception: {arg2}");
				}
			}
			try
			{
				service = CreateOfflineNetworkingService();
				service.Initialize();
				if (!service.IsInitialized)
				{
					if (logger != null)
					{
						logger.LogError((object)("Fallback networking service '" + service.GetType().Name + "' initialization returned without becoming active. Networking service disabled."));
					}
					try
					{
						service.Shutdown();
					}
					catch (Exception arg3)
					{
						if (logger != null)
						{
							logger.LogWarning((object)$"Failed to shutdown uninitialized fallback networking service '{service.GetType().Name}': {arg3}");
						}
					}
					service = null;
					return false;
				}
				return true;
			}
			catch (Exception arg4)
			{
				SafeShutdown(service, "failed fallback OfflineNetworkingService initialization");
				if (logger != null)
				{
					logger.LogError((object)$"FATAL: Failed to initialize fallback OfflineNetworkingService. Networking service disabled. Exception: {arg4}");
				}
				service = null;
				return false;
			}
		}

		private IEnumerator RetrySteamInitializationForStartupWindow(float retryWindowSeconds)
		{
			float delaySeconds = 0.5f;
			float deadline = RealtimeSinceStartupProvider() + Mathf.Max(0.1f, retryWindowSeconds);
			while (RealtimeSinceStartupProvider() < deadline)
			{
				yield return WaitForSecondsRealtimeFactory(delaySeconds);
				INetworkingService service = Service;
				if (service == null)
				{
					yield break;
				}
				INetworkingService networkingService;
				DefaultServiceSelectionReason defaultServiceSelectionReason;
				try
				{
					(networkingService, defaultServiceSelectionReason) = CreateDefaultNetworkingServiceWithReason();
				}
				catch (Exception ex)
				{
					LogStartupRetryTransitionThrottled("Aborting startup retries: default service creation threw " + ex.GetType().Name + ": " + ex.Message);
					yield break;
				}
				if (defaultServiceSelectionReason == DefaultServiceSelectionReason.SteamReady)
				{
					try
					{
						networkingService.Initialize();
						if (!networkingService.IsInitialized)
						{
							LogStartupRetryTransitionThrottled("Steam readiness probe passed but Steam service did not initialize yet; continuing startup retries.");
							SafeShutdown(networkingService, "startup retry steam candidate");
							delaySeconds = Mathf.Min(3f, delaySeconds * 1.8f);
							continue;
						}
						if (Service == service)
						{
							if (service.InLobby)
							{
								LogStartupRetryTransitionThrottled("Skipping Steam promotion during startup retry because the active service is currently in a lobby.");
								SafeShutdown(networkingService, "startup retry steam candidate while active lobby");
								delaySeconds = Mathf.Min(3f, delaySeconds * 1.8f);
								continue;
							}
							ReplaceService(service, networkingService, "Steam became ready during startup retry window.");
							yield break;
						}
						SafeShutdown(networkingService, "startup retry stale steam candidate");
						yield break;
					}
					catch (Exception ex2)
					{
						SafeShutdown(networkingService, "startup retry steam candidate after initialize exception");
						LogStartupRetryTransitionThrottled("Aborting startup retries: Steam service initialization threw " + ex2.GetType().Name + ": " + ex2.Message);
						yield break;
					}
				}
				SafeShutdown(networkingService, "startup retry discarded candidate");
				if (defaultServiceSelectionReason == DefaultServiceSelectionReason.SteamApiNotReady)
				{
					delaySeconds = Mathf.Min(3f, delaySeconds * 1.8f);
					continue;
				}
				LogStartupRetryTransitionThrottled($"Aborting startup retries: default selection reason is {defaultServiceSelectionReason}.");
				yield break;
			}
			LogStartupRetryTransitionThrottled("Startup retry window elapsed before Steam became ready.");
		}

		private static void ReplaceService(INetworkingService previousService, INetworkingService nextService, string reason)
		{
			if (previousService == nextService)
			{
				return;
			}
			try
			{
				if (previousService is INetworkingServiceStateTransfer networkingServiceStateTransfer)
				{
					networkingServiceStateTransfer.CopyRuntimeStateTo(nextService);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Logger;
				if (logger != null)
				{
					logger.LogWarning((object)("Failed to migrate networking runtime state before service replacement: " + ex.Message));
				}
				SafeShutdown(nextService, "startup retry failed replacement candidate");
				return;
			}
			SafeShutdown(previousService, "startup retry previous service");
			Service = nextService;
			LogStartupRetryTransitionThrottled("Service transition completed: " + previousService.GetType().Name + " -> " + nextService.GetType().Name + ". Reason: " + reason);
		}

		private static void SafeShutdown(INetworkingService? service, string context)
		{
			if (service == null)
			{
				return;
			}
			try
			{
				service.Shutdown();
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Logger;
				if (logger != null)
				{
					logger.LogWarning((object)("Failed to shutdown networking service during " + context + ": " + ex.Message));
				}
			}
		}

		private static void LogStartupRetryTransitionThrottled(string message)
		{
			NetLog.DebugThrottled("Net.StartupRetry", "Net.StartupRetry.Transition", 1.5, message, () => RealtimeSinceStartupProvider(), includeOriginalMessageInFallback: true);
		}

		internal static void ResetNetworkingStartupHooks()
		{
			CreateDefaultNetworkingServiceWithReason = delegate
			{
				DefaultServiceSelectionReason reason;
				INetworkingService item = NetworkingServiceFactory.CreateDefaultServiceWithReason(out reason);
				return (item, reason);
			};
			CreateOfflineNetworkingService = () => new OfflineNetworkingService();
			IsApplicationPlaying = () => Application.isPlaying;
			DestroyObject = Object.Destroy;
			DestroyObjectImmediate = Object.DestroyImmediate;
			RealtimeSinceStartupProvider = () => Time.realtimeSinceStartup;
			WaitForSecondsRealtimeFactory = (float seconds) => (object?)new WaitForSecondsRealtime(seconds);
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "AeralisFoundation.NetworkingLibrary";

		public const string PLUGIN_NAME = "NetworkingLibrary";

		public const string PLUGIN_VERSION = "1.0.9";
	}
}
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 RPC(uint modId, string methodName, ReliableType reliable, Type[] parameterTypes, params object?[] parameters);

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

		void RPCTarget(uint modId, string methodName, ulong targetSteamId64, ReliableType reliable, Type[] parameterTypes, 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);
	}
	internal interface INetworkingServiceStateTransfer
	{
		void CopyRuntimeStateTo(INetworkingService target);
	}
	public class NetworkingPoller : MonoBehaviour
	{
		private const string LogSource = "NetworkingPoller";

		private const float ErrorLogCooldownSeconds = 2f;

		internal static Func<float> TimeProvider = () => Time.unscaledTime;

		internal static Func<double> FallbackTimeProvider = () => DateTime.UtcNow.Subtract(DateTime.UnixEpoch).TotalSeconds;

		private double mainThreadDispatcherLastErrorLogTime = double.NegativeInfinity;

		private bool mainThreadDispatcherHadFault;

		private bool mainThreadDispatcherSuppressedFault;

		private int mainThreadDispatcherSuppressedExceptionCount;

		private double pollReceiveLastErrorLogTime = double.NegativeInfinity;

		private bool pollReceiveHadFault;

		private bool pollReceiveSuppressedFault;

		private int pollReceiveSuppressedExceptionCount;

		private void Update()
		{
			PollGuarded(UnityMainThreadDispatcher.ProcessPendingMainThreadWork, "Main-thread dispatcher", ref mainThreadDispatcherLastErrorLogTime, ref mainThreadDispatcherHadFault, ref mainThreadDispatcherSuppressedFault, ref mainThreadDispatcherSuppressedExceptionCount);
			PollGuarded(delegate
			{
				Net.Service?.PollReceive();
			}, "PollReceive", ref pollReceiveLastErrorLogTime, ref pollReceiveHadFault, ref pollReceiveSuppressedFault, ref pollReceiveSuppressedExceptionCount);
		}

		private static void PollGuarded(Action action, string name, ref double lastErrorLogTime, ref bool hadFault, ref bool suppressedFault, ref int suppressedExceptionCount)
		{
			bool flag = true;
			try
			{
				action();
			}
			catch (Exception arg)
			{
				flag = false;
				hadFault = true;
				double num = ResolveTime();
				if (num < lastErrorLogTime || num - lastErrorLogTime >= 2.0)
				{
					lastErrorLogTime = num;
					NetLog.Error("NetworkingPoller", $"{name} error: {arg}");
					suppressedFault = false;
					suppressedExceptionCount = 0;
				}
				else
				{
					suppressedFault = true;
					suppressedExceptionCount++;
				}
			}
			if (flag && hadFault)
			{
				NetLog.Info("NetworkingPoller", suppressedFault ? $"{name} recovered after repeated failures. Suppressed {suppressedExceptionCount} errors since last emitted error." : $"{name} recovered. Suppressed {suppressedExceptionCount} errors since last emitted error.");
				hadFault = false;
				suppressedFault = false;
				suppressedExceptionCount = 0;
			}
		}

		private static double ResolveTime()
		{
			try
			{
				float num = TimeProvider();
				if (!float.IsNaN(num) && !float.IsInfinity(num))
				{
					return num;
				}
			}
			catch
			{
			}
			try
			{
				double num2 = FallbackTimeProvider();
				if (!double.IsNaN(num2) && !double.IsInfinity(num2))
				{
					return num2;
				}
			}
			catch
			{
			}
			return DateTime.UtcNow.Subtract(DateTime.UnixEpoch).TotalSeconds;
		}

		private void Awake()
		{
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
		}
	}
	public enum DefaultServiceSelectionReason
	{
		SteamReady,
		SteamClientNotRunning,
		SteamApiNotReady,
		ProbeFailed,
		UnityEditorOffline
	}
	public static class NetworkingServiceFactory
	{
		private const string LogSource = "NetworkingServiceFactory";

		private const float ProbeDebugLogCooldownSeconds = 2f;

		private static readonly string[] steamManagerTypeCandidates = new string[3] { "pworld.Scripts.SteamManager, Assembly-CSharp", "SteamManager, Assembly-CSharp", "SteamManager" };

		internal static Func<bool> IsSteamClientRunning = () => SteamAPI.IsSteamRunning();

		internal static Func<bool> IsSteamApiInitialized = ProbeSteamApiInitialized;

		internal static Func<INetworkingService> CreateSteamService = () => new SteamNetworkingService();

		internal static Func<INetworkingService> CreateOfflineService = () => new OfflineNetworkingService();

		internal static Func<string, Type?> ResolveType = Type.GetType;

		internal static Func<float> UnscaledTimeProvider = () => Time.unscaledTime;

		public static INetworkingService CreateDefaultService()
		{
			DefaultServiceSelectionReason reason;
			return CreateDefaultServiceWithReason(out reason);
		}

		internal static INetworkingService CreateDefaultServiceWithReason(out DefaultServiceSelectionReason reason)
		{
			DefaultServiceSelectionReason defaultServiceSelectionReason = DefaultServiceSelectionReason.SteamApiNotReady;
			try
			{
				bool flag = IsSteamClientRunning();
				NetLog.Info("NetworkingServiceFactory", $"Steam client running: {flag}.");
				if (!flag)
				{
					defaultServiceSelectionReason = DefaultServiceSelectionReason.SteamClientNotRunning;
					NetLog.Info("NetworkingServiceFactory", "Falling back to OfflineNetworkingService. Reason: Steam client is not running.");
				}
				else
				{
					bool flag2 = IsSteamApiInitialized();
					NetLog.Info("NetworkingServiceFactory", $"Steam API initialized: {flag2}.");
					if (flag2)
					{
						reason = DefaultServiceSelectionReason.SteamReady;
						NetLog.Info("NetworkingServiceFactory", "Steam ready. Creating SteamNetworkingService.");
						return CreateSteamService();
					}
					defaultServiceSelectionReason = DefaultServiceSelectionReason.SteamApiNotReady;
					NetLog.Info("NetworkingServiceFactory", "Falling back to OfflineNetworkingService. Reason: Steam API is not initialized.");
				}
			}
			catch (Exception arg)
			{
				reason = DefaultServiceSelectionReason.ProbeFailed;
				NetLog.Error("NetworkingServiceFactory", $"Steam readiness probe failed. Falling back to OfflineNetworkingService. Exception: {arg}");
				return CreateOfflineService();
			}
			reason = defaultServiceSelectionReason;
			return CreateOfflineService();
		}

		private static bool ProbeSteamApiInitialized()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if (TryReadSteamManagerInitialized(out var isInitialized))
			{
				return isInitialized;
			}
			try
			{
				return SteamUser.GetSteamID() != CSteamID.Nil;
			}
			catch (Exception ex)
			{
				NetLog.DebugThrottled("NetworkingServiceFactory", "NetworkingServiceFactory.ProbeSteamApiInitialized", 2.0, "ProbeSteamApiInitialized fallback SteamUser.GetSteamID failed: " + ex.GetType().Name + ": " + ex.Message, () => UnscaledTimeProvider(), includeOriginalMessageInFallback: true);
				return false;
			}
		}

		private static bool TryReadSteamManagerInitialized(out bool isInitialized)
		{
			isInitialized = false;
			try
			{
				string[] array = steamManagerTypeCandidates;
				foreach (string text in array)
				{
					Type type = ResolveType(text);
					if (!(type == null) && ShouldUseResolvedSteamManagerCandidate(text, type) && TryReadInitializedSafely(type, out isInitialized))
					{
						return true;
					}
				}
				Type type2 = ResolveLoadedSteamManagerType();
				if (type2 != null && TryReadInitializedSafely(type2, out isInitialized))
				{
					return true;
				}
				return false;
			}
			catch (Exception ex)
			{
				NetLog.DebugThrottled("NetworkingServiceFactory", "NetworkingServiceFactory.TryReadSteamManagerInitialized", 2.0, "TryReadSteamManagerInitialized reflection probe failed: " + ex.GetType().Name + ": " + ex.Message, () => UnscaledTimeProvider(), includeOriginalMessageInFallback: true);
				return false;
			}
		}

		private static bool TryReadInitializedSafely(Type steamManagerType, out bool isInitialized)
		{
			isInitialized = false;
			try
			{
				return TryReadInitializedFromType(steamManagerType, out isInitialized);
			}
			catch (Exception ex)
			{
				NetLog.DebugThrottled("NetworkingServiceFactory", "NetworkingServiceFactory.TryReadInitializedSafely", 2.0, "TryReadInitializedFromType failed for '" + (steamManagerType.FullName ?? steamManagerType.Name) + "': " + ex.GetType().Name + ": " + ex.Message, () => UnscaledTimeProvider(), includeOriginalMessageInFallback: true);
				return false;
			}
		}

		private static bool ShouldUseResolvedSteamManagerCandidate(string candidateTypeName, Type steamManagerType)
		{
			if (string.Equals(candidateTypeName, "SteamManager", StringComparison.Ordinal))
			{
				return IsPreferredSteamManagerType(steamManagerType);
			}
			return true;
		}

		private static Type? ResolveLoadedSteamManagerType()
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if (assembly == null || assembly.IsDynamic)
				{
					continue;
				}
				Type[] array;
				try
				{
					array = assembly.GetTypes();
				}
				catch (ReflectionTypeLoadException exception)
				{
					array = GetLoadableTypes(exception);
				}
				catch
				{
					continue;
				}
				foreach (Type type in array)
				{
					if (!(type == null) && string.Equals(type.Name, "SteamManager", StringComparison.Ordinal) && (!(type.GetProperty("Initialized", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.PropertyType != typeof(bool)) || !(type.GetField("Initialized", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.FieldType != typeof(bool))) && IsPreferredSteamManagerType(type))
					{
						return type;
					}
				}
			}
			return null;
		}

		private static Type[] GetLoadableTypes(ReflectionTypeLoadException exception)
		{
			Type[] types = exception.Types;
			int num = 0;
			for (int i = 0; i < types.Length; i++)
			{
				if (types[i] != null)
				{
					num++;
				}
			}
			if (num == 0)
			{
				return Array.Empty<Type>();
			}
			Type[] array = new Type[num];
			int num2 = 0;
			foreach (Type type in types)
			{
				if (!(type == null))
				{
					array[num2++] = type;
				}
			}
			return array;
		}

		private static bool IsPreferredSteamManagerType(Type steamManagerType)
		{
			string fullName = steamManagerType.FullName;
			if (string.Equals(fullName, "pworld.Scripts.SteamManager", StringComparison.Ordinal))
			{
				return true;
			}
			if (string.Equals(fullName, "SteamManager", StringComparison.Ordinal))
			{
				return true;
			}
			string name = steamManagerType.Assembly.GetName().Name;
			return string.Equals(name, "Assembly-CSharp", StringComparison.Ordinal);
		}

		private static bool TryReadInitializedFromType(Type steamManagerType, out bool isInitialized)
		{
			isInitialized = false;
			PropertyInfo property = steamManagerType.GetProperty("Initialized", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (property?.PropertyType == typeof(bool))
			{
				isInitialized = (bool)(property.GetValue(null) ?? ((object)false));
				return true;
			}
			FieldInfo field = steamManagerType.GetField("Initialized", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (field?.FieldType == typeof(bool))
			{
				isInitialized = (bool)(field.GetValue(null) ?? ((object)false));
				return true;
			}
			return false;
		}

		internal static void ResetTestHooks()
		{
			IsSteamClientRunning = () => SteamAPI.IsSteamRunning();
			IsSteamApiInitialized = ProbeSteamApiInitialized;
			CreateSteamService = () => new SteamNetworkingService();
			CreateOfflineService = () => new OfflineNetworkingService();
			ResolveType = Type.GetType;
			UnscaledTimeProvider = () => Time.unscaledTime;
		}
	}
	public class OfflineNetworkingService : INetworkingService, INetworkingServiceStateTransfer
	{
		private sealed class RegistrationTransferToken : IDisposable
		{
			private readonly object sync = new object();

			private Action? onDispose;

			private bool isDisposed;

			public void SetDisposeAction(Action? action)
			{
				Action action2 = null;
				bool flag = false;
				lock (sync)
				{
					if (isDisposed)
					{
						flag = true;
					}
					else
					{
						action2 = onDispose;
						onDispose = action;
					}
				}
				if (flag)
				{
					action?.Invoke();
				}
				else
				{
					action2?.Invoke();
				}
			}

			public void Dispose()
			{
				Action action;
				lock (sync)
				{
					if (isDisposed)
					{
						return;
					}
					isDisposed = true;
					action = onDispose;
					onDispose = null;
				}
				action?.Invoke();
			}
		}

		private sealed class HandlerRegistration
		{
			public string MethodName = string.Empty;

			public MessageHandler Handler;
		}

		private sealed class RuntimeRegistration
		{
			public object? Instance;

			public Type? Type;

			public uint ModId;

			public int Mask;

			public RegistrationTransferToken Token;
		}

		private class SlidingWindowRateLimiter
		{
			private readonly int limit;

			private readonly TimeSpan window;

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

			private readonly object qLock = new object();

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

			public bool IncomingAllowed()
			{
				lock (qLock)
				{
					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;

			public int ParameterCountWithoutRpcInfo;

			public string OverloadKey = string.Empty;
		}

		private readonly object rpcLock = new object();

		private readonly object cryptoStateLock = new object();

		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 MessageSizePolicy messageSizePolicy;

		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 static readonly TimeSpan LogFallbackCooldown = TimeSpan.FromSeconds(5.0);

		private static readonly object logFallbackLock = new object();

		private static DateTime lastLogFallbackUtc = DateTime.MinValue;

		private static int suppressedLogFallbackCount;

		private static readonly TimeSpan DeserializeFailureCooldown = TimeSpan.FromSeconds(2.0);

		private static readonly object deserializeFailureLock = new object();

		private static DateTime lastDeserializeFailureUtc = DateTime.MinValue;

		private static int suppressedDeserializeFailureCount;

		private static readonly TimeSpan ExceptionLogCooldown = TimeSpan.FromSeconds(3.0);

		private static readonly object exceptionLogThrottleLock = new object();

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

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

		internal static Func<DateTime> UtcNow = () => DateTime.UtcNow;

		private long _nextMessageId;

		private bool offlineIsHost;

		private readonly List<RuntimeRegistration> runtimeRegistrations = new List<RuntimeRegistration>();

		public bool IsInitialized { get; private set; }

		public bool InLobby { get; private set; }

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


		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;

		private static void LogError(string message)
		{
			try
			{
				ManualLogSource logger = Net.Logger;
				if (logger == null)
				{
					throw new InvalidOperationException("Net logger is unavailable.");
				}
				logger.LogError((object)message);
			}
			catch (Exception ex)
			{
				LogFallbackWarningThrottled("error", message, ex);
			}
		}

		private static void LogWarning(string message)
		{
			try
			{
				ManualLogSource logger = Net.Logger;
				if (logger == null)
				{
					throw new InvalidOperationException("Net logger is unavailable.");
				}
				logger.LogWarning((object)message);
			}
			catch (Exception ex)
			{
				LogFallbackWarningThrottled("warning", message, ex);
			}
		}

		private static void LogFallbackWarningThrottled(string level, string originalMessage, Exception ex)
		{
			DateTime now = UtcNow();
			lock (logFallbackLock)
			{
				if (IsInCooldown(now, lastLogFallbackUtc, LogFallbackCooldown))
				{
					suppressedLogFallbackCount++;
					return;
				}
				int num = suppressedLogFallbackCount;
				suppressedLogFallbackCount = 0;
				lastLogFallbackUtc = now;
				string text = ((num > 0) ? $" Suppressed {num} similar logger failures." : string.Empty);
				string text2 = "[OfflineNetworkingService] Failed to write " + level + " log. Exception: " + ex.GetType().Name + ": " + ex.Message + ". Original message: " + originalMessage + "." + text;
				try
				{
					Debug.LogWarning((object)text2);
				}
				catch
				{
					Trace.TraceWarning(text2);
				}
			}
		}

		private static string FormatException(Exception ex)
		{
			return ex.GetType().Name + ": " + ex.Message;
		}

		private static void LogDeserializeFailureThrottled(Exception ex, string context)
		{
			DateTime now = UtcNow();
			lock (deserializeFailureLock)
			{
				if (IsInCooldown(now, lastDeserializeFailureUtc, DeserializeFailureCooldown))
				{
					suppressedDeserializeFailureCount++;
					return;
				}
				int num = suppressedDeserializeFailureCount;
				suppressedDeserializeFailureCount = 0;
				lastDeserializeFailureUtc = now;
				string text = ((num > 0) ? $" Suppressed {num} similar deserialization failures." : string.Empty);
				LogWarning("Offline RPC deserialization skipped handler (" + context + "). Exception: " + FormatException(ex) + "." + text);
			}
		}

		private static void LogExceptionThrottled(string key, bool error, string messagePrefix, Exception ex)
		{
			DateTime dateTime = UtcNow();
			lock (exceptionLogThrottleLock)
			{
				if (lastExceptionLogByKey.TryGetValue(key, out var value) && IsInCooldown(dateTime, value, ExceptionLogCooldown))
				{
					suppressedExceptionLogByKey[key] = ((!suppressedExceptionLogByKey.TryGetValue(key, out var value2)) ? 1 : (value2 + 1));
					return;
				}
				lastExceptionLogByKey[key] = dateTime;
				int value3;
				int num = (suppressedExceptionLogByKey.TryGetValue(key, out value3) ? value3 : 0);
				suppressedExceptionLogByKey.Remove(key);
				string text = ((num > 0) ? $" Suppressed {num} similar exceptions." : string.Empty);
				string message = messagePrefix + " Exception: " + FormatException(ex) + "." + text;
				if (error)
				{
					LogError(message);
				}
				else
				{
					LogWarning(message);
				}
			}
		}

		private static bool IsInCooldown(DateTime now, DateTime previous, TimeSpan cooldown)
		{
			if (previous != DateTime.MinValue && now >= previous)
			{
				return now - previous < cooldown;
			}
			return false;
		}

		public ulong GetLocalSteam64()
		{
			return LocalSteamId;
		}

		public ulong[] GetLobbyMemberSteamIds()
		{
			lock (rpcLock)
			{
				if (!InLobby || perPlayerData.Count == 0)
				{
					return Array.Empty<ulong>();
				}
				ulong[] array = new ulong[perPlayerData.Count];
				int num = 0;
				foreach (ulong key in perPlayerData.Keys)
				{
					array[num++] = key;
				}
				Array.Sort(array);
				return array;
			}
		}

		public void RegisterModSigner(uint modId, Func<byte[], byte[]> signerDelegate)
		{
			if (signerDelegate == null)
			{
				throw new ArgumentNullException("signerDelegate");
			}
			lock (cryptoStateLock)
			{
				modSigners[modId] = signerDelegate;
			}
		}

		public void RegisterModPublicKey(uint modId, RSAParameters pub)
		{
			if (pub.Modulus == null || pub.Modulus.Length == 0)
			{
				throw new ArgumentException("RSA public key modulus must not be empty.", "pub");
			}
			if (pub.Exponent == null || pub.Exponent.Length == 0)
			{
				throw new ArgumentException("RSA public key exponent must not be empty.", "pub");
			}
			lock (cryptoStateLock)
			{
				modPublicKeys[modId] = CloneRsaParameters(pub);
			}
		}

		public void CopyRuntimeStateTo(INetworkingService target)
		{
			if (target == null)
			{
				throw new ArgumentNullException("target");
			}
			(object, Type, uint, int, RegistrationTransferToken)[] array;
			string[] array2;
			string[] array3;
			lock (rpcLock)
			{
				array = runtimeRegistrations.Select((RuntimeRegistration registration) => (registration.Instance, registration.Type, registration.ModId, registration.Mask, registration.Token)).ToArray();
				array2 = lobbyKeys.ToArray();
				array3 = playerKeys.ToArray();
			}
			KeyValuePair<uint, Func<byte[], byte[]>>[] array4;
			KeyValuePair<uint, RSAParameters>[] array5;
			lock (cryptoStateLock)
			{
				array4 = modSigners.ToArray();
				array5 = modPublicKeys.Select((KeyValuePair<uint, RSAParameters> entry) => new KeyValuePair<uint, RSAParameters>(entry.Key, CloneRsaParameters(entry.Value))).ToArray();
			}
			List<IDisposable> list = new List<IDisposable>();
			Func<Message, ulong, bool> incomingValidator = target.IncomingValidator;
			try
			{
				target.IncomingValidator = IncomingValidator;
				(object, Type, uint, int, RegistrationTransferToken)[] array6 = array;
				for (int i = 0; i < array6.Length; i++)
				{
					(object, Type, uint, int, RegistrationTransferToken) tuple = array6[i];
					list.Add((tuple.Item2 != null) ? target.RegisterNetworkType(tuple.Item2, tuple.Item3, tuple.Item4) : target.RegisterNetworkObject(tuple.Item1, tuple.Item3, tuple.Item4));
				}
				string[] array7 = array2;
				foreach (string key in array7)
				{
					target.RegisterLobbyDataKey(key);
				}
				string[] array8 = array3;
				foreach (string key2 in array8)
				{
					target.RegisterPlayerDataKey(key2);
				}
				KeyValuePair<uint, Func<byte[], byte[]>>[] array9 = array4;
				for (int l = 0; l < array9.Length; l++)
				{
					KeyValuePair<uint, Func<byte[], byte[]>> keyValuePair = array9[l];
					target.RegisterModSigner(keyValuePair.Key, keyValuePair.Value);
				}
				KeyValuePair<uint, RSAParameters>[] array10 = array5;
				for (int m = 0; m < array10.Length; m++)
				{
					KeyValuePair<uint, RSAParameters> keyValuePair2 = array10[m];
					target.RegisterModPublicKey(keyValuePair2.Key, keyValuePair2.Value);
				}
			}
			catch
			{
				try
				{
					target.IncomingValidator = incomingValidator;
				}
				catch
				{
				}
				for (int num = list.Count - 1; num >= 0; num--)
				{
					list[num].Dispose();
				}
				throw;
			}
			for (int n = 0; n < array.Length; n++)
			{
				array[n].Item5.SetDisposeAction(list[n].Dispose);
			}
		}

		private static RSAParameters CloneRsaParameters(RSAParameters source)
		{
			RSAParameters result = default(RSAParameters);
			result.Modulus = ((source.Modulus == null) ? null : ((byte[])source.Modulus.Clone()));
			result.Exponent = ((source.Exponent == null) ? null : ((byte[])source.Exponent.Clone()));
			return result;
		}

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

		public OfflineNetworkingService(MessageSizePolicy? messageSizePolicy = null)
		{
			this.messageSizePolicy = messageSizePolicy ?? Message.DefaultSizePolicy;
		}

		public void Initialize()
		{
			if (!IsInitialized)
			{
				EnsureLocalPeerKey();
				IsInitialized = true;
			}
		}

		public void Shutdown()
		{
			if (InLobby)
			{
				LeaveLobby();
			}
			IncomingValidator = null;
			IsInitialized = false;
			offlineIsHost = false;
			HostSteamId64 = LocalSteamId;
			lock (rpcLock)
			{
				rpcs.Clear();
				runtimeRegistrations.Clear();
				lobbyKeys.Clear();
				playerKeys.Clear();
				lobbyData.Clear();
				perPlayerData.Clear();
			}
			lock (cryptoStateLock)
			{
				modSigners.Clear();
				modPublicKeys.Clear();
				ClearPerPeerSymmetricKeysUnderLock();
				ClearGlobalSharedSecretUnderLock();
				globalHmac?.Dispose();
				globalHmac = null;
			}
			lock (exceptionLogThrottleLock)
			{
				lastExceptionLogByKey.Clear();
				suppressedExceptionLogByKey.Clear();
			}
		}

		public void CreateLobby(int maxPlayers = 8)
		{
			if (!IsInitialized)
			{
				LogError("CreateLobby called before OfflineNetworkingService.Initialize.");
				return;
			}
			if (InLobby)
			{
				LogWarning("CreateLobby called while already in a lobby. Leaving current lobby before creating a new one.");
				LeaveLobby();
			}
			if (maxPlayers != 8)
			{
				LogWarning($"Offline mode is single-peer only; requested lobby capacity {maxPlayers} is ignored.");
			}
			EnsureLocalPeerKey();
			lock (rpcLock)
			{
				InLobby = true;
				HostSteamId64 = LocalSteamId;
				lobbyData.Clear();
				perPlayerData.Clear();
				perPlayerData[LocalSteamId] = new Dictionary<string, string>();
				offlineIsHost = true;
			}
			this.LobbyCreated?.Invoke();
			this.LobbyEntered?.Invoke();
			this.PlayerEntered?.Invoke(LocalSteamId);
		}

		public void JoinLobby(ulong lobbySteamId64)
		{
			if (!IsInitialized)
			{
				LogError("JoinLobby called before OfflineNetworkingService.Initialize.");
				return;
			}
			if (lobbySteamId64 == 0L)
			{
				LogWarning("JoinLobby called with invalid lobby id 0.");
				return;
			}
			if (InLobby)
			{
				LogWarning("JoinLobby called while already in a lobby. Leaving current lobby before joining a new lobby.");
				LeaveLobby();
			}
			EnsureLocalPeerKey();
			if (lobbySteamId64 != LocalSteamId)
			{
				LogWarning($"Offline mode is single-peer only; treating JoinLobby argument {lobbySteamId64} as lobby id and preserving local host identity {LocalSteamId}.");
			}
			lock (rpcLock)
			{
				InLobby = true;
				HostSteamId64 = LocalSteamId;
				lobbyData.Clear();
				perPlayerData.Clear();
				perPlayerData[LocalSteamId] = new Dictionary<string, string>();
				offlineIsHost = true;
			}
			this.LobbyEntered?.Invoke();
			this.PlayerEntered?.Invoke(LocalSteamId);
		}

		public void LeaveLobby()
		{
			bool flag;
			lock (rpcLock)
			{
				if (!InLobby)
				{
					return;
				}
				flag = perPlayerData.ContainsKey(LocalSteamId);
				InLobby = false;
				HostSteamId64 = LocalSteamId;
				lobbyData.Clear();
				perPlayerData.Clear();
				offlineIsHost = false;
			}
			lock (cryptoStateLock)
			{
				ClearPerPeerSymmetricKeysUnderLock();
				ClearGlobalSharedSecretUnderLock();
				globalHmac?.Dispose();
				globalHmac = null;
			}
			this.LobbyLeft?.Invoke();
			if (flag)
			{
				this.PlayerLeft?.Invoke(LocalSteamId);
			}
		}

		private void ClearPerPeerSymmetricKeysUnderLock()
		{
			foreach (byte[] value in perPeerSymmetricKey.Values)
			{
				if (value != null)
				{
					CryptographicOperations.ZeroMemory(value);
				}
			}
			perPeerSymmetricKey.Clear();
		}

		private void ClearGlobalSharedSecretUnderLock()
		{
			if (globalSharedSecret != null)
			{
				CryptographicOperations.ZeroMemory(globalSharedSecret);
				globalSharedSecret = null;
			}
		}

		public void InviteToLobby(ulong steamId64)
		{
			if (!IsInitialized)
			{
				LogError("InviteToLobby called before OfflineNetworkingService.Initialize.");
			}
			else if (InLobby)
			{
				if (steamId64 == 0L)
				{
					LogWarning("InviteToLobby called with invalid target Steam64 id 0.");
				}
				else if (steamId64 != LocalSteamId)
				{
					LogWarning($"Offline mode supports strict local loopback only; invite target {steamId64} is ignored.");
				}
			}
		}

		private void EnsureLocalPeerKey()
		{
			lock (cryptoStateLock)
			{
				if (perPeerSymmetricKey.ContainsKey(LocalSteamId))
				{
					return;
				}
				using RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create();
				byte[] array = new byte[32];
				randomNumberGenerator.GetBytes(array);
				perPeerSymmetricKey[LocalSteamId] = array;
			}
		}

		public IDisposable RegisterNetworkObject(object instance, uint modId, int mask = 0)
		{
			object instance2 = instance;
			if (instance2 == null)
			{
				throw new ArgumentNullException("instance");
			}
			Type type = instance2.GetType();
			List<HandlerRegistration> registeredHandlers = new List<HandlerRegistration>();
			RegistrationTransferToken token = new RegistrationTransferToken();
			lock (rpcLock)
			{
				MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo method in methods)
				{
					if (method.IsDefined(typeof(CustomRPCAttribute), inherit: false))
					{
						if (!rpcs.TryGetValue(modId, out Dictionary<string, List<MessageHandler>> value))
						{
							value = new Dictionary<string, List<MessageHandler>>();
							rpcs[modId] = value;
						}
						if (!value.TryGetValue(method.Name, out var value2))
						{
							value2 = new List<MessageHandler>();
							value[method.Name] = value2;
						}
						ParameterInfo[] parameters = method.GetParameters();
						if (!value2.Any((MessageHandler existing) => existing.Mask == mask && existing.Method == method && existing.Target == instance2))
						{
							MessageHandler messageHandler = new MessageHandler
							{
								Target = instance2,
								Method = method,
								Parameters = parameters,
								TakesInfo = (parameters.Length != 0 && RpcInfoParameterTypeHelper.IsRpcInfoParameterType(parameters.Last().ParameterType)),
								Mask = mask
							};
							messageHandler.ParameterCountWithoutRpcInfo = (messageHandler.TakesInfo ? (parameters.Length - 1) : parameters.Length);
							messageHandler.OverloadKey = BuildOverloadKey(messageHandler);
							value2.Add(messageHandler);
							registeredHandlers.Add(new HandlerRegistration
							{
								MethodName = method.Name,
								Handler = messageHandler
							});
						}
					}
				}
				if (registeredHandlers.Count > 0)
				{
					runtimeRegistrations.Add(new RuntimeRegistration
					{
						Instance = instance2,
						ModId = modId,
						Mask = mask,
						Token = token
					});
				}
			}
			token.SetDisposeAction(delegate
			{
				DeregisterHandlers(modId, registeredHandlers);
				RemoveRuntimeRegistration(token);
			});
			return token;
		}

		public IDisposable RegisterNetworkType(Type type, uint modId, int mask = 0)
		{
			if (type == null)
			{
				throw new ArgumentNullException("type");
			}
			List<HandlerRegistration> registeredHandlers = new List<HandlerRegistration>();
			RegistrationTransferToken token = new RegistrationTransferToken();
			lock (rpcLock)
			{
				MethodInfo methodInfo = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo method) => method.IsDefined(typeof(CustomRPCAttribute), inherit: false));
				if (methodInfo != null)
				{
					throw new InvalidOperationException("Cannot register instance RPC method " + type.FullName + "." + methodInfo.Name + " without an instance.");
				}
				MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo method2 in methods)
				{
					if (method2.IsDefined(typeof(CustomRPCAttribute), inherit: false))
					{
						if (!rpcs.TryGetValue(modId, out Dictionary<string, List<MessageHandler>> value))
						{
							value = new Dictionary<string, List<MessageHandler>>();
							rpcs[modId] = value;
						}
						if (!value.TryGetValue(method2.Name, out var value2))
						{
							value2 = new List<MessageHandler>();
							value[method2.Name] = value2;
						}
						ParameterInfo[] parameters = method2.GetParameters();
						if (!value2.Any((MessageHandler existing) => existing.Mask == mask && existing.Method == method2))
						{
							MessageHandler messageHandler = new MessageHandler
							{
								Target = null,
								Method = method2,
								Parameters = parameters,
								TakesInfo = (parameters.Length != 0 && RpcInfoParameterTypeHelper.IsRpcInfoParameterType(parameters.Last().ParameterType)),
								Mask = mask
							};
							messageHandler.ParameterCountWithoutRpcInfo = (messageHandler.TakesInfo ? (parameters.Length - 1) : parameters.Length);
							messageHandler.OverloadKey = BuildOverloadKey(messageHandler);
							value2.Add(messageHandler);
							registeredHandlers.Add(new HandlerRegistration
							{
								MethodName = method2.Name,
								Handler = messageHandler
							});
						}
					}
				}
				if (registeredHandlers.Count > 0)
				{
					runtimeRegistrations.Add(new RuntimeRegistration
					{
						Type = type,
						ModId = modId,
						Mask = mask,
						Token = token
					});
				}
			}
			token.SetDisposeAction(delegate
			{
				DeregisterHandlers(modId, registeredHandlers);
				RemoveRuntimeRegistration(token);
			});
			return token;
		}

		private void DeregisterHandlers(uint modId, List<HandlerRegistration> handlersToRemove)
		{
			lock (rpcLock)
			{
				if (!rpcs.TryGetValue(modId, out Dictionary<string, List<MessageHandler>> value) || handlersToRemove.Count == 0)
				{
					return;
				}
				foreach (HandlerRegistration item in handlersToRemove)
				{
					if (value.TryGetValue(item.MethodName, out var value2))
					{
						value2.Remove(item.Handler);
						if (value2.Count == 0)
						{
							value.Remove(item.MethodName);
						}
					}
				}
				if (value.Count == 0)
				{
					rpcs.Remove(modId);
				}
			}
		}

		public void DeregisterNetworkObject(object instance, uint modId, int mask = 0)
		{
			object instance2 = instance;
			if (instance2 == null)
			{
				throw new ArgumentNullException("instance");
			}
			lock (rpcLock)
			{
				if (!rpcs.TryGetValue(modId, out Dictionary<string, List<MessageHandler>> value))
				{
					return;
				}
				MethodInfo[] methods = instance2.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo in methods)
				{
					if (!methodInfo.IsDefined(typeof(CustomRPCAttribute), inherit: false) || !value.TryGetValue(methodInfo.Name, out var value2))
					{
						continue;
					}
					for (int num = value2.Count - 1; num >= 0; num--)
					{
						if (value2[num].Target == instance2 && value2[num].Mask == mask)
						{
							value2.RemoveAt(num);
						}
					}
					if (value2.Count == 0)
					{
						value.Remove(methodInfo.Name);
					}
				}
				if (value.Count == 0)
				{
					rpcs.Remove(modId);
				}
				runtimeRegistrations.RemoveAll((RuntimeRegistration registration) => registration.Type == null && registration.ModId == modId && registration.Mask == mask && registration.Instance == instance2);
			}
		}

		public void DeregisterNetworkType(Type type, uint modId, int mask = 0)
		{
			Type type2 = type;
			if (type2 == null)
			{
				throw new ArgumentNullException("type");
			}
			lock (rpcLock)
			{
				if (!rpcs.TryGetValue(modId, out Dictionary<string, List<MessageHandler>> value))
				{
					return;
				}
				string[] array = value.Keys.ToArray();
				foreach (string key in array)
				{
					if (!value.TryGetValue(key, out var value2))
					{
						continue;
					}
					for (int num = value2.Count - 1; num >= 0; num--)
					{
						if (value2[num].Target == null && value2[num].Method.DeclaringType == type2 && value2[num].Mask == mask)
						{
							value2.RemoveAt(num);
						}
					}
					if (value2.Count == 0)
					{
						value.Remove(key);
					}
				}
				if (value.Count == 0)
				{
					rpcs.Remove(modId);
				}
				runtimeRegistrations.RemoveAll((RuntimeRegistration registration) => registration.Type == type2 && registration.ModId == modId && registration.Mask == mask);
			}
		}

		private void RemoveRuntimeRegistration(RegistrationTransferToken token)
		{
			RegistrationTransferToken token2 = token;
			lock (rpcLock)
			{
				runtimeRegistrations.RemoveAll((RuntimeRegistration registration) => registration.Token == token2);
			}
		}

		public void RPC(uint modId, string methodName, ReliableType reliable, params object[] parameters)
		{
			if (!InLobby)
			{
				LogError("RPC called while not in lobby");
				return;
			}
			Message message = BuildMessage(modId, methodName, 0, parameters, null);
			if (message != null)
			{
				DispatchIncoming(message, LocalSteamId);
			}
		}

		public void RPC(uint modId, string methodName, ReliableType reliable, Type[] parameterTypes, params object?[] parameters)
		{
			if (!InLobby)
			{
				LogError("RPC called while not in lobby");
				return;
			}
			Message message = BuildMessage(modId, methodName, 0, parameters, parameterTypes);
			if (message != null)
			{
				DispatchIncoming(message, LocalSteamId);
			}
		}

		public void RPCTarget(uint modId, string methodName, ulong targetSteamId64, ReliableType reliable, params object[] parameters)
		{
			if (!InLobby)
			{
				LogError("Cannot RPC target when not in lobby");
				return;
			}
			Message message = BuildMessage(modId, methodName, 0, parameters, null);
			if (message != null && targetSteamId64 == LocalSteamId)
			{
				DispatchIncoming(message, LocalSteamId);
			}
		}

		public void RPCTarget(uint modId, string methodName, ulong targetSteamId64, ReliableType reliable, Type[] parameterTypes, params object?[] parameters)
		{
			if (!InLobby)
			{
				LogError("Cannot RPC target when not in lobby");
				return;
			}
			Message message = BuildMessage(modId, methodName, 0, parameters, parameterTypes);
			if (message != null && targetSteamId64 == LocalSteamId)
			{
				DispatchIncoming(message, LocalSteamId);
			}
		}

		public void RPCToHost(uint modId, string methodName, ReliableType reliable, params object[] parameters)
		{
			if (!InLobby)
			{
				LogError("Not in lobby");
			}
			else
			{
				RPCTarget(modId, methodName, HostSteamId64, reliable, parameters);
			}
		}

		public void RegisterLobbyDataKey(string key)
		{
			ValidateDataKey(key, "key");
			lock (rpcLock)
			{
				lobbyKeys.Add(key);
			}
		}

		public void SetLobbyData(string key, object value)
		{
			ValidateDataKey(key, "key");
			string text = Convert.ToString(value, CultureInfo.InvariantCulture) ?? string.Empty;
			bool flag = false;
			bool flag2 = false;
			bool flag3 = false;
			lock (rpcLock)
			{
				if (InLobby)
				{
					flag = !lobbyKeys.Contains(key);
					flag3 = !lobbyData.TryGetValue(key, out string value2) || value2 != text;
					if (flag3)
					{
						lobbyData[key] = text;
					}
				}
				else
				{
					flag2 = true;
				}
			}
			if (flag2)
			{
				LogError("Cannot set lobby data when not in lobby.");
				return;
			}
			if (flag)
			{
				LogWarning("Accessing unregistered lobby key " + key);
			}
			if (flag3)
			{
				this.LobbyDataChanged?.Invoke(new string[1] { key });
			}
		}

		public T GetLobbyData<T>(string key)
		{
			ValidateDataKey(key, "key");
			string value = null;
			bool flag = false;
			bool flag2 = false;
			bool flag3 = false;
			lock (rpcLock)
			{
				if (InLobby)
				{
					flag2 = !lobbyKeys.Contains(key);
					flag = lobbyData.TryGetValue(key, out value);
				}
				else
				{
					flag3 = true;
				}
			}
			if (flag3)
			{
				LogError("Cannot get lobby data when not in lobby.");
				return default(T);
			}
			if (flag2)
			{
				LogWarning("Accessing unregistered lobby key " + key);
			}
			if (!flag)
			{
				return default(T);
			}
			try
			{
				return DataValueConverter.ConvertTo<T>(value);
			}
			catch (Exception ex)
			{
				LogExceptionThrottled("lobby_parse:" + key, error: true, "Could not parse lobby data [" + key + "," + value + "].", ex);
				return default(T);
			}
		}

		public void RegisterPlayerDataKey(string key)
		{
			ValidateDataKey(key, "key");
			lock (rpcLock)
			{
				playerKeys.Add(key);
			}
		}

		public void SetPlayerData(string key, object value)
		{
			ValidateDataKey(key, "key");
			string text = Convert.ToString(value, CultureInfo.InvariantCulture) ?? string.Empty;
			bool flag = false;
			bool flag2 = false;
			bool flag3 = false;
			bool flag4 = false;
			lock (rpcLock)
			{
				if (InLobby)
				{
					flag = !playerKeys.Contains(key);
					if (!perPlayerData.TryGetValue(LocalSteamId, out Dictionary<string, string> value2))
					{
						value2 = new Dictionary<string, string>();
						perPlayerData[LocalSteamId] = value2;
						flag3 = true;
					}
					flag4 = !value2.TryGetValue(key, out var value3) || value3 != text;
					if (flag4)
					{
						value2[key] = text;
					}
				}
				else
				{
					flag2 = true;
				}
			}
			if (flag2)
			{
				LogError("Cannot set player data when not in lobby.");
				return;
			}
			if (flag)
			{
				LogWarning("Accessing unregistered player key " + key);
			}
			if (flag3)
			{
				LogWarning($"Local player {LocalSteamId} was missing in per-player state while setting key '{key}'. Recreating local state bucket.");
			}
			if (flag4)
			{
				this.PlayerDataChanged?.Invoke(LocalSteamId, new string[1] { key });
			}
		}

		public T GetPlayerData<T>(ulong steamId64, string key)
		{
			ValidateDataKey(key, "key");
			string value = null;
			bool flag = false;
			bool flag2 = false;
			bool flag3 = false;
			lock (rpcLock)
			{
				if (InLobby)
				{
					flag2 = !playerKeys.Contains(key);
					flag = perPlayerData.TryGetValue(steamId64, out Dictionary<string, string> value2) && value2.TryGetValue(key, out value);
				}
				else
				{
					flag3 = true;
				}
			}
			if (flag3)
			{
				LogError("Cannot get player data when not in lobby.");
				return default(T);
			}
			if (flag2)
			{
				LogWarning("Accessing unregistered player key " + key);
			}
			if (!flag)
			{
				return default(T);
			}
			try
			{
				return DataValueConverter.ConvertTo<T>(value);
			}
			catch (Exception ex)
			{
				LogExceptionThrottled("player_parse:" + key, error: true, "Could not parse player data [" + key + "," + value + "].", ex);
				return default(T);
			}
		}

		public void PollReceive()
		{
		}

		private static void ValidateDataKey(string key, string paramName)
		{
			if (string.IsNullOrWhiteSpace(key))
			{
				throw new ArgumentException("Data key must be a non-empty string.", paramName);
			}
		}

		private Message? BuildMessage(uint modId, string methodName, int mask, object?[] parameters, Type[]? parameterTypes)
		{
			object?[] parameters2 = parameters;
			Type[] parameterTypes2 = parameterTypes;
			string methodName2 = methodName;
			MessageHandler[] handlersSnapshot;
			try
			{
				handlersSnapshot = Array.Empty<MessageHandler>();
				lock (rpcLock)
				{
					if (rpcs.TryGetValue(modId, out Dictionary<string, List<MessageHandler>> value) && value.TryGetValue(methodName2, out var value2) && value2.Count > 0)
					{
						handlersSnapshot = value2.ToArray();
					}
				}
				if (handlersSnapshot.Length != 0)
				{
					if (parameterTypes2 != null && parameterTypes2.Length != parameters2.Length)
					{
						throw new ArgumentException($"Parameter type count mismatch: expected {parameterTypes2.Length}, got {parameters2.Length}", "parameters");
					}
					MessageHandler messageHandler = null;
					if (parameterTypes2 != null)
					{
						messageHandler = FindTypedHandler(exactMatch: true) ?? FindTypedHandler(exactMatch: false);
					}
					if (messageHandler == null)
					{
						messageHandler = FindUntypedHandler(allowNullableConversions: false) ?? FindUntypedHandler(allowNullableConversions: true);
					}
					if (messageHandler == null && parameterTypes2 == null)
					{
						messageHandler = handlersSnapshot.FirstOrDefault((MessageHandler h) => h.ParameterCountWithoutRpcInfo == parameters2.Length && h.Mask == mask);
					}
					if (messageHandler == null)
					{
						LogError($"No RPC overload matched method '{methodName2}' for mask {mask} and parameter list.");
						return null;
					}
					Message message = new Message(modId, methodName2, mask, messageHandler.OverloadKey, messageSizePolicy);
					ParameterInfo[] parameters3 = messageHandler.Parameters;
					int parameterCountWithoutRpcInfo = messageHandler.ParameterCountWithoutRpcInfo;
					if (parameterCountWithoutRpcInfo != parameters2.Length)
					{
						throw new ArgumentException($"Parameter count mismatch for {methodName2}: expected {parameterCountWithoutRpcInfo}, got {parameters2.Length}", "parameters");
					}
					for (int i = 0; i < parameterCountWithoutRpcInfo; i++)
					{
						Type parameterType = parameters3[i].ParameterType;
						object obj = parameters2[i];
						if (obj == null)
						{
							if (parameterType.IsValueType && Nullable.GetUnderlyingType(parameterType) == null)
							{
								throw new ArgumentNullException("parameters", $"Parameter {i} for {methodName2} cannot be null; expected non-nullable {parameterType}.");
							}
							message.WriteObject(parameterType, null);
						}
						else
						{
							if (!IsParameterValueCompatible(parameterType, obj))
							{
								throw new ArgumentException($"Parameter {i} type mismatch: expected {parameterType}, got {obj.GetType()}", "parameters");
							}
							message.WriteObject(parameterType, obj);
						}
					}
					if (message.Length() > messageSizePolicy.MaxLogicalSize)
					{
						LogError("Message exceeds maximum allowed overall size.");
						return null;
					}
					return message;
				}
				Message message2 = new Message(modId, methodName2, mask, messageSizePolicy);
				if (parameterTypes2 != null)
				{
					if (parameterTypes2.Length != parameters2.Length)
					{
						throw new ArgumentException($"Parameter type count mismatch: expected {parameterTypes2.Length}, got {parameters2.Length}", "parameters");
					}
					for (int j = 0; j < parameters2.Length; j++)
					{
						Type type = parameterTypes2[j];
						object obj2 = parameters2[j];
						if (obj2 == null)
						{
							if (type.IsValueType && Nullable.GetUnderlyingType(type) == null)
							{
								throw new ArgumentNullException("parameters", $"Parameter {j} for {methodName2} cannot be null; expected non-nullable {type}.");
							}
							message2.WriteObject(type, null);
						}
						else
						{
							if (!IsParameterValueCompatible(type, obj2))
							{
								throw new ArgumentException($"Parameter {j} type mismatch: expected {type}, got {obj2.GetType()}", "parameters");
							}
							message2.WriteObject(type, obj2);
						}
					}
				}
				else
				{
					for (int k = 0; k < parameters2.Length; k++)
					{
						object obj3 = parameters2[k] ?? throw new ArgumentNullException("parameters", $"Parameter {k} is null for unregistered RPC {methodName2}; use typed RPC overload.");
						message2.WriteObject(obj3.GetType(), obj3);
					}
				}
				if (message2.Length() > messageSizePolicy.MaxLogicalSize)
				{
					LogError("Message exceeds maximum allowed overall size.");
					return null;
				}
				return message2;
			}
			catch (Exception arg)
			{
				LogError($"BuildMessage failed: {arg}");
				return null;
			}
			MessageHandler? FindTypedHandler(bool exactMatch)
			{
				MessageHandler[] array2 = handlersSnapshot;
				foreach (MessageHandler messageHandler3 in array2)
				{
					if (messageHandler3.Mask == mask)
					{
						ParameterInfo[] parameters5 = messageHandler3.Parameters;
						int parameterCountWithoutRpcInfo3 = messageHandler3.ParameterCountWithoutRpcInfo;
						if (parameterCountWithoutRpcInfo3 == parameterTypes2.Length)
						{
							bool flag2 = true;
							for (int num = 0; num < parameterCountWithoutRpcInfo3; num++)
							{
								Type parameterType3 = parameters5[num].ParameterType;
								Type suppliedType = parameterTypes2[num] ?? throw new ArgumentNullException("parameterTypes", $"Parameter type {num} for {methodName2} cannot be null.");
								if (!IsParameterTypeCompatible(parameterType3, suppliedType, exactMatch))
								{
									flag2 = false;
									break;
								}
								object obj5 = parameters2[num];
								if (obj5 == null)
								{
									if (parameterType3.IsValueType && Nullable.GetUnderlyingType(parameterType3) == null)
									{
										flag2 = false;
										break;
									}
								}
								else if (!IsParameterValueCompatible(parameterType3, obj5))
								{
									flag2 = false;
									break;
								}
							}
							if (flag2)
							{
								return messageHandler3;
							}
						}
					}
				}
				return null;
			}
			MessageHandler? FindUntypedHandler(bool allowNullableConversions)
			{
				MessageHandler[] array = handlersSnapshot;
				foreach (MessageHandler messageHandler2 in array)
				{
					if (messageHandler2.Mask == mask)
					{
						ParameterInfo[] parameters4 = messageHandler2.Parameters;
						int parameterCountWithoutRpcInfo2 = messageHandler2.ParameterCountWithoutRpcInfo;
						if (parameterCountWithoutRpcInfo2 == parameters2.Length)
						{
							bool flag = true;
							for (int m = 0; m < parameterCountWithoutRpcInfo2; m++)
							{
								Type parameterType2 = parameters4[m].ParameterType;
								object obj4 = parameters2[m];
								if (obj4 == null)
								{
									if (parameterType2.IsValueType && Nullable.GetUnderlyingType(parameterType2) == null)
									{
										flag = false;
										break;
									}
								}
								else if (allowNullableConversions ? (!IsParameterValueCompatible(parameterType2, obj4)) : (!IsParameterValueCompatibleWithoutNullableFallback(parameterType2, obj4)))
								{
									flag = false;
									break;
								}
							}
							if (flag)
							{
								return messageHandler2;
							}
						}
					}
				}
				return null;
			}
		}

		private void DispatchIncoming(Message message, ulong from)
		{
			Message message2 = message;
			if ((IncomingValidator != null && !IncomingValidator(message2, from)) || !rateLimiter.IncomingAllowed())
			{
				return;
			}
			MessageHandler[] handlersSnapshot;
			lock (rpcLock)
			{
				if (!rpcs.TryGetValue(message2.ModID, out Dictionary<string, List<MessageHandler>> value))
				{
					LogWarning($"No mod {message2.ModID}");
					return;
				}
				if (!value.TryGetValue(message2.MethodName, out var value2))
				{
					LogWarning("No method " + message2.MethodName);
					return;
				}
				handlersSnapshot = value2.ToArray();
			}
			MessageHandler chosenHandler = null;
			object[] chosenParams = null;
			MessageHandler fallbackHandler = null;
			object[] fallbackParams = null;
			bool flag = !string.IsNullOrEmpty(message2.OverloadKey);
			bool flag2 = false;
			if (flag)
			{
				foreach (MessageHandler messageHandler in handlersSnapshot)
				{
					if (messageHandler.Mask == message2.Mask && !(messageHandler.OverloadKey != message2.OverloadKey))
					{
						flag2 = true;
						break;
					}
				}
			}
			if (flag2)
			{
				TryDispatch(true);
				if (chosenHandler == null)
				{
					TryDispatch(false);
				}
			}
			else
			{
				TryDispatch(null);
			}
			if (chosenHandler == null)
			{
				chosenHandler = fallbackHandler;
				chosenParams = fallbackParams;
			}
			if (chosenHandler == null || chosenParams == null)
			{
				LogWarning($"No matching overload for {message2.MethodName} (mask {message2.Mask})");
				return;
			}
			try
			{
				chosenHandler.Method.Invoke(chosenHandler.Target, chosenParams);
			}
			catch (Exception arg)
			{
				LogError($"Invoke RPC error: {arg}");
			}
			void TryDispatch(bool? preferOverloadKeyMatch)
			{
				foreach (MessageHandler messageHandler2 in handlersSnapshot)
				{
					if (messageHandler2.Mask == message2.Mask)
					{
						if (preferOverloadKeyMatch.HasValue)
						{
							bool flag3 = messageHandler2.OverloadKey == message2.OverloadKey;
							if (preferOverloadKeyMatch.Value != flag3)
							{
								continue;
							}
						}
						if (TryDeserializeForHandler(message2, messageHandler2, from, isLocalLoopback: true, out object[] callParams, out int unread))
						{
							if (unread == 0)
							{
								chosenHandler = messageHandler2;
								chosenParams = callParams;
								break;
							}
							if (fallbackHandler == null)
							{
								fallbackHandler = messageHandler2;
							}
							if (fallbackParams == null)
							{
								fallbackParams = callParams;
							}
						}
					}
				}
			}
		}

		private bool TryDeserializeForHandler(Message source, MessageHandler handler, ulong from, bool isLocalLoopback, out object[] callParams, out int unread)
		{
			callParams = null;
			unread = int.MaxValue;
			Message.ReadCursor readCursor = source.SaveReadCursor();
			Message.ReadCursor cursor = readCursor;
			try
			{
				if (readCursor.Position == 0)
				{
					cursor = AdvanceCursorPastHeader(source);
				}
				source.RestoreReadCursor(cursor);
				ParameterInfo[] parameters = handler.Parameters;
				int parameterCountWithoutRpcInfo = handler.ParameterCountWithoutRpcInfo;
				callParams = new object[parameters.Length];
				for (int i = 0; i < parameterCountWithoutRpcInfo; i++)
				{
					callParams[i] = source.ReadObject(parameters[i].ParameterType);
				}
				if (handler.TakesInfo)
				{
					Type parameterType = parameters[^1].ParameterType;
					callParams[parameters.Length - 1] = CreateRpcInfoInstance(parameterType, from, isLocalLoopback);
				}
				unread = source.UnreadLength();
				return true;
			}
			catch (Exception ex)
			{
				LogDeserializeFailureThrottled(ex, (handler.Method.DeclaringType?.Name ?? "UnknownType") + "." + handler.Method.Name);
				return false;
			}
			finally
			{
				source.RestoreReadCursor(readCursor);
			}
		}

		private static Message.ReadCursor AdvanceCursorPastHeader(Message message)
		{
			message.ReadByte();
			message.ReadUInt();
			message.ReadString();
			message.ReadInt();
			if (message.ProtocolVersion >= 3 && message.ReadBool())
			{
				message.ReadString();
			}
			return message.SaveReadCursor();
		}

		private object CreateRpcInfoInstance(Type infoType, ulong from, bool isLocalLoopback)
		{
			try
			{
				ConstructorInfo constructor = infoType.GetConstructor(new Type[3]
				{
					typeof(ulong),
					typeof(string),
					typeof(bool)
				});
				if (constructor != null)
				{
					return constructor.Invoke(new object[3]
					{
						from,
						from.ToString(),
						isLocalLoopback
					});
				}
				ConstructorInfo constructor2 = infoType.GetConstructor(new Type[1] { typeof(ulong) });
				if (constructor2 != null)
				{
					return constructor2.Invoke(new object[1] { from });
				}
				object obj = Activator.CreateInstance(infoType);
				if (obj == null)
				{
					return null;
				}
				AssignRpcIdentityMembers(obj, infoType, from, isLocalLoopback);
				return obj;
			}
			catch (Exception ex)
			{
				LogExceptionThrottled("rpc_info:" + (infoType.FullName ?? infoType.Name), error: false, "CreateRpcInfoInstance failed for " + (infoType.FullName ?? infoType.Name) + ".", ex);
				return null;
			}
		}

		private static void AssignRpcIdentityMembers(object instance, Type infoType, ulong steamId64, bool isLocalLoopback)
		{
			string steamIdString = steamId64.ToString();
			AssignRpcIdentityMember(instance, infoType, "SenderSteamID", steamId64, steamIdString);
			AssignRpcIdentityMember(instance, infoType, "Sender", steamId64, steamIdString);
			AssignRpcIdentityMember(instance, infoType, "SteamId64", steamId64, steamIdString);
			AssignRpcIdentityMember(instance, infoType, "SteamIdString", steamId64, steamIdString);
			AssignRpcLoopbackMember(instance, infoType, isLocalLoopback);
		}

		private static void AssignRpcIdentityMember(object instance, Type infoType, string memberName, ulong steamId64, string steamIdString)
		{
			object instance2 = instance;
			FieldInfo field = infoType.GetField(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				TryAssignMemberValue(field.FieldType, delegate(object value)
				{
					field.SetValue(instance2, value);
				}, steamId64, steamIdString);
				return;
			}
			PropertyInfo property = infoType.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (!(property == null) && property.CanWrite)
			{
				TryAssignMemberValue(property.PropertyType, delegate(object value)
				{
					property.SetValue(instance2, value);
				}, steamId64, steamIdString);
			}
		}

		private static void AssignRpcLoopbackMember(object instance, Type infoType, bool isLocalLoopback)
		{
			object instance2 = instance;
			FieldInfo field = infoType.GetField("IsLocalLoopback", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				TryAssignLoopbackMemberValue(field.FieldType, delegate(object value)
				{
					field.SetValue(instance2, value);
				}, isLocalLoopback);
				return;
			}
			PropertyInfo property = infoType.GetProperty("IsLocalLoopback", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (!(property == null) && property.CanWrite)
			{
				TryAssignLoopbackMemberValue(property.PropertyType, delegate(object value)
				{
					property.SetValue(instance2, value);
				}, isLocalLoopback);
			}
		}

		private static void TryAssignLoopbackMemberValue(Type memberType, Action<object> assign, bool isLocalLoopback)
		{
			if (memberType == typeof(bool))
			{
				assign(isLocalLoopback);
			}
			else if (memberType == typeof(bool?))
			{
				assign(isLocalLoopback);
			}
		}

		private static void TryAssignMemberValue(Type memberType, Action<object> assign, ulong steamId64, string steamIdString)
		{
			Type type = Nullable.GetUnderlyingType(memberType) ?? memberType;
			if (type == typeof(string))
			{
				assign(steamIdString);
			}
			else if (IsCompatibleIntegralType(type))
			{
				assign(ConvertIntegral(steamId64, type));
			}
		}

		private static bool IsCompatibleIntegralType(Type t)
		{
			if (!(t == typeof(ulong)) && !(t == typeof(long)) && !(t == typeof(uint)) && !(t == typeof(int)) && !(t == typeof(ushort)) && !(t == typeof(short)) && !(t == typeof(byte)))
			{
				return t == typeof(sbyte);
			}
			return true;
		}

		private static object ConvertIntegral(ulong value, Type targetType)
		{
			if (targetType == typeof(ulong))
			{
				return value;
			}
			checked
			{
				if (targetType == typeof(long))
				{
					return (long)value;
				}
				if (targetType == typeof(uint))
				{
					return (uint)value;
				}
				if (targetType == typeof(int))
				{
					return (int)value;
				}
				if (targetType == typeof(ushort))
				{
					return (ushort)value;
				}
				if (targetType == typeof(short))
				{
					return (short)value;
				}
				if (targetType == typeof(byte))
				{
					return (byte)value;
				}
				if (targetType == typeof(sbyte))
				{
					return (sbyte)value;
				}
				throw new InvalidCastException($"Unsupported integral conversion to {targetType}.");
			}
		}

		private static string BuildOverloadKey(MessageHandler handler)
		{
			ParameterInfo[] parameters = handler.Parameters;
			int num = (handler.TakesInfo ? (parameters.Length - 1) : parameters.Length);
			if (num <= 0)
			{
				return string.Empty;
			}
			return string.Join("|", from p in parameters.Take(num)
				select p.ParameterType.AssemblyQualifiedName ?? p.ParameterType.FullName ?? p.ParameterType.Name);
		}

		private static bool IsParameterValueCompatible(Type parameterType, object value)
		{
			Type underlyingType = Nullable.GetUnderlyingType(parameterType);
			if (!(underlyingType != null))
			{
				return parameterType.IsAssignableFrom(value.GetType());
			}
			return underlyingType.IsAssignableFrom(value.GetType());
		}

		private static bool IsParameterValueCompatibleWithoutNullableFallback(Type parameterType, object value)
		{
			if (Nullable.GetUnderlyingType(parameterType) != null)
			{
				return false;
			}
			return parameterType.IsAssignableFrom(value.GetType());
		}

		private static bool IsParameterTypeCompatible(Type parameterType, Type suppliedType, bool exactMatch)
		{
			if (exactMatch)
			{
				return parameterType == suppliedType;
			}
			Type underlyingType = Nullable.GetUnderlyingType(parameterType);
			if (!parameterType.IsAssignableFrom(suppliedType))
			{
				return underlyingType?.IsAssignableFrom(suppliedType) ?? false;
			}
			return true;
		}
	}
	public class SteamNetworkingService : INetworkingService, INetworkingServiceStateTransfer
	{
		private class FragmentBuffer
		{
			public int Total;

			public DateTime FirstSeen = DateTime.UtcNow;

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

		private sealed class HandlerRegistration
		{
			public string MethodName = string.Empty;

			public MessageHandler Handler;
		}

		private sealed class RuntimeRegistration
		{
			public object? Instance;

			public Type Type;

			public uint ModId;

			public int Mask;

			public RegistrationToken Token;
		}

		private sealed class RegistrationToken : IDisposable
		{
			private readonly object sync = new object();

			private Action? disposeAction;

			private bool disposed;

			public void SetDisposeAction(Action? action)
			{
				Action action2 = null;
				bool flag = false;
				lock (sync)
				{
					if (disposed)
					{
						flag = true;
					}
					else
					{
						action2 = disposeAction;
						disposeAction = action;
					}
				}
				if (flag)
				{
					action?.Invoke();
				}
				else
				{
					action2?.Invoke();
				}
			}

			public void Dispose()
			{
				Action action;
				lock (sync)
				{
					if (disposed)
					{
						return;
					}
					disposed = true;
					action = disposeAction;
					disposeAction = null;
				}
				action?.Invoke();
			}
		}

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

			private readonly object qLock = new object();

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

			public bool Allowed()
			{
				lock (qLock)
				{
					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 => BinaryPrimitives.ReadUInt64LittleEndian(Framed.AsSpan(1, 8));
		}

		private class MessageHandler
		{
			public object Target;

			public MethodInfo Method;

			public ParameterInfo[] Parameters;

			public bool TakesInfo;

			public int Mask;

			public int ParameterCountWithoutRpcInfo;

			public string OverloadKey = string.Empty;
		}

		private const string LogSource = "SteamNetworkingService";

		private const int CHANNEL = 120;

		private const int MAX_IN_MESSAGES = 500;

		private const int MAX_NORMAL_QUEUE_DEPTH = 256;

		private const int MAX_LOW_QUEUE_DEPTH = 128;

		private const double DebugLogCooldownSeconds = 2.0;

		private const double QueueOverflowWarningCooldownSeconds = 2.0;

		private const string GetLocalSteam64DebugCooldownKey = "SteamNetworkingService.GetLocalSteam64";

		private const string IsHostDebugCooldownKey = "SteamNetworkingService.IsHost";

		private const string LocalSteamIdDebugCooldownKey = "SteamNetworkingService.LocalSteamId";

		private const string LobbyOwnerDebugCooldownKey = "SteamNetworkingService.LobbyOwner";

		private const string ProcessIncomingFrameExceptionCooldownKey = "SteamNetworkingService.ReceiveMessages.ProcessIncomingFrameException";

		private const string ReceiveMessagesOuterExceptionCooldownKey = "SteamNetworkingService.ReceiveMessages.OuterException";

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

		private readonly object rpcLock = new object();

		private readonly object cryptoStateLock = new object();

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

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

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

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

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

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

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

		private Func<CSteamID, int> getNumLobbyMembers = SteamMatchmaking.GetNumLobbyMembers;

		private Func<CSteamID, CSteamID> getLobbyOwner = SteamMatchmaking.GetLobbyOwner;

		private Func<CSteamID> getLocalSteamId = SteamUser.GetSteamID;

		private Func<CSteamID, int, CSteamID> getLobbyMemberByIndex = SteamMatchmaking.GetLobbyMemberByIndex;

		private Action<CSteamID, string, string> setLobbyData = delegate(CSteamID lobby, string key, string value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			SteamMatchmaking.SetLobbyData(lobby, key, value);
		};

		private Func<CSteamID, string, string> getLobbyData = SteamMatchmaking.GetLobbyData;

		private Action<CSteamID, string, string> setLobbyMemberData = SteamMatchmaking.SetLobbyMemberData;

		private Func<CSteamID, CSteamID, string, string> getLobbyMemberData = SteamMatchmaking.GetLobbyMemberData;

		private Func<int, IntPtr[], int, int> receiveMessagesOnChannel = SteamNetworkingMessages.ReceiveMessagesOnChannel;

		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 List<RuntimeRegistration> runtimeRegistrations = new List<RuntimeRegistration>();

		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 readonly List<(ulong sender, ulong msgId)> staleFragmentKeys = new List<(ulong, ulong)>();

		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 RSAParameters[] modPublicKeysSnapshot = Array.Empty<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 const int FRAME_HEADER_SIZE = 25;

		private RSA? LocalRsa;

		private Func<RSA> localRsaFactory = () => RSA.Create(2048);

		private readonly MessageSizePolicy messageSizePolicy;

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

		private readonly TimeSpan FragmentCleanupInterval = TimeSpan.FromSeconds(5.0);

		private DateTime nextFragmentCleanupAt = DateTime.MinValue;

		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_0017: 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_0033: Unknown result type (might be due to invalid IL or missing references)
				//IL_0042: Unknown result type (might be due to invalid IL or missing references)
				if (Lobby == CSteamID.Nil)
				{
					return 0uL;
				}
				if (!TryGetLobbyOwner(Lobby, out var owner, "SteamNetworkingService.LobbyOwner", "HostSteamId64"))
				{
					return 0uL;
				}
				if (owner == CSteamID.Nil)
				{
					return 0uL;
				}
				return owner.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_001a: 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)
				if (Lobby == CSteamID.Nil)
				{
					return string.Empty;
				}
				if (!TryGetLobbyOwner(Lobby, out var owner, "SteamNetworkingService.LobbyOwner", "HostIdString"))
				{
					return string.Empty;
				}
				if (!(owner == CSteamID.Nil))
				{
					return ((object)(CSteamID)(ref owner)).ToString();
				}
				return string.Empty;
			}
		}

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


		public bool IsHost
		{
			get
			{
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0056: 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)
				try
				{
					if (!InLobby)
					{
						return false;
					}
					if (!TryGetLobbyOwner(Lobby, out var owner, "SteamNetworkingService.IsHost", "IsHost"))
					{
						return false;
					}
					if (owner == CSteamID.Nil)
					{
						return false;
					}
					if (!TryGetLocalSteamId(out var steamId, "SteamNetworkingService.IsHost", "IsHost"))
					{
						return false;
					}
					return owner == steamId;
				}
				catch (Exception ex)
				{
					NetLog.DebugThrottled("SteamNetworkingService", "SteamNetworkingService.IsHost", 2.0, "IsHost check failed: " + ex.GetType().Name + ": " + ex.Message);
					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_0017: Unknown result type (might be due to invalid IL or missing references)
			if (!TryGetLocalSteamId(out var steamId, "SteamNetworkingService.GetLocalSteam64", "GetLocalSteam64"))
			{
				return 0uL;
			}
			return steamId.m_SteamID;
		}

		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_0028: 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_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			if (!InLobby || Lobby == CSteamID.Nil)
			{
				return Array.Empty<ulong>();
			}
			try
			{
				int num = getNumLobbyMembers(Lobby);
				if (num <= 0)
				{
					return Array.Empty<ulong>();
				}
				ulong[] array = new ulong[num];
				int num2 = 0;
				for (int i = 0; i < num; i++)
				{
					CSteamID val = getLobbyMemberByIndex(Lobby, i);
					if (!(val == CSteamID.Nil))
					{
						array[num2++] = val.m_SteamID;
					}
				}
				if (num2 == 0)
				{
					return Array.Empty<ulong>();
				}
				if (num2 == array.Length)
				{
					return array;
				}
				ulong[] array2 = new ulong[num2];
				Array.Copy(array, array2, num2);
				return array2;
			}
			catch (Exception arg)
			{
				NetLog.Error("SteamNetworkingService", $"GetLobbyMemberSteamIds error: {arg}");
				return Array.Empty<ulong>();
			}
		}

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

		public SteamNetworkingService(MessageSizePolicy? messageSizePolicy = null)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			this.messageSizePolicy = messageSizePolicy ?? CreateDefaultMessageSizePolicy();
		}

		private static void LogInfo(string message)
		{
			try
			{
				NetLog.Info("SteamNetworkingService", message);
			}
			catch (Exception ex)
			{
				Trace.TraceWarning("[SteamNetworkingService] Failed to write info log. Exception: " + ex.GetType().Name + ": " + ex.Message + ". Original message: " + message);
			}
		}

		private static MessageSizePolicy CreateDefaultMessageSizePolicy()
		{
			try
			{
				return new MessageSizePolicy(524288);
			}
			catch
			{
				return Message.DefaultSizePolicy;
			}
		}

		public void Initialize()
		{
			if (IsInitialized)
			{
				return;
			}
			GameObject createdPumpGameObject = null;
			SteamCallbackPump createdPumpComponent = null;
			bool callbackPumpingEnabled = SteamCallbackPump.CallbackPumpingEnabled;
			bool enabledPumpingInThisInitialize = false;
			bool flag = !Application.isPlaying;
			if (Application.isPlaying)
			{
				try
				{
					PrepareCanonicalSteamCallbackPump(out createdPumpGameObject, out createdPumpComponent);
					SteamCallbackPump.EnablePumping();
					enabledPumpingInThisInitialize = true;
					flag = true;
				}
				catch (Exception arg)
				{
					NetLog.Error("SteamNetworkingService", $"Failed to create SteamCallbackPump: {arg}");
				}
			}
			if (!flag)
			{
				CleanupFailedPumpSetup(enabledPumpingInThisInitialize, callbackPumpingEnabled, createdPumpGameObject, createdPumpComponent, "Initialize.DisablePumpingAfterPumpSetupFailure");
				IsInitialized = false;
			}
			else if (!TryInitializeSteamCallbacksAndCrypto())
			{
				CleanupFailedPumpSetup(enabledPumpingInThisInitialize, callbackPumpingEnabled, createdPumpGameObject, createdPumpComponent, "Initialize.DisablePumpingAfterCallbackFailure");
				IsInitialized = false;
			}
			else
			{
				IsInitialized = true;
				LogInfo("SteamNetworkingService initialized");
			}
		}

		private static SteamCallbackPump PrepareCanonicalSteamCallbackPump(out GameObject? createdPumpGameObject, out SteamCallbackPump? createdPumpComponent)
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Expected O, but got Unknown
			createdPumpGameObject = null;
			createdPumpComponent = null;
			List<SteamCallbackPump> source = (from p in Object.FindObjectsByType<SteamCallbackPump>((FindObjectsInactive)1, (FindObjectsSortMode)0)
				where (Object)(object)p != (Object)null && (Object)(object)((Component)p).gameObject != (Object)null
				orderby ((Behaviour)p).isActiveAndEnabled descending, ((Component)p).gameObject.activeInHierarchy descending, ((Object)p).GetInstanceID()
				select p).ToList();
			SteamCallbackPump steamCallbackPump = source.FirstOrDefault();
			if ((Object)(object)steamCallbackPump == (Object)null)
			{
				GameObject val = GameObject.Find("SteamCallbackPump");
				if ((Object)(object)val == (Object)null)
				{
					val = (createdPumpGameObject = new GameObject("SteamCallbackPump"));
					LogInfo("Created SteamCallbackPump GameObject.");
				}
				steamCallbackPump = val.GetComponent<SteamCallbackPump>();
				if ((Object)(object)steamCallbackPump == (Object)null)
				{
					steamCallbackPump = val.AddComponent<SteamCallbackPump>();
					if ((Object)(object)createdPumpGameObject == (Object)null)
					{
						createdPumpComponent = steamCallbackPump;
					}
				}
			}
			else
			{
				CleanupDuplicatePumps(steamCallbackPump, source.Skip(1));
			}
			GameObject gameObject = ((Component)steamCallbackPump).gameObject;
			if (!gameObject.activeSelf)
			{
				gameObject.SetActive(true);
			}
			if (!gameObject.activeInHierarchy)
			{
				gameObject.transform.SetParent((Transform)null, true);
				if (!gameObject.activeSelf)
				{
					gameObject.SetActive(true);
				}
			}
			if (!((Behaviour)steamCallbackPump).enabled)
			{
				((Behaviour)steamCallbackPump).enabled = true;
			}
			Object.DontDestroyOnLoad((Object)(object)gameObject);
			return steamCallbackPump;
		}

		private static void CleanupDuplicatePumps(SteamCallbackPump canonicalPump, IEnumerable<SteamCallbackPump> duplicatePumps)
		{
			foreach (SteamCallbackPump duplicatePump in duplicatePumps)
			{
				if (!((Object)(object)duplicatePump == (Object)null))
				{
					GameObject gameObject = ((Component)duplicatePump).gameObject;
					if (!((Object)(object)gameObject == (Object)null))
					{
						bool flag = HasOnlyTransformAndSteamCallbackPumpComponents(gameObject);
						bool flag2 = flag && (Object)(object)gameObject.transform != (Object)null && ((Component)canonicalPump).transform.IsChildOf(gameObject.transform);
						DestroyPumpDuringStartup((Object)(object)((flag && !flag2) ? ((SteamCallbackPump)(object)gameObject) : duplicatePump));
					}
				}
			}
		}

		private static bool HasOnlyTransformAndSteamCallbackPumpComponents(GameObject pumpObject)
		{
			Component[] components = pumpObject.GetComponents<Component>();
			bool flag = false;
			bool flag2 = false;
			int num = 0;
			foreach (Component val in components)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				num++;
				if (val is Transform)
				{
					flag = true;
					continue;
				}
				if (val is SteamCallbackPump)
				{
					flag2 = true;
					continue;
				}
				return false;
			}
			return num == 2 && flag && flag2;
		}

		private static void DestroyPumpDuringStartup(Object target)
		{
			if (target is SteamCallbackPump steamCallbackPump)
			{
				((Behaviour)steamCallbackPump).enabled = false;
			}
			else
			{
				GameObject val = (GameObject)(object)((target is GameObject) ? target : null);
				if (val != null)
				{
					val.SetActive(false);
				}
			}
			if (Application.isPlaying)
			{
				Object.Destroy(target);
			}
			else
			{
				Object.DestroyImmediate(target);
			}
		}

		private void CleanupFailedPumpSetup(bool enabledPumpingInThisInit