1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-15 13:07:24 +08:00

Merge pull request #30073 from peppy/updates-outside-of-gameplay-only-2

Avoid updates (and update notifications) from appearing in more gameplay cases
This commit is contained in:
Dean Herbert 2024-10-08 15:17:09 +09:00 committed by GitHub
commit b658d9a681
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 131 additions and 78 deletions

View File

@ -6,28 +6,29 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game; using osu.Game;
using osu.Game.Screens.Play;
namespace osu.Android namespace osu.Android
{ {
public partial class GameplayScreenRotationLocker : Component public partial class GameplayScreenRotationLocker : Component
{ {
private Bindable<bool> localUserPlaying = null!; private IBindable<LocalUserPlayingState> localUserPlaying = null!;
[Resolved] [Resolved]
private OsuGameActivity gameActivity { get; set; } = null!; private OsuGameActivity gameActivity { get; set; } = null!;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuGame game) private void load(ILocalUserPlayInfo localUserPlayInfo)
{ {
localUserPlaying = game.LocalUserPlaying.GetBoundCopy(); localUserPlaying = localUserPlayInfo.PlayingState.GetBoundCopy();
localUserPlaying.BindValueChanged(updateLock, true); localUserPlaying.BindValueChanged(updateLock, true);
} }
private void updateLock(ValueChangedEvent<bool> userPlaying) private void updateLock(ValueChangedEvent<LocalUserPlayingState> userPlaying)
{ {
gameActivity.RunOnUiThread(() => gameActivity.RunOnUiThread(() =>
{ {
gameActivity.RequestedOrientation = userPlaying.NewValue ? ScreenOrientation.Locked : gameActivity.DefaultOrientation; gameActivity.RequestedOrientation = userPlaying.NewValue != LocalUserPlayingState.NotPlaying ? ScreenOrientation.Locked : gameActivity.DefaultOrientation;
}); });
} }
} }

View File

@ -25,6 +25,8 @@ namespace osu.Desktop.Updater
[Resolved] [Resolved]
private ILocalUserPlayInfo? localUserInfo { get; set; } private ILocalUserPlayInfo? localUserInfo { get; set; }
private bool isInGameplay => localUserInfo?.PlayingState.Value != LocalUserPlayingState.NotPlaying;
private UpdateInfo? pendingUpdate; private UpdateInfo? pendingUpdate;
public VelopackUpdateManager() public VelopackUpdateManager()
@ -43,7 +45,7 @@ namespace osu.Desktop.Updater
protected override async Task<bool> PerformUpdateCheck() => await checkForUpdateAsync().ConfigureAwait(false); protected override async Task<bool> PerformUpdateCheck() => await checkForUpdateAsync().ConfigureAwait(false);
private async Task<bool> checkForUpdateAsync(UpdateProgressNotification? notification = null) private async Task<bool> checkForUpdateAsync()
{ {
// whether to check again in 30 minutes. generally only if there's an error or no update was found (yet). // whether to check again in 30 minutes. generally only if there's an error or no update was found (yet).
bool scheduleRecheck = false; bool scheduleRecheck = false;
@ -51,10 +53,10 @@ namespace osu.Desktop.Updater
try try
{ {
// Avoid any kind of update checking while gameplay is running. // Avoid any kind of update checking while gameplay is running.
if (localUserInfo?.IsPlaying.Value == true) if (isInGameplay)
{ {
scheduleRecheck = true; scheduleRecheck = true;
return false; return true;
} }
// TODO: we should probably be checking if there's a more recent update, rather than shortcutting here. // TODO: we should probably be checking if there's a more recent update, rather than shortcutting here.
@ -84,27 +86,22 @@ namespace osu.Desktop.Updater
} }
// An update is found, let's notify the user and start downloading it. // An update is found, let's notify the user and start downloading it.
if (notification == null) UpdateProgressNotification notification = new UpdateProgressNotification
{ {
notification = new UpdateProgressNotification CompletionClickAction = () =>
{ {
CompletionClickAction = () => Task.Run(restartToApplyUpdate);
{ return true;
Task.Run(restartToApplyUpdate); },
return true; };
},
};
Schedule(() => notificationOverlay.Post(notification));
}
runOutsideOfGameplay(() => notificationOverlay.Post(notification));
notification.StartDownload(); notification.StartDownload();
try try
{ {
await updateManager.DownloadUpdatesAsync(pendingUpdate, p => notification.Progress = p / 100f).ConfigureAwait(false); await updateManager.DownloadUpdatesAsync(pendingUpdate, p => notification.Progress = p / 100f).ConfigureAwait(false);
runOutsideOfGameplay(() => notification.State = ProgressNotificationState.Completed);
notification.State = ProgressNotificationState.Completed;
} }
catch (Exception e) catch (Exception e)
{ {
@ -131,6 +128,17 @@ namespace osu.Desktop.Updater
return true; return true;
} }
private void runOutsideOfGameplay(Action action)
{
if (isInGameplay)
{
Scheduler.AddDelayed(() => runOutsideOfGameplay(action), 1000);
return;
}
action();
}
private async Task restartToApplyUpdate() private async Task restartToApplyUpdate()
{ {
await updateManager.WaitExitThenApplyUpdatesAsync(pendingUpdate?.TargetFullRelease).ConfigureAwait(false); await updateManager.WaitExitThenApplyUpdatesAsync(pendingUpdate?.TargetFullRelease).ConfigureAwait(false);

View File

@ -13,7 +13,7 @@ namespace osu.Desktop.Windows
public partial class GameplayWinKeyBlocker : Component public partial class GameplayWinKeyBlocker : Component
{ {
private Bindable<bool> disableWinKey = null!; private Bindable<bool> disableWinKey = null!;
private IBindable<bool> localUserPlaying = null!; private IBindable<LocalUserPlayingState> localUserPlaying = null!;
private IBindable<bool> isActive = null!; private IBindable<bool> isActive = null!;
[Resolved] [Resolved]
@ -22,7 +22,7 @@ namespace osu.Desktop.Windows
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(ILocalUserPlayInfo localUserInfo, OsuConfigManager config) private void load(ILocalUserPlayInfo localUserInfo, OsuConfigManager config)
{ {
localUserPlaying = localUserInfo.IsPlaying.GetBoundCopy(); localUserPlaying = localUserInfo.PlayingState.GetBoundCopy();
localUserPlaying.BindValueChanged(_ => updateBlocking()); localUserPlaying.BindValueChanged(_ => updateBlocking());
isActive = host.IsActive.GetBoundCopy(); isActive = host.IsActive.GetBoundCopy();
@ -34,7 +34,7 @@ namespace osu.Desktop.Windows
private void updateBlocking() private void updateBlocking()
{ {
bool shouldDisable = isActive.Value && disableWinKey.Value && localUserPlaying.Value; bool shouldDisable = isActive.Value && disableWinKey.Value && localUserPlaying.Value == LocalUserPlayingState.Playing;
if (shouldDisable) if (shouldDisable)
host.InputThread.Scheduler.Add(WindowsKey.Disable); host.InputThread.Scheduler.Add(WindowsKey.Disable);

View File

@ -22,9 +22,9 @@ namespace osu.Game.Tests.Database
[HeadlessTest] [HeadlessTest]
public partial class BackgroundDataStoreProcessorTests : OsuTestScene, ILocalUserPlayInfo public partial class BackgroundDataStoreProcessorTests : OsuTestScene, ILocalUserPlayInfo
{ {
public IBindable<bool> IsPlaying => isPlaying; public IBindable<LocalUserPlayingState> PlayingState => isPlaying;
private readonly Bindable<bool> isPlaying = new Bindable<bool>(); private readonly Bindable<LocalUserPlayingState> isPlaying = new Bindable<LocalUserPlayingState>();
private BeatmapSetInfo importedSet = null!; private BeatmapSetInfo importedSet = null!;
@ -37,7 +37,7 @@ namespace osu.Game.Tests.Database
[SetUpSteps] [SetUpSteps]
public void SetUpSteps() public void SetUpSteps()
{ {
AddStep("Set not playing", () => isPlaying.Value = false); AddStep("Set not playing", () => isPlaying.Value = LocalUserPlayingState.NotPlaying);
} }
[Test] [Test]
@ -89,7 +89,7 @@ namespace osu.Game.Tests.Database
}); });
}); });
AddStep("Set playing", () => isPlaying.Value = true); AddStep("Set playing", () => isPlaying.Value = LocalUserPlayingState.Playing);
AddStep("Reset difficulty", () => AddStep("Reset difficulty", () =>
{ {
@ -117,7 +117,7 @@ namespace osu.Game.Tests.Database
}); });
}); });
AddStep("Set not playing", () => isPlaying.Value = false); AddStep("Set not playing", () => isPlaying.Value = LocalUserPlayingState.NotPlaying);
AddUntilStep("wait for difficulties repopulated", () => AddUntilStep("wait for difficulties repopulated", () =>
{ {

View File

@ -3,11 +3,13 @@
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Input; using osu.Game.Input;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
namespace osu.Game.Tests.Input namespace osu.Game.Tests.Input
@ -15,9 +17,20 @@ namespace osu.Game.Tests.Input
[HeadlessTest] [HeadlessTest]
public partial class ConfineMouseTrackerTest : OsuGameTestScene public partial class ConfineMouseTrackerTest : OsuGameTestScene
{ {
private readonly Bindable<LocalUserPlayingState> playingState = new Bindable<LocalUserPlayingState>();
[Resolved] [Resolved]
private FrameworkConfigManager frameworkConfigManager { get; set; } = null!; private FrameworkConfigManager frameworkConfigManager { get; set; } = null!;
[SetUpSteps]
public override void SetUpSteps()
{
base.SetUpSteps();
// a bit dodgy.
AddStep("bind playing state", () => ((IBindable<LocalUserPlayingState>)playingState).BindTo(((ILocalUserPlayInfo)Game).PlayingState));
}
[TestCase(WindowMode.Windowed)] [TestCase(WindowMode.Windowed)]
[TestCase(WindowMode.Borderless)] [TestCase(WindowMode.Borderless)]
public void TestDisableConfining(WindowMode windowMode) public void TestDisableConfining(WindowMode windowMode)
@ -88,7 +101,7 @@ namespace osu.Game.Tests.Input
=> AddStep($"set {mode} game-side", () => Game.LocalConfig.SetValue(OsuSetting.ConfineMouseMode, mode)); => AddStep($"set {mode} game-side", () => Game.LocalConfig.SetValue(OsuSetting.ConfineMouseMode, mode));
private void setLocalUserPlayingTo(bool playing) private void setLocalUserPlayingTo(bool playing)
=> AddStep($"local user {(playing ? "playing" : "not playing")}", () => Game.LocalUserPlaying.Value = playing); => AddStep($"local user {(playing ? "playing" : "not playing")}", () => playingState.Value = playing ? LocalUserPlayingState.Playing : LocalUserPlayingState.NotPlaying);
private void gameSideModeIs(OsuConfineMouseMode mode) private void gameSideModeIs(OsuConfineMouseMode mode)
=> AddAssert($"mode is {mode} game-side", () => Game.LocalConfig.Get<OsuConfineMouseMode>(OsuSetting.ConfineMouseMode) == mode); => AddAssert($"mode is {mode} game-side", () => Game.LocalConfig.Get<OsuConfineMouseMode>(OsuSetting.ConfineMouseMode) == mode);

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Game.Overlays; using osu.Game.Overlays;
@ -12,7 +10,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
public partial class TestSceneOverlayActivation : OsuPlayerTestScene public partial class TestSceneOverlayActivation : OsuPlayerTestScene
{ {
protected new OverlayTestPlayer Player => base.Player as OverlayTestPlayer; protected new OverlayTestPlayer Player => (OverlayTestPlayer)base.Player;
public override void SetUpSteps() public override void SetUpSteps()
{ {

View File

@ -25,14 +25,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Cached(typeof(ILocalUserPlayInfo))] [Cached(typeof(ILocalUserPlayInfo))]
private ILocalUserPlayInfo localUserInfo; private ILocalUserPlayInfo localUserInfo;
private readonly Bindable<bool> localUserPlaying = new Bindable<bool>(); private readonly Bindable<LocalUserPlayingState> playingState = new Bindable<LocalUserPlayingState>();
private TextBox textBox => chatDisplay.ChildrenOfType<TextBox>().First(); private TextBox textBox => chatDisplay.ChildrenOfType<TextBox>().First();
public TestSceneGameplayChatDisplay() public TestSceneGameplayChatDisplay()
{ {
var mockLocalUserInfo = new Mock<ILocalUserPlayInfo>(); var mockLocalUserInfo = new Mock<ILocalUserPlayInfo>();
mockLocalUserInfo.SetupGet(i => i.IsPlaying).Returns(localUserPlaying); mockLocalUserInfo.SetupGet(i => i.PlayingState).Returns(playingState);
localUserInfo = mockLocalUserInfo.Object; localUserInfo = mockLocalUserInfo.Object;
} }
@ -124,6 +124,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddAssert($"chat {(isFocused ? "focused" : "not focused")}", () => textBox.HasFocus == isFocused); AddAssert($"chat {(isFocused ? "focused" : "not focused")}", () => textBox.HasFocus == isFocused);
private void setLocalUserPlaying(bool playing) => private void setLocalUserPlaying(bool playing) =>
AddStep($"local user {(playing ? "playing" : "not playing")}", () => localUserPlaying.Value = playing); AddStep($"local user {(playing ? "playing" : "not playing")}", () => playingState.Value = playing ? LocalUserPlayingState.Playing : LocalUserPlayingState.NotPlaying);
} }
} }

View File

@ -606,7 +606,7 @@ namespace osu.Game.Database
{ {
// Importantly, also sleep if high performance session is active. // Importantly, also sleep if high performance session is active.
// If we don't do this, memory usage can become runaway due to GC running in a more lenient mode. // If we don't do this, memory usage can become runaway due to GC running in a more lenient mode.
while (localUserPlayInfo?.IsPlaying.Value == true || highPerformanceSessionManager?.IsSessionActive == true) while (localUserPlayInfo?.PlayingState.Value != LocalUserPlayingState.NotPlaying || highPerformanceSessionManager?.IsSessionActive == true)
{ {
Logger.Log("Background processing sleeping due to active gameplay..."); Logger.Log("Background processing sleeping due to active gameplay...");
Thread.Sleep(TimeToSleepDuringGameplay); Thread.Sleep(TimeToSleepDuringGameplay);

View File

@ -15,7 +15,7 @@ namespace osu.Game.Input
{ {
/// <summary> /// <summary>
/// Connects <see cref="OsuSetting.ConfineMouseMode"/> with <see cref="FrameworkSetting.ConfineMouseMode"/>. /// Connects <see cref="OsuSetting.ConfineMouseMode"/> with <see cref="FrameworkSetting.ConfineMouseMode"/>.
/// If <see cref="OsuGame.LocalUserPlaying"/> is true, we should also confine the mouse cursor if it has been /// If <see cref="ILocalUserPlayInfo.PlayingState"/> is playing, we should also confine the mouse cursor if it has been
/// requested with <see cref="OsuConfineMouseMode.DuringGameplay"/>. /// requested with <see cref="OsuConfineMouseMode.DuringGameplay"/>.
/// </summary> /// </summary>
public partial class ConfineMouseTracker : Component public partial class ConfineMouseTracker : Component
@ -25,7 +25,7 @@ namespace osu.Game.Input
private Bindable<bool> frameworkMinimiseOnFocusLossInFullscreen; private Bindable<bool> frameworkMinimiseOnFocusLossInFullscreen;
private Bindable<OsuConfineMouseMode> osuConfineMode; private Bindable<OsuConfineMouseMode> osuConfineMode;
private IBindable<bool> localUserPlaying; private IBindable<LocalUserPlayingState> localUserPlaying;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(ILocalUserPlayInfo localUserInfo, FrameworkConfigManager frameworkConfigManager, OsuConfigManager osuConfigManager) private void load(ILocalUserPlayInfo localUserInfo, FrameworkConfigManager frameworkConfigManager, OsuConfigManager osuConfigManager)
@ -37,7 +37,7 @@ namespace osu.Game.Input
frameworkMinimiseOnFocusLossInFullscreen.BindValueChanged(_ => updateConfineMode()); frameworkMinimiseOnFocusLossInFullscreen.BindValueChanged(_ => updateConfineMode());
osuConfineMode = osuConfigManager.GetBindable<OsuConfineMouseMode>(OsuSetting.ConfineMouseMode); osuConfineMode = osuConfigManager.GetBindable<OsuConfineMouseMode>(OsuSetting.ConfineMouseMode);
localUserPlaying = localUserInfo.IsPlaying.GetBoundCopy(); localUserPlaying = localUserInfo.PlayingState.GetBoundCopy();
osuConfineMode.ValueChanged += _ => updateConfineMode(); osuConfineMode.ValueChanged += _ => updateConfineMode();
localUserPlaying.BindValueChanged(_ => updateConfineMode(), true); localUserPlaying.BindValueChanged(_ => updateConfineMode(), true);
@ -63,7 +63,7 @@ namespace osu.Game.Input
break; break;
case OsuConfineMouseMode.DuringGameplay: case OsuConfineMouseMode.DuringGameplay:
frameworkConfineMode.Value = localUserPlaying.Value ? ConfineMouseMode.Always : ConfineMouseMode.Never; frameworkConfineMode.Value = localUserPlaying.Value == LocalUserPlayingState.Playing ? ConfineMouseMode.Always : ConfineMouseMode.Never;
break; break;
case OsuConfineMouseMode.Always: case OsuConfineMouseMode.Always:

View File

@ -3,15 +3,16 @@
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Screens.Play;
using osuTK.Input; using osuTK.Input;
namespace osu.Game.Input namespace osu.Game.Input
{ {
public partial class OsuUserInputManager : UserInputManager public partial class OsuUserInputManager : UserInputManager
{ {
protected override bool AllowRightClickFromLongTouch => !LocalUserPlaying.Value; protected override bool AllowRightClickFromLongTouch => PlayingState.Value != LocalUserPlayingState.Playing;
public readonly BindableBool LocalUserPlaying = new BindableBool(); public readonly IBindable<LocalUserPlayingState> PlayingState = new Bindable<LocalUserPlayingState>();
internal OsuUserInputManager() internal OsuUserInputManager()
{ {

View File

@ -175,14 +175,9 @@ namespace osu.Game
/// </summary> /// </summary>
public readonly IBindable<OverlayActivation> OverlayActivationMode = new Bindable<OverlayActivation>(); public readonly IBindable<OverlayActivation> OverlayActivationMode = new Bindable<OverlayActivation>();
/// <summary> IBindable<LocalUserPlayingState> ILocalUserPlayInfo.PlayingState => playingState;
/// Whether the local user is currently interacting with the game in a way that should not be interrupted.
/// </summary> private readonly Bindable<LocalUserPlayingState> playingState = new Bindable<LocalUserPlayingState>();
/// <remarks>
/// This is exclusively managed by <see cref="Player"/>. If other components are mutating this state, a more
/// resilient method should be used to ensure correct state.
/// </remarks>
public Bindable<bool> LocalUserPlaying = new BindableBool();
protected OsuScreenStack ScreenStack; protected OsuScreenStack ScreenStack;
@ -302,7 +297,7 @@ namespace osu.Game
protected override UserInputManager CreateUserInputManager() protected override UserInputManager CreateUserInputManager()
{ {
var userInputManager = base.CreateUserInputManager(); var userInputManager = base.CreateUserInputManager();
(userInputManager as OsuUserInputManager)?.LocalUserPlaying.BindTo(LocalUserPlaying); (userInputManager as OsuUserInputManager)?.PlayingState.BindTo(playingState);
return userInputManager; return userInputManager;
} }
@ -391,11 +386,11 @@ namespace osu.Game
// Transfer any runtime changes back to configuration file. // Transfer any runtime changes back to configuration file.
SkinManager.CurrentSkinInfo.ValueChanged += skin => configSkin.Value = skin.NewValue.ID.ToString(); SkinManager.CurrentSkinInfo.ValueChanged += skin => configSkin.Value = skin.NewValue.ID.ToString();
LocalUserPlaying.BindValueChanged(p => playingState.BindValueChanged(p =>
{ {
BeatmapManager.PauseImports = p.NewValue; BeatmapManager.PauseImports = p.NewValue != LocalUserPlayingState.NotPlaying;
SkinManager.PauseImports = p.NewValue; SkinManager.PauseImports = p.NewValue != LocalUserPlayingState.NotPlaying;
ScoreManager.PauseImports = p.NewValue; ScoreManager.PauseImports = p.NewValue != LocalUserPlayingState.NotPlaying;
}, true); }, true);
IsActive.BindValueChanged(active => updateActiveState(active.NewValue), true); IsActive.BindValueChanged(active => updateActiveState(active.NewValue), true);
@ -1553,6 +1548,16 @@ namespace osu.Game
scope.SetTag(@"screen", newScreen?.GetType().ReadableName() ?? @"none"); scope.SetTag(@"screen", newScreen?.GetType().ReadableName() ?? @"none");
}); });
switch (current)
{
case Player player:
player.PlayingState.UnbindFrom(playingState);
// reset for sanity.
playingState.Value = LocalUserPlayingState.NotPlaying;
break;
}
switch (newScreen) switch (newScreen)
{ {
case IntroScreen intro: case IntroScreen intro:
@ -1565,14 +1570,15 @@ namespace osu.Game
versionManager?.Show(); versionManager?.Show();
break; break;
case Player player:
player.PlayingState.BindTo(playingState);
break;
default: default:
versionManager?.Hide(); versionManager?.Hide();
break; break;
} }
// reset on screen change for sanity.
LocalUserPlaying.Value = false;
if (current is IOsuScreen currentOsuScreen) if (current is IOsuScreen currentOsuScreen)
{ {
OverlayActivationMode.UnbindFrom(currentOsuScreen.OverlayActivationMode); OverlayActivationMode.UnbindFrom(currentOsuScreen.OverlayActivationMode);
@ -1621,7 +1627,5 @@ namespace osu.Game
if (newScreen == null) if (newScreen == null)
Exit(); Exit();
} }
IBindable<bool> ILocalUserPlayInfo.IsPlaying => LocalUserPlaying;
} }
} }

View File

@ -98,7 +98,7 @@ namespace osu.Game.Overlays
apiUser.BindValueChanged(_ => Schedule(() => apiUser.BindValueChanged(_ => Schedule(() =>
{ {
if (api.IsLoggedIn) if (api.IsLoggedIn)
replaceResultsAreaContent(Drawable.Empty()); replaceResultsAreaContent(Empty());
})); }));
} }

View File

@ -75,7 +75,7 @@ namespace osu.Game.Overlays.Chat
Height = LineHeight, Height = LineHeight,
Colour = colourProvider?.Background5 ?? Colour4.White, Colour = colourProvider?.Background5 ?? Colour4.White,
}, },
Drawable.Empty(), Empty(),
new OsuSpriteText new OsuSpriteText
{ {
Anchor = Anchor.CentreRight, Anchor = Anchor.CentreRight,
@ -87,7 +87,7 @@ namespace osu.Game.Overlays.Chat
} }
}, },
}, },
Drawable.Empty(), Empty(),
new Circle new Circle
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,

View File

@ -66,7 +66,7 @@ namespace osu.Game.Overlays.Settings
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
Size = new Vector2(SettingsSidebar.EXPANDED_WIDTH); Size = new Vector2(EXPANDED_WIDTH);
Padding = new MarginPadding(40); Padding = new MarginPadding(40);

View File

@ -23,9 +23,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
[CanBeNull] [CanBeNull]
private ILocalUserPlayInfo localUserInfo { get; set; } private ILocalUserPlayInfo localUserInfo { get; set; }
private readonly IBindable<bool> localUserPlaying = new Bindable<bool>(); private readonly IBindable<LocalUserPlayingState> localUserPlaying = new Bindable<LocalUserPlayingState>();
public override bool PropagatePositionalInputSubTree => !localUserPlaying.Value; public override bool PropagatePositionalInputSubTree => localUserPlaying.Value != LocalUserPlayingState.Playing;
public Bindable<bool> Expanded = new Bindable<bool>(); public Bindable<bool> Expanded = new Bindable<bool>();
@ -58,7 +58,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
base.LoadComplete(); base.LoadComplete();
if (localUserInfo != null) if (localUserInfo != null)
localUserPlaying.BindTo(localUserInfo.IsPlaying); localUserPlaying.BindTo(localUserInfo.PlayingState);
localUserPlaying.BindValueChanged(playing => localUserPlaying.BindValueChanged(playing =>
{ {
@ -67,7 +67,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
TextBox.HoldFocus = false; TextBox.HoldFocus = false;
// only hold focus (after sending a message) during breaks // only hold focus (after sending a message) during breaks
TextBox.ReleaseFocusOnCommit = playing.NewValue; TextBox.ReleaseFocusOnCommit = playing.NewValue == LocalUserPlayingState.Playing;
}, true); }, true);
Expanded.BindValueChanged(_ => updateExpandedState(), true); Expanded.BindValueChanged(_ => updateExpandedState(), true);

View File

@ -10,8 +10,8 @@ namespace osu.Game.Screens.Play
public interface ILocalUserPlayInfo public interface ILocalUserPlayInfo
{ {
/// <summary> /// <summary>
/// Whether the local user is currently playing. /// Whether the local user is currently interacting (playing) with the game in a way that should not be interrupted.
/// </summary> /// </summary>
IBindable<bool> IsPlaying { get; } IBindable<LocalUserPlayingState> PlayingState { get; }
} }
} }

View File

@ -0,0 +1,23 @@
// 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.
namespace osu.Game.Screens.Play
{
public enum LocalUserPlayingState
{
/// <summary>
/// The local player is not current in gameplay. If watching a replay, gameplay always remains in this state.
/// </summary>
NotPlaying,
/// <summary>
/// The local player is in a break, paused, or failed but still at the gameplay screen.
/// </summary>
Break,
/// <summary>
/// The local user is in active gameplay.
/// </summary>
Playing,
}
}

View File

@ -94,6 +94,7 @@ namespace osu.Game.Screens.Play
public IBindable<bool> LocalUserPlaying => localUserPlaying; public IBindable<bool> LocalUserPlaying => localUserPlaying;
private readonly Bindable<bool> localUserPlaying = new Bindable<bool>(); private readonly Bindable<bool> localUserPlaying = new Bindable<bool>();
private readonly Bindable<LocalUserPlayingState> playingState = new Bindable<LocalUserPlayingState>();
public int RestartCount; public int RestartCount;
@ -231,9 +232,6 @@ namespace osu.Game.Screens.Play
if (game != null) if (game != null)
gameActive.BindTo(game.IsActive); gameActive.BindTo(game.IsActive);
if (game is OsuGame osuGame)
LocalUserPlaying.BindTo(osuGame.LocalUserPlaying);
DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, gameplayMods); DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, gameplayMods);
dependencies.CacheAs(DrawableRuleset); dependencies.CacheAs(DrawableRuleset);
@ -510,9 +508,16 @@ namespace osu.Game.Screens.Play
private void updateGameplayState() private void updateGameplayState()
{ {
bool inGameplay = !DrawableRuleset.HasReplayLoaded.Value && !DrawableRuleset.IsPaused.Value && !breakTracker.IsBreakTime.Value && !GameplayState.HasFailed; bool inGameplay = !DrawableRuleset.HasReplayLoaded.Value && !GameplayState.HasPassed && !GameplayState.HasFailed;
OverlayActivationMode.Value = inGameplay ? OverlayActivation.Disabled : OverlayActivation.UserTriggered; bool inBreak = breakTracker.IsBreakTime.Value || DrawableRuleset.IsPaused.Value;
localUserPlaying.Value = inGameplay;
if (inGameplay)
playingState.Value = inBreak ? LocalUserPlayingState.Break : LocalUserPlayingState.Playing;
else
playingState.Value = LocalUserPlayingState.NotPlaying;
localUserPlaying.Value = playingState.Value == LocalUserPlayingState.Playing;
OverlayActivationMode.Value = playingState.Value == LocalUserPlayingState.Playing ? OverlayActivation.Disabled : OverlayActivation.UserTriggered;
} }
private void updateSampleDisabledState() private void updateSampleDisabledState()
@ -1279,6 +1284,6 @@ namespace osu.Game.Screens.Play
IBindable<bool> ISamplePlaybackDisabler.SamplePlaybackDisabled => samplePlaybackDisabled; IBindable<bool> ISamplePlaybackDisabler.SamplePlaybackDisabled => samplePlaybackDisabled;
IBindable<bool> ILocalUserPlayInfo.IsPlaying => LocalUserPlaying; public IBindable<LocalUserPlayingState> PlayingState => playingState;
} }
} }