1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 11:20:04 +08:00

Merge remote-tracking branch 'refs/remotes/ppy/master' into user-cards-update

This commit is contained in:
Andrei Zavatski 2020-03-07 04:01:36 +03:00
commit 66f40f7f6e
51 changed files with 547 additions and 319 deletions

42
.vscode/launch.json vendored
View File

@ -11,11 +11,6 @@
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
},
{
@ -28,11 +23,6 @@
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
},
{
@ -45,11 +35,6 @@
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
},
{
@ -62,11 +47,6 @@
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
},
{
@ -80,11 +60,6 @@
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
},
{
@ -98,11 +73,6 @@
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
},
{
@ -116,11 +86,6 @@
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build tournament tests (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
},
{
@ -134,11 +99,6 @@
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build tournament tests (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
},
{
@ -169,4 +129,4 @@
"externalConsole": false
}
]
}
}

View File

@ -52,6 +52,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.304.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.302.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.305.0" />
</ItemGroup>
</Project>

View File

@ -30,11 +30,6 @@ namespace osu.Android
}
}
protected override void LoadComplete()
{
base.LoadComplete();
Add(new SimpleUpdateManager());
}
protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager();
}
}

View File

@ -47,20 +47,25 @@ namespace osu.Desktop
return null;
}
protected override UpdateManager CreateUpdateManager()
{
switch (RuntimeInfo.OS)
{
case RuntimeInfo.Platform.Windows:
return new SquirrelUpdateManager();
default:
return new SimpleUpdateManager();
}
}
protected override void LoadComplete()
{
base.LoadComplete();
if (!noVersionOverlay)
{
LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue }, Add);
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows)
Add(new SquirrelUpdateManager());
else
Add(new SimpleUpdateManager());
}
LoadComponentAsync(new DiscordRichPresence(), Add);
}

View File

@ -7,7 +7,6 @@ using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual;
using osuTK;
@ -51,7 +50,7 @@ namespace osu.Game.Rulesets.Catch.Tests
return beatmap;
}
protected override Player CreatePlayer(Ruleset ruleset)
protected override TestPlayer CreatePlayer(Ruleset ruleset)
{
SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
return base.CreatePlayer(ruleset);

View File

@ -0,0 +1,86 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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.Graphics.Containers;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests.Mods
{
public class TestSceneOsuModDifficultyAdjust : ModTestScene
{
public TestSceneOsuModDifficultyAdjust()
: base(new OsuRuleset())
{
}
[Test]
public void TestNoAdjustment() => CreateModTest(new ModTestData
{
Mod = new OsuModDifficultyAdjust(),
Autoplay = true,
PassCondition = checkSomeHit
});
[Test]
public void TestCircleSize1() => CreateModTest(new ModTestData
{
Mod = new OsuModDifficultyAdjust { CircleSize = { Value = 1 } },
Autoplay = true,
PassCondition = () => checkSomeHit() && checkObjectsScale(0.78f)
});
[Test]
public void TestCircleSize10() => CreateModTest(new ModTestData
{
Mod = new OsuModDifficultyAdjust { CircleSize = { Value = 10 } },
Autoplay = true,
PassCondition = () => checkSomeHit() && checkObjectsScale(0.15f)
});
[Test]
public void TestApproachRate1() => CreateModTest(new ModTestData
{
Mod = new OsuModDifficultyAdjust { ApproachRate = { Value = 1 } },
Autoplay = true,
PassCondition = () => checkSomeHit() && checkObjectsPreempt(1680)
});
[Test]
public void TestApproachRate10() => CreateModTest(new ModTestData
{
Mod = new OsuModDifficultyAdjust { ApproachRate = { Value = 10 } },
Autoplay = true,
PassCondition = () => checkSomeHit() && checkObjectsPreempt(450)
});
private bool checkObjectsPreempt(double target)
{
var objects = Player.ChildrenOfType<DrawableHitCircle>();
if (!objects.Any())
return false;
return objects.All(o => o.HitObject.TimePreempt == target);
}
private bool checkObjectsScale(float target)
{
var objects = Player.ChildrenOfType<DrawableHitCircle>();
if (!objects.Any())
return false;
return objects.All(o => Precision.AlmostEquals(o.ChildrenOfType<ShakeContainer>().First().Children.OfType<Container>().Single().Scale.X, target));
}
private bool checkSomeHit()
{
return Player.ScoreProcessor.JudgedHits >= 2;
}
}
}

View File

@ -0,0 +1,35 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Framework.Utils;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests.Mods
{
public class TestSceneOsuModDoubleTime : ModTestScene
{
public TestSceneOsuModDoubleTime()
: base(new OsuRuleset())
{
}
[TestCase(0.5)]
[TestCase(1.01)]
[TestCase(1.5)]
[TestCase(2)]
[TestCase(5)]
public void TestSpeedChangeCustomisation(double rate)
{
var mod = new OsuModDoubleTime { SpeedChange = { Value = rate } };
CreateModTest(new ModTestData
{
Mod = mod,
PassCondition = () => Player.ScoreProcessor.JudgedHits >= 2 &&
Precision.AlmostEquals(Player.GameplayClockContainer.GameplayClock.Rate, mod.SpeedChange.Value)
});
}
}
}

View File

@ -3,13 +3,13 @@
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests
{
public class TestSceneOsuFlashlight : TestSceneOsuPlayer
{
protected override Player CreatePlayer(Ruleset ruleset)
protected override TestPlayer CreatePlayer(Ruleset ruleset)
{
SelectedMods.Value = new Mod[] { new OsuModAutoplay(), new OsuModFlashlight(), };

View File

@ -18,7 +18,6 @@ using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Screens.Play;
using osu.Game.Skinning;
using osu.Game.Storyboards;
using osu.Game.Tests.Visual;
@ -56,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.Tests
private void checkNextHitObject(string skin) =>
AddUntilStep($"check skin from {skin}", () =>
{
var firstObject = ((TestPlayer)Player).DrawableRuleset.Playfield.HitObjectContainer.AliveObjects.OfType<DrawableHitCircle>().FirstOrDefault();
var firstObject = Player.DrawableRuleset.Playfield.HitObjectContainer.AliveObjects.OfType<DrawableHitCircle>().FirstOrDefault();
if (firstObject == null)
return false;
@ -75,7 +74,7 @@ namespace osu.Game.Rulesets.Osu.Tests
[Resolved]
private AudioManager audio { get; set; }
protected override Player CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(testUserSkin);
protected override TestPlayer CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(testUserSkin);
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) => new CustomSkinWorkingBeatmap(beatmap, storyboard, Clock, audio, testBeatmapSkin);

View File

@ -11,7 +11,6 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Tests.Visual;
using osuTK;
using System.Collections.Generic;
using System.Linq;
@ -44,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Tests
base.SetUpSteps();
AddUntilStep("wait for track to start running", () => track.IsRunning);
AddStep("retrieve spinner", () => drawableSpinner = (DrawableSpinner)((TestPlayer)Player).DrawableRuleset.Playfield.AllHitObjects.First());
AddStep("retrieve spinner", () => drawableSpinner = (DrawableSpinner)Player.DrawableRuleset.Playfield.AllHitObjects.First());
}
[Test]
@ -89,7 +88,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
AddStep($"seek to {time}", () => track.Seek(time));
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, ((TestPlayer)Player).DrawableRuleset.FrameStableClock.CurrentTime, 100));
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
}
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new Beatmap

View File

@ -1,23 +1,16 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Taiko.Tests
{
public class TestSceneSwellJudgements : PlayerTestScene
{
protected new TestPlayer Player => (TestPlayer)base.Player;
public TestSceneSwellJudgements()
: base(new TaikoRuleset())
{
@ -49,25 +42,5 @@ namespace osu.Game.Rulesets.Taiko.Tests
return beatmap;
}
protected override Player CreatePlayer(Ruleset ruleset) => new TestPlayer();
protected class TestPlayer : Player
{
public readonly List<JudgementResult> Results = new List<JudgementResult>();
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public TestPlayer()
: base(false, false)
{
}
[BackgroundDependencyLoader]
private void load()
{
ScoreProcessor.NewJudgement += r => Results.Add(r);
}
}
}
}

View File

@ -4,11 +4,9 @@
using System.Linq;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Beatmaps;
using osu.Game.Rulesets.Taiko.Mods;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Taiko.Tests
@ -22,10 +20,10 @@ namespace osu.Game.Rulesets.Taiko.Tests
protected override bool AllowFail => true;
protected override Player CreatePlayer(Ruleset ruleset)
protected override TestPlayer CreatePlayer(Ruleset ruleset)
{
SelectedMods.Value = SelectedMods.Value.Concat(new[] { new TaikoModSuddenDeath() }).ToArray();
return new ScoreAccessiblePlayer();
return base.CreatePlayer(ruleset);
}
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) =>
@ -49,20 +47,10 @@ namespace osu.Game.Rulesets.Taiko.Tests
AddStep("Setup judgements", () =>
{
judged = false;
((ScoreAccessiblePlayer)Player).ScoreProcessor.NewJudgement += b => judged = true;
Player.ScoreProcessor.NewJudgement += b => judged = true;
});
AddUntilStep("swell judged", () => judged);
AddAssert("not failed", () => !Player.HasFailed);
}
private class ScoreAccessiblePlayer : TestPlayer
{
public ScoreAccessiblePlayer()
: base(false, false)
{
}
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
}
}
}

View File

@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Background
private DummySongSelect songSelect;
private TestPlayerLoader playerLoader;
private TestPlayer player;
private LoadBlockingTestPlayer player;
private BeatmapManager manager;
private RulesetStore rulesets;
@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual.Background
public void PlayerLoaderSettingsHoverTest()
{
setupUserSettings();
AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new TestPlayer { BlockLoad = true })));
AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new LoadBlockingTestPlayer { BlockLoad = true })));
AddUntilStep("Wait for Player Loader to load", () => playerLoader?.IsLoaded ?? false);
AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent());
AddStep("Trigger background preview", () =>
@ -268,7 +268,7 @@ namespace osu.Game.Tests.Visual.Background
{
setupUserSettings();
AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new TestPlayer(allowPause))));
AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new LoadBlockingTestPlayer(allowPause))));
AddUntilStep("Wait for Player Loader to load", () => playerLoader.IsLoaded);
AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
@ -347,7 +347,7 @@ namespace osu.Game.Tests.Visual.Background
public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR);
}
private class TestPlayer : Visual.TestPlayer
private class LoadBlockingTestPlayer : TestPlayer
{
protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
@ -360,7 +360,7 @@ namespace osu.Game.Tests.Visual.Background
public readonly Bindable<bool> ReplacesBackground = new Bindable<bool>();
public readonly Bindable<bool> IsPaused = new Bindable<bool>();
public TestPlayer(bool allowPause = true)
public LoadBlockingTestPlayer(bool allowPause = true)
: base(allowPause)
{
}

View File

@ -3,53 +3,32 @@
using System.ComponentModel;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Storyboards;
namespace osu.Game.Tests.Visual.Gameplay
{
[Description("Player instantiated with an autoplay mod.")]
public class TestSceneAutoplay : TestSceneAllRulesetPlayers
{
private ClockBackedTestWorkingBeatmap.TrackVirtualManual track;
protected new TestPlayer Player => (TestPlayer)base.Player;
protected override Player CreatePlayer(Ruleset ruleset)
{
SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
return new ScoreAccessiblePlayer();
return new TestPlayer(false, false);
}
protected override void AddCheckSteps()
{
AddUntilStep("score above zero", () => ((ScoreAccessiblePlayer)Player).ScoreProcessor.TotalScore.Value > 0);
AddUntilStep("key counter counted keys", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 2));
AddStep("rewind", () => track.Seek(-10000));
AddUntilStep("key counter reset", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
}
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
{
var working = base.CreateWorkingBeatmap(beatmap, storyboard);
track = (ClockBackedTestWorkingBeatmap.TrackVirtualManual)working.Track;
return working;
}
private class ScoreAccessiblePlayer : TestPlayer
{
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public new HUDOverlay HUDOverlay => base.HUDOverlay;
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
public ScoreAccessiblePlayer()
: base(false, false)
{
}
AddUntilStep("score above zero", () => Player.ScoreProcessor.TotalScore.Value > 0);
AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 2));
AddStep("seek to break time", () => Player.GameplayClockContainer.Seek(Player.BreakOverlay.Breaks.First().StartTime));
AddUntilStep("wait for seek to complete", () =>
Player.HUDOverlay.Progress.ReferenceClock.CurrentTime >= Player.BreakOverlay.Breaks.First().StartTime);
AddAssert("test keys not counting", () => !Player.HUDOverlay.KeyCounter.IsCounting);
AddStep("rewind", () => Player.GameplayClockContainer.Seek(-80000));
AddUntilStep("key counter reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
}
}
}

View File

@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
@ -11,12 +10,8 @@ using osu.Framework.Utils;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play;
using osu.Game.Storyboards;
using osuTK;
@ -24,8 +19,6 @@ namespace osu.Game.Tests.Visual.Gameplay
{
public class TestSceneGameplayRewinding : PlayerTestScene
{
private RulesetExposingPlayer player => (RulesetExposingPlayer)Player;
[Resolved]
private AudioManager audioManager { get; set; }
@ -48,13 +41,13 @@ namespace osu.Game.Tests.Visual.Gameplay
{
AddUntilStep("wait for track to start running", () => track.IsRunning);
addSeekStep(3000);
AddAssert("all judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged));
AddUntilStep("key counter counted keys", () => player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses >= 7));
AddStep("clear results", () => player.AppliedResults.Clear());
AddAssert("all judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged));
AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses >= 7));
AddStep("clear results", () => Player.Results.Clear());
addSeekStep(0);
AddAssert("none judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged));
AddUntilStep("key counters reset", () => player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
AddAssert("no results triggered", () => player.AppliedResults.Count == 0);
AddAssert("none judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged));
AddUntilStep("key counters reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
AddAssert("no results triggered", () => Player.Results.Count == 0);
}
private void addSeekStep(double time)
@ -62,13 +55,13 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep($"seek to {time}", () => track.Seek(time));
// Allow a few frames of lenience
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
}
protected override Player CreatePlayer(Ruleset ruleset)
protected override TestPlayer CreatePlayer(Ruleset ruleset)
{
SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
return new RulesetExposingPlayer();
return base.CreatePlayer(ruleset);
}
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
@ -89,29 +82,5 @@ namespace osu.Game.Tests.Visual.Gameplay
return beatmap;
}
private class RulesetExposingPlayer : Player
{
public readonly List<JudgementResult> AppliedResults = new List<JudgementResult>();
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public new HUDOverlay HUDOverlay => base.HUDOverlay;
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
public new DrawableRuleset DrawableRuleset => base.DrawableRuleset;
public RulesetExposingPlayer()
: base(false, false)
{
}
[BackgroundDependencyLoader]
private void load()
{
ScoreProcessor.NewJudgement += r => AppliedResults.Add(r);
}
}
}
}

View File

@ -47,21 +47,22 @@ namespace osu.Game.Tests.Visual.Gameplay
Key testKey = ((KeyCounterKeyboard)kc.Children.First()).Key;
AddStep($"Press {testKey} key", () =>
void addPressKeyStep()
{
InputManager.PressKey(testKey);
InputManager.ReleaseKey(testKey);
});
AddStep($"Press {testKey} key", () =>
{
InputManager.PressKey(testKey);
InputManager.ReleaseKey(testKey);
});
}
addPressKeyStep();
AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses == 1);
AddStep($"Press {testKey} key", () =>
{
InputManager.PressKey(testKey);
InputManager.ReleaseKey(testKey);
});
addPressKeyStep();
AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses == 2);
AddStep("Disable counting", () => testCounter.IsCounting = false);
addPressKeyStep();
AddAssert($"Check {testKey} count has not changed", () => testCounter.CountPresses == 2);
Add(kc);
}

View File

@ -11,7 +11,6 @@ using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Cursor;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play;
using osuTK;
using osuTK.Input;
@ -282,14 +281,10 @@ namespace osu.Game.Tests.Visual.Gameplay
protected override bool AllowFail => true;
protected override Player CreatePlayer(Ruleset ruleset) => new PausePlayer();
protected override TestPlayer CreatePlayer(Ruleset ruleset) => new PausePlayer();
protected class PausePlayer : TestPlayer
{
public new HealthProcessor HealthProcessor => base.HealthProcessor;
public new HUDOverlay HUDOverlay => base.HUDOverlay;
public bool FailOverlayVisible => FailOverlay.State.Value == Visibility.Visible;
public bool PauseOverlayVisible => PauseOverlay.State.Value == Visibility.Visible;

View File

@ -9,15 +9,12 @@ using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual.Gameplay
{
[HeadlessTest] // we alter unsafe properties on the game host to test inactive window state.
public class TestScenePauseWhenInactive : PlayerTestScene
{
protected new TestPlayer Player => (TestPlayer)base.Player;
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{
var beatmap = (Beatmap)base.CreateBeatmap(ruleset);
@ -46,6 +43,6 @@ namespace osu.Game.Tests.Visual.Gameplay
AddAssert("time of pause is after gameplay start time", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= Player.DrawableRuleset.GameplayStartTime);
}
protected override Player CreatePlayer(Ruleset ruleset) => new TestPlayer(true, true, true);
protected override TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(true, true, true);
}
}

View File

@ -9,7 +9,6 @@ using System.Threading.Tasks;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
@ -307,17 +306,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
}
private class TestPlayer : Visual.TestPlayer
{
public new Bindable<IReadOnlyList<Mod>> Mods => base.Mods;
public TestPlayer(bool allowPause = true, bool showResults = true)
: base(allowPause, showResults)
{
}
}
protected class SlowLoadPlayer : Visual.TestPlayer
protected class SlowLoadPlayer : TestPlayer
{
public readonly ManualResetEventSlim AllowLoad = new ManualResetEventSlim(false);

View File

@ -62,14 +62,7 @@ namespace osu.Game.Tests.Visual.Navigation
var frameworkConfig = host.Dependencies.Get<FrameworkConfigManager>();
frameworkConfig.GetBindable<double>(FrameworkSetting.CursorSensitivity).Disabled = false;
Game = new TestOsuGame(LocalStorage, API);
Game.SetHost(host);
// 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);
Add(Game);
CreateGame();
});
AddUntilStep("Wait for load", () => Game.IsLoaded);
@ -78,6 +71,18 @@ namespace osu.Game.Tests.Visual.Navigation
ConfirmAtMainMenu();
}
protected void CreateGame()
{
Game = new TestOsuGame(LocalStorage, API);
Game.SetHost(host);
// 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);
Add(Game);
}
protected void PushAndConfirm(Func<Screen> newScreen)
{
Screen screen = null;
@ -97,12 +102,17 @@ namespace osu.Game.Tests.Visual.Navigation
public new SettingsPanel Settings => base.Settings;
public new MusicController MusicController => base.MusicController;
public new OsuConfigManager LocalConfig => base.LocalConfig;
public new Bindable<WorkingBeatmap> Beatmap => base.Beatmap;
public new Bindable<RulesetInfo> Ruleset => base.Ruleset;
// if we don't do this, when running under nUnit the version that gets populated is that of nUnit.
public override string Version => "test game";
protected override Loader CreateLoader() => new TestLoader();
public new void PerformFromScreen(Action<IScreen> action, IEnumerable<Type> validScreens = null) => base.PerformFromScreen(action, validScreens);

View File

@ -114,6 +114,22 @@ namespace osu.Game.Tests.Visual.Navigation
AddAssert("Options overlay was closed", () => Game.Settings.State.Value == Visibility.Hidden);
}
[Test]
public void TestWaitForNextTrackInMenu()
{
bool trackCompleted = false;
AddUntilStep("Wait for music controller", () => Game.MusicController.IsLoaded);
AddStep("Seek close to end", () =>
{
Game.MusicController.SeekTo(Game.Beatmap.Value.Track.Length - 1000);
Game.Beatmap.Value.Track.Completed += () => trackCompleted = true;
});
AddUntilStep("Track was completed", () => trackCompleted);
AddUntilStep("Track was restarted", () => Game.Beatmap.Value.Track.IsRunning);
}
private void pushEscape() =>
AddStep("Press escape", () => pressAndRelease(Key.Escape));

View File

@ -0,0 +1,41 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Framework.Utils;
using osu.Game.Configuration;
namespace osu.Game.Tests.Visual.Navigation
{
public class TestSettingsMigration : OsuGameTestScene
{
public override void RecycleLocalStorage()
{
base.RecycleLocalStorage();
using (var config = new OsuConfigManager(LocalStorage))
{
config.Set(OsuSetting.Version, "2020.101.0");
config.Set(OsuSetting.DisplayStarsMaximum, 10.0);
}
}
[Test]
public void TestDisplayStarsMigration()
{
AddAssert("config has migrated value", () => Precision.AlmostEquals(Game.LocalConfig.Get<double>(OsuSetting.DisplayStarsMaximum), 10.1));
AddStep("set value again", () => Game.LocalConfig.Set<double>(OsuSetting.DisplayStarsMaximum, 10));
AddStep("force save config", () => Game.LocalConfig.Save());
AddStep("remove game", () => Remove(Game));
AddStep("create game again", CreateGame);
AddUntilStep("Wait for load", () => Game.IsLoaded);
AddAssert("config did not migrate value", () => Precision.AlmostEquals(Game.LocalConfig.Get<double>(OsuSetting.DisplayStarsMaximum), 10));
}
}
}

View File

@ -1,12 +1,13 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.IO;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Video;
using osu.Framework.Platform;
using osu.Framework.Timing;
using osu.Game.Graphics;
@ -14,21 +15,24 @@ namespace osu.Game.Tournament.Components
{
public class TourneyVideo : CompositeDrawable
{
private readonly VideoSprite video;
private readonly string filename;
private readonly bool drawFallbackGradient;
private VideoSprite video;
private readonly ManualClock manualClock;
private ManualClock manualClock;
public TourneyVideo(Stream stream)
public TourneyVideo(string filename, bool drawFallbackGradient = false)
{
if (stream == null)
{
InternalChild = new Box
{
Colour = ColourInfo.GradientVertical(OsuColour.Gray(0.3f), OsuColour.Gray(0.6f)),
RelativeSizeAxes = Axes.Both,
};
}
else
this.filename = filename;
this.drawFallbackGradient = drawFallbackGradient;
}
[BackgroundDependencyLoader]
private void load(Storage storage)
{
var stream = storage.GetStream($@"videos/{filename}.m4v");
if (stream != null)
{
InternalChild = video = new VideoSprite(stream)
{
@ -37,6 +41,14 @@ namespace osu.Game.Tournament.Components
Clock = new FramedClock(manualClock = new ManualClock())
};
}
else if (drawFallbackGradient)
{
InternalChild = new Box
{
Colour = ColourInfo.GradientVertical(OsuColour.Gray(0.3f), OsuColour.Gray(0.6f)),
RelativeSizeAxes = Axes.Both,
};
}
}
public bool Loop

View File

@ -129,8 +129,6 @@ namespace osu.Game.Tournament.Screens.Editors
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
LayoutDuration = 200,
LayoutEasing = Easing.OutQuint,
ChildrenEnumerable = round.Beatmaps.Select(p => new RoundBeatmapRow(round, p))
};
}

View File

@ -124,8 +124,6 @@ namespace osu.Game.Tournament.Screens.Editors
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
LayoutDuration = 200,
LayoutEasing = Easing.OutQuint,
ChildrenEnumerable = round.Beatmaps.Select(p => new SeedingBeatmapRow(round, p))
};
}

View File

@ -202,8 +202,6 @@ namespace osu.Game.Tournament.Screens.Editors
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
LayoutDuration = 200,
LayoutEasing = Easing.OutQuint,
ChildrenEnumerable = team.Players.Select(p => new PlayerRow(team, p))
};
}

View File

@ -48,8 +48,6 @@ namespace osu.Game.Tournament.Screens.Editors
Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
LayoutDuration = 200,
LayoutEasing = Easing.OutQuint,
Spacing = new Vector2(20)
},
},

View File

@ -6,6 +6,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Platform;
using osu.Framework.Threading;
using osu.Game.Graphics.UserInterface;
using osu.Game.Tournament.Components;
@ -19,7 +20,7 @@ using osuTK.Graphics;
namespace osu.Game.Tournament.Screens.Gameplay
{
public class GameplayScreen : BeatmapInfoScreen
public class GameplayScreen : BeatmapInfoScreen, IProvideVideo
{
private readonly BindableBool warmup = new BindableBool();
@ -39,12 +40,17 @@ namespace osu.Game.Tournament.Screens.Gameplay
private TournamentMatchChatDisplay chat { get; set; }
[BackgroundDependencyLoader]
private void load(LadderInfo ladder, MatchIPCInfo ipc)
private void load(LadderInfo ladder, MatchIPCInfo ipc, Storage storage)
{
this.ipc = ipc;
AddRangeInternal(new Drawable[]
{
new TourneyVideo("gameplay")
{
Loop = true,
RelativeSizeAxes = Axes.Both,
},
new MatchHeader(),
new Container
{

View File

@ -42,7 +42,7 @@ namespace osu.Game.Tournament.Screens.Ladder
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new TourneyVideo(storage.GetStream(@"videos/ladder.m4v"))
new TourneyVideo("ladder")
{
RelativeSizeAxes = Axes.Both,
Loop = true,

View File

@ -18,7 +18,7 @@ using osuTK.Graphics;
namespace osu.Game.Tournament.Screens.Schedule
{
public class ScheduleScreen : TournamentScreen, IProvideVideo
public class ScheduleScreen : TournamentScreen
{
private readonly Bindable<TournamentMatch> currentMatch = new Bindable<TournamentMatch>();
private Container mainContainer;
@ -33,7 +33,7 @@ namespace osu.Game.Tournament.Screens.Schedule
InternalChildren = new Drawable[]
{
new TourneyVideo(storage.GetStream(@"videos/schedule.m4v"))
new TourneyVideo("schedule")
{
RelativeSizeAxes = Axes.Both,
Loop = true,

View File

@ -34,7 +34,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
InternalChildren = new Drawable[]
{
new TourneyVideo(storage.GetStream(@"videos/seeding.m4v"))
new TourneyVideo("seeding")
{
RelativeSizeAxes = Axes.Both,
Loop = true,

View File

@ -26,7 +26,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
InternalChildren = new Drawable[]
{
new TourneyVideo(storage.GetStream(@"videos/teamintro.m4v"))
new TourneyVideo("teamintro")
{
RelativeSizeAxes = Axes.Both,
Loop = true,

View File

@ -31,13 +31,13 @@ namespace osu.Game.Tournament.Screens.TeamWin
InternalChildren = new Drawable[]
{
blueWinVideo = new TourneyVideo(storage.GetStream(@"videos/teamwin-blue.m4v"))
blueWinVideo = new TourneyVideo("teamwin-blue")
{
Alpha = 1,
RelativeSizeAxes = Axes.Both,
Loop = true,
},
redWinVideo = new TourneyVideo(storage.GetStream(@"videos/teamwin-red.m4v"))
redWinVideo = new TourneyVideo("teamwin-red")
{
Alpha = 0,
RelativeSizeAxes = Axes.Both,

View File

@ -18,6 +18,9 @@ namespace osu.Game.Tournament.Screens
protected TournamentScreen()
{
RelativeSizeAxes = Axes.Both;
FillMode = FillMode.Fit;
FillAspectRatio = 16 / 9f;
}
public override void Hide() => this.FadeOut(FADE_DELAY);

View File

@ -61,7 +61,7 @@ namespace osu.Game.Tournament
//Masking = true,
Children = new Drawable[]
{
video = new TourneyVideo(storage.GetStream("videos/main.m4v"))
video = new TourneyVideo("main", true)
{
Loop = true,
RelativeSizeAxes = Axes.Both,

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Framework.Configuration;
using osu.Framework.Configuration.Tracking;
using osu.Framework.Extensions;
@ -126,6 +127,35 @@ namespace osu.Game.Configuration
public OsuConfigManager(Storage storage)
: base(storage)
{
Migrate();
}
public void Migrate()
{
// arrives as 2020.123.0
var rawVersion = Get<string>(OsuSetting.Version);
if (rawVersion.Length < 6)
return;
var pieces = rawVersion.Split('.');
// on a fresh install or when coming from a non-release build, execution will end here.
// we don't want to run migrations in such cases.
if (!int.TryParse(pieces[0], out int year)) return;
if (!int.TryParse(pieces[1], out int monthDay)) return;
int combined = (year * 10000) + monthDay;
if (combined < 20200305)
{
// the maximum value of this setting was changed.
// if we don't manually increase this, it causes song select to filter out beatmaps the user expects to see.
var maxStars = (BindableDouble)GetOriginalBindable<double>(OsuSetting.DisplayStarsMaximum);
if (maxStars.Value == 10)
maxStars.Value = maxStars.MaxValue;
}
}
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings

View File

@ -43,6 +43,7 @@ using osu.Game.Overlays.Volume;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
using osu.Game.Screens.Select;
using osu.Game.Updater;
using osu.Game.Utils;
using LogLevel = osu.Framework.Logging.LogLevel;
@ -390,24 +391,35 @@ namespace osu.Game
protected virtual Loader CreateLoader() => new Loader();
protected virtual UpdateManager CreateUpdateManager() => new UpdateManager();
protected override Container CreateScalingContainer() => new ScalingContainer(ScalingMode.Everything);
#region Beatmap progression
private void beatmapChanged(ValueChangedEvent<WorkingBeatmap> beatmap)
{
var nextBeatmap = beatmap.NewValue;
if (nextBeatmap?.Track != null)
nextBeatmap.Track.Completed += currentTrackCompleted;
var oldBeatmap = beatmap.OldValue;
if (oldBeatmap?.Track != null)
oldBeatmap.Track.Completed -= currentTrackCompleted;
beatmap.OldValue?.CancelAsyncLoad();
updateModDefaults();
oldBeatmap?.CancelAsyncLoad();
nextBeatmap?.BeginAsyncLoad();
var newBeatmap = beatmap.NewValue;
if (newBeatmap != null)
{
newBeatmap.Track.Completed += () => Scheduler.AddOnce(() => trackCompleted(newBeatmap));
newBeatmap.BeginAsyncLoad();
}
void trackCompleted(WorkingBeatmap b)
{
// the source of track completion is the audio thread, so the beatmap may have changed before firing.
if (Beatmap.Value != b)
return;
if (!Beatmap.Value.Track.Looping && !Beatmap.Disabled)
MusicController.NextTrack();
}
}
private void modsChanged(ValueChangedEvent<IReadOnlyList<Mod>> mods)
@ -428,12 +440,6 @@ namespace osu.Game
}
}
private void currentTrackCompleted() => Schedule(() =>
{
if (!Beatmap.Value.Track.Looping && !Beatmap.Disabled)
musicController.NextTrack();
});
#endregion
private ScheduledDelegate performFromMainMenuTask;
@ -585,7 +591,7 @@ namespace osu.Game
loadComponentSingleFile(new OnScreenDisplay(), Add, true);
loadComponentSingleFile(musicController = new MusicController(), Add, true);
loadComponentSingleFile(MusicController = new MusicController(), Add, true);
loadComponentSingleFile(notifications = new NotificationOverlay
{
@ -628,6 +634,7 @@ namespace osu.Game
chatOverlay.State.ValueChanged += state => channelManager.HighPollRate.Value = state.NewValue == Visibility.Visible;
Add(externalLinkOpener = new ExternalLinkOpener());
Add(CreateUpdateManager()); // dependency on notification overlay
// side overlays which cancel each other.
var singleDisplaySideOverlays = new OverlayContainer[] { Settings, notifications };
@ -893,7 +900,7 @@ namespace osu.Game
private ScalingContainer screenContainer;
private MusicController musicController;
protected MusicController MusicController { get; private set; }
protected override bool OnExiting()
{
@ -951,7 +958,7 @@ namespace osu.Game
{
OverlayActivationMode.Value = newOsuScreen.InitialOverlayActivationMode;
musicController.AllowRateAdjustments = newOsuScreen.AllowRateAdjustments;
MusicController.AllowRateAdjustments = newOsuScreen.AllowRateAdjustments;
if (newOsuScreen.HideOverlaysOnEnter)
CloseAllOverlays();

View File

@ -97,7 +97,7 @@ namespace osu.Game
public bool IsDeployedBuild => AssemblyVersion.Major > 0;
public string Version
public virtual string Version
{
get
{
@ -211,6 +211,10 @@ namespace osu.Game
Audio.Tracks.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8));
Beatmap = new NonNullableBindable<WorkingBeatmap>(defaultBeatmap);
// ScheduleAfterChildren is safety against something in the current frame accessing the previous beatmap's track
// and potentially causing a reload of it after just unloading.
// Note that the reason for this being added *has* been resolved, so it may be feasible to removed this if required.
Beatmap.BindValueChanged(b => ScheduleAfterChildren(() =>
{
// compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo)

View File

@ -15,7 +15,7 @@ namespace osu.Game.Overlays.SearchableList
{
public abstract class SearchableListOverlay : FullscreenOverlay
{
public const float WIDTH_PADDING = 10;
public const float WIDTH_PADDING = 80;
protected SearchableListOverlay(OverlayColourScheme colourScheme)
: base(colourScheme)
@ -80,7 +80,7 @@ namespace osu.Game.Overlays.SearchableList
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Horizontal = WIDTH_PADDING, Bottom = 50 },
Padding = new MarginPadding { Horizontal = 10, Bottom = 50 },
Direction = FillDirection.Vertical,
},
},

View File

@ -184,7 +184,7 @@ namespace osu.Game.Screens.Play
foreach (var mod in Mods.Value.OfType<IApplicableToHealthProcessor>())
mod.ApplyToHealthProcessor(HealthProcessor);
BreakOverlay.IsBreakTime.ValueChanged += _ => updatePauseOnFocusLostState();
BreakOverlay.IsBreakTime.BindValueChanged(onBreakTimeChanged, true);
}
private void addUnderlayComponents(Container target)
@ -229,7 +229,11 @@ namespace osu.Game.Screens.Play
IsPaused = { BindTarget = GameplayClockContainer.IsPaused }
},
PlayerSettingsOverlay = { PlaybackSettings = { UserPlaybackRate = { BindTarget = GameplayClockContainer.UserPlaybackRate } } },
KeyCounter = { AlwaysVisible = { BindTarget = DrawableRuleset.HasReplayLoaded } },
KeyCounter =
{
AlwaysVisible = { BindTarget = DrawableRuleset.HasReplayLoaded },
IsCounting = false
},
RequestSeek = GameplayClockContainer.Seek,
Anchor = Anchor.Centre,
Origin = Anchor.Centre
@ -286,6 +290,12 @@ namespace osu.Game.Screens.Play
HealthProcessor.IsBreakTime.BindTo(BreakOverlay.IsBreakTime);
}
private void onBreakTimeChanged(ValueChangedEvent<bool> isBreakTime)
{
updatePauseOnFocusLostState();
HUDOverlay.KeyCounter.IsCounting = !isBreakTime.NewValue;
}
private void updatePauseOnFocusLostState() =>
HUDOverlay.HoldToQuit.PauseOnFocusLost = PauseOnFocusLost
&& !DrawableRuleset.HasReplayLoaded.Value

View File

@ -116,7 +116,7 @@ namespace osu.Game.Screens.Play
{
base.Update();
var progress = Math.Max(0, 1 - (gameplayClock.CurrentTime - displayTime) / (fadeOutBeginTime - displayTime));
var progress = fadeOutBeginTime <= displayTime ? 1 : Math.Max(0, 1 - (gameplayClock.CurrentTime - displayTime) / (fadeOutBeginTime - displayTime));
remainingTimeBox.Width = (float)Interpolation.Lerp(remainingTimeBox.Width, progress, Math.Clamp(Time.Elapsed / 40, 0, 1));

View File

@ -0,0 +1,99 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Tests.Visual
{
public abstract class ModTestScene : PlayerTestScene
{
protected sealed override bool HasCustomSteps => true;
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(ModTestScene)
};
protected ModTestScene(Ruleset ruleset)
: base(ruleset)
{
}
private ModTestData currentTestData;
protected void CreateModTest(ModTestData testData) => CreateTest(() =>
{
AddStep("set test data", () => currentTestData = testData);
});
public override void TearDownSteps()
{
AddUntilStep("test passed", () =>
{
if (currentTestData == null)
return true;
return currentTestData.PassCondition?.Invoke() ?? false;
});
base.TearDownSteps();
}
protected sealed override IBeatmap CreateBeatmap(RulesetInfo ruleset) => currentTestData?.Beatmap ?? base.CreateBeatmap(ruleset);
protected sealed override TestPlayer CreatePlayer(Ruleset ruleset)
{
var mods = new List<Mod>(SelectedMods.Value);
if (currentTestData.Mod != null)
mods.Add(currentTestData.Mod);
if (currentTestData.Autoplay)
mods.Add(ruleset.GetAutoplayMod());
SelectedMods.Value = mods;
return new ModTestPlayer(AllowFail);
}
protected class ModTestPlayer : TestPlayer
{
protected override bool AllowFail { get; }
public ModTestPlayer(bool allowFail)
: base(false, false)
{
AllowFail = allowFail;
}
}
protected class ModTestData
{
/// <summary>
/// Whether to use a replay to simulate an auto-play. True by default.
/// </summary>
public bool Autoplay = true;
/// <summary>
/// The beatmap for this test case.
/// </summary>
[CanBeNull]
public IBeatmap Beatmap;
/// <summary>
/// The conditions that cause this test case to pass.
/// </summary>
[CanBeNull]
public Func<bool> PassCondition;
/// <summary>
/// The <see cref="Mod"/> this test case tests.
/// </summary>
public Mod Mod;
}
}
}

View File

@ -104,7 +104,7 @@ namespace osu.Game.Tests.Visual
base.Content.Add(content = new DrawSizePreservingFillContainer());
}
public void RecycleLocalStorage()
public virtual void RecycleLocalStorage()
{
if (localStorage?.IsValueCreated == true)
{
@ -231,15 +231,8 @@ namespace osu.Game.Tests.Visual
{
private readonly IFrameBasedClock referenceClock;
private readonly ManualClock clock = new ManualClock();
private bool running;
/// <summary>
/// Local offset added to the reference clock to resolve correct time.
/// </summary>
private double offset;
public TrackVirtualManual(IFrameBasedClock referenceClock)
{
this.referenceClock = referenceClock;
@ -248,10 +241,10 @@ namespace osu.Game.Tests.Visual
public override bool Seek(double seek)
{
offset = Math.Clamp(seek, 0, Length);
accumulated = Math.Clamp(seek, 0, Length);
lastReferenceTime = null;
return offset == seek;
return accumulated == seek;
}
public override void Start()
@ -270,9 +263,6 @@ namespace osu.Game.Tests.Visual
if (running)
{
running = false;
// on stopping, the current value should be transferred out of the clock, as we can no longer rely on
// the referenceClock (which will still be counting time).
offset = clock.CurrentTime;
lastReferenceTime = null;
}
}
@ -281,7 +271,9 @@ namespace osu.Game.Tests.Visual
private double? lastReferenceTime;
public override double CurrentTime => clock.CurrentTime;
private double accumulated;
public override double CurrentTime => Math.Min(accumulated, Length);
protected override void UpdateState()
{
@ -291,18 +283,12 @@ namespace osu.Game.Tests.Visual
{
double refTime = referenceClock.CurrentTime;
if (!lastReferenceTime.HasValue)
{
// if the clock just started running, the current value should be transferred to the offset
// (to zero the progression of time).
offset -= refTime;
}
if (lastReferenceTime.HasValue)
accumulated += (refTime - lastReferenceTime.Value) * Rate;
lastReferenceTime = refTime;
}
clock.CurrentTime = Math.Min((lastReferenceTime ?? 0) + offset, Length);
if (CurrentTime >= Length)
{
Stop();

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
@ -8,15 +9,19 @@ using osu.Framework.Testing;
using osu.Game.Configuration;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
{
public abstract class PlayerTestScene : RateAdjustedBeatmapTestScene
{
/// <summary>
/// Whether custom test steps are provided. Custom tests should invoke <see cref="CreateTest"/> to create the test steps.
/// </summary>
protected virtual bool HasCustomSteps { get; } = false;
private readonly Ruleset ruleset;
protected Player Player;
protected TestPlayer Player;
protected PlayerTestScene(Ruleset ruleset)
{
@ -37,7 +42,18 @@ namespace osu.Game.Tests.Visual
{
base.SetUpSteps();
AddStep(ruleset.RulesetInfo.Name, loadPlayer);
if (!HasCustomSteps)
CreateTest(null);
}
protected void CreateTest(Action action)
{
if (action != null && !HasCustomSteps)
throw new InvalidOperationException($"Cannot add custom test steps without {nameof(HasCustomSteps)} being set.");
action?.Invoke();
AddStep(ruleset.RulesetInfo.Name, LoadPlayer);
AddUntilStep("player loaded", () => Player.IsLoaded && Player.Alpha == 1);
}
@ -45,12 +61,14 @@ namespace osu.Game.Tests.Visual
protected virtual bool Autoplay => false;
private void loadPlayer()
protected void LoadPlayer()
{
var beatmap = CreateBeatmap(ruleset.RulesetInfo);
Beatmap.Value = CreateWorkingBeatmap(beatmap);
SelectedMods.Value = Array.Empty<Mod>();
if (!AllowFail)
{
var noFailMod = ruleset.GetAllMods().FirstOrDefault(m => m is ModNoFail);
@ -69,6 +87,6 @@ namespace osu.Game.Tests.Visual
LoadScreen(Player);
}
protected virtual Player CreatePlayer(Ruleset ruleset) => new TestPlayer(false, false);
protected virtual TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(false, false);
}
}

View File

@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual
public virtual void SetUpSteps() => addExitAllScreensStep();
[TearDownSteps]
public void TearDownSteps() => addExitAllScreensStep();
public virtual void TearDownSteps() => addExitAllScreensStep();
private void addExitAllScreensStep()
{

View File

@ -1,23 +1,51 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
{
/// <summary>
/// A player that exposes many components that would otherwise not be available, for testing purposes.
/// </summary>
public class TestPlayer : Player
{
protected override bool PauseOnFocusLost { get; }
public new DrawableRuleset DrawableRuleset => base.DrawableRuleset;
/// <summary>
/// Mods from *player* (not OsuScreen).
/// </summary>
public new Bindable<IReadOnlyList<Mod>> Mods => base.Mods;
public new HUDOverlay HUDOverlay => base.HUDOverlay;
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public new HealthProcessor HealthProcessor => base.HealthProcessor;
public readonly List<JudgementResult> Results = new List<JudgementResult>();
public TestPlayer(bool allowPause = true, bool showResults = true, bool pauseOnFocusLost = false)
: base(allowPause, showResults)
{
PauseOnFocusLost = pauseOnFocusLost;
}
[BackgroundDependencyLoader]
private void load()
{
ScoreProcessor.NewJudgement += r => Results.Add(r);
}
}
}

View File

@ -34,12 +34,14 @@ namespace osu.Game.Updater
if (game.IsDeployedBuild && version != lastVersion)
{
config.Set(OsuSetting.Version, version);
// only show a notification if we've previously saved a version to the config file (ie. not the first run).
if (!string.IsNullOrEmpty(lastVersion))
Notifications.Post(new UpdateCompleteNotification(version));
}
// 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);
}
private class UpdateCompleteNotification : SimpleNotification

View File

@ -43,6 +43,8 @@ namespace osu.Game.Users.Drawables
set => base.EdgeEffect = value;
}
protected override double LoadDelay => 200;
/// <summary>
/// Whether to show a default guest representation on null user (as opposed to nothing).
/// </summary>

View File

@ -23,7 +23,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.304.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.302.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.305.0" />
<PackageReference Include="Sentry" Version="2.1.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" />

View File

@ -71,7 +71,7 @@
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.304.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.302.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.305.0" />
</ItemGroup>
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
<ItemGroup Label="Transitive Dependencies">
@ -79,7 +79,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Framework" Version="2020.302.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.305.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" />

View File

@ -11,12 +11,5 @@ namespace osu.iOS
public class OsuGameIOS : OsuGame
{
public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString());
protected override void LoadComplete()
{
base.LoadComplete();
Add(new UpdateManager());
}
}
}