diff --git a/osu.Desktop/app.manifest b/osu.Desktop/app.manifest
new file mode 100644
index 0000000000..2e9127bf44
--- /dev/null
+++ b/osu.Desktop/app.manifest
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
\ No newline at end of file
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index 453cf6f94d..01e4ada2f1 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -8,6 +8,7 @@
osu!lazer
osu!lazer
lazer.ico
+ app.manifest
0.0.0
0.0.0
diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs
index 479c250eab..f5b7d9166f 100644
--- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs
@@ -49,7 +49,11 @@ namespace osu.Game.Rulesets.Osu.Skinning
return this.GetAnimation(component.LookupName, true, false);
case OsuSkinComponents.SliderFollowCircle:
- return this.GetAnimation("sliderfollowcircle", true, true);
+ var followCircle = this.GetAnimation("sliderfollowcircle", true, true);
+ if (followCircle != null)
+ // follow circles are 2x the hitcircle resolution in legacy skins (since they are scaled down from >1x
+ followCircle.Scale *= 0.5f;
+ return followCircle;
case OsuSkinComponents.SliderBall:
var sliderBallContent = this.GetAnimation("sliderb", true, true, "");
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
index 74ae641bfe..dbea8d28a6 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
@@ -19,6 +20,7 @@ using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens;
@@ -55,6 +57,9 @@ namespace osu.Game.Tests.Visual.Gameplay
beforeLoadAction?.Invoke();
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
+ foreach (var mod in Mods.Value.OfType())
+ mod.ApplyToClock(Beatmap.Value.Track);
+
InputManager.Child = container = new TestPlayerLoaderContainer(
loader = new TestPlayerLoader(() =>
{
@@ -63,6 +68,24 @@ namespace osu.Game.Tests.Visual.Gameplay
}));
}
+ ///
+ /// When exits early, it has to wait for the player load task
+ /// to complete before running disposal on player. This previously caused an issue where mod
+ /// speed adjustments were undone too late, causing cross-screen pollution.
+ ///
+ [Test]
+ public void TestEarlyExit()
+ {
+ AddStep("load dummy beatmap", () => ResetPlayer(false, () => Mods.Value = new[] { new OsuModNightcore() }));
+ AddUntilStep("wait for current", () => loader.IsCurrentScreen());
+ AddAssert("mod rate applied", () => Beatmap.Value.Track.Rate != 1);
+ AddStep("exit loader", () => loader.Exit());
+ AddUntilStep("wait for not current", () => !loader.IsCurrentScreen());
+ AddAssert("player did not load", () => !player.IsLoaded);
+ AddUntilStep("player disposed", () => loader.DisposalTask?.IsCompleted == true);
+ AddAssert("mod rate still applied", () => Beatmap.Value.Track.Rate != 1);
+ }
+
[Test]
public void TestBlockLoadViaMouseMovement()
{
@@ -196,6 +219,8 @@ namespace osu.Game.Tests.Visual.Gameplay
{
public new VisualSettings VisualSettings => base.VisualSettings;
+ public new Task DisposalTask => base.DisposalTask;
+
public TestPlayerLoader(Func createPlayer)
: base(createPlayer)
{
diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs
index ff78d85bf0..58c9a6a784 100644
--- a/osu.Game/Screens/Play/GameplayClockContainer.cs
+++ b/osu.Game/Screens/Play/GameplayClockContainer.cs
@@ -214,10 +214,13 @@ namespace osu.Game.Screens.Play
base.Update();
}
+ private bool speedAdjustmentsApplied;
+
private void updateRate()
{
if (sourceClock == null) return;
+ speedAdjustmentsApplied = true;
sourceClock.ResetSpeedAdjustments();
if (sourceClock is IHasTempoAdjust tempo)
@@ -239,7 +242,12 @@ namespace osu.Game.Screens.Play
private void removeSourceClockAdjustments()
{
- sourceClock.ResetSpeedAdjustments();
+ if (speedAdjustmentsApplied)
+ {
+ sourceClock.ResetSpeedAdjustments();
+ speedAdjustmentsApplied = false;
+ }
+
(sourceClock as IAdjustableAudioComponent)?.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
}
}
diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs
index 87d902b547..57021dfc68 100644
--- a/osu.Game/Screens/Play/PlayerLoader.cs
+++ b/osu.Game/Screens/Play/PlayerLoader.cs
@@ -55,7 +55,9 @@ namespace osu.Game.Screens.Play
protected override bool PlayResumeSound => false;
- private Task loadTask;
+ protected Task LoadTask { get; private set; }
+
+ protected Task DisposalTask { get; private set; }
private InputManager inputManager;
private IdleTracker idleTracker;
@@ -159,7 +161,7 @@ namespace osu.Game.Screens.Play
player.RestartCount = restartCount;
player.RestartRequested = restartRequested;
- loadTask = LoadComponentAsync(player, _ => info.Loading = false);
+ LoadTask = LoadComponentAsync(player, _ => info.Loading = false);
}
private void contentIn()
@@ -250,7 +252,7 @@ namespace osu.Game.Screens.Play
{
if (!this.IsCurrentScreen()) return;
- loadTask = null;
+ LoadTask = null;
//By default, we want to load the player and never be returned to.
//Note that this may change if the player we load requested a re-run.
@@ -301,7 +303,7 @@ namespace osu.Game.Screens.Play
if (isDisposing)
{
// if the player never got pushed, we should explicitly dispose it.
- loadTask?.ContinueWith(_ => player.Dispose());
+ DisposalTask = LoadTask?.ContinueWith(_ => player.Dispose());
}
}
diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs
index 9ca5d60cb0..fda031e6cb 100644
--- a/osu.Game/Skinning/SkinnableDrawable.cs
+++ b/osu.Game/Skinning/SkinnableDrawable.cs
@@ -29,13 +29,13 @@ namespace osu.Game.Skinning
/// A function to create the default skin implementation of this element.
/// A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present.
/// How (if at all) the should be resize to fit within our own bounds.
- public SkinnableDrawable(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
+ public SkinnableDrawable(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling)
: this(component, allowFallback, confineMode)
{
createDefault = defaultImplementation;
}
- protected SkinnableDrawable(ISkinComponent component, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
+ protected SkinnableDrawable(ISkinComponent component, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling)
: base(allowFallback)
{
this.component = component;
diff --git a/osu.Game/Skinning/SkinnableSprite.cs b/osu.Game/Skinning/SkinnableSprite.cs
index e225bfc490..5352928ec6 100644
--- a/osu.Game/Skinning/SkinnableSprite.cs
+++ b/osu.Game/Skinning/SkinnableSprite.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Skinning
[Resolved]
private TextureStore textures { get; set; }
- public SkinnableSprite(string textureName, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
+ public SkinnableSprite(string textureName, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling)
: base(new SpriteComponent(textureName), allowFallback, confineMode)
{
}
diff --git a/osu.Game/Skinning/SkinnableSpriteText.cs b/osu.Game/Skinning/SkinnableSpriteText.cs
index e72f9c9811..567dd348e1 100644
--- a/osu.Game/Skinning/SkinnableSpriteText.cs
+++ b/osu.Game/Skinning/SkinnableSpriteText.cs
@@ -8,7 +8,7 @@ namespace osu.Game.Skinning
{
public class SkinnableSpriteText : SkinnableDrawable, IHasText
{
- public SkinnableSpriteText(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
+ public SkinnableSpriteText(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling)
: base(component, defaultImplementation, allowFallback, confineMode)
{
}