Decompiled source of InteractiveMapCompanion v2026.718.0

plugins/InteractiveMapCompanion/InteractiveMapCompanion.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Data.SqlTypes;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Numerics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Authentication;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ErenshorMods.Input;
using Fleck;
using Fleck.Handlers;
using Fleck.Helpers;
using HarmonyLib;
using InteractiveMapCompanion.Config;
using InteractiveMapCompanion.Entities;
using InteractiveMapCompanion.Overlay;
using InteractiveMapCompanion.Patches;
using InteractiveMapCompanion.Protocol;
using InteractiveMapCompanion.Server;
using InteractiveMapCompanion.State;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Bson;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Linq.JsonPath;
using Newtonsoft.Json.Schema;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Utilities;
using Steamworks;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("InteractiveMapCompanion")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+a826b99152e16b11b7c8914bcf0596f0d0d7876a")]
[assembly: AssemblyProduct("InteractiveMapCompanion")]
[assembly: AssemblyTitle("InteractiveMapCompanion")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ErenshorMods.Input
{
	public interface IKeyboardInput
	{
		bool IsHeld(KeyCode key);

		bool WasPressed(KeyCode key);
	}
	public sealed class UnityKeyboardInput : IKeyboardInput
	{
		public static UnityKeyboardInput Instance { get; } = new UnityKeyboardInput();

		private UnityKeyboardInput()
		{
		}

		public bool IsHeld(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return Input.GetKey(key);
		}

		public bool WasPressed(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return Input.GetKeyDown(key);
		}
	}
	public static class KeyboardShortcuts
	{
		public static bool WasPressed(KeyCode key, IKeyboardInput keyboard)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			return (int)key != 0 && keyboard.WasPressed(key);
		}

		public static bool IsHeld(KeyCode key, IKeyboardInput keyboard)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			return (int)key != 0 && keyboard.IsHeld(key);
		}

		public static bool IsHeld(IReadOnlyList<KeyCode> keys, IKeyboardInput keyboard)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if (keys.Count == 0)
			{
				return false;
			}
			for (int i = 0; i < keys.Count; i++)
			{
				if ((int)keys[i] == 0 || !keyboard.IsHeld(keys[i]))
				{
					return false;
				}
			}
			return true;
		}

		public static bool IsHeld(KeyCode mainKey, IReadOnlyList<KeyCode> modifiers, IKeyboardInput keyboard)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if ((int)mainKey == 0 || !keyboard.IsHeld(mainKey))
			{
				return false;
			}
			for (int i = 0; i < modifiers.Count; i++)
			{
				if ((int)modifiers[i] == 0 || !keyboard.IsHeld(modifiers[i]))
				{
					return false;
				}
			}
			return true;
		}
	}
}
namespace InteractiveMapCompanion
{
	public interface IModLogger
	{
		void LogDebug(string message);

		void LogInfo(string message);

		void LogWarning(string message);

		void LogError(string message);
	}
	public sealed class InteractiveMapRuntime
	{
		private readonly GameObject _owner;

		private readonly IModConfig _config;

		private readonly IModLogger _log;

		private Harmony? _harmony;

		private InteractiveMapCompanion.Server.IWebSocketServer? _server;

		private IBroadcastLoop? _broadcastLoop;

		private MapOverlay? _overlay;

		private bool _started;

		private bool _stopped;

		private bool _applicationQuitting;

		public InteractiveMapRuntime(GameObject owner, IModConfig config, IModLogger log)
		{
			_owner = owner;
			_config = config;
			_log = log;
		}

		public void Start()
		{
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Expected O, but got Unknown
			if (_started || _stopped)
			{
				return;
			}
			_started = true;
			try
			{
				EntityFinder finder = new EntityFinder();
				EntityClassifier classifier = new EntityClassifier();
				EntityExtractor extractor = new EntityExtractor();
				EntityTrackerAdapter entityTracker = new EntityTrackerAdapter(finder, classifier, extractor, (EntityType _) => true);
				_server = new InteractiveMapCompanion.Server.WebSocketServer(_config, _log);
				_server.Start();
				_broadcastLoop = new BroadcastLoop(entityTracker, _server, _config, delegate(string message)
				{
					if (_config.ModLogLevel == InteractiveMapCompanion.Config.LogLevel.Debug)
					{
						_log.LogDebug(message);
					}
				});
				SceneManager.sceneLoaded += OnSceneLoaded;
				IBroadcastLoop? broadcastLoop = _broadcastLoop;
				Scene activeScene = SceneManager.GetActiveScene();
				broadcastLoop.OnSceneLoaded(((Scene)(ref activeScene)).name);
				_overlay = _owner.AddComponent<MapOverlay>();
				_overlay.Config = _config;
				_overlay.Log = _log;
				_harmony = new Harmony("wow-much.interactive-map-companion");
				_harmony.PatchAll();
				_log.LogInfo("Interactive Map Companion v2026.718.0 loaded");
			}
			catch (Exception arg)
			{
				_log.LogError(string.Format("Failed to start {0}: {1}", "Interactive Map Companion", arg));
				Stop();
			}
		}

		public void Tick(float deltaTime, bool togglePressed)
		{
			if (_started && !_stopped)
			{
				_overlay?.HandleShortcut(togglePressed);
				_broadcastLoop?.Tick(deltaTime);
			}
		}

		public void NotifyApplicationQuitting()
		{
			if (!_applicationQuitting)
			{
				_applicationQuitting = true;
				_overlay?.NotifyApplicationQuitting();
			}
		}

		public void Stop()
		{
			if (!_stopped)
			{
				_stopped = true;
				_broadcastLoop?.Stop();
				_server?.Stop();
				_overlay?.Stop();
				if ((Object)(object)_overlay != (Object)null)
				{
					Object.Destroy((Object)(object)_overlay);
				}
				SceneManager.sceneLoaded -= OnSceneLoaded;
				Harmony? harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
				MapKeyPatches.SuppressMapKey = false;
				CharSelectManagerPatch.ResetPlayerTyping();
				_overlay = null;
				_broadcastLoop = null;
				_server = null;
				_harmony = null;
			}
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			if (!_stopped)
			{
				_broadcastLoop?.OnSceneLoaded(((Scene)(ref scene)).name);
				if (((Scene)(ref scene)).name != "LoadScene")
				{
					CharSelectManagerPatch.ResetPlayerTyping();
				}
			}
		}
	}
	internal static class PluginInfo
	{
		public const string GUID = "wow-much.interactive-map-companion";

		public const string Name = "Interactive Map Companion";

		public const string Version = "2026.718.0";
	}
	[BepInPlugin("wow-much.interactive-map-companion", "Interactive Map Companion", "2026.718.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private sealed class BepModLogger : IModLogger
		{
			private readonly ManualLogSource _logger;

			internal BepModLogger(ManualLogSource logger)
			{
				_logger = logger;
			}

			public void LogDebug(string message)
			{
				_logger.LogDebug((object)message);
			}

			public void LogInfo(string message)
			{
				_logger.LogInfo((object)message);
			}

			public void LogWarning(string message)
			{
				_logger.LogWarning((object)message);
			}

			public void LogError(string message)
			{
				_logger.LogError((object)message);
			}
		}

		private sealed class BepModConfig : ModConfigBase
		{
			private readonly ConfigEntry<int> _port;

			private readonly ConfigEntry<int> _updateInterval;

			private readonly ConfigEntry<InteractiveMapCompanion.Config.LogLevel> _webSocketLogLevel;

			private readonly ConfigEntry<InteractiveMapCompanion.Config.LogLevel> _modLogLevel;

			private readonly ConfigEntry<bool> _enableOverlay;

			private readonly ConfigEntry<KeyCode> _toggleKey;

			private readonly ConfigEntry<float> _anchorX;

			private readonly ConfigEntry<float> _anchorY;

			private readonly ConfigEntry<int> _overlayWidth;

			private readonly ConfigEntry<int> _overlayHeight;

			private readonly ConfigEntry<bool> _resetToDefaults;

			public override int Port => _port.Value;

			public override int UpdateInterval => _updateInterval.Value;

			public override InteractiveMapCompanion.Config.LogLevel WebSocketLogLevel => _webSocketLogLevel.Value;

			public override InteractiveMapCompanion.Config.LogLevel ModLogLevel => _modLogLevel.Value;

			public override bool EnableOverlay => _enableOverlay.Value;

			public override KeyCode ToggleKey => _toggleKey.Value;

			public override float AnchorX
			{
				get
				{
					return _anchorX.Value;
				}
				set
				{
					_anchorX.Value = value;
				}
			}

			public override float AnchorY
			{
				get
				{
					return _anchorY.Value;
				}
				set
				{
					_anchorY.Value = value;
				}
			}

			public override int OverlayWidth
			{
				get
				{
					return _overlayWidth.Value;
				}
				set
				{
					_overlayWidth.Value = value;
				}
			}

			public override int OverlayHeight
			{
				get
				{
					return _overlayHeight.Value;
				}
				set
				{
					_overlayHeight.Value = value;
				}
			}

			public override bool ResetToDefaults
			{
				get
				{
					return _resetToDefaults.Value;
				}
				set
				{
					_resetToDefaults.Value = value;
				}
			}

			internal BepModConfig(ConfigFile config)
			{
				_port = config.Bind<int>("Server", "Port", 18585, "WebSocket server port. Clients connect to ws://localhost:{port}");
				_updateInterval = config.Bind<int>("Server", "UpdateInterval", 100, "Interval in milliseconds between state broadcasts to clients");
				_webSocketLogLevel = config.Bind<InteractiveMapCompanion.Config.LogLevel>("Logging", "WebSocketLogLevel", InteractiveMapCompanion.Config.LogLevel.Warning, "Log level for WebSocket library. Debug shows all messages (verbose), Warning shows only issues (recommended).");
				_modLogLevel = config.Bind<InteractiveMapCompanion.Config.LogLevel>("Logging", "ModLogLevel", InteractiveMapCompanion.Config.LogLevel.Info, "Log level for the mod itself. Debug shows detailed diagnostics, Info shows important events (recommended).");
				_enableOverlay = config.Bind<bool>("Overlay", "EnableOverlay", true, "Show the interactive map as an in-game overlay panel (requires Steam)");
				_toggleKey = config.Bind<KeyCode>("Overlay", "ToggleKey", (KeyCode)109, "Key to show/hide the in-game map overlay");
				_anchorX = config.Bind<float>("Overlay", "AnchorX", -1f, "Normalized horizontal anchor for the overlay panel (0 = left edge, 1 = right edge). -1 = auto (centred, computed on first run)");
				_anchorY = config.Bind<float>("Overlay", "AnchorY", -1f, "Normalized vertical anchor for the overlay panel (0 = bottom, 1 = top). -1 = auto (centred, computed on first run)");
				_overlayWidth = config.Bind<int>("Overlay", "Width", 0, "Width of the in-game map overlay in pixels. 0 = auto (80% of screen width, computed on first run)");
				_overlayHeight = config.Bind<int>("Overlay", "Height", 0, "Height of the in-game map overlay in pixels. 0 = auto (80% of screen height, computed on first run)");
				_resetToDefaults = config.Bind<bool>("Overlay", "ResetToDefaults", false, "Set to true to reset size and position to auto-computed defaults on next game launch. Resets itself to false automatically.");
			}
		}

		private InteractiveMapRuntime? _runtime;

		private BepModConfig? _config;

		private void Awake()
		{
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			_config = new BepModConfig(((BaseUnityPlugin)this).Config);
			BepModLogger log = new BepModLogger(((BaseUnityPlugin)this).Logger);
			_runtime = new InteractiveMapRuntime(((Component)this).gameObject, _config, log);
			_runtime.Start();
		}

		private void Update()
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			bool togglePressed = _config != null && KeyboardShortcuts.WasPressed(_config.ToggleKey, UnityKeyboardInput.Instance);
			_runtime?.Tick(Time.deltaTime, togglePressed);
		}

		private void OnApplicationQuit()
		{
			_runtime?.NotifyApplicationQuitting();
		}

		private void OnDestroy()
		{
			_runtime?.Stop();
			_runtime = null;
			_config = null;
		}
	}
}
namespace InteractiveMapCompanion.State
{
	public sealed class BroadcastLoop : IBroadcastLoop
	{
		private readonly IEntityTracker _entityTracker;

		private readonly InteractiveMapCompanion.Server.IWebSocketServer _server;

		private readonly IModConfig _config;

		private readonly Action<string>? _log;

		private float _elapsed;

		private string _currentZone = "";

		private bool _stopped;

		public BroadcastLoop(IEntityTracker entityTracker, InteractiveMapCompanion.Server.IWebSocketServer server, IModConfig config, Action<string>? log = null)
		{
			_entityTracker = entityTracker;
			_server = server;
			_config = config;
			_log = log;
		}

		public void Tick(float deltaTime)
		{
			if (!_stopped)
			{
				_elapsed += deltaTime;
				float num = (float)_config.UpdateInterval / 1000f;
				if (!(_elapsed < num))
				{
					_elapsed = 0f;
					BroadcastState();
				}
			}
		}

		public void OnSceneLoaded(string newZone)
		{
			if (!_stopped)
			{
				string currentZone = _currentZone;
				_currentZone = newZone;
				if (!string.IsNullOrEmpty(currentZone) && currentZone != newZone)
				{
					SendZoneChange(currentZone, newZone);
				}
				BroadcastState();
			}
		}

		public void Stop()
		{
			if (!_stopped)
			{
				_stopped = true;
				_elapsed = 0f;
				_currentZone = "";
			}
		}

		private void BroadcastState()
		{
			if (_stopped || _server.ClientCount == 0)
			{
				return;
			}
			try
			{
				IReadOnlyList<EntityData> trackedEntities = _entityTracker.GetTrackedEntities();
				StateUpdateMessage message = StateUpdateMessage.Create(_currentZone, trackedEntities.ToArray());
				string message2 = MessageSerializer.Serialize(message);
				_server.Broadcast(message2);
			}
			catch (Exception ex)
			{
				_log?.Invoke("Error broadcasting state: " + ex.Message);
			}
		}

		private void SendZoneChange(string previousZone, string newZone)
		{
			if (_stopped || _server.ClientCount == 0)
			{
				return;
			}
			try
			{
				ZoneChangeMessage message = ZoneChangeMessage.Create(previousZone, newZone);
				string message2 = MessageSerializer.Serialize(message);
				_server.Broadcast(message2);
				_log?.Invoke("Zone changed: " + previousZone + " -> " + newZone);
			}
			catch (Exception ex)
			{
				_log?.Invoke("Error sending zone change: " + ex.Message);
			}
		}
	}
	public interface IBroadcastLoop
	{
		void Tick(float deltaTime);

		void Stop();

		void OnSceneLoaded(string newZone);
	}
}
namespace InteractiveMapCompanion.Server
{
	public interface IWebSocketServer : IDisposable
	{
		int ClientCount { get; }

		void Start();

		void Stop();

		void Broadcast(string message);
	}
	public sealed class WebSocketServer : IWebSocketServer, IDisposable
	{
		private readonly IModConfig _config;

		private readonly IModLogger _logger;

		private readonly ConcurrentDictionary<Guid, IWebSocketConnection> _clients = new ConcurrentDictionary<Guid, IWebSocketConnection>();

		private readonly object _lifecycleGate = new object();

		private Fleck.WebSocketServer? _server;

		private Action<Fleck.LogLevel, string, Exception>? _previousFleckLogAction;

		private Action<Fleck.LogLevel, string, Exception>? _fleckLogAction;

		private bool _stopped;

		private bool _disposed;

		public int ClientCount => _clients.Count;

		public WebSocketServer(IModConfig config, IModLogger logger)
		{
			_config = config;
			_logger = logger;
			ConfigureFleckLogging();
		}

		public void Start()
		{
			int port = _config.Port;
			string text = $"ws://0.0.0.0:{port}";
			lock (_lifecycleGate)
			{
				if (_disposed || _server != null)
				{
					return;
				}
				try
				{
					Fleck.WebSocketServer webSocketServer = new Fleck.WebSocketServer(text);
					_stopped = false;
					_server = webSocketServer;
					webSocketServer.Start(ConfigureSocket);
					_logger.LogInfo("WebSocket server started on " + text);
				}
				catch (Exception ex)
				{
					_server = null;
					_stopped = true;
					_logger.LogError($"Failed to start WebSocket server on port {port}: {ex.Message}");
					_logger.LogDebug(ex.ToString());
				}
			}
		}

		public void Stop()
		{
			Fleck.WebSocketServer server;
			IWebSocketConnection[] array;
			lock (_lifecycleGate)
			{
				if (_stopped && _server == null && _clients.IsEmpty)
				{
					return;
				}
				_stopped = true;
				server = _server;
				_server = null;
				array = _clients.Values.ToArray();
				_clients.Clear();
			}
			IWebSocketConnection[] array2 = array;
			foreach (IWebSocketConnection webSocketConnection in array2)
			{
				try
				{
					webSocketConnection.Close();
				}
				catch
				{
				}
			}
			try
			{
				server?.Dispose();
			}
			catch (Exception ex)
			{
				_logger.LogDebug("WebSocket server disposal failed: " + ex.Message);
			}
			if (server != null)
			{
				_logger.LogInfo("WebSocket server stopped");
			}
		}

		public void Broadcast(string message)
		{
			if (_disposed || _stopped)
			{
				return;
			}
			foreach (KeyValuePair<Guid, IWebSocketConnection> client in _clients)
			{
				client.Deconstruct(out var key, out var value);
				Guid guid = key;
				IWebSocketConnection webSocketConnection = value;
				try
				{
					if (webSocketConnection.IsAvailable && !_stopped && !_disposed)
					{
						webSocketConnection.Send(message);
					}
					else
					{
						_clients.TryRemove(guid, out value);
					}
				}
				catch (Exception ex)
				{
					_logger.LogWarning($"Failed to send to client {guid}: {ex.Message}");
					_clients.TryRemove(guid, out value);
				}
			}
		}

		public void Dispose()
		{
			lock (_lifecycleGate)
			{
				if (_disposed)
				{
					return;
				}
				_disposed = true;
			}
			Stop();
			RestoreFleckLogging();
		}

		private void ConfigureSocket(IWebSocketConnection socket)
		{
			socket.OnOpen = delegate
			{
				OnClientConnected(socket);
			};
			socket.OnClose = delegate
			{
				OnClientDisconnected(socket);
			};
			socket.OnError = delegate(Exception ex)
			{
				OnClientError(socket, ex);
			};
			socket.OnMessage = delegate(string message)
			{
				OnClientMessage(socket, message);
			};
		}

		private void OnClientConnected(IWebSocketConnection socket)
		{
			bool flag;
			lock (_lifecycleGate)
			{
				flag = _disposed || _stopped;
				if (!flag)
				{
					_clients[socket.ConnectionInfo.Id] = socket;
				}
			}
			if (flag)
			{
				try
				{
					socket.Close();
					return;
				}
				catch
				{
					return;
				}
			}
			_logger.LogInfo($"Client connected: {socket.ConnectionInfo.ClientIpAddress} (total: {ClientCount})");
			SendHandshake(socket);
		}

		private void OnClientDisconnected(IWebSocketConnection socket)
		{
			_clients.TryRemove(socket.ConnectionInfo.Id, out IWebSocketConnection _);
			if (!_disposed)
			{
				_logger.LogInfo($"Client disconnected: {socket.ConnectionInfo.ClientIpAddress} (total: {ClientCount})");
			}
		}

		private void OnClientError(IWebSocketConnection socket, Exception ex)
		{
			if (!_disposed)
			{
				_logger.LogWarning("Client error (" + socket.ConnectionInfo.ClientIpAddress + "): " + ex.Message);
			}
			_clients.TryRemove(socket.ConnectionInfo.Id, out IWebSocketConnection _);
		}

		private void OnClientMessage(IWebSocketConnection socket, string message)
		{
			if (!_disposed && !_stopped)
			{
				_logger.LogDebug("Received message from " + socket.ConnectionInfo.ClientIpAddress + ": " + message);
			}
		}

		private void SendHandshake(IWebSocketConnection socket)
		{
			string currentZone = GetCurrentZone();
			string[] capabilities = _config.GetCapabilities();
			HandshakeMessage message = HandshakeMessage.Create(currentZone, capabilities);
			string message2 = MessageSerializer.Serialize(message);
			try
			{
				if (!_disposed && !_stopped)
				{
					socket.Send(message2);
				}
			}
			catch (Exception ex)
			{
				_logger.LogWarning("Failed to send handshake: " + ex.Message);
			}
		}

		private static string GetCurrentZone()
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Scene activeScene = SceneManager.GetActiveScene();
				return ((Scene)(ref activeScene)).name;
			}
			catch
			{
				return "";
			}
		}

		private void ConfigureFleckLogging()
		{
			_previousFleckLogAction = FleckLog.LogAction;
			_fleckLogAction = delegate(Fleck.LogLevel level, string message, Exception ex)
			{
				InteractiveMapCompanion.Config.LogLevel webSocketLogLevel = _config.WebSocketLogLevel;
				if (1 == 0)
				{
				}
				bool flag = level switch
				{
					Fleck.LogLevel.Debug => webSocketLogLevel == InteractiveMapCompanion.Config.LogLevel.Debug, 
					Fleck.LogLevel.Info => webSocketLogLevel == InteractiveMapCompanion.Config.LogLevel.Debug || webSocketLogLevel == InteractiveMapCompanion.Config.LogLevel.Info, 
					Fleck.LogLevel.Warn => webSocketLogLevel == InteractiveMapCompanion.Config.LogLevel.Debug || webSocketLogLevel == InteractiveMapCompanion.Config.LogLevel.Info || webSocketLogLevel == InteractiveMapCompanion.Config.LogLevel.Warning, 
					Fleck.LogLevel.Error => true, 
					_ => false, 
				};
				if (1 == 0)
				{
				}
				if (flag)
				{
					switch (level)
					{
					case Fleck.LogLevel.Debug:
						_logger.LogDebug("[Fleck] " + message);
						break;
					case Fleck.LogLevel.Info:
						_logger.LogInfo("[Fleck] " + message);
						break;
					case Fleck.LogLevel.Warn:
						_logger.LogWarning("[Fleck] " + message);
						break;
					case Fleck.LogLevel.Error:
						_logger.LogError("[Fleck] " + message);
						if (ex != null)
						{
							_logger.LogDebug(ex.ToString());
						}
						break;
					}
				}
			};
			FleckLog.LogAction = _fleckLogAction;
		}

		private void RestoreFleckLogging()
		{
			if (_fleckLogAction != null && (object)FleckLog.LogAction == _fleckLogAction)
			{
				FleckLog.LogAction = _previousFleckLogAction;
			}
			_fleckLogAction = null;
			_previousFleckLogAction = null;
		}
	}
}
namespace InteractiveMapCompanion.Protocol
{
	public sealed class HandshakeMessage
	{
		public string Type { get; }

		public string ProtocolVersion { get; }

		public string ModVersion { get; }

		public string Zone { get; }

		public string[] Capabilities { get; }

		public HandshakeMessage(string Type, string ProtocolVersion, string ModVersion, string Zone, string[] Capabilities)
		{
			this.Type = Type;
			this.ProtocolVersion = ProtocolVersion;
			this.ModVersion = ModVersion;
			this.Zone = Zone;
			this.Capabilities = Capabilities;
		}

		public static HandshakeMessage Create(string zone, string[] capabilities)
		{
			return new HandshakeMessage("handshake", "0.2.0", "2026.718.0", zone, capabilities);
		}
	}
	public sealed class StateUpdateMessage
	{
		public string Type { get; }

		public string Zone { get; }

		public long Timestamp { get; }

		public EntityData[] Entities { get; }

		public StateUpdateMessage(string Type, string Zone, long Timestamp, EntityData[] Entities)
		{
			this.Type = Type;
			this.Zone = Zone;
			this.Timestamp = Timestamp;
			this.Entities = Entities;
		}

		public static StateUpdateMessage Create(string zone, EntityData[] entities)
		{
			return new StateUpdateMessage("stateUpdate", zone, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), entities);
		}
	}
	public sealed class ZoneChangeMessage
	{
		public string Type { get; }

		public string PreviousZone { get; }

		public string Zone { get; }

		public long Timestamp { get; }

		public ZoneChangeMessage(string Type, string PreviousZone, string Zone, long Timestamp)
		{
			this.Type = Type;
			this.PreviousZone = PreviousZone;
			this.Zone = Zone;
			this.Timestamp = Timestamp;
		}

		public static ZoneChangeMessage Create(string previousZone, string zone)
		{
			return new ZoneChangeMessage("zoneChange", previousZone, zone, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
		}
	}
	public static class MessageSerializer
	{
		private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
		{
			ContractResolver = new DefaultContractResolver
			{
				NamingStrategy = new CamelCaseNamingStrategy()
			},
			NullValueHandling = NullValueHandling.Ignore,
			Formatting = Newtonsoft.Json.Formatting.None
		};

		public static string Serialize<T>(T message)
		{
			return JsonConvert.SerializeObject(message, Settings);
		}

		public static T? Deserialize<T>(string json)
		{
			return JsonConvert.DeserializeObject<T>(json, Settings);
		}
	}
	public static class ProtocolVersion
	{
		public const string Current = "0.2.0";
	}
}
namespace InteractiveMapCompanion.Patches
{
	[HarmonyPatch(typeof(CharSelectManager), "Update")]
	internal static class CharSelectManagerPatch
	{
		internal static bool _weSetPlayerTyping;

		internal static void ResetPlayerTyping()
		{
			if (_weSetPlayerTyping)
			{
				GameData.PlayerTyping = false;
				_weSetPlayerTyping = false;
			}
		}

		[HarmonyPostfix]
		private static void Postfix(CharSelectManager __instance)
		{
			if (!((Object)(object)EventSystem.current == (Object)null))
			{
				int num;
				if (__instance.CharCreate.activeSelf)
				{
					GameObject currentSelectedGameObject = EventSystem.current.currentSelectedGameObject;
					num = ((((currentSelectedGameObject != null) ? ((Object)currentSelectedGameObject).name : null) == "InputField (TMP)") ? 1 : 0);
				}
				else
				{
					num = 0;
				}
				bool flag = (byte)num != 0;
				if (flag && !_weSetPlayerTyping)
				{
					GameData.PlayerTyping = true;
					_weSetPlayerTyping = true;
				}
				else if (!flag && _weSetPlayerTyping)
				{
					GameData.PlayerTyping = false;
					_weSetPlayerTyping = false;
				}
			}
		}
	}
	internal static class MapKeyPatches
	{
		internal static bool SuppressMapKey;

		internal static bool GetKeyDownUnlessSuppressed(KeyCode key)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			return !SuppressMapKey && Input.GetKeyDown(key);
		}
	}
	[HarmonyPatch(typeof(HotkeyManager), "OpenCloseMap")]
	internal static class OpenCloseMapPatch
	{
		[HarmonyPrefix]
		private static bool Prefix()
		{
			return !MapKeyPatches.SuppressMapKey;
		}
	}
	[HarmonyPatch(typeof(Minimap), "Update")]
	internal static class MinimapUpdatePatch
	{
		private static readonly MethodInfo _getKeyDown = typeof(Input).GetMethod("GetKeyDown", new Type[1] { typeof(KeyCode) });

		private static readonly FieldInfo _inputManagerMap = typeof(InputManager).GetField("Map");

		private static readonly MethodInfo _helper = typeof(MapKeyPatches).GetMethod("GetKeyDownUnlessSuppressed", BindingFlags.Static | BindingFlags.NonPublic);

		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			bool flag = false;
			for (int i = 0; i < list.Count - 1; i++)
			{
				bool flag2 = list[i].opcode == OpCodes.Ldsfld && list[i].operand is FieldInfo fieldInfo && fieldInfo == _inputManagerMap;
				bool flag3 = list[i + 1].opcode == OpCodes.Call && list[i + 1].operand is MethodInfo methodInfo && methodInfo == _getKeyDown;
				if (flag2 && flag3)
				{
					list[i + 1] = new CodeInstruction(OpCodes.Call, (object)_helper);
					flag = true;
					break;
				}
			}
			if (!flag)
			{
				throw new InvalidOperationException("[InteractiveMapCompanion] MinimapUpdatePatch: could not find Input.GetKeyDown(InputManager.Map) in Minimap.Update(). The game may have been updated — please update the transpiler.");
			}
			return list;
		}
	}
}
namespace InteractiveMapCompanion.Overlay
{
	internal sealed class BrowserManager : IDisposable
	{
		internal const string MapUrl = "https://erenshor.compendiums.org/map";

		private const string SameTabNavigationScript = "(function () {\n            if (window.__erenshorSameTab) return;\n            window.__erenshorSameTab = true;\n            window.open = function (url) {\n                if (url) window.location.href = url;\n                return null;\n            };\n            document.addEventListener('click', function (e) {\n                if (e.defaultPrevented || e.button !== 0) return;\n                var link = e.target && e.target.closest ? e.target.closest('a[href]') : null;\n                if (!link) return;\n                var target = (link.getAttribute('target') || '').toLowerCase();\n                if (target !== '_blank' && target !== '_new') return;\n                e.preventDefault();\n                window.location.href = link.href;\n            });\n        })();";

		private readonly IModLogger _log;

		private readonly Action<HTML_NeedsPaint_t> _onPaint;

		private HHTMLBrowser _browser;

		private bool _browserReady;

		private bool _initialized;

		private bool _visible;

		private bool _disposed;

		private bool _appIsQuitting;

		private bool _canGoBack;

		private bool _canGoForward;

		private string? _pendingNavigationUrl;

		private Callback<HTML_NeedsPaint_t>? _paintCallback;

		private Callback<HTML_StartRequest_t>? _startRequestCallback;

		private Callback<HTML_OpenLinkInNewTab_t>? _openLinkCallback;

		private Callback<HTML_NewWindow_t>? _newWindowCallback;

		private Callback<HTML_FinishedRequest_t>? _finishedRequestCallback;

		private Callback<HTML_JSAlert_t>? _jsAlertCallback;

		private Callback<HTML_JSConfirm_t>? _jsConfirmCallback;

		private Callback<HTML_FileOpenDialog_t>? _fileOpenDialogCallback;

		private Callback<HTML_CanGoBackAndForward_t>? _historyCallback;

		private CallResult<HTML_BrowserReady_t>? _browserReadyResult;

		internal bool IsReady => _browserReady;

		internal bool CanGoBack => _browserReady && _canGoBack;

		internal bool CanGoForward => _browserReady && _canGoForward;

		internal HHTMLBrowser BrowserHandle => _browser;

		internal event Action? NavigationStateChanged;

		internal BrowserManager(IModLogger log, Action<HTML_NeedsPaint_t> onPaint)
		{
			_log = log;
			_onPaint = onPaint;
		}

		internal void NotifyAppIsQuitting()
		{
			_appIsQuitting = true;
		}

		internal bool Initialize(int width, int height, string url)
		{
			if (_disposed || _appIsQuitting)
			{
				return false;
			}
			if (_initialized)
			{
				return true;
			}
			if (!SteamHTMLSurface.Init())
			{
				_log.LogWarning("[Overlay] SteamHTMLSurface.Init() failed — map overlay disabled.");
				return false;
			}
			_initialized = true;
			RegisterCallbacks();
			CreateBrowser(width, height, url);
			return true;
		}

		internal void SetVisible(bool visible)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			_visible = visible;
			if (!_disposed && !_appIsQuitting && _browserReady)
			{
				SteamHTMLSurface.SetBackgroundMode(_browser, !visible);
			}
		}

		internal void SetSize(int width, int height)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			if (!_disposed && !_appIsQuitting && _browserReady)
			{
				SteamHTMLSurface.SetSize(_browser, (uint)width, (uint)height);
			}
		}

		internal void LoadUrl(string url)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			if (!_disposed && !_appIsQuitting && _browserReady)
			{
				SteamHTMLSurface.LoadURL(_browser, url, (string)null);
			}
		}

		internal void GoBack()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			if (!_disposed && !_appIsQuitting && CanGoBack)
			{
				SteamHTMLSurface.GoBack(_browser);
			}
		}

		internal void ProcessPendingNavigation()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			if (!_disposed && !_appIsQuitting && _browserReady && _pendingNavigationUrl != null)
			{
				string pendingNavigationUrl = _pendingNavigationUrl;
				_pendingNavigationUrl = null;
				_log.LogInfo("[Overlay] Opening external link: " + pendingNavigationUrl);
				SteamHTMLSurface.LoadURL(_browser, pendingNavigationUrl, (string)null);
			}
		}

		internal void GoForward()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			if (!_disposed && !_appIsQuitting && CanGoForward)
			{
				SteamHTMLSurface.GoForward(_browser);
			}
		}

		internal void LoadMap()
		{
			LoadUrl("https://erenshor.compendiums.org/map");
		}

		private void RegisterCallbacks()
		{
			_paintCallback = Callback<HTML_NeedsPaint_t>.Create((DispatchDelegate<HTML_NeedsPaint_t>)OnNeedsPaint);
			_startRequestCallback = Callback<HTML_StartRequest_t>.Create((DispatchDelegate<HTML_StartRequest_t>)OnStartRequest);
			_openLinkCallback = Callback<HTML_OpenLinkInNewTab_t>.Create((DispatchDelegate<HTML_OpenLinkInNewTab_t>)OnOpenLinkInNewTab);
			_newWindowCallback = Callback<HTML_NewWindow_t>.Create((DispatchDelegate<HTML_NewWindow_t>)OnNewWindow);
			_finishedRequestCallback = Callback<HTML_FinishedRequest_t>.Create((DispatchDelegate<HTML_FinishedRequest_t>)OnFinishedRequest);
			_jsAlertCallback = Callback<HTML_JSAlert_t>.Create((DispatchDelegate<HTML_JSAlert_t>)OnJSAlert);
			_jsConfirmCallback = Callback<HTML_JSConfirm_t>.Create((DispatchDelegate<HTML_JSConfirm_t>)OnJSConfirm);
			_fileOpenDialogCallback = Callback<HTML_FileOpenDialog_t>.Create((DispatchDelegate<HTML_FileOpenDialog_t>)OnFileOpenDialog);
			_historyCallback = Callback<HTML_CanGoBackAndForward_t>.Create((DispatchDelegate<HTML_CanGoBackAndForward_t>)OnHistoryChanged);
		}

		private void CreateBrowser(int width, int height, string url)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			SteamAPICall_t val = SteamHTMLSurface.CreateBrowser((string)null, (string)null);
			_browserReadyResult = CallResult<HTML_BrowserReady_t>.Create((APIDispatchDelegate<HTML_BrowserReady_t>)delegate(HTML_BrowserReady_t param, bool ioFailure)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				OnBrowserReady(param, ioFailure, width, height, url);
			});
			_browserReadyResult.Set(val, (APIDispatchDelegate<HTML_BrowserReady_t>)null);
			_log.LogInfo("[Overlay] Browser creation requested, waiting for ready callback...");
		}

		private void OnBrowserReady(HTML_BrowserReady_t param, bool ioFailure, int width, int height, string url)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: 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_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			if (!_disposed && !_appIsQuitting)
			{
				if (ioFailure)
				{
					_log.LogWarning("[Overlay] Browser creation failed (IO failure) — map overlay disabled.");
					return;
				}
				_browser = param.unBrowserHandle;
				_browserReady = true;
				_canGoBack = false;
				_canGoForward = false;
				SteamHTMLSurface.SetSize(_browser, (uint)width, (uint)height);
				SteamHTMLSurface.LoadURL(_browser, url, (string)null);
				SteamHTMLSurface.SetBackgroundMode(_browser, !_visible);
				this.NavigationStateChanged?.Invoke();
				_log.LogInfo($"[Overlay] Browser ready (handle={_browser}), surface={width}x{height}, loading {url}");
			}
		}

		private void OnNeedsPaint(HTML_NeedsPaint_t param)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (!_disposed && !_appIsQuitting && _visible && !(param.unBrowserHandle != _browser))
			{
				_onPaint(param);
			}
		}

		private void OnStartRequest(HTML_StartRequest_t param)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (!_disposed && !_appIsQuitting)
			{
				SteamHTMLSurface.AllowStartRequest(param.unBrowserHandle, true);
			}
		}

		private void OnOpenLinkInNewTab(HTML_OpenLinkInNewTab_t param)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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)
			if (!_disposed && !_appIsQuitting && !(param.unBrowserHandle != _browser))
			{
				QueueExternalNavigation(param.pchURL);
			}
		}

		private void OnNewWindow(HTML_NewWindow_t param)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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)
			if (!_disposed && !_appIsQuitting && !(param.unBrowserHandle != _browser))
			{
				QueueExternalNavigation(param.pchURL);
			}
		}

		private void QueueExternalNavigation(string url)
		{
			if (!string.IsNullOrWhiteSpace(url))
			{
				_pendingNavigationUrl = url;
			}
		}

		private void OnFinishedRequest(HTML_FinishedRequest_t param)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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)
			if (!_disposed && !_appIsQuitting && !(param.unBrowserHandle != _browser))
			{
				SteamHTMLSurface.ExecuteJavascript(_browser, "(function () {\n            if (window.__erenshorSameTab) return;\n            window.__erenshorSameTab = true;\n            window.open = function (url) {\n                if (url) window.location.href = url;\n                return null;\n            };\n            document.addEventListener('click', function (e) {\n                if (e.defaultPrevented || e.button !== 0) return;\n                var link = e.target && e.target.closest ? e.target.closest('a[href]') : null;\n                if (!link) return;\n                var target = (link.getAttribute('target') || '').toLowerCase();\n                if (target !== '_blank' && target !== '_new') return;\n                e.preventDefault();\n                window.location.href = link.href;\n            });\n        })();");
			}
		}

		private void OnHistoryChanged(HTML_CanGoBackAndForward_t param)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			if (!_disposed && !_appIsQuitting && !(param.unBrowserHandle != _browser) && (_canGoBack != param.bCanGoBack || _canGoForward != param.bCanGoForward))
			{
				_canGoBack = param.bCanGoBack;
				_canGoForward = param.bCanGoForward;
				this.NavigationStateChanged?.Invoke();
			}
		}

		private void OnJSAlert(HTML_JSAlert_t param)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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)
			if (!_disposed && !_appIsQuitting)
			{
				if (param.unBrowserHandle == _browser)
				{
					_log.LogDebug("[Overlay] JS alert: " + param.pchMessage);
				}
				SteamHTMLSurface.JSDialogResponse(param.unBrowserHandle, true);
			}
		}

		private void OnJSConfirm(HTML_JSConfirm_t param)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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)
			if (!_disposed && !_appIsQuitting)
			{
				if (param.unBrowserHandle == _browser)
				{
					_log.LogDebug("[Overlay] JS confirm: " + param.pchMessage);
				}
				SteamHTMLSurface.JSDialogResponse(param.unBrowserHandle, true);
			}
		}

		private void OnFileOpenDialog(HTML_FileOpenDialog_t param)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (!_disposed && !_appIsQuitting)
			{
				SteamHTMLSurface.FileLoadDialogResponse(param.unBrowserHandle, IntPtr.Zero);
			}
		}

		public void Dispose()
		{
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			if (_disposed)
			{
				return;
			}
			_disposed = true;
			_paintCallback?.Dispose();
			_startRequestCallback?.Dispose();
			_openLinkCallback?.Dispose();
			_newWindowCallback?.Dispose();
			_finishedRequestCallback?.Dispose();
			_jsAlertCallback?.Dispose();
			_jsConfirmCallback?.Dispose();
			_fileOpenDialogCallback?.Dispose();
			_historyCallback?.Dispose();
			_browserReadyResult?.Dispose();
			if (!_appIsQuitting)
			{
				if (_browserReady)
				{
					SteamHTMLSurface.RemoveBrowser(_browser);
				}
				if (_initialized)
				{
					SteamHTMLSurface.Shutdown();
				}
			}
			_browserReady = false;
			_initialized = false;
			_canGoBack = false;
			_canGoForward = false;
			_pendingNavigationUrl = null;
			_browser = default(HHTMLBrowser);
		}
	}
	internal sealed class BrowserNavigationToolbar
	{
		private const int ControlWidth = 240;

		private const int ControlHeight = 32;

		private const int TopInset = 10;

		private const int BorderThickness = 1;

		private const int HorizontalPadding = 4;

		private const int ButtonGap = 4;

		private const int BackButtonWidth = 72;

		private const int ForwardButtonWidth = 88;

		private const int MapButtonWidth = 62;

		private const int LabelSize = 13;

		private static readonly Color ToolbarBorder = Color32.op_Implicit(new Color32((byte)67, (byte)81, (byte)95, (byte)170));

		private static readonly Color ToolbarBackground = Color32.op_Implicit(new Color32((byte)8, (byte)12, (byte)18, (byte)220));

		private static readonly Color ButtonNormal = Color32.op_Implicit(new Color32((byte)25, (byte)33, (byte)43, (byte)230));

		private static readonly Color MapButtonNormal = Color32.op_Implicit(new Color32((byte)25, (byte)53, (byte)64, (byte)240));

		private static readonly Color ButtonHover = Color32.op_Implicit(new Color32((byte)26, (byte)73, (byte)86, byte.MaxValue));

		private static readonly Color ButtonPressed = Color32.op_Implicit(new Color32((byte)15, (byte)47, (byte)58, byte.MaxValue));

		private static readonly Color ButtonFocused = Color32.op_Implicit(new Color32((byte)24, (byte)58, (byte)70, byte.MaxValue));

		private static readonly Color ButtonDisabled = Color32.op_Implicit(new Color32((byte)15, (byte)21, (byte)28, (byte)175));

		private static readonly Color LabelNormal = Color32.op_Implicit(new Color32((byte)232, (byte)239, (byte)246, byte.MaxValue));

		private static readonly Color LabelDisabled = Color32.op_Implicit(new Color32((byte)135, (byte)148, (byte)161, byte.MaxValue));

		private readonly Button _backButton;

		private readonly Button _forwardButton;

		private readonly Button _mapButton;

		private readonly Text _backLabel;

		private readonly Text _forwardLabel;

		private readonly Text _mapLabel;

		internal RectTransform RootRect { get; }

		internal BrowserNavigationToolbar(RectTransform parent, Action goBack, Action goForward, Action loadMap)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			//IL_003d: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Expected O, but got Unknown
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("MapBrowserToolbar");
			val.transform.SetParent((Transform)(object)parent, false);
			RootRect = val.AddComponent<RectTransform>();
			RootRect.anchorMin = new Vector2(0.5f, 1f);
			RootRect.anchorMax = new Vector2(0.5f, 1f);
			RootRect.pivot = new Vector2(0.5f, 1f);
			RootRect.anchoredPosition = new Vector2(0f, -10f);
			RootRect.sizeDelta = new Vector2(240f, 32f);
			Image val2 = val.AddComponent<Image>();
			((Graphic)val2).color = ToolbarBorder;
			((Graphic)val2).raycastTarget = true;
			GameObject val3 = new GameObject("Background");
			val3.transform.SetParent((Transform)(object)RootRect, false);
			RectTransform val4 = val3.AddComponent<RectTransform>();
			val4.anchorMin = Vector2.zero;
			val4.anchorMax = Vector2.one;
			val4.offsetMin = new Vector2(1f, 1f);
			val4.offsetMax = new Vector2(-1f, -1f);
			Image val5 = val3.AddComponent<Image>();
			((Graphic)val5).color = ToolbarBackground;
			((Graphic)val5).raycastTarget = false;
			float num = 5f;
			_backButton = CreateButton(RootRect, "Back", "< Back", num, 72f, ButtonNormal, goBack, out _backLabel);
			num += 76f;
			_forwardButton = CreateButton(RootRect, "Forward", "Forward >", num, 88f, ButtonNormal, goForward, out _forwardLabel);
			num += 92f;
			_mapButton = CreateButton(RootRect, "Map", "Map", num, 62f, MapButtonNormal, loadMap, out _mapLabel);
			SetState(browserReady: false, canGoBack: false, canGoForward: false);
		}

		internal void SetState(bool browserReady, bool canGoBack, bool canGoForward)
		{
			SetInteractable(_backButton, _backLabel, browserReady && canGoBack);
			SetInteractable(_forwardButton, _forwardLabel, browserReady && canGoForward);
			SetInteractable(_mapButton, _mapLabel, browserReady);
		}

		private static Button CreateButton(RectTransform parent, string name, string label, float x, float width, Color normalColor, Action action, out Text text)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Expected O, but got Unknown
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Expected O, but got Unknown
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("MapBrowser" + name + "Button");
			val.transform.SetParent((Transform)(object)parent, false);
			RectTransform val2 = val.AddComponent<RectTransform>();
			val2.anchorMin = new Vector2(0f, 0.5f);
			val2.anchorMax = new Vector2(0f, 0.5f);
			val2.pivot = new Vector2(0f, 0.5f);
			val2.anchoredPosition = new Vector2(x, 0f);
			val2.sizeDelta = new Vector2(width, 30f);
			Image val3 = val.AddComponent<Image>();
			((Graphic)val3).color = normalColor;
			Button val4 = val.AddComponent<Button>();
			((Selectable)val4).targetGraphic = (Graphic)(object)val3;
			((Selectable)val4).transition = (Transition)1;
			ColorBlock colors = default(ColorBlock);
			((ColorBlock)(ref colors)).normalColor = normalColor;
			((ColorBlock)(ref colors)).highlightedColor = ButtonHover;
			((ColorBlock)(ref colors)).pressedColor = ButtonPressed;
			((ColorBlock)(ref colors)).selectedColor = ButtonFocused;
			((ColorBlock)(ref colors)).disabledColor = ButtonDisabled;
			((ColorBlock)(ref colors)).colorMultiplier = 1f;
			((ColorBlock)(ref colors)).fadeDuration = 0.15f;
			((Selectable)val4).colors = colors;
			((UnityEvent)val4.onClick).AddListener((UnityAction)delegate
			{
				action();
			});
			GameObject val5 = new GameObject("Label");
			val5.transform.SetParent(val.transform, false);
			RectTransform val6 = val5.AddComponent<RectTransform>();
			val6.anchorMin = Vector2.zero;
			val6.anchorMax = Vector2.one;
			val6.sizeDelta = Vector2.zero;
			val6.anchoredPosition = Vector2.zero;
			text = val5.AddComponent<Text>();
			text.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
			text.fontSize = 13;
			text.fontStyle = (FontStyle)1;
			text.alignment = (TextAnchor)4;
			((Graphic)text).color = LabelNormal;
			((Graphic)text).raycastTarget = false;
			text.supportRichText = false;
			text.text = label;
			return val4;
		}

		private static void SetInteractable(Button button, Text label, bool interactable)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			((Selectable)button).interactable = interactable;
			((Graphic)label).color = (interactable ? LabelNormal : LabelDisabled);
		}
	}
	internal sealed class BrowserRenderer : IDisposable
	{
		private Texture2D? _texture;

		private readonly RawImage _rawImage;

		private int _width;

		private int _height;

		private bool _textureDirty;

		private bool _disposed;

		internal BrowserRenderer(RawImage rawImage, int width, int height)
		{
			_rawImage = rawImage;
			_width = width;
			_height = height;
			CreateTexture(width, height);
		}

		internal void OnPaint(HTML_NeedsPaint_t param)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			int unWide = (int)param.unWide;
			int unTall = (int)param.unTall;
			if (unWide != _width || unTall != _height)
			{
				_width = unWide;
				_height = unTall;
				CreateTexture(unWide, unTall);
			}
			if (!((Object)(object)_texture == (Object)null))
			{
				_texture.LoadRawTextureData(param.pBGRA, unWide * unTall * 4);
				_textureDirty = true;
			}
		}

		internal void Update()
		{
			if (_textureDirty && !((Object)(object)_texture == (Object)null))
			{
				_textureDirty = false;
				_texture.Apply(false, false);
			}
		}

		private void CreateTexture(int width, int height)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_texture != (Object)null)
			{
				Object.Destroy((Object)(object)_texture);
			}
			_texture = new Texture2D(width, height, (TextureFormat)14, false);
			_rawImage.texture = (Texture)(object)_texture;
			_rawImage.uvRect = new Rect(0f, 1f, 1f, -1f);
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				_disposed = true;
				if ((Object)(object)_texture != (Object)null)
				{
					Object.Destroy((Object)(object)_texture);
					_texture = null;
				}
			}
		}
	}
	internal sealed class InputForwarder
	{
		private readonly RectTransform _contentRect;

		private readonly RectTransform _excludedRect;

		private readonly Action _goBack;

		private readonly Action _goForward;

		private int _browserWidth;

		private int _browserHeight;

		private int _lastMouseX;

		private int _lastMouseY;

		private bool _hasMousePosition;

		private bool _focused;

		private readonly bool[] _buttonsDown = new bool[3];

		private static readonly EHTMLMouseButton[] ButtonMap = (EHTMLMouseButton[])(object)new EHTMLMouseButton[3]
		{
			default(EHTMLMouseButton),
			(EHTMLMouseButton)1,
			(EHTMLMouseButton)2
		};

		private bool AnyButtonDown
		{
			get
			{
				bool[] buttonsDown = _buttonsDown;
				for (int i = 0; i < buttonsDown.Length; i++)
				{
					if (buttonsDown[i])
					{
						return true;
					}
				}
				return false;
			}
		}

		internal InputForwarder(RectTransform contentRect, RectTransform excludedRect, int browserWidth, int browserHeight, Action goBack, Action goForward)
		{
			_contentRect = contentRect;
			_excludedRect = excludedRect;
			_browserWidth = browserWidth;
			_browserHeight = browserHeight;
			_goBack = goBack;
			_goForward = goForward;
		}

		internal void Tick(HHTMLBrowser browser)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			Vector2 browserPos;
			bool mouseOver = IsMouseOverPanel(out browserPos);
			ForwardMouseMove(browser, browserPos, mouseOver);
			ForwardMouseButtons(browser, mouseOver);
			ForwardMouseWheel(browser, mouseOver);
			ForwardKeyboard(browser);
		}

		private bool IsMouseOverPanel(out Vector2 browserPos)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			if (RectTransformUtility.RectangleContainsScreenPoint(_excludedRect, Vector2.op_Implicit(Input.mousePosition), (Camera)null))
			{
				browserPos = Vector2.zero;
				return false;
			}
			Vector2 val = default(Vector2);
			if (RectTransformUtility.ScreenPointToLocalPointInRectangle(_contentRect, Vector2.op_Implicit(Input.mousePosition), (Camera)null, ref val))
			{
				Rect rect = _contentRect.rect;
				float num = (val.x - ((Rect)(ref rect)).xMin) / ((Rect)(ref rect)).width;
				float num2 = (val.y - ((Rect)(ref rect)).yMin) / ((Rect)(ref rect)).height;
				bool result = num >= 0f && num <= 1f && num2 >= 0f && num2 <= 1f;
				browserPos = new Vector2(num * (float)_browserWidth, (1f - num2) * (float)_browserHeight);
				return result;
			}
			browserPos = Vector2.zero;
			return false;
		}

		private void ForwardMouseMove(HHTMLBrowser browser, Vector2 browserPos, bool mouseOver)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			if (!mouseOver && !AnyButtonDown)
			{
				_hasMousePosition = false;
				return;
			}
			int num = (int)Mathf.Clamp(browserPos.x, 0f, (float)(_browserWidth - 1));
			int num2 = (int)Mathf.Clamp(browserPos.y, 0f, (float)(_browserHeight - 1));
			if (!_hasMousePosition || num != _lastMouseX || num2 != _lastMouseY)
			{
				SteamHTMLSurface.MouseMove(browser, num, num2);
				_lastMouseX = num;
				_lastMouseY = num2;
				_hasMousePosition = true;
			}
		}

		private void ForwardMouseButtons(HHTMLBrowser browser, bool mouseOver)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < ButtonMap.Length; i++)
			{
				if (Input.GetMouseButtonDown(i))
				{
					if (mouseOver)
					{
						_focused = true;
						if (!_buttonsDown[i])
						{
							SteamHTMLSurface.MouseDown(browser, ButtonMap[i]);
						}
						_buttonsDown[i] = true;
					}
					else
					{
						_focused = false;
					}
				}
				if (Input.GetMouseButtonUp(i) && _buttonsDown[i])
				{
					SteamHTMLSurface.MouseUp(browser, ButtonMap[i]);
					_buttonsDown[i] = false;
				}
			}
			if (mouseOver && Input.GetMouseButtonDown(3))
			{
				_goBack();
			}
			if (mouseOver && Input.GetMouseButtonDown(4))
			{
				_goForward();
			}
		}

		private void ForwardMouseWheel(HHTMLBrowser browser, bool mouseOver)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			if (mouseOver)
			{
				float y = Input.mouseScrollDelta.y;
				if (y != 0f)
				{
					int num = (int)(Mathf.Clamp(y, -3f, 3f) * 120f);
					SteamHTMLSurface.MouseWheel(browser, num);
				}
			}
		}

		private void ForwardKeyboard(HHTMLBrowser browser)
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			if (!_focused)
			{
				return;
			}
			bool flag = Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307) || Input.GetKey((KeyCode)310) || Input.GetKey((KeyCode)309);
			if (flag && Input.GetKeyDown((KeyCode)276))
			{
				_goBack();
			}
			if (flag && Input.GetKeyDown((KeyCode)275))
			{
				_goForward();
			}
			string inputString = Input.inputString;
			foreach (char c in inputString)
			{
				if (c != '\b' && c != 0)
				{
					SteamHTMLSurface.KeyChar(browser, (uint)c, (EHTMLKeyModifiers)0);
				}
			}
		}

		internal void ResetMouseState(HHTMLBrowser browser)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < ButtonMap.Length; i++)
			{
				if (_buttonsDown[i])
				{
					SteamHTMLSurface.MouseUp(browser, ButtonMap[i]);
					_buttonsDown[i] = false;
				}
			}
			_hasMousePosition = false;
			_focused = false;
		}
	}
	[DefaultExecutionOrder(-100)]
	internal sealed class MapOverlay : MonoBehaviour
	{
		private Canvas? _canvas;

		private RawImage? _rawImage;

		private BrowserManager? _browser;

		private BrowserRenderer? _renderer;

		private InputForwarder? _input;

		private BrowserNavigationToolbar? _toolbar;

		private bool _visible;

		private bool _ready;

		private bool _stopped;

		private bool _applicationQuitting;

		internal IModLogger? Log { get; set; }

		internal IModConfig? Config { get; set; }

		private void Start()
		{
			if (_stopped)
			{
				return;
			}
			if (_applicationQuitting)
			{
				Stop();
				Object.Destroy((Object)(object)this);
				return;
			}
			if (Config == null || Log == null)
			{
				Debug.LogError((object)"[InteractiveMapCompanion] Overlay started without config/logger.");
				Stop();
				Object.Destroy((Object)(object)this);
				return;
			}
			if (!Config.EnableOverlay)
			{
				Log.LogInfo("[Overlay] Overlay disabled via config.");
				Stop();
				Object.Destroy((Object)(object)this);
				return;
			}
			try
			{
				BuildUI();
				StartBrowser();
				if (_browser != null)
				{
					_ready = true;
				}
			}
			catch (Exception arg)
			{
				Log.LogError($"[Overlay] Failed to initialise: {arg}");
				Stop();
				Object.Destroy((Object)(object)this);
			}
		}

		private void BuildUI()
		{
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Expected O, but got Unknown
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Expected O, but got Unknown
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Expected O, but got Unknown
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_024f: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			IModConfig config = Config;
			IModLogger log = Log;
			if (config.ResetToDefaults)
			{
				config.OverlayWidth = 0;
				config.OverlayHeight = 0;
				config.AnchorX = -1f;
				config.AnchorY = -1f;
				config.ResetToDefaults = false;
				log.LogInfo("[Overlay] Reset size/position to auto-computed defaults.");
			}
			if (config.OverlayWidth <= 0)
			{
				config.OverlayWidth = Mathf.RoundToInt((float)Screen.width * 0.8f);
				config.OverlayHeight = Mathf.RoundToInt((float)Screen.height * 0.8f);
				log.LogInfo($"[Overlay] Auto-sized to {config.OverlayWidth}x{config.OverlayHeight} (screen: {Screen.width}x{Screen.height})");
			}
			if (config.AnchorX < 0f)
			{
				config.AnchorX = 0.5f;
				config.AnchorY = 0.5f;
			}
			int overlayWidth = config.OverlayWidth;
			int overlayHeight = config.OverlayHeight;
			float num = Mathf.Clamp01(config.AnchorX);
			float num2 = Mathf.Clamp01(config.AnchorY);
			GameObject val = new GameObject("MapOverlayCanvas");
			Object.DontDestroyOnLoad((Object)(object)val);
			_canvas = val.AddComponent<Canvas>();
			_canvas.renderMode = (RenderMode)0;
			_canvas.sortingOrder = 100;
			val.AddComponent<CanvasScaler>();
			val.AddComponent<GraphicRaycaster>();
			GameObject val2 = new GameObject("MapOverlayPanel");
			val2.transform.SetParent(val.transform, false);
			RectTransform val3 = val2.AddComponent<RectTransform>();
			Vector2 val4 = default(Vector2);
			((Vector2)(ref val4))..ctor(num, num2);
			val3.anchorMin = val4;
			val3.anchorMax = val4;
			val3.pivot = val4;
			val3.anchoredPosition = Vector2.zero;
			val3.sizeDelta = new Vector2((float)overlayWidth, (float)overlayHeight);
			GameObject val5 = new GameObject("MapOverlayImage");
			val5.transform.SetParent(val2.transform, false);
			_rawImage = val5.AddComponent<RawImage>();
			RectTransform component = val5.GetComponent<RectTransform>();
			component.anchorMin = Vector2.zero;
			component.anchorMax = Vector2.one;
			component.offsetMin = Vector2.zero;
			component.offsetMax = Vector2.zero;
			_toolbar = new BrowserNavigationToolbar(val3, delegate
			{
				_browser?.GoBack();
			}, delegate
			{
				_browser?.GoForward();
			}, delegate
			{
				_browser?.LoadMap();
			});
			_input = new InputForwarder(component, _toolbar.RootRect, overlayWidth, overlayHeight, delegate
			{
				_browser?.GoBack();
			}, delegate
			{
				_browser?.GoForward();
			});
			val.SetActive(false);
		}

		private void StartBrowser()
		{
			if (!((Object)(object)_rawImage == (Object)null))
			{
				int overlayWidth = Config.OverlayWidth;
				int overlayHeight = Config.OverlayHeight;
				_renderer = new BrowserRenderer(_rawImage, overlayWidth, overlayHeight);
				_browser = new BrowserManager(Log, _renderer.OnPaint);
				_browser.NavigationStateChanged += UpdateNavigationState;
				UpdateNavigationState();
				if (!_browser.Initialize(overlayWidth, overlayHeight, "https://erenshor.compendiums.org/map"))
				{
					Log.LogWarning("[Overlay] Browser initialisation failed — overlay will not be shown.");
					_browser.NavigationStateChanged -= UpdateNavigationState;
					_browser.Dispose();
					_browser = null;
					UpdateNavigationState();
				}
			}
		}

		private void UpdateNavigationState()
		{
			_toolbar?.SetState(_browser?.IsReady ?? false, _browser?.CanGoBack ?? false, _browser?.CanGoForward ?? false);
		}

		internal void HandleShortcut(bool wasPressed)
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			if (_stopped || !_ready || !wasPressed)
			{
				return;
			}
			int num;
			if (GameData.InCharSelect)
			{
				EventSystem current = EventSystem.current;
				object obj;
				if (current == null)
				{
					obj = null;
				}
				else
				{
					GameObject currentSelectedGameObject = current.currentSelectedGameObject;
					obj = ((currentSelectedGameObject != null) ? ((Object)currentSelectedGameObject).name : null);
				}
				num = (((string?)obj == "InputField (TMP)") ? 1 : 0);
			}
			else
			{
				num = 0;
			}
			bool flag = (byte)num != 0;
			IModConfig config = Config;
			if (GameData.PlayerTyping || flag)
			{
				Log?.LogDebug($"[Overlay] Ignored {config.ToggleKey} while text input was active.");
				return;
			}
			SetVisible(!_visible);
			if (config.ToggleKey == InputManager.Map)
			{
				MapKeyPatches.SuppressMapKey = true;
			}
		}

		private void Update()
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			if (!_stopped && _ready && _browser != null)
			{
				_browser.ProcessPendingNavigation();
				_renderer?.Update();
				if (_visible && _browser.IsReady)
				{
					_input?.Tick(_browser.BrowserHandle);
				}
			}
		}

		private void LateUpdate()
		{
			if (!_stopped)
			{
				MapKeyPatches.SuppressMapKey = false;
			}
		}

		private void OnApplicationFocus(bool hasFocus)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			if (!_stopped && _ready)
			{
				BrowserManager? browser = _browser;
				if (browser != null && browser.IsReady && (!hasFocus || _visible))
				{
					_input?.ResetMouseState(_browser.BrowserHandle);
				}
			}
		}

		internal void NotifyApplicationQuitting()
		{
			_applicationQuitting = true;
			_browser?.NotifyAppIsQuitting();
		}

		private void SetVisible(bool visible)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			if (!_stopped)
			{
				_visible = visible;
				_browser?.SetVisible(visible);
				if ((Object)(object)_canvas != (Object)null)
				{
					((Component)_canvas).gameObject.SetActive(visible);
				}
				BrowserManager? browser = _browser;
				if (browser != null && browser.IsReady)
				{
					_input?.ResetMouseState(_browser.BrowserHandle);
				}
				Log?.LogDebug("[Overlay] " + (visible ? "Shown" : "Hidden") + ".");
			}
		}

		internal void Stop()
		{
			if (!_stopped)
			{
				_stopped = true;
				_ready = false;
				MapKeyPatches.SuppressMapKey = false;
				if (_browser != null)
				{
					_browser.NavigationStateChanged -= UpdateNavigationState;
					_browser.Dispose();
					_browser = null;
				}
				_renderer?.Dispose();
				_renderer = null;
				_input = null;
				_toolbar = null;
				if ((Object)(object)_canvas != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)_canvas).gameObject);
					_canvas = null;
				}
			}
		}

		private void OnDestroy()
		{
			Stop();
		}
	}
}
namespace InteractiveMapCompanion.Entities
{
	public sealed class EntityClassifier : IEntityClassifier
	{
		public EntityType? Classify(Character character)
		{
			if ((Object)(object)character == (Object)null)
			{
				return null;
			}
			if (character.MiningNode)
			{
				return null;
			}
			NPC myNPC = character.MyNPC;
			if (myNPC != null && myNPC.TreasureChest)
			{
				return null;
			}
			if ((Object)(object)GameData.PlayerControl?.Myself == (Object)(object)character)
			{
				return EntityType.Player;
			}
			if ((Object)(object)character.Master != (Object)null)
			{
				return EntityType.Pet;
			}
			if ((Object)(object)((Component)character).GetComponent<SimPlayer>() != (Object)null)
			{
				return EntityType.SimPlayer;
			}
			if (IsHostileToPlayer(character))
			{
				return EntityType.NpcEnemy;
			}
			return EntityType.NpcFriendly;
		}

		private static bool IsHostileToPlayer(Character character)
		{
			if (character.AggressiveTowards != null && (character.AggressiveTowards.Contains((Faction)0) || character.AggressiveTowards.Contains((Faction)11)))
			{
				return true;
			}
			if ((Object)(object)character.MyWorldFaction != (Object)null && character.MyWorldFaction.FactionValue <= 0f)
			{
				return true;
			}
			return false;
		}
	}
	public sealed class EntityData
	{
		public int Id { get; }

		public string EntityType { get; }

		public string Name { get; }

		public float[] Position { get; }

		public float Rotation { get; }

		public int? Level { get; }

		public string? Rarity { get; }

		public string? CharacterClass { get; }

		public string? Owner { get; }

		public EntityData(int Id, string EntityType, string Name, float[] Position, float Rotation, int? Level = null, string? Rarity = null, string? CharacterClass = null, string? Owner = null)
		{
			this.Id = Id;
			this.EntityType = EntityType;
			this.Name = Name;
			this.Position = Position;
			this.Rotation = Rotation;
			this.Level = Level;
			this.Rarity = Rarity;
			this.CharacterClass = CharacterClass;
			this.Owner = Owner;
		}
	}
	public sealed class EntityExtractor : IEntityExtractor
	{
		public EntityData Extract(Character character, EntityType entityType)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = ((Component)character).transform;
			Stats myStats = character.MyStats;
			return new EntityData(((Object)character).GetInstanceID(), EntityTypeToString(entityType), myStats?.MyName ?? ((Object)character).name, new float[3]
			{
				transform.position.x,
				transform.position.y,
				transform.position.z
			}, NormalizeRotation(transform.eulerAngles.y), GetLevel(myStats, entityType), GetRarity(character, entityType), GetCharacterClass(myStats, entityType), GetOwner(character, entityType));
		}

		private static string EntityTypeToString(EntityType type)
		{
			if (1 == 0)
			{
			}
			string result = type switch
			{
				EntityType.Player => "player", 
				EntityType.SimPlayer => "simplayer", 
				EntityType.Pet => "pet", 
				EntityType.NpcFriendly => "npc_friendly", 
				EntityType.NpcEnemy => "npc_enemy", 
				_ => "unknown", 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		private static float NormalizeRotation(float degrees)
		{
			degrees %= 360f;
			if (degrees < 0f)
			{
				degrees += 360f;
			}
			return degrees;
		}

		private static int? GetLevel(Stats? stats, EntityType type)
		{
			return stats?.Level;
		}

		private static string? GetRarity(Character character, EntityType type)
		{
			if (type != EntityType.NpcEnemy)
			{
				return null;
			}
			if (character.BossXp > 1f)
			{
				return "boss";
			}
			return "common";
		}

		private static string? GetCharacterClass(Stats? stats, EntityType type)
		{
			if ((uint)type > 1u)
			{
				return null;
			}
			return stats?.CharacterClass?.DisplayName;
		}

		private static string? GetOwner(Character character, EntityType type)
		{
			if (type != EntityType.Pet)
			{
				return null;
			}
			return character.Master?.MyStats?.MyName ?? "Unknown";
		}
	}
	public sealed class EntityFinder : IEntityFinder
	{
		public IEnumerable<Character> FindAll()
		{
			return Object.FindObjectsOfType<Character>();
		}
	}
	public sealed class EntityTracker<TCharacter> where TCharacter : class
	{
		private readonly Func<IEnumerable<TCharacter>> _findEntities;

		private readonly Func<TCharacter, EntityType?> _classify;

		private readonly Func<TCharacter, EntityType, EntityData> _extract;

		private readonly Func<EntityType, bool> _shouldTrack;

		public EntityTracker(Func<IEnumerable<TCharacter>> findEntities, Func<TCharacter, EntityType?> classify, Func<TCharacter, EntityType, EntityData> extract, Func<EntityType, bool> shouldTrack)
		{
			_findEntities = findEntities;
			_classify = classify;
			_extract = extract;
			_shouldTrack = shouldTrack;
		}

		public IReadOnlyList<EntityData> GetTrackedEntities()
		{
			List<EntityData> list = new List<EntityData>();
			foreach (TCharacter item2 in _findEntities())
			{
				EntityType? entityType = _classify(item2);
				if (entityType.HasValue && _shouldTrack(entityType.Value))
				{
					EntityData item = _extract(item2, entityType.Value);
					list.Add(item);
				}
			}
			return list;
		}
	}
	public sealed class EntityTrackerAdapter : IEntityTracker
	{
		private readonly EntityTracker<Character> _inner;

		public EntityTrackerAdapter(IEntityFinder finder, IEntityClassifier classifier, IEntityExtractor extractor, Func<EntityType, bool> entityFilter)
		{
			_inner = new EntityTracker<Character>(finder.FindAll, classifier.Classify, extractor.Extract, entityFilter);
		}

		public IReadOnlyList<EntityData> GetTrackedEntities()
		{
			return _inner.GetTrackedEntities();
		}
	}
	public enum EntityType
	{
		Player,
		SimPlayer,
		Pet,
		NpcFriendly,
		NpcEnemy
	}
	public interface IEntityClassifier
	{
		EntityType? Classify(Character character);
	}
	public interface IEntityExtractor
	{
		EntityData Extract(Character character, EntityType entityType);
	}
	public interface IEntityFinder
	{
		IEnumerable<Character> FindAll();
	}
	public interface IEntityTracker
	{
		IReadOnlyList<EntityData> GetTrackedEntities();
	}
}
namespace InteractiveMapCompanion.Config
{
	public enum LogLevel
	{
		Debug,
		Info,
		Warning,
		Error
	}
	public interface IModConfig
	{
		int Port { get; }

		int UpdateInterval { get; }

		LogLevel WebSocketLogLevel { get; }

		LogLevel ModLogLevel { get; }

		bool EnableOverlay { get; }

		KeyCode ToggleKey { get; }

		float AnchorX { get; set; }

		float AnchorY { get; set; }

		int OverlayWidth { get; set; }

		int OverlayHeight { get; set; }

		bool ResetToDefaults { get; set; }

		string[] GetCapabilities();
	}
	public abstract class ModConfigBase : IModConfig
	{
		public abstract int Port { get; }

		public abstract int UpdateInterval { get; }

		public abstract LogLevel WebSocketLogLevel { get; }

		public abstract LogLevel ModLogLevel { get; }

		public abstract bool EnableOverlay { get; }

		public abstract KeyCode ToggleKey { get; }

		public abstract float AnchorX { get; set; }

		public abstract float AnchorY { get; set; }

		public abstract int OverlayWidth { get; set; }

		public abstract int OverlayHeight { get; set; }

		public abstract bool ResetToDefaults { get; set; }

		public string[] GetCapabilities()
		{
			return new string[1] { "entities" };
		}
	}
}
namespace Fleck
{
	internal class ConnectionNotAvailableException : Exception
	{
		public ConnectionNotAvailableException()
		{
		}

		public ConnectionNotAvailableException(string message)
			: base(message)
		{
		}

		public ConnectionNotAvailableException(string message, Exception innerException)
			: base(message, innerException)
		{
		}
	}
	internal enum LogLevel
	{
		Debug,
		Info,
		Warn,
		Error
	}
	internal class FleckLog
	{
		public static LogLevel Level = LogLevel.Info;

		public static Action<LogLevel, string, Exception> LogAction = delegate(LogLevel level, string message, Exception ex)
		{
			if (level >= Level)
			{
				Console.WriteLine("{0} [{1}] {2} {3}", DateTime.Now, level, message, ex);
			}
		};

		public static void Warn(string message, Exception ex = null)
		{
			LogAction(LogLevel.Warn, message, ex);
		}

		public static void Error(string message, Exception ex = null)
		{
			LogAction(LogLevel.Error, message, ex);
		}

		public static void Debug(string message, Exception ex = null)
		{
			LogAction(LogLevel.Debug, message, ex);
		}

		public static void Info(string message, Exception ex = null)
		{
			LogAction(LogLevel.Info, message, ex);
		}
	}
	internal enum FrameType : byte
	{
		Continuation = 0,
		Text = 1,
		Binary = 2,
		Close = 8,
		Ping = 9,
		Pong = 10
	}
	internal class HandlerFactory
	{
		public static IHandler BuildHandler(WebSocketHttpRequest request, Action<string> onMessage, Action onClose, Action<byte[]> onBinary, Action<byte[]> onPing, Action<byte[]> onPong)
		{
			switch (GetVersion(request))
			{
			case "76":
				return Draft76Handler.Create(request, onMessage);
			case "7":
			case "8":
			case "13":
				return Hybi13Handler.Create(request, onMessage, onClose, onBinary, onPing, onPong);
			case "policy-file-request":
				return FlashSocketPolicyRequestHandler.Create(request);
			default:
				throw new WebSocketException(1003);
			}
		}

		public static string GetVersion(WebSocketHttpRequest request)
		{
			if (request.Headers.TryGetValue("Sec-WebSocket-Version", out var value))
			{
				return value;
			}
			if (request.Headers.TryGetValue("Sec-WebSocket-Draft", out value))
			{
				return value;
			}
			if (request.Headers.ContainsKey("Sec-WebSocket-Key1"))
			{
				return "76";
			}
			if (request.Body != null && request.Body.ToLower().Contains("policy-file-request"))
			{
				return "policy-file-request";
			}
			return "75";
		}
	}
	internal interface IHandler
	{
		byte[] CreateHandshake(string subProtocol = null);

		void Receive(IEnumerable<byte> data);

		byte[] FrameText(string text);

		byte[] FrameBinary(byte[] bytes);

		byte[] FramePing(byte[] bytes);

		byte[] FramePong(byte[] bytes);

		byte[] FrameClose(int code);
	}
	internal interface ISocket
	{
		bool Connected { get; }

		string RemoteIpAddress { get; }

		int RemotePort { get; }

		Stream Stream { get; }

		bool NoDelay { get; set; }

		EndPoint LocalEndPoint { get; }

		Task<ISocket> Accept(Action<ISocket> callback, Action<Exception> error);

		Task Send(byte[] buffer, Action callback, Action<Exception> error);

		Task<int> Receive(byte[] buffer, Action<int> callback, Action<Exception> error, int offset = 0);

		Task Authenticate(X509Certificate2 certificate, SslProtocols enabledSslProtocols, Action callback, Action<Exception> error);

		void Dispose();

		void Close();

		void Bind(EndPoint ipLocal);

		void Listen(int backlog);
	}
	internal interface IWebSocketConnection
	{
		Action OnOpen { get; set; }

		Action OnClose { get; set; }

		Action<string> OnMessage { get; set; }

		Action<byte[]> OnBinary { get; set; }

		Action<byte[]> OnPing { get; set; }

		Action<byte[]> OnPong { get; set; }

		Action<Exception> OnError { get; set; }

		IWebSocketConnectionInfo ConnectionInfo { get; }

		bool IsAvailable { get; }

		Task Send(string message);

		Task Send(byte[] message);

		Task SendPing(byte[] message);

		Task SendPong(byte[] message);

		void Close();

		void Close(int code);
	}
	internal interface IWebSocketConnectionInfo
	{
		string SubProtocol { get; }

		string Origin { get; }

		string Host { get; }

		string Path { get; }

		string ClientIpAddress { get; }

		int ClientPort { get; }

		IDictionary<string, string> Cookies { get; }

		IDictionary<string, string> Headers { get; }

		Guid Id { get; }

		string NegotiatedSubProtocol { get; }
	}
	internal interface IWebSocketServer : IDisposable
	{
		void Start(Action<IWebSocketConnection> config);
	}
	internal static class IntExtensions
	{
		public static byte[] ToBigEndianBytes<T>(this int source)
		{
			Type typeFromHandle = typeof(T);
			byte[] bytes;
			if (typeFromHandle == typeof(ushort))
			{
				bytes = BitConverter.GetBytes((ushort)source);
			}
			else if (typeFromHandle == typeof(ulong))
			{
				bytes = BitConverter.GetBytes((ulong)source);
			}
			else
			{
				if (!(typeFromHandle == typeof(int)))
				{
					throw new InvalidCastException("Cannot be cast to T");
				}
				bytes = BitConverter.GetBytes(source);
			}
			if (BitConverter.IsLittleEndian)
			{
				Array.Reverse((Array)bytes);
			}
			return bytes;
		}

		public static int ToLittleEndianInt(this byte[] source)
		{
			if (BitConverter.IsLittleEndian)
			{
				Array.Reverse((Array)source);
			}
			if (source.Length == 2)
			{
				return BitConverter.ToUInt16(source, 0);
			}
			if (source.Length == 8)
			{
				return (int)BitConverter.ToUInt64(source, 0);
			}
			throw new ArgumentException("Unsupported Size");
		}
	}
	internal class QueuedStream : Stream
	{
		private class WriteData
		{
			public readonly byte[] Buffer;

			public readonly int Offset;

			public readonly int Count;

			public readonly AsyncCallback Callback;

			public readonly object State;

			public readonly QueuedWriteResult AsyncResult;

			public WriteData(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
			{
				Buffer = buffer;
				Offset = offset;
				Count = count;
				Callback = callback;
				State = state;
				AsyncResult = new QueuedWriteResult(state);
			}
		}

		private class QueuedWriteResult : IAsyncResult
		{
			private readonly object _state;

			public Exception Exception { get; set; }

			public IAsyncResult ActualResult { get; set; }

			public object AsyncState => _state;

			public WaitHandle AsyncWaitHandle
			{
				get
				{
					throw new NotSupportedException("Queued write operations do not support wait handle.");
				}
			}

			public bool CompletedSynchronously => false;

			public bool IsCompleted
			{
				get
				{
					if (ActualResult != null)
					{
						return ActualResult.IsCompleted;
					}
					return false;
				}
			}

			public QueuedWriteResult(object state)
			{
				_state = state;
			}
		}

		private readonly Stream _stream;

		private readonly Queue<WriteData> _queue = new Queue<WriteData>();

		private int _pendingWrite;

		private bool _disposed;

		public override bool CanRead => _stream.CanRead;

		public override bool CanSeek => _stream.CanSeek;

		public override bool CanWrite => _stream.CanWrite;

		public override long Length => _stream.Length;

		public override long Position
		{
			get
			{
				return _stream.Position;
			}
			set
			{
				_stream.Position = value;
			}
		}

		public QueuedStream(Stream stream)
		{
			_stream = stream;
		}

		public override int Read(byte[] buffer, int offset, int count)
		{
			return _stream.Read(buffer, offset, count);
		}

		public override long Seek(long offset, SeekOrigin origin)
		{
			return _stream.Seek(offset, origin);
		}

		public override void SetLength(long value)
		{
			_stream.SetLength(value);
		}

		public override void Write(byte[] buffer, int offset, int count)
		{
			throw new NotSupportedException("QueuedStream does not support synchronous write operations yet.");
		}

		public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
		{
			return _stream.BeginRead(buffer, offset, count, callback, state);
		}

		public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
		{
			lock (_queue)
			{
				WriteData writeData = new WriteData(buffer, offset, count, callback, state);
				if (_pendingWrite > 0)
				{
					_queue.Enqueue(writeData);
					return writeData.AsyncResult;
				}
				return BeginWriteInternal(buffer, offset, count, callback, state, writeData);
			}
		}

		public override int EndRead(IAsyncResult asyncResult)
		{
			return _stream.EndRead(asyncResult);
		}

		public override void EndWrite(IAsyncResult asyncResult)
		{
			if (asyncResult is QueuedWriteResult)
			{
				QueuedWriteResult queuedWriteResult = asyncResult as QueuedWriteResult;
				if (queuedWriteResult.Exception != null)
				{
					throw queuedWriteResult.Exception;
				}
				if (queuedWriteResult.ActualResult == null)
				{
					throw new NotSupportedException("QueuedStream does not support synchronous write operations. Please wait for callback to be invoked before calling EndWrite.");
				}
				return;
			}
			throw new ArgumentException();
		}

		public override void Flush()
		{
			_stream.Flush();
		}

		public override void Close()
		{
			_stream.Close();
		}

		protected override void Dispose(bool disposing)
		{
			if (!_disposed)
			{
				if (disposing)
				{
					_stream.Dispose();
				}
				_disposed = true;
			}
			base.Dispose(disposing);
		}

		private IAsyncResult BeginWriteInternal(byte[] buffer, int offset, int count, AsyncCallback callback, object state, WriteData queued)
		{
			_pendingWrite++;
			IAsyncResult actualResult = _stream.BeginWrite(buffer, offset, count, delegate(IAsyncResult ar)
			{
				queued.AsyncResult.ActualResult = ar;
				try
				{
					_stream.EndWrite(ar);
				}
				catch (Exception exception)
				{
					queued.AsyncResult.Exception = exception;
				}
				lock (_queue)
				{
					_pendingWrite--;
					while (_queue.Count > 0)
					{
						WriteData writeData = _queue.Dequeue();
						try
						{
							writeData.AsyncResult.ActualResult = BeginWriteInternal(writeData.Buffer, writeData.Offset, writeData.Count, writeData.Callback, writeData.State, writeData);
						}
						catch (Exception exception2)
						{
							_pendingWrite--;
							writeData.AsyncResult.Exception = exception2;
							writeData.Callback(writeData.AsyncResult);
							continue;
						}
						break;
					}
					callback(queued.AsyncResult);
				}
			}, state);
			queued.AsyncResult.ActualResult = actualResult;
			return queued.AsyncResult;
		}
	}
	internal class ReadState
	{
		public List<byte> Data { get; private set; }

		public FrameType? FrameType { get; set; }

		public ReadState()
		{
			Data = new List<byte>();
		}

		public void Clear()
		{
			Data.Clear();
			FrameType = null;
		}
	}
	internal class RequestParser
	{
		private const string pattern = "^(?<method>[^\\s]+)\\s(?<path>[^\\s]+)\\sHTTP\\/1\\.1\\r\\n((?<field_name>[^:\\r\\n]+):(?([^\\r\\n])\\s)*(?<field_value>[^\\r\\n]*)\\r\\n)+\\r\\n(?<body>.+)?";

		private const string FlashSocketPolicyRequestPattern = "^[<]policy-file-request\\s*[/][>]";

		private static readonly Regex _regex = new Regex("^(?<method>[^\\s]+)\\s(?<path>[^\\s]+)\\sHTTP\\/1\\.1\\r\\n((?<field_name>[^:\\r\\n]+):(?([^\\r\\n])\\s)*(?<field_value>[^\\r\\n]*)\\r\\n)+\\r\\n(?<body>.+)?", RegexOptions.IgnoreCase | RegexOptions.Compiled);

		private static readonly Regex _FlashSocketPolicyRequestRegex = new Regex("^[<]policy-file-request\\s*[/][>]", RegexOptions.IgnoreCase | RegexOptions.Compiled);

		public static WebSocketHttpRequest Parse(byte[] bytes)
		{
			return Parse(bytes, "ws");
		}

		public static WebSocketHttpRequest Parse(byte[] bytes, string scheme)
		{
			string text = Encoding.UTF8.GetString(bytes);
			Match match = _regex.Match(text);
			if (!match.Success)
			{
				match = _FlashSocketPolicyRequestRegex.Match(text);
				if (match.Success)
				{
					return new WebSocketHttpRequest
					{
						Body = text,
						Bytes = bytes
					};
				}
				return null;
			}
			WebSocketHttpRequest webSocketHttpRequest = new WebSocketHttpRequest
			{
				Method = match.Groups["method"].Value,
				Path = match.Groups["path"].Value,
				Body = match.Groups["body"].Value,
				Bytes = bytes,
				Scheme = scheme
			};
			CaptureCollection captures = match.Groups["field_name"].Captures;
			CaptureCollection captures2 = match.Groups["field_value"].Captures;
			for (int i = 0; i < captures.Count; i++)
			{
				string key = captures[i].ToString();
				string value = captures2[i].ToString();
				webSocketHttpRequest.Headers[key] = value;
			}
			return webSocketHttpRequest;
		}
	}
	internal class SocketWrapper : ISocket
	{
		public const uint KeepAliveInterval = 60000u;

		public const uint RetryInterval = 10000u;

		private readonly Socket _socket;

		private Stream _stream;

		private CancellationTokenSource _tokenSource;

		private TaskFactory _taskFactory;

		public string RemoteIpAddress
		{
			get
			{
				if (!(_socket.RemoteEndPoint is IPEndPoint iPEndPoint))
				{
					return null;
				}
				return iPEndPoint.Address.ToString();
			}
		}

		public int RemotePort
		{
			get
			{
				if (!(_socket.RemoteEndPoint is IPEndPoint iPEndPoint))
				{
					return -1;
				}
				return iPEndPoint.Port;
			}
		}

		public bool Connected => _socket.Connected;

		public Stream Stream => _stream;

		public bool NoDelay
		{
			get
			{
				return _socket.NoDelay;
			}
			set
			{
				_socket.NoDelay = value;
			}
		}

		public EndPoint LocalEndPoint => _socket.LocalEndPoint;

		public void SetKeepAlive(Socket socket, uint keepAliveInterval, uint retryInterval)
		{
			int num = 4;
			byte[] array = new byte[num * 3];
			Array.Copy(BitConverter.GetBytes(1u), 0, array, 0, num);
			Array.Copy(BitConverter.GetBytes(keepAliveInterval), 0, array, num, num);
			Array.Copy(BitConverter.GetBytes(retryInterval), 0, array, num * 2, num);
			socket.IOControl(IOControlCode.KeepAliveValues, array, null);
		}

		public SocketWrapper(Socket socket)
		{
			_tokenSource = new CancellationTokenSource();
			_taskFactory = new TaskFactory(_tokenSource.Token);
			_socket = socket;
			if (_socket.Connected)
			{
				_stream = new NetworkStream(_socket);
			}
			if (FleckRuntime.IsRunningOnWindows())
			{
				SetKeepAlive(socket, 60000u, 10000u);
			}
		}

		public Task Authenticate(X509Certificate2 certificate, SslProtocols enabledSslProtocols, Action callback, Action<Exception> error)
		{
			SslStream ssl = new SslStream(_stream, leaveInnerStreamOpen: false);
			_stream = new QueuedStream(ssl);
			Func<AsyncCallback, object, IAsyncResult> beginMethod = (AsyncCallback cb, object s) => ssl.BeginAuthenticateAsServer(certificate, clientCertificateRequired: false, enabledSslProtocols, checkCertificateRevocation: false, cb, s);
			Task task = Task.Factory.FromAsync(beginMethod, ssl.EndAuthenticateAsServer, null);
			task.ContinueWith(delegate
			{
				callback();
			}, TaskContinuationOptions.NotOnFaulted).ContinueWith(delegate(Task t)
			{
				error(t.Exception);
			}, TaskContinuationOptions.OnlyOnFaulted);
			task.ContinueWith(delegate(Task t)
			{
				error(t.Exception);
			}, TaskContinuationOptions.OnlyOnFaulted);
			return task;
		}

		public void Listen(int backlog)
		{
			_socket.Listen(backlog);
		}

		public void Bind(EndPoint endPoint)
		{
			_socket.Bind(endPoint);
		}

		public Task<int> Receive(byte[] buffer, Action<int> callback, Action<Exception> error, int offset)
		{
			try
			{
				Func<AsyncCallback, object, IAsyncResult> beginMethod = (AsyncCallback cb, object s) => _stream.BeginRead(buffer, offset, buffer.Length, cb, s);
				Task<int> task = Task.Factory.FromAsync(beginMethod, (Func<IAsyncResult, int>)_stream.EndRead, (object?)null);
				task.ContinueWith(delegate(Task<int> t)
				{
					callback(t.Result);
				}, TaskContinuationOptions.NotOnFaulted).ContinueWith(delegate(Task t)
				{
					error(t.Exception);
				}, TaskContinuationOptions.OnlyOnFaulted);
				task.ContinueWith(delegate(Task<int> t)
				{
					error(t.Exception);
				}, TaskContinuationOptions.OnlyOnFaulted);
				return task;
			}
			catch (Exception obj)
			{
				error(obj);
				return null;
			}
		}

		public Task<ISocket> Accept(Action<ISocket> callback, Action<Exception> error)
		{
			Func<IAsyncResult, ISocket> endMethod = (IAsyncResult r) => (!_tokenSource.Token.IsCancellationRequested) ? new SocketWrapper(_socket.EndAccept(r)) : null;
			Task<ISocket> task = _taskFactory.FromAsync(_socket.BeginAccept, endMethod, null);
			task.ContinueWith(delegate(Task<ISocket> t)
			{
				callback(t.Result);
			}, TaskContinuationOptions.OnlyOnRanToCompletion).ContinueWith(delegate(Task t)
			{
				error(t.Exception);
			}, TaskContinuationOptions.OnlyOnFaulted);
			task.ContinueWith(delegate(Task<ISocket> t)
			{
				error(t.Exception);
			}, TaskContinuationOptions.OnlyOnFaulted);
			return task;
		}

		public void Dispose()
		{
			_tokenSource.Cancel();
			if (_stream != null)
			{
				_stream.Dispose();
			}
			if (_socket != null)
			{
				_socket.Dispose();
			}
		}

		public void Close()
		{
			_tokenSource.Cancel();
			if (_stream != null)
			{
				_stream.Close();
			}
			if (_socket != null)
			{
				_socket.Close();
			}
		}

		public int EndSend(IAsyncResult asyncResult)
		{
			_stream.EndWrite(asyncResult);
			return 0;
		}

		public Task Send(byte[] buffer, Action callback, Action<Exception> error)
		{
			if (_tokenSource.IsCancellationRequested)
			{
				return null;
			}
			try
			{
				Func<AsyncCallback, object, IAsyncResult> beginMethod = (AsyncCallback cb, object s) => _stream.BeginWrite(buffer, 0, buffer.Length, cb, s);
				Task task = Task.Factory.FromAsync(beginMethod, _stream.EndWrite, null);
				task.ContinueWith(delegate
				{
					callback();
				}, TaskContinuationOptions.NotOnFaulted).ContinueWith(delegate(Task t)
				{
					error(t.Exception);
				}, TaskContinuationOptions.OnlyOnFaulted);
				task.ContinueWith(delegate(Task t)
				{
					error(t.Exception);
				}, TaskContinuationOptions.OnlyOnFaulted);
				return task;
			}
			catch (Exception obj)
			{
				error(obj);
				return null;
			}
		}
	}
	internal class SubProtocolNegotiationFailureException : Exception
	{
		public SubProtocolNegotiationFailureException()
		{
		}

		public SubProtocolNegotiationFailureException(string message)
			: base(message)
		{
		}

		public SubProtocolNegotiationFailureException(string message, Exception innerException)
			: base(message, innerException)
		{
		}
	}
	internal static class SubProtocolNegotiator
	{
		public static string Negotiate(IEnumerable<string> server, IEnumerable<string> client)
		{
			if (!server.Any() || !client.Any())
			{
				return null;
			}
			IEnumerable<string> source = client.Intersect(server);
			if (!source.Any())
			{
				throw new SubProtocolNegotiationFailureException("Unable to negotiate a subprotocol");
			}
			return source.First();
		}
	}
	internal class WebSocketConnection : IWebSocketConnection
	{
		private readonly Action<IWebSocketConnection> _initialize;

		private readonly Func<WebSocketHttpRequest, IHandler> _handlerFactory;

		private readonly Func<IEnumerable<string>, string> _negotiateSubProtocol;

		private readonly Func<byte[], WebSocketHttpRequest> _parseRequest;

		private bool _closing;

		private bool _closed;

		private const int ReadSize = 4096;

		public ISocket Socket { get; set; }

		public IHandler Handler { get; set; }

		public Action OnOpen { get; set; }

		public Action OnClose { get; set; }

		public Action<string> OnMessage { get; set; }

		public Action<byte[]> OnBinary { get; set; }

		public Action<byte[]> OnPing { get; set; }

		public Action<byte[]> OnPong { get; set; }

		public Action<Exception> OnError { get; set; }

		public IWebSocketConnectionInfo Con