From 22d998dc2a518728c00d1f2d84c40aef12479110 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 30 May 2022 19:18:38 +0900 Subject: [PATCH] Use new score processor in MultiplayerGameplayLeaderboard --- ...MultiplayerGameplayLeaderboardTestScene.cs | 35 +++-- .../TestSceneMultiSpectatorLeaderboard.cs | 6 +- ...TestSceneMultiplayerGameplayLeaderboard.cs | 42 ++++- ...ceneMultiplayerGameplayLeaderboardTeams.cs | 5 +- osu.Game/Online/Spectator/SpectatorClient.cs | 2 +- .../Multiplayer/MultiplayerPlayer.cs | 11 +- .../Spectate/MultiSpectatorLeaderboard.cs | 48 +----- .../Spectate/MultiSpectatorScreen.cs | 8 +- .../HUD/MultiplayerGameplayLeaderboard.cs | 147 +++++------------- 9 files changed, 110 insertions(+), 194 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs index 4e6342868a..62cea378e6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs @@ -18,7 +18,6 @@ using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer; using osu.Game.Online.Spectator; using osu.Game.Replays.Legacy; -using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; @@ -27,7 +26,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { public abstract class MultiplayerGameplayLeaderboardTestScene : OsuTestScene { - private const int total_users = 16; + protected const int TOTAL_USERS = 16; protected readonly BindableList MultiplayerUsers = new BindableList(); @@ -35,9 +34,10 @@ namespace osu.Game.Tests.Visual.Multiplayer protected virtual MultiplayerRoomUser CreateUser(int userId) => new MultiplayerRoomUser(userId); - protected abstract MultiplayerGameplayLeaderboard CreateLeaderboard(OsuScoreProcessor scoreProcessor); + protected abstract MultiplayerGameplayLeaderboard CreateLeaderboard(); private readonly BindableList multiplayerUserIds = new BindableList(); + private readonly BindableDictionary watchedUserStates = new BindableDictionary(); private OsuConfigManager config; @@ -81,6 +81,9 @@ namespace osu.Game.Tests.Visual.Multiplayer multiplayerClient.SetupGet(c => c.CurrentMatchPlayingUserIds) .Returns(() => multiplayerUserIds); + + spectatorClient.SetupGet(c => c.WatchedUserStates) + .Returns(() => watchedUserStates); } [SetUpSteps] @@ -100,8 +103,23 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("populate users", () => { MultiplayerUsers.Clear(); - for (int i = 0; i < total_users; i++) - MultiplayerUsers.Add(CreateUser(i)); + + for (int i = 0; i < TOTAL_USERS; i++) + { + var user = CreateUser(i); + + MultiplayerUsers.Add(user); + + watchedUserStates[i] = new SpectatorState + { + BeatmapID = 0, + RulesetID = 0, + Mods = user.Mods, + MaxAchievableCombo = 1000, + MaxAchievableBaseScore = 10000, + TotalBasicHitObjects = 1000 + }; + } }); AddStep("create leaderboard", () => @@ -109,13 +127,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Leaderboard?.Expire(); Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); - var playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); - OsuScoreProcessor scoreProcessor = new OsuScoreProcessor(); - scoreProcessor.ApplyBeatmap(playableBeatmap); - Child = scoreProcessor; - - LoadComponentAsync(Leaderboard = CreateLeaderboard(scoreProcessor), Add); + LoadComponentAsync(Leaderboard = CreateLeaderboard(), Add); }); AddUntilStep("wait for load", () => Leaderboard.IsLoaded); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs index f57a54d84c..e94e198a4a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs @@ -8,7 +8,6 @@ using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer; -using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate; using osu.Game.Screens.Play.HUD; @@ -42,11 +41,8 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("create leaderboard", () => { Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); - var playable = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); - var scoreProcessor = new OsuScoreProcessor(); - scoreProcessor.ApplyBeatmap(playable); - LoadComponentAsync(leaderboard = new MultiSpectatorLeaderboard(Ruleset.Value, scoreProcessor, clocks.Keys.Select(id => new MultiplayerRoomUser(id)).ToArray()) + LoadComponentAsync(leaderboard = new MultiSpectatorLeaderboard(clocks.Keys.Select(id => new MultiplayerRoomUser(id)).ToArray()) { Expanded = { Value = true } }, Add); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index 6e4aa48b0e..cbbd535cee 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -1,22 +1,58 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.Linq; +using NUnit.Framework; using osu.Framework.Graphics; -using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Online.API; +using osu.Game.Online.Multiplayer; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.Play.HUD; namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMultiplayerGameplayLeaderboard : MultiplayerGameplayLeaderboardTestScene { - protected override MultiplayerGameplayLeaderboard CreateLeaderboard(OsuScoreProcessor scoreProcessor) + protected override MultiplayerRoomUser CreateUser(int userId) { - return new MultiplayerGameplayLeaderboard(Ruleset.Value, scoreProcessor, MultiplayerUsers.ToArray()) + var user = base.CreateUser(userId); + + if (userId == TOTAL_USERS - 1) + user.Mods = new[] { new APIMod(new OsuModNoFail()) }; + + return user; + } + + protected override MultiplayerGameplayLeaderboard CreateLeaderboard() + { + return new TestLeaderboard(MultiplayerUsers.ToArray()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, }; } + + [Test] + public void TestPerUserMods() + { + AddStep("first user has no mods", () => Assert.That(((TestLeaderboard)Leaderboard).UserMods[0], Is.Empty)); + AddStep("last user has NF mod", () => + { + Assert.That(((TestLeaderboard)Leaderboard).UserMods[TOTAL_USERS - 1], Has.One.Items); + Assert.That(((TestLeaderboard)Leaderboard).UserMods[TOTAL_USERS - 1].Single(), Is.TypeOf()); + }); + } + + private class TestLeaderboard : MultiplayerGameplayLeaderboard + { + public Dictionary> UserMods => UserScores.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ScoreProcessor.Mods); + + public TestLeaderboard(MultiplayerRoomUser[] users) + : base(users) + { + } + } } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs index 5caab9487e..c25884039f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs @@ -5,7 +5,6 @@ using System.Linq; using osu.Framework.Graphics; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; -using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.Play.HUD; @@ -25,8 +24,8 @@ namespace osu.Game.Tests.Visual.Multiplayer return user; } - protected override MultiplayerGameplayLeaderboard CreateLeaderboard(OsuScoreProcessor scoreProcessor) => - new MultiplayerGameplayLeaderboard(Ruleset.Value, scoreProcessor, MultiplayerUsers.ToArray()) + protected override MultiplayerGameplayLeaderboard CreateLeaderboard() => + new MultiplayerGameplayLeaderboard(MultiplayerUsers.ToArray()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 3c31acca01..527f6d06ce 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -39,7 +39,7 @@ namespace osu.Game.Online.Spectator /// /// The states of all users currently being watched. /// - public IBindableDictionary WatchedUserStates => watchedUserStates; + public virtual IBindableDictionary WatchedUserStates => watchedUserStates; /// /// A global list of all players currently playing. diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 3bf8a09cb9..f4bfec169d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -82,7 +82,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer LocalUserPlaying.BindValueChanged(_ => updateLeaderboardExpandedState(), true); // todo: this should be implemented via a custom HUD implementation, and correctly masked to the main content area. - LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(GameplayState.Ruleset.RulesetInfo, ScoreProcessor, users), l => + LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(users), l => { if (!LoadedBeatmapSuccessfully) return; @@ -149,16 +149,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override void StartGameplay() { - // We can enter this screen one of two ways: - // 1. Via the automatic natural progression of PlayerLoader into Player. - // We'll arrive here in a Loaded state, and we need to let the server know that we're ready to start. - // 2. Via the server forcefully starting gameplay because players have been hanging out in PlayerLoader for too long. - // We'll arrive here in a Playing state, and we should neither show the loading spinner nor tell the server that we're ready to start (gameplay has already started). - // - // The base call is blocked here because in both cases gameplay is started only when the server says so via onGameplayStarted(). - if (client.LocalUser?.State == MultiplayerUserState.Loaded) { + // block base call, but let the server know we are ready to start. loadingDisplay.Show(); client.ChangeState(MultiplayerUserState.ReadyForGameplay); } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs index 4545913db8..589f5ce988 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs @@ -2,19 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using System; -using JetBrains.Annotations; using osu.Framework.Timing; using osu.Game.Online.Multiplayer; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play.HUD; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { public class MultiSpectatorLeaderboard : MultiplayerGameplayLeaderboard { - public MultiSpectatorLeaderboard(RulesetInfo ruleset, [NotNull] ScoreProcessor scoreProcessor, MultiplayerRoomUser[] users) - : base(ruleset, scoreProcessor, users) + public MultiSpectatorLeaderboard(MultiplayerRoomUser[] users) + : base(users) { } @@ -23,52 +20,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate if (!UserScores.TryGetValue(userId, out var data)) throw new ArgumentException(@"Provided user is not tracked by this leaderboard", nameof(userId)); - ((SpectatingTrackedUserData)data).Clock = clock; + data.ScoreProcessor.Clock = new FramedClock(clock); } - public void RemoveClock(int userId) - { - if (!UserScores.TryGetValue(userId, out var data)) - throw new ArgumentException(@"Provided user is not tracked by this leaderboard", nameof(userId)); - - ((SpectatingTrackedUserData)data).Clock = null; - } - - protected override TrackedUserData CreateUserData(MultiplayerRoomUser user, RulesetInfo ruleset, ScoreProcessor scoreProcessor) => new SpectatingTrackedUserData(user, ruleset, scoreProcessor); - protected override void Update() { base.Update(); foreach (var (_, data) in UserScores) - data.UpdateScore(); - } - - private class SpectatingTrackedUserData : TrackedUserData - { - [CanBeNull] - public IClock Clock; - - public SpectatingTrackedUserData(MultiplayerRoomUser user, RulesetInfo ruleset, ScoreProcessor scoreProcessor) - : base(user, ruleset, scoreProcessor) - { - } - - public override void UpdateScore() - { - if (Frames.Count == 0) - return; - - if (Clock == null) - return; - - int frameIndex = Frames.BinarySearch(new TimedFrame(Clock.CurrentTime)); - if (frameIndex < 0) - frameIndex = ~frameIndex; - frameIndex = Math.Clamp(frameIndex - 1, 0, Frames.Count - 1); - - SetFrame(Frames[frameIndex]); - } + data.ScoreProcessor.UpdateScore(); } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index 2d03276fe5..d9c19cdfdd 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -128,12 +128,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate syncManager.AddPlayerClock(instances[i].GameplayClock); } - // Todo: This is not quite correct - it should be per-user to adjust for other mod combinations. - var playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); - var scoreProcessor = Ruleset.Value.CreateInstance().CreateScoreProcessor(); - scoreProcessor.ApplyBeatmap(playableBeatmap); - - LoadComponentAsync(leaderboard = new MultiSpectatorLeaderboard(Ruleset.Value, scoreProcessor, users) + LoadComponentAsync(leaderboard = new MultiSpectatorLeaderboard(users) { Expanded = { Value = true }, }, l => @@ -240,7 +235,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate instance.FadeColour(colours.Gray4, 400, Easing.OutQuint); syncManager.RemovePlayerClock(instance.GameplayClock); - leaderboard.RemoveClock(userId); } public override bool OnBackButton() diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 41b40e9a91..5ee6000cf0 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; +using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; @@ -17,9 +18,7 @@ using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Online.Spectator; -using osu.Game.Rulesets; using osu.Game.Rulesets.Scoring; -using osu.Game.Scoring; using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD @@ -43,8 +42,6 @@ namespace osu.Game.Screens.Play.HUD [Resolved] private UserLookupCache userLookupCache { get; set; } - private readonly RulesetInfo ruleset; - private readonly ScoreProcessor scoreProcessor; private readonly MultiplayerRoomUser[] playingUsers; private Bindable scoringMode; @@ -55,57 +52,56 @@ namespace osu.Game.Screens.Play.HUD /// /// Construct a new leaderboard. /// - /// The ruleset. - /// A score processor instance to handle score calculation for scores of users in the match. /// IDs of all users in this match. - public MultiplayerGameplayLeaderboard(RulesetInfo ruleset, ScoreProcessor scoreProcessor, MultiplayerRoomUser[] users) + public MultiplayerGameplayLeaderboard(MultiplayerRoomUser[] users) { - // todo: this will eventually need to be created per user to support different mod combinations. - this.ruleset = ruleset; - this.scoreProcessor = scoreProcessor; - playingUsers = users; } [BackgroundDependencyLoader] - private void load(OsuConfigManager config, IAPIProvider api) + private void load(OsuConfigManager config, IAPIProvider api, CancellationToken cancellationToken) { scoringMode = config.GetBindable(OsuSetting.ScoreDisplayMode); foreach (var user in playingUsers) { - var trackedUser = CreateUserData(user, ruleset, scoreProcessor); - - trackedUser.ScoringMode.BindTo(scoringMode); - trackedUser.Score.BindValueChanged(_ => Scheduler.AddOnce(updateTotals)); + var scoreProcessor = new SpectatorScoreProcessor(user.UserID); + scoreProcessor.Mode.BindTo(scoringMode); + scoreProcessor.TotalScore.BindValueChanged(_ => Scheduler.AddOnce(updateTotals)); + AddInternal(scoreProcessor); + var trackedUser = new TrackedUserData(user, scoreProcessor); UserScores[user.UserID] = trackedUser; if (trackedUser.Team is int team && !TeamScores.ContainsKey(team)) TeamScores.Add(team, new BindableLong()); } - userLookupCache.GetUsersAsync(playingUsers.Select(u => u.UserID).ToArray()).ContinueWith(task => Schedule(() => - { - var users = task.GetResultSafely(); + userLookupCache.GetUsersAsync(playingUsers.Select(u => u.UserID).ToArray(), cancellationToken) + .ContinueWith(task => + { + Schedule(() => + { + var users = task.GetResultSafely(); - for (int i = 0; i < users.Length; i++) - { - var user = users[i] ?? new APIUser - { - Id = playingUsers[i].UserID, - Username = "Unknown user", - }; + for (int i = 0; i < users.Length; i++) + { + var user = users[i] ?? new APIUser + { + Id = playingUsers[i].UserID, + Username = "Unknown user", + }; - var trackedUser = UserScores[user.Id]; + var trackedUser = UserScores[user.Id]; - var leaderboardScore = Add(user, user.Id == api.LocalUser.Value.Id); - leaderboardScore.Accuracy.BindTo(trackedUser.Accuracy); - leaderboardScore.TotalScore.BindTo(trackedUser.Score); - leaderboardScore.Combo.BindTo(trackedUser.CurrentCombo); - leaderboardScore.HasQuit.BindTo(trackedUser.UserQuit); - } - })); + var leaderboardScore = Add(user, user.Id == api.LocalUser.Value.Id); + leaderboardScore.Accuracy.BindTo(trackedUser.ScoreProcessor.Accuracy); + leaderboardScore.TotalScore.BindTo(trackedUser.ScoreProcessor.TotalScore); + leaderboardScore.Combo.BindTo(trackedUser.ScoreProcessor.Combo); + leaderboardScore.HasQuit.BindTo(trackedUser.UserQuit); + } + }); + }, cancellationToken); } protected override void LoadComplete() @@ -118,20 +114,15 @@ namespace osu.Game.Screens.Play.HUD spectatorClient.WatchUser(user.UserID); if (!multiplayerClient.CurrentMatchPlayingUserIds.Contains(user.UserID)) - usersChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new[] { user.UserID })); + playingUsersChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new[] { user.UserID })); } // bind here is to support players leaving the match. // new players are not supported. playingUserIds.BindTo(multiplayerClient.CurrentMatchPlayingUserIds); - playingUserIds.BindCollectionChanged(usersChanged); - - // this leaderboard should be guaranteed to be completely loaded before the gameplay starts (is a prerequisite in MultiplayerPlayer). - spectatorClient.OnNewFrames += handleIncomingFrames; + playingUserIds.BindCollectionChanged(playingUsersChanged); } - protected virtual TrackedUserData CreateUserData(MultiplayerRoomUser user, RulesetInfo ruleset, ScoreProcessor scoreProcessor) => new TrackedUserData(user, ruleset, scoreProcessor); - protected override GameplayLeaderboardScore CreateLeaderboardScoreDrawable(APIUser user, bool isTracked) { var leaderboardScore = base.CreateLeaderboardScoreDrawable(user, isTracked); @@ -157,7 +148,7 @@ namespace osu.Game.Screens.Play.HUD } } - private void usersChanged(object sender, NotifyCollectionChangedEventArgs e) + private void playingUsersChanged(object sender, NotifyCollectionChangedEventArgs e) { switch (e.Action) { @@ -174,15 +165,6 @@ namespace osu.Game.Screens.Play.HUD } } - private void handleIncomingFrames(int userId, FrameDataBundle bundle) => Schedule(() => - { - if (!UserScores.TryGetValue(userId, out var trackedData)) - return; - - trackedData.Frames.Add(new TimedFrame(bundle.Frames.First().Time, bundle.Header)); - trackedData.UpdateScore(); - }); - private void updateTotals() { if (!hasTeams) @@ -196,7 +178,7 @@ namespace osu.Game.Screens.Play.HUD continue; if (TeamScores.TryGetValue(u.Team.Value, out var team)) - team.Value += (int)Math.Round(u.Score.Value); + team.Value += (int)Math.Round(u.ScoreProcessor.TotalScore.Value); } } @@ -207,83 +189,26 @@ namespace osu.Game.Screens.Play.HUD if (spectatorClient != null) { foreach (var user in playingUsers) - { spectatorClient.StopWatchingUser(user.UserID); - } - - spectatorClient.OnNewFrames -= handleIncomingFrames; } } protected class TrackedUserData { public readonly MultiplayerRoomUser User; - public readonly ScoreProcessor ScoreProcessor; + public readonly SpectatorScoreProcessor ScoreProcessor; - public readonly BindableDouble Score = new BindableDouble(); - public readonly BindableDouble Accuracy = new BindableDouble(1); - public readonly BindableInt CurrentCombo = new BindableInt(); public readonly BindableBool UserQuit = new BindableBool(); - public readonly IBindable ScoringMode = new Bindable(); - - public readonly List Frames = new List(); - public int? Team => (User.MatchState as TeamVersusUserState)?.TeamID; - private readonly ScoreInfo scoreInfo; - - public TrackedUserData(MultiplayerRoomUser user, RulesetInfo ruleset, ScoreProcessor scoreProcessor) + public TrackedUserData(MultiplayerRoomUser user, SpectatorScoreProcessor scoreProcessor) { User = user; ScoreProcessor = scoreProcessor; - - scoreInfo = new ScoreInfo { Ruleset = ruleset }; - - ScoringMode.BindValueChanged(_ => UpdateScore()); } public void MarkUserQuit() => UserQuit.Value = true; - - public virtual void UpdateScore() - { - if (Frames.Count == 0) - return; - - SetFrame(Frames.Last()); - } - - protected void SetFrame(TimedFrame frame) - { - var header = frame.Header; - - scoreInfo.MaxCombo = header.MaxCombo; - scoreInfo.Statistics = header.Statistics; - - Score.Value = ScoreProcessor.ComputePartialScore(ScoringMode.Value, scoreInfo); - - Accuracy.Value = header.Accuracy; - CurrentCombo.Value = header.Combo; - } - } - - protected class TimedFrame : IComparable - { - public readonly double Time; - public readonly FrameHeader Header; - - public TimedFrame(double time) - { - Time = time; - } - - public TimedFrame(double time, FrameHeader header) - { - Time = time; - Header = header; - } - - public int CompareTo(TimedFrame other) => Time.CompareTo(other.Time); } } }