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(); }
Task IMultiplayerClient.UserJoined(MultiplayerRoomUser user) { Debug.Assert(Room != null); Room.Users.Add(user); return(Task.CompletedTask); }
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. } }
Task IMultiplayerClient.UserLeft(MultiplayerRoomUser user) { Debug.Assert(Room != null); Room.Users.Remove(user); return(Task.CompletedTask); }
public ParticipantPanel(MultiplayerRoomUser user) { User = user; RelativeSizeAxes = Axes.X; Height = 40; }
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()); }
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)); }
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(); }
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(); }
public TeamDisplay(MultiplayerRoomUser user) { this.user = user; RelativeSizeAxes = Axes.Y; AutoSizeAxes = Axes.X; Margin = new MarginPadding { Horizontal = 3 }; }
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(); }
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); }
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), }; }); }
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)); }
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); }
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); } }); }
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); }
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); }
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)); }
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()); } }
protected override void UserKicked(MultiplayerRoomUser user) { base.UserKicked(user); userKickedSample?.Play(); }
protected override void UserLeft(MultiplayerRoomUser user) { base.UserLeft(user); userLeftSample?.Play(); }