2020-12-18 23:18:41 +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.
using System ;
2020-12-20 22:10:45 +08:00
using System.Collections.Generic ;
2020-12-18 23:18:41 +08:00
using System.Diagnostics ;
using System.Threading.Tasks ;
using osu.Framework.Allocation ;
2020-12-19 00:01:09 +08:00
using osu.Framework.Bindables ;
2020-12-18 23:18:41 +08:00
using osu.Framework.Logging ;
using osu.Game.Online.Multiplayer ;
using osu.Game.Online.RealtimeMultiplayer ;
using osu.Game.Screens.Multi.Components ;
namespace osu.Game.Screens.Multi.RealtimeMultiplayer
{
public class RealtimeRoomManager : RoomManager
{
[Resolved]
private StatefulMultiplayerClient multiplayerClient { get ; set ; }
2020-12-19 00:01:09 +08:00
public readonly Bindable < double > TimeBetweenListingPolls = new Bindable < double > ( ) ;
public readonly Bindable < double > TimeBetweenSelectionPolls = new Bindable < double > ( ) ;
private readonly IBindable < bool > isConnected = new Bindable < bool > ( ) ;
2020-12-19 00:57:30 +08:00
private readonly Bindable < bool > allowPolling = new Bindable < bool > ( ) ;
2020-12-19 00:01:09 +08:00
2020-12-19 01:02:04 +08:00
private ListingPollingComponent listingPollingComponent ;
2020-12-19 00:01:09 +08:00
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
isConnected . BindTo ( multiplayerClient . IsConnected ) ;
2020-12-20 17:05:43 +08:00
isConnected . BindValueChanged ( _ = > Schedule ( updatePolling ) ) ;
JoinedRoom . BindValueChanged ( _ = > updatePolling ( ) ) ;
2020-12-19 00:57:30 +08:00
updatePolling ( ) ;
2020-12-19 00:01:09 +08:00
}
2020-12-18 23:18:41 +08:00
public override void CreateRoom ( Room room , Action < Room > onSuccess = null , Action < string > onError = null )
2020-12-22 14:27:49 +08:00
= > base . CreateRoom ( room , r = > joinMultiplayerRoom ( r , onSuccess , onError ) , onError ) ;
2020-12-18 23:18:41 +08:00
public override void JoinRoom ( Room room , Action < Room > onSuccess = null , Action < string > onError = null )
2020-12-22 14:27:49 +08:00
= > base . JoinRoom ( room , r = > joinMultiplayerRoom ( r , onSuccess , onError ) , onError ) ;
2020-12-18 23:18:41 +08:00
2020-12-19 01:02:04 +08:00
public override void PartRoom ( )
{
2020-12-21 14:38:20 +08:00
if ( JoinedRoom . Value = = null )
2020-12-19 01:02:04 +08:00
return ;
2020-12-20 17:05:43 +08:00
var joinedRoom = JoinedRoom . Value ;
2020-12-19 01:02:04 +08:00
base . PartRoom ( ) ;
2020-12-20 21:51:33 +08:00
multiplayerClient . LeaveRoom ( ) ;
2020-12-19 01:02:04 +08:00
// Todo: This is not the way to do this. Basically when we're the only participant and the room closes, there's no way to know if this is actually the case.
// This is delayed one frame because upon exiting the match subscreen, multiplayer updates the polling rate and messes with polling.
2020-12-20 23:39:31 +08:00
Schedule ( ( ) = >
{
RemoveRoom ( joinedRoom ) ;
listingPollingComponent . PollImmediately ( ) ;
} ) ;
2020-12-19 01:02:04 +08:00
}
2020-12-22 14:27:49 +08:00
private void joinMultiplayerRoom ( Room room , Action < Room > onSuccess = null , Action < string > onError = null )
2020-12-18 23:18:41 +08:00
{
Debug . Assert ( room . RoomID . Value ! = null ) ;
var joinTask = multiplayerClient . JoinRoom ( room ) ;
2020-12-20 17:03:30 +08:00
joinTask . ContinueWith ( _ = > onSuccess ? . Invoke ( room ) , TaskContinuationOptions . OnlyOnRanToCompletion ) ;
2020-12-18 23:18:41 +08:00
joinTask . ContinueWith ( t = >
{
PartRoom ( ) ;
if ( t . Exception ! = null )
Logger . Error ( t . Exception , "Failed to join multiplayer room." ) ;
2020-12-22 14:27:49 +08:00
onError ? . Invoke ( t . Exception ? . ToString ( ) ? ? string . Empty ) ;
2020-12-18 23:18:41 +08:00
} , TaskContinuationOptions . NotOnRanToCompletion ) ;
}
2020-12-19 00:57:30 +08:00
private void updatePolling ( )
{
if ( ! isConnected . Value )
ClearRooms ( ) ;
// Don't poll when not connected or when a room has been joined.
2020-12-20 17:05:43 +08:00
allowPolling . Value = isConnected . Value & & JoinedRoom . Value = = null ;
2020-12-19 00:57:30 +08:00
}
2020-12-20 22:10:45 +08:00
protected override IEnumerable < RoomPollingComponent > CreatePollingComponents ( ) = > new RoomPollingComponent [ ]
2020-12-18 23:18:41 +08:00
{
2020-12-19 01:02:04 +08:00
listingPollingComponent = new RealtimeListingPollingComponent
2020-12-19 00:01:09 +08:00
{
TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls } ,
2020-12-19 00:57:30 +08:00
AllowPolling = { BindTarget = allowPolling }
2020-12-19 00:01:09 +08:00
} ,
new RealtimeSelectionPollingComponent
{
TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls } ,
2020-12-19 00:57:30 +08:00
AllowPolling = { BindTarget = allowPolling }
2020-12-19 00:01:09 +08:00
}
2020-12-18 23:18:41 +08:00
} ;
2020-12-19 00:01:09 +08:00
private class RealtimeListingPollingComponent : ListingPollingComponent
{
public readonly IBindable < bool > AllowPolling = new Bindable < bool > ( ) ;
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
2020-12-20 17:05:43 +08:00
AllowPolling . BindValueChanged ( allowPolling = >
2020-12-19 00:01:09 +08:00
{
2020-12-20 17:05:43 +08:00
if ( ! allowPolling . NewValue )
return ;
2020-12-19 00:01:09 +08:00
if ( IsLoaded )
PollImmediately ( ) ;
} ) ;
}
protected override Task Poll ( ) = > ! AllowPolling . Value ? Task . CompletedTask : base . Poll ( ) ;
}
private class RealtimeSelectionPollingComponent : SelectionPollingComponent
{
public readonly IBindable < bool > AllowPolling = new Bindable < bool > ( ) ;
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
2020-12-20 17:05:43 +08:00
AllowPolling . BindValueChanged ( allowPolling = >
2020-12-19 00:01:09 +08:00
{
2020-12-20 17:05:43 +08:00
if ( ! allowPolling . NewValue )
return ;
2020-12-19 00:01:09 +08:00
if ( IsLoaded )
PollImmediately ( ) ;
} ) ;
}
protected override Task Poll ( ) = > ! AllowPolling . Value ? Task . CompletedTask : base . Poll ( ) ;
}
2020-12-18 23:18:41 +08:00
}
}