1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-15 16:22:21 +08:00

Merge pull request #29914 from peppy/fix-judgement-counter-sync

Fix judgement counter not showing correct counts when spectating user mid-play
This commit is contained in:
Bartłomiej Dach 2024-09-27 10:57:26 +02:00 committed by GitHub
commit a00ed8dd77
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 74 additions and 21 deletions

View File

@ -19,6 +19,7 @@ using osu.Game.Rulesets.UI;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens; using osu.Game.Screens;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Screens.Play.HUD.JudgementCounter;
using osu.Game.Tests.Beatmaps.IO; using osu.Game.Tests.Beatmaps.IO;
using osu.Game.Tests.Gameplay; using osu.Game.Tests.Gameplay;
using osu.Game.Tests.Visual.Multiplayer; using osu.Game.Tests.Visual.Multiplayer;
@ -167,14 +168,16 @@ namespace osu.Game.Tests.Visual.Gameplay
public void TestSpectatingDuringGameplay() public void TestSpectatingDuringGameplay()
{ {
start(); start();
sendFrames(300); sendFrames(300, initialResultCount: 100);
loadSpectatingScreen(); loadSpectatingScreen();
waitForPlayerCurrent(); waitForPlayerCurrent();
sendFrames(300); sendFrames(300, initialResultCount: 100);
AddUntilStep("playing from correct point in time", () => player.ChildrenOfType<DrawableRuleset>().First().FrameStableClock.CurrentTime, () => Is.GreaterThan(30000)); AddUntilStep("playing from correct point in time", () => player.ChildrenOfType<DrawableRuleset>().First().FrameStableClock.CurrentTime, () => Is.GreaterThan(30000));
AddAssert("check judgement counts are correct", () => player.ChildrenOfType<JudgementCountController>().Single().Counters.Sum(c => c.ResultCount.Value),
() => Is.GreaterThanOrEqualTo(100));
} }
[Test] [Test]
@ -405,9 +408,9 @@ namespace osu.Game.Tests.Visual.Gameplay
private void checkPaused(bool state) => private void checkPaused(bool state) =>
AddUntilStep($"game is {(state ? "paused" : "playing")}", () => player.ChildrenOfType<DrawableRuleset>().First().IsPaused.Value == state); AddUntilStep($"game is {(state ? "paused" : "playing")}", () => player.ChildrenOfType<DrawableRuleset>().First().IsPaused.Value == state);
private void sendFrames(int count = 10, double startTime = 0) private void sendFrames(int count = 10, double startTime = 0, int initialResultCount = 0)
{ {
AddStep("send frames", () => spectatorClient.SendFramesFromUser(streamingUser.Id, count, startTime)); AddStep("send frames", () => spectatorClient.SendFramesFromUser(streamingUser.Id, count, startTime, initialResultCount));
} }
private void loadSpectatingScreen() private void loadSpectatingScreen()

View File

@ -181,6 +181,8 @@ namespace osu.Game.Rulesets.Scoring
} }
} }
public IReadOnlyDictionary<HitResult, int> Statistics => ScoreResultCounts;
private bool beatmapApplied; private bool beatmapApplied;
protected readonly Dictionary<HitResult, int> ScoreResultCounts = new Dictionary<HitResult, int>(); protected readonly Dictionary<HitResult, int> ScoreResultCounts = new Dictionary<HitResult, int>();

View File

@ -0,0 +1,18 @@
// 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 osu.Framework.Bindables;
using osu.Framework.Localisation;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Screens.Play.HUD.JudgementCounter
{
public struct JudgementCount
{
public LocalisableString DisplayName { get; set; }
public HitResult[] Types { get; set; }
public BindableInt ResultCount { get; set; }
}
}

View File

@ -6,7 +6,6 @@ using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -53,10 +52,29 @@ namespace osu.Game.Screens.Play.HUD.JudgementCounter
{ {
base.LoadComplete(); base.LoadComplete();
scoreProcessor.OnResetFromReplayFrame += updateAllCountsFromReplayFrame;
scoreProcessor.NewJudgement += judgement => updateCount(judgement, false); scoreProcessor.NewJudgement += judgement => updateCount(judgement, false);
scoreProcessor.JudgementReverted += judgement => updateCount(judgement, true); scoreProcessor.JudgementReverted += judgement => updateCount(judgement, true);
} }
private bool hasUpdatedCountsFromReplayFrame;
private void updateAllCountsFromReplayFrame()
{
if (hasUpdatedCountsFromReplayFrame)
return;
foreach (var kvp in scoreProcessor.Statistics)
{
if (!results.TryGetValue(kvp.Key, out var count))
continue;
count.ResultCount.Value = kvp.Value;
}
hasUpdatedCountsFromReplayFrame = true;
}
private void updateCount(JudgementResult judgement, bool revert) private void updateCount(JudgementResult judgement, bool revert)
{ {
if (!results.TryGetValue(judgement.Type, out var count)) if (!results.TryGetValue(judgement.Type, out var count))
@ -67,14 +85,5 @@ namespace osu.Game.Screens.Play.HUD.JudgementCounter
else else
count.ResultCount.Value++; count.ResultCount.Value++;
} }
public struct JudgementCount
{
public LocalisableString DisplayName { get; set; }
public HitResult[] Types { get; set; }
public BindableInt ResultCount { get; set; }
}
} }
} }

View File

@ -9,7 +9,6 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
namespace osu.Game.Screens.Play.HUD.JudgementCounter namespace osu.Game.Screens.Play.HUD.JudgementCounter
@ -19,16 +18,16 @@ namespace osu.Game.Screens.Play.HUD.JudgementCounter
public BindableBool ShowName = new BindableBool(); public BindableBool ShowName = new BindableBool();
public Bindable<FillDirection> Direction = new Bindable<FillDirection>(); public Bindable<FillDirection> Direction = new Bindable<FillDirection>();
public readonly JudgementCountController.JudgementCount Result; public readonly JudgementCount Result;
public JudgementCounter(JudgementCountController.JudgementCount result) => Result = result; public JudgementCounter(JudgementCount result) => Result = result;
public OsuSpriteText ResultName = null!; public OsuSpriteText ResultName = null!;
private FillFlowContainer flowContainer = null!; private FillFlowContainer flowContainer = null!;
private JudgementRollingCounter counter = null!; private JudgementRollingCounter counter = null!;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours, IBindable<RulesetInfo> ruleset) private void load(OsuColour colours)
{ {
AutoSizeAxes = Axes.Both; AutoSizeAxes = Axes.Both;

View File

@ -126,7 +126,7 @@ namespace osu.Game.Screens.Play.HUD.JudgementCounter
} }
} }
private JudgementCounter createCounter(JudgementCountController.JudgementCount info) => private JudgementCounter createCounter(JudgementCount info) =>
new JudgementCounter(info) new JudgementCounter(info)
{ {
State = { Value = Visibility.Hidden }, State = { Value = Visibility.Hidden },

View File

@ -13,6 +13,8 @@ using osu.Game.Online.API;
using osu.Game.Online.Spectator; using osu.Game.Online.Spectator;
using osu.Game.Replays.Legacy; using osu.Game.Replays.Legacy;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring; using osu.Game.Scoring;
@ -99,13 +101,24 @@ namespace osu.Game.Tests.Visual.Spectator
/// <param name="userId">The user to send frames for.</param> /// <param name="userId">The user to send frames for.</param>
/// <param name="count">The total number of frames to send.</param> /// <param name="count">The total number of frames to send.</param>
/// <param name="startTime">The time to start gameplay frames from.</param> /// <param name="startTime">The time to start gameplay frames from.</param>
public void SendFramesFromUser(int userId, int count, double startTime = 0) /// <param name="initialResultCount">Add a number of misses to frame header data for testing purposes.</param>
public void SendFramesFromUser(int userId, int count, double startTime = 0, int initialResultCount = 0)
{ {
var frames = new List<LegacyReplayFrame>(); var frames = new List<LegacyReplayFrame>();
int currentFrameIndex = userNextFrameDictionary[userId]; int currentFrameIndex = userNextFrameDictionary[userId];
int lastFrameIndex = currentFrameIndex + count - 1; int lastFrameIndex = currentFrameIndex + count - 1;
var scoreProcessor = new ScoreProcessor(rulesetStore.GetRuleset(0)!.CreateInstance());
for (int i = 0; i < initialResultCount; i++)
{
scoreProcessor.ApplyResult(new JudgementResult(new HitObject(), new Judgement())
{
Type = HitResult.Miss,
});
}
for (; currentFrameIndex <= lastFrameIndex; currentFrameIndex++) for (; currentFrameIndex <= lastFrameIndex; currentFrameIndex++)
{ {
// This is done in the next frame so that currentFrameIndex is updated to the correct value. // This is done in the next frame so that currentFrameIndex is updated to the correct value.
@ -130,7 +143,16 @@ namespace osu.Game.Tests.Visual.Spectator
Combo = currentFrameIndex, Combo = currentFrameIndex,
TotalScore = (long)(currentFrameIndex * 123478 * RNG.NextDouble(0.99, 1.01)), TotalScore = (long)(currentFrameIndex * 123478 * RNG.NextDouble(0.99, 1.01)),
Accuracy = RNG.NextDouble(0.98, 1), Accuracy = RNG.NextDouble(0.98, 1),
}, new ScoreProcessor(rulesetStore.GetRuleset(0)!.CreateInstance()), frames.ToArray()); Statistics = scoreProcessor.Statistics.ToDictionary(),
}, scoreProcessor, frames.ToArray());
if (initialResultCount > 0)
{
foreach (var f in frames)
f.Header = bundle.Header;
}
scoreProcessor.ResetFromReplayFrame(frames.Last());
((ISpectatorClient)this).UserSentFrames(userId, bundle); ((ISpectatorClient)this).UserSentFrames(userId, bundle);
frames.Clear(); frames.Clear();