From 958e3fb68bffcd8bbad6f178c585f13bf849faa2 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 31 Jul 2019 22:42:23 +0300 Subject: [PATCH 01/64] Add a property for acquiring online API access --- osu.Game/Tests/Visual/OsuTestScene.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 27d72f3950..dd68ed93e6 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -16,6 +16,7 @@ using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Database; +using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Tests.Beatmaps; @@ -47,6 +48,12 @@ namespace osu.Game.Tests.Visual private readonly Lazy contextFactory; protected DatabaseContextFactory ContextFactory => contextFactory.Value; + /// + /// Whether this test scene requires API access + /// Setting this will cache an actual . + /// + protected virtual bool RequiresAPIAccess => false; + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { // This is the earliest we can get OsuGameBase, which is used by the dummy working beatmap to find textures @@ -57,7 +64,17 @@ namespace osu.Game.Tests.Visual Default = working }; - return Dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + Dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + + if (!RequiresAPIAccess) + { + var dummyAPI = new DummyAPIAccess(); + + Dependencies.CacheAs(dummyAPI); + Add(dummyAPI); + } + + return Dependencies; } protected OsuTestScene() From 034345f1bd5dd28dcd9f5b91bdaca3a6fead35d2 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 31 Jul 2019 22:43:05 +0300 Subject: [PATCH 02/64] Resolve API for dummy-caching tests --- osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs | 7 +------ .../Online/TestSceneAccountCreationOverlay.cs | 14 ++++++++------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs b/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs index f2718b8e80..13116de320 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs @@ -10,14 +10,9 @@ namespace osu.Game.Tests.Visual.Menus { public class TestSceneDisclaimer : ScreenTestScene { - [Cached(typeof(IAPIProvider))] - private readonly DummyAPIAccess api = new DummyAPIAccess(); - [BackgroundDependencyLoader] - private void load() + private void load(IAPIProvider api) { - Add(api); - AddStep("load disclaimer", () => LoadScreen(new Disclaimer())); AddStep("toggle support", () => diff --git a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs index 35449f5687..66ab1fe18a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs @@ -25,17 +25,14 @@ namespace osu.Game.Tests.Visual.Online typeof(AccountCreationScreen), }; - [Cached(typeof(IAPIProvider))] - private DummyAPIAccess api = new DummyAPIAccess(); + private readonly Container userPanelArea; public TestSceneAccountCreationOverlay() { - Container userPanelArea; AccountCreationOverlay accountCreation; Children = new Drawable[] { - api, accountCreation = new AccountCreationOverlay(), userPanelArea = new Container { @@ -46,11 +43,16 @@ namespace osu.Game.Tests.Visual.Online }, }; + AddStep("show", () => accountCreation.Show()); + } + + [BackgroundDependencyLoader] + private void load(IAPIProvider api) + { api.Logout(); api.LocalUser.BindValueChanged(user => { userPanelArea.Child = new UserPanel(user.NewValue) { Width = 200 }; }, true); - AddStep("show", () => accountCreation.Show()); - AddStep("logout", () => api.Logout()); + AddStep("logout", api.Logout); } } } From 849ed0c69df35d11c0b2c6e133e943143bb19e01 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 31 Jul 2019 22:44:44 +0300 Subject: [PATCH 03/64] Acquire api access for online tests --- .../Multiplayer/TestSceneMatchLeaderboard.cs | 7 +++---- .../Visual/Multiplayer/TestSceneMultiScreen.cs | 2 ++ .../Visual/Online/TestSceneBeatmapSetOverlay.cs | 2 ++ .../Visual/Online/TestSceneChangelogOverlay.cs | 2 ++ .../Visual/Online/TestSceneDirectOverlay.cs | 2 ++ .../Visual/Online/TestSceneHistoricalSection.cs | 17 +++++++++-------- .../Visual/Online/TestSceneSocialOverlay.cs | 2 ++ .../Visual/Online/TestSceneUserProfileHeader.cs | 2 ++ .../Online/TestSceneUserProfileOverlay.cs | 2 ++ .../Visual/Online/TestSceneUserRanks.cs | 2 ++ ...estSceneUpdateableBeatmapBackgroundSprite.cs | 2 ++ 11 files changed, 30 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs index fa3c392b2e..723e5fc03d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs @@ -14,6 +14,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMatchLeaderboard : MultiplayerTestScene { + protected override bool RequiresAPIAccess => true; + public TestSceneMatchLeaderboard() { Room.RoomID.Value = 3; @@ -27,11 +29,8 @@ namespace osu.Game.Tests.Visual.Multiplayer }); } - [Resolved] - private IAPIProvider api { get; set; } - [BackgroundDependencyLoader] - private void load() + private void load(IAPIProvider api) { var req = new GetRoomScoresRequest(); req.Success += v => { }; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs index 069e133c2b..b646433846 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiScreen.cs @@ -12,6 +12,8 @@ namespace osu.Game.Tests.Visual.Multiplayer [TestFixture] public class TestSceneMultiScreen : ScreenTestScene { + protected override bool RequiresAPIAccess => true; + public override IReadOnlyList RequiredTypes => new[] { typeof(Screens.Multi.Multiplayer), diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index daee419b52..edb232359f 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -42,6 +42,8 @@ namespace osu.Game.Tests.Visual.Online typeof(BeatmapAvailability), }; + protected override bool RequiresAPIAccess => true; + private RulesetInfo taikoRuleset; private RulesetInfo maniaRuleset; diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index cf8bac7642..324291c9d7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -27,6 +27,8 @@ namespace osu.Game.Tests.Visual.Online typeof(Comments), }; + protected override bool RequiresAPIAccess => true; + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs index 75c2a2a6a1..14ae975806 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectOverlay.cs @@ -13,6 +13,8 @@ namespace osu.Game.Tests.Visual.Online { private DirectOverlay direct; + protected override bool RequiresAPIAccess => true; + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs index 838347800f..c98f98c23d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs @@ -17,14 +17,15 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneHistoricalSection : OsuTestScene { - public override IReadOnlyList RequiredTypes => - new[] - { - typeof(HistoricalSection), - typeof(PaginatedMostPlayedBeatmapContainer), - typeof(DrawableMostPlayedBeatmap), - typeof(DrawableProfileRow) - }; + protected override bool RequiresAPIAccess => true; + + public override IReadOnlyList RequiredTypes => new[] + { + typeof(HistoricalSection), + typeof(PaginatedMostPlayedBeatmapContainer), + typeof(DrawableMostPlayedBeatmap), + typeof(DrawableProfileRow) + }; public TestSceneHistoricalSection() { diff --git a/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs index 5cb96c7ed2..806b36e855 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneSocialOverlay.cs @@ -13,6 +13,8 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneSocialOverlay : OsuTestScene { + protected override bool RequiresAPIAccess => true; + public override IReadOnlyList RequiredTypes => new[] { typeof(UserPanel), diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs index 2285c9b799..555d5334d8 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs @@ -17,6 +17,8 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneUserProfileHeader : OsuTestScene { + protected override bool RequiresAPIAccess => true; + public override IReadOnlyList RequiredTypes => new[] { typeof(ProfileHeader), diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs index c2376aa153..39ba0ea3da 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs @@ -19,6 +19,8 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneUserProfileOverlay : OsuTestScene { + protected override bool RequiresAPIAccess => true; + private readonly TestUserProfileOverlay profile; [Resolved] diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs index 9f0a8c769a..d777f9766a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs @@ -18,6 +18,8 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneUserRanks : OsuTestScene { + protected override bool RequiresAPIAccess => true; + public override IReadOnlyList RequiredTypes => new[] { typeof(DrawableProfileScore), typeof(RanksSection) }; public TestSceneUserRanks() diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs index 9cdfcb6cc4..fdc50be3fa 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs @@ -20,6 +20,8 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneUpdateableBeatmapBackgroundSprite : OsuTestScene { + protected override bool RequiresAPIAccess => true; + private BeatmapSetInfo testBeatmap; private IAPIProvider api; private RulesetStore rulesets; From c77e6074f647574b09403131874ba9566ea1e807 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Wed, 7 Aug 2019 19:58:26 +0300 Subject: [PATCH 04/64] Disallow adding to score after the player has failed --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 2e863f7edb..fd851f2cbb 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -316,6 +316,9 @@ namespace osu.Game.Rulesets.Scoring /// The to apply. protected virtual void ApplyResult(JudgementResult result) { + if (HasFailed) + return; + result.ComboAtJudgement = Combo.Value; result.HighestComboAtJudgement = HighestCombo.Value; result.HealthAtJudgement = Health.Value; From 566d874641922a3b680b648542aecc7702a0236b Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 8 Aug 2019 15:25:07 +0300 Subject: [PATCH 05/64] Prevent failing when reverting to a hasFailedAtJudgement --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index fd851f2cbb..5ca33ff0bc 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Scoring /// /// Whether this ScoreProcessor has already triggered the failed state. /// - public virtual bool HasFailed { get; private set; } + public virtual bool HasFailed { get; protected set; } /// /// The default conditions for failing. @@ -309,6 +309,7 @@ namespace osu.Game.Rulesets.Scoring } private readonly Dictionary scoreResultCounts = new Dictionary(); + private Judgement hasFailedAtJudgement; /// /// Applies the score change of a to this . @@ -317,7 +318,12 @@ namespace osu.Game.Rulesets.Scoring protected virtual void ApplyResult(JudgementResult result) { if (HasFailed) + { + if (hasFailedAtJudgement == null) + hasFailedAtJudgement = result.Judgement; + return; + } result.ComboAtJudgement = Combo.Value; result.HighestComboAtJudgement = HighestCombo.Value; @@ -365,6 +371,17 @@ namespace osu.Game.Rulesets.Scoring /// The judgement scoring result. protected virtual void RevertResult(JudgementResult result) { + if (HasFailed) + { + if (hasFailedAtJudgement == result.Judgement) + { + hasFailedAtJudgement = null; + HasFailed = false; + } + + return; + } + Combo.Value = result.ComboAtJudgement; HighestCombo.Value = result.HighestComboAtJudgement; Health.Value = result.HealthAtJudgement; From 88fa06efbadc23501209021ad4df1436bd4acb7f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Aug 2019 12:29:58 +0900 Subject: [PATCH 06/64] Refactor as proposed --- .../Rulesets/Judgements/JudgementResult.cs | 5 ++++ osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 29 ++++++------------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/osu.Game/Rulesets/Judgements/JudgementResult.cs b/osu.Game/Rulesets/Judgements/JudgementResult.cs index 195fe316ac..0b3c3943c3 100644 --- a/osu.Game/Rulesets/Judgements/JudgementResult.cs +++ b/osu.Game/Rulesets/Judgements/JudgementResult.cs @@ -42,6 +42,11 @@ namespace osu.Game.Rulesets.Judgements /// public double HealthAtJudgement { get; internal set; } + /// + /// Whether the user was in a failed state prior to this occurring. + /// + public bool FailedAtJudgement { get; internal set; } + /// /// Whether a miss or hit occurred. /// diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 5ca33ff0bc..dd8aae27d8 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -309,7 +309,6 @@ namespace osu.Game.Rulesets.Scoring } private readonly Dictionary scoreResultCounts = new Dictionary(); - private Judgement hasFailedAtJudgement; /// /// Applies the score change of a to this . @@ -317,17 +316,13 @@ namespace osu.Game.Rulesets.Scoring /// The to apply. protected virtual void ApplyResult(JudgementResult result) { - if (HasFailed) - { - if (hasFailedAtJudgement == null) - hasFailedAtJudgement = result.Judgement; - - return; - } - result.ComboAtJudgement = Combo.Value; result.HighestComboAtJudgement = HighestCombo.Value; result.HealthAtJudgement = Health.Value; + result.FailedAtJudgement = HasFailed; + + if (HasFailed) + return; JudgedHits++; @@ -371,21 +366,15 @@ namespace osu.Game.Rulesets.Scoring /// The judgement scoring result. protected virtual void RevertResult(JudgementResult result) { - if (HasFailed) - { - if (hasFailedAtJudgement == result.Judgement) - { - hasFailedAtJudgement = null; - HasFailed = false; - } - - return; - } - Combo.Value = result.ComboAtJudgement; HighestCombo.Value = result.HighestComboAtJudgement; Health.Value = result.HealthAtJudgement; + // Todo: Revert HasFailed state with proper player support + + if (result.FailedAtJudgement) + return; + JudgedHits--; if (result.Type != HitResult.None) From 33f4b628a5fb5316f0be69d52f9856e8d3ccef4e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Aug 2019 13:42:02 +0900 Subject: [PATCH 07/64] Make HasFailed private set --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index dd8aae27d8..a39b432e20 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Scoring /// /// Whether this ScoreProcessor has already triggered the failed state. /// - public virtual bool HasFailed { get; protected set; } + public virtual bool HasFailed { get; private set; } /// /// The default conditions for failing. From d47a8c0826c06af5595d01e8a946df9141065169 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Aug 2019 14:04:51 +0900 Subject: [PATCH 08/64] Remove unused combo result count statistic --- .../Scoring/OsuScoreProcessor.cs | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index cf0565c6da..dc4e59294b 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; -using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; @@ -22,8 +20,6 @@ namespace osu.Game.Rulesets.Osu.Scoring private float hpDrainRate; - private readonly Dictionary comboResultCounts = new Dictionary(); - protected override void ApplyBeatmap(Beatmap beatmap) { base.ApplyBeatmap(beatmap); @@ -31,22 +27,6 @@ namespace osu.Game.Rulesets.Osu.Scoring hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate; } - protected override void Reset(bool storeResults) - { - base.Reset(storeResults); - comboResultCounts.Clear(); - } - - protected override void ApplyResult(JudgementResult result) - { - base.ApplyResult(result); - - var osuResult = (OsuJudgementResult)result; - - if (result.Type != HitResult.None) - comboResultCounts[osuResult.ComboType] = comboResultCounts.GetOrDefault(osuResult.ComboType) + 1; - } - protected override double HealthAdjustmentFactorFor(JudgementResult result) { switch (result.Type) From a9c4b5ac4e9c9c25527c4e71fb550809f7962d81 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Aug 2019 14:04:56 +0900 Subject: [PATCH 09/64] Add tests --- .../Visual/Gameplay/TestSceneFailJudgement.cs | 60 +++++++++++++++++++ osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 4 +- 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs new file mode 100644 index 0000000000..bb0901524f --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs @@ -0,0 +1,60 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Scoring; +using osu.Game.Screens.Play; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneFailJudgement : AllPlayersTestScene + { + protected override Player CreatePlayer(Ruleset ruleset) + { + Mods.Value = Array.Empty(); + + var beatmap = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Array.Empty()); + return new FailPlayer(ruleset.GetAutoplayMod().CreateReplayScore(beatmap)); + } + + protected override void AddCheckSteps() + { + AddUntilStep("wait for fail", () => Player.HasFailed); + AddUntilStep("wait for multiple judged objects", () => ((FailPlayer)Player).DrawableRuleset.Playfield.AllHitObjects.Count(h => h.AllJudged) > 1); + AddAssert("total judgements == 1", () => + { + int count = 0; + + foreach (var stat in (HitResult[])Enum.GetValues(typeof(HitResult))) + count += ((FailPlayer)Player).ScoreProcessor.GetStatistic(stat); + + return count == 1; + }); + } + + private class FailPlayer : ReplayPlayer + { + public new DrawableRuleset DrawableRuleset => base.DrawableRuleset; + + public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; + + protected override bool PauseOnFocusLost => false; + + public FailPlayer(Score score) + : base(score, false, false) + { + } + + protected override void LoadComplete() + { + base.LoadComplete(); + ScoreProcessor.FailConditions += (_, __) => true; + } + } + } +} diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index a39b432e20..1e7cd3810d 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -193,7 +193,7 @@ namespace osu.Game.Rulesets.Scoring score.Statistics[result] = GetStatistic(result); } - protected abstract int GetStatistic(HitResult result); + public abstract int GetStatistic(HitResult result); public abstract double GetStandardisedScore(); } @@ -421,7 +421,7 @@ namespace osu.Game.Rulesets.Scoring } } - protected override int GetStatistic(HitResult result) => scoreResultCounts.GetOrDefault(result); + public override int GetStatistic(HitResult result) => scoreResultCounts.GetOrDefault(result); public override double GetStandardisedScore() => getScore(ScoringMode.Standardised); From cb55159b280d19c9376718093e34491c76852d02 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 3 Sep 2019 01:28:51 +0300 Subject: [PATCH 10/64] Use float types for cursor sizes --- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs | 8 ++++---- osu.Game/Configuration/OsuConfigManager.cs | 4 ++-- osu.Game/Graphics/Cursor/MenuCursor.cs | 7 +++---- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 10 +++++----- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs index eb1977a13d..f384b1150e 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private bool cursorExpand; - private Bindable cursorScale; + private Bindable cursorScale; private Bindable autoCursorScale; private readonly IBindable beatmap = new Bindable(); @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor this.beatmap.BindTo(beatmap); this.beatmap.ValueChanged += _ => calculateScale(); - cursorScale = config.GetBindable(OsuSetting.GameplayCursorSize); + cursorScale = config.GetBindable(OsuSetting.GameplayCursorSize); cursorScale.ValueChanged += _ => calculateScale(); autoCursorScale = config.GetBindable(OsuSetting.AutoCursorSize); @@ -70,12 +70,12 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private void calculateScale() { - float scale = (float)cursorScale.Value; + float scale = cursorScale.Value; if (autoCursorScale.Value && beatmap.Value != null) { // if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier. - scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY); + scale *= 1f - 0.7f * (1f + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY; } scaleTarget.Scale = new Vector2(scale); diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 0cecbb225f..c901a28a59 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -58,8 +58,8 @@ namespace osu.Game.Configuration Set(OsuSetting.AudioOffset, 0, -500.0, 500.0, 1); // Input - Set(OsuSetting.MenuCursorSize, 1.0, 0.5f, 2, 0.01); - Set(OsuSetting.GameplayCursorSize, 1.0, 0.1f, 2, 0.01); + Set(OsuSetting.MenuCursorSize, 1.0f, 0.5f, 2f, 0.01f); + Set(OsuSetting.GameplayCursorSize, 1.0f, 0.1f, 2f, 0.01f); Set(OsuSetting.AutoCursorSize, false); Set(OsuSetting.MouseDisableButtons, false); diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index e103798355..5a83d8e4ce 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -124,7 +124,7 @@ namespace osu.Game.Graphics.Cursor public class Cursor : Container { private Container cursorContainer; - private Bindable cursorScale; + private Bindable cursorScale; private const float base_scale = 0.15f; public Sprite AdditiveLayer; @@ -159,9 +159,8 @@ namespace osu.Game.Graphics.Cursor } }; - cursorScale = config.GetBindable(OsuSetting.MenuCursorSize); - cursorScale.ValueChanged += scale => cursorContainer.Scale = new Vector2((float)scale.NewValue * base_scale); - cursorScale.TriggerChange(); + cursorScale = config.GetBindable(OsuSetting.MenuCursorSize); + cursorScale.BindValueChanged(scale => cursorContainer.Scale = new Vector2(scale.NewValue * base_scale), true); } } diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 35be930a2e..d3029d8ab9 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -35,16 +35,16 @@ namespace osu.Game.Overlays.Settings.Sections Children = new Drawable[] { skinDropdown = new SkinSettingsDropdown(), - new SettingsSlider + new SettingsSlider { LabelText = "Menu cursor size", - Bindable = config.GetBindable(OsuSetting.MenuCursorSize), + Bindable = config.GetBindable(OsuSetting.MenuCursorSize), KeyboardStep = 0.01f }, - new SettingsSlider + new SettingsSlider { LabelText = "Gameplay cursor size", - Bindable = config.GetBindable(OsuSetting.GameplayCursorSize), + Bindable = config.GetBindable(OsuSetting.GameplayCursorSize), KeyboardStep = 0.01f }, new SettingsCheckbox @@ -95,7 +95,7 @@ namespace osu.Game.Overlays.Settings.Sections } } - private class SizeSlider : OsuSliderBar + private class SizeSlider : OsuSliderBar { public override string TooltipText => Current.Value.ToString(@"0.##x"); } From de6dba9716ba720f8756b38662ecc48aa32d148d Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 3 Sep 2019 01:50:52 +0300 Subject: [PATCH 11/64] Use float type for chat overlay height --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- osu.Game/Overlays/ChatOverlay.cs | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 0cecbb225f..9246acb00b 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -31,7 +31,7 @@ namespace osu.Game.Configuration Set(OsuSetting.RandomSelectAlgorithm, RandomSelectAlgorithm.RandomPermutation); - Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1); + Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2f, 1f); // Online settings Set(OsuSetting.Username, string.Empty); diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 53a05656b1..6f848c7627 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -54,7 +54,7 @@ namespace osu.Game.Overlays private Box chatBackground; private Box tabBackground; - public Bindable ChatHeight { get; set; } + public Bindable ChatHeight { get; set; } private Container channelSelectionContainer; protected ChannelSelectionOverlay ChannelSelectionOverlay; @@ -190,14 +190,13 @@ namespace osu.Game.Overlays ChannelSelectionOverlay.OnRequestJoin = channel => channelManager.JoinChannel(channel); ChannelSelectionOverlay.OnRequestLeave = channelManager.LeaveChannel; - ChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight); - ChatHeight.ValueChanged += height => + ChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight); + ChatHeight.BindValueChanged(height => { - chatContainer.Height = (float)height.NewValue; - channelSelectionContainer.Height = 1f - (float)height.NewValue; - tabBackground.FadeTo(height.NewValue == 1 ? 1 : 0.8f, 200); - }; - ChatHeight.TriggerChange(); + chatContainer.Height = height.NewValue; + channelSelectionContainer.Height = 1f - height.NewValue; + tabBackground.FadeTo(height.NewValue == 1f ? 1f : 0.8f, 200); + }, true); chatBackground.Colour = colours.ChatBlue; @@ -273,7 +272,7 @@ namespace osu.Game.Overlays } } - private double startDragChatHeight; + private float startDragChatHeight; private bool isDragging; protected override bool OnDragStart(DragStartEvent e) @@ -291,7 +290,7 @@ namespace osu.Game.Overlays { if (isDragging) { - double targetChatHeight = startDragChatHeight - (e.MousePosition.Y - e.MouseDownPosition.Y) / Parent.DrawSize.Y; + float targetChatHeight = startDragChatHeight - (e.MousePosition.Y - e.MouseDownPosition.Y) / Parent.DrawSize.Y; // If the channel selection screen is shown, mind its minimum height if (ChannelSelectionOverlay.State.Value == Visibility.Visible && targetChatHeight > 1f - channel_selection_min_height) From bebc3309ced768307cac8398cebf804055dbf79c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 17:57:34 +0900 Subject: [PATCH 12/64] Refactor skin configuration to be infinitely extensible --- .../TestSceneCatcher.cs | 4 +- .../TestSceneSkinFallbacks.cs | 2 + .../Objects/Drawables/DrawableSlider.cs | 9 ++-- .../Objects/Drawables/Pieces/SliderBall.cs | 3 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- .../Skinning/LegacySliderBall.cs | 2 +- ...acySkin.cs => OsuLegacySkinTransformer.cs} | 51 +++++++++--------- .../Skinning/OsuSkinColour.cs | 12 +++++ .../Skinning/OsuSkinConfiguration.cs | 14 +++++ osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs | 3 +- .../Gameplay/TestSceneSkinnableDrawable.cs | 10 ++-- .../Objects/Drawables/DrawableHitObject.cs | 6 ++- osu.Game/Screens/Menu/LogoVisualisation.cs | 2 +- osu.Game/Screens/Menu/MenuSideFlashes.cs | 2 +- osu.Game/Skinning/DefaultSkin.cs | 5 +- osu.Game/Skinning/DefaultSkinConfiguration.cs | 4 -- osu.Game/Skinning/GameplaySkinComponent.cs | 2 +- osu.Game/Skinning/GlobalSkinColour.cs | 10 ++++ osu.Game/Skinning/GlobalSkinConfiguration.cs | 10 ++++ osu.Game/Skinning/ISkin.cs | 4 +- osu.Game/Skinning/LegacySkin.cs | 43 +++++++++++++++ osu.Game/Skinning/LegacySkinDecoder.cs | 54 +++++++------------ osu.Game/Skinning/Skin.cs | 8 +-- osu.Game/Skinning/SkinConfigManager.cs | 16 ++++++ osu.Game/Skinning/SkinConfiguration.cs | 10 +--- osu.Game/Skinning/SkinCustomColourLookup.cs | 15 ++++++ osu.Game/Skinning/SkinManager.cs | 4 +- osu.Game/Skinning/SkinProvidingContainer.cs | 14 +++-- 28 files changed, 214 insertions(+), 107 deletions(-) rename osu.Game.Rulesets.Osu/Skinning/{OsuLegacySkin.cs => OsuLegacySkinTransformer.cs} (70%) create mode 100644 osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs create mode 100644 osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs create mode 100644 osu.Game/Skinning/GlobalSkinColour.cs create mode 100644 osu.Game/Skinning/GlobalSkinConfiguration.cs create mode 100644 osu.Game/Skinning/SkinConfigManager.cs create mode 100644 osu.Game/Skinning/SkinCustomColourLookup.cs diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index c89cd95f36..6a4294a178 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osuTK.Graphics; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics.Textures; using osu.Game.Audio; @@ -99,8 +100,7 @@ namespace osu.Game.Rulesets.Catch.Tests public Texture GetTexture(string componentName) => throw new NotImplementedException(); - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => - throw new NotImplementedException(); + public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException(); } } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs index fe73e7c861..02c65db6ad 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; @@ -135,6 +136,7 @@ namespace osu.Game.Rulesets.Osu.Tests public SampleChannel GetSample(ISampleInfo sampleInfo) => null; public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => default; + public IBindable GetConfig(TLookup lookup) => null; public event Action SourceChanged; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 1749ea1f60..00c953c393 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -12,6 +12,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Configuration; +using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Scoring; using osuTK.Graphics; using osu.Game.Skinning; @@ -166,12 +167,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.SkinChanged(skin, allowFallback); - Body.BorderSize = skin.GetValue(s => s.SliderBorderSize) ?? SliderBody.DEFAULT_BORDER_SIZE; - sliderPathRadius = skin.GetValue(s => s.SliderPathRadius) ?? OsuHitObject.OBJECT_RADIUS; + Body.BorderSize = skin.GetConfig(OsuSkinConfiguration.SliderBorderSize)?.Value ?? SliderBody.DEFAULT_BORDER_SIZE; + sliderPathRadius = skin.GetConfig(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS; updatePathRadius(); - Body.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : (Color4?)null) ?? AccentColour.Value; - Body.BorderColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : (Color4?)null) ?? Color4.White; + Body.AccentColour = skin.GetConfig(OsuSkinColour.SliderTrackOverride)?.Value ?? AccentColour.Value; + Body.BorderColour = skin.GetConfig(OsuSkinColour.SliderBorder)?.Value ?? Color4.White; } private void updatePathRadius() => Body.PathRadius = slider.Scale * sliderPathRadius; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 7c871c6ccd..ef7b077480 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -11,6 +11,7 @@ using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Skinning; using osuTK.Graphics; using osu.Game.Skinning; using osuTK; @@ -218,7 +219,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { RelativeSizeAxes = Axes.Both; - float radius = skin.GetValue(s => s.SliderPathRadius) ?? OsuHitObject.OBJECT_RADIUS; + float radius = skin.GetConfig(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS; InternalChild = new CircularContainer { diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 27899ab56e..ceb9ed9343 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.Osu public override RulesetSettingsSubsection CreateSettings() => new OsuSettingsSubsection(this); - public override ISkin CreateLegacySkinProvider(ISkinSource source) => new OsuLegacySkin(source); + public override ISkin CreateLegacySkinProvider(ISkinSource source) => new OsuLegacySkinTransformer(source); public override int? LegacyID => 0; diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs index ec838c596d..81c02199d0 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Skinning [BackgroundDependencyLoader] private void load(ISkinSource skin, DrawableHitObject drawableObject) { - animationContent.Colour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Color4.White; + animationContent.Colour = skin.GetConfig(OsuSkinColour.SliderBall)?.Value ?? Color4.White; InternalChildren = new[] { diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs similarity index 70% rename from osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs rename to osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index e3e302b81c..284259705a 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkin.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -3,21 +3,19 @@ using System; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Game.Audio; using osu.Game.Skinning; using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning { - public class OsuLegacySkin : ISkin + public class OsuLegacySkinTransformer : ISkin { private readonly ISkin source; - private Lazy configuration; - private Lazy hasHitCircle; /// @@ -27,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Skinning /// private const float legacy_circle_radius = 64 - 5; - public OsuLegacySkin(ISkinSource source) + public OsuLegacySkinTransformer(ISkinSource source) { this.source = source; @@ -37,21 +35,6 @@ namespace osu.Game.Rulesets.Osu.Skinning private void sourceChanged() { - // these need to be lazy in order to ensure they aren't called before the dependencies have been loaded into our source. - configuration = new Lazy(() => - { - var config = new SkinConfiguration(); - if (hasHitCircle.Value) - config.SliderPathRadius = legacy_circle_radius; - - // defaults should only be applied for non-beatmap skins (which are parsed via this constructor). - config.CustomColours["SliderBall"] = - source.GetValue(s => s.CustomColours.TryGetValue("SliderBall", out var val) ? val : (Color4?)null) - ?? new Color4(2, 170, 255, 255); - - return config; - }); - hasHitCircle = new Lazy(() => source.GetTexture("hitcircle") != null); } @@ -96,8 +79,8 @@ namespace osu.Game.Rulesets.Osu.Skinning return null; case OsuSkinComponents.HitCircleText: - string font = GetValue(config => config.HitCircleFont); - var overlap = GetValue(config => config.HitCircleOverlap); + var font = GetConfig(OsuSkinConfiguration.HitCircleFont)?.Value ?? "default"; + var overlap = GetConfig(OsuSkinConfiguration.HitCircleOverlap)?.Value ?? 0; return !hasFont(font) ? null @@ -116,13 +99,27 @@ namespace osu.Game.Rulesets.Osu.Skinning public SampleChannel GetSample(ISampleInfo sample) => source.GetSample(sample); - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration + public IBindable GetConfig(TLookup lookup) { - TValue val; - if (configuration.Value is TConfiguration conf && (val = query.Invoke(conf)) != null) - return val; + switch (lookup) + { + case OsuSkinColour colour: + return source.GetConfig(new SkinCustomColourLookup(colour)); - return source.GetValue(query); + case OsuSkinConfiguration osuLookup: + switch (osuLookup) + { + case OsuSkinConfiguration.SliderPathRadius: + if (hasHitCircle.Value) + return new BindableFloat(legacy_circle_radius) as Bindable; + + break; + } + + break; + } + + return source.GetConfig(lookup); } private bool hasFont(string fontName) => source.GetTexture($"{fontName}-0") != null; diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs new file mode 100644 index 0000000000..4e6d3ef0e4 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs @@ -0,0 +1,12 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Osu.Skinning +{ + public enum OsuSkinColour + { + SliderTrackOverride, + SliderBorder, + SliderBall + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs new file mode 100644 index 0000000000..a6b87150ae --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs @@ -0,0 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Osu.Skinning +{ + public enum OsuSkinConfiguration + { + HitCircleFont, + HitCircleOverlap, + SliderBorderSize, + SliderPathRadius, + CursorExpand, + } +} diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs index 869c27dcac..ac641ecfbc 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -38,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor protected override void SkinChanged(ISkinSource skin, bool allowFallback) { - cursorExpand = skin.GetValue(s => s.CursorExpand ?? true); + cursorExpand = skin.GetConfig(OsuSkinConfiguration.CursorExpand)?.Value ?? true; } [BackgroundDependencyLoader] diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs index ee5552c6e0..91ee16cab7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Linq; using NUnit.Framework; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -137,7 +138,8 @@ namespace osu.Game.Tests.Visual.Gameplay { public new Drawable Drawable => base.Drawable; - public ExposedSkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) + public ExposedSkinnableDrawable(string name, Func defaultImplementation, Func allowFallback = null, + ConfineMode confineMode = ConfineMode.ScaleDownToFit) : base(new TestSkinComponent(name), defaultImplementation, allowFallback, confineMode) { } @@ -256,7 +258,7 @@ namespace osu.Game.Tests.Visual.Gameplay public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); + public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException(); } private class SecondarySource : ISkin @@ -267,7 +269,7 @@ namespace osu.Game.Tests.Visual.Gameplay public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); + public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException(); } private class SkinSourceContainer : Container, ISkin @@ -278,7 +280,7 @@ namespace osu.Game.Tests.Visual.Gameplay public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); + public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException(); } private class TestSkinComponent : ISkinComponent diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 1a224b2cea..a6d0aad880 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -241,7 +241,11 @@ namespace osu.Game.Rulesets.Objects.Drawables base.SkinChanged(skin, allowFallback); if (HitObject is IHasComboInformation combo) - AccentColour.Value = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; + { + var comboColours = skin.GetConfig>(GlobalSkinConfiguration.ComboColours)?.Value; + + AccentColour.Value = comboColours?.Count > 0 ? comboColours[combo.ComboIndex % comboColours.Count] : Color4.White; + } } /// diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index 6984959e9c..59ab6ad265 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -122,7 +122,7 @@ namespace osu.Game.Screens.Menu Color4 defaultColour = Color4.White.Opacity(0.2f); if (user.Value?.IsSupporter ?? false) - AccentColour = skin.Value.GetValue(s => s.CustomColours.ContainsKey("MenuGlow") ? s.CustomColours["MenuGlow"] : (Color4?)null) ?? defaultColour; + AccentColour = skin.Value.GetConfig(GlobalSkinColour.MenuGlow)?.Value ?? defaultColour; else AccentColour = defaultColour; } diff --git a/osu.Game/Screens/Menu/MenuSideFlashes.cs b/osu.Game/Screens/Menu/MenuSideFlashes.cs index 393964561c..55a6a33e89 100644 --- a/osu.Game/Screens/Menu/MenuSideFlashes.cs +++ b/osu.Game/Screens/Menu/MenuSideFlashes.cs @@ -112,7 +112,7 @@ namespace osu.Game.Screens.Menu Color4 baseColour = colours.Blue; if (user.Value?.IsSupporter ?? false) - baseColour = skin.Value.GetValue(s => s.CustomColours.ContainsKey("MenuGlow") ? s.CustomColours["MenuGlow"] : (Color4?)null) ?? baseColour; + baseColour = skin.Value.GetConfig(GlobalSkinColour.MenuGlow)?.Value ?? baseColour; // linear colour looks better in this case, so let's use it for now. Color4 gradientDark = baseColour.Opacity(0).ToLinear(); diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 9eda5d597a..4dee70a47f 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -1,7 +1,8 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Game.Audio; @@ -21,5 +22,7 @@ namespace osu.Game.Skinning public override Texture GetTexture(string componentName) => null; public override SampleChannel GetSample(ISampleInfo sampleInfo) => null; + + public override IBindable GetConfig(TLookup lookup) => null; } } diff --git a/osu.Game/Skinning/DefaultSkinConfiguration.cs b/osu.Game/Skinning/DefaultSkinConfiguration.cs index 722b35f102..f52fac6077 100644 --- a/osu.Game/Skinning/DefaultSkinConfiguration.cs +++ b/osu.Game/Skinning/DefaultSkinConfiguration.cs @@ -12,8 +12,6 @@ namespace osu.Game.Skinning { public DefaultSkinConfiguration() { - HitCircleFont = "default"; - ComboColours.AddRange(new[] { new Color4(17, 136, 170, 255), @@ -21,8 +19,6 @@ namespace osu.Game.Skinning new Color4(204, 102, 0, 255), new Color4(121, 9, 13, 255) }); - - CursorExpand = true; } } } diff --git a/osu.Game/Skinning/GameplaySkinComponent.cs b/osu.Game/Skinning/GameplaySkinComponent.cs index 8695b3d720..2aa380fa90 100644 --- a/osu.Game/Skinning/GameplaySkinComponent.cs +++ b/osu.Game/Skinning/GameplaySkinComponent.cs @@ -5,7 +5,7 @@ using System.Linq; namespace osu.Game.Skinning { - public class GameplaySkinComponent : ISkinComponent where T : struct + public class GameplaySkinComponent : ISkinComponent { public readonly T Component; diff --git a/osu.Game/Skinning/GlobalSkinColour.cs b/osu.Game/Skinning/GlobalSkinColour.cs new file mode 100644 index 0000000000..d039be98ce --- /dev/null +++ b/osu.Game/Skinning/GlobalSkinColour.cs @@ -0,0 +1,10 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Skinning +{ + public enum GlobalSkinColour + { + MenuGlow + } +} diff --git a/osu.Game/Skinning/GlobalSkinConfiguration.cs b/osu.Game/Skinning/GlobalSkinConfiguration.cs new file mode 100644 index 0000000000..66dc9a9395 --- /dev/null +++ b/osu.Game/Skinning/GlobalSkinConfiguration.cs @@ -0,0 +1,10 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Skinning +{ + public enum GlobalSkinConfiguration + { + ComboColours + } +} diff --git a/osu.Game/Skinning/ISkin.cs b/osu.Game/Skinning/ISkin.cs index bc1ae634c9..841ff3d357 100644 --- a/osu.Game/Skinning/ISkin.cs +++ b/osu.Game/Skinning/ISkin.cs @@ -1,8 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Game.Audio; @@ -20,6 +20,6 @@ namespace osu.Game.Skinning SampleChannel GetSample(ISampleInfo sampleInfo); - TValue GetValue(Func query) where TConfiguration : SkinConfiguration; + IBindable GetConfig(TLookup lookup); } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 535471f455..53f7c54003 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -1,10 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.IO; using System.Linq; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; @@ -48,6 +50,47 @@ namespace osu.Game.Skinning Samples?.Dispose(); } + public override IBindable GetConfig(TLookup lookup) + { + switch (lookup) + { + case GlobalSkinConfiguration global: + switch (global) + { + case GlobalSkinConfiguration.ComboColours: + return new Bindable>(Configuration.ComboColours) as IBindable; + } + + break; + + case GlobalSkinColour colour: + return getCustomColour(colour.ToString()) as IBindable; + + case SkinCustomColourLookup customColour: + return getCustomColour(customColour.Lookup.ToString()) as IBindable; + + default: + try + { + if (Configuration.ConfigDictionary.TryGetValue(lookup.ToString(), out var val)) + { + var bindable = new Bindable(); + bindable.Parse(val); + return bindable; + } + } + catch + { + } + + break; + } + + return null; + } + + private IBindable getCustomColour(string lookup) => Configuration.CustomColours.TryGetValue(lookup, out var col) ? new Bindable(col) : null; + public override Drawable GetDrawableComponent(ISkinComponent component) { switch (component) diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs index 0160755eed..1912c4cd05 100644 --- a/osu.Game/Skinning/LegacySkinDecoder.cs +++ b/osu.Game/Skinning/LegacySkinDecoder.cs @@ -14,47 +14,31 @@ namespace osu.Game.Skinning protected override void ParseLine(DefaultSkinConfiguration skin, Section section, string line) { - line = StripComments(line); - - var pair = SplitKeyVal(line); - - switch (section) + if (section != Section.Colours) { - case Section.General: - switch (pair.Key) - { - case @"Name": - skin.SkinInfo.Name = pair.Value; - break; + line = StripComments(line); - case @"Author": - skin.SkinInfo.Creator = pair.Value; - break; + var pair = SplitKeyVal(line); - case @"CursorExpand": - skin.CursorExpand = pair.Value != "0"; - break; + switch (section) + { + case Section.General: + switch (pair.Key) + { + case @"Name": + skin.SkinInfo.Name = pair.Value; + return; - case @"SliderBorderSize": - skin.SliderBorderSize = Parsing.ParseFloat(pair.Value); - break; - } + case @"Author": + skin.SkinInfo.Creator = pair.Value; + return; + } - break; + break; + } - case Section.Fonts: - switch (pair.Key) - { - case "HitCirclePrefix": - skin.HitCircleFont = pair.Value; - break; - - case "HitCircleOverlap": - skin.HitCircleOverlap = int.Parse(pair.Value); - break; - } - - break; + if (!string.IsNullOrEmpty(pair.Key)) + skin.ConfigDictionary[$"{section}/{pair.Key}"] = pair.Value; } base.ParseLine(skin, section, line); diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 299f257e57..fa4aebd8a5 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -1,8 +1,9 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Game.Audio; @@ -13,7 +14,7 @@ namespace osu.Game.Skinning { public readonly SkinInfo SkinInfo; - public virtual SkinConfiguration Configuration { get; protected set; } + public SkinConfiguration Configuration { get; protected set; } public abstract Drawable GetDrawableComponent(ISkinComponent componentName); @@ -21,8 +22,7 @@ namespace osu.Game.Skinning public abstract Texture GetTexture(string componentName); - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration - => Configuration is TConfiguration conf ? query.Invoke(conf) : default; + public abstract IBindable GetConfig(TLookup lookup); protected Skin(SkinInfo skin) { diff --git a/osu.Game/Skinning/SkinConfigManager.cs b/osu.Game/Skinning/SkinConfigManager.cs new file mode 100644 index 0000000000..896444d1d2 --- /dev/null +++ b/osu.Game/Skinning/SkinConfigManager.cs @@ -0,0 +1,16 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Configuration; + +namespace osu.Game.Skinning +{ + public class SkinConfigManager : ConfigManager where T : struct + { + protected override void PerformLoad() + { + } + + protected override bool PerformSave() => false; + } +} diff --git a/osu.Game/Skinning/SkinConfiguration.cs b/osu.Game/Skinning/SkinConfiguration.cs index d585c58ef1..54aac86e3c 100644 --- a/osu.Game/Skinning/SkinConfiguration.cs +++ b/osu.Game/Skinning/SkinConfiguration.cs @@ -18,14 +18,6 @@ namespace osu.Game.Skinning public Dictionary CustomColours { get; set; } = new Dictionary(); - public string HitCircleFont { get; set; } - - public int HitCircleOverlap { get; set; } - - public float? SliderBorderSize { get; set; } - - public float? SliderPathRadius { get; set; } - - public bool? CursorExpand { get; set; } + public readonly Dictionary ConfigDictionary = new Dictionary(); } } diff --git a/osu.Game/Skinning/SkinCustomColourLookup.cs b/osu.Game/Skinning/SkinCustomColourLookup.cs new file mode 100644 index 0000000000..b8e5ac9b53 --- /dev/null +++ b/osu.Game/Skinning/SkinCustomColourLookup.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Skinning +{ + public class SkinCustomColourLookup + { + public readonly object Lookup; + + public SkinCustomColourLookup(object lookup) + { + Lookup = lookup; + } + } +} diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index a55a128dff..aa3b3981c2 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -131,6 +131,6 @@ namespace osu.Game.Skinning public SampleChannel GetSample(ISampleInfo sampleInfo) => CurrentSkin.Value.GetSample(sampleInfo); - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => CurrentSkin.Value.GetValue(query); + public IBindable GetConfig(TLookup lookup) => CurrentSkin.Value.GetConfig(lookup); } } diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index 85a80655ea..ef7f5f381b 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; @@ -64,13 +65,16 @@ namespace osu.Game.Skinning return fallbackSource?.GetSample(sampleInfo); } - public TValue GetValue(Func query) where TConfiguration : SkinConfiguration + public IBindable GetConfig(TLookup lookup) { - TValue val; - if (AllowConfigurationLookup && skin != null && (val = skin.GetValue(query)) != null) - return val; + if (AllowConfigurationLookup && skin != null) + { + var bindable = skin.GetConfig(lookup); + if (bindable != null) + return bindable; + } - return fallbackSource == null ? default : fallbackSource.GetValue(query); + return fallbackSource?.GetConfig(lookup); } protected virtual void TriggerSourceChanged() => SourceChanged?.Invoke(); From 097012dc95c05e829864d451b35be143844cdc7e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 18:05:16 +0900 Subject: [PATCH 13/64] Move slider ball colouring to DefaultLegacySkin for now --- osu.Game/Skinning/DefaultLegacySkin.cs | 2 ++ osu.Game/Skinning/LegacySkin.cs | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index b35c9c7b97..98f158c725 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -3,6 +3,7 @@ using osu.Framework.Audio; using osu.Framework.IO.Stores; +using osuTK.Graphics; namespace osu.Game.Skinning { @@ -11,6 +12,7 @@ namespace osu.Game.Skinning public DefaultLegacySkin(IResourceStore storage, AudioManager audioManager) : base(Info, storage, audioManager, string.Empty) { + Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255); } public static SkinInfo Info { get; } = new SkinInfo diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 53f7c54003..5f0afae075 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -25,8 +25,6 @@ namespace osu.Game.Skinning public LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager) : this(skin, new LegacySkinResourceStore(skin, storage), audioManager, "skin.ini") { - // defaults should only be applied for non-beatmap skins (which are parsed via this constructor). - if (!Configuration.CustomColours.ContainsKey("SliderBall")) Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255); } protected LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager, string filename) From 2f74ef513140bfcd0e8b69c16a4d0731f87b5790 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 18:21:57 +0900 Subject: [PATCH 14/64] Add test for changing of a source --- .../Gameplay/TestSceneSkinnableDrawable.cs | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs index ee5552c6e0..80015099cf 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs @@ -5,6 +5,7 @@ using System; using System.Globalization; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -133,6 +134,48 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("skinchanged only called once", () => consumer.SkinChangedCount == 1); } + [Test] + public void TestSwitchOff() + { + SkinConsumer consumer = null; + SwitchableSkinProvidingContainer target = null; + + AddStep("setup layout", () => + { + Child = new SkinSourceContainer + { + RelativeSizeAxes = Axes.Both, + Child = target = new SwitchableSkinProvidingContainer(new SecondarySource()) + { + RelativeSizeAxes = Axes.Both, + } + }; + }); + + AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true))); + AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox); + AddStep("disable", () => target.Disable()); + AddAssert("consumer using base source", () => consumer.Drawable is BaseSourceBox); + } + + private class SwitchableSkinProvidingContainer : SkinProvidingContainer + { + private bool allow = true; + + protected override bool AllowDrawableLookup(ISkinComponent component) => allow; + + public void Disable() + { + allow = false; + TriggerSourceChanged(); + } + + public SwitchableSkinProvidingContainer(ISkin skin) + : base(skin) + { + } + } + private class ExposedSkinnableDrawable : SkinnableDrawable { public new Drawable Drawable => base.Drawable; @@ -270,7 +313,8 @@ namespace osu.Game.Tests.Visual.Gameplay public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); } - private class SkinSourceContainer : Container, ISkin + [Cached(typeof(ISkinSource))] + private class SkinSourceContainer : Container, ISkinSource { public Drawable GetDrawableComponent(ISkinComponent componentName) => new BaseSourceBox(); @@ -279,6 +323,8 @@ namespace osu.Game.Tests.Visual.Gameplay public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); + + public event Action SourceChanged; } private class TestSkinComponent : ISkinComponent From 002de80c30b2f65eb842e8089382ac74cf2e1964 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 18:30:22 +0900 Subject: [PATCH 15/64] Add xmldoc to ISkin --- osu.Game/Skinning/ISkin.cs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game/Skinning/ISkin.cs b/osu.Game/Skinning/ISkin.cs index 841ff3d357..cb2a379b8e 100644 --- a/osu.Game/Skinning/ISkin.cs +++ b/osu.Game/Skinning/ISkin.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using JetBrains.Annotations; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -14,12 +15,36 @@ namespace osu.Game.Skinning /// public interface ISkin { + /// + /// Retrieve a component implementation. + /// + /// The requested component. + /// A drawable representation for the requested component, or null if unavailable. + [CanBeNull] Drawable GetDrawableComponent(ISkinComponent component); + /// + /// Retrieve a . + /// + /// The requested texture. + /// A matching texture, or null if unavailable. + [CanBeNull] Texture GetTexture(string componentName); + /// + /// Retrieve a . + /// + /// The requested sample. + /// A matching sample channel, or null if unavailable. + [CanBeNull] SampleChannel GetSample(ISampleInfo sampleInfo); + /// + /// Retrieve a configuration value. + /// + /// The requested configuration value. + /// A matching value boxed in an , or null if unavailable. + [CanBeNull] IBindable GetConfig(TLookup lookup); } } From f58ca823986f79491946fd4fe3394ec9fbd4afa1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 18:56:01 +0900 Subject: [PATCH 16/64] Don't include section for now --- osu.Game/Skinning/LegacySkinDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs index 1912c4cd05..e97664e75e 100644 --- a/osu.Game/Skinning/LegacySkinDecoder.cs +++ b/osu.Game/Skinning/LegacySkinDecoder.cs @@ -38,7 +38,7 @@ namespace osu.Game.Skinning } if (!string.IsNullOrEmpty(pair.Key)) - skin.ConfigDictionary[$"{section}/{pair.Key}"] = pair.Value; + skin.ConfigDictionary[pair.Key] = pair.Value; } base.ParseLine(skin, section, line); From 343af28ed578cc247ba842c9983d72e4dce138a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 18:59:11 +0900 Subject: [PATCH 17/64] Add extra legacy skin parsing tests --- osu.Game.Tests/Resources/skin.ini | 1 + osu.Game.Tests/Skins/LegacySkinDecoderTest.cs | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/osu.Game.Tests/Resources/skin.ini b/osu.Game.Tests/Resources/skin.ini index 0e5737b4ea..7f7f0b32a6 100644 --- a/osu.Game.Tests/Resources/skin.ini +++ b/osu.Game.Tests/Resources/skin.ini @@ -1,5 +1,6 @@ [General] Name: test skin +TestLookup: TestValue [Colours] Combo1 : 142,199,255 diff --git a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs index 24ef9e4535..8bd846518b 100644 --- a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs +++ b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs @@ -41,5 +41,20 @@ namespace osu.Game.Tests.Skins Assert.AreEqual(expectedColors[i], comboColors[i]); } } + + [Test] + public void TestDecodeGeneral() + { + var decoder = new LegacySkinDecoder(); + + using (var resStream = TestResources.OpenResource("skin.ini")) + using (var stream = new StreamReader(resStream)) + { + var config = decoder.Decode(stream); + + Assert.AreEqual("test skin", config.SkinInfo.Name); + Assert.AreEqual("TestValue", config.ConfigDictionary["TestLookup"]); + } + } } } From 4b2cb8854e06735d859568ba1b22e4ceb3a71541 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2019 19:28:10 +0900 Subject: [PATCH 18/64] Fix storyboard samples not stopping on exit --- osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs index b04f1d4518..f3f8308964 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs @@ -64,5 +64,11 @@ namespace osu.Game.Storyboards.Drawables LifetimeEnd = sampleInfo.StartTime; } } + + protected override void Dispose(bool isDisposing) + { + channel?.Stop(); + base.Dispose(isDisposing); + } } } From a8f16503e2faa8dcfe98d1f28297e32149f2cd01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 3 Sep 2019 23:18:39 +0200 Subject: [PATCH 19/64] Add backslash escaping to new link format For users to be able to add square brackets inside of links using the new format, the regular expression used for parsing those links contained a balancing group, which can be used for matching pairs of tokens (in this case, opening and closing brackets, in that order). However, this means that users could not post links with unmatched brackets inside of them (ie. ones that contain single brackets, or a closing bracket and then an opening one). Allow for escaping opening and closing brackets using the backslash character. The change substitutes this old fragment of the regex in the display text group: [^\[\]]* // any character other than closing/opening bracket for this one: (((?<=\\)[\[\]])|[^\[\]])* The second pattern in the alternative remains the same; the first one performs the escaping, as follows: ( (?<=\\) // positive lookbehind expression: // this match will succeed, if the next expression // is preceded by a single backslash [\[\]] // either an opening or closing brace ) Since the entire display group is matched, unfortunately the lookbehind expression does not actually strip the backslashes, so they are manually stripped in handleMatches. As demonstrated in the unit tests attached, this also allows balanced brackets to be mixed with escaped ones. --- osu.Game.Tests/Chat/MessageFormatterTests.cs | 36 +++++++++++++++++++ .../Visual/Online/TestSceneChatLink.cs | 1 + osu.Game/Online/Chat/MessageFormatter.cs | 11 ++++-- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs index 0d6ed67767..1de6280531 100644 --- a/osu.Game.Tests/Chat/MessageFormatterTests.cs +++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs @@ -131,6 +131,42 @@ namespace osu.Game.Tests.Chat Assert.AreEqual(11, result.Links[0].Length); } + [Test] + public void TestNewFormatLinkWithEscapedBrackets() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [https://osu.ppy.sh nasty link with escaped brackets: \\] and \\[]" }); + + Assert.AreEqual("This is a nasty link with escaped brackets: ] and [", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(41, result.Links[0].Length); + } + + [Test] + public void TestNewFormatLinkWithBackslashesInside() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [https://osu.ppy.sh link \\ with \\ backslashes \\]" }); + + Assert.AreEqual("This is a link \\ with \\ backslashes \\", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(27, result.Links[0].Length); + } + + [Test] + public void TestNewFormatLinkWithEscapedAndBalancedBrackets() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [https://osu.ppy.sh [link [with \\] too many brackets \\[ ]]]" }); + + Assert.AreEqual("This is a [link [with ] too many brackets [ ]]", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(36, result.Links[0].Length); + } + [Test] public void TestMarkdownFormatLink() { diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index c18e0e3064..056ccafe79 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -127,6 +127,7 @@ namespace osu.Game.Tests.Visual.Online addMessageWithChecks("is now playing [https://osu.ppy.sh/b/252238 IMAGE -MATERIAL- ]", 1, true, expectedActions: LinkAction.OpenBeatmap); addMessageWithChecks("Let's (try)[https://osu.ppy.sh/home] [https://osu.ppy.sh/b/252238 multiple links] https://osu.ppy.sh/home", 3, expectedActions: new[] { LinkAction.External, LinkAction.OpenBeatmap, LinkAction.External }); + addMessageWithChecks("[https://osu.ppy.sh/home New link format with escaped [and \\[ paired] braces]", expectedActions: LinkAction.External); // note that there's 0 links here (they get removed if a channel is not found) addMessageWithChecks("#lobby or #osu would be blue (and work) in the ChatDisplay test (when a proper ChatOverlay is present)."); addMessageWithChecks("I am important!", 0, false, true); diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index db26945ef3..a9fffc196c 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text.RegularExpressions; namespace osu.Game.Online.Chat @@ -16,7 +17,7 @@ namespace osu.Game.Online.Chat private static readonly Regex old_link_regex = new Regex(@"\(([^\)]*)\)\[([a-z]+://[^ ]+)\]"); // [https://osu.ppy.sh/b/1234 Beatmap [Hard] (poop)] -> Beatmap [hard] (poop) (https://osu.ppy.sh/b/1234) - private static readonly Regex new_link_regex = new Regex(@"\[([a-z]+://[^ ]+) ([^\[\]]*(((?\[)[^\[\]]*)+((?\])[^\[\]]*)+)*(?(open)(?!)))\]"); + private static readonly Regex new_link_regex = new Regex(@"\[([a-z]+://[^ ]+) ((((?<=\\)[\[\]])|[^\[\]])*(((?\[)(((?<=\\)[\[\]])|[^\[\]])*)+((?\])(((?<=\\)[\[\]])|[^\[\]])*)+)*(?(open)(?!)))\]"); // [test](https://osu.ppy.sh/b/1234) -> test (https://osu.ppy.sh/b/1234) aka correct markdown format private static readonly Regex markdown_link_regex = new Regex(@"\[([^\]]*)\]\(([a-z]+://[^ ]+)\)"); @@ -48,7 +49,7 @@ namespace osu.Game.Online.Chat // Unicode emojis private static readonly Regex emoji_regex = new Regex(@"(\uD83D[\uDC00-\uDE4F])"); - private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0, LinkAction? linkActionOverride = null) + private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0, LinkAction? linkActionOverride = null, char[] escapeChars = null) { int captureOffset = 0; @@ -68,6 +69,10 @@ namespace osu.Game.Online.Chat if (displayText.Length == 0 || linkText.Length == 0) continue; + // Remove backslash escapes in front of the characters provided in escapeChars + if (escapeChars != null) + displayText = escapeChars.Aggregate(displayText, (current, c) => current.Replace($"\\{c}", c.ToString())); + // Check for encapsulated links if (result.Links.Find(l => (l.Index <= index && l.Index + l.Length >= index + m.Length) || (index <= l.Index && index + m.Length >= l.Index + l.Length)) == null) { @@ -183,7 +188,7 @@ namespace osu.Game.Online.Chat var result = new MessageFormatterResult(toFormat); // handle the [link display] format - handleMatches(new_link_regex, "{2}", "{1}", result, startIndex); + handleMatches(new_link_regex, "{2}", "{1}", result, startIndex, escapeChars: new[] { '[', ']' }); // handle the standard markdown []() format handleMatches(markdown_link_regex, "{1}", "{2}", result, startIndex); From 24d4f0372c851280eb017553101712ad86731133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 3 Sep 2019 23:56:07 +0200 Subject: [PATCH 20/64] Refactor link parsing regexes to use named groups For the sake of readability, consistency and to make further changes easier, introduce named groups (?) and (?) to all link parsing regexes which have parts containing the desired link text and (optionally) URL. The introduction of the named groups additionally simplifies handleMatches() and makes all calls to it consistent. --- osu.Game/Online/Chat/MessageFormatter.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index a9fffc196c..23b5cdc0a6 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -11,16 +11,16 @@ namespace osu.Game.Online.Chat public static class MessageFormatter { // [[Performance Points]] -> wiki:Performance Points (https://osu.ppy.sh/wiki/Performance_Points) - private static readonly Regex wiki_regex = new Regex(@"\[\[([^\]]+)\]\]"); + private static readonly Regex wiki_regex = new Regex(@"\[\[(?[^\]]+)\]\]"); // (test)[https://osu.ppy.sh/b/1234] -> test (https://osu.ppy.sh/b/1234) - private static readonly Regex old_link_regex = new Regex(@"\(([^\)]*)\)\[([a-z]+://[^ ]+)\]"); + private static readonly Regex old_link_regex = new Regex(@"\((?[^\)]*)\)\[(?[a-z]+://[^ ]+)\]"); // [https://osu.ppy.sh/b/1234 Beatmap [Hard] (poop)] -> Beatmap [hard] (poop) (https://osu.ppy.sh/b/1234) - private static readonly Regex new_link_regex = new Regex(@"\[([a-z]+://[^ ]+) ((((?<=\\)[\[\]])|[^\[\]])*(((?\[)(((?<=\\)[\[\]])|[^\[\]])*)+((?\])(((?<=\\)[\[\]])|[^\[\]])*)+)*(?(open)(?!)))\]"); + private static readonly Regex new_link_regex = new Regex(@"\[(?[a-z]+://[^ ]+) (?(((?<=\\)[\[\]])|[^\[\]])*(((?\[)(((?<=\\)[\[\]])|[^\[\]])*)+((?\])(((?<=\\)[\[\]])|[^\[\]])*)+)*(?(open)(?!)))\]"); // [test](https://osu.ppy.sh/b/1234) -> test (https://osu.ppy.sh/b/1234) aka correct markdown format - private static readonly Regex markdown_link_regex = new Regex(@"\[([^\]]*)\]\(([a-z]+://[^ ]+)\)"); + private static readonly Regex markdown_link_regex = new Regex(@"\[(?[^\]]*)\]\((?[a-z]+://[^ ]+)\)"); // advanced, RFC-compatible regular expression that matches any possible URL, *but* allows certain invalid characters that are widely used // This is in the format (, [optional]): @@ -59,13 +59,13 @@ namespace osu.Game.Online.Chat var displayText = string.Format(display, m.Groups[0], - m.Groups.Count > 1 ? m.Groups[1].Value : "", - m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim(); + m.Groups["text"].Value, + m.Groups["url"].Value).Trim(); var linkText = string.Format(link, m.Groups[0], - m.Groups.Count > 1 ? m.Groups[1].Value : "", - m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim(); + m.Groups["text"].Value, + m.Groups["url"].Value).Trim(); if (displayText.Length == 0 || linkText.Length == 0) continue; @@ -188,7 +188,7 @@ namespace osu.Game.Online.Chat var result = new MessageFormatterResult(toFormat); // handle the [link display] format - handleMatches(new_link_regex, "{2}", "{1}", result, startIndex, escapeChars: new[] { '[', ']' }); + handleMatches(new_link_regex, "{1}", "{2}", result, startIndex, escapeChars: new[] { '[', ']' }); // handle the standard markdown []() format handleMatches(markdown_link_regex, "{1}", "{2}", result, startIndex); From f04add6d9edda7bbdf080adc7e6959ee16e6f222 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 4 Sep 2019 00:01:26 +0200 Subject: [PATCH 21/64] Add bracket handling to Markdown link format Allow users to put both balanced brackets, as well as unbalanced escaped ones, in Markdown link text. The implementation is the exact same as in the case of new format links. For completion's sake, tests also included. --- osu.Game.Tests/Chat/MessageFormatterTests.cs | 47 +++++++++++++++++++ .../Visual/Online/TestSceneChatLink.cs | 1 + osu.Game/Online/Chat/MessageFormatter.cs | 4 +- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs index 1de6280531..198267d78a 100644 --- a/osu.Game.Tests/Chat/MessageFormatterTests.cs +++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs @@ -179,6 +179,53 @@ namespace osu.Game.Tests.Chat Assert.AreEqual(11, result.Links[0].Length); } + [Test] + public void TestMarkdownFormatLinkWithBalancedBrackets() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [tricky [one]](https://osu.ppy.sh)!" }); + + Assert.AreEqual("This is a tricky [one]!", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(12, result.Links[0].Length); + } + + [Test] + public void TestMarkdownFormatLinkWithEscapedBrackets() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is [another loose bracket \\]](https://osu.ppy.sh)." }); + + Assert.AreEqual("This is another loose bracket ].", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(8, result.Links[0].Index); + Assert.AreEqual(23, result.Links[0].Length); + } + + [Test] + public void TestMarkdownFormatWithBackslashes() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This link [should end with a backslash \\](https://osu.ppy.sh)." }); + Assert.AreEqual("This link should end with a backslash \\.", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(29, result.Links[0].Length); + } + + [Test] + public void TestMarkdownFormatLinkWithEscapedAndBalancedBrackets() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [\\]super\\[\\[ tricky [one]](https://osu.ppy.sh)!" }); + + Assert.AreEqual("This is a ]super[[ tricky [one]!", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(21, result.Links[0].Length); + } + [Test] public void TestChannelLink() { diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index 056ccafe79..61c7d3f5b6 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -128,6 +128,7 @@ namespace osu.Game.Tests.Visual.Online addMessageWithChecks("Let's (try)[https://osu.ppy.sh/home] [https://osu.ppy.sh/b/252238 multiple links] https://osu.ppy.sh/home", 3, expectedActions: new[] { LinkAction.External, LinkAction.OpenBeatmap, LinkAction.External }); addMessageWithChecks("[https://osu.ppy.sh/home New link format with escaped [and \\[ paired] braces]", expectedActions: LinkAction.External); + addMessageWithChecks("[Markdown link format with escaped [and \\[ paired] braces](https://osu.ppy.sh/home)", expectedActions: LinkAction.External); // note that there's 0 links here (they get removed if a channel is not found) addMessageWithChecks("#lobby or #osu would be blue (and work) in the ChatDisplay test (when a proper ChatOverlay is present)."); addMessageWithChecks("I am important!", 0, false, true); diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 23b5cdc0a6..e40bb05381 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -20,7 +20,7 @@ namespace osu.Game.Online.Chat private static readonly Regex new_link_regex = new Regex(@"\[(?[a-z]+://[^ ]+) (?(((?<=\\)[\[\]])|[^\[\]])*(((?\[)(((?<=\\)[\[\]])|[^\[\]])*)+((?\])(((?<=\\)[\[\]])|[^\[\]])*)+)*(?(open)(?!)))\]"); // [test](https://osu.ppy.sh/b/1234) -> test (https://osu.ppy.sh/b/1234) aka correct markdown format - private static readonly Regex markdown_link_regex = new Regex(@"\[(?[^\]]*)\]\((?[a-z]+://[^ ]+)\)"); + private static readonly Regex markdown_link_regex = new Regex(@"\[(?(((?<=\\)[\[\]])|[^\[\]])*(((?\[)(((?<=\\)[\[\]])|[^\[\]])*)+((?\])(((?<=\\)[\[\]])|[^\[\]])*)+)*(?(open)(?!)))\]\((?[a-z]+://[^ ]+)\)"); // advanced, RFC-compatible regular expression that matches any possible URL, *but* allows certain invalid characters that are widely used // This is in the format (, [optional]): @@ -191,7 +191,7 @@ namespace osu.Game.Online.Chat handleMatches(new_link_regex, "{1}", "{2}", result, startIndex, escapeChars: new[] { '[', ']' }); // handle the standard markdown []() format - handleMatches(markdown_link_regex, "{1}", "{2}", result, startIndex); + handleMatches(markdown_link_regex, "{1}", "{2}", result, startIndex, escapeChars: new[] { '[', ']' }); // handle the ()[] link format handleMatches(old_link_regex, "{1}", "{2}", result, startIndex); From 08350a1acaf4c3bb70176e384c845fc83735d497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 4 Sep 2019 00:17:52 +0200 Subject: [PATCH 22/64] Add parenthesis handling to old link format Allow users to put both balanced round parentheses, as well as unbalanced escaped ones, in old style link text. The implementation is the same as for Markdown and new style links, except for swapping all instances of \[\] to \(\) for obvious reasons (different type of parenthesis requiring escaping). Tests also included. --- osu.Game.Tests/Chat/MessageFormatterTests.cs | 47 +++++++++++++++++++ .../Visual/Online/TestSceneChatLink.cs | 5 +- osu.Game/Online/Chat/MessageFormatter.cs | 4 +- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs index 198267d78a..9b4a90e9a9 100644 --- a/osu.Game.Tests/Chat/MessageFormatterTests.cs +++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs @@ -119,6 +119,53 @@ namespace osu.Game.Tests.Chat Assert.AreEqual(11, result.Links[0].Length); } + [Test] + public void TestOldFormatLinkWithBalancedBrackets() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a (tricky (one))[https://osu.ppy.sh]!" }); + + Assert.AreEqual("This is a tricky (one)!", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(12, result.Links[0].Length); + } + + [Test] + public void TestOldFormatLinkWithEscapedBrackets() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is (another loose bracket \\))[https://osu.ppy.sh]." }); + + Assert.AreEqual("This is another loose bracket ).", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(8, result.Links[0].Index); + Assert.AreEqual(23, result.Links[0].Length); + } + + [Test] + public void TestOldFormatWithBackslashes() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This link (should end with a backslash \\)[https://osu.ppy.sh]." }); + Assert.AreEqual("This link should end with a backslash \\.", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(29, result.Links[0].Length); + } + + [Test] + public void TestOldFormatLinkWithEscapedAndBalancedBrackets() + { + Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a (\\)super\\(\\( tricky (one))[https://osu.ppy.sh]!" }); + + Assert.AreEqual("This is a )super(( tricky (one)!", result.DisplayContent); + Assert.AreEqual(1, result.Links.Count); + Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url); + Assert.AreEqual(10, result.Links[0].Index); + Assert.AreEqual(21, result.Links[0].Length); + } + [Test] public void TestNewFormatLink() { diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index 61c7d3f5b6..a1c77e2db0 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -127,8 +127,9 @@ namespace osu.Game.Tests.Visual.Online addMessageWithChecks("is now playing [https://osu.ppy.sh/b/252238 IMAGE -MATERIAL- ]", 1, true, expectedActions: LinkAction.OpenBeatmap); addMessageWithChecks("Let's (try)[https://osu.ppy.sh/home] [https://osu.ppy.sh/b/252238 multiple links] https://osu.ppy.sh/home", 3, expectedActions: new[] { LinkAction.External, LinkAction.OpenBeatmap, LinkAction.External }); - addMessageWithChecks("[https://osu.ppy.sh/home New link format with escaped [and \\[ paired] braces]", expectedActions: LinkAction.External); - addMessageWithChecks("[Markdown link format with escaped [and \\[ paired] braces](https://osu.ppy.sh/home)", expectedActions: LinkAction.External); + addMessageWithChecks("[https://osu.ppy.sh/home New link format with escaped [and \\[ paired] braces]", 1, expectedActions: LinkAction.External); + addMessageWithChecks("[Markdown link format with escaped [and \\[ paired] braces](https://osu.ppy.sh/home)", 1, expectedActions: LinkAction.External); + addMessageWithChecks("(Old link format with escaped (and \\( paired) parentheses)[https://osu.ppy.sh/home] and [[also a rogue wiki link]]", 2, expectedActions: new[] { LinkAction.External, LinkAction.External }); // note that there's 0 links here (they get removed if a channel is not found) addMessageWithChecks("#lobby or #osu would be blue (and work) in the ChatDisplay test (when a proper ChatOverlay is present)."); addMessageWithChecks("I am important!", 0, false, true); diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index e40bb05381..24d17612ee 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -14,7 +14,7 @@ namespace osu.Game.Online.Chat private static readonly Regex wiki_regex = new Regex(@"\[\[(?[^\]]+)\]\]"); // (test)[https://osu.ppy.sh/b/1234] -> test (https://osu.ppy.sh/b/1234) - private static readonly Regex old_link_regex = new Regex(@"\((?[^\)]*)\)\[(?[a-z]+://[^ ]+)\]"); + private static readonly Regex old_link_regex = new Regex(@"\((?(((?<=\\)[\(\)])|[^\(\)])*(((?\()(((?<=\\)[\(\)])|[^\(\)])*)+((?\))(((?<=\\)[\(\)])|[^\(\)])*)+)*(?(open)(?!)))\)\[(?[a-z]+://[^ ]+)\]"); // [https://osu.ppy.sh/b/1234 Beatmap [Hard] (poop)] -> Beatmap [hard] (poop) (https://osu.ppy.sh/b/1234) private static readonly Regex new_link_regex = new Regex(@"\[(?[a-z]+://[^ ]+) (?(((?<=\\)[\[\]])|[^\[\]])*(((?\[)(((?<=\\)[\[\]])|[^\[\]])*)+((?\])(((?<=\\)[\[\]])|[^\[\]])*)+)*(?(open)(?!)))\]"); @@ -194,7 +194,7 @@ namespace osu.Game.Online.Chat handleMatches(markdown_link_regex, "{1}", "{2}", result, startIndex, escapeChars: new[] { '[', ']' }); // handle the ()[] link format - handleMatches(old_link_regex, "{1}", "{2}", result, startIndex); + handleMatches(old_link_regex, "{1}", "{2}", result, startIndex, escapeChars: new[] { '(', ')' }); // handle wiki links handleMatches(wiki_regex, "{1}", "https://osu.ppy.sh/wiki/{1}", result, startIndex); From 04c2c33c64bbc908324510672c822681bf40667b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 13:29:55 +0900 Subject: [PATCH 23/64] Allow LegacySkin to be constructed with all nulls --- osu.Game/Skinning/LegacySkin.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 5f0afae075..7bdb980eaf 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -30,14 +30,14 @@ namespace osu.Game.Skinning protected LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager, string filename) : base(skin) { - Stream stream = storage.GetStream(filename); + Stream stream = storage?.GetStream(filename); if (stream != null) using (StreamReader reader = new StreamReader(stream)) Configuration = new LegacySkinDecoder().Decode(reader); else Configuration = new DefaultSkinConfiguration(); - Samples = audioManager.GetSampleStore(storage); + Samples = audioManager?.GetSampleStore(storage); Textures = new TextureStore(new TextureLoaderStore(storage)); } @@ -72,6 +72,10 @@ namespace osu.Game.Skinning { if (Configuration.ConfigDictionary.TryGetValue(lookup.ToString(), out var val)) { + // special case for handling skins which use 1 or 0 to signify a boolean state. + if (typeof(TValue) == typeof(bool)) + val = val == "1" ? "true" : "false"; + var bindable = new Bindable(); bindable.Parse(val); return bindable; From f655cd451681e8fc64e0f6c8faebcdc1c0ab08ea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 13:30:46 +0900 Subject: [PATCH 24/64] Fix parsing of null configuration elements --- osu.Game/Skinning/LegacySkin.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 7bdb980eaf..94e2a49908 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -77,7 +77,8 @@ namespace osu.Game.Skinning val = val == "1" ? "true" : "false"; var bindable = new Bindable(); - bindable.Parse(val); + if (val != null) + bindable.Parse(val); return bindable; } } From fb3d050209bb8b72c02435d5225b8f7318afd98f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 13:36:50 +0900 Subject: [PATCH 25/64] Add comprehensive configuration lookup tests --- .../Skins/SkinConfigurationLookupTest.cs | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 osu.Game.Tests/Skins/SkinConfigurationLookupTest.cs diff --git a/osu.Game.Tests/Skins/SkinConfigurationLookupTest.cs b/osu.Game.Tests/Skins/SkinConfigurationLookupTest.cs new file mode 100644 index 0000000000..1344d20d9f --- /dev/null +++ b/osu.Game.Tests/Skins/SkinConfigurationLookupTest.cs @@ -0,0 +1,137 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Textures; +using osu.Game.Audio; +using osu.Game.Skinning; +using osu.Game.Tests.Visual; +using osuTK.Graphics; + +namespace osu.Game.Tests.Skins +{ + [TestFixture] + public class TestSceneSkinConfigurationLookup : OsuTestScene + { + private LegacySkin source1; + private LegacySkin source2; + private SkinRequester requester; + + [SetUp] + public void SetUp() => Schedule(() => + { + Add(new SkinProvidingContainer(source1 = new SkinSource()) + .WithChild(new SkinProvidingContainer(source2 = new SkinSource()) + .WithChild(requester = new SkinRequester()))); + }); + + [Test] + public void TestBasicLookup() + { + AddStep("Add config values", () => + { + source1.Configuration.ConfigDictionary["Lookup"] = "source1"; + source2.Configuration.ConfigDictionary["Lookup"] = "source2"; + }); + + AddAssert("Check lookup finds source2", () => requester.GetConfig("Lookup")?.Value == "source2"); + } + + [Test] + public void TestParsingLookup() + { + AddStep("Add config values", () => + { + source1.Configuration.ConfigDictionary["FloatTest"] = "1.1"; + source2.Configuration.ConfigDictionary["BoolTest"] = "1"; + }); + + AddAssert("Check float parse lookup", () => requester.GetConfig("FloatTest")?.Value == 1.1f); + AddAssert("Check bool parse lookup", () => requester.GetConfig("BoolTest")?.Value == true); + } + + [Test] + public void TestEnumLookup() + { + AddStep("Add config values", () => { source1.Configuration.ConfigDictionary["Test"] = "Test2"; }); + + AddAssert("Check float parse lookup", () => requester.GetConfig(LookupType.Test)?.Value == ValueType.Test2); + } + + [Test] + public void TestLookupFailure() + { + AddAssert("Check lookup failure", () => requester.GetConfig("Lookup") == null); + } + + [Test] + public void TestLookupNull() + { + AddStep("Add config values", () => { source1.Configuration.ConfigDictionary["Lookup"] = null; }); + + AddAssert("Check lookup null", () => + { + var bindable = requester.GetConfig("Lookup"); + return bindable != null && bindable.Value == null; + }); + } + + [Test] + public void TestColourLookup() + { + AddStep("Add config colour", () => { source1.Configuration.CustomColours["Lookup"] = Color4.Red; }); + AddAssert("Check colour lookup", () => requester.GetConfig(new SkinCustomColourLookup("Lookup"))?.Value == Color4.Red); + } + + [Test] + public void TestGlobalLookup() + { + AddAssert("Check combo colours", () => requester.GetConfig>(GlobalSkinConfiguration.ComboColours)?.Value?.Count > 0); + } + + public enum LookupType + { + Test + } + + public enum ValueType + { + Test1, + Test2, + Test3 + } + + public class SkinSource : LegacySkin + { + public SkinSource() + : base(new SkinInfo(), null, null, string.Empty) + { + } + } + + public class SkinRequester : Drawable, ISkin + { + private ISkinSource skin; + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + this.skin = skin; + } + + public Drawable GetDrawableComponent(ISkinComponent component) => skin.GetDrawableComponent(component); + + public Texture GetTexture(string componentName) => skin.GetTexture(componentName); + + public SampleChannel GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo); + + public IBindable GetConfig(TLookup lookup) => skin.GetConfig(lookup); + } + } +} From 8d48cc3533ef470fba9045e8206763caac0a7820 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 13:40:36 +0900 Subject: [PATCH 26/64] Fix filename --- ...igurationLookupTest.cs => TestSceneSkinConfigurationLookup.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename osu.Game.Tests/Skins/{SkinConfigurationLookupTest.cs => TestSceneSkinConfigurationLookup.cs} (100%) diff --git a/osu.Game.Tests/Skins/SkinConfigurationLookupTest.cs b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs similarity index 100% rename from osu.Game.Tests/Skins/SkinConfigurationLookupTest.cs rename to osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs From 69b9d70a35c2c1f42214d16f083b6e73f43ddc4c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 15:56:18 +0900 Subject: [PATCH 27/64] Add minimal configuration to support nuget package creation --- osu.Game/osu.Game.csproj | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7d106f0484..3147ca749f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -6,6 +6,17 @@ AnyCPU true + + osu! + ppy.osu.Game + ppy Pty Ltd + https://github.com/ppy/osu/blob/master/LICENCE.md + https://github.com/ppy/osu + https://github.com/ppy/osu + Automated release. + Copyright (c) 2019 ppy Pty Ltd + osu game + @@ -15,7 +26,7 @@ - + From 8ea82123e4afc36ac897735d24743a17b259d25b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 15:59:09 +0900 Subject: [PATCH 28/64] Fix nullref on test disposal --- osu.Game/Skinning/LegacySkin.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 94e2a49908..cd2ad2d61c 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; @@ -18,8 +19,10 @@ namespace osu.Game.Skinning { public class LegacySkin : Skin { + [CanBeNull] protected TextureStore Textures; + [CanBeNull] protected IResourceStore Samples; public LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager) @@ -37,8 +40,11 @@ namespace osu.Game.Skinning else Configuration = new DefaultSkinConfiguration(); - Samples = audioManager?.GetSampleStore(storage); - Textures = new TextureStore(new TextureLoaderStore(storage)); + if (storage != null) + { + Samples = audioManager?.GetSampleStore(storage); + Textures = new TextureStore(new TextureLoaderStore(storage)); + } } protected override void Dispose(bool isDisposing) @@ -125,12 +131,12 @@ namespace osu.Game.Skinning componentName = getFallbackName(componentName); float ratio = 2; - var texture = Textures.Get($"{componentName}@2x"); + var texture = Textures?.Get($"{componentName}@2x"); if (texture == null) { ratio = 1; - texture = Textures.Get(componentName); + texture = Textures?.Get(componentName); } if (texture != null) @@ -143,7 +149,7 @@ namespace osu.Game.Skinning { foreach (var lookup in sampleInfo.LookupNames) { - var sample = Samples.Get(getFallbackName(lookup)); + var sample = Samples?.Get(getFallbackName(lookup)); if (sample != null) return sample; @@ -151,7 +157,7 @@ namespace osu.Game.Skinning if (sampleInfo is HitSampleInfo hsi) // Try fallback to non-bank samples. - return Samples.Get(hsi.Name); + return Samples?.Get(hsi.Name); return null; } From 07f662071d5c7dc31424e692fb9e438587cff662 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 4 Sep 2019 18:14:55 +0900 Subject: [PATCH 29/64] Remove judgementOccurred --- .../Objects/Drawables/DrawableHitObject.cs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 1a224b2cea..f7efa625a5 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -76,8 +76,6 @@ namespace osu.Game.Rulesets.Objects.Drawables /// public JudgementResult Result { get; private set; } - private bool judgementOccurred; - public override bool RemoveWhenNotAlive => false; public override bool RemoveCompletedTransforms => false; protected override bool RequiresChildrenUpdate => true; @@ -342,8 +340,6 @@ namespace osu.Game.Rulesets.Objects.Drawables if (!Result.HasResult) throw new InvalidOperationException($"{GetType().ReadableName()} applied a {nameof(JudgementResult)} but did not update {nameof(JudgementResult.Type)}."); - judgementOccurred = true; - // Ensure that the judgement is given a valid time offset, because this may not get set by the caller var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; Result.TimeOffset = Time.Current - endTime; @@ -376,21 +372,13 @@ namespace osu.Game.Rulesets.Objects.Drawables if (Time.Elapsed < 0) return false; - judgementOccurred = false; - - if (AllJudged) + if (Judged) return false; - foreach (var d in NestedHitObjects) - judgementOccurred |= d.UpdateResult(userTriggered); - - if (judgementOccurred || Judged) - return judgementOccurred; - var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; CheckForResult(userTriggered, Time.Current - endTime); - return judgementOccurred; + return Judged; } /// From a87a1e60314352fc3bd241935d5116f9e07cc075 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 4 Sep 2019 19:38:12 +0900 Subject: [PATCH 30/64] Don't redraw certain buffered containers on scale change --- osu.Game/Graphics/Backgrounds/Background.cs | 3 ++- osu.Game/Overlays/NowPlayingOverlay.cs | 5 ++++- osu.Game/Screens/Play/SquareGraph.cs | 1 + osu.Game/Screens/Select/BeatmapInfoWedge.cs | 2 ++ .../Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs | 1 + 5 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs index d13475189d..0f923c3a28 100644 --- a/osu.Game/Graphics/Backgrounds/Background.cs +++ b/osu.Game/Graphics/Backgrounds/Background.cs @@ -57,8 +57,9 @@ namespace osu.Game.Graphics.Backgrounds AddInternal(bufferedContainer = new BufferedContainer { - CacheDrawnFrameBuffer = true, RelativeSizeAxes = Axes.Both, + CacheDrawnFrameBuffer = true, + RedrawOnScale = false, Child = Sprite }); } diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index a3243a655e..c8361c6114 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -346,10 +346,13 @@ namespace osu.Game.Overlays public Background(WorkingBeatmap beatmap = null) { this.beatmap = beatmap; - CacheDrawnFrameBuffer = true; + Depth = float.MaxValue; RelativeSizeAxes = Axes.Both; + CacheDrawnFrameBuffer = true; + RedrawOnScale = false; + Children = new Drawable[] { sprite = new Sprite diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index 9c56725c4e..05f6128ac2 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -103,6 +103,7 @@ namespace osu.Game.Screens.Play var newColumns = new BufferedContainer { CacheDrawnFrameBuffer = true, + RedrawOnScale = false, RelativeSizeAxes = Axes.Both, }; diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 5f6307e3b4..65ecd7b812 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -154,6 +154,8 @@ namespace osu.Game.Screens.Select var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); CacheDrawnFrameBuffer = true; + RedrawOnScale = false; + RelativeSizeAxes = Axes.Both; titleBinding = localisation.GetLocalisedString(new LocalisedString((metadata.TitleUnicode, metadata.Title))); diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 97b6a78804..699e01bca7 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -146,6 +146,7 @@ namespace osu.Game.Screens.Select.Carousel public PanelBackground(WorkingBeatmap working) { CacheDrawnFrameBuffer = true; + RedrawOnScale = false; Children = new Drawable[] { From b80a8296cd591db9dd34b8746fdbb85570694be2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 20:27:27 +0900 Subject: [PATCH 31/64] Fix unavailable rulesets crashing at song select --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 2 +- osu.Game/Rulesets/RulesetInfo.cs | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 81f517dd86..8014631eca 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -84,7 +84,7 @@ namespace osu.Game.Beatmaps.Drawables Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, // the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment) - Icon = ruleset?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle } + Icon = ruleset?.CreateInstance()?.CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle } } }; } diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs index d9cff86265..c982ef7be1 100644 --- a/osu.Game/Rulesets/RulesetInfo.cs +++ b/osu.Game/Rulesets/RulesetInfo.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; using Newtonsoft.Json; namespace osu.Game.Rulesets @@ -20,7 +21,13 @@ namespace osu.Game.Rulesets [JsonIgnore] public bool Available { get; set; } - public virtual Ruleset CreateInstance() => (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo), this); + [CanBeNull] + public virtual Ruleset CreateInstance() + { + if (!Available) return null; + + return (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo), this); + } public bool Equals(RulesetInfo other) => other != null && ID == other.ID && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo; From c59a2bf9bb6bce70906c6fd3b3e99417ff314249 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 20:28:04 +0900 Subject: [PATCH 32/64] Fix tests crashing if a ruleset doesn't provide a NoFail mod --- osu.Game/Tests/Visual/PlayerTestScene.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/PlayerTestScene.cs b/osu.Game/Tests/Visual/PlayerTestScene.cs index ccd996098c..2c5a51ca02 100644 --- a/osu.Game/Tests/Visual/PlayerTestScene.cs +++ b/osu.Game/Tests/Visual/PlayerTestScene.cs @@ -50,7 +50,11 @@ namespace osu.Game.Tests.Visual Beatmap.Value = CreateWorkingBeatmap(beatmap); if (!AllowFail) - Mods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) }; + { + var noFailMod = ruleset.GetAllMods().FirstOrDefault(m => m is ModNoFail); + if (noFailMod != null) + Mods.Value = new[] { noFailMod }; + } if (Autoplay) { From 6197c7fd31d3edcbae5a07c1b06d7157b3eb25b6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 20:28:21 +0900 Subject: [PATCH 33/64] Add automatic resource mapping for rulesets to their own dll --- osu.Game/Rulesets/Ruleset.cs | 3 +++ osu.Game/Rulesets/UI/DrawableRuleset.cs | 20 ++++++++++++++++++++ osu.Game/Skinning/SkinnableSound.cs | 14 ++++++++------ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index b63292757d..197c089f71 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; +using osu.Framework.IO.Stores; using osu.Game.Beatmaps; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Edit; @@ -83,6 +84,8 @@ namespace osu.Game.Rulesets public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.Solid.QuestionCircle }; + public virtual IResourceStore CreateReourceStore() => new NamespacedResourceStore(new DllResourceStore(GetType().Assembly.Location), @"Resources"); + public abstract string Description { get; } public virtual RulesetSettingsSubsection CreateSettings() => null; diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index a32407d180..562b2c4667 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -14,10 +14,14 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using JetBrains.Annotations; +using osu.Framework.Audio; +using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Textures; using osu.Framework.Input; using osu.Framework.Input.Events; +using osu.Framework.IO.Stores; using osu.Game.Configuration; using osu.Game.Graphics.Cursor; using osu.Game.Input.Handlers; @@ -51,6 +55,10 @@ namespace osu.Game.Rulesets.UI private readonly Lazy playfield; + private TextureStore textureStore; + + private ISampleStore sampleStore; + /// /// The playfield. /// @@ -142,6 +150,18 @@ namespace osu.Game.Rulesets.UI { var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + var resources = Ruleset.CreateReourceStore(); + + if (resources != null) + { + textureStore = new TextureStore(new TextureLoaderStore(new NamespacedResourceStore(resources, "Textures"))); + textureStore.AddStore(dependencies.Get()); + dependencies.Cache(textureStore); + + sampleStore = dependencies.Get().GetSampleStore(new NamespacedResourceStore(resources, "Samples")); + dependencies.CacheAs(sampleStore); + } + onScreenDisplay = dependencies.Get(); Config = dependencies.Get().GetConfigFor(Ruleset); diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 3d0219ed93..bdf8be773b 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Audio; @@ -20,7 +21,7 @@ namespace osu.Game.Skinning private SampleChannel[] channels; - private AudioManager audio; + private ISampleStore samples; public SkinnableSound(IEnumerable hitSamples) { @@ -33,9 +34,9 @@ namespace osu.Game.Skinning } [BackgroundDependencyLoader] - private void load(AudioManager audio) + private void load(ISampleStore samples) { - this.audio = audio; + this.samples = samples; } private bool looping; @@ -81,7 +82,7 @@ namespace osu.Game.Skinning if (ch == null && allowFallback) foreach (var lookup in s.LookupNames) - if ((ch = audio.Samples.Get($"Gameplay/{lookup}")) != null) + if ((ch = samples.Get($"Gameplay/{lookup}")) != null) break; if (ch != null) @@ -102,8 +103,9 @@ namespace osu.Game.Skinning { base.Dispose(isDisposing); - foreach (var c in channels) - c.Dispose(); + if (channels != null) + foreach (var c in channels) + c.Dispose(); } } } From 8e8f33ec7b5fb0a24f279692c76ba3d214997bc2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 21:35:59 +0900 Subject: [PATCH 34/64] Remove null hinting for now --- osu.Game/Rulesets/RulesetInfo.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs index c982ef7be1..6a69fd8dd0 100644 --- a/osu.Game/Rulesets/RulesetInfo.cs +++ b/osu.Game/Rulesets/RulesetInfo.cs @@ -3,7 +3,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using JetBrains.Annotations; using Newtonsoft.Json; namespace osu.Game.Rulesets @@ -21,7 +20,6 @@ namespace osu.Game.Rulesets [JsonIgnore] public bool Available { get; set; } - [CanBeNull] public virtual Ruleset CreateInstance() { if (!Available) return null; From f9fa5988e69a679a7a344baa0a1855703d7f7caf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2019 23:16:56 +0900 Subject: [PATCH 35/64] Add basic nuget deploy support --- appveyor.yml | 2 -- appveyor_deploy.yml | 10 ++++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 appveyor_deploy.yml diff --git a/appveyor.yml b/appveyor.yml index 4dcaa7b45e..be1727e7d7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,5 @@ clone_depth: 1 version: '{branch}-{build}' image: Previous Visual Studio 2017 test: off -install: - - cmd: git submodule update --init --recursive --depth=5 build_script: - cmd: PowerShell -Version 2.0 .\build.ps1 diff --git a/appveyor_deploy.yml b/appveyor_deploy.yml new file mode 100644 index 0000000000..d36298175b --- /dev/null +++ b/appveyor_deploy.yml @@ -0,0 +1,10 @@ +clone_depth: 1 +version: '{build}' +image: Previous Visual Studio 2017 +test: off +skip_non_tags: true +build_script: + - cmd: PowerShell -Version 2.0 .\build.ps1 +deploy: + - provider: Environment + name: nuget From 50de4d1a3a4569da2532c52b3b74371f28f46ae7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Sep 2019 01:11:53 +0900 Subject: [PATCH 36/64] Remove PrivateAssets changes for now --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3147ca749f..f2a605e7a7 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + From afac512a1bfd09d115fc60b9dcfc5d8c0b7e776e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Sep 2019 02:12:17 +0900 Subject: [PATCH 37/64] Fix databased config save performance Adds proper save debounce logic. Closes #5991. --- .../Configuration/DatabasedConfigManager.cs | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/osu.Game/Configuration/DatabasedConfigManager.cs b/osu.Game/Configuration/DatabasedConfigManager.cs index d5cdd7e4bc..6aa89cdd69 100644 --- a/osu.Game/Configuration/DatabasedConfigManager.cs +++ b/osu.Game/Configuration/DatabasedConfigManager.cs @@ -16,11 +16,11 @@ namespace osu.Game.Configuration private readonly int? variant; - private readonly List databasedSettings; + private List databasedSettings; private readonly RulesetInfo ruleset; - private readonly bool legacySettingsExist; + private bool legacySettingsExist; protected DatabasedConfigManager(SettingsStore settings, RulesetInfo ruleset = null, int? variant = null) { @@ -28,21 +28,34 @@ namespace osu.Game.Configuration this.ruleset = ruleset; this.variant = variant; - databasedSettings = settings.Query(ruleset?.ID, variant); - legacySettingsExist = databasedSettings.Any(s => int.TryParse(s.Key, out var _)); + Load(); InitialiseDefaults(); } protected override void PerformLoad() { + databasedSettings = settings.Query(ruleset?.ID, variant); + legacySettingsExist = databasedSettings.Any(s => int.TryParse(s.Key, out var _)); } protected override bool PerformSave() { + lock (dirtySettings) + { + if (dirtySettings.Count > 0) + { + foreach (var setting in dirtySettings) + settings.Update(setting); + dirtySettings.Clear(); + } + } + return true; } + private readonly List dirtySettings = new List(); + protected override void AddBindable(T lookup, Bindable bindable) { base.AddBindable(lookup, bindable); @@ -80,7 +93,9 @@ namespace osu.Game.Configuration bindable.ValueChanged += b => { setting.Value = b.NewValue; - settings.Update(setting); + lock (dirtySettings) + if (!dirtySettings.Contains(setting)) + dirtySettings.Add(setting); }; } } From d40129aabe9a8947add0a28b6a51fa85b7017365 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Sep 2019 13:36:37 +0900 Subject: [PATCH 38/64] Remove unnecessary count check --- osu.Game/Configuration/DatabasedConfigManager.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game/Configuration/DatabasedConfigManager.cs b/osu.Game/Configuration/DatabasedConfigManager.cs index 6aa89cdd69..0046f4fa7f 100644 --- a/osu.Game/Configuration/DatabasedConfigManager.cs +++ b/osu.Game/Configuration/DatabasedConfigManager.cs @@ -43,12 +43,9 @@ namespace osu.Game.Configuration { lock (dirtySettings) { - if (dirtySettings.Count > 0) - { - foreach (var setting in dirtySettings) - settings.Update(setting); - dirtySettings.Clear(); - } + foreach (var setting in dirtySettings) + settings.Update(setting); + dirtySettings.Clear(); } return true; From 070a005294668614deaf2779450eed7b1e90051e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Sep 2019 13:37:11 +0900 Subject: [PATCH 39/64] Add braces to lock() Personal preference, I want to be sure that everything is wrapped correctly. --- osu.Game/Configuration/DatabasedConfigManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Configuration/DatabasedConfigManager.cs b/osu.Game/Configuration/DatabasedConfigManager.cs index 0046f4fa7f..02382cfd2b 100644 --- a/osu.Game/Configuration/DatabasedConfigManager.cs +++ b/osu.Game/Configuration/DatabasedConfigManager.cs @@ -90,9 +90,12 @@ namespace osu.Game.Configuration bindable.ValueChanged += b => { setting.Value = b.NewValue; + lock (dirtySettings) + { if (!dirtySettings.Contains(setting)) dirtySettings.Add(setting); + } }; } } From 2e6af84ca839d5e22f6fdab8fe790c0fd80894c4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Sep 2019 14:39:02 +0900 Subject: [PATCH 40/64] Don't redraw leaderboard scores --- osu.Game/Graphics/Sprites/GlowingSpriteText.cs | 9 ++++++++- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs index 24816deeb5..892b27da75 100644 --- a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs +++ b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs @@ -12,6 +12,7 @@ namespace osu.Game.Graphics.Sprites public class GlowingSpriteText : Container, IHasText { private readonly OsuSpriteText spriteText, blurredText; + private readonly BufferedContainer buffer; public string Text { @@ -43,13 +44,19 @@ namespace osu.Game.Graphics.Sprites set => blurredText.Colour = value; } + public bool RedrawOnScale + { + get => buffer.RedrawOnScale; + set => buffer.RedrawOnScale = value; + } + public GlowingSpriteText() { AutoSizeAxes = Axes.Both; Children = new Drawable[] { - new BufferedContainer + buffer = new BufferedContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 008f8208eb..e29748060e 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -193,6 +193,7 @@ namespace osu.Game.Online.Leaderboards GlowColour = OsuColour.FromHex(@"83ccfa"), Text = score.TotalScore.ToString(@"N0"), Font = OsuFont.Numeric.With(size: 23), + RedrawOnScale = false, }, RankContainer = new Container { @@ -338,6 +339,7 @@ namespace osu.Game.Online.Leaderboards GlowColour = OsuColour.FromHex(@"83ccfa"), Text = statistic.Value, Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold), + RedrawOnScale = false }, }, }; From 99579255ade422b7985b80c2dce5034735d8038c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Sep 2019 14:41:02 +0900 Subject: [PATCH 41/64] Force glowing sprite text to never redraw --- osu.Game/Graphics/Sprites/GlowingSpriteText.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs index 892b27da75..12688da9df 100644 --- a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs +++ b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs @@ -12,7 +12,6 @@ namespace osu.Game.Graphics.Sprites public class GlowingSpriteText : Container, IHasText { private readonly OsuSpriteText spriteText, blurredText; - private readonly BufferedContainer buffer; public string Text { @@ -44,24 +43,19 @@ namespace osu.Game.Graphics.Sprites set => blurredText.Colour = value; } - public bool RedrawOnScale - { - get => buffer.RedrawOnScale; - set => buffer.RedrawOnScale = value; - } - public GlowingSpriteText() { AutoSizeAxes = Axes.Both; Children = new Drawable[] { - buffer = new BufferedContainer + new BufferedContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, BlurSigma = new Vector2(4), CacheDrawnFrameBuffer = true, + RedrawOnScale = false, RelativeSizeAxes = Axes.Both, Blending = BlendingParameters.Additive, Size = new Vector2(3f), From a1d7291ffa8623eeb91bebb947bba1423e0ee1ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Sep 2019 16:31:10 +0900 Subject: [PATCH 42/64] Fix pause menu keyboard navigation being affected by initial cursor hover --- .../Visual/Gameplay/TestSceneGameplayMenuOverlay.cs | 9 +++++++-- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 3 +++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs index cc275009ba..c1635ffc83 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs @@ -17,7 +17,7 @@ using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay { - [System.ComponentModel.Description("player pause/fail screens")] + [Description("player pause/fail screens")] public class TestSceneGameplayMenuOverlay : ManualInputManagerTestScene { public override IReadOnlyList RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseOverlay) }; @@ -152,7 +152,8 @@ namespace osu.Game.Tests.Visual.Gameplay } /// - /// Tests that entering menu with cursor initially on button selects it. + /// Tests that entering menu with cursor initially on button doesn't selects it immediately. + /// This is to allow for stable keyboard navigation. /// [Test] public void TestInitialButtonHover() @@ -164,6 +165,10 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Hide overlay", () => pauseOverlay.Hide()); showOverlay(); + AddAssert("First button not selected", () => !getButton(0).Selected.Value); + + AddStep("Move slightly", () => InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(1))); + AddAssert("First button selected", () => getButton(0).Selected.Value); } diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index f93d5d8b02..c5202fa792 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -304,6 +304,9 @@ namespace osu.Game.Screens.Play private class Button : DialogButton { + // required to ensure keyboard navigation always starts from an extremity (unless the cursor is moved) + protected override bool OnHover(HoverEvent e) => true; + protected override bool OnMouseMove(MouseMoveEvent e) { Selected.Value = true; From 55c6feab6408d37f69c26706eb981906d5e61bc9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Sep 2019 16:33:14 +0900 Subject: [PATCH 43/64] 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 90d1854c39..adc340a734 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -61,6 +61,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7d106f0484..cb30eee33a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 8390a2229b..88d181454f 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -118,8 +118,8 @@ - - + + From 174f8ddb31b75b9a4e925c05871ea22f37a97fe0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Sep 2019 16:37:47 +0900 Subject: [PATCH 44/64] Remove incorrect usages --- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index e29748060e..008f8208eb 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -193,7 +193,6 @@ namespace osu.Game.Online.Leaderboards GlowColour = OsuColour.FromHex(@"83ccfa"), Text = score.TotalScore.ToString(@"N0"), Font = OsuFont.Numeric.With(size: 23), - RedrawOnScale = false, }, RankContainer = new Container { @@ -339,7 +338,6 @@ namespace osu.Game.Online.Leaderboards GlowColour = OsuColour.FromHex(@"83ccfa"), Text = statistic.Value, Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold), - RedrawOnScale = false }, }, }; From bda21998c4de0a87d57ed019a6c77b0c86925223 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Sep 2019 16:39:58 +0900 Subject: [PATCH 45/64] Add helper method to make direct casts be used --- .../Skinning/OsuLegacySkinTransformer.cs | 2 +- .../Skins/TestSceneSkinConfigurationLookup.cs | 19 +++++++++++++++++++ osu.Game/Skinning/LegacySkin.cs | 6 +++--- osu.Game/Skinning/SkinUtils.cs | 18 ++++++++++++++++++ 4 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 osu.Game/Skinning/SkinUtils.cs diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 284259705a..5957b81d7e 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Osu.Skinning { case OsuSkinConfiguration.SliderPathRadius: if (hasHitCircle.Value) - return new BindableFloat(legacy_circle_radius) as Bindable; + return SkinUtils.As(new BindableFloat(legacy_circle_radius)); break; } diff --git a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs index 1344d20d9f..71df038311 100644 --- a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs +++ b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs @@ -95,6 +95,25 @@ namespace osu.Game.Tests.Skins AddAssert("Check combo colours", () => requester.GetConfig>(GlobalSkinConfiguration.ComboColours)?.Value?.Count > 0); } + [Test] + public void TestWrongColourType() + { + AddStep("Add config colour", () => { source1.Configuration.CustomColours["Lookup"] = Color4.Red; }); + + AddAssert("perform incorrect lookup", () => + { + try + { + requester.GetConfig(new SkinCustomColourLookup("Lookup")); + return false; + } + catch + { + return true; + } + }); + } + public enum LookupType { Test diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index cd2ad2d61c..0b1076be01 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -62,16 +62,16 @@ namespace osu.Game.Skinning switch (global) { case GlobalSkinConfiguration.ComboColours: - return new Bindable>(Configuration.ComboColours) as IBindable; + return SkinUtils.As(new Bindable>(Configuration.ComboColours)); } break; case GlobalSkinColour colour: - return getCustomColour(colour.ToString()) as IBindable; + return SkinUtils.As(getCustomColour(colour.ToString())); case SkinCustomColourLookup customColour: - return getCustomColour(customColour.Lookup.ToString()) as IBindable; + return SkinUtils.As(getCustomColour(customColour.Lookup.ToString())); default: try diff --git a/osu.Game/Skinning/SkinUtils.cs b/osu.Game/Skinning/SkinUtils.cs new file mode 100644 index 0000000000..18059bc4bf --- /dev/null +++ b/osu.Game/Skinning/SkinUtils.cs @@ -0,0 +1,18 @@ +using osu.Framework.Bindables; + +namespace osu.Game.Skinning +{ + /// + /// Contains helper methods to assist in implementing s. + /// + public static class SkinUtils + { + /// + /// Converts an to a . Used for returning configuration values of specific types. + /// + /// The value. + /// The type of value , and the type of the resulting bindable. + /// The resulting bindable. + public static Bindable As(object value) => (Bindable)value; + } +} From 696802e6743088e1d450ad822f940afe348c4f1e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Sep 2019 16:52:53 +0900 Subject: [PATCH 46/64] Don't use in music player for now --- osu.Game/Overlays/NowPlayingOverlay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index c8361c6114..cf42c8005a 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -351,7 +351,6 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both; CacheDrawnFrameBuffer = true; - RedrawOnScale = false; Children = new Drawable[] { From 8e204ba2e90182dd3f691d94fa58ac526b91232e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Sep 2019 16:55:24 +0900 Subject: [PATCH 47/64] Refactor tests --- .../Skins/TestSceneSkinConfigurationLookup.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs index 71df038311..bbcc4140a9 100644 --- a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs +++ b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs @@ -44,24 +44,24 @@ namespace osu.Game.Tests.Skins } [Test] - public void TestParsingLookup() + public void TestFloatLookup() { - AddStep("Add config values", () => - { - source1.Configuration.ConfigDictionary["FloatTest"] = "1.1"; - source2.Configuration.ConfigDictionary["BoolTest"] = "1"; - }); - + AddStep("Add config values", () => source1.Configuration.ConfigDictionary["FloatTest"] = "1.1"); AddAssert("Check float parse lookup", () => requester.GetConfig("FloatTest")?.Value == 1.1f); + } + + [Test] + public void TestBoolLookup() + { + AddStep("Add config values", () => source1.Configuration.ConfigDictionary["BoolTest"] = "1"); AddAssert("Check bool parse lookup", () => requester.GetConfig("BoolTest")?.Value == true); } [Test] public void TestEnumLookup() { - AddStep("Add config values", () => { source1.Configuration.ConfigDictionary["Test"] = "Test2"; }); - - AddAssert("Check float parse lookup", () => requester.GetConfig(LookupType.Test)?.Value == ValueType.Test2); + AddStep("Add config values", () => source1.Configuration.ConfigDictionary["Test"] = "Test2"); + AddAssert("Check enum parse lookup", () => requester.GetConfig(LookupType.Test)?.Value == ValueType.Test2); } [Test] @@ -73,7 +73,7 @@ namespace osu.Game.Tests.Skins [Test] public void TestLookupNull() { - AddStep("Add config values", () => { source1.Configuration.ConfigDictionary["Lookup"] = null; }); + AddStep("Add config values", () => source1.Configuration.ConfigDictionary["Lookup"] = null); AddAssert("Check lookup null", () => { @@ -85,7 +85,7 @@ namespace osu.Game.Tests.Skins [Test] public void TestColourLookup() { - AddStep("Add config colour", () => { source1.Configuration.CustomColours["Lookup"] = Color4.Red; }); + AddStep("Add config colour", () => source1.Configuration.CustomColours["Lookup"] = Color4.Red); AddAssert("Check colour lookup", () => requester.GetConfig(new SkinCustomColourLookup("Lookup"))?.Value == Color4.Red); } @@ -98,7 +98,7 @@ namespace osu.Game.Tests.Skins [Test] public void TestWrongColourType() { - AddStep("Add config colour", () => { source1.Configuration.CustomColours["Lookup"] = Color4.Red; }); + AddStep("Add config colour", () => source1.Configuration.CustomColours["Lookup"] = Color4.Red); AddAssert("perform incorrect lookup", () => { From 90985b6af65a6008b55fd968d523e7c9ebc40d05 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Sep 2019 17:00:43 +0900 Subject: [PATCH 48/64] Add missing license header --- osu.Game/Skinning/SkinUtils.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Skinning/SkinUtils.cs b/osu.Game/Skinning/SkinUtils.cs index 18059bc4bf..e3bc5e28b8 100644 --- a/osu.Game/Skinning/SkinUtils.cs +++ b/osu.Game/Skinning/SkinUtils.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using osu.Framework.Bindables; namespace osu.Game.Skinning From 0a6c42972c8896ef0943133b93661895cb129841 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Sep 2019 23:01:35 +0900 Subject: [PATCH 49/64] Add back missing sample fallback to default skin --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 8 ++- osu.Game/Rulesets/UI/FallbackSampleStore.cs | 64 +++++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Rulesets/UI/FallbackSampleStore.cs diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 562b2c4667..2a8f64c42e 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.UI private TextureStore textureStore; - private ISampleStore sampleStore; + private ISampleStore localSampleStore; /// /// The playfield. @@ -158,8 +158,8 @@ namespace osu.Game.Rulesets.UI textureStore.AddStore(dependencies.Get()); dependencies.Cache(textureStore); - sampleStore = dependencies.Get().GetSampleStore(new NamespacedResourceStore(resources, "Samples")); - dependencies.CacheAs(sampleStore); + localSampleStore = dependencies.Get().GetSampleStore(new NamespacedResourceStore(resources, "Samples")); + dependencies.CacheAs(new FallbackSampleStore(localSampleStore, dependencies.Get())); } onScreenDisplay = dependencies.Get(); @@ -334,6 +334,8 @@ namespace osu.Game.Rulesets.UI { base.Dispose(isDisposing); + localSampleStore?.Dispose(); + if (Config != null) { onScreenDisplay?.StopTracking(this, Config); diff --git a/osu.Game/Rulesets/UI/FallbackSampleStore.cs b/osu.Game/Rulesets/UI/FallbackSampleStore.cs new file mode 100644 index 0000000000..64e273b72e --- /dev/null +++ b/osu.Game/Rulesets/UI/FallbackSampleStore.cs @@ -0,0 +1,64 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Audio.Track; +using osu.Framework.Bindables; + +namespace osu.Game.Rulesets.UI +{ + public class FallbackSampleStore : ISampleStore + { + private readonly ISampleStore primary; + private readonly ISampleStore secondary; + + public FallbackSampleStore(ISampleStore primary, ISampleStore secondary) + { + this.primary = primary; + this.secondary = secondary; + } + + public void Dispose() + { + primary.Dispose(); + secondary.Dispose(); + } + + public SampleChannel Get(string name) => primary.Get(name) ?? secondary.Get(name); + + public Task GetAsync(string name) => primary.GetAsync(name) ?? secondary.GetAsync(name); + + public Stream GetStream(string name) => primary.GetStream(name) ?? secondary.GetStream(name); + + public IEnumerable GetAvailableResources() => primary.GetAvailableResources().Concat(secondary.GetAvailableResources()); + + public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) + { + primary.AddAdjustment(type, adjustBindable); + secondary.AddAdjustment(type, adjustBindable); + } + + public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) + { + primary.RemoveAdjustment(type, adjustBindable); + primary.RemoveAdjustment(type, adjustBindable); + } + + public BindableDouble Volume => primary.Volume; + + public BindableDouble Balance => primary.Balance; + + public BindableDouble Frequency => primary.Frequency; + + public int PlaybackConcurrency + { + get => primary.PlaybackConcurrency; + set => primary.PlaybackConcurrency = value; + } + } +} From 60c2d113b80eed1630d0ccdc951a3b1fa954b9e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Sep 2019 23:07:03 +0900 Subject: [PATCH 50/64] Fix typo --- osu.Game/Rulesets/UI/FallbackSampleStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/FallbackSampleStore.cs b/osu.Game/Rulesets/UI/FallbackSampleStore.cs index 64e273b72e..cdefae458a 100644 --- a/osu.Game/Rulesets/UI/FallbackSampleStore.cs +++ b/osu.Game/Rulesets/UI/FallbackSampleStore.cs @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.UI public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) { primary.RemoveAdjustment(type, adjustBindable); - primary.RemoveAdjustment(type, adjustBindable); + secondary.RemoveAdjustment(type, adjustBindable); } public BindableDouble Volume => primary.Volume; From a0aeccf2322d06c782c9fc15d5eda86d1b15df6b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Sep 2019 23:24:13 +0900 Subject: [PATCH 51/64] Fix fallback to default combo colours not working --- osu.Game/Skinning/DefaultSkin.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 4dee70a47f..c0d6bb34e0 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -1,11 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Game.Audio; +using osuTK.Graphics; namespace osu.Game.Skinning { @@ -23,6 +25,21 @@ namespace osu.Game.Skinning public override SampleChannel GetSample(ISampleInfo sampleInfo) => null; - public override IBindable GetConfig(TLookup lookup) => null; + public override IBindable GetConfig(TLookup lookup) + { + switch (lookup) + { + case GlobalSkinConfiguration global: + switch (global) + { + case GlobalSkinConfiguration.ComboColours: + return SkinUtils.As(new Bindable>(Configuration.ComboColours)); + } + + break; + } + + return null; + } } } From dafe9da851437098d15f6104ce720d7eba2fc351 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 00:22:35 +0900 Subject: [PATCH 52/64] Dispose config managers ahead of time to avoid database errors --- osu.Game/Rulesets/RulesetConfigCache.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Rulesets/RulesetConfigCache.cs b/osu.Game/Rulesets/RulesetConfigCache.cs index 8c9e3c94e2..abaf7a96f2 100644 --- a/osu.Game/Rulesets/RulesetConfigCache.cs +++ b/osu.Game/Rulesets/RulesetConfigCache.cs @@ -36,5 +36,13 @@ namespace osu.Game.Rulesets return configCache.GetOrAdd(ruleset.RulesetInfo.ID.Value, _ => ruleset.CreateConfig(settingsStore)); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + foreach (var c in configCache.Values) + (c as IDisposable)?.Dispose(); + } } } From 5b094f8e1de30a7473e9b29c9b7933696f77c226 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 01:13:58 +0900 Subject: [PATCH 53/64] Actually register the RulesetConfigCache as a component --- osu.Game/OsuGameBase.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index de8f316b06..d6b8ad3e67 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -207,6 +207,7 @@ namespace osu.Game FileStore.Cleanup(); AddInternal(API); + AddInternal(RulesetConfigCache); GlobalActionContainer globalBinding; From d6cdde552daa84e04a76e6bf1c1d61872002c0bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 01:14:37 +0900 Subject: [PATCH 54/64] Add comment explaining dispose logic --- osu.Game/Rulesets/RulesetConfigCache.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/RulesetConfigCache.cs b/osu.Game/Rulesets/RulesetConfigCache.cs index abaf7a96f2..cdcd2666cf 100644 --- a/osu.Game/Rulesets/RulesetConfigCache.cs +++ b/osu.Game/Rulesets/RulesetConfigCache.cs @@ -41,6 +41,7 @@ namespace osu.Game.Rulesets { base.Dispose(isDisposing); + // ensures any potential database operations are finalised before game destruction. foreach (var c in configCache.Values) (c as IDisposable)?.Dispose(); } From 50985d1b1d952b7d922c0a4ccd3936218061cc26 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 10:43:19 +0900 Subject: [PATCH 55/64] Fix disposal logic --- osu.Game/Rulesets/UI/FallbackSampleStore.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/UI/FallbackSampleStore.cs b/osu.Game/Rulesets/UI/FallbackSampleStore.cs index cdefae458a..f9e1c85e38 100644 --- a/osu.Game/Rulesets/UI/FallbackSampleStore.cs +++ b/osu.Game/Rulesets/UI/FallbackSampleStore.cs @@ -23,12 +23,6 @@ namespace osu.Game.Rulesets.UI this.secondary = secondary; } - public void Dispose() - { - primary.Dispose(); - secondary.Dispose(); - } - public SampleChannel Get(string name) => primary.Get(name) ?? secondary.Get(name); public Task GetAsync(string name) => primary.GetAsync(name) ?? secondary.GetAsync(name); @@ -58,7 +52,15 @@ namespace osu.Game.Rulesets.UI public int PlaybackConcurrency { get => primary.PlaybackConcurrency; - set => primary.PlaybackConcurrency = value; + set + { + primary.PlaybackConcurrency = value; + secondary.PlaybackConcurrency = value; + } + } + + public void Dispose() + { } } } From 43aed7fea7c1be8da0e906f5f18d6dafc17f6716 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2019 02:58:58 +0000 Subject: [PATCH 56/64] Bump Humanizer from 2.6.2 to 2.7.2 Bumps [Humanizer](https://github.com/Humanizr/Humanizer) from 2.6.2 to 2.7.2. - [Release notes](https://github.com/Humanizr/Humanizer/releases) - [Changelog](https://github.com/Humanizr/Humanizer/blob/master/release_notes.md) - [Commits](https://github.com/Humanizr/Humanizer/compare/v2.6.2...v2.7.2) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8e4ce03e1a..5f2aad24dc 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -21,7 +21,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 88d181454f..5027a4ef8c 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -113,7 +113,7 @@ - + From 7d1f5310d20a866a9b8c9283eb3a2a1bc6903840 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 12:03:29 +0900 Subject: [PATCH 57/64] Don't implement anything --- osu.Game/Rulesets/UI/FallbackSampleStore.cs | 30 +++++++-------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/osu.Game/Rulesets/UI/FallbackSampleStore.cs b/osu.Game/Rulesets/UI/FallbackSampleStore.cs index f9e1c85e38..f1df8bf359 100644 --- a/osu.Game/Rulesets/UI/FallbackSampleStore.cs +++ b/osu.Game/Rulesets/UI/FallbackSampleStore.cs @@ -1,9 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading.Tasks; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -29,34 +29,22 @@ namespace osu.Game.Rulesets.UI public Stream GetStream(string name) => primary.GetStream(name) ?? secondary.GetStream(name); - public IEnumerable GetAvailableResources() => primary.GetAvailableResources().Concat(secondary.GetAvailableResources()); + public IEnumerable GetAvailableResources() => throw new NotImplementedException(); - public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) - { - primary.AddAdjustment(type, adjustBindable); - secondary.AddAdjustment(type, adjustBindable); - } + public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException(); - public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) - { - primary.RemoveAdjustment(type, adjustBindable); - secondary.RemoveAdjustment(type, adjustBindable); - } + public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException(); - public BindableDouble Volume => primary.Volume; + public BindableDouble Volume => throw new NotImplementedException(); - public BindableDouble Balance => primary.Balance; + public BindableDouble Balance => throw new NotImplementedException(); - public BindableDouble Frequency => primary.Frequency; + public BindableDouble Frequency => throw new NotImplementedException(); public int PlaybackConcurrency { - get => primary.PlaybackConcurrency; - set - { - primary.PlaybackConcurrency = value; - secondary.PlaybackConcurrency = value; - } + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); } public void Dispose() From bf6f803e691edbc776896433beb8f8c4f0f2f3ea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 12:12:27 +0900 Subject: [PATCH 58/64] Nest temporary class --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 49 +++++++++++++++++++ osu.Game/Rulesets/UI/FallbackSampleStore.cs | 54 --------------------- 2 files changed, 49 insertions(+), 54 deletions(-) delete mode 100644 osu.Game/Rulesets/UI/FallbackSampleStore.cs diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 2a8f64c42e..a34bb6e8ea 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -11,10 +11,13 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading; +using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics.Cursor; @@ -465,4 +468,50 @@ namespace osu.Game.Rulesets.UI { } } + + /// + /// A sample store which adds a fallback source. + /// + /// + /// This is a temporary implementation to workaround ISampleStore limitations. + /// + public class FallbackSampleStore : ISampleStore + { + private readonly ISampleStore primary; + private readonly ISampleStore secondary; + + public FallbackSampleStore(ISampleStore primary, ISampleStore secondary) + { + this.primary = primary; + this.secondary = secondary; + } + + public SampleChannel Get(string name) => primary.Get(name) ?? secondary.Get(name); + + public Task GetAsync(string name) => primary.GetAsync(name) ?? secondary.GetAsync(name); + + public Stream GetStream(string name) => primary.GetStream(name) ?? secondary.GetStream(name); + + public IEnumerable GetAvailableResources() => throw new NotImplementedException(); + + public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException(); + + public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException(); + + public BindableDouble Volume => throw new NotImplementedException(); + + public BindableDouble Balance => throw new NotImplementedException(); + + public BindableDouble Frequency => throw new NotImplementedException(); + + public int PlaybackConcurrency + { + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); + } + + public void Dispose() + { + } + } } diff --git a/osu.Game/Rulesets/UI/FallbackSampleStore.cs b/osu.Game/Rulesets/UI/FallbackSampleStore.cs deleted file mode 100644 index f1df8bf359..0000000000 --- a/osu.Game/Rulesets/UI/FallbackSampleStore.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Framework.Audio.Track; -using osu.Framework.Bindables; - -namespace osu.Game.Rulesets.UI -{ - public class FallbackSampleStore : ISampleStore - { - private readonly ISampleStore primary; - private readonly ISampleStore secondary; - - public FallbackSampleStore(ISampleStore primary, ISampleStore secondary) - { - this.primary = primary; - this.secondary = secondary; - } - - public SampleChannel Get(string name) => primary.Get(name) ?? secondary.Get(name); - - public Task GetAsync(string name) => primary.GetAsync(name) ?? secondary.GetAsync(name); - - public Stream GetStream(string name) => primary.GetStream(name) ?? secondary.GetStream(name); - - public IEnumerable GetAvailableResources() => throw new NotImplementedException(); - - public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException(); - - public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException(); - - public BindableDouble Volume => throw new NotImplementedException(); - - public BindableDouble Balance => throw new NotImplementedException(); - - public BindableDouble Frequency => throw new NotImplementedException(); - - public int PlaybackConcurrency - { - get => throw new NotImplementedException(); - set => throw new NotImplementedException(); - } - - public void Dispose() - { - } - } -} From f4f95197616cce29716fb31f1f36d3cb03bde0b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 12:16:20 +0900 Subject: [PATCH 59/64] Add todo comment --- osu.Game/Skinning/DefaultSkin.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index c0d6bb34e0..529c1afca5 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -29,6 +29,8 @@ namespace osu.Game.Skinning { switch (lookup) { + // todo: this code is pulled from LegacySkin and should not exist. + // will likely change based on how databased storage of skin configuration goes. case GlobalSkinConfiguration global: switch (global) { From f925e781a9ac8a1a770d0e2fc09d0b421b54af47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 15:24:00 +0900 Subject: [PATCH 60/64] Refactor HitWindows for legibility --- .../Objects/CatchHitObject.cs | 1 + .../{Objects => Scoring}/CatchHitWindows.cs | 3 +- .../Scoring/CatchScoreProcessor.cs | 1 - .../Difficulty/ManiaDifficultyCalculator.cs | 6 +- osu.Game.Rulesets.Mania/Objects/HoldNote.cs | 2 +- .../Objects/HoldNoteTick.cs | 2 +- .../Objects/ManiaHitObject.cs | 2 + .../Objects/ManiaHitWindows.cs | 35 ----- .../Scoring/ManiaHitWindows.cs | 11 ++ .../Scoring/ManiaScoreProcessor.cs | 1 - .../TestSceneShaking.cs | 2 +- .../Difficulty/OsuDifficultyCalculator.cs | 5 +- .../Objects/Drawables/DrawableHitCircle.cs | 6 +- .../Objects/Drawables/DrawableRepeatPoint.cs | 2 +- .../Objects/Drawables/DrawableSliderTick.cs | 2 +- .../Objects/Drawables/DrawableSpinner.cs | 2 +- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 2 + .../Objects/OsuHitWindows.cs | 29 ---- osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Slider.cs | 1 + .../Objects/SliderTailCircle.cs | 1 + osu.Game.Rulesets.Osu/Objects/SliderTick.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Spinner.cs | 2 +- .../Replays/OsuAutoGenerator.cs | 20 +-- .../Scoring/OsuHitWindows.cs | 20 +++ .../TestSceneTaikoPlayfield.cs | 2 +- .../Difficulty/TaikoDifficultyCalculator.cs | 5 +- .../Objects/Drawables/DrawableHit.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 2 +- .../Objects/DrumRollTick.cs | 2 +- .../Objects/StrongHitObject.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/Swell.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/SwellTick.cs | 2 +- .../Objects/TaikoHitObject.cs | 2 + .../Objects/TaikoHitWindows.cs | 41 ------ .../Scoring/TaikoHitWindows.cs | 19 +++ .../Scoring/TaikoScoreProcessor.cs | 1 - .../Beatmaps/Formats/OsuJsonDecoderTest.cs | 2 +- .../Gameplay/TestSceneBarHitErrorMeter.cs | 20 +-- .../Objects/Drawables/DrawableHitObject.cs | 2 +- osu.Game/Rulesets/Objects/HitObject.cs | 1 + .../Objects/Legacy/Mania/ConvertHit.cs | 1 + .../Objects/Legacy/Mania/ConvertHold.cs | 1 + .../Objects/Legacy/Mania/ConvertSlider.cs | 1 + .../Objects/Legacy/Mania/ConvertSpinner.cs | 1 + .../Rulesets/Objects/Legacy/Osu/ConvertHit.cs | 1 + .../Objects/Legacy/Osu/ConvertSlider.cs | 1 + .../Objects/Legacy/Osu/ConvertSpinner.cs | 1 + .../Objects/Legacy/Taiko/ConvertHit.cs | 2 + .../Objects/Legacy/Taiko/ConvertSlider.cs | 2 + .../Objects/Legacy/Taiko/ConvertSpinner.cs | 1 + osu.Game/Rulesets/Scoring/HitResult.cs | 4 + .../{Objects => Scoring}/HitWindows.cs | 131 +++++++++++++----- osu.Game/Scoring/ScoreInfo.cs | 2 +- osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 1 - .../HUD/HitErrorMeters/BarHitErrorMeter.cs | 3 +- .../Play/HUD/HitErrorMeters/HitErrorMeter.cs | 2 +- 57 files changed, 225 insertions(+), 199 deletions(-) rename osu.Game.Rulesets.Catch/{Objects => Scoring}/CatchHitWindows.cs (87%) delete mode 100644 osu.Game.Rulesets.Mania/Objects/ManiaHitWindows.cs create mode 100644 osu.Game.Rulesets.Mania/Scoring/ManiaHitWindows.cs delete mode 100644 osu.Game.Rulesets.Osu/Objects/OsuHitWindows.cs create mode 100644 osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs delete mode 100644 osu.Game.Rulesets.Taiko/Objects/TaikoHitWindows.cs create mode 100644 osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs rename osu.Game/Rulesets/{Objects => Scoring}/HitWindows.cs (56%) diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 19a1b59752..a25d9cb67e 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -6,6 +6,7 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Catch.Objects { diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitWindows.cs b/osu.Game.Rulesets.Catch/Scoring/CatchHitWindows.cs similarity index 87% rename from osu.Game.Rulesets.Catch/Objects/CatchHitWindows.cs rename to osu.Game.Rulesets.Catch/Scoring/CatchHitWindows.cs index 837662f5fe..ff793a372e 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitWindows.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchHitWindows.cs @@ -1,10 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; -namespace osu.Game.Rulesets.Catch.Objects +namespace osu.Game.Rulesets.Catch.Scoring { public class CatchHitWindows : HitWindows { diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index 99b22b2d56..18785d65ea 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -4,7 +4,6 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index d945abdb04..37cba1fd3c 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -11,9 +11,9 @@ using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Difficulty.Preprocessing; using osu.Game.Rulesets.Mania.Difficulty.Skills; using osu.Game.Rulesets.Mania.Mods; -using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Difficulty { @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty StarRating = difficultyValue(skills) * star_scaling_factor, Mods = mods, // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future - GreatHitWindow = (int)(hitWindows.Great / 2) / clockRate, + GreatHitWindow = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate, Skills = skills }; } diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs index d28d04b3c1..0c82cf7bbc 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs @@ -5,8 +5,8 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Judgements; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Objects { diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs index 6bb21633b6..d0125f8793 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs @@ -3,7 +3,7 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Judgements; -using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Objects { diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs index 70720a926b..995e1516cb 100644 --- a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs @@ -3,7 +3,9 @@ using osu.Framework.Bindables; using osu.Game.Rulesets.Mania.Objects.Types; +using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Objects { diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitWindows.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitWindows.cs deleted file mode 100644 index 5f2ceab48b..0000000000 --- a/osu.Game.Rulesets.Mania/Objects/ManiaHitWindows.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Mania.Objects -{ - public class ManiaHitWindows : HitWindows - { - private static readonly IReadOnlyDictionary base_ranges = new Dictionary - { - { HitResult.Perfect, (44.8, 38.8, 27.8) }, - { HitResult.Great, (128, 98, 68) }, - { HitResult.Good, (194, 164, 134) }, - { HitResult.Ok, (254, 224, 194) }, - { HitResult.Meh, (302, 272, 242) }, - { HitResult.Miss, (376, 346, 316) }, - }; - - public override bool IsHitResultAllowed(HitResult result) => true; - - public override void SetDifficulty(double difficulty) - { - Perfect = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Perfect]); - Great = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Great]); - Good = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Good]); - Ok = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Ok]); - Meh = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Meh]); - Miss = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Miss]); - } - } -} diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaHitWindows.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaHitWindows.cs new file mode 100644 index 0000000000..549f0f9214 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaHitWindows.cs @@ -0,0 +1,11 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Mania.Scoring +{ + public class ManiaHitWindows : HitWindows + { + } +} diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index 5caf08fb1e..49894a644c 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -4,7 +4,6 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs index 585fdb9cb4..863d0eda09 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneShaking.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Tests Debug.Assert(drawableHitObject.HitObject.HitWindows != null); - double delay = drawableHitObject.HitObject.StartTime - (drawableHitObject.HitObject.HitWindows.HalfWindowFor(HitResult.Miss) + RNG.Next(0, 300)) - Time.Current; + double delay = drawableHitObject.HitObject.StartTime - (drawableHitObject.HitObject.HitWindows.WindowFor(HitResult.Miss) + RNG.Next(0, 300)) - Time.Current; Scheduler.AddDelayed(() => drawableHitObject.TriggerJudgement(), delay); return drawableHitObject; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 61e9f60cdd..b0d261a1cc 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -9,11 +9,12 @@ 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; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Difficulty { @@ -39,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future - double hitWindowGreat = (int)(hitWindows.Great / 2) / clockRate; + double hitWindowGreat = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate; double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; int maxCombo = beatmap.HitObjects.Count; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 3decc4e51f..985dcbca86 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -10,8 +10,8 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; -using osuTK; using osu.Game.Rulesets.Scoring; +using osuTK; using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Objects.Drawables @@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (result == HitResult.None) { - Shake(Math.Abs(timeOffset) - HitObject.HitWindows.HalfWindowFor(HitResult.Miss)); + Shake(Math.Abs(timeOffset) - HitObject.HitWindows.WindowFor(HitResult.Miss)); return; } @@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables hitArea.HitAction = null; // override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early. - LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.HalfWindowFor(HitResult.Miss); + LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.WindowFor(HitResult.Miss); break; case ArmedState.Miss: diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 50187781f6..00a943a67f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -9,8 +9,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.MathUtils; using osu.Game.Rulesets.Objects.Drawables; -using osuTK; using osu.Game.Rulesets.Scoring; +using osuTK; using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Objects.Drawables diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index c5fa5f0af5..ba931976a8 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -8,9 +8,9 @@ using osu.Game.Rulesets.Objects.Drawables; using osuTK; using osuTK.Graphics; using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects.Drawables { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index a0bd301fdb..49aaa2aaea 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -13,8 +13,8 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; -using osu.Game.Screens.Ranking; using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Ranking; namespace osu.Game.Rulesets.Osu.Objects.Drawables { diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index b52bfcd181..2cf877b000 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -7,6 +7,8 @@ using osu.Game.Rulesets.Objects; using osuTK; using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects { diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitWindows.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitWindows.cs deleted file mode 100644 index add8fd53c7..0000000000 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitWindows.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Osu.Objects -{ - public class OsuHitWindows : HitWindows - { - private static readonly IReadOnlyDictionary base_ranges = new Dictionary - { - { HitResult.Great, (160, 100, 40) }, - { HitResult.Good, (280, 200, 120) }, - { HitResult.Meh, (400, 300, 200) }, - { HitResult.Miss, (400, 400, 400) }, - }; - - public override void SetDifficulty(double difficulty) - { - Great = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Great]); - Good = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Good]); - Meh = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Meh]); - Miss = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Miss]); - } - } -} diff --git a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs index 5bd480c0ff..a794e57c9e 100644 --- a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs @@ -5,8 +5,8 @@ using System; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects { diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 93231844bb..2805494021 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -13,6 +13,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects { diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs index c53a88337e..7e540a577b 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs @@ -5,6 +5,7 @@ using osu.Framework.Bindables; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects { diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs index 60e9084ed3..af7cf5b144 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs @@ -4,8 +4,8 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects { diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index 69c779a182..2e7b763966 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -6,8 +6,8 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects { diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index e5fa571d4d..24320b6579 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -10,9 +10,9 @@ using System.Diagnostics; using System.Linq; using osu.Framework.Graphics; using osu.Game.Replays; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Replays @@ -118,29 +118,29 @@ namespace osu.Game.Rulesets.Osu.Replays Debug.Assert(hitWindows != null); // Make the cursor stay at a hitObject as long as possible (mainly for autopilot). - if (h.StartTime - hitWindows.HalfWindowFor(HitResult.Miss) > endTime + hitWindows.HalfWindowFor(HitResult.Meh) + 50) + if (h.StartTime - hitWindows.WindowFor(HitResult.Miss) > endTime + hitWindows.WindowFor(HitResult.Meh) + 50) { if (!(prev is Spinner) && h.StartTime - endTime < 1000) - AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.WindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); if (!(h is Spinner)) - AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.HalfWindowFor(HitResult.Miss), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); + AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.WindowFor(HitResult.Miss), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } - else if (h.StartTime - hitWindows.HalfWindowFor(HitResult.Meh) > endTime + hitWindows.HalfWindowFor(HitResult.Meh) + 50) + else if (h.StartTime - hitWindows.WindowFor(HitResult.Meh) > endTime + hitWindows.WindowFor(HitResult.Meh) + 50) { if (!(prev is Spinner) && h.StartTime - endTime < 1000) - AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.WindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); if (!(h is Spinner)) - AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); + AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.WindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } - else if (h.StartTime - hitWindows.HalfWindowFor(HitResult.Good) > endTime + hitWindows.HalfWindowFor(HitResult.Good) + 50) + else if (h.StartTime - hitWindows.WindowFor(HitResult.Good) > endTime + hitWindows.WindowFor(HitResult.Good) + 50) { if (!(prev is Spinner) && h.StartTime - endTime < 1000) - AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.HalfWindowFor(HitResult.Good), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.WindowFor(HitResult.Good), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); if (!(h is Spinner)) - AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.HalfWindowFor(HitResult.Good), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); + AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.WindowFor(HitResult.Good), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } } diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs new file mode 100644 index 0000000000..8f258c9e3d --- /dev/null +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Scoring +{ + public class OsuHitWindows : HitWindows + { + private static readonly DifficultyRange[] osu_ranges = + { + new DifficultyRange(HitResult.Great, 80, 50, 20), + new DifficultyRange(HitResult.Good, 140, 100, 60), + new DifficultyRange(HitResult.Meh, 200, 150, 100), + new DifficultyRange(HitResult.Miss, 200, 200, 200), + }; + + protected override DifficultyRange[] GetRanges() => osu_ranges; + } +} diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs index 6fd16c213b..3aa461e779 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs @@ -14,13 +14,13 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.UI; using osu.Game.Tests.Visual; using osuTK; -using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Taiko.Tests { diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index fc93bccb94..32d49ea39c 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -8,11 +8,12 @@ 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.Scoring; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; using osu.Game.Rulesets.Taiko.Difficulty.Skills; using osu.Game.Rulesets.Taiko.Mods; using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Scoring; namespace osu.Game.Rulesets.Taiko.Difficulty { @@ -38,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty StarRating = skills.Single().DifficultyValue() * star_scaling_factor, Mods = mods, // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future - GreatHitWindow = (int)(hitWindows.Great / 2) / clockRate, + GreatHitWindow = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate, MaxCombo = beatmap.HitObjects.Count(h => h is Hit), Skills = skills }; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 0942b37f58..676ecd5a0b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables validActionPressed = false; UnproxyContent(); - this.Delay(HitObject.HitWindows.HalfWindowFor(HitResult.Miss)).Expire(); + this.Delay(HitObject.HitWindows.WindowFor(HitResult.Miss)).Expire(); break; case ArmedState.Miss: diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 3ed52f21f0..4e02c76a8b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -6,7 +6,7 @@ using System; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs index 39e2b45e24..c466ca7c8a 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects diff --git a/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs index 830e640242..d660149528 100644 --- a/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects diff --git a/osu.Game.Rulesets.Taiko/Objects/Swell.cs b/osu.Game.Rulesets.Taiko/Objects/Swell.cs index e7812841bf..f96c033dce 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Swell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Swell.cs @@ -4,7 +4,7 @@ using System; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects diff --git a/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs index 049fa7de5f..68212e8f12 100644 --- a/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index 3592d73004..6f4fbd0651 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -4,7 +4,9 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; +using osu.Game.Rulesets.Taiko.Scoring; namespace osu.Game.Rulesets.Taiko.Objects { diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitWindows.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitWindows.cs deleted file mode 100644 index f232919cbf..0000000000 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitWindows.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Taiko.Objects -{ - public class TaikoHitWindows : HitWindows - { - private static readonly IReadOnlyDictionary base_ranges = new Dictionary - { - { HitResult.Great, (100, 70, 40) }, - { HitResult.Good, (240, 160, 100) }, - { HitResult.Miss, (270, 190, 140) }, - }; - - public override bool IsHitResultAllowed(HitResult result) - { - switch (result) - { - case HitResult.Great: - case HitResult.Good: - case HitResult.Miss: - return true; - - default: - return false; - } - } - - public override void SetDifficulty(double difficulty) - { - Great = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Great]); - Good = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Good]); - Miss = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Miss]); - } - } -} diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs new file mode 100644 index 0000000000..77d59f9638 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Taiko.Scoring +{ + public class TaikoHitWindows : HitWindows + { + private static readonly DifficultyRange[] taiko_ranges = + { + new DifficultyRange(HitResult.Great, 50, 35, 20), + new DifficultyRange(HitResult.Good, 120, 80, 50), + new DifficultyRange(HitResult.Miss, 135, 95, 70), + }; + + protected override DifficultyRange[] GetRanges() => taiko_ranges; + } +} diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index 68ddf2db19..75a27ff639 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -3,7 +3,6 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.UI; diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index a725c58462..4859abbb8e 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -9,8 +9,8 @@ using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.IO.Serialization; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; using osu.Game.Tests.Resources; using osuTK; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs index e9c15dab9b..a934d22b5d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBarHitErrorMeter.cs @@ -3,18 +3,18 @@ using NUnit.Framework; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Taiko.Objects; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Catch.Objects; using System; using System.Collections.Generic; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Scoring; using osu.Framework.MathUtils; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Game.Rulesets.Catch.Scoring; +using osu.Game.Rulesets.Mania.Scoring; +using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Scoring; using osu.Game.Screens.Play.HUD.HitErrorMeters; namespace osu.Game.Tests.Visual.Gameplay @@ -36,8 +36,8 @@ namespace osu.Game.Tests.Visual.Gameplay AddRepeatStep("New random judgement", () => newJudgement(), 40); - AddRepeatStep("New max negative", () => newJudgement(-hitWindows.HalfWindowFor(HitResult.Meh)), 20); - AddRepeatStep("New max positive", () => newJudgement(hitWindows.HalfWindowFor(HitResult.Meh)), 20); + AddRepeatStep("New max negative", () => newJudgement(-hitWindows.WindowFor(HitResult.Meh)), 20); + AddRepeatStep("New max positive", () => newJudgement(hitWindows.WindowFor(HitResult.Meh)), 20); AddStep("New fixed judgement (50ms)", () => newJudgement(50)); } @@ -85,9 +85,9 @@ namespace osu.Game.Tests.Visual.Gameplay AutoSizeAxes = Axes.Both, Children = new[] { - new SpriteText { Text = $@"Great: {hitWindows?.Great}" }, - new SpriteText { Text = $@"Good: {hitWindows?.Good}" }, - new SpriteText { Text = $@"Meh: {hitWindows?.Meh}" }, + new SpriteText { Text = $@"Great: {hitWindows?.WindowFor(HitResult.Great)}" }, + new SpriteText { Text = $@"Good: {hitWindows?.WindowFor(HitResult.Good)}" }, + new SpriteText { Text = $@"Meh: {hitWindows?.WindowFor(HitResult.Meh)}" }, } }); diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index a6d0aad880..5dfab5ef77 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -164,7 +164,7 @@ namespace osu.Game.Rulesets.Objects.Drawables { UpdateInitialTransforms(); - var judgementOffset = Math.Min(HitObject.HitWindows?.HalfWindowFor(HitResult.Miss) ?? double.MaxValue, Result?.TimeOffset ?? 0); + var judgementOffset = Math.Min(HitObject.HitWindows?.WindowFor(HitResult.Miss) ?? double.MaxValue, Result?.TimeOffset ?? 0); using (BeginDelayedSequence(InitialLifetimeOffset + judgementOffset, true)) { diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 5e029139d9..96297ab44f 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -9,6 +9,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Objects { diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs index 06fde576d2..609bdd571a 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Objects.Legacy.Mania { diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs index 096c07f7d2..350ee3185d 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHold.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Objects.Legacy.Mania { diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs index 226d91bb86..e372fbd273 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Objects.Legacy.Mania { diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs index eb20fa67f1..067377d300 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSpinner.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Objects.Legacy.Mania { diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs index 84b66a4c26..c9851a0074 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; using osuTK; namespace osu.Game.Rulesets.Objects.Legacy.Osu diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs index c850feb189..1c1180702b 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; using osuTK; namespace osu.Game.Rulesets.Objects.Legacy.Osu diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs index e5a8884aa2..bc94ea1803 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; using osuTK; namespace osu.Game.Rulesets.Objects.Legacy.Osu diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs index 5cecc2a59f..709345170f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.Rulesets.Scoring; + namespace osu.Game.Rulesets.Objects.Legacy.Taiko { /// diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs index 5cedc6e2e5..c173b3e11a 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.Rulesets.Scoring; + namespace osu.Game.Rulesets.Objects.Legacy.Taiko { /// diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs index ca9fdd53ed..9a35ad2776 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSpinner.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Objects.Legacy.Taiko { diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs index 2376f12e9e..7ba88d3df8 100644 --- a/osu.Game/Rulesets/Scoring/HitResult.cs +++ b/osu.Game/Rulesets/Scoring/HitResult.cs @@ -16,6 +16,10 @@ namespace osu.Game.Rulesets.Scoring /// /// Indicates that the object has been judged as a miss. /// + /// + /// This miss window should determine how early a hit can be before it is considered for judgement (as opposed to being ignored as + /// "too far in the future). It should also define when a forced miss should be triggered (as a result of no user input in time). + /// [Description(@"Miss")] Miss, diff --git a/osu.Game/Rulesets/Objects/HitWindows.cs b/osu.Game/Rulesets/Scoring/HitWindows.cs similarity index 56% rename from osu.Game/Rulesets/Objects/HitWindows.cs rename to osu.Game/Rulesets/Scoring/HitWindows.cs index e88af67c7c..beba62044a 100644 --- a/osu.Game/Rulesets/Objects/HitWindows.cs +++ b/osu.Game/Rulesets/Scoring/HitWindows.cs @@ -4,51 +4,61 @@ using System; using System.Collections.Generic; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Objects; -namespace osu.Game.Rulesets.Objects +namespace osu.Game.Rulesets.Scoring { + /// + /// A structure containing timing data for hit window based gameplay. + /// public class HitWindows { - private static readonly IReadOnlyDictionary base_ranges = new Dictionary + private static readonly DifficultyRange[] base_ranges = { - { HitResult.Perfect, (44.8, 38.8, 27.8) }, - { HitResult.Great, (128, 98, 68) }, - { HitResult.Good, (194, 164, 134) }, - { HitResult.Ok, (254, 224, 194) }, - { HitResult.Meh, (302, 272, 242) }, - { HitResult.Miss, (376, 346, 316) }, + new DifficultyRange(HitResult.Perfect, 22.4D, 19.4D, 13.9D), + new DifficultyRange(HitResult.Great, 64, 49, 34), + new DifficultyRange(HitResult.Good, 97, 82, 67), + new DifficultyRange(HitResult.Ok, 127, 112, 97), + new DifficultyRange(HitResult.Meh, 151, 136, 121), + new DifficultyRange(HitResult.Miss, 188, 173, 158), }; /// /// Hit window for a result. /// - public double Perfect { get; protected set; } + private double perfect; /// /// Hit window for a result. /// - public double Great { get; protected set; } + /// + /// Note that this value includes both the early and late region. + /// + private double great; /// /// Hit window for a result. /// - public double Good { get; protected set; } + private double good; /// /// Hit window for an result. /// - public double Ok { get; protected set; } + private double ok; /// /// Hit window for a result. /// - public double Meh { get; protected set; } + private double meh; /// /// Hit window for a result. /// - public double Miss { get; protected set; } + /// + /// This miss window should determine how early a hit can be before it is considered for judgement (as opposed to being ignored as + /// "too far in the future). It should also define when a forced miss should be triggered (as a result of no user input in time). + /// + private double miss; /// /// Retrieves the with the largest hit window that produces a successful hit. @@ -66,15 +76,15 @@ namespace osu.Game.Rulesets.Objects } /// - /// Retrieves a mapping of s to their half window timing for all allowed s. + /// Retrieves a mapping of s to their timing windows for all allowed s. /// /// - public IEnumerable<(HitResult result, double length)> GetAllAvailableHalfWindows() + public IEnumerable<(HitResult result, double length)> GetAllAvailableWindows() { for (var result = HitResult.Meh; result <= HitResult.Perfect; ++result) { if (IsHitResultAllowed(result)) - yield return (result, HalfWindowFor(result)); + yield return (result, WindowFor(result)); } } @@ -100,14 +110,39 @@ namespace osu.Game.Rulesets.Objects /// Sets hit windows with values that correspond to a difficulty parameter. /// /// The parameter. - public virtual void SetDifficulty(double difficulty) + public void SetDifficulty(double difficulty) { - Perfect = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Perfect]); - Great = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Great]); - Good = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Good]); - Ok = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Ok]); - Meh = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Meh]); - Miss = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Miss]); + foreach (var range in GetRanges()) + { + var value = BeatmapDifficulty.DifficultyRange(difficulty, (range.Min, range.Average, range.Max)); + + switch (range.Result) + { + case HitResult.Miss: + miss = value; + break; + + case HitResult.Meh: + meh = value; + break; + + case HitResult.Ok: + ok = value; + break; + + case HitResult.Good: + good = value; + break; + + case HitResult.Great: + great = value; + break; + + case HitResult.Perfect: + perfect = value; + break; + } + } } /// @@ -121,7 +156,7 @@ namespace osu.Game.Rulesets.Objects for (var result = HitResult.Perfect; result >= HitResult.Miss; --result) { - if (IsHitResultAllowed(result) && timeOffset <= HalfWindowFor(result)) + if (IsHitResultAllowed(result) && timeOffset <= WindowFor(result)) return result; } @@ -129,32 +164,32 @@ namespace osu.Game.Rulesets.Objects } /// - /// Retrieves half the hit window for a . - /// This is useful if the hit window for one half of the hittable range of a is required. + /// Retrieves the hit window for a . + /// This is the number of +/- milliseconds allowed for the requested result (so the actual hittable range is double this). /// /// The expected . /// One half of the hit window for . - public double HalfWindowFor(HitResult result) + public double WindowFor(HitResult result) { switch (result) { case HitResult.Perfect: - return Perfect / 2; + return perfect; case HitResult.Great: - return Great / 2; + return great; case HitResult.Good: - return Good / 2; + return good; case HitResult.Ok: - return Ok / 2; + return ok; case HitResult.Meh: - return Meh / 2; + return meh; case HitResult.Miss: - return Miss / 2; + return miss; default: throw new ArgumentException(nameof(result)); @@ -167,6 +202,30 @@ namespace osu.Game.Rulesets.Objects /// /// The time offset. /// Whether the can be hit at any point in the future from this time offset. - public bool CanBeHit(double timeOffset) => timeOffset <= HalfWindowFor(LowestSuccessfulHitResult()); + public bool CanBeHit(double timeOffset) => timeOffset <= WindowFor(LowestSuccessfulHitResult()); + + /// + /// Retrieve a valid list of s representing hit windows. + /// Defaults are provided but can be overridden to customise for a ruleset. + /// + protected virtual DifficultyRange[] GetRanges() => base_ranges; + } + + public struct DifficultyRange + { + public readonly HitResult Result; + + public double Min; + public double Average; + public double Max; + + public DifficultyRange(HitResult result, double min, double average, double max) + { + Result = result; + + Min = min; + Average = average; + Max = max; + } } } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 266725a739..d3c37bd4f4 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -11,8 +11,8 @@ using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; -using osu.Game.Users; using osu.Game.Rulesets.Scoring; +using osu.Game.Users; namespace osu.Game.Scoring { diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index adda94d629..920d11c910 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Configuration; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play.HUD.HitErrorMeters; diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 594dd64e52..03a0f23fb6 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osuTK; using osuTK.Graphics; @@ -146,7 +145,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters private void createColourBars(OsuColour colours) { - var windows = HitWindows.GetAllAvailableHalfWindows().ToArray(); + var windows = HitWindows.GetAllAvailableWindows().ToArray(); maxHitWindow = windows.First().length; diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs index da1d9fff0d..dee25048ed 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs @@ -3,7 +3,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Screens.Play.HUD.HitErrorMeters { From 6cdc87bd29cfade223c0fe44fe7c0808a8634c80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 15:37:20 +0900 Subject: [PATCH 61/64] Fix allowed results omissions --- osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs | 14 ++++++++++++++ osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs | 13 +++++++++++++ 2 files changed, 27 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs index 8f258c9e3d..a6491bb3f3 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs @@ -15,6 +15,20 @@ namespace osu.Game.Rulesets.Osu.Scoring new DifficultyRange(HitResult.Miss, 200, 200, 200), }; + public override bool IsHitResultAllowed(HitResult result) + { + switch (result) + { + case HitResult.Great: + case HitResult.Good: + case HitResult.Meh: + case HitResult.Miss: + return true; + } + + return false; + } + protected override DifficultyRange[] GetRanges() => osu_ranges; } } diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs index 77d59f9638..9d273392ff 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs @@ -14,6 +14,19 @@ namespace osu.Game.Rulesets.Taiko.Scoring new DifficultyRange(HitResult.Miss, 135, 95, 70), }; + public override bool IsHitResultAllowed(HitResult result) + { + switch (result) + { + case HitResult.Great: + case HitResult.Good: + case HitResult.Miss: + return true; + } + + return false; + } + protected override DifficultyRange[] GetRanges() => taiko_ranges; } } From 4031f51745ae15add836cbeb2d5c9b6116ac37d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 15:37:30 +0900 Subject: [PATCH 62/64] More permissive IsHitResultAllow by default --- osu.Game/Rulesets/Scoring/HitWindows.cs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/HitWindows.cs b/osu.Game/Rulesets/Scoring/HitWindows.cs index beba62044a..6d8107cf9c 100644 --- a/osu.Game/Rulesets/Scoring/HitWindows.cs +++ b/osu.Game/Rulesets/Scoring/HitWindows.cs @@ -93,18 +93,7 @@ namespace osu.Game.Rulesets.Scoring /// /// The result type to check. /// Whether the can be achieved. - public virtual bool IsHitResultAllowed(HitResult result) - { - switch (result) - { - case HitResult.Perfect: - case HitResult.Ok: - return false; - - default: - return true; - } - } + public virtual bool IsHitResultAllowed(HitResult result) => true; /// /// Sets hit windows with values that correspond to a difficulty parameter. From 985375d1c64daec3290130d80957ea980ee8b836 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 15:47:01 +0900 Subject: [PATCH 63/64] Remove private field xmldoc --- osu.Game/Rulesets/Scoring/HitWindows.cs | 30 ------------------------- 1 file changed, 30 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/HitWindows.cs b/osu.Game/Rulesets/Scoring/HitWindows.cs index 6d8107cf9c..efc4cd9f5c 100644 --- a/osu.Game/Rulesets/Scoring/HitWindows.cs +++ b/osu.Game/Rulesets/Scoring/HitWindows.cs @@ -23,41 +23,11 @@ namespace osu.Game.Rulesets.Scoring new DifficultyRange(HitResult.Miss, 188, 173, 158), }; - /// - /// Hit window for a result. - /// private double perfect; - - /// - /// Hit window for a result. - /// - /// - /// Note that this value includes both the early and late region. - /// private double great; - - /// - /// Hit window for a result. - /// private double good; - - /// - /// Hit window for an result. - /// private double ok; - - /// - /// Hit window for a result. - /// private double meh; - - /// - /// Hit window for a result. - /// - /// - /// This miss window should determine how early a hit can be before it is considered for judgement (as opposed to being ignored as - /// "too far in the future). It should also define when a forced miss should be triggered (as a result of no user input in time). - /// private double miss; /// From b89fb5cdf7ca5e5b1ac8932ed6b3e79fb7a8bedd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Sep 2019 16:51:30 +0900 Subject: [PATCH 64/64] Fix failing test --- .../Visual/Gameplay/TestSceneFailJudgement.cs | 10 +--------- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 9 +++++++-- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs index bb0901524f..d57ec44f39 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs @@ -26,15 +26,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddUntilStep("wait for fail", () => Player.HasFailed); AddUntilStep("wait for multiple judged objects", () => ((FailPlayer)Player).DrawableRuleset.Playfield.AllHitObjects.Count(h => h.AllJudged) > 1); - AddAssert("total judgements == 1", () => - { - int count = 0; - - foreach (var stat in (HitResult[])Enum.GetValues(typeof(HitResult))) - count += ((FailPlayer)Player).ScoreProcessor.GetStatistic(stat); - - return count == 1; - }); + AddAssert("total judgements == 1", () => ((FailPlayer)Player).ScoreProcessor.JudgedHits == 1); } private class FailPlayer : ReplayPlayer diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 9f2c79df33..e4f20c27b4 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -92,6 +92,11 @@ namespace osu.Game.Rulesets.Scoring /// public virtual bool HasCompleted => false; + /// + /// The total number of judged s at the current point in time. + /// + public int JudgedHits { get; protected set; } + /// /// Whether this ScoreProcessor has already triggered the failed state. /// @@ -142,6 +147,8 @@ namespace osu.Game.Rulesets.Scoring Rank.Value = ScoreRank.X; HighestCombo.Value = 0; + JudgedHits = 0; + HasFailed = false; } @@ -208,7 +215,6 @@ namespace osu.Game.Rulesets.Scoring public sealed override bool HasCompleted => JudgedHits == MaxHits; protected int MaxHits { get; private set; } - protected int JudgedHits { get; private set; } private double maxHighestCombo; @@ -441,7 +447,6 @@ namespace osu.Game.Rulesets.Scoring base.Reset(storeResults); - JudgedHits = 0; baseScore = 0; rollingMaxBaseScore = 0; bonusScore = 0;