]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
join queue: UI improvements and refactoring
authorbones_was_here <bones_was_here@xonotic.au>
Fri, 14 Jun 2024 08:36:49 +0000 (18:36 +1000)
committerbones_was_here <bones_was_here@xonotic.au>
Mon, 5 Aug 2024 16:26:27 +0000 (02:26 +1000)
Fixes duplicate and redundant chatcon notifications.

Displays relevant centreprint each time the team selection GUI or +jump
are used to try to join (even if the player is already queued) for
better noob friendliness.

Always notifies the player when their team selection conflicts with that
of a queued player.

Notifies queued players to get their attention when they join as it may
take some time before another player triggers the join.

Includes the team the player actually got assigned to in the relevant
centreprint (for the cases where they had no preference, or someone else
chose their preferred team first).

Prevents SetPlayerTeam() and queuePlayer() when the player definitely
can't play (version mismatch, locked teams), fixing misleading
notifications.
Allows players to join the queue even when g_maxplayers blocks them from
joining the match, so they can tag in if someone leaves.

Fixes bug with > 2 teams where if a player with a lower entity number
queues for any available team, a player with a higher entity number
and a specific team preference could be assigned to the same team (by
adding a second pass in Join() ).

Fixes lack of "now playing" notification when a bot joins a gametype
without teams.

This required some refactoring:

Calls joinAllowed() from ClientCommand_selectteam(), passing the
team_index through to queuePlayer().  This means calling queuePlayer
from SetPlayerTeam() is no longer needed.
Uses a different logic order in joinAllowed() when the queue is enabled.

Moves the ForbidSpawn MUTATOR hook from joinAllowed() to
PutPlayerInServer(). It seemed to be in the wrong place anyway: the
player is only blocked from spawning, not from joining the match.

No longer sets the .team field when adding a player to the queue
(they're not actually on the team yet), instead calls SetPlayerTeam()
from Join() when actually spawning the queued player.

Uses the .team_selected field to pass selection conflicts to Join(), it
can't be done with a parameter because `join` and `selectteam` are
separate commands.

Moves the queue conflict detection from Player_SetTeamIndexChecked() to
queuePlayer().

Reduces TeamBalance_JoinBestTeam() callsites.

Fixes some uses of NOTIF_ONE where it should have been NOTIF_ONE_ONLY.

notifications.cfg
qcsrc/common/notifications/all.inc
qcsrc/server/bot/default/bot.qc
qcsrc/server/client.qc
qcsrc/server/client.qh
qcsrc/server/clientkill.qc
qcsrc/server/command/cmd.qc
qcsrc/server/teamplay.qc

index 0f5b959d8ba6ff26d9c608b64878f839bd8c3e89..b1c203976c337b9404b92c0696db6aa054d07e9d 100644 (file)
@@ -488,10 +488,12 @@ seta notification_CENTER_ITEM_WEAPON_PRIMORSEC "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_ITEM_WEAPON_UNAVAILABLE "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_JOIN_NOSPAWNS "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_JOIN_PLAYBAN "1" "0 = off, 1 = centerprint"
+seta notification_CENTER_JOIN_PLAY_TEAM_QUEUECONFLICT "1" "0 = off, 1 = centerprint"
+seta notification_CENTER_JOIN_PLAY_TEAM "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_JOIN_PREVENT "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_JOIN_PREVENT_MINIGAME "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_JOIN_PREVENT_QUEUE "1" "0 = off, 1 = centerprint"
-seta notification_CENTER_JOIN_PREVENT_QUEUE_TEAM_FAIL "1" "0 = off, 1 = centerprint"
+seta notification_CENTER_JOIN_PREVENT_QUEUE_TEAM_CONFLICT "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_JOIN_PREVENT_QUEUE_TEAM "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_KEEPAWAY_DROPPED "1" "0 = off, 1 = centerprint"
 seta notification_CENTER_KEEPAWAY_PICKUP "1" "0 = off, 1 = centerprint"
index 0b4c0402d29ad1a0aec4543d1a5f5156c7dc362f..8e9a1ced60167ac1f4b7313dfaeb468e6514634f 100644 (file)
@@ -725,7 +725,9 @@ string multiteam_info_sprintf(string input, string teamname) { return ((input !=
     MSG_CENTER_NOTIF(JOIN_PREVENT_MINIGAME,             N_ENABLE,    0, 0, "",               CPID_Null,              "0 0",  _("^K1Cannot join given minigame session!"), "" )
     MSG_CENTER_NOTIF(JOIN_PREVENT_QUEUE,                N_ENABLE,    0, 0, "",               CPID_PREVENT_JOIN,      "0 0",  _("^BGYou're queued to join any available team."), "")
     MULTITEAM_CENTER(JOIN_PREVENT_QUEUE_TEAM,           N_ENABLE,    0, 0, "",               CPID_PREVENT_JOIN,      "0 0",  _("^BGYou're queued to join the ^TC^TT^BG team."), "", NAME)
-    MULTITEAM_CENTER(JOIN_PREVENT_QUEUE_TEAM_FAIL,      N_ENABLE,    1, 0, "s1",             CPID_PREVENT_JOIN,      "0 0",  _("^K2Please choose a different team! %s^K2 chose ^TC^TT^K2 first."), "", NAME)
+    MULTITEAM_CENTER(JOIN_PREVENT_QUEUE_TEAM_CONFLICT,  N_ENABLE,    1, 0, "s1",             CPID_PREVENT_JOIN,      "0 0",  _("^K2You're queued to join any available team.\n%s^K2 chose ^TC^TT^K2 first."), "", NAME)
+    MULTITEAM_CENTER(JOIN_PLAY_TEAM_QUEUECONFLICT,      N_ENABLE,    1, 0, "s1",             CPID_Null,              "0 0",  _("^K2You're now playing on ^TC^TT^K2 team!\n%s^K2 chose your preferred team first."), "", NAME)
+    MULTITEAM_CENTER(JOIN_PLAY_TEAM,                    N_ENABLE,    0, 0, "",               CPID_Null,              "0 0",  _("^BGYou're now playing on ^TC^TT^BG team!"), "", NAME)
 
     MSG_CENTER_NOTIF(KEEPAWAY_DROPPED,                  N_ENABLE,    1, 0, "s1",             CPID_KEEPAWAY,          "0 0",  _("^BG%s^BG has dropped the ball!"), "")
     MSG_CENTER_NOTIF(KEEPAWAY_PICKUP,                   N_ENABLE,    1, 0, "s1",             CPID_KEEPAWAY,          "0 0",  _("^BG%s^BG has picked up the ball!"), "")
index 07587befa8794a13bdeb6d0240828f611ce6f8ef..6965b0795f986fd0f4395514ebf7fb827a5105e0 100644 (file)
@@ -3,6 +3,7 @@
 #include <common/constants.qh>
 #include <common/mapinfo.qh>
 #include <common/net_linked.qh>
+#include <common/notifications/all.qh>
 #include <common/physics/player.qh>
 #include <common/stats.qh>
 #include <common/teams.qh>
@@ -52,6 +53,8 @@ entity bot_spawn()
                ClientConnect(bot);
                bot_setclientfields(bot);
                PutClientInServer(bot);
+               if (!teamplay)
+                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_PLAY, bot.netname);
        }
        return bot;
 }
index 749bbf8c476bd2d0132eb269914805d53b2838c8..dbb62150a0aa88e44989e189355f9731e1517b85 100644 (file)
@@ -552,16 +552,23 @@ void GiveWarmupResources(entity this)
 
 void PutPlayerInServer(entity this)
 {
+       if (MUTATOR_CALLHOOK(ForbidSpawn, this))
+               return;
+
        if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
 
        PlayerState_attach(this);
        accuracy_resend(this);
 
-       if (teamplay && this.bot_forced_team)
-               SetPlayerTeam(this, this.bot_forced_team, TEAM_CHANGE_MANUAL);
-
-       if (this.team < 0)
-               TeamBalance_JoinBestTeam(this);
+       if (teamplay)
+       {
+               if (this.bot_forced_team)
+                       SetPlayerTeam(this, this.bot_forced_team, TEAM_CHANGE_MANUAL);
+               else if (this.wants_join > 0)
+                       SetPlayerTeam(this, this.wants_join, TEAM_CHANGE_MANUAL);
+               else if (this.team <= 0 || this.wants_join < 0 || autocvar_g_campaign)
+                       TeamBalance_JoinBestTeam(this);
+       }
 
        entity spot = SelectSpawnPoint(this, false);
        if (!spot) {
@@ -2034,46 +2041,63 @@ bool ShowTeamSelection(entity this)
        return true;
 }
 
+/// it's assumed this isn't called for bots (campaign_bots_may_start, centreprints)
 void Join(entity this, bool queued_join)
 {
-       bool teamautoselect = autocvar_g_campaign || autocvar_g_balance_teams || this.wants_join < 0;
+       entity player_with_dibs = NULL;
 
        if (autocvar_g_campaign && !campaign_bots_may_start && !game_stopped && time >= game_starttime)
                ReadyRestart(true);
 
-       TRANSMUTE(Player, this);
-
        if(queued_join
        && TeamBalance_AreEqual(this, true)) // if a player couldn't tag in for balance, don't join them here as it would cause a stack
        {
-               // First we must put queued player(s) in their team(s) (they chose first).
-               FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != this && it.wants_join,
+               // First we must join player(s) queued for specific team(s) (they chose first)
+               // so TeamBalance_JoinBestTeam() (if necessary) won't select the same team(s).
+               // Relies on `this` skipping the queue (this.team already set, this.wants_join not set) or using autoselect.
+               FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != this && it.wants_join > 0,
                {
+                       // detect any conflict between `this` and a queued player (queuePlayer() handles other conflicts)
+                       if (this.team < 0 && this.team_selected > 0 // `this` can't have their preference
+                       && it.wants_join == this.team_selected) // `it` is the player who already chose the team `this` wanted
+                               player_with_dibs = it;
+
                        Join(it, false);
-                       // ensure TeamBalance_JoinBestTeam will run if necessary for `this`
-                       teamautoselect = true;
                });
-       }
 
-       if(!this.team_selected && teamautoselect)
-               TeamBalance_JoinBestTeam(this);
+               // Second pass: queued players whose team will be autoselected
+               FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != this && it.wants_join < 0,
+               {
+                       Join(it, false);
+               });
+       }
 
        if(autocvar_g_campaign)
                campaign_bots_may_start = true;
 
        Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_PREVENT_JOIN);
 
+       TRANSMUTE(Player, this);
        PutClientInServer(this);
 
-       if(IS_PLAYER(this))
-       if(teamplay && this.team != -1)
+       if(IS_PLAYER(this)) // could be false due to PutClientInServer() mutator hook
        {
-               if(this.wants_join)
-                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_JOIN_PLAY_TEAM), this.netname);
+               if (!teamplay)
+                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_PLAY, this.netname);
+               else if (player_with_dibs)
+                       // limitation: notifications support only 1 translated team name
+                       // so the team `this` preferred can't be mentioned, only the team they got assigned to.
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, APP_TEAM_NUM(this.team, CENTER_JOIN_PLAY_TEAM_QUEUECONFLICT), player_with_dibs.netname);
+               else if (this.wants_join)
+               {
+                       // Get queued player's attention
+                       if (game_starttime <= time) // No countdown in progress
+                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_ANNCE, ANNCE_BEGIN);
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, APP_TEAM_NUM(this.team, CENTER_JOIN_PLAY_TEAM));
+               }
        }
-       else
-               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_PLAY, this.netname);
-       this.team_selected = false;
+
+       this.team_selected = 0;
        this.wants_join = 0;
 }
 
@@ -2147,41 +2171,81 @@ int nJoinAllowed(entity this, entity ignore)
        return free_slots;
 }
 
+// Callsites other than ClientCommand_selectteam() should pass this.wants_join as team_index
+// so the player won't accidentally reset a specific preference by pressing +jump
+// and will see the centreprint with their current preference each time they press +jump.
 bool queuePlayer(entity this, int team_index)
 {
-       if(IS_BOT_CLIENT(this) || !QueueNeeded(this) || QueuedPlayersReady(this, false))
+       if (IS_BOT_CLIENT(this) || !QueueNeeded(this))
                return false;
 
-       if(team_index <= 0)
+       // check if a queued player already chose the selected team
+       if (team_index > 0)
        {
-               // defer team selection until Join()
+               FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != this && it.wants_join == team_index,
+               {
+                       if (QueuedPlayersReady(this, false))
+                       {
+                               // Join() will handle the notification so it can mention the team `player` will actually get
+                               this.team = -1; // force autoselect in Join() (last player skips queue)
+                               this.team_selected = team_index; // tell it which team to check for to find the conflict
+                       }
+                       else // > 2 teams
+                       {
+                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, APP_TEAM_NUM(Team_IndexToTeam(team_index), CENTER_JOIN_PREVENT_QUEUE_TEAM_CONFLICT), it.netname);
+                               this.wants_join = -1; // force autoselect in Join()
+                               this.team_selected = -1; // prevents clobbering by CENTER_JOIN_PREVENT_QUEUE
+                       }
+                       return true;
+               });
+       }
+
+       if (QueuedPlayersReady(this, false))
+               return false;
+
+       if (team_index <= 0) // team auto select deferred until Join()
+       {
+               if (team_index != this.wants_join || !this.wants_join) // prevents chatcon spam
+                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_WANTS, this.netname);
+               if (this.team_selected >= 0) // prevents CENTER_JOIN_PREVENT_QUEUE_TEAM_CONFLICT getting clobbered
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT_QUEUE);
                this.wants_join = -1;
-               this.team_selected = false;
-               this.team = -1;
-               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_WANTS, this.netname);
-               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_JOIN_PREVENT_QUEUE);
+               this.team_selected = 0;
        }
        else
        {
+               int team_num = Team_IndexToTeam(team_index);
+               if (team_index != this.wants_join) // prevents chatcon spam
+                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(team_num, INFO_JOIN_WANTS_TEAM), this.netname);
+               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, APP_TEAM_NUM(team_num, CENTER_JOIN_PREVENT_QUEUE_TEAM));
                this.wants_join = team_index; // Player queued to join
-               this.team_selected = true; // no autoselect in Join()
-               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_JOIN_WANTS_TEAM), this.netname);
-               Send_Notification(NOTIF_ONE, this, MSG_CENTER, APP_TEAM_NUM(this.team, CENTER_JOIN_PREVENT_QUEUE_TEAM));
+               this.team_selected = team_index;
        }
 
        return true;
 }
 
-bool joinAllowed(entity this)
+bool joinAllowed(entity this, int team_index)
 {
        if (CS(this).version_mismatch) return false;
        if (time < CS(this).jointime + MIN_SPEC_TIME) return false;
-       if (!nJoinAllowed(this, this)) return false;
        if (teamplay && lockteams) return false;
-       if (MUTATOR_CALLHOOK(ForbidSpawn, this)) return false;
-       if (ShowTeamSelection(this)) return false;
-       if (this.wants_join) return false;
-       if (queuePlayer(this, 0)) return false;
+
+       if (QueueNeeded(this))
+       {
+               if (team_index == 0) // so ClientCommand_selectteam() can check joinAllowed() before calling SetPlayerTeam() without chicken/egg problem
+                       if (ShowTeamSelection(this)) return false; // only needed by callsites other than selectteam
+               // queuePlayer called here so that only conditions above block queuing (g_maxplayers shouldn't)
+               if (queuePlayer(this, team_index)) return false;
+               if (!nJoinAllowed(this, this)) return false;
+       }
+       else
+       {
+               if (!nJoinAllowed(this, this)) return false;
+               if (team_index == 0) // so ClientCommand_selectteam() can check joinAllowed() before calling SetPlayerTeam() without chicken/egg problem
+                       if (ShowTeamSelection(this)) return false; // only needed by callsites other than selectteam
+       }
+
        return true;
 }
 
@@ -2393,7 +2457,7 @@ void ObserverOrSpectatorThink(entity this)
        }
 
        if (this.flags & FL_JUMPRELEASED) {
-               if (PHYS_INPUT_BUTTON_JUMP(this) && (joinAllowed(this) || time < CS(this).jointime + MIN_SPEC_TIME)) {
+               if (PHYS_INPUT_BUTTON_JUMP(this) && (joinAllowed(this, this.wants_join) || time < CS(this).jointime + MIN_SPEC_TIME)) {
                        this.flags &= ~FL_JUMPRELEASED;
                        this.flags |= FL_SPAWNING;
                } else if((is_spec && (PHYS_INPUT_BUTTON_ATCK(this) || CS(this).impulse == 10 || CS(this).impulse == 15 || CS(this).impulse == 18 || (CS(this).impulse >= 200 && CS(this).impulse <= 209)))
@@ -2446,8 +2510,8 @@ void ObserverOrSpectatorThink(entity this)
                        if(this.flags & FL_SPAWNING)
                        {
                                this.flags &= ~FL_SPAWNING;
-                               if(joinAllowed(this))
-                                       Join(this, true);
+                               if(joinAllowed(this, this.wants_join))
+                                       Join(this, teamplay);
                                else if(time < CS(this).jointime + MIN_SPEC_TIME)
                                        CS(this).autojoin_checked = -1;
                                return;
@@ -2551,8 +2615,8 @@ void PlayerPreThink (entity this)
                        || (!(autocvar_sv_spectate || autocvar_g_campaign || (Player_GetForcedTeamIndex(this) == TEAM_FORCE_SPECTATOR))
                                && (!teamplay || autocvar_g_balance_teams)))
                {
-                       if(joinAllowed(this))
-                               Join(this, true);
+                       if(joinAllowed(this, this.wants_join))
+                               Join(this, teamplay);
                        return;
                }
        }
@@ -2903,7 +2967,7 @@ void PlayerFrame (entity this)
                                        // Can't do this in PutObserverInServer() or SetPlayerTeam() cos it causes
                                        // mouse2 (change spectate mode) to kick the player off the join queue.
                                        this.wants_join = 0;
-                                       this.team_selected = false;
+                                       this.team_selected = 0;
                                        // when the player is kicked off the server, these are called in ClientDisconnect()
                                        if (!TeamBalance_QueuedPlayersTagIn(this))
                                        if (autocvar_g_balance_teams_remove)
index 2bff393590cb278b38e61c70a3611df4de903a50..890a223a02f09eec53e77fc49f8df6c29e2422ee 100644 (file)
@@ -72,7 +72,8 @@ float autocvar_sv_player_scale;
 .int spectatee_status;
 .bool zoomstate;
 
-.bool team_selected;
+/// > 0 is a team index, if wants_join == -1 the player can't have the team they selected (conflict)
+.int team_selected;
 .bool just_joined;
 /// > 0 is a team index, -1 means team selection is deferred until Join()
 .int wants_join;
@@ -404,8 +405,7 @@ int GetPlayerLimit();
 const int MIN_SPEC_TIME = 1;
 void Join(entity this, bool queued_join);
 int nJoinAllowed(entity this, entity ignore);
-bool queuePlayer(entity this, int team_index);
-bool joinAllowed(entity this);
+bool joinAllowed(entity this, int team_index);
 
 void PlayerFrame (entity this);
 
index 21df2319ce91290818352e34590bd1c449ddddd6..d692d8723eec12feadcc5fbc9c8c14bb9b51bf28 100644 (file)
@@ -37,12 +37,13 @@ void ClientKill_Now_TeamChange(entity this)
 
                if (this.wants_join)
                {
+                       Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_PREVENT_JOIN);
                        Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_QUEUE, this.netname);
                        SetPlayerTeam(this, -1, TEAM_CHANGE_SPECTATOR);
                        // Can't do this in PutObserverInServer() or SetPlayerTeam() cos it causes
                        // mouse2 (change spectate mode) to kick the player off the join queue.
                        this.wants_join = 0;
-                       this.team_selected = false;
+                       this.team_selected = 0;
                }
                else
                {
index 850efa1f3009fe90140bc277ba05717bac193b6d..41e75d5a4de54dd1f91a40e130bce66bc562e42b 100644 (file)
@@ -359,8 +359,8 @@ void ClientCommand_join(entity caller, int request)
                {
                        if (!game_stopped && IS_CLIENT(caller) && !IS_PLAYER(caller))
                        {
-                               if (joinAllowed(caller))
-                                       Join(caller, true);
+                               if (joinAllowed(caller, caller.wants_join))
+                                       Join(caller, teamplay);
                                else if(time < CS(caller).jointime + MIN_SPEC_TIME)
                                        CS(caller).autojoin_checked = -1;
                        }
@@ -524,7 +524,6 @@ void ClientCommand_say_team(entity caller, int request, int argc, string command
        }
 }
 
-.bool team_selected;
 void ClientCommand_selectteam(entity caller, int request, int argc)
 {
        switch (request)
@@ -581,11 +580,9 @@ void ClientCommand_selectteam(entity caller, int request, int argc)
                                }
                                TeamBalance_Destroy(balance);
                        }
-                       if (team_num)
-                               ClientKill_TeamChange(caller, team_num);
-                       else // auto
-                               if (!queuePlayer(caller, 0)) // the queue uses deferred autoselect
-                                       ClientKill_TeamChange(caller, -1);
+
+                       if (joinAllowed(caller, team_num ? Team_TeamToIndex(team_num) : -1))
+                               ClientKill_TeamChange(caller, team_num ? team_num : -1);
 
                        return;
                }
index f2c35665cc0411b42594f3a3bdd23974f78afffd..4558eb723d08b67f1a154043708abd8c1b9c2b69 100644 (file)
@@ -274,11 +274,8 @@ bool SetPlayerTeam(entity player, int team_index, int type)
 
                if (team_index != -1)
                {
-                       if (!queuePlayer(player, team_index))
-                       {
-                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(player.team, INFO_JOIN_PLAY_TEAM), player.netname);
-                               player.team_selected = true; // no autoselect in Join()
-                       }
+                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(player.team, INFO_JOIN_PLAY_TEAM), player.netname);
+                       player.team_selected = team_index; // no autoselect in Join()
 
                        if (warmup_stage)
                                ReadyCount(); // teams might be balanced now
@@ -351,18 +348,6 @@ void Player_SetTeamIndexChecked(entity player, int team_index)
        }
        TeamBalance_Destroy(balance);
 
-       // g_balance_teams_queue: before joining the queue,
-       // check if a queued player already chose the selected team
-       if (!IS_BOT_CLIENT(player) && QueueNeeded(player))
-       {
-               FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != player && it.wants_join == team_index,
-               {
-                       Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_NUM(Team_IndexToTeam(team_index), CENTER_JOIN_PREVENT_QUEUE_TEAM_FAIL), it.netname);
-                       player.team_selected = false;
-                       return;
-               });
-       }
-
        SetPlayerTeam(player, team_index, TEAM_CHANGE_MANUAL);
 }
 
@@ -941,7 +926,7 @@ void TeamBalance_GetTeamCounts(entity balance, entity ignore)
                        }
                        if (it.wants_join)
                        {
-                               continue; // Queued players aren't actually in the game.
+                               continue; // Queued players aren't actually in the game (and shouldn't have .team set).
                        }
                        int team_num;
                        // TODO: Reconsider when the player is truly on the team.