1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-17 21:13:01 +08:00
Files
osu-lazer/osu.Desktop/IPC/OsuWebSocketProvider.cs
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

76 lines
2.3 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.Linq;
using System.Threading;
using osu.Desktop.IPC.Messages;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Logging;
using osu.Game.Configuration;
using osu.Game.IPC;
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using JsonConvert = Newtonsoft.Json.JsonConvert;
namespace osu.Desktop.IPC
{
public partial class OsuWebSocketProvider : Component
{
private WebSocketServer? server;
private readonly Bindable<ScoreInfo> lastLocalScore = new Bindable<ScoreInfo>();
[BackgroundDependencyLoader]
private void load(SessionStatics sessionStatics)
{
server = new WebSocketServer(49727);
server.StartAsync().FireAndForget(onError: ex => Logger.Error(ex, "Failed to start websocket"));
sessionStatics.BindWith(Static.LastLocalUserScore, lastLocalScore);
}
protected override void LoadComplete()
{
base.LoadComplete();
lastLocalScore.BindValueChanged(val =>
{
if (val.NewValue == null)
return;
if (server?.IsRunning != true)
return;
var msg = new HitCountMessage { NewHits = val.NewValue.Statistics.Where(kv => kv.Key.IsBasic() && kv.Key.IsHit()).Sum(kv => kv.Value) };
broadcast(msg);
});
}
private void broadcast(OsuWebSocketMessage message)
{
if (server?.IsRunning != true)
return;
string messageString = JsonConvert.SerializeObject(message);
server.BroadcastAsync(messageString).FireAndForget();
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (server?.IsRunning == true)
{
var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(10));
server.StopAsync(cts.Token).WaitSafely();
server = null;
}
}
}
}