
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 1999if (!(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