// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Online; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; using osu.Game.Rulesets; using osu.Game.Screens.Multi.Lounge.Components; namespace osu.Game.Screens.Multi { public class RoomManager : PollingComponent, IRoomManager { public event Action RoomsUpdated; private readonly BindableList rooms = new BindableList(); public IBindableList Rooms => rooms; private Room joinedRoom; [Resolved] private Bindable currentFilter { get; set; } [Resolved] private APIAccess api { get; set; } [Resolved] private RulesetStore rulesets { get; set; } [Resolved] private BeatmapManager beatmaps { get; set; } [BackgroundDependencyLoader] private void load() { currentFilter.BindValueChanged(_ => { if (IsLoaded) PollImmediately(); }); } protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); PartRoom(); } public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) { room.Host.Value = api.LocalUser; 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 onSuccess = null, Action onError = null) { currentJoinRoomRequest?.Cancel(); currentJoinRoomRequest = null; currentJoinRoomRequest = new JoinRoomRequest(room, api.LocalUser.Value); currentJoinRoomRequest.Success += () => { joinedRoom = room; onSuccess?.Invoke(room); }; currentJoinRoomRequest.Failure += exception => { Logger.Log($"Failed to join room: {exception}", level: LogLevel.Important); onError?.Invoke(exception.ToString()); }; api.Queue(currentJoinRoomRequest); } public void PartRoom() { if (joinedRoom == null) return; api.Queue(new PartRoomRequest(joinedRoom, api.LocalUser.Value)); joinedRoom = null; } private GetRoomsRequest pollReq; protected override Task Poll() { if (!api.IsLoggedIn) return base.Poll(); var tcs = new TaskCompletionSource(); pollReq?.Cancel(); pollReq = new GetRoomsRequest(currentFilter.Value.PrimaryFilter); pollReq.Success += result => { // Remove past matches foreach (var r in rooms.ToList()) { if (result.All(e => e.RoomID.Value != r.RoomID.Value)) rooms.Remove(r); } for (int i = 0; i < result.Count; i++) { var r = result[i]; r.Position.Value = i; update(r, r); addRoom(r); } RoomsUpdated?.Invoke(); tcs.SetResult(true); }; pollReq.Failure += _ => tcs.SetResult(false); api.Queue(pollReq); return tcs.Task; } /// /// Updates a local with a remote copy. /// /// The local to update. /// The remote to update with. private void update(Room local, Room remote) { foreach (var pi in remote.Playlist) pi.MapObjects(beatmaps, rulesets); local.CopyFrom(remote); } /// /// Adds a to the list of available rooms. /// /// The to add.< 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); } } }