From f53ce5aedfd17d18eaa9a882e14707636a806a92 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 23 Jan 2022 11:11:12 +0800 Subject: [PATCH 01/80] Fix max combo calculation in osu diffcalc --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index c5b1baaad1..c80b19e1d3 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -63,8 +63,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty double drainRate = beatmap.Difficulty.DrainRate; int maxCombo = beatmap.HitObjects.Count; - // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) - maxCombo += beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1); + // Add the ticks + tail of the slider + // 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) + // an additional 1 is subtracted if only nested objects are judged because the hit result of the entire slider would not contribute to combo + maxCombo += beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1 - (s.OnlyJudgeNestedObjects ? 1 : 0)); int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle); int sliderCount = beatmap.HitObjects.Count(h => h is Slider); From 44311c1f4e3e18548f9b574de968468d52f8c282 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 23 Jan 2022 11:25:22 +0800 Subject: [PATCH 02/80] Add tests for diffcalc max combo --- .../CatchDifficultyCalculatorTest.cs | 12 +++++------ .../ManiaDifficultyCalculatorTest.cs | 12 +++++------ .../OsuDifficultyCalculatorTest.cs | 21 ++++++++++++------- .../TaikoDifficultyCalculatorTest.cs | 16 +++++++------- .../Beatmaps/DifficultyCalculatorTest.cs | 7 +++++-- 5 files changed, 38 insertions(+), 30 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs index 7e8d567fbe..48d46636df 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs @@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Catch.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Catch"; - [TestCase(4.0505463516206195d, "diffcalc-test")] - public void Test(double expected, string name) - => base.Test(expected, name); + [TestCase(4.0505463516206195d, 127, "diffcalc-test")] + public void Test(double expectedStarRating, int expectedMaxCombo, string name) + => base.Test(expectedStarRating, expectedMaxCombo, name); - [TestCase(5.1696411260785498d, "diffcalc-test")] - public void TestClockRateAdjusted(double expected, string name) - => Test(expected, name, new CatchModDoubleTime()); + [TestCase(5.1696411260785498d, 127, "diffcalc-test")] + public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name) + => Test(expectedStarRating, expectedMaxCombo, name, new CatchModDoubleTime()); protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new CatchDifficultyCalculator(new CatchRuleset().RulesetInfo, beatmap); diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs index 6ec49d7634..715614a201 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs @@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Mania.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; - [TestCase(2.3449735700206298d, "diffcalc-test")] - public void Test(double expected, string name) - => base.Test(expected, name); + [TestCase(2.3449735700206298d, 151, "diffcalc-test")] + public void Test(double expectedStarRating, int expectedMaxCombo, string name) + => base.Test(expectedStarRating, expectedMaxCombo, name); - [TestCase(2.7879104989252959d, "diffcalc-test")] - public void TestClockRateAdjusted(double expected, string name) - => Test(expected, name, new ManiaModDoubleTime()); + [TestCase(2.7879104989252959d, 151, "diffcalc-test")] + public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name) + => Test(expectedStarRating, expectedMaxCombo, name, new ManiaModDoubleTime()); protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new ManiaDifficultyCalculator(new ManiaRuleset().RulesetInfo, beatmap); diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index b7984e6995..df577ea8d3 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -15,15 +15,20 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - [TestCase(6.6972307565739273d, "diffcalc-test")] - [TestCase(1.4484754139145539d, "zero-length-sliders")] - public void Test(double expected, string name) - => base.Test(expected, name); + [TestCase(6.6972307565739273d, 206, "diffcalc-test")] + [TestCase(1.4484754139145539d, 45, "zero-length-sliders")] + public void Test(double expectedStarRating, int expectedMaxCombo, string name) + => base.Test(expectedStarRating, expectedMaxCombo, name); - [TestCase(8.9382559208689809d, "diffcalc-test")] - [TestCase(1.7548875851757628d, "zero-length-sliders")] - public void TestClockRateAdjusted(double expected, string name) - => Test(expected, name, new OsuModDoubleTime()); + [TestCase(8.9382559208689809d, 206, "diffcalc-test")] + [TestCase(1.7548875851757628d, 45, "zero-length-sliders")] + public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name) + => Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime()); + + [TestCase(6.6972307218715166d, 239, "diffcalc-test")] + [TestCase(1.4484754139145537d, 54, "zero-length-sliders")] + public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name) + => Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic()); protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset().RulesetInfo, beatmap); diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs index 2b1cbc580e..226da7df09 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs @@ -14,15 +14,15 @@ namespace osu.Game.Rulesets.Taiko.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko"; - [TestCase(2.2420075288523802d, "diffcalc-test")] - [TestCase(2.2420075288523802d, "diffcalc-test-strong")] - public void Test(double expected, string name) - => base.Test(expected, name); + [TestCase(2.2420075288523802d, 200, "diffcalc-test")] + [TestCase(2.2420075288523802d, 200, "diffcalc-test-strong")] + public void Test(double expectedStarRating, int expectedMaxCombo, string name) + => base.Test(expectedStarRating, expectedMaxCombo, name); - [TestCase(3.134084469440479d, "diffcalc-test")] - [TestCase(3.134084469440479d, "diffcalc-test-strong")] - public void TestClockRateAdjusted(double expected, string name) - => Test(expected, name, new TaikoModDoubleTime()); + [TestCase(3.134084469440479d, 200, "diffcalc-test")] + [TestCase(3.134084469440479d, 200, "diffcalc-test-strong")] + public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name) + => Test(expectedStarRating, expectedMaxCombo, name, new TaikoModDoubleTime()); protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset().RulesetInfo, beatmap); diff --git a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs index 9f8811c7f9..ed00c7959b 100644 --- a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs +++ b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs @@ -22,10 +22,13 @@ namespace osu.Game.Tests.Beatmaps protected abstract string ResourceAssembly { get; } - protected void Test(double expected, string name, params Mod[] mods) + protected void Test(double expectedStarRating, int expectedMaxCombo, string name, params Mod[] mods) { + var attributes = CreateDifficultyCalculator(getBeatmap(name)).Calculate(mods); + // Platform-dependent math functions (Pow, Cbrt, Exp, etc) may result in minute differences. - Assert.That(CreateDifficultyCalculator(getBeatmap(name)).Calculate(mods).StarRating, Is.EqualTo(expected).Within(0.00001)); + Assert.That(attributes.StarRating, Is.EqualTo(expectedStarRating).Within(0.00001)); + Assert.That(attributes.MaxCombo, Is.EqualTo(expectedMaxCombo)); } private IWorkingBeatmap getBeatmap(string name) From 74a55ead7711108c4d6b856e11433b476459c35a Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 23 Jan 2022 13:00:54 +0800 Subject: [PATCH 03/80] Simplify combo counting logic --- .../Difficulty/OsuDifficultyCalculator.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index c80b19e1d3..d04d0872d8 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -9,6 +9,7 @@ using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Difficulty.Skills; using osu.Game.Rulesets.Osu.Mods; @@ -62,11 +63,20 @@ namespace osu.Game.Rulesets.Osu.Difficulty double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; double drainRate = beatmap.Difficulty.DrainRate; - int maxCombo = beatmap.HitObjects.Count; - // Add the ticks + tail of the slider - // 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) - // an additional 1 is subtracted if only nested objects are judged because the hit result of the entire slider would not contribute to combo - maxCombo += beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1 - (s.OnlyJudgeNestedObjects ? 1 : 0)); + int maxCombo = 0; + + void countCombo(HitObject ho) + { + if (ho.CreateJudgement().MaxResult.AffectsCombo()) + maxCombo++; + } + + foreach (HitObject ho in beatmap.HitObjects) + { + countCombo(ho); + foreach (HitObject nested in ho.NestedHitObjects) + countCombo(nested); + } int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle); int sliderCount = beatmap.HitObjects.Count(h => h is Slider); From 215da7e933ded0423618e6fb6f58e28c65ea2339 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 16 Feb 2022 12:05:55 +0900 Subject: [PATCH 04/80] Reimplement as extension method on IBeatmap Implementation has changed slightly to support arbitrary levels of nested hitobjects. --- .../Difficulty/OsuDifficultyCalculator.cs | 17 +------------ osu.Game/Beatmaps/IBeatmap.cs | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index d04d0872d8..df6fd19d36 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -9,7 +9,6 @@ using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Difficulty.Skills; using osu.Game.Rulesets.Osu.Mods; @@ -62,21 +61,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; double drainRate = beatmap.Difficulty.DrainRate; - - int maxCombo = 0; - - void countCombo(HitObject ho) - { - if (ho.CreateJudgement().MaxResult.AffectsCombo()) - maxCombo++; - } - - foreach (HitObject ho in beatmap.HitObjects) - { - countCombo(ho); - foreach (HitObject nested in ho.NestedHitObjects) - countCombo(nested); - } + int maxCombo = beatmap.GetMaxCombo(); int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle); int sliderCount = beatmap.HitObjects.Count(h => h is Slider); diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs index 3f598cd1e5..dec1ef4294 100644 --- a/osu.Game/Beatmaps/IBeatmap.cs +++ b/osu.Game/Beatmaps/IBeatmap.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Beatmaps { @@ -70,4 +71,27 @@ namespace osu.Game.Beatmaps /// new IReadOnlyList HitObjects { get; } } + + public static class BeatmapExtensions + { + /// + /// Finds the maximum achievable combo by hitting all s in a beatmap. + /// + public static int GetMaxCombo(this IBeatmap beatmap) + { + int combo = 0; + foreach (var h in beatmap.HitObjects) + addCombo(h, ref combo); + return combo; + + static void addCombo(HitObject hitObject, ref int combo) + { + if (hitObject.CreateJudgement().MaxResult.AffectsCombo()) + combo++; + + foreach (var nested in hitObject.NestedHitObjects) + addCombo(nested, ref combo); + } + } + } } From 3945cd24ebb84579fe1492c083d820155581f652 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 17 Feb 2022 21:14:49 +0900 Subject: [PATCH 05/80] wip --- .../Rulesets/Difficulty/DifficultyCalculator.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 6b61dd3efb..7d6c235fc1 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -15,6 +15,7 @@ using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; +using osu.Game.Utils; namespace osu.Game.Rulesets.Difficulty { @@ -122,12 +123,17 @@ namespace osu.Game.Rulesets.Difficulty /// A collection of structures describing the difficulty of the beatmap for each mod combination. public IEnumerable CalculateAll(CancellationToken cancellationToken = default) { + var rulesetInstance = ruleset.CreateInstance(); + foreach (var combination in CreateDifficultyAdjustmentModCombinations()) { - if (combination is MultiMod multi) - yield return Calculate(multi.Mods, cancellationToken); - else - yield return Calculate(combination.Yield(), cancellationToken); + Mod classicMod = rulesetInstance.CreateAllMods().SingleOrDefault(m => m is ModClassic); + + var finalCombination = ModUtils.FlattenMod(combination); + if (classicMod != null) + finalCombination = finalCombination.Append(classicMod); + + yield return Calculate(finalCombination.ToArray(), cancellationToken); } } From bedd07d2e4dca93e160949614f9f1a75f03a2fe2 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 22 Feb 2022 18:12:55 +0900 Subject: [PATCH 06/80] Add remark about usage of CalculateAll() --- osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 7d6c235fc1..0935f26de6 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -120,6 +120,9 @@ namespace osu.Game.Rulesets.Difficulty /// /// Calculates the difficulty of the beatmap using all mod combinations applicable to the beatmap. /// + /// + /// This should only be used to compute difficulties for legacy mod combinations. + /// /// A collection of structures describing the difficulty of the beatmap for each mod combination. public IEnumerable CalculateAll(CancellationToken cancellationToken = default) { From de1fbda648a1ce13522b5f0a8faa602161df5e58 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 27 Mar 2022 20:54:56 +0300 Subject: [PATCH 07/80] Clarify that searching includes both issues and Q&A discussions --- .github/ISSUE_TEMPLATE/bug-issue.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug-issue.yml b/.github/ISSUE_TEMPLATE/bug-issue.yml index 5b19c3732c..d77b28316a 100644 --- a/.github/ISSUE_TEMPLATE/bug-issue.yml +++ b/.github/ISSUE_TEMPLATE/bug-issue.yml @@ -9,7 +9,7 @@ body: Important to note that your issue may have already been reported before. Please check: - Pinned issues, at the top of https://github.com/ppy/osu/issues. - Current open `priority:0` issues, filterable [here](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Apriority%3A0). - - And most importantly, search for your issue. If you find that it already exists, respond with a reaction or add any further information that may be helpful. + - And most importantly, search across the [issue listing](https://github.com/ppy/osu/issues) and [Q&A discussion listing](https://github.com/ppy/osu/discussions/categories/q-a) for your issue. If you find that it already exists, respond with a reaction or add any further information that may be helpful. - type: dropdown attributes: From f847f9a31592d98e5070eaad37ef8e48cf313ae9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 27 Mar 2022 20:57:00 +0300 Subject: [PATCH 08/80] Exclude "open osu! folder" logs procedure from mobile platforms --- .github/ISSUE_TEMPLATE/bug-issue.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-issue.yml b/.github/ISSUE_TEMPLATE/bug-issue.yml index d77b28316a..8f54b5d1c8 100644 --- a/.github/ISSUE_TEMPLATE/bug-issue.yml +++ b/.github/ISSUE_TEMPLATE/bug-issue.yml @@ -48,19 +48,27 @@ body: Attaching log files is required for every reported bug. See instructions below on how to find them. + **Logs are reset when you reopen the game.** If the game crashed or has been closed since you found the bug, retrieve the logs using the file explorer instead. + + ### Desktop platforms + If the game has not yet been closed since you found the bug: 1. Head on to game settings and click on "Open osu! folder" 2. Then open the `logs` folder located there - **Logs are reset when you reopen the game.** If the game crashed or has been closed since you found the bug, retrieve the logs using the file explorer instead. - - The default places to find the logs are as follows: + The default places to find the logs on desktop platforms are as follows: - `%AppData%/osu/logs` *on Windows* - `~/.local/share/osu/logs` *on Linux & macOS* + + If you have selected a custom location for the game files, you can find the `logs` folder there. + + ### Mobile platforms + + The places to find the logs on mobile platforms are as follows: - `Android/data/sh.ppy.osulazer/files/logs` *on Android* - *On iOS*, they can be obtained by connecting your device to your desktop and copying the `logs` directory from the app's own document storage using iTunes. (https://support.apple.com/en-us/HT201301#copy-to-computer) - If you have selected a custom location for the game files, you can find the `logs` folder there. + --- After locating the `logs` folder, select all log files inside and drag them into the "Logs" box below. From ebf520921520d486fbf99cd8c07b0e611b81fc0b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 27 Mar 2022 21:38:55 +0300 Subject: [PATCH 09/80] Reword issue searching note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- .github/ISSUE_TEMPLATE/bug-issue.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug-issue.yml b/.github/ISSUE_TEMPLATE/bug-issue.yml index 8f54b5d1c8..ea5ee298fb 100644 --- a/.github/ISSUE_TEMPLATE/bug-issue.yml +++ b/.github/ISSUE_TEMPLATE/bug-issue.yml @@ -9,7 +9,7 @@ body: Important to note that your issue may have already been reported before. Please check: - Pinned issues, at the top of https://github.com/ppy/osu/issues. - Current open `priority:0` issues, filterable [here](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Apriority%3A0). - - And most importantly, search across the [issue listing](https://github.com/ppy/osu/issues) and [Q&A discussion listing](https://github.com/ppy/osu/discussions/categories/q-a) for your issue. If you find that it already exists, respond with a reaction or add any further information that may be helpful. + - And most importantly, search for your issue both in the [issue listing](https://github.com/ppy/osu/issues) and the [Q&A discussion listing](https://github.com/ppy/osu/discussions/categories/q-a). If you find that it already exists, respond with a reaction or add any further information that may be helpful. - type: dropdown attributes: From f049d7cb677e899f9984611ae19410b01a18c978 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Tue, 29 Mar 2022 21:36:08 +0100 Subject: [PATCH 10/80] Implement `ChatTextBox` for new chat design Reference design: https://www.figma.com/file/f8b2dHp9LJCMOqYP4mdrPZ/Client%2FChat?node-id=1%3A297 Adds new component `ChatTextBox`. Exposes `BindableBool` `ShowSearch` to change text input behaviour between normal and search behaviour. Adds new component `ChatTextBar`. Exposes `BindableBool` `ShowSearch` which toggles between showing current chat channel or search icon. Additionally binds to child `ChatTextBox` components. Requires a cached `Bindable` instance to be managed by a parent component. --- InspectCode.sh | 2 +- .../Visual/Online/TestSceneChatTextBox.cs | 96 ++++++++++ osu.Game/Overlays/Chat/ChatTextBar.cs | 174 ++++++++++++++++++ osu.Game/Overlays/Chat/ChatTextBox.cs | 36 ++++ 4 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs create mode 100644 osu.Game/Overlays/Chat/ChatTextBar.cs create mode 100644 osu.Game/Overlays/Chat/ChatTextBox.cs diff --git a/InspectCode.sh b/InspectCode.sh index cf2bc18175..5a72324dd4 100755 --- a/InspectCode.sh +++ b/InspectCode.sh @@ -2,5 +2,5 @@ dotnet tool restore dotnet CodeFileSanity -dotnet jb inspectcode "osu.Desktop.slnf" --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN +dotnet jb inspectcode "osu.Game/osu.Game.csproj" --no-build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN dotnet nvika parsereport "inspectcodereport.xml" --treatwarningsaserrors diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs new file mode 100644 index 0000000000..e72a1d6652 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs @@ -0,0 +1,96 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Chat; +using osu.Game.Overlays; +using osu.Game.Overlays.Chat; + +namespace osu.Game.Tests.Visual.Online +{ + [TestFixture] + public class TestSceneChatTextBox : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); + + [Cached] + private readonly Bindable currentChannel = new Bindable(); + + private OsuSpriteText commitText; + private ChatTextBar bar; + + [SetUp] + public void SetUp() + { + Schedule(() => + { + Child = new GridContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + RowDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 30), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] + { + commitText = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Font = OsuFont.Default.With(size: 20), + }, + }, + new Drawable[] + { + bar = new ChatTextBar + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Width = 0.99f, + }, + }, + }, + }; + + bar.TextBox.OnCommit += (sender, newText) => + { + commitText.Text = $"Commit: {sender.Text}"; + commitText.FadeOutFromOne(1000, Easing.InQuint); + sender.Text = string.Empty; + }; + }); + } + + [Test] + public void TestVisual() + { + AddStep("Public Channel", () => currentChannel.Value = createPublicChannel("#osu")); + AddStep("Public Channel Long Name", () => currentChannel.Value = createPublicChannel("#public-channel-long-name")); + AddStep("Private Channel", () => currentChannel.Value = createPrivateChannel("peppy", 2)); + AddStep("Private Long Name", () => currentChannel.Value = createPrivateChannel("test user long name", 3)); + + AddStep("Chat Mode Channel", () => bar.ShowSearch.Value = false); + AddStep("Chat Mode Search", () => bar.ShowSearch.Value = true); + } + + private static Channel createPublicChannel(string name) + => new Channel { Name = name, Type = ChannelType.Public, Id = 1234 }; + + private static Channel createPrivateChannel(string username, int id) + => new Channel(new APIUser { Id = id, Username = username }); + } +} diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs new file mode 100644 index 0000000000..00284fdd33 --- /dev/null +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -0,0 +1,174 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; +using osuTK; + +namespace osu.Game.Overlays.Chat +{ + public class ChatTextBar : Container + { + public readonly BindableBool ShowSearch = new BindableBool(); + + public ChatTextBox TextBox => chatTextBox; + + [Resolved] + private Bindable currentChannel { get; set; } = null!; + + private OsuTextFlowContainer chattingTextContainer = null!; + private Container searchIconContainer = null!; + private ChatTextBox chatTextBox = null!; + private Container enterContainer = null!; + + private const float chatting_text_width = 180; + private const float search_icon_width = 40; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + RelativeSizeAxes = Axes.X; + Height = 60; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background5, + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] + { + chattingTextContainer = new OsuTextFlowContainer(t => t.Font = t.Font.With(size: 20)) + { + Masking = true, + Width = chatting_text_width, + Padding = new MarginPadding { Left = 10 }, + RelativeSizeAxes = Axes.Y, + TextAnchor = Anchor.CentreRight, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Colour = colourProvider.Background1, + }, + searchIconContainer = new Container + { + RelativeSizeAxes = Axes.Y, + Width = search_icon_width, + Child = new SpriteIcon + { + Icon = FontAwesome.Solid.Search, + Origin = Anchor.CentreRight, + Anchor = Anchor.CentreRight, + Size = new Vector2(20), + Margin = new MarginPadding { Right = 2 }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = 5 }, + Child = chatTextBox = new ChatTextBox + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.X, + ShowSearch = { BindTarget = ShowSearch }, + HoldFocus = true, + ReleaseFocusOnCommit = false, + }, + }, + enterContainer = new Container + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Masking = true, + BorderColour = colourProvider.Background1, + BorderThickness = 2, + CornerRadius = 10, + Margin = new MarginPadding { Right = 10 }, + Size = new Vector2(60, 30), + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.Transparent, + }, + new OsuSpriteText + { + Text = "Enter", + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Colour = colourProvider.Background1, + Font = OsuFont.Torus.With(size: 20), + Margin = new MarginPadding { Bottom = 2 }, + }, + }, + }, + }, + }, + }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + ShowSearch.BindValueChanged(change => + { + if (change.NewValue) + { + chattingTextContainer.Hide(); + enterContainer.Hide(); + searchIconContainer.Show(); + } + else + { + chattingTextContainer.Show(); + enterContainer.Show(); + searchIconContainer.Hide(); + } + }, true); + + currentChannel.BindValueChanged(change => + { + Channel newChannel = change.NewValue; + switch (newChannel?.Type) + { + case ChannelType.Public: + chattingTextContainer.Text = $"chatting in {newChannel.Name}"; + break; + case ChannelType.PM: + chattingTextContainer.Text = $"chatting with {newChannel.Name}"; + break; + default: + chattingTextContainer.Text = ""; + break; + } + }, true); + } + } +} diff --git a/osu.Game/Overlays/Chat/ChatTextBox.cs b/osu.Game/Overlays/Chat/ChatTextBox.cs new file mode 100644 index 0000000000..35ed26cda3 --- /dev/null +++ b/osu.Game/Overlays/Chat/ChatTextBox.cs @@ -0,0 +1,36 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using osu.Framework.Bindables; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Chat +{ + public class ChatTextBox : FocusedTextBox + { + public readonly BindableBool ShowSearch = new BindableBool(); + + public override bool HandleLeftRightArrows => !ShowSearch.Value; + + protected override void LoadComplete() + { + base.LoadComplete(); + + ShowSearch.BindValueChanged(change => + { + PlaceholderText = change.NewValue ? "type here to search" : "type here"; + Schedule(() => Text = string.Empty); + }, true); + } + + protected override void Commit() + { + if (ShowSearch.Value) + return; + + base.Commit(); + } + } +} From 06c32aa1362c5f31d0ca068f9b2cf799d00997ad Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Tue, 29 Mar 2022 22:50:24 +0100 Subject: [PATCH 11/80] Remove changes to `InspectCode.sh` --- InspectCode.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InspectCode.sh b/InspectCode.sh index 5a72324dd4..cf2bc18175 100755 --- a/InspectCode.sh +++ b/InspectCode.sh @@ -2,5 +2,5 @@ dotnet tool restore dotnet CodeFileSanity -dotnet jb inspectcode "osu.Game/osu.Game.csproj" --no-build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN +dotnet jb inspectcode "osu.Desktop.slnf" --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN dotnet nvika parsereport "inspectcodereport.xml" --treatwarningsaserrors From e7d2d94eeef6b11cc13660855b8267247ffc202c Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 30 Mar 2022 02:16:50 +0100 Subject: [PATCH 12/80] Fix code quality issues in `ChatTextBar` --- osu.Game/Overlays/Chat/ChatTextBar.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index 00284fdd33..66f9f281c9 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -21,14 +21,13 @@ namespace osu.Game.Overlays.Chat { public readonly BindableBool ShowSearch = new BindableBool(); - public ChatTextBox TextBox => chatTextBox; + public ChatTextBox TextBox { get; private set; } = null!; [Resolved] private Bindable currentChannel { get; set; } = null!; private OsuTextFlowContainer chattingTextContainer = null!; private Container searchIconContainer = null!; - private ChatTextBox chatTextBox = null!; private Container enterContainer = null!; private const float chatting_text_width = 180; @@ -89,7 +88,7 @@ namespace osu.Game.Overlays.Chat { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Right = 5 }, - Child = chatTextBox = new ChatTextBox + Child = TextBox = new ChatTextBox { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, @@ -156,16 +155,19 @@ namespace osu.Game.Overlays.Chat currentChannel.BindValueChanged(change => { Channel newChannel = change.NewValue; + switch (newChannel?.Type) { case ChannelType.Public: chattingTextContainer.Text = $"chatting in {newChannel.Name}"; break; + case ChannelType.PM: chattingTextContainer.Text = $"chatting with {newChannel.Name}"; break; + default: - chattingTextContainer.Text = ""; + chattingTextContainer.Text = string.Empty; break; } }, true); From eec3fef7a66382337c2c2ee99c7f28ebb951407e Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 30 Mar 2022 20:25:23 +0100 Subject: [PATCH 13/80] Remove the enter box in `ChatTextBar` --- osu.Game/Overlays/Chat/ChatTextBar.cs | 32 --------------------------- 1 file changed, 32 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index 66f9f281c9..7ff1b8d1d3 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -28,7 +28,6 @@ namespace osu.Game.Overlays.Chat private OsuTextFlowContainer chattingTextContainer = null!; private Container searchIconContainer = null!; - private Container enterContainer = null!; private const float chatting_text_width = 180; private const float search_icon_width = 40; @@ -54,7 +53,6 @@ namespace osu.Game.Overlays.Chat new Dimension(GridSizeMode.AutoSize), new Dimension(GridSizeMode.AutoSize), new Dimension(), - new Dimension(GridSizeMode.AutoSize), }, Content = new[] { @@ -98,34 +96,6 @@ namespace osu.Game.Overlays.Chat ReleaseFocusOnCommit = false, }, }, - enterContainer = new Container - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Masking = true, - BorderColour = colourProvider.Background1, - BorderThickness = 2, - CornerRadius = 10, - Margin = new MarginPadding { Right = 10 }, - Size = new Vector2(60, 30), - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Colour4.Transparent, - }, - new OsuSpriteText - { - Text = "Enter", - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Colour = colourProvider.Background1, - Font = OsuFont.Torus.With(size: 20), - Margin = new MarginPadding { Bottom = 2 }, - }, - }, - }, }, }, }, @@ -141,13 +111,11 @@ namespace osu.Game.Overlays.Chat if (change.NewValue) { chattingTextContainer.Hide(); - enterContainer.Hide(); searchIconContainer.Show(); } else { chattingTextContainer.Show(); - enterContainer.Show(); searchIconContainer.Hide(); } }, true); From fff30e8a6ea20b9189f87547e86be90cd01ddfa7 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Wed, 30 Mar 2022 21:01:28 +0100 Subject: [PATCH 14/80] Simplify show/hide of text and search in `ChatTextBar` --- osu.Game/Overlays/Chat/ChatTextBar.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index 7ff1b8d1d3..d7edbb83b6 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -108,16 +108,10 @@ namespace osu.Game.Overlays.Chat ShowSearch.BindValueChanged(change => { - if (change.NewValue) - { - chattingTextContainer.Hide(); - searchIconContainer.Show(); - } - else - { - chattingTextContainer.Show(); - searchIconContainer.Hide(); - } + bool showSearch = change.NewValue; + + chattingTextContainer.FadeTo(showSearch ? 0 : 1); + searchIconContainer.FadeTo(showSearch ? 1 : 0); }, true); currentChannel.BindValueChanged(change => From 40b6f3ff0a1f21da5f2b98b7ed6398906e8500c0 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 31 Mar 2022 15:09:06 +0900 Subject: [PATCH 15/80] Rename method to CalculateAllLegacyCombinations() --- osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 0935f26de6..b5aec0d659 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -121,10 +121,10 @@ namespace osu.Game.Rulesets.Difficulty /// Calculates the difficulty of the beatmap using all mod combinations applicable to the beatmap. /// /// - /// This should only be used to compute difficulties for legacy mod combinations. + /// This can only be used to compute difficulties for legacy mod combinations. /// /// A collection of structures describing the difficulty of the beatmap for each mod combination. - public IEnumerable CalculateAll(CancellationToken cancellationToken = default) + public IEnumerable CalculateAllLegacyCombinations(CancellationToken cancellationToken = default) { var rulesetInstance = ruleset.CreateInstance(); From a987cda30daa1a235b035fafeb4add95618ddcc8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 12:15:26 +0900 Subject: [PATCH 16/80] Rename "Aim Assist" to "Magnetised" to better suit the mod's behaviour As proposed in https://github.com/ppy/osu/discussions/17375. --- ...uModAimAssist.cs => TestSceneOsuModMagnetised.cs} | 6 +++--- osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs | 2 +- .../Mods/{OsuModAimAssist.cs => OsuModMagnetised.cs} | 12 ++++++------ osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs | 2 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) rename osu.Game.Rulesets.Osu.Tests/Mods/{TestSceneOsuModAimAssist.cs => TestSceneOsuModMagnetised.cs} (79%) rename osu.Game.Rulesets.Osu/Mods/{OsuModAimAssist.cs => OsuModMagnetised.cs} (87%) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAimAssist.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMagnetised.cs similarity index 79% rename from osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAimAssist.cs rename to osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMagnetised.cs index b8310bc4e7..e7a40d6337 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAimAssist.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMagnetised.cs @@ -6,16 +6,16 @@ using osu.Game.Rulesets.Osu.Mods; namespace osu.Game.Rulesets.Osu.Tests.Mods { - public class TestSceneOsuModAimAssist : OsuModTestScene + public class TestSceneOsuModMagnetised : OsuModTestScene { [TestCase(0.1f)] [TestCase(0.5f)] [TestCase(1)] - public void TestAimAssist(float strength) + public void TestMagnetised(float strength) { CreateModTest(new ModTestData { - Mod = new OsuModAimAssist + Mod = new OsuModMagnetised { AssistStrength = { Value = strength }, }, diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs index 983964d639..aaf455e95f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Automation; public override string Description => @"Automatic cursor movement - just follow the rhythm."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail), typeof(ModAutoplay), typeof(OsuModAimAssist) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail), typeof(ModAutoplay), typeof(OsuModMagnetised) }; public bool PerformFail() => false; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs index 31179cdf4a..b31ef5d2fd 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModAutoplay : ModAutoplay { - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAimAssist), typeof(OsuModAutopilot), typeof(OsuModSpunOut) }).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModAutopilot), typeof(OsuModSpunOut) }).ToArray(); public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList mods) => new ModReplayData(new OsuAutoGenerator(beatmap, mods).Generate(), new ModCreatedUser { Username = "Autoplay" }); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs b/osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs index d677ab43d0..5b42772358 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModCinema : ModCinema { - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAimAssist), typeof(OsuModAutopilot), typeof(OsuModSpunOut) }).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModAutopilot), typeof(OsuModSpunOut) }).ToArray(); public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList mods) => new ModReplayData(new OsuAutoGenerator(beatmap, mods).Generate(), new ModCreatedUser { Username = "Autoplay" }); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAimAssist.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs similarity index 87% rename from osu.Game.Rulesets.Osu/Mods/OsuModAimAssist.cs rename to osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs index 1abbd67d8f..31598c50e7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAimAssist.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs @@ -16,19 +16,19 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Mods { - internal class OsuModAimAssist : Mod, IUpdatableByPlayfield, IApplicableToDrawableRuleset + internal class OsuModMagnetised : Mod, IUpdatableByPlayfield, IApplicableToDrawableRuleset { - public override string Name => "Aim Assist"; - public override string Acronym => "AA"; - public override IconUsage? Icon => FontAwesome.Solid.MousePointer; + public override string Name => "Magnetised"; + public override string Acronym => "MG"; + public override IconUsage? Icon => FontAwesome.Solid.Magnet; public override ModType Type => ModType.Fun; - public override string Description => "No need to chase the circle – the circle chases you!"; + public override string Description => "No need to chase the circles – your cursor is a magnet!"; public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModRelax) }; private IFrameStableClock gameplayClock; - [SettingSource("Assist strength", "How much this mod will assist you.", 0)] + [SettingSource("Attraction strength", "How strong the pull is.", 0)] public BindableFloat AssistStrength { get; } = new BindableFloat(0.5f) { Precision = 0.05f, diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index 9719de441e..6b81efdca6 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Mods public class OsuModRelax : ModRelax, IUpdatableByPlayfield, IApplicableToDrawableRuleset, IApplicableToPlayer { public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things."; - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModAimAssist) }).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModMagnetised) }).ToArray(); /// /// How early before a hitobject's start time to trigger a hit. diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs index 28c3b069b6..45ce4d555a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; public override string Description => "Everything rotates. EVERYTHING."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModWiggle), typeof(OsuModAimAssist) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModWiggle), typeof(OsuModMagnetised) }; private float theta; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs index 40a05400ea..693a5bee0b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; public override string Description => "They just won't stay still..."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModTransform), typeof(OsuModAimAssist) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModTransform), typeof(OsuModMagnetised) }; private const int wiggle_duration = 90; // (ms) Higher = fewer wiggles private const int wiggle_strength = 10; // Higher = stronger wiggles diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 47a2618ddd..207e7a4ab0 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -195,7 +195,7 @@ namespace osu.Game.Rulesets.Osu new OsuModApproachDifferent(), new OsuModMuted(), new OsuModNoScope(), - new OsuModAimAssist(), + new OsuModMagnetised(), new ModAdaptiveSpeed() }; From ea672745b0d026ce7ca08d45239dca083e614635 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 12:44:49 +0900 Subject: [PATCH 17/80] Add ability to switch between most common tournament scenes using key bindings --- osu.Game.Tournament/TournamentSceneManager.cs | 66 ++++++++++++++++--- 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tournament/TournamentSceneManager.cs b/osu.Game.Tournament/TournamentSceneManager.cs index 80a9c07cde..98338244e4 100644 --- a/osu.Game.Tournament/TournamentSceneManager.cs +++ b/osu.Game.Tournament/TournamentSceneManager.cs @@ -7,8 +7,10 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; using osu.Framework.Threading; using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; using osu.Game.Tournament.Components; using osu.Game.Tournament.Screens; using osu.Game.Tournament.Screens.Drawings; @@ -23,6 +25,7 @@ using osu.Game.Tournament.Screens.TeamIntro; using osu.Game.Tournament.Screens.TeamWin; using osuTK; using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Tournament { @@ -123,16 +126,16 @@ namespace osu.Game.Tournament new ScreenButton(typeof(RoundEditorScreen)) { Text = "Rounds Editor", RequestSelection = SetScreen }, new ScreenButton(typeof(LadderEditorScreen)) { Text = "Bracket Editor", RequestSelection = SetScreen }, new Separator(), - new ScreenButton(typeof(ScheduleScreen)) { Text = "Schedule", RequestSelection = SetScreen }, - new ScreenButton(typeof(LadderScreen)) { Text = "Bracket", RequestSelection = SetScreen }, + new ScreenButton(typeof(ScheduleScreen), Key.S) { Text = "Schedule", RequestSelection = SetScreen }, + new ScreenButton(typeof(LadderScreen), Key.B) { Text = "Bracket", RequestSelection = SetScreen }, new Separator(), - new ScreenButton(typeof(TeamIntroScreen)) { Text = "Team Intro", RequestSelection = SetScreen }, - new ScreenButton(typeof(SeedingScreen)) { Text = "Seeding", RequestSelection = SetScreen }, + new ScreenButton(typeof(TeamIntroScreen), Key.I) { Text = "Team Intro", RequestSelection = SetScreen }, + new ScreenButton(typeof(SeedingScreen), Key.D) { Text = "Seeding", RequestSelection = SetScreen }, new Separator(), - new ScreenButton(typeof(MapPoolScreen)) { Text = "Map Pool", RequestSelection = SetScreen }, - new ScreenButton(typeof(GameplayScreen)) { Text = "Gameplay", RequestSelection = SetScreen }, + new ScreenButton(typeof(MapPoolScreen), Key.M) { Text = "Map Pool", RequestSelection = SetScreen }, + new ScreenButton(typeof(GameplayScreen), Key.G) { Text = "Gameplay", RequestSelection = SetScreen }, new Separator(), - new ScreenButton(typeof(TeamWinScreen)) { Text = "Win", RequestSelection = SetScreen }, + new ScreenButton(typeof(TeamWinScreen), Key.W) { Text = "Win", RequestSelection = SetScreen }, new Separator(), new ScreenButton(typeof(DrawingsScreen)) { Text = "Drawings", RequestSelection = SetScreen }, new ScreenButton(typeof(ShowcaseScreen)) { Text = "Showcase", RequestSelection = SetScreen }, @@ -231,13 +234,60 @@ namespace osu.Game.Tournament { public readonly Type Type; - public ScreenButton(Type type) + private readonly Key? shortcutKey; + + public ScreenButton(Type type, Key? shortcutKey = null) { + this.shortcutKey = shortcutKey; + Type = type; + BackgroundColour = OsuColour.Gray(0.2f); Action = () => RequestSelection?.Invoke(type); RelativeSizeAxes = Axes.X; + + if (shortcutKey != null) + { + Add(new Container + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(24), + Margin = new MarginPadding(5), + Masking = true, + CornerRadius = 4, + Alpha = 0.5f, + Blending = BlendingParameters.Additive, + Children = new Drawable[] + { + new Box + { + Colour = OsuColour.Gray(0.1f), + RelativeSizeAxes = Axes.Both, + }, + new OsuSpriteText + { + Font = OsuFont.Default.With(size: 24), + Y = -2, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = shortcutKey.ToString(), + } + } + }); + } + } + + protected override bool OnKeyDown(KeyDownEvent e) + { + if (e.Key == shortcutKey) + { + TriggerClick(); + return true; + } + + return base.OnKeyDown(e); } private bool isSelected; From de625125d6d04b97d8571d62267e6e04328dfde8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 13:03:48 +0900 Subject: [PATCH 18/80] Rename magnetised mod attraction strength property to match new naming --- osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMagnetised.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMagnetised.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMagnetised.cs index e7a40d6337..9b49e60363 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMagnetised.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMagnetised.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { Mod = new OsuModMagnetised { - AssistStrength = { Value = strength }, + AttractionStrength = { Value = strength }, }, PassCondition = () => true, Autoplay = false, diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs index 31598c50e7..ca6e9cfb1d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Mods private IFrameStableClock gameplayClock; [SettingSource("Attraction strength", "How strong the pull is.", 0)] - public BindableFloat AssistStrength { get; } = new BindableFloat(0.5f) + public BindableFloat AttractionStrength { get; } = new BindableFloat(0.5f) { Precision = 0.05f, MinValue = 0.05f, @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Mods private void easeTo(DrawableHitObject hitObject, Vector2 destination) { - double dampLength = Interpolation.Lerp(3000, 40, AssistStrength.Value); + double dampLength = Interpolation.Lerp(3000, 40, AttractionStrength.Value); float x = (float)Interpolation.DampContinuously(hitObject.X, destination.X, dampLength, gameplayClock.ElapsedFrameTime); float y = (float)Interpolation.DampContinuously(hitObject.Y, destination.Y, dampLength, gameplayClock.ElapsedFrameTime); From 69d4f8612268ab280408f765d44256e385f25f9a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 14:11:53 +0900 Subject: [PATCH 19/80] Fix automatically created "(modified)" skins getting conflicting names Applies the already tested and proven method that is used in the editor to the mutable skin creation flow. --- osu.Game/Skinning/SkinManager.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index bad559d9fe..44b9c69794 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -24,6 +24,7 @@ using osu.Game.Database; using osu.Game.IO; using osu.Game.IO.Archives; using osu.Game.Overlays.Notifications; +using osu.Game.Utils; namespace osu.Game.Skinning { @@ -144,20 +145,26 @@ namespace osu.Game.Skinning if (!s.Protected) return; + var existingSkinNames = realm.Run(r => r.All() + .Where(skin => !skin.DeletePending) + .AsEnumerable() + .Select(skin => skin.Name)); + // if the user is attempting to save one of the default skin implementations, create a copy first. - var result = skinModelManager.Import(new SkinInfo + var skinInfo = new SkinInfo { - Name = s.Name + @" (modified)", Creator = s.Creator, InstantiationInfo = s.InstantiationInfo, - }); + Name = NamingUtils.GetNextBestName(existingSkinNames, $"{s.Name} (modified)") + }; + + var result = skinModelManager.Import(skinInfo); if (result != null) { // save once to ensure the required json content is populated. // currently this only happens on save. result.PerformRead(skin => Save(skin.CreateInstance(this))); - CurrentSkinInfo.Value = result; } }); From 88306a61804c4d960f0c1b06090f9c914fb3f6b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 14:22:26 +0900 Subject: [PATCH 20/80] Disable ability to select random skin from within the skin editor Reasoning is explained in inline comment. I knowingly only applied this to the shortcut key. It's still feasible a user can choose the option from the skin dropdown while the editor is open, but that's less of an issue (because a user won't get the same compulsion that I get to mash the key, only to be greeted with 100 new mutable skins created). --- osu.Game/OsuGame.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 4cd954a646..73121f6e7d 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1061,6 +1061,12 @@ namespace osu.Game return true; case GlobalAction.RandomSkin: + // Don't allow random skin selection while in the skin editor. + // This is mainly to stop many "osu! default (modified)" skins being created via the SkinManager.EnsureMutableSkin() path. + // If people want this to work we can potentially avoid selecting default skins when the editor is open, or allow a maximum of one mutable skin somehow. + if (skinEditor.State.Value == Visibility.Visible) + return false; + SkinManager.SelectRandomSkin(); return true; } From 01829cf2d89ebec7056a5e5cb082b8d91f5cd46d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 14:30:02 +0900 Subject: [PATCH 21/80] Move `SkinnableInfo` error handling to lower level Handling was recently added to handle the usage in `Skin.GetDrawableCompoent`, but it turns out this is also required for `DrawableExtensions.ApplySkinnableInfo` which can throw in a similar fashion. Found while working on sprite support for the editor, where this becomes an actual issue (ie. switching to a branch where the new sprite support is not present can cause unexpected crashes). --- osu.Game/Screens/Play/HUD/SkinnableInfo.cs | 15 ++++++++++++--- osu.Game/Skinning/Skin.cs | 11 +---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs index 95395f8181..1f659fd5bf 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs @@ -9,6 +9,7 @@ using Newtonsoft.Json; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Logging; using osu.Game.Configuration; using osu.Game.Extensions; using osu.Game.Skinning; @@ -84,9 +85,17 @@ namespace osu.Game.Screens.Play.HUD /// The new instance. public Drawable CreateInstance() { - Drawable d = (Drawable)Activator.CreateInstance(Type); - d.ApplySkinnableInfo(this); - return d; + try + { + Drawable d = (Drawable)Activator.CreateInstance(Type); + d.ApplySkinnableInfo(this); + return d; + } + catch (Exception e) + { + Logger.Error(e, $"Unable to create skin component {Type.Name}"); + return Drawable.Empty(); + } } } } diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 2f01bb7301..5d4afc00c4 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -155,16 +155,7 @@ namespace osu.Game.Skinning var components = new List(); foreach (var i in skinnableInfo) - { - try - { - components.Add(i.CreateInstance()); - } - catch (Exception e) - { - Logger.Error(e, $"Unable to create skin component {i.Type.Name}"); - } - } + components.Add(i.CreateInstance()); return new SkinnableTargetComponentsContainer { From 01681ee8749c3562672e20fe603597c39a467410 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 16:19:00 +0900 Subject: [PATCH 22/80] Add missing `ToArray` call Not sure where this went, was there in my original commit. --- osu.Game/Skinning/SkinManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 44b9c69794..832cb01d22 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -145,10 +145,10 @@ namespace osu.Game.Skinning if (!s.Protected) return; - var existingSkinNames = realm.Run(r => r.All() - .Where(skin => !skin.DeletePending) - .AsEnumerable() - .Select(skin => skin.Name)); + string[] existingSkinNames = realm.Run(r => r.All() + .Where(skin => !skin.DeletePending) + .AsEnumerable() + .Select(skin => skin.Name)).ToArray(); // if the user is attempting to save one of the default skin implementations, create a copy first. var skinInfo = new SkinInfo From fc3ebe9b510d34b5d75aae931076328b09851d9d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Apr 2022 10:40:16 +0300 Subject: [PATCH 23/80] Reword log retrieval steps on mobile platforms Co-authored-by: Dean Herbert --- .github/ISSUE_TEMPLATE/bug-issue.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-issue.yml b/.github/ISSUE_TEMPLATE/bug-issue.yml index ea5ee298fb..91ca622f55 100644 --- a/.github/ISSUE_TEMPLATE/bug-issue.yml +++ b/.github/ISSUE_TEMPLATE/bug-issue.yml @@ -65,8 +65,8 @@ body: ### Mobile platforms The places to find the logs on mobile platforms are as follows: - - `Android/data/sh.ppy.osulazer/files/logs` *on Android* - - *On iOS*, they can be obtained by connecting your device to your desktop and copying the `logs` directory from the app's own document storage using iTunes. (https://support.apple.com/en-us/HT201301#copy-to-computer) + - *On Android*, navigate to `Android/data/sh.ppy.osulazer/files/logs` using a file browser app. + - *On iOS*, connect your device to a PC and copy the `logs` directory from the app's document storage using iTunes. (https://support.apple.com/en-us/HT201301#copy-to-computer) --- From 37dea0ff211db68d591a98a008cc3e4c76879ebb Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 1 Apr 2022 17:05:11 +0900 Subject: [PATCH 24/80] Add failing test case --- .../TestSceneMultiplayerPlaylist.cs | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs index cbd8b472b8..f8c0939ea9 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -13,6 +14,7 @@ using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Rulesets; @@ -183,14 +185,41 @@ namespace osu.Game.Tests.Visual.Multiplayer assertItemInHistoryListStep(2, 0); } + [Test] + public void TestInsertedItemDoesNotRefreshAllOthers() + { + AddStep("change to round robin queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayersRoundRobin }).WaitSafely()); + + // Add a few items for the local user. + addItemStep(); + addItemStep(); + addItemStep(); + addItemStep(); + addItemStep(); + + DrawableRoomPlaylistItem[] drawableItems = null; + AddStep("get drawable items", () => drawableItems = this.ChildrenOfType().ToArray()); + + // Add 1 item for another user. + AddStep("join second user", () => MultiplayerClient.AddUser(new APIUser { Id = 10 })); + addItemStep(userId: 10); + + // New item inserted towards the top of the list. + assertItemInQueueListStep(7, 1); + AddAssert("all previous playlist items remained", () => drawableItems.All(this.ChildrenOfType().Contains)); + } + /// /// Adds a step to create a new playlist item. /// - private void addItemStep(bool expired = false) => AddStep("add item", () => MultiplayerClient.AddPlaylistItem(new MultiplayerPlaylistItem(new PlaylistItem(importedBeatmap) + private void addItemStep(bool expired = false, int? userId = null) => AddStep("add item", () => { - Expired = expired, - PlayedAt = DateTimeOffset.Now - }))); + MultiplayerClient.AddUserPlaylistItem(userId ?? API.LocalUser.Value.OnlineID, new MultiplayerPlaylistItem(new PlaylistItem(importedBeatmap) + { + Expired = expired, + PlayedAt = DateTimeOffset.Now + })).WaitSafely(); + }); /// /// Asserts the position of a given playlist item in the queue list. From 16d4544ff9b4ca72fcd0899c9cd56a1a92f91246 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 1 Apr 2022 17:06:37 +0900 Subject: [PATCH 25/80] Prevent reloads when playlist item order changes --- osu.Game/Online/Rooms/PlaylistItem.cs | 6 ++++-- .../Match/Playlist/MultiplayerPlaylist.cs | 20 +++++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/Rooms/PlaylistItem.cs b/osu.Game/Online/Rooms/PlaylistItem.cs index f696362cbb..a56851cfe6 100644 --- a/osu.Game/Online/Rooms/PlaylistItem.cs +++ b/osu.Game/Online/Rooms/PlaylistItem.cs @@ -11,6 +11,7 @@ using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Utils; namespace osu.Game.Online.Rooms { @@ -101,13 +102,13 @@ namespace osu.Game.Online.Rooms #endregion - public PlaylistItem With(IBeatmapInfo beatmap) => new PlaylistItem(beatmap) + public PlaylistItem With(Optional beatmap = default, Optional playlistOrder = default) => new PlaylistItem(beatmap.GetOr(Beatmap)) { ID = ID, OwnerID = OwnerID, RulesetID = RulesetID, Expired = Expired, - PlaylistOrder = PlaylistOrder, + PlaylistOrder = playlistOrder.GetOr(PlaylistOrder), PlayedAt = PlayedAt, AllowedMods = AllowedMods, RequiredMods = RequiredMods, @@ -119,6 +120,7 @@ namespace osu.Game.Online.Rooms && Beatmap.OnlineID == other.Beatmap.OnlineID && RulesetID == other.RulesetID && Expired == other.Expired + && PlaylistOrder == other.PlaylistOrder && AllowedMods.SequenceEqual(other.AllowedMods) && RequiredMods.SequenceEqual(other.RequiredMods); } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs index 879a21e7c1..41f548a630 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs @@ -117,8 +117,24 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist { base.PlaylistItemChanged(item); - removeItemFromLists(item.ID); - addItemToLists(item); + var newApiItem = Playlist.SingleOrDefault(i => i.ID == item.ID); + var existingApiItemInQueue = queueList.Items.SingleOrDefault(i => i.ID == item.ID); + + // Test if the only change between the two playlist items is the order. + if (newApiItem != null && existingApiItemInQueue != null && existingApiItemInQueue.With(playlistOrder: newApiItem.PlaylistOrder).Equals(newApiItem)) + { + // Set the new playlist order directly without refreshing the DrawablePlaylistItem. + existingApiItemInQueue.PlaylistOrder = newApiItem.PlaylistOrder; + + // The following isn't really required, but is here for safety and explicitness. + // MultiplayerQueueList internally binds to changes in Playlist to invalidate its own layout, which is mutated on every playlist operation. + queueList.Invalidate(); + } + else + { + removeItemFromLists(item.ID); + addItemToLists(item); + } } private void addItemToLists(MultiplayerPlaylistItem item) From 6e6271d0c0ac4e00b0b2fd18b124e3cd4a0a6ca2 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 1 Apr 2022 18:31:17 +0900 Subject: [PATCH 26/80] Fix "server-side" room playlist not updated Remove unused using --- .../TestSceneMultiplayerPlaylist.cs | 1 - osu.Game/Online/Rooms/PlaylistItem.cs | 13 ++++++++ .../Multiplayer/TestMultiplayerClient.cs | 33 +++++++++++++++---- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs index f8c0939ea9..1231866b36 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; diff --git a/osu.Game/Online/Rooms/PlaylistItem.cs b/osu.Game/Online/Rooms/PlaylistItem.cs index a56851cfe6..6ec884d79c 100644 --- a/osu.Game/Online/Rooms/PlaylistItem.cs +++ b/osu.Game/Online/Rooms/PlaylistItem.cs @@ -85,6 +85,19 @@ namespace osu.Game.Online.Rooms Beatmap = beatmap; } + public PlaylistItem(MultiplayerPlaylistItem item) + : this(new APIBeatmap { OnlineID = item.BeatmapID }) + { + ID = item.ID; + OwnerID = item.OwnerID; + RulesetID = item.RulesetID; + Expired = item.Expired; + PlaylistOrder = item.PlaylistOrder; + PlayedAt = item.PlayedAt; + RequiredMods = item.RequiredMods.ToArray(); + AllowedMods = item.AllowedMods.ToArray(); + } + public void MarkInvalid() => valid.Value = false; #region Newtonsoft.Json implicit ShouldSerialize() methods diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index b9304f713d..0efaf16f99 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -31,7 +31,11 @@ namespace osu.Game.Tests.Visual.Multiplayer public override IBindable IsConnected => isConnected; private readonly Bindable isConnected = new Bindable(true); + /// + /// The local client's . This is not always equivalent to the server-side room. + /// public new Room? APIRoom => base.APIRoom; + public Action? RoomSetupAction; public bool RoomJoined { get; private set; } @@ -46,6 +50,11 @@ namespace osu.Game.Tests.Visual.Multiplayer /// private readonly List serverSidePlaylist = new List(); + /// + /// Guaranteed up-to-date API room. + /// + private Room? serverSideAPIRoom; + private MultiplayerPlaylistItem? currentItem => Room?.Playlist[currentIndex]; private int currentIndex; private long lastPlaylistItemId; @@ -192,13 +201,13 @@ namespace osu.Game.Tests.Visual.Multiplayer protected override async Task JoinRoom(long roomId, string? password = null) { - var apiRoom = roomManager.ServerSideRooms.Single(r => r.RoomID.Value == roomId); + serverSideAPIRoom = roomManager.ServerSideRooms.Single(r => r.RoomID.Value == roomId); - if (password != apiRoom.Password.Value) + if (password != serverSideAPIRoom.Password.Value) throw new InvalidOperationException("Invalid password."); serverSidePlaylist.Clear(); - serverSidePlaylist.AddRange(apiRoom.Playlist.Select(item => new MultiplayerPlaylistItem(item))); + serverSidePlaylist.AddRange(serverSideAPIRoom.Playlist.Select(item => new MultiplayerPlaylistItem(item))); lastPlaylistItemId = serverSidePlaylist.Max(item => item.ID); var localUser = new MultiplayerRoomUser(api.LocalUser.Value.Id) @@ -210,11 +219,11 @@ namespace osu.Game.Tests.Visual.Multiplayer { Settings = { - Name = apiRoom.Name.Value, - MatchType = apiRoom.Type.Value, + Name = serverSideAPIRoom.Name.Value, + MatchType = serverSideAPIRoom.Type.Value, Password = password, - QueueMode = apiRoom.QueueMode.Value, - AutoStartDuration = apiRoom.AutoStartDuration.Value + QueueMode = serverSideAPIRoom.QueueMode.Value, + AutoStartDuration = serverSideAPIRoom.AutoStartDuration.Value }, Playlist = serverSidePlaylist.ToList(), Users = { localUser }, @@ -479,6 +488,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Debug.Assert(Room != null); Debug.Assert(APIRoom != null); + Debug.Assert(serverSideAPIRoom != null); var item = serverSidePlaylist.Find(i => i.ID == playlistItemId); @@ -495,6 +505,7 @@ namespace osu.Game.Tests.Visual.Multiplayer throw new InvalidOperationException("Attempted to remove an item which has already been played."); serverSidePlaylist.Remove(item); + serverSideAPIRoom.Playlist.RemoveAll(i => i.ID == item.ID); await ((IMultiplayerClient)this).PlaylistItemRemoved(playlistItemId).ConfigureAwait(false); await updateCurrentItem(Room).ConfigureAwait(false); @@ -576,10 +587,12 @@ namespace osu.Game.Tests.Visual.Multiplayer private async Task addItem(MultiplayerPlaylistItem item) { Debug.Assert(Room != null); + Debug.Assert(serverSideAPIRoom != null); item.ID = ++lastPlaylistItemId; serverSidePlaylist.Add(item); + serverSideAPIRoom.Playlist.Add(new PlaylistItem(item)); await ((IMultiplayerClient)this).PlaylistItemAdded(item).ConfigureAwait(false); await updatePlaylistOrder(Room).ConfigureAwait(false); @@ -603,6 +616,8 @@ namespace osu.Game.Tests.Visual.Multiplayer private async Task updatePlaylistOrder(MultiplayerRoom room) { + Debug.Assert(serverSideAPIRoom != null); + List orderedActiveItems; switch (room.Settings.QueueMode) @@ -648,6 +663,10 @@ namespace osu.Game.Tests.Visual.Multiplayer await ((IMultiplayerClient)this).PlaylistItemChanged(item).ConfigureAwait(false); } + + // Also ensure that the API room's playlist is correct. + foreach (var item in serverSideAPIRoom.Playlist) + item.PlaylistOrder = serverSidePlaylist.Single(i => i.ID == item.ID).PlaylistOrder; } } } From 43d03f28255858659aac9c2f5078593ba0ecfd24 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Apr 2022 19:30:16 +0900 Subject: [PATCH 27/80] Put `ToArray` call in correct place in brackets --- osu.Game/Skinning/SkinManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 832cb01d22..71920fb166 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -148,7 +148,7 @@ namespace osu.Game.Skinning string[] existingSkinNames = realm.Run(r => r.All() .Where(skin => !skin.DeletePending) .AsEnumerable() - .Select(skin => skin.Name)).ToArray(); + .Select(skin => skin.Name).ToArray()); // if the user is attempting to save one of the default skin implementations, create a copy first. var skinInfo = new SkinInfo From 0f4b75ab15241c71ac89389a19586cd04ee821b7 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 1 Apr 2022 21:33:57 +0900 Subject: [PATCH 28/80] Add multiplayer lobby countdown SFX --- .../Match/MultiplayerReadyButton.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 62be9ad3bd..8068e80534 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -5,6 +5,8 @@ using System; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Localisation; using osu.Framework.Threading; using osu.Game.Graphics; @@ -27,6 +29,17 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [CanBeNull] private MultiplayerRoom room => multiplayerClient.Room; + private Sample countdownTickSample; + private Sample countdownTickFinalSample; + private int? lastTickPlayed; + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + countdownTickSample = audio.Samples.Get(@"Multiplayer/countdown-tick"); + countdownTickFinalSample = audio.Samples.Get(@"Multiplayer/countdown-tick-final"); + } + protected override void LoadComplete() { base.LoadComplete(); @@ -83,6 +96,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match else countdownRemaining = countdown.TimeRemaining - timeElapsed; + if (countdownRemaining.Seconds <= 10 && (lastTickPlayed == null || lastTickPlayed != countdownRemaining.Seconds)) + { + countdownTickSample?.Play(); + + if (countdownRemaining.Seconds <= 3) + countdownTickFinalSample?.Play(); + + lastTickPlayed = countdownRemaining.Seconds; + } + string countdownText = $"Starting in {countdownRemaining:mm\\:ss}"; switch (localUser?.State) From b07152a119d3df101600ff18356c476ffdce9326 Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Sat, 2 Apr 2022 02:35:37 +0200 Subject: [PATCH 29/80] Initial attempt at writing a test for the toolbarClock I'll add more once I know if this code passes review or not --- .../Visual/Menus/TestSceneToolbarClock.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs index 064d6f82fd..0b5904aa2b 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs @@ -16,6 +16,7 @@ namespace osu.Game.Tests.Visual.Menus public class TestSceneToolbarClock : OsuManualInputManagerTestScene { private readonly Container mainContainer; + private readonly ToolbarClock toolbarClock; public TestSceneToolbarClock() { @@ -49,7 +50,7 @@ namespace osu.Game.Tests.Visual.Menus RelativeSizeAxes = Axes.Y, Width = 2, }, - new ToolbarClock(), + toolbarClock = new ToolbarClock(), new Box { Colour = Color4.DarkRed, @@ -76,5 +77,22 @@ namespace osu.Game.Tests.Visual.Menus { AddStep("Set game time long", () => mainContainer.Clock = new FramedOffsetClock(Clock, false) { Offset = 3600.0 * 24 * 1000 * 98 }); } + + [Test] + public void TestHoverBackground() + { + Box hoverBackground = null; + + AddStep("Retrieve hover background", () => hoverBackground = (Box)toolbarClock.Children[0]); + + AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(mainContainer, new Vector2(0,200))); + AddAssert("Hover background is not visible", () => hoverBackground.Alpha == 0); + + AddStep("Move mouse on top of clock", () => InputManager.MoveMouseTo(mainContainer)); + AddAssert("Hover background is visible", () => hoverBackground.Alpha != 0); + + AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(mainContainer, new Vector2(0,200))); + AddUntilStep("Hover background is not visible", () => hoverBackground.Alpha == 0); + } } } From 6685c97147a92e71fa27b8632a69ae592ad535f4 Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Sat, 2 Apr 2022 02:43:44 +0200 Subject: [PATCH 30/80] mainContainer -> toolbarClock --- osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs index 0b5904aa2b..a17e4030ff 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs @@ -85,13 +85,13 @@ namespace osu.Game.Tests.Visual.Menus AddStep("Retrieve hover background", () => hoverBackground = (Box)toolbarClock.Children[0]); - AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(mainContainer, new Vector2(0,200))); + AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0,200))); AddAssert("Hover background is not visible", () => hoverBackground.Alpha == 0); - AddStep("Move mouse on top of clock", () => InputManager.MoveMouseTo(mainContainer)); + AddStep("Move mouse on top of clock", () => InputManager.MoveMouseTo(toolbarClock)); AddAssert("Hover background is visible", () => hoverBackground.Alpha != 0); - - AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(mainContainer, new Vector2(0,200))); + + AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0,200))); AddUntilStep("Hover background is not visible", () => hoverBackground.Alpha == 0); } } From dc744f18ff7700d1cadd4ac9ad7cfa883a3ecabc Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Sat, 2 Apr 2022 02:43:51 +0200 Subject: [PATCH 31/80] Trim whitespace --- osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs index a17e4030ff..b20ec7cbe5 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs @@ -87,10 +87,8 @@ namespace osu.Game.Tests.Visual.Menus AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0,200))); AddAssert("Hover background is not visible", () => hoverBackground.Alpha == 0); - AddStep("Move mouse on top of clock", () => InputManager.MoveMouseTo(toolbarClock)); AddAssert("Hover background is visible", () => hoverBackground.Alpha != 0); - AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0,200))); AddUntilStep("Hover background is not visible", () => hoverBackground.Alpha == 0); } From 9350f6f5f841684c70864ea64b059e29b0001f95 Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Sat, 2 Apr 2022 02:59:07 +0200 Subject: [PATCH 32/80] Add spaces around commas in Vector2 construction --- osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs index b20ec7cbe5..df61e56011 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs @@ -85,11 +85,11 @@ namespace osu.Game.Tests.Visual.Menus AddStep("Retrieve hover background", () => hoverBackground = (Box)toolbarClock.Children[0]); - AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0,200))); + AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0, 200))); AddAssert("Hover background is not visible", () => hoverBackground.Alpha == 0); AddStep("Move mouse on top of clock", () => InputManager.MoveMouseTo(toolbarClock)); AddAssert("Hover background is visible", () => hoverBackground.Alpha != 0); - AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0,200))); + AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0, 200))); AddUntilStep("Hover background is not visible", () => hoverBackground.Alpha == 0); } } From 95ccac50d4c1f2dfbeefbea492f69f3b06979830 Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Sat, 2 Apr 2022 04:26:16 +0200 Subject: [PATCH 33/80] Add display mode changing test Yup this is gonna fail horribly --- .../Visual/Menus/TestSceneToolbarClock.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs index df61e56011..7643e5032b 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs @@ -2,10 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Timing; +using osu.Game.Configuration; using osu.Game.Overlays.Toolbar; using osuTK; using osuTK.Graphics; @@ -15,6 +18,8 @@ namespace osu.Game.Tests.Visual.Menus [TestFixture] public class TestSceneToolbarClock : OsuManualInputManagerTestScene { + private Bindable clockDisplayMode; + private readonly Container mainContainer; private readonly ToolbarClock toolbarClock; @@ -66,6 +71,12 @@ namespace osu.Game.Tests.Visual.Menus AddSliderStep("scale", 0.5, 4, 1, scale => mainContainer.Scale = new Vector2((float)scale)); } + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + clockDisplayMode = config.GetBindable(OsuSetting.ToolbarClockDisplayMode); + } + [Test] public void TestRealGameTime() { @@ -92,5 +103,22 @@ namespace osu.Game.Tests.Visual.Menus AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0, 200))); AddUntilStep("Hover background is not visible", () => hoverBackground.Alpha == 0); } + + [Test] + public void TestDisplayModeChange() + { + ToolbarClockDisplayMode initialDisplayMode = 0; + + AddStep("Retrieve current state", () => initialDisplayMode = (ToolbarClockDisplayMode)clockDisplayMode.Value); + + AddStep("Trigger click", () => toolbarClock.TriggerClick()); + AddAssert("State changed from initial", () => clockDisplayMode.Value != initialDisplayMode); + AddStep("Trigger click", () => toolbarClock.TriggerClick()); + AddAssert("State changed from initial", () => clockDisplayMode.Value != initialDisplayMode); + AddStep("Trigger click", () => toolbarClock.TriggerClick()); + AddAssert("State changed from initial", () => clockDisplayMode.Value != initialDisplayMode); + AddStep("Trigger click", () => toolbarClock.TriggerClick()); + AddAssert("State is equal to initial", () => clockDisplayMode.Value == initialDisplayMode); + } } } From 245e452d41fcc2af69007f36f1f33122f2805bd5 Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Sat, 2 Apr 2022 04:31:43 +0200 Subject: [PATCH 34/80] Remove redundant typecast I accidentally left in (Thanks InspectCode) --- osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs index 7643e5032b..c1b8cd919d 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs @@ -109,7 +109,7 @@ namespace osu.Game.Tests.Visual.Menus { ToolbarClockDisplayMode initialDisplayMode = 0; - AddStep("Retrieve current state", () => initialDisplayMode = (ToolbarClockDisplayMode)clockDisplayMode.Value); + AddStep("Retrieve current state", () => initialDisplayMode = clockDisplayMode.Value); AddStep("Trigger click", () => toolbarClock.TriggerClick()); AddAssert("State changed from initial", () => clockDisplayMode.Value != initialDisplayMode); From ee9696855ba9e1098d33353179f903283a2cb69a Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Sat, 2 Apr 2022 04:41:05 +0200 Subject: [PATCH 35/80] Remove hover test --- .../Visual/Menus/TestSceneToolbarClock.cs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs index c1b8cd919d..269b3cbad0 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs @@ -89,21 +89,6 @@ namespace osu.Game.Tests.Visual.Menus AddStep("Set game time long", () => mainContainer.Clock = new FramedOffsetClock(Clock, false) { Offset = 3600.0 * 24 * 1000 * 98 }); } - [Test] - public void TestHoverBackground() - { - Box hoverBackground = null; - - AddStep("Retrieve hover background", () => hoverBackground = (Box)toolbarClock.Children[0]); - - AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0, 200))); - AddAssert("Hover background is not visible", () => hoverBackground.Alpha == 0); - AddStep("Move mouse on top of clock", () => InputManager.MoveMouseTo(toolbarClock)); - AddAssert("Hover background is visible", () => hoverBackground.Alpha != 0); - AddStep("Move mouse away from clock", () => InputManager.MoveMouseTo(toolbarClock, new Vector2(0, 200))); - AddUntilStep("Hover background is not visible", () => hoverBackground.Alpha == 0); - } - [Test] public void TestDisplayModeChange() { From a1baced7774603a400e74c191ec6f3811a66f728 Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Sat, 2 Apr 2022 04:45:21 +0200 Subject: [PATCH 36/80] Change behavior of the display mode test I remembered to run InspectCode this time, all good --- osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs index 269b3cbad0..2cf73f5442 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs @@ -92,12 +92,10 @@ namespace osu.Game.Tests.Visual.Menus [Test] public void TestDisplayModeChange() { - ToolbarClockDisplayMode initialDisplayMode = 0; - - AddStep("Retrieve current state", () => initialDisplayMode = clockDisplayMode.Value); + AddStep("Set clock display mode", () => clockDisplayMode.Value = ToolbarClockDisplayMode.Full); AddStep("Trigger click", () => toolbarClock.TriggerClick()); - AddAssert("State changed from initial", () => clockDisplayMode.Value != initialDisplayMode); + AddAssert("State is digital with runtime", () => clockDisplayMode.Value == ToolbarClockDisplayMode.DigitalWithRuntime); AddStep("Trigger click", () => toolbarClock.TriggerClick()); AddAssert("State changed from initial", () => clockDisplayMode.Value != initialDisplayMode); AddStep("Trigger click", () => toolbarClock.TriggerClick()); From c103cee1d5dce8e5b157f594ebc9c9af0825363e Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Sat, 2 Apr 2022 04:53:47 +0200 Subject: [PATCH 37/80] Need to commit the second half again because my Git UI messed up --- osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs index 2cf73f5442..87d836687f 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbarClock.cs @@ -97,11 +97,11 @@ namespace osu.Game.Tests.Visual.Menus AddStep("Trigger click", () => toolbarClock.TriggerClick()); AddAssert("State is digital with runtime", () => clockDisplayMode.Value == ToolbarClockDisplayMode.DigitalWithRuntime); AddStep("Trigger click", () => toolbarClock.TriggerClick()); - AddAssert("State changed from initial", () => clockDisplayMode.Value != initialDisplayMode); + AddAssert("State is digital", () => clockDisplayMode.Value == ToolbarClockDisplayMode.Digital); AddStep("Trigger click", () => toolbarClock.TriggerClick()); - AddAssert("State changed from initial", () => clockDisplayMode.Value != initialDisplayMode); + AddAssert("State is analog", () => clockDisplayMode.Value == ToolbarClockDisplayMode.Analog); AddStep("Trigger click", () => toolbarClock.TriggerClick()); - AddAssert("State is equal to initial", () => clockDisplayMode.Value == initialDisplayMode); + AddAssert("State is full", () => clockDisplayMode.Value == ToolbarClockDisplayMode.Full); } } } From a252c4cad57cae5472adb69101e288e46ab2c18c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 2 Apr 2022 18:08:23 +0300 Subject: [PATCH 38/80] Add random pass/play count data in test scene --- .../Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs index be3fc7aff9..82b34c50c2 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs @@ -71,7 +71,9 @@ namespace osu.Game.Tests.Visual.Online { Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(), Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(), - } + }, + PassCount = RNG.Next(0, 999), + PlayCount = RNG.Next(1000, 1999), }; } From ced5be33e6ac062126ba7352c31eddf00683f303 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 2 Apr 2022 18:08:57 +0300 Subject: [PATCH 39/80] Display pass/play count in success rate percentage tooltip --- osu.Game/Overlays/BeatmapSet/SuccessRate.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs index e08f099226..fed3d7ddaa 100644 --- a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs +++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs @@ -5,6 +5,8 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -19,7 +21,7 @@ namespace osu.Game.Overlays.BeatmapSet protected readonly FailRetryGraph Graph; private readonly FillFlowContainer header; - private readonly OsuSpriteText successPercent; + private readonly SuccessRatePercentage successPercent; private readonly Bar successRate; private readonly Container percentContainer; @@ -45,6 +47,7 @@ namespace osu.Game.Overlays.BeatmapSet float rate = playCount != 0 ? (float)passCount / playCount : 0; successPercent.Text = rate.ToLocalisableString(@"0.#%"); + successPercent.TooltipText = $"{passCount} / {playCount}"; successRate.Length = rate; percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); @@ -80,7 +83,7 @@ namespace osu.Game.Overlays.BeatmapSet RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Width = 0f, - Child = successPercent = new OsuSpriteText + Child = successPercent = new SuccessRatePercentage { Anchor = Anchor.TopRight, Origin = Anchor.TopCentre, @@ -121,5 +124,10 @@ namespace osu.Game.Overlays.BeatmapSet Graph.Padding = new MarginPadding { Top = header.DrawHeight }; } + + private class SuccessRatePercentage : OsuSpriteText, IHasTooltip + { + public LocalisableString TooltipText { get; set; } + } } } From c4635f3c03f1ef7aac5a153459d0b55d8e068259 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 2 Apr 2022 18:36:32 +0300 Subject: [PATCH 40/80] Add failing test case --- .../Online/TestSceneBeatmapListingOverlay.cs | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index a056e0cd2c..159c49ebea 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -5,9 +5,11 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Beatmaps.Drawables.Cards; +using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; @@ -29,6 +31,14 @@ namespace osu.Game.Tests.Visual.Online private BeatmapListingSearchControl searchControl => overlay.ChildrenOfType().Single(); + private OsuConfigManager localConfig; + + [BackgroundDependencyLoader] + private void load() + { + Dependencies.Cache(localConfig = new OsuConfigManager(LocalStorage)); + } + [SetUpSteps] public void SetUpSteps() { @@ -61,6 +71,8 @@ namespace osu.Game.Tests.Visual.Online Id = API.LocalUser.Value.Id + 1, }; }); + + AddStep("reset size", () => localConfig.SetValue(OsuSetting.BeatmapListingCardSize, BeatmapCardSize.Normal)); } [Test] @@ -120,24 +132,25 @@ namespace osu.Game.Tests.Visual.Online assertAllCardsOfType(30); } - [Test] - public void TestCardSizeSwitching() + [TestCase(false)] + [TestCase(true)] + public void TestCardSizeSwitching(bool viaConfig) { AddAssert("is visible", () => overlay.State.Value == Visibility.Visible); AddStep("show many results", () => fetchFor(Enumerable.Repeat(CreateAPIBeatmapSet(Ruleset.Value), 100).ToArray())); assertAllCardsOfType(100); - setCardSize(BeatmapCardSize.Extra); + setCardSize(BeatmapCardSize.Extra, viaConfig); assertAllCardsOfType(100); - setCardSize(BeatmapCardSize.Normal); + setCardSize(BeatmapCardSize.Normal, viaConfig); assertAllCardsOfType(100); AddStep("fetch for 0 beatmaps", () => fetchFor()); AddUntilStep("placeholder shown", () => overlay.ChildrenOfType().SingleOrDefault()?.IsPresent == true); - setCardSize(BeatmapCardSize.Extra); + setCardSize(BeatmapCardSize.Extra, viaConfig); AddAssert("placeholder shown", () => overlay.ChildrenOfType().SingleOrDefault()?.IsPresent == true); } @@ -361,7 +374,13 @@ namespace osu.Game.Tests.Visual.Online AddUntilStep("\"no maps found\" placeholder not shown", () => !overlay.ChildrenOfType().Any(d => d.IsPresent)); } - private void setCardSize(BeatmapCardSize cardSize) => AddStep($"set card size to {cardSize}", () => overlay.ChildrenOfType().Single().Current.Value = cardSize); + private void setCardSize(BeatmapCardSize cardSize, bool viaConfig) => AddStep($"set card size to {cardSize}", () => + { + if (!viaConfig) + overlay.ChildrenOfType().Single().Current.Value = cardSize; + else + localConfig.SetValue(OsuSetting.BeatmapListingCardSize, cardSize); + }); private void assertAllCardsOfType(int expectedCount) where T : BeatmapCard => @@ -370,5 +389,11 @@ namespace osu.Game.Tests.Visual.Online int loadedCorrectCount = this.ChildrenOfType().Count(card => card.IsLoaded && card.GetType() == typeof(T)); return loadedCorrectCount > 0 && loadedCorrectCount == expectedCount; }); + + protected override void Dispose(bool isDisposing) + { + localConfig?.Dispose(); + base.Dispose(isDisposing); + } } } From beb8426d3b866743fdaebf84e82d2e518bc3769c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 2 Apr 2022 18:36:49 +0300 Subject: [PATCH 41/80] Save beatmap listing card size to game config --- osu.Game/Configuration/OsuConfigManager.cs | 4 ++++ .../BeatmapListing/BeatmapListingFilterControl.cs | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index e8f13ba902..2f966ac0a9 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -10,6 +10,7 @@ using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Framework.Testing; +using osu.Game.Beatmaps.Drawables.Cards; using osu.Game.Input; using osu.Game.Input.Bindings; using osu.Game.Localisation; @@ -44,6 +45,8 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2f, 1f); + SetDefault(OsuSetting.BeatmapListingCardSize, BeatmapCardSize.Normal); + SetDefault(OsuSetting.ToolbarClockDisplayMode, ToolbarClockDisplayMode.Full); // Online settings @@ -297,6 +300,7 @@ namespace osu.Game.Configuration RandomSelectAlgorithm, ShowFpsDisplay, ChatDisplayHeight, + BeatmapListingCardSize, ToolbarClockDisplayMode, Version, ShowConvertedBeatmaps, diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index 0f87f04270..e4628e3723 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; using osu.Framework.Threading; using osu.Game.Beatmaps.Drawables.Cards; +using osu.Game.Configuration; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -53,7 +54,9 @@ namespace osu.Game.Overlays.BeatmapListing /// /// The currently selected . /// - public IBindable CardSize { get; } = new Bindable(); + public IBindable CardSize => cardSize; + + private readonly Bindable cardSize = new Bindable(); private readonly BeatmapListingSearchControl searchControl; private readonly BeatmapListingSortTabControl sortControl; @@ -128,6 +131,9 @@ namespace osu.Game.Overlays.BeatmapListing }; } + [Resolved] + private OsuConfigManager config { get; set; } + [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider, IAPIProvider api) { @@ -141,6 +147,8 @@ namespace osu.Game.Overlays.BeatmapListing { base.LoadComplete(); + config.BindWith(OsuSetting.BeatmapListingCardSize, cardSize); + var sortCriteria = sortControl.Current; var sortDirection = sortControl.SortDirection; From b9421d1415af7e15c07b3f7237603170ea4a7a92 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 2 Apr 2022 17:14:27 +0100 Subject: [PATCH 42/80] Simplify text clear and placeholder change in `ChatTextBox` --- osu.Game/Overlays/Chat/ChatTextBox.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatTextBox.cs b/osu.Game/Overlays/Chat/ChatTextBox.cs index 35ed26cda3..e0f949caba 100644 --- a/osu.Game/Overlays/Chat/ChatTextBox.cs +++ b/osu.Game/Overlays/Chat/ChatTextBox.cs @@ -20,8 +20,10 @@ namespace osu.Game.Overlays.Chat ShowSearch.BindValueChanged(change => { - PlaceholderText = change.NewValue ? "type here to search" : "type here"; - Schedule(() => Text = string.Empty); + bool showSearch = change.NewValue; + + PlaceholderText = showSearch ? "type here to search" : "type here"; + Text = string.Empty; }, true); } From 2297073b7eec019a1553735e7fd846f9488aad34 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 2 Apr 2022 17:15:19 +0100 Subject: [PATCH 43/80] Use `OnChatMessageCommit` & `OnSearchTermsChanged` events in `ChatTextBar` --- .../Visual/Online/TestSceneChatTextBox.cs | 39 ++++++++++++++++--- osu.Game/Overlays/Chat/ChatTextBar.cs | 31 +++++++++++++-- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs index e72a1d6652..982fbc397d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs @@ -25,6 +25,7 @@ namespace osu.Game.Tests.Visual.Online private readonly Bindable currentChannel = new Bindable(); private OsuSpriteText commitText; + private OsuSpriteText searchText; private ChatTextBar bar; [SetUp] @@ -47,11 +48,32 @@ namespace osu.Game.Tests.Visual.Online { new Drawable[] { - commitText = new OsuSpriteText + new GridContainer { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Font = OsuFont.Default.With(size: 20), + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(), + }, + Content = new[] + { + new Drawable[] + { + commitText = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Font = OsuFont.Default.With(size: 20), + }, + searchText = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Font = OsuFont.Default.With(size: 20), + }, + }, + }, }, }, new Drawable[] @@ -66,12 +88,17 @@ namespace osu.Game.Tests.Visual.Online }, }; - bar.TextBox.OnCommit += (sender, newText) => + bar.OnChatMessageCommit += (sender, newText) => { - commitText.Text = $"Commit: {sender.Text}"; + commitText.Text = $"OnChatMessageCommit: {sender.Text}"; commitText.FadeOutFromOne(1000, Easing.InQuint); sender.Text = string.Empty; }; + + bar.OnSearchTermsChanged += (text) => + { + searchText.Text = $"OnSearchTermsChanged: {text}"; + }; }); } diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index d7edbb83b6..51c74ee0a5 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -3,15 +3,15 @@ #nullable enable +using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; +using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; using osuTK; @@ -21,13 +21,16 @@ namespace osu.Game.Overlays.Chat { public readonly BindableBool ShowSearch = new BindableBool(); - public ChatTextBox TextBox { get; private set; } = null!; + public event TextBox.OnCommitHandler? OnChatMessageCommit; + + public event Action? OnSearchTermsChanged; [Resolved] private Bindable currentChannel { get; set; } = null!; private OsuTextFlowContainer chattingTextContainer = null!; private Container searchIconContainer = null!; + private ChatTextBox chatTextBox = null!; private const float chatting_text_width = 180; private const float search_icon_width = 40; @@ -86,7 +89,7 @@ namespace osu.Game.Overlays.Chat { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Right = 5 }, - Child = TextBox = new ChatTextBox + Child = chatTextBox = new ChatTextBox { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, @@ -106,12 +109,20 @@ namespace osu.Game.Overlays.Chat { base.LoadComplete(); + chatTextBox.Current.ValueChanged += chatTextBoxChange; + chatTextBox.OnCommit += chatTextBoxCommit; + ShowSearch.BindValueChanged(change => { bool showSearch = change.NewValue; chattingTextContainer.FadeTo(showSearch ? 0 : 1); searchIconContainer.FadeTo(showSearch ? 1 : 0); + + // Clear search terms if any exist when switching back to chat mode + if (!showSearch) + OnSearchTermsChanged?.Invoke(string.Empty); + }, true); currentChannel.BindValueChanged(change => @@ -134,5 +145,17 @@ namespace osu.Game.Overlays.Chat } }, true); } + + private void chatTextBoxChange(ValueChangedEvent change) + { + if (ShowSearch.Value) + OnSearchTermsChanged?.Invoke(change.NewValue); + } + + private void chatTextBoxCommit(TextBox sender, bool newText) + { + if (!ShowSearch.Value) + OnChatMessageCommit?.Invoke(sender, newText); + } } } From 8534dd34632508c05edb0fd6132aca68023c845f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 2 Apr 2022 19:24:16 +0300 Subject: [PATCH 44/80] Simplify `TestCase` attributes to one `Values` attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- .../Visual/Online/TestSceneBeatmapListingOverlay.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index 159c49ebea..9a7bd17902 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -132,9 +132,8 @@ namespace osu.Game.Tests.Visual.Online assertAllCardsOfType(30); } - [TestCase(false)] - [TestCase(true)] - public void TestCardSizeSwitching(bool viaConfig) + [Test] + public void TestCardSizeSwitching([Values] bool viaConfig) { AddAssert("is visible", () => overlay.State.Value == Visibility.Visible); From 9e152cd3fd8aa48354e8bc34bfe3589d2ba77ad4 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 2 Apr 2022 17:27:44 +0100 Subject: [PATCH 45/80] Fix code quality issues --- osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs | 2 +- osu.Game/Overlays/Chat/ChatTextBar.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs index 982fbc397d..42d4a8efbd 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs @@ -95,7 +95,7 @@ namespace osu.Game.Tests.Visual.Online sender.Text = string.Empty; }; - bar.OnSearchTermsChanged += (text) => + bar.OnSearchTermsChanged += text => { searchText.Text = $"OnSearchTermsChanged: {text}"; }; diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index 51c74ee0a5..0eb8056d76 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -122,7 +122,6 @@ namespace osu.Game.Overlays.Chat // Clear search terms if any exist when switching back to chat mode if (!showSearch) OnSearchTermsChanged?.Invoke(string.Empty); - }, true); currentChannel.BindValueChanged(change => From b815f685fc67d869fa99ebd65a8ae49972122a22 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 2 Apr 2022 19:28:33 +0300 Subject: [PATCH 46/80] Flip `viaConfig` conditional branch --- .../Visual/Online/TestSceneBeatmapListingOverlay.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index 9a7bd17902..5999125013 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -375,10 +375,10 @@ namespace osu.Game.Tests.Visual.Online private void setCardSize(BeatmapCardSize cardSize, bool viaConfig) => AddStep($"set card size to {cardSize}", () => { - if (!viaConfig) - overlay.ChildrenOfType().Single().Current.Value = cardSize; - else + if (viaConfig) localConfig.SetValue(OsuSetting.BeatmapListingCardSize, cardSize); + else + overlay.ChildrenOfType().Single().Current.Value = cardSize; }); private void assertAllCardsOfType(int expectedCount) From 0f9461689085cb790de5a3850d051a344d3f0fc2 Mon Sep 17 00:00:00 2001 From: Ame Date: Sat, 2 Apr 2022 19:41:15 +0200 Subject: [PATCH 47/80] Make overlay shortcuts able to be toggled instead of repeatable --- osu.Game/OsuGame.cs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 73121f6e7d..3703318e81 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -95,6 +95,8 @@ namespace osu.Game private SkinEditorOverlay skinEditor; + private NowPlayingOverlay nowPlayingOverlay; + private Container overlayContent; private Container rightFloatingOverlayContent; @@ -818,7 +820,7 @@ namespace osu.Game Origin = Anchor.TopRight, }, rightFloatingOverlayContent.Add, true); - loadComponentSingleFile(new NowPlayingOverlay + loadComponentSingleFile(nowPlayingOverlay = new NowPlayingOverlay { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -1069,6 +1071,26 @@ namespace osu.Game SkinManager.SelectRandomSkin(); return true; + + case GlobalAction.ToggleChat: + chatOverlay.ToggleVisibility(); + return true; + + case GlobalAction.ToggleSocial: + dashboard.ToggleVisibility(); + return true; + + case GlobalAction.ToggleNowPlaying: + nowPlayingOverlay.ToggleVisibility(); + return true; + + case GlobalAction.ToggleBeatmapListing: + beatmapListing.ToggleVisibility(); + return true; + + case GlobalAction.ToggleSettings: + Settings.ToggleVisibility(); + return true; } return false; From 35629e9be815c41788633363c38ec9f6450669ea Mon Sep 17 00:00:00 2001 From: Ame Date: Sat, 2 Apr 2022 20:45:00 +0200 Subject: [PATCH 48/80] General fix for ToolbarButton toggle repetition --- osu.Game/OsuGame.cs | 24 +--------------------- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 2 +- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 3703318e81..73121f6e7d 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -95,8 +95,6 @@ namespace osu.Game private SkinEditorOverlay skinEditor; - private NowPlayingOverlay nowPlayingOverlay; - private Container overlayContent; private Container rightFloatingOverlayContent; @@ -820,7 +818,7 @@ namespace osu.Game Origin = Anchor.TopRight, }, rightFloatingOverlayContent.Add, true); - loadComponentSingleFile(nowPlayingOverlay = new NowPlayingOverlay + loadComponentSingleFile(new NowPlayingOverlay { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -1071,26 +1069,6 @@ namespace osu.Game SkinManager.SelectRandomSkin(); return true; - - case GlobalAction.ToggleChat: - chatOverlay.ToggleVisibility(); - return true; - - case GlobalAction.ToggleSocial: - dashboard.ToggleVisibility(); - return true; - - case GlobalAction.ToggleNowPlaying: - nowPlayingOverlay.ToggleVisibility(); - return true; - - case GlobalAction.ToggleBeatmapListing: - beatmapListing.ToggleVisibility(); - return true; - - case GlobalAction.ToggleSettings: - Settings.ToggleVisibility(); - return true; } return false; diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index c855b76680..670ce65c6b 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -190,7 +190,7 @@ namespace osu.Game.Overlays.Toolbar public bool OnPressed(KeyBindingPressEvent e) { - if (e.Action == Hotkey) + if (!e.Repeat && e.Action == Hotkey) { TriggerClick(); return true; From 01b10e68d2fc8de98912f715bb5e02599d4ba9a9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 2 Apr 2022 23:47:20 +0300 Subject: [PATCH 49/80] Adjust taiko hit objects sizes to match osu!(stable) --- osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index f047c03f4b..1a1fde1990 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Objects /// /// Default size of a drawable taiko hit object. /// - public const float DEFAULT_SIZE = 0.45f; + public const float DEFAULT_SIZE = 0.475f; public override Judgement CreateJudgement() => new TaikoJudgement(); diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs index 6c17573b50..43a099b900 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Objects /// /// Scale multiplier for a strong drawable taiko hit object. /// - public const float STRONG_SCALE = 1.4f; + public const float STRONG_SCALE = 1.525f; /// /// Default size of a strong drawable taiko hit object. From 94fa5e2ef20ab519484638d34be6bbb5238a256c Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Sat, 2 Apr 2022 21:58:54 +0100 Subject: [PATCH 50/80] Use `Action` for event `OnChatMessageCommitted` & clear textbox internally --- osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs | 5 ++--- osu.Game/Overlays/Chat/ChatTextBar.cs | 6 ++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs index 42d4a8efbd..e5dd492183 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs @@ -88,11 +88,10 @@ namespace osu.Game.Tests.Visual.Online }, }; - bar.OnChatMessageCommit += (sender, newText) => + bar.OnChatMessageCommitted += text => { - commitText.Text = $"OnChatMessageCommit: {sender.Text}"; + commitText.Text = $"OnChatMessageCommitted: {text}"; commitText.FadeOutFromOne(1000, Easing.InQuint); - sender.Text = string.Empty; }; bar.OnSearchTermsChanged += text => diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index 0eb8056d76..531c417abc 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Chat { public readonly BindableBool ShowSearch = new BindableBool(); - public event TextBox.OnCommitHandler? OnChatMessageCommit; + public event Action? OnChatMessageCommitted; public event Action? OnSearchTermsChanged; @@ -154,7 +154,9 @@ namespace osu.Game.Overlays.Chat private void chatTextBoxCommit(TextBox sender, bool newText) { if (!ShowSearch.Value) - OnChatMessageCommit?.Invoke(sender, newText); + OnChatMessageCommitted?.Invoke(sender.Text); + + sender.Text = string.Empty; } } } From 4ce69890d4fbad0f7990fe6ceb645cf05ae5252a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 3 Apr 2022 00:21:23 +0300 Subject: [PATCH 51/80] Use `TaikoHitObject.DEFAULT_SIZE` for default circle piece symbol size --- osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs index a106c4f629..f2452ad88c 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; +using osu.Game.Rulesets.Taiko.Objects; using osuTK.Graphics; namespace osu.Game.Rulesets.Taiko.Skinning.Default @@ -24,8 +25,9 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default /// public abstract class CirclePiece : BeatSyncedContainer, IHasAccentColour { - public const float SYMBOL_SIZE = 0.45f; + public const float SYMBOL_SIZE = TaikoHitObject.DEFAULT_SIZE; public const float SYMBOL_BORDER = 8; + private const double pre_beat_transition_time = 80; private Color4 accentColour; From 534cc18ff9ff6d1a8f5726bef122a3ff4c79c006 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 3 Apr 2022 01:21:48 +0300 Subject: [PATCH 52/80] Adjust osu!taiko legacy hit target size to match osu!(stable) --- .../Skinning/Legacy/TaikoLegacyHitTarget.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyHitTarget.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyHitTarget.cs index 9feb2054da..c4657fcc49 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyHitTarget.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyHitTarget.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy new Sprite { Texture = skin.GetTexture("approachcircle"), - Scale = new Vector2(0.73f), + Scale = new Vector2(0.83f), Alpha = 0.47f, // eyeballed to match stable Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy new Sprite { Texture = skin.GetTexture("taikobigcircle"), - Scale = new Vector2(0.7f), + Scale = new Vector2(0.8f), Alpha = 0.22f, // eyeballed to match stable Anchor = Anchor.Centre, Origin = Anchor.Centre, From 970b1951ace07653526d5633f7975019ba006b68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Apr 2022 14:23:45 +0200 Subject: [PATCH 53/80] Rewrite logic slightly to better convey meaning of textbox clear --- osu.Game/Overlays/Chat/ChatTextBar.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index 531c417abc..ef20149dac 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -153,9 +153,10 @@ namespace osu.Game.Overlays.Chat private void chatTextBoxCommit(TextBox sender, bool newText) { - if (!ShowSearch.Value) - OnChatMessageCommitted?.Invoke(sender.Text); + if (ShowSearch.Value) + return; + OnChatMessageCommitted?.Invoke(sender.Text); sender.Text = string.Empty; } } From 6d1844adc3a9b01b8d33a07bdf56fcdc6d671c4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Apr 2022 14:27:37 +0200 Subject: [PATCH 54/80] Use `nameof()` in test to reference event names --- osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs index e5dd492183..a241aa0517 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs @@ -90,13 +90,13 @@ namespace osu.Game.Tests.Visual.Online bar.OnChatMessageCommitted += text => { - commitText.Text = $"OnChatMessageCommitted: {text}"; + commitText.Text = $"{nameof(bar.OnChatMessageCommitted)}: {text}"; commitText.FadeOutFromOne(1000, Easing.InQuint); }; bar.OnSearchTermsChanged += text => { - searchText.Text = $"OnSearchTermsChanged: {text}"; + searchText.Text = $"{nameof(bar.OnSearchTermsChanged)}: {text}"; }; }); } From 1393e3628be595cc9f2ab67cc7ee92d453e2acf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Apr 2022 15:24:00 +0200 Subject: [PATCH 55/80] Invert operands for better readability --- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 670ce65c6b..4a839b048c 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -190,7 +190,7 @@ namespace osu.Game.Overlays.Toolbar public bool OnPressed(KeyBindingPressEvent e) { - if (!e.Repeat && e.Action == Hotkey) + if (e.Action == Hotkey && !e.Repeat) { TriggerClick(); return true; From e1f147a207a42f47370ac42527226212e23d3f9c Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 4 Apr 2022 13:46:41 +0900 Subject: [PATCH 56/80] Mutate playlist in EditUserPlaylistItem --- osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 0efaf16f99..4a974cf61d 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -458,8 +458,8 @@ namespace osu.Game.Tests.Visual.Multiplayer public async Task EditUserPlaylistItem(int userId, MultiplayerPlaylistItem item) { Debug.Assert(Room != null); - Debug.Assert(APIRoom != null); Debug.Assert(currentItem != null); + Debug.Assert(serverSideAPIRoom != null); item.OwnerID = userId; @@ -478,6 +478,7 @@ namespace osu.Game.Tests.Visual.Multiplayer item.PlaylistOrder = existingItem.PlaylistOrder; serverSidePlaylist[serverSidePlaylist.IndexOf(existingItem)] = item; + serverSideAPIRoom.Playlist[serverSideAPIRoom.Playlist.IndexOf(serverSideAPIRoom.Playlist.Single(i => i.ID == item.ID))] = new PlaylistItem(item); await ((IMultiplayerClient)this).PlaylistItemChanged(item).ConfigureAwait(false); } From 39c6eed81969fe883264c04610c4b05ea0968816 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 14:10:57 +0900 Subject: [PATCH 57/80] Update resources --- 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 6a3b113fa2..200008017f 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3c01f29671..18faf318a6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index c8f170497d..b0c382c695 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + From 0abebe4d23a17cfd772fe1b700ddefb2a2427a57 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 14:36:03 +0900 Subject: [PATCH 58/80] Stabilise countdown updates to be based on when whole seconds change --- .../Match/MultiplayerReadyButton.cs | 50 ++++++++++++++----- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 62be9ad3bd..a20d250ea8 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -47,17 +47,33 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match countdownChangeTime = DateTimeOffset.Now; } + scheduleNextCountdownUpdate(); + + updateButtonText(); + updateButtonColour(); + }); + + private void scheduleNextCountdownUpdate() + { if (countdown != null) - countdownUpdateDelegate ??= Scheduler.AddDelayed(updateButtonText, 100, true); + { + // If a countdown is active, schedule relevant components to update on the next whole second. + double timeToNextSecond = countdownTimeRemaining.TotalMilliseconds % 1000; + + countdownUpdateDelegate = Scheduler.AddDelayed(onCountdownTick, timeToNextSecond); + } else { countdownUpdateDelegate?.Cancel(); countdownUpdateDelegate = null; } - updateButtonText(); - updateButtonColour(); - }); + void onCountdownTick() + { + updateButtonText(); + scheduleNextCountdownUpdate(); + } + } private void updateButtonText() { @@ -75,15 +91,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match if (countdown != null) { - TimeSpan timeElapsed = DateTimeOffset.Now - countdownChangeTime; - TimeSpan countdownRemaining; - - if (timeElapsed > countdown.TimeRemaining) - countdownRemaining = TimeSpan.Zero; - else - countdownRemaining = countdown.TimeRemaining - timeElapsed; - - string countdownText = $"Starting in {countdownRemaining:mm\\:ss}"; + string countdownText = $"Starting in {countdownTimeRemaining:mm\\:ss}"; switch (localUser?.State) { @@ -116,6 +124,22 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match } } + private TimeSpan countdownTimeRemaining + { + get + { + TimeSpan timeElapsed = DateTimeOffset.Now - countdownChangeTime; + TimeSpan remaining; + + if (timeElapsed > countdown.TimeRemaining) + remaining = TimeSpan.Zero; + else + remaining = countdown.TimeRemaining - timeElapsed; + + return remaining; + } + } + private void updateButtonColour() { if (room == null) From 09e15f5496a8ecee0f0c77f3743a3b664a150da9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 20:27:03 +0900 Subject: [PATCH 59/80] Remove nullable on `RealmBackedResourceStore` realm parameter --- osu.Game/Skinning/RealmBackedResourceStore.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/RealmBackedResourceStore.cs b/osu.Game/Skinning/RealmBackedResourceStore.cs index e727a7e59a..c81e976a67 100644 --- a/osu.Game/Skinning/RealmBackedResourceStore.cs +++ b/osu.Game/Skinning/RealmBackedResourceStore.cs @@ -24,7 +24,7 @@ namespace osu.Game.Skinning private readonly Live liveSource; private readonly IDisposable? realmSubscription; - public RealmBackedResourceStore(Live source, IResourceStore underlyingStore, RealmAccess? realm) + public RealmBackedResourceStore(Live source, IResourceStore underlyingStore, RealmAccess realm) : base(underlyingStore) { liveSource = source; @@ -32,7 +32,7 @@ namespace osu.Game.Skinning invalidateCache(); Debug.Assert(fileToStoragePathMapping != null); - realmSubscription = realm?.RegisterForNotifications(r => r.All().Where(s => s.ID == source.ID), skinChanged); + realmSubscription = realm.RegisterForNotifications(r => r.All().Where(s => s.ID == source.ID), skinChanged); } protected override void Dispose(bool disposing) From 300feadf6a9adba8120db8dc3e50fcef5c33c99d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 20:27:46 +0900 Subject: [PATCH 60/80] Update `SkinnableSprite` to match more broad usage --- osu.Game/Skinning/SkinnableSprite.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index c6cc4c1bdd..e7c62302b1 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -15,7 +15,7 @@ using osuTK; namespace osu.Game.Skinning { /// - /// A skinnable element which uses a stable sprite and can therefore share implementation logic. + /// A skinnable element which uses a single texture backing. /// public class SkinnableSprite : SkinnableDrawable, ISkinnableDrawable { From dac5dfde8f8cee98be41887a5b4c94444efe3953 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 20:28:43 +0900 Subject: [PATCH 61/80] Remove unnecessary `LazyThreadSafetyMode` specification --- osu.Game/Skinning/RealmBackedResourceStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/RealmBackedResourceStore.cs b/osu.Game/Skinning/RealmBackedResourceStore.cs index c81e976a67..0353b8a64d 100644 --- a/osu.Game/Skinning/RealmBackedResourceStore.cs +++ b/osu.Game/Skinning/RealmBackedResourceStore.cs @@ -61,7 +61,7 @@ namespace osu.Game.Skinning return null; } - private void invalidateCache() => fileToStoragePathMapping = new Lazy>(initialiseFileCache, LazyThreadSafetyMode.ExecutionAndPublication); + private void invalidateCache() => fileToStoragePathMapping = new Lazy>(initialiseFileCache); private Dictionary initialiseFileCache() => liveSource.PerformRead(source => { From de30a42558b7ab5d0e03cae2ed80d574cb694732 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 20:30:14 +0900 Subject: [PATCH 62/80] Add `region` for import methods and move `Dispose` to end of time --- osu.Game/Skinning/Editor/SkinEditor.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index df0bb7a70c..607a881d28 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -197,13 +197,6 @@ namespace osu.Game.Skinning.Editor SelectedComponents.BindCollectionChanged((_, __) => Scheduler.AddOnce(populateSettings), true); } - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - game?.UnregisterImportHandler(this); - } - public void UpdateTargetScreen(Drawable targetScreen) { this.targetScreen = targetScreen; @@ -337,6 +330,8 @@ namespace osu.Game.Skinning.Editor availableTargets.FirstOrDefault(t => t.Components.Contains(item))?.Remove(item); } + #region Drag & drop import handling + public Task Import(params string[] paths) { Schedule(() => @@ -368,5 +363,14 @@ namespace osu.Game.Skinning.Editor public Task Import(params ImportTask[] tasks) => throw new NotImplementedException(); public IEnumerable HandledExtensions => new[] { ".jpg", ".jpeg", ".png" }; + + #endregion + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + game?.UnregisterImportHandler(this); + } } } From 8185020f128dcda5d5a1feb236b02e7a3fbd51d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 20:35:48 +0900 Subject: [PATCH 63/80] Improve the visual of the missing sprite sprite --- osu.Game/Skinning/RealmBackedResourceStore.cs | 1 - osu.Game/Skinning/SkinnableSprite.cs | 33 +++++++++++++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/osu.Game/Skinning/RealmBackedResourceStore.cs b/osu.Game/Skinning/RealmBackedResourceStore.cs index 0353b8a64d..0057132044 100644 --- a/osu.Game/Skinning/RealmBackedResourceStore.cs +++ b/osu.Game/Skinning/RealmBackedResourceStore.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Threading; using osu.Framework.Extensions; using osu.Framework.IO.Stores; using osu.Game.Database; diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index e7c62302b1..c5f110f908 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -6,9 +6,11 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Configuration; +using osu.Game.Graphics.Sprites; using osu.Game.Overlays.Settings; using osuTK; @@ -55,13 +57,7 @@ namespace osu.Game.Skinning var texture = textures.Get(component.LookupName); if (texture == null) - { - return new SpriteIcon - { - Size = new Vector2(100), - Icon = FontAwesome.Solid.QuestionCircle - }; - } + return new SpriteNotFound(component.LookupName); return new Sprite { Texture = texture }; } @@ -98,5 +94,28 @@ namespace osu.Game.Skinning Items = availableFiles; } } + + public class SpriteNotFound : CompositeDrawable + { + public SpriteNotFound(string lookup) + { + AutoSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new SpriteIcon + { + Size = new Vector2(50), + Icon = FontAwesome.Solid.QuestionCircle + }, + new OsuSpriteText + { + Position = new Vector2(25, 50), + Text = $"missing: {lookup}", + Origin = Anchor.TopCentre, + } + }; + } + } } } From 5f358a04e98ba580ce6fa7b7c32ee23a35d8cba3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 20:40:19 +0900 Subject: [PATCH 64/80] Return a valid "lighting" response from `DefaultSkin` This is temporary to allow the new sprite lookup flow to potentially be merged before hit lighting skinnability is addressed. --- osu.Game/Skinning/DefaultSkin.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index c645b0fae4..119b0ec9ad 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -158,6 +158,13 @@ namespace osu.Game.Skinning break; } + switch (component.LookupName) + { + // Temporary until default skin has a valid hit lighting. + case @"lighting": + return Drawable.Empty(); + } + if (GetTexture(component.LookupName) is Texture t) return new Sprite { Texture = t }; From 6b5ee6d89dbf64de2003f0b8cb9e77da75def66b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Apr 2022 20:44:05 +0900 Subject: [PATCH 65/80] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 6a3b113fa2..ff14c97cd9 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3c01f29671..7b0f8c72c5 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index c8f170497d..88daf2eda7 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From 3708f2b744b46251c3d680b27ce7583109f3f2b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 01:05:04 +0900 Subject: [PATCH 66/80] Update resources --- 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 3203ffeac3..fbe13b11ee 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7e193a37f9..1bebf78d97 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index a68bebeabe..efd5bac38e 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + From 9a07a95d39817ce408487253e410d845ee11c27d Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Mon, 4 Apr 2022 19:22:53 +0200 Subject: [PATCH 67/80] Make several delete confirmation buttons dangerous buttons Includes: - Mass deletion - Beatmap deletion - Local score deletion --- .../Sections/Maintenance/MassDeleteConfirmationDialog.cs | 2 +- osu.Game/Screens/Select/BeatmapDeleteDialog.cs | 2 +- osu.Game/Screens/Select/LocalScoreDeleteDialog.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs index 6380232bbb..c481c80d82 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance HeaderText = @"Confirm deletion of"; Buttons = new PopupDialogButton[] { - new PopupDialogOkButton + new PopupDialogDangerousButton { Text = @"Yes. Go for it.", Action = deleteAction diff --git a/osu.Game/Screens/Select/BeatmapDeleteDialog.cs b/osu.Game/Screens/Select/BeatmapDeleteDialog.cs index 1ac278d045..b156c2485b 100644 --- a/osu.Game/Screens/Select/BeatmapDeleteDialog.cs +++ b/osu.Game/Screens/Select/BeatmapDeleteDialog.cs @@ -26,7 +26,7 @@ namespace osu.Game.Screens.Select HeaderText = @"Confirm deletion of"; Buttons = new PopupDialogButton[] { - new PopupDialogOkButton + new PopupDialogDangerousButton { Text = @"Yes. Totally. Delete it.", Action = () => manager?.Delete(beatmap), diff --git a/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs b/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs index 1ae244281b..cb96e3f23e 100644 --- a/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs +++ b/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Select HeaderText = "Confirm deletion of local score"; Buttons = new PopupDialogButton[] { - new PopupDialogOkButton + new PopupDialogDangerousButton { Text = "Yes. Please.", Action = () => scoreManager?.Delete(score) From a1ded66fd85bcc09e2cfe32cf2ca3d938726b2c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 4 Apr 2022 21:59:09 +0200 Subject: [PATCH 68/80] Fix various breakage in delete local score test scene --- .../TestSceneDeleteLocalScore.cs | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index a0a1feff36..c8a8fd43fb 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -44,9 +44,6 @@ namespace osu.Game.Tests.Visual.UserInterface private BeatmapInfo beatmapInfo; - [Resolved] - private RealmAccess realm { get; set; } - [Cached] private readonly DialogOverlay dialogOverlay; @@ -92,6 +89,12 @@ namespace osu.Game.Tests.Visual.UserInterface dependencies.Cache(scoreManager = new ScoreManager(dependencies.Get(), () => beatmapManager, LocalStorage, Realm, Scheduler)); Dependencies.Cache(Realm); + return dependencies; + } + + [BackgroundDependencyLoader] + private void load() => Schedule(() => + { var imported = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).GetResultSafely(); imported?.PerformRead(s => @@ -115,26 +118,26 @@ namespace osu.Game.Tests.Visual.UserInterface importedScores.Add(scoreManager.Import(score).Value); } }); - - return dependencies; - } - - [SetUp] - public void Setup() => Schedule(() => - { - realm.Run(r => - { - // Due to soft deletions, we can re-use deleted scores between test runs - scoreManager.Undelete(r.All().Where(s => s.DeletePending).ToList()); - }); - - leaderboard.BeatmapInfo = beatmapInfo; - leaderboard.RefetchScores(); // Required in the case that the beatmap hasn't changed }); [SetUpSteps] public void SetupSteps() { + AddUntilStep("ensure scores imported", () => importedScores.Count == 50); + AddStep("undelete scores", () => + { + Realm.Run(r => + { + // Due to soft deletions, we can re-use deleted scores between test runs + scoreManager.Undelete(r.All().Where(s => s.DeletePending).ToList()); + }); + }); + AddStep("set up leaderboard", () => + { + leaderboard.BeatmapInfo = beatmapInfo; + leaderboard.RefetchScores(); // Required in the case that the beatmap hasn't changed + }); + // Ensure the leaderboard items have finished showing up AddStep("finish transforms", () => leaderboard.FinishTransforms(true)); AddUntilStep("wait for drawables", () => leaderboard.ChildrenOfType().Any()); From f73062a0d6c141290c93f227db7dd63173175324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 4 Apr 2022 22:22:55 +0200 Subject: [PATCH 69/80] Revert "Remove nullable on `RealmBackedResourceStore` realm parameter" This reverts commit 09e15f5496a8ecee0f0c77f3743a3b664a150da9. --- osu.Game/Skinning/RealmBackedResourceStore.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/RealmBackedResourceStore.cs b/osu.Game/Skinning/RealmBackedResourceStore.cs index 0057132044..7fa24284ee 100644 --- a/osu.Game/Skinning/RealmBackedResourceStore.cs +++ b/osu.Game/Skinning/RealmBackedResourceStore.cs @@ -23,7 +23,7 @@ namespace osu.Game.Skinning private readonly Live liveSource; private readonly IDisposable? realmSubscription; - public RealmBackedResourceStore(Live source, IResourceStore underlyingStore, RealmAccess realm) + public RealmBackedResourceStore(Live source, IResourceStore underlyingStore, RealmAccess? realm) : base(underlyingStore) { liveSource = source; @@ -31,7 +31,7 @@ namespace osu.Game.Skinning invalidateCache(); Debug.Assert(fileToStoragePathMapping != null); - realmSubscription = realm.RegisterForNotifications(r => r.All().Where(s => s.ID == source.ID), skinChanged); + realmSubscription = realm?.RegisterForNotifications(r => r.All().Where(s => s.ID == source.ID), skinChanged); } protected override void Dispose(bool disposing) From da315f8a61b8e6d876dc47bfe709c17b8dc1241e Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Mon, 4 Apr 2022 22:44:35 +0200 Subject: [PATCH 70/80] Make the test hold the button instead of pressing it --- .../Visual/UserInterface/TestSceneDeleteLocalScore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index a0a1feff36..02a37627d7 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -169,7 +169,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("click delete button", () => { InputManager.MoveMouseTo(dialogOverlay.ChildrenOfType().First()); - InputManager.Click(MouseButton.Left); + InputManager.PressButton(MouseButton.Left); }); AddUntilStep("wait for fetch", () => leaderboard.Scores != null); From b2c822a3b1b1498513829c9d469062a52a4a12ec Mon Sep 17 00:00:00 2001 From: CenTdemeern1 Date: Mon, 4 Apr 2022 23:02:07 +0200 Subject: [PATCH 71/80] Release mouse button --- .../Visual/UserInterface/TestSceneDeleteLocalScore.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 02a37627d7..26e7b5bdda 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -174,6 +174,9 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("wait for fetch", () => leaderboard.Scores != null); AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineID != scoreBeingDeleted.OnlineID)); + + // "Clean up" + AddStep("release left mouse button", () => InputManager.ReleaseButton(MouseButton.Left)); } [Test] From 32c89f8643633fa7880292aa7d6828d7e6acaefa Mon Sep 17 00:00:00 2001 From: Ame Date: Tue, 5 Apr 2022 00:33:41 +0200 Subject: [PATCH 72/80] Handle repeated `OnPressed()` on `FooterButton` (without `FooterButtonRandom`) --- osu.Game/Screens/Select/FooterButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/FooterButton.cs b/osu.Game/Screens/Select/FooterButton.cs index 8d2ea47757..9cb178ca8b 100644 --- a/osu.Game/Screens/Select/FooterButton.cs +++ b/osu.Game/Screens/Select/FooterButton.cs @@ -174,7 +174,7 @@ namespace osu.Game.Screens.Select public virtual bool OnPressed(KeyBindingPressEvent e) { - if (e.Action == Hotkey) + if (e.Action == Hotkey && !e.Repeat) { TriggerClick(); return true; From 117d81d84f13c40fb7a782cfda8d1df6c5fa0c92 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 5 Apr 2022 03:04:53 +0300 Subject: [PATCH 73/80] Use perfect osu!(stable) strong scale value Co-authored-by: Dean Herbert --- osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs index 43a099b900..6e0f6a3109 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Objects /// /// Scale multiplier for a strong drawable taiko hit object. /// - public const float STRONG_SCALE = 1.525f; + public const float STRONG_SCALE = 1 / 0.65f; /// /// Default size of a strong drawable taiko hit object. From 174dc1641c3fcc03cf72d8f3d35105a07c732d3e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 11:49:57 +0900 Subject: [PATCH 74/80] Fix multiple issues with timekeeping - Using realtime (`DateTimeOffset.Now`) meant that values would be changing in the same frame, causing misfirings or incorrect displays - No debounce on sample playback meant that scheduling edge cases could potentially cause samples to be played more than once. --- .../Match/MultiplayerReadyButton.cs | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index a9092bc25a..9b7e9a925e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -48,7 +48,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match } private MultiplayerCountdown countdown; - private DateTimeOffset countdownChangeTime; + private double countdownChangeTime; private ScheduledDelegate countdownUpdateDelegate; private void onRoomUpdated() => Scheduler.AddOnce(() => @@ -56,7 +56,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match if (countdown != room?.Countdown) { countdown = room?.Countdown; - countdownChangeTime = DateTimeOffset.Now; + countdownChangeTime = Time.Current; } scheduleNextCountdownUpdate(); @@ -86,13 +86,27 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match int secondsRemaining = countdownTimeRemaining.Seconds; - if (secondsRemaining < 10) countdownTickSample?.Play(); - if (secondsRemaining <= 3) countdownTickFinalSample?.Play(); + playTickSound(secondsRemaining); - scheduleNextCountdownUpdate(); + if (secondsRemaining > 0) + scheduleNextCountdownUpdate(); } } + private double? lastTickSampleTime; + + private void playTickSound(int secondsRemaining) + { + // Simplified debounce. Ticks should only be played roughly once per second regardless of how often this function is called. + if (Time.Current - lastTickSampleTime < 500) + return; + + lastTickSampleTime = Time.Current; + + if (secondsRemaining < 10) countdownTickSample?.Play(); + if (secondsRemaining <= 3) countdownTickFinalSample?.Play(); + } + private void updateButtonText() { if (room == null) @@ -146,13 +160,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { get { - TimeSpan timeElapsed = DateTimeOffset.Now - countdownChangeTime; + double timeElapsed = Time.Current - countdownChangeTime; TimeSpan remaining; - if (timeElapsed > countdown.TimeRemaining) + if (timeElapsed > countdown.TimeRemaining.TotalMilliseconds) remaining = TimeSpan.Zero; else - remaining = countdown.TimeRemaining - timeElapsed; + remaining = countdown.TimeRemaining - TimeSpan.FromMilliseconds(timeElapsed); return remaining; } From 31bf0c4a9b78bb40f5175ea0cb0388243d95c0c8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 13:16:06 +0900 Subject: [PATCH 75/80] Disable "final" sample in countdown for the time being --- .../OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 9b7e9a925e..6eed900963 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -104,7 +104,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match lastTickSampleTime = Time.Current; if (secondsRemaining < 10) countdownTickSample?.Play(); - if (secondsRemaining <= 3) countdownTickFinalSample?.Play(); + // disabled for now pending further work on sound effect + // if (secondsRemaining <= 3) countdownTickFinalSample?.Play(); } private void updateButtonText() From d0f83885cea57adb37334b3646f5eb2e75da784f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 13:20:34 +0900 Subject: [PATCH 76/80] Appease the CI --- .../OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 6eed900963..7e092bd353 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -30,13 +30,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private MultiplayerRoom room => multiplayerClient.Room; private Sample countdownTickSample; - private Sample countdownTickFinalSample; [BackgroundDependencyLoader] private void load(AudioManager audio) { countdownTickSample = audio.Samples.Get(@"Multiplayer/countdown-tick"); - countdownTickFinalSample = audio.Samples.Get(@"Multiplayer/countdown-tick-final"); + // disabled for now pending further work on sound effect + // countdownTickFinalSample = audio.Samples.Get(@"Multiplayer/countdown-tick-final"); } protected override void LoadComplete() From 5f415cbe53b1d406aa83abf402ceb7249305ea61 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 15:48:18 +0900 Subject: [PATCH 77/80] Full potential null reference and add better commentary on countdown scheduling --- .../OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 7e092bd353..4cde7e71c3 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -67,9 +67,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void scheduleNextCountdownUpdate() { + countdownUpdateDelegate?.Cancel(); + if (countdown != null) { - // If a countdown is active, schedule relevant components to update on the next whole second. + // The remaining time on a countdown may be at a fractional portion between two seconds. + // We want to align certain audio/visual cues to the point at which integer seconds change. + // To do so, we schedule to the next whole second. + // Note that scheduler invocation isn't guaranteed to be accurate, so this may still occur slightly late. double timeToNextSecond = countdownTimeRemaining.TotalMilliseconds % 1000; countdownUpdateDelegate = Scheduler.AddDelayed(onCountdownTick, timeToNextSecond); From 8e543204cdca8305cf224f338848876d31120982 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 15:49:47 +0900 Subject: [PATCH 78/80] Remove debounce logic (not required after switching to `Update` clock time) --- .../Multiplayer/Match/MultiplayerReadyButton.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 4cde7e71c3..4860078a79 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -98,16 +98,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match } } - private double? lastTickSampleTime; - private void playTickSound(int secondsRemaining) { - // Simplified debounce. Ticks should only be played roughly once per second regardless of how often this function is called. - if (Time.Current - lastTickSampleTime < 500) - return; - - lastTickSampleTime = Time.Current; - if (secondsRemaining < 10) countdownTickSample?.Play(); // disabled for now pending further work on sound effect // if (secondsRemaining <= 3) countdownTickFinalSample?.Play(); From 3d8ae0465f1313981960f10a949aadbd21bd6651 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Apr 2022 15:51:04 +0900 Subject: [PATCH 79/80] Reword comment slightly --- .../OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 4860078a79..d275f309cb 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -73,8 +73,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { // The remaining time on a countdown may be at a fractional portion between two seconds. // We want to align certain audio/visual cues to the point at which integer seconds change. - // To do so, we schedule to the next whole second. - // Note that scheduler invocation isn't guaranteed to be accurate, so this may still occur slightly late. + // To do so, we schedule to the next whole second. Note that scheduler invocation isn't + // guaranteed to be accurate, so this may still occur slightly late, but even in such a case + // the next invocation will be roughly correct. double timeToNextSecond = countdownTimeRemaining.TotalMilliseconds % 1000; countdownUpdateDelegate = Scheduler.AddDelayed(onCountdownTick, timeToNextSecond); From 2ec15a1ebe83c3e18bbde06f9f41c29db7780797 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 5 Apr 2022 16:47:15 +0900 Subject: [PATCH 80/80] Fix lookup through transformers --- osu.Game/Skinning/LegacySkinTransformer.cs | 2 +- osu.Game/Skinning/SkinnableSprite.cs | 23 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkinTransformer.cs b/osu.Game/Skinning/LegacySkinTransformer.cs index 97084f34e0..9481fc7182 100644 --- a/osu.Game/Skinning/LegacySkinTransformer.cs +++ b/osu.Game/Skinning/LegacySkinTransformer.cs @@ -23,7 +23,7 @@ namespace osu.Game.Skinning /// The which is being transformed. /// [NotNull] - protected ISkin Skin { get; } + protected internal ISkin Skin { get; } protected LegacySkinTransformer([NotNull] ISkin skin) { diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs index c5f110f908..4b4d7fe2c6 100644 --- a/osu.Game/Skinning/SkinnableSprite.cs +++ b/osu.Game/Skinning/SkinnableSprite.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -83,7 +84,7 @@ namespace osu.Game.Skinning // Round-about way of getting the user's skin to find available resources. // In the future we'll probably want to allow access to resources from the fallbacks, or potentially other skins // but that requires further thought. - var highestPrioritySkin = ((SkinnableSprite)SettingSourceObject).source.AllSources.First() as Skin; + var highestPrioritySkin = getHighestPriorityUserSkin(((SkinnableSprite)SettingSourceObject).source.AllSources) as Skin; string[] availableFiles = highestPrioritySkin?.SkinInfo.PerformRead(s => s.Files .Where(f => f.Filename.EndsWith(".png", StringComparison.Ordinal) @@ -92,6 +93,26 @@ namespace osu.Game.Skinning if (availableFiles?.Length > 0) Items = availableFiles; + + static ISkin getHighestPriorityUserSkin(IEnumerable skins) + { + foreach (var skin in skins) + { + if (skin is LegacySkinTransformer transformer && isUserSkin(transformer.Skin)) + return transformer.Skin; + + if (isUserSkin(skin)) + return skin; + } + + return null; + } + + // Temporarily used to exclude undesirable ISkin implementations + static bool isUserSkin(ISkin skin) + => skin.GetType() == typeof(DefaultSkin) + || skin.GetType() == typeof(DefaultLegacySkin) + || skin.GetType() == typeof(LegacySkin); } }