osu/osu.Game/Online/Metadata/OnlineMetadataClient.cs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

132 lines
4.5 KiB
C#
Raw Normal View History

2022-07-04 08:18:33 +00:00
// 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.
2022-07-04 08:49:08 +00:00
using System.Diagnostics;
2022-07-04 08:18:33 +00:00
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client;
using osu.Framework.Allocation;
2022-07-04 08:49:08 +00:00
using osu.Framework.Bindables;
2022-07-04 08:18:33 +00:00
using osu.Framework.Logging;
2022-07-04 09:13:53 +00:00
using osu.Game.Beatmaps;
2022-07-04 08:18:33 +00:00
using osu.Game.Online.API;
namespace osu.Game.Online.Metadata
{
public class OnlineMetadataClient : MetadataClient
{
2022-07-04 09:13:53 +00:00
private readonly BeatmapUpdater beatmapUpdater;
2022-07-04 08:18:33 +00:00
private readonly string endpoint;
private IHubClientConnector? connector;
2022-07-04 08:49:08 +00:00
private uint? lastQueueId;
2022-07-04 08:18:33 +00:00
private HubConnection? connection => connector?.CurrentConnection;
2022-07-04 09:13:53 +00:00
public OnlineMetadataClient(EndpointConfiguration endpoints, BeatmapUpdater beatmapUpdater)
2022-07-04 08:18:33 +00:00
{
2022-07-04 09:13:53 +00:00
this.beatmapUpdater = beatmapUpdater;
2022-07-04 08:18:33 +00:00
endpoint = endpoints.MetadataEndpointUrl;
}
[BackgroundDependencyLoader]
private void load(IAPIProvider api)
{
// Importantly, we are intentionally not using MessagePack here to correctly support derived class serialization.
// More information on the limitations / reasoning can be found in osu-server-spectator's initialisation code.
connector = api.GetHubConnector(nameof(OnlineMetadataClient), endpoint);
if (connector != null)
{
connector.ConfigureConnection = connection =>
{
// this is kind of SILLY
// https://github.com/dotnet/aspnetcore/issues/15198
connection.On<BeatmapUpdates>(nameof(IMetadataClient.BeatmapSetsUpdated), ((IMetadataClient)this).BeatmapSetsUpdated);
};
2022-07-04 08:49:08 +00:00
connector.IsConnected.BindValueChanged(isConnectedChanged, true);
}
}
private bool catchingUp;
private void isConnectedChanged(ValueChangedEvent<bool> connected)
{
if (!connected.NewValue)
return;
if (lastQueueId != null)
{
catchingUp = true;
Task.Run(async () =>
{
try
{
while (true)
{
Logger.Log($"Requesting catch-up from {lastQueueId.Value}");
var catchUpChanges = await GetChangesSince(lastQueueId.Value);
lastQueueId = catchUpChanges.LastProcessedQueueID;
if (catchUpChanges.BeatmapSetIDs.Length == 0)
{
Logger.Log($"Catch-up complete at {lastQueueId.Value}");
break;
}
await ProcessChanges(catchUpChanges.BeatmapSetIDs);
}
}
finally
{
catchingUp = false;
}
});
2022-07-04 08:18:33 +00:00
}
}
2022-07-04 08:49:08 +00:00
public override async Task BeatmapSetsUpdated(BeatmapUpdates updates)
2022-07-04 08:18:33 +00:00
{
Logger.Log($"Received beatmap updates {updates.BeatmapSetIDs.Length} updates with last id {updates.LastProcessedQueueID}");
2022-07-04 08:49:08 +00:00
// If we're still catching up, avoid updating the last ID as it will interfere with catch-up efforts.
if (!catchingUp)
lastQueueId = updates.LastProcessedQueueID;
await ProcessChanges(updates.BeatmapSetIDs);
}
protected Task ProcessChanges(int[] beatmapSetIDs)
{
foreach (int id in beatmapSetIDs)
2022-07-04 09:13:53 +00:00
{
2022-07-04 08:49:08 +00:00
Logger.Log($"Processing {id}...");
2022-07-04 09:13:53 +00:00
beatmapUpdater.Queue(id);
}
2022-07-04 08:18:33 +00:00
return Task.CompletedTask;
}
public override Task<BeatmapUpdates> GetChangesSince(uint queueId)
{
2022-07-04 08:49:08 +00:00
if (connector?.IsConnected.Value != true)
return Task.FromCanceled<BeatmapUpdates>(default);
Logger.Log($"Requesting any changes since last known queue id {queueId}");
Debug.Assert(connection != null);
return connection.InvokeAsync<BeatmapUpdates>(nameof(IMetadataServer.GetChangesSince), queueId);
2022-07-04 08:18:33 +00:00
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
connector?.Dispose();
}
}
}