Files
Shogi/Shogi.UI/Shared/ShogiSocket.cs

98 lines
3.3 KiB
C#

using Microsoft.AspNetCore.Http.Extensions;
using Shogi.Contracts.Socket;
using Shogi.Contracts.Types;
using System.Buffers;
using System.Net.WebSockets;
using System.Text.Json;
namespace Shogi.UI.Shared;
public class ShogiSocket : IDisposable
{
public event EventHandler<SessionCreatedSocketMessage>? OnCreateGameMessage;
private readonly ClientWebSocket socket;
private readonly JsonSerializerOptions serializerOptions;
private readonly UriBuilder uriBuilder;
private readonly CancellationTokenSource cancelToken;
private readonly IMemoryOwner<byte> memoryOwner;
private bool disposedValue;
public ShogiSocket(IConfiguration configuration, ClientWebSocket socket, JsonSerializerOptions serializerOptions)
{
this.socket = socket;
this.serializerOptions = serializerOptions;
this.uriBuilder = new UriBuilder(configuration["SocketUrl"] ?? throw new InvalidOperationException("SocketUrl configuration is missing."));
this.cancelToken = new CancellationTokenSource();
this.memoryOwner = MemoryPool<byte>.Shared.Rent(1024 * 2);
}
public async Task OpenAsync(string token)
{
uriBuilder.Query = new QueryBuilder
{
{ "token", token }
}.ToQueryString().Value;
await socket.ConnectAsync(this.uriBuilder.Uri, cancelToken.Token);
Console.WriteLine("Socket Connected");
// Fire and forget! I'm way too lazy to write my own javascript interop to a web worker. Nooo thanks.
var listening = Listen().ContinueWith(antecedent =>
{
if (antecedent.Exception != null)
{
throw antecedent.Exception;
}
}, TaskContinuationOptions.OnlyOnFaulted);
}
private async Task Listen()
{
while (socket.State == WebSocketState.Open && !cancelToken.IsCancellationRequested)
{
var result = await socket.ReceiveAsync(this.memoryOwner.Memory, cancelToken.Token);
var memory = this.memoryOwner.Memory[..result.Count].ToArray();
var action = JsonDocument
.Parse(memory[..result.Count])
.RootElement
.GetProperty(nameof(ISocketResponse.Action))
.Deserialize<SocketAction>();
switch (action)
{
case SocketAction.SessionCreated:
Console.WriteLine("Session created event.");
this.OnCreateGameMessage?.Invoke(this, JsonSerializer.Deserialize<SessionCreatedSocketMessage>(memory, this.serializerOptions)!);
break;
default:
break;
}
}
if (!cancelToken.IsCancellationRequested)
{
throw new InvalidOperationException("Stopped socket listening without cancelling.");
}
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
cancelToken.Cancel();
socket.Dispose();
memoryOwner.Dispose();
}
disposedValue = true;
}
}
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}