2020-12-18 15:18:41 +00: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 14:10:45 +00:00
using System.Collections.Generic ;
2020-12-18 15:18:41 +00:00
using System.Diagnostics ;
using System.Threading.Tasks ;
using osu.Framework.Allocation ;
2020-12-18 16:01:09 +00:00
using osu.Framework.Bindables ;
2020-12-23 16:08:28 +00:00
using osu.Framework.Extensions.ExceptionExtensions ;
2020-12-18 15:18:41 +00:00
using osu.Framework.Logging ;
using osu.Game.Online.Multiplayer ;
2020-12-25 04:38:11 +00:00
using osu.Game.Online.Rooms ;
using osu.Game.Online.Rooms.RoomStatuses ;
2020-12-25 15:50:00 +00:00
using osu.Game.Screens.OnlinePlay.Components ;
2020-12-18 15:18:41 +00:00
2020-12-25 15:50:00 +00:00
namespace osu.Game.Screens.OnlinePlay.Multiplayer
2020-12-18 15:18:41 +00:00
{
2020-12-25 04:38:11 +00:00
public class MultiplayerRoomManager : RoomManager
2020-12-18 15:18:41 +00:00
{
[Resolved]
2021-05-20 06:39:45 +00:00
private MultiplayerClient multiplayerClient { get ; set ; }
2020-12-18 15:18:41 +00:00
2020-12-18 16:01:09 +00:00
public readonly Bindable < double > TimeBetweenListingPolls = new Bindable < double > ( ) ;
2021-01-12 07:03:12 +00:00
public readonly Bindable < double > TimeBetweenSelectionPolls = new Bindable < double > ( ) ;
2020-12-18 16:01:09 +00:00
private readonly IBindable < bool > isConnected = new Bindable < bool > ( ) ;
2020-12-18 16:57:30 +00:00
private readonly Bindable < bool > allowPolling = new Bindable < bool > ( ) ;
2020-12-18 16:01:09 +00:00
2020-12-18 17:02:04 +00:00
private ListingPollingComponent listingPollingComponent ;
2020-12-18 16:01:09 +00:00
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
isConnected . BindTo ( multiplayerClient . IsConnected ) ;
2021-01-12 10:04:16 +00:00
isConnected . BindValueChanged ( _ = > Scheduler . AddOnce ( updatePolling ) ) ;
JoinedRoom . BindValueChanged ( _ = > Scheduler . AddOnce ( updatePolling ) , true ) ;
2020-12-18 16:01:09 +00:00
}
2020-12-18 15:18:41 +00:00
public override void CreateRoom ( Room room , Action < Room > onSuccess = null , Action < string > onError = null )
2020-12-22 06:27:49 +00:00
= > base . CreateRoom ( room , r = > joinMultiplayerRoom ( r , onSuccess , onError ) , onError ) ;
2020-12-18 15:18:41 +00:00
public override void JoinRoom ( Room room , Action < Room > onSuccess = null , Action < string > onError = null )
2020-12-23 02:52:10 +00:00
{
2020-12-23 07:17:55 +00:00
if ( ! multiplayerClient . IsConnected . Value )
{
onError ? . Invoke ( "Not currently connected to the multiplayer server." ) ;
return ;
}
2020-12-23 04:57:48 +00:00
// this is done here as a pre-check to avoid clicking on already closed rooms in the lounge from triggering a server join.
// should probably be done at a higher level, but due to the current structure of things this is the easiest place for now.
2020-12-23 02:52:10 +00:00
if ( room . Status . Value is RoomStatusEnded )
{
onError ? . Invoke ( "Cannot join an ended room." ) ;
return ;
}
base . JoinRoom ( room , r = > joinMultiplayerRoom ( r , onSuccess , onError ) , onError ) ;
}
2020-12-18 15:18:41 +00:00
2020-12-18 17:02:04 +00:00
public override void PartRoom ( )
{
2020-12-21 06:38:20 +00:00
if ( JoinedRoom . Value = = null )
2020-12-18 17:02:04 +00:00
return ;
2020-12-20 09:05:43 +00:00
var joinedRoom = JoinedRoom . Value ;
2020-12-18 17:02:04 +00:00
base . PartRoom ( ) ;
2020-12-23 08:10:34 +00:00
2021-01-29 07:32:28 +00:00
multiplayerClient . LeaveRoom ( ) ;
2020-12-18 17:02:04 +00: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 15:39:31 +00:00
Schedule ( ( ) = >
{
RemoveRoom ( joinedRoom ) ;
listingPollingComponent . PollImmediately ( ) ;
} ) ;
2020-12-18 17:02:04 +00:00
}
2020-12-22 06:27:49 +00:00
private void joinMultiplayerRoom ( Room room , Action < Room > onSuccess = null , Action < string > onError = null )
2020-12-18 15:18:41 +00:00
{
Debug . Assert ( room . RoomID . Value ! = null ) ;
2020-12-23 07:56:51 +00:00
multiplayerClient . JoinRoom ( room ) . ContinueWith ( t = >
2020-12-18 15:18:41 +00:00
{
2020-12-23 07:56:51 +00:00
if ( t . IsCompletedSuccessfully )
Schedule ( ( ) = > onSuccess ? . Invoke ( room ) ) ;
2021-01-30 22:39:01 +00:00
else if ( t . IsFaulted )
2020-12-23 07:56:51 +00:00
{
2020-12-23 16:08:28 +00:00
const string message = "Failed to join multiplayer room." ;
2020-12-23 07:56:51 +00:00
if ( t . Exception ! = null )
2020-12-23 16:08:28 +00:00
Logger . Error ( t . Exception , message ) ;
2020-12-23 07:56:51 +00:00
PartRoom ( ) ;
2020-12-23 16:08:28 +00:00
Schedule ( ( ) = > onError ? . Invoke ( t . Exception ? . AsSingular ( ) . Message ? ? message ) ) ;
2020-12-23 07:56:51 +00:00
}
} ) ;
2020-12-18 15:18:41 +00:00
}
2020-12-18 16:57:30 +00:00
private void updatePolling ( )
{
if ( ! isConnected . Value )
ClearRooms ( ) ;
// Don't poll when not connected or when a room has been joined.
2020-12-20 09:05:43 +00:00
allowPolling . Value = isConnected . Value & & JoinedRoom . Value = = null ;
2020-12-18 16:57:30 +00:00
}
2020-12-20 14:10:45 +00:00
protected override IEnumerable < RoomPollingComponent > CreatePollingComponents ( ) = > new RoomPollingComponent [ ]
2020-12-18 15:18:41 +00:00
{
2020-12-25 04:38:11 +00:00
listingPollingComponent = new MultiplayerListingPollingComponent
2020-12-18 16:01:09 +00:00
{
TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls } ,
2020-12-18 16:57:30 +00:00
AllowPolling = { BindTarget = allowPolling }
2020-12-18 16:01:09 +00:00
} ,
2021-01-12 07:03:12 +00:00
new MultiplayerSelectionPollingComponent
{
TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls } ,
AllowPolling = { BindTarget = allowPolling }
}
2020-12-18 15:18:41 +00:00
} ;
2020-12-18 16:01:09 +00:00
2020-12-25 04:38:11 +00:00
private class MultiplayerListingPollingComponent : ListingPollingComponent
2020-12-18 16:01:09 +00:00
{
public readonly IBindable < bool > AllowPolling = new Bindable < bool > ( ) ;
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
2020-12-20 09:05:43 +00:00
AllowPolling . BindValueChanged ( allowPolling = >
2020-12-18 16:01:09 +00:00
{
2020-12-20 09:05:43 +00:00
if ( ! allowPolling . NewValue )
return ;
2020-12-18 16:01:09 +00:00
if ( IsLoaded )
PollImmediately ( ) ;
} ) ;
}
protected override Task Poll ( ) = > ! AllowPolling . Value ? Task . CompletedTask : base . Poll ( ) ;
}
2021-01-12 07:03:12 +00:00
private class MultiplayerSelectionPollingComponent : SelectionPollingComponent
{
public readonly IBindable < bool > AllowPolling = new Bindable < bool > ( ) ;
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
AllowPolling . BindValueChanged ( allowPolling = >
{
if ( ! allowPolling . NewValue )
return ;
if ( IsLoaded )
PollImmediately ( ) ;
} ) ;
}
protected override Task Poll ( ) = > ! AllowPolling . Value ? Task . CompletedTask : base . Poll ( ) ;
}
2020-12-18 15:18:41 +00:00
}
}