Please disclose if any significant portion of your mod was created 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 ForgeMap v1.3.1
ForgeMap.dll
Decompiled 2 weeks 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.Concurrent; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using Steamworks; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.Playables; using WebMap.Models; using WebMap.Models.Unity; using WebMap.Util; using WebSocketSharp; using WebSocketSharp.Net; using WebSocketSharp.Server; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("WebMap.Tests")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: AssemblyCompany("ForgeServer.fr")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("(c) 2026 ForgeServer.fr; WebMap (c) 2025 Various Authors; MIT")] [assembly: AssemblyFileVersion("1.3.1.0")] [assembly: AssemblyInformationalVersion("1.3.1+323ac9a7b6ea99bddabc89aea791e3a9c604b30a")] [assembly: AssemblyProduct("ForgeMap")] [assembly: AssemblyTitle("ForgeMap, live web map for Valheim (ForgeServer.fr, based on WebMap)")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.3.1.0")] [module: UnverifiableCode] namespace WebMap { internal static class Announce { private struct Pending { public string text; public bool feed; } private const string TokenFile = "announce.token"; private static readonly Queue<Pending> pending = new Queue<Pending>(); private static string tokenCache; private static DateTime tokenStamp; public static string Token { get { try { string path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? "", "announce.token"); if (!File.Exists(path)) { return null; } DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(path); if (tokenCache == null || lastWriteTimeUtc != tokenStamp) { tokenCache = File.ReadAllText(path).Trim(); tokenStamp = lastWriteTimeUtc; } return string.IsNullOrEmpty(tokenCache) ? null : tokenCache; } catch { return null; } } } public static void Enqueue(string text, bool feed = true) { if (string.IsNullOrEmpty(text)) { return; } lock (pending) { pending.Enqueue(new Pending { text = ((text.Length > 300) ? text.Substring(0, 300) : text), feed = feed }); } } public static IEnumerator Pump() { while (true) { Pending pending = default(Pending); lock (Announce.pending) { if (Announce.pending.Count > 0) { pending = Announce.pending.Dequeue(); } } if (pending.text != null) { Send(pending.text, pending.feed); } yield return (object)new WaitForSeconds(0.5f); } } private static void Send(string text, bool feed) { try { if (ZRoutedRpc.instance == null || (Object)(object)ZNet.instance == (Object)null) { return; } ZRoutedRpc.instance.InvokeRoutedRPC(0L, "ShowMessage", new object[2] { 2, text }); int num = 0; try { num = ZNet.instance.GetPeers()?.Count ?? (-1); } catch { num = -2; } ZLog.Log((object)$"WebMap: announced (peers seen: {num}): \"{text}\""); if (!feed) { return; } try { WebMap.mapDataServer?.AddMessage(0L, 2, WebMapConfig.ANNOUNCE_NAME, text); } catch { } } catch (Exception ex) { ZLog.LogWarning((object)("WebMap: announce failed: " + ex)); } } } internal static class Auth { internal sealed class Session { public string Id; public string Name; public string Avatar; public string Perms; public bool Owner; public List<string> Roles = new List<string>(); } internal const string Broker = "https://valheim-proxy.hunterjsb.workers.dev"; internal const string BrokerKey = "vXDqIj4PivgOvGg/uNIisLfE2tMp1iF/TtLHn6Y2VwLmygV2LoO/G1P7hUsuRMjJKz+fWPZir8m255XlDdE1Xs1Lzj+a74fenDx6C3Db02Y2qgCHDik2vOKblbEsu1jkHc53PqERO7SO1vcKeimHugtCulJKWhKj1EfjACNVLNunu3pfaXC4PwZ+Y+w7Bww6g1XwCgS59echk38CxyiF+GqXEEHoMI+3yJQ08GVOMaHedRX3cZC4n3aR7M0e7K4rI/XMuHhxuRqSJS8AUj+jKGd2vAG2C/nSyRVgQ/+dGmkYS7iyoMU2A50fxGTYtwLh4akMLJ3IVS9eg6tT8v4WYw==.AQAB"; private static readonly Regex TokenShape = new Regex("^([A-Za-z0-9_-]+)\\.([A-Za-z0-9_-]+)$"); private static readonly Regex Quoted = new Regex("\"((?:[^\"\\\\]|\\\\.)*)\""); internal static RSAParameters? Key(string text) { string[] array = (text ?? "").Trim().Split(new char[1] { '.' }); if (array.Length != 2) { return null; } byte[] array2 = B64(array[0]); byte[] array3 = B64(array[1]); if (array2 == null || array3 == null || array3.Length == 0) { return null; } int i; for (i = 0; i < array2.Length - 1 && array2[i] == 0; i++) { } if (array2.Length - i < 256) { return null; } byte[] array4 = new byte[array2.Length - i]; Array.Copy(array2, i, array4, 0, array4.Length); return new RSAParameters { Modulus = array4, Exponent = array3 }; } internal static Session Verify(string token, string key, string guild, ICollection<string> origins, long now, long signedOutBefore = 0L) { Match match = TokenShape.Match(token ?? ""); RSAParameters? rSAParameters = Key(key); if (!match.Success || !rSAParameters.HasValue || string.IsNullOrEmpty(guild)) { return null; } string value = match.Groups[1].Value; string text; try { byte[] array = B64(match.Groups[2].Value); if (array == null) { return null; } using (RSACryptoServiceProvider rSACryptoServiceProvider = new RSACryptoServiceProvider()) { rSACryptoServiceProvider.ImportParameters(rSAParameters.Value); if (!rSACryptoServiceProvider.VerifyData(Encoding.ASCII.GetBytes(value), CryptoConfig.MapNameToOID("SHA256"), array)) { return null; } } text = Encoding.UTF8.GetString(B64(value)); } catch { return null; } if (!(Body.Num(text, "exp") > (double)now)) { return null; } if (!(Body.Num(text, "iat") >= (double)signedOutBefore)) { return null; } if (Body.Str(text, "guild") != guild) { return null; } string text2 = Origin(Body.Str(text, "aud")); if (text2 == null || !origins.Contains(text2)) { return null; } Session session = new Session { Id = Body.Str(text, "id"), Name = (Body.Str(text, "name") ?? ""), Avatar = Body.Str(text, "avatar"), Perms = Body.Str(text, "perms"), Owner = Body.Bool(text, "owner") }; if (string.IsNullOrEmpty(session.Id)) { return null; } foreach (Match item in Quoted.Matches(Body.Arr(text, "roles") ?? "")) { session.Roles.Add(item.Groups[1].Value); } return session; } internal static bool IsAdmin(bool owner, string perms, IList<string> roles, string adminRole) { if (owner) { return true; } if (!string.IsNullOrEmpty(perms) && Regex.IsMatch(perms, "^\\d+$") && (int.Parse((perms.Length > 4) ? perms.Substring(perms.Length - 4) : perms, CultureInfo.InvariantCulture) & 8) != 0) { return true; } if (!string.IsNullOrEmpty(adminRole) && roles != null) { return roles.Contains(adminRole); } return false; } internal static string Origin(string url) { if (string.IsNullOrEmpty(url) || !Uri.TryCreate(url, UriKind.Absolute, out Uri result)) { return null; } if ((result.Scheme != "http" && result.Scheme != "https") || result.UserInfo.Length > 0 || result.Host.Length == 0) { return null; } return result.Scheme + "://" + result.Host.ToLowerInvariant() + (result.IsDefaultPort ? "" : (":" + result.Port.ToString(CultureInfo.InvariantCulture))); } internal static List<string> Origins(string host, IPAddress peer, string publicUrl, string publicIp, int port) { List<string> list = new List<string>(); Add(Origin(publicUrl)); if (IPAddress.TryParse(publicIp ?? "", out IPAddress address)) { Add(Origin("http://" + ((address.AddressFamily == AddressFamily.InterNetworkV6) ? ("[" + address?.ToString() + "]") : address.ToString()) + ":" + port.ToString(CultureInfo.InvariantCulture))); } string text = (string.IsNullOrEmpty(host) ? null : Origin("http://" + host)); if (text != null && Private(peer) && Uri.TryCreate(text, UriKind.Absolute, out Uri result) && Local(result.Host)) { Add(text); } return list; void Add(string o) { if (o != null && !list.Contains(o)) { list.Add(o); } } } internal static bool Private(IPAddress a) { if (a == null) { return false; } if (a.IsIPv4MappedToIPv6) { a = a.MapToIPv4(); } if (IPAddress.IsLoopback(a)) { return true; } byte[] addressBytes = a.GetAddressBytes(); if (a.AddressFamily == AddressFamily.InterNetwork) { if (addressBytes[0] != 10 && (addressBytes[0] != 172 || (addressBytes[1] & 0xF0) != 16)) { if (addressBytes[0] == 192) { return addressBytes[1] == 168; } return false; } return true; } if (a.AddressFamily == AddressFamily.InterNetworkV6) { return (addressBytes[0] & 0xFE) == 252; } return false; } private static bool Local(string host) { if (!(host == "localhost")) { if (IPAddress.TryParse(host.Trim('[', ']'), out IPAddress address)) { return Private(address); } return false; } return true; } internal static bool Same(string secret, string given) { if (string.IsNullOrEmpty(secret) || given == null) { return false; } int num = secret.Length ^ given.Length; for (int i = 0; i < secret.Length; i++) { num |= secret[i] ^ ((given.Length > 0) ? given[i % given.Length] : '\0'); } return num == 0; } internal static string LoginUrl(string broker, string to, string guild, ICollection<string> origins) { string text = Origin(to); if (text == null || to.Length > 2000 || !origins.Contains(text) || string.IsNullOrEmpty(guild)) { return null; } return (string.IsNullOrEmpty(broker) ? "https://valheim-proxy.hunterjsb.workers.dev" : broker).TrimEnd(new char[1] { '/' }) + "/auth/login?to=" + Uri.EscapeDataString(to) + "&guild=" + Uri.EscapeDataString(guild) + "&aud=" + Uri.EscapeDataString(text); } private static byte[] B64(string s) { s = s.Replace('-', '+').Replace('_', '/'); s += new string('=', (4 - s.Length % 4) % 4); try { return Convert.FromBase64String(s); } catch (FormatException) { return null; } } } internal static class Body { public static string Str(string body, string key) { Match match = Regex.Match(body ?? "", "\"" + key + "\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\""); if (!match.Success) { return null; } return Regex.Replace(match.Groups[1].Value, "\\\\(u[0-9a-fA-F]{4}|.)", delegate(Match mm) { string value = mm.Groups[1].Value; return (value[0] == 'u') ? ((char)Convert.ToInt32(value.Substring(1), 16)).ToString() : (value switch { "n" => "\n", "t" => "\t", "r" => "\r", "b" => "\b", "f" => "\f", _ => value, }); }); } public static double Num(string body, string key) { Match match = Regex.Match(body ?? "", "\"" + key + "\"\\s*:\\s*(-?\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?)"); if (!match.Success || !double.TryParse(match.Groups[1].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return double.NaN; } return result; } public static bool Has(string body, string key) { return Regex.IsMatch(body ?? "", "\"" + key + "\"\\s*:"); } public static bool Bool(string body, string key) { return Regex.IsMatch(body ?? "", "\"" + key + "\"\\s*:\\s*true"); } public static List<string> Items(string json) { List<string> list = new List<string>(); json = (json ?? "").Trim(); if (json.Length < 2 || json[0] != '[') { return list; } int num = 0; int num2 = -1; bool flag = false; bool flag2 = false; for (int i = 1; i < json.Length - 1; i++) { char c = json[i]; if (flag2) { flag2 = false; } else if (c == '\\' && flag) { flag2 = true; } else if (c == '"') { flag = !flag; } else { if (flag) { continue; } switch (c) { case '[': case '{': if (num == 0) { num2 = i; } num++; break; case ']': case '}': num--; if (num == 0 && num2 >= 0) { list.Add(json.Substring(num2, i - num2 + 1)); num2 = -1; } break; } } } return list; } public static string Arr(string json, string key) { Match match = Regex.Match(json ?? "", "\"" + key + "\"\\s*:\\s*\\["); if (!match.Success) { return null; } int num = match.Index + match.Length - 1; int num2 = 0; bool flag = false; bool flag2 = false; for (int i = num; i < json.Length; i++) { char c = json[i]; if (flag2) { flag2 = false; } else if (c == '\\' && flag) { flag2 = true; } else if (c == '"') { flag = !flag; } else { if (flag) { continue; } switch (c) { case '[': num2++; break; case ']': num2--; if (num2 == 0) { return json.Substring(num, i - num + 1); } break; } } } return null; } public static string Obj(string json, string key) { Match match = Regex.Match(json ?? "", "\"" + key + "\"\\s*:\\s*\\{"); if (!match.Success) { return null; } int num = match.Index + match.Length - 1; int num2 = 0; bool flag = false; bool flag2 = false; for (int i = num; i < json.Length; i++) { char c = json[i]; if (flag2) { flag2 = false; } else if (c == '\\' && flag) { flag2 = true; } else if (c == '"') { flag = !flag; } else { if (flag) { continue; } switch (c) { case '{': num2++; break; case '}': num2--; if (num2 == 0) { return json.Substring(num, i - num + 1); } break; } } } return null; } } internal static class Chart { private const string FileName = "chart.v2.png"; private static volatile byte[] png; private static volatile bool building; public static volatile int Rev; private static readonly Color32 Water = new Color32((byte)43, (byte)79, (byte)122, byte.MaxValue); private const int Classes = 33; private static Color32 Albedo(Biome b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected I4, but got Unknown //IL_00d4: 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_0036: Invalid comparison between Unknown and I4 //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0098: 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_001f: Invalid comparison between Unknown and I4 //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Invalid comparison between Unknown and I4 //IL_006a: 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_0024: Invalid comparison between Unknown and I4 //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) if ((int)b <= 16) { switch (b - 1) { default: if ((int)b != 8) { if ((int)b != 16) { break; } return new Color32((byte)231, (byte)171, (byte)120, byte.MaxValue); } return new Color32((byte)107, (byte)116, (byte)63, byte.MaxValue); case 0: return new Color32((byte)146, (byte)167, (byte)92, byte.MaxValue); case 1: return new Color32((byte)163, (byte)114, (byte)88, byte.MaxValue); case 3: return new Color32((byte)236, (byte)238, (byte)240, byte.MaxValue); case 2: break; } } else { if ((int)b == 32) { return new Color32((byte)176, (byte)49, (byte)49, byte.MaxValue); } if ((int)b == 64) { return new Color32((byte)230, (byte)236, (byte)242, byte.MaxValue); } if ((int)b == 512) { return new Color32((byte)92, (byte)56, (byte)102, byte.MaxValue); } } return Water; } public static void Load(string worldDataPath) { png = null; string path = Path.Combine(worldDataPath, "chart.v2.png"); try { if (File.Exists(path)) { Rev = Fnv.Of(png = File.ReadAllBytes(path)); } } catch { } try { string path2 = Path.Combine(worldDataPath, "chart.png"); if (File.Exists(path2)) { File.Delete(path2); } } catch { } if (png == null) { StaticCoroutine.Start(Build(worldDataPath)); } } private static IEnumerator Build(string worldDataPath) { if (building) { yield break; } building = true; int size = WebMapConfig.TEXTURE_SIZE; int half = size / 2; float ps = WebMapConfig.PIXEL_SIZE; float halfPx = ps / 2f; byte[] cls = new byte[size * size]; float water = ZoneSystem.instance.m_waterLevel; Color val = default(Color); for (int y = 0; y < size; y++) { yield return null; float num = (float)(y - half) * ps + halfPx; for (int i = 0; i < size; i++) { float num2 = (float)(i - half) * ps + halfPx; Biome biome = WorldGenerator.instance.GetBiome(num2, num, 0.02f, false); float biomeHeight = WorldGenerator.instance.GetBiomeHeight(biome, num2, num, ref val, false, true); cls[y * size + i] = (byte)((!(biomeHeight < water)) ? ((byte)(Index(biome) + 1)) : 0); } } ThreadPool.QueueUserWorkItem(delegate { //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) try { byte[] array = new byte[cls.Length]; int[] array2 = new int[33]; for (int j = 0; j < size; j++) { for (int k = 0; k < size; k++) { Array.Clear(array2, 0, array2.Length); for (int l = -1; l <= 1; l++) { int num3 = j + l; if (num3 >= 0 && num3 < size) { for (int m = -1; m <= 1; m++) { int num4 = k + m; if (num4 >= 0 && num4 < size) { array2[cls[num3 * size + num4]]++; } } } } int num5 = cls[j * size + k]; for (int n = 0; n < 33; n++) { if (array2[n] > array2[num5]) { num5 = n; } } array[j * size + k] = (byte)num5; } } byte[] array3 = new byte[size * size * 4]; for (int num6 = 0; num6 < array.Length; num6++) { Color32 val2 = ((array[num6] == 0) ? Water : Albedo(BiomeAt(array[num6] - 1))); int num7 = num6 * 4; array3[num7] = val2.r; array3[num7 + 1] = val2.g; array3[num7 + 2] = val2.b; array3[num7 + 3] = byte.MaxValue; } byte[] array4 = (png = ImageConv.EncodeRgbaToPNG(array3, size, size)); Rev = Fnv.Of(array4); File.WriteAllBytes(Path.Combine(worldDataPath, "chart.v2.png"), array4); ZLog.Log((object)$"WebMap: chart built, {array4.Length} bytes"); } catch (Exception ex) { ZLog.LogWarning((object)("WebMap: chart not written: " + ex.Message)); } finally { building = false; } }); } private static int Index(Biome b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Expected I4, but got Unknown int num = (int)b; int num2 = 0; while (num > 1 && num2 < 31) { num >>= 1; num2++; } return num2; } private static Biome BiomeAt(int index) { return (Biome)(1 << index); } public static byte[] GetPng() { return png ?? new byte[0]; } } internal static class ChatCommands { public enum Kind { None, Pin, Undo, Delete } public struct Command { public Kind kind; public string type; public string text; } public const int MaxText = 20; private static readonly string[] PinWords = new string[2] { "!pin", "!repere" }; private static readonly string[] UndoWords = new string[2] { "!undopin", "!annulerrepere" }; private static readonly string[] DeleteWords = new string[2] { "!deletepin", "!supprrepere" }; private static readonly string[,] FrenchTypes = new string[4, 2] { { "point", "dot" }, { "feu", "fire" }, { "maison", "house" }, { "grotte", "cave" } }; private static readonly Regex NotLabel = new Regex("[^\\p{L}\\p{N} \\-]"); public static Command Parse(string message) { Command result = new Command { kind = Kind.None, type = "dot", text = "" }; message = (message ?? "").Trim(); if (message.Length < 2 || message[0] != '!') { return result; } string[] array = message.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); string value = Fold(array[0]); if (Array.IndexOf(PinWords, value) >= 0) { result.kind = Kind.Pin; } else if (Array.IndexOf(UndoWords, value) >= 0) { result.kind = Kind.Undo; } else { if (Array.IndexOf(DeleteWords, value) < 0) { return result; } result.kind = Kind.Delete; } int num = 1; if (result.kind == Kind.Pin && array.Length > 1) { string text = PinType(array[1]); if (text != null) { result.type = text; num = 2; } } if (result.kind != Kind.Undo && num < array.Length) { result.text = CleanText(string.Join(" ", array, num, array.Length - num)); } return result; } public static string PinType(string word) { string text = Fold(word); if (Array.IndexOf(WebMap.ALLOWED_PINS, text) >= 0) { return text; } for (int i = 0; i < FrenchTypes.GetLength(0); i++) { if (FrenchTypes[i, 0] == text) { return FrenchTypes[i, 1]; } } return null; } public static string CleanText(string text) { text = text ?? ""; if (text.Length > 20) { text = text.Substring(0, 20); } return NotLabel.Replace(text, "").Trim(); } public static string Fold(string s) { if (string.IsNullOrEmpty(s)) { return ""; } string text = s.Normalize(NormalizationForm.FormD); StringBuilder stringBuilder = new StringBuilder(text.Length); string text2 = text; foreach (char c in text2) { if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark) { stringBuilder.Append(c); } } return stringBuilder.ToString().ToLowerInvariant(); } } internal static class ChatRelay { private const int PollMs = 3000; internal const int Cap = 5; private static Timer timer; private static int polling; private static string afterId; private static string seededChannel; private static int warned; public static void Start() { if (timer != null) { return; } timer = new Timer(delegate { if (Interlocked.CompareExchange(ref polling, 1, 0) != 0) { return; } try { Poll(); } catch (Exception ex) { WarnOnce(ex); } finally { polling = 0; } }, null, 3000, 3000); } private static void Poll() { string dISCORD_CHAT_CHANNEL = WebMapConfig.DISCORD_CHAT_CHANNEL; if (string.IsNullOrEmpty(WebMapConfig.DISCORD_BOT_TOKEN) || string.IsNullOrEmpty(dISCORD_CHAT_CHANNEL) || !WebMapConfig.CHAT_RELAY) { seededChannel = null; return; } if (seededChannel != dISCORD_CHAT_CHANNEL || afterId == null) { afterId = Discord.LatestMessageId(dISCORD_CHAT_CHANNEL); seededChannel = dISCORD_CHAT_CHANNEL; return; } List<DiscordMessage> list = Discord.MessagesAfter(dISCORD_CHAT_CHANNEL, afterId, 50); if (list.Count == 0) { return; } afterId = Newest(list, afterId); foreach (string item in LinesToSpeak(list)) { Announce.Enqueue(item); } } internal static string Newest(List<DiscordMessage> msgs, string current) { ulong result = 0uL; string result2 = current; ulong.TryParse(current ?? "", out result); foreach (DiscordMessage msg in msgs) { if (ulong.TryParse(msg.Id ?? "", out var result3) && result3 > result) { result = result3; result2 = msg.Id; } } return result2; } internal static List<string> LinesToSpeak(List<DiscordMessage> newestFirst) { List<string> list = new List<string>(); int num = newestFirst.Count - 1; while (num >= 0 && list.Count < 5) { DiscordMessage discordMessage = newestFirst[num]; if (!discordMessage.Bot && !discordMessage.Webhook && !string.IsNullOrEmpty(discordMessage.Content)) { list.Add("[Discord] " + discordMessage.DisplayName + ": " + discordMessage.Content); } num--; } return list; } private static void WarnOnce(Exception ex) { if (Interlocked.CompareExchange(ref warned, 1, 0) == 0) { ZLog.LogWarning((object)("WebMap: discord chat relay failed, will keep trying quietly: " + ex.Message)); } } } internal static class WebMapConfig { public static int TEXTURE_SIZE = 2048; public static int PIXEL_SIZE = 12; public static int RENDER_SIZE = 2048; 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 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 bool SHOW_VEHICLES = true; public static string ANNOUNCE_NAME = "Server"; public static string DISCORD_WEBHOOK = ""; public static string DISCORD_BOT_TOKEN = ""; public static string DISCORD_GUILD = ""; public static string DISCORD_LOG_CHANNEL = ""; public static string DISCORD_CHAT_CHANNEL = ""; public static bool CHAT_RELAY = true; public static string DISCORD_ADMIN_ROLE = ""; public static string PUBLIC_URL = ""; public static string AUTH_URL = ""; public static string AUTH_PUBLIC_KEY = ""; public static long SIGN_OUT_BEFORE = 0L; public static string URL = ""; public static bool EXPORT_MODELS = true; public static bool EXTRACT_TEXTURES = true; public static bool EXTRACT_MESHES = true; public static int EXPORT_MS_PER_FRAME = 6; public static int TEXTURE_MAX_SIZE = 512; public static string OBJECT_CATEGORIES = "piece,other,rock,bush,tree"; public static bool LIGHT_MODE = false; public static readonly string[] LightCategories = new string[2] { "piece", "other" }; 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; RENDER_SIZE = config.Bind<int>("Texture", "render_size", RENDER_SIZE, "Pixels across the world render (the terrain picture). Covers the same area as texture_size, just sharper: 4096 halves the metres per pixel. Costs a one-time render and a bigger download; the overlays stay at texture_size, where extra resolution buys nothing.").Value; if (RENDER_SIZE < TEXTURE_SIZE) { RENDER_SIZE = TEXTURE_SIZE; } 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, "Kept for front-ends of your own; the bundled viewer ignores it.").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, "Extra log lines: fog saves, routed RPC hashes, chat and pin diagnostics.").Value; SHOW_VEHICLES = config.Bind<bool>("Server", "show_vehicles", SHOW_VEHICLES, "Report boats and carts at /vehicles. They are only ever reported in territory players have already explored, but turning this off stops the endpoint reporting anything at all.").Value; DISCORD_WEBHOOK = config.Bind<string>("Server", "discord_webhook", DISCORD_WEBHOOK, "Discord webhook that gets the server coming on and off line, joins and leaves.").Value; DISCORD_BOT_TOKEN = config.Bind<string>("Server", "discord_bot_token", DISCORD_BOT_TOKEN, "Discord bot token (Bot ...). Empty turns off the audit log, the chat relay and the settings picker; the webhook above keeps working either way.").Value; DISCORD_GUILD = config.Bind<string>("Server", "discord_guild", DISCORD_GUILD, "Your Discord server's id (Developer Mode on, right-click the server, Copy Server ID). Its members can sign in to the map, and its owner and administrators are the map's admins; the settings picker lists its channels. Empty: no sign-in.").Value; DISCORD_ADMIN_ROLE = config.Bind<string>("Server", "discord_admin_role", DISCORD_ADMIN_ROLE, "A role id whose members are the map's admins too, beside the server's owner and administrators.").Value; PUBLIC_URL = config.Bind<string>("Server", "public_url", PUBLIC_URL, "The map's address when visitors reach it through a proxy or a name (https://map.example.com). Empty: whatever address it is visited at.").Value; AUTH_URL = config.Bind<string>("Server", "auth_url", AUTH_URL, "The sign-in service. Empty: the project's own, https://valheim-proxy.hunterjsb.workers.dev. Only for running your own.").Value; AUTH_PUBLIC_KEY = config.Bind<string>("Server", "auth_public_key", AUTH_PUBLIC_KEY, "The public key that service signs sessions with, as <modulus base64>.<exponent base64>. Empty: the project's own.").Value; if (AUTH_PUBLIC_KEY.Length > 0 && !Auth.Key(AUTH_PUBLIC_KEY).HasValue) { ZLog.LogWarning((object)"WebMap: auth_public_key is not an RSA key of 2048 bits or more; nobody can sign in"); } SIGN_OUT_BEFORE = config.Bind<long>("Server", "sign_out_before", SIGN_OUT_BEFORE, "Unix seconds: sessions signed in before this are refused, so everyone signs in again. The settings page's Sign everyone out sets it to now.").Value; DISCORD_LOG_CHANNEL = config.Bind<string>("Server", "discord_log_channel", DISCORD_LOG_CHANNEL, "Channel that gets what people do on the site: names, pins, settings changes.").Value; DISCORD_CHAT_CHANNEL = config.Bind<string>("Server", "discord_chat_channel", DISCORD_CHAT_CHANNEL, "Channel relayed both ways with in-game chat.").Value; CHAT_RELAY = config.Bind<bool>("Server", "chat_relay", CHAT_RELAY, "Relay chat between the game and discord_chat_channel.").Value; URL = config.Bind<string>("Server", "webmap_url", URL, "URL to view the web map.").Value; EXPORT_MODELS = config.Bind<bool>("Models", "export_models", EXPORT_MODELS, "Export each prefab the world holds as a glTF model for the 3D view, a few a frame, into map_data/models. Once per prefab; kept across restarts.").Value; EXTRACT_TEXTURES = config.Bind<bool>("Models", "extract_textures", EXTRACT_TEXTURES, "Read the models' textures out of the game's own asset files on a background thread, once per game version (the dedicated server cannot read them any other way). Off: flat colours.").Value; EXTRACT_MESHES = config.Bind<bool>("Models", "extract_meshes", EXTRACT_MESHES, "Read the meshes the engine keeps locked (most of them) out of the game's asset files the same way. Off: those models are left out or drawn as boxes.").Value; EXPORT_MS_PER_FRAME = config.Bind<int>("Models", "export_ms_per_frame", EXPORT_MS_PER_FRAME, "Game-thread milliseconds a frame the model export may take.").Value; TEXTURE_MAX_SIZE = config.Bind<int>("Models", "texture_max_size", TEXTURE_MAX_SIZE, "Longest edge of an extracted texture, in pixels.").Value; OBJECT_CATEGORIES = config.Bind<string>("Models", "object_categories", OBJECT_CATEGORIES, "What the 3D view is sent, of piece, other, rock, bush, tree (comma separated).").Value; LIGHT_MODE = config.Bind<bool>("Performance", "light_mode", LIGHT_MODE, "FR : mode léger pour les petits serveurs (2 à 4 Go). Coupe l'export des modèles 3D et la lecture des textures et maillages du jeu, n'envoie à la vue 3D que les constructions et les lieux (ni arbres, ni rochers, ni buissons), garde moins de terrain 3D en mémoire, limite render_size à texture_size et ne balaie le monde que toutes les 5 minutes. La carte 2D reste complète. EN: light mode for small servers (2-4 GB). Turns off the 3D model export and reading the game's textures and meshes, sends the 3D view buildings and locations only (no trees, rocks or bushes), keeps less 3D ground in memory, caps render_size at texture_size and sweeps the world every 5 minutes instead of every minute. The 2D map stays complete.").Value; } public static List<string> ApplyLightMode() { List<string> list = new List<string>(); if (!LIGHT_MODE) { return list; } if (EXPORT_MODELS) { EXPORT_MODELS = false; list.Add("model export off (models already in the library are still served)"); } if (EXTRACT_TEXTURES) { EXTRACT_TEXTURES = false; list.Add("texture extraction from the game files off"); } if (EXTRACT_MESHES) { EXTRACT_MESHES = false; list.Add("mesh extraction from the game files off"); } List<string> list2 = new List<string>(); string[] array = (OBJECT_CATEGORIES ?? "").Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim().ToLowerInvariant(); if (Array.IndexOf(LightCategories, text) >= 0 && !list2.Contains(text)) { list2.Add(text); } } if (list2.Count == 0) { list2.Add("piece"); } string text2 = string.Join(",", list2); if (text2 != OBJECT_CATEGORIES) { list.Add("3D objects: " + text2 + " only (was " + OBJECT_CATEGORIES + ")"); OBJECT_CATEGORIES = text2; } if (RENDER_SIZE > TEXTURE_SIZE) { list.Add($"render_size {TEXTURE_SIZE} (was {RENDER_SIZE})"); RENDER_SIZE = TEXTURE_SIZE; } list.Add("3D ground cache 12 chunks (48 normally), world sweep every 5 minutes (every minute normally)"); return list; } 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"; } } [HarmonyPatch(typeof(ZRoutedRpc), "RouteRPC")] internal class DeathWatch { private const double DUPLICATE_WINDOW = 5.0; private static int deathRpcHash; private static bool deathRpcHashReady; private static readonly Dictionary<long, DateTime> lastDeath = new Dictionary<long, DateTime>(); private static void Postfix(RoutedRPCData rpcData) { try { if (rpcData != null) { if (!deathRpcHashReady) { deathRpcHash = StringExtensionMethods.GetStableHashCode("OnDeath"); deathRpcHashReady = true; } if (rpcData.m_methodHash == deathRpcHash) { Announce(rpcData); } } } catch (Exception ex) { ZLog.LogWarning((object)("WebMap: death watch failed: " + ex.Message)); } } private static void Announce(RoutedRPCData rpcData) { //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_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) MapDataServer instance = MapDataServer.getInstance(); if (instance == null) { return; } ZDOID targetZDO = rpcData.m_targetZDO; long num = rpcData.m_senderPeerID; string text = null; List<ZNetPeer> players = instance.players; if (players != null) { foreach (ZNetPeer item in players) { if (item != null && !item.m_server) { if (!((ZDOID)(ref targetZDO)).IsNone() && item.m_characterID == targetZDO) { text = item.m_playerName; num = item.m_uid; break; } if (text == null && item.m_uid == rpcData.m_senderPeerID) { text = item.m_playerName; } } } } if (string.IsNullOrEmpty(text) && !((ZDOID)(ref targetZDO)).IsNone()) { ZDO val = null; try { val = ZDOMan.instance.GetZDO(targetZDO); } catch { } if (val != null) { text = val.GetString(ZDOVars.s_playerName, ""); } } if (!string.IsNullOrEmpty(text) && ShouldAnnounce(num)) { ZLog.Log((object)("WebMap: player " + text + " died")); instance.AddMessage(num, 1, "Server", "player _" + text + "_ died", ev: true); Stats.Death(text); } } private static bool ShouldAnnounce(long key) { DateTime utcNow = DateTime.UtcNow; if (lastDeath.TryGetValue(key, out var value) && (utcNow - value).TotalSeconds < 5.0) { return false; } if (lastDeath.Count > 64) { List<long> list = new List<long>(); foreach (KeyValuePair<long, DateTime> item in lastDeath) { if ((utcNow - item.Value).TotalSeconds >= 5.0) { list.Add(item.Key); } } list.ForEach(delegate(long k) { lastDeath.Remove(k); }); } lastDeath[key] = utcNow; return true; } } internal static class Deeds { public enum Kind { Kill, Tree, Rock } private struct Last { public string who; public float t; } private const float WindowS = 10f; private static readonly Dictionary<ZDOID, Last> last = new Dictionary<ZDOID, Last>(); private static float pruned; public static void Hit(ZDOID target, string player, float now) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(player)) { return; } last[target] = new Last { who = player, t = now }; if (now - pruned < 10f) { return; } pruned = now; List<ZDOID> list = new List<ZDOID>(); foreach (KeyValuePair<ZDOID, Last> item in last) { if (now - item.Value.t > 10f) { list.Add(item.Key); } } foreach (ZDOID item2 in list) { last.Remove(item2); } } public static void AreaBroken(ZDOID target, float now, string owner = null) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Credit(Who(target, now, owner), Kind.Rock); } public static void Gone(ZDOID target, Kind kind, float now, string owner = null) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) Credit(Who(target, now, owner), kind); last.Remove(target); } private static string Who(ZDOID target, float now, string owner) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (!last.TryGetValue(target, out var value) || !(now - value.t <= 10f)) { return owner; } return value.who; } private static void Credit(string who, Kind kind) { if (!string.IsNullOrEmpty(who)) { Stats.Deed(who, kind); } } internal static void ResetForTests() { last.Clear(); pruned = 0f; } internal static string OwnerBeside(long owner, Vector3 at, float m) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) ZNetPeer val = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetPeer(owner) : null); if (val == null || string.IsNullOrEmpty(val.m_playerName)) { return null; } ZDO zDO = ZDOMan.instance.GetZDO(val.m_characterID); if (zDO == null) { return null; } Vector3 position = zDO.GetPosition(); float num = position.x - at.x; float num2 = position.z - at.z; if (!(num * num + num2 * num2 <= m * m)) { return null; } return val.m_playerName; } } [HarmonyPatch(typeof(ZRoutedRpc), "RouteRPC")] internal class DeedsRoutePatch { private static readonly int DamageHash = StringExtensionMethods.GetStableHashCode("RPC_Damage"); private static readonly int HitHash = StringExtensionMethods.GetStableHashCode("Hit"); private static readonly int AreaHash = StringExtensionMethods.GetStableHashCode("RPC_SetAreaHealth"); private static readonly int HideHash = StringExtensionMethods.GetStableHashCode("Hide"); private static readonly int AddItemHash = StringExtensionMethods.GetStableHashCode("RPC_AddItem"); private static readonly int SlotHash = StringExtensionMethods.GetStableHashCode("RPC_SetSlotVisual"); private const float AreaM = 32f; private static bool warned; private static void Prefix(RoutedRPCData rpcData) { //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Expected O, but got Unknown //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: 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_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) if (rpcData == null) { return; } int methodHash = rpcData.m_methodHash; if (methodHash != DamageHash && methodHash != HitHash && methodHash != AreaHash && methodHash != HideHash && methodHash != AddItemHash && methodHash != SlotHash) { return; } ZPackage parameters = rpcData.m_parameters; if (parameters == null) { return; } int pos = parameters.GetPos(); try { parameters.SetPos(0); float realtimeSinceStartup = Time.realtimeSinceStartup; if (methodHash == AddItemHash || methodHash == SlotHash) { string text = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetPeer(rpcData.m_senderPeerID) : null)?.m_playerName; if (methodHash == AddItemHash) { Kitchen.Asked(rpcData.m_targetZDO, text, realtimeSinceStartup); return; } int slot = parameters.ReadInt(); Kitchen.Shown(rpcData.m_targetZDO, text, slot, parameters.ReadString(), realtimeSinceStartup); } else if (methodHash == DamageHash || methodHash == HitHash) { HitData val = new HitData(); val.Deserialize(ref parameters); if (!((ZDOID)(ref val.m_attacker)).IsNone()) { ZDO zDO = ZDOMan.instance.GetZDO(val.m_attacker); string text2 = ((zDO != null) ? zDO.GetString(ZDOVars.s_playerName, "") : ""); Deeds.Hit(rpcData.m_targetZDO, text2, realtimeSinceStartup); if (methodHash == DamageHash && text2.Length > 0) { Gear.Struck(text2, rpcData.m_targetZDO, val); } } } else { parameters.ReadInt(); if (methodHash != AreaHash || !(parameters.ReadSingle() > 0f)) { ZDO zDO2 = ZDOMan.instance.GetZDO(rpcData.m_targetZDO); string owner = ((zDO2 != null) ? Deeds.OwnerBeside(rpcData.m_senderPeerID, zDO2.GetPosition(), 32f) : null); Deeds.AreaBroken(rpcData.m_targetZDO, realtimeSinceStartup, owner); } } } catch (Exception ex) { if (!warned) { warned = true; ZLog.LogWarning((object)("WebMap: deeds: reading a forwarded RPC failed: " + ex.Message)); } } finally { parameters.SetPos(pos); } } } [HarmonyPatch(typeof(ZDOMan), "HandleDestroyedZDO")] internal class DeedsGonePatch { private const float BesideM = 16f; private static readonly Dictionary<int, int> kinds = new Dictionary<int, int>(); private static bool warned; private static void Prefix(ZDOID uid) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) try { ZDO zDO = ZDOMan.instance.GetZDO(uid); if (zDO == null) { return; } int num = KindOf(zDO.GetPrefab()); if (num < 0) { return; } Deeds.Kind kind = (Deeds.Kind)num; string text; if (kind == Deeds.Kind.Kill) { if (zDO.GetBool(ZDOVars.s_tamed, false)) { return; } text = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetPeer(zDO.GetOwner()) : null)?.m_playerName; if (!string.IsNullOrEmpty(text)) { int s_attackers = ZDOVars.s_attackers; if (!zDO.GetBool(s_attackers + text, false)) { text = null; } } } else { text = Deeds.OwnerBeside(zDO.GetOwner(), zDO.GetPosition(), 16f); } Deeds.Gone(uid, kind, Time.realtimeSinceStartup, text); } catch (Exception ex) { if (!warned) { warned = true; ZLog.LogWarning((object)("WebMap: deeds: reading a destroy failed: " + ex.Message)); } } } internal static int KindOf(int prefab) { if (kinds.TryGetValue(prefab, out var value)) { return value; } if ((Object)(object)ZNetScene.instance == (Object)null) { return -1; } value = -1; GameObject prefab2 = ZNetScene.instance.GetPrefab(prefab); if ((Object)(object)prefab2 != (Object)null) { if ((Object)(object)prefab2.GetComponent<Character>() != (Object)null) { if ((Object)(object)prefab2.GetComponent<Player>() == (Object)null) { value = 0; } } else if ((Object)(object)prefab2.GetComponent<TreeBase>() != (Object)null) { value = 1; } else if ((Object)(object)prefab2.GetComponent<Destructible>() != (Object)null && WorldObjects.ClassifyName(((Object)prefab2).name.ToLowerInvariant()) == WorldObjects.Cat.Rock) { value = 2; } } kinds[prefab] = value; return value; } } internal struct DiscordMessage { public string Id; public string Content; public string DisplayName; public bool Bot; public bool Webhook; } internal struct DiscordRef { public string Id; public string Name; } internal static class Discord { internal struct Result { public int Status; public string Body; public double RetryAfter; } private const string Api = "https://discord.com/api/v10"; private static readonly CultureInfo Inv = CultureInfo.InvariantCulture; internal static Func<string, string, string, Result> Transport = DefaultTransport; private static int warnedPost; private static int warnedGuilds; private static int warnedChannels; private static int warnedMessages; private static Dictionary<string, string> roleNames; private static DateTime rolesAt; private static string Token => WebMapConfig.DISCORD_BOT_TOKEN; public static void Tell(string line) { Post(WebMapConfig.DISCORD_LOG_CHANNEL, line); } public static void PostChat(string name, string text) { if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(name) && !(name == WebMapConfig.ANNOUNCE_NAME) && WebMapConfig.CHAT_RELAY) { Post(WebMapConfig.DISCORD_CHAT_CHANNEL, "**" + name + "**: " + text); } } public static void Post(string channelId, string content) { if (string.IsNullOrEmpty(Token) || string.IsNullOrEmpty(channelId) || string.IsNullOrEmpty(content)) { return; } ThreadPool.QueueUserWorkItem(delegate { try { string body = "{\"content\":\"" + JsonEsc(Clip(content, 1900)) + "\",\"allowed_mentions\":{\"parse\":[]}}"; Call("POST", "/channels/" + channelId + "/messages", body); } catch (Exception ex) { WarnOnce(ref warnedPost, "post a message", ex); } }); } public static List<DiscordRef> Guilds() { List<DiscordRef> list = new List<DiscordRef>(); if (string.IsNullOrEmpty(Token)) { return list; } try { foreach (string item in Body.Items(Call("GET", "/users/@me/guilds", null).Body)) { list.Add(new DiscordRef { Id = Body.Str(item, "id"), Name = Body.Str(item, "name") }); } } catch (Exception ex) { WarnOnce(ref warnedGuilds, "list guilds", ex); } return list; } public static List<DiscordRef> Channels(string guildId) { List<DiscordRef> list = new List<DiscordRef>(); if (string.IsNullOrEmpty(Token) || string.IsNullOrEmpty(guildId)) { return list; } try { foreach (string item in Body.Items(Call("GET", "/guilds/" + guildId + "/channels", null).Body)) { double num = Body.Num(item, "type"); if (num == 0.0 || num == 5.0) { list.Add(new DiscordRef { Id = Body.Str(item, "id"), Name = Body.Str(item, "name") }); } } } catch (Exception ex) { WarnOnce(ref warnedChannels, "list channels", ex); } return list; } public static List<DiscordMessage> MessagesAfter(string channelId, string afterId, int limit) { List<DiscordMessage> list = new List<DiscordMessage>(); if (string.IsNullOrEmpty(Token) || string.IsNullOrEmpty(channelId)) { return list; } try { string path = "/channels/" + channelId + "/messages?limit=" + limit.ToString(Inv) + (string.IsNullOrEmpty(afterId) ? "" : ("&after=" + afterId)); foreach (string item in Body.Items(Call("GET", path, null).Body)) { list.Add(Parse(item)); } } catch (Exception ex) { WarnOnce(ref warnedMessages, "read messages", ex); } return list; } public static string LatestMessageId(string channelId) { if (string.IsNullOrEmpty(Token) || string.IsNullOrEmpty(channelId)) { return null; } try { List<string> list = Body.Items(Call("GET", "/channels/" + channelId + "/messages?limit=1", null).Body); return (list.Count > 0) ? Body.Str(list[0], "id") : null; } catch (Exception ex) { WarnOnce(ref warnedMessages, "read messages", ex); return null; } } internal static string Plain(string content, string item) { if (string.IsNullOrEmpty(content)) { return content; } Dictionary<string, string> names = new Dictionary<string, string>(); foreach (string item2 in Body.Items(Body.Arr(item ?? "", "mentions") ?? "[]")) { string text = Body.Str(item2, "id"); if (text != null) { names[text] = Body.Str(item2, "global_name") ?? Body.Str(item2, "username") ?? "someone"; } } content = Regex.Replace(content, "<@!?(\\d+)>", (Match m) => "@" + (names.TryGetValue(m.Groups[1].Value, out var value) ? value : "someone")); content = Regex.Replace(content, "<@&(\\d+)>", (Match m) => "@" + RoleName(m.Groups[1].Value)); content = Regex.Replace(content, "<#(\\d+)>", "#channel"); content = Regex.Replace(content, "<a?:(\\w+):\\d+>", ":$1:"); return content; } private static string RoleName(string id) { try { if (roleNames == null || (DateTime.UtcNow - rolesAt).TotalMinutes > 10.0) { Dictionary<string, string> dictionary = new Dictionary<string, string>(); string dISCORD_GUILD = WebMapConfig.DISCORD_GUILD; if (!string.IsNullOrEmpty(dISCORD_GUILD)) { foreach (string item in Body.Items(Call("GET", "/guilds/" + dISCORD_GUILD + "/roles", null).Body)) { string text = Body.Str(item, "id"); string text2 = Body.Str(item, "name"); if (text != null && text2 != null) { dictionary[text] = text2; } } } roleNames = dictionary; rolesAt = DateTime.UtcNow; } string value; return roleNames.TryGetValue(id, out value) ? value : "role"; } catch { return "role"; } } private static DiscordMessage Parse(string item) { string body = Body.Obj(item, "author") ?? "{}"; string text = Body.Obj(item, "member"); string text2 = ((text != null) ? Body.Str(text, "nick") : null); string text3 = Body.Str(body, "global_name"); string text4 = Body.Str(body, "username") ?? "someone"; return new DiscordMessage { Id = Body.Str(item, "id"), Content = Plain(Body.Str(item, "content") ?? "", item), Bot = Body.Bool(body, "bot"), Webhook = Body.Has(item, "webhook_id"), DisplayName = ((!string.IsNullOrEmpty(text2)) ? text2 : ((!string.IsNullOrEmpty(text3)) ? text3 : text4)) }; } internal static Result Call(string method, string path, string body) { Result result = Transport(method, path, body); if (result.Status == 429) { Thread.Sleep((int)Math.Ceiling(result.RetryAfter * 1000.0) + 50); result = Transport(method, path, body); } if (result.Status >= 300) { throw new Exception($"discord {method} {path}: HTTP {result.Status}"); } return result; } private static Result DefaultTransport(string method, string path, string body) { HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create("https://discord.com/api/v10" + path); httpWebRequest.Method = method; httpWebRequest.Headers["Authorization"] = "Bot " + Token; httpWebRequest.UserAgent = "WebMap (https://github.com/hunter-jsb/valheim-webmap, 1.0)"; httpWebRequest.Timeout = 10000; if (body != null) { httpWebRequest.ContentType = "application/json"; byte[] bytes = Encoding.UTF8.GetBytes(body); using Stream stream = httpWebRequest.GetRequestStream(); stream.Write(bytes, 0, bytes.Length); } HttpWebResponse httpWebResponse2 = default(HttpWebResponse); try { using HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse(); return new Result { Status = (int)httpWebResponse.StatusCode, Body = ReadBody(httpWebResponse), RetryAfter = 0.0 }; } catch (WebException ex) when (((Func<bool>)delegate { // Could not convert BlockContainer to single expression httpWebResponse2 = ex.Response as HttpWebResponse; return httpWebResponse2 != null; }).Invoke()) { string body2 = ReadBody(httpWebResponse2); double result = Body.Num(body2, "retry_after"); if (double.IsNaN(result)) { double.TryParse(httpWebResponse2.Headers["Retry-After"], NumberStyles.Float, Inv, out result); } return new Result { Status = (int)httpWebResponse2.StatusCode, Body = body2, RetryAfter = result }; } } private static string ReadBody(HttpWebResponse resp) { using Stream stream = resp.GetResponseStream(); using StreamReader streamReader = new StreamReader(stream ?? Stream.Null, Encoding.UTF8); return streamReader.ReadToEnd(); } private static string Clip(string s, int max) { if (s.Length <= max) { return s; } return s.Substring(0, max); } private static string JsonEsc(string s) { StringBuilder stringBuilder = new StringBuilder(); foreach (char c in s) { if (c == '"' || c == '\\') { stringBuilder.Append('\\').Append(c); } else if (c == '\n') { stringBuilder.Append("\\n"); } else if (c == '\r') { stringBuilder.Append("\\r"); } else if (c < ' ') { stringBuilder.Append(' '); } else { stringBuilder.Append(c); } } return stringBuilder.ToString(); } private static void WarnOnce(ref int flag, string what, Exception ex) { if (Interlocked.CompareExchange(ref flag, 1, 0) == 0) { ZLog.LogWarning((object)("WebMap: discord " + what + " failed, will keep trying quietly: " + ex.Message)); } } } public class DiscordWebHook { private readonly string webHookUrl; public DiscordWebHook(string url) { webHookUrl = url; } public void SendMessage(string msgSend) { if (Ext.IsNullOrEmpty(webHookUrl)) { return; } ThreadPool.QueueUserWorkItem(delegate { try { NameValueCollection data = new NameValueCollection { { "content", msgSend } }; using WebClient webClient = new WebClient(); webClient.UploadValues(webHookUrl, data); } catch (Exception ex) { ZLog.LogWarning((object)("WebMap: discord webhook failed: " + ex.Message)); } }); } } internal static class Features { private class Grids { public ushort[] land; public ushort[] region; public ushort[] water; public ushort[] range; public byte[] cls; public float[] hgt; public float cell; } private class Named { public string name; public string by; public long t; } private class Feature { public string id; public string kind; public float x; public float z; public float w; public float h; public float area; public bool hasPeak; public float px; public float pz; public float py; public bool hasElev; public float hmin; public float hmax; public float hmean; public List<float[]> line; } private const string NamesFile = "names.tsv"; private const int N = 1024; private const float Edge = 10500f; public static volatile int Rev; private static volatile string json = "{\"features\":[],\"count\":0,\"rev\":0}"; private static volatile bool building; private static string dir; private static readonly object gate = new object(); private static readonly Dictionary<string, Named> names = new Dictionary<string, Named>(); private static Feature[] found = new Feature[0]; private static readonly Dictionary<string, string> generated = new Dictionary<string, string>(); private static volatile Grids grids; private static readonly string[] Stems = new string[113] { "Ask", "Bjarn", "Brim", "Dag", "Eld", "Fenn", "Frost", "Gard", "Grim", "Gull", "Hav", "Hjal", "Isa", "Jarn", "Kald", "Lang", "Nord", "Orm", "Ragn", "Sten", "Torn", "Ulf", "Var", "Vind", "Ygg", "Aud", "Ein", "Skal", "Hrim", "Sval", "Ravn", "Bjork", "Eik", "Lind", "Hol", "Sol", "Mor", "Raud", "Gra", "Svart", "Hvit", "Blak", "Kol", "Sig", "Thor", "Odd", "Rune", "Hild", "Alf", "Ger", "Gunn", "Ing", "Ketil", "Odin", "Tyr", "Vale", "Agn", "Arn", "Bal", "Berg", "Bod", "Dyr", "Egg", "Eir", "Fal", "Fjor", "Galt", "Geir", "Hakon", "Hall", "Har", "Heid", "Helg", "Hjort", "Hrafn", "Ivar", "Jor", "Kar", "Knut", "Leif", "Loft", "Magn", "Mjol", "Njal", "Ol", "Ott", "Rag", "Rand", "Rolf", "Saem", "Sand", "Sindr", "Skeg", "Snor", "Stein", "Styr", "Sun", "Svein", "Tind", "Tofa", "Trygg", "Ulfr", "Una", "Vig", "Vott", "Yng", "Yr", "Aesir", "Bragi", "Freyr", "Idun", "Loki", "Skadi" }; private static readonly string[] Links = new string[10] { "", "", "s", "s", "ar", "a", "e", "i", "u", "en" }; private const float HubReach = 150f; internal const int Cells = 1024; private static string Inv(FormattableString f) { return f.ToString(CultureInfo.InvariantCulture); } public static void Load(string worldDataPath) { dir = worldDataPath; lock (gate) { LoadNames(); } StaticCoroutine.Start(Build()); } private static void LoadNames() { names.Clear(); try { string path = Path.Combine(dir, "names.tsv"); if (!File.Exists(path)) { return; } string[] array = File.ReadAllLines(path); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { '\t' }); if (array2.Length >= 2 && array2[0].Length != 0) { long result = 0L; if (array2.Length > 3) { long.TryParse(array2[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out result); } names[array2[0]] = new Named { name = array2[1], by = ((array2.Length > 2) ? array2[2] : ""), t = result }; } } } catch (Exception ex) { ZLog.LogWarning((object)("WebMap: names not loaded: " + ex.Message)); } } private static IEnumerator Build() { if (building) { yield break; } building = true; float cell = (float)(WebMapConfig.TEXTURE_SIZE * WebMapConfig.PIXEL_SIZE) / 1024f; float half = 512f; byte[] cls = new byte[1048576]; float[] hgt = new float[1048576]; float water = ZoneSystem.instance.m_waterLevel; WorldGenerator wg = WorldGenerator.instance; Color val = default(Color); for (int y = 0; y < 1024; y++) { yield return null; float num = ((float)y - half) * cell + cell / 2f; for (int i = 0; i < 1024; i++) { float num2 = ((float)i - half) * cell + cell / 2f; int num3 = y * 1024 + i; if (num2 * num2 + num * num > 110250000f) { cls[num3] = 0; hgt[num3] = 0f; continue; } Biome biome = wg.GetBiome(num2, num, 0.02f, false); float biomeHeight = wg.GetBiomeHeight(biome, num2, num, ref val, false, true); hgt[num3] = biomeHeight; cls[num3] = (byte)((!(biomeHeight < water)) ? ((byte)(Index(biome) + 1)) : 0); } } List<List<Vector2>> rivers = new List<List<Vector2>>(); try { foreach (River river in wg.GetRivers()) { rivers.Add(RiverPath(river)); } } catch (Exception ex) { ZLog.LogWarning((object)("WebMap: rivers not read: " + ex.Message)); } string seed = ""; try { seed = ((wg.m_world != null) ? (wg.m_world.m_seedName ?? "") : ""); } catch { } ThreadPool.QueueUserWorkItem(delegate { try { Analyse(cls, hgt, rivers, cell, seed); } catch (Exception ex2) { ZLog.LogWarning((object)("WebMap: geography not read: " + ex2.Message + "\n" + ex2.StackTrace)); } finally { building = false; } }); } private static List<Vector2> RiverPath(River r) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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_0043: 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_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: 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_00be: Unknown result type (might be due to invalid IL or missing references) List<Vector2> list = new List<Vector2>(); float num = r.widthMin / 8f; Vector2 val = r.p1 - r.p0; Vector2 normalized = ((Vector2)(ref val)).normalized; Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0f - normalized.y, normalized.x); float num2 = Vector2.Distance(r.p0, r.p1); for (float num3 = 0f; num3 <= num2; num3 += num) { float num4 = num3 / r.curveWavelength; float num5 = (float)(Math.Sin(num4) * Math.Sin((double)num4 * 0.634119987487793) * Math.Sin((double)num4 * 0.3341200053691864) * (double)r.curveWidth); list.Add(r.p0 + normalized * num3 + val2 * num5); } return list; } private static int Index(Biome b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Expected I4, but got Unknown int num = (int)b; int num2 = 0; while (num > 1 && num2 < 31) { num >>= 1; num2++; } return num2; } private static byte Class(Biome b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return (byte)(Index(b) + 1); } private static void Analyse(byte[] cls, float[] hgt, List<List<Vector2>> rivers, float cell, string seed) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_04ac: Unknown result type (might be due to invalid IL or missing references) //IL_0564: Unknown result type (might be due to invalid IL or missing references) //IL_056d: Unknown result type (might be due to invalid IL or missing references) //IL_05f4: Unknown result type (might be due to invalid IL or missing references) //IL_05f9: Unknown result type (might be due to invalid IL or missing references) //IL_0727: Unknown result type (might be due to invalid IL or missing references) //IL_073a: Unknown result type (might be due to invalid IL or missing references) //IL_0610: Unknown result type (might be due to invalid IL or missing references) //IL_0619: Unknown result type (might be due to invalid IL or missing references) //IL_0650: Unknown result type (might be due to invalid IL or missing references) //IL_0666: Unknown result type (might be due to invalid IL or missing references) //IL_06bb: Unknown result type (might be due to invalid IL or missing references) //IL_06d2: Unknown result type (might be due to invalid IL or missing references) //IL_06e9: Unknown result type (might be due to invalid IL or missing references) //IL_0700: Unknown result type (might be due to invalid IL or missing references) //IL_0691: Unknown result type (might be due to invalid IL or missing references) //IL_069e: Unknown result type (might be due to invalid IL or missing references) //IL_06ae: Unknown result type (might be due to invalid IL or missing references) //IL_06b3: Unknown result type (might be due to invalid IL or missing references) List<Feature> list = new List<Feature>(); int num = 1048576; float cellKm2 = cell * cell / 1000000f; bool[] array = new bool[num]; foreach (List<Vector2> river in rivers) { float num2 = 4f; foreach (Vector2 item2 in river) { int num3 = (int)Math.Floor(item2.x / cell + 512f); int num4 = (int)Math.Floor(item2.y / cell + 512f); for (int i = -(int)num2; (float)i <= num2; i++) { for (int j = -(int)num2; (float)j <= num2; j++) { int num5 = num3 + j; int num6 = num4 + i; if (num5 >= 0 && num6 >= 0 && num5 < 1024 && num6 < 1024 && !((float)(j * j + i * i) > num2 * num2)) { array[num6 * 1024 + num5] = true; } } } } } Grids grids = new Grids { land = new ushort[num], region = new ushort[num], water = new ushort[num], range = new ushort[num], cls = cls, hgt = hgt, cell = cell }; bool[] array2 = new bool[num]; for (int k = 0; k < num; k++) { array2[k] = cls[k] != 0; } Regions(array2, cell, 20, (int num25) => (!((float)num25 * cellKm2 >= 2f)) ? ((!((float)num25 * cellKm2 >= 0.1f)) ? "holm" : "island") : "continent", list, hgt, withPeak: false, grids.land); bool[] array3 = new bool[num]; for (int num7 = 0; num7 < num; num7++) { array3[num7] = !array2[num7] && !array[num7]; } int count; int[] area; int[] array4 = Label(array3, out count, out area); bool[] array5 = new bool[count + 1]; for (int num8 = 0; num8 < 1024; num8++) { array5[array4[num8]] = true; array5[array4[1047552 + num8]] = true; } for (int num9 = 0; num9 < 1024; num9++) { array5[array4[num9 * 1024]] = true; array5[array4[num9 * 1024 + 1024 - 1]] = true; } bool[] array6 = new bool[num]; for (int num10 = 0; num10 < num; num10++) { array6[num10] = array3[num10] && !array5[array4[num10]]; } Regions(array6, cell, 12, (int _) => "lake", list, null, withPeak: false, grids.water); bool[] array7 = new bool[num]; for (int num11 = 0; num11 < num; num11++) { array7[num11] = !array2[num11] && array5[array4[num11]]; } bool[] array8 = Erode(Dilate(array2, 8), 8); bool[] array9 = new bool[num]; for (int num12 = 0; num12 < num; num12++) { array9[num12] = array8[num12] && array7[num12]; } Regions(array9, cell, 40, (int _) => "bay", list, null, withPeak: false, grids.water); bool[] array10 = new bool[num]; byte b = Class((Biome)4); for (int num13 = 0; num13 < num; num13++) { array10[num13] = cls[num13] == b; } Regions(array10, cell, 40, (int _) => "range", list, hgt, withPeak: true, grids.range); (Biome, string)[] array11 = new(Biome, string)[7] { ((Biome)8, "forest"), ((Biome)2, "swamp"), ((Biome)16, "plains"), ((Biome)512, "mistlands"), ((Biome)1, "meadows"), ((Biome)32, "ashlands"), ((Biome)64, "north") }; for (int num14 = 0; num14 < array11.Length; num14++) { (Biome, string) tuple = array11[num14]; Biome item = tuple.Item1; string kind = tuple.Item2; byte b2 = Class(item); bool[] array12 = new bool[num]; for (int num15 = 0; num15 < num; num15++) { array12[num15] = cls[num15] == b2; } Regions(array12, cell, (int)(0.5f / cellKm2), (int _) => kind, list, hgt, withPeak: false, grids.region); } foreach (List<Vector2> river2 in rivers) { if (river2.Count < 2) { continue; } float num16 = 0f; for (int num17 = 1; num17 < river2.Count; num17++) { num16 += Vector2.Distance(river2[num17 - 1], river2[num17]); } if (num16 < 300f) { continue; } Feature feature = new Feature { kind = "river", line = new List<float[]>(), area = 0f }; float num18 = 0f; float num19 = 0f; float num20 = float.MaxValue; float num21 = float.MaxValue; float num22 = float.MinValue; float num23 = float.MinValue; Vector2 val = river2[river2.Count / 2]; for (int num24 = 0; num24 < river2.Count; num24++) { if (num24 > 0) { num18 += Vector2.Distance(river2[num24 - 1], river2[num24]); } if (num24 == 0 || num24 == river2.Count - 1 || num18 >= num19) { feature.line.Add(new float[2] { R1(river2[num24].x), R1(river2[num24].y) }); num19 = num18 + 50f; } if (num18 >= num16 / 2f && val == river2[river2.Count / 2]) { val = river2[num24]; } num20 = Math.Min(num20, river2[num24].x); num22 = Math.Max(num22, river2[num24].x); num21 = Math.Min(num21, river2[num24].y); num23 = Math.Max(num23, river2[num24].y); } feature.x = R1(val.x); feature.z = R1(val.y); feature.w = R1(num22 - num20); feature.h = R1(num23 - num21); feature.area = R1(num16); feature.id = Id(feature.kind, feature.x, feature.z); list.Add(feature); } Dictionary<string, string> dictionary = new Dictionary<string, string>(); HashSet<string> used = new HashSet<string>(); foreach (Feature item3 in list) { dictionary[item3.id] = Generate(seed, item3, used); } lock (gate) { found = list.ToArray(); Features.grids = grids; generated.Clear(); foreach (KeyValuePair<string, string> item4 in dictionary) { generated[item4.Key] = item4.Value; } Rebuild(); } Dictionary<string, int> dictionary2 = new Dictionary<string, int>(); foreach (Feature item5 in list) { dictionary2[item5.kind] = ((!dictionary2.TryGetValue(item5.kind, out var value)) ? 1 : (value + 1)); } List<string> list2 = new List<string>(); foreach (KeyValuePair<string, int> item6 in dictionary2) { list2.Add(item6.Value + " " + item6.Key); } ZLog.Log((object)("WebMap: geography read: " + string.Join(", ", list2.ToArray()))); } private static float R1(float v) { return (float)Math.Round(v, 1); } private static string Id(string kind, float x, float z) { return kind + "@" + ((int)Math.Round(x / 48f) * 48).ToString(CultureInfo.InvariantCulture) + "," + ((int)Math.Round(z / 48f) * 48).ToString(CultureInfo.InvariantCulture); } private static void Regions(bool[] mask, float cell, int minCells, Func<int, string> kindOf, List<Feature> feats, float[] hgt, bool withPeak, ushort[] into) { int num = 1048576; int count; int[] area; int[] array = Label(mask, out count, out area); if (count == 0) { return; } int[] array2 = Depth(mask); int[] array3 = new int[count + 1]; int[] array4 = new int[count + 1]; int[] array5 = new int[count + 1]; int[] array6 = new int[count + 1]; int[] array7 = new int[count + 1]; int[] array8 = new int[count + 1]; int[] array9 = new int[count + 1]; float[] array10 = new float[count + 1]; float[] array11 = new float[count + 1]; double[] array12 = new double[count + 1]; for (int i = 1; i <= count; i++) { array5[i] = (array6[i] = 1024); array7[i] = (array8[i] = -1); array4[i] = -1; array10[i] = float.MinValue; array11[i] = float.MaxValue; } for (int j = 0; j < num; j++) { int num2 = array[j]; if (num2 == 0) { continue; } int num3 = j % 1024; int num4 = j / 1024; if (array2[j] > array4[num2]) { array4[num2] = array2[j]; array3[num2] = j; } if (num3 < array5[num2]) { array5[num2] = num3; } if (num3 > array7[num2]) { array7[num2] = num3; } if (num4 < array6[num2]) { array6[num2] = num4; } if (num4 > array8[num2]) { array8[num2] = num4; } if (hgt != null) { if (hgt[j] > array10[num2]) { array10[num2] = hgt[j]; array9[num2] = j; } if (hgt[j] < array11[num2]) { array11[num2] = hgt[j]; } array12[num2] += hgt[j]; } } float num5 = cell * cell / 1000000f; ushort[] array13 = new ushort[count + 1]; for (int k = 1; k <= count; k++) { if (area[k] < minCells) { continue; } Feature feature = new Feature { kind = kindOf(area[k]), area = R1((float)area[k] * num5 * 100f) / 100f }; feature.x = R1(World(array3[k] % 1024, cell)); feature.z = R1(World(array3[k] / 1024, cell)); feature.w = R1((float)(array7[k] - array5[k] + 1) * cell); feature.h = R1((float)(array8[k] - array6[k] + 1) * cell); if (hgt != null) { feature.hasElev = true; feature.hmin = R1(array11[k]); feature.hmax = R1(array10[k]); feature.hmean = R1((float)(array12[k] / (double)area[k])); if (withPeak) { feature.hasPeak = true; feature.px = R1(World(array9[k] % 1024, cell)); feature.pz = R1(World(array9[k] / 1024, cell)); feature.py = R1(array10[k]); } } feature.id = Id(feature.kind, feature.x, feature.z); if (feats.Count < 65535) { array13[k] = (ushort)(feats.Count + 1); } feats.Add(feature); } if (into == null) { return; } for (int l = 0; l < num; l++) { if (array[l] != 0 && array13[array[l]] != 0) { into[l] = array13[array[l]]; } } } private static float World(int c, float cell) { return ((float)c - 512f) * cell + cell / 2f; } private static int[] Label(bool[] mask, out int count, out int[] area) { int num = 1048576; int[] array = new int[num]; List<int> list = new List<int> { 0 }; int[] array2 = new int[num]; count = 0; for (int i = 0; i < num; i++) { if (!mask[i] || array[i] != 0) { continue; } int num2 = ++count; int num3 = 0; int num4 = 0; int num5 = 0; array2[num4++] = i; array[i] = num2; while (num3 < num4) { int num6 = array2[num3++]; num5++; int num7 = num6 % 1024; int num8 = num6 / 1024; if (num7 > 0 && mask[num6 - 1] && array[num6 - 1] == 0) { array[num6 - 1] = num2; array2[num4++] = num6 - 1; } if (num7 < 1023 && mask[num6 + 1] && array[num6 + 1] == 0) { array[num6 + 1] = num2; array2[num4++] = num6 + 1; } if (num8 > 0 && mask[num6 - 1024] && array[num6 - 1024] == 0) { array[num6 - 1024] = num2; array2[num4++] = num6 - 1024; } if (num8 < 1023 && mask[num6 + 1024] && array[num6 + 1024] == 0) { array[num6 + 1024] = num2; array2[num4++] = num6 + 1024; } } list.Add(num5); } area = list.ToArray(); return array; } private static int[] Depth(bool[] mask) { int num = 1048576; int[] array = new int[num]; int[] array2 = new int[num]; int num2 = 0; int num3 = 0; for (int i = 0; i < num; i++) { array[i] = -1; if (mask[i]) { int num4 = i % 1024; int num5 = i / 1024; if (num4 == 0 || num5 == 0 || num4 == 1023 || num5 == 1023 || !mask[i - 1] || !mask[i + 1] || !mask[i - 1024] || !mask[i + 1024]) { array[i] = 0; array2[num3++] = i; } } } while (num2 < num3) { int num6 = array2[num2++]; int num7 = num6 % 1024; int num8 = num6 / 1024; int num9 = array[num6] + 1; if (num7 > 0 && mask[num6 - 1] && array[num6 - 1] < 0) { array[num6 - 1] = num9; array2[num3++] = num6 - 1; } if (num7 < 1023 && mask[num6 + 1] && array[num6 + 1] < 0) { array[num6 + 1] = num9; array2[num3++] = num6 + 1; } if (num8 > 0 && mask[num6 - 1024] && array[num6 - 1024] < 0) { array[num6 - 1024] = num9; array2[num3++] = num6 - 1024; } if (num8 < 1023 && mask[num6 + 1024] && array[num6 + 1024] < 0) { array[num6 + 1024] = num9; array2[num3++] = num6 + 1024; } } return array; } private static bool[] Dilate(bool[] a, int r) { return Pass(Pass(a, r, rows: true, any: true), r, rows: false, any: true); } private static bool[] Erode(bool[] a, int r) { return Pass(Pass(a, r, rows: true, any: false), r, rows: false, any: false); } private static bool[] Pass(bool[] a, int r, bool rows, bool any) { bool[] array = new bool[a.Length]; for (int i = 0; i < 1024; i++) { for (int j = 0; j < 1024; j++) { bool flag = !any; for (int k = -r; k <= r; k++) { int num = (rows ? (j + k) : j); int num2 = (rows ? i : (i + k)); bool flag2 = num >= 0 && num2 >= 0 && num < 1024 && num2 < 1024 && a[num2 * 1024 + num]; if (any ? flag2 : (!flag2)) { flag = any; break; } } array[i * 1024 + j] = flag; } } return array; } private static string Suffix(string kind) { return kind switch { "continent" => "land", "island" => "ey", "holm" => "holm", "range" => "fjell", "lake" => "vatn", "river" => "á", "bay" => "vik", "forest" => "mork", "swamp" => "myr", "plains" => "vellir", "meadows" => "eng", "mistlands" => "dimma", "ashlands" => "brand", "north" => "frost", _ => "stad", }; } private static string Generate(string seed, Feature f, HashSet<string> used) { string text = null; string text2 = ((f.kind == "continent") ? ("island" + f.id.Substring(f.id.IndexOf('@'))) : f.id); for (int i = 0; i < 24; i++) { uint num = (uint)Fnv.Of(seed + "|" + text2 + "|" + i); string text3 = Stems[num % (uint)Stems.Length]; string text4 = Suffix(f.kind); bool flag = "aeiouy".IndexOf(char.ToLowerInvariant(text3[text3.Length - 1])) >= 0; bool flag2 = "aeiouyá".IndexOf(text4[0]) >= 0; if (flag && flag2) { text3 = text3.Substring(0, text3.Length - 1); flag = false; } string text5 = (flag ? "" : Links[(num >> 8) % (uint)Links.Length]); if (flag2 && text5.Length > 0 && "aeiou".IndexOf(text5[text5.Length - 1]) >= 0) { text5 = ""; } text = text3 + text5 + text4; if (used.Add(text)) { return text; } } return text; } private static string Alt(string id) { int num = id.IndexOf('@'); if (num < 0) { return null; } string text = id.Substring(0, num); if (!(text == "continent")) { if (!(text == "island")) { return null; } return "continent" + id.Substring(num); } return "island" + id.Substring(num); } private static bool Given(string id, out Named nm) { if (names.TryGetValue(id, out nm) && !string.IsNullOrEmpty(nm.name)) { return true; } string text = Alt(id); if (text != null && names.TryGetValue(text, out nm)) { return !string.IsNullOrEmpty(nm.name); } return false; } private static void Open(StringBuilder sb, Feature f) { Named nm; bool flag = Given(f.id, out nm); string value; string s = (flag ? nm.name : (generated.TryGetValue(f.id, out value) ? value : f.kind)); sb.Append(Inv($"{{\"id\":\"{Esc(f.id)}\",\"kind\":\"{f.kind}\",\"name\":\"{Esc(s)}\",\"x\":{f.x:0.#},\"z\":{f.z:0.#},\"w\":{f.w:0.#},\"h\":{f.h:0.#},\"area\":{f.area:0.##}")); if (flag) { sb.Append(Inv($",\"by\":\"{Esc(nm.by)}\",\"t\":{nm.t}")); } if (f.hasPeak) { sb.Append(Inv($",\"peak\":{{\"x\":{f.px:0.#},\"z\":{f.pz:0.#},\"y\":{f.py:0.#}}}")); } if (f.hasElev) { sb.Append(Inv($",\"elev\":{{\"min\":{f.hmin:0.#},\"max\":{f.hmax:0.#},\"mean\":{f.hmean:0.#}}}")); } if (f.line == null) { return; } sb.Append(",\"line\":["); for (int i = 0; i < f.line.Count; i++) { if (i > 0) { sb.Append(','); } sb.Append(Inv($"[{f.line[i][0]:0.#},{f.line[i][1]:0.#}]")); } sb.Append(']'); } private static void Rebuild() { StringBuilder stringBuilder = new StringBuilder("{\"features\":["); int value = 0; Feature[] array = found; foreach (Feature f in array) { if (value++ > 0) { stringBuilder.Append(','); } Open(stringBuilder, f); stringBuilder.Append('}'); } stringBuilder.Append("],\"count\":").Append(value); int num = Fnv.Of(stringBuilder.ToString()); stringBuilder.Append(",\"rev\":").Append(num).Append('}'); json = stringBuilder.ToString(); Rev = num; } private static string Esc(string s) { StringBuilder stringBuilder = new StringBuilder(); string text = s ?? ""; foreach (char c in text) { if (c == '"' || c == '\\') { stringBuilder.Append('\\').Append(c); } else if (c < ' ') { stringBuilder.Append(' '); } else { stringBuilder.Append(c); } } return stringBuilder.ToString(); } public static string Json() { return json; } public static string At(float x, float z) { Grids grids = Features.grids; if (grids == null) { return null; } int num = (int)Math.Floor(x / grids.cell + 512f); int num2 = (int)Math.Floor(z / grids.cell + 512f); if (num < 0 || num2 < 0 || num >= 1024 || num2 >= 1024) { return null; } if (!MapFog.Explored(x, z)) { return null; } int num3 = num2 * 1024 + num; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(Inv($"{{\"x\":{x:0.#},\"z\":{z:0.#},\"biome\":\"{BiomeName(grids.cls[num3])}\",\"height\":{grids.hgt[num3]:0.#},\"here\":[")); int num4 = 0; lock (gate) { ushort[][] array = new ushort[4][] { grids.water, grids.range, grids.region, grids.land }; foreach (ushort[] array2 in array) { int num5 = array2[num3]; if (num5 != 0 && num5 <= found.Length) { Feature f = found[num5 - 1]; if (num4++ > 0) { stringBuilder.Append(','); } Open(stringBuilder, f); Measure(grids, array2, (ushort)num5, f, out var walked, out var builds, out var portals); int num6 = Count(grids, array2, (ushort)num5, Stats.DeathPositions); stringBuilder.Append(Inv($",\"explored\":{walked:0.###},\"builds\":{builds},\"portals\":{portals},\"deaths\":{num6}}}")); } } } stringBuilder.Append("]}"); return stringBuilder.ToString(); } private static void Measure(Grids g, ushort[] layer, ushort k, Feature f, out float walked, out int builds, out int portals) { int num = 0; int num2 = 0; byte[] array = ((WebMap.mapDataServer != null) ? WebMap.mapDataServer.fogRgba : null); int tEXTURE_SIZE = WebMapConfig.TEXTURE_SIZE; int num3 = tEXTURE_SIZE / 1024; for (int i = 0; i < 1024; i++) { int num4 = i * 1024; for (int j = 0; j < 1024; j++) { if (layer[num4 + j] != k) { continue; } num++; if (array == null) { num2++; continue; } int num5 = j * num3 + num3 / 2; int num6 = i * num3 + num3 / 2; if (array[(num6 * tEXTURE_SIZE + num5) * 4] > 127) { num2++; } } } walked = ((num == 0) ? 0f : ((float)num2 / (float)num)); builds = Count(g, layer, k, Pieces.Positions); portals = Count(g, layer, k, Portals.Positions); } private static int Count(Grids g, ushort[] layer, ushort k, float[] pos) { int num = 0; if (pos == null) { return 0; } for (int i = 0; i + 1 < pos.Length; i += 2) { int num2 = (int)Math.Floor(pos[i] / g.cell + 512f); int num3 = (int)Math.Floor(pos[i + 1] / g.cell + 512f); if (num2 >= 0 && num3 >= 0 && num2 < 1024 && num3 < 1024 && layer[num3 * 1024 + num2] == k) { num++; } } return num; } public static int ClassAt(float x, float z) { Grids grids = Features.grids; if (grids == null) { return -1; } int num = (int)Math.Floor(x / grids.cell + 512f); int num2 = (int)Math.Floor(z / grids.cell + 512f); if (num < 0 || num2 < 0 || num >= 1024 || num2 >= 1024) { return -1; } return grids.cls[num2 * 1024 + num]; } public static string BiomeName(byte c) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Invalid comparison between Unknown and I4 //IL_0017: 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_002f: Expected I4, but got Unknown //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Invalid comparison between Unknown and I4 //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Invalid comparison between Unknown and I4 if (c == 0) { return "Ocean"; } Biome val = (Biome)(1 << c - 1); if ((int)val <= 16) { switch (val - 1) { default: if ((int)val != 8) { if ((int)val != 16) { break; } return "Plains"; } return "Black Forest"; case 0: return "Meadows"; case 1: return "Swamp"; case 3: return "Mountain"; case 2: break; } } else { if ((int)val == 32) { return "Ashlands"; } if ((int)val == 64) { return "Deep North"; } if ((int)val == 512) { return "Mistlands"; } } return "Ocean"; } public static string SetName(string id, string name, string by) { string log; return SetName(id, name, by, out log); } public static string SetName(string id, string name, string by, out string log) { log = null; id = (id ?? "").Trim(); by = (by ?? "").Trim(); name = Regex.Replace(name ?? "", "[\\p{C}]", "").Trim(); if (name.Length > 40) { return "a name is at most 40 characters"; } if (id.StartsWith("hub@")) { return SetHubName(id, name, by, out log); } lock (gate) { Feature feature = null; Feature[] array = found; foreach (Feature feature2 in array) { if (feature2.id == id) { feature = feature2; break; } } if (feature == null) { return "no such place"; } Named nm; string value; string text = (Given(id, out nm) ? nm.name : (generated.TryGetValue(id, out value) ? value : "")); string text2 = Alt(id); if (text2 != null) { names.Remove(text2); } if (name.Length == 0) { names.Remove(id); } else { names[id] = new Named { name = name, by = by, t = DateTimeOffset.UtcNow.ToUnixTimeSeconds() }; } Rebuild(); Save(); log = ((by.Length > 0) ? by : "someone") + " named the " + feature.kind + " '" + text + "' " + ((name.Length == 0) ? "back to its own name" : ("'" + name + "'")); ZLog.Log((object)("WebMap: " + log)); Discord.Tell(log); } return null; } private static bool ParseHub(string id, out float x, out float z) { x = (z = 0f); Match match = Regex.Match(id, "^hub@(-?\\d+(?:\\.\\d+)?),(-?\\d+(?:\\.\\d+)?)$"); if (match.Success && float.TryParse(match.Groups[1].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out x)) { return float.TryParse(match.Groups[2].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out z); } return false; } private static string SetHubName(string id, string name, string by, out string log) { log = null; if (!ParseHub(id, out var x, out var z)) { return "no such place"; } float[] positions = Portals.Positions; bool flag = false; for (int i = 0; i + 1 < positions.Length; i += 2) { if (Math.Abs(positions[i] - x) < 150f && Math.Abs(positions[i + 1] - z) < 150f && Math.Sqrt((positions[i] - x) * (positions[i] - x) + (positions[i + 1] - z) * (positions[i + 1] - z)) < 150.0) { flag = true; break; } } if (!flag) { return "no portal stands there"; } string key = Inv($"hub@{Math.Round(x)},{Math.Round(z)}"); lock (gate) { string text = ""; foreach (string item in new List<string>(names.Keys)) { if (item.StartsWith("hub@") && ParseHub(item, out var x2, out var z2) && Math.Sqrt((x2 - x) * (x2 - x) + (z2 - z) * (z2 - z)) < 150.0) { text = names[item].name; names.Remove(item); } } if (name.Length > 0) { names[key] = new Named { name = name, by = by, t = DateTimeOffset.UtcNow.ToUnixTimeSeconds() }; } Save(); log = ((by.Length > 0) ? by : "someone") + " named the hub " + ((text.Length > 0) ? ("'" + text + "' ") : "") + ((name.Length == 0) ? "back to its gates" : ("'" + name + "'")); ZLog.Log((object)("WebMap: " + log)); Discord.Tell(log); } return null; } public static string HubNamesJson() { StringBuilder stringBuilder = new StringBuilder("["); int num = 0; lock (gate) { foreach (KeyValuePair<string, Named> name in names) { if (name.Key.StartsWith("hub@") && ParseHub(name.Key, out var x, out var z)) { if (num++ > 0) { stringBuilder.Append(','); } stringBuilder.Append(Inv($"{{\"id\":\"{Esc(name.Key)}\",\"x\":{x:0.#},\"z\":{z:0.#},\"name\":\"{Esc(name.Value.name)}\",\"by\":\"{Esc(name.Value.by)}\"}}")); } } } return stringBuilder.Append(']').ToString(); } private static void Save() { try { StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair<string, Named> name in names) { stringBuilder.Append(name.Key).Append('\t').Append(name.Value.name.Replace('\t', ' ')) .Append('\t') .Append((name.Value.by ?? "").Replace('\t', ' ')) .Append('\t') .Append(name.Value.t.ToString(CultureInfo.InvariantCulture)) .Append('\n'); } string text = Path.Combine(dir, "names.tsv"); string text2 = text + ".new"; File.WriteAllText(text2, stringBuilder.ToString()); if (File.Exists(text)) { File.Replace(text2, text, null); } else { File.Move(text2, text); } } catch (Exception ex) { ZLog.LogWarning((object)("WebMap: names not saved: " + ex.Message)); } } public static bool ParseBody(string body, out string id, out string name, string a = "id", string b = "name") { id = Body.Str(body, a); name = Body.Str(body, b); if (id != null) { return name != null; } return false; } internal static void ResetForTests(string worldDataPath) { lock (gate) { generated.Clear(); found = new Feature[0]; grids = null; dir = worldDataPath; LoadNames(); } } internal static void AnalyseForTests(byte[] cls, float[] hgt, List<List<Vector2>> rivers, string seed) { Analyse(cls, hgt, rivers, (float)(WebMapConfig.TEXTURE_SIZE * WebMapConfig.PIXEL_SIZE) / 1024f, seed); } } internal static class ForestMap { private enum Kind { Other, Tree, Stump } private struct Cell { public int trees; public int stumps; } private const float Steepness = 225f; private const float HalfShade = 2f; private const float MaxShade = 200f; private static readonly Dictionary<int, Cell> cells = new Dictionary<int, Cell>(); private static readonly Dictionary<int, Kind> kindCache = new Dictionary<int, Kind>(); private static byte[] rgba; private static volatile byte[] png; public static volatile int Rev; private static volatile string statsJson = "{\"trees\":0,\"stumps\":0}"; public static int LastTrees { get; private set; } public static int LastStumps { get; private set; } private static Kind Classify(int prefabHash) { if (kindCache.TryGetValue(prefabHash, out var value)) { return value; } string text = null; try { GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefabHash) : null); if ((Object)(object)val != (Object)null) { text = ((Object)val).name.ToLowerInvariant(); } } catch { } Kind kind = ((text != null) ? (text.Contains("_stub") ? Kind.Stump : ((!text.Contains("_log") && !text.Contains("logs")) ? ((text.Contains("tree") || text.Contains("beech") || text.Contains("birch") || text.Contains("oak") || text.Contains("yggashoot")) ? Kind.Tree : Kind.Other) : Kind.Stump)) : Kind.Other); kindCache[prefabHash] = kind; return kind; } public static void Begin() { cells.Clear(); LastTrees = 0; LastStumps = 0; if (rgba == null) { int tEXTURE_SIZE = WebMapConfig.TEXTURE_SIZE; rgba = new byte[tEXTURE_SIZE * tEXTURE_SIZE * 4]; } } public static void Observe(int prefabHash, int idx)
websocket-sharp.dll
Decompiled 2 weeks 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);