Rotation files are means to replace old quake cvar-based rotation system. To enable it, just set xp_rotation
to the file your rotation script is.
If the xp_rotation
cvar is empty, or the file does not exist or it's empty (there are no maps on it, even if the file is not really empty), it falls back to the default quake behavior and executes whatever it is on the nextmap
cvar.
In its simplest form, you can just write a list of maps that your server will keep rotating once a map finishes. For example:
// very simple rotation file q3dm1 q3dm2 q3dm3 q3dm4 q3dm5
The first line of the script is a comment and is ignored by the parser. Everything after double slashes //
and a hash mark #
until the end of the line is ignored. Additionally, an slash followed by an asterisk /*
indicates the start of a comment, which can be spread across multiple lines until an asterisk followed by an slash */
is found.
From the second to last line on the example we have our rotation, which is simply a list of maps. Maps are separated with a newline character, or with a semicolon ;
.
Additionally, you can modify the values of server cvars inside your rotation files by adding a dollar-sign $
before the cvar name and after it the equal sign =
, followed by its value, optionally enclosed by double quotes "
. Also, you can execute server commands, by prefixing it with a slash /
or inverted slash \
, and is read until the end of the line. This is illustrated in the following example:
// global settings, will be executed always before each map $g_gametype = 3; // team deathmatch $timelimit = 20; $fraglimit = 30; $xp_config = "excessive5.cfg"; // strings can also be assigned // kick any bot added in the previous map /k allbots // actual rotation q3dm1 q3dm2 q3dm3
To separate multiple cvars you can alternatively use semicolons ;
, which allows you to assign several cvars in the same line.
All these cvars and commands will be executed before each map, which is good to ensure that your server is running the right settings all the time. However, you can also set settings and run commands specifically for one map in the rotation, if you enclose these between brackets { }
immediately after a map, like the following example shows:
// this will be executed before each map $timelimit = 20; $fraglimit = 35; $g_gametype = 0; q3dm1 { $fraglimit = 50; } q3dm2 q3dm3 q3ctf1 { $g_gametype = 4; $timelimit = 15; $capturelimit = 10; /load excessive3 } // this will be ffa! remember the "g_gametype = 0" q3ctf4
A very common usage of map blocks is to set the g_motd cvar to the information of the next map in the rotation, and the message will be printed to clients in their loading screens:
// this will be executed before each map $timelimit = 20; $fraglimit = 35; $g_gametype = 0; q3dm1 { $g_motd = "^5Next map: ^2q3dm2" } q3dm2 { $g_motd = "^5Next map: ^2q3dm3" } q3dm3 { $g_motd = "^5Next map: ^2q3dm1" }
By default, a map will be rotated only at the end of the map. However, you can additionally use the command /rcon rotate [step]
to manually change the current map. The command accepts an optional parameter, which can be:
xp_rotateStep
will be used as the first parameter. Since a rotation is by definition cyclical, there is no problem in using any number as this parameter, even arbitrarily big ones, because for example in a 10 map rotation, the first map is also the 11th, 21th, and so on.If you want to let your users the possibility of chose which maps to play on, you can add the rotate
string to the xp_vote
cvar. However, this will let the user the liberty to specify a parameter for the command. So, you can alternatively just add the nextmap
string, which will just execute a /rotate
command without any parameter.
As you could read in the latest section, the xp_rotateStep
cvar controls how many maps to "jump" with a /rotate
command without any parameter, which is what is used at the end of maps. You can use this cvar to modify the flow of your rotation, for example a value of -1
will mean that your rotation will run backward.
However, a more useful application for this xp_rotateStep
cvar is the possibility for random rotations. To achieve that effect, just add the following line to your Crontab file:
// randomize rotation * * * * * /execstr set xp_rotateStep $(sv_serverid)
This will change the value of the xp_rotateStep
cvar to the value of the sv_serverid
cvar, which is pretty much a random number that is used as the identifier for the server, and it changes every map. This allows for a true random map rotation.
Alternatively, if you want to keep your normal rotation, but let a user to callvote a random map, you can use a little quake scripting, and add the following line to the server cfg:
// cvar to hold the command to callvote a random map /set randomMap "execstr rotate $(sv_serverid)"
The you just have to add the string vstr randomMap
to the xp_vote
cvar, and then the users can do /callvote "vstr randomMap"
.
To make server rotation even more dynamic, basic if-else structures are supported in rotation scripts. This allows you to rotate to specific maps only if a certain condition is met. To illustrate the syntax of if-else constructions, take a look at the example:
if ( $g_gametype != 3 ) { // NOT team deathmatch $timelimit = 15; $fraglimit = 50; } else { // team deathmatch $timelimit = 10; $fraglimit = 100; } // just the same.. if ($g_gametype!=3) { $timelimit = 15; $fraglimit = 50; } else { $timelimit = 10; $fraglimit = 100; } // a bit more complex? if ( $g_gametype == 3 || $g_gametype == 8 ) { // tdm or freeze tag $g_quadFactor = 1; if ( $timelimit >= 20 ) { // 20+ minutes $fraglimit = 50; } else if ( $timelimit >= 12 ) { // 12+ minutes but less than 20 $fraglimit = 35; } else { // less than 12 minutes $fraglimit = 25; $g_quadFactor = 3.5; /say ^1Quad Damage ^7is enabled because of the short timelimit! } }
As you can see, the condition has to be of the form cvar comparison_operator value
, where the operator can be one of the following:
==
!=
<
<=
>
>
The starting and closing brackets { }
after the condition are mandatory.
The main usage of if-else constructions in rotation files, and actually the reason for its existence, is the possibility to modify the rotation and settings depending on the number of active clients on the server. For this purpose the cvar xp_activeClients
exists, which holds the number of non-spectators currently playing on the server:
if ( $xp_activeClients >= 12 ) { // big maps, 12 clients or more q3dm12 q3dm14 q3dm15 } else if ( $xp_activeClients >= 6 ) { // normal maps, from 6 to 11 clients q3dm6 q3dm7 q3dm8 } else { // small maps, 5 clients or less q3dm2 q3dm3 q3dm5 }
You can also use conditions inside map blocks:
$g_gametype = 3; q3dm6 { if ( $xp_activeClients > 6 ) { $fraglimit = 100; } else { $fraglimit = 50; } }
To finalize, there is an issue to keep an eye on when using conditions inside the rotation file. To illustrate it, consider the following rotation:
q3dm6 if ( $xp_activeClients > 6 ) { q3dm13 } q3dm8 q3dm9
It's pretty obvious what is the objective with that rotation file: to rotate to q3dm13 only if there is more than 6 clients on the server, but otherwise rotate directly to q3dm8.
However, a problem arises here, and it's because of the way the rotation works internally: it first counts the number of maps on the rotation, ignoring the maps for which the conditions are not met, and then it executes the map in turn. The effects of this are two problems:
These problems can become worse the more we use that kind of conditions. To fix this, always include an else
with the same number of maps of the original condition so that the total number of maps doesn't vary depending on the number of players. Applying this to the example, the fixed rotation would look something like the following:
q3dm6 if ( $xp_activeClients > 6 ) { q3dm13 } else { q3dm7 } q3dm8 q3dm9