From 1d314a1f4b86ce5fd6ea95804acb8d411d95413c Mon Sep 17 00:00:00 2001 From: voidedWarranties Date: Wed, 11 Mar 2020 22:40:08 -0700 Subject: [PATCH 001/202] Prevent playback from going beyond song end --- .../Edit/Components/PlaybackControl.cs | 3 +++ osu.Game/Screens/Edit/EditorClock.cs | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/osu.Game/Screens/Edit/Components/PlaybackControl.cs b/osu.Game/Screens/Edit/Components/PlaybackControl.cs index 897c6ec531..ff650a7ad7 100644 --- a/osu.Game/Screens/Edit/Components/PlaybackControl.cs +++ b/osu.Game/Screens/Edit/Components/PlaybackControl.cs @@ -87,6 +87,9 @@ namespace osu.Game.Screens.Edit.Components private void togglePause() { + if ((adjustableClock as EditorClock)?.PlaybackFinished == true) + adjustableClock.Seek(0); + if (adjustableClock.IsRunning) adjustableClock.Stop(); else diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index e5e47507f3..0e5b42fe69 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -21,6 +21,8 @@ namespace osu.Game.Screens.Edit private readonly BindableBeatDivisor beatDivisor; + public bool PlaybackFinished { get; private set; } + public EditorClock(WorkingBeatmap beatmap, BindableBeatDivisor beatDivisor) { this.beatDivisor = beatDivisor; @@ -37,6 +39,23 @@ namespace osu.Game.Screens.Edit TrackLength = trackLength; } + public override void ProcessFrame() + { + base.ProcessFrame(); + + if (CurrentTime >= TrackLength) + { + if (!PlaybackFinished) + { + PlaybackFinished = true; + Stop(); + Seek(TrackLength); + } + } + else + PlaybackFinished = false; + } + /// /// Seek to the closest snappable beat from a time. /// From 7e4f58c2d3adc15ccb14a69d99094de5efcd7c13 Mon Sep 17 00:00:00 2001 From: voidedWarranties Date: Fri, 13 Mar 2020 16:42:05 -0700 Subject: [PATCH 002/202] Internalize both looping and stopping --- .../Screens/Edit/Components/PlaybackControl.cs | 3 --- osu.Game/Screens/Edit/EditorClock.cs | 14 ++++++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/PlaybackControl.cs b/osu.Game/Screens/Edit/Components/PlaybackControl.cs index ff650a7ad7..897c6ec531 100644 --- a/osu.Game/Screens/Edit/Components/PlaybackControl.cs +++ b/osu.Game/Screens/Edit/Components/PlaybackControl.cs @@ -87,9 +87,6 @@ namespace osu.Game.Screens.Edit.Components private void togglePause() { - if ((adjustableClock as EditorClock)?.PlaybackFinished == true) - adjustableClock.Seek(0); - if (adjustableClock.IsRunning) adjustableClock.Stop(); else diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index 0e5b42fe69..aef304bd6e 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens.Edit private readonly BindableBeatDivisor beatDivisor; - public bool PlaybackFinished { get; private set; } + private bool playbackFinished; public EditorClock(WorkingBeatmap beatmap, BindableBeatDivisor beatDivisor) { @@ -43,17 +43,19 @@ namespace osu.Game.Screens.Edit { base.ProcessFrame(); - if (CurrentTime >= TrackLength) + var playbackAlreadyStopped = playbackFinished; + playbackFinished = CurrentTime >= TrackLength; + + if (playbackFinished && IsRunning) { - if (!PlaybackFinished) + if (!playbackAlreadyStopped) { - PlaybackFinished = true; Stop(); Seek(TrackLength); } + else + Seek(0); } - else - PlaybackFinished = false; } /// From a38c912c6d4060a85f5172adf1bcc89e0ed9dc0e Mon Sep 17 00:00:00 2001 From: voidedWarranties Date: Sat, 21 Mar 2020 12:15:20 -0700 Subject: [PATCH 003/202] Test stopping behavior --- .../Visual/Editor/TestSceneEditorClock.cs | 51 +++++++++++++++++++ .../Visual/Editor/TimelineTestScene.cs | 16 +++--- 2 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 osu.Game.Tests/Visual/Editor/TestSceneEditorClock.cs diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorClock.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorClock.cs new file mode 100644 index 0000000000..0b128a974c --- /dev/null +++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorClock.cs @@ -0,0 +1,51 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Screens.Edit.Components; +using osuTK; + +namespace osu.Game.Tests.Visual.Editor +{ + [TestFixture] + public class TestSceneEditorClock : EditorClockTestScene + { + public TestSceneEditorClock() + { + Add(new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new TimeInfoContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(200, 100) + }, + new PlaybackControl + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(200, 100) + } + } + }); + } + + [Test] + public void TestStopAtTrackEnd() + { + AddStep("Reset clock", () => Clock.Seek(0)); + AddStep("Start clock", Clock.Start); + AddAssert("Clock running", () => Clock.IsRunning); + AddStep("Seek near end", () => Clock.Seek(Clock.TrackLength - 250)); + AddUntilStep("Clock stops", () => !Clock.IsRunning); + AddAssert("Clock stopped at end", () => Clock.CurrentTime == Clock.TrackLength); + AddStep("Start clock again", Clock.Start); + AddAssert("Clock looped", () => Clock.IsRunning && Clock.CurrentTime < Clock.TrackLength); + } + } +} diff --git a/osu.Game.Tests/Visual/Editor/TimelineTestScene.cs b/osu.Game.Tests/Visual/Editor/TimelineTestScene.cs index 7081eb3af5..83a0455b46 100644 --- a/osu.Game.Tests/Visual/Editor/TimelineTestScene.cs +++ b/osu.Game.Tests/Visual/Editor/TimelineTestScene.cs @@ -113,7 +113,6 @@ namespace osu.Game.Tests.Visual.Editor private class StartStopButton : OsuButton { private IAdjustableClock adjustableClock; - private bool started; public StartStopButton() { @@ -132,18 +131,17 @@ namespace osu.Game.Tests.Visual.Editor private void onClick() { - if (started) - { + if (adjustableClock.IsRunning) adjustableClock.Stop(); - Text = "Start"; - } else - { adjustableClock.Start(); - Text = "Stop"; - } + } - started = !started; + protected override void Update() + { + base.Update(); + + Text = adjustableClock.IsRunning ? "Stop" : "Start"; } } } From b41f3f1cad7312708d864269cfb1c587ed2353ca Mon Sep 17 00:00:00 2001 From: voidedWarranties Date: Mon, 23 Mar 2020 22:37:53 -0700 Subject: [PATCH 004/202] Fix seeking back to beginning too early --- .../Compose/Components/Timeline/Timeline.cs | 4 +++- osu.Game/Screens/Edit/EditorClock.cs | 21 +++++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index ddca5e42c2..590abf20b4 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -130,7 +130,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (!track.IsLoaded) return; - adjustableClock.Seek(Current / Content.DrawWidth * track.Length); + double target = Current / Content.DrawWidth * track.Length; + + adjustableClock.Seek(Math.Min(track.Length, target)); } private void scrollToTrackTime() diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index aef304bd6e..b4ab867774 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -43,18 +43,21 @@ namespace osu.Game.Screens.Edit { base.ProcessFrame(); - var playbackAlreadyStopped = playbackFinished; - playbackFinished = CurrentTime >= TrackLength; - - if (playbackFinished && IsRunning) + if (IsRunning) { - if (!playbackAlreadyStopped) + var playbackAlreadyStopped = playbackFinished; + playbackFinished = CurrentTime >= TrackLength; + + if (playbackFinished) { - Stop(); - Seek(TrackLength); + if (!playbackAlreadyStopped) + { + Stop(); + Seek(TrackLength); + } + else + Seek(0); } - else - Seek(0); } } From d42c872f8f19923dd56388cd1ba632cc7298292a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 Mar 2020 16:02:20 +0900 Subject: [PATCH 005/202] Better ensure track restarted --- osu.Game.Tests/Visual/Editor/TestSceneEditorClock.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editor/TestSceneEditorClock.cs b/osu.Game.Tests/Visual/Editor/TestSceneEditorClock.cs index 0b128a974c..a824696022 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneEditorClock.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneEditorClock.cs @@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.Editor AddUntilStep("Clock stops", () => !Clock.IsRunning); AddAssert("Clock stopped at end", () => Clock.CurrentTime == Clock.TrackLength); AddStep("Start clock again", Clock.Start); - AddAssert("Clock looped", () => Clock.IsRunning && Clock.CurrentTime < Clock.TrackLength); + AddAssert("Clock looped to start", () => Clock.IsRunning && Clock.CurrentTime < 500); } } } From 00574a528868d1ef31907484e71623969c0cfeaf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Feb 2021 18:32:28 +0900 Subject: [PATCH 006/202] Use ISample everywhere in Skin GetSample lookup path --- .../Skinning/Legacy/ManiaLegacySkinTransformer.cs | 2 +- osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs | 2 +- osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs | 2 +- .../Skinning/Legacy/TaikoLegacySkinTransformer.cs | 2 +- osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs | 2 +- osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs | 4 ++-- .../NonVisual/Skinning/LegacySkinAnimationTest.cs | 2 +- osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs | 2 +- .../Visual/Gameplay/TestSceneSkinnableDrawable.cs | 6 +++--- osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs | 2 +- osu.Game/Skinning/DefaultSkin.cs | 2 +- osu.Game/Skinning/ISkin.cs | 2 +- osu.Game/Skinning/LegacyBeatmapSkin.cs | 2 +- osu.Game/Skinning/LegacySkin.cs | 2 +- osu.Game/Skinning/LegacySkinTransformer.cs | 2 +- osu.Game/Skinning/Skin.cs | 2 +- osu.Game/Skinning/SkinManager.cs | 2 +- osu.Game/Skinning/SkinProvidingContainer.cs | 4 ++-- 18 files changed, 22 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index cbbbacfe19..24ccae895d 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -140,7 +140,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy return animation == null ? null : new LegacyManiaJudgementPiece(result, animation); } - public override Sample GetSample(ISampleInfo sampleInfo) + public override ISample GetSample(ISampleInfo sampleInfo) { // layered hit sounds never play in mania if (sampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacySample && legacySample.IsLayered) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs index e2d9f144c0..8fd13c7417 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Tests return null; } - public Sample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); + public ISample GetSample(ISampleInfo sampleInfo) => 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 8dbb48c048..19b6779619 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs @@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Osu.Tests public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null; - public Sample GetSample(ISampleInfo sampleInfo) => null; + public ISample GetSample(ISampleInfo sampleInfo) => null; public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => default; public IBindable GetConfig(TLookup lookup) => null; diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs index 9f29675230..d1214d3456 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs @@ -152,7 +152,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy throw new ArgumentOutOfRangeException(nameof(component), $"Invalid component type: {component}"); } - public override Sample GetSample(ISampleInfo sampleInfo) => Source.GetSample(new LegacyTaikoSampleInfo(sampleInfo)); + public override ISample GetSample(ISampleInfo sampleInfo) => Source.GetSample(new LegacyTaikoSampleInfo(sampleInfo)); public override IBindable GetConfig(TLookup lookup) => Source.GetConfig(lookup); diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs index 3ded3009bd..883791c35c 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs @@ -121,7 +121,7 @@ namespace osu.Game.Tests.Gameplay public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => throw new NotImplementedException(); - public Sample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); + public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); public IBindable GetConfig(TLookup lookup) { diff --git a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs index 7a0dd5b719..6fa1839556 100644 --- a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs @@ -35,7 +35,7 @@ namespace osu.Game.Tests.Gameplay public void TestRetrieveTopLevelSample() { ISkin skin = null; - Sample channel = null; + ISample channel = null; AddStep("create skin", () => skin = new TestSkin("test-sample", this)); AddStep("retrieve sample", () => channel = skin.GetSample(new SampleInfo("test-sample"))); @@ -47,7 +47,7 @@ namespace osu.Game.Tests.Gameplay public void TestRetrieveSampleInSubFolder() { ISkin skin = null; - Sample channel = null; + ISample channel = null; AddStep("create skin", () => skin = new TestSkin("folder/test-sample", this)); AddStep("retrieve sample", () => channel = skin.GetSample(new SampleInfo("folder/test-sample"))); diff --git a/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs b/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs index da004b9088..b08a228de3 100644 --- a/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs +++ b/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs @@ -59,7 +59,7 @@ namespace osu.Game.Tests.NonVisual.Skinning } public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotSupportedException(); - public Sample GetSample(ISampleInfo sampleInfo) => throw new NotSupportedException(); + public ISample GetSample(ISampleInfo sampleInfo) => throw new NotSupportedException(); public IBindable GetConfig(TLookup lookup) => throw new NotSupportedException(); } diff --git a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs index 414f7d3f88..732a3f3f42 100644 --- a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs +++ b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs @@ -219,7 +219,7 @@ namespace osu.Game.Tests.Skins public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => skin.GetTexture(componentName, wrapModeS, wrapModeT); - public Sample GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo); + public ISample GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo); public IBindable GetConfig(TLookup lookup) => skin.GetConfig(lookup); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs index 44142b69d7..7a6e2f54c2 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs @@ -298,7 +298,7 @@ namespace osu.Game.Tests.Visual.Gameplay public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => throw new NotImplementedException(); - public Sample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); + public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException(); } @@ -309,7 +309,7 @@ namespace osu.Game.Tests.Visual.Gameplay public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => throw new NotImplementedException(); - public Sample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); + public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException(); } @@ -321,7 +321,7 @@ namespace osu.Game.Tests.Visual.Gameplay public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => throw new NotImplementedException(); - public Sample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); + public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException(); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs index d688e9cb21..d792405eeb 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableSound.cs @@ -145,7 +145,7 @@ namespace osu.Game.Tests.Visual.Gameplay public Drawable GetDrawableComponent(ISkinComponent component) => source?.GetDrawableComponent(component); public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => source?.GetTexture(componentName, wrapModeS, wrapModeT); - public Sample GetSample(ISampleInfo sampleInfo) => source?.GetSample(sampleInfo); + public ISample GetSample(ISampleInfo sampleInfo) => source?.GetSample(sampleInfo); public IBindable GetConfig(TLookup lookup) => source?.GetConfig(lookup); public void TriggerSourceChanged() diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 346c7b3c65..0b3f5f3cde 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -24,7 +24,7 @@ namespace osu.Game.Skinning public override Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null; - public override Sample GetSample(ISampleInfo sampleInfo) => null; + public override ISample GetSample(ISampleInfo sampleInfo) => null; public override IBindable GetConfig(TLookup lookup) { diff --git a/osu.Game/Skinning/ISkin.cs b/osu.Game/Skinning/ISkin.cs index ef8de01042..73f7cf6d39 100644 --- a/osu.Game/Skinning/ISkin.cs +++ b/osu.Game/Skinning/ISkin.cs @@ -48,7 +48,7 @@ namespace osu.Game.Skinning /// The requested sample. /// A matching sample channel, or null if unavailable. [CanBeNull] - Sample GetSample(ISampleInfo sampleInfo); + ISample GetSample(ISampleInfo sampleInfo); /// /// Retrieve a configuration value. diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index fb4207b647..3ec205e897 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -39,7 +39,7 @@ namespace osu.Game.Skinning return base.GetConfig(lookup); } - public override Sample GetSample(ISampleInfo sampleInfo) + public override ISample GetSample(ISampleInfo sampleInfo) { if (sampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy && legacy.CustomSampleBank == 0) { diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index e5d0217671..1ee797098c 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -452,7 +452,7 @@ namespace osu.Game.Skinning return null; } - public override Sample GetSample(ISampleInfo sampleInfo) + public override ISample GetSample(ISampleInfo sampleInfo) { IEnumerable lookupNames; diff --git a/osu.Game/Skinning/LegacySkinTransformer.cs b/osu.Game/Skinning/LegacySkinTransformer.cs index e2f4a82a54..ae8faf1a3b 100644 --- a/osu.Game/Skinning/LegacySkinTransformer.cs +++ b/osu.Game/Skinning/LegacySkinTransformer.cs @@ -34,7 +34,7 @@ namespace osu.Game.Skinning public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => Source.GetTexture(componentName, wrapModeS, wrapModeT); - public virtual Sample GetSample(ISampleInfo sampleInfo) + public virtual ISample GetSample(ISampleInfo sampleInfo) { if (!(sampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacySample)) return Source.GetSample(sampleInfo); diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index e8d84b49f9..13f5385c20 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -19,7 +19,7 @@ namespace osu.Game.Skinning public abstract Drawable GetDrawableComponent(ISkinComponent componentName); - public abstract Sample GetSample(ISampleInfo sampleInfo); + public abstract ISample GetSample(ISampleInfo sampleInfo); public Texture GetTexture(string componentName) => GetTexture(componentName, default, default); diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 2826c826a5..9e730b2ce1 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -171,7 +171,7 @@ namespace osu.Game.Skinning public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => CurrentSkin.Value.GetTexture(componentName, wrapModeS, wrapModeT); - public Sample GetSample(ISampleInfo sampleInfo) => CurrentSkin.Value.GetSample(sampleInfo); + public ISample GetSample(ISampleInfo sampleInfo) => CurrentSkin.Value.GetSample(sampleInfo); public IBindable GetConfig(TLookup lookup) => CurrentSkin.Value.GetConfig(lookup); diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index ba67d0a678..cf22b2e820 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -59,9 +59,9 @@ namespace osu.Game.Skinning return fallbackSource?.GetTexture(componentName, wrapModeS, wrapModeT); } - public Sample GetSample(ISampleInfo sampleInfo) + public ISample GetSample(ISampleInfo sampleInfo) { - Sample sourceChannel; + ISample sourceChannel; if (AllowSampleLookup(sampleInfo) && (sourceChannel = skin?.GetSample(sampleInfo)) != null) return sourceChannel; From 4aff54412a0a23707b4284510705dd2b2c91492d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Feb 2021 18:32:39 +0900 Subject: [PATCH 007/202] Move dispose method to end of file --- osu.Game/Skinning/LegacySkin.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 1ee797098c..12abc4d867 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -100,13 +100,6 @@ namespace osu.Game.Skinning true) != null); } - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - Textures?.Dispose(); - Samples?.Dispose(); - } - public override IBindable GetConfig(TLookup lookup) { switch (lookup) @@ -504,5 +497,12 @@ namespace osu.Game.Skinning string lastPiece = componentName.Split('/').Last(); yield return componentName.StartsWith("Gameplay/taiko/", StringComparison.Ordinal) ? "taiko-" + lastPiece : lastPiece; } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + Textures?.Dispose(); + Samples?.Dispose(); + } } } From 880fe820733d9159c6eb866f85336b01081ea1c2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Feb 2021 18:32:51 +0900 Subject: [PATCH 008/202] Add sample wrapper in LegacySkin to keep a reference and avoid GC death --- osu.Game/Skinning/LegacySkin.cs | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 12abc4d867..5d015ca5ab 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -461,12 +461,43 @@ namespace osu.Game.Skinning var sample = Samples?.Get(lookup); if (sample != null) - return sample; + return new LegacySkinSample(sample, this); } return null; } + /// + /// A sample wrapper which keeps a reference to the contained skin to avoid finalizer garbage collection of the managing SampleStore. + /// + private class LegacySkinSample : ISample + { + private readonly Sample sample; + + [UsedImplicitly] + private readonly LegacySkin skin; + + public LegacySkinSample(Sample sample, LegacySkin skin) + { + this.sample = sample; + this.skin = skin; + } + + public SampleChannel Play() + { + return sample.Play(); + } + + public SampleChannel GetChannel() + { + return sample.GetChannel(); + } + + public double Length => sample.Length; + + public Bindable PlaybackConcurrency => sample.PlaybackConcurrency; + } + private IEnumerable getLegacyLookupNames(HitSampleInfo hitSample) { var lookupNames = hitSample.LookupNames.SelectMany(getFallbackNames); From 487a39eea95a5215d5aec323c923a600144ba51a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Feb 2021 18:52:34 +0900 Subject: [PATCH 009/202] Update interface implementations with framework changes --- .../TestSceneDrawableRulesetDependencies.cs | 6 ++-- .../UI/DrawableRulesetDependencies.cs | 11 +++++++ osu.Game/Skinning/LegacySkin.cs | 33 +++++++++++++++++++ osu.Game/Skinning/PoolableSkinnableSample.cs | 8 +++++ osu.Game/Skinning/SkinnableSound.cs | 17 ++++++---- 5 files changed, 67 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs b/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs index 787f72ba79..a2f2c5e41f 100644 --- a/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs +++ b/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs @@ -118,9 +118,11 @@ namespace osu.Game.Tests.Rulesets public BindableNumber Frequency => throw new NotImplementedException(); public BindableNumber Tempo => throw new NotImplementedException(); - public void AddAdjustment(AdjustableProperty type, BindableNumber adjustBindable) => throw new NotImplementedException(); + public void BindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException(); + public void UnbindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException(); - public void RemoveAdjustment(AdjustableProperty type, BindableNumber adjustBindable) => throw new NotImplementedException(); + public void AddAdjustment(AdjustableProperty type, IBindable adjustBindable) => throw new NotImplementedException(); + public void RemoveAdjustment(AdjustableProperty type, IBindable adjustBindable) => throw new NotImplementedException(); public void RemoveAllAdjustments(AdjustableProperty type) => throw new NotImplementedException(); diff --git a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs index deec948d14..6c31f05337 100644 --- a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs +++ b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs @@ -114,6 +114,11 @@ namespace osu.Game.Rulesets.UI public void RemoveAdjustment(AdjustableProperty type, BindableNumber adjustBindable) => throw new NotSupportedException(); + public void RemoveAdjustment(AdjustableProperty type, IBindable adjustBindable) + { + throw new NotImplementedException(); + } + public void RemoveAllAdjustments(AdjustableProperty type) => throw new NotSupportedException(); public BindableNumber Volume => throw new NotSupportedException(); @@ -124,6 +129,12 @@ namespace osu.Game.Rulesets.UI public BindableNumber Tempo => throw new NotSupportedException(); + public void BindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException(); + + public void UnbindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException(); + + public void AddAdjustment(AdjustableProperty type, IBindable adjustBindable) => throw new NotImplementedException(); + public IBindable GetAggregate(AdjustableProperty type) => throw new NotSupportedException(); public IBindable AggregateVolume => throw new NotSupportedException(); diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 5d015ca5ab..2edc36a770 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using JetBrains.Annotations; +using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -496,6 +497,38 @@ namespace osu.Game.Skinning public double Length => sample.Length; public Bindable PlaybackConcurrency => sample.PlaybackConcurrency; + public BindableNumber Volume => sample.Volume; + + public BindableNumber Balance => sample.Balance; + + public BindableNumber Frequency => sample.Frequency; + + public BindableNumber Tempo => sample.Tempo; + + public void BindAdjustments(IAggregateAudioAdjustment component) + { + sample.BindAdjustments(component); + } + + public void UnbindAdjustments(IAggregateAudioAdjustment component) + { + sample.UnbindAdjustments(component); + } + + public void AddAdjustment(AdjustableProperty type, IBindable adjustBindable) + { + sample.AddAdjustment(type, adjustBindable); + } + + public void RemoveAdjustment(AdjustableProperty type, IBindable adjustBindable) + { + sample.RemoveAdjustment(type, adjustBindable); + } + + public void RemoveAllAdjustments(AdjustableProperty type) + { + sample.RemoveAllAdjustments(type); + } } private IEnumerable getLegacyLookupNames(HitSampleInfo hitSample) diff --git a/osu.Game/Skinning/PoolableSkinnableSample.cs b/osu.Game/Skinning/PoolableSkinnableSample.cs index 9025fdbd0f..b12fbf90f3 100644 --- a/osu.Game/Skinning/PoolableSkinnableSample.cs +++ b/osu.Game/Skinning/PoolableSkinnableSample.cs @@ -165,6 +165,14 @@ namespace osu.Game.Skinning public BindableNumber Tempo => sampleContainer.Tempo; + public void BindAdjustments(IAggregateAudioAdjustment component) => sampleContainer.BindAdjustments(component); + + public void UnbindAdjustments(IAggregateAudioAdjustment component) => sampleContainer.UnbindAdjustments(component); + + public void AddAdjustment(AdjustableProperty type, IBindable adjustBindable) => sampleContainer.AddAdjustment(type, adjustBindable); + + public void RemoveAdjustment(AdjustableProperty type, IBindable adjustBindable) => sampleContainer.RemoveAdjustment(type, adjustBindable); + public void AddAdjustment(AdjustableProperty type, BindableNumber adjustBindable) => sampleContainer.AddAdjustment(type, adjustBindable); public void RemoveAdjustment(AdjustableProperty type, BindableNumber adjustBindable) => sampleContainer.RemoveAdjustment(type, adjustBindable); diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index b3db2d6558..c971517c7f 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -176,14 +176,19 @@ namespace osu.Game.Skinning public BindableNumber Tempo => samplesContainer.Tempo; - public void AddAdjustment(AdjustableProperty type, BindableNumber adjustBindable) - => samplesContainer.AddAdjustment(type, adjustBindable); + public void BindAdjustments(IAggregateAudioAdjustment component) => samplesContainer.BindAdjustments(component); - public void RemoveAdjustment(AdjustableProperty type, BindableNumber adjustBindable) - => samplesContainer.RemoveAdjustment(type, adjustBindable); + public void UnbindAdjustments(IAggregateAudioAdjustment component) => samplesContainer.UnbindAdjustments(component); - public void RemoveAllAdjustments(AdjustableProperty type) - => samplesContainer.RemoveAllAdjustments(type); + public void AddAdjustment(AdjustableProperty type, IBindable adjustBindable) => samplesContainer.AddAdjustment(type, adjustBindable); + + public void RemoveAdjustment(AdjustableProperty type, IBindable adjustBindable) => samplesContainer.RemoveAdjustment(type, adjustBindable); + + public void AddAdjustment(AdjustableProperty type, BindableNumber adjustBindable) => samplesContainer.AddAdjustment(type, adjustBindable); + + public void RemoveAdjustment(AdjustableProperty type, BindableNumber adjustBindable) => samplesContainer.RemoveAdjustment(type, adjustBindable); + + public void RemoveAllAdjustments(AdjustableProperty type) => samplesContainer.RemoveAllAdjustments(type); /// /// Whether any samples are currently playing. From 0bda9e4b794f16d108f234563746202bf3b8a160 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 22 Feb 2021 18:31:33 +0900 Subject: [PATCH 010/202] Implement some new methods --- osu.Game/Skinning/LegacySkin.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 2edc36a770..6bdc4575c9 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -529,6 +529,14 @@ namespace osu.Game.Skinning { sample.RemoveAllAdjustments(type); } + + public IBindable AggregateVolume => sample.AggregateVolume; + + public IBindable AggregateBalance => sample.AggregateBalance; + + public IBindable AggregateFrequency => sample.AggregateFrequency; + + public IBindable AggregateTempo => sample.AggregateTempo; } private IEnumerable getLegacyLookupNames(HitSampleInfo hitSample) From f48e017ac901bcd5387a87d53b2a5ab5dc771d06 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 22 Feb 2021 18:34:05 +0900 Subject: [PATCH 011/202] Move nested class to bottom of file --- osu.Game/Skinning/LegacySkin.cs | 78 ++++++++++++++++----------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 6bdc4575c9..571d65e28b 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -468,7 +468,45 @@ namespace osu.Game.Skinning return null; } - /// + private IEnumerable getLegacyLookupNames(HitSampleInfo hitSample) + { + var lookupNames = hitSample.LookupNames.SelectMany(getFallbackNames); + + if (!UseCustomSampleBanks && !string.IsNullOrEmpty(hitSample.Suffix)) + { + // for compatibility with stable, exclude the lookup names with the custom sample bank suffix, if they are not valid for use in this skin. + // using .EndsWith() is intentional as it ensures parity in all edge cases + // (see LegacyTaikoSampleInfo for an example of one - prioritising the taiko prefix should still apply, but the sample bank should not). + lookupNames = lookupNames.Where(name => !name.EndsWith(hitSample.Suffix, StringComparison.Ordinal)); + } + + foreach (var l in lookupNames) + yield return l; + + // also for compatibility, try falling back to non-bank samples (so-called "universal" samples) as the last resort. + // going forward specifying banks shall always be required, even for elements that wouldn't require it on stable, + // which is why this is done locally here. + yield return hitSample.Name; + } + + private IEnumerable getFallbackNames(string componentName) + { + // May be something like "Gameplay/osu/approachcircle" from lazer, or "Arrows/note1" from a user skin. + yield return componentName; + + // Fall back to using the last piece for components coming from lazer (e.g. "Gameplay/osu/approachcircle" -> "approachcircle"). + string lastPiece = componentName.Split('/').Last(); + yield return componentName.StartsWith("Gameplay/taiko/", StringComparison.Ordinal) ? "taiko-" + lastPiece : lastPiece; + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + Textures?.Dispose(); + Samples?.Dispose(); + } + + /// /// A sample wrapper which keeps a reference to the contained skin to avoid finalizer garbage collection of the managing SampleStore. /// private class LegacySkinSample : ISample @@ -538,43 +576,5 @@ namespace osu.Game.Skinning public IBindable AggregateTempo => sample.AggregateTempo; } - - private IEnumerable getLegacyLookupNames(HitSampleInfo hitSample) - { - var lookupNames = hitSample.LookupNames.SelectMany(getFallbackNames); - - if (!UseCustomSampleBanks && !string.IsNullOrEmpty(hitSample.Suffix)) - { - // for compatibility with stable, exclude the lookup names with the custom sample bank suffix, if they are not valid for use in this skin. - // using .EndsWith() is intentional as it ensures parity in all edge cases - // (see LegacyTaikoSampleInfo for an example of one - prioritising the taiko prefix should still apply, but the sample bank should not). - lookupNames = lookupNames.Where(name => !name.EndsWith(hitSample.Suffix, StringComparison.Ordinal)); - } - - foreach (var l in lookupNames) - yield return l; - - // also for compatibility, try falling back to non-bank samples (so-called "universal" samples) as the last resort. - // going forward specifying banks shall always be required, even for elements that wouldn't require it on stable, - // which is why this is done locally here. - yield return hitSample.Name; - } - - private IEnumerable getFallbackNames(string componentName) - { - // May be something like "Gameplay/osu/approachcircle" from lazer, or "Arrows/note1" from a user skin. - yield return componentName; - - // Fall back to using the last piece for components coming from lazer (e.g. "Gameplay/osu/approachcircle" -> "approachcircle"). - string lastPiece = componentName.Split('/').Last(); - yield return componentName.StartsWith("Gameplay/taiko/", StringComparison.Ordinal) ? "taiko-" + lastPiece : lastPiece; - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - Textures?.Dispose(); - Samples?.Dispose(); - } } } From f1302d16006b567343c12a2624eeb544c7eae9bf Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Thu, 11 Mar 2021 19:23:56 +0300 Subject: [PATCH 012/202] Update Microsoft.EntityFrameworkCore --- osu.Desktop/osu.Desktop.csproj | 7 +++++-- osu.Game/Database/OsuDbContext.cs | 3 --- osu.Game/osu.Game.csproj | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 3e0f0cb7f6..4af69c573d 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -27,8 +27,11 @@ - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index 2aae62edea..d27da50448 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -111,9 +111,6 @@ namespace osu.Game.Database { base.OnConfiguring(optionsBuilder); optionsBuilder - // this is required for the time being due to the way we are querying in places like BeatmapStore. - // if we ever move to having consumers file their own .Includes, or get eager loading support, this could be re-enabled. - .ConfigureWarnings(warnings => warnings.Ignore(CoreEventId.IncludeIgnoredWarning)) .UseSqlite(connectionString, sqliteOptions => sqliteOptions.CommandTimeout(10)) .UseLoggerFactory(logger.Value); } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 90c8b98f42..fa1b0a95c3 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,10 +24,10 @@ - - + + - + From 47b80d2474f6c2a371dd91aa48fda003a60e808e Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Thu, 11 Mar 2021 20:51:54 +0300 Subject: [PATCH 013/202] Workaround InvalidOperation exceptions --- osu.Game/Beatmaps/BeatmapManager.cs | 16 ++++++++++++++++ osu.Game/Database/ArchiveModelManager.cs | 8 +++++++- osu.Game/Skinning/SkinManager.cs | 12 ++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 29b3f5d3a3..3254f53574 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -174,6 +174,22 @@ namespace osu.Game.Beatmaps if (beatmapSet.Beatmaps.Any(b => b.BaseDifficulty == null)) throw new InvalidOperationException($"Cannot import {nameof(BeatmapInfo)} with null {nameof(BeatmapInfo.BaseDifficulty)}."); + var dbContext = ContextFactory.Get(); + + // Workaround System.InvalidOperationException + // The instance of entity type 'RulesetInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + foreach (var beatmap in beatmapSet.Beatmaps) + { + beatmap.Ruleset = dbContext.RulesetInfo.Find(beatmap.RulesetID); + } + + // Workaround System.InvalidOperationException + // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + foreach (var file in beatmapSet.Files) + { + file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); + } + // check if a set already exists with the same online id, delete if it does. if (beatmapSet.OnlineBeatmapSetID != null) { diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index d809dbcb01..fe2caaa0b7 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -462,6 +462,10 @@ namespace osu.Game.Database // Dereference the existing file info, since the file model will be removed. if (file.FileInfo != null) { + // Workaround System.InvalidOperationException + // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + file.FileInfo = usage.Context.FileInfo.Find(file.FileInfoID); + Files.Dereference(file.FileInfo); // This shouldn't be required, but here for safety in case the provided TModel is not being change tracked @@ -635,10 +639,12 @@ namespace osu.Game.Database { using (Stream s = reader.GetStream(file)) { + var fileInfo = files.Add(s); fileInfos.Add(new TFileModel { Filename = file.Substring(prefix.Length).ToStandardisedPath(), - FileInfo = files.Add(s) + FileInfo = fileInfo, + FileInfoID = fileInfo.ID }); } } diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index fcde9f041b..2bb27b60d6 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -142,6 +142,18 @@ namespace osu.Game.Skinning } } + protected override void PreImport(SkinInfo model) + { + var dbContext = ContextFactory.Get(); + + // Workaround System.InvalidOperationException + // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + foreach (var file in model.Files) + { + file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); + } + } + /// /// Retrieve a instance for the provided /// From c6c616f244eb08e664c04937234ffcd6dd84b9c0 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Thu, 11 Mar 2021 21:02:40 +0300 Subject: [PATCH 014/202] Actualize tests --- .../Multiplayer/TestSceneMultiplayerMatchSongSelect.cs | 1 + .../SongSelect/TestSceneBeatmapRecommendations.cs | 1 + .../Visual/SongSelect/TestScenePlaySongSelect.cs | 4 +++- osu.Game/Scoring/ScoreInfo.cs | 10 ++-------- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs index faa5d9e6fc..4a9eaa1842 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs @@ -56,6 +56,7 @@ namespace osu.Game.Tests.Visual.Multiplayer beatmaps.Add(new BeatmapInfo { Ruleset = rulesets.GetRuleset(i % 4), + RulesetID = i % 4, OnlineBeatmapID = beatmapId, Length = length, BPM = bpm, diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index 53a956c77c..223ace6ca5 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -186,6 +186,7 @@ namespace osu.Game.Tests.Visual.SongSelect Metadata = metadata, BaseDifficulty = new BeatmapDifficulty(), Ruleset = ruleset, + RulesetID = ruleset.ID.GetValueOrDefault(), StarDifficulty = difficultyIndex + 1, Version = $"SR{difficultyIndex + 1}" }).ToList() diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 35c6d62cb7..4b402d0c54 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -911,9 +911,11 @@ namespace osu.Game.Tests.Visual.SongSelect int length = RNG.Next(30000, 200000); double bpm = RNG.NextSingle(80, 200); + var ruleset = getRuleset(); beatmaps.Add(new BeatmapInfo { - Ruleset = getRuleset(), + Ruleset = ruleset, + RulesetID = ruleset.ID.GetValueOrDefault(), OnlineBeatmapID = beatmapId, Version = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})", Length = length, diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index f5192f3a40..c5ad43abba 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -73,7 +73,7 @@ namespace osu.Game.Scoring } set { - modsJson = null; + modsJson = JsonConvert.SerializeObject(value.Select(m => new DeserializedMod { Acronym = m.Acronym })); mods = value; } } @@ -88,13 +88,7 @@ namespace osu.Game.Scoring { get { - if (modsJson != null) - return modsJson; - - if (mods == null) - return null; - - return modsJson = JsonConvert.SerializeObject(mods.Select(m => new DeserializedMod { Acronym = m.Acronym })); + return modsJson; } set { From d2f943395d349e08ae8e5a72b1f2996ea0d1d539 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Thu, 11 Mar 2021 22:12:47 +0300 Subject: [PATCH 015/202] Hotfix importing scores from stable --- osu.Game/Scoring/ScoreManager.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 96ec9644b5..a97c516a1b 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -52,6 +52,23 @@ namespace osu.Game.Scoring this.configManager = configManager; } + protected override void PreImport(ScoreInfo model) + { + var dbContext = ContextFactory.Get(); + + // Workaround System.InvalidOperationException + // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + foreach (var file in model.Files) + { + file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); + } + + foreach (var file in model.Beatmap.BeatmapSet.Files) + { + file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); + } + } + protected override ScoreInfo CreateModel(ArchiveReader archive) { if (archive == null) From 5a4b0174b187e649b9fb739fafd076ea19817f23 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Thu, 11 Mar 2021 22:40:35 +0300 Subject: [PATCH 016/202] Ignore MultipleCollectionIncludeWarning --- osu.Game/Database/OsuDbContext.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index d27da50448..689f248de8 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -112,6 +112,7 @@ namespace osu.Game.Database base.OnConfiguring(optionsBuilder); optionsBuilder .UseSqlite(connectionString, sqliteOptions => sqliteOptions.CommandTimeout(10)) + .ConfigureWarnings(w => w.Ignore(RelationalEventId.MultipleCollectionIncludeWarning)) .UseLoggerFactory(logger.Value); } From a60ff80c04850c1f09ad9f741197378e691fc78d Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Fri, 12 Mar 2021 00:02:29 +0300 Subject: [PATCH 017/202] Use expression body in ModsJson get accessor --- osu.Game/Scoring/ScoreInfo.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index c5ad43abba..78101991f6 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -86,10 +86,7 @@ namespace osu.Game.Scoring [Column("Mods")] public string ModsJson { - get - { - return modsJson; - } + get => modsJson; set { modsJson = value; From b9b095ee75c960287a8636d06c07afddade6865f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Mar 2021 14:49:38 +0900 Subject: [PATCH 018/202] Local framework --- osu.Desktop.slnf | 6 ++++-- osu.Game/osu.Game.csproj | 4 +++- osu.iOS.props | 4 ++-- osu.sln | 42 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/osu.Desktop.slnf b/osu.Desktop.slnf index d2c14d321a..1e41d0af0e 100644 --- a/osu.Desktop.slnf +++ b/osu.Desktop.slnf @@ -15,7 +15,9 @@ "osu.Game.Tests\\osu.Game.Tests.csproj", "osu.Game.Tournament.Tests\\osu.Game.Tournament.Tests.csproj", "osu.Game.Tournament\\osu.Game.Tournament.csproj", - "osu.Game\\osu.Game.csproj" + "osu.Game\\osu.Game.csproj", + "../osu-framework/osu.Framework/osu.Framework.csproj", + "../osu-framework/osu.Framework/osu.Framework.NativeLibs.csproj" ] } -} \ No newline at end of file +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 90c8b98f42..f2fc1726cd 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,11 +29,13 @@ - + + + diff --git a/osu.iOS.props b/osu.iOS.props index ccd33bf88c..30df8c423e 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + diff --git a/osu.sln b/osu.sln index c9453359b1..4d0b3656e7 100644 --- a/osu.sln +++ b/osu.sln @@ -66,6 +66,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Benchmarks", "osu.Game.Benchmarks\osu.Game.Benchmarks.csproj", "{93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework", "..\osu-framework\osu.Framework\osu.Framework.csproj", "{7EBA330C-6DD9-4F30-9332-6542D86D5BE1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework.iOS", "..\osu-framework\osu.Framework.iOS\osu.Framework.iOS.csproj", "{7A6EEFF0-760C-4EE5-BB5E-101E7D013392}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework.NativeLibs", "..\osu-framework\osu.Framework.NativeLibs\osu.Framework.NativeLibs.csproj", "{500039B3-0706-40C3-B6E7-1FD9187644A5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -412,6 +418,42 @@ Global {93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}.Release|iPhone.Build.0 = Release|Any CPU {93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Debug|iPhone.Build.0 = Debug|Any CPU + {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Release|Any CPU.Build.0 = Release|Any CPU + {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Release|iPhone.ActiveCfg = Release|Any CPU + {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Release|iPhone.Build.0 = Release|Any CPU + {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Debug|iPhone.Build.0 = Debug|Any CPU + {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Release|Any CPU.Build.0 = Release|Any CPU + {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Release|iPhone.ActiveCfg = Release|Any CPU + {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Release|iPhone.Build.0 = Release|Any CPU + {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {500039B3-0706-40C3-B6E7-1FD9187644A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {500039B3-0706-40C3-B6E7-1FD9187644A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {500039B3-0706-40C3-B6E7-1FD9187644A5}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {500039B3-0706-40C3-B6E7-1FD9187644A5}.Debug|iPhone.Build.0 = Debug|Any CPU + {500039B3-0706-40C3-B6E7-1FD9187644A5}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {500039B3-0706-40C3-B6E7-1FD9187644A5}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {500039B3-0706-40C3-B6E7-1FD9187644A5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {500039B3-0706-40C3-B6E7-1FD9187644A5}.Release|Any CPU.Build.0 = Release|Any CPU + {500039B3-0706-40C3-B6E7-1FD9187644A5}.Release|iPhone.ActiveCfg = Release|Any CPU + {500039B3-0706-40C3-B6E7-1FD9187644A5}.Release|iPhone.Build.0 = Release|Any CPU + {500039B3-0706-40C3-B6E7-1FD9187644A5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {500039B3-0706-40C3-B6E7-1FD9187644A5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From a33ffd56b80878e02a0cf03cac93a22869af4787 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Mar 2021 12:43:04 +0900 Subject: [PATCH 019/202] Allow CreateSettingsControls to work with all bindables in target class --- .../Configuration/SettingSourceAttribute.cs | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs index cfce615130..a8cebb97b4 100644 --- a/osu.Game/Configuration/SettingSourceAttribute.cs +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Reflection; using JetBrains.Annotations; using osu.Framework.Bindables; +using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Overlays.Settings; @@ -61,9 +62,13 @@ namespace osu.Game.Configuration public static class SettingSourceExtensions { - public static IEnumerable CreateSettingsControls(this object obj) + public static IEnumerable CreateSettingsControls(this object obj) => createSettingsControls(obj, obj.GetOrderedSettingsSourceProperties()); + + public static IEnumerable CreateSettingsControlsFromAllBindables(this object obj) => createSettingsControls(obj, obj.GetSettingsSourcePropertiesFromBindables()); + + private static IEnumerable createSettingsControls(object obj, IEnumerable<(SettingSourceAttribute, PropertyInfo)> sourceAttribs) { - foreach (var (attr, property) in obj.GetOrderedSettingsSourceProperties()) + foreach (var (attr, property) in sourceAttribs) { object value = property.GetValue(obj); @@ -139,6 +144,30 @@ namespace osu.Game.Configuration } } + public static IEnumerable<(SettingSourceAttribute, PropertyInfo)> GetSettingsSourcePropertiesFromBindables(this object obj) + { + HashSet handledProperties = new HashSet(); + + // reverse and de-dupe properties to surface base class settings to the top of return order. + foreach (var type in obj.GetType().EnumerateBaseTypes().Reverse()) + { + foreach (var property in type.GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance)) + { + if (handledProperties.Contains(property.Name)) + continue; + + handledProperties.Add(property.Name); + + if (typeof(IBindable).IsAssignableFrom(property.PropertyType)) + { + var val = property.GetValue(obj); + string description = (val as IHasDescription)?.Description ?? string.Empty; + yield return (new SettingSourceAttribute(property.Name, description), property); + } + } + } + } + public static IEnumerable<(SettingSourceAttribute, PropertyInfo)> GetSettingsSourceProperties(this object obj) { foreach (var property in obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance)) From 4374e7da81cbece7dfed94b3478e22b19b5b49ca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Mar 2021 15:26:18 +0900 Subject: [PATCH 020/202] Convert bindable names to human readable sentences --- osu.Game/Configuration/SettingSourceAttribute.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs index a8cebb97b4..fe8886b52e 100644 --- a/osu.Game/Configuration/SettingSourceAttribute.cs +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using Humanizer; using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Extensions.TypeExtensions; @@ -162,7 +163,7 @@ namespace osu.Game.Configuration { var val = property.GetValue(obj); string description = (val as IHasDescription)?.Description ?? string.Empty; - yield return (new SettingSourceAttribute(property.Name, description), property); + yield return (new SettingSourceAttribute(property.Name.Humanize(), description), property); } } } From 6eadae8aaf56831d4ecfc19fb0f4dd1c0fdab2ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Mar 2021 18:35:42 +0900 Subject: [PATCH 021/202] Remove remnants of OsuTK --- osu.Desktop/OsuGameDesktop.cs | 22 +++++----------------- osu.Desktop/Program.cs | 2 +- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 5909b82c8f..b2487568ce 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -136,24 +136,12 @@ namespace osu.Desktop var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico"); - switch (host.Window) - { - // Legacy osuTK DesktopGameWindow - case OsuTKDesktopWindow desktopGameWindow: - desktopGameWindow.CursorState |= CursorState.Hidden; - desktopGameWindow.SetIconFromStream(iconStream); - desktopGameWindow.Title = Name; - desktopGameWindow.FileDrop += (_, e) => fileDrop(e.FileNames); - break; + var desktopWindow = (SDL2DesktopWindow)host.Window; - // SDL2 DesktopWindow - case SDL2DesktopWindow desktopWindow: - desktopWindow.CursorState |= CursorState.Hidden; - desktopWindow.SetIconFromStream(iconStream); - desktopWindow.Title = Name; - desktopWindow.DragDrop += f => fileDrop(new[] { f }); - break; - } + desktopWindow.CursorState |= CursorState.Hidden; + desktopWindow.SetIconFromStream(iconStream); + desktopWindow.Title = Name; + desktopWindow.DragDrop += f => fileDrop(new[] { f }); } private void fileDrop(string[] filePaths) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 6ca7079654..0c527ba881 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -24,7 +24,7 @@ namespace osu.Desktop var cwd = Environment.CurrentDirectory; bool useOsuTK = args.Contains("--tk"); - using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true, useOsuTK: useOsuTK)) + using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true)) { host.ExceptionThrown += handleException; From 3c21c83cc88e8a0613aeda4f6cb04069172c6777 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Mar 2021 18:36:28 +0900 Subject: [PATCH 022/202] Rename KeyboardSection to BindingSection --- .../Input/{KeyboardSettings.cs => BindingSettings.cs} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename osu.Game/Overlays/Settings/Sections/Input/{KeyboardSettings.cs => BindingSettings.cs} (70%) diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs similarity index 70% rename from osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs rename to osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs index db6f24a954..79c73863cf 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs @@ -5,17 +5,17 @@ using osu.Framework.Graphics; namespace osu.Game.Overlays.Settings.Sections.Input { - public class KeyboardSettings : SettingsSubsection + public class BindingSettings : SettingsSubsection { - protected override string Header => "Keyboard"; + protected override string Header => "Shortcut and gameplay bindings"; - public KeyboardSettings(KeyBindingPanel keyConfig) + public BindingSettings(KeyBindingPanel keyConfig) { Children = new Drawable[] { new SettingsButton { - Text = "Key configuration", + Text = "Configure", TooltipText = "change global shortcut keys and gameplay bindings", Action = keyConfig.ToggleVisibility }, From 8635abbc4a616771e7ed702140109737919a4dd5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Mar 2021 18:37:55 +0900 Subject: [PATCH 023/202] Add the ability to not get controls for disabled bindables --- osu.Game/Configuration/SettingSourceAttribute.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs index fe8886b52e..39d7fba32b 100644 --- a/osu.Game/Configuration/SettingSourceAttribute.cs +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -63,16 +63,21 @@ namespace osu.Game.Configuration public static class SettingSourceExtensions { - public static IEnumerable CreateSettingsControls(this object obj) => createSettingsControls(obj, obj.GetOrderedSettingsSourceProperties()); + public static IReadOnlyList CreateSettingsControls(this object obj, bool includeDisabled = true) => + createSettingsControls(obj, obj.GetOrderedSettingsSourceProperties(), includeDisabled).ToArray(); - public static IEnumerable CreateSettingsControlsFromAllBindables(this object obj) => createSettingsControls(obj, obj.GetSettingsSourcePropertiesFromBindables()); + public static IReadOnlyList CreateSettingsControlsFromAllBindables(this object obj, bool includeDisabled = true) => + createSettingsControls(obj, obj.GetSettingsSourcePropertiesFromBindables(), includeDisabled).ToArray(); - private static IEnumerable createSettingsControls(object obj, IEnumerable<(SettingSourceAttribute, PropertyInfo)> sourceAttribs) + private static IEnumerable createSettingsControls(object obj, IEnumerable<(SettingSourceAttribute, PropertyInfo)> sourceAttribs, bool includeDisabled = true) { foreach (var (attr, property) in sourceAttribs) { object value = property.GetValue(obj); + if ((value as IBindable)?.Disabled == true) + continue; + switch (value) { case BindableNumber bNumber: From 03230edcb11760ff457ce3667b3377e6aea30660 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Mar 2021 18:38:16 +0900 Subject: [PATCH 024/202] Update bindings settings to handle the new structure and show all handlers --- .../Settings/Sections/Input/MouseSettings.cs | 60 ++++++----------- .../Settings/Sections/InputSection.cs | 64 ++++++++++++++++++- 2 files changed, 82 insertions(+), 42 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index 3a78cff890..036c4edfba 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -1,11 +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.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Input.Handlers.Mouse; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Input; @@ -14,35 +14,39 @@ namespace osu.Game.Overlays.Settings.Sections.Input { public class MouseSettings : SettingsSubsection { + private readonly MouseHandler mouseHandler; + protected override string Header => "Mouse"; - private readonly BindableBool rawInputToggle = new BindableBool(); - - private Bindable configSensitivity; + private Bindable handlerSensitivity; private Bindable localSensitivity; - private Bindable ignoredInputHandlers; - private Bindable windowMode; private SettingsEnumDropdown confineMouseModeSetting; + private Bindable relativeMode; + + public MouseSettings(MouseHandler mouseHandler) + { + this.mouseHandler = mouseHandler; + } [BackgroundDependencyLoader] private void load(OsuConfigManager osuConfig, FrameworkConfigManager config) { // use local bindable to avoid changing enabled state of game host's bindable. - configSensitivity = config.GetBindable(FrameworkSetting.CursorSensitivity); - localSensitivity = configSensitivity.GetUnboundCopy(); + handlerSensitivity = mouseHandler.Sensitivity.GetBoundCopy(); + localSensitivity = handlerSensitivity.GetUnboundCopy(); + relativeMode = mouseHandler.UseRelativeMode.GetBoundCopy(); windowMode = config.GetBindable(FrameworkSetting.WindowMode); - ignoredInputHandlers = config.GetBindable(FrameworkSetting.IgnoredInputHandlers); Children = new Drawable[] { new SettingsCheckbox { - LabelText = "Raw input", - Current = rawInputToggle + LabelText = "High precision mouse", + Current = relativeMode }, new SensitivitySetting { @@ -76,7 +80,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input { base.LoadComplete(); - configSensitivity.BindValueChanged(val => + relativeMode.BindValueChanged(relative => localSensitivity.Disabled = !relative.NewValue, true); + + handlerSensitivity.BindValueChanged(val => { var disabled = localSensitivity.Disabled; @@ -85,7 +91,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input localSensitivity.Disabled = disabled; }, true); - localSensitivity.BindValueChanged(val => configSensitivity.Value = val.NewValue); + localSensitivity.BindValueChanged(val => handlerSensitivity.Value = val.NewValue); windowMode.BindValueChanged(mode => { @@ -102,32 +108,6 @@ namespace osu.Game.Overlays.Settings.Sections.Input confineMouseModeSetting.TooltipText = string.Empty; } }, true); - - if (RuntimeInfo.OS != RuntimeInfo.Platform.Windows) - { - rawInputToggle.Disabled = true; - localSensitivity.Disabled = true; - } - else - { - rawInputToggle.ValueChanged += enabled => - { - // this is temporary until we support per-handler settings. - const string raw_mouse_handler = @"OsuTKRawMouseHandler"; - const string standard_mouse_handlers = @"OsuTKMouseHandler MouseHandler"; - - ignoredInputHandlers.Value = enabled.NewValue ? standard_mouse_handlers : raw_mouse_handler; - }; - - ignoredInputHandlers.ValueChanged += handler => - { - bool raw = !handler.NewValue.Contains("Raw"); - rawInputToggle.Value = raw; - localSensitivity.Disabled = !raw; - }; - - ignoredInputHandlers.TriggerChange(); - } } private class SensitivitySetting : SettingsSlider @@ -141,7 +121,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private class SensitivitySlider : OsuSliderBar { - public override string TooltipText => Current.Disabled ? "enable raw input to adjust sensitivity" : $"{base.TooltipText}x"; + public override string TooltipText => Current.Disabled ? "enable high precision mouse to adjust sensitivity" : $"{base.TooltipText}x"; } } } diff --git a/osu.Game/Overlays/Settings/Sections/InputSection.cs b/osu.Game/Overlays/Settings/Sections/InputSection.cs index b43453f53d..107e37909c 100644 --- a/osu.Game/Overlays/Settings/Sections/InputSection.cs +++ b/osu.Game/Overlays/Settings/Sections/InputSection.cs @@ -1,28 +1,88 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Handlers; +using osu.Framework.Input.Handlers.Mouse; +using osu.Framework.Platform; +using osu.Game.Configuration; using osu.Game.Overlays.Settings.Sections.Input; namespace osu.Game.Overlays.Settings.Sections { public class InputSection : SettingsSection { + private readonly KeyBindingPanel keyConfig; + public override string Header => "Input"; + [Resolved] + private GameHost host { get; set; } + public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.Solid.Keyboard }; public InputSection(KeyBindingPanel keyConfig) + { + this.keyConfig = keyConfig; + } + + [BackgroundDependencyLoader] + private void load() { Children = new Drawable[] { - new MouseSettings(), - new KeyboardSettings(keyConfig), + new BindingSettings(keyConfig), }; + + foreach (var handler in host.AvailableInputHandlers) + { + var handlerSection = createSectionFor(handler); + + if (handlerSection != null) + Add(handlerSection); + } + } + + private SettingsSubsection createSectionFor(InputHandler handler) + { + var settingsControls = handler.CreateSettingsControlsFromAllBindables(false); + + if (settingsControls.Count == 0) + return null; + + SettingsSubsection section; + + switch (handler) + { + case MouseHandler mh: + section = new MouseSettings(mh); + break; + + default: + section = new HandlerSection(handler); + break; + } + + section.AddRange(settingsControls); + + return section; + } + + private class HandlerSection : SettingsSubsection + { + private readonly InputHandler handler; + + public HandlerSection(InputHandler handler) + { + this.handler = handler; + } + + protected override string Header => handler.Description; } } } From 3458dcc33aa07615dad66ba2b2ee643f874b5f22 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Mar 2021 18:40:38 +0900 Subject: [PATCH 025/202] Use whitelist to avoid exposing settings to user that shouldn't be --- .../Settings/Sections/InputSection.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/InputSection.cs b/osu.Game/Overlays/Settings/Sections/InputSection.cs index 107e37909c..e6aaa1ade9 100644 --- a/osu.Game/Overlays/Settings/Sections/InputSection.cs +++ b/osu.Game/Overlays/Settings/Sections/InputSection.cs @@ -5,6 +5,8 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Handlers; +using osu.Framework.Input.Handlers.Joystick; +using osu.Framework.Input.Handlers.Midi; using osu.Framework.Input.Handlers.Mouse; using osu.Framework.Platform; using osu.Game.Configuration; @@ -50,11 +52,6 @@ namespace osu.Game.Overlays.Settings.Sections private SettingsSubsection createSectionFor(InputHandler handler) { - var settingsControls = handler.CreateSettingsControlsFromAllBindables(false); - - if (settingsControls.Count == 0) - return null; - SettingsSubsection section; switch (handler) @@ -63,11 +60,21 @@ namespace osu.Game.Overlays.Settings.Sections section = new MouseSettings(mh); break; - default: + // whitelist the handlers which should be displayed to avoid any weird cases of users touching settings they shouldn't. + case JoystickHandler _: + case MidiHandler _: section = new HandlerSection(handler); break; + + default: + return null; } + var settingsControls = handler.CreateSettingsControlsFromAllBindables(false); + + if (settingsControls.Count == 0) + return null; + section.AddRange(settingsControls); return section; From 86164c027a052af679caeb9d1937828bde61df8e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Mar 2021 18:44:10 +0900 Subject: [PATCH 026/202] Update the method we use to reset input settings --- osu.Game/OsuGame.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b7398efdc2..4dd7f97a72 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -880,8 +880,7 @@ namespace osu.Game switch (action) { case GlobalAction.ResetInputSettings: - frameworkConfig.GetBindable(FrameworkSetting.IgnoredInputHandlers).SetDefault(); - frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity).SetDefault(); + Host.ResetInputHandlers(); frameworkConfig.GetBindable(FrameworkSetting.ConfineMouseMode).SetDefault(); return true; From d0644221ff74801457b2b17efa728de723ddea20 Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 9 Nov 2020 14:43:06 -0800 Subject: [PATCH 027/202] Add test showing toolbar behavior change --- .../Navigation/TestSceneScreenNavigation.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index fc49517cdf..f2bb518b2e 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -229,6 +229,35 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("settings displayed", () => Game.Settings.State.Value == Visibility.Visible); } + [Test] + public void TestToolbarHiddenByUser() + { + AddStep("Enter menu", () => InputManager.Key(Key.Enter)); + + AddUntilStep("Wait for toolbar to load", () => Game.Toolbar.IsLoaded); + + AddStep("Hide toolbar", () => + { + InputManager.PressKey(Key.ControlLeft); + InputManager.Key(Key.T); + InputManager.ReleaseKey(Key.ControlLeft); + }); + + pushEscape(); + + AddStep("Enter menu", () => InputManager.Key(Key.Enter)); + + AddAssert("Toolbar is hidden", () => Game.Toolbar.State.Value == Visibility.Hidden); + + AddStep("Enter song select", () => + { + InputManager.Key(Key.Enter); + InputManager.Key(Key.Enter); + }); + + AddAssert("Toolbar is hidden", () => Game.Toolbar.State.Value == Visibility.Hidden); + } + private void pushEscape() => AddStep("Press escape", () => InputManager.Key(Key.Escape)); From 6c0734a09ff87754eb91917623f717476ee400e6 Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 9 Nov 2020 14:45:20 -0800 Subject: [PATCH 028/202] Handle global action in toolbar instead of osugame --- osu.Game/OsuGame.cs | 4 ---- osu.Game/Overlays/Toolbar/Toolbar.cs | 22 ++++++++++++++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b7398efdc2..dd775888a1 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -885,10 +885,6 @@ namespace osu.Game frameworkConfig.GetBindable(FrameworkSetting.ConfineMouseMode).SetDefault(); return true; - case GlobalAction.ToggleToolbar: - Toolbar.ToggleVisibility(); - return true; - case GlobalAction.ToggleGameplayMouseButtons: LocalConfig.Set(OsuSetting.MouseDisableButtons, !LocalConfig.Get(OsuSetting.MouseDisableButtons)); return true; diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 0ccb22df3a..011f5a03c9 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -13,10 +13,12 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Input.Events; using osu.Game.Rulesets; +using osu.Framework.Input.Bindings; +using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { - public class Toolbar : VisibilityContainer + public class Toolbar : VisibilityContainer, IKeyBindingHandler { public const float HEIGHT = 40; public const float TOOLTIP_HEIGHT = 30; @@ -30,7 +32,7 @@ namespace osu.Game.Overlays.Toolbar protected readonly IBindable OverlayActivationMode = new Bindable(OverlayActivation.All); - // Toolbar components like RulesetSelector should receive keyboard input events even when the toolbar is hidden. + // Toolbar and its components need keyboard input even when hidden. public override bool PropagateNonPositionalInputSubTree => true; public Toolbar() @@ -164,5 +166,21 @@ namespace osu.Game.Overlays.Toolbar this.MoveToY(-DrawSize.Y, transition_time, Easing.OutQuint); this.FadeOut(transition_time, Easing.InQuint); } + + public bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.ToggleToolbar: + ToggleVisibility(); + return true; + } + + return false; + } + + public void OnReleased(GlobalAction action) + { + } } } From 62f2a823f6be7d27ec7c6db751193023927bb130 Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 9 Nov 2020 14:46:08 -0800 Subject: [PATCH 029/202] Hide toolbar forever when the user hides it --- osu.Game/OsuGame.cs | 2 +- osu.Game/Overlays/Toolbar/Toolbar.cs | 6 ++++++ osu.Game/Screens/Menu/ButtonSystem.cs | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index dd775888a1..fa9a0d4eb5 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -983,7 +983,7 @@ namespace osu.Game if (newOsuScreen.HideOverlaysOnEnter) CloseAllOverlays(); - else + else if (!Toolbar.HiddenByUser) Toolbar.Show(); if (newOsuScreen.AllowBackButton) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 011f5a03c9..7f77e5add9 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -23,6 +23,8 @@ namespace osu.Game.Overlays.Toolbar public const float HEIGHT = 40; public const float TOOLTIP_HEIGHT = 30; + public bool HiddenByUser; + public Action OnHome; private ToolbarUserButton userButton; @@ -169,10 +171,14 @@ namespace osu.Game.Overlays.Toolbar public bool OnPressed(GlobalAction action) { + if (OverlayActivationMode.Value == OverlayActivation.Disabled) + return false; + switch (action) { case GlobalAction.ToggleToolbar: ToggleVisibility(); + HiddenByUser = State.Value == Visibility.Hidden; return true; } diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 81b1cb0bf1..8f1fd627f5 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -352,7 +352,8 @@ namespace osu.Game.Screens.Menu if (impact) logo.Impact(); - game?.Toolbar.Show(); + if (game?.Toolbar.HiddenByUser == false) + game.Toolbar.Show(); }, 200); break; From 5999e4ba3361f54828eaa25f1539ca0e2b991279 Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 9 Nov 2020 15:16:35 -0800 Subject: [PATCH 030/202] Add xmldoc for hiddenbyuser bool --- osu.Game/Overlays/Toolbar/Toolbar.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 7f77e5add9..483d82200b 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -23,6 +23,9 @@ namespace osu.Game.Overlays.Toolbar public const float HEIGHT = 40; public const float TOOLTIP_HEIGHT = 30; + /// + /// Whether the user hid this with . + /// public bool HiddenByUser; public Action OnHome; From 0ba5312a4063e3308c666e17ab4590bfe2f38267 Mon Sep 17 00:00:00 2001 From: Joehu Date: Sat, 13 Mar 2021 00:05:26 -0800 Subject: [PATCH 031/202] Move blocking show logic to UpdateState --- osu.Game/OsuGame.cs | 2 +- osu.Game/Overlays/Toolbar/Toolbar.cs | 12 +++++++++--- osu.Game/Screens/Menu/ButtonSystem.cs | 3 +-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index fa9a0d4eb5..dd775888a1 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -983,7 +983,7 @@ namespace osu.Game if (newOsuScreen.HideOverlaysOnEnter) CloseAllOverlays(); - else if (!Toolbar.HiddenByUser) + else Toolbar.Show(); if (newOsuScreen.AllowBackButton) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 483d82200b..7497f4d210 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -26,7 +26,9 @@ namespace osu.Game.Overlays.Toolbar /// /// Whether the user hid this with . /// - public bool HiddenByUser; + private bool hiddenByUser; + + private bool userToggled; public Action OnHome; @@ -149,7 +151,9 @@ namespace osu.Game.Overlays.Toolbar protected override void UpdateState(ValueChangedEvent state) { - if (state.NewValue == Visibility.Visible && OverlayActivationMode.Value == OverlayActivation.Disabled) + var blockShow = !userToggled && hiddenByUser; + + if (state.NewValue == Visibility.Visible && (OverlayActivationMode.Value == OverlayActivation.Disabled || blockShow)) { State.Value = Visibility.Hidden; return; @@ -180,8 +184,10 @@ namespace osu.Game.Overlays.Toolbar switch (action) { case GlobalAction.ToggleToolbar: + userToggled = true; ToggleVisibility(); - HiddenByUser = State.Value == Visibility.Hidden; + hiddenByUser = State.Value == Visibility.Hidden; + userToggled = false; return true; } diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 8f1fd627f5..81b1cb0bf1 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -352,8 +352,7 @@ namespace osu.Game.Screens.Menu if (impact) logo.Impact(); - if (game?.Toolbar.HiddenByUser == false) - game.Toolbar.Show(); + game?.Toolbar.Show(); }, 200); break; From b13f193c8dd60573aa579bb47b06d1d02ed0b5ef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 13 Mar 2021 19:26:38 +0900 Subject: [PATCH 032/202] Fix incorrect task being returned for changelog continuations --- osu.Game/Overlays/ChangelogOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index 2da5be5e6c..eda7748367 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -163,7 +163,7 @@ namespace osu.Game.Overlays await API.PerformAsync(req).ConfigureAwait(false); return tcs.Task; - }); + }).Unwrap(); } private CancellationTokenSource loadContentCancellation; From 4afbccfcff2544fa4a4e80d765a2bccb270c1658 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 13 Mar 2021 19:30:40 +0900 Subject: [PATCH 033/202] Fix initial operation potentially running before DI is completed --- osu.Game/Overlays/ChangelogOverlay.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index eda7748367..e7d68853ad 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -21,6 +21,8 @@ namespace osu.Game.Overlays { public class ChangelogOverlay : OnlineOverlay { + public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; + public readonly Bindable Current = new Bindable(); private Sample sampleBack; @@ -126,8 +128,11 @@ namespace osu.Game.Overlays private Task initialFetchTask; - private void performAfterFetch(Action action) => fetchListing()?.ContinueWith(_ => - Schedule(action), TaskContinuationOptions.OnlyOnRanToCompletion); + private void performAfterFetch(Action action) => Schedule(() => + { + fetchListing()?.ContinueWith(_ => + Schedule(action), TaskContinuationOptions.OnlyOnRanToCompletion); + }); private Task fetchListing() { From e70ba2d005c45bb485d3c985873fbecf99a1c71c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 13 Mar 2021 23:29:01 +0900 Subject: [PATCH 034/202] Remove unnecessary second variable --- osu.Game/Overlays/Toolbar/Toolbar.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 7497f4d210..5e2280e2fc 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -28,8 +28,6 @@ namespace osu.Game.Overlays.Toolbar /// private bool hiddenByUser; - private bool userToggled; - public Action OnHome; private ToolbarUserButton userButton; @@ -151,9 +149,9 @@ namespace osu.Game.Overlays.Toolbar protected override void UpdateState(ValueChangedEvent state) { - var blockShow = !userToggled && hiddenByUser; + bool blockShow = hiddenByUser || OverlayActivationMode.Value == OverlayActivation.Disabled; - if (state.NewValue == Visibility.Visible && (OverlayActivationMode.Value == OverlayActivation.Disabled || blockShow)) + if (state.NewValue == Visibility.Visible && blockShow) { State.Value = Visibility.Hidden; return; @@ -184,10 +182,8 @@ namespace osu.Game.Overlays.Toolbar switch (action) { case GlobalAction.ToggleToolbar: - userToggled = true; + hiddenByUser = State.Value == Visibility.Visible; // set before toggling to allow the operation to always succeed. ToggleVisibility(); - hiddenByUser = State.Value == Visibility.Hidden; - userToggled = false; return true; } From a227b0a581103004c10e767f0511f01bb8af2fbc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 13 Mar 2021 23:29:47 +0900 Subject: [PATCH 035/202] Build on xmldoc with rationale --- osu.Game/Overlays/Toolbar/Toolbar.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 5e2280e2fc..d049c2d3ec 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -25,6 +25,7 @@ namespace osu.Game.Overlays.Toolbar /// /// Whether the user hid this with . + /// In this state, automatic toggles should not occur, respecting the user's preference to have no toolbar. /// private bool hiddenByUser; From 8b74666cc339416b2b3f443c8dfdbb518c2f2148 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 14 Mar 2021 15:51:38 +0100 Subject: [PATCH 036/202] Add support for pooling explosions in taiko --- .../Skinning/TestSceneHitExplosion.cs | 4 +- .../Skinning/Legacy/LegacyHitExplosion.cs | 51 +++++------- .../UI/DefaultHitExplosion.cs | 41 +++++++--- osu.Game.Rulesets.Taiko/UI/HitExplosion.cs | 80 ++++++++++++++----- .../UI/HitExplosionPool.cs | 24 ++++++ osu.Game.Rulesets.Taiko/UI/IHitExplosion.cs | 23 ++++++ osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 11 ++- 7 files changed, 170 insertions(+), 64 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/UI/HitExplosionPool.cs create mode 100644 osu.Game.Rulesets.Taiko/UI/IHitExplosion.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs index fecb5d4a74..ba6e04c92e 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs @@ -38,11 +38,11 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning // the hit needs to be added to hierarchy in order for nested objects to be created correctly. // setting zero alpha is supposed to prevent the test from looking broken. hit.With(h => h.Alpha = 0), - new HitExplosion(hit, hit.Type) + new HitExplosion(hit.Type) { Anchor = Anchor.Centre, Origin = Anchor.Centre, - } + }.With(explosion => explosion.Apply(hit)) } }; } diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs index 651cdd6438..9734e12413 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs @@ -1,22 +1,24 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Game.Rulesets.Taiko.UI; namespace osu.Game.Rulesets.Taiko.Skinning.Legacy { - public class LegacyHitExplosion : CompositeDrawable + public class LegacyHitExplosion : CompositeDrawable, IHitExplosion { - private readonly Drawable sprite; - private readonly Drawable strongSprite; + public override bool RemoveWhenNotAlive => false; - private DrawableStrongNestedHit nestedStrongHit; - private bool switchedToStrongSprite; + private readonly Drawable sprite; + + [CanBeNull] + private readonly Drawable strongSprite; /// /// Creates a new legacy hit explosion. @@ -27,14 +29,14 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy /// /// The normal legacy explosion sprite. /// The strong legacy explosion sprite. - public LegacyHitExplosion(Drawable sprite, Drawable strongSprite = null) + public LegacyHitExplosion(Drawable sprite, [CanBeNull] Drawable strongSprite = null) { this.sprite = sprite; this.strongSprite = strongSprite; } [BackgroundDependencyLoader] - private void load(DrawableHitObject judgedObject) + private void load() { Anchor = Anchor.Centre; Origin = Anchor.Centre; @@ -56,17 +58,15 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy s.Origin = Anchor.Centre; })); } - - if (judgedObject is DrawableHit hit) - nestedStrongHit = hit.NestedHitObjects.SingleOrDefault() as DrawableStrongNestedHit; } - protected override void LoadComplete() + public void Animate(DrawableHitObject drawableHitObject) { - base.LoadComplete(); - const double animation_time = 120; + (sprite as IFramedAnimation)?.GotoFrame(0); + (strongSprite as IFramedAnimation)?.GotoFrame(0); + this.FadeInFromZero(animation_time).Then().FadeOut(animation_time * 1.5); this.ScaleTo(0.6f) @@ -77,24 +77,13 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy Expire(true); } - protected override void Update() + public void AnimateSecondHit() { - base.Update(); + if (strongSprite == null) + return; - if (shouldSwitchToStrongSprite() && !switchedToStrongSprite) - { - sprite.FadeOut(50, Easing.OutQuint); - strongSprite.FadeIn(50, Easing.OutQuint); - switchedToStrongSprite = true; - } - } - - private bool shouldSwitchToStrongSprite() - { - if (nestedStrongHit == null || strongSprite == null) - return false; - - return nestedStrongHit.IsHit; + sprite.FadeOut(50, Easing.OutQuint); + strongSprite.FadeIn(50, Easing.OutQuint); } } } diff --git a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs index 3bd20e4bb4..2519573ce9 100644 --- a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.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.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -13,19 +14,25 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Taiko.UI { - internal class DefaultHitExplosion : CircularContainer + internal class DefaultHitExplosion : CircularContainer, IHitExplosion { - private readonly DrawableHitObject judgedObject; + public override bool RemoveWhenNotAlive => false; + private readonly HitResult result; - public DefaultHitExplosion(DrawableHitObject judgedObject, HitResult result) + [CanBeNull] + private Box body; + + [Resolved] + private OsuColour colours { get; set; } + + public DefaultHitExplosion(HitResult result) { - this.judgedObject = judgedObject; this.result = result; } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { RelativeSizeAxes = Axes.Both; @@ -40,26 +47,38 @@ namespace osu.Game.Rulesets.Taiko.UI if (!result.IsHit()) return; - bool isRim = (judgedObject.HitObject as Hit)?.Type == HitType.Rim; - InternalChildren = new[] { - new Box + body = new Box { RelativeSizeAxes = Axes.Both, - Colour = isRim ? colours.BlueDarker : colours.PinkDarker, } }; + + updateColour(); } - protected override void LoadComplete() + private void updateColour([CanBeNull] DrawableHitObject judgedObject = null) { - base.LoadComplete(); + if (body == null) + return; + + bool isRim = (judgedObject?.HitObject as Hit)?.Type == HitType.Rim; + body.Colour = isRim ? colours.BlueDarker : colours.PinkDarker; + } + + public void Animate(DrawableHitObject drawableHitObject) + { + updateColour(drawableHitObject); this.ScaleTo(3f, 1000, Easing.OutQuint); this.FadeOut(500); Expire(true); } + + public void AnimateSecondHit() + { + } } } diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs index d1fb3348b9..d2ae36a03e 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs @@ -2,10 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; +using JetBrains.Annotations; using osuTK; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Pooling; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects; @@ -16,31 +18,35 @@ namespace osu.Game.Rulesets.Taiko.UI /// /// A circle explodes from the hit target to indicate a hitobject has been hit. /// - internal class HitExplosion : CircularContainer + internal class HitExplosion : PoolableDrawable { public override bool RemoveWhenNotAlive => true; - - [Cached(typeof(DrawableHitObject))] - public readonly DrawableHitObject JudgedObject; + public override bool RemoveCompletedTransforms => false; private readonly HitResult result; + [CanBeNull] + public DrawableHitObject JudgedObject; + private SkinnableDrawable skinnable; - public override double LifetimeStart => skinnable.Drawable.LifetimeStart; - - public override double LifetimeEnd => skinnable.Drawable.LifetimeEnd; - - public HitExplosion(DrawableHitObject judgedObject, HitResult result) + /// + /// This constructor only exists to meet the new() type constraint of . + /// + public HitExplosion() + : this(HitResult.Great) + { + } + + public HitExplosion(HitResult result) { - JudgedObject = judgedObject; this.result = result; Anchor = Anchor.Centre; Origin = Anchor.Centre; - RelativeSizeAxes = Axes.Both; Size = new Vector2(TaikoHitObject.DEFAULT_SIZE); + RelativeSizeAxes = Axes.Both; RelativePositionAxes = Axes.Both; } @@ -48,7 +54,44 @@ namespace osu.Game.Rulesets.Taiko.UI [BackgroundDependencyLoader] private void load() { - Child = skinnable = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(result)), _ => new DefaultHitExplosion(JudgedObject, result)); + InternalChild = skinnable = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(result)), _ => new DefaultHitExplosion(result)); + skinnable.OnSkinChanged += runAnimation; + } + + public void Apply([CanBeNull] DrawableHitObject drawableHitObject) + { + JudgedObject = drawableHitObject; + } + + protected override void PrepareForUse() + { + base.PrepareForUse(); + runAnimation(); + } + + protected override void FreeAfterUse() + { + base.FreeAfterUse(); + + // clean up transforms on free instead of on prepare as is usually the case + // to avoid potentially overriding the effects of VisualiseSecondHit() in the case it is called before PrepareForUse(). + ApplyTransformsAt(double.MinValue, true); + ClearTransforms(true); + } + + private void runAnimation() + { + if (JudgedObject?.Result == null) + return; + + double resultTime = JudgedObject.Result.TimeAbsolute; + + LifetimeStart = resultTime; + + using (BeginAbsoluteSequence(resultTime)) + (skinnable.Drawable as IHitExplosion)?.Animate(JudgedObject); + + LifetimeEnd = skinnable.Drawable.LatestTransformEndTime; } private static TaikoSkinComponents getComponentName(HitResult result) @@ -68,12 +111,13 @@ namespace osu.Game.Rulesets.Taiko.UI throw new ArgumentOutOfRangeException(nameof(result), $"Invalid result type: {result}"); } - /// - /// Transforms this hit explosion to visualise a secondary hit. - /// - public void VisualiseSecondHit() + public void VisualiseSecondHit(JudgementResult judgementResult) { - this.ResizeTo(new Vector2(TaikoStrongableHitObject.DEFAULT_STRONG_SIZE), 50); + using (BeginAbsoluteSequence(judgementResult.TimeAbsolute)) + { + this.ResizeTo(new Vector2(TaikoStrongableHitObject.DEFAULT_STRONG_SIZE), 50); + (skinnable.Drawable as IHitExplosion)?.AnimateSecondHit(); + } } } } diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosionPool.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosionPool.cs new file mode 100644 index 0000000000..badf34554c --- /dev/null +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosionPool.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Pooling; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Taiko.UI +{ + /// + /// Pool for hit explosions of a specific type. + /// + internal class HitExplosionPool : DrawablePool + { + private readonly HitResult hitResult; + + public HitExplosionPool(HitResult hitResult) + : base(15) + { + this.hitResult = hitResult; + } + + protected override HitExplosion CreateNewDrawable() => new HitExplosion(hitResult); + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/IHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/IHitExplosion.cs new file mode 100644 index 0000000000..7af941d1ba --- /dev/null +++ b/osu.Game.Rulesets.Taiko/UI/IHitExplosion.cs @@ -0,0 +1,23 @@ +// 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.Drawables; + +namespace osu.Game.Rulesets.Taiko.UI +{ + /// + /// Interface for hit explosions shown on the playfield's hit target in taiko. + /// + public interface IHitExplosion + { + /// + /// Shows the hit explosion for the supplied . + /// + void Animate(DrawableHitObject drawableHitObject); + + /// + /// Transforms the hit explosion to visualise a secondary hit. + /// + void AnimateSecondHit(); + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index d2e7b604bb..46dafc3a30 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Taiko.UI private SkinnableDrawable mascot; private readonly IDictionary> judgementPools = new Dictionary>(); + private readonly IDictionary explosionPools = new Dictionary(); private ProxyContainer topLevelHitContainer; private Container rightArea; @@ -166,10 +167,15 @@ namespace osu.Game.Rulesets.Taiko.UI RegisterPool(100); var hitWindows = new TaikoHitWindows(); + foreach (var result in Enum.GetValues(typeof(HitResult)).OfType().Where(r => hitWindows.IsHitResultAllowed(r))) + { judgementPools.Add(result, new DrawablePool(15)); + explosionPools.Add(result, new HitExplosionPool(result)); + } AddRangeInternal(judgementPools.Values); + AddRangeInternal(explosionPools.Values); } protected override void LoadComplete() @@ -281,7 +287,7 @@ namespace osu.Game.Rulesets.Taiko.UI { case TaikoStrongJudgement _: if (result.IsHit) - hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).ParentHitObject)?.VisualiseSecondHit(); + hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).ParentHitObject)?.VisualiseSecondHit(result); break; case TaikoDrumRollTickJudgement _: @@ -315,7 +321,8 @@ namespace osu.Game.Rulesets.Taiko.UI private void addExplosion(DrawableHitObject drawableObject, HitResult result, HitType type) { - hitExplosionContainer.Add(new HitExplosion(drawableObject, result)); + hitExplosionContainer.Add(explosionPools[result] + .Get(explosion => explosion.Apply(drawableObject))); if (drawableObject.HitObject.Kiai) kiaiExplosionContainer.Add(new KiaiHitExplosion(drawableObject, type)); } From 00306c007529f176d92666f445a8ef7219787cea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 14 Mar 2021 15:56:34 +0100 Subject: [PATCH 037/202] Adjust test code after explosion pooling changes --- .../Skinning/TestSceneHitExplosion.cs | 19 ++++++++++++++----- .../TestSceneHits.cs | 10 +++++----- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs index ba6e04c92e..61ea8b664d 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs @@ -2,8 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +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.Taiko.UI; @@ -13,6 +17,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [TestFixture] public class TestSceneHitExplosion : TaikoSkinnableTestScene { + protected override double TimePerAction => 100; + [Test] public void TestNormalHit() { @@ -21,11 +27,14 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning AddStep("Miss", () => SetContents(() => getContentFor(createHit(HitResult.Miss)))); } - [Test] - public void TestStrongHit([Values(false, true)] bool hitBoth) + [TestCase(HitResult.Great)] + [TestCase(HitResult.Ok)] + public void TestStrongHit(HitResult type) { - AddStep("Great", () => SetContents(() => getContentFor(createStrongHit(HitResult.Great, hitBoth)))); - AddStep("Good", () => SetContents(() => getContentFor(createStrongHit(HitResult.Ok, hitBoth)))); + AddStep("create hit", () => SetContents(() => getContentFor(createStrongHit(type)))); + AddStep("visualise second hit", + () => this.ChildrenOfType() + .ForEach(e => e.VisualiseSecondHit(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement())))); } private Drawable getContentFor(DrawableTestHit hit) @@ -49,6 +58,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning private DrawableTestHit createHit(HitResult type) => new DrawableTestHit(new Hit { StartTime = Time.Current }, type); - private DrawableTestHit createStrongHit(HitResult type, bool hitBoth) => new DrawableTestStrongHit(Time.Current, type, hitBoth); + private DrawableTestHit createStrongHit(HitResult type) => new DrawableTestStrongHit(Time.Current, type); } } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs index 7695ca067b..87c936d386 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs @@ -11,7 +11,6 @@ using osu.Game.Audio; 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; using osu.Game.Rulesets.Taiko.Objects; @@ -108,12 +107,12 @@ namespace osu.Game.Rulesets.Taiko.Tests { HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Ok : HitResult.Great; - Hit hit = new Hit(); + Hit hit = new Hit { StartTime = DrawableRuleset.Playfield.Time.Current }; var h = new DrawableTestHit(hit, kiai: kiai) { X = RNG.NextSingle(hitResult == HitResult.Ok ? -0.1f : -0.05f, hitResult == HitResult.Ok ? 0.1f : 0.05f) }; DrawableRuleset.Playfield.Add(h); - ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult }); + ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(hit, new TaikoJudgement()) { Type = hitResult }); } private void addStrongHitJudgement(bool kiai) @@ -122,6 +121,7 @@ namespace osu.Game.Rulesets.Taiko.Tests Hit hit = new Hit { + StartTime = DrawableRuleset.Playfield.Time.Current, IsStrong = true, Samples = createSamples(strong: true) }; @@ -129,8 +129,8 @@ namespace osu.Game.Rulesets.Taiko.Tests DrawableRuleset.Playfield.Add(h); - ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult }); - ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h.NestedHitObjects.Single(), new JudgementResult(new HitObject(), new TaikoStrongJudgement()) { Type = HitResult.Great }); + ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(hit, new TaikoJudgement()) { Type = hitResult }); + ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h.NestedHitObjects.Single(), new JudgementResult(hit.NestedHitObjects.Single(), new TaikoStrongJudgement()) { Type = HitResult.Great }); } private void addMissJudgement() From 0a1e325fc774f371785ba95b618f71bd0637bb2e Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Sun, 14 Mar 2021 19:34:53 +0300 Subject: [PATCH 038/202] Extract requerying of navigational properties from DbContext --- osu.Game/Beatmaps/BeatmapManager.cs | 16 +---------- osu.Game/Database/Extensions.cs | 44 +++++++++++++++++++++++++++++ osu.Game/Scoring/ScoreManager.cs | 14 +-------- osu.Game/Skinning/SkinManager.cs | 9 +----- 4 files changed, 47 insertions(+), 36 deletions(-) create mode 100644 osu.Game/Database/Extensions.cs diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 3254f53574..f42fba79cb 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -174,21 +174,7 @@ namespace osu.Game.Beatmaps if (beatmapSet.Beatmaps.Any(b => b.BaseDifficulty == null)) throw new InvalidOperationException($"Cannot import {nameof(BeatmapInfo)} with null {nameof(BeatmapInfo.BaseDifficulty)}."); - var dbContext = ContextFactory.Get(); - - // Workaround System.InvalidOperationException - // The instance of entity type 'RulesetInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. - foreach (var beatmap in beatmapSet.Beatmaps) - { - beatmap.Ruleset = dbContext.RulesetInfo.Find(beatmap.RulesetID); - } - - // Workaround System.InvalidOperationException - // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. - foreach (var file in beatmapSet.Files) - { - file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); - } + beatmapSet.Requery(ContextFactory); // check if a set already exists with the same online id, delete if it does. if (beatmapSet.OnlineBeatmapSetID != null) diff --git a/osu.Game/Database/Extensions.cs b/osu.Game/Database/Extensions.cs new file mode 100644 index 0000000000..3af26c348e --- /dev/null +++ b/osu.Game/Database/Extensions.cs @@ -0,0 +1,44 @@ +// 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.Scoring; + +namespace osu.Game.Database +{ + public static class Extensions + { + public static void Requery(this BeatmapSetInfo beatmapSetInfo, IDatabaseContextFactory databaseContextFactory) + { + var dbContext = databaseContextFactory.Get(); + + foreach (var beatmap in beatmapSetInfo.Beatmaps) + { + // Workaround System.InvalidOperationException + // The instance of entity type 'RulesetInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + beatmap.Ruleset = dbContext.RulesetInfo.Find(beatmap.RulesetID); + } + + beatmapSetInfo.Files.Requery(databaseContextFactory); + } + + public static void Requery(this ScoreInfo scoreInfo, IDatabaseContextFactory databaseContextFactory) + { + scoreInfo.Files.Requery(databaseContextFactory); + scoreInfo.Beatmap.BeatmapSet.Files.Requery(databaseContextFactory); + } + + public static void Requery(this List files, IDatabaseContextFactory databaseContextFactory) where T : class, INamedFileInfo + { + var dbContext = databaseContextFactory.Get(); + + foreach (var file in files) + { + // Workaround System.InvalidOperationException + // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); + } + } + } +} diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index a97c516a1b..1e90ee1ac7 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -54,19 +54,7 @@ namespace osu.Game.Scoring protected override void PreImport(ScoreInfo model) { - var dbContext = ContextFactory.Get(); - - // Workaround System.InvalidOperationException - // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. - foreach (var file in model.Files) - { - file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); - } - - foreach (var file in model.Beatmap.BeatmapSet.Files) - { - file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); - } + model.Requery(ContextFactory); } protected override ScoreInfo CreateModel(ArchiveReader archive) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 2bb27b60d6..c25f00eccb 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -144,14 +144,7 @@ namespace osu.Game.Skinning protected override void PreImport(SkinInfo model) { - var dbContext = ContextFactory.Get(); - - // Workaround System.InvalidOperationException - // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. - foreach (var file in model.Files) - { - file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); - } + model.Files.Requery(ContextFactory); } /// From 61d5a6cc57941cab31e2abb0acee00c8fad3f80f Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Sun, 14 Mar 2021 19:47:14 +0300 Subject: [PATCH 039/202] Simplify Microsoft.EntityFrameworkCore.Design PackageReference --- osu.Desktop/osu.Desktop.csproj | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 4af69c573d..d9d23dea6b 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -28,10 +28,7 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + From 28ef64b62a5d873110a163c4275fb48c9c47b262 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Sun, 14 Mar 2021 21:43:27 +0300 Subject: [PATCH 040/202] Explicitly specify SingleQuery behavior --- osu.Game/Database/OsuDbContext.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index 689f248de8..e5ae530018 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -3,7 +3,6 @@ using System; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.Logging; using osu.Framework.Logging; using osu.Framework.Statistics; @@ -111,8 +110,7 @@ namespace osu.Game.Database { base.OnConfiguring(optionsBuilder); optionsBuilder - .UseSqlite(connectionString, sqliteOptions => sqliteOptions.CommandTimeout(10)) - .ConfigureWarnings(w => w.Ignore(RelationalEventId.MultipleCollectionIncludeWarning)) + .UseSqlite(connectionString, sqliteOptions => sqliteOptions.CommandTimeout(10).UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery)) .UseLoggerFactory(logger.Value); } From 900da7b891c3a74faa3ebbe0f030e9cf3350b273 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 12:42:41 +0900 Subject: [PATCH 041/202] Rename and refactor extenion methods to be easier to read --- .../Database/DatabaseWorkaroundExtensions.cs | 48 +++++++++++++++++++ osu.Game/Database/Extensions.cs | 44 ----------------- 2 files changed, 48 insertions(+), 44 deletions(-) create mode 100644 osu.Game/Database/DatabaseWorkaroundExtensions.cs delete mode 100644 osu.Game/Database/Extensions.cs diff --git a/osu.Game/Database/DatabaseWorkaroundExtensions.cs b/osu.Game/Database/DatabaseWorkaroundExtensions.cs new file mode 100644 index 0000000000..07ce7e8529 --- /dev/null +++ b/osu.Game/Database/DatabaseWorkaroundExtensions.cs @@ -0,0 +1,48 @@ +// 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.Scoring; + +namespace osu.Game.Database +{ + public static class DatabaseWorkaroundExtensions + { + public static void Requery(this IHasPrimaryKey model, IDatabaseContextFactory contextFactory) + { + switch (model) + { + case ScoreInfo scoreInfo: + scoreInfo.Beatmap.BeatmapSet.Requery(contextFactory); + scoreInfo.Files.RequeryFiles(contextFactory); + break; + + case BeatmapSetInfo beatmapSetInfo: + var context = contextFactory.Get(); + + foreach (var beatmap in beatmapSetInfo.Beatmaps) + { + // Workaround System.InvalidOperationException + // The instance of entity type 'RulesetInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + beatmap.Ruleset = context.RulesetInfo.Find(beatmap.RulesetID); + } + + requeryFiles(beatmapSetInfo.Files, contextFactory); + break; + } + } + + public static void RequeryFiles(this List files, IDatabaseContextFactory databaseContextFactory) where T : class, INamedFileInfo + { + var dbContext = databaseContextFactory.Get(); + + foreach (var file in files) + { + // Workaround System.InvalidOperationException + // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); + } + } + } +} diff --git a/osu.Game/Database/Extensions.cs b/osu.Game/Database/Extensions.cs deleted file mode 100644 index 3af26c348e..0000000000 --- a/osu.Game/Database/Extensions.cs +++ /dev/null @@ -1,44 +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.Scoring; - -namespace osu.Game.Database -{ - public static class Extensions - { - public static void Requery(this BeatmapSetInfo beatmapSetInfo, IDatabaseContextFactory databaseContextFactory) - { - var dbContext = databaseContextFactory.Get(); - - foreach (var beatmap in beatmapSetInfo.Beatmaps) - { - // Workaround System.InvalidOperationException - // The instance of entity type 'RulesetInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. - beatmap.Ruleset = dbContext.RulesetInfo.Find(beatmap.RulesetID); - } - - beatmapSetInfo.Files.Requery(databaseContextFactory); - } - - public static void Requery(this ScoreInfo scoreInfo, IDatabaseContextFactory databaseContextFactory) - { - scoreInfo.Files.Requery(databaseContextFactory); - scoreInfo.Beatmap.BeatmapSet.Files.Requery(databaseContextFactory); - } - - public static void Requery(this List files, IDatabaseContextFactory databaseContextFactory) where T : class, INamedFileInfo - { - var dbContext = databaseContextFactory.Get(); - - foreach (var file in files) - { - // Workaround System.InvalidOperationException - // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. - file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); - } - } - } -} From 2bdffd10044984ea0a5639754b8ecff8f4fc979b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 12:47:58 +0900 Subject: [PATCH 042/202] Move skin requery logic into extension methods --- .../Database/DatabaseWorkaroundExtensions.cs | 19 +++++++++++++++++-- osu.Game/Skinning/SkinManager.cs | 2 +- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/DatabaseWorkaroundExtensions.cs b/osu.Game/Database/DatabaseWorkaroundExtensions.cs index 07ce7e8529..39bf358071 100644 --- a/osu.Game/Database/DatabaseWorkaroundExtensions.cs +++ b/osu.Game/Database/DatabaseWorkaroundExtensions.cs @@ -1,21 +1,36 @@ // 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 osu.Game.Beatmaps; using osu.Game.Scoring; +using osu.Game.Skinning; namespace osu.Game.Database { + /// + /// Extension methods which contain workarounds to make EFcore 5.x work with our existing (incorrect) thread safety. + /// The intention is to avoid blocking package updates while we consider the future of the database backend, with a potential backend switch imminent. + /// public static class DatabaseWorkaroundExtensions { + /// + /// Re-query the provided model to ensure it is in a sane state. This method requires explicit implementation per model type. + /// + /// + /// public static void Requery(this IHasPrimaryKey model, IDatabaseContextFactory contextFactory) { switch (model) { + case SkinInfo skinInfo: + requeryFiles(skinInfo.Files, contextFactory); + break; + case ScoreInfo scoreInfo: scoreInfo.Beatmap.BeatmapSet.Requery(contextFactory); - scoreInfo.Files.RequeryFiles(contextFactory); + requeryFiles(scoreInfo.Files, contextFactory); break; case BeatmapSetInfo beatmapSetInfo: @@ -33,7 +48,7 @@ namespace osu.Game.Database } } - public static void RequeryFiles(this List files, IDatabaseContextFactory databaseContextFactory) where T : class, INamedFileInfo + private static void requeryFiles(List files, IDatabaseContextFactory databaseContextFactory) where T : class, INamedFileInfo { var dbContext = databaseContextFactory.Get(); diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index c25f00eccb..601b77e782 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -144,7 +144,7 @@ namespace osu.Game.Skinning protected override void PreImport(SkinInfo model) { - model.Files.Requery(ContextFactory); + model.Requery(ContextFactory); } /// From 8a3553388972349a6833afcf915f44a7dae19995 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 12:48:23 +0900 Subject: [PATCH 043/202] Add fall-through case to catch a potential requery for unsupported model type --- osu.Game/Database/DatabaseWorkaroundExtensions.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Database/DatabaseWorkaroundExtensions.cs b/osu.Game/Database/DatabaseWorkaroundExtensions.cs index 39bf358071..1d5c98ed8d 100644 --- a/osu.Game/Database/DatabaseWorkaroundExtensions.cs +++ b/osu.Game/Database/DatabaseWorkaroundExtensions.cs @@ -45,6 +45,9 @@ namespace osu.Game.Database requeryFiles(beatmapSetInfo.Files, contextFactory); break; + + default: + throw new ArgumentException($"{nameof(Requery)} does not have support for the provided model type", nameof(model)); } } From 1573298e682329d133059a74dd56c8c849b86bba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 13:12:10 +0900 Subject: [PATCH 044/202] Update remaining package references to point to efcore5 --- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.iOS.props | 2 -- 6 files changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index 728af5124e..42f70151ac 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -5,7 +5,7 @@ - + WinExe diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index af16f39563..e51b20c9fe 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -5,7 +5,7 @@ - + WinExe diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index 3d2d1f3fec..f1f75148ef 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -5,7 +5,7 @@ - + WinExe diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index fa00922706..c9a320bdd5 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -5,7 +5,7 @@ - + WinExe diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index e36b3cdc74..6f8e0fac6f 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index ccd33bf88c..71fcdd45f3 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -90,8 +90,6 @@ - - From 79d3379f55b2b23dbfdd613c58adc8a8b3259768 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 13:20:22 +0900 Subject: [PATCH 045/202] Reformat application of configuration --- osu.Game/Database/OsuDbContext.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index e5ae530018..2342ab07d4 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -110,7 +110,10 @@ namespace osu.Game.Database { base.OnConfiguring(optionsBuilder); optionsBuilder - .UseSqlite(connectionString, sqliteOptions => sqliteOptions.CommandTimeout(10).UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery)) + .UseSqlite(connectionString, + sqliteOptions => sqliteOptions + .CommandTimeout(10) + .UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery)) .UseLoggerFactory(logger.Value); } From 2904f479c65bf9f2cd76a3164ab51d13ff53dc96 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 13:26:14 +0900 Subject: [PATCH 046/202] Share file lookup workaround in ArchiveModelManager with workaround extensions class --- osu.Game/Database/ArchiveModelManager.cs | 4 +--- .../Database/DatabaseWorkaroundExtensions.cs | 23 +++++++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index fe2caaa0b7..31c365b478 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -462,9 +462,7 @@ namespace osu.Game.Database // Dereference the existing file info, since the file model will be removed. if (file.FileInfo != null) { - // Workaround System.InvalidOperationException - // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. - file.FileInfo = usage.Context.FileInfo.Find(file.FileInfoID); + file.Requery(usage.Context); Files.Dereference(file.FileInfo); diff --git a/osu.Game/Database/DatabaseWorkaroundExtensions.cs b/osu.Game/Database/DatabaseWorkaroundExtensions.cs index 1d5c98ed8d..8ac05f78e0 100644 --- a/osu.Game/Database/DatabaseWorkaroundExtensions.cs +++ b/osu.Game/Database/DatabaseWorkaroundExtensions.cs @@ -49,18 +49,23 @@ namespace osu.Game.Database default: throw new ArgumentException($"{nameof(Requery)} does not have support for the provided model type", nameof(model)); } + + void requeryFiles(List files, IDatabaseContextFactory databaseContextFactory) where T : class, INamedFileInfo + { + var dbContext = databaseContextFactory.Get(); + + foreach (var file in files) + { + Requery(file, dbContext); + } + } } - private static void requeryFiles(List files, IDatabaseContextFactory databaseContextFactory) where T : class, INamedFileInfo + public static void Requery(this INamedFileInfo file, OsuDbContext dbContext) { - var dbContext = databaseContextFactory.Get(); - - foreach (var file in files) - { - // Workaround System.InvalidOperationException - // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. - file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); - } + // Workaround System.InvalidOperationException + // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. + file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); } } } From fce21f23d687ab72be391c23987ad1edff8740f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 13:29:26 +0900 Subject: [PATCH 047/202] Add comments marking workarounds required for EFcore 5 --- .../Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs | 2 +- .../Visual/SongSelect/TestSceneBeatmapRecommendations.cs | 2 +- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 2 +- osu.Game/Database/ArchiveModelManager.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs index 4a9eaa1842..8cfe5d8af2 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs @@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual.Multiplayer beatmaps.Add(new BeatmapInfo { Ruleset = rulesets.GetRuleset(i % 4), - RulesetID = i % 4, + RulesetID = i % 4, // workaround for efcore 5 compatibility. OnlineBeatmapID = beatmapId, Length = length, BPM = bpm, diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index 223ace6ca5..9b8b74e6f6 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -186,7 +186,7 @@ namespace osu.Game.Tests.Visual.SongSelect Metadata = metadata, BaseDifficulty = new BeatmapDifficulty(), Ruleset = ruleset, - RulesetID = ruleset.ID.GetValueOrDefault(), + RulesetID = ruleset.ID.GetValueOrDefault(), // workaround for efcore 5 compatibility. StarDifficulty = difficultyIndex + 1, Version = $"SR{difficultyIndex + 1}" }).ToList() diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 4b402d0c54..2d192ae207 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -915,7 +915,7 @@ namespace osu.Game.Tests.Visual.SongSelect beatmaps.Add(new BeatmapInfo { Ruleset = ruleset, - RulesetID = ruleset.ID.GetValueOrDefault(), + RulesetID = ruleset.ID.GetValueOrDefault(), // workaround for efcore 5 compatibility. OnlineBeatmapID = beatmapId, Version = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})", Length = length, diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 31c365b478..64428882ac 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -642,7 +642,7 @@ namespace osu.Game.Database { Filename = file.Substring(prefix.Length).ToStandardisedPath(), FileInfo = fileInfo, - FileInfoID = fileInfo.ID + FileInfoID = fileInfo.ID // workaround for efcore 5 compatibility. }); } } From 6d4c1ba2aee43c9fdab49f83ba16c0bcdedcce78 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 13:35:08 +0900 Subject: [PATCH 048/202] Fix a couple of new inspections introduced in Rider EAPs --- osu.Game/Beatmaps/BeatmapConverter.cs | 10 +++++----- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 3 --- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs index cb0b3a8d09..b291edd19d 100644 --- a/osu.Game/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Beatmaps/BeatmapConverter.cs @@ -17,12 +17,12 @@ namespace osu.Game.Beatmaps public abstract class BeatmapConverter : IBeatmapConverter where T : HitObject { - private event Action> ObjectConverted; + private event Action> objectConverted; event Action> IBeatmapConverter.ObjectConverted { - add => ObjectConverted += value; - remove => ObjectConverted -= value; + add => objectConverted += value; + remove => objectConverted -= value; } public IBeatmap Beatmap { get; } @@ -92,10 +92,10 @@ namespace osu.Game.Beatmaps var converted = ConvertHitObject(obj, beatmap, cancellationToken); - if (ObjectConverted != null) + if (objectConverted != null) { converted = converted.ToList(); - ObjectConverted.Invoke(obj, converted); + objectConverted.Invoke(obj, converted); } foreach (var c in converted) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index df940e8c8e..d06478b9de 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -471,9 +471,6 @@ namespace osu.Game.Beatmaps.Formats private string toLegacyCustomSampleBank(HitSampleInfo hitSampleInfo) { - if (hitSampleInfo == null) - return "0"; - if (hitSampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy) return legacy.CustomSampleBank.ToString(CultureInfo.InvariantCulture); From 1e519f0d31125a3bb508be2dd97777556f69f0b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 14:20:59 +0900 Subject: [PATCH 049/202] Fix seemingly innocent logic change causing breakage in score imports --- osu.Game/Database/DatabaseWorkaroundExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/DatabaseWorkaroundExtensions.cs b/osu.Game/Database/DatabaseWorkaroundExtensions.cs index 8ac05f78e0..a3a982f232 100644 --- a/osu.Game/Database/DatabaseWorkaroundExtensions.cs +++ b/osu.Game/Database/DatabaseWorkaroundExtensions.cs @@ -29,7 +29,7 @@ namespace osu.Game.Database break; case ScoreInfo scoreInfo: - scoreInfo.Beatmap.BeatmapSet.Requery(contextFactory); + requeryFiles(scoreInfo.Beatmap.BeatmapSet.Files, contextFactory); requeryFiles(scoreInfo.Files, contextFactory); break; From 3dd72d6f7d229bd7cb8311e707ace797fed31d3e Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 14 Mar 2021 22:47:05 -0700 Subject: [PATCH 050/202] Fix disable mouse buttons setting not showing default indicator when using keybind --- osu.Game/OsuGame.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index dd775888a1..eb34ba4a37 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -886,7 +886,9 @@ namespace osu.Game return true; case GlobalAction.ToggleGameplayMouseButtons: - LocalConfig.Set(OsuSetting.MouseDisableButtons, !LocalConfig.Get(OsuSetting.MouseDisableButtons)); + var mouseDisableButtons = LocalConfig.GetBindable(OsuSetting.MouseDisableButtons); + + mouseDisableButtons.Value = !mouseDisableButtons.Value; return true; case GlobalAction.RandomSkin: From 393f1fbd3f7f4169fefb1df16b689e66314e6fdf Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 15 Mar 2021 10:07:50 -0700 Subject: [PATCH 051/202] Remove skype --- osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs | 1 - osu.Game/Users/User.cs | 3 --- 2 files changed, 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index 2925107766..662f55317b 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -138,7 +138,6 @@ namespace osu.Game.Overlays.Profile.Header if (!string.IsNullOrEmpty(user.Twitter)) anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}"); anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Discord, user.Discord); - anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Skype, user.Skype, @"skype:" + user.Skype + @"?chat"); anyInfoAdded |= tryAddInfo(FontAwesome.Solid.Link, websiteWithoutProtocol, user.Website); // If no information was added to the bottomLinkContainer, hide it to avoid unwanted padding diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index 4d537b91bd..6c45417db0 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -111,9 +111,6 @@ namespace osu.Game.Users [JsonProperty(@"twitter")] public string Twitter; - [JsonProperty(@"skype")] - public string Skype; - [JsonProperty(@"discord")] public string Discord; From 58220481dbd71edf491a56494bafa302f8250915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 15 Mar 2021 20:38:11 +0100 Subject: [PATCH 052/202] Rename `I{-> Animatable}HitExplosion` --- osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs | 2 +- osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs | 2 +- osu.Game.Rulesets.Taiko/UI/HitExplosion.cs | 4 ++-- .../UI/{IHitExplosion.cs => IAnimatableHitExplosion.cs} | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) rename osu.Game.Rulesets.Taiko/UI/{IHitExplosion.cs => IAnimatableHitExplosion.cs} (79%) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs index 9734e12413..aad9f53b93 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs @@ -11,7 +11,7 @@ using osu.Game.Rulesets.Taiko.UI; namespace osu.Game.Rulesets.Taiko.Skinning.Legacy { - public class LegacyHitExplosion : CompositeDrawable, IHitExplosion + public class LegacyHitExplosion : CompositeDrawable, IAnimatableHitExplosion { public override bool RemoveWhenNotAlive => false; diff --git a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs index 2519573ce9..5bb463353d 100644 --- a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs @@ -14,7 +14,7 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Taiko.UI { - internal class DefaultHitExplosion : CircularContainer, IHitExplosion + internal class DefaultHitExplosion : CircularContainer, IAnimatableHitExplosion { public override bool RemoveWhenNotAlive => false; diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs index d2ae36a03e..bdebe9da17 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs @@ -89,7 +89,7 @@ namespace osu.Game.Rulesets.Taiko.UI LifetimeStart = resultTime; using (BeginAbsoluteSequence(resultTime)) - (skinnable.Drawable as IHitExplosion)?.Animate(JudgedObject); + (skinnable.Drawable as IAnimatableHitExplosion)?.Animate(JudgedObject); LifetimeEnd = skinnable.Drawable.LatestTransformEndTime; } @@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Taiko.UI using (BeginAbsoluteSequence(judgementResult.TimeAbsolute)) { this.ResizeTo(new Vector2(TaikoStrongableHitObject.DEFAULT_STRONG_SIZE), 50); - (skinnable.Drawable as IHitExplosion)?.AnimateSecondHit(); + (skinnable.Drawable as IAnimatableHitExplosion)?.AnimateSecondHit(); } } } diff --git a/osu.Game.Rulesets.Taiko/UI/IHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/IAnimatableHitExplosion.cs similarity index 79% rename from osu.Game.Rulesets.Taiko/UI/IHitExplosion.cs rename to osu.Game.Rulesets.Taiko/UI/IAnimatableHitExplosion.cs index 7af941d1ba..cf0f5f9fb6 100644 --- a/osu.Game.Rulesets.Taiko/UI/IHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/IAnimatableHitExplosion.cs @@ -6,9 +6,9 @@ using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Taiko.UI { /// - /// Interface for hit explosions shown on the playfield's hit target in taiko. + /// A skinnable element of a hit explosion that supports playing an animation from the current point in time. /// - public interface IHitExplosion + public interface IAnimatableHitExplosion { /// /// Shows the hit explosion for the supplied . From f4e508b57051e887a00a8f4649e4616b762a8c8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 15 Mar 2021 20:43:30 +0100 Subject: [PATCH 053/202] Remove unnecessary overrides --- osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs | 2 -- osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs | 2 -- 2 files changed, 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs b/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs index 69b81d6d5c..1b5d576c1e 100644 --- a/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs +++ b/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs @@ -21,8 +21,6 @@ namespace osu.Game.Rulesets.Mania.UI { private const float default_large_faint_size = 0.8f; - public override bool RemoveWhenNotAlive => true; - [Resolved] private Column column { get; set; } diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs index aad9f53b93..bef9279bac 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs @@ -13,8 +13,6 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy { public class LegacyHitExplosion : CompositeDrawable, IAnimatableHitExplosion { - public override bool RemoveWhenNotAlive => false; - private readonly Drawable sprite; [CanBeNull] From 72c18fbdfe8f91798a1adf16ca1f92ecbc593ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 15 Mar 2021 20:48:19 +0100 Subject: [PATCH 054/202] Restructure explosion animation to avoid resetting transforms on free --- osu.Game.Rulesets.Taiko/UI/HitExplosion.cs | 32 ++++++++++++---------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs index bdebe9da17..8f5e9e54ab 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs @@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly HitResult result; + private double? secondHitTime; + [CanBeNull] public DrawableHitObject JudgedObject; @@ -61,6 +63,7 @@ namespace osu.Game.Rulesets.Taiko.UI public void Apply([CanBeNull] DrawableHitObject drawableHitObject) { JudgedObject = drawableHitObject; + secondHitTime = null; } protected override void PrepareForUse() @@ -69,16 +72,6 @@ namespace osu.Game.Rulesets.Taiko.UI runAnimation(); } - protected override void FreeAfterUse() - { - base.FreeAfterUse(); - - // clean up transforms on free instead of on prepare as is usually the case - // to avoid potentially overriding the effects of VisualiseSecondHit() in the case it is called before PrepareForUse(). - ApplyTransformsAt(double.MinValue, true); - ClearTransforms(true); - } - private void runAnimation() { if (JudgedObject?.Result == null) @@ -88,9 +81,21 @@ namespace osu.Game.Rulesets.Taiko.UI LifetimeStart = resultTime; + ApplyTransformsAt(double.MinValue, true); + ClearTransforms(true); + using (BeginAbsoluteSequence(resultTime)) (skinnable.Drawable as IAnimatableHitExplosion)?.Animate(JudgedObject); + if (secondHitTime != null) + { + using (BeginAbsoluteSequence(secondHitTime.Value)) + { + this.ResizeTo(new Vector2(TaikoStrongableHitObject.DEFAULT_STRONG_SIZE), 50); + (skinnable.Drawable as IAnimatableHitExplosion)?.AnimateSecondHit(); + } + } + LifetimeEnd = skinnable.Drawable.LatestTransformEndTime; } @@ -113,11 +118,8 @@ namespace osu.Game.Rulesets.Taiko.UI public void VisualiseSecondHit(JudgementResult judgementResult) { - using (BeginAbsoluteSequence(judgementResult.TimeAbsolute)) - { - this.ResizeTo(new Vector2(TaikoStrongableHitObject.DEFAULT_STRONG_SIZE), 50); - (skinnable.Drawable as IAnimatableHitExplosion)?.AnimateSecondHit(); - } + secondHitTime = judgementResult.TimeAbsolute; + runAnimation(); } } } From da3dc61aae29202b7a0ebb498c274852c1d955ab Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 16 Mar 2021 10:58:42 +0900 Subject: [PATCH 055/202] Remove newline --- osu.Game/OsuGame.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index eb34ba4a37..7d11029a9c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -887,7 +887,6 @@ namespace osu.Game case GlobalAction.ToggleGameplayMouseButtons: var mouseDisableButtons = LocalConfig.GetBindable(OsuSetting.MouseDisableButtons); - mouseDisableButtons.Value = !mouseDisableButtons.Value; return true; From c7740d1181a165466b9960ed54f10c7e14e85075 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 15:52:24 +0900 Subject: [PATCH 056/202] Fix opening the editor occasionally causing a hard crash due to incorrect threading logic Setting one of the global screen `Bindable`s (in this case, `Beatmap`) is not valid from anywhere but the update thread. This changes the order in which things happen during the editor startup process to ensure correctness. Closes #11968. --- osu.Game/Screens/Edit/Editor.cs | 67 +++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 0ba202b082..3a4c3491ff 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -106,26 +106,29 @@ namespace osu.Game.Screens.Edit [BackgroundDependencyLoader] private void load(OsuColour colours, GameHost host, OsuConfigManager config) { - if (Beatmap.Value is DummyWorkingBeatmap) + var loadableBeatmap = Beatmap.Value; + + if (loadableBeatmap is DummyWorkingBeatmap) { isNewBeatmap = true; - var newBeatmap = beatmapManager.CreateNew(Ruleset.Value, api.LocalUser.Value); + loadableBeatmap = beatmapManager.CreateNew(Ruleset.Value, api.LocalUser.Value); + + // required so we can get the track length in EditorClock. + // this is safe as nothing has yet got a reference to this new beatmap. + loadableBeatmap.LoadTrack(); // this is a bit haphazard, but guards against setting the lease Beatmap bindable if // the editor has already been exited. if (!ValidForPush) return; - - // this probably shouldn't be set in the asynchronous load method, but everything following relies on it. - Beatmap.Value = newBeatmap; } - beatDivisor.Value = Beatmap.Value.BeatmapInfo.BeatDivisor; - beatDivisor.BindValueChanged(divisor => Beatmap.Value.BeatmapInfo.BeatDivisor = divisor.NewValue); + beatDivisor.Value = loadableBeatmap.BeatmapInfo.BeatDivisor; + beatDivisor.BindValueChanged(divisor => loadableBeatmap.BeatmapInfo.BeatDivisor = divisor.NewValue); // Todo: should probably be done at a DrawableRuleset level to share logic with Player. - clock = new EditorClock(Beatmap.Value, beatDivisor) { IsCoupled = false }; + clock = new EditorClock(loadableBeatmap, beatDivisor) { IsCoupled = false }; UpdateClockSource(); @@ -139,7 +142,7 @@ namespace osu.Game.Screens.Edit try { - playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset); + playableBeatmap = loadableBeatmap.GetPlayableBeatmap(loadableBeatmap.BeatmapInfo.Ruleset); // clone these locally for now to avoid incurring overhead on GetPlayableBeatmap usages. // eventually we will want to improve how/where this is done as there are issues with *not* cloning it in all cases. @@ -153,13 +156,21 @@ namespace osu.Game.Screens.Edit return; } - AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap, Beatmap.Value.Skin)); + AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap, loadableBeatmap.Skin)); dependencies.CacheAs(editorBeatmap); changeHandler = new EditorChangeHandler(editorBeatmap); dependencies.CacheAs(changeHandler); updateLastSavedHash(); + Schedule(() => + { + // we need to avoid changing the beatmap from an asynchronous load thread. it can potentially cause weirdness including crashes. + // this assumes that nothing during the rest of this load() method is accessing Beatmap.Value (loadableBeatmap should be preferred). + // generally this is quite safe, as the actual load of editor content comes after menuBar.Mode.ValueChanged is fired in its own LoadComplete. + Beatmap.Value = loadableBeatmap; + }); + OsuMenuItem undoMenuItem; OsuMenuItem redoMenuItem; @@ -167,17 +178,6 @@ namespace osu.Game.Screens.Edit EditorMenuItem copyMenuItem; EditorMenuItem pasteMenuItem; - var fileMenuItems = new List - { - new EditorMenuItem("Save", MenuItemType.Standard, Save) - }; - - if (RuntimeInfo.IsDesktop) - fileMenuItems.Add(new EditorMenuItem("Export package", MenuItemType.Standard, exportBeatmap)); - - fileMenuItems.Add(new EditorMenuItemSpacer()); - fileMenuItems.Add(new EditorMenuItem("Exit", MenuItemType.Standard, this.Exit)); - AddInternal(new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, @@ -209,7 +209,7 @@ namespace osu.Game.Screens.Edit { new MenuItem("File") { - Items = fileMenuItems + Items = createFileMenuItems() }, new MenuItem("Edit") { @@ -242,7 +242,11 @@ namespace osu.Game.Screens.Edit Height = 60, Children = new Drawable[] { - bottomBackground = new Box { RelativeSizeAxes = Axes.Both }, + bottomBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray2 + }, new Container { RelativeSizeAxes = Axes.Both, @@ -299,8 +303,6 @@ namespace osu.Game.Screens.Edit clipboard.BindValueChanged(content => pasteMenuItem.Action.Disabled = string.IsNullOrEmpty(content.NewValue)); menuBar.Mode.ValueChanged += onModeChanged; - - bottomBackground.Colour = colours.Gray2; } /// @@ -681,6 +683,21 @@ namespace osu.Game.Screens.Edit lastSavedHash = changeHandler.CurrentStateHash; } + private List createFileMenuItems() + { + var fileMenuItems = new List + { + new EditorMenuItem("Save", MenuItemType.Standard, Save) + }; + + if (RuntimeInfo.IsDesktop) + fileMenuItems.Add(new EditorMenuItem("Export package", MenuItemType.Standard, exportBeatmap)); + + fileMenuItems.Add(new EditorMenuItemSpacer()); + fileMenuItems.Add(new EditorMenuItem("Exit", MenuItemType.Standard, this.Exit)); + return fileMenuItems; + } + public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime); public double GetBeatLengthAtTime(double referenceTime) => editorBeatmap.GetBeatLengthAtTime(referenceTime); From 7fa5fd56475124fa78c84cbb18e3c19838d98e5a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 17 Mar 2021 16:10:16 +0900 Subject: [PATCH 057/202] Update usages of config with framework changes --- .../TestSceneCatchModHidden.cs | 2 +- .../TestSceneCatcher.cs | 4 +- .../ManiaRulesetConfigManager.cs | 4 +- .../TestSceneDrawableJudgement.cs | 4 +- .../TestSceneGameplayCursor.cs | 12 +- .../TestSceneSkinFallbacks.cs | 20 +-- .../Configuration/OsuRulesetConfigManager.cs | 8 +- .../Input/ConfineMouseTrackerTest.cs | 2 +- .../NonVisual/CustomDataDirectoryTest.cs | 4 +- .../TestSceneSeasonalBackgroundLoader.cs | 4 +- .../Visual/Gameplay/TestSceneFailingLayer.cs | 12 +- .../Visual/Gameplay/TestSceneHUDOverlay.cs | 8 +- .../TestSceneStoryboardSamplePlayback.cs | 2 +- .../Visual/Navigation/OsuGameTestScene.cs | 4 +- .../Navigation/TestSettingsMigration.cs | 6 +- .../SongSelect/TestScenePlaySongSelect.cs | 24 +-- .../TestSceneBeatmapListingSearchControl.cs | 4 +- .../UserInterface/TestSceneOnScreenDisplay.cs | 12 +- .../NonVisual/CustomTourneyDirectoryTest.cs | 2 +- osu.Game.Tournament/IO/TournamentStorage.cs | 2 +- .../Components/DrawingsConfigManager.cs | 4 +- osu.Game/Configuration/OsuConfigManager.cs | 142 +++++++++--------- osu.Game/Configuration/SessionStatics.cs | 8 +- .../Configuration/StorageConfigManager.cs | 2 +- osu.Game/IO/OsuStorage.cs | 4 +- osu.Game/Online/API/APIAccess.cs | 4 +- osu.Game/Updater/UpdateManager.cs | 2 +- 27 files changed, 153 insertions(+), 153 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs index f15da29993..1248409b2a 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Tests [BackgroundDependencyLoader] private void load() { - LocalConfig.Set(OsuSetting.IncreaseFirstObjectVisibility, false); + LocalConfig.SetValue(OsuSetting.IncreaseFirstObjectVisibility, false); } [Test] diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index e8bb57cdf3..48efd73222 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -202,7 +202,7 @@ namespace osu.Game.Rulesets.Catch.Tests public void TestHitLightingColour() { var fruitColour = SkinConfiguration.DefaultComboColours[1]; - AddStep("enable hit lighting", () => config.Set(OsuSetting.HitLighting, true)); + AddStep("enable hit lighting", () => config.SetValue(OsuSetting.HitLighting, true)); AddStep("catch fruit", () => attemptCatch(new Fruit())); AddAssert("correct hit lighting colour", () => catcher.ChildrenOfType().First()?.ObjectColour == fruitColour); @@ -211,7 +211,7 @@ namespace osu.Game.Rulesets.Catch.Tests [Test] public void TestHitLightingDisabled() { - AddStep("disable hit lighting", () => config.Set(OsuSetting.HitLighting, false)); + AddStep("disable hit lighting", () => config.SetValue(OsuSetting.HitLighting, false)); AddStep("catch fruit", () => attemptCatch(new Fruit())); AddAssert("no hit lighting", () => !catcher.ChildrenOfType().Any()); } diff --git a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs index 756f2b7b2f..39d0f4bae4 100644 --- a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs +++ b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs @@ -20,8 +20,8 @@ namespace osu.Game.Rulesets.Mania.Configuration { base.InitialiseDefaults(); - Set(ManiaRulesetSetting.ScrollTime, 1500.0, DrawableManiaRuleset.MIN_TIME_RANGE, DrawableManiaRuleset.MAX_TIME_RANGE, 5); - Set(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down); + SetDefault(ManiaRulesetSetting.ScrollTime, 1500.0, DrawableManiaRuleset.MIN_TIME_RANGE, DrawableManiaRuleset.MAX_TIME_RANGE, 5); + SetDefault(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down); } public override TrackedSettings CreateTrackedSettings() => new TrackedSettings diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs index e4158d8f07..4395ca6281 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Tests [Test] public void TestHitLightingDisabled() { - AddStep("hit lighting disabled", () => config.Set(OsuSetting.HitLighting, false)); + AddStep("hit lighting disabled", () => config.SetValue(OsuSetting.HitLighting, false)); showResult(HitResult.Great); @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Tests [Test] public void TestHitLightingEnabled() { - AddStep("hit lighting enabled", () => config.Set(OsuSetting.HitLighting, true)); + AddStep("hit lighting enabled", () => config.SetValue(OsuSetting.HitLighting, true)); showResult(HitResult.Great); diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs index 461779b185..e3ccf83715 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Tests AddSliderStep("circle size", 0f, 10f, 0f, val => { - config.Set(OsuSetting.AutoCursorSize, true); + config.SetValue(OsuSetting.AutoCursorSize, true); gameplayBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = val; Scheduler.AddOnce(recreate); }); @@ -64,21 +64,21 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(10, 1.5f)] public void TestSizing(int circleSize, float userScale) { - AddStep($"set user scale to {userScale}", () => config.Set(OsuSetting.GameplayCursorSize, userScale)); + AddStep($"set user scale to {userScale}", () => config.SetValue(OsuSetting.GameplayCursorSize, userScale)); AddStep($"adjust cs to {circleSize}", () => gameplayBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = circleSize); - AddStep("turn on autosizing", () => config.Set(OsuSetting.AutoCursorSize, true)); + AddStep("turn on autosizing", () => config.SetValue(OsuSetting.AutoCursorSize, true)); AddStep("load content", loadContent); AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == OsuCursorContainer.GetScaleForCircleSize(circleSize) * userScale); - AddStep("set user scale to 1", () => config.Set(OsuSetting.GameplayCursorSize, 1f)); + AddStep("set user scale to 1", () => config.SetValue(OsuSetting.GameplayCursorSize, 1f)); AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == OsuCursorContainer.GetScaleForCircleSize(circleSize)); - AddStep("turn off autosizing", () => config.Set(OsuSetting.AutoCursorSize, false)); + AddStep("turn off autosizing", () => config.SetValue(OsuSetting.AutoCursorSize, false)); AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == 1); - AddStep($"set user scale to {userScale}", () => config.Set(OsuSetting.GameplayCursorSize, userScale)); + AddStep($"set user scale to {userScale}", () => config.SetValue(OsuSetting.GameplayCursorSize, userScale)); AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == userScale); } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs index 8dbb48c048..56f6fb85fa 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs @@ -42,10 +42,10 @@ namespace osu.Game.Rulesets.Osu.Tests { AddStep("enable user provider", () => testUserSkin.Enabled = true); - AddStep("enable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, true)); + AddStep("enable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, true)); checkNextHitObject("beatmap"); - AddStep("disable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, false)); + AddStep("disable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, false)); checkNextHitObject("user"); AddStep("disable user provider", () => testUserSkin.Enabled = false); @@ -57,20 +57,20 @@ namespace osu.Game.Rulesets.Osu.Tests { AddStep("enable user provider", () => testUserSkin.Enabled = true); - AddStep("enable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, true)); - AddStep("enable beatmap colours", () => LocalConfig.Set(OsuSetting.BeatmapColours, true)); + AddStep("enable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, true)); + AddStep("enable beatmap colours", () => LocalConfig.SetValue(OsuSetting.BeatmapColours, true)); checkNextHitObject("beatmap"); - AddStep("enable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, true)); - AddStep("disable beatmap colours", () => LocalConfig.Set(OsuSetting.BeatmapColours, false)); + AddStep("enable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, true)); + AddStep("disable beatmap colours", () => LocalConfig.SetValue(OsuSetting.BeatmapColours, false)); checkNextHitObject("beatmap"); - AddStep("disable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, false)); - AddStep("enable beatmap colours", () => LocalConfig.Set(OsuSetting.BeatmapColours, true)); + AddStep("disable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, false)); + AddStep("enable beatmap colours", () => LocalConfig.SetValue(OsuSetting.BeatmapColours, true)); checkNextHitObject("user"); - AddStep("disable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, false)); - AddStep("disable beatmap colours", () => LocalConfig.Set(OsuSetting.BeatmapColours, false)); + AddStep("disable beatmap skin", () => LocalConfig.SetValue(OsuSetting.BeatmapSkins, false)); + AddStep("disable beatmap colours", () => LocalConfig.SetValue(OsuSetting.BeatmapColours, false)); checkNextHitObject("user"); AddStep("disable user provider", () => testUserSkin.Enabled = false); diff --git a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs index e8272057f3..9589fd576f 100644 --- a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs +++ b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs @@ -17,10 +17,10 @@ namespace osu.Game.Rulesets.Osu.Configuration protected override void InitialiseDefaults() { base.InitialiseDefaults(); - Set(OsuRulesetSetting.SnakingInSliders, true); - Set(OsuRulesetSetting.SnakingOutSliders, true); - Set(OsuRulesetSetting.ShowCursorTrail, true); - Set(OsuRulesetSetting.PlayfieldBorderStyle, PlayfieldBorderStyle.None); + SetDefault(OsuRulesetSetting.SnakingInSliders, true); + SetDefault(OsuRulesetSetting.SnakingOutSliders, true); + SetDefault(OsuRulesetSetting.ShowCursorTrail, true); + SetDefault(OsuRulesetSetting.PlayfieldBorderStyle, PlayfieldBorderStyle.None); } } diff --git a/osu.Game.Tests/Input/ConfineMouseTrackerTest.cs b/osu.Game.Tests/Input/ConfineMouseTrackerTest.cs index b90382488f..27cece42e8 100644 --- a/osu.Game.Tests/Input/ConfineMouseTrackerTest.cs +++ b/osu.Game.Tests/Input/ConfineMouseTrackerTest.cs @@ -88,7 +88,7 @@ namespace osu.Game.Tests.Input => AddStep($"make window {mode}", () => frameworkConfigManager.GetBindable(FrameworkSetting.WindowMode).Value = mode); private void setGameSideModeTo(OsuConfineMouseMode mode) - => AddStep($"set {mode} game-side", () => Game.LocalConfig.Set(OsuSetting.ConfineMouseMode, mode)); + => AddStep($"set {mode} game-side", () => Game.LocalConfig.SetValue(OsuSetting.ConfineMouseMode, mode)); private void setLocalUserPlayingTo(bool playing) => AddStep($"local user {(playing ? "playing" : "not playing")}", () => Game.LocalUserPlaying.Value = playing); diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index 045246e5ed..a763544c37 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -47,7 +47,7 @@ namespace osu.Game.Tests.NonVisual using (var host = new CustomTestHeadlessGameHost()) { using (var storageConfig = new StorageConfigManager(host.InitialStorage)) - storageConfig.Set(StorageConfig.FullPath, customPath); + storageConfig.SetValue(StorageConfig.FullPath, customPath); try { @@ -73,7 +73,7 @@ namespace osu.Game.Tests.NonVisual using (var host = new CustomTestHeadlessGameHost()) { using (var storageConfig = new StorageConfigManager(host.InitialStorage)) - storageConfig.Set(StorageConfig.FullPath, customPath); + storageConfig.SetValue(StorageConfig.FullPath, customPath); try { diff --git a/osu.Game.Tests/Visual/Background/TestSceneSeasonalBackgroundLoader.cs b/osu.Game.Tests/Visual/Background/TestSceneSeasonalBackgroundLoader.cs index fba0d92d4b..e7cf830db0 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneSeasonalBackgroundLoader.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneSeasonalBackgroundLoader.cs @@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.Background public void SetUp() => Schedule(() => { // reset API response in statics to avoid test crosstalk. - statics.Set(Static.SeasonalBackgrounds, null); + statics.SetValue(Static.SeasonalBackgrounds, null); textureStore.PerformedLookups.Clear(); dummyAPI.SetState(APIState.Online); @@ -146,7 +146,7 @@ namespace osu.Game.Tests.Visual.Background }); private void setSeasonalBackgroundMode(SeasonalBackgroundMode mode) - => AddStep($"set seasonal mode to {mode}", () => config.Set(OsuSetting.SeasonalBackgroundMode, mode)); + => AddStep($"set seasonal mode to {mode}", () => config.SetValue(OsuSetting.SeasonalBackgroundMode, mode)); private void createLoader() => AddStep("create loader", () => diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs index 1c55595c97..5a1a9d3d87 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Gameplay }); AddStep("show health", () => showHealth.Value = true); - AddStep("enable layer", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, true)); + AddStep("enable layer", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true)); AddUntilStep("layer is visible", () => layer.IsPresent); } @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestLayerDisabledViaConfig() { - AddStep("disable layer", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, false)); + AddStep("disable layer", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, false)); AddStep("set health to 0.10", () => layer.Current.Value = 0.1); AddUntilStep("layer is not visible", () => !layer.IsPresent); } @@ -81,19 +81,19 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("set health to 0.10", () => layer.Current.Value = 0.1); AddStep("don't show health", () => showHealth.Value = false); - AddStep("disable FadePlayfieldWhenHealthLow", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, false)); + AddStep("disable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, false)); AddUntilStep("layer fade is invisible", () => !layer.IsPresent); AddStep("don't show health", () => showHealth.Value = false); - AddStep("enable FadePlayfieldWhenHealthLow", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, true)); + AddStep("enable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true)); AddUntilStep("layer fade is invisible", () => !layer.IsPresent); AddStep("show health", () => showHealth.Value = true); - AddStep("disable FadePlayfieldWhenHealthLow", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, false)); + AddStep("disable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, false)); AddUntilStep("layer fade is invisible", () => !layer.IsPresent); AddStep("show health", () => showHealth.Value = true); - AddStep("enable FadePlayfieldWhenHealthLow", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, true)); + AddStep("enable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true)); AddUntilStep("layer fade is visible", () => layer.IsPresent); } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index f9914e0193..3cefb8623f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("get original config value", () => originalConfigValue = config.Get(OsuSetting.HUDVisibilityMode)); - AddStep("set hud to never show", () => config.Set(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never)); + AddStep("set hud to never show", () => config.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never)); AddUntilStep("wait for fade", () => !hideTarget.IsPresent); @@ -91,7 +91,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("stop trigering", () => InputManager.ReleaseKey(Key.ControlLeft)); AddUntilStep("wait for fade", () => !hideTarget.IsPresent); - AddStep("set original config value", () => config.Set(OsuSetting.HUDVisibilityMode, originalConfigValue)); + AddStep("set original config value", () => config.SetValue(OsuSetting.HUDVisibilityMode, originalConfigValue)); } [Test] @@ -120,7 +120,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("set keycounter visible false", () => { - config.Set(OsuSetting.KeyOverlay, false); + config.SetValue(OsuSetting.KeyOverlay, false); hudOverlay.KeyCounter.AlwaysVisible.Value = false; }); @@ -132,7 +132,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("hidetarget is visible", () => hideTarget.IsPresent); AddAssert("key counters still hidden", () => !keyCounterFlow.IsPresent); - AddStep("return value", () => config.Set(OsuSetting.KeyOverlay, keyCounterVisibleValue)); + AddStep("return value", () => config.SetValue(OsuSetting.KeyOverlay, keyCounterVisibleValue)); } private void createNew(Action action = null) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardSamplePlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardSamplePlayback.cs index 1544f8fd35..a718a98aa6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardSamplePlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardSamplePlayback.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual.Gameplay [BackgroundDependencyLoader] private void load(OsuConfigManager config) { - config.Set(OsuSetting.ShowStoryboard, true); + config.SetValue(OsuSetting.ShowStoryboard, true); storyboard = new Storyboard(); var backgroundLayer = storyboard.GetLayer("Background"); diff --git a/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs b/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs index 96393cc4c3..6ca7707906 100644 --- a/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs +++ b/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs @@ -82,7 +82,7 @@ namespace osu.Game.Tests.Visual.Navigation // todo: this can be removed once we can run audio tracks without a device present // see https://github.com/ppy/osu/issues/1302 - Game.LocalConfig.Set(OsuSetting.IntroSequence, IntroSequence.Circles); + Game.LocalConfig.SetValue(OsuSetting.IntroSequence, IntroSequence.Circles); Add(Game); } @@ -136,7 +136,7 @@ namespace osu.Game.Tests.Visual.Navigation base.LoadComplete(); API.Login("Rhythm Champion", "osu!"); - Dependencies.Get().Set(Static.MutedAudioNotificationShownOnce, true); + Dependencies.Get().SetValue(Static.MutedAudioNotificationShownOnce, true); } } diff --git a/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs b/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs index c0b77b580e..768f2057a2 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs @@ -15,8 +15,8 @@ namespace osu.Game.Tests.Visual.Navigation using (var config = new OsuConfigManager(LocalStorage)) { - config.Set(OsuSetting.Version, "2020.101.0"); - config.Set(OsuSetting.DisplayStarsMaximum, 10.0); + config.SetValue(OsuSetting.Version, "2020.101.0"); + config.SetValue(OsuSetting.DisplayStarsMaximum, 10.0); } } @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Navigation { AddAssert("config has migrated value", () => Precision.AlmostEquals(Game.LocalConfig.Get(OsuSetting.DisplayStarsMaximum), 10.1)); - AddStep("set value again", () => Game.LocalConfig.Set(OsuSetting.DisplayStarsMaximum, 10)); + AddStep("set value again", () => Game.LocalConfig.SetValue(OsuSetting.DisplayStarsMaximum, 10)); AddStep("force save config", () => Game.LocalConfig.Save()); diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 2d192ae207..057b539e44 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -207,14 +207,14 @@ namespace osu.Game.Tests.Visual.SongSelect addRulesetImportStep(0); addRulesetImportStep(0); - AddStep("change convert setting", () => config.Set(OsuSetting.ShowConvertedBeatmaps, false)); + AddStep("change convert setting", () => config.SetValue(OsuSetting.ShowConvertedBeatmaps, false)); createSongSelect(); AddStep("push child screen", () => Stack.Push(new TestSceneOsuScreenStack.TestScreen("test child"))); AddUntilStep("wait for not current", () => !songSelect.IsCurrentScreen()); - AddStep("change convert setting", () => config.Set(OsuSetting.ShowConvertedBeatmaps, true)); + AddStep("change convert setting", () => config.SetValue(OsuSetting.ShowConvertedBeatmaps, true)); AddStep("return", () => songSelect.MakeCurrent()); AddUntilStep("wait for current", () => songSelect.IsCurrentScreen()); @@ -297,13 +297,13 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap); - AddStep(@"Sort by Artist", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Artist)); - AddStep(@"Sort by Title", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Title)); - AddStep(@"Sort by Author", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Author)); - AddStep(@"Sort by DateAdded", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.DateAdded)); - AddStep(@"Sort by BPM", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.BPM)); - AddStep(@"Sort by Length", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Length)); - AddStep(@"Sort by Difficulty", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Difficulty)); + AddStep(@"Sort by Artist", () => config.SetValue(OsuSetting.SongSelectSortingMode, SortMode.Artist)); + AddStep(@"Sort by Title", () => config.SetValue(OsuSetting.SongSelectSortingMode, SortMode.Title)); + AddStep(@"Sort by Author", () => config.SetValue(OsuSetting.SongSelectSortingMode, SortMode.Author)); + AddStep(@"Sort by DateAdded", () => config.SetValue(OsuSetting.SongSelectSortingMode, SortMode.DateAdded)); + AddStep(@"Sort by BPM", () => config.SetValue(OsuSetting.SongSelectSortingMode, SortMode.BPM)); + AddStep(@"Sort by Length", () => config.SetValue(OsuSetting.SongSelectSortingMode, SortMode.Length)); + AddStep(@"Sort by Difficulty", () => config.SetValue(OsuSetting.SongSelectSortingMode, SortMode.Difficulty)); } [Test] @@ -470,7 +470,7 @@ namespace osu.Game.Tests.Visual.SongSelect changeRuleset(0); // used for filter check below - AddStep("allow convert display", () => config.Set(OsuSetting.ShowConvertedBeatmaps, true)); + AddStep("allow convert display", () => config.SetValue(OsuSetting.ShowConvertedBeatmaps, true)); AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null); @@ -648,7 +648,7 @@ namespace osu.Game.Tests.Visual.SongSelect { int changeCount = 0; - AddStep("change convert setting", () => config.Set(OsuSetting.ShowConvertedBeatmaps, false)); + AddStep("change convert setting", () => config.SetValue(OsuSetting.ShowConvertedBeatmaps, false)); AddStep("bind beatmap changed", () => { Beatmap.ValueChanged += onChange; @@ -686,7 +686,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("selection changed only fired twice", () => changeCount == 2); AddStep("unbind beatmap changed", () => Beatmap.ValueChanged -= onChange); - AddStep("change convert setting", () => config.Set(OsuSetting.ShowConvertedBeatmaps, true)); + AddStep("change convert setting", () => config.SetValue(OsuSetting.ShowConvertedBeatmaps, true)); // ReSharper disable once AccessToModifiedClosure void onChange(ValueChangedEvent valueChangedEvent) => changeCount++; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs index a9747e73f9..9602758ffc 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs @@ -92,10 +92,10 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestExplicitConfig() { - AddStep("configure explicit content to allowed", () => localConfig.Set(OsuSetting.ShowOnlineExplicitContent, true)); + AddStep("configure explicit content to allowed", () => localConfig.SetValue(OsuSetting.ShowOnlineExplicitContent, true)); AddAssert("explicit control set to show", () => control.ExplicitContent.Value == SearchExplicit.Show); - AddStep("configure explicit content to disallowed", () => localConfig.Set(OsuSetting.ShowOnlineExplicitContent, false)); + AddStep("configure explicit content to disallowed", () => localConfig.SetValue(OsuSetting.ShowOnlineExplicitContent, false)); AddAssert("explicit control set to hide", () => control.ExplicitContent.Value == SearchExplicit.Hide); } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs index 45720548c8..493e2f54e5 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOnScreenDisplay.cs @@ -44,22 +44,22 @@ namespace osu.Game.Tests.Visual.UserInterface protected override void InitialiseDefaults() { - Set(TestConfigSetting.ToggleSettingNoKeybind, false); - Set(TestConfigSetting.EnumSettingNoKeybind, EnumSetting.Setting1); - Set(TestConfigSetting.ToggleSettingWithKeybind, false); - Set(TestConfigSetting.EnumSettingWithKeybind, EnumSetting.Setting1); + SetDefault(TestConfigSetting.ToggleSettingNoKeybind, false); + SetDefault(TestConfigSetting.EnumSettingNoKeybind, EnumSetting.Setting1); + SetDefault(TestConfigSetting.ToggleSettingWithKeybind, false); + SetDefault(TestConfigSetting.EnumSettingWithKeybind, EnumSetting.Setting1); base.InitialiseDefaults(); } - public void ToggleSetting(TestConfigSetting setting) => Set(setting, !Get(setting)); + public void ToggleSetting(TestConfigSetting setting) => SetValue(setting, !Get(setting)); public void IncrementEnumSetting(TestConfigSetting setting) { var nextValue = Get(setting) + 1; if (nextValue > EnumSetting.Setting4) nextValue = EnumSetting.Setting1; - Set(setting, nextValue); + SetValue(setting, nextValue); } public override TrackedSettings CreateTrackedSettings() => new TrackedSettings diff --git a/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs b/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs index 567d9f0d62..46c3b8bc3b 100644 --- a/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs +++ b/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tournament.Tests.NonVisual storage.DeleteDirectory(string.Empty); using (var storageConfig = new TournamentStorageManager(storage)) - storageConfig.Set(StorageConfig.CurrentTournament, custom_tournament); + storageConfig.SetValue(StorageConfig.CurrentTournament, custom_tournament); try { diff --git a/osu.Game.Tournament/IO/TournamentStorage.cs b/osu.Game.Tournament/IO/TournamentStorage.cs index 2ba1b6be8f..5d9fed6288 100644 --- a/osu.Game.Tournament/IO/TournamentStorage.cs +++ b/osu.Game.Tournament/IO/TournamentStorage.cs @@ -70,7 +70,7 @@ namespace osu.Game.Tournament.IO moveFileIfExists("drawings.ini", destination); ChangeTargetStorage(newStorage); - storageConfig.Set(StorageConfig.CurrentTournament, default_tournament); + storageConfig.SetValue(StorageConfig.CurrentTournament, default_tournament); storageConfig.Save(); } diff --git a/osu.Game.Tournament/Screens/Drawings/Components/DrawingsConfigManager.cs b/osu.Game.Tournament/Screens/Drawings/Components/DrawingsConfigManager.cs index d197c0f5d9..1a2f5a1ff4 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/DrawingsConfigManager.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/DrawingsConfigManager.cs @@ -12,8 +12,8 @@ namespace osu.Game.Tournament.Screens.Drawings.Components protected override void InitialiseDefaults() { - Set(DrawingsConfig.Groups, 8, 1, 8); - Set(DrawingsConfig.TeamsPerGroup, 8, 1, 8); + SetDefault(DrawingsConfig.Groups, 8, 1, 8); + SetDefault(DrawingsConfig.TeamsPerGroup, 8, 1, 8); } public DrawingsConfigManager(Storage storage) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index d0fa45bb7a..387cfbb193 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -24,126 +24,126 @@ namespace osu.Game.Configuration protected override void InitialiseDefaults() { // UI/selection defaults - Set(OsuSetting.Ruleset, 0, 0, int.MaxValue); - Set(OsuSetting.Skin, 0, -1, int.MaxValue); + SetDefault(OsuSetting.Ruleset, 0, 0, int.MaxValue); + SetDefault(OsuSetting.Skin, 0, -1, int.MaxValue); - Set(OsuSetting.BeatmapDetailTab, PlayBeatmapDetailArea.TabType.Details); - Set(OsuSetting.BeatmapDetailModsFilter, false); + SetDefault(OsuSetting.BeatmapDetailTab, PlayBeatmapDetailArea.TabType.Details); + SetDefault(OsuSetting.BeatmapDetailModsFilter, false); - Set(OsuSetting.ShowConvertedBeatmaps, true); - Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1); - Set(OsuSetting.DisplayStarsMaximum, 10.1, 0, 10.1, 0.1); + SetDefault(OsuSetting.ShowConvertedBeatmaps, true); + SetDefault(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1); + SetDefault(OsuSetting.DisplayStarsMaximum, 10.1, 0, 10.1, 0.1); - Set(OsuSetting.SongSelectGroupingMode, GroupMode.All); - Set(OsuSetting.SongSelectSortingMode, SortMode.Title); + SetDefault(OsuSetting.SongSelectGroupingMode, GroupMode.All); + SetDefault(OsuSetting.SongSelectSortingMode, SortMode.Title); - Set(OsuSetting.RandomSelectAlgorithm, RandomSelectAlgorithm.RandomPermutation); + SetDefault(OsuSetting.RandomSelectAlgorithm, RandomSelectAlgorithm.RandomPermutation); - Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2f, 1f); + SetDefault(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2f, 1f); // Online settings - Set(OsuSetting.Username, string.Empty); - Set(OsuSetting.Token, string.Empty); + SetDefault(OsuSetting.Username, string.Empty); + SetDefault(OsuSetting.Token, string.Empty); - Set(OsuSetting.AutomaticallyDownloadWhenSpectating, false); + SetDefault(OsuSetting.AutomaticallyDownloadWhenSpectating, false); - Set(OsuSetting.SavePassword, false).ValueChanged += enabled => + SetDefault(OsuSetting.SavePassword, false).ValueChanged += enabled => { - if (enabled.NewValue) Set(OsuSetting.SaveUsername, true); + if (enabled.NewValue) SetValue(OsuSetting.SaveUsername, true); }; - Set(OsuSetting.SaveUsername, true).ValueChanged += enabled => + SetDefault(OsuSetting.SaveUsername, true).ValueChanged += enabled => { - if (!enabled.NewValue) Set(OsuSetting.SavePassword, false); + if (!enabled.NewValue) SetValue(OsuSetting.SavePassword, false); }; - Set(OsuSetting.ExternalLinkWarning, true); - Set(OsuSetting.PreferNoVideo, false); + SetDefault(OsuSetting.ExternalLinkWarning, true); + SetDefault(OsuSetting.PreferNoVideo, false); - Set(OsuSetting.ShowOnlineExplicitContent, false); + SetDefault(OsuSetting.ShowOnlineExplicitContent, false); // Audio - Set(OsuSetting.VolumeInactive, 0.25, 0, 1, 0.01); + SetDefault(OsuSetting.VolumeInactive, 0.25, 0, 1, 0.01); - Set(OsuSetting.MenuVoice, true); - Set(OsuSetting.MenuMusic, true); + SetDefault(OsuSetting.MenuVoice, true); + SetDefault(OsuSetting.MenuMusic, true); - Set(OsuSetting.AudioOffset, 0, -500.0, 500.0, 1); + SetDefault(OsuSetting.AudioOffset, 0, -500.0, 500.0, 1); // Input - Set(OsuSetting.MenuCursorSize, 1.0f, 0.5f, 2f, 0.01f); - Set(OsuSetting.GameplayCursorSize, 1.0f, 0.1f, 2f, 0.01f); - Set(OsuSetting.AutoCursorSize, false); + SetDefault(OsuSetting.MenuCursorSize, 1.0f, 0.5f, 2f, 0.01f); + SetDefault(OsuSetting.GameplayCursorSize, 1.0f, 0.1f, 2f, 0.01f); + SetDefault(OsuSetting.AutoCursorSize, false); - Set(OsuSetting.MouseDisableButtons, false); - Set(OsuSetting.MouseDisableWheel, false); - Set(OsuSetting.ConfineMouseMode, OsuConfineMouseMode.DuringGameplay); + SetDefault(OsuSetting.MouseDisableButtons, false); + SetDefault(OsuSetting.MouseDisableWheel, false); + SetDefault(OsuSetting.ConfineMouseMode, OsuConfineMouseMode.DuringGameplay); // Graphics - Set(OsuSetting.ShowFpsDisplay, false); + SetDefault(OsuSetting.ShowFpsDisplay, false); - Set(OsuSetting.ShowStoryboard, true); - Set(OsuSetting.BeatmapSkins, true); - Set(OsuSetting.BeatmapColours, true); - Set(OsuSetting.BeatmapHitsounds, true); + SetDefault(OsuSetting.ShowStoryboard, true); + SetDefault(OsuSetting.BeatmapSkins, true); + SetDefault(OsuSetting.BeatmapColours, true); + SetDefault(OsuSetting.BeatmapHitsounds, true); - Set(OsuSetting.CursorRotation, true); + SetDefault(OsuSetting.CursorRotation, true); - Set(OsuSetting.MenuParallax, true); + SetDefault(OsuSetting.MenuParallax, true); // Gameplay - Set(OsuSetting.DimLevel, 0.8, 0, 1, 0.01); - Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01); - Set(OsuSetting.LightenDuringBreaks, true); + SetDefault(OsuSetting.DimLevel, 0.8, 0, 1, 0.01); + SetDefault(OsuSetting.BlurLevel, 0, 0, 1, 0.01); + SetDefault(OsuSetting.LightenDuringBreaks, true); - Set(OsuSetting.HitLighting, true); + SetDefault(OsuSetting.HitLighting, true); - Set(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always); - Set(OsuSetting.ShowProgressGraph, true); - Set(OsuSetting.ShowHealthDisplayWhenCantFail, true); - Set(OsuSetting.FadePlayfieldWhenHealthLow, true); - Set(OsuSetting.KeyOverlay, false); - Set(OsuSetting.PositionalHitSounds, true); - Set(OsuSetting.AlwaysPlayFirstComboBreak, true); - Set(OsuSetting.ScoreMeter, ScoreMeterType.HitErrorBoth); + SetDefault(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always); + SetDefault(OsuSetting.ShowProgressGraph, true); + SetDefault(OsuSetting.ShowHealthDisplayWhenCantFail, true); + SetDefault(OsuSetting.FadePlayfieldWhenHealthLow, true); + SetDefault(OsuSetting.KeyOverlay, false); + SetDefault(OsuSetting.PositionalHitSounds, true); + SetDefault(OsuSetting.AlwaysPlayFirstComboBreak, true); + SetDefault(OsuSetting.ScoreMeter, ScoreMeterType.HitErrorBoth); - Set(OsuSetting.FloatingComments, false); + SetDefault(OsuSetting.FloatingComments, false); - Set(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised); + SetDefault(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised); - Set(OsuSetting.IncreaseFirstObjectVisibility, true); - Set(OsuSetting.GameplayDisableWinKey, true); + SetDefault(OsuSetting.IncreaseFirstObjectVisibility, true); + SetDefault(OsuSetting.GameplayDisableWinKey, true); // Update - Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer); + SetDefault(OsuSetting.ReleaseStream, ReleaseStream.Lazer); - Set(OsuSetting.Version, string.Empty); + SetDefault(OsuSetting.Version, string.Empty); - Set(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg); - Set(OsuSetting.ScreenshotCaptureMenuCursor, false); + SetDefault(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg); + SetDefault(OsuSetting.ScreenshotCaptureMenuCursor, false); - Set(OsuSetting.SongSelectRightMouseScroll, false); + SetDefault(OsuSetting.SongSelectRightMouseScroll, false); - Set(OsuSetting.Scaling, ScalingMode.Off); + SetDefault(OsuSetting.Scaling, ScalingMode.Off); - Set(OsuSetting.ScalingSizeX, 0.8f, 0.2f, 1f); - Set(OsuSetting.ScalingSizeY, 0.8f, 0.2f, 1f); + SetDefault(OsuSetting.ScalingSizeX, 0.8f, 0.2f, 1f); + SetDefault(OsuSetting.ScalingSizeY, 0.8f, 0.2f, 1f); - Set(OsuSetting.ScalingPositionX, 0.5f, 0f, 1f); - Set(OsuSetting.ScalingPositionY, 0.5f, 0f, 1f); + SetDefault(OsuSetting.ScalingPositionX, 0.5f, 0f, 1f); + SetDefault(OsuSetting.ScalingPositionY, 0.5f, 0f, 1f); - Set(OsuSetting.UIScale, 1f, 0.8f, 1.6f, 0.01f); + SetDefault(OsuSetting.UIScale, 1f, 0.8f, 1.6f, 0.01f); - Set(OsuSetting.UIHoldActivationDelay, 200f, 0f, 500f, 50f); + SetDefault(OsuSetting.UIHoldActivationDelay, 200f, 0f, 500f, 50f); - Set(OsuSetting.IntroSequence, IntroSequence.Triangles); + SetDefault(OsuSetting.IntroSequence, IntroSequence.Triangles); - Set(OsuSetting.MenuBackgroundSource, BackgroundSource.Skin); - Set(OsuSetting.SeasonalBackgroundMode, SeasonalBackgroundMode.Sometimes); + SetDefault(OsuSetting.MenuBackgroundSource, BackgroundSource.Skin); + SetDefault(OsuSetting.SeasonalBackgroundMode, SeasonalBackgroundMode.Sometimes); - Set(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Full); + SetDefault(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Full); - Set(OsuSetting.EditorWaveformOpacity, 1f); + SetDefault(OsuSetting.EditorWaveformOpacity, 1f); } public OsuConfigManager(Storage storage) diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index fd401119ff..36eb6964dd 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -14,10 +14,10 @@ namespace osu.Game.Configuration { protected override void InitialiseDefaults() { - Set(Static.LoginOverlayDisplayed, false); - Set(Static.MutedAudioNotificationShownOnce, false); - Set(Static.LastHoverSoundPlaybackTime, (double?)null); - Set(Static.SeasonalBackgrounds, null); + SetDefault(Static.LoginOverlayDisplayed, false); + SetDefault(Static.MutedAudioNotificationShownOnce, false); + SetDefault(Static.LastHoverSoundPlaybackTime, (double?)null); + SetDefault(Static.SeasonalBackgrounds, null); } } diff --git a/osu.Game/Configuration/StorageConfigManager.cs b/osu.Game/Configuration/StorageConfigManager.cs index 929f8f22ad..90ea42b638 100644 --- a/osu.Game/Configuration/StorageConfigManager.cs +++ b/osu.Game/Configuration/StorageConfigManager.cs @@ -19,7 +19,7 @@ namespace osu.Game.Configuration { base.InitialiseDefaults(); - Set(StorageConfig.FullPath, string.Empty); + SetDefault(StorageConfig.FullPath, string.Empty); } } diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs index 8097f61ea4..7df5d820ee 100644 --- a/osu.Game/IO/OsuStorage.cs +++ b/osu.Game/IO/OsuStorage.cs @@ -58,7 +58,7 @@ namespace osu.Game.IO /// public void ResetCustomStoragePath() { - storageConfig.Set(StorageConfig.FullPath, string.Empty); + storageConfig.SetValue(StorageConfig.FullPath, string.Empty); storageConfig.Save(); ChangeTargetStorage(defaultStorage); @@ -103,7 +103,7 @@ namespace osu.Game.IO public override void Migrate(Storage newStorage) { base.Migrate(newStorage); - storageConfig.Set(StorageConfig.FullPath, newStorage.GetFullPath(".")); + storageConfig.SetValue(StorageConfig.FullPath, newStorage.GetFullPath(".")); storageConfig.Save(); } } diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index ede64c0340..944525c119 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -89,7 +89,7 @@ namespace osu.Game.Online.API thread.Start(); } - private void onTokenChanged(ValueChangedEvent e) => config.Set(OsuSetting.Token, config.Get(OsuSetting.SavePassword) ? authentication.TokenString : string.Empty); + private void onTokenChanged(ValueChangedEvent e) => config.SetValue(OsuSetting.Token, config.Get(OsuSetting.SavePassword) ? authentication.TokenString : string.Empty); internal new void Schedule(Action action) => base.Schedule(action); @@ -134,7 +134,7 @@ namespace osu.Game.Online.API state.Value = APIState.Connecting; // save the username at this point, if the user requested for it to be. - config.Set(OsuSetting.Username, config.Get(OsuSetting.SaveUsername) ? ProvidedUsername : string.Empty); + config.SetValue(OsuSetting.Username, config.Get(OsuSetting.SaveUsername) ? ProvidedUsername : string.Empty); if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(ProvidedUsername, password)) { diff --git a/osu.Game/Updater/UpdateManager.cs b/osu.Game/Updater/UpdateManager.cs index 9a0454bc95..1c72f3ebe2 100644 --- a/osu.Game/Updater/UpdateManager.cs +++ b/osu.Game/Updater/UpdateManager.cs @@ -52,7 +52,7 @@ namespace osu.Game.Updater // debug / local compilations will reset to a non-release string. // can be useful to check when an install has transitioned between release and otherwise (see OsuConfigManager's migrations). - config.Set(OsuSetting.Version, version); + config.SetValue(OsuSetting.Version, version); } private readonly object updateTaskLock = new object(); From eda891223c469cdb23a07fa8faeac7331db2aa33 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 16:47:12 +0900 Subject: [PATCH 058/202] Start the editor with empty artist/creator/difficulty name fields --- osu.Game/Beatmaps/BeatmapManager.cs | 3 --- osu.Game/Screens/Edit/Setup/MetadataSection.cs | 8 ++++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f42fba79cb..115d1b33bb 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -113,8 +113,6 @@ namespace osu.Game.Beatmaps { var metadata = new BeatmapMetadata { - Artist = "artist", - Title = "title", Author = user, }; @@ -128,7 +126,6 @@ namespace osu.Game.Beatmaps BaseDifficulty = new BeatmapDifficulty(), Ruleset = ruleset, Metadata = metadata, - Version = "difficulty" } } }; diff --git a/osu.Game/Screens/Edit/Setup/MetadataSection.cs b/osu.Game/Screens/Edit/Setup/MetadataSection.cs index e812c042fb..2b10be0423 100644 --- a/osu.Game/Screens/Edit/Setup/MetadataSection.cs +++ b/osu.Game/Screens/Edit/Setup/MetadataSection.cs @@ -28,25 +28,25 @@ namespace osu.Game.Screens.Edit.Setup }, artistTextBox = new LabelledTextBox { - Label = "Artist", + PlaceholderText = "Artist", Current = { Value = Beatmap.Metadata.Artist }, TabbableContentContainer = this }, titleTextBox = new LabelledTextBox { - Label = "Title", + PlaceholderText = "Title", Current = { Value = Beatmap.Metadata.Title }, TabbableContentContainer = this }, creatorTextBox = new LabelledTextBox { - Label = "Creator", + PlaceholderText = "Creator", Current = { Value = Beatmap.Metadata.AuthorString }, TabbableContentContainer = this }, difficultyTextBox = new LabelledTextBox { - Label = "Difficulty Name", + PlaceholderText = "Difficulty Name", Current = { Value = Beatmap.BeatmapInfo.Version }, TabbableContentContainer = this }, From 26d6f96c4e2decdba1836a98280a2ba6381c379d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 16:56:58 +0900 Subject: [PATCH 059/202] Fix LabelledTextBox not correctly forwarding focus to its underlying TextBox component --- osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs index 4aeda74be8..266eb11319 100644 --- a/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; using osu.Game.Graphics.UserInterface; namespace osu.Game.Graphics.UserInterfaceV2 @@ -53,6 +54,14 @@ namespace osu.Game.Graphics.UserInterfaceV2 CornerRadius = CORNER_RADIUS, }; + public override bool AcceptsFocus => true; + + protected override void OnFocus(FocusEvent e) + { + base.OnFocus(e); + GetContainingInputManager().ChangeFocus(Component); + } + protected override OsuTextBox CreateComponent() => CreateTextBox().With(t => { t.OnCommit += (sender, newText) => OnCommit?.Invoke(sender, newText); From 5adc675862afb015ad737db47ebda917fc24c2f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 16:57:14 +0900 Subject: [PATCH 060/202] Focus artist textbox on entering song setup if fields are empty --- osu.Game/Screens/Edit/Setup/MetadataSection.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Screens/Edit/Setup/MetadataSection.cs b/osu.Game/Screens/Edit/Setup/MetadataSection.cs index 2b10be0423..c5a2b77ab4 100644 --- a/osu.Game/Screens/Edit/Setup/MetadataSection.cs +++ b/osu.Game/Screens/Edit/Setup/MetadataSection.cs @@ -56,6 +56,14 @@ namespace osu.Game.Screens.Edit.Setup item.OnCommit += onCommit; } + protected override void LoadComplete() + { + base.LoadComplete(); + + if (string.IsNullOrEmpty(artistTextBox.Current.Value)) + GetContainingInputManager().ChangeFocus(artistTextBox); + } + private void onCommit(TextBox sender, bool newText) { if (!newText) return; From 3b6a1180b68515b82a9e970f84a3e51d5f908f4e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 17:02:11 +0900 Subject: [PATCH 061/202] Remove non-accessed field --- osu.Game/Screens/Edit/Editor.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 3a4c3491ff..0c24eb6a4d 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -74,7 +74,6 @@ namespace osu.Game.Screens.Edit private string lastSavedHash; - private Box bottomBackground; private Container screenContainer; private EditorScreen currentScreen; @@ -242,7 +241,7 @@ namespace osu.Game.Screens.Edit Height = 60, Children = new Drawable[] { - bottomBackground = new Box + new Box { RelativeSizeAxes = Axes.Both, Colour = colours.Gray2 From d0e61e5b4d609c342e55e2b8a2ed86c66977d11e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 17:14:04 +0900 Subject: [PATCH 062/202] Put back the label --- osu.Game/Screens/Edit/Setup/MetadataSection.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Setup/MetadataSection.cs b/osu.Game/Screens/Edit/Setup/MetadataSection.cs index c5a2b77ab4..f429164ece 100644 --- a/osu.Game/Screens/Edit/Setup/MetadataSection.cs +++ b/osu.Game/Screens/Edit/Setup/MetadataSection.cs @@ -28,25 +28,25 @@ namespace osu.Game.Screens.Edit.Setup }, artistTextBox = new LabelledTextBox { - PlaceholderText = "Artist", + Label = "Artist", Current = { Value = Beatmap.Metadata.Artist }, TabbableContentContainer = this }, titleTextBox = new LabelledTextBox { - PlaceholderText = "Title", + Label = "Title", Current = { Value = Beatmap.Metadata.Title }, TabbableContentContainer = this }, creatorTextBox = new LabelledTextBox { - PlaceholderText = "Creator", + Label = "Creator", Current = { Value = Beatmap.Metadata.AuthorString }, TabbableContentContainer = this }, difficultyTextBox = new LabelledTextBox { - PlaceholderText = "Difficulty Name", + Label = "Difficulty Name", Current = { Value = Beatmap.BeatmapInfo.Version }, TabbableContentContainer = this }, From a1a0074c3203e4f259fde9f87f9516acca239cf2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 18:05:11 +0900 Subject: [PATCH 063/202] Revert "Local framework" This reverts commit b9b095ee75c960287a8636d06c07afddade6865f. --- osu.Desktop.slnf | 6 ++---- osu.Game/osu.Game.csproj | 4 +--- osu.iOS.props | 4 ++-- osu.sln | 42 ---------------------------------------- 4 files changed, 5 insertions(+), 51 deletions(-) diff --git a/osu.Desktop.slnf b/osu.Desktop.slnf index 1e41d0af0e..d2c14d321a 100644 --- a/osu.Desktop.slnf +++ b/osu.Desktop.slnf @@ -15,9 +15,7 @@ "osu.Game.Tests\\osu.Game.Tests.csproj", "osu.Game.Tournament.Tests\\osu.Game.Tournament.Tests.csproj", "osu.Game.Tournament\\osu.Game.Tournament.csproj", - "osu.Game\\osu.Game.csproj", - "../osu-framework/osu.Framework/osu.Framework.csproj", - "../osu-framework/osu.Framework/osu.Framework.NativeLibs.csproj" + "osu.Game\\osu.Game.csproj" ] } -} +} \ No newline at end of file diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f2fc1726cd..90c8b98f42 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,13 +29,11 @@ + - - - diff --git a/osu.iOS.props b/osu.iOS.props index 30df8c423e..ccd33bf88c 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + diff --git a/osu.sln b/osu.sln index 4d0b3656e7..c9453359b1 100644 --- a/osu.sln +++ b/osu.sln @@ -66,12 +66,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Benchmarks", "osu.Game.Benchmarks\osu.Game.Benchmarks.csproj", "{93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework", "..\osu-framework\osu.Framework\osu.Framework.csproj", "{7EBA330C-6DD9-4F30-9332-6542D86D5BE1}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework.iOS", "..\osu-framework\osu.Framework.iOS\osu.Framework.iOS.csproj", "{7A6EEFF0-760C-4EE5-BB5E-101E7D013392}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework.NativeLibs", "..\osu-framework\osu.Framework.NativeLibs\osu.Framework.NativeLibs.csproj", "{500039B3-0706-40C3-B6E7-1FD9187644A5}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -418,42 +412,6 @@ Global {93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}.Release|iPhone.Build.0 = Release|Any CPU {93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {93632F2D-2BB4-46C1-A7B8-F8CF2FB27118}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Debug|iPhone.Build.0 = Debug|Any CPU - {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Release|Any CPU.Build.0 = Release|Any CPU - {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Release|iPhone.ActiveCfg = Release|Any CPU - {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Release|iPhone.Build.0 = Release|Any CPU - {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7EBA330C-6DD9-4F30-9332-6542D86D5BE1}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Debug|iPhone.Build.0 = Debug|Any CPU - {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Release|Any CPU.Build.0 = Release|Any CPU - {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Release|iPhone.ActiveCfg = Release|Any CPU - {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Release|iPhone.Build.0 = Release|Any CPU - {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7A6EEFF0-760C-4EE5-BB5E-101E7D013392}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {500039B3-0706-40C3-B6E7-1FD9187644A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {500039B3-0706-40C3-B6E7-1FD9187644A5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {500039B3-0706-40C3-B6E7-1FD9187644A5}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {500039B3-0706-40C3-B6E7-1FD9187644A5}.Debug|iPhone.Build.0 = Debug|Any CPU - {500039B3-0706-40C3-B6E7-1FD9187644A5}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {500039B3-0706-40C3-B6E7-1FD9187644A5}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {500039B3-0706-40C3-B6E7-1FD9187644A5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {500039B3-0706-40C3-B6E7-1FD9187644A5}.Release|Any CPU.Build.0 = Release|Any CPU - {500039B3-0706-40C3-B6E7-1FD9187644A5}.Release|iPhone.ActiveCfg = Release|Any CPU - {500039B3-0706-40C3-B6E7-1FD9187644A5}.Release|iPhone.Build.0 = Release|Any CPU - {500039B3-0706-40C3-B6E7-1FD9187644A5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {500039B3-0706-40C3-B6E7-1FD9187644A5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 577d40d8d155daf4af76278b7dce81df355eef59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 18:05:18 +0900 Subject: [PATCH 064/202] 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 5b700224db..e0392bd687 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index fa1b0a95c3..360c522193 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,7 +29,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 71fcdd45f3..b763a91dfb 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -91,7 +91,7 @@ - + From 79041c1c4b7a386781a4c01cd2d56a1e74f601eb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 18:07:42 +0900 Subject: [PATCH 065/202] Remove osuTK reference --- osu.Desktop/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 0c527ba881..d06c4b6746 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -22,7 +22,6 @@ namespace osu.Desktop { // Back up the cwd before DesktopGameHost changes it var cwd = Environment.CurrentDirectory; - bool useOsuTK = args.Contains("--tk"); using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true)) { From fccd495f27e3955f33b4a2f3fd5258fffd7b5faa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 18:07:51 +0900 Subject: [PATCH 066/202] Remove obsoleted setting for now --- osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index 036c4edfba..fb908a7669 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -53,11 +53,6 @@ namespace osu.Game.Overlays.Settings.Sections.Input LabelText = "Cursor sensitivity", Current = localSensitivity }, - new SettingsCheckbox - { - LabelText = "Map absolute input to window", - Current = config.GetBindable(FrameworkSetting.MapAbsoluteInputToWindow) - }, confineMouseModeSetting = new SettingsEnumDropdown { LabelText = "Confine mouse cursor to window", From 4bf57ad86080133e9d437a0079eb3255c8e9c789 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 18:24:24 +0900 Subject: [PATCH 067/202] Remove remaining reference to obsolete value --- osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs b/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs index 6ca7707906..bf5338d81a 100644 --- a/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs +++ b/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Platform; @@ -62,10 +61,6 @@ namespace osu.Game.Tests.Visual.Navigation RecycleLocalStorage(); - // see MouseSettings - var frameworkConfig = host.Dependencies.Get(); - frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity).Disabled = false; - CreateGame(); }); From f7ec79c5f42cd90f3201ae8855bc4d53fc5594fc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 17 Mar 2021 19:02:25 +0900 Subject: [PATCH 068/202] Fix incorrect generic type --- osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs b/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs index 768f2057a2..c1c968e862 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Navigation { AddAssert("config has migrated value", () => Precision.AlmostEquals(Game.LocalConfig.Get(OsuSetting.DisplayStarsMaximum), 10.1)); - AddStep("set value again", () => Game.LocalConfig.SetValue(OsuSetting.DisplayStarsMaximum, 10)); + AddStep("set value again", () => Game.LocalConfig.SetValue(OsuSetting.DisplayStarsMaximum, 10.0)); AddStep("force save config", () => Game.LocalConfig.Save()); From e59b8b4ce66a6b30057add268d1620d5af8be231 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 17 Mar 2021 19:07:29 +0900 Subject: [PATCH 069/202] Fix test checking nullable string --- .../Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs index 2f558a6379..591095252f 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs @@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Ranking })); AddAssert("mapped by text not present", () => - this.ChildrenOfType().All(spriteText => !containsAny(spriteText.Current.Value, "mapped", "by"))); + this.ChildrenOfType().All(spriteText => !containsAny(spriteText.Text.ToString(), "mapped", "by"))); } private void showPanel(ScoreInfo score) => Child = new ExpandedPanelMiddleContentContainer(score); From 3bfde7341f25b9fb06213426fed89a9e5accda0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 17 Mar 2021 17:14:53 +0100 Subject: [PATCH 070/202] Revert "Remove unnecessary overrides" This reverts commit f4e508b57051e887a00a8f4649e4616b762a8c8c. --- osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs | 2 ++ osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs b/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs index 1b5d576c1e..69b81d6d5c 100644 --- a/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs +++ b/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs @@ -21,6 +21,8 @@ namespace osu.Game.Rulesets.Mania.UI { private const float default_large_faint_size = 0.8f; + public override bool RemoveWhenNotAlive => true; + [Resolved] private Column column { get; set; } diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs index bef9279bac..aad9f53b93 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs @@ -13,6 +13,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy { public class LegacyHitExplosion : CompositeDrawable, IAnimatableHitExplosion { + public override bool RemoveWhenNotAlive => false; + private readonly Drawable sprite; [CanBeNull] From f1e66cc420ce69e133f08068fdc2c1affd3e02cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 17 Mar 2021 18:37:11 +0100 Subject: [PATCH 071/202] Adjust test namespace --- osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs index a824696022..aafd0ee32b 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs @@ -7,7 +7,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Screens.Edit.Components; using osuTK; -namespace osu.Game.Tests.Visual.Editor +namespace osu.Game.Tests.Visual.Editing { [TestFixture] public class TestSceneEditorClock : EditorClockTestScene From 3b55eeb416c01f1f2ba5d2fa0a8ce0d7a25d6bd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 17 Mar 2021 18:39:48 +0100 Subject: [PATCH 072/202] Fix test failure by setting beatmap Post-merge, it was failing because somewhere along the way `EditorClockTestScene` started expecting inheritors to set the beatmap themselves explicitly in BDL. --- osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs index aafd0ee32b..63d7dbc2b5 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs @@ -2,8 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.Edit.Components; using osuTK; @@ -35,6 +37,12 @@ namespace osu.Game.Tests.Visual.Editing }); } + [BackgroundDependencyLoader] + private void load() + { + Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); + } + [Test] public void TestStopAtTrackEnd() { From 21e18c9f6eae256c894ec81ff00dc529dc7762fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 17 Mar 2021 18:44:21 +0100 Subject: [PATCH 073/202] Fix test hangs in browser due to changing tracks via music controller --- osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs index 63d7dbc2b5..58375f295b 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs @@ -41,6 +41,9 @@ namespace osu.Game.Tests.Visual.Editing private void load() { Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); + // ensure that music controller does not change this beatmap due to it + // completing naturally as part of the test. + Beatmap.Disabled = true; } [Test] From 6cea74f0fada8f90bf27e3005cccd2ca5dc42706 Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 17 Mar 2021 13:13:13 -0700 Subject: [PATCH 074/202] Remove available kudosu section from user profile overlay in line with web --- .../Profile/Sections/Kudosu/KudosuInfo.cs | 34 ++----------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index d4d0976724..e5b4193f3b 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -23,44 +23,17 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu { this.user.BindTo(user); CountSection total; - CountSection avaliable; RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Masking = true; CornerRadius = 3; - Children = new Drawable[] - { - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5, 0), - Children = new[] - { - total = new CountTotal(), - avaliable = new CountAvailable() - } - } - }; - this.user.ValueChanged += u => - { - total.Count = u.NewValue?.Kudosu.Total ?? 0; - avaliable.Count = u.NewValue?.Kudosu.Available ?? 0; - }; + Child = total = new CountTotal(); + + this.user.ValueChanged += u => total.Count = u.NewValue?.Kudosu.Total ?? 0; } protected override bool OnClick(ClickEvent e) => true; - private class CountAvailable : CountSection - { - public CountAvailable() - : base("Kudosu Avaliable") - { - DescriptionText.Text = "Kudosu can be traded for kudosu stars, which will help your beatmap get more attention. This is the number of kudosu you haven't traded in yet."; - } - } - private class CountTotal : CountSection { public CountTotal() @@ -86,7 +59,6 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu public CountSection(string header) { RelativeSizeAxes = Axes.X; - Width = 0.5f; AutoSizeAxes = Axes.Y; Padding = new MarginPadding { Top = 10, Bottom = 20 }; Child = new FillFlowContainer From 599c55fca5d08f0f2a30cde6be78273b6ad24404 Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 17 Mar 2021 13:14:18 -0700 Subject: [PATCH 075/202] Update total kudosu earned description text in line with web --- osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index e5b4193f3b..87622939e2 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -40,7 +40,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu : base("Total Kudosu Earned") { DescriptionText.AddText("Based on how much of a contribution the user has made to beatmap moderation. See "); - DescriptionText.AddLink("this link", "https://osu.ppy.sh/wiki/Kudosu"); + DescriptionText.AddLink("this page", "https://osu.ppy.sh/wiki/Kudosu"); DescriptionText.AddText(" for more information."); } } From 2e63c2ce20ef4c707150f2eaf3c5a640a9c963c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 17 Mar 2021 21:50:45 +0100 Subject: [PATCH 076/202] Fix selection box operation hotkeys not registering in change handler Could lead to crashes after reversing a note cluster and playing it back. The root cause of the crash was that the hotkey operations were not ran inside of an editor change handler operation. This, in turn, caused the autoplay replay to not be regenerated after flipping an object cluster, therefore finally manifesting as a hard crash due to negative time offsets appearing in judgement results, which interfered with the default implementation of note lock. Note that this incidentally also fixes the fact that selection box hotkey operations (reverse and flip) did not handle undo/redo. --- .../Edit/Compose/Components/SelectionBox.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 2f4721f63e..9d6b44e207 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -113,16 +113,25 @@ namespace osu.Game.Screens.Edit.Compose.Components if (e.Repeat || !e.ControlPressed) return false; + bool runOperationFromHotkey(Func operation) + { + operationStarted(); + bool result = operation?.Invoke() ?? false; + operationEnded(); + + return result; + } + switch (e.Key) { case Key.G: - return CanReverse && OnReverse?.Invoke() == true; + return CanReverse && runOperationFromHotkey(OnReverse); case Key.H: - return CanScaleX && OnFlip?.Invoke(Direction.Horizontal) == true; + return CanScaleX && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Horizontal) ?? false); case Key.J: - return CanScaleY && OnFlip?.Invoke(Direction.Vertical) == true; + return CanScaleY && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Vertical) ?? false); } return base.OnKeyDown(e); From 08ffe425f9b9a1cd9eba6c3cd903264680244090 Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 17 Mar 2021 14:46:23 -0700 Subject: [PATCH 077/202] Update kudosu description color in line with web --- osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index 87622939e2..115d705766 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -103,7 +103,6 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu private void load(OverlayColourProvider colourProvider) { lineBackground.Colour = colourProvider.Highlight1; - DescriptionText.Colour = colourProvider.Foreground1; } } } From f95ce90c95495691e1d7c57e10a8c456ffbfc82c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 17 Mar 2021 23:32:08 +0100 Subject: [PATCH 078/202] Adjust kudosu count formatting --- osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index 115d705766..cdb24b784c 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -53,7 +53,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu public new int Count { - set => valueText.Text = value.ToString(); + set => valueText.Text = value.ToString("N0"); } public CountSection(string header) From df6570ebf544dd6157e14376055ec271fd9c132c Mon Sep 17 00:00:00 2001 From: voidedWarranties Date: Wed, 17 Mar 2021 15:31:16 -0700 Subject: [PATCH 079/202] Improve logic and add previously failing test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- .../Visual/Editing/TestSceneEditorClock.cs | 35 ++++++++++++++----- osu.Game/Screens/Edit/EditorClock.cs | 26 +++++++------- 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs index 58375f295b..390198be04 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs @@ -49,14 +49,33 @@ namespace osu.Game.Tests.Visual.Editing [Test] public void TestStopAtTrackEnd() { - AddStep("Reset clock", () => Clock.Seek(0)); - AddStep("Start clock", Clock.Start); - AddAssert("Clock running", () => Clock.IsRunning); - AddStep("Seek near end", () => Clock.Seek(Clock.TrackLength - 250)); - AddUntilStep("Clock stops", () => !Clock.IsRunning); - AddAssert("Clock stopped at end", () => Clock.CurrentTime == Clock.TrackLength); - AddStep("Start clock again", Clock.Start); - AddAssert("Clock looped to start", () => Clock.IsRunning && Clock.CurrentTime < 500); + AddStep("reset clock", () => Clock.Seek(0)); + + AddStep("start clock", Clock.Start); + AddAssert("clock running", () => Clock.IsRunning); + + AddStep("seek near end", () => Clock.Seek(Clock.TrackLength - 250)); + AddUntilStep("clock stops", () => !Clock.IsRunning); + + AddAssert("clock stopped at end", () => Clock.CurrentTime == Clock.TrackLength); + + AddStep("start clock again", Clock.Start); + AddAssert("clock looped to start", () => Clock.IsRunning && Clock.CurrentTime < 500); + } + + [Test] + public void TestWrapWhenStoppedAtTrackEnd() + { + AddStep("reset clock", () => Clock.Seek(0)); + + AddStep("stop clock", Clock.Stop); + AddAssert("clock stopped", () => !Clock.IsRunning); + + AddStep("seek exactly to end", () => Clock.Seek(Clock.TrackLength)); + AddAssert("clock stopped at end", () => Clock.CurrentTime == Clock.TrackLength); + + AddStep("start clock again", Clock.Start); + AddAssert("clock looped to start", () => Clock.IsRunning && Clock.CurrentTime < 500); } } } diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index 94bb4f5228..e227bd29bf 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -172,6 +172,10 @@ namespace osu.Game.Screens.Edit public void Start() { ClearTransforms(); + + if (playbackFinished) + underlyingClock.Seek(0); + underlyingClock.Start(); } @@ -222,21 +226,15 @@ namespace osu.Game.Screens.Edit { underlyingClock.ProcessFrame(); - if (IsRunning) - { - var playbackAlreadyStopped = playbackFinished; - playbackFinished = CurrentTime >= TrackLength; + var playbackAlreadyStopped = playbackFinished; + playbackFinished = CurrentTime >= TrackLength; - if (playbackFinished) - { - if (!playbackAlreadyStopped) - { - underlyingClock.Stop(); - underlyingClock.Seek(TrackLength); - } - else - underlyingClock.Seek(0); - } + if (playbackFinished && !playbackAlreadyStopped) + { + if (IsRunning) + underlyingClock.Stop(); + + underlyingClock.Seek(TrackLength); } } From bb3c3f302aa4b0cd2f0e5e8a9d80235fc7d810fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Mar 2021 15:36:07 +0900 Subject: [PATCH 080/202] Fix skin parser not stripping whitespace before parsing --- osu.Game.Tests/Resources/skin-with-space.ini | 2 ++ osu.Game.Tests/Skins/LegacySkinDecoderTest.cs | 9 +++++++++ osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 2 ++ 3 files changed, 13 insertions(+) create mode 100644 osu.Game.Tests/Resources/skin-with-space.ini diff --git a/osu.Game.Tests/Resources/skin-with-space.ini b/osu.Game.Tests/Resources/skin-with-space.ini new file mode 100644 index 0000000000..3e64257a3e --- /dev/null +++ b/osu.Game.Tests/Resources/skin-with-space.ini @@ -0,0 +1,2 @@ +[General] +Version: 2 \ No newline at end of file diff --git a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs index aedf26ee75..dcb866c99f 100644 --- a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs +++ b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs @@ -91,6 +91,15 @@ namespace osu.Game.Tests.Skins Assert.AreEqual(2.0m, decoder.Decode(stream).LegacyVersion); } + [Test] + public void TestStripWhitespace() + { + var decoder = new LegacySkinDecoder(); + using (var resStream = TestResources.OpenResource("skin-with-space.ini")) + using (var stream = new LineBufferedReader(resStream)) + Assert.AreEqual(2.0m, decoder.Decode(stream).LegacyVersion); + } + [Test] public void TestDecodeLatestVersion() { diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 2fb24c24e0..bd1b6627b4 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -36,6 +36,8 @@ namespace osu.Game.Beatmaps.Formats if (ShouldSkipLine(line)) continue; + line = line.Trim(); + if (line.StartsWith('[') && line.EndsWith(']')) { if (!Enum.TryParse(line[1..^1], out section)) From 5b0d75ee56690dbb3a121d741768515417f51ee8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Mar 2021 16:30:30 +0900 Subject: [PATCH 081/202] Only trim trailing spaces to avoid breakage in storyboard parsing --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 14 ++++++-------- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 4 +--- .../Beatmaps/Formats/LegacyStoryboardDecoder.cs | 2 -- osu.Game/Skinning/LegacyManiaSkinDecoder.cs | 2 -- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 99dffa7041..40bc75e847 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -67,16 +67,14 @@ namespace osu.Game.Beatmaps.Formats protected override void ParseLine(Beatmap beatmap, Section section, string line) { - var strippedLine = StripComments(line); - switch (section) { case Section.General: - handleGeneral(strippedLine); + handleGeneral(line); return; case Section.Editor: - handleEditor(strippedLine); + handleEditor(line); return; case Section.Metadata: @@ -84,19 +82,19 @@ namespace osu.Game.Beatmaps.Formats return; case Section.Difficulty: - handleDifficulty(strippedLine); + handleDifficulty(line); return; case Section.Events: - handleEvent(strippedLine); + handleEvent(line); return; case Section.TimingPoints: - handleTimingPoint(strippedLine); + handleTimingPoint(line); return; case Section.HitObjects: - handleHitObject(strippedLine); + handleHitObject(line); return; } diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index bd1b6627b4..10a716963e 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -36,7 +36,7 @@ namespace osu.Game.Beatmaps.Formats if (ShouldSkipLine(line)) continue; - line = line.Trim(); + line = StripComments(line).TrimEnd(); if (line.StartsWith('[') && line.EndsWith(']')) { @@ -73,8 +73,6 @@ namespace osu.Game.Beatmaps.Formats protected virtual void ParseLine(T output, Section section, string line) { - line = StripComments(line); - switch (section) { case Section.Colours: diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index b9bf6823b5..6301c42deb 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -45,8 +45,6 @@ namespace osu.Game.Beatmaps.Formats protected override void ParseLine(Storyboard storyboard, Section section, string line) { - line = StripComments(line); - switch (section) { case Section.General: diff --git a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs index 0a1de461ea..5308640bdd 100644 --- a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs +++ b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs @@ -31,8 +31,6 @@ namespace osu.Game.Skinning protected override void ParseLine(List output, Section section, string line) { - line = StripComments(line); - switch (section) { case Section.Mania: From b68dc686ee9bf7c978fd746248403a6decbf18a4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 18 Mar 2021 19:19:53 +0900 Subject: [PATCH 082/202] Fix converted mania scores not accounting for GREATs --- osu.Game/Scoring/ScoreManager.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 1e90ee1ac7..5fa971ce80 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -20,6 +20,7 @@ using osu.Game.IO.Archives; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Rulesets; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring.Legacy; @@ -157,9 +158,19 @@ namespace osu.Game.Scoring } int beatmapMaxCombo; + double accuracy = score.Accuracy; if (score.IsLegacyScore) { + if (score.RulesetID == 3) + { + // Recalculate mania's accuracy based on hit statistics. + double maxBaseScore = score.Statistics.Select(kvp => kvp.Value).Sum() * Judgement.ToNumericResult(HitResult.Perfect); + double baseScore = score.Statistics.Select(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value).Sum(); + if (maxBaseScore > 0) + accuracy = baseScore / maxBaseScore; + } + // This score is guaranteed to be an osu!stable score. // The combo must be determined through either the beatmap's max combo value or the difficulty calculator, as lazer's scoring has changed and the score statistics cannot be used. if (score.Beatmap.MaxCombo == null) @@ -176,7 +187,7 @@ namespace osu.Game.Scoring difficultyBindable.BindValueChanged(d => { if (d.NewValue is StarDifficulty diff) - updateScore(diff.MaxCombo); + updateScore(diff.MaxCombo, accuracy); }, true); return; @@ -191,10 +202,10 @@ namespace osu.Game.Scoring beatmapMaxCombo = Enum.GetValues(typeof(HitResult)).OfType().Where(r => r.AffectsCombo()).Select(r => score.Statistics.GetOrDefault(r)).Sum(); } - updateScore(beatmapMaxCombo); + updateScore(beatmapMaxCombo, accuracy); } - private void updateScore(int beatmapMaxCombo) + private void updateScore(int beatmapMaxCombo, double accuracy) { if (beatmapMaxCombo == 0) { @@ -207,7 +218,7 @@ namespace osu.Game.Scoring scoreProcessor.Mods.Value = score.Mods; - Value = (long)Math.Round(scoreProcessor.GetScore(ScoringMode.Value, beatmapMaxCombo, score.Accuracy, (double)score.MaxCombo / beatmapMaxCombo, score.Statistics)); + Value = (long)Math.Round(scoreProcessor.GetScore(ScoringMode.Value, beatmapMaxCombo, accuracy, (double)score.MaxCombo / beatmapMaxCombo, score.Statistics)); } } From 917717686a6bef391d86cb4a92683e9442ac8c78 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 18 Mar 2021 19:26:29 +0900 Subject: [PATCH 083/202] Expand explanatory comment --- osu.Game/Scoring/ScoreManager.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 5fa971ce80..7d0abc5996 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -164,7 +164,9 @@ namespace osu.Game.Scoring { if (score.RulesetID == 3) { - // Recalculate mania's accuracy based on hit statistics. + // In osu!stable, a full-GREAT score has 100% accuracy in mania. Along with a full combo, the score becomes indistinguishable from a full-PERFECT score. + // To get around this, recalculate accuracy based on the hit statistics. + // Note: This cannot be applied universally to all legacy scores, as some rulesets (e.g. catch) group multiple judgements together. double maxBaseScore = score.Statistics.Select(kvp => kvp.Value).Sum() * Judgement.ToNumericResult(HitResult.Perfect); double baseScore = score.Statistics.Select(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value).Sum(); if (maxBaseScore > 0) From 0c3c8141dac75fbc7ccd5c8d79746dc413c8df21 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 18 Mar 2021 19:39:42 +0900 Subject: [PATCH 084/202] Remove Expires and RemoveWhenNotAlive override --- osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs | 4 ---- osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs | 4 ---- 2 files changed, 8 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs index aad9f53b93..21bd35ad22 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs @@ -13,8 +13,6 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy { public class LegacyHitExplosion : CompositeDrawable, IAnimatableHitExplosion { - public override bool RemoveWhenNotAlive => false; - private readonly Drawable sprite; [CanBeNull] @@ -73,8 +71,6 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy .Then().ScaleTo(1.1f, animation_time * 0.8) .Then().ScaleTo(0.9f, animation_time * 0.4) .Then().ScaleTo(1f, animation_time * 0.2); - - Expire(true); } public void AnimateSecondHit() diff --git a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs index 5bb463353d..91e844187a 100644 --- a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs @@ -16,8 +16,6 @@ namespace osu.Game.Rulesets.Taiko.UI { internal class DefaultHitExplosion : CircularContainer, IAnimatableHitExplosion { - public override bool RemoveWhenNotAlive => false; - private readonly HitResult result; [CanBeNull] @@ -73,8 +71,6 @@ namespace osu.Game.Rulesets.Taiko.UI this.ScaleTo(3f, 1000, Easing.OutQuint); this.FadeOut(500); - - Expire(true); } public void AnimateSecondHit() From c694deb7d605759a3d7c6b7877d85bf422be4425 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Mar 2021 21:16:50 +0900 Subject: [PATCH 085/202] Revert changes to SettingsSourceAttribute class --- .../Configuration/SettingSourceAttribute.cs | 39 +------------------ 1 file changed, 2 insertions(+), 37 deletions(-) diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs index 39d7fba32b..cfce615130 100644 --- a/osu.Game/Configuration/SettingSourceAttribute.cs +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -5,10 +5,8 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; -using Humanizer; using JetBrains.Annotations; using osu.Framework.Bindables; -using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Overlays.Settings; @@ -63,21 +61,12 @@ namespace osu.Game.Configuration public static class SettingSourceExtensions { - public static IReadOnlyList CreateSettingsControls(this object obj, bool includeDisabled = true) => - createSettingsControls(obj, obj.GetOrderedSettingsSourceProperties(), includeDisabled).ToArray(); - - public static IReadOnlyList CreateSettingsControlsFromAllBindables(this object obj, bool includeDisabled = true) => - createSettingsControls(obj, obj.GetSettingsSourcePropertiesFromBindables(), includeDisabled).ToArray(); - - private static IEnumerable createSettingsControls(object obj, IEnumerable<(SettingSourceAttribute, PropertyInfo)> sourceAttribs, bool includeDisabled = true) + public static IEnumerable CreateSettingsControls(this object obj) { - foreach (var (attr, property) in sourceAttribs) + foreach (var (attr, property) in obj.GetOrderedSettingsSourceProperties()) { object value = property.GetValue(obj); - if ((value as IBindable)?.Disabled == true) - continue; - switch (value) { case BindableNumber bNumber: @@ -150,30 +139,6 @@ namespace osu.Game.Configuration } } - public static IEnumerable<(SettingSourceAttribute, PropertyInfo)> GetSettingsSourcePropertiesFromBindables(this object obj) - { - HashSet handledProperties = new HashSet(); - - // reverse and de-dupe properties to surface base class settings to the top of return order. - foreach (var type in obj.GetType().EnumerateBaseTypes().Reverse()) - { - foreach (var property in type.GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance)) - { - if (handledProperties.Contains(property.Name)) - continue; - - handledProperties.Add(property.Name); - - if (typeof(IBindable).IsAssignableFrom(property.PropertyType)) - { - var val = property.GetValue(obj); - string description = (val as IHasDescription)?.Description ?? string.Empty; - yield return (new SettingSourceAttribute(property.Name.Humanize(), description), property); - } - } - } - } - public static IEnumerable<(SettingSourceAttribute, PropertyInfo)> GetSettingsSourceProperties(this object obj) { foreach (var property in obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance)) From a8cc3a3b4468c885504c69ef9fbdb0dc62df059c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Mar 2021 21:17:04 +0900 Subject: [PATCH 086/202] Implement enable state changes locally for InputHandlers which should be toggleable --- .../Settings/Sections/InputSection.cs | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/InputSection.cs b/osu.Game/Overlays/Settings/Sections/InputSection.cs index e6aaa1ade9..8d5944f5bf 100644 --- a/osu.Game/Overlays/Settings/Sections/InputSection.cs +++ b/osu.Game/Overlays/Settings/Sections/InputSection.cs @@ -9,7 +9,6 @@ using osu.Framework.Input.Handlers.Joystick; using osu.Framework.Input.Handlers.Midi; using osu.Framework.Input.Handlers.Mouse; using osu.Framework.Platform; -using osu.Game.Configuration; using osu.Game.Overlays.Settings.Sections.Input; namespace osu.Game.Overlays.Settings.Sections @@ -70,13 +69,6 @@ namespace osu.Game.Overlays.Settings.Sections return null; } - var settingsControls = handler.CreateSettingsControlsFromAllBindables(false); - - if (settingsControls.Count == 0) - return null; - - section.AddRange(settingsControls); - return section; } @@ -89,6 +81,19 @@ namespace osu.Game.Overlays.Settings.Sections this.handler = handler; } + [BackgroundDependencyLoader] + private void load() + { + Children = new Drawable[] + { + new SettingsCheckbox + { + LabelText = "Enabled", + Current = handler.Enabled + }, + }; + } + protected override string Header => handler.Description; } } From b419d2c2e27762af7701c1823ae9e7786a730e0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 18 Mar 2021 19:52:38 +0100 Subject: [PATCH 087/202] Fix invalid xmldoc indent --- osu.Game/Skinning/LegacySkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 571d65e28b..b69e99773c 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -506,7 +506,7 @@ namespace osu.Game.Skinning Samples?.Dispose(); } - /// + /// /// A sample wrapper which keeps a reference to the contained skin to avoid finalizer garbage collection of the managing SampleStore. /// private class LegacySkinSample : ISample From b9761c819629f64d647d6178e7bbd7c9987b2b28 Mon Sep 17 00:00:00 2001 From: voidedWarranties Date: Thu, 18 Mar 2021 16:20:31 -0700 Subject: [PATCH 088/202] Further simplify logic --- osu.Game/Screens/Edit/EditorClock.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index e227bd29bf..d0197ce1ec 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -226,15 +226,15 @@ namespace osu.Game.Screens.Edit { underlyingClock.ProcessFrame(); - var playbackAlreadyStopped = playbackFinished; playbackFinished = CurrentTime >= TrackLength; - if (playbackFinished && !playbackAlreadyStopped) + if (playbackFinished) { if (IsRunning) underlyingClock.Stop(); - underlyingClock.Seek(TrackLength); + if (CurrentTime > TrackLength) + underlyingClock.Seek(TrackLength); } } From 5f31304d05fa71b356651643608aa34f8207e323 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 14:00:26 +0900 Subject: [PATCH 089/202] Give each type of slider path type a unique colour to help visually distinguish them --- .../Components/PathControlPointPiece.cs | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index e9838de63d..311ab8ee62 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -12,6 +12,7 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit; using osuTK; @@ -195,7 +196,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components markerRing.Alpha = IsSelected.Value ? 1 : 0; - Color4 colour = ControlPoint.Type.Value != null ? colours.Red : colours.Yellow; + Color4 colour = getColourFromNodeType(); if (IsHovered || IsSelected.Value) colour = colour.Lighten(1); @@ -203,5 +204,26 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components marker.Colour = colour; marker.Scale = new Vector2(slider.Scale); } + + private Color4 getColourFromNodeType() + { + if (!(ControlPoint.Type.Value is PathType pathType)) + return colours.Yellow; + + switch (pathType) + { + case PathType.Catmull: + return colours.Seafoam; + + case PathType.Bezier: + return colours.Pink; + + case PathType.PerfectCurve: + return colours.PurpleDark; + + default: + return colours.Red; + } + } } } From 0e821e857e34f810e1ede0c9f28df6678602caf3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 15:23:31 +0900 Subject: [PATCH 090/202] Remove unnecessary duplicated skin changed handling For some reason we were handling this both in `DrawableSkinnableSound` and `PoolableSkinnableSample` in very similar ways. Only one seems required. --- osu.Game/Skinning/SkinnableSound.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index edd3a2cdd3..e447f9c44c 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -139,12 +139,6 @@ namespace osu.Game.Skinning samplesContainer.ForEach(c => c.Stop()); } - protected override void SkinChanged(ISkinSource skin, bool allowFallback) - { - base.SkinChanged(skin, allowFallback); - updateSamples(); - } - private void updateSamples() { bool wasPlaying = IsPlaying; From bf4317d3f06ecd3ba29c2f43c33561e5557c3b24 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 15:34:19 +0900 Subject: [PATCH 091/202] Ensure looping is disabled on old samples when switching skins --- osu.Game/Skinning/PoolableSkinnableSample.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Skinning/PoolableSkinnableSample.cs b/osu.Game/Skinning/PoolableSkinnableSample.cs index 9103a6a960..09e087d0f2 100644 --- a/osu.Game/Skinning/PoolableSkinnableSample.cs +++ b/osu.Game/Skinning/PoolableSkinnableSample.cs @@ -83,6 +83,14 @@ namespace osu.Game.Skinning bool wasPlaying = Playing; + if (activeChannel != null) + { + // when switching away from previous samples, we don't want to call Stop() on them as it sounds better to let them play out. + // this may change in the future if we use PoolableSkinSample in more locations than gameplay. + // we *do* want to turn off looping, else we end up with an infinite looping sample running in the background. + activeChannel.Looping = false; + } + sampleContainer.Clear(); Sample = null; From 9491e6394a65de287bb05c96188db69ca7837158 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 15:46:43 +0900 Subject: [PATCH 092/202] Include the bundled skins when selecting a random skin --- osu.Game/Skinning/SkinManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 601b77e782..752c742a45 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -86,7 +86,7 @@ namespace osu.Game.Skinning public void SelectRandomSkin() { // choose from only user skins, removing the current selection to ensure a new one is chosen. - var randomChoices = GetAllUsableSkins().Where(s => s.ID > 0 && s.ID != CurrentSkinInfo.Value.ID).ToArray(); + var randomChoices = GetAllUsableSkins().Where(s => s.ID != CurrentSkinInfo.Value.ID).ToArray(); if (randomChoices.Length == 0) { From a9c4fa442a983534897b6d91e3fe9dd60d97dead Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 16:47:39 +0900 Subject: [PATCH 093/202] Avoid potential crash if an overlay is toggled before it has been loaded --- osu.Game/OsuGame.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 66b9141ce8..e5e1f6946e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -756,6 +756,10 @@ namespace osu.Game private void showOverlayAboveOthers(OverlayContainer overlay, OverlayContainer[] otherOverlays) { + // generally shouldn't ever hit this state, but protects against a crash on attempting to change ChildDepth. + if (overlay.LoadState < LoadState.Ready) + return; + otherOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); // show above others if not visible at all, else leave at current depth. From 27c38db14dee71d4f5a40300acbe90eb84d8bf3e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 16:58:08 +0900 Subject: [PATCH 094/202] Add tooltips for slider path nodes which aren't inheriting --- .../Blueprints/Sliders/Components/PathControlPointPiece.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 311ab8ee62..1390675a1a 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Graphics; @@ -24,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components /// /// A visualisation of a single in a . /// - public class PathControlPointPiece : BlueprintPiece + public class PathControlPointPiece : BlueprintPiece, IHasTooltip { public Action RequestSelection; @@ -225,5 +226,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components return colours.Red; } } + + public string TooltipText => ControlPoint.Type.Value.ToString() ?? string.Empty; } } From 0195d654cacb2e496ce7cc2b3c3b3991111eabb3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 17:09:49 +0900 Subject: [PATCH 095/202] Increase the precision of speed multiplier to match osu-stable --- osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index 0bc5605051..73337ab6f5 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -19,7 +19,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// public readonly BindableDouble SpeedMultiplierBindable = new BindableDouble(1) { - Precision = 0.1, + Precision = 0.01, Default = 1, MinValue = 0.1, MaxValue = 10 From 32c571fc94a0ca6521b25226670f79238dc9bf98 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 17:13:30 +0900 Subject: [PATCH 096/202] Adjust keyboard step to be something sensible --- osu.Game/Screens/Edit/Timing/DifficultySection.cs | 3 ++- osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Timing/DifficultySection.cs b/osu.Game/Screens/Edit/Timing/DifficultySection.cs index b87b8961f8..9d80ca0b14 100644 --- a/osu.Game/Screens/Edit/Timing/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Timing/DifficultySection.cs @@ -18,7 +18,8 @@ namespace osu.Game.Screens.Edit.Timing { multiplierSlider = new SliderWithTextBoxInput("Speed Multiplier") { - Current = new DifficultyControlPoint().SpeedMultiplierBindable + Current = new DifficultyControlPoint().SpeedMultiplierBindable, + KeyboardStep = 0.1f } }); } diff --git a/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs b/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs index f2f9f76143..10a5771520 100644 --- a/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs +++ b/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs @@ -69,6 +69,15 @@ namespace osu.Game.Screens.Edit.Timing }, true); } + /// + /// A custom step value for each key press which actuates a change on this control. + /// + public float KeyboardStep + { + get => slider.KeyboardStep; + set => slider.KeyboardStep = value; + } + public Bindable Current { get => slider.Current; From 1c865682ae3b0d4116fa8483b1e173c67ab079a3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 16:25:50 +0900 Subject: [PATCH 097/202] Add tablet configuration tests --- .../Settings/TestSceneTabletAreaSelection.cs | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 osu.Game.Tests/Visual/Settings/TestSceneTabletAreaSelection.cs diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletAreaSelection.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletAreaSelection.cs new file mode 100644 index 0000000000..30e265baaa --- /dev/null +++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletAreaSelection.cs @@ -0,0 +1,104 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using OpenTabletDriver.Plugin.Tablet; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual.Settings +{ + [TestFixture] + public class TestSceneTabletAreaSelection : OsuTestScene + { + private TabletAreaSelection areaSelection; + + [BackgroundDependencyLoader] + private void load() + { + DigitizerIdentifier testTablet = new DigitizerIdentifier + { + // size specifications in millimetres. + Width = 160, + Height = 100, + }; + + AddRange(new[] + { + areaSelection = new TabletAreaSelection(testTablet) + { + State = { Value = Visibility.Visible } + } + }); + } + } + + public class TabletAreaSelection : OsuFocusedOverlayContainer + { + private readonly DigitizerIdentifier tablet; + + private readonly Container tabletContainer; + private readonly Container usableAreaContainer; + + public TabletAreaSelection(DigitizerIdentifier tablet) + { + RelativeSizeAxes = Axes.Both; + + this.tablet = tablet; + + InternalChildren = new Drawable[] + { + tabletContainer = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(3), + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + }, + usableAreaContainer = new Container + { + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Yellow, + }, + new OsuSpriteText + { + Text = "usable area", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = Color4.Black, + Font = OsuFont.Default.With(size: 12) + } + } + }, + } + } + }; + } + + [BackgroundDependencyLoader] + private void load() + { + // TODO: handle tablet device changes etc. + tabletContainer.Size = new Vector2(tablet.Width, tablet.Height); + + usableAreaContainer.Position = new Vector2(10, 30); + usableAreaContainer.Size = new Vector2(80, 60); + } + } +} From d026c8da851a5fbd399206b8efff28b76c09c643 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Mar 2021 18:37:46 +0900 Subject: [PATCH 098/202] Initial pass of configuration interface --- .../Settings/TestSceneTabletSettings.cs | 40 +++++++ .../Sections/Input/TabletAreaSelection.cs | 79 ++++++------- .../Settings/Sections/Input/TabletSettings.cs | 111 ++++++++++++++++++ .../Settings/Sections/InputSection.cs | 6 + 4 files changed, 194 insertions(+), 42 deletions(-) create mode 100644 osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs rename osu.Game.Tests/Visual/Settings/TestSceneTabletAreaSelection.cs => osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs (55%) create mode 100644 osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs new file mode 100644 index 0000000000..be5b355e06 --- /dev/null +++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs @@ -0,0 +1,40 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Drawing; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Input.Handlers.Tablet; +using osu.Framework.Platform; +using osu.Game.Overlays.Settings.Sections.Input; + +namespace osu.Game.Tests.Visual.Settings +{ + [TestFixture] + public class TestSceneTabletSettings : OsuTestScene + { + [BackgroundDependencyLoader] + private void load(GameHost host) + { + var tabletHandler = host.AvailableInputHandlers.OfType().FirstOrDefault(); + + if (tabletHandler == null) + return; + + tabletHandler.AreaOffset.MinValue = new Size(0, 0); + tabletHandler.AreaOffset.MaxValue = new Size(160, 100); + tabletHandler.AreaOffset.Value = new Size(10, 10); + + tabletHandler.AreaSize.MinValue = new Size(0, 0); + tabletHandler.AreaSize.MaxValue = new Size(160, 100); + tabletHandler.AreaSize.Value = new Size(100, 80); + + AddRange(new Drawable[] + { + new TabletSettings(tabletHandler), + }); + } + } +} diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs similarity index 55% rename from osu.Game.Tests/Visual/Settings/TestSceneTabletAreaSelection.cs rename to osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs index 30e265baaa..31a2768735 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneTabletAreaSelection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs @@ -1,65 +1,46 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using NUnit.Framework; -using OpenTabletDriver.Plugin.Tablet; +using System.Drawing; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Handlers.Tablet; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osuTK; + using osuTK.Graphics; -namespace osu.Game.Tests.Visual.Settings +namespace osu.Game.Overlays.Settings.Sections.Input { - [TestFixture] - public class TestSceneTabletAreaSelection : OsuTestScene + public class TabletAreaSelection : CompositeDrawable { - private TabletAreaSelection areaSelection; - - [BackgroundDependencyLoader] - private void load() - { - DigitizerIdentifier testTablet = new DigitizerIdentifier - { - // size specifications in millimetres. - Width = 160, - Height = 100, - }; - - AddRange(new[] - { - areaSelection = new TabletAreaSelection(testTablet) - { - State = { Value = Visibility.Visible } - } - }); - } - } - - public class TabletAreaSelection : OsuFocusedOverlayContainer - { - private readonly DigitizerIdentifier tablet; + private readonly ITabletHandler handler; private readonly Container tabletContainer; private readonly Container usableAreaContainer; - public TabletAreaSelection(DigitizerIdentifier tablet) - { - RelativeSizeAxes = Axes.Both; + private readonly Bindable areaOffset = new BindableSize(); + private readonly Bindable areaSize = new BindableSize(); + private readonly Bindable tabletSize = new BindableSize(); - this.tablet = tablet; + public TabletAreaSelection(ITabletHandler handler) + { + this.handler = handler; + + Padding = new MarginPadding(5); InternalChildren = new Drawable[] { tabletContainer = new Container { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(3), + Masking = true, + CornerRadius = 5, + BorderThickness = 2, + BorderColour = Color4.Black, Children = new Drawable[] { new Box @@ -69,6 +50,8 @@ namespace osu.Game.Tests.Visual.Settings }, usableAreaContainer = new Container { + Masking = true, + CornerRadius = 5, Children = new Drawable[] { new Box @@ -94,11 +77,23 @@ namespace osu.Game.Tests.Visual.Settings [BackgroundDependencyLoader] private void load() { - // TODO: handle tablet device changes etc. - tabletContainer.Size = new Vector2(tablet.Width, tablet.Height); + areaOffset.BindTo(handler.AreaOffset); + areaOffset.BindValueChanged(val => + { + usableAreaContainer.MoveTo(new Vector2(val.NewValue.Width, val.NewValue.Height), 100, Easing.OutQuint); + }, true); - usableAreaContainer.Position = new Vector2(10, 30); - usableAreaContainer.Size = new Vector2(80, 60); + areaSize.BindTo(handler.AreaSize); + areaSize.BindValueChanged(val => + { + usableAreaContainer.ResizeTo(new Vector2(val.NewValue.Width, val.NewValue.Height), 100, Easing.OutQuint); + }, true); + + ((IBindable)tabletSize).BindTo(handler.TabletSize); + tabletSize.BindValueChanged(val => + { + tabletContainer.ResizeTo(new Vector2(tabletSize.Value.Width, tabletSize.Value.Height), 100, Easing.OutQuint); + }, true); } } } diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs new file mode 100644 index 0000000000..5df9c879eb --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -0,0 +1,111 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Drawing; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Input.Handlers.Tablet; +using osu.Game.Configuration; + +namespace osu.Game.Overlays.Settings.Sections.Input +{ + public class TabletSettings : SettingsSubsection + { + private readonly ITabletHandler tabletHandler; + + private readonly BindableSize areaOffset = new BindableSize(); + private readonly BindableSize areaSize = new BindableSize(); + private readonly BindableSize tabletSize = new BindableSize(); + + private readonly BindableNumber offsetX = new BindableNumber { MinValue = 0 }; + private readonly BindableNumber offsetY = new BindableNumber { MinValue = 0 }; + + private readonly BindableNumber sizeX = new BindableNumber { MinValue = 0 }; + private readonly BindableNumber sizeY = new BindableNumber { MinValue = 0 }; + + protected override string Header => "Tablet"; + + public TabletSettings(ITabletHandler tabletHandler) + { + this.tabletHandler = tabletHandler; + } + + [BackgroundDependencyLoader] + private void load(OsuConfigManager osuConfig, FrameworkConfigManager config) + { + // TODO: this should all eventually be replaced with a control that handles BindableSize. + areaOffset.BindTo(tabletHandler.AreaOffset); + areaOffset.BindValueChanged(val => + { + offsetX.Value = val.NewValue.Width; + offsetY.Value = val.NewValue.Height; + }, true); + + offsetX.BindValueChanged(val => areaOffset.Value = new Size(val.NewValue, areaOffset.Value.Height)); + offsetY.BindValueChanged(val => areaOffset.Value = new Size(areaOffset.Value.Width, val.NewValue)); + + areaSize.BindTo(tabletHandler.AreaSize); + areaSize.BindValueChanged(val => + { + sizeX.Value = val.NewValue.Width; + sizeY.Value = val.NewValue.Height; + }, true); + + sizeX.BindValueChanged(val => areaSize.Value = new Size(val.NewValue, areaSize.Value.Height)); + sizeY.BindValueChanged(val => areaSize.Value = new Size(areaSize.Value.Width, val.NewValue)); + + ((IBindable)tabletSize).BindTo(tabletHandler.TabletSize); + tabletSize.BindValueChanged(val => + { + // todo: these should propagate from a TabletChanged event or similar. + offsetX.MaxValue = val.NewValue.Width; + sizeX.Default = sizeX.MaxValue = val.NewValue.Width; + + offsetY.MaxValue = val.NewValue.Height; + sizeY.Default = sizeY.MaxValue = val.NewValue.Height; + + updateDisplay(); + }, true); + } + + private void updateDisplay() + { + if (tabletSize.Value == System.Drawing.Size.Empty) + { + Clear(); + return; + } + + Children = new Drawable[] + { + new SettingsSlider + { + LabelText = "Offset X", + Current = offsetX + }, + new SettingsSlider + { + LabelText = "Offset Y", + Current = offsetY + }, + new SettingsSlider + { + LabelText = "Size X", + Current = sizeX + }, + new SettingsSlider + { + LabelText = "Size Y", + Current = sizeY + }, + new TabletAreaSelection(tabletHandler) + { + RelativeSizeAxes = Axes.X, + Height = 100, + } + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/InputSection.cs b/osu.Game/Overlays/Settings/Sections/InputSection.cs index 8d5944f5bf..6e99891794 100644 --- a/osu.Game/Overlays/Settings/Sections/InputSection.cs +++ b/osu.Game/Overlays/Settings/Sections/InputSection.cs @@ -8,6 +8,7 @@ using osu.Framework.Input.Handlers; using osu.Framework.Input.Handlers.Joystick; using osu.Framework.Input.Handlers.Midi; using osu.Framework.Input.Handlers.Mouse; +using osu.Framework.Input.Handlers.Tablet; using osu.Framework.Platform; using osu.Game.Overlays.Settings.Sections.Input; @@ -55,6 +56,11 @@ namespace osu.Game.Overlays.Settings.Sections switch (handler) { + // ReSharper disable once SuspiciousTypeConversion.Global (net standard fuckery) + case ITabletHandler th: + section = new TabletSettings(th); + break; + case MouseHandler mh: section = new MouseSettings(mh); break; From 3b7edf13337a26a9d358b2dd70eef5773ba73332 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Mar 2021 14:45:21 +0900 Subject: [PATCH 099/202] Make tablet display always fit to size of settings area --- .../Settings/TestSceneTabletSettings.cs | 26 ++++- .../Sections/Input/TabletAreaSelection.cs | 105 ++++++++++-------- 2 files changed, 78 insertions(+), 53 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs index be5b355e06..6455f51ab9 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs @@ -2,9 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System.Drawing; -using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Handlers.Tablet; using osu.Framework.Platform; @@ -18,16 +18,13 @@ namespace osu.Game.Tests.Visual.Settings [BackgroundDependencyLoader] private void load(GameHost host) { - var tabletHandler = host.AvailableInputHandlers.OfType().FirstOrDefault(); - - if (tabletHandler == null) - return; + var tabletHandler = new TestTabletHandler(); tabletHandler.AreaOffset.MinValue = new Size(0, 0); tabletHandler.AreaOffset.MaxValue = new Size(160, 100); tabletHandler.AreaOffset.Value = new Size(10, 10); - tabletHandler.AreaSize.MinValue = new Size(0, 0); + tabletHandler.AreaSize.MinValue = new Size(10, 10); tabletHandler.AreaSize.MaxValue = new Size(160, 100); tabletHandler.AreaSize.Value = new Size(100, 80); @@ -35,6 +32,23 @@ namespace osu.Game.Tests.Visual.Settings { new TabletSettings(tabletHandler), }); + + AddStep("Test with wide tablet", () => tabletHandler.SetTabletSize(new Size(160, 100))); + AddStep("Test with square tablet", () => tabletHandler.SetTabletSize(new Size(300, 300))); + AddStep("Test with tall tablet", () => tabletHandler.SetTabletSize(new Size(100, 300))); + AddStep("Test with very tall tablet", () => tabletHandler.SetTabletSize(new Size(100, 700))); + } + + public class TestTabletHandler : ITabletHandler + { + private readonly Bindable tabletSize = new Bindable(); + + public BindableSize AreaOffset { get; } = new BindableSize(); + public BindableSize AreaSize { get; } = new BindableSize(); + public IBindable TabletSize => tabletSize; + public BindableBool Enabled { get; } = new BindableBool(true); + + public void SetTabletSize(Size size) => tabletSize.Value = size; } } } diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs index 31a2768735..775aceb5f9 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.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 System; using System.Drawing; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -11,7 +12,6 @@ using osu.Framework.Input.Handlers.Tablet; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; - using osuTK.Graphics; namespace osu.Game.Overlays.Settings.Sections.Input @@ -20,8 +20,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input { private readonly ITabletHandler handler; - private readonly Container tabletContainer; - private readonly Container usableAreaContainer; + private Container tabletContainer; + private Container usableAreaContainer; private readonly Bindable areaOffset = new BindableSize(); private readonly Bindable areaSize = new BindableSize(); @@ -30,53 +30,50 @@ namespace osu.Game.Overlays.Settings.Sections.Input public TabletAreaSelection(ITabletHandler handler) { this.handler = handler; - - Padding = new MarginPadding(5); - - InternalChildren = new Drawable[] - { - tabletContainer = new Container - { - Masking = true, - CornerRadius = 5, - BorderThickness = 2, - BorderColour = Color4.Black, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.White, - }, - usableAreaContainer = new Container - { - Masking = true, - CornerRadius = 5, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Yellow, - }, - new OsuSpriteText - { - Text = "usable area", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Colour = Color4.Black, - Font = OsuFont.Default.With(size: 12) - } - } - }, - } - } - }; } [BackgroundDependencyLoader] private void load() { + Padding = new MarginPadding(5); + + InternalChild = tabletContainer = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Masking = true, + CornerRadius = 5, + BorderThickness = 2, + BorderColour = Color4.Black, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + }, + usableAreaContainer = new Container + { + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Yellow, + }, + new OsuSpriteText + { + Text = "usable area", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = Color4.Black, + Font = OsuFont.Default.With(size: 12) + } + } + }, + } + }; + areaOffset.BindTo(handler.AreaOffset); areaOffset.BindValueChanged(val => { @@ -92,8 +89,22 @@ namespace osu.Game.Overlays.Settings.Sections.Input ((IBindable)tabletSize).BindTo(handler.TabletSize); tabletSize.BindValueChanged(val => { - tabletContainer.ResizeTo(new Vector2(tabletSize.Value.Width, tabletSize.Value.Height), 100, Easing.OutQuint); - }, true); + tabletContainer.Size = new Vector2(val.NewValue.Width, val.NewValue.Height); + }); + } + + protected override void Update() + { + base.Update(); + + var size = tabletSize.Value; + + float fitX = size.Width / DrawWidth; + float fitY = size.Height / DrawHeight; + + float adjust = MathF.Max(fitX, fitY); + + tabletContainer.Scale = new Vector2(1 / adjust); } } } From 926e40925ef0ba15bb6a3994f71e291dfd7196c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Mar 2021 15:57:06 +0900 Subject: [PATCH 100/202] Add exclude rule to fix dynamic compilations issues with settings sections --- osu.Game/Overlays/Settings/SettingsSubsection.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Settings/SettingsSubsection.cs b/osu.Game/Overlays/Settings/SettingsSubsection.cs index 1b82d973e9..6abf6283b9 100644 --- a/osu.Game/Overlays/Settings/SettingsSubsection.cs +++ b/osu.Game/Overlays/Settings/SettingsSubsection.cs @@ -8,10 +8,12 @@ using osu.Game.Graphics.Sprites; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Testing; using osu.Game.Graphics; namespace osu.Game.Overlays.Settings { + [ExcludeFromDynamicCompile] public abstract class SettingsSubsection : FillFlowContainer, IHasFilterableChildren { protected override Container Content => FlowContent; From 0a6525baee8afa45b44432e8acc0e13fb0ca1be7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Mar 2021 15:57:29 +0900 Subject: [PATCH 101/202] Fix slider bars reloading each time the tablet size is changed --- .../Settings/Sections/Input/TabletSettings.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 5df9c879eb..ac4a42e984 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -4,10 +4,8 @@ using System.Drawing; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Input.Handlers.Tablet; -using osu.Game.Configuration; namespace osu.Game.Overlays.Settings.Sections.Input { @@ -33,9 +31,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input } [BackgroundDependencyLoader] - private void load(OsuConfigManager osuConfig, FrameworkConfigManager config) + private void load() { - // TODO: this should all eventually be replaced with a control that handles BindableSize. areaOffset.BindTo(tabletHandler.AreaOffset); areaOffset.BindValueChanged(val => { @@ -59,6 +56,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input ((IBindable)tabletSize).BindTo(tabletHandler.TabletSize); tabletSize.BindValueChanged(val => { + if (tabletSize.Value == System.Drawing.Size.Empty) + return; + // todo: these should propagate from a TabletChanged event or similar. offsetX.MaxValue = val.NewValue.Width; sizeX.Default = sizeX.MaxValue = val.NewValue.Width; @@ -72,11 +72,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input private void updateDisplay() { - if (tabletSize.Value == System.Drawing.Size.Empty) - { - Clear(); + if (Children.Count > 0) return; - } Children = new Drawable[] { @@ -103,7 +100,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input new TabletAreaSelection(tabletHandler) { RelativeSizeAxes = Axes.X, - Height = 100, + Height = 300, } }; } From 94f184d113200bdca2c7c89496948a9e2e3d9990 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Mar 2021 16:02:39 +0900 Subject: [PATCH 102/202] Add feedback when area extends beyond tablet size --- .../Sections/Input/TabletAreaSelection.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs index 775aceb5f9..77b16a970d 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs @@ -59,7 +59,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4.Yellow, + Colour = Color4.White, }, new OsuSpriteText { @@ -78,21 +78,37 @@ namespace osu.Game.Overlays.Settings.Sections.Input areaOffset.BindValueChanged(val => { usableAreaContainer.MoveTo(new Vector2(val.NewValue.Width, val.NewValue.Height), 100, Easing.OutQuint); + checkBounds(); }, true); areaSize.BindTo(handler.AreaSize); areaSize.BindValueChanged(val => { usableAreaContainer.ResizeTo(new Vector2(val.NewValue.Width, val.NewValue.Height), 100, Easing.OutQuint); + checkBounds(); }, true); ((IBindable)tabletSize).BindTo(handler.TabletSize); tabletSize.BindValueChanged(val => { tabletContainer.Size = new Vector2(val.NewValue.Width, val.NewValue.Height); + checkBounds(); }); } + [Resolved] + private OsuColour colour { get; set; } + + private void checkBounds() + { + Size areaExtent = areaOffset.Value + areaSize.Value; + + bool isWithinBounds = areaExtent.Width < tabletSize.Value.Width + && areaExtent.Height < tabletSize.Value.Height; + + usableAreaContainer.FadeColour(isWithinBounds ? colour.Blue : colour.RedLight, 100); + } + protected override void Update() { base.Update(); From 464702182d6064023064720824403e8277bd19a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Mar 2021 16:23:46 +0900 Subject: [PATCH 103/202] Consume device name --- .../Visual/Settings/TestSceneTabletSettings.cs | 1 + .../Settings/Sections/Input/TabletAreaSelection.cs | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs index 6455f51ab9..1c9cd6c2ba 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs @@ -46,6 +46,7 @@ namespace osu.Game.Tests.Visual.Settings public BindableSize AreaOffset { get; } = new BindableSize(); public BindableSize AreaSize { get; } = new BindableSize(); public IBindable TabletSize => tabletSize; + public string DeviceName => "test tablet T-421"; public BindableBool Enabled { get; } = new BindableBool(true); public void SetTabletSize(Size size) => tabletSize.Value = size; diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs index 77b16a970d..54a14cd822 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs @@ -27,6 +27,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input private readonly Bindable areaSize = new BindableSize(); private readonly Bindable tabletSize = new BindableSize(); + private OsuSpriteText tabletName; + public TabletAreaSelection(ITabletHandler handler) { this.handler = handler; @@ -44,13 +46,13 @@ namespace osu.Game.Overlays.Settings.Sections.Input Masking = true, CornerRadius = 5, BorderThickness = 2, - BorderColour = Color4.Black, + BorderColour = colour.Gray3, Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4.White, + Colour = colour.Gray1, }, usableAreaContainer = new Container { @@ -59,7 +61,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4.White, + Alpha = 0.6f, }, new OsuSpriteText { @@ -71,6 +73,11 @@ namespace osu.Game.Overlays.Settings.Sections.Input } } }, + tabletName = new OsuSpriteText + { + Padding = new MarginPadding(3), + Font = OsuFont.Default.With(size: 8) + }, } }; @@ -92,6 +99,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input tabletSize.BindValueChanged(val => { tabletContainer.Size = new Vector2(val.NewValue.Width, val.NewValue.Height); + tabletName.Text = handler.DeviceName; checkBounds(); }); } From 382109c7a221f83b02b00a70e002551e7244157f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Mar 2021 16:24:08 +0900 Subject: [PATCH 104/202] Make test scene feel more like settings (width-wise) --- .../Visual/Settings/TestSceneTabletSettings.cs | 9 ++++++++- osu.Game/Overlays/SettingsPanel.cs | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs index 1c9cd6c2ba..3d65db9420 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs @@ -8,6 +8,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Handlers.Tablet; using osu.Framework.Platform; +using osu.Game.Overlays; using osu.Game.Overlays.Settings.Sections.Input; namespace osu.Game.Tests.Visual.Settings @@ -30,7 +31,13 @@ namespace osu.Game.Tests.Visual.Settings AddRange(new Drawable[] { - new TabletSettings(tabletHandler), + new TabletSettings(tabletHandler) + { + RelativeSizeAxes = Axes.None, + Width = SettingsPanel.WIDTH, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + } }); AddStep("Test with wide tablet", () => tabletHandler.SetTabletSize(new Size(160, 100))); diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index f1270f750e..8f3274b2b5 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays private const float sidebar_width = Sidebar.DEFAULT_WIDTH; - protected const float WIDTH = 400; + public const float WIDTH = 400; protected Container ContentContainer; From 2dc2cb04c317de94a762dbdb74dfede0a91b33ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Mar 2021 16:24:20 +0900 Subject: [PATCH 105/202] Fix bounds check becoming false when using full area --- .../Overlays/Settings/Sections/Input/TabletAreaSelection.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs index 54a14cd822..6a3cc46e2b 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs @@ -111,8 +111,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input { Size areaExtent = areaOffset.Value + areaSize.Value; - bool isWithinBounds = areaExtent.Width < tabletSize.Value.Width - && areaExtent.Height < tabletSize.Value.Height; + bool isWithinBounds = areaExtent.Width <= tabletSize.Value.Width + && areaExtent.Height <= tabletSize.Value.Height; usableAreaContainer.FadeColour(isWithinBounds ? colour.Blue : colour.RedLight, 100); } From 9b70f0ee1fca97f9d8d686c131dd66f5a8be20ef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Mar 2021 16:24:51 +0900 Subject: [PATCH 106/202] Tidy up visual appearance of settings and add a reset button --- .../Settings/Sections/Input/TabletSettings.cs | 57 ++++++++++++------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index ac4a42e984..ca0a0349ab 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -66,6 +66,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input offsetY.MaxValue = val.NewValue.Height; sizeY.Default = sizeY.MaxValue = val.NewValue.Height; + areaSize.Default = new Size(sizeX.Default, sizeY.Default); + updateDisplay(); }, true); } @@ -77,31 +79,44 @@ namespace osu.Game.Overlays.Settings.Sections.Input Children = new Drawable[] { - new SettingsSlider - { - LabelText = "Offset X", - Current = offsetX - }, - new SettingsSlider - { - LabelText = "Offset Y", - Current = offsetY - }, - new SettingsSlider - { - LabelText = "Size X", - Current = sizeX - }, - new SettingsSlider - { - LabelText = "Size Y", - Current = sizeY - }, new TabletAreaSelection(tabletHandler) { RelativeSizeAxes = Axes.X, Height = 300, - } + }, + new SettingsButton + { + Text = "Reset to full area", + Action = () => + { + areaOffset.SetDefault(); + areaSize.SetDefault(); + }, + }, + new SettingsCheckbox + { + LabelText = "Lock aspect ratio", + }, + new SettingsSlider + { + LabelText = "X Offset", + Current = offsetX + }, + new SettingsSlider + { + LabelText = "Y Offset", + Current = offsetY + }, + new SettingsSlider + { + LabelText = "Width", + Current = sizeX + }, + new SettingsSlider + { + LabelText = "Height", + Current = sizeY + }, }; } } From 43359553c1a3f6326ce13c0080440667ff13548e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Mar 2021 17:04:22 +0900 Subject: [PATCH 107/202] Add aspect ratio display and limiting --- .../Settings/Sections/Input/TabletSettings.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index ca0a0349ab..5d85ecf138 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.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 System; using System.Drawing; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -23,6 +24,15 @@ namespace osu.Game.Overlays.Settings.Sections.Input private readonly BindableNumber sizeX = new BindableNumber { MinValue = 0 }; private readonly BindableNumber sizeY = new BindableNumber { MinValue = 0 }; + private SettingsButton aspectResetButton; + + private readonly BindableNumber aspectRatio = new BindableFloat(1) + { + MinValue = 0.5f, + MaxValue = 2, + Precision = 0.01f, + }; + protected override string Header => "Tablet"; public TabletSettings(ITabletHandler tabletHandler) @@ -48,6 +58,33 @@ namespace osu.Game.Overlays.Settings.Sections.Input { sizeX.Value = val.NewValue.Width; sizeY.Value = val.NewValue.Height; + + float proposedAspectRatio = (float)sizeX.Value / sizeY.Value; + + aspectRatio.Value = proposedAspectRatio; + + if (proposedAspectRatio < aspectRatio.MinValue || proposedAspectRatio > aspectRatio.MaxValue) + { + // apply aspect ratio restrictions to keep things in a usable state. + + // correction is always going to be below 1. + float correction = proposedAspectRatio > aspectRatio.Value + ? aspectRatio.Value / proposedAspectRatio + : proposedAspectRatio / aspectRatio.Value; + + if (val.NewValue.Width != val.OldValue.Width) + { + if (val.NewValue.Width > val.OldValue.Width) + correction = 1 / correction; + areaSize.Value = new Size(areaSize.Value.Width, (int)(val.NewValue.Height * correction)); + } + else + { + if (val.NewValue.Height > val.OldValue.Height) + correction = 1 / correction; + areaSize.Value = new Size((int)(val.NewValue.Width * correction), areaSize.Value.Height); + } + } }, true); sizeX.BindValueChanged(val => areaSize.Value = new Size(val.NewValue, areaSize.Value.Height)); @@ -97,6 +134,15 @@ namespace osu.Game.Overlays.Settings.Sections.Input { LabelText = "Lock aspect ratio", }, + aspectResetButton = new SettingsButton + { + Text = "Take aspect ratio from screen size", + }, + new SettingsSlider + { + LabelText = "Aspect Ratio", + Current = aspectRatio + }, new SettingsSlider { LabelText = "X Offset", From e3bed4c97dff26b9af05609fa100c2d748956b3c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Mar 2021 17:57:50 +0900 Subject: [PATCH 108/202] Simplify aspect ratio application, add window conforming and direct adjustment --- .../Settings/Sections/Input/TabletSettings.cs | 120 ++++++++++++------ 1 file changed, 81 insertions(+), 39 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 5d85ecf138..9fa74eda18 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -1,12 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; +using System.ComponentModel; using System.Drawing; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Handlers.Tablet; +using osu.Framework.Platform; +using osu.Framework.Threading; namespace osu.Game.Overlays.Settings.Sections.Input { @@ -21,18 +23,28 @@ namespace osu.Game.Overlays.Settings.Sections.Input private readonly BindableNumber offsetX = new BindableNumber { MinValue = 0 }; private readonly BindableNumber offsetY = new BindableNumber { MinValue = 0 }; - private readonly BindableNumber sizeX = new BindableNumber { MinValue = 0 }; - private readonly BindableNumber sizeY = new BindableNumber { MinValue = 0 }; + private readonly BindableNumber sizeX = new BindableNumber { MinValue = 10 }; + private readonly BindableNumber sizeY = new BindableNumber { MinValue = 10 }; - private SettingsButton aspectResetButton; + [Resolved] + private GameHost host { get; set; } + + /// + /// Based on the longest available smartphone. + /// + private const float largest_feasible_aspect_ratio = 20f / 9; private readonly BindableNumber aspectRatio = new BindableFloat(1) { - MinValue = 0.5f, - MaxValue = 2, + MinValue = 1 / largest_feasible_aspect_ratio, + MaxValue = largest_feasible_aspect_ratio, Precision = 0.01f, }; + private readonly BindableBool aspectLock = new BindableBool(); + + private ScheduledDelegate aspectRatioApplication; + protected override string Header => "Tablet"; public TabletSettings(ITabletHandler tabletHandler) @@ -59,37 +71,18 @@ namespace osu.Game.Overlays.Settings.Sections.Input sizeX.Value = val.NewValue.Width; sizeY.Value = val.NewValue.Height; - float proposedAspectRatio = (float)sizeX.Value / sizeY.Value; - - aspectRatio.Value = proposedAspectRatio; - - if (proposedAspectRatio < aspectRatio.MinValue || proposedAspectRatio > aspectRatio.MaxValue) - { - // apply aspect ratio restrictions to keep things in a usable state. - - // correction is always going to be below 1. - float correction = proposedAspectRatio > aspectRatio.Value - ? aspectRatio.Value / proposedAspectRatio - : proposedAspectRatio / aspectRatio.Value; - - if (val.NewValue.Width != val.OldValue.Width) - { - if (val.NewValue.Width > val.OldValue.Width) - correction = 1 / correction; - areaSize.Value = new Size(areaSize.Value.Width, (int)(val.NewValue.Height * correction)); - } - else - { - if (val.NewValue.Height > val.OldValue.Height) - correction = 1 / correction; - areaSize.Value = new Size((int)(val.NewValue.Width * correction), areaSize.Value.Height); - } - } + aspectRatioApplication?.Cancel(); + aspectRatioApplication = Schedule(() => applyAspectRatio(val)); }, true); sizeX.BindValueChanged(val => areaSize.Value = new Size(val.NewValue, areaSize.Value.Height)); sizeY.BindValueChanged(val => areaSize.Value = new Size(areaSize.Value.Width, val.NewValue)); + aspectRatio.BindValueChanged(aspect => + { + forceAspectRatio(aspect.NewValue); + }); + ((IBindable)tabletSize).BindTo(tabletHandler.TabletSize); tabletSize.BindValueChanged(val => { @@ -109,6 +102,33 @@ namespace osu.Game.Overlays.Settings.Sections.Input }, true); } + private void applyAspectRatio(ValueChangedEvent sizeChanged) + { + float proposedAspectRatio = (float)sizeX.Value / sizeY.Value; + + if (!aspectLock.Value) + { + aspectRatio.Value = proposedAspectRatio; + + // aspect ratio was in a valid range. + if (proposedAspectRatio >= aspectRatio.MinValue && proposedAspectRatio <= aspectRatio.MaxValue) + return; + } + + if (sizeChanged.NewValue.Width != sizeChanged.OldValue.Width) + { + areaSize.Value = new Size(areaSize.Value.Width, (int)(areaSize.Value.Width / aspectRatio.Value)); + } + else + { + areaSize.Value = new Size((int)(areaSize.Value.Height * aspectRatio.Value), areaSize.Value.Height); + } + + // cancel any event which may have fired while updating variables as a result of aspect ratio limitations. + // this avoids a potential feedback loop. + aspectRatioApplication?.Cancel(); + } + private void updateDisplay() { if (Children.Count > 0) @@ -121,22 +141,24 @@ namespace osu.Game.Overlays.Settings.Sections.Input RelativeSizeAxes = Axes.X, Height = 300, }, - new SettingsButton + new DangerousSettingsButton { Text = "Reset to full area", Action = () => { + aspectLock.Value = false; + areaOffset.SetDefault(); areaSize.SetDefault(); }, }, - new SettingsCheckbox + new SettingsButton { - LabelText = "Lock aspect ratio", - }, - aspectResetButton = new SettingsButton - { - Text = "Take aspect ratio from screen size", + Text = "Conform to current game aspect ratio", + Action = () => + { + forceAspectRatio((float)host.Window.ClientSize.Width / host.Window.ClientSize.Height); + } }, new SettingsSlider { @@ -153,6 +175,11 @@ namespace osu.Game.Overlays.Settings.Sections.Input LabelText = "Y Offset", Current = offsetY }, + new SettingsCheckbox + { + LabelText = "Lock aspect ratio", + Current = aspectLock + }, new SettingsSlider { LabelText = "Width", @@ -165,5 +192,20 @@ namespace osu.Game.Overlays.Settings.Sections.Input }, }; } + + private void forceAspectRatio(float aspectRatio) + { + aspectLock.Value = false; + + int proposedHeight = (int)(sizeX.Value / aspectRatio); + + if (proposedHeight < sizeY.MaxValue) + sizeY.Value = proposedHeight; + else + sizeX.Value = (int)(sizeY.Value * aspectRatio); + + aspectRatioApplication?.Cancel(); + aspectLock.Value = true; + } } } From 932745e5c4049e8cf15074f5ecca437364d18fd9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Mar 2021 18:14:29 +0900 Subject: [PATCH 109/202] Fix remaining feedback loops --- .../Settings/Sections/Input/TabletSettings.cs | 51 ++++++++++++------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 9fa74eda18..e94df7dc1b 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.ComponentModel; using System.Drawing; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -80,7 +79,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input aspectRatio.BindValueChanged(aspect => { - forceAspectRatio(aspect.NewValue); + aspectRatioApplication?.Cancel(); + aspectRatioApplication = Schedule(() => forceAspectRatio(aspect.NewValue)); }); ((IBindable)tabletSize).BindTo(tabletHandler.TabletSize); @@ -102,31 +102,44 @@ namespace osu.Game.Overlays.Settings.Sections.Input }, true); } + private float curentAspectRatio => (float)sizeX.Value / sizeY.Value; + private void applyAspectRatio(ValueChangedEvent sizeChanged) { - float proposedAspectRatio = (float)sizeX.Value / sizeY.Value; + float proposedAspectRatio = curentAspectRatio; - if (!aspectLock.Value) + try { - aspectRatio.Value = proposedAspectRatio; + if (!aspectLock.Value) + { + // aspect ratio was in a valid range. + if (proposedAspectRatio >= aspectRatio.MinValue && proposedAspectRatio <= aspectRatio.MaxValue) + { + updateAspectRatio(); + return; + } + } - // aspect ratio was in a valid range. - if (proposedAspectRatio >= aspectRatio.MinValue && proposedAspectRatio <= aspectRatio.MaxValue) - return; + if (sizeChanged.NewValue.Width != sizeChanged.OldValue.Width) + { + areaSize.Value = new Size(areaSize.Value.Width, (int)(areaSize.Value.Width / aspectRatio.Value)); + } + else + { + areaSize.Value = new Size((int)(areaSize.Value.Height * aspectRatio.Value), areaSize.Value.Height); + } } - - if (sizeChanged.NewValue.Width != sizeChanged.OldValue.Width) + finally { - areaSize.Value = new Size(areaSize.Value.Width, (int)(areaSize.Value.Width / aspectRatio.Value)); - } - else - { - areaSize.Value = new Size((int)(areaSize.Value.Height * aspectRatio.Value), areaSize.Value.Height); + // cancel any event which may have fired while updating variables as a result of aspect ratio limitations. + // this avoids a potential feedback loop. + aspectRatioApplication?.Cancel(); } + } - // cancel any event which may have fired while updating variables as a result of aspect ratio limitations. - // this avoids a potential feedback loop. - aspectRatioApplication?.Cancel(); + private void updateAspectRatio() + { + aspectRatio.Value = curentAspectRatio; } private void updateDisplay() @@ -204,6 +217,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input else sizeX.Value = (int)(sizeY.Value * aspectRatio); + updateAspectRatio(); + aspectRatioApplication?.Cancel(); aspectLock.Value = true; } From bba25a0182660443283b91c01356742cae676e56 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Mar 2021 18:40:21 +0900 Subject: [PATCH 110/202] Tidy up draw hierarchy and bindable logic --- .../Sections/Input/TabletAreaSelection.cs | 7 +- .../Settings/Sections/Input/TabletSettings.cs | 184 +++++++++--------- 2 files changed, 95 insertions(+), 96 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs index 6a3cc46e2b..3a278820f0 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private readonly Bindable areaOffset = new BindableSize(); private readonly Bindable areaSize = new BindableSize(); - private readonly Bindable tabletSize = new BindableSize(); + private readonly IBindable tabletSize = new BindableSize(); private OsuSpriteText tabletName; @@ -95,7 +95,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input checkBounds(); }, true); - ((IBindable)tabletSize).BindTo(handler.TabletSize); + tabletSize.BindTo(handler.TabletSize); tabletSize.BindValueChanged(val => { tabletContainer.Size = new Vector2(val.NewValue.Width, val.NewValue.Height); @@ -123,6 +123,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input var size = tabletSize.Value; + if (size == System.Drawing.Size.Empty) + return; + float fitX = size.Width / DrawWidth; float fitY = size.Height / DrawHeight; diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index e94df7dc1b..3f8723025f 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private readonly BindableSize areaOffset = new BindableSize(); private readonly BindableSize areaSize = new BindableSize(); - private readonly BindableSize tabletSize = new BindableSize(); + private readonly IBindable tabletSize = new BindableSize(); private readonly BindableNumber offsetX = new BindableNumber { MinValue = 0 }; private readonly BindableNumber offsetY = new BindableNumber { MinValue = 0 }; @@ -54,99 +54,6 @@ namespace osu.Game.Overlays.Settings.Sections.Input [BackgroundDependencyLoader] private void load() { - areaOffset.BindTo(tabletHandler.AreaOffset); - areaOffset.BindValueChanged(val => - { - offsetX.Value = val.NewValue.Width; - offsetY.Value = val.NewValue.Height; - }, true); - - offsetX.BindValueChanged(val => areaOffset.Value = new Size(val.NewValue, areaOffset.Value.Height)); - offsetY.BindValueChanged(val => areaOffset.Value = new Size(areaOffset.Value.Width, val.NewValue)); - - areaSize.BindTo(tabletHandler.AreaSize); - areaSize.BindValueChanged(val => - { - sizeX.Value = val.NewValue.Width; - sizeY.Value = val.NewValue.Height; - - aspectRatioApplication?.Cancel(); - aspectRatioApplication = Schedule(() => applyAspectRatio(val)); - }, true); - - sizeX.BindValueChanged(val => areaSize.Value = new Size(val.NewValue, areaSize.Value.Height)); - sizeY.BindValueChanged(val => areaSize.Value = new Size(areaSize.Value.Width, val.NewValue)); - - aspectRatio.BindValueChanged(aspect => - { - aspectRatioApplication?.Cancel(); - aspectRatioApplication = Schedule(() => forceAspectRatio(aspect.NewValue)); - }); - - ((IBindable)tabletSize).BindTo(tabletHandler.TabletSize); - tabletSize.BindValueChanged(val => - { - if (tabletSize.Value == System.Drawing.Size.Empty) - return; - - // todo: these should propagate from a TabletChanged event or similar. - offsetX.MaxValue = val.NewValue.Width; - sizeX.Default = sizeX.MaxValue = val.NewValue.Width; - - offsetY.MaxValue = val.NewValue.Height; - sizeY.Default = sizeY.MaxValue = val.NewValue.Height; - - areaSize.Default = new Size(sizeX.Default, sizeY.Default); - - updateDisplay(); - }, true); - } - - private float curentAspectRatio => (float)sizeX.Value / sizeY.Value; - - private void applyAspectRatio(ValueChangedEvent sizeChanged) - { - float proposedAspectRatio = curentAspectRatio; - - try - { - if (!aspectLock.Value) - { - // aspect ratio was in a valid range. - if (proposedAspectRatio >= aspectRatio.MinValue && proposedAspectRatio <= aspectRatio.MaxValue) - { - updateAspectRatio(); - return; - } - } - - if (sizeChanged.NewValue.Width != sizeChanged.OldValue.Width) - { - areaSize.Value = new Size(areaSize.Value.Width, (int)(areaSize.Value.Width / aspectRatio.Value)); - } - else - { - areaSize.Value = new Size((int)(areaSize.Value.Height * aspectRatio.Value), areaSize.Value.Height); - } - } - finally - { - // cancel any event which may have fired while updating variables as a result of aspect ratio limitations. - // this avoids a potential feedback loop. - aspectRatioApplication?.Cancel(); - } - } - - private void updateAspectRatio() - { - aspectRatio.Value = curentAspectRatio; - } - - private void updateDisplay() - { - if (Children.Count > 0) - return; - Children = new Drawable[] { new TabletAreaSelection(tabletHandler) @@ -204,6 +111,91 @@ namespace osu.Game.Overlays.Settings.Sections.Input Current = sizeY }, }; + + areaOffset.BindTo(tabletHandler.AreaOffset); + areaOffset.BindValueChanged(val => + { + offsetX.Value = val.NewValue.Width; + offsetY.Value = val.NewValue.Height; + }, true); + + offsetX.BindValueChanged(val => areaOffset.Value = new Size(val.NewValue, areaOffset.Value.Height)); + offsetY.BindValueChanged(val => areaOffset.Value = new Size(areaOffset.Value.Width, val.NewValue)); + + areaSize.BindTo(tabletHandler.AreaSize); + areaSize.BindValueChanged(val => + { + sizeX.Value = val.NewValue.Width; + sizeY.Value = val.NewValue.Height; + }, true); + + sizeX.BindValueChanged(val => + { + areaSize.Value = new Size(val.NewValue, areaSize.Value.Height); + + aspectRatioApplication?.Cancel(); + aspectRatioApplication = Schedule(() => applyAspectRatio(sizeX)); + }); + + sizeY.BindValueChanged(val => + { + areaSize.Value = new Size(areaSize.Value.Width, val.NewValue); + + aspectRatioApplication?.Cancel(); + aspectRatioApplication = Schedule(() => applyAspectRatio(sizeY)); + }); + + aspectRatio.BindValueChanged(aspect => + { + aspectRatioApplication?.Cancel(); + aspectRatioApplication = Schedule(() => forceAspectRatio(aspect.NewValue)); + }); + + tabletSize.BindTo(tabletHandler.TabletSize); + tabletSize.BindValueChanged(val => + { + if (tabletSize.Value == System.Drawing.Size.Empty) + return; + + // todo: these should propagate from a TabletChanged event or similar. + offsetX.MaxValue = val.NewValue.Width; + sizeX.Default = sizeX.MaxValue = val.NewValue.Width; + + offsetY.MaxValue = val.NewValue.Height; + sizeY.Default = sizeY.MaxValue = val.NewValue.Height; + + areaSize.Default = new Size(sizeX.Default, sizeY.Default); + }, true); + } + + private void applyAspectRatio(BindableNumber sizeChanged) + { + try + { + if (!aspectLock.Value) + { + float proposedAspectRatio = curentAspectRatio; + + if (proposedAspectRatio >= aspectRatio.MinValue && proposedAspectRatio <= aspectRatio.MaxValue) + { + // aspect ratio was in a valid range. + updateAspectRatio(); + return; + } + } + + // if lock is applied (or the specified values were out of range) aim to adjust the axis the user was not adjusting to conform. + if (sizeChanged == sizeX) + sizeY.Value = (int)(areaSize.Value.Width / aspectRatio.Value); + else + sizeX.Value = (int)(areaSize.Value.Height * aspectRatio.Value); + } + finally + { + // cancel any event which may have fired while updating variables as a result of aspect ratio limitations. + // this avoids a potential feedback loop. + aspectRatioApplication?.Cancel(); + } } private void forceAspectRatio(float aspectRatio) @@ -222,5 +214,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input aspectRatioApplication?.Cancel(); aspectLock.Value = true; } + + private void updateAspectRatio() => aspectRatio.Value = curentAspectRatio; + + private float curentAspectRatio => (float)sizeX.Value / sizeY.Value; } } From a8e319a320a1aae74a29432b0794a16ce869330a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Mar 2021 18:51:43 +0900 Subject: [PATCH 111/202] Remove min/max from test scene to fix weirdness when switching test sizings --- osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs index 3d65db9420..aaf2f13953 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs @@ -21,12 +21,7 @@ namespace osu.Game.Tests.Visual.Settings { var tabletHandler = new TestTabletHandler(); - tabletHandler.AreaOffset.MinValue = new Size(0, 0); - tabletHandler.AreaOffset.MaxValue = new Size(160, 100); tabletHandler.AreaOffset.Value = new Size(10, 10); - - tabletHandler.AreaSize.MinValue = new Size(10, 10); - tabletHandler.AreaSize.MaxValue = new Size(160, 100); tabletHandler.AreaSize.Value = new Size(100, 80); AddRange(new Drawable[] From 9a6a0f3df5e8a2d4a67f4bc567ba3e5db8a76a2c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Mar 2021 23:47:08 +0900 Subject: [PATCH 112/202] Add test coverage and better UI handling of no tablet connected scenario --- .../Settings/TestSceneTabletSettings.cs | 10 +- .../Settings/Sections/Input/TabletSettings.cs | 130 +++++++++++------- 2 files changed, 89 insertions(+), 51 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs index aaf2f13953..2baeadddc0 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs @@ -8,6 +8,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Handlers.Tablet; using osu.Framework.Platform; +using osu.Framework.Utils; using osu.Game.Overlays; using osu.Game.Overlays.Settings.Sections.Input; @@ -39,6 +40,7 @@ namespace osu.Game.Tests.Visual.Settings AddStep("Test with square tablet", () => tabletHandler.SetTabletSize(new Size(300, 300))); AddStep("Test with tall tablet", () => tabletHandler.SetTabletSize(new Size(100, 300))); AddStep("Test with very tall tablet", () => tabletHandler.SetTabletSize(new Size(100, 700))); + AddStep("Test no tablet present", () => tabletHandler.SetTabletSize(System.Drawing.Size.Empty)); } public class TestTabletHandler : ITabletHandler @@ -48,10 +50,14 @@ namespace osu.Game.Tests.Visual.Settings public BindableSize AreaOffset { get; } = new BindableSize(); public BindableSize AreaSize { get; } = new BindableSize(); public IBindable TabletSize => tabletSize; - public string DeviceName => "test tablet T-421"; + public string DeviceName { get; private set; } public BindableBool Enabled { get; } = new BindableBool(true); - public void SetTabletSize(Size size) => tabletSize.Value = size; + public void SetTabletSize(Size size) + { + DeviceName = size != System.Drawing.Size.Empty ? $"test tablet T-{RNG.Next(999):000}" : string.Empty; + tabletSize.Value = size; + } } } } diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 3f8723025f..7da61cf192 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -5,9 +5,11 @@ using System.Drawing; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Input.Handlers.Tablet; using osu.Framework.Platform; using osu.Framework.Threading; +using osu.Game.Graphics.Sprites; namespace osu.Game.Overlays.Settings.Sections.Input { @@ -44,6 +46,10 @@ namespace osu.Game.Overlays.Settings.Sections.Input private ScheduledDelegate aspectRatioApplication; + private FillFlowContainer mainSettings; + + private OsuSpriteText noTabletMessage; + protected override string Header => "Tablet"; public TabletSettings(ITabletHandler tabletHandler) @@ -56,60 +62,77 @@ namespace osu.Game.Overlays.Settings.Sections.Input { Children = new Drawable[] { - new TabletAreaSelection(tabletHandler) + noTabletMessage = new OsuSpriteText { + Text = "No tablet detected!", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + mainSettings = new FillFlowContainer + { + Alpha = 0, RelativeSizeAxes = Axes.X, - Height = 300, - }, - new DangerousSettingsButton - { - Text = "Reset to full area", - Action = () => + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] { - aspectLock.Value = false; + new TabletAreaSelection(tabletHandler) + { + RelativeSizeAxes = Axes.X, + Height = 300, + Margin = new MarginPadding(10) + }, + new DangerousSettingsButton + { + Text = "Reset to full area", + Action = () => + { + aspectLock.Value = false; - areaOffset.SetDefault(); - areaSize.SetDefault(); - }, - }, - new SettingsButton - { - Text = "Conform to current game aspect ratio", - Action = () => - { - forceAspectRatio((float)host.Window.ClientSize.Width / host.Window.ClientSize.Height); + areaOffset.SetDefault(); + areaSize.SetDefault(); + }, + }, + new SettingsButton + { + Text = "Conform to current game aspect ratio", + Action = () => + { + forceAspectRatio((float)host.Window.ClientSize.Width / host.Window.ClientSize.Height); + } + }, + new SettingsSlider + { + LabelText = "Aspect Ratio", + Current = aspectRatio + }, + new SettingsSlider + { + LabelText = "X Offset", + Current = offsetX + }, + new SettingsSlider + { + LabelText = "Y Offset", + Current = offsetY + }, + new SettingsCheckbox + { + LabelText = "Lock aspect ratio", + Current = aspectLock + }, + new SettingsSlider + { + LabelText = "Width", + Current = sizeX + }, + new SettingsSlider + { + LabelText = "Height", + Current = sizeY + }, } }, - new SettingsSlider - { - LabelText = "Aspect Ratio", - Current = aspectRatio - }, - new SettingsSlider - { - LabelText = "X Offset", - Current = offsetX - }, - new SettingsSlider - { - LabelText = "Y Offset", - Current = offsetY - }, - new SettingsCheckbox - { - LabelText = "Lock aspect ratio", - Current = aspectLock - }, - new SettingsSlider - { - LabelText = "Width", - Current = sizeX - }, - new SettingsSlider - { - LabelText = "Height", - Current = sizeY - }, }; areaOffset.BindTo(tabletHandler.AreaOffset); @@ -154,8 +177,17 @@ namespace osu.Game.Overlays.Settings.Sections.Input tabletSize.BindTo(tabletHandler.TabletSize); tabletSize.BindValueChanged(val => { - if (tabletSize.Value == System.Drawing.Size.Empty) + bool tabletFound = tabletSize.Value != System.Drawing.Size.Empty; + + if (!tabletFound) + { + mainSettings.Hide(); + noTabletMessage.Show(); return; + } + + mainSettings.Show(); + noTabletMessage.Hide(); // todo: these should propagate from a TabletChanged event or similar. offsetX.MaxValue = val.NewValue.Width; From d422a6590036dc26a881fa2519da4d09111ac479 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Mar 2021 23:47:18 +0900 Subject: [PATCH 113/202] Fix initial tablet size not being initialised --- .../Settings/Sections/Input/TabletAreaSelection.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs index 3a278820f0..af144c8102 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs @@ -80,6 +80,11 @@ namespace osu.Game.Overlays.Settings.Sections.Input }, } }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); areaOffset.BindTo(handler.AreaOffset); areaOffset.BindValueChanged(val => @@ -101,7 +106,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input tabletContainer.Size = new Vector2(val.NewValue.Width, val.NewValue.Height); tabletName.Text = handler.DeviceName; checkBounds(); - }); + }, true); } [Resolved] From 9d0c8902a6e6dcc1edc369b8ebf768089f22c8a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Mar 2021 23:57:05 +0900 Subject: [PATCH 114/202] Fix margins and spacing between sub flowed items --- .../Overlays/Settings/Sections/Input/TabletAreaSelection.cs | 6 +++--- osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs index af144c8102..3b1bae7cf0 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs @@ -32,13 +32,13 @@ namespace osu.Game.Overlays.Settings.Sections.Input public TabletAreaSelection(ITabletHandler handler) { this.handler = handler; + + Padding = new MarginPadding { Horizontal = SettingsPanel.CONTENT_MARGINS }; } [BackgroundDependencyLoader] private void load() { - Padding = new MarginPadding(5); - InternalChild = tabletContainer = new Container { Anchor = Anchor.Centre, @@ -131,7 +131,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input if (size == System.Drawing.Size.Empty) return; - float fitX = size.Width / DrawWidth; + float fitX = size.Width / (DrawWidth - Padding.Left - Padding.Right); float fitY = size.Height / DrawHeight; float adjust = MathF.Max(fitX, fitY); diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 7da61cf192..b17cfced95 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -10,6 +10,7 @@ using osu.Framework.Input.Handlers.Tablet; using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Graphics.Sprites; +using osuTK; namespace osu.Game.Overlays.Settings.Sections.Input { @@ -67,12 +68,14 @@ namespace osu.Game.Overlays.Settings.Sections.Input Text = "No tablet detected!", Anchor = Anchor.Centre, Origin = Anchor.Centre, + Padding = new MarginPadding { Horizontal = SettingsPanel.CONTENT_MARGINS } }, mainSettings = new FillFlowContainer { Alpha = 0, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + Spacing = new Vector2(0, 8), Direction = FillDirection.Vertical, Children = new Drawable[] { @@ -80,7 +83,6 @@ namespace osu.Game.Overlays.Settings.Sections.Input { RelativeSizeAxes = Axes.X, Height = 300, - Margin = new MarginPadding(10) }, new DangerousSettingsButton { From 196f95ae545853c00e3da66ba9bc20e4871c75a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 12:50:02 +0900 Subject: [PATCH 115/202] Update to use new bindables and centered area offset --- .../Settings/TestSceneTabletSettings.cs | 44 +++++++----- .../Sections/Input/TabletAreaSelection.cs | 49 ++++++++------ .../Settings/Sections/Input/TabletSettings.cs | 67 ++++++++++--------- 3 files changed, 89 insertions(+), 71 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs index 2baeadddc0..a7f6c8c0d3 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Drawing; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -11,6 +10,7 @@ using osu.Framework.Platform; using osu.Framework.Utils; using osu.Game.Overlays; using osu.Game.Overlays.Settings.Sections.Input; +using osuTK; namespace osu.Game.Tests.Visual.Settings { @@ -22,9 +22,6 @@ namespace osu.Game.Tests.Visual.Settings { var tabletHandler = new TestTabletHandler(); - tabletHandler.AreaOffset.Value = new Size(10, 10); - tabletHandler.AreaSize.Value = new Size(100, 80); - AddRange(new Drawable[] { new TabletSettings(tabletHandler) @@ -36,27 +33,40 @@ namespace osu.Game.Tests.Visual.Settings } }); - AddStep("Test with wide tablet", () => tabletHandler.SetTabletSize(new Size(160, 100))); - AddStep("Test with square tablet", () => tabletHandler.SetTabletSize(new Size(300, 300))); - AddStep("Test with tall tablet", () => tabletHandler.SetTabletSize(new Size(100, 300))); - AddStep("Test with very tall tablet", () => tabletHandler.SetTabletSize(new Size(100, 700))); - AddStep("Test no tablet present", () => tabletHandler.SetTabletSize(System.Drawing.Size.Empty)); + AddStep("Test with wide tablet", () => tabletHandler.SetTabletSize(new Vector2(160, 100))); + AddStep("Test with square tablet", () => tabletHandler.SetTabletSize(new Vector2(300, 300))); + AddStep("Test with tall tablet", () => tabletHandler.SetTabletSize(new Vector2(100, 300))); + AddStep("Test with very tall tablet", () => tabletHandler.SetTabletSize(new Vector2(100, 700))); + AddStep("Test no tablet present", () => tabletHandler.SetTabletSize(Vector2.Zero)); } public class TestTabletHandler : ITabletHandler { - private readonly Bindable tabletSize = new Bindable(); + public Bindable AreaOffset { get; } = new Bindable(); + public Bindable AreaSize { get; } = new Bindable(); + + public IBindable Tablet => tablet; + + private readonly Bindable tablet = new Bindable(); - public BindableSize AreaOffset { get; } = new BindableSize(); - public BindableSize AreaSize { get; } = new BindableSize(); - public IBindable TabletSize => tabletSize; - public string DeviceName { get; private set; } public BindableBool Enabled { get; } = new BindableBool(true); - public void SetTabletSize(Size size) + public void SetTabletSize(Vector2 size) { - DeviceName = size != System.Drawing.Size.Empty ? $"test tablet T-{RNG.Next(999):000}" : string.Empty; - tabletSize.Value = size; + tablet.Value = size != Vector2.Zero ? new TabletInfo($"test tablet T-{RNG.Next(999):000}", size) : null; + + AreaSize.Default = new Vector2(size.X, size.Y); + + // if it's clear the user has not configured the area, take the full area from the tablet that was just found. + if (AreaSize.Value == Vector2.Zero) + AreaSize.SetDefault(); + + AreaOffset.Default = new Vector2(size.X / 2, size.Y / 2); + + // likewise with the position, use the centre point if it has not been configured. + // it's safe to assume no user would set their centre point to 0,0 for now. + if (AreaOffset.Value == Vector2.Zero) + AreaOffset.SetDefault(); } } } diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs index 3b1bae7cf0..c0412fb99d 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Drawing; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -23,9 +22,10 @@ namespace osu.Game.Overlays.Settings.Sections.Input private Container tabletContainer; private Container usableAreaContainer; - private readonly Bindable areaOffset = new BindableSize(); - private readonly Bindable areaSize = new BindableSize(); - private readonly IBindable tabletSize = new BindableSize(); + private readonly Bindable areaOffset = new Bindable(); + private readonly Bindable areaSize = new Bindable(); + + private readonly IBindable tablet = new Bindable(); private OsuSpriteText tabletName; @@ -56,6 +56,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input }, usableAreaContainer = new Container { + Origin = Anchor.Centre, Children = new Drawable[] { new Box @@ -89,24 +90,27 @@ namespace osu.Game.Overlays.Settings.Sections.Input areaOffset.BindTo(handler.AreaOffset); areaOffset.BindValueChanged(val => { - usableAreaContainer.MoveTo(new Vector2(val.NewValue.Width, val.NewValue.Height), 100, Easing.OutQuint); - checkBounds(); + usableAreaContainer.MoveTo(val.NewValue, 100, Easing.OutQuint) + .OnComplete(_ => checkBounds()); // required as we are using SSDQ. }, true); areaSize.BindTo(handler.AreaSize); areaSize.BindValueChanged(val => { - usableAreaContainer.ResizeTo(new Vector2(val.NewValue.Width, val.NewValue.Height), 100, Easing.OutQuint); + usableAreaContainer.ResizeTo(val.NewValue, 100, Easing.OutQuint) + .OnComplete(_ => checkBounds()); // required as we are using SSDQ. + }, true); + + tablet.BindTo(handler.Tablet); + tablet.BindValueChanged(val => + { + tabletContainer.Size = val.NewValue?.Size ?? Vector2.Zero; + tabletName.Text = val.NewValue?.Name ?? string.Empty; checkBounds(); }, true); - tabletSize.BindTo(handler.TabletSize); - tabletSize.BindValueChanged(val => - { - tabletContainer.Size = new Vector2(val.NewValue.Width, val.NewValue.Height); - tabletName.Text = handler.DeviceName; - checkBounds(); - }, true); + // initial animation should be instant. + FinishTransforms(true); } [Resolved] @@ -114,10 +118,13 @@ namespace osu.Game.Overlays.Settings.Sections.Input private void checkBounds() { - Size areaExtent = areaOffset.Value + areaSize.Value; + if (tablet.Value == null) + return; - bool isWithinBounds = areaExtent.Width <= tabletSize.Value.Width - && areaExtent.Height <= tabletSize.Value.Height; + var usableSsdq = usableAreaContainer.ScreenSpaceDrawQuad; + + bool isWithinBounds = tabletContainer.ScreenSpaceDrawQuad.Contains(usableSsdq.TopLeft) && + tabletContainer.ScreenSpaceDrawQuad.Contains(usableSsdq.BottomRight); usableAreaContainer.FadeColour(isWithinBounds ? colour.Blue : colour.RedLight, 100); } @@ -126,13 +133,11 @@ namespace osu.Game.Overlays.Settings.Sections.Input { base.Update(); - var size = tabletSize.Value; - - if (size == System.Drawing.Size.Empty) + if (!(tablet.Value?.Size is Vector2 size)) return; - float fitX = size.Width / (DrawWidth - Padding.Left - Padding.Right); - float fitY = size.Height / DrawHeight; + float fitX = size.X / (DrawWidth - Padding.Left - Padding.Right); + float fitY = size.Y / DrawHeight; float adjust = MathF.Max(fitX, fitY); diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index b17cfced95..9f81391434 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Drawing; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -18,15 +17,15 @@ namespace osu.Game.Overlays.Settings.Sections.Input { private readonly ITabletHandler tabletHandler; - private readonly BindableSize areaOffset = new BindableSize(); - private readonly BindableSize areaSize = new BindableSize(); - private readonly IBindable tabletSize = new BindableSize(); + private readonly Bindable areaOffset = new Bindable(); + private readonly Bindable areaSize = new Bindable(); + private readonly IBindable tablet = new Bindable(); - private readonly BindableNumber offsetX = new BindableNumber { MinValue = 0 }; - private readonly BindableNumber offsetY = new BindableNumber { MinValue = 0 }; + private readonly BindableNumber offsetX = new BindableNumber { MinValue = 0 }; + private readonly BindableNumber offsetY = new BindableNumber { MinValue = 0 }; - private readonly BindableNumber sizeX = new BindableNumber { MinValue = 10 }; - private readonly BindableNumber sizeY = new BindableNumber { MinValue = 10 }; + private readonly BindableNumber sizeX = new BindableNumber { MinValue = 10 }; + private readonly BindableNumber sizeY = new BindableNumber { MinValue = 10 }; [Resolved] private GameHost host { get; set; } @@ -108,12 +107,12 @@ namespace osu.Game.Overlays.Settings.Sections.Input LabelText = "Aspect Ratio", Current = aspectRatio }, - new SettingsSlider + new SettingsSlider { LabelText = "X Offset", Current = offsetX }, - new SettingsSlider + new SettingsSlider { LabelText = "Y Offset", Current = offsetY @@ -123,12 +122,12 @@ namespace osu.Game.Overlays.Settings.Sections.Input LabelText = "Lock aspect ratio", Current = aspectLock }, - new SettingsSlider + new SettingsSlider { LabelText = "Width", Current = sizeX }, - new SettingsSlider + new SettingsSlider { LabelText = "Height", Current = sizeY @@ -140,23 +139,23 @@ namespace osu.Game.Overlays.Settings.Sections.Input areaOffset.BindTo(tabletHandler.AreaOffset); areaOffset.BindValueChanged(val => { - offsetX.Value = val.NewValue.Width; - offsetY.Value = val.NewValue.Height; + offsetX.Value = val.NewValue.X; + offsetY.Value = val.NewValue.Y; }, true); - offsetX.BindValueChanged(val => areaOffset.Value = new Size(val.NewValue, areaOffset.Value.Height)); - offsetY.BindValueChanged(val => areaOffset.Value = new Size(areaOffset.Value.Width, val.NewValue)); + offsetX.BindValueChanged(val => areaOffset.Value = new Vector2(val.NewValue, areaOffset.Value.Y)); + offsetY.BindValueChanged(val => areaOffset.Value = new Vector2(areaOffset.Value.X, val.NewValue)); areaSize.BindTo(tabletHandler.AreaSize); areaSize.BindValueChanged(val => { - sizeX.Value = val.NewValue.Width; - sizeY.Value = val.NewValue.Height; + sizeX.Value = val.NewValue.X; + sizeY.Value = val.NewValue.Y; }, true); sizeX.BindValueChanged(val => { - areaSize.Value = new Size(val.NewValue, areaSize.Value.Height); + areaSize.Value = new Vector2(val.NewValue, areaSize.Value.Y); aspectRatioApplication?.Cancel(); aspectRatioApplication = Schedule(() => applyAspectRatio(sizeX)); @@ -164,7 +163,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input sizeY.BindValueChanged(val => { - areaSize.Value = new Size(areaSize.Value.Width, val.NewValue); + areaSize.Value = new Vector2(areaSize.Value.X, val.NewValue); aspectRatioApplication?.Cancel(); aspectRatioApplication = Schedule(() => applyAspectRatio(sizeY)); @@ -176,10 +175,12 @@ namespace osu.Game.Overlays.Settings.Sections.Input aspectRatioApplication = Schedule(() => forceAspectRatio(aspect.NewValue)); }); - tabletSize.BindTo(tabletHandler.TabletSize); - tabletSize.BindValueChanged(val => + tablet.BindTo(tabletHandler.Tablet); + tablet.BindValueChanged(val => { - bool tabletFound = tabletSize.Value != System.Drawing.Size.Empty; + var tab = val.NewValue; + + bool tabletFound = tab != null; if (!tabletFound) { @@ -192,17 +193,19 @@ namespace osu.Game.Overlays.Settings.Sections.Input noTabletMessage.Hide(); // todo: these should propagate from a TabletChanged event or similar. - offsetX.MaxValue = val.NewValue.Width; - sizeX.Default = sizeX.MaxValue = val.NewValue.Width; + offsetX.MaxValue = tab.Size.X; + offsetX.Default = tab.Size.X / 2; + sizeX.Default = sizeX.MaxValue = tab.Size.X; - offsetY.MaxValue = val.NewValue.Height; - sizeY.Default = sizeY.MaxValue = val.NewValue.Height; + offsetY.MaxValue = tab.Size.Y; + offsetY.Default = tab.Size.Y / 2; + sizeY.Default = sizeY.MaxValue = tab.Size.Y; - areaSize.Default = new Size(sizeX.Default, sizeY.Default); + areaSize.Default = new Vector2(sizeX.Default, sizeY.Default); }, true); } - private void applyAspectRatio(BindableNumber sizeChanged) + private void applyAspectRatio(BindableNumber sizeChanged) { try { @@ -220,9 +223,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input // if lock is applied (or the specified values were out of range) aim to adjust the axis the user was not adjusting to conform. if (sizeChanged == sizeX) - sizeY.Value = (int)(areaSize.Value.Width / aspectRatio.Value); + sizeY.Value = (int)(areaSize.Value.X / aspectRatio.Value); else - sizeX.Value = (int)(areaSize.Value.Height * aspectRatio.Value); + sizeX.Value = (int)(areaSize.Value.Y * aspectRatio.Value); } finally { @@ -251,6 +254,6 @@ namespace osu.Game.Overlays.Settings.Sections.Input private void updateAspectRatio() => aspectRatio.Value = curentAspectRatio; - private float curentAspectRatio => (float)sizeX.Value / sizeY.Value; + private float curentAspectRatio => sizeX.Value / sizeY.Value; } } From fb7d095e4a0a934664db0a5cbc7585eea55b46da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 13:03:06 +0900 Subject: [PATCH 116/202] Show aspect ratio for current usable area --- .../Sections/Input/TabletAreaSelection.cs | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs index c0412fb99d..ba219cfe7d 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs @@ -29,6 +29,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input private OsuSpriteText tabletName; + private Box usableFill; + private OsuSpriteText usableAreaText; + public TabletAreaSelection(ITabletHandler handler) { this.handler = handler; @@ -59,17 +62,16 @@ namespace osu.Game.Overlays.Settings.Sections.Input Origin = Anchor.Centre, Children = new Drawable[] { - new Box + usableFill = new Box { RelativeSizeAxes = Axes.Both, Alpha = 0.6f, }, - new OsuSpriteText + usableAreaText = new OsuSpriteText { - Text = "usable area", Anchor = Anchor.Centre, Origin = Anchor.Centre, - Colour = Color4.Black, + Colour = Color4.White, Font = OsuFont.Default.With(size: 12) } } @@ -99,6 +101,12 @@ namespace osu.Game.Overlays.Settings.Sections.Input { usableAreaContainer.ResizeTo(val.NewValue, 100, Easing.OutQuint) .OnComplete(_ => checkBounds()); // required as we are using SSDQ. + + int x = (int)val.NewValue.X; + int y = (int)val.NewValue.Y; + int commonDivider = greatestCommonDivider(x, y); + + usableAreaText.Text = $"{(float)x / commonDivider}:{(float)y / commonDivider}"; }, true); tablet.BindTo(handler.Tablet); @@ -113,6 +121,18 @@ namespace osu.Game.Overlays.Settings.Sections.Input FinishTransforms(true); } + private static int greatestCommonDivider(int a, int b) + { + while (b != 0) + { + int remainder = a % b; + a = b; + b = remainder; + } + + return a; + } + [Resolved] private OsuColour colour { get; set; } @@ -126,7 +146,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input bool isWithinBounds = tabletContainer.ScreenSpaceDrawQuad.Contains(usableSsdq.TopLeft) && tabletContainer.ScreenSpaceDrawQuad.Contains(usableSsdq.BottomRight); - usableAreaContainer.FadeColour(isWithinBounds ? colour.Blue : colour.RedLight, 100); + usableFill.FadeColour(isWithinBounds ? colour.Blue : colour.RedLight, 100); } protected override void Update() From e8c20bdcb12a3a1f513a6fa63f72511630799afe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 13:05:43 +0900 Subject: [PATCH 117/202] Add centre crosshair --- .../Sections/Input/TabletAreaSelection.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs index ba219cfe7d..0a44b1a44d 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs @@ -67,12 +67,27 @@ namespace osu.Game.Overlays.Settings.Sections.Input RelativeSizeAxes = Axes.Both, Alpha = 0.6f, }, + new Box + { + Colour = Color4.White, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Height = 5, + }, + new Box + { + Colour = Color4.White, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 5, + }, usableAreaText = new OsuSpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, Colour = Color4.White, - Font = OsuFont.Default.With(size: 12) + Font = OsuFont.Default.With(size: 12), + Y = 10 } } }, From 6285dcd1a1d9989dd9daeeb685102b48472e4ba6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 13:09:15 +0900 Subject: [PATCH 118/202] Add arbitrary value to fix FP contains check failures --- .../Overlays/Settings/Sections/Input/TabletAreaSelection.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs index 0a44b1a44d..df25668411 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs @@ -158,8 +158,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input var usableSsdq = usableAreaContainer.ScreenSpaceDrawQuad; - bool isWithinBounds = tabletContainer.ScreenSpaceDrawQuad.Contains(usableSsdq.TopLeft) && - tabletContainer.ScreenSpaceDrawQuad.Contains(usableSsdq.BottomRight); + bool isWithinBounds = tabletContainer.ScreenSpaceDrawQuad.Contains(usableSsdq.TopLeft + new Vector2(1)) && + tabletContainer.ScreenSpaceDrawQuad.Contains(usableSsdq.BottomRight - new Vector2(1)); usableFill.FadeColour(isWithinBounds ? colour.Blue : colour.RedLight, 100); } From c624aa939774cc3f783eb2a588d96c8afefc37f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 13:23:23 +0900 Subject: [PATCH 119/202] Only update tablet values on commit --- osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 9f81391434..b2d37e345f 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -104,16 +104,19 @@ namespace osu.Game.Overlays.Settings.Sections.Input }, new SettingsSlider { + TransferValueOnCommit = true, LabelText = "Aspect Ratio", Current = aspectRatio }, new SettingsSlider { + TransferValueOnCommit = true, LabelText = "X Offset", Current = offsetX }, new SettingsSlider { + TransferValueOnCommit = true, LabelText = "Y Offset", Current = offsetY }, @@ -124,11 +127,13 @@ namespace osu.Game.Overlays.Settings.Sections.Input }, new SettingsSlider { + TransferValueOnCommit = true, LabelText = "Width", Current = sizeX }, new SettingsSlider { + TransferValueOnCommit = true, LabelText = "Height", Current = sizeY }, From b1c4ac9f42842943d451f13b57da44231b34431d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 14:13:53 +0900 Subject: [PATCH 120/202] Remove local implementation of Vector2Converter This has been moved to framework in https://github.com/ppy/osu-framework/pull/4285. --- .../Converters/Vector2Converter.cs | 34 ------------------- .../IO/Serialization/IJsonSerializable.cs | 2 -- 2 files changed, 36 deletions(-) delete mode 100644 osu.Game/IO/Serialization/Converters/Vector2Converter.cs diff --git a/osu.Game/IO/Serialization/Converters/Vector2Converter.cs b/osu.Game/IO/Serialization/Converters/Vector2Converter.cs deleted file mode 100644 index 46447b607b..0000000000 --- a/osu.Game/IO/Serialization/Converters/Vector2Converter.cs +++ /dev/null @@ -1,34 +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 Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using osuTK; - -namespace osu.Game.IO.Serialization.Converters -{ - /// - /// A type of that serializes only the X and Y coordinates of a . - /// - public class Vector2Converter : JsonConverter - { - public override Vector2 ReadJson(JsonReader reader, Type objectType, Vector2 existingValue, bool hasExistingValue, JsonSerializer serializer) - { - var obj = JObject.Load(reader); - return new Vector2((float)obj["x"], (float)obj["y"]); - } - - public override void WriteJson(JsonWriter writer, Vector2 value, JsonSerializer serializer) - { - writer.WriteStartObject(); - - writer.WritePropertyName("x"); - writer.WriteValue(value.X); - writer.WritePropertyName("y"); - writer.WriteValue(value.Y); - - writer.WriteEndObject(); - } - } -} diff --git a/osu.Game/IO/Serialization/IJsonSerializable.cs b/osu.Game/IO/Serialization/IJsonSerializable.cs index ac95d47c4b..30430e6f7f 100644 --- a/osu.Game/IO/Serialization/IJsonSerializable.cs +++ b/osu.Game/IO/Serialization/IJsonSerializable.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using Newtonsoft.Json; -using osu.Game.IO.Serialization.Converters; namespace osu.Game.IO.Serialization { @@ -28,7 +27,6 @@ namespace osu.Game.IO.Serialization Formatting = Formatting.Indented, ObjectCreationHandling = ObjectCreationHandling.Replace, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, - Converters = new JsonConverter[] { new Vector2Converter() }, ContractResolver = new KeyContractResolver() }; } From 1e82033c840725d4facd32a95be7d7ebe682e908 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 14:18:56 +0900 Subject: [PATCH 121/202] Move bindings to LoadComplete to avoid cross-thread issues --- osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index b2d37e345f..19a3c5b6f2 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -140,6 +140,11 @@ namespace osu.Game.Overlays.Settings.Sections.Input } }, }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); areaOffset.BindTo(tabletHandler.AreaOffset); areaOffset.BindValueChanged(val => From fefb0078056fcfada6602ca1396318b618767bde Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Mar 2021 14:19:15 +0900 Subject: [PATCH 122/202] Remove no longer relevant comment --- osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 19a3c5b6f2..d0e3ddbd91 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -202,7 +202,6 @@ namespace osu.Game.Overlays.Settings.Sections.Input mainSettings.Show(); noTabletMessage.Hide(); - // todo: these should propagate from a TabletChanged event or similar. offsetX.MaxValue = tab.Size.X; offsetX.Default = tab.Size.X / 2; sizeX.Default = sizeX.MaxValue = tab.Size.X; From bd1e2da1c2260457234bf40d16af2f130f68765a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 19:09:39 +0900 Subject: [PATCH 123/202] Always hide other overlays, even if the new one is not loaded --- osu.Game/OsuGame.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e5e1f6946e..e2f0f0c05b 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -756,12 +756,12 @@ namespace osu.Game private void showOverlayAboveOthers(OverlayContainer overlay, OverlayContainer[] otherOverlays) { + otherOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); + // generally shouldn't ever hit this state, but protects against a crash on attempting to change ChildDepth. if (overlay.LoadState < LoadState.Ready) return; - otherOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); - // show above others if not visible at all, else leave at current depth. if (!overlay.IsPresent) overlayContent.ChangeChildDepth(overlay, (float)-Clock.CurrentTime); From c0c8b3e46c49290709a8c9948693c72857404704 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 19:22:47 +0900 Subject: [PATCH 124/202] Fix regression meaning `SkinnableSound` initialisation may never happen --- osu.Game/Skinning/SkinnableSound.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index e447f9c44c..9c6a4f7970 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -131,6 +131,14 @@ namespace osu.Game.Skinning }); } + protected override void LoadAsyncComplete() + { + base.LoadAsyncComplete(); + + if (!samplesContainer.Any()) + updateSamples(); + } + /// /// Stops the samples. /// From 9be7981e0d981cea5ed9b605ab9423e77877b7f7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 19:45:00 +0900 Subject: [PATCH 125/202] Adjust timeline ticks to be more visible --- osu.Game/Screens/Edit/BindableBeatDivisor.cs | 3 +-- .../Edit/Compose/Components/DistanceSnapGrid.cs | 2 +- .../Components/Timeline/TimelineTickDisplay.cs | 13 +++++++++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Edit/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/BindableBeatDivisor.cs index d9477dd4bc..ff33f0c70d 100644 --- a/osu.Game/Screens/Edit/BindableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/BindableBeatDivisor.cs @@ -4,7 +4,6 @@ using System; using System.Linq; using osu.Framework.Bindables; -using osu.Framework.Graphics.Colour; using osu.Game.Graphics; using osuTK.Graphics; @@ -48,7 +47,7 @@ namespace osu.Game.Screens.Edit /// The beat divisor. /// The set of colours. /// The applicable colour from for . - public static ColourInfo GetColourFor(int beatDivisor, OsuColour colours) + public static Color4 GetColourFor(int beatDivisor, OsuColour colours) { switch (beatDivisor) { diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index 8a92a2011d..59f88ac641 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -132,7 +132,7 @@ namespace osu.Game.Screens.Edit.Compose.Components var colour = BindableBeatDivisor.GetColourFor(BindableBeatDivisor.GetDivisorForBeatIndex(beatIndex + placementIndex + 1, beatDivisor.Value), Colours); int repeatIndex = placementIndex / beatDivisor.Value; - return colour.MultiplyAlpha(0.5f / (repeatIndex + 1)); + return ColourInfo.SingleColour(colour).MultiplyAlpha(0.5f / (repeatIndex + 1)); } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs index fb11b859a7..c070c833f8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs @@ -6,7 +6,9 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Caching; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; @@ -124,25 +126,28 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (beat == 0 && i == 0) nextMinTick = float.MinValue; - var indexInBar = beat % ((int)point.TimeSignature * beatDivisor.Value); + int indexInBar = beat % ((int)point.TimeSignature * beatDivisor.Value); var divisor = BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value); var colour = BindableBeatDivisor.GetColourFor(divisor, colours); + bool isMainBeat = indexInBar == 0; + // even though "bar lines" take up the full vertical space, we render them in two pieces because it allows for less anchor/origin churn. - var height = indexInBar == 0 ? 0.5f : 0.1f - (float)divisor / highestDivisor * 0.08f; + float height = isMainBeat ? 0.5f : 0.4f - (float)divisor / highestDivisor * 0.2f; + float gradientOpacity = isMainBeat ? 1 : 0; var topPoint = getNextUsablePoint(); topPoint.X = xPos; - topPoint.Colour = colour; topPoint.Height = height; + topPoint.Colour = ColourInfo.GradientVertical(colour, colour.Opacity(gradientOpacity)); topPoint.Anchor = Anchor.TopLeft; topPoint.Origin = Anchor.TopCentre; var bottomPoint = getNextUsablePoint(); bottomPoint.X = xPos; - bottomPoint.Colour = colour; bottomPoint.Anchor = Anchor.BottomLeft; + bottomPoint.Colour = ColourInfo.GradientVertical(colour.Opacity(gradientOpacity), colour); bottomPoint.Origin = Anchor.BottomCentre; bottomPoint.Height = height; } From 8955071703d40ed9f0210a45def45546bf1c19cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 20:01:45 +0900 Subject: [PATCH 126/202] Change editor speed adjust to adjust frequency --- osu.Game/Screens/Edit/Components/PlaybackControl.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/PlaybackControl.cs b/osu.Game/Screens/Edit/Components/PlaybackControl.cs index 9739f2876a..bdc6e238c8 100644 --- a/osu.Game/Screens/Edit/Components/PlaybackControl.cs +++ b/osu.Game/Screens/Edit/Components/PlaybackControl.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Edit.Components [Resolved] private EditorClock editorClock { get; set; } - private readonly BindableNumber tempo = new BindableDouble(1); + private readonly BindableNumber freqAdjust = new BindableDouble(1); [BackgroundDependencyLoader] private void load() @@ -58,16 +58,16 @@ namespace osu.Game.Screens.Edit.Components RelativeSizeAxes = Axes.Both, Height = 0.5f, Padding = new MarginPadding { Left = 45 }, - Child = new PlaybackTabControl { Current = tempo }, + Child = new PlaybackTabControl { Current = freqAdjust }, } }; - Track.BindValueChanged(tr => tr.NewValue?.AddAdjustment(AdjustableProperty.Tempo, tempo), true); + Track.BindValueChanged(tr => tr.NewValue?.AddAdjustment(AdjustableProperty.Frequency, freqAdjust), true); } protected override void Dispose(bool isDisposing) { - Track.Value?.RemoveAdjustment(AdjustableProperty.Tempo, tempo); + Track.Value?.RemoveAdjustment(AdjustableProperty.Frequency, freqAdjust); base.Dispose(isDisposing); } @@ -101,7 +101,7 @@ namespace osu.Game.Screens.Edit.Components private class PlaybackTabControl : OsuTabControl { - private static readonly double[] tempo_values = { 0.5, 0.75, 1 }; + private static readonly double[] tempo_values = { 0.25, 0.5, 0.75, 1 }; protected override TabItem CreateTabItem(double value) => new PlaybackTabItem(value); From 86b229b1c94a1e0e3379b4e013206fd5ac996771 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 20:05:18 +0900 Subject: [PATCH 127/202] Increase maximum usable aspect ratio to account for ultrawide monitors --- osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index d0e3ddbd91..73ac8e4f35 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -31,9 +31,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input private GameHost host { get; set; } /// - /// Based on the longest available smartphone. + /// Based on ultrawide monitor configurations. /// - private const float largest_feasible_aspect_ratio = 20f / 9; + private const float largest_feasible_aspect_ratio = 21f / 9; private readonly BindableNumber aspectRatio = new BindableFloat(1) { From 4795170c6044bc7092b4c78b687f5a5bdc880088 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 20:07:13 +0900 Subject: [PATCH 128/202] Add back the default json converter locally to ensure it's actually used --- osu.Game/IO/Serialization/IJsonSerializable.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/IO/Serialization/IJsonSerializable.cs b/osu.Game/IO/Serialization/IJsonSerializable.cs index 30430e6f7f..ba188963ea 100644 --- a/osu.Game/IO/Serialization/IJsonSerializable.cs +++ b/osu.Game/IO/Serialization/IJsonSerializable.cs @@ -1,7 +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.Collections.Generic; using Newtonsoft.Json; +using osu.Framework.IO.Serialization; namespace osu.Game.IO.Serialization { @@ -27,6 +29,7 @@ namespace osu.Game.IO.Serialization Formatting = Formatting.Indented, ObjectCreationHandling = ObjectCreationHandling.Replace, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, + Converters = new List { new Vector2Converter() }, ContractResolver = new KeyContractResolver() }; } From 095b7f86685799249f5427ba7010f55a6a7e6646 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 20:09:12 +0900 Subject: [PATCH 129/202] Rewrite code to account for non-loaded edge case --- osu.Game/OsuGame.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e2f0f0c05b..be919d60ca 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -758,13 +758,15 @@ namespace osu.Game { otherOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); - // generally shouldn't ever hit this state, but protects against a crash on attempting to change ChildDepth. - if (overlay.LoadState < LoadState.Ready) + // Partially visible so leave it at the current depth. + if (overlay.IsPresent) return; - // show above others if not visible at all, else leave at current depth. - if (!overlay.IsPresent) + // Show above all other overlays. + if (overlay.IsLoaded) overlayContent.ChangeChildDepth(overlay, (float)-Clock.CurrentTime); + else + overlay.Depth = (float)-Clock.CurrentTime; } private void forwardLoggedErrorsToNotifications() From 6f32c302eb692f2a8c7bf6122149a28d96672446 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 20:13:51 +0900 Subject: [PATCH 130/202] Add checkbox to optionally disable tablet handling --- .../Settings/Sections/Input/TabletSettings.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 73ac8e4f35..893fe575cd 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -62,11 +62,18 @@ namespace osu.Game.Overlays.Settings.Sections.Input { Children = new Drawable[] { + new SettingsCheckbox + { + LabelText = "Enabled", + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Current = tabletHandler.Enabled + }, noTabletMessage = new OsuSpriteText { Text = "No tablet detected!", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, Padding = new MarginPadding { Horizontal = SettingsPanel.CONTENT_MARGINS } }, mainSettings = new FillFlowContainer From 63cbac3bd059778a8b94ad0d5e5b47beab637f33 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 20:15:29 +0900 Subject: [PATCH 131/202] Ensure aspect ratio slider gets an initial value --- osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 893fe575cd..b06b148984 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -186,6 +186,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input aspectRatioApplication = Schedule(() => applyAspectRatio(sizeY)); }); + updateAspectRatio(); aspectRatio.BindValueChanged(aspect => { aspectRatioApplication?.Cancel(); From b2d8db3a92d65c2db7a26d08f276a30fef82c7c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 20:25:21 +0900 Subject: [PATCH 132/202] Rename incorrect variable --- osu.Game/Skinning/PoolableSkinnableSample.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Skinning/PoolableSkinnableSample.cs b/osu.Game/Skinning/PoolableSkinnableSample.cs index 09e087d0f2..c01a6d20cc 100644 --- a/osu.Game/Skinning/PoolableSkinnableSample.cs +++ b/osu.Game/Skinning/PoolableSkinnableSample.cs @@ -94,21 +94,21 @@ namespace osu.Game.Skinning sampleContainer.Clear(); Sample = null; - var ch = CurrentSkin.GetSample(sampleInfo); + var sample = CurrentSkin.GetSample(sampleInfo); - if (ch == null && AllowDefaultFallback) + if (sample == null && AllowDefaultFallback) { foreach (var lookup in sampleInfo.LookupNames) { - if ((ch = sampleStore.Get(lookup)) != null) + if ((sample = sampleStore.Get(lookup)) != null) break; } } - if (ch == null) + if (sample == null) return; - sampleContainer.Add(Sample = new DrawableSample(ch)); + sampleContainer.Add(Sample = new DrawableSample(sample)); // Start playback internally for the new sample if the previous one was playing beforehand. if (wasPlaying && Looping) From 68aaf90702458f288913e191c4d27c342aa7a358 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 20:30:52 +0900 Subject: [PATCH 133/202] Fix disposal rather than performing some weird hack --- osu.Game/Skinning/LegacySkin.cs | 4 +++- osu.Game/Skinning/PoolableSkinnableSample.cs | 8 -------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index b69e99773c..ec49d43c67 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -509,7 +509,7 @@ namespace osu.Game.Skinning /// /// A sample wrapper which keeps a reference to the contained skin to avoid finalizer garbage collection of the managing SampleStore. /// - private class LegacySkinSample : ISample + private class LegacySkinSample : ISample, IDisposable { private readonly Sample sample; @@ -575,6 +575,8 @@ namespace osu.Game.Skinning public IBindable AggregateFrequency => sample.AggregateFrequency; public IBindable AggregateTempo => sample.AggregateTempo; + + public void Dispose() => sample.Dispose(); } } } diff --git a/osu.Game/Skinning/PoolableSkinnableSample.cs b/osu.Game/Skinning/PoolableSkinnableSample.cs index c01a6d20cc..b04158a58f 100644 --- a/osu.Game/Skinning/PoolableSkinnableSample.cs +++ b/osu.Game/Skinning/PoolableSkinnableSample.cs @@ -83,14 +83,6 @@ namespace osu.Game.Skinning bool wasPlaying = Playing; - if (activeChannel != null) - { - // when switching away from previous samples, we don't want to call Stop() on them as it sounds better to let them play out. - // this may change in the future if we use PoolableSkinSample in more locations than gameplay. - // we *do* want to turn off looping, else we end up with an infinite looping sample running in the background. - activeChannel.Looping = false; - } - sampleContainer.Clear(); Sample = null; From 9634560d4b761a2a3aa90d78ffb1933e2c96c7c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 19 Mar 2021 21:36:28 +0100 Subject: [PATCH 134/202] Fix control point visualiser crashing after deselections `SliderSelectionBlueprint.OnDeselected()` would expire the `ControlPointVisualiser` on deselection, leading to its removal from the blueprint and eventual disposal, but still kept a separate reference to said visualiser in another field. This could lead to that stale reference to a disposed child getting read in `ReceivePositionalInputAt()`, crashing quite a ways down over at the framework side on futilely trying to compute the bounding box of a drawable with no parent. --- .../Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 3d3dff653a..befe3c6695 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -114,6 +114,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders // throw away frame buffers on deselection. ControlPointVisualiser?.Expire(); + ControlPointVisualiser = null; + BodyPiece.RecyclePath(); } From e67c759eef36a409e4b57ab1c7643eb307280cd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 19 Mar 2021 22:44:31 +0100 Subject: [PATCH 135/202] Mark control point visualiser as possibly-null --- .../Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index befe3c6695..ba9bb3c485 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -28,6 +29,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders protected SliderBodyPiece BodyPiece { get; private set; } protected SliderCircleSelectionBlueprint HeadBlueprint { get; private set; } protected SliderCircleSelectionBlueprint TailBlueprint { get; private set; } + + [CanBeNull] protected PathControlPointVisualiser ControlPointVisualiser { get; private set; } private readonly DrawableSlider slider; From 8e0536e1e2d2eb66a7572dcf27f19d65cb4f5e7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 19 Mar 2021 22:20:26 +0100 Subject: [PATCH 136/202] Add failing test scene --- .../Editing/TestSceneBlueprintSelection.cs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs new file mode 100644 index 0000000000..fd9c09fd5f --- /dev/null +++ b/osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs @@ -0,0 +1,70 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Screens.Edit.Compose.Components; +using osu.Game.Tests.Beatmaps; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Editing +{ + public class TestSceneBlueprintSelection : EditorTestScene + { + protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false); + + private BlueprintContainer blueprintContainer + => Editor.ChildrenOfType().First(); + + [Test] + public void TestSelectedObjectHasPriorityWhenOverlapping() + { + var firstSlider = new Slider + { + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2()), + new PathControlPoint(new Vector2(150, -50)), + new PathControlPoint(new Vector2(300, 0)) + }), + Position = new Vector2(0, 100) + }; + var secondSlider = new Slider + { + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2()), + new PathControlPoint(new Vector2(-50, 50)), + new PathControlPoint(new Vector2(-100, 100)) + }), + Position = new Vector2(200, 0) + }; + + AddStep("add overlapping sliders", () => + { + EditorBeatmap.Add(firstSlider); + EditorBeatmap.Add(secondSlider); + }); + AddStep("select first slider", () => EditorBeatmap.SelectedHitObjects.Add(firstSlider)); + + AddStep("move mouse to common point", () => + { + var pos = blueprintContainer.ChildrenOfType().ElementAt(1).ScreenSpaceDrawQuad.Centre; + InputManager.MoveMouseTo(pos); + }); + AddStep("right click", () => InputManager.Click(MouseButton.Right)); + + AddAssert("selection is unchanged", () => EditorBeatmap.SelectedHitObjects.Single() == firstSlider); + } + } +} From dd48b68f8ad6fc513d17f9847d0863df2c90dc1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 19 Mar 2021 22:20:40 +0100 Subject: [PATCH 137/202] Ensure selected blueprints are given selection priority --- osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 051d0766bf..7def7e1d16 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -338,7 +338,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private bool beginClickSelection(MouseButtonEvent e) { // Iterate from the top of the input stack (blueprints closest to the front of the screen first). - foreach (SelectionBlueprint blueprint in SelectionBlueprints.AliveChildren.Reverse()) + // Priority is given to already-selected blueprints. + foreach (SelectionBlueprint blueprint in SelectionBlueprints.AliveChildren.Reverse().OrderByDescending(b => b.IsSelected)) { if (!blueprint.IsHovered) continue; From ca943a897a387e7a7f7d8a934aee7125f262f698 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 20 Mar 2021 10:51:58 +0900 Subject: [PATCH 138/202] Fix back to front initialisation order --- osu.Game/Skinning/SkinnableSound.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 9c6a4f7970..f935adf7a5 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -133,10 +133,11 @@ namespace osu.Game.Skinning protected override void LoadAsyncComplete() { - base.LoadAsyncComplete(); - + // ensure samples are constructed before SkinChanged() is called via base.LoadAsyncComplete(). if (!samplesContainer.Any()) updateSamples(); + + base.LoadAsyncComplete(); } /// From d28bed6ed29f9aeea901ccbec2c075367007fb47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 20 Mar 2021 12:29:24 +0100 Subject: [PATCH 139/202] Schedule adding transforms on tablet changes Fixes `InvalidThreadForMutationException`s that pop up when disconnecting/reconnecting tablets during the game's operation. In those cases the value change callback executes from an OpenTabletDriver thread. --- .../Sections/Input/TabletAreaSelection.cs | 15 ++++++----- .../Settings/Sections/Input/TabletSettings.cs | 25 +++++++++++++------ 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs index df25668411..ecb8acce54 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs @@ -125,17 +125,20 @@ namespace osu.Game.Overlays.Settings.Sections.Input }, true); tablet.BindTo(handler.Tablet); - tablet.BindValueChanged(val => - { - tabletContainer.Size = val.NewValue?.Size ?? Vector2.Zero; - tabletName.Text = val.NewValue?.Name ?? string.Empty; - checkBounds(); - }, true); + tablet.BindValueChanged(_ => Scheduler.AddOnce(updateTabletDetails)); + updateTabletDetails(); // initial animation should be instant. FinishTransforms(true); } + private void updateTabletDetails() + { + tabletContainer.Size = tablet.Value?.Size ?? Vector2.Zero; + tabletName.Text = tablet.Value?.Name ?? string.Empty; + checkBounds(); + } + private static int greatestCommonDivider(int a, int b) { while (b != 0) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index b06b148984..4baf43783d 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -196,19 +196,13 @@ namespace osu.Game.Overlays.Settings.Sections.Input tablet.BindTo(tabletHandler.Tablet); tablet.BindValueChanged(val => { + Scheduler.AddOnce(toggleVisibility); + var tab = val.NewValue; bool tabletFound = tab != null; - if (!tabletFound) - { - mainSettings.Hide(); - noTabletMessage.Show(); return; - } - - mainSettings.Show(); - noTabletMessage.Hide(); offsetX.MaxValue = tab.Size.X; offsetX.Default = tab.Size.X / 2; @@ -222,6 +216,21 @@ namespace osu.Game.Overlays.Settings.Sections.Input }, true); } + private void toggleVisibility() + { + bool tabletFound = tablet.Value != null; + + if (!tabletFound) + { + mainSettings.Hide(); + noTabletMessage.Show(); + return; + } + + mainSettings.Show(); + noTabletMessage.Hide(); + } + private void applyAspectRatio(BindableNumber sizeChanged) { try From 86b569f5f7a13700b7ef728fafb6fe1c014ea8d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 20 Mar 2021 12:34:41 +0100 Subject: [PATCH 140/202] Fix typo in identifier --- osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 4baf43783d..bd0f7ddc4c 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -237,7 +237,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input { if (!aspectLock.Value) { - float proposedAspectRatio = curentAspectRatio; + float proposedAspectRatio = currentAspectRatio; if (proposedAspectRatio >= aspectRatio.MinValue && proposedAspectRatio <= aspectRatio.MaxValue) { @@ -278,8 +278,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input aspectLock.Value = true; } - private void updateAspectRatio() => aspectRatio.Value = curentAspectRatio; + private void updateAspectRatio() => aspectRatio.Value = currentAspectRatio; - private float curentAspectRatio => sizeX.Value / sizeY.Value; + private float currentAspectRatio => sizeX.Value / sizeY.Value; } } From a16c0641b27ab69f59c56a4cf96f388e941affa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 21 Mar 2021 11:01:06 +0100 Subject: [PATCH 141/202] Revert EF Core to version 2.2 This reverts commit f3faad74d587bbbd104395f5072723203c9d54aa, reversing changes made to 712e7bc7bfaa94dd8c7248d9e800e7bc916476c0. Several issues arose after migrating to 5.0, including, but possibly not limited to, performance regressions in song select, as well as failures when attempting to save beatmaps after metadata changes in the editor. --- osu.Desktop/osu.Desktop.csproj | 4 +- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- .../osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- .../TestSceneMultiplayerMatchSongSelect.cs | 1 - .../TestSceneBeatmapRecommendations.cs | 1 - .../SongSelect/TestScenePlaySongSelect.cs | 4 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game/Beatmaps/BeatmapManager.cs | 2 - osu.Game/Database/ArchiveModelManager.cs | 6 +- .../Database/DatabaseWorkaroundExtensions.cs | 71 ------------------- osu.Game/Database/OsuDbContext.cs | 9 +-- osu.Game/Scoring/ScoreInfo.cs | 13 +++- osu.Game/Scoring/ScoreManager.cs | 5 -- osu.Game/Skinning/SkinManager.cs | 5 -- osu.Game/osu.Game.csproj | 6 +- osu.iOS.props | 2 + 18 files changed, 30 insertions(+), 109 deletions(-) delete mode 100644 osu.Game/Database/DatabaseWorkaroundExtensions.cs diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index d9d23dea6b..3e0f0cb7f6 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -27,8 +27,8 @@ - - + + diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index 42f70151ac..728af5124e 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -5,7 +5,7 @@ - + WinExe diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index e51b20c9fe..af16f39563 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -5,7 +5,7 @@ - + WinExe diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index f1f75148ef..3d2d1f3fec 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -5,7 +5,7 @@ - + WinExe diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index c9a320bdd5..fa00922706 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -5,7 +5,7 @@ - + WinExe diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs index 8cfe5d8af2..faa5d9e6fc 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs @@ -56,7 +56,6 @@ namespace osu.Game.Tests.Visual.Multiplayer beatmaps.Add(new BeatmapInfo { Ruleset = rulesets.GetRuleset(i % 4), - RulesetID = i % 4, // workaround for efcore 5 compatibility. OnlineBeatmapID = beatmapId, Length = length, BPM = bpm, diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index 9b8b74e6f6..53a956c77c 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -186,7 +186,6 @@ namespace osu.Game.Tests.Visual.SongSelect Metadata = metadata, BaseDifficulty = new BeatmapDifficulty(), Ruleset = ruleset, - RulesetID = ruleset.ID.GetValueOrDefault(), // workaround for efcore 5 compatibility. StarDifficulty = difficultyIndex + 1, Version = $"SR{difficultyIndex + 1}" }).ToList() diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 057b539e44..5731b1ac2c 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -911,11 +911,9 @@ namespace osu.Game.Tests.Visual.SongSelect int length = RNG.Next(30000, 200000); double bpm = RNG.NextSingle(80, 200); - var ruleset = getRuleset(); beatmaps.Add(new BeatmapInfo { - Ruleset = ruleset, - RulesetID = ruleset.ID.GetValueOrDefault(), // workaround for efcore 5 compatibility. + Ruleset = getRuleset(), OnlineBeatmapID = beatmapId, Version = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})", Length = length, diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 6f8e0fac6f..e36b3cdc74 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 115d1b33bb..b4ea898b7d 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -171,8 +171,6 @@ namespace osu.Game.Beatmaps if (beatmapSet.Beatmaps.Any(b => b.BaseDifficulty == null)) throw new InvalidOperationException($"Cannot import {nameof(BeatmapInfo)} with null {nameof(BeatmapInfo.BaseDifficulty)}."); - beatmapSet.Requery(ContextFactory); - // check if a set already exists with the same online id, delete if it does. if (beatmapSet.OnlineBeatmapSetID != null) { diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 64428882ac..d809dbcb01 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -462,8 +462,6 @@ namespace osu.Game.Database // Dereference the existing file info, since the file model will be removed. if (file.FileInfo != null) { - file.Requery(usage.Context); - Files.Dereference(file.FileInfo); // This shouldn't be required, but here for safety in case the provided TModel is not being change tracked @@ -637,12 +635,10 @@ namespace osu.Game.Database { using (Stream s = reader.GetStream(file)) { - var fileInfo = files.Add(s); fileInfos.Add(new TFileModel { Filename = file.Substring(prefix.Length).ToStandardisedPath(), - FileInfo = fileInfo, - FileInfoID = fileInfo.ID // workaround for efcore 5 compatibility. + FileInfo = files.Add(s) }); } } diff --git a/osu.Game/Database/DatabaseWorkaroundExtensions.cs b/osu.Game/Database/DatabaseWorkaroundExtensions.cs deleted file mode 100644 index a3a982f232..0000000000 --- a/osu.Game/Database/DatabaseWorkaroundExtensions.cs +++ /dev/null @@ -1,71 +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 osu.Game.Beatmaps; -using osu.Game.Scoring; -using osu.Game.Skinning; - -namespace osu.Game.Database -{ - /// - /// Extension methods which contain workarounds to make EFcore 5.x work with our existing (incorrect) thread safety. - /// The intention is to avoid blocking package updates while we consider the future of the database backend, with a potential backend switch imminent. - /// - public static class DatabaseWorkaroundExtensions - { - /// - /// Re-query the provided model to ensure it is in a sane state. This method requires explicit implementation per model type. - /// - /// - /// - public static void Requery(this IHasPrimaryKey model, IDatabaseContextFactory contextFactory) - { - switch (model) - { - case SkinInfo skinInfo: - requeryFiles(skinInfo.Files, contextFactory); - break; - - case ScoreInfo scoreInfo: - requeryFiles(scoreInfo.Beatmap.BeatmapSet.Files, contextFactory); - requeryFiles(scoreInfo.Files, contextFactory); - break; - - case BeatmapSetInfo beatmapSetInfo: - var context = contextFactory.Get(); - - foreach (var beatmap in beatmapSetInfo.Beatmaps) - { - // Workaround System.InvalidOperationException - // The instance of entity type 'RulesetInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. - beatmap.Ruleset = context.RulesetInfo.Find(beatmap.RulesetID); - } - - requeryFiles(beatmapSetInfo.Files, contextFactory); - break; - - default: - throw new ArgumentException($"{nameof(Requery)} does not have support for the provided model type", nameof(model)); - } - - void requeryFiles(List files, IDatabaseContextFactory databaseContextFactory) where T : class, INamedFileInfo - { - var dbContext = databaseContextFactory.Get(); - - foreach (var file in files) - { - Requery(file, dbContext); - } - } - } - - public static void Requery(this INamedFileInfo file, OsuDbContext dbContext) - { - // Workaround System.InvalidOperationException - // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. - file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID); - } - } -} diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index 2342ab07d4..2aae62edea 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -3,6 +3,7 @@ using System; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.Logging; using osu.Framework.Logging; using osu.Framework.Statistics; @@ -110,10 +111,10 @@ namespace osu.Game.Database { base.OnConfiguring(optionsBuilder); optionsBuilder - .UseSqlite(connectionString, - sqliteOptions => sqliteOptions - .CommandTimeout(10) - .UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery)) + // this is required for the time being due to the way we are querying in places like BeatmapStore. + // if we ever move to having consumers file their own .Includes, or get eager loading support, this could be re-enabled. + .ConfigureWarnings(warnings => warnings.Ignore(CoreEventId.IncludeIgnoredWarning)) + .UseSqlite(connectionString, sqliteOptions => sqliteOptions.CommandTimeout(10)) .UseLoggerFactory(logger.Value); } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 78101991f6..f5192f3a40 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -73,7 +73,7 @@ namespace osu.Game.Scoring } set { - modsJson = JsonConvert.SerializeObject(value.Select(m => new DeserializedMod { Acronym = m.Acronym })); + modsJson = null; mods = value; } } @@ -86,7 +86,16 @@ namespace osu.Game.Scoring [Column("Mods")] public string ModsJson { - get => modsJson; + get + { + if (modsJson != null) + return modsJson; + + if (mods == null) + return null; + + return modsJson = JsonConvert.SerializeObject(mods.Select(m => new DeserializedMod { Acronym = m.Acronym })); + } set { modsJson = value; diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 7d0abc5996..c7ee26c248 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -53,11 +53,6 @@ namespace osu.Game.Scoring this.configManager = configManager; } - protected override void PreImport(ScoreInfo model) - { - model.Requery(ContextFactory); - } - protected override ScoreInfo CreateModel(ArchiveReader archive) { if (archive == null) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 894a068b7f..9257636301 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -142,11 +142,6 @@ namespace osu.Game.Skinning } } - protected override void PreImport(SkinInfo model) - { - model.Requery(ContextFactory); - } - /// /// Retrieve a instance for the provided /// diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 360c522193..9731c1d5ea 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,10 +24,10 @@ - - + + - + diff --git a/osu.iOS.props b/osu.iOS.props index b763a91dfb..11677d345e 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -90,6 +90,8 @@ + + From c4f3714385c411dca8092f3163aad2817ff7f62b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 21 Mar 2021 18:39:52 +0100 Subject: [PATCH 142/202] Make hold note input tests fail due to head hiding --- .../TestSceneHoldNoteInput.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs index 596430f9e5..7ae69bf7d7 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs @@ -5,11 +5,13 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Screens; +using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Replays; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Objects; @@ -345,6 +347,14 @@ namespace osu.Game.Rulesets.Mania.Tests AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0); AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen()); + + AddUntilStep("wait for head", () => currentPlayer.GameplayClockContainer.GameplayClock.CurrentTime >= time_head); + AddAssert("head is visible", + () => currentPlayer.ChildrenOfType() + .Single(note => note.HitObject == beatmap.HitObjects[0]) + .Head + .Alpha == 1); + AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value); } @@ -352,6 +362,8 @@ namespace osu.Game.Rulesets.Mania.Tests { public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; + public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer; + protected override bool PauseOnFocusLost => false; public ScoreAccessibleReplayPlayer(Score score) From 9a330c4c56aeeda049cfccdd6a15f10c1966758c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 21 Mar 2021 18:34:31 +0100 Subject: [PATCH 143/202] Fix mania hold note heads hiding when frozen This was an insidious regression from a3dc1d5. Prior to that commit, `DrawableHoldNoteHead` had `UpdateStateTransforms()` overridden, to set the hold note head's lifetime. When that method was split into `UpdateInitialStateTransforms()` and `UpdateHitStateTransforms()`, the lifetime set was moved to the former. Unfortunately, that override served two purposes: both to set the lifetime, and to suppress hit animations which would normally be added by the base `DrawableManiaHitObject`. That fact being missed led to `UpdateHitStateTransforms()` hiding the hold note head immediately on hit and with a slight delay on miss. To resolve, explicitly override `UpdateHitStateTransforms()` and suppress the base call, with an explanatory comment. --- .../Objects/Drawables/DrawableHoldNoteHead.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs index 75dcf0e55e..35ba2465fa 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.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.Objects.Drawables; + namespace osu.Game.Rulesets.Mania.Objects.Drawables { /// @@ -25,6 +27,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables LifetimeEnd = LifetimeStart + 30000; } + protected override void UpdateHitStateTransforms(ArmedState state) + { + // suppress the base call explicitly. + // the hold note head should never change its visual state on its own due to the "freezing" mechanic + // (when hit, it remains visible in place at the judgement line; when dropped, it will scroll past the line). + // it will be hidden along with its parenting hold note when required. + } + public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note public override void OnReleased(ManiaAction action) From e31d583a7f468853844bb1e8aa38f2d70225f122 Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 21 Mar 2021 11:16:59 -0700 Subject: [PATCH 144/202] Add comments count to user profile overlay --- osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs | 6 ++++++ osu.Game/Users/User.cs | 3 +++ 2 files changed, 9 insertions(+) diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index 662f55317b..e73579fad0 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using Humanizer; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; @@ -115,6 +116,11 @@ namespace osu.Game.Overlays.Profile.Header topLinkContainer.AddText("Contributed "); topLinkContainer.AddLink($@"{user.PostCount:#,##0} forum posts", $"{api.WebsiteRootUrl}/users/{user.Id}/posts", creationParameters: embolden); + addSpacer(topLinkContainer); + + topLinkContainer.AddText("Posted "); + topLinkContainer.AddLink("comment".ToQuantity(user.CommentsCount, "#,##0"), $"{api.WebsiteRootUrl}/comments?user_id={user.Id}", creationParameters: embolden); + string websiteWithoutProtocol = user.Website; if (!string.IsNullOrEmpty(websiteWithoutProtocol)) diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index 6c45417db0..74ffb7c457 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -120,6 +120,9 @@ namespace osu.Game.Users [JsonProperty(@"post_count")] public int PostCount; + [JsonProperty(@"comments_count")] + public int CommentsCount; + [JsonProperty(@"follower_count")] public int FollowerCount; From 9bc6cdf042317265f7e37b3feaf58f10770d6fac Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 21 Mar 2021 11:19:07 -0700 Subject: [PATCH 145/202] Fix singular format regression on forum post text --- osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index e73579fad0..fe61e532e1 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -114,7 +114,7 @@ namespace osu.Game.Overlays.Profile.Header } topLinkContainer.AddText("Contributed "); - topLinkContainer.AddLink($@"{user.PostCount:#,##0} forum posts", $"{api.WebsiteRootUrl}/users/{user.Id}/posts", creationParameters: embolden); + topLinkContainer.AddLink("forum post".ToQuantity(user.PostCount, "#,##0"), $"{api.WebsiteRootUrl}/users/{user.Id}/posts", creationParameters: embolden); addSpacer(topLinkContainer); From f7bf23dbe9b51fa6f2a8168efdf4ce1b081e8874 Mon Sep 17 00:00:00 2001 From: owen-young Date: Sun, 21 Mar 2021 21:50:19 -0500 Subject: [PATCH 146/202] first attempt at changing windowMode to be fullscreen on default --- osu.Game/OsuGame.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index dd775888a1..2fd6331c86 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -126,6 +126,8 @@ namespace osu.Game private Bindable configSkin; + private Bindable windowMode; + private readonly string[] args; private readonly List overlays = new List(); @@ -631,6 +633,12 @@ namespace osu.Game loadComponentSingleFile(volume = new VolumeOverlay(), leftFloatingOverlayContent.Add, true); + frameworkConfig.GetBindable(FrameworkSetting.WindowMode); + windowMode.BindValueChanged(mode => ScheduleAfterChildren(() => + { + windowMode.Value = WindowMode.Windowed; + }), true); + var onScreenDisplay = new OnScreenDisplay(); onScreenDisplay.BeginTracking(this, frameworkConfig); From 073dba5330663073433b38798626ae0d6781d971 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Mar 2021 14:05:37 +0900 Subject: [PATCH 147/202] Remove local workarounds to attempt to avoid crashes on skin change --- osu.Game/Skinning/LegacySkin.cs | 76 +-------------------------------- 1 file changed, 1 insertion(+), 75 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index ec49d43c67..12abc4d867 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.IO; using System.Linq; using JetBrains.Annotations; -using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -462,7 +461,7 @@ namespace osu.Game.Skinning var sample = Samples?.Get(lookup); if (sample != null) - return new LegacySkinSample(sample, this); + return sample; } return null; @@ -505,78 +504,5 @@ namespace osu.Game.Skinning Textures?.Dispose(); Samples?.Dispose(); } - - /// - /// A sample wrapper which keeps a reference to the contained skin to avoid finalizer garbage collection of the managing SampleStore. - /// - private class LegacySkinSample : ISample, IDisposable - { - private readonly Sample sample; - - [UsedImplicitly] - private readonly LegacySkin skin; - - public LegacySkinSample(Sample sample, LegacySkin skin) - { - this.sample = sample; - this.skin = skin; - } - - public SampleChannel Play() - { - return sample.Play(); - } - - public SampleChannel GetChannel() - { - return sample.GetChannel(); - } - - public double Length => sample.Length; - - public Bindable PlaybackConcurrency => sample.PlaybackConcurrency; - public BindableNumber Volume => sample.Volume; - - public BindableNumber Balance => sample.Balance; - - public BindableNumber Frequency => sample.Frequency; - - public BindableNumber Tempo => sample.Tempo; - - public void BindAdjustments(IAggregateAudioAdjustment component) - { - sample.BindAdjustments(component); - } - - public void UnbindAdjustments(IAggregateAudioAdjustment component) - { - sample.UnbindAdjustments(component); - } - - public void AddAdjustment(AdjustableProperty type, IBindable adjustBindable) - { - sample.AddAdjustment(type, adjustBindable); - } - - public void RemoveAdjustment(AdjustableProperty type, IBindable adjustBindable) - { - sample.RemoveAdjustment(type, adjustBindable); - } - - public void RemoveAllAdjustments(AdjustableProperty type) - { - sample.RemoveAllAdjustments(type); - } - - public IBindable AggregateVolume => sample.AggregateVolume; - - public IBindable AggregateBalance => sample.AggregateBalance; - - public IBindable AggregateFrequency => sample.AggregateFrequency; - - public IBindable AggregateTempo => sample.AggregateTempo; - - public void Dispose() => sample.Dispose(); - } } } From db64fac8241710308f02399909ec7f6f05be6251 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 22 Mar 2021 15:26:22 +0900 Subject: [PATCH 148/202] Delay key fade in legacy mania skins --- osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs index 78ccb83a8c..174324f5f6 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs @@ -101,8 +101,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy { if (action == column.Action.Value) { - upSprite.FadeTo(1); - downSprite.FadeTo(0); + upSprite.Delay(80).FadeTo(1); + downSprite.Delay(80).FadeTo(0); } } } From fc632fd48aae56b1045a1955b035d8ac574609d8 Mon Sep 17 00:00:00 2001 From: Owen Young Date: Mon, 22 Mar 2021 01:30:20 -0500 Subject: [PATCH 149/202] Added WindowSetting setting to OsuSetting enum so that it can be set by default at startup. Modified LayoutSettings.cs so that when it is changed in the settings, it is written to the local settings as well. --- osu.Game/Configuration/OsuConfigManager.cs | 3 +++ osu.Game/OsuGame.cs | 9 +++------ .../Settings/Sections/Graphics/LayoutSettings.cs | 7 ++++++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index d0fa45bb7a..cd74fe25f4 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -91,6 +91,8 @@ namespace osu.Game.Configuration Set(OsuSetting.MenuParallax, true); + Set(OsuSetting.WindowSetting, WindowMode.Fullscreen); + // Gameplay Set(OsuSetting.DimLevel, 0.8, 0, 1, 0.01); Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01); @@ -233,6 +235,7 @@ namespace osu.Game.Configuration MenuVoice, CursorRotation, MenuParallax, + WindowSetting, BeatmapDetailTab, BeatmapDetailModsFilter, Username, diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2fd6331c86..84737a56e4 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -234,6 +234,9 @@ namespace osu.Game SelectedMods.BindValueChanged(modsChanged); Beatmap.BindValueChanged(beatmapChanged, true); + + windowMode = LocalConfig.GetBindable(OsuSetting.WindowSetting); + frameworkConfig.GetBindable(FrameworkSetting.WindowMode).Value = windowMode.Value; } private ExternalLinkOpener externalLinkOpener; @@ -633,12 +636,6 @@ namespace osu.Game loadComponentSingleFile(volume = new VolumeOverlay(), leftFloatingOverlayContent.Add, true); - frameworkConfig.GetBindable(FrameworkSetting.WindowMode); - windowMode.BindValueChanged(mode => ScheduleAfterChildren(() => - { - windowMode.Value = WindowMode.Windowed; - }), true); - var onScreenDisplay = new OnScreenDisplay(); onScreenDisplay.BeginTracking(this, frameworkConfig); diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 4d5c2e06eb..ab662cb9a0 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -31,6 +31,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private Bindable scalingMode; private Bindable sizeFullscreen; + private Bindable windowMode; private readonly BindableList resolutions = new BindableList(new[] { new Size(9999, 9999) }); @@ -56,6 +57,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics scalingSizeY = osuConfig.GetBindable(OsuSetting.ScalingSizeY); scalingPositionX = osuConfig.GetBindable(OsuSetting.ScalingPositionX); scalingPositionY = osuConfig.GetBindable(OsuSetting.ScalingPositionY); + windowMode = osuConfig.GetBindable(OsuSetting.WindowSetting); if (host.Window != null) { @@ -141,7 +143,10 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics scalingSettings.ForEach(s => bindPreviewEvent(s.Current)); - windowModeDropdown.Current.ValueChanged += _ => updateResolutionDropdown(); + windowModeDropdown.Current.ValueChanged += mode => { + windowMode.Value = mode.NewValue; + updateResolutionDropdown(); + }; windowModes.BindCollectionChanged((sender, args) => { From c4d08463addcf532926308dc319eebb981d530a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Mar 2021 16:04:51 +0900 Subject: [PATCH 150/202] Fix spinners playing looping sound too long in the editor The `OnComplete` event was never being run due to the transform playing out longer than the spinner's lifetime. I've matched the durations, but also moved the `Stop()` call to what I deem a safer place to run it (I did notice that without this it would still potentially never fire). Note that this is more noticeable in the editor because of lifetime extension. In gameplay, the returning of a spinner to the pool will clean things up (but in the editor that can take longer, depending on timeline zoom level). Another thing worth mentioning is that the fade doesn't actually work. This is due to https://github.com/ppy/osu-framework/pull/4212. Closes #12119. --- .../Objects/Drawables/DrawableSpinner.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 3d614c2dbd..d92f63eb89 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -39,6 +39,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private Bindable isSpinning; private bool spinnerFrequencyModulate; + private const double fade_out_duration = 160; + public DrawableSpinner() : this(null) { @@ -136,7 +138,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } else { - spinningSample?.VolumeTo(0, 300).OnComplete(_ => spinningSample.Stop()); + if (spinningSample != null) + spinningSample.Volume.Value = 0; + + spinningSample?.VolumeTo(0, fade_out_duration); } } @@ -173,7 +178,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.UpdateHitStateTransforms(state); - this.FadeOut(160).Expire(); + this.FadeOut(fade_out_duration).OnComplete(_ => + { + // looping sample should be stopped here as it is safer than running in the OnComplete + // of the volume transition above. + spinningSample.Stop(); + }); + + Expire(); // skin change does a rewind of transforms, which will stop the spinning sound from playing if it's currently in playback. isSpinning?.TriggerChange(); From 690fb9224aaee2d2d4930de181992eb53970b32d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Mar 2021 16:18:31 +0900 Subject: [PATCH 151/202] Combine constants for readability --- osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitExplosion.cs | 4 +++- osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitExplosion.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitExplosion.cs index 73aece1ed4..9ec122a12c 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitExplosion.cs @@ -17,6 +17,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy { public class LegacyHitExplosion : LegacyManiaColumnElement, IHitExplosion { + public const double FADE_IN_DURATION = 80; + private readonly IBindable direction = new Bindable(); private Drawable explosion; @@ -72,7 +74,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy (explosion as IFramedAnimation)?.GotoFrame(0); - explosion?.FadeInFromZero(80) + explosion?.FadeInFromZero(fade_in_duration) .Then().FadeOut(120); } } diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs index 174324f5f6..10319a7d4d 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs @@ -101,8 +101,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy { if (action == column.Action.Value) { - upSprite.Delay(80).FadeTo(1); - downSprite.Delay(80).FadeTo(0); + upSprite.Delay(LegacyHitExplosion.FADE_IN_DURATION).FadeTo(1); + downSprite.Delay(LegacyHitExplosion.FADE_IN_DURATION).FadeTo(0); } } } From 5b1d9f4cf07abd14860bc9ecf887fa98e55b4dc6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Mar 2021 16:19:29 +0900 Subject: [PATCH 152/202] Fix constant case --- osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitExplosion.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitExplosion.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitExplosion.cs index 9ec122a12c..e4d466dca5 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHitExplosion.cs @@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy (explosion as IFramedAnimation)?.GotoFrame(0); - explosion?.FadeInFromZero(fade_in_duration) + explosion?.FadeInFromZero(FADE_IN_DURATION) .Then().FadeOut(120); } } From e60ff45b73a2e236eed250f869a3b4afbafd3292 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 22 Mar 2021 16:57:40 +0900 Subject: [PATCH 153/202] Add another test for colinear perfect curves --- .../colinear-perfect-curve-expected-conversion.json | 13 +++++++++++++ .../Testing/Beatmaps/colinear-perfect-curve.osu | 4 +++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json index 96e4bf1637..1a0bd66246 100644 --- a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json +++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json @@ -1,5 +1,18 @@ { "Mappings": [{ + "StartTime": 114993, + "Objects": [{ + "StartTime": 114993, + "EndTime": 114993, + "X": 493, + "Y": 92 + }, { + "StartTime": 115290, + "EndTime": 115290, + "X": 451.659241, + "Y": 267.188 + }] + }, { "StartTime": 118858.0, "Objects": [{ "StartTime": 118858.0, diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve.osu b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve.osu index 8c3edc9571..dd35098502 100644 --- a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve.osu +++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve.osu @@ -9,7 +9,9 @@ SliderMultiplier:1.87 SliderTickRate:1 [TimingPoints] -49051,230.769230769231,4,2,1,15,1,0 +114000,346.820809248555,4,2,1,71,1,0 +118000,230.769230769231,4,2,1,15,1,0 [HitObjects] +493,92,114993,2,0,P|472:181|442:308,1,180,12|0,0:0|0:0,0:0:0:0: 219,215,118858,2,0,P|224:170|244:-10,1,187,8|2,0:0|0:0,0:0:0:0: From a65e491768441017e9cfe7e2e4fec2d15e57d708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 22 Mar 2021 20:00:36 +0100 Subject: [PATCH 154/202] Remove osuTK desktop rider run config No longer operational since 6eadae8. --- .../runConfigurations/osu___legacy_osuTK_.xml | 20 ------------------- 1 file changed, 20 deletions(-) delete mode 100644 .idea/.idea.osu.Desktop/.idea/runConfigurations/osu___legacy_osuTK_.xml diff --git a/.idea/.idea.osu.Desktop/.idea/runConfigurations/osu___legacy_osuTK_.xml b/.idea/.idea.osu.Desktop/.idea/runConfigurations/osu___legacy_osuTK_.xml deleted file mode 100644 index 9ece926b34..0000000000 --- a/.idea/.idea.osu.Desktop/.idea/runConfigurations/osu___legacy_osuTK_.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - \ No newline at end of file From d85929d721254391d5e7e23d2e81d56180ee07f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 22 Mar 2021 22:45:18 +0100 Subject: [PATCH 155/202] Adjust autoplay generation tests to match expected behaviour --- .../TestSceneAutoGeneration.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs index a5248c7712..399a46aa77 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneAutoGeneration.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Mania.Tests Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames"); Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time"); - Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time"); + Assert.AreEqual(3000, generated.Frames[frame_offset + 1].Time, "Incorrect release time"); Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Special1), "Special1 has not been pressed"); Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Special1), "Special1 has not been released"); } @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Mania.Tests Assert.IsTrue(generated.Frames.Count == frame_offset + 2, "Replay must have 3 frames"); Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time"); - Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time"); + Assert.AreEqual(3000, generated.Frames[frame_offset + 1].Time, "Incorrect release time"); Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed"); Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released"); @@ -148,9 +148,9 @@ namespace osu.Game.Rulesets.Mania.Tests Assert.IsTrue(generated.Frames.Count == frame_offset + 4, "Replay must have 4 generated frames"); Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect first note hit time"); - Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 2].Time, "Incorrect first note release time"); + Assert.AreEqual(3000, generated.Frames[frame_offset + 2].Time, "Incorrect first note release time"); Assert.AreEqual(2000, generated.Frames[frame_offset + 1].Time, "Incorrect second note hit time"); - Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 3].Time, "Incorrect second note release time"); + Assert.AreEqual(4000, generated.Frames[frame_offset + 3].Time, "Incorrect second note release time"); Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed"); Assert.IsTrue(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed"); Assert.IsFalse(checkContains(generated.Frames[frame_offset + 2], ManiaAction.Key1), "Key1 has not been released"); @@ -168,7 +168,7 @@ namespace osu.Game.Rulesets.Mania.Tests // | | | var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); - beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 - ManiaAutoGenerator.RELEASE_DELAY }); + beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); beatmap.HitObjects.Add(new Note { StartTime = 3000, Column = 1 }); var generated = new ManiaAutoGenerator(beatmap).Generate(); From 29d4162e4e2110973571a8fcfe83509e3586d50a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 22 Mar 2021 22:38:51 +0100 Subject: [PATCH 156/202] Remove release delay for hold notes when generating autoplay It was more intended for normal notes anyway (as they would be released pretty much instantaneously, if it weren't for the delay). --- .../Replays/ManiaAutoGenerator.cs | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index 3ebbe5af8e..7c51d58b74 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Replays; using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Replays; @@ -85,20 +86,28 @@ namespace osu.Game.Rulesets.Mania.Replays { var currentObject = Beatmap.HitObjects[i]; var nextObjectInColumn = GetNextObject(i); // Get the next object that requires pressing the same button - - double endTime = currentObject.GetEndTime(); - - bool canDelayKeyUp = nextObjectInColumn == null || - nextObjectInColumn.StartTime > endTime + RELEASE_DELAY; - - double calculatedDelay = canDelayKeyUp ? RELEASE_DELAY : (nextObjectInColumn.StartTime - endTime) * 0.9; + var releaseTime = calculateReleaseTime(currentObject, nextObjectInColumn); yield return new HitPoint { Time = currentObject.StartTime, Column = currentObject.Column }; - yield return new ReleasePoint { Time = endTime + calculatedDelay, Column = currentObject.Column }; + yield return new ReleasePoint { Time = releaseTime, Column = currentObject.Column }; } } + private double calculateReleaseTime(HitObject currentObject, HitObject nextObject) + { + double endTime = currentObject.GetEndTime(); + + if (currentObject is HoldNote) + // hold note releases must be timed exactly. + return endTime; + + bool canDelayKeyUpFully = nextObject == null || + nextObject.StartTime > endTime + RELEASE_DELAY; + + return endTime + (canDelayKeyUpFully ? RELEASE_DELAY : (nextObject.StartTime - endTime) * 0.9); + } + protected override HitObject GetNextObject(int currentIndex) { int desiredColumn = Beatmap.HitObjects[currentIndex].Column; From 8ea7271d5c96b062bebcb8b702da739d9ef4dac1 Mon Sep 17 00:00:00 2001 From: Owen Young Date: Mon, 22 Mar 2021 19:48:52 -0500 Subject: [PATCH 157/202] moved windowmode code to LoadComplete (?) --- osu.Game/OsuGame.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 84737a56e4..ff215b63e5 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -234,9 +234,6 @@ namespace osu.Game SelectedMods.BindValueChanged(modsChanged); Beatmap.BindValueChanged(beatmapChanged, true); - - windowMode = LocalConfig.GetBindable(OsuSetting.WindowSetting); - frameworkConfig.GetBindable(FrameworkSetting.WindowMode).Value = windowMode.Value; } private ExternalLinkOpener externalLinkOpener; @@ -576,6 +573,9 @@ namespace osu.Game dependencies.CacheAs(idleTracker = new GameIdleTracker(6000)); + windowMode = LocalConfig.GetBindable(OsuSetting.WindowSetting); + frameworkConfig.GetBindable(FrameworkSetting.WindowMode).Value = windowMode.Value; + AddRange(new Drawable[] { new VolumeControlReceptor From bdcb9451f79798197825106a0762a0c823b771c7 Mon Sep 17 00:00:00 2001 From: Owen Young Date: Mon, 22 Mar 2021 20:17:05 -0500 Subject: [PATCH 158/202] added code to OsuGameBase to default to fullscreen, but that might not be a good place to put it. --- osu.Game/OsuGameBase.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index e1c7b67a8c..bcd384604f 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -21,6 +21,7 @@ using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Cursor; using osu.Game.Online.API; +using osu.Framework.Configuration; using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Textures; using osu.Framework.Input; @@ -119,6 +120,7 @@ namespace osu.Game protected Bindable Beatmap { get; private set; } // cached via load() method private Bindable fpsDisplayVisible; + private Bindable windowMode; public virtual Version AssemblyVersion => Assembly.GetEntryAssembly()?.GetName().Version ?? new Version(); @@ -361,6 +363,9 @@ namespace osu.Game fpsDisplayVisible.ValueChanged += visible => { FrameStatistics.Value = visible.NewValue ? FrameStatisticsMode.Minimal : FrameStatisticsMode.None; }; fpsDisplayVisible.TriggerChange(); + windowMode = LocalConfig.GetBindable(OsuSetting.WindowSetting); + windowMode.Value = WindowMode.Fullscreen; + FrameStatistics.ValueChanged += e => fpsDisplayVisible.Value = e.NewValue != FrameStatisticsMode.None; } From 098005393e999f5623769adc9e5458bb86e37513 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Mar 2021 10:38:37 +0900 Subject: [PATCH 159/202] Remove unnecessary null checks and debug code --- .../Objects/Drawables/DrawableSpinner.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index d92f63eb89..32a0a14dc0 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -133,15 +133,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (tracking.NewValue) { if (!spinningSample.IsPlaying) - spinningSample?.Play(); - spinningSample?.VolumeTo(1, 300); + spinningSample.Play(); + + spinningSample.VolumeTo(1, 300); } else { - if (spinningSample != null) - spinningSample.Volume.Value = 0; - - spinningSample?.VolumeTo(0, fade_out_duration); + spinningSample.VolumeTo(0, fade_out_duration); } } From 16b3f22caf2754d97aacbf40f0d429e8b3cecdce Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 22 Mar 2021 19:32:17 -0700 Subject: [PATCH 160/202] Fix incorrect trash icon being used on deleted comments counter --- osu.Game/Overlays/Comments/DeletedCommentsCounter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Comments/DeletedCommentsCounter.cs b/osu.Game/Overlays/Comments/DeletedCommentsCounter.cs index 56588ef0a8..8c40d79f7a 100644 --- a/osu.Game/Overlays/Comments/DeletedCommentsCounter.cs +++ b/osu.Game/Overlays/Comments/DeletedCommentsCounter.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Comments { new SpriteIcon { - Icon = FontAwesome.Solid.Trash, + Icon = FontAwesome.Regular.TrashAlt, Size = new Vector2(14), }, countText = new OsuSpriteText From 9f788f58548d629144c4b8946b74542b424ea7a4 Mon Sep 17 00:00:00 2001 From: Owen Young Date: Mon, 22 Mar 2021 22:52:16 -0500 Subject: [PATCH 161/202] removed code from OsuGameBase for fullscreen.....OsuSetting still exists but cannot figure out a way to set it to a default and have it actually work --- osu.Game/OsuGame.cs | 6 +++--- osu.Game/OsuGameBase.cs | 4 ---- .../Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 6 ++++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index ff215b63e5..2f2428e781 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -126,7 +126,7 @@ namespace osu.Game private Bindable configSkin; - private Bindable windowMode; + // private Bindable windowMode; private readonly string[] args; @@ -573,8 +573,8 @@ namespace osu.Game dependencies.CacheAs(idleTracker = new GameIdleTracker(6000)); - windowMode = LocalConfig.GetBindable(OsuSetting.WindowSetting); - frameworkConfig.GetBindable(FrameworkSetting.WindowMode).Value = windowMode.Value; + // windowMode = LocalConfig.GetBindable(OsuSetting.WindowSetting); + // frameworkConfig.GetBindable(FrameworkSetting.WindowMode).Value = windowMode.Value; AddRange(new Drawable[] { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index bcd384604f..8b1fe20708 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -120,7 +120,6 @@ namespace osu.Game protected Bindable Beatmap { get; private set; } // cached via load() method private Bindable fpsDisplayVisible; - private Bindable windowMode; public virtual Version AssemblyVersion => Assembly.GetEntryAssembly()?.GetName().Version ?? new Version(); @@ -363,9 +362,6 @@ namespace osu.Game fpsDisplayVisible.ValueChanged += visible => { FrameStatistics.Value = visible.NewValue ? FrameStatisticsMode.Minimal : FrameStatisticsMode.None; }; fpsDisplayVisible.TriggerChange(); - windowMode = LocalConfig.GetBindable(OsuSetting.WindowSetting); - windowMode.Value = WindowMode.Fullscreen; - FrameStatistics.ValueChanged += e => fpsDisplayVisible.Value = e.NewValue != FrameStatisticsMode.None; } diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index ab662cb9a0..a0cb8fc2de 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; +using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Configuration; using osu.Game.Graphics.Containers; @@ -57,7 +58,8 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics scalingSizeY = osuConfig.GetBindable(OsuSetting.ScalingSizeY); scalingPositionX = osuConfig.GetBindable(OsuSetting.ScalingPositionX); scalingPositionY = osuConfig.GetBindable(OsuSetting.ScalingPositionY); - windowMode = osuConfig.GetBindable(OsuSetting.WindowSetting); + windowMode = config.GetBindable(FrameworkSetting.WindowMode); + Logger.Log($"windowMode {windowMode.Value}"); if (host.Window != null) { @@ -71,7 +73,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { LabelText = "Screen mode", ItemSource = windowModes, - Current = config.GetBindable(FrameworkSetting.WindowMode), + Current = config.GetBindable(FrameworkSetting.WindowMode) }, resolutionDropdown = new ResolutionSettingsDropdown { From d9e2c44a34c17d9ed0f93587460fd506480ad478 Mon Sep 17 00:00:00 2001 From: Owen Young Date: Mon, 22 Mar 2021 23:36:55 -0500 Subject: [PATCH 162/202] implemented GetFrameworkConfigDefaults for overriding framework default, removed previous code that added a new OsuSetting and modified settings layout. --- osu.Game/Configuration/OsuConfigManager.cs | 3 --- osu.Game/OsuGame.cs | 11 ++++++----- osu.Game/OsuGameBase.cs | 1 - .../Settings/Sections/Graphics/LayoutSettings.cs | 11 ++--------- 4 files changed, 8 insertions(+), 18 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index cd74fe25f4..d0fa45bb7a 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -91,8 +91,6 @@ namespace osu.Game.Configuration Set(OsuSetting.MenuParallax, true); - Set(OsuSetting.WindowSetting, WindowMode.Fullscreen); - // Gameplay Set(OsuSetting.DimLevel, 0.8, 0, 1, 0.01); Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01); @@ -235,7 +233,6 @@ namespace osu.Game.Configuration MenuVoice, CursorRotation, MenuParallax, - WindowSetting, BeatmapDetailTab, BeatmapDetailModsFilter, Username, diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2f2428e781..ffb694c27e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -126,8 +126,6 @@ namespace osu.Game private Bindable configSkin; - // private Bindable windowMode; - private readonly string[] args; private readonly List overlays = new List(); @@ -573,9 +571,6 @@ namespace osu.Game dependencies.CacheAs(idleTracker = new GameIdleTracker(6000)); - // windowMode = LocalConfig.GetBindable(OsuSetting.WindowSetting); - // frameworkConfig.GetBindable(FrameworkSetting.WindowMode).Value = windowMode.Value; - AddRange(new Drawable[] { new VolumeControlReceptor @@ -1012,5 +1007,11 @@ namespace osu.Game if (newScreen == null) Exit(); } + + protected override IDictionary GetFrameworkConfigDefaults() { + IDictionary defaultOverrides = new Dictionary(); + defaultOverrides.Add(FrameworkSetting.WindowMode, WindowMode.Fullscreen); + return defaultOverrides; + } } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 8b1fe20708..e1c7b67a8c 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -21,7 +21,6 @@ using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Cursor; using osu.Game.Online.API; -using osu.Framework.Configuration; using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Textures; using osu.Framework.Input; diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index a0cb8fc2de..4d5c2e06eb 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -12,7 +12,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; -using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Configuration; using osu.Game.Graphics.Containers; @@ -32,7 +31,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private Bindable scalingMode; private Bindable sizeFullscreen; - private Bindable windowMode; private readonly BindableList resolutions = new BindableList(new[] { new Size(9999, 9999) }); @@ -58,8 +56,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics scalingSizeY = osuConfig.GetBindable(OsuSetting.ScalingSizeY); scalingPositionX = osuConfig.GetBindable(OsuSetting.ScalingPositionX); scalingPositionY = osuConfig.GetBindable(OsuSetting.ScalingPositionY); - windowMode = config.GetBindable(FrameworkSetting.WindowMode); - Logger.Log($"windowMode {windowMode.Value}"); if (host.Window != null) { @@ -73,7 +69,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { LabelText = "Screen mode", ItemSource = windowModes, - Current = config.GetBindable(FrameworkSetting.WindowMode) + Current = config.GetBindable(FrameworkSetting.WindowMode), }, resolutionDropdown = new ResolutionSettingsDropdown { @@ -145,10 +141,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics scalingSettings.ForEach(s => bindPreviewEvent(s.Current)); - windowModeDropdown.Current.ValueChanged += mode => { - windowMode.Value = mode.NewValue; - updateResolutionDropdown(); - }; + windowModeDropdown.Current.ValueChanged += _ => updateResolutionDropdown(); windowModes.BindCollectionChanged((sender, args) => { From 58c60100b431e6ce6ee720ff72206e9f9071d070 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Mar 2021 14:04:47 +0900 Subject: [PATCH 163/202] Fix APIScoreToken's data type not matching server side --- osu.Game/Online/Rooms/APIScoreToken.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/APIScoreToken.cs b/osu.Game/Online/Rooms/APIScoreToken.cs index f652c1720d..6b559876de 100644 --- a/osu.Game/Online/Rooms/APIScoreToken.cs +++ b/osu.Game/Online/Rooms/APIScoreToken.cs @@ -8,6 +8,6 @@ namespace osu.Game.Online.Rooms public class APIScoreToken { [JsonProperty("id")] - public int ID { get; set; } + public long ID { get; set; } } } From 9c690f9545959c117b4358e391333b3882abb34c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Mar 2021 14:08:00 +0900 Subject: [PATCH 164/202] Fix second usage --- osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs index a75e4bdc07..2b6dbd9dcb 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists protected readonly PlaylistItem PlaylistItem; - protected int? Token { get; private set; } + protected long? Token { get; private set; } [Resolved] private IAPIProvider api { get; set; } From 254b0f5dc3b2469fdccf6baf0231af30baf11c9b Mon Sep 17 00:00:00 2001 From: Owen Young Date: Tue, 23 Mar 2021 00:24:33 -0500 Subject: [PATCH 165/202] removed line (?) - tried doing testing to see if it launched in fullscreen (i.e., overriding the method ppy mentioned), but to no avail :( --- osu.Game/OsuGame.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index ffb694c27e..a52899433a 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1007,7 +1007,6 @@ namespace osu.Game if (newScreen == null) Exit(); } - protected override IDictionary GetFrameworkConfigDefaults() { IDictionary defaultOverrides = new Dictionary(); defaultOverrides.Add(FrameworkSetting.WindowMode, WindowMode.Fullscreen); From 1171214541361b72ae23405a3549fd918af16fed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Mar 2021 14:51:22 +0900 Subject: [PATCH 166/202] 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 e0392bd687..75ac298626 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9731c1d5ea..b90c938a8b 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,7 +29,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 11677d345e..ce182a3054 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 08fcdc8ee46748eee4c8d66d5842b57f16d9d896 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Mar 2021 15:38:00 +0900 Subject: [PATCH 167/202] Update difficulty calculator tests with floating point differences --- .../OsuDifficultyCalculatorTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index a365ea10d4..c2119585ab 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - [TestCase(6.9311451172608853d, "diffcalc-test")] - [TestCase(1.0736587013228804d, "zero-length-sliders")] + [TestCase(6.9311451172574934d, "diffcalc-test")] + [TestCase(1.0736586907780401d, "zero-length-sliders")] public void Test(double expected, string name) => base.Test(expected, name); - [TestCase(8.6228371119393064d, "diffcalc-test")] - [TestCase(1.2864585434597433d, "zero-length-sliders")] + [TestCase(8.6228371119271454d, "diffcalc-test")] + [TestCase(1.2864585280364178d, "zero-length-sliders")] public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new OsuModDoubleTime()); From f5ba746ae5522e6c1ba34f6a34218491ff4aa626 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Mar 2021 17:31:28 +0900 Subject: [PATCH 168/202] Fail all API requests sent to DummyAPIAccess Until now, API requests sent to dummy API were just lost in the void. In most cases this somehow worked as expected, but any logic which is waiting on a request to finish will potentially never get a response. Going forward, I'm not 100% sure that every `Wait` on a web response will have local timeout logic (I think there is a certain amount of assumption that this is being managed for us by `APIAccess`), so I've made this change to better handle such cases going forward. Now, rather than nothing happening, requests will trigger a failure via the existing exception logic rather than silently pretending the request never arrived. --- osu.Game/Online/API/APIRequest.cs | 8 ++++++-- osu.Game/Online/API/DummyAPIAccess.cs | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index a7174324d8..16d1b3ab17 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -181,9 +181,13 @@ namespace osu.Game.Online.API /// Whether we are in a failed or cancelled state. private bool checkAndScheduleFailure() { - if (API == null || pendingFailure == null) return cancelled; + if (pendingFailure == null) return cancelled; + + if (API == null) + pendingFailure(); + else + API.Schedule(pendingFailure); - API.Schedule(pendingFailure); pendingFailure = null; return true; } diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index 943b52db88..3cb22381c1 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -55,7 +55,12 @@ namespace osu.Game.Online.API public virtual void Queue(APIRequest request) { - HandleRequest?.Invoke(request); + if (HandleRequest != null) + HandleRequest.Invoke(request); + else + // this will fail due to not receiving an APIAccess, and trigger a failure on the request. + // this is intended - any request in testing that needs non-failures should use HandleRequest. + request.Perform(this); } public void Perform(APIRequest request) => HandleRequest?.Invoke(request); From ce452565f45bba3106ffe89a21c63d4662e8a40e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Mar 2021 17:50:31 +0900 Subject: [PATCH 169/202] Avoid firing any kind of failures after success --- osu.Game/Online/API/APIRequest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 16d1b3ab17..1a6868cfa4 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -131,8 +131,11 @@ namespace osu.Game.Online.API { } + private bool succeeded; + internal virtual void TriggerSuccess() { + succeeded = true; Success?.Invoke(); } @@ -145,10 +148,7 @@ namespace osu.Game.Online.API public void Fail(Exception e) { - if (WebRequest?.Completed == true) - return; - - if (cancelled) + if (succeeded || cancelled) return; cancelled = true; From aeff9bd8531962e7d509623407a819f3c7f69ae8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Mar 2021 18:08:32 +0900 Subject: [PATCH 170/202] Add return bool to HandleRequest to better trigger failures --- .../Online/TestDummyAPIRequestHandling.cs | 8 ++++++-- .../TestSceneSeasonalBackgroundLoader.cs | 4 +++- .../Online/TestSceneBeatmapListingOverlay.cs | 13 ++++++------ .../Online/TestSceneChangelogOverlay.cs | 6 ++++-- .../Visual/Online/TestSceneChatOverlay.cs | 20 +++++++++++++++++++ .../Online/TestSceneCommentsContainer.cs | 3 ++- .../Visual/Online/TestSceneNewsOverlay.cs | 3 ++- .../TestScenePlaylistsResultsScreen.cs | 13 ++++++++++++ .../TestSceneBeatmapRecommendations.cs | 4 +++- osu.Game/Online/API/DummyAPIAccess.cs | 9 +++++---- .../Multiplayer/TestMultiplayerRoomManager.cs | 18 +++++++++-------- 11 files changed, 75 insertions(+), 26 deletions(-) diff --git a/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs b/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs index 42948c3731..aa29d76843 100644 --- a/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs +++ b/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs @@ -23,8 +23,10 @@ namespace osu.Game.Tests.Online { case CommentVoteRequest cRequest: cRequest.TriggerSuccess(new CommentBundle()); - break; + return true; } + + return false; }); CommentVoteRequest request = null; @@ -108,8 +110,10 @@ namespace osu.Game.Tests.Online { case LeaveChannelRequest cRequest: cRequest.TriggerSuccess(); - break; + return true; } + + return false; }); } } diff --git a/osu.Game.Tests/Visual/Background/TestSceneSeasonalBackgroundLoader.cs b/osu.Game.Tests/Visual/Background/TestSceneSeasonalBackgroundLoader.cs index e7cf830db0..dc5a4f4a3e 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneSeasonalBackgroundLoader.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneSeasonalBackgroundLoader.cs @@ -135,13 +135,15 @@ namespace osu.Game.Tests.Visual.Background dummyAPI.HandleRequest = request => { if (dummyAPI.State.Value != APIState.Online || !(request is GetSeasonalBackgroundsRequest backgroundsRequest)) - return; + return false; backgroundsRequest.TriggerSuccess(new APISeasonalBackgrounds { Backgrounds = seasonal_background_urls.Select(url => new APISeasonalBackground { Url = url }).ToList(), EndDate = endDate }); + + return true; }; }); diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index 1349264bf9..156d6b744e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -30,13 +30,14 @@ namespace osu.Game.Tests.Visual.Online ((DummyAPIAccess)API).HandleRequest = req => { - if (req is SearchBeatmapSetsRequest searchBeatmapSetsRequest) + if (!(req is SearchBeatmapSetsRequest searchBeatmapSetsRequest)) return false; + + searchBeatmapSetsRequest.TriggerSuccess(new SearchBeatmapSetsResponse { - searchBeatmapSetsRequest.TriggerSuccess(new SearchBeatmapSetsResponse - { - BeatmapSets = setsForResponse, - }); - } + BeatmapSets = setsForResponse, + }); + + return true; }; } diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index cd2c4e9346..8818ac75b1 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -63,13 +63,15 @@ namespace osu.Game.Tests.Visual.Online Builds = builds.Values.ToList() }; changelogRequest.TriggerSuccess(changelogResponse); - break; + return true; case GetChangelogBuildRequest buildRequest: if (requestedBuild != null) buildRequest.TriggerSuccess(requestedBuild); - break; + return true; } + + return false; }; Child = changelog = new TestChangelogOverlay(); diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index fca642ad6c..b13dd34ebc 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -11,6 +11,8 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; using osu.Game.Online.Chat; using osu.Game.Overlays; using osu.Game.Overlays.Chat.Selection; @@ -64,6 +66,24 @@ namespace osu.Game.Tests.Visual.Online }); } + [SetUpSteps] + public void SetUpSteps() + { + AddStep("register request handling", () => + { + ((DummyAPIAccess)API).HandleRequest = req => + { + switch (req) + { + case JoinChannelRequest _: + return true; + } + + return false; + }; + }); + } + [Test] public void TestHideOverlay() { diff --git a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs index c2a18330c9..cd22bb2513 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs @@ -85,9 +85,10 @@ namespace osu.Game.Tests.Visual.Online dummyAPI.HandleRequest = request => { if (!(request is GetCommentsRequest getCommentsRequest)) - return; + return false; getCommentsRequest.TriggerSuccess(commentBundle); + return true; }; }); diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs index 37d51c16d2..6ebe8fcc07 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs @@ -33,9 +33,10 @@ namespace osu.Game.Tests.Visual.Online dummyAPI.HandleRequest = request => { if (!(request is GetNewsRequest getNewsRequest)) - return; + return false; getNewsRequest.TriggerSuccess(r); + return true; }; }); diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index be8032cde8..61d49e4018 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -170,6 +170,17 @@ namespace osu.Game.Tests.Visual.Playlists private void bindHandler(bool delayed = false, ScoreInfo userScore = null, bool failRequests = false) => ((DummyAPIAccess)API).HandleRequest = request => { + // pre-check for requests we should be handling (as they are scheduled below). + switch (request) + { + case ShowPlaylistUserScoreRequest _: + case IndexPlaylistScoresRequest _: + break; + + default: + return false; + } + requestComplete = false; double delay = delayed ? 3000 : 0; @@ -196,6 +207,8 @@ namespace osu.Game.Tests.Visual.Playlists break; } }, delay); + + return true; }; private void triggerSuccess(APIRequest req, T result) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index 53a956c77c..5e2d5eba5d 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -32,8 +32,10 @@ namespace osu.Game.Tests.Visual.SongSelect { case GetUserRequest userRequest: userRequest.TriggerSuccess(getUser(userRequest.Ruleset.ID)); - break; + return true; } + + return false; }; }); diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index 3cb22381c1..52f2365165 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -34,8 +34,9 @@ namespace osu.Game.Online.API /// /// Provide handling logic for an arbitrary API request. + /// Should return true is a request was handled. If null or false return, the request will be failed with a . /// - public Action HandleRequest; + public Func HandleRequest; private readonly Bindable state = new Bindable(APIState.Online); @@ -55,12 +56,12 @@ namespace osu.Game.Online.API public virtual void Queue(APIRequest request) { - if (HandleRequest != null) - HandleRequest.Invoke(request); - else + if (HandleRequest?.Invoke(request) != true) + { // this will fail due to not receiving an APIAccess, and trigger a failure on the request. // this is intended - any request in testing that needs non-failures should use HandleRequest. request.Perform(this); + } } public void Perform(APIRequest request) => HandleRequest?.Invoke(request); diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs index 7e824c4d7c..315be510a3 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs @@ -52,15 +52,15 @@ namespace osu.Game.Tests.Visual.Multiplayer Rooms.Add(createdRoom); createRoomRequest.TriggerSuccess(createdRoom); - break; + return true; case JoinRoomRequest joinRoomRequest: joinRoomRequest.TriggerSuccess(); - break; + return true; case PartRoomRequest partRoomRequest: partRoomRequest.TriggerSuccess(); - break; + return true; case GetRoomsRequest getRoomsRequest: var roomsWithoutParticipants = new List(); @@ -76,11 +76,11 @@ namespace osu.Game.Tests.Visual.Multiplayer } getRoomsRequest.TriggerSuccess(roomsWithoutParticipants); - break; + return true; case GetRoomRequest getRoomRequest: getRoomRequest.TriggerSuccess(Rooms.Single(r => r.RoomID.Value == getRoomRequest.RoomId)); - break; + return true; case GetBeatmapSetRequest getBeatmapSetRequest: var onlineReq = new GetBeatmapSetRequest(getBeatmapSetRequest.ID, getBeatmapSetRequest.Type); @@ -89,11 +89,11 @@ namespace osu.Game.Tests.Visual.Multiplayer // Get the online API from the game's dependencies. game.Dependencies.Get().Queue(onlineReq); - break; + return true; case CreateRoomScoreRequest createRoomScoreRequest: createRoomScoreRequest.TriggerSuccess(new APIScoreToken { ID = 1 }); - break; + return true; case SubmitRoomScoreRequest submitRoomScoreRequest: submitRoomScoreRequest.TriggerSuccess(new MultiplayerScore @@ -108,8 +108,10 @@ namespace osu.Game.Tests.Visual.Multiplayer User = api.LocalUser.Value, Statistics = new Dictionary() }); - break; + return true; } + + return false; }; } From 5267fb74c441c1c7f6ba48637f6981876ae5227e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Mar 2021 14:44:06 +0900 Subject: [PATCH 171/202] Add submission requests --- .../Online/Solo/CreateSoloScoreRequest.cs | 32 +++++++++++++ .../Online/Solo/SubmitSoloScoreRequest.cs | 45 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 osu.Game/Online/Solo/CreateSoloScoreRequest.cs create mode 100644 osu.Game/Online/Solo/SubmitSoloScoreRequest.cs diff --git a/osu.Game/Online/Solo/CreateSoloScoreRequest.cs b/osu.Game/Online/Solo/CreateSoloScoreRequest.cs new file mode 100644 index 0000000000..ae5ac5e26c --- /dev/null +++ b/osu.Game/Online/Solo/CreateSoloScoreRequest.cs @@ -0,0 +1,32 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Net.Http; +using osu.Framework.IO.Network; +using osu.Game.Online.API; +using osu.Game.Online.Rooms; + +namespace osu.Game.Online.Solo +{ + public class CreateSoloScoreRequest : APIRequest + { + private readonly int beatmapId; + private readonly string versionHash; + + public CreateSoloScoreRequest(int beatmapId, string versionHash) + { + this.beatmapId = beatmapId; + this.versionHash = versionHash; + } + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + req.Method = HttpMethod.Post; + req.AddParameter("version_hash", versionHash); + return req; + } + + protected override string Target => $@"solo/{beatmapId}/scores"; + } +} diff --git a/osu.Game/Online/Solo/SubmitSoloScoreRequest.cs b/osu.Game/Online/Solo/SubmitSoloScoreRequest.cs new file mode 100644 index 0000000000..98ba4fa052 --- /dev/null +++ b/osu.Game/Online/Solo/SubmitSoloScoreRequest.cs @@ -0,0 +1,45 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Net.Http; +using Newtonsoft.Json; +using osu.Framework.IO.Network; +using osu.Game.Online.API; +using osu.Game.Online.Rooms; +using osu.Game.Scoring; + +namespace osu.Game.Online.Solo +{ + public class SubmitSoloScoreRequest : APIRequest + { + private readonly long scoreId; + + private readonly int beatmapId; + + private readonly ScoreInfo scoreInfo; + + public SubmitSoloScoreRequest(int beatmapId, long scoreId, ScoreInfo scoreInfo) + { + this.beatmapId = beatmapId; + this.scoreId = scoreId; + this.scoreInfo = scoreInfo; + } + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + + req.ContentType = "application/json"; + req.Method = HttpMethod.Put; + + req.AddRaw(JsonConvert.SerializeObject(scoreInfo, new JsonSerializerSettings + { + ReferenceLoopHandling = ReferenceLoopHandling.Ignore + })); + + return req; + } + + protected override string Target => $@"solo/{beatmapId}/scores/{scoreId}"; + } +} From 6cb14e91c91043e1e3ddf0d2c999b9665def1d65 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Mar 2021 14:47:15 +0900 Subject: [PATCH 172/202] Make Player abstract and introduce SoloPlayer --- .../Visual/Navigation/TestScenePerformFromScreen.cs | 4 ++-- osu.Game/Screens/Play/Player.cs | 4 ++-- osu.Game/Screens/Play/SoloPlayer.cs | 9 +++++++++ osu.Game/Screens/Select/PlaySongSelect.cs | 2 +- 4 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 osu.Game/Screens/Play/SoloPlayer.cs diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index 21d3bdaae3..2791952b66 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.Navigation public void TestPerformAtSongSelectFromPlayerLoader() { PushAndConfirm(() => new PlaySongSelect()); - PushAndConfirm(() => new PlayerLoader(() => new Player())); + PushAndConfirm(() => new PlayerLoader(() => new SoloPlayer())); AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true, new[] { typeof(PlaySongSelect) })); AddUntilStep("returned to song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect); @@ -69,7 +69,7 @@ namespace osu.Game.Tests.Visual.Navigation public void TestPerformAtMenuFromPlayerLoader() { PushAndConfirm(() => new PlaySongSelect()); - PushAndConfirm(() => new PlayerLoader(() => new Player())); + PushAndConfirm(() => new PlayerLoader(() => new SoloPlayer())); AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true)); AddUntilStep("returned to song select", () => Game.ScreenStack.CurrentScreen is MainMenu); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 0e221351aa..4cf0274614 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Play { [Cached] [Cached(typeof(ISamplePlaybackDisabler))] - public class Player : ScreenWithBeatmapBackground, ISamplePlaybackDisabler + public abstract class Player : ScreenWithBeatmapBackground, ISamplePlaybackDisabler { /// /// The delay upon completion of the beatmap before displaying the results screen. @@ -135,7 +135,7 @@ namespace osu.Game.Screens.Play /// /// Create a new player instance. /// - public Player(PlayerConfiguration configuration = null) + protected Player(PlayerConfiguration configuration = null) { Configuration = configuration ?? new PlayerConfiguration(); } diff --git a/osu.Game/Screens/Play/SoloPlayer.cs b/osu.Game/Screens/Play/SoloPlayer.cs new file mode 100644 index 0000000000..79860f3eda --- /dev/null +++ b/osu.Game/Screens/Play/SoloPlayer.cs @@ -0,0 +1,9 @@ +// 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.Screens.Play +{ + public class SoloPlayer : Player + { + } +} diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index e61d5cce85..dfb4b59060 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -106,7 +106,7 @@ namespace osu.Game.Screens.Select SampleConfirm?.Play(); - this.Push(player = new PlayerLoader(() => new Player())); + this.Push(player = new PlayerLoader(() => new SoloPlayer())); return true; } From 7045fce55542b0f56a5907f0a87532bfef0728f3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Mar 2021 15:00:02 +0900 Subject: [PATCH 173/202] Move score submission logic in general out to its own Player type --- .../Multiplayer/MultiplayerPlayer.cs | 1 - .../OnlinePlay/Playlists/PlaylistsPlayer.cs | 74 ++----------- osu.Game/Screens/Play/SubmittingPlayer.cs | 100 ++++++++++++++++++ 3 files changed, 106 insertions(+), 69 deletions(-) create mode 100644 osu.Game/Screens/Play/SubmittingPlayer.cs diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index b3cd44d55a..ef34d40497 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -19,7 +19,6 @@ using osuTK; namespace osu.Game.Screens.OnlinePlay.Multiplayer { - // Todo: The "room" part of PlaylistsPlayer should be split out into an abstract player class to be inherited instead. public class MultiplayerPlayer : PlaylistsPlayer { protected override bool PauseOnFocusLost => false; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs index 2b6dbd9dcb..a6aff5e43f 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs @@ -4,11 +4,8 @@ using System; using System.Diagnostics; using System.Linq; -using System.Threading; -using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Online.API; using osu.Game.Online.Rooms; @@ -19,23 +16,12 @@ using osu.Game.Screens.Ranking; namespace osu.Game.Screens.OnlinePlay.Playlists { - public class PlaylistsPlayer : Player + public class PlaylistsPlayer : RoomSubmittingPlayer { public Action Exited; - [Resolved(typeof(Room), nameof(Room.RoomID))] - protected Bindable RoomId { get; private set; } - protected readonly PlaylistItem PlaylistItem; - protected long? Token { get; private set; } - - [Resolved] - private IAPIProvider api { get; set; } - - [Resolved] - private IBindable ruleset { get; set; } - public PlaylistsPlayer(PlaylistItem playlistItem, PlayerConfiguration configuration = null) : base(configuration) { @@ -43,12 +29,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists } [BackgroundDependencyLoader] - private void load() + private void load(IBindable ruleset) { - Token = null; - - bool failed = false; - // Sanity checks to ensure that PlaylistsPlayer matches the settings for the current PlaylistItem if (Beatmap.Value.BeatmapInfo.OnlineBeatmapID != PlaylistItem.Beatmap.Value.OnlineBeatmapID) throw new InvalidOperationException("Current Beatmap does not match PlaylistItem's Beatmap"); @@ -58,31 +40,12 @@ namespace osu.Game.Screens.OnlinePlay.Playlists if (!PlaylistItem.RequiredMods.All(m => Mods.Value.Any(m.Equals))) throw new InvalidOperationException("Current Mods do not match PlaylistItem's RequiredMods"); - - var req = new CreateRoomScoreRequest(RoomId.Value ?? 0, PlaylistItem.ID, Game.VersionHash); - req.Success += r => Token = r.ID; - req.Failure += e => - { - failed = true; - - if (string.IsNullOrEmpty(e.Message)) - Logger.Error(e, "Failed to retrieve a score submission token."); - else - Logger.Log($"You are not able to submit a score: {e.Message}", level: LogLevel.Important); - - Schedule(() => - { - ValidForResume = false; - this.Exit(); - }); - }; - - api.Queue(req); - - while (!failed && !Token.HasValue) - Thread.Sleep(1000); } + protected override APIRequest CreateTokenRequestRequest() => new CreateRoomScoreRequest(RoomId.Value ?? 0, PlaylistItem.ID, Game.VersionHash); + + public override APIRequest CreateSubmissionRequest(Score score, int token) => new SubmitRoomScoreRequest(token, RoomId.Value ?? 0, PlaylistItem.ID, score.ScoreInfo); + public override bool OnExiting(IScreen next) { if (base.OnExiting(next)) @@ -106,31 +69,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists return score; } - protected override async Task SubmitScore(Score score) - { - await base.SubmitScore(score).ConfigureAwait(false); - - Debug.Assert(Token != null); - - var tcs = new TaskCompletionSource(); - var request = new SubmitRoomScoreRequest(Token.Value, RoomId.Value ?? 0, PlaylistItem.ID, score.ScoreInfo); - - request.Success += s => - { - score.ScoreInfo.OnlineScoreID = s.ID; - tcs.SetResult(true); - }; - - request.Failure += e => - { - Logger.Error(e, "Failed to submit score"); - tcs.SetResult(false); - }; - - api.Queue(request); - await tcs.Task.ConfigureAwait(false); - } - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs new file mode 100644 index 0000000000..250e308b3c --- /dev/null +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -0,0 +1,100 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Logging; +using osu.Framework.Screens; +using osu.Game.Online.API; +using osu.Game.Online.Rooms; +using osu.Game.Scoring; + +namespace osu.Game.Screens.Play +{ + public abstract class RoomSubmittingPlayer : SubmittingPlayer + { + [Resolved(typeof(Room), nameof(Room.RoomID))] + protected Bindable RoomId { get; private set; } + + protected RoomSubmittingPlayer(PlayerConfiguration configuration) + : base(configuration) + { + } + } + + public abstract class SubmittingPlayer : Player + { + protected long? Token { get; private set; } + + [Resolved] + private IAPIProvider api { get; set; } + + protected SubmittingPlayer(PlayerConfiguration configuration) + : base(configuration) + { + } + + [BackgroundDependencyLoader] + private void load() + { + Token = null; + + bool failed = false; + + var req = CreateTokenRequestRequest(); + req.Success += r => Token = r.ID; + req.Failure += e => + { + failed = true; + + if (string.IsNullOrEmpty(e.Message)) + Logger.Error(e, "Failed to retrieve a score submission token."); + else + Logger.Log($"You are not able to submit a score: {e.Message}", level: LogLevel.Important); + + Schedule(() => + { + ValidForResume = false; + this.Exit(); + }); + }; + + api.Queue(req); + + while (!failed && !Token.HasValue) + Thread.Sleep(1000); + } + + protected override async Task SubmitScore(Score score) + { + await base.SubmitScore(score).ConfigureAwait(false); + + Debug.Assert(Token != null); + + var tcs = new TaskCompletionSource(); + var request = CreateSubmissionRequest(score, Token.Value); + + request.Success += s => + { + score.ScoreInfo.OnlineScoreID = s.ID; + tcs.SetResult(true); + }; + + request.Failure += e => + { + Logger.Error(e, "Failed to submit score"); + tcs.SetResult(false); + }; + + api.Queue(request); + await tcs.Task.ConfigureAwait(false); + } + + protected abstract APIRequest CreateSubmissionRequest(Score score, long token); + + protected abstract APIRequest CreateTokenRequestRequest(); + } +} From 12f050264aa31af09089a32d23472defe67a0ec5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Mar 2021 15:33:31 +0900 Subject: [PATCH 174/202] Further split out a player class which submits to "rooms" --- .../Multiplayer/MultiplayerPlayer.cs | 3 +- .../OnlinePlay/Playlists/PlaylistsPlayer.cs | 10 +----- osu.Game/Screens/Play/RoomSubmittingPlayer.cs | 32 +++++++++++++++++++ osu.Game/Screens/Play/SubmittingPlayer.cs | 15 ++------- 4 files changed, 37 insertions(+), 23 deletions(-) create mode 100644 osu.Game/Screens/Play/RoomSubmittingPlayer.cs diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index ef34d40497..2ba04b75d6 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -11,7 +11,6 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Scoring; -using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Ranking; @@ -19,7 +18,7 @@ using osuTK; namespace osu.Game.Screens.OnlinePlay.Multiplayer { - public class MultiplayerPlayer : PlaylistsPlayer + public class MultiplayerPlayer : RoomSubmittingPlayer { protected override bool PauseOnFocusLost => false; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs index a6aff5e43f..260d4961ff 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs @@ -7,7 +7,6 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Screens; -using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Scoring; @@ -20,12 +19,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { public Action Exited; - protected readonly PlaylistItem PlaylistItem; - public PlaylistsPlayer(PlaylistItem playlistItem, PlayerConfiguration configuration = null) - : base(configuration) + : base(playlistItem, configuration) { - PlaylistItem = playlistItem; } [BackgroundDependencyLoader] @@ -42,10 +38,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists throw new InvalidOperationException("Current Mods do not match PlaylistItem's RequiredMods"); } - protected override APIRequest CreateTokenRequestRequest() => new CreateRoomScoreRequest(RoomId.Value ?? 0, PlaylistItem.ID, Game.VersionHash); - - public override APIRequest CreateSubmissionRequest(Score score, int token) => new SubmitRoomScoreRequest(token, RoomId.Value ?? 0, PlaylistItem.ID, score.ScoreInfo); - public override bool OnExiting(IScreen next) { if (base.OnExiting(next)) diff --git a/osu.Game/Screens/Play/RoomSubmittingPlayer.cs b/osu.Game/Screens/Play/RoomSubmittingPlayer.cs new file mode 100644 index 0000000000..c695e99874 --- /dev/null +++ b/osu.Game/Screens/Play/RoomSubmittingPlayer.cs @@ -0,0 +1,32 @@ +// 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.Allocation; +using osu.Framework.Bindables; +using osu.Game.Online.API; +using osu.Game.Online.Rooms; +using osu.Game.Scoring; + +namespace osu.Game.Screens.Play +{ + /// + /// A player instance which submits to a room backing. This is generally used by playlists and multiplayer. + /// + public abstract class RoomSubmittingPlayer : SubmittingPlayer + { + [Resolved(typeof(Room), nameof(Room.RoomID))] + protected Bindable RoomId { get; private set; } + + protected readonly PlaylistItem PlaylistItem; + + protected RoomSubmittingPlayer(PlaylistItem playlistItem, PlayerConfiguration configuration = null) + : base(configuration) + { + PlaylistItem = playlistItem; + } + + protected override APIRequest CreateTokenRequestRequest() => new CreateRoomScoreRequest(RoomId.Value ?? 0, PlaylistItem.ID, Game.VersionHash); + + public override APIRequest CreateSubmissionRequest(Score score, int token) => new SubmitRoomScoreRequest(token, RoomId.Value ?? 0, PlaylistItem.ID, score.ScoreInfo); + } +} diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 250e308b3c..5577683f05 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -5,7 +5,6 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Online.API; @@ -14,17 +13,9 @@ using osu.Game.Scoring; namespace osu.Game.Screens.Play { - public abstract class RoomSubmittingPlayer : SubmittingPlayer - { - [Resolved(typeof(Room), nameof(Room.RoomID))] - protected Bindable RoomId { get; private set; } - - protected RoomSubmittingPlayer(PlayerConfiguration configuration) - : base(configuration) - { - } - } - + /// + /// A player instance which supports submitting scores to an online store. + /// public abstract class SubmittingPlayer : Player { protected long? Token { get; private set; } From 194b2d05d3749b6b60796c699490df6320c62d59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Mar 2021 15:35:06 +0900 Subject: [PATCH 175/202] Update SoloPlayer to derive SubmittingPlayer --- osu.Game/Screens/Play/SoloPlayer.cs | 15 ++++++++++++++- osu.Game/Screens/Play/SubmittingPlayer.cs | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/SoloPlayer.cs b/osu.Game/Screens/Play/SoloPlayer.cs index 79860f3eda..f2f97c5d0d 100644 --- a/osu.Game/Screens/Play/SoloPlayer.cs +++ b/osu.Game/Screens/Play/SoloPlayer.cs @@ -1,9 +1,22 @@ // 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.Online.API; +using osu.Game.Online.Rooms; +using osu.Game.Scoring; + namespace osu.Game.Screens.Play { - public class SoloPlayer : Player + public class SoloPlayer : SubmittingPlayer { + public override APIRequest CreateSubmissionRequest(Score score, int token) + { + throw new System.NotImplementedException(); + } + + protected override APIRequest CreateTokenRequestRequest() + { + throw new System.NotImplementedException(); + } } } diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 5577683f05..e7847a9902 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Play [Resolved] private IAPIProvider api { get; set; } - protected SubmittingPlayer(PlayerConfiguration configuration) + protected SubmittingPlayer(PlayerConfiguration configuration = null) : base(configuration) { } From 571124669daf940ef35bc5a6a81cd8b64fa02bc7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Mar 2021 15:45:22 +0900 Subject: [PATCH 176/202] Remove all references to "score submission" from Player --- .../Multiplayer/MultiplayerPlayer.cs | 4 ++-- osu.Game/Screens/Play/Player.cs | 20 +++++++++---------- osu.Game/Screens/Play/SubmittingPlayer.cs | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 2ba04b75d6..3797adf360 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -133,9 +133,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private void onResultsReady() => resultsReady.SetResult(true); - protected override async Task SubmitScore(Score score) + protected override async Task PrepareScoreForResultsAsync(Score score) { - await base.SubmitScore(score).ConfigureAwait(false); + await base.PrepareScoreForResultsAsync(score).ConfigureAwait(false); await client.ChangeState(MultiplayerUserState.FinishedPlay).ConfigureAwait(false); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 4cf0274614..efe5d26409 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -559,7 +559,7 @@ namespace osu.Game.Screens.Play } private ScheduledDelegate completionProgressDelegate; - private Task scoreSubmissionTask; + private Task prepareScoreForDisplayTask; private void updateCompletionState(ValueChangedEvent completionState) { @@ -586,17 +586,17 @@ namespace osu.Game.Screens.Play if (!Configuration.ShowResults) return; - scoreSubmissionTask ??= Task.Run(async () => + prepareScoreForDisplayTask ??= Task.Run(async () => { var score = CreateScore(); try { - await SubmitScore(score).ConfigureAwait(false); + await PrepareScoreForResultsAsync(score).ConfigureAwait(false); } catch (Exception ex) { - Logger.Error(ex, "Score submission failed!"); + Logger.Error(ex, "Score preparation failed!"); } try @@ -617,7 +617,7 @@ namespace osu.Game.Screens.Play private void scheduleCompletion() => completionProgressDelegate = Schedule(() => { - if (!scoreSubmissionTask.IsCompleted) + if (!prepareScoreForDisplayTask.IsCompleted) { scheduleCompletion(); return; @@ -625,7 +625,7 @@ namespace osu.Game.Screens.Play // screen may be in the exiting transition phase. if (this.IsCurrentScreen()) - this.Push(CreateResults(scoreSubmissionTask.Result)); + this.Push(CreateResults(prepareScoreForDisplayTask.Result)); }); protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !GameplayClockContainer.IsPaused.Value; @@ -895,11 +895,11 @@ namespace osu.Game.Screens.Play } /// - /// Submits the player's . + /// Prepare the for display at results. /// - /// The to submit. - /// The submitted score. - protected virtual Task SubmitScore(Score score) => Task.CompletedTask; + /// The to prepare. + /// A task that prepares the provided score. On completion, the score is assumed to be ready for display. + protected virtual Task PrepareScoreForResultsAsync(Score score) => Task.CompletedTask; /// /// Creates the for a . diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index e7847a9902..d876cad941 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -59,9 +59,9 @@ namespace osu.Game.Screens.Play Thread.Sleep(1000); } - protected override async Task SubmitScore(Score score) + protected override async Task PrepareScoreForResultsAsync(Score score) { - await base.SubmitScore(score).ConfigureAwait(false); + await base.PrepareScoreForResultsAsync(score).ConfigureAwait(false); Debug.Assert(Token != null); From 3cd8bf2d7f30d2be87d7e67acdcae1a4cfef69f6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Mar 2021 16:05:40 +0900 Subject: [PATCH 177/202] Move token request construction to LoadAsyncComplete to better allow DI usage --- .../Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs | 5 +++++ osu.Game/Screens/Play/SubmittingPlayer.cs | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 3797adf360..a5adcdb8ad 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -61,6 +61,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(ScoreProcessor, userIds), HUDOverlay.Add); HUDOverlay.Add(loadingDisplay = new LoadingLayer(true) { Depth = float.MaxValue }); + } + + protected override void LoadAsyncComplete() + { + base.LoadAsyncComplete(); if (Token == null) return; // Todo: Somehow handle token retrieval failure. diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index d876cad941..356f70f4bf 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -28,11 +28,11 @@ namespace osu.Game.Screens.Play { } - [BackgroundDependencyLoader] - private void load() + protected override void LoadAsyncComplete() { - Token = null; + base.LoadAsyncComplete(); + // Token request construction should happen post-load to allow derived classes to potentially prepare DI backings that are used to create the request. bool failed = false; var req = CreateTokenRequestRequest(); From 242b847516cdf53f4f2c54fdd7097b9ec862f9a5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Mar 2021 16:41:36 +0900 Subject: [PATCH 178/202] Add flow for allowing gameplay to continue even when an error occurs with token retrieval --- osu.Game/Screens/Play/RoomSubmittingPlayer.cs | 2 +- osu.Game/Screens/Play/SubmittingPlayer.cs | 71 +++++++++++++------ 2 files changed, 51 insertions(+), 22 deletions(-) diff --git a/osu.Game/Screens/Play/RoomSubmittingPlayer.cs b/osu.Game/Screens/Play/RoomSubmittingPlayer.cs index c695e99874..6ef39e4b75 100644 --- a/osu.Game/Screens/Play/RoomSubmittingPlayer.cs +++ b/osu.Game/Screens/Play/RoomSubmittingPlayer.cs @@ -27,6 +27,6 @@ namespace osu.Game.Screens.Play protected override APIRequest CreateTokenRequestRequest() => new CreateRoomScoreRequest(RoomId.Value ?? 0, PlaylistItem.ID, Game.VersionHash); - public override APIRequest CreateSubmissionRequest(Score score, int token) => new SubmitRoomScoreRequest(token, RoomId.Value ?? 0, PlaylistItem.ID, score.ScoreInfo); + protected override APIRequest CreateSubmissionRequest(Score score, long token) => new SubmitRoomScoreRequest(token, RoomId.Value ?? 0, PlaylistItem.ID, score.ScoreInfo); } } diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 356f70f4bf..55f4ba4b9b 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -1,8 +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 System.Diagnostics; -using System.Threading; +using System; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Logging; @@ -30,40 +29,70 @@ namespace osu.Game.Screens.Play protected override void LoadAsyncComplete() { - base.LoadAsyncComplete(); - // Token request construction should happen post-load to allow derived classes to potentially prepare DI backings that are used to create the request. - bool failed = false; + var tcs = new TaskCompletionSource(); + + if (!api.IsLoggedIn) + { + fail(new InvalidOperationException("API is not online.")); + return; + } var req = CreateTokenRequestRequest(); - req.Success += r => Token = r.ID; - req.Failure += e => + + if (req == null) { - failed = true; + fail(new InvalidOperationException("Request could not be constructed.")); + return; + } - if (string.IsNullOrEmpty(e.Message)) - Logger.Error(e, "Failed to retrieve a score submission token."); - else - Logger.Log($"You are not able to submit a score: {e.Message}", level: LogLevel.Important); - - Schedule(() => - { - ValidForResume = false; - this.Exit(); - }); + req.Success += r => + { + Token = r.ID; + tcs.SetResult(true); }; + req.Failure += fail; api.Queue(req); - while (!failed && !Token.HasValue) - Thread.Sleep(1000); + tcs.Task.Wait(); + + void fail(Exception exception) + { + if (HandleTokenRetrievalFailure(exception)) + { + if (string.IsNullOrEmpty(exception.Message)) + Logger.Error(exception, "Failed to retrieve a score submission token."); + else + Logger.Log($"You are not able to submit a score: {exception.Message}", level: LogLevel.Important); + + Schedule(() => + { + ValidForResume = false; + this.Exit(); + }); + } + + tcs.SetResult(false); + } + + base.LoadAsyncComplete(); } + /// + /// Called when a token could not be retrieved for submission. + /// + /// The error causing the failure. + /// Whether gameplay should be immediately exited as a result. Returning false allows the gameplay session to continue. Defaults to true. + protected virtual bool HandleTokenRetrievalFailure(Exception exception) => true; + protected override async Task PrepareScoreForResultsAsync(Score score) { await base.PrepareScoreForResultsAsync(score).ConfigureAwait(false); - Debug.Assert(Token != null); + // token may be null if the request failed but gameplay was still allowed (see HandleTokenRetrievalFailure). + if (Token == null) + return; var tcs = new TaskCompletionSource(); var request = CreateSubmissionRequest(score, Token.Value); From e649a330a405d7d58490e50ea8c7f3b17cb3e27c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Mar 2021 16:41:52 +0900 Subject: [PATCH 179/202] Implement SoloPlayer's request construction --- osu.Game/Screens/Play/SoloPlayer.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/SoloPlayer.cs b/osu.Game/Screens/Play/SoloPlayer.cs index f2f97c5d0d..3dc9df146e 100644 --- a/osu.Game/Screens/Play/SoloPlayer.cs +++ b/osu.Game/Screens/Play/SoloPlayer.cs @@ -1,22 +1,34 @@ // 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.Diagnostics; using osu.Game.Online.API; using osu.Game.Online.Rooms; +using osu.Game.Online.Solo; using osu.Game.Scoring; namespace osu.Game.Screens.Play { public class SoloPlayer : SubmittingPlayer { - public override APIRequest CreateSubmissionRequest(Score score, int token) + protected override APIRequest CreateSubmissionRequest(Score score, long token) { - throw new System.NotImplementedException(); + Debug.Assert(Beatmap.Value.BeatmapInfo.OnlineBeatmapID != null); + + int beatmapId = Beatmap.Value.BeatmapInfo.OnlineBeatmapID.Value; + + return new SubmitSoloScoreRequest(beatmapId, token, score.ScoreInfo); } protected override APIRequest CreateTokenRequestRequest() { - throw new System.NotImplementedException(); + if (!(Beatmap.Value.BeatmapInfo.OnlineBeatmapID is int beatmapId)) + return null; + + return new CreateSoloScoreRequest(beatmapId, Game.VersionHash); } + + protected override bool HandleTokenRetrievalFailure(Exception exception) => false; } } From 64e85ba995a20dcf1cd3eb6f95fc65ae1a47a0cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Mar 2021 19:19:07 +0900 Subject: [PATCH 180/202] Always fade out approach circles at a HitObject's start time to better match stable --- .../Objects/Drawables/DrawableHitCircle.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 77094f928b..189003875d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -164,28 +164,29 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables ApproachCircle.Expire(true); } + protected override void UpdateStartTimeStateTransforms() + { + base.UpdateStartTimeStateTransforms(); + + ApproachCircle.FadeOut(50); + } + protected override void UpdateHitStateTransforms(ArmedState state) { Debug.Assert(HitObject.HitWindows != null); + // todo: temporary / arbitrary, used for lifetime optimisation. + this.Delay(800).FadeOut(); + switch (state) { case ArmedState.Idle: - this.Delay(HitObject.TimePreempt).FadeOut(500); HitArea.HitAction = null; break; case ArmedState.Miss: - ApproachCircle.FadeOut(50); this.FadeOut(100); break; - - case ArmedState.Hit: - ApproachCircle.FadeOut(50); - - // todo: temporary / arbitrary - this.Delay(800).FadeOut(); - break; } Expire(); From d10ff615feeadb9ece5d1f67863ddb9d26152bdc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Mar 2021 19:22:37 +0900 Subject: [PATCH 181/202] Fix default skin's glow resetting fade on miss --- osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs index fcbe4c1b28..46aeadc59b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs @@ -74,10 +74,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default private void updateState(DrawableHitObject drawableObject, ArmedState state) { - using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime, true)) - { + using (BeginAbsoluteSequence(drawableObject.StateUpdateTime)) glow.FadeOut(400); + using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime)) + { switch (state) { case ArmedState.Hit: From d17c431faf92affd103a85b46f0a79fdf633040e Mon Sep 17 00:00:00 2001 From: Shivam Date: Tue, 23 Mar 2021 23:22:17 +0100 Subject: [PATCH 182/202] Disable relative mode for TournamentGame --- osu.Game.Tournament/TournamentGame.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index fadb821bef..bf43b198c4 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -2,18 +2,21 @@ // See the LICENCE file in the repository root for full licence text. using System.Drawing; -using osu.Framework.Extensions.Color4Extensions; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Configuration; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Colour; -using osu.Game.Graphics.Cursor; -using osu.Game.Tournament.Models; +using osu.Framework.Input.Handlers.Mouse; +using osu.Framework.Platform; using osu.Game.Graphics; +using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; +using osu.Game.Tournament.Models; using osuTK; using osuTK.Graphics; @@ -36,7 +39,7 @@ namespace osu.Game.Tournament private LoadingSpinner loadingSpinner; [BackgroundDependencyLoader] - private void load(FrameworkConfigManager frameworkConfig) + private void load(FrameworkConfigManager frameworkConfig, GameHost host) { windowSize = frameworkConfig.GetBindable(FrameworkSetting.WindowedSize); windowMode = frameworkConfig.GetBindable(FrameworkSetting.WindowMode); @@ -48,6 +51,9 @@ namespace osu.Game.Tournament Margin = new MarginPadding(40), }); + var m = (MouseHandler)host.AvailableInputHandlers.Single(t => t is MouseHandler); + m.UseRelativeMode.Value = false; + loadingSpinner.Show(); BracketLoadTask.ContinueWith(_ => LoadComponentsAsync(new[] From fbb992fc7e432900ce9090d98446e6e3e18588ae Mon Sep 17 00:00:00 2001 From: Owen Young Date: Tue, 23 Mar 2021 19:18:32 -0500 Subject: [PATCH 183/202] Added a comment to new method --- osu.Game/OsuGame.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index a52899433a..e8284c0bad 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1008,6 +1008,7 @@ namespace osu.Game Exit(); } protected override IDictionary GetFrameworkConfigDefaults() { + // Overriding settings determined by Framework IDictionary defaultOverrides = new Dictionary(); defaultOverrides.Add(FrameworkSetting.WindowMode, WindowMode.Fullscreen); return defaultOverrides; From 67a03ebc2371c2bb6c14b721fd03f2627cb58b92 Mon Sep 17 00:00:00 2001 From: Owen Young Date: Tue, 23 Mar 2021 19:31:16 -0500 Subject: [PATCH 184/202] Fixed formatting issues to be in line with osu coding standards --- osu.Game/OsuGame.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e8284c0bad..ca8fa9f1f6 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1007,7 +1007,9 @@ namespace osu.Game if (newScreen == null) Exit(); } - protected override IDictionary GetFrameworkConfigDefaults() { + + protected override IDictionary GetFrameworkConfigDefaults() + { // Overriding settings determined by Framework IDictionary defaultOverrides = new Dictionary(); defaultOverrides.Add(FrameworkSetting.WindowMode, WindowMode.Fullscreen); From 437dadc85f0927936004abc72bc5331289d7e333 Mon Sep 17 00:00:00 2001 From: Owen Young Date: Tue, 23 Mar 2021 19:37:55 -0500 Subject: [PATCH 185/202] Changed comment on GetFrameworkConfigDefaults() to be more accurate --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index ca8fa9f1f6..f211723c59 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1010,7 +1010,7 @@ namespace osu.Game protected override IDictionary GetFrameworkConfigDefaults() { - // Overriding settings determined by Framework + // Overriding config defaults determined by Framework IDictionary defaultOverrides = new Dictionary(); defaultOverrides.Add(FrameworkSetting.WindowMode, WindowMode.Fullscreen); return defaultOverrides; From a1c35677efb5e68f5691896b8ca6dfb3763b12e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Mar 2021 13:02:17 +0900 Subject: [PATCH 186/202] Add more xmldoc --- osu.Game/Screens/Play/SubmittingPlayer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 55f4ba4b9b..24d540fbf3 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -17,6 +17,9 @@ namespace osu.Game.Screens.Play /// public abstract class SubmittingPlayer : Player { + /// + /// The token to be used for the current submission. This is fetched via a request created by . + /// protected long? Token { get; private set; } [Resolved] From 8bed7748d696666bfe0023f78710cf84440bc244 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Mar 2021 13:02:37 +0900 Subject: [PATCH 187/202] Rename token request method to avoid double Request terminology --- osu.Game/Screens/Play/RoomSubmittingPlayer.cs | 2 +- osu.Game/Screens/Play/SoloPlayer.cs | 2 +- osu.Game/Screens/Play/SubmittingPlayer.cs | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/RoomSubmittingPlayer.cs b/osu.Game/Screens/Play/RoomSubmittingPlayer.cs index 6ef39e4b75..d7b49bf2cb 100644 --- a/osu.Game/Screens/Play/RoomSubmittingPlayer.cs +++ b/osu.Game/Screens/Play/RoomSubmittingPlayer.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Play PlaylistItem = playlistItem; } - protected override APIRequest CreateTokenRequestRequest() => new CreateRoomScoreRequest(RoomId.Value ?? 0, PlaylistItem.ID, Game.VersionHash); + protected override APIRequest CreateTokenRequest() => new CreateRoomScoreRequest(RoomId.Value ?? 0, PlaylistItem.ID, Game.VersionHash); protected override APIRequest CreateSubmissionRequest(Score score, long token) => new SubmitRoomScoreRequest(token, RoomId.Value ?? 0, PlaylistItem.ID, score.ScoreInfo); } diff --git a/osu.Game/Screens/Play/SoloPlayer.cs b/osu.Game/Screens/Play/SoloPlayer.cs index 3dc9df146e..5c465b6d8f 100644 --- a/osu.Game/Screens/Play/SoloPlayer.cs +++ b/osu.Game/Screens/Play/SoloPlayer.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens.Play return new SubmitSoloScoreRequest(beatmapId, token, score.ScoreInfo); } - protected override APIRequest CreateTokenRequestRequest() + protected override APIRequest CreateTokenRequest() { if (!(Beatmap.Value.BeatmapInfo.OnlineBeatmapID is int beatmapId)) return null; diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 24d540fbf3..55b10abd5c 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -18,7 +18,7 @@ namespace osu.Game.Screens.Play public abstract class SubmittingPlayer : Player { /// - /// The token to be used for the current submission. This is fetched via a request created by . + /// The token to be used for the current submission. This is fetched via a request created by . /// protected long? Token { get; private set; } @@ -41,7 +41,7 @@ namespace osu.Game.Screens.Play return; } - var req = CreateTokenRequestRequest(); + var req = CreateTokenRequest(); if (req == null) { @@ -118,6 +118,6 @@ namespace osu.Game.Screens.Play protected abstract APIRequest CreateSubmissionRequest(Score score, long token); - protected abstract APIRequest CreateTokenRequestRequest(); + protected abstract APIRequest CreateTokenRequest(); } } From e372e355efba030da6866f61fad35abca462cd6d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Mar 2021 13:12:51 +0900 Subject: [PATCH 188/202] Reorder overrides in SoloPlayer to better follow chronological request order --- osu.Game/Screens/Play/SoloPlayer.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Play/SoloPlayer.cs b/osu.Game/Screens/Play/SoloPlayer.cs index 5c465b6d8f..ee1ccdc5b3 100644 --- a/osu.Game/Screens/Play/SoloPlayer.cs +++ b/osu.Game/Screens/Play/SoloPlayer.cs @@ -12,15 +12,6 @@ namespace osu.Game.Screens.Play { public class SoloPlayer : SubmittingPlayer { - protected override APIRequest CreateSubmissionRequest(Score score, long token) - { - Debug.Assert(Beatmap.Value.BeatmapInfo.OnlineBeatmapID != null); - - int beatmapId = Beatmap.Value.BeatmapInfo.OnlineBeatmapID.Value; - - return new SubmitSoloScoreRequest(beatmapId, token, score.ScoreInfo); - } - protected override APIRequest CreateTokenRequest() { if (!(Beatmap.Value.BeatmapInfo.OnlineBeatmapID is int beatmapId)) @@ -30,5 +21,14 @@ namespace osu.Game.Screens.Play } protected override bool HandleTokenRetrievalFailure(Exception exception) => false; + + protected override APIRequest CreateSubmissionRequest(Score score, long token) + { + Debug.Assert(Beatmap.Value.BeatmapInfo.OnlineBeatmapID != null); + + int beatmapId = Beatmap.Value.BeatmapInfo.OnlineBeatmapID.Value; + + return new SubmitSoloScoreRequest(beatmapId, token, score.ScoreInfo); + } } } From a0c6c4da35dfe5cf2a28efa20da64acab28f25ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Mar 2021 13:17:13 +0900 Subject: [PATCH 189/202] Rename and refactor token request process to be easier to understand --- osu.Game/Screens/Play/SubmittingPlayer.cs | 24 +++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 55b10abd5c..5d087c212d 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -3,6 +3,7 @@ using System; using System.Threading.Tasks; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Logging; using osu.Framework.Screens; @@ -37,7 +38,7 @@ namespace osu.Game.Screens.Play if (!api.IsLoggedIn) { - fail(new InvalidOperationException("API is not online.")); + handleFailure(new InvalidOperationException("API is not online.")); return; } @@ -45,7 +46,7 @@ namespace osu.Game.Screens.Play if (req == null) { - fail(new InvalidOperationException("Request could not be constructed.")); + handleFailure(new InvalidOperationException("Request could not be constructed.")); return; } @@ -54,13 +55,13 @@ namespace osu.Game.Screens.Play Token = r.ID; tcs.SetResult(true); }; - req.Failure += fail; + req.Failure += handleFailure; api.Queue(req); tcs.Task.Wait(); - void fail(Exception exception) + void handleFailure(Exception exception) { if (HandleTokenRetrievalFailure(exception)) { @@ -116,8 +117,19 @@ namespace osu.Game.Screens.Play await tcs.Task.ConfigureAwait(false); } - protected abstract APIRequest CreateSubmissionRequest(Score score, long token); - + /// + /// Construct a request to be used for retrieval of the score token. + /// Can return null, at which point will be fired. + /// + [CanBeNull] protected abstract APIRequest CreateTokenRequest(); + + /// + /// Construct a request to submit the score. + /// Will only be invoked if the request constructed via was successful. + /// + /// The score to be submitted. + /// The submission token. + protected abstract APIRequest CreateSubmissionRequest(Score score, long token); } } From 84b2f9a848c0c61b047f5eb463e7848cdbd9b03a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Mar 2021 13:20:44 +0900 Subject: [PATCH 190/202] Make token private --- .../Multiplayer/MultiplayerPlayer.cs | 4 ++-- osu.Game/Screens/Play/SubmittingPlayer.cs | 20 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index a5adcdb8ad..aaacf891bb 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -67,8 +67,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { base.LoadAsyncComplete(); - if (Token == null) - return; // Todo: Somehow handle token retrieval failure. + if (!ValidForResume) + return; // token retrieval may have failed. client.MatchStarted += onMatchStarted; client.ResultsReady += onResultsReady; diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 5d087c212d..87a4eb5efe 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens.Play /// /// The token to be used for the current submission. This is fetched via a request created by . /// - protected long? Token { get; private set; } + private long? token; [Resolved] private IAPIProvider api { get; set; } @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Play if (!api.IsLoggedIn) { - handleFailure(new InvalidOperationException("API is not online.")); + handleTokenFailure(new InvalidOperationException("API is not online.")); return; } @@ -46,22 +46,24 @@ namespace osu.Game.Screens.Play if (req == null) { - handleFailure(new InvalidOperationException("Request could not be constructed.")); + handleTokenFailure(new InvalidOperationException("Request could not be constructed.")); return; } req.Success += r => { - Token = r.ID; + token = r.ID; tcs.SetResult(true); }; - req.Failure += handleFailure; + req.Failure += handleTokenFailure; api.Queue(req); tcs.Task.Wait(); - void handleFailure(Exception exception) + base.LoadAsyncComplete(); + + void handleTokenFailure(Exception exception) { if (HandleTokenRetrievalFailure(exception)) { @@ -79,8 +81,6 @@ namespace osu.Game.Screens.Play tcs.SetResult(false); } - - base.LoadAsyncComplete(); } /// @@ -95,11 +95,11 @@ namespace osu.Game.Screens.Play await base.PrepareScoreForResultsAsync(score).ConfigureAwait(false); // token may be null if the request failed but gameplay was still allowed (see HandleTokenRetrievalFailure). - if (Token == null) + if (token == null) return; var tcs = new TaskCompletionSource(); - var request = CreateSubmissionRequest(score, Token.Value); + var request = CreateSubmissionRequest(score, token.Value); request.Success += s => { From d55324585d678603435adcaed815d3d85186037b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Mar 2021 13:23:23 +0900 Subject: [PATCH 191/202] Change RoomSubmittingPlayer's request implementation to return null on RoomID missing, rather than silently succeeding --- osu.Game/Screens/Play/RoomSubmittingPlayer.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/RoomSubmittingPlayer.cs b/osu.Game/Screens/Play/RoomSubmittingPlayer.cs index d7b49bf2cb..7ba12f5db6 100644 --- a/osu.Game/Screens/Play/RoomSubmittingPlayer.cs +++ b/osu.Game/Screens/Play/RoomSubmittingPlayer.cs @@ -25,7 +25,13 @@ namespace osu.Game.Screens.Play PlaylistItem = playlistItem; } - protected override APIRequest CreateTokenRequest() => new CreateRoomScoreRequest(RoomId.Value ?? 0, PlaylistItem.ID, Game.VersionHash); + protected override APIRequest CreateTokenRequest() + { + if (!(RoomId.Value is long roomId)) + return null; + + return new CreateRoomScoreRequest(roomId, PlaylistItem.ID, Game.VersionHash); + } protected override APIRequest CreateSubmissionRequest(Score score, long token) => new SubmitRoomScoreRequest(token, RoomId.Value ?? 0, PlaylistItem.ID, score.ScoreInfo); } From f95175983ae6f4f79a2a27877467e8ef40f5cb22 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Mar 2021 13:37:37 +0900 Subject: [PATCH 192/202] Make code more concise and move method to a more appropriate place --- osu.Game/OsuGame.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 53dc900254..dd1fa32ad9 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -531,6 +531,13 @@ namespace osu.Game SentryLogger.Dispose(); } + protected override IDictionary GetFrameworkConfigDefaults() + => new Dictionary + { + // General expectation that osu! starts in fullscreen by default (also gives the most predictable performance) + { FrameworkSetting.WindowMode, WindowMode.Fullscreen } + }; + protected override void LoadComplete() { base.LoadComplete(); @@ -1013,13 +1020,5 @@ namespace osu.Game if (newScreen == null) Exit(); } - - protected override IDictionary GetFrameworkConfigDefaults() - { - // Overriding config defaults determined by Framework - IDictionary defaultOverrides = new Dictionary(); - defaultOverrides.Add(FrameworkSetting.WindowMode, WindowMode.Fullscreen); - return defaultOverrides; - } } } From 5ad8dc316fa1aeed2f2dc194c2f9520b7f93ba09 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Mar 2021 14:09:15 +0900 Subject: [PATCH 193/202] Add inline comment and improve linq robustness --- osu.Game.Tournament/TournamentGame.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index bf43b198c4..87e23e3404 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -51,8 +51,12 @@ namespace osu.Game.Tournament Margin = new MarginPadding(40), }); - var m = (MouseHandler)host.AvailableInputHandlers.Single(t => t is MouseHandler); - m.UseRelativeMode.Value = false; + // in order to have the OS mouse cursor visible, relative mode needs to be disabled. + // can potentially be removed when https://github.com/ppy/osu-framework/issues/4309 is resolved. + var mouseHandler = host.AvailableInputHandlers.OfType().FirstOrDefault(); + + if (mouseHandler != null) + mouseHandler.UseRelativeMode.Value = false; loadingSpinner.Show(); From fc5719e445750c10ddad28ead84239a9cb0f519d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 24 Mar 2021 21:31:53 +0300 Subject: [PATCH 194/202] Fix SkinManager not handling extensions casing comparsion properly --- osu.Game/Skinning/SkinManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 9257636301..dfc2981c6f 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -104,7 +104,7 @@ namespace osu.Game.Skinning protected override string ComputeHash(SkinInfo item, ArchiveReader reader = null) { // we need to populate early to create a hash based off skin.ini contents - if (item.Name?.Contains(".osk") == true) + if (item.Name?.EndsWith(".osk", StringComparison.OrdinalIgnoreCase) == true) populateMetadata(item); if (item.Creator != null && item.Creator != unknown_creator_string) @@ -122,7 +122,7 @@ namespace osu.Game.Skinning { await base.Populate(model, archive, cancellationToken).ConfigureAwait(false); - if (model.Name?.Contains(".osk") == true) + if (model.Name?.EndsWith(".osk", StringComparison.OrdinalIgnoreCase) == true) populateMetadata(model); } @@ -137,7 +137,7 @@ namespace osu.Game.Skinning } else { - item.Name = item.Name.Replace(".osk", ""); + item.Name = item.Name.Replace(".osk", "", StringComparison.OrdinalIgnoreCase); item.Creator ??= unknown_creator_string; } } From 35810bb2fb98b7f310d2e19b29a8369e1ddb99a8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 24 Mar 2021 22:55:15 +0300 Subject: [PATCH 195/202] Add test coverage --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 25 +++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index a5b4b04ef5..8124bd4199 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -113,6 +113,31 @@ namespace osu.Game.Tests.Skins.IO } } + [Test] + public async Task TestImportUpperCasedOskArchive() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest))) + { + try + { + var osu = LoadOsuIntoHost(host); + + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1"), "skin1.OsK")); + + Assert.That(imported.Name, Is.EqualTo("name 1")); + Assert.That(imported.Creator, Is.EqualTo("author 1")); + + var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1"), "skin1.oSK")); + + Assert.That(imported2.Hash, Is.EqualTo(imported.Hash)); + } + finally + { + host.Exit(); + } + } + } + private MemoryStream createOsk(string name, string author) { var zipStream = new MemoryStream(); From 8753d45b71baab76b83f6245e5e8ad98c3ea3e92 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Mar 2021 12:32:51 +0900 Subject: [PATCH 196/202] Remove duplicate crash report issue template --- .github/ISSUE_TEMPLATE/02-crash-issues.md | 20 ------------------- ...issues.md => 02-feature-request-issues.md} | 2 +- 2 files changed, 1 insertion(+), 21 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/02-crash-issues.md rename .github/ISSUE_TEMPLATE/{03-feature-request-issues.md => 02-feature-request-issues.md} (62%) diff --git a/.github/ISSUE_TEMPLATE/02-crash-issues.md b/.github/ISSUE_TEMPLATE/02-crash-issues.md deleted file mode 100644 index 04170312d1..0000000000 --- a/.github/ISSUE_TEMPLATE/02-crash-issues.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Crash Report -about: Issues regarding crashes or permanent freezes. ---- -**Describe the crash:** - -**Screenshots or videos showing encountered issue:** - -**osu!lazer version:** - -**Logs:** - - -**Computer Specifications:** diff --git a/.github/ISSUE_TEMPLATE/03-feature-request-issues.md b/.github/ISSUE_TEMPLATE/02-feature-request-issues.md similarity index 62% rename from .github/ISSUE_TEMPLATE/03-feature-request-issues.md rename to .github/ISSUE_TEMPLATE/02-feature-request-issues.md index 54c4ff94e5..c3357dd780 100644 --- a/.github/ISSUE_TEMPLATE/03-feature-request-issues.md +++ b/.github/ISSUE_TEMPLATE/02-feature-request-issues.md @@ -1,6 +1,6 @@ --- name: Feature Request -about: Features you would like to see in the game! +about: Propose a feature you would like to see in the game! --- **Describe the new feature:** From f8f461a7a4b2c50edca8d0a2f3c9318d244394a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Mar 2021 12:33:07 +0900 Subject: [PATCH 197/202] Include blurb in issue template to hopefully help people find existing reports --- .github/ISSUE_TEMPLATE/01-bug-issues.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/01-bug-issues.md b/.github/ISSUE_TEMPLATE/01-bug-issues.md index 6050036cbf..e45893b97a 100644 --- a/.github/ISSUE_TEMPLATE/01-bug-issues.md +++ b/.github/ISSUE_TEMPLATE/01-bug-issues.md @@ -1,7 +1,18 @@ --- name: Bug Report -about: Issues regarding encountered bugs. +about: Report a bug or crash to desktop --- + + + + **Describe the bug:** **Screenshots or videos showing encountered issue:** @@ -9,6 +20,7 @@ about: Issues regarding encountered bugs. **osu!lazer version:** **Logs:** +