Decompiled source of TITSLethalCompany v1.0.1

BepInEx/plugins/TITSLethalCompany.dll

Decompiled 10 months ago
using 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);
		}
	}
}