From 5d5e6a5ab750f380393a9c6ffd9cd3028e486f1f Mon Sep 17 00:00:00 2001 From: Hydria Date: Mon, 3 Jul 2023 17:45:30 +0100 Subject: [PATCH 001/124] Finalised LN Adjustment Values Spent a couple days discussing this on the pp rework server about values that were the most acceptable, these seemed to be the best from the community standpoint of top players. Note: This is more to fix issues with the current system, not to be a final solution. Related Google Sheets Page: https://docs.google.com/spreadsheets/d/1P0AxfdKvMHwWBQder4ZkFGO1fC9eADSGCryA5-UGriU/edit?usp=sharing --- osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs index 06c825e37d..0a4fec3a70 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills { private const double individual_decay_base = 0.125; private const double overall_decay_base = 0.30; - private const double release_threshold = 24; + private const double release_threshold = 30; protected override double SkillMultiplier => 1; protected override double StrainDecayBase => 1; @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills // 0.0 +--------+-+---------------> Release Difference / ms // release_threshold if (isOverlapping) - holdAddition = 1 / (1 + Math.Exp(0.5 * (release_threshold - closestEndTime))); + holdAddition = 1 / (1 + Math.Exp(0.27 * (release_threshold - closestEndTime))); // Decay and increase individualStrains in own column individualStrains[column] = applyDecay(individualStrains[column], startTime - startTimes[column], individual_decay_base); From ef2134a92a32f0607d0f69bc04c8dbfd2a477acd Mon Sep 17 00:00:00 2001 From: Hydria Date: Sat, 22 Jul 2023 10:45:48 +0100 Subject: [PATCH 002/124] Fix issue with processing LN orders Related Issue: https://github.com/ppy/osu/issues/22756 The trigger in question happens when (1) in a chord: a longer LN, then a shorter LN is processed respectively. (2) in a chord: a long LN, then a note is processed respectively. however, given the opposite processing step, it will fail to trigger. We observe that both situations have the same pattern, however has undeterministic results, which only depends on the order the mapper placed each note. --- osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs index 0a4fec3a70..7d8d010da0 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs @@ -53,7 +53,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills isOverlapping |= Precision.DefinitelyBigger(endTimes[i], startTime, 1) && Precision.DefinitelyBigger(endTime, endTimes[i], 1); // We give a slight bonus to everything if something is held meanwhile - if (Precision.DefinitelyBigger(endTimes[i], endTime, 1)) + if (Precision.DefinitelyBigger(endTimes[i], endTime, 1) && + Precision.DefinitelyBigger(startTime, startTimes[i], 1)) holdFactor = 1.25; closestEndTime = Math.Min(closestEndTime, Math.Abs(endTime - endTimes[i])); From 1e19def1538d718421e4d7c6802045011616d21e Mon Sep 17 00:00:00 2001 From: Hydria Date: Sat, 22 Jul 2023 16:44:01 +0100 Subject: [PATCH 003/124] 2nd fix to cover all scenarios --- osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs index 7d8d010da0..a24fcaad8d 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs @@ -50,7 +50,9 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills for (int i = 0; i < endTimes.Length; ++i) { // The current note is overlapped if a previous note or end is overlapping the current note body - isOverlapping |= Precision.DefinitelyBigger(endTimes[i], startTime, 1) && Precision.DefinitelyBigger(endTime, endTimes[i], 1); + isOverlapping |= Precision.DefinitelyBigger(endTimes[i], startTime, 1) && + Precision.DefinitelyBigger(endTime, endTimes[i], 1) && + Precision.DefinitelyBigger(startTime, startTimes[i], 1); // We give a slight bonus to everything if something is held meanwhile if (Precision.DefinitelyBigger(endTimes[i], endTime, 1) && From 0bd34a4f13eb79a0dba578f5a17c20e872976a09 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 25 Jul 2023 21:14:21 -0700 Subject: [PATCH 004/124] Always show supporter-only playlist durations --- osu.Game/Localisation/OnlinePlayStrings.cs | 19 +++++++ .../Match/Components/RoomSettingsOverlay.cs | 5 +- .../Playlists/PlaylistsRoomSettingsOverlay.cs | 55 +++++++++++++------ 3 files changed, 59 insertions(+), 20 deletions(-) create mode 100644 osu.Game/Localisation/OnlinePlayStrings.cs diff --git a/osu.Game/Localisation/OnlinePlayStrings.cs b/osu.Game/Localisation/OnlinePlayStrings.cs new file mode 100644 index 0000000000..e9f17cddc0 --- /dev/null +++ b/osu.Game/Localisation/OnlinePlayStrings.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class OnlinePlayStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.OnlinePlay"; + + /// + /// "This duration is only available for osu!supporters." + /// + public static LocalisableString SupporterOnlyDurationNotice => new TranslatableString(getKey(@"supporter_only_duration_notice"), @"This duration is only available for osu!supporters."); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs index 05232fe0e2..916b799d50 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs @@ -113,7 +113,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components protected partial class Section : Container { - private readonly Container content; + private readonly ReverseChildIDFillFlowContainer content; protected override Container Content => content; @@ -135,10 +135,11 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 12), Text = title.ToUpperInvariant(), }, - content = new Container + content = new ReverseChildIDFillFlowContainer { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical }, }, }; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index e93f56c2e2..c69ccb7b1c 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -23,6 +23,7 @@ using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Screens.OnlinePlay.Match.Components; using osuTK; +using osu.Game.Localisation; namespace osu.Game.Screens.OnlinePlay.Playlists { @@ -80,6 +81,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private IBindable localUser = null!; private readonly Room room; + private OsuSpriteText durationNoticeText = null!; public MatchSettings(Room room) { @@ -141,14 +143,22 @@ namespace osu.Game.Screens.OnlinePlay.Playlists }, new Section("Duration") { - Child = new Container + Children = new Drawable[] { - RelativeSizeAxes = Axes.X, - Height = 40, - Child = DurationField = new DurationDropdown + new Container { - RelativeSizeAxes = Axes.X - } + RelativeSizeAxes = Axes.X, + Height = 40, + Child = DurationField = new DurationDropdown + { + RelativeSizeAxes = Axes.X + }, + }, + durationNoticeText = new OsuSpriteText + { + Alpha = 0, + Colour = colours.Yellow, + }, } }, new Section("Allowed attempts (across all playlist items)") @@ -305,6 +315,17 @@ namespace osu.Game.Screens.OnlinePlay.Playlists MaxAttempts.BindValueChanged(count => MaxAttemptsField.Text = count.NewValue?.ToString(), true); Duration.BindValueChanged(duration => DurationField.Current.Value = duration.NewValue ?? TimeSpan.FromMinutes(30), true); + DurationField.Current.BindValueChanged(duration => + { + if (selectedAvailableDuration) + durationNoticeText.Hide(); + else + { + durationNoticeText.Show(); + durationNoticeText.Text = OnlinePlayStrings.SupporterOnlyDurationNotice; + } + }); + localUser = api.LocalUser.GetBoundCopy(); localUser.BindValueChanged(populateDurations, true); @@ -314,6 +335,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private void populateDurations(ValueChangedEvent user) { + // roughly correct (see https://github.com/Humanizr/Humanizer/blob/18167e56c082449cc4fe805b8429e3127a7b7f93/readme.md?plain=1#L427) + // if we want this to be more accurate we might consider sending an actual end time, not a time span. probably not required though. + const int days_in_month = 31; + DurationField.Items = new[] { TimeSpan.FromMinutes(30), @@ -326,18 +351,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists TimeSpan.FromDays(3), TimeSpan.FromDays(7), TimeSpan.FromDays(14), + TimeSpan.FromDays(days_in_month), + TimeSpan.FromDays(days_in_month * 3), }; - - // TODO: show these in the interface at all times. - if (user.NewValue.IsSupporter) - { - // roughly correct (see https://github.com/Humanizr/Humanizer/blob/18167e56c082449cc4fe805b8429e3127a7b7f93/readme.md?plain=1#L427) - // if we want this to be more accurate we might consider sending an actual end time, not a time span. probably not required though. - const int days_in_month = 31; - - DurationField.AddDropdownItem(TimeSpan.FromDays(days_in_month)); - DurationField.AddDropdownItem(TimeSpan.FromDays(days_in_month * 3)); - } } protected override void Update() @@ -352,7 +368,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private void onPlaylistChanged(object? sender, NotifyCollectionChangedEventArgs e) => playlistLength.Text = $"Length: {Playlist.GetTotalDuration()}"; - private bool hasValidSettings => RoomID.Value == null && NameField.Text.Length > 0 && Playlist.Count > 0; + private bool hasValidSettings => RoomID.Value == null && NameField.Text.Length > 0 && Playlist.Count > 0 + && selectedAvailableDuration; + + private bool selectedAvailableDuration => DurationField.Current.Value < TimeSpan.FromDays(31) || localUser.Value.IsSupporter; private void apply() { From 30e0e00c520d8072eb9cfe8fca2c0e2a14fd06e9 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 25 Jul 2023 22:57:01 -0700 Subject: [PATCH 005/124] Rename `selectedAvailableDuration` to `hasValidDuration` --- .../OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index c69ccb7b1c..f7bd7fc530 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -317,7 +317,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists DurationField.Current.BindValueChanged(duration => { - if (selectedAvailableDuration) + if (hasValidDuration) durationNoticeText.Hide(); else { @@ -369,9 +369,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists playlistLength.Text = $"Length: {Playlist.GetTotalDuration()}"; private bool hasValidSettings => RoomID.Value == null && NameField.Text.Length > 0 && Playlist.Count > 0 - && selectedAvailableDuration; + && hasValidDuration; - private bool selectedAvailableDuration => DurationField.Current.Value < TimeSpan.FromDays(31) || localUser.Value.IsSupporter; + private bool hasValidDuration => DurationField.Current.Value < TimeSpan.FromDays(31) || localUser.Value.IsSupporter; private void apply() { From c1ba8fe175c9bff9e018f2409fbc3cfa041e4902 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Jul 2023 16:07:45 +0900 Subject: [PATCH 006/124] Rename `BackgroundBeatmapProcessor` to `BackgroundDataStoreProcessor` --- ...orTests.cs => BackgroundDataStoreProcessorTests.cs} | 8 ++++---- ...mapProcessor.cs => BackgroundDataStoreProcessor.cs} | 10 +++++++--- osu.Game/OsuGame.cs | 2 +- osu.Game/Scoring/ScoreInfo.cs | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) rename osu.Game.Tests/Database/{BackgroundBeatmapProcessorTests.cs => BackgroundDataStoreProcessorTests.cs} (92%) rename osu.Game/{BackgroundBeatmapProcessor.cs => BackgroundDataStoreProcessor.cs} (97%) diff --git a/osu.Game.Tests/Database/BackgroundBeatmapProcessorTests.cs b/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs similarity index 92% rename from osu.Game.Tests/Database/BackgroundBeatmapProcessorTests.cs rename to osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs index c876316be4..15bb0fc09c 100644 --- a/osu.Game.Tests/Database/BackgroundBeatmapProcessorTests.cs +++ b/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs @@ -15,7 +15,7 @@ using osu.Game.Tests.Visual; namespace osu.Game.Tests.Database { [HeadlessTest] - public partial class BackgroundBeatmapProcessorTests : OsuTestScene, ILocalUserPlayInfo + public partial class BackgroundDataStoreProcessorTests : OsuTestScene, ILocalUserPlayInfo { public IBindable IsPlaying => isPlaying; @@ -59,7 +59,7 @@ namespace osu.Game.Tests.Database AddStep("Run background processor", () => { - Add(new TestBackgroundBeatmapProcessor()); + Add(new TestBackgroundDataStoreProcessor()); }); AddUntilStep("wait for difficulties repopulated", () => @@ -98,7 +98,7 @@ namespace osu.Game.Tests.Database AddStep("Run background processor", () => { - Add(new TestBackgroundBeatmapProcessor()); + Add(new TestBackgroundDataStoreProcessor()); }); AddWaitStep("wait some", 500); @@ -124,7 +124,7 @@ namespace osu.Game.Tests.Database }); } - public partial class TestBackgroundBeatmapProcessor : BackgroundBeatmapProcessor + public partial class TestBackgroundDataStoreProcessor : BackgroundDataStoreProcessor { protected override int TimeToSleepDuringGameplay => 10; } diff --git a/osu.Game/BackgroundBeatmapProcessor.cs b/osu.Game/BackgroundDataStoreProcessor.cs similarity index 97% rename from osu.Game/BackgroundBeatmapProcessor.cs rename to osu.Game/BackgroundDataStoreProcessor.cs index b553fee503..c475a07c79 100644 --- a/osu.Game/BackgroundBeatmapProcessor.cs +++ b/osu.Game/BackgroundDataStoreProcessor.cs @@ -24,7 +24,10 @@ using osu.Game.Screens.Play; namespace osu.Game { - public partial class BackgroundBeatmapProcessor : Component + /// + /// Performs background updating of data stores at startup. + /// + public partial class BackgroundDataStoreProcessor : Component { [Resolved] private RulesetStore rulesetStore { get; set; } = null!; @@ -61,7 +64,8 @@ namespace osu.Game Task.Factory.StartNew(() => { - Logger.Log("Beginning background beatmap processing.."); + Logger.Log("Beginning background data store processing.."); + checkForOutdatedStarRatings(); processBeatmapSetsWithMissingMetrics(); processScoresWithMissingStatistics(); @@ -74,7 +78,7 @@ namespace osu.Game return; } - Logger.Log("Finished background beatmap processing!"); + Logger.Log("Finished background data store processing!"); }); } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 1a40bb8e3d..c60bff9e4c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1025,7 +1025,7 @@ namespace osu.Game loadComponentSingleFile(CreateHighPerformanceSession(), Add); - loadComponentSingleFile(new BackgroundBeatmapProcessor(), Add); + loadComponentSingleFile(new BackgroundDataStoreProcessor(), Add); Add(difficultyRecommender); Add(externalLinkOpener = new ExternalLinkOpener()); diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index c6f4433824..a3bbb2c09e 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -66,7 +66,7 @@ namespace osu.Game.Scoring /// If this does not match , /// the total score has not yet been updated to reflect the current scoring values. /// - /// See 's conversion logic. + /// See 's conversion logic. /// /// /// This may not match the version stored in the replay files. From 30baac0f3db82d4df246c75bb458113be90c210e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Jul 2023 16:08:02 +0900 Subject: [PATCH 007/124] Avoid reprocessing scores which already failed an upgrade previously Closes https://github.com/ppy/osu/issues/24301. --- osu.Game/BackgroundDataStoreProcessor.cs | 6 ++++-- osu.Game/Database/RealmAccess.cs | 3 ++- osu.Game/Scoring/ScoreInfo.cs | 9 +++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/osu.Game/BackgroundDataStoreProcessor.cs b/osu.Game/BackgroundDataStoreProcessor.cs index c475a07c79..ae9e9527de 100644 --- a/osu.Game/BackgroundDataStoreProcessor.cs +++ b/osu.Game/BackgroundDataStoreProcessor.cs @@ -186,7 +186,7 @@ namespace osu.Game realmAccess.Run(r => { - foreach (var score in r.All()) + foreach (var score in r.All().Where(s => !s.TotalScoreUpgradeFailed)) { if (score.BeatmapInfo != null && score.Statistics.Sum(kvp => kvp.Value) > 0 @@ -225,6 +225,7 @@ namespace osu.Game catch (Exception e) { Logger.Log(@$"Failed to populate maximum statistics for {id}: {e}"); + realmAccess.Write(r => r.Find(id)!.TotalScoreUpgradeFailed = true); } } } @@ -234,7 +235,7 @@ namespace osu.Game Logger.Log("Querying for scores that need total score conversion..."); HashSet scoreIds = realmAccess.Run(r => new HashSet(r.All() - .Where(s => s.BeatmapInfo != null && s.TotalScoreVersion == 30000002) + .Where(s => !s.TotalScoreUpgradeFailed && s.BeatmapInfo != null && s.TotalScoreVersion == 30000002) .AsEnumerable().Select(s => s.ID))); Logger.Log($"Found {scoreIds.Count} scores which require total score conversion."); @@ -283,6 +284,7 @@ namespace osu.Game catch (Exception e) { Logger.Log($"Failed to convert total score for {id}: {e}"); + realmAccess.Write(r => r.Find(id)!.TotalScoreUpgradeFailed = true); ++failedCount; } } diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index f32b161bb6..04a4b28fa4 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -82,8 +82,9 @@ namespace osu.Game.Database /// 30 2023-06-16 Run migration of old lazer scores again. This time with more correct rounding considerations. /// 31 2023-06-26 Add Version and LegacyTotalScore to ScoreInfo, set Version to 30000002 and copy TotalScore into LegacyTotalScore for legacy scores. /// 32 2023-07-09 Populate legacy scores with the ScoreV2 mod (and restore TotalScore to the legacy total for such scores) using replay files. + /// 33 2023-07-26 Add TotalScoreUpgradeFailed flag to ScoreInfo to track upgrade failures. /// - private const int schema_version = 32; + private const int schema_version = 33; /// /// Lock object which is held during sections, blocking realm retrieval during blocking periods. diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index a3bbb2c09e..c3a45332e4 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -81,6 +81,15 @@ namespace osu.Game.Scoring /// public long? LegacyTotalScore { get; set; } + /// + /// If an reprocess of total score failed to update this score to the latest version, this flag will become true. + /// Should be used to ensure we don't repeatedly attempt to update the same scores each startup even though we already know they will fail. + /// + /// + /// See https://github.com/ppy/osu/issues/24301 for one example of how this can occur(missing beatmap file on disk). + /// + public bool TotalScoreUpgradeFailed { get; set; } + public int MaxCombo { get; set; } public double Accuracy { get; set; } From a2f1ced5a224e6ff1c239c430be62793b3b24fe0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Jul 2023 16:22:21 +0900 Subject: [PATCH 008/124] Add test coverage of failed-to-upgrade flag --- .../BackgroundDataStoreProcessorTests.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs b/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs index 15bb0fc09c..d7ec572026 100644 --- a/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs +++ b/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs @@ -8,6 +8,9 @@ using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Scoring; +using osu.Game.Scoring.Legacy; using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps.IO; using osu.Game.Tests.Visual; @@ -124,6 +127,32 @@ namespace osu.Game.Tests.Database }); } + [Test] + public void TestScoreUpgradeFailed() + { + ScoreInfo scoreInfo = null!; + + AddStep("Add score which requires upgrade (but has no beatmap)", () => + { + Realm.Write(r => + { + r.Add(scoreInfo = new ScoreInfo(ruleset: r.All().First(), beatmap: new BeatmapInfo + { + BeatmapSet = new BeatmapSetInfo(), + Ruleset = r.All().First(), + }) + { + TotalScoreVersion = 30000002, + IsLegacyScore = true, + }); + }); + }); + + AddStep("Run background processor", () => Add(new TestBackgroundDataStoreProcessor())); + + AddUntilStep("Score marked as failed", () => Realm.Run(r => r.Find(scoreInfo.ID)!.TotalScoreUpgradeFailed)); + } + public partial class TestBackgroundDataStoreProcessor : BackgroundDataStoreProcessor { protected override int TimeToSleepDuringGameplay => 10; From 235cb2d73fcc64ffb2c85d1dafb2a03486557276 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Jul 2023 16:28:38 +0900 Subject: [PATCH 009/124] Add test coverage of upgrade success for good measure --- .../BackgroundDataStoreProcessorTests.cs | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs b/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs index d7ec572026..23b88b7395 100644 --- a/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs +++ b/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs @@ -127,6 +127,30 @@ namespace osu.Game.Tests.Database }); } + [Test] + public void TestScoreUpgradeSuccess() + { + ScoreInfo scoreInfo = null!; + + AddStep("Add score which requires upgrade (but has no beatmap)", () => + { + Realm.Write(r => + { + r.Add(scoreInfo = new ScoreInfo(ruleset: r.All().First(), beatmap: r.All().First()) + { + TotalScoreVersion = 30000002, + LegacyTotalScore = 123456, + IsLegacyScore = true, + }); + }); + }); + + AddStep("Run background processor", () => Add(new TestBackgroundDataStoreProcessor())); + + AddUntilStep("Score version upgraded", () => Realm.Run(r => r.Find(scoreInfo.ID)!.TotalScoreVersion), () => Is.EqualTo(LegacyScoreEncoder.LATEST_VERSION)); + AddAssert("Score not marked as failed", () => Realm.Run(r => r.Find(scoreInfo.ID)!.TotalScoreUpgradeFailed), () => Is.False); + } + [Test] public void TestScoreUpgradeFailed() { @@ -150,7 +174,8 @@ namespace osu.Game.Tests.Database AddStep("Run background processor", () => Add(new TestBackgroundDataStoreProcessor())); - AddUntilStep("Score marked as failed", () => Realm.Run(r => r.Find(scoreInfo.ID)!.TotalScoreUpgradeFailed)); + AddUntilStep("Score marked as failed", () => Realm.Run(r => r.Find(scoreInfo.ID)!.TotalScoreUpgradeFailed), () => Is.True); + AddAssert("Score version not upgraded", () => Realm.Run(r => r.Find(scoreInfo.ID)!.TotalScoreVersion), () => Is.EqualTo(30000002)); } public partial class TestBackgroundDataStoreProcessor : BackgroundDataStoreProcessor From 9d87ff2986f3430b4c44b03d895720f5c8b57bd3 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 26 Jul 2023 01:37:55 -0700 Subject: [PATCH 010/124] Improve supporter-only duration notice and fix max duration of non-supporter See https://github.com/ppy/osu-web/blob/3c542292569b336c4dff38e2b52a823149ec1a81/.env.example#L230. Might have more duration options in the future. --- osu.Game/Localisation/OnlinePlayStrings.cs | 4 ++-- .../OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Localisation/OnlinePlayStrings.cs b/osu.Game/Localisation/OnlinePlayStrings.cs index e9f17cddc0..1853cb753a 100644 --- a/osu.Game/Localisation/OnlinePlayStrings.cs +++ b/osu.Game/Localisation/OnlinePlayStrings.cs @@ -10,9 +10,9 @@ namespace osu.Game.Localisation private const string prefix = @"osu.Game.Resources.Localisation.OnlinePlay"; /// - /// "This duration is only available for osu!supporters." + /// "Playlist durations longer than 2 weeks require an active osu!supporter tag." /// - public static LocalisableString SupporterOnlyDurationNotice => new TranslatableString(getKey(@"supporter_only_duration_notice"), @"This duration is only available for osu!supporters."); + public static LocalisableString SupporterOnlyDurationNotice => new TranslatableString(getKey(@"supporter_only_duration_notice"), @"Playlist durations longer than 2 weeks require an active osu!supporter tag."); private static string getKey(string key) => $@"{prefix}:{key}"; } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index f7bd7fc530..84e419d67a 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -371,7 +371,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private bool hasValidSettings => RoomID.Value == null && NameField.Text.Length > 0 && Playlist.Count > 0 && hasValidDuration; - private bool hasValidDuration => DurationField.Current.Value < TimeSpan.FromDays(31) || localUser.Value.IsSupporter; + private bool hasValidDuration => DurationField.Current.Value <= TimeSpan.FromDays(14) || localUser.Value.IsSupporter; private void apply() { From 26c128a09323cb0ee00cdb3c93d0463367b2f451 Mon Sep 17 00:00:00 2001 From: QuantumSno Date: Fri, 28 Jul 2023 14:39:30 -0400 Subject: [PATCH 011/124] added keybind and localization string --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 6 +++++- osu.Game/Localisation/GlobalActionKeyBindingStrings.cs | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 01c454e3f9..3d24afbb16 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -116,9 +116,10 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.F3 }, GlobalAction.DecreaseScrollSpeed), new KeyBinding(new[] { InputKey.F4 }, GlobalAction.IncreaseScrollSpeed), new KeyBinding(new[] { InputKey.Shift, InputKey.Tab }, GlobalAction.ToggleInGameInterface), + new KeyBinding(InputKey.Tab, GlobalAction.ToggleInGameLeaderboard), new KeyBinding(InputKey.MouseMiddle, GlobalAction.PauseGameplay), new KeyBinding(InputKey.Control, GlobalAction.HoldForHUD), - new KeyBinding(InputKey.Tab, GlobalAction.ToggleChatFocus), + new KeyBinding(InputKey.Enter, GlobalAction.ToggleChatFocus), new KeyBinding(InputKey.F1, GlobalAction.SaveReplay), new KeyBinding(InputKey.F2, GlobalAction.ExportReplay), }; @@ -285,6 +286,9 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleInGameInterface))] ToggleInGameInterface, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleInGameLeaderboard))] + ToggleInGameLeaderboard, + // Song select keybindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleModSelection))] ToggleModSelection, diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index f93d86225c..ceefc27968 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -219,6 +219,11 @@ namespace osu.Game.Localisation /// public static LocalisableString ToggleInGameInterface => new TranslatableString(getKey(@"toggle_in_game_interface"), @"Toggle in-game interface"); + /// + /// "Toggle in-game leaderboard" + /// + public static LocalisableString ToggleInGameLeaderboard => new TranslatableString(getKey(@"toggle_in_game_leaderboard"), @"Toggle in-game leaderboard"); + /// /// "Toggle mod select" /// From a4065486c198fd49fd538ba779f7e6cfb08fe79e Mon Sep 17 00:00:00 2001 From: QuantumSno Date: Fri, 28 Jul 2023 14:39:41 -0400 Subject: [PATCH 012/124] bound bind during gameplay --- osu.Game/Screens/Play/HUDOverlay.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index d11171e3fe..9c001b3db3 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -78,6 +78,7 @@ namespace osu.Game.Screens.Play public Bindable ShowHud { get; } = new BindableBool(); private Bindable configVisibilityMode; + private Bindable configLeaderboardVisibility; private Bindable configSettingsOverlay; private readonly BindableBool replayLoaded = new BindableBool(); @@ -179,6 +180,7 @@ namespace osu.Game.Screens.Play ModDisplay.Current.Value = mods; configVisibilityMode = config.GetBindable(OsuSetting.HUDVisibilityMode); + configLeaderboardVisibility = config.GetBindable(OsuSetting.GameplayLeaderboard); configSettingsOverlay = config.GetBindable(OsuSetting.ReplaySettingsOverlay); if (configVisibilityMode.Value == HUDVisibilityMode.Never && !hasShownNotificationOnce) @@ -381,6 +383,10 @@ namespace osu.Game.Screens.Play } return true; + + case GlobalAction.ToggleInGameLeaderboard: + configLeaderboardVisibility.Value = !configLeaderboardVisibility.Value; + return true; } return false; From 65b4ae506ee563b9a2b940c4a72b9c9d34ac5dc2 Mon Sep 17 00:00:00 2001 From: QuantumSno Date: Sat, 29 Jul 2023 13:18:47 -0400 Subject: [PATCH 013/124] Moved enum to bottom of enumeration table --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 3d24afbb16..79f098f90e 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -286,9 +286,6 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleInGameInterface))] ToggleInGameInterface, - [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleInGameLeaderboard))] - ToggleInGameLeaderboard, - // Song select keybindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleModSelection))] ToggleModSelection, @@ -382,5 +379,9 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleReplaySettings))] ToggleReplaySettings, + + // Editor (cont) + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleInGameLeaderboard))] + ToggleInGameLeaderboard, } } From 2e2b0c4e4109905f46e17f478ab1c0e936abfb23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 1 Aug 2023 00:22:08 +0200 Subject: [PATCH 014/124] Move `SliderWithTextBoxInput` to more general namespace --- .../UserInterfaceV2}/SliderWithTextBoxInput.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename osu.Game/{Screens/Edit/Timing => Graphics/UserInterfaceV2}/SliderWithTextBoxInput.cs (97%) diff --git a/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs b/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs similarity index 97% rename from osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs rename to osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs index 1bf0e5299d..3f4e133d45 100644 --- a/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs +++ b/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs @@ -8,12 +8,11 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; -using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays.Settings; using osu.Game.Utils; using osuTK; -namespace osu.Game.Screens.Edit.Timing +namespace osu.Game.Graphics.UserInterfaceV2 { public partial class SliderWithTextBoxInput : CompositeDrawable, IHasCurrentValue where T : struct, IEquatable, IComparable, IConvertible From ca81f233563aca7d39822a6d551fc8824c1d3d48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 1 Aug 2023 00:44:05 +0200 Subject: [PATCH 015/124] Add test covering non-instantaneous behaviour --- .../TestSceneSliderWithTextBoxInput.cs | 63 +++++++++++++++++++ .../UserInterfaceV2/SliderWithTextBoxInput.cs | 2 - 2 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs new file mode 100644 index 0000000000..dc70e2b9cb --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs @@ -0,0 +1,63 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public partial class TestSceneSliderWithTextBoxInput : OsuManualInputManagerTestScene + { + private SliderWithTextBoxInput sliderWithTextBoxInput = null!; + + private OsuSliderBar slider => sliderWithTextBoxInput.ChildrenOfType>().Single(); + private Nub nub => sliderWithTextBoxInput.ChildrenOfType().Single(); + private OsuTextBox textBox => sliderWithTextBoxInput.ChildrenOfType().Single(); + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create slider", () => Child = sliderWithTextBoxInput = new SliderWithTextBoxInput("Test Slider") + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 0.5f, + Current = new BindableFloat + { + MinValue = -5, + MaxValue = 5, + Precision = 0.2f + } + }); + } + + [Test] + public void TestNonInstantaneousMode() + { + AddStep("focus textbox", () => InputManager.ChangeFocus(textBox)); + AddStep("change text", () => textBox.Text = "3"); + AddAssert("slider not moved", () => slider.Current.Value, () => Is.Zero); + AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.Zero); + + AddStep("commit text", () => InputManager.Key(Key.Enter)); + AddAssert("slider moved", () => slider.Current.Value, () => Is.EqualTo(3)); + AddAssert("current changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(3)); + + AddStep("move mouse to nub", () => InputManager.MoveMouseTo(nub)); + AddStep("hold left mouse", () => InputManager.PressButton(MouseButton.Left)); + AddStep("move mouse to minimum", () => InputManager.MoveMouseTo(sliderWithTextBoxInput.ScreenSpaceDrawQuad.BottomLeft)); + AddAssert("textbox not changed", () => textBox.Current.Value, () => Is.EqualTo("3")); + AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(3)); + + AddStep("release left mouse", () => InputManager.ReleaseButton(MouseButton.Left)); + AddAssert("textbox changed", () => textBox.Current.Value, () => Is.EqualTo("-5")); + AddAssert("current changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); + } + } +} diff --git a/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs b/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs index 3f4e133d45..38de0ea94a 100644 --- a/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs +++ b/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs @@ -51,8 +51,6 @@ namespace osu.Game.Graphics.UserInterfaceV2 textBox.OnCommit += (t, isNew) => { - if (!isNew) return; - try { switch (slider.Current) From 0b019f1d4526dbad742fb8d36d3d7923b575c091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 1 Aug 2023 00:45:57 +0200 Subject: [PATCH 016/124] Add test covering desired instantaneous behaviour --- .../TestSceneSliderWithTextBoxInput.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs index dc70e2b9cb..37f18cd3dd 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs @@ -59,5 +59,28 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("textbox changed", () => textBox.Current.Value, () => Is.EqualTo("-5")); AddAssert("current changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); } + + [Test] + public void TestInstantaneousMode() + { + AddStep("focus textbox", () => InputManager.ChangeFocus(textBox)); + AddStep("change text", () => textBox.Text = "3"); + AddAssert("slider moved", () => slider.Current.Value, () => Is.EqualTo(3)); + AddAssert("current changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(3)); + + AddStep("commit text", () => InputManager.Key(Key.Enter)); + AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(3)); + AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(3)); + + AddStep("move mouse to nub", () => InputManager.MoveMouseTo(nub)); + AddStep("hold left mouse", () => InputManager.PressButton(MouseButton.Left)); + AddStep("move mouse to minimum", () => InputManager.MoveMouseTo(sliderWithTextBoxInput.ScreenSpaceDrawQuad.BottomLeft)); + AddAssert("textbox changed", () => textBox.Current.Value, () => Is.EqualTo("-5")); + AddAssert("current changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); + + AddStep("release left mouse", () => InputManager.ReleaseButton(MouseButton.Left)); + AddAssert("textbox not changed", () => textBox.Current.Value, () => Is.EqualTo("-5")); + AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); + } } } From 4fb0ff8800281dc7c4fd97c99359791e1edd6390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 1 Aug 2023 00:51:29 +0200 Subject: [PATCH 017/124] Implement instantaneous `SliderWithTextBoxInput` mode --- .../TestSceneSliderWithTextBoxInput.cs | 4 + .../UserInterfaceV2/SliderWithTextBoxInput.cs | 128 ++++++++++++------ 2 files changed, 88 insertions(+), 44 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs index 37f18cd3dd..797afccc77 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs @@ -40,6 +40,8 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestNonInstantaneousMode() { + AddStep("set instantaneous to false", () => sliderWithTextBoxInput.Instantaneous = false); + AddStep("focus textbox", () => InputManager.ChangeFocus(textBox)); AddStep("change text", () => textBox.Text = "3"); AddAssert("slider not moved", () => slider.Current.Value, () => Is.Zero); @@ -63,6 +65,8 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestInstantaneousMode() { + AddStep("set instantaneous to true", () => sliderWithTextBoxInput.Instantaneous = true); + AddStep("focus textbox", () => InputManager.ChangeFocus(textBox)); AddStep("change text", () => textBox.Text = "3"); AddAssert("slider moved", () => slider.Current.Value, () => Is.EqualTo(3)); diff --git a/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs b/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs index 38de0ea94a..fc0e4d2083 100644 --- a/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs +++ b/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs @@ -17,12 +17,42 @@ namespace osu.Game.Graphics.UserInterfaceV2 public partial class SliderWithTextBoxInput : CompositeDrawable, IHasCurrentValue where T : struct, IEquatable, IComparable, IConvertible { + /// + /// A custom step value for each key press which actuates a change on this control. + /// + public float KeyboardStep + { + get => slider.KeyboardStep; + set => slider.KeyboardStep = value; + } + + public Bindable Current + { + get => slider.Current; + set => slider.Current = value; + } + + private bool instantaneous; + + /// + /// Whether changes to the slider should instantaneously transfer to the text box (and vice versa). + /// If , the transfer will happen on text box commit (explicit, or implicit via focus loss), or on slider drag end. + /// + public bool Instantaneous + { + get => instantaneous; + set + { + instantaneous = value; + slider.TransferValueOnCommit = !instantaneous; + } + } + private readonly SettingsSlider slider; + private readonly LabelledTextBox textBox; public SliderWithTextBoxInput(LocalisableString labelText) { - LabelledTextBox textBox; - RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -49,55 +79,65 @@ namespace osu.Game.Graphics.UserInterfaceV2 }, }; - textBox.OnCommit += (t, isNew) => - { - try - { - switch (slider.Current) - { - case Bindable bindableInt: - bindableInt.Value = int.Parse(t.Text); - break; + textBox.OnCommit += textCommitted; + textBox.Current.BindValueChanged(textChanged); - case Bindable bindableDouble: - bindableDouble.Value = double.Parse(t.Text); - break; - - default: - slider.Current.Parse(t.Text); - break; - } - } - catch - { - // TriggerChange below will restore the previous text value on failure. - } - - // This is run regardless of parsing success as the parsed number may not actually trigger a change - // due to bindable clamping. Even in such a case we want to update the textbox to a sane visual state. - Current.TriggerChange(); - }; - - Current.BindValueChanged(_ => - { - decimal decimalValue = slider.Current.Value.ToDecimal(NumberFormatInfo.InvariantInfo); - textBox.Text = decimalValue.ToString($@"N{FormatUtils.FindPrecision(decimalValue)}"); - }, true); + Current.BindValueChanged(updateTextBoxFromSlider, true); } - /// - /// A custom step value for each key press which actuates a change on this control. - /// - public float KeyboardStep + private bool updatingFromTextBox; + + private void textChanged(ValueChangedEvent change) { - get => slider.KeyboardStep; - set => slider.KeyboardStep = value; + if (!instantaneous) return; + + tryUpdateSliderFromTextBox(); } - public Bindable Current + private void textCommitted(TextBox t, bool isNew) { - get => slider.Current; - set => slider.Current = value; + tryUpdateSliderFromTextBox(); + + // If the attempted update above failed, restore text box to match the slider. + Current.TriggerChange(); + } + + private void tryUpdateSliderFromTextBox() + { + updatingFromTextBox = true; + + try + { + switch (slider.Current) + { + case Bindable bindableInt: + bindableInt.Value = int.Parse(textBox.Current.Value); + break; + + case Bindable bindableDouble: + bindableDouble.Value = double.Parse(textBox.Current.Value); + break; + + default: + slider.Current.Parse(textBox.Current.Value); + break; + } + } + catch + { + // ignore parsing failures. + // sane state will eventually be restored by a commit (either explicit, or implicit via focus loss). + } + + updatingFromTextBox = false; + } + + private void updateTextBoxFromSlider(ValueChangedEvent _) + { + if (updatingFromTextBox) return; + + decimal decimalValue = slider.Current.Value.ToDecimal(NumberFormatInfo.InvariantInfo); + textBox.Text = decimalValue.ToString($@"N{FormatUtils.FindPrecision(decimalValue)}"); } } } From 049215f2dc1cbdd2ae17c10d28a0430b0dd28ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 1 Aug 2023 00:54:40 +0200 Subject: [PATCH 018/124] Add test coverage for invalid input scenarios --- .../TestSceneSliderWithTextBoxInput.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs index 797afccc77..d23fcebae3 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs @@ -60,6 +60,26 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("release left mouse", () => InputManager.ReleaseButton(MouseButton.Left)); AddAssert("textbox changed", () => textBox.Current.Value, () => Is.EqualTo("-5")); AddAssert("current changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); + + AddStep("focus textbox", () => InputManager.ChangeFocus(textBox)); + AddStep("set text to invalid", () => textBox.Text = "garbage"); + AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5)); + AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); + + AddStep("commit text", () => InputManager.Key(Key.Enter)); + AddAssert("text restored", () => textBox.Text, () => Is.EqualTo("-5")); + AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5)); + AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); + + AddStep("focus textbox", () => InputManager.ChangeFocus(textBox)); + AddStep("set text to invalid", () => textBox.Text = "garbage"); + AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5)); + AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); + + AddStep("lose focus", () => InputManager.ChangeFocus(null)); + AddAssert("text restored", () => textBox.Text, () => Is.EqualTo("-5")); + AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5)); + AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); } [Test] @@ -85,6 +105,26 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("release left mouse", () => InputManager.ReleaseButton(MouseButton.Left)); AddAssert("textbox not changed", () => textBox.Current.Value, () => Is.EqualTo("-5")); AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); + + AddStep("focus textbox", () => InputManager.ChangeFocus(textBox)); + AddStep("set text to invalid", () => textBox.Text = "garbage"); + AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5)); + AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); + + AddStep("commit text", () => InputManager.Key(Key.Enter)); + AddAssert("text restored", () => textBox.Text, () => Is.EqualTo("-5")); + AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5)); + AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); + + AddStep("focus textbox", () => InputManager.ChangeFocus(textBox)); + AddStep("set text to invalid", () => textBox.Text = "garbage"); + AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5)); + AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); + + AddStep("lose focus", () => InputManager.ChangeFocus(null)); + AddAssert("text restored", () => textBox.Text, () => Is.EqualTo("-5")); + AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5)); + AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); } } } From 426bc52fd544a7512f27284a28ef6cc50225d286 Mon Sep 17 00:00:00 2001 From: QuantumSno Date: Tue, 1 Aug 2023 14:27:37 -0400 Subject: [PATCH 019/124] Updated schema_version number and added braces around some migration cases to help with re-using variable names --- osu.Game/Database/RealmAccess.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index f32b161bb6..c949ccbe43 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -82,8 +82,9 @@ namespace osu.Game.Database /// 30 2023-06-16 Run migration of old lazer scores again. This time with more correct rounding considerations. /// 31 2023-06-26 Add Version and LegacyTotalScore to ScoreInfo, set Version to 30000002 and copy TotalScore into LegacyTotalScore for legacy scores. /// 32 2023-07-09 Populate legacy scores with the ScoreV2 mod (and restore TotalScore to the legacy total for such scores) using replay files. + /// 32 2023-08-01 Added a new keybind that toggles the in-game leaderboard on and off /// - private const int schema_version = 32; + private const int schema_version = 33; /// /// Lock object which is held during sections, blocking realm retrieval during blocking periods. @@ -771,6 +772,7 @@ namespace osu.Game.Database break; case 8: + { // Ctrl -/+ now adjusts UI scale so let's clear any bindings which overlap these combinations. // New defaults will be populated by the key store afterwards. var keyBindings = migration.NewRealm.All(); @@ -784,6 +786,7 @@ namespace osu.Game.Database migration.NewRealm.Remove(decreaseSpeedBinding); break; + } case 9: // Pretty pointless to do this as beatmaps aren't really loaded via realm yet, but oh well. @@ -838,6 +841,7 @@ namespace osu.Game.Database break; case 11: + { string keyBindingClassName = getMappedOrOriginalName(typeof(RealmKeyBinding)); if (!migration.OldRealm.Schema.TryFindObjectSchema(keyBindingClassName, out _)) @@ -864,6 +868,7 @@ namespace osu.Game.Database } break; + } case 14: foreach (var beatmap in migration.NewRealm.All()) @@ -1012,6 +1017,14 @@ namespace osu.Game.Database break; } + + case 33: + { + var oldKeyBinds = migration.NewRealm.All(); + var newKeyBinds = migration.NewRealm.All(); + + break; + } } Logger.Log($"Migration completed in {stopwatch.ElapsedMilliseconds}ms"); From 5443f89dca0823fac233d54d1dd850b793b4145c Mon Sep 17 00:00:00 2001 From: QuantumSno Date: Tue, 1 Aug 2023 16:58:52 -0400 Subject: [PATCH 020/124] Assuming its still set to tab, removes binding for toggle chat so itll be set to the new default --- osu.Game/Database/RealmAccess.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index c949ccbe43..d751cce834 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -1020,8 +1020,14 @@ namespace osu.Game.Database case 33: { - var oldKeyBinds = migration.NewRealm.All(); - var newKeyBinds = migration.NewRealm.All(); + // Get all current keybinds, and find the toggle chat bind + // (by default it used to overlap the new toggle leaderboard) + var newKeyBindings = migration.NewRealm.All().ToList(); + var toggleChatBind = newKeyBindings.FirstOrDefault(bind => bind.ActionInt == (int)GlobalAction.ToggleChatFocus); + + // If we have a bind for it, and that bind is tab, remove it + if (toggleChatBind != default && toggleChatBind.KeyCombination.Keys.SequenceEqual(new[] { InputKey.Tab })) + migration.NewRealm.Remove(toggleChatBind); break; } From 87fee001c786b29db34063ef3350e9a9f024d3ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Aug 2023 18:26:36 +0900 Subject: [PATCH 021/124] Fix multiplayer spectator potentially taking too long to start When watching from the middle of gameplay, due to a series of failures, `SpectatorClock` would not get seeked to the current time, causing all clients to look like they were out of sync. This is a hotfix for the issue. A better fix will require framework changes or considerable restructuring. I'd recommend testing this works in practice and agreeing that while it is a hack, it's likely not going to cause issues and is something we want to see fixed sooner rather than later. --- .../Multiplayer/Spectate/SpectatorPlayerClock.cs | 2 ++ osu.Game/Screens/Play/GameplayClockContainer.cs | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorPlayerClock.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorPlayerClock.cs index 45615d4e19..2ce78818a0 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorPlayerClock.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorPlayerClock.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Logging; using osu.Framework.Timing; using osu.Game.Screens.Play; @@ -59,6 +60,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public bool Seek(double position) { + Logger.Log($"{nameof(SpectatorPlayerClock)} seeked to {position}"); CurrentTime = position; return true; } diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 22e6884526..226108209a 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -160,6 +160,13 @@ namespace osu.Game.Screens.Play Seek(StartTime); + // This is a workaround for the fact that DecoupleableInterpolatingFramedClock doesn't seek the source + // if the source is not IsRunning. (see https://github.com/ppy/osu-framework/blob/2102638056dfcf85d21b4d85266d53b5dd018767/osu.Framework/Timing/DecoupleableInterpolatingFramedClock.cs#L209-L210) + // + // This breaks in multiplayer spectator. + // I hope to remove this once we knock some sense into clocks in general. + (SourceClock as IAdjustableClock)?.Seek(StartTime); + if (!wasPaused || startClock) Start(); } From 71c42167e7e8fce7e4671f67e672786e5584fe21 Mon Sep 17 00:00:00 2001 From: QuantumSno Date: Thu, 10 Aug 2023 10:17:12 -0400 Subject: [PATCH 022/124] Updated wording on realm comments --- osu.Game/Database/RealmAccess.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index d751cce834..6eb1e3bf9b 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -1021,11 +1021,11 @@ namespace osu.Game.Database case 33: { // Get all current keybinds, and find the toggle chat bind - // (by default it used to overlap the new toggle leaderboard) + // (by default it would to overlap the new toggle leaderboard) var newKeyBindings = migration.NewRealm.All().ToList(); var toggleChatBind = newKeyBindings.FirstOrDefault(bind => bind.ActionInt == (int)GlobalAction.ToggleChatFocus); - // If we have a bind for it, and that bind is tab, remove it + // If we have a bind for it, and that bind is still tab, remove it if (toggleChatBind != default && toggleChatBind.KeyCombination.Keys.SequenceEqual(new[] { InputKey.Tab })) migration.NewRealm.Remove(toggleChatBind); From 5d6ebb4df9f1fffd6bdadfe454dff9d1249dd589 Mon Sep 17 00:00:00 2001 From: QuantumSno Date: Thu, 10 Aug 2023 10:18:15 -0400 Subject: [PATCH 023/124] Removed comments organizing GlobalActionContainer.cs, since they were incorrectly grouping actions --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 79f098f90e..1090eeb462 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -205,7 +205,6 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleMute))] ToggleMute, - // In-Game Keybindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SkipCutscene))] SkipCutscene, @@ -233,7 +232,6 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.QuickExit))] QuickExit, - // Game-wide beatmap music controller keybindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.MusicNext))] MusicNext, @@ -261,7 +259,6 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.PauseGameplay))] PauseGameplay, - // Editor [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorSetupMode))] EditorSetupMode, @@ -286,7 +283,6 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleInGameInterface))] ToggleInGameInterface, - // Song select keybindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleModSelection))] ToggleModSelection, @@ -380,7 +376,6 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleReplaySettings))] ToggleReplaySettings, - // Editor (cont) [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleInGameLeaderboard))] ToggleInGameLeaderboard, } From 2b738edb93dbc34d1f850db710221656871c3e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 10 Aug 2023 22:55:02 +0200 Subject: [PATCH 024/124] Reword / fix comments --- osu.Game/Database/RealmAccess.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 6eb1e3bf9b..90e39da201 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -82,7 +82,7 @@ namespace osu.Game.Database /// 30 2023-06-16 Run migration of old lazer scores again. This time with more correct rounding considerations. /// 31 2023-06-26 Add Version and LegacyTotalScore to ScoreInfo, set Version to 30000002 and copy TotalScore into LegacyTotalScore for legacy scores. /// 32 2023-07-09 Populate legacy scores with the ScoreV2 mod (and restore TotalScore to the legacy total for such scores) using replay files. - /// 32 2023-08-01 Added a new keybind that toggles the in-game leaderboard on and off + /// 33 2023-08-01 Reset default chat toggle keybind to avoid conflict with leaderboard toggle keybind. /// private const int schema_version = 33; @@ -1020,12 +1020,11 @@ namespace osu.Game.Database case 33: { - // Get all current keybinds, and find the toggle chat bind - // (by default it would to overlap the new toggle leaderboard) + // Clear default bindings for the chat focus toggle, + // as they would conflict with the newly-added leaderboard toggle. var newKeyBindings = migration.NewRealm.All().ToList(); var toggleChatBind = newKeyBindings.FirstOrDefault(bind => bind.ActionInt == (int)GlobalAction.ToggleChatFocus); - // If we have a bind for it, and that bind is still tab, remove it if (toggleChatBind != default && toggleChatBind.KeyCombination.Keys.SequenceEqual(new[] { InputKey.Tab })) migration.NewRealm.Remove(toggleChatBind); From 37361cd6837b4714a3f161a1e05873ef3f39456f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 12 Aug 2023 00:49:39 +0200 Subject: [PATCH 025/124] Repurpose `Flat{File -> }WorkingBeatmap` --- osu.Desktop/LegacyIpc/LegacyTcpIpcProvider.cs | 2 +- ...orkingBeatmap.cs => FlatWorkingBeatmap.cs} | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) rename osu.Game/Beatmaps/{FlatFileWorkingBeatmap.cs => FlatWorkingBeatmap.cs} (76%) diff --git a/osu.Desktop/LegacyIpc/LegacyTcpIpcProvider.cs b/osu.Desktop/LegacyIpc/LegacyTcpIpcProvider.cs index 5d950eef55..d1ac42f22b 100644 --- a/osu.Desktop/LegacyIpc/LegacyTcpIpcProvider.cs +++ b/osu.Desktop/LegacyIpc/LegacyTcpIpcProvider.cs @@ -75,7 +75,7 @@ namespace osu.Desktop.LegacyIpc case LegacyIpcDifficultyCalculationRequest req: try { - WorkingBeatmap beatmap = new FlatFileWorkingBeatmap(req.BeatmapFile); + WorkingBeatmap beatmap = new FlatWorkingBeatmap(req.BeatmapFile); var ruleset = beatmap.BeatmapInfo.Ruleset.CreateInstance(); Mod[] mods = ruleset.ConvertFromLegacyMods((LegacyMods)req.Mods).ToArray(); diff --git a/osu.Game/Beatmaps/FlatFileWorkingBeatmap.cs b/osu.Game/Beatmaps/FlatWorkingBeatmap.cs similarity index 76% rename from osu.Game/Beatmaps/FlatFileWorkingBeatmap.cs rename to osu.Game/Beatmaps/FlatWorkingBeatmap.cs index d20baf1edb..c2505ec109 100644 --- a/osu.Game/Beatmaps/FlatFileWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/FlatWorkingBeatmap.cs @@ -12,25 +12,26 @@ using osu.Game.Skinning; namespace osu.Game.Beatmaps { /// - /// A which can be constructed directly from a .osu file, providing an implementation for + /// A which can be constructed directly from an .osu file (via ) + /// or an instance (via , + /// providing an implementation for /// . /// - public class FlatFileWorkingBeatmap : WorkingBeatmap + public class FlatWorkingBeatmap : WorkingBeatmap { - private readonly Beatmap beatmap; + private readonly IBeatmap beatmap; - public FlatFileWorkingBeatmap(string file, int? beatmapId = null) - : this(readFromFile(file), beatmapId) + public FlatWorkingBeatmap(string file, int? beatmapId = null) + : this(readFromFile(file)) { + if (beatmapId.HasValue) + beatmap.BeatmapInfo.OnlineID = beatmapId.Value; } - private FlatFileWorkingBeatmap(Beatmap beatmap, int? beatmapId = null) + public FlatWorkingBeatmap(IBeatmap beatmap) : base(beatmap.BeatmapInfo, null) { this.beatmap = beatmap; - - if (beatmapId.HasValue) - beatmap.BeatmapInfo.OnlineID = beatmapId.Value; } private static Beatmap readFromFile(string filename) From 253392409e70f5914988c8af85d3a7aabec25c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 12 Aug 2023 00:50:31 +0200 Subject: [PATCH 026/124] Fix `LegacyBeatmapExporter` not converting beatmap between decode and re-encode --- osu.Game/Database/LegacyBeatmapExporter.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs index e054652efa..a874353f73 100644 --- a/osu.Game/Database/LegacyBeatmapExporter.cs +++ b/osu.Game/Database/LegacyBeatmapExporter.cs @@ -29,9 +29,9 @@ namespace osu.Game.Database protected override Stream? GetFileContents(BeatmapSetInfo model, INamedFileUsage file) { - bool isBeatmap = model.Beatmaps.Any(o => o.Hash == file.File.Hash); + var beatmapInfo = model.Beatmaps.SingleOrDefault(o => o.Hash == file.File.Hash); - if (!isBeatmap) + if (beatmapInfo == null) return base.GetFileContents(model, file); // Read the beatmap contents and skin @@ -43,6 +43,9 @@ namespace osu.Game.Database using var contentStreamReader = new LineBufferedReader(contentStream); var beatmapContent = new LegacyBeatmapDecoder().Decode(contentStreamReader); + var workingBeatmap = new FlatWorkingBeatmap(beatmapContent); + var playableBeatmap = workingBeatmap.GetPlayableBeatmap(beatmapInfo.Ruleset); + using var skinStream = base.GetFileContents(model, file); if (skinStream == null) @@ -56,10 +59,10 @@ namespace osu.Game.Database // Convert beatmap elements to be compatible with legacy format // So we truncate time and position values to integers, and convert paths with multiple segments to bezier curves - foreach (var controlPoint in beatmapContent.ControlPointInfo.AllControlPoints) + foreach (var controlPoint in playableBeatmap.ControlPointInfo.AllControlPoints) controlPoint.Time = Math.Floor(controlPoint.Time); - foreach (var hitObject in beatmapContent.HitObjects) + foreach (var hitObject in playableBeatmap.HitObjects) { // Truncate end time before truncating start time because end time is dependent on start time if (hitObject is IHasDuration hasDuration && hitObject is not IHasPath) @@ -86,7 +89,7 @@ namespace osu.Game.Database // Encode to legacy format var stream = new MemoryStream(); using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) - new LegacyBeatmapEncoder(beatmapContent, beatmapSkin).Encode(sw); + new LegacyBeatmapEncoder(playableBeatmap, beatmapSkin).Encode(sw); stream.Seek(0, SeekOrigin.Begin); From 562af8b46d1cbe3bf028e70cd044b3f93e71aaa4 Mon Sep 17 00:00:00 2001 From: QuantumSno Date: Sat, 12 Aug 2023 00:18:10 -0400 Subject: [PATCH 027/124] Updated test TestSceneGameplayChatDisplay.cs. Now uses new default bind for toggle chat (enter) --- .../Multiplayer/TestSceneGameplayChatDisplay.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs index 979cb4424e..99b898efda 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs @@ -84,12 +84,12 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - public void TestFocusOnTabKeyWhenExpanded() + public void TestFocusOnEnterKeyWhenExpanded() { setLocalUserPlaying(true); assertChatFocused(false); - AddStep("press tab", () => InputManager.Key(Key.Tab)); + AddStep("press enter", () => InputManager.Key(Key.Enter)); assertChatFocused(true); } @@ -99,19 +99,19 @@ namespace osu.Game.Tests.Visual.Multiplayer setLocalUserPlaying(true); assertChatFocused(false); - AddStep("press tab", () => InputManager.Key(Key.Tab)); + AddStep("press enter", () => InputManager.Key(Key.Enter)); assertChatFocused(true); AddStep("press escape", () => InputManager.Key(Key.Escape)); assertChatFocused(false); } [Test] - public void TestFocusOnTabKeyWhenNotExpanded() + public void TestFocusOnEnterKeyWhenNotExpanded() { AddStep("set not expanded", () => chatDisplay.Expanded.Value = false); AddUntilStep("is not visible", () => !chatDisplay.IsPresent); - AddStep("press tab", () => InputManager.Key(Key.Tab)); + AddStep("press enter", () => InputManager.Key(Key.Enter)); assertChatFocused(true); AddUntilStep("is visible", () => chatDisplay.IsPresent); @@ -126,11 +126,11 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("set not expanded", () => chatDisplay.Expanded.Value = false); AddUntilStep("is not visible", () => !chatDisplay.IsPresent); - AddStep("press tab", () => InputManager.Key(Key.Tab)); + AddStep("press enter", () => InputManager.Key(Key.Enter)); assertChatFocused(true); AddUntilStep("is visible", () => chatDisplay.IsPresent); - AddStep("press tab", () => InputManager.Key(Key.Tab)); + AddStep("press enter", () => InputManager.Key(Key.Enter)); assertChatFocused(false); AddUntilStep("is not visible", () => !chatDisplay.IsPresent); } From 0eaa38312078352184e8bb3b581daa2c85534cbc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 12 Aug 2023 17:54:05 +0900 Subject: [PATCH 028/124] 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 1ac85532b8..46f3247a8c 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + - + \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/AndroidManifest.xml b/osu.Game.Rulesets.Mania.Tests.Android/AndroidManifest.xml index 4a1545a423..f5a49210ea 100644 --- a/osu.Game.Rulesets.Mania.Tests.Android/AndroidManifest.xml +++ b/osu.Game.Rulesets.Mania.Tests.Android/AndroidManifest.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/AndroidManifest.xml b/osu.Game.Rulesets.Osu.Tests.Android/AndroidManifest.xml index 45d27dda70..ed4725dd94 100644 --- a/osu.Game.Rulesets.Osu.Tests.Android/AndroidManifest.xml +++ b/osu.Game.Rulesets.Osu.Tests.Android/AndroidManifest.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/AndroidManifest.xml b/osu.Game.Rulesets.Taiko.Tests.Android/AndroidManifest.xml index 452b9683ec..cc88d3080a 100644 --- a/osu.Game.Rulesets.Taiko.Tests.Android/AndroidManifest.xml +++ b/osu.Game.Rulesets.Taiko.Tests.Android/AndroidManifest.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/osu.Game.Tests.Android/AndroidManifest.xml b/osu.Game.Tests.Android/AndroidManifest.xml index f25b2e5328..6f91fb928c 100644 --- a/osu.Game.Tests.Android/AndroidManifest.xml +++ b/osu.Game.Tests.Android/AndroidManifest.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file From 912f31dabc7160fbd32ccb0a47fd733e427274e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 20 Aug 2023 23:37:11 +0200 Subject: [PATCH 103/124] Declare media permissions in game project for editor usage --- osu.Android/AndroidManifest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Android/AndroidManifest.xml b/osu.Android/AndroidManifest.xml index fb54c8e151..af102a1e4e 100644 --- a/osu.Android/AndroidManifest.xml +++ b/osu.Android/AndroidManifest.xml @@ -2,4 +2,7 @@ + + + \ No newline at end of file From a942b6ff745193ff9b3b0f8c624d5905a4495225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 21 Aug 2023 07:27:02 +0200 Subject: [PATCH 104/124] Replace inline comment with actual explanation of what's happening --- osu.Game/Database/LegacyBeatmapExporter.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs index c00977d072..ece705f685 100644 --- a/osu.Game/Database/LegacyBeatmapExporter.cs +++ b/osu.Game/Database/LegacyBeatmapExporter.cs @@ -72,7 +72,16 @@ namespace osu.Game.Database if (hitObject is not IHasPath hasPath) continue; - // Make sure the last control point is inherit type + // stable's hit object parsing expects the entire slider to use only one type of curve, + // and happens to use the last non-empty curve type read for the entire slider. + // this clear of the last control point type handles an edge case + // wherein the last control point of an otherwise-single-segment slider path has a different type than previous, + // which would lead to sliders being mangled when exported back to stable. + // normally, that would be handled by the `BezierConverter.ConvertToModernBezier()` call below, + // which outputs a slider path containing only Bezier control points, + // but a non-inherited last control point is (rightly) not considered to be starting a new segment, + // therefore it would fail to clear the `CountSegments() <= 1` check. + // by clearing explicitly we both fix the issue and avoid unnecessary conversions to Bezier. if (hasPath.Path.ControlPoints.Count > 1) hasPath.Path.ControlPoints[^1].Type = null; From dd1ac461db2c779c5f9a24bfedbdb3437f503c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 21 Aug 2023 08:23:58 +0200 Subject: [PATCH 105/124] Reformat xmldoc --- osu.Game/Rulesets/Objects/SliderPath.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 1c02b18a0f..34113285a4 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -201,13 +201,20 @@ namespace osu.Game.Rulesets.Objects /// Returns the progress values at which (control point) segments of the path end. /// Ranges from 0 (beginning of the path) to 1 (end of the path) to infinity (beyond the end of the path). /// - /// In case is less than , + /// + /// truncates the progression values to [0,1], + /// so you can't use this method in conjunction with that one to retrieve the positions of segment ends beyond the end of the path. + /// + /// + /// + /// In case is less than , /// the last segment ends after the end of the path, hence it returns a value greater than 1. - /// + /// + /// /// In case is greater than , - /// the last segment ends before the end of the path, hence it returns a value less than 1. - /// truncates the progression values to [0,1], - /// so you can't use this method to retrieve the positions of segment ends beyond the end of the path. + /// the last segment ends before the end of the path, hence it returns a value less than 1. + /// + /// public IEnumerable GetSegmentEnds() { ensureValid(); From 479c463751e0159049bb63fe27937072a9b836c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 21 Aug 2023 08:27:08 +0200 Subject: [PATCH 106/124] Explain why segment end positions are not recovered in test --- osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs index 16e4ae13d9..635d9f9604 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs @@ -207,6 +207,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("shorten last segment", () => path.ExpectedDistance.Value = 150); AddAssert("segment ends are correct", () => path.GetSegmentEnds(), () => Is.EqualTo(distances.Select(d => d / 150))); + // see remarks in `GetSegmentEnds()` xmldoc (`SliderPath.PositionAt()` clamps progress to [0,1]). AddAssert("segment end positions not recovered", () => path.GetSegmentEnds().Select(p => path.PositionAt(p)), () => Is.EqualTo(new[] { positions[1], From 5009fd379421f928bca35028b00e6b88374bdb5e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 20 Aug 2023 17:57:45 +0900 Subject: [PATCH 107/124] Add test coverage of song bar crash --- .../Components/TestSceneSongBar.cs | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs b/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs index 0f31192a9c..d52b453185 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs @@ -2,27 +2,34 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Beatmaps.Legacy; -using osu.Game.Tests.Visual; using osu.Game.Tournament.Components; using osu.Game.Tournament.Models; namespace osu.Game.Tournament.Tests.Components { [TestFixture] - public partial class TestSceneSongBar : OsuTestScene + public partial class TestSceneSongBar : TournamentTestScene { - [Cached] - private readonly LadderInfo ladder = new LadderInfo(); - private SongBar songBar = null!; [SetUpSteps] - public void SetUpSteps() + public override void SetUpSteps() { + base.SetUpSteps(); + + AddStep("setup picks bans", () => + { + Ladder.CurrentMatch.Value!.PicksBans.Add(new BeatmapChoice + { + BeatmapID = CreateSampleBeatmap().OnlineID, + Team = TeamColour.Red, + Type = ChoiceType.Pick, + }); + }); + AddStep("create bar", () => Child = songBar = new SongBar { RelativeSizeAxes = Axes.X, @@ -38,12 +45,14 @@ namespace osu.Game.Tournament.Tests.Components AddStep("set beatmap", () => { var beatmap = CreateAPIBeatmap(Ruleset.Value); + beatmap.CircleSize = 3.4f; beatmap.ApproachRate = 6.8f; beatmap.OverallDifficulty = 5.5f; beatmap.StarRating = 4.56f; beatmap.Length = 123456; beatmap.BPM = 133; + beatmap.OnlineID = CreateSampleBeatmap().OnlineID; songBar.Beatmap = new TournamentBeatmap(beatmap); }); From 1067769b24e924bb34d4cc8975e94e162c7198c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 21 Aug 2023 15:33:58 +0900 Subject: [PATCH 108/124] Remove masking on song bar Turns out this breaks when a border style is applied for picks/bans, and it wasn't doing much for visuals anyway. --- osu.Game.Tournament/Components/SongBar.cs | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index 3d060600f7..cde826628e 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -234,7 +234,7 @@ namespace osu.Game.Tournament.Components } } }, - new UnmaskedTournamentBeatmapPanel(beatmap) + new TournamentBeatmapPanel(beatmap) { RelativeSizeAxes = Axes.X, Width = 0.5f, @@ -277,18 +277,4 @@ namespace osu.Game.Tournament.Components } } } - - internal partial class UnmaskedTournamentBeatmapPanel : TournamentBeatmapPanel - { - public UnmaskedTournamentBeatmapPanel(IBeatmapInfo? beatmap, string mod = "") - : base(beatmap, mod) - { - } - - [BackgroundDependencyLoader] - private void load() - { - Masking = false; - } - } } From f03c64462e5bd6a21626e7030cfaaf13a50c2e78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 21 Aug 2023 08:58:49 +0200 Subject: [PATCH 109/124] Better convey meaning of zero last year placement via tooltip --- osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs index 241692d515..250d5acaae 100644 --- a/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs @@ -10,7 +10,9 @@ using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Overlays.Settings; using osu.Game.Tournament.Models; @@ -128,7 +130,7 @@ namespace osu.Game.Tournament.Screens.Editors Width = 0.2f, Current = Model.Seed }, - new SettingsSlider + new SettingsSlider { LabelText = "Last Year Placement", Width = 0.33f, @@ -175,6 +177,11 @@ namespace osu.Game.Tournament.Screens.Editors }; } + private partial class LastYearPlacementSlider : RoundedSliderBar + { + public override LocalisableString TooltipText => Current.Value == 0 ? "N/A" : base.TooltipText; + } + public partial class PlayerEditor : CompositeDrawable { private readonly TournamentTeam team; From 827d48adcc5fb3d334c5175879898b604871b4d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 21 Aug 2023 16:10:48 +0900 Subject: [PATCH 110/124] Fix test coverage not actually covering crash --- osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs b/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs index d52b453185..e0444b6126 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneSongBar.cs @@ -14,6 +14,7 @@ namespace osu.Game.Tournament.Tests.Components public partial class TestSceneSongBar : TournamentTestScene { private SongBar songBar = null!; + private TournamentBeatmap ladderBeatmap = null!; [SetUpSteps] public override void SetUpSteps() @@ -22,9 +23,10 @@ namespace osu.Game.Tournament.Tests.Components AddStep("setup picks bans", () => { + ladderBeatmap = CreateSampleBeatmap(); Ladder.CurrentMatch.Value!.PicksBans.Add(new BeatmapChoice { - BeatmapID = CreateSampleBeatmap().OnlineID, + BeatmapID = ladderBeatmap.OnlineID, Team = TeamColour.Red, Type = ChoiceType.Pick, }); @@ -52,7 +54,7 @@ namespace osu.Game.Tournament.Tests.Components beatmap.StarRating = 4.56f; beatmap.Length = 123456; beatmap.BPM = 133; - beatmap.OnlineID = CreateSampleBeatmap().OnlineID; + beatmap.OnlineID = ladderBeatmap.OnlineID; songBar.Beatmap = new TournamentBeatmap(beatmap); }); From e7d61e00022a4a09e8164eb809e2a1dc6b34dfa6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 21 Aug 2023 17:59:24 +0900 Subject: [PATCH 111/124] Fix star fountain directions not matching stable --- .../Visual/Menus/TestSceneStarFountain.cs | 2 +- osu.Game/Screens/Menu/KiaiMenuFountains.cs | 30 +++++++++++++++---- osu.Game/Screens/Menu/StarFountain.cs | 6 ++-- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneStarFountain.cs b/osu.Game.Tests/Visual/Menus/TestSceneStarFountain.cs index b12f3e7946..bb327e5962 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneStarFountain.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneStarFountain.cs @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.Menus foreach (var fountain in Children.OfType()) { if (RNG.NextSingle() > 0.8f) - fountain.Shoot(); + fountain.Shoot(RNG.Next(-1, 2)); } }, 150); } diff --git a/osu.Game/Screens/Menu/KiaiMenuFountains.cs b/osu.Game/Screens/Menu/KiaiMenuFountains.cs index a4d58e398a..07c06dcdb9 100644 --- a/osu.Game/Screens/Menu/KiaiMenuFountains.cs +++ b/osu.Game/Screens/Menu/KiaiMenuFountains.cs @@ -2,10 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Graphics; +using osu.Framework.Utils; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.Containers; @@ -13,6 +13,9 @@ namespace osu.Game.Screens.Menu { public partial class KiaiMenuFountains : BeatSyncedContainer { + private StarFountain leftFountain = null!; + private StarFountain rightFountain = null!; + [BackgroundDependencyLoader] private void load() { @@ -20,13 +23,13 @@ namespace osu.Game.Screens.Menu Children = new[] { - new StarFountain + leftFountain = new StarFountain { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, X = 250, }, - new StarFountain + rightFountain = new StarFountain { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, @@ -58,8 +61,25 @@ namespace osu.Game.Screens.Menu if (lastTrigger != null && Clock.CurrentTime - lastTrigger < 500) return; - foreach (var fountain in Children.OfType()) - fountain.Shoot(); + int direction = RNG.Next(-1, 2); + + switch (direction) + { + case -1: + leftFountain.Shoot(1); + rightFountain.Shoot(-1); + break; + + case 0: + leftFountain.Shoot(0); + rightFountain.Shoot(0); + break; + + case 1: + leftFountain.Shoot(-1); + rightFountain.Shoot(1); + break; + } lastTrigger = Clock.CurrentTime; } diff --git a/osu.Game/Screens/Menu/StarFountain.cs b/osu.Game/Screens/Menu/StarFountain.cs index 0d35f6e0e0..fd59ec3573 100644 --- a/osu.Game/Screens/Menu/StarFountain.cs +++ b/osu.Game/Screens/Menu/StarFountain.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Menu InternalChild = spewer = new StarFountainSpewer(); } - public void Shoot() => spewer.Shoot(); + public void Shoot(int direction) => spewer.Shoot(direction); protected override void SkinChanged(ISkinSource skin) { @@ -81,10 +81,10 @@ namespace osu.Game.Screens.Menu return lastShootDirection * x_velocity_from_direction * (float)(1 - 2 * (Clock.CurrentTime - lastShootTime!.Value) / shoot_duration) + getRandomVariance(x_velocity_random_variance); } - public void Shoot() + public void Shoot(int direction) { lastShootTime = Clock.CurrentTime; - lastShootDirection = RNG.Next(-1, 2); + lastShootDirection = direction; } private static float getRandomVariance(float variance) => RNG.NextSingle(-variance, variance); From 5f040a991b3f1492c97d247799a3245b2b825ee1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 21 Aug 2023 19:05:20 +0900 Subject: [PATCH 112/124] Fix potential crash when loading menu items due to cross-thread ops --- osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs b/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs index eb046932e6..2f2cb7e5f8 100644 --- a/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs @@ -40,8 +40,14 @@ namespace osu.Game.Graphics.UserInterface AddInternal(hoverClickSounds = new HoverClickSounds()); updateTextColour(); + } + + protected override void LoadComplete() + { + base.LoadComplete(); Item.Action.BindDisabledChanged(_ => updateState(), true); + FinishTransforms(); } private void updateTextColour() From 662073c47220d513ed3ef510d70ab0ccb850e334 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 21 Aug 2023 19:35:04 +0900 Subject: [PATCH 113/124] Fix some incorrect comments / test step descriptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs | 2 +- osu.Game/Scoring/ScoreInfo.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs b/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs index 23b88b7395..f766faec9a 100644 --- a/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs +++ b/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs @@ -132,7 +132,7 @@ namespace osu.Game.Tests.Database { ScoreInfo scoreInfo = null!; - AddStep("Add score which requires upgrade (but has no beatmap)", () => + AddStep("Add score which requires upgrade (and has beatmap)", () => { Realm.Write(r => { diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index c3a45332e4..526c4217ef 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -86,7 +86,7 @@ namespace osu.Game.Scoring /// Should be used to ensure we don't repeatedly attempt to update the same scores each startup even though we already know they will fail. /// /// - /// See https://github.com/ppy/osu/issues/24301 for one example of how this can occur(missing beatmap file on disk). + /// See https://github.com/ppy/osu/issues/24301 for one example of how this can occur (missing beatmap file on disk). /// public bool TotalScoreUpgradeFailed { get; set; } From b3e7416972f097242bf8c9fe407dec3567d489d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 21 Aug 2023 19:36:22 +0900 Subject: [PATCH 114/124] Rename new flag and update xmldoc to match --- .../Database/BackgroundDataStoreProcessorTests.cs | 4 ++-- osu.Game/BackgroundDataStoreProcessor.cs | 8 ++++---- osu.Game/Scoring/ScoreInfo.cs | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs b/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs index f766faec9a..da46392e4b 100644 --- a/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs +++ b/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs @@ -148,7 +148,7 @@ namespace osu.Game.Tests.Database AddStep("Run background processor", () => Add(new TestBackgroundDataStoreProcessor())); AddUntilStep("Score version upgraded", () => Realm.Run(r => r.Find(scoreInfo.ID)!.TotalScoreVersion), () => Is.EqualTo(LegacyScoreEncoder.LATEST_VERSION)); - AddAssert("Score not marked as failed", () => Realm.Run(r => r.Find(scoreInfo.ID)!.TotalScoreUpgradeFailed), () => Is.False); + AddAssert("Score not marked as failed", () => Realm.Run(r => r.Find(scoreInfo.ID)!.BackgroundReprocessingFailed), () => Is.False); } [Test] @@ -174,7 +174,7 @@ namespace osu.Game.Tests.Database AddStep("Run background processor", () => Add(new TestBackgroundDataStoreProcessor())); - AddUntilStep("Score marked as failed", () => Realm.Run(r => r.Find(scoreInfo.ID)!.TotalScoreUpgradeFailed), () => Is.True); + AddUntilStep("Score marked as failed", () => Realm.Run(r => r.Find(scoreInfo.ID)!.BackgroundReprocessingFailed), () => Is.True); AddAssert("Score version not upgraded", () => Realm.Run(r => r.Find(scoreInfo.ID)!.TotalScoreVersion), () => Is.EqualTo(30000002)); } diff --git a/osu.Game/BackgroundDataStoreProcessor.cs b/osu.Game/BackgroundDataStoreProcessor.cs index ae9e9527de..f29b100ee8 100644 --- a/osu.Game/BackgroundDataStoreProcessor.cs +++ b/osu.Game/BackgroundDataStoreProcessor.cs @@ -186,7 +186,7 @@ namespace osu.Game realmAccess.Run(r => { - foreach (var score in r.All().Where(s => !s.TotalScoreUpgradeFailed)) + foreach (var score in r.All().Where(s => !s.BackgroundReprocessingFailed)) { if (score.BeatmapInfo != null && score.Statistics.Sum(kvp => kvp.Value) > 0 @@ -225,7 +225,7 @@ namespace osu.Game catch (Exception e) { Logger.Log(@$"Failed to populate maximum statistics for {id}: {e}"); - realmAccess.Write(r => r.Find(id)!.TotalScoreUpgradeFailed = true); + realmAccess.Write(r => r.Find(id)!.BackgroundReprocessingFailed = true); } } } @@ -235,7 +235,7 @@ namespace osu.Game Logger.Log("Querying for scores that need total score conversion..."); HashSet scoreIds = realmAccess.Run(r => new HashSet(r.All() - .Where(s => !s.TotalScoreUpgradeFailed && s.BeatmapInfo != null && s.TotalScoreVersion == 30000002) + .Where(s => !s.BackgroundReprocessingFailed && s.BeatmapInfo != null && s.TotalScoreVersion == 30000002) .AsEnumerable().Select(s => s.ID))); Logger.Log($"Found {scoreIds.Count} scores which require total score conversion."); @@ -284,7 +284,7 @@ namespace osu.Game catch (Exception e) { Logger.Log($"Failed to convert total score for {id}: {e}"); - realmAccess.Write(r => r.Find(id)!.TotalScoreUpgradeFailed = true); + realmAccess.Write(r => r.Find(id)!.BackgroundReprocessingFailed = true); ++failedCount; } } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 526c4217ef..2efea2105c 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -82,13 +82,13 @@ namespace osu.Game.Scoring public long? LegacyTotalScore { get; set; } /// - /// If an reprocess of total score failed to update this score to the latest version, this flag will become true. - /// Should be used to ensure we don't repeatedly attempt to update the same scores each startup even though we already know they will fail. + /// If background processing of this beatmap failed in some way, this flag will become true. + /// Should be used to ensure we don't repeatedly attempt to reprocess the same scores each startup even though we already know they will fail. /// /// /// See https://github.com/ppy/osu/issues/24301 for one example of how this can occur (missing beatmap file on disk). /// - public bool TotalScoreUpgradeFailed { get; set; } + public bool BackgroundReprocessingFailed { get; set; } public int MaxCombo { get; set; } From bdac05263164d51af00fa23d4fe84f1e1fcf905c Mon Sep 17 00:00:00 2001 From: tsrk Date: Mon, 21 Aug 2023 15:29:41 +0200 Subject: [PATCH 115/124] refactor(MessageNotifier): apply changes required by framework --- osu.Game/Online/Chat/MessageNotifier.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Chat/MessageNotifier.cs b/osu.Game/Online/Chat/MessageNotifier.cs index b91cf06847..de38d3ef26 100644 --- a/osu.Game/Online/Chat/MessageNotifier.cs +++ b/osu.Game/Online/Chat/MessageNotifier.cs @@ -127,7 +127,7 @@ namespace osu.Game.Online.Chat if (!notifyOnPrivateMessage.Value || channel.Type != ChannelType.PM) return false; - (host as DesktopGameHost)?.FlashWindow(); + host.Window?.Flash(); notifications.Post(new PrivateMessageNotification(message, channel)); return true; @@ -137,7 +137,7 @@ namespace osu.Game.Online.Chat { if (!notifyOnUsername.Value || !CheckContainsUsername(message.Content, localUser.Value.Username)) return; - (host as DesktopGameHost)?.FlashWindow(); + host.Window?.Flash(); notifications.Post(new MentionNotification(message, channel)); } From 8533cba0bf6a02b05b8e9bfe99aaee2822dfe5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 21 Aug 2023 17:27:05 +0200 Subject: [PATCH 116/124] Fix mismatching schema version in comment --- 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 36781b0454..db4f0d9864 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -83,7 +83,7 @@ namespace osu.Game.Database /// 31 2023-06-26 Add Version and LegacyTotalScore to ScoreInfo, set Version to 30000002 and copy TotalScore into LegacyTotalScore for legacy scores. /// 32 2023-07-09 Populate legacy scores with the ScoreV2 mod (and restore TotalScore to the legacy total for such scores) using replay files. /// 33 2023-08-16 Reset default chat toggle key binding to avoid conflict with newly added leaderboard toggle key binding. - /// 35 2023-08-21 Add TotalScoreUpgradeFailed flag to ScoreInfo to track upgrade failures. + /// 34 2023-08-21 Add TotalScoreUpgradeFailed flag to ScoreInfo to track upgrade failures. /// private const int schema_version = 34; From 273dcf9150f441c475c524455b12376a96d3930f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 21 Aug 2023 17:44:35 +0200 Subject: [PATCH 117/124] Also update the reference to added flag in schema change breakdown --- 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 db4f0d9864..cd97bb6430 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -83,7 +83,7 @@ namespace osu.Game.Database /// 31 2023-06-26 Add Version and LegacyTotalScore to ScoreInfo, set Version to 30000002 and copy TotalScore into LegacyTotalScore for legacy scores. /// 32 2023-07-09 Populate legacy scores with the ScoreV2 mod (and restore TotalScore to the legacy total for such scores) using replay files. /// 33 2023-08-16 Reset default chat toggle key binding to avoid conflict with newly added leaderboard toggle key binding. - /// 34 2023-08-21 Add TotalScoreUpgradeFailed flag to ScoreInfo to track upgrade failures. + /// 34 2023-08-21 Add BackgroundReprocessingFailed flag to ScoreInfo to track upgrade failures. /// private const int schema_version = 34; From 5454d1caa1927428fe339e3d5b47ddfe45f9dfb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 6 Aug 2023 17:40:55 +0200 Subject: [PATCH 118/124] Remove global action container input queue workaround As described in #24248, the workaround employed by `GlobalActionContainer`, wherein it tried to handle actions with priority before its children by being placed in front of the children and not _actually containing_ said children, is blocking the resolution of some rather major input handling issues that allow key releases to be received by deparented drawables. To resolve, migrate `GlobalActionContainer` to use `Prioritised`, which can be done without regressing certain mouse button flows after ppy/osu-framework#5966. --- .../Input/Bindings/GlobalActionContainer.cs | 34 +++++-------------- osu.Game/OsuGameBase.cs | 19 ++++++----- .../Visual/OsuManualInputManagerTestScene.cs | 8 ++++- 3 files changed, 25 insertions(+), 36 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 9a0a2d5c15..296232d9ea 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -3,33 +3,26 @@ using System.Collections.Generic; using System.Linq; -using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Localisation; namespace osu.Game.Input.Bindings { - public partial class GlobalActionContainer : DatabasedKeyBindingContainer, IHandleGlobalKeyboardInput + public partial class GlobalActionContainer : DatabasedKeyBindingContainer, IHandleGlobalKeyboardInput, IKeyBindingHandler { - private readonly Drawable? handler; - - private InputManager? parentInputManager; + private readonly IKeyBindingHandler? handler; public GlobalActionContainer(OsuGameBase? game) : base(matchingMode: KeyCombinationMatchingMode.Modifiers) { - if (game is IKeyBindingHandler) - handler = game; + if (game is IKeyBindingHandler h) + handler = h; } - protected override void LoadComplete() - { - base.LoadComplete(); - - parentInputManager = GetContainingInputManager(); - } + protected override bool Prioritised => true; // IMPORTANT: Take care when changing order of the items in the enumerable. // It is used to decide the order of precedence, with the earlier items having higher precedence. @@ -161,20 +154,9 @@ namespace osu.Game.Input.Bindings new KeyBinding(InputKey.F3, GlobalAction.MusicPlay) }; - protected override IEnumerable KeyBindingInputQueue - { - get - { - // To ensure the global actions are handled with priority, this GlobalActionContainer is actually placed after game content. - // It does not contain children as expected, so we need to forward the NonPositionalInputQueue from the parent input manager to correctly - // allow the whole game to handle these actions. + public bool OnPressed(KeyBindingPressEvent e) => handler?.OnPressed(e) == true; - // An eventual solution to this hack is to create localised action containers for individual components like SongSelect, but this will take some rearranging. - var inputQueue = parentInputManager?.NonPositionalInputQueue ?? base.KeyBindingInputQueue; - - return handler != null ? inputQueue.Prepend(handler) : inputQueue; - } - } + public void OnReleased(KeyBindingReleaseEvent e) => handler?.OnReleased(e); } public enum GlobalAction diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 6737caa5f9..75b46a0a4d 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -392,17 +392,18 @@ namespace osu.Game { SafeAreaOverrideEdges = SafeAreaOverrideEdges, RelativeSizeAxes = Axes.Both, - Child = CreateScalingContainer().WithChildren(new Drawable[] + Child = CreateScalingContainer().WithChild(globalBindings = new GlobalActionContainer(this) { - (GlobalCursorDisplay = new GlobalCursorDisplay + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both - }).WithChild(content = new OsuTooltipContainer(GlobalCursorDisplay.MenuCursor) - { - RelativeSizeAxes = Axes.Both - }), - // to avoid positional input being blocked by children, ensure the GlobalActionContainer is above everything. - globalBindings = new GlobalActionContainer(this) + (GlobalCursorDisplay = new GlobalCursorDisplay + { + RelativeSizeAxes = Axes.Both + }).WithChild(content = new OsuTooltipContainer(GlobalCursorDisplay.MenuCursor) + { + RelativeSizeAxes = Axes.Both + }), + } }) }); diff --git a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs index 37260b3b13..ffe40243ab 100644 --- a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs +++ b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs @@ -57,7 +57,13 @@ namespace osu.Game.Tests.Visual } if (CreateNestedActionContainer) - mainContent.Add(new GlobalActionContainer(null)); + { + var globalActionContainer = new GlobalActionContainer(null) + { + Child = mainContent + }; + mainContent = globalActionContainer; + } base.Content.AddRange(new Drawable[] { From 96c58c86ea85c1e2e85cb578a730be649993351d Mon Sep 17 00:00:00 2001 From: tsrk Date: Mon, 21 Aug 2023 23:36:54 +0200 Subject: [PATCH 119/124] refactor: make flashing available in `Notifications` This will be used in `NotificationOverlay` when a `Notification` is posted. --- osu.Game/Online/Chat/MessageNotifier.cs | 4 ---- osu.Game/Overlays/NotificationOverlay.cs | 9 +++++++++ osu.Game/Overlays/Notifications/Notification.cs | 5 +++++ osu.Game/Screens/Play/PlayerLoader.cs | 2 ++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/Chat/MessageNotifier.cs b/osu.Game/Online/Chat/MessageNotifier.cs index de38d3ef26..65aac723da 100644 --- a/osu.Game/Online/Chat/MessageNotifier.cs +++ b/osu.Game/Online/Chat/MessageNotifier.cs @@ -127,8 +127,6 @@ namespace osu.Game.Online.Chat if (!notifyOnPrivateMessage.Value || channel.Type != ChannelType.PM) return false; - host.Window?.Flash(); - notifications.Post(new PrivateMessageNotification(message, channel)); return true; } @@ -137,8 +135,6 @@ namespace osu.Game.Online.Chat { if (!notifyOnUsername.Value || !CheckContainsUsername(message.Content, localUser.Value.Username)) return; - host.Window?.Flash(); - notifications.Post(new MentionNotification(message, channel)); } diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index c9d09848f8..08c567af82 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -43,6 +43,9 @@ namespace osu.Game.Overlays [Resolved] private AudioManager audio { get; set; } = null!; + [Resolved] + private OsuGame game { get; set; } = null!; + [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); @@ -176,6 +179,12 @@ namespace osu.Game.Overlays playDebouncedSample(notification.PopInSampleName); + if (notification.FlashTaskbar) + { + game.Window?.Flash(notification.IsImportant); + notification.Closed += () => game.Window?.CancelFlash(); + } + if (State.Value == Visibility.Hidden) { notification.IsInToastTray = true; diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 8cdc373417..53fc152c96 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -38,6 +38,11 @@ namespace osu.Game.Overlays.Notifications /// public virtual bool IsImportant => true; + /// + /// Whether this notification should trigger a taskbar flash if the window is un-focused when posted. + /// + public bool FlashTaskbar { get; init; } = true; + /// /// Run on user activating the notification. Return true to close. /// diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 872425e3fd..eccfc4dc7b 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -568,6 +568,7 @@ namespace osu.Game.Screens.Play public MutedNotification() { Text = NotificationsStrings.GameVolumeTooLow; + FlashTaskbar = false; } [BackgroundDependencyLoader] @@ -623,6 +624,7 @@ namespace osu.Game.Screens.Play public BatteryWarningNotification() { Text = NotificationsStrings.BatteryLow; + FlashTaskbar = false; } [BackgroundDependencyLoader] From e8337c592a69e1905e75d459bf5ff6aad6c0d47f Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 22 Aug 2023 12:50:13 +0900 Subject: [PATCH 120/124] Update framework and apply changes to support masking SSBO --- .../UI/Cursor/CursorTrail.cs | 20 ++++++++++++++----- .../Resources/Shaders/sh_TestVertex.vs | 2 +- osu.Game/Screens/Loader.cs | 5 +---- osu.Game/osu.Game.csproj | 2 +- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index a29faac5a0..0774d34488 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -286,7 +286,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor if (time - part.Time >= 1) continue; - vertexBatch.Add(new TexturedTrailVertex + vertexBatch.Add(new TexturedTrailVertex(renderer) { Position = new Vector2(part.Position.X - size.X * originPosition.X, part.Position.Y + size.Y * (1 - originPosition.Y)), TexturePosition = textureRect.BottomLeft, @@ -295,7 +295,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Time = part.Time }); - vertexBatch.Add(new TexturedTrailVertex + vertexBatch.Add(new TexturedTrailVertex(renderer) { Position = new Vector2(part.Position.X + size.X * (1 - originPosition.X), part.Position.Y + size.Y * (1 - originPosition.Y)), TexturePosition = textureRect.BottomRight, @@ -304,7 +304,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Time = part.Time }); - vertexBatch.Add(new TexturedTrailVertex + vertexBatch.Add(new TexturedTrailVertex(renderer) { Position = new Vector2(part.Position.X + size.X * (1 - originPosition.X), part.Position.Y - size.Y * originPosition.Y), TexturePosition = textureRect.TopRight, @@ -313,7 +313,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Time = part.Time }); - vertexBatch.Add(new TexturedTrailVertex + vertexBatch.Add(new TexturedTrailVertex(renderer) { Position = new Vector2(part.Position.X - size.X * originPosition.X, part.Position.Y - size.Y * originPosition.Y), TexturePosition = textureRect.TopLeft, @@ -362,12 +362,22 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor [VertexMember(1, VertexAttribPointerType.Float)] public float Time; + [VertexMember(1, VertexAttribPointerType.Int)] + private readonly int maskingIndex; + + public TexturedTrailVertex(IRenderer renderer) + { + this = default; + maskingIndex = renderer.CurrentMaskingIndex; + } + public bool Equals(TexturedTrailVertex other) { return Position.Equals(other.Position) && TexturePosition.Equals(other.TexturePosition) && Colour.Equals(other.Colour) - && Time.Equals(other.Time); + && Time.Equals(other.Time) + && maskingIndex == other.maskingIndex; } } } diff --git a/osu.Game.Tests/Resources/Shaders/sh_TestVertex.vs b/osu.Game.Tests/Resources/Shaders/sh_TestVertex.vs index 505554bb33..80ed686ba5 100644 --- a/osu.Game.Tests/Resources/Shaders/sh_TestVertex.vs +++ b/osu.Game.Tests/Resources/Shaders/sh_TestVertex.vs @@ -13,7 +13,7 @@ layout(location = 4) out mediump vec2 v_BlendRange; void main(void) { // Transform from screen space to masking space. - highp vec3 maskingPos = g_ToMaskingSpace * vec3(m_Position, 1.0); + highp vec3 maskingPos = g_MaskingInfo.ToMaskingSpace * vec3(m_Position, 1.0); v_MaskingPosition = maskingPos.xy / maskingPos.z; v_Colour = m_Colour; diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index 372cfe748e..962c7d9d14 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -126,12 +126,9 @@ namespace osu.Game.Screens private void load(ShaderManager manager) { loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE)); - loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.BLUR)); - + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2_NO_MASKING, FragmentShaderDescriptor.BLUR)); loadTargets.Add(manager.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE)); - loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, "TriangleBorder")); - loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE)); } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7d4a721c91..08107c2fad 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + From f09b81841810df3e63a9bc6bfa11a369803f0d89 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Aug 2023 13:17:12 +0900 Subject: [PATCH 121/124] 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 08107c2fad..4c3205178a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From be1a712f33bfa755a049d9b2763a22f45f8a1498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 22 Aug 2023 08:54:41 +0200 Subject: [PATCH 122/124] Make `OsuGame` dependency nullable --- osu.Game/Overlays/NotificationOverlay.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 08c567af82..6dd344ca99 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -44,7 +44,7 @@ namespace osu.Game.Overlays private AudioManager audio { get; set; } = null!; [Resolved] - private OsuGame game { get; set; } = null!; + private OsuGame? game { get; set; } [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); @@ -181,8 +181,8 @@ namespace osu.Game.Overlays if (notification.FlashTaskbar) { - game.Window?.Flash(notification.IsImportant); - notification.Closed += () => game.Window?.CancelFlash(); + game?.Window?.Flash(notification.IsImportant); + notification.Closed += () => game?.Window?.CancelFlash(); } if (State.Value == Visibility.Hidden) From aa29e00578a01768d1ee7bf537e5b2a335c46b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 22 Aug 2023 08:58:10 +0200 Subject: [PATCH 123/124] Remove `FlashTaskbar` and use `IsImportant` directly instead --- osu.Game/Overlays/NotificationOverlay.cs | 4 ++-- osu.Game/Overlays/Notifications/Notification.cs | 5 ----- osu.Game/Screens/Play/PlayerLoader.cs | 2 -- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 6dd344ca99..b93d5f1e12 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -179,9 +179,9 @@ namespace osu.Game.Overlays playDebouncedSample(notification.PopInSampleName); - if (notification.FlashTaskbar) + if (notification.IsImportant) { - game?.Window?.Flash(notification.IsImportant); + game?.Window?.Flash(); notification.Closed += () => game?.Window?.CancelFlash(); } diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 53fc152c96..8cdc373417 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -38,11 +38,6 @@ namespace osu.Game.Overlays.Notifications /// public virtual bool IsImportant => true; - /// - /// Whether this notification should trigger a taskbar flash if the window is un-focused when posted. - /// - public bool FlashTaskbar { get; init; } = true; - /// /// Run on user activating the notification. Return true to close. /// diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index eccfc4dc7b..872425e3fd 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -568,7 +568,6 @@ namespace osu.Game.Screens.Play public MutedNotification() { Text = NotificationsStrings.GameVolumeTooLow; - FlashTaskbar = false; } [BackgroundDependencyLoader] @@ -624,7 +623,6 @@ namespace osu.Game.Screens.Play public BatteryWarningNotification() { Text = NotificationsStrings.BatteryLow; - FlashTaskbar = false; } [BackgroundDependencyLoader] From 142abe1fd0a87a2144ee4f61c40cff1c96ce4605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 22 Aug 2023 09:00:59 +0200 Subject: [PATCH 124/124] Make highlight messages important in order to trigger window flash --- osu.Game/Online/Chat/MessageNotifier.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Online/Chat/MessageNotifier.cs b/osu.Game/Online/Chat/MessageNotifier.cs index 65aac723da..56f490cb21 100644 --- a/osu.Game/Online/Chat/MessageNotifier.cs +++ b/osu.Game/Online/Chat/MessageNotifier.cs @@ -182,8 +182,6 @@ namespace osu.Game.Online.Chat private readonly Message message; private readonly Channel channel; - public override bool IsImportant => false; - [BackgroundDependencyLoader] private void load(OsuColour colours, ChatOverlay chatOverlay, INotificationOverlay notificationOverlay) {