2021-04-06 20:37:21 +08:00
// 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.
2024-09-02 15:29:41 +08:00
using System.Threading ;
2021-04-06 20:37:21 +08:00
using osu.Framework.Allocation ;
using osu.Framework.Bindables ;
2024-09-02 15:29:41 +08:00
using osu.Framework.Extensions ;
2021-04-06 20:37:21 +08:00
using osu.Framework.Graphics ;
2024-09-02 15:29:41 +08:00
using osu.Game.Beatmaps ;
using osu.Game.Configuration ;
using osu.Game.Database ;
2021-04-06 20:37:21 +08:00
using osu.Game.Graphics ;
2022-11-24 15:26:57 +08:00
using osu.Game.Graphics.UserInterfaceV2 ;
2021-04-06 20:37:21 +08:00
using osu.Game.Online.Multiplayer ;
2024-09-02 15:29:41 +08:00
using osu.Game.Online.Rooms ;
2021-04-06 20:37:21 +08:00
using osuTK ;
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
{
public partial class MultiplayerSpectateButton : MultiplayerRoomComposite
{
[Resolved]
2024-09-02 15:35:10 +08:00
private OngoingOperationTracker ongoingOperationTracker { get ; set ; } = null ! ;
2021-04-06 20:37:21 +08:00
[Resolved]
2024-09-02 15:35:10 +08:00
private OsuColour colours { get ; set ; } = null ! ;
2021-04-06 20:37:21 +08:00
2024-09-02 15:35:10 +08:00
private IBindable < bool > operationInProgress = null ! ;
2021-04-06 20:37:21 +08:00
2022-11-24 15:26:57 +08:00
private readonly RoundedButton button ;
2021-04-06 20:37:21 +08:00
public MultiplayerSpectateButton ( )
{
2022-11-24 15:26:57 +08:00
InternalChild = button = new RoundedButton
2021-04-06 20:37:21 +08:00
{
RelativeSizeAxes = Axes . Both ,
Size = Vector2 . One ,
Enabled = { Value = true } ,
2022-03-17 17:36:33 +08:00
Action = onClick
2021-04-06 20:37:21 +08:00
} ;
}
2022-03-17 17:36:33 +08:00
private void onClick ( )
{
var clickOperation = ongoingOperationTracker . BeginOperation ( ) ;
2022-06-24 20:25:23 +08:00
Client . ToggleSpectate ( ) . ContinueWith ( _ = > endOperation ( ) ) ;
2022-03-17 17:36:33 +08:00
void endOperation ( ) = > clickOperation ? . Dispose ( ) ;
}
2021-04-06 20:37:21 +08:00
[BackgroundDependencyLoader]
2024-09-02 15:29:41 +08:00
private void load ( OsuConfigManager config )
2021-04-06 20:37:21 +08:00
{
operationInProgress = ongoingOperationTracker . InProgress . GetBoundCopy ( ) ;
operationInProgress . BindValueChanged ( _ = > updateState ( ) ) ;
2024-09-02 15:29:41 +08:00
automaticallyDownload = config . GetBindable < bool > ( OsuSetting . AutomaticallyDownloadMissingBeatmaps ) ;
2021-04-06 20:37:21 +08:00
}
2024-09-02 18:18:43 +08:00
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
CurrentPlaylistItem . BindValueChanged ( _ = > Scheduler . AddOnce ( checkForAutomaticDownload ) , true ) ;
}
2021-04-06 20:37:21 +08:00
protected override void OnRoomUpdated ( )
{
base . OnRoomUpdated ( ) ;
updateState ( ) ;
}
private void updateState ( )
{
2021-07-13 16:59:35 +08:00
switch ( Client . LocalUser ? . State )
2021-04-06 20:37:21 +08:00
{
default :
button . Text = "Spectate" ;
2021-04-08 14:47:55 +08:00
button . BackgroundColour = colours . BlueDark ;
2021-04-06 20:37:21 +08:00
break ;
case MultiplayerUserState . Spectating :
button . Text = "Stop spectating" ;
2021-04-08 14:47:55 +08:00
button . BackgroundColour = colours . Gray4 ;
2021-04-06 20:37:21 +08:00
break ;
}
2021-07-13 16:59:35 +08:00
button . Enabled . Value = Client . Room ! = null
& & Client . Room . State ! = MultiplayerRoomState . Closed
& & ! operationInProgress . Value ;
2024-09-02 15:29:41 +08:00
Scheduler . AddOnce ( checkForAutomaticDownload ) ;
}
#region Automatic download handling
[Resolved]
2024-09-02 15:35:10 +08:00
private BeatmapLookupCache beatmapLookupCache { get ; set ; } = null ! ;
2024-09-02 15:29:41 +08:00
[Resolved]
private BeatmapModelDownloader beatmapDownloader { get ; set ; } = null ! ;
[Resolved]
2024-09-02 15:35:10 +08:00
private BeatmapManager beatmaps { get ; set ; } = null ! ;
2024-09-02 15:29:41 +08:00
2024-09-02 15:35:10 +08:00
private Bindable < bool > automaticallyDownload = null ! ;
2024-09-02 15:29:41 +08:00
2024-09-02 15:35:10 +08:00
private CancellationTokenSource ? downloadCheckCancellation ;
2024-09-02 15:29:41 +08:00
private void checkForAutomaticDownload ( )
{
2024-09-02 18:18:43 +08:00
PlaylistItem ? currentItem = CurrentPlaylistItem . Value ;
2024-09-02 15:29:41 +08:00
downloadCheckCancellation ? . Cancel ( ) ;
2024-09-02 18:18:43 +08:00
if ( currentItem = = null )
2024-09-02 15:29:41 +08:00
return ;
if ( ! automaticallyDownload . Value )
return ;
// While we can support automatic downloads when not spectating, there are some usability concerns.
// - In host rotate mode, this could potentially be unwanted by some users (even though they want automatic downloads everywhere else).
// - When first joining a room, the expectation should be that the user is checking out the room, and they may not immediately want to download the selected beatmap.
//
// Rather than over-complicating this flow, let's only auto-download when spectating for the time being.
// A potential path forward would be to have a local auto-download checkbox above the playlist item list area.
if ( Client . LocalUser ? . State ! = MultiplayerUserState . Spectating )
return ;
// In a perfect world we'd use BeatmapAvailability, but there's no event-driven flow for when a selection changes.
// ie. if selection changes from "not downloaded" to another "not downloaded" we wouldn't get a value changed raised.
beatmapLookupCache
2024-09-02 18:18:43 +08:00
. GetBeatmapAsync ( currentItem . Beatmap . OnlineID , ( downloadCheckCancellation = new CancellationTokenSource ( ) ) . Token )
2024-09-02 15:29:41 +08:00
. ContinueWith ( resolved = > Schedule ( ( ) = >
{
var beatmapSet = resolved . GetResultSafely ( ) ? . BeatmapSet ;
if ( beatmapSet = = null )
return ;
if ( beatmaps . IsAvailableLocally ( new BeatmapSetInfo { OnlineID = beatmapSet . OnlineID } ) )
return ;
beatmapDownloader . Download ( beatmapSet ) ;
} ) ) ;
}
#endregion
2021-04-06 20:37:21 +08:00
}
}