implement status command for nq clients that expect something to be printed. IP addresses are withheld.

fix stats issue.
support menuqc-based loading screens.
fixed 2d render-to-texture issues.
(finally) throttle 'connection lost or aborted' messages. report the ip address too, because we can.
begun work on nq-style player ip-logging. ip addresses are collected now, but there's still no actual database code yet.
rewrote engine auto-update. now part of the updates menu instead of system-specific special-case code. Still requires a system function to actually invoke the updated engine however.
added sdl audio capture support (requires sdl 2.0.5).
treat q_version like f_version etc. respond to both.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5046 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2017-01-24 10:27:39 +00:00
parent 9055d674eb
commit c68cbfec24
36 changed files with 855 additions and 631 deletions

View File

@ -41,6 +41,7 @@ static void CL_ForceStopDownload (qboolean finish);
// references them even when on a unix system. // references them even when on a unix system.
qboolean noclip_anglehack; // remnant from old quake qboolean noclip_anglehack; // remnant from old quake
int startuppending;
void Host_FinishLoading(void); void Host_FinishLoading(void);
@ -737,6 +738,7 @@ void CL_CheckForResend (void)
int contype = 0; int contype = 0;
qboolean keeptrying = true; qboolean keeptrying = true;
char *host; char *host;
extern int r_blockvidrestart;
#ifndef CLIENTONLY #ifndef CLIENTONLY
if (!cls.state && (!connectinfo.trying || sv.state != ss_clustermode) && sv.state) if (!cls.state && (!connectinfo.trying || sv.state != ss_clustermode) && sv.state)
@ -970,6 +972,8 @@ void CL_CheckForResend (void)
if (!connectinfo.trying) if (!connectinfo.trying)
return; return;
if (startuppending || r_blockvidrestart)
return; //don't send connect requests until we've actually initialised fully. this isn't a huge issue, but makes the startup prints a little more sane.
/* /*
#ifdef NQPROT #ifdef NQPROT
@ -4952,7 +4956,6 @@ Runs all active servers
extern cvar_t cl_netfps; extern cvar_t cl_netfps;
extern cvar_t cl_sparemsec; extern cvar_t cl_sparemsec;
int startuppending;
void CL_StartCinematicOrMenu(void); void CL_StartCinematicOrMenu(void);
int nopacketcount; int nopacketcount;
void SNDDMA_SetUnderWater(qboolean underwater); void SNDDMA_SetUnderWater(qboolean underwater);
@ -5363,7 +5366,7 @@ void CL_StartCinematicOrMenu(void)
{ {
COM_MainThreadWork(); COM_MainThreadWork();
if (FS_DownloadingPackage()) if (com_installer && FS_DownloadingPackage())
{ {
startuppending = true; startuppending = true;
return; return;
@ -5681,6 +5684,7 @@ Host_Init
*/ */
void Host_Init (quakeparms_t *parms) void Host_Init (quakeparms_t *parms)
{ {
char engineupdated[MAX_OSPATH];
int man; int man;
com_parseutf8.ival = 1; //enable utf8 parsing even before cvars are registered. com_parseutf8.ival = 1; //enable utf8 parsing even before cvars are registered.
@ -5704,9 +5708,31 @@ void Host_Init (quakeparms_t *parms)
COM_ParsePlusSets(false); COM_ParsePlusSets(false);
Cbuf_Init (); Cbuf_Init ();
Cmd_Init (); Cmd_Init ();
COM_Init ();
//we have enough of the filesystem inited now that we can read the package list and figure out which engine was last installed.
if (PM_FindUpdatedEngine(engineupdated, sizeof(engineupdated)))
{
PM_Shutdown(); //will restart later as needed, but we need to be sure that no files are open or anything.
if (Sys_EngineWasUpdated(engineupdated))
{
COM_Shutdown();
Cmd_Shutdown();
Sys_Shutdown();
Con_Shutdown();
Memory_DeInit();
Cvar_Shutdown();
Sys_Quit();
return;
}
}
V_Init (); V_Init ();
NET_Init (); NET_Init ();
COM_Init ();
#ifdef PLUGINS
Plug_Initialise(false);
#endif
#ifdef Q2BSPS #ifdef Q2BSPS
CM_Init(); CM_Init();
#endif #endif
@ -5784,10 +5810,7 @@ to run quit through here before the final handoff to the sys code.
void Host_Shutdown(void) void Host_Shutdown(void)
{ {
if (!host_initialized) if (!host_initialized)
{
Sys_Printf ("recursive shutdown\n");
return; return;
}
host_initialized = false; host_initialized = false;
#ifdef WEBCLIENT #ifdef WEBCLIENT

View File

@ -7371,18 +7371,19 @@ static char *CLNQ_ParseProQuakeMessage (char *s)
return s; return s;
} }
static enum {
CLNQPP_NONE,
CLNQPP_PINGS
} cl_nqparseprint;
qboolean CLNQ_ParseNQPrints(char *s) qboolean CLNQ_ParseNQPrints(char *s)
{ {
int i; int i;
char *start = s; char *start = s;
if (cl_nqparseprint == CLNQPP_PINGS) if (!strcmp(s, "Client ping times:\n"))
{
cl.nqparseprint = CLNQPP_PINGS;
return true;
}
else if (cl.nqparseprint == CLNQPP_PINGS)
{ {
char *pingstart; char *pingstart;
cl_nqparseprint = CLNQPP_NONE; cl.nqparseprint = CLNQPP_NONE;
while(*s == ' ') while(*s == ' ')
s++; s++;
pingstart = s; pingstart = s;
@ -7414,7 +7415,7 @@ qboolean CLNQ_ParseNQPrints(char *s)
{ {
cl.players[i].ping = atoi(pingstart); cl.players[i].ping = atoi(pingstart);
} }
cl_nqparseprint = CLNQPP_PINGS; cl.nqparseprint = CLNQPP_PINGS;
return true; return true;
} }
} }
@ -7422,10 +7423,51 @@ qboolean CLNQ_ParseNQPrints(char *s)
s = start; s = start;
} }
if (!strcmp(s, "Client ping times:\n")) if (!strncmp(s, "host: ", 9))
{ {
cl_nqparseprint = CLNQPP_PINGS; cl.nqparseprint = CLNQPP_STATUS;
return true; return cls.nqexpectingstatusresponse;
}
else if (cl.nqparseprint == CLNQPP_STATUS)
{
if (!strncmp(s, "players: ", 9))
{
cl.nqparseprint = CLNQPP_STATUSPLAYER;
return cls.nqexpectingstatusresponse;
}
else if (strchr(s, ':'))
return cls.nqexpectingstatusresponse;
cl.nqparseprint = CLNQPP_NONE; //error of some kind...
cls.nqexpectingstatusresponse = false;
}
if (cl.nqparseprint == CLNQPP_STATUSPLAYER)
{
if (*s == '#')
{
cl.nqparseprint = CLNQPP_STATUSPLAYERIP;
cl.nqparseprintplayer = atoi(s+1)-1;
if (cl.nqparseprintplayer >= 0 && cl.nqparseprintplayer < cl.allocated_client_slots)
return cls.nqexpectingstatusresponse;
}
cl.nqparseprint = CLNQPP_NONE; //error of some kind...
cls.nqexpectingstatusresponse = false;
}
if (cl.nqparseprint == CLNQPP_STATUSPLAYERIP)
{
if (!strncmp(s, " ", 3))
{
while(*s == ' ')
s++;
COM_ParseOut(s, cl.players[cl.nqparseprintplayer].ip, sizeof(cl.players[cl.nqparseprintplayer].ip));
IPLog_Add(cl.players[cl.nqparseprintplayer].ip, cl.players[cl.nqparseprintplayer].name);
if (*cl.players[cl.nqparseprintplayer].ip != '[' && *cl.players[cl.nqparseprintplayer].ip < '0' && *cl.players[cl.nqparseprintplayer].ip > '9')
*cl.players[cl.nqparseprintplayer].ip = 0; //non-numeric addresses are not useful.
cl.nqparseprint = CLNQPP_STATUSPLAYER;
return cls.nqexpectingstatusresponse;
}
cl.nqparseprint = CLNQPP_NONE; //error of some kind...
cls.nqexpectingstatusresponse = false;
} }
return false; return false;
@ -7740,6 +7782,8 @@ void CLNQ_ParseServerMessage (void)
if (*cl.players[i].name) if (*cl.players[i].name)
cl.players[i].userid = i+1; cl.players[i].userid = i+1;
Info_SetValueForKey(cl.players[i].userinfo, "name", cl.players[i].name, sizeof(cl.players[i].userinfo)); Info_SetValueForKey(cl.players[i].userinfo, "name", cl.players[i].name, sizeof(cl.players[i].userinfo));
if (!cl.nqplayernamechanged)
cl.nqplayernamechanged = realtime+2;
} }
break; break;

View File

@ -1706,7 +1706,11 @@ void SCR_DrawLoading (qboolean opaque)
int h2depth; int h2depth;
if (CSQC_UseGamecodeLoadingScreen()) if (CSQC_UseGamecodeLoadingScreen())
return; return; //will be drawn as part of the regular screen updates
#ifdef MENU_DAT
if (MP_UsingGamecodeLoadingScreen())
return; //menuqc should have just drawn whatever overlays it wanted.
#endif
//int mtype = M_GameType(); //unused variable //int mtype = M_GameType(); //unused variable
y = vid.height/2; y = vid.height/2;

View File

@ -169,6 +169,8 @@ typedef struct player_info_s
int ping; int ping;
qbyte pl; qbyte pl;
char ip[128];
struct struct
{ {
float time; //invalid if too old. float time; //invalid if too old.
@ -531,6 +533,7 @@ typedef struct
int language; int language;
colourised_t *colourised; colourised_t *colourised;
qboolean nqexpectingstatusresponse;
} client_static_t; } client_static_t;
extern client_static_t cls; extern client_static_t cls;
@ -917,6 +920,16 @@ typedef struct
MATCH_STANDBY, MATCH_STANDBY,
MATCH_INPROGRESS MATCH_INPROGRESS
} matchstate; } matchstate;
enum {
CLNQPP_NONE,
CLNQPP_PINGS,
CLNQPP_STATUS, //"host: *\n" ... "players: *\n\n"
CLNQPP_STATUSPLAYER, //#...\n
CLNQPP_STATUSPLAYERIP, // foobar\n
} nqparseprint;
int nqparseprintplayer;
float nqplayernamechanged;
} client_state_t; } client_state_t;
extern unsigned int cl_teamtopcolor; extern unsigned int cl_teamtopcolor;

View File

@ -746,6 +746,9 @@ void Key_DefaultLinkClicked(console_t *con, char *text, char *info)
if (i == cl.splitclients) if (i == cl.splitclients)
{ {
extern cvar_t rcon_password; extern cvar_t rcon_password;
if (*cl.players[player].ip)
Con_Footerf(con, true, "\n%s", cl.players[player].ip);
if (cl.spectator || cls.demoplayback) if (cl.spectator || cls.demoplayback)
{ {
//we're spectating, or an mvd //we're spectating, or an mvd

View File

@ -36,9 +36,9 @@ vfsfile_t *FS_GZ_DecompressWriteFilter(vfsfile_t *outfile, qboolean autoclosefil
#define PHPMIN #define PHPMIN
#endif #endif
#ifdef NOLEGACY #ifdef NOLEGACY
#define PHPLEG "&leg=0" #define PHPLEG "&leg=0&test=1"
#else #else
#define PHPLEG "&leg=1" #define PHPLEG "&leg=1&test=1"
#endif #endif
#if defined(_DEBUG) || defined(DEBUG) #if defined(_DEBUG) || defined(DEBUG)
#define PHPDBG "&dbg=1" #define PHPDBG "&dbg=1"
@ -48,11 +48,13 @@ vfsfile_t *FS_GZ_DecompressWriteFilter(vfsfile_t *outfile, qboolean autoclosefil
#ifndef SVNREVISION #ifndef SVNREVISION
#define SVNREVISION - #define SVNREVISION -
#endif #endif
#define DOWNLOADABLESARGS "ver=" STRINGIFY(SVNREVISION) PHPVK PHPGL PHPD3D PHPMIN PHPLEG PHPDBG "&arch="PLATFORM "_" ARCH_CPU_POSTFIX #define SVNREVISIONSTR STRINGIFY(SVNREVISION)
#define DOWNLOADABLESARGS "ver=" SVNREVISIONSTR PHPVK PHPGL PHPD3D PHPMIN PHPLEG PHPDBG "&arch="PLATFORM "_" ARCH_CPU_POSTFIX
extern cvar_t fs_downloads_url; extern cvar_t pm_autoupdate;
extern cvar_t pm_downloads_url;
#define INSTALLEDFILES "installed.lst" //the file that resides in the quakedir (saying what's installed). #define INSTALLEDFILES "installed.lst" //the file that resides in the quakedir (saying what's installed).
//installed native okay [previously manually installed, or has no a qhash] //installed native okay [previously manually installed, or has no a qhash]
@ -70,7 +72,7 @@ extern cvar_t fs_downloads_url;
//!installed * missing [simply not installed] //!installed * missing [simply not installed]
#define DPF_INSTALLED 0x01 #define DPF_ENABLED 0x01
#define DPF_NATIVE 0x02 //appears to be installed properly #define DPF_NATIVE 0x02 //appears to be installed properly
#define DPF_CACHED 0x04 //appears to be installed in their dlcache dir (and has a qhash) #define DPF_CACHED 0x04 //appears to be installed in their dlcache dir (and has a qhash)
#define DPF_CORRUPT 0x08 //will be deleted before it can be changed #define DPF_CORRUPT 0x08 //will be deleted before it can be changed
@ -82,7 +84,9 @@ extern cvar_t fs_downloads_url;
#define DPF_ENGINE 0x100 //engine update. replaces old autoupdate mechanism #define DPF_ENGINE 0x100 //engine update. replaces old autoupdate mechanism
#define DPF_PURGE 0x200 //package should be completely removed (ie: the dlcache dir too). if its still marked then it should be reinstalled anew. available on cached or corrupt packages, implied by native. #define DPF_PURGE 0x200 //package should be completely removed (ie: the dlcache dir too). if its still marked then it should be reinstalled anew. available on cached or corrupt packages, implied by native.
#define DPF_MANIFEST 0x400 //package was named by the manifest, and should only be uninstalled after a warning. #define DPF_MANIFEST 0x400 //package was named by the manifest, and should only be uninstalled after a warning.
#define DPF_TESTING 0x800 //package is provided on a testing/trial basis, and will only be selected/listed if autoupdates are configured to allow it.
#define DPF_PRESENT (DPF_NATIVE|DPF_CACHED)
//pak.lst //pak.lst
//priories <0 //priories <0
//pakX //pakX
@ -127,7 +131,7 @@ typedef struct package_s {
struct package_s *alternative; //alternative (hidden) forms of this package. struct package_s *alternative; //alternative (hidden) forms of this package.
unsigned int trymirrors; unsigned int trymirrors;
char *mirror[8]; char *mirror[8]; //FIXME: move to two types of dep...
char gamedir[16]; char gamedir[16];
enum fs_relative fsroot; enum fs_relative fsroot;
char version[16]; char version[16];
@ -156,6 +160,8 @@ typedef struct package_s {
DEP_FILECONFLICT, //don't install if this file already exists. DEP_FILECONFLICT, //don't install if this file already exists.
DEP_REQUIRE, DEP_REQUIRE,
DEP_RECOMMEND, //like depend, but uninstalling will not bubble. DEP_RECOMMEND, //like depend, but uninstalling will not bubble.
// DEP_MIRROR,
// DEP_FAILEDMIRROR,
DEP_FILE DEP_FILE
} dtype; } dtype;
@ -237,6 +243,24 @@ static void PM_FreePackage(package_t *p)
Z_Free(p); Z_Free(p);
} }
qboolean PM_PurgeOnDisable(package_t *p)
{
//corrupt packages must be purged
if (p->flags & DPF_CORRUPT)
return true;
//engine updates can be present and not enabled
if (p->flags & DPF_ENGINE)
return false;
//hashed packages can also be present and not enabled, but only if they're in the cache and not native
if (*p->gamedir && p->qhash && (p->flags & DPF_CACHED))
return false;
//FIXME: add basedir-plugins to the package manager so they can be enabled/disabled properly.
//if (p->arch)
// return false;
//all other packages must be deleted to disable them
return true;
}
//checks the status of each package //checks the status of each package
void PM_ValidatePackage(package_t *p) void PM_ValidatePackage(package_t *p)
{ {
@ -244,7 +268,7 @@ void PM_ValidatePackage(package_t *p)
struct packagedep_s *dep; struct packagedep_s *dep;
vfsfile_t *pf; vfsfile_t *pf;
p->flags &=~ (DPF_NATIVE|DPF_CACHED|DPF_CORRUPT); p->flags &=~ (DPF_NATIVE|DPF_CACHED|DPF_CORRUPT);
if (p->flags & DPF_INSTALLED) if (p->flags & DPF_ENABLED)
{ {
for (dep = p->deps; dep; dep = dep->next) for (dep = p->deps; dep; dep = dep->next)
{ {
@ -309,7 +333,7 @@ void PM_ValidatePackage(package_t *p)
{ {
if (o == p) if (o == p)
continue; continue;
if (o->flags & DPF_INSTALLED) if (o->flags & DPF_ENABLED)
{ {
if (!strcmp(p->gamedir, o->gamedir) && p->fsroot == o->fsroot) if (!strcmp(p->gamedir, o->gamedir) && p->fsroot == o->fsroot)
if (strcmp(p->name, o->name) || strcmp(p->version, o->version)) if (strcmp(p->name, o->name) || strcmp(p->version, o->version))
@ -328,7 +352,9 @@ void PM_ValidatePackage(package_t *p)
p->flags |= DPF_CACHED; p->flags |= DPF_CACHED;
else if (!o) else if (!o)
{ {
if (p->qhash) if (!PM_PurgeOnDisable(p))
p->flags |= fl;
else if (p->qhash)
{ {
char buf[8]; char buf[8];
searchpathfuncs_t *archive; searchpathfuncs_t *archive;
@ -355,7 +381,7 @@ void PM_ValidatePackage(package_t *p)
{ {
p->flags |= fl; p->flags |= fl;
if (fl&DPF_NATIVE) if (fl&DPF_NATIVE)
p->flags |= DPF_MARKED|DPF_INSTALLED; p->flags |= DPF_MARKED|DPF_ENABLED;
break; break;
} }
else else
@ -445,7 +471,8 @@ static qboolean PM_MergePackage(package_t *oldp, package_t *newp)
newp->mirror[nm] = NULL; newp->mirror[nm] = NULL;
} }
} }
oldp->flags &= ~DPF_FORGETONUNINSTALL | (newp->flags & DPF_FORGETONUNINSTALL); //these flags should only remain set if set in both.
oldp->flags &= ~(DPF_FORGETONUNINSTALL|DPF_TESTING) | (newp->flags & (DPF_FORGETONUNINSTALL|DPF_TESTING));
PM_FreePackage(newp); PM_FreePackage(newp);
return true; return true;
@ -631,7 +658,7 @@ static void PM_ParsePackageList(vfsfile_t *f, int parseflags, const char *url, c
subprefix = va("%s/%s", prefix, Cmd_Argv(2)); subprefix = va("%s/%s", prefix, Cmd_Argv(2));
else else
subprefix = Cmd_Argv(2); subprefix = Cmd_Argv(2);
PM_AddSubList(Cmd_Argv(1), subprefix, (parseflags & DPF_INSTALLED)?true:false); PM_AddSubList(Cmd_Argv(1), subprefix, (parseflags & DPF_ENABLED)?true:false);
continue; continue;
} }
if (!strcmp(Cmd_Argv(0), "set")) if (!strcmp(Cmd_Argv(0), "set"))
@ -654,6 +681,11 @@ static void PM_ParsePackageList(vfsfile_t *f, int parseflags, const char *url, c
nummirrors++; nummirrors++;
} }
} }
else if (!strcmp(Cmd_Argv(1), "updatemode"))
{
if (!(parseflags & DPF_ENABLED)) //don't use a downloaded file's version of this
Cvar_ForceSet(&pm_autoupdate, Cmd_Argv(2));
}
else else
{ {
//erk //erk
@ -682,7 +714,7 @@ static void PM_ParsePackageList(vfsfile_t *f, int parseflags, const char *url, c
int i; int i;
if (version > 2) if (version > 2)
flags &= ~DPF_INSTALLED; flags &= ~DPF_ENABLED;
p = Z_Malloc(sizeof(*p)); p = Z_Malloc(sizeof(*p));
for (i = 1; i < argc; i++) for (i = 1; i < argc; i++)
@ -739,10 +771,12 @@ static void PM_ParsePackageList(vfsfile_t *f, int parseflags, const char *url, c
PM_AddDep(p, DEP_FILECONFLICT, arg+13); PM_AddDep(p, DEP_FILECONFLICT, arg+13);
else if (!strncmp(arg, "recommend=", 10)) else if (!strncmp(arg, "recommend=", 10))
PM_AddDep(p, DEP_RECOMMEND, arg+10); PM_AddDep(p, DEP_RECOMMEND, arg+10);
else if (!strncmp(arg, "test=", 5))
flags |= DPF_TESTING;
else if (!strncmp(arg, "stale=", 6) && version==2) else if (!strncmp(arg, "stale=", 6) && version==2)
flags &= ~DPF_INSTALLED; flags &= ~DPF_ENABLED;
else if (!strncmp(arg, "installed=", 6) && version>2) else if (!strncmp(arg, "installed=", 6) && version>2)
flags |= parseflags & DPF_INSTALLED; flags |= parseflags & DPF_ENABLED;
else else
{ {
Con_DPrintf("Unknown package property\n"); Con_DPrintf("Unknown package property\n");
@ -868,7 +902,7 @@ static void PM_ParsePackageList(vfsfile_t *f, int parseflags, const char *url, c
{ {
if (!Q_strcasecmp(p->arch, THISENGINE)) if (!Q_strcasecmp(p->arch, THISENGINE))
{ {
if (Sys_GetAutoUpdateSetting() == UPD_UNSUPPORTED) if (!Sys_EngineCanUpdate())
p->flags |= DPF_HIDDEN; p->flags |= DPF_HIDDEN;
else else
p->flags |= DPF_ENGINE; p->flags |= DPF_ENGINE;
@ -891,7 +925,7 @@ static void PM_ParsePackageList(vfsfile_t *f, int parseflags, const char *url, c
p->flags |= DPF_HIDDEN; p->flags |= DPF_HIDDEN;
} }
} }
if (p->flags & DPF_INSTALLED) if (p->flags & DPF_ENABLED)
p->flags |= DPF_MARKED; p->flags |= DPF_MARKED;
PM_InsertPackage(p); PM_InsertPackage(p);
@ -913,7 +947,7 @@ void PM_LoadPackages(searchpath_t **oldpaths, const char *parent_pure, const cha
loadedinstalled = true; loadedinstalled = true;
if (f) if (f)
{ {
PM_ParsePackageList(f, DPF_FORGETONUNINSTALL|DPF_INSTALLED, NULL, ""); PM_ParsePackageList(f, DPF_FORGETONUNINSTALL|DPF_ENABLED, NULL, "");
VFS_CLOSE(f); VFS_CLOSE(f);
} }
} }
@ -924,14 +958,14 @@ void PM_LoadPackages(searchpath_t **oldpaths, const char *parent_pure, const cha
pri = maxpri; pri = maxpri;
for (p = availablepackages; p; p = p->next) for (p = availablepackages; p; p = p->next)
{ {
if ((p->flags & DPF_INSTALLED) && p->qhash && p->priority>=minpri&&p->priority<pri && !Q_strcasecmp(parent_pure, p->gamedir)) if ((p->flags & DPF_ENABLED) && p->qhash && p->priority>=minpri&&p->priority<pri && !Q_strcasecmp(parent_pure, p->gamedir))
pri = p->priority; pri = p->priority;
} }
minpri = pri+1; minpri = pri+1;
for (p = availablepackages; p; p = p->next) for (p = availablepackages; p; p = p->next)
{ {
if ((p->flags & DPF_INSTALLED) && p->qhash && p->priority==pri && !Q_strcasecmp(parent_pure, p->gamedir)) if ((p->flags & DPF_ENABLED) && p->qhash && p->priority==pri && !Q_strcasecmp(parent_pure, p->gamedir))
{ {
for (d = p->deps; d; d = d->next) for (d = p->deps; d; d = d->next)
{ {
@ -950,7 +984,7 @@ void PM_Shutdown(void)
{ {
//free everything... //free everything...
loadedinstalled = false; loadedinstalled = false;
fs_downloads_url.modified = false; pm_downloads_url.modified = false;
downloadablessequence++; downloadablessequence++;
@ -984,7 +1018,7 @@ static void PM_PreparePackageList(void)
loadedinstalled = true; loadedinstalled = true;
if (f) if (f)
{ {
PM_ParsePackageList(f, DPF_FORGETONUNINSTALL|DPF_INSTALLED, NULL, ""); PM_ParsePackageList(f, DPF_FORGETONUNINSTALL|DPF_ENABLED, NULL, "");
VFS_CLOSE(f); VFS_CLOSE(f);
} }
} }
@ -1030,7 +1064,7 @@ static void PM_RevertChanges(void)
package_t *p; package_t *p;
for (p = availablepackages; p; p = p->next) for (p = availablepackages; p; p = p->next)
{ {
if (p->flags & DPF_INSTALLED) if (p->flags & DPF_ENABLED)
p->flags |= DPF_MARKED; p->flags |= DPF_MARKED;
else else
p->flags &= ~DPF_MARKED; p->flags &= ~DPF_MARKED;
@ -1181,8 +1215,10 @@ static unsigned int PM_MarkUpdates (void)
{ {
if ((p->flags & DPF_ENGINE) && !(p->flags & DPF_HIDDEN)) if ((p->flags & DPF_ENGINE) && !(p->flags & DPF_HIDDEN))
{ {
if ((p->flags & DPF_MARKED) || !e || strcmp(e->version, p->version) < 0) if (!(p->flags & DPF_TESTING) || pm_autoupdate.ival >= UPD_TESTING)
e = p; if (!e || strcmp(e->version, p->version) < 0) //package must be more recent than the previously found engine
if (strcmp(SVNREVISIONSTR, "-") && strcmp(SVNREVISIONSTR, p->version) < 0) //package must be more recent than the current engine too, there's no point auto-updating to an older revision.
e = p;
} }
if (p->flags & DPF_MARKED) if (p->flags & DPF_MARKED)
{ {
@ -1191,11 +1227,12 @@ static unsigned int PM_MarkUpdates (void)
{ {
if (p == o || (o->flags & DPF_HIDDEN)) if (p == o || (o->flags & DPF_HIDDEN))
continue; continue;
if (!strcmp(o->name, p->name) && !strcmp(o->arch?o->arch:"", p->arch?p->arch:"") && strcmp(o->version, p->version) > 0) if (!(p->flags & DPF_TESTING) || pm_autoupdate.ival >= UPD_TESTING)
{ if (!strcmp(o->name, p->name) && !strcmp(o->arch?o->arch:"", p->arch?p->arch:"") && strcmp(o->version, p->version) > 0)
if (!b || strcmp(b->version, o->version) < 0) {
b = o; if (!b || strcmp(b->version, o->version) < 0)
} b = o;
}
} }
if (b) if (b)
@ -1208,7 +1245,7 @@ static unsigned int PM_MarkUpdates (void)
} }
if (e && !(e->flags & DPF_MARKED)) if (e && !(e->flags & DPF_MARKED))
{ {
if (Sys_GetAutoUpdateSetting() >= UPD_STABLE) if (pm_autoupdate.ival >= UPD_STABLE)
{ {
changecount++; changecount++;
PM_MarkPackage(e); PM_MarkPackage(e);
@ -1224,7 +1261,7 @@ static void PM_PrintChanges(void)
package_t *p; package_t *p;
for (p = availablepackages; p; p=p->next) for (p = availablepackages; p; p=p->next)
{ {
if (!(p->flags & DPF_MARKED) != !(p->flags & DPF_INSTALLED) || (p->flags & DPF_PURGE)) if (!(p->flags & DPF_MARKED) != !(p->flags & DPF_ENABLED) || (p->flags & DPF_PURGE))
{ {
changes++; changes++;
if (p->flags & DPF_MARKED) if (p->flags & DPF_MARKED)
@ -1340,16 +1377,15 @@ static void PM_ListDownloaded(struct dl_download *dl)
static void PM_UpdatePackageList(qboolean autoupdate, int retry) static void PM_UpdatePackageList(qboolean autoupdate, int retry)
{ {
unsigned int i; unsigned int i;
int setting;
if (retry>1 || fs_downloads_url.modified) if (retry>1 || pm_downloads_url.modified)
PM_Shutdown(); PM_Shutdown();
PM_PreparePackageList(); PM_PreparePackageList();
//make sure our sources are okay. //make sure our sources are okay.
if (*fs_downloads_url.string) if (*pm_downloads_url.string)
PM_AddSubList(fs_downloads_url.string, "", true); PM_AddSubList(pm_downloads_url.string, "", true);
doautoupdate |= autoupdate; doautoupdate |= autoupdate;
@ -1362,11 +1398,7 @@ static void PM_UpdatePackageList(qboolean autoupdate, int retry)
if (downloadablelist[i].curdl) if (downloadablelist[i].curdl)
continue; continue;
setting = Sys_GetAutoUpdateSetting(); downloadablelist[i].curdl = HTTP_CL_Get(va("%s%s"DOWNLOADABLESARGS, downloadablelist[i].url, strchr(downloadablelist[i].url,'?')?"&":"?"), NULL, PM_ListDownloaded);
// if (setting == UPD_UNSUPPORTED)
// setting = autoupdatesetting+1;
downloadablelist[i].curdl = HTTP_CL_Get(va("%s%s"DOWNLOADABLESARGS"%s", downloadablelist[i].url, strchr(downloadablelist[i].url,'?')?"&":"?", (setting>=UPD_TESTING)?"test=1":""), NULL, PM_ListDownloaded);
if (downloadablelist[i].curdl) if (downloadablelist[i].curdl)
{ {
downloadablelist[i].curdl->user_num = i; downloadablelist[i].curdl->user_num = i;
@ -1437,6 +1469,9 @@ static void PM_WriteInstalledPackages(void)
s = "version 2\n"; s = "version 2\n";
VFS_WRITE(f, s, strlen(s)); VFS_WRITE(f, s, strlen(s));
s = va("set updatemode \"%s\"\n", pm_autoupdate.string);
VFS_WRITE(f, s, strlen(s));
for (i = 0; i < numdownloadablelists; i++) for (i = 0; i < numdownloadablelists; i++)
{ {
if (downloadablelist[i].save) if (downloadablelist[i].save)
@ -1448,12 +1483,12 @@ static void PM_WriteInstalledPackages(void)
for (p = availablepackages; p ; p=p->next) for (p = availablepackages; p ; p=p->next)
{ {
if (p->flags & (DPF_CACHED|DPF_INSTALLED)) if (p->flags & (DPF_PRESENT|DPF_ENABLED))
{ {
char buf[8192]; char buf[8192];
buf[0] = 0; buf[0] = 0;
COM_QuotedString(va("%s%s", p->category, p->name), buf, sizeof(buf), false); COM_QuotedString(va("%s%s", p->category, p->name), buf, sizeof(buf), false);
if (p->flags & DPF_INSTALLED) if (p->flags & DPF_ENABLED)
{ //v3+ { //v3+
// Q_strncatz(buf, " ", sizeof(buf)); // Q_strncatz(buf, " ", sizeof(buf));
// COM_QuotedConcat(va("installed=1"), buf, sizeof(buf)); // COM_QuotedConcat(va("installed=1"), buf, sizeof(buf));
@ -1521,7 +1556,7 @@ static void PM_WriteInstalledPackages(void)
{ {
Q_strncatz(buf, " ", sizeof(buf)); Q_strncatz(buf, " ", sizeof(buf));
COM_QuotedConcat(va("file=%s", dep->name), buf, sizeof(buf)); COM_QuotedConcat(va("file=%s", dep->name), buf, sizeof(buf));
if ((p->flags & DPF_ENGINE) && (!e || strcmp(e->version, p->version) < 0)) if ((p->flags & DPF_ENABLED) && (p->flags & DPF_ENGINE) && (!e || strcmp(e->version, p->version) < 0))
{ {
e = p; e = p;
ef = dep; ef = dep;
@ -1549,6 +1584,12 @@ static void PM_WriteInstalledPackages(void)
} }
} }
if (p->flags & DPF_TESTING)
{
Q_strncatz(buf, " ", sizeof(buf));
COM_QuotedConcat("test=1", buf, sizeof(buf));
}
buf[sizeof(buf)-2] = 0; //just in case. buf[sizeof(buf)-2] = 0; //just in case.
Q_strncatz(buf, "\n", sizeof(buf)); Q_strncatz(buf, "\n", sizeof(buf));
VFS_WRITE(f, buf, strlen(buf)); VFS_WRITE(f, buf, strlen(buf));
@ -1586,7 +1627,7 @@ static int QDECL PM_ExtractFiles(const char *fname, qofs_t fsize, time_t mtime,
else else
n = fname; n = fname;
if (FS_WriteFile(n, f, loc.len, p->fsroot)) if (FS_WriteFile(n, f, loc.len, p->fsroot))
p->flags |= DPF_NATIVE|DPF_INSTALLED; p->flags |= DPF_NATIVE|DPF_ENABLED;
free(f); free(f);
//keep track of the installed files, so we can delete them properly after. //keep track of the installed files, so we can delete them properly after.
@ -1600,6 +1641,7 @@ static void PM_StartADownload(void);
//callback from PM_StartADownload //callback from PM_StartADownload
static void PM_Download_Got(struct dl_download *dl) static void PM_Download_Got(struct dl_download *dl)
{ {
char native[MAX_OSPATH];
qboolean successful = dl->status == DL_FINISHED; qboolean successful = dl->status == DL_FINISHED;
package_t *p; package_t *p;
char *tempname = dl->user_ctx; char *tempname = dl->user_ctx;
@ -1640,7 +1682,7 @@ static void PM_Download_Got(struct dl_download *dl)
searchpathfuncs_t *archive = FSZIP_LoadArchive(f, tempname, NULL); searchpathfuncs_t *archive = FSZIP_LoadArchive(f, tempname, NULL);
if (archive) if (archive)
{ {
p->flags &= ~(DPF_NATIVE|DPF_CACHED|DPF_CORRUPT|DPF_INSTALLED); p->flags &= ~(DPF_NATIVE|DPF_CACHED|DPF_CORRUPT|DPF_ENABLED);
archive->EnumerateFiles(archive, "*", PM_ExtractFiles, p); archive->EnumerateFiles(archive, "*", PM_ExtractFiles, p);
archive->EnumerateFiles(archive, "*/*", PM_ExtractFiles, p); archive->EnumerateFiles(archive, "*/*", PM_ExtractFiles, p);
archive->EnumerateFiles(archive, "*/*/*", PM_ExtractFiles, p); archive->EnumerateFiles(archive, "*/*/*", PM_ExtractFiles, p);
@ -1694,19 +1736,23 @@ static void PM_Download_Got(struct dl_download *dl)
} }
else else
destname = dep->name; destname = dep->name;
nfl |= DPF_INSTALLED | (p->flags & ~(DPF_CACHED|DPF_NATIVE|DPF_CORRUPT)); nfl |= DPF_ENABLED | (p->flags & ~(DPF_CACHED|DPF_NATIVE|DPF_CORRUPT));
FS_CreatePath(destname, p->fsroot); FS_CreatePath(destname, p->fsroot);
if (FS_Remove(destname, p->fsroot)) if (FS_Remove(destname, p->fsroot))
; ;
if (!FS_Rename2(tempname, destname, p->fsroot, p->fsroot)) if (!FS_Rename2(tempname, destname, p->fsroot, p->fsroot))
{ {
//error! //error!
Con_Printf("Couldn't rename %s to %s. Removed instead.\n", tempname, destname); if (!FS_NativePath(destname, p->fsroot, native, sizeof(native)))
Q_strncpyz(native, destname, sizeof(native));
Con_Printf("Couldn't rename %s to %s. Removed instead.\n", tempname, native);
FS_Remove (tempname, p->fsroot); FS_Remove (tempname, p->fsroot);
} }
else else
{ //success! { //success!
Con_Printf("Downloaded %s (to %s)\n", p->name, destname); if (!FS_NativePath(destname, p->fsroot, native, sizeof(native)))
Q_strncpyz(native, destname, sizeof(native));
Con_Printf("Downloaded %s (to %s)\n", p->name, native);
p->flags = nfl; p->flags = nfl;
PM_WriteInstalledPackages(); PM_WriteInstalledPackages();
} }
@ -1869,16 +1915,16 @@ static void PM_StartADownload(void)
{ //this appears to be a meta package with no download { //this appears to be a meta package with no download
//just directly install it. //just directly install it.
p->flags &= ~(DPF_NATIVE|DPF_CACHED|DPF_CORRUPT); p->flags &= ~(DPF_NATIVE|DPF_CACHED|DPF_CORRUPT);
p->flags |= DPF_INSTALLED; p->flags |= DPF_ENABLED;
PM_WriteInstalledPackages(); PM_WriteInstalledPackages();
} }
continue; continue;
} }
if (p->qhash && (p->flags & DPF_CACHED)) if (!PM_PurgeOnDisable(p))
{ //its in our cache directory, so lets just use that { //its in our cache directory, so lets just use that
p->trymirrors = 0; p->trymirrors = 0;
p->flags |= DPF_INSTALLED; p->flags |= DPF_ENABLED;
PM_WriteInstalledPackages(); PM_WriteInstalledPackages();
FS_ReloadPackFiles(); FS_ReloadPackFiles();
continue; continue;
@ -1955,42 +2001,56 @@ static void PM_ApplyChanges(void)
p = *link; p = *link;
if (p->download) if (p->download)
; //erk, dude, don't do two! ; //erk, dude, don't do two!
else if ((p->flags & DPF_PURGE) || (!(p->flags&DPF_MARKED) && (p->flags&DPF_INSTALLED))) else if ((p->flags & DPF_PURGE) || (!(p->flags&DPF_MARKED) && (p->flags&DPF_ENABLED)))
{ //if we don't want it but we have it anyway: { //if we don't want it but we have it anyway. don't bother to follow this logic when reinstalling
qboolean reloadpacks = false; qboolean reloadpacks = false;
struct packagedep_s *dep; struct packagedep_s *dep;
for (dep = p->deps; dep; dep = dep->next)
if ((p->flags & DPF_PURGE) || PM_PurgeOnDisable(p))
{ {
if (dep->dtype == DEP_FILE) for (dep = p->deps; dep; dep = dep->next)
{ {
if (!reloadpacks) if (dep->dtype == DEP_FILE)
{ {
char ext[8]; if (!reloadpacks)
COM_FileExtension(dep->name, ext, sizeof(ext));
if (!stricmp(ext, "pak") || !stricmp(ext, "pk3"))
{ {
reloadpacks = true; char ext[8];
FS_UnloadPackFiles(); COM_FileExtension(dep->name, ext, sizeof(ext));
if (!stricmp(ext, "pak") || !stricmp(ext, "pk3"))
{
reloadpacks = true;
FS_UnloadPackFiles();
}
} }
} if (*p->gamedir)
if (*p->gamedir)
{
char *f = va("%s/%s", p->gamedir, dep->name);
char temp[MAX_OSPATH];
if (p->qhash && FS_GenCachedPakName(f, p->qhash, temp, sizeof(temp)) && PM_CheckFile(temp, p->fsroot))
{ {
if (p->flags & DPF_PURGE) char *f = va("%s/%s", p->gamedir, dep->name);
FS_Remove(temp, p->fsroot); char temp[MAX_OSPATH];
if (p->qhash && FS_GenCachedPakName(f, p->qhash, temp, sizeof(temp)) && PM_CheckFile(temp, p->fsroot))
{
if (!FS_Remove(temp, p->fsroot))
p->flags |= DPF_CACHED;
}
else if (!FS_Remove(va("%s/%s", p->gamedir, dep->name), p->fsroot))
p->flags |= DPF_NATIVE;
} }
else else if (!FS_Remove(dep->name, p->fsroot))
FS_Remove(va("%s/%s", p->gamedir, dep->name), p->fsroot); p->flags |= DPF_NATIVE;
} }
else
FS_Remove(dep->name, p->fsroot);
} }
} }
p->flags &= ~(DPF_PURGE|DPF_ENABLED);
/* FIXME: windows bug:
** deleting an exe might 'succeed' but leave the file on disk for a while anyway.
** the file will eventually disappear, but until then we'll still see it as present,
** be unable to delete it again, and trying to open it to see if it still exists
** will fail.
** there's nothing we can do other than wait until whatever part of
** windows that's fucking up releases its handles.
** thankfully this only affects reinstalling exes/engines.
*/
p->flags &= ~(DPF_NATIVE|DPF_CACHED|DPF_CORRUPT|DPF_PURGE|DPF_INSTALLED);
PM_ValidatePackage(p); PM_ValidatePackage(p);
PM_WriteInstalledPackages(); PM_WriteInstalledPackages();
@ -2028,7 +2088,7 @@ static void PM_ApplyChanges(void)
//and flag any new/updated ones for a download //and flag any new/updated ones for a download
for (p = availablepackages; p ; p=p->next) for (p = availablepackages; p ; p=p->next)
{ {
if ((p->flags&DPF_MARKED) && !(p->flags&DPF_INSTALLED) && !p->download) if ((p->flags&DPF_MARKED) && !(p->flags&DPF_ENABLED) && !p->download)
p->trymirrors = ~0u; p->trymirrors = ~0u;
} }
PM_StartADownload(); //and try to do those downloads. PM_StartADownload(); //and try to do those downloads.
@ -2071,7 +2131,7 @@ void PM_Command_f(void)
{ {
const char *status; const char *status;
char *markup; char *markup;
if (p->flags & DPF_INSTALLED) if (p->flags & DPF_ENABLED)
markup = S_COLOR_GREEN; markup = S_COLOR_GREEN;
else if (p->flags & DPF_CORRUPT) else if (p->flags & DPF_CORRUPT)
markup = S_COLOR_RED; markup = S_COLOR_RED;
@ -2080,7 +2140,7 @@ void PM_Command_f(void)
else else
markup = S_COLOR_WHITE; markup = S_COLOR_WHITE;
if (!(p->flags & DPF_MARKED) != !(p->flags & DPF_INSTALLED) || (p->flags & DPF_PURGE)) if (!(p->flags & DPF_MARKED) != !(p->flags & DPF_ENABLED) || (p->flags & DPF_PURGE))
{ {
if (p->flags & DPF_MARKED) if (p->flags & DPF_MARKED)
{ {
@ -2094,7 +2154,7 @@ void PM_Command_f(void)
else else
status = S_COLOR_CYAN"<disable>"; status = S_COLOR_CYAN"<disable>";
} }
else if ((p->flags & (DPF_INSTALLED|DPF_CACHED)) == DPF_CACHED) else if ((p->flags & (DPF_ENABLED|DPF_CACHED)) == DPF_CACHED)
status = S_COLOR_CYAN"<disabled>"; status = S_COLOR_CYAN"<disabled>";
else else
status = ""; status = "";
@ -2124,7 +2184,7 @@ void PM_Command_f(void)
if (p->flags & DPF_MARKED) if (p->flags & DPF_MARKED)
{ {
if (p->flags & DPF_INSTALLED) if (p->flags & DPF_ENABLED)
{ {
if (p->flags & DPF_PURGE) if (p->flags & DPF_PURGE)
Con_Printf(" package is flagged to be re-installed\n"); Con_Printf(" package is flagged to be re-installed\n");
@ -2136,7 +2196,7 @@ void PM_Command_f(void)
} }
else else
{ {
if (p->flags & DPF_INSTALLED) if (p->flags & DPF_ENABLED)
{ {
if (p->flags & DPF_PURGE) if (p->flags & DPF_PURGE)
Con_Printf(" package is flagged to be purged\n"); Con_Printf(" package is flagged to be purged\n");
@ -2160,6 +2220,8 @@ void PM_Command_f(void)
Con_Printf(" package is hidden\n"); Con_Printf(" package is hidden\n");
if (p->flags & DPF_ENGINE) if (p->flags & DPF_ENGINE)
Con_Printf(" package is an engine update\n"); Con_Printf(" package is an engine update\n");
if (p->flags & DPF_TESTING)
Con_Printf(" package is untested\n");
return; return;
} }
Con_Printf("<package not found>\n"); Con_Printf("<package not found>\n");
@ -2282,6 +2344,56 @@ void PM_Command_f(void)
Con_Printf("%s: Unknown action %s\nShould be one of list, show, search, revert, add, rem, del, changes, apply\n", Cmd_Argv(0), act); Con_Printf("%s: Unknown action %s\nShould be one of list, show, search, revert, add, rem, del, changes, apply\n", Cmd_Argv(0), act);
} }
qboolean PM_FindUpdatedEngine(char *syspath, size_t syspathsize)
{
struct packagedep_s *dep;
package_t *e = NULL, *p;
char *pfname;
//figure out what we've previously installed.
if (!loadedinstalled)
{
vfsfile_t *f = FS_OpenVFS(INSTALLEDFILES, "rb", FS_ROOT);
loadedinstalled = true;
if (f)
{
PM_ParsePackageList(f, DPF_FORGETONUNINSTALL|DPF_ENABLED, NULL, "");
VFS_CLOSE(f);
}
}
for (p = availablepackages; p; p = p->next)
{
if ((p->flags & DPF_ENGINE) && !(p->flags & DPF_HIDDEN) && p->fsroot == FS_ROOT)
{
if ((p->flags & DPF_ENABLED) && (!e || strcmp(e->version, p->version) < 0))
if (strcmp(SVNREVISIONSTR, "-") && strcmp(SVNREVISIONSTR, p->version) < 0) //package must be more recent than the current engine too, there's no point auto-updating to an older revision.
{
for (dep = p->deps, pfname = NULL; dep; dep = dep->next)
{
if (dep->dtype != DEP_FILE)
continue;
if (pfname)
{
pfname = NULL;
break;
}
pfname = dep->name;
}
if (pfname && PM_CheckFile(pfname, p->fsroot))
{
if (FS_NativePath(pfname, p->fsroot, syspath, syspathsize))
e = p;
}
}
}
}
if (e)
return true;
return false;
}
#else #else
void PM_Command_f (void) void PM_Command_f (void)
{ {
@ -2307,7 +2419,6 @@ typedef struct {
qboolean populated; qboolean populated;
} dlmenu_t; } dlmenu_t;
static int autoupdatesetting = UPD_UNSUPPORTED;
static void MD_Draw (int x, int y, struct menucustom_s *c, struct menu_s *m) static void MD_Draw (int x, int y, struct menucustom_s *c, struct menu_s *m)
{ {
package_t *p; package_t *p;
@ -2326,7 +2437,7 @@ static void MD_Draw (int x, int y, struct menucustom_s *c, struct menu_s *m)
Draw_FunString (x+4, y, "PND"); Draw_FunString (x+4, y, "PND");
else else
{ {
switch((p->flags & (DPF_INSTALLED | DPF_MARKED))) switch((p->flags & (DPF_ENABLED | DPF_MARKED)))
{ {
case 0: case 0:
if (p->flags & DPF_PURGE) if (p->flags & DPF_PURGE)
@ -2339,10 +2450,12 @@ static void MD_Draw (int x, int y, struct menucustom_s *c, struct menu_s *m)
{ {
Draw_FunString (x+4, y, "^Ue080^Ue082"); Draw_FunString (x+4, y, "^Ue080^Ue082");
Draw_FunString (x+8, y, "^Ue081"); Draw_FunString (x+8, y, "^Ue081");
if (p->flags & DPF_PRESENT)
Draw_FunString (x+8, y, "C");
} }
break; break;
case DPF_INSTALLED: case DPF_ENABLED:
if (p->flags & DPF_PURGE || !(p->qhash && (p->flags & DPF_CACHED))) if ((p->flags & DPF_PURGE) || PM_PurgeOnDisable(p))
Draw_FunString (x, y, "DEL"); Draw_FunString (x, y, "DEL");
else else
Draw_FunString (x, y, "REM"); Draw_FunString (x, y, "REM");
@ -2350,12 +2463,12 @@ static void MD_Draw (int x, int y, struct menucustom_s *c, struct menu_s *m)
case DPF_MARKED: case DPF_MARKED:
if (p->flags & DPF_PURGE) if (p->flags & DPF_PURGE)
Draw_FunString (x, y, "GET"); Draw_FunString (x, y, "GET");
else if (p->flags & (DPF_CACHED|DPF_NATIVE)) else if (p->flags & (DPF_PRESENT))
Draw_FunString (x, y, "USE"); Draw_FunString (x, y, "USE");
else else
Draw_FunString (x, y, "GET"); Draw_FunString (x, y, "GET");
break; break;
case DPF_INSTALLED | DPF_MARKED: case DPF_ENABLED | DPF_MARKED:
if (p->flags & DPF_PURGE) if (p->flags & DPF_PURGE)
Draw_FunString (x, y, "GET"); //purge and reinstall. Draw_FunString (x, y, "GET"); //purge and reinstall.
else if (p->flags & DPF_CORRUPT) else if (p->flags & DPF_CORRUPT)
@ -2373,6 +2486,11 @@ static void MD_Draw (int x, int y, struct menucustom_s *c, struct menu_s *m)
if (p->flags & DPF_DISPLAYVERSION) if (p->flags & DPF_DISPLAYVERSION)
n = va("%s (%s)", n, *p->version?p->version:"unversioned"); n = va("%s (%s)", n, *p->version?p->version:"unversioned");
if (p->flags & DPF_TESTING) //hide testing updates
n = va("^h%s", n);
// if (!(p->flags & (DPF_ENABLED|DPF_MARKED|DPF_PRESENT))
// continue;
if (&m->selecteditem->common == &c->common) if (&m->selecteditem->common == &c->common)
Draw_AltFunString (x+48, y, n); Draw_AltFunString (x+48, y, n);
else else
@ -2392,7 +2510,7 @@ static qboolean MD_Key (struct menucustom_s *c, struct menu_s *m, int key, unsig
if (p->alternative && (p->flags & DPF_HIDDEN)) if (p->alternative && (p->flags & DPF_HIDDEN))
p = p->alternative; p = p->alternative;
if (p->flags & DPF_INSTALLED) if (p->flags & DPF_ENABLED)
{ {
switch (p->flags & (DPF_PURGE|DPF_MARKED)) switch (p->flags & (DPF_PURGE|DPF_MARKED))
{ {
@ -2401,7 +2519,7 @@ static qboolean MD_Key (struct menucustom_s *c, struct menu_s *m, int key, unsig
break; break;
case 0: case 0:
p->flags |= DPF_PURGE; //purge p->flags |= DPF_PURGE; //purge
if (p->flags & (DPF_CACHED | DPF_CORRUPT)) if (!PM_PurgeOnDisable(p))
break; break;
//fall through //fall through
case DPF_PURGE: case DPF_PURGE:
@ -2425,13 +2543,13 @@ static qboolean MD_Key (struct menucustom_s *c, struct menu_s *m, int key, unsig
case DPF_MARKED: case DPF_MARKED:
p->flags |= DPF_PURGE; p->flags |= DPF_PURGE;
//now: re-get despite already having it. //now: re-get despite already having it.
if (p->flags & (DPF_CACHED | DPF_CORRUPT)) if ((p->flags & DPF_PRESENT) && !PM_PurgeOnDisable(p))
break; //only makes sense if we already have a cached copy that we're not going to use. break; //only makes sense if we already have a cached copy that we're not going to use.
//fallthrough //fallthrough
case DPF_MARKED|DPF_PURGE: case DPF_MARKED|DPF_PURGE:
PM_UnmarkPackage(p); PM_UnmarkPackage(p);
//now: delete //now: delete
if (p->flags & (DPF_CACHED | DPF_CORRUPT)) if ((p->flags & DPF_PRESENT) && !PM_PurgeOnDisable(p))
break; //only makes sense if we have a cached/corrupt copy of it already break; //only makes sense if we have a cached/corrupt copy of it already
//fallthrough //fallthrough
case DPF_PURGE: case DPF_PURGE:
@ -2477,20 +2595,13 @@ static void MD_AutoUpdate_Draw (int x, int y, struct menucustom_s *c, struct men
{ {
char *settings[] = char *settings[] =
{ {
"Unsupported",
"Revert Engine",
"Off", "Off",
"Stable Updates", "Stable Updates",
"Test Updates" "Test Updates"
}; };
char *text; char *text;
int setting = Sys_GetAutoUpdateSetting(); int setting = bound(0, pm_autoupdate.ival, 2);
if (setting == UPD_UNSUPPORTED) text = va("Auto Update: %s", settings[setting]);
text = va("Auto Update: %s", settings[autoupdatesetting+1]);
else if (autoupdatesetting == UPD_UNSUPPORTED)
text = va("Auto Update: %s", settings[setting+1]);
else
text = va("Auto Update: %s (unsaved)", settings[autoupdatesetting+1]);
if (&m->selecteditem->common == &c->common) if (&m->selecteditem->common == &c->common)
Draw_AltFunString (x+4, y, text); Draw_AltFunString (x+4, y, text);
else else
@ -2500,12 +2611,13 @@ static qboolean MD_AutoUpdate_Key (struct menucustom_s *c, struct menu_s *m, int
{ {
if (key == K_ENTER || key == K_KP_ENTER || key == K_MOUSE1) if (key == K_ENTER || key == K_KP_ENTER || key == K_MOUSE1)
{ {
if (autoupdatesetting == UPD_UNSUPPORTED) char nv[8] = "0";
autoupdatesetting = min(0, Sys_GetAutoUpdateSetting()); if (pm_autoupdate.ival < UPD_TESTING && pm_autoupdate.ival >= 0)
autoupdatesetting+=1; Q_snprintfz(nv, sizeof(nv), "%i", pm_autoupdate.ival+1);
if (autoupdatesetting > UPD_TESTING) Cvar_ForceSet(&pm_autoupdate, nv);
autoupdatesetting = (Sys_GetAutoUpdateSetting() == UPD_UNSUPPORTED)?1:0; PM_WriteInstalledPackages();
PM_UpdatePackageList(true, 2);
PM_UpdatePackageList(true, 0);
} }
return false; return false;
} }
@ -2524,14 +2636,6 @@ static qboolean MD_ApplyDownloads (union menuoption_s *mo,struct menu_s *m,int k
{ {
if (key == K_ENTER || key == K_KP_ENTER || key == K_MOUSE1) if (key == K_ENTER || key == K_KP_ENTER || key == K_MOUSE1)
{ {
#ifdef HAVEAUTOUPDATE
if (autoupdatesetting != UPD_UNSUPPORTED)
{
Sys_SetAutoUpdateSetting(autoupdatesetting);
autoupdatesetting = UPD_UNSUPPORTED;
}
#endif
PM_ApplyChanges(); PM_ApplyChanges();
return true; return true;
} }
@ -2599,8 +2703,11 @@ void M_AddItemsToDownloadMenu(menu_t *m)
{ {
if (strncmp(p->category, info->pathprefix, prefixlen)) if (strncmp(p->category, info->pathprefix, prefixlen))
continue; continue;
if ((p->flags & DPF_HIDDEN) && (p->arch || !(p->flags & DPF_INSTALLED))) if ((p->flags & DPF_HIDDEN) && (p->arch || !(p->flags & DPF_ENABLED)))
continue; continue;
// if (p->flags & DPF_TESTING) //hide testing updates
// if (!(p->flags & (DPF_ENABLED|DPF_MARKED|DPF_PRESENT))
// continue;
slash = strchr(p->category+prefixlen, '/'); slash = strchr(p->category+prefixlen, '/');
if (slash) if (slash)
@ -2622,7 +2729,7 @@ void M_AddItemsToDownloadMenu(menu_t *m)
{ {
if (!strncmp(s->category, info->pathprefix, slash-path) || s->category[slash-path] != '/') if (!strncmp(s->category, info->pathprefix, slash-path) || s->category[slash-path] != '/')
continue; continue;
if (!(s->flags & DPF_INSTALLED) != !(s->flags & DPF_MARKED)) if (!(s->flags & DPF_ENABLED) != !(s->flags & DPF_MARKED))
break; break;
} }
@ -2712,6 +2819,7 @@ void Menu_DownloadStuff_f (void)
menu = M_CreateMenu(sizeof(dlmenu_t)); menu = M_CreateMenu(sizeof(dlmenu_t));
info = menu->data; info = menu->data;
menu->persist = true;
menu->predraw = M_Download_UpdateStatus; menu->predraw = M_Download_UpdateStatus;
info->downloadablessequence = downloadablessequence; info->downloadablessequence = downloadablessequence;
@ -2727,7 +2835,7 @@ void Menu_DownloadStuff_f (void)
//should only be called AFTER the filesystem etc is inited. //should only be called AFTER the filesystem etc is inited.
void Menu_Download_Update(void) void Menu_Download_Update(void)
{ {
if (Sys_GetAutoUpdateSetting() == UPD_OFF || Sys_GetAutoUpdateSetting() == UPD_REVERT) if (!pm_autoupdate.ival)
return; return;
PM_UpdatePackageList(true, 2); PM_UpdatePackageList(true, 2);

View File

@ -1581,7 +1581,11 @@ void M_RemoveMenu (menu_t *menu)
if (menu->remove) if (menu->remove)
menu->remove(menu); menu->remove(menu);
if (menu == firstmenu) if (menu == firstmenu)
{
firstmenu = menu->parent; firstmenu = menu->parent;
if (firstmenu)
firstmenu->child = NULL;
}
else else
{ {
menu_t *prev; menu_t *prev;
@ -1636,14 +1640,8 @@ void M_RemoveAllMenus (qboolean leaveprompts)
for (link = &firstmenu; *link; ) for (link = &firstmenu; *link; )
{ {
m = *link; m = *link;
if (!m->exclusive && leaveprompts) if ((m->persist || !m->exclusive) && leaveprompts)
{ link = &m->parent;
//this is WEIRD.
if (m == firstmenu)
link = &m->parent;
else
link = &m->child;
}
else else
M_RemoveMenu(m); M_RemoveMenu(m);
} }

View File

@ -172,15 +172,6 @@ qboolean M_Options_InvertMouse (menucheck_t *option, struct menu_s *menu, chk_se
} }
} }
#ifdef HAVEAUTOUPDATE
static void M_Options_Remove(menu_t *m)
{
menucombo_t *c = m->data;
if (c)
Sys_SetAutoUpdateSetting(c->selectedoption);
}
#endif
//options menu. //options menu.
void M_Menu_Options_f (void) void M_Menu_Options_f (void)
{ {
@ -190,16 +181,19 @@ void M_Menu_Options_f (void)
#endif #endif
int y; int y;
#ifdef HAVEAUTOUPDATE #ifdef WEBCLIENT
#define HAVEAUTOUPDATE
menuoption_t *updatecbo;
static const char *autoupopts[] = { static const char *autoupopts[] = {
"Revert",
"Off", "Off",
"Tested(Recommended)", "Tested(Recommended)",
"Untested(Latest)", "Untested(Latest)",
NULL NULL
}; };
static const char *autoupvals[] = {
"0",
"1",
"2",
NULL
};
#endif #endif
static const char *projections[] = { static const char *projections[] = {
"Regular", "Regular",
@ -256,8 +250,8 @@ void M_Menu_Options_f (void)
MB_CHECKBOXCVAR("Lookspring", lookspring, 0), MB_CHECKBOXCVAR("Lookspring", lookspring, 0),
MB_CHECKBOXCVAR("Lookstrafe", lookstrafe, 0), MB_CHECKBOXCVAR("Lookstrafe", lookstrafe, 0),
MB_CHECKBOXCVAR("Windowed Mouse", _windowed_mouse, 0), MB_CHECKBOXCVAR("Windowed Mouse", _windowed_mouse, 0),
#ifdef HAVEAUTOUPDATE #ifdef WEBCLIENT
MB_COMBORETURN("Auto Update", autoupopts, Sys_GetAutoUpdateSetting(), updatecbo, "This downloads engine updates from the internet, when a new build is available."), MB_COMBOCVAR("Auto Update", pm_autoupdate, autoupopts, autoupvals, "This offers to download engine+package updates from the internet, when new versions are available."),
#endif #endif
#ifndef CLIENTONLY #ifndef CLIENTONLY
MB_COMBOCVAR("Auto Save", sv_autosave, autosaveopts, autosavevals, NULL), MB_COMBOCVAR("Auto Save", sv_autosave, autosaveopts, autosavevals, NULL),
@ -311,11 +305,6 @@ void M_Menu_Options_f (void)
MC_AddCvarCombo(menu, 16, 216, y, "Use Hud Plugin", &plug_sbar, hudplugopts, hudplugvalues); y += 8; MC_AddCvarCombo(menu, 16, 216, y, "Use Hud Plugin", &plug_sbar, hudplugopts, hudplugvalues); y += 8;
} }
#endif #endif
#ifdef HAVEAUTOUPDATE
menu->data = updatecbo;
menu->remove = M_Options_Remove;
#endif
} }
#ifndef __CYGWIN__ #ifndef __CYGWIN__

View File

@ -1260,7 +1260,10 @@ void M_Shutdown(qboolean total)
MP_Shutdown(); MP_Shutdown();
#endif #endif
if (total) if (total)
{
M_RemoveAllMenus(false);
M_DeInit_Internal(); M_DeInit_Internal();
}
} }
void M_Reinit(void) void M_Reinit(void)

View File

@ -274,6 +274,7 @@ typedef struct menu_s {
qboolean iszone; qboolean iszone;
qboolean exclusive; qboolean exclusive;
qboolean persist; //persists despite menuqc/engine changes etc
void *data; //typecast void *data; //typecast
@ -467,6 +468,7 @@ qboolean MP_Init (void);
void MP_Shutdown (void); void MP_Shutdown (void);
qboolean MP_Toggle(int mode); qboolean MP_Toggle(int mode);
void MP_Draw(void); void MP_Draw(void);
qboolean MP_UsingGamecodeLoadingScreen(void);
void MP_RegisterCvarsAndCmds(void); void MP_RegisterCvarsAndCmds(void);
qboolean MP_Keydown(int key, int unicode, unsigned int devid); qboolean MP_Keydown(int key, int unicode, unsigned int devid);
void MP_Keyup(int key, int unicode, unsigned int devid); void MP_Keyup(int key, int unicode, unsigned int devid);

View File

@ -2384,6 +2384,7 @@ world_t menu_world;
func_t mp_init_function; func_t mp_init_function;
func_t mp_shutdown_function; func_t mp_shutdown_function;
func_t mp_draw_function; func_t mp_draw_function;
func_t mp_drawloading_function;
func_t mp_keydown_function; func_t mp_keydown_function;
func_t mp_keyup_function; func_t mp_keyup_function;
func_t mp_inputevent_function; func_t mp_inputevent_function;
@ -2604,6 +2605,7 @@ qboolean MP_Init (void)
mp_init_function = PR_FindFunction(menu_world.progs, "m_init", PR_ANY); mp_init_function = PR_FindFunction(menu_world.progs, "m_init", PR_ANY);
mp_shutdown_function = PR_FindFunction(menu_world.progs, "m_shutdown", PR_ANY); mp_shutdown_function = PR_FindFunction(menu_world.progs, "m_shutdown", PR_ANY);
mp_draw_function = PR_FindFunction(menu_world.progs, "m_draw", PR_ANY); mp_draw_function = PR_FindFunction(menu_world.progs, "m_draw", PR_ANY);
mp_drawloading_function = PR_FindFunction(menu_world.progs, "m_drawloading", PR_ANY);
mp_inputevent_function = PR_FindFunction(menu_world.progs, "Menu_InputEvent", PR_ANY); mp_inputevent_function = PR_FindFunction(menu_world.progs, "Menu_InputEvent", PR_ANY);
mp_keydown_function = PR_FindFunction(menu_world.progs, "m_keydown", PR_ANY); mp_keydown_function = PR_FindFunction(menu_world.progs, "m_keydown", PR_ANY);
mp_keyup_function = PR_FindFunction(menu_world.progs, "m_keyup", PR_ANY); mp_keyup_function = PR_FindFunction(menu_world.progs, "m_keyup", PR_ANY);
@ -2734,8 +2736,15 @@ void MP_RegisterCvarsAndCmds(void)
Cvar_Set(&forceqmenu, "1"); Cvar_Set(&forceqmenu, "1");
} }
qboolean MP_UsingGamecodeLoadingScreen(void)
{
return menu_world.progs && mp_drawloading_function;
}
void MP_Draw(void) void MP_Draw(void)
{ {
extern qboolean scr_drawloading;
globalvars_t *pr_globals;
if (!menu_world.progs) if (!menu_world.progs)
return; return;
if (setjmp(mp_abort)) if (setjmp(mp_abort))
@ -2748,14 +2757,14 @@ void MP_Draw(void)
*menu_world.g.frametime = host_frametime; *menu_world.g.frametime = host_frametime;
inmenuprogs++; inmenuprogs++;
if (mp_draw_function) pr_globals = PR_globals(menu_world.progs, PR_CURRENT);
{ ((float *)pr_globals)[OFS_PARM0+0] = vid.width;
globalvars_t *pr_globals = PR_globals(menu_world.progs, PR_CURRENT); ((float *)pr_globals)[OFS_PARM0+1] = vid.height;
((float *)pr_globals)[OFS_PARM0+0] = vid.width; ((float *)pr_globals)[OFS_PARM0+2] = 0;
((float *)pr_globals)[OFS_PARM0+1] = vid.height; if (mp_drawloading_function && scr_drawloading)
((float *)pr_globals)[OFS_PARM0+2] = 0; PR_ExecuteProgram(menu_world.progs, mp_drawloading_function);
else if (mp_draw_function)
PR_ExecuteProgram(menu_world.progs, mp_draw_function); PR_ExecuteProgram(menu_world.progs, mp_draw_function);
}
inmenuprogs--; inmenuprogs--;
} }

View File

@ -290,7 +290,8 @@ extern qboolean noclip_anglehack;
extern quakeparms_t host_parms; extern quakeparms_t host_parms;
extern cvar_t fs_gamename; extern cvar_t fs_gamename;
extern cvar_t fs_downloads_url; extern cvar_t pm_downloads_url;
extern cvar_t pm_autoupdate;
extern cvar_t com_protocolname; extern cvar_t com_protocolname;
extern cvar_t com_nogamedirnativecode; extern cvar_t com_nogamedirnativecode;
extern cvar_t com_parseutf8; extern cvar_t com_parseutf8;

View File

@ -324,7 +324,7 @@ cvar_t gl_conback = CVARFDC ("gl_conback", "",
// CVAR_ARCHIVE); // CVAR_ARCHIVE);
//cvar_t gl_detailscale = CVAR ("gl_detailscale", "5"); //cvar_t gl_detailscale = CVAR ("gl_detailscale", "5");
cvar_t gl_font = CVARFD ("gl_font", "", cvar_t gl_font = CVARFD ("gl_font", "",
CVAR_RENDERERCALLBACK, ("Specifies the font file to use. a value such as FONT:ALTFONT specifies an alternative font to be used when ^^a is used.\n" CVAR_RENDERERCALLBACK|CVAR_ARCHIVE, ("Specifies the font file to use. a value such as FONT:ALTFONT specifies an alternative font to be used when ^^a is used.\n"
"When using TTF fonts, you will likely need to scale text to at least 150% - vid_conautoscale 1.5 will do this.\n" "When using TTF fonts, you will likely need to scale text to at least 150% - vid_conautoscale 1.5 will do this.\n"
"TTF fonts may be loaded from your windows directory. \'gl_font cour?col=1,1,1:couri?col=0,1,0\' loads eg: c:\\windows\\fonts\\cour.ttf, and uses the italic version of courier for alternative text, with specific colour tints." "TTF fonts may be loaded from your windows directory. \'gl_font cour?col=1,1,1:couri?col=0,1,0\' loads eg: c:\\windows\\fonts\\cour.ttf, and uses the italic version of courier for alternative text, with specific colour tints."
)); ));

View File

@ -3324,6 +3324,15 @@ void Sbar_DeathmatchOverlay (int start)
CL_SendClientCommand(true, "ping"); CL_SendClientCommand(true, "ping");
} }
} }
if (cls.protocol == CP_NETQUAKE)
{
if (cl.nqplayernamechanged && cl.nqplayernamechanged < realtime)
{
cl.nqplayernamechanged = 0;
cls.nqexpectingstatusresponse = true;
CL_SendClientCommand(true, "status");
}
}
if (start) if (start)
y = start; y = start;

View File

@ -1139,6 +1139,7 @@ static void OpenAL_Shutdown (soundcardinfo_t *sc)
palcMakeContextCurrent(NULL); palcMakeContextCurrent(NULL);
palcDestroyContext(oali->OpenAL_Context); palcDestroyContext(oali->OpenAL_Context);
palcCloseDevice(oali->OpenAL_Device); palcCloseDevice(oali->OpenAL_Device);
Z_Free(oali->source);
Z_Free(oali); Z_Free(oali);
} }

View File

@ -1134,7 +1134,7 @@ static BOOL CALLBACK dsound_capture_enumerate_ds(LPGUID lpGuid, LPCSTR lpcstrDes
StringFromGUID2(lpGuid, mssuck, sizeof(mssuck)/sizeof(mssuck[0])); StringFromGUID2(lpGuid, mssuck, sizeof(mssuck)/sizeof(mssuck[0]));
wcstombs(guidbuf, mssuck, sizeof(guidbuf)); wcstombs(guidbuf, mssuck, sizeof(guidbuf));
callback(SDRVNAME, guidbuf, lpcstrDescription); callback(SDRVNAME, guidbuf, va("DS: %s", lpcstrDescription));
return TRUE; return TRUE;
} }

View File

@ -437,10 +437,12 @@ extern snd_capture_driver_t OPENAL_Capture;
#endif #endif
snd_capture_driver_t DSOUND_Capture; snd_capture_driver_t DSOUND_Capture;
snd_capture_driver_t OSS_Capture; snd_capture_driver_t OSS_Capture;
snd_capture_driver_t SDL_Capture;
snd_capture_driver_t *capturedrivers[] = snd_capture_driver_t *capturedrivers[] =
{ {
&DSOUND_Capture, &DSOUND_Capture,
&SDL_Capture,
&OSS_Capture, &OSS_Capture,
#ifdef AVAIL_OPENAL #ifdef AVAIL_OPENAL
&OPENAL_Capture, &OPENAL_Capture,
@ -1735,7 +1737,7 @@ static void QDECL S_Voip_EnumeratedCaptureDevice(const char *driver, const char
fullintname = va("%s:%s", driver, devicecode); fullintname = va("%s:%s", driver, devicecode);
else else
fullintname = driver; fullintname = driver;
Q_snprintfz(opts, sizeof(opts), "%s%s%s %s", snd_voip_capturedevice_opts.string, *snd_voip_capturedevice_opts.string?" ":"", COM_QuotedString(fullintname, nbuf, sizeof(nbuf), false), COM_QuotedString(readabledevice, dbuf, sizeof(dbuf), false)); Q_snprintfz(opts, sizeof(opts), "%s%s%s %s", snd_voip_capturedevice_opts.string, *snd_voip_capturedevice_opts.string?" ":"", COM_QuotedString(fullintname, nbuf, sizeof(nbuf), false), COM_QuotedString(readabledevice, dbuf, sizeof(dbuf), false));
Cvar_ForceSet(&snd_voip_capturedevice_opts, opts); Cvar_ForceSet(&snd_voip_capturedevice_opts, opts);
} }
@ -2072,6 +2074,7 @@ void S_ShutdownCard(soundcardinfo_t *sc)
*link = sc->next; *link = sc->next;
if (sc->Shutdown) if (sc->Shutdown)
sc->Shutdown(sc); sc->Shutdown(sc);
Z_Free(sc->channel);
Z_Free(sc); Z_Free(sc);
break; break;
} }

View File

@ -3,6 +3,13 @@
#ifdef DYNAMIC_SDL #ifdef DYNAMIC_SDL
#define SDL_MAJOR_VERSION 2 #define SDL_MAJOR_VERSION 2
#define SDL_MINOR_VERSION 0
#define SDL_PATCHLEVEL 5
#define SDL_VERSIONNUM(X, Y, Z) ((X)*1000 + (Y)*100 + (Z))
#define SDL_COMPILEDVERSION SDL_VERSIONNUM(SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL)
#define SDL_VERSION_ATLEAST(X, Y, Z) (SDL_COMPILEDVERSION >= SDL_VERSIONNUM(X, Y, Z))
//if we're not an sdl build, we probably want to link to sdl libraries dynamically or something. //if we're not an sdl build, we probably want to link to sdl libraries dynamically or something.
#include <stdint.h> #include <stdint.h>
#define SDL_AudioDeviceID uint32_t #define SDL_AudioDeviceID uint32_t
@ -18,9 +25,10 @@
#else #else
#define AUDIO_S16SYS AUDIO_S16LSB #define AUDIO_S16SYS AUDIO_S16LSB
#endif #endif
#define SDLCALL QDECL
typedef uint16_t SDL_AudioFormat; typedef uint16_t SDL_AudioFormat;
typedef void VARGS (*SDL_AudioCallback)(void *userdata, uint8_t *stream, int len); typedef void (SDLCALL *SDL_AudioCallback)(void *userdata, uint8_t *stream, int len);
typedef struct SDL_AudioSpec typedef struct SDL_AudioSpec
{ {
@ -35,16 +43,20 @@ typedef struct SDL_AudioSpec
void *userdata; void *userdata;
} SDL_AudioSpec; } SDL_AudioSpec;
static int (*SDL_Init) (uint32_t flags); static int (SDLCALL *SDL_Init) (uint32_t flags);
static int (*SDL_InitSubSystem) (uint32_t flags); static int (SDLCALL *SDL_InitSubSystem) (uint32_t flags);
static SDL_AudioDeviceID (*SDL_OpenAudioDevice) (const char *dev, int iscapture, const SDL_AudioSpec *desired, SDL_AudioSpec *obtained, int allowed_changes); static SDL_AudioDeviceID (SDLCALL *SDL_OpenAudioDevice) (const char *dev, int iscapture, const SDL_AudioSpec *desired, SDL_AudioSpec *obtained, int allowed_changes);
static void (*SDL_PauseAudioDevice) (SDL_AudioDeviceID fd, int pausestate); static void (SDLCALL *SDL_PauseAudioDevice) (SDL_AudioDeviceID fd, int pausestate);
static void (*SDL_LockAudioDevice) (SDL_AudioDeviceID fd); static void (SDLCALL *SDL_LockAudioDevice) (SDL_AudioDeviceID fd);
static void (*SDL_UnlockAudioDevice) (SDL_AudioDeviceID fd); static void (SDLCALL *SDL_UnlockAudioDevice) (SDL_AudioDeviceID fd);
static int (*SDL_CloseAudioDevice) (SDL_AudioDeviceID fd); static int (SDLCALL *SDL_CloseAudioDevice) (SDL_AudioDeviceID fd);
static int (*SDL_GetNumAudioDevices) (int iscapture); static int (SDLCALL *SDL_GetNumAudioDevices) (int iscapture);
static const char *(*SDL_GetAudioDeviceName) (int index, int iscapture); static const char *(SDLCALL *SDL_GetAudioDeviceName) (int index, int iscapture);
static const char *(*SDL_GetError) (void); static const char *(SDLCALL *SDL_GetError) (void);
#if SDL_VERSION_ATLEAST(2,0,5)
static uint32_t (SDLCALL *SDL_GetQueuedAudioSize) (SDL_AudioDeviceID dev);
static uint32_t (SDLCALL *SDL_DequeueAudio) (SDL_AudioDeviceID dev, void *data, uint32_t len);
#endif
#else #else
#include <SDL.h> #include <SDL.h>
#endif #endif
@ -74,6 +86,10 @@ static qboolean SSDL_InitAudio(void)
{(void*)&SDL_GetNumAudioDevices, "SDL_GetNumAudioDevices"}, {(void*)&SDL_GetNumAudioDevices, "SDL_GetNumAudioDevices"},
{(void*)&SDL_GetAudioDeviceName, "SDL_GetAudioDeviceName"}, {(void*)&SDL_GetAudioDeviceName, "SDL_GetAudioDeviceName"},
{(void*)&SDL_GetError, "SDL_GetError"}, {(void*)&SDL_GetError, "SDL_GetError"},
#if SDL_VERSION_ATLEAST(2,0,5)
{(void*)&SDL_GetQueuedAudioSize, "SDL_GetQueuedAudioSize"},
{(void*)&SDL_DequeueAudio, "SDL_DequeueAudio"},
#endif
{NULL, NULL} {NULL, NULL}
}; };
static dllhandle_t *libsdl; static dllhandle_t *libsdl;
@ -82,6 +98,10 @@ static qboolean SSDL_InitAudio(void)
libsdl = Sys_LoadLibrary("libSDL2-2.0.so.0", funcs); libsdl = Sys_LoadLibrary("libSDL2-2.0.so.0", funcs);
if (!libsdl) if (!libsdl)
libsdl = Sys_LoadLibrary("libSDL2.so", funcs); //maybe they have a dev package installed that fixes this mess. libsdl = Sys_LoadLibrary("libSDL2.so", funcs); //maybe they have a dev package installed that fixes this mess.
#ifdef _WIN32
if (!libsdl)
libsdl = Sys_LoadLibrary("SDL2", funcs);
#endif
if (libsdl) if (libsdl)
SDL_Init(SDL_INIT_NOPARACHUTE); SDL_Init(SDL_INIT_NOPARACHUTE);
else else
@ -185,7 +205,7 @@ static void SSDL_Submit(soundcardinfo_t *sc, int start, int end)
//SDL will call SSDL_Paint to paint when it's time, and the sound buffer is always there... //SDL will call SSDL_Paint to paint when it's time, and the sound buffer is always there...
} }
static qboolean SDL_InitCard(soundcardinfo_t *sc, const char *devicename) static qboolean QDECL SDL_InitCard(soundcardinfo_t *sc, const char *devicename)
{ {
SDL_AudioSpec desired, obtained; SDL_AudioSpec desired, obtained;
@ -273,7 +293,7 @@ static qboolean QDECL SDL_Enumerate(void (QDECL *cb) (const char *drivername, co
{ {
const char *devname = SDL_GetAudioDeviceName(i, false); const char *devname = SDL_GetAudioDeviceName(i, false);
if (devname) if (devname)
cb(SDRVNAME, devname, va("SDL (%s)", devname)); cb(SDRVNAME, devname, va("SDL:%s", devname));
} }
} }
return true; return true;
@ -289,4 +309,91 @@ sounddriver_t SDL_Output =
SDL_Enumerate SDL_Enumerate
}; };
#if SDL_VERSION_ATLEAST(2,0,5) && defined(VOICECHAT)
//Requires SDL 2.0.5+ supposedly.
//Bugging out for me on windows, with really low audio levels. looks like there's been some float->int conversion without a multiplier. asking for float audio gives stupidly low values too.
typedef struct
{
SDL_AudioDeviceID dev;
} sdlcapture_t;
static void QDECL SDL_Capture_Start(void *ctx)
{
sdlcapture_t *d = ctx;
SDL_PauseAudioDevice(d->dev, FALSE);
}
static void QDECL SDL_Capture_Stop(void *ctx)
{
sdlcapture_t *d = ctx;
SDL_PauseAudioDevice(d->dev, TRUE);
}
static void QDECL SDL_Capture_Shutdown(void *ctx)
{
sdlcapture_t *d = ctx;
SDL_CloseAudioDevice(d->dev);
Z_Free(d);
}
static qboolean QDECL SDL_Capture_Enumerate(void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename))
{
int i, count;
if (SSDL_InitAudio())
{
count = SDL_GetNumAudioDevices(true);
for (i = 0; i < count; i++)
{
const char *name = SDL_GetAudioDeviceName(i, true);
if (name)
callback(SDRVNAME, name, va("SDL:%s", name));
}
}
return true;
}
static void *QDECL SDL_Capture_Init (int rate, const char *devname)
{
SDL_AudioSpec want, have;
sdlcapture_t c, *r;
memset(&want, 0, sizeof(want));
want.freq = rate;
want.format = AUDIO_S16SYS;
want.channels = 1;
want.samples = 256; //this seems to be chunk sizes rather than total buffer size, so lets keep it reasonably small for lower latencies
want.callback = NULL;
c.dev = SDL_OpenAudioDevice(devname, true, &want, &have, 0);
if (!c.dev) //failed?
return NULL;
r = Z_Malloc(sizeof(*r));
*r = c;
return r;
}
/*minbytes is a hint to not bother wasting time*/
static unsigned int QDECL SDL_Capture_Update(void *ctx, unsigned char *buffer, unsigned int minbytes, unsigned int maxbytes)
{
sdlcapture_t *c = ctx;
unsigned int queuedsize = SDL_GetQueuedAudioSize(c->dev);
if (queuedsize < minbytes)
return 0;
if (queuedsize > maxbytes)
queuedsize = maxbytes;
queuedsize = SDL_DequeueAudio(c->dev, buffer, queuedsize);
return queuedsize;
}
snd_capture_driver_t SDL_Capture =
{
1,
SDRVNAME,
SDL_Capture_Enumerate,
SDL_Capture_Init,
SDL_Capture_Start,
SDL_Capture_Update,
SDL_Capture_Stop,
SDL_Capture_Shutdown
};
#endif

View File

@ -1031,10 +1031,12 @@ qboolean Sys_remove (char *path)
if (WinNT) if (WinNT)
{ {
wchar_t wide[MAX_OSPATH]; wchar_t wide[MAX_OSPATH];
DWORD err;
widen(wide, sizeof(wide), path); widen(wide, sizeof(wide), path);
if (DeleteFileW(wide)) if (DeleteFileW(wide))
return true; //success return true; //success
if (GetLastError() == ERROR_FILE_NOT_FOUND) err = GetLastError();
if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)
return true; //succeed when the file already didn't exist return true; //succeed when the file already didn't exist
return false; //other errors? panic return false; //other errors? panic
} }
@ -2619,406 +2621,118 @@ void Win7_TaskListInit(void)
} }
#endif #endif
BOOL CopyFileU(const char *src, const char *dst, BOOL bFailIfExists) #ifndef SVNREVISION
{ #if 0 //1 to debug engine update in msvc.
wchar_t wide1[2048]; #define SVNREVISION 1
wchar_t wide2[2048];
return CopyFileW(widen(wide1, sizeof(wide1), src), widen(wide2, sizeof(wide2), dst), bFailIfExists);
}
//#define SVNREVISION 1
#if defined(SVNREVISION) && !defined(MINIMAL) && !defined(NOLEGACY)
#define SVNREVISIONSTR STRINGIFY(SVNREVISION)
#if defined(OFFICIAL_RELEASE)
#define UPD_BUILDTYPE "rel"
#else #else
#define UPD_BUILDTYPE "test" #define SVNREVISION -
//WARNING: Security comes from the fact that the triptohell.info certificate is hardcoded in the tls code. #endif
//this will correctly detect insecure tls proxies also. #endif
#define UPDATE_URL_ROOT "https://triptohell.info/moodles/" #define SVNREVISIONSTR STRINGIFY(SVNREVISION)
#define UPDATE_URL_TESTED UPDATE_URL_ROOT "autoup/"
#define UPDATE_URL_NIGHTLY UPDATE_URL_ROOT #ifndef NOLEGACY
#define UPDATE_URL_VERSION "%sversion.txt" #if defined(SVNREVISION) && !defined(MINIMAL)
#ifdef NOLEGACY #if defined(OFFICIAL_RELEASE)
#ifdef _WIN64 #define UPD_BUILDTYPE "rel"
#define UPDATE_URL_BUILD "%snocompat64/fte" EXETYPE "64.exe"
#else
#define UPDATE_URL_BUILD "%snocompat/fte" EXETYPE ".exe"
#endif
#else #else
#ifdef _WIN64 #define UPD_BUILDTYPE "test"
#define UPDATE_URL_BUILD "%swin64/fte" EXETYPE "64.exe" //WARNING: Security comes from the fact that the triptohell.info certificate is hardcoded in the tls code.
//this will correctly detect insecure tls proxies also.
#define UPDATE_URL_ROOT "https://triptohell.info/moodles/"
#define UPDATE_URL_TESTED UPDATE_URL_ROOT "autoup/"
#define UPDATE_URL_NIGHTLY UPDATE_URL_ROOT
#define UPDATE_URL_VERSION "%sversion.txt"
#ifdef NOLEGACY
#ifdef _WIN64
#define UPDATE_URL_BUILD "%snocompat64/fte" EXETYPE "64.exe"
#else
#define UPDATE_URL_BUILD "%snocompat/fte" EXETYPE ".exe"
#endif
#else #else
#define UPDATE_URL_BUILD "%swin32/fte" EXETYPE ".exe" #ifdef _WIN64
#define UPDATE_URL_BUILD "%swin64/fte" EXETYPE "64.exe"
#else
#define UPDATE_URL_BUILD "%swin32/fte" EXETYPE ".exe"
#endif
#endif #endif
#endif #endif
#if defined(SERVERONLY)
#define EXETYPE "qwsv" //not gonna happen, but whatever.
#elif defined(GLQUAKE) && defined(D3DQUAKE)
#define EXETYPE "qw"
#elif defined(GLQUAKE)
#ifdef MINIMAL
#define EXETYPE "minglqw"
#else
#define EXETYPE "glqw"
#endif
#elif defined(D3DQUAKE)
#define EXETYPE "d3dqw"
#elif defined(SWQUAKE)
#define EXETYPE "swqw"
#else
//erm...
#define EXETYPE "qw"
#endif
#endif #endif
#endif #endif
#if defined(SERVERONLY) #ifdef HAVEAUTOUPDATE
#define EXETYPE "qwsv" //not gonna happen, but whatever. //this is for legacy reasons. old builds stored a 'pending' name in the registry for the 'frontend' to rename+use
#elif defined(GLQUAKE) && defined(D3DQUAKE)
#define EXETYPE "qw"
#elif defined(GLQUAKE)
#ifdef MINIMAL
#define EXETYPE "minglqw"
#else
#define EXETYPE "glqw"
#endif
#elif defined(D3DQUAKE)
#define EXETYPE "d3dqw"
#elif defined(SWQUAKE)
#define EXETYPE "swqw"
#else
//erm...
#define EXETYPE "qw"
#endif
#ifdef UPDATE_URL_ROOT
int sys_autoupdatesetting;
qboolean Update_GetHomeDirectory(char *homedir, int homedirsize)
{
HMODULE shfolder = LoadLibraryA("shfolder.dll");
if (shfolder)
{
HRESULT (WINAPI *dSHGetFolderPathW) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath);
dSHGetFolderPathW = (void *)GetProcAddress(shfolder, "SHGetFolderPathW");
if (dSHGetFolderPathW)
{
wchar_t folderw[MAX_PATH];
// 0x5 == CSIDL_PERSONAL
if (dSHGetFolderPathW(NULL, 0x5, NULL, 0, folderw) == S_OK)
{
narrowen(homedir, homedirsize, folderw);
Q_strncatz(homedir, "/My Games/"FULLENGINENAME"/", homedirsize);
return true;
}
}
// FreeLibrary(shfolder);
}
return false;
}
static void Update_CreatePath (char *path)
{
char *ofs;
for (ofs = path+1 ; *ofs ; ofs++)
{
if (*ofs == '/')
{ // create the directory
*ofs = 0;
Sys_mkdir (path);
*ofs = '/';
}
}
}
//ctx is a pointer to the original frontend process
void Update_PromptedDownloaded(void *ctx, int foo)
{
if (foo == 0 && ctx)
{
PROCESS_INFORMATION childinfo;
STARTUPINFOW startinfo = {sizeof(startinfo)};
wchar_t widearg[2048];
wchar_t wideexe[2048];
char cmdline[2048];
#ifndef SERVERONLY
SetHookState(false);
Host_Shutdown ();
CloseHandle (qwclsemaphore);
SetHookState(false);
#else
SV_Shutdown();
#endif
TL_Shutdown();
narrowen(cmdline, sizeof(cmdline), GetCommandLineW());
widen(wideexe, sizeof(wideexe), ctx);
widen(widearg, sizeof(widearg), va("\"%s\" %s", (char*)ctx, COM_Parse(cmdline)));
CreateProcessW(wideexe, widearg, NULL, NULL, TRUE, 0, NULL, NULL, &startinfo, &childinfo);
Z_Free(ctx);
exit(1);
}
else
Z_Free(ctx);
}
void Update_Version_Updated(struct dl_download *dl)
{
//happens in a thread, avoid va
if (dl->file)
{
if (dl->status == DL_FINISHED)
{
char buf[8192];
unsigned int size = 0, chunk;
char pendingname[MAX_OSPATH];
vfsfile_t *pending;
Update_GetHomeDirectory(pendingname, sizeof(pendingname));
Q_strncatz(pendingname, DISTRIBUTION UPD_BUILDTYPE EXETYPE".tmp", sizeof(pendingname));
Update_CreatePath(pendingname);
pending = VFSOS_Open(pendingname, "wb");
if (!pending)
Con_Printf("Unable to write to \"%s\"\n", pendingname);
else
{
while(1)
{
chunk = VFS_READ(dl->file, buf, sizeof(buf));
if (!chunk)
break;
size += VFS_WRITE(pending, buf, chunk);
}
VFS_CLOSE(pending);
if (VFS_GETLEN(dl->file) != size)
Con_Printf("Download was the wrong size / corrupt\n");
else
{
//figure out the original binary that was executed, so we can start from scratch.
//this is to attempt to avoid the new process appearing as 'foo.tmp'. which needlessly confuses firewall rules etc.
int ffe = COM_CheckParm("--fromfrontend");
wchar_t wbinarypath[MAX_PATH];
char ubinarypath[MAX_PATH];
char *ffp;
GetModuleFileNameW(NULL, wbinarypath, countof(wbinarypath)-1);
narrowen(ubinarypath, sizeof(ubinarypath), wbinarypath);
ffp = Z_StrDup(ffe?com_argv[ffe+2]:ubinarypath);
//make it pending
MyRegSetValue(HKEY_CURRENT_USER, "Software\\"FULLENGINENAME, "pending" UPD_BUILDTYPE EXETYPE, REG_SZ, pendingname, strlen(pendingname)+1);
Key_Dest_Remove(kdm_console);
M_Menu_Prompt(Update_PromptedDownloaded, ffp, "An update was downloaded", "Restart to activate.", "", ffp?"Restart":NULL, "", "Okay");
}
}
}
else
Con_Printf("Update download failed\n");
}
}
void Update_PromptedForUpdate(void *ctx, int foo)
{
if (foo == 0)
{
struct dl_download *dl;
Con_Printf("Downloading update\n");
dl = HTTP_CL_Get(va(UPDATE_URL_BUILD, (char*)ctx), NULL, Update_Version_Updated);
dl->file = FS_OpenTemp();
#ifdef MULTITHREAD
DL_CreateThread(dl, NULL, NULL);
#endif
}
else
Con_Printf("Not downloading update\n");
}
void Update_Versioninfo_Available(struct dl_download *dl)
{
if (dl->file)
{
if (dl->status == DL_FINISHED)
{
char linebuf[1024];
while(VFS_GETS(dl->file, linebuf, sizeof(linebuf)))
{
if (!strnicmp(linebuf, "Revision: ", 10))
{
if (atoi(linebuf+10) > atoi(SVNREVISIONSTR))
{
char *revision = va("Revision %i", atoi(linebuf+10));
char *current = va("Current %i", atoi(SVNREVISIONSTR));
Con_Printf("An update is available, revision %i\n", atoi(linebuf+10));
if (COM_CheckParm("-autoupdate") || COM_CheckParm("--autoupdate"))
Update_PromptedForUpdate(dl->user_ctx, 0);
else
{
Key_Dest_Remove(kdm_console);
M_Menu_Prompt(Update_PromptedForUpdate, dl->user_ctx, "An update is available.", revision, current, "Download", "", "Ignore");
}
}
else
Con_Printf("autoupdate: already at latest version\n");
return;
}
}
}
}
}
void Update_Check(void)
{
static qboolean doneupdatecheck; //once per run
struct dl_download *dl;
if (sys_autoupdatesetting <= UPD_OFF) //not if disabled (do it once it does get enabled)
return;
if (!doneupdatecheck)
{
char *updateroot = (sys_autoupdatesetting>=UPD_TESTING)?UPDATE_URL_NIGHTLY:UPDATE_URL_TESTED;
doneupdatecheck = true;
dl = HTTP_CL_Get(va(UPDATE_URL_VERSION, updateroot), NULL, Update_Versioninfo_Available);
dl->file = FS_OpenTemp();
dl->user_ctx = updateroot;
dl->isquery = true;
#ifdef MULTITHREAD
DL_CreateThread(dl, NULL, NULL);
#endif
}
}
int Sys_GetAutoUpdateSetting(void)
{
return sys_autoupdatesetting;
}
void Sys_SetAutoUpdateSetting(int newval)
{
if (sys_autoupdatesetting == newval)
return;
sys_autoupdatesetting = newval;
MyRegSetValue(HKEY_CURRENT_USER, "Software\\"FULLENGINENAME, "AutoUpdateEnabled", REG_DWORD, &sys_autoupdatesetting, sizeof(sys_autoupdatesetting));
Update_Check();
}
BOOL DeleteFileU(const char *path)
{
wchar_t wide[2048];
return DeleteFileW(widen(wide, sizeof(wide), path));
}
BOOL MoveFileU(const char *src, const char *dst)
{
wchar_t wide1[2048];
wchar_t wide2[2048];
return MoveFileExW(widen(wide1, sizeof(wide1), src), widen(wide2, sizeof(wide2), dst), MOVEFILE_COPY_ALLOWED);
}
void Sys_SetUpdatedBinary(const char *binary) void Sys_SetUpdatedBinary(const char *binary)
{ {
#ifdef UPD_BUILDTYPE
//downloads menu has provided a new binary to use //downloads menu has provided a new binary to use
MyRegSetValue(HKEY_CURRENT_USER, "Software\\"FULLENGINENAME, "pending" UPD_BUILDTYPE EXETYPE, REG_SZ, binary, strlen(binary)+1); MyRegSetValue(HKEY_CURRENT_USER, "Software\\"FULLENGINENAME, "pending" UPD_BUILDTYPE EXETYPE, REG_SZ, binary, strlen(binary)+1);
#endif
} }
qboolean Sys_EngineCanUpdate(void)
{
char *e;
qboolean Sys_CheckUpdated(char *bindir, size_t bindirsize) //no revision info in this build, meaning its custom built and thus cannot check against the available updated versions.
if (!strcmp(SVNREVISIONSTR, "-"))
return false;
//svn revision didn't parse as an exact number. this implies it has an 'M' in it to mark it as modified.
//either way, its bad and autoupdates when we don't know what we're updating from is a bad idea.
strtoul(SVNREVISIONSTR, &e, 10);
if (!*SVNREVISIONSTR || *e)
return false;
//update blocked via commandline
if (COM_CheckParm("-noupdate") || COM_CheckParm("--noupdate") || COM_CheckParm("-noautoupdate") || COM_CheckParm("--noautoupdate"))
return false;
return true;
}
qboolean Sys_EngineWasUpdated(char *newbinary)
{ {
wchar_t wide1[2048]; wchar_t wide1[2048];
int ffe = COM_CheckParm("--fromfrontend"); wchar_t widefe[MAX_OSPATH];
wchar_t wargs[8192];
PROCESS_INFORMATION childinfo; PROCESS_INFORMATION childinfo;
STARTUPINFOW startinfo = {sizeof(startinfo)}; STARTUPINFOW startinfo = {sizeof(startinfo)};
char *e; //if we were called from a frontend, then don't chain to another, because that would be recursive, and that would be bad.
strtoul(SVNREVISIONSTR, &e, 10); if (COM_CheckParm("--fromfrontend"))
if (!*SVNREVISIONSTR || *e) //svn revision didn't parse as an exact number. this implies it has an 'M' in it to mark it as modified, or a - to mean unknown. either way, its bad and autoupdates when we don't know what we're updating from is a bad idea. return false;
sys_autoupdatesetting = UPD_UNSUPPORTED; //if we're not allowed for some other reason
else if (COM_CheckParm("-noupdate") || COM_CheckParm("--noupdate") || COM_CheckParm("-noautoupdate") || COM_CheckParm("--noautoupdate")) if (!Sys_EngineCanUpdate())
sys_autoupdatesetting = UPD_REVERT;
else if (COM_CheckParm("-autoupdate") || COM_CheckParm("--autoupdate"))
sys_autoupdatesetting = UPD_TESTING;
else
{
//favour 'tested'
sys_autoupdatesetting = MyRegGetIntValue(HKEY_CURRENT_USER, "Software\\"FULLENGINENAME, "AutoUpdateEnabled", 2);
}
if (!strcmp(SVNREVISIONSTR, "-"))
return false; //no revision info in this build, meaning its custom built and thus cannot check against the available updated versions.
else if (sys_autoupdatesetting == UPD_REVERT || sys_autoupdatesetting == UPD_UNSUPPORTED)
return false; return false;
else if (isPlugin == 1)
{
//download, but don't invoke. the caller is expected to start us up properly (once installed).
}
else if (!ffe)
{
//if we're not from the frontend (ie: we ARE the frontend), we should run the updated build instead
char pendingpath[MAX_OSPATH];
char updatedpath[MAX_OSPATH];
//FIXME: store versions instead of names startinfo.dwFlags = STARTF_USESTDHANDLES;
MyRegGetStringValue(HKEY_CURRENT_USER, "Software\\"FULLENGINENAME, "pending" UPD_BUILDTYPE EXETYPE, pendingpath, sizeof(pendingpath)); startinfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
if (*pendingpath) startinfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
{ startinfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
qboolean okay;
MyRegDeleteKeyValue(HKEY_CURRENT_USER, "Software\\"FULLENGINENAME, "pending" UPD_BUILDTYPE EXETYPE);
Update_GetHomeDirectory(updatedpath, sizeof(updatedpath));
Update_CreatePath(updatedpath);
Q_strncatz(updatedpath, "cur" UPD_BUILDTYPE EXETYPE".exe", sizeof(updatedpath));
DeleteFileU(updatedpath);
okay = MoveFileU(pendingpath, updatedpath);
if (!okay)
{ //if we just downloaded an update, we may need to wait for the existing process to close.
//sadly I'm too lazy to provide any sync mechanism (and wouldn't trust any auto-released handles or whatever), so lets just retry after a delay.
Sleep(2000);
okay = MoveFileU(pendingpath, updatedpath);
}
if (okay)
MyRegSetValue(HKEY_CURRENT_USER, "Software\\"FULLENGINENAME, UPD_BUILDTYPE EXETYPE, REG_SZ, updatedpath, strlen(updatedpath)+1);
else
{
MessageBox(NULL, va("Unable to rename %s to %s", pendingpath, updatedpath), FULLENGINENAME" autoupdate", 0);
DeleteFileU(pendingpath);
}
}
MyRegGetStringValue(HKEY_CURRENT_USER, "Software\\"FULLENGINENAME, UPD_BUILDTYPE EXETYPE, updatedpath, sizeof(updatedpath)); GetModuleFileNameW(NULL, widefe, countof(widefe)-1);
_snwprintf(wargs, countof(wargs), L"%s --fromfrontend \"%s\" \"%s\"", GetCommandLineW(), widen(wide1, sizeof(wide1), SVNREVISIONSTR), widefe);
if (*updatedpath) if (CreateProcessW(widen(wide1, sizeof(wide1), newbinary), wargs, NULL, NULL, TRUE, 0, NULL, NULL, &startinfo, &childinfo))
{ return true; //it started up, we need to die now.
wchar_t widefe[MAX_OSPATH], wargs[2048];
GetModuleFileNameW(NULL, widefe, countof(widefe)-1);
_snwprintf(wargs, countof(wargs), L"%s --fromfrontend \"%s\" \"%s\"", GetCommandLineW(), widen(wide1, sizeof(wide1), SVNREVISIONSTR), widefe);
if (CreateProcessW(widen(wide1, sizeof(wide1), updatedpath), wargs, NULL, NULL, TRUE, 0, NULL, NULL, &startinfo, &childinfo))
return true;
}
}
else
{
// char frontendpath[MAX_OSPATH];
//com_argv[ffe+1] is frontend revision
//com_argv[ffe+2] is frontend location
if (atoi(com_argv[ffe+1]) > atoi(SVNREVISIONSTR))
{
//ping-pong it back, to make sure we're running the most recent version.
// GetModuleFileName(NULL, frontendpath, sizeof(frontendpath)-1);
if (CreateProcessW(widen(wide1, sizeof(wide1), com_argv[ffe+2]), GetCommandLineW(), NULL, NULL, TRUE, 0, NULL, NULL, &startinfo, &childinfo))
return true;
}
if (com_argv[ffe+2])
{
com_argv[0] = com_argv[ffe+2];
Q_strncpyz(bindir, com_argv[0], bindirsize);
*COM_SkipPath(bindir) = 0;
}
} return false; //failure!
return false;
}
#else
#ifdef HAVEAUTOUPDATE
int Sys_GetAutoUpdateSetting(void)
{
return -1;
}
void Sys_SetAutoUpdateSetting(int newval)
{
}
void Sys_SetUpdatedBinary(const char *binary)
{
}
#endif
qboolean Sys_CheckUpdated(char *bindir, size_t bindirsize)
{
return false;
}
void Update_Check(void)
{
} }
#endif #endif
@ -3475,6 +3189,13 @@ LRESULT CALLBACK NoCloseWindowProc(HWND w, UINT m, WPARAM wp, LPARAM lp)
return DefWindowProc(w, m, wp, lp); return DefWindowProc(w, m, wp, lp);
} }
BOOL CopyFileU(const char *src, const char *dst, BOOL bFailIfExists)
{
wchar_t wide1[2048];
wchar_t wide2[2048];
return CopyFileW(widen(wide1, sizeof(wide1), src), widen(wide2, sizeof(wide2), dst), bFailIfExists);
}
void FS_CreateBasedir(const char *path); void FS_CreateBasedir(const char *path);
qboolean Sys_DoInstall(void) qboolean Sys_DoInstall(void)
{ {
@ -4048,9 +3769,6 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
isPlugin = 0; isPlugin = 0;
} }
if (Sys_CheckUpdated(bindir, sizeof(bindir)))
return true;
if (COM_CheckParm("-register_types")) if (COM_CheckParm("-register_types"))
{ {
Sys_DoFileAssociations(1); Sys_DoFileAssociations(1);
@ -4205,6 +3923,10 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
if (!Sys_Startup_CheckMem(&parms)) if (!Sys_Startup_CheckMem(&parms))
Sys_Error ("Not enough memory free; check disk space\n"); Sys_Error ("Not enough memory free; check disk space\n");
// FS_ChangeGame(NULL, true, true);
// if (Sys_CheckUpdated(bindir, sizeof(bindir)))
// return true;
#ifndef CLIENTONLY #ifndef CLIENTONLY
if (isDedicated) //compleate denial to switch to anything else - many of the client structures are not initialized. if (isDedicated) //compleate denial to switch to anything else - many of the client structures are not initialized.
@ -4264,8 +3986,6 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
fflush(stdout); fflush(stdout);
} }
Update_Check();
/* main window message loop */ /* main window message loop */
while (1) while (1)
{ {

View File

@ -613,49 +613,58 @@ void Validation_Auto_Response(int playernum, char *s)
static float cmdlineresponsetime; static float cmdlineresponsetime;
static float scriptsresponsetime; static float scriptsresponsetime;
if (!strncmp(s, "f_version", 9) && versionresponsetime < Sys_DoubleTime()) //respond to it. //quakeworld tends to use f_*
//netquake uses the slightly more guessable q_* form
if (!strncmp(s, "f_", 2))
s+=2;
else if (!strncmp(s, "q_", 2))
s+=2;
else
return;
if (!strncmp(s, "version", 7) && versionresponsetime < Sys_DoubleTime()) //respond to it.
{ {
Validation_Version(); Validation_Version();
versionresponsetime = Sys_DoubleTime() + 5; versionresponsetime = Sys_DoubleTime() + 5;
} }
else if (cl.spectator) else if (cl.spectator)
return; return;
else if (!strncmp(s, "f_server", 8) && serverresponsetime < Sys_DoubleTime()) //respond to it. else if (!strncmp(s, "server", 6) && serverresponsetime < Sys_DoubleTime()) //respond to it.
{ {
Validation_Server(); Validation_Server();
serverresponsetime = Sys_DoubleTime() + 5; serverresponsetime = Sys_DoubleTime() + 5;
} }
else if (!strncmp(s, "f_system", 8) && systemresponsetime < Sys_DoubleTime()) else if (!strncmp(s, "system", 6) && systemresponsetime < Sys_DoubleTime())
{ {
Validation_System(); Validation_System();
systemresponsetime = Sys_DoubleTime() + 5; systemresponsetime = Sys_DoubleTime() + 5;
} }
else if (!strncmp(s, "f_cmdline", 9) && cmdlineresponsetime < Sys_DoubleTime()) else if (!strncmp(s, "cmdline", 7) && cmdlineresponsetime < Sys_DoubleTime())
{ {
Validation_CmdLine(); Validation_CmdLine();
cmdlineresponsetime = Sys_DoubleTime() + 5; cmdlineresponsetime = Sys_DoubleTime() + 5;
} }
else if (!strncmp(s, "f_fakeshaft", 11) && fakeshaftresponsetime < Sys_DoubleTime()) else if (!strncmp(s, "fakeshaft", 9) && fakeshaftresponsetime < Sys_DoubleTime())
{ {
Validation_FakeShaft(); Validation_FakeShaft();
fakeshaftresponsetime = Sys_DoubleTime() + 5; fakeshaftresponsetime = Sys_DoubleTime() + 5;
} }
else if (!strncmp(s, "f_modified", 10) && modifiedresponsetime < Sys_DoubleTime()) //respond to it. else if (!strncmp(s, "modified", 8) && modifiedresponsetime < Sys_DoubleTime()) //respond to it.
{ {
Validation_FilesModified(); Validation_FilesModified();
modifiedresponsetime = Sys_DoubleTime() + 5; modifiedresponsetime = Sys_DoubleTime() + 5;
} }
else if (!strncmp(s, "f_scripts", 9) && scriptsresponsetime < Sys_DoubleTime()) else if (!strncmp(s, "scripts", 7) && scriptsresponsetime < Sys_DoubleTime())
{ {
Validation_Scripts(); Validation_Scripts();
scriptsresponsetime = Sys_DoubleTime() + 5; scriptsresponsetime = Sys_DoubleTime() + 5;
} }
else if (!strncmp(s, "f_skins", 7) && skinsresponsetime < Sys_DoubleTime()) //respond to it. else if (!strncmp(s, "skins", 5) && skinsresponsetime < Sys_DoubleTime()) //respond to it.
{ {
Validation_Skins(); Validation_Skins();
skinsresponsetime = Sys_DoubleTime() + 5; skinsresponsetime = Sys_DoubleTime() + 5;
} }
else if (!strncmp(s, "f_ruleset", 9) && rulesetresponsetime < Sys_DoubleTime()) else if (!strncmp(s, "ruleset", 7) && rulesetresponsetime < Sys_DoubleTime())
{ {
if (1) if (1)
Validation_AllChecks(); Validation_AllChecks();

View File

@ -1793,8 +1793,8 @@ void TP_SearchForMsgTriggers (char *s, int level)
&& t->string[0] && strstr(s, t->string)) && t->string[0] && strstr(s, t->string))
{ {
if (level == PRINT_CHAT && ( if (level == PRINT_CHAT && (
strstr (s, "f_version") || strstr (s, "f_system") || strstr (s, "_version") || strstr (s, "_system") ||
strstr (s, "f_speed") || strstr (s, "f_modified") || strstr (s, "f_ruleset"))) strstr (s, "_speed") || strstr (s, "_modified") || strstr (s, "_ruleset")))
continue; // don't let llamas fake proxy replies continue; // don't let llamas fake proxy replies
string = Cmd_AliasExist (t->name, RESTRICT_LOCAL); string = Cmd_AliasExist (t->name, RESTRICT_LOCAL);

View File

@ -108,13 +108,14 @@ cvar_t gameversion = CVARFD("gameversion","", CVAR_SERVERINFO, "gamecode version
cvar_t gameversion_min = CVARD("gameversion_min","", "gamecode version for server browsers"); cvar_t gameversion_min = CVARD("gameversion_min","", "gamecode version for server browsers");
cvar_t gameversion_max = CVARD("gameversion_max","", "gamecode version for server browsers"); cvar_t gameversion_max = CVARD("gameversion_max","", "gamecode version for server browsers");
cvar_t fs_gamename = CVARAFD("com_fullgamename", NULL, "fs_gamename", CVAR_NOSET, "The filesystem is trying to run this game"); cvar_t fs_gamename = CVARAFD("com_fullgamename", NULL, "fs_gamename", CVAR_NOSET, "The filesystem is trying to run this game");
cvar_t fs_downloads_url = CVARFD("fs_downloads_url", NULL, CVAR_NOTFROMSERVER|CVAR_NOSAVE|CVAR_NOSET, "The URL of a package updates list.");
cvar_t com_protocolname = CVARAD("com_protocolname", NULL, "com_gamename", "The protocol game name used for dpmaster queries. For compatibility with DP, you can set this to 'DarkPlaces-Quake' in order to be listed in DP's master server, and to list DP servers."); cvar_t com_protocolname = CVARAD("com_protocolname", NULL, "com_gamename", "The protocol game name used for dpmaster queries. For compatibility with DP, you can set this to 'DarkPlaces-Quake' in order to be listed in DP's master server, and to list DP servers.");
cvar_t com_parseutf8 = CVARD("com_parseutf8", "1", "Interpret console messages/playernames/etc as UTF-8. Requires special fonts. -1=iso 8859-1. 0=quakeascii(chat uses high chars). 1=utf8, revert to ascii on decode errors. 2=utf8 ignoring errors"); //1 parse. 2 parse, but stop parsing that string if a char was malformed. cvar_t com_parseutf8 = CVARD("com_parseutf8", "1", "Interpret console messages/playernames/etc as UTF-8. Requires special fonts. -1=iso 8859-1. 0=quakeascii(chat uses high chars). 1=utf8, revert to ascii on decode errors. 2=utf8 ignoring errors"); //1 parse. 2 parse, but stop parsing that string if a char was malformed.
cvar_t com_parseezquake = CVARD("com_parseezquake", "0", "Treat chevron chars from configs as a per-character flag. You should use this only for compat with nquake's configs."); cvar_t com_parseezquake = CVARD("com_parseezquake", "0", "Treat chevron chars from configs as a per-character flag. You should use this only for compat with nquake's configs.");
cvar_t com_highlightcolor = CVARD("com_highlightcolor", STRINGIFY(COLOR_RED), "ANSI colour to be used for highlighted text, used when com_parseutf8 is active."); cvar_t com_highlightcolor = CVARD("com_highlightcolor", STRINGIFY(COLOR_RED), "ANSI colour to be used for highlighted text, used when com_parseutf8 is active.");
cvar_t com_nogamedirnativecode = CVARFD("com_nogamedirnativecode", "1", CVAR_NOTFROMSERVER, FULLENGINENAME" blocks all downloads of files with a .dll or .so extension, however other engines (eg: ezquake and fodquake) do not - this omission can be used to trigger delayed eremote exploits in any engine (including "DISTRIBUTION") which is later run from the same gamedir.\nQuake2, Quake3(when debugging), and KTX typically run native gamecode from within gamedirs, so if you wish to run any of these games you will need to ensure this cvar is changed to 0, as well as ensure that you don't run unsafe clients.\n"); cvar_t com_nogamedirnativecode = CVARFD("com_nogamedirnativecode", "1", CVAR_NOTFROMSERVER, FULLENGINENAME" blocks all downloads of files with a .dll or .so extension, however other engines (eg: ezquake and fodquake) do not - this omission can be used to trigger delayed eremote exploits in any engine (including "DISTRIBUTION") which is later run from the same gamedir.\nQuake2, Quake3(when debugging), and KTX typically run native gamecode from within gamedirs, so if you wish to run any of these games you will need to ensure this cvar is changed to 0, as well as ensure that you don't run unsafe clients.\n");
cvar_t sys_platform = CVAR("sys_platform", PLATFORM); cvar_t sys_platform = CVAR("sys_platform", PLATFORM);
cvar_t pm_downloads_url = CVARFD("pm_downloads_url", NULL, CVAR_NOTFROMSERVER|CVAR_NOSAVE|CVAR_NOSET, "The URL of a package updates list."); //read from the default.fmf
cvar_t pm_autoupdate = CVARFD("pm_autoupdate", "1", CVAR_NOTFROMSERVER|CVAR_NOSAVE|CVAR_NOSET, "0: off.\n1: enabled (stable only).\n2: enabled (unstable).\nNote that autoupdate will still prompt the user to actually apply the changes."); //read from the package list only.
qboolean com_modified; // set true if using non-id files qboolean com_modified; // set true if using non-id files
@ -5553,6 +5554,14 @@ void COM_Init (void)
nullentitystate.solidsize = 0;//ES_SOLID_BSP; nullentitystate.solidsize = 0;//ES_SOLID_BSP;
} }
void COM_Shutdown (void)
{
#ifdef LOADERTHREAD
COM_DestroyWorkerThread();
#endif
COM_BiDi_Shutdown();
FS_Shutdown();
}
/* /*
============ ============
@ -5596,7 +5605,7 @@ int memsearch (qbyte *start, int count, int search)
} }
#ifdef NQPROT #ifdef NQPROT
//for compat with dpp7 protocols //for compat with dpp7 protocols, or dp gamecode that neglects to properly precache particles.
void COM_Effectinfo_Enumerate(int (*cb)(const char *pname)) void COM_Effectinfo_Enumerate(int (*cb)(const char *pname))
{ {
int i; int i;

View File

@ -374,6 +374,7 @@ int COM_CheckParm (const char *parm); //WARNING: Legacy arguments should be list
int COM_CheckNextParm (const char *parm, int last); int COM_CheckNextParm (const char *parm, int last);
void COM_AddParm (const char *parm); void COM_AddParm (const char *parm);
void COM_Shutdown (void);
void COM_Init (void); void COM_Init (void);
void COM_InitArgv (int argc, const char **argv); void COM_InitArgv (int argc, const char **argv);
void COM_ParsePlusSets (qboolean docbuf); void COM_ParsePlusSets (qboolean docbuf);
@ -730,6 +731,7 @@ void Log_String (logtype_t lognum, char *s);
void Con_Log (char *s); void Con_Log (char *s);
void Log_Logfile_f (void); void Log_Logfile_f (void);
void Log_Init(void); void Log_Init(void);
void IPLog_Add(const char *ip, const char *name); //for associating player ip addresses with names.
/*used by and for botlib and q3 gamecode*/ /*used by and for botlib and q3 gamecode*/

View File

@ -2955,7 +2955,6 @@ typedef struct {
const char *manifestfile; const char *manifestfile;
} gamemode_info_t; } gamemode_info_t;
const gamemode_info_t gamemode_info[] = { const gamemode_info_t gamemode_info[] = {
#define MASTER_PREFIX "FTE-"
//note that there is no basic 'fte' gamemode, this is because we aim for network compatability. Darkplaces-Quake is the closest we get. //note that there is no basic 'fte' gamemode, this is because we aim for network compatability. Darkplaces-Quake is the closest we get.
//this is to avoid having too many gamemodes anyway. //this is to avoid having too many gamemodes anyway.
@ -2964,13 +2963,13 @@ const gamemode_info_t gamemode_info[] = {
//for quake, we also allow extracting all files from paks. some people think it loads faster that way or something. //for quake, we also allow extracting all files from paks. some people think it loads faster that way or something.
//cmdline switch exename protocol name(dpmaster) identifying file exec dir1 dir2 dir3 dir(fte) full name //cmdline switch exename protocol name(dpmaster) identifying file exec dir1 dir2 dir3 dir(fte) full name
{"-quake", "q1", MASTER_PREFIX"Quake", {"id1/pak0.pak", "id1/quake.rc"},QCFG, {"id1", "qw", "*fte"}, "Quake", "https://fte.triptohell.info/downloadables.php" /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/}, {"-quake", "q1", "FTE-Quake DarkPlaces-Quake", {"id1/pak0.pak", "id1/quake.rc"},QCFG, {"id1", "qw", "*fte"}, "Quake", "https://fte.triptohell.info/downloadables.php" /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/},
//quake's mission packs should not be favoured over the base game nor autodetected //quake's mission packs should not be favoured over the base game nor autodetected
//third part mods also tend to depend upon the mission packs for their huds, even if they don't use any other content. //third part mods also tend to depend upon the mission packs for their huds, even if they don't use any other content.
//and q2 also has a rogue/pak0.pak file that we don't want to find and cause quake2 to look like dissolution of eternity //and q2 also has a rogue/pak0.pak file that we don't want to find and cause quake2 to look like dissolution of eternity
//so just make these require the same files as good ol' quake. //so just make these require the same files as good ol' quake.
{"-hipnotic", "hipnotic", MASTER_PREFIX"Hipnotic",{"id1/pak0.pak","id1/quake.rc"},QCFG, {"id1", "qw", "hipnotic", "*fte"}, "Quake: Scourge of Armagon"}, {"-hipnotic", "hipnotic", "FTE-Hipnotic",{"id1/pak0.pak","id1/quake.rc"},QCFG, {"id1", "qw", "hipnotic", "*fte"}, "Quake: Scourge of Armagon"},
{"-rogue", "rogue", MASTER_PREFIX"Rogue", {"id1/pak0.pak","id1/quake.rc"},QCFG, {"id1", "qw", "rogue", "*fte"}, "Quake: Dissolution of Eternity"}, {"-rogue", "rogue", "FTE-Rogue", {"id1/pak0.pak","id1/quake.rc"},QCFG, {"id1", "qw", "rogue", "*fte"}, "Quake: Dissolution of Eternity"},
//various quake-based standalone mods. //various quake-based standalone mods.
{"-nexuiz", "nexuiz", "Nexuiz", {"nexuiz.exe"}, NEXCFG, {"data", "*ftedata"}, "Nexuiz"}, {"-nexuiz", "nexuiz", "Nexuiz", {"nexuiz.exe"}, NEXCFG, {"data", "*ftedata"}, "Nexuiz"},
@ -3968,7 +3967,7 @@ void FS_Shutdown(void)
fs_thread_mutex = NULL; fs_thread_mutex = NULL;
Cvar_SetEngineDefault(&fs_gamename, NULL); Cvar_SetEngineDefault(&fs_gamename, NULL);
Cvar_SetEngineDefault(&fs_downloads_url, NULL); Cvar_SetEngineDefault(&pm_downloads_url, NULL);
Cvar_SetEngineDefault(&com_protocolname, NULL); Cvar_SetEngineDefault(&com_protocolname, NULL);
} }
@ -5118,11 +5117,11 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
if (reloadconfigs) if (reloadconfigs)
{ {
Cvar_SetEngineDefault(&fs_gamename, man->formalname?man->formalname:"FTE"); Cvar_SetEngineDefault(&fs_gamename, man->formalname?man->formalname:"FTE");
Cvar_SetEngineDefault(&fs_downloads_url, man->downloadsurl?man->downloadsurl:""); Cvar_SetEngineDefault(&pm_downloads_url, man->downloadsurl?man->downloadsurl:"");
Cvar_SetEngineDefault(&com_protocolname, man->protocolname?man->protocolname:"FTE"); Cvar_SetEngineDefault(&com_protocolname, man->protocolname?man->protocolname:"FTE");
//FIXME: flag this instead and do it after a delay? //FIXME: flag this instead and do it after a delay?
Cvar_ForceSet(&fs_gamename, fs_gamename.enginevalue); Cvar_ForceSet(&fs_gamename, fs_gamename.enginevalue);
Cvar_ForceSet(&fs_downloads_url, fs_downloads_url.enginevalue); Cvar_ForceSet(&pm_downloads_url, pm_downloads_url.enginevalue);
Cvar_ForceSet(&com_protocolname, com_protocolname.enginevalue); Cvar_ForceSet(&com_protocolname, com_protocolname.enginevalue);
vidrestart = false; vidrestart = false;
@ -5567,7 +5566,8 @@ void COM_InitFilesystem (void)
Cvar_Register(&cfg_reload_on_gamedir, "Filesystem"); Cvar_Register(&cfg_reload_on_gamedir, "Filesystem");
Cvar_Register(&com_fs_cache, "Filesystem"); Cvar_Register(&com_fs_cache, "Filesystem");
Cvar_Register(&fs_gamename, "Filesystem"); Cvar_Register(&fs_gamename, "Filesystem");
Cvar_Register(&fs_downloads_url, "Filesystem"); Cvar_Register(&pm_downloads_url, "Filesystem");
Cvar_Register(&pm_autoupdate, "Filesystem");
Cvar_Register(&com_protocolname, "Server Info"); Cvar_Register(&com_protocolname, "Server Info");
Cvar_Register(&fs_game, "Filesystem"); Cvar_Register(&fs_game, "Filesystem");
#ifdef Q2SERVER #ifdef Q2SERVER
@ -5697,10 +5697,6 @@ void COM_InitFilesystem (void)
fs_readonly = COM_CheckParm("-readonly"); fs_readonly = COM_CheckParm("-readonly");
fs_thread_mutex = Sys_CreateMutex(); fs_thread_mutex = Sys_CreateMutex();
#ifdef PLUGINS
Plug_Initialise(false);
#endif
} }

View File

@ -70,6 +70,7 @@ void FS_AddHashedPackage(searchpath_t **oldpaths, const char *parent_pure, const
void PM_LoadPackages(searchpath_t **oldpaths, const char *parent_pure, const char *parent_logical, searchpath_t *search, unsigned int loadstuff, int minpri, int maxpri); void PM_LoadPackages(searchpath_t **oldpaths, const char *parent_pure, const char *parent_logical, searchpath_t *search, unsigned int loadstuff, int minpri, int maxpri);
int PM_IsApplying(void); int PM_IsApplying(void);
void PM_ManifestPackage(const char *name, qboolean doinstall); void PM_ManifestPackage(const char *name, qboolean doinstall);
qboolean PM_FindUpdatedEngine(char *syspath, size_t syspathsize); //names the engine we should be running
void Menu_Download_Update(void); void Menu_Download_Update(void);
int FS_EnumerateKnownGames(qboolean (*callback)(void *usr, ftemanifest_t *man), void *usr); int FS_EnumerateKnownGames(qboolean (*callback)(void *usr, ftemanifest_t *man), void *usr);

View File

@ -325,6 +325,89 @@ void SV_Fraglogfile_f (void)
} }
*/ */
static qboolean IPLog_Merge_File(const char *fname)
{
char ip[MAX_ADR_SIZE];
char name[256];
char line[1024];
vfsfile_t *f;
if (!*fname)
fname = "iplog.txt";
f = FS_OpenVFS(fname, "rb", FS_GAME);
if (!f)
return false;
if (!Q_strcasecmp(COM_FileExtension(fname, name, sizeof(name)), ".dat"))
{ //we don't write this format because of it being limited to ipv4, as well as player name lengths
while (VFS_READ(f, line, 20) == 20)
{
Q_snprintfz(ip, sizeof(ip), "%i.%i.%i.%i", (qbyte)line[0], (qbyte)line[1], (qbyte)line[2], (qbyte)line[3]);
memcpy(name, line+4, 20-4);
name[20-4] = 0;
IPLog_Add(ip, name);
}
}
else
{
while (VFS_GETS(f, line, sizeof(line)-1))
{
//whether the name contains quotes or what is an awkward one.
//we always write quotes (including string markup to avoid issues)
//dp doesn't, and our parser is lazy, so its possible we'll get gibberish that way
if (COM_ParseOut(COM_ParseOut(line, ip, sizeof(ip)), name, sizeof(name)))
IPLog_Add(ip, name);
}
}
VFS_CLOSE(f);
return true;
}
void IPLog_Add(const char *ipstr, const char *name)
{
if (*ipstr != '[' && *ipstr < '0' && *ipstr > '9')
return;
//might be x.y.z.w:port
//might be x.y.z.FUCKED
//might be x.y.z.0/24
//might be [::]:port
//might be [::]/bits
//or other ways to express an ip address
//note that ipv4 addresses should be converted to ipv6 ::ffff:x.y.z.w format for internal use or something, then this code only needs to deal with a single 128bit address format.
//ipv6 addresses generally only need 64bits to identify a user's home router, the other 64bits are generally 'just' to avoid nats.
//ignore ipx addresses, I doubt anyone will ever actually use it, and even if they do its just lans.
}
static void IPLog_Identify_f(void)
{
// const char *nameorip = Cmd_Argv(1);
Con_Printf("Not yet implemented\n");
//if *, use a mask that includes all ips
//try to parse as an ip
//if server is active, walk players to see if there's a name match to get their address and guess an address mask
//else if client is active, walk players to see if there's a name match, to get their address+mask if known via nq hacks
//look for matches
}
static void IPLog_Dump_f(void)
{
const char *fname = Cmd_Argv(1);
if (!*fname)
fname = "iplog.txt";
#if 1
Con_Printf("Not yet implemented\n");
#else
vfsfile_t *f = FS_OpenVFS(fname, "wb", FS_GAMEONLY);
VFS_PRINTF(f, "//generated by "FULLENGINENAME"\n", foo->ip, foo->name);
for (foo = first; foo; foo = foo->next)
{
char buf[1024];
VFS_PRINTF(f, "%s %s\n", foo->ip, COM_QuotedString(foo->name, buf, sizeof(buf), false));
}
VFS_CLOSE(f);
#endif
}
static void IPLog_Merge_f(void)
{
const char *fname = Cmd_Argv(1);
if (!IPLog_Merge_File(fname))
Con_Printf("unable to read %s\n", fname);
}
void Log_Init(void) void Log_Init(void)
{ {
@ -346,6 +429,10 @@ void Log_Init(void)
Cmd_AddCommand("logfile", Log_Logfile_f); Cmd_AddCommand("logfile", Log_Logfile_f);
Cmd_AddCommand("identify", IPLog_Identify_f);
Cmd_AddCommand("ipmerge", IPLog_Merge_f);
Cmd_AddCommand("ipdump", IPLog_Dump_f);
// cmd line options, debug options // cmd line options, debug options
#ifdef CRAZYDEBUGGING #ifdef CRAZYDEBUGGING
Cvar_ForceSet(&log_enable[LOG_CONSOLE], "1"); Cvar_ForceSet(&log_enable[LOG_CONSOLE], "1");

View File

@ -2510,24 +2510,39 @@ qboolean FTENET_Generic_GetPacket(ftenet_generic_connection_t *con)
return false; return false;
if (err == NET_EMSGSIZE) if (err == NET_EMSGSIZE)
{ {
SockadrToNetadr (&from, &net_from); static unsigned int resettime;
Con_TPrintf ("Warning: Oversize packet from %s\n", unsigned int curtime = Sys_Milliseconds();
if (curtime-resettime >= 5000) //throttle prints to once per 5 secs (even if they're about different clients, yay ddos)
{
SockadrToNetadr (&from, &net_from);
Con_TPrintf ("Warning: Oversize packet from %s\n",
NET_AdrToString (adr, sizeof(adr), &net_from)); NET_AdrToString (adr, sizeof(adr), &net_from));
}
return false; return false;
} }
if (err == NET_ECONNABORTED || err == NET_ECONNRESET) if (err == NET_ECONNABORTED || err == NET_ECONNRESET)
{ {
Con_TPrintf ("Connection lost or aborted\n"); //server died/connection lost. static unsigned int resettime;
#ifndef SERVERONLY unsigned int curtime = Sys_Milliseconds();
if (cls.state != ca_disconnected && !con->islisten) if (curtime-resettime >= 5000 || err == NET_ECONNRESET) //throttle prints to once per 5 secs (even if they're about different clients, yay ddos)
{ {
if (cls.lastarbiatarypackettime+5 < Sys_DoubleTime()) //too many mvdsv if (err == NET_ECONNABORTED)
Cbuf_AddText("disconnect\nreconnect\n", RESTRICT_LOCAL); //retry connecting. Con_TPrintf ("Connection lost or aborted (%s)\n", NET_AdrToString (adr, sizeof(adr), &net_from)); //server died/connection lost.
else else
Con_Printf("Packet was not delivered - server might be badly configured\n"); Con_TPrintf ("Connection lost or aborted\n"); //server died/connection lost.
return false; resettime = curtime;
} #ifndef SERVERONLY
//fixme: synthesise a reset packet for the caller to handle? "\xff\xff\xff\xffreset" ?
if (cls.state != ca_disconnected && !con->islisten)
{
if (cls.lastarbiatarypackettime+5 < Sys_DoubleTime()) //too many mvdsv
Cbuf_AddText("disconnect\nreconnect\n", RESTRICT_LOCAL); //retry connecting.
else
Con_Printf("Packet was not delivered - server might be badly configured\n");
return false;
}
#endif #endif
}
return false; return false;
} }
@ -2535,7 +2550,7 @@ qboolean FTENET_Generic_GetPacket(ftenet_generic_connection_t *con)
Con_Printf ("NET_GetPacket: Error (%i): %s\n", err, strerror(err)); Con_Printf ("NET_GetPacket: Error (%i): %s\n", err, strerror(err));
return false; return false;
} }
SockadrToNetadr (&from, &net_from); SockadrToNetadr (&from, &net_from);
net_message.packing = SZ_RAWBYTES; net_message.packing = SZ_RAWBYTES;
net_message.currentbit = 0; net_message.currentbit = 0;

View File

@ -168,23 +168,21 @@ qboolean NPQTV_Sys_Startup(int argc, char *argv[]);
void NPQTV_Sys_MainLoop(void); void NPQTV_Sys_MainLoop(void);
#endif #endif
#define UPD_UNSUPPORTED -1 #define UPD_OFF 0
#define UPD_REVERT 0 #define UPD_STABLE 1
#define UPD_OFF 1 #define UPD_TESTING 2
#define UPD_STABLE 2
#define UPD_TESTING 3
#if defined(WEBCLIENT) && defined(_WIN32) && !defined(SERVERONLY) #if defined(WEBCLIENT) && defined(_WIN32) && !defined(SERVERONLY)
int StartLocalServer(int close); int StartLocalServer(int close);
#define HAVEAUTOUPDATE #define HAVEAUTOUPDATE
int Sys_GetAutoUpdateSetting(void); void Sys_SetUpdatedBinary(const char *fname); //legacy, so old build can still deal with updates properly
void Sys_SetAutoUpdateSetting(int newval); qboolean Sys_EngineCanUpdate(void); //says whether the system code is able to invoke new binaries properly
void Sys_SetUpdatedBinary(const char *fname); qboolean Sys_EngineWasUpdated(char *newbinary); //invoke the given system-path binary
#else #else
#define Sys_GetAutoUpdateSetting() UPD_UNSUPPORTED #define Sys_EngineCanUpdate() false
#define Sys_SetAutoUpdateSetting(n)
#define Sys_SetUpdatedBinary(n) #define Sys_SetUpdatedBinary(n)
#define Sys_EngineWasUpdated(n) false
#endif #endif
void Sys_Init (void); void Sys_Init (void);

View File

@ -663,7 +663,7 @@
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
Optimization="0" Optimization="0"
AdditionalIncludeDirectories="../libs/speex,..\client,../libs/freetype2/include,../common,../server,../gl,../sw,../qclib,../libs,../libs/dxsdk7/include" AdditionalIncludeDirectories="../libs/speex,..\client,../libs/freetype2/include,../common,../server,../gl,../sw,../qclib,../libs,../libs/dxsdk7/include"
PreprocessorDefinitions="_DEBUG;GLQUAKE;VKQUAKE;WIN32;_WINDOWS;BOTLIB_STATIC;USE_MSVCRT_DEBUG" PreprocessorDefinitions="_DEBUG;GLQUAKE;VKQUAKE;WIN32;_WINDOWS;BOTLIB_STATIC;USE_MSVCRT_DEBUG;DYNAMIC_SDL"
BasicRuntimeChecks="3" BasicRuntimeChecks="3"
SmallerTypeCheck="true" SmallerTypeCheck="true"
RuntimeLibrary="1" RuntimeLibrary="1"

View File

@ -211,6 +211,11 @@ void GL_Set2D (qboolean flipped)
qglLoadMatrixf(r_refdef.m_view); qglLoadMatrixf(r_refdef.m_view);
} }
if (flipped)
r_refdef.flipcull = SHADER_CULL_FLIP;
else
r_refdef.flipcull = 0;
GL_SetShaderState2D(true); GL_SetShaderState2D(true);
} }

View File

@ -11508,6 +11508,7 @@ void PR_DumpPlatform_f(void)
{"m_init", "void()", MENU}, {"m_init", "void()", MENU},
{"m_shutdown", "void()", MENU}, {"m_shutdown", "void()", MENU},
{"m_draw", "void(vector screensize)", MENU, "Provides the menuqc with a chance to draw. Will be called even if the menu does not have focus, so be sure to avoid that. COMPAT: screensize is not provided in DP."}, {"m_draw", "void(vector screensize)", MENU, "Provides the menuqc with a chance to draw. Will be called even if the menu does not have focus, so be sure to avoid that. COMPAT: screensize is not provided in DP."},
{"m_drawloading", "void(vector screensize)", MENU, "Additional drawing function to draw loading screen overlays."},
{"m_keydown", "void(float scan, float chr)", MENU}, {"m_keydown", "void(float scan, float chr)", MENU},
{"m_keyup", "void(float scan, float chr)", MENU}, {"m_keyup", "void(float scan, float chr)", MENU},
{"m_toggle", "void(float wantmode)", MENU}, {"m_toggle", "void(float wantmode)", MENU},

View File

@ -1765,6 +1765,14 @@ static void SV_Status_f (void)
int columns = 80; int columns = 80;
extern cvar_t sv_listen_qw, sv_listen_nq, sv_listen_dp, sv_listen_q3; extern cvar_t sv_listen_qw, sv_listen_nq, sv_listen_dp, sv_listen_q3;
#ifndef SERVERONLY
if (!sv.state && cls.state >= ca_connected && !cls.demoplayback && cls.protocol == CP_NETQUAKE)
{ //nq can normally forward the request to the server.
Cmd_ForwardToServer();
return;
}
#endif
if (sv_redirected != RD_OBLIVION && (sv_redirected != RD_NONE if (sv_redirected != RD_OBLIVION && (sv_redirected != RD_NONE
#ifndef SERVERONLY #ifndef SERVERONLY
|| (vid.width < 68*8 && qrenderer != QR_NONE) || (vid.width < 68*8 && qrenderer != QR_NONE)

View File

@ -1537,7 +1537,7 @@ qboolean SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizeb
MSG_WriteByte (msg, svcfte_updateentities); MSG_WriteByte (msg, svcfte_updateentities);
if (ISNQCLIENT(client) && (client->fteprotocolextensions2 & PEXT2_PREDINFO)) if (ISNQCLIENT(client) && (client->fteprotocolextensions2 & PEXT2_PREDINFO))
{ {
MSG_WriteShort(msg, client->last_sequence); MSG_WriteShort(msg, client->last_sequence&0xffff);
} }
// Con_Printf("Gen sequence %i\n", sequence); // Con_Printf("Gen sequence %i\n", sequence);
MSG_WriteFloat(msg, sv.world.physicstime); MSG_WriteFloat(msg, sv.world.physicstime);

View File

@ -2199,6 +2199,8 @@ client_t *SVC_DirectConnect(void)
{ {
{"FITZ", 1u<<SCP_FITZ666}, //dp doesn't support this, but this is for potential compat if other engines use this handshake {"FITZ", 1u<<SCP_FITZ666}, //dp doesn't support this, but this is for potential compat if other engines use this handshake
{"666", 1u<<SCP_FITZ666}, //dp doesn't support this, but this is for potential compat if other engines use this handshake {"666", 1u<<SCP_FITZ666}, //dp doesn't support this, but this is for potential compat if other engines use this handshake
{"RMQ", 1u<<SCP_FITZ666}, //fte doesn't distinguish, but assumes clients will support both
{"999", 1u<<SCP_FITZ666}, //fte doesn't distinguish, but assumes clients will support both
{"DP7", 1u<<SCP_DARKPLACES7}, {"DP7", 1u<<SCP_DARKPLACES7},
{"DP6", 1u<<SCP_DARKPLACES6}, {"DP6", 1u<<SCP_DARKPLACES6},
{"DP5", 0}, {"DP5", 0},
@ -2206,10 +2208,10 @@ client_t *SVC_DirectConnect(void)
{"DP3", 0}, {"DP3", 0},
{"DP2", 0}, {"DP2", 0},
{"DP1", 0}, {"DP1", 0},
{"QW", 0}, //mixing protocols doesn't make sense, and would just confuse the client.
{"QUAKEDP", 1u<<SCP_NETQUAKE}, {"QUAKEDP", 1u<<SCP_NETQUAKE},
{"QUAKE", 1u<<SCP_NETQUAKE}, {"QUAKE", 1u<<SCP_NETQUAKE},
{"QW", 0}, //mixing protocols doesn't make sense, and would just confuse the client. {"NEHAHRAMOVIE", 1u<<SCP_NETQUAKE},
{"NEHAHRAMOVIE", 0},
{"NEHAHRABJP", 0}, {"NEHAHRABJP", 0},
{"NEHAHRABJP2", 0}, {"NEHAHRABJP2", 0},
{"NEHAHRABJP3", 1u<<SCP_BJP3}, {"NEHAHRABJP3", 1u<<SCP_BJP3},
@ -3108,6 +3110,9 @@ client_t *SVC_DirectConnect(void)
SSV_SavePlayerStats(newcl, 0); SSV_SavePlayerStats(newcl, 0);
#endif #endif
if (Q_strncasecmp(newcl->name, "unconnected", 11) && Q_strncasecmp(newcl->name, "connecting", 10))
IPLog_Add(NET_AdrToString(adrbuf,sizeof(adrbuf), &newcl->netchan.remote_address), newcl->name);
return newcl; return newcl;
} }

View File

@ -2484,10 +2484,11 @@ qboolean SV_SendClientDatagram (client_t *client)
qbyte buf[MAX_OVERALLMSGLEN]; qbyte buf[MAX_OVERALLMSGLEN];
sizebuf_t msg; sizebuf_t msg;
unsigned int sentbytes; unsigned int sentbytes;
unsigned int outframeseq = client->netchan.incoming_sequence; //this is so weird... but at least covers nq/qw sequence vs unreliables weirdness...
if (ISQWCLIENT(client) || ISNQCLIENT(client)) if (ISQWCLIENT(client) || ISNQCLIENT(client))
{ {
client_frame_t *frame = &client->frameunion.frames[client->netchan.outgoing_sequence & UPDATE_MASK]; client_frame_t *frame = &client->frameunion.frames[outframeseq & UPDATE_MASK];
frame->numresendstats = 0; frame->numresendstats = 0;
} }
@ -2522,11 +2523,11 @@ qboolean SV_SendClientDatagram (client_t *client)
#endif #endif
{ {
if (!ISQ2CLIENT(client) && Netchan_CanReliable (&client->netchan, SV_RateForClient(client))) if (!ISQ2CLIENT(client) && ((client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) || Netchan_CanReliable (&client->netchan, SV_RateForClient(client))))
{ {
int pnum=1; int pnum=1;
client_t *c; client_t *c;
client_frame_t *frame = &client->frameunion.frames[client->netchan.outgoing_sequence & UPDATE_MASK]; client_frame_t *frame = &client->frameunion.frames[outframeseq & UPDATE_MASK];
SV_UpdateClientStats (client, 0, &msg, frame); SV_UpdateClientStats (client, 0, &msg, frame);
for (c = client->controlled; c; c = c->controlled,pnum++) for (c = client->controlled; c; c = c->controlled,pnum++)

View File

@ -635,6 +635,10 @@ void SVNQ_New_f (void)
if (!gamedir[0]) if (!gamedir[0])
{ {
gamedir = FS_GetGamedir(true); gamedir = FS_GetGamedir(true);
#ifndef NOLEGACY
if (!strcmp(gamedir, "qw")) //hack: hide the qw dir from nq clients.
gamedir = "";
#endif
} }
COM_FileBase(sv.modelname, mapname, sizeof(mapname)); COM_FileBase(sv.modelname, mapname, sizeof(mapname));
@ -5170,7 +5174,7 @@ void SV_CalcNetRates(client_t *cl, double *ftime, int *frames, double *minf, dou
} }
} }
void Cmd_FPSList_f(void) static void Cmd_FPSList_f(void)
{ {
client_t *cl; client_t *cl;
int c; int c;
@ -5233,7 +5237,7 @@ static void SV_STFU_f(void)
} }
#ifdef NQPROT #ifdef NQPROT
void SVNQ_Spawn_f (void) static void SVNQ_Spawn_f (void)
{ {
extern cvar_t sv_gravity; extern cvar_t sv_gravity;
int i; int i;
@ -5331,7 +5335,7 @@ void SVNQ_Spawn_f (void)
host_client->send_message = true; host_client->send_message = true;
} }
void SVNQ_Begin_f (void) static void SVNQ_Begin_f (void)
{ {
unsigned pmodel = 0, emodel = 0; unsigned pmodel = 0, emodel = 0;
int i; int i;
@ -5460,7 +5464,7 @@ void SVNQ_Begin_f (void)
SV_RunCmd (&host_client->lastcmd, false); SV_RunCmd (&host_client->lastcmd, false);
SV_PostRunCmd(); SV_PostRunCmd();
} }
void SVNQ_PreSpawn_f (void) static void SVNQ_PreSpawn_f (void)
{ {
if (host_client->prespawn_stage < PRESPAWN_MAPCHECK) if (host_client->prespawn_stage < PRESPAWN_MAPCHECK)
SV_StuffcmdToClient(host_client, va("cmd prespawn %s\n", Cmd_Args())); SV_StuffcmdToClient(host_client, va("cmd prespawn %s\n", Cmd_Args()));
@ -5499,13 +5503,13 @@ void SVNQ_PreSpawn_f (void)
host_client->send_message = true; host_client->send_message = true;
} }
void SVNQ_NQInfo_f (void) static void SVNQ_NQInfo_f (void)
{ {
Cmd_TokenizeString(va("setinfo \"%s\" \"%s\"\n", Cmd_Argv(0), Cmd_Argv(1)), false, false); Cmd_TokenizeString(va("setinfo \"%s\" \"%s\"\n", Cmd_Argv(0), Cmd_Argv(1)), false, false);
SV_SetInfo_f(); SV_SetInfo_f();
} }
void SVNQ_NQColour_f (void) static void SVNQ_NQColour_f (void)
{ {
char *val; char *val;
int top; int top;
@ -5576,7 +5580,7 @@ void SVNQ_NQColour_f (void)
SV_ExtractFromUserinfo (host_client, true); SV_ExtractFromUserinfo (host_client, true);
} }
void SVNQ_Ping_f(void) static void SVNQ_Ping_f(void)
{ {
int i; int i;
client_t *cl; client_t *cl;
@ -5592,8 +5596,54 @@ void SVNQ_Ping_f(void)
SV_PrintToClient(host_client, PRINT_HIGH, va("%3i %s\n", SV_CalcPing (cl, false), cl->name)); SV_PrintToClient(host_client, PRINT_HIGH, va("%3i %s\n", SV_CalcPing (cl, false), cl->name));
} }
} }
static void SVNQ_Status_f(void)
{ //note: numerous NQ clients poll for this...
//so try to ensure that we adhere to various rules...
//we have a different function for server operators to use which contains more info.
int i;
client_t *cl;
int count;
extern cvar_t maxclients, maxspectators;
void SVNQ_Protocols_f(void) /*
int nummodels, numsounds;
for (nummodels = 1; nummodels < MAX_PRECACHE_MODELS; nummodels++)
if (!sv.strings.model_precache[nummodels])
break;
for (numsounds = 1; numsounds < MAX_PRECACHE_SOUNDS; numsounds++)
if (!sv.strings.sound_precache[numsounds])
break;*/
SV_PrintToClient(host_client, PRINT_HIGH, va("host: %s\n", hostname.string)); //must be first, with same first 9 chars
SV_PrintToClient(host_client, PRINT_HIGH, va("version: %s\n", version_string()));
// SV_PrintToClient(host_client, PRINT_HIGH, va("IPv4: \n", ));
// SV_PrintToClient(host_client, PRINT_HIGH, va("IPv6: \n", ));
SV_PrintToClient(host_client, PRINT_HIGH, va("map: %s\n", svs.name));
/* for (count = 1; count < MAX_PRECACHE_MODELS; count++)
if (!sv.strings.model_precache[count])
break;
SV_PrintToClient(host_client, PRINT_HIGH, va("models: %i/%i\n", count-1, MAX_PRECACHE_MODELS-1));*/
/* for (count = 1; count < MAX_PRECACHE_SOUNDS; count++)
if (!sv.strings.sound_precache[count])
break;
SV_PrintToClient(host_client, PRINT_HIGH, va("sounds: %i/%i\n", count-1, MAX_PRECACHE_SOUNDS-1));*/
// SV_PrintToClient(host_client, PRINT_HIGH, va("entities:%i/%i\n", sv.world.num_edicts, sv.world.max_edicts));
for (count=0,i=0,cl=svs.clients ; i<sv.allocated_client_slots ; i++,cl++)
{
if (cl->state)
count++;
}
SV_PrintToClient(host_client, PRINT_HIGH, va("players: %i active (%i max)\n\n", count, min(maxclients.ival+maxspectators.ival,sv.allocated_client_slots)));//must be last
for (i=0,cl=svs.clients ; i<sv.allocated_client_slots ; i++,cl++)
{
if (!cl->state)
continue;
SV_PrintToClient(host_client, PRINT_HIGH, va("#%i\n", i+1));
SV_PrintToClient(host_client, PRINT_HIGH, va(" %s\n", "WITHHELD"));
}
}
static void SVNQ_Protocols_f(void)
{ {
int i; int i;
host_client->supportedprotocols = 0; host_client->supportedprotocols = 0;
@ -5841,7 +5891,7 @@ ucmd_t nqucmds[] =
{"begin", SVNQ_Begin_f, true}, {"begin", SVNQ_Begin_f, true},
{"prespawn", SVNQ_PreSpawn_f, true}, {"prespawn", SVNQ_PreSpawn_f, true},
{"status", NULL}, {"status", SVNQ_Status_f},
{"god", Cmd_God_f}, {"god", Cmd_God_f},