]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'z411/srestart' into 'master'
authorterencehill <piuntn@gmail.com>
Sun, 2 Jan 2022 09:55:44 +0000 (09:55 +0000)
committerterencehill <piuntn@gmail.com>
Sun, 2 Jan 2022 09:55:44 +0000 (09:55 +0000)
Add resetmatch command to soft restart the map without the need to respawn the server [restart] (fixes #2609)

Closes #2609

See merge request xonotic/xonotic-data.pk3dir!940

commands.cfg
qcsrc/client/hud/panel/timer.qc
qcsrc/server/command/cmd.qc
qcsrc/server/command/cmd.qh
qcsrc/server/command/sv_cmd.qc
qcsrc/server/command/vote.qc
qcsrc/server/command/vote.qh
qcsrc/server/main.qc
qcsrc/server/world.qc
qcsrc/server/world.qh
xonotic-server.cfg

index 60ffbced2ba90c275e2ea8b0ce0a1a673132d101..a6162badf4037b12ecdc6a15bd90b55b8e3e44d2 100644 (file)
@@ -229,6 +229,7 @@ alias nospectators         "qc_cmd_sv     nospectators         ${* ?}" // Automa
 alias printstats           "qc_cmd_sv     printstats           ${* ?}" // Dump eventlog player stats and other score information
 alias radarmap             "qc_cmd_sv     radarmap             ${* ?}" // Generate a radar image of the map
 alias reducematchtime      "qc_cmd_sv     reducematchtime      ${* ?}" // Decrease the timelimit value incrementally
+alias resetmatch           "qc_cmd_sv     resetmatch           ${* ?}" // Soft restart the map
 alias setbots              "qc_cmd_sv     setbots              ${* ?}" // Adjust how many bots are in the match
 alias shuffleteams         "qc_cmd_sv     shuffleteams         ${* ?}" // Randomly move players to different teams
 alias stuffto              "qc_cmd_sv     stuffto              ${* ?}" // Send a command to be executed on a client
@@ -307,6 +308,7 @@ set sv_vote_command_restriction_endmatch "0"
 set sv_vote_command_restriction_reducematchtime "0"
 set sv_vote_command_restriction_extendmatchtime "0"
 set sv_vote_command_restriction_allready "0"
+set sv_vote_command_restriction_resetmatch "0"
 set sv_vote_command_restriction_kick "1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" // enough space for ban reason
 set sv_vote_command_restriction_kickban "1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" // enough space for ban reason
 set sv_vote_command_restriction_cointoss "0"
@@ -327,7 +329,7 @@ set sv_vote_command_help_gotomap "\nUsage:^3 vcall gotomap mapname\n^7  Where 'm
 // =================================
 set sv_vote_call 1 "Allow users to call a vote for the commands in sv_vote_commands"
 set sv_vote_change 1 "Allow voters to change their mind after already voting"
-set sv_vote_commands "restart fraglimit gotomap nextmap endmatch reducematchtime extendmatchtime allready kick cointoss movetoauto shuffleteams bots nobots" "these commands can be voted by players or used directly by masters (vdo) in addition to sv_vote_master_commands"
+set sv_vote_commands "restart fraglimit gotomap nextmap endmatch reducematchtime extendmatchtime allready resetmatch kick cointoss movetoauto shuffleteams bots nobots" "these commands can be voted by players or used directly by masters (vdo) in addition to sv_vote_master_commands"
 set sv_vote_only_commands ""
 set sv_vote_limit 160 "Maximum allowed length of a vote command"
 set sv_vote_master_commands "movetored movetoblue movetoyellow movetopink movetospec" "Extra commands which vote masters can execute by themselves, along with the normal sv_vote_commands." // maybe add kickban here (but then sv_vote_master 0)
@@ -377,4 +379,4 @@ alias vdoend "vdo endmatch"
 //  rcon server commands
 // ======================
 rcon_secure 1
-set rcon_restricted_commands "restart fraglimit chmap gotomap nextmap endmatch reducematchtime extendmatchtime allready kick cointoss movetoauto shuffleteams bots nobots movetored movetoblue movetoyellow movetopink movetospec kickban \"sv_cmd bans\" \"sv_cmd unban *\" status \"sv_cmd teamstatus\""
+set rcon_restricted_commands "restart fraglimit chmap gotomap nextmap endmatch reducematchtime extendmatchtime allready resetmatch kick cointoss movetoauto shuffleteams bots nobots movetored movetoblue movetoyellow movetopink movetospec kickban \"sv_cmd bans\" \"sv_cmd unban *\" status \"sv_cmd teamstatus\""
index 0cdfefa7899af591aaf4f3ef3c567152d02e41ac..f1c4dcfe807b89e456ba230abc91e59bc7b276bf 100644 (file)
@@ -51,7 +51,7 @@ void HUD_Timer()
        {
                float warmup_timelimit = STAT(WARMUP_TIMELIMIT);
                if(warmup_timelimit > 0)
-                       warmup_timeleft = max(0, warmup_timelimit - time);
+                       warmup_timeleft = max(0, warmup_timelimit - time + STAT(GAMESTARTTIME));
                else if(warmup_timelimit == 0)
                        warmup_timeleft = timeleft;
                warmup_timeleft = ceil(warmup_timeleft);
index a8a8747e1822558f0b3c572b58a5d105b7d4c773..7241b7415dbb4487035fdca152c73ac2c99bfca8 100644 (file)
@@ -374,32 +374,25 @@ void ClientCommand_ready(entity caller, int request)  // todo: anti-spam for tog
                {
                        if (IS_CLIENT(caller))
                        {
-                               if (warmup_stage || autocvar_sv_ready_restart || g_race_qualifying == 2)
+                               if (warmup_stage || g_race_qualifying == 2)
                                {
-                                       if (!readyrestart_happened || autocvar_sv_ready_restart_repeatable)
+                                       if (time < game_starttime) // game is already restarting
+                                               return;
+                                       if (caller.ready)            // toggle
                                        {
-                                               if (time < game_starttime) // game is already restarting
-                                                       return;
-                                               if (caller.ready)            // toggle
-                                               {
-                                                       caller.ready = false;
-                                                       if(IS_PLAYER(caller) || caller.caplayer == 1)
-                                                               bprint(playername(caller.netname, caller.team, false), "^2 is ^1NOT^2 ready\n");
-                                               }
-                                               else
-                                               {
-                                                       caller.ready = true;
-                                                       if(IS_PLAYER(caller) || caller.caplayer == 1)
-                                                               bprint(playername(caller.netname, caller.team, false), "^2 is ready\n");
-                                               }
-
-                                               // cannot reset the game while a timeout is active!
-                                               if (!timeout_status) ReadyCount();
+                                               caller.ready = false;
+                                               if(IS_PLAYER(caller) || caller.caplayer == 1)
+                                                       bprint(playername(caller.netname, caller.team, false), "^2 is ^1NOT^2 ready\n");
                                        }
                                        else
                                        {
-                                               sprint(caller, "^1Game has already been restarted\n");
+                                               caller.ready = true;
+                                               if(IS_PLAYER(caller) || caller.caplayer == 1)
+                                                       bprint(playername(caller.netname, caller.team, false), "^2 is ready\n");
                                        }
+
+                                       // cannot reset the game while a timeout is active!
+                                       if (!timeout_status) ReadyCount();
                                }
                        }
                        return;  // never fall through to usage
index f2f72157140916a6e483117cee46eabbab424fc7..802afc8bde6f86e1ed3e145df61f111ea51ec715 100644 (file)
@@ -2,8 +2,6 @@
 
 float autocvar_sv_clientcommand_antispam_time;
 int autocvar_sv_clientcommand_antispam_count;
-bool autocvar_sv_ready_restart;
-bool autocvar_sv_ready_restart_repeatable;
 
 .float cmd_floodtime;
 .float cmd_floodcount;
index 0f97fbe6b5e368b2a1d3952b30d3df776688def6..daa59b5cf7bed78d48d035326644f82061460b0a 100644 (file)
@@ -162,7 +162,13 @@ void GameCommand_allready(int request)
        {
                case CMD_REQUEST_COMMAND:
                {
-                       ReadyRestart();
+                       if(warmup_stage)
+                       {
+                               ReadyRestart(true);
+                       }
+                       else
+                               LOG_INFO("Not in warmup.");
+
                        return;
                }
 
@@ -1308,6 +1314,26 @@ void GameCommand_shuffleteams(int request)
        }
 }
 
+void GameCommand_resetmatch(int request)
+{
+       switch (request)
+       {
+               case CMD_REQUEST_COMMAND:
+               {
+                       ReadyRestart(false);
+                       return;
+               }
+
+               default:
+               case CMD_REQUEST_USAGE:
+               {
+                       LOG_HELP("Usage:^3 sv_cmd resetmatch");
+                       LOG_HELP("  No arguments required.");
+                       return;
+               }
+       }
+}
+
 void GameCommand_stuffto(int request, int argc)
 {
        // This... is a fairly dangerous and powerful command... - It allows any arguments to be sent to a client via rcon.
@@ -1629,7 +1655,7 @@ void GameCommand_(int request)
 
 // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
 SERVER_COMMAND(adminmsg, "Send an admin message to a client directly") { GameCommand_adminmsg(request, arguments); }
-SERVER_COMMAND(allready, "Restart the server and reset the players") { GameCommand_allready(request); }
+SERVER_COMMAND(allready, "Ends warmup and starts the match") { GameCommand_allready(request); }
 SERVER_COMMAND(allspec, "Force all players to spectate") { GameCommand_allspec(request, arguments); }
 SERVER_COMMAND(anticheat, "Create an anticheat report for a client") { GameCommand_anticheat(request, arguments); }
 SERVER_COMMAND(animbench, "Benchmark model animation (LAGS)") { GameCommand_animbench(request, arguments); }
@@ -1652,6 +1678,7 @@ SERVER_COMMAND(nospectators, "Automatically remove spectators from a match") { G
 SERVER_COMMAND(printstats, "Dump eventlog player stats and other score information") { GameCommand_printstats(request); }
 SERVER_COMMAND(radarmap, "Generate a radar image of the map") { GameCommand_radarmap(request, arguments); }
 SERVER_COMMAND(reducematchtime, "Decrease the timelimit value incrementally") { GameCommand_reducematchtime(request); }
+SERVER_COMMAND(resetmatch, "Soft restart the game without changing teams; goes back to warmup if enabled") { GameCommand_resetmatch(request); }
 SERVER_COMMAND(setbots, "Adjust how many bots are in the match") { GameCommand_setbots(request, arguments); }
 SERVER_COMMAND(shuffleteams, "Randomly move players to different teams") { GameCommand_shuffleteams(request); }
 SERVER_COMMAND(stuffto, "Send a command to be executed on a client") { GameCommand_stuffto(request, arguments); }
index f99c182466a535ba0273044045344df0ba748b7b..aefbc3fd1c502896cd6cfc41c4dc67053bdbe080 100644 (file)
@@ -396,25 +396,16 @@ void reset_map(bool dorespawn)
        {
                if (!MUTATOR_CALLHOOK(reset_map_players))
                {
-                       if (restart_mapalreadyrestarted || (time < game_starttime))
+                       FOREACH_CLIENT(IS_PLAYER(it),
                        {
-                               FOREACH_CLIENT(IS_PLAYER(it),
-                               {
-                                       /*
-                                       only reset players if a restart countdown is active
-                                       this can either be due to cvar sv_ready_restart_after_countdown having set
-                                       restart_mapalreadyrestarted to 1 after the countdown ended or when
-                                       sv_ready_restart_after_countdown is not used and countdown is still running
-                                       */
-                                       // PlayerScore_Clear(it);
-                                       CS(it).killcount = 0;
-                                       // stop the player from moving so that he stands still once he gets respawned
-                                       it.velocity = '0 0 0';
-                                       it.avelocity = '0 0 0';
-                                       CS(it).movement = '0 0 0';
-                                       PutClientInServer(it);
-                               });
-                       }
+                               // PlayerScore_Clear(it);
+                               CS(it).killcount = 0;
+                               // stop the player from moving so that he stands still once he gets respawned
+                               it.velocity = '0 0 0';
+                               it.avelocity = '0 0 0';
+                               CS(it).movement = '0 0 0';
+                               PutClientInServer(it);
+                       });
                }
        }
 }
@@ -422,7 +413,6 @@ void reset_map(bool dorespawn)
 // Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown is set)
 void ReadyRestart_think(entity this)
 {
-       restart_mapalreadyrestarted = true;
        reset_map(true);
        Score_ClearAll();
        delete(this);
@@ -434,7 +424,7 @@ void ReadyRestart_force()
        if (time <= game_starttime && game_stopped)
                return;
 
-       bprint("^1Server is restarting...\n");
+       bprint("^1Match is restarting...\n");
 
        VoteReset();
 
@@ -442,9 +432,11 @@ void ReadyRestart_force()
        if (checkrules_overtimesadded > 0 && g_race_qualifying != 2)
                cvar_set("timelimit", ftos(autocvar_timelimit - (checkrules_overtimesadded * autocvar_timelimit_overtime)));
        checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = 0;
-
-       readyrestart_happened = true;
-       game_starttime = time + RESTART_COUNTDOWN;
+       
+       if(warmup_stage)
+               game_starttime = time; // Warmup: No countdown in warmup
+       else
+               game_starttime = time + RESTART_COUNTDOWN; // Go into match mode
 
        // clear player attributes
        FOREACH_CLIENT(IS_PLAYER(it), {
@@ -452,12 +444,9 @@ void ReadyRestart_force()
                CS(it).killcount = 0;
        });
 
-       restart_mapalreadyrestarted = false; // reset this var, needed when cvar sv_ready_restart_repeatable is in use
-
-       // disable the warmup global for the server
-       if(warmup_stage)
+       // if we're ending the warmup stage call the corresponding hook
+       if(!warmup_stage)
                localcmd("\nsv_hook_warmupend\n");
-       warmup_stage = 0;                // once the game is restarted the game is in match stage
 
        // reset the .ready status of all players (also spectators)
        FOREACH_CLIENT(IS_REAL_CLIENT(it), { it.ready = false; });
@@ -466,13 +455,10 @@ void ReadyRestart_force()
 
        // lock teams with lockonrestart
        if (autocvar_teamplay_lockonrestart && teamplay)
-       {
-               lockteams = true;
-               bprint("^1The teams are now locked.\n");
-       }
+               lockteams = !warmup_stage;
 
        // initiate the restart-countdown-announcer entity
-       if (sv_ready_restart_after_countdown)
+       if (sv_ready_restart_after_countdown && !warmup_stage)
        {
                entity restart_timer = new_pure(restart_timer);
                setthink(restart_timer, ReadyRestart_think);
@@ -485,11 +471,11 @@ void ReadyRestart_force()
                FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { CS(it).allowed_timeouts = autocvar_sv_timeout_number; });
        }
 
-       if (!sv_ready_restart_after_countdown) reset_map(true);
+       if (!sv_ready_restart_after_countdown || warmup_stage) reset_map(true);
        if (autocvar_sv_eventlog) GameLogEcho(":restart");
 }
 
-void ReadyRestart()
+void ReadyRestart(bool forceWarmupEnd)
 {
        if (MUTATOR_CALLHOOK(ReadyRestart_Deny) || intermission_running || race_completing) localcmd("restart\n");
        else localcmd("\nsv_hook_readyrestart\n");
@@ -497,6 +483,12 @@ void ReadyRestart()
        // Reset ALL scores, but only do that at the beginning of the countdown if sv_ready_restart_after_countdown is off!
        // Otherwise scores could be manipulated during the countdown.
        if (!sv_ready_restart_after_countdown) Score_ClearAll();
+
+       if(forceWarmupEnd)
+               warmup_stage = 0; // forcefully end warmup and go to match stage
+       else
+               warmup_stage = cvar("g_warmup"); // go into warmup if it's enabled, otherwise restart into match stage
+
        ReadyRestart_force();
 }
 
@@ -518,7 +510,7 @@ void ReadyCount()
        ready_needed_factor = bound(0.5, cvar("g_warmup_majority_factor"), 0.999);
        ready_needed_count = floor(t_players * ready_needed_factor) + 1;
 
-       if (readycount >= ready_needed_count) ReadyRestart();
+       if (readycount >= ready_needed_count) ReadyRestart(true);
 }
 
 
@@ -768,12 +760,23 @@ int VoteCommand_parse(entity caller, string vote_command, string vote_list, floa
                }
 
                case "restart":
+               case "resetmatch":  // re-direct all match restarting to resetmatch
                {
-                       // add a delay so that vote result can be seen and announcer can be heard
-                       // if the vote is accepted
-                       vote_parsed_command = strcat("defer 1 ", vote_command);
-                       vote_parsed_display = strzone(strcat("^1", vote_command));
+                       vote_parsed_command = "resetmatch";
+                       vote_parsed_display = strzone("^1resetmatch");
+
+                       break;
+               }
 
+               case "allready":
+               {
+                       if(!warmup_stage) {
+                               print_to(caller, "Game already started. Use the resetmatch command to restart the match.");
+                               return -1;
+                       }
+
+                       vote_parsed_command = vote_command;
+                       vote_parsed_display = strzone(strcat("^1", vote_command));
                        break;
                }
 
index 4dfd9e8575f2abf22c9cdc1e639171ca64988e0e..843d59618e9c43a6b77310e6e8ebe9b40fb101b6 100644 (file)
@@ -63,8 +63,6 @@ void VoteCommand(int request, entity caller, int argc, string vote_command);
 const float RESTART_COUNTDOWN = 10;
 entity nagger;
 float readycount;                  // amount of players who are ready
-float readyrestart_happened;       // keeps track of whether a restart has already happened
-float restart_mapalreadyrestarted; // bool, indicates whether reset_map() was already executed
 .float ready;                      // flag for if a player is ready
 .int team_saved;                   // team number to restore upon map reset
 .void(entity this) reset;             // if set, an entity is reset using this
index cf91b9de4bafd7d6b250a860732702a82222f409..2b29422b8969362356b6a552106158c9b881d0e7 100644 (file)
@@ -331,8 +331,8 @@ void StartFrame()
        CreatureFrame_All();
        CheckRules_World();
 
-       if (warmup_stage && !game_stopped && warmup_limit > 0 && time >= warmup_limit) {
-               ReadyRestart();
+       if (warmup_stage && !game_stopped && warmup_limit > 0 && time - game_starttime >= warmup_limit) {
+               ReadyRestart(true);
                return;
        }
 
index 728c9d757e6ffa77ad2f1edfc0637492dc2c8c0f..2af6c47dcdfe60e34924050326e6f29da92e7664 100644 (file)
@@ -494,7 +494,6 @@ void cvar_changes_init()
                BADCVAR("sv_maxrate");
                BADCVAR("sv_motd");
                BADCVAR("sv_public");
-               BADCVAR("sv_ready_restart");
                BADCVAR("sv_showfps");
                BADCVAR("sv_status_privacy");
                BADCVAR("sv_taunt");
@@ -1645,7 +1644,7 @@ void CheckRules_World()
                                if(readyplayers || playerswithlaps >= 2)
                                {
                                        checkrules_suddendeathend = 0;
-                                       ReadyRestart(); // go to race
+                                       ReadyRestart(true); // go to race
                                        return;
                                }
                                else
index bdaac8c271e5ae08f79af6ba5f32e02fa8fb717c..e87edd6af58a0126a86da9908f8b893f4935ff16 100644 (file)
@@ -134,7 +134,7 @@ const int WINNING_STARTSUDDENDEATHOVERTIME = 3; // no winner, enter suddendeath
 
 float WinningCondition_Scores(float limit, float leadlimit);
 void SetWinners(.float field, float value);
-void ReadyRestart();
+void ReadyRestart(bool endWarmup);
 
 void DumpStats(float final);
 
index 1f7a0c088e22c01b973d94758a54bdbeb1c3829e..f8a16c104ae10653556cada00da00a3b5afcda68 100644 (file)
@@ -15,9 +15,7 @@ set minplayers 0 "fill server with bots to reach this number of players in teaml
 set minplayers_per_team 0 "fill server with bots to reach this number of players per team (if bot_number is not enough)"
 
 // restart server if all players hit "ready"-button
-set sv_ready_restart 0 "allow a map to be restarted once all players pressed the \"ready\" button"
 set sv_ready_restart_after_countdown 0 "reset players and map items after the countdown ended, instead of at the beginning of the countdown"
-set sv_ready_restart_repeatable 0 "allows the players to restart the game as often as needed"
 
 alias sv_hook_readyrestart