1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-12 21:43:22 +08:00

Merge remote-tracking branch 'upstream/master' into multi-background-stack

This commit is contained in:
Dean Herbert 2019-03-22 16:05:39 +09:00
commit db89796752
23 changed files with 517 additions and 436 deletions

View File

@ -27,8 +27,8 @@
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="System.IO.Packaging" Version="4.5.0" /> <PackageReference Include="System.IO.Packaging" Version="4.5.0" />
<PackageReference Include="ppy.squirrel.windows" Version="1.9.0.3" /> <PackageReference Include="ppy.squirrel.windows" Version="1.9.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.3" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Resources"> <ItemGroup Label="Resources">
<EmbeddedResource Include="lazer.ico" /> <EmbeddedResource Include="lazer.ico" />

View File

@ -2,9 +2,9 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.12.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">

View File

@ -2,9 +2,9 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.12.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">

View File

@ -2,9 +2,9 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.12.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics.Cursor;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
@ -19,10 +20,7 @@ namespace osu.Game.Rulesets.Osu.Edit
private class OsuPlayfieldNoCursor : OsuPlayfield private class OsuPlayfieldNoCursor : OsuPlayfield
{ {
public OsuPlayfieldNoCursor() protected override CursorContainer CreateCursor() => null;
{
Cursor?.Expire();
}
} }
} }
} }

View File

@ -2,9 +2,9 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.12.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">

View File

@ -3,9 +3,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Online.API;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.AccountCreation; using osu.Game.Overlays.AccountCreation;
using osu.Game.Users;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
@ -21,12 +25,32 @@ namespace osu.Game.Tests.Visual
typeof(AccountCreationScreen), typeof(AccountCreationScreen),
}; };
[Cached(typeof(IAPIProvider))]
private DummyAPIAccess api = new DummyAPIAccess();
public TestCaseAccountCreationOverlay() public TestCaseAccountCreationOverlay()
{ {
var accountCreation = new AccountCreationOverlay(); Container userPanelArea;
Child = accountCreation; AccountCreationOverlay accountCreation;
accountCreation.State = Visibility.Visible; Children = new Drawable[]
{
api,
accountCreation = new AccountCreationOverlay(),
userPanelArea = new Container
{
Padding = new MarginPadding(10),
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
},
};
api.Logout();
api.LocalUser.BindValueChanged(user => { userPanelArea.Child = new UserPanel(user.NewValue) { Width = 200 }; }, true);
AddStep("show", () => accountCreation.State = Visibility.Visible);
AddStep("logout", () => api.Logout());
} }
} }
} }

View File

@ -183,10 +183,10 @@ namespace osu.Game.Tests.Visual
public void PauseTest() public void PauseTest()
{ {
performFullSetup(true); performFullSetup(true);
AddStep("Pause", () => player.CurrentPausableGameplayContainer.Pause()); AddStep("Pause", () => player.Pause());
waitForDim(); waitForDim();
AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
AddStep("Unpause", () => player.CurrentPausableGameplayContainer.Resume()); AddStep("Unpause", () => player.Resume());
waitForDim(); waitForDim();
AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
} }
@ -348,8 +348,6 @@ namespace osu.Game.Tests.Visual
}; };
} }
public PausableGameplayContainer CurrentPausableGameplayContainer => PausableGameplayContainer;
public UserDimContainer CurrentStoryboardContainer => StoryboardContainer; public UserDimContainer CurrentStoryboardContainer => StoryboardContainer;
// Whether or not the player should be allowed to load. // Whether or not the player should be allowed to load.

View File

@ -16,6 +16,8 @@ namespace osu.Game.Tests.Visual
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
Add(api);
AddStep("load disclaimer", () => LoadScreen(new Disclaimer())); AddStep("load disclaimer", () => LoadScreen(new Disclaimer()));
AddStep("toggle support", () => AddStep("toggle support", () =>

View File

@ -17,15 +17,15 @@ namespace osu.Game.Tests.Visual
[Description("player pause/fail screens")] [Description("player pause/fail screens")]
public class TestCaseGameplayMenuOverlay : ManualInputManagerTestCase public class TestCaseGameplayMenuOverlay : ManualInputManagerTestCase
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(FailOverlay), typeof(PausableGameplayContainer) }; public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseOverlay) };
private FailOverlay failOverlay; private FailOverlay failOverlay;
private PausableGameplayContainer.PauseOverlay pauseOverlay; private PauseOverlay pauseOverlay;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
Add(pauseOverlay = new PausableGameplayContainer.PauseOverlay Add(pauseOverlay = new PauseOverlay
{ {
OnResume = () => Logger.Log(@"Resume"), OnResume = () => Logger.Log(@"Resume"),
OnRetry = () => Logger.Log(@"Retry"), OnRetry = () => Logger.Log(@"Retry"),

View File

@ -0,0 +1,152 @@
// 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.Graphics.Containers;
using osu.Framework.Screens;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
{
public class TestCasePause : PlayerTestCase
{
protected new PausePlayer Player => (PausePlayer)base.Player;
public TestCasePause()
: base(new OsuRuleset())
{
}
[Test]
public void TestPauseResume()
{
pauseAndConfirm();
resumeAndConfirm();
}
[Test]
public void TestPauseTooSoon()
{
pauseAndConfirm();
resumeAndConfirm();
pause();
confirmClockRunning(true);
confirmPauseOverlayShown(false);
}
[Test]
public void TestExitTooSoon()
{
pauseAndConfirm();
resume();
AddStep("exit too soon", () => Player.Exit());
confirmClockRunning(true);
confirmPauseOverlayShown(false);
AddAssert("not exited", () => Player.IsCurrentScreen());
}
[Test]
public void TestPauseAfterFail()
{
AddUntilStep("wait for fail", () => Player.HasFailed);
AddAssert("fail overlay shown", () => Player.FailOverlayVisible);
confirmClockRunning(false);
pause();
confirmClockRunning(false);
confirmPauseOverlayShown(false);
AddAssert("fail overlay still shown", () => Player.FailOverlayVisible);
exitAndConfirm();
}
[Test]
public void TestExitFromGameplay()
{
AddStep("exit", () => Player.Exit());
confirmPaused();
exitAndConfirm();
}
[Test]
public void TestExitFromPause()
{
pauseAndConfirm();
exitAndConfirm();
}
private void pauseAndConfirm()
{
pause();
confirmPaused();
}
private void resumeAndConfirm()
{
resume();
confirmResumed();
}
private void exitAndConfirm()
{
AddUntilStep("player not exited", () => Player.IsCurrentScreen());
AddStep("exit", () => Player.Exit());
confirmExited();
}
private void confirmPaused()
{
confirmClockRunning(false);
AddAssert("pause overlay shown", () => Player.PauseOverlayVisible);
}
private void confirmResumed()
{
confirmClockRunning(true);
confirmPauseOverlayShown(false);
}
private void confirmExited()
{
AddUntilStep("player exited", () => !Player.IsCurrentScreen());
}
private void pause() => AddStep("pause", () => Player.Pause());
private void resume() => AddStep("resume", () => Player.Resume());
private void confirmPauseOverlayShown(bool isShown) =>
AddAssert("pause overlay " + (isShown ? "shown" : "hidden"), () => Player.PauseOverlayVisible == isShown);
private void confirmClockRunning(bool isRunning) =>
AddAssert("clock " + (isRunning ? "running" : "stopped"), () => Player.GameplayClockContainer.GameplayClock.IsRunning == isRunning);
protected override bool AllowFail => true;
protected override Player CreatePlayer(Ruleset ruleset) => new PausePlayer();
protected class PausePlayer : Player
{
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public bool FailOverlayVisible => FailOverlay.State == Visibility.Visible;
public bool PauseOverlayVisible => PauseOverlay.State == Visibility.Visible;
}
}
}

View File

@ -3,9 +3,9 @@
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="DeepEqual" Version="2.0.0" /> <PackageReference Include="DeepEqual" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.12.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">

View File

@ -266,22 +266,20 @@ namespace osu.Game.Online.API
get => state; get => state;
private set private set
{ {
APIState oldState = state; if (state == value)
APIState newState = value; return;
APIState oldState = state;
state = value; state = value;
if (oldState != newState) log.Add($@"We just went {state}!");
{
log.Add($@"We just went {newState}!");
Scheduler.Add(delegate Scheduler.Add(delegate
{ {
components.ForEach(c => c.APIStateChanged(this, newState)); components.ForEach(c => c.APIStateChanged(this, state));
OnStateChange?.Invoke(oldState, newState); OnStateChange?.Invoke(oldState, state);
}); });
} }
} }
}
private bool handleWebException(WebException we) private bool handleWebException(WebException we)
{ {

View File

@ -1,12 +1,15 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Threading;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Online.API namespace osu.Game.Online.API
{ {
public class DummyAPIAccess : IAPIProvider public class DummyAPIAccess : Component, IAPIProvider
{ {
public Bindable<User> LocalUser { get; } = new Bindable<User>(new User public Bindable<User> LocalUser { get; } = new Bindable<User>(new User
{ {
@ -20,7 +23,23 @@ namespace osu.Game.Online.API
public string Endpoint => "http://localhost"; public string Endpoint => "http://localhost";
public APIState State => LocalUser.Value.Id == 1 ? APIState.Offline : APIState.Online; private APIState state = APIState.Online;
private readonly List<IOnlineComponent> components = new List<IOnlineComponent>();
public APIState State
{
get => state;
private set
{
if (state == value)
return;
state = value;
Scheduler.Add(() => components.ForEach(c => c.APIStateChanged(this, value)));
}
}
public virtual void Queue(APIRequest request) public virtual void Queue(APIRequest request)
{ {
@ -28,28 +47,36 @@ namespace osu.Game.Online.API
public void Register(IOnlineComponent component) public void Register(IOnlineComponent component)
{ {
// todo: add support Scheduler.Add(delegate { components.Add(component); });
component.APIStateChanged(this, state);
} }
public void Unregister(IOnlineComponent component) public void Unregister(IOnlineComponent component)
{ {
// todo: add support Scheduler.Add(delegate { components.Remove(component); });
} }
public void Login(string username, string password) public void Login(string username, string password)
{ {
LocalUser.Value = new User LocalUser.Value = new User
{ {
Username = @"Dummy", Username = username,
Id = 1001, Id = 1001,
}; };
State = APIState.Online;
} }
public void Logout() public void Logout()
{ {
LocalUser.Value = new GuestUser(); LocalUser.Value = new GuestUser();
State = APIState.Offline;
} }
public RegistrationRequest.RegistrationRequestErrors CreateAccount(string email, string username, string password) => null; public RegistrationRequest.RegistrationRequestErrors CreateAccount(string email, string username, string password)
{
Thread.Sleep(200);
return null;
}
} }
} }

View File

@ -169,6 +169,8 @@ namespace osu.Game.Rulesets.UI
mod.ApplyToDrawableHitObjects(Playfield.HitObjectContainer.Objects); mod.ApplyToDrawableHitObjects(Playfield.HitObjectContainer.Objects);
} }
public override void RequestResume(Action continueResume) => continueResume();
/// <summary> /// <summary>
/// Creates and adds the visual representation of a <see cref="TObject"/> to this <see cref="DrawableRuleset{TObject}"/>. /// Creates and adds the visual representation of a <see cref="TObject"/> to this <see cref="DrawableRuleset{TObject}"/>.
/// </summary> /// </summary>
@ -339,6 +341,13 @@ namespace osu.Game.Rulesets.UI
/// <param name="replayScore">The replay, null for local input.</param> /// <param name="replayScore">The replay, null for local input.</param>
public abstract void SetReplayScore(Score replayScore); public abstract void SetReplayScore(Score replayScore);
/// <summary>
/// Invoked when the interactive user requests resuming from a paused state.
/// Allows potentially delaying the resume process until an interaction is performed.
/// </summary>
/// <param name="continueResume">The action to run when resuming is to be completed.</param>
public abstract void RequestResume(Action continueResume);
/// <summary> /// <summary>
/// Create a <see cref="ScoreProcessor"/> for the associated ruleset and link with this /// Create a <see cref="ScoreProcessor"/> for the associated ruleset and link with this
/// <see cref="DrawableRuleset"/>. /// <see cref="DrawableRuleset"/>.

View File

@ -18,7 +18,7 @@ using osu.Game.Rulesets.Mods;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play
{ {
/// <summary> /// <summary>
/// Encapsulates gameplay timing logic and provides a <see cref="GameplayClock"/> for children. /// Encapsulates gameplay timing logic and provides a <see cref="Play.GameplayClock"/> for children.
/// </summary> /// </summary>
public class GameplayClockContainer : Container public class GameplayClockContainer : Container
{ {
@ -48,7 +48,7 @@ namespace osu.Game.Screens.Play
/// The final clock which is exposed to underlying components. /// The final clock which is exposed to underlying components.
/// </summary> /// </summary>
[Cached] [Cached]
private readonly GameplayClock gameplayClock; public readonly GameplayClock GameplayClock;
private Bindable<double> userAudioOffset; private Bindable<double> userAudioOffset;
@ -78,7 +78,7 @@ namespace osu.Game.Screens.Play
offsetClock = new FramedOffsetClock(platformOffsetClock); offsetClock = new FramedOffsetClock(platformOffsetClock);
// the clock to be exposed via DI to children. // the clock to be exposed via DI to children.
gameplayClock = new GameplayClock(offsetClock); GameplayClock = new GameplayClock(offsetClock);
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -118,11 +118,16 @@ namespace osu.Game.Screens.Play
// This accounts for the audio clock source potentially taking time to enter a completely stopped state // This accounts for the audio clock source potentially taking time to enter a completely stopped state
adjustableClock.Seek(adjustableClock.CurrentTime); adjustableClock.Seek(adjustableClock.CurrentTime);
adjustableClock.Start(); adjustableClock.Start();
IsPaused.Value = false;
} }
public void Seek(double time) => adjustableClock.Seek(time); public void Seek(double time) => adjustableClock.Seek(time);
public void Stop() => adjustableClock.Stop(); public void Stop()
{
adjustableClock.Stop();
IsPaused.Value = true;
}
public void ResetLocalAdjustments() public void ResetLocalAdjustments()
{ {

View File

@ -1,137 +0,0 @@
// 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.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osuTK.Graphics;
namespace osu.Game.Screens.Play
{
/// <summary>
/// A container which handles pausing children, displaying an overlay blocking its children during paused state.
/// </summary>
public class PausableGameplayContainer : Container
{
public readonly BindableBool IsPaused = new BindableBool();
public Func<bool> CheckCanPause;
private const double pause_cooldown = 1000;
private double lastPauseActionTime;
private readonly PauseOverlay pauseOverlay;
private readonly Container content;
protected override Container<Drawable> Content => content;
public int Retries
{
set => pauseOverlay.Retries = value;
}
public bool CanPause => (CheckCanPause?.Invoke() ?? true) && Time.Current >= lastPauseActionTime + pause_cooldown;
public bool IsResuming { get; private set; }
public Action OnRetry;
public Action OnQuit;
public Action Stop;
public Action Start;
/// <summary>
/// Creates a new <see cref="PausableGameplayContainer"/>.
/// </summary>
public PausableGameplayContainer()
{
RelativeSizeAxes = Axes.Both;
InternalChildren = new[]
{
content = new Container
{
RelativeSizeAxes = Axes.Both
},
pauseOverlay = new PauseOverlay
{
OnResume = () =>
{
IsResuming = true;
this.Delay(400).Schedule(Resume);
},
OnRetry = () => OnRetry(),
OnQuit = () => OnQuit(),
}
};
}
public void Pause(bool force = false) => Schedule(() => // Scheduled to ensure a stable position in execution order, no matter how it was called.
{
if (!CanPause && !force) return;
if (IsPaused.Value) return;
// stop the seekable clock (stops the audio eventually)
Stop?.Invoke();
IsPaused.Value = true;
pauseOverlay.Show();
lastPauseActionTime = Time.Current;
});
public void Resume()
{
if (!IsPaused.Value) return;
IsResuming = false;
lastPauseActionTime = Time.Current;
IsPaused.Value = false;
Start?.Invoke();
pauseOverlay.Hide();
}
private OsuGameBase game;
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
{
this.game = game;
}
protected override void Update()
{
// eagerly pause when we lose window focus (if we are locally playing).
if (!game.IsActive.Value && CanPause)
Pause();
base.Update();
}
public class PauseOverlay : GameplayMenuOverlay
{
public Action OnResume;
public override string Header => "paused";
public override string Description => "you're not going to do what i think you're going to do, are ya?";
protected override Action BackAction => () => InternalButtons.Children.First().Click();
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
AddButton("Continue", colours.Green, () => OnResume?.Invoke());
AddButton("Retry", colours.YellowDark, () => OnRetry?.Invoke());
AddButton("Quit", new Color4(170, 27, 39, 255), () => OnQuit?.Invoke());
}
}
}
}

View File

@ -0,0 +1,29 @@
// 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.Game.Graphics;
using osuTK.Graphics;
namespace osu.Game.Screens.Play
{
public class PauseOverlay : GameplayMenuOverlay
{
public Action OnResume;
public override string Header => "paused";
public override string Description => "you're not going to do what i think you're going to do, are ya?";
protected override Action BackAction => () => InternalButtons.Children.First().Click();
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
AddButton("Continue", colours.Green, () => OnResume?.Invoke());
AddButton("Retry", colours.YellowDark, () => OnRetry?.Invoke());
AddButton("Quit", new Color4(170, 27, 39, 255), () => OnQuit?.Invoke());
}
}
}

View File

@ -56,8 +56,6 @@ namespace osu.Game.Screens.Play
[Resolved] [Resolved]
private ScoreManager scoreManager { get; set; } private ScoreManager scoreManager { get; set; }
protected PausableGameplayContainer PausableGameplayContainer { get; private set; }
private RulesetInfo ruleset; private RulesetInfo ruleset;
private IAPIProvider api; private IAPIProvider api;
@ -68,23 +66,10 @@ namespace osu.Game.Screens.Play
protected DrawableRuleset DrawableRuleset { get; private set; } protected DrawableRuleset DrawableRuleset { get; private set; }
protected HUDOverlay HUDOverlay { get; private set; } protected HUDOverlay HUDOverlay { get; private set; }
private FailOverlay failOverlay;
private DrawableStoryboard storyboard;
protected UserDimContainer StoryboardContainer { get; private set; }
private Bindable<bool> showStoryboard;
protected virtual UserDimContainer CreateStoryboardContainer() => new UserDimContainer(true)
{
RelativeSizeAxes = Axes.Both,
Alpha = 1,
EnableUserDim = { Value = true }
};
public bool LoadedBeatmapSuccessfully => DrawableRuleset?.Objects.Any() == true; public bool LoadedBeatmapSuccessfully => DrawableRuleset?.Objects.Any() == true;
private GameplayClockContainer gameplayClockContainer; protected GameplayClockContainer GameplayClockContainer { get; private set; }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(AudioManager audio, IAPIProvider api, OsuConfigManager config) private void load(AudioManager audio, IAPIProvider api, OsuConfigManager config)
@ -105,20 +90,9 @@ namespace osu.Game.Screens.Play
if (!ScoreProcessor.Mode.Disabled) if (!ScoreProcessor.Mode.Disabled)
config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode); config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode);
InternalChild = gameplayClockContainer = new GameplayClockContainer(working, AllowLeadIn, DrawableRuleset.GameplayStartTime); InternalChild = GameplayClockContainer = new GameplayClockContainer(working, AllowLeadIn, DrawableRuleset.GameplayStartTime);
gameplayClockContainer.Children = new Drawable[] GameplayClockContainer.Children = new[]
{
PausableGameplayContainer = new PausableGameplayContainer
{
Retries = RestartCount,
OnRetry = Restart,
OnQuit = performUserRequestedExit,
Start = gameplayClockContainer.Start,
Stop = gameplayClockContainer.Stop,
IsPaused = { BindTarget = gameplayClockContainer.IsPaused },
CheckCanPause = () => AllowPause && ValidForResume && !HasFailed && !DrawableRuleset.HasReplayLoaded.Value,
Children = new[]
{ {
StoryboardContainer = CreateStoryboardContainer(), StoryboardContainer = CreateStoryboardContainer(),
new ScalingContainer(ScalingMode.Gameplay) new ScalingContainer(ScalingMode.Gameplay)
@ -140,23 +114,28 @@ namespace osu.Game.Screens.Play
HUDOverlay = new HUDOverlay(ScoreProcessor, DrawableRuleset, working) HUDOverlay = new HUDOverlay(ScoreProcessor, DrawableRuleset, working)
{ {
HoldToQuit = { Action = performUserRequestedExit }, HoldToQuit = { Action = performUserRequestedExit },
PlayerSettingsOverlay = { PlaybackSettings = { UserPlaybackRate = { BindTarget = gameplayClockContainer.UserPlaybackRate } } }, PlayerSettingsOverlay = { PlaybackSettings = { UserPlaybackRate = { BindTarget = GameplayClockContainer.UserPlaybackRate } } },
KeyCounter = { Visible = { BindTarget = DrawableRuleset.HasReplayLoaded } }, KeyCounter = { Visible = { BindTarget = DrawableRuleset.HasReplayLoaded } },
RequestSeek = gameplayClockContainer.Seek, RequestSeek = GameplayClockContainer.Seek,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre Origin = Anchor.Centre
}, },
new SkipOverlay(DrawableRuleset.GameplayStartTime) new SkipOverlay(DrawableRuleset.GameplayStartTime)
{ {
RequestSeek = gameplayClockContainer.Seek RequestSeek = GameplayClockContainer.Seek
}, },
} FailOverlay = new FailOverlay
},
failOverlay = new FailOverlay
{ {
OnRetry = Restart, OnRetry = Restart,
OnQuit = performUserRequestedExit, OnQuit = performUserRequestedExit,
}, },
PauseOverlay = new PauseOverlay
{
OnResume = Resume,
Retries = RestartCount,
OnRetry = Restart,
OnQuit = performUserRequestedExit,
},
new HotkeyRetryOverlay new HotkeyRetryOverlay
{ {
Action = () => Action = () =>
@ -170,9 +149,9 @@ namespace osu.Game.Screens.Play
}; };
// bind clock into components that require it // bind clock into components that require it
DrawableRuleset.IsPaused.BindTo(gameplayClockContainer.IsPaused); DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused);
if (showStoryboard.Value) // load storyboard as part of player's load if we can
initializeStoryboard(false); initializeStoryboard(false);
// Bind ScoreProcessor to ourselves // Bind ScoreProcessor to ourselves
@ -289,19 +268,148 @@ namespace osu.Game.Screens.Play
return score; return score;
} }
protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !GameplayClockContainer.IsPaused.Value;
protected virtual Results CreateResults(ScoreInfo score) => new SoloResults(score);
#region Storyboard
private DrawableStoryboard storyboard;
protected UserDimContainer StoryboardContainer { get; private set; }
protected virtual UserDimContainer CreateStoryboardContainer() => new UserDimContainer(true)
{
RelativeSizeAxes = Axes.Both,
Alpha = 1,
EnableUserDim = { Value = true }
};
private Bindable<bool> showStoryboard;
private void initializeStoryboard(bool asyncLoad)
{
if (StoryboardContainer == null || storyboard != null)
return;
if (!showStoryboard.Value)
return;
var beatmap = Beatmap.Value;
storyboard = beatmap.Storyboard.CreateDrawable();
storyboard.Masking = true;
if (asyncLoad)
LoadComponentAsync(storyboard, StoryboardContainer.Add);
else
StoryboardContainer.Add(storyboard);
}
#endregion
#region Fail Logic
protected FailOverlay FailOverlay { get; private set; }
private bool onFail() private bool onFail()
{ {
if (Beatmap.Value.Mods.Value.OfType<IApplicableFailOverride>().Any(m => !m.AllowFail)) if (Beatmap.Value.Mods.Value.OfType<IApplicableFailOverride>().Any(m => !m.AllowFail))
return false; return false;
gameplayClockContainer.Stop(); GameplayClockContainer.Stop();
HasFailed = true; HasFailed = true;
failOverlay.Retries = RestartCount;
failOverlay.Show(); // There is a chance that we could be in a paused state as the ruleset's internal clock (see FrameStabilityContainer)
// could process an extra frame after the GameplayClock is stopped.
// In such cases we want the fail state to precede a user triggered pause.
if (PauseOverlay.State == Visibility.Visible)
PauseOverlay.Hide();
FailOverlay.Retries = RestartCount;
FailOverlay.Show();
return true; return true;
} }
#endregion
#region Pause Logic
public bool IsResuming { get; private set; }
/// <summary>
/// The amount of gameplay time after which a second pause is allowed.
/// </summary>
private const double pause_cooldown = 1000;
protected PauseOverlay PauseOverlay { get; private set; }
private double? lastPauseActionTime;
private bool canPause =>
// must pass basic screen conditions (beatmap loaded, instance allows pause)
LoadedBeatmapSuccessfully && AllowPause && ValidForResume
// replays cannot be paused and exit immediately
&& !DrawableRuleset.HasReplayLoaded.Value
// cannot pause if we are already in a fail state
&& !HasFailed
// cannot pause if already paused (or in a cooldown state) unless we are in a resuming state.
&& (IsResuming || (GameplayClockContainer.IsPaused.Value == false && !pauseCooldownActive));
private bool pauseCooldownActive =>
lastPauseActionTime.HasValue && GameplayClockContainer.GameplayClock.CurrentTime < lastPauseActionTime + pause_cooldown;
private bool canResume =>
// cannot resume from a non-paused state
GameplayClockContainer.IsPaused.Value
// cannot resume if we are already in a fail state
&& !HasFailed
// already resuming
&& !IsResuming;
protected override void Update()
{
base.Update();
// eagerly pause when we lose window focus (if we are locally playing).
if (!Game.IsActive.Value)
Pause();
}
public void Pause()
{
if (!canPause) return;
IsResuming = false;
GameplayClockContainer.Stop();
PauseOverlay.Show();
lastPauseActionTime = GameplayClockContainer.GameplayClock.CurrentTime;
}
public void Resume()
{
if (!canResume) return;
IsResuming = true;
PauseOverlay.Hide();
// time-based conditions may allow instant resume.
if (GameplayClockContainer.GameplayClock.CurrentTime < Beatmap.Value.Beatmap.HitObjects.First().StartTime)
completeResume();
else
DrawableRuleset.RequestResume(completeResume);
void completeResume()
{
GameplayClockContainer.Start();
IsResuming = false;
}
}
#endregion
#region Screen Logic
public override void OnEntering(IScreen last) public override void OnEntering(IScreen last)
{ {
base.OnEntering(last); base.OnEntering(last);
@ -316,10 +424,7 @@ namespace osu.Game.Screens.Play
.Delay(250) .Delay(250)
.FadeIn(250); .FadeIn(250);
showStoryboard.ValueChanged += enabled => showStoryboard.ValueChanged += _ => initializeStoryboard(true);
{
if (enabled.NewValue) initializeStoryboard(true);
};
Background.EnableUserDim.Value = true; Background.EnableUserDim.Value = true;
Background.BlurAmount.Value = 0; Background.BlurAmount.Value = 0;
@ -329,10 +434,8 @@ namespace osu.Game.Screens.Play
storyboardReplacesBackground.Value = Beatmap.Value.Storyboard.ReplacesBackground && Beatmap.Value.Storyboard.HasDrawable; storyboardReplacesBackground.Value = Beatmap.Value.Storyboard.ReplacesBackground && Beatmap.Value.Storyboard.HasDrawable;
gameplayClockContainer.Restart(); GameplayClockContainer.Restart();
GameplayClockContainer.FadeInFromZero(750, Easing.OutQuint);
PausableGameplayContainer.Alpha = 0;
PausableGameplayContainer.FadeIn(750, Easing.OutQuint);
} }
public override void OnSuspending(IScreen next) public override void OnSuspending(IScreen next)
@ -350,20 +453,22 @@ namespace osu.Game.Screens.Play
return true; return true;
} }
if ((!AllowPause || HasFailed || !ValidForResume || PausableGameplayContainer?.IsPaused.Value != false || DrawableRuleset?.HasReplayLoaded.Value != false) && (!PausableGameplayContainer?.IsResuming ?? true)) if (canPause)
{ {
gameplayClockContainer.ResetLocalAdjustments(); Pause();
return true;
}
if (pauseCooldownActive && !GameplayClockContainer.IsPaused.Value)
// still want to block if we are within the cooldown period and not already paused.
return true;
GameplayClockContainer.ResetLocalAdjustments();
fadeOut(); fadeOut();
return base.OnExiting(next); return base.OnExiting(next);
} }
if (LoadedBeatmapSuccessfully)
PausableGameplayContainer?.Pause();
return true;
}
private void fadeOut(bool instant = false) private void fadeOut(bool instant = false)
{ {
float fadeOutDuration = instant ? 0 : 250; float fadeOutDuration = instant ? 0 : 250;
@ -373,24 +478,6 @@ namespace osu.Game.Screens.Play
storyboardReplacesBackground.Value = false; storyboardReplacesBackground.Value = false;
} }
protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !PausableGameplayContainer.IsPaused.Value; #endregion
private void initializeStoryboard(bool asyncLoad)
{
if (StoryboardContainer == null || storyboard != null)
return;
var beatmap = Beatmap.Value;
storyboard = beatmap.Storyboard.CreateDrawable();
storyboard.Masking = true;
if (asyncLoad)
LoadComponentAsync(storyboard, StoryboardContainer.Add);
else
StoryboardContainer.Add(storyboard);
}
protected virtual Results CreateResults(ScoreInfo score) => new SoloResults(score);
} }
} }

View File

@ -55,9 +55,9 @@ namespace osu.Game.Screens.Select
public override bool HandlePositionalInput => AllowSelection; public override bool HandlePositionalInput => AllowSelection;
/// <summary> /// <summary>
/// Used to avoid firing null selections before the initial beatmaps have been loaded via <see cref="BeatmapSets"/>. /// Whether carousel items have completed asynchronously loaded.
/// </summary> /// </summary>
private bool initialLoadComplete; public bool BeatmapSetsLoaded { get; private set; }
private IEnumerable<CarouselBeatmapSet> beatmapSets => root.Children.OfType<CarouselBeatmapSet>(); private IEnumerable<CarouselBeatmapSet> beatmapSets => root.Children.OfType<CarouselBeatmapSet>();
@ -90,7 +90,7 @@ namespace osu.Game.Screens.Select
Schedule(() => Schedule(() =>
{ {
BeatmapSetsChanged?.Invoke(); BeatmapSetsChanged?.Invoke();
initialLoadComplete = true; BeatmapSetsLoaded = true;
}); });
})); }));
} }
@ -327,6 +327,9 @@ namespace osu.Game.Screens.Select
private void select(CarouselItem item) private void select(CarouselItem item)
{ {
if (!AllowSelection)
return;
if (item == null) return; if (item == null) return;
item.State.Value = CarouselItemState.Selected; item.State.Value = CarouselItemState.Selected;
@ -593,7 +596,7 @@ namespace osu.Game.Screens.Select
currentY += DrawHeight / 2; currentY += DrawHeight / 2;
scrollableContent.Height = currentY; scrollableContent.Height = currentY;
if (initialLoadComplete && (selectedBeatmapSet == null || selectedBeatmap == null || selectedBeatmapSet.State.Value != CarouselItemState.Selected)) if (BeatmapSetsLoaded && (selectedBeatmapSet == null || selectedBeatmap == null || selectedBeatmapSet.State.Value != CarouselItemState.Selected))
{ {
selectedBeatmapSet = null; selectedBeatmapSet = null;
SelectionChanged?.Invoke(null); SelectionChanged?.Invoke(null);

View File

@ -300,6 +300,10 @@ namespace osu.Game.Screens.Select
/// <param name="performStartAction">Whether to trigger <see cref="OnStart"/>.</param> /// <param name="performStartAction">Whether to trigger <see cref="OnStart"/>.</param>
public void FinaliseSelection(BeatmapInfo beatmap = null, bool performStartAction = true) public void FinaliseSelection(BeatmapInfo beatmap = null, bool performStartAction = true)
{ {
// This is very important as we have not yet bound to screen-level bindables before the carousel load is completed.
if (!Carousel.BeatmapSetsLoaded)
return;
// if we have a pending filter operation, we want to run it now. // if we have a pending filter operation, we want to run it now.
// it could change selection (ie. if the ruleset has been changed). // it could change selection (ie. if the ruleset has been changed).
Carousel.FlushPendingFilterOperations(); Carousel.FlushPendingFilterOperations();
@ -373,6 +377,13 @@ namespace osu.Game.Screens.Select
var beatmap = beatmapNoDebounce; var beatmap = beatmapNoDebounce;
var ruleset = rulesetNoDebounce; var ruleset = rulesetNoDebounce;
selectionChangedDebounce?.Cancel();
if (beatmap == null)
run();
else
selectionChangedDebounce = Scheduler.AddDelayed(run, 200);
void run() void run()
{ {
Logger.Log($"updating selection with beatmap:{beatmap?.ID.ToString() ?? "null"} ruleset:{ruleset?.ID.ToString() ?? "null"}"); Logger.Log($"updating selection with beatmap:{beatmap?.ID.ToString() ?? "null"} ruleset:{ruleset?.ID.ToString() ?? "null"}");
@ -417,13 +428,6 @@ namespace osu.Game.Screens.Select
if (this.IsCurrentScreen()) ensurePlayingSelected(preview); if (this.IsCurrentScreen()) ensurePlayingSelected(preview);
UpdateBeatmap(Beatmap.Value); UpdateBeatmap(Beatmap.Value);
} }
selectionChangedDebounce?.Cancel();
if (beatmap == null)
run();
else
selectionChangedDebounce = Scheduler.AddDelayed(run, 200);
} }
private void triggerRandom() private void triggerRandom()
@ -593,18 +597,7 @@ namespace osu.Game.Screens.Select
private void carouselBeatmapsLoaded() private void carouselBeatmapsLoaded()
{ {
if (rulesetNoDebounce == null) bindBindables();
{
// manual binding to parent ruleset to allow for delayed load in the incoming direction.
rulesetNoDebounce = decoupledRuleset.Value = Ruleset.Value;
Ruleset.ValueChanged += r => updateSelectedRuleset(r.NewValue);
decoupledRuleset.ValueChanged += r => Ruleset.Value = r.NewValue;
decoupledRuleset.DisabledChanged += r => Ruleset.Disabled = r;
Beatmap.BindDisabledChanged(disabled => Carousel.AllowSelection = !disabled, true);
Beatmap.BindValueChanged(workingBeatmapChanged);
}
if (!Beatmap.IsDefault && Beatmap.Value.BeatmapSetInfo?.DeletePending == false && Beatmap.Value.BeatmapSetInfo?.Protected == false if (!Beatmap.IsDefault && Beatmap.Value.BeatmapSetInfo?.DeletePending == false && Beatmap.Value.BeatmapSetInfo?.Protected == false
&& Carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo, false)) && Carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo, false))
@ -618,6 +611,26 @@ namespace osu.Game.Screens.Select
} }
} }
private bool boundLocalBindables;
private void bindBindables()
{
if (boundLocalBindables)
return;
// manual binding to parent ruleset to allow for delayed load in the incoming direction.
rulesetNoDebounce = decoupledRuleset.Value = Ruleset.Value;
Ruleset.ValueChanged += r => updateSelectedRuleset(r.NewValue);
decoupledRuleset.ValueChanged += r => Ruleset.Value = r.NewValue;
decoupledRuleset.DisabledChanged += r => Ruleset.Disabled = r;
Beatmap.BindDisabledChanged(disabled => Carousel.AllowSelection = !disabled, true);
Beatmap.BindValueChanged(workingBeatmapChanged);
boundLocalBindables = true;
}
private void delete(BeatmapSetInfo beatmap) private void delete(BeatmapSetInfo beatmap)
{ {
if (beatmap == null || beatmap.ID <= 0) return; if (beatmap == null || beatmap.ID <= 0) return;

View File

@ -1,127 +0,0 @@
// 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.Graphics.Shapes;
using osu.Framework.Lists;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual
{
public abstract class TestCasePlayer : RateAdjustedBeatmapTestCase
{
private readonly Ruleset ruleset;
protected Player Player;
protected TestCasePlayer(Ruleset ruleset)
{
this.ruleset = ruleset;
}
protected TestCasePlayer()
{
}
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
Add(new Box
{
RelativeSizeAxes = Framework.Graphics.Axes.Both,
Colour = Color4.Black,
Depth = int.MaxValue
});
if (ruleset != null)
{
Player p = null;
AddStep(ruleset.RulesetInfo.Name, () => p = loadPlayerFor(ruleset));
AddCheckSteps(() => p);
}
else
{
foreach (var r in rulesets.AvailableRulesets)
{
Player p = null;
AddStep(r.Name, () => p = loadPlayerFor(r));
AddCheckSteps(() => p);
AddUntilStep("no leaked beatmaps", () =>
{
p = null;
GC.Collect();
GC.WaitForPendingFinalizers();
int count = 0;
workingWeakReferences.ForEachAlive(_ => count++);
return count == 1;
});
AddUntilStep("no leaked players", () =>
{
GC.Collect();
GC.WaitForPendingFinalizers();
int count = 0;
playerWeakReferences.ForEachAlive(_ => count++);
return count == 1;
});
}
}
}
protected virtual void AddCheckSteps(Func<Player> player)
{
AddUntilStep("player loaded", () => player().IsLoaded);
}
protected virtual IBeatmap CreateBeatmap(Ruleset ruleset) => new TestBeatmap(ruleset.RulesetInfo);
private readonly WeakList<WorkingBeatmap> workingWeakReferences = new WeakList<WorkingBeatmap>();
private readonly WeakList<Player> playerWeakReferences = new WeakList<Player>();
private Player loadPlayerFor(RulesetInfo ri)
{
Ruleset.Value = ri;
return loadPlayerFor(ri.CreateInstance());
}
private Player loadPlayerFor(Ruleset r)
{
var beatmap = CreateBeatmap(r);
var working = new TestWorkingBeatmap(beatmap, Clock);
workingWeakReferences.Add(working);
Beatmap.Value = working;
Beatmap.Value.Mods.Value = new[] { r.GetAllMods().First(m => m is ModNoFail) };
Player?.Exit();
Player = CreatePlayer(r);
playerWeakReferences.Add(Player);
LoadScreen(Player);
return Player;
}
protected virtual Player CreatePlayer(Ruleset ruleset) => new Player
{
AllowPause = false,
AllowLeadIn = false,
AllowResults = false,
};
}
}

View File

@ -11,13 +11,13 @@
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" /> <Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Humanizer" Version="2.5.16" /> <PackageReference Include="Humanizer" Version="2.6.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.3" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.128.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2019.128.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.320.0" /> <PackageReference Include="ppy.osu.Framework" Version="2019.320.0" />
<PackageReference Include="SharpCompress" Version="0.22.0" /> <PackageReference Include="SharpCompress" Version="0.23.0" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" /> <PackageReference Include="SharpRaven" Version="2.4.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" /> <PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" />