Decompiled source of WebMap v2.6.1

WebMap.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;
using WebMap.Patches;
using WebSocketSharp;
using WebSocketSharp.Net;
using WebSocketSharp.Server;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("WebMap")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("(c) 2024 Various Authors")]
[assembly: AssemblyFileVersion("2.6.1.0")]
[assembly: AssemblyInformationalVersion("2.6.1+0d2e19f969a56b5da67488c47eb46b84792b27ff")]
[assembly: AssemblyProduct("WebMap")]
[assembly: AssemblyTitle("Valheim WebMap")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.6.1.0")]
[module: UnverifiableCode]
namespace WebMap
{
	internal static class WebMapConfig
	{
		public static int TEXTURE_SIZE = 2048;

		public static int PIXEL_SIZE = 12;

		public static float EXPLORE_RADIUS = 100f;

		public static float UPDATE_FOG_TEXTURE_INTERVAL = 2f;

		public static float SAVE_FOG_TEXTURE_INTERVAL = 30f;

		public static int MAX_PINS_PER_USER = 50;

		public static int MAX_MESSAGES = 100;

		public static bool ALWAYS_MAP = true;

		public static bool ALWAYS_VISIBLE = false;

		public static bool DEBUG = false;

		public static bool TEST = false;

		public static int SERVER_PORT = 3000;

		public static float PLAYER_UPDATE_INTERVAL = 1f;

		public static bool CACHE_SERVER_FILES = true;

		public static string WORLD_NAME = "";

		public static Vector3 WORLD_START_POS = Vector3.zero;

		public static int DEFAULT_ZOOM = 100;

		public static string DISCORD_WEBHOOK = "";

		public static string DISCORD_INVITE_URL = "";

		public static string URL = "";

		public static void ReadConfigFile(ConfigFile config)
		{
			TEXTURE_SIZE = config.Bind<int>("Texture", "texture_size", TEXTURE_SIZE, "How large is the map texture? Probably dont change this.").Value;
			PIXEL_SIZE = config.Bind<int>("Texture", "pixel_size", PIXEL_SIZE, "How many in game units does a map pixel represent? Probably dont change this.").Value;
			EXPLORE_RADIUS = config.Bind<float>("Texture", "explore_radius", EXPLORE_RADIUS, "A larger explore_radius reveals the map more quickly.").Value;
			UPDATE_FOG_TEXTURE_INTERVAL = config.Bind<float>("Interval", "update_fog_texture_interval", UPDATE_FOG_TEXTURE_INTERVAL, "How often do we update the fog texture on the server in seconds.").Value;
			SAVE_FOG_TEXTURE_INTERVAL = config.Bind<float>("Interval", "save_fog_texture_interval", SAVE_FOG_TEXTURE_INTERVAL, "How often do we save the fog texture in seconds.").Value;
			MAX_PINS_PER_USER = config.Bind<int>("User", "max_pins_per_user", MAX_PINS_PER_USER, "How many pins each client is allowed to make before old ones start being deleted.").Value;
			SERVER_PORT = config.Bind<int>("Server", "server_port", SERVER_PORT, "HTTP port for the website. The map will be display on this site.").Value;
			PLAYER_UPDATE_INTERVAL = config.Bind<float>("Interval", "player_update_interval", PLAYER_UPDATE_INTERVAL, "How often do we send position data to web browsers in seconds.").Value;
			CACHE_SERVER_FILES = config.Bind<bool>("Server", "cache_server_files", CACHE_SERVER_FILES, "Should the server cache web files to be more performant?").Value;
			DEFAULT_ZOOM = config.Bind<int>("Texture", "default_zoom", DEFAULT_ZOOM, "How zoomed in should the web map start at? Higher is more zoomed in.").Value;
			MAX_MESSAGES = config.Bind<int>("Server", "max_messages", MAX_MESSAGES, "How many messages to keep buffered and display to client.").Value;
			ALWAYS_MAP = config.Bind<bool>("User", "always_map", ALWAYS_MAP, "Update the map to show where hidden players have traveled.").Value;
			ALWAYS_VISIBLE = config.Bind<bool>("User", "always_visible", ALWAYS_VISIBLE, "Completely ignore the players preference to be hidden.").Value;
			DEBUG = config.Bind<bool>("Server", "debug", DEBUG, "Output debugging information.").Value;
			DEBUG = config.Bind<bool>("Server", "test", TEST, "Enable test features (bugs).").Value;
			DISCORD_WEBHOOK = config.Bind<string>("Server", "discord_webhook", DISCORD_WEBHOOK, "Discord webhook URL").Value;
			DISCORD_INVITE_URL = config.Bind<string>("Server", "discord_invite_url", DISCORD_INVITE_URL, "Optional Discord invite URL to be added to the webpage.").Value;
			URL = config.Bind<string>("Server", "webmap_url", URL, "URL to view the web map.").Value;
		}

		public static string GetWorldName()
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				WORLD_NAME = ZNet.instance.GetWorldName();
			}
			else
			{
				string[] commandLineArgs = Environment.GetCommandLineArgs();
				string wORLD_NAME = "";
				for (int i = 0; i < commandLineArgs.Length; i++)
				{
					if (commandLineArgs[i] == "-world")
					{
						wORLD_NAME = commandLineArgs[i + 1];
						break;
					}
				}
				WORLD_NAME = wORLD_NAME;
			}
			return WORLD_NAME;
		}

		public static string MakeClientConfigJson()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			return DictionaryToJson(new Dictionary<string, object>
			{
				["world_name"] = GetWorldName(),
				["world_start_pos"] = WORLD_START_POS,
				["default_zoom"] = DEFAULT_ZOOM,
				["texture_size"] = TEXTURE_SIZE,
				["pixel_size"] = PIXEL_SIZE,
				["update_interval"] = PLAYER_UPDATE_INTERVAL,
				["explore_radius"] = EXPLORE_RADIUS,
				["max_messages"] = MAX_MESSAGES,
				["always_map"] = ALWAYS_MAP,
				["always_visible"] = ALWAYS_VISIBLE
			});
		}

		private static string DictionaryToJson(Dictionary<string, object> dict)
		{
			IEnumerable<string> values = dict.Select(delegate(KeyValuePair<string, object> d)
			{
				//IL_0061: Unknown result type (might be due to invalid IL or missing references)
				//IL_0066: Unknown result type (might be due to invalid IL or missing references)
				object value = d.Value;
				if (value is float num)
				{
					return "\"" + d.Key + "\": " + num.ToString("F2", CultureInfo.InvariantCulture);
				}
				if (value is double num2)
				{
					return "\"" + d.Key + "\": " + num2.ToString("F2", CultureInfo.InvariantCulture);
				}
				if (value is string text)
				{
					return "\"" + d.Key + "\": \"" + text + "\"";
				}
				if (value is bool flag)
				{
					return "\"" + d.Key + "\": " + flag.ToString().ToLower();
				}
				return (value is Vector3 val) ? ("\"" + d.Key + "\": \"" + val.x.ToString("F2", CultureInfo.InvariantCulture) + "," + val.y.ToString("F2", CultureInfo.InvariantCulture) + "," + val.z.ToString("F2", CultureInfo.InvariantCulture) + "\"") : $"\"{d.Key}\": {d.Value}";
			});
			return "{\n    " + string.Join(",\n    ", values) + "\n}\n";
		}
	}
	public class DiscordWebHook : IDisposable
	{
		private readonly WebClient webClient;

		private static readonly NameValueCollection values = new NameValueCollection();

		private readonly string webHookUrl;

		public DiscordWebHook(string url)
		{
			webHookUrl = url;
			webClient = new WebClient();
		}

		public void SendMessage(string msgSend)
		{
			values.Remove("content");
			values.Add("content", msgSend);
			if (Ext.IsNullOrEmpty(webHookUrl))
			{
				ZLog.Log((object)$"WebMap::DiscordWebHook::SendMessage: {values}");
			}
			else
			{
				webClient.UploadValues(webHookUrl, values);
			}
		}

		public void Dispose()
		{
			webClient.Dispose();
		}
	}
	[Serializable]
	public struct MapMessage
	{
		public long id;

		public int type;

		public string name;

		public string message;

		public string ts;

		public MapMessage(long id, int type, string name, string message)
		{
			this.id = id;
			this.type = type;
			this.name = name;
			this.message = message;
			ts = DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture);
		}

		public string ToJson()
		{
			return JsonUtility.ToJson((object)this);
		}
	}
	public class WebSocketHandler : WebSocketBehavior
	{
		protected override void OnOpen()
		{
			string text = ((WebSocketBehavior)this).Context.Headers.Get("X-Forwarded-For");
			if (Ext.IsNullOrEmpty(text))
			{
				text = ((WebSocketBehavior)this).Context.UserEndPoint.ToString();
			}
			ZLog.Log((object)("WebMap: new visitor connected from " + text));
			((WebSocketBehavior)this).OnOpen();
		}

		protected override void OnMessage(MessageEventArgs e)
		{
			if (e.Data.ToString() == "players")
			{
				((WebSocketBehavior)this).Send(MapDataServer.getInstance().getPlayerResponse(sendLast: true));
			}
			((WebSocketBehavior)this).OnMessage(e);
		}
	}
	public class MapDataServer
	{
		private static readonly Dictionary<string, string> contentTypes = new Dictionary<string, string>
		{
			{ "html", "text/html" },
			{ "js", "text/javascript" },
			{ "css", "text/css" },
			{ "png", "image/png" },
			{ "jpg", "image/jpeg" },
			{ "webp", "image/webp" }
		};

		private readonly Timer broadcastTimer;

		private readonly Dictionary<string, byte[]> fileCache;

		public Texture2D fogTexture;

		private readonly HttpServer httpServer;

		public byte[] mapImageData;

		public List<string> pins = new List<string>();

		public List<MapMessage> sentMessages = new List<MapMessage>();

		public List<MapMessage> newMessages = new List<MapMessage>();

		public List<ZNetPeer> players = new List<ZNetPeer>();

		public string lastPlayerResponse = "";

		private bool forceReload;

		private readonly string publicRoot;

		private readonly WebSocketServiceHost webSocketHandler;

		private static MapDataServer __instance;

		public MapDataServer()
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			__instance = this;
			httpServer = new HttpServer(WebMapConfig.SERVER_PORT);
			httpServer.AddWebSocketService<WebSocketHandler>("/");
			httpServer.KeepClean = true;
			webSocketHandler = httpServer.WebSocketServices["/"];
			broadcastTimer = new Timer(delegate
			{
				string text = "";
				if (forceReload)
				{
					webSocketHandler.Sessions.Broadcast("reload\n");
					forceReload = false;
				}
				else
				{
					text = getPlayerResponse(sendLast: false);
					if (text != lastPlayerResponse)
					{
						webSocketHandler.Sessions.Broadcast(text);
						lastPlayerResponse = text;
					}
					if (newMessages.Count > 0)
					{
						List<string> tosend = new List<string>();
						newMessages.ForEach(delegate(MapMessage message)
						{
							if (WebMapConfig.MAX_MESSAGES < sentMessages.Count)
							{
								sentMessages.RemoveAt(0);
							}
							tosend.Add(message.ToJson());
							sentMessages.Add(message);
						});
						if (tosend.Count > 0)
						{
							webSocketHandler.Sessions.Broadcast("messages\n[" + string.Join(",", tosend) + "]");
						}
						newMessages.Clear();
						newMessages.TrimExcess();
					}
				}
			}, null, TimeSpan.Zero, TimeSpan.FromSeconds(WebMapConfig.PLAYER_UPDATE_INTERVAL));
			publicRoot = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty, "web");
			fileCache = new Dictionary<string, byte[]>();
			httpServer.OnGet += delegate(object sender, HttpRequestEventArgs e)
			{
				_ = e.Request;
				if (!ProcessSpecialRoutes(e))
				{
					ServeStaticFiles(e);
				}
			};
		}

		public string getPlayerResponse(bool sendLast)
		{
			if (sendLast && lastPlayerResponse.Length > 0)
			{
				return lastPlayerResponse;
			}
			string dataString = "players\n";
			players.ForEach(delegate(ZNetPeer player)
			{
				//IL_0008: 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_0024: 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_0122: Unknown result type (might be due to invalid IL or missing references)
				ZDO val = null;
				try
				{
					val = ZDOMan.instance.GetZDO(player.m_characterID);
				}
				catch
				{
				}
				if (val != null)
				{
					Vector3 position = val.GetPosition();
					int num = (int)Math.Ceiling(val.GetFloat("max_health", 25f));
					int num2 = (int)Math.Ceiling(val.GetFloat("health", (float)num));
					int num3 = (val.GetBool("dead", false) ? 1 : 0);
					int num4 = (val.GetBool("pvp", false) ? 1 : 0);
					int num5 = (val.GetBool("inBed", false) ? 1 : 0);
					num = Math.Max(num, num2);
					dataString += $"{player.m_uid}\n{player.m_playerName}\n{num2}\n{num}\n";
					if (!player.m_publicRefPos)
					{
						dataString += "hidden\n";
					}
					if (player.m_publicRefPos || WebMapConfig.ALWAYS_VISIBLE || WebMapConfig.ALWAYS_MAP)
					{
						dataString += $"{position.x:0.##},{position.z:0.##}\n";
					}
					dataString += $"{num3}{num4}{num5}\n\n";
				}
			});
			return dataString.Trim();
		}

		public static MapDataServer getInstance()
		{
			return __instance;
		}

		public void Stop()
		{
			broadcastTimer.Dispose();
			httpServer.Stop();
		}

		private void ServeStaticFiles(HttpRequestEventArgs e)
		{
			HttpListenerRequest request = e.Request;
			HttpListenerResponse response = e.Response;
			string text = request.RawUrl;
			if (text == "/")
			{
				text = "/index.html";
			}
			string text2 = text.Split(new char[1] { '/' })[^1];
			string key = text2.Split(new char[1] { '.' })[^1];
			if (contentTypes.ContainsKey(key))
			{
				byte[] array = new byte[0];
				if (fileCache.ContainsKey(text2))
				{
					array = fileCache[text2];
				}
				else
				{
					string path = Path.Combine(publicRoot, text2);
					try
					{
						array = File.ReadAllBytes(path);
						if (WebMapConfig.CACHE_SERVER_FILES)
						{
							fileCache.Add(text2, array);
						}
					}
					catch (Exception ex)
					{
						ZLog.LogError((object)("WebMap: FAILED TO READ FILE! " + ex.Message));
					}
				}
				if (array.Length != 0)
				{
					response.Headers.Add((HttpResponseHeader)0, "public, max-age=604800, immutable");
					response.ContentType = contentTypes[key];
					response.StatusCode = 200;
					response.ContentLength64 = array.Length;
					response.Close(array, true);
				}
				else
				{
					response.StatusCode = 404;
					response.Close();
				}
			}
			else
			{
				response.StatusCode = 404;
				response.Close();
			}
		}

		private bool ProcessSpecialRoutes(HttpRequestEventArgs e)
		{
			HttpListenerRequest request = e.Request;
			HttpListenerResponse response = e.Response;
			string rawUrl = request.RawUrl;
			switch (rawUrl)
			{
			case "/config":
			{
				response.Headers.Add((HttpResponseHeader)0, "no-cache");
				response.ContentType = "application/json";
				response.StatusCode = 200;
				byte[] bytes = Encoding.UTF8.GetBytes(WebMapConfig.MakeClientConfigJson());
				response.ContentLength64 = bytes.Length;
				response.Close(bytes, true);
				return true;
			}
			case "/map":
				response.Headers.Add((HttpResponseHeader)0, "public, max-age=604800, immutable");
				response.ContentType = "application/octet-stream";
				response.StatusCode = 200;
				response.ContentLength64 = mapImageData.Length;
				response.Close(mapImageData, true);
				return true;
			case "/fog":
			{
				response.Headers.Add((HttpResponseHeader)0, "no-cache");
				response.ContentType = "image/png";
				response.StatusCode = 200;
				byte[] array = ImageConversion.EncodeToPNG(fogTexture);
				response.ContentLength64 = array.Length;
				response.Close(array, true);
				return true;
			}
			case "/messages":
			{
				response.Headers.Add((HttpResponseHeader)0, "no-cache");
				response.ContentType = "applicaion/json";
				response.StatusCode = 200;
				List<string> tosend = new List<string>();
				sentMessages.ForEach(delegate(MapMessage message)
				{
					tosend.Add(message.ToJson());
				});
				byte[] bytes = Encoding.UTF8.GetBytes("[" + string.Join(", ", tosend) + "]");
				response.ContentLength64 = bytes.Length;
				response.Close(bytes, true);
				return true;
			}
			case "/pins":
			{
				response.Headers.Add((HttpResponseHeader)0, "no-cache");
				response.ContentType = "text/csv";
				response.StatusCode = 200;
				string s = string.Join("\n", pins);
				byte[] bytes = Encoding.UTF8.GetBytes(s);
				response.ContentLength64 = bytes.Length;
				response.Close(bytes, true);
				return true;
			}
			default:
				return false;
			}
		}

		public void Reload()
		{
			forceReload = true;
		}

		public void ListenAsync()
		{
			httpServer.Start();
			if (httpServer.IsListening)
			{
				ZLog.Log((object)$"WebMap: HTTP Server Listening on port {WebMapConfig.SERVER_PORT}");
			}
			else
			{
				ZLog.LogError((object)"WebMap: HTTP Server Failed To Start !!!");
			}
		}

		public void BroadcastPing(long id, string name, Vector3 position)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			webSocketHandler.Sessions.Broadcast($"ping\n{id}\n{name}\n{FixedValue(position.x)},{FixedValue(position.z)}");
		}

		public void BroadcastMessage(long id, int type, string name, string message)
		{
			webSocketHandler.Sessions.Broadcast($"message\n{id}\n{type}\n{name}\n{message}");
		}

		public void AddPin(string id, string pinId, string type, string name, Vector3 position, string pinText)
		{
			//IL_0040: 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_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			pins.Add(id + "," + pinId + "," + type + "," + name + "," + FixedValue(position.x) + "," + FixedValue(position.z) + "," + pinText);
			webSocketHandler.Sessions.Broadcast("pin\n" + id + "\n" + pinId + "\n" + type + "\n" + name + "\n" + FixedValue(position.x) + "," + FixedValue(position.z) + "\n" + pinText);
		}

		public void RemovePin(int idx)
		{
			string[] array = pins[idx].Split(new char[1] { ',' });
			pins.RemoveAt(idx);
			webSocketHandler.Sessions.Broadcast("rmpin\n" + array[1]);
		}

		public void AddMessage(long id, int type, string name, string message)
		{
			newMessages.Add(new MapMessage(id, type, name, message));
		}

		private static string FixedValue(float f)
		{
			return f.ToString("F2", CultureInfo.InvariantCulture);
		}
	}
	[BepInPlugin("com.github.h0tw1r3.valheim.webmap", "WebMap", "2.6.1")]
	public class WebMap : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(ZoneSystem), "Start")]
		private class ZoneSystemPatch
		{
			private static readonly Color DeepWaterColor = new Color(0.36105883f, 0.36105883f, 22f / 51f);

			private static readonly Color ShallowWaterColor = new Color(0.574f, 0.50709206f, 0.47892025f);

			private static readonly Color ShoreColor = new Color(21f / 106f, 0.12241901f, 0.1503943f);

			private static Color GetMaskColor(float wx, float wy, float height, Biome biome)
			{
				//IL_0045: Unknown result type (might be due to invalid IL or missing references)
				//IL_0047: Invalid comparison between Unknown and I4
				//IL_0043: Unknown result type (might be due to invalid IL or missing references)
				//IL_0060: Unknown result type (might be due to invalid IL or missing references)
				//IL_0063: Invalid comparison between Unknown and I4
				//IL_0050: Unknown result type (might be due to invalid IL or missing references)
				//IL_0081: Unknown result type (might be due to invalid IL or missing references)
				//IL_0083: Invalid comparison between Unknown and I4
				//IL_006c: Unknown result type (might be due to invalid IL or missing references)
				//IL_005e: Unknown result type (might be due to invalid IL or missing references)
				//IL_005c: Unknown result type (might be due to invalid IL or missing references)
				//IL_008d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0085: Unknown result type (might be due to invalid IL or missing references)
				//IL_008b: Invalid comparison between Unknown and I4
				//IL_007f: Unknown result type (might be due to invalid IL or missing references)
				//IL_007d: Unknown result type (might be due to invalid IL or missing references)
				//IL_008f: Unknown result type (might be due to invalid IL or missing references)
				Color result = default(Color);
				((Color)(ref result))..ctor(0f, 0f, 0f, 0f);
				Color result2 = default(Color);
				((Color)(ref result2))..ctor(1f, 0f, 0f, 0f);
				if (height < ZoneSystem.instance.m_waterLevel)
				{
					return result;
				}
				if ((int)biome == 1)
				{
					if (!WorldGenerator.InForest(new Vector3(wx, 0f, wy)))
					{
						return result;
					}
					return result2;
				}
				if ((int)biome == 16)
				{
					if (WorldGenerator.GetForestFactor(new Vector3(wx, 0f, wy)) >= 0.8f)
					{
						return result;
					}
					return result2;
				}
				if ((int)biome == 8 || (int)biome == 512)
				{
					return result2;
				}
				return result;
			}

			private static Color GetPixelColor(Biome biome)
			{
				//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b3: Invalid comparison between Unknown and I4
				//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00db: Invalid comparison between Unknown and I4
				//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00cd: Expected I4, but got Unknown
				//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ef: Invalid comparison between Unknown and I4
				//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e0: Invalid comparison between Unknown and I4
				//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
				//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
				//IL_0115: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
				//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
				//IL_00cf: Invalid comparison between Unknown and I4
				//IL_010c: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f7: Invalid comparison between Unknown and I4
				//IL_0106: 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_00e5: Invalid comparison between Unknown and I4
				//IL_0101: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d4: Invalid comparison between Unknown and I4
				//IL_0112: Unknown result type (might be due to invalid IL or missing references)
				//IL_0109: Unknown result type (might be due to invalid IL or missing references)
				//IL_0103: Unknown result type (might be due to invalid IL or missing references)
				Color result = default(Color);
				((Color)(ref result))..ctor(0.573f, 0.655f, 0.361f);
				Color result2 = default(Color);
				((Color)(ref result2))..ctor(0.639f, 0.447f, 0.345f);
				Color result3 = default(Color);
				((Color)(ref result3))..ctor(1f, 1f, 1f);
				Color result4 = default(Color);
				((Color)(ref result4))..ctor(0.42f, 0.455f, 0.247f);
				Color result5 = default(Color);
				((Color)(ref result5))..ctor(0.906f, 0.671f, 0.47f);
				Color result6 = default(Color);
				((Color)(ref result6))..ctor(0.69f, 0.192f, 0.192f);
				Color result7 = default(Color);
				((Color)(ref result7))..ctor(1f, 1f, 1f);
				Color result8 = default(Color);
				((Color)(ref result8))..ctor(0.36f, 0.22f, 0.4f);
				if ((int)biome <= 16)
				{
					switch (biome - 1)
					{
					default:
						if ((int)biome != 8)
						{
							if ((int)biome != 16)
							{
								break;
							}
							return result5;
						}
						return result4;
					case 0:
						return result;
					case 1:
						return result2;
					case 3:
						return result3;
					case 2:
						break;
					}
				}
				else if ((int)biome <= 64)
				{
					if ((int)biome == 32)
					{
						return result6;
					}
					if ((int)biome == 64)
					{
						return result7;
					}
				}
				else
				{
					if ((int)biome == 256)
					{
						return Color.white;
					}
					if ((int)biome == 512)
					{
						return result8;
					}
				}
				return Color.white;
			}

			private static void Postfix(ZoneSystem __instance)
			{
				//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
				//IL_00de: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
				//IL_0100: Unknown result type (might be due to invalid IL or missing references)
				//IL_0102: Unknown result type (might be due to invalid IL or missing references)
				//IL_0107: Unknown result type (might be due to invalid IL or missing references)
				//IL_010c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0328: Unknown result type (might be due to invalid IL or missing references)
				//IL_032d: Unknown result type (might be due to invalid IL or missing references)
				//IL_033a: Expected O, but got Unknown
				//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
				//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
				//IL_0200: Unknown result type (might be due to invalid IL or missing references)
				//IL_0214: Unknown result type (might be due to invalid IL or missing references)
				//IL_0219: Unknown result type (might be due to invalid IL or missing references)
				//IL_021d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0222: Unknown result type (might be due to invalid IL or missing references)
				//IL_0224: Unknown result type (might be due to invalid IL or missing references)
				//IL_0226: Unknown result type (might be due to invalid IL or missing references)
				//IL_022b: Unknown result type (might be due to invalid IL or missing references)
				//IL_029d: Unknown result type (might be due to invalid IL or missing references)
				//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
				//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
				//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
				//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
				//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
				//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
				//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
				//IL_02be: Unknown result type (might be due to invalid IL or missing references)
				//IL_02c2: Unknown result type (might be due to invalid IL or missing references)
				//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
				//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
				//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
				//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
				//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
				//IL_02dd: Unknown result type (might be due to invalid IL or missing references)
				//IL_02e7: Unknown result type (might be due to invalid IL or missing references)
				//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
				//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
				//IL_0302: Unknown result type (might be due to invalid IL or missing references)
				//IL_0307: Unknown result type (might be due to invalid IL or missing references)
				instance.NewWorld();
				if (mapDataServer.mapImageData != null)
				{
					ZLog.Log((object)"WebMap: MAP ALREADY BUILT!");
					return;
				}
				ZLog.Log((object)"WebMap: BUILD MAP!");
				int num = WebMapConfig.TEXTURE_SIZE / 2;
				float num2 = (float)WebMapConfig.PIXEL_SIZE / 2f;
				Color32[] array = (Color32[])(object)new Color32[WebMapConfig.TEXTURE_SIZE * WebMapConfig.TEXTURE_SIZE];
				Color32[] array2 = (Color32[])(object)new Color32[WebMapConfig.TEXTURE_SIZE * WebMapConfig.TEXTURE_SIZE];
				float[] array3 = new float[WebMapConfig.TEXTURE_SIZE * WebMapConfig.TEXTURE_SIZE];
				Color val = default(Color);
				for (int i = 0; i < WebMapConfig.TEXTURE_SIZE; i++)
				{
					for (int j = 0; j < WebMapConfig.TEXTURE_SIZE; j++)
					{
						float num3 = (float)(j - num) * (float)WebMapConfig.PIXEL_SIZE + num2;
						float num4 = (float)(i - num) * (float)WebMapConfig.PIXEL_SIZE + num2;
						Biome biome = WorldGenerator.instance.GetBiome(num3, num4, 0.02f, false);
						float biomeHeight = WorldGenerator.instance.GetBiomeHeight(biome, num3, num4, ref val, false);
						array[i * WebMapConfig.TEXTURE_SIZE + j] = Color32.op_Implicit(GetPixelColor(biome));
						array2[i * WebMapConfig.TEXTURE_SIZE + j] = Color32.op_Implicit(GetMaskColor(num3, num4, biomeHeight, biome));
						array3[i * WebMapConfig.TEXTURE_SIZE + j] = biomeHeight;
					}
				}
				float waterLevel = ZoneSystem.instance.m_waterLevel;
				Vector3 val2 = default(Vector3);
				((Vector3)(ref val2))..ctor(-0.57735f, 0.57735f, 0.57735f);
				Color[] array4 = (Color[])(object)new Color[array.Length];
				for (int k = 0; k < array.Length; k++)
				{
					float num5 = array3[k];
					int num6 = k - WebMapConfig.TEXTURE_SIZE;
					if (num6 < 0)
					{
						num6 = k;
					}
					int num7 = k + WebMapConfig.TEXTURE_SIZE;
					if (num7 > array.Length - 1)
					{
						num7 = k;
					}
					int num8 = k + 1;
					if (num8 > array.Length - 1)
					{
						num8 = k;
					}
					int num9 = k - 1;
					if (num9 < 0)
					{
						num9 = k;
					}
					float num10 = array3[num6];
					float num11 = array3[num8];
					float num12 = array3[num9];
					float num13 = array3[num7];
					Vector3 val3 = new Vector3(2f, 0f, num11 - num12);
					Vector3 normalized = ((Vector3)(ref val3)).normalized;
					val3 = new Vector3(0f, 2f, num10 - num13);
					Vector3 normalized2 = ((Vector3)(ref val3)).normalized;
					float num14 = Vector3.Dot(Vector3.Cross(normalized, normalized2), val2) * 0.25f + 0.75f;
					float num15 = Mathf.Clamp(num5 - waterLevel, 0f, 1f);
					float num16 = Mathf.Clamp((num5 - waterLevel + 2.5f) * 0.5f, 0f, 1f);
					float num17 = Mathf.Clamp((num5 - waterLevel + 12.5f) * 0.1f, 0f, 1f);
					Color32 val4 = array[k];
					Color val5 = Color.Lerp(ShoreColor, Color32.op_Implicit(val4), num15);
					val5 = Color.Lerp(ShallowWaterColor, val5, num16);
					val5 = Color.Lerp(DeepWaterColor, val5, num17);
					array4[k] = new Color(val5.r * num14, val5.g * num14, val5.b * num14, val5.a);
				}
				Texture2D val6 = new Texture2D(WebMapConfig.TEXTURE_SIZE, WebMapConfig.TEXTURE_SIZE, (TextureFormat)4, false);
				val6.SetPixels(array4);
				byte[] array5 = ImageConversion.EncodeToPNG(val6);
				mapDataServer.mapImageData = array5;
				try
				{
					File.WriteAllBytes(Path.Combine(worldDataPath, "map.png"), array5);
					ZLog.Log((object)"WebMap: BUILDING MAP DONE!");
				}
				catch (Exception ex)
				{
					ZLog.LogError((object)("WebMap: FAILED TO WRITE MAP FILE! " + ex.Message));
				}
			}
		}

		[HarmonyPatch(typeof(ZoneSystem), "Load")]
		private class ZoneSystemLoadPatch
		{
			private static void Postfix()
			{
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				LocationInstance val = default(LocationInstance);
				if (ZoneSystem.instance.FindClosestLocation("StartTemple", Vector3.zero, ref val))
				{
					WebMapConfig.WORLD_START_POS = val.m_position;
					ZLog.Log((object)("WebMap: starting point " + ((object)(Vector3)(ref WebMapConfig.WORLD_START_POS)).ToString()));
				}
				else
				{
					ZLog.LogError((object)"WebMap: failed to find starting point");
				}
				instance.Online();
				mapDataServer.ListenAsync();
			}
		}

		[HarmonyPatch(typeof(ZNet), "Start")]
		private class ZNetPatchStart
		{
			private static void Postfix(List<ZNetPeer> ___m_peers)
			{
				mapDataServer.players = ___m_peers;
			}
		}

		[HarmonyPatch(typeof(ZNet), "Shutdown")]
		private class ZNetPatchShutdown
		{
			private static void Postfix()
			{
				mapDataServer.Stop();
				instance.NotifyOffline();
			}
		}

		[HarmonyPatch(typeof(ZNet), "SetServer")]
		private class ZNetPatchSetServer
		{
			private static void Postfix(bool server, bool openServer, bool publicServer, string serverName, string password, World world)
			{
				instance.SetServerInfo(openServer, publicServer, serverName, password, world.m_name, world.m_seedName);
			}
		}

		[HarmonyPatch(typeof(ZNet), "Disconnect")]
		private class ZNetPatchDisconnect
		{
			private static void Prefix(ref ZNetPeer peer)
			{
				if (!peer.m_server)
				{
					instance.NotifyLeave(peer);
				}
			}
		}

		[HarmonyPatch(typeof(ZRoutedRpc), "AddPeer")]
		private class ZRoutedRpcAddPeerPatch
		{
			private static void Postfix(ZNetPeer peer)
			{
				if (!peer.m_server)
				{
					instance.NotifyJoin(peer);
				}
			}
		}

		[HarmonyPatch(typeof(ZRoutedRpc), "HandleRoutedRPC")]
		private class ZRoutedRpcPatch
		{
			private static string[] ignoreRpc = new string[4] { "DestroyZDO", "SetEvent", "OnTargeted", "Step" };

			private static void Postfix(ref ZRoutedRpc __instance, ref RoutedRPCData data)
			{
				//IL_0114: Unknown result type (might be due to invalid IL or missing references)
				//IL_011e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0123: Unknown result type (might be due to invalid IL or missing references)
				//IL_0137: Unknown result type (might be due to invalid IL or missing references)
				//IL_013e: Expected O, but got Unknown
				//IL_0414: Unknown result type (might be due to invalid IL or missing references)
				//IL_0281: Unknown result type (might be due to invalid IL or missing references)
				//IL_04ec: Unknown result type (might be due to invalid IL or missing references)
				//IL_04f3: Expected O, but got Unknown
				//IL_04f5: Unknown result type (might be due to invalid IL or missing references)
				//IL_04fa: Unknown result type (might be due to invalid IL or missing references)
				//IL_0505: Unknown result type (might be due to invalid IL or missing references)
				//IL_050c: Expected O, but got Unknown
				//IL_052d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0539: Unknown result type (might be due to invalid IL or missing references)
				//IL_059e: Unknown result type (might be due to invalid IL or missing references)
				string methodName = StringExtensionMethods_Patch.GetStableHashName(data?.m_methodHash ?? 0);
				if (Array.Exists(ignoreRpc, (string x) => x == methodName))
				{
					return;
				}
				if (WebMapConfig.DEBUG)
				{
					ZLog.Log((object)("HandleRoutedRPC: " + methodName));
				}
				ZNetPeer peer = ZNet.instance.GetPeer(data.m_senderPeerID);
				string steamid = "";
				try
				{
					steamid = peer.m_rpc.GetSocket().GetHostName();
				}
				catch
				{
				}
				if (data?.m_methodHash == sayMethodHash || data?.m_methodHash == StringExtensionMethods.GetStableHashCode("Say"))
				{
					sayMethodHash = data.m_methodHash;
					try
					{
						Vector3 position = ZDOMan.instance.GetZDO(peer.m_characterID).GetPosition();
						ZPackage parameters = data.m_parameters;
						int num = parameters.ReadInt();
						UserInfo val = new UserInfo();
						val.Deserialize(ref parameters);
						string text = parameters.ReadString() ?? "";
						text = text.Trim();
						if (text.StartsWith("!pin"))
						{
							string[] messageParts = text.Split(new char[1] { ' ' });
							string type = "dot";
							int num2 = 1;
							if (messageParts.Length > 1 && Array.Exists(ALLOWED_PINS, (string e) => e == messageParts[1]))
							{
								type = messageParts[1];
								num2 = 2;
							}
							string text2 = "";
							if (num2 < messageParts.Length)
							{
								text2 = string.Join(" ", messageParts, num2, messageParts.Length - num2);
							}
							if (text2.Length > 20)
							{
								text2 = text2.Substring(0, 20);
							}
							string pinText2 = Regex.Replace(text2, "[^a-zA-Z0-9 ]", "");
							long num3 = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
							string pinId = $"{num3}-{Random.Range(1000, 9999)}";
							mapDataServer.AddPin(steamid, pinId, type, val.Name, position, pinText2);
							for (int num4 = mapDataServer.pins.FindAll((string pin) => pin.StartsWith(steamid)).Count - WebMapConfig.MAX_PINS_PER_USER; num4 > 0; num4--)
							{
								int idx = mapDataServer.pins.FindIndex((string pin) => pin.StartsWith(steamid));
								mapDataServer.RemovePin(idx);
							}
							SavePins();
						}
						else if (text.StartsWith("!undoPin"))
						{
							int num5 = mapDataServer.pins.FindLastIndex((string pin) => pin.StartsWith(steamid));
							if (num5 > -1)
							{
								mapDataServer.RemovePin(num5);
								SavePins();
							}
						}
						else if (text.StartsWith("!deletePin"))
						{
							string[] array = text.Split(new char[1] { ' ' });
							string pinText = "";
							if (array.Length > 1)
							{
								pinText = string.Join(" ", array, 1, array.Length - 1);
							}
							int num6 = mapDataServer.pins.FindLastIndex(delegate(string pin)
							{
								string[] array2 = pin.Split(new char[1] { ',' });
								return array2[0] == steamid && array2[^1] == pinText;
							});
							if (num6 > -1)
							{
								mapDataServer.RemovePin(num6);
								SavePins();
							}
						}
						else
						{
							if (num != 0)
							{
								mapDataServer.AddMessage(data.m_senderPeerID, num, val.Name, text);
							}
							ZLog.Log((object)$"WebMap: (say) {position} | {num} | {val.Name} | {text}");
						}
						return;
					}
					catch (Exception ex)
					{
						if (WebMapConfig.DEBUG)
						{
							ZLog.LogError((object)ex.ToString());
						}
						return;
					}
				}
				if (data?.m_methodHash != chatMessageMethodHash && data?.m_methodHash != StringExtensionMethods.GetStableHashCode("ChatMessage"))
				{
					return;
				}
				chatMessageMethodHash = data.m_methodHash;
				try
				{
					ZPackage val2 = new ZPackage(data.m_parameters.GetArray());
					Vector3 val3 = val2.ReadVector3();
					int num7 = val2.ReadInt();
					UserInfo val4 = new UserInfo();
					val4.Deserialize(ref val2);
					if (num7 == 3)
					{
						mapDataServer.BroadcastPing(data.m_senderPeerID, val4.Name, val3);
						ZLog.Log((object)$"WebMap: (ping) {val3} | {num7} | {val4.Name}");
						return;
					}
					string text3 = val2.ReadString() ?? "";
					text3 = text3.Trim();
					mapDataServer.AddMessage(data.m_senderPeerID, num7, val4.Name, text3);
					ZLog.Log((object)$"WebMap: (chat) {val3} | {num7} | {val4.Name} | {text3}");
				}
				catch (Exception ex2)
				{
					if (WebMapConfig.DEBUG)
					{
						ZLog.LogError((object)ex2.ToString());
					}
				}
			}
		}

		public const string GUID = "com.github.h0tw1r3.valheim.webmap";

		public const string NAME = "WebMap";

		public const string VERSION = "2.6.1";

		private static readonly string[] ALLOWED_PINS = new string[5] { "dot", "fire", "mine", "house", "cave" };

		public DiscordWebHook discordWebHook;

		public static MapDataServer mapDataServer;

		public static string worldDataPath;

		public static string mapDataPath;

		public static string pluginPath;

		public static int sayMethodHash = 0;

		public static int chatMessageMethodHash = 0;

		public static bool fogTextureNeedsSaving;

		public static string currentWorldName;

		public static Dictionary<string, object> serverInfo;

		private static Harmony harmony;

		public static WebMap instance;

		public void Awake()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			instance = this;
			harmony = new Harmony("com.github.h0tw1r3.valheim.webmap");
			harmony.PatchAll(Assembly.GetExecutingAssembly());
			pluginPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			mapDataPath = Path.Combine(pluginPath ?? string.Empty, "map_data");
			Directory.CreateDirectory(mapDataPath);
			WebMapConfig.ReadConfigFile(((BaseUnityPlugin)this).Config);
			discordWebHook = new DiscordWebHook(WebMapConfig.DISCORD_WEBHOOK);
		}

		public void OnDestroy()
		{
			((BaseUnityPlugin)this).Config.Save();
		}

		public void Online()
		{
			StaticCoroutine.Start(SaveFogTextureLoop());
			StaticCoroutine.Start(UpdateFogTextureLoop());
			NotifyOnline();
		}

		public void SetServerInfo(bool openServer, bool publicServer, string serverName, string password, string worldName, string worldSeed)
		{
			serverInfo = new Dictionary<string, object>();
			serverInfo.Add("openServer", openServer);
			serverInfo.Add("publicServer", publicServer);
			serverInfo.Add("serverName", serverName);
			serverInfo.Add("password", password);
			serverInfo.Add("worldName", worldName);
			serverInfo.Add("worldSeed", worldSeed);
		}

		public void NotifyOnline()
		{
			discordWebHook.SendMessage(string.Format("\ud83c\udfae **{0}** is *online* \ud83d\udfe2\n\ud83d\udcbb {1}:{2}\n\ud83d\udd11 {3}\n\ud83d\uddfa {4}", serverInfo["serverName"], AccessTools.Method(typeof(ZNet), "GetServerIP", (Type[])null, (Type[])null).Invoke(ZNet.instance, new object[0]), ZNet.instance.m_hostPort, serverInfo["password"], WebMapConfig.URL));
		}

		public void NotifyOffline()
		{
			discordWebHook.SendMessage(string.Format("\ud83c\udfae **{0}** is *offline* \ud83d\udd34", serverInfo["serverName"]));
		}

		public void NotifyJoin(ZNetPeer peer)
		{
			string text = "player _" + peer.m_playerName + "_ joined";
			discordWebHook.SendMessage(string.Format("\ud83c\udfae **{0}** {1}", serverInfo["serverName"], text));
			mapDataServer.AddMessage(peer.m_uid, 1, "Server", text);
		}

		public void NotifyLeave(ZNetPeer peer)
		{
			string text = "player _" + peer.m_playerName + "_ left";
			discordWebHook.SendMessage(string.Format("\ud83c\udfae **{0}** {1}", serverInfo["serverName"], text));
			MessageHud.instance.MessageAll((MessageType)2, text);
			mapDataServer.AddMessage(peer.m_uid, 1, "Server", text);
		}

		public void NewWorld()
		{
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Expected O, but got Unknown
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Expected O, but got Unknown
			//IL_013e: 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_0148: Unknown result type (might be due to invalid IL or missing references)
			string worldName = WebMapConfig.GetWorldName();
			bool flag = currentWorldName != worldName;
			worldDataPath = Path.Combine(mapDataPath, WebMapConfig.GetWorldName());
			Directory.CreateDirectory(worldDataPath);
			if (mapDataServer == null)
			{
				ZLog.Log((object)("WebMap: loading existing world: #" + worldName));
				mapDataServer = new MapDataServer();
			}
			else if (flag)
			{
				ZLog.Log((object)("WebMap: loading a new world! old: #" + currentWorldName + " new: #" + worldName));
			}
			currentWorldName = worldName;
			string path = Path.Combine(worldDataPath, "map.png");
			try
			{
				mapDataServer.mapImageData = File.ReadAllBytes(path);
			}
			catch (Exception ex)
			{
				ZLog.LogError((object)("WebMap: Failed to read map image data from disk. " + ex.Message));
			}
			string path2 = Path.Combine(worldDataPath, "fog.png");
			try
			{
				Texture2D val = new Texture2D(WebMapConfig.TEXTURE_SIZE, WebMapConfig.TEXTURE_SIZE);
				byte[] array = File.ReadAllBytes(path2);
				ImageConversion.LoadImage(val, array);
				mapDataServer.fogTexture = val;
			}
			catch (Exception ex2)
			{
				ZLog.LogWarning((object)("WebMap: Failed to read fog image data from disk... Making new fog image..." + ex2.Message));
				Texture2D val2 = new Texture2D(WebMapConfig.TEXTURE_SIZE, WebMapConfig.TEXTURE_SIZE, (TextureFormat)63, false);
				Color32[] array2 = (Color32[])(object)new Color32[WebMapConfig.TEXTURE_SIZE * WebMapConfig.TEXTURE_SIZE];
				for (int i = 0; i < array2.Length; i++)
				{
					array2[i] = Color32.op_Implicit(Color.black);
				}
				val2.SetPixels32(array2);
				byte[] bytes = ImageConversion.EncodeToPNG(val2);
				mapDataServer.fogTexture = val2;
				try
				{
					File.WriteAllBytes(path2, bytes);
				}
				catch (Exception ex3)
				{
					ZLog.LogError((object)("WebMap: FAILED TO WRITE FOG FILE! " + ex3.Message));
				}
			}
			string path3 = Path.Combine(worldDataPath, "pins.csv");
			try
			{
				string[] collection = File.ReadAllLines(path3);
				mapDataServer.pins = new List<string>(collection);
			}
			catch (Exception ex4)
			{
				ZLog.LogError((object)("WebMap: Failed to read pins.csv from disk. " + ex4.Message));
			}
			if (flag)
			{
				mapDataServer.Reload();
			}
		}

		public IEnumerator UpdateFogTextureLoop()
		{
			while (true)
			{
				yield return (object)new WaitForSeconds(WebMapConfig.UPDATE_FOG_TEXTURE_INTERVAL);
				UpdateFogTexture();
			}
		}

		public void UpdateFogTexture()
		{
			int pixelExploreRadius = (int)Mathf.Ceil(WebMapConfig.EXPLORE_RADIUS / (float)WebMapConfig.PIXEL_SIZE);
			int pixelExploreRadiusSquared = pixelExploreRadius * pixelExploreRadius;
			int halfTextureSize = WebMapConfig.TEXTURE_SIZE / 2;
			mapDataServer.players.ForEach(delegate(ZNetPeer player)
			{
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				//IL_0038: Unknown result type (might be due to invalid IL or missing references)
				//IL_003d: 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)
				//IL_010e: Unknown result type (might be due to invalid IL or missing references)
				if (player.m_publicRefPos || WebMapConfig.ALWAYS_MAP || WebMapConfig.ALWAYS_VISIBLE)
				{
					ZDO val = null;
					try
					{
						val = ZDOMan.instance.GetZDO(player.m_characterID);
					}
					catch
					{
					}
					if (val != null)
					{
						Vector3 position = val.GetPosition();
						int num = Mathf.RoundToInt(position.x / (float)WebMapConfig.PIXEL_SIZE + (float)halfTextureSize);
						int num2 = Mathf.RoundToInt(position.z / (float)WebMapConfig.PIXEL_SIZE + (float)halfTextureSize);
						for (int i = num2 - pixelExploreRadius; i <= num2 + pixelExploreRadius; i++)
						{
							for (int j = num - pixelExploreRadius; j <= num + pixelExploreRadius; j++)
							{
								if (i >= 0 && j >= 0 && i < WebMapConfig.TEXTURE_SIZE && j < WebMapConfig.TEXTURE_SIZE)
								{
									int num3 = num - j;
									int num4 = num2 - i;
									if (num3 * num3 + num4 * num4 < pixelExploreRadiusSquared && mapDataServer.fogTexture.GetPixel(j, i) != Color.white)
									{
										if (WebMapConfig.DEBUG && !fogTextureNeedsSaving)
										{
											ZLog.Log((object)"Fog needs saving");
										}
										fogTextureNeedsSaving = true;
										mapDataServer.fogTexture.SetPixel(j, i, Color.white);
									}
								}
							}
						}
					}
				}
			});
		}

		public IEnumerator SaveFogTextureLoop()
		{
			while (true)
			{
				yield return (object)new WaitForSeconds(WebMapConfig.SAVE_FOG_TEXTURE_INTERVAL);
				SaveFogTexture();
			}
		}

		public void SaveFogTexture()
		{
			if (mapDataServer.players.Count > 0 && fogTextureNeedsSaving)
			{
				byte[] bytes = ImageConversion.EncodeToPNG(mapDataServer.fogTexture);
				if (WebMapConfig.DEBUG)
				{
					ZLog.Log((object)"Saving Fog");
				}
				try
				{
					File.WriteAllBytes(Path.Combine(worldDataPath, "fog.png"), bytes);
					fogTextureNeedsSaving = false;
				}
				catch (Exception ex)
				{
					ZLog.LogError((object)("WebMap: FAILED TO WRITE FOG FILE! " + ex.Message));
				}
			}
		}

		public static void SavePins()
		{
			string path = Path.Combine(worldDataPath, "pins.csv");
			try
			{
				File.WriteAllLines(path, mapDataServer.pins);
			}
			catch (Exception ex)
			{
				ZLog.Log((object)("WebMap: FAILED TO WRITE PINS FILE! " + ex.Message));
			}
		}
	}
	public class StaticCoroutine
	{
		private class StaticCoroutineRunner : MonoBehaviour
		{
		}

		private static StaticCoroutineRunner runner;

		public static Coroutine Start(IEnumerator coroutine)
		{
			EnsureRunner();
			return ((MonoBehaviour)runner).StartCoroutine(coroutine);
		}

		private static void EnsureRunner()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)runner == (Object)null)
			{
				runner = new GameObject("[Static Coroutine Runner]").AddComponent<StaticCoroutineRunner>();
				Object.DontDestroyOnLoad((Object)(object)((Component)runner).gameObject);
			}
		}
	}
}
namespace WebMap.Patches
{
	[HarmonyPatch]
	internal class StringExtensionMethods_Patch
	{
		internal static Dictionary<int, string> stablehashNames = new Dictionary<int, string>();

		internal static Dictionary<int, string> stablehashNamesAnim = new Dictionary<int, string>();

		internal static Dictionary<string, int> stablehashLookup = new Dictionary<string, int>();

		internal static Dictionary<string, int> stablehashLookupAnim = new Dictionary<string, int>();

		[HarmonyPatch(typeof(StringExtensionMethods), "GetStableHashCode")]
		[HarmonyPrefix]
		public static bool GetStableHashCode(string str, ref int __result)
		{
			if (stablehashLookup.TryGetValue(str, out __result))
			{
				return false;
			}
			int num = 5381;
			int num2 = num;
			for (int i = 0; i < str.Length && str[i] != 0; i += 2)
			{
				num = ((num << 5) + num) ^ str[i];
				if (i == str.Length - 1 || str[i + 1] == '\0')
				{
					break;
				}
				num2 = ((num2 << 5) + num2) ^ str[i + 1];
			}
			__result = num + num2 * 1566083941;
			stablehashNames[__result] = str;
			stablehashLookup[str] = __result;
			if (WebMapConfig.DEBUG)
			{
				ZLog.Log((object)$"First GetStableHashCode: {str} -> {__result}");
			}
			return false;
		}

		[HarmonyPatch(typeof(ZSyncAnimation), "GetHash")]
		[HarmonyPrefix]
		public static void GetAnimHash(string name, ref int __result, ref bool __runOriginal)
		{
			if (stablehashLookupAnim.TryGetValue(name, out __result))
			{
				__runOriginal = false;
			}
			else
			{
				__runOriginal = true;
			}
		}

		[HarmonyPatch(typeof(ZSyncAnimation), "GetHash")]
		[HarmonyPostfix]
		public static void AddAnimHash(string name, ref int __result, ref bool __runOriginal)
		{
			if (__runOriginal)
			{
				stablehashNamesAnim[__result] = name;
				stablehashLookupAnim[name] = __result;
				if (WebMapConfig.DEBUG)
				{
					ZLog.Log((object)$"First GetAnimHash: {name} -> {__result}");
				}
			}
		}

		public static string GetStableHashName(int code)
		{
			if (stablehashNames.TryGetValue(code, out var value))
			{
				return value;
			}
			if (stablehashNamesAnim.TryGetValue(code - 438569, out value))
			{
				return value + " (A)";
			}
			return code.ToString();
		}
	}
	[HarmonyPatch]
	internal class ZRoutedRpc_Patch
	{
		private static string[] ignoreRpc = new string[4] { "DestroyZDO", "SetEvent", "OnTargeted", "Step" };

		[HarmonyPatch(typeof(ZRoutedRpc), "InvokeRoutedRPC", new Type[]
		{
			typeof(long),
			typeof(ZDOID),
			typeof(string),
			typeof(object[])
		})]
		[HarmonyPrefix]
		private static void InvokeRoutedRPC(ref ZRoutedRpc __instance, ref long targetPeerID, ZDOID targetZDO, string methodName, params object[] parameters)
		{
			if (WebMapConfig.DEBUG && !Array.Exists(ignoreRpc, (string x) => x == methodName))
			{
				ZLog.Log((object)("RoutedRPC Invoking: " + methodName + " " + StringExtensionMethods.GetStableHashCode(methodName)));
			}
			if (WebMapConfig.TEST && methodName == "DiscoverLocationRespons")
			{
				ZLog.Log((object)("TEST: Sending discovered location to everyone: " + methodName + " " + parameters[0]?.ToString() + " " + parameters[1]?.ToString() + " " + parameters[2]?.ToString() + " " + parameters[3]));
				targetPeerID = ZRoutedRpc.Everybody;
			}
		}
	}
}

websocket-sharp.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Security.Authentication;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Permissions;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Timers;
using WebSocketSharp.Net;
using WebSocketSharp.Net.WebSockets;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("websocket-sharp")]
[assembly: AssemblyDescription("A C# implementation of the WebSocket protocol client and server")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("websocket-sharp.dll")]
[assembly: AssemblyCopyright("sta.blockhead")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("1.0.2.29017")]
namespace WebSocketSharp
{
	public static class Ext
	{
		private static readonly byte[] _last = new byte[1];

		private static readonly int _retry = 5;

		private const string _tspecials = "()<>@,;:\\\"/[]?={} \t";

		private static byte[] compress(this byte[] data)
		{
			if (data.LongLength == 0)
			{
				return data;
			}
			using MemoryStream stream = new MemoryStream(data);
			return stream.compressToArray();
		}

		private static MemoryStream compress(this Stream stream)
		{
			MemoryStream memoryStream = new MemoryStream();
			if (stream.Length == 0)
			{
				return memoryStream;
			}
			stream.Position = 0L;
			using DeflateStream deflateStream = new DeflateStream(memoryStream, CompressionMode.Compress, leaveOpen: true);
			CopyTo(stream, deflateStream, 1024);
			deflateStream.Close();
			memoryStream.Write(_last, 0, 1);
			memoryStream.Position = 0L;
			return memoryStream;
		}

		private static byte[] compressToArray(this Stream stream)
		{
			using MemoryStream memoryStream = stream.compress();
			memoryStream.Close();
			return memoryStream.ToArray();
		}

		private static byte[] decompress(this byte[] data)
		{
			if (data.LongLength == 0)
			{
				return data;
			}
			using MemoryStream stream = new MemoryStream(data);
			return stream.decompressToArray();
		}

		private static MemoryStream decompress(this Stream stream)
		{
			MemoryStream memoryStream = new MemoryStream();
			if (stream.Length == 0)
			{
				return memoryStream;
			}
			stream.Position = 0L;
			using DeflateStream source = new DeflateStream(stream, CompressionMode.Decompress, leaveOpen: true);
			CopyTo(source, memoryStream, 1024);
			memoryStream.Position = 0L;
			return memoryStream;
		}

		private static byte[] decompressToArray(this Stream stream)
		{
			using MemoryStream memoryStream = stream.decompress();
			memoryStream.Close();
			return memoryStream.ToArray();
		}

		private static bool isHttpMethod(this string value)
		{
			int result;
			switch (value)
			{
			default:
				result = ((value == "TRACE") ? 1 : 0);
				break;
			case "GET":
			case "HEAD":
			case "POST":
			case "PUT":
			case "DELETE":
			case "CONNECT":
			case "OPTIONS":
				result = 1;
				break;
			}
			return (byte)result != 0;
		}

		private static bool isHttpMethod10(this string value)
		{
			return value == "GET" || value == "HEAD" || value == "POST";
		}

		internal static byte[] Append(this ushort code, string reason)
		{
			byte[] array = code.InternalToByteArray(ByteOrder.Big);
			if (reason == null || reason.Length == 0)
			{
				return array;
			}
			List<byte> list = new List<byte>(array);
			list.AddRange(Encoding.UTF8.GetBytes(reason));
			return list.ToArray();
		}

		internal static byte[] Compress(this byte[] data, CompressionMethod method)
		{
			return (method == CompressionMethod.Deflate) ? data.compress() : data;
		}

		internal static Stream Compress(this Stream stream, CompressionMethod method)
		{
			return (method == CompressionMethod.Deflate) ? stream.compress() : stream;
		}

		internal static byte[] CompressToArray(this Stream stream, CompressionMethod method)
		{
			return (method == CompressionMethod.Deflate) ? stream.compressToArray() : stream.ToByteArray();
		}

		internal static bool Contains(this string value, params char[] anyOf)
		{
			return anyOf != null && anyOf.Length != 0 && value.IndexOfAny(anyOf) > -1;
		}

		internal static bool Contains(this NameValueCollection collection, string name)
		{
			return collection[name] != null;
		}

		internal static bool Contains(this NameValueCollection collection, string name, string value, StringComparison comparisonTypeForValue)
		{
			string text = collection[name];
			if (text == null)
			{
				return false;
			}
			string[] array = text.Split(new char[1] { ',' });
			foreach (string text2 in array)
			{
				if (text2.Trim().Equals(value, comparisonTypeForValue))
				{
					return true;
				}
			}
			return false;
		}

		internal static bool Contains<T>(this IEnumerable<T> source, Func<T, bool> condition)
		{
			foreach (T item in source)
			{
				if (condition(item))
				{
					return true;
				}
			}
			return false;
		}

		internal static bool ContainsTwice(this string[] values)
		{
			int len = values.Length;
			int end = len - 1;
			Func<int, bool> seek = null;
			seek = delegate(int idx)
			{
				if (idx == end)
				{
					return false;
				}
				string text = values[idx];
				for (int i = idx + 1; i < len; i++)
				{
					if (values[i] == text)
					{
						return true;
					}
				}
				return seek(++idx);
			};
			return seek(0);
		}

		internal static T[] Copy<T>(this T[] source, int length)
		{
			T[] array = new T[length];
			Array.Copy(source, 0, array, 0, length);
			return array;
		}

		internal static T[] Copy<T>(this T[] source, long length)
		{
			T[] array = new T[length];
			Array.Copy(source, 0L, array, 0L, length);
			return array;
		}

		internal static void CopyTo(this Stream source, Stream destination, int bufferLength)
		{
			byte[] buffer = new byte[bufferLength];
			int num = 0;
			while (true)
			{
				num = source.Read(buffer, 0, bufferLength);
				if (num <= 0)
				{
					break;
				}
				destination.Write(buffer, 0, num);
			}
		}

		internal static void CopyToAsync(this Stream source, Stream destination, int bufferLength, Action completed, Action<Exception> error)
		{
			byte[] buff = new byte[bufferLength];
			AsyncCallback callback = null;
			callback = delegate(IAsyncResult ar)
			{
				try
				{
					int num = source.EndRead(ar);
					if (num <= 0)
					{
						if (completed != null)
						{
							completed();
						}
					}
					else
					{
						destination.Write(buff, 0, num);
						source.BeginRead(buff, 0, bufferLength, callback, null);
					}
				}
				catch (Exception obj2)
				{
					if (error != null)
					{
						error(obj2);
					}
				}
			};
			try
			{
				source.BeginRead(buff, 0, bufferLength, callback, null);
			}
			catch (Exception obj)
			{
				if (error != null)
				{
					error(obj);
				}
			}
		}

		internal static byte[] Decompress(this byte[] data, CompressionMethod method)
		{
			return (method == CompressionMethod.Deflate) ? data.decompress() : data;
		}

		internal static Stream Decompress(this Stream stream, CompressionMethod method)
		{
			return (method == CompressionMethod.Deflate) ? stream.decompress() : stream;
		}

		internal static byte[] DecompressToArray(this Stream stream, CompressionMethod method)
		{
			return (method == CompressionMethod.Deflate) ? stream.decompressToArray() : stream.ToByteArray();
		}

		internal static void Emit(this EventHandler eventHandler, object sender, EventArgs e)
		{
			eventHandler?.Invoke(sender, e);
		}

		internal static void Emit<TEventArgs>(this EventHandler<TEventArgs> eventHandler, object sender, TEventArgs e) where TEventArgs : EventArgs
		{
			eventHandler?.Invoke(sender, e);
		}

		internal static bool EqualsWith(this int value, char c, Action<int> action)
		{
			action(value);
			return value == c;
		}

		internal static string GetAbsolutePath(this Uri uri)
		{
			if (uri.IsAbsoluteUri)
			{
				return uri.AbsolutePath;
			}
			string originalString = uri.OriginalString;
			if (originalString[0] != '/')
			{
				return null;
			}
			int num = originalString.IndexOfAny(new char[2] { '?', '#' });
			return (num > 0) ? originalString.Substring(0, num) : originalString;
		}

		internal static WebSocketSharp.Net.CookieCollection GetCookies(this NameValueCollection headers, bool response)
		{
			string text = headers[response ? "Set-Cookie" : "Cookie"];
			return (text != null) ? WebSocketSharp.Net.CookieCollection.Parse(text, response) : new WebSocketSharp.Net.CookieCollection();
		}

		internal static string GetDnsSafeHost(this Uri uri, bool bracketIPv6)
		{
			return (bracketIPv6 && uri.HostNameType == UriHostNameType.IPv6) ? uri.Host : uri.DnsSafeHost;
		}

		internal static string GetMessage(this CloseStatusCode code)
		{
			return code switch
			{
				CloseStatusCode.TlsHandshakeFailure => "An error has occurred during a TLS handshake.", 
				CloseStatusCode.ServerError => "WebSocket server got an internal error.", 
				CloseStatusCode.MandatoryExtension => "WebSocket client didn't receive expected extension(s).", 
				CloseStatusCode.TooBig => "A too big message has been received.", 
				CloseStatusCode.PolicyViolation => "A policy violation has occurred.", 
				CloseStatusCode.InvalidData => "Invalid data has been received.", 
				CloseStatusCode.Abnormal => "An exception has occurred.", 
				CloseStatusCode.UnsupportedData => "Unsupported data has been received.", 
				CloseStatusCode.ProtocolError => "A WebSocket protocol error has occurred.", 
				_ => string.Empty, 
			};
		}

		internal static string GetName(this string nameAndValue, char separator)
		{
			int num = nameAndValue.IndexOf(separator);
			return (num > 0) ? nameAndValue.Substring(0, num).Trim() : null;
		}

		internal static string GetUTF8DecodedString(this byte[] bytes)
		{
			return Encoding.UTF8.GetString(bytes);
		}

		internal static byte[] GetUTF8EncodedBytes(this string s)
		{
			return Encoding.UTF8.GetBytes(s);
		}

		internal static string GetValue(this string nameAndValue, char separator)
		{
			return nameAndValue.GetValue(separator, unquote: false);
		}

		internal static string GetValue(this string nameAndValue, char separator, bool unquote)
		{
			int num = nameAndValue.IndexOf(separator);
			if (num < 0 || num == nameAndValue.Length - 1)
			{
				return null;
			}
			string text = nameAndValue.Substring(num + 1).Trim();
			return unquote ? text.Unquote() : text;
		}

		internal static byte[] InternalToByteArray(this ushort value, ByteOrder order)
		{
			byte[] bytes = BitConverter.GetBytes(value);
			if (!order.IsHostOrder())
			{
				Array.Reverse((Array)bytes);
			}
			return bytes;
		}

		internal static byte[] InternalToByteArray(this ulong value, ByteOrder order)
		{
			byte[] bytes = BitConverter.GetBytes(value);
			if (!order.IsHostOrder())
			{
				Array.Reverse((Array)bytes);
			}
			return bytes;
		}

		internal static bool IsCompressionExtension(this string value, CompressionMethod method)
		{
			return value.StartsWith(method.ToExtensionString());
		}

		internal static bool IsControl(this byte opcode)
		{
			return opcode > 7 && opcode < 16;
		}

		internal static bool IsControl(this Opcode opcode)
		{
			return (int)opcode >= 8;
		}

		internal static bool IsData(this byte opcode)
		{
			return opcode == 1 || opcode == 2;
		}

		internal static bool IsData(this Opcode opcode)
		{
			return opcode == Opcode.Text || opcode == Opcode.Binary;
		}

		internal static bool IsHttpMethod(this string value, Version version)
		{
			return (version == WebSocketSharp.Net.HttpVersion.Version10) ? value.isHttpMethod10() : value.isHttpMethod();
		}

		internal static bool IsPortNumber(this int value)
		{
			return value > 0 && value < 65536;
		}

		internal static bool IsReserved(this ushort code)
		{
			return code == 1004 || code == 1005 || code == 1006 || code == 1015;
		}

		internal static bool IsReserved(this CloseStatusCode code)
		{
			return code == CloseStatusCode.Undefined || code == CloseStatusCode.NoStatus || code == CloseStatusCode.Abnormal || code == CloseStatusCode.TlsHandshakeFailure;
		}

		internal static bool IsSupported(this byte opcode)
		{
			return Enum.IsDefined(typeof(Opcode), opcode);
		}

		internal static bool IsText(this string value)
		{
			int length = value.Length;
			for (int i = 0; i < length; i++)
			{
				char c = value[i];
				if (c < ' ')
				{
					if ("\r\n\t".IndexOf(c) == -1)
					{
						return false;
					}
					if (c == '\n')
					{
						i++;
						if (i == length)
						{
							break;
						}
						c = value[i];
						if (" \t".IndexOf(c) == -1)
						{
							return false;
						}
					}
				}
				else if (c == '\u007f')
				{
					return false;
				}
			}
			return true;
		}

		internal static bool IsToken(this string value)
		{
			foreach (char c in value)
			{
				if (c < ' ')
				{
					return false;
				}
				if (c > '~')
				{
					return false;
				}
				if ("()<>@,;:\\\"/[]?={} \t".IndexOf(c) > -1)
				{
					return false;
				}
			}
			return true;
		}

		internal static bool KeepsAlive(this NameValueCollection headers, Version version)
		{
			StringComparison comparisonTypeForValue = StringComparison.OrdinalIgnoreCase;
			return (version < WebSocketSharp.Net.HttpVersion.Version11) ? headers.Contains("Connection", "keep-alive", comparisonTypeForValue) : (!headers.Contains("Connection", "close", comparisonTypeForValue));
		}

		internal static string Quote(this string value)
		{
			return string.Format("\"{0}\"", value.Replace("\"", "\\\""));
		}

		internal static byte[] ReadBytes(this Stream stream, int length)
		{
			byte[] array = new byte[length];
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			while (length > 0)
			{
				num3 = stream.Read(array, num, length);
				if (num3 <= 0)
				{
					if (num2 >= _retry)
					{
						return array.SubArray(0, num);
					}
					num2++;
				}
				else
				{
					num2 = 0;
					num += num3;
					length -= num3;
				}
			}
			return array;
		}

		internal static byte[] ReadBytes(this Stream stream, long length, int bufferLength)
		{
			using MemoryStream memoryStream = new MemoryStream();
			byte[] buffer = new byte[bufferLength];
			int num = 0;
			int num2 = 0;
			while (length > 0)
			{
				if (length < bufferLength)
				{
					bufferLength = (int)length;
				}
				num2 = stream.Read(buffer, 0, bufferLength);
				if (num2 <= 0)
				{
					if (num >= _retry)
					{
						break;
					}
					num++;
				}
				else
				{
					num = 0;
					memoryStream.Write(buffer, 0, num2);
					length -= num2;
				}
			}
			memoryStream.Close();
			return memoryStream.ToArray();
		}

		internal static void ReadBytesAsync(this Stream stream, int length, Action<byte[]> completed, Action<Exception> error)
		{
			byte[] buff = new byte[length];
			int offset = 0;
			int retry = 0;
			AsyncCallback callback = null;
			callback = delegate(IAsyncResult ar)
			{
				try
				{
					int num = stream.EndRead(ar);
					if (num <= 0)
					{
						if (retry < _retry)
						{
							retry++;
							stream.BeginRead(buff, offset, length, callback, null);
						}
						else if (completed != null)
						{
							completed(buff.SubArray(0, offset));
						}
					}
					else if (num == length)
					{
						if (completed != null)
						{
							completed(buff);
						}
					}
					else
					{
						retry = 0;
						offset += num;
						length -= num;
						stream.BeginRead(buff, offset, length, callback, null);
					}
				}
				catch (Exception obj2)
				{
					if (error != null)
					{
						error(obj2);
					}
				}
			};
			try
			{
				stream.BeginRead(buff, offset, length, callback, null);
			}
			catch (Exception obj)
			{
				if (error != null)
				{
					error(obj);
				}
			}
		}

		internal static void ReadBytesAsync(this Stream stream, long length, int bufferLength, Action<byte[]> completed, Action<Exception> error)
		{
			MemoryStream dest = new MemoryStream();
			byte[] buff = new byte[bufferLength];
			int retry = 0;
			Action<long> read = null;
			read = delegate(long len)
			{
				if (len < bufferLength)
				{
					bufferLength = (int)len;
				}
				stream.BeginRead(buff, 0, bufferLength, delegate(IAsyncResult ar)
				{
					try
					{
						int num = stream.EndRead(ar);
						if (num <= 0)
						{
							if (retry < _retry)
							{
								int num2 = retry;
								retry = num2 + 1;
								read(len);
							}
							else
							{
								if (completed != null)
								{
									dest.Close();
									completed(dest.ToArray());
								}
								dest.Dispose();
							}
						}
						else
						{
							dest.Write(buff, 0, num);
							if (num == len)
							{
								if (completed != null)
								{
									dest.Close();
									completed(dest.ToArray());
								}
								dest.Dispose();
							}
							else
							{
								retry = 0;
								read(len - num);
							}
						}
					}
					catch (Exception obj2)
					{
						dest.Dispose();
						if (error != null)
						{
							error(obj2);
						}
					}
				}, null);
			};
			try
			{
				read(length);
			}
			catch (Exception obj)
			{
				dest.Dispose();
				if (error != null)
				{
					error(obj);
				}
			}
		}

		internal static T[] Reverse<T>(this T[] array)
		{
			int num = array.Length;
			T[] array2 = new T[num];
			int num2 = num - 1;
			for (int i = 0; i <= num2; i++)
			{
				array2[i] = array[num2 - i];
			}
			return array2;
		}

		internal static IEnumerable<string> SplitHeaderValue(this string value, params char[] separators)
		{
			int len = value.Length;
			int end = len - 1;
			StringBuilder buff = new StringBuilder(32);
			bool escaped = false;
			bool quoted = false;
			for (int i = 0; i <= end; i++)
			{
				char c = value[i];
				buff.Append(c);
				switch (c)
				{
				case '"':
					if (escaped)
					{
						escaped = false;
					}
					else
					{
						quoted = !quoted;
					}
					continue;
				case '\\':
					if (i == end)
					{
						break;
					}
					if (value[i + 1] == '"')
					{
						escaped = true;
					}
					continue;
				default:
					if (Array.IndexOf(separators, c) > -1 && !quoted)
					{
						buff.Length--;
						yield return buff.ToString();
						buff.Length = 0;
					}
					continue;
				}
				break;
			}
			yield return buff.ToString();
		}

		internal static byte[] ToByteArray(this Stream stream)
		{
			using MemoryStream memoryStream = new MemoryStream();
			stream.Position = 0L;
			CopyTo(stream, memoryStream, 1024);
			memoryStream.Close();
			return memoryStream.ToArray();
		}

		internal static CompressionMethod ToCompressionMethod(this string value)
		{
			Array values = Enum.GetValues(typeof(CompressionMethod));
			foreach (CompressionMethod item in values)
			{
				if (item.ToExtensionString() == value)
				{
					return item;
				}
			}
			return CompressionMethod.None;
		}

		internal static string ToExtensionString(this CompressionMethod method, params string[] parameters)
		{
			if (method == CompressionMethod.None)
			{
				return string.Empty;
			}
			string text = $"permessage-{method.ToString().ToLower()}";
			return (parameters != null && parameters.Length != 0) ? string.Format("{0}; {1}", text, parameters.ToString("; ")) : text;
		}

		internal static IPAddress ToIPAddress(this string value)
		{
			if (value == null || value.Length == 0)
			{
				return null;
			}
			if (IPAddress.TryParse(value, out IPAddress address))
			{
				return address;
			}
			try
			{
				IPAddress[] hostAddresses = Dns.GetHostAddresses(value);
				return hostAddresses[0];
			}
			catch
			{
				return null;
			}
		}

		internal static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
		{
			return new List<TSource>(source);
		}

		internal static string ToString(this IPAddress address, bool bracketIPv6)
		{
			return (bracketIPv6 && address.AddressFamily == AddressFamily.InterNetworkV6) ? $"[{address.ToString()}]" : address.ToString();
		}

		internal static ushort ToUInt16(this byte[] source, ByteOrder sourceOrder)
		{
			return BitConverter.ToUInt16(source.ToHostOrder(sourceOrder), 0);
		}

		internal static ulong ToUInt64(this byte[] source, ByteOrder sourceOrder)
		{
			return BitConverter.ToUInt64(source.ToHostOrder(sourceOrder), 0);
		}

		internal static IEnumerable<string> TrimEach(this IEnumerable<string> source)
		{
			foreach (string elm in source)
			{
				yield return elm.Trim();
			}
		}

		internal static string TrimSlashFromEnd(this string value)
		{
			string text = value.TrimEnd(new char[1] { '/' });
			return (text.Length > 0) ? text : "/";
		}

		internal static string TrimSlashOrBackslashFromEnd(this string value)
		{
			string text = value.TrimEnd('/', '\\');
			return (text.Length > 0) ? text : value[0].ToString();
		}

		internal static bool TryCreateVersion(this string versionString, out Version result)
		{
			result = null;
			try
			{
				result = new Version(versionString);
			}
			catch
			{
				return false;
			}
			return true;
		}

		internal static bool TryCreateWebSocketUri(this string uriString, out Uri result, out string message)
		{
			result = null;
			message = null;
			Uri uri = uriString.ToUri();
			if (uri == null)
			{
				message = "An invalid URI string.";
				return false;
			}
			if (!uri.IsAbsoluteUri)
			{
				message = "A relative URI.";
				return false;
			}
			string scheme = uri.Scheme;
			if (!(scheme == "ws") && !(scheme == "wss"))
			{
				message = "The scheme part is not 'ws' or 'wss'.";
				return false;
			}
			int port = uri.Port;
			if (port == 0)
			{
				message = "The port part is zero.";
				return false;
			}
			if (uri.Fragment.Length > 0)
			{
				message = "It includes the fragment component.";
				return false;
			}
			result = ((port != -1) ? uri : new Uri(string.Format("{0}://{1}:{2}{3}", scheme, uri.Host, (scheme == "ws") ? 80 : 443, uri.PathAndQuery)));
			return true;
		}

		internal static bool TryGetUTF8DecodedString(this byte[] bytes, out string s)
		{
			s = null;
			try
			{
				s = Encoding.UTF8.GetString(bytes);
			}
			catch
			{
				return false;
			}
			return true;
		}

		internal static bool TryGetUTF8EncodedBytes(this string s, out byte[] bytes)
		{
			bytes = null;
			try
			{
				bytes = Encoding.UTF8.GetBytes(s);
			}
			catch
			{
				return false;
			}
			return true;
		}

		internal static bool TryOpenRead(this FileInfo fileInfo, out FileStream fileStream)
		{
			fileStream = null;
			try
			{
				fileStream = fileInfo.OpenRead();
			}
			catch
			{
				return false;
			}
			return true;
		}

		internal static string Unquote(this string value)
		{
			int num = value.IndexOf('"');
			if (num == -1)
			{
				return value;
			}
			int num2 = value.LastIndexOf('"');
			if (num2 == num)
			{
				return value;
			}
			int num3 = num2 - num - 1;
			return (num3 > 0) ? value.Substring(num + 1, num3).Replace("\\\"", "\"") : string.Empty;
		}

		internal static bool Upgrades(this NameValueCollection headers, string protocol)
		{
			StringComparison comparisonTypeForValue = StringComparison.OrdinalIgnoreCase;
			return headers.Contains("Upgrade", protocol, comparisonTypeForValue) && headers.Contains("Connection", "Upgrade", comparisonTypeForValue);
		}

		internal static string UrlDecode(this string value, Encoding encoding)
		{
			return HttpUtility.UrlDecode(value, encoding);
		}

		internal static string UrlEncode(this string value, Encoding encoding)
		{
			return HttpUtility.UrlEncode(value, encoding);
		}

		internal static void WriteBytes(this Stream stream, byte[] bytes, int bufferLength)
		{
			using MemoryStream source = new MemoryStream(bytes);
			CopyTo(source, stream, bufferLength);
		}

		internal static void WriteBytesAsync(this Stream stream, byte[] bytes, int bufferLength, Action completed, Action<Exception> error)
		{
			MemoryStream src = new MemoryStream(bytes);
			src.CopyToAsync(stream, bufferLength, delegate
			{
				if (completed != null)
				{
					completed();
				}
				src.Dispose();
			}, delegate(Exception ex)
			{
				src.Dispose();
				if (error != null)
				{
					error(ex);
				}
			});
		}

		public static string GetDescription(this WebSocketSharp.Net.HttpStatusCode code)
		{
			return ((int)code).GetStatusDescription();
		}

		public static string GetStatusDescription(this int code)
		{
			return code switch
			{
				100 => "Continue", 
				101 => "Switching Protocols", 
				102 => "Processing", 
				200 => "OK", 
				201 => "Created", 
				202 => "Accepted", 
				203 => "Non-Authoritative Information", 
				204 => "No Content", 
				205 => "Reset Content", 
				206 => "Partial Content", 
				207 => "Multi-Status", 
				300 => "Multiple Choices", 
				301 => "Moved Permanently", 
				302 => "Found", 
				303 => "See Other", 
				304 => "Not Modified", 
				305 => "Use Proxy", 
				307 => "Temporary Redirect", 
				400 => "Bad Request", 
				401 => "Unauthorized", 
				402 => "Payment Required", 
				403 => "Forbidden", 
				404 => "Not Found", 
				405 => "Method Not Allowed", 
				406 => "Not Acceptable", 
				407 => "Proxy Authentication Required", 
				408 => "Request Timeout", 
				409 => "Conflict", 
				410 => "Gone", 
				411 => "Length Required", 
				412 => "Precondition Failed", 
				413 => "Request Entity Too Large", 
				414 => "Request-Uri Too Long", 
				415 => "Unsupported Media Type", 
				416 => "Requested Range Not Satisfiable", 
				417 => "Expectation Failed", 
				422 => "Unprocessable Entity", 
				423 => "Locked", 
				424 => "Failed Dependency", 
				500 => "Internal Server Error", 
				501 => "Not Implemented", 
				502 => "Bad Gateway", 
				503 => "Service Unavailable", 
				504 => "Gateway Timeout", 
				505 => "Http Version Not Supported", 
				507 => "Insufficient Storage", 
				_ => string.Empty, 
			};
		}

		public static bool IsCloseStatusCode(this ushort value)
		{
			return value > 999 && value < 5000;
		}

		public static bool IsEnclosedIn(this string value, char c)
		{
			if (value == null)
			{
				return false;
			}
			int length = value.Length;
			if (length < 2)
			{
				return false;
			}
			return value[0] == c && value[length - 1] == c;
		}

		public static bool IsHostOrder(this ByteOrder order)
		{
			return BitConverter.IsLittleEndian == (order == ByteOrder.Little);
		}

		public static bool IsLocal(this IPAddress address)
		{
			if (address == null)
			{
				throw new ArgumentNullException("address");
			}
			if (address.Equals(IPAddress.Any))
			{
				return true;
			}
			if (address.Equals(IPAddress.Loopback))
			{
				return true;
			}
			if (Socket.OSSupportsIPv6)
			{
				if (address.Equals(IPAddress.IPv6Any))
				{
					return true;
				}
				if (address.Equals(IPAddress.IPv6Loopback))
				{
					return true;
				}
			}
			string hostName = Dns.GetHostName();
			IPAddress[] hostAddresses = Dns.GetHostAddresses(hostName);
			IPAddress[] array = hostAddresses;
			foreach (IPAddress obj in array)
			{
				if (address.Equals(obj))
				{
					return true;
				}
			}
			return false;
		}

		public static bool IsNullOrEmpty(this string value)
		{
			return value == null || value.Length == 0;
		}

		public static bool IsPredefinedScheme(this string value)
		{
			if (value == null || value.Length < 2)
			{
				return false;
			}
			switch (value[0])
			{
			case 'h':
				return value == "http" || value == "https";
			case 'w':
				return value == "ws" || value == "wss";
			case 'f':
				return value == "file" || value == "ftp";
			case 'g':
				return value == "gopher";
			case 'm':
				return value == "mailto";
			case 'n':
			{
				char c = value[1];
				return (c != 'e') ? (value == "nntp") : (value == "news" || value == "net.pipe" || value == "net.tcp");
			}
			default:
				return false;
			}
		}

		public static bool MaybeUri(this string value)
		{
			if (value == null)
			{
				return false;
			}
			if (value.Length == 0)
			{
				return false;
			}
			int num = value.IndexOf(':');
			if (num == -1)
			{
				return false;
			}
			if (num >= 10)
			{
				return false;
			}
			string value2 = value.Substring(0, num);
			return value2.IsPredefinedScheme();
		}

		public static T[] SubArray<T>(this T[] array, int startIndex, int length)
		{
			if (array == null)
			{
				throw new ArgumentNullException("array");
			}
			int num = array.Length;
			if (num == 0)
			{
				if (startIndex != 0)
				{
					throw new ArgumentOutOfRangeException("startIndex");
				}
				if (length != 0)
				{
					throw new ArgumentOutOfRangeException("length");
				}
				return array;
			}
			if (startIndex < 0 || startIndex >= num)
			{
				throw new ArgumentOutOfRangeException("startIndex");
			}
			if (length < 0 || length > num - startIndex)
			{
				throw new ArgumentOutOfRangeException("length");
			}
			if (length == 0)
			{
				return new T[0];
			}
			if (length == num)
			{
				return array;
			}
			T[] array2 = new T[length];
			Array.Copy(array, startIndex, array2, 0, length);
			return array2;
		}

		public static T[] SubArray<T>(this T[] array, long startIndex, long length)
		{
			if (array == null)
			{
				throw new ArgumentNullException("array");
			}
			long num = array.LongLength;
			if (num == 0)
			{
				if (startIndex != 0)
				{
					throw new ArgumentOutOfRangeException("startIndex");
				}
				if (length != 0)
				{
					throw new ArgumentOutOfRangeException("length");
				}
				return array;
			}
			if (startIndex < 0 || startIndex >= num)
			{
				throw new ArgumentOutOfRangeException("startIndex");
			}
			if (length < 0 || length > num - startIndex)
			{
				throw new ArgumentOutOfRangeException("length");
			}
			if (length == 0)
			{
				return new T[0];
			}
			if (length == num)
			{
				return array;
			}
			T[] array2 = new T[length];
			Array.Copy(array, startIndex, array2, 0L, length);
			return array2;
		}

		public static void Times(this int n, Action action)
		{
			if (n > 0 && action != null)
			{
				for (int i = 0; i < n; i++)
				{
					action();
				}
			}
		}

		public static void Times(this long n, Action action)
		{
			if (n > 0 && action != null)
			{
				for (long num = 0L; num < n; num++)
				{
					action();
				}
			}
		}

		public static void Times(this uint n, Action action)
		{
			if (n != 0 && action != null)
			{
				for (uint num = 0u; num < n; num++)
				{
					action();
				}
			}
		}

		public static void Times(this ulong n, Action action)
		{
			if (n != 0 && action != null)
			{
				for (ulong num = 0uL; num < n; num++)
				{
					action();
				}
			}
		}

		public static void Times(this int n, Action<int> action)
		{
			if (n > 0 && action != null)
			{
				for (int i = 0; i < n; i++)
				{
					action(i);
				}
			}
		}

		public static void Times(this long n, Action<long> action)
		{
			if (n > 0 && action != null)
			{
				for (long num = 0L; num < n; num++)
				{
					action(num);
				}
			}
		}

		public static void Times(this uint n, Action<uint> action)
		{
			if (n != 0 && action != null)
			{
				for (uint num = 0u; num < n; num++)
				{
					action(num);
				}
			}
		}

		public static void Times(this ulong n, Action<ulong> action)
		{
			if (n != 0 && action != null)
			{
				for (ulong num = 0uL; num < n; num++)
				{
					action(num);
				}
			}
		}

		[Obsolete("This method will be removed.")]
		public static T To<T>(this byte[] source, ByteOrder sourceOrder) where T : struct
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (source.Length == 0)
			{
				return default(T);
			}
			Type typeFromHandle = typeof(T);
			byte[] value = source.ToHostOrder(sourceOrder);
			return ((object)typeFromHandle == typeof(bool)) ? ((T)(object)BitConverter.ToBoolean(value, 0)) : (((object)typeFromHandle == typeof(char)) ? ((T)(object)BitConverter.ToChar(value, 0)) : (((object)typeFromHandle == typeof(double)) ? ((T)(object)BitConverter.ToDouble(value, 0)) : (((object)typeFromHandle == typeof(short)) ? ((T)(object)BitConverter.ToInt16(value, 0)) : (((object)typeFromHandle == typeof(int)) ? ((T)(object)BitConverter.ToInt32(value, 0)) : (((object)typeFromHandle == typeof(long)) ? ((T)(object)BitConverter.ToInt64(value, 0)) : (((object)typeFromHandle == typeof(float)) ? ((T)(object)BitConverter.ToSingle(value, 0)) : (((object)typeFromHandle == typeof(ushort)) ? ((T)(object)BitConverter.ToUInt16(value, 0)) : (((object)typeFromHandle == typeof(uint)) ? ((T)(object)BitConverter.ToUInt32(value, 0)) : (((object)typeFromHandle == typeof(ulong)) ? ((T)(object)BitConverter.ToUInt64(value, 0)) : default(T))))))))));
		}

		[Obsolete("This method will be removed.")]
		public static byte[] ToByteArray<T>(this T value, ByteOrder order) where T : struct
		{
			Type typeFromHandle = typeof(T);
			byte[] array = (((object)typeFromHandle == typeof(bool)) ? BitConverter.GetBytes((bool)(object)value) : (((object)typeFromHandle != typeof(byte)) ? (((object)typeFromHandle == typeof(char)) ? BitConverter.GetBytes((char)(object)value) : (((object)typeFromHandle == typeof(double)) ? BitConverter.GetBytes((double)(object)value) : (((object)typeFromHandle == typeof(short)) ? BitConverter.GetBytes((short)(object)value) : (((object)typeFromHandle == typeof(int)) ? BitConverter.GetBytes((int)(object)value) : (((object)typeFromHandle == typeof(long)) ? BitConverter.GetBytes((long)(object)value) : (((object)typeFromHandle == typeof(float)) ? BitConverter.GetBytes((float)(object)value) : (((object)typeFromHandle == typeof(ushort)) ? BitConverter.GetBytes((ushort)(object)value) : (((object)typeFromHandle == typeof(uint)) ? BitConverter.GetBytes((uint)(object)value) : (((object)typeFromHandle == typeof(ulong)) ? BitConverter.GetBytes((ulong)(object)value) : WebSocket.EmptyBytes))))))))) : new byte[1] { (byte)(object)value }));
			if (array.Length > 1 && !order.IsHostOrder())
			{
				Array.Reverse((Array)array);
			}
			return array;
		}

		public static byte[] ToHostOrder(this byte[] source, ByteOrder sourceOrder)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (source.Length < 2)
			{
				return source;
			}
			if (sourceOrder.IsHostOrder())
			{
				return source;
			}
			return source.Reverse();
		}

		public static string ToString<T>(this T[] array, string separator)
		{
			if (array == null)
			{
				throw new ArgumentNullException("array");
			}
			int num = array.Length;
			if (num == 0)
			{
				return string.Empty;
			}
			if (separator == null)
			{
				separator = string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder(64);
			int num2 = num - 1;
			for (int i = 0; i < num2; i++)
			{
				stringBuilder.AppendFormat("{0}{1}", array[i], separator);
			}
			stringBuilder.Append(array[num2].ToString());
			return stringBuilder.ToString();
		}

		public static Uri ToUri(this string value)
		{
			Uri.TryCreate(value, value.MaybeUri() ? UriKind.Absolute : UriKind.Relative, out Uri result);
			return result;
		}

		[Obsolete("This method will be removed.")]
		public static void WriteContent(this WebSocketSharp.Net.HttpListenerResponse response, byte[] content)
		{
			if (response == null)
			{
				throw new ArgumentNullException("response");
			}
			if (content == null)
			{
				throw new ArgumentNullException("content");
			}
			long num = content.LongLength;
			if (num == 0)
			{
				response.Close();
				return;
			}
			response.ContentLength64 = num;
			Stream outputStream = response.OutputStream;
			if (num <= int.MaxValue)
			{
				outputStream.Write(content, 0, (int)num);
			}
			else
			{
				outputStream.WriteBytes(content, 1024);
			}
			outputStream.Close();
		}
	}
	public class MessageEventArgs : EventArgs
	{
		private string _data;

		private bool _dataSet;

		private Opcode _opcode;

		private byte[] _rawData;

		internal Opcode Opcode => _opcode;

		public string Data
		{
			get
			{
				setData();
				return _data;
			}
		}

		public bool IsBinary => _opcode == Opcode.Binary;

		public bool IsPing => _opcode == Opcode.Ping;

		public bool IsText => _opcode == Opcode.Text;

		public byte[] RawData
		{
			get
			{
				setData();
				return _rawData;
			}
		}

		internal MessageEventArgs(WebSocketFrame frame)
		{
			_opcode = frame.Opcode;
			_rawData = frame.PayloadData.ApplicationData;
		}

		internal MessageEventArgs(Opcode opcode, byte[] rawData)
		{
			if ((ulong)rawData.LongLength > PayloadData.MaxLength)
			{
				throw new WebSocketException(CloseStatusCode.TooBig);
			}
			_opcode = opcode;
			_rawData = rawData;
		}

		private void setData()
		{
			if (_dataSet)
			{
				return;
			}
			if (_opcode == Opcode.Binary)
			{
				_dataSet = true;
				return;
			}
			if (_rawData.TryGetUTF8DecodedString(out var s))
			{
				_data = s;
			}
			_dataSet = true;
		}
	}
	public class CloseEventArgs : EventArgs
	{
		private bool _clean;

		private PayloadData _payloadData;

		public ushort Code => _payloadData.Code;

		public string Reason => _payloadData.Reason;

		public bool WasClean => _clean;

		internal CloseEventArgs(PayloadData payloadData, bool clean)
		{
			_payloadData = payloadData;
			_clean = clean;
		}

		internal CloseEventArgs(ushort code, string reason, bool clean)
		{
			_payloadData = new PayloadData(code, reason);
			_clean = clean;
		}
	}
	public enum ByteOrder
	{
		Little,
		Big
	}
	public class ErrorEventArgs : EventArgs
	{
		private Exception _exception;

		private string _message;

		public Exception Exception => _exception;

		public string Message => _message;

		internal ErrorEventArgs(string message)
			: this(message, null)
		{
		}

		internal ErrorEventArgs(string message, Exception exception)
		{
			_message = message;
			_exception = exception;
		}
	}
	public class WebSocket : IDisposable
	{
		private AuthenticationChallenge _authChallenge;

		private string _base64Key;

		private bool _client;

		private Action _closeContext;

		private CompressionMethod _compression;

		private WebSocketContext _context;

		private WebSocketSharp.Net.CookieCollection _cookies;

		private WebSocketSharp.Net.NetworkCredential _credentials;

		private bool _emitOnPing;

		private bool _enableRedirection;

		private string _extensions;

		private bool _extensionsRequested;

		private object _forMessageEventQueue;

		private object _forPing;

		private object _forSend;

		private object _forState;

		private MemoryStream _fragmentsBuffer;

		private bool _fragmentsCompressed;

		private Opcode _fragmentsOpcode;

		private const string _guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

		private Func<WebSocketContext, string> _handshakeRequestChecker;

		private bool _ignoreExtensions;

		private bool _inContinuation;

		private volatile bool _inMessage;

		private volatile Logger _logger;

		private static readonly int _maxRetryCountForConnect;

		private Action<MessageEventArgs> _message;

		private Queue<MessageEventArgs> _messageEventQueue;

		private uint _nonceCount;

		private string _origin;

		private ManualResetEvent _pongReceived;

		private bool _preAuth;

		private string _protocol;

		private string[] _protocols;

		private bool _protocolsRequested;

		private WebSocketSharp.Net.NetworkCredential _proxyCredentials;

		private Uri _proxyUri;

		private volatile WebSocketState _readyState;

		private ManualResetEvent _receivingExited;

		private int _retryCountForConnect;

		private bool _secure;

		private ClientSslConfiguration _sslConfig;

		private Stream _stream;

		private TcpClient _tcpClient;

		private Uri _uri;

		private const string _version = "13";

		private TimeSpan _waitTime;

		internal static readonly byte[] EmptyBytes;

		internal static readonly int FragmentLength;

		internal static readonly RandomNumberGenerator RandomNumber;

		internal WebSocketSharp.Net.CookieCollection CookieCollection => _cookies;

		internal Func<WebSocketContext, string> CustomHandshakeRequestChecker
		{
			get
			{
				return _handshakeRequestChecker;
			}
			set
			{
				_handshakeRequestChecker = value;
			}
		}

		internal bool HasMessage
		{
			get
			{
				lock (_forMessageEventQueue)
				{
					return _messageEventQueue.Count > 0;
				}
			}
		}

		internal bool IgnoreExtensions
		{
			get
			{
				return _ignoreExtensions;
			}
			set
			{
				_ignoreExtensions = value;
			}
		}

		internal bool IsConnected => _readyState == WebSocketState.Open || _readyState == WebSocketState.Closing;

		public CompressionMethod Compression
		{
			get
			{
				return _compression;
			}
			set
			{
				string text = null;
				if (!_client)
				{
					text = "This instance is not a client.";
					throw new InvalidOperationException(text);
				}
				if (!canSet(out text))
				{
					_logger.Warn(text);
					return;
				}
				lock (_forState)
				{
					if (!canSet(out text))
					{
						_logger.Warn(text);
					}
					else
					{
						_compression = value;
					}
				}
			}
		}

		public IEnumerable<WebSocketSharp.Net.Cookie> Cookies
		{
			get
			{
				lock (_cookies.SyncRoot)
				{
					foreach (WebSocketSharp.Net.Cookie cookie in _cookies)
					{
						yield return cookie;
					}
				}
			}
		}

		public WebSocketSharp.Net.NetworkCredential Credentials => _credentials;

		public bool EmitOnPing
		{
			get
			{
				return _emitOnPing;
			}
			set
			{
				_emitOnPing = value;
			}
		}

		public bool EnableRedirection
		{
			get
			{
				return _enableRedirection;
			}
			set
			{
				string text = null;
				if (!_client)
				{
					text = "This instance is not a client.";
					throw new InvalidOperationException(text);
				}
				if (!canSet(out text))
				{
					_logger.Warn(text);
					return;
				}
				lock (_forState)
				{
					if (!canSet(out text))
					{
						_logger.Warn(text);
					}
					else
					{
						_enableRedirection = value;
					}
				}
			}
		}

		public string Extensions => _extensions ?? string.Empty;

		public bool IsAlive => ping(EmptyBytes);

		public bool IsSecure => _secure;

		public Logger Log
		{
			get
			{
				return _logger;
			}
			internal set
			{
				_logger = value;
			}
		}

		public string Origin
		{
			get
			{
				return _origin;
			}
			set
			{
				string text = null;
				if (!_client)
				{
					text = "This instance is not a client.";
					throw new InvalidOperationException(text);
				}
				if (!value.IsNullOrEmpty())
				{
					if (!Uri.TryCreate(value, UriKind.Absolute, out Uri result))
					{
						text = "Not an absolute URI string.";
						throw new ArgumentException(text, "value");
					}
					if (result.Segments.Length > 1)
					{
						text = "It includes the path segments.";
						throw new ArgumentException(text, "value");
					}
				}
				if (!canSet(out text))
				{
					_logger.Warn(text);
					return;
				}
				lock (_forState)
				{
					if (!canSet(out text))
					{
						_logger.Warn(text);
						return;
					}
					_origin = ((!value.IsNullOrEmpty()) ? value.TrimEnd(new char[1] { '/' }) : value);
				}
			}
		}

		public string Protocol
		{
			get
			{
				return _protocol ?? string.Empty;
			}
			internal set
			{
				_protocol = value;
			}
		}

		public WebSocketState ReadyState => _readyState;

		public ClientSslConfiguration SslConfiguration
		{
			get
			{
				if (!_client)
				{
					string text = "This instance is not a client.";
					throw new InvalidOperationException(text);
				}
				if (!_secure)
				{
					string text2 = "This instance does not use a secure connection.";
					throw new InvalidOperationException(text2);
				}
				return getSslConfiguration();
			}
		}

		public Uri Url => _client ? _uri : _context.RequestUri;

		public TimeSpan WaitTime
		{
			get
			{
				return _waitTime;
			}
			set
			{
				if (value <= TimeSpan.Zero)
				{
					throw new ArgumentOutOfRangeException("value", "Zero or less.");
				}
				if (!canSet(out var text))
				{
					_logger.Warn(text);
					return;
				}
				lock (_forState)
				{
					if (!canSet(out text))
					{
						_logger.Warn(text);
					}
					else
					{
						_waitTime = value;
					}
				}
			}
		}

		public event EventHandler<CloseEventArgs> OnClose;

		public event EventHandler<ErrorEventArgs> OnError;

		public event EventHandler<MessageEventArgs> OnMessage;

		public event EventHandler OnOpen;

		static WebSocket()
		{
			_maxRetryCountForConnect = 10;
			EmptyBytes = new byte[0];
			FragmentLength = 1016;
			RandomNumber = new RNGCryptoServiceProvider();
		}

		internal WebSocket(HttpListenerWebSocketContext context, string protocol)
		{
			_context = context;
			_protocol = protocol;
			_closeContext = context.Close;
			_logger = context.Log;
			_message = messages;
			_secure = context.IsSecureConnection;
			_stream = context.Stream;
			_waitTime = TimeSpan.FromSeconds(1.0);
			init();
		}

		internal WebSocket(TcpListenerWebSocketContext context, string protocol)
		{
			_context = context;
			_protocol = protocol;
			_closeContext = context.Close;
			_logger = context.Log;
			_message = messages;
			_secure = context.IsSecureConnection;
			_stream = context.Stream;
			_waitTime = TimeSpan.FromSeconds(1.0);
			init();
		}

		public WebSocket(string url, params string[] protocols)
		{
			if (url == null)
			{
				throw new ArgumentNullException("url");
			}
			if (url.Length == 0)
			{
				throw new ArgumentException("An empty string.", "url");
			}
			if (!url.TryCreateWebSocketUri(out _uri, out var text))
			{
				throw new ArgumentException(text, "url");
			}
			if (protocols != null && protocols.Length != 0)
			{
				if (!checkProtocols(protocols, out text))
				{
					throw new ArgumentException(text, "protocols");
				}
				_protocols = protocols;
			}
			_base64Key = CreateBase64Key();
			_client = true;
			_logger = new Logger();
			_message = messagec;
			_secure = _uri.Scheme == "wss";
			_waitTime = TimeSpan.FromSeconds(5.0);
			init();
		}

		private bool accept()
		{
			if (_readyState == WebSocketState.Open)
			{
				string text = "The handshake request has already been accepted.";
				_logger.Warn(text);
				return false;
			}
			lock (_forState)
			{
				if (_readyState == WebSocketState.Open)
				{
					string text2 = "The handshake request has already been accepted.";
					_logger.Warn(text2);
					return false;
				}
				if (_readyState == WebSocketState.Closing)
				{
					string text3 = "The close process has set in.";
					_logger.Error(text3);
					text3 = "An interruption has occurred while attempting to accept.";
					error(text3, null);
					return false;
				}
				if (_readyState == WebSocketState.Closed)
				{
					string text4 = "The connection has been closed.";
					_logger.Error(text4);
					text4 = "An interruption has occurred while attempting to accept.";
					error(text4, null);
					return false;
				}
				try
				{
					if (!acceptHandshake())
					{
						return false;
					}
				}
				catch (Exception ex)
				{
					_logger.Fatal(ex.Message);
					_logger.Debug(ex.ToString());
					string text5 = "An exception has occurred while attempting to accept.";
					fatal(text5, ex);
					return false;
				}
				_readyState = WebSocketState.Open;
				return true;
			}
		}

		private bool acceptHandshake()
		{
			_logger.Debug($"A handshake request from {_context.UserEndPoint}:\n{_context}");
			if (!checkHandshakeRequest(_context, out var text))
			{
				_logger.Error(text);
				refuseHandshake(CloseStatusCode.ProtocolError, "A handshake error has occurred while attempting to accept.");
				return false;
			}
			if (!customCheckHandshakeRequest(_context, out text))
			{
				_logger.Error(text);
				refuseHandshake(CloseStatusCode.PolicyViolation, "A handshake error has occurred while attempting to accept.");
				return false;
			}
			_base64Key = _context.Headers["Sec-WebSocket-Key"];
			if (_protocol != null)
			{
				IEnumerable<string> secWebSocketProtocols = _context.SecWebSocketProtocols;
				processSecWebSocketProtocolClientHeader(secWebSocketProtocols);
			}
			if (!_ignoreExtensions)
			{
				string value = _context.Headers["Sec-WebSocket-Extensions"];
				processSecWebSocketExtensionsClientHeader(value);
			}
			return sendHttpResponse(createHandshakeResponse());
		}

		private bool canSet(out string message)
		{
			message = null;
			if (_readyState == WebSocketState.Open)
			{
				message = "The connection has already been established.";
				return false;
			}
			if (_readyState == WebSocketState.Closing)
			{
				message = "The connection is closing.";
				return false;
			}
			return true;
		}

		private bool checkHandshakeRequest(WebSocketContext context, out string message)
		{
			message = null;
			if (!context.IsWebSocketRequest)
			{
				message = "Not a handshake request.";
				return false;
			}
			if (context.RequestUri == null)
			{
				message = "It specifies an invalid Request-URI.";
				return false;
			}
			NameValueCollection headers = context.Headers;
			string text = headers["Sec-WebSocket-Key"];
			if (text == null)
			{
				message = "It includes no Sec-WebSocket-Key header.";
				return false;
			}
			if (text.Length == 0)
			{
				message = "It includes an invalid Sec-WebSocket-Key header.";
				return false;
			}
			string text2 = headers["Sec-WebSocket-Version"];
			if (text2 == null)
			{
				message = "It includes no Sec-WebSocket-Version header.";
				return false;
			}
			if (text2 != "13")
			{
				message = "It includes an invalid Sec-WebSocket-Version header.";
				return false;
			}
			string text3 = headers["Sec-WebSocket-Protocol"];
			if (text3 != null && text3.Length == 0)
			{
				message = "It includes an invalid Sec-WebSocket-Protocol header.";
				return false;
			}
			if (!_ignoreExtensions)
			{
				string text4 = headers["Sec-WebSocket-Extensions"];
				if (text4 != null && text4.Length == 0)
				{
					message = "It includes an invalid Sec-WebSocket-Extensions header.";
					return false;
				}
			}
			return true;
		}

		private bool checkHandshakeResponse(HttpResponse response, out string message)
		{
			message = null;
			if (response.IsRedirect)
			{
				message = "Indicates the redirection.";
				return false;
			}
			if (response.IsUnauthorized)
			{
				message = "Requires the authentication.";
				return false;
			}
			if (!response.IsWebSocketResponse)
			{
				message = "Not a WebSocket handshake response.";
				return false;
			}
			NameValueCollection headers = response.Headers;
			if (!validateSecWebSocketAcceptHeader(headers["Sec-WebSocket-Accept"]))
			{
				message = "Includes no Sec-WebSocket-Accept header, or it has an invalid value.";
				return false;
			}
			if (!validateSecWebSocketProtocolServerHeader(headers["Sec-WebSocket-Protocol"]))
			{
				message = "Includes no Sec-WebSocket-Protocol header, or it has an invalid value.";
				return false;
			}
			if (!validateSecWebSocketExtensionsServerHeader(headers["Sec-WebSocket-Extensions"]))
			{
				message = "Includes an invalid Sec-WebSocket-Extensions header.";
				return false;
			}
			if (!validateSecWebSocketVersionServerHeader(headers["Sec-WebSocket-Version"]))
			{
				message = "Includes an invalid Sec-WebSocket-Version header.";
				return false;
			}
			return true;
		}

		private static bool checkProtocols(string[] protocols, out string message)
		{
			message = null;
			Func<string, bool> condition = (string protocol) => protocol.IsNullOrEmpty() || !protocol.IsToken();
			if (protocols.Contains(condition))
			{
				message = "It contains a value that is not a token.";
				return false;
			}
			if (protocols.ContainsTwice())
			{
				message = "It contains a value twice.";
				return false;
			}
			return true;
		}

		private bool checkReceivedFrame(WebSocketFrame frame, out string message)
		{
			message = null;
			bool isMasked = frame.IsMasked;
			if (_client && isMasked)
			{
				message = "A frame from the server is masked.";
				return false;
			}
			if (!_client && !isMasked)
			{
				message = "A frame from a client is not masked.";
				return false;
			}
			if (_inContinuation && frame.IsData)
			{
				message = "A data frame has been received while receiving continuation frames.";
				return false;
			}
			if (frame.IsCompressed && _compression == CompressionMethod.None)
			{
				message = "A compressed frame has been received without any agreement for it.";
				return false;
			}
			if (frame.Rsv2 == Rsv.On)
			{
				message = "The RSV2 of a frame is non-zero without any negotiation for it.";
				return false;
			}
			if (frame.Rsv3 == Rsv.On)
			{
				message = "The RSV3 of a frame is non-zero without any negotiation for it.";
				return false;
			}
			return true;
		}

		private void close(ushort code, string reason)
		{
			if (_readyState == WebSocketState.Closing)
			{
				_logger.Info("The closing is already in progress.");
				return;
			}
			if (_readyState == WebSocketState.Closed)
			{
				_logger.Info("The connection has already been closed.");
				return;
			}
			if (code == 1005)
			{
				close(PayloadData.Empty, send: true, receive: true, received: false);
				return;
			}
			bool receive = !code.IsReserved();
			close(new PayloadData(code, reason), receive, receive, received: false);
		}

		private void close(PayloadData payloadData, bool send, bool receive, bool received)
		{
			lock (_forState)
			{
				if (_readyState == WebSocketState.Closing)
				{
					_logger.Info("The closing is already in progress.");
					return;
				}
				if (_readyState == WebSocketState.Closed)
				{
					_logger.Info("The connection has already been closed.");
					return;
				}
				send = send && _readyState == WebSocketState.Open;
				receive = send && receive;
				_readyState = WebSocketState.Closing;
			}
			_logger.Trace("Begin closing the connection.");
			bool clean = closeHandshake(payloadData, send, receive, received);
			releaseResources();
			_logger.Trace("End closing the connection.");
			_readyState = WebSocketState.Closed;
			CloseEventArgs e = new CloseEventArgs(payloadData, clean);
			try
			{
				this.OnClose.Emit(this, e);
			}
			catch (Exception ex)
			{
				_logger.Error(ex.Message);
				_logger.Debug(ex.ToString());
			}
		}

		private void closeAsync(ushort code, string reason)
		{
			if (_readyState == WebSocketState.Closing)
			{
				_logger.Info("The closing is already in progress.");
				return;
			}
			if (_readyState == WebSocketState.Closed)
			{
				_logger.Info("The connection has already been closed.");
				return;
			}
			if (code == 1005)
			{
				closeAsync(PayloadData.Empty, send: true, receive: true, received: false);
				return;
			}
			bool receive = !code.IsReserved();
			closeAsync(new PayloadData(code, reason), receive, receive, received: false);
		}

		private void closeAsync(PayloadData payloadData, bool send, bool receive, bool received)
		{
			Action<PayloadData, bool, bool, bool> closer = close;
			closer.BeginInvoke(payloadData, send, receive, received, delegate(IAsyncResult ar)
			{
				closer.EndInvoke(ar);
			}, null);
		}

		private bool closeHandshake(byte[] frameAsBytes, bool receive, bool received)
		{
			bool flag = frameAsBytes != null && sendBytes(frameAsBytes);
			if (!received && flag && receive && _receivingExited != null)
			{
				received = _receivingExited.WaitOne(_waitTime);
			}
			bool flag2 = flag && received;
			_logger.Debug($"Was clean?: {flag2}\n  sent: {flag}\n  received: {received}");
			return flag2;
		}

		private bool closeHandshake(PayloadData payloadData, bool send, bool receive, bool received)
		{
			bool flag = false;
			if (send)
			{
				WebSocketFrame webSocketFrame = WebSocketFrame.CreateCloseFrame(payloadData, _client);
				flag = sendBytes(webSocketFrame.ToArray());
				if (_client)
				{
					webSocketFrame.Unmask();
				}
			}
			if (!received && flag && receive && _receivingExited != null)
			{
				received = _receivingExited.WaitOne(_waitTime);
			}
			bool flag2 = flag && received;
			_logger.Debug($"Was clean?: {flag2}\n  sent: {flag}\n  received: {received}");
			return flag2;
		}

		private bool connect()
		{
			if (_readyState == WebSocketState.Open)
			{
				string text = "The connection has already been established.";
				_logger.Warn(text);
				return false;
			}
			lock (_forState)
			{
				if (_readyState == WebSocketState.Open)
				{
					string text2 = "The connection has already been established.";
					_logger.Warn(text2);
					return false;
				}
				if (_readyState == WebSocketState.Closing)
				{
					string text3 = "The close process has set in.";
					_logger.Error(text3);
					text3 = "An interruption has occurred while attempting to connect.";
					error(text3, null);
					return false;
				}
				if (_retryCountForConnect > _maxRetryCountForConnect)
				{
					string text4 = "An opportunity for reconnecting has been lost.";
					_logger.Error(text4);
					text4 = "An interruption has occurred while attempting to connect.";
					error(text4, null);
					return false;
				}
				_readyState = WebSocketState.Connecting;
				try
				{
					doHandshake();
				}
				catch (Exception ex)
				{
					_retryCountForConnect++;
					_logger.Fatal(ex.Message);
					_logger.Debug(ex.ToString());
					string text5 = "An exception has occurred while attempting to connect.";
					fatal(text5, ex);
					return false;
				}
				_retryCountForConnect = 1;
				_readyState = WebSocketState.Open;
				return true;
			}
		}

		private string createExtensions()
		{
			StringBuilder stringBuilder = new StringBuilder(80);
			if (_compression != 0)
			{
				string arg = _compression.ToExtensionString("server_no_context_takeover", "client_no_context_takeover");
				stringBuilder.AppendFormat("{0}, ", arg);
			}
			int length = stringBuilder.Length;
			if (length > 2)
			{
				stringBuilder.Length = length - 2;
				return stringBuilder.ToString();
			}
			return null;
		}

		private HttpResponse createHandshakeFailureResponse(WebSocketSharp.Net.HttpStatusCode code)
		{
			HttpResponse httpResponse = HttpResponse.CreateCloseResponse(code);
			httpResponse.Headers["Sec-WebSocket-Version"] = "13";
			return httpResponse;
		}

		private HttpRequest createHandshakeRequest()
		{
			HttpRequest httpRequest = HttpRequest.CreateWebSocketRequest(_uri);
			NameValueCollection headers = httpRequest.Headers;
			if (!_origin.IsNullOrEmpty())
			{
				headers["Origin"] = _origin;
			}
			headers["Sec-WebSocket-Key"] = _base64Key;
			_protocolsRequested = _protocols != null;
			if (_protocolsRequested)
			{
				headers["Sec-WebSocket-Protocol"] = _protocols.ToString(", ");
			}
			_extensionsRequested = _compression != CompressionMethod.None;
			if (_extensionsRequested)
			{
				headers["Sec-WebSocket-Extensions"] = createExtensions();
			}
			headers["Sec-WebSocket-Version"] = "13";
			AuthenticationResponse authenticationResponse = null;
			if (_authChallenge != null && _credentials != null)
			{
				authenticationResponse = new AuthenticationResponse(_authChallenge, _credentials, _nonceCount);
				_nonceCount = authenticationResponse.NonceCount;
			}
			else if (_preAuth)
			{
				authenticationResponse = new AuthenticationResponse(_credentials);
			}
			if (authenticationResponse != null)
			{
				headers["Authorization"] = authenticationResponse.ToString();
			}
			if (_cookies.Count > 0)
			{
				httpRequest.SetCookies(_cookies);
			}
			return httpRequest;
		}

		private HttpResponse createHandshakeResponse()
		{
			HttpResponse httpResponse = HttpResponse.CreateWebSocketResponse();
			NameValueCollection headers = httpResponse.Headers;
			headers["Sec-WebSocket-Accept"] = CreateResponseKey(_base64Key);
			if (_protocol != null)
			{
				headers["Sec-WebSocket-Protocol"] = _protocol;
			}
			if (_extensions != null)
			{
				headers["Sec-WebSocket-Extensions"] = _extensions;
			}
			if (_cookies.Count > 0)
			{
				httpResponse.SetCookies(_cookies);
			}
			return httpResponse;
		}

		private bool customCheckHandshakeRequest(WebSocketContext context, out string message)
		{
			message = null;
			if (_handshakeRequestChecker == null)
			{
				return true;
			}
			message = _handshakeRequestChecker(context);
			return message == null;
		}

		private MessageEventArgs dequeueFromMessageEventQueue()
		{
			lock (_forMessageEventQueue)
			{
				return (_messageEventQueue.Count > 0) ? _messageEventQueue.Dequeue() : null;
			}
		}

		private void doHandshake()
		{
			setClientStream();
			HttpResponse httpResponse = sendHandshakeRequest();
			if (!checkHandshakeResponse(httpResponse, out var text))
			{
				throw new WebSocketException(CloseStatusCode.ProtocolError, text);
			}
			if (_protocolsRequested)
			{
				_protocol = httpResponse.Headers["Sec-WebSocket-Protocol"];
			}
			if (_extensionsRequested)
			{
				processSecWebSocketExtensionsServerHeader(httpResponse.Headers["Sec-WebSocket-Extensions"]);
			}
			processCookies(httpResponse.Cookies);
		}

		private void enqueueToMessageEventQueue(MessageEventArgs e)
		{
			lock (_forMessageEventQueue)
			{
				_messageEventQueue.Enqueue(e);
			}
		}

		private void error(string message, Exception exception)
		{
			try
			{
				this.OnError.Emit(this, new ErrorEventArgs(message, exception));
			}
			catch (Exception ex)
			{
				_logger.Error(ex.Message);
				_logger.Debug(ex.ToString());
			}
		}

		private void fatal(string message, Exception exception)
		{
			CloseStatusCode code = ((exception is WebSocketException) ? ((WebSocketException)exception).Code : CloseStatusCode.Abnormal);
			fatal(message, (ushort)code);
		}

		private void fatal(string message, ushort code)
		{
			PayloadData payloadData = new PayloadData(code, message);
			close(payloadData, !code.IsReserved(), receive: false, received: false);
		}

		private void fatal(string message, CloseStatusCode code)
		{
			fatal(message, (ushort)code);
		}

		private ClientSslConfiguration getSslConfiguration()
		{
			if (_sslConfig == null)
			{
				_sslConfig = new ClientSslConfiguration(_uri.DnsSafeHost);
			}
			return _sslConfig;
		}

		private void init()
		{
			_compression = CompressionMethod.None;
			_cookies = new WebSocketSharp.Net.CookieCollection();
			_forPing = new object();
			_forSend = new object();
			_forState = new object();
			_messageEventQueue = new Queue<MessageEventArgs>();
			_forMessageEventQueue = ((ICollection)_messageEventQueue).SyncRoot;
			_readyState = WebSocketState.Connecting;
		}

		private void message()
		{
			MessageEventArgs obj = null;
			lock (_forMessageEventQueue)
			{
				if (_inMessage || _messageEventQueue.Count == 0 || _readyState != WebSocketState.Open)
				{
					return;
				}
				_inMessage = true;
				obj = _messageEventQueue.Dequeue();
			}
			_message(obj);
		}

		private void messagec(MessageEventArgs e)
		{
			while (true)
			{
				try
				{
					this.OnMessage.Emit(this, e);
				}
				catch (Exception ex)
				{
					_logger.Error(ex.ToString());
					error("An error has occurred during an OnMessage event.", ex);
				}
				lock (_forMessageEventQueue)
				{
					if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open)
					{
						_inMessage = false;
						break;
					}
					e = _messageEventQueue.Dequeue();
				}
				bool flag = true;
			}
		}

		private void messages(MessageEventArgs e)
		{
			try
			{
				this.OnMessage.Emit(this, e);
			}
			catch (Exception ex)
			{
				_logger.Error(ex.ToString());
				error("An error has occurred during an OnMessage event.", ex);
			}
			lock (_forMessageEventQueue)
			{
				if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open)
				{
					_inMessage = false;
					return;
				}
				e = _messageEventQueue.Dequeue();
			}
			ThreadPool.QueueUserWorkItem(delegate
			{
				messages(e);
			});
		}

		private void open()
		{
			_inMessage = true;
			startReceiving();
			try
			{
				this.OnOpen.Emit(this, EventArgs.Empty);
			}
			catch (Exception ex)
			{
				_logger.Error(ex.ToString());
				error("An error has occurred during the OnOpen event.", ex);
			}
			MessageEventArgs obj = null;
			lock (_forMessageEventQueue)
			{
				if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open)
				{
					_inMessage = false;
					return;
				}
				obj = _messageEventQueue.Dequeue();
			}
			_message.BeginInvoke(obj, delegate(IAsyncResult ar)
			{
				_message.EndInvoke(ar);
			}, null);
		}

		private bool ping(byte[] data)
		{
			if (_readyState != WebSocketState.Open)
			{
				return false;
			}
			ManualResetEvent pongReceived = _pongReceived;
			if (pongReceived == null)
			{
				return false;
			}
			lock (_forPing)
			{
				try
				{
					pongReceived.Reset();
					if (!send(Fin.Final, Opcode.Ping, data, compressed: false))
					{
						return false;
					}
					return pongReceived.WaitOne(_waitTime);
				}
				catch (ObjectDisposedException)
				{
					return false;
				}
			}
		}

		private bool processCloseFrame(WebSocketFrame frame)
		{
			PayloadData payloadData = frame.PayloadData;
			close(payloadData, !payloadData.HasReservedCode, receive: false, received: true);
			return false;
		}

		private void processCookies(WebSocketSharp.Net.CookieCollection cookies)
		{
			if (cookies.Count != 0)
			{
				_cookies.SetOrRemove(cookies);
			}
		}

		private bool processDataFrame(WebSocketFrame frame)
		{
			enqueueToMessageEventQueue(frame.IsCompressed ? new MessageEventArgs(frame.Opcode, frame.PayloadData.ApplicationData.Decompress(_compression)) : new MessageEventArgs(frame));
			return true;
		}

		private bool processFragmentFrame(WebSocketFrame frame)
		{
			if (!_inContinuation)
			{
				if (frame.IsContinuation)
				{
					return true;
				}
				_fragmentsOpcode = frame.Opcode;
				_fragmentsCompressed = frame.IsCompressed;
				_fragmentsBuffer = new MemoryStream();
				_inContinuation = true;
			}
			_fragmentsBuffer.WriteBytes(frame.PayloadData.ApplicationData, 1024);
			if (frame.IsFinal)
			{
				using (_fragmentsBuffer)
				{
					byte[] rawData = (_fragmentsCompressed ? _fragmentsBuffer.DecompressToArray(_compression) : _fragmentsBuffer.ToArray());
					enqueueToMessageEventQueue(new MessageEventArgs(_fragmentsOpcode, rawData));
				}
				_fragmentsBuffer = null;
				_inContinuation = false;
			}
			return true;
		}

		private bool processPingFrame(WebSocketFrame frame)
		{
			_logger.Trace("A ping was received.");
			WebSocketFrame webSocketFrame = WebSocketFrame.CreatePongFrame(frame.PayloadData, _client);
			lock (_forState)
			{
				if (_readyState != WebSocketState.Open)
				{
					_logger.Error("The connection is closing.");
					return true;
				}
				if (!sendBytes(webSocketFrame.ToArray()))
				{
					return false;
				}
			}
			_logger.Trace("A pong to this ping has been sent.");
			if (_emitOnPing)
			{
				if (_client)
				{
					webSocketFrame.Unmask();
				}
				enqueueToMessageEventQueue(new MessageEventArgs(frame));
			}
			return true;
		}

		private bool processPongFrame(WebSocketFrame frame)
		{
			_logger.Trace("A pong was received.");
			try
			{
				_pongReceived.Set();
			}
			catch (NullReferenceException ex)
			{
				_logger.Error(ex.Message);
				_logger.Debug(ex.ToString());
				return false;
			}
			catch (ObjectDisposedException ex2)
			{
				_logger.Error(ex2.Message);
				_logger.Debug(ex2.ToString());
				return false;
			}
			_logger.Trace("It has been signaled.");
			return true;
		}

		private bool processReceivedFrame(WebSocketFrame frame)
		{
			if (!checkReceivedFrame(frame, out var text))
			{
				throw new WebSocketException(CloseStatusCode.ProtocolError, text);
			}
			frame.Unmask();
			return frame.IsFragment ? processFragmentFrame(frame) : (frame.IsData ? processDataFrame(frame) : (frame.IsPing ? processPingFrame(frame) : (frame.IsPong ? processPongFrame(frame) : (frame.IsClose ? processCloseFrame(frame) : processUnsupportedFrame(frame)))));
		}

		private void processSecWebSocketExtensionsClientHeader(string value)
		{
			if (value == null)
			{
				return;
			}
			StringBuilder stringBuilder = new StringBuilder(80);
			bool flag = false;
			foreach (string item in value.SplitHeaderValue(','))
			{
				string text = item.Trim();
				if (text.Length != 0 && !flag && text.IsCompressionExtension(CompressionMethod.Deflate))
				{
					_compression = CompressionMethod.Deflate;
					stringBuilder.AppendFormat("{0}, ", _compression.ToExtensionString("client_no_context_takeover", "server_no_context_takeover"));
					flag = true;
				}
			}
			int length = stringBuilder.Length;
			if (length > 2)
			{
				stringBuilder.Length = length - 2;
				_extensions = stringBuilder.ToString();
			}
		}

		private void processSecWebSocketExtensionsServerHeader(string value)
		{
			if (value == null)
			{
				_compression = CompressionMethod.None;
			}
			else
			{
				_extensions = value;
			}
		}

		private void processSecWebSocketProtocolClientHeader(IEnumerable<string> values)
		{
			if (!values.Contains((string val) => val == _protocol))
			{
				_protocol = null;
			}
		}

		private bool processUnsupportedFrame(WebSocketFrame frame)
		{
			_logger.Fatal("An unsupported frame:" + frame.PrintToString(dumped: false));
			fatal("There is no way to handle it.", CloseStatusCode.PolicyViolation);
			return false;
		}

		private void refuseHandshake(CloseStatusCode code, string reason)
		{
			_readyState = WebSocketState.Closing;
			HttpResponse response = createHandshakeFailureResponse(WebSocketSharp.Net.HttpStatusCode.BadRequest);
			sendHttpResponse(response);
			releaseServerResources();
			_readyState = WebSocketState.Closed;
			CloseEventArgs e = new CloseEventArgs((ushort)code, reason, clean: false);
			try
			{
				this.OnClose.Emit(this, e);
			}
			catch (Exception ex)
			{
				_logger.Error(ex.Message);
				_logger.Debug(ex.ToString());
			}
		}

		private void releaseClientResources()
		{
			if (_stream != null)
			{
				_stream.Dispose();
				_stream = null;
			}
			if (_tcpClient != null)
			{
				_tcpClient.Close();
				_tcpClient = null;
			}
		}

		private void releaseCommonResources()
		{
			if (_fragmentsBuffer != null)
			{
				_fragmentsBuffer.Dispose();
				_fragmentsBuffer = null;
				_inContinuation = false;
			}
			if (_pongReceived != null)
			{
				_pongReceived.Close();
				_pongReceived = null;
			}
			if (_receivingExited != null)
			{
				_receivingExited.Close();
				_receivingExited = null;
			}
		}

		private void releaseResources()
		{
			if (_client)
			{
				releaseClientResources();
			}
			else
			{
				releaseServerResources();
			}
			releaseCommonResources();
		}

		private void releaseServerResources()
		{
			if (_closeContext != null)
			{
				_closeContext();
				_closeContext = null;
				_stream = null;
				_context = null;
			}
		}

		private bool send(Opcode opcode, Stream stream)
		{
			lock (_forSend)
			{
				Stream stream2 = stream;
				bool flag = false;
				bool flag2 = false;
				try
				{
					if (_compression != 0)
					{
						stream = stream.Compress(_compression);
						flag = true;
					}
					flag2 = send(opcode, stream, flag);
					if (!flag2)
					{
						error("A send has been interrupted.", null);
					}
				}
				catch (Exception ex)
				{
					_logger.Error(ex.ToString());
					error("An error has occurred during a send.", ex);
				}
				finally
				{
					if (flag)
					{
						stream.Dispose();
					}
					stream2.Dispose();
				}
				return flag2;
			}
		}

		private bool send(Opcode opcode, Stream stream, bool compressed)
		{
			long length = stream.Length;
			if (length == 0)
			{
				return send(Fin.Final, opcode, EmptyBytes, compressed: false);
			}
			long num = length / FragmentLength;
			int num2 = (int)(length % FragmentLength);
			byte[] array = null;
			switch (num)
			{
			case 0L:
				array = new byte[num2];
				return stream.Read(array, 0, num2) == num2 && send(Fin.Final, opcode, array, compressed);
			case 1L:
				if (num2 == 0)
				{
					array = new byte[FragmentLength];
					return stream.Read(array, 0, FragmentLength) == FragmentLength && send(Fin.Final, opcode, array, compressed);
				}
				break;
			}
			array = new byte[FragmentLength];
			if (stream.Read(array, 0, FragmentLength) != FragmentLength || !send(Fin.More, opcode, array, compressed))
			{
				return false;
			}
			long num3 = ((num2 == 0) ? (num - 2) : (num - 1));
			for (long num4 = 0L; num4 < num3; num4++)
			{
				if (stream.Read(array, 0, FragmentLength) != FragmentLength || !send(Fin.More, Opcode.Cont, array, compressed: false))
				{
					return false;
				}
			}
			if (num2 == 0)
			{
				num2 = FragmentLength;
			}
			else
			{
				array = new byte[num2];
			}
			return stream.Read(array, 0, num2) == num2 && send(Fin.Final, Opcode.Cont, array, compressed: false);
		}

		private bool send(Fin fin, Opcode opcode, byte[] data, bool compressed)
		{
			lock (_forState)
			{
				if (_readyState != WebSocketState.Open)
				{
					_logger.Error("The connection is closing.");
					return false;
				}
				WebSocketFrame webSocketFrame = new WebSocketFrame(fin, opcode, data, compressed, _client);
				return sendBytes(webSocketFrame.ToArray());
			}
		}

		private void sendAsync(Opcode opcode, Stream stream, Action<bool> completed)
		{
			Func<Opcode, Stream, bool> sender = send;
			sender.BeginInvoke(opcode, stream, delegate(IAsyncResult ar)
			{
				try
				{
					bool obj = sender.EndInvoke(ar);
					if (completed != null)
					{
						completed(obj);
					}
				}
				catch (Exception ex)
				{
					_logger.Error(ex.ToString());
					error("An error has occurred during the callback for an async send.", ex);
				}
			}, null);
		}

		private bool sendBytes(byte[] bytes)
		{
			try
			{
				_stream.Write(bytes, 0, bytes.Length);
			}
			catch (Exception ex)
			{
				_logger.Error(ex.Message);
				_logger.Debug(ex.ToString());
				return false;
			}
			return true;
		}

		private HttpResponse sendHandshakeRequest()
		{
			HttpRequest httpRequest = createHandshakeRequest();
			HttpResponse httpResponse = sendHttpRequest(httpRequest, 90000);
			if (httpResponse.IsUnauthorized)
			{
				string text = httpResponse.Headers["WWW-Authenticate"];
				_logger.Warn($"Received an authentication requirement for '{text}'.");
				if (text.IsNullOrEmpty())
				{
					_logger.Error("No authentication challenge is specified.");
					return httpResponse;
				}
				_authChallenge = AuthenticationChallenge.Parse(text);
				if (_authChallenge == null)
				{
					_logger.Error("An invalid authentication challenge is specified.");
					return httpResponse;
				}
				if (_credentials != null && (!_preAuth || _authChallenge.Scheme == WebSocketSharp.Net.AuthenticationSchemes.Digest))
				{
					if (httpResponse.HasConnectionClose)
					{
						releaseClientResources();
						setClientStream();
					}
					AuthenticationResponse authenticationResponse = new AuthenticationResponse(_authChallenge, _credentials, _nonceCount);
					_nonceCount = authenticationResponse.NonceCount;
					httpRequest.Headers["Authorization"] = authenticationResponse.ToString();
					httpResponse = sendHttpRequest(httpRequest, 15000);
				}
			}
			if (httpResponse.IsRedirect)
			{
				string text2 = httpResponse.Headers["Location"];
				_logger.Warn($"Received a redirection to '{text2}'.");
				if (_enableRedirection)
				{
					if (text2.IsNullOrEmpty())
					{
						_logger.Error("No url to redirect is located.");
						return httpResponse;
					}
					if (!text2.TryCreateWebSocketUri(out var result, out var text3))
					{
						_logger.Error("An invalid url to redirect is located: " + text3);
						return httpResponse;
					}
					releaseClientResources();
					_uri = result;
					_secure = result.Scheme == "wss";
					setClientStream();
					return sendHandshakeRequest();
				}
			}
			return httpResponse;
		}

		private HttpResponse sendHttpRequest(HttpRequest request, int millisecondsTimeout)
		{
			_logger.Debug("A request to the server:\n" + request.ToString());
			HttpResponse response = request.GetResponse(_stream, millisecondsTimeout);
			_logger.Debug("A response to this request:\n" + response.ToString());
			return response;
		}

		private bool sendHttpResponse(HttpResponse response)
		{
			_logger.Debug($"A response to {_context.UserEndPoint}:\n{response}");
			return sendBytes(response.ToByteArray());
		}

		private void sendProxyConnectRequest()
		{
			HttpRequest httpRequest = HttpRequest.CreateConnectRequest(_uri);
			HttpResponse httpResponse = sendHttpRequest(httpRequest, 90000);
			if (httpResponse.IsProxyAuthenticationRequired)
			{
				string text = httpResponse.Headers["Proxy-Authenticate"];
				_logger.Warn($"Received a proxy authentication requirement for '{text}'.");
				if (text.IsNullOrEmpty())
				{
					throw new WebSocketException("No proxy authentication challenge is specified.");
				}
				AuthenticationChallenge authenticationChallenge = AuthenticationChallenge.Parse(text);
				if (authenticationChallenge == null)
				{
					throw new WebSocketException("An invalid proxy authentication challenge is specified.");
				}
				if (_proxyCredentials != null)
				{
					if (httpResponse.HasConnectionClose)
					{
						releaseClientResources();
						_tcpClient = new TcpClient(_proxyUri.DnsSafeHost, _proxyUri.Port);
						_stream = _tcpClient.GetStream();
					}
					AuthenticationResponse authenticationResponse = new AuthenticationResponse(authenticationChallenge, _proxyCredentials, 0u);
					httpRequest.Headers["Proxy-Authorization"] = authenticationResponse.ToString();
					httpResponse = sendHttpRequest(httpRequest, 15000);
				}
				if (httpResponse.IsProxyAuthenticationRequired)
				{
					throw new WebSocketException("A proxy authentication is required.");
				}
			}
			if (httpResponse.StatusCode[0] != '2')
			{
				throw new WebSocketException("The proxy has failed a connection to the requested host and port.");
			}
		}

		private void setClientStream()
		{
			if (_proxyUri != null)
			{
				_tcpClient = new TcpClient(_proxyUri.DnsSafeHost, _proxyUri.Port);
				_stream = _tcpClient.GetStream();
				sendProxyConnectRequest();
			}
			else
			{
				_tcpClient = new TcpClient(_uri.DnsSafeHost, _uri.Port);
				_stream = _tcpClient.GetStream();
			}
			if (_secure)
			{
				ClientSslConfiguration sslConfiguration = getSslConfiguration();
				string targetHost = sslConfiguration.TargetHost;
				if (targetHost != _uri.DnsSafeHost)
				{
					throw new WebSocketException(CloseStatusCode.TlsHandshakeFailure, "An invalid host name is specified.");
				}
				try
				{
					SslStream sslStream = new SslStream(_stream, leaveInnerStreamOpen: false, sslConfiguration.ServerCertificateValidationCallback, sslConfiguration.ClientCertificateSelectionCallback);
					sslStream.AuthenticateAsClient(targetHost, sslConfiguration.ClientCertificates, sslConfiguration.EnabledSslProtocols, sslConfiguration.CheckCertificateRevocation);
					_stream = sslStream;
				}
				catch (Exception innerException)
				{
					throw new WebSocketException(CloseStatusCode.TlsHandshakeFailure, innerException);
				}
			}
		}

		private void startReceiving()
		{
			if (_messageEventQueue.Count > 0)
			{
				_messageEventQueue.Clear();
			}
			_pongReceived = new ManualResetEvent(initialState: false);
			_receivingExited = new ManualResetEvent(initialState: false);
			Action receive = null;
			receive = delegate
			{
				WebSocketFrame.ReadFrameAsync(_stream, unmask: false, delegate(WebSocketFrame frame)
				{
					if (!processReceivedFrame(frame) || _readyState == WebSocketState.Closed)
					{
						_receivingExited?.Set();
					}
					else
					{
						receive();
						if (!_inMessage && HasMessage && _readyState == WebSocketState.Open)
						{
							message();
						}
					}
				}, delegate(Exception ex)
				{
					_logger.Fatal(ex.ToString());
					fatal("An exception has occurred while receiving.", ex);
				});
			};
			receive();
		}

		private bool validateSecWebSocketAcceptHeader(string value)
		{
			return value != null && value == CreateResponseKey(_base64Key);
		}

		private bool validateSecWebSocketExtensionsServerHeader(string value)
		{
			if (value == null)
			{
				return true;
			}
			if (value.Length == 0)
			{
				return false;
			}
			if (!_extensionsRequested)
			{
				return false;
			}
			bool flag = _compression != CompressionMethod.None;
			foreach (string item in value.SplitHeaderValue(','))
			{
				string text = item.Trim();
				if (flag && text.IsCompressionExtension(_compression))
				{
					if (!text.Contains("server_no_context_takeover"))
					{
						_logger.Error("The server hasn't sent back 'server_no_context_takeover'.");
						return false;
					}
					if (!text.Contains("client_no_context_takeover"))
					{
						_logger.Warn("The server hasn't sent back 'client_no_context_takeover'.");
					}
					string method = _compression.ToExtensionString();
					if (text.SplitHeaderValue(';').Contains(delegate(string t)
					{
						t = t.Trim();
						return t != method && t != "server_no_context_takeover" && t != "client_no_context_takeover";
					}))
					{
						return false;
					}
					continue;
				}
				return false;
			}
			return true;
		}

		private bool validateSecWebSocketProtocolServerHeader(string value)
		{
			if (value == null)
			{
				return !_protocolsRequested;
			}
			if (value.Length == 0)
			{
				return false;
			}
			return _protocolsRequested && _protocols.Contains((string p) => p == value);
		}

		private bool validateSecWebSocketVersionServerHeader(string value)
		{
			return value == null || value == "13";
		}

		internal void Close(HttpResponse response)
		{
			_readyState = WebSocketState.Closing;
			sendHttpResponse(response);
			releaseServerResources();
			_readyState = WebSocketState.Closed;
		}

		internal void Close(WebSocketSharp.Net.HttpStatusCode code)
		{
			Close(createHandshakeFailureResponse(code));
		}

		internal void Close(PayloadData payloadData, byte[] frameAsBytes)
		{
			lock (_forState)
			{
				if (_readyState == WebSocketState.Closing)
				{
					_logger.Info("The closing is already in progress.");
					return;
				}
				if (_readyState == WebSocketState.Closed)
				{
					_logger.Info("The connection has already been closed.");
					return;
				}
				_readyState = WebSocketState.Closing;
			}
			_logger.Trace("Begin closing the connection.");
			bool flag = frameAsBytes != null && sendBytes(frameAsBytes);
			bool flag2 = flag && _receivingExited != null && _receivingExited.WaitOne(_waitTime);
			bool flag3 = flag && flag2;
			_logger.Debug($"Was clean?: {flag3}\n  sent: {flag}\n  received: {flag2}");
			releaseServerResources();
			releaseCommonResources();
			_logger.Trace("End closing the connection.");
			_readyState = WebSocketState.Closed;
			CloseEventArgs e = new CloseEventArgs(payloadData, flag3);
			try
			{
				this.OnClose.Emit(this, e);
			}
			catch (Exception ex)
			{
				_logger.Error(ex.Message);
				_logger.Debug(ex.ToString());
			}
		}

		internal static string CreateBase64Key()
		{
			byte[] array = new byte[16];
			RandomNumber.GetBytes(array);
			return Convert.ToBase64String(array);
		}

		internal static string CreateResponseKey(string base64Key)
		{
			StringBuilder stringBuilder = new StringBuilder(base64Key, 64);
			stringBuilder.Append("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
			SHA1 sHA = new SHA1CryptoServiceProvider();
			byte[] inArray = sHA.ComputeHash(stringBuilder.ToString().GetUTF8EncodedBytes());
			return Convert.ToBase64String(inArray);
		}

		internal void InternalAccept()
		{
			try
			{
				if (!acceptHandshake())
				{
					return;
				}
			}
			catch (Exception ex)
			{
				_logger.Fatal(ex.Message);
				_logger.Debug(ex.ToString());
				string text = "An exception has occurred while attempting to accept.";
				fatal(text, ex);
				return;
			}
			_readyState = WebSocketState.Open;
			open();
		}

		internal bool Ping(byte[] frameAsBytes, TimeSpan timeout)
		{
			if (_readyState != WebSocketState.Open)
			{
				return false;
			}
			ManualResetEvent pongReceived = _pongReceived;
			if (pongReceived == null)
			{
				return false;
			}
			lock (_forPing)
			{
				try
				{
					pongReceived.Reset();
					lock (_forState)
					{
						if (_readyState != WebSocketState.Open)
						{
							return false;
						}
						if (!sendBytes(frameAsBytes))
						{
							return false;
						}
					}
					return pongReceived.WaitOne(timeout);
				}
				catch (ObjectDisposedException)
				{
					return false;
				}
			}
		}

		internal void Send(Opcode opcode, byte[] data, Dictionary<CompressionMethod, byte[]> cache)
		{
			lock (_forSend)
			{
				lock (_forState)
				{
					if (_readyState != WebSocketState.Open)
					{
						_logger.Error("The connection is closing.");
						return;
					}
					if (!cache.TryGetValue(_compression, out var value))
					{
						value = new WebSocketFrame(Fin.Final, opcode, data.Compress(_compression), _compression != CompressionMethod.None, mask: false).ToArray();
						cache.Add(_compression, value);
					}
					sendBytes(value);
				}
			}
		}

		internal void Send(Opcode opcode, Stream stream, Dictionary<CompressionMethod, Stream> cache)
		{
			lock (_forSend)
			{
				if (!cache.TryGetValue(_compression, out var value))
				{
					value = stream.Compress(_compression);
					cache.Add(_compression, value);
				}
				else
				{
					value.Position = 0L;
				}
				send(opcode, value, _compression != CompressionMethod.None);
			}
		}

		public void Accept()
		{
			if (_client)
			{
				string text = "This instance is a client.";
				throw new InvalidOperationException(text);
			}
			if (_readyState == WebSocketState.Closing)
			{
				string text2 = "The close process is in progress.";
				throw new InvalidOperationException(text2);
			}
			if (_readyState == WebSocketState.Closed)
			{
				string text3 = "The connection has already been closed.";
				throw new InvalidOperationException(text3);
			}
			if (accept())
			{
				open();
			}
		}

		public void AcceptAsync()
		{
			if (_client)
			{
				string text = "This instance is a client.";
				throw new InvalidOperationException(text);
			}
			if (_readyState == WebSocketState.Closing)
			{
				string text2 = "The close process is in progress.";
				throw new InvalidOperationException(text2);
			}
			if (_readyState == WebSocketState.Closed)
			{
				string text3 = "The connection has already been closed.";
				throw new InvalidOperationException(text3);
			}
			Func<bool> acceptor = accept;
			acceptor.BeginInvoke(delegate(IAsyncResult ar)
			{
				if (acceptor.EndInvoke(ar))
				{
					open();
				}
			}, null);
		}

		public void Close()
		{
			close(1005, string.Empty);
		}

		public void Close(ushort code)
		{
			if (!code.IsCloseStatusCode())
			{
				string text = "Less than 1000 or greater than 4999.";
				throw new ArgumentOutOfRangeException("code", text);
			}
			if (_client && code == 1011)
			{
				string text2 = "1011 cannot be used.";
				throw new ArgumentException(text2, "code");
			}
			if (!_client && code == 1010)
			{
				string text3 = "1010 cannot be used.";
				throw new ArgumentException(text3, "code");
			}
			close(code, string.Empty);
		}

		public void Close(CloseStatusCode code)
		{
			if (_client && code == CloseStatusCode.ServerError)
			{
				string text = "ServerError cannot be used.";
				throw new ArgumentException(text, "code");
			}
			if (!_client && code == CloseStatusCode.MandatoryExtension)
			{
				string text2 = "MandatoryExtension cannot be used.";
				throw new ArgumentException(text2, "code");
			}
			close((ushort)code, string.Empty);
		}

		public void Close(ushort code, string reason)
		{
			if (!code.IsCloseStatusCode())
			{
				string text = "Less than 1000 or greater than 4999.";
				throw new ArgumentOutOfRangeException("code", text);
			}
			if (_client && code == 1011)
			{
				string text2 = "1011 cannot be used.";
				throw new ArgumentException(text2, "code");
			}
			if (!_client && code == 1010)
			{
				string text3 = "1010 cannot be used.";
				throw new ArgumentException(text3, "code");
			}
			if (reason.IsNullOrEmpty())
			{
				close(code, string.Empty);
				return;
			}
			if (code == 1005)
			{
				string text4 = "1005 cannot be used.";
				throw new ArgumentException(text4, "code");
			}
			if (!reason.TryGetUTF8EncodedBytes(out var bytes))
			{
				string text5 = "It could not be UTF-8-encoded.";
				throw new ArgumentException(text5, "reason");
			}
			if (bytes.Length > 123)
			{
				string text6 = "Its size is greater than 123 bytes.";
				throw new ArgumentOutOfRangeException("reason", text6);
			}
			close(code, reason);
		}

		public void Close(CloseStatusCode code, string reason)
		{
			if (_client && code == CloseStatusCode.ServerError)
			{
				string text = "ServerError cannot be used.";
				throw new ArgumentException(text, "code");
			}
			if (!_client && code == CloseStatusCode.MandatoryExtension)
			{
				string text2 = "MandatoryExtension cannot be used.";
				throw new ArgumentException(text2, "code");
			}
			if (reason.IsNullOrEmpty())
			{
				close((ushort)code, string.Empty);
				return;
			}
			if (code == CloseStatusCode.NoStatus)
			{
				string text3 = "NoStatus cannot be used.";
				throw new ArgumentException(text3, "code");
			}
			if (!reason.TryGetUTF8EncodedBytes(out var bytes))
			{
				string text4 = "It could not be UTF-8-encoded.";
				throw new ArgumentException(text4, "reason");
			}
			if (bytes.Length > 123)
			{
				string text5 = "Its size is greater than 123 bytes.";
				throw new ArgumentOutOfRangeException("reason", text5);
			}
			close((ushort)code, reason);
		}

		public void CloseAsync()
		{
			closeAsync(1005, string.Empty);
		}

		public void CloseAsync(ushort code)
		{
			if (!code.IsCloseStatusCode())
			{
				string text = "Less than 1000 or greater than 4999.";
				throw new ArgumentOutOfRangeException("code", text);
			}
			if (_client && code == 1011)
			{
				string text2 = "1011 cannot be used.";
				throw new ArgumentException(text2, "code");
			}
			if (!_client && code == 1010)
			{
				string text3 = "1010 cannot be used.";
				throw new ArgumentException(text3, "code");
			}
			closeAsync(code, string.Empty);
		}

		public void CloseAsync(CloseStatusCode code)
		{
			if (_client && code == CloseStatusCode.ServerError)
			{
				string text = "ServerError cannot be used.";
				throw new ArgumentException(text, "code");
			}
			if (!_client && code == CloseStatusCode.MandatoryExtension)
			{
				string text2 = "MandatoryExtension cannot be used.";
				throw new ArgumentException(text2, "code");
			}
			closeAsync((ushort)code, string.Empty);
		}

		public void CloseAsync(ushort code, string reason)
		{
			if (!code.IsCloseStatusCode())
			{
				string text = "Less than 1000 or greater than 4999.";
				throw new ArgumentOutOfRangeException("code", text);
			}
			if (_client && code == 1011)
			{
				string text2 = "1011 cannot be used.";
				throw new ArgumentException(text2, "code");
			}
			if (!_client && code == 1010)
			{
				string text3 = "1010 cannot be used.";
				throw new ArgumentException(text3, "code");
			}
			if (reason.IsNullOrEmpty())
			{
				closeAsync(code, string.Empty);
				return;
			}
			if (code == 1005)
			{
				string text4 = "1005 cannot be used.";
				throw new ArgumentException(text4, "code");
			}
			if (!reason.TryGetUTF8EncodedBytes(out var bytes))
			{
				string text5 = "It could not be UTF-8-encoded.";
				throw new ArgumentException(text5, "reason");
			}
			if (bytes.Length > 123)
			{
				string text6 = "Its size is greater than 123 bytes.";
				throw new ArgumentOutOfRangeException("reason", text6);
			}
			closeAsync(code, reason);
		}

		public void CloseAsync(CloseStatusCode code, string reason)
		{
			if (_client && code == CloseStatusCode.ServerError)
			{
				string text = "ServerError cannot be used.";
				throw new ArgumentException(text, "code");
			}
			if (!_client && code == CloseStatusCode.MandatoryExtension)
			{
				string text2 = "MandatoryExtension cannot be used.";
				throw new ArgumentException(text2, "code");
			}
			if (reason.IsNullOrEmpty())
			{
				closeAsync((ushort)code, string.Empty);
				return;
			}
			if (code == CloseStatusCode.NoStatus)
			{
				string text3 = "NoStatus cannot be used.";
				throw new ArgumentException(text3, "code");
			}
			if (!reason.TryGetUTF8EncodedBytes(out var bytes))
			{
				string text4 = "It could not be UTF-8-encoded.";
				throw new ArgumentException(text4, "reason");
			}
			if (bytes.Length > 123)
			{
				string text5 = "Its size is greater than 123 bytes.";
				throw new ArgumentOutOfRangeException("reason", text5);
			}
			closeAsync((ushort)code, reason);
		}

		public void Connect()
		{
			if (!_client)
			{
				string text = "This instance is not a client.";
				throw new InvalidOperationException(text);
			}
			if (_readyState == WebSocketState.Closing)
			{
				string text2 = "The close process is in progress.";
				throw new InvalidOperationException(text2);
			}
			if (_retryCountForConnect > _maxRetryCountForConnect)
			{
				string text3 = "A series of reconnecting has failed.";
				throw new InvalidOperationException(text3);
			}
			if (connect())
			{
				open();
			}
		}

		public void ConnectAsync()
		{
			if (!_client)
			{
				string text = "This instance is not a client.";
				throw new InvalidOperationException(text);
			}
			if (_readyState == WebSocketState.Closing)
			{
				string text2 = "The close process is in progress.";
				throw new InvalidOperationException(text2);
			}
			if (_retryCountForConnect > _maxRetryCountForConnect)
			{
				string text3 = "A series of reconnecting has failed.";
				throw new InvalidOperationException(text3);
			}
			Func<bool> connector = connect;
			connector.BeginInvoke(delegate(IAsyncResult ar)
			{
				if (connector.EndInvoke(ar))
				{
					open();
				}
			}, null);
		}

		public bool Ping()
		{
			return ping(EmptyBytes);
		}

		public bool Ping(string message)
		{
			if (message.IsNullOrEmpty())
			{
				return ping(EmptyBytes);
			}
			if (!message.TryGetUTF8EncodedBytes(out var bytes))
			{
				string text = "It could not be UTF-8-encoded.";
				throw new ArgumentException(text, "message");
			}
			if (bytes.Length > 125)
			{
				string text2 = "Its size is greater than 125 bytes.";
				throw new ArgumentOutOfRangeException("message", text2);
			}
			return ping(bytes);
		}

		public void Send(byte[] data)
		{
			if (_readyState != WebSocketState.Open)
			{
				string text = "The current state of the connection is not Open.";
				throw new InvalidOperationException(text);
			}
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			send(Opcode.Binary, new MemoryStream(data));
		}

		public void Send(FileInfo fileInfo)
		{
			if (_readyState != WebSocketState.Open)
			{
				string text = "The current state of the connection is not Open.";
				throw new InvalidOperationException(text);
			}
			if (fileInfo == null)
			{
				throw new ArgumentNullException("fileInfo");
			}
			if (!fileInfo.Exists)
			{
				string text2 = "The file does not exist.";
				throw new ArgumentException(text2, "fileInfo");
			}
			if (!fileInfo.TryOpenRead(out var fileStream))
			{
				string text3 = "The file could not be opened.";
				throw new ArgumentException(text3, "fileInfo");
			}
			send(Opcode.Binary, fileStream);
		}

		public void Send(string data)
		{
			if (_readyState != WebSocketState.Open)
			{
				string text = "The current state of the connection is not Open.";
				throw new InvalidOperationException(text);
			}
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			if (!data.TryGetUTF8EncodedBytes(out var bytes))
			{
				string text2 = "It could not be UTF-8-encoded.";
				throw new ArgumentException(text2, "data");
			}
			send(Opcode.Text, new MemoryStream(bytes));
		}

		public void Send(Stream stream, int length)
		{
			if (_readyState != WebSocketState.Open)
			{
				string text = "The current state of the connection is not Open.";
				throw new InvalidOperationException(text);
			}
			if (stream == null)
			{
				throw new ArgumentNullException("stream");
			}
			if (!stream.CanRead)
			{
				string text2 = "It cannot be read.";
				throw new ArgumentException(text2, "stream");
			}
			if (length < 1)
			{
				string text3 = "Less than 1.";
				throw new ArgumentException(text3, "length");
			}
			byte[] array = stream.ReadBytes(length);
			int num = array.Length;
			if (num == 0)
			{
				string text4 = "No data could be read from it.";
				throw new ArgumentException(text4, "stream");
			}
			if (num < length)
			{
				_logger.Warn($"Only {num} byte(s) of data could be read from the stream.");
			}
			send(Opcode.Binary, new MemoryStream(array));
		}

		public void SendAsync(byte[] data, Action<bool> completed)
		{
			if (_readyState != WebSocketState.Open)
			{
				string text = "The current state of the connection is not Open.";
				throw new InvalidOperationException(text);
			}
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			sendAsync(Opcode.Binary, new MemoryStream(data), completed);
		}

		public void SendAsync(FileInfo fileInfo, Action<bool> completed)
		{
			if (_readyState != WebSocketState.Open)
			{
				string text = "The current state of the connection is not Open.";
				throw new InvalidOperationException(text);
			}
			if (fileInfo == null)
			{
				throw new ArgumentNullException("fileInfo");
			}
			if (!fileInfo.Exists)
			{
				string text2 = "The file does not exist.";
				throw new ArgumentException(text2, "fileInfo");
			}
			if (!fileInfo.TryOpenRead(out var fileStream))
			{
				string text3 = "The file could not be opened.";
				throw new ArgumentException(text3, "fileInfo");
			}
			sendAsync(Opcode.Binary, fileStream, completed);
		}

		public void SendAsync(string data, Action<bool> completed)
		{
			if (_readyState != WebSocketState.Open)
			{
				string text = "The current state of the connection is not Open.";
				throw new InvalidOperationException(text);
			}
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			if (!data.TryGetUTF8EncodedBytes(out var bytes))
			{
				string text2 = "It could not be UTF-8-encoded.";
				throw new ArgumentException(text2, "data");
			}
			sendAsync(Opcode.Text, new MemoryStream(bytes), completed);
		}

		public void SendAsync(Stream stream, int length, Action<bool> completed)
		{
			if (_readyState != WebSocketState.Open)
			{
				string text = "The current state of the connection is not Open.";
				throw new InvalidOperationException(text);
			}
			if (stream == null)
			{
				throw new ArgumentNullException("stream");
			}
			if (!stream.CanRead)
			{
				string text2 = "It cannot be read.";
				throw new ArgumentException(text2, "stream");
			}
			if (length < 1)
			{
				string text3 = "Less than 1.";
				throw new ArgumentException(text3, "length");
			}
			byte[] array = stream.ReadBytes(length);
			int num = array.Length;
			if (num == 0)
			{
				string text4 = "No data could be read from it.";
				throw new ArgumentException(text4, "stream");
			}
			if (num < length)
			{
				_logger.Warn($"Only {num} byte(s) of data could be read from the stream.");
			}
			sendAsync(Opcode.Binary, new MemoryStream(array), completed);
		}

		public void SetCookie(WebSocketSharp.Net.Cookie cookie)
		{
			string text = null;
			if (!_client)
			{
				text = "This instance is not a client.";
				throw new InvalidOperationException(text);
			}
			if (cookie == null)
			{
				throw new ArgumentNullException("cookie");
			}
			if (!canSet(out text))
			{
				_logger.Warn(text);
				return;
			}
			lock (_forState)
			{
				if (!canSet(out text))
				{
					_logger.Warn(text);
					return;
				}
				lock (_cookies.SyncRoot)
				{
					_cookies.SetOrRemove(cookie);
				}
			}
		}

		public void SetCredentials(string username, string password, bool preAuth)
		{
			string text = null;
			if (!_client)
			{
				text = "This instance is not a client.";
				throw new InvalidOperationException(text);
			}
			if (!username.IsNullOrEmpty() && (Ext.Contains(username, ':') || !username.IsText()))
			{
				text = "It contains an invalid character.";
				throw new ArgumentException(text, "username");
			}
			if (!password.IsNullOrEmpty() && !password.IsText())
			{
				text = "It contains an invalid character.";
				throw new ArgumentException(text, "password");
			}
			if (!canSet(out text))
			{
				_logger.Warn(text);
				return;
			}
			lock (_forState)
			{
				if (!canSet(out text))
				{
					_logger.Warn(text);
				}
				else if (username.IsNullOrEmpty())
				{
					_credentials = null;
					_preAuth = false;
				}
				else
				{
					_credentials = new WebSocketSharp.Net.NetworkCredential(username, password, _uri.PathAndQuery);
					_preAuth = preAuth;
				}
			}
		}

		public void SetProxy(string url, string username, string password)
		{
			string text = null;
			if (!_client)
			{
				text = "This instance is not a client.";
				throw new InvalidOperationException(text);
			}
			Uri result = null;
			if (!url.IsNullOrEmpty())
			{
				if (!Uri.TryCreate(url, UriKind.Absolute, out result))
				{
					text = "Not an absolute URI string.";
					throw new ArgumentException(text, "url");
				}
				if (result.Scheme != "http")
				{
					text = "The scheme part is not http.";
					throw new ArgumentException(text, "url");
				}
				if (result.Segments.Length > 1)
				{
					text = "It includes the path segments.";
					throw new ArgumentException(text, "url");
				}
			}
			if (!username.IsNullOrEmpty() && (Ext.Contains(username, ':') || !username.IsText()))
			{
				text = "It contains an invalid character.";
				throw new ArgumentException(text, "username");
			}
			if (!password.IsNullOrEmpty() && !password.IsText())
			{
				text = "It contains an invalid character.";
				throw new ArgumentException(text, "password");
			}
			if (!canSet(out text))
			{
				_logger.Warn(text);
				return;
			}
			lock (_forState)
			{
				if (!canSet(out text))
				{
					_logger.Warn(text);