private void ensureIsHost(MultiplayerRoom room)
 {
     if (room.Host?.UserID != CurrentContextUserId)
     {
         throw new NotHostException();
     }
 }
 public Task MarkRoomActiveAsync(MultiplayerRoom room)
 {
     return(connection.ExecuteAsync("UPDATE multiplayer_rooms SET ends_at = null WHERE id = @RoomID", new
     {
         RoomID = room.RoomID
     }));
 }
Esempio n. 3
0
        private async Task updateCurrentItem(MultiplayerRoom room, bool notify = true)
        {
            MultiplayerPlaylistItem newItem;

            switch (room.Settings.QueueMode)
            {
            default:
                // Pick the single non-expired playlist item.
                newItem = serverSidePlaylist.FirstOrDefault(i => !i.Expired) ?? serverSidePlaylist.Last();
                break;

            case QueueMode.AllPlayersRoundRobin:
                // Group playlist items by (user_id -> count_expired), and select the first available playlist item from a user that has available beatmaps where count_expired is the lowest.
                throw new NotImplementedException();
            }

            currentIndex = serverSidePlaylist.IndexOf(newItem);

            long lastItem = room.Settings.PlaylistItemId;

            room.Settings.PlaylistItemId = newItem.ID;

            if (notify && newItem.ID != lastItem)
            {
                await((IMultiplayerClient)this).SettingsChanged(room.Settings).ConfigureAwait(false);
            }
        }
Esempio n. 4
0
        protected virtual async Task UpdateDatabaseParticipants(MultiplayerRoom room)
        {
            using (var conn = Database.GetConnection())
            {
                using (var transaction = await conn.BeginTransactionAsync())
                {
                    // This should be considered *very* temporary, and for display purposes only!
                    await conn.ExecuteAsync("DELETE FROM multiplayer_rooms_high WHERE room_id = @RoomID", new
                    {
                        RoomID = room.RoomID
                    }, transaction);

                    foreach (var u in room.Users)
                    {
                        await conn.ExecuteAsync("INSERT INTO multiplayer_rooms_high (room_id, user_id) VALUES (@RoomID, @UserID)", new
                        {
                            RoomID = room.RoomID,
                            UserID = u.UserID
                        }, transaction);
                    }

                    await transaction.CommitAsync();
                }

                await conn.ExecuteAsync("UPDATE multiplayer_rooms SET participant_count = @Count WHERE id = @RoomID", new
                {
                    RoomID = room.RoomID,
                    Count  = room.Users.Count
                });
            }
        }
        private async Task setNewHost(MultiplayerRoom room, MultiplayerRoomUser newHost)
        {
            room.Host = newHost;
            await Clients.Group(GetGroupId(room.RoomID)).HostChanged(newHost.UserID);

            await updateDatabaseHost(room);
        }
        private async Task markRoomActive(MultiplayerRoom room)
        {
            Log($"Host marking room active {room.RoomID}");

            using (var db = databaseFactory.GetInstance())
                await db.MarkRoomActiveAsync(room);
        }
        public async Task UpdateRoomParticipantsAsync(MultiplayerRoom room)
        {
            try
            {
                using (var transaction = await connection.BeginTransactionAsync())
                {
                    // This should be considered *very* temporary, and for display purposes only!
                    await connection.ExecuteAsync("DELETE FROM multiplayer_rooms_high WHERE room_id = @RoomID", new
                    {
                        RoomID = room.RoomID
                    }, transaction);

                    foreach (var u in room.Users)
                    {
                        await connection.ExecuteAsync("INSERT INTO multiplayer_rooms_high (room_id, user_id) VALUES (@RoomID, @UserID)", new
                        {
                            RoomID = room.RoomID,
                            UserID = u.UserID
                        }, transaction);
                    }

                    await transaction.CommitAsync();
                }

                await connection.ExecuteAsync("UPDATE multiplayer_rooms SET participant_count = @Count WHERE id = @RoomID", new
                {
                    RoomID = room.RoomID,
                    Count  = room.Users.Count
                });
            }
            catch (MySqlException)
            {
                // for now we really don't care about failures in this. it's updating display information each time a user joins/quits and doesn't need to be perfect.
            }
        }
 public Task EndMatchAsync(MultiplayerRoom room)
 {
     return(connection.ExecuteAsync("UPDATE multiplayer_rooms SET ends_at = NOW() WHERE id = @RoomID", new
     {
         RoomID = room.RoomID
     }));
 }
Esempio n. 9
0
        protected override Task <MultiplayerRoom> JoinRoom(long roomId)
        {
            var apiRoom = roomManager.Rooms.Single(r => r.RoomID.Value == roomId);

            var localUser = new MultiplayerRoomUser(api.LocalUser.Value.Id)
            {
                User = api.LocalUser.Value
            };

            var room = new MultiplayerRoom(roomId)
            {
                Settings =
                {
                    Name            = apiRoom.Name.Value,
                    BeatmapID       = apiRoom.Playlist.Last().BeatmapID,
                    RulesetID       = apiRoom.Playlist.Last().RulesetID,
                    BeatmapChecksum = apiRoom.Playlist.Last().Beatmap.Value.MD5Hash,
                    RequiredMods    = apiRoom.Playlist.Last().RequiredMods.Select(m => new APIMod(m)).ToArray(),
                    AllowedMods     = apiRoom.Playlist.Last().AllowedMods.Select(m => new APIMod(m)).ToArray(),
                    PlaylistItemId  = apiRoom.Playlist.Last().ID
                },
                Users = { localUser },
                Host  = localUser
            };

            RoomSetupAction?.Invoke(room);
            RoomSetupAction = null;

            APIRoom = apiRoom;

            return(Task.FromResult(room));
        }
Esempio n. 10
0
        public async Task RemoveRoomParticipantAsync(MultiplayerRoom room, MultiplayerRoomUser user)
        {
            try
            {
                using (var transaction = await connection.BeginTransactionAsync())
                {
                    await connection.ExecuteAsync("UPDATE multiplayer_rooms_high SET in_room = 0 WHERE room_id = @RoomID AND user_id = @UserID", new
                    {
                        RoomID = room.RoomID,
                        UserID = user.UserID
                    }, transaction);

                    await connection.ExecuteAsync("UPDATE multiplayer_rooms SET participant_count = @Count WHERE id = @RoomID", new
                    {
                        RoomID = room.RoomID,
                        Count  = room.Users.Count
                    }, transaction);

                    await transaction.CommitAsync();
                }
            }
            catch (MySqlException)
            {
                // for now we really don't care about failures in this. it's updating display information each time a user joins/quits and doesn't need to be perfect.
            }
        }
Esempio n. 11
0
        public async Task AddRoomParticipantAsync(MultiplayerRoom room, MultiplayerRoomUser user)
        {
            try
            {
                using (var transaction = await connection.BeginTransactionAsync())
                {
                    // the user may have previously been in the room and set some scores, so need to update their presence if existing.
                    await connection.ExecuteAsync("INSERT INTO multiplayer_rooms_high (room_id, user_id, in_room) VALUES (@RoomID, @UserID, 1) ON DUPLICATE KEY UPDATE in_room = 1", new
                    {
                        RoomID = room.RoomID,
                        UserID = user.UserID
                    }, transaction);

                    await connection.ExecuteAsync("UPDATE multiplayer_rooms SET participant_count = @Count WHERE id = @RoomID", new
                    {
                        RoomID = room.RoomID,
                        Count  = room.Users.Count
                    }, transaction);

                    await transaction.CommitAsync();
                }
            }
            catch (MySqlException)
            {
                // for now we really don't care about failures in this. it's updating display information each time a user joins/quits and doesn't need to be perfect.
            }
        }
Esempio n. 12
0
        private async Task updateDatabaseSettings(MultiplayerRoom room)
        {
            using (var db = databaseFactory.GetInstance())
            {
                var item   = new multiplayer_playlist_item(room);
                var dbItem = await db.GetPlaylistItemFromRoomAsync(room.RoomID, item.id);

                if (dbItem == null)
                {
                    throw new InvalidStateException("Attempted to select a playlist item not contained by the room.");
                }

                if (dbItem.expired)
                {
                    throw new InvalidStateException("Attempted to select an expired playlist item.");
                }

                string?beatmapChecksum = await db.GetBeatmapChecksumAsync(item.beatmap_id);

                if (beatmapChecksum == null)
                {
                    throw new InvalidStateException("Attempted to select a beatmap which does not exist online.");
                }

                if (room.Settings.BeatmapChecksum != beatmapChecksum)
                {
                    throw new InvalidStateException("Attempted to select a beatmap which has been modified.");
                }

                await db.UpdateRoomSettingsAsync(room);
            }
        }
Esempio n. 13
0
        private static bool validateMods(MultiplayerRoom room, IEnumerable <APIMod> proposedMods, [NotNullWhen(false)] out IEnumerable <APIMod>?validMods)
        {
            bool proposedWereValid = true;

            proposedWereValid &= populateValidModsForRuleset(room.Settings.RulesetID, proposedMods, out var valid);

            // check allowed by room
            foreach (var mod in valid.ToList())
            {
                if (room.Settings.AllowedMods.All(m => m.Acronym != mod.Acronym))
                {
                    valid.Remove(mod);
                    proposedWereValid = false;
                }
            }

            // check valid as combination
            if (!ModUtils.CheckCompatibleSet(valid, out var invalid))
            {
                proposedWereValid = false;
                foreach (var mod in invalid)
                {
                    valid.Remove(mod);
                }
            }

            validMods = valid.Select(m => new APIMod(m));
            return(proposedWereValid);
        }
        private async Task updatePlaylistOrder(MultiplayerRoom room)
        {
            Debug.Assert(serverSideAPIRoom != null);

            List <MultiplayerPlaylistItem> orderedActiveItems;

            switch (room.Settings.QueueMode)
            {
            default:
                orderedActiveItems = serverSidePlaylist.Where(item => !item.Expired).OrderBy(item => item.ID).ToList();
                break;

            case QueueMode.AllPlayersRoundRobin:
                var itemsByPriority = new List <(MultiplayerPlaylistItem item, int priority)>();

                // Assign a priority for items from each user, starting from 0 and increasing in order which the user added the items.
                foreach (var group in serverSidePlaylist.Where(item => !item.Expired).OrderBy(item => item.ID).GroupBy(item => item.OwnerID))
                {
                    int priority = 0;
                    itemsByPriority.AddRange(group.Select(item => (item, priority++)));
                }

                orderedActiveItems = itemsByPriority
                                     // Order by each user's priority.
                                     .OrderBy(i => i.priority)
                                     // Many users will have the same priority of items, so attempt to break the tie by maintaining previous ordering.
                                     // Suppose there are two users: User1 and User2. User1 adds two items, and then User2 adds a third. If the previous order is not maintained,
                                     // then after playing the first item by User1, their second item will become priority=0 and jump to the front of the queue (because it was added first).
                                     .ThenBy(i => i.item.PlaylistOrder)
                                     // If there are still ties (normally shouldn't happen), break ties by making items added earlier go first.
                                     // This could happen if e.g. the item orders get reset.
                                     .ThenBy(i => i.item.ID)
                                     .Select(i => i.item)
                                     .ToList();

                break;
            }

            for (int i = 0; i < orderedActiveItems.Count; i++)
            {
                var item = orderedActiveItems[i];

                if (item.PlaylistOrder == i)
                {
                    continue;
                }

                item.PlaylistOrder = (ushort)i;

                await((IMultiplayerClient)this).PlaylistItemChanged(item).ConfigureAwait(false);
            }

            // Also ensure that the API room's playlist is correct.
            foreach (var item in serverSideAPIRoom.Playlist)
            {
                item.PlaylistOrder = serverSidePlaylist.Single(i => i.ID == item.ID).PlaylistOrder;
            }
        }
    public static void RPCRequestVoice(int id)
    {
        if (!instance.studentsRequesting.Contains(id))
        {
            instance.studentsRequesting.Add(id);
        }

        var player = MultiplayerRoom.GetPlayerByID(id);
    }
        /// <summary>
        /// Create a playlist item model from the latest settings in a room.
        /// </summary>
        /// <param name="room">The room to retrieve settings from.</param>
        public multiplayer_playlist_item(MultiplayerRoom room)
        {
            room_id = room.RoomID;

            beatmap_id    = room.Settings.BeatmapID;
            ruleset_id    = (short)room.Settings.RulesetID;
            required_mods = JsonConvert.SerializeObject(room.Settings.Mods);
            updated_at    = DateTimeOffset.Now;
        }
Esempio n. 17
0
        private void ensureValidStateSwitch(MultiplayerRoom room, MultiplayerUserState oldState, MultiplayerUserState newState)
        {
            switch (newState)
            {
            case MultiplayerUserState.Idle:
                // any state can return to idle.
                break;

            case MultiplayerUserState.Ready:
                if (oldState != MultiplayerUserState.Idle)
                {
                    throw new InvalidStateChangeException(oldState, newState);
                }

                break;

            case MultiplayerUserState.WaitingForLoad:
                // state is managed by the server.
                throw new InvalidStateChangeException(oldState, newState);

            case MultiplayerUserState.Loaded:
                if (oldState != MultiplayerUserState.WaitingForLoad)
                {
                    throw new InvalidStateChangeException(oldState, newState);
                }

                break;

            case MultiplayerUserState.Playing:
                // state is managed by the server.
                throw new InvalidStateChangeException(oldState, newState);

            case MultiplayerUserState.FinishedPlay:
                if (oldState != MultiplayerUserState.Playing)
                {
                    throw new InvalidStateChangeException(oldState, newState);
                }

                break;

            case MultiplayerUserState.Results:
                // state is managed by the server.
                throw new InvalidStateChangeException(oldState, newState);

            case MultiplayerUserState.Spectating:
                if (oldState != MultiplayerUserState.Idle && oldState != MultiplayerUserState.Ready)
                {
                    throw new InvalidStateChangeException(oldState, newState);
                }

                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(newState), newState, null);
            }
        }
Esempio n. 18
0
 private async Task ensureAllUsersValidMods(MultiplayerRoom room)
 {
     foreach (var user in room.Users)
     {
         if (!validateMods(room, user.Mods, out var validMods))
         {
             await changeUserMods(validMods, room, user);
         }
     }
 }
Esempio n. 19
0
 protected virtual async Task EndDatabaseMatch(MultiplayerRoom room)
 {
     using (var conn = Database.GetConnection())
     {
         await conn.ExecuteAsync("UPDATE multiplayer_rooms SET ends_at = NOW() WHERE id = @RoomID", new
         {
             RoomID = room.RoomID
         });
     }
 }
        public async Task UpdateRoomSettingsAsync(MultiplayerRoom room)
        {
            var dbPlaylistItem = new multiplayer_playlist_item(room);

            await connection.ExecuteAsync("UPDATE multiplayer_rooms SET name = @Name WHERE id = @RoomID", new
            {
                RoomID = room.RoomID,
                Name   = room.Settings.Name
            });

            await connection.ExecuteAsync("UPDATE multiplayer_playlist_items SET beatmap_id = @beatmap_id, ruleset_id = @ruleset_id, required_mods = @required_mods, updated_at = NOW() WHERE room_id = @room_id", dbPlaylistItem);
        }
        public async Task ClearRoomScoresAsync(MultiplayerRoom room)
        {
            // for now, clear all existing scores out of the playlist item to ensure no duplicates.
            // eventually we will want to increment to a new playlist item rather than reusing the same one.
            long playlistItemId = await connection.QuerySingleAsync <long>("SELECT id FROM multiplayer_playlist_items WHERE room_id = @RoomID", new
            {
                RoomID = room.RoomID,
            });

            await connection.ExecuteAsync("DELETE FROM multiplayer_scores WHERE playlist_item_id = @PlaylistItemID", new { PlaylistItemID = playlistItemId });

            await connection.ExecuteAsync("DELETE FROM multiplayer_scores_high WHERE playlist_item_id = @PlaylistItemID", new { PlaylistItemID = playlistItemId });
        }
Esempio n. 22
0
        public void TestSerialiseRoom()
        {
            var room = new MultiplayerRoom(1)
            {
                MatchState = new TeamVersusRoomState()
            };

            byte[] serialized = MessagePackSerializer.Serialize(room);

            var deserialized = MessagePackSerializer.Deserialize <MultiplayerRoom>(serialized);

            Assert.IsTrue(deserialized.MatchState is TeamVersusRoomState);
        }
Esempio n. 23
0
        protected virtual async Task UpdateDatabaseSettings(MultiplayerRoom room)
        {
            using (var conn = Database.GetConnection())
            {
                var dbPlaylistItem = new multiplayer_playlist_item(room);

                await conn.ExecuteAsync("UPDATE multiplayer_rooms SET name = @Name WHERE id = @RoomID", new
                {
                    RoomID = room.RoomID,
                    Name   = room.Settings.Name
                });

                await conn.ExecuteAsync("UPDATE multiplayer_playlist_items SET beatmap_id = @beatmap_id, ruleset_id = @ruleset_id, required_mods = @required_mods, updated_at = NOW() WHERE room_id = @room_id", dbPlaylistItem);
            }
        }
Esempio n. 24
0
        private async Task updateRoomStateIfRequired(MultiplayerRoom room)
        {
            //check whether a room state change is required.
            switch (room.State)
            {
            case MultiplayerRoomState.WaitingForLoad:
                if (room.Users.All(u => u.State != MultiplayerUserState.WaitingForLoad))
                {
                    var loadedUsers = room.Users.Where(u => u.State == MultiplayerUserState.Loaded).ToArray();

                    if (loadedUsers.Length == 0)
                    {
                        // all users have bailed from the load sequence. cancel the game start.
                        await changeRoomState(room, MultiplayerRoomState.Open);

                        return;
                    }

                    foreach (var u in loadedUsers)
                    {
                        await changeAndBroadcastUserState(room, u, MultiplayerUserState.Playing);
                    }

                    await Clients.Group(GetGroupId(room.RoomID)).MatchStarted();

                    await changeRoomState(room, MultiplayerRoomState.Playing);
                }

                break;

            case MultiplayerRoomState.Playing:
                if (room.Users.All(u => u.State != MultiplayerUserState.Playing))
                {
                    foreach (var u in room.Users.Where(u => u.State == MultiplayerUserState.FinishedPlay))
                    {
                        await changeAndBroadcastUserState(room, u, MultiplayerUserState.Results);
                    }

                    await changeRoomState(room, MultiplayerRoomState.Open);

                    await Clients.Group(GetGroupId(room.RoomID)).ResultsReady();

                    await selectNextPlaylistItem(room);
                }

                break;
            }
        }
Esempio n. 25
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);
            }
        }
Esempio n. 26
0
        public async Task EndMatchAsync(MultiplayerRoom room)
        {
            // Remove all non-expired items from the playlist as they have no scores.
            await connection.ExecuteAsync(
                "DELETE FROM multiplayer_playlist_items p WHERE p.room_id = @RoomID AND p.expired = 0 AND (SELECT COUNT(*) FROM multiplayer_scores s WHERE s.playlist_item_id = p.id) = 0",
                new
            {
                RoomID = room.RoomID
            });

            // Close the room.
            await connection.ExecuteAsync("UPDATE multiplayer_rooms SET ends_at = NOW() WHERE id = @RoomID", new
            {
                RoomID = room.RoomID
            });
        }
Esempio n. 27
0
        public async Task UpdateRoomHostAsync(MultiplayerRoom room)
        {
            Debug.Assert(room.Host != null);

            try
            {
                await connection.ExecuteAsync("UPDATE multiplayer_rooms SET user_id = @HostUserID WHERE id = @RoomID", new
                {
                    HostUserID = room.Host.UserID,
                    RoomID     = room.RoomID
                });
            }
            catch (MySqlException)
            {
                // for now we really don't care about failures in this. it's updating display information each time a user joins/quits and doesn't need to be perfect.
            }
        }
Esempio n. 28
0
        public void SetUpSteps()
        {
            AddStep("reset state", () =>
            {
                multiplayerClient.Invocations.Clear();

                beatmapAvailability.Value = BeatmapAvailability.LocallyAvailable();

                var playlistItem = new PlaylistItem(Beatmap.Value.BeatmapInfo)
                {
                    RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID
                };

                room.Value = new Room
                {
                    Playlist            = { playlistItem },
                    CurrentPlaylistItem = { Value = playlistItem }
                };

                localUser = new MultiplayerRoomUser(API.LocalUser.Value.Id)
                {
                    User = API.LocalUser.Value
                };

                multiplayerRoom = new MultiplayerRoom(0)
                {
                    Playlist =
                    {
                        new MultiplayerPlaylistItem(playlistItem),
                    },
                    Users = { localUser },
                    Host  = localUser,
                };
            });

            AddStep("create control", () =>
            {
                content.Child = control = new MatchStartControl
                {
                    Anchor = Anchor.Centre,
                    Origin = Anchor.Centre,
                    Size   = new Vector2(250, 50),
                };
            });
        }
Esempio n. 29
0
        protected override Task <MultiplayerRoom> JoinRoom(long roomId)
        {
            var user = new MultiplayerRoomUser(api.LocalUser.Value.Id)
            {
                User = api.LocalUser.Value
            };

            var room = new MultiplayerRoom(roomId);

            room.Users.Add(user);

            if (room.Users.Count == 1)
            {
                room.Host = user;
            }

            return(Task.FromResult(room));
        }
Esempio n. 30
0
        public async Task EndMatchAsync(MultiplayerRoom room)
        {
            // Remove all non-expired items from the playlist as they have no scores.
            await connection.ExecuteAsync("DELETE FROM multiplayer_playlist_items p WHERE p.room_id = @RoomID AND p.expired = 0 AND (SELECT COUNT(*) FROM multiplayer_scores s WHERE s.playlist_item_id = p.id) = 0",
                                          new
            {
                RoomID = room.RoomID
            });

            int totalUsers = connection.QuerySingle <int>("SELECT COUNT(*) FROM multiplayer_rooms_high WHERE room_id = @RoomID", new { RoomID = room.RoomID });

            // Close the room.
            await connection.ExecuteAsync("UPDATE multiplayer_rooms SET participant_count = @Count, ends_at = NOW() WHERE id = @RoomID", new
            {
                RoomID = room.RoomID,
                Count  = totalUsers,
            });
        }