From c65a79614dcee1f5d570a31fe1dc37a0bcc1487a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Mar 2023 15:45:02 +0900 Subject: [PATCH 001/169] Don't show offset calibration controls when "relax" mod is active Closes https://github.com/ppy/osu/issues/22892. --- osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index 9492614b66..8d5459809e 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -18,6 +18,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Localisation; using osu.Game.Overlays.Settings; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Ranking.Statistics; @@ -183,7 +184,7 @@ namespace osu.Game.Screens.Play.PlayerSettings if (score.NewValue == null) return; - if (score.NewValue.Mods.Any(m => !m.UserPlayable)) + if (score.NewValue.Mods.Any(m => !m.UserPlayable || m is ModRelax)) return; var hitEvents = score.NewValue.HitEvents; From 74e7a958bbbba4dc52159d19aabd758e28524cb6 Mon Sep 17 00:00:00 2001 From: tsrk Date: Fri, 5 May 2023 05:31:06 +0100 Subject: [PATCH 002/169] feat(ArgonKeyCounter): make animation Currently WIP. Do not consider this final. --- osu.Game/Screens/Play/ArgonKeyCounter.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/ArgonKeyCounter.cs b/osu.Game/Screens/Play/ArgonKeyCounter.cs index 6818b30823..8635783d71 100644 --- a/osu.Game/Screens/Play/ArgonKeyCounter.cs +++ b/osu.Game/Screens/Play/ArgonKeyCounter.cs @@ -69,8 +69,19 @@ namespace osu.Game.Screens.Play { base.LoadComplete(); - IsActive.BindValueChanged(e => inputIndicator.Alpha = e.NewValue ? 1 : 0.5f, true); CountPresses.BindValueChanged(e => countText.Text = e.NewValue.ToString(@"#,0"), true); } + + protected override void Activate(bool forwardPlayback = true) + { + base.Activate(forwardPlayback); + inputIndicator.FadeIn().MoveToY(0).Then().MoveToY(3, 100, Easing.OutQuart); + } + + protected override void Deactivate(bool forwardPlayback = true) + { + base.Deactivate(forwardPlayback); + inputIndicator.MoveToY(0, 200, Easing.OutQuart).FadeTo(0.5f, 200, Easing.OutQuart); + } } } From e9d7cd7a795c85846895a0a98c295841f4ee00eb Mon Sep 17 00:00:00 2001 From: tsrk Date: Sat, 6 May 2023 19:08:30 +0100 Subject: [PATCH 003/169] feat(ArgonKeyCounter): flash key name --- osu.Game/Screens/Play/ArgonKeyCounter.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/ArgonKeyCounter.cs b/osu.Game/Screens/Play/ArgonKeyCounter.cs index 8635783d71..53305901ee 100644 --- a/osu.Game/Screens/Play/ArgonKeyCounter.cs +++ b/osu.Game/Screens/Play/ArgonKeyCounter.cs @@ -14,6 +14,7 @@ namespace osu.Game.Screens.Play public partial class ArgonKeyCounter : KeyCounter { private Circle inputIndicator = null!; + private OsuSpriteText keyNameText = null!; private OsuSpriteText countText = null!; // These values were taken from Figma @@ -42,7 +43,7 @@ namespace osu.Game.Screens.Play Height = line_height * scale_factor, Alpha = 0.5f }, - new OsuSpriteText + keyNameText = new OsuSpriteText { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, @@ -75,6 +76,7 @@ namespace osu.Game.Screens.Play protected override void Activate(bool forwardPlayback = true) { base.Activate(forwardPlayback); + keyNameText.FlashColour(Colour4.White, 200); inputIndicator.FadeIn().MoveToY(0).Then().MoveToY(3, 100, Easing.OutQuart); } From f1f30b94a694204b84061e9b85e5b9fc2ecdabe4 Mon Sep 17 00:00:00 2001 From: timiimit Date: Sun, 14 May 2023 11:05:47 +0200 Subject: [PATCH 004/169] Add StarRating Property --- osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs b/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs index c45f703b05..ef71e0ee17 100644 --- a/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs +++ b/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs @@ -53,6 +53,9 @@ namespace osu.Game.Online.Rooms [Key(9)] public DateTimeOffset? PlayedAt { get; set; } + [Key(10)] + public double StarRating { get; set; } + public MultiplayerPlaylistItem() { } @@ -69,6 +72,7 @@ namespace osu.Game.Online.Rooms Expired = item.Expired; PlaylistOrder = item.PlaylistOrder ?? 0; PlayedAt = item.PlayedAt; + // TODO: assign StarRating } } } From 5d687013214366aa6ff7fe09e998dea7f91cd79e Mon Sep 17 00:00:00 2001 From: timiimit Date: Sun, 14 May 2023 12:08:37 +0200 Subject: [PATCH 005/169] Copy StarRange when creating PlaylistItem --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 2 +- osu.Game/Online/Rooms/PlaylistItem.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 2be7327234..5716b7ad3b 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -783,7 +783,7 @@ namespace osu.Game.Online.Multiplayer RoomUpdated?.Invoke(); } - private PlaylistItem createPlaylistItem(MultiplayerPlaylistItem item) => new PlaylistItem(new APIBeatmap { OnlineID = item.BeatmapID }) + private PlaylistItem createPlaylistItem(MultiplayerPlaylistItem item) => new PlaylistItem(new APIBeatmap { OnlineID = item.BeatmapID, StarRating = item.StarRating }) { ID = item.ID, OwnerID = item.OwnerID, diff --git a/osu.Game/Online/Rooms/PlaylistItem.cs b/osu.Game/Online/Rooms/PlaylistItem.cs index 2213311c67..a900d8f3d7 100644 --- a/osu.Game/Online/Rooms/PlaylistItem.cs +++ b/osu.Game/Online/Rooms/PlaylistItem.cs @@ -91,7 +91,7 @@ namespace osu.Game.Online.Rooms } public PlaylistItem(MultiplayerPlaylistItem item) - : this(new APIBeatmap { OnlineID = item.BeatmapID }) + : this(new APIBeatmap { OnlineID = item.BeatmapID, StarRating = item.StarRating }) { ID = item.ID; OwnerID = item.OwnerID; From fd80ffd52bd4741c82266e78d8e03f405d75b0c7 Mon Sep 17 00:00:00 2001 From: timiimit Date: Sun, 14 May 2023 12:09:15 +0200 Subject: [PATCH 006/169] Fix display update condition --- .../Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs index 93c8faf0b0..2ee3bb30dd 100644 --- a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs @@ -85,15 +85,15 @@ namespace osu.Game.Screens.OnlinePlay.Components StarDifficulty minDifficulty; StarDifficulty maxDifficulty; - if (DifficultyRange.Value != null) + if (DifficultyRange.Value != null && Playlist.Count == 0) { + // When Playlist is empty (in lounge) we take retrieved range minDifficulty = new StarDifficulty(DifficultyRange.Value.Min, 0); maxDifficulty = new StarDifficulty(DifficultyRange.Value.Max, 0); } else { - // In multiplayer rooms, the beatmaps of playlist items will not be populated to a point this can be correct. - // Either populating them via BeatmapLookupCache or polling the API for the room's DifficultyRange will be required. + // When Playlist is not empty (in room) we compute actual range var orderedDifficulties = Playlist.Select(p => p.Beatmap).OrderBy(b => b.StarRating).ToArray(); minDifficulty = new StarDifficulty(orderedDifficulties.Length > 0 ? orderedDifficulties[0].StarRating : 0, 0); From 86a834fb568922ae975f014c20d410e71969ce44 Mon Sep 17 00:00:00 2001 From: timiimit Date: Sun, 14 May 2023 12:20:16 +0200 Subject: [PATCH 007/169] Fix TODO comment --- osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs b/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs index ef71e0ee17..64f971a2e4 100644 --- a/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs +++ b/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs @@ -72,7 +72,7 @@ namespace osu.Game.Online.Rooms Expired = item.Expired; PlaylistOrder = item.PlaylistOrder ?? 0; PlayedAt = item.PlayedAt; - // TODO: assign StarRating + StarRating = item.Beatmap.StarRating; // generally not available, but lets at least try to use it } } } From cb8d5f459f40e4c0bd4f4da51867deb546d0c908 Mon Sep 17 00:00:00 2001 From: timiimit Date: Mon, 15 May 2023 07:34:27 +0200 Subject: [PATCH 008/169] Remove bad comment --- osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs b/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs index 64f971a2e4..a102d118fe 100644 --- a/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs +++ b/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs @@ -72,7 +72,7 @@ namespace osu.Game.Online.Rooms Expired = item.Expired; PlaylistOrder = item.PlaylistOrder ?? 0; PlayedAt = item.PlayedAt; - StarRating = item.Beatmap.StarRating; // generally not available, but lets at least try to use it + StarRating = item.Beatmap.StarRating; } } } From 83ceb3457f8cc800d320ceb1406a04f7f9e3ae55 Mon Sep 17 00:00:00 2001 From: timiimit Date: Mon, 15 May 2023 07:35:09 +0200 Subject: [PATCH 009/169] Add xmldoc comment --- osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs b/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs index a102d118fe..daf45c5aee 100644 --- a/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs +++ b/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs @@ -60,6 +60,9 @@ namespace osu.Game.Online.Rooms { } + /// + /// This constructor should only be used for test purposes. + /// public MultiplayerPlaylistItem(PlaylistItem item) { ID = item.ID; From 7a5349d747862fc4ad165b82f6b54035ced4156c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 May 2023 20:09:40 +0900 Subject: [PATCH 010/169] Remove constructor from `MultiplayerPlaylistItem` which is only used in tests --- .../Multiplayer/TestSceneMatchStartControl.cs | 2 +- .../Multiplayer/TestSceneMultiplayer.cs | 4 ++-- .../TestSceneMultiplayerPlaylist.cs | 2 +- .../TestSceneMultiplayerQueueList.cs | 2 +- .../Online/Rooms/MultiplayerPlaylistItem.cs | 19 +----------------- .../Multiplayer/TestMultiplayerClient.cs | 20 +++++++++++++++++-- 6 files changed, 24 insertions(+), 25 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index 3efc7fbd30..6d309078e6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -129,7 +129,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Playlist = { - new MultiplayerPlaylistItem(playlistItem), + TestMultiplayerClient.CreateMultiplayerPlaylistItem(playlistItem), }, Users = { localUser }, Host = localUser, diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index d747d23229..09624f63b7 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -906,7 +906,7 @@ namespace osu.Game.Tests.Visual.Multiplayer enterGameplay(); AddStep("join other user", () => multiplayerClient.AddUser(new APIUser { Id = 1234 })); - AddStep("add item as other user", () => multiplayerClient.AddUserPlaylistItem(1234, new MultiplayerPlaylistItem( + AddStep("add item as other user", () => multiplayerClient.AddUserPlaylistItem(1234, TestMultiplayerClient.CreateMultiplayerPlaylistItem( new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID, @@ -938,7 +938,7 @@ namespace osu.Game.Tests.Visual.Multiplayer enterGameplay(); AddStep("join other user", () => multiplayerClient.AddUser(new APIUser { Id = 1234 })); - AddStep("add item as other user", () => multiplayerClient.AddUserPlaylistItem(1234, new MultiplayerPlaylistItem( + AddStep("add item as other user", () => multiplayerClient.AddUserPlaylistItem(1234, TestMultiplayerClient.CreateMultiplayerPlaylistItem( new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID, diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs index d7578b4114..2100f82886 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs @@ -215,7 +215,7 @@ namespace osu.Game.Tests.Visual.Multiplayer /// private void addItemStep(bool expired = false, int? userId = null) => AddStep("add item", () => { - MultiplayerClient.AddUserPlaylistItem(userId ?? API.LocalUser.Value.OnlineID, new MultiplayerPlaylistItem(new PlaylistItem(importedBeatmap) + MultiplayerClient.AddUserPlaylistItem(userId ?? API.LocalUser.Value.OnlineID, TestMultiplayerClient.CreateMultiplayerPlaylistItem(new PlaylistItem(importedBeatmap) { Expired = expired, PlayedAt = DateTimeOffset.Now diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs index bb37f1a5a7..47fb4e06ea 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs @@ -130,7 +130,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("add playlist item", () => { - MultiplayerPlaylistItem item = new MultiplayerPlaylistItem(new PlaylistItem(importedBeatmap)); + MultiplayerPlaylistItem item = TestMultiplayerClient.CreateMultiplayerPlaylistItem(new PlaylistItem(importedBeatmap)); MultiplayerClient.AddUserPlaylistItem(userId(), item).WaitSafely(); diff --git a/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs b/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs index daf45c5aee..8be703e620 100644 --- a/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs +++ b/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs @@ -56,26 +56,9 @@ namespace osu.Game.Online.Rooms [Key(10)] public double StarRating { get; set; } + [SerializationConstructor] public MultiplayerPlaylistItem() { } - - /// - /// This constructor should only be used for test purposes. - /// - public MultiplayerPlaylistItem(PlaylistItem item) - { - ID = item.ID; - OwnerID = item.OwnerID; - BeatmapID = item.Beatmap.OnlineID; - BeatmapChecksum = item.Beatmap.MD5Hash; - RulesetID = item.RulesetID; - RequiredMods = item.RequiredMods.ToArray(); - AllowedMods = item.AllowedMods.ToArray(); - Expired = item.Expired; - PlaylistOrder = item.PlaylistOrder ?? 0; - PlayedAt = item.PlayedAt; - StarRating = item.Beatmap.StarRating; - } } } diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index ad5e3f6c4d..0d9f91caa1 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -108,7 +108,8 @@ namespace osu.Game.Tests.Visual.Multiplayer // simulate the server's automatic assignment of users to teams on join. // the "best" team is the one with the least users on it. int bestTeam = teamVersus.Teams - .Select(team => (teamID: team.ID, userCount: ServerRoom.Users.Count(u => (u.MatchState as TeamVersusUserState)?.TeamID == team.ID))).MinBy(pair => pair.userCount).teamID; + .Select(team => (teamID: team.ID, userCount: ServerRoom.Users.Count(u => (u.MatchState as TeamVersusUserState)?.TeamID == team.ID))) + .MinBy(pair => pair.userCount).teamID; user.MatchState = new TeamVersusUserState { TeamID = bestTeam }; ((IMultiplayerClient)this).MatchUserStateChanged(clone(user.UserID), clone(user.MatchState)).WaitSafely(); @@ -232,7 +233,7 @@ namespace osu.Game.Tests.Visual.Multiplayer QueueMode = ServerAPIRoom.QueueMode.Value, AutoStartDuration = ServerAPIRoom.AutoStartDuration.Value }, - Playlist = ServerAPIRoom.Playlist.Select(item => new MultiplayerPlaylistItem(item)).ToList(), + Playlist = ServerAPIRoom.Playlist.Select(item => TestMultiplayerClient.CreateMultiplayerPlaylistItem(item)).ToList(), Users = { localUser }, Host = localUser }; @@ -637,5 +638,20 @@ namespace osu.Game.Tests.Visual.Multiplayer byte[]? serialized = MessagePackSerializer.Serialize(typeof(T), incoming, SignalRUnionWorkaroundResolver.OPTIONS); return MessagePackSerializer.Deserialize(serialized, SignalRUnionWorkaroundResolver.OPTIONS); } + + public static MultiplayerPlaylistItem CreateMultiplayerPlaylistItem(PlaylistItem item) => new MultiplayerPlaylistItem + { + ID = item.ID, + OwnerID = item.OwnerID, + BeatmapID = item.Beatmap.OnlineID, + BeatmapChecksum = item.Beatmap.MD5Hash, + RulesetID = item.RulesetID, + RequiredMods = item.RequiredMods.ToArray(), + AllowedMods = item.AllowedMods.ToArray(), + Expired = item.Expired, + PlaylistOrder = item.PlaylistOrder ?? 0, + PlayedAt = item.PlayedAt, + StarRating = item.Beatmap.StarRating, + }; } } From 1c199b83e3453678888cfbe03b73cb123e43cfdb Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 29 May 2023 21:14:03 +0900 Subject: [PATCH 011/169] Replace mania scroll "time" with scroll "speed" --- .../ManiaRulesetConfigManager.cs | 29 +++++++++++--- .../ManiaSettingsSubsection.cs | 9 ++--- .../UI/DrawableManiaRuleset.cs | 38 ++++++++++--------- .../Localisation/RulesetSettingsStrings.cs | 2 +- 4 files changed, 49 insertions(+), 29 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs index 99a80ef28d..5ba5125c2d 100644 --- a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs +++ b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs @@ -15,24 +15,41 @@ namespace osu.Game.Rulesets.Mania.Configuration public ManiaRulesetConfigManager(SettingsStore? settings, RulesetInfo ruleset, int? variant = null) : base(settings, ruleset, variant) { + migrate(); } protected override void InitialiseDefaults() { base.InitialiseDefaults(); - SetDefault(ManiaRulesetSetting.ScrollTime, 1500.0, DrawableManiaRuleset.MIN_TIME_RANGE, DrawableManiaRuleset.MAX_TIME_RANGE, 5); +#pragma warning disable CS0618 + // Although obsolete, this is still required to populate the bindable from the database in case migration is required. + SetDefault(ManiaRulesetSetting.ScrollTime, null); +#pragma warning restore CS0618 + + SetDefault(ManiaRulesetSetting.ScrollSpeed, 8, 1, 40); SetDefault(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down); SetDefault(ManiaRulesetSetting.TimingBasedNoteColouring, false); } +#pragma warning disable CS0618 + private void migrate() + { + if (Get(ManiaRulesetSetting.ScrollTime) is double scrollTime) + { + SetValue(ManiaRulesetSetting.ScrollSpeed, (int)Math.Round(DrawableManiaRuleset.MAX_TIME_RANGE / scrollTime)); + SetValue(ManiaRulesetSetting.ScrollTime, null); + } + } +#pragma warning restore CS0618 + public override TrackedSettings CreateTrackedSettings() => new TrackedSettings { - new TrackedSetting(ManiaRulesetSetting.ScrollTime, - scrollTime => new SettingDescription( - rawValue: scrollTime, + new TrackedSetting(ManiaRulesetSetting.ScrollSpeed, + speed => new SettingDescription( + rawValue: speed, name: RulesetSettingsStrings.ScrollSpeed, - value: RulesetSettingsStrings.ScrollSpeedTooltip(scrollTime, (int)Math.Round(DrawableManiaRuleset.MAX_TIME_RANGE / scrollTime)) + value: RulesetSettingsStrings.ScrollSpeedTooltip(DrawableManiaRuleset.ComputeScrollTime(speed), speed) ) ) }; @@ -40,7 +57,9 @@ namespace osu.Game.Rulesets.Mania.Configuration public enum ManiaRulesetSetting { + [Obsolete("Use ScrollSpeed instead.")] // Can be removed 2023-11-30 ScrollTime, + ScrollSpeed, ScrollDirection, TimingBasedNoteColouring } diff --git a/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs b/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs index fc0b4a9ed9..a5434a36ab 100644 --- a/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs +++ b/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs @@ -1,7 +1,6 @@ // 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.Graphics; using osu.Framework.Localisation; @@ -34,10 +33,10 @@ namespace osu.Game.Rulesets.Mania LabelText = RulesetSettingsStrings.ScrollingDirection, Current = config.GetBindable(ManiaRulesetSetting.ScrollDirection) }, - new SettingsSlider + new SettingsSlider { LabelText = RulesetSettingsStrings.ScrollSpeed, - Current = config.GetBindable(ManiaRulesetSetting.ScrollTime), + Current = config.GetBindable(ManiaRulesetSetting.ScrollSpeed), KeyboardStep = 5 }, new SettingsCheckbox @@ -48,9 +47,9 @@ namespace osu.Game.Rulesets.Mania }; } - private partial class ManiaScrollSlider : RoundedSliderBar + private partial class ManiaScrollSlider : RoundedSliderBar { - public override LocalisableString TooltipText => RulesetSettingsStrings.ScrollSpeedTooltip(Current.Value, (int)Math.Round(DrawableManiaRuleset.MAX_TIME_RANGE / Current.Value)); + public override LocalisableString TooltipText => RulesetSettingsStrings.ScrollSpeedTooltip(DrawableManiaRuleset.ComputeScrollTime(Current.Value), Current.Value); } } } diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index af8758fb5e..2d373c0471 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -33,12 +33,12 @@ namespace osu.Game.Rulesets.Mania.UI public partial class DrawableManiaRuleset : DrawableScrollingRuleset { /// - /// The minimum time range. This occurs at a of 40. + /// The minimum time range. This occurs at a of 40. /// public const double MIN_TIME_RANGE = 290; /// - /// The maximum time range. This occurs at a of 1. + /// The maximum time range. This occurs with a of 1. /// public const double MAX_TIME_RANGE = 11485; @@ -69,7 +69,8 @@ namespace osu.Game.Rulesets.Mania.UI protected override ScrollVisualisationMethod VisualisationMethod => scrollMethod; private readonly Bindable configDirection = new Bindable(); - private readonly BindableDouble configTimeRange = new BindableDouble(); + private readonly BindableInt configScrollSpeed = new BindableInt(); + private double smoothTimeRange; // Stores the current speed adjustment active in gameplay. private readonly Track speedAdjustmentTrack = new TrackVirtual(0); @@ -78,6 +79,9 @@ namespace osu.Game.Rulesets.Mania.UI : base(ruleset, beatmap, mods) { BarLines = new BarLineGenerator(Beatmap).BarLines; + + TimeRange.MinValue = 1; + TimeRange.MaxValue = MAX_TIME_RANGE; } [BackgroundDependencyLoader] @@ -104,30 +108,28 @@ namespace osu.Game.Rulesets.Mania.UI Config.BindWith(ManiaRulesetSetting.ScrollDirection, configDirection); configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true); - Config.BindWith(ManiaRulesetSetting.ScrollTime, configTimeRange); - TimeRange.MinValue = configTimeRange.MinValue; - TimeRange.MaxValue = configTimeRange.MaxValue; + Config.BindWith(ManiaRulesetSetting.ScrollSpeed, configScrollSpeed); + configScrollSpeed.BindValueChanged(speed => this.TransformTo(nameof(smoothTimeRange), ComputeScrollTime(speed.NewValue), 200, Easing.OutQuint)); + + TimeRange.Value = smoothTimeRange = ComputeScrollTime(configScrollSpeed.Value); } - protected override void AdjustScrollSpeed(int amount) - { - this.TransformTo(nameof(relativeTimeRange), relativeTimeRange + amount, 200, Easing.OutQuint); - } - - private double relativeTimeRange - { - get => MAX_TIME_RANGE / configTimeRange.Value; - set => configTimeRange.Value = MAX_TIME_RANGE / value; - } + protected override void AdjustScrollSpeed(int amount) => configScrollSpeed.Value += amount; protected override void Update() { base.Update(); - updateTimeRange(); } - private void updateTimeRange() => TimeRange.Value = configTimeRange.Value * speedAdjustmentTrack.AggregateTempo.Value * speedAdjustmentTrack.AggregateFrequency.Value; + private void updateTimeRange() => TimeRange.Value = smoothTimeRange * speedAdjustmentTrack.AggregateTempo.Value * speedAdjustmentTrack.AggregateFrequency.Value; + + /// + /// Computes a scroll time (in milliseconds) from a scroll speed in the range of 1-40. + /// + /// The scroll speed. + /// The scroll time. + public static double ComputeScrollTime(int scrollSpeed) => MAX_TIME_RANGE / scrollSpeed; public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new ManiaPlayfieldAdjustmentContainer(); diff --git a/osu.Game/Localisation/RulesetSettingsStrings.cs b/osu.Game/Localisation/RulesetSettingsStrings.cs index 52e6a5eaac..91bbece004 100644 --- a/osu.Game/Localisation/RulesetSettingsStrings.cs +++ b/osu.Game/Localisation/RulesetSettingsStrings.cs @@ -82,7 +82,7 @@ namespace osu.Game.Localisation /// /// "{0}ms (speed {1})" /// - public static LocalisableString ScrollSpeedTooltip(double arg0, int arg1) => new TranslatableString(getKey(@"ruleset"), @"{0}ms (speed {1})", arg0, arg1); + public static LocalisableString ScrollSpeedTooltip(double scrollTime, int scrollSpeed) => new TranslatableString(getKey(@"ruleset"), @"{0:0}ms (speed {1})", scrollTime, scrollSpeed); private static string getKey(string key) => $@"{prefix}:{key}"; } From b456c36f6453309ad902c8a488696dd6798ed86f Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 30 May 2023 17:27:48 +0900 Subject: [PATCH 012/169] Migrate in InitialiseDefaults() --- .../Configuration/ManiaRulesetConfigManager.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs index 5ba5125c2d..b2155968ea 100644 --- a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs +++ b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs @@ -15,33 +15,27 @@ namespace osu.Game.Rulesets.Mania.Configuration public ManiaRulesetConfigManager(SettingsStore? settings, RulesetInfo ruleset, int? variant = null) : base(settings, ruleset, variant) { - migrate(); } protected override void InitialiseDefaults() { base.InitialiseDefaults(); -#pragma warning disable CS0618 - // Although obsolete, this is still required to populate the bindable from the database in case migration is required. - SetDefault(ManiaRulesetSetting.ScrollTime, null); -#pragma warning restore CS0618 - SetDefault(ManiaRulesetSetting.ScrollSpeed, 8, 1, 40); SetDefault(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down); SetDefault(ManiaRulesetSetting.TimingBasedNoteColouring, false); - } #pragma warning disable CS0618 - private void migrate() - { + // Although obsolete, this is still required to populate the bindable from the database in case migration is required. + SetDefault(ManiaRulesetSetting.ScrollTime, null); + if (Get(ManiaRulesetSetting.ScrollTime) is double scrollTime) { SetValue(ManiaRulesetSetting.ScrollSpeed, (int)Math.Round(DrawableManiaRuleset.MAX_TIME_RANGE / scrollTime)); SetValue(ManiaRulesetSetting.ScrollTime, null); } - } #pragma warning restore CS0618 + } public override TrackedSettings CreateTrackedSettings() => new TrackedSettings { From d119447a10a1ac0312a6aa9691348cbabed8c6ba Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 30 May 2023 16:41:42 +0300 Subject: [PATCH 013/169] Fix editor timeline hitobjects popping in --- .../Compose/Components/Timeline/TimelineHitObjectBlueprint.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index ea063e9216..900f0ff4a2 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -244,6 +244,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public override Vector2 ScreenSpaceSelectionPoint => ScreenSpaceDrawQuad.TopLeft; + protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false; + private partial class Tick : Circle { public Tick() From 7bc3b2072caed1124ddc3b722957a08e0084fd44 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 1 Jun 2023 00:44:13 +0900 Subject: [PATCH 014/169] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 6aebae665d..c88bea8265 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -11,7 +11,7 @@ manifestmerger.jar - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 0fd2b0c2c5..8a941ca6c1 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index e4a169f8e5..1dcece7741 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -16,6 +16,6 @@ iossimulator-x64 - + From 659fb80c16ce124dfd66ac1c4d3ea864e376ec0c Mon Sep 17 00:00:00 2001 From: timiimit Date: Wed, 31 May 2023 19:06:41 +0200 Subject: [PATCH 015/169] Add `BeatmapInfo.LastEditTime` property --- osu.Game/Beatmaps/BeatmapInfo.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 393feff087..7ca0e24913 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -171,6 +171,8 @@ namespace osu.Game.Beatmaps public double TimelineZoom { get; set; } = 1.0; + public double? LastEditTime { get; set; } + [Ignored] public CountdownType Countdown { get; set; } = CountdownType.Normal; From a58426dab6ef91c11db45400ed16a6acfc2f6563 Mon Sep 17 00:00:00 2001 From: timiimit Date: Wed, 31 May 2023 19:07:04 +0200 Subject: [PATCH 016/169] Use `LastEditTime` in `Editor` --- osu.Game/Screens/Edit/Editor.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index b8fa7f6579..c3d72c7aec 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -441,6 +441,8 @@ namespace osu.Game.Screens.Edit try { + editorBeatmap.BeatmapInfo.LastEditTime = clock.CurrentTime; + // save the loaded beatmap's data stream. beatmapManager.Save(editorBeatmap.BeatmapInfo, editorBeatmap.PlayableBeatmap, editorBeatmap.BeatmapSkin); } @@ -833,7 +835,11 @@ namespace osu.Game.Screens.Edit { double targetTime = 0; - if (Beatmap.Value.Beatmap.HitObjects.Count > 0) + if (editorBeatmap.BeatmapInfo.LastEditTime != null) + { + targetTime = editorBeatmap.BeatmapInfo.LastEditTime.Value; + } + else if (Beatmap.Value.Beatmap.HitObjects.Count > 0) { // seek to one beat length before the first hitobject targetTime = Beatmap.Value.Beatmap.HitObjects[0].StartTime; From 310c54fe28ed32af6ac804c084c5c1de18d38651 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 1 Jun 2023 13:26:46 +0900 Subject: [PATCH 017/169] Add test coverage ensuring positional data is present in hit events --- .../Mods/TestSceneOsuModAutoplay.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs index 8fdab9f1f9..616a9c362d 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs @@ -17,6 +17,18 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { public partial class TestSceneOsuModAutoplay : OsuModTestScene { + [Test] + public void TestCursorPositionStoredToJudgement() + { + CreateModTest(new ModTestData + { + Autoplay = true, + PassCondition = () => + Player.ScoreProcessor.JudgedHits >= 1 + && Player.ScoreProcessor.HitEvents.Any(e => e.Position != null) + }); + } + [Test] public void TestSpmUnaffectedByRateAdjust() => runSpmTest(new OsuModDaycore From e830b96e6182308093e0ab61afcb6f3bf01db9cd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 1 Jun 2023 13:09:47 +0900 Subject: [PATCH 018/169] Add back required override to make `AccuracyHeatmap` work --- osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index ab07ac3e9d..f97be0d7ff 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Scoring @@ -13,6 +15,9 @@ namespace osu.Game.Rulesets.Osu.Scoring { } + protected override HitEvent CreateHitEvent(JudgementResult result) + => base.CreateHitEvent(result).With((result as OsuHitCircleJudgementResult)?.CursorPositionAtHit); + protected override double ComputeTotalScore(double comboProgress, double accuracyProgress, double bonusPortion) { return 700000 * comboProgress From dc595b83f12761a410240e404dc965a27258a37f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 1 Jun 2023 14:25:18 +0900 Subject: [PATCH 019/169] Remove unused `Dimension` specification from `StatisticItem` --- osu.Game/Screens/Ranking/Statistics/StatisticItem.cs | 10 +--------- osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Ranking/Statistics/StatisticItem.cs b/osu.Game/Screens/Ranking/Statistics/StatisticItem.cs index 5bbd260d3f..77a40959ae 100644 --- a/osu.Game/Screens/Ranking/Statistics/StatisticItem.cs +++ b/osu.Game/Screens/Ranking/Statistics/StatisticItem.cs @@ -6,7 +6,6 @@ using System; using JetBrains.Annotations; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; namespace osu.Game.Screens.Ranking.Statistics @@ -26,11 +25,6 @@ namespace osu.Game.Screens.Ranking.Statistics /// public readonly Func CreateContent; - /// - /// The of this row. This can be thought of as the column dimension of an encompassing . - /// - public readonly Dimension Dimension; - /// /// Whether this item requires hit events. If true, will not be called if no hit events are available. /// @@ -42,13 +36,11 @@ namespace osu.Game.Screens.Ranking.Statistics /// The name of the item. Can be to hide the item header. /// A function returning the content to be displayed. /// Whether this item requires hit events. If true, will not be called if no hit events are available. - /// The of this item. This can be thought of as the column dimension of an encompassing . - public StatisticItem(LocalisableString name, [NotNull] Func createContent, bool requiresHitEvents = false, [CanBeNull] Dimension dimension = null) + public StatisticItem(LocalisableString name, [NotNull] Func createContent, bool requiresHitEvents = false) { Name = name; RequiresHitEvents = requiresHitEvents; CreateContent = createContent; - Dimension = dimension; } } } diff --git a/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs b/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs index 4c22afd8f7..c11c42e290 100644 --- a/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs +++ b/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs @@ -168,7 +168,7 @@ namespace osu.Game.Screens.Ranking.Statistics Origin = Anchor.Centre, }); - dimensions.Add(col.Dimension ?? new Dimension()); + dimensions.Add(new Dimension()); } rows.Add(new GridContainer From 985604fab5bb4a74ef7eacdcd842e8190c74047b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 1 Jun 2023 14:35:14 +0900 Subject: [PATCH 020/169] Return `StatisticItem`s rather than `StatisticRow`s from ruleset There were no usages of more than one column being provided per row, so it seemed like unnecessarily complexity. I'm currently trying to reduce complexity so we can improve the layout of the results screen, which currently has up to three levels of nested `GridContainer`s. Of note, I can't add backwards compatibility because the method signature has not changed in `Ruleset` (only the return type). If we do want to keep compatibility with other rulesets, we could designate a new name for the updated method. --- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 44 ++++--------- osu.Game.Rulesets.Osu/OsuRuleset.cs | 58 +++++------------ osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 44 ++++--------- .../Ranking/TestSceneStatisticsPanel.cs | 65 +++---------------- osu.Game/Rulesets/Ruleset.cs | 4 +- .../Statistics/SimpleStatisticTable.cs | 2 +- .../Ranking/Statistics/SoloStatisticsPanel.cs | 26 +++----- .../Ranking/Statistics/StatisticItem.cs | 2 +- .../Ranking/Statistics/StatisticRow.cs | 21 ------ .../Ranking/Statistics/StatisticsPanel.cs | 40 +++++------- 10 files changed, 83 insertions(+), 223 deletions(-) delete mode 100644 osu.Game/Screens/Ranking/Statistics/StatisticRow.cs diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index d324682989..e8fda3ec80 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -389,41 +389,23 @@ namespace osu.Game.Rulesets.Mania return base.GetDisplayNameForHitResult(result); } - public override StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) => new[] + public override StatisticItem[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) => new[] { - new StatisticRow + new StatisticItem("Performance Breakdown", () => new PerformanceBreakdownChart(score, playableBeatmap) { - Columns = new[] - { - new StatisticItem("Performance Breakdown", () => new PerformanceBreakdownChart(score, playableBeatmap) - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y - }), - } - }, - new StatisticRow + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }), + new StatisticItem("Timing Distribution", () => new HitEventTimingDistributionGraph(score.HitEvents) { - Columns = new[] - { - new StatisticItem("Timing Distribution", () => new HitEventTimingDistributionGraph(score.HitEvents) - { - RelativeSizeAxes = Axes.X, - Height = 250 - }, true), - } - }, - new StatisticRow + RelativeSizeAxes = Axes.X, + Height = 250 + }, true), + new StatisticItem(string.Empty, () => new SimpleStatisticTable(3, new SimpleStatisticItem[] { - Columns = new[] - { - new StatisticItem(string.Empty, () => new SimpleStatisticTable(3, new SimpleStatisticItem[] - { - new AverageHitError(score.HitEvents), - new UnstableRate(score.HitEvents) - }), true) - } - } + new AverageHitError(score.HitEvents), + new UnstableRate(score.HitEvents) + }), true) }; public override IRulesetFilterCriteria CreateRulesetFilterCriteria() diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 922594a93a..8ce55d78dd 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -291,56 +291,32 @@ namespace osu.Game.Rulesets.Osu return base.GetDisplayNameForHitResult(result); } - public override StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) + public override StatisticItem[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) { var timedHitEvents = score.HitEvents.Where(e => e.HitObject is HitCircle && !(e.HitObject is SliderTailCircle)).ToList(); return new[] { - new StatisticRow + new StatisticItem("Performance Breakdown", () => new PerformanceBreakdownChart(score, playableBeatmap) { - Columns = new[] - { - new StatisticItem("Performance Breakdown", () => new PerformanceBreakdownChart(score, playableBeatmap) - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y - }), - } - }, - new StatisticRow + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }), + new StatisticItem("Timing Distribution", () => new HitEventTimingDistributionGraph(timedHitEvents) { - Columns = new[] - { - new StatisticItem("Timing Distribution", () => new HitEventTimingDistributionGraph(timedHitEvents) - { - RelativeSizeAxes = Axes.X, - Height = 250 - }, true), - } - }, - new StatisticRow + RelativeSizeAxes = Axes.X, + Height = 250 + }, true), + new StatisticItem("Accuracy Heatmap", () => new AccuracyHeatmap(score, playableBeatmap) { - Columns = new[] - { - new StatisticItem("Accuracy Heatmap", () => new AccuracyHeatmap(score, playableBeatmap) - { - RelativeSizeAxes = Axes.X, - Height = 250 - }, true), - } - }, - new StatisticRow + RelativeSizeAxes = Axes.X, + Height = 250 + }, true), + new StatisticItem(string.Empty, () => new SimpleStatisticTable(3, new SimpleStatisticItem[] { - Columns = new[] - { - new StatisticItem(string.Empty, () => new SimpleStatisticTable(3, new SimpleStatisticItem[] - { - new AverageHitError(timedHitEvents), - new UnstableRate(timedHitEvents) - }), true) - } - } + new AverageHitError(timedHitEvents), + new UnstableRate(timedHitEvents) + }), true) }; } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index a35fdb890d..d6824109b3 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -229,45 +229,27 @@ namespace osu.Game.Rulesets.Taiko return base.GetDisplayNameForHitResult(result); } - public override StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) + public override StatisticItem[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) { var timedHitEvents = score.HitEvents.Where(e => e.HitObject is Hit).ToList(); return new[] { - new StatisticRow + new StatisticItem("Performance Breakdown", () => new PerformanceBreakdownChart(score, playableBeatmap) { - Columns = new[] - { - new StatisticItem("Performance Breakdown", () => new PerformanceBreakdownChart(score, playableBeatmap) - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y - }), - } - }, - new StatisticRow + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }), + new StatisticItem("Timing Distribution", () => new HitEventTimingDistributionGraph(timedHitEvents) { - Columns = new[] - { - new StatisticItem("Timing Distribution", () => new HitEventTimingDistributionGraph(timedHitEvents) - { - RelativeSizeAxes = Axes.X, - Height = 250 - }, true), - } - }, - new StatisticRow + RelativeSizeAxes = Axes.X, + Height = 250 + }, true), + new StatisticItem(string.Empty, () => new SimpleStatisticTable(3, new SimpleStatisticItem[] { - Columns = new[] - { - new StatisticItem(string.Empty, () => new SimpleStatisticTable(3, new SimpleStatisticItem[] - { - new AverageHitError(timedHitEvents), - new UnstableRate(timedHitEvents) - }), true) - } - } + new AverageHitError(timedHitEvents), + new UnstableRate(timedHitEvents) + }), true) }; } } diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs b/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs index fcd5f97fcc..67211a3b72 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneStatisticsPanel.cs @@ -174,78 +174,33 @@ namespace osu.Game.Tests.Visual.Ranking private class TestRulesetAllStatsRequireHitEvents : TestRuleset { - public override StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) + public override StatisticItem[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) => new[] { - return new[] - { - new StatisticRow - { - Columns = new[] - { - new StatisticItem("Statistic Requiring Hit Events 1", - () => CreatePlaceholderStatistic("Placeholder statistic. Requires hit events"), true) - } - }, - new StatisticRow - { - Columns = new[] - { - new StatisticItem("Statistic Requiring Hit Events 2", - () => CreatePlaceholderStatistic("Placeholder statistic. Requires hit events"), true) - } - } - }; - } + new StatisticItem("Statistic Requiring Hit Events 1", () => CreatePlaceholderStatistic("Placeholder statistic. Requires hit events"), true), + new StatisticItem("Statistic Requiring Hit Events 2", () => CreatePlaceholderStatistic("Placeholder statistic. Requires hit events"), true) + }; } private class TestRulesetNoStatsRequireHitEvents : TestRuleset { - public override StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) + public override StatisticItem[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) { return new[] { - new StatisticRow - { - Columns = new[] - { - new StatisticItem("Statistic Not Requiring Hit Events 1", - () => CreatePlaceholderStatistic("Placeholder statistic. Does not require hit events")) - } - }, - new StatisticRow - { - Columns = new[] - { - new StatisticItem("Statistic Not Requiring Hit Events 2", - () => CreatePlaceholderStatistic("Placeholder statistic. Does not require hit events")) - } - } + new StatisticItem("Statistic Not Requiring Hit Events 1", () => CreatePlaceholderStatistic("Placeholder statistic. Does not require hit events")), + new StatisticItem("Statistic Not Requiring Hit Events 2", () => CreatePlaceholderStatistic("Placeholder statistic. Does not require hit events")) }; } } private class TestRulesetMixed : TestRuleset { - public override StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) + public override StatisticItem[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) { return new[] { - new StatisticRow - { - Columns = new[] - { - new StatisticItem("Statistic Requiring Hit Events", - () => CreatePlaceholderStatistic("Placeholder statistic. Requires hit events"), true) - } - }, - new StatisticRow - { - Columns = new[] - { - new StatisticItem("Statistic Not Requiring Hit Events", - () => CreatePlaceholderStatistic("Placeholder statistic. Does not require hit events")) - } - } + new StatisticItem("Statistic Requiring Hit Events", () => CreatePlaceholderStatistic("Placeholder statistic. Requires hit events"), true), + new StatisticItem("Statistic Not Requiring Hit Events", () => CreatePlaceholderStatistic("Placeholder statistic. Does not require hit events")) }; } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index a77068eb14..490ec1475c 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -321,8 +321,8 @@ namespace osu.Game.Rulesets /// /// The to create the statistics for. The score is guaranteed to have populated. /// The , converted for this with all relevant s applied. - /// The s to display. Each may contain 0 or more . - public virtual StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) => Array.Empty(); + /// The s to display. + public virtual StatisticItem[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) => Array.Empty(); /// /// Get all valid s for this ruleset. diff --git a/osu.Game/Screens/Ranking/Statistics/SimpleStatisticTable.cs b/osu.Game/Screens/Ranking/Statistics/SimpleStatisticTable.cs index d10888be43..d68df4558a 100644 --- a/osu.Game/Screens/Ranking/Statistics/SimpleStatisticTable.cs +++ b/osu.Game/Screens/Ranking/Statistics/SimpleStatisticTable.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.Ranking.Statistics { /// /// Represents a table with simple statistics (ones that only need textual display). - /// Richer visualisations should be done with s and s. + /// Richer visualisations should be done with s. /// public partial class SimpleStatisticTable : CompositeDrawable { diff --git a/osu.Game/Screens/Ranking/Statistics/SoloStatisticsPanel.cs b/osu.Game/Screens/Ranking/Statistics/SoloStatisticsPanel.cs index 57d072b7de..73b9897096 100644 --- a/osu.Game/Screens/Ranking/Statistics/SoloStatisticsPanel.cs +++ b/osu.Game/Screens/Ranking/Statistics/SoloStatisticsPanel.cs @@ -23,32 +23,26 @@ namespace osu.Game.Screens.Ranking.Statistics public Bindable StatisticsUpdate { get; } = new Bindable(); - protected override ICollection CreateStatisticRows(ScoreInfo newScore, IBeatmap playableBeatmap) + protected override ICollection CreateStatisticItems(ScoreInfo newScore, IBeatmap playableBeatmap) { - var rows = base.CreateStatisticRows(newScore, playableBeatmap); + var items = base.CreateStatisticItems(newScore, playableBeatmap); if (newScore.UserID > 1 && newScore.UserID == achievedScore.UserID && newScore.OnlineID > 0 && newScore.OnlineID == achievedScore.OnlineID) { - rows = rows.Append(new StatisticRow + items = items.Append(new StatisticItem("Overall Ranking", () => new OverallRanking { - Columns = new[] - { - new StatisticItem("Overall Ranking", () => new OverallRanking - { - RelativeSizeAxes = Axes.X, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Width = 0.5f, - StatisticsUpdate = { BindTarget = StatisticsUpdate } - }) - } - }).ToArray(); + RelativeSizeAxes = Axes.X, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 0.5f, + StatisticsUpdate = { BindTarget = StatisticsUpdate } + })).ToArray(); } - return rows; + return items; } } } diff --git a/osu.Game/Screens/Ranking/Statistics/StatisticItem.cs b/osu.Game/Screens/Ranking/Statistics/StatisticItem.cs index 77a40959ae..c5bdc6f6f5 100644 --- a/osu.Game/Screens/Ranking/Statistics/StatisticItem.cs +++ b/osu.Game/Screens/Ranking/Statistics/StatisticItem.cs @@ -31,7 +31,7 @@ namespace osu.Game.Screens.Ranking.Statistics public readonly bool RequiresHitEvents; /// - /// Creates a new , to be displayed inside a in the results screen. + /// Creates a new , to be displayed in the results screen. /// /// The name of the item. Can be to hide the item header. /// A function returning the content to be displayed. diff --git a/osu.Game/Screens/Ranking/Statistics/StatisticRow.cs b/osu.Game/Screens/Ranking/Statistics/StatisticRow.cs deleted file mode 100644 index 9f5f44918e..0000000000 --- a/osu.Game/Screens/Ranking/Statistics/StatisticRow.cs +++ /dev/null @@ -1,21 +0,0 @@ -// 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 JetBrains.Annotations; - -namespace osu.Game.Screens.Ranking.Statistics -{ - /// - /// A row of statistics to be displayed in the results screen. - /// - public class StatisticRow - { - /// - /// The columns of this . - /// - [ItemNotNull] - public StatisticItem[] Columns; - } -} diff --git a/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs b/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs index c11c42e290..31dd5df27a 100644 --- a/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs +++ b/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs @@ -100,9 +100,9 @@ namespace osu.Game.Screens.Ranking.Statistics bool hitEventsAvailable = newScore.HitEvents.Count != 0; Container container; - var statisticRows = CreateStatisticRows(newScore, task.GetResultSafely()); + var statisticItems = CreateStatisticItems(newScore, task.GetResultSafely()); - if (!hitEventsAvailable && statisticRows.SelectMany(r => r.Columns).All(c => c.RequiresHitEvents)) + if (!hitEventsAvailable && statisticItems.All(c => c.RequiresHitEvents)) { container = new FillFlowContainer { @@ -144,33 +144,25 @@ namespace osu.Game.Screens.Ranking.Statistics bool anyRequiredHitEvents = false; - foreach (var row in statisticRows) + foreach (var item in statisticItems) { - var columns = row.Columns; - - if (columns.Length == 0) - continue; - var columnContent = new List(); var dimensions = new List(); - foreach (var col in columns) + if (!hitEventsAvailable && item.RequiresHitEvents) { - if (!hitEventsAvailable && col.RequiresHitEvents) - { - anyRequiredHitEvents = true; - continue; - } - - columnContent.Add(new StatisticContainer(col) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }); - - dimensions.Add(new Dimension()); + anyRequiredHitEvents = true; + continue; } + columnContent.Add(new StatisticContainer(item) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }); + + dimensions.Add(new Dimension()); + rows.Add(new GridContainer { Anchor = Anchor.TopCentre, @@ -219,11 +211,11 @@ namespace osu.Game.Screens.Ranking.Statistics } /// - /// Creates the s to be displayed in this panel for a given . + /// Creates the s to be displayed in this panel for a given . /// /// The score to create the rows for. /// The beatmap on which the score was set. - protected virtual ICollection CreateStatisticRows(ScoreInfo newScore, IBeatmap playableBeatmap) + protected virtual ICollection CreateStatisticItems(ScoreInfo newScore, IBeatmap playableBeatmap) => newScore.Ruleset.CreateInstance().CreateStatisticsForScore(newScore, playableBeatmap); protected override bool OnClick(ClickEvent e) From 9f8a13480bcf6d760b11e92381b51d59d53a3e4a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 1 Jun 2023 15:16:47 +0900 Subject: [PATCH 021/169] Automatically disable tablet support on error Closes #23710. --- osu.Game/OsuGame.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index fe6e479d19..33ac8cc101 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1136,12 +1136,19 @@ namespace osu.Game if (entry.Level == LogLevel.Error) { - Schedule(() => Notifications.Post(new SimpleNotification + Schedule(() => { - Text = $"Encountered tablet error: \"{message}\"", - Icon = FontAwesome.Solid.PenSquare, - IconColour = Colours.RedDark, - })); + Notifications.Post(new SimpleNotification + { + Text = $"Disabling tablet support due to error: \"{message}\"", + Icon = FontAwesome.Solid.PenSquare, + IconColour = Colours.RedDark, + }); + + var tabletHandler = Host.AvailableInputHandlers.OfType().FirstOrDefault(); + if (tabletHandler != null) + tabletHandler.Enabled.Value = false; + }); } else if (notifyOnWarning) { From 0a7d5a51d419055990d9da48345de1284408639f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 1 Jun 2023 15:50:10 +0900 Subject: [PATCH 022/169] Fix mouse cursor potentially disappearing for good if screenshot capture fails --- osu.Game/Graphics/ScreenshotManager.cs | 115 +++++++++++++------------ 1 file changed, 60 insertions(+), 55 deletions(-) diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index d799e82bc9..dd90cbc074 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -86,70 +86,75 @@ namespace osu.Game.Graphics { Interlocked.Increment(ref screenShotTasks); - if (!captureMenuCursor.Value) + try { - cursorVisibility.Value = false; - - // We need to wait for at most 3 draw nodes to be drawn, following which we can be assured at least one DrawNode has been generated/drawn with the set value - const int frames_to_wait = 3; - - int framesWaited = 0; - - using (var framesWaitedEvent = new ManualResetEventSlim(false)) + if (!captureMenuCursor.Value) { - ScheduledDelegate waitDelegate = host.DrawThread.Scheduler.AddDelayed(() => + cursorVisibility.Value = false; + + // We need to wait for at most 3 draw nodes to be drawn, following which we can be assured at least one DrawNode has been generated/drawn with the set value + const int frames_to_wait = 3; + + int framesWaited = 0; + + using (var framesWaitedEvent = new ManualResetEventSlim(false)) { - if (framesWaited++ >= frames_to_wait) - // ReSharper disable once AccessToDisposedClosure - framesWaitedEvent.Set(); - }, 10, true); + ScheduledDelegate waitDelegate = host.DrawThread.Scheduler.AddDelayed(() => + { + if (framesWaited++ >= frames_to_wait) + // ReSharper disable once AccessToDisposedClosure + framesWaitedEvent.Set(); + }, 10, true); - if (!framesWaitedEvent.Wait(1000)) - throw new TimeoutException("Screenshot data did not arrive in a timely fashion"); + if (!framesWaitedEvent.Wait(1000)) + throw new TimeoutException("Screenshot data did not arrive in a timely fashion"); - waitDelegate.Cancel(); + waitDelegate.Cancel(); + } + } + + using (var image = await host.TakeScreenshotAsync().ConfigureAwait(false)) + { + host.GetClipboard()?.SetImage(image); + + (string filename, var stream) = getWritableStream(); + + if (filename == null) return; + + using (stream) + { + switch (screenshotFormat.Value) + { + case ScreenshotFormat.Png: + await image.SaveAsPngAsync(stream).ConfigureAwait(false); + break; + + case ScreenshotFormat.Jpg: + const int jpeg_quality = 92; + + await image.SaveAsJpegAsync(stream, new JpegEncoder { Quality = jpeg_quality }).ConfigureAwait(false); + break; + + default: + throw new InvalidOperationException($"Unknown enum member {nameof(ScreenshotFormat)} {screenshotFormat.Value}."); + } + } + + notificationOverlay.Post(new SimpleNotification + { + Text = $"Screenshot {filename} saved!", + Activated = () => + { + storage.PresentFileExternally(filename); + return true; + } + }); } } - - using (var image = await host.TakeScreenshotAsync().ConfigureAwait(false)) + finally { - if (Interlocked.Decrement(ref screenShotTasks) == 0 && cursorVisibility.Value == false) + if (Interlocked.Decrement(ref screenShotTasks) == 0) cursorVisibility.Value = true; - - host.GetClipboard()?.SetImage(image); - - (string filename, var stream) = getWritableStream(); - - if (filename == null) return; - - using (stream) - { - switch (screenshotFormat.Value) - { - case ScreenshotFormat.Png: - await image.SaveAsPngAsync(stream).ConfigureAwait(false); - break; - - case ScreenshotFormat.Jpg: - const int jpeg_quality = 92; - - await image.SaveAsJpegAsync(stream, new JpegEncoder { Quality = jpeg_quality }).ConfigureAwait(false); - break; - - default: - throw new InvalidOperationException($"Unknown enum member {nameof(ScreenshotFormat)} {screenshotFormat.Value}."); - } - } - - notificationOverlay.Post(new SimpleNotification - { - Text = $"Screenshot {filename} saved!", - Activated = () => - { - storage.PresentFileExternally(filename); - return true; - } - }); } }); From 289f0e9862d690cf40e7fcd081cd962a1791fb39 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 1 Jun 2023 15:50:37 +0900 Subject: [PATCH 023/169] Use `FireAndForget` to avoid unobserved exception --- osu.Game/Graphics/ScreenshotManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index dd90cbc074..82f89d6889 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -19,6 +19,7 @@ using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Configuration; using osu.Game.Input.Bindings; +using osu.Game.Online.Multiplayer; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; using SixLabors.ImageSharp; @@ -69,7 +70,7 @@ namespace osu.Game.Graphics { case GlobalAction.TakeScreenshot: shutter.Play(); - TakeScreenshotAsync(); + TakeScreenshotAsync().FireAndForget(); return true; } From 98f35f74811329500e558943464d003b3543f276 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 1 Jun 2023 16:06:34 +0900 Subject: [PATCH 024/169] Fix osu!mania hold notes snapping to judgement area too early on early hits Closes https://github.com/ppy/osu/issues/23515. --- .../Objects/Drawables/DrawableHoldNote.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index ce34addeff..3f91328128 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -245,7 +245,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables // As the note is being held, adjust the size of the sizing container. This has two effects: // 1. The contained masking container will mask the body and ticks. // 2. The head note will move along with the new "head position" in the container. - if (Head.IsHit && releaseTime == null && DrawHeight > 0) + // + // As per stable, this should not apply for early hits, waiting until the object starts to touch the + // judgement area first. + if (Head.IsHit && releaseTime == null && DrawHeight > 0 && Time.Current >= HitObject.StartTime) { // How far past the hit target this hold note is. float yOffset = Direction.Value == ScrollingDirection.Up ? -Y : Y; From f52ed41f107fe79db605fc35acab62faa9f9d9c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 1 Jun 2023 16:28:03 +0900 Subject: [PATCH 025/169] Use better defaults of 1/4 and 1/6 when cycling types --- .../Screens/Edit/Compose/Components/BeatDivisorControl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 432c5ea280..e510d1aac8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -196,11 +196,11 @@ namespace osu.Game.Screens.Edit.Compose.Components switch ((BeatDivisorType)nextDivisorType) { case BeatDivisorType.Common: - beatDivisor.ValidDivisors.Value = BeatDivisorPresetCollection.COMMON; + beatDivisor.SetArbitraryDivisor(4); break; case BeatDivisorType.Triplets: - beatDivisor.ValidDivisors.Value = BeatDivisorPresetCollection.TRIPLETS; + beatDivisor.SetArbitraryDivisor(6); break; case BeatDivisorType.Custom: From bcde2cbc73bf31ed0d32cfe7a49e87b6afa174fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 1 Jun 2023 16:28:47 +0900 Subject: [PATCH 026/169] Apply NRT to `BeatDivisorControl` --- .../Visual/Editing/TestSceneBeatDivisorControl.cs | 8 +++----- .../Screens/Edit/Compose/Components/BeatDivisorControl.cs | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs index 353acfa4ba..3ee5ea79db 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.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 System.Diagnostics; using System.Linq; @@ -23,8 +21,8 @@ namespace osu.Game.Tests.Visual.Editing { public partial class TestSceneBeatDivisorControl : OsuManualInputManagerTestScene { - private BeatDivisorControl beatDivisorControl; - private BindableBeatDivisor bindableBeatDivisor; + private BeatDivisorControl beatDivisorControl = null!; + private BindableBeatDivisor bindableBeatDivisor = null!; private SliderBar tickSliderBar => beatDivisorControl.ChildrenOfType>().Single(); private Triangle tickMarkerHead => tickSliderBar.ChildrenOfType().Single(); @@ -237,7 +235,7 @@ namespace osu.Game.Tests.Visual.Editing InputManager.Click(MouseButton.Left); }); - BeatDivisorControl.CustomDivisorPopover popover = null; + BeatDivisorControl.CustomDivisorPopover? popover = null; AddUntilStep("wait for popover", () => (popover = this.ChildrenOfType().SingleOrDefault()) != null && popover.IsLoaded); AddStep($"set divisor to {divisor}", () => { diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index e510d1aac8..213246d516 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.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 System.Diagnostics; using System.Linq; @@ -372,10 +370,10 @@ namespace osu.Game.Screens.Edit.Compose.Components private partial class TickSliderBar : SliderBar { - private Marker marker; + private Marker marker = null!; [Resolved] - private OsuColour colours { get; set; } + private OsuColour colours { get; set; } = null!; private readonly BindableBeatDivisor beatDivisor; @@ -517,7 +515,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private partial class Marker : CompositeDrawable { [Resolved] - private OverlayColourProvider colourProvider { get; set; } + private OverlayColourProvider colourProvider { get; set; } = null!; [BackgroundDependencyLoader] private void load() From 32207d411222f1328466a6e9c40a261ebb042c16 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 1 Jun 2023 16:40:33 +0900 Subject: [PATCH 027/169] Remember the last used custom divisor when cycling divisor types --- .../Compose/Components/BeatDivisorControl.cs | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 213246d516..dce9e01192 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -29,6 +29,8 @@ namespace osu.Game.Screens.Edit.Compose.Components { public partial class BeatDivisorControl : CompositeDrawable { + private int? lastCustomDivisor; + private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); public BeatDivisorControl(BindableBeatDivisor beatDivisor) @@ -182,16 +184,30 @@ namespace osu.Game.Screens.Edit.Compose.Components }; } + protected override void LoadComplete() + { + base.LoadComplete(); + + beatDivisor.ValidDivisors.BindValueChanged(valid => + { + if (valid.NewValue.Type == BeatDivisorType.Custom) + lastCustomDivisor = valid.NewValue.Presets.Last(); + }, true); + } + private void cycleDivisorType(int direction) { - Debug.Assert(Math.Abs(direction) == 1); - int nextDivisorType = (int)beatDivisor.ValidDivisors.Value.Type + direction; - if (nextDivisorType > (int)BeatDivisorType.Triplets) - nextDivisorType = (int)BeatDivisorType.Common; - else if (nextDivisorType < (int)BeatDivisorType.Common) - nextDivisorType = (int)BeatDivisorType.Triplets; + int totalTypes = Enum.GetValues().Length; + BeatDivisorType currentType = beatDivisor.ValidDivisors.Value.Type; - switch ((BeatDivisorType)nextDivisorType) + Debug.Assert(Math.Abs(direction) == 1); + + cycleOnce(); + + if (lastCustomDivisor == null && currentType == BeatDivisorType.Custom) + cycleOnce(); + + switch (currentType) { case BeatDivisorType.Common: beatDivisor.SetArbitraryDivisor(4); @@ -202,9 +218,12 @@ namespace osu.Game.Screens.Edit.Compose.Components break; case BeatDivisorType.Custom: - beatDivisor.ValidDivisors.Value = BeatDivisorPresetCollection.Custom(beatDivisor.ValidDivisors.Value.Presets.Max()); + Debug.Assert(lastCustomDivisor != null); + beatDivisor.SetArbitraryDivisor(lastCustomDivisor.Value); break; } + + void cycleOnce() => currentType = (BeatDivisorType)(((int)currentType + totalTypes + direction) % totalTypes); } protected override bool OnKeyDown(KeyDownEvent e) @@ -302,12 +321,12 @@ namespace osu.Game.Screens.Edit.Compose.Components { base.LoadComplete(); BeatDivisor.BindValueChanged(_ => updateState(), true); - divisorTextBox.OnCommit += (_, _) => setPresets(); + divisorTextBox.OnCommit += (_, _) => setPresetsFromTextBoxEntry(); Schedule(() => GetContainingInputManager().ChangeFocus(divisorTextBox)); } - private void setPresets() + private void setPresetsFromTextBoxEntry() { if (!int.TryParse(divisorTextBox.Text, out int divisor) || divisor < 1 || divisor > 64) { From 949fe327402bb9418b2875aba0a0e7658f931b91 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 31 May 2023 01:23:42 +0300 Subject: [PATCH 028/169] Use combined area of children as a mask instead --- .../Timeline/TimelineHitObjectBlueprint.cs | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 900f0ff4a2..31cf6ad766 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -50,6 +50,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private readonly Container colouredComponents; private readonly OsuSpriteText comboIndexText; + private readonly SamplePointPiece samplePointPiece; + private readonly DifficultyPointPiece difficultyPointPiece = null!; [Resolved] private ISkinSource skin { get; set; } = null!; @@ -101,7 +103,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }, } }, - new SamplePointPiece(Item) + samplePointPiece = new SamplePointPiece(Item) { Anchor = Anchor.BottomLeft, Origin = Anchor.TopCentre @@ -118,7 +120,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (item is IHasSliderVelocity) { - AddInternal(new DifficultyPointPiece(Item) + AddInternal(difficultyPointPiece = new DifficultyPointPiece(Item) { Anchor = Anchor.TopLeft, Origin = Anchor.BottomCentre @@ -244,7 +246,23 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public override Vector2 ScreenSpaceSelectionPoint => ScreenSpaceDrawQuad.TopLeft; - protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false; + protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) + { + // Since children are exceeding the component size, we need to use a custom quad to compute whether it should be masked away. + + // When component isn't masked away there's no need to apply custom logic. + if (!base.ComputeIsMaskedAway(maskingBounds)) + return false; + + // If component is considered masked away we'll use children to create an extended quad. + var rect = RectangleF.Union(ScreenSpaceDrawQuad.AABBFloat, circle.ScreenSpaceDrawQuad.AABBFloat); + rect = RectangleF.Union(rect, samplePointPiece.ScreenSpaceDrawQuad.AABBFloat); + + if (difficultyPointPiece != null) + rect = RectangleF.Union(rect, difficultyPointPiece.ScreenSpaceDrawQuad.AABBFloat); + + return !Precision.AlmostIntersects(maskingBounds, rect); + } private partial class Tick : Circle { From 03eb7c78300cd689d504dd8874b700a67dec1fa3 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 1 Jun 2023 21:21:01 +0300 Subject: [PATCH 029/169] Fix nullability --- .../Compose/Components/Timeline/TimelineHitObjectBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 31cf6ad766..638e2d43c8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private readonly Container colouredComponents; private readonly OsuSpriteText comboIndexText; private readonly SamplePointPiece samplePointPiece; - private readonly DifficultyPointPiece difficultyPointPiece = null!; + private readonly DifficultyPointPiece? difficultyPointPiece; [Resolved] private ISkinSource skin { get; set; } = null!; From c2d89a32a985515edb376b9abd3d23555dda5445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 1 Jun 2023 21:18:00 +0200 Subject: [PATCH 030/169] Adjust inline comment --- .../Components/Timeline/TimelineHitObjectBlueprint.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 638e2d43c8..55f122669d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -250,11 +250,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { // Since children are exceeding the component size, we need to use a custom quad to compute whether it should be masked away. - // When component isn't masked away there's no need to apply custom logic. + // If the component isn't considered masked away by itself, there's no need to apply custom logic. if (!base.ComputeIsMaskedAway(maskingBounds)) return false; - // If component is considered masked away we'll use children to create an extended quad. + // If the component is considered masked away, we'll use children to create an extended quad that encapsulates all parts of this blueprint + // to ensure it doesn't pop in and out of existence abruptly when scrolling the timeline. var rect = RectangleF.Union(ScreenSpaceDrawQuad.AABBFloat, circle.ScreenSpaceDrawQuad.AABBFloat); rect = RectangleF.Union(rect, samplePointPiece.ScreenSpaceDrawQuad.AABBFloat); From c33ddedca0b6cdfaf4054dc80bbdcd2f5f20e842 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 2 Jun 2023 08:48:47 +0900 Subject: [PATCH 031/169] Disable all tablet handlers to guard against a grim future --- osu.Game/OsuGame.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 33ac8cc101..3768dad370 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1145,8 +1145,11 @@ namespace osu.Game IconColour = Colours.RedDark, }); - var tabletHandler = Host.AvailableInputHandlers.OfType().FirstOrDefault(); - if (tabletHandler != null) + // We only have one tablet handler currently. + // The loop here is weakly guarding against a future where more than one is added. + // If this is ever the case, this logic needs adjustment as it should probably only + // disable the relevant tablet handler rather than all. + foreach (var tabletHandler in Host.AvailableInputHandlers.OfType()) tabletHandler.Enabled.Value = false; }); } From a44b20832372c0f509f7fda6bfe4b35e829d649f Mon Sep 17 00:00:00 2001 From: John Biddle Date: Thu, 1 Jun 2023 21:25:49 -0700 Subject: [PATCH 032/169] Updated languages in Language.cs to match what is in osu-web/resources/lang/. Added Catalan, Persian, Filipino, Hebrew, Croatian, Lithuanian, Latvian, Malay, Slovenian, Serbian, Tajik, and Sinhala. --- osu.Game/Localisation/Language.cs | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/osu.Game/Localisation/Language.cs b/osu.Game/Localisation/Language.cs index 6a4e5110e6..3f0f24f45f 100644 --- a/osu.Game/Localisation/Language.cs +++ b/osu.Game/Localisation/Language.cs @@ -22,6 +22,9 @@ namespace osu.Game.Localisation [Description(@"Български")] bg, + [Description(@"Català")] + ca, + [Description(@"Česky")] cs, @@ -37,12 +40,24 @@ namespace osu.Game.Localisation [Description(@"español")] es, + [Description(@"فارسی")] + fa_ir, + [Description(@"Suomi")] fi, + [Description(@"Filipino")] + fil, + [Description(@"français")] fr, + [Description(@"עברית")] + he, + + [Description(@"Hrvatski")] + hr_hr, + [Description(@"Magyar")] hu, @@ -58,6 +73,15 @@ namespace osu.Game.Localisation [Description(@"한국어")] ko, + [Description(@"Lietuvių")] + lt, + + [Description(@"Latviešu")] + lv_lv, + + [Description(@"Melayu")] + ms_my, + [Description(@"Nederlands")] nl, @@ -79,12 +103,24 @@ namespace osu.Game.Localisation [Description(@"Русский")] ru, + [Description(@"සිංහල")] + si_lk, + [Description(@"Slovenčina")] sk, + [Description(@"Slovenščina")] + sl, + + [Description(@"Српски")] + sr, + [Description(@"Svenska")] sv, + [Description(@"Тоҷикӣ")] + tg_tj, + [Description(@"ไทย")] th, @@ -105,6 +141,9 @@ namespace osu.Game.Localisation [Description(@"简体中文")] zh, + [Description(@"繁體中文")] + zh_tw, + // Traditional Chinese (Hong Kong) is listed in web sources but has no associated localisations, // and was wrongly falling back to Simplified Chinese. // Can be revisited if localisations ever arrive. From bfe80fe143249cefe1798db4675c024075d3db12 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 2 Jun 2023 17:37:43 +0900 Subject: [PATCH 033/169] Fix legacy diffcalc creating all mods unnecessarily --- osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 8dd1b51cae..00c90bd317 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -137,7 +137,7 @@ namespace osu.Game.Rulesets.Difficulty foreach (var combination in CreateDifficultyAdjustmentModCombinations()) { - Mod classicMod = rulesetInstance.CreateAllMods().SingleOrDefault(m => m is ModClassic); + Mod classicMod = rulesetInstance.CreateMod(); var finalCombination = ModUtils.FlattenMod(combination); if (classicMod != null) From a5fd833214566781159fa51ccc57df785c10817b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 2 Jun 2023 23:01:03 +0900 Subject: [PATCH 034/169] Fix "bubbles" mod not adding pool to hierarchy (and constructing too early) --- osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs index 12e2090f89..b74b722bad 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs @@ -42,14 +42,14 @@ namespace osu.Game.Rulesets.Osu.Mods private PlayfieldAdjustmentContainer bubbleContainer = null!; + private DrawablePool bubblePool = null!; + private readonly Bindable currentCombo = new BindableInt(); private float maxSize; private float bubbleSize; private double bubbleFade; - private readonly DrawablePool bubblePool = new DrawablePool(100); - public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) @@ -72,6 +72,7 @@ namespace osu.Game.Rulesets.Osu.Mods bubbleContainer = drawableRuleset.CreatePlayfieldAdjustmentContainer(); drawableRuleset.Overlays.Add(bubbleContainer); + drawableRuleset.Overlays.Add(bubblePool = new DrawablePool(100)); } public void ApplyToDrawableHitObject(DrawableHitObject drawableObject) From 474f7c2bc075202e756aa8fe1141f9fe7faab1f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 3 Jun 2023 16:50:58 +0200 Subject: [PATCH 035/169] Fix code quality inspection --- osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 0d9f91caa1..c27e30d5bb 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -233,7 +233,7 @@ namespace osu.Game.Tests.Visual.Multiplayer QueueMode = ServerAPIRoom.QueueMode.Value, AutoStartDuration = ServerAPIRoom.AutoStartDuration.Value }, - Playlist = ServerAPIRoom.Playlist.Select(item => TestMultiplayerClient.CreateMultiplayerPlaylistItem(item)).ToList(), + Playlist = ServerAPIRoom.Playlist.Select(CreateMultiplayerPlaylistItem).ToList(), Users = { localUser }, Host = localUser }; From 602d5db3bb66e84b9ade2d99b004fbdd5e130be6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 3 Jun 2023 19:40:01 +0200 Subject: [PATCH 036/169] Simplify column dimensions code `dimensions` would always receive exactly one item, so might as well inline it. And yes, at this point the grid container is mostly a glorified `FillFlowContainer { Direction = FlowDirection.Vertical }`, but I am not touching that in this pull pending further decisions with respect to direction. --- osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs b/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs index 31dd5df27a..c36d7726dc 100644 --- a/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs +++ b/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs @@ -147,7 +147,6 @@ namespace osu.Game.Screens.Ranking.Statistics foreach (var item in statisticItems) { var columnContent = new List(); - var dimensions = new List(); if (!hitEventsAvailable && item.RequiresHitEvents) { @@ -161,8 +160,6 @@ namespace osu.Game.Screens.Ranking.Statistics Origin = Anchor.Centre, }); - dimensions.Add(new Dimension()); - rows.Add(new GridContainer { Anchor = Anchor.TopCentre, @@ -170,7 +167,7 @@ namespace osu.Game.Screens.Ranking.Statistics RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Content = new[] { columnContent.ToArray() }, - ColumnDimensions = dimensions.ToArray(), + ColumnDimensions = new[] { new Dimension() }, RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) } }); } From a8b102ef72ef42706d870030b1935c5e3de15a8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 3 Jun 2023 21:32:12 +0200 Subject: [PATCH 037/169] Remove duplicated `zh_tw` language `zh_hant` already covers it. --- osu.Game/Localisation/Language.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Localisation/Language.cs b/osu.Game/Localisation/Language.cs index 3f0f24f45f..14dedf9a2e 100644 --- a/osu.Game/Localisation/Language.cs +++ b/osu.Game/Localisation/Language.cs @@ -141,9 +141,6 @@ namespace osu.Game.Localisation [Description(@"简体中文")] zh, - [Description(@"繁體中文")] - zh_tw, - // Traditional Chinese (Hong Kong) is listed in web sources but has no associated localisations, // and was wrongly falling back to Simplified Chinese. // Can be revisited if localisations ever arrive. From a8a4c02bb3ca788580eb770695c76fe13936ef4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 3 Jun 2023 21:48:03 +0200 Subject: [PATCH 038/169] Comment out languages with no glyph support --- osu.Game/Localisation/Language.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/osu.Game/Localisation/Language.cs b/osu.Game/Localisation/Language.cs index 14dedf9a2e..7104583f95 100644 --- a/osu.Game/Localisation/Language.cs +++ b/osu.Game/Localisation/Language.cs @@ -40,8 +40,9 @@ namespace osu.Game.Localisation [Description(@"español")] es, - [Description(@"فارسی")] - fa_ir, + // TODO: Requires Arabic glyphs to be added to resources (and possibly also RTL support). + // [Description(@"فارسی")] + // fa_ir, [Description(@"Suomi")] fi, @@ -52,8 +53,9 @@ namespace osu.Game.Localisation [Description(@"français")] fr, - [Description(@"עברית")] - he, + // TODO: Requires Hebrew glyphs to be added to resources (and possibly also RTL support). + // [Description(@"עברית")] + // he, [Description(@"Hrvatski")] hr_hr, @@ -103,8 +105,10 @@ namespace osu.Game.Localisation [Description(@"Русский")] ru, - [Description(@"සිංහල")] - si_lk, + // TODO: Requires Sinhala glyphs to be added to resources. + // Additionally, no translations available yet. + // [Description(@"සිංහල")] + // si_lk, [Description(@"Slovenčina")] sk, From 14309ba951f01fa49a6cfb7e0786da5499279b57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 3 Jun 2023 23:42:08 +0200 Subject: [PATCH 039/169] Comment out Tajik due to lack of support on Windows ^<10 --- osu.Game/Localisation/Language.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Localisation/Language.cs b/osu.Game/Localisation/Language.cs index 7104583f95..962734e67a 100644 --- a/osu.Game/Localisation/Language.cs +++ b/osu.Game/Localisation/Language.cs @@ -122,8 +122,10 @@ namespace osu.Game.Localisation [Description(@"Svenska")] sv, - [Description(@"Тоҷикӣ")] - tg_tj, + // Tajik has no associated localisations yet, and is not supported on Windows versions <10. + // TODO: update language mapping in osu-resources to redirect tg-TJ to tg-Cyrl-TJ (which is supported on earlier Windows versions) + // [Description(@"Тоҷикӣ")] + // tg_tj, [Description(@"ไทย")] th, From 4f5dfecbb850a41b532ce25b0bf24fd1772c2075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 3 Jun 2023 23:55:05 +0200 Subject: [PATCH 040/169] Comment out Filipino due to satellite assembly copy failure --- osu.Game/Localisation/Language.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Localisation/Language.cs b/osu.Game/Localisation/Language.cs index 962734e67a..711e95486f 100644 --- a/osu.Game/Localisation/Language.cs +++ b/osu.Game/Localisation/Language.cs @@ -47,8 +47,9 @@ namespace osu.Game.Localisation [Description(@"Suomi")] fi, - [Description(@"Filipino")] - fil, + // TODO: Doesn't work as appropriate satellite assemblies aren't copied from resources (see: https://github.com/ppy/osu/discussions/18851#discussioncomment-3042170) + // [Description(@"Filipino")] + // fil, [Description(@"français")] fr, From 3e308e4c278a01e94090a4c578fe5cf4970a1f32 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 4 Jun 2023 12:50:30 +0900 Subject: [PATCH 041/169] Add test coverage showing commit failure in manage collections dialog --- .../TestSceneManageCollectionsDialog.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs index 1e9982f8d4..cfa45ec6ef 100644 --- a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs +++ b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs @@ -264,8 +264,9 @@ namespace osu.Game.Tests.Visual.Collections assertCollectionName(1, "First"); } - [Test] - public void TestCollectionRenamedOnTextChange() + [TestCase(false)] + [TestCase(true)] + public void TestCollectionRenamedOnTextChange(bool commitWithEnter) { BeatmapCollection first = null!; DrawableCollectionListItem firstItem = null!; @@ -293,9 +294,19 @@ namespace osu.Game.Tests.Visual.Collections AddStep("change first collection name", () => { firstItem.ChildrenOfType().First().Text = "First"; - InputManager.Key(Key.Enter); }); + if (commitWithEnter) + AddStep("commit via enter", () => InputManager.Key(Key.Enter)); + else + { + AddStep("commit via click away", () => + { + InputManager.MoveMouseTo(firstItem.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)); + InputManager.Click(MouseButton.Left); + }); + } + AddUntilStep("collection has new name", () => first.Name == "First"); } From eb7586b517dd0f4794f94edd59fcaff7bc8597cd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 4 Jun 2023 12:51:03 +0900 Subject: [PATCH 042/169] Ensure collection edit textbox commits on focus loss As discussed in https://github.com/ppy/osu/discussions/23739 --- osu.Game/Collections/DrawableCollectionListItem.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Collections/DrawableCollectionListItem.cs b/osu.Game/Collections/DrawableCollectionListItem.cs index 23156b1ad5..0ab0ff520d 100644 --- a/osu.Game/Collections/DrawableCollectionListItem.cs +++ b/osu.Game/Collections/DrawableCollectionListItem.cs @@ -86,6 +86,7 @@ namespace osu.Game.Collections RelativeSizeAxes = Axes.Both, Size = Vector2.One, CornerRadius = item_height / 2, + CommitOnFocusLost = true, PlaceholderText = collection.IsManaged ? string.Empty : "Create a new collection" }, } From 4eb0f0261ce153539329360534eda00a551561ea Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 4 Jun 2023 22:52:05 -0700 Subject: [PATCH 043/169] Fix song select beatmap panels not displaying correct background shown in web --- osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 3975bb6bb6..d544db66b1 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -108,7 +108,7 @@ namespace osu.Game.Screens.Select.Carousel Header.Children = new Drawable[] { - background = new DelayedLoadWrapper(() => new SetPanelBackground(manager.GetWorkingBeatmap(beatmapSet.Beatmaps.FirstOrDefault())) + background = new DelayedLoadWrapper(() => new SetPanelBackground(manager.GetWorkingBeatmap(beatmapSet.Beatmaps.MinBy(b => b.OnlineID))) { RelativeSizeAxes = Axes.Both, }, 300) From 5c1abdc7044d57ca24f6f3f8c09ba9ad8874015c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 5 Jun 2023 21:33:08 +0200 Subject: [PATCH 044/169] Fix screen navigation test hijacking dummy request handler In an upcoming change, I stumbled upon a test failure mode wherein tests in `TestSceneScreenNavigation` would die on the following exception: 2023-05-07 17:58:42 [error]: System.ObjectDisposedException: Cannot access a closed Realm. 2023-05-07 17:58:42 [error]: Object name: 'Realms.Realm'. 2023-05-07 17:58:42 [error]: at Realms.Realm.ThrowIfDisposed() 2023-05-07 17:58:42 [error]: at Realms.Realm.All[T]() 2023-05-07 17:58:42 [error]: at osu.Game.Beatmaps.BeatmapManager.<>c__DisplayClass25_0.b__0(Realm r) in D:\a\osu\osu\osu.Game\Beatmaps\BeatmapManager.cs:line 282 2023-05-07 17:58:42 [error]: at osu.Game.Database.RealmAccess.Run[T](Func`2 action) in D:\a\osu\osu\osu.Game\Database\RealmAccess.cs:line 387 2023-05-07 17:58:42 [error]: at osu.Game.Beatmaps.BeatmapManager.QueryBeatmap(Expression`1 query) in D:\a\osu\osu\osu.Game\Beatmaps\BeatmapManager.cs:line 282 2023-05-07 17:58:42 [error]: at osu.Game.Tests.Visual.OnlinePlay.TestRoomRequestsHandler.g__createResponseBeatmaps|6_0(Int32[] beatmapIds, <>c__DisplayClass6_0& ) in D:\a\osu\osu\osu.Game\Tests\Visual\OnlinePlay\TestRoomRequestsHandler.cs:line 174 2023-05-07 17:58:42 [error]: at osu.Game.Tests.Visual.OnlinePlay.TestRoomRequestsHandler.HandleRequest(APIRequest request, APIUser localUser, BeatmapManager beatmapManager) in D:\a\osu\osu\osu.Game\Tests\Visual\OnlinePlay\TestRoomRequestsHandler.cs:line 140 2023-05-07 17:58:42 [error]: at osu.Game.Tests.Visual.TestMultiplayerComponents.<>c__DisplayClass18_0.b__0(APIRequest request) in D:\a\osu\osu\osu.Game.Tests\Visual\TestMultiplayerComponents.cs:line 80 2023-05-07 17:58:42 [error]: at osu.Game.Online.API.DummyAPIAccess.<>c__DisplayClass32_0.b__0() in D:\a\osu\osu\osu.Game\Online\API\DummyAPIAccess.cs:line 74 Upon closer inspection, one of the tests in the scene instantiates a `TestMultiplayerComponents` instance. `TestMultiplayerComponents` registers a custom request handler onto `DummyAPIAccess`. Normally, this is not an issue; however, because `TestSceneScreenNavigation` is an `OsuGameTestScene`, and therefore has its storage recycled after every test, this leads to the error above in the following scenario: 1. `TestPushMatchSubScreenAndPressBackButtonImmediately()` passes. 2. The test is cleaned up, and the test case's storage is recycled, including the test case's realm database. 3. In a subsequent test, a web request handled by the dummy API request handler is fired. The dummy API request handler subsequently attempts to access a realm that does not exist anymore. As the usage of `TestMultiplayerComponents` is highly unorthodox in this particular case, I'm opting for a localised fix which ensures that the request handler is cleaned up appropriately. --- .../Visual/Navigation/TestSceneScreenNavigation.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 193cec8907..18aef99ccd 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -17,6 +17,7 @@ using osu.Game.Beatmaps; using osu.Game.Collections; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; using osu.Game.Online.Leaderboards; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapListing; @@ -539,6 +540,11 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("open room", () => multiplayerComponents.ChildrenOfType().Single().Open()); AddStep("press back button", () => Game.ChildrenOfType().First().Action()); AddWaitStep("wait two frames", 2); + + AddStep("exit lounge", () => Game.ScreenStack.Exit()); + // `TestMultiplayerComponents` registers a request handler in its BDL, but never unregisters it. + // to prevent the handler living for longer than it should be, clean up manually. + AddStep("clean up multiplayer request handler", () => ((DummyAPIAccess)API).HandleRequest = null); } [Test] From c54670aee1d715c7662517daab78ac591e8dd773 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jun 2023 13:30:56 +0900 Subject: [PATCH 045/169] Add comment explaining implementation --- osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index d544db66b1..b97d37c854 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -108,6 +108,7 @@ namespace osu.Game.Screens.Select.Carousel Header.Children = new Drawable[] { + // Choice of background image matches BSS implementation (always uses the lowest `beatmap_id` from the set). background = new DelayedLoadWrapper(() => new SetPanelBackground(manager.GetWorkingBeatmap(beatmapSet.Beatmaps.MinBy(b => b.OnlineID))) { RelativeSizeAxes = Axes.Both, From 2f11bd5473af40d1b53b61de79912b587dd9451f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jun 2023 14:42:42 +0900 Subject: [PATCH 046/169] Adjust animations slightly --- osu.Game/Screens/Play/ArgonKeyCounter.cs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/ArgonKeyCounter.cs b/osu.Game/Screens/Play/ArgonKeyCounter.cs index 53305901ee..2d725898d8 100644 --- a/osu.Game/Screens/Play/ArgonKeyCounter.cs +++ b/osu.Game/Screens/Play/ArgonKeyCounter.cs @@ -25,13 +25,16 @@ namespace osu.Game.Screens.Play // Make things look bigger without using Scale private const float scale_factor = 1.5f; + [Resolved] + private OsuColour colours { get; set; } = null!; + public ArgonKeyCounter(InputTrigger trigger) : base(trigger) { } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { Children = new Drawable[] { @@ -76,14 +79,27 @@ namespace osu.Game.Screens.Play protected override void Activate(bool forwardPlayback = true) { base.Activate(forwardPlayback); - keyNameText.FlashColour(Colour4.White, 200); - inputIndicator.FadeIn().MoveToY(0).Then().MoveToY(3, 100, Easing.OutQuart); + + keyNameText + .FadeColour(Colour4.White, 10, Easing.OutQuint); + + inputIndicator + .FadeIn(10, Easing.OutQuint) + .MoveToY(0) + .Then() + .MoveToY(4, 60, Easing.OutQuint); } protected override void Deactivate(bool forwardPlayback = true) { base.Deactivate(forwardPlayback); - inputIndicator.MoveToY(0, 200, Easing.OutQuart).FadeTo(0.5f, 200, Easing.OutQuart); + + keyNameText + .FadeColour(colours.Blue0, 200, Easing.OutQuart); + + inputIndicator + .MoveToY(0, 250, Easing.OutQuart) + .FadeTo(0.5f, 250, Easing.OutQuart); } } } From 22f7fe1d79c325fe0c38418ca31a0dced1a5e1d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jun 2023 15:11:31 +0900 Subject: [PATCH 047/169] Rename variable and ensure timestamp is updated even when not saving --- osu.Game/Beatmaps/BeatmapInfo.cs | 5 ++++- osu.Game/Screens/Edit/Editor.cs | 17 +++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 7ca0e24913..5019d64276 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -171,7 +171,10 @@ namespace osu.Game.Beatmaps public double TimelineZoom { get; set; } = 1.0; - public double? LastEditTime { get; set; } + /// + /// The time in milliseconds when last exiting the editor with this beatmap loaded. + /// + public double? EditorTimestamp { get; set; } [Ignored] public CountdownType Countdown { get; set; } = CountdownType.Normal; diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index c3d72c7aec..bb052b1d22 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -28,6 +28,7 @@ using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; +using osu.Game.Database; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; @@ -92,6 +93,9 @@ namespace osu.Game.Screens.Edit [Resolved(canBeNull: true)] private INotificationOverlay notifications { get; set; } + [Resolved] + private RealmAccess realm { get; set; } + public readonly Bindable Mode = new Bindable(); public IBindable SamplePlaybackDisabled => samplePlaybackDisabled; @@ -441,8 +445,6 @@ namespace osu.Game.Screens.Edit try { - editorBeatmap.BeatmapInfo.LastEditTime = clock.CurrentTime; - // save the loaded beatmap's data stream. beatmapManager.Save(editorBeatmap.BeatmapInfo, editorBeatmap.PlayableBeatmap, editorBeatmap.BeatmapSkin); } @@ -702,6 +704,13 @@ namespace osu.Game.Screens.Edit } } + realm.Write(r => + { + var beatmap = r.Find(editorBeatmap.BeatmapInfo.ID); + if (beatmap != null) + beatmap.EditorTimestamp = clock.CurrentTime; + }); + ApplyToBackground(b => { b.DimWhenUserSettingsIgnored.Value = 0; @@ -835,9 +844,9 @@ namespace osu.Game.Screens.Edit { double targetTime = 0; - if (editorBeatmap.BeatmapInfo.LastEditTime != null) + if (editorBeatmap.BeatmapInfo.EditorTimestamp != null) { - targetTime = editorBeatmap.BeatmapInfo.LastEditTime.Value; + targetTime = editorBeatmap.BeatmapInfo.EditorTimestamp.Value; } else if (Beatmap.Value.Beatmap.HitObjects.Count > 0) { From 878cdb2ed34655f54dad8d59395202e850e98d30 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jun 2023 15:11:37 +0900 Subject: [PATCH 048/169] Bump schema version --- osu.Game/Database/RealmAccess.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 94108531e8..9e5f41502b 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -71,8 +71,9 @@ namespace osu.Game.Database /// 24 2022-08-22 Added MaximumStatistics to ScoreInfo. /// 25 2022-09-18 Remove skins to add with new naming. /// 26 2023-02-05 Added BeatmapHash to ScoreInfo. + /// 27 2023-06-06 Added EditorTimestamp to ScoreInfo. /// - private const int schema_version = 26; + private const int schema_version = 27; /// /// Lock object which is held during sections, blocking realm retrieval during blocking periods. From 66b8b5192b2d2661bca537bf1e6311b3578bf3f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jun 2023 15:23:59 +0900 Subject: [PATCH 049/169] Add test coverage of editor timestamp remembering --- .../TestSceneBeatmapEditorNavigation.cs | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs index 1b2bb57b84..5483be5676 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs @@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("open editor", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0))); AddUntilStep("wait for editor open", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.ReadyForUse); - AddStep("test gameplay", () => ((Editor)Game.ScreenStack.CurrentScreen).TestGameplay()); + AddStep("test gameplay", () => getEditor().TestGameplay()); AddUntilStep("wait for player", () => { @@ -141,6 +141,37 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for editor exit", () => Game.ScreenStack.CurrentScreen is not Editor); } - private EditorBeatmap getEditorBeatmap() => ((Editor)Game.ScreenStack.CurrentScreen).ChildrenOfType().Single(); + [Test] + public void TestLastTimestampRememberedOnExit() + { + BeatmapSetInfo beatmapSet = null!; + + AddStep("import test beatmap", () => Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely()); + AddStep("retrieve beatmap", () => beatmapSet = Game.BeatmapManager.QueryBeatmapSet(set => !set.Protected).AsNonNull().Value.Detach()); + + AddStep("present beatmap", () => Game.PresentBeatmap(beatmapSet)); + AddUntilStep("wait for song select", + () => Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet) + && Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect + && songSelect.IsLoaded); + + AddStep("open editor", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0))); + AddUntilStep("wait for editor open", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.ReadyForUse); + + AddStep("seek to arbitrary time", () => getEditor().ChildrenOfType().First().Seek(1234)); + AddUntilStep("time is correct", () => getEditor().ChildrenOfType().First().CurrentTime, () => Is.EqualTo(1234)); + + AddStep("exit editor", () => InputManager.Key(Key.Escape)); + AddUntilStep("wait for editor exit", () => Game.ScreenStack.CurrentScreen is not Editor); + + AddStep("open editor", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).Edit()); + + AddUntilStep("wait for editor open", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.ReadyForUse); + AddUntilStep("time is correct", () => getEditor().ChildrenOfType().First().CurrentTime, () => Is.EqualTo(1234)); + } + + private EditorBeatmap getEditorBeatmap() => getEditor().ChildrenOfType().Single(); + + private Editor getEditor() => (Editor)Game.ScreenStack.CurrentScreen; } } From a18a2e48f75e4aca729abbd34e6a7285ea8804cc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jun 2023 15:47:30 +0900 Subject: [PATCH 050/169] Colour argon osu!taiko explosions based on the object hit --- .../Skinning/Argon/ArgonHitExplosion.cs | 26 ++++++++--------- .../Skinning/Argon/ArgonInputDrum.cs | 28 ++++++++++++------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonHitExplosion.cs index a47fd7e62e..cddae7f05b 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonHitExplosion.cs @@ -1,9 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; @@ -18,7 +16,9 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon public partial class ArgonHitExplosion : CompositeDrawable, IAnimatableHitExplosion { private readonly TaikoSkinComponents component; + private readonly Circle outer; + private readonly Circle inner; public ArgonHitExplosion(TaikoSkinComponents component) { @@ -34,13 +34,9 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical( - new Color4(255, 227, 236, 255), - new Color4(255, 198, 211, 255) - ), Masking = true, }, - new Circle + inner = new Circle { Name = "Inner circle", Anchor = Anchor.Centre, @@ -48,12 +44,6 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon RelativeSizeAxes = Axes.Both, Colour = Color4.White, Size = new Vector2(0.85f), - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = new Color4(255, 132, 191, 255).Opacity(0.5f), - Radius = 45, - }, Masking = true, }, }; @@ -63,6 +53,16 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon { this.FadeOut(); + bool isRim = (drawableHitObject.HitObject as Hit)?.Type == HitType.Rim; + + outer.Colour = isRim ? ArgonInputDrum.RIM_HIT_GRADIENT : ArgonInputDrum.CENTRE_HIT_GRADIENT; + inner.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = (isRim ? ArgonInputDrum.RIM_HIT_GLOW : ArgonInputDrum.CENTRE_HIT_GLOW).Opacity(0.5f), + Radius = 45, + }; + switch (component) { case TaikoSkinComponents.TaikoExplosionGreat: diff --git a/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonInputDrum.cs b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonInputDrum.cs index e7b0a5537a..f7b7105bdc 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonInputDrum.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonInputDrum.cs @@ -19,6 +19,20 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon { public partial class ArgonInputDrum : AspectContainer { + public static readonly ColourInfo RIM_HIT_GRADIENT = ColourInfo.GradientHorizontal( + new Color4(227, 248, 255, 255), + new Color4(198, 245, 255, 255) + ); + + public static readonly Colour4 RIM_HIT_GLOW = new Color4(126, 215, 253, 255); + + public static readonly ColourInfo CENTRE_HIT_GRADIENT = ColourInfo.GradientHorizontal( + new Color4(255, 227, 236, 255), + new Color4(255, 198, 211, 255) + ); + + public static readonly Colour4 CENTRE_HIT_GLOW = new Color4(255, 147, 199, 255); + private const float rim_size = 0.3f; public ArgonInputDrum() @@ -141,14 +155,11 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon Anchor = anchor, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal( - new Color4(227, 248, 255, 255), - new Color4(198, 245, 255, 255) - ), + Colour = RIM_HIT_GRADIENT, EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, - Colour = new Color4(126, 215, 253, 170), + Colour = RIM_HIT_GLOW.Opacity(0.66f), Radius = 50, }, Alpha = 0, @@ -166,14 +177,11 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon Anchor = anchor, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal( - new Color4(255, 227, 236, 255), - new Color4(255, 198, 211, 255) - ), + Colour = CENTRE_HIT_GRADIENT, EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, - Colour = new Color4(255, 147, 199, 255), + Colour = CENTRE_HIT_GLOW, Radius = 50, }, Size = new Vector2(1 - rim_size), From b8d9c9ff93c3b27c6bbe832eb032401144d7c807 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jun 2023 16:07:00 +0900 Subject: [PATCH 051/169] Move `ApplyGameWideClock` to extension method I don't see an issue with applying this workaround to more places, even though it is a workaround, because it marks each usage very clearly. If we design a better solution in the future it should be easy to replace the usages. --- osu.Game.Rulesets.Mania/UI/Column.cs | 17 +++-------------- osu.Game/Extensions/DrawableExtensions.cs | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 6ca830a82f..7033fd6755 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Pooling; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Platform; +using osu.Game.Extensions; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; @@ -97,8 +98,8 @@ namespace osu.Game.Rulesets.Mania.UI new ColumnTouchInputArea(this) }; - applyGameWideClock(background); - applyGameWideClock(keyArea); + background.ApplyGameWideClock(host); + keyArea.ApplyGameWideClock(host); TopLevelContainer.Add(HitObjectArea.Explosions.CreateProxy()); @@ -107,18 +108,6 @@ namespace osu.Game.Rulesets.Mania.UI RegisterPool(10, 50); RegisterPool(10, 50); RegisterPool(50, 250); - - // Some elements don't handle rewind correctly and fixing them is non-trivial. - // In the future we need a better solution to this, but as a temporary work-around, give these components the game-wide - // clock so they don't need to worry about rewind. - // This only works because they handle OnPressed/OnReleased which results in a correct state while rewinding. - // - // This is kinda dodgy (and will cause weirdness when pausing gameplay) but is better than completely broken rewind. - void applyGameWideClock(Drawable drawable) - { - drawable.Clock = host.UpdateThread.Clock; - drawable.ProcessCustomClock = false; - } } private void onSourceChanged() diff --git a/osu.Game/Extensions/DrawableExtensions.cs b/osu.Game/Extensions/DrawableExtensions.cs index 915a2292a2..6553ad3886 100644 --- a/osu.Game/Extensions/DrawableExtensions.cs +++ b/osu.Game/Extensions/DrawableExtensions.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Platform; using osuTK; namespace osu.Game.Extensions @@ -43,5 +44,20 @@ namespace osu.Game.Extensions /// The delta vector in Parent's coordinates. public static Vector2 ScreenSpaceDeltaToParentSpace(this Drawable drawable, Vector2 delta) => drawable.Parent.ToLocalSpace(drawable.Parent.ToScreenSpace(Vector2.Zero) + delta); + + /// + /// Some elements don't handle rewind correctly and fixing them is non-trivial. + /// In the future we need a better solution to this, but as a temporary work-around, give these components the game-wide + /// clock so they don't need to worry about rewind. + /// + /// This only works if input handling components handle OnPressed/OnReleased which results in a correct state while rewinding. + /// + /// This is kinda dodgy (and will cause weirdness when pausing gameplay) but is better than completely broken rewind. + /// + public static void ApplyGameWideClock(this Drawable drawable, GameHost host) + { + drawable.Clock = host.UpdateThread.Clock; + drawable.ProcessCustomClock = false; + } } } From c0016fa5d2a837bd02cd29d83a44061f20633b75 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jun 2023 16:07:34 +0900 Subject: [PATCH 052/169] Fix gameplay playfield border being affected by beatmap track time Closes https://github.com/ppy/osu/issues/23470. --- osu.Game/Rulesets/UI/PlayfieldBorder.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Rulesets/UI/PlayfieldBorder.cs b/osu.Game/Rulesets/UI/PlayfieldBorder.cs index 211a87de84..18bd5b9b93 100644 --- a/osu.Game/Rulesets/UI/PlayfieldBorder.cs +++ b/osu.Game/Rulesets/UI/PlayfieldBorder.cs @@ -4,10 +4,13 @@ #nullable disable using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Platform; +using osu.Game.Extensions; using osuTK; using osuTK.Graphics; @@ -74,6 +77,12 @@ namespace osu.Game.Rulesets.UI }; } + [BackgroundDependencyLoader] + private void load(GameHost host) + { + this.ApplyGameWideClock(host); + } + protected override void LoadComplete() { base.LoadComplete(); From b096e03a57d57faf775bf2c846d34f0f9358be77 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jun 2023 16:31:57 +0900 Subject: [PATCH 053/169] Fix ticks being created after the end of drum rolls in osu!taiko editor Closes https://github.com/ppy/osu/issues/23135. --- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 2f4a98bd8f..987eb6625a 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -1,11 +1,10 @@ // 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 osu.Game.Rulesets.Objects.Types; using System.Threading; using osu.Framework.Bindables; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Formats; @@ -86,7 +85,10 @@ namespace osu.Game.Rulesets.Taiko.Objects bool first = true; - for (double t = StartTime; t < EndTime + tickSpacing / 2; t += tickSpacing) + // TODO: this implementation of drum roll tick does not match stable. + // Stable uses next-object intrinsics to decide whether the end of a drum roll gets a tick. + // It also changes the rate of ticks based on BPM. This is quite important. + for (double t = StartTime; Precision.AlmostBigger(EndTime, t, 1); t += tickSpacing) { cancellationToken.ThrowIfCancellationRequested(); From 7a1766e378ef251e9b30d303843e11c461509568 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jun 2023 16:42:20 +0900 Subject: [PATCH 054/169] Update argon osu!mania column colours in line with latest proposal Matches https://github.com/ppy/osu/discussions/21996#discussioncomment-5516872. --- .../Argon/ManiaArgonSkinTransformer.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index 007d02400a..3a15cb359f 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -139,11 +139,11 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon case 3: switch (columnIndex) { - case 0: return colour_pink; + case 0: return colour_green; - case 1: return colour_orange; + case 1: return colour_purple; - case 2: return colour_yellow; + case 2: return colour_cyan; default: throw new ArgumentOutOfRangeException(); } @@ -185,11 +185,11 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon case 1: return colour_orange; - case 2: return colour_yellow; + case 2: return colour_green; case 3: return colour_cyan; - case 4: return colour_purple; + case 4: return colour_orange; case 5: return colour_pink; @@ -201,17 +201,17 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { case 0: return colour_pink; - case 1: return colour_cyan; + case 1: return colour_orange; case 2: return colour_pink; case 3: return colour_special_column; - case 4: return colour_green; + case 4: return colour_pink; - case 5: return colour_cyan; + case 5: return colour_orange; - case 6: return colour_green; + case 6: return colour_pink; default: throw new ArgumentOutOfRangeException(); } @@ -225,9 +225,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon case 2: return colour_orange; - case 3: return colour_yellow; + case 3: return colour_green; - case 4: return colour_yellow; + case 4: return colour_cyan; case 5: return colour_orange; @@ -273,9 +273,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon case 3: return colour_yellow; - case 4: return colour_cyan; + case 4: return colour_green; - case 5: return colour_green; + case 5: return colour_cyan; case 6: return colour_yellow; From 3db080fad2d8bd27f335a1a062d78e4956fd3401 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jun 2023 16:54:33 +0900 Subject: [PATCH 055/169] Fix osu!taiko drum rolls not getting correct tick rate after placement in the editor --- osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs | 1 - osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index e298e313df..5226cb0794 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -133,7 +133,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps StartTime = obj.StartTime, Samples = obj.Samples, Duration = taikoDuration, - TickRate = beatmap.Difficulty.SliderTickRate == 3 ? 3 : 4, 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 987eb6625a..5b77ad7f91 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -4,7 +4,6 @@ using osu.Game.Rulesets.Objects.Types; using System.Threading; using osu.Framework.Bindables; -using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Formats; @@ -68,6 +67,8 @@ namespace osu.Game.Rulesets.Taiko.Objects double scoringDistance = base_distance * difficulty.SliderMultiplier * SliderVelocity; Velocity = scoringDistance / timingPoint.BeatLength; + TickRate = difficulty.SliderTickRate == 3 ? 3 : 4; + tickSpacing = timingPoint.BeatLength / TickRate; } @@ -85,10 +86,7 @@ namespace osu.Game.Rulesets.Taiko.Objects bool first = true; - // TODO: this implementation of drum roll tick does not match stable. - // Stable uses next-object intrinsics to decide whether the end of a drum roll gets a tick. - // It also changes the rate of ticks based on BPM. This is quite important. - for (double t = StartTime; Precision.AlmostBigger(EndTime, t, 1); t += tickSpacing) + for (double t = StartTime; t < EndTime + tickSpacing; t += tickSpacing) { cancellationToken.ThrowIfCancellationRequested(); From 56758afeed0ff662f107be0584b53261c81a8a52 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jun 2023 16:59:28 +0900 Subject: [PATCH 056/169] Add note about `lastTickHittable` requirement --- osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 5226cb0794..1c2e7abafe 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -92,6 +92,14 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps }).ToList(); } + // TODO: stable makes the last tick of a drumroll non-required when the next object is too close. + // This probably needs to be reimplemented: + // + // List hitobjects = hitObjectManager.hitObjects; + // int ind = hitobjects.IndexOf(this); + // if (i < hitobjects.Count - 1 && hitobjects[i + 1].HittableStartTime - (EndTime + (int)TickSpacing) <= (int)TickSpacing) + // lastTickHittable = false; + return converted; } From c1f23ef2119b613fc018a461f73a73c06f2c72ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jun 2023 17:52:29 +0900 Subject: [PATCH 057/169] Add beat snap grid for osu!catch editor As discussed in https://github.com/ppy/osu/discussions/23462. --- .../Edit/CatchBeatSnapGrid.cs | 180 ++++++++++++++++++ .../Edit/CatchHitObjectComposer.cs | 27 +++ osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 7 + 3 files changed, 214 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Edit/CatchBeatSnapGrid.cs diff --git a/osu.Game.Rulesets.Catch/Edit/CatchBeatSnapGrid.cs b/osu.Game.Rulesets.Catch/Edit/CatchBeatSnapGrid.cs new file mode 100644 index 0000000000..a2421c2b29 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/CatchBeatSnapGrid.cs @@ -0,0 +1,180 @@ +// 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 System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Caching; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Screens.Edit; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Edit +{ + /// + /// A grid which displays coloured beat divisor lines in proximity to the selection or placement cursor. + /// + /// + /// This class heavily borrows from osu!mania's implementation (ManiaBeatSnapGrid). + /// If further changes are to be made, they should also be applied there. + /// If the scale of the changes are large enough, abstracting may be a good path. + /// + public partial class CatchBeatSnapGrid : Component + { + private const double visible_range = 750; + + /// + /// The range of time values of the current selection. + /// + public (double start, double end)? SelectionTimeRange + { + set + { + if (value == selectionTimeRange) + return; + + selectionTimeRange = value; + lineCache.Invalidate(); + } + } + + [Resolved] + private EditorBeatmap beatmap { get; set; } = null!; + + [Resolved] + private OsuColour colours { get; set; } = null!; + + [Resolved] + private BindableBeatDivisor beatDivisor { get; set; } = null!; + + private readonly Cached lineCache = new Cached(); + + private (double start, double end)? selectionTimeRange; + + private ScrollingHitObjectContainer lineContainer = null!; + + [BackgroundDependencyLoader] + private void load(HitObjectComposer composer) + { + lineContainer = new ScrollingHitObjectContainer(); + + ((CatchPlayfield)composer.Playfield).UnderlayElements.Add(lineContainer); + + beatDivisor.BindValueChanged(_ => createLines(), true); + } + + protected override void Update() + { + base.Update(); + + if (!lineCache.IsValid) + { + lineCache.Validate(); + createLines(); + } + } + + private readonly Stack availableLines = new Stack(); + + private void createLines() + { + foreach (var line in lineContainer.Objects.OfType()) + availableLines.Push(line); + + lineContainer.Clear(); + + if (selectionTimeRange == null) + return; + + var range = selectionTimeRange.Value; + + var timingPoint = beatmap.ControlPointInfo.TimingPointAt(range.start - visible_range); + + double time = timingPoint.Time; + int beat = 0; + + // progress time until in the visible range. + while (time < range.start - visible_range) + { + time += timingPoint.BeatLength / beatDivisor.Value; + beat++; + } + + while (time < range.end + visible_range) + { + var nextTimingPoint = beatmap.ControlPointInfo.TimingPointAt(time); + + // switch to the next timing point if we have reached it. + if (nextTimingPoint.Time > timingPoint.Time) + { + beat = 0; + time = nextTimingPoint.Time; + timingPoint = nextTimingPoint; + } + + Color4 colour = BindableBeatDivisor.GetColourFor( + BindableBeatDivisor.GetDivisorForBeatIndex(Math.Max(1, beat), beatDivisor.Value), colours); + + if (!availableLines.TryPop(out var line)) + line = new DrawableGridLine(); + + line.HitObject.StartTime = time; + line.Colour = colour; + + lineContainer.Add(line); + + beat++; + time += timingPoint.BeatLength / beatDivisor.Value; + } + + // required to update ScrollingHitObjectContainer's cache. + lineContainer.UpdateSubTree(); + + foreach (var line in lineContainer.Objects.OfType()) + { + time = line.HitObject.StartTime; + + if (time >= range.start && time <= range.end) + line.Alpha = 1; + else + { + double timeSeparation = time < range.start ? range.start - time : time - range.end; + line.Alpha = (float)Math.Max(0, 1 - timeSeparation / visible_range); + } + } + } + + private partial class DrawableGridLine : DrawableHitObject + { + public DrawableGridLine() + : base(new HitObject()) + { + RelativeSizeAxes = Axes.X; + Height = 2; + + AddInternal(new Box { RelativeSizeAxes = Axes.Both }); + } + + [BackgroundDependencyLoader] + private void load() + { + Origin = Anchor.BottomLeft; + Anchor = Anchor.BottomLeft; + } + + protected override void UpdateInitialTransforms() + { + // don't perform any fading – we are handling that ourselves. + LifetimeEnd = HitObject.StartTime + visible_range; + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index 8afeca3e51..f2877572e8 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -33,6 +33,8 @@ namespace osu.Game.Rulesets.Catch.Edit private InputManager inputManager = null!; + private CatchBeatSnapGrid beatSnapGrid = null!; + private readonly BindableDouble timeRangeMultiplier = new BindableDouble(1) { MinValue = 1, @@ -65,6 +67,8 @@ namespace osu.Game.Rulesets.Catch.Edit Catcher.BASE_DASH_SPEED, -Catcher.BASE_DASH_SPEED, Catcher.BASE_WALK_SPEED, -Catcher.BASE_WALK_SPEED, })); + + AddInternal(beatSnapGrid = new CatchBeatSnapGrid()); } protected override void LoadComplete() @@ -74,6 +78,29 @@ namespace osu.Game.Rulesets.Catch.Edit inputManager = GetContainingInputManager(); } + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + if (BlueprintContainer.CurrentTool is SelectTool) + { + if (EditorBeatmap.SelectedHitObjects.Any()) + { + beatSnapGrid.SelectionTimeRange = (EditorBeatmap.SelectedHitObjects.Min(h => h.StartTime), EditorBeatmap.SelectedHitObjects.Max(h => h.GetEndTime())); + } + else + beatSnapGrid.SelectionTimeRange = null; + } + else + { + var result = FindSnappedPositionAndTime(inputManager.CurrentState.Mouse.Position); + if (result.Time is double time) + beatSnapGrid.SelectionTimeRange = (time, time); + else + beatSnapGrid.SelectionTimeRange = null; + } + } + protected override double ReadCurrentDistanceSnap(HitObject before, HitObject after) { // osu!catch's distance snap implementation is limited, in that a custom spacing cannot be specified. diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index cf7337fd0d..f091dee845 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; @@ -41,6 +42,8 @@ namespace osu.Game.Rulesets.Catch.UI internal CatcherArea CatcherArea { get; private set; } = null!; + public Container UnderlayElements { get; private set; } = null!; + private readonly IBeatmapDifficultyInfo difficulty; public CatchPlayfield(IBeatmapDifficultyInfo difficulty) @@ -62,6 +65,10 @@ namespace osu.Game.Rulesets.Catch.UI AddRangeInternal(new[] { + UnderlayElements = new Container + { + RelativeSizeAxes = Axes.Both, + }, droppedObjectContainer, Catcher.CreateProxiedContent(), HitObjectContainer.CreateProxy(), From 6b18f2f2bb7492c0cf0208452e220dcd74d4b866 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jun 2023 21:40:09 +0900 Subject: [PATCH 058/169] Fix osu!mania scroll speed milliseconds having too much precision Closes #23750. --- .../Configuration/ManiaRulesetConfigManager.cs | 2 +- osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs | 2 +- osu.Game/Localisation/RulesetSettingsStrings.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs index b2155968ea..f975c7f1d4 100644 --- a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs +++ b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Mania.Configuration speed => new SettingDescription( rawValue: speed, name: RulesetSettingsStrings.ScrollSpeed, - value: RulesetSettingsStrings.ScrollSpeedTooltip(DrawableManiaRuleset.ComputeScrollTime(speed), speed) + value: RulesetSettingsStrings.ScrollSpeedTooltip((int)DrawableManiaRuleset.ComputeScrollTime(speed), speed) ) ) }; diff --git a/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs b/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs index a5434a36ab..065534eec4 100644 --- a/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs +++ b/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mania private partial class ManiaScrollSlider : RoundedSliderBar { - public override LocalisableString TooltipText => RulesetSettingsStrings.ScrollSpeedTooltip(DrawableManiaRuleset.ComputeScrollTime(Current.Value), Current.Value); + public override LocalisableString TooltipText => RulesetSettingsStrings.ScrollSpeedTooltip((int)DrawableManiaRuleset.ComputeScrollTime(Current.Value), Current.Value); } } } diff --git a/osu.Game/Localisation/RulesetSettingsStrings.cs b/osu.Game/Localisation/RulesetSettingsStrings.cs index 91bbece004..3fa7656cbb 100644 --- a/osu.Game/Localisation/RulesetSettingsStrings.cs +++ b/osu.Game/Localisation/RulesetSettingsStrings.cs @@ -82,7 +82,7 @@ namespace osu.Game.Localisation /// /// "{0}ms (speed {1})" /// - public static LocalisableString ScrollSpeedTooltip(double scrollTime, int scrollSpeed) => new TranslatableString(getKey(@"ruleset"), @"{0:0}ms (speed {1})", scrollTime, scrollSpeed); + public static LocalisableString ScrollSpeedTooltip(int scrollTime, int scrollSpeed) => new TranslatableString(getKey(@"ruleset"), @"{0}ms (speed {1})", scrollTime, scrollSpeed); private static string getKey(string key) => $@"{prefix}:{key}"; } From e0e013cca10ef665af0647c1d914955dfb45d42e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jun 2023 22:17:05 +0900 Subject: [PATCH 059/169] Fix incorrect realm schema comment Co-authored-by: timiimit <32331609+timiimit@users.noreply.github.com> --- osu.Game/Database/RealmAccess.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 9e5f41502b..f4c6c802f1 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -71,7 +71,7 @@ namespace osu.Game.Database /// 24 2022-08-22 Added MaximumStatistics to ScoreInfo. /// 25 2022-09-18 Remove skins to add with new naming. /// 26 2023-02-05 Added BeatmapHash to ScoreInfo. - /// 27 2023-06-06 Added EditorTimestamp to ScoreInfo. + /// 27 2023-06-06 Added EditorTimestamp to BeatmapInfo. /// private const int schema_version = 27; From 247fa088dbc947770df1e131aae3e8e6104d3b3f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jun 2023 22:22:42 +0900 Subject: [PATCH 060/169] Add `IHasNoTimedInputs` interface to scope change further --- osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 2 +- osu.Game/Rulesets/Mods/IHasNoTimedInputs.cs | 15 +++++++++++++++ .../Play/PlayerSettings/BeatmapOffsetControl.cs | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Rulesets/Mods/IHasNoTimedInputs.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index 32ffb545e0..aaa7c70a8d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -18,7 +18,7 @@ using static osu.Game.Input.Handlers.ReplayInputHandler; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModRelax : ModRelax, IUpdatableByPlayfield, IApplicableToDrawableRuleset, IApplicableToPlayer + public class OsuModRelax : ModRelax, IUpdatableByPlayfield, IApplicableToDrawableRuleset, IApplicableToPlayer, IHasNoTimedInputs { public override LocalisableString Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things."; diff --git a/osu.Game/Rulesets/Mods/IHasNoTimedInputs.cs b/osu.Game/Rulesets/Mods/IHasNoTimedInputs.cs new file mode 100644 index 0000000000..c0d709ad4a --- /dev/null +++ b/osu.Game/Rulesets/Mods/IHasNoTimedInputs.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Mods +{ + /// + /// Denotes a mod which removes timed inputs from a ruleset which would usually have them. + /// + /// + /// This will be used, for instance, to omit showing offset calibration UI post-gameplay. + /// + public interface IHasNoTimedInputs + { + } +} diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index 8d5459809e..b542707185 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -184,7 +184,7 @@ namespace osu.Game.Screens.Play.PlayerSettings if (score.NewValue == null) return; - if (score.NewValue.Mods.Any(m => !m.UserPlayable || m is ModRelax)) + if (score.NewValue.Mods.Any(m => !m.UserPlayable || m is IHasNoTimedInputs)) return; var hitEvents = score.NewValue.HitEvents; From be59eb11169324ec3df62629d4d5b0cabf75c775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 6 Jun 2023 18:01:15 +0200 Subject: [PATCH 061/169] Fix triangles song progress bar blinking during gameplay Closes #23760. --- osu.Game/Screens/Play/HUD/DefaultSongProgress.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs index 6eed563703..202ead2d66 100644 --- a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs +++ b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Localisation.HUD; @@ -101,7 +102,12 @@ namespace osu.Game.Screens.Play.HUD protected override void Update() { base.Update(); - Height = bottom_bar_height + graph_height + handle_size.Y + info.Height - graph.Y; + + // to prevent unnecessary invalidations of the song progress graph due to changes in size, apply tolerance when updating the height. + float newHeight = bottom_bar_height + graph_height + handle_size.Y + info.Height - graph.Y; + + if (!Precision.AlmostEquals(Height, newHeight, 5f)) + Height = newHeight; } private void updateBarVisibility() From 46ec250d342721c70e54a20795f6994a31c8a12b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 6 Jun 2023 20:21:55 +0200 Subject: [PATCH 062/169] Fix test failures due to cross-test state pollution `TestSceneEditorTestGameplay` is not isolated from database, and one of the tests exiting editor when seeked to 60000 milliseconds (`TestClockTimeTransferIsOneDirectional()`) ended up changing `EditorTimestamp` to the same value, causing `TestSaveChangesBeforeGameplayTest()` to fail due to changing initial state. To fix, perform a direct deletion of imported beatmaps in realm to avert this scenario, contrary to the soft-deletion via `BeatmapManager` done previously. --- .../Visual/Editing/TestSceneEditorTestGameplay.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs index 2250868a39..007716bd6c 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs @@ -209,10 +209,14 @@ namespace osu.Game.Tests.Visual.Editing public override void TearDownSteps() { base.TearDownSteps(); - AddStep("delete imported", () => + AddStep("delete imported", () => Realm.Write(r => { - beatmaps.Delete(importedBeatmapSet); - }); + // delete from realm directly rather than via `BeatmapManager` to avoid cross-test pollution + // (`BeatmapManager.Delete()` uses soft deletion, which can lead to beatmap reuse between test cases). + r.RemoveAll(); + r.RemoveAll(); + r.RemoveAll(); + })); } } } From 7694aa721978e88a39c8517fb3cfde27d881ca45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 6 Jun 2023 20:36:44 +0200 Subject: [PATCH 063/169] Add test coverage --- .../Gameplay/TestSceneBeatmapOffsetControl.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapOffsetControl.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapOffsetControl.cs index 6eae795630..4b2909739d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapOffsetControl.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapOffsetControl.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Overlays.Settings; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Scoring; using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Tests.Visual.Ranking; @@ -49,6 +50,21 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("No calibration button", () => !offsetControl.ChildrenOfType().Any()); } + [Test] + public void TestModRemovingTimedInputs() + { + AddStep("Set score with mod removing timed inputs", () => + { + offsetControl.ReferenceScore.Value = new ScoreInfo + { + HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents(10), + Mods = new[] { new OsuModRelax() } + }; + }); + + AddAssert("No calibration button", () => !offsetControl.ChildrenOfType().Any()); + } + [Test] public void TestCalibrationFromZero() { From f51b8a6a058c53e2be50aaf7255680b7a9dbed15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 6 Jun 2023 21:11:24 +0200 Subject: [PATCH 064/169] Fix code quality inspection --- .../Visual/Gameplay/TestSceneBeatmapOffsetControl.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapOffsetControl.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapOffsetControl.cs index 4b2909739d..f3701b664c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapOffsetControl.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapOffsetControl.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Overlays.Settings; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Scoring; using osu.Game.Screens.Play.PlayerSettings; @@ -58,7 +59,7 @@ namespace osu.Game.Tests.Visual.Gameplay offsetControl.ReferenceScore.Value = new ScoreInfo { HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents(10), - Mods = new[] { new OsuModRelax() } + Mods = new Mod[] { new OsuModRelax() } }; }); From cdd931633defa30d99d0fb46bb34c5634b34cc38 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jun 2023 12:10:30 +0900 Subject: [PATCH 065/169] Change 3K middle key to "special" purple colour MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- .../Skinning/Argon/ManiaArgonSkinTransformer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index 3a15cb359f..ddd6365c25 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -141,7 +141,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { case 0: return colour_green; - case 1: return colour_purple; + case 1: return colour_special_column; case 2: return colour_cyan; From 6d446d3e97dd6f936753e8fb0427929ccab541a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jun 2023 12:25:34 +0900 Subject: [PATCH 066/169] Fix incorrect colouring of beat snap grids (mania and catch) --- osu.Game.Rulesets.Catch/Edit/CatchBeatSnapGrid.cs | 2 +- osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchBeatSnapGrid.cs b/osu.Game.Rulesets.Catch/Edit/CatchBeatSnapGrid.cs index a2421c2b29..6862696b3a 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchBeatSnapGrid.cs @@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.Catch.Edit } Color4 colour = BindableBeatDivisor.GetColourFor( - BindableBeatDivisor.GetDivisorForBeatIndex(Math.Max(1, beat), beatDivisor.Value), colours); + BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value), colours); if (!availableLines.TryPop(out var line)) line = new DrawableGridLine(); diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs index d1d5492b7a..2d4b5f718c 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs @@ -129,7 +129,7 @@ namespace osu.Game.Rulesets.Mania.Edit } Color4 colour = BindableBeatDivisor.GetColourFor( - BindableBeatDivisor.GetDivisorForBeatIndex(Math.Max(1, beat), beatDivisor.Value), colours); + BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value), colours); foreach (var grid in grids) { From c276b728ecee7dc7ef471464445c69e4a729d570 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jun 2023 13:30:04 +0900 Subject: [PATCH 067/169] Clean up `TestSceneDrumRollJudgements` --- .../Judgements/TestSceneDrumRollJudgements.cs | 67 ++++--------------- 1 file changed, 14 insertions(+), 53 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Judgements/TestSceneDrumRollJudgements.cs b/osu.Game.Rulesets.Taiko.Tests/Judgements/TestSceneDrumRollJudgements.cs index a9231b4783..bccede38b3 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Judgements/TestSceneDrumRollJudgements.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Judgements/TestSceneDrumRollJudgements.cs @@ -15,8 +15,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements [Test] public void TestHitAllDrumRoll() { - const double hit_time = 1000; - PerformTest(new List { new TaikoReplayFrame(0), @@ -24,11 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements new TaikoReplayFrame(1001), new TaikoReplayFrame(2000, TaikoAction.LeftCentre), new TaikoReplayFrame(2001), - }, CreateBeatmap(new DrumRoll - { - StartTime = hit_time, - Duration = 1000 - })); + }, CreateBeatmap(createDrumRoll(false))); AssertJudgementCount(3); AssertResult(0, HitResult.SmallBonus); @@ -39,18 +33,12 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements [Test] public void TestHitSomeDrumRoll() { - const double hit_time = 1000; - PerformTest(new List { new TaikoReplayFrame(0), new TaikoReplayFrame(2000, TaikoAction.LeftCentre), new TaikoReplayFrame(2001), - }, CreateBeatmap(new DrumRoll - { - StartTime = hit_time, - Duration = 1000 - })); + }, CreateBeatmap(createDrumRoll(false))); AssertJudgementCount(3); AssertResult(0, HitResult.IgnoreMiss); @@ -61,16 +49,10 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements [Test] public void TestHitNoneDrumRoll() { - const double hit_time = 1000; - PerformTest(new List { new TaikoReplayFrame(0), - }, CreateBeatmap(new DrumRoll - { - StartTime = hit_time, - Duration = 1000 - })); + }, CreateBeatmap(createDrumRoll(false))); AssertJudgementCount(3); AssertResult(0, HitResult.IgnoreMiss); @@ -81,8 +63,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements [Test] public void TestHitAllStrongDrumRollWithOneKey() { - const double hit_time = 1000; - PerformTest(new List { new TaikoReplayFrame(0), @@ -90,12 +70,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements new TaikoReplayFrame(1001), new TaikoReplayFrame(2000, TaikoAction.LeftCentre), new TaikoReplayFrame(2001), - }, CreateBeatmap(new DrumRoll - { - StartTime = hit_time, - Duration = 1000, - IsStrong = true - })); + }, CreateBeatmap(createDrumRoll(true))); AssertJudgementCount(6); @@ -112,19 +87,12 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements [Test] public void TestHitSomeStrongDrumRollWithOneKey() { - const double hit_time = 1000; - PerformTest(new List { new TaikoReplayFrame(0), new TaikoReplayFrame(2000, TaikoAction.LeftCentre), new TaikoReplayFrame(2001), - }, CreateBeatmap(new DrumRoll - { - StartTime = hit_time, - Duration = 1000, - IsStrong = true - })); + }, CreateBeatmap(createDrumRoll(true))); AssertJudgementCount(6); @@ -141,8 +109,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements [Test] public void TestHitAllStrongDrumRollWithBothKeys() { - const double hit_time = 1000; - PerformTest(new List { new TaikoReplayFrame(0), @@ -150,12 +116,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements new TaikoReplayFrame(1001), new TaikoReplayFrame(2000, TaikoAction.LeftCentre, TaikoAction.RightCentre), new TaikoReplayFrame(2001), - }, CreateBeatmap(new DrumRoll - { - StartTime = hit_time, - Duration = 1000, - IsStrong = true - })); + }, CreateBeatmap(createDrumRoll(true))); AssertJudgementCount(6); @@ -172,19 +133,12 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements [Test] public void TestHitSomeStrongDrumRollWithBothKeys() { - const double hit_time = 1000; - PerformTest(new List { new TaikoReplayFrame(0), new TaikoReplayFrame(2000, TaikoAction.LeftCentre, TaikoAction.RightCentre), new TaikoReplayFrame(2001), - }, CreateBeatmap(new DrumRoll - { - StartTime = hit_time, - Duration = 1000, - IsStrong = true - })); + }, CreateBeatmap(createDrumRoll(true))); AssertJudgementCount(6); @@ -197,5 +151,12 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements AssertResult(0, HitResult.IgnoreHit); AssertResult(2, HitResult.IgnoreHit); } + + private DrumRoll createDrumRoll(bool strong) => new DrumRoll + { + StartTime = 1000, + Duration = 1000, + IsStrong = strong + }; } } From 2fca81492663c57039bad6dab6635a37be0c87b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jun 2023 13:44:01 +0900 Subject: [PATCH 068/169] Update drum roll judgement tests to work correctly with more correct tick rate applied --- .../Judgements/TestSceneDrumRollJudgements.cs | 79 +++++++++++++------ 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Judgements/TestSceneDrumRollJudgements.cs b/osu.Game.Rulesets.Taiko.Tests/Judgements/TestSceneDrumRollJudgements.cs index bccede38b3..21f2b8f1be 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Judgements/TestSceneDrumRollJudgements.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Judgements/TestSceneDrumRollJudgements.cs @@ -20,13 +20,22 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements new TaikoReplayFrame(0), new TaikoReplayFrame(1000, TaikoAction.LeftCentre), new TaikoReplayFrame(1001), + new TaikoReplayFrame(1250, TaikoAction.LeftCentre), + new TaikoReplayFrame(1251), + new TaikoReplayFrame(1500, TaikoAction.LeftCentre), + new TaikoReplayFrame(1501), + new TaikoReplayFrame(1750, TaikoAction.LeftCentre), + new TaikoReplayFrame(1751), new TaikoReplayFrame(2000, TaikoAction.LeftCentre), new TaikoReplayFrame(2001), }, CreateBeatmap(createDrumRoll(false))); - AssertJudgementCount(3); + AssertJudgementCount(6); AssertResult(0, HitResult.SmallBonus); AssertResult(1, HitResult.SmallBonus); + AssertResult(2, HitResult.SmallBonus); + AssertResult(3, HitResult.SmallBonus); + AssertResult(4, HitResult.SmallBonus); AssertResult(0, HitResult.IgnoreHit); } @@ -40,9 +49,12 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements new TaikoReplayFrame(2001), }, CreateBeatmap(createDrumRoll(false))); - AssertJudgementCount(3); + AssertJudgementCount(6); AssertResult(0, HitResult.IgnoreMiss); - AssertResult(1, HitResult.SmallBonus); + AssertResult(1, HitResult.IgnoreMiss); + AssertResult(2, HitResult.IgnoreMiss); + AssertResult(3, HitResult.IgnoreMiss); + AssertResult(4, HitResult.SmallBonus); AssertResult(0, HitResult.IgnoreHit); } @@ -54,9 +66,12 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements new TaikoReplayFrame(0), }, CreateBeatmap(createDrumRoll(false))); - AssertJudgementCount(3); + AssertJudgementCount(6); AssertResult(0, HitResult.IgnoreMiss); AssertResult(1, HitResult.IgnoreMiss); + AssertResult(2, HitResult.IgnoreMiss); + AssertResult(3, HitResult.IgnoreMiss); + AssertResult(4, HitResult.IgnoreMiss); AssertResult(0, HitResult.IgnoreHit); } @@ -68,20 +83,26 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements new TaikoReplayFrame(0), new TaikoReplayFrame(1000, TaikoAction.LeftCentre), new TaikoReplayFrame(1001), + new TaikoReplayFrame(1250, TaikoAction.LeftCentre), + new TaikoReplayFrame(1251), + new TaikoReplayFrame(1500, TaikoAction.LeftCentre), + new TaikoReplayFrame(1501), + new TaikoReplayFrame(1750, TaikoAction.LeftCentre), + new TaikoReplayFrame(1751), new TaikoReplayFrame(2000, TaikoAction.LeftCentre), new TaikoReplayFrame(2001), }, CreateBeatmap(createDrumRoll(true))); - AssertJudgementCount(6); + AssertJudgementCount(12); - AssertResult(0, HitResult.SmallBonus); - AssertResult(0, HitResult.LargeBonus); - - AssertResult(1, HitResult.SmallBonus); - AssertResult(1, HitResult.LargeBonus); + for (int i = 0; i < 5; i++) + { + AssertResult(i, HitResult.SmallBonus); + AssertResult(i, HitResult.LargeBonus); + } AssertResult(0, HitResult.IgnoreHit); - AssertResult(2, HitResult.IgnoreHit); + AssertResult(5, HitResult.IgnoreHit); } [Test] @@ -94,16 +115,16 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements new TaikoReplayFrame(2001), }, CreateBeatmap(createDrumRoll(true))); - AssertJudgementCount(6); + AssertJudgementCount(12); AssertResult(0, HitResult.IgnoreMiss); AssertResult(0, HitResult.IgnoreMiss); - AssertResult(1, HitResult.SmallBonus); - AssertResult(1, HitResult.LargeBonus); + AssertResult(4, HitResult.SmallBonus); + AssertResult(4, HitResult.LargeBonus); AssertResult(0, HitResult.IgnoreHit); - AssertResult(2, HitResult.IgnoreHit); + AssertResult(5, HitResult.IgnoreHit); } [Test] @@ -114,20 +135,26 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements new TaikoReplayFrame(0), new TaikoReplayFrame(1000, TaikoAction.LeftCentre, TaikoAction.RightCentre), new TaikoReplayFrame(1001), + new TaikoReplayFrame(1250, TaikoAction.LeftCentre, TaikoAction.RightCentre), + new TaikoReplayFrame(1251), + new TaikoReplayFrame(1500, TaikoAction.LeftCentre, TaikoAction.RightCentre), + new TaikoReplayFrame(1501), + new TaikoReplayFrame(1750, TaikoAction.LeftCentre, TaikoAction.RightCentre), + new TaikoReplayFrame(1751), new TaikoReplayFrame(2000, TaikoAction.LeftCentre, TaikoAction.RightCentre), new TaikoReplayFrame(2001), }, CreateBeatmap(createDrumRoll(true))); - AssertJudgementCount(6); + AssertJudgementCount(12); - AssertResult(0, HitResult.SmallBonus); - AssertResult(0, HitResult.LargeBonus); - - AssertResult(1, HitResult.SmallBonus); - AssertResult(1, HitResult.LargeBonus); + for (int i = 0; i < 5; i++) + { + AssertResult(i, HitResult.SmallBonus); + AssertResult(i, HitResult.LargeBonus); + } AssertResult(0, HitResult.IgnoreHit); - AssertResult(2, HitResult.IgnoreHit); + AssertResult(5, HitResult.IgnoreHit); } [Test] @@ -140,16 +167,16 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements new TaikoReplayFrame(2001), }, CreateBeatmap(createDrumRoll(true))); - AssertJudgementCount(6); + AssertJudgementCount(12); AssertResult(0, HitResult.IgnoreMiss); AssertResult(0, HitResult.IgnoreMiss); - AssertResult(1, HitResult.SmallBonus); - AssertResult(1, HitResult.LargeBonus); + AssertResult(4, HitResult.SmallBonus); + AssertResult(4, HitResult.LargeBonus); AssertResult(0, HitResult.IgnoreHit); - AssertResult(2, HitResult.IgnoreHit); + AssertResult(5, HitResult.IgnoreHit); } private DrumRoll createDrumRoll(bool strong) => new DrumRoll From 30f17d586986f6dc97a6cd110694272cdea9483b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jun 2023 14:03:04 +0900 Subject: [PATCH 069/169] Disabling reporting users from in-game chat Addresses #23684. Safer alternative to #23698. --- osu.Game/Overlays/Chat/ChatLine.cs | 33 ++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index 2f4c175ac4..c85206d5f7 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -56,11 +56,11 @@ namespace osu.Game.Overlays.Chat [Resolved] private OverlayColourProvider? colourProvider { get; set; } - private readonly OsuSpriteText drawableTimestamp; + private OsuSpriteText drawableTimestamp = null!; - private readonly DrawableChatUsername drawableUsername; + private DrawableChatUsername drawableUsername = null!; - private readonly LinkFlowContainer drawableContentFlow; + private LinkFlowContainer drawableContentFlow = null!; private readonly Bindable prefer24HourTime = new Bindable(); @@ -69,8 +69,16 @@ namespace osu.Game.Overlays.Chat public ChatLine(Message message) { Message = message; + RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; + } + + [BackgroundDependencyLoader] + private void load(OsuConfigManager configManager) + { + configManager.BindWith(OsuSetting.Prefer24HourTime, prefer24HourTime); + prefer24HourTime.BindValueChanged(_ => updateTimestamp()); InternalChild = new GridContainer { @@ -103,7 +111,6 @@ namespace osu.Game.Overlays.Chat Origin = Anchor.TopRight, Anchor = Anchor.TopRight, Margin = new MarginPadding { Horizontal = Spacing }, - ReportRequested = this.ShowPopover, }, drawableContentFlow = new LinkFlowContainer(styleMessageContent) { @@ -115,13 +122,6 @@ namespace osu.Game.Overlays.Chat }; } - [BackgroundDependencyLoader] - private void load(OsuConfigManager configManager) - { - configManager.BindWith(OsuSetting.Prefer24HourTime, prefer24HourTime); - prefer24HourTime.BindValueChanged(_ => updateTimestamp()); - } - protected override void LoadComplete() { base.LoadComplete(); @@ -130,6 +130,17 @@ namespace osu.Game.Overlays.Chat updateMessageContent(); FinishTransforms(true); + + if (this.FindClosestParent() != null) + { + // This guards against cases like in-game chat where there's no available popover container. + // There may be a future where a global one becomes available, at which point this code may be unnecessary. + // + // See: + // https://github.com/ppy/osu/pull/23698 + // https://github.com/ppy/osu/pull/14554 + drawableUsername.ReportRequested = this.ShowPopover; + } } public Popover GetPopover() => new ReportChatPopover(message); From 62cb6a98cae4856c6a3ef3b75cd5065d4ad173a0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 7 Jun 2023 08:20:38 +0300 Subject: [PATCH 070/169] Remove redundant nullable suppression directives --- osu.Game.Rulesets.Mania/Objects/HeadNote.cs | 2 -- osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPosition.cs | 2 -- osu.Game.Rulesets.Osu/Objects/Drawables/IRequireTracking.cs | 2 -- osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs | 2 -- osu.Game.Rulesets.Taiko/Objects/HitType.cs | 2 -- osu.Game.Tests/Rulesets/TestSceneBrokenRulesetHandling.cs | 2 -- osu.Game.Tournament/IPC/TourneyState.cs | 2 -- .../Screens/Ladder/Components/ConditionalTournamentMatch.cs | 2 -- osu.Game/Beatmaps/DifficultyRating.cs | 2 -- osu.Game/Beatmaps/Drawables/Cards/BeatmapCardSize.cs | 2 -- osu.Game/Beatmaps/Legacy/LegacyOrigins.cs | 2 -- osu.Game/Configuration/IntroSequence.cs | 2 -- osu.Game/Configuration/ReleaseStream.cs | 2 -- osu.Game/Configuration/ToolbarClockDisplayMode.cs | 2 -- osu.Game/Database/ISoftDelete.cs | 2 -- osu.Game/Graphics/UserInterface/MenuItemType.cs | 2 -- osu.Game/Graphics/UserInterface/SelectionState.cs | 2 -- osu.Game/Graphics/UserInterface/TernaryState.cs | 2 -- osu.Game/Online/API/APIRequestCompletionState.cs | 2 -- osu.Game/Online/Chat/ChannelType.cs | 2 -- osu.Game/Online/Leaderboards/LeaderboardState.cs | 2 -- osu.Game/Online/Multiplayer/ForceGameplayStartCountdown.cs | 2 -- osu.Game/Online/Multiplayer/IMultiplayerServer.cs | 2 -- osu.Game/Online/Multiplayer/MultiplayerUserState.cs | 2 -- osu.Game/Online/Spectator/SpectatedUserState.cs | 2 -- osu.Game/Overlays/OverlayActivation.cs | 2 -- osu.Game/Overlays/SortDirection.cs | 2 -- osu.Game/Rulesets/Configuration/IRulesetConfigManager.cs | 2 -- osu.Game/Rulesets/Objects/Drawables/ArmedState.cs | 2 -- osu.Game/Rulesets/Objects/IBarLine.cs | 2 -- osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 2 -- osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs | 2 -- osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs | 2 -- osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs | 2 -- osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs | 2 -- osu.Game/Rulesets/Objects/Types/IHasColumn.cs | 2 -- osu.Game/Rulesets/Objects/Types/IHasCombo.cs | 2 -- osu.Game/Rulesets/Objects/Types/IHasDistance.cs | 2 -- osu.Game/Rulesets/Objects/Types/IHasDuration.cs | 2 -- osu.Game/Rulesets/Objects/Types/IHasHold.cs | 2 -- osu.Game/Rulesets/Objects/Types/IHasLegacyLastTickOffset.cs | 2 -- osu.Game/Rulesets/Objects/Types/IHasXPosition.cs | 2 -- osu.Game/Rulesets/Objects/Types/IHasYPosition.cs | 2 -- osu.Game/Rulesets/Objects/Types/PathType.cs | 2 -- osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs | 2 -- osu.Game/Screens/Edit/Compose/Components/BeatDivisorType.cs | 2 -- .../Match/Playlist/MultiplayerPlaylistDisplayMode.cs | 2 -- .../Screens/OnlinePlay/Multiplayer/Spectate/MasterClockState.cs | 2 -- osu.Game/Screens/Ranking/PanelState.cs | 2 -- osu.Game/Screens/ScorePresentType.cs | 2 -- osu.Game/Screens/Select/Filter/Operator.cs | 2 -- 51 files changed, 102 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/HeadNote.cs b/osu.Game.Rulesets.Mania/Objects/HeadNote.cs index fb5c7b4ddd..e69cc62aed 100644 --- a/osu.Game.Rulesets.Mania/Objects/HeadNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/HeadNote.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 - namespace osu.Game.Rulesets.Mania.Objects { public class HeadNote : Note diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPosition.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPosition.cs index 92071d4a57..616bb17e05 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPosition.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPosition.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 - namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { public enum SliderPosition diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/IRequireTracking.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/IRequireTracking.cs index 55de5a0e8d..b1815b23c9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/IRequireTracking.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/IRequireTracking.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 - namespace osu.Game.Rulesets.Osu.Objects.Drawables { public interface IRequireTracking diff --git a/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs b/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs index eddd251bda..7594f7c2e0 100644 --- a/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs +++ b/osu.Game.Rulesets.Osu/Objects/ISliderProgress.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 - namespace osu.Game.Rulesets.Osu.Objects { public interface ISliderProgress diff --git a/osu.Game.Rulesets.Taiko/Objects/HitType.cs b/osu.Game.Rulesets.Taiko/Objects/HitType.cs index eae7fa683a..17b3fdbd04 100644 --- a/osu.Game.Rulesets.Taiko/Objects/HitType.cs +++ b/osu.Game.Rulesets.Taiko/Objects/HitType.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 - namespace osu.Game.Rulesets.Taiko.Objects { /// diff --git a/osu.Game.Tests/Rulesets/TestSceneBrokenRulesetHandling.cs b/osu.Game.Tests/Rulesets/TestSceneBrokenRulesetHandling.cs index c3a6b7c474..dac6beea65 100644 --- a/osu.Game.Tests/Rulesets/TestSceneBrokenRulesetHandling.cs +++ b/osu.Game.Tests/Rulesets/TestSceneBrokenRulesetHandling.cs @@ -73,7 +73,5 @@ namespace osu.Game.Tests.Rulesets public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => null; public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => null; } - -#nullable enable } } diff --git a/osu.Game.Tournament/IPC/TourneyState.cs b/osu.Game.Tournament/IPC/TourneyState.cs index 2c7253dc10..ef1c612a53 100644 --- a/osu.Game.Tournament/IPC/TourneyState.cs +++ b/osu.Game.Tournament/IPC/TourneyState.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 - namespace osu.Game.Tournament.IPC { public enum TourneyState diff --git a/osu.Game.Tournament/Screens/Ladder/Components/ConditionalTournamentMatch.cs b/osu.Game.Tournament/Screens/Ladder/Components/ConditionalTournamentMatch.cs index 04155fcb89..16224a7fb4 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/ConditionalTournamentMatch.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/ConditionalTournamentMatch.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 osu.Game.Tournament.Models; namespace osu.Game.Tournament.Screens.Ladder.Components diff --git a/osu.Game/Beatmaps/DifficultyRating.cs b/osu.Game/Beatmaps/DifficultyRating.cs index 478c0e36df..f0ee0ad705 100644 --- a/osu.Game/Beatmaps/DifficultyRating.cs +++ b/osu.Game/Beatmaps/DifficultyRating.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 - namespace osu.Game.Beatmaps { public enum DifficultyRating diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardSize.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardSize.cs index 1f6538a890..098265506d 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardSize.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardSize.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 - namespace osu.Game.Beatmaps.Drawables.Cards { /// diff --git a/osu.Game/Beatmaps/Legacy/LegacyOrigins.cs b/osu.Game/Beatmaps/Legacy/LegacyOrigins.cs index 62b0edc384..31f67d6dfd 100644 --- a/osu.Game/Beatmaps/Legacy/LegacyOrigins.cs +++ b/osu.Game/Beatmaps/Legacy/LegacyOrigins.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 - namespace osu.Game.Beatmaps.Legacy { internal enum LegacyOrigins diff --git a/osu.Game/Configuration/IntroSequence.cs b/osu.Game/Configuration/IntroSequence.cs index 8327ea2f57..5672c44bbe 100644 --- a/osu.Game/Configuration/IntroSequence.cs +++ b/osu.Game/Configuration/IntroSequence.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 - namespace osu.Game.Configuration { public enum IntroSequence diff --git a/osu.Game/Configuration/ReleaseStream.cs b/osu.Game/Configuration/ReleaseStream.cs index 9cdd91bfd0..ed0bee1dd8 100644 --- a/osu.Game/Configuration/ReleaseStream.cs +++ b/osu.Game/Configuration/ReleaseStream.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 - namespace osu.Game.Configuration { public enum ReleaseStream diff --git a/osu.Game/Configuration/ToolbarClockDisplayMode.cs b/osu.Game/Configuration/ToolbarClockDisplayMode.cs index 682e221ef8..2f42f7a9b5 100644 --- a/osu.Game/Configuration/ToolbarClockDisplayMode.cs +++ b/osu.Game/Configuration/ToolbarClockDisplayMode.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 - namespace osu.Game.Configuration { public enum ToolbarClockDisplayMode diff --git a/osu.Game/Database/ISoftDelete.cs b/osu.Game/Database/ISoftDelete.cs index b07c8db2de..afa42c2002 100644 --- a/osu.Game/Database/ISoftDelete.cs +++ b/osu.Game/Database/ISoftDelete.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 - namespace osu.Game.Database { /// diff --git a/osu.Game/Graphics/UserInterface/MenuItemType.cs b/osu.Game/Graphics/UserInterface/MenuItemType.cs index 1eb45d6b1c..0269f2cb57 100644 --- a/osu.Game/Graphics/UserInterface/MenuItemType.cs +++ b/osu.Game/Graphics/UserInterface/MenuItemType.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 - namespace osu.Game.Graphics.UserInterface { public enum MenuItemType diff --git a/osu.Game/Graphics/UserInterface/SelectionState.cs b/osu.Game/Graphics/UserInterface/SelectionState.cs index edabf0547b..c85b2ad3ab 100644 --- a/osu.Game/Graphics/UserInterface/SelectionState.cs +++ b/osu.Game/Graphics/UserInterface/SelectionState.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 - namespace osu.Game.Graphics.UserInterface { public enum SelectionState diff --git a/osu.Game/Graphics/UserInterface/TernaryState.cs b/osu.Game/Graphics/UserInterface/TernaryState.cs index effbe624c3..d4de28044f 100644 --- a/osu.Game/Graphics/UserInterface/TernaryState.cs +++ b/osu.Game/Graphics/UserInterface/TernaryState.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 - namespace osu.Game.Graphics.UserInterface { /// diff --git a/osu.Game/Online/API/APIRequestCompletionState.cs b/osu.Game/Online/API/APIRequestCompletionState.cs index 52eb669a7d..84c9974dd8 100644 --- a/osu.Game/Online/API/APIRequestCompletionState.cs +++ b/osu.Game/Online/API/APIRequestCompletionState.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 - namespace osu.Game.Online.API { public enum APIRequestCompletionState diff --git a/osu.Game/Online/Chat/ChannelType.cs b/osu.Game/Online/Chat/ChannelType.cs index a864e20830..bd628e90c4 100644 --- a/osu.Game/Online/Chat/ChannelType.cs +++ b/osu.Game/Online/Chat/ChannelType.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 - namespace osu.Game.Online.Chat { public enum ChannelType diff --git a/osu.Game/Online/Leaderboards/LeaderboardState.cs b/osu.Game/Online/Leaderboards/LeaderboardState.cs index abc0ef4f19..6b07500a98 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardState.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardState.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 - namespace osu.Game.Online.Leaderboards { public enum LeaderboardState diff --git a/osu.Game/Online/Multiplayer/ForceGameplayStartCountdown.cs b/osu.Game/Online/Multiplayer/ForceGameplayStartCountdown.cs index bbfc5a02c6..c497601e37 100644 --- a/osu.Game/Online/Multiplayer/ForceGameplayStartCountdown.cs +++ b/osu.Game/Online/Multiplayer/ForceGameplayStartCountdown.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 MessagePack; namespace osu.Game.Online.Multiplayer diff --git a/osu.Game/Online/Multiplayer/IMultiplayerServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerServer.cs index cc7a474ce7..d3a070af6d 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerServer.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 - namespace osu.Game.Online.Multiplayer { /// diff --git a/osu.Game/Online/Multiplayer/MultiplayerUserState.cs b/osu.Game/Online/Multiplayer/MultiplayerUserState.cs index 0f7dc6b8cd..d1369a7970 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerUserState.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerUserState.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 - namespace osu.Game.Online.Multiplayer { public enum MultiplayerUserState diff --git a/osu.Game/Online/Spectator/SpectatedUserState.cs b/osu.Game/Online/Spectator/SpectatedUserState.cs index edf0859a33..0f0a3068b8 100644 --- a/osu.Game/Online/Spectator/SpectatedUserState.cs +++ b/osu.Game/Online/Spectator/SpectatedUserState.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 - namespace osu.Game.Online.Spectator { public enum SpectatedUserState diff --git a/osu.Game/Overlays/OverlayActivation.cs b/osu.Game/Overlays/OverlayActivation.cs index 354153734e..68d7ee8ea9 100644 --- a/osu.Game/Overlays/OverlayActivation.cs +++ b/osu.Game/Overlays/OverlayActivation.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 - namespace osu.Game.Overlays { public enum OverlayActivation diff --git a/osu.Game/Overlays/SortDirection.cs b/osu.Game/Overlays/SortDirection.cs index 98ac31103f..3af9614972 100644 --- a/osu.Game/Overlays/SortDirection.cs +++ b/osu.Game/Overlays/SortDirection.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 - namespace osu.Game.Overlays { public enum SortDirection diff --git a/osu.Game/Rulesets/Configuration/IRulesetConfigManager.cs b/osu.Game/Rulesets/Configuration/IRulesetConfigManager.cs index af315bfb28..5a3ad5e786 100644 --- a/osu.Game/Rulesets/Configuration/IRulesetConfigManager.cs +++ b/osu.Game/Rulesets/Configuration/IRulesetConfigManager.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.Configuration.Tracking; diff --git a/osu.Game/Rulesets/Objects/Drawables/ArmedState.cs b/osu.Game/Rulesets/Objects/Drawables/ArmedState.cs index 4faf0920d1..b2d9f50602 100644 --- a/osu.Game/Rulesets/Objects/Drawables/ArmedState.cs +++ b/osu.Game/Rulesets/Objects/Drawables/ArmedState.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 - namespace osu.Game.Rulesets.Objects.Drawables { public enum ArmedState diff --git a/osu.Game/Rulesets/Objects/IBarLine.cs b/osu.Game/Rulesets/Objects/IBarLine.cs index 8cdead6776..14df80e3b9 100644 --- a/osu.Game/Rulesets/Objects/IBarLine.cs +++ b/osu.Game/Rulesets/Objects/IBarLine.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 - namespace osu.Game.Rulesets.Objects { /// diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index d9738ecd0a..8eda2a8f61 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -586,7 +586,5 @@ namespace osu.Game.Rulesets.Objects.Legacy public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), Filename); } - -#nullable disable } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs index 639cacb128..0b69817c13 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.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 osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Objects.Legacy.Mania diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs index 330ebf72c7..84cde5fa95 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.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 osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Objects.Legacy.Mania diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs index 980d37ccd5..cb5178ce48 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.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 - namespace osu.Game.Rulesets.Objects.Legacy.Taiko { /// diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs index a391c8cb43..821554f7ee 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.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 - namespace osu.Game.Rulesets.Objects.Legacy.Taiko { /// diff --git a/osu.Game/Rulesets/Objects/Types/IHasColumn.cs b/osu.Game/Rulesets/Objects/Types/IHasColumn.cs index 3978a7e765..dc07cfbb6a 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasColumn.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasColumn.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 - namespace osu.Game.Rulesets.Objects.Types { /// diff --git a/osu.Game/Rulesets/Objects/Types/IHasCombo.cs b/osu.Game/Rulesets/Objects/Types/IHasCombo.cs index d02b97a3e4..d1a4683a1d 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCombo.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasCombo.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 - namespace osu.Game.Rulesets.Objects.Types { /// diff --git a/osu.Game/Rulesets/Objects/Types/IHasDistance.cs b/osu.Game/Rulesets/Objects/Types/IHasDistance.cs index 549abc046a..b497ca5da3 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasDistance.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasDistance.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 - namespace osu.Game.Rulesets.Objects.Types { /// diff --git a/osu.Game/Rulesets/Objects/Types/IHasDuration.cs b/osu.Game/Rulesets/Objects/Types/IHasDuration.cs index 06ed8eba76..ca734da5ad 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasDuration.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasDuration.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 - namespace osu.Game.Rulesets.Objects.Types { /// diff --git a/osu.Game/Rulesets/Objects/Types/IHasHold.cs b/osu.Game/Rulesets/Objects/Types/IHasHold.cs index 91b05dc3fd..469b8b7892 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasHold.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasHold.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 - namespace osu.Game.Rulesets.Objects.Types { /// diff --git a/osu.Game/Rulesets/Objects/Types/IHasLegacyLastTickOffset.cs b/osu.Game/Rulesets/Objects/Types/IHasLegacyLastTickOffset.cs index dfc526383a..caf22c3023 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasLegacyLastTickOffset.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasLegacyLastTickOffset.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 - namespace osu.Game.Rulesets.Objects.Types { /// diff --git a/osu.Game/Rulesets/Objects/Types/IHasXPosition.cs b/osu.Game/Rulesets/Objects/Types/IHasXPosition.cs index f688c783e1..7e55b21050 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasXPosition.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasXPosition.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 - namespace osu.Game.Rulesets.Objects.Types { /// diff --git a/osu.Game/Rulesets/Objects/Types/IHasYPosition.cs b/osu.Game/Rulesets/Objects/Types/IHasYPosition.cs index 3c0cc595fb..d2561b10a7 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasYPosition.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasYPosition.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 - namespace osu.Game.Rulesets.Objects.Types { /// diff --git a/osu.Game/Rulesets/Objects/Types/PathType.cs b/osu.Game/Rulesets/Objects/Types/PathType.cs index 266a3de6ec..923ce9eba4 100644 --- a/osu.Game/Rulesets/Objects/Types/PathType.cs +++ b/osu.Game/Rulesets/Objects/Types/PathType.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 - namespace osu.Game.Rulesets.Objects.Types { public enum PathType diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs index 58bb80accd..81e1a6c916 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.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 - namespace osu.Game.Rulesets.UI.Scrolling { public enum ScrollingDirection diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorType.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorType.cs index ebdb030e76..4a25144881 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorType.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorType.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 - namespace osu.Game.Screens.Edit.Compose.Components { public enum BeatDivisorType diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylistDisplayMode.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylistDisplayMode.cs index 1672f98637..cc3dca6a34 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylistDisplayMode.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylistDisplayMode.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 - namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist { /// diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MasterClockState.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MasterClockState.cs index 92dbde9f08..8982d1669d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MasterClockState.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MasterClockState.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 - namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { public enum MasterClockState diff --git a/osu.Game/Screens/Ranking/PanelState.cs b/osu.Game/Screens/Ranking/PanelState.cs index 3af74fe0f3..94e2c7cef4 100644 --- a/osu.Game/Screens/Ranking/PanelState.cs +++ b/osu.Game/Screens/Ranking/PanelState.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 - namespace osu.Game.Screens.Ranking { public enum PanelState diff --git a/osu.Game/Screens/ScorePresentType.cs b/osu.Game/Screens/ScorePresentType.cs index 24105467f1..3216f92091 100644 --- a/osu.Game/Screens/ScorePresentType.cs +++ b/osu.Game/Screens/ScorePresentType.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 - namespace osu.Game.Screens { public enum ScorePresentType diff --git a/osu.Game/Screens/Select/Filter/Operator.cs b/osu.Game/Screens/Select/Filter/Operator.cs index a6a53f0c3e..706daf631f 100644 --- a/osu.Game/Screens/Select/Filter/Operator.cs +++ b/osu.Game/Screens/Select/Filter/Operator.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 - namespace osu.Game.Screens.Select.Filter { /// From d57c2ab7e48de5cd5cb9486dee2a95b582467a20 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jun 2023 14:23:23 +0900 Subject: [PATCH 071/169] Fix osu!mania barlines showing in front of notes Closes #23412. --- osu.Game.Rulesets.Mania/Objects/BarLine.cs | 2 -- osu.Game.Rulesets.Mania/UI/Column.cs | 22 +++++++++++++--------- osu.Game.Rulesets.Mania/UI/Stage.cs | 14 ++++++++++++-- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/BarLine.cs b/osu.Game.Rulesets.Mania/Objects/BarLine.cs index 3f04a4fafe..09a746042b 100644 --- a/osu.Game.Rulesets.Mania/Objects/BarLine.cs +++ b/osu.Game.Rulesets.Mania/Objects/BarLine.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 osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 7033fd6755..659b63b640 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -40,7 +40,11 @@ namespace osu.Game.Rulesets.Mania.UI public readonly Bindable Action = new Bindable(); public readonly ColumnHitObjectArea HitObjectArea; + + internal readonly Container BackgroundContainer = new Container { RelativeSizeAxes = Axes.Both }; + internal readonly Container TopLevelContainer = new Container { RelativeSizeAxes = Axes.Both }; + private DrawablePool hitExplosionPool; private readonly OrderedHitPolicy hitPolicy; public Container UnderlayElements => HitObjectArea.UnderlayElements; @@ -77,30 +81,30 @@ namespace osu.Game.Rulesets.Mania.UI skin.SourceChanged += onSourceChanged; onSourceChanged(); - Drawable background = new SkinnableDrawable(new ManiaSkinComponentLookup(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground()) - { - RelativeSizeAxes = Axes.Both, - }; - - InternalChildren = new[] + InternalChildren = new Drawable[] { hitExplosionPool = new DrawablePool(5), sampleTriggerSource = new GameplaySampleTriggerSource(HitObjectContainer), - // For input purposes, the background is added at the highest depth, but is then proxied back below all other elements - background.CreateProxy(), HitObjectArea, keyArea = new SkinnableDrawable(new ManiaSkinComponentLookup(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea()) { RelativeSizeAxes = Axes.Both, }, - background, + // For input purposes, the background is added at the highest depth, but is then proxied back below all other elements externally. + BackgroundContainer, TopLevelContainer, new ColumnTouchInputArea(this) }; + var background = new SkinnableDrawable(new ManiaSkinComponentLookup(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground()) + { + RelativeSizeAxes = Axes.Both, + }; + background.ApplyGameWideClock(host); keyArea.ApplyGameWideClock(host); + BackgroundContainer.Add(background); TopLevelContainer.Add(HitObjectArea.Explosions.CreateProxy()); RegisterPool(10, 50); diff --git a/osu.Game.Rulesets.Mania/UI/Stage.cs b/osu.Game.Rulesets.Mania/UI/Stage.cs index c1d3e85bf1..215d81b64a 100644 --- a/osu.Game.Rulesets.Mania/UI/Stage.cs +++ b/osu.Game.Rulesets.Mania/UI/Stage.cs @@ -60,6 +60,7 @@ namespace osu.Game.Rulesets.Mania.UI RelativeSizeAxes = Axes.Y; AutoSizeAxes = Axes.X; + Container columnBackgrounds; Container topLevelContainer; InternalChildren = new Drawable[] @@ -77,9 +78,13 @@ namespace osu.Game.Rulesets.Mania.UI { RelativeSizeAxes = Axes.Both }, - columnFlow = new ColumnFlow(definition) + columnBackgrounds = new Container { - RelativeSizeAxes = Axes.Y, + Name = "Column backgrounds", + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + } }, new Container { @@ -98,6 +103,10 @@ namespace osu.Game.Rulesets.Mania.UI RelativeSizeAxes = Axes.Y, } }, + columnFlow = new ColumnFlow(definition) + { + RelativeSizeAxes = Axes.Y, + }, new SkinnableDrawable(new ManiaSkinComponentLookup(ManiaSkinComponents.StageForeground), _ => null) { RelativeSizeAxes = Axes.Both @@ -126,6 +135,7 @@ namespace osu.Game.Rulesets.Mania.UI }; topLevelContainer.Add(column.TopLevelContainer.CreateProxy()); + columnBackgrounds.Add(column.BackgroundContainer.CreateProxy()); columnFlow.SetContentForColumn(i, column); AddNested(column); } From 7d49f5d7c6b8cd329335e18500c08e6589c979a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jun 2023 14:53:37 +0900 Subject: [PATCH 072/169] Apply NRT to `SectionsContainer` --- .../Graphics/Containers/SectionsContainer.cs | 62 ++++++++++--------- osu.Game/Overlays/UserProfileOverlay.cs | 2 +- .../Screens/Edit/Setup/SetupScreenHeader.cs | 2 +- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 8dd6eac7bb..01e1af06c0 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -1,12 +1,9 @@ // 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 System.Diagnostics; using System.Linq; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -23,11 +20,35 @@ namespace osu.Game.Graphics.Containers public partial class SectionsContainer : Container where T : Drawable { - public Bindable SelectedSection { get; } = new Bindable(); + public Bindable SelectedSection { get; } = new Bindable(); - private T lastClickedSection; + private T? lastClickedSection; - public Drawable ExpandableHeader + protected override Container Content => scrollContentContainer; + + private readonly UserTrackingScrollContainer scrollContainer; + private readonly Container headerBackgroundContainer; + private readonly MarginPadding originalSectionsMargin; + + private Drawable? fixedHeader; + + private Drawable? footer; + private Drawable? headerBackground; + + private FlowContainer scrollContentContainer = null!; + + private float? headerHeight, footerHeight; + + private float? lastKnownScroll; + + /// + /// The percentage of the container to consider the centre-point for deciding the active section (and scrolling to a requested section). + /// + private const float scroll_y_centre = 0.1f; + + private Drawable? expandableHeader; + + public Drawable? ExpandableHeader { get => expandableHeader; set @@ -42,11 +63,12 @@ namespace osu.Game.Graphics.Containers if (value == null) return; AddInternal(expandableHeader); + lastKnownScroll = null; } } - public Drawable FixedHeader + public Drawable? FixedHeader { get => fixedHeader; set @@ -63,7 +85,7 @@ namespace osu.Game.Graphics.Containers } } - public Drawable Footer + public Drawable? Footer { get => footer; set @@ -75,16 +97,17 @@ namespace osu.Game.Graphics.Containers footer = value; - if (value == null) return; + if (footer == null) return; footer.Anchor |= Anchor.y2; footer.Origin |= Anchor.y2; + scrollContainer.Add(footer); lastKnownScroll = null; } } - public Drawable HeaderBackground + public Drawable? HeaderBackground { get => headerBackground; set @@ -102,23 +125,6 @@ namespace osu.Game.Graphics.Containers } } - protected override Container Content => scrollContentContainer; - - private readonly UserTrackingScrollContainer scrollContainer; - private readonly Container headerBackgroundContainer; - private readonly MarginPadding originalSectionsMargin; - private Drawable expandableHeader, fixedHeader, footer, headerBackground; - private FlowContainer scrollContentContainer; - - private float? headerHeight, footerHeight; - - private float? lastKnownScroll; - - /// - /// The percentage of the container to consider the centre-point for deciding the active section (and scrolling to a requested section). - /// - private const float scroll_y_centre = 0.1f; - public SectionsContainer() { AddRangeInternal(new Drawable[] @@ -171,10 +177,8 @@ namespace osu.Game.Graphics.Containers public void ScrollToTop() => scrollContainer.ScrollTo(0); - [NotNull] protected virtual UserTrackingScrollContainer CreateScrollContainer() => new UserTrackingScrollContainer(); - [NotNull] protected virtual FlowContainer CreateScrollContentContainer() => new FillFlowContainer { diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index ab4f07b982..0ab842c907 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -120,7 +120,7 @@ namespace osu.Game.Overlays if (lastSection != section.NewValue) { lastSection = section.NewValue; - tabs.Current.Value = lastSection; + tabs.Current.Value = lastSection!; } }; diff --git a/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs b/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs index 1d66830adf..788beba9d9 100644 --- a/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs +++ b/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs @@ -65,7 +65,7 @@ namespace osu.Game.Screens.Edit.Setup { base.LoadComplete(); - sections.SelectedSection.BindValueChanged(section => tabControl.Current.Value = section.NewValue); + sections.SelectedSection.BindValueChanged(section => tabControl.Current.Value = section.NewValue!); tabControl.Current.BindValueChanged(section => { if (section.NewValue != sections.SelectedSection.Value) From 67e952150f8670888389aba1ec96e3b8e0fb9981 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jun 2023 15:24:12 +0900 Subject: [PATCH 073/169] Fix scroll operations in `SectionsContainer` failing if target moves due to loaded content --- .../Graphics/Containers/SectionsContainer.cs | 48 ++++++++++++++++--- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 01e1af06c0..27ff6b851d 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -9,6 +9,8 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Layout; +using osu.Framework.Logging; +using osu.Framework.Threading; using osu.Framework.Utils; namespace osu.Game.Graphics.Containers @@ -156,23 +158,57 @@ namespace osu.Game.Graphics.Containers footerHeight = null; } + private ScheduledDelegate? scrollToTargetDelegate; + public void ScrollTo(Drawable target) { + Logger.Log($"Scrolling to {target}.."); + lastKnownScroll = null; - // implementation similar to ScrollIntoView but a bit more nuanced. - float top = scrollContainer.GetChildPosInContent(target); + float scrollTarget = getScrollTargetForDrawable(target); - float bottomScrollExtent = scrollContainer.ScrollableExtent; - float scrollTarget = top - scrollContainer.DisplayableContent * scroll_y_centre; - - if (scrollTarget > bottomScrollExtent) + if (scrollTarget > scrollContainer.ScrollableExtent) scrollContainer.ScrollToEnd(); else scrollContainer.ScrollTo(scrollTarget); if (target is T section) lastClickedSection = section; + + // Content may load in as a scroll occurs, changing the scroll target we need to aim for. + // This scheduled operation ensures that we keep trying until actually arriving at the target. + scrollToTargetDelegate?.Cancel(); + scrollToTargetDelegate = Scheduler.AddDelayed(() => + { + if (scrollContainer.UserScrolling) + { + Logger.Log("Scroll operation interrupted by user scroll"); + scrollToTargetDelegate?.Cancel(); + scrollToTargetDelegate = null; + return; + } + + if (Precision.AlmostEquals(scrollContainer.Current, scrollTarget, 1)) + { + Logger.Log($"Finished scrolling to {target}!"); + scrollToTargetDelegate?.Cancel(); + scrollToTargetDelegate = null; + return; + } + + if (!Precision.AlmostEquals(getScrollTargetForDrawable(target), scrollTarget, 1)) + { + Logger.Log($"Reattempting scroll to {target} due to change in position"); + ScrollTo(target); + } + }, 50, true); + } + + private float getScrollTargetForDrawable(Drawable target) + { + // implementation similar to ScrollIntoView but a bit more nuanced. + return scrollContainer.GetChildPosInContent(target) - scrollContainer.DisplayableContent * scroll_y_centre; } public void ScrollToTop() => scrollContainer.ScrollTo(0); From 757596fffa5eb98b09dcdaad6245297843b8d50d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jun 2023 15:48:04 +0900 Subject: [PATCH 074/169] Add test coverage of scroll failing --- .../TestSceneSectionsContainer.cs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs index 05fffc903d..3a1eb554ab 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs @@ -80,6 +80,24 @@ namespace osu.Game.Tests.Visual.UserInterface }); } + [Test] + public void TestCorrectScrollToWhenContentLoads() + { + AddRepeatStep("add many sections", () => append(1f), 3); + + AddStep("add section with delayed load content", () => + { + container.Add(new TestDelayedLoadSection("delayed")); + }); + + AddStep("add final section", () => append(0.5f)); + + AddStep("scroll to final section", () => container.ScrollTo(container.Children.Last())); + + AddUntilStep("correct section selected", () => container.SelectedSection.Value == container.Children.Last()); + AddUntilStep("wait for scroll to section", () => container.ScreenSpaceDrawQuad.AABBFloat.Contains(container.Children.Last().ScreenSpaceDrawQuad.AABBFloat)); + } + [Test] public void TestSelection() { @@ -196,6 +214,33 @@ namespace osu.Game.Tests.Visual.UserInterface InputManager.ScrollVerticalBy(direction); } + private partial class TestDelayedLoadSection : TestSection + { + public TestDelayedLoadSection(string label) + : base(label) + { + BackgroundColour = default_colour; + Width = 300; + AutoSizeAxes = Axes.Y; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Box box; + + Add(box = new Box + { + Alpha = 0.01f, + RelativeSizeAxes = Axes.X, + }); + + // Emulate an operation that will be inhibited by IsMaskedAway. + box.ResizeHeightTo(2000, 50); + } + } + private partial class TestSection : TestBox { public bool Selected From 2c89af608ac5c136f81ed90a97ccf702a4f0c021 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jun 2023 17:15:13 +0900 Subject: [PATCH 075/169] Add ability to cycle beat snap divisor using hotkeys Defaults to Ctrl+Shift+Wheel (as per stable). Closes #23785. --- .../Input/Bindings/GlobalActionContainer.cs | 10 +++++- .../GlobalActionKeyBindingStrings.cs | 10 ++++++ .../Compose/Components/BeatDivisorControl.cs | 34 ++++++++++++++++++- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index d580eea248..e84663db5e 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -101,6 +101,8 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Control, InputKey.J }, GlobalAction.EditorFlipVertically), new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.MouseWheelDown }, GlobalAction.EditorDecreaseDistanceSpacing), new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.MouseWheelUp }, GlobalAction.EditorIncreaseDistanceSpacing), + new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.MouseWheelDown }, GlobalAction.EditorCyclePreviousBeatSnapDivisor), + new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.MouseWheelUp }, GlobalAction.EditorCycleNextBeatSnapDivisor), }; public IEnumerable InGameKeyBindings => new[] @@ -355,6 +357,12 @@ namespace osu.Game.Input.Bindings ToggleProfile, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorCloneSelection))] - EditorCloneSelection + EditorCloneSelection, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorCyclePreviousBeatSnapDivisor))] + EditorCyclePreviousBeatSnapDivisor, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorCycleNextBeatSnapDivisor))] + EditorCycleNextBeatSnapDivisor, } } diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index 303dbb6f46..423fea6793 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -279,6 +279,16 @@ namespace osu.Game.Localisation /// public static LocalisableString EditorDecreaseDistanceSpacing => new TranslatableString(getKey(@"editor_decrease_distance_spacing"), @"Decrease distance spacing"); + /// + /// "Cycle previous beat snap divisor" + /// + public static LocalisableString EditorCyclePreviousBeatSnapDivisor => new TranslatableString(getKey(@"editor_decrease_distance_spacing"), @"Cycle previous beat snap divisor"); + + /// + /// "Cycle next beat snap divisor" + /// + public static LocalisableString EditorCycleNextBeatSnapDivisor => new TranslatableString(getKey(@"editor_increase_distance_spacing"), @"Cycle next beat snap divisor"); + /// /// "Toggle skin editor" /// diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 432c5ea280..e96039ce16 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -16,12 +16,14 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Input.Bindings; using osu.Game.Overlays; using osuTK; using osuTK.Graphics; @@ -29,7 +31,7 @@ using osuTK.Input; namespace osu.Game.Screens.Edit.Compose.Components { - public partial class BeatDivisorControl : CompositeDrawable + public partial class BeatDivisorControl : CompositeDrawable, IKeyBindingHandler { private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); @@ -220,6 +222,36 @@ namespace osu.Game.Screens.Edit.Compose.Components return base.OnKeyDown(e); } + public virtual bool OnPressed(KeyBindingPressEvent e) + { + switch (e.Action) + { + case GlobalAction.EditorCycleNextBeatSnapDivisor: + cycle(1); + return true; + + case GlobalAction.EditorCyclePreviousBeatSnapDivisor: + cycle(-1); + return true; + } + + return false; + } + + private void cycle(int direction) + { + var presets = beatDivisor.ValidDivisors.Value.Presets; + + int selectedIndex = presets.Count(e => e < beatDivisor.Value); + int newIndex = Math.Clamp(selectedIndex + direction, 0, presets.Count - 1); + + beatDivisor.Value = presets[newIndex]; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + internal partial class DivisorDisplay : OsuAnimatedButton, IHasPopover { public BindableBeatDivisor BeatDivisor { get; } = new BindableBeatDivisor(); From 15725fb18611925b70ed82396675bd419b848d70 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jun 2023 18:07:56 +0900 Subject: [PATCH 076/169] Change default bindings to account for mouse wheel rotation --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index e84663db5e..fdd96d3890 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -101,8 +101,10 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Control, InputKey.J }, GlobalAction.EditorFlipVertically), new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.MouseWheelDown }, GlobalAction.EditorDecreaseDistanceSpacing), new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.MouseWheelUp }, GlobalAction.EditorIncreaseDistanceSpacing), - new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.MouseWheelDown }, GlobalAction.EditorCyclePreviousBeatSnapDivisor), - new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.MouseWheelUp }, GlobalAction.EditorCycleNextBeatSnapDivisor), + // Framework automatically converts wheel up/down to left/right when shift is held. + // See https://github.com/ppy/osu-framework/blob/master/osu.Framework/Input/StateChanges/MouseScrollRelativeInput.cs#L37-L38. + new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.MouseWheelRight }, GlobalAction.EditorCyclePreviousBeatSnapDivisor), + new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.MouseWheelLeft }, GlobalAction.EditorCycleNextBeatSnapDivisor), }; public IEnumerable InGameKeyBindings => new[] From f8a3be24c8f1923671da7159b05a0e88b735d85e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jun 2023 00:22:06 +0900 Subject: [PATCH 077/169] Update config.yml to mention "performance" in the discussion link --- .github/ISSUE_TEMPLATE/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 47a6a4c3d3..ec57232126 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,7 +2,7 @@ blank_issues_enabled: false contact_links: - name: Help url: https://github.com/ppy/osu/discussions/categories/q-a - about: osu! not working as you'd expect? Not sure it's a bug? Check the Q&A section! + about: osu! not working or performing as you'd expect? Not sure it's a bug? Check the Q&A section! - name: Suggestions or feature request url: https://github.com/ppy/osu/discussions/categories/ideas about: Got something you think should change or be added? Search for or start a new discussion! From 3a014987895681911b6a7ee8a8f3a3bf2e96e4f6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jun 2023 00:53:58 +0900 Subject: [PATCH 078/169] Use existing next/previous methods (and remove looping behaviour) --- .../Visual/Editing/TestSceneBeatDivisorControl.cs | 11 +++++++---- osu.Game/Screens/Edit/BindableBeatDivisor.cs | 10 ++++++---- .../Edit/Compose/Components/BeatDivisorControl.cs | 12 ++++++------ 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs index 353acfa4ba..59fa331abe 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs @@ -51,9 +51,9 @@ namespace osu.Game.Tests.Visual.Editing [Test] public void TestBindableBeatDivisor() { - AddRepeatStep("move previous", () => bindableBeatDivisor.Previous(), 2); + AddRepeatStep("move previous", () => bindableBeatDivisor.SelectPrevious(), 2); AddAssert("divisor is 4", () => bindableBeatDivisor.Value == 4); - AddRepeatStep("move next", () => bindableBeatDivisor.Next(), 1); + AddRepeatStep("move next", () => bindableBeatDivisor.SelectNext(), 1); AddAssert("divisor is 12", () => bindableBeatDivisor.Value == 8); } @@ -101,6 +101,9 @@ namespace osu.Game.Tests.Visual.Editing public void TestBeatChevronNavigation() { switchBeatSnap(1); + assertBeatSnap(16); + + switchBeatSnap(-4); assertBeatSnap(1); switchBeatSnap(3); @@ -110,7 +113,7 @@ namespace osu.Game.Tests.Visual.Editing assertBeatSnap(4); switchBeatSnap(-3); - assertBeatSnap(16); + assertBeatSnap(1); } [Test] @@ -207,7 +210,7 @@ namespace osu.Game.Tests.Visual.Editing }, Math.Abs(direction)); private void assertBeatSnap(int expected) => AddAssert($"beat snap is {expected}", - () => bindableBeatDivisor.Value == expected); + () => bindableBeatDivisor.Value, () => Is.EqualTo(expected)); private void switchPresets(int direction) => AddRepeatStep($"move presets {(direction > 0 ? "forward" : "backward")}", () => { diff --git a/osu.Game/Screens/Edit/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/BindableBeatDivisor.cs index 1da224d850..ffa4f01e75 100644 --- a/osu.Game/Screens/Edit/BindableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/BindableBeatDivisor.cs @@ -59,16 +59,18 @@ namespace osu.Game.Screens.Edit Value = 1; } - public void Next() + public void SelectNext() { var presets = ValidDivisors.Value.Presets; - Value = presets.Cast().SkipWhile(preset => preset != Value).ElementAtOrDefault(1) ?? presets[0]; + if (presets.Cast().SkipWhile(preset => preset != Value).ElementAtOrDefault(1) is int newValue) + Value = newValue; } - public void Previous() + public void SelectPrevious() { var presets = ValidDivisors.Value.Presets; - Value = presets.Cast().TakeWhile(preset => preset != Value).LastOrDefault() ?? presets[^1]; + if (presets.Cast().TakeWhile(preset => preset != Value).LastOrDefault() is int newValue) + Value = newValue; } protected override int DefaultPrecision => 1; diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index e96039ce16..9b47990c57 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -103,13 +103,13 @@ namespace osu.Game.Screens.Edit.Compose.Components new ChevronButton { Icon = FontAwesome.Solid.ChevronLeft, - Action = beatDivisor.Previous + Action = beatDivisor.SelectPrevious }, new DivisorDisplay { BeatDivisor = { BindTarget = beatDivisor } }, new ChevronButton { Icon = FontAwesome.Solid.ChevronRight, - Action = beatDivisor.Next + Action = beatDivisor.SelectNext } }, }, @@ -227,11 +227,11 @@ namespace osu.Game.Screens.Edit.Compose.Components switch (e.Action) { case GlobalAction.EditorCycleNextBeatSnapDivisor: - cycle(1); + beatDivisor.SelectNext(); return true; case GlobalAction.EditorCyclePreviousBeatSnapDivisor: - cycle(-1); + beatDivisor.SelectPrevious(); return true; } @@ -474,12 +474,12 @@ namespace osu.Game.Screens.Edit.Compose.Components switch (e.Key) { case Key.Right: - beatDivisor.Next(); + beatDivisor.SelectNext(); OnUserChange(Current.Value); return true; case Key.Left: - beatDivisor.Previous(); + beatDivisor.SelectPrevious(); OnUserChange(Current.Value); return true; From 8463f5c7ddab3867818996d22f3a86fd2e08eb10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 7 Jun 2023 21:50:16 +0200 Subject: [PATCH 079/169] Spell out location of external proxy explicitly --- osu.Game.Rulesets.Mania/UI/Column.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 659b63b640..f38571a6d3 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -90,7 +90,8 @@ namespace osu.Game.Rulesets.Mania.UI { RelativeSizeAxes = Axes.Both, }, - // For input purposes, the background is added at the highest depth, but is then proxied back below all other elements externally. + // For input purposes, the background is added at the highest depth, but is then proxied back below all other elements externally + // (see `Stage.columnBackgrounds`). BackgroundContainer, TopLevelContainer, new ColumnTouchInputArea(this) From c8507837d156c3b6f30506e1382252d215ef484a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 7 Jun 2023 21:51:35 +0200 Subject: [PATCH 080/169] Remove redundant initialisation of `Children` to empty array --- osu.Game.Rulesets.Mania/UI/Stage.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Stage.cs b/osu.Game.Rulesets.Mania/UI/Stage.cs index 215d81b64a..879c704450 100644 --- a/osu.Game.Rulesets.Mania/UI/Stage.cs +++ b/osu.Game.Rulesets.Mania/UI/Stage.cs @@ -82,9 +82,6 @@ namespace osu.Game.Rulesets.Mania.UI { Name = "Column backgrounds", RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - } }, new Container { From d9281ac8c75475fb79ff70efc76f0fc6a5924bfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 7 Jun 2023 23:28:27 +0200 Subject: [PATCH 081/169] Remove unused `virtual` spec --- osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 9b47990c57..cbe4830e40 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -222,7 +222,7 @@ namespace osu.Game.Screens.Edit.Compose.Components return base.OnKeyDown(e); } - public virtual bool OnPressed(KeyBindingPressEvent e) + public bool OnPressed(KeyBindingPressEvent e) { switch (e.Action) { From 3c8f387a6c9ba41bdf9c87431df33d94626142cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 7 Jun 2023 23:28:36 +0200 Subject: [PATCH 082/169] Remove unused method --- .../Edit/Compose/Components/BeatDivisorControl.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index cbe4830e40..5a1fbbee1e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -238,16 +238,6 @@ namespace osu.Game.Screens.Edit.Compose.Components return false; } - private void cycle(int direction) - { - var presets = beatDivisor.ValidDivisors.Value.Presets; - - int selectedIndex = presets.Count(e => e < beatDivisor.Value); - int newIndex = Math.Clamp(selectedIndex + direction, 0, presets.Count - 1); - - beatDivisor.Value = presets[newIndex]; - } - public void OnReleased(KeyBindingReleaseEvent e) { } From 35e41d816ae6750f36555d86f0c7c99119c308cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 7 Jun 2023 23:31:46 +0200 Subject: [PATCH 083/169] Cover clamping to max beat divisor in test --- osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs index 59fa331abe..c7b6d984ed 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs @@ -109,7 +109,10 @@ namespace osu.Game.Tests.Visual.Editing switchBeatSnap(3); assertBeatSnap(8); - switchBeatSnap(-1); + switchBeatSnap(3); + assertBeatSnap(16); + + switchBeatSnap(-2); assertBeatSnap(4); switchBeatSnap(-3); From f416b3a226844e951bbd6b75e7a488927b0a4cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 7 Jun 2023 23:43:21 +0200 Subject: [PATCH 084/169] Fix new localisations using lookup keys of existing ones --- osu.Game/Localisation/GlobalActionKeyBindingStrings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index 423fea6793..aa608a603b 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -282,12 +282,12 @@ namespace osu.Game.Localisation /// /// "Cycle previous beat snap divisor" /// - public static LocalisableString EditorCyclePreviousBeatSnapDivisor => new TranslatableString(getKey(@"editor_decrease_distance_spacing"), @"Cycle previous beat snap divisor"); + public static LocalisableString EditorCyclePreviousBeatSnapDivisor => new TranslatableString(getKey(@"editor_cycle_previous_beat_snap_divisor"), @"Cycle previous beat snap divisor"); /// /// "Cycle next beat snap divisor" /// - public static LocalisableString EditorCycleNextBeatSnapDivisor => new TranslatableString(getKey(@"editor_increase_distance_spacing"), @"Cycle next beat snap divisor"); + public static LocalisableString EditorCycleNextBeatSnapDivisor => new TranslatableString(getKey(@"editor_cycle_next_snap_divisor"), @"Cycle next beat snap divisor"); /// /// "Toggle skin editor" From 1a90f71540fc4a0bab3ef257cd8b0a6690d0a2eb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 8 Jun 2023 02:47:07 +0300 Subject: [PATCH 085/169] Centralise game language update logic --- osu.Game/OsuGameBase.cs | 25 +++++++++++++- .../Overlays/FirstRunSetup/ScreenWelcome.cs | 33 +++++++------------ .../Sections/General/LanguageSettings.cs | 22 ++----------- 3 files changed, 39 insertions(+), 41 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index c55b6c249f..ed954f609e 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -14,6 +14,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; +using osu.Framework.Configuration; using osu.Framework.Development; using osu.Framework.Extensions; using osu.Framework.Graphics; @@ -27,6 +28,7 @@ using osu.Framework.Input.Handlers.Mouse; using osu.Framework.Input.Handlers.Tablet; using osu.Framework.Input.Handlers.Touch; using osu.Framework.IO.Stores; +using osu.Framework.Localisation; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Timing; @@ -36,11 +38,13 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Formats; using osu.Game.Configuration; using osu.Game.Database; +using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Cursor; using osu.Game.Input; using osu.Game.Input.Bindings; using osu.Game.IO; +using osu.Game.Localisation; using osu.Game.Online; using osu.Game.Online.API; using osu.Game.Online.Chat; @@ -157,6 +161,11 @@ namespace osu.Game protected Storage Storage { get; set; } + /// + /// The language in which the game is currently displayed in. + /// + public Bindable CurrentLanguage { get; } = new Bindable(); + protected Bindable Beatmap { get; private set; } // cached via load() method /// @@ -216,6 +225,10 @@ namespace osu.Game private readonly BindableNumber globalTrackVolumeAdjust = new BindableNumber(global_track_volume_adjust); + private Bindable frameworkLocale = null!; + + private IBindable localisationParameters = null!; + /// /// Number of unhandled exceptions to allow before aborting execution. /// @@ -238,7 +251,7 @@ namespace osu.Game } [BackgroundDependencyLoader] - private void load(ReadableKeyCombinationProvider keyCombinationProvider) + private void load(ReadableKeyCombinationProvider keyCombinationProvider, FrameworkConfigManager frameworkConfig) { try { @@ -284,6 +297,14 @@ namespace osu.Game MessageFormatter.WebsiteRootUrl = endpoints.WebsiteRootUrl; dependencies.CacheAs(API ??= new APIAccess(LocalConfig, endpoints, VersionHash)); + frameworkLocale = frameworkConfig.GetBindable(FrameworkSetting.Locale); + frameworkLocale.BindValueChanged(_ => updateLanguage()); + + localisationParameters = Localisation.CurrentParameters.GetBoundCopy(); + localisationParameters.BindValueChanged(_ => updateLanguage(), true); + + CurrentLanguage.BindValueChanged(val => frameworkLocale.Value = val.NewValue.ToCultureCode()); + var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures); @@ -394,6 +415,8 @@ namespace osu.Game Beatmap.BindValueChanged(onBeatmapChanged); } + private void updateLanguage() => CurrentLanguage.Value = LanguageExtensions.GetLanguageFor(frameworkLocale.Value, localisationParameters.Value); + private void addFilesWarning() { var realmStore = new RealmFileStore(realm, Storage); diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs index b8d802ad4b..68c6c78986 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs @@ -13,7 +13,6 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Framework.Threading; -using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -68,13 +67,12 @@ namespace osu.Game.Overlays.FirstRunSetup private partial class LanguageSelectionFlow : FillFlowContainer { - private Bindable frameworkLocale = null!; - private IBindable localisationParameters = null!; + private Bindable language = null!; private ScheduledDelegate? updateSelectedDelegate; [BackgroundDependencyLoader] - private void load(FrameworkConfigManager frameworkConfig, LocalisationManager localisation) + private void load(OsuGameBase game) { Direction = FillDirection.Full; Spacing = new Vector2(5); @@ -82,25 +80,18 @@ namespace osu.Game.Overlays.FirstRunSetup ChildrenEnumerable = Enum.GetValues() .Select(l => new LanguageButton(l) { - Action = () => frameworkLocale.Value = l.ToCultureCode() + Action = () => language.Value = l, }); - frameworkLocale = frameworkConfig.GetBindable(FrameworkSetting.Locale); - frameworkLocale.BindValueChanged(_ => onLanguageChange()); - - localisationParameters = localisation.CurrentParameters.GetBoundCopy(); - localisationParameters.BindValueChanged(_ => onLanguageChange(), true); - } - - private void onLanguageChange() - { - var language = LanguageExtensions.GetLanguageFor(frameworkLocale.Value, localisationParameters.Value); - - // Changing language may cause a short period of blocking the UI thread while the new glyphs are loaded. - // Scheduling ensures the button animation plays smoothly after any blocking operation completes. - // Note that a delay is required (the alternative would be a double-schedule; delay feels better). - updateSelectedDelegate?.Cancel(); - updateSelectedDelegate = Scheduler.AddDelayed(() => updateSelectedStates(language), 50); + language = game.CurrentLanguage.GetBoundCopy(); + language.BindValueChanged(v => + { + // Changing language may cause a short period of blocking the UI thread while the new glyphs are loaded. + // Scheduling ensures the button animation plays smoothly after any blocking operation completes. + // Note that a delay is required (the alternative would be a double-schedule; delay feels better). + updateSelectedDelegate?.Cancel(); + updateSelectedDelegate = Scheduler.AddDelayed(() => updateSelectedStates(v.NewValue), 50); + }, true); } private void updateSelectedStates(Language language) diff --git a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs index 982cbec376..d3b657b5be 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs @@ -2,35 +2,27 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Configuration; -using osu.Game.Extensions; using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.General { public partial class LanguageSettings : SettingsSubsection { - private SettingsDropdown languageSelection = null!; - private Bindable frameworkLocale = null!; - private IBindable localisationParameters = null!; - protected override LocalisableString Header => GeneralSettingsStrings.LanguageHeader; [BackgroundDependencyLoader] - private void load(FrameworkConfigManager frameworkConfig, OsuConfigManager config, LocalisationManager localisation) + private void load(OsuGame game, OsuConfigManager config, FrameworkConfigManager frameworkConfig) { - frameworkLocale = frameworkConfig.GetBindable(FrameworkSetting.Locale); - localisationParameters = localisation.CurrentParameters.GetBoundCopy(); - Children = new Drawable[] { - languageSelection = new SettingsEnumDropdown + new SettingsEnumDropdown { LabelText = GeneralSettingsStrings.LanguageDropdown, + Current = game.CurrentLanguage, }, new SettingsCheckbox { @@ -43,14 +35,6 @@ namespace osu.Game.Overlays.Settings.Sections.General Current = config.GetBindable(OsuSetting.Prefer24HourTime) }, }; - - frameworkLocale.BindValueChanged(_ => updateSelection()); - localisationParameters.BindValueChanged(_ => updateSelection(), true); - - languageSelection.Current.BindValueChanged(val => frameworkLocale.Value = val.NewValue.ToCultureCode()); } - - private void updateSelection() => - languageSelection.Current.Value = LanguageExtensions.GetLanguageFor(frameworkLocale.Value, localisationParameters.Value); } } From ab790ec96a30c296d6ac255b990c7736c27557ee Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 8 Jun 2023 02:50:14 +0300 Subject: [PATCH 086/169] Expose currently selected language from `IAPIProvider` --- osu.Game/Online/API/APIAccess.cs | 7 ++++++- osu.Game/Online/API/DummyAPIAccess.cs | 3 +++ osu.Game/Online/API/IAPIProvider.cs | 6 ++++++ osu.Game/OsuGameBase.cs | 2 +- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 94bb77d6ec..4f586c8fff 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -18,6 +18,7 @@ using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Game.Configuration; +using osu.Game.Localisation; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Notifications; @@ -28,6 +29,7 @@ namespace osu.Game.Online.API { public partial class APIAccess : Component, IAPIProvider { + private readonly OsuGameBase game; private readonly OsuConfigManager config; private readonly string versionHash; @@ -52,6 +54,8 @@ namespace osu.Game.Online.API public IBindableList Friends => friends; public IBindable Activity => activity; + public Language Language => game.CurrentLanguage.Value; + private Bindable localUser { get; } = new Bindable(createGuestUser()); private BindableList friends { get; } = new BindableList(); @@ -64,8 +68,9 @@ namespace osu.Game.Online.API private readonly Logger log; - public APIAccess(OsuConfigManager config, EndpointConfiguration endpointConfiguration, string versionHash) + public APIAccess(OsuGameBase game, OsuConfigManager config, EndpointConfiguration endpointConfiguration, string versionHash) { + this.game = game; this.config = config; this.versionHash = versionHash; diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index abe2755654..16afef8e30 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Notifications; using osu.Game.Tests; @@ -29,6 +30,8 @@ namespace osu.Game.Online.API public Bindable Activity { get; } = new Bindable(); + public Language Language => Language.en; + public string AccessToken => "token"; public bool IsLoggedIn => State.Value == APIState.Online; diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs index 6054effaa1..a1d7006c8c 100644 --- a/osu.Game/Online/API/IAPIProvider.cs +++ b/osu.Game/Online/API/IAPIProvider.cs @@ -4,6 +4,7 @@ using System; using System.Threading.Tasks; using osu.Framework.Bindables; +using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Notifications; using osu.Game.Users; @@ -27,6 +28,11 @@ namespace osu.Game.Online.API /// IBindable Activity { get; } + /// + /// The language supplied by this provider to API requests. + /// + Language Language { get; } + /// /// Retrieve the OAuth access token. /// diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index ed954f609e..63efe0e2c8 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -296,7 +296,6 @@ namespace osu.Game MessageFormatter.WebsiteRootUrl = endpoints.WebsiteRootUrl; - dependencies.CacheAs(API ??= new APIAccess(LocalConfig, endpoints, VersionHash)); frameworkLocale = frameworkConfig.GetBindable(FrameworkSetting.Locale); frameworkLocale.BindValueChanged(_ => updateLanguage()); @@ -305,6 +304,7 @@ namespace osu.Game CurrentLanguage.BindValueChanged(val => frameworkLocale.Value = val.NewValue.ToCultureCode()); + dependencies.CacheAs(API ??= new APIAccess(this, LocalConfig, endpoints, VersionHash)); var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures); From 9f842ccdc057e6c6ec60def1ac607f1ea00f02e1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 8 Jun 2023 02:50:29 +0300 Subject: [PATCH 087/169] Supply `Accept-Language` header in API requests --- osu.Game/Online/API/APIRequest.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index dc6a3fe3d5..cd6e8df754 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -9,6 +9,7 @@ using JetBrains.Annotations; using Newtonsoft.Json; using osu.Framework.IO.Network; using osu.Framework.Logging; +using osu.Game.Extensions; using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Online.API @@ -116,10 +117,11 @@ namespace osu.Game.Online.API WebRequest.Failed += Fail; WebRequest.AllowRetryOnTimeout = false; - WebRequest.AddHeader("x-api-version", API.APIVersion.ToString(CultureInfo.InvariantCulture)); + WebRequest.AddHeader(@"Accept-Language", API.Language.ToCultureCode()); + WebRequest.AddHeader(@"x-api-version", API.APIVersion.ToString(CultureInfo.InvariantCulture)); if (!string.IsNullOrEmpty(API.AccessToken)) - WebRequest.AddHeader("Authorization", $"Bearer {API.AccessToken}"); + WebRequest.AddHeader(@"Authorization", $@"Bearer {API.AccessToken}"); if (isFailing) return; From 251f23b2c2e85c70d08803c911ec3e4fe4cb0791 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 8 Jun 2023 03:27:03 +0300 Subject: [PATCH 088/169] Handle culture code differences of Traditional Chinese --- osu.Game/Extensions/LanguageExtensions.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/osu.Game/Extensions/LanguageExtensions.cs b/osu.Game/Extensions/LanguageExtensions.cs index 04231c384c..44932cf3c8 100644 --- a/osu.Game/Extensions/LanguageExtensions.cs +++ b/osu.Game/Extensions/LanguageExtensions.cs @@ -21,7 +21,12 @@ namespace osu.Game.Extensions /// This is required as enum member names are not allowed to contain hyphens. /// public static string ToCultureCode(this Language language) - => language.ToString().Replace("_", "-"); + { + if (language == Language.zh_hant) + return @"zh-tw"; + + return language.ToString().Replace("_", "-"); + } /// /// Attempts to parse the supplied to a value. @@ -30,7 +35,15 @@ namespace osu.Game.Extensions /// The parsed . Valid only if the return value of the method is . /// Whether the parsing succeeded. public static bool TryParseCultureCode(string cultureCode, out Language language) - => Enum.TryParse(cultureCode.Replace("-", "_"), out language); + { + if (cultureCode == @"zh-tw") + { + language = Language.zh_hant; + return true; + } + + return Enum.TryParse(cultureCode.Replace("-", "_"), out language); + } /// /// Parses the that is specified in , From d523785bfc34192a695ac0b8136272fe50b05ad8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 8 Jun 2023 03:27:23 +0300 Subject: [PATCH 089/169] Fix `LanguageSettings` requiring `OsuGame` as dependency --- osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs index d3b657b5be..cf7f63211e 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs @@ -15,7 +15,7 @@ namespace osu.Game.Overlays.Settings.Sections.General protected override LocalisableString Header => GeneralSettingsStrings.LanguageHeader; [BackgroundDependencyLoader] - private void load(OsuGame game, OsuConfigManager config, FrameworkConfigManager frameworkConfig) + private void load(OsuGameBase game, OsuConfigManager config, FrameworkConfigManager frameworkConfig) { Children = new Drawable[] { From a842f79ad49cc98afaef897c85f2f5657ca32029 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jun 2023 16:17:44 +0900 Subject: [PATCH 090/169] Refactor `IWorkingBeatmap.Background` to `GetBackground()` --- osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs | 2 +- osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs | 2 +- .../Visual/Background/TestSceneBackgroundScreenDefault.cs | 2 +- osu.Game.Tests/WaveformTestBeatmap.cs | 2 +- osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs | 5 +++-- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 2 +- osu.Game/Beatmaps/FlatFileWorkingBeatmap.cs | 2 +- osu.Game/Beatmaps/IWorkingBeatmap.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 4 +--- osu.Game/Beatmaps/WorkingBeatmapCache.cs | 2 +- osu.Game/Graphics/Backgrounds/BeatmapBackground.cs | 2 +- osu.Game/Overlays/NowPlayingOverlay.cs | 2 +- osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs | 2 +- osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs | 2 +- .../Screens/OnlinePlay/Components/PlaylistItemBackground.cs | 2 +- osu.Game/Screens/Play/BeatmapMetadataDisplay.cs | 2 +- osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs | 2 +- osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs | 2 +- osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs | 2 +- 19 files changed, 21 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index 09130ac57d..fac5e098b9 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -231,7 +231,7 @@ namespace osu.Game.Tests.Beatmaps.Formats protected override IBeatmap GetBeatmap() => beatmap; - protected override Texture GetBackground() => throw new NotImplementedException(); + public override Texture GetBackground() => throw new NotImplementedException(); protected override Track GetBeatmapTrack() => throw new NotImplementedException(); diff --git a/osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs b/osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs index 295a10ba5b..3092966a7e 100644 --- a/osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs @@ -131,7 +131,7 @@ namespace osu.Game.Tests.Editing.Checks var mock = new Mock(); mock.SetupGet(w => w.Beatmap).Returns(beatmap); - mock.SetupGet(w => w.Background).Returns(background); + mock.SetupGet(w => w.GetBackground()).Returns(background); mock.Setup(w => w.GetStream(It.IsAny())).Returns(stream); return mock; diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs index 8f4250799e..1523ae7027 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs @@ -286,7 +286,7 @@ namespace osu.Game.Tests.Visual.Background this.renderer = renderer; } - protected override Texture GetBackground() => renderer.CreateTexture(1, 1); + public override Texture GetBackground() => renderer.CreateTexture(1, 1); } private partial class TestWorkingBeatmapWithStoryboard : TestWorkingBeatmap diff --git a/osu.Game.Tests/WaveformTestBeatmap.cs b/osu.Game.Tests/WaveformTestBeatmap.cs index 5e41392560..12660ed2e1 100644 --- a/osu.Game.Tests/WaveformTestBeatmap.cs +++ b/osu.Game.Tests/WaveformTestBeatmap.cs @@ -51,7 +51,7 @@ namespace osu.Game.Tests protected override IBeatmap GetBeatmap() => beatmap; - protected override Texture GetBackground() => null; + public override Texture GetBackground() => null; protected override Waveform GetWaveform() => new Waveform(trackStore.GetStream(firstAudioFile)); diff --git a/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs index 767504fcb1..5b9cf6846c 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs @@ -23,8 +23,9 @@ namespace osu.Game.Beatmaps.Drawables [BackgroundDependencyLoader] private void load() { - if (working.Background != null) - Texture = working.Background; + var background = working.GetBackground(); + if (background != null) + Texture = background; } } } diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 0b390a2ab5..8089d789c1 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -52,7 +52,7 @@ namespace osu.Game.Beatmaps protected override IBeatmap GetBeatmap() => new Beatmap(); - protected override Texture GetBackground() => textures?.Get(@"Backgrounds/bg4"); + public override Texture GetBackground() => textures?.Get(@"Backgrounds/bg4"); protected override Track GetBeatmapTrack() => GetVirtualTrack(); diff --git a/osu.Game/Beatmaps/FlatFileWorkingBeatmap.cs b/osu.Game/Beatmaps/FlatFileWorkingBeatmap.cs index 02fcde5257..0b53278ab3 100644 --- a/osu.Game/Beatmaps/FlatFileWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/FlatFileWorkingBeatmap.cs @@ -43,7 +43,7 @@ namespace osu.Game.Beatmaps } protected override IBeatmap GetBeatmap() => beatmap; - protected override Texture GetBackground() => throw new NotImplementedException(); + public override Texture GetBackground() => throw new NotImplementedException(); protected override Track GetBeatmapTrack() => throw new NotImplementedException(); protected internal override ISkin GetSkin() => throw new NotImplementedException(); public override Stream GetStream(string storagePath) => throw new NotImplementedException(); diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs index 0f0e72b0ac..4b0a498a56 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -47,7 +47,7 @@ namespace osu.Game.Beatmaps /// /// Retrieves the background for this . /// - Texture Background { get; } + Texture GetBackground(); /// /// Retrieves the for the of this . diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 59a71fd80c..a69859f724 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -34,8 +34,6 @@ namespace osu.Game.Beatmaps public Storyboard Storyboard => storyboard.Value; - public Texture Background => GetBackground(); // Texture uses ref counting, so we want to return a new instance every usage. - public ISkin Skin => skin.Value; private AudioManager audioManager { get; } @@ -67,7 +65,7 @@ namespace osu.Game.Beatmaps protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo }; protected abstract IBeatmap GetBeatmap(); - protected abstract Texture GetBackground(); + public abstract Texture GetBackground(); protected abstract Track GetBeatmapTrack(); /// diff --git a/osu.Game/Beatmaps/WorkingBeatmapCache.cs b/osu.Game/Beatmaps/WorkingBeatmapCache.cs index ef843909d8..94865ed8d0 100644 --- a/osu.Game/Beatmaps/WorkingBeatmapCache.cs +++ b/osu.Game/Beatmaps/WorkingBeatmapCache.cs @@ -160,7 +160,7 @@ namespace osu.Game.Beatmaps } } - protected override Texture GetBackground() + public override Texture GetBackground() { if (string.IsNullOrEmpty(Metadata?.BackgroundFile)) return null; diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackground.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackground.cs index b79eb4927f..3ace67f410 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackground.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackground.cs @@ -24,7 +24,7 @@ namespace osu.Game.Graphics.Backgrounds [BackgroundDependencyLoader] private void load(LargeTextureStore textures) { - Sprite.Texture = Beatmap?.Background ?? textures.Get(fallbackTextureName); + Sprite.Texture = Beatmap?.GetBackground() ?? textures.Get(fallbackTextureName); } public override bool Equals(Background other) diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index 66fb3571ba..e3e3b4bd80 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -415,7 +415,7 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load(LargeTextureStore textures) { - sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4"); + sprite.Texture = beatmap?.GetBackground() ?? textures.Get(@"Backgrounds/bg4"); } } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs index 23fa28e7bc..8c3a5c026d 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Edit.Checks if (backgroundFile == null) yield break; - var texture = context.WorkingBeatmap.Background; + var texture = context.WorkingBeatmap.GetBackground(); if (texture == null) yield break; diff --git a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs index 2cf823ca0c..565379f391 100644 --- a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs +++ b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs @@ -204,7 +204,7 @@ namespace osu.Game.Screens.Edit protected override IBeatmap GetBeatmap() => beatmap; - protected override Texture GetBackground() => throw new NotImplementedException(); + public override Texture GetBackground() => throw new NotImplementedException(); protected override Track GetBeatmapTrack() => throw new NotImplementedException(); diff --git a/osu.Game/Screens/OnlinePlay/Components/PlaylistItemBackground.cs b/osu.Game/Screens/OnlinePlay/Components/PlaylistItemBackground.cs index 997ba6b639..6b06eaee1e 100644 --- a/osu.Game/Screens/OnlinePlay/Components/PlaylistItemBackground.cs +++ b/osu.Game/Screens/OnlinePlay/Components/PlaylistItemBackground.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.OnlinePlay.Components if (Beatmap?.BeatmapSet is IBeatmapSetOnlineInfo online) texture = textures.Get(online.Covers.Cover); - Sprite.Texture = texture ?? beatmaps.DefaultBeatmap.Background; + Sprite.Texture = texture ?? beatmaps.DefaultBeatmap.GetBackground(); } public override bool Equals(Background? other) diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index a152f4be19..66aa3d9cc0 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -109,7 +109,7 @@ namespace osu.Game.Screens.Play new Sprite { RelativeSizeAxes = Axes.Both, - Texture = beatmap.Background, + Texture = beatmap.GetBackground(), Origin = Anchor.Centre, Anchor = Anchor.Centre, FillMode = FillMode.Fill, diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 4f37c215e9..82f116b4ae 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -226,7 +226,7 @@ namespace osu.Game.Screens.Play.HUD protected override IBeatmap GetBeatmap() => gameplayBeatmap; - protected override Texture GetBackground() => throw new NotImplementedException(); + public override Texture GetBackground() => throw new NotImplementedException(); protected override Track GetBeatmapTrack() => throw new NotImplementedException(); diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index 79f629ce49..b57b0daa1b 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -232,7 +232,7 @@ namespace osu.Game.Tests.Beatmaps protected override IBeatmap GetBeatmap() => beatmap; - protected override Texture GetBackground() => throw new NotImplementedException(); + public override Texture GetBackground() => throw new NotImplementedException(); protected override Track GetBeatmapTrack() => throw new NotImplementedException(); diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs index 7d2aa99dbe..ba6d9ca8b5 100644 --- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Beatmaps public override Stream? GetStream(string storagePath) => null; - protected override Texture? GetBackground() => null; + public override Texture? GetBackground() => null; protected override Track? GetBeatmapTrack() => null; } From 5162f5c3d8d18bee824fddf63ac65fa036d0723e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 8 Jun 2023 09:20:43 +0200 Subject: [PATCH 091/169] Fix code quality inspections --- osu.Game/Overlays/SettingsPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index d571557993..1681187f82 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -328,7 +328,7 @@ namespace osu.Game.Overlays base.UpdateAfterChildren(); // no null check because the usage of this class is strict - HeaderBackground.Alpha = -ExpandableHeader.Y / ExpandableHeader.LayoutSize.Y; + HeaderBackground!.Alpha = -ExpandableHeader!.Y / ExpandableHeader.LayoutSize.Y; } } } From 3978d4babb86917150c7bfb5a19ce3c72830b885 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jun 2023 16:30:14 +0900 Subject: [PATCH 092/169] Crop and disable mipmaps on beatmap panel backgrounds This is an effort to improve general performance at song select. At least on the metal renderer, I can notice very high draw frame overheads related to texture uploads. By reducing the size of the texture uploads to roughly match what is actually being displayed on screen (using a relatively inexpensive crop operation), we can bastly reduce stuttering both during initial load and carousel scroll. You might ask if it's safe to disable mipmapping, but I've tested with lower resolutions and bilinear filtering seems to handle just fine. Bilinear without mipmaps only falls apart when you scale below 50% and we're not going too far past that at minimum game scale, if at all. --- ...eatmapPanelBackgroundTextureLoaderStore.cs | 88 +++++++++++++++++++ osu.Game/Beatmaps/IBeatmapResourceProvider.cs | 5 ++ osu.Game/Beatmaps/IWorkingBeatmap.cs | 5 ++ osu.Game/Beatmaps/WorkingBeatmap.cs | 1 + osu.Game/Beatmaps/WorkingBeatmapCache.cs | 11 ++- .../Select/Carousel/SetPanelBackground.cs | 24 ++++- 6 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs diff --git a/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs b/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs new file mode 100644 index 0000000000..0ee516a912 --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs @@ -0,0 +1,88 @@ +// 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 System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using osu.Framework.Graphics.Textures; +using osu.Framework.IO.Stores; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Processing; + +namespace osu.Game.Beatmaps +{ + // Implementation of this class is based off of `MaxDimensionLimitedTextureLoaderStore`. + // If issues are found it's worth checking to make sure similar issues exist there. + public class BeatmapPanelBackgroundTextureLoaderStore : IResourceStore + { + // These numbers are taken from the draw visualiser size requirements for song select panel textures at extreme aspect ratios. + private const int height = 130; + private const int max_width = 1280; + + private readonly IResourceStore? textureStore; + + public BeatmapPanelBackgroundTextureLoaderStore(IResourceStore? textureStore) + { + this.textureStore = textureStore; + } + + public void Dispose() + { + textureStore?.Dispose(); + } + + public TextureUpload Get(string name) + { + var textureUpload = textureStore?.Get(name); + + // NRT not enabled on framework side classes (IResourceStore / TextureLoaderStore), welp. + if (textureUpload == null) + return null!; + + return limitTextureUploadSize(textureUpload); + } + + public async Task GetAsync(string name, CancellationToken cancellationToken = new CancellationToken()) + { + // NRT not enabled on framework side classes (IResourceStore / TextureLoaderStore), welp. + if (textureStore == null) + return null!; + + var textureUpload = await textureStore.GetAsync(name, cancellationToken).ConfigureAwait(false); + + if (textureUpload == null) + return null!; + + return await Task.Run(() => limitTextureUploadSize(textureUpload), cancellationToken).ConfigureAwait(false); + } + + private TextureUpload limitTextureUploadSize(TextureUpload textureUpload) + { + var image = Image.LoadPixelData(textureUpload.Data.ToArray(), textureUpload.Width, textureUpload.Height); + + // The original texture upload will no longer be returned or used. + textureUpload.Dispose(); + + Size size = image.Size(); + int usableWidth = Math.Min(max_width, size.Width); + + // Crop the centre region of the background for now. + Rectangle cropRectangle = new Rectangle( + (size.Width - usableWidth) / 2, + size.Height / 2 - height / 2, + usableWidth, + height + ); + + image.Mutate(i => i.Crop(cropRectangle)); + + return new TextureUpload(image); + } + + public Stream? GetStream(string name) => textureStore?.GetStream(name); + + public IEnumerable GetAvailableResources() => textureStore?.GetAvailableResources() ?? Array.Empty(); + } +} diff --git a/osu.Game/Beatmaps/IBeatmapResourceProvider.cs b/osu.Game/Beatmaps/IBeatmapResourceProvider.cs index 22ff7ce8c8..d033608c95 100644 --- a/osu.Game/Beatmaps/IBeatmapResourceProvider.cs +++ b/osu.Game/Beatmaps/IBeatmapResourceProvider.cs @@ -16,6 +16,11 @@ namespace osu.Game.Beatmaps /// TextureStore LargeTextureStore { get; } + /// + /// Retrieve a global large texture store, used specifically for retrieving cropped beatmap panel backgrounds. + /// + TextureStore BeatmapPanelTextureStore { get; } + /// /// Access a global track store for retrieving beatmap tracks from. /// diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs index 4b0a498a56..905bca45b3 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -49,6 +49,11 @@ namespace osu.Game.Beatmaps /// Texture GetBackground(); + /// + /// Retrieves a cropped background for this used for display on panels. + /// + Texture GetPanelBackground(); + /// /// Retrieves the for the of this . /// diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index a69859f724..25159996f3 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -66,6 +66,7 @@ namespace osu.Game.Beatmaps protected abstract IBeatmap GetBeatmap(); public abstract Texture GetBackground(); + public virtual Texture GetPanelBackground() => GetBackground(); protected abstract Track GetBeatmapTrack(); /// diff --git a/osu.Game/Beatmaps/WorkingBeatmapCache.cs b/osu.Game/Beatmaps/WorkingBeatmapCache.cs index 94865ed8d0..0f3d61f527 100644 --- a/osu.Game/Beatmaps/WorkingBeatmapCache.cs +++ b/osu.Game/Beatmaps/WorkingBeatmapCache.cs @@ -42,6 +42,7 @@ namespace osu.Game.Beatmaps private readonly AudioManager audioManager; private readonly IResourceStore resources; private readonly LargeTextureStore largeTextureStore; + private readonly LargeTextureStore beatmapPanelTextureStore; private readonly ITrackStore trackStore; private readonly IResourceStore files; @@ -58,6 +59,7 @@ namespace osu.Game.Beatmaps this.host = host; this.files = files; largeTextureStore = new LargeTextureStore(host?.Renderer ?? new DummyRenderer(), host?.CreateTextureLoaderStore(files)); + beatmapPanelTextureStore = new LargeTextureStore(host?.Renderer ?? new DummyRenderer(), new BeatmapPanelBackgroundTextureLoaderStore(host?.CreateTextureLoaderStore(files))); this.trackStore = trackStore; } @@ -110,6 +112,7 @@ namespace osu.Game.Beatmaps #region IResourceStorageProvider TextureStore IBeatmapResourceProvider.LargeTextureStore => largeTextureStore; + TextureStore IBeatmapResourceProvider.BeatmapPanelTextureStore => beatmapPanelTextureStore; ITrackStore IBeatmapResourceProvider.Tracks => trackStore; IRenderer IStorageResourceProvider.Renderer => host?.Renderer ?? new DummyRenderer(); AudioManager IStorageResourceProvider.AudioManager => audioManager; @@ -160,7 +163,11 @@ namespace osu.Game.Beatmaps } } - public override Texture GetBackground() + public override Texture GetPanelBackground() => getBackgroundFromStore(resources.BeatmapPanelTextureStore); + + public override Texture GetBackground() => getBackgroundFromStore(resources.LargeTextureStore); + + private Texture getBackgroundFromStore(TextureStore store) { if (string.IsNullOrEmpty(Metadata?.BackgroundFile)) return null; @@ -168,7 +175,7 @@ namespace osu.Game.Beatmaps try { string fileStorePath = BeatmapSetInfo.GetPathForFile(Metadata.BackgroundFile); - var texture = resources.LargeTextureStore.Get(fileStorePath); + var texture = store.Get(fileStorePath); if (texture == null) { diff --git a/osu.Game/Screens/Select/Carousel/SetPanelBackground.cs b/osu.Game/Screens/Select/Carousel/SetPanelBackground.cs index 6f13a34bfc..b8729b7174 100644 --- a/osu.Game/Screens/Select/Carousel/SetPanelBackground.cs +++ b/osu.Game/Screens/Select/Carousel/SetPanelBackground.cs @@ -1,12 +1,14 @@ // 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.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables; using osuTK; using osuTK.Graphics; @@ -21,7 +23,7 @@ namespace osu.Game.Screens.Select.Carousel Children = new Drawable[] { - new BeatmapBackgroundSprite(working) + new PanelBeatmapBackground(working) { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, @@ -68,5 +70,23 @@ namespace osu.Game.Screens.Select.Carousel }, }; } + + public partial class PanelBeatmapBackground : Sprite + { + private readonly IWorkingBeatmap working; + + public PanelBeatmapBackground(IWorkingBeatmap working) + { + ArgumentNullException.ThrowIfNull(working); + + this.working = working; + } + + [BackgroundDependencyLoader] + private void load() + { + Texture = working.GetPanelBackground(); + } + } } } From 7acd186a3d78a6e2ae99446eaf8d8dd89c2dbb8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 8 Jun 2023 09:56:39 +0200 Subject: [PATCH 093/169] Revert to previous bound in drum roll tick generation While `EndTime + tickSpacing` _was_ closer to what stable was using, it could cause undesirable edge cases wherein a tick would be spawned outside of the drum roll's duration (see https://github.com/ppy/osu/pull/23768/files#r1222073027). For this reason, stick with the old code for now, as it is not as susceptible to that sort of breakage. --- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 5b77ad7f91..79d17b4a1f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Taiko.Objects bool first = true; - for (double t = StartTime; t < EndTime + tickSpacing; t += tickSpacing) + for (double t = StartTime; t < EndTime + tickSpacing / 2; t += tickSpacing) { cancellationToken.ThrowIfCancellationRequested(); From 079e687bee87b39fdd104a78661e893ef2ac6c77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 8 Jun 2023 10:09:27 +0200 Subject: [PATCH 094/169] Fix incorrect mock setup after refactor --- osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs b/osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs index 3092966a7e..3d1f7c5b17 100644 --- a/osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs @@ -131,7 +131,7 @@ namespace osu.Game.Tests.Editing.Checks var mock = new Mock(); mock.SetupGet(w => w.Beatmap).Returns(beatmap); - mock.SetupGet(w => w.GetBackground()).Returns(background); + mock.Setup(w => w.GetBackground()).Returns(background); mock.Setup(w => w.GetStream(It.IsAny())).Returns(stream); return mock; From 10c43d22739b6c83006977c0d9aa92fb98372cb5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jun 2023 17:27:00 +0900 Subject: [PATCH 095/169] Reduce delays and fades for carousel panels to improve song select initial display performance Entering song select has seen a hit since the new renderer implementations. The underlying cause is large numbers of vertex buffer uploads (the counter hits >200k for me during the transition). Song select is in the process of being redesigned, and we are probably going to make improvements to the renderer to alleviate this, but in the mean time we can greatly improve the user experience by reducing how long the initial fade in delays take on panels. Visually this doesn't look too jarring, and gives a more immediate feeling when scrolling. It's also more feasible to load elements sooner with https://github.com/ppy/osu/pull/23809 applied. --- .../Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index b97d37c854..4234184ad1 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -112,11 +112,11 @@ namespace osu.Game.Screens.Select.Carousel background = new DelayedLoadWrapper(() => new SetPanelBackground(manager.GetWorkingBeatmap(beatmapSet.Beatmaps.MinBy(b => b.OnlineID))) { RelativeSizeAxes = Axes.Both, - }, 300) + }, 200) { RelativeSizeAxes = Axes.Both }, - mainFlow = new DelayedLoadWrapper(() => new SetPanelContent((CarouselBeatmapSet)Item), 100) + mainFlow = new DelayedLoadWrapper(() => new SetPanelContent((CarouselBeatmapSet)Item), 50) { RelativeSizeAxes = Axes.Both }, @@ -126,7 +126,7 @@ namespace osu.Game.Screens.Select.Carousel mainFlow.DelayedLoadComplete += fadeContentIn; } - private void fadeContentIn(Drawable d) => d.FadeInFromZero(750, Easing.OutQuint); + private void fadeContentIn(Drawable d) => d.FadeInFromZero(150); protected override void Deselected() { From 95ad18ed2ea65cf312ec098c5f79be921beec3bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jun 2023 17:44:32 +0900 Subject: [PATCH 096/169] Reduce the radius of note-to-note snapping in osu! editor Stable uses `0.15f`, but for whatever reason that feels too large still. I've ballparked this. Addresses https://github.com/ppy/osu/discussions/23806. --- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index ad6af6d74e..aac5f6ffb1 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -187,7 +187,7 @@ namespace osu.Game.Rulesets.Osu.Edit var playfield = PlayfieldAtScreenSpacePosition(screenSpacePosition); float snapRadius = - playfield.GamefieldToScreenSpace(new Vector2(OsuHitObject.OBJECT_RADIUS / 5)).X - + playfield.GamefieldToScreenSpace(new Vector2(OsuHitObject.OBJECT_RADIUS * 0.10f)).X - playfield.GamefieldToScreenSpace(Vector2.Zero).X; foreach (var b in blueprints) From 2958ce35be318ddec498e3c430f0725231291985 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 8 Jun 2023 13:19:47 +0200 Subject: [PATCH 097/169] Adjust object snapping test cases to pass with new radius --- .../Editor/TestSceneObjectObjectSnap.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs index 3b8a5a90a5..9af1855167 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("place first object", () => InputManager.Click(MouseButton.Left)); - AddStep("move mouse slightly", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.02f, 0))); + AddStep("move mouse slightly", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.01f, 0))); AddStep("place second object", () => InputManager.Click(MouseButton.Left)); @@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("enter circle placement mode", () => InputManager.Key(Key.Number2)); - AddStep("move mouse slightly", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.235f, 0))); + AddStep("move mouse slightly", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.205f, 0))); AddStep("place second object", () => InputManager.Click(MouseButton.Left)); @@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("begin drag", () => InputManager.PressButton(MouseButton.Left)); - AddStep("move mouse slightly off centre", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.02f, 0))); + AddStep("move mouse slightly off centre", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.01f, 0))); AddAssert("object 3 snapped to 1", () => { @@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor return Precision.AlmostEquals(first.EndPosition, third.Position); }); - AddStep("move mouse slightly off centre", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * -0.22f, playfield.ScreenSpaceDrawQuad.Width * 0.21f))); + AddStep("move mouse slightly off centre", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * -0.21f, playfield.ScreenSpaceDrawQuad.Width * 0.205f))); AddAssert("object 2 snapped to 1", () => { From dbb7ddac5238e667a1a1ba99268e41b3d80af0e2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jun 2023 20:55:09 +0900 Subject: [PATCH 098/169] Change osu!mania scoring ratio to 99% acc (to match previous lazer scoring) --- osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index 3341f834dd..6292ed75cd 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -18,8 +18,8 @@ namespace osu.Game.Rulesets.Mania.Scoring protected override double ComputeTotalScore(double comboProgress, double accuracyProgress, double bonusPortion) { - return 200000 * comboProgress - + 800000 * Math.Pow(Accuracy.Value, 2 + 2 * Accuracy.Value) * accuracyProgress + return 10000 * comboProgress + + 990000 * Math.Pow(Accuracy.Value, 2 + 2 * Accuracy.Value) * accuracyProgress + bonusPortion; } From ff4d376c84871b7b2307e7fe6d1c40bbbc17acbf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jun 2023 21:01:05 +0900 Subject: [PATCH 099/169] Mark relevant components as `internal` --- osu.Game/Beatmaps/IBeatmapResourceProvider.cs | 2 +- osu.Game/Beatmaps/IWorkingBeatmap.cs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/IBeatmapResourceProvider.cs b/osu.Game/Beatmaps/IBeatmapResourceProvider.cs index d033608c95..9e79e03785 100644 --- a/osu.Game/Beatmaps/IBeatmapResourceProvider.cs +++ b/osu.Game/Beatmaps/IBeatmapResourceProvider.cs @@ -9,7 +9,7 @@ using osu.Game.IO; namespace osu.Game.Beatmaps { - public interface IBeatmapResourceProvider : IStorageResourceProvider + internal interface IBeatmapResourceProvider : IStorageResourceProvider { /// /// Retrieve a global large texture store, used for loading beatmap backgrounds. diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs index 905bca45b3..dc69a7f776 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -32,12 +32,12 @@ namespace osu.Game.Beatmaps /// /// Whether the Beatmap has finished loading. /// - public bool BeatmapLoaded { get; } + bool BeatmapLoaded { get; } /// /// Whether the Track has finished loading. /// - public bool TrackLoaded { get; } + bool TrackLoaded { get; } /// /// Retrieves the which this represents. @@ -52,7 +52,7 @@ namespace osu.Game.Beatmaps /// /// Retrieves a cropped background for this used for display on panels. /// - Texture GetPanelBackground(); + internal Texture GetPanelBackground(); /// /// Retrieves the for the of this . @@ -129,12 +129,12 @@ namespace osu.Game.Beatmaps /// /// Beings loading the contents of this asynchronously. /// - public void BeginAsyncLoad(); + void BeginAsyncLoad(); /// /// Cancels the asynchronous loading of the contents of this . /// - public void CancelAsyncLoad(); + void CancelAsyncLoad(); /// /// Reads the correct track restart point from beatmap metadata and sets looping to enabled. From cccc06de4873afd0522bea33687c259b385cf8d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jun 2023 21:10:40 +0900 Subject: [PATCH 100/169] Fix potential failure if beatmap background isn't tall enough --- .../Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs b/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs index 0ee516a912..ef5de4d9b2 100644 --- a/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs +++ b/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs @@ -18,7 +18,7 @@ namespace osu.Game.Beatmaps public class BeatmapPanelBackgroundTextureLoaderStore : IResourceStore { // These numbers are taken from the draw visualiser size requirements for song select panel textures at extreme aspect ratios. - private const int height = 130; + private const int max_height = 130; private const int max_width = 1280; private readonly IResourceStore? textureStore; @@ -67,13 +67,14 @@ namespace osu.Game.Beatmaps Size size = image.Size(); int usableWidth = Math.Min(max_width, size.Width); + int usableHeight = Math.Min(max_height, size.Height); // Crop the centre region of the background for now. Rectangle cropRectangle = new Rectangle( (size.Width - usableWidth) / 2, - size.Height / 2 - height / 2, + (size.Height - usableHeight) / 3, usableWidth, - height + usableHeight ); image.Mutate(i => i.Crop(cropRectangle)); From 6dbf02454f0f7c2dee97aea8c01ea5120e676cff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 8 Jun 2023 14:19:34 +0200 Subject: [PATCH 101/169] Fix bad math --- osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs b/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs index ef5de4d9b2..6d5b90521e 100644 --- a/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs +++ b/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs @@ -72,7 +72,7 @@ namespace osu.Game.Beatmaps // Crop the centre region of the background for now. Rectangle cropRectangle = new Rectangle( (size.Width - usableWidth) / 2, - (size.Height - usableHeight) / 3, + (size.Height - usableHeight) / 2, usableWidth, usableHeight ); From facf7de053a63b4155bff561b8ffede50573c561 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 8 Jun 2023 21:24:40 +0900 Subject: [PATCH 102/169] Parse ScoreInfo.IsLegacyScore from replays --- osu.Game/Database/RealmAccess.cs | 49 ++++++++++++++++++- osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 3 ++ osu.Game/Scoring/ScoreInfo.cs | 3 +- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index f4c6c802f1..21e025c79d 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -22,12 +22,15 @@ using osu.Framework.Statistics; using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Extensions; using osu.Game.Input.Bindings; +using osu.Game.IO.Legacy; using osu.Game.Models; using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; +using osu.Game.Scoring.Legacy; using osu.Game.Skinning; using Realms; using Realms.Exceptions; @@ -72,8 +75,9 @@ namespace osu.Game.Database /// 25 2022-09-18 Remove skins to add with new naming. /// 26 2023-02-05 Added BeatmapHash to ScoreInfo. /// 27 2023-06-06 Added EditorTimestamp to BeatmapInfo. + /// 28 2023-06-08 Added IsLegacyScore to ScoreInfo, parsed from replay files. /// - private const int schema_version = 27; + private const int schema_version = 28; /// /// Lock object which is held during sections, blocking realm retrieval during blocking periods. @@ -880,6 +884,7 @@ namespace osu.Game.Database break; case 26: + { // Add ScoreInfo.BeatmapHash property to ensure scores correspond to the correct version of beatmap. var scores = migration.NewRealm.All(); @@ -887,6 +892,33 @@ namespace osu.Game.Database score.BeatmapHash = score.BeatmapInfo.Hash; break; + } + + case 28: + { + var files = new RealmFileStore(this, storage); + var scores = migration.NewRealm.All(); + + foreach (var score in scores) + { + string? replayFilename = score.Files.FirstOrDefault(f => f.Filename.EndsWith(@".osr", StringComparison.InvariantCultureIgnoreCase))?.File.GetStoragePath(); + + if (replayFilename == null) + continue; + + using (var stream = files.Store.GetStream(replayFilename)) + { + if (stream == null) + continue; + + int version = new ReplayVersionParser().Parse(stream); + if (version < LegacyScoreEncoder.FIRST_LAZER_VERSION) + score.IsLegacyScore = true; + } + } + + break; + } } } @@ -1107,5 +1139,20 @@ namespace osu.Game.Database isDisposed = true; } } + + /// + /// A trimmed down specialised to extract the version from replays. + /// + private class ReplayVersionParser + { + public int Parse(Stream stream) + { + using (SerializationReader sr = new SerializationReader(stream)) + { + sr.ReadByte(); // Ruleset. + return sr.ReadInt32(); + } + } + } } } diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index 9b145ad56e..c6461840aa 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -46,6 +46,9 @@ namespace osu.Game.Scoring.Legacy score.ScoreInfo = scoreInfo; int version = sr.ReadInt32(); + + scoreInfo.IsLegacyScore = version < LegacyScoreEncoder.FIRST_LAZER_VERSION; + string beatmapHash = sr.ReadString(); workingBeatmap = GetBeatmap(beatmapHash); diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index e084c45de0..d56338c6a4 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -181,8 +181,7 @@ namespace osu.Game.Scoring /// /// Whether this represents a legacy (osu!stable) score. /// - [Ignored] - public bool IsLegacyScore => Mods.OfType().Any(); + public bool IsLegacyScore { get; set; } private Dictionary? statistics; From 76df11c39843f92b4bc305b07a8ee77a08b9067a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 8 Jun 2023 21:26:32 +0900 Subject: [PATCH 103/169] Don't scale stable scores with the classic scoring mode --- osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs b/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs index 84bf6d15f6..52dec20b32 100644 --- a/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs +++ b/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs @@ -16,7 +16,13 @@ namespace osu.Game.Scoring.Legacy => getDisplayScore(scoreProcessor.Ruleset.RulesetInfo.OnlineID, scoreProcessor.TotalScore.Value, mode, scoreProcessor.MaximumStatistics); public static long GetDisplayScore(this ScoreInfo scoreInfo, ScoringMode mode) - => getDisplayScore(scoreInfo.Ruleset.OnlineID, scoreInfo.TotalScore, mode, scoreInfo.MaximumStatistics); + { + // Temporary to not scale stable scores that are already in the XX-millions with the classic scoring mode. + if (scoreInfo.IsLegacyScore) + return scoreInfo.TotalScore; + + return getDisplayScore(scoreInfo.Ruleset.OnlineID, scoreInfo.TotalScore, mode, scoreInfo.MaximumStatistics); + } private static long getDisplayScore(int rulesetId, long score, ScoringMode mode, IReadOnlyDictionary maximumStatistics) { From 05bd912a21c3b18159199fdf0ac5e4038f0f69a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 8 Jun 2023 15:18:31 +0200 Subject: [PATCH 104/169] Revert `internal` access modifier application Unfortunately breaks a few classes (which is fixable), and also breaks Moq/Castle dynamic proxies (which is unfortunate). Relevant error: System.TypeLoadException : Method 'GetPanelBackground' in type 'Castle.Proxies.IWorkingBeatmapProxy' from assembly 'DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation. Stack Trace: at System.Reflection.Emit.TypeBuilder.CreateTypeNoLock() at System.Reflection.Emit.TypeBuilder.CreateTypeInfo() at Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.BuildType() at Castle.DynamicProxy.Generators.BaseInterfaceProxyGenerator.GenerateType(String typeName, INamingScope namingScope) at Castle.DynamicProxy.Generators.BaseProxyGenerator.<>c__DisplayClass13_0.b__0(CacheKey cacheKey) at Castle.Core.Internal.SynchronizedDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory) at Castle.DynamicProxy.Generators.BaseProxyGenerator.GetProxyType() at Castle.DynamicProxy.DefaultProxyBuilder.CreateInterfaceProxyTypeWithoutTarget(Type interfaceToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options) at Castle.DynamicProxy.ProxyGenerator.CreateInterfaceProxyTypeWithoutTarget(Type interfaceToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options) at Castle.DynamicProxy.ProxyGenerator.CreateInterfaceProxyWithoutTarget(Type interfaceToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, IInterceptor[] interceptors) at Moq.CastleProxyFactory.CreateProxy(Type mockType, IInterceptor interceptor, Type[] interfaces, Object[] arguments) in C:\projects\moq4\src\Moq\Interception\CastleProxyFactory.cs:line 50 In theory it would be possible to fix this via application of [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] onto the `osu.Game` assembly, but I think this would be bad precedent. --- osu.Game/Beatmaps/IWorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs index dc69a7f776..bdfa6bdf6d 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -52,7 +52,7 @@ namespace osu.Game.Beatmaps /// /// Retrieves a cropped background for this used for display on panels. /// - internal Texture GetPanelBackground(); + Texture GetPanelBackground(); /// /// Retrieves the for the of this . From 46dc47b0b4254a9b90f00ab2b68c540b7ee94685 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jun 2023 01:22:29 +0900 Subject: [PATCH 105/169] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index c88bea8265..f4d08e443c 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -11,7 +11,7 @@ manifestmerger.jar - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8a941ca6c1..b2faa7dfc2 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 1dcece7741..9aafec6c50 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -16,6 +16,6 @@ iossimulator-x64 - + From 0875fc6233475969d1bb72dbc4f3fb5bcfa98be6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 1 Jun 2023 17:12:00 +0900 Subject: [PATCH 106/169] Update tests in line with new behaviour --- .../Visual/Editing/TestSceneBeatDivisorControl.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs index 3ee5ea79db..17cb3c1449 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs @@ -161,9 +161,11 @@ namespace osu.Game.Tests.Visual.Editing switchPresets(1); assertPreset(BeatDivisorType.Triplets); + assertBeatSnap(6); switchPresets(1); assertPreset(BeatDivisorType.Common); + assertBeatSnap(4); switchPresets(-1); assertPreset(BeatDivisorType.Triplets); @@ -179,6 +181,7 @@ namespace osu.Game.Tests.Visual.Editing setDivisorViaInput(15); assertPreset(BeatDivisorType.Custom, 15); + assertBeatSnap(15); switchBeatSnap(-1); assertBeatSnap(5); @@ -188,12 +191,14 @@ namespace osu.Game.Tests.Visual.Editing setDivisorViaInput(5); assertPreset(BeatDivisorType.Custom, 15); + assertBeatSnap(5); switchPresets(1); assertPreset(BeatDivisorType.Common); switchPresets(-1); - assertPreset(BeatDivisorType.Triplets); + assertPreset(BeatDivisorType.Custom, 15); + assertBeatSnap(15); } private void switchBeatSnap(int direction) => AddRepeatStep($"move snap {(direction > 0 ? "forward" : "backward")}", () => @@ -217,7 +222,7 @@ namespace osu.Game.Tests.Visual.Editing private void assertPreset(BeatDivisorType type, int? maxDivisor = null) { - AddAssert($"preset is {type}", () => bindableBeatDivisor.ValidDivisors.Value.Type == type); + AddAssert($"preset is {type}", () => bindableBeatDivisor.ValidDivisors.Value.Type, () => Is.EqualTo(type)); if (type == BeatDivisorType.Custom) { From b66d1aa33d08539cdbb0e859d8d2c2d3380be2e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 8 Jun 2023 20:32:16 +0200 Subject: [PATCH 107/169] Fix code quality inspection --- osu.Game/Screens/Edit/EditorScreenWithTimeline.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index 84cfac8f65..b8cbff047e 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -82,7 +82,7 @@ namespace osu.Game.Screens.Edit AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Right = 5 }, }, - new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both } + new BeatDivisorControl(this.beatDivisor) { RelativeSizeAxes = Axes.Both } }, }, RowDimensions = new[] From 519923e843ea2fd2e98a9f2afa88106a48b83568 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 8 Jun 2023 17:09:20 -0700 Subject: [PATCH 108/169] Remove redundant `EllipsisString` assign --- osu.Game/Overlays/Chat/DrawableChatUsername.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/DrawableChatUsername.cs b/osu.Game/Overlays/Chat/DrawableChatUsername.cs index 4b4afc204c..46e3ff5b37 100644 --- a/osu.Game/Overlays/Chat/DrawableChatUsername.cs +++ b/osu.Game/Overlays/Chat/DrawableChatUsername.cs @@ -87,7 +87,6 @@ namespace osu.Game.Overlays.Chat { Shadow = false, Truncate = true, - EllipsisString = "…", Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }; From 85fedbd02555618be2892688d2b121ae7e0c1843 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 8 Jun 2023 17:11:17 -0700 Subject: [PATCH 109/169] Add tooltips to truncated text --- .../Drawables/Cards/BeatmapCardExtra.cs | 9 +++------ .../Drawables/Cards/BeatmapCardNormal.cs | 6 ++---- .../Graphics/Sprites/TruncatingSpriteText.cs | 20 +++++++++++++++++++ .../Graphics/UserInterface/OsuDropdown.cs | 3 +-- .../Chat/ChannelList/ChannelListItem.cs | 3 +-- osu.Game/Overlays/Chat/ChatTextBar.cs | 3 +-- .../Overlays/Chat/DrawableChatUsername.cs | 3 +-- .../Dashboard/Home/DashboardBeatmapPanel.cs | 6 ++---- .../Play/HUD/GameplayLeaderboardScore.cs | 3 +-- .../Expanded/ExpandedPanelMiddleContent.cs | 9 +++------ osu.Game/Screens/Select/BeatmapInfoWedge.cs | 9 +++------ 11 files changed, 38 insertions(+), 36 deletions(-) create mode 100644 osu.Game/Graphics/Sprites/TruncatingSpriteText.cs diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs index 5c6f0c4ee1..175c15ea7b 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs @@ -106,12 +106,11 @@ namespace osu.Game.Beatmaps.Drawables.Cards { new Drawable[] { - new OsuSpriteText + new TruncatingSpriteText { Text = new RomanisableString(BeatmapSet.TitleUnicode, BeatmapSet.Title), Font = OsuFont.Default.With(size: 22.5f, weight: FontWeight.SemiBold), RelativeSizeAxes = Axes.X, - Truncate = true }, titleBadgeArea = new FillFlowContainer { @@ -140,21 +139,19 @@ namespace osu.Game.Beatmaps.Drawables.Cards { new[] { - new OsuSpriteText + new TruncatingSpriteText { Text = createArtistText(), Font = OsuFont.Default.With(size: 17.5f, weight: FontWeight.SemiBold), RelativeSizeAxes = Axes.X, - Truncate = true }, Empty() }, } }, - new OsuSpriteText + new TruncatingSpriteText { RelativeSizeAxes = Axes.X, - Truncate = true, Text = BeatmapSet.Source, Shadow = false, Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold), diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs index 720d892495..18e1584a98 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs @@ -107,12 +107,11 @@ namespace osu.Game.Beatmaps.Drawables.Cards { new Drawable[] { - new OsuSpriteText + new TruncatingSpriteText { Text = new RomanisableString(BeatmapSet.TitleUnicode, BeatmapSet.Title), Font = OsuFont.Default.With(size: 22.5f, weight: FontWeight.SemiBold), RelativeSizeAxes = Axes.X, - Truncate = true }, titleBadgeArea = new FillFlowContainer { @@ -141,12 +140,11 @@ namespace osu.Game.Beatmaps.Drawables.Cards { new[] { - new OsuSpriteText + new TruncatingSpriteText { Text = createArtistText(), Font = OsuFont.Default.With(size: 17.5f, weight: FontWeight.SemiBold), RelativeSizeAxes = Axes.X, - Truncate = true }, Empty() }, diff --git a/osu.Game/Graphics/Sprites/TruncatingSpriteText.cs b/osu.Game/Graphics/Sprites/TruncatingSpriteText.cs new file mode 100644 index 0000000000..da0dbd49d2 --- /dev/null +++ b/osu.Game/Graphics/Sprites/TruncatingSpriteText.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Cursor; +using osu.Framework.Localisation; + +namespace osu.Game.Graphics.Sprites +{ + public sealed partial class TruncatingSpriteText : OsuSpriteText, IHasTooltip + { + public LocalisableString TooltipText => Text; + + public override bool HandlePositionalInput => IsTruncated; + + public TruncatingSpriteText() + { + Truncate = true; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index 3230bb0569..b530172f3e 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -335,12 +335,11 @@ namespace osu.Game.Graphics.UserInterface { new Drawable[] { - Text = new OsuSpriteText + Text = new TruncatingSpriteText { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, RelativeSizeAxes = Axes.X, - Truncate = true, }, Icon = new SpriteIcon { diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs index 57b6f6268c..21b6147113 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs @@ -85,7 +85,7 @@ namespace osu.Game.Overlays.Chat.ChannelList new Drawable?[] { createIcon(), - text = new OsuSpriteText + text = new TruncatingSpriteText { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, @@ -94,7 +94,6 @@ namespace osu.Game.Overlays.Chat.ChannelList Colour = colourProvider.Light3, Margin = new MarginPadding { Bottom = 2 }, RelativeSizeAxes = Axes.X, - Truncate = true, }, createMentionPill(), close = createCloseButton(), diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index fd5e0e9836..87e787fcb8 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -73,14 +73,13 @@ namespace osu.Game.Overlays.Chat Width = chatting_text_width, Masking = true, Padding = new MarginPadding { Horizontal = padding }, - Child = chattingText = new OsuSpriteText + Child = chattingText = new TruncatingSpriteText { MaxWidth = chatting_text_width - padding * 2, Font = OsuFont.Torus.With(size: 20), Colour = colourProvider.Background1, Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - Truncate = true, }, }, searchIconContainer = new Container diff --git a/osu.Game/Overlays/Chat/DrawableChatUsername.cs b/osu.Game/Overlays/Chat/DrawableChatUsername.cs index 46e3ff5b37..18632aa4af 100644 --- a/osu.Game/Overlays/Chat/DrawableChatUsername.cs +++ b/osu.Game/Overlays/Chat/DrawableChatUsername.cs @@ -83,10 +83,9 @@ namespace osu.Game.Overlays.Chat Action = openUserProfile; - drawableText = new OsuSpriteText + drawableText = new TruncatingSpriteText { Shadow = false, - Truncate = true, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }; diff --git a/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs index 792d6cc785..f36e6b49bb 100644 --- a/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs @@ -100,17 +100,15 @@ namespace osu.Game.Overlays.Dashboard.Home Direction = FillDirection.Vertical, Children = new Drawable[] { - new OsuSpriteText + new TruncatingSpriteText { RelativeSizeAxes = Axes.X, - Truncate = true, Font = OsuFont.GetFont(weight: FontWeight.Regular), Text = BeatmapSet.Title }, - new OsuSpriteText + new TruncatingSpriteText { RelativeSizeAxes = Axes.X, - Truncate = true, Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular), Text = BeatmapSet.Artist }, diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 4ac2f1afda..dcb2c1071e 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -235,7 +235,7 @@ namespace osu.Game.Screens.Play.HUD } } }, - usernameText = new OsuSpriteText + usernameText = new TruncatingSpriteText { RelativeSizeAxes = Axes.X, Width = 0.6f, @@ -244,7 +244,6 @@ namespace osu.Game.Screens.Play.HUD Colour = Color4.White, Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), Text = User?.Username ?? string.Empty, - Truncate = true, Shadow = false, } } diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index f23b469f5c..fe74c1ba0d 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -101,23 +101,21 @@ namespace osu.Game.Screens.Ranking.Expanded Direction = FillDirection.Vertical, Children = new Drawable[] { - new OsuSpriteText + new TruncatingSpriteText { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Text = new RomanisableString(metadata.TitleUnicode, metadata.Title), Font = OsuFont.Torus.With(size: 20, weight: FontWeight.SemiBold), MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, - Truncate = true, }, - new OsuSpriteText + new TruncatingSpriteText { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Text = new RomanisableString(metadata.ArtistUnicode, metadata.Artist), Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, - Truncate = true, }, new Container { @@ -156,14 +154,13 @@ namespace osu.Game.Screens.Ranking.Expanded AutoSizeAxes = Axes.Both, Children = new Drawable[] { - new OsuSpriteText + new TruncatingSpriteText { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Text = beatmap.DifficultyName, Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold), MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, - Truncate = true, }, new OsuTextFlowContainer(s => s.Font = OsuFont.Torus.With(size: 12)) { diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 2102df1022..961f8684ce 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -233,12 +233,11 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.X, Children = new Drawable[] { - VersionLabel = new OsuSpriteText + VersionLabel = new TruncatingSpriteText { Text = beatmapInfo.DifficultyName, Font = OsuFont.GetFont(size: 24, italics: true), RelativeSizeAxes = Axes.X, - Truncate = true, }, } }, @@ -286,19 +285,17 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.X, Children = new Drawable[] { - TitleLabel = new OsuSpriteText + TitleLabel = new TruncatingSpriteText { Current = { BindTarget = titleBinding }, Font = OsuFont.GetFont(size: 28, italics: true), RelativeSizeAxes = Axes.X, - Truncate = true, }, - ArtistLabel = new OsuSpriteText + ArtistLabel = new TruncatingSpriteText { Current = { BindTarget = artistBinding }, Font = OsuFont.GetFont(size: 17, italics: true), RelativeSizeAxes = Axes.X, - Truncate = true, }, MapperContainer = new FillFlowContainer { From 67562a38568ff7ded79ec7bacaa4faedea31de97 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 9 Jun 2023 14:35:29 +0900 Subject: [PATCH 110/169] Catch errors during score parsing --- osu.Game/Database/RealmAccess.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 21e025c79d..493987733b 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -911,9 +911,16 @@ namespace osu.Game.Database if (stream == null) continue; - int version = new ReplayVersionParser().Parse(stream); - if (version < LegacyScoreEncoder.FIRST_LAZER_VERSION) - score.IsLegacyScore = true; + try + { + int version = new ReplayVersionParser().Parse(stream); + if (version < LegacyScoreEncoder.FIRST_LAZER_VERSION) + score.IsLegacyScore = true; + } + catch (Exception e) + { + Logger.Error(e, $"Failed to read replay {replayFilename}", LoggingTarget.Database); + } } } From 1ab3b43b593fdc06e1d8b987636047824fffa128 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jun 2023 14:36:17 +0900 Subject: [PATCH 111/169] Fix weird right-toolbox distance snapping display in osu!catch editor --- .../Edit/DistancedHitObjectComposer.cs | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index a8972775de..817e8bd5fe 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -125,6 +125,7 @@ namespace osu.Game.Rulesets.Edit if (currentSnap > DistanceSpacingMultiplier.MinValue) { currentDistanceSpacingButton.Enabled.Value = currentDistanceSpacingButton.Expanded.Value + && !DistanceSpacingMultiplier.Disabled && !Precision.AlmostEquals(currentSnap, DistanceSpacingMultiplier.Value, DistanceSpacingMultiplier.Precision / 2); currentDistanceSpacingButton.ContractedLabelText = $"current {currentSnap:N2}x"; currentDistanceSpacingButton.ExpandedLabelText = $"Use current ({currentSnap:N2}x)"; @@ -141,28 +142,31 @@ namespace osu.Game.Rulesets.Edit { base.LoadComplete(); - if (!DistanceSpacingMultiplier.Disabled) + if (DistanceSpacingMultiplier.Disabled) { - DistanceSpacingMultiplier.Value = EditorBeatmap.BeatmapInfo.DistanceSpacing; - DistanceSpacingMultiplier.BindValueChanged(multiplier => - { - distanceSpacingSlider.ContractedLabelText = $"D. S. ({multiplier.NewValue:0.##x})"; - distanceSpacingSlider.ExpandedLabelText = $"Distance Spacing ({multiplier.NewValue:0.##x})"; - - if (multiplier.NewValue != multiplier.OldValue) - onScreenDisplay?.Display(new DistanceSpacingToast(multiplier.NewValue.ToLocalisableString(@"0.##x"), multiplier)); - - EditorBeatmap.BeatmapInfo.DistanceSpacing = multiplier.NewValue; - }, true); - - // Manual binding to handle enabling distance spacing when the slider is interacted with. - distanceSpacingSlider.Current.BindValueChanged(spacing => - { - DistanceSpacingMultiplier.Value = spacing.NewValue; - DistanceSnapToggle.Value = TernaryState.True; - }); - DistanceSpacingMultiplier.BindValueChanged(spacing => distanceSpacingSlider.Current.Value = spacing.NewValue); + distanceSpacingSlider.Hide(); + return; } + + DistanceSpacingMultiplier.Value = EditorBeatmap.BeatmapInfo.DistanceSpacing; + DistanceSpacingMultiplier.BindValueChanged(multiplier => + { + distanceSpacingSlider.ContractedLabelText = $"D. S. ({multiplier.NewValue:0.##x})"; + distanceSpacingSlider.ExpandedLabelText = $"Distance Spacing ({multiplier.NewValue:0.##x})"; + + if (multiplier.NewValue != multiplier.OldValue) + onScreenDisplay?.Display(new DistanceSpacingToast(multiplier.NewValue.ToLocalisableString(@"0.##x"), multiplier)); + + EditorBeatmap.BeatmapInfo.DistanceSpacing = multiplier.NewValue; + }, true); + + // Manual binding to handle enabling distance spacing when the slider is interacted with. + distanceSpacingSlider.Current.BindValueChanged(spacing => + { + DistanceSpacingMultiplier.Value = spacing.NewValue; + DistanceSnapToggle.Value = TernaryState.True; + }); + DistanceSpacingMultiplier.BindValueChanged(spacing => distanceSpacingSlider.Current.Value = spacing.NewValue); } protected override IEnumerable CreateTernaryButtons() => base.CreateTernaryButtons().Concat(new[] From df874b9ae8445c8daea597918c058e6fc784faa6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 9 Jun 2023 09:16:05 +0300 Subject: [PATCH 112/169] Fix beatmap panel background looking different than usual --- .../Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs b/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs index 6d5b90521e..8f70f8a04f 100644 --- a/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs +++ b/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs @@ -66,7 +66,10 @@ namespace osu.Game.Beatmaps textureUpload.Dispose(); Size size = image.Size(); - int usableWidth = Math.Min(max_width, size.Width); + + float aspectRatio = (float)size.Width / size.Height; + + int usableWidth = Math.Min((int)(max_width * aspectRatio), size.Width); int usableHeight = Math.Min(max_height, size.Height); // Crop the centre region of the background for now. From a9071e7afd554c6e1c25fe606277e3f188700b25 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jun 2023 17:33:59 +0900 Subject: [PATCH 113/169] try-catch more --- osu.Game/Database/RealmAccess.cs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 493987733b..048ff43f0a 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -902,25 +902,24 @@ namespace osu.Game.Database foreach (var score in scores) { string? replayFilename = score.Files.FirstOrDefault(f => f.Filename.EndsWith(@".osr", StringComparison.InvariantCultureIgnoreCase))?.File.GetStoragePath(); - if (replayFilename == null) continue; - using (var stream = files.Store.GetStream(replayFilename)) + try { - if (stream == null) - continue; - - try + using (var stream = files.Store.GetStream(replayFilename)) { + if (stream == null) + continue; + int version = new ReplayVersionParser().Parse(stream); if (version < LegacyScoreEncoder.FIRST_LAZER_VERSION) score.IsLegacyScore = true; } - catch (Exception e) - { - Logger.Error(e, $"Failed to read replay {replayFilename}", LoggingTarget.Database); - } + } + catch (Exception e) + { + Logger.Error(e, $"Failed to read replay {replayFilename}", LoggingTarget.Database); } } From 53f935714ea69df4e9114427939286665b22f1c0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jun 2023 17:34:27 +0900 Subject: [PATCH 114/169] Inline binary reading to avoid polluting `RealmAccess` with nested class --- osu.Game/Database/RealmAccess.cs | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 048ff43f0a..50044cb8d9 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -912,9 +912,14 @@ namespace osu.Game.Database if (stream == null) continue; - int version = new ReplayVersionParser().Parse(stream); - if (version < LegacyScoreEncoder.FIRST_LAZER_VERSION) - score.IsLegacyScore = true; + // Trimmed down logic from LegacyScoreDecoder to extract the version from replays. + using (SerializationReader sr = new SerializationReader(stream)) + { + sr.ReadByte(); // Ruleset. + int version = sr.ReadInt32(); + if (version < LegacyScoreEncoder.FIRST_LAZER_VERSION) + score.IsLegacyScore = true; + } } } catch (Exception e) @@ -1145,20 +1150,5 @@ namespace osu.Game.Database isDisposed = true; } } - - /// - /// A trimmed down specialised to extract the version from replays. - /// - private class ReplayVersionParser - { - public int Parse(Stream stream) - { - using (SerializationReader sr = new SerializationReader(stream)) - { - sr.ReadByte(); // Ruleset. - return sr.ReadInt32(); - } - } - } } } From c2663f27a1bf742ef637a59346c018ace18126d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jun 2023 17:58:44 +0900 Subject: [PATCH 115/169] Fix audio settings not displaying while watching a replay --- osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs index 45b2c1b13c..064d2071ce 100644 --- a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs +++ b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs @@ -42,7 +42,8 @@ namespace osu.Game.Screens.Play.HUD //CollectionSettings = new CollectionSettings(), //DiscussionSettings = new DiscussionSettings(), PlaybackSettings = new PlaybackSettings { Expanded = { Value = false } }, - VisualSettings = new VisualSettings { Expanded = { Value = false } } + VisualSettings = new VisualSettings { Expanded = { Value = false } }, + new AudioSettings { Expanded = { Value = false } } } }; } From ca25ac446b76d7be944c7815c26ba74994bc5209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 9 Jun 2023 11:20:12 +0200 Subject: [PATCH 116/169] Be slightly more specific with error message --- osu.Game/Database/RealmAccess.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 50044cb8d9..63ab18db8c 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -924,7 +924,7 @@ namespace osu.Game.Database } catch (Exception e) { - Logger.Error(e, $"Failed to read replay {replayFilename}", LoggingTarget.Database); + Logger.Error(e, $"Failed to read replay {replayFilename} during score migration", LoggingTarget.Database); } } From 4685ba83e1da43d2251f0da670f8a9ba85f7a55f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jun 2023 18:24:38 +0900 Subject: [PATCH 117/169] Apply NRT to `MemoryCachingComponent` classes --- osu.Game/Database/MemoryCachingComponent.cs | 17 +++++----- osu.Game/Database/OnlineLookupCache.cs | 31 ++++++++----------- osu.Game/Database/UserLookupCache.cs | 10 ++---- .../Online/API/Requests/GetUsersRequest.cs | 2 -- 4 files changed, 24 insertions(+), 36 deletions(-) diff --git a/osu.Game/Database/MemoryCachingComponent.cs b/osu.Game/Database/MemoryCachingComponent.cs index 5d1a381f09..e98475efae 100644 --- a/osu.Game/Database/MemoryCachingComponent.cs +++ b/osu.Game/Database/MemoryCachingComponent.cs @@ -1,13 +1,11 @@ // 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 System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using JetBrains.Annotations; using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; using osu.Framework.Statistics; @@ -19,8 +17,9 @@ namespace osu.Game.Database /// Currently not persisted between game sessions. /// public abstract partial class MemoryCachingComponent : Component + where TLookup : notnull { - private readonly ConcurrentDictionary cache = new ConcurrentDictionary(); + private readonly ConcurrentDictionary cache = new ConcurrentDictionary(); private readonly GlobalStatistic statistics; @@ -37,12 +36,12 @@ namespace osu.Game.Database /// /// The lookup to retrieve. /// An optional to cancel the operation. - protected async Task GetAsync([NotNull] TLookup lookup, CancellationToken token = default) + protected async Task GetAsync(TLookup lookup, CancellationToken token = default) { - if (CheckExists(lookup, out TValue performance)) + if (CheckExists(lookup, out TValue? existing)) { statistics.Value.HitCount++; - return performance; + return existing; } var computed = await ComputeValueAsync(lookup, token).ConfigureAwait(false); @@ -73,7 +72,7 @@ namespace osu.Game.Database statistics.Value.Usage = cache.Count; } - protected bool CheckExists([NotNull] TLookup lookup, out TValue value) => + protected bool CheckExists(TLookup lookup, [MaybeNullWhen(false)] out TValue value) => cache.TryGetValue(lookup, out value); /// @@ -82,7 +81,7 @@ namespace osu.Game.Database /// The lookup to retrieve. /// An optional to cancel the operation. /// The computed value. - protected abstract Task ComputeValueAsync(TLookup lookup, CancellationToken token = default); + protected abstract Task ComputeValueAsync(TLookup lookup, CancellationToken token = default); private class MemoryCachingStatistics { diff --git a/osu.Game/Database/OnlineLookupCache.cs b/osu.Game/Database/OnlineLookupCache.cs index d9b37e2f29..3b54804fec 100644 --- a/osu.Game/Database/OnlineLookupCache.cs +++ b/osu.Game/Database/OnlineLookupCache.cs @@ -1,14 +1,11 @@ // 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 System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Game.Online.API; @@ -21,7 +18,7 @@ namespace osu.Game.Database where TRequest : APIRequest { [Resolved] - private IAPIProvider api { get; set; } + private IAPIProvider api { get; set; } = null!; /// /// Creates an to retrieve the values for a given collection of s. @@ -32,8 +29,7 @@ namespace osu.Game.Database /// /// Retrieves a list of s from a successful created by . /// - [CanBeNull] - protected abstract IEnumerable RetrieveResults(TRequest request); + protected abstract IEnumerable? RetrieveResults(TRequest request); /// /// Perform a lookup using the specified , populating a . @@ -41,8 +37,7 @@ namespace osu.Game.Database /// The ID to lookup. /// An optional cancellation token. /// The populated , or null if the value does not exist or the request could not be satisfied. - [ItemCanBeNull] - protected Task LookupAsync(TLookup id, CancellationToken token = default) => GetAsync(id, token); + protected Task LookupAsync(TLookup id, CancellationToken token = default) => GetAsync(id, token); /// /// Perform an API lookup on the specified , populating a . @@ -50,9 +45,9 @@ namespace osu.Game.Database /// The IDs to lookup. /// An optional cancellation token. /// The populated values. May include null results for failed retrievals. - protected Task LookupAsync(TLookup[] ids, CancellationToken token = default) + protected Task LookupAsync(TLookup[] ids, CancellationToken token = default) { - var lookupTasks = new List>(); + var lookupTasks = new List>(); foreach (var id in ids) { @@ -69,18 +64,18 @@ namespace osu.Game.Database } // cannot be sealed due to test usages (see TestUserLookupCache). - protected override async Task ComputeValueAsync(TLookup lookup, CancellationToken token = default) + protected override async Task ComputeValueAsync(TLookup lookup, CancellationToken token = default) => await queryValue(lookup).ConfigureAwait(false); - private readonly Queue<(TLookup id, TaskCompletionSource)> pendingTasks = new Queue<(TLookup, TaskCompletionSource)>(); - private Task pendingRequestTask; + private readonly Queue<(TLookup id, TaskCompletionSource)> pendingTasks = new Queue<(TLookup, TaskCompletionSource)>(); + private Task? pendingRequestTask; private readonly object taskAssignmentLock = new object(); - private Task queryValue(TLookup id) + private Task queryValue(TLookup id) { lock (taskAssignmentLock) { - var tcs = new TaskCompletionSource(); + var tcs = new TaskCompletionSource(); // Add to the queue. pendingTasks.Enqueue((id, tcs)); @@ -96,14 +91,14 @@ namespace osu.Game.Database private async Task performLookup() { // contains at most 50 unique IDs from tasks, which is used to perform the lookup. - var nextTaskBatch = new Dictionary>>(); + var nextTaskBatch = new Dictionary>>(); // Grab at most 50 unique IDs from the queue. lock (taskAssignmentLock) { while (pendingTasks.Count > 0 && nextTaskBatch.Count < 50) { - (TLookup id, TaskCompletionSource task) next = pendingTasks.Dequeue(); + (TLookup id, TaskCompletionSource task) next = pendingTasks.Dequeue(); // Perform a secondary check for existence, in case the value was queried in a previous batch. if (CheckExists(next.id, out var existing)) @@ -113,7 +108,7 @@ namespace osu.Game.Database if (nextTaskBatch.TryGetValue(next.id, out var tasks)) tasks.Add(next.task); else - nextTaskBatch[next.id] = new List> { next.task }; + nextTaskBatch[next.id] = new List> { next.task }; } } } diff --git a/osu.Game/Database/UserLookupCache.cs b/osu.Game/Database/UserLookupCache.cs index b1609fbf7b..e581d5ce82 100644 --- a/osu.Game/Database/UserLookupCache.cs +++ b/osu.Game/Database/UserLookupCache.cs @@ -1,13 +1,10 @@ // 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.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; -using JetBrains.Annotations; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -21,8 +18,7 @@ namespace osu.Game.Database /// The user to lookup. /// An optional cancellation token. /// The populated user, or null if the user does not exist or the request could not be satisfied. - [ItemCanBeNull] - public Task GetUserAsync(int userId, CancellationToken token = default) => LookupAsync(userId, token); + public Task GetUserAsync(int userId, CancellationToken token = default) => LookupAsync(userId, token); /// /// Perform an API lookup on the specified users, populating a model. @@ -30,10 +26,10 @@ namespace osu.Game.Database /// The users to lookup. /// An optional cancellation token. /// The populated users. May include null results for failed retrievals. - public Task GetUsersAsync(int[] userIds, CancellationToken token = default) => LookupAsync(userIds, token); + public Task GetUsersAsync(int[] userIds, CancellationToken token = default) => LookupAsync(userIds, token); protected override GetUsersRequest CreateRequest(IEnumerable ids) => new GetUsersRequest(ids.ToArray()); - protected override IEnumerable RetrieveResults(GetUsersRequest request) => request.Response?.Users; + protected override IEnumerable? RetrieveResults(GetUsersRequest request) => request.Response?.Users; } } diff --git a/osu.Game/Online/API/Requests/GetUsersRequest.cs b/osu.Game/Online/API/Requests/GetUsersRequest.cs index b57bb215aa..6f7e9c07d2 100644 --- a/osu.Game/Online/API/Requests/GetUsersRequest.cs +++ b/osu.Game/Online/API/Requests/GetUsersRequest.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; namespace osu.Game.Online.API.Requests From c5e77e13de1c81017459dc3e14306f4fe81bd7f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jun 2023 19:03:02 +0900 Subject: [PATCH 118/169] Add a very simple user cache to `ScoreImporter` --- osu.Game/Scoring/ScoreImporter.cs | 34 ++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreImporter.cs b/osu.Game/Scoring/ScoreImporter.cs index f69c1b9385..1c24cfbc85 100644 --- a/osu.Game/Scoring/ScoreImporter.cs +++ b/osu.Game/Scoring/ScoreImporter.cs @@ -146,16 +146,48 @@ namespace osu.Game.Scoring #pragma warning restore CS0618 } + // Very naive local caching to improve performance of large score imports (where the username is usually the same for most or all scores). + private readonly Dictionary usernameLookupCache = new Dictionary(); + protected override void PostImport(ScoreInfo model, Realm realm, ImportParameters parameters) { base.PostImport(model, realm, parameters); - var userRequest = new GetUserRequest(model.RealmUser.Username); + populateUserDetails(model); + } + + /// + /// Legacy replays only store a username. + /// This will populate a user ID during import. + /// + private void populateUserDetails(ScoreInfo model) + { + string username = model.RealmUser.Username; + + if (usernameLookupCache.TryGetValue(username, out var existing)) + { + model.User = existing; + return; + } + + var userRequest = new GetUserRequest(username); api.Perform(userRequest); if (userRequest.Response is APIUser user) + { + usernameLookupCache.TryAdd(username, new APIUser + { + // Because this is a permanent cache, let's only store the pieces we're interested in, + // rather than the full API response. If we start to store more than these three fields + // in realm, this should be undone. + Id = user.Id, + Username = user.Username, + CountryCode = user.CountryCode, + }); + model.User = user; + } } } } From 49c77a64efeb5886abe5de78aa820c7ded8a5558 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jun 2023 19:30:28 +0900 Subject: [PATCH 119/169] Apply more correct fix, factoring in minimum display ratio --- ...eatmapPanelBackgroundTextureLoaderStore.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs b/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs index 8f70f8a04f..110cebbe0e 100644 --- a/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs +++ b/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs @@ -17,9 +17,8 @@ namespace osu.Game.Beatmaps // If issues are found it's worth checking to make sure similar issues exist there. public class BeatmapPanelBackgroundTextureLoaderStore : IResourceStore { - // These numbers are taken from the draw visualiser size requirements for song select panel textures at extreme aspect ratios. - private const int max_height = 130; - private const int max_width = 1280; + // The aspect ratio of SetPanelBackground at its maximum size (very tall window). + private const float minimum_display_ratio = 512 / 80f; private readonly IResourceStore? textureStore; @@ -67,16 +66,18 @@ namespace osu.Game.Beatmaps Size size = image.Size(); - float aspectRatio = (float)size.Width / size.Height; - - int usableWidth = Math.Min((int)(max_width * aspectRatio), size.Width); - int usableHeight = Math.Min(max_height, size.Height); + // Assume that panel backgrounds are always displayed using `FillMode.Fill`. + // Also assume that all backgrounds are wider than they are tall, so the + // fill is always going to be based on width. + // + // We need to include enough height to make this work for all ratio panels are displayed at. + int usableHeight = (int)Math.Ceiling(size.Width * 1 / minimum_display_ratio); // Crop the centre region of the background for now. Rectangle cropRectangle = new Rectangle( - (size.Width - usableWidth) / 2, + 0, (size.Height - usableHeight) / 2, - usableWidth, + size.Width, usableHeight ); From 11694f35fe5f475e6321121c1708add60983eb70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 9 Jun 2023 13:47:35 +0200 Subject: [PATCH 120/169] Apply NRT in `MemoryCachingComponent` subclasses too --- osu.Game/Database/BeatmapLookupCache.cs | 10 +++------- osu.Game/Scoring/ScorePerformanceCache.cs | 9 +++------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/osu.Game/Database/BeatmapLookupCache.cs b/osu.Game/Database/BeatmapLookupCache.cs index d9bf0138dc..973c25ec4f 100644 --- a/osu.Game/Database/BeatmapLookupCache.cs +++ b/osu.Game/Database/BeatmapLookupCache.cs @@ -1,13 +1,10 @@ // 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.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; -using JetBrains.Annotations; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -21,8 +18,7 @@ namespace osu.Game.Database /// The beatmap to lookup. /// An optional cancellation token. /// The populated beatmap, or null if the beatmap does not exist or the request could not be satisfied. - [ItemCanBeNull] - public Task GetBeatmapAsync(int beatmapId, CancellationToken token = default) => LookupAsync(beatmapId, token); + public Task GetBeatmapAsync(int beatmapId, CancellationToken token = default) => LookupAsync(beatmapId, token); /// /// Perform an API lookup on the specified beatmaps, populating a model. @@ -30,10 +26,10 @@ namespace osu.Game.Database /// The beatmaps to lookup. /// An optional cancellation token. /// The populated beatmaps. May include null results for failed retrievals. - public Task GetBeatmapsAsync(int[] beatmapIds, CancellationToken token = default) => LookupAsync(beatmapIds, token); + public Task GetBeatmapsAsync(int[] beatmapIds, CancellationToken token = default) => LookupAsync(beatmapIds, token); protected override GetBeatmapsRequest CreateRequest(IEnumerable ids) => new GetBeatmapsRequest(ids.ToArray()); - protected override IEnumerable RetrieveResults(GetBeatmapsRequest request) => request.Response?.Beatmaps; + protected override IEnumerable? RetrieveResults(GetBeatmapsRequest request) => request.Response?.Beatmaps; } } diff --git a/osu.Game/Scoring/ScorePerformanceCache.cs b/osu.Game/Scoring/ScorePerformanceCache.cs index 17a0c0ea6a..bdbcfe4efe 100644 --- a/osu.Game/Scoring/ScorePerformanceCache.cs +++ b/osu.Game/Scoring/ScorePerformanceCache.cs @@ -1,12 +1,9 @@ // 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 System.Threading; using System.Threading.Tasks; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Database; @@ -21,7 +18,7 @@ namespace osu.Game.Scoring public partial class ScorePerformanceCache : MemoryCachingComponent { [Resolved] - private BeatmapDifficultyCache difficultyCache { get; set; } + private BeatmapDifficultyCache difficultyCache { get; set; } = null!; protected override bool CacheNullValues => false; @@ -30,10 +27,10 @@ namespace osu.Game.Scoring /// /// The score to do the calculation on. /// An optional to cancel the operation. - public Task CalculatePerformanceAsync([NotNull] ScoreInfo score, CancellationToken token = default) => + public Task CalculatePerformanceAsync(ScoreInfo score, CancellationToken token = default) => GetAsync(new PerformanceCacheLookup(score), token); - protected override async Task ComputeValueAsync(PerformanceCacheLookup lookup, CancellationToken token = default) + protected override async Task ComputeValueAsync(PerformanceCacheLookup lookup, CancellationToken token = default) { var score = lookup.ScoreInfo; From 58507291b9c213e94c6ca2337f379ecc01f68899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 9 Jun 2023 13:47:50 +0200 Subject: [PATCH 121/169] Apply NRT in `MemoryCachingComponent` test-only subclasses --- .../Online/TestSceneCurrentlyPlayingDisplay.cs | 12 +++++------- .../SongSelect/TestSceneBeatmapMetadataDisplay.cs | 14 ++++++++------ osu.Game/Tests/Visual/TestUserLookupCache.cs | 8 +++----- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs index 4f825e1191..885c00be80 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.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 System.Linq; using System.Threading; @@ -24,8 +22,8 @@ namespace osu.Game.Tests.Visual.Online { private readonly APIUser streamingUser = new APIUser { Id = 2, Username = "Test user" }; - private TestSpectatorClient spectatorClient; - private CurrentlyPlayingDisplay currentlyPlaying; + private TestSpectatorClient spectatorClient = null!; + private CurrentlyPlayingDisplay currentlyPlaying = null!; [SetUpSteps] public void SetUpSteps() @@ -88,13 +86,13 @@ namespace osu.Game.Tests.Visual.Online "pishifat" }; - protected override Task ComputeValueAsync(int lookup, CancellationToken token = default) + protected override Task ComputeValueAsync(int lookup, CancellationToken token = default) { // tests against failed lookups if (lookup == 13) - return Task.FromResult(null); + return Task.FromResult(null); - return Task.FromResult(new APIUser + return Task.FromResult(new APIUser { Id = lookup, Username = usernames[lookup % usernames.Length], diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index c2537cff79..379bd838cd 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -1,10 +1,9 @@ // 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 System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -24,10 +23,10 @@ namespace osu.Game.Tests.Visual.SongSelect { public partial class TestSceneBeatmapMetadataDisplay : OsuTestScene { - private BeatmapMetadataDisplay display; + private BeatmapMetadataDisplay display = null!; [Resolved] - private BeatmapManager manager { get; set; } + private BeatmapManager manager { get; set; } = null!; [Cached(typeof(BeatmapDifficultyCache))] private readonly TestBeatmapDifficultyCache testDifficultyCache = new TestBeatmapDifficultyCache(); @@ -121,7 +120,7 @@ namespace osu.Game.Tests.Visual.SongSelect private partial class TestBeatmapDifficultyCache : BeatmapDifficultyCache { - private TaskCompletionSource calculationBlocker; + private TaskCompletionSource? calculationBlocker; private bool blockCalculation; @@ -142,10 +141,13 @@ namespace osu.Game.Tests.Visual.SongSelect } } - public override async Task GetDifficultyAsync(IBeatmapInfo beatmapInfo, IRulesetInfo rulesetInfo = null, IEnumerable mods = null, CancellationToken cancellationToken = default) + public override async Task GetDifficultyAsync(IBeatmapInfo beatmapInfo, IRulesetInfo? rulesetInfo = null, IEnumerable? mods = null, CancellationToken cancellationToken = default) { if (blockCalculation) + { + Debug.Assert(calculationBlocker != null); await calculationBlocker.Task.ConfigureAwait(false); + } return await base.GetDifficultyAsync(beatmapInfo, rulesetInfo, mods, cancellationToken).ConfigureAwait(false); } diff --git a/osu.Game/Tests/Visual/TestUserLookupCache.cs b/osu.Game/Tests/Visual/TestUserLookupCache.cs index a3028f1a34..261e0fa75c 100644 --- a/osu.Game/Tests/Visual/TestUserLookupCache.cs +++ b/osu.Game/Tests/Visual/TestUserLookupCache.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.Threading; using System.Threading.Tasks; using osu.Game.Database; @@ -18,12 +16,12 @@ namespace osu.Game.Tests.Visual /// public const int UNRESOLVED_USER_ID = -1; - protected override Task ComputeValueAsync(int lookup, CancellationToken token = default) + protected override Task ComputeValueAsync(int lookup, CancellationToken token = default) { if (lookup == UNRESOLVED_USER_ID) - return Task.FromResult((APIUser)null); + return Task.FromResult(null); - return Task.FromResult(new APIUser + return Task.FromResult(new APIUser { Id = lookup, Username = $"User {lookup}" From 287229efd5f2f0633510daa691b11eb982742124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 9 Jun 2023 14:25:53 +0200 Subject: [PATCH 122/169] Fix code quality inspection --- osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs index 1d496cc636..cce633d46a 100644 --- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs @@ -67,7 +67,7 @@ namespace osu.Game.Online.Rooms { var beatmap = task.GetResultSafely(); - if (SelectedItem.Value?.Beatmap.OnlineID == beatmap.OnlineID) + if (beatmap != null && SelectedItem.Value?.Beatmap.OnlineID == beatmap.OnlineID) { selectedBeatmap = beatmap; beginTracking(); From 2823a62b5fe82c2232951198b73ac4253d7835f0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 9 Jun 2023 15:27:53 +0300 Subject: [PATCH 123/169] Avoid potential cropping error on very tall backgrounds --- osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs b/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs index 110cebbe0e..acd60b664d 100644 --- a/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs +++ b/osu.Game/Beatmaps/BeatmapPanelBackgroundTextureLoaderStore.cs @@ -73,6 +73,8 @@ namespace osu.Game.Beatmaps // We need to include enough height to make this work for all ratio panels are displayed at. int usableHeight = (int)Math.Ceiling(size.Width * 1 / minimum_display_ratio); + usableHeight = Math.Min(size.Height, usableHeight); + // Crop the centre region of the background for now. Rectangle cropRectangle = new Rectangle( 0, From 52412c673d45eb9bb1cbef5d6e54644082907b16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 9 Jun 2023 14:47:34 +0200 Subject: [PATCH 124/169] Bump replay version in encoder after Score V2 changes One release too late, but this may help in the future if we need to discern replays set with Score V2 from older lazer replays. --- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index a78ae24da2..87e1e79f87 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -28,9 +28,10 @@ namespace osu.Game.Scoring.Legacy /// /// /// 30000001: Appends to the end of scores. + /// 30000002: Score stored to replay calculated using the Score V2 algorithm. /// /// - public const int LATEST_VERSION = 30000001; + public const int LATEST_VERSION = 30000002; /// /// The first stable-compatible YYYYMMDD format version given to lazer usage of replays. From 4b8c4bd503075fa76a375ee90fc42838338eeb69 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 10 Jun 2023 01:51:53 +0900 Subject: [PATCH 125/169] Fix black jaggies around argon hitcircles --- osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs index 1c5cf49625..83de50184f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs @@ -62,7 +62,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon { outerFill = new Circle // renders white outer border and dark fill { - Size = Size, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + // Slightly inset to prevent bleeding outside the ring + Size = Size - new Vector2(0.5f), Alpha = withOuterFill ? 1 : 0, }, outerGradient = new Circle // renders the outer bright gradient From 7bf73463f1ae711066074ec704e25b711738ff4f Mon Sep 17 00:00:00 2001 From: Xinnoh <30382015+Xinnoh@users.noreply.github.com> Date: Fri, 9 Jun 2023 22:46:07 -0700 Subject: [PATCH 126/169] reduce argon kiai flashing --- osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonCirclePiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonCirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonCirclePiece.cs index d7e37899ce..e67417c384 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonCirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonCirclePiece.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon private const double pre_beat_transition_time = 80; - private const float flash_opacity = 0.3f; + private const float flash_opacity = 0.15f; private ColourInfo accentColour; From 0c42aa7f3b19d810d2eac06803697060b179922b Mon Sep 17 00:00:00 2001 From: Xinnoh <30382015+Xinnoh@users.noreply.github.com> Date: Fri, 9 Jun 2023 22:51:45 -0700 Subject: [PATCH 127/169] lower kiai pulse brightness --- osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs index bde502bbed..776996fdd2 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default private const double pre_beat_transition_time = 80; - private const float flash_opacity = 0.3f; + private const float flash_opacity = 0.15f; [Resolved] private DrawableHitObject drawableHitObject { get; set; } = null!; From 9224486ec7b5c4f868438b395458b5c5328ca69a Mon Sep 17 00:00:00 2001 From: Cootz Date: Sat, 10 Jun 2023 12:38:26 +0300 Subject: [PATCH 128/169] Add mod select overlay statics --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 ++ .../Overlays/Mods/ModSelectOverlayStatics.cs | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 osu.Game/Overlays/Mods/ModSelectOverlayStatics.cs diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 38ae8c68cb..a46de75273 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -103,6 +103,8 @@ namespace osu.Game.Overlays.Mods private readonly BindableBool customisationVisible = new BindableBool(); + protected readonly ModSelectOverlayStatics statics = new ModSelectOverlayStatics(); + private ModSettingsArea modSettingsArea = null!; private ColumnScrollContainer columnScroll = null!; private ColumnFlowContainer columnFlow = null!; diff --git a/osu.Game/Overlays/Mods/ModSelectOverlayStatics.cs b/osu.Game/Overlays/Mods/ModSelectOverlayStatics.cs new file mode 100644 index 0000000000..91f7f2180d --- /dev/null +++ b/osu.Game/Overlays/Mods/ModSelectOverlayStatics.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Configuration; + +namespace osu.Game.Overlays.Mods +{ + public class ModSelectOverlayStatics : InMemoryConfigManager + { + protected override void InitialiseDefaults() + { + SetDefault(Static.LastModSelectPanelSoundPlaybackTime, (double?)null); + } + } + + public enum Static + { + LastModSelectPanelSoundPlaybackTime + } +} From 09cd5580e133218739c2dfde63d1871b82e435c8 Mon Sep 17 00:00:00 2001 From: Cootz Date: Sat, 10 Jun 2023 13:13:34 +0300 Subject: [PATCH 129/169] Add sample playback time restrictions --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 3 ++- osu.Game/Overlays/Mods/ModSelectOverlayStatics.cs | 4 ++-- osu.Game/Overlays/Mods/ModSelectPanel.cs | 11 +++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index a46de75273..f2b61e3543 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -103,7 +103,8 @@ namespace osu.Game.Overlays.Mods private readonly BindableBool customisationVisible = new BindableBool(); - protected readonly ModSelectOverlayStatics statics = new ModSelectOverlayStatics(); + [Cached] + protected readonly ModSelectOverlayStatics Statics = new ModSelectOverlayStatics(); private ModSettingsArea modSettingsArea = null!; private ColumnScrollContainer columnScroll = null!; diff --git a/osu.Game/Overlays/Mods/ModSelectOverlayStatics.cs b/osu.Game/Overlays/Mods/ModSelectOverlayStatics.cs index 91f7f2180d..d9e66d8f4f 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlayStatics.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlayStatics.cs @@ -9,12 +9,12 @@ namespace osu.Game.Overlays.Mods { protected override void InitialiseDefaults() { - SetDefault(Static.LastModSelectPanelSoundPlaybackTime, (double?)null); + SetDefault(Static.LastModSelectPanelSamplePlaybackTime, (double?)null); } } public enum Static { - LastModSelectPanelSoundPlaybackTime + LastModSelectPanelSamplePlaybackTime } } diff --git a/osu.Game/Overlays/Mods/ModSelectPanel.cs b/osu.Game/Overlays/Mods/ModSelectPanel.cs index 81285833bd..42f72fbdca 100644 --- a/osu.Game/Overlays/Mods/ModSelectPanel.cs +++ b/osu.Game/Overlays/Mods/ModSelectPanel.cs @@ -44,6 +44,7 @@ namespace osu.Game.Overlays.Mods public const float CORNER_RADIUS = 7; public const float HEIGHT = 42; + public double SAMPLE_PLAYBACK_DELAY = 30; protected virtual float IdleSwitchWidth => 14; protected virtual float ExpandedSwitchWidth => 30; @@ -62,6 +63,9 @@ namespace osu.Game.Overlays.Mods [Resolved] protected OverlayColourProvider ColourProvider { get; private set; } = null!; + [Resolved] + protected ModSelectOverlayStatics ModOverlayStatics { get; private set; } = null!; + private readonly OsuSpriteText titleText; private readonly OsuSpriteText descriptionText; @@ -192,10 +196,17 @@ namespace osu.Game.Overlays.Mods if (samplePlaybackDisabled.Value) return; + double? lastPlaybackTime = ModOverlayStatics.Get(Static.LastModSelectPanelSamplePlaybackTime); + + if (lastPlaybackTime is not null && Time.Current - lastPlaybackTime < SAMPLE_PLAYBACK_DELAY) + return; + if (Active.Value) sampleOn?.Play(); else sampleOff?.Play(); + + ModOverlayStatics.SetValue(Static.LastModSelectPanelSamplePlaybackTime, Time.Current); } protected override bool OnHover(HoverEvent e) From 3bf900a4df9299a3cd9bcae8caa57b42372934a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 10 Jun 2023 10:30:10 +0200 Subject: [PATCH 130/169] Add failing test case for slider scenario --- .../Mods/TestSceneOsuModAutoplay.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs index 616a9c362d..32028823ae 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs @@ -1,22 +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.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.Scoring; +using osuTK; namespace osu.Game.Rulesets.Osu.Tests.Mods { public partial class TestSceneOsuModAutoplay : OsuModTestScene { + protected override bool AllowFail => true; + [Test] public void TestCursorPositionStoredToJudgement() { @@ -44,6 +51,36 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods FinalRate = { Value = 1.3 } }); + [Test] + public void TestPerfectScoreOnShortSliderWithRepeat() + { + AddStep("set score to standardised", () => LocalConfig.SetValue(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised)); + + CreateModTest(new ModTestData + { + Autoplay = true, + Beatmap = new Beatmap + { + HitObjects = new List + { + new Slider + { + StartTime = 500, + Position = new Vector2(256, 192), + Path = new SliderPath(new[] + { + new PathControlPoint(), + new PathControlPoint(new Vector2(0, 6.25f)) + }), + RepeatCount = 1, + SliderVelocity = 10 + } + } + }, + PassCondition = () => Player.ScoreProcessor.TotalScore.Value == 1_000_000 + }); + } + private void runSpmTest(Mod mod) { SpinnerSpmCalculator? spmCalculator = null; From 7e5533e2057a215f8bcbc9b9f1e9edc2282edcdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 10 Jun 2023 11:02:29 +0200 Subject: [PATCH 131/169] Fix not being able to receive full score for extremely short sliders with repeats Closes #23862. Score V2 is a scoring algorithm, which aside from the raw numerical values of each judgement, incorporates a combo component, wherein each judgement's "combo score" is derived from both the raw numerical value of the object and the current combo after the given judgement. In particular, this means that Score V2 is sensitive to the _order_ of judging objects, as if two objects with the same start time are judged using different ordering, they can end up having a different "combo score". The issue that this change is fixing is an instance of one such reordering. Upon inspection, it turned out that the simulated autoplay run, which is used to determine max possible score so that it can be standardised to 1 million again, was processing a slider repeat before a slider tail circle, while actual gameplay was processing the same slider repeat _after_ the slider tail circle. The cause of that behaviour is unfortunately due to `LegacyLastTick`. The sliders which cause the issue are extremely short. Stable had a behaviour, in which to provide leniency, slider tails were artificially offset back by 36ms. However, if the slider is not long enough to make this possible, the last tick is placed in the middle of the slider. If that slider also happens to have exactly 1 repeat, then this means that the last tick and the repeat have the same time instant. Because of the time equality, what begins to matter now is the _order_ of processing the elements of the drawable slider in the hierarchy. For the purposes of legacy skins, tail circles were moved below ticks in fce3eacd7de3254ce75619efaa2d15d59d564623 - but in this particular case, it means that the order of processing the slider elements is now inadvertently inverted, causing the entire debacle. While the fact that scoring depends on order of processing of visuals is suboptimal, there isn't a great way to address this without significant restructuring. Due to the structure of processing judgements currently in place, in which each judgement is processed independently from others by its corresponding drawable hit object, this is probably the best that can be done for the time being at least. --- .../Objects/Drawables/DrawableSlider.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 664a8146e7..01174d4d61 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -75,18 +75,24 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables [BackgroundDependencyLoader] private void load() { + tailContainer = new Container { RelativeSizeAxes = Axes.Both }; + AddRangeInternal(new Drawable[] { shakeContainer = new ShakeContainer { ShakeDuration = 30, RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + Children = new[] { Body = new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.SliderBody), _ => new DefaultSliderBody(), confineMode: ConfineMode.NoScaling), - tailContainer = new Container { RelativeSizeAxes = Axes.Both }, + // proxied here so that the tail is drawn under repeats/ticks - legacy skins rely on this + tailContainer.CreateProxy(), tickContainer = new Container { RelativeSizeAxes = Axes.Both }, repeatContainer = new Container { RelativeSizeAxes = Axes.Both }, + // actual tail container is placed here to ensure that tail hitobjects are processed after ticks/repeats. + // this is required for the correct operation of Score V2. + tailContainer, } }, // slider head is not included in shake as it handles hit detection, and handles its own shaking. From 31f370ec9b944ab994c3b4100b835f2a7d780fdb Mon Sep 17 00:00:00 2001 From: Cootz Date: Sat, 10 Jun 2023 13:50:45 +0300 Subject: [PATCH 132/169] Add comments for `ModSelectOverlayStatics` --- osu.Game/Overlays/Mods/ModSelectOverlayStatics.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlayStatics.cs b/osu.Game/Overlays/Mods/ModSelectOverlayStatics.cs index d9e66d8f4f..2d432b010f 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlayStatics.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlayStatics.cs @@ -5,6 +5,9 @@ using osu.Game.Configuration; namespace osu.Game.Overlays.Mods { + /// + /// Stores global mod overlay statics. These will not be stored after disposal of + /// public class ModSelectOverlayStatics : InMemoryConfigManager { protected override void InitialiseDefaults() @@ -15,6 +18,10 @@ namespace osu.Game.Overlays.Mods public enum Static { + /// + /// The last playback time in milliseconds of an on/off sample (from ). + /// Used to debounce on/off sounds game-wide to avoid volume saturation, especially in activating mod presets with many mods. + /// LastModSelectPanelSamplePlaybackTime } } From 502193e1c6f2cbb812c5b2f2af6392794e6af456 Mon Sep 17 00:00:00 2001 From: Cootz Date: Sat, 10 Jun 2023 13:55:37 +0300 Subject: [PATCH 133/169] Use debounce constant for select/deselect animation --- osu.Game/Overlays/Mods/ModColumn.cs | 2 +- osu.Game/Overlays/Mods/ModSelectPanel.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 5d9f616e5f..fe42cc0abf 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -176,7 +176,7 @@ namespace osu.Game.Overlays.Mods dequeuedAction(); // each time we play an animation, we decrease the time until the next animation (to ramp the visual and audible elements). - selectionDelay = Math.Max(30, selectionDelay * 0.8f); + selectionDelay = Math.Max(ModSelectPanel.SAMPLE_PLAYBACK_DELAY, selectionDelay * 0.8f); lastSelection = Time.Current; } else diff --git a/osu.Game/Overlays/Mods/ModSelectPanel.cs b/osu.Game/Overlays/Mods/ModSelectPanel.cs index 42f72fbdca..4c06378d89 100644 --- a/osu.Game/Overlays/Mods/ModSelectPanel.cs +++ b/osu.Game/Overlays/Mods/ModSelectPanel.cs @@ -44,7 +44,7 @@ namespace osu.Game.Overlays.Mods public const float CORNER_RADIUS = 7; public const float HEIGHT = 42; - public double SAMPLE_PLAYBACK_DELAY = 30; + public const double SAMPLE_PLAYBACK_DELAY = 30; protected virtual float IdleSwitchWidth => 14; protected virtual float ExpandedSwitchWidth => 30; From 274736b9c7fe0055c9da9c2401205d884c3ad9f7 Mon Sep 17 00:00:00 2001 From: Cootz Date: Sat, 10 Jun 2023 15:14:25 +0300 Subject: [PATCH 134/169] Fix missing dependency exception in unit tests --- osu.Game.Tests/Visual/UserInterface/TestSceneModColumn.cs | 3 +++ osu.Game.Tests/Visual/UserInterface/TestSceneModPanel.cs | 3 +++ .../Visual/UserInterface/TestSceneModPresetColumn.cs | 3 +++ osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs | 3 +++ 4 files changed, 12 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModColumn.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModColumn.cs index a11000214c..08110382df 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModColumn.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModColumn.cs @@ -27,6 +27,9 @@ namespace osu.Game.Tests.Visual.UserInterface [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); + [Cached] + private ModSelectOverlayStatics modOverlayStatics = new ModSelectOverlayStatics(); + [Resolved] private OsuConfigManager configManager { get; set; } = null!; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModPanel.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModPanel.cs index 64bdc167c2..f1283994d6 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModPanel.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModPanel.cs @@ -23,6 +23,9 @@ namespace osu.Game.Tests.Visual.UserInterface [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); + [Cached] + private ModSelectOverlayStatics modOverlayStatics = new ModSelectOverlayStatics(); + [Test] public void TestVariousPanels() { diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs index 3efdba8754..3bbc24ed23 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs @@ -37,6 +37,9 @@ namespace osu.Game.Tests.Visual.UserInterface [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); + [Cached] + private ModSelectOverlayStatics modOverlayStatics = new ModSelectOverlayStatics(); + [Cached(typeof(IDialogOverlay))] private readonly DialogOverlay dialogOverlay = new DialogOverlay(); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs index 35e352534b..f1899b9e29 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs @@ -26,6 +26,9 @@ namespace osu.Game.Tests.Visual.UserInterface [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); + [Cached] + private ModSelectOverlayStatics modOverlayStatics = new ModSelectOverlayStatics(); + [SetUpSteps] public void SetUpSteps() { From 61a9c6fd7eb29b964a566e8224fef41c464b86c2 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sat, 10 Jun 2023 11:53:45 -0700 Subject: [PATCH 135/169] Disable `Truncate` in `OsuSpriteText` Co-Authored-By: Salman Ahmed --- osu.Game/Graphics/Sprites/OsuSpriteText.cs | 7 +++++++ osu.Game/Graphics/Sprites/TruncatingSpriteText.cs | 3 ++- osu.Game/Overlays/Mods/ModSelectPanel.cs | 6 ++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Sprites/OsuSpriteText.cs b/osu.Game/Graphics/Sprites/OsuSpriteText.cs index e149e0abfb..afbec0eab4 100644 --- a/osu.Game/Graphics/Sprites/OsuSpriteText.cs +++ b/osu.Game/Graphics/Sprites/OsuSpriteText.cs @@ -3,12 +3,19 @@ #nullable disable +using System; using osu.Framework.Graphics.Sprites; namespace osu.Game.Graphics.Sprites { public partial class OsuSpriteText : SpriteText { + [Obsolete("Use TruncatingSpriteText instead.")] + public new bool Truncate + { + set => throw new InvalidOperationException($"Use {nameof(TruncatingSpriteText)} instead."); + } + public OsuSpriteText() { Shadow = true; diff --git a/osu.Game/Graphics/Sprites/TruncatingSpriteText.cs b/osu.Game/Graphics/Sprites/TruncatingSpriteText.cs index da0dbd49d2..229fad29f9 100644 --- a/osu.Game/Graphics/Sprites/TruncatingSpriteText.cs +++ b/osu.Game/Graphics/Sprites/TruncatingSpriteText.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; namespace osu.Game.Graphics.Sprites @@ -14,7 +15,7 @@ namespace osu.Game.Graphics.Sprites public TruncatingSpriteText() { - Truncate = true; + ((SpriteText)this).Truncate = true; } } } diff --git a/osu.Game/Overlays/Mods/ModSelectPanel.cs b/osu.Game/Overlays/Mods/ModSelectPanel.cs index 81285833bd..6179f31637 100644 --- a/osu.Game/Overlays/Mods/ModSelectPanel.cs +++ b/osu.Game/Overlays/Mods/ModSelectPanel.cs @@ -118,22 +118,20 @@ namespace osu.Game.Overlays.Mods Direction = FillDirection.Vertical, Children = new[] { - titleText = new OsuSpriteText + titleText = new TruncatingSpriteText { Font = OsuFont.TorusAlternate.With(size: 18, weight: FontWeight.SemiBold), RelativeSizeAxes = Axes.X, - Truncate = true, Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), Margin = new MarginPadding { Left = -18 * ShearedOverlayContainer.SHEAR } }, - descriptionText = new OsuSpriteText + descriptionText = new TruncatingSpriteText { Font = OsuFont.Default.With(size: 12), RelativeSizeAxes = Axes.X, - Truncate = true, Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0) } } From 0d51e4f6cea0aeded84760a17156f0331c89f3bf Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sat, 10 Jun 2023 12:24:58 -0700 Subject: [PATCH 136/169] Fix mod select panels having conflicting tooltips Going simple with a bool instead of making `TooltipText` init-able, as the current cases will just init `string.Empty`. And not sure if we want custom tooltip text in the future. --- osu.Game/Graphics/Sprites/TruncatingSpriteText.cs | 4 +++- osu.Game/Overlays/Mods/ModSelectPanel.cs | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Sprites/TruncatingSpriteText.cs b/osu.Game/Graphics/Sprites/TruncatingSpriteText.cs index 229fad29f9..254e708183 100644 --- a/osu.Game/Graphics/Sprites/TruncatingSpriteText.cs +++ b/osu.Game/Graphics/Sprites/TruncatingSpriteText.cs @@ -9,9 +9,11 @@ namespace osu.Game.Graphics.Sprites { public sealed partial class TruncatingSpriteText : OsuSpriteText, IHasTooltip { + public bool ShowTooltip { get; init; } = true; + public LocalisableString TooltipText => Text; - public override bool HandlePositionalInput => IsTruncated; + public override bool HandlePositionalInput => IsTruncated && ShowTooltip; public TruncatingSpriteText() { diff --git a/osu.Game/Overlays/Mods/ModSelectPanel.cs b/osu.Game/Overlays/Mods/ModSelectPanel.cs index 6179f31637..09f5e5ced7 100644 --- a/osu.Game/Overlays/Mods/ModSelectPanel.cs +++ b/osu.Game/Overlays/Mods/ModSelectPanel.cs @@ -126,13 +126,15 @@ namespace osu.Game.Overlays.Mods Margin = new MarginPadding { Left = -18 * ShearedOverlayContainer.SHEAR - } + }, + ShowTooltip = false, }, descriptionText = new TruncatingSpriteText { Font = OsuFont.Default.With(size: 12), RelativeSizeAxes = Axes.X, - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0) + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + ShowTooltip = false, } } } From 4819a2879190b80c87c8822f451abb45b1d90cfc Mon Sep 17 00:00:00 2001 From: Cootz Date: Sun, 11 Jun 2023 13:53:17 +0300 Subject: [PATCH 137/169] Move mod overlay statics to `SessionStatics` --- .../UserInterface/TestSceneModColumn.cs | 3 --- .../Visual/UserInterface/TestSceneModPanel.cs | 3 --- .../UserInterface/TestSceneModPresetColumn.cs | 3 --- .../UserInterface/TestSceneModPresetPanel.cs | 3 --- osu.Game/Configuration/SessionStatics.cs | 8 ++++++ osu.Game/Overlays/Mods/ModSelectOverlay.cs | 3 --- .../Overlays/Mods/ModSelectOverlayStatics.cs | 27 ------------------- osu.Game/Overlays/Mods/ModSelectPanel.cs | 7 ++--- 8 files changed, 12 insertions(+), 45 deletions(-) delete mode 100644 osu.Game/Overlays/Mods/ModSelectOverlayStatics.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModColumn.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModColumn.cs index 08110382df..a11000214c 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModColumn.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModColumn.cs @@ -27,9 +27,6 @@ namespace osu.Game.Tests.Visual.UserInterface [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); - [Cached] - private ModSelectOverlayStatics modOverlayStatics = new ModSelectOverlayStatics(); - [Resolved] private OsuConfigManager configManager { get; set; } = null!; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModPanel.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModPanel.cs index f1283994d6..64bdc167c2 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModPanel.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModPanel.cs @@ -23,9 +23,6 @@ namespace osu.Game.Tests.Visual.UserInterface [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); - [Cached] - private ModSelectOverlayStatics modOverlayStatics = new ModSelectOverlayStatics(); - [Test] public void TestVariousPanels() { diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs index 3bbc24ed23..3efdba8754 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs @@ -37,9 +37,6 @@ namespace osu.Game.Tests.Visual.UserInterface [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); - [Cached] - private ModSelectOverlayStatics modOverlayStatics = new ModSelectOverlayStatics(); - [Cached(typeof(IDialogOverlay))] private readonly DialogOverlay dialogOverlay = new DialogOverlay(); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs index f1899b9e29..35e352534b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs @@ -26,9 +26,6 @@ namespace osu.Game.Tests.Visual.UserInterface [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); - [Cached] - private ModSelectOverlayStatics modOverlayStatics = new ModSelectOverlayStatics(); - [SetUpSteps] public void SetUpSteps() { diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index 276563e163..5e2f0c2128 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -6,6 +6,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; +using osu.Game.Overlays.Mods; namespace osu.Game.Configuration { @@ -21,6 +22,7 @@ namespace osu.Game.Configuration SetDefault(Static.LowBatteryNotificationShownOnce, false); SetDefault(Static.FeaturedArtistDisclaimerShownOnce, false); SetDefault(Static.LastHoverSoundPlaybackTime, (double?)null); + SetDefault(Static.LastModSelectPanelSamplePlaybackTime, (double?)null); SetDefault(Static.SeasonalBackgrounds, null); } @@ -56,5 +58,11 @@ namespace osu.Game.Configuration /// Used to debounce hover sounds game-wide to avoid volume saturation, especially in scrolling views with many UI controls like . /// LastHoverSoundPlaybackTime, + + /// + /// The last playback time in milliseconds of an on/off sample (from ). + /// Used to debounce on/off sounds game-wide to avoid volume saturation, especially in activating mod presets with many mods. + /// + LastModSelectPanelSamplePlaybackTime } } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index f2b61e3543..38ae8c68cb 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -103,9 +103,6 @@ namespace osu.Game.Overlays.Mods private readonly BindableBool customisationVisible = new BindableBool(); - [Cached] - protected readonly ModSelectOverlayStatics Statics = new ModSelectOverlayStatics(); - private ModSettingsArea modSettingsArea = null!; private ColumnScrollContainer columnScroll = null!; private ColumnFlowContainer columnFlow = null!; diff --git a/osu.Game/Overlays/Mods/ModSelectOverlayStatics.cs b/osu.Game/Overlays/Mods/ModSelectOverlayStatics.cs deleted file mode 100644 index 2d432b010f..0000000000 --- a/osu.Game/Overlays/Mods/ModSelectOverlayStatics.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Configuration; - -namespace osu.Game.Overlays.Mods -{ - /// - /// Stores global mod overlay statics. These will not be stored after disposal of - /// - public class ModSelectOverlayStatics : InMemoryConfigManager - { - protected override void InitialiseDefaults() - { - SetDefault(Static.LastModSelectPanelSamplePlaybackTime, (double?)null); - } - } - - public enum Static - { - /// - /// The last playback time in milliseconds of an on/off sample (from ). - /// Used to debounce on/off sounds game-wide to avoid volume saturation, especially in activating mod presets with many mods. - /// - LastModSelectPanelSamplePlaybackTime - } -} diff --git a/osu.Game/Overlays/Mods/ModSelectPanel.cs b/osu.Game/Overlays/Mods/ModSelectPanel.cs index 4c06378d89..be07759984 100644 --- a/osu.Game/Overlays/Mods/ModSelectPanel.cs +++ b/osu.Game/Overlays/Mods/ModSelectPanel.cs @@ -14,6 +14,7 @@ using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Framework.Utils; using osu.Game.Audio; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -64,7 +65,7 @@ namespace osu.Game.Overlays.Mods protected OverlayColourProvider ColourProvider { get; private set; } = null!; [Resolved] - protected ModSelectOverlayStatics ModOverlayStatics { get; private set; } = null!; + protected SessionStatics Statics { get; private set; } = null!; private readonly OsuSpriteText titleText; private readonly OsuSpriteText descriptionText; @@ -196,7 +197,7 @@ namespace osu.Game.Overlays.Mods if (samplePlaybackDisabled.Value) return; - double? lastPlaybackTime = ModOverlayStatics.Get(Static.LastModSelectPanelSamplePlaybackTime); + double? lastPlaybackTime = Statics.Get(Static.LastModSelectPanelSamplePlaybackTime); if (lastPlaybackTime is not null && Time.Current - lastPlaybackTime < SAMPLE_PLAYBACK_DELAY) return; @@ -206,7 +207,7 @@ namespace osu.Game.Overlays.Mods else sampleOff?.Play(); - ModOverlayStatics.SetValue(Static.LastModSelectPanelSamplePlaybackTime, Time.Current); + Statics.SetValue(Static.LastModSelectPanelSamplePlaybackTime, Time.Current); } protected override bool OnHover(HoverEvent e) From 82b7e570cddc271e36360de10ee447d129733114 Mon Sep 17 00:00:00 2001 From: yhsphd Date: Sun, 11 Jun 2023 22:43:06 +0900 Subject: [PATCH 138/169] Add a checkbox to toggle line breaking for each mod in mappool screen --- .../Screens/MapPool/MapPoolScreen.cs | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index f0e34d78c3..fcb0c4d70b 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Framework.Threading; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Settings; using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Models; @@ -25,6 +26,7 @@ namespace osu.Game.Tournament.Screens.MapPool public partial class MapPoolScreen : TournamentMatchScreen { private readonly FillFlowContainer> mapFlows; + private TournamentMatch currentMatch; [Resolved(canBeNull: true)] private TournamentSceneManager sceneManager { get; set; } @@ -37,6 +39,8 @@ namespace osu.Game.Tournament.Screens.MapPool private readonly OsuButton buttonRedPick; private readonly OsuButton buttonBluePick; + private readonly SettingsCheckbox chkBoxLineBreak; + public MapPoolScreen() { InternalChildren = new Drawable[] @@ -98,6 +102,15 @@ namespace osu.Game.Tournament.Screens.MapPool Action = reset }, new ControlPanel.Spacer(), + new TournamentSpriteText + { + Text = "Each modpool takes" + }, + new TournamentSpriteText + { + Text = "different row" + }, + chkBoxLineBreak = new SettingsCheckbox() }, } }; @@ -107,6 +120,8 @@ namespace osu.Game.Tournament.Screens.MapPool private void load(MatchIPCInfo ipc) { ipc.Beatmap.BindValueChanged(beatmapChanged); + chkBoxLineBreak.Current.Value = true; + chkBoxLineBreak.Current.BindValueChanged(_ => rearrangeMappool()); } private void beatmapChanged(ValueChangedEvent beatmap) @@ -213,37 +228,42 @@ namespace osu.Game.Tournament.Screens.MapPool protected override void CurrentMatchChanged(ValueChangedEvent match) { base.CurrentMatchChanged(match); + currentMatch = match.NewValue; + rearrangeMappool(); + } + private void rearrangeMappool() + { mapFlows.Clear(); - if (match.NewValue == null) + if (currentMatch == null) return; - int totalRows = 0; - if (match.NewValue.Round.Value != null) + if (currentMatch.Round.Value != null) { FillFlowContainer currentFlow = null; string currentMod = null; - int flowCount = 0; - foreach (var b in match.NewValue.Round.Value.Beatmaps) + foreach (var b in currentMatch.Round.Value.Beatmaps) { if (currentFlow == null || currentMod != b.Mods) { - mapFlows.Add(currentFlow = new FillFlowContainer + if (chkBoxLineBreak.Current.Value || currentFlow == null) { - Spacing = new Vector2(10, 5), - Direction = FillDirection.Full, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y - }); + mapFlows.Add(currentFlow = new FillFlowContainer + { + Spacing = new Vector2(10, 5), + Direction = FillDirection.Full, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }); + totalRows++; + flowCount = 0; + } currentMod = b.Mods; - - totalRows++; - flowCount = 0; } if (++flowCount > 2) From b986d1cee1d679fd893704e5b010a8464ab785b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jun 2023 14:18:26 +0900 Subject: [PATCH 139/169] Rename variables to give more context --- osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonCirclePiece.cs | 4 ++-- osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonCirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonCirclePiece.cs index e67417c384..cecb99c690 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonCirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonCirclePiece.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon private const double pre_beat_transition_time = 80; - private const float flash_opacity = 0.15f; + private const float kiai_flash_opacity = 0.15f; private ColourInfo accentColour; @@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon if (drawableHitObject.State.Value == ArmedState.Idle) { flash - .FadeTo(flash_opacity) + .FadeTo(kiai_flash_opacity) .Then() .FadeOut(timingPoint.BeatLength * 0.75, Easing.OutSine); } diff --git a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs index 776996fdd2..b3833d372c 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default private const double pre_beat_transition_time = 80; - private const float flash_opacity = 0.15f; + private const float kiai_flash_opacity = 0.15f; [Resolved] private DrawableHitObject drawableHitObject { get; set; } = null!; @@ -187,7 +187,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default if (drawableHitObject.State.Value == ArmedState.Idle) { flashBox - .FadeTo(flash_opacity) + .FadeTo(kiai_flash_opacity) .Then() .FadeOut(timingPoint.BeatLength * 0.75, Easing.OutSine); } From 03a5b701e907d46861accc439a629e7778df1e79 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jun 2023 14:20:54 +0900 Subject: [PATCH 140/169] Fix incorrect inline comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs index 83de50184f..b0492ca6df 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon InternalChildren = new Drawable[] { - outerFill = new Circle // renders white outer border and dark fill + outerFill = new Circle // renders dark fill { Anchor = Anchor.Centre, Origin = Anchor.Centre, From bddb91dc0af428928485082c5db73560d61bbfbe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jun 2023 14:23:46 +0900 Subject: [PATCH 141/169] Adjust adjustment to 1px based on review feedback Also split out `Size` variable for clarity --- .../Skinning/Argon/ArgonMainCirclePiece.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs index b0492ca6df..3427031dc8 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs @@ -48,12 +48,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon private Bindable configHitLighting = null!; + private static readonly Vector2 circle_size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); + [Resolved] private DrawableHitObject drawableObject { get; set; } = null!; public ArgonMainCirclePiece(bool withOuterFill) { - Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); + Size = circle_size; Anchor = Anchor.Centre; Origin = Anchor.Centre; @@ -65,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon Anchor = Anchor.Centre, Origin = Anchor.Centre, // Slightly inset to prevent bleeding outside the ring - Size = Size - new Vector2(0.5f), + Size = circle_size - new Vector2(1), Alpha = withOuterFill ? 1 : 0, }, outerGradient = new Circle // renders the outer bright gradient @@ -91,7 +93,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon Masking = true, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = Size, + Size = circle_size, Child = new KiaiFlash { RelativeSizeAxes = Axes.Both, From 1e774fc0170585b67d2a0d8b01500998a884c3f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jun 2023 14:32:55 +0900 Subject: [PATCH 142/169] Refactor implementation to roughly match existing `HoverSampleDebounceComponent` --- osu.Game/Overlays/Mods/ModSelectPanel.cs | 28 +++++++++++++----------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectPanel.cs b/osu.Game/Overlays/Mods/ModSelectPanel.cs index be07759984..be01d239b6 100644 --- a/osu.Game/Overlays/Mods/ModSelectPanel.cs +++ b/osu.Game/Overlays/Mods/ModSelectPanel.cs @@ -45,6 +45,7 @@ namespace osu.Game.Overlays.Mods public const float CORNER_RADIUS = 7; public const float HEIGHT = 42; + public const double SAMPLE_PLAYBACK_DELAY = 30; protected virtual float IdleSwitchWidth => 14; @@ -64,9 +65,6 @@ namespace osu.Game.Overlays.Mods [Resolved] protected OverlayColourProvider ColourProvider { get; private set; } = null!; - [Resolved] - protected SessionStatics Statics { get; private set; } = null!; - private readonly OsuSpriteText titleText; private readonly OsuSpriteText descriptionText; @@ -74,6 +72,8 @@ namespace osu.Game.Overlays.Mods private Sample? sampleOff; private Sample? sampleOn; + private Bindable lastPlaybackTime = null!; + protected ModSelectPanel() { RelativeSizeAxes = Axes.X; @@ -168,13 +168,15 @@ namespace osu.Game.Overlays.Mods protected abstract void Deselect(); [BackgroundDependencyLoader] - private void load(AudioManager audio, ISamplePlaybackDisabler? samplePlaybackDisabler) + private void load(AudioManager audio, SessionStatics statics, ISamplePlaybackDisabler? samplePlaybackDisabler) { sampleOn = audio.Samples.Get(@"UI/check-on"); sampleOff = audio.Samples.Get(@"UI/check-off"); if (samplePlaybackDisabler != null) ((IBindable)samplePlaybackDisabled).BindTo(samplePlaybackDisabler.SamplePlaybackDisabled); + + lastPlaybackTime = statics.GetBindable(Static.LastHoverSoundPlaybackTime); } protected sealed override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(sampleSet); @@ -197,17 +199,17 @@ namespace osu.Game.Overlays.Mods if (samplePlaybackDisabled.Value) return; - double? lastPlaybackTime = Statics.Get(Static.LastModSelectPanelSamplePlaybackTime); + bool enoughTimePassedSinceLastPlayback = !lastPlaybackTime.Value.HasValue || Time.Current - lastPlaybackTime.Value >= SAMPLE_PLAYBACK_DELAY; - if (lastPlaybackTime is not null && Time.Current - lastPlaybackTime < SAMPLE_PLAYBACK_DELAY) - return; + if (enoughTimePassedSinceLastPlayback) + { + if (Active.Value) + sampleOn?.Play(); + else + sampleOff?.Play(); - if (Active.Value) - sampleOn?.Play(); - else - sampleOff?.Play(); - - Statics.SetValue(Static.LastModSelectPanelSamplePlaybackTime, Time.Current); + lastPlaybackTime.Value = Time.Current; + } } protected override bool OnHover(HoverEvent e) From 8864014af84f1dcb6932d6f4573b1c9dae6d38f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jun 2023 15:25:12 +0900 Subject: [PATCH 143/169] Add xmldoc --- osu.Game/Graphics/Sprites/TruncatingSpriteText.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Graphics/Sprites/TruncatingSpriteText.cs b/osu.Game/Graphics/Sprites/TruncatingSpriteText.cs index 254e708183..46abdbf09e 100644 --- a/osu.Game/Graphics/Sprites/TruncatingSpriteText.cs +++ b/osu.Game/Graphics/Sprites/TruncatingSpriteText.cs @@ -7,8 +7,14 @@ using osu.Framework.Localisation; namespace osu.Game.Graphics.Sprites { + /// + /// A derived version of which automatically shows non-truncated text in tooltip when required. + /// public sealed partial class TruncatingSpriteText : OsuSpriteText, IHasTooltip { + /// + /// Whether a tooltip should be shown with non-truncated text on hover. + /// public bool ShowTooltip { get; init; } = true; public LocalisableString TooltipText => Text; From 99f93f518555bebd6c024230b359addca0954181 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jun 2023 15:31:22 +0900 Subject: [PATCH 144/169] Add comment mentioning why `ShowTooltip` is disabled in mod select panels --- osu.Game/Overlays/Mods/ModSelectPanel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectPanel.cs b/osu.Game/Overlays/Mods/ModSelectPanel.cs index 09f5e5ced7..d6916c49da 100644 --- a/osu.Game/Overlays/Mods/ModSelectPanel.cs +++ b/osu.Game/Overlays/Mods/ModSelectPanel.cs @@ -127,14 +127,14 @@ namespace osu.Game.Overlays.Mods { Left = -18 * ShearedOverlayContainer.SHEAR }, - ShowTooltip = false, + ShowTooltip = false, // Tooltip is handled by `IncompatibilityDisplayingModPanel`. }, descriptionText = new TruncatingSpriteText { Font = OsuFont.Default.With(size: 12), RelativeSizeAxes = Axes.X, Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), - ShowTooltip = false, + ShowTooltip = false, // Tooltip is handled by `IncompatibilityDisplayingModPanel`. } } } From a29f6772cd57f5194a07fb717a333bcd6d3f0b62 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jun 2023 17:22:11 +0900 Subject: [PATCH 145/169] Fix storyboard being null if file doesn't exist It's expected that `WorkingBeatmap.Storyboard` is non-null. An example fail case is in `BeatmapBackgroundWithStoryboard.load`. --- osu.Game/Beatmaps/WorkingBeatmapCache.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmapCache.cs b/osu.Game/Beatmaps/WorkingBeatmapCache.cs index 0f3d61f527..78eed626f2 100644 --- a/osu.Game/Beatmaps/WorkingBeatmapCache.cs +++ b/osu.Game/Beatmaps/WorkingBeatmapCache.cs @@ -264,7 +264,7 @@ namespace osu.Game.Beatmaps if (beatmapFileStream == null) { Logger.Log($"Beatmap failed to load (file {BeatmapInfo.Path} not found on disk at expected location {fileStorePath})", level: LogLevel.Error); - return null; + return new Storyboard(); } using (var reader = new LineBufferedReader(beatmapFileStream)) From f30c1a564fa074b4ffc1167cb34acdd35b4e68e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jun 2023 01:08:51 +0900 Subject: [PATCH 146/169] Add basic setup for score migration --- osu.Game/Database/RealmAccess.cs | 50 ++++++++- .../StandardisedScoreMigrationTools.cs | 100 ++++++++++++++++++ 2 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Database/StandardisedScoreMigrationTools.cs diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 63ab18db8c..4c55a408c4 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -28,7 +28,10 @@ using osu.Game.IO.Legacy; using osu.Game.Models; using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Scoring.Legacy; using osu.Game.Skinning; @@ -76,8 +79,9 @@ namespace osu.Game.Database /// 26 2023-02-05 Added BeatmapHash to ScoreInfo. /// 27 2023-06-06 Added EditorTimestamp to BeatmapInfo. /// 28 2023-06-08 Added IsLegacyScore to ScoreInfo, parsed from replay files. + /// 29 2023-06-12 Run migration of old lazer scores to be best-effort in the new scoring number space. No actual realm changes. /// - private const int schema_version = 28; + private const int schema_version = 29; /// /// Lock object which is held during sections, blocking realm retrieval during blocking periods. @@ -930,6 +934,28 @@ namespace osu.Game.Database break; } + + case 29: + { + var scores = migration.NewRealm + .All() + .Where(s => !s.IsLegacyScore); + + foreach (var score in scores) + { + // Recalculate the old-style standardised score to see if this was an old lazer score. + long oldStandardised = StandardisedScoreMigrationTools.GetOldStandardised(score); + + if (oldStandardised == score.TotalScore) + { + long calculatedNew = StandardisedScoreMigrationTools.GetNewStandardised(score); + Logger.Log($"Converting score {score.Rank} {score.Accuracy:P1} {score.TotalScore} -> {calculatedNew}"); + score.TotalScore = calculatedNew; + } + } + + break; + } } } @@ -1151,4 +1177,26 @@ namespace osu.Game.Database } } } + + internal class FakeHit : HitObject + { + private readonly Judgement judgement; + + public override Judgement CreateJudgement() => judgement; + + public FakeHit(Judgement judgement) + { + this.judgement = judgement; + } + } + + internal class FakeJudgement : Judgement + { + public override HitResult MaxResult { get; } + + public FakeJudgement(HitResult result) + { + MaxResult = result; + } + } } diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs new file mode 100644 index 0000000000..3133095df6 --- /dev/null +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -0,0 +1,100 @@ +// 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.Diagnostics; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; + +namespace osu.Game.Database +{ + public static class StandardisedScoreMigrationTools + { + public static long GetNewStandardised(ScoreInfo score) + { + var processor = score.Ruleset.CreateInstance().CreateScoreProcessor(); + + var beatmap = new Beatmap(); + + var maximumJudgements = score.MaximumStatistics + .Where(kvp => kvp.Key.AffectsCombo()) + .OrderByDescending(kvp => Judgement.ToNumericResult(kvp.Key)) + .SelectMany(kvp => Enumerable.Repeat(new FakeJudgement(kvp.Key), kvp.Value)) + .ToList(); + + // This is a list of all results, ordered from best to worst. + // We are constructing a "best possible" score from the statistics provided because it's the best we can do. + List sortedHits = score.Statistics + .Where(kvp => kvp.Key.AffectsCombo() && kvp.Key != HitResult.Miss && kvp.Key != HitResult.LargeTickMiss) + .OrderByDescending(kvp => Judgement.ToNumericResult(kvp.Key)) + .SelectMany(kvp => Enumerable.Repeat(kvp.Key, kvp.Value)) + .ToList(); + + foreach (var judgement in maximumJudgements) + beatmap.HitObjects.Add(new FakeHit(judgement)); + + processor.ApplyBeatmap(beatmap); + + Queue misses = new Queue(score.Statistics + .Where(kvp => kvp.Key == HitResult.Miss || kvp.Key == HitResult.LargeTickMiss) + .SelectMany(kvp => Enumerable.Repeat(kvp.Key, kvp.Value))); + + int maxJudgementIndex = 0; + + foreach (var result in sortedHits) + { + if (processor.Combo.Value == score.MaxCombo) + { + processor.ApplyResult(new JudgementResult(null!, maximumJudgements[maxJudgementIndex++]) + { + Type = misses.Dequeue(), + }); + } + + // TODO: pass a Judgement with correct MaxResult + processor.ApplyResult(new JudgementResult(null!, maximumJudgements[maxJudgementIndex++]) + { + Type = result + }); + } + + var bonusHits = score.Statistics + .Where(kvp => kvp.Key.IsBonus()) + .SelectMany(kvp => Enumerable.Repeat(kvp.Key, kvp.Value)); + + foreach (var result in bonusHits) + processor.ApplyResult(new JudgementResult(null!, new FakeJudgement(result)) { Type = result }); + + Debug.Assert(processor.HighestCombo.Value == score.MaxCombo); + + return processor.TotalScore.Value; + } + + public static long GetOldStandardised(ScoreInfo score) + { + double accuracyScore = + (double)score.Statistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value) + / score.MaximumStatistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value); + double comboScore = (double)score.MaxCombo / score.MaximumStatistics.Where(kvp => kvp.Key.AffectsCombo()).Sum(kvp => kvp.Value); + double bonusScore = score.Statistics.Where(kvp => kvp.Key.IsBonus()).Sum(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value); + + double accuracyPortion = 0.3; + + switch (score.RulesetID) + { + case 1: + accuracyPortion = 0.75; + break; + + case 3: + accuracyPortion = 0.99; + break; + } + + return (long)(1000000 * (accuracyPortion * accuracyScore + (1 - accuracyPortion) * comboScore) + bonusScore); + } + } +} From d19f8997fc2288df0ec333fca0369be974f0c0d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jun 2023 01:39:38 +0900 Subject: [PATCH 147/169] Account for scores which don't have correct maximum statistics populated --- .../StandardisedScoreMigrationTools.cs | 65 +++++++++++++++++-- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 3133095df6..e8f1fd640b 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -15,10 +15,13 @@ namespace osu.Game.Database { public static long GetNewStandardised(ScoreInfo score) { - var processor = score.Ruleset.CreateInstance().CreateScoreProcessor(); + var ruleset = score.Ruleset.CreateInstance(); + var processor = ruleset.CreateScoreProcessor(); var beatmap = new Beatmap(); + HitResult maxRulesetJudgement = ruleset.GetHitResults().First().result; + var maximumJudgements = score.MaximumStatistics .Where(kvp => kvp.Key.AffectsCombo()) .OrderByDescending(kvp => Judgement.ToNumericResult(kvp.Key)) @@ -28,11 +31,20 @@ namespace osu.Game.Database // This is a list of all results, ordered from best to worst. // We are constructing a "best possible" score from the statistics provided because it's the best we can do. List sortedHits = score.Statistics - .Where(kvp => kvp.Key.AffectsCombo() && kvp.Key != HitResult.Miss && kvp.Key != HitResult.LargeTickMiss) + .Where(kvp => kvp.Key.AffectsCombo()) .OrderByDescending(kvp => Judgement.ToNumericResult(kvp.Key)) .SelectMany(kvp => Enumerable.Repeat(kvp.Key, kvp.Value)) .ToList(); + if (maximumJudgements.Count != sortedHits.Count) + { + // Older scores may not have maximum judgements populated correctly. + // In this case we need to fill them. + maximumJudgements = sortedHits + .Select(r => new FakeJudgement(getMaxJudgementFor(r, maxRulesetJudgement))) + .ToList(); + } + foreach (var judgement in maximumJudgements) beatmap.HitObjects.Add(new FakeHit(judgement)); @@ -46,15 +58,29 @@ namespace osu.Game.Database foreach (var result in sortedHits) { + // misses are handled from the queue. + if (result == HitResult.Miss || result == HitResult.LargeTickMiss) + continue; + if (processor.Combo.Value == score.MaxCombo) { - processor.ApplyResult(new JudgementResult(null!, maximumJudgements[maxJudgementIndex++]) + if (misses.Count > 0) { - Type = misses.Dequeue(), - }); + processor.ApplyResult(new JudgementResult(null!, maximumJudgements[maxJudgementIndex++]) + { + Type = misses.Dequeue(), + }); + } + else + { + // worst case scenario, insert a miss. + processor.ApplyResult(new JudgementResult(null!, new FakeJudgement(getMaxJudgementFor(HitResult.Miss, maxRulesetJudgement))) + { + Type = HitResult.Miss, + }); + } } - // TODO: pass a Judgement with correct MaxResult processor.ApplyResult(new JudgementResult(null!, maximumJudgements[maxJudgementIndex++]) { Type = result @@ -68,11 +94,36 @@ namespace osu.Game.Database foreach (var result in bonusHits) processor.ApplyResult(new JudgementResult(null!, new FakeJudgement(result)) { Type = result }); - Debug.Assert(processor.HighestCombo.Value == score.MaxCombo); + // Not true for all scores for whatever reason. Oh well. + // Debug.Assert(processor.HighestCombo.Value == score.MaxCombo); return processor.TotalScore.Value; } + private static HitResult getMaxJudgementFor(HitResult hitResult, HitResult max) + { + switch (hitResult) + { + case HitResult.Miss: + case HitResult.Meh: + case HitResult.Ok: + case HitResult.Good: + case HitResult.Great: + case HitResult.Perfect: + return max; + + case HitResult.SmallTickMiss: + case HitResult.SmallTickHit: + return HitResult.SmallTickHit; + + case HitResult.LargeTickMiss: + case HitResult.LargeTickHit: + return HitResult.LargeTickHit; + } + + return HitResult.IgnoreHit; + } + public static long GetOldStandardised(ScoreInfo score) { double accuracyScore = From 0e9576acfb0d282dbde2046a3cdcfbe7e840efe3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jun 2023 01:40:32 +0900 Subject: [PATCH 148/169] Remove logging and catch any kind of errors --- osu.Game/Database/RealmAccess.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 4c55a408c4..070738363a 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -944,13 +944,20 @@ namespace osu.Game.Database foreach (var score in scores) { // Recalculate the old-style standardised score to see if this was an old lazer score. - long oldStandardised = StandardisedScoreMigrationTools.GetOldStandardised(score); + bool oldScoreMatchesExpectations = StandardisedScoreMigrationTools.GetOldStandardised(score) == score.TotalScore; + // Some older score don't have correct statistics populated, so let's give them benefit of doubt. + bool scoreIsVeryOld = score.Date < new DateTime(2023, 1, 1, 0, 0, 0); - if (oldStandardised == score.TotalScore) + if (oldScoreMatchesExpectations || scoreIsVeryOld) { - long calculatedNew = StandardisedScoreMigrationTools.GetNewStandardised(score); - Logger.Log($"Converting score {score.Rank} {score.Accuracy:P1} {score.TotalScore} -> {calculatedNew}"); - score.TotalScore = calculatedNew; + try + { + long calculatedNew = StandardisedScoreMigrationTools.GetNewStandardised(score); + score.TotalScore = calculatedNew; + } + catch + { + } } } From 0916ae1671b18cfba735f3804cbc56ecd57983a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jun 2023 01:48:32 +0900 Subject: [PATCH 149/169] Add basic profiling output of realm migrations --- osu.Game/Database/RealmAccess.cs | 8 ++++++++ osu.Game/Database/StandardisedScoreMigrationTools.cs | 1 - 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 070738363a..cea307f255 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -37,6 +37,7 @@ using osu.Game.Scoring.Legacy; using osu.Game.Skinning; using Realms; using Realms.Exceptions; +using Stopwatch = System.Diagnostics.Stopwatch; namespace osu.Game.Database { @@ -728,6 +729,11 @@ namespace osu.Game.Database private void applyMigrationsForVersion(Migration migration, ulong targetVersion) { + Logger.Log($"Running realm migration to version {targetVersion}..."); + Stopwatch stopwatch = new Stopwatch(); + + stopwatch.Start(); + switch (targetVersion) { case 7: @@ -964,6 +970,8 @@ namespace osu.Game.Database break; } } + + Logger.Log($"Migration completed in {stopwatch.ElapsedMilliseconds}ms"); } private string? getRulesetShortNameFromLegacyID(long rulesetId) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index e8f1fd640b..f103ded6d5 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; From 87520ae4000ec34efbc22560d5566c8602acc782 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jun 2023 02:05:00 +0900 Subject: [PATCH 150/169] Avoid overhead from retrieving `MaxCombo` inside loop from realm --- osu.Game/Database/StandardisedScoreMigrationTools.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index f103ded6d5..b621b67cf8 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -14,6 +14,9 @@ namespace osu.Game.Database { public static long GetNewStandardised(ScoreInfo score) { + // Avoid retrieving from realm inside loops. + int maxCombo = score.MaxCombo; + var ruleset = score.Ruleset.CreateInstance(); var processor = ruleset.CreateScoreProcessor(); @@ -61,7 +64,7 @@ namespace osu.Game.Database if (result == HitResult.Miss || result == HitResult.LargeTickMiss) continue; - if (processor.Combo.Value == score.MaxCombo) + if (processor.Combo.Value == maxCombo) { if (misses.Count > 0) { From e0ebb000d697aebeab253bf810c371d020b7ec31 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jun 2023 02:05:11 +0900 Subject: [PATCH 151/169] Avoid unnecessary operations during score processor simulation --- osu.Game/Rulesets/Scoring/JudgementProcessor.cs | 9 +++++++++ osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 9 ++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/JudgementProcessor.cs b/osu.Game/Rulesets/Scoring/JudgementProcessor.cs index 09b5f0a6bc..b16c307206 100644 --- a/osu.Game/Rulesets/Scoring/JudgementProcessor.cs +++ b/osu.Game/Rulesets/Scoring/JudgementProcessor.cs @@ -30,6 +30,11 @@ namespace osu.Game.Rulesets.Scoring /// protected int MaxHits { get; private set; } + /// + /// Whether is currently running. + /// + protected bool IsSimulating { get; private set; } + /// /// The total number of judged s at the current point in time. /// @@ -146,6 +151,8 @@ namespace osu.Game.Rulesets.Scoring /// The to simulate. protected virtual void SimulateAutoplay(IBeatmap beatmap) { + IsSimulating = true; + foreach (var obj in beatmap.HitObjects) simulate(obj); @@ -163,6 +170,8 @@ namespace osu.Game.Rulesets.Scoring result.Type = GetSimulatedHitResult(judgement); ApplyResult(result); } + + IsSimulating = false; } protected override void Update() diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index ac17de32d8..87f2b1e5ee 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -226,10 +226,13 @@ namespace osu.Game.Rulesets.Scoring ApplyScoreChange(result); - hitEvents.Add(CreateHitEvent(result)); - lastHitObject = result.HitObject; + if (!IsSimulating) + { + hitEvents.Add(CreateHitEvent(result)); + lastHitObject = result.HitObject; - updateScore(); + updateScore(); + } } /// From 385f6dbd84f3af5d76d984f09a489e5b44da15f6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jun 2023 02:12:23 +0900 Subject: [PATCH 152/169] Move local classes to their rightful location --- osu.Game/Database/RealmAccess.cs | 22 ------------------ .../StandardisedScoreMigrationTools.cs | 23 +++++++++++++++++++ 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index cea307f255..1194e1d9f8 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -1192,26 +1192,4 @@ namespace osu.Game.Database } } } - - internal class FakeHit : HitObject - { - private readonly Judgement judgement; - - public override Judgement CreateJudgement() => judgement; - - public FakeHit(Judgement judgement) - { - this.judgement = judgement; - } - } - - internal class FakeJudgement : Judgement - { - public override HitResult MaxResult { get; } - - public FakeJudgement(HitResult result) - { - MaxResult = result; - } - } } diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index b621b67cf8..ec08168f7c 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; @@ -149,5 +150,27 @@ namespace osu.Game.Database return (long)(1000000 * (accuracyPortion * accuracyScore + (1 - accuracyPortion) * comboScore) + bonusScore); } + + private class FakeHit : HitObject + { + private readonly Judgement judgement; + + public override Judgement CreateJudgement() => judgement; + + public FakeHit(Judgement judgement) + { + this.judgement = judgement; + } + } + + private class FakeJudgement : Judgement + { + public override HitResult MaxResult { get; } + + public FakeJudgement(HitResult result) + { + MaxResult = result; + } + } } } From e9fb1f89326d549ee6f67e457daa471f1ee23467 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jun 2023 02:15:08 +0900 Subject: [PATCH 153/169] Avoid tracking hit events altogether during migration --- osu.Game/Database/StandardisedScoreMigrationTools.cs | 2 ++ osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index ec08168f7c..c06502b521 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -21,6 +21,8 @@ namespace osu.Game.Database var ruleset = score.Ruleset.CreateInstance(); var processor = ruleset.CreateScoreProcessor(); + processor.TrackHitEvents = false; + var beatmap = new Beatmap(); HitResult maxRulesetJudgement = ruleset.GetHitResults().First().result; diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 87f2b1e5ee..f29e3533a0 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -30,6 +30,11 @@ namespace osu.Game.Rulesets.Scoring private const double accuracy_cutoff_c = 0.7; private const double accuracy_cutoff_d = 0; + /// + /// Whether should be populated during application of results. + /// + internal bool TrackHitEvents = true; + /// /// Invoked when this was reset from a replay frame. /// @@ -226,7 +231,7 @@ namespace osu.Game.Rulesets.Scoring ApplyScoreChange(result); - if (!IsSimulating) + if (!IsSimulating && TrackHitEvents) { hitEvents.Add(CreateHitEvent(result)); lastHitObject = result.HitObject; From afb5a9243a2b47e084ff1dc01f4ef2037ded3ea8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jun 2023 05:13:22 +0900 Subject: [PATCH 154/169] Fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Database/RealmAccess.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 1194e1d9f8..663833575a 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -951,7 +951,7 @@ namespace osu.Game.Database { // Recalculate the old-style standardised score to see if this was an old lazer score. bool oldScoreMatchesExpectations = StandardisedScoreMigrationTools.GetOldStandardised(score) == score.TotalScore; - // Some older score don't have correct statistics populated, so let's give them benefit of doubt. + // Some older scores don't have correct statistics populated, so let's give them benefit of doubt. bool scoreIsVeryOld = score.Date < new DateTime(2023, 1, 1, 0, 0, 0); if (oldScoreMatchesExpectations || scoreIsVeryOld) From 3304e41a3012492bf054460462ea449f1d55d002 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jun 2023 05:20:29 +0900 Subject: [PATCH 155/169] Add more commenting --- .../StandardisedScoreMigrationTools.cs | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index c06502b521..edc3288f61 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -27,12 +27,6 @@ namespace osu.Game.Database HitResult maxRulesetJudgement = ruleset.GetHitResults().First().result; - var maximumJudgements = score.MaximumStatistics - .Where(kvp => kvp.Key.AffectsCombo()) - .OrderByDescending(kvp => Judgement.ToNumericResult(kvp.Key)) - .SelectMany(kvp => Enumerable.Repeat(new FakeJudgement(kvp.Key), kvp.Value)) - .ToList(); - // This is a list of all results, ordered from best to worst. // We are constructing a "best possible" score from the statistics provided because it's the best we can do. List sortedHits = score.Statistics @@ -41,20 +35,29 @@ namespace osu.Game.Database .SelectMany(kvp => Enumerable.Repeat(kvp.Key, kvp.Value)) .ToList(); + // Attempt to use maximum statistics from the database. + var maximumJudgements = score.MaximumStatistics + .Where(kvp => kvp.Key.AffectsCombo()) + .OrderByDescending(kvp => Judgement.ToNumericResult(kvp.Key)) + .SelectMany(kvp => Enumerable.Repeat(new FakeJudgement(kvp.Key), kvp.Value)) + .ToList(); + + // Some older scores may not have maximum statistics populated correctly. + // In this case we need to fill them with best-known-defaults. if (maximumJudgements.Count != sortedHits.Count) { - // Older scores may not have maximum judgements populated correctly. - // In this case we need to fill them. maximumJudgements = sortedHits .Select(r => new FakeJudgement(getMaxJudgementFor(r, maxRulesetJudgement))) .ToList(); } + // This is required to get the correct maximum combo portion. foreach (var judgement in maximumJudgements) beatmap.HitObjects.Add(new FakeHit(judgement)); - processor.ApplyBeatmap(beatmap); + // Insert all misses into a queue. + // These will be nibbled at whenever we need to reset the combo. Queue misses = new Queue(score.Statistics .Where(kvp => kvp.Key == HitResult.Miss || kvp.Key == HitResult.LargeTickMiss) .SelectMany(kvp => Enumerable.Repeat(kvp.Key, kvp.Value))); @@ -63,7 +66,7 @@ namespace osu.Game.Database foreach (var result in sortedHits) { - // misses are handled from the queue. + // For the main part of this loop, ignore all misses, as they will be inserted from the queue. if (result == HitResult.Miss || result == HitResult.LargeTickMiss) continue; @@ -78,7 +81,8 @@ namespace osu.Game.Database } else { - // worst case scenario, insert a miss. + // We ran out of misses. But we can't let max combo increase beyond the known value, + // so let's forge a miss. processor.ApplyResult(new JudgementResult(null!, new FakeJudgement(getMaxJudgementFor(HitResult.Miss, maxRulesetJudgement))) { Type = HitResult.Miss, @@ -169,9 +173,9 @@ namespace osu.Game.Database { public override HitResult MaxResult { get; } - public FakeJudgement(HitResult result) + public FakeJudgement(HitResult maxResult) { - MaxResult = result; + MaxResult = maxResult; } } } From c1b0c60e79c9b828b6ed69e8e34ec82f5e6cda58 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jun 2023 05:24:04 +0900 Subject: [PATCH 156/169] Ensure all misses are dequeued --- .../StandardisedScoreMigrationTools.cs | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index edc3288f61..d980f7a858 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -15,6 +15,8 @@ namespace osu.Game.Database { public static long GetNewStandardised(ScoreInfo score) { + int maxJudgementIndex = 0; + // Avoid retrieving from realm inside loops. int maxCombo = score.MaxCombo; @@ -62,33 +64,15 @@ namespace osu.Game.Database .Where(kvp => kvp.Key == HitResult.Miss || kvp.Key == HitResult.LargeTickMiss) .SelectMany(kvp => Enumerable.Repeat(kvp.Key, kvp.Value))); - int maxJudgementIndex = 0; - foreach (var result in sortedHits) { // For the main part of this loop, ignore all misses, as they will be inserted from the queue. if (result == HitResult.Miss || result == HitResult.LargeTickMiss) continue; + // Reset combo if required. if (processor.Combo.Value == maxCombo) - { - if (misses.Count > 0) - { - processor.ApplyResult(new JudgementResult(null!, maximumJudgements[maxJudgementIndex++]) - { - Type = misses.Dequeue(), - }); - } - else - { - // We ran out of misses. But we can't let max combo increase beyond the known value, - // so let's forge a miss. - processor.ApplyResult(new JudgementResult(null!, new FakeJudgement(getMaxJudgementFor(HitResult.Miss, maxRulesetJudgement))) - { - Type = HitResult.Miss, - }); - } - } + insertMiss(); processor.ApplyResult(new JudgementResult(null!, maximumJudgements[maxJudgementIndex++]) { @@ -96,6 +80,10 @@ namespace osu.Game.Database }); } + // Ensure we haven't forgotten any misses. + while (misses.Count > 0) + insertMiss(); + var bonusHits = score.Statistics .Where(kvp => kvp.Key.IsBonus()) .SelectMany(kvp => Enumerable.Repeat(kvp.Key, kvp.Value)); @@ -107,6 +95,26 @@ namespace osu.Game.Database // Debug.Assert(processor.HighestCombo.Value == score.MaxCombo); return processor.TotalScore.Value; + + void insertMiss() + { + if (misses.Count > 0) + { + processor.ApplyResult(new JudgementResult(null!, maximumJudgements[maxJudgementIndex++]) + { + Type = misses.Dequeue(), + }); + } + else + { + // We ran out of misses. But we can't let max combo increase beyond the known value, + // so let's forge a miss. + processor.ApplyResult(new JudgementResult(null!, new FakeJudgement(getMaxJudgementFor(HitResult.Miss, maxRulesetJudgement))) + { + Type = HitResult.Miss, + }); + } + } } private static HitResult getMaxJudgementFor(HitResult hitResult, HitResult max) From 422e87f0ec362506ccbe16943f3732a14c9018cd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jun 2023 05:30:12 +0900 Subject: [PATCH 157/169] Fix weird usings --- osu.Game/Database/RealmAccess.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 663833575a..68a4679656 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -28,16 +28,12 @@ using osu.Game.IO.Legacy; using osu.Game.Models; using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; -using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Scoring.Legacy; using osu.Game.Skinning; using Realms; using Realms.Exceptions; -using Stopwatch = System.Diagnostics.Stopwatch; namespace osu.Game.Database { From 0ab9a48f004be0e6b044391eed4b2677bf07180d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jun 2023 11:44:52 +0900 Subject: [PATCH 158/169] Fix score not updating when `TrackHitEvents` is set to `false` --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index f29e3533a0..7d2bc17bda 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -33,6 +33,9 @@ namespace osu.Game.Rulesets.Scoring /// /// Whether should be populated during application of results. /// + /// + /// Should only be disabled for special cases. + /// When disabled, cannot be used. internal bool TrackHitEvents = true; /// @@ -231,10 +234,13 @@ namespace osu.Game.Rulesets.Scoring ApplyScoreChange(result); - if (!IsSimulating && TrackHitEvents) + if (!IsSimulating) { - hitEvents.Add(CreateHitEvent(result)); - lastHitObject = result.HitObject; + if (TrackHitEvents) + { + hitEvents.Add(CreateHitEvent(result)); + lastHitObject = result.HitObject; + } updateScore(); } @@ -250,6 +256,9 @@ namespace osu.Game.Rulesets.Scoring protected sealed override void RevertResultInternal(JudgementResult result) { + if (!TrackHitEvents) + throw new InvalidOperationException(@$"Rewind is not supported when {nameof(TrackHitEvents)} is disabled."); + Combo.Value = result.ComboAtJudgement; HighestCombo.Value = result.HighestComboAtJudgement; From 4cdd4561c4e00c5253ff10d735046bd0be21f715 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jun 2023 14:17:32 +0900 Subject: [PATCH 159/169] Add a few more search keywords for offset settings https://github.com/ppy/osu/discussions/23898#discussioncomment-6159206 --- osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs index 1755c12f94..fc354027c1 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio { protected override LocalisableString Header => AudioSettingsStrings.OffsetHeader; - public override IEnumerable FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { "universal", "uo", "timing" }); + public override IEnumerable FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { "universal", "uo", "timing", "delay", "latency" }); [BackgroundDependencyLoader] private void load(OsuConfigManager config) From a5c3c9a93f4adf8ee0ceab485eb650de8e1acf70 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 12 Jun 2023 23:30:08 -0700 Subject: [PATCH 160/169] Use `FillFlowContainer` instead of `GridContainer` for drawable room background gradient --- .../Lounge/Components/DrawableRoom.cs | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 8c85a8235c..522438227a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -103,29 +103,25 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components CornerRadius = CORNER_RADIUS, Children = new Drawable[] { - new GridContainer + new FillFlowContainer { RelativeSizeAxes = Axes.Both, - ColumnDimensions = new[] + Direction = FillDirection.Horizontal, + Children = new Drawable[] { - new Dimension(GridSizeMode.Relative, 0.2f) - }, - Content = new[] - { - new Drawable[] + new Box { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Background5, - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(colours.Background5, colours.Background5.Opacity(0.3f)) - }, - } - } + RelativeSizeAxes = Axes.Both, + Colour = colours.Background5, + Width = 0.2f, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientHorizontal(colours.Background5, colours.Background5.Opacity(0.3f)), + Width = 0.8f, + }, + }, }, new Container { From 0adb399ea869c758796790edb8184df173c43f69 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 13 Jun 2023 00:09:58 -0700 Subject: [PATCH 161/169] Use anchor instead of `FillFlowContainer` --- .../Lounge/Components/DrawableRoom.cs | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 522438227a..f1fc751630 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -103,25 +103,19 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components CornerRadius = CORNER_RADIUS, Children = new Drawable[] { - new FillFlowContainer + new Box { RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Background5, - Width = 0.2f, - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(colours.Background5, colours.Background5.Opacity(0.3f)), - Width = 0.8f, - }, - }, + Colour = colours.Background5, + Width = 0.2f, + }, + new Box + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientHorizontal(colours.Background5, colours.Background5.Opacity(0.3f)), + Width = 0.8f, }, new Container { From 3334323eb7cdecf753fe3266e5edc85e332b6373 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Jun 2023 01:54:57 +0900 Subject: [PATCH 162/169] Run `updateScore` on `Reset` for good measure --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 7d2bc17bda..35a7dfe369 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -328,6 +328,9 @@ namespace osu.Game.Rulesets.Scoring /// Whether to store the current state of the for future use. protected override void Reset(bool storeResults) { + // Run one last time to store max values. + updateScore(); + base.Reset(storeResults); hitEvents.Clear(); From 6205864c62571faea9a6af432091c3f7b30a4fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 13 Jun 2023 18:45:35 +0200 Subject: [PATCH 163/169] Fix score migration not considering mod multipliers --- osu.Game/Database/StandardisedScoreMigrationTools.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index d980f7a858..af91bee9e4 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -57,6 +57,7 @@ namespace osu.Game.Database foreach (var judgement in maximumJudgements) beatmap.HitObjects.Add(new FakeHit(judgement)); processor.ApplyBeatmap(beatmap); + processor.Mods.Value = score.Mods; // Insert all misses into a queue. // These will be nibbled at whenever we need to reset the combo. @@ -162,7 +163,12 @@ namespace osu.Game.Database break; } - return (long)(1000000 * (accuracyPortion * accuracyScore + (1 - accuracyPortion) * comboScore) + bonusScore); + double modMultiplier = 1; + + foreach (var mod in score.Mods) + modMultiplier *= mod.ScoreMultiplier; + + return (long)((1000000 * (accuracyPortion * accuracyScore + (1 - accuracyPortion) * comboScore) + bonusScore) * modMultiplier); } private class FakeHit : HitObject From f553efba8a03639f8e086a244eabfbc8b2527759 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Jun 2023 02:29:56 +0900 Subject: [PATCH 164/169] Fix playback controls in editor handling key repeat when they probably shouldn't Closes https://github.com/ppy/osu/issues/23903. --- osu.Game/Screens/Edit/Components/PlaybackControl.cs | 3 +++ osu.Game/Screens/Edit/Editor.cs | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/osu.Game/Screens/Edit/Components/PlaybackControl.cs b/osu.Game/Screens/Edit/Components/PlaybackControl.cs index 72c299f443..431336aa60 100644 --- a/osu.Game/Screens/Edit/Components/PlaybackControl.cs +++ b/osu.Game/Screens/Edit/Components/PlaybackControl.cs @@ -76,6 +76,9 @@ namespace osu.Game.Screens.Edit.Components protected override bool OnKeyDown(KeyDownEvent e) { + if (e.Repeat) + return false; + switch (e.Key) { case Key.Space: diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index bb052b1d22..74947aab09 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -533,6 +533,9 @@ namespace osu.Game.Screens.Edit // Track traversal keys. // Matching osu-stable implementations. case Key.Z: + if (e.Repeat) + return false; + // Seek to first object time, or track start if already there. double? firstObjectTime = editorBeatmap.HitObjects.FirstOrDefault()?.StartTime; @@ -543,12 +546,18 @@ namespace osu.Game.Screens.Edit return true; case Key.X: + if (e.Repeat) + return false; + // Restart playback from beginning of track. clock.Seek(0); clock.Start(); return true; case Key.C: + if (e.Repeat) + return false; + // Pause or resume. if (clock.IsRunning) clock.Stop(); @@ -557,6 +566,9 @@ namespace osu.Game.Screens.Edit return true; case Key.V: + if (e.Repeat) + return false; + // Seek to last object time, or track end if already there. // Note that in osu-stable subsequent presses when at track end won't return to last object. // This has intentionally been changed to make it more useful. From 83abc80950f5e19c590d8c5099aaf872411a7708 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Jun 2023 13:54:37 +0900 Subject: [PATCH 165/169] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b2faa7dfc2..e08b09aef9 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From 6543c720efcf154f0d29b8e839aa076e5a8359d0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 14 Jun 2023 09:58:39 +0300 Subject: [PATCH 166/169] Fix map pool test scene using invalid mod acronyms --- osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs index 5695cb5574..48375c2cbd 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs @@ -92,7 +92,7 @@ namespace osu.Game.Tournament.Tests.Screens Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear(); for (int i = 0; i < 11; i++) - addBeatmap(i > 4 ? $"M{i}" : "NM"); + addBeatmap(i > 4 ? Ruleset.Value.CreateInstance().AllMods.ElementAt(i).Acronym : "NM"); }); AddStep("reset match", () => @@ -118,7 +118,7 @@ namespace osu.Game.Tournament.Tests.Screens Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear(); for (int i = 0; i < 12; i++) - addBeatmap(i > 4 ? $"M{i}" : "NM"); + addBeatmap(i > 4 ? Ruleset.Value.CreateInstance().AllMods.ElementAt(i).Acronym : "NM"); }); AddStep("reset match", () => @@ -130,7 +130,7 @@ namespace osu.Game.Tournament.Tests.Screens assertThreeWide(); } - private void addBeatmap(string mods = "nm") + private void addBeatmap(string mods = "NM") { Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Add(new RoundBeatmap { From 90a5c75474275cd56bfd8031bd35b6a94d7c3e4c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 14 Jun 2023 10:01:01 +0300 Subject: [PATCH 167/169] Add setting to ladder info and simplify changes --- osu.Game.Tournament/Models/LadderInfo.cs | 2 + .../Screens/MapPool/MapPoolScreen.cs | 79 +++++++++---------- 2 files changed, 39 insertions(+), 42 deletions(-) diff --git a/osu.Game.Tournament/Models/LadderInfo.cs b/osu.Game.Tournament/Models/LadderInfo.cs index 6b64a1156e..cb4e8bc16a 100644 --- a/osu.Game.Tournament/Models/LadderInfo.cs +++ b/osu.Game.Tournament/Models/LadderInfo.cs @@ -42,5 +42,7 @@ namespace osu.Game.Tournament.Models }; public Bindable AutoProgressScreens = new BindableBool(true); + + public Bindable SplitMapPoolByMods = new BindableBool(true); } } diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index fcb0c4d70b..cb6c5902ec 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Framework.Threading; using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Settings; using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Models; @@ -25,8 +24,7 @@ namespace osu.Game.Tournament.Screens.MapPool { public partial class MapPoolScreen : TournamentMatchScreen { - private readonly FillFlowContainer> mapFlows; - private TournamentMatch currentMatch; + private FillFlowContainer> mapFlows; [Resolved(canBeNull: true)] private TournamentSceneManager sceneManager { get; set; } @@ -34,14 +32,13 @@ namespace osu.Game.Tournament.Screens.MapPool private TeamColour pickColour; private ChoiceType pickType; - private readonly OsuButton buttonRedBan; - private readonly OsuButton buttonBlueBan; - private readonly OsuButton buttonRedPick; - private readonly OsuButton buttonBluePick; + private OsuButton buttonRedBan; + private OsuButton buttonBlueBan; + private OsuButton buttonRedPick; + private OsuButton buttonBluePick; - private readonly SettingsCheckbox chkBoxLineBreak; - - public MapPoolScreen() + [BackgroundDependencyLoader] + private void load(MatchIPCInfo ipc) { InternalChildren = new Drawable[] { @@ -102,26 +99,26 @@ namespace osu.Game.Tournament.Screens.MapPool Action = reset }, new ControlPanel.Spacer(), - new TournamentSpriteText + new OsuCheckbox { - Text = "Each modpool takes" + LabelText = "Split display by mods", + Current = LadderInfo.SplitMapPoolByMods, }, - new TournamentSpriteText - { - Text = "different row" - }, - chkBoxLineBreak = new SettingsCheckbox() }, } }; + + ipc.Beatmap.BindValueChanged(beatmapChanged); } - [BackgroundDependencyLoader] - private void load(MatchIPCInfo ipc) + private Bindable splitMapPoolByMods; + + protected override void LoadComplete() { - ipc.Beatmap.BindValueChanged(beatmapChanged); - chkBoxLineBreak.Current.Value = true; - chkBoxLineBreak.Current.BindValueChanged(_ => rearrangeMappool()); + base.LoadComplete(); + + splitMapPoolByMods = LadderInfo.SplitMapPoolByMods.GetBoundCopy(); + splitMapPoolByMods.BindValueChanged(_ => updateDisplay()); } private void beatmapChanged(ValueChangedEvent beatmap) @@ -228,42 +225,40 @@ namespace osu.Game.Tournament.Screens.MapPool protected override void CurrentMatchChanged(ValueChangedEvent match) { base.CurrentMatchChanged(match); - currentMatch = match.NewValue; - rearrangeMappool(); + updateDisplay(); } - private void rearrangeMappool() + private void updateDisplay() { mapFlows.Clear(); - if (currentMatch == null) + if (CurrentMatch.Value == null) return; + int totalRows = 0; - if (currentMatch.Round.Value != null) + if (CurrentMatch.Value.Round.Value != null) { FillFlowContainer currentFlow = null; - string currentMod = null; + string currentMods = null; int flowCount = 0; - foreach (var b in currentMatch.Round.Value.Beatmaps) + foreach (var b in CurrentMatch.Value.Round.Value.Beatmaps) { - if (currentFlow == null || currentMod != b.Mods) + if (currentFlow == null || (LadderInfo.SplitMapPoolByMods.Value && currentMods != b.Mods)) { - if (chkBoxLineBreak.Current.Value || currentFlow == null) + mapFlows.Add(currentFlow = new FillFlowContainer { - mapFlows.Add(currentFlow = new FillFlowContainer - { - Spacing = new Vector2(10, 5), - Direction = FillDirection.Full, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y - }); + Spacing = new Vector2(10, 5), + Direction = FillDirection.Full, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }); - totalRows++; - flowCount = 0; - } - currentMod = b.Mods; + currentMods = b.Mods; + + totalRows++; + flowCount = 0; } if (++flowCount > 2) From 78fe71182430e145a4d647b40c15a6118b448550 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 14 Jun 2023 10:01:52 +0300 Subject: [PATCH 168/169] Add visual test case --- .../Screens/TestSceneMapPoolScreen.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs index 48375c2cbd..0ffbeeb491 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs @@ -24,6 +24,9 @@ namespace osu.Game.Tournament.Tests.Screens Add(screen = new MapPoolScreen { Width = 0.7f }); } + [SetUp] + public void SetUp() => Schedule(() => Ladder.SplitMapPoolByMods.Value = true); + [Test] public void TestFewMaps() { @@ -130,6 +133,26 @@ namespace osu.Game.Tournament.Tests.Screens assertThreeWide(); } + [Test] + public void TestDisableMapsPerMod() + { + AddStep("load many maps", () => + { + Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear(); + + for (int i = 0; i < 12; i++) + addBeatmap(i > 4 ? Ruleset.Value.CreateInstance().AllMods.ElementAt(i).Acronym : "NM"); + }); + + AddStep("disable maps per mod", () => Ladder.SplitMapPoolByMods.Value = false); + + AddStep("reset match", () => + { + Ladder.CurrentMatch.Value = new TournamentMatch(); + Ladder.CurrentMatch.Value = Ladder.Matches.First(); + }); + } + private void addBeatmap(string mods = "NM") { Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Add(new RoundBeatmap From 4cc2bb0c7db35e4b52ac230e81cb5f50a08dfbb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 14 Jun 2023 21:33:11 +0200 Subject: [PATCH 169/169] Rename things in test to match --- osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs index 0ffbeeb491..94086f10f2 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs @@ -134,7 +134,7 @@ namespace osu.Game.Tournament.Tests.Screens } [Test] - public void TestDisableMapsPerMod() + public void TestSplitMapPoolByMods() { AddStep("load many maps", () => { @@ -144,7 +144,7 @@ namespace osu.Game.Tournament.Tests.Screens addBeatmap(i > 4 ? Ruleset.Value.CreateInstance().AllMods.ElementAt(i).Acronym : "NM"); }); - AddStep("disable maps per mod", () => Ladder.SplitMapPoolByMods.Value = false); + AddStep("disable splitting map pool by mods", () => Ladder.SplitMapPoolByMods.Value = false); AddStep("reset match", () => {