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:
commit
db89796752
@ -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" />
|
||||||
|
@ -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">
|
||||||
|
@ -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">
|
||||||
|
@ -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">
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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">
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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", () =>
|
||||||
|
@ -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"),
|
||||||
|
152
osu.Game.Tests/Visual/TestCasePause.cs
Normal file
152
osu.Game.Tests/Visual/TestCasePause.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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">
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"/>.
|
||||||
|
@ -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()
|
||||||
{
|
{
|
||||||
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
29
osu.Game/Screens/Play/PauseOverlay.cs
Normal file
29
osu.Game/Screens/Play/PauseOverlay.cs
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -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" />
|
||||||
|
Loading…
Reference in New Issue
Block a user