Ejemplo n.º 1
0
        private async Task <PlaylistItem> createPlaylistItem(MultiplayerPlaylistItem item, bool populateBeatmapImmediately)
        {
            var ruleset = Rulesets.GetRuleset(item.RulesetID);

            Debug.Assert(ruleset != null);

            var rulesetInstance = ruleset.CreateInstance();

            var playlistItem = new PlaylistItem
            {
                ID            = item.ID,
                BeatmapID     = item.BeatmapID,
                OwnerID       = item.OwnerID,
                Ruleset       = { Value = ruleset },
                Expired       = item.Expired,
                PlaylistOrder = item.PlaylistOrder,
                PlayedAt      = item.PlayedAt
            };

            playlistItem.RequiredMods.AddRange(item.RequiredMods.Select(m => m.ToMod(rulesetInstance)));
            playlistItem.AllowedMods.AddRange(item.AllowedMods.Select(m => m.ToMod(rulesetInstance)));

            if (populateBeatmapImmediately)
            {
                playlistItem.Beatmap.Value = await GetAPIBeatmap(item.BeatmapID).ConfigureAwait(false);
            }

            return(playlistItem);
        }
Ejemplo n.º 2
0
        public Task PlaylistItemAdded(MultiplayerPlaylistItem item)
        {
            if (Room == null)
            {
                return(Task.CompletedTask);
            }

            Scheduler.Add(() =>
            {
                if (Room == null)
                {
                    return;
                }

                Debug.Assert(APIRoom != null);

                Room.Playlist.Add(item);
                APIRoom.Playlist.Add(createPlaylistItem(item));

                ItemAdded?.Invoke(item);
                RoomUpdated?.Invoke();
            });

            return(Task.CompletedTask);
        }
Ejemplo n.º 3
0
        private async Task <PlaylistItem> createPlaylistItem(MultiplayerPlaylistItem item)
        {
            var set = await GetOnlineBeatmapSet(item.BeatmapID).ConfigureAwait(false);

            // The incoming response is deserialised without circular reference handling currently.
            // Because we require using metadata from this instance, populate the nested beatmaps' sets manually here.
            foreach (var b in set.Beatmaps)
            {
                b.BeatmapSet = set;
            }

            var beatmap = set.Beatmaps.Single(b => b.OnlineID == item.BeatmapID);

            beatmap.Checksum = item.BeatmapChecksum;

            var ruleset         = Rulesets.GetRuleset(item.RulesetID);
            var rulesetInstance = ruleset.CreateInstance();

            var playlistItem = new PlaylistItem
            {
                ID      = item.ID,
                OwnerID = item.OwnerID,
                Beatmap = { Value = beatmap },
                Ruleset = { Value = ruleset },
                Expired = item.Expired
            };

            playlistItem.RequiredMods.AddRange(item.RequiredMods.Select(m => m.ToMod(rulesetInstance)));
            playlistItem.AllowedMods.AddRange(item.AllowedMods.Select(m => m.ToMod(rulesetInstance)));

            return(playlistItem);
        }
Ejemplo n.º 4
0
        public async Task EditUserPlaylistItem(int userId, MultiplayerPlaylistItem item)
        {
            Debug.Assert(Room != null);
            Debug.Assert(APIRoom != null);
            Debug.Assert(currentItem != null);

            item.OwnerID = userId;

            var existingItem = serverSidePlaylist.SingleOrDefault(i => i.ID == item.ID);

            if (existingItem == null)
            {
                throw new InvalidOperationException("Attempted to change an item that doesn't exist.");
            }

            if (existingItem.OwnerID != userId && Room.Host?.UserID != LocalUser?.UserID)
            {
                throw new InvalidOperationException("Attempted to change an item which is not owned by the user.");
            }

            if (existingItem.Expired)
            {
                throw new InvalidOperationException("Attempted to change an item which has already been played.");
            }

            // Ensure the playlist order doesn't change.
            item.PlaylistOrder = existingItem.PlaylistOrder;

            serverSidePlaylist[serverSidePlaylist.IndexOf(existingItem)] = item;

            await((IMultiplayerClient)this).PlaylistItemChanged(item).ConfigureAwait(false);
        }
Ejemplo n.º 5
0
        protected override void PlaylistItemChanged(MultiplayerPlaylistItem item)
        {
            base.PlaylistItemChanged(item);

            removeItemFromLists(item.ID);
            addItemToLists(item);
        }
Ejemplo n.º 6
0
        public async Task PlaylistItemChanged(MultiplayerPlaylistItem item)
        {
            if (Room == null)
            {
                return;
            }

            var playlistItem = await createPlaylistItem(item).ConfigureAwait(false);

            Scheduler.Add(() =>
            {
                if (Room == null)
                {
                    return;
                }

                Debug.Assert(APIRoom != null);

                Room.Playlist[Room.Playlist.IndexOf(Room.Playlist.Single(existing => existing.ID == item.ID))] = item;

                int existingIndex = APIRoom.Playlist.IndexOf(APIRoom.Playlist.Single(existing => existing.ID == item.ID));
                APIRoom.Playlist.RemoveAt(existingIndex);
                APIRoom.Playlist.Insert(existingIndex, playlistItem);

                // If the currently-selected item was the one that got replaced, update the selected item to the new one.
                if (CurrentMatchPlayingItem.Value?.ID == playlistItem.ID)
                {
                    CurrentMatchPlayingItem.Value = playlistItem;
                }

                RoomUpdated?.Invoke();
            });
        }
Ejemplo n.º 7
0
        protected override void SelectItem(PlaylistItem item)
        {
            // If the client is already in a room, update via the client.
            // Otherwise, update the playlist directly in preparation for it to be submitted to the API on match creation.
            if (client.Room != null)
            {
                loadingLayer.Show();

                var multiplayerItem = new MultiplayerPlaylistItem
                {
                    ID              = itemToEdit ?? 0,
                    BeatmapID       = item.Beatmap.OnlineID,
                    BeatmapChecksum = item.Beatmap.MD5Hash,
                    RulesetID       = item.RulesetID,
                    RequiredMods    = item.RequiredMods.ToArray(),
                    AllowedMods     = item.AllowedMods.ToArray()
                };

                Task task = itemToEdit != null?client.EditPlaylistItem(multiplayerItem) : client.AddPlaylistItem(multiplayerItem);

                task.ContinueWith(t =>
                {
                    Schedule(() =>
                    {
                        loadingLayer.Hide();

                        if (t.IsFaulted)
                        {
                            Exception exception = t.Exception;

                            if (exception is AggregateException ae)
                            {
                                exception = ae.InnerException;
                            }

                            Debug.Assert(exception != null);

                            string message = exception is HubException
                                             // HubExceptions arrive with additional message context added, but we want to display the human readable message:
                                             // "An unexpected error occurred invoking 'AddPlaylistItem' on the server.InvalidStateException: Can't enqueue more than 3 items at once."
                                             // We generally use the message field for a user-parseable error (eventually to be replaced), so drop the first part for now.
                                ? exception.Message.Substring(exception.Message.IndexOf(':') + 1).Trim()
                                : exception.Message;

                            Logger.Log(message, level: LogLevel.Important);
                            Carousel.AllowSelection = true;
                            return;
                        }

                        this.Exit();
                    });
                });
            }
            else
            {
                Playlist.Clear();
                Playlist.Add(item);
                this.Exit();
            }
        }
Ejemplo n.º 8
0
        public async Task PlaylistItemAdded(MultiplayerPlaylistItem item)
        {
            if (Room == null)
            {
                return;
            }

            var playlistItem = await createPlaylistItem(item, true).ConfigureAwait(false);

            Scheduler.Add(() =>
            {
                if (Room == null)
                {
                    return;
                }

                Debug.Assert(APIRoom != null);

                Room.Playlist.Add(item);
                APIRoom.Playlist.Add(playlistItem);

                ItemAdded?.Invoke(item);
                RoomUpdated?.Invoke();
            });
        }
Ejemplo n.º 9
0
        public Task PlaylistItemChanged(MultiplayerPlaylistItem item)
        {
            if (Room == null)
            {
                return(Task.CompletedTask);
            }

            Scheduler.Add(() =>
            {
                if (Room == null)
                {
                    return;
                }

                Debug.Assert(APIRoom != null);

                Room.Playlist[Room.Playlist.IndexOf(Room.Playlist.Single(existing => existing.ID == item.ID))] = item;

                int existingIndex = APIRoom.Playlist.IndexOf(APIRoom.Playlist.Single(existing => existing.ID == item.ID));
                APIRoom.Playlist.RemoveAt(existingIndex);
                APIRoom.Playlist.Insert(existingIndex, createPlaylistItem(item));

                ItemChanged?.Invoke(item);
                RoomUpdated?.Invoke();
            });

            return(Task.CompletedTask);
        }
Ejemplo n.º 10
0
        public override Task AddPlaylistItem(MultiplayerPlaylistItem item)
        {
            if (!IsConnected.Value)
            {
                return(Task.CompletedTask);
            }

            return(connection.InvokeAsync(nameof(IMultiplayerServer.AddPlaylistItem), item));
        }
Ejemplo n.º 11
0
        protected override bool SelectItem(PlaylistItem item)
        {
            if (operationInProgress.Value)
            {
                return(false);
            }

            // If the client is already in a room, update via the client.
            // Otherwise, update the playlist directly in preparation for it to be submitted to the API on match creation.
            if (client.Room != null)
            {
                selectionOperation = operationTracker.BeginOperation();

                var multiplayerItem = new MultiplayerPlaylistItem
                {
                    ID              = itemToEdit ?? 0,
                    BeatmapID       = item.Beatmap.OnlineID,
                    BeatmapChecksum = item.Beatmap.MD5Hash,
                    RulesetID       = item.RulesetID,
                    RequiredMods    = item.RequiredMods.ToArray(),
                    AllowedMods     = item.AllowedMods.ToArray()
                };

                Task task = itemToEdit != null?client.EditPlaylistItem(multiplayerItem) : client.AddPlaylistItem(multiplayerItem);

                task.FireAndForget(onSuccess: () =>
                {
                    selectionOperation.Dispose();

                    Schedule(() =>
                    {
                        // If an error or server side trigger occurred this screen may have already exited by external means.
                        if (this.IsCurrentScreen())
                        {
                            this.Exit();
                        }
                    });
                }, onError: _ =>
                {
                    selectionOperation.Dispose();

                    Schedule(() =>
                    {
                        Carousel.AllowSelection = true;
                    });
                });
            }
            else
            {
                Playlist.Clear();
                Playlist.Add(item);
                this.Exit();
            }

            return(true);
        }
Ejemplo n.º 12
0
        private async Task addItem(MultiplayerPlaylistItem item)
        {
            Debug.Assert(Room != null);

            item.ID = ++lastPlaylistItemId;

            serverSidePlaylist.Add(item);
            await((IMultiplayerClient)this).PlaylistItemAdded(item).ConfigureAwait(false);

            await updatePlaylistOrder(Room).ConfigureAwait(false);
        }
Ejemplo n.º 13
0
        public override Task EditPlaylistItem(MultiplayerPlaylistItem item)
        {
            if (!IsConnected.Value)
            {
                return(Task.CompletedTask);
            }

            Debug.Assert(connection != null);

            return(connection.InvokeAsync(nameof(IMultiplayerServer.EditPlaylistItem), item));
        }
Ejemplo n.º 14
0
 private PlaylistItem createPlaylistItem(MultiplayerPlaylistItem item) => new PlaylistItem
 {
     ID            = item.ID,
     BeatmapID     = item.BeatmapID,
     OwnerID       = item.OwnerID,
     RulesetID     = item.RulesetID,
     Expired       = item.Expired,
     PlaylistOrder = item.PlaylistOrder,
     PlayedAt      = item.PlayedAt,
     RequiredMods  = item.RequiredMods.ToArray(),
     AllowedMods   = item.AllowedMods.ToArray()
 };
Ejemplo n.º 15
0
        private void addItemToLists(MultiplayerPlaylistItem item)
        {
            var apiItem = Playlist.Single(i => i.ID == item.ID);

            if (item.Expired)
            {
                historyList.Items.Add(apiItem);
            }
            else
            {
                queueList.Items.Add(apiItem);
            }
        }
Ejemplo n.º 16
0
        public async Task AddUserPlaylistItem(int userId, MultiplayerPlaylistItem item)
        {
            Debug.Assert(Room != null);
            Debug.Assert(APIRoom != null);
            Debug.Assert(currentItem != null);

            if (Room.Settings.QueueMode == QueueMode.HostOnly && Room.Host?.UserID != LocalUser?.UserID)
            {
                throw new InvalidOperationException("Local user is not the room host.");
            }

            item.OwnerID = userId;

            await addItem(item).ConfigureAwait(false);
            await updateCurrentItem(Room).ConfigureAwait(false);
        }
Ejemplo n.º 17
0
        private async Task updateCurrentItem(MultiplayerRoom room, bool notify = true)
        {
            // Pick the next non-expired playlist item by playlist order, or default to the most-recently-expired item.
            MultiplayerPlaylistItem nextItem = upcomingItems.FirstOrDefault() ?? serverSidePlaylist.OrderByDescending(i => i.PlayedAt).First();

            currentIndex = serverSidePlaylist.IndexOf(nextItem);

            long lastItem = room.Settings.PlaylistItemId;

            room.Settings.PlaylistItemId = nextItem.ID;

            if (notify && nextItem.ID != lastItem)
            {
                await((IMultiplayerClient)this).SettingsChanged(room.Settings).ConfigureAwait(false);
            }
        }
Ejemplo n.º 18
0
        private async Task duplicateCurrentItem()
        {
            Debug.Assert(Room != null);
            Debug.Assert(APIRoom != null);
            Debug.Assert(currentItem != null);

            var newItem = new MultiplayerPlaylistItem
            {
                ID              = serverSidePlaylist.Last().ID + 1,
                BeatmapID       = currentItem.BeatmapID,
                BeatmapChecksum = currentItem.BeatmapChecksum,
                RulesetID       = currentItem.RulesetID,
                RequiredMods    = currentItem.RequiredMods,
                AllowedMods     = currentItem.AllowedMods
            };

            serverSidePlaylist.Add(newItem);
            await((IMultiplayerClient)this).PlaylistItemAdded(newItem).ConfigureAwait(false);
        }
Ejemplo n.º 19
0
        private void addItemToLists(MultiplayerPlaylistItem item)
        {
            var apiItem = Playlist.SingleOrDefault(i => i.ID == item.ID);

            // Item could have been removed from the playlist while the local player was in gameplay.
            if (apiItem == null)
            {
                return;
            }

            if (item.Expired)
            {
                historyList.Items.Add(apiItem);
            }
            else
            {
                queueList.Items.Add(apiItem);
            }
        }
Ejemplo n.º 20
0
        public async Task AddUserPlaylistItem(int userId, MultiplayerPlaylistItem item)
        {
            Debug.Assert(Room != null);
            Debug.Assert(APIRoom != null);
            Debug.Assert(currentItem != null);

            if (Room.Settings.QueueMode == QueueMode.HostOnly && Room.Host?.UserID != LocalUser?.UserID)
            {
                throw new InvalidOperationException("Local user is not the room host.");
            }

            switch (Room.Settings.QueueMode)
            {
            case QueueMode.HostOnly:
                // In host-only mode, the current item is re-used.
                item.ID      = currentItem.ID;
                item.OwnerID = currentItem.OwnerID;

                serverSidePlaylist[currentIndex] = item;
                await((IMultiplayerClient)this).PlaylistItemChanged(item).ConfigureAwait(false);

                // Note: Unlike the server, this is the easiest way to update the current item at this point.
                await updateCurrentItem(Room, false).ConfigureAwait(false);

                break;

            default:
                item.ID      = serverSidePlaylist.Last().ID + 1;
                item.OwnerID = userId;

                serverSidePlaylist.Add(item);
                await((IMultiplayerClient)this).PlaylistItemAdded(item).ConfigureAwait(false);

                await updateCurrentItem(Room).ConfigureAwait(false);

                break;
            }
        }
Ejemplo n.º 21
0
        protected override void PlaylistItemChanged(MultiplayerPlaylistItem item)
        {
            base.PlaylistItemChanged(item);

            var newApiItem             = Playlist.SingleOrDefault(i => i.ID == item.ID);
            var existingApiItemInQueue = queueList.Items.SingleOrDefault(i => i.ID == item.ID);

            // Test if the only change between the two playlist items is the order.
            if (newApiItem != null && existingApiItemInQueue != null && existingApiItemInQueue.With(playlistOrder: newApiItem.PlaylistOrder).Equals(newApiItem))
            {
                // Set the new playlist order directly without refreshing the DrawablePlaylistItem.
                existingApiItemInQueue.PlaylistOrder = newApiItem.PlaylistOrder;

                // The following isn't really required, but is here for safety and explicitness.
                // MultiplayerQueueList internally binds to changes in Playlist to invalidate its own layout, which is mutated on every playlist operation.
                queueList.Invalidate();
            }
            else
            {
                removeItemFromLists(item.ID);
                addItemToLists(item);
            }
        }
Ejemplo n.º 22
0
        public async Task AddUserPlaylistItem(int userId, MultiplayerPlaylistItem item)
        {
            Debug.Assert(Room != null);
            Debug.Assert(APIRoom != null);
            Debug.Assert(currentItem != null);

            if (Room.Settings.QueueMode == QueueMode.HostOnly && Room.Host?.UserID != LocalUser?.UserID)
            {
                throw new InvalidOperationException("Local user is not the room host.");
            }

            item.OwnerID = userId;

            switch (Room.Settings.QueueMode)
            {
            case QueueMode.HostOnly:
                // In host-only mode, the current item is re-used.
                item.ID            = currentItem.ID;
                item.PlaylistOrder = currentItem.PlaylistOrder;

                serverSidePlaylist[currentIndex] = item;
                await((IMultiplayerClient)this).PlaylistItemChanged(item).ConfigureAwait(false);

                // Note: Unlike the server, this is the easiest way to update the current item at this point.
                await updateCurrentItem(Room, false).ConfigureAwait(false);

                break;

            default:
                await addItem(item).ConfigureAwait(false);

                // The current item can change as a result of an item being added. For example, if all items earlier in the queue were expired.
                await updateCurrentItem(Room).ConfigureAwait(false);

                break;
            }
        }
Ejemplo n.º 23
0
        private PlaylistItem createPlaylistItem(MultiplayerPlaylistItem item)
        {
            var ruleset = Rulesets.GetRuleset(item.RulesetID);

            Debug.Assert(ruleset != null);

            var rulesetInstance = ruleset.CreateInstance();

            var playlistItem = new PlaylistItem
            {
                ID            = item.ID,
                BeatmapID     = item.BeatmapID,
                OwnerID       = item.OwnerID,
                Ruleset       = { Value = ruleset },
                Expired       = item.Expired,
                PlaylistOrder = item.PlaylistOrder,
                PlayedAt      = item.PlayedAt
            };

            playlistItem.RequiredMods.AddRange(item.RequiredMods.Select(m => m.ToMod(rulesetInstance)));
            playlistItem.AllowedMods.AddRange(item.AllowedMods.Select(m => m.ToMod(rulesetInstance)));

            return(playlistItem);
        }
Ejemplo n.º 24
0
 private void invokeItemChanged(MultiplayerPlaylistItem item) => Schedule(() => PlaylistItemChanged(item));
Ejemplo n.º 25
0
 public abstract Task AddPlaylistItem(MultiplayerPlaylistItem item);
Ejemplo n.º 26
0
 public override Task EditPlaylistItem(MultiplayerPlaylistItem item) => EditUserPlaylistItem(api.LocalUser.Value.OnlineID, item);
Ejemplo n.º 27
0
 protected override void PlaylistItemAdded(MultiplayerPlaylistItem item)
 {
     base.PlaylistItemAdded(item);
     addItemToLists(item);
 }
Ejemplo n.º 28
0
 protected virtual void PlaylistItemChanged(MultiplayerPlaylistItem item)
 {
 }