mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 11:42:56 +08:00
Merge branch 'master' into user-beatmap-downloading-states-2
This commit is contained in:
commit
6e34e7d750
@ -0,0 +1,55 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
using static osu.Game.Screens.Edit.Compose.Components.Timeline.TimelineHitObjectBlueprint;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
public class TestSceneTimelineHitObjectBlueprint : TimelineTestScene
|
||||
{
|
||||
public override Drawable CreateTestComponent() => new TimelineBlueprintContainer(Composer);
|
||||
|
||||
[Test]
|
||||
public void TestDisallowZeroDurationObjects()
|
||||
{
|
||||
DragBar dragBar;
|
||||
|
||||
AddStep("add spinner", () =>
|
||||
{
|
||||
EditorBeatmap.Clear();
|
||||
EditorBeatmap.Add(new Spinner
|
||||
{
|
||||
Position = new Vector2(256, 256),
|
||||
StartTime = 150,
|
||||
Duration = 500
|
||||
});
|
||||
});
|
||||
|
||||
AddStep("hold down drag bar", () =>
|
||||
{
|
||||
// distinguishes between the actual drag bar and its "underlay shadow".
|
||||
dragBar = this.ChildrenOfType<DragBar>().Single(bar => bar.HandlePositionalInput);
|
||||
InputManager.MoveMouseTo(dragBar);
|
||||
InputManager.PressButton(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddStep("try to drag bar past start", () =>
|
||||
{
|
||||
var blueprint = this.ChildrenOfType<TimelineHitObjectBlueprint>().Single();
|
||||
InputManager.MoveMouseTo(blueprint.SelectionQuad.TopLeft - new Vector2(100, 0));
|
||||
InputManager.ReleaseButton(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddAssert("object has non-zero duration", () => EditorBeatmap.HitObjects.OfType<IHasDuration>().Single().Duration > 0);
|
||||
}
|
||||
}
|
||||
}
|
@ -23,22 +23,24 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
|
||||
protected HitObjectComposer Composer { get; private set; }
|
||||
|
||||
protected EditorBeatmap EditorBeatmap { get; private set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio)
|
||||
{
|
||||
Beatmap.Value = new WaveformTestBeatmap(audio);
|
||||
|
||||
var playable = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset);
|
||||
var editorBeatmap = new EditorBeatmap(playable);
|
||||
EditorBeatmap = new EditorBeatmap(playable);
|
||||
|
||||
Dependencies.Cache(editorBeatmap);
|
||||
Dependencies.CacheAs<IBeatSnapProvider>(editorBeatmap);
|
||||
Dependencies.Cache(EditorBeatmap);
|
||||
Dependencies.CacheAs<IBeatSnapProvider>(EditorBeatmap);
|
||||
|
||||
Composer = playable.BeatmapInfo.Ruleset.CreateInstance().CreateHitObjectComposer().With(d => d.Alpha = 0);
|
||||
|
||||
AddRange(new Drawable[]
|
||||
{
|
||||
editorBeatmap,
|
||||
EditorBeatmap,
|
||||
Composer,
|
||||
new FillFlowContainer
|
||||
{
|
||||
|
@ -143,6 +143,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
/// Tests that the same <see cref="Mod"/> instances are not shared between two playlist items.
|
||||
/// </summary>
|
||||
[Test]
|
||||
[Ignore("Temporarily disabled due to a non-trivial test failure")]
|
||||
public void TestNewItemHasNewModInstances()
|
||||
{
|
||||
AddStep("set dt mod", () => SelectedMods.Value = new[] { new OsuModDoubleTime() });
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
@ -27,11 +26,15 @@ namespace osu.Game.Online.Multiplayer
|
||||
private readonly Bindable<bool> isConnected = new Bindable<bool>();
|
||||
private readonly IBindable<APIState> apiState = new Bindable<APIState>();
|
||||
|
||||
private readonly SemaphoreSlim connectionLock = new SemaphoreSlim(1);
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
private HubConnection? connection;
|
||||
|
||||
private CancellationTokenSource connectCancelSource = new CancellationTokenSource();
|
||||
|
||||
private readonly string endpoint;
|
||||
|
||||
public MultiplayerClient(EndpointConfiguration endpoints)
|
||||
@ -52,88 +55,67 @@ namespace osu.Game.Online.Multiplayer
|
||||
{
|
||||
case APIState.Failing:
|
||||
case APIState.Offline:
|
||||
connection?.StopAsync();
|
||||
connection = null;
|
||||
Task.Run(() => disconnect(true));
|
||||
break;
|
||||
|
||||
case APIState.Online:
|
||||
Task.Run(Connect);
|
||||
Task.Run(connect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual async Task Connect()
|
||||
private async Task connect()
|
||||
{
|
||||
if (connection != null)
|
||||
return;
|
||||
cancelExistingConnect();
|
||||
|
||||
var builder = new HubConnectionBuilder()
|
||||
.WithUrl(endpoint, options => { options.Headers.Add("Authorization", $"Bearer {api.AccessToken}"); });
|
||||
if (!await connectionLock.WaitAsync(10000))
|
||||
throw new TimeoutException("Could not obtain a lock to connect. A previous attempt is likely stuck.");
|
||||
|
||||
if (RuntimeInfo.SupportsJIT)
|
||||
builder.AddMessagePackProtocol();
|
||||
else
|
||||
try
|
||||
{
|
||||
// eventually we will precompile resolvers for messagepack, but this isn't working currently
|
||||
// see https://github.com/neuecc/MessagePack-CSharp/issues/780#issuecomment-768794308.
|
||||
builder.AddNewtonsoftJsonProtocol(options => { options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; });
|
||||
}
|
||||
|
||||
connection = builder.Build();
|
||||
|
||||
// this is kind of SILLY
|
||||
// https://github.com/dotnet/aspnetcore/issues/15198
|
||||
connection.On<MultiplayerRoomState>(nameof(IMultiplayerClient.RoomStateChanged), ((IMultiplayerClient)this).RoomStateChanged);
|
||||
connection.On<MultiplayerRoomUser>(nameof(IMultiplayerClient.UserJoined), ((IMultiplayerClient)this).UserJoined);
|
||||
connection.On<MultiplayerRoomUser>(nameof(IMultiplayerClient.UserLeft), ((IMultiplayerClient)this).UserLeft);
|
||||
connection.On<int>(nameof(IMultiplayerClient.HostChanged), ((IMultiplayerClient)this).HostChanged);
|
||||
connection.On<MultiplayerRoomSettings>(nameof(IMultiplayerClient.SettingsChanged), ((IMultiplayerClient)this).SettingsChanged);
|
||||
connection.On<int, MultiplayerUserState>(nameof(IMultiplayerClient.UserStateChanged), ((IMultiplayerClient)this).UserStateChanged);
|
||||
connection.On<int, BeatmapAvailability>(nameof(IMultiplayerClient.UserBeatmapAvailabilityChanged), ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged);
|
||||
connection.On(nameof(IMultiplayerClient.LoadRequested), ((IMultiplayerClient)this).LoadRequested);
|
||||
connection.On(nameof(IMultiplayerClient.MatchStarted), ((IMultiplayerClient)this).MatchStarted);
|
||||
connection.On(nameof(IMultiplayerClient.ResultsReady), ((IMultiplayerClient)this).ResultsReady);
|
||||
connection.On<int, IEnumerable<APIMod>>(nameof(IMultiplayerClient.UserModsChanged), ((IMultiplayerClient)this).UserModsChanged);
|
||||
|
||||
connection.Closed += async ex =>
|
||||
{
|
||||
isConnected.Value = false;
|
||||
|
||||
Logger.Log(ex != null
|
||||
? $"Multiplayer client lost connection: {ex}"
|
||||
: "Multiplayer client disconnected", LoggingTarget.Network);
|
||||
|
||||
if (connection != null)
|
||||
await tryUntilConnected();
|
||||
};
|
||||
|
||||
await tryUntilConnected();
|
||||
|
||||
async Task tryUntilConnected()
|
||||
{
|
||||
Logger.Log("Multiplayer client connecting...", LoggingTarget.Network);
|
||||
|
||||
while (api.State.Value == APIState.Online)
|
||||
{
|
||||
// ensure any previous connection was disposed.
|
||||
// this will also create a new cancellation token source.
|
||||
await disconnect(false);
|
||||
|
||||
// this token will be valid for the scope of this connection.
|
||||
// if cancelled, we can be sure that a disconnect or reconnect is handled elsewhere.
|
||||
var cancellationToken = connectCancelSource.Token;
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
Logger.Log("Multiplayer client connecting...", LoggingTarget.Network);
|
||||
|
||||
try
|
||||
{
|
||||
Debug.Assert(connection != null);
|
||||
// importantly, rebuild the connection each attempt to get an updated access token.
|
||||
connection = createConnection(cancellationToken);
|
||||
|
||||
await connection.StartAsync(cancellationToken);
|
||||
|
||||
// reconnect on any failure
|
||||
await connection.StartAsync();
|
||||
Logger.Log("Multiplayer client connected!", LoggingTarget.Network);
|
||||
|
||||
// Success.
|
||||
isConnected.Value = true;
|
||||
break;
|
||||
return;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
//connection process was cancelled.
|
||||
throw;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log($"Multiplayer client connection error: {e}", LoggingTarget.Network);
|
||||
await Task.Delay(5000);
|
||||
|
||||
// retry on any failure.
|
||||
await Task.Delay(5000, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
connectionLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
protected override Task<MultiplayerRoom> JoinRoom(long roomId)
|
||||
@ -207,5 +189,86 @@ namespace osu.Game.Online.Multiplayer
|
||||
|
||||
return connection.InvokeAsync(nameof(IMultiplayerServer.StartMatch));
|
||||
}
|
||||
|
||||
private async Task disconnect(bool takeLock)
|
||||
{
|
||||
cancelExistingConnect();
|
||||
|
||||
if (takeLock)
|
||||
{
|
||||
if (!await connectionLock.WaitAsync(10000))
|
||||
throw new TimeoutException("Could not obtain a lock to disconnect. A previous attempt is likely stuck.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (connection != null)
|
||||
await connection.DisposeAsync();
|
||||
}
|
||||
finally
|
||||
{
|
||||
connection = null;
|
||||
if (takeLock)
|
||||
connectionLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelExistingConnect()
|
||||
{
|
||||
connectCancelSource.Cancel();
|
||||
connectCancelSource = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
private HubConnection createConnection(CancellationToken cancellationToken)
|
||||
{
|
||||
var builder = new HubConnectionBuilder()
|
||||
.WithUrl(endpoint, options => { options.Headers.Add("Authorization", $"Bearer {api.AccessToken}"); });
|
||||
|
||||
if (RuntimeInfo.SupportsJIT)
|
||||
builder.AddMessagePackProtocol();
|
||||
else
|
||||
{
|
||||
// eventually we will precompile resolvers for messagepack, but this isn't working currently
|
||||
// see https://github.com/neuecc/MessagePack-CSharp/issues/780#issuecomment-768794308.
|
||||
builder.AddNewtonsoftJsonProtocol(options => { options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; });
|
||||
}
|
||||
|
||||
var newConnection = builder.Build();
|
||||
|
||||
// this is kind of SILLY
|
||||
// https://github.com/dotnet/aspnetcore/issues/15198
|
||||
newConnection.On<MultiplayerRoomState>(nameof(IMultiplayerClient.RoomStateChanged), ((IMultiplayerClient)this).RoomStateChanged);
|
||||
newConnection.On<MultiplayerRoomUser>(nameof(IMultiplayerClient.UserJoined), ((IMultiplayerClient)this).UserJoined);
|
||||
newConnection.On<MultiplayerRoomUser>(nameof(IMultiplayerClient.UserLeft), ((IMultiplayerClient)this).UserLeft);
|
||||
newConnection.On<int>(nameof(IMultiplayerClient.HostChanged), ((IMultiplayerClient)this).HostChanged);
|
||||
newConnection.On<MultiplayerRoomSettings>(nameof(IMultiplayerClient.SettingsChanged), ((IMultiplayerClient)this).SettingsChanged);
|
||||
newConnection.On<int, MultiplayerUserState>(nameof(IMultiplayerClient.UserStateChanged), ((IMultiplayerClient)this).UserStateChanged);
|
||||
newConnection.On(nameof(IMultiplayerClient.LoadRequested), ((IMultiplayerClient)this).LoadRequested);
|
||||
newConnection.On(nameof(IMultiplayerClient.MatchStarted), ((IMultiplayerClient)this).MatchStarted);
|
||||
newConnection.On(nameof(IMultiplayerClient.ResultsReady), ((IMultiplayerClient)this).ResultsReady);
|
||||
newConnection.On<int, IEnumerable<APIMod>>(nameof(IMultiplayerClient.UserModsChanged), ((IMultiplayerClient)this).UserModsChanged);
|
||||
newConnection.On<int, BeatmapAvailability>(nameof(IMultiplayerClient.UserBeatmapAvailabilityChanged), ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged);
|
||||
|
||||
newConnection.Closed += ex =>
|
||||
{
|
||||
isConnected.Value = false;
|
||||
|
||||
Logger.Log(ex != null ? $"Multiplayer client lost connection: {ex}" : "Multiplayer client disconnected", LoggingTarget.Network);
|
||||
|
||||
// make sure a disconnect wasn't triggered (and this is still the active connection).
|
||||
if (!cancellationToken.IsCancellationRequested)
|
||||
Task.Run(connect, default);
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
return newConnection;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
cancelExistingConnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
@ -387,7 +388,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
case IHasDuration endTimeHitObject:
|
||||
var snappedTime = Math.Max(hitObject.StartTime, beatSnapProvider.SnapTime(time));
|
||||
|
||||
if (endTimeHitObject.EndTime == snappedTime)
|
||||
if (endTimeHitObject.EndTime == snappedTime || Precision.AlmostEquals(snappedTime, hitObject.StartTime, beatmap.GetBeatLengthAtTime(snappedTime)))
|
||||
return;
|
||||
|
||||
endTimeHitObject.Duration = snappedTime - hitObject.StartTime;
|
||||
|
@ -23,32 +23,6 @@ namespace osu.Game.Screens.Play
|
||||
/// </summary>
|
||||
public class BeatmapMetadataDisplay : Container
|
||||
{
|
||||
private class MetadataLine : Container
|
||||
{
|
||||
public MetadataLine(string left, string right)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopRight,
|
||||
Margin = new MarginPadding { Right = 5 },
|
||||
Colour = OsuColour.Gray(0.8f),
|
||||
Text = left,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopLeft,
|
||||
Margin = new MarginPadding { Left = 5 },
|
||||
Text = string.IsNullOrEmpty(right) ? @"-" : right,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private readonly WorkingBeatmap beatmap;
|
||||
private readonly Bindable<IReadOnlyList<Mod>> mods;
|
||||
private readonly Drawable facade;
|
||||
@ -144,15 +118,34 @@ namespace osu.Game.Screens.Play
|
||||
Bottom = 40
|
||||
},
|
||||
},
|
||||
new MetadataLine("Source", metadata.Source)
|
||||
new GridContainer
|
||||
{
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
},
|
||||
new MetadataLine("Mapper", metadata.AuthorString)
|
||||
{
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
},
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
new MetadataLineLabel("Source"),
|
||||
new MetadataLineInfo(metadata.Source)
|
||||
},
|
||||
new Drawable[]
|
||||
{
|
||||
new MetadataLineLabel("Mapper"),
|
||||
new MetadataLineInfo(metadata.AuthorString)
|
||||
}
|
||||
}
|
||||
},
|
||||
new ModDisplay
|
||||
{
|
||||
@ -168,5 +161,26 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
Loading = true;
|
||||
}
|
||||
|
||||
private class MetadataLineLabel : OsuSpriteText
|
||||
{
|
||||
public MetadataLineLabel(string text)
|
||||
{
|
||||
Anchor = Anchor.TopRight;
|
||||
Origin = Anchor.TopRight;
|
||||
Margin = new MarginPadding { Right = 5 };
|
||||
Colour = OsuColour.Gray(0.8f);
|
||||
Text = text;
|
||||
}
|
||||
}
|
||||
|
||||
private class MetadataLineInfo : OsuSpriteText
|
||||
{
|
||||
public MetadataLineInfo(string text)
|
||||
{
|
||||
Margin = new MarginPadding { Left = 5 };
|
||||
Text = string.IsNullOrEmpty(text) ? @"-" : text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,16 +23,17 @@ namespace osu.Game.Screens.Play
|
||||
/// </summary>
|
||||
public IBindable<bool> IsBreakTime => isBreakTime;
|
||||
|
||||
private readonly BindableBool isBreakTime = new BindableBool();
|
||||
private readonly BindableBool isBreakTime = new BindableBool(true);
|
||||
|
||||
public IReadOnlyList<BreakPeriod> Breaks
|
||||
{
|
||||
set
|
||||
{
|
||||
isBreakTime.Value = false;
|
||||
|
||||
breaks = new PeriodTracker(value.Where(b => b.HasEffect)
|
||||
.Select(b => new Period(b.StartTime, b.EndTime - BreakOverlay.BREAK_FADE_DURATION)));
|
||||
|
||||
if (IsLoaded)
|
||||
updateBreakTime();
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +46,11 @@ namespace osu.Game.Screens.Play
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
updateBreakTime();
|
||||
}
|
||||
|
||||
private void updateBreakTime()
|
||||
{
|
||||
var time = Clock.CurrentTime;
|
||||
|
||||
isBreakTime.Value = breaks?.IsInAny(time) == true
|
||||
|
@ -88,11 +88,6 @@ namespace osu.Game.Screens.Play.HUD
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
public bool PauseOnFocusLost
|
||||
{
|
||||
set => button.PauseOnFocusLost = value;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@ -120,8 +115,6 @@ namespace osu.Game.Screens.Play.HUD
|
||||
public Action HoverGained;
|
||||
public Action HoverLost;
|
||||
|
||||
private readonly IBindable<bool> gameActive = new Bindable<bool>(true);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, Framework.Game game)
|
||||
{
|
||||
@ -164,14 +157,6 @@ namespace osu.Game.Screens.Play.HUD
|
||||
};
|
||||
|
||||
bind();
|
||||
|
||||
gameActive.BindTo(game.IsActive);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
gameActive.BindValueChanged(_ => updateActive(), true);
|
||||
}
|
||||
|
||||
private void bind()
|
||||
@ -221,31 +206,6 @@ namespace osu.Game.Screens.Play.HUD
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
private bool pauseOnFocusLost = true;
|
||||
|
||||
public bool PauseOnFocusLost
|
||||
{
|
||||
set
|
||||
{
|
||||
if (pauseOnFocusLost == value)
|
||||
return;
|
||||
|
||||
pauseOnFocusLost = value;
|
||||
if (IsLoaded)
|
||||
updateActive();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateActive()
|
||||
{
|
||||
if (!pauseOnFocusLost || IsPaused.Value) return;
|
||||
|
||||
if (gameActive.Value)
|
||||
AbortConfirm();
|
||||
else
|
||||
BeginConfirm();
|
||||
}
|
||||
|
||||
public bool OnPressed(GlobalAction action)
|
||||
{
|
||||
switch (action)
|
||||
|
@ -59,6 +59,8 @@ namespace osu.Game.Screens.Play
|
||||
// We are managing our own adjustments (see OnEntering/OnExiting).
|
||||
public override bool AllowRateAdjustments => false;
|
||||
|
||||
private readonly IBindable<bool> gameActive = new Bindable<bool>(true);
|
||||
|
||||
private readonly Bindable<bool> samplePlaybackDisabled = new Bindable<bool>();
|
||||
|
||||
/// <summary>
|
||||
@ -154,6 +156,8 @@ namespace osu.Game.Screens.Play
|
||||
// replays should never be recorded or played back when autoplay is enabled
|
||||
if (!Mods.Value.Any(m => m is ModAutoplay))
|
||||
PrepareReplay();
|
||||
|
||||
gameActive.BindValueChanged(_ => updatePauseOnFocusLostState(), true);
|
||||
}
|
||||
|
||||
[CanBeNull]
|
||||
@ -187,7 +191,10 @@ namespace osu.Game.Screens.Play
|
||||
mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
|
||||
|
||||
if (game != null)
|
||||
{
|
||||
LocalUserPlaying.BindTo(game.LocalUserPlaying);
|
||||
gameActive.BindTo(game.IsActive);
|
||||
}
|
||||
|
||||
DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, Mods.Value);
|
||||
|
||||
@ -258,8 +265,6 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updateGameplayState());
|
||||
|
||||
DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true);
|
||||
|
||||
// bind clock into components that require it
|
||||
DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused);
|
||||
|
||||
@ -420,10 +425,14 @@ namespace osu.Game.Screens.Play
|
||||
samplePlaybackDisabled.Value = DrawableRuleset.FrameStableClock.IsCatchingUp.Value || GameplayClockContainer.GameplayClock.IsPaused.Value;
|
||||
}
|
||||
|
||||
private void updatePauseOnFocusLostState() =>
|
||||
HUDOverlay.HoldToQuit.PauseOnFocusLost = PauseOnFocusLost
|
||||
&& !DrawableRuleset.HasReplayLoaded.Value
|
||||
&& !breakTracker.IsBreakTime.Value;
|
||||
private void updatePauseOnFocusLostState()
|
||||
{
|
||||
if (!PauseOnFocusLost || breakTracker.IsBreakTime.Value)
|
||||
return;
|
||||
|
||||
if (gameActive.Value == false)
|
||||
Pause();
|
||||
}
|
||||
|
||||
private IBeatmap loadPlayableBeatmap()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user