21860bd9dc
git-svn-id: https://svn.code.sf.net/p/fteqw/code/branches/wip@3805 fc73d0e0-1445-4013-8a0c-d673dee63da5
918 lines
18 KiB
C
918 lines
18 KiB
C
#include "qwsvdef.h"
|
|
#include "errno.h"
|
|
|
|
#ifndef CLIENTONLY
|
|
|
|
#if defined(_WIN32) && !defined(MINGW)
|
|
#define inline //_inline //fix for stupid VC
|
|
#elif defined(CLANG)
|
|
#define inline // fix for stupid clang
|
|
#endif
|
|
|
|
#ifdef SVRANKING
|
|
|
|
typedef struct {
|
|
int ident;
|
|
int version;
|
|
int usedslots;
|
|
int leader;
|
|
int freeslot;
|
|
} rankfileheader_t;
|
|
|
|
//endian
|
|
#define swaplong LittleLong
|
|
#define swapfloat LittleFloat
|
|
|
|
rankfileheader_t rankfileheader;
|
|
FILE *rankfile;
|
|
|
|
cvar_t rank_autoadd = SCVAR("rank_autoadd", "1");
|
|
cvar_t rank_needlogin = SCVAR("rank_needlogin", "0");
|
|
cvar_t rank_filename = SCVAR("rank_filename", "");
|
|
char rank_cvargroup[] = "server rankings";
|
|
|
|
#define RANKFILE_VERSION ((NUM_RANK_SPAWN_PARMS==32)?0:0x00000001)
|
|
#define RANKFILE_IDENT *(int*)"RANK"
|
|
|
|
void inline READ_PLAYERSTATS(int x, rankstats_t *os)
|
|
{
|
|
int i;
|
|
size_t result;
|
|
|
|
fseek(rankfile, sizeof(rankfileheader_t)+sizeof(rankheader_t)+((x-1)*sizeof(rankinfo_t)), SEEK_SET);
|
|
result = fread(os, sizeof(rankstats_t), 1, rankfile);
|
|
|
|
if (result != sizeof(rankstats_t))
|
|
Con_Printf("READ_PLAYERSTATS() fread: expected %lu, result was %u (%s)\n",(long unsigned int)sizeof(rankstats_t),(unsigned int)result,strerror(errno));
|
|
|
|
os->kills = swaplong(os->kills);
|
|
os->deaths = swaplong(os->deaths);
|
|
for (i = 0; i < NUM_RANK_SPAWN_PARMS; i++)
|
|
os->parm[i] = swapfloat(os->parm[i]);
|
|
os->timeonserver = swapfloat(os->timeonserver);
|
|
// os->flags1 = (os->flags1);
|
|
// os->trustlevel = (os->trustlevel);
|
|
// os->pad2 = (os->pad2);
|
|
// os->pad3 = (os->pad3);
|
|
}
|
|
|
|
void inline WRITE_PLAYERSTATS(int x, rankstats_t *os)
|
|
{
|
|
rankstats_t ns;
|
|
int i;
|
|
|
|
fseek(rankfile, sizeof(rankfileheader_t)+sizeof(rankheader_t)+((x-1)*sizeof(rankinfo_t)), SEEK_SET);
|
|
|
|
ns.kills = swaplong(os->kills);
|
|
ns.deaths = swaplong(os->deaths);
|
|
for (i = 0; i < NUM_RANK_SPAWN_PARMS; i++)
|
|
ns.parm[i] = swapfloat(os->parm[i]);
|
|
ns.timeonserver = swapfloat(os->timeonserver);
|
|
ns.flags1 = (os->flags1);
|
|
ns.trustlevel = (os->trustlevel);
|
|
ns.pad2 = (os->pad2);
|
|
ns.pad3 = (os->pad3);
|
|
|
|
fwrite(&ns, sizeof(rankstats_t), 1, rankfile);
|
|
}
|
|
|
|
void inline READ_PLAYERHEADER(int x, rankheader_t *oh)
|
|
{
|
|
size_t result;
|
|
|
|
fseek(rankfile, sizeof(rankfileheader_t)+((x-1)*sizeof(rankinfo_t)), SEEK_SET);
|
|
|
|
result = fread(oh, sizeof(rankheader_t), 1, rankfile);
|
|
|
|
if (result != sizeof(rankheader_t))
|
|
Con_Printf("READ_PLAYERHEADER() fread: expected %lu, result was %u (%s)\n",(long unsigned int)sizeof(rankheader_t),(unsigned int)result,strerror(errno));
|
|
|
|
oh->prev = swaplong(oh->prev); //score is held for convineance.
|
|
oh->next = swaplong(oh->next);
|
|
// strcpy(oh->name, oh->name);
|
|
oh->pwd = swaplong(oh->pwd);
|
|
oh->score = swapfloat(oh->score);
|
|
}
|
|
|
|
void inline WRITE_PLAYERHEADER(int x, rankheader_t *oh)
|
|
{
|
|
rankheader_t nh;
|
|
|
|
fseek(rankfile, sizeof(rankfileheader_t)+((x-1)*sizeof(rankinfo_t)), SEEK_SET);
|
|
|
|
nh.prev = swaplong(oh->prev); //score is held for convineance.
|
|
nh.next = swaplong(oh->next);
|
|
Q_strncpyz(nh.name, oh->name, sizeof(nh.name));
|
|
nh.pwd = swaplong(oh->pwd);
|
|
nh.score = swapfloat(oh->score);
|
|
|
|
fwrite(&nh, sizeof(rankheader_t), 1, rankfile);
|
|
}
|
|
|
|
void inline READ_PLAYERINFO(int x, rankinfo_t *inf)
|
|
{
|
|
READ_PLAYERHEADER(x, &inf->h);
|
|
READ_PLAYERSTATS(x, &inf->s);
|
|
}
|
|
|
|
void inline WRITEHEADER(void)
|
|
{
|
|
rankfileheader_t nh;
|
|
|
|
nh.ident = RANKFILE_IDENT;
|
|
nh.version = swaplong(RANKFILE_VERSION);
|
|
nh.usedslots = swaplong(rankfileheader.usedslots);
|
|
nh.leader = swaplong(rankfileheader.leader);
|
|
nh.freeslot = swaplong(rankfileheader.freeslot);
|
|
|
|
fseek(rankfile, 0, SEEK_SET);
|
|
fwrite(&nh, sizeof(rankfileheader_t), 1, rankfile);
|
|
}
|
|
//#define WRITEHEADER() {fseek(rankfile, 0, SEEK_SET);fwrite(&rankfileheader, sizeof(rankfileheader_t), 1, rankfile);}
|
|
|
|
#define NAMECMP(saved, against) Q_strncasecmp(saved, against, 31)
|
|
|
|
qboolean Rank_OpenRankings(void)
|
|
{
|
|
char syspath[MAX_OSPATH];
|
|
size_t result;
|
|
qboolean created;
|
|
if (!rankfile)
|
|
{
|
|
if (!*rank_filename.string)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!FS_NativePath(rank_filename.string, FS_GAMEONLY, syspath, sizeof(syspath)))
|
|
return false;
|
|
|
|
rankfile = fopen(syspath, "r+b");
|
|
if (!rankfile) //hmm... try creating
|
|
{
|
|
rankfile = fopen(syspath, "w+b");
|
|
created = true;
|
|
}
|
|
else
|
|
created = false;
|
|
if (!rankfile)
|
|
return false; //couldn't open file.
|
|
|
|
memset(&rankfileheader, 0, sizeof(rankfileheader));
|
|
|
|
fseek(rankfile, 0, SEEK_SET);
|
|
result = fread(&rankfileheader, sizeof(rankfileheader_t), 1, rankfile);
|
|
|
|
if (result != sizeof(rankfileheader_t))
|
|
Con_Printf("Rank_OpenRankings() fread: expected %lu, result was %u (%s)\n",(long unsigned int)sizeof(rankfileheader_t),(unsigned int)result,strerror(errno));
|
|
|
|
rankfileheader.version = swaplong(rankfileheader.version);
|
|
rankfileheader.usedslots = swaplong(rankfileheader.usedslots);
|
|
rankfileheader.leader = swaplong(rankfileheader.leader);
|
|
rankfileheader.freeslot = swaplong(rankfileheader.freeslot);
|
|
|
|
if (!created && (rankfileheader.version != RANKFILE_VERSION || rankfileheader.ident != RANKFILE_IDENT))
|
|
{
|
|
Con_Printf("Rank file is version %i not %i\nEither delete the file or use an equivelent version of " DISTRIBUTION "\n", rankfileheader.version, RANKFILE_VERSION);
|
|
fclose(rankfile);
|
|
rankfile = NULL;
|
|
|
|
return false;
|
|
}
|
|
|
|
return true; //success.
|
|
}
|
|
return true; //already open
|
|
}
|
|
|
|
void LINKUN(int id)
|
|
{
|
|
int idnext, idprev;
|
|
rankheader_t hnext = {0}, hprev = {0}, info;
|
|
|
|
READ_PLAYERHEADER(id, &info);
|
|
|
|
idnext = info.next;
|
|
if (idnext)
|
|
READ_PLAYERHEADER(idnext, &hnext);
|
|
idprev = info.prev;
|
|
if (idprev)
|
|
READ_PLAYERHEADER(idprev, &hprev);
|
|
|
|
if (idnext)
|
|
{
|
|
hnext.prev = idprev;
|
|
WRITE_PLAYERHEADER(idnext, &hnext);
|
|
}
|
|
if (idprev)
|
|
{
|
|
hprev.next = idnext;
|
|
WRITE_PLAYERHEADER(idprev, &hprev);
|
|
}
|
|
else if (rankfileheader.leader == id) //ensure header is accurate
|
|
{
|
|
rankfileheader.leader = info.next;
|
|
WRITEHEADER();
|
|
}
|
|
else if (rankfileheader.freeslot == id)
|
|
{
|
|
rankfileheader.freeslot = info.next;
|
|
WRITEHEADER();
|
|
}
|
|
|
|
info.next = 0;
|
|
info.prev = 0;
|
|
WRITE_PLAYERHEADER(id, &info);
|
|
}
|
|
|
|
void LINKBEFORE(int bef, int id, rankheader_t *info)
|
|
{
|
|
int idnext, idprev;
|
|
rankheader_t hnext, hprev = {0};
|
|
|
|
if (!bef)
|
|
Sys_Error("Cannot link before no entry\n");
|
|
|
|
idnext = bef;
|
|
READ_PLAYERHEADER(idnext, &hnext);
|
|
idprev = hnext.prev;
|
|
if (idprev)
|
|
READ_PLAYERHEADER(idprev, &hprev);
|
|
|
|
//now we know the before and after entries.
|
|
|
|
hnext.prev = id;
|
|
WRITE_PLAYERHEADER(idnext, &hnext);
|
|
|
|
if (idprev)
|
|
{
|
|
hprev.next = id;
|
|
WRITE_PLAYERHEADER(idprev, &hprev);
|
|
}
|
|
else if (rankfileheader.leader == bef)
|
|
{
|
|
rankfileheader.leader = id;
|
|
WRITEHEADER();
|
|
}
|
|
else if (rankfileheader.freeslot == bef)
|
|
{
|
|
rankfileheader.freeslot = id;
|
|
WRITEHEADER();
|
|
}
|
|
info->next = idnext;
|
|
info->prev = idprev;
|
|
WRITE_PLAYERHEADER(id, info);
|
|
}
|
|
|
|
void LINKAFTER(int aft, int id, rankheader_t *info)
|
|
{
|
|
int idnext, idprev;
|
|
rankheader_t hnext = {0}, hprev = {0};
|
|
|
|
idprev = aft;
|
|
if (idprev)
|
|
{
|
|
READ_PLAYERHEADER(idprev, &hprev);
|
|
idnext = hprev.next;
|
|
}
|
|
else
|
|
idnext = rankfileheader.leader;
|
|
|
|
if (idnext)
|
|
READ_PLAYERHEADER(idnext, &hnext);
|
|
|
|
//now we know the before and after entries.
|
|
|
|
if (idnext)
|
|
{
|
|
hnext.prev = id;
|
|
WRITE_PLAYERHEADER(idnext, &hnext);
|
|
}
|
|
|
|
if (idprev)
|
|
{
|
|
hprev.next = id;
|
|
WRITE_PLAYERHEADER(idprev, &hprev);
|
|
}
|
|
else if (rankfileheader.leader == idnext)
|
|
{
|
|
rankfileheader.leader = id;
|
|
WRITEHEADER();
|
|
}
|
|
else if (rankfileheader.freeslot == idnext)
|
|
{
|
|
rankfileheader.freeslot = id;
|
|
WRITEHEADER();
|
|
}
|
|
info->next = idnext;
|
|
info->prev = idprev;
|
|
WRITE_PLAYERHEADER(id, info);
|
|
}
|
|
|
|
rankstats_t *Rank_GetPlayerStats(int id, rankstats_t *buffer) //returns the players persistant stats.
|
|
{
|
|
if (!Rank_OpenRankings())
|
|
return NULL;
|
|
if (!id)
|
|
{
|
|
Con_Printf("WARNING: Rank_GetPlayerStats with id 0\n");
|
|
memset(buffer, 0, sizeof(rankstats_t));
|
|
return NULL;
|
|
}
|
|
READ_PLAYERSTATS(id, buffer);
|
|
|
|
return buffer;
|
|
}
|
|
rankinfo_t *Rank_GetPlayerInfo(int id, rankinfo_t *buffer) //return stats + rankings.
|
|
{
|
|
if (!id)
|
|
{
|
|
Con_Printf("WARNING: Rank_GetPlayerInfo with id 0\n");
|
|
memset(buffer, 0, sizeof(rankinfo_t));
|
|
return NULL;
|
|
}
|
|
|
|
if (!Rank_OpenRankings())
|
|
return NULL;
|
|
|
|
READ_PLAYERINFO(id, buffer);
|
|
|
|
return buffer;
|
|
}
|
|
void Rank_SetPlayerStats(int id, rankstats_t *stats)
|
|
{
|
|
//rewrite to seek in a proper direction.
|
|
int nid;
|
|
rankheader_t rh, nh;
|
|
|
|
if (!id)
|
|
{
|
|
Con_Printf("WARNING: Rank_SetPlayerStats with id 0\n");
|
|
return;
|
|
}
|
|
|
|
//write
|
|
WRITE_PLAYERSTATS(id, stats);
|
|
|
|
//now re-sort.
|
|
READ_PLAYERHEADER(id, &nh);
|
|
nh.score = (stats->kills+1)/((float)stats->deaths+1);
|
|
//WRITE_PLAYERHEADER(id, &nh); //saved on link.
|
|
|
|
LINKUN(id);
|
|
|
|
nid = rankfileheader.leader;
|
|
if (!nid) //Hmm. First player!
|
|
{
|
|
LINKAFTER(0, id, &nh);
|
|
fflush(rankfile);
|
|
return;
|
|
}
|
|
while(nid)
|
|
{
|
|
READ_PLAYERHEADER(nid, &rh);
|
|
if (rh.score < nh.score)
|
|
{
|
|
LINKAFTER(rh.prev, id, &nh);
|
|
//LINKBEFORE(nid, id, &nh); //we are doing better than this guy.
|
|
fflush(rankfile);
|
|
return;
|
|
}
|
|
if (!rh.next)
|
|
{
|
|
LINKAFTER(nid, id, &nh); //Bum. We got to the end of the list and we are the WORST player!
|
|
fflush(rankfile);
|
|
return;
|
|
}
|
|
nid = rh.next;
|
|
}
|
|
}
|
|
|
|
int Rank_GetPlayerID(char *name, int pwd, qboolean allowadd, qboolean requirepasswordtobeset)
|
|
{
|
|
rankstats_t rs;
|
|
rankheader_t rh;
|
|
int id;
|
|
|
|
if (requirepasswordtobeset)
|
|
if (!pwd)
|
|
return 0;
|
|
|
|
if (!Rank_OpenRankings())
|
|
return 0;
|
|
|
|
id = rankfileheader.leader; //assumtion. A leader is more likly to be logging in than a begginer.
|
|
while(id)
|
|
{
|
|
READ_PLAYERHEADER(id, &rh);
|
|
if (!NAMECMP(rh.name, name))
|
|
{
|
|
if (rh.pwd == pwd || !rh.pwd)
|
|
{
|
|
if (!rh.pwd && requirepasswordtobeset)
|
|
return 0;
|
|
return id;
|
|
}
|
|
return 0;
|
|
}
|
|
id = rh.next;
|
|
}
|
|
|
|
if (!rank_autoadd.value || !allowadd)
|
|
return 0;
|
|
|
|
id = rankfileheader.freeslot;
|
|
if (id)
|
|
{
|
|
READ_PLAYERHEADER(id, &rh);
|
|
rankfileheader.freeslot = rh.next;
|
|
WRITEHEADER();
|
|
|
|
memset(&rh, 0, sizeof(rh));
|
|
Q_strncpyz(rh.name, name, sizeof(rh.name));
|
|
rh.pwd = pwd;
|
|
rh.prev = 0;
|
|
rh.next = rankfileheader.usedslots;
|
|
rankfileheader.usedslots = id;
|
|
WRITEHEADER();
|
|
|
|
WRITE_PLAYERHEADER(id, &rh);
|
|
|
|
memset(&rs, 0, sizeof(rs));
|
|
rs.trustlevel = 1;
|
|
Rank_SetPlayerStats(id, &rs);
|
|
|
|
fflush(rankfile);
|
|
return id;
|
|
}
|
|
|
|
id = ++rankfileheader.usedslots;
|
|
WRITEHEADER();
|
|
memset(&rh, 0, sizeof(rh));
|
|
Q_strncpyz(rh.name, name, sizeof(rh.name));
|
|
rh.prev = 0;
|
|
rh.pwd = pwd;
|
|
rh.next = 0;
|
|
WRITE_PLAYERHEADER(id, &rh);
|
|
|
|
memset(&rs, 0, sizeof(rs));
|
|
rs.trustlevel = 1;
|
|
WRITE_PLAYERSTATS(id, &rs);
|
|
|
|
Rank_SetPlayerStats(id, &rs);
|
|
|
|
fflush(rankfile);
|
|
return id;
|
|
}
|
|
|
|
void Rank_AddUser_f (void)
|
|
{
|
|
rankstats_t rs;
|
|
rankheader_t rh;
|
|
int id;
|
|
|
|
char *name = Cmd_Argv(1);
|
|
int pwd = atoi(Cmd_Argv(2));
|
|
int userlevel = atoi(Cmd_Argv(3));
|
|
|
|
if (Cmd_Argc() < 2)
|
|
{
|
|
Con_Printf("%s: <name> [pwd] [rights]\n", Cmd_Argv(0));
|
|
return;
|
|
}
|
|
//2
|
|
if (Cmd_Argc() >= 4)
|
|
{
|
|
if (userlevel >= Cmd_ExecLevel)
|
|
{
|
|
Con_Printf("You cannot add a user of equal or higher rank.\n");
|
|
return;
|
|
}
|
|
else if (userlevel < RESTRICT_MIN)
|
|
userlevel = RESTRICT_MIN;
|
|
}
|
|
if (Cmd_Argc() > 4)
|
|
{
|
|
Con_Printf("Too many arguments\n");
|
|
return;
|
|
}
|
|
|
|
SV_FixupName(name, name, sizeof(name));
|
|
|
|
if (!Rank_OpenRankings())
|
|
{
|
|
Con_Printf("Failed to open rankings file.\n");
|
|
return;
|
|
}
|
|
|
|
id = rankfileheader.leader;
|
|
while(id)
|
|
{
|
|
READ_PLAYERHEADER(id, &rh);
|
|
if (!NAMECMP(rh.name, name))
|
|
{
|
|
Con_Printf("User %s already exists\n", name);
|
|
return;
|
|
}
|
|
id = rh.next;
|
|
}
|
|
|
|
id = rankfileheader.freeslot;
|
|
if (id)
|
|
{
|
|
READ_PLAYERHEADER(id, &rh);
|
|
rankfileheader.freeslot = rh.next;
|
|
WRITEHEADER();
|
|
|
|
memset(&rh, 0, sizeof(rh));
|
|
Q_strncpyz(rh.name, name, sizeof(rh.name));
|
|
rh.pwd = pwd;
|
|
rh.prev = 0;
|
|
rh.next = rankfileheader.usedslots;
|
|
rankfileheader.usedslots = id;
|
|
WRITEHEADER();
|
|
|
|
WRITE_PLAYERHEADER(id, &rh);
|
|
|
|
memset(&rs, 0, sizeof(rs));
|
|
rs.trustlevel = userlevel;
|
|
Rank_SetPlayerStats(id, &rs);
|
|
|
|
fflush(rankfile);
|
|
return;
|
|
}
|
|
|
|
id = ++rankfileheader.usedslots;
|
|
WRITEHEADER();
|
|
memset(&rh, 0, sizeof(rh));
|
|
Q_strncpyz(rh.name, name, sizeof(rh.name));
|
|
rh.prev = 0;
|
|
rh.pwd = pwd;
|
|
rh.next = 0;
|
|
WRITE_PLAYERHEADER(id, &rh);
|
|
|
|
memset(&rs, 0, sizeof(rs));
|
|
rs.trustlevel = userlevel;
|
|
WRITE_PLAYERSTATS(id, &rs);
|
|
|
|
Rank_SetPlayerStats(id, &rs);
|
|
|
|
fflush(rankfile);
|
|
}
|
|
|
|
void Rank_SetPass_f (void)
|
|
{
|
|
rankheader_t rh;
|
|
char *name = Cmd_Argv(1);
|
|
int newpass = atoi(Cmd_Argv(2));
|
|
|
|
int id;
|
|
|
|
if (Cmd_Argc() != 3)
|
|
{
|
|
Con_Printf("setpass <name> <newpass>\n");
|
|
return;
|
|
}
|
|
|
|
if (!Rank_OpenRankings())
|
|
{
|
|
Con_Printf("Failed to open rankings file.\n");
|
|
return;
|
|
}
|
|
|
|
SV_FixupName(name, name, sizeof(name));
|
|
|
|
id = rankfileheader.leader;
|
|
while(id)
|
|
{
|
|
READ_PLAYERHEADER(id, &rh);
|
|
if (!NAMECMP(rh.name, name))
|
|
{
|
|
Con_Printf("Changing passcode of user %s.\n", rh.name);
|
|
rh.pwd = newpass;
|
|
WRITE_PLAYERHEADER(id, &rh);
|
|
return;
|
|
}
|
|
id = rh.next;
|
|
}
|
|
}
|
|
|
|
int Rank_GetPass (char *name)
|
|
{
|
|
rankheader_t rh;
|
|
|
|
int id;
|
|
|
|
if (!Rank_OpenRankings())
|
|
{
|
|
Con_Printf("Failed to open rankings file.\n");
|
|
return 0;
|
|
}
|
|
|
|
SV_FixupName(name, name, sizeof(name));
|
|
|
|
id = rankfileheader.leader;
|
|
while(id)
|
|
{
|
|
READ_PLAYERHEADER(id, &rh);
|
|
if (!NAMECMP(rh.name, name))
|
|
{
|
|
return rh.pwd;
|
|
}
|
|
id = rh.next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Rank_Enumerate (unsigned int first, unsigned int last, void (*callback) (const rankinfo_t *ri)) //leader first.
|
|
{
|
|
rankinfo_t ri;
|
|
int id;
|
|
int num;
|
|
|
|
if (!Rank_OpenRankings())
|
|
{
|
|
Con_Printf("Failed to open rankings file.\n");
|
|
return 0;
|
|
}
|
|
|
|
id = rankfileheader.leader; //start at the leaders
|
|
num=1;
|
|
while(id)
|
|
{
|
|
READ_PLAYERINFO(id, &ri);
|
|
|
|
if (num >= last)
|
|
return num - first;
|
|
if (num >= first)
|
|
callback(&ri);
|
|
|
|
num++;
|
|
id = ri.h.next;
|
|
}
|
|
return num - first;
|
|
}
|
|
|
|
void Rank_RankingList_f (void)
|
|
{
|
|
rankinfo_t ri;
|
|
int id;
|
|
int num;
|
|
|
|
FILE *outfile;
|
|
|
|
if (!Rank_OpenRankings())
|
|
{
|
|
Con_Printf("Failed to open rankings file.\n");
|
|
return;
|
|
}
|
|
|
|
outfile = fopen("list.txt", "wb");
|
|
|
|
fprintf(outfile, "%5s: %32s, %5s %5s\r\n", "", "Name", "Kills", "Deaths");
|
|
|
|
id = rankfileheader.leader; //start at the leaders
|
|
num=1;
|
|
while(id)
|
|
{
|
|
READ_PLAYERINFO(id, &ri);
|
|
|
|
fprintf(outfile, "%5i: %32s, %5i %5i\r\n", num, ri.h.name, ri.s.kills, ri.s.deaths);
|
|
|
|
num++;
|
|
id = ri.h.next;
|
|
}
|
|
|
|
fclose(outfile);
|
|
}
|
|
|
|
void Rank_Remove_f (void)
|
|
{
|
|
rankinfo_t ri;
|
|
int id;
|
|
int num;
|
|
int remnum;
|
|
|
|
if (Cmd_Argc() < 2)
|
|
{
|
|
Con_Printf("Removes a ranking entry.\nUse ranklist to find the entry number.");
|
|
return;
|
|
}
|
|
remnum = atoi(Cmd_Argv(1));
|
|
|
|
if (!Rank_OpenRankings())
|
|
{
|
|
Con_Printf("Failed to open rankings file.\n");
|
|
return;
|
|
}
|
|
|
|
id = rankfileheader.leader; //start at the leaders
|
|
num=1;
|
|
while(id)
|
|
{
|
|
READ_PLAYERINFO(id, &ri);
|
|
|
|
if (num == remnum)
|
|
{
|
|
LINKUN(id);
|
|
ri.h.next = rankfileheader.freeslot;
|
|
ri.h.prev = 0;
|
|
rankfileheader.freeslot = id;
|
|
WRITE_PLAYERHEADER(id, &ri.h);
|
|
WRITEHEADER();
|
|
fflush(rankfile);
|
|
|
|
Con_Printf("Client %s removed from rankings\n", ri.h.name);
|
|
return;
|
|
}
|
|
num++;
|
|
id = ri.h.next;
|
|
}
|
|
|
|
Con_Printf("Client %i not found\n", remnum);
|
|
}
|
|
|
|
void Rank_ListTop10_f (void)
|
|
{
|
|
rankinfo_t ri;
|
|
int id;
|
|
int num;
|
|
extern redirect_t sv_redirected;
|
|
|
|
if (!Rank_OpenRankings())
|
|
{
|
|
Con_Printf("Failed to open rankings file.\n");
|
|
return;
|
|
}
|
|
|
|
id = rankfileheader.leader; //start at the leaders
|
|
num=1;
|
|
while(id)
|
|
{
|
|
READ_PLAYERINFO(id, &ri);
|
|
|
|
if (sv_redirected)
|
|
Con_Printf("%2i: %5i %5i %s\n", num, ri.s.kills, ri.s.deaths, ri.h.name);
|
|
else
|
|
Con_Printf("%2i: %32s, %5i %5i\n", num, ri.h.name, ri.s.kills, ri.s.deaths);
|
|
if (num >= 10)
|
|
break;
|
|
|
|
num++;
|
|
id = ri.h.next;
|
|
}
|
|
if (num < 10)
|
|
Con_Printf("END\n");
|
|
}
|
|
|
|
void Rank_Find_f (void)
|
|
{
|
|
rankinfo_t ri;
|
|
int id;
|
|
|
|
char *match = Cmd_Argv(1);
|
|
|
|
if (!Rank_OpenRankings())
|
|
{
|
|
Con_Printf("Failed to open rankings file.\n");
|
|
return;
|
|
}
|
|
|
|
id = rankfileheader.leader; //start at the leaders
|
|
|
|
while(id)
|
|
{
|
|
READ_PLAYERINFO(id, &ri);
|
|
|
|
if (strstr(ri.h.name, match))
|
|
{
|
|
Con_Printf("%i %s\n", id, ri.h.name);
|
|
}
|
|
|
|
id = ri.h.next;
|
|
}
|
|
}
|
|
|
|
void Rank_Refresh_f(void)
|
|
{
|
|
int i;
|
|
if (!Rank_OpenRankings())
|
|
{
|
|
Con_Printf("Failed to open rankings file.\n");
|
|
return;
|
|
}
|
|
|
|
for (i=0, host_client = svs.clients ; i<MAX_CLIENTS ; i++, host_client++)
|
|
{
|
|
if (host_client->state != cs_spawned)
|
|
continue;
|
|
|
|
if (host_client->rankid)
|
|
{
|
|
rankstats_t rs = {0};
|
|
Rank_GetPlayerStats(host_client->rankid, &rs);
|
|
rs.timeonserver += realtime - host_client->stats_started;
|
|
host_client->stats_started = realtime;
|
|
rs.kills += host_client->kills;
|
|
rs.deaths += host_client->deaths;
|
|
host_client->kills=0;
|
|
host_client->deaths=0;
|
|
Rank_SetPlayerStats(host_client->rankid, &rs);
|
|
}
|
|
}
|
|
if (rankfile)
|
|
{
|
|
fclose(rankfile);
|
|
rankfile = NULL;
|
|
}
|
|
}
|
|
|
|
void Rank_RCon_f(void)
|
|
{
|
|
int gofor, num, id;
|
|
int newlevel;
|
|
rankstats_t rs = {0};
|
|
rankinfo_t ri;
|
|
|
|
if (!Rank_OpenRankings())
|
|
{
|
|
Con_Printf("Failed to open rankings file.\n");
|
|
return;
|
|
}
|
|
|
|
gofor = atoi(Cmd_Argv(1));
|
|
newlevel = atoi(Cmd_Argv(2));
|
|
if (newlevel >= Cmd_ExecLevel)
|
|
{
|
|
Con_Printf("You cannot promote a user to the same level as you\n");
|
|
return;
|
|
}
|
|
else if (newlevel < RESTRICT_MIN)
|
|
newlevel = RESTRICT_MIN;
|
|
|
|
//get user id
|
|
id = rankfileheader.leader; //start at the leaders
|
|
num = 1;
|
|
while(id)
|
|
{
|
|
READ_PLAYERINFO(id, &ri);
|
|
|
|
if (num == gofor)
|
|
{
|
|
//save new level
|
|
Rank_GetPlayerStats(id, &rs);
|
|
|
|
if (rs.trustlevel >= Cmd_ExecLevel)
|
|
{
|
|
Con_Printf("You cannot demote a higher or equal user.\n");
|
|
return;
|
|
}
|
|
rs.trustlevel = newlevel;
|
|
Rank_SetPlayerStats(id, &rs);
|
|
|
|
if (!ri.h.pwd && newlevel > 1)
|
|
Con_Printf("WARNING: user has no password set\n");
|
|
|
|
fflush(rankfile);
|
|
return;
|
|
}
|
|
|
|
num++;
|
|
id = ri.h.next;
|
|
}
|
|
Con_Printf("Couldn't find ranked user %i\n", gofor);
|
|
}
|
|
|
|
|
|
void Rank_RegisterCommands(void)
|
|
{
|
|
Cmd_AddCommand("ranklist", Rank_RankingList_f);
|
|
Cmd_AddCommand("ranktopten", Rank_ListTop10_f);
|
|
Cmd_AddCommand("rankfind", Rank_Find_f);
|
|
Cmd_AddCommand("rankremove", Rank_Remove_f);
|
|
Cmd_AddCommand("rankrefresh", Rank_Refresh_f);
|
|
|
|
Cmd_AddCommand("rankrconlevel", Rank_RCon_f);
|
|
|
|
Cmd_AddCommand("rankadd", Rank_AddUser_f);
|
|
Cmd_AddCommand("adduser", Rank_AddUser_f);
|
|
Cmd_AddCommand("setpass", Rank_SetPass_f);
|
|
|
|
Cvar_Register(&rank_autoadd, rank_cvargroup);
|
|
Cvar_Register(&rank_needlogin, rank_cvargroup);
|
|
Cvar_Register(&rank_filename, rank_cvargroup);
|
|
}
|
|
void Rank_Flush (void) //new game dir?
|
|
{
|
|
if (rankfile)
|
|
{
|
|
Rank_Refresh_f();
|
|
if (!rankfile)
|
|
return;
|
|
fclose(rankfile);
|
|
rankfile=NULL;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|