Please disclose if your mod was created primarily using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of TITSLethalCompany v1.0.1
BepInEx/plugins/TITSLethalCompany.dll
Decompiled 2 years agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net.WebSockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using LC_API.GameInterfaceAPI.Features; using LC_API.Networking; using LC_API.Networking.Serializers; using Microsoft.CodeAnalysis; using NativeWebSocket; using Newtonsoft.Json; using TITSLethalCompany.Networking; using TITSLethalCompany.TITSApi; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("TITSLethalCompany")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("My first plugin")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1+1a489bfe6491453b98bc71f004832e306db4404a")] [assembly: AssemblyProduct("TITSLethalCompany")] [assembly: AssemblyTitle("TITSLethalCompany")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } public class MainThreadUtil : MonoBehaviour { public static MainThreadUtil Instance { get; private set; } public static SynchronizationContext synchronizationContext { get; private set; } [RuntimeInitializeOnLoadMethod(/*Could not decode attribute arguments.*/)] public static void Setup() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) Instance = new GameObject("MainThreadUtil").AddComponent<MainThreadUtil>(); synchronizationContext = SynchronizationContext.Current; } public static void Run(IEnumerator waitForUpdate) { IEnumerator waitForUpdate2 = waitForUpdate; synchronizationContext.Post(delegate { ((MonoBehaviour)Instance).StartCoroutine(waitForUpdate2); }, null); } private void Awake() { ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); } } public class WaitForUpdate : CustomYieldInstruction { public class MainThreadAwaiter : INotifyCompletion { private Action continuation; public bool IsCompleted { get; set; } public void GetResult() { } public void Complete() { IsCompleted = true; continuation?.Invoke(); } void INotifyCompletion.OnCompleted(Action continuation) { this.continuation = continuation; } } public override bool keepWaiting => false; public MainThreadAwaiter GetAwaiter() { MainThreadAwaiter mainThreadAwaiter = new MainThreadAwaiter(); MainThreadUtil.Run(CoroutineWrapper((IEnumerator)this, mainThreadAwaiter)); return mainThreadAwaiter; } public static IEnumerator CoroutineWrapper(IEnumerator theWorker, MainThreadAwaiter awaiter) { yield return theWorker; awaiter.Complete(); } } namespace TITSLethalCompany { public static class ConfigManager { public static string TITSPort = "42069"; public static int ItemDamage = 10; public static bool PullPinOnStunGrenade = true; public static bool DamageFromAnyItem = false; public static bool SpawnItems = true; private static readonly string configPath = Path.Combine(Paths.ConfigPath, "TITSLethalCompany.cfg"); private static ConfigFile? configFile; public static void LoadConfig() { EnsureConfigExists(); if (configFile != null) { TITSPort = GetConfigValue<string>("TITSPort", TITSPort, "What port to use for TITS API."); ItemDamage = GetConfigValue<int>("ItemDamage", ItemDamage, "How much damage should throwing items deal to the player. Set to 0 or lower to disable damage."); SpawnItems = GetConfigValue<bool>("SpawnItems", SpawnItems, "Set to false to disable spawning items completely."); PullPinOnStunGrenade = GetConfigValue<bool>("PullPinOnStunGrenade", PullPinOnStunGrenade, "If a stun grenade is thrown at the player should the pin be automatically pulled."); DamageFromAnyItem = GetConfigValue<bool>("DamageFromAnyItem", DamageFromAnyItem, "When set to false the damage will only be applied if the thrown item is a recognized Lethal Company item, set to true for all thrown items to cause damage."); } static T GetConfigValue<T>(string key, T defaultValue, string? description = null) { return configFile.Bind<T>("Settings", key, defaultValue, description).Value; } } private static void EnsureConfigExists() { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown if (configFile != null) { return; } if (!File.Exists(configPath)) { using (File.CreateText(configPath)) { } } configFile = new ConfigFile(configPath, true); } } [HarmonyPatch(typeof(TimeOfDay))] public class TimeOfDayPatch { [HarmonyPrefix] [HarmonyPatch("Update")] public static void Update(ref TimeOfDay __instance) { Plugin.Instance?.TimeOfDayUpdate(); } [HarmonyPrefix] [HarmonyPatch("Awake")] public static void Awake(ref TimeOfDay __instance) { Plugin.Instance?.OnTimeOfDayAwake(); } } [BepInPlugin("TITSLethalCompany", "TITSLethalCompany", "1.0.1")] public class Plugin : BaseUnityPlugin { public static Plugin? Instance; public static ManualLogSource StaticLogger; private readonly TITSWebSocket webSocket = new TITSWebSocket(); public static void OnItemHit(TITSHitResponseData data) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) string itemName = data.itemName; Utils.LogInfo("Threw item: " + itemName); bool flag = Utils.FindItemByName(itemName) != null; GameNetworkManager instance = GameNetworkManager.Instance; if (instance == null) { return; } PlayerControllerB localPlayerController = instance.localPlayerController; if (localPlayerController != null) { bool flag2 = flag || ConfigManager.DamageFromAnyItem; if (ConfigManager.ItemDamage > 0 && flag2) { localPlayerController.DamagePlayer(ConfigManager.ItemDamage, true, true, (CauseOfDeath)6, 0, false, default(Vector3)); } if (ConfigManager.SpawnItems && flag) { NetworkHandler.NetSpawnItemOnPlayer(itemName, ((Component)localPlayerController).transform.position, localPlayerController.isInElevator); } } } public static void SpawnItemOnPlayer(string itemName, Vector3 pos, bool inElevator) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) Transform val = (inElevator ? StartOfRound.Instance.elevatorTransform : StartOfRound.Instance.propsContainer); Item val2 = Utils.FindItemByName(itemName); if (val2 == null) { Utils.LogError("No matching item found for " + itemName); return; } GameObject val3 = Object.Instantiate<GameObject>(val2.spawnPrefab, pos, Quaternion.identity, val); GrabbableObject component = val3.GetComponent<GrabbableObject>(); if (component != null) { component.SetScrapValue(0); component.fallTime = 0f; } NetworkObject component2 = val3.GetComponent<NetworkObject>(); if (component2 != null) { component2.Spawn(false); } if (ConfigManager.PullPinOnStunGrenade) { StunGrenadeItem component3 = val3.GetComponent<StunGrenadeItem>(); if (component3 != null) { NetworkHandler.NetPullStunGrenadePin(component3); } } } public void OnTimeOfDayAwake() { webSocket.ConnectWebSockets(); } public void TimeOfDayUpdate() { webSocket.Update(); } public void Awake() { ConfigManager.LoadConfig(); NetworkHandler.Register(); StaticLogger = ((BaseUnityPlugin)this).Logger; Instance = this; ((BaseUnityPlugin)this).Logger.LogInfo((object)"TITSLethalCompany 1.0.1 loaded"); Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null); } } public static class Utils { public static Item? FindItemByName(string itemName) { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return null; } AllItemsList allItemsList = instance.allItemsList; if ((Object)(object)allItemsList == (Object)null) { return null; } List<Item> itemsList = allItemsList.itemsList; if (itemsList == null) { return null; } foreach (Item item in itemsList) { if (!item.itemName.Equals(itemName, StringComparison.OrdinalIgnoreCase)) { continue; } return item; } return null; } public static void LogInfo(string msg) { Plugin.StaticLogger.LogInfo((object)("TITSLethalCompany: " + msg)); } public static void LogError(string msg) { Plugin.StaticLogger.LogError((object)("TITSLethalCompany: " + msg)); } } public static class PluginInfo { public const string PLUGIN_GUID = "TITSLethalCompany"; public const string PLUGIN_NAME = "TITSLethalCompany"; public const string PLUGIN_VERSION = "1.0.1"; } } namespace TITSLethalCompany.TITSApi { public record struct TITSApiError(long timestamp, string requestID, string messageType, string apiName, Version apiVersion, TITSApiErrorData data); public record struct TITSApiErrorData(int errorId, string message); public record struct TITSApiResponse(long timestamp, string requestID, string apiName, Version apiVersion, string messageType); public record struct TITSHitEvent(long timestamp, string requestID, string apiName, Version apiVersion, string messageType, TITSHitResponseData data); public record struct TITSHitResponseData(Guid itemId, string itemName, Guid triggerId, string triggerName, float strength, HitVector3 direction); public record struct HitVector3(float x, float y, float z); public class TITSWebSocket { private NativeWebSocket.WebSocket? eventsClient; private static string eventsWebSocketUri => "ws://localhost:" + ConfigManager.TITSPort + "/events"; public async void ConnectWebSockets() { Close(); eventsClient = WebSocketFactory.CreateInstance(eventsWebSocketUri); eventsClient.OnOpen += delegate { Utils.LogInfo("TITS WebSocket Connected!"); }; eventsClient.OnError += delegate(string e) { Utils.LogError("TITS WebSocket Error: " + e); }; eventsClient.OnClose += delegate(WebSocketCloseCode e) { Utils.LogInfo($"TITS WebSocket Disconnected: {e}"); }; eventsClient.OnMessage += delegate(byte[] msg) { string @string = Encoding.UTF8.GetString(msg); string messageType = JsonConvert.DeserializeObject<TITSApiResponse>(@string).messageType; string text = messageType; if (!(text == "TITSHitEvent")) { if (text == "TITSApiError") { Utils.LogError("Received TITSAPIError: " + JsonConvert.DeserializeObject<TITSApiError>(@string).data.message); } } else { Plugin.OnItemHit(JsonConvert.DeserializeObject<TITSHitEvent>(@string).data); } }; await eventsClient.Connect(); } private async void Close() { if (eventsClient != null) { await eventsClient.Close(); } } public void Update() { eventsClient?.DispatchMessageQueue(); } } } namespace TITSLethalCompany.Networking { public class NetPullFlashBangPin { public ulong NetworkId { get; set; } } public class NetSpawnItemOnPlayer { public string ItemName { get; set; } = string.Empty; public Vector3S Position { get; set; } public bool InElevator { get; set; } } public static class NetworkHandler { private const string ItemHitNetworkMessage = "TITSItemHit"; private const string FlashBangPullPinNetworkMessage = "TITSFlashBangPullPin"; public static void Register() { Network.RegisterAll(); } [NetworkMessage("TITSItemHit", true)] public static void TITSItemHitHandler(ulong sender, NetSpawnItemOnPlayer message) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) GameNetworkManager instance = GameNetworkManager.Instance; if (instance != null) { PlayerControllerB localPlayerController = instance.localPlayerController; if (localPlayerController != null && ((NetworkBehaviour)localPlayerController).IsHost) { Plugin.SpawnItemOnPlayer(message.ItemName, Vector3S.op_Implicit(message.Position), message.InElevator); } } } [NetworkMessage("TITSFlashBangPullPin", true)] public static void TITSFlashBangPullPinHandler(ulong sender, NetPullFlashBangPin message) { Item val = default(Item); if (Item.TryGet(message.NetworkId, ref val)) { StunGrenadeItem component = ((Component)val).GetComponent<StunGrenadeItem>(); if (component != null) { component.pinPulled = true; } } } public static void NetSpawnItemOnPlayer(string itemName, Vector3 pos, bool inElevator) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) Network.Broadcast<NetSpawnItemOnPlayer>("TITSItemHit", new NetSpawnItemOnPlayer { ItemName = itemName, Position = Vector3S.op_Implicit(pos), InElevator = inElevator }); } public static void NetPullStunGrenadePin(StunGrenadeItem item) { Network.Broadcast<NetPullFlashBangPin>("TITSFlashBangPullPin", new NetPullFlashBangPin { NetworkId = ((NetworkBehaviour)item).NetworkObjectId }); } } } namespace NativeWebSocket { public delegate void WebSocketOpenEventHandler(); public delegate void WebSocketMessageEventHandler(byte[] data); public delegate void WebSocketErrorEventHandler(string errorMsg); public delegate void WebSocketCloseEventHandler(WebSocketCloseCode closeCode); public enum WebSocketCloseCode { NotSet = 0, Normal = 1000, Away = 1001, ProtocolError = 1002, UnsupportedData = 1003, Undefined = 1004, NoStatus = 1005, Abnormal = 1006, InvalidData = 1007, PolicyViolation = 1008, TooBig = 1009, MandatoryExtension = 1010, ServerError = 1011, TlsHandshakeFailure = 1015 } public enum WebSocketState { Connecting, Open, Closing, Closed } public interface IWebSocket { WebSocketState State { get; } event WebSocketOpenEventHandler OnOpen; event WebSocketMessageEventHandler OnMessage; event WebSocketErrorEventHandler OnError; event WebSocketCloseEventHandler OnClose; } public static class WebSocketHelpers { public static WebSocketCloseCode ParseCloseCodeEnum(int closeCode) { if (Enum.IsDefined(typeof(WebSocketCloseCode), closeCode)) { return (WebSocketCloseCode)closeCode; } return WebSocketCloseCode.Undefined; } public static WebSocketException GetErrorMessageFromCode(int errorCode, Exception inner) { return errorCode switch { -1 => new WebSocketUnexpectedException("WebSocket instance not found.", inner), -2 => new WebSocketInvalidStateException("WebSocket is already connected or in connecting state.", inner), -3 => new WebSocketInvalidStateException("WebSocket is not connected.", inner), -4 => new WebSocketInvalidStateException("WebSocket is already closing.", inner), -5 => new WebSocketInvalidStateException("WebSocket is already closed.", inner), -6 => new WebSocketInvalidStateException("WebSocket is not in open state.", inner), -7 => new WebSocketInvalidArgumentException("Cannot close WebSocket. An invalid code was specified or reason is too long.", inner), _ => new WebSocketUnexpectedException("Unknown error.", inner), }; } } public class WebSocketException : Exception { public WebSocketException() { } public WebSocketException(string message) : base(message) { } public WebSocketException(string message, Exception inner) : base(message, inner) { } } public class WebSocketUnexpectedException : WebSocketException { public WebSocketUnexpectedException() { } public WebSocketUnexpectedException(string message) : base(message) { } public WebSocketUnexpectedException(string message, Exception inner) : base(message, inner) { } } public class WebSocketInvalidArgumentException : WebSocketException { public WebSocketInvalidArgumentException() { } public WebSocketInvalidArgumentException(string message) : base(message) { } public WebSocketInvalidArgumentException(string message, Exception inner) : base(message, inner) { } } public class WebSocketInvalidStateException : WebSocketException { public WebSocketInvalidStateException() { } public WebSocketInvalidStateException(string message) : base(message) { } public WebSocketInvalidStateException(string message, Exception inner) : base(message, inner) { } } public class WaitForBackgroundThread { public ConfiguredTaskAwaitable.ConfiguredTaskAwaiter GetAwaiter() { return Task.Run(delegate { }).ConfigureAwait(continueOnCapturedContext: false).GetAwaiter(); } } public class WebSocket : IWebSocket { private Uri uri; private Dictionary<string, string> headers; private List<string> subprotocols; private ClientWebSocket m_Socket = new ClientWebSocket(); private CancellationTokenSource m_TokenSource; private CancellationToken m_CancellationToken; private readonly object OutgoingMessageLock = new object(); private readonly object IncomingMessageLock = new object(); private bool isSending = false; private List<ArraySegment<byte>> sendBytesQueue = new List<ArraySegment<byte>>(); private List<ArraySegment<byte>> sendTextQueue = new List<ArraySegment<byte>>(); private List<byte[]> m_MessageList = new List<byte[]>(); public WebSocketState State { get { switch (m_Socket.State) { case System.Net.WebSockets.WebSocketState.Connecting: return WebSocketState.Connecting; case System.Net.WebSockets.WebSocketState.Open: return WebSocketState.Open; case System.Net.WebSockets.WebSocketState.CloseSent: case System.Net.WebSockets.WebSocketState.CloseReceived: return WebSocketState.Closing; case System.Net.WebSockets.WebSocketState.Closed: return WebSocketState.Closed; default: return WebSocketState.Closed; } } } public event WebSocketOpenEventHandler OnOpen; public event WebSocketMessageEventHandler OnMessage; public event WebSocketErrorEventHandler OnError; public event WebSocketCloseEventHandler OnClose; public WebSocket(string url, Dictionary<string, string> headers = null) { uri = new Uri(url); if (headers == null) { this.headers = new Dictionary<string, string>(); } else { this.headers = headers; } subprotocols = new List<string>(); string scheme = uri.Scheme; if (!scheme.Equals("ws") && !scheme.Equals("wss")) { throw new ArgumentException("Unsupported protocol: " + scheme); } } public WebSocket(string url, string subprotocol, Dictionary<string, string> headers = null) { uri = new Uri(url); if (headers == null) { this.headers = new Dictionary<string, string>(); } else { this.headers = headers; } subprotocols = new List<string> { subprotocol }; string scheme = uri.Scheme; if (!scheme.Equals("ws") && !scheme.Equals("wss")) { throw new ArgumentException("Unsupported protocol: " + scheme); } } public WebSocket(string url, List<string> subprotocols, Dictionary<string, string> headers = null) { uri = new Uri(url); if (headers == null) { this.headers = new Dictionary<string, string>(); } else { this.headers = headers; } this.subprotocols = subprotocols; string scheme = uri.Scheme; if (!scheme.Equals("ws") && !scheme.Equals("wss")) { throw new ArgumentException("Unsupported protocol: " + scheme); } } public void CancelConnection() { m_TokenSource?.Cancel(); } public async Task Connect() { try { m_TokenSource = new CancellationTokenSource(); m_CancellationToken = m_TokenSource.Token; m_Socket = new ClientWebSocket(); foreach (KeyValuePair<string, string> header in headers) { m_Socket.Options.SetRequestHeader(header.Key, header.Value); } foreach (string subprotocol in subprotocols) { m_Socket.Options.AddSubProtocol(subprotocol); } await m_Socket.ConnectAsync(uri, m_CancellationToken); this.OnOpen?.Invoke(); await Receive(); } catch (Exception ex2) { Exception ex = ex2; this.OnError?.Invoke(ex.Message); this.OnClose?.Invoke(WebSocketCloseCode.Abnormal); } finally { if (m_Socket != null) { m_TokenSource.Cancel(); m_Socket.Dispose(); } } } public Task Send(byte[] bytes) { return SendMessage(sendBytesQueue, WebSocketMessageType.Binary, new ArraySegment<byte>(bytes)); } public Task SendText(string message) { byte[] bytes = Encoding.UTF8.GetBytes(message); return SendMessage(sendTextQueue, WebSocketMessageType.Text, new ArraySegment<byte>(bytes, 0, bytes.Length)); } private async Task SendMessage(List<ArraySegment<byte>> queue, WebSocketMessageType messageType, ArraySegment<byte> buffer) { if (buffer.Count == 0) { return; } bool sending; lock (OutgoingMessageLock) { sending = isSending; if (!isSending) { isSending = true; } } if (!sending) { if (!Monitor.TryEnter(m_Socket, 1000)) { await m_Socket.CloseAsync(WebSocketCloseStatus.InternalServerError, string.Empty, m_CancellationToken); return; } try { Task t = m_Socket.SendAsync(buffer, messageType, endOfMessage: true, m_CancellationToken); t.Wait(m_CancellationToken); } finally { Monitor.Exit(m_Socket); } lock (OutgoingMessageLock) { isSending = false; } await HandleQueue(queue, messageType); } else { lock (OutgoingMessageLock) { queue.Add(buffer); } } } private async Task HandleQueue(List<ArraySegment<byte>> queue, WebSocketMessageType messageType) { ArraySegment<byte> buffer = default(ArraySegment<byte>); lock (OutgoingMessageLock) { if (queue.Count > 0) { buffer = queue[0]; queue.RemoveAt(0); } } if (buffer.Count > 0) { await SendMessage(queue, messageType, buffer); } } public void DispatchMessageQueue() { if (m_MessageList.Count != 0) { List<byte[]> list; lock (IncomingMessageLock) { list = new List<byte[]>(m_MessageList); m_MessageList.Clear(); } int count = list.Count; for (int i = 0; i < count; i++) { this.OnMessage?.Invoke(list[i]); } } } public async Task Receive() { WebSocketCloseCode closeCode = WebSocketCloseCode.Abnormal; await new WaitForBackgroundThread(); ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[8192]); try { while (m_Socket.State == System.Net.WebSockets.WebSocketState.Open) { using MemoryStream ms = new MemoryStream(); WebSocketReceiveResult result; do { result = await m_Socket.ReceiveAsync(buffer, m_CancellationToken); ms.Write(buffer.Array, buffer.Offset, result.Count); } while (!result.EndOfMessage); ms.Seek(0L, SeekOrigin.Begin); if (result.MessageType == WebSocketMessageType.Text) { lock (IncomingMessageLock) { m_MessageList.Add(ms.ToArray()); } } else if (result.MessageType == WebSocketMessageType.Binary) { lock (IncomingMessageLock) { m_MessageList.Add(ms.ToArray()); } } else if (result.MessageType == WebSocketMessageType.Close) { await Close(); closeCode = WebSocketHelpers.ParseCloseCodeEnum((int)result.CloseStatus.Value); break; } } } catch (Exception) { m_TokenSource.Cancel(); } finally { await new WaitForUpdate(); this.OnClose?.Invoke(closeCode); } } public async Task Close() { if (State == WebSocketState.Open) { await m_Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, m_CancellationToken); } } } public static class WebSocketFactory { public static WebSocket CreateInstance(string url) { return new WebSocket(url); } } }