Add project files.
This commit is contained in:
140
LeagueAPI/LcuWebsocket.cs
Normal file
140
LeagueAPI/LcuWebsocket.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using LeagueAPI.Utils;
|
||||
|
||||
namespace LeagueAPI;
|
||||
|
||||
public class LcuWebsocket : IDisposable
|
||||
{
|
||||
private const int OPCODE_SUBSCRIBE = 5;
|
||||
private const int OPCODE_UNSUBSCRIBE = 6;
|
||||
private const int OPCODE_SEND = 8;
|
||||
private static readonly IReadOnlyList<string> SUBSCRIBE_EVENTS =
|
||||
[
|
||||
"OnJsonApiEvent_lol-champ-select_v1_session",
|
||||
"OnJsonApiEvent_lol-matchmaking_v1_search",
|
||||
"OnJsonApiEvent_lol-matchmaking_v1_ready-check",
|
||||
];
|
||||
|
||||
private bool _isDisposed;
|
||||
|
||||
private readonly ClientWebSocket _socket = new();
|
||||
|
||||
public event EventHandler<LcuApiEvent>? LcuApiEvent;
|
||||
public event EventHandler<Exception>? LcuApiException;
|
||||
|
||||
[MemberNotNull(nameof(RiotAuthentication))]
|
||||
public ProcessInfo? ProcessInfo { get; internal set; }
|
||||
public RiotAuthentication? RiotAuthentication
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ProcessInfo is not null)
|
||||
{
|
||||
return new RiotAuthentication(ProcessInfo.RemotingAuthToken);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Connect()
|
||||
{
|
||||
ProcessInfo = ProcessFinder.GetProcessInfo();
|
||||
if (!ProcessFinder.IsPortOpen(ProcessInfo))
|
||||
{
|
||||
throw new InvalidOperationException("Failed to connect to LCUx process port.");
|
||||
}
|
||||
|
||||
// ignore SSL certificate errors
|
||||
_socket.Options.RemoteCertificateValidationCallback = (_, _, _, _) => true;
|
||||
_socket.Options.Credentials = RiotAuthentication.ToNetworkCredential();
|
||||
|
||||
Uri uri = new($"wss://127.0.0.1:{ProcessInfo.AppPort}/");
|
||||
try
|
||||
{
|
||||
await _socket.ConnectAsync(uri, CancellationToken.None);
|
||||
|
||||
foreach (string eventName in SUBSCRIBE_EVENTS)
|
||||
{
|
||||
string message = $"[{OPCODE_SUBSCRIBE}, \"{eventName}\"]";
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(message);
|
||||
Memory<byte> memory = new(buffer);
|
||||
await _socket.SendAsync(memory, WebSocketMessageType.Text, true, CancellationToken.None);
|
||||
}
|
||||
|
||||
while (_socket.State is WebSocketState.Open)
|
||||
{
|
||||
try
|
||||
{
|
||||
WebsocketMessageResult result = await _socket.ReadFullMessage(CancellationToken.None);
|
||||
if (result.MessageType is WebSocketMessageType.Close)
|
||||
{
|
||||
await _socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result.Message.Length == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
JsonElement response = JsonElement.Parse(result.Message);
|
||||
if (response.ValueKind != JsonValueKind.Array
|
||||
|| response.GetArrayLength() != 3
|
||||
|| !response[0].TryGetInt32(out int value)
|
||||
|| value != OPCODE_SEND)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
LcuApiEvent? apiEvent = response[2].Deserialize<LcuApiEvent>();
|
||||
if (apiEvent is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
LcuApiEvent?.Invoke(this, apiEvent);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LcuApiException?.Invoke(this, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (ex is not WebSocketException)
|
||||
{
|
||||
LcuApiException?.Invoke(this, ex);
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_isDisposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (_socket.State == WebSocketState.Open)
|
||||
{
|
||||
_socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None)
|
||||
.Wait();
|
||||
}
|
||||
_socket?.Dispose();
|
||||
ProcessInfo = null;
|
||||
}
|
||||
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user