1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-17 08:42:35 +08:00
Files
osu-lazer/osu.Game.Tests/IPC/WebSocketClient.cs
T
Bartłomiej Dach 9a2846539f Implement WebSocket server skeleton for external integrations (#37335)
- Supersedes / closes https://github.com/ppy/osu/pull/18129. Reasons I
didn't use that PR are hopefully obvious upon comparing diffs but I can
elaborate if they are not.
- Single metric included for demonstration purposes.
- Do not want to talk about further schema design at this time.
- Specify `OSU_WEBSOCKET_SERVER=1` envvar to enable.
- Can test consumption with [this five minute html
job](https://github.com/user-attachments/files/26839923/index.html)
(works even as a standalone file opened in browser, no CORS bs!)
- There's a lot of inline comments, go read them. There are many WTFs
because the .NET frozen websocket API is weird and stanky and reeks of
the year 2007. The inline comments attempt to explain.
2026-04-21 18:25:12 +09:00

62 lines
1.8 KiB
C#

// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using osu.Game.IPC;
namespace osu.Game.Tests.IPC
{
public sealed class WebSocketClient : IDisposable
{
public event Action<string>? MessageReceived;
public event Action? Closed;
private readonly int port;
private WebSocketChannel? channel;
public WebSocketClient(int port)
{
this.port = port;
}
public async Task Start(CancellationToken cancellationToken = default)
{
var webSocket = new ClientWebSocket();
await webSocket.ConnectAsync(new Uri($@"ws://localhost:{port}/"), cancellationToken);
channel = new WebSocketChannel(webSocket);
channel.MessageReceived += msg => MessageReceived?.Invoke(msg);
channel.ClosedPrematurely += () => Closed?.Invoke();
channel.Start(cancellationToken);
}
public async Task SendAsync(string message)
{
if (channel == null)
throw new InvalidOperationException($@"Must {nameof(Start)} first.");
await channel.SendAsync(message);
}
public async Task StopAsync(CancellationToken stoppingToken = default)
{
try
{
if (channel != null)
await channel.StopAsync(stoppingToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// has to be caught manually because outer task isn't accepting `stoppingToken`.
}
}
public void Dispose()
{
channel?.Dispose();
}
}
}