mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-28 14:41:57 +00:00
8dadfb4878
Cmake: Add FTE_WERROR option, defaults to true in debug builds and off in release builds (in case future compilers have issues). Cmake: Pull in libXscreensaver so we don't get interrupted by screensavers when playing demos. Make: Added `make webcl-rel` for a web build without server bloat (eg for sites focused on demo playback. Yes, this means you XantoM). fteqcc: Include the decompiler in fteqcc (non-gui) builds ('-d' arg). fteqcc: Decompiler can now mostly handle hexen2 mods without any unknown opcodes. Allow ezHud and OpenSSL to be compiled as in-engine plugins, potentially for web and windows ports respectively. Web: Fix support for ogg vorbis. Add support for voip. Web: Added basic support for WebXR. QTV: Don't try seeking on unseekable qtv streams. Don't spam when developer 1 is set. QTV: add support for some eztv extensions. MVD: added hack to use ktx's vweps in mvd where mvdsv doesn't bother to record the info. qwfwd: hack around a hack in qwfwd, allowing it to work again. recording: favour qwd in single player, instead of mvd. Protocol: reduce client memory used for precache names. Bump maximum precache counts - some people are just abusive, yes you Orl. hexen2: add enough clientside protocol compat to play the demo included with h2mp. lacks effects. in_xflip: restored this setting. fs_hidesyspaths: new cvar, defaults to enabled so you won't find your username or whatever turning up in screenshots or the like. change it to 0 before debuging stuff eg via 'path'. gl_overbright_models: Added cvar to match QS. netchan: Added MTU determination, we'll no longer fail to connect when routers stupidly drop icmp packets. Win: try a few other versions of xinput too. CSQC: Added a CSQC_GenerateMaterial function, to give the csqc a chance to generate custom materials. MenuQC: Added support for the skeletal objects API.
948 lines
19 KiB
C
948 lines
19 KiB
C
#include "quakedef.h"
|
|
|
|
#ifndef CLIENTONLY
|
|
//FIXME: this is shitty old code.
|
|
//possible improvements: using a hash table for player names for faster logons
|
|
//threading logins
|
|
//using a real database...
|
|
|
|
#ifdef SVRANKING
|
|
|
|
typedef struct {
|
|
int ident;
|
|
int version;
|
|
int usedslots;
|
|
int leader;
|
|
int freeslot;
|
|
} rankfileheader_t;
|
|
|
|
//endian
|
|
#define NOENDIAN
|
|
#ifdef NOENDIAN
|
|
#define swaplong(l) l
|
|
#define swapfloat(f) f
|
|
#else
|
|
#define swaplong LittleLong
|
|
#define swapfloat LittleFloat
|
|
#endif
|
|
|
|
rankfileheader_t rankfileheader;
|
|
vfsfile_t *rankfile;
|
|
|
|
cvar_t rank_autoadd = CVARD("rank_autoadd", "1", "Automatically register players into the ranking system");
|
|
cvar_t rank_needlogin = CVARD("rank_needlogin", "0", "If set to 1, prohibits players from joining if they're not yet registered. This will require an external mechanism to register users, presumably via rcon.");
|
|
cvar_t rank_filename = CVARD("rank_filename", "", "Specifies which file to use as a rankings database. Enables the ranking system if set.");
|
|
cvar_t rank_parms_first = CVARD("rank_parms_first", "0", "Mod setting: first parm saved");
|
|
cvar_t rank_parms_last = CVARD("rank_parms_last", "31", "Mod setting: the index of the last parm to be saved. Clamped to 32.");
|
|
char rank_cvargroup[] = "server rankings";
|
|
|
|
#define RANKFILE_VERSION ((NUM_RANK_SPAWN_PARMS==32)?0:0x00000001)
|
|
#define RANKFILE_IDENT (('R'<<0)|('A'<<8)|('N'<<16)|('K'<<24))
|
|
|
|
static void READ_PLAYERSTATS(int x, rankstats_t *os)
|
|
{
|
|
#ifndef NOENDIAN
|
|
int i;
|
|
#endif
|
|
size_t result;
|
|
|
|
VFS_SEEK(rankfile, sizeof(rankfileheader_t)+sizeof(rankheader_t)+((x-1)*sizeof(rankinfo_t)));
|
|
result = VFS_READ(rankfile, os, sizeof(rankstats_t));
|
|
|
|
if (result != sizeof(rankstats_t))
|
|
Con_Printf("READ_PLAYERSTATS() fread: expected %lu, result was %u\n",(long unsigned int)sizeof(rankstats_t),(unsigned int)result);
|
|
|
|
#ifndef NOENDIAN
|
|
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);
|
|
#endif
|
|
}
|
|
|
|
static void WRITE_PLAYERSTATS(int x, rankstats_t *os)
|
|
{
|
|
#ifdef NOENDIAN
|
|
VFS_SEEK(rankfile, sizeof(rankfileheader_t)+sizeof(rankheader_t)+((x-1)*sizeof(rankinfo_t)));
|
|
VFS_WRITE(rankfile, os, sizeof(rankstats_t));
|
|
#else
|
|
rankstats_t ns;
|
|
int i;
|
|
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);
|
|
|
|
VFS_SEEK(rankfile, sizeof(rankfileheader_t)+sizeof(rankheader_t)+((x-1)*sizeof(rankinfo_t)));
|
|
VFS_WRITE(rankfile, &ns, sizeof(rankstats_t));
|
|
#endif
|
|
}
|
|
|
|
static void READ_PLAYERHEADER(int x, rankheader_t *oh)
|
|
{
|
|
size_t result;
|
|
|
|
VFS_SEEK(rankfile, sizeof(rankfileheader_t)+((x-1)*sizeof(rankinfo_t)));
|
|
|
|
result = VFS_READ(rankfile, oh, sizeof(rankheader_t));
|
|
|
|
if (result != sizeof(rankheader_t))
|
|
Con_Printf("READ_PLAYERHEADER() fread: expected %lu, result was %u\n",(long unsigned int)sizeof(rankheader_t),(unsigned int)result);
|
|
|
|
#ifndef NOENDIAN
|
|
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);
|
|
#endif
|
|
}
|
|
|
|
static void WRITE_PLAYERHEADER(int x, rankheader_t *oh)
|
|
{
|
|
#ifdef NOENDIAN
|
|
VFS_SEEK(rankfile, sizeof(rankfileheader_t)+((x-1)*sizeof(rankinfo_t)));
|
|
VFS_WRITE(rankfile, &oh, sizeof(rankheader_t));
|
|
#else
|
|
rankheader_t nh;
|
|
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);
|
|
|
|
VFS_SEEK(rankfile, sizeof(rankfileheader_t)+((x-1)*sizeof(rankinfo_t)));
|
|
VFS_WRITE(rankfile, &nh, sizeof(rankheader_t));
|
|
#endif
|
|
}
|
|
|
|
static void READ_PLAYERINFO(int x, rankinfo_t *inf)
|
|
{
|
|
READ_PLAYERHEADER(x, &inf->h);
|
|
READ_PLAYERSTATS(x, &inf->s);
|
|
}
|
|
|
|
static void 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);
|
|
|
|
VFS_SEEK(rankfile, 0);
|
|
VFS_WRITE(rankfile, &nh, sizeof(rankfileheader_t));
|
|
}
|
|
//#define WRITEHEADER() {fseek(rankfile, 0, SEEK_SET);fwrite(&rankfileheader, 1, sizeof(rankfileheader_t), rankfile);}
|
|
|
|
#define NAMECMP(saved, against) Q_strncasecmp(saved, against, 31)
|
|
|
|
qboolean Rank_OpenRankings(void)
|
|
{
|
|
size_t result;
|
|
qboolean created;
|
|
if (!rankfile)
|
|
{
|
|
if (!*rank_filename.string)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
rankfile = FS_OpenVFS(rank_filename.string, "r+b", FS_GAMEONLY);
|
|
if (!rankfile) //hmm... try creating
|
|
{
|
|
rankfile = FS_OpenVFS(rank_filename.string, "w+b", FS_GAMEONLY);
|
|
created = true;
|
|
}
|
|
else
|
|
created = false;
|
|
if (!rankfile)
|
|
return false; //couldn't open file.
|
|
|
|
memset(&rankfileheader, 0, sizeof(rankfileheader));
|
|
|
|
VFS_SEEK(rankfile, 0);
|
|
result = VFS_READ(rankfile, &rankfileheader, sizeof(rankfileheader_t));
|
|
|
|
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, rank_filename.string);
|
|
|
|
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 " FULLENGINENAME "\n", rankfileheader.version, RANKFILE_VERSION);
|
|
VFS_CLOSE(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);
|
|
VFS_FLUSH(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.
|
|
VFS_FLUSH(rankfile);
|
|
return;
|
|
}
|
|
if (!rh.next)
|
|
{
|
|
LINKAFTER(nid, id, &nh); //Bum. We got to the end of the list and we are the WORST player!
|
|
VFS_FLUSH(rankfile);
|
|
return;
|
|
}
|
|
nid = rh.next;
|
|
}
|
|
}
|
|
|
|
int Rank_GetPlayerID(char *guid, const 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);
|
|
|
|
VFS_FLUSH(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);
|
|
|
|
VFS_FLUSH(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));
|
|
char fixed[80];
|
|
|
|
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, fixed, sizeof(fixed));
|
|
name = fixed;
|
|
|
|
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);
|
|
|
|
VFS_FLUSH(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;
|
|
#if NUM_RANK_SPAWN_PARMS>32
|
|
rs.created = rs.lastseen = time(NULL);
|
|
#endif
|
|
WRITE_PLAYERSTATS(id, &rs);
|
|
|
|
Rank_SetPlayerStats(id, &rs);
|
|
|
|
VFS_FLUSH(rankfile);
|
|
}
|
|
|
|
void Rank_SetPass_f (void)
|
|
{
|
|
rankheader_t rh;
|
|
char *name = Cmd_Argv(1);
|
|
int newpass = atoi(Cmd_Argv(2));
|
|
char fixed[80];
|
|
|
|
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, fixed, sizeof(fixed));
|
|
name = fixed;
|
|
|
|
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;
|
|
char fixed[80];
|
|
|
|
int id;
|
|
|
|
if (!Rank_OpenRankings())
|
|
{
|
|
Con_Printf("Failed to open rankings file.\n");
|
|
return 0;
|
|
}
|
|
|
|
SV_FixupName(name, fixed, sizeof(fixed));
|
|
name = fixed;
|
|
|
|
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;
|
|
|
|
vfsfile_t *outfile;
|
|
|
|
if (!Rank_OpenRankings())
|
|
{
|
|
Con_Printf("Failed to open rankings file.\n");
|
|
return;
|
|
}
|
|
|
|
outfile = FS_OpenVFS("list.txt", "wb", FS_GAMEONLY);
|
|
if (!outfile)
|
|
{
|
|
Con_Printf("Couldn't open list.txt\n");
|
|
return;
|
|
}
|
|
|
|
VFS_PRINTF(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);
|
|
|
|
VFS_PRINTF(outfile, "%5i: %32s, %5i %5i\r\n", num, ri.h.name, ri.s.kills, ri.s.deaths);
|
|
|
|
num++;
|
|
id = ri.h.next;
|
|
}
|
|
|
|
VFS_CLOSE(outfile);
|
|
}
|
|
|
|
void Rank_RemoveID_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();
|
|
VFS_FLUSH(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 = Q_strlwr(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 (wildcmp(match, Q_strlwr(ri.h.name)))
|
|
{
|
|
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<svs.allocated_client_slots ; 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)
|
|
{
|
|
VFS_CLOSE(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");
|
|
|
|
VFS_FLUSH(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_RemoveID_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);
|
|
|
|
Cvar_Register(&rank_parms_first, rank_cvargroup);
|
|
Cvar_Register(&rank_parms_last, rank_cvargroup);
|
|
}
|
|
void Rank_Flush (void) //new game dir?
|
|
{
|
|
if (rankfile)
|
|
{
|
|
Rank_Refresh_f();
|
|
if (!rankfile)
|
|
return;
|
|
VFS_CLOSE(rankfile);
|
|
rankfile=NULL;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|