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.
qboolean noclip_anglehack; // remnant from old quake
int startuppending;
void Host_FinishLoading(void);
@ -737,6 +738,7 @@ void CL_CheckForResend (void)
int contype = 0;
qboolean keeptrying = true;
char *host;
extern int r_blockvidrestart;
#ifndef CLIENTONLY
if (!cls.state && (!connectinfo.trying || sv.state != ss_clustermode) && sv.state)
@ -970,6 +972,8 @@ void CL_CheckForResend (void)
if (!connectinfo.trying)
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
@ -4952,7 +4956,6 @@ Runs all active servers
extern cvar_t cl_netfps;
extern cvar_t cl_sparemsec;
int startuppending;
void CL_StartCinematicOrMenu(void);
int nopacketcount;
void SNDDMA_SetUnderWater(qboolean underwater);
@ -5363,7 +5366,7 @@ void CL_StartCinematicOrMenu(void)
{
COM_MainThreadWork();
if (FS_DownloadingPackage())
if (com_installer && FS_DownloadingPackage())
{
startuppending = true;
return;
@ -5681,6 +5684,7 @@ Host_Init
*/
void Host_Init (quakeparms_t *parms)
{
char engineupdated[MAX_OSPATH];
int man;
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);
Cbuf_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 ();
NET_Init ();
COM_Init ();
#ifdef PLUGINS
Plug_Initialise(false);
#endif
#ifdef Q2BSPS
CM_Init();
#endif
@ -5784,10 +5810,7 @@ to run quit through here before the final handoff to the sys code.
void Host_Shutdown(void)
{
if (!host_initialized)
{
Sys_Printf ("recursive shutdown\n");
return;
}
host_initialized = false;
#ifdef WEBCLIENT

View File

@ -7371,18 +7371,19 @@ static char *CLNQ_ParseProQuakeMessage (char *s)
return s;
}
static enum {
CLNQPP_NONE,
CLNQPP_PINGS
} cl_nqparseprint;
qboolean CLNQ_ParseNQPrints(char *s)
{
int i;
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;
cl_nqparseprint = CLNQPP_NONE;
cl.nqparseprint = CLNQPP_NONE;
while(*s == ' ')
s++;
pingstart = s;
@ -7414,7 +7415,7 @@ qboolean CLNQ_ParseNQPrints(char *s)
{
cl.players[i].ping = atoi(pingstart);
}
cl_nqparseprint = CLNQPP_PINGS;
cl.nqparseprint = CLNQPP_PINGS;
return true;
}
}
@ -7422,10 +7423,51 @@ qboolean CLNQ_ParseNQPrints(char *s)
s = start;
}
if (!strcmp(s, "Client ping times:\n"))
if (!strncmp(s, "host: ", 9))
{
cl_nqparseprint = CLNQPP_PINGS;
return true;
cl.nqparseprint = CLNQPP_STATUS;
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;
@ -7740,6 +7782,8 @@ void CLNQ_ParseServerMessage (void)
if (*cl.players[i].name)
cl.players[i].userid = i+1;
Info_SetValueForKey(cl.players[i].userinfo, "name", cl.players[i].name, sizeof(cl.players[i].userinfo));
if (!cl.nqplayernamechanged)
cl.nqplayernamechanged = realtime+2;
}
break;

View File

@ -1706,7 +1706,11 @@ void SCR_DrawLoading (qboolean opaque)
int h2depth;
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
y = vid.height/2;

View File

@ -169,6 +169,8 @@ typedef struct player_info_s
int ping;
qbyte pl;
char ip[128];
struct
{
float time; //invalid if too old.
@ -531,6 +533,7 @@ typedef struct
int language;
colourised_t *colourised;
qboolean nqexpectingstatusresponse;
} client_static_t;
extern client_static_t cls;
@ -917,6 +920,16 @@ typedef struct
MATCH_STANDBY,
MATCH_INPROGRESS
} 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;
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)
{
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)
{
//we're spectating, or an mvd

View File

@ -36,9 +36,9 @@ vfsfile_t *FS_GZ_DecompressWriteFilter(vfsfile_t *outfile, qboolean autoclosefil
#define PHPMIN
#endif
#ifdef NOLEGACY
#define PHPLEG "&leg=0"
#define PHPLEG "&leg=0&test=1"
#else
#define PHPLEG "&leg=1"
#define PHPLEG "&leg=1&test=1"
#endif
#if defined(_DEBUG) || defined(DEBUG)
#define PHPDBG "&dbg=1"
@ -48,11 +48,13 @@ vfsfile_t *FS_GZ_DecompressWriteFilter(vfsfile_t *outfile, qboolean autoclosefil
#ifndef SVNREVISION
#define SVNREVISION -
#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).
//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]
#define DPF_INSTALLED 0x01
#define DPF_ENABLED 0x01
#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_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_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_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
//priories <0
//pakX
@ -127,7 +131,7 @@ typedef struct package_s {
struct package_s *alternative; //alternative (hidden) forms of this package.
unsigned int trymirrors;
char *mirror[8];
char *mirror[8]; //FIXME: move to two types of dep...
char gamedir[16];
enum fs_relative fsroot;
char version[16];
@ -156,6 +160,8 @@ typedef struct package_s {
DEP_FILECONFLICT, //don't install if this file already exists.
DEP_REQUIRE,
DEP_RECOMMEND, //like depend, but uninstalling will not bubble.
// DEP_MIRROR,
// DEP_FAILEDMIRROR,
DEP_FILE
} dtype;
@ -237,6 +243,24 @@ static void PM_FreePackage(package_t *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
void PM_ValidatePackage(package_t *p)
{
@ -244,7 +268,7 @@ void PM_ValidatePackage(package_t *p)
struct packagedep_s *dep;
vfsfile_t *pf;
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)
{
@ -309,7 +333,7 @@ void PM_ValidatePackage(package_t *p)
{
if (o == p)
continue;
if (o->flags & DPF_INSTALLED)
if (o->flags & DPF_ENABLED)
{
if (!strcmp(p->gamedir, o->gamedir) && p->fsroot == o->fsroot)
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;
else if (!o)
{
if (p->qhash)
if (!PM_PurgeOnDisable(p))
p->flags |= fl;
else if (p->qhash)
{
char buf[8];
searchpathfuncs_t *archive;
@ -355,7 +381,7 @@ void PM_ValidatePackage(package_t *p)
{
p->flags |= fl;
if (fl&DPF_NATIVE)
p->flags |= DPF_MARKED|DPF_INSTALLED;
p->flags |= DPF_MARKED|DPF_ENABLED;
break;
}
else
@ -445,7 +471,8 @@ static qboolean PM_MergePackage(package_t *oldp, package_t *newp)
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);
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));
else
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;
}
if (!strcmp(Cmd_Argv(0), "set"))
@ -654,6 +681,11 @@ static void PM_ParsePackageList(vfsfile_t *f, int parseflags, const char *url, c
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
{
//erk
@ -682,7 +714,7 @@ static void PM_ParsePackageList(vfsfile_t *f, int parseflags, const char *url, c
int i;
if (version > 2)
flags &= ~DPF_INSTALLED;
flags &= ~DPF_ENABLED;
p = Z_Malloc(sizeof(*p));
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);
else if (!strncmp(arg, "recommend=", 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)
flags &= ~DPF_INSTALLED;
flags &= ~DPF_ENABLED;
else if (!strncmp(arg, "installed=", 6) && version>2)
flags |= parseflags & DPF_INSTALLED;
flags |= parseflags & DPF_ENABLED;
else
{
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 (Sys_GetAutoUpdateSetting() == UPD_UNSUPPORTED)
if (!Sys_EngineCanUpdate())
p->flags |= DPF_HIDDEN;
else
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;
}
}
if (p->flags & DPF_INSTALLED)
if (p->flags & DPF_ENABLED)
p->flags |= DPF_MARKED;
PM_InsertPackage(p);
@ -913,7 +947,7 @@ void PM_LoadPackages(searchpath_t **oldpaths, const char *parent_pure, const cha
loadedinstalled = true;
if (f)
{
PM_ParsePackageList(f, DPF_FORGETONUNINSTALL|DPF_INSTALLED, NULL, "");
PM_ParsePackageList(f, DPF_FORGETONUNINSTALL|DPF_ENABLED, NULL, "");
VFS_CLOSE(f);
}
}
@ -924,14 +958,14 @@ void PM_LoadPackages(searchpath_t **oldpaths, const char *parent_pure, const cha
pri = maxpri;
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;
}
minpri = pri+1;
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)
{
@ -950,7 +984,7 @@ void PM_Shutdown(void)
{
//free everything...
loadedinstalled = false;
fs_downloads_url.modified = false;
pm_downloads_url.modified = false;
downloadablessequence++;
@ -984,7 +1018,7 @@ static void PM_PreparePackageList(void)
loadedinstalled = true;
if (f)
{
PM_ParsePackageList(f, DPF_FORGETONUNINSTALL|DPF_INSTALLED, NULL, "");
PM_ParsePackageList(f, DPF_FORGETONUNINSTALL|DPF_ENABLED, NULL, "");
VFS_CLOSE(f);
}
}
@ -1030,7 +1064,7 @@ static void PM_RevertChanges(void)
package_t *p;
for (p = availablepackages; p; p = p->next)
{
if (p->flags & DPF_INSTALLED)
if (p->flags & DPF_ENABLED)
p->flags |= DPF_MARKED;
else
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_MARKED) || !e || strcmp(e->version, p->version) < 0)
e = p;
if (!(p->flags & DPF_TESTING) || pm_autoupdate.ival >= UPD_TESTING)
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)
{
@ -1191,11 +1227,12 @@ static unsigned int PM_MarkUpdates (void)
{
if (p == o || (o->flags & DPF_HIDDEN))
continue;
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 (!(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)
@ -1208,7 +1245,7 @@ static unsigned int PM_MarkUpdates (void)
}
if (e && !(e->flags & DPF_MARKED))
{
if (Sys_GetAutoUpdateSetting() >= UPD_STABLE)
if (pm_autoupdate.ival >= UPD_STABLE)
{
changecount++;
PM_MarkPackage(e);
@ -1224,7 +1261,7 @@ static void PM_PrintChanges(void)
package_t *p;
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++;
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)
{
unsigned int i;
int setting;
if (retry>1 || fs_downloads_url.modified)
if (retry>1 || pm_downloads_url.modified)
PM_Shutdown();
PM_PreparePackageList();
//make sure our sources are okay.
if (*fs_downloads_url.string)
PM_AddSubList(fs_downloads_url.string, "", true);
if (*pm_downloads_url.string)
PM_AddSubList(pm_downloads_url.string, "", true);
doautoupdate |= autoupdate;
@ -1362,11 +1398,7 @@ static void PM_UpdatePackageList(qboolean autoupdate, int retry)
if (downloadablelist[i].curdl)
continue;
setting = Sys_GetAutoUpdateSetting();
// 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);
downloadablelist[i].curdl = HTTP_CL_Get(va("%s%s"DOWNLOADABLESARGS, downloadablelist[i].url, strchr(downloadablelist[i].url,'?')?"&":"?"), NULL, PM_ListDownloaded);
if (downloadablelist[i].curdl)
{
downloadablelist[i].curdl->user_num = i;
@ -1437,6 +1469,9 @@ static void PM_WriteInstalledPackages(void)
s = "version 2\n";
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++)
{
if (downloadablelist[i].save)
@ -1448,12 +1483,12 @@ static void PM_WriteInstalledPackages(void)
for (p = availablepackages; p ; p=p->next)
{
if (p->flags & (DPF_CACHED|DPF_INSTALLED))
if (p->flags & (DPF_PRESENT|DPF_ENABLED))
{
char buf[8192];
buf[0] = 0;
COM_QuotedString(va("%s%s", p->category, p->name), buf, sizeof(buf), false);
if (p->flags & DPF_INSTALLED)
if (p->flags & DPF_ENABLED)
{ //v3+
// Q_strncatz(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));
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;
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.
Q_strncatz(buf, "\n", sizeof(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
n = fname;
if (FS_WriteFile(n, f, loc.len, p->fsroot))
p->flags |= DPF_NATIVE|DPF_INSTALLED;
p->flags |= DPF_NATIVE|DPF_ENABLED;
free(f);
//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
static void PM_Download_Got(struct dl_download *dl)
{
char native[MAX_OSPATH];
qboolean successful = dl->status == DL_FINISHED;
package_t *p;
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);
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);
@ -1694,19 +1736,23 @@ static void PM_Download_Got(struct dl_download *dl)
}
else
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);
if (FS_Remove(destname, p->fsroot))
;
if (!FS_Rename2(tempname, destname, p->fsroot, p->fsroot))
{
//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);
}
else
{ //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;
PM_WriteInstalledPackages();
}
@ -1869,16 +1915,16 @@ static void PM_StartADownload(void)
{ //this appears to be a meta package with no download
//just directly install it.
p->flags &= ~(DPF_NATIVE|DPF_CACHED|DPF_CORRUPT);
p->flags |= DPF_INSTALLED;
p->flags |= DPF_ENABLED;
PM_WriteInstalledPackages();
}
continue;
}
if (p->qhash && (p->flags & DPF_CACHED))
if (!PM_PurgeOnDisable(p))
{ //its in our cache directory, so lets just use that
p->trymirrors = 0;
p->flags |= DPF_INSTALLED;
p->flags |= DPF_ENABLED;
PM_WriteInstalledPackages();
FS_ReloadPackFiles();
continue;
@ -1955,42 +2001,56 @@ static void PM_ApplyChanges(void)
p = *link;
if (p->download)
; //erk, dude, don't do two!
else if ((p->flags & DPF_PURGE) || (!(p->flags&DPF_MARKED) && (p->flags&DPF_INSTALLED)))
{ //if we don't want it but we have it anyway:
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. don't bother to follow this logic when reinstalling
qboolean reloadpacks = false;
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];
COM_FileExtension(dep->name, ext, sizeof(ext));
if (!stricmp(ext, "pak") || !stricmp(ext, "pk3"))
if (!reloadpacks)
{
reloadpacks = true;
FS_UnloadPackFiles();
char ext[8];
COM_FileExtension(dep->name, ext, sizeof(ext));
if (!stricmp(ext, "pak") || !stricmp(ext, "pk3"))
{
reloadpacks = true;
FS_UnloadPackFiles();
}
}
}
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->gamedir)
{
if (p->flags & DPF_PURGE)
FS_Remove(temp, p->fsroot);
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 (!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
FS_Remove(va("%s/%s", p->gamedir, dep->name), p->fsroot);
else if (!FS_Remove(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_WriteInstalledPackages();
@ -2028,7 +2088,7 @@ static void PM_ApplyChanges(void)
//and flag any new/updated ones for a download
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;
}
PM_StartADownload(); //and try to do those downloads.
@ -2071,7 +2131,7 @@ void PM_Command_f(void)
{
const char *status;
char *markup;
if (p->flags & DPF_INSTALLED)
if (p->flags & DPF_ENABLED)
markup = S_COLOR_GREEN;
else if (p->flags & DPF_CORRUPT)
markup = S_COLOR_RED;
@ -2080,7 +2140,7 @@ void PM_Command_f(void)
else
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)
{
@ -2094,7 +2154,7 @@ void PM_Command_f(void)
else
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>";
else
status = "";
@ -2124,7 +2184,7 @@ void PM_Command_f(void)
if (p->flags & DPF_MARKED)
{
if (p->flags & DPF_INSTALLED)
if (p->flags & DPF_ENABLED)
{
if (p->flags & DPF_PURGE)
Con_Printf(" package is flagged to be re-installed\n");
@ -2136,7 +2196,7 @@ void PM_Command_f(void)
}
else
{
if (p->flags & DPF_INSTALLED)
if (p->flags & DPF_ENABLED)
{
if (p->flags & DPF_PURGE)
Con_Printf(" package is flagged to be purged\n");
@ -2160,6 +2220,8 @@ void PM_Command_f(void)
Con_Printf(" package is hidden\n");
if (p->flags & DPF_ENGINE)
Con_Printf(" package is an engine update\n");
if (p->flags & DPF_TESTING)
Con_Printf(" package is untested\n");
return;
}
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);
}
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
void PM_Command_f (void)
{
@ -2307,7 +2419,6 @@ typedef struct {
qboolean populated;
} dlmenu_t;
static int autoupdatesetting = UPD_UNSUPPORTED;
static void MD_Draw (int x, int y, struct menucustom_s *c, struct menu_s *m)
{
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");
else
{
switch((p->flags & (DPF_INSTALLED | DPF_MARKED)))
switch((p->flags & (DPF_ENABLED | DPF_MARKED)))
{
case 0:
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+8, y, "^Ue081");
if (p->flags & DPF_PRESENT)
Draw_FunString (x+8, y, "C");
}
break;
case DPF_INSTALLED:
if (p->flags & DPF_PURGE || !(p->qhash && (p->flags & DPF_CACHED)))
case DPF_ENABLED:
if ((p->flags & DPF_PURGE) || PM_PurgeOnDisable(p))
Draw_FunString (x, y, "DEL");
else
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:
if (p->flags & DPF_PURGE)
Draw_FunString (x, y, "GET");
else if (p->flags & (DPF_CACHED|DPF_NATIVE))
else if (p->flags & (DPF_PRESENT))
Draw_FunString (x, y, "USE");
else
Draw_FunString (x, y, "GET");
break;
case DPF_INSTALLED | DPF_MARKED:
case DPF_ENABLED | DPF_MARKED:
if (p->flags & DPF_PURGE)
Draw_FunString (x, y, "GET"); //purge and reinstall.
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)
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)
Draw_AltFunString (x+48, y, n);
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))
p = p->alternative;
if (p->flags & DPF_INSTALLED)
if (p->flags & DPF_ENABLED)
{
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;
case 0:
p->flags |= DPF_PURGE; //purge
if (p->flags & (DPF_CACHED | DPF_CORRUPT))
if (!PM_PurgeOnDisable(p))
break;
//fall through
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:
p->flags |= DPF_PURGE;
//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.
//fallthrough
case DPF_MARKED|DPF_PURGE:
PM_UnmarkPackage(p);
//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
//fallthrough
case DPF_PURGE:
@ -2477,20 +2595,13 @@ static void MD_AutoUpdate_Draw (int x, int y, struct menucustom_s *c, struct men
{
char *settings[] =
{
"Unsupported",
"Revert Engine",
"Off",
"Stable Updates",
"Test Updates"
};
char *text;
int setting = Sys_GetAutoUpdateSetting();
if (setting == UPD_UNSUPPORTED)
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]);
int setting = bound(0, pm_autoupdate.ival, 2);
text = va("Auto Update: %s", settings[setting]);
if (&m->selecteditem->common == &c->common)
Draw_AltFunString (x+4, y, text);
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 (autoupdatesetting == UPD_UNSUPPORTED)
autoupdatesetting = min(0, Sys_GetAutoUpdateSetting());
autoupdatesetting+=1;
if (autoupdatesetting > UPD_TESTING)
autoupdatesetting = (Sys_GetAutoUpdateSetting() == UPD_UNSUPPORTED)?1:0;
PM_UpdatePackageList(true, 2);
char nv[8] = "0";
if (pm_autoupdate.ival < UPD_TESTING && pm_autoupdate.ival >= 0)
Q_snprintfz(nv, sizeof(nv), "%i", pm_autoupdate.ival+1);
Cvar_ForceSet(&pm_autoupdate, nv);
PM_WriteInstalledPackages();
PM_UpdatePackageList(true, 0);
}
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)
{
#ifdef HAVEAUTOUPDATE
if (autoupdatesetting != UPD_UNSUPPORTED)
{
Sys_SetAutoUpdateSetting(autoupdatesetting);
autoupdatesetting = UPD_UNSUPPORTED;
}
#endif
PM_ApplyChanges();
return true;
}
@ -2599,8 +2703,11 @@ void M_AddItemsToDownloadMenu(menu_t *m)
{
if (strncmp(p->category, info->pathprefix, prefixlen))
continue;
if ((p->flags & DPF_HIDDEN) && (p->arch || !(p->flags & DPF_INSTALLED)))
if ((p->flags & DPF_HIDDEN) && (p->arch || !(p->flags & DPF_ENABLED)))
continue;
// if (p->flags & DPF_TESTING) //hide testing updates
// if (!(p->flags & (DPF_ENABLED|DPF_MARKED|DPF_PRESENT))
// continue;
slash = strchr(p->category+prefixlen, '/');
if (slash)
@ -2622,7 +2729,7 @@ void M_AddItemsToDownloadMenu(menu_t *m)
{
if (!strncmp(s->category, info->pathprefix, slash-path) || s->category[slash-path] != '/')
continue;
if (!(s->flags & DPF_INSTALLED) != !(s->flags & DPF_MARKED))
if (!(s->flags & DPF_ENABLED) != !(s->flags & DPF_MARKED))
break;
}
@ -2712,6 +2819,7 @@ void Menu_DownloadStuff_f (void)
menu = M_CreateMenu(sizeof(dlmenu_t));
info = menu->data;
menu->persist = true;
menu->predraw = M_Download_UpdateStatus;
info->downloadablessequence = downloadablessequence;
@ -2727,7 +2835,7 @@ void Menu_DownloadStuff_f (void)
//should only be called AFTER the filesystem etc is inited.
void Menu_Download_Update(void)
{
if (Sys_GetAutoUpdateSetting() == UPD_OFF || Sys_GetAutoUpdateSetting() == UPD_REVERT)
if (!pm_autoupdate.ival)
return;
PM_UpdatePackageList(true, 2);

View File

@ -1581,7 +1581,11 @@ void M_RemoveMenu (menu_t *menu)
if (menu->remove)
menu->remove(menu);
if (menu == firstmenu)
{
firstmenu = menu->parent;
if (firstmenu)
firstmenu->child = NULL;
}
else
{
menu_t *prev;
@ -1636,14 +1640,8 @@ void M_RemoveAllMenus (qboolean leaveprompts)
for (link = &firstmenu; *link; )
{
m = *link;
if (!m->exclusive && leaveprompts)
{
//this is WEIRD.
if (m == firstmenu)
link = &m->parent;
else
link = &m->child;
}
if ((m->persist || !m->exclusive) && leaveprompts)
link = &m->parent;
else
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.
void M_Menu_Options_f (void)
{
@ -190,16 +181,19 @@ void M_Menu_Options_f (void)
#endif
int y;
#ifdef HAVEAUTOUPDATE
#define HAVEAUTOUPDATE
menuoption_t *updatecbo;
#ifdef WEBCLIENT
static const char *autoupopts[] = {
"Revert",
"Off",
"Tested(Recommended)",
"Untested(Latest)",
NULL
};
static const char *autoupvals[] = {
"0",
"1",
"2",
NULL
};
#endif
static const char *projections[] = {
"Regular",
@ -256,8 +250,8 @@ void M_Menu_Options_f (void)
MB_CHECKBOXCVAR("Lookspring", lookspring, 0),
MB_CHECKBOXCVAR("Lookstrafe", lookstrafe, 0),
MB_CHECKBOXCVAR("Windowed Mouse", _windowed_mouse, 0),
#ifdef HAVEAUTOUPDATE
MB_COMBORETURN("Auto Update", autoupopts, Sys_GetAutoUpdateSetting(), updatecbo, "This downloads engine updates from the internet, when a new build is available."),
#ifdef WEBCLIENT
MB_COMBOCVAR("Auto Update", pm_autoupdate, autoupopts, autoupvals, "This offers to download engine+package updates from the internet, when new versions are available."),
#endif
#ifndef CLIENTONLY
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;
}
#endif
#ifdef HAVEAUTOUPDATE
menu->data = updatecbo;
menu->remove = M_Options_Remove;
#endif
}
#ifndef __CYGWIN__

View File

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

View File

@ -274,6 +274,7 @@ typedef struct menu_s {
qboolean iszone;
qboolean exclusive;
qboolean persist; //persists despite menuqc/engine changes etc
void *data; //typecast
@ -467,6 +468,7 @@ qboolean MP_Init (void);
void MP_Shutdown (void);
qboolean MP_Toggle(int mode);
void MP_Draw(void);
qboolean MP_UsingGamecodeLoadingScreen(void);
void MP_RegisterCvarsAndCmds(void);
qboolean MP_Keydown(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_shutdown_function;
func_t mp_draw_function;
func_t mp_drawloading_function;
func_t mp_keydown_function;
func_t mp_keyup_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_shutdown_function = PR_FindFunction(menu_world.progs, "m_shutdown", 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_keydown_function = PR_FindFunction(menu_world.progs, "m_keydown", 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");
}
qboolean MP_UsingGamecodeLoadingScreen(void)
{
return menu_world.progs && mp_drawloading_function;
}
void MP_Draw(void)
{
extern qboolean scr_drawloading;
globalvars_t *pr_globals;
if (!menu_world.progs)
return;
if (setjmp(mp_abort))
@ -2748,14 +2757,14 @@ void MP_Draw(void)
*menu_world.g.frametime = host_frametime;
inmenuprogs++;
if (mp_draw_function)
{
globalvars_t *pr_globals = PR_globals(menu_world.progs, PR_CURRENT);
((float *)pr_globals)[OFS_PARM0+0] = vid.width;
((float *)pr_globals)[OFS_PARM0+1] = vid.height;
((float *)pr_globals)[OFS_PARM0+2] = 0;
pr_globals = PR_globals(menu_world.progs, PR_CURRENT);
((float *)pr_globals)[OFS_PARM0+0] = vid.width;
((float *)pr_globals)[OFS_PARM0+1] = vid.height;
((float *)pr_globals)[OFS_PARM0+2] = 0;
if (mp_drawloading_function && scr_drawloading)
PR_ExecuteProgram(menu_world.progs, mp_drawloading_function);
else if (mp_draw_function)
PR_ExecuteProgram(menu_world.progs, mp_draw_function);
}
inmenuprogs--;
}

View File

@ -290,7 +290,8 @@ extern qboolean noclip_anglehack;
extern quakeparms_t host_parms;
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_nogamedirnativecode;
extern cvar_t com_parseutf8;

View File

@ -324,7 +324,7 @@ cvar_t gl_conback = CVARFDC ("gl_conback", "",
// CVAR_ARCHIVE);
//cvar_t gl_detailscale = CVAR ("gl_detailscale", "5");
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"
"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");
}
}
if (cls.protocol == CP_NETQUAKE)
{
if (cl.nqplayernamechanged && cl.nqplayernamechanged < realtime)
{
cl.nqplayernamechanged = 0;
cls.nqexpectingstatusresponse = true;
CL_SendClientCommand(true, "status");
}
}
if (start)
y = start;

View File

@ -1139,6 +1139,7 @@ static void OpenAL_Shutdown (soundcardinfo_t *sc)
palcMakeContextCurrent(NULL);
palcDestroyContext(oali->OpenAL_Context);
palcCloseDevice(oali->OpenAL_Device);
Z_Free(oali->source);
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]));
wcstombs(guidbuf, mssuck, sizeof(guidbuf));
callback(SDRVNAME, guidbuf, lpcstrDescription);
callback(SDRVNAME, guidbuf, va("DS: %s", lpcstrDescription));
return TRUE;
}

View File

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

View File

@ -3,6 +3,13 @@
#ifdef DYNAMIC_SDL
#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.
#include <stdint.h>
#define SDL_AudioDeviceID uint32_t
@ -18,9 +25,10 @@
#else
#define AUDIO_S16SYS AUDIO_S16LSB
#endif
#define SDLCALL QDECL
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
{
@ -35,16 +43,20 @@ typedef struct SDL_AudioSpec
void *userdata;
} SDL_AudioSpec;
static int (*SDL_Init) (uint32_t flags);
static int (*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 void (*SDL_PauseAudioDevice) (SDL_AudioDeviceID fd, int pausestate);
static void (*SDL_LockAudioDevice) (SDL_AudioDeviceID fd);
static void (*SDL_UnlockAudioDevice) (SDL_AudioDeviceID fd);
static int (*SDL_CloseAudioDevice) (SDL_AudioDeviceID fd);
static int (*SDL_GetNumAudioDevices) (int iscapture);
static const char *(*SDL_GetAudioDeviceName) (int index, int iscapture);
static const char *(*SDL_GetError) (void);
static int (SDLCALL *SDL_Init) (uint32_t flags);
static int (SDLCALL *SDL_InitSubSystem) (uint32_t flags);
static SDL_AudioDeviceID (SDLCALL *SDL_OpenAudioDevice) (const char *dev, int iscapture, const SDL_AudioSpec *desired, SDL_AudioSpec *obtained, int allowed_changes);
static void (SDLCALL *SDL_PauseAudioDevice) (SDL_AudioDeviceID fd, int pausestate);
static void (SDLCALL *SDL_LockAudioDevice) (SDL_AudioDeviceID fd);
static void (SDLCALL *SDL_UnlockAudioDevice) (SDL_AudioDeviceID fd);
static int (SDLCALL *SDL_CloseAudioDevice) (SDL_AudioDeviceID fd);
static int (SDLCALL *SDL_GetNumAudioDevices) (int iscapture);
static const char *(SDLCALL *SDL_GetAudioDeviceName) (int index, int iscapture);
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
#include <SDL.h>
#endif
@ -74,6 +86,10 @@ static qboolean SSDL_InitAudio(void)
{(void*)&SDL_GetNumAudioDevices, "SDL_GetNumAudioDevices"},
{(void*)&SDL_GetAudioDeviceName, "SDL_GetAudioDeviceName"},
{(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}
};
static dllhandle_t *libsdl;
@ -82,6 +98,10 @@ static qboolean SSDL_InitAudio(void)
libsdl = Sys_LoadLibrary("libSDL2-2.0.so.0", funcs);
if (!libsdl)
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)
SDL_Init(SDL_INIT_NOPARACHUTE);
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...
}
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;
@ -273,7 +293,7 @@ static qboolean QDECL SDL_Enumerate(void (QDECL *cb) (const char *drivername, co
{
const char *devname = SDL_GetAudioDeviceName(i, false);
if (devname)
cb(SDRVNAME, devname, va("SDL (%s)", devname));
cb(SDRVNAME, devname, va("SDL:%s", devname));
}
}
return true;
@ -289,4 +309,91 @@ sounddriver_t SDL_Output =
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)
{
wchar_t wide[MAX_OSPATH];
DWORD err;
widen(wide, sizeof(wide), path);
if (DeleteFileW(wide))
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 false; //other errors? panic
}
@ -2619,406 +2621,118 @@ void Win7_TaskListInit(void)
}
#endif
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);
}
//#define SVNREVISION 1
#if defined(SVNREVISION) && !defined(MINIMAL) && !defined(NOLEGACY)
#define SVNREVISIONSTR STRINGIFY(SVNREVISION)
#if defined(OFFICIAL_RELEASE)
#define UPD_BUILDTYPE "rel"
#ifndef SVNREVISION
#if 0 //1 to debug engine update in msvc.
#define SVNREVISION 1
#else
#define UPD_BUILDTYPE "test"
//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
#define SVNREVISION -
#endif
#endif
#define SVNREVISIONSTR STRINGIFY(SVNREVISION)
#ifndef NOLEGACY
#if defined(SVNREVISION) && !defined(MINIMAL)
#if defined(OFFICIAL_RELEASE)
#define UPD_BUILDTYPE "rel"
#else
#ifdef _WIN64
#define UPDATE_URL_BUILD "%swin64/fte" EXETYPE "64.exe"
#define UPD_BUILDTYPE "test"
//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
#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
#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
#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
#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);
}
#ifdef HAVEAUTOUPDATE
//this is for legacy reasons. old builds stored a 'pending' name in the registry for the 'frontend' to rename+use
void Sys_SetUpdatedBinary(const char *binary)
{
#ifdef UPD_BUILDTYPE
//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);
#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];
int ffe = COM_CheckParm("--fromfrontend");
wchar_t widefe[MAX_OSPATH];
wchar_t wargs[8192];
PROCESS_INFORMATION childinfo;
STARTUPINFOW startinfo = {sizeof(startinfo)};
char *e;
strtoul(SVNREVISIONSTR, &e, 10);
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.
sys_autoupdatesetting = UPD_UNSUPPORTED;
else if (COM_CheckParm("-noupdate") || COM_CheckParm("--noupdate") || COM_CheckParm("-noautoupdate") || COM_CheckParm("--noautoupdate"))
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)
//if we were called from a frontend, then don't chain to another, because that would be recursive, and that would be bad.
if (COM_CheckParm("--fromfrontend"))
return false;
//if we're not allowed for some other reason
if (!Sys_EngineCanUpdate())
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
MyRegGetStringValue(HKEY_CURRENT_USER, "Software\\"FULLENGINENAME, "pending" UPD_BUILDTYPE EXETYPE, pendingpath, sizeof(pendingpath));
if (*pendingpath)
{
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);
}
}
startinfo.dwFlags = STARTF_USESTDHANDLES;
startinfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startinfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
startinfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
MyRegGetStringValue(HKEY_CURRENT_USER, "Software\\"FULLENGINENAME, UPD_BUILDTYPE EXETYPE, updatedpath, sizeof(updatedpath));
if (*updatedpath)
{
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;
}
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), newbinary), wargs, NULL, NULL, TRUE, 0, NULL, NULL, &startinfo, &childinfo))
return true; //it started up, we need to die now.
}
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)
{
return false; //failure!
}
#endif
@ -3475,6 +3189,13 @@ LRESULT CALLBACK NoCloseWindowProc(HWND w, UINT m, WPARAM wp, LPARAM 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);
qboolean Sys_DoInstall(void)
{
@ -4048,9 +3769,6 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
isPlugin = 0;
}
if (Sys_CheckUpdated(bindir, sizeof(bindir)))
return true;
if (COM_CheckParm("-register_types"))
{
Sys_DoFileAssociations(1);
@ -4205,6 +3923,10 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
if (!Sys_Startup_CheckMem(&parms))
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
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);
}
Update_Check();
/* main window message loop */
while (1)
{

View File

@ -613,49 +613,58 @@ void Validation_Auto_Response(int playernum, char *s)
static float cmdlineresponsetime;
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();
versionresponsetime = Sys_DoubleTime() + 5;
}
else if (cl.spectator)
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();
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();
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();
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();
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();
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();
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();
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)
Validation_AllChecks();

View File

@ -1793,8 +1793,8 @@ void TP_SearchForMsgTriggers (char *s, int level)
&& t->string[0] && strstr(s, t->string))
{
if (level == PRINT_CHAT && (
strstr (s, "f_version") || strstr (s, "f_system") ||
strstr (s, "f_speed") || strstr (s, "f_modified") || strstr (s, "f_ruleset")))
strstr (s, "_version") || strstr (s, "_system") ||
strstr (s, "_speed") || strstr (s, "_modified") || strstr (s, "_ruleset")))
continue; // don't let llamas fake proxy replies
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_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_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_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_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 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
@ -5553,6 +5554,14 @@ void COM_Init (void)
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
//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))
{
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);
void COM_AddParm (const char *parm);
void COM_Shutdown (void);
void COM_Init (void);
void COM_InitArgv (int argc, const char **argv);
void COM_ParsePlusSets (qboolean docbuf);
@ -730,6 +731,7 @@ void Log_String (logtype_t lognum, char *s);
void Con_Log (char *s);
void Log_Logfile_f (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*/

View File

@ -2955,7 +2955,6 @@ typedef struct {
const char *manifestfile;
} gamemode_info_t;
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.
//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.
//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
//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
//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"},
{"-rogue", "rogue", MASTER_PREFIX"Rogue", {"id1/pak0.pak","id1/quake.rc"},QCFG, {"id1", "qw", "rogue", "*fte"}, "Quake: Dissolution of Eternity"},
{"-hipnotic", "hipnotic", "FTE-Hipnotic",{"id1/pak0.pak","id1/quake.rc"},QCFG, {"id1", "qw", "hipnotic", "*fte"}, "Quake: Scourge of Armagon"},
{"-rogue", "rogue", "FTE-Rogue", {"id1/pak0.pak","id1/quake.rc"},QCFG, {"id1", "qw", "rogue", "*fte"}, "Quake: Dissolution of Eternity"},
//various quake-based standalone mods.
{"-nexuiz", "nexuiz", "Nexuiz", {"nexuiz.exe"}, NEXCFG, {"data", "*ftedata"}, "Nexuiz"},
@ -3968,7 +3967,7 @@ void FS_Shutdown(void)
fs_thread_mutex = NULL;
Cvar_SetEngineDefault(&fs_gamename, NULL);
Cvar_SetEngineDefault(&fs_downloads_url, NULL);
Cvar_SetEngineDefault(&pm_downloads_url, NULL);
Cvar_SetEngineDefault(&com_protocolname, NULL);
}
@ -5118,11 +5117,11 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
if (reloadconfigs)
{
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");
//FIXME: flag this instead and do it after a delay?
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);
vidrestart = false;
@ -5567,7 +5566,8 @@ void COM_InitFilesystem (void)
Cvar_Register(&cfg_reload_on_gamedir, "Filesystem");
Cvar_Register(&com_fs_cache, "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(&fs_game, "Filesystem");
#ifdef Q2SERVER
@ -5697,10 +5697,6 @@ void COM_InitFilesystem (void)
fs_readonly = COM_CheckParm("-readonly");
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);
int PM_IsApplying(void);
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);
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)
{
@ -346,6 +429,10 @@ void Log_Init(void)
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
#ifdef CRAZYDEBUGGING
Cvar_ForceSet(&log_enable[LOG_CONSOLE], "1");

View File

@ -2510,24 +2510,39 @@ qboolean FTENET_Generic_GetPacket(ftenet_generic_connection_t *con)
return false;
if (err == NET_EMSGSIZE)
{
SockadrToNetadr (&from, &net_from);
Con_TPrintf ("Warning: Oversize packet from %s\n",
static unsigned int resettime;
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));
}
return false;
}
if (err == NET_ECONNABORTED || err == NET_ECONNRESET)
{
Con_TPrintf ("Connection lost or aborted\n"); //server died/connection lost.
#ifndef SERVERONLY
if (cls.state != ca_disconnected && !con->islisten)
static unsigned int resettime;
unsigned int curtime = Sys_Milliseconds();
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
Cbuf_AddText("disconnect\nreconnect\n", RESTRICT_LOCAL); //retry connecting.
if (err == NET_ECONNABORTED)
Con_TPrintf ("Connection lost or aborted (%s)\n", NET_AdrToString (adr, sizeof(adr), &net_from)); //server died/connection lost.
else
Con_Printf("Packet was not delivered - server might be badly configured\n");
return false;
}
Con_TPrintf ("Connection lost or aborted\n"); //server died/connection lost.
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
}
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));
return false;
}
SockadrToNetadr (&from, &net_from);
SockadrToNetadr (&from, &net_from);
net_message.packing = SZ_RAWBYTES;
net_message.currentbit = 0;

View File

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

View File

@ -663,7 +663,7 @@
Name="VCCLCompilerTool"
Optimization="0"
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"
SmallerTypeCheck="true"
RuntimeLibrary="1"

View File

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

View File

@ -11508,6 +11508,7 @@ void PR_DumpPlatform_f(void)
{"m_init", "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_drawloading", "void(vector screensize)", MENU, "Additional drawing function to draw loading screen overlays."},
{"m_keydown", "void(float scan, float chr)", MENU},
{"m_keyup", "void(float scan, float chr)", MENU},
{"m_toggle", "void(float wantmode)", MENU},

View File

@ -1765,6 +1765,14 @@ static void SV_Status_f (void)
int columns = 80;
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
#ifndef SERVERONLY
|| (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);
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);
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
{"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},
{"DP6", 1u<<SCP_DARKPLACES6},
{"DP5", 0},
@ -2206,10 +2208,10 @@ client_t *SVC_DirectConnect(void)
{"DP3", 0},
{"DP2", 0},
{"DP1", 0},
{"QW", 0}, //mixing protocols doesn't make sense, and would just confuse the client.
{"QUAKEDP", 1u<<SCP_NETQUAKE},
{"QUAKE", 1u<<SCP_NETQUAKE},
{"QW", 0}, //mixing protocols doesn't make sense, and would just confuse the client.
{"NEHAHRAMOVIE", 0},
{"NEHAHRAMOVIE", 1u<<SCP_NETQUAKE},
{"NEHAHRABJP", 0},
{"NEHAHRABJP2", 0},
{"NEHAHRABJP3", 1u<<SCP_BJP3},
@ -3108,6 +3110,9 @@ client_t *SVC_DirectConnect(void)
SSV_SavePlayerStats(newcl, 0);
#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;
}

View File

@ -2484,10 +2484,11 @@ qboolean SV_SendClientDatagram (client_t *client)
qbyte buf[MAX_OVERALLMSGLEN];
sizebuf_t msg;
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))
{
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;
}
@ -2522,11 +2523,11 @@ qboolean SV_SendClientDatagram (client_t *client)
#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;
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);
for (c = client->controlled; c; c = c->controlled,pnum++)

View File

@ -635,6 +635,10 @@ void SVNQ_New_f (void)
if (!gamedir[0])
{
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));
@ -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;
int c;
@ -5233,7 +5237,7 @@ static void SV_STFU_f(void)
}
#ifdef NQPROT
void SVNQ_Spawn_f (void)
static void SVNQ_Spawn_f (void)
{
extern cvar_t sv_gravity;
int i;
@ -5331,7 +5335,7 @@ void SVNQ_Spawn_f (void)
host_client->send_message = true;
}
void SVNQ_Begin_f (void)
static void SVNQ_Begin_f (void)
{
unsigned pmodel = 0, emodel = 0;
int i;
@ -5460,7 +5464,7 @@ void SVNQ_Begin_f (void)
SV_RunCmd (&host_client->lastcmd, false);
SV_PostRunCmd();
}
void SVNQ_PreSpawn_f (void)
static void SVNQ_PreSpawn_f (void)
{
if (host_client->prespawn_stage < PRESPAWN_MAPCHECK)
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;
}
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);
SV_SetInfo_f();
}
void SVNQ_NQColour_f (void)
static void SVNQ_NQColour_f (void)
{
char *val;
int top;
@ -5576,7 +5580,7 @@ void SVNQ_NQColour_f (void)
SV_ExtractFromUserinfo (host_client, true);
}
void SVNQ_Ping_f(void)
static void SVNQ_Ping_f(void)
{
int i;
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));
}
}
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;
host_client->supportedprotocols = 0;
@ -5841,7 +5891,7 @@ ucmd_t nqucmds[] =
{"begin", SVNQ_Begin_f, true},
{"prespawn", SVNQ_PreSpawn_f, true},
{"status", NULL},
{"status", SVNQ_Status_f},
{"god", Cmd_God_f},