From d6e7781be13821b52baafd1a06cb003344b43341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 27 Jun 2024 10:44:28 +0200 Subject: [PATCH] Add client/server models for allowing clients to receive realtime playlist updates --- osu.Game/Online/Metadata/IMetadataClient.cs | 6 +++ osu.Game/Online/Metadata/IMetadataServer.cs | 10 ++++ osu.Game/Online/Metadata/MetadataClient.cs | 18 +++++++ .../Metadata/MultiplayerPlaylistItemStats.cs | 29 +++++++++++ .../Metadata/MultiplayerRoomScoreSetEvent.cs | 50 +++++++++++++++++++ .../Online/Metadata/OnlineMetadataClient.cs | 19 +++++++ .../DailyChallengeScoreBreakdown.cs | 3 +- .../Visual/Metadata/TestMetadataClient.cs | 5 ++ 8 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Online/Metadata/MultiplayerPlaylistItemStats.cs create mode 100644 osu.Game/Online/Metadata/MultiplayerRoomScoreSetEvent.cs diff --git a/osu.Game/Online/Metadata/IMetadataClient.cs b/osu.Game/Online/Metadata/IMetadataClient.cs index ee7a726bfc..97c1bbde5f 100644 --- a/osu.Game/Online/Metadata/IMetadataClient.cs +++ b/osu.Game/Online/Metadata/IMetadataClient.cs @@ -26,5 +26,11 @@ namespace osu.Game.Online.Metadata /// Null value means there is no "daily challenge" currently active. /// Task DailyChallengeUpdated(DailyChallengeInfo? info); + + /// + /// Delivers information that a multiplayer score was set in a watched room. + /// To receive these, the client must call for a given room first. + /// + Task MultiplayerRoomScoreSet(MultiplayerRoomScoreSetEvent roomScoreSetEvent); } } diff --git a/osu.Game/Online/Metadata/IMetadataServer.cs b/osu.Game/Online/Metadata/IMetadataServer.cs index 8bf3f8f56b..79ed8b5634 100644 --- a/osu.Game/Online/Metadata/IMetadataServer.cs +++ b/osu.Game/Online/Metadata/IMetadataServer.cs @@ -43,5 +43,15 @@ namespace osu.Game.Online.Metadata /// Signals to the server that the current user would like to stop receiving updates on other users' online presence. /// Task EndWatchingUserPresence(); + + /// + /// Signals to the server that the current user would like to begin receiving updates about the state of the multiplayer room with the given . + /// + Task BeginWatchingMultiplayerRoom(long id); + + /// + /// Signals to the server that the current user would like to stop receiving updates about the state of the multiplayer room with the given . + /// + Task EndWatchingMultiplayerRoom(long id); } } diff --git a/osu.Game/Online/Metadata/MetadataClient.cs b/osu.Game/Online/Metadata/MetadataClient.cs index b619970494..8a5fe1733e 100644 --- a/osu.Game/Online/Metadata/MetadataClient.cs +++ b/osu.Game/Online/Metadata/MetadataClient.cs @@ -68,6 +68,24 @@ namespace osu.Game.Online.Metadata #endregion + #region Multiplayer room watching + + public abstract Task BeginWatchingMultiplayerRoom(long id); + + public abstract Task EndWatchingMultiplayerRoom(long id); + + public event Action? MultiplayerRoomScoreSet; + + Task IMetadataClient.MultiplayerRoomScoreSet(MultiplayerRoomScoreSetEvent roomScoreSetEvent) + { + if (MultiplayerRoomScoreSet != null) + Schedule(MultiplayerRoomScoreSet, roomScoreSetEvent); + + return Task.CompletedTask; + } + + #endregion + #region Disconnection handling public event Action? Disconnecting; diff --git a/osu.Game/Online/Metadata/MultiplayerPlaylistItemStats.cs b/osu.Game/Online/Metadata/MultiplayerPlaylistItemStats.cs new file mode 100644 index 0000000000..d13705bf5b --- /dev/null +++ b/osu.Game/Online/Metadata/MultiplayerPlaylistItemStats.cs @@ -0,0 +1,29 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using MessagePack; + +namespace osu.Game.Online.Metadata +{ + [MessagePackObject] + [Serializable] + public class MultiplayerPlaylistItemStats + { + public const int TOTAL_SCORE_DISTRIBUTION_BINS = 13; + + /// + /// The ID of the playlist item which these stats pertain to. + /// + [Key(0)] + public long PlaylistItemID { get; set; } + + /// + /// The count of scores with given total ranges in the room. + /// The ranges are bracketed into bins, each of 100,000 score width. + /// The last bin will contain count of all scores with total of 1,200,000 or larger. + /// + [Key(1)] + public long[] TotalScoreDistribution { get; set; } = new long[TOTAL_SCORE_DISTRIBUTION_BINS]; + } +} diff --git a/osu.Game/Online/Metadata/MultiplayerRoomScoreSetEvent.cs b/osu.Game/Online/Metadata/MultiplayerRoomScoreSetEvent.cs new file mode 100644 index 0000000000..00bc5dc840 --- /dev/null +++ b/osu.Game/Online/Metadata/MultiplayerRoomScoreSetEvent.cs @@ -0,0 +1,50 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using MessagePack; + +namespace osu.Game.Online.Metadata +{ + [Serializable] + [MessagePackObject] + public class MultiplayerRoomScoreSetEvent + { + /// + /// The ID of the room in which the score was set. + /// + [Key(0)] + public long RoomID { get; set; } + + /// + /// The ID of the playlist item on which the score was set. + /// + [Key(1)] + public long PlaylistItemID { get; set; } + + /// + /// The ID of the score set. + /// + [Key(2)] + public long ScoreID { get; set; } + + /// + /// The ID of the user who set the score. + /// + [Key(3)] + public int UserID { get; set; } + + /// + /// The total score set by the player. + /// + [Key(4)] + public long TotalScore { get; set; } + + /// + /// If the set score is the user's new best on a playlist item, this member will contain the user's new rank in the room overall. + /// Otherwise, it will contain . + /// + [Key(5)] + public int? NewRank { get; set; } + } +} diff --git a/osu.Game/Online/Metadata/OnlineMetadataClient.cs b/osu.Game/Online/Metadata/OnlineMetadataClient.cs index b94f26a71d..80fcf7571d 100644 --- a/osu.Game/Online/Metadata/OnlineMetadataClient.cs +++ b/osu.Game/Online/Metadata/OnlineMetadataClient.cs @@ -62,6 +62,7 @@ namespace osu.Game.Online.Metadata connection.On(nameof(IMetadataClient.BeatmapSetsUpdated), ((IMetadataClient)this).BeatmapSetsUpdated); connection.On(nameof(IMetadataClient.UserPresenceUpdated), ((IMetadataClient)this).UserPresenceUpdated); connection.On(nameof(IMetadataClient.DailyChallengeUpdated), ((IMetadataClient)this).DailyChallengeUpdated); + connection.On(nameof(IMetadataClient.MultiplayerRoomScoreSet), ((IMetadataClient)this).MultiplayerRoomScoreSet); connection.On(nameof(IStatefulUserHubClient.DisconnectRequested), ((IMetadataClient)this).DisconnectRequested); }; @@ -240,6 +241,24 @@ namespace osu.Game.Online.Metadata return Task.CompletedTask; } + public override async Task BeginWatchingMultiplayerRoom(long id) + { + if (connector?.IsConnected.Value != true) + throw new OperationCanceledException(); + + Debug.Assert(connection != null); + return await connection.InvokeAsync(nameof(IMetadataServer.BeginWatchingMultiplayerRoom), id).ConfigureAwait(false); + } + + public override async Task EndWatchingMultiplayerRoom(long id) + { + if (connector?.IsConnected.Value != true) + throw new OperationCanceledException(); + + Debug.Assert(connection != null); + await connection.InvokeAsync(nameof(IMetadataServer.EndWatchingMultiplayerRoom)).ConfigureAwait(false); + } + public override async Task DisconnectRequested() { await base.DisconnectRequested().ConfigureAwait(false); diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeScoreBreakdown.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeScoreBreakdown.cs index 3d4f27c44b..d251a10f9a 100644 --- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeScoreBreakdown.cs +++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeScoreBreakdown.cs @@ -11,6 +11,7 @@ using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Metadata; using osu.Game.Overlays; using osu.Game.Scoring; using osuTK; @@ -21,7 +22,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge { private FillFlowContainer barsContainer = null!; - private const int bin_count = 13; + private const int bin_count = MultiplayerPlaylistItemStats.TOTAL_SCORE_DISTRIBUTION_BINS; private long[] bins = new long[bin_count]; [BackgroundDependencyLoader] diff --git a/osu.Game/Tests/Visual/Metadata/TestMetadataClient.cs b/osu.Game/Tests/Visual/Metadata/TestMetadataClient.cs index b589e66d8b..fa64a83352 100644 --- a/osu.Game/Tests/Visual/Metadata/TestMetadataClient.cs +++ b/osu.Game/Tests/Visual/Metadata/TestMetadataClient.cs @@ -86,5 +86,10 @@ namespace osu.Game.Tests.Visual.Metadata dailyChallengeInfo.Value = info; return Task.CompletedTask; } + + public override Task BeginWatchingMultiplayerRoom(long id) + => Task.FromResult(new MultiplayerPlaylistItemStats[MultiplayerPlaylistItemStats.TOTAL_SCORE_DISTRIBUTION_BINS]); + + public override Task EndWatchingMultiplayerRoom(long id) => Task.CompletedTask; } }