Quake DeveLS - Server Maplist Rotation/Manipulation v1.0

Author: L. Allan Campbell (Geist)
Difficulty: Hard

*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:  So far, this only works with non-dedicated servers, as right now the easiest way to implement this command is through ClientCommand().  It could probably be done using cvars, but according to John Carmack's Jan10,'98 .plan:

Therefore, I will update this document with dedicated-server-specific code after the above-mentioned update is available, and I've had plenty of time to test it.  All-in-all, it shouldn't be a drastic update for this code.

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 the end of 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 818):

 *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 4096, but it must be an unused power of 2 that an 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 705):

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)
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 651):

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

g_main.c    (modification)
Here we go.  The EndDMLevel() function is called whenever a timelimit or fraglimit is reached in DeathMatch play.  ExitLevel() is called whenever the the player reaches the exit in single-player mode (and most likely co-op mode in the future).  Since right now we're only concerned with DM, we'll only modify EndDMLevel() -- as follows (around line 165):

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 procede 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 now how it does it), check out the output of DisplayMaplistUsage().  It's the same explanation a player would see when typing /cmd maplist help. (or just about any invalid command.  I have tried to catch data errors of any type, and I've tested it quite a bit.  Right now, client 0 (the machine that is running the non-dedicated server) is the only player 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 (client 0) can /cmd maplist myserver.ini or /cmd maplist myserver.ini 1, then /cmd 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 it, and send me any questions/suggestions for improvement.  (email address at top)  It's not the best out there, but it seems to be one of the first.  (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)

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.  I will update it in the future with dedicated server support, and possibly a password feature (so you can leave your server unattended or access it remotely).  The latter will most likely be part of a larger remote server mod, however.

Copyright 1998 - L. Allan Campbell  (Geist)