mirror of
https://github.com/ppy/osu
synced 2024-12-15 03:16:17 +00:00
Merge branch 'master' into master
This commit is contained in:
commit
2fe642a12b
9
.idea/.idea.osu/.idea/runConfigurations/osu_.xml
generated
9
.idea/.idea.osu/.idea/runConfigurations/osu_.xml
generated
@ -1,17 +1,20 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="osu!" type="DotNetProject" factoryName=".NET Project">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp2.1/osu!.dll" />
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp2.2/osu!.dll" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop" />
|
||||
<option name="PASS_PARENT_ENVS" value="1" />
|
||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||
<option name="USE_MONO" value="0" />
|
||||
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Desktop/osu.Desktop.csproj" />
|
||||
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v2.1" />
|
||||
<method />
|
||||
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v2.2" />
|
||||
<method v="2">
|
||||
<option name="Build" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\osu.Game.props" />
|
||||
<PropertyGroup Label="Project">
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
|
@ -9,7 +9,7 @@
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
|
||||
|
@ -9,7 +9,7 @@
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
|
||||
|
@ -9,7 +9,7 @@
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
|
||||
|
@ -9,7 +9,7 @@
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
||||
|
143
osu.Game.Tests/Visual/TestCasePollingComponent.cs
Normal file
143
osu.Game.Tests/Visual/TestCasePollingComponent.cs
Normal file
@ -0,0 +1,143 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public class TestCasePollingComponent : OsuTestCase
|
||||
{
|
||||
private Container pollBox;
|
||||
private TestPoller poller;
|
||||
|
||||
private const float safety_adjust = 1f;
|
||||
private int count;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
count = 0;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
pollBox = new Container
|
||||
{
|
||||
Alpha = 0,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(0.4f),
|
||||
Colour = Color4.LimeGreen,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = "Poll!",
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestInstantPolling()
|
||||
{
|
||||
createPoller(true);
|
||||
|
||||
AddStep("set poll interval to 1", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust);
|
||||
checkCount(1);
|
||||
checkCount(2);
|
||||
checkCount(3);
|
||||
|
||||
AddStep("set poll interval to 5", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust * 5);
|
||||
checkCount(4);
|
||||
checkCount(4);
|
||||
checkCount(4);
|
||||
|
||||
skip();
|
||||
|
||||
checkCount(5);
|
||||
checkCount(5);
|
||||
|
||||
AddStep("set poll interval to 1", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust);
|
||||
checkCount(6);
|
||||
checkCount(7);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Ignore("i have no idea how to fix the timing of this one")]
|
||||
public void TestSlowPolling()
|
||||
{
|
||||
createPoller(false);
|
||||
|
||||
AddStep("set poll interval to 1", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust * 5);
|
||||
checkCount(0);
|
||||
skip();
|
||||
checkCount(0);
|
||||
skip();
|
||||
skip();
|
||||
checkCount(0);
|
||||
skip();
|
||||
skip();
|
||||
checkCount(0);
|
||||
}
|
||||
|
||||
private void skip() => AddStep("skip", () =>
|
||||
{
|
||||
// could be 4 or 5 at this point due to timing discrepancies (safety_adjust @ 0.2 * 5 ~= 1)
|
||||
// easiest to just ignore the value at this point and move on.
|
||||
});
|
||||
|
||||
private void checkCount(int checkValue)
|
||||
{
|
||||
Logger.Log($"value is {count}");
|
||||
AddAssert($"count is {checkValue}", () => count == checkValue);
|
||||
}
|
||||
|
||||
private void createPoller(bool instant) => AddStep("create poller", () =>
|
||||
{
|
||||
poller?.Expire();
|
||||
|
||||
Add(poller = instant ? new TestPoller() : new TestSlowPoller());
|
||||
poller.OnPoll += () =>
|
||||
{
|
||||
pollBox.FadeOutFromOne(500);
|
||||
count++;
|
||||
};
|
||||
});
|
||||
|
||||
protected override double TimePerAction => 500;
|
||||
|
||||
public class TestPoller : PollingComponent
|
||||
{
|
||||
public event Action OnPoll;
|
||||
|
||||
protected override Task Poll()
|
||||
{
|
||||
Schedule(() => OnPoll?.Invoke());
|
||||
return base.Poll();
|
||||
}
|
||||
}
|
||||
|
||||
public class TestSlowPoller : TestPoller
|
||||
{
|
||||
protected override Task Poll() => Task.Delay((int)(TimeBetweenPolls / 2f / Clock.Rate)).ContinueWith(_ => base.Poll());
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
|
||||
|
@ -199,6 +199,12 @@ namespace osu.Game.Online.API
|
||||
try
|
||||
{
|
||||
Logger.Log($@"Performing request {req}", LoggingTarget.Network);
|
||||
req.Failure += ex =>
|
||||
{
|
||||
if (ex is WebException we)
|
||||
handleWebException(we);
|
||||
};
|
||||
|
||||
req.Perform(this);
|
||||
|
||||
//we could still be in initialisation, at which point we don't want to say we're Online yet.
|
||||
@ -210,37 +216,12 @@ namespace osu.Game.Online.API
|
||||
}
|
||||
catch (WebException we)
|
||||
{
|
||||
HttpStatusCode statusCode = (we.Response as HttpWebResponse)?.StatusCode
|
||||
?? (we.Status == WebExceptionStatus.UnknownError ? HttpStatusCode.NotAcceptable : HttpStatusCode.RequestTimeout);
|
||||
var removeFromQueue = handleWebException(we);
|
||||
|
||||
// special cases for un-typed but useful message responses.
|
||||
switch (we.Message)
|
||||
{
|
||||
case "Unauthorized":
|
||||
statusCode = HttpStatusCode.Unauthorized;
|
||||
break;
|
||||
}
|
||||
if (removeFromQueue)
|
||||
req.Fail(we);
|
||||
|
||||
switch (statusCode)
|
||||
{
|
||||
case HttpStatusCode.Unauthorized:
|
||||
Logout(false);
|
||||
return true;
|
||||
case HttpStatusCode.RequestTimeout:
|
||||
failureCount++;
|
||||
log.Add($@"API failure count is now {failureCount}");
|
||||
|
||||
if (failureCount < 3)
|
||||
//we might try again at an api level.
|
||||
return false;
|
||||
|
||||
State = APIState.Failing;
|
||||
flushQueue();
|
||||
return true;
|
||||
}
|
||||
|
||||
req.Fail(we);
|
||||
return true;
|
||||
return removeFromQueue;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -276,6 +257,41 @@ namespace osu.Game.Online.API
|
||||
}
|
||||
}
|
||||
|
||||
private bool handleWebException(WebException we)
|
||||
{
|
||||
HttpStatusCode statusCode = (we.Response as HttpWebResponse)?.StatusCode
|
||||
?? (we.Status == WebExceptionStatus.UnknownError ? HttpStatusCode.NotAcceptable : HttpStatusCode.RequestTimeout);
|
||||
|
||||
// special cases for un-typed but useful message responses.
|
||||
switch (we.Message)
|
||||
{
|
||||
case "Unauthorized":
|
||||
case "Forbidden":
|
||||
statusCode = HttpStatusCode.Unauthorized;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (statusCode)
|
||||
{
|
||||
case HttpStatusCode.Unauthorized:
|
||||
Logout(false);
|
||||
return true;
|
||||
case HttpStatusCode.RequestTimeout:
|
||||
failureCount++;
|
||||
log.Add($@"API failure count is now {failureCount}");
|
||||
|
||||
if (failureCount < 3)
|
||||
//we might try again at an api level.
|
||||
return false;
|
||||
|
||||
State = APIState.Failing;
|
||||
flushQueue();
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsLoggedIn => LocalUser.Value.Id > 1;
|
||||
|
||||
public void Queue(APIRequest request)
|
||||
|
@ -4,11 +4,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Users;
|
||||
@ -18,7 +17,7 @@ namespace osu.Game.Online.Chat
|
||||
/// <summary>
|
||||
/// Manages everything channel related
|
||||
/// </summary>
|
||||
public class ChannelManager : Component, IOnlineComponent
|
||||
public class ChannelManager : PollingComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// The channels the player joins on startup
|
||||
@ -49,11 +48,14 @@ namespace osu.Game.Online.Chat
|
||||
public IBindableCollection<Channel> AvailableChannels => availableChannels;
|
||||
|
||||
private IAPIProvider api;
|
||||
private ScheduledDelegate fetchMessagesScheduleder;
|
||||
|
||||
public readonly BindableBool HighPollRate = new BindableBool();
|
||||
|
||||
public ChannelManager()
|
||||
{
|
||||
CurrentChannel.ValueChanged += currentChannelChanged;
|
||||
|
||||
HighPollRate.BindValueChanged(high => TimeBetweenPolls = high ? 1000 : 6000, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -360,73 +362,60 @@ namespace osu.Game.Online.Chat
|
||||
}
|
||||
}
|
||||
|
||||
public void APIStateChanged(APIAccess api, APIState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case APIState.Online:
|
||||
fetchUpdates();
|
||||
break;
|
||||
default:
|
||||
fetchMessagesScheduleder?.Cancel();
|
||||
fetchMessagesScheduleder = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private long lastMessageId;
|
||||
private const int update_poll_interval = 1000;
|
||||
|
||||
private bool channelsInitialised;
|
||||
|
||||
private void fetchUpdates()
|
||||
protected override Task Poll()
|
||||
{
|
||||
fetchMessagesScheduleder?.Cancel();
|
||||
fetchMessagesScheduleder = Scheduler.AddDelayed(() =>
|
||||
if (!api.IsLoggedIn)
|
||||
return base.Poll();
|
||||
|
||||
var fetchReq = new GetUpdatesRequest(lastMessageId);
|
||||
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
|
||||
fetchReq.Success += updates =>
|
||||
{
|
||||
var fetchReq = new GetUpdatesRequest(lastMessageId);
|
||||
|
||||
fetchReq.Success += updates =>
|
||||
if (updates?.Presence != null)
|
||||
{
|
||||
if (updates?.Presence != null)
|
||||
foreach (var channel in updates.Presence)
|
||||
{
|
||||
foreach (var channel in updates.Presence)
|
||||
{
|
||||
// we received this from the server so should mark the channel already joined.
|
||||
JoinChannel(channel, true);
|
||||
}
|
||||
|
||||
//todo: handle left channels
|
||||
|
||||
handleChannelMessages(updates.Messages);
|
||||
|
||||
foreach (var group in updates.Messages.GroupBy(m => m.ChannelId))
|
||||
JoinedChannels.FirstOrDefault(c => c.Id == group.Key)?.AddNewMessages(group.ToArray());
|
||||
|
||||
lastMessageId = updates.Messages.LastOrDefault()?.Id ?? lastMessageId;
|
||||
// we received this from the server so should mark the channel already joined.
|
||||
JoinChannel(channel, true);
|
||||
}
|
||||
|
||||
if (!channelsInitialised)
|
||||
{
|
||||
channelsInitialised = true;
|
||||
// we want this to run after the first presence so we can see if the user is in any channels already.
|
||||
initializeChannels();
|
||||
}
|
||||
//todo: handle left channels
|
||||
|
||||
fetchUpdates();
|
||||
};
|
||||
handleChannelMessages(updates.Messages);
|
||||
|
||||
fetchReq.Failure += delegate { fetchUpdates(); };
|
||||
foreach (var group in updates.Messages.GroupBy(m => m.ChannelId))
|
||||
JoinedChannels.FirstOrDefault(c => c.Id == group.Key)?.AddNewMessages(group.ToArray());
|
||||
|
||||
api.Queue(fetchReq);
|
||||
}, update_poll_interval);
|
||||
lastMessageId = updates.Messages.LastOrDefault()?.Id ?? lastMessageId;
|
||||
}
|
||||
|
||||
if (!channelsInitialised)
|
||||
{
|
||||
channelsInitialised = true;
|
||||
// we want this to run after the first presence so we can see if the user is in any channels already.
|
||||
initializeChannels();
|
||||
}
|
||||
|
||||
tcs.SetResult(true);
|
||||
};
|
||||
|
||||
fetchReq.Failure += _ => tcs.SetResult(false);
|
||||
|
||||
api.Queue(fetchReq);
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IAPIProvider api)
|
||||
{
|
||||
this.api = api;
|
||||
api.Register(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
118
osu.Game/Online/PollingComponent.cs
Normal file
118
osu.Game/Online/PollingComponent.cs
Normal file
@ -0,0 +1,118 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Threading;
|
||||
|
||||
namespace osu.Game.Online
|
||||
{
|
||||
/// <summary>
|
||||
/// A component which requires a constant polling process.
|
||||
/// </summary>
|
||||
public abstract class PollingComponent : Component
|
||||
{
|
||||
private double? lastTimePolled;
|
||||
|
||||
private ScheduledDelegate scheduledPoll;
|
||||
|
||||
private bool pollingActive;
|
||||
|
||||
private double timeBetweenPolls;
|
||||
|
||||
/// <summary>
|
||||
/// The time in milliseconds to wait between polls.
|
||||
/// Setting to zero stops all polling.
|
||||
/// </summary>
|
||||
public double TimeBetweenPolls
|
||||
{
|
||||
get => timeBetweenPolls;
|
||||
set
|
||||
{
|
||||
timeBetweenPolls = value;
|
||||
scheduledPoll?.Cancel();
|
||||
pollIfNecessary();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="timeBetweenPolls">The initial time in milliseconds to wait between polls. Setting to zero stops al polling.</param>
|
||||
protected PollingComponent(double timeBetweenPolls = 0)
|
||||
{
|
||||
TimeBetweenPolls = timeBetweenPolls;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
pollIfNecessary();
|
||||
}
|
||||
|
||||
private bool pollIfNecessary()
|
||||
{
|
||||
// we must be loaded so we have access to clock.
|
||||
if (!IsLoaded) return false;
|
||||
|
||||
// there's already a poll process running.
|
||||
if (pollingActive) return false;
|
||||
|
||||
// don't try polling if the time between polls hasn't been set.
|
||||
if (timeBetweenPolls == 0) return false;
|
||||
|
||||
if (!lastTimePolled.HasValue)
|
||||
{
|
||||
doPoll();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Time.Current - lastTimePolled.Value > timeBetweenPolls)
|
||||
{
|
||||
doPoll();
|
||||
return true;
|
||||
}
|
||||
|
||||
// not ennough time has passed since the last poll. we do want to schedule a poll to happen, though.
|
||||
scheduleNextPoll();
|
||||
return false;
|
||||
}
|
||||
|
||||
private void doPoll()
|
||||
{
|
||||
scheduledPoll = null;
|
||||
pollingActive = true;
|
||||
Poll().ContinueWith(_ => pollComplete());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform the polling in this method. Call <see cref="pollComplete"/> when done.
|
||||
/// </summary>
|
||||
protected virtual Task Poll()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call when a poll operation has completed.
|
||||
/// </summary>
|
||||
private void pollComplete()
|
||||
{
|
||||
lastTimePolled = Time.Current;
|
||||
pollingActive = false;
|
||||
|
||||
if (scheduledPoll == null)
|
||||
scheduleNextPoll();
|
||||
}
|
||||
|
||||
private void scheduleNextPoll()
|
||||
{
|
||||
scheduledPoll?.Cancel();
|
||||
|
||||
double lastPollDuration = lastTimePolled.HasValue ? Time.Current - lastTimePolled.Value : 0;
|
||||
|
||||
scheduledPoll = Scheduler.AddDelayed(doPoll, Math.Max(0, timeBetweenPolls - lastPollDuration));
|
||||
}
|
||||
}
|
||||
}
|
@ -418,6 +418,8 @@ namespace osu.Game
|
||||
dependencies.Cache(notifications);
|
||||
dependencies.Cache(dialogOverlay);
|
||||
|
||||
chatOverlay.StateChanged += state => channelManager.HighPollRate.Value = state == Visibility.Visible;
|
||||
|
||||
Add(externalLinkOpener = new ExternalLinkOpener());
|
||||
|
||||
var singleDisplaySideOverlays = new OverlayContainer[] { settings, notifications };
|
||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
|
||||
public override FontAwesome Icon => FontAwesome.fa_paint_brush;
|
||||
|
||||
private readonly Bindable<SkinInfo> dropdownBindable = new Bindable<SkinInfo>();
|
||||
private readonly Bindable<SkinInfo> dropdownBindable = new Bindable<SkinInfo> { Default = SkinInfo.Default };
|
||||
private readonly Bindable<int> configBindable = new Bindable<int>();
|
||||
|
||||
private SkinManager skins;
|
||||
|
@ -25,13 +25,20 @@ namespace osu.Game.Overlays
|
||||
protected override void PopIn()
|
||||
{
|
||||
base.PopIn();
|
||||
|
||||
Waves.Show();
|
||||
|
||||
this.FadeIn();
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
base.PopOut();
|
||||
|
||||
Waves.Hide();
|
||||
|
||||
// this is required or we will remain present even though our waves are hidden.
|
||||
this.Delay(WaveContainer.DISAPPEAR_DURATION).FadeOut();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,7 +170,7 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
Retries = RestartCount,
|
||||
OnRetry = Restart,
|
||||
OnQuit = Exit,
|
||||
OnQuit = performUserRequestedExit,
|
||||
CheckCanPause = () => AllowPause && ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded,
|
||||
Children = new[]
|
||||
{
|
||||
@ -211,7 +211,7 @@ namespace osu.Game.Screens.Play
|
||||
failOverlay = new FailOverlay
|
||||
{
|
||||
OnRetry = Restart,
|
||||
OnQuit = Exit,
|
||||
OnQuit = performUserRequestedExit,
|
||||
},
|
||||
new HotkeyRetryOverlay
|
||||
{
|
||||
@ -225,7 +225,7 @@ namespace osu.Game.Screens.Play
|
||||
}
|
||||
};
|
||||
|
||||
hudOverlay.HoldToQuit.Action = Exit;
|
||||
hudOverlay.HoldToQuit.Action = performUserRequestedExit;
|
||||
hudOverlay.KeyCounter.Visible.BindTo(RulesetContainer.HasReplayLoaded);
|
||||
|
||||
RulesetContainer.IsPaused.BindTo(pauseContainer.IsPaused);
|
||||
@ -250,8 +250,16 @@ namespace osu.Game.Screens.Play
|
||||
mod.ApplyToClock(sourceClock);
|
||||
}
|
||||
|
||||
private void performUserRequestedExit()
|
||||
{
|
||||
if (!IsCurrentScreen) return;
|
||||
Exit();
|
||||
}
|
||||
|
||||
public void Restart()
|
||||
{
|
||||
if (!IsCurrentScreen) return;
|
||||
|
||||
sampleRestart?.Play();
|
||||
ValidForResume = false;
|
||||
RestartRequested?.Invoke();
|
||||
|
@ -16,8 +16,8 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
public event Action SourceChanged;
|
||||
|
||||
private Bindable<bool> beatmapSkins = new Bindable<bool>();
|
||||
private Bindable<bool> beatmapHitsounds = new Bindable<bool>();
|
||||
private readonly Bindable<bool> beatmapSkins = new Bindable<bool>();
|
||||
private readonly Bindable<bool> beatmapHitsounds = new Bindable<bool>();
|
||||
|
||||
public Drawable GetDrawableComponent(string componentName)
|
||||
{
|
||||
@ -84,11 +84,8 @@ namespace osu.Game.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
beatmapSkins = config.GetBindable<bool>(OsuSetting.BeatmapSkins);
|
||||
beatmapSkins.BindValueChanged(_ => onSourceChanged());
|
||||
|
||||
beatmapHitsounds = config.GetBindable<bool>(OsuSetting.BeatmapHitsounds);
|
||||
beatmapHitsounds.BindValueChanged(_ => onSourceChanged(), true);
|
||||
config.BindWith(OsuSetting.BeatmapSkins, beatmapSkins);
|
||||
config.BindWith(OsuSetting.BeatmapHitsounds, beatmapHitsounds);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -97,6 +94,9 @@ namespace osu.Game.Skinning
|
||||
|
||||
if (fallbackSource != null)
|
||||
fallbackSource.SourceChanged += onSourceChanged;
|
||||
|
||||
beatmapSkins.BindValueChanged(_ => onSourceChanged());
|
||||
beatmapHitsounds.BindValueChanged(_ => onSourceChanged(), true);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
|
@ -18,7 +18,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2018.1207.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2018.1214.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.22.0" />
|
||||
<PackageReference Include="NUnit" Version="3.11.0" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user