Please disclose if your mod was created primarily using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of WebMap v2.7.1
WebMap.dll
Decompiled 2 months agousing 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.Reflection.Emit; 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 Splatform; using Steamworks; 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) 2025 Various Authors")] [assembly: AssemblyFileVersion("2.7.1.0")] [assembly: AssemblyInformationalVersion("2.7.1+ae612ee6fd5208de1f8730bb29c5dbefa079ec6e")] [assembly: AssemblyProduct("WebMap")] [assembly: AssemblyTitle("Valheim WebMap")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.7.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_011f: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) 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 += FormattableString.Invariant($"{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); } } public class ServerClient { [HarmonyPatch(typeof(ZNet), "TryGetPlayerByPlatformUserID")] public class RecognizeServerClient { private static bool Postfix(bool result, PlatformUserID platformUserID, ref PlayerInfo playerInfo) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_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) if (result) { return result; } if (platformUserID != Client.m_userInfo.m_id) { return result; } playerInfo = Client; return true; } } [HarmonyPatch(typeof(ZNet), "SendPlayerList")] public class AddExtraPlayer { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown return new CodeMatcher(instructions, (ILGenerator)null).End().MatchStartBackwards((CodeMatch[])(object)new CodeMatch[1] { new CodeMatch((OpCode?)OpCodes.Ldfld, (object)AccessTools.Field(typeof(ZNet), "m_players"), (string)null) }).Advance(-1) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Ldarg_0, (object)null) }) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Ldloc_0, (object)null) }) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(AddExtraPlayer), "AddServer", (Type[])null, (Type[])null)) }) .InstructionEnumeration(); } private static void AddServer(ZNet net, ZPackage pkg) { int pos = pkg.GetPos(); pkg.SetPos(0); if (IsExtraPlayerAdded(net, pkg.ReadInt())) { pkg.SetPos(pos); return; } pkg.SetPos(0); pkg.Write(net.m_players.Count + 1); Write(pkg); } private static bool IsExtraPlayerAdded(ZNet net, int count) { return count >= net.m_players.Count + 1; } } private static PlayerInfo? client; public static PlayerInfo Client { get { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) PlayerInfo valueOrDefault = client.GetValueOrDefault(); if (!client.HasValue) { valueOrDefault = CreatePlayerInfo(); client = valueOrDefault; return valueOrDefault; } return valueOrDefault; } } private static PlayerInfo CreatePlayerInfo() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) PlayerInfo result = default(PlayerInfo); result.m_name = "Server"; result.m_characterID = new ZDOID(ZDOMan.GetSessionID(), uint.MaxValue); result.m_userInfo = new CrossNetworkUserInfo { m_id = new PlatformUserID(ZNet.instance.m_steamPlatform, GetId()), m_displayName = "Server" }; result.m_serverAssignedDisplayName = "Server"; result.m_publicPosition = false; result.m_position = Vector3.zero; return result; } private static string GetId() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) try { CSteamID steamID = SteamGameServer.GetSteamID(); return ((object)(CSteamID)(ref steamID)).ToString(); } catch (InvalidOperationException) { return "0"; } } public static void Write(ZPackage pkg) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: 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) pkg.Write(Client.m_name); pkg.Write(Client.m_characterID); PlayerInfo val = Client; pkg.Write(((object)(PlatformUserID)(ref val.m_userInfo.m_id)).ToString()); pkg.Write(Client.m_userInfo.m_displayName); pkg.Write(Client.m_serverAssignedDisplayName); pkg.Write(false); } } [BepInPlugin("com.github.h0tw1r3.valheim.webmap", "WebMap", "2.7.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 && !string.IsNullOrEmpty(peer.m_playerName)) { instance.NotifyLeave(peer); } } } [HarmonyPatch(typeof(ZRoutedRpc), "AddPeer")] private class ZRoutedRpcAddPeerPatch { private static void Postfix(ZNetPeer peer) { if (!peer.m_server && !string.IsNullOrEmpty(peer.m_playerName)) { 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_0428: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_0500: Unknown result type (might be due to invalid IL or missing references) //IL_0507: Expected O, but got Unknown //IL_0509: Unknown result type (might be due to invalid IL or missing references) //IL_050e: Unknown result type (might be due to invalid IL or missing references) //IL_0519: Unknown result type (might be due to invalid IL or missing references) //IL_0520: Expected O, but got Unknown //IL_0541: Unknown result type (might be due to invalid IL or missing references) //IL_054d: Unknown result type (might be due to invalid IL or missing references) //IL_05b2: 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.ToUpper().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].ToLower())) { type = messageParts[1].ToLower(); 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.ToUpper().StartsWith("!UNDOPIN")) { int num5 = mapDataServer.pins.FindLastIndex((string pin) => pin.StartsWith(steamid)); if (num5 > -1) { mapDataServer.RemovePin(num5); SavePins(); } } else if (text.ToUpper().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.7.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 2 months ago
The result has been truncated due to the large size, download it to view full contents!
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);