From 99dec2f0f59857cf93b64f68f7eefd4862bb9119 Mon Sep 17 00:00:00 2001 From: z411 Date: Sun, 28 May 2023 05:28:24 -0400 Subject: [PATCH] Added autospec feature --- qcsrc/common/notifications/all.inc | 2 + qcsrc/server/client.qc | 9 +++-- qcsrc/server/teamplay.qc | 60 ++++++++++++++++++++++++++---- qcsrc/server/teamplay.qh | 3 ++ xonotic-server.cfg | 2 + 5 files changed, 65 insertions(+), 11 deletions(-) diff --git a/qcsrc/common/notifications/all.inc b/qcsrc/common/notifications/all.inc index cbcf5aa05..eb9884485 100644 --- a/qcsrc/common/notifications/all.inc +++ b/qcsrc/common/notifications/all.inc @@ -422,9 +422,11 @@ string multiteam_info_sprintf(string input, string teamname) { return ((input != MSG_INFO_NOTIF(QUIT_DISCONNECT, N_CHATCON, 1, 0, "s1", "", "", _("^BG%s^F3 disconnected"), "") MSG_INFO_NOTIF(QUIT_KICK_IDLING, N_CHATCON, 1, 1, "s1 f1", "", "", _("^BG%s^F3 was kicked after idling for %s seconds"), "") MSG_INFO_NOTIF(MOVETOSPEC_IDLING, N_CHATCON, 1, 1, "s1 f1", "", "", _("^BG%s^F3 was moved to^BG spectators^F3 after idling for %s seconds"), "") + MSG_INFO_NOTIF(MOVETOSPEC_REMOVE, N_CHATCON, 1, 0, "s1", "", "", _("^BG%s^F3 was moved to^BG spectators^F3 for balance reasons"), "") MSG_INFO_NOTIF(QUIT_KICK_SPECTATING, N_CONSOLE, 0, 0, "", "", "", _("^F2You were kicked from the server because you are a spectator and spectators aren't allowed at the moment."), "") MSG_INFO_NOTIF(QUIT_KICK_TEAMKILL, N_CHATCON, 1, 0, "s1", "", "", _("^BG%s^F3 was kicked for excessive teamkilling"), "") MSG_INFO_NOTIF(QUIT_SPECTATE, N_CHATCON, 1, 0, "s1", "", "", _("^BG%s^F3 is now^BG spectating"), "") + MSG_INFO_NOTIF(QUIT_QUEUE, N_CHATCON, 1, 0, "s1", "", "", _("^BG%s^F3 has left the queue"), "") MSG_INFO_NOTIF(RACE_ABANDONED, N_CONSOLE, 1, 0, "s1", "", "", _("^BG%s^BG has abandoned the race"), "") MSG_INFO_NOTIF(RACE_FAIL_RANKED, N_CONSOLE, 1, 3, "s1 race_col f1ord race_col f3race_time race_diff", "s1 f3race_time", "race_newfail", _("^BG%s^BG couldn't break their %s%s^BG place record of %s%s %s"), "") diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc index da1daaf0a..5e4c5eaa3 100644 --- a/qcsrc/server/client.qc +++ b/qcsrc/server/client.qc @@ -1118,7 +1118,7 @@ void ClientConnect(entity this) GameLogEcho(strcat(":join:", ftos(this.playerid), ":", ftos(etof(this)), ":", ((IS_REAL_CLIENT(this)) ? GameLog_ProcessIP(this.netaddress) : "bot"), ":", playername(this.netname, this.team, false))); CS(this).just_joined = true; // stop spamming the eventlog with additional lines when the client connects - this.wants_join = false; + this.wants_join = 0; stuffcmd(this, clientstuff, "\n"); stuffcmd(this, "cl_particles_reloadeffects\n"); // TODO do we still need this? @@ -1265,6 +1265,9 @@ void ClientDisconnect(entity this) if (player_count == 0) localcmd("\nsv_hook_lastleave\n"); + + if (teamplay && autocvar_g_balance_teams_remove) + TeamBalance_RemoveExcessPlayers(NULL); } void ChatBubbleThink(entity this) @@ -2006,9 +2009,7 @@ void Join(entity this) else Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_PLAY, this.netname); this.team_selected = false; - - if(queued_join) - this.wants_join = 0; + this.wants_join = 0; } int GetPlayerLimit() diff --git a/qcsrc/server/teamplay.qc b/qcsrc/server/teamplay.qc index 4afe7d1e4..ba3277ad5 100644 --- a/qcsrc/server/teamplay.qc +++ b/qcsrc/server/teamplay.qc @@ -236,7 +236,7 @@ bool Player_SetTeamIndex(entity player, int index) bool IsQueueNeeded(entity ignore) { - return (teamplay && TeamBalance_AreEqual(ignore)); + return (teamplay && autocvar_g_balance_teams_queue && TeamBalance_AreEqual(ignore)); } entity SpectatorWantsJoin(entity this) @@ -276,7 +276,7 @@ bool SetPlayerTeam(entity player, int team_index, int type) if (IsQueueNeeded(player) && !SpectatorWantsJoin(player)) { Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(player.team, INFO_JOIN_WANTS_TEAM), player.netname); - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_JOIN_PREVENT_QUEUE); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_JOIN_PREVENT_QUEUE); player.wants_join = team_index; // Player queued to join } else @@ -292,14 +292,17 @@ bool SetPlayerTeam(entity player, int team_index, int type) Kill_Notification(NOTIF_ONE_ONLY, player, MSG_CENTER, CPID_IDLING); CS(player).idlekick_lasttimeleft = 0; } - else if (player.wants_join) + else if (player.wants_join > 0) { + LOG_INFOF("Triggered by %s (%d)", player.netname, player.wants_join); player.wants_join = 0; - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_SPECTATE, player.netname); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_QUEUE, player.netname); } else if (!CS(player).just_joined && player.frags != FRAGS_SPECTATOR) { Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_SPECTATE, player.netname); + if(autocvar_g_balance_teams_remove) + TeamBalance_RemoveExcessPlayers(player); } } @@ -700,10 +703,10 @@ bool TeamBalance_AreEqual(entity ignore) int total; int prev_total = 0; - for(int i = 0; i < AVAILABLE_TEAMS; i++) + for(int i = 1; i <= AVAILABLE_TEAMS; i++) { - total = TeamBalance_GetTeamFromIndex(balance, i+1).m_num_players; - if(i > 0 && total != prev_total) + total = TeamBalance_GetTeamFromIndex(balance, i).m_num_players; + if(i > 1 && total != prev_total) { TeamBalance_Destroy(balance); return false; @@ -714,6 +717,49 @@ bool TeamBalance_AreEqual(entity ignore) return true; } +void TeamBalance_RemoveExcessPlayers(entity ignore) +{ + entity balance = TeamBalance_CheckAllowedTeams(ignore); + TeamBalance_GetTeamCounts(balance, ignore); + + int min = 0; + + for(int i = 1; i <= AVAILABLE_TEAMS; i++) + { + int cur = TeamBalance_GetTeamFromIndex(balance, i).m_num_players; + if(cur < min) + min = cur; + } + + for(int tmi = 1; tmi <= AVAILABLE_TEAMS; tmi++) + { + int cur = TeamBalance_GetTeamFromIndex(balance, tmi).m_num_players; + if(cur > 0 && cur > min) + { + // Get newest player + int latest_join = 0; + entity latest_join_pl = NULL; + + FOREACH_CLIENT(IS_CLIENT(it) || INGAME(it), { + if(it.team == Team_IndexToTeam(tmi) && CS(it).startplaytime > latest_join) + { + latest_join = CS(it).startplaytime; + latest_join_pl = it; + } + }); + + // Force player to spectate + if(latest_join_pl) + { + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_MOVETOSPEC_REMOVE, latest_join_pl.netname); + PutObserverInServer(latest_join_pl, true, true); + } + } + } + + TeamBalance_Destroy(balance); +} + bool TeamBalance_IsTeamAllowed(entity balance, int index) { if (balance == NULL) diff --git a/qcsrc/server/teamplay.qh b/qcsrc/server/teamplay.qh index f50808ff4..08423dc08 100644 --- a/qcsrc/server/teamplay.qh +++ b/qcsrc/server/teamplay.qh @@ -7,6 +7,8 @@ bool autocvar_teamplay_lockonrestart; bool autocvar_g_balance_teams; bool autocvar_g_balance_teams_prevent_imbalance; +bool autocvar_g_balance_teams_queue; +bool autocvar_g_balance_teams_remove; string autocvar_g_forced_team_otherwise; @@ -187,6 +189,7 @@ void TeamBalance_Destroy(entity balance); int TeamBalance_GetAllowedTeams(entity balance); bool TeamBalance_AreEqual(entity ignore); +void TeamBalance_RemoveExcessPlayers(entity ignore); /// \brief Returns whether the team change to the specified team is allowed. /// \param[in] balance Team balance entity. diff --git a/xonotic-server.cfg b/xonotic-server.cfg index 03404ebf4..0cc7674dd 100644 --- a/xonotic-server.cfg +++ b/xonotic-server.cfg @@ -277,6 +277,8 @@ set g_teamdamage_resetspeed 20 "for teamplay_mode 4: how fast player's team set g_balance_teams 1 "automatically balance out players entering instead of asking them for their preferred team" set g_balance_teams_prevent_imbalance 1 "prevent players from changing to larger teams" +set g_balance_teams_queue 1 "queue players before joining" +set g_balance_teams_remove 1 "remove excess players from teams" set g_changeteam_banned 0 "not allowed to change team" set sv_teamnagger 1 "enable a nag message when the teams are unbalanced" -- 2.39.2