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);
}
}
}