1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-21 18:42:56 +08:00

Move listing polling component to LoungeSubScreen

This commit is contained in:
smoogipoo 2021-08-13 17:39:09 +09:00
parent df24f7a81e
commit 8910781bcd
19 changed files with 308 additions and 451 deletions

View File

@ -74,7 +74,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
var room = RoomManager.Rooms[1]; var room = RoomManager.Rooms[1];
RoomManager.RemoveRoom(room); RoomManager.RemoveRoom(room);
RoomManager.AddRoom(room); RoomManager.AddOrUpdateRoom(room);
}); });
AddAssert("no selection", () => checkRoomSelected(null)); AddAssert("no selection", () => checkRoomSelected(null));

View File

@ -1,157 +1,157 @@
// 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.
//
using System; // using System;
using NUnit.Framework; // using NUnit.Framework;
using osu.Framework.Testing; // using osu.Framework.Testing;
using osu.Game.Online.Rooms; // using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Components; // using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Tests.Beatmaps; // using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Visual.OnlinePlay; // using osu.Game.Tests.Visual.OnlinePlay;
//
namespace osu.Game.Tests.Visual.Multiplayer // namespace osu.Game.Tests.Visual.Multiplayer
{ // {
[HeadlessTest] // [HeadlessTest]
public class TestSceneMultiplayerRoomManager : MultiplayerTestScene // public class TestSceneMultiplayerRoomManager : MultiplayerTestScene
{ // {
protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies(); // protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies();
//
public TestSceneMultiplayerRoomManager() // public TestSceneMultiplayerRoomManager()
: base(false) // : base(false)
{ // {
} // }
//
[Test] // [Test]
public void TestPollsInitially() // public void TestPollsInitially()
{ // {
AddStep("create room manager with a few rooms", () => // AddStep("create room manager with a few rooms", () =>
{ // {
RoomManager.CreateRoom(createRoom(r => r.Name.Value = "1")); // RoomManager.CreateRoom(createRoom(r => r.Name.Value = "1"));
RoomManager.PartRoom(); // RoomManager.PartRoom();
RoomManager.CreateRoom(createRoom(r => r.Name.Value = "2")); // RoomManager.CreateRoom(createRoom(r => r.Name.Value = "2"));
RoomManager.PartRoom(); // RoomManager.PartRoom();
RoomManager.ClearRooms(); // RoomManager.ClearRooms();
}); // });
//
AddAssert("manager polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 2); // AddAssert("manager polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 2);
AddAssert("initial rooms received", () => RoomManager.InitialRoomsReceived.Value); // AddAssert("initial rooms received", () => RoomManager.InitialRoomsReceived.Value);
} // }
//
[Test] // [Test]
public void TestRoomsClearedOnDisconnection() // public void TestRoomsClearedOnDisconnection()
{ // {
AddStep("create room manager with a few rooms", () => // AddStep("create room manager with a few rooms", () =>
{ // {
RoomManager.CreateRoom(createRoom()); // RoomManager.CreateRoom(createRoom());
RoomManager.PartRoom(); // RoomManager.PartRoom();
RoomManager.CreateRoom(createRoom()); // RoomManager.CreateRoom(createRoom());
RoomManager.PartRoom(); // RoomManager.PartRoom();
}); // });
//
AddStep("disconnect", () => Client.Disconnect()); // AddStep("disconnect", () => Client.Disconnect());
//
AddAssert("rooms cleared", () => ((RoomManager)RoomManager).Rooms.Count == 0); // AddAssert("rooms cleared", () => ((RoomManager)RoomManager).Rooms.Count == 0);
AddAssert("initial rooms not received", () => !RoomManager.InitialRoomsReceived.Value); // AddAssert("initial rooms not received", () => !RoomManager.InitialRoomsReceived.Value);
} // }
//
[Test] // [Test]
public void TestRoomsPolledOnReconnect() // public void TestRoomsPolledOnReconnect()
{ // {
AddStep("create room manager with a few rooms", () => // AddStep("create room manager with a few rooms", () =>
{ // {
RoomManager.CreateRoom(createRoom()); // RoomManager.CreateRoom(createRoom());
RoomManager.PartRoom(); // RoomManager.PartRoom();
RoomManager.CreateRoom(createRoom()); // RoomManager.CreateRoom(createRoom());
RoomManager.PartRoom(); // RoomManager.PartRoom();
}); // });
//
AddStep("disconnect", () => Client.Disconnect()); // AddStep("disconnect", () => Client.Disconnect());
AddStep("connect", () => Client.Connect()); // AddStep("connect", () => Client.Connect());
//
AddAssert("manager polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 2); // AddAssert("manager polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 2);
AddAssert("initial rooms received", () => RoomManager.InitialRoomsReceived.Value); // AddAssert("initial rooms received", () => RoomManager.InitialRoomsReceived.Value);
} // }
//
[Test] // [Test]
public void TestRoomsNotPolledWhenJoined() // public void TestRoomsNotPolledWhenJoined()
{ // {
AddStep("create room manager with a room", () => // AddStep("create room manager with a room", () =>
{ // {
RoomManager.CreateRoom(createRoom()); // RoomManager.CreateRoom(createRoom());
RoomManager.ClearRooms(); // RoomManager.ClearRooms();
}); // });
//
AddAssert("manager not polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 0); // AddAssert("manager not polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 0);
AddAssert("initial rooms not received", () => !RoomManager.InitialRoomsReceived.Value); // AddAssert("initial rooms not received", () => !RoomManager.InitialRoomsReceived.Value);
} // }
//
[Test] // [Test]
public void TestMultiplayerRoomJoinedWhenCreated() // public void TestMultiplayerRoomJoinedWhenCreated()
{ // {
AddStep("create room manager with a room", () => // AddStep("create room manager with a room", () =>
{ // {
RoomManager.CreateRoom(createRoom()); // RoomManager.CreateRoom(createRoom());
}); // });
//
AddUntilStep("multiplayer room joined", () => Client.Room != null); // AddUntilStep("multiplayer room joined", () => Client.Room != null);
} // }
//
[Test] // [Test]
public void TestMultiplayerRoomPartedWhenAPIRoomParted() // public void TestMultiplayerRoomPartedWhenAPIRoomParted()
{ // {
AddStep("create room manager with a room", () => // AddStep("create room manager with a room", () =>
{ // {
RoomManager.CreateRoom(createRoom()); // RoomManager.CreateRoom(createRoom());
RoomManager.PartRoom(); // RoomManager.PartRoom();
}); // });
//
AddAssert("multiplayer room parted", () => Client.Room == null); // AddAssert("multiplayer room parted", () => Client.Room == null);
} // }
//
[Test] // [Test]
public void TestMultiplayerRoomJoinedWhenAPIRoomJoined() // public void TestMultiplayerRoomJoinedWhenAPIRoomJoined()
{ // {
AddStep("create room manager with a room", () => // AddStep("create room manager with a room", () =>
{ // {
var r = createRoom(); // var r = createRoom();
RoomManager.CreateRoom(r); // RoomManager.CreateRoom(r);
RoomManager.PartRoom(); // RoomManager.PartRoom();
RoomManager.JoinRoom(r); // RoomManager.JoinRoom(r);
}); // });
//
AddUntilStep("multiplayer room joined", () => Client.Room != null); // AddUntilStep("multiplayer room joined", () => Client.Room != null);
} // }
//
private Room createRoom(Action<Room> initFunc = null) // private Room createRoom(Action<Room> initFunc = null)
{ // {
var room = new Room // var room = new Room
{ // {
Name = // Name =
{ // {
Value = "test room" // Value = "test room"
}, // },
Playlist = // Playlist =
{ // {
new PlaylistItem // new PlaylistItem
{ // {
Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo }, // Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo },
Ruleset = { Value = Ruleset.Value } // Ruleset = { Value = Ruleset.Value }
} // }
} // }
}; // };
//
initFunc?.Invoke(room); // initFunc?.Invoke(room);
return room; // return room;
} // }
//
private class TestDependencies : MultiplayerTestSceneDependencies // private class TestDependencies : MultiplayerTestSceneDependencies
{ // {
public TestDependencies() // public TestDependencies()
{ // {
// Need to set these values as early as possible. // // Need to set these values as early as possible.
RoomManager.TimeBetweenListingPolls.Value = 1; // RoomManager.TimeBetweenListingPolls.Value = 1;
RoomManager.TimeBetweenSelectionPolls.Value = 1; // RoomManager.TimeBetweenSelectionPolls.Value = 1;
} // }
} // }
} // }
} // }

View File

@ -141,6 +141,18 @@ namespace osu.Game.Tests.Visual.Playlists
public IBindableList<Room> Rooms => null; public IBindableList<Room> Rooms => null;
public void AddOrUpdateRoom(Room room)
{
}
public void RemoveRoom(Room room)
{
}
public void ClearRooms()
{
}
public void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null) public void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
{ {
if (CreateRequested == null) if (CreateRequested == null)

View File

@ -134,7 +134,7 @@ namespace osu.Game.Online.Rooms
/// The position of this <see cref="Room"/> in the list. This is not read from or written to the API. /// The position of this <see cref="Room"/> in the list. This is not read from or written to the API.
/// </summary> /// </summary>
[JsonIgnore] [JsonIgnore]
public readonly Bindable<int> Position = new Bindable<int>(-1); public readonly Bindable<long> Position = new Bindable<long>(-1);
public Room() public Room()
{ {

View File

@ -1,6 +1,7 @@
// 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.
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@ -14,6 +15,9 @@ namespace osu.Game.Screens.OnlinePlay.Components
/// </summary> /// </summary>
public class ListingPollingComponent : RoomPollingComponent public class ListingPollingComponent : RoomPollingComponent
{ {
public IBindable<bool> HasPolledOnce => hasPolledOnce;
private readonly Bindable<bool> hasPolledOnce = new Bindable<bool>();
[Resolved] [Resolved]
private Bindable<FilterCriteria> currentFilter { get; set; } private Bindable<FilterCriteria> currentFilter { get; set; }
@ -25,7 +29,9 @@ namespace osu.Game.Screens.OnlinePlay.Components
{ {
currentFilter.BindValueChanged(_ => currentFilter.BindValueChanged(_ =>
{ {
NotifyRoomsReceived(null); RoomManager.ClearRooms();
hasPolledOnce.Value = false;
if (IsLoaded) if (IsLoaded)
PollImmediately(); PollImmediately();
}); });
@ -45,17 +51,16 @@ namespace osu.Game.Screens.OnlinePlay.Components
pollReq.Success += result => pollReq.Success += result =>
{ {
for (int i = 0; i < result.Count; i++) foreach (var existing in RoomManager.Rooms.ToArray())
{ {
if (result[i].RoomID.Value == selectedRoom.Value?.RoomID.Value) if (result.All(r => r.RoomID.Value != existing.RoomID.Value))
{ RoomManager.RemoveRoom(existing);
// The listing request always has less information than the opened room, so don't include it.
result[i] = selectedRoom.Value;
break;
}
} }
NotifyRoomsReceived(result); foreach (var incoming in result)
RoomManager.AddOrUpdateRoom(incoming);
hasPolledOnce.Value = true;
tcs.SetResult(true); tcs.SetResult(true);
}; };

View File

@ -8,7 +8,6 @@ using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Online.API; using osu.Game.Online.API;
@ -17,15 +16,12 @@ using osu.Game.Rulesets;
namespace osu.Game.Screens.OnlinePlay.Components namespace osu.Game.Screens.OnlinePlay.Components
{ {
public abstract class RoomManager : CompositeDrawable, IRoomManager public class RoomManager : Component, IRoomManager
{ {
public event Action RoomsUpdated; public event Action RoomsUpdated;
private readonly BindableList<Room> rooms = new BindableList<Room>(); private readonly BindableList<Room> rooms = new BindableList<Room>();
public IBindable<bool> InitialRoomsReceived => initialRoomsReceived;
private readonly Bindable<bool> initialRoomsReceived = new Bindable<bool>();
public IBindableList<Room> Rooms => rooms; public IBindableList<Room> Rooms => rooms;
protected IBindable<Room> JoinedRoom => joinedRoom; protected IBindable<Room> JoinedRoom => joinedRoom;
@ -40,15 +36,9 @@ namespace osu.Game.Screens.OnlinePlay.Components
[Resolved] [Resolved]
private IAPIProvider api { get; set; } private IAPIProvider api { get; set; }
protected RoomManager() public RoomManager()
{ {
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
InternalChildren = CreatePollingComponents().Select(p =>
{
p.RoomsReceived = onRoomsReceived;
return p;
}).ToList();
} }
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
@ -118,56 +108,41 @@ namespace osu.Game.Screens.OnlinePlay.Components
private readonly HashSet<long> ignoredRooms = new HashSet<long>(); private readonly HashSet<long> ignoredRooms = new HashSet<long>();
private void onRoomsReceived(List<Room> received) public void AddOrUpdateRoom(Room room)
{ {
if (received == null) Debug.Assert(room.RoomID.Value != null);
{
ClearRooms(); if (ignoredRooms.Contains(room.RoomID.Value.Value))
return; return;
}
// Remove past matches room.Position.Value = -room.RoomID.Value.Value;
foreach (var r in rooms.ToList())
try
{ {
if (received.All(e => e.RoomID.Value != r.RoomID.Value)) update(room, room);
rooms.Remove(r); addRoom(room);
} }
catch (Exception ex)
for (int i = 0; i < received.Count; i++)
{ {
var room = received[i]; Logger.Error(ex, $"Failed to update room: {room.Name.Value}.");
Debug.Assert(room.RoomID.Value != null); ignoredRooms.Add(room.RoomID.Value.Value);
rooms.Remove(room);
if (ignoredRooms.Contains(room.RoomID.Value.Value))
continue;
room.Position.Value = i;
try
{
update(room, room);
addRoom(room);
}
catch (Exception ex)
{
Logger.Error(ex, $"Failed to update room: {room.Name.Value}.");
ignoredRooms.Add(room.RoomID.Value.Value);
rooms.Remove(room);
}
} }
RoomsUpdated?.Invoke(); notifyRoomsUpdated();
initialRoomsReceived.Value = true;
} }
protected void RemoveRoom(Room room) => rooms.Remove(room); public void RemoveRoom(Room room)
{
rooms.Remove(room);
notifyRoomsUpdated();
}
protected void ClearRooms() public void ClearRooms()
{ {
rooms.Clear(); rooms.Clear();
initialRoomsReceived.Value = false; notifyRoomsUpdated();
} }
/// <summary> /// <summary>
@ -196,6 +171,6 @@ namespace osu.Game.Screens.OnlinePlay.Components
existing.CopyFrom(room); existing.CopyFrom(room);
} }
protected abstract IEnumerable<RoomPollingComponent> CreatePollingComponents(); private void notifyRoomsUpdated() => Scheduler.AddOnce(() => RoomsUpdated?.Invoke());
} }
} }

View File

@ -1,29 +1,18 @@
// 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.
using System;
using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Online; using osu.Game.Online;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.Rooms;
namespace osu.Game.Screens.OnlinePlay.Components namespace osu.Game.Screens.OnlinePlay.Components
{ {
public abstract class RoomPollingComponent : PollingComponent public abstract class RoomPollingComponent : PollingComponent
{ {
/// <summary>
/// Invoked when any <see cref="Room"/>s have been received from the API.
/// <para>
/// Any <see cref="Room"/>s present locally but not returned by this event are to be removed from display.
/// If null, the display of local rooms is reset to an initial state.
/// </para>
/// </summary>
public Action<List<Room>> RoomsReceived;
[Resolved] [Resolved]
protected IAPIProvider API { get; private set; } protected IAPIProvider API { get; private set; }
protected void NotifyRoomsReceived(List<Room> rooms) => RoomsReceived?.Invoke(rooms); [Resolved]
protected IRoomManager RoomManager { get; set; }
} }
} }

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.
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@ -48,17 +46,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
pollReq.Success += result => pollReq.Success += result =>
{ {
// existing rooms need to be ordered by their position because the received of NotifyRoomsReceives expects to be able to sort them based on this order. RoomManager.AddOrUpdateRoom(result);
var rooms = new List<Room>(roomManager.Rooms.OrderBy(r => r.Position.Value));
int index = rooms.FindIndex(r => r.RoomID.Value == result.RoomID.Value);
if (index < 0)
return;
rooms[index] = result;
NotifyRoomsReceived(rooms);
tcs.SetResult(true); tcs.SetResult(true);
}; };

View File

@ -18,16 +18,17 @@ namespace osu.Game.Screens.OnlinePlay
/// </summary> /// </summary>
event Action RoomsUpdated; event Action RoomsUpdated;
/// <summary>
/// Whether an initial listing of rooms has been received.
/// </summary>
IBindable<bool> InitialRoomsReceived { get; }
/// <summary> /// <summary>
/// All the active <see cref="Room"/>s. /// All the active <see cref="Room"/>s.
/// </summary> /// </summary>
IBindableList<Room> Rooms { get; } IBindableList<Room> Rooms { get; }
void AddOrUpdateRoom(Room room);
void RemoveRoom(Room room);
void ClearRooms();
/// <summary> /// <summary>
/// Creates a new <see cref="Room"/>. /// Creates a new <see cref="Room"/>.
/// </summary> /// </summary>

View File

@ -12,14 +12,17 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Logging;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Input;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Screens.OnlinePlay.Match;
using osu.Game.Users; using osu.Game.Users;
@ -41,10 +44,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
AutoSizeAxes = Axes.Both AutoSizeAxes = Axes.Both
}; };
private readonly IBindable<bool> initialRoomsReceived = new Bindable<bool>(); protected ListingPollingComponent ListingPollingComponent { get; private set; }
private readonly IBindable<bool> operationInProgress = new Bindable<bool>();
private LoadingLayer loadingLayer;
[Resolved] [Resolved]
private Bindable<Room> selectedRoom { get; set; } private Bindable<Room> selectedRoom { get; set; }
@ -61,9 +61,15 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
[Resolved] [Resolved]
private IBindable<RulesetInfo> ruleset { get; set; } private IBindable<RulesetInfo> ruleset { get; set; }
[Resolved(CanBeNull = true)]
private IdleTracker idleTracker { get; set; }
[CanBeNull] [CanBeNull]
private IDisposable joiningRoomOperation { get; set; } private IDisposable joiningRoomOperation { get; set; }
private readonly IBindable<bool> operationInProgress = new Bindable<bool>();
private readonly IBindable<bool> isIdle = new BindableBool();
private LoadingLayer loadingLayer;
private RoomsContainer roomsContainer; private RoomsContainer roomsContainer;
private SearchTextBox searchTextBox; private SearchTextBox searchTextBox;
private Dropdown<RoomStatusFilter> statusDropdown; private Dropdown<RoomStatusFilter> statusDropdown;
@ -77,6 +83,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
ListingPollingComponent = CreatePollingComponent(),
new Container new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
@ -176,8 +183,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
searchTextBox.Current.BindValueChanged(_ => updateFilterDebounced()); searchTextBox.Current.BindValueChanged(_ => updateFilterDebounced());
ruleset.BindValueChanged(_ => UpdateFilter()); ruleset.BindValueChanged(_ => UpdateFilter());
initialRoomsReceived.BindTo(RoomManager.InitialRoomsReceived); ListingPollingComponent.HasPolledOnce.BindValueChanged(_ => updateLoadingLayer());
initialRoomsReceived.BindValueChanged(_ => updateLoadingLayer());
if (idleTracker != null)
{
isIdle.BindTo(idleTracker.IsIdle);
isIdle.BindValueChanged(_ => updatePollingRate(), true);
}
if (ongoingOperationTracker != null) if (ongoingOperationTracker != null)
{ {
@ -231,7 +243,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
public override void OnEntering(IScreen last) public override void OnEntering(IScreen last)
{ {
base.OnEntering(last); base.OnEntering(last);
onReturning(); onReturning();
} }
@ -266,11 +277,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
private void onReturning() private void onReturning()
{ {
updatePollingRate();
searchTextBox.HoldFocus = true; searchTextBox.HoldFocus = true;
} }
private void onLeaving() private void onLeaving()
{ {
updatePollingRate();
searchTextBox.HoldFocus = false; searchTextBox.HoldFocus = false;
// ensure any password prompt is dismissed. // ensure any password prompt is dismissed.
@ -316,6 +329,24 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
this.Push(CreateRoomSubScreen(room)); this.Push(CreateRoomSubScreen(room));
} }
private void updateLoadingLayer()
{
if (operationInProgress.Value || !ListingPollingComponent.HasPolledOnce.Value)
loadingLayer.Show();
else
loadingLayer.Hide();
}
private void updatePollingRate()
{
if (!this.IsCurrentScreen())
ListingPollingComponent.TimeBetweenPolls.Value = 0;
else
ListingPollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 120000 : 15000;
Logger.Log($"Polling adjusted (listing: {ListingPollingComponent.TimeBetweenPolls.Value})");
}
protected abstract OsuButton CreateNewRoomButton(); protected abstract OsuButton CreateNewRoomButton();
/// <summary> /// <summary>
@ -326,13 +357,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
protected abstract RoomSubScreen CreateRoomSubScreen(Room room); protected abstract RoomSubScreen CreateRoomSubScreen(Room room);
private void updateLoadingLayer() protected abstract ListingPollingComponent CreatePollingComponent();
{
if (operationInProgress.Value || !initialRoomsReceived.Value)
loadingLayer.Show();
else
loadingLayer.Hide();
}
private class LoungeSearchTextBox : SearchTextBox private class LoungeSearchTextBox : SearchTextBox
{ {

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Logging;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer;
using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Components;
@ -23,35 +22,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
client.ChangeState(MultiplayerUserState.Idle); client.ChangeState(MultiplayerUserState.Idle);
} }
protected override void UpdatePollingRate(bool isIdle)
{
var multiplayerRoomManager = (MultiplayerRoomManager)RoomManager;
if (!this.IsCurrentScreen())
{
multiplayerRoomManager.TimeBetweenListingPolls.Value = 0;
multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0;
}
else
{
switch (CurrentSubScreen)
{
case LoungeSubScreen _:
multiplayerRoomManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000;
multiplayerRoomManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000;
break;
// Don't poll inside the match or anywhere else.
default:
multiplayerRoomManager.TimeBetweenListingPolls.Value = 0;
multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0;
break;
}
}
Logger.Log($"Polling adjusted (listing: {multiplayerRoomManager.TimeBetweenListingPolls.Value}, selection: {multiplayerRoomManager.TimeBetweenSelectionPolls.Value})");
}
protected override string ScreenTitle => "Multiplayer"; protected override string ScreenTitle => "Multiplayer";
protected override RoomManager CreateRoomManager() => new MultiplayerRoomManager(); protected override RoomManager CreateRoomManager() => new MultiplayerRoomManager();

View File

@ -1,12 +1,16 @@
// 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.
using System.Threading.Tasks;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Screens.OnlinePlay.Match;
@ -21,6 +25,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
[Resolved] [Resolved]
private MultiplayerClient client { get; set; } private MultiplayerClient client { get; set; }
public override void OnResuming(IScreen last)
{
base.OnResuming(last);
ListingPollingComponent.PollImmediately();
}
protected override FilterCriteria CreateFilterCriteria() protected override FilterCriteria CreateFilterCriteria()
{ {
var criteria = base.CreateFilterCriteria(); var criteria = base.CreateFilterCriteria();
@ -39,6 +49,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
protected override RoomSubScreen CreateRoomSubScreen(Room room) => new MultiplayerMatchSubScreen(room); protected override RoomSubScreen CreateRoomSubScreen(Room room) => new MultiplayerMatchSubScreen(room);
protected override ListingPollingComponent CreatePollingComponent() => new MultiplayerListingPollingComponent();
protected override void OpenNewRoom(Room room) protected override void OpenNewRoom(Room room)
{ {
if (client?.IsConnected.Value != true) if (client?.IsConnected.Value != true)
@ -49,5 +61,26 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
base.OpenNewRoom(room); base.OpenNewRoom(room);
} }
private class MultiplayerListingPollingComponent : ListingPollingComponent
{
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();
}
} }
} }

View File

@ -49,9 +49,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
private OngoingOperationTracker ongoingOperationTracker { get; set; } private OngoingOperationTracker ongoingOperationTracker { get; set; }
[Resolved] [Resolved]
private Bindable<Room> currentRoom { get; set; } private Bindable<Room> currentRoom { get; set; } // Todo: This should not exist.
private MultiplayerMatchSettingsOverlay settingsOverlay;
private readonly IBindable<bool> isConnected = new Bindable<bool>(); private readonly IBindable<bool> isConnected = new Bindable<bool>();
@ -59,6 +57,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
private IDisposable readyClickOperation; private IDisposable readyClickOperation;
private GridContainer mainContent; private GridContainer mainContent;
private MultiplayerMatchSettingsOverlay settingsOverlay;
public MultiplayerMatchSubScreen(Room room) public MultiplayerMatchSubScreen(Room room)
{ {
@ -324,6 +323,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
public override bool OnExiting(IScreen next) public override bool OnExiting(IScreen next)
{ {
// We don't know whether we're the only participant in the room, and whether the room will close after we leave it as a result.
// To work around this, temporarily remove the room until the next listing poll retrieves it.
RoomManager?.RemoveRoom(currentRoom.Value);
// the room may not be left immediately after a disconnection due to async flow, // the room may not be left immediately after a disconnection due to async flow,
// so checking the IsConnected status is also required. // so checking the IsConnected status is also required.
if (client.Room == null || !client.IsConnected.Value) if (client.Room == null || !client.IsConnected.Value)

View File

@ -2,9 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Threading.Tasks;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.ExceptionExtensions; using osu.Framework.Extensions.ExceptionExtensions;
@ -21,13 +19,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
[Resolved] [Resolved]
private MultiplayerClient multiplayerClient { get; set; } private MultiplayerClient multiplayerClient { get; set; }
public readonly Bindable<double> TimeBetweenListingPolls = new Bindable<double>();
public readonly Bindable<double> TimeBetweenSelectionPolls = new Bindable<double>();
private readonly IBindable<bool> isConnected = new Bindable<bool>(); private readonly IBindable<bool> isConnected = new Bindable<bool>();
private readonly Bindable<bool> allowPolling = new Bindable<bool>(); private readonly Bindable<bool> allowPolling = new Bindable<bool>();
private ListingPollingComponent listingPollingComponent;
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
@ -64,19 +58,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
if (JoinedRoom.Value == null) if (JoinedRoom.Value == null)
return; return;
var joinedRoom = JoinedRoom.Value;
base.PartRoom(); base.PartRoom();
multiplayerClient.LeaveRoom(); multiplayerClient.LeaveRoom();
// 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.
Schedule(() =>
{
RemoveRoom(joinedRoom);
listingPollingComponent.PollImmediately();
});
} }
private void joinMultiplayerRoom(Room room, string password, Action<Room> onSuccess = null, Action<string> onError = null) private void joinMultiplayerRoom(Room room, string password, Action<Room> onSuccess = null, Action<string> onError = null)
@ -108,61 +91,5 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
// Don't poll when not connected or when a room has been joined. // Don't poll when not connected or when a room has been joined.
allowPolling.Value = isConnected.Value && JoinedRoom.Value == null; allowPolling.Value = isConnected.Value && JoinedRoom.Value == null;
} }
protected override IEnumerable<RoomPollingComponent> CreatePollingComponents() => new RoomPollingComponent[]
{
listingPollingComponent = new MultiplayerListingPollingComponent
{
TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls },
AllowPolling = { BindTarget = allowPolling }
},
new MultiplayerSelectionPollingComponent
{
TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls },
AllowPolling = { BindTarget = allowPolling }
}
};
private class MultiplayerListingPollingComponent : ListingPollingComponent
{
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();
}
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();
}
} }
} }

View File

@ -13,7 +13,6 @@ using osu.Framework.Logging;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Input;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Overlays; using osu.Game.Overlays;
@ -43,8 +42,6 @@ namespace osu.Game.Screens.OnlinePlay
private LoungeSubScreen loungeSubScreen; private LoungeSubScreen loungeSubScreen;
private ScreenStack screenStack; private ScreenStack screenStack;
private readonly IBindable<bool> isIdle = new BindableBool();
[Cached(Type = typeof(IRoomManager))] [Cached(Type = typeof(IRoomManager))]
protected RoomManager RoomManager { get; private set; } protected RoomManager RoomManager { get; private set; }
@ -66,9 +63,6 @@ namespace osu.Game.Screens.OnlinePlay
[Resolved] [Resolved]
protected IAPIProvider API { get; private set; } protected IAPIProvider API { get; private set; }
[Resolved(CanBeNull = true)]
private IdleTracker idleTracker { get; set; }
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private OsuLogo logo { get; set; } private OsuLogo logo { get; set; }
@ -151,12 +145,6 @@ namespace osu.Game.Screens.OnlinePlay
apiState.BindTo(API.State); apiState.BindTo(API.State);
apiState.BindValueChanged(onlineStateChanged, true); apiState.BindValueChanged(onlineStateChanged, true);
if (idleTracker != null)
{
isIdle.BindTo(idleTracker.IsIdle);
isIdle.BindValueChanged(idle => UpdatePollingRate(idle.NewValue), true);
}
} }
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
@ -166,8 +154,6 @@ namespace osu.Game.Screens.OnlinePlay
return dependencies; return dependencies;
} }
protected abstract void UpdatePollingRate(bool isIdle);
private void forcefullyExit() private void forcefullyExit()
{ {
// This is temporary since we don't currently have a way to force screens to be exited // This is temporary since we don't currently have a way to force screens to be exited
@ -203,8 +189,6 @@ namespace osu.Game.Screens.OnlinePlay
screenStack.CurrentScreen.OnResuming(last); screenStack.CurrentScreen.OnResuming(last);
base.OnResuming(last); base.OnResuming(last);
UpdatePollingRate(isIdle.Value);
} }
public override void OnSuspending(IScreen next) public override void OnSuspending(IScreen next)
@ -214,8 +198,6 @@ namespace osu.Game.Screens.OnlinePlay
Debug.Assert(screenStack.CurrentScreen != null); Debug.Assert(screenStack.CurrentScreen != null);
screenStack.CurrentScreen.OnSuspending(next); screenStack.CurrentScreen.OnSuspending(next);
UpdatePollingRate(isIdle.Value);
} }
public override bool OnExiting(IScreen next) public override bool OnExiting(IScreen next)
@ -279,15 +261,13 @@ namespace osu.Game.Screens.OnlinePlay
if (newScreen is IOsuScreen newOsuScreen) if (newScreen is IOsuScreen newOsuScreen)
((IBindable<UserActivity>)Activity).BindTo(newOsuScreen.Activity); ((IBindable<UserActivity>)Activity).BindTo(newOsuScreen.Activity);
UpdatePollingRate(isIdle.Value);
} }
protected IScreen CurrentSubScreen => screenStack.CurrentScreen; protected IScreen CurrentSubScreen => screenStack.CurrentScreen;
protected abstract string ScreenTitle { get; } protected abstract string ScreenTitle { get; }
protected abstract RoomManager CreateRoomManager(); protected virtual RoomManager CreateRoomManager() => new RoomManager();
protected abstract LoungeSubScreen CreateLounge(); protected abstract LoungeSubScreen CreateLounge();

View File

@ -1,53 +1,14 @@
// 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.
using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Match;
namespace osu.Game.Screens.OnlinePlay.Playlists namespace osu.Game.Screens.OnlinePlay.Playlists
{ {
public class Playlists : OnlinePlayScreen public class Playlists : OnlinePlayScreen
{ {
protected override void UpdatePollingRate(bool isIdle)
{
var playlistsManager = (PlaylistsRoomManager)RoomManager;
if (!this.IsCurrentScreen())
{
playlistsManager.TimeBetweenListingPolls.Value = 0;
playlistsManager.TimeBetweenSelectionPolls.Value = 0;
}
else
{
switch (CurrentSubScreen)
{
case LoungeSubScreen _:
playlistsManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000;
playlistsManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000;
break;
case RoomSubScreen _:
playlistsManager.TimeBetweenListingPolls.Value = 0;
playlistsManager.TimeBetweenSelectionPolls.Value = isIdle ? 30000 : 5000;
break;
default:
playlistsManager.TimeBetweenListingPolls.Value = 0;
playlistsManager.TimeBetweenSelectionPolls.Value = 0;
break;
}
}
Logger.Log($"Polling adjusted (listing: {playlistsManager.TimeBetweenListingPolls.Value}, selection: {playlistsManager.TimeBetweenSelectionPolls.Value})");
}
protected override string ScreenTitle => "Playlists"; protected override string ScreenTitle => "Playlists";
protected override RoomManager CreateRoomManager() => new PlaylistsRoomManager();
protected override LoungeSubScreen CreateLounge() => new PlaylistsLoungeSubScreen(); protected override LoungeSubScreen CreateLounge() => new PlaylistsLoungeSubScreen();
} }
} }

View File

@ -9,6 +9,7 @@ using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Screens.OnlinePlay.Match;
@ -66,6 +67,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
protected override RoomSubScreen CreateRoomSubScreen(Room room) => new PlaylistsRoomSubScreen(room); protected override RoomSubScreen CreateRoomSubScreen(Room room) => new PlaylistsRoomSubScreen(room);
protected override ListingPollingComponent CreatePollingComponent() => new ListingPollingComponent();
private enum PlaylistsCategory private enum PlaylistsCategory
{ {
Any, Any,

View File

@ -1,21 +0,0 @@
// 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.Collections.Generic;
using osu.Framework.Bindables;
using osu.Game.Screens.OnlinePlay.Components;
namespace osu.Game.Screens.OnlinePlay.Playlists
{
public class PlaylistsRoomManager : RoomManager
{
public readonly Bindable<double> TimeBetweenListingPolls = new Bindable<double>();
public readonly Bindable<double> TimeBetweenSelectionPolls = new Bindable<double>();
protected override IEnumerable<RoomPollingComponent> CreatePollingComponents() => new RoomPollingComponent[]
{
new ListingPollingComponent { TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls } },
new SelectionPollingComponent { TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls } }
};
}
}

View File

@ -33,10 +33,10 @@ namespace osu.Game.Tests.Visual.OnlinePlay
room.RoomID.Value ??= Rooms.Select(r => r.RoomID.Value).Where(id => id != null).Select(id => id.Value).DefaultIfEmpty().Max() + 1; room.RoomID.Value ??= Rooms.Select(r => r.RoomID.Value).Where(id => id != null).Select(id => id.Value).DefaultIfEmpty().Max() + 1;
onSuccess?.Invoke(room); onSuccess?.Invoke(room);
AddRoom(room); AddOrUpdateRoom(room);
} }
public void AddRoom(Room room) public void AddOrUpdateRoom(Room room)
{ {
Rooms.Add(room); Rooms.Add(room);
RoomsUpdated?.Invoke(); RoomsUpdated?.Invoke();
@ -48,6 +48,12 @@ namespace osu.Game.Tests.Visual.OnlinePlay
RoomsUpdated?.Invoke(); RoomsUpdated?.Invoke();
} }
public void ClearRooms()
{
Rooms.Clear();
RoomsUpdated?.Invoke();
}
public void JoinRoom(Room room, string password, Action<Room> onSuccess = null, Action<string> onError = null) public void JoinRoom(Room room, string password, Action<Room> onSuccess = null, Action<string> onError = null)
{ {
JoinRoomRequested?.Invoke(room, password); JoinRoomRequested?.Invoke(room, password);