Server Maplist Rotation/Manipulation v2.00  (Dedicated Server Version)
by Allan "Geist" Campbell

*Code color conventions:    red = original code;    green = author's optional comments;    blue = new code

This document is not primarily a tutorial, but instructions for implementing my .dll mod in your source.  Although a lot of "tutorials" out there do this, I do not really call it a tutorial, but a procedure (or recipe, if you will).  Once I have some time to do so, I will (or anyone else can) break down the code and explain it in detail, but I tried to be generous with my inline comments.

*Note:  Version 1.01 of this document was written for v3.05 of id's source release, and it only worked with listen servers.  Now that we have v3.14 of the source, user-defined dedicated server commands are now available.  Also, when you run a listen server, you are actually running a server AND a client -- and on some systems (including mine), you can actually get a performance gain by running a dedicated server, then running a SEPARATE client to connect.  Therefore, I have rewritten this code to ONLY be controlled by a dedicated server.  If you want to control it from a client, you can set a rcon_password from dedicated console, then use "rcon <password> sv maplist <whatever>" for your command line.  Also, from my tests, it seems that aliases on the server do not work properly with user-defined commands.  But if you find a way, let me know ;)

Now then, on with the code implementation.  You will be making modifications to the following files:

And you will be creating these files (you can just copy/paste from this doc, to make it easier): And finally, a sample data file called mysever.ini is provided.

You will notice that some of these files are extremely small, but they provide modularity, and plenty of room for future expansion.  So lets get started!

q_shared.h    (modification)
Just add one line at the end of the #define DF_* section (around line 855):

 *Note:  If I modify the original source, I always mark the modified code (with my initials, etc).  This makes it much easier to go back and find my modifications later.  For a single line, I usually use //LAC, but for blocks of code, //LAC+++ and //LAC--- are used.

Other mod authors will eventually add entries to this list.  DF_MAP_LIST does not have to be 65536, but it must be an unused power of 2 that an unsigned int can hold. (It's a bit-positioned value)  All this really does is give our code a bit/slot in dmflags->value to flag when a maplist is in effect.

g_local.h    (modification)
Add the following lines, just after the function prototypes for g_main.c (around line 754):

This just prototypes the EndDMLevel() function that id included in g_main.c (we'll use it in maplist.c), and makes our new #defines, structs, prototypes, etc. available to all other files in the .dll project.

g_cmds.c    (modification)
In ClientCommand(), you can insert these lines basically anywhere in the else if() statements, as long as you obey the structure of the existing blocks (i.e. put it after the {}).  I chose to put it just before that last else statement (around line 905 -- the end of the file):

Very simple.  When a player types /cmd maplist <whatever> at the console, Cmd_Maplist_f() is called to parse the line and execute the request.

g_svcmds.c    (modification)
Basically the same modification that we just did to g_cmds.c.  Except this change enables the dedicated server maplist command.  Add this code to ServerCommand() just before the else (around line 24):

So then, when you type sv maplist <whatever> at the dedicated server console, Svcmd_Maplist_f() will be called.  This structure is in parallel to the ClientCommand() modification.

g_main.c    (modification)
Here we go.  The EndDMLevel() function is called whenever a timelimit or fraglimit is reached in DeathMatch play.  Here's the modified code (around line 164):

All we did here was test our newly assigned bit in dmflags->value, and if it is set, we have a maplist to follow.  So we proceed to pick the next map based on the type of rotation specified when the maplist was created (we'll get to that later).  I decided to use a switch() here, in case I want to test for more values in the future.  Then an entity of class "target_changelevel" is created and it's .map variable is set to the name of our chosen map from the list. (This is nearly identical to the level-changing code in the rest of the function)

fileio.c    (new file)
We're going to use an external ASCII text file for the maplist names.  This way you can change the names in the file and reload them into Quake2 without even leaving the game or the server!  So then, we obviously need functions to open and close files.  Here is fileio.c in its entirety:

Albeit, these functions are extremely limited.  And as they exist now, you'd probably be better off just using fopen() and fclose() in your code.  However, you can fleshed them out to provide more features, and they illustrate passing of file handles between functions.

fileio.h    (new file)
Ok, here's the VERY short file, prototyping the functions we used in fileio.c:

Again, you can always add more functions later for file IO.

maplist.h    (new file)
And another short file.  This one has a bit more usefulness, however.  Here's the whole file:

This file introduces a new structure that is typedef'd as maplist_t.  This structure will hold the total number of maps in the rotation, their names, an index to the current map, and a rotationflag that specifies how the maps will progress, whether sequentially, or at random, etc. (you can add more values to this).  Then a global maplist is declared.

maplist.c    (new file)
Ok, here's the big one.  Below it is displayed as a whole, but I feel that it should be commented well enough for most coders to see what it does.  If I get enough questions about it, however, I will provide more explanation in this procedure.

To get a brief explanation of what each function does (but not how it does it), check out the output of DisplayMaplistUsage().  It's the same explanation a server-op would see when typing sv maplist help.  Notice that players will not see this entire list of server functions.  I have tried to catch data errors of any type, and I've tested it quite a bit.  The dedicated server-op (and/or players with the rcon password) is the only one that can enable, disable, start, advance, or skip levels in the maplist system.  All clients, however, can display the list of maps, and the help screen.

myserver.ini    (new runtime sample file)
I chose a Windows .ini format for the data file, with the mapnames listed in the [maplist] section.  If you place the following file in your Quake2 base directory (e.g. C:\Quake2\), then you (server-op) can sv maplist myserver.ini or sv maplist myserver.ini 1, then sv mapserver start (or next -- currently does the same thing as start) at the console to begin the rotation.

*Note:  You can use almost anything for the filename -- except for the maplist commands such as start, goto, etc.  But who names a file one of those?

The [maplist] section can be anywhere in the file, but be sure not to put any comments between [maplist] and ###. (blank lines are ok, though)

Don't forget to add fileio.c and maplist.c to your project (if necessary).  Compile and test it, and send me any questions/suggestions for improvement.  (email address at top and bottom of this page)  It's not the best out there, but it seems to be one of the first "tutorials" on the subject.  (I never really had a desire to do this mod, but someone asked for it, and I never saw anyone else take up the request)  By no means do I consider this a complete server mod.  I intentionally left features out, because I wanted this doc to concentrate only on the maplist function.  You will probably even find a better way to do maplists, as well, but at least I've provided somewhere to start -- and that's good enough for me.

For your protection and mine, I will not provide a compiled version of this code, because it could eventually pass through malicious hands, and my reputation could be tarnished.  And I figure that if you don't have a compiler or you're not looking to learn a little programming, you probably wont be reading this anyway. :)

As for future development of this mod, it is so basic that anyone can use it and modify it in any (non-malicious) way they want.  I would appreciate an honorable mention (and an email about your use of it), but it's no biggie.  However, if you reproduce this procedure (or "tutorial", if you're so inclined), you must credit me as a source.

Copyright 1998 - L. Allan Campbell  (Geist)