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-24 00:08:28 +08:00
using osu.Framework.Extensions.ExceptionExtensions ;
2020-12-18 23:18:41 +08:00
using osu.Framework.Logging ;
using osu.Game.Online.Multiplayer ;
2020-12-25 12:38:11 +08:00
using osu.Game.Online.Rooms ;
using osu.Game.Online.Rooms.RoomStatuses ;
2020-12-25 23:50:00 +08:00
using osu.Game.Screens.OnlinePlay.Components ;
2020-12-18 23:18:41 +08:00
2020-12-25 23:50:00 +08:00
namespace osu.Game.Screens.OnlinePlay.Multiplayer
2020-12-18 23:18:41 +08:00
{
2020-12-25 12:38:11 +08:00
public class MultiplayerRoomManager : RoomManager
2020-12-18 23:18:41 +08:00
{
[Resolved]
2021-05-20 14:39:45 +08:00
private MultiplayerClient multiplayerClient { get ; set ; }
2020-12-18 23:18:41 +08:00
2020-12-19 00:01:09 +08:00
public readonly Bindable < double > TimeBetweenListingPolls = new Bindable < double > ( ) ;
2021-01-12 15:03:12 +08:00
public readonly Bindable < double > TimeBetweenSelectionPolls = new Bindable < double > ( ) ;
2020-12-19 00:01:09 +08:00
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 ) ;
2021-01-12 18:04:16 +08:00
isConnected . BindValueChanged ( _ = > Scheduler . AddOnce ( updatePolling ) ) ;
JoinedRoom . BindValueChanged ( _ = > Scheduler . AddOnce ( updatePolling ) , true ) ;
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 )
2021-07-10 15:08:12 +08:00
= > base . CreateRoom ( room , r = > joinMultiplayerRoom ( r , r . Password . Value , onSuccess , onError ) , onError ) ;
2020-12-18 23:18:41 +08:00
2021-07-10 15:08:12 +08:00
public override void JoinRoom ( Room room , string password = null , Action < Room > onSuccess = null , Action < string > onError = null )
2020-12-23 10:52:10 +08:00
{
2020-12-23 15:17:55 +08:00
if ( ! multiplayerClient . IsConnected . Value )
{
onError ? . Invoke ( "Not currently connected to the multiplayer server." ) ;
return ;
}
2020-12-23 12:57:48 +08: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 10:52:10 +08:00
if ( room . Status . Value is RoomStatusEnded )
{
onError ? . Invoke ( "Cannot join an ended room." ) ;
return ;
}
2021-07-10 15:08:12 +08:00
base . JoinRoom ( room , password , r = > joinMultiplayerRoom ( r , password , onSuccess , onError ) , onError ) ;
2020-12-23 10:52:10 +08:00
}
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-23 16:10:34 +08:00
2021-01-29 15:32:28 +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
}
2021-07-10 15:08:12 +08:00
private void joinMultiplayerRoom ( Room room , string password , Action < Room > onSuccess = null , Action < string > onError = null )
2020-12-18 23:18:41 +08:00
{
Debug . Assert ( room . RoomID . Value ! = null ) ;
2021-07-10 15:08:12 +08:00
multiplayerClient . JoinRoom ( room , password ) . ContinueWith ( t = >
2020-12-18 23:18:41 +08:00
{
2020-12-23 15:56:51 +08:00
if ( t . IsCompletedSuccessfully )
Schedule ( ( ) = > onSuccess ? . Invoke ( room ) ) ;
2021-01-31 06:39:01 +08:00
else if ( t . IsFaulted )
2020-12-23 15:56:51 +08:00
{
2020-12-24 00:08:28 +08:00
const string message = "Failed to join multiplayer room." ;
2020-12-23 15:56:51 +08:00
if ( t . Exception ! = null )
2020-12-24 00:08:28 +08:00
Logger . Error ( t . Exception , message ) ;
2020-12-23 15:56:51 +08:00
PartRoom ( ) ;
2020-12-24 00:08:28 +08:00
Schedule ( ( ) = > onError ? . Invoke ( t . Exception ? . AsSingular ( ) . Message ? ? message ) ) ;
2020-12-23 15:56:51 +08:00
}
} ) ;
2020-12-18 23:18:41 +08:00
}
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-25 12:38:11 +08:00
listingPollingComponent = new MultiplayerListingPollingComponent
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
} ,
2021-01-12 15:03:12 +08:00
new MultiplayerSelectionPollingComponent
{
TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls } ,
AllowPolling = { BindTarget = allowPolling }
}
2020-12-18 23:18:41 +08:00
} ;
2020-12-19 00:01:09 +08:00
2020-12-25 12:38:11 +08:00
private class MultiplayerListingPollingComponent : ListingPollingComponent
2020-12-19 00:01:09 +08:00
{
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 ( ) ;
}
2021-01-12 15:03:12 +08: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 23:18:41 +08:00
}
}