Consistant Players

This tutorial (which was requested) is designed to allow you to have a number of stats which remain constant between games. When a player logs in (or when you stuffcmd 'exec login.cfg') it searches for a player data structure thats been loaded from a text file and assigns the one it finds (if one doesn't exist it create a new one) to that player. As of yet it has no encryption built in, but it's designed so that it is really easy to add one. as with most of my tutorials these days, it comes with a file containing most of the code already implemented. I'll be explaining bits of it though so that you can add your own set of stats to it


Down to business. Open up g_local.h here is where we will be adding the definitions that make the tut work.

Paste the code below at the very bottom of the file after the last }

 
 
// consistant players. FuzzySteve 1999  Start
 
 
typedef struct
{
        char    userid[20];
        char    pass[10];
        int     level;
        int     points;
        struct playerdata_s *nextplayer;
} playerdata_s;
 
 
extern playerdata_s *playerlist;
 
void clearplayerlist(playerdata_s *listitem);
qboolean loadplayerdata(void);
 
 
// consistant players. FuzzySteve 1999 End

If you want to have your own stats, add them to the structure. you'll also need to modify the save and load routines, but thats not too much of a problem. I'd suggest always refering to the data in the players own copy of this, rather than keeping it in a seperate structure and copying it over when saves are occuring. the only down side to this is the fact that people that haven't logged in can't advance (you'll always need to check to see if they have logged in or you'll crash the server when it tries to change something that doesn't exist)

You'll also need to add the code below to the bottom of the definition of gclient_s

// consistant players. FuzzySteve 1999
        struct playerdata_s    *playerinfo;

just below the code that follows

 
        edict_t        *chase_target;         // player we are chasing
        qboolean       update_chase;          // need to update chase info?

playerinfo will be linked to a set of player data when the player logs in. before then it's a pointer to NULL, so always check if it exists before using it. (i.e. encapsulate the modifications to playerinfo within an if statement checking ent->client->playerinfo)

That's us done with g_local.h.


Next we go to g_spawn.c. Find the function SP_worldspawn and add the code below to it, just at the top of that function

// consistant players. FuzzySteve 1999
if (!(playerlist==NULL))
{
        clearplayerlist(playerlist);
}
        loadplayerdata();

All this does is checks to see if there is any player data, if there is then clear it. The it loads up the data again. (if you have hundreds of players you may wish to change this, but it should work ok even with lots of players as there isn't that much data flowing around) Done with this file


Now load up p_client.c When I was asked to develop this, then only times peoples stats would change is when someone got killed. Therefore thats the only time I would need to rewrite the data file (that way if the server crashed no data would be lost). If you are wanting to cut down the number of saves, then ignore this bit and add the call to the save function somewhere else where it won't be called as often (or if you are changing stats elsewhere)

At the end of player_die add the line below. Thats us done with this file

        saveplayerdata();

Now comes the login interface. This annoyed me for a fair time as it seems you can only access the arguments to a user comamnd withing g_cmds.c. So open up that file

Scroll to the end of it and modify it to look like below

        else if (Q_stricmp(cmd, "playerlist") == 0)
               Cmd_PlayerList_f(ent);
        else if (Q_stricmp(cmd,"logon")==0)
               logon(ent);
        else    // anything that doesn't match a command will be a chat
               Cmd_Say_f (ent, false, true);

Now scroll to the top and add the code below, just after the defines at the top of that file.

 
// Consistant players FuzzySteve 1999 start
 
int finduser(char *userid,char *pass,edict_t *ent);
void logon (edict_t *ent)
{
 
char *tempuserid,*temppass;
int i;
 
if (gi.argc () < 2)
        return;
 
tempuserid=gi.argv(1);
temppass=gi.argv(2);
 
i=finduser(tempuserid,temppass,ent);
switch (i)
{
        case 0:
        CreateNewUser(ent,tempuserid,temppass);
        break;
        case 1:
        gi.cprintf(ent,PRINT_HIGH,"Welcome To the Game %s \n",tempuserid);
        break;
        case 2:
        gi.cprintf(ent,PRINT_HIGH,"Sorry, that username is used (or you got the password wrong)\n");
        break;
}
}
 
// consistant players FuzzySteve 1999 end

Fairly easy huh? Finally add the file consplay.c to your project/makefile. The file can be obtained at the bottom of this page


If no player data file exists one will be created. You'll need to create a folder called 'config' within your mod directory though. To logon on the command to give is logon [username <20 characters] [password <10 characters] if the username doesn't exist it will be created. if it exists with a different password you will be informed and will have to log in again with a different name. If both exist then you will log in and the data will be linked to you.


Now to some explanation of how to add your own stats to the tut. Adding them to the save file is really easy. I've designed that in a seperate function from the rest of the save file stuff. Open up consplay.c and look for the funtion assembleoutput I've copied it below

void assembleoutput(char output[],playerdata_s *playerdata)
{
int i;
 
i=sprintf(output,"%-20.20s ",playerdata->userid);
i+= sprintf(output+i,"%-20.20s ",playerdata->pass);
i+=sprintf(output+i,"%d ",playerdata->level);
i+=sprintf(output+i,"%d ",playerdata->points);
 
encrypt(output);
}

The code should be fairly simple to understand (the i is there so the text already there isn't overwritten) The "%-20.20s " is just there to say, left align the text with 20 characters maximum and minimum space [the rest is filled with blank space) it makes it easy to seperate the username and password. If you wanted to add you own stats then you just add more lines formatted like the ones above at the end. the call to encrypt currently doesn't do anything. I'll be releasing function to make it work later.


Now we need to recover the text from the file. Go to the function loadplayerdata the bits of code that are important are below

 
               decrypt(buffer);
                sscanf(buffer,"%s%s%d%d",&tmpplayer.userid,&tmpplayer.pass,&tmpplayer.level,&tmpplayer.points);                    

again decrypt does nothing (that function will be released later) All I'm doing is copying the data into a temporary playerdata structure. it should be fairly simple to see how to add your own data to that.

next the data has to be copied into the linked list. the code to do that is also in loadplayerdata and is copied below

        strcpy(newhead->userid,tmpplayer.userid);
        strcpy(newhead->pass,tmpplayer.pass);
        newhead->level=tmpplayer.level;
        newhead->points=tmpplayer.points;
        newhead->nextplayer=NULL;

You should be able to see how to add your own here too.


Thats us done. If you have any problems feel free to email me and we can try and work them out (I'm also on icq, so you can message me) be sure to tell me exactly what is going wrong rather that 'it doesn't work' as thats kinda useless


Skunkworks Tutorials