]> 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

1  2 
qcsrc/server/command/sv_cmd.qc
qcsrc/server/command/vote.qc
qcsrc/server/world.qc
xonotic-server.cfg

index 0f97fbe6b5e368b2a1d3952b30d3df776688def6,16008bd5932650733a7e88c699f1edc85565bb3c..daa59b5cf7bed78d48d035326644f82061460b0a
@@@ -23,7 -23,6 +23,7 @@@
  #include <server/ipban.qh>
  #include <server/mutators/_mod.qh>
  #include <server/player.qh>
 +#include <server/scores_rules.qh>
  #include <server/teamplay.qh>
  #include <server/world.qh>
  
@@@ -162,7 -161,13 +162,13 @@@ void GameCommand_allready(int request
        {
                case CMD_REQUEST_COMMAND:
                {
-                       ReadyRestart();
+                       if(warmup_stage)
+                       {
+                               ReadyRestart(true);
+                       }
+                       else
+                               LOG_INFO("Not in warmup.");
                        return;
                }
  
@@@ -999,7 -1004,6 +1005,7 @@@ void GameCommand_moveplayer(int request
                                        // Check to see if the player is a valid target
                                        client = GetFilteredEntity(t);
                                        accepted = VerifyClientEntity(client, false, false);
 +                                      string client_num_str = ftos(etof(client));
  
                                        if (accepted <= 0)
                                        {
                                                }
                                                else
                                                {
 -                                                      LOG_INFO("Player ", ftos(GetFilteredNumber(t)), " (", pl_name, ") is already spectating.");
 +                                                      LOG_INFO("Player #", client_num_str, " (", pl_name, ") is already spectating.");
                                                }
                                        }
                                        else
                                                if (IS_SPEC(client) || IS_OBSERVER(client))
                                                {
                                                        // well technically we could, but should we allow that? :P
 -                                                      LOG_INFO("Player ", ftos(GetFilteredNumber(t)), " (", pl_name, ") is not in the game.");
 +                                                      LOG_INFO("Player #", client_num_str, " (", pl_name, ") is not in the game.");
                                                        continue;
                                                }
  
                                                if (team_num == client.team)  // already on the destination team
                                                {
                                                        // keep the forcing undone
 -                                                      LOG_INFO("Player ", ftos(GetFilteredNumber(t)), " (", pl_name, ") is already on the ", Team_ColoredFullName(team_num), ".");
 +                                                      LOG_INFO("Player #", client_num_str, " (", pl_name, ") is already on the ", Team_ColoredFullName(team_num), "^7.");
                                                        continue;
                                                }
                                                else if (team_num == 0)  // auto team
                                                int team_id = Team_TeamToIndex(team_num);
                                                if (team_id == -1)
                                                {
 -                                                      LOG_INFO("Sorry, can't move player here if team ", destination, " doesn't exist.");
 +                                                      LOG_INFO("Can't move player to ", destination, " team because it doesn't exist.");
                                                        TeamBalance_Destroy(balance);
                                                        return;
                                                }
 -                                              if (!TeamBalance_IsTeamAllowed(balance, team_id))
 +                                              if (!IsTeamAvailable(team_num))
                                                {
 -                                                      LOG_INFOF("Sorry, can't move player to %s team if it doesn't exist.", destination);
 +                                                      LOG_INFO("Can't move player to ", destination, " team because it isn't available.");
                                                        TeamBalance_Destroy(balance);
                                                        return;
                                                }
 +                                              if (!TeamBalance_IsTeamAllowed(balance, team_id))
 +                                              {
 +                                                      LOG_INFO("Player #", client_num_str, " (", pl_name, ") is not allowed to join the ", Team_ColoredFullName(team_num), "^7.");
 +                                                      TeamBalance_Destroy(balance);
 +                                                      continue;
 +                                              }
                                                TeamBalance_Destroy(balance);
  
                                                // If so, lets continue and finally move the player
                                                if (MoveToTeam(client, team_id, 6))
                                                {
                                                        successful = strcat(successful, (successful ? ", " : ""), pl_name);
 -                                                      LOG_INFO("Player ", ftos(GetFilteredNumber(t)), " (", pl_name, ") has been moved to the ", Team_ColoredFullName(team_num), "^7.");
 +                                                      LOG_INFO("Player #", client_num_str, " (", pl_name, ") has been moved to the ", Team_ColoredFullName(team_num), "^7.");
                                                }
                                                else
                                                {
 -                                                      LOG_INFO("Unable to move player ", ftos(GetFilteredNumber(t)), " (", pl_name, ")");
 +                                                      LOG_INFO("Unable to move player #", client_num_str, " (", pl_name, ")");
                                                }
                                        }
                                } // loop end
@@@ -1308,6 -1306,26 +1314,26 @@@ void GameCommand_shuffleteams(int reque
        }
  }
  
+ 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 -1647,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 -1670,7 +1678,7 @@@ SERVER_COMMAND(nospectators, "Automatic
  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,b3aa8ca22e9333e3cbbce4256665b69f90c4e12c..aefbc3fd1c502896cd6cfc41c4dc67053bdbe080
@@@ -23,7 -23,6 +23,7 @@@
  #include <server/round_handler.qh>
  #include <server/scores.qh>
  #include <server/teamplay.qh>
 +#include <server/weapons/accuracy.qh>
  #include <server/world.qh>
  
  // =============================================
@@@ -342,8 -341,6 +342,8 @@@ void reset_map(bool dorespawn
        {
                if (game_stopped)
                        return;
 +
 +              PlayerStats_GameReport_Reset_All();
                if (round_handler_IsActive())
                        round_handler_Reset(game_starttime);
        }
                shuffleteams_on_reset_map = false;
        }
  
 -      FOREACH_CLIENT(IS_PLAYER(it),
 -      {
 +      FOREACH_CLIENT(true, {
 +              if (time <= game_starttime)
 +                      accuracy_reset(it); // for spectators too because weapon accuracy is persistent
 +              if (!IS_PLAYER(it))
 +                      continue;
                if (STAT(FROZEN, it))
                        Unfreeze(it, false);
                player_powerups_remove_all(it);
        {
                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);
+                       });
                }
        }
  }
  // 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 -418,7 +424,7 @@@ void ReadyRestart_force(
        if (time <= game_starttime && game_stopped)
                return;
  
-       bprint("^1Server is restarting...\n");
+       bprint("^1Match is restarting...\n");
  
        VoteReset();
  
        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), {
                it.alivetime = 0;
                CS(it).killcount = 0;
 -              float val = PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, 0);
 -              PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, -val);
        });
  
-       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; });
  
        // 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);
                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");
        // 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 -506,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 -756,23 +760,23 @@@ int VoteCommand_parse(entity caller, st
                }
  
                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;
                }
  
diff --combined qcsrc/server/world.qc
index 728c9d757e6ffa77ad2f1edfc0637492dc2c8c0f,00733e809142fe8e623940a25b160e3fb833f928..2af6c47dcdfe60e34924050326e6f29da92e7664
@@@ -461,7 -461,6 +461,7 @@@ void cvar_changes_init(
                BADCVAR("g_physics_clientselect");
                BADCVAR("g_pinata");
                BADCVAR("g_powerups");
 +              BADCVAR("g_powerups_drop_ondeath");
                BADCVAR("g_player_brightness");
                BADCVAR("g_rocket_flying");
                BADCVAR("g_rocket_flying_disabledelays");
                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 -1643,7 +1644,7 @@@ void CheckRules_World(
                                if(readyplayers || playerswithlaps >= 2)
                                {
                                        checkrules_suddendeathend = 0;
-                                       ReadyRestart(); // go to race
+                                       ReadyRestart(true); // go to race
                                        return;
                                }
                                else
diff --combined xonotic-server.cfg
index 1f7a0c088e22c01b973d94758a54bdbeb1c3829e,f6a4b6256714a6fb5340bccfc7cde814977e2cd2..f8a16c104ae10653556cada00da00a3b5afcda68
@@@ -15,9 -15,7 +15,7 @@@ set minplayers 0 "fill server with bot
  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
  
@@@ -198,7 -196,6 +196,7 @@@ set g_shootfromfixedorigin "" "if set t
  set g_weapon_stay 0 "1: ghost weapons can be picked up but give no ammo, thrown guns have ammo 2: ghost weapons can be picked up and refill ammo to one pickup size, thrown guns have no ammo (to prevent infinite ammo abuse)"
  set g_weapon_throwable 1 "if set to 1, weapons can be dropped"
  set g_powerups -1 "if set to 0 no powerups will spawn, if 1 they will spawn in all game modes, -1 is game mode default"
 +set g_powerups_drop_ondeath 0 "players will drop their powerups on death (1 = timer continues, 2 = timer freezes until picked up)"
  set g_powerups_stack 0 "enables stacking of powerup timers when picking up a powerup you already have; otherwise timer is reset to the time granted by the item, if greater than the time you currently have"
  set g_powerups_strength 1 "allow strength powerups to spawn"
  set g_powerups_shield 1 "allow shield powerups to spawn"
@@@ -232,7 -229,6 +230,7 @@@ set g_maplist_sizes_count_bots 1 "inclu
  
  set g_items_mindist 4000 "starting distance for the fading of items"
  set g_items_maxdist 4500 "maximum distance at which an item can be viewed, after which it will be invisible"
 +set g_items_dropped_lifetime 20 "default lifetime for dropped items unless explicitly overriden (ie. flags)"
  
  set g_grab_range 200 "distance at which dragable objects can be grabbed"