1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-16 05:37:19 +08:00

Allow unsubscribing from solo statistics updates

This is more of a safety item. To avoid potential duplicate key in
dictionary errors (and also avoid being slightly memory-leaky), allow
`SoloStatisticsWatcher` consumers to dispose of the subscriptions they
take out.
This commit is contained in:
Bartłomiej Dach 2022-12-28 07:50:10 +01:00
parent 04f9a354c3
commit 3c0b8af8f1
No known key found for this signature in database
3 changed files with 47 additions and 17 deletions

View File

@ -35,6 +35,8 @@ namespace osu.Game.Tests.Visual.Online
private Action<GetUsersRequest>? handleGetUsersRequest;
private Action<GetUserRequest>? handleGetUserRequest;
private IDisposable? subscription;
private readonly Dictionary<(int userId, string rulesetName), UserStatistics> serverSideStatistics = new Dictionary<(int userId, string rulesetName), UserStatistics>();
[SetUpSteps]
@ -246,6 +248,26 @@ namespace osu.Game.Tests.Visual.Online
AddAssert("values after are correct", () => update!.After.TotalScore, () => Is.EqualTo(6_000_000));
}
[Test]
public void TestStatisticsUpdateNotFiredAfterSubscriptionDisposal()
{
int userId = getUserId();
setUpUser(userId);
long scoreId = getScoreId();
var ruleset = new OsuRuleset().RulesetInfo;
SoloStatisticsUpdate? update = null;
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
AddStep("unsubscribe", () => subscription!.Dispose());
feignScoreProcessing(userId, ruleset, 5_000_000);
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, scoreId));
AddWaitStep("wait a bit", 5);
AddAssert("update not received", () => update == null);
}
private int nextUserId = 2000;
private long nextScoreId = 50000;
@ -266,7 +288,7 @@ namespace osu.Game.Tests.Visual.Online
}
private void registerForUpdates(long scoreId, RulesetInfo rulesetInfo, Action<SoloStatisticsUpdate> onUpdateReady) =>
AddStep("register for updates", () => watcher.RegisterForStatisticsUpdateAfter(
AddStep("register for updates", () => subscription = watcher.RegisterForStatisticsUpdateAfter(
new ScoreInfo(Beatmap.Value.BeatmapInfo, new OsuRuleset().RulesetInfo, new RealmUser())
{
Ruleset = rulesetInfo,

View File

@ -46,24 +46,30 @@ namespace osu.Game.Online.Solo
/// </summary>
/// <param name="score">The score to listen for the statistics update for.</param>
/// <param name="onUpdateReady">The callback to be invoked once the statistics update has been prepared.</param>
public void RegisterForStatisticsUpdateAfter(ScoreInfo score, Action<SoloStatisticsUpdate> onUpdateReady) => Schedule(() =>
/// <returns>An <see cref="IDisposable"/> representing the subscription. Disposing it is equivalent to unsubscribing from future notifications.</returns>
public IDisposable RegisterForStatisticsUpdateAfter(ScoreInfo score, Action<SoloStatisticsUpdate> onUpdateReady)
{
if (!api.IsLoggedIn)
return;
if (!score.Ruleset.IsLegacyRuleset() || score.OnlineID <= 0)
return;
var callback = new StatisticsUpdateCallback(score, onUpdateReady);
if (lastProcessedScoreId == score.OnlineID)
Schedule(() =>
{
requestStatisticsUpdate(api.LocalUser.Value.Id, callback);
return;
}
if (!api.IsLoggedIn)
return;
callbacks.Add(score.OnlineID, callback);
});
if (!score.Ruleset.IsLegacyRuleset() || score.OnlineID <= 0)
return;
var callback = new StatisticsUpdateCallback(score, onUpdateReady);
if (lastProcessedScoreId == score.OnlineID)
{
requestStatisticsUpdate(api.LocalUser.Value.Id, callback);
return;
}
callbacks.Add(score.OnlineID, callback);
});
return new InvokeOnDisposal(() => Schedule(() => callbacks.Remove(score.OnlineID)));
}
private void onUserChanged(APIUser? localUser) => Schedule(() =>
{

View File

@ -32,6 +32,7 @@ namespace osu.Game.Screens.Ranking
[Resolved]
private SoloStatisticsWatcher soloStatisticsWatcher { get; set; } = null!;
private IDisposable? statisticsSubscription;
private readonly Bindable<SoloStatisticsUpdate?> statisticsUpdate = new Bindable<SoloStatisticsUpdate?>();
public SoloResultsScreen(ScoreInfo score, bool allowRetry)
@ -44,7 +45,7 @@ namespace osu.Game.Screens.Ranking
base.LoadComplete();
if (ShowUserStatistics)
soloStatisticsWatcher.RegisterForStatisticsUpdateAfter(Score, update => statisticsUpdate.Value = update);
statisticsSubscription = soloStatisticsWatcher.RegisterForStatisticsUpdateAfter(Score, update => statisticsUpdate.Value = update);
}
protected override StatisticsPanel CreateStatisticsPanel()
@ -75,6 +76,7 @@ namespace osu.Game.Screens.Ranking
base.Dispose(isDisposing);
getScoreRequest?.Cancel();
statisticsSubscription?.Dispose();
}
}
}