Quake DeveLS - Winner Logging

Author: Kieren Johnstone a.k.a. -X'BuG-
Difficulty: Easy

Today, we are going to find out how to find out who wins at the end of each match and log them to a file on the server! It's a real quickie, but it's great for ranking the best and/or worst players on the server !!

  • Please that this tutorial assumes you have downloaded the q_devels.c functions, which always help out !!
  • You should also have the 3.20 source code.

    Here we go then...Today, we are only going to be editing one function in g_main.c.
    Find the EndDMLevel function. You should see something like this:

    /*
    =================
    EndDMLevel
    
    The timelimit or fraglimit has been exceeded
    =================
    */
    void EndDMLevel (void)
    {
    	edict_t		*ent;
    	char *s, *t, *f;
    	int			its_a_draw;
    	static const char *seps = " ,\n\r";
    
    	// stay on same level flag
    	if ((int)dmflags->value & DF_SAME_LEVEL)
    	{
    		BeginIntermission (CreateTargetChangeLevel (level.mapname) );
    		return;
    	}
    

    As you may have guessed, this is the function that is executed when it's time for the map to change - either the fraglimit or timelimit (or in CTF, caplimit) has been reached.
    To enable us to display and/or log the winner, we need to change this...replace EndDMLevel with the code below.

    /*
    =================
    EndDMLevel
    
    The timelimit or fraglimit has been exceeded
    =================
    */
    void EndDMLevel (void)
    {
    	//---------------------------//
    	//  WINNER LOGGING BEGIN     //
    	//---------------------------//
     	// | This code is pretty    |
    	// | much self-explanitary, |
    	// | just read the comments |
    	// | or e-mail me if you    |
    	// | don't understand.      |
    	// \                       /
    	//  \_____________________/
    
    	edict_t			*ent;
    	edict_t			*joe_bloggs;  // this is the entity to test (see if its a winner)
    	char			*s, *t, *f;
    	int			i;            // this is a temporary counter
    	int			winningfrags; // this is the frags of the winner
    	edict_t			*winner;      // this points to the winning person
    	file			*logfile;     // this is our file handle
    	static const char *seps = " ,\n\r";
    
    	winningfrags = 0;        // reset winning frags - gotta get more than zero !!
    	winner = 0;              // reset winner to nobody
    
    	for_each_player(joe_bloggs,i)     // for every single player
    	{
    		// tell them their frags
    
    		gi.dprintf(joe_bloggs,PRINT_MEDIUM, "%s got %i frags.\n",joe_bloggs->client->pers.netname,joe_bloggs->client->resp.score);
    
    
    		// if they've got higher frags than the highest so far,
    
    		if (joe_bloggs->client->resp.score > winningfrags) {
    			// set the new winning frags
    			winningfrags = joe_bloggs->client->resp.score;
    			// and point to them as the winner
    			winner = joe_bloggs;
    		}
    	}
    
    	// say the winner to everybody
    	if (winner == 0) {
    		// nobody is in / got more than zero frags
    		gi.bprintf(PRINT_HIGH, "Nobody won\n");
    	} else {
    		// "winner" wins
    		gi.bprintf(PRINT_HIGH, "%s wins!!\n",winner->client->pers.netname);
    	}
    
    	// ---
    	// file logging
    	// ---
    
    	// open output to "winners.log" (wa = write, append)
    	logfile = fopen("wa","winners.log");
    
    	// write winner name
    	fputs(winner->client->pers.netname,logfile);
    
    	// and newline
    	fputs("\r\n",logfile);
    
    	// close file
    	fclose(logfile);
    
    	//---------------------------//
    	//  WINNER LOGGING END       //
    	//---------------------------//
    
    	// normal code again....
    
    	// stay on same level flag
    	if ((int)dmflags->value & DF_SAME_LEVEL)
    	{
    		BeginIntermission (CreateTargetChangeLevel (level.mapname) );
    		return;
    	}
    
    	// see if it's in the map list
    	if (*sv_maplist->string) {
    		s = strdup(sv_maplist->string);
    		f = NULL;
    		t = strtok(s, seps);
    		while (t != NULL) {
    			if (Q_stricmp(t, level.mapname) == 0) {
    				// it's in the list, go to the next one
    				t = strtok(NULL, seps);
    				if (t == NULL) { // end of list, go to first one
    					if (f == NULL) // there isn't a first one, same level
    						BeginIntermission (CreateTargetChangeLevel (level.mapname) );
    					else
    						BeginIntermission (CreateTargetChangeLevel (f) );
    				} else
    					BeginIntermission (CreateTargetChangeLevel (t) );
    				free(s);
    				return;
    			}
    			if (!f)
    				f = t;
    			t = strtok(NULL, seps);
    		}
    		free(s);
    	}
    
    	if (level.nextmap[0]) // go to a specific map
    		BeginIntermission (CreateTargetChangeLevel (level.nextmap) );
    	else {	// search for a changelevel
    		ent = G_Find (NULL, FOFS(classname), "target_changelevel");
    		if (!ent)
    		{	// the map designer didn't include a changelevel,
    			// so create a fake ent that goes back to the same level
    			BeginIntermission (CreateTargetChangeLevel (level.mapname) );
    			return;
    		}
    		BeginIntermission (ent);
    	}
    }
    
    And that's all you need to do - I told you it's a quickie!
    If you have any trouble compiling or understanding this code, please feel free to e-mail me.


    Tutorial by Kieren Johnstone, author of forthcoming mod CLOT.

    This site, and all content and graphics displayed on it,
    are ©opyrighted to the Quake DeveLS team. All rights received.
    Got a suggestion? Comment? Question? Hate mail? Send it to us!
    Oh yeah, this site is best viewed in 16 Bit or higher, with the resolution on 800*600.
    Thanks to Planet Quake for there great help and support with hosting.
    Best viewed with Netscape 4 or IE 5