private void addUser(MultiplayerRoomUser user)
        {
            ((IMultiplayerClient)this).UserJoined(user).Wait();

            // We want the user to be immediately available for testing, so force a scheduler update to run the update-bound continuation.
            Scheduler.Update();
        }
Exemple #2
0
        Task IMultiplayerClient.UserJoined(MultiplayerRoomUser user)
        {
            Debug.Assert(Room != null);
            Room.Users.Add(user);

            return(Task.CompletedTask);
        }
Exemple #3
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.
            }
        }
Exemple #4
0
        Task IMultiplayerClient.UserLeft(MultiplayerRoomUser user)
        {
            Debug.Assert(Room != null);
            Room.Users.Remove(user);

            return(Task.CompletedTask);
        }
Exemple #5
0
        public ParticipantPanel(MultiplayerRoomUser user)
        {
            User = user;

            RelativeSizeAxes = Axes.X;
            Height           = 40;
        }
Exemple #6
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.
            }
        }
        private async Task setNewHost(MultiplayerRoom room, MultiplayerRoomUser newHost)
        {
            room.Host = newHost;
            await Clients.Group(GetGroupId(room.RoomID)).HostChanged(newHost.UserID);

            await updateDatabaseHost(room);
        }
        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));
        }
            public TrackedUserData(MultiplayerRoomUser user, ScoreProcessor scoreProcessor)
            {
                User           = user;
                ScoreProcessor = scoreProcessor;

                ScoringMode.BindValueChanged(_ => UpdateScore());
            }
Exemple #10
0
        public async Task UserJoinLeaveNotifiesOtherUsers()
        {
            await Hub.JoinRoom(ROOM_ID); // join an arbitrary first user (listener).

            SetUserContext(ContextUser2);
            await Hub.JoinRoom(ROOM_ID);

            Database.Verify(db => db.AddRoomParticipantAsync(It.Is <MultiplayerRoom>(r => r.RoomID == ROOM_ID), It.Is <MultiplayerRoomUser>(u => u.UserID == USER_ID)), Times.Once);

            var roomUser = new MultiplayerRoomUser(USER_ID_2);

            await Assert.ThrowsAsync <InvalidStateException>(() => Hub.JoinRoom(ROOM_ID)); // invalid join

            Receiver.Verify(r => r.UserJoined(roomUser), Times.Once);
            Database.Verify(db => db.AddRoomParticipantAsync(It.Is <MultiplayerRoom>(r => r.RoomID == ROOM_ID), It.Is <MultiplayerRoomUser>(u => u.UserID == USER_ID_2)), Times.Once);

            await Hub.LeaveRoom();

            Receiver.Verify(r => r.UserLeft(roomUser), Times.Once);
            Database.Verify(db => db.RemoveRoomParticipantAsync(It.Is <MultiplayerRoom>(r => r.RoomID == ROOM_ID), It.Is <MultiplayerRoomUser>(u => u.UserID == USER_ID_2)), Times.Once);

            await Hub.JoinRoom(ROOM_ID);

            Receiver.Verify(r => r.UserJoined(roomUser), Times.Exactly(2));

            await Hub.LeaveRoom();

            Receiver.Verify(r => r.UserLeft(roomUser), Times.Exactly(2));
        }
Exemple #11
0
        protected override void OnRoomChanged()
        {
            base.OnRoomChanged();

            localUser            = Room?.Users.Single(u => u.User?.Id == api.LocalUser.Value.Id);
            button.Enabled.Value = Client.Room?.State == MultiplayerRoomState.Open;
            updateState();
        }
Exemple #12
0
            public TrackedUserData(MultiplayerRoomUser user, RulesetInfo ruleset, ScoreProcessor scoreProcessor)
            {
                this.ruleset = ruleset;

                User           = user;
                ScoreProcessor = scoreProcessor;

                ScoringMode.BindValueChanged(_ => UpdateScore());
            }
        protected override void OnRoomUpdated()
        {
            base.OnRoomUpdated();

            // this method is called on leaving the room, so the local user may not exist in the room any more.
            localUser = Room?.Users.SingleOrDefault(u => u.User?.Id == api.LocalUser.Value.Id);

            button.Enabled.Value = Client.Room?.State == MultiplayerRoomState.Open;
            updateState();
        }
Exemple #14
0
        public TeamDisplay(MultiplayerRoomUser user)
        {
            this.user = user;

            RelativeSizeAxes = Axes.Y;

            AutoSizeAxes = Axes.X;

            Margin = new MarginPadding {
                Horizontal = 3
            };
        }
Exemple #15
0
        public TeamDisplay(MultiplayerRoomUser user)
        {
            this.user = user;

            RelativeSizeAxes = Axes.Y;
            Width            = 15;

            Margin = new MarginPadding {
                Horizontal = 3
            };

            Alpha = 0;
            Scale = new Vector2(0, 1);
        }
        private void addUser(APIUser user, bool asHost = false)
        {
            var multiplayerRoomUser = new MultiplayerRoomUser(user.Id)
            {
                User = user
            };

            multiplayerRoom.Users.Add(multiplayerRoomUser);

            if (asHost)
            {
                transferHost(multiplayerRoomUser);
            }

            raiseRoomUpdated();
        }
Exemple #17
0
        public MultiplayerRoomUser AddUser(APIUser user, bool markAsPlaying = false)
        {
            var roomUser = new MultiplayerRoomUser(user.Id)
            {
                User = user
            };

            addUser(roomUser);

            if (markAsPlaying)
            {
                PlayingUserIds.Add(user.Id);
            }

            return(roomUser);
        }
Exemple #18
0
        public MultiplayerRoomUser AddUser(User user, bool markAsPlaying = false)
        {
            var roomUser = new MultiplayerRoomUser(user.Id)
            {
                User = user
            };

            ((IMultiplayerClient)this).UserJoined(roomUser);

            if (markAsPlaying)
            {
                PlayingUserIds.Add(user.Id);
            }

            return(roomUser);
        }
        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),
                };
            });
        }
Exemple #20
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));
        }
Exemple #21
0
        private void pressReadyButton(int?playingUserId = null)
        {
            AddUntilStep("wait for ready button to be enabled", () => readyButton.Enabled.Value);

            MultiplayerUserState lastState = MultiplayerUserState.Idle;
            MultiplayerRoomUser  user      = null;

            AddStep("click ready button", () =>
            {
                user      = playingUserId == null ? client.LocalUser : client.Room?.Users.Single(u => u.UserID == playingUserId);
                lastState = user?.State ?? MultiplayerUserState.Idle;

                InputManager.MoveMouseTo(readyButton);
                InputManager.Click(MouseButton.Left);
            });

            AddUntilStep("wait for state change", () => user?.State != lastState);
        }
Exemple #22
0
        private void start(int[] userIds, int?beatmapId = null)
        {
            AddStep("start play", () =>
            {
                foreach (int id in userIds)
                {
                    var user = new MultiplayerRoomUser(id)
                    {
                        User = new User {
                            Id = id
                        },
                    };

                    OnlinePlayDependencies.Client.AddUser(user.User, true);
                    SpectatorClient.StartPlay(id, beatmapId ?? importedBeatmapId);

                    playingUsers.Add(user);
                }
            });
        }
Exemple #23
0
        public async Task <MultiplayerRoom> JoinRoom(long roomId)
        {
            var state = await GetLocalUserState();

            if (state != null)
            {
                // if the user already has a state, it means they are already in a room and can't join another without first leaving.
                throw new InvalidStateException("Can't join a room when already in another room.");
            }

            // add the user to the room.
            var roomUser = new MultiplayerRoomUser(CurrentContextUserId);

            // check whether we are already aware of this match.
            if (!TryGetRoom(roomId, out var room))
            {
                room = await RetrieveRoom(roomId);

                room.Host = roomUser;

                lock (active_rooms)
                {
                    Console.WriteLine($"Tracking new room {roomId}.");
                    active_rooms.Add(roomId, room);
                }
            }

            using (room.LockForUpdate())
            {
                room.Users.Add(roomUser);
                await Clients.Group(GetGroupId(roomId)).UserJoined(roomUser);

                await Groups.AddToGroupAsync(Context.ConnectionId, GetGroupId(roomId));

                await UpdateDatabaseParticipants(room);
            }

            await UpdateLocalUserState(new MultiplayerClientState(roomId));

            return(room);
        }
Exemple #24
0
        protected override async Task <MultiplayerRoom> JoinRoom(long roomId, string?password = null)
        {
            var apiRoom = roomManager.ServerSideRooms.Single(r => r.RoomID.Value == roomId);

            if (password != apiRoom.Password.Value)
            {
                throw new InvalidOperationException("Invalid password.");
            }

            serverSidePlaylist.Clear();
            serverSidePlaylist.AddRange(apiRoom.Playlist.Select(item => new MultiplayerPlaylistItem(item)));
            lastPlaylistItemId = serverSidePlaylist.Max(item => item.ID);

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

            var room = new MultiplayerRoom(roomId)
            {
                Settings =
                {
                    Name              = apiRoom.Name.Value,
                    MatchType         = apiRoom.Type.Value,
                    Password          = password,
                    QueueMode         = apiRoom.QueueMode.Value,
                    AutoStartDuration = apiRoom.AutoStartDuration.Value
                },
                Playlist = serverSidePlaylist.ToList(),
                Users    = { localUser },
                Host     = localUser
            };

            await updatePlaylistOrder(room).ConfigureAwait(false);
            await updateCurrentItem(room, false).ConfigureAwait(false);

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

            return(room);
        }
Exemple #25
0
        protected override Task <MultiplayerRoom> JoinRoom(long roomId, string?password = null)
        {
            var apiRoom = roomManager.ServerSideRooms.Single(r => r.RoomID.Value == roomId);

            if (password != apiRoom.Password.Value)
            {
                throw new InvalidOperationException("Invalid password.");
            }

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

            var room = new MultiplayerRoom(roomId)
            {
                Settings =
                {
                    Name            = apiRoom.Name.Value,
                    MatchType       = apiRoom.Type.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,
                    // ReSharper disable once ConstantNullCoalescingCondition Incorrect inspection due to lack of nullable in Room.cs.
                    Password        = password ?? string.Empty,
                },
                Users = { localUser },
                Host  = localUser
            };

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

            return(Task.FromResult(room));
        }
Exemple #26
0
        private void addUser(MultiplayerRoomUser user)
        {
            ((IMultiplayerClient)this).UserJoined(user).Wait();

            // We want the user to be immediately available for testing, so force a scheduler update to run the update-bound continuation.
            Scheduler.Update();

            switch (Room?.MatchState)
            {
            case TeamVersusRoomState teamVersus:
                Debug.Assert(Room != null);

                // simulate the server's automatic assignment of users to teams on join.
                // the "best" team is the one with the least users on it.
                int bestTeam = teamVersus.Teams
                               .Select(team => (teamID: team.ID, userCount: Room.Users.Count(u => (u.MatchState as TeamVersusUserState)?.TeamID == team.ID)))
                               .OrderBy(pair => pair.userCount)
                               .First().teamID;
                ((IMultiplayerClient)this).MatchUserStateChanged(user.UserID, new TeamVersusUserState {
                    TeamID = bestTeam
                }).Wait();
                break;
            }
        }
 private Task changeAndBroadcastUserState(MultiplayerRoom room, MultiplayerRoomUser user, MultiplayerUserState state)
 {
     user.State = state;
     return(Clients.Group(GetGroupId(room.RoomID)).UserStateChanged(user.UserID, user.State));
 }
        public async Task <MultiplayerRoom> JoinRoom(long roomId)
        {
            Log($"Joining room {roomId}");

            bool isRestricted;

            using (var db = databaseFactory.GetInstance())
                isRestricted = await db.IsUserRestrictedAsync(CurrentContextUserId);

            if (isRestricted)
            {
                throw new InvalidStateException("Can't join a room when restricted.");
            }

            using (var userUsage = await GetOrCreateLocalUserState())
            {
                if (userUsage.Item != null)
                {
                    // if the user already has a state, it means they are already in a room and can't join another without first leaving.
                    throw new InvalidStateException("Can't join a room when already in another room.");
                }

                // add the user to the room.
                var roomUser = new MultiplayerRoomUser(CurrentContextUserId);

                // track whether this join necessitated starting the process of fetching the room and adding it to the ACTIVE_ROOMS store.
                bool newRoomFetchStarted = false;

                MultiplayerRoom?room = null;

                using (var roomUsage = await ACTIVE_ROOMS.GetForUse(roomId, true))
                {
                    try
                    {
                        if (roomUsage.Item == null)
                        {
                            newRoomFetchStarted = true;

                            // the requested room is not yet tracked by this server.
                            room = await retrieveRoom(roomId);

                            // the above call will only succeed if this user is the host.
                            room.Host = roomUser;

                            // mark the room active - and wait for confirmation of this operation from the database - before adding the user to the room.
                            await markRoomActive(room);

                            roomUsage.Item = room;
                        }
                        else
                        {
                            room = roomUsage.Item;

                            // this is a sanity check to keep *rooms* in a good state.
                            // in theory the connection clean-up code should handle this correctly.
                            if (room.Users.Any(u => u.UserID == roomUser.UserID))
                            {
                                throw new InvalidOperationException($"User {roomUser.UserID} attempted to join room {room.RoomID} they are already present in.");
                            }
                        }

                        userUsage.Item = new MultiplayerClientState(Context.ConnectionId, CurrentContextUserId, roomId);
                        room.Users.Add(roomUser);

                        await updateDatabaseParticipants(room);

                        await Clients.Group(GetGroupId(roomId)).UserJoined(roomUser);

                        await Groups.AddToGroupAsync(Context.ConnectionId, GetGroupId(roomId));

                        Log($"Joined room {room.RoomID}");
                    }
                    catch
                    {
                        try
                        {
                            if (userUsage.Item != null)
                            {
                                // the user was joined to the room, so we can run the standard leaveRoom method.
                                // this will handle closing the room if this was the only user.
                                await leaveRoom(userUsage.Item, roomUsage);
                            }
                            else if (newRoomFetchStarted)
                            {
                                if (room != null)
                                {
                                    // the room was retrieved and associated to the usage, but something failed before the user (host) could join.
                                    // for now, let's mark the room as ended if this happens.
                                    await endDatabaseMatch(room);
                                }

                                roomUsage.Destroy();
                            }
                        }
                        finally
                        {
                            // no matter how we end up cleaning up the room, ensure the user's context is destroyed.
                            userUsage.Destroy();
                        }

                        throw;
                    }
                }

                return(JsonConvert.DeserializeObject <MultiplayerRoom>(JsonConvert.SerializeObject(room)) ?? throw new InvalidOperationException());
            }
        }
Exemple #29
0
        protected override void UserKicked(MultiplayerRoomUser user)
        {
            base.UserKicked(user);

            userKickedSample?.Play();
        }
Exemple #30
0
        protected override void UserLeft(MultiplayerRoomUser user)
        {
            base.UserLeft(user);

            userLeftSample?.Play();
        }