From a97007ae5a29788f2d21b347b26ee837d4154e83 Mon Sep 17 00:00:00 2001 From: Spoike Date: Tue, 21 Mar 2017 05:27:07 +0000 Subject: [PATCH] dpp7: Treat 'dropped' c2s packets as choked when using dpp7 protocols. This is because the protocol provides no way to disambiguate, and I don't like false reports of packetloss (only reliables loss can be detected, and that's not frequent enough to be meaningful). Pings can still be determined with dpp7, for those few packets which are acked. package manager: reworked to enable/disable plugins when downloaded, which can also be present-but-disabled. package manager: display a confirmation prompt before applying changes. do not allow other changes to be made while applying. prompt may be skipped with 'pkg apply' in dedicated servers. sv: downloads are no longer forced to lower case. sv: added sv_demoAutoCompress cvar. set to 1 to directly record to *.mvd.gz cl: properly support directly playing .mvd.gz files menus: reworked to separate mouse and keyboard focus. mouse focus becomes keyboard focus only on mouse clicks. tooltips follow mouse cursors. menus: cleaned up menu heirachy a little. now simpler. server browser: changed 'hide *' filters to 'show *' instead. I felt it was more logical. deluxmapping: changed to disabled, load, generate, like r_loadlit is. render targets api now supports negative formats to mean nearest filtering, where filtering is part of texture state. drawrotpic fixed, now batches and interacts with drawpic correctly. drawline fixed, no interacts with draw* correctly, but still does not batch. fixed saving games. provide proper userinfo to nq clients, where supported. qcc: catch string table overflows safely, giving errors instead of crashes. switch to 32bit statements if some over-sized function requires it. qtv: some bigcoords support tweaks git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5073 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/cl_demo.c | 52 ++--- engine/client/cl_main.c | 117 +++++----- engine/client/cl_parse.c | 384 ++++++++++++++++++-------------- engine/client/cl_plugin.inc | 2 +- engine/client/cl_screen.c | 1 + engine/client/client.h | 3 +- engine/client/m_download.c | 421 +++++++++++++++++++++++++++++++----- engine/client/m_items.c | 293 ++++++++++++------------- engine/client/m_master.c | 15 +- engine/client/m_options.c | 36 ++- engine/client/m_script.c | 6 +- engine/client/menu.c | 88 +++++--- engine/client/menu.h | 9 +- engine/client/merged.h | 2 +- engine/client/p_script.c | 4 +- engine/client/pr_csqc.c | 180 ++++++++++++++- engine/client/pr_menu.c | 8 +- engine/client/r_2d.c | 28 ++- engine/client/r_surf.c | 3 +- engine/client/render.h | 13 +- engine/client/renderer.c | 23 +- engine/client/sys_win.c | 12 + engine/client/textedit.c | 2 +- engine/client/vid.h | 3 +- engine/client/view.c | 4 +- engine/common/bothdefs.h | 4 +- engine/common/common.c | 77 +++---- engine/common/common.h | 9 +- engine/common/console.h | 2 +- engine/common/fs.c | 47 +++- engine/common/fs.h | 4 + engine/common/fs_win32.c | 10 +- engine/common/fs_zip.c | 40 +++- engine/common/plugin.c | 61 +++++- engine/common/qvm.c | 5 + engine/d3d/d3d_backend.c | 2 +- engine/d3d/vid_d3d.c | 2 +- engine/gl/gl_backend.c | 2 +- engine/gl/gl_draw.c | 2 +- engine/gl/gl_heightmap.c | 6 +- engine/gl/gl_model.c | 10 +- engine/gl/gl_model.h | 2 +- engine/gl/gl_rlight.c | 2 + engine/gl/gl_shader.c | 7 +- engine/gl/glquake.h | 2 - engine/gl/shader.h | 27 ++- engine/http/httpclient.c | 6 +- engine/qclib/qccmain.c | 49 ++++- engine/server/pr_cmds.c | 48 +--- engine/server/server.h | 5 +- engine/server/sv_ccmds.c | 1 - engine/server/sv_ents.c | 9 +- engine/server/sv_main.c | 22 +- engine/server/sv_mvd.c | 90 +++++++- engine/server/sv_send.c | 108 ++++++++- engine/server/sv_user.c | 186 ++++++++-------- fteqtv/msg.c | 14 ++ fteqtv/parse.c | 72 +++--- fteqtv/qtv.h | 10 +- fteqtv/qw.c | 107 ++++----- plugins/ezhud/hud_common.c | 11 +- 61 files changed, 1877 insertions(+), 893 deletions(-) diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 449ec24a..09a433b2 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -2327,39 +2327,34 @@ void CL_PlayDemo(char *demoname, qboolean usesystempath) { char name[256]; vfsfile_t *f; + int i; + char *exts[] = + { + ".qwd", + ".dem", + ".mvd", + ".dm2", + }; // // open the demo file // - Q_strncpyz (name, demoname, sizeof(name)); - COM_DefaultExtension (name, ".qwd", sizeof(name)); - f = CL_OpenFileInZipOrSys(name, usesystempath); - if (!f) + f = NULL; + for (i = 0; i < countof(exts); i++) { Q_strncpyz (name, demoname, sizeof(name)); - COM_DefaultExtension (name, ".dem", sizeof(name)); - if (usesystempath) - f = VFSOS_Open(name, "rb"); - else - f = FS_OpenVFS(name, "rb", FS_GAME); - } - if (!f) - { + COM_DefaultExtension (name, exts[i], sizeof(name)); + f = CL_OpenFileInZipOrSys(name, usesystempath); + if (f) + break; + +#ifdef AVAIL_GZDEC Q_strncpyz (name, demoname, sizeof(name)); - COM_DefaultExtension (name, ".mvd", sizeof(name)); - if (usesystempath) - f = VFSOS_Open(name, "rb"); - else - f = FS_OpenVFS(name, "rb", FS_GAME); - } - if (!f) - { - Q_strncpyz (name, demoname, sizeof(name)); - COM_DefaultExtension (name, ".dm2", sizeof(name)); - if (usesystempath) - f = VFSOS_Open(name, "rb"); - else - f = FS_OpenVFS(name, "rb", FS_GAME); + COM_DefaultExtension (name, va("%s.gz", exts[i]), sizeof(name)); + f = CL_OpenFileInZipOrSys(name, usesystempath); + if (f) + break; +#endif } if (!f) { @@ -2371,6 +2366,11 @@ void CL_PlayDemo(char *demoname, qboolean usesystempath) } Q_strncpyz (lastdemoname, demoname, sizeof(lastdemoname)); +#ifdef AVAIL_GZDEC + if (strlen(name) >= 3 && !Q_strcasecmp(name + strlen(name) - 3, ".gz")) + f = FS_DecompressGZip(f, NULL); +#endif + CL_PlayDemoFile(f, name, usesystempath); } diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 2a32ed7c..25da9651 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -286,8 +286,6 @@ jmp_buf host_abort; void Master_Connect_f (void); -float server_version = 0; // version of server we connected to - char emodel_name[] = { 'e' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 }; char pmodel_name[] = @@ -1819,7 +1817,7 @@ void CL_User_f (void) if (cl.players[i].userid == uid || !strcmp(cl.players[i].name, Cmd_Argv(1)) ) { - if (cls.protocol == CP_NETQUAKE) + if (!cl.players[i].userinfovalid) Con_Printf("name: %s\ncolour %i %i\nping: %i\n", cl.players[i].name, cl.players[i].rbottomcolor, cl.players[i].rtopcolor, cl.players[i].ping); else Info_Print (cl.players[i].userinfo, ""); @@ -2011,6 +2009,7 @@ void CL_CheckServerInfo(void) int oldstate; #endif int oldteamplay; + qboolean spectating = cl.spectator && cl.spectator != 2; //spectator 2 = spectator-with-scores, considered to be players. this means we don't want to allow spec cheats while they're inactive, because that would be weird. oldteamplay = cl.teamplay; cl.teamplay = atoi(Info_ValueForKey(cl.serverinfo, "teamplay")); @@ -2026,11 +2025,12 @@ void CL_CheckServerInfo(void) cls.allow_csqc = atoi(Info_ValueForKey(cl.serverinfo, "anycsqc")) || *Info_ValueForKey(cl.serverinfo, "*csprogs"); + cl.csqcdebug = atoi(Info_ValueForKey(cl.serverinfo, "*csqcdebug")); - if (cl.spectator || cls.demoplayback || atoi(Info_ValueForKey(cl.serverinfo, "watervis"))) + if (spectating || cls.demoplayback || atoi(Info_ValueForKey(cl.serverinfo, "watervis"))) cls.allow_watervis=true; - if (cl.spectator || cls.demoplayback || atoi(Info_ValueForKey(cl.serverinfo, "allow_skybox")) || atoi(Info_ValueForKey(cl.serverinfo, "allow_skyboxes"))) + if (spectating || cls.demoplayback || atoi(Info_ValueForKey(cl.serverinfo, "allow_skybox")) || atoi(Info_ValueForKey(cl.serverinfo, "allow_skyboxes"))) cls.allow_skyboxes=true; //mostly obsolete. s = Info_ValueForKey(cl.serverinfo, "fbskins"); @@ -2042,7 +2042,7 @@ void CL_CheckServerInfo(void) cls.allow_fbskins = 1; s = Info_ValueForKey(cl.serverinfo, "*cheats"); - if (cl.spectator || cls.demoplayback || !stricmp(s, "on")) + if (spectating || cls.demoplayback || !stricmp(s, "on")) cls.allow_cheats = true; #ifndef CLIENTONLY @@ -2052,7 +2052,7 @@ void CL_CheckServerInfo(void) #endif s = Info_ValueForKey(cl.serverinfo, "strict"); - if ((!cl.spectator && !cls.demoplayback && *s && strcmp(s, "0")) || !ruleset_allow_semicheats.ival) + if ((!spectating && !cls.demoplayback && *s && strcmp(s, "0")) || !ruleset_allow_semicheats.ival) { cls.allow_semicheats = false; cls.allow_cheats = false; @@ -2127,7 +2127,7 @@ void CL_CheckServerInfo(void) cls.allow_anyparticles = false; - if (cl.spectator || cls.demoplayback) + if (spectating || cls.demoplayback) cl.fpd = 0; else cl.fpd = atoi(Info_ValueForKey(cl.serverinfo, "fpd")); @@ -2181,44 +2181,6 @@ void CL_CheckServerInfo(void) if (oldteamplay != cl.teamplay) Skin_FlushPlayers(); } -/* -================== -CL_FullServerinfo_f - -Sent by server just after the svc_serverdata -================== -*/ -void CL_FullServerinfo_f (void) -{ - char *p; - float v; - - if (!Cmd_FromGamecode()) - { - Con_Printf("Hey! fullserverinfo is meant to come from the server!\n"); - return; - } - - if (Cmd_Argc() != 2) - { - Con_TPrintf ("usage: fullserverinfo \n"); - return; - } - - Q_strncpyz (cl.serverinfo, Cmd_Argv(1), sizeof(cl.serverinfo)); - - if ((p = Info_ValueForKey(cl.serverinfo, "*version")) && *p) { - v = Q_atof(p); - if (v) { - if (!server_version) - Con_TPrintf ("Version %1.2f Server\n", v); - server_version = v; - } - } - CL_CheckServerInfo(); - - cl.csqcdebug = atoi(Info_ValueForKey(cl.serverinfo, "*csqcdebug")); -} /* ================== @@ -3537,11 +3499,14 @@ void CL_ReadPackets (void) //============================================================================= -qboolean CL_AllowArbitaryDownload(char *localfile) +qboolean CL_AllowArbitaryDownload(char *oldname, char *localfile) { int allow; //never allow certain (native code) arbitary downloads. - if (!strnicmp(localfile, "game", 4) || !stricmp(localfile, "progs.dat") || !stricmp(localfile, "menu.dat") || !stricmp(localfile, "csprogs.dat") || !stricmp(localfile, "qwprogs.dat") || strstr(localfile, "..") || strstr(localfile, ":") || strstr(localfile, "//") || strstr(localfile, ".qvm") || strstr(localfile, ".dll") || strstr(localfile, ".so")) + if (!Q_strncasecmp(localfile, "game", 4) || //q2-ey things + !Q_strcasecmp(localfile, "progs.dat") || !Q_strcasecmp(localfile, "menu.dat") || !Q_strcasecmp(localfile, "csprogs.dat") || !Q_strcasecmp(localfile, "qwprogs.dat") || //overriding gamecode is bad (csqc should be dlcached) + strstr(localfile, "\\") || strstr(localfile, "..") || strstr(localfile, "./") || strstr(localfile, ":") || strstr(localfile, "//") || //certain path patterns are just bad + Q_strcasestr(localfile, ".qvm") || Q_strcasestr(localfile, ".dll") || Q_strcasestr(localfile, ".so")) //disallow any native code { //yes, I know the user can use a different progs from the one that is specified. If you leave it blank there will be no problem. (server isn't allowed to stuff progs cvar) Con_Printf("Ignoring arbitary download to \"%s\" due to possible security risk\n", localfile); return false; @@ -3551,8 +3516,11 @@ qboolean CL_AllowArbitaryDownload(char *localfile) { char ext[8]; COM_FileExtension(localfile, ext, sizeof(ext)); - if (!strncmp(localfile, "package/", 8) || !strcmp(ext, "pak") || !strcmp(ext, "pk3") || !strcmp(ext, "pk4") || (!strncmp(localfile, "demos/", 6) && !strcmp(ext, "mvd"))) - return true; + if (!strncmp(localfile, "demos/", 6) && (!Q_strcasecmp(ext, "mvd") || !Q_strcasecmp(ext, "gz"))) + return true; //mvdsv popularised the server sending 'download demo/foobar.mvd' in response to 'download demonum/5' aka 'cmd dl #' + else if (!strncmp(localfile, "package/", 8) && (!Q_strcasecmp(ext, "pak") || !Q_strcasecmp(ext, "pk3") || !Q_strcasecmp(ext, "pk4"))) + return true; //packages, woo. + //fixme: we should probably try using package/$gamedir/foo.pak if we get redirected to that. else { Con_Printf("Ignoring non-package download redirection to \"%s\"\n", localfile); @@ -3630,7 +3598,7 @@ void CL_Download_f (void) DL_Abort(cls.download, QDL_FAILED); //don't let gamecode order us to download random junk - if (!CL_AllowArbitaryDownload(localname)) + if (!CL_AllowArbitaryDownload(NULL, localname)) return; CL_CheckOrEnqueDownloadFile(url, localname, DLLF_REQUIRED|DLLF_VERBOSE); @@ -3671,7 +3639,7 @@ void CL_DownloadSize_f(void) { //'download this file instead' redirection = Cmd_Argv(3); - if (!CL_AllowArbitaryDownload(redirection)) + if (!CL_AllowArbitaryDownload(rname, redirection)) return; dl = CL_DownloadFailed(rname, NULL); @@ -4138,7 +4106,6 @@ void CL_Init (void) Cmd_AddCommand ("setinfo", CL_SetInfo_f); Cmd_AddCommand ("fullinfo", CL_FullInfo_f); - Cmd_AddCommand ("fullserverinfo", CL_FullServerinfo_f); Cmd_AddCommand ("color", CL_Color_f); Cmd_AddCommand ("download", CL_Download_f); @@ -4827,7 +4794,7 @@ void Host_DoRunFile(hrf_t *f) { if (!(f->flags & HRF_ACTION)) { - M_Menu_Prompt(Host_RunFilePrompted, f, "File already exists.", "What would you like to do?", displayname, "Overwrite", "Run old", "Cancel"); + M_Menu_Prompt(Host_RunFilePrompted, f, va("File already exists.\nWhat would you like to do?\n%s\n", displayname), "Overwrite", "Run old", "Cancel"); return; } } @@ -4835,7 +4802,7 @@ void Host_DoRunFile(hrf_t *f) { if (!(f->flags & HRF_ACTION)) { - M_Menu_Prompt(Host_RunFilePrompted, f, "File appears new.", "Would you like to install", displayname, "Install!", "", "Cancel"); + M_Menu_Prompt(Host_RunFilePrompted, f, va("File appears new.\nWould you like to install\n%s\n", displayname), "Install!", "", "Cancel"); return; } } @@ -4885,6 +4852,7 @@ qboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file) fname = utf8; nlen = strlen(fname); } + else #elif !defined(FTE_TARGET_WEB) //unix file urls are fairly consistant. if (nlen >= 8 && !strncmp(fname, "file:///", 8)) @@ -4892,7 +4860,46 @@ qboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file) fname += 7; nlen -= 7; } + else #endif + if (nlen >= 5 && !strncmp(fname, "qw://", 5)) + { //this is also implemented by ezquake, so be careful here... + //"qw://[stream@]host[:port]/COMMAND" join, spectate, qtvplay + char *t, *cmd; + const char *url; + char buffer[8192]; + t = Z_Malloc(nlen+1); + memcpy(t, fname, nlen); + t[nlen] = 0; + url = t+5; + + for (cmd = t+5; *cmd; cmd++) + { + if (*cmd == '/') + { + *cmd++ = 0; + break; + } + } + + //quote the url safely. + url = COM_QuotedString(url, buffer, sizeof(buffer), false); + + //now figure out what the command actually was + if (!Q_strcasecmp(cmd, "join")) + Cbuf_AddText(va("join %s\n", url), RESTRICT_LOCAL); + else if (!Q_strcasecmp(cmd, "spectate") || !strcmp(cmd, "observe")) + Cbuf_AddText(va("observe %s\n", url), RESTRICT_LOCAL); + else if (!Q_strcasecmp(cmd, "qtvplay")) + Cbuf_AddText(va("qtvplay %s\n", url), RESTRICT_LOCAL); + else if (!*cmd || !Q_strcasecmp(cmd, "connect")) + Cbuf_AddText(va("connect %s\n", url), RESTRICT_LOCAL); + else + Con_Printf("Unknown url command: %s\n", cmd); + + Z_Free(t); + return true; + } f = Z_Malloc(sizeof(*f) + nlen); memcpy(f->fname, fname, nlen); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 5caabe04..839e25c0 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -490,7 +490,12 @@ void CL_AckedInputFrame(int inseq, int outseq, qboolean worldstateokay) { //nq has no concept of choking. outbound packets that are accepted during a single frame will be erroneoulsy considered dropped. nq never had a netgraph based upon outgoing timings. // Con_Printf("Dropped moveframe %i\n", i); - cl.outframes[i].latency = -1; + if (cls.protocol == CP_NETQUAKE && CPNQ_IS_DP) + { //dp doesn't ack every single packet. trying to report packet loss correctly is futile, we'll just get bad-mouthed. + cl.outframes[i].latency = -2; //flag as choked + } + else + cl.outframes[i].latency = -1; //flag as dropped } } cl.inframes[inseq&UPDATE_MASK].ackframe = outseq; @@ -704,16 +709,19 @@ void CL_SendDownloadStartRequest(char *filename, char *localname, unsigned int f #ifdef WEBCLIENT if (!strncmp(filename, "http://", 7) || !strncmp(filename, "https://", 8)) { - struct dl_download *wdl = HTTP_CL_Get(filename, localname, CL_WebDownloadFinished); - if (wdl) + if (!cls.download || !(cls.download->flags & DLLF_ALLOWWEB)) { - if (!(flags & DLLF_TEMPORARY)) - Con_TPrintf ("Downloading %s to %s...\n", wdl->url, wdl->localname); - wdl->qdownload.flags = flags; - cls.download = &wdl->qdownload; + struct dl_download *wdl = HTTP_CL_Get(filename, localname, CL_WebDownloadFinished); + if (wdl) + { + if (!(flags & DLLF_TEMPORARY)) + Con_TPrintf ("Downloading %s to %s...\n", wdl->url, wdl->localname); + wdl->qdownload.flags = flags; + cls.download = &wdl->qdownload; + } + else + CL_DownloadFailed(filename, NULL); } - else - CL_DownloadFailed(filename, NULL); return; } #endif @@ -1140,7 +1148,7 @@ void Model_CheckDownloads (void) continue; #endif - CL_CheckOrEnqueDownloadFile(s, s, (i==1)?DLLF_REQUIRED:0); //world is required to be loaded. + CL_CheckOrEnqueDownloadFile(s, s, (i==1)?DLLF_REQUIRED|DLLF_ALLOWWEB:0); //world is required to be loaded. CL_CheckModelResources(s); } @@ -2669,7 +2677,7 @@ void CLDP_ParseDownloadBegin(char *s) char buffer[8192]; qofs_t size, pos, chunk; char *fname; - Cmd_TokenizeString(s+1, false, false); + Cmd_TokenizeString(s, false, false); size = (qofs_t)strtoull(Cmd_Argv(1), NULL, 0); fname = Cmd_Argv(2); @@ -2717,7 +2725,7 @@ void CLDP_ParseDownloadFinished(char *s) if (!dl || !dl->file) return; - Cmd_TokenizeString(s+1, false, false); + Cmd_TokenizeString(s, false, false); VFS_CLOSE (dl->file); @@ -2744,7 +2752,7 @@ void CLDP_ParseDownloadFinished(char *s) return; } - Cmd_TokenizeString(s+1, false, false); + Cmd_TokenizeString(s, false, false); if (size != atoi(Cmd_Argv(1))) { Con_Printf("Download failed: wrong file size\n"); @@ -3670,6 +3678,12 @@ void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caution. CSQC_Init(cls.demoplayback, false, 0); #endif } +static void CLNQ_SendInitialUserInfo(void *ctx, const char *key, const char *value) +{ + char keybuf[2048]; + char valbuf[4096]; + CL_SendClientCommand(true, "setinfo %s %s\n", COM_QuotedString(key, keybuf, sizeof(keybuf), false), COM_QuotedString(value, valbuf, sizeof(valbuf), false)); +} void CLNQ_SignonReply (void) { extern cvar_t topcolor; @@ -3690,27 +3704,16 @@ Con_DPrintf ("CL_SignonReply: %i\n", cls.signon); case 2: CL_SendClientCommand(true, "name \"%s\"\n", name.string); - CL_SendClientCommand(true, "color %i %i\n", topcolor.ival, bottomcolor.ival); - - CL_SendClientCommand(true, "spawn %s", ""); - - if (CPNQ_IS_DP) //dp needs a couple of extras to work properly. + if (cl.haveserverinfo) + Info_Enumerate(cls.userinfo[0], NULL, CLNQ_SendInitialUserInfo); + else if (CPNQ_IS_DP) //dp needs a couple of extras to work properly. don't send them on other servers because that generally results in error messages. { CL_SendClientCommand(true, "rate %s", rate.string); - CL_SendClientCommand(true, "playermodel %s", model.string); CL_SendClientCommand(true, "playerskin %s", skin.string); - /* -#ifdef PEXT_CSQC - { - char *s; - s = Info_ValueForKey(cl.serverinfo, "*csprogs"); - CSQC_Init(false, *s?true:false, atoi(s)); - } -#endif - */ } + CL_SendClientCommand(true, "spawn %s", ""); break; case 3: @@ -4044,6 +4047,7 @@ void CLQ2_ParseClientinfo(int i, char *s) char *model, *name; player_info_t *player; //s contains "name\model/skin" + //q2 doesn't really do much with userinfos. if (i >= MAX_CLIENTS) return; @@ -4051,6 +4055,7 @@ void CLQ2_ParseClientinfo(int i, char *s) player = &cl.players[i]; *player->userinfo = '\0'; + cl.players[i].userinfovalid = true; model = strchr(s, '\\'); if (model) @@ -5039,6 +5044,7 @@ void CL_UpdateUserinfo (void) player = &cl.players[slot]; player->userid = MSG_ReadLong (); Q_strncpyz (player->userinfo, MSG_ReadString(), sizeof(player->userinfo)); + player->userinfovalid = true; CL_ProcessUserInfo (slot, player); @@ -5079,6 +5085,7 @@ void CL_ParseSetInfo (void) Con_DPrintf("SETINFO %s: %s=%s\n", player->name, key, value); Info_SetValueForStarKey (player->userinfo, key, value, sizeof(player->userinfo)); + player->userinfovalid = true; CL_ProcessUserInfo (slot, player); } @@ -6139,7 +6146,7 @@ static void CL_ParseItemTimer(void) for (timer = cl.itemtimers; timer; timer = timer->next) { - if (VectorCompare(timer->origin, org) && timer->entnum == entnum) + if (VectorCompare(timer->origin, org) && timer->entnum == entnum && entnum) break; } if (!timer) @@ -6206,111 +6213,197 @@ void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds from n Con_DLPrintf((cls.state==ca_active)?1:2, "stufftext: %s\n", stufftext); if (!strncmp(stufftext, "fullserverinfo ", 15)) { - Cmd_ExecuteString(stufftext, RESTRICT_SERVER+destsplit); //do this NOW so that it's done before any models or anything are loaded + Cmd_TokenizeString(stufftext, false, false); + if (Cmd_Argc() == 2) + { + cl.haveserverinfo = true; + Q_strncpyz (cl.serverinfo, Cmd_Argv(1), sizeof(cl.serverinfo)); + CL_CheckServerInfo(); + } + #if _MSC_VER > 1200 if (cls.netchan.remote_address.type != NA_LOOPBACK) Sys_RecentServer("+connect", cls.servername, va("%s (%s)", Info_ValueForKey(cl.serverinfo, "hostname"), cls.servername), "Join QW Server"); #endif } - else + else if (!strncmp(stufftext, "//svi ", 6)) //for serverinfo over NQ protocols { - if (!strncmp(stufftext, "//querycmd ", 11)) + Cmd_TokenizeString(stufftext+2, false, false); + Con_DPrintf("SERVERINFO: %s=%s\n", Cmd_Argv(1), Cmd_Argv(2)); + Info_SetValueForStarKey (cl.serverinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_SERVERINFO_STRING); + CL_CheckServerInfo(); + } + +#ifdef NQPROT + //DP's download protocol + else if (cls.protocol == CP_NETQUAKE && !strncmp(stufftext, "cl_serverextension_download ", 28)) //. server lets us know that it supports it. + cl_dp_serverextension_download = true; //warning, this is sent BEFORE svc_serverdata, so cannot use cl.foo + else if (cls.protocol == CP_NETQUAKE && !strncmp(stufftext, "cl_downloadbegin ", 17)) // . server [reliably] lets us know that its going to start sending data. + CLDP_ParseDownloadBegin(stufftext); + else if (cls.protocol == CP_NETQUAKE && !strncmp(stufftext, "cl_downloadfinished ", 20)) // . server [reliably] lets us know that we acked the entire thing + CLDP_ParseDownloadFinished(stufftext); + else if (cls.protocol == CP_NETQUAKE && !strcmp(stufftext, "stopdownload")) //download command reported failure. safe to request the next. + { + if (cls.download) + CL_DownloadFailed(cls.download->remotename, cls.download); + } + + //DP servers use these to report the correct csprogs.dat file+version to use. + //WARNING: these are sent BEFORE svc_serverdata, so we cannot store this state into cl.foo + //we poke the data into cl.serverinfo once we get the following svc_serverdata. + //we then clobber it from a fullserverinfo message if its an fte server running dpp7, but hey. + else if (cls.protocol == CP_NETQUAKE && !strncmp(stufftext, "csqc_progname ", 14)) + COM_ParseOut(stufftext+14, cl_dp_csqc_progsname, sizeof(cl_dp_csqc_progsname)); + else if (cls.protocol == CP_NETQUAKE && !strncmp(stufftext, "csqc_progsize ", 14)) + cl_dp_csqc_progssize = atoi(stufftext+14); + else if (cls.protocol == CP_NETQUAKE && !strncmp(stufftext, "csqc_progcrc ", 13)) + cl_dp_csqc_progscrc = atoi(stufftext+13); + + //NQ servers/mods like spamming this. Its annoying, but we might as well use it if we can, while also muting it. + else if (!strncmp(stufftext, "cl_fullpitch ", 13) || !strncmp(stufftext, "pq_fullpitch ", 13)) + { + if (!cl.haveserverinfo) { - COM_Parse(stufftext + 11); - if (Cmd_Exists(com_token)) + Info_SetValueForStarKey(cl.serverinfo, "maxpitch", (atoi(stufftext+13))? "90":"", sizeof(cl.serverinfo)); + Info_SetValueForStarKey(cl.serverinfo, "minpitch", (atoi(stufftext+13))?"-90":"", sizeof(cl.serverinfo)); + CL_CheckServerInfo(); + } + } +#endif + + else if (!strncmp(stufftext, "//querycmd ", 11)) //for servers to check if a command exists or not. + { + COM_Parse(stufftext + 11); + if (Cmd_Exists(com_token)) + { + Cbuf_AddText ("cmd cmdsupported ", RESTRICT_SERVER+destsplit); + Cbuf_AddText (com_token, RESTRICT_SERVER+destsplit); + Cbuf_AddText ("\n", RESTRICT_SERVER+destsplit); + } + } + else if (!strncmp(stufftext, "//paknames ", 11)) //so that the client knows what to download... + { //there's a couple of prefixes involved etc + Q_strncatz(cl.serverpaknames, stufftext+11, sizeof(cl.serverpaknames)); + cl.serverpakschanged = true; + } + else if (!strncmp(stufftext, "//paks ", 7)) //gives the client a list of hashes to match against + { //the client can re-order for cl_pure support, or download dupes to avoid version mismatches + Q_strncatz(cl.serverpakcrcs, stufftext+7, sizeof(cl.serverpakcrcs)); + cl.serverpakschanged = true; + CL_CheckServerPacks(); + } + else if (!strncmp(stufftext, "//vwep ", 7)) //list of vwep model indexes, because using the normal model precaches wasn't cool enough + { //(from zquake/ezquake) + int i; + char *mname; + Cmd_TokenizeString(stufftext+7, false, false); + for (i = 0; i < Cmd_Argc(); i++) + { + mname = Cmd_Argv(i); + if (strcmp(mname, "-")) { - Cbuf_AddText ("cmd cmdsupported ", RESTRICT_SERVER+destsplit); - Cbuf_AddText (com_token, RESTRICT_SERVER+destsplit); - Cbuf_AddText ("\n", RESTRICT_SERVER+destsplit); - } - } - else if (!strncmp(stufftext, "//paknames ", 11)) - { - Q_strncatz(cl.serverpaknames, stufftext+11, sizeof(cl.serverpaknames)); - cl.serverpakschanged = true; - } - else if (!strncmp(stufftext, "//paks ", 7)) - { - Q_strncatz(cl.serverpakcrcs, stufftext+7, sizeof(cl.serverpakcrcs)); - cl.serverpakschanged = true; - CL_CheckServerPacks(); - } - else if (!strncmp(stufftext, "//vwep ", 7)) - { - int i; - char *mname; - Cmd_TokenizeString(stufftext+7, false, false); - for (i = 0; i < Cmd_Argc(); i++) - { - mname = Cmd_Argv(i); - if (strcmp(mname, "-")) + mname = va("progs/%s.mdl", Cmd_Argv(i)); + Q_strncpyz(cl.model_name_vwep[i], mname, sizeof(cl.model_name_vwep[i])); + if (cls.state == ca_active) { - mname = va("progs/%s.mdl", Cmd_Argv(i)); - Q_strncpyz(cl.model_name_vwep[i], mname, sizeof(cl.model_name_vwep[i])); - if (cls.state == ca_active) - { - CL_CheckOrEnqueDownloadFile(cl.model_name_vwep[i], NULL, 0); - cl.model_precache_vwep[i] = Mod_ForName(cl.model_name_vwep[i], MLV_WARN); - } + CL_CheckOrEnqueDownloadFile(cl.model_name_vwep[i], NULL, 0); + cl.model_precache_vwep[i] = Mod_ForName(cl.model_name_vwep[i], MLV_WARN); } } } - else if (!strncmp(stufftext, "//exectrigger ", 14)) + } + else if (!strncmp(stufftext, "//exectrigger ", 14)) //so that mods can add whatever 'alias grabbedarmour' or whatever triggers that users might want to script responses for, without errors about unknown commands + { + COM_Parse(stufftext + 14); + if (Cmd_AliasExist(com_token, RESTRICT_SERVER)) + Cmd_ExecuteString(com_token, RESTRICT_SERVER); //do this NOW so that it's done before any models or anything are loaded + } + else if (!strncmp(stufftext, "//set ", 6)) //equivelent to regular set, except non-spammy if it doesn't exist, and happens instantly without extra latency. + { + Cmd_ExecuteString(stufftext+2, RESTRICT_SERVER+destsplit); //do this NOW so that it's done before any models or anything are loaded + } + else if (!strncmp(stufftext, "//at ", 5)) //ktx autotrack hints + { + Cam_SetModAutoTrack(atoi(stufftext+5)); + } + else if (!strncmp(stufftext, "//wps ", 5)) //ktx weapon statistics + { + Cmd_TokenizeString(stufftext+5, false, false); + CL_ParseWeaponStats(); + } + else if (!strncmp(stufftext, "//kickfile ", 11)) //FTE sends this to give a more friendly error about modified BSP files, although it could be used for more stuff. + { + flocation_t loc; + Cmd_TokenizeString(stufftext+2, false, false); + if (FS_FLocateFile(Cmd_Argv(1), FSLF_IFFOUND, &loc)) { - COM_Parse(stufftext + 14); - if (Cmd_AliasExist(com_token, RESTRICT_SERVER)) - Cmd_ExecuteString(com_token, RESTRICT_SERVER); //do this NOW so that it's done before any models or anything are loaded + if (!*loc.rawname) + Con_Printf("You have been kicked due to the file "U8("%s")" being modified, inside "U8("%s")"\n", Cmd_Argv(1), loc.search->logicalpath); + else + Con_Printf("You have been kicked due to the file "U8("%s")" being modfied, located at "U8("%s")"\n", Cmd_Argv(1), loc.rawname); } - else if (!strncmp(stufftext, "//set ", 6)) + } + else if (!strncmp(stufftext, "//it ", 5)) //it + { + Cmd_TokenizeString(stufftext+5, false, false); + CL_ParseItemTimer(); + } + else if (!strncmp(stufftext, "//fui ", 6)) //ui . Full user info updates. + { + unsigned int slot; + const char *value; + Cmd_TokenizeString(stufftext+6, false, false); + slot = atoi(Cmd_Argv(0)); + value = Cmd_Argv(1); + if (slot < MAX_CLIENTS) { - Cmd_ExecuteString(stufftext+2, RESTRICT_SERVER+destsplit); //do this NOW so that it's done before any models or anything are loaded + player_info_t *player = &cl.players[slot]; + Con_DPrintf("SETINFO %s: %s\n", player->name, value); + Q_strncpyz(player->userinfo, value, sizeof(player->userinfo)); + player->userinfovalid = true; + CL_ProcessUserInfo (slot, player); } - else if (!strncmp(stufftext, "//at ", 5)) + } + else if (!strncmp(stufftext, "//ui ", 5)) //ui . Full user info updates. + { + unsigned int slot; + const char *key, *value; + Cmd_TokenizeString(stufftext+5, false, false); + slot = atoi(Cmd_Argv(0)); + key = Cmd_Argv(1); + value = Cmd_Argv(2); + if (slot < MAX_CLIENTS) { - Cam_SetModAutoTrack(atoi(stufftext+5)); - } - else if (!strncmp(stufftext, "//wps ", 5)) - { - Cmd_TokenizeString(stufftext+5, false, false); - CL_ParseWeaponStats(); - } - else if (!strncmp(stufftext, "//kickfile ", 11)) - { - flocation_t loc; - Cmd_TokenizeString(stufftext+2, false, false); - if (FS_FLocateFile(Cmd_Argv(1), FSLF_IFFOUND, &loc)) - Con_Printf("You have been kicked due to the file \"%s\" being modified.\n", Cmd_Argv(1)); - } - else if (!strncmp(stufftext, "//it ", 5)) - { - Cmd_TokenizeString(stufftext+5, false, false); - CL_ParseItemTimer(); + player_info_t *player = &cl.players[slot]; + Con_DPrintf("SETINFO %s: %s=%s\n", player->name, key, value); + Info_SetValueForStarKey (player->userinfo, key, value, sizeof(player->userinfo)); + CL_ProcessUserInfo (slot, player); } + } #ifdef PLUGINS - else if (!strncmp(stufftext, "//tinfo ", 8)) - { - Cmd_TokenizeString(stufftext+2, false, false); - CL_ParseTeamInfo(); - Plug_Command_f(); //FIXME: deprecate this call - } - else if (!strncmp(stufftext, "//sn ", 5)) - { - Cmd_TokenizeString(stufftext+2, false, false); - Plug_Command_f(); - } + else if (!strncmp(stufftext, "//tinfo ", 8)) //ktx-team-info + { + Cmd_TokenizeString(stufftext+2, false, false); + CL_ParseTeamInfo(); + Plug_Command_f(); //FIXME: deprecate this call + } + else if (!strncmp(stufftext, "//sn ", 5)) + { + Cmd_TokenizeString(stufftext+2, false, false); + Plug_Command_f(); + } #endif #ifdef CSQC_DAT - else if (CSQC_StuffCmd(destsplit, stufftext, msg)) - { - } + else if (CSQC_StuffCmd(destsplit, stufftext, msg)) + { + } #endif - else - { - if (!strncmp(stufftext, "cmd ", 4)) - Cbuf_AddText (va("p%i ", destsplit+1), RESTRICT_SERVER+destsplit); //without this, in_forceseat can break directed cmds. - Cbuf_AddText (stufftext, RESTRICT_SERVER+destsplit); - Cbuf_AddText ("\n", RESTRICT_SERVER+destsplit); - } + else + { + if (!strncmp(stufftext, "cmd ", 4)) + Cbuf_AddText (va("p%i ", destsplit+1), RESTRICT_SERVER+destsplit); //without this, in_forceseat can break directed cmds. + Cbuf_AddText (stufftext, RESTRICT_SERVER+destsplit); + Cbuf_AddText ("\n", RESTRICT_SERVER+destsplit); } msg++; @@ -6609,7 +6702,6 @@ void CLQW_ParseServerMessage (void) case svc_stufftext: s = MSG_ReadString (); - CL_ParseStuffCmd(s, destsplit); break; @@ -7479,6 +7571,17 @@ qboolean CLNQ_ParseNQPrints(char *s) return false; } +void CLNQ_CheckPlayerIsSpectator(int i) +{ + cl.players[i].spectator = + (cl.players[i].frags==-999) || //DP mods tend to use -999 + (cl.players[i].frags==-99); //crmod uses -99 for spectators, which is annoying. + //we can't add any colour checks, as apparently this fucks up too. + + if (!*cl.players[i].name) + cl.players[i].spectator = false; +} + void CLNQ_ParseServerMessage (void) { const int destsplit = 0; @@ -7564,53 +7667,14 @@ void CLNQ_ParseServerMessage (void) s = MSG_ReadString (); #ifdef PLUGINS - if (Plug_CenterPrintMessage(s, 0)) + if (Plug_CenterPrintMessage(s, destsplit)) #endif - SCR_CenterPrint (0, s, false); + SCR_CenterPrint (destsplit, s, false); break; case svc_stufftext: s = MSG_ReadString (); - if (*s == 1 && !cls.allow_csqc) - { - Con_DPrintf("Proquake: %s\n", s); - s = CLNQ_ParseProQuakeMessage(s); - } - Con_DPrintf ("stufftext: %s\n", s); - if (!strncmp(s, "cl_serverextension_download ", 14)) - { - cl_dp_serverextension_download = true; - } - else if (!strncmp(s, "//svi ", 6)) - { - Cmd_TokenizeString(s+2, false, false); - Con_DPrintf("SERVERINFO: %s=%s\n", Cmd_Argv(1), Cmd_Argv(2)); - Info_SetValueForStarKey (cl.serverinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_SERVERINFO_STRING); - CL_CheckServerInfo(); - } - else if (!strncmp(s, "\ncl_downloadbegin ", 17)) - CLDP_ParseDownloadBegin(s); - else if (!strncmp(s, "\ncl_downloadfinished ", 17)) - CLDP_ParseDownloadFinished(s); - else if (!strcmp(s, "\nstopdownload\n")) - { - if (cls.download) - CL_DownloadFailed(cls.download->remotename, cls.download); - } - else if (!strncmp(s, "csqc_progname ", 14)) - COM_ParseOut(s+14, cl_dp_csqc_progsname, sizeof(cl_dp_csqc_progsname)); - else if (!strncmp(s, "csqc_progsize ", 14)) - cl_dp_csqc_progssize = atoi(s+14); - else if (!strncmp(s, "csqc_progcrc ", 13)) - cl_dp_csqc_progscrc = atoi(s+13); - else if (!strncmp(s, "cl_fullpitch ", 13) || !strncmp(s, "pq_fullpitch ", 13)) - { - // - } - else - { - Cbuf_AddText (s, RESTRICT_SERVER); //no cheating here... - } + CL_ParseStuffCmd(s, destsplit); break; case svc_version: @@ -7798,6 +7862,8 @@ void CLNQ_ParseServerMessage (void) Info_SetValueForKey(cl.players[i].userinfo, "name", cl.players[i].name, sizeof(cl.players[i].userinfo)); if (!cl.nqplayernamechanged) cl.nqplayernamechanged = realtime+2; + + CLNQ_CheckPlayerIsSpectator(i); } break; @@ -7807,7 +7873,10 @@ void CLNQ_ParseServerMessage (void) if (i >= MAX_CLIENTS) MSG_ReadShort(); else + { cl.players[i].frags = MSG_ReadShort(); + CLNQ_CheckPlayerIsSpectator(i); + } break; case svc_updatecolors: { @@ -7818,6 +7887,7 @@ void CLNQ_ParseServerMessage (void) { cl.players[i].rtopcolor = a&0x0f; cl.players[i].rbottomcolor = (a&0xf0)>>4; + CLNQ_CheckPlayerIsSpectator(i); sprintf(cl.players[i].team, "%2d", cl.players[i].rbottomcolor); @@ -7931,7 +8001,7 @@ void CLNQ_ParseServerMessage (void) cl.completed_time = cl.gametime; } cl.intermissionmode = IM_NQFINALE; - SCR_CenterPrint (0, MSG_ReadString (), false); + SCR_CenterPrint (destsplit, MSG_ReadString (), false); break; case svc_cutscene: @@ -7941,7 +8011,7 @@ void CLNQ_ParseServerMessage (void) cl.completed_time = cl.gametime; } cl.intermissionmode = IM_NQCUTSCENE; - SCR_CenterPrint (0, MSG_ReadString (), false); + SCR_CenterPrint (destsplit, MSG_ReadString (), false); break; case svc_sellscreen: //pantsie diff --git a/engine/client/cl_plugin.inc b/engine/client/cl_plugin.inc index fd8389a8..4fd9001d 100644 --- a/engine/client/cl_plugin.inc +++ b/engine/client/cl_plugin.inc @@ -689,7 +689,7 @@ static qintptr_t VARGS Plug_GetNetworkInfo(void *offset, quintptr_t mask, const if (has(capturing)) { #ifdef HAVE_MEDIA_ENCODER - outptr->capturing = Media_Capturing(); + outptr->capturing = Media_Capturing(); #else outptr->capturing = 0; #endif diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index cd888a68..98020bdb 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -3134,6 +3134,7 @@ void SCR_DeInit (void) Cmd_RemoveCommand ("screenshot_mega"); Cmd_RemoveCommand ("screenshot_stereo"); Cmd_RemoveCommand ("screenshot_vr"); + Cmd_RemoveCommand ("screenshot_cubemap"); Cmd_RemoveCommand ("envmap"); Cmd_RemoveCommand ("sizeup"); Cmd_RemoveCommand ("sizedown"); diff --git a/engine/client/client.h b/engine/client/client.h index eabacf2c..960a4841 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -158,6 +158,7 @@ typedef struct player_info_s { int userid; char userinfo[EXTENDED_INFO_STRING]; + qboolean userinfovalid; //set if we actually know the userinfo (ie: false on vanilla nq servers) char teamstatus[128]; float teamstatustime; @@ -739,6 +740,7 @@ typedef struct qboolean stillloading; // set when doing something slow, and the game is still loading. + qboolean haveserverinfo; //nq servers will usually be false. don't override stuff if we already know better. char serverinfo[MAX_SERVERINFO_STRING]; char serverpaknames[1024]; char serverpakcrcs[1024]; @@ -1022,7 +1024,6 @@ extern int rtlights_first, rtlights_max; extern int cl_baselines_count; extern qboolean nomaster; -extern float server_version; // version of server we connected to //============================================================================= diff --git a/engine/client/m_download.c b/engine/client/m_download.c index 0365768b..0ebdf377 100644 --- a/engine/client/m_download.c +++ b/engine/client/m_download.c @@ -11,8 +11,6 @@ #ifdef PACKAGEMANAGER #include "fs.h" -vfsfile_t *FS_XZ_DecompressWriteFilter(vfsfile_t *infile); -vfsfile_t *FS_GZ_DecompressWriteFilter(vfsfile_t *outfile, qboolean autoclosefile); //whole load of extra args for the downloads menu (for the downloads menu to handle engine updates). #ifdef VKQUAKE @@ -81,12 +79,15 @@ extern cvar_t pm_downloads_url; #define DPF_DISPLAYVERSION 0x20 //some sort of conflict, the package is listed twice, so show versions so the user knows what's old. #define DPF_FORGETONUNINSTALL 0x40 //for previously installed packages, remove them from the list if there's no current version any more (should really be automatic if there's no known mirrors) #define DPF_HIDDEN 0x80 //wrong arch, file conflicts, etc. still listed if actually installed. -#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_ENGINE 0x1000 //engine update. replaces old autoupdate mechanism +#define DPF_PLUGIN 0x2000 //this is a plugin package, with a dll + #define DPF_PRESENT (DPF_NATIVE|DPF_CACHED) +#define DPF_DISABLEDINSTALLED (DPF_ENGINE|DPF_PLUGIN) //engines+plugins can be installed without being enabled. //pak.lst //priories <0 //pakX @@ -179,10 +180,12 @@ typedef struct package_s { static qboolean loadedinstalled; static package_t *availablepackages; static int numpackages; -static char *manifestpackage; //metapackage named by the manicfest. +static char *manifestpackages; //metapackage named by the manicfest. +static char *declinedpackages; //metapackage named by the manicfest. static int domanifestinstall; //SECURITY_MANIFEST_* static qboolean doautoupdate; //updates will be marked (but not applied without the user's actions) +static qboolean pkg_updating; //when flagged, further changes are blocked until completion. //FIXME: these are allocated for the life of the exe. changing basedir should purge the list. static int numdownloadablelists = 0; @@ -248,8 +251,8 @@ 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) + //certain updates can be present and not enabled + if (p->flags & DPF_DISABLEDINSTALLED) 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)) @@ -353,7 +356,10 @@ void PM_ValidatePackage(package_t *p) else if (!o) { if (!PM_PurgeOnDisable(p)) + { p->flags |= fl; + VFS_CLOSE(pf); + } else if (p->qhash) { char buf[8]; @@ -683,9 +689,20 @@ static void PM_ParsePackageList(vfsfile_t *f, int parseflags, const char *url, c } else if (!strcmp(Cmd_Argv(1), "updatemode")) { - if (!(parseflags & DPF_ENABLED)) //don't use a downloaded file's version of this + if (parseflags & DPF_ENABLED) //don't use a downloaded file's version of this, only use the local version of it. Cvar_ForceSet(&pm_autoupdate, Cmd_Argv(2)); } + else if (!strcmp(Cmd_Argv(1), "declined")) + { + if (parseflags & DPF_ENABLED) //don't use a downloaded file's version of this, only use the local version of it. + { + Z_Free(declinedpackages); + if (*Cmd_Argv(2)) + declinedpackages = Z_StrDup(Cmd_Argv(2)); + else + declinedpackages = NULL; + } + } else { //erk @@ -774,7 +791,7 @@ static void PM_ParsePackageList(vfsfile_t *f, int parseflags, const char *url, c else if (!strncmp(arg, "test=", 5)) flags |= DPF_TESTING; else if (!strncmp(arg, "stale=", 6) && version==2) - flags &= ~DPF_ENABLED; + flags &= ~DPF_ENABLED; //known about, (probably) cached, but not actually enabled. else if (!strncmp(arg, "installed=", 6) && version>2) flags |= parseflags & DPF_ENABLED; else @@ -908,7 +925,10 @@ static void PM_ParsePackageList(vfsfile_t *f, int parseflags, const char *url, c p->flags |= DPF_ENGINE; } else if (!Q_strcasecmp(p->arch, THISARCH)) - ; + { + if (p->fsroot == FS_ROOT && !*p->gamedir) + p->flags |= DPF_PLUGIN; + } else p->flags |= DPF_HIDDEN; //other engine builds or other cpus are all hidden } @@ -933,6 +953,28 @@ static void PM_ParsePackageList(vfsfile_t *f, int parseflags, const char *url, c } } +#ifdef PLUGINS +void PM_EnumeratePlugins(void (*callback)(const char *name)) +{ + package_t *p; + struct packagedep_s *d; + for (p = availablepackages; p; p = p->next) + { + if ((p->flags & DPF_ENABLED) && (p->flags & DPF_PLUGIN)) + { + for (d = p->deps; d; d = d->next) + { + if (d->dtype == DEP_FILE) + { + if (!Q_strncasecmp(d->name, "fteplug_", 8)) + callback(d->name); + } + } + } + } +} +#endif + void PM_LoadPackages(searchpath_t **oldpaths, const char *parent_pure, const char *parent_logical, searchpath_t *search, unsigned int loadstuff, int minpri, int maxpri) { package_t *p; @@ -983,7 +1025,6 @@ void PM_LoadPackages(searchpath_t **oldpaths, const char *parent_pure, const cha void PM_Shutdown(void) { //free everything... - loadedinstalled = false; pm_downloads_url.modified = false; downloadablessequence++; @@ -1006,6 +1047,7 @@ void PM_Shutdown(void) while (availablepackages) PM_FreePackage(availablepackages); + loadedinstalled = false; } @@ -1062,6 +1104,10 @@ static package_t *PM_MarkedPackage(const char *packagename) static void PM_RevertChanges(void) { package_t *p; + + if (pkg_updating) + return; + for (p = availablepackages; p; p = p->next) { if (p->flags & DPF_ENABLED) @@ -1078,6 +1124,9 @@ static void PM_UnmarkPackage(package_t *package) package_t *o; struct packagedep_s *dep; + if (pkg_updating) + return; + if (!(package->flags & DPF_MARKED)) return; //looks like its already deselected. package->flags &= ~(DPF_MARKED); @@ -1107,6 +1156,9 @@ static void PM_MarkPackage(package_t *package) struct packagedep_s *dep, *dep2; qboolean replacing = false; + if (pkg_updating) + return; + if (package->flags & DPF_MARKED) return; //looks like its already picked. @@ -1206,26 +1258,47 @@ static void PM_MarkPackage(package_t *package) } } +static qboolean PM_NameIsInStrings(const char *strings, const char *match) +{ + char tok[1024]; + while (strings && *strings) + { + strings = COM_ParseStringSetSep(strings, ';', tok, sizeof(tok)); + if (!Q_strcasecmp(tok, match)) //okay its here. + return true; + } + return false; +} + //just flag stuff as needing updating static unsigned int PM_MarkUpdates (void) { unsigned int changecount = 0; package_t *p, *o, *b, *e = NULL; - if (manifestpackage) + if (manifestpackages) { - p = PM_MarkedPackage(manifestpackage); - if (!p) + char tok[1024]; + char *strings = manifestpackages; + while (strings && *strings) { - p = PM_FindPackage(manifestpackage); - if (p) + strings = COM_ParseStringSetSep(strings, ';', tok, sizeof(tok)); + if (PM_NameIsInStrings(declinedpackages, tok)) + continue; + + p = PM_MarkedPackage(tok); + if (!p) { - PM_MarkPackage(p); - changecount++; + p = PM_FindPackage(tok); + if (p) + { + PM_MarkPackage(p); + changecount++; + } } + else if (!(p->flags & DPF_ENABLED)) + changecount++; } - else if (!(p->flags & DPF_PRESENT)) - changecount++; } for (p = availablepackages; p; p = p->next) @@ -1272,6 +1345,61 @@ static unsigned int PM_MarkUpdates (void) return changecount; } +#if defined(M_Menu_Prompt) || defined(SERVERONLY) +#else +static unsigned int PM_ChangeList(char *out, size_t outsize) +{ + unsigned int changes = 0; + const char *change; + package_t *p; + size_t l; + size_t ofs = 0; + if (!outsize) + out = NULL; + else + *out = 0; + for (p = availablepackages; p; p=p->next) + { + if (!(p->flags & DPF_MARKED) != !(p->flags & DPF_ENABLED) || (p->flags & DPF_PURGE)) + { + changes++; + if (!out) + continue; + + if (p->flags & DPF_MARKED) + { + if (p->flags & DPF_PURGE) + change = va(" reinstall %s\n", p->name); + else if (p->flags & DPF_PRESENT) + change = va(" enable %s\n", p->name); + else + change = va(" install %s\n", p->name); + } + else if ((p->flags & DPF_PURGE) || !(p->qhash && (p->flags & DPF_CACHED))) + change = va(" uninstall %s\n", p->name); + else + change = va(" disable %s\n", p->name); + + l = strlen(change); + if (ofs+l >= outsize) + { + Q_strncpyz(out, "Too many changes\n", outsize); + out = NULL; + + break; + } + else + { + memcpy(out+ofs, change, l); + ofs += l; + out[ofs] = 0; + } + } + } + return changes; +} +#endif + static void PM_PrintChanges(void) { qboolean changes = 0; @@ -1309,6 +1437,9 @@ static void PM_ListDownloaded(struct dl_download *dl) f = dl->file; dl->file = NULL; + if (!availablepackages) + Con_Printf("ZOMG NO PACKAGES\n"); + i = dl->user_num; if (dl != downloadablelist[i].curdl) @@ -1335,13 +1466,14 @@ static void PM_ListDownloaded(struct dl_download *dl) if (!downloadablelist[i].received) break; } - if (domanifestinstall == MANIFEST_SECURITY_INSTALLER && manifestpackage) +/* + if (domanifestinstall == MANIFEST_SECURITY_INSTALLER && manifestpackages) { package_t *meta; - meta = PM_MarkedPackage(manifestpackage); + meta = PM_MarkedPackage(manifestpackages); if (!meta) { - meta = PM_FindPackage(manifestpackage); + meta = PM_FindPackage(manifestpackages); if (meta) { PM_RevertChanges(); @@ -1368,6 +1500,7 @@ static void PM_ListDownloaded(struct dl_download *dl) } } } +*/ if ((doautoupdate || domanifestinstall == MANIFEST_SECURITY_DEFAULT) && i == numdownloadablelists) { if (PM_MarkUpdates()) @@ -1474,6 +1607,7 @@ static void COM_QuotedConcat(const char *cat, char *buf, size_t bufsize) } static void PM_WriteInstalledPackages(void) { + char buf[8192]; int i; char *s; package_t *p, *e = NULL; @@ -1488,7 +1622,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); + s = va("set updatemode %s\n", COM_QuotedString(pm_autoupdate.string, buf, sizeof(buf), false)); + VFS_WRITE(f, s, strlen(s)); + s = va("set declined %s\n", COM_QuotedString(declinedpackages?declinedpackages:"", buf, sizeof(buf), false)); VFS_WRITE(f, s, strlen(s)); for (i = 0; i < numdownloadablelists; i++) @@ -1504,7 +1640,6 @@ static void PM_WriteInstalledPackages(void) { 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_ENABLED) @@ -1656,6 +1791,31 @@ static int QDECL PM_ExtractFiles(const char *fname, qofs_t fsize, time_t mtime, return 1; } +//package has been downloaded and installed, but some packages need to be enabled +//(plugins might have other dll dependancies, so this can only happen AFTER the entire package was extracted) +void PM_PackageEnabled(package_t *p) +{ + char ext[8]; + struct packagedep_s *dep; + FS_FlushFSHashFull(); + for (dep = p->deps; dep; dep = dep->next) + { + if (dep->dtype != DEP_FILE) + continue; + COM_FileExtension(dep->name, ext, sizeof(ext)); + if (!stricmp(ext, "pak") || !stricmp(ext, "pk3")) + FS_ReloadPackFiles(); +#ifdef PLUGINS + if ((p->flags & DPF_PLUGIN) && !Q_strncasecmp(dep->name, "fteplug_", 8)) + Cmd_ExecuteString(va("plug_load %s\n", dep->name), RESTRICT_LOCAL); +#endif +#ifdef MENU_DAT + if (!Q_strcasecmp(dep->name, "menu.dat")) + Cmd_ExecuteString("menu_restart\n", RESTRICT_LOCAL); +#endif + } +} + static void PM_StartADownload(void); //callback from PM_StartADownload static void PM_Download_Got(struct dl_download *dl) @@ -1739,8 +1899,10 @@ static void PM_Download_Got(struct dl_download *dl) COM_FileExtension(dep->name, ext, sizeof(ext)); if (!stricmp(ext, "pak") || !stricmp(ext, "pk3")) FS_UnloadPackFiles(); //we reload them after +#ifdef PLUGINS if ((!stricmp(ext, "dll") || !stricmp(ext, "so")) && !Q_strncmp(dep->name, "fteplug_", 8)) Cmd_ExecuteString(va("plug_close %s\n", dep->name), RESTRICT_LOCAL); //try to purge plugins so there's no files left open +#endif nfl = DPF_NATIVE; if (*p->gamedir) @@ -1778,10 +1940,7 @@ static void PM_Download_Got(struct dl_download *dl) PM_ValidatePackage(p); - if (!stricmp(ext, "pak") || !stricmp(ext, "pk3")) - FS_ReloadPackFiles(); - if ((!stricmp(ext, "dll") || !stricmp(ext, "so")) && !Q_strncmp(dep->name, "fteplug_", 8)) - Cmd_ExecuteString(va("plug_load %s\n", dep->name), RESTRICT_LOCAL); + PM_PackageEnabled(p); Z_Free(tempname); PM_StartADownload(); @@ -1911,9 +2070,13 @@ static void PM_StartADownload(void) package_t *p; const int simultaneous = PM_IsApplying(true)?1:2; int i; + qboolean downloading = false; for (p = availablepackages; p && simultaneous > PM_IsApplying(false); p=p->next) { + if (p->download) + downloading = true; + if (p->trymirrors) { //flagged for a (re?)download char *mirror = NULL; @@ -1936,19 +2099,25 @@ static void PM_StartADownload(void) if (i == countof(p->mirror)) { //this appears to be a meta package with no download //just directly install it. + //FIXME: make sure there's no files... p->flags &= ~(DPF_NATIVE|DPF_CACHED|DPF_CORRUPT); p->flags |= DPF_ENABLED; + + Con_Printf("Enabled meta package %s\n", p->name); PM_WriteInstalledPackages(); + PM_PackageEnabled(p); } continue; } - if (!PM_PurgeOnDisable(p)) + if ((p->flags & DPF_PRESENT) && !PM_PurgeOnDisable(p)) { //its in our cache directory, so lets just use that p->trymirrors = 0; p->flags |= DPF_ENABLED; + + Con_Printf("Enabled cached package %s\n", p->name); PM_WriteInstalledPackages(); - FS_ReloadPackFiles(); + PM_PackageEnabled(p); continue; } @@ -1980,7 +2149,7 @@ static void PM_StartADownload(void) { vfsfile_t *raw; raw = FS_OpenVFS(temp, "wb", p->fsroot); - tmpfile = FS_GZ_DecompressWriteFilter(raw, true); + tmpfile = FS_GZ_WriteFilter(raw, true, false); if (!tmpfile) VFS_CLOSE(raw); } @@ -2000,6 +2169,7 @@ static void PM_StartADownload(void) p->download->user_ctx = temp; DL_CreateThread(p->download, NULL, NULL); + downloading = true; } else { @@ -2011,12 +2181,19 @@ static void PM_StartADownload(void) } } } + + //clear the updating flag once there's no more activity needed + pkg_updating = downloading; } //'just' starts doing all the things needed to remove/install selected packages static void PM_ApplyChanges(void) { package_t *p, **link; + if (pkg_updating) + return; + pkg_updating = true; + //delete any that don't exist for (link = &availablepackages; *link ; ) { @@ -2028,22 +2205,33 @@ static void PM_ApplyChanges(void) qboolean reloadpacks = false; struct packagedep_s *dep; + + for (dep = p->deps; dep; dep = dep->next) + { + if (dep->dtype == DEP_FILE) + { + char ext[8]; + COM_FileExtension(dep->name, ext, sizeof(ext)); + if (!stricmp(ext, "pak") || !stricmp(ext, "pk3")) + reloadpacks = true; + +#ifdef PLUGINS //when disabling/purging plugins, be sure to unload them first (unfortunately there might be some latency before this can actually happen). + if ((p->flags & DPF_PLUGIN) && !Q_strncasecmp(dep->name, "fteplug_", 8)) + Cmd_ExecuteString(va("plug_close %s\n", dep->name), RESTRICT_LOCAL); //try to purge plugins so there's no files left open +#endif + + } + } + if (reloadpacks) //okay, some package was removed, unload all, do the deletions/disables, then reload them. This is kinda shit. Would be better to remove individual packages, which would avoid unnecessary config execs. + FS_UnloadPackFiles(); + if ((p->flags & DPF_PURGE) || PM_PurgeOnDisable(p)) { + Con_Printf("Purging package %s\n", p->name); for (dep = p->deps; dep; dep = dep->next) { if (dep->dtype == DEP_FILE) { - if (!reloadpacks) - { - 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); @@ -2061,6 +2249,8 @@ static void PM_ApplyChanges(void) } } } + else + Con_Printf("Disabling package %s\n", p->name); p->flags &= ~(DPF_PURGE|DPF_ENABLED); /* FIXME: windows bug: @@ -2116,20 +2306,131 @@ static void PM_ApplyChanges(void) PM_StartADownload(); //and try to do those downloads. } +#if defined(M_Menu_Prompt) || defined(SERVERONLY) +//if M_Menu_Prompt is a define, then its a stub... +static void PM_PromptApplyChanges(void) +{ + PM_ApplyChanges(); +} +#else +static qboolean PM_DeclinedPackages(char *out, size_t outsize) +{ + size_t ofs = 0; + package_t *p; + qboolean ret = false; + if (manifestpackages) + { + char tok[1024]; + char *strings = manifestpackages; + while (strings && *strings) + { + strings = COM_ParseStringSetSep(strings, ';', tok, sizeof(tok)); + + //already in the list + if (PM_NameIsInStrings(declinedpackages, tok)) + continue; + + p = PM_MarkedPackage(tok); + if (p) //don't mark it as declined if it wasn't + continue; + + p = PM_FindPackage(tok); + if (p) + { //okay, it was declined + ret = true; + if (!out) + { //we're confirming that they should be flagged as declined + if (declinedpackages) + { + char *t = declinedpackages; + declinedpackages = Z_StrDup(va("%s;%s", declinedpackages, tok)); + Z_Free(t); + } + else + declinedpackages = Z_StrDup(tok); + } + else + { //we're collecting a list of package names + char *change = va("%s\n", p->name); + size_t l = strlen(change); + if (ofs+l >= outsize) + { + Q_strncpyz(out, "Too many changes\n", outsize); + out = NULL; + + break; + } + else + { + memcpy(out+ofs, change, l); + ofs += l; + out[ofs] = 0; + } + break; + } + } + } + } + if (!out && ret) + PM_WriteInstalledPackages(); + return ret; +} +static void PM_PromptApplyChanges_Callback(void *ctx, int opt) +{ + pkg_updating = false; + if (opt == 0) + PM_ApplyChanges(); +} +static void PM_PromptApplyChanges(void); +static void PM_PromptApplyDecline_Callback(void *ctx, int opt) +{ + pkg_updating = false; + if (opt == 1) + { + PM_DeclinedPackages(NULL, 0); + PM_PromptApplyChanges(); + } +} +static void PM_PromptApplyChanges(void) +{ + unsigned int changes; + char text[8192]; + //lock it down, so noone can make any changes while this prompt is still displayed + if (pkg_updating) + { + M_Menu_Prompt(PM_PromptApplyChanges_Callback, NULL, "An update is already in progress\nPlease wait\n", NULL, NULL, "Cancel"); + return; + } + pkg_updating = true; + + strcpy(text, "Really decline the following\nrecommendedpackages?\n\n"); + if (PM_DeclinedPackages(text+strlen(text), sizeof(text)-strlen(text))) + M_Menu_Prompt(PM_PromptApplyDecline_Callback, NULL, text, NULL, "Confirm", "Cancel"); + else + { + strcpy(text, "Apply the following changes?\n\n"); + changes = PM_ChangeList(text+strlen(text), sizeof(text)-strlen(text)); + if (!changes) + pkg_updating = false;//no changes... + else + M_Menu_Prompt(PM_PromptApplyChanges_Callback, NULL, text, "Apply", NULL, "Cancel"); + } +} +#endif + //names packages that were listed from the manifest. //if 'mark' is true, then this is an initial install. void PM_ManifestPackage(const char *metaname, int security) { domanifestinstall = security; - Z_Free(manifestpackage); + Z_Free(manifestpackages); if (metaname && security) { - manifestpackage = Z_StrDup(metaname); - if (security) - PM_UpdatePackageList(false, false); + manifestpackages = Z_StrDup(metaname); +// PM_UpdatePackageList(false, false); } else - manifestpackage = NULL; + manifestpackages = NULL; } void PM_Command_f(void) @@ -2181,7 +2482,7 @@ void PM_Command_f(void) else status = ""; - Con_Printf(" ^[%s%s%s%s^] %s^9 %s\n", markup, p->name, p->arch?":":"", p->arch?p->arch:"", status, strcmp(p->name, p->title)?p->title:""); + Con_Printf(" ^[%s%s%s%s^] %s^9 %s (%s%s)\n", markup, p->name, p->arch?":":"", p->arch?p->arch:"", status, strcmp(p->name, p->title)?p->title:"", p->version, (p->flags&DPF_TESTING)?"-testing":""); } Con_Printf("\n"); } @@ -2277,24 +2578,29 @@ void PM_Command_f(void) else if (!strcmp(act, "apply")) { Con_Printf("Applying package changes\n"); - PM_ApplyChanges(); + if (qrenderer != QR_NONE) + PM_PromptApplyChanges(); + else + PM_ApplyChanges(); } else if (!strcmp(act, "changes")) { PM_PrintChanges(); } - else if (!strcmp(act, "reset")) + else if (!strcmp(act, "reset") || !strcmp(act, "revert")) { - Con_Printf("Applying package changes\n"); PM_RevertChanges(); - PM_ApplyChanges(); - PM_PrintChanges(); } else if (!strcmp(act, "upgrade")) { - Con_Printf("Updating packages\n"); - PM_MarkUpdates(); - PM_ApplyChanges(); + unsigned int changes = PM_MarkUpdates(); + if (changes) + { + Con_Printf("%u packages flagged\n"); + PM_PromptApplyChanges(); + } + else + Con_Printf("Already using latest versions of all packages\n"); } else if (!strcmp(act, "add") || !strcmp(act, "get") || !strcmp(act, "install") || !strcmp(act, "enable")) { @@ -2424,6 +2730,9 @@ void PM_Command_f (void) 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_EnumeratePlugins(void (*callback)(const char *name)) +{ +} void PM_ManifestPackage(const char *metaname, int security) { } @@ -2666,7 +2975,7 @@ 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) { - PM_ApplyChanges(); + PM_PromptApplyChanges(); return true; } return false; diff --git a/engine/client/m_items.c b/engine/client/m_items.c index 51c12758..d7d2b4b1 100644 --- a/engine/client/m_items.c +++ b/engine/client/m_items.c @@ -86,8 +86,7 @@ extern cvar_t cl_cursor; extern cvar_t cl_cursorsize; extern cvar_t cl_cursorbias; extern cvar_t m_preset_chosen; -menu_t *currentmenu; -menu_t *firstmenu; +menu_t *topmenu; menuoption_t *M_NextSelectableItem(menu_t *m, menuoption_t *old); #ifdef HEXEN2 @@ -328,12 +327,50 @@ static qboolean MI_Selectable(menuoption_t *op) } } +static qboolean M_MouseMoved(menu_t *menu) +{ + menuoption_t *option; +// if (menu->prev && !menu->exclusive) +// if (M_MouseMoved(menu->prev)) +// return true; + for(option = menu->options; option; option = option->common.next) + { + if (mousemoved && !bindingactive && !option->common.ishidden) + { + if (mousecursor_x > menu->xpos+option->common.posx-option->common.extracollide && mousecursor_x < menu->xpos+option->common.posx+option->common.width) + { + if (mousecursor_y > menu->ypos+option->common.posy && mousecursor_y < menu->ypos+option->common.posy+option->common.height) + { + if (MI_Selectable(option)) + { + if (menu->mouseitem != option) + { +/* if (!option->common.noselectionsound && vid.activeapp) + { +#ifdef HEXEN2 + if (M_GameType() == MGT_HEXEN2) + S_LocalSound ("raven/menu1.wav"); + else +#endif + S_LocalSound ("misc/menu1.wav"); + } +*/ + menu->mouseitem = option; + menu->tooltiptime = realtime + 1; + MenuTooltipChange(menu, menu->mouseitem->common.tooltip); + } +// if (menu->cursoritem) +// menu->cursoritem->common.posy = menu->selecteditem->common.posy; + } + } + } + } + } + return true; +} + static void M_CheckMouseMove(void) { - qboolean foundexclusive = false; - menu_t *menu; - menuoption_t *option; - if (omousex != (int)mousecursor_x || omousey != (int)mousecursor_y) mousemoved = true; else @@ -342,51 +379,7 @@ static void M_CheckMouseMove(void) omousey = mousecursor_y; if (mousemoved) - { - for (menu = firstmenu; menu; menu = menu->parent) - { - if (menu->exclusive) - { - if (foundexclusive) - continue; - foundexclusive=true; - } - - for(option = menu->options; option; option = option->common.next) - { - if (mousemoved && !bindingactive && !option->common.ishidden) - { - if (mousecursor_x > menu->xpos+option->common.posx-option->common.extracollide && mousecursor_x < menu->xpos+option->common.posx+option->common.width) - { - if (mousecursor_y > menu->ypos+option->common.posy && mousecursor_y < menu->ypos+option->common.posy+option->common.height) - { - if (MI_Selectable(option)) - { - if (menu->selecteditem != option) - { - if (!option->common.noselectionsound && vid.activeapp) - { -#ifdef HEXEN2 - if (M_GameType() == MGT_HEXEN2) - S_LocalSound ("raven/menu1.wav"); - else -#endif - S_LocalSound ("misc/menu1.wav"); - } - - menu->selecteditem = option; - menu->tooltiptime = realtime + 1; - MenuTooltipChange(menu, menu->selecteditem->common.tooltip); - } - if (menu->cursoritem) - menu->cursoritem->common.posy = menu->selecteditem->common.posy; - } - } - } - } - } - } - } + M_MouseMoved(topmenu); } static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, menu_t *menu) @@ -395,6 +388,20 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, menu_t *menu mpic_t *p; int pw,ph; + if (option && option->common.type == mt_box && !option->common.ishidden) + { + Draw_TextBox(xpos+option->common.posx, ypos+option->common.posy, option->box.width, option->box.height); + option = option->common.next; + } + + if (menu == topmenu && menu->mouseitem) + { + float alphamax = 0.5, alphamin = 0.2; + R2D_ImageColours(.5,.4,0,(sin(realtime*2)+1)*0.5*(alphamax-alphamin)+alphamin); + R2D_FillBlock(xpos+menu->mouseitem->common.posx, ypos+menu->mouseitem->common.posy, menu->mouseitem->common.width, menu->mouseitem->common.height); + R2D_ImageColours(1,1,1,1); + } + while (option) { if (!option->common.ishidden) @@ -411,7 +418,7 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, menu_t *menu Draw_FunString(xpos+option->common.posx, ypos+option->common.posy, "^Ue00d"); } else if (option->common.width) - Draw_FunStringWidth(xpos + option->common.posx, ypos+option->common.posy, option->text.text, option->common.width, true, option->text.isred); + Draw_FunStringWidth(xpos + option->common.posx, ypos+option->common.posy, option->text.text, option->common.width, option->text.rightalign, option->text.isred); else if (option->text.isred) Draw_AltFunString(xpos+option->common.posx, ypos+option->common.posy, option->text.text); else @@ -524,9 +531,9 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, menu_t *menu } #if 0 if (on) - Draw_Character (x, y, 131); + Draw_Character (x, y, 0xe083); else - Draw_Character (x, y, 129); + Draw_Character (x, y, 0xe081); #endif if (!menu->cursoritem && menu->selecteditem == option) Draw_AltFunString (x, y, on ? "on" : "off"); @@ -619,15 +626,19 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, menu_t *menu static void MenuDraw(menu_t *menu) { + if (!menu->exclusive && menu->prev) //popup menus draw the one underneath them + MenuDraw(menu->prev); + if (!menu->dontexpand) menu->xpos = ((vid.width - 320)>>1); if (menu->predraw) menu->predraw(menu); MenuDrawItems(menu->xpos, menu->ypos, menu->options, menu); // draw tooltip - if (menu->selecteditem && menu->tooltip && realtime > menu->tooltiptime) + if (menu->mouseitem && menu->tooltip && realtime > menu->tooltiptime) { - menuoption_t *option = menu->selecteditem; + menuoption_t *option = menu->mouseitem; + if (omousex > menu->xpos+option->common.posx && omousex < menu->xpos+option->common.posx+option->common.width) if (omousey > menu->ypos+option->common.posy && omousey < menu->ypos+option->common.posy+option->common.height) { @@ -688,7 +699,8 @@ menutext_t *MC_AddWhiteText(menu_t *menu, int lhs, int rhs, int y, const char *t n->common.iszone = true; n->common.posx = lhs; n->common.posy = y; - n->common.width = (rhs && rightalign)?rhs-lhs:0; + n->common.width = (rhs)?rhs-lhs:0; + n->rightalign = rightalign; if (text) { n->text = (char*)(n+1); @@ -1410,7 +1422,7 @@ void MC_Slider_Key(menuslider_t *option, int key) void MC_CheckBox_Key(menucheck_t *option, menu_t *menu, int key) { - if (key != K_ENTER && key != K_KP_ENTER && key != K_LEFTARROW && key != K_RIGHTARROW && key != K_MOUSE1) + if (key != K_ENTER && key != K_KP_ENTER && key != K_LEFTARROW && key != K_RIGHTARROW && key != K_MWHEELUP && key != K_MWHEELDOWN && key != K_MOUSE1) return; if (option->func) option->func(option, menu, CHK_TOGGLE); @@ -1476,7 +1488,7 @@ void MC_EditBox_Key(menuedit_t *edit, int key, unsigned int unicode) void MC_Combo_Key(menucombo_t *combo, int key) { - if (key == K_ENTER || key == K_KP_ENTER || key == K_RIGHTARROW || key == K_MOUSE1) + if (key == K_ENTER || key == K_KP_ENTER || key == K_RIGHTARROW || key == K_MWHEELDOWN || key == K_MOUSE1) { combo->selectedoption++; if (combo->selectedoption >= combo->numoptions) @@ -1487,7 +1499,7 @@ changed: Cvar_Set(combo->cvar, (char *)combo->values[combo->selectedoption]); S_LocalSound ("misc/menu2.wav"); } - else if (key == K_LEFTARROW) + else if (key == K_LEFTARROW || key == K_MWHEELUP) { combo->selectedoption--; if (combo->selectedoption < 0) @@ -1496,41 +1508,16 @@ changed: } } -void M_AddMenuFront (menu_t *menu) -{ - menu_t *pmenu; - m_state = m_complex; - if (!firstmenu) - { - M_AddMenu(menu); - return; - } - pmenu = firstmenu; - while(pmenu->parent) - pmenu = pmenu->parent; - pmenu->parent = menu; - menu->child = pmenu; - menu->parent = NULL; - - menu->exclusive = true; - - menu->xpos = ((vid.width - 320)>>1); - - currentmenu = menu; -} - void M_AddMenu (menu_t *menu) { m_state = m_complex; - menu->parent = firstmenu; - if (firstmenu) - firstmenu->child = menu; - menu->child = NULL; - firstmenu = menu; + menu->prev = topmenu; + if (topmenu) + topmenu->next = menu; + menu->next = NULL; + topmenu = menu; menu->exclusive = true; - - currentmenu = menu; } menu_t *M_CreateMenu (int extrasize) { @@ -1550,23 +1537,29 @@ menu_t *M_CreateMenuInfront (int extrasize) menu->iszone=true; menu->data = menu+1; - M_AddMenuFront(menu); + M_AddMenu(menu); + menu->xpos = ((vid.width - 320)>>1); menu->exclusive = false; return menu; } void M_HideMenu (menu_t *menu) { - if (menu == firstmenu) - firstmenu = menu->parent; + if (menu == topmenu) + { + topmenu = menu->prev; + if (topmenu) + topmenu->next = NULL; + menu->prev = NULL; + } else { menu_t *prev; - prev = menu->child; + prev = menu->next; if (prev) - prev->parent = menu->parent; - if (menu->parent) - menu->parent->child = menu; + prev->prev = menu->prev; + if (menu->prev) + menu->prev->next = menu; } } void M_RemoveMenu (menu_t *menu) @@ -1580,20 +1573,20 @@ void M_RemoveMenu (menu_t *menu) if (menu->remove) menu->remove(menu); - if (menu == firstmenu) + if (menu == topmenu) { - firstmenu = menu->parent; - if (firstmenu) - firstmenu->child = NULL; + topmenu = menu->prev; + if (topmenu) + topmenu->next = NULL; } else { menu_t *prev; - prev = menu->child; + prev = menu->next; if (prev) - prev->parent = menu->parent; - if (menu->parent) - menu->parent->child = menu; + prev->prev = menu->prev; + if (menu->prev) + menu->prev->next = menu; } op = menu->options; @@ -1617,16 +1610,13 @@ void M_RemoveMenu (menu_t *menu) menu->iszone=false; Z_Free(menu); } - - if (menu == currentmenu) - currentmenu = firstmenu; } void M_ReloadMenus(void) { menu_t *m; - for (m = firstmenu; m; m = m->parent) + for (m = topmenu; m; m = m->prev) { if (m->reset) m->reset(m); @@ -1637,11 +1627,11 @@ void M_RemoveAllMenus (qboolean leaveprompts) { menu_t **link, *m; - for (link = &firstmenu; *link; ) + for (link = &topmenu; *link; ) { m = *link; if ((m->persist || !m->exclusive) && leaveprompts) - link = &m->parent; + link = &m->prev; else M_RemoveMenu(m); } @@ -1649,17 +1639,14 @@ void M_RemoveAllMenus (qboolean leaveprompts) } void M_MenuPop_f (void) { - if (!currentmenu) + if (!topmenu) return; - M_RemoveMenu(currentmenu); + M_RemoveMenu(topmenu); } void M_Complex_Draw(void) { - menu_t *menu, *cmenu; - qboolean foundexclusive = false; - - if (!firstmenu) + if (!topmenu) { Key_Dest_Remove(kdm_emenu); m_state = m_none; @@ -1668,19 +1655,7 @@ void M_Complex_Draw(void) M_CheckMouseMove(); - for (menu = firstmenu; menu; ) - { - cmenu = menu; - menu = menu->parent; //this way we can remove the currently drawn menu easily (if needed) - - if (cmenu->exclusive) - { - if (foundexclusive) - continue; - foundexclusive=true; - } - MenuDraw(cmenu); - } + MenuDraw(topmenu); } menuoption_t *M_NextItem(menu_t *m, menuoption_t *old) @@ -1707,11 +1682,11 @@ menuoption_t *M_NextSelectableItem(menu_t *m, menuoption_t *old) while (1) { if (!op) - op = currentmenu->options; + op = m->options; op = M_NextItem(m, op); if (!op) - op = currentmenu->options; + op = m->options; if (op == old) { @@ -1731,18 +1706,18 @@ menuoption_t *M_PrevSelectableItem(menu_t *m, menuoption_t *old) menuoption_t *op; if (!old) - old = currentmenu->options; + old = m->options; op = old; while (1) { if (!op) - op = currentmenu->options; + op = m->options; op = op->common.next; if (!op) - op = currentmenu->options; + op = m->options; if (op == old) return old; //whoops. @@ -1755,6 +1730,7 @@ menuoption_t *M_PrevSelectableItem(menu_t *m, menuoption_t *old) void M_Complex_Key(int key, int unicode) { + menu_t *currentmenu = topmenu; if (!currentmenu) return; //erm... @@ -1854,6 +1830,34 @@ void M_Complex_Key(int key, int unicode) currentmenu->cursoritem->common.posy = currentmenu->selecteditem->common.posy; } break; + + case K_MOUSE1: + case K_MOUSE3: + case K_MOUSE4: + case K_MOUSE5: + case K_MOUSE6: + case K_MOUSE7: + case K_MOUSE8: + case K_MOUSE9: + case K_MOUSE10: + case K_MWHEELUP: + case K_MWHEELDOWN: + if (!currentmenu->mouseitem) + break; + if (currentmenu->mouseitem && currentmenu->selecteditem != currentmenu->mouseitem) + { + currentmenu->selecteditem = currentmenu->mouseitem; +#ifdef HEXEN2 + if (M_GameType() == MGT_HEXEN2) + S_LocalSound ("raven/menu1.wav"); + else +#endif + S_LocalSound ("misc/menu1.wav"); + + if (currentmenu->cursoritem) + currentmenu->cursoritem->common.posy = currentmenu->selecteditem->common.posy; + } + //fall through default: if (!currentmenu->selecteditem) { @@ -2131,12 +2135,14 @@ void M_Menu_Main_f (void) } else { + int width; m_state = m_complex; Key_Dest_Add(kdm_emenu); mainm = M_CreateMenu(0); - p = R2D_SafeCachePic("gfx/ttl_main.lmp"); - if (R_GetShaderSizes(p, NULL, NULL, true) <= 0) + p = R2D_SafeCachePic("gfx/mainmenu.lmp"); + R2D_SafeCachePic("gfx/ttl_main.lmp"); + if (R_GetShaderSizes(p, &width, NULL, true) <= 0) { MC_AddRedText(mainm, 16, 170, 0, "MAIN MENU", false); @@ -2152,21 +2158,18 @@ void M_Menu_Main_f (void) MC_AddCenterPicture(mainm, 4, 24, "gfx/ttl_main.lmp"); MC_AddPicture(mainm, 72, 32, 240, 112, "gfx/mainmenu.lmp"); - - p = R2D_SafeCachePic("gfx/mainmenu.lmp"); - b=MC_AddConsoleCommand (mainm, 72, 312, 32, "", "menu_single\n"); b->common.tooltip = "Start singleplayer Quake game."; mainm->selecteditem = (menuoption_t *)b; - b->common.width = p->width; + b->common.width = width; b->common.height = 20; b=MC_AddConsoleCommand (mainm, 72, 312, 52, "", "menu_multi\n"); b->common.tooltip = "Multiplayer menu."; - b->common.width = p->width; + b->common.width = width; b->common.height = 20; b=MC_AddConsoleCommand (mainm, 72, 312, 72, "", "menu_options\n"); b->common.tooltip = "Options menu."; - b->common.width = p->width; + b->common.width = width; b->common.height = 20; if (m_helpismedia.value) { @@ -2178,7 +2181,7 @@ void M_Menu_Main_f (void) b=MC_AddConsoleCommand(mainm, 72, 312, 92, "", "help\n"); b->common.tooltip = "Help menu."; } - b->common.width = p->width; + b->common.width = width; b->common.height = 20; b=MC_AddConsoleCommand (mainm, 72, 312, 112, "", "menu_quit\n"); #ifdef FTE_TARGET_WEB @@ -2186,7 +2189,7 @@ void M_Menu_Main_f (void) #else b->common.tooltip = "Exit to DOS."; #endif - b->common.width = p->width; + b->common.width = width; b->common.height = 20; mainm->cursoritem = (menuoption_t *)MC_AddCursor(mainm, &resel, 54, 32); diff --git a/engine/client/m_master.c b/engine/client/m_master.c index 0c5f0880..33d40cfd 100644 --- a/engine/client/m_master.c +++ b/engine/client/m_master.c @@ -1060,7 +1060,7 @@ static void CalcFilters(menu_t *menu) if (info->filter[2]) Master_SetMaskInteger(false, SLKEY_BASEGAME, SS_QUAKEWORLD, SLIST_TEST_NOTEQUAL); } if (info->filter[3]) Master_SetMaskInteger(false, SLKEY_FLAGS, SS_PROXY, SLIST_TEST_NOTCONTAIN); - if (info->filter[5]) Master_SetMaskInteger(false, SLKEY_FLAGS, SS_FAVORITE, SLIST_TEST_CONTAINS); + if (!info->filter[5]) Master_SetMaskInteger(false, SLKEY_FLAGS, SS_FAVORITE, SLIST_TEST_CONTAINS); if (info->filter[6]) Master_SetMaskInteger(false, SLKEY_NUMHUMANS, 0, SLIST_TEST_NOTEQUAL); if (info->filter[7]) Master_SetMaskInteger(false, SLKEY_FREEPLAYERS, 0, SLIST_TEST_NOTEQUAL); @@ -1075,7 +1075,7 @@ static qboolean SL_ReFilter (menucheck_t *option, menu_t *menu, chk_set_t set) switch(set) { case CHK_CHECKED: - return info->filter[option->bits]; + return !info->filter[option->bits]; case CHK_TOGGLE: if (option->bits>0) { @@ -1201,22 +1201,23 @@ void M_Menu_ServerList2_f(void) #ifdef NQPROT if (M_GameType() == MGT_QUAKE1) { - MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*1, "Hide NQ ", SL_ReFilter, 1); - MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*2, "Hide QW ", SL_ReFilter, 2); + MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*1, "Show NQ ", SL_ReFilter, 1); + MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*2, "Show QW ", SL_ReFilter, 2); } #endif - MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*3, "Hide Proxies", SL_ReFilter, 3); + MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*3, "Show Proxies", SL_ReFilter, 3); info->filtertext = MC_AddEditCvar (menu, 128, 200, vid.height - 64+8*4, "Filter ", sb_filtertext.name, true); MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*5, "Only Favs ", SL_ReFilter, 5); - MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*6, "Hide Empty", SL_ReFilter, 6); - MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*7, "Hide Full ", SL_ReFilter, 7); + MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*6, "Show Empty", SL_ReFilter, 6); + MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*7, "Show Full ", SL_ReFilter, 7); MC_AddCommand(menu, 64, 320, 0, info->refreshtext, SL_DoRefresh); info->filter[1] = !!sb_hidenetquake.value; info->filter[2] = !!sb_hidequakeworld.value; info->filter[3] = !!sb_hideproxies.value; + info->filter[5] = true;//!sb_showonlyfavourites.value; info->filter[6] = !!sb_hideempty.value; info->filter[7] = !!sb_hidefull.value; diff --git a/engine/client/m_options.c b/engine/client/m_options.c index 8f12becd..82343d27 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -181,20 +181,6 @@ void M_Menu_Options_f (void) #endif int y; -#ifdef WEBCLIENT - static const char *autoupopts[] = { - "Off", - "Tested(Recommended)", - "Untested(Latest)", - NULL - }; - static const char *autoupvals[] = { - "0", - "1", - "2", - NULL - }; -#endif static const char *projections[] = { "Regular", "Stereographic", @@ -237,6 +223,9 @@ void M_Menu_Options_f (void) menubulk_t bulk[] = { MB_CONSOLECMD("Customize controls", "menu_keys\n", "Modify keyboard and mouse inputs."), +#ifdef WEBCLIENT + MB_CONSOLECMD("Updates and packages", "menu_download\n", "Modify keyboard and mouse inputs."), +#endif MB_CONSOLECMD("Go to console", "toggleconsole\nplay misc/menu2.wav\n", "Open up the engine console."), MB_CONSOLECMD("Reset to defaults", "cvarreset *\nexec default.cfg\nplay misc/menu2.wav\n", "Reloads the default configuration."), MB_CONSOLECMD("Save all settings", "cfg_save\n", "Writes changed settings out to a config file."), @@ -250,9 +239,6 @@ void M_Menu_Options_f (void) MB_CHECKBOXCVAR("Lookspring", lookspring, 0), MB_CHECKBOXCVAR("Lookstrafe", lookstrafe, 0), MB_CHECKBOXCVAR("Windowed Mouse", _windowed_mouse, 0), -#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), #endif @@ -760,7 +746,7 @@ const char *presetexec[] = "seta gl_polyblend 0;" "seta gl_flashblend 0;" "seta gl_specular 0;" - "seta r_deluxemapping 0;" + "seta r_deluxmapping 0;" "seta r_loadlit 0;" "seta r_fastsky 1;" "seta r_drawflame 0;" @@ -880,12 +866,12 @@ const char *presetexec[] = "r_shadow_realtime_dlight 1;" // "gl_detail 1;" "r_lightstylesmooth 1;" - "r_deluxemapping 1;" + "r_deluxmapping 2;" "gl_texture_anisotropic_filtering 4;" , // realtime options "r_bloom 1;" - "r_deluxemapping 0;" //won't be seen anyway + "r_deluxmapping 0;" //won't be seen anyway "r_particledesc \"high tsshaft\";" "r_waterstyle 3;" "r_glsl_offsetmapping 1;" @@ -944,7 +930,7 @@ void M_Menu_Preset_f (void) #else bias = 1; #endif - if (r_deluxemapping_cvar.ival) + if (r_deluxmapping_cvar.ival) item = 2; //nice else if (gl_load24bit.ival) item = 3; //normal @@ -1216,7 +1202,6 @@ void M_Menu_Textures_f (void) MB_COMBOCVAR("Anisotropy", gl_texture_anisotropic_filtering, anisotropylevels, anisotropyvalues, NULL), MB_SPACING(4), MB_CHECKBOXCVAR("Software-style Rendering", r_softwarebanding_cvar, 0), - MB_CHECKBOXCVAR("Deluxemapping", r_deluxemapping_cvar, 0), MB_CHECKBOXCVAR("Specular Highlights", gl_specular, 0), // MB_CHECKBOXCVAR("Detail Textures", gl_detail, 0), MB_CHECKBOXCVAR("offsetmapping", r_glsl_offsetmapping, 0), @@ -1481,6 +1466,7 @@ void M_Menu_Lighting_f (void) MB_SPACING(4), #endif MB_COMBOCVAR("LIT Loading", r_loadlits, loadlitopts, loadlitvalues, "Determines if the engine should use external colored lighting for maps. The generated setting will cause the engine to generate colored lighting for maps that don't have the associated data."), + MB_COMBOCVAR("Deluxmapping", r_deluxmapping_cvar, loadlitopts, loadlitvalues, "Controls whether static lighting should respond to lighting directions."), MB_CHECKBOXCVAR("Lightstyle Lerp", r_lightstylesmooth, 0), MB_SPACING(4), MB_COMBOCVAR("Flash Blend", r_flashblend, fbopts, fbvalues, "Disables or enables the spherical light effect for dynamic lights. Traced means the sphere effect will be line of sight checked before displaying the effect."), @@ -2507,6 +2493,9 @@ void M_Menu_Video_f (void) #ifdef VKQUAKE "Vulkan (Experimental)", #endif +#ifdef D3D8QUAKE + "Direct3D 8 (limited)", +#endif #ifdef D3D9QUAKE "Direct3D 9 (limited)", #endif @@ -2533,6 +2522,9 @@ void M_Menu_Video_f (void) #ifdef VKQUAKE "vk", #endif +#ifdef D3D8QUAKE + "d3d8", +#endif #ifdef D3D9QUAKE "d3d9", #endif diff --git a/engine/client/m_script.c b/engine/client/m_script.c index 238672d8..089cb8ba 100644 --- a/engine/client/m_script.c +++ b/engine/client/m_script.c @@ -89,8 +89,8 @@ void M_MenuS_Clear_f (void) void M_MenuS_Script_f (void) //create a menu. { + extern menu_t *topmenu; int items; - extern menu_t *currentmenu; menu_t *oldmenu; char *alias = Cmd_Argv(1); Key_Dest_Add(kdm_emenu); @@ -115,14 +115,14 @@ void M_MenuS_Script_f (void) //create a menu. M_MenuS_Clear_f(); } - oldmenu = currentmenu; + oldmenu = topmenu; menu_script = M_CreateMenu(0); if (oldmenu) { M_HideMenu(oldmenu); //bring to front M_AddMenu(oldmenu); - } + } menu_script->remove = M_Script_Remove; menu_script->key = M_Script_Key; diff --git a/engine/client/menu.c b/engine/client/menu.c index 2c8c2385..b3a14c10 100644 --- a/engine/client/menu.c +++ b/engine/client/menu.c @@ -748,49 +748,79 @@ static void M_Menu_Prompt_Cancel (struct menu_s *gm) if (callback) callback(ctx, -1); } -void M_Menu_Prompt (void (*callback)(void *, int), void *ctx, char *m1, char *m2, char *m3, char *optionyes, char *optionno, char *optioncancel) +void M_Menu_Prompt (void (*callback)(void *, int), void *ctx, const char *messages, char *optionyes, char *optionno, char *optioncancel) { promptmenu_t *m; char *t; + int y; + int x = 64, w = 224; Key_Dest_Add(kdm_emenu); m_state = m_complex; - m = (promptmenu_t*)M_CreateMenuInfront(sizeof(*m) - sizeof(m->m) + strlen(m1)+strlen(m2)+strlen(m3)+strlen(optionyes)+strlen(optionyes)+strlen(optioncancel)+6); + m = (promptmenu_t*)M_CreateMenuInfront(sizeof(*m) - sizeof(m->m) + strlen(messages)+(optionyes?strlen(optionyes):0)+(optionno?strlen(optionno):0)+(optioncancel?strlen(optioncancel):0)+6); m->callback = callback; m->ctx = ctx; m->m.remove = M_Menu_Prompt_Cancel; t = (char*)(m+1); - strcpy(t, m1); - m1 = t; - t += strlen(t)+1; - strcpy(t, m2); - m2 = t; - t += strlen(t)+1; - strcpy(t, m3); - m3 = t; - t += strlen(t)+1; - strcpy(t, optionyes); - optionyes = t; - t += strlen(t)+1; - strcpy(t, optionno); - optionno = t; - t += strlen(t)+1; - strcpy(t, optioncancel); - optioncancel = t; + if (optionyes) + { + strcpy(t, optionyes); + optionyes = t; + t += strlen(t)+1; + } + if (optionno) + { + strcpy(t, optionno); + optionno = t; + t += strlen(t)+1; + } + if (optioncancel) + { + strcpy(t, optioncancel); + optioncancel = t; + t += strlen(t)+1; + } - MC_AddWhiteText(&m->m, 64, 0, 84, m1, false); - MC_AddWhiteText(&m->m, 64, 0, 92, m2, false); - MC_AddWhiteText(&m->m, 64, 0, 100, m3, false); - - m->b_yes = MC_AddCommand(&m->m, 64, 0, 116, optionyes, M_Menu_Prompt_Button); - m->b_no = MC_AddCommand(&m->m, 144,0, 116, optionno, M_Menu_Prompt_Button); - m->b_cancel = MC_AddCommand(&m->m, 224,0, 116, optioncancel, M_Menu_Prompt_Button); + y = 76; + y += 8; //top border + strcpy(t, messages); + + for(messages = t; t; y += 8) + { + messages = t; + t = strchr(messages, '\n'); + if (t) + *t++ = 0; + if (*messages) + MC_AddWhiteText(&m->m, x, x+w, y, messages, 2); + } + + y += 8; //blank space + + if (optionyes) + { + m->b_yes = MC_AddCommand(&m->m, x, x+70, y, optionyes, M_Menu_Prompt_Button); + m->b_yes->rightalign = 2; + } + if (optionno) + { + m->b_no = MC_AddCommand(&m->m, x+w/3, x+(2*w)/3, y, optionno, M_Menu_Prompt_Button); + m->b_no->rightalign = 2; //actually center align + } + if (optioncancel) + { + m->b_cancel = MC_AddCommand(&m->m, x+(2*w)/3, x+w, y, optioncancel, M_Menu_Prompt_Button); + m->b_cancel->rightalign = 2; + } + y += 8; //footer + + y += 8; //bottom border m->m.selecteditem = (menuoption_t *)m->b_cancel; - MC_AddBox (&m->m, 56, 76, 25, 5); + MC_AddBox (&m->m, x-8, 76, (w/8), (y-76)/8); } //============================================================================= @@ -1372,8 +1402,8 @@ void M_Draw (int uimenu) #ifndef NOBUILTINMENUS if ((!menu_script || scr_con_current) && !m_recursiveDraw) { - extern menu_t *firstmenu; - if (m_state == m_complex && firstmenu && firstmenu->selecteditem && firstmenu->selecteditem->common.type == mt_slider && (firstmenu->selecteditem->slider.var == &v_gamma || firstmenu->selecteditem->slider.var == &v_contrast)) + extern menu_t *topmenu; + if (m_state == m_complex && topmenu && topmenu->selecteditem && topmenu->selecteditem->common.type == mt_slider && (topmenu->selecteditem->slider.var == &v_gamma || topmenu->selecteditem->slider.var == &v_contrast)) /*no menu tint if we're trying to adjust gamma*/; else R2D_FadeScreen (); diff --git a/engine/client/menu.h b/engine/client/menu.h index d2eacabc..24a99b3b 100644 --- a/engine/client/menu.h +++ b/engine/client/menu.h @@ -111,7 +111,7 @@ void M_Menu_Mods_f (void); //used at startup if the current gamedirs look dodgy. void M_Menu_Installer (void); //given an embedded manifest, this displays an install menu for said game. mpic_t *M_CachePic (char *path); void M_Menu_Quit_f (void); -void M_Menu_Prompt (void (*callback)(void *, int), void *ctx, char *m1, char *m2, char *m3, char *optionyes, char *optionno, char *optioncancel); +void M_Menu_Prompt (void (*callback)(void *, int), void *ctx, const char *messages, char *optionyes, char *optionno, char *optioncancel); struct menu_s; @@ -198,6 +198,7 @@ typedef struct menucheck_s { typedef struct { menucommon_t common; const char *text; + qboolean rightalign; qboolean isred; } menutext_t; @@ -286,12 +287,13 @@ typedef struct menu_s { menuoption_t *options; menuoption_t *selecteditem; + menuoption_t *mouseitem; menutooltip_t *tooltip; double tooltiptime; - struct menu_s *child; - struct menu_s *parent; + struct menu_s *prev; + struct menu_s *next; int cursorpos; menuoption_t *cursoritem; @@ -371,7 +373,6 @@ menu_t *M_Options_Title(int *y, int infosize); /*Create a menu with the default menu_t *M_CreateMenu (int extrasize); menu_t *M_CreateMenuInfront (int extrasize); void M_AddMenu (menu_t *menu); -void M_AddMenuFront (menu_t *menu); void M_HideMenu (menu_t *menu); void M_RemoveMenu (menu_t *menu); void M_RemoveAllMenus (qboolean leaveprompts); diff --git a/engine/client/merged.h b/engine/client/merged.h index a4b7b8dc..ea36050e 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -488,7 +488,7 @@ typedef struct rendererinfo_s { #define BE_RenderToTextureUpdate2d rf->BE_RenderToTextureUpdate2d -#define RT_IMAGEFLAGS IF_NOMIPMAP|IF_CLAMP|IF_LINEAR|IF_RENDERTARGET +#define RT_IMAGEFLAGS (IF_NOMIPMAP|IF_CLAMP|IF_LINEAR|IF_RENDERTARGET) texid_t R2D_RT_Configure(const char *id, int width, int height, uploadfmt_t rtfmt, unsigned int imageflags); texid_t R2D_RT_GetTexture(const char *id, unsigned int *width, unsigned int *height); diff --git a/engine/client/p_script.c b/engine/client/p_script.c index 1fcb34de..9058dd2c 100644 --- a/engine/client/p_script.c +++ b/engine/client/p_script.c @@ -4503,7 +4503,7 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, return 1; // inwater check, switch only once - if (r_part_contentswitch.ival && ptype->inwater >= 0 && cl.worldmodel) + if (r_part_contentswitch.ival && ptype->inwater >= 0 && cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADED) { int cont; cont = cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, org); @@ -5937,7 +5937,7 @@ static int PScript_ParticleTrail (vec3_t startpos, vec3_t end, int type, int dlk return 1; // inwater check, switch only once - if (r_part_contentswitch.ival && ptype->inwater >= 0 && cl.worldmodel) + if (r_part_contentswitch.ival && ptype->inwater >= 0 && cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADED) { int cont; cont = cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, startpos); diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 4387aef6..8e75d752 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -759,6 +759,14 @@ static model_t *CSQC_GetModelForIndex(int index) return NULL; } +static float CSQC_PitchScaleForModelIndex(int index) +{ + model_t *mod = CSQC_GetModelForIndex(index); + if (mod && (mod->type == mod_alias || mod->type == mod_halflife)) + return r_meshpitch.value; //these are buggy. + return 1; +} + static qboolean CopyCSQCEdictToEntity(csqcedict_t *in, entity_t *out) { int ival; @@ -827,7 +835,7 @@ static qboolean CopyCSQCEdictToEntity(csqcedict_t *in, entity_t *out) else { VectorCopy(in->v->angles, out->angles); - if (model->type == mod_alias) + if (model && model->type == mod_alias) out->angles[0] *= r_meshpitch.value; AngleVectors(out->angles, out->axis[0], out->axis[1], out->axis[2]); VectorInverse(out->axis[1]); @@ -1512,7 +1520,7 @@ typedef struct // vec3_t sdir; // vec3_t tdir; } qcvertex_t; -void QCBUILTIN PF_R_AddTrisoup(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +void QCBUILTIN PF_R_AddTrisoup_Simple(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { shader_t *shader; //parm 0 unsigned int qcflags = G_INT(OFS_PARM1); @@ -2060,7 +2068,10 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_ { float fmt = G_FLOAT(OFS_PARM2); float *size = G_VECTOR(OFS_PARM3); - R2D_RT_Configure(r_refdef.rt_destcolour[i].texname, size[0], size[1], PR_TranslateTextureFormat(fmt), RT_IMAGEFLAGS); + if (fmt < 0) + R2D_RT_Configure(r_refdef.rt_destcolour[i].texname, size[0], size[1], PR_TranslateTextureFormat(-fmt), (RT_IMAGEFLAGS&~IF_LINEAR)|IF_NEAREST); + else + R2D_RT_Configure(r_refdef.rt_destcolour[i].texname, size[0], size[1], PR_TranslateTextureFormat(fmt), RT_IMAGEFLAGS); } BE_RenderToTextureUpdate2d(true); } @@ -2071,7 +2082,10 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_ { float fmt = G_FLOAT(OFS_PARM2); float *size = G_VECTOR(OFS_PARM3); - R2D_RT_Configure(r_refdef.rt_sourcecolour.texname, size[0], size[1], PR_TranslateTextureFormat(fmt), RT_IMAGEFLAGS); + if (fmt < 0) + R2D_RT_Configure(r_refdef.rt_sourcecolour.texname, size[0], size[1], PR_TranslateTextureFormat(-fmt), (RT_IMAGEFLAGS&~IF_LINEAR)|IF_NEAREST); + else + R2D_RT_Configure(r_refdef.rt_sourcecolour.texname, size[0], size[1], PR_TranslateTextureFormat(fmt), RT_IMAGEFLAGS); } BE_RenderToTextureUpdate2d(false); break; @@ -2081,7 +2095,10 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_ { float fmt = G_FLOAT(OFS_PARM2); float *size = G_VECTOR(OFS_PARM3); - R2D_RT_Configure(r_refdef.rt_depth.texname, size[0], size[1], PR_TranslateTextureFormat(fmt), RT_IMAGEFLAGS); + if (fmt < 0) + R2D_RT_Configure(r_refdef.rt_depth.texname, size[0], size[1], PR_TranslateTextureFormat(-fmt), (RT_IMAGEFLAGS&~IF_LINEAR)|IF_NEAREST); + else + R2D_RT_Configure(r_refdef.rt_depth.texname, size[0], size[1], PR_TranslateTextureFormat(fmt), RT_IMAGEFLAGS); } BE_RenderToTextureUpdate2d(false); break; @@ -2091,7 +2108,10 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_ { float fmt = G_FLOAT(OFS_PARM2); float *size = G_VECTOR(OFS_PARM3); - R2D_RT_Configure(r_refdef.rt_ripplemap.texname, size[0], size[1], PR_TranslateTextureFormat(fmt), RT_IMAGEFLAGS); + if (fmt < 0) + R2D_RT_Configure(r_refdef.rt_ripplemap.texname, size[0], size[1], PR_TranslateTextureFormat(-fmt), (RT_IMAGEFLAGS&~IF_LINEAR)|IF_NEAREST); + else + R2D_RT_Configure(r_refdef.rt_ripplemap.texname, size[0], size[1], PR_TranslateTextureFormat(fmt), RT_IMAGEFLAGS); } BE_RenderToTextureUpdate2d(false); break; @@ -4987,6 +5007,14 @@ static void QCBUILTIN PF_DeltaListen(pubprogfuncs_t *prinst, struct globalvars_s } } +static void AngleVectorsIndex (const vec3_t angles, int modelindex, vec3_t forward, vec3_t right, vec3_t up) +{ + vec3_t fixedangles; + fixedangles[0] = angles[0] * CSQC_PitchScaleForModelIndex(modelindex); + fixedangles[1] = angles[1]; + fixedangles[2] = angles[2]; + AngleVectors(fixedangles, forward, right, up); +} static void QCBUILTIN PF_getentity(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int entnum = G_FLOAT(OFS_PARM0); @@ -5000,6 +5028,136 @@ static void QCBUILTIN PF_getentity(pubprogfuncs_t *prinst, struct globalvars_s * return; } + if (entnum > 0 && entnum <= cl.allocated_client_slots && cl.lerpplayers[entnum-1].sequence == cl.lerpentssequence) + { + player_state_t *ps = &cl.inframes[cl.validsequence&UPDATE_MASK].playerstate[entnum-1]; + le = &cl.lerpplayers[entnum-1]; + + switch(fldnum) + { + case GE_ACTIVE: + G_FLOAT(OFS_RETURN) = 1; + break; + + case GE_ORIGIN: + /*lerped position*/ + VectorCopy(le->origin, G_VECTOR(OFS_RETURN)); + break; + case GE_SCALE: + G_FLOAT(OFS_RETURN) = ps->scale / 16.0f; + break; + case GE_ALPHA: + G_FLOAT(OFS_RETURN) = ps->alpha / 255.0f; + break; + case GE_COLORMOD: + G_FLOAT(OFS_RETURN+0) = ps->colourmod[0] / 8.0f; + G_FLOAT(OFS_RETURN+1) = ps->colourmod[1] / 8.0f; + G_FLOAT(OFS_RETURN+2) = ps->colourmod[2] / 8.0f; + break; + case GE_SKIN: + G_FLOAT(OFS_RETURN) = ps->skinnum; + break; + case GE_MINS: + VectorCopy(ps->szmins, G_VECTOR(OFS_RETURN)); + break; + case GE_MAXS: + VectorCopy(ps->szmaxs, G_VECTOR(OFS_RETURN)); + break; + + case GE_ABSMIN: + VectorAdd(ps->szmins, le->origin, G_VECTOR(OFS_RETURN)); + break; + case GE_ABSMAX: + VectorAdd(ps->szmaxs, le->origin, G_VECTOR(OFS_RETURN)); + break; + case GE_ORIGINANDVECTORS: + VectorCopy(le->origin, G_VECTOR(OFS_RETURN)); + AngleVectorsIndex(le->angles, ps->modelindex, csqcg.forward, csqcg.right, csqcg.up); + break; + case GE_FORWARD: + AngleVectorsIndex(le->angles, ps->modelindex, G_VECTOR(OFS_RETURN), NULL, NULL); + break; + case GE_RIGHT: + AngleVectorsIndex(le->angles, ps->modelindex, NULL, G_VECTOR(OFS_RETURN), NULL); + break; + case GE_UP: + AngleVectorsIndex(le->angles, ps->modelindex, NULL, NULL, G_VECTOR(OFS_RETURN)); + break; + case GE_PANTSCOLOR: + G_FLOAT(OFS_RETURN) = cl.players[entnum-1].tbottomcolor; + break; + case GE_SHIRTCOLOR: + G_FLOAT(OFS_RETURN) = cl.players[entnum-1].ttopcolor; + break; + case GE_LIGHT: + G_FLOAT(OFS_RETURN) = 0; + break; + + case GE_MODELINDEX: + G_FLOAT(OFS_RETURN) = ps->modelindex; + break; + case GE_MODELINDEX2: + G_FLOAT(OFS_RETURN) = ps->command.impulse; //evil hack + break; + case GE_EFFECTS: + G_FLOAT(OFS_RETURN) = ps->effects; + break; + case GE_FRAME: + G_FLOAT(OFS_RETURN) = ps->frame; + break; + case GE_ANGLES: + VectorCopy(le->angles, G_VECTOR(OFS_RETURN)); + break; + case GE_FATNESS: + G_FLOAT(OFS_RETURN) = ps->fatness; + break; + case GE_DRAWFLAGS: + G_FLOAT(OFS_RETURN) = SCALE_ORIGIN_ORIGIN; + break; + case GE_ABSLIGHT: + G_FLOAT(OFS_RETURN) = 0; + break; + case GE_GLOWMOD: + VectorSet(G_VECTOR(OFS_RETURN), 1, 1, 1); + break; + case GE_GLOWSIZE: + G_FLOAT(OFS_RETURN) = 0; + break; + case GE_GLOWCOLOUR: + G_FLOAT(OFS_RETURN) = 0; + break; + case GE_RTSTYLE: + G_FLOAT(OFS_RETURN) = 0; + break; + case GE_RTPFLAGS: + G_FLOAT(OFS_RETURN) = 0; + break; + case GE_RTCOLOUR: + VectorSet(G_VECTOR(OFS_RETURN), 1, 1, 1); + break; + case GE_RTRADIUS: + G_FLOAT(OFS_RETURN) = 0; + break; + case GE_TAGENTITY: + G_FLOAT(OFS_RETURN) = 0; + break; + case GE_TAGINDEX: + G_FLOAT(OFS_RETURN) = 0; + break; + case GE_GRAVITYDIR: + VectorCopy(ps->gravitydir, G_VECTOR(OFS_RETURN)); + break; + case GE_TRAILEFFECTNUM: + G_FLOAT(OFS_RETURN) = 0; + break; + + default: + VectorCopy(vec3_origin, G_VECTOR(OFS_RETURN)); + break; + } + return; + } + if (entnum >= cl.maxlerpents || !cl.lerpentssequence || cl.lerpents[entnum].sequence != cl.lerpentssequence) { if (fldnum != GE_ACTIVE) @@ -5061,16 +5219,16 @@ static void QCBUILTIN PF_getentity(pubprogfuncs_t *prinst, struct globalvars_s * break; case GE_ORIGINANDVECTORS: VectorCopy(le->origin, G_VECTOR(OFS_RETURN)); - AngleVectors(le->angles, csqcg.forward, csqcg.right, csqcg.up); + AngleVectorsIndex(le->angles, es->modelindex, csqcg.forward, csqcg.right, csqcg.up); break; case GE_FORWARD: - AngleVectors(le->angles, G_VECTOR(OFS_RETURN), NULL, NULL); + AngleVectorsIndex(le->angles, es->modelindex, G_VECTOR(OFS_RETURN), NULL, NULL); break; case GE_RIGHT: - AngleVectors(le->angles, NULL, G_VECTOR(OFS_RETURN), NULL); + AngleVectorsIndex(le->angles, es->modelindex, NULL, G_VECTOR(OFS_RETURN), NULL); break; case GE_UP: - AngleVectors(le->angles, NULL, NULL, G_VECTOR(OFS_RETURN)); + AngleVectorsIndex(le->angles, es->modelindex, NULL, NULL, G_VECTOR(OFS_RETURN)); break; case GE_PANTSCOLOR: if (es->colormap <= cl.allocated_client_slots && !(es->dpflags & RENDER_COLORMAPPED)) @@ -5829,7 +5987,7 @@ static struct { {"R_BeginPolygon", PF_R_PolygonBegin, 306}, // #306 void(string texturename) R_BeginPolygon (EXT_CSQC_???) {"R_PolygonVertex", PF_R_PolygonVertex, 307}, // #307 void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex (EXT_CSQC_???) {"R_EndPolygon", PF_R_PolygonEnd, 308}, // #308 void() R_EndPolygon (EXT_CSQC_???) - {"addtrisoup_1", PF_R_AddTrisoup, 0}, + {"addtrisoup_simple", PF_R_AddTrisoup_Simple, 0}, {"getproperty", PF_R_GetViewFlag, 309}, // #309 vector/float(float property) getproperty (EXT_CSQC_1) diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 68de3ad9..9e1bb60b 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -693,8 +693,6 @@ void QCBUILTIN PF_CL_uploadimage (pubprogfuncs_t *prinst, struct globalvars_s *p G_INT(OFS_RETURN) = 0; //assume the worst -#define RT_IMAGEFLAGS IF_NOMIPMAP|IF_CLAMP|IF_LINEAR|IF_RENDERTARGET - if (width < 0 || height < 0 || width > 16384 || height > 16384) { //this is actually kinda likely when everyone assumes everything is a float. PR_BIError(prinst, "PF_CL_uploadimage: dimensions are out of range\n"); @@ -892,7 +890,11 @@ void QCBUILTIN PF_CL_drawline (pubprogfuncs_t *prinst, struct globalvars_s *pr_g "}\n" "}\n"); - BE_DrawMesh_Single(shader_draw_line, &mesh, NULL, flags|BEF_LINES); + R2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha); + R2D_Line(point1[0], point1[1], point2[0], point2[1], shader_draw_line); + R2D_ImageColours(1,1,1,1); + +// BE_DrawMesh_Single(shader_draw_line, &mesh, NULL, flags|BEF_LINES); } //vector drawgetimagesize(string pic) = #460; diff --git a/engine/client/r_2d.c b/engine/client/r_2d.c index b2d4bc2a..57c5765c 100644 --- a/engine/client/r_2d.c +++ b/engine/client/r_2d.c @@ -649,17 +649,28 @@ void R2D_Image2dQuad(vec2_t points[], vec2_t texcoords[], mpic_t *pic) return; } - if (R2D_Flush) - R2D_Flush(); + if (draw_active_shader != pic || draw_active_flags != r2d_be_flags || draw_mesh.numvertexes+4 > DRAW_QUADS) + { + if (R2D_Flush) + R2D_Flush(); + + draw_active_shader = pic; + draw_active_flags = r2d_be_flags; + R2D_Flush = R2D_ImageFlush; + + draw_mesh.numindexes = 0; + draw_mesh.numvertexes = 0; + } for (i = 0; i < 4; i++) { - Vector2Copy(points[i], draw_mesh_xyz[i]); - Vector2Copy(texcoords[i], draw_mesh_st[i]); - Vector4Copy(draw_active_colour, draw_mesh_colors[i]); + Vector2Copy(points[i], draw_mesh_xyz[draw_mesh.numvertexes+i]); + Vector2Copy(texcoords[i], draw_mesh_st[draw_mesh.numvertexes+i]); + Vector4Copy(draw_active_colour, draw_mesh_colors[draw_mesh.numvertexes+i]); } - BE_DrawMesh_Single(pic, &draw_mesh, NULL, r2d_be_flags); + draw_mesh.numvertexes += 4; + draw_mesh.numindexes += 6; } /*draws a block of the current colour on the screen*/ @@ -702,13 +713,18 @@ void R2D_FillBlock(float x, float y, float w, float h) void R2D_Line(float x1, float y1, float x2, float y2, shader_t *shader) { if (R2D_Flush) + { R2D_Flush(); + R2D_Flush = NULL; + } VectorSet(draw_mesh_xyz[0], x1, y1, 0); Vector2Set(draw_mesh_st[0], 0, 0); + Vector4Copy(draw_active_colour, draw_mesh_colors[0]); VectorSet(draw_mesh_xyz[1], x2, y2, 0); Vector2Set(draw_mesh_st[1], 1, 0); + Vector4Copy(draw_active_colour, draw_mesh_colors[1]); if (!shader) { diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index dc6b8c62..16207f88 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -3316,6 +3316,7 @@ void Surf_LightmapMode(void) lightmap_bgra = true; break; #ifdef D3DQUAKE + case QR_DIRECT3D8: case QR_DIRECT3D9: case QR_DIRECT3D11: /*always bgra, hope your card supports it*/ @@ -3797,7 +3798,7 @@ TRACE(("dbg: Surf_NewMap: tp\n")); void Surf_PreNewMap(void) { - r_loadbumpmapping = r_deluxemapping || r_glsl_offsetmapping.ival; + r_loadbumpmapping = r_deluxmapping || r_glsl_offsetmapping.ival; #ifdef RTLIGHTS r_loadbumpmapping |= r_shadow_realtime_world.ival || r_shadow_realtime_dlight.ival; #endif diff --git a/engine/client/render.h b/engine/client/render.h index 7aa57267..45a73351 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -43,7 +43,7 @@ static const texid_t r_nulltex = NULL; //desktop-gl will generally cope with ints, but expect a performance hit from that with old gpus (so we don't bother) //vulkan+dx10 can cope with ints, but might be 24bit //either way, all renderers in the same build need to use the same thing. -#if (defined(GLQUAKE) && !defined(NOLEGACY)) || defined(MINIMAL) || defined(D3D9QUAKE) || defined(ANDROID) +#if (defined(GLQUAKE) && !defined(NOLEGACY)) || defined(MINIMAL) || defined(D3D8QUAKE) || defined(D3D9QUAKE) || defined(ANDROID) #define sizeof_index_t 2 #endif #if sizeof_index_t == 2 @@ -438,7 +438,14 @@ void Image_Shutdown(void); image_t *Image_LoadTexture (const char *identifier, int width, int height, uploadfmt_t fmt, void *data, unsigned int flags); +#ifdef D3D8QUAKE +void D3D8_Set2D (void); +void D3D8_UpdateFiltering (image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float anis); +qboolean D3D8_LoadTextureMips (texid_t tex, struct pendingtextureinfo *mips); +void D3D8_DestroyTexture (texid_t tex); +#endif #ifdef D3D9QUAKE +void D3D9_Set2D (void); void D3D9_UpdateFiltering (image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float anis); qboolean D3D9_LoadTextureMips (texid_t tex, struct pendingtextureinfo *mips); void D3D9_DestroyTexture (texid_t tex); @@ -597,8 +604,8 @@ extern cvar_t r_telestyle; extern cvar_t r_dynamic; extern cvar_t r_novis; extern cvar_t r_netgraph; -extern cvar_t r_deluxemapping_cvar; -extern qboolean r_deluxemapping; +extern cvar_t r_deluxmapping_cvar; +extern qboolean r_deluxmapping; extern cvar_t r_softwarebanding_cvar; extern qboolean r_softwarebanding; extern cvar_t r_lightprepass_cvar; diff --git a/engine/client/renderer.c b/engine/client/renderer.c index abdf1fb5..1bf5b9ee 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -201,7 +201,7 @@ cvar_t scr_allowsnap = CVARF ("scr_allowsnap", "1", CVAR_NOTFROMSERVER); cvar_t scr_centersbar = CVAR ("scr_centersbar", "2"); cvar_t scr_centertime = CVAR ("scr_centertime", "2"); -cvar_t scr_logcenterprint = CVARD ("con_logcenterprint", "1", ""); +cvar_t scr_logcenterprint = CVARD ("con_logcenterprint", "1", "Specifies whether to print centerprints on the console.\n0: never\n1: single-player or coop only.\n2: always.\n"); cvar_t scr_chatmodecvar = CVAR ("scr_chatmode", "0"); cvar_t scr_conalpha = CVARC ("scr_conalpha", "0.7", Cvar_Limiter_ZeroToOne_Callback); @@ -314,9 +314,9 @@ cvar_t gl_ati_truform_type = CVAR ("gl_ati_truform_type", "1"); cvar_t r_tessellation_level = CVAR ("r_tessellation_level", "5"); cvar_t gl_blend2d = CVAR ("gl_blend2d", "1"); cvar_t gl_blendsprites = CVARD ("gl_blendsprites", "0", "Blend sprites instead of alpha testing them"); -cvar_t r_deluxemapping_cvar = CVARAFD ("r_deluxemapping", "0", "r_glsl_deluxemapping", - CVAR_ARCHIVE, "Enables bumpmapping based upon precomputed light directions"); -qboolean r_deluxemapping; +cvar_t r_deluxmapping_cvar = CVARAFD ("r_deluxmapping", "0", "r_deluxemapping", //fixme: rename to r_glsl_deluxmapping once configs catch up + CVAR_ARCHIVE, "Enables bumpmapping based upon precomputed light directions.\n0=off\n1=use if available\n2=auto-generate (if possible)"); +qboolean r_deluxmapping; cvar_t r_shaderblobs = CVARD ("r_shaderblobs", "0", "If enabled, can massively accelerate vid restarts / loading (especially with the d3d renderer). Can cause issues when upgrading engine versions, so this is disabled by default."); cvar_t gl_compress = CVARFD ("gl_compress", "0", CVAR_ARCHIVE, "Enable automatic texture compression even for textures which are not pre-compressed."); cvar_t gl_conback = CVARFDC ("gl_conback", "", @@ -476,7 +476,7 @@ void GLRenderer_Init(void) Cvar_Register (&gl_smoothcrosshair, GRAPHICALNICETIES); - Cvar_Register (&r_deluxemapping_cvar, GRAPHICALNICETIES); + Cvar_Register (&r_deluxmapping_cvar, GRAPHICALNICETIES); #ifdef R_XFLIP Cvar_Register (&r_xflip, GLRENDEREROPTIONS); @@ -1051,8 +1051,13 @@ extern rendererinfo_t rpirendererinfo; rendererinfo_t waylandrendererinfo; rendererinfo_t fbdevrendererinfo; #endif -#ifdef D3DQUAKE +#ifdef D3D8QUAKE +extern rendererinfo_t d3d8rendererinfo; +#endif +#ifdef D3D9QUAKE extern rendererinfo_t d3d9rendererinfo; +#endif +#ifdef D3D11QUAKE extern rendererinfo_t d3d11rendererinfo; #endif #ifdef SWQUAKE @@ -1091,6 +1096,9 @@ rendererinfo_t *rendererinfo[] = #ifdef VKQUAKE &vkrendererinfo, #endif +#ifdef D3D8QUAKE + &d3d8rendererinfo, +#endif #ifndef NPQTV &dedicatedrendererinfo, #endif @@ -1347,7 +1355,7 @@ TRACE(("dbg: R_ApplyRenderer: Palette loaded\n")); TRACE(("dbg: R_ApplyRenderer: vid applied\n")); r_softwarebanding = false; - r_deluxemapping = false; + r_deluxmapping = false; r_lightprepass = false; W_LoadWadFile("gfx.wad"); @@ -1950,6 +1958,7 @@ mspriteframe_t *R_GetSpriteFrame (entity_t *currententity) else if (psprite->frames[frame].type == SPR_ANGLED) { pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; +// pspriteframe = pspritegroup->frames[(int)((r_refdef.viewangles[1]-currententity->angles[1])/360*pspritegroup->numframes + 0.5-4)%pspritegroup->numframes]; pspriteframe = pspritegroup->frames[(int)((r_refdef.viewangles[1]-currententity->angles[1])/360*8 + 0.5-4)&7]; } else diff --git a/engine/client/sys_win.c b/engine/client/sys_win.c index e8fd6031..81564027 100644 --- a/engine/client/sys_win.c +++ b/engine/client/sys_win.c @@ -1038,6 +1038,8 @@ qboolean Sys_remove (char *path) err = GetLastError(); if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) return true; //succeed when the file already didn't exist + if (err == ERROR_ACCESS_DENIED) + return false; //windows is shite. this may simply include that its open in another process that didn't include the SHARE_DELETE permission. return false; //other errors? panic } else @@ -3657,6 +3659,14 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin memset(&parms, 0, sizeof(parms)); + /*work around potentially serious windows flaw loading dlls from the current directory, by loading a dll...*/ + { + BOOL (WINAPI *pSetDllDirectoryW)(LPCWSTR lpPathName); + dllfunction_t ffsfuncs[] = {{(void*)&pSetDllDirectoryW, "SetDllDirectoryW"}, {NULL,NULL}}; + if (Sys_LoadLibrary("kernel32.dll", ffsfuncs)) + pSetDllDirectoryW(L""); //disables it (null for 'use working directory') + } + Win7_Init(); #ifdef _MSC_VER @@ -3850,6 +3860,8 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin old = FindWindowW(L"FTED3D11QUAKE", NULL); if (!old) old = FindWindowW(L"FTED3D9QUAKE", NULL); + if (!old) + old = FindWindowW(L"FTED3D8QUAKE", NULL); if (old) { COPYDATASTRUCT cds; diff --git a/engine/client/textedit.c b/engine/client/textedit.c index 94704b79..36a04d83 100644 --- a/engine/client/textedit.c +++ b/engine/client/textedit.c @@ -737,7 +737,7 @@ qboolean Con_Editor_Close(console_t *con, qboolean force) { if (!strncmp(con->title, "MODIFIED: ", 10)) { - M_Menu_Prompt(Con_Editor_CloseCallback, con, "Save changes?", con->name, "", "Yes", "No", "Cancel"); + M_Menu_Prompt(Con_Editor_CloseCallback, con, va("Save changes?\n%s\n", con->name), "Yes", "No", "Cancel"); return false; } } diff --git a/engine/client/vid.h b/engine/client/vid.h index 36bc40d7..cc03f969 100644 --- a/engine/client/vid.h +++ b/engine/client/vid.h @@ -35,7 +35,8 @@ typedef enum QR_SOFTWARE, //not worth using QR_VULKAN, // QR_DIRECT3D12, //no implementation - QR_METAL //no implementation + QR_METAL, //no implementation + QR_DIRECT3D8 // } r_qrenderer_t; typedef struct { diff --git a/engine/client/view.c b/engine/client/view.c index bec0e49e..3c43864a 100644 --- a/engine/client/view.c +++ b/engine/client/view.c @@ -76,8 +76,8 @@ cvar_t crosshair = CVARF("crosshair", "1", CVAR_ARCHIVE); cvar_t crosshaircolor = CVARF("crosshaircolor", "255 255 255", CVAR_ARCHIVE); cvar_t crosshairsize = CVARF("crosshairsize", "8", CVAR_ARCHIVE); -cvar_t cl_crossx = CVARF("cl_crossx", "0", CVAR_ARCHIVE); -cvar_t cl_crossy = CVARF("cl_crossy", "0", CVAR_ARCHIVE); +cvar_t cl_crossx = CVARF("cl_crossx", "0", CVAR_ARCHIVE); +cvar_t cl_crossy = CVARF("cl_crossy", "0", CVAR_ARCHIVE); cvar_t crosshaircorrect = CVARF("crosshaircorrect", "0", CVAR_SEMICHEAT); cvar_t crosshairimage = CVAR("crosshairimage", ""); cvar_t crosshairalpha = CVAR("crosshairalpha", "1"); diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index e905ce29..8ec5b015 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -347,7 +347,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #if defined(SWQUAKE) && !defined(_DEBUG) #undef SWQUAKE #endif -#if (defined(D3D9QUAKE) || defined(D3D11QUAKE)) && !defined(D3DQUAKE) +#if (defined(D3D8QUAKE) || defined(D3D9QUAKE) || defined(D3D11QUAKE)) && !defined(D3DQUAKE) #define D3DQUAKE #endif @@ -463,7 +463,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define SQL #endif -#if (defined(AVAIL_GZDEC) && !defined(ZLIB)) || !defined(NPFTE) +#if defined(AVAIL_GZDEC) && (!defined(AVAIL_ZLIB) || defined(NPFTE)) //gzip needs zlib to work (pk3s can still contain non-compressed files) #undef AVAIL_GZDEC #endif diff --git a/engine/common/common.c b/engine/common/common.c index 0185d655..9c6dd188 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -115,7 +115,7 @@ cvar_t com_highlightcolor = CVARD("com_highlightcolor", STRINGIFY(COLOR_RED), "A 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_ARCHIVE, "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. +cvar_t pm_autoupdate = CVARFD("pm_autoupdate", "1", CVAR_NOTFROMSERVER|CVAR_NOSAVE|CVAR_NOSET, "Controls autoupdates, can only be changed via the downloads menu.\n0: 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 @@ -308,11 +308,11 @@ int Q_strlen (char *str) char *Q_strrchr(char *s, char c) { - int len = Q_strlen(s); - s += len; - while (len--) - if (*--s == c) return s; - return 0; + int len = Q_strlen(s); + s += len; + while (len--) + if (*--s == c) return s; + return 0; } void Q_strcat (char *dest, char *src) @@ -355,6 +355,7 @@ int Q_strncmp (char *s1, char *s2, int count) #endif +//case comparisons are specific to ascii only, so this should be 'safe' for utf-8 strings too. int Q_strncasecmp (const char *s1, const char *s2, int n) { int c1, c2; @@ -2933,32 +2934,33 @@ static char *bidi_chartype; static unsigned int bidi_charcount; -//semi-colon delimited tokens -char *COM_ParseStringSetSep (const char *data, char sep) +//semi-colon delimited tokens, without whitespace awareness +char *COM_ParseStringSetSep (const char *data, char sep, char *out, size_t outsize) { int c; size_t len; - COM_AssertMainThread("COM_ParseStringSetSep"); + if (out == com_token) + COM_AssertMainThread("COM_ParseStringSetSep"); len = 0; - com_token[0] = 0; + out[0] = 0; if (data) for (;*data;) { - if (len >= sizeof(com_token)-1) + if (len >= outsize-1) { - com_token[len] = 0; + out[len] = 0; return (char*)data; } c = *data++; if (c == ';') break; - com_token[len++] = c; + out[len++] = c; } - com_token[len] = 0; + out[len] = 0; return (char*)data; } void COM_BiDi_Shutdown(void) @@ -3004,12 +3006,12 @@ static void COM_BiDi_Setup(void) if (end) *end++ = 0; - tok = COM_ParseStringSetSep(line,';'); //number + tok = COM_ParseStringSetSep(line,';', com_token, sizeof(com_token)); //number c = strtoul(com_token, NULL, 16); - tok = COM_ParseStringSetSep(tok,';'); //name - tok = COM_ParseStringSetSep(tok,';'); //class? - tok = COM_ParseStringSetSep(tok,';'); //? - tok = COM_ParseStringSetSep(tok,';'); //bidi + tok = COM_ParseStringSetSep(tok,';', com_token, sizeof(com_token)); //name + tok = COM_ParseStringSetSep(tok,';', com_token, sizeof(com_token)); //class? + tok = COM_ParseStringSetSep(tok,';', com_token, sizeof(com_token)); //? + tok = COM_ParseStringSetSep(tok,';', com_token, sizeof(com_token)); //bidi if (c < bidi_charcount) { if (!Q_strcasecmp(com_token, "R") || !Q_strcasecmp(com_token, "AL")) @@ -5825,7 +5827,7 @@ char *Info_ValueForKey (const char *s, const char *key) } } -char *Info_KeyForNumber (char *s, int num) +char *Info_KeyForNumber (const char *s, int num) { static char pkey[1024]; char *o; @@ -6111,50 +6113,49 @@ void Info_SetValueForKey (char *s, const char *key, const char *value, int maxsi Info_SetValueForStarKey (s, key, value, maxsize); } -void Info_Print (char *s, char *lineprefix) +void Info_Enumerate (const char *s, void *ctx, void(*cb)(void *ctx, const char *key, const char *value)) { char key[1024]; char value[1024]; char *o; - int l; if (*s == '\\') s++; while (*s) { o = key; - while (*s && *s != '\\') + while (*s && *s != '\\' && o < key+countof(key)-1) *o++ = *s++; + *o = 0; - l = o - key; - if (l < 20) - { - memset (o, ' ', 20-l); - key[20] = 0; - } - else - *o = 0; - Con_Printf ("%s%s", lineprefix, key); - - if (!*s) + if (!*s++) { //should never happen. - Con_Printf ("\n"); + cb(ctx, key, ""); return; } o = value; - s++; - while (*s && *s != '\\') + while (*s && *s != '\\' && o < value+countof(value)-1) *o++ = *s++; *o = 0; if (*s) s++; - Con_Printf ("%s\n", value); + cb(ctx, key, value); } } +static void Info_PrintCB (void *ctx, const char *key, const char *value) +{ + char *lineprefix = ctx; + Con_Printf ("%s%-20s%s\n", lineprefix, key, value); +} +void Info_Print (const char *s, const char *lineprefix) +{ + Info_Enumerate(s, (void*)lineprefix, Info_PrintCB); +} + void Info_WriteToFile(vfsfile_t *f, char *info, char *commandname, int cvarflags) { const char *quotedvalue; diff --git a/engine/common/common.h b/engine/common/common.h index a56ae062..1bfa376f 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -335,7 +335,6 @@ void QDECL Q_strncpyz(char*d, const char*s, int n); #define strncpy Q_strncpy #endif*/ - /*replacement functions which do not care for locale in text formatting ('C' locale), or are non-standard*/ char *Q_strcasestr(const char *haystack, const char *needle); int Q_strncasecmp (const char *s1, const char *s2, int n); @@ -359,7 +358,8 @@ extern qboolean com_eof; #define COM_Parse(d) COM_ParseOut(d,com_token, sizeof(com_token)) #define COM_ParseOut(d,o,l) COM_ParseType(d,o,l,NULL) char *COM_ParseType (const char *data, char *out, int outlen, com_tokentype_t *toktype); -char *COM_ParseStringSet (const char *data, char *out, size_t outlen); +char *COM_ParseStringSet (const char *data, char *out, size_t outlen); //whitespace or semi-colon separators +char *COM_ParseStringSetSep (const char *data, char sep, char *out, size_t outsize); //single-char-separator, no whitespace char *COM_ParseCString (const char *data, char *out, size_t maxoutlen, size_t *writtenlen); char *COM_StringParse (const char *data, char *token, unsigned int tokenlen, qboolean expandmacros, qboolean qctokenize); char *COM_ParseToken (const char *data, const char *punctuation); @@ -688,12 +688,13 @@ unsigned int COM_RemapMapChecksum(unsigned int checksum); #define MAX_INFO_KEY 256 char *Info_ValueForKey (const char *s, const char *key); void Info_RemoveKey (char *s, const char *key); -char *Info_KeyForNumber (char *s, int num); +char *Info_KeyForNumber (const char *s, int num); void Info_RemovePrefixedKeys (char *start, char prefix); void Info_RemoveNonStarKeys (char *start); void Info_SetValueForKey (char *s, const char *key, const char *value, int maxsize); void Info_SetValueForStarKey (char *s, const char *key, const char *value, int maxsize); -void Info_Print (char *s, char *lineprefix); +void Info_Print (const char *s, const char *lineprefix); +void Info_Enumerate (const char *s, void *ctx, void(*cb)(void *ctx, const char *key, const char *value)); void Info_WriteToFile(vfsfile_t *f, char *info, char *commandname, int cvarflags); void Com_BlocksChecksum (int blocks, void **buffer, int *len, unsigned char *outbuf); diff --git a/engine/common/console.h b/engine/common/console.h index b166dc69..9ac7a01a 100644 --- a/engine/common/console.h +++ b/engine/common/console.h @@ -196,7 +196,7 @@ extern console_t *con_chat; //shared between console and keys. //really the console input should be in console.c instead of keys.c I suppose. #define MAXCMDLINE 8192 -#define CON_EDIT_LINES_MASK ((1<<6)-1) +#define CON_EDIT_LINES_MASK ((1<<8)-1) extern unsigned char *key_lines[CON_EDIT_LINES_MASK+1]; extern int edit_line; extern int key_linepos; diff --git a/engine/common/fs.c b/engine/common/fs.c index 8832b2cd..c225b962 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -1878,9 +1878,9 @@ vfsfile_t *FS_OpenVFS(const char *filename, const char *mode, enum fs_relative r COM_CreatePath(fullname); vfs = VFSOS_Open(fullname, mode); } - if (vfs && !strcmp(mode, "wb")) - - return vfs; + if (vfs && (*mode == 'w' || *mode == 'a')) + return vfs; + //fall through case FS_PUBGAMEONLY: if (!FS_NativePath(filename, relativeto, fullname, sizeof(fullname))) return NULL; @@ -2912,7 +2912,7 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths) if (*dir == '.' || !strcmp(dir, ".") || strstr(dir, "..") || strstr(dir, "/") || strstr(dir, "\\") || strstr(dir, ":") || strstr(dir, "\"") ) { - Con_Printf ("Gamedir should be a single filename, not a path\n"); + Con_Printf ("Gamedir should be a single filename, not \"%s\"\n", dir); return; } @@ -3105,6 +3105,24 @@ const gamemode_info_t gamemode_info[] = { {NULL} }; +void QDECL Q_strnlowercatz(char *d, const char *s, int n) +{ + int c = strlen(d); + d += c; + n -= c; + n -= 1; //for the null + while (*s && n-- > 0) + { + if (*s >= 'A' && *s <= 'Z') + *d = (*s-'A') + 'a'; + else + *d = *s; + d++; + s++; + } + *d = 0; +} + qboolean FS_GenCachedPakName(const char *pname, const char *crc, char *local, int llen) { const char *fn; @@ -3117,7 +3135,8 @@ qboolean FS_GenCachedPakName(const char *pname, const char *crc, char *local, in if (!strncmp(pname, "downloads/", 10)) { - Q_snprintfz(local, llen, "%s", pname); + *local = 0; + Q_strnlowercatz(local, pname, llen); return true; } @@ -3137,7 +3156,7 @@ qboolean FS_GenCachedPakName(const char *pname, const char *crc, char *local, in } Q_strncpyz(local, pname, min((fn - pname) + 1, llen)); Q_strncatz(local, "dlcache/", llen); - Q_strncatz(local, fn, llen); + Q_strnlowercatz(local, fn, llen); if (crc && *crc) { Q_strncatz(local, ".", llen); @@ -3198,6 +3217,7 @@ qboolean FS_LoadPackageFromFile(vfsfile_t *vfs, char *pname, char *localname, in //'small' wrapper to open foo.zip/bar to read files within zips that are not part of the gamedir. //name needs to be null terminated. recursive. pass null for search. +//name is restored to its original state after the call, only technically not const vfsfile_t *CL_OpenFileInPackage(searchpathfuncs_t *search, char *name) { int found; @@ -3208,6 +3228,9 @@ vfsfile_t *CL_OpenFileInPackage(searchpathfuncs_t *search, char *name) char *end; int i; + //keep chopping off the last part of the filename until we get an actual package + //once we do, recurse into that package + end = name + strlen(name); while (end > name) @@ -3412,7 +3435,13 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags) if (!*dir || *dir == '.' || !strcmp(dir, ".") || strstr(dir, "..") || strstr(dir, "/") || strstr(dir, "\\") || strstr(dir, ":") ) { - Con_Printf ("Gamedir should be a single filename, not a path\n"); + Con_Printf ("Gamedir should be a single filename, not \"%s\"\n", dir); + continue; + } + + if (!Q_strncasecmp(dir, "downloads", 9)) + { + Con_Printf ("Gamedir should not be \"%s\"\n", dir); continue; } @@ -4296,8 +4325,6 @@ qboolean FS_DownloadingPackage(void) return !fs_manifest || !!curpackagedownload; } //vfsfile_t *FS_DecompressXZip(vfsfile_t *infile, vfsfile_t *outfile); -vfsfile_t *FS_XZ_DecompressWriteFilter(vfsfile_t *infile); -vfsfile_t *FS_GZ_DecompressWriteFilter(vfsfile_t *outfile, qboolean autoclosefile); static void FS_ExtractPackage(searchpathfuncs_t *archive, flocation_t *loc, const char *origname, const char *finalname) { vfsfile_t *in = archive->OpenVFS(archive, loc, "rb"); @@ -4681,7 +4708,7 @@ static qboolean FS_BeginPackageDownload(struct manpack_s *pack, char *baseurl, q break; case X_GZ: #ifdef AVAIL_GZDEC - tmpf = FS_GZ_DecompressWriteFilter(tmpf, true); + tmpf = FS_GZ_WriteFilter(tmpf, true, false); #else VFS_CLOSE(tmpf); tmpf = NULL; diff --git a/engine/common/fs.h b/engine/common/fs.h index 24a1590f..e5bece0f 100644 --- a/engine/common/fs.h +++ b/engine/common/fs.h @@ -68,6 +68,7 @@ void FS_UnRegisterFileSystemModule(void *module); void FS_AddHashedPackage(searchpath_t **oldpaths, const char *parent_pure, const char *parent_logical, searchpath_t *search, unsigned int loadstuff, const char *pakpath, const char *qhash, const char *pakprefix); 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_EnumeratePlugins(void (*callback)(const char *name)); int PM_IsApplying(qboolean listsonly); void PM_ManifestPackage(const char *name, int security); qboolean PM_FindUpdatedEngine(char *syspath, size_t syspathsize); //names the engine we should be running @@ -84,3 +85,6 @@ int FS_EnumerateKnownGames(qboolean (*callback)(void *usr, ftemanifest_t *man), #define SPF_WRITABLE 64 //safe to write here. lots of weird rules etc. #define SPF_BASEPATH 128 //part of the basegames, and not the mod gamedir(s). qboolean FS_LoadPackageFromFile(vfsfile_t *vfs, char *pname, char *localname, int *crc, unsigned int flags); + +vfsfile_t *FS_XZ_DecompressWriteFilter(vfsfile_t *infile); +vfsfile_t *FS_GZ_WriteFilter(vfsfile_t *outfile, qboolean autoclosefile, qboolean compress); diff --git a/engine/common/fs_win32.c b/engine/common/fs_win32.c index b1b2eebf..fe6fe418 100644 --- a/engine/common/fs_win32.c +++ b/engine/common/fs_win32.c @@ -333,11 +333,11 @@ static vfsfile_t *QDECL VFSW32_OpenInternal(vfsw32path_t *handle, const char *qu { //FILE_SHARE_DELETE is not supported in 9x, sorry. if ((write && read) || append) - h = CreateFileA(osname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + h = CreateFileA(osname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); else if (write) - h = CreateFileA(osname, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + h = CreateFileA(osname, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); else if (read) - h = CreateFileA(osname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + h = CreateFileA(osname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); else h = INVALID_HANDLE_VALUE; } @@ -627,9 +627,9 @@ searchpathfuncs_t *QDECL VFSW32_OpenPath(vfsfile_t *mustbenull, const char *desc wchar_t wide[MAX_OSPATH]; memcpy(np->rootpath, desc, dlen+1); if (!WinNT) - np->changenotification = FindFirstChangeNotificationA(np->rootpath, true, FILE_NOTIFY_CHANGE_FILE_NAME); + np->changenotification = FindFirstChangeNotificationA(np->rootpath, true, FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_CREATION); else - np->changenotification = FindFirstChangeNotificationW(widen(wide, sizeof(wide), np->rootpath), true, FILE_NOTIFY_CHANGE_FILE_NAME); + np->changenotification = FindFirstChangeNotificationW(widen(wide, sizeof(wide), np->rootpath), true, FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_CREATION); } np->pub.fsver = FSVER; diff --git a/engine/common/fs_zip.c b/engine/common/fs_zip.c index 9a55c7b9..e6d7a2b1 100644 --- a/engine/common/fs_zip.c +++ b/engine/common/fs_zip.c @@ -46,11 +46,17 @@ void *zlib_handle; #define qinflateEnd inflateEnd #define qinflate inflate #define qinflateInit2_ inflateInit2_ + #define qdeflateEnd deflateEnd + #define qdeflate deflate + #define qdeflateInit2_ deflateInit2_ #define qget_crc_table get_crc_table #endif #define qinflateInit2(strm, windowBits) \ qinflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define qdeflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + qdeflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) qboolean LibZ_Init(void) { @@ -60,6 +66,9 @@ qboolean LibZ_Init(void) {(void*)&qinflateEnd, "inflateEnd"}, {(void*)&qinflate, "inflate"}, {(void*)&qinflateInit2_, "inflateInit2_"}, + {(void*)&qdeflateEnd, "deflateEnd"}, + {(void*)&qdeflate, "deflate"}, + {(void*)&qdeflateInit2_, "deflateInit2_"}, // {(void*)&qcrc32, "crc32"}, #ifdef ZIPCRYPT {(void*)&qget_crc_table, "get_crc_table"}, @@ -260,6 +269,7 @@ typedef struct vfsfile_t vf; vfsfile_t *outfile; qboolean autoclosefile; + qboolean compress; //gzip header handling qboolean headerparsed; @@ -273,9 +283,21 @@ typedef struct static qboolean QDECL FS_GZ_Dec_Close(vfsfile_t *f) { vf_gz_dec_t *n = (vf_gz_dec_t*)f; + + if (n->compress) + qdeflate(&n->strm, Z_FINISH); + else + qinflate(&n->strm, Z_FINISH); + if (n->strm.next_out != n->out) + VFS_WRITE(n->outfile, n->out, n->strm.next_out-n->out); + + if (n->compress) + qdeflateEnd(&n->strm); + else + qinflateEnd(&n->strm); + if (n->autoclosefile) VFS_CLOSE(n->outfile); - qinflateEnd(&n->strm); Z_Free(n); return true; } @@ -380,7 +402,10 @@ noheader: while(n->strm.avail_in) { - ret = qinflate(&n->strm, Z_SYNC_FLUSH); + if (n->compress) + ret = qdeflate(&n->strm, Z_SYNC_FLUSH); + else + ret = qinflate(&n->strm, Z_SYNC_FLUSH); if (!n->strm.avail_out) { @@ -437,12 +462,13 @@ noheader: return len; } -vfsfile_t *FS_GZ_DecompressWriteFilter(vfsfile_t *outfile, qboolean autoclosefile) +vfsfile_t *FS_GZ_WriteFilter(vfsfile_t *outfile, qboolean autoclosefile, qboolean compress) { vf_gz_dec_t *n = Z_Malloc(sizeof(*n)); n->outfile = outfile; n->autoclosefile = autoclosefile; + n->compress = compress; n->strm.next_in = NULL; n->strm.avail_in = 0; @@ -460,7 +486,13 @@ vfsfile_t *FS_GZ_DecompressWriteFilter(vfsfile_t *outfile, qboolean autoclosefil n->vf.WriteBytes = FS_GZ_Dec_Write; n->vf.seekingisabadplan = true; - qinflateInit2(&n->strm, -MAX_WBITS); + if (n->compress) + { + n->headerparsed = true; //deflate will write out a minimal header for us, so we can just splurge everything without rewinding. + qdeflateInit2(&n->strm, Z_BEST_COMPRESSION, Z_DEFLATED, MAX_WBITS|16, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); + } + else + qinflateInit2(&n->strm, -MAX_WBITS); return &n->vf; } diff --git a/engine/common/plugin.c b/engine/common/plugin.c index 9f320c70..084e96f1 100644 --- a/engine/common/plugin.c +++ b/engine/common/plugin.c @@ -13,7 +13,7 @@ #ifdef PLUGINS cvar_t plug_sbar = CVARD("plug_sbar", "3", "Controls whether plugins are allowed to draw the hud, rather than the engine (when allowed by csqc). This is typically used to permit the ezhud plugin without needing to bother unloading it.\n=0: never use hud plugins.\n&1: Use hud plugins in deathmatch.\n&2: Use hud plugins in singleplayer/coop.\n=3: Always use hud plugins (when loaded)."); -cvar_t plug_loaddefault = CVAR("plug_loaddefault", "1"); +cvar_t plug_loaddefault = CVARD("plug_loaddefault", "1", "0: Load plugins only via explicit plug_load commands\n1: Load built-in plugins and those selected via the package manager\n2: Scan for misc plugins, loading all that can be found, but not built-ins.\n3: Scan for plugins, and then load any built-ins"); qintptr_t Plug_Bullet_Init(qintptr_t *args); qintptr_t Plug_ODE_Init(qintptr_t *args); @@ -248,20 +248,48 @@ static qintptr_t EXPORT_FN Plug_SystemCallsNative(qintptr_t arg, ...) } qintptr_t (QDECL *plugin_syscall)( qintptr_t arg, ... ) = Plug_SystemCallsNative; +static char *Plug_CleanName(const char *file, char *out, size_t sizeof_out) +{ + size_t len; + //"fteplug_ezhud_x86.REV.dll" gets converted into "ezhud" + + //skip fteplug_ + if (!Q_strncasecmp(file, "fteplug_", 8)) + file += 8; + + //strip .REV.dll + COM_StripAllExtensions(file, out, sizeof_out); + + //strip _x86 + len = strlen(out); + if (len > strlen("_"ARCH_CPU_POSTFIX) && !Q_strncasecmp(out+len-strlen("_"ARCH_CPU_POSTFIX), "_"ARCH_CPU_POSTFIX, strlen("_"ARCH_CPU_POSTFIX))) + { + len -= strlen("_"ARCH_CPU_POSTFIX); + out[len] = 0; + } + return out; +} plugin_t *Plug_Load(const char *file, int type) { + char temp[MAX_OSPATH]; plugin_t *newplug; + Plug_CleanName(file, temp, sizeof(temp)); for (newplug = plugs; newplug; newplug = newplug->next) { - if (!Q_strcasecmp(newplug->name, file)) + if (!Q_strcasecmp(newplug->name, temp)) return newplug; } - newplug = Z_Malloc(sizeof(plugin_t)+strlen(file)+1); + newplug = Z_Malloc(sizeof(plugin_t)+strlen(temp)+1); newplug->name = (char*)(newplug+1); - strcpy(newplug->name, file); + strcpy(newplug->name, temp); + if (!newplug->vm && (type & PLUG_NATIVE) && !Q_strncasecmp(file, "fteplug_", 8) && !Q_strcasecmp(ARCH_DL_POSTFIX+1, COM_FileExtension(file, temp, sizeof(temp)))) + { + COM_StripExtension(file, temp, sizeof(temp)); + newplug->vm = VM_Create(temp, Plug_SystemCallsNative, NULL); + } if (!newplug->vm && (type & PLUG_NATIVE)) newplug->vm = VM_Create(va("fteplug_%s_", file), Plug_SystemCallsNative, NULL); if (!newplug->vm && (type & PLUG_NATIVE)) @@ -1529,6 +1557,11 @@ void VM_Test_f(void) } }*/ +static void Plug_LoadDownloaded(const char *fname) +{ + Plug_Load(fname, PLUG_NATIVE); +} + void Plug_Initialise(qboolean fromgamedir) { char nat[MAX_OSPATH]; @@ -1536,11 +1569,7 @@ void Plug_Initialise(qboolean fromgamedir) if (!numplugbuiltins) { Cvar_Register(&plug_sbar, "plugins"); -#ifdef SUBSERVERS - if (!SSV_IsSubServer()) -#endif - - Cvar_Register(&plug_loaddefault, "plugins"); + Cvar_Register(&plug_loaddefault, "plugins"); Cmd_AddCommand("plug_closeall", Plug_CloseAll_f); Cmd_AddCommand("plug_close", Plug_Close_f); @@ -1620,9 +1649,11 @@ void Plug_Initialise(qboolean fromgamedir) Plug_Client_Init(); } - if (plug_loaddefault.value) +#ifdef SUBSERVERS + if (!SSV_IsSubServer()) //subservers will need plug_load I guess +#endif + if (plug_loaddefault.ival & 2) { - unsigned int u; if (!fromgamedir) { FS_NativePath("", FS_BINARYPATH, nat, sizeof(nat)); @@ -1633,6 +1664,11 @@ void Plug_Initialise(qboolean fromgamedir) { COM_EnumerateFiles("plugins/*.qvm", Plug_Emumerated, ".qvm"); } + } + if (plug_loaddefault.ival & 1) + { + unsigned int u; + PM_EnumeratePlugins(Plug_LoadDownloaded); for (u = 0; staticplugins[u].name; u++) { Plug_Load(staticplugins[u].name, PLUG_NATIVE); @@ -2040,12 +2076,15 @@ void Plug_Close_f(void) { plugin_t *plug; char *name = Cmd_Argv(1); + char cleaned[MAX_OSPATH]; if (Cmd_Argc()<2) { Con_Printf("Close which plugin?\n"); return; } + name = Plug_CleanName(name, cleaned, sizeof(cleaned)); + if (currentplug) Sys_Error("Plug_CloseAll_f called inside a plugin!\n"); diff --git a/engine/common/qvm.c b/engine/common/qvm.c index 73e789ec..ae96363a 100644 --- a/engine/common/qvm.c +++ b/engine/common/qvm.c @@ -104,6 +104,11 @@ qboolean QVM_LoadDLL(vm_t *vm, const char *name, qboolean binroot, void **vmMain if (!hVM && FS_NativePath(dllname_anycpu, FS_BINARYPATH, fname, sizeof(fname))) hVM = Sys_LoadLibrary(fname, funcs); + if (!hVM && FS_NativePath(dllname_arch, FS_ROOT, fname, sizeof(fname))) + hVM = Sys_LoadLibrary(fname, funcs); + if (!hVM && FS_NativePath(dllname_anycpu, FS_ROOT, fname, sizeof(fname))) + hVM = Sys_LoadLibrary(fname, funcs); + // run through the search paths iterator = NULL; while (!hVM && COM_IteratePaths(&iterator, NULL, 0, gpath, sizeof(gpath))) diff --git a/engine/d3d/d3d_backend.c b/engine/d3d/d3d_backend.c index 5f2d17c3..235f8040 100644 --- a/engine/d3d/d3d_backend.c +++ b/engine/d3d/d3d_backend.c @@ -671,7 +671,7 @@ void D3D9BE_Init(void) R_InitFlashblends(); } -void D3DBE_Set2D(void) +void D3D9BE_Set2D(void) { //start of some 2d rendering code. r_refdef.time = realtime; shaderstate.curtime = r_refdef.time; diff --git a/engine/d3d/vid_d3d.c b/engine/d3d/vid_d3d.c index 8d61afa7..5fa5e311 100644 --- a/engine/d3d/vid_d3d.c +++ b/engine/d3d/vid_d3d.c @@ -898,7 +898,7 @@ void D3D9_Set2D (void) r_refdef.pxrect.width = vid.fbpwidth; r_refdef.pxrect.height = vid.fbpheight; - D3DBE_Set2D(); + D3D9BE_Set2D(); } static int d3d9error(int i) diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index 8b82fad4..2e816cb7 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -1386,7 +1386,6 @@ void GenerateFogTexture(texid_t *tex, float density, float zscale) void GLBE_DestroyFBOs(void) { size_t i; - GLBE_FBO_Destroy(&shaderstate.fbo_2dfbo); GLBE_FBO_Destroy(&shaderstate.fbo_lprepass); for (i = 0; i < R_MAX_RECURSE; i++) @@ -1430,6 +1429,7 @@ void GLBE_DestroyFBOs(void) void GLBE_Shutdown(void) { + GLBE_FBO_Destroy(&shaderstate.fbo_2dfbo); GLBE_DestroyFBOs(); BZ_Free(shaderstate.wbatches); diff --git a/engine/gl/gl_draw.c b/engine/gl/gl_draw.c index cd306674..29321527 100644 --- a/engine/gl/gl_draw.c +++ b/engine/gl/gl_draw.c @@ -60,7 +60,7 @@ Draw_Init void GLDraw_Init (void) { //figure out which extra features we can support on these drivers. - r_deluxemapping = r_deluxemapping_cvar.ival; + r_deluxmapping = r_deluxmapping_cvar.ival; r_lightprepass = r_lightprepass_cvar.ival && sh_config.progs_supported; r_softwarebanding = r_softwarebanding_cvar.ival && sh_config.progs_supported; if (gl_config.gles && gl_config.glversion < 3.0) diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index 5292a9bc..59695e99 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -292,7 +292,7 @@ static int dehex_e(int i, qboolean *error) *error = true; return 0; } -static qboolean Terr_IsSectionFName(heightmap_t *hm, char *fname, int *sx, int *sy) +static qboolean Terr_IsSectionFName(heightmap_t *hm, const char *fname, int *sx, int *sy) { int l; qboolean error = false; @@ -1873,7 +1873,7 @@ int Heightmap_Save(heightmap_t *hm) #ifndef CLIENTONLY //on servers, we can get requests to download current map sections. if so, give them it. -qboolean Terrain_LocateSection(char *name, flocation_t *loc) +qboolean Terrain_LocateSection(const char *name, flocation_t *loc) { heightmap_t *hm; hmsection_t *s; @@ -1881,7 +1881,7 @@ qboolean Terrain_LocateSection(char *name, flocation_t *loc) char fname[MAX_QPATH]; //reject if its not in maps - if (strncmp(name, "maps/", 5)) + if (Q_strncasecmp(name, "maps/", 5)) return false; if (!sv.world.worldmodel) diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index 01979654..442af6c2 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -1755,7 +1755,7 @@ void Mod_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean samples = ql2->lmsize; litdata = shifts+ql2->numsurfs; - if (r_deluxemapping) + if (r_deluxmapping) luxdata = litdata+samples*3; } } @@ -1817,7 +1817,7 @@ void Mod_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean } - if (!luxdata && r_loadlits.ival && r_deluxemapping) + if (!luxdata && r_loadlits.ival && r_deluxmapping) { //the map util has a '-scalecos X' parameter. use 0 if you're going to use only just lux. without lux scalecos 0 is hideous. char luxname[MAX_QPATH]; size_t luxsz = 0; @@ -1882,14 +1882,14 @@ void Mod_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean #endif #ifdef RUNTIMELIGHTING - if (!lightmodel && r_loadlits.value == 2 && (!litdata || (!luxdata && r_deluxemapping))) + if (!lightmodel && r_loadlits.value == 2 && (!litdata || (!luxdata && r_deluxmapping))) { writelitfile = !litdata; numlightdata = l->filelen; lightmodel = loadmodel; relitsurface = 0; } - else if (!lightmodel && r_loadlits.value && r_deluxemapping && !luxdata && !(r_shadow_realtime_world.ival && r_shadow_realtime_world_lightmaps.value<=0)) + else if (!lightmodel && r_deluxmapping_cvar.value>1 && r_deluxmapping && !luxdata && !(r_shadow_realtime_world.ival && r_shadow_realtime_world_lightmaps.value<=0)) { //if deluxemapping is on, generate missing lux files a little more often, but don't bother if we have rtlights on anyway. writelitfile = false; numlightdata = l->filelen; @@ -1915,7 +1915,7 @@ void Mod_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean } } /*if we're relighting, make sure there's the proper lux data to be updated*/ - if (lightmodel == loadmodel && r_deluxemapping && !luxdata) + if (lightmodel == loadmodel && r_deluxmapping && !luxdata) { int i; luxdata = ZG_Malloc(&loadmodel->memgroup, samples*3); diff --git a/engine/gl/gl_model.h b/engine/gl/gl_model.h index 8d889402..7de94acd 100644 --- a/engine/gl/gl_model.h +++ b/engine/gl/gl_model.h @@ -1023,7 +1023,7 @@ void Terr_FreeModel(model_t *mod); void Terr_FinishTerrain(model_t *model); void Terr_PurgeTerrainModel(model_t *hm, qboolean lightmapsonly, qboolean lightmapreusable); void *Mod_LoadTerrainInfo(model_t *mod, char *loadname, qboolean force); //call this after loading a bsp -qboolean Terrain_LocateSection(char *name, flocation_t *loc); //used on servers to generate sections for download. +qboolean Terrain_LocateSection(const char *name, flocation_t *loc); //used on servers to generate sections for download. qboolean Heightmap_Trace(model_t *model, int forcehullnum, framestate_t *framestate, vec3_t axis[3], vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, qboolean capsule, unsigned int contentmask, struct trace_s *trace); unsigned int Heightmap_PointContents(model_t *model, vec3_t axis[3], vec3_t org); struct fragmentdecal_s; diff --git a/engine/gl/gl_rlight.c b/engine/gl/gl_rlight.c index edcd3d42..b94f422a 100644 --- a/engine/gl/gl_rlight.c +++ b/engine/gl/gl_rlight.c @@ -929,6 +929,8 @@ qboolean R_ImportRTLights(const char *entlump) lightscale = 1; if (fadescale <= 0) fadescale = 1; + if (color[0] >= 16 || color[1] >= 16 || color[2] >= 16) //_color 255 255 255 should be identity, not super-oversaturated. + VectorScale(color, 1/255.0, color); //if only there were standards for this sort of thing. if (color[0] == color[1] && color[0] == color[2]) { color[0] *= overridecolor[0]; diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 5bd4c8e8..f731b734 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -299,7 +299,7 @@ static qboolean Shader_EvaluateCondition(shader_t *shader, char **ptr) else if (!Q_stricmp(token, "lightmap")) conditiontrue = conditiontrue == !r_fullbright.value; else if (!Q_stricmp(token, "deluxmap")) - conditiontrue = conditiontrue == r_deluxemapping; + conditiontrue = conditiontrue == r_deluxmapping; else if (!Q_stricmp(token, "softwarebanding")) conditiontrue = conditiontrue == r_softwarebanding; @@ -311,6 +311,8 @@ static qboolean Shader_EvaluateCondition(shader_t *shader, char **ptr) conditiontrue = conditiontrue == (qrenderer == QR_VULKAN); else if (!Q_stricmp(token, "opengl")) conditiontrue = conditiontrue == (qrenderer == QR_OPENGL); + else if (!Q_stricmp(token, "d3d8")) + conditiontrue = conditiontrue == (qrenderer == QR_DIRECT3D8); else if (!Q_stricmp(token, "d3d9")) conditiontrue = conditiontrue == (qrenderer == QR_DIRECT3D9); else if (!Q_stricmp(token, "d3d11")) @@ -1354,6 +1356,7 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip cantess = true; else if (strncmp("SPECULAR", script, end - script)) if (strncmp("DELUXE", script, end - script)) + if (strncmp("DELUX", script, end - script)) if (strncmp("OFFSETMAPPING", script, end - script)) if (strncmp("RELIEFMAPPING", script, end - script)) Con_DPrintf("Unknown pemutation in glsl program %s\n", name); @@ -1585,7 +1588,7 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip permutationdefines[pn++] = "#define RELIEFMAPPING\n"; } - if (r_deluxemapping) //fixme: should be per-model really + if (r_deluxmapping) //fixme: should be per-model really permutationdefines[pn++] = "#define DELUXE\n"; } permutationdefines[pn++] = NULL; diff --git a/engine/gl/glquake.h b/engine/gl/glquake.h index 1fcf58c5..79c17a64 100644 --- a/engine/gl/glquake.h +++ b/engine/gl/glquake.h @@ -46,8 +46,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define APIENTRY #endif -void D3D9_Set2D (void); - void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs); qboolean BoundsIntersect (vec3_t mins1, vec3_t maxs1, vec3_t mins2, vec3_t maxs2); void ClearBounds (vec3_t mins, vec3_t maxs); diff --git a/engine/gl/shader.h b/engine/gl/shader.h index c09bf860..66e85609 100644 --- a/engine/gl/shader.h +++ b/engine/gl/shader.h @@ -787,6 +787,31 @@ void GLBE_FBO_Pop(int oldfbo); void GLBE_FBO_Destroy(fbostate_t *state); int GLBE_FBO_Update(fbostate_t *state, unsigned int enables, texid_t *destcol, int colourbuffers, texid_t destdepth, int width, int height, int layer); #endif +#ifdef D3D8QUAKE +void D3D8BE_Init(void); +void D3D8BE_Shutdown(void); +void D3D8BE_SelectMode(backendmode_t mode); +void D3D8BE_DrawMesh_List(shader_t *shader, int nummeshes, mesh_t **mesh, vbo_t *vbo, texnums_t *texnums, unsigned int beflags); +void D3D8BE_DrawMesh_Single(shader_t *shader, mesh_t *meshchain, vbo_t *vbo, unsigned int beflags); +void D3D8BE_SubmitBatch(batch_t *batch); +batch_t *D3D8BE_GetTempBatch(void); +void D3D8BE_GenBrushModelVBO(model_t *mod); +void D3D8BE_ClearVBO(vbo_t *vbo); +void D3D8BE_UploadAllLightmaps(void); +void D3D8BE_DrawWorld (batch_t **worldbatches); +qboolean D3D8BE_LightCullModel(vec3_t org, model_t *model); +void D3D8BE_SelectEntity(entity_t *ent); +qboolean D3D8BE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode); +void D3D8BE_VBO_Begin(vbobctx_t *ctx, size_t maxsize); +void D3D8BE_VBO_Data(vbobctx_t *ctx, void *data, size_t size, vboarray_t *varray); +void D3D8BE_VBO_Finish(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earray, void **vbomem, void **ebomem); +void D3D8BE_VBO_Destroy(vboarray_t *vearray, void *mem); +void D3D8BE_Scissor(srect_t *rect); + +void D3D8Shader_Init(void); +void D3D8BE_Reset(qboolean before); +void D3D8BE_Set2D(void); +#endif #ifdef D3D9QUAKE void D3D9BE_Init(void); void D3D9BE_Shutdown(void); @@ -810,7 +835,7 @@ void D3D9BE_Scissor(srect_t *rect); void D3D9Shader_Init(void); void D3D9BE_Reset(qboolean before); -void D3DBE_Set2D(void); +void D3D9BE_Set2D(void); #endif #ifdef D3D11QUAKE void D3D11BE_Init(void); diff --git a/engine/http/httpclient.c b/engine/http/httpclient.c index 89497107..4bd8af4a 100644 --- a/engine/http/httpclient.c +++ b/engine/http/httpclient.c @@ -5,8 +5,6 @@ #include "netinc.h" #include "fs.h" -vfsfile_t *FS_GZ_DecompressWriteFilter(vfsfile_t *outfile, qboolean autoclosefile); - #if defined(WEBCLIENT) #ifndef NPFTE static struct dl_download *activedownloads; @@ -794,7 +792,7 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) if (con->gzip) { #ifdef AVAIL_GZDEC - con->file = FS_GZ_DecompressWriteFilter(dl->file, false); + con->file = FS_GZ_WriteFilter(dl->file, false, false); #else Con_Printf("HTTP: no support for gzipped files \"%s\"\n", dl->localname); dl->status = DL_FAILED; @@ -925,7 +923,7 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) dl->status = DL_FAILED; else { -#if AVAIL_GZDEC +#ifdef AVAIL_GZDEC if (con->gzip && con->file) { VFS_CLOSE(con->file); diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index 8b09199b..f3fd4fba 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -452,6 +452,7 @@ stringtab_t *stringtablist[256]; int QCC_CopyString (char *str) { int old; + size_t len; if (!str) return 0; @@ -492,8 +493,11 @@ int QCC_CopyString (char *str) } old = strofs; - strcpy (strings+strofs, str); - strofs += strlen(str)+1; + len = strlen(str)+1; + if (strofs + len > MAX_STRINGS) + QCC_Error(ERR_INTERNAL, "QCC_CopyString: stringtable size limit exceeded\n"); + memcpy (strings+strofs, str, len); + strofs += len; return old; } @@ -507,6 +511,8 @@ int QCC_CopyStringLength (char *str, size_t length) return !flag_nullemptystr; old = strofs; + if (strofs + length > MAX_STRINGS) + QCC_Error(ERR_INTERNAL, "QCC_CopyString: stringtable size limit exceeded\n"); memcpy (strings+strofs, str, length); strofs += length; return old; @@ -514,6 +520,7 @@ int QCC_CopyStringLength (char *str, size_t length) int QCC_CopyDupBackString (char *str) { + size_t length; int old; char *s; @@ -522,8 +529,11 @@ int QCC_CopyDupBackString (char *str) return s-strings; old = strofs; + length = strlen(str)+1; + if (strofs + length > MAX_STRINGS) + QCC_Error(ERR_INTERNAL, "QCC_CopyString: stringtable size limit exceeded\n"); strcpy (strings+strofs, str); - strofs += strlen(str)+1; + strofs += length; return old; } @@ -1232,6 +1242,7 @@ pbool QCC_WriteData (int crc) int *statement_linenums; void *funcdata; size_t funcdatasize; + pbool bigjumps; extern char *basictypenames[]; @@ -1270,6 +1281,17 @@ pbool QCC_WriteData (int crc) QCC_UnmarshalLocals(); QCC_FinaliseTemps(); + for (i=0 ; i 0x7fff || statements[i].a.ofs < 0x7fff)) + break; + if (!statements[i].a.sym && (statements[i].a.ofs > 0x7fff || statements[i].a.ofs < 0x7fff)) + break; + if (!statements[i].a.sym && (statements[i].a.ofs > 0x7fff || statements[i].a.ofs < 0x7fff)) + break; + } + bigjumps = i 65530 ) + if (bigjumps) + { + printf("Forcing target to FTE32 due to large functions\n"); + outputsttype = PST_FTE32; + } + else if (numpr_globals > 65530) { printf("Forcing target to FTE32 due to numpr_globals\n"); outputsttype = PST_FTE32; @@ -1309,10 +1336,18 @@ pbool QCC_WriteData (int crc) if (qcc_targetformat == QCF_FTEDEBUG) debugtarget = true; - if (numpr_globals > 65530) + if (outputsttype != PST_FTE32) { - printf("Using 32 bit target due to numpr_globals\n"); - outputsttype = PST_FTE32; + if (bigjumps) + { + printf("Using 32 bit target due to large functions\n"); + outputsttype = PST_FTE32; + } + else if (numpr_globals > 65530) + { + printf("Using 32 bit target due to numpr_globals\n"); + outputsttype = PST_FTE32; + } } if (qcc_targetformat == QCF_DARKPLACES) diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 1a06a484..4696453d 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -9007,29 +9007,7 @@ int PF_ForceInfoKey_Internal(unsigned int entnum, const char *key, const char *v SV_ExtractFromUserinfo (&svs.clients[entnum-1], false); - MSG_WriteByte (&sv.reliable_datagram, svc_setinfo); - MSG_WriteByte (&sv.reliable_datagram, entnum-1); - MSG_WriteString (&sv.reliable_datagram, key); - MSG_WriteString (&sv.reliable_datagram, Info_ValueForKey(svs.clients[entnum-1].userinfo, key)); - -#ifdef NQPROT - if (!strcmp(key, "name")) - { - MSG_WriteByte(&sv.nqreliable_datagram, svc_updatename); - MSG_WriteByte(&sv.nqreliable_datagram, entnum-1); - MSG_WriteString (&sv.nqreliable_datagram, Info_ValueForKey(svs.clients[entnum-1].userinfo, key)); - } - else if (!strcmp(key, "topcolor") || !strcmp(key, "bottomcolor")) - { - int c; - //this sucks, but whatever. - c = (atoi(Info_ValueForKey(svs.clients[entnum-1].userinfo, "topcolor" )) & 0xf)<<4; - c|= (atoi(Info_ValueForKey(svs.clients[entnum-1].userinfo, "bottomcolor")) & 0xf); - MSG_WriteByte(&sv.nqreliable_datagram, svc_updatecolors); - MSG_WriteByte(&sv.nqreliable_datagram, entnum-1); - MSG_WriteByte (&sv.nqreliable_datagram, c); - } -#endif + value = Info_ValueForKey(svs.clients[entnum-1].userinfo, key); if (!strcmp(key, "*spectator")) { @@ -9050,10 +9028,8 @@ int PF_ForceInfoKey_Internal(unsigned int entnum, const char *key, const char *v else svs.clients[entnum-1].spectator = ns; } -#ifdef _DEBUG - if (!strcmp(key, "*transfer")) - Con_Printf("WARNING: *transfer is no longer supported\n"); -#endif + + SV_BroadcastUserinfoChange(host_client, SV_UserInfoIsBasic(key), key, value); } return 1; @@ -9063,7 +9039,7 @@ int PF_ForceInfoKey_Internal(unsigned int entnum, const char *key, const char *v return 0; } -void QCBUILTIN PF_ForceInfoKey(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +static void QCBUILTIN PF_ForceInfoKey(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { edict_t *e; const char *value; @@ -10044,15 +10020,15 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs //we support them for mvdsv compatability but some of them look very hacky. //these ones are not honoured with numbers, but can be used via the proper means. {"teamfield", PF_teamfield, 0, 0, 0, 87, D("void(.string teamfield)",NULL), true}, - {"substr", PF_substr, 0, 0, 0, 88, D("string(string str, float start, float len)",NULL), true}, + {"substr", PF_substr, 0, 0, 0, 88, D("string(string str, float start, float len)","Returns the theDoes not work on tempstrings nor zoned strings."), true}, {"mvdstrcat", PF_strcat, 0, 0, 0, 89, D("string(string s1, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8)",NULL), true}, {"mvdstrlen", PF_strlen, 0, 0, 0, 90, D("float(string s)",NULL), true}, - {"str2byte", PF_str2byte, 0, 0, 0, 91, D("float(string str)",NULL), true}, - {"str2short", PF_str2short, 0, 0, 0, 92, D("float(string str)",NULL), true}, - {"mvdnewstr", PF_mvdsv_newstring, 0, 0, 0, 93, D("string(string s, optional float bufsize)",NULL), true}, - {"mvdfreestr", PF_mvdsv_freestring,0, 0, 0, 94, D("void(string s)",NULL), true}, - {"conprint", PF_conprint, 0, 0, 0, 95, D("void(string s, ...)",NULL), true}, - {"readcmd", PF_readcmd, 0, 0, 0, 96, D("string(string str)",NULL), true}, + {"str2byte", PF_str2byte, 0, 0, 0, 91, D("float(string str)","Returns the value of the first byte of the given string."), true}, + {"str2short", PF_str2short, 0, 0, 0, 92, D("float(string str)","Returns the value of the first two bytes of the given string, treated as a short."), true}, + {"mvdnewstr", PF_mvdsv_newstring, 0, 0, 0, 93, D("string(string s, optional float bufsize)","Allocs a copy of the string. If bufsize is longer than the string then there will be extra space available on the end. The resulting string can then be modified freely."), true}, + {"mvdfreestr", PF_mvdsv_freestring,0, 0, 0, 94, D("void(string s)","Frees memory allocated by mvdnewstr."), true}, + {"conprint", PF_conprint, 0, 0, 0, 95, D("void(string s, ...)","Prints the string(s) onto the local console, bypassing redirects."), true}, + {"readcmd", PF_readcmd, 0, 0, 0, 0/*96*/, D("string(string str)",NULL), true}, {"mvdstrcpy", PF_MVDSV_strcpy, 0, 0, 0, 97, D("void(string dst, string src)",NULL), true}, {"strstr", PF_strstr, 0, 0, 0, 98, D("string(string str, string sub)",NULL), true}, {"mvdstrncpy", PF_MVDSV_strncpy, 0, 0, 0, 99, D("void(string dst, string src, float count)",NULL), true}, @@ -10342,7 +10318,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"clearscene", PF_Fixme, 0, 0, 0, 300, D("void()", "Forgets all rentities, polygons, and temporary dlights. Resets all view properties to their default values.")},// (EXT_CSQC) {"addentities", PF_Fixme, 0, 0, 0, 301, D("void(float mask)", "Walks through all entities effectively doing this:\n if (ent.drawmask&mask){ if (!ent.predaw()) addentity(ent); }\nIf mask&MASK_DELTA, non-csqc entities, particles, and related effects will also be added to the rentity list.\n If mask&MASK_STDVIEWMODEL then the default view model will also be added.")},// (EXT_CSQC) {"addentity", PF_Fixme, 0, 0, 0, 302, D("void(entity ent)", "Copies the entity fields into a new rentity for later rendering via addscene.")},// (EXT_CSQC) - {"addtrisoup_1", PF_Fixme, 0, 0, 0, 0, D("void(string texturename, float flags, void *verts, int *indexes, int numindexes)", "Adds the specified trisoup into the scene as additional geometry. This permits caching geometry to reduce builtin spam. Indexes are a triangle list (so eg quads will need 6 indicies to form two triangles). NOTE: this is not going to be a speedup over polygons if you're still generating lots of new data every frame.")}, + {"addtrisoup_simple",PF_Fixme, 0, 0, 0, 0, D("typedef float vec2[2];\ntypedef float vec3[3];\ntypedef float vec4[4];\ntypedef struct trisoup_simple_vert_s {vec3 xyz;vec2 st;vec4 rgba;} trisoup_simple_vert_t;\nvoid(string texturename, int flags, struct trisoup_simple_vert_s *verts, int *indexes, int numindexes)", "Adds the specified trisoup into the scene as additional geometry. This permits caching geometry to reduce builtin spam. Indexes are a triangle list (so eg quads will need 6 indicies to form two triangles). NOTE: this is not going to be a speedup over polygons if you're still generating lots of new data every frame.")}, {"setproperty", PF_Fixme, 0, 0, 0, 303, D("#define setviewprop setproperty\nfloat(float property, ...)", "Allows you to override default view properties like viewport, fov, and whether the engine hud will be drawn. Different VF_ values have slightly different arguments, some are vectors, some floats.")},// (EXT_CSQC) {"renderscene", PF_Fixme, 0, 0, 0, 304, D("void()", "Draws all entities, polygons, and particles on the rentity list (which were added via addentities or addentity), using the various view properties set via setproperty. There is no ordering dependancy.\nThe scene must generally be cleared again before more entities are added, as entities will persist even over to the next frame.\nYou may call this builtin multiple times per frame, but should only be called from CSQC_UpdateView.")},// (EXT_CSQC) diff --git a/engine/server/server.h b/engine/server/server.h index b4431550..5d4ffe5b 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -1220,6 +1220,8 @@ void SV_SendServerInfoChange(const char *key, const char *value); void SV_SendMessagesToAll (void); void SV_FindModelNumbers (void); +void SV_BroadcastUserinfoChange(client_t *about, qboolean isbasic, const char *key, const char *newval); + // // sv_user.c // @@ -1227,7 +1229,7 @@ void SV_FindModelNumbers (void); void SVNQ_New_f (void); void SVNQ_ExecuteClientMessage (client_t *cl); #endif -qboolean SV_UserInfoIsBasic(char *infoname); //standard message. +qboolean SV_UserInfoIsBasic(const char *infoname); //standard message. void SV_ExecuteClientMessage (client_t *cl); void SVQ2_ExecuteClientMessage (client_t *cl); int SV_PMTypeForClient (client_t *cl, edict_t *ent); @@ -1498,6 +1500,7 @@ extern cvar_t sv_demoMaxDirSize; char *SV_Demo_CurrentOutput(void); void SV_MVDInit(void); char *SV_MVDNum(char *buffer, int bufferlen, int num); +const char *SV_MVDLastNum(unsigned int num); void SV_SendMVDMessage(void); void SV_MVD_WriteReliables(qboolean writebroadcasts); qboolean SV_ReadMVD (void); diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index e4e01ca5..0ed1b048 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -2135,7 +2135,6 @@ SV_Serverinfo_f Examine or change the serverinfo string =========== */ -extern char *Info_KeyForNumber(char *s, int num); void SV_Serverinfo_f (void) { cvar_t *var; diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index 07fb28db..a900ae58 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -178,9 +178,14 @@ qboolean SV_AddNailUpdate (edict_t *ent) { if (ent->v->modelindex != sv_nailmodel && ent->v->modelindex != sv_supernailmodel) - return false; + return false; //must be a nail if (sv_nailhack.value || (host_client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)) - return false; + return false; //'nailhack' is named because of a qizmo-publicised binary hack to disable svc_nails. replacementdeltas also trims much of the state so we may as well use it. + //should probably also detect qizmo specifically - its trajectory stuff beats svc_nails. + if (ent->v->origin[0] <= -4096 || ent->v->origin[0] >= 4096 || + ent->v->origin[1] <= -4096 || ent->v->origin[1] >= 4096 || + ent->v->origin[2] <= -4096 || ent->v->origin[2] >= 4096) + return !(host_client->fteprotocolextensions & PEXT_FLOATCOORDS); //outside the bounds of the nails protocol. just swallow it if it can't be sent anyway. #ifdef SERVER_DEMO_PLAYBACK demonails = false; diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 6a8cc3c1..33582e33 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -1028,6 +1028,14 @@ void SV_FullClientUpdate (client_t *client, client_t *to) ClientReliableWrite_Begin (to, svc_updatecolors, 3); ClientReliableWrite_Byte (to, i); ClientReliableWrite_Byte (to, playercolor); + + if (to->fteprotocolextensions2 & PEXT2_PREDINFO) + { + char quotedval[8192]; + char *s = va("//fui %i %s\n", i, COM_QuotedString(client->userinfo, quotedval, sizeof(quotedval), false)); + ClientReliableWrite_Begin(to, svc_stufftext, 2+strlen(s)); + ClientReliableWrite_String(to, s); + } } } @@ -2084,7 +2092,7 @@ Q3: connect "\key\val" DP: connect\key\val QW: connect $VER $QPORT $CHALLENGE "\key\val" SS: connect2 $VER $QPORT $CHALLENGE "\key\val" "\key\val" -NQ: hacked to take the form of QW +NQ: hacked to take the form of QW, but with protocol version 3. extension flags follow it. */ client_t *SVC_DirectConnect(void) @@ -2318,7 +2326,12 @@ client_t *SVC_DirectConnect(void) // note an extra qbyte is needed to replace spectator key for (i = 0; i < numssclients; i++) + { Q_strncpyz (userinfo[i], Cmd_Argv(4+i), sizeof(userinfo[i])-1); + + if (protocol == SCP_NETQUAKE) + Info_RemoveKey(userinfo[i], "mod"); //its served its purpose. + } } { @@ -5085,12 +5098,7 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose) { Info_SetValueForKey(cl->userinfo, "team", p, sizeof(cl->userinfo)); if (verbose) - { - MSG_WriteByte (&sv.reliable_datagram, svc_setinfo); - MSG_WriteByte (&sv.reliable_datagram, cl-svs.clients); - MSG_WriteString (&sv.reliable_datagram, "team"); - MSG_WriteString (&sv.reliable_datagram, p); - } + SV_BroadcastUserinfoChange(cl, true, "team", p); } Q_strncpyz (cl->team, val, sizeof(cl->teambuf)); diff --git a/engine/server/sv_mvd.c b/engine/server/sv_mvd.c index f031b6b9..dc2b71e8 100644 --- a/engine/server/sv_mvd.c +++ b/engine/server/sv_mvd.c @@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifndef CLIENTONLY #include "winquake.h" +#include "fs.h" #include "netinc.h" @@ -41,6 +42,7 @@ cvar_t sv_demoPings = CVARD("sv_demoPings", "10", "Interval between ping updates cvar_t sv_demoMaxSize = CVARD("sv_demoMaxSize", "", "Demos will be truncated to be no larger than this size."); cvar_t sv_demoExtraNames = CVAR("sv_demoExtraNames", ""); cvar_t sv_demoExtensions = CVARD("sv_demoExtensions", "", "Enables protocol extensions within MVDs. This will cause older/non-fte clients to error upon playback.\n0: off.\n1: all extensions.\n2: extensions also supported by a certain other engine."); +cvar_t sv_demoAutoCompress = CVARD("sv_demoAutoCompress", "", "Specifies whether to compress demos as they're recorded.\n0 = no compression.\n1 = gzip compression."); cvar_t qtv_password = CVAR( "qtv_password", ""); cvar_t qtv_streamport = CVARAF( "qtv_streamport", "0", @@ -55,6 +57,17 @@ cvar_t sv_demotxt = CVAR("sv_demotxt", "1"); void SV_WriteMVDMessage (sizebuf_t *msg, int type, int to, float time); void SV_WriteRecordMVDMessage (sizebuf_t *msg); + +static struct +{ //tracks the previously recorded demos, so we don't have to content with dates and filesystem ordering and stuff. +#define DEMOLOG_LENGTH 16 + unsigned int sequence; //incremented + struct + { + char filename[MAX_QPATH]; + } log[DEMOLOG_LENGTH]; +} demolog; + demo_t demo; static float demo_prevtime; //static dbuffer_t *demobuffer; @@ -105,6 +118,8 @@ static void DestClose(mvddest_t *d, enum mvdclosereason_e reason) else if (d->desttype != DEST_STREAM) { char buf[512]; + Q_strncpyz(demolog.log[demolog.sequence%DEMOLOG_LENGTH].filename, d->simplename, sizeof(demolog.log[demolog.sequence%DEMOLOG_LENGTH].filename)); + demolog.sequence++; SV_BroadcastPrintf (PRINT_CHAT, "Server recording complete\n^[/download %s^]\n", COM_QuotedString(va("demos/%s",d->simplename), buf, sizeof(buf), false)); } @@ -729,6 +744,7 @@ typedef struct { char name[MAX_MVD_NAME]; int size; + time_t mtime; } file_t; typedef struct @@ -759,11 +775,24 @@ static int QDECL Sys_listdirFound(const char *fname, qofs_t fsize, time_t mtime, f = &dir->files[dir->numfiles++]; Q_strncpyz(f->name, fname, sizeof(f->name)); f->size = fsize; + f->mtime = mtime; dir->size += fsize; return true; } +static int Sys_listdir_Sort(const void *va, const void *vb) +{ + const file_t *fa = va; + const file_t *fb = vb; + + if (fa->mtime == fb->mtime) + return 0; + if (fa->mtime >= fb->mtime) + return 1; + return -1; +} + static dir_t *Sys_listdir (char *path, char *ext, qboolean usesorting) { char searchterm[MAX_QPATH]; @@ -777,6 +806,15 @@ static dir_t *Sys_listdir (char *path, char *ext, qboolean usesorting) Q_strncpyz(searchterm, va("%s/*%s", path, ext), sizeof(searchterm)); COM_EnumerateFiles(searchterm, Sys_listdirFound, dir); + if (!strcmp(ext, ".mvd")) + { + Q_strncpyz(searchterm, va("%s/*%s.gz", path, ext), sizeof(searchterm)); + COM_EnumerateFiles(searchterm, Sys_listdirFound, dir); + } + + if (usesorting) + qsort(dir->files, dir->numfiles, sizeof(*dir->files), Sys_listdir_Sort); + return dir; } static void Sys_freedir(dir_t *dir) @@ -1244,18 +1282,19 @@ void MVD_Init (void) { #define MVDVARGROUP "Server MVD cvars" - Cvar_Register (&sv_demofps, MVDVARGROUP); + Cvar_Register (&sv_demofps, MVDVARGROUP); Cvar_Register (&sv_demoPings, MVDVARGROUP); Cvar_Register (&sv_demoUseCache, MVDVARGROUP); Cvar_Register (&sv_demoCacheSize, MVDVARGROUP); Cvar_Register (&sv_demoMaxSize, MVDVARGROUP); Cvar_Register (&sv_demoMaxDirSize, MVDVARGROUP); - Cvar_Register (&sv_demoDir, MVDVARGROUP); + Cvar_Register (&sv_demoDir, MVDVARGROUP); Cvar_Register (&sv_demoPrefix, MVDVARGROUP); Cvar_Register (&sv_demoSuffix, MVDVARGROUP); - Cvar_Register (&sv_demotxt, MVDVARGROUP); + Cvar_Register (&sv_demotxt, MVDVARGROUP); Cvar_Register (&sv_demoExtraNames, MVDVARGROUP); Cvar_Register (&sv_demoExtensions, MVDVARGROUP); + Cvar_Register (&sv_demoAutoCompress,MVDVARGROUP); } static char *SV_PrintTeams(void) @@ -1361,6 +1400,11 @@ mvddest_t *SV_MVD_InitRecordFile (char *name) return NULL; } +#ifdef AVAIL_GZDEC + if (!Q_strcasecmp("gz", COM_FileExtension(name, path, sizeof(path)))) + file = FS_GZ_WriteFilter(file, true, true); +#endif + dst = Z_Malloc(sizeof(mvddest_t)); dst->socket = INVALID_SOCKET; strcpy(dst->filename, name); @@ -1649,7 +1693,7 @@ qboolean SV_MVD_Record (mvddest_t *dest) if (sv_demoExtensions.ival == 2 || !*sv_demoExtensions.string) { /*more limited subset supported by ezquake*/ - demo.recorder.fteprotocolextensions = PEXT_CHUNKEDDOWNLOADS|PEXT_256PACKETENTITIES|PEXT_FLOATCOORDS|PEXT_MODELDBL|PEXT_ENTITYDBL|PEXT_ENTITYDBL2|PEXT_SPAWNSTATIC2; + demo.recorder.fteprotocolextensions = PEXT_CHUNKEDDOWNLOADS|PEXT_256PACKETENTITIES|/*PEXT_FLOATCOORDS|*/PEXT_MODELDBL|PEXT_ENTITYDBL|PEXT_ENTITYDBL2|PEXT_SPAWNSTATIC2; // demo.recorder.fteprotocolextensions |= PEXT_HLBSP; /*ezquake DOES have this, but it is pointless and should have been in some feature mask rather than protocol extensions*/ // demo.recorder.fteprotocolextensions |= PEXT_ACCURATETIMINGS; /*ezquake does not support this any more*/ // demo.recorder.fteprotocolextensions |= PEXT_TRANS; /*ezquake has no support for .alpha*/ @@ -1936,7 +1980,12 @@ void SV_MVD_Record_f (void) COM_StripExtension(name, name, sizeof(name)); - COM_DefaultExtension(name, ".mvd", sizeof(name)); +#ifdef AVAIL_GZDEC + if (sv_demoAutoCompress.ival == 1) + COM_DefaultExtension(name, ".mvd.gz", sizeof(name)); + else +#endif + COM_DefaultExtension(name, ".mvd", sizeof(name)); FS_CreatePath (name, FS_GAMEONLY); // @@ -2223,7 +2272,7 @@ void SV_MVDEasyRecord_f (void) Q_strncpyz(name2, name, sizeof(name2)); // COM_StripExtension(name2, name2); FS_CreatePath (name2, FS_GAMEONLY); - strcat (name2, ".mvd"); + Q_strncatz(name2, ".mvd", sizeof(name2)); if ((f = FS_OpenVFS(name2, "rb", FS_GAMEONLY)) == 0) f = FS_OpenVFS(va("%s.gz", name2), "rb", FS_GAMEONLY); @@ -2234,13 +2283,17 @@ void SV_MVDEasyRecord_f (void) VFS_CLOSE (f); snprintf(name2, sizeof(name2), "%s_%02i", name, i); // COM_StripExtension(name2, name2); - strcat (name2, ".mvd"); + Q_strncatz(name2, ".mvd", sizeof(name2)); if ((f = FS_OpenVFS (name2, "rb", FS_GAMEONLY)) == 0) f = FS_OpenVFS(va("%s.gz", name2), "rb", FS_GAMEONLY); i++; } while (f); } +#ifdef AVAIL_GZDEC + if (sv_demoAutoCompress.ival == 1) + Q_strncatz(name2, ".gz", sizeof(name2)); +#endif SV_MVD_Record (SV_MVD_InitRecordFile(name2)); } @@ -2370,6 +2423,7 @@ void SV_MVDStream_Poll(void) #endif } +//console command for servers/admins void SV_MVDList_f (void) { mvddest_t *d; @@ -2420,6 +2474,7 @@ void SV_MVDList_f (void) Sys_freedir(dir); } +//console command used to print to connected clients (we're acting as a dedicated server) void SV_UserCmdMVDList_f (void) { mvddest_t *d; @@ -2451,8 +2506,16 @@ void SV_UserCmdMVDList_f (void) SV_ClientPrintf(host_client, PRINT_HIGH, "*%d: %s %dk\n", i, list->name, d->totalsize/1024); } if (!d) - SV_ClientPrintf(host_client, PRINT_HIGH, "%d: %s %dk\n", i, list->name, list->size/1024); + { + if (host_client->fteprotocolextensions2 & PEXT_CSQC) //its a hack to use csqc this way, but oh well, but other clients don't want the gibberish. + SV_ClientPrintf(host_client, PRINT_HIGH, "%d: ^[%s\\type\\/download demos/%s^] %dk\n", i, list->name, list->name, list->size/1024); + else + SV_ClientPrintf(host_client, PRINT_HIGH, "%d: %s %dk\n", i, list->name, list->size/1024); + } } + + if (host_client->num_backbuf == MAX_BACK_BUFFERS) + SV_ClientPrintf(host_client, PRINT_HIGH, "*MORE*\n", i, list->name, list->size/1024); } for (d = demo.dest; d; d = d->nextdest) @@ -2470,7 +2533,14 @@ void SV_UserCmdMVDList_f (void) Sys_freedir(dir); } -char *SV_MVDNum(char *buffer, int bufferlen, int num) +const char *SV_MVDLastNum(unsigned int num) +{ + if (!num || num > DEMOLOG_LENGTH) + return NULL; + num = demolog.sequence - num; + return demolog.log[num % DEMOLOG_LENGTH].filename; +} +char *SV_MVDNum(char *buffer, int bufferlen, int num) //lame number->name lookup according to a list generated at an arbitrary time { file_t *list; dir_t *dir; @@ -2478,6 +2548,8 @@ char *SV_MVDNum(char *buffer, int bufferlen, int num) dir = Sys_listdir(sv_demoDir.string, ".mvd", SORT_BY_DATE); list = dir->files; + if (num < 0) + num = dir->numfiles + num; if (num > dir->numfiles || num <= 0) { Sys_freedir(dir); diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index d5aa590b..d4197d8c 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -1196,14 +1196,17 @@ void SV_MulticastCB(vec3_t origin, multicast_t to, int dimension_mask, void (*ca if (reliable) { - char msgbuf[1024]; + char msgbuf[8192]; sizebuf_t msg = {0}; msg.data = msgbuf; msg.maxsize = sizeof(msgbuf); msg.prim = client->datagram.prim; callback(client, &msg, ctx); - ClientReliableCheckBlock(client, msg.cursize); - ClientReliableWrite_SZ(client, msg.data, msg.cursize); + if (msg.cursize) + { + ClientReliableCheckBlock(client, msg.cursize); + ClientReliableWrite_SZ(client, msg.data, msg.cursize); + } } else callback(client, &client->datagram, ctx); @@ -2863,7 +2866,12 @@ void SV_UpdateToReliableMessages (void) continue; ClientReliableWrite_Begin(client, svc_updatefrags, 4); ClientReliableWrite_Byte(client, i); - ClientReliableWrite_Short(client, host_client->edict->v->frags); +#ifdef NQPROT + if (ISNQCLIENT(client) && host_client->spectator == 1) + ClientReliableWrite_Short(client, -999); + else +#endif + ClientReliableWrite_Short(client, host_client->edict->v->frags); } if (sv.mvdrecording) @@ -2926,7 +2934,12 @@ void SV_UpdateToReliableMessages (void) continue; ClientReliableWrite_Begin(client, svc_updatefrags, 4); ClientReliableWrite_Byte(client, i); - ClientReliableWrite_Short(client, curfrags); +#ifdef NQPROT + if (ISNQCLIENT(client) && host_client->spectator == 1) + ClientReliableWrite_Short(client, -999); + else +#endif + ClientReliableWrite_Short(client, curfrags); } if (sv.mvdrecording) @@ -3011,6 +3024,91 @@ void SV_UpdateToReliableMessages (void) //#endif +//a single userinfo value was changed. +static void SV_SendUserinfoChange(client_t *to, client_t *about, qboolean isbasic, const char *key, const char *newval) +{ + int playernum = about - svs.clients; + + if (ISQWCLIENT(to)) + { + if (isbasic || (to->fteprotocolextensions & PEXT_BIGUSERINFOS)) + { + ClientReliableWrite_Begin(to, svc_setinfo, 4+strlen(key)+strlen(newval)); + ClientReliableWrite_Byte(to, playernum); + ClientReliableWrite_String(to, key); + ClientReliableWrite_String(to, newval); + } + } +#ifdef NQPROT + else if (ISNQCLIENT(to)) + { + if (!strcmp(key, "*spectator")) + { //nq does not support spectators, mods tend to use frags=-999 or -99 instead. + //yes, this breaks things. + ClientReliableWrite_Begin(to, svc_updatefrags, 4); + ClientReliableWrite_Byte(to, playernum); + if (atoi(newval) == 1) + ClientReliableWrite_Short(to, -999); + else + ClientReliableWrite_Short(to, about->old_frags); //restore their true frag count + } + else if (!strcmp(key, "name")) + { + ClientReliableWrite_Begin(to, svc_updatename, 3+strlen(newval)); + ClientReliableWrite_Byte(to, playernum); + ClientReliableWrite_String(to, newval); + } + else if (!strcmp(key, "topcolor") || !strcmp(key, "bottomcolor")) + { //due to these being combined, nq players get double colour change notifications... + int tc = atoi(Info_ValueForKey(about->userinfo, "topcolor")); + int bc = atoi(Info_ValueForKey(about->userinfo, "bottomcolor")); + if (tc < 0 || tc > 13) + tc = 0; + if (bc < 0 || bc > 13) + bc = 0; + ClientReliableWrite_Begin(to, svc_updatecolors, 3); + ClientReliableWrite_Byte(to, playernum); + ClientReliableWrite_Byte(to, 16*tc + bc); + } + + if (to->fteprotocolextensions2 & PEXT2_PREDINFO) + { + char quotedkey[1024]; + char quotedval[8192]; + char *s = va("//ui %i %s %s\n", playernum, COM_QuotedString(key, quotedkey, sizeof(quotedkey), false), COM_QuotedString(newval, quotedval, sizeof(quotedval), false)); + ClientReliableWrite_Begin(to, svc_stufftext, 2+strlen(s)); + ClientReliableWrite_String(to, s); + } + } +#endif +} +void SV_BroadcastUserinfoChange(client_t *about, qboolean isbasic, const char *key, const char *newval) +{ + client_t *client; + int j; + for (j = 0; j < svs.allocated_client_slots; j++) + { + client = svs.clients+j; + if (client->state < cs_connected) + continue; // reliables go to all connected or spawned + if (client->controller) + continue; //splitscreen + + if (client->protocol == SCP_BAD) + continue; //botclient + + SV_SendUserinfoChange(client, about, isbasic, key, newval); + } + + if (sv.mvdrecording && (isbasic || (demo.recorder.fteprotocolextensions & PEXT_BIGUSERINFOS))) + { + sizebuf_t *msg = MVDWrite_Begin (dem_all, 0, strlen(key)+strlen(newval)+4); + MSG_WriteByte (msg, svc_setinfo); + MSG_WriteByte (msg, about - svs.clients); + MSG_WriteString (msg, key); + MSG_WriteString (msg, newval); + } +} /* ======================= diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 0a3ca036..525c06cf 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -2813,13 +2813,13 @@ qboolean SV_AllowDownload (const char *name) //block attempts to download logs if (!Q_strcasecmp("log", ext)) return !!allow_download_logs.value; - if (strncmp(name, "logs/", 5) == 0) + if (Q_strncasecmp(name, "logs/", 5) == 0) return !!allow_download_logs.value; - if (!strncmp(name, "package/", 8)) + if (!Q_strncasecmp(name, "package/", 8)) { //eg: package/id1/foobar.pk3 - if (!strcmp("pk4", ext) || !strcmp("pk3", ext) || !strcmp("pak", ext) || (!strncmp(name, "package/downloads/", 18) && !strcmp("zip", ext))) + if (!Q_strcasecmp("pk4", ext) || !Q_strcasecmp("pk3", ext) || !Q_strcasecmp("pak", ext) || (!Q_strncasecmp(name, "package/downloads/", 18) && !Q_strcasecmp("zip", ext))) { if (!allow_download_packages.ival) return false; @@ -2832,49 +2832,49 @@ qboolean SV_AllowDownload (const char *name) return false; } - if (strncmp(name, "maps/", 5) == 0) + if (Q_strncasecmp(name, "maps/", 5) == 0) return !!allow_download_maps.value; //skins? - if (strncmp(name, "skins/", 6) == 0) + if (Q_strncasecmp(name, "skins/", 6) == 0) return !!allow_download_skins.value; //models - if ((strncmp(name, "progs/", 6) == 0) || - (strncmp(name, "models/", 7) == 0)) + if ((Q_strncasecmp(name, "progs/", 6) == 0) || + (Q_strncasecmp(name, "models/", 7) == 0)) return !!allow_download_models.value; //sound - if (strncmp(name, "sound/", 6) == 0) + if (Q_strncasecmp(name, "sound/", 6) == 0) return !!allow_download_sounds.value; //particles - if (strncmp(name, "particles/", 6) == 0) + if (Q_strncasecmp(name, "particles/", 6) == 0) return !!allow_download_particles.value; //demos - if (strncmp(name, "demos/", 6) == 0) + if (Q_strncasecmp(name, "demos/", 6) == 0) return !!allow_download_demos.value; //textures - if (strncmp(name, "textures/", 9) == 0) + if (Q_strncasecmp(name, "textures/", 9) == 0) return !!allow_download_textures.value; - if (strncmp(name, "locs/", 5) == 0) + if (Q_strncasecmp(name, "locs/", 5) == 0) return !!allow_download_locs.value; //wads - if (strncmp(name, "wads/", 5) == 0) + if (Q_strncasecmp(name, "wads/", 5) == 0) return !!allow_download_wads.value; - if (!strchr(name, '/') && !strcmp("wad", ext)) + if (!strchr(name, '/') && !Q_strcasecmp("wad", ext)) return !!allow_download_wads.value; //configs - if (strncmp(name, "config/", 7) == 0) + if (Q_strncasecmp(name, "config/", 7) == 0) return !!allow_download_configs.value; - if (!strcmp("cfg", ext)) + if (!Q_strcasecmp("cfg", ext)) return !!allow_download_configs.value; //pak/pk3s. - if (!strchr(name, '/') && (!strcmp("pk4", ext) || !strcmp("pk3", ext) || !strcmp("pak", ext))) + if (!strchr(name, '/') && (!Q_strcasecmp("pk4", ext) || !Q_strcasecmp("pk3", ext) || !Q_strcasecmp("pak", ext))) { - if (strnicmp(name, "pak", 3)) //don't give out core pak/pk3 files. This matches q3 logic. + if (Q_strncasecmp(name, "pak", 3)) //don't give out core pak/pk3 files. This matches q3 logic. return !!allow_download_packages.value; else return !!allow_download_packages.value && !!allow_download_copyrighted.value; @@ -2892,7 +2892,7 @@ qboolean SV_AllowDownload (const char *name) return !!allow_download_other.value; } -static int SV_LocateDownload(char *name, flocation_t *loc, char **replacementname, qboolean redirectpaks) +static int SV_LocateDownload(const char *name, flocation_t *loc, char **replacementname, qboolean redirectpaks) { extern cvar_t allow_download_anymap, allow_download_pakcontents; extern cvar_t sv_demoDir; @@ -2903,16 +2903,8 @@ static int SV_LocateDownload(char *name, flocation_t *loc, char **replacementnam if (replacementname) *replacementname = NULL; - //mangle the name by making it lower case. - { - char *p; - - for (p = name; *p; p++) - *p = (char)tolower((unsigned char)*p); - } - //mvdsv demo downloading support demonum/ -> demos/XXXX (sets up the client paths) - if (!strncmp(name, "demonum/", 8)) + if (!Q_strncasecmp(name, "demonum/", 8)) { if (replacementname) { @@ -2936,13 +2928,13 @@ static int SV_LocateDownload(char *name, flocation_t *loc, char **replacementnam } //mvdsv demo downloading support. demos/ -> demodir (sets up the server paths) - if (!strncmp(name, "demos/", 6)) + if (Q_strncasecmp(name, "demos/", 6)) { Q_snprintfz(tmpname, sizeof(tmpname), "%s/%s", sv_demoDir.string, name+6); name = tmpname; } - if (!strncmp(name, "package/", 8)) + if (!Q_strncasecmp(name, "package/", 8)) { vfsfile_t *f = FS_OpenVFS(name+8, "rb", FS_ROOT); if (f) @@ -2978,11 +2970,11 @@ static int SV_LocateDownload(char *name, flocation_t *loc, char **replacementnam { char tryalt[MAX_QPATH]; char ext[8]; - if (strncmp(name, alternatives[alt][0], strlen(alternatives[alt][0]))) + if (Q_strncasecmp(name, alternatives[alt][0], strlen(alternatives[alt][0]))) { if (*alternatives[alt][2]) { - if (strcmp(COM_FileExtension(name, ext, sizeof(ext)), alternatives[alt][2]+1)) + if (Q_strcasecmp(COM_FileExtension(name, ext, sizeof(ext)), alternatives[alt][2]+1)) continue; memcpy(tryalt, alternatives[alt][1], strlen(alternatives[alt][1])); COM_StripExtension(name+strlen(alternatives[alt][0]), tryalt+strlen(alternatives[alt][1]), sizeof(tryalt)-strlen(alternatives[alt][1])); @@ -3011,7 +3003,7 @@ static int SV_LocateDownload(char *name, flocation_t *loc, char **replacementnam // special check for maps, if it came from a pak file, don't allow download if (protectedpak) { - if (!allow_download_anymap.value && !strncmp(name, "maps/", 5)) + if (!allow_download_anymap.value && !Q_strncasecmp(name, "maps/", 5)) { Sys_Printf ("%s denied download of %s - it is in a pak\n", host_client->name, name+8); return DLERR_PERMISSIONS; @@ -3103,6 +3095,64 @@ void SV_DownloadSize_f(void) } } +void SV_DemoDownload_f(void) +{ + int arg; + unsigned long num; + const char *name, *mvdname; + char mvdnamebuffer[MAX_QPATH]; + if (Cmd_Argc() < 2) + { + //fixme: help text, or possibly just display links to the last 10 demos? + return; + } + if (Cmd_Argc() == 2) + { + name = Cmd_Argv(1); + if (!strcmp(name, "\\") || !Q_strcasecmp(name, "stop") || !Q_strcasecmp(name, "cancel")) + { + //fte servers don't do download queues, as it is impossible to avoid race conditions with vanilla clients anyway. + return; + } + } + + for (arg = 1; arg < Cmd_Argc(); arg++) + { + name = Cmd_Argv(arg); + if (*name == '.') + { //just count the dots + for (num = 0, mvdname = name; *mvdname == '.'; mvdname++) + num++; + if (*mvdname) + { + SV_ClientPrintf (host_client, PRINT_HIGH, "invalid demo id %s\n", name); + continue; + } + mvdname = SV_MVDLastNum(num); + } + else + { + char *e; + num = strtoul(name, &e, 10); + if (!num || *e) + { + SV_ClientPrintf (host_client, PRINT_HIGH, "invalid demo id %s\n", name); + continue; + } + mvdname = SV_MVDNum(mvdnamebuffer, sizeof(mvdnamebuffer), num); + } + + if (!mvdname) + SV_ClientPrintf (host_client, PRINT_HIGH, "%s is an invalid MVD demonum.\n", name); + else + { + const char *s = va("download \"demos/%s\"\n", mvdname); + ClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(s)); + ClientReliableWrite_String (host_client, s); + } + } +} + /* ================== SV_BeginDownload_f @@ -3249,7 +3299,7 @@ void SV_BeginDownload_f(void) else #endif if (ISNQCLIENT(host_client)) - { + { //dp's download protocol SV_PrintToClient(host_client, PRINT_HIGH, error); ClientReliableWrite_Begin (host_client, svc_stufftext, 2+12); @@ -4051,7 +4101,7 @@ void SV_Msg_f (void) SV_ClientTPrintf (host_client, PRINT_HIGH, "new msg level set to %i\n", host_client->messagelevel); } -qboolean SV_UserInfoIsBasic(char *infoname) +qboolean SV_UserInfoIsBasic(const char *infoname) { int i; char *basicinfos[] = { @@ -4082,12 +4132,9 @@ Allow clients to change userinfo */ void SV_SetInfo_f (void) { - int i, j; + int i; char oldval[MAX_INFO_KEY]; char *key, *val; - qboolean basic; //infos that we send to any old qw client. - client_t *client; - if (Cmd_Argc() == 1) { @@ -4149,48 +4196,7 @@ void SV_SetInfo_f (void) i = host_client - svs.clients; val = Info_ValueForKey(host_client->userinfo, key); - basic = SV_UserInfoIsBasic(key); - - for (j = 0; j < svs.allocated_client_slots; j++) - { - client = svs.clients+j; - if (client->state < cs_connected) - continue; // reliables go to all connected or spawned - if (client->controller) - continue; //splitscreen - - if (client->protocol == SCP_BAD) - continue; //botclient - - if (ISQWCLIENT(client)) - { - if (basic || (client->fteprotocolextensions & PEXT_BIGUSERINFOS)) - { - ClientReliableWrite_Begin(client, svc_setinfo, 1+1+strlen(key)+1+strlen(val)+1); - ClientReliableWrite_Byte(client, i); - ClientReliableWrite_String(client, key); - ClientReliableWrite_String(client, val); - } - } - if (ISNQCLIENT(client)) - { - if (!strcmp(key, "name")) - { - ClientReliableWrite_Begin(client, svc_updatename, 1+1+strlen(val)+1); - ClientReliableWrite_Byte(client, i); - ClientReliableWrite_String(client, val); - } - } - } - - if (sv.mvdrecording && (basic || (demo.recorder.fteprotocolextensions & PEXT_BIGUSERINFOS))) - { - sizebuf_t *msg = MVDWrite_Begin (dem_all, 0, strlen(key)+strlen(val)+4); - MSG_WriteByte (msg, svc_setinfo); - MSG_WriteByte (msg, i); - MSG_WriteString (msg, key); - MSG_WriteString (msg, val); - } + SV_BroadcastUserinfoChange(host_client, SV_UserInfoIsBasic(key), key, val); } //doh't spam chat changes. they're not interesting, and just spammy. @@ -5824,9 +5830,12 @@ ucmd_t ucmds[] = {"serverinfo", SV_ShowServerinfo_f}, /*demo/download commands*/ - {"stopdownload", SV_StopDownload_f}, {"demolist", SV_UserCmdMVDList_f}, + {"dlist", SV_UserCmdMVDList_f}, //apparently people are too lazy to type. {"demoinfo", SV_MVDInfo_f}, + {"dl", SV_DemoDownload_f}, + + {"stopdownload", SV_StopDownload_f}, {"dlsize", SV_DownloadSize_f}, {"download", SV_BeginDownload_f}, {"nextdl", SV_NextDownload_f, true}, @@ -6437,7 +6446,7 @@ int SV_PMTypeForClient (client_t *cl, edict_t *ent) { //it prevents bugs from being visible in unsuspecting mods. if (cl && cl->spectator) { - if (cl->zquake_extensions & Z_EXT_PM_TYPE_NEW) + if ((cl->zquake_extensions & Z_EXT_PM_TYPE_NEW) || (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)) return PM_SPECTATOR; return PM_OLD_SPECTATOR; } @@ -6451,8 +6460,9 @@ int SV_PMTypeForClient (client_t *cl, edict_t *ent) { case MOVETYPE_NOCLIP: /*older/vanilla clients have a b0rked spectator mode that we don't want to break*/ - if (cl && cl->zquake_extensions & Z_EXT_PM_TYPE_NEW) - return PM_SPECTATOR; + if (cl) + if ((cl->zquake_extensions & Z_EXT_PM_TYPE_NEW) || (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)) + return PM_SPECTATOR; return PM_OLD_SPECTATOR; case MOVETYPE_WALLWALK: @@ -7891,7 +7901,7 @@ void SVNQ_ReadClientMove (usercmd_t *move) move->sidemove = MSG_ReadShort (); move->upmove = MSG_ReadShort (); - move->msec=bound(0, timesincelast*1000, 255); + move->msec=bound(0, timesincelast*1000, 250); frame->move_msecs = timesincelast*1000; // read buttons @@ -7932,8 +7942,8 @@ void SVNQ_ReadClientMove (usercmd_t *move) if (SV_CanTrack(host_client, i)) break; } - if (i == host_client->spec_track) - i = 0; + if (i >= host_client->spec_track) + i = 0; } host_client->spec_track = i; diff --git a/fteqtv/msg.c b/fteqtv/msg.c index 0e9dbc6d..c0202950 100644 --- a/fteqtv/msg.c +++ b/fteqtv/msg.c @@ -134,6 +134,20 @@ void WriteFloat(netmsg_t *b, float f) u.f = f; WriteLong(b, u.i); } +void WriteCoord(netmsg_t *b, float c, unsigned int pext) +{ + if (pext & PEXT_FLOATCOORDS) + WriteFloat(b, c); + else + WriteShort(b, c*8); +} +void WriteAngle(netmsg_t *b, float a, unsigned int pext) +{ + if (pext & PEXT_FLOATCOORDS) + WriteShort(b, (a/360.0)*0x10000); + else + WriteByte(b, (a/360.0)*0x100 + 0.49); //round better, to avoid rounding bias +} void WriteString2(netmsg_t *b, const char *str) { //no null terminator, convienience function. while(*str) diff --git a/fteqtv/parse.c b/fteqtv/parse.c index 25439088..343e43da 100644 --- a/fteqtv/parse.c +++ b/fteqtv/parse.c @@ -574,7 +574,7 @@ static int ParseList(sv_t *tv, netmsg_t *m, filename_t *list, int to, unsigned i return ReadByte(m); } -static void ParseEntityState(entity_state_t *es, netmsg_t *m) //for baselines/static entities +static void ParseEntityState(sv_t *tv, entity_state_t *es, netmsg_t *m) //for baselines/static entities { int i; @@ -584,8 +584,8 @@ static void ParseEntityState(entity_state_t *es, netmsg_t *m) //for baselines/st es->skinnum = ReadByte(m); for (i = 0; i < 3; i++) { - es->origin[i] = ReadShort(m); - es->angles[i] = ReadByte(m); + es->origin[i] = ReadCoord(m, tv->pext); + es->angles[i] = ReadAngle(m, tv->pext); } } static void ParseBaseline(sv_t *tv, netmsg_t *m, int to, unsigned int mask) @@ -597,7 +597,7 @@ static void ParseBaseline(sv_t *tv, netmsg_t *m, int to, unsigned int mask) ParseError(m); return; } - ParseEntityState(&tv->map.entity[entnum].baseline, m); + ParseEntityState(tv, &tv->map.entity[entnum].baseline, m); ConnectionData(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1); } @@ -642,7 +642,7 @@ void ParseSpawnStatic(sv_t *tv, netmsg_t *m, int to, unsigned int mask) Sys_Printf(tv->cluster, "Too many static entities\n"); } - ParseEntityState(&tv->map.spawnstatic[tv->map.spawnstatic_count], m); + ParseEntityState(tv, &tv->map.spawnstatic[tv->map.spawnstatic_count], m); tv->map.spawnstatic_count++; @@ -679,9 +679,9 @@ static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers) { flags = (unsigned short)ReadShort (m); - tv->map.players[num].current.origin[0] = ReadShort (m); - tv->map.players[num].current.origin[1] = ReadShort (m); - tv->map.players[num].current.origin[2] = ReadShort (m); + tv->map.players[num].current.origin[0] = ReadCoord (m, tv->pext); + tv->map.players[num].current.origin[1] = ReadCoord (m, tv->pext); + tv->map.players[num].current.origin[2] = ReadCoord (m, tv->pext); tv->map.players[num].current.frame = ReadByte(m); @@ -754,7 +754,7 @@ static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers) for (i = 0; i < 3; i++) { if (flags & (DF_ORIGIN << i)) - tv->map.players[num].current.origin[i] = ReadShort (m); + tv->map.players[num].current.origin[i] = ReadCoord (m, tv->pext); } for (i = 0; i < 3; i++) @@ -782,9 +782,9 @@ static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers) } tv->map.players[num].leafcount = BSP_SphereLeafNums(tv->map.bsp, MAX_ENTITY_LEAFS, tv->map.players[num].leafs, - tv->map.players[num].current.origin[0]/8.0f, - tv->map.players[num].current.origin[1]/8.0f, - tv->map.players[num].current.origin[2]/8.0f, 32); + tv->map.players[num].current.origin[0], + tv->map.players[num].current.origin[1], + tv->map.players[num].current.origin[2], 32); } static int readentitynum(netmsg_t *m, unsigned int *retflags) @@ -837,26 +837,26 @@ static void ParseEntityDelta(sv_t *tv, netmsg_t *m, entity_state_t *old, entity_ new->effects = ReadByte(m); if (flags & U_ORIGIN1) - new->origin[0] = ReadShort(m); + new->origin[0] = ReadCoord(m, tv->pext); if (flags & U_ANGLE1) - new->angles[0] = ReadByte(m); + new->angles[0] = ReadAngle(m, tv->pext); if (flags & U_ORIGIN2) - new->origin[1] = ReadShort(m); + new->origin[1] = ReadCoord(m, tv->pext); if (flags & U_ANGLE2) - new->angles[1] = ReadByte(m); + new->angles[1] = ReadAngle(m, tv->pext); if (flags & U_ORIGIN3) - new->origin[2] = ReadShort(m); + new->origin[2] = ReadCoord(m, tv->pext); if (flags & U_ANGLE3) - new->angles[2] = ReadByte(m); + new->angles[2] = ReadAngle(m, tv->pext); if (forcerelink || (flags & (U_ORIGIN1|U_ORIGIN2|U_ORIGIN3|U_MODEL))) { ent->leafcount = BSP_SphereLeafNums(tv->map.bsp, MAX_ENTITY_LEAFS, ent->leafs, - new->origin[0]/8.0f, - new->origin[1]/8.0f, - new->origin[2]/8.0f, 32); + new->origin[0], + new->origin[1], + new->origin[2], 32); } } @@ -1098,9 +1098,9 @@ return; if ((flags & (U_ORIGIN1 | U_ORIGIN2 | U_ORIGIN3)) || forcerelink) tv->entity[entnum].leafcount = BSP_SphereLeafNums(tv->bsp, MAX_ENTITY_LEAFS, tv->entity[entnum].leafs, - tv->entity[entnum].current.origin[0]/8.0f, - tv->entity[entnum].current.origin[1]/8.0f, - tv->entity[entnum].current.origin[2]/8.0f, 32); + tv->entity[entnum].current.origin[0], + tv->entity[entnum].current.origin[1], + tv->entity[entnum].current.origin[2], 32); } */ } @@ -1252,12 +1252,12 @@ static void ParseSound(sv_t *tv, netmsg_t *m, int to, unsigned int mask) channel = (unsigned short)ReadShort(m); - if (channel & SND_VOLUME) + if (channel & SND_VOLUME) vol = ReadByte (m); else vol = DEFAULT_SOUND_PACKET_VOLUME; - if (channel & SND_ATTENUATION) + if (channel & SND_ATTENUATION) atten = ReadByte (m) / 64.0; else atten = DEFAULT_SOUND_PACKET_ATTENUATION; @@ -1268,7 +1268,7 @@ static void ParseSound(sv_t *tv, netmsg_t *m, int to, unsigned int mask) channel &= 7; for (i=0 ; i<3 ; i++) - org[i] = ReadShort (m); + org[i] = ReadCoord (m, tv->pext); Multicast(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, QW); @@ -1307,9 +1307,9 @@ static void ParseDamage(sv_t *tv, netmsg_t *m, int to, unsigned int mask) { ReadByte (m); ReadByte (m); - ReadShort (m); - ReadShort (m); - ReadShort (m); + ReadCoord (m, tv->pext); + ReadCoord (m, tv->pext); + ReadCoord (m, tv->pext); Multicast(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, QW); } @@ -1621,9 +1621,9 @@ void ParseMessage(sv_t *tv, void *buffer, int length, int to, int mask) case svc_setangle: if (!tv->usequakeworldprotocols) ReadByte(&buf); - tv->proxyplayerangles[0] = ReadByte(&buf)*360.0/255; - tv->proxyplayerangles[1] = ReadByte(&buf)*360.0/255; - tv->proxyplayerangles[2] = ReadByte(&buf)*360.0/255; + tv->proxyplayerangles[0] = ReadAngle(&buf, tv->pext); + tv->proxyplayerangles[1] = ReadAngle(&buf, tv->pext); + tv->proxyplayerangles[2] = ReadAngle(&buf, tv->pext); if (tv->usequakeworldprotocols && tv->controller) SendBufferToViewer(tv->controller, (char*)buf.data+buf.startpos, buf.readpos - buf.startpos, true); @@ -1657,9 +1657,9 @@ void ParseMessage(sv_t *tv, void *buffer, int length, int to, int mask) //#define svc_updatecolors 17 // [qbyte] [qbyte] [qbyte] case svc_particle: - ReadShort(&buf); - ReadShort(&buf); - ReadShort(&buf); + ReadCoord(&buf, tv->pext); + ReadCoord(&buf, tv->pext); + ReadCoord(&buf, tv->pext); ReadByte(&buf); ReadByte(&buf); ReadByte(&buf); diff --git a/fteqtv/qtv.h b/fteqtv/qtv.h index 9ada21f0..a75b5c1a 100644 --- a/fteqtv/qtv.h +++ b/fteqtv/qtv.h @@ -336,15 +336,15 @@ typedef struct { unsigned char modelindex; unsigned char colormap; unsigned char skinnum; - short origin[3]; - char angles[3]; + float origin[3]; + float angles[3]; unsigned char effects; } entity_state_t; typedef struct { unsigned char frame; unsigned char modelindex; unsigned char skinnum; - short origin[3]; + float origin[3]; short velocity[3]; short angles[3]; unsigned char effects; @@ -810,6 +810,8 @@ unsigned short ReadShort(netmsg_t *b); unsigned int ReadLong(netmsg_t *b); float ReadFloat(netmsg_t *b); void ReadString(netmsg_t *b, char *string, int maxlen); +float ReadCoord(netmsg_t *b, unsigned int pext); +float ReadAngle(netmsg_t *b, unsigned int pext); unsigned int SwapLong(unsigned int val); unsigned int BigLong(unsigned int val); @@ -837,6 +839,8 @@ void WriteByte(netmsg_t *b, unsigned char c); void WriteShort(netmsg_t *b, unsigned short l); void WriteLong(netmsg_t *b, unsigned int l); void WriteFloat(netmsg_t *b, float f); +void WriteCoord(netmsg_t *b, float c, unsigned int pext); +void WriteAngle(netmsg_t *b, float a, unsigned int pext); void WriteString2(netmsg_t *b, const char *str); void WriteString(netmsg_t *b, const char *str); void WriteData(netmsg_t *b, const void *data, int length); diff --git a/fteqtv/qw.c b/fteqtv/qw.c index 4b3ffa66..178889f0 100644 --- a/fteqtv/qw.c +++ b/fteqtv/qw.c @@ -467,7 +467,7 @@ int SendCurrentUserinfos(sv_t *tv, int cursize, netmsg_t *msg, int i, int thispl return i; } -void WriteEntityState(netmsg_t *msg, entity_state_t *es) +void WriteEntityState(netmsg_t *msg, entity_state_t *es, unsigned int pext) { int i; WriteByte(msg, es->modelindex); @@ -476,8 +476,8 @@ void WriteEntityState(netmsg_t *msg, entity_state_t *es) WriteByte(msg, es->skinnum); for (i = 0; i < 3; i++) { - WriteShort(msg, es->origin[i]); - WriteByte(msg, es->angles[i]); + WriteCoord(msg, es->origin[i], pext); + WriteAngle(msg, es->angles[i], pext); } } int SendCurrentBaselines(sv_t *tv, int cursize, netmsg_t *msg, int maxbuffersize, int i) @@ -497,7 +497,7 @@ int SendCurrentBaselines(sv_t *tv, int cursize, netmsg_t *msg, int maxbuffersize { WriteByte(msg, svc_spawnbaseline); WriteShort(msg, i); - WriteEntityState(msg, &tv->map.entity[i].baseline); + WriteEntityState(msg, &tv->map.entity[i].baseline, tv->pext); } } @@ -535,9 +535,9 @@ int SendStaticSounds(sv_t *tv, int cursize, netmsg_t *msg, int maxbuffersize, in continue; WriteByte(msg, svc_spawnstaticsound); - WriteShort(msg, tv->map.staticsound[i].origin[0]); - WriteShort(msg, tv->map.staticsound[i].origin[1]); - WriteShort(msg, tv->map.staticsound[i].origin[2]); + WriteCoord(msg, tv->map.staticsound[i].origin[0], tv->pext); + WriteCoord(msg, tv->map.staticsound[i].origin[1], tv->pext); + WriteCoord(msg, tv->map.staticsound[i].origin[2], tv->pext); WriteByte(msg, tv->map.staticsound[i].soundindex); WriteByte(msg, tv->map.staticsound[i].volume); WriteByte(msg, tv->map.staticsound[i].attenuation); @@ -560,7 +560,7 @@ int SendStaticEntities(sv_t *tv, int cursize, netmsg_t *msg, int maxbuffersize, continue; WriteByte(msg, svc_spawnstatic); - WriteEntityState(msg, &tv->map.spawnstatic[i]); + WriteEntityState(msg, &tv->map.spawnstatic[i], tv->pext); } return i; @@ -1248,7 +1248,7 @@ void ConnectionlessPacket(cluster_t *cluster, netadr_t *from, netmsg_t *m) } -void SV_WriteDelta(int entnum, const entity_state_t *from, const entity_state_t *to, netmsg_t *msg, qboolean force) +void SV_WriteDelta(int entnum, const entity_state_t *from, const entity_state_t *to, netmsg_t *msg, qboolean force, unsigned int pext) { unsigned int i; unsigned int bits; @@ -1311,17 +1311,17 @@ void SV_WriteDelta(int entnum, const entity_state_t *from, const entity_state_t if (bits & U_EFFECTS) WriteByte (msg, to->effects&0x00ff); if (bits & U_ORIGIN1) - WriteShort (msg, to->origin[0]); + WriteCoord(msg, to->origin[0], pext); if (bits & U_ANGLE1) - WriteByte(msg, to->angles[0]); + WriteAngle(msg, to->angles[0], pext); if (bits & U_ORIGIN2) - WriteShort (msg, to->origin[1]); + WriteCoord(msg, to->origin[1], pext); if (bits & U_ANGLE2) - WriteByte(msg, to->angles[1]); + WriteAngle(msg, to->angles[1], pext); if (bits & U_ORIGIN3) - WriteShort (msg, to->origin[2]); + WriteCoord(msg, to->origin[2], pext); if (bits & U_ANGLE3) - WriteByte(msg, to->angles[2]); + WriteAngle(msg, to->angles[2], pext); } const entity_state_t nullentstate = {0}; @@ -1365,7 +1365,7 @@ void SV_EmitPacketEntities (const sv_t *qtv, const viewer_t *v, const packet_ent if (newnum == oldnum) { // delta update from old position //Con_Printf ("delta %i\n", newnum); - SV_WriteDelta (newnum, &from->ents[oldindex], &to->ents[newindex], msg, false); + SV_WriteDelta (newnum, &from->ents[oldindex], &to->ents[newindex], msg, false, qtv->pext); oldindex++; newindex++; @@ -1376,7 +1376,7 @@ void SV_EmitPacketEntities (const sv_t *qtv, const viewer_t *v, const packet_ent { // this is a new entity, send it from the baseline baseline = &qtv->map.entity[newnum].baseline; //Con_Printf ("baseline %i\n", newnum); - SV_WriteDelta (newnum, baseline, &to->ents[newindex], msg, true); + SV_WriteDelta (newnum, baseline, &to->ents[newindex], msg, true, qtv->pext); newindex++; continue; @@ -1403,7 +1403,7 @@ void Prox_SendInitialEnts(sv_t *qtv, oproxy_t *prox, netmsg_t *msg) for (i = 0; i < frame->numents; i++) { entnum = frame->entnums[i]; - SV_WriteDelta(entnum, &qtv->map.entity[entnum].baseline, &frame->ents[i], msg, true); + SV_WriteDelta(entnum, &qtv->map.entity[entnum].baseline, &frame->ents[i], msg, true, qtv->pext); } WriteShort(msg, 0); } @@ -1448,9 +1448,9 @@ void SendLocalPlayerState(sv_t *tv, viewer_t *v, int playernum, netmsg_t *msg) flags |= (PF_VELOCITY1<map.players[tv->map.thisplayer].current.origin[0]); - WriteShort(msg, tv->map.players[tv->map.thisplayer].current.origin[1]); - WriteShort(msg, tv->map.players[tv->map.thisplayer].current.origin[2]); + WriteCoord(msg, tv->map.players[tv->map.thisplayer].current.origin[0], tv->pext); + WriteCoord(msg, tv->map.players[tv->map.thisplayer].current.origin[1], tv->pext); + WriteCoord(msg, tv->map.players[tv->map.thisplayer].current.origin[2], tv->pext); WriteByte(msg, tv->map.players[tv->map.thisplayer].current.frame); for (j=0 ; j<3 ; j++) @@ -1475,9 +1475,9 @@ void SendLocalPlayerState(sv_t *tv, viewer_t *v, int playernum, netmsg_t *msg) flags |= (PF_VELOCITY1<origin[0]*8); - WriteShort(msg, v->origin[1]*8); - WriteShort(msg, v->origin[2]*8); + WriteCoord(msg, v->origin[0], tv->pext); + WriteCoord(msg, v->origin[1], tv->pext); + WriteCoord(msg, v->origin[2], tv->pext); WriteByte(msg, 0); for (j=0 ; j<3 ; j++) @@ -1628,7 +1628,7 @@ void SendNQPlayerStates(cluster_t *cluster, sv_t *tv, viewer_t *v, netmsg_t *msg usercmd_t to; float lerp; int bits; - unsigned short org[3]; + float org[3]; entity_t *ent; playerinfo_t *pl; @@ -1712,17 +1712,17 @@ void SendNQPlayerStates(cluster_t *cluster, sv_t *tv, viewer_t *v, netmsg_t *msg if (bits & UNQ_EFFECTS) WriteByte (msg, 0); if (bits & UNQ_ORIGIN1) - WriteShort (msg, v->origin[0]*8); + WriteCoord (msg, v->origin[0], tv->pext); if (bits & UNQ_ANGLE1) - WriteByte(msg, -(v->ucmds[2].angles[0]>>8)); + WriteAngle(msg, -(360.0*v->ucmds[2].angles[0])/0x10000, tv->pext); if (bits & UNQ_ORIGIN2) - WriteShort (msg, v->origin[1]*8); + WriteCoord (msg, v->origin[1], tv->pext); if (bits & UNQ_ANGLE2) - WriteByte(msg, v->ucmds[2].angles[1]>>8); + WriteAngle(msg, (360.0*v->ucmds[2].angles[1])/0x10000, tv->pext); if (bits & UNQ_ORIGIN3) - WriteShort (msg, v->origin[2]*8); + WriteCoord (msg, v->origin[2], tv->pext); if (bits & UNQ_ANGLE3) - WriteByte(msg, v->ucmds[2].angles[2]>>8); + WriteAngle(msg, (360.0*v->ucmds[2].angles[2])/0x10000, tv->pext); continue; } @@ -1800,17 +1800,17 @@ void SendNQPlayerStates(cluster_t *cluster, sv_t *tv, viewer_t *v, netmsg_t *msg if (bits & UNQ_EFFECTS) WriteByte (msg, pl->current.effects); if (bits & UNQ_ORIGIN1) - WriteShort (msg, org[0]); + WriteCoord (msg, org[0], tv->pext); if (bits & UNQ_ANGLE1) - WriteByte(msg, -(pl->current.angles[0]>>8)); + WriteAngle(msg, -(360.0*pl->current.angles[0])/0x10000, tv->pext); if (bits & UNQ_ORIGIN2) - WriteShort (msg, org[1]); + WriteCoord (msg, org[1], tv->pext); if (bits & UNQ_ANGLE2) - WriteByte(msg, pl->current.angles[1]>>8); + WriteAngle(msg, (360.0*pl->current.angles[1])/0x10000, tv->pext); if (bits & UNQ_ORIGIN3) - WriteShort (msg, org[2]); + WriteCoord (msg, org[2], tv->pext); if (bits & UNQ_ANGLE3) - WriteByte(msg, pl->current.angles[2]>>8); + WriteAngle(msg, (360.0*pl->current.angles[2])/0x10000, tv->pext); } @@ -1928,9 +1928,9 @@ void SendNQPlayerStates(cluster_t *cluster, sv_t *tv, viewer_t *v, netmsg_t *msg WriteShort (msg,UNQ_MOREBITS|UNQ_MODEL|UNQ_ORIGIN1 | UNQ_ORIGIN2 | UNQ_ORIGIN3 | UNQ_SIGNAL); WriteByte (msg, v->thisplayer+1); WriteByte (msg, 2); //model - WriteShort (msg, v->origin[0]); - WriteShort (msg, v->origin[1]); - WriteShort (msg, v->origin[2]); + WriteCoord (msg, v->origin[0], tv->pext); + WriteCoord (msg, v->origin[1], tv->pext); + WriteCoord (msg, v->origin[2], tv->pext); } } @@ -1961,7 +1961,7 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) int i; usercmd_t to; unsigned short flags; - short interp; + float interp; float lerp; int track; @@ -2065,18 +2065,18 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg) (tv->map.players[i].current.origin[1] - tv->map.players[i].old.origin[1])*(tv->map.players[i].current.origin[1] - tv->map.players[i].old.origin[1]) > snapdist || (tv->map.players[i].current.origin[2] - tv->map.players[i].old.origin[2])*(tv->map.players[i].current.origin[2] - tv->map.players[i].old.origin[2]) > snapdist) { //teleported (or respawned), so don't interpolate - WriteShort(msg, tv->map.players[i].current.origin[0]); - WriteShort(msg, tv->map.players[i].current.origin[1]); - WriteShort(msg, tv->map.players[i].current.origin[2]); + WriteCoord(msg, tv->map.players[i].current.origin[0], tv->pext); + WriteCoord(msg, tv->map.players[i].current.origin[1], tv->pext); + WriteCoord(msg, tv->map.players[i].current.origin[2], tv->pext); } else { //send interpolated angles interp = (lerp)*tv->map.players[i].current.origin[0] + (1-lerp)*tv->map.players[i].old.origin[0]; - WriteShort(msg, interp); + WriteCoord(msg, interp, tv->pext); interp = (lerp)*tv->map.players[i].current.origin[1] + (1-lerp)*tv->map.players[i].old.origin[1]; - WriteShort(msg, interp); + WriteCoord(msg, interp, tv->pext); interp = (lerp)*tv->map.players[i].current.origin[2] + (1-lerp)*tv->map.players[i].old.origin[2]; - WriteShort(msg, interp); + WriteCoord(msg, interp, tv->pext); } WriteByte(msg, tv->map.players[i].current.frame); @@ -2329,13 +2329,13 @@ void PMove(viewer_t *v, usercmd_t *cmd) pmove_t pmove; if (v->server && v->server->controller == v) { - v->origin[0] = v->server->map.players[v->server->map.thisplayer].current.origin[0]/8.0f; - v->origin[1] = v->server->map.players[v->server->map.thisplayer].current.origin[1]/8.0f; - v->origin[2] = v->server->map.players[v->server->map.thisplayer].current.origin[2]/8.0f; + v->origin[0] = v->server->map.players[v->server->map.thisplayer].current.origin[0]; + v->origin[1] = v->server->map.players[v->server->map.thisplayer].current.origin[1]; + v->origin[2] = v->server->map.players[v->server->map.thisplayer].current.origin[2]; - v->velocity[0] = v->server->map.players[v->server->map.thisplayer].current.velocity[2]/8.0f; - v->velocity[1] = v->server->map.players[v->server->map.thisplayer].current.velocity[2]/8.0f; - v->velocity[2] = v->server->map.players[v->server->map.thisplayer].current.velocity[2]/8.0f; + v->velocity[0] = v->server->map.players[v->server->map.thisplayer].current.velocity[0]; + v->velocity[1] = v->server->map.players[v->server->map.thisplayer].current.velocity[1]; + v->velocity[2] = v->server->map.players[v->server->map.thisplayer].current.velocity[2]; return; } pmove.origin[0] = v->origin[0]; @@ -4230,6 +4230,7 @@ void QW_ProcessUDPPacket(cluster_t *cluster, netmsg_t *m, netadr_t from) int passwd = ReadLong(m); //fte extension, sent so that dual-protocol servers will not create connections for dual-protocol clients + //the nqconnect command disables this (as well as the qw hand shake) if you really want to use nq protocols with fte clients ReadString(m, tempbuffer, sizeof(tempbuffer)); if (!strncmp(tempbuffer, "getchallenge", 12)) break; diff --git a/plugins/ezhud/hud_common.c b/plugins/ezhud/hud_common.c index f3edad87..d9a476d3 100644 --- a/plugins/ezhud/hud_common.c +++ b/plugins/ezhud/hud_common.c @@ -937,7 +937,7 @@ static void SCR_NetStats(int x, int y, float period, vmnetinfo_t *netinfo) static float ping_dev; static float f_min, f_max, f_avg; static int lost_lost, lost_delta, lost_rate, lost_total; - static int size_all, size_in, size_out; + static int size_all, size_in, size_out, pps_in, pps_out; static int bandwidth_all, bandwidth_in, bandwidth_out; static int with_delta; @@ -982,6 +982,9 @@ static void SCR_NetStats(int x, int y, float period, vmnetinfo_t *netinfo) clamp(lost_delta, 0, 100); clamp(lost_total, 0, 100); + pps_in = netinfo->clrate.in_pps; + pps_out = netinfo->clrate.out_pps; + //per packet sizes size_in = (int)(netinfo->clrate.in_bps/netinfo->clrate.in_pps + 0.5); size_out = (int)(netinfo->clrate.out_bps/netinfo->clrate.out_pps + 0.5); @@ -1047,15 +1050,15 @@ static void SCR_NetStats(int x, int y, float period, vmnetinfo_t *netinfo) Draw_Alt_String(x+4, y, "packet size/BPS"); y+=12; - snprintf(line, sizeof (line), "out %3d %5d", size_out, bandwidth_out); + snprintf(line, sizeof (line), "out %3d %5d %d", size_out, bandwidth_out, pps_out); Draw_String(x, y, line); y+=8; - snprintf(line, sizeof (line), "in %3d %5d", size_in, bandwidth_in); + snprintf(line, sizeof (line), "in %3d %5d %3d", size_in, bandwidth_in, pps_in); Draw_String(x, y, line); y+=8; - snprintf(line, sizeof (line), "total %3d %5d", size_all, bandwidth_all); + snprintf(line, sizeof (line), "total %3d %5d %3d", size_all, bandwidth_all, pps_in+pps_out); Draw_String(x, y, line); y+=8; }