Standard Logging Support!

   This tutorial will show you how to implement support for the standard log, a system which all the programs like Gibstats use to tally player statistics and put them on webpages.

First off, this code is courtesy of Mark Davies, who runs the Statlogging site on PlanetQuake.

In order for this tutorial to work properly, you must be using MSVC++ 5.0 or greater. It is also important that you download this zip file with the standard log source files:

Standard Logging Files:  stdlog.zip

After you unzip the files, place the stdlog.obj in the release folder with the other object files.  You will need to add the stdlog.obj file to your project within MSVC by clicking on Project->Add To Project->Files, specifying .obj files, and selecting the stdlog.obj.  The other files go with your source code and also must be added to the project in the same manner as the stdlog.obj, except they are not object files.

If all that has been done you now have 3 new files and 1 new object file added to your project.

Now let's get started!

Add in the blue code and remove any pink code.

First open up g_local.h.  Add this new cvar as shown:

 

extern    cvar_t     *sv_maplist;

// StdLog
extern    cvar_t    *stdlogfile;

 Now go down to the very bottom of the file and put this:

// StdLog
void StartGSLogFile(void);

 Close the file.  Next open up g_main.c and place these #includes right near the top:

#include "g_local.h"

#include "stdlog.h"    // StdLog
#include "g_statlog.h"    // StdLog

 Now go down until you find the list of cvars and add in the new one:

cvar_t    *sv_maplist;

//StdLog
cvar_t    *stdlogfile;

 Go down to the ShutdownGame function and add this:

void ShutdownGame (void)
{
    gi.dprintf ("==== ShutdownGame ====\n");

    sl_GameEnd( &gi, level );     // StdLog

    gi.FreeTags (TAG_LEVEL);
    gi.FreeTags (TAG_GAME);
}

Now go to the EndDMLevel function and add this:

void EndDMLevel (void)
{
    edict_t        *ent;
    char *s, *t, *f;
    static const char *seps = " ,\n\r";

    sl_GameEnd( &gi, level );     // StdLog - Mark Davies

    // stay on same level flag
    if ((int)dmflags->value & DF_SAME_LEVEL)
    {
        BeginIntermission (CreateTargetChangeLevel (level.mapname) );
        return;
    }

Close the file.  Next open up g_save.c and add the #includes at the top:

#include "g_local.h"

#include "stdlog.h"    // StdLog
#include "g_statlog.h"    // StdLog

Next go the the InitGame function and add the indicated lines:

void InitGame (void)
{
    //StdLog Start
    cvar_t *stdlogfile = gi.cvar("stdlogfile","0",CVAR_LATCH);

    if(stdlogfile->value)
        StartGSLogFile();

    sl_Logging( &gi, "My Mod's Name" );
    //StdLog End

    gi.dprintf ("==== InitGame ====\n");

Close the file.  Now open up g_spawn.c and add the #Includes at the top:

#include "g_local.h"

#include "stdlog.h"    // StdLog
#include "g_statlog.h"    // StdLog

Go to the spawnentities function and add this to the end:

    G_FindTeams ();

    sl_GameStart( &gi, level );     // StdLog
}

Close the file.  Next open up p_client.c and add the #Includes at the top:

#include "g_local.h"
#include "m_player.h"

#include "stdlog.h"    //StdLog
#include "g_statlog.h"    //StdLog

Go to the player_die function and add this:

    if (!self->deadflag)
    {
        self->client->respawn_time = level.time + 1.0;
        LookAtKiller (self, inflictor, attacker);
        self->client->ps.pmove.pm_type = PM_DEAD;
        ClientObituary (self, inflictor, attacker);

        sl_WriteStdLogDeath( &gi, level, self, inflictor, attacker);    // StdLog

        TossClientWeapon (self);
        if (deathmatch->value)
            Cmd_Help_f (self);         // show scores

        // clear inventory
        // this is kind of ugly, but it's how we want to handle keys in coop
        for (n = 0; n < game.num_items; n++)
        {
            self->client->pers.inventory[n] = 0;
        }
    }

Next go to the clientbegindeathmatch function and add this to the end:

    sl_WriteStdLogPlayerEntered( &gi, level, ent );     // StdLog

    // make sure all view stuff is valid
    ClientEndServerFrame (ent);
}

Next go to the clientdisconnect function and add this:

    if (!ent->client)
        return;

    gi.bprintf (PRINT_HIGH, "%s disconnected\n", ent->client->pers.netname);

   sl_LogPlayerDisconnect( &gi, level, ent );     // StdLog

Now you need to open up g_statlog.c and go down to the StartGSLogfile function.  Near the beginning it points to where the file is opened and saved.

Change it to suit your mod. (if your mod resides in quake2/mymod then you want the function to point to mymod/stdlog.log)

You're done!  Explanation:  All the calls made with the sl_whatever simply write to the logging file about the player.  More things can be added, but for now it simply covers time connected, frags, and deaths.  Make sure you turn it on using the stdlogfile variable. 

Tutorial by Willi