1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 09:27:29 +08:00

Abstract RoomManager and Multiplayer

This commit is contained in:
smoogipoo 2020-12-19 00:15:41 +09:00
parent 0abe2b36b2
commit 4494bb1eb5
12 changed files with 460 additions and 385 deletions

View File

@ -4,6 +4,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Overlays;
using osu.Game.Screens.Multi.Timeshift;
namespace osu.Game.Tests.Visual.Multiplayer
{
@ -17,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
public TestSceneMultiScreen()
{
Screens.Multi.Multiplayer multi = new Screens.Multi.Multiplayer();
var multi = new TimeshiftMultiplayer();
AddStep("show", () => LoadScreen(multi));
AddUntilStep("wait for loaded", () => multi.IsLoaded);

View File

@ -11,6 +11,7 @@ using osu.Game.Beatmaps;
using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
using osu.Game.Overlays.Toolbar;
using osu.Game.Screens.Multi.Timeshift;
using osu.Game.Screens.Play;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Options;
@ -107,14 +108,14 @@ namespace osu.Game.Tests.Visual.Navigation
[Test]
public void TestExitMultiWithEscape()
{
PushAndConfirm(() => new Screens.Multi.Multiplayer());
PushAndConfirm(() => new TimeshiftMultiplayer());
exitViaEscapeAndConfirm();
}
[Test]
public void TestExitMultiWithBackButton()
{
PushAndConfirm(() => new Screens.Multi.Multiplayer());
PushAndConfirm(() => new TimeshiftMultiplayer());
exitViaBackButtonAndConfirm();
}

View File

@ -17,7 +17,7 @@ using osu.Game.Online.API;
using osu.Game.Overlays;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Multi;
using osu.Game.Screens.Multi.Timeshift;
using osu.Game.Screens.Select;
namespace osu.Game.Screens.Menu
@ -104,7 +104,7 @@ namespace osu.Game.Screens.Menu
this.Push(new Editor());
},
OnSolo = onSolo,
OnMulti = delegate { this.Push(new Multiplayer()); },
OnMulti = delegate { this.Push(new TimeshiftMultiplayer()); },
OnExit = confirmAndExit,
}
}

View File

@ -0,0 +1,68 @@
// 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.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Game.Online.Multiplayer;
using osu.Game.Screens.Multi.Lounge.Components;
namespace osu.Game.Screens.Multi.Components
{
/// <summary>
/// A <see cref="RoomPollingComponent"/> that polls for the lounge listing.
/// </summary>
public class ListingPollingComponent : RoomPollingComponent
{
[Resolved]
private Bindable<FilterCriteria> currentFilter { get; set; }
[Resolved]
private Bindable<Room> selectedRoom { get; set; }
[BackgroundDependencyLoader]
private void load()
{
currentFilter.BindValueChanged(_ =>
{
if (IsLoaded)
PollImmediately();
});
}
private GetRoomsRequest pollReq;
protected override Task Poll()
{
if (!API.IsLoggedIn)
return base.Poll();
var tcs = new TaskCompletionSource<bool>();
pollReq?.Cancel();
pollReq = new GetRoomsRequest(currentFilter.Value.Status, currentFilter.Value.Category);
pollReq.Success += result =>
{
for (int i = 0; i < result.Count; i++)
{
if (result[i].RoomID.Value == selectedRoom.Value?.RoomID.Value)
{
// The listing request always has less information than the opened room, so don't include it.
result[i] = selectedRoom.Value;
break;
}
}
NotifyRoomsReceived(result);
tcs.SetResult(true);
};
pollReq.Failure += _ => tcs.SetResult(false);
API.Queue(pollReq);
return tcs.Task;
}
}
}

View File

@ -0,0 +1,188 @@
// 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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets;
namespace osu.Game.Screens.Multi.Components
{
public abstract class RoomManager : CompositeDrawable, IRoomManager
{
public event Action RoomsUpdated;
private readonly BindableList<Room> rooms = new BindableList<Room>();
public Bindable<bool> InitialRoomsReceived { get; } = new Bindable<bool>();
public IBindableList<Room> Rooms => rooms;
[Resolved]
private RulesetStore rulesets { get; set; }
[Resolved]
private BeatmapManager beatmaps { get; set; }
[Resolved]
private IAPIProvider api { get; set; }
private Room joinedRoom;
protected RoomManager()
{
RelativeSizeAxes = Axes.Both;
InternalChildren = CreatePollingComponents().Select(p =>
{
p.InitialRoomsReceived.BindTo(InitialRoomsReceived);
p.RoomsReceived = onRoomsReceived;
return p;
}).ToList();
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
PartRoom();
}
public virtual void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
{
room.Host.Value = api.LocalUser.Value;
var req = new CreateRoomRequest(room);
req.Success += result =>
{
joinedRoom = room;
update(room, result);
addRoom(room);
RoomsUpdated?.Invoke();
onSuccess?.Invoke(room);
};
req.Failure += exception =>
{
if (req.Result != null)
onError?.Invoke(req.Result.Error);
else
Logger.Log($"Failed to create the room: {exception}", level: LogLevel.Important);
};
api.Queue(req);
}
private JoinRoomRequest currentJoinRoomRequest;
public virtual void JoinRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
{
currentJoinRoomRequest?.Cancel();
currentJoinRoomRequest = new JoinRoomRequest(room);
currentJoinRoomRequest.Success += () =>
{
joinedRoom = room;
onSuccess?.Invoke(room);
};
currentJoinRoomRequest.Failure += exception =>
{
if (!(exception is OperationCanceledException))
Logger.Log($"Failed to join room: {exception}", level: LogLevel.Important);
onError?.Invoke(exception.ToString());
};
api.Queue(currentJoinRoomRequest);
}
public void PartRoom()
{
currentJoinRoomRequest?.Cancel();
if (joinedRoom == null)
return;
api.Queue(new PartRoomRequest(joinedRoom));
joinedRoom = null;
}
private readonly HashSet<int> ignoredRooms = new HashSet<int>();
private void onRoomsReceived(List<Room> received)
{
// Remove past matches
foreach (var r in rooms.ToList())
{
if (received.All(e => e.RoomID.Value != r.RoomID.Value))
rooms.Remove(r);
}
for (int i = 0; i < received.Count; i++)
{
var room = received[i];
Debug.Assert(room.RoomID.Value != null);
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();
}
/// <summary>
/// Updates a local <see cref="Room"/> with a remote copy.
/// </summary>
/// <param name="local">The local <see cref="Room"/> to update.</param>
/// <param name="remote">The remote <see cref="Room"/> to update with.</param>
private void update(Room local, Room remote)
{
foreach (var pi in remote.Playlist)
pi.MapObjects(beatmaps, rulesets);
local.CopyFrom(remote);
}
/// <summary>
/// Adds a <see cref="Room"/> to the list of available rooms.
/// </summary>
/// <param name="room">The <see cref="Room"/> to add.</param>
private void addRoom(Room room)
{
var existing = rooms.FirstOrDefault(e => e.RoomID.Value == room.RoomID.Value);
if (existing == null)
rooms.Add(room);
else
existing.CopyFrom(room);
}
protected abstract RoomPollingComponent[] CreatePollingComponents();
}
}

View File

@ -0,0 +1,41 @@
// 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;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Game.Online;
using osu.Game.Online.API;
using osu.Game.Online.Multiplayer;
namespace osu.Game.Screens.Multi.Components
{
public abstract class RoomPollingComponent : PollingComponent
{
public Action<List<Room>> RoomsReceived;
/// <summary>
/// The time in milliseconds to wait between polls.
/// Setting to zero stops all polling.
/// </summary>
public new readonly Bindable<double> TimeBetweenPolls = new Bindable<double>();
public IBindable<bool> InitialRoomsReceived => initialRoomsReceived;
private readonly Bindable<bool> initialRoomsReceived = new Bindable<bool>();
[Resolved]
protected IAPIProvider API { get; private set; }
protected RoomPollingComponent()
{
TimeBetweenPolls.BindValueChanged(time => base.TimeBetweenPolls = time.NewValue);
}
protected void NotifyRoomsReceived(List<Room> rooms)
{
initialRoomsReceived.Value = true;
RoomsReceived?.Invoke(rooms);
}
}
}

View File

@ -0,0 +1,69 @@
// 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 System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Game.Online.Multiplayer;
namespace osu.Game.Screens.Multi.Components
{
/// <summary>
/// A <see cref="RoomPollingComponent"/> that polls for the currently-selected room.
/// </summary>
public class SelectionPollingComponent : RoomPollingComponent
{
[Resolved]
private Bindable<Room> selectedRoom { get; set; }
[Resolved]
private IRoomManager roomManager { get; set; }
[BackgroundDependencyLoader]
private void load()
{
selectedRoom.BindValueChanged(_ =>
{
if (IsLoaded)
PollImmediately();
});
}
private GetRoomRequest pollReq;
protected override Task Poll()
{
if (!API.IsLoggedIn)
return base.Poll();
if (selectedRoom.Value?.RoomID.Value == null)
return base.Poll();
var tcs = new TaskCompletionSource<bool>();
pollReq?.Cancel();
pollReq = new GetRoomRequest(selectedRoom.Value.RoomID.Value.Value);
pollReq.Success += result =>
{
var rooms = new List<Room>(roomManager.Rooms);
int index = rooms.FindIndex(r => r.RoomID == result.RoomID);
if (index < 0)
return;
rooms[index] = result;
NotifyRoomsReceived(rooms);
tcs.SetResult(true);
};
pollReq.Failure += _ => tcs.SetResult(false);
API.Queue(pollReq);
return tcs.Task;
}
}
}

View File

@ -2,11 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Game.Online.Multiplayer;
namespace osu.Game.Screens.Multi
{
[Cached(typeof(IRoomManager))]
public interface IRoomManager
{
/// <summary>

View File

@ -8,7 +8,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
@ -30,7 +29,7 @@ using osuTK;
namespace osu.Game.Screens.Multi
{
[Cached]
public class Multiplayer : OsuScreen
public abstract class Multiplayer : OsuScreen
{
public override bool CursorVisible => (screenStack.CurrentScreen as IMultiplayerSubScreen)?.CursorVisible ?? true;
@ -46,6 +45,9 @@ namespace osu.Game.Screens.Multi
private readonly IBindable<bool> isIdle = new BindableBool();
[Cached(Type = typeof(IRoomManager))]
protected IRoomManager RoomManager { get; private set; }
[Cached]
private readonly Bindable<Room> selectedRoom = new Bindable<Room>();
@ -55,9 +57,6 @@ namespace osu.Game.Screens.Multi
[Resolved(CanBeNull = true)]
private MusicController music { get; set; }
[Cached(Type = typeof(IRoomManager))]
private RoomManager roomManager;
[Resolved]
private OsuGameBase game { get; set; }
@ -70,7 +69,7 @@ namespace osu.Game.Screens.Multi
private readonly Drawable header;
private readonly Drawable headerBackground;
public Multiplayer()
protected Multiplayer()
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
@ -82,7 +81,7 @@ namespace osu.Game.Screens.Multi
InternalChild = waves = new MultiplayerWaveContainer
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
Children = new[]
{
new Box
{
@ -137,7 +136,7 @@ namespace osu.Game.Screens.Multi
Origin = Anchor.TopRight,
Action = () => CreateRoom()
},
roomManager = new RoomManager()
(Drawable)(RoomManager = CreateRoomManager())
}
};
@ -168,7 +167,7 @@ namespace osu.Game.Screens.Multi
protected override void LoadComplete()
{
base.LoadComplete();
isIdle.BindValueChanged(idle => updatePollingRate(idle.NewValue), true);
isIdle.BindValueChanged(idle => UpdatePollingRate(idle.NewValue), true);
}
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
@ -178,36 +177,7 @@ namespace osu.Game.Screens.Multi
return dependencies;
}
private void updatePollingRate(bool idle)
{
if (!this.IsCurrentScreen())
{
roomManager.TimeBetweenListingPolls = 0;
roomManager.TimeBetweenSelectionPolls = 0;
}
else
{
switch (screenStack.CurrentScreen)
{
case LoungeSubScreen _:
roomManager.TimeBetweenListingPolls = idle ? 120000 : 15000;
roomManager.TimeBetweenSelectionPolls = idle ? 120000 : 15000;
break;
case MatchSubScreen _:
roomManager.TimeBetweenListingPolls = 0;
roomManager.TimeBetweenSelectionPolls = idle ? 30000 : 5000;
break;
default:
roomManager.TimeBetweenListingPolls = 0;
roomManager.TimeBetweenSelectionPolls = 0;
break;
}
}
Logger.Log($"Polling adjusted (listing: {roomManager.TimeBetweenListingPolls}, selection: {roomManager.TimeBetweenSelectionPolls})");
}
protected abstract void UpdatePollingRate(bool isIdle);
private void forcefullyExit()
{
@ -241,7 +211,7 @@ namespace osu.Game.Screens.Multi
beginHandlingTrack();
updatePollingRate(isIdle.Value);
UpdatePollingRate(isIdle.Value);
}
public override void OnSuspending(IScreen next)
@ -251,12 +221,12 @@ namespace osu.Game.Screens.Multi
endHandlingTrack();
updatePollingRate(isIdle.Value);
UpdatePollingRate(isIdle.Value);
}
public override bool OnExiting(IScreen next)
{
roomManager.PartRoom();
RoomManager.PartRoom();
waves.Hide();
@ -344,12 +314,14 @@ namespace osu.Game.Screens.Multi
if (newScreen is IOsuScreen newOsuScreen)
((IBindable<UserActivity>)Activity).BindTo(newOsuScreen.Activity);
updatePollingRate(isIdle.Value);
UpdatePollingRate(isIdle.Value);
createButton.FadeTo(newScreen is LoungeSubScreen ? 1 : 0, 200);
updateTrack();
}
protected IScreen CurrentSubScreen => screenStack.CurrentScreen;
private void updateTrack(ValueChangedEvent<WorkingBeatmap> _ = null)
{
if (screenStack.CurrentScreen is MatchSubScreen)
@ -381,6 +353,8 @@ namespace osu.Game.Screens.Multi
}
}
protected abstract IRoomManager CreateRoomManager();
private class MultiplayerWaveContainer : WaveContainer
{
protected override bool StartHidden => true;

View File

@ -1,337 +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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Online;
using osu.Game.Online.API;
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets;
using osu.Game.Screens.Multi.Lounge.Components;
namespace osu.Game.Screens.Multi
{
public class RoomManager : CompositeDrawable, IRoomManager
{
public event Action RoomsUpdated;
private readonly BindableList<Room> rooms = new BindableList<Room>();
public Bindable<bool> InitialRoomsReceived { get; } = new Bindable<bool>();
public IBindableList<Room> Rooms => rooms;
public double TimeBetweenListingPolls
{
get => listingPollingComponent.TimeBetweenPolls;
set => listingPollingComponent.TimeBetweenPolls = value;
}
public double TimeBetweenSelectionPolls
{
get => selectionPollingComponent.TimeBetweenPolls;
set => selectionPollingComponent.TimeBetweenPolls = value;
}
[Resolved]
private RulesetStore rulesets { get; set; }
[Resolved]
private BeatmapManager beatmaps { get; set; }
[Resolved]
private IAPIProvider api { get; set; }
[Resolved]
private Bindable<Room> selectedRoom { get; set; }
private readonly ListingPollingComponent listingPollingComponent;
private readonly SelectionPollingComponent selectionPollingComponent;
private Room joinedRoom;
public RoomManager()
{
RelativeSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
listingPollingComponent = new ListingPollingComponent
{
InitialRoomsReceived = { BindTarget = InitialRoomsReceived },
RoomsReceived = onListingReceived
},
selectionPollingComponent = new SelectionPollingComponent { RoomReceived = onSelectedRoomReceived }
};
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
PartRoom();
}
public void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
{
room.Host.Value = api.LocalUser.Value;
var req = new CreateRoomRequest(room);
req.Success += result =>
{
joinedRoom = room;
update(room, result);
addRoom(room);
RoomsUpdated?.Invoke();
onSuccess?.Invoke(room);
};
req.Failure += exception =>
{
if (req.Result != null)
onError?.Invoke(req.Result.Error);
else
Logger.Log($"Failed to create the room: {exception}", level: LogLevel.Important);
};
api.Queue(req);
}
private JoinRoomRequest currentJoinRoomRequest;
public void JoinRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
{
currentJoinRoomRequest?.Cancel();
currentJoinRoomRequest = new JoinRoomRequest(room);
currentJoinRoomRequest.Success += () =>
{
joinedRoom = room;
onSuccess?.Invoke(room);
};
currentJoinRoomRequest.Failure += exception =>
{
if (!(exception is OperationCanceledException))
Logger.Log($"Failed to join room: {exception}", level: LogLevel.Important);
onError?.Invoke(exception.ToString());
};
api.Queue(currentJoinRoomRequest);
}
public void PartRoom()
{
currentJoinRoomRequest?.Cancel();
if (joinedRoom == null)
return;
api.Queue(new PartRoomRequest(joinedRoom));
joinedRoom = null;
}
private readonly HashSet<int> ignoredRooms = new HashSet<int>();
/// <summary>
/// Invoked when the listing of all <see cref="Room"/>s is received from the server.
/// </summary>
/// <param name="listing">The listing.</param>
private void onListingReceived(List<Room> listing)
{
// Remove past matches
foreach (var r in rooms.ToList())
{
if (listing.All(e => e.RoomID.Value != r.RoomID.Value))
rooms.Remove(r);
}
for (int i = 0; i < listing.Count; i++)
{
if (selectedRoom.Value?.RoomID?.Value == listing[i].RoomID.Value)
{
// The listing request contains less data than the selection request, so data from the selection request is always preferred while the room is selected.
continue;
}
var room = listing[i];
Debug.Assert(room.RoomID.Value != null);
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();
}
/// <summary>
/// Invoked when a <see cref="Room"/> is received from the server.
/// </summary>
/// <param name="toUpdate">The received <see cref="Room"/>.</param>
private void onSelectedRoomReceived(Room toUpdate)
{
foreach (var room in rooms)
{
if (room.RoomID.Value == toUpdate.RoomID.Value)
{
toUpdate.Position.Value = room.Position.Value;
update(room, toUpdate);
break;
}
}
}
/// <summary>
/// Updates a local <see cref="Room"/> with a remote copy.
/// </summary>
/// <param name="local">The local <see cref="Room"/> to update.</param>
/// <param name="remote">The remote <see cref="Room"/> to update with.</param>
private void update(Room local, Room remote)
{
foreach (var pi in remote.Playlist)
pi.MapObjects(beatmaps, rulesets);
local.CopyFrom(remote);
}
/// <summary>
/// Adds a <see cref="Room"/> to the list of available rooms.
/// </summary>
/// <param name="room">The <see cref="Room"/> to add.</param>
private void addRoom(Room room)
{
var existing = rooms.FirstOrDefault(e => e.RoomID.Value == room.RoomID.Value);
if (existing == null)
rooms.Add(room);
else
existing.CopyFrom(room);
}
private class SelectionPollingComponent : PollingComponent
{
public Action<Room> RoomReceived;
[Resolved]
private IAPIProvider api { get; set; }
[Resolved]
private Bindable<Room> selectedRoom { get; set; }
[BackgroundDependencyLoader]
private void load()
{
selectedRoom.BindValueChanged(_ =>
{
if (IsLoaded)
PollImmediately();
});
}
private GetRoomRequest pollReq;
protected override Task Poll()
{
if (!api.IsLoggedIn)
return base.Poll();
if (selectedRoom.Value?.RoomID.Value == null)
return base.Poll();
var tcs = new TaskCompletionSource<bool>();
pollReq?.Cancel();
pollReq = new GetRoomRequest(selectedRoom.Value.RoomID.Value.Value);
pollReq.Success += result =>
{
RoomReceived?.Invoke(result);
tcs.SetResult(true);
};
pollReq.Failure += _ => tcs.SetResult(false);
api.Queue(pollReq);
return tcs.Task;
}
}
private class ListingPollingComponent : PollingComponent
{
public Action<List<Room>> RoomsReceived;
public readonly Bindable<bool> InitialRoomsReceived = new Bindable<bool>();
[Resolved]
private IAPIProvider api { get; set; }
[Resolved]
private Bindable<FilterCriteria> currentFilter { get; set; }
[BackgroundDependencyLoader]
private void load()
{
currentFilter.BindValueChanged(_ =>
{
InitialRoomsReceived.Value = false;
if (IsLoaded)
PollImmediately();
});
}
private GetRoomsRequest pollReq;
protected override Task Poll()
{
if (!api.IsLoggedIn)
return base.Poll();
var tcs = new TaskCompletionSource<bool>();
pollReq?.Cancel();
pollReq = new GetRoomsRequest(currentFilter.Value.Status, currentFilter.Value.Category);
pollReq.Success += result =>
{
InitialRoomsReceived.Value = true;
RoomsReceived?.Invoke(result);
tcs.SetResult(true);
};
pollReq.Failure += _ => tcs.SetResult(false);
api.Queue(pollReq);
return tcs.Task;
}
}
}
}

View File

@ -0,0 +1,48 @@
// 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 osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Game.Screens.Multi.Lounge;
using osu.Game.Screens.Multi.Match;
namespace osu.Game.Screens.Multi.Timeshift
{
public class TimeshiftMultiplayer : Multiplayer
{
protected override void UpdatePollingRate(bool isIdle)
{
var timeshiftManager = (TimeshiftRoomManager)RoomManager;
if (!this.IsCurrentScreen())
{
timeshiftManager.TimeBetweenListingPolls.Value = 0;
timeshiftManager.TimeBetweenSelectionPolls.Value = 0;
}
else
{
switch (CurrentSubScreen)
{
case LoungeSubScreen _:
timeshiftManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000;
timeshiftManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000;
break;
case MatchSubScreen _:
timeshiftManager.TimeBetweenListingPolls.Value = 0;
timeshiftManager.TimeBetweenSelectionPolls.Value = isIdle ? 30000 : 5000;
break;
default:
timeshiftManager.TimeBetweenListingPolls.Value = 0;
timeshiftManager.TimeBetweenSelectionPolls.Value = 0;
break;
}
}
Logger.Log($"Polling adjusted (listing: {timeshiftManager.TimeBetweenListingPolls.Value}, selection: {timeshiftManager.TimeBetweenSelectionPolls.Value})");
}
protected override IRoomManager CreateRoomManager() => new TimeshiftRoomManager();
}
}

View File

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