From 5d5e6a5ab750f380393a9c6ffd9cd3028e486f1f Mon Sep 17 00:00:00 2001 From: Hydria Date: Mon, 3 Jul 2023 17:45:30 +0100 Subject: [PATCH 01/15] Finalised LN Adjustment Values Spent a couple days discussing this on the pp rework server about values that were the most acceptable, these seemed to be the best from the community standpoint of top players. Note: This is more to fix issues with the current system, not to be a final solution. Related Google Sheets Page: https://docs.google.com/spreadsheets/d/1P0AxfdKvMHwWBQder4ZkFGO1fC9eADSGCryA5-UGriU/edit?usp=sharing --- osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs index 06c825e37d..0a4fec3a70 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills { private const double individual_decay_base = 0.125; private const double overall_decay_base = 0.30; - private const double release_threshold = 24; + private const double release_threshold = 30; protected override double SkillMultiplier => 1; protected override double StrainDecayBase => 1; @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills // 0.0 +--------+-+---------------> Release Difference / ms // release_threshold if (isOverlapping) - holdAddition = 1 / (1 + Math.Exp(0.5 * (release_threshold - closestEndTime))); + holdAddition = 1 / (1 + Math.Exp(0.27 * (release_threshold - closestEndTime))); // Decay and increase individualStrains in own column individualStrains[column] = applyDecay(individualStrains[column], startTime - startTimes[column], individual_decay_base); From ef2134a92a32f0607d0f69bc04c8dbfd2a477acd Mon Sep 17 00:00:00 2001 From: Hydria Date: Sat, 22 Jul 2023 10:45:48 +0100 Subject: [PATCH 02/15] Fix issue with processing LN orders Related Issue: https://github.com/ppy/osu/issues/22756 The trigger in question happens when (1) in a chord: a longer LN, then a shorter LN is processed respectively. (2) in a chord: a long LN, then a note is processed respectively. however, given the opposite processing step, it will fail to trigger. We observe that both situations have the same pattern, however has undeterministic results, which only depends on the order the mapper placed each note. --- osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs index 0a4fec3a70..7d8d010da0 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs @@ -53,7 +53,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills isOverlapping |= Precision.DefinitelyBigger(endTimes[i], startTime, 1) && Precision.DefinitelyBigger(endTime, endTimes[i], 1); // We give a slight bonus to everything if something is held meanwhile - if (Precision.DefinitelyBigger(endTimes[i], endTime, 1)) + if (Precision.DefinitelyBigger(endTimes[i], endTime, 1) && + Precision.DefinitelyBigger(startTime, startTimes[i], 1)) holdFactor = 1.25; closestEndTime = Math.Min(closestEndTime, Math.Abs(endTime - endTimes[i])); From 1e19def1538d718421e4d7c6802045011616d21e Mon Sep 17 00:00:00 2001 From: Hydria Date: Sat, 22 Jul 2023 16:44:01 +0100 Subject: [PATCH 03/15] 2nd fix to cover all scenarios --- osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs index 7d8d010da0..a24fcaad8d 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs @@ -50,7 +50,9 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills for (int i = 0; i < endTimes.Length; ++i) { // The current note is overlapped if a previous note or end is overlapping the current note body - isOverlapping |= Precision.DefinitelyBigger(endTimes[i], startTime, 1) && Precision.DefinitelyBigger(endTime, endTimes[i], 1); + isOverlapping |= Precision.DefinitelyBigger(endTimes[i], startTime, 1) && + Precision.DefinitelyBigger(endTime, endTimes[i], 1) && + Precision.DefinitelyBigger(startTime, startTimes[i], 1); // We give a slight bonus to everything if something is held meanwhile if (Precision.DefinitelyBigger(endTimes[i], endTime, 1) && From 87fee001c786b29db34063ef3350e9a9f024d3ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Aug 2023 18:26:36 +0900 Subject: [PATCH 04/15] Fix multiplayer spectator potentially taking too long to start When watching from the middle of gameplay, due to a series of failures, `SpectatorClock` would not get seeked to the current time, causing all clients to look like they were out of sync. This is a hotfix for the issue. A better fix will require framework changes or considerable restructuring. I'd recommend testing this works in practice and agreeing that while it is a hack, it's likely not going to cause issues and is something we want to see fixed sooner rather than later. --- .../Multiplayer/Spectate/SpectatorPlayerClock.cs | 2 ++ osu.Game/Screens/Play/GameplayClockContainer.cs | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorPlayerClock.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorPlayerClock.cs index 45615d4e19..2ce78818a0 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorPlayerClock.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorPlayerClock.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Logging; using osu.Framework.Timing; using osu.Game.Screens.Play; @@ -59,6 +60,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public bool Seek(double position) { + Logger.Log($"{nameof(SpectatorPlayerClock)} seeked to {position}"); CurrentTime = position; return true; } diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 22e6884526..226108209a 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -160,6 +160,13 @@ namespace osu.Game.Screens.Play Seek(StartTime); + // This is a workaround for the fact that DecoupleableInterpolatingFramedClock doesn't seek the source + // if the source is not IsRunning. (see https://github.com/ppy/osu-framework/blob/2102638056dfcf85d21b4d85266d53b5dd018767/osu.Framework/Timing/DecoupleableInterpolatingFramedClock.cs#L209-L210) + // + // This breaks in multiplayer spectator. + // I hope to remove this once we knock some sense into clocks in general. + (SourceClock as IAdjustableClock)?.Seek(StartTime); + if (!wasPaused || startClock) Start(); } From 71a96e8be2e19397e7ab6b14e39a272517c9927e Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sun, 13 Aug 2023 21:47:13 +0200 Subject: [PATCH 05/15] Remove SV from DrumRoll --- .../Beatmaps/TaikoBeatmapConverter.cs | 1 - osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 19 +++---------------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index dddd1e3c5a..5f3d0f898e 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -139,7 +139,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps StartTime = obj.StartTime, Samples = obj.Samples, Duration = taikoDuration, - SliderVelocity = obj is IHasSliderVelocity velocityData ? velocityData.SliderVelocity : 1 }; } diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 083b8cc547..5f47d486e6 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -3,7 +3,6 @@ using osu.Game.Rulesets.Objects.Types; using System.Threading; -using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Formats; @@ -14,7 +13,7 @@ using osuTK; namespace osu.Game.Rulesets.Taiko.Objects { - public class DrumRoll : TaikoStrongableHitObject, IHasPath, IHasSliderVelocity + public class DrumRoll : TaikoStrongableHitObject, IHasPath { /// /// Drum roll distance that results in a duration of 1 speed-adjusted beat length. @@ -34,19 +33,6 @@ namespace osu.Game.Rulesets.Taiko.Objects /// public double Velocity { get; private set; } - public BindableNumber SliderVelocityBindable { get; } = new BindableDouble(1) - { - Precision = 0.01, - MinValue = 0.1, - MaxValue = 10 - }; - - public double SliderVelocity - { - get => SliderVelocityBindable.Value; - set => SliderVelocityBindable.Value = value; - } - /// /// Numer of ticks per beat length. /// @@ -63,8 +49,9 @@ namespace osu.Game.Rulesets.Taiko.Objects base.ApplyDefaultsToSelf(controlPointInfo, difficulty); TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); + EffectControlPoint effectPoint = controlPointInfo.EffectPointAt(StartTime); - double scoringDistance = base_distance * difficulty.SliderMultiplier * SliderVelocity; + double scoringDistance = base_distance * difficulty.SliderMultiplier * effectPoint.ScrollSpeed; Velocity = scoringDistance / timingPoint.BeatLength; TickRate = difficulty.SliderTickRate == 3 ? 3 : 4; From bb98f10ff67b87794a542f8f51f4cc1aeb1f1743 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Aug 2023 16:38:49 +0900 Subject: [PATCH 06/15] Use `CurrentTime` instead of `StartTime` for hotfix seek (and update comment) --- osu.Game/Screens/Play/GameplayClockContainer.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 226108209a..a50c089d16 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -162,10 +162,18 @@ namespace osu.Game.Screens.Play // This is a workaround for the fact that DecoupleableInterpolatingFramedClock doesn't seek the source // if the source is not IsRunning. (see https://github.com/ppy/osu-framework/blob/2102638056dfcf85d21b4d85266d53b5dd018767/osu.Framework/Timing/DecoupleableInterpolatingFramedClock.cs#L209-L210) - // - // This breaks in multiplayer spectator. // I hope to remove this once we knock some sense into clocks in general. - (SourceClock as IAdjustableClock)?.Seek(StartTime); + // + // Without this seek, the multiplayer spectator start sequence breaks: + // - Individual clients' clocks are never updated to their expected time + // - The sync manager thinks they are running behind + // - Gameplay doesn't start when it should (until a timeout occurs because nothing is happening for 10+ seconds) + // + // In addition, we use `CurrentTime` for this seek instead of `StartTime` as the above seek may have applied inherent + // offsets which need to be accounted for (ie. FramedBeatmapClock.TotalApliedOffset). + // + // See https://github.com/ppy/osu/pull/24451/files/87fee001c786b29db34063ef3350e9a9f024d3ab#diff-28ca02979641e2d98a15fe5d5e806f56acf60ac100258a059fa72503b6cc54e8. + (SourceClock as IAdjustableClock)?.Seek(CurrentTime); if (!wasPaused || startClock) Start(); From 56a4989f7158a071bf78b009e4524944f172493b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Aug 2023 16:55:48 +0900 Subject: [PATCH 07/15] Disable IPC binding in tournament client to allow running concurrently --- osu.Desktop/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 5a1373e040..a33e845f5b 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -85,7 +85,7 @@ namespace osu.Desktop } } - using (DesktopGameHost host = Host.GetSuitableDesktopHost(gameName, new HostOptions { BindIPC = true })) + using (DesktopGameHost host = Host.GetSuitableDesktopHost(gameName, new HostOptions { BindIPC = !tournamentClient })) { if (!host.IsPrimaryInstance) { From add1ef77d0b1ef4cb41ce083a937b9154eacc7b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 16 Aug 2023 10:07:12 +0200 Subject: [PATCH 08/15] Fix typo in comment --- osu.Game/Screens/Play/GameplayClockContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index a50c089d16..20bf6c3829 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -170,7 +170,7 @@ namespace osu.Game.Screens.Play // - Gameplay doesn't start when it should (until a timeout occurs because nothing is happening for 10+ seconds) // // In addition, we use `CurrentTime` for this seek instead of `StartTime` as the above seek may have applied inherent - // offsets which need to be accounted for (ie. FramedBeatmapClock.TotalApliedOffset). + // offsets which need to be accounted for (ie. FramedBeatmapClock.TotalAppliedOffset). // // See https://github.com/ppy/osu/pull/24451/files/87fee001c786b29db34063ef3350e9a9f024d3ab#diff-28ca02979641e2d98a15fe5d5e806f56acf60ac100258a059fa72503b6cc54e8. (SourceClock as IAdjustableClock)?.Seek(CurrentTime); From 965da343a409fbcf714c05226ddcd8bbffdb9fdd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Aug 2023 14:56:02 +0900 Subject: [PATCH 09/15] Fix tournament song bar tests not showing anything by default --- .../Components/TestSceneSongBar.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs b/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs index 762cfa2519..0f31192a9c 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Testing; using osu.Game.Beatmaps.Legacy; using osu.Game.Tests.Visual; using osu.Game.Tournament.Components; @@ -17,11 +18,11 @@ namespace osu.Game.Tournament.Tests.Components [Cached] private readonly LadderInfo ladder = new LadderInfo(); - [Test] - public void TestSongBar() - { - SongBar songBar = null!; + private SongBar songBar = null!; + [SetUpSteps] + public void SetUpSteps() + { AddStep("create bar", () => Child = songBar = new SongBar { RelativeSizeAxes = Axes.X, @@ -29,7 +30,11 @@ namespace osu.Game.Tournament.Tests.Components Origin = Anchor.Centre }); AddUntilStep("wait for loaded", () => songBar.IsLoaded); + } + [Test] + public void TestSongBar() + { AddStep("set beatmap", () => { var beatmap = CreateAPIBeatmap(Ruleset.Value); From 56eb44d15bd2e119dec201562f1db41d22534e0c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Aug 2023 16:07:03 +0900 Subject: [PATCH 10/15] Change `TestSpectatorClient` to provide some better fake data for score / acc --- .../Multiplayer/TestSceneMultiSpectatorLeaderboard.cs | 2 ++ osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs index 049c02ffde..4bf2ebc1a4 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs @@ -49,6 +49,8 @@ namespace osu.Game.Tests.Visual.Multiplayer LoadComponentAsync(leaderboard = new MultiSpectatorLeaderboard(clocks.Keys.Select(id => new MultiplayerRoomUser(id)).ToArray()) { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Expanded = { Value = true } }, Add); }); diff --git a/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs index 305a615102..5db08810ca 100644 --- a/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs +++ b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs @@ -124,7 +124,12 @@ namespace osu.Game.Tests.Visual.Spectator if (frames.Count == 0) return; - var bundle = new FrameDataBundle(new ScoreInfo { Combo = currentFrameIndex }, new ScoreProcessor(rulesetStore.GetRuleset(0)!.CreateInstance()), frames.ToArray()); + var bundle = new FrameDataBundle(new ScoreInfo + { + Combo = currentFrameIndex, + TotalScore = (long)(currentFrameIndex * 123478 * RNG.NextDouble(0.99, 1.01)), + Accuracy = RNG.NextDouble(0.98, 1), + }, new ScoreProcessor(rulesetStore.GetRuleset(0)!.CreateInstance()), frames.ToArray()); ((ISpectatorClient)this).UserSentFrames(userId, bundle); frames.Clear(); From 26d05afadcbacaa9a8dacfb2c98d79a3c4483e81 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Aug 2023 18:28:06 +0900 Subject: [PATCH 11/15] Add more test coverage to multi spectator screen --- .../TestSceneMultiSpectatorScreen.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index a61c3f1234..cebc75f90c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -79,6 +79,19 @@ namespace osu.Game.Tests.Visual.Multiplayer AddWaitStep("wait a bit", 20); } + [TestCase(2)] + [TestCase(16)] + public void TestTeams(int count) + { + int[] userIds = getPlayerIds(count); + + start(userIds, teams: true); + loadSpectateScreen(); + + sendFrames(userIds, 1000); + AddWaitStep("wait a bit", 20); + } + [Test] public void TestMultipleStartRequests() { @@ -450,16 +463,18 @@ namespace osu.Game.Tests.Visual.Multiplayer private void start(int userId, int? beatmapId = null) => start(new[] { userId }, beatmapId); - private void start(int[] userIds, int? beatmapId = null, APIMod[]? mods = null) + private void start(int[] userIds, int? beatmapId = null, APIMod[]? mods = null, bool teams = false) { AddStep("start play", () => { - foreach (int id in userIds) + for (int i = 0; i < userIds.Length; i++) { + int id = userIds[i]; var user = new MultiplayerRoomUser(id) { User = new APIUser { Id = id }, Mods = mods ?? Array.Empty(), + MatchState = teams ? new TeamVersusUserState { TeamID = i % 2 } : null, }; OnlinePlayDependencies.MultiplayerClient.AddUser(user, true); From f02416f877c758d4d8e7a3fbe78b413260c7b997 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Aug 2023 14:56:48 +0900 Subject: [PATCH 12/15] Improve visuals of tournament song bar This is a stop-gap until we add new versions (and share between game and tournament client). --- osu.Game.Tournament/Components/SongBar.cs | 39 ++++++++++++++----- .../Components/TournamentBeatmapPanel.cs | 6 +-- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index fa0cbda16b..3d060600f7 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -14,7 +14,6 @@ using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Rulesets; using osu.Game.Screens.Menu; -using osu.Game.Tournament.Models; using osuTK; using osuTK.Graphics; @@ -22,14 +21,14 @@ namespace osu.Game.Tournament.Components { public partial class SongBar : CompositeDrawable { - private TournamentBeatmap? beatmap; + private IBeatmapInfo? beatmap; public const float HEIGHT = 145 / 2f; [Resolved] private IBindable ruleset { get; set; } = null!; - public TournamentBeatmap? Beatmap + public IBeatmapInfo? Beatmap { set { @@ -37,7 +36,7 @@ namespace osu.Game.Tournament.Components return; beatmap = value; - update(); + refreshContent(); } } @@ -49,7 +48,7 @@ namespace osu.Game.Tournament.Components set { mods = value; - update(); + refreshContent(); } } @@ -71,19 +70,25 @@ namespace osu.Game.Tournament.Components protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false; [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; + Masking = true; + CornerRadius = 5; + InternalChildren = new Drawable[] { + new Box + { + Colour = colours.Gray3, + RelativeSizeAxes = Axes.Both, + }, flow = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - LayoutDuration = 500, - LayoutEasing = Easing.OutQuint, Direction = FillDirection.Full, Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, @@ -93,7 +98,7 @@ namespace osu.Game.Tournament.Components Expanded = true; } - private void update() + private void refreshContent() { if (beatmap == null) { @@ -229,7 +234,7 @@ namespace osu.Game.Tournament.Components } } }, - new TournamentBeatmapPanel(beatmap) + new UnmaskedTournamentBeatmapPanel(beatmap) { RelativeSizeAxes = Axes.X, Width = 0.5f, @@ -272,4 +277,18 @@ namespace osu.Game.Tournament.Components } } } + + internal partial class UnmaskedTournamentBeatmapPanel : TournamentBeatmapPanel + { + public UnmaskedTournamentBeatmapPanel(IBeatmapInfo? beatmap, string mod = "") + : base(beatmap, mod) + { + } + + [BackgroundDependencyLoader] + private void load() + { + Masking = false; + } + } } diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index ba922c7c7b..4e0adb30ac 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tournament.Components { public partial class TournamentBeatmapPanel : CompositeDrawable { - public readonly TournamentBeatmap? Beatmap; + public readonly IBeatmapInfo? Beatmap; private readonly string mod; @@ -30,7 +30,7 @@ namespace osu.Game.Tournament.Components private Box flash = null!; - public TournamentBeatmapPanel(TournamentBeatmap? beatmap, string mod = "") + public TournamentBeatmapPanel(IBeatmapInfo? beatmap, string mod = "") { Beatmap = beatmap; this.mod = mod; @@ -58,7 +58,7 @@ namespace osu.Game.Tournament.Components { RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(0.5f), - OnlineInfo = Beatmap, + OnlineInfo = (Beatmap as IBeatmapSetOnlineInfo), }, new FillFlowContainer { From ab826c35b7515619535fb2bc4959f57afab1fe97 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Aug 2023 18:28:59 +0900 Subject: [PATCH 13/15] Add score diff display to multiplayer spectator Basically pulling changes over from the tournament client implementation --- .../Screens/Play/HUD/MatchScoreDisplay.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs index 58bf4eea4b..e1e388d872 100644 --- a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs +++ b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs @@ -30,6 +30,8 @@ namespace osu.Game.Screens.Play.HUD private Drawable score1Bar; private Drawable score2Bar; + private MatchScoreDiffCounter scoreDiffText; + [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -98,6 +100,16 @@ namespace osu.Game.Screens.Play.HUD }, } }, + scoreDiffText = new MatchScoreDiffCounter + { + Anchor = Anchor.TopCentre, + Margin = new MarginPadding + { + Top = bar_height / 4, + Horizontal = 8 + }, + Alpha = 0 + } }; } @@ -139,6 +151,10 @@ namespace osu.Game.Screens.Play.HUD losingBar.ResizeWidthTo(0, 400, Easing.OutQuint); winningBar.ResizeWidthTo(Math.Min(0.4f, MathF.Pow(diff / 1500000f, 0.5f) / 2), 400, Easing.OutQuint); + + scoreDiffText.Alpha = diff != 0 ? 1 : 0; + scoreDiffText.Current.Value = -diff; + scoreDiffText.Origin = Team1Score.Value > Team2Score.Value ? Anchor.TopLeft : Anchor.TopRight; } protected override void UpdateAfterChildren() @@ -174,5 +190,14 @@ namespace osu.Game.Screens.Play.HUD ? OsuFont.Torus.With(weight: FontWeight.Bold, size: font_size, fixedWidth: true) : OsuFont.Torus.With(weight: FontWeight.Regular, size: font_size * 0.8f, fixedWidth: true); } + + private partial class MatchScoreDiffCounter : CommaSeparatedScoreCounter + { + protected override OsuSpriteText CreateSpriteText() => base.CreateSpriteText().With(s => + { + s.Spacing = new Vector2(-2); + s.Font = OsuFont.Torus.With(weight: FontWeight.Regular, size: bar_height, fixedWidth: true); + }); + } } } From 8b9759c569590687ee78591aa713bdbd99d3c4a4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Aug 2023 17:22:55 +0900 Subject: [PATCH 14/15] Apply nullability to `MatchScoreDisplay` --- osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs index e1e388d872..4a61c7fd1b 100644 --- a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs +++ b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -24,13 +22,13 @@ namespace osu.Game.Screens.Play.HUD public BindableLong Team1Score = new BindableLong(); public BindableLong Team2Score = new BindableLong(); - protected MatchScoreCounter Score1Text; - protected MatchScoreCounter Score2Text; + protected MatchScoreCounter Score1Text = null!; + protected MatchScoreCounter Score2Text = null!; - private Drawable score1Bar; - private Drawable score2Bar; + private Drawable score1Bar = null!; + private Drawable score2Bar = null!; - private MatchScoreDiffCounter scoreDiffText; + private MatchScoreDiffCounter scoreDiffText = null!; [BackgroundDependencyLoader] private void load(OsuColour colours) @@ -166,7 +164,7 @@ namespace osu.Game.Screens.Play.HUD protected partial class MatchScoreCounter : CommaSeparatedScoreCounter { - private OsuSpriteText displayedSpriteText; + private OsuSpriteText displayedSpriteText = null!; public MatchScoreCounter() { From d309865b0daac418ad4f7de6a3f512ab4dd2b51b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Aug 2023 17:25:25 +0900 Subject: [PATCH 15/15] Update `TournamentMatchScoreDisplay` to share base implementation --- osu.Game.Tournament/IPC/MatchIPCInfo.cs | 4 +- .../Components/TournamentMatchScoreDisplay.cs | 170 +----------------- 2 files changed, 6 insertions(+), 168 deletions(-) diff --git a/osu.Game.Tournament/IPC/MatchIPCInfo.cs b/osu.Game.Tournament/IPC/MatchIPCInfo.cs index f57518971f..b4575144e7 100644 --- a/osu.Game.Tournament/IPC/MatchIPCInfo.cs +++ b/osu.Game.Tournament/IPC/MatchIPCInfo.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tournament.IPC public Bindable Mods { get; } = new Bindable(); public Bindable State { get; } = new Bindable(); public Bindable ChatChannel { get; } = new Bindable(); - public BindableInt Score1 { get; } = new BindableInt(); - public BindableInt Score2 { get; } = new BindableInt(); + public BindableLong Score1 { get; } = new BindableLong(); + public BindableLong Score2 { get; } = new BindableLong(); } } diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/TournamentMatchScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/TournamentMatchScoreDisplay.cs index 7ae20acc77..f8de34a511 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/TournamentMatchScoreDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/TournamentMatchScoreDisplay.cs @@ -1,181 +1,19 @@ // 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 osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Play.HUD; using osu.Game.Tournament.IPC; -using osuTK; namespace osu.Game.Tournament.Screens.Gameplay.Components { - // TODO: Update to derive from osu-side class? - public partial class TournamentMatchScoreDisplay : CompositeDrawable + public partial class TournamentMatchScoreDisplay : MatchScoreDisplay { - private const float bar_height = 18; - - private readonly BindableInt score1 = new BindableInt(); - private readonly BindableInt score2 = new BindableInt(); - - private readonly MatchScoreCounter score1Text; - private readonly MatchScoreCounter score2Text; - - private readonly MatchScoreDiffCounter scoreDiffText; - - private readonly Drawable score1Bar; - private readonly Drawable score2Bar; - - public TournamentMatchScoreDisplay() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - InternalChildren = new[] - { - new Box - { - Name = "top bar red (static)", - RelativeSizeAxes = Axes.X, - Height = bar_height / 4, - Width = 0.5f, - Colour = TournamentGame.COLOUR_RED, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopRight - }, - new Box - { - Name = "top bar blue (static)", - RelativeSizeAxes = Axes.X, - Height = bar_height / 4, - Width = 0.5f, - Colour = TournamentGame.COLOUR_BLUE, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopLeft - }, - score1Bar = new Box - { - Name = "top bar red", - RelativeSizeAxes = Axes.X, - Height = bar_height, - Width = 0, - Colour = TournamentGame.COLOUR_RED, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopRight - }, - score1Text = new MatchScoreCounter - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre - }, - score2Bar = new Box - { - Name = "top bar blue", - RelativeSizeAxes = Axes.X, - Height = bar_height, - Width = 0, - Colour = TournamentGame.COLOUR_BLUE, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopLeft - }, - score2Text = new MatchScoreCounter - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre - }, - scoreDiffText = new MatchScoreDiffCounter - { - Anchor = Anchor.TopCentre, - Margin = new MarginPadding - { - Top = bar_height / 4, - Horizontal = 8 - }, - Alpha = 0 - } - }; - } - [BackgroundDependencyLoader] private void load(MatchIPCInfo ipc) { - score1.BindValueChanged(_ => updateScores()); - score1.BindTo(ipc.Score1); - - score2.BindValueChanged(_ => updateScores()); - score2.BindTo(ipc.Score2); - } - - private void updateScores() - { - score1Text.Current.Value = score1.Value; - score2Text.Current.Value = score2.Value; - - var winningText = score1.Value > score2.Value ? score1Text : score2Text; - var losingText = score1.Value <= score2.Value ? score1Text : score2Text; - - winningText.Winning = true; - losingText.Winning = false; - - var winningBar = score1.Value > score2.Value ? score1Bar : score2Bar; - var losingBar = score1.Value <= score2.Value ? score1Bar : score2Bar; - - int diff = Math.Max(score1.Value, score2.Value) - Math.Min(score1.Value, score2.Value); - - losingBar.ResizeWidthTo(0, 400, Easing.OutQuint); - winningBar.ResizeWidthTo(Math.Min(0.4f, MathF.Pow(diff / 1500000f, 0.5f) / 2), 400, Easing.OutQuint); - - scoreDiffText.Alpha = diff != 0 ? 1 : 0; - scoreDiffText.Current.Value = -diff; - scoreDiffText.Origin = score1.Value > score2.Value ? Anchor.TopLeft : Anchor.TopRight; - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - score1Text.X = -Math.Max(5 + score1Text.DrawWidth / 2, score1Bar.DrawWidth); - score2Text.X = Math.Max(5 + score2Text.DrawWidth / 2, score2Bar.DrawWidth); - } - - private partial class MatchScoreCounter : CommaSeparatedScoreCounter - { - private OsuSpriteText displayedSpriteText = null!; - - public MatchScoreCounter() - { - Margin = new MarginPadding { Top = bar_height, Horizontal = 10 }; - } - - public bool Winning - { - set => updateFont(value); - } - - protected override OsuSpriteText CreateSpriteText() => base.CreateSpriteText().With(s => - { - displayedSpriteText = s; - displayedSpriteText.Spacing = new Vector2(-6); - updateFont(false); - }); - - private void updateFont(bool winning) - => displayedSpriteText.Font = winning - ? OsuFont.Torus.With(weight: FontWeight.Bold, size: 50, fixedWidth: true) - : OsuFont.Torus.With(weight: FontWeight.Regular, size: 40, fixedWidth: true); - } - - private partial class MatchScoreDiffCounter : CommaSeparatedScoreCounter - { - protected override OsuSpriteText CreateSpriteText() => base.CreateSpriteText().With(s => - { - s.Spacing = new Vector2(-2); - s.Font = OsuFont.Torus.With(weight: FontWeight.Regular, size: bar_height, fixedWidth: true); - }); + Team1Score.BindTo(ipc.Score1); + Team2Score.BindTo(ipc.Score2); } } }