diff --git a/engine/Makefile b/engine/Makefile index 7eb07184d..cf48aaf33 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -919,7 +919,7 @@ ifdef windir M_LDFLAGS=$(MLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs` SV_LDFLAGS=`$(SDLCONFIG) --static-libs` else - GL_LDFLAGS=$(GLLDFLAGS) $(IMAGELDFLAGS) $(SDLCONFIG) --static-libs` + GL_LDFLAGS=$(GLLDFLAGS) $(IMAGELDFLAGS) `$(SDLCONFIG) --static-libs` VK_LDFLAGS=$(VKLDFLAGS) $(IMAGELDFLAGS) `$(SDLCONFIG) --static-libs` M_LDFLAGS=$(MLDFLAGS) $(IMAGELDFLAGS) `$(SDLCONFIG) --static-libs` SV_LDFLAGS=`$(SDLCONFIG) --static-libs` diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index d3711928d..e23e0f4eb 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -1007,7 +1007,7 @@ void CL_Stop_f (void) { if (!cls.demorecording) { -#ifndef CLIENTONLY +#if !defined(CLIENTONLY) && defined(MVD_RECORDING) SV_MVDStop_f(); #else Con_Printf ("Not recording a demo.\n"); @@ -1135,8 +1135,10 @@ void CL_WriteSetDemoMessage (void) record a single player game. */ #ifndef CLIENTONLY +#ifdef MVD_RECORDING mvddest_t *SV_MVD_InitRecordFile (char *name); qboolean SV_MVD_Record (mvddest_t *dest); +#endif void CL_RecordMap_f (void) { char demoname[MAX_QPATH]; @@ -1154,9 +1156,22 @@ void CL_RecordMap_f (void) SV_SpawnServer (mapname, NULL, false, false); +#ifdef MVD_RECORDING COM_DefaultExtension(demoname, ".mvd", sizeof(demoname)); +#else + COM_DefaultExtension(demoname, ".dem", sizeof(demoname)); +#endif COM_FileExtension(demoname, demoext, sizeof(demoext)); +#if defined(AVAIL_GZDEC) && !defined(CLIENTONLY) + { + extern cvar_t sv_demoAutoCompress; + if (sv_demoAutoCompress.ival) + Q_strncatz(demoname, ".gz", sizeof(demoname)); + } +#endif + +#ifdef MVD_RECORDING if (!strcmp(demoext, "mvd")) { if (!SV_MVD_Record (SV_MVD_InitRecordFile(demoname))) @@ -1165,6 +1180,7 @@ void CL_RecordMap_f (void) // Cbuf_AddText(va("mvdrecord %s\n", COM_QuotedString(demoname, buf, sizeof(buf))), RESTRICT_LOCAL); } else +#endif { cls.demooutfile = FS_OpenVFS (demoname, "wb", FS_GAME); if (!cls.demooutfile) @@ -1172,6 +1188,11 @@ void CL_RecordMap_f (void) CL_Disconnect_f(); return; } +#ifdef AVAIL_GZDEC + if (!Q_strcasecmp(".gz", COM_GetFileExtension(demoname, NULL))) + cls.demooutfile = FS_GZ_WriteFilter(cls.demooutfile, true, true); +#endif + #ifdef NQPROT if (!strcmp(demoext, "dem")) { @@ -1197,10 +1218,10 @@ static void CLQW_RecordServerData(sizebuf_t *buf, int *seq) // send the serverdata MSG_WriteByte (buf, svc_serverdata); #ifdef PROTOCOL_VERSION_FTE - if (cls.fteprotocolextensions) //maintain demo compatability + if (cls.fteprotocolextensions&~PEXT1_HIDEPROTOCOLS) //maintain demo compatability { MSG_WriteLong (buf, PROTOCOL_VERSION_FTE); - MSG_WriteLong (buf, cls.fteprotocolextensions); + MSG_WriteLong (buf, cls.fteprotocolextensions&~PEXT1_HIDEPROTOCOLS); } if (cls.fteprotocolextensions2) //maintain demo compatability { @@ -1308,6 +1329,7 @@ void CLNQ_WriteServerData(sizebuf_t *buf) //for demo recording unsigned int i; const char *val; + //This is for compat with DP. val = InfoBuf_ValueForKey(&cl.serverinfo, "*csprogs"); if (*val) { @@ -1328,10 +1350,10 @@ void CLNQ_WriteServerData(sizebuf_t *buf) //for demo recording } MSG_WriteByte(buf, svc_serverdata); - if (cls.fteprotocolextensions) + if (cls.fteprotocolextensions&~PEXT1_HIDEPROTOCOLS) { MSG_WriteLong (buf, PROTOCOL_VERSION_FTE); - MSG_WriteLong (buf, cls.fteprotocolextensions); + MSG_WriteLong (buf, cls.fteprotocolextensions&~PEXT1_HIDEPROTOCOLS); } if (cls.fteprotocolextensions2) { @@ -1365,6 +1387,8 @@ void CLNQ_WriteServerData(sizebuf_t *buf) //for demo recording if (protmain == PROTOCOL_VERSION_RMQ) MSG_WriteLong (buf, protfl); + if (cls.fteprotocolextensions2 & PEXT2_PREDINFO) + MSG_WriteString(buf, FS_GetGamedir(true)); MSG_WriteByte (buf, cl.allocated_client_slots); MSG_WriteByte (buf, cl.deathmatch?GAME_DEATHMATCH:GAME_COOP); MSG_WriteString (buf, cl.levelname); @@ -1586,6 +1610,123 @@ static int CL_Record_Lightstyles(sizebuf_t *buf, int seq) return seq; } +// send current status of all other players +static int CL_RecordInitialPlayers(sizebuf_t *buf, int seq, qboolean isnq) +{ + char info[MAX_LOCALINFO_STRING]; + player_info_t *player; + int i; + for (i = 0; i < cl.allocated_client_slots; i++) + { + player = cl.players + i; + + if (buf->cursize > buf->maxsize/2) + CL_WriteRecordDemoMessage (buf, seq++); + + if (player->frags != 0) + { + MSG_WriteByte (buf, svc_updatefrags); + MSG_WriteByte (buf, i); + MSG_WriteShort(buf, player->frags); + } + if (isnq) + { + if (!*player->name) + continue; + MSG_WriteByte (buf, svc_updatename); + MSG_WriteByte (buf, i); + MSG_WriteString (buf, player->name); + + MSG_WriteByte (buf, svc_updatecolors); + MSG_WriteByte (buf, i); + MSG_WriteByte (buf, player->rtopcolor*16+player->rbottomcolor); + } + else + { + if (player->ping != 0) + { + MSG_WriteByte (buf, svc_updateping); + MSG_WriteByte (buf, i); + MSG_WriteShort (buf, player->ping); + } + + if (player->pl != 0) + { + MSG_WriteByte (buf, svc_updatepl); + MSG_WriteByte (buf, i); + MSG_WriteByte (buf, player->pl); + } + + if (player->userinfo.numkeys) + { + MSG_WriteByte (buf, svc_updateentertime); + MSG_WriteByte (buf, i); + MSG_WriteFloat (buf, realtime - player->realentertime); //seconds since + } + + if (player->userinfo.numkeys) + { + InfoBuf_ToString(&player->userinfo, info, min(buf->maxsize-buf->cursize-6, sizeof(info)), basicuserinfos, NULL, NULL, NULL, NULL); + MSG_WriteByte (buf, svc_updateuserinfo); + MSG_WriteByte (buf, i); + MSG_WriteLong (buf, player->userid); + MSG_WriteString (buf, info); + + //spam svc_setinfo for all the infos that didn't fit. + } + } + } + return seq; +} +static int CL_RecordInitialStats(sizebuf_t *buf, int seq, qboolean isnq) +{ + int seat, i; + for (seat = 0; seat < cl.splitclients; seat++) + { + //higher stats should be 0 and thus not be sent, if not valid. + for (i = 0; i < MAX_CL_STATS; i++) + { + if (cl.playerview[seat].stats[i] || cl.playerview[seat].statsf[i]) + { + double fs = cl.playerview[seat].statsf[i]; + double is = cl.playerview[seat].stats[i]; + if (seat) + { + MSG_WriteByte (buf, svcfte_choosesplitclient); + MSG_WriteByte (buf, seat); + } + if ((int)fs == is) + { + MSG_WriteByte (buf, isnq?svcnq_updatestatlong:svcqw_updatestatlong); + MSG_WriteByte (buf, i); + MSG_WriteLong (buf, is); + } + else + { + MSG_WriteByte (buf, svcfte_updatestatfloat); + MSG_WriteByte (buf, i); + MSG_WriteLong (buf, fs); + } + } + if (cl.playerview[seat].statsstr[i]) + { + if (seat) + { + MSG_WriteByte (buf, svcfte_choosesplitclient); + MSG_WriteByte (buf, seat); + } + MSG_WriteByte (buf, svcfte_updatestatstring); + MSG_WriteByte (buf, i); + MSG_WriteString (buf, cl.playerview[seat].statsstr[i]); + } + + if (buf->cursize > buf->maxsize/2) + CL_WriteRecordDemoMessage (buf, seq++); + } + } + return seq; +} + const char *Get_Q2ConfigString(int i); /* @@ -1601,9 +1742,8 @@ void CL_Record_f (void) char name[MAX_OSPATH]; sizebuf_t buf; char buf_data[MAX_OVERALLMSGLEN]; - int n, i, seat; + int n, i; char *s, *p, *fname; - player_info_t *player; extern char gamedirfile[]; int seq = 1; const char *defaultext; @@ -1653,7 +1793,7 @@ void CL_Record_f (void) // They did. if ( s != NULL ) { if (!Q_strcasecmp(s, defaultext)) - *s = 0; //hack away that extension that they added. + *s = 0; //hack away that extension that they added, so that we don't get dupes. } } else @@ -1707,7 +1847,12 @@ void CL_Record_f (void) } // Make sure the filename doesn't contain illegal characters - for (p=fname ; *p ; p++) + p=fname; +#ifdef MVD_RECORDING + if (*sv_demoDir.string && !strncmp(p, sv_demoDir.string, strlen(sv_demoDir.string)) && p[strlen(sv_demoDir.string)] == '/') + p += strlen(sv_demoDir.string)+1; //allow a demos/ prefix (primarily because of autodemos) +#endif + for ( ; *p ; p++) { char c; *p &= 0x7F; // strip high bit @@ -1716,10 +1861,18 @@ void CL_Record_f (void) || c=='<' || c=='>' || c=='"' || c=='.') *p = '_'; } - Q_strncpyz(name, fname, sizeof(name)-8); + Q_strncpyz(name, fname, sizeof(name)-4-strlen(defaultext)); + +#if defined(AVAIL_GZDEC) && !defined(CLIENTONLY) && defined(MVD_RECORDING) + { + extern cvar_t sv_demoAutoCompress; + if (sv_demoAutoCompress.ival == 1 || !*sv_demoAutoCompress.string) + defaultext = va("%s.gz", defaultext); + } +#endif //make a unique name (unless the user specified it). - strcat (name, defaultext); //we have the space + Q_strncatz(name, defaultext, sizeof(name)); if (c != 2) { vfsfile_t *f; @@ -1728,7 +1881,7 @@ void CL_Record_f (void) if (f) { //remove the extension again - Q_strncpyz(name, fname, sizeof(name)-8); + Q_strncpyz(name, fname, sizeof(name)-4-strlen(defaultext)); p = name + strlen(name); strcat(p, "_XX"); strcat(p, defaultext); @@ -1737,8 +1890,8 @@ void CL_Record_f (void) do { VFS_CLOSE (f); - p[0] = i%100 + '0'; - p[1] = i%10 + '0'; + p[0] = ((i/10)%10) + '0'; + p[1] = (i%10) + '0'; f = FS_OpenVFS (name, "rb", FS_GAME); i++; } while (f && i < 100); @@ -1748,12 +1901,18 @@ void CL_Record_f (void) // // open the demo file // - cls.demooutfile = FS_OpenVFS (name, "wb", FS_GAME); + cls.demooutfile = FS_OpenVFS (name, "wb", FS_GAMEONLY); if (!cls.demooutfile) { Con_Printf ("ERROR: couldn't open.\n"); return; } + +#ifdef AVAIL_GZDEC + if (!Q_strcasecmp(".gz", COM_GetFileExtension(name, NULL))) + cls.demooutfile = FS_GZ_WriteFilter(cls.demooutfile, true, true); +#endif + cls.demohadkeyframe = false; Con_Printf ("recording to %s.\n", name); @@ -1903,100 +2062,9 @@ void CL_Record_f (void) if (buf.cursize) CL_WriteRecordDemoMessage (&buf, seq++); - - // send current status of all other players - - for (i = 0; i < cl.allocated_client_slots; i++) - { - player = cl.players + i; - - if (player->frags != 0) - { - MSG_WriteByte (&buf, svc_updatefrags); - MSG_WriteByte (&buf, i); - MSG_WriteShort (&buf, player->frags); - } - - if (player->ping != 0) - { - MSG_WriteByte (&buf, svc_updateping); - MSG_WriteByte (&buf, i); - MSG_WriteShort (&buf, player->ping); - } - - if (player->pl != 0) - { - MSG_WriteByte (&buf, svc_updatepl); - MSG_WriteByte (&buf, i); - MSG_WriteByte (&buf, player->pl); - } - - if (player->userinfo.numkeys) - { - MSG_WriteByte (&buf, svc_updateentertime); - MSG_WriteByte (&buf, i); - MSG_WriteFloat (&buf, realtime - player->realentertime); //seconds since - } - - if (player->userinfo.numkeys) - { - char info[MAX_LOCALINFO_STRING]; - InfoBuf_ToString(&player->userinfo, info, sizeof(info), basicuserinfos, NULL, NULL, NULL, NULL); - MSG_WriteByte (&buf, svc_updateuserinfo); - MSG_WriteByte (&buf, i); - MSG_WriteLong (&buf, player->userid); - MSG_WriteString (&buf, info); - } - - if (buf.cursize > buf.maxsize/2) - CL_WriteRecordDemoMessage (&buf, seq++); - } - + seq = CL_RecordInitialPlayers(&buf, seq, false); seq = CL_Record_Lightstyles(&buf, seq); - - for (seat = 0; seat < cl.splitclients; seat++) - { - //higher stats should be 0 and thus not be sent, if not valid. - for (i = 0; i < MAX_CL_STATS; i++) - { - if (cl.playerview[seat].stats[i] || cl.playerview[seat].statsf[i]) - { - double fs = cl.playerview[seat].statsf[i]; - double is = cl.playerview[seat].stats[i]; - if (seat) - { - MSG_WriteByte (&buf, svcfte_choosesplitclient); - MSG_WriteByte (&buf, seat); - } - if ((int)fs == is) - { - MSG_WriteByte (&buf, svcqw_updatestatlong); - MSG_WriteByte (&buf, i); - MSG_WriteLong (&buf, is); - } - else - { - MSG_WriteByte (&buf, svcfte_updatestatfloat); - MSG_WriteByte (&buf, i); - MSG_WriteLong (&buf, fs); - } - } - if (cl.playerview[seat].statsstr[i]) - { - if (seat) - { - MSG_WriteByte (&buf, svcfte_choosesplitclient); - MSG_WriteByte (&buf, seat); - } - MSG_WriteByte (&buf, svcfte_updatestatstring); - MSG_WriteByte (&buf, i); - MSG_WriteString (&buf, cl.playerview[seat].statsstr[i]); - } - - if (buf.cursize > buf.maxsize/2) - CL_WriteRecordDemoMessage (&buf, seq++); - } - } + seq = CL_RecordInitialStats(&buf, seq, false); // get the client to check and download skins // when that is completed, a begin command will be issued @@ -2075,9 +2143,9 @@ void CL_Record_f (void) MSG_WriteByte (&buf, svcnq_signonnum); MSG_WriteByte (&buf, 2); CL_WriteRecordDemoMessage (&buf, seq++); - //fixme: clients + seq = CL_RecordInitialPlayers(&buf, seq, true); seq = CL_Record_Lightstyles(&buf, seq); - //fixme: stats + seq = CL_RecordInitialStats(&buf, seq, true); MSG_WriteByte (&buf, svcnq_signonnum); MSG_WriteByte (&buf, 3); CL_WriteRecordDemoMessage (&buf, seq++); @@ -2089,6 +2157,10 @@ void CL_Record_f (void) CL_Stop_f(); break; } + + if (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) + if (cl.numackframes < sizeof(cl.ackframes)/sizeof(cl.ackframes[0])) + cl.ackframes[cl.numackframes++] = -1; } static int QDECL CompleteDemoList (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath) @@ -2102,7 +2174,11 @@ void CL_DemoList_c(int argn, const char *partial, struct xcommandargcompletioncb if (argn == 1) { COM_EnumerateFiles(va("%s*.qwd", partial), CompleteDemoList, ctx); + COM_EnumerateFiles(va("%s*.qwd.gz", partial), CompleteDemoList, ctx); +#ifdef NQPROT COM_EnumerateFiles(va("%s*.dem", partial), CompleteDemoList, ctx); + COM_EnumerateFiles(va("%s*.dem.gz", partial), CompleteDemoList, ctx); +#endif COM_EnumerateFiles(va("%s*.mvd", partial), CompleteDemoList, ctx); COM_EnumerateFiles(va("%s*.mvd.gz", partial), CompleteDemoList, ctx); @@ -2181,6 +2257,11 @@ void CL_ReRecord_f (void) return; } +#ifdef AVAIL_GZDEC + if (!Q_strcasecmp(".gz", COM_GetFileExtension(name, NULL))) + cls.demooutfile = FS_GZ_WriteFilter(cls.demooutfile, true, true); +#endif + Con_Printf ("recording to %s.\n", name); #ifdef NQPROT @@ -2497,7 +2578,7 @@ void CL_PlayDemo(char *demoname, qboolean usesystempath) Q_strncpyz (cls.lastdemoname, demoname, sizeof(cls.lastdemoname)); #ifdef AVAIL_GZDEC - if (strlen(name) >= 3 && !Q_strcasecmp(name + strlen(name) - 3, ".gz")) + if (!strcmp(COM_GetFileExtension(name,NULL), ".gz")) f = FS_DecompressGZip(f, NULL); #endif diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index ffbfbb7ac..b70719455 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -4002,6 +4002,7 @@ void CL_LinkPacketEntities (void) modelflags = model->flags; } +#ifndef NOLEGACY if (cl.model_precache_vwep[0] && state->modelindex2 < MAX_VWEP_MODELS) { if (state->modelindex == cl_playerindex && cl.model_precache_vwep[0]->loadstate == MLS_LOADED && @@ -4013,7 +4014,9 @@ void CL_LinkPacketEntities (void) else model2 = NULL; } - else if (state->modelindex2 && state->modelindex2 < MAX_PRECACHE_MODELS) + else +#endif + if (state->modelindex2 && state->modelindex2 < MAX_PRECACHE_MODELS) model2 = cl.model_precache[state->modelindex2]; else model2 = NULL; @@ -4972,7 +4975,9 @@ void CL_LinkPlayers (void) static int flickertime; static int flicker; float predictmsmult = 1000*cl_predict_players_frac.value; +#ifndef NOLEGACY int modelindex2; +#endif extern cvar_t cl_demospeed; int displayseq; @@ -5023,15 +5028,19 @@ void CL_LinkPlayers (void) continue; //the extra modelindex check is to stop lame mods from using vweps with rings +#ifndef NOLEGACY if (state->command.impulse && cl.model_precache_vwep[0] && cl.model_precache_vwep[0]->type != mod_dummy && state->modelindex == cl_playerindex) { model = cl.model_precache_vwep[0]; modelindex2 = state->command.impulse; } else +#endif { model = cl.model_precache[state->modelindex]; +#ifndef NOLEGACY modelindex2 = 0; +#endif } // spawn light flashes, even ones coming from invisible objects @@ -5221,8 +5230,10 @@ void CL_LinkPlayers (void) CL_AddFlagModels (ent, 0); else if (state->effects & QWEF_FLAG2) CL_AddFlagModels (ent, 1); +#ifndef NOLEGACY if (modelindex2) CL_AddVWeapModel (ent, cl.model_precache_vwep[modelindex2]); +#endif CLQ1_AddShadow(ent); CLQ1_AddPowerupShell(ent, false, state->effects); diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 83ab2486d..f6edb3db7 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -390,6 +390,17 @@ void CL_MakeActive(char *gamename) SCR_EndLoadingPlaque(); CL_UpdateWindowTitle(); +#ifdef MVD_RECORDING + if (sv_demoAutoRecord.ival && !sv.mvdrecording && !cls.demorecording && !cls.demoplayback && MVD_CheckSpace(false)) + { //don't auto-record if we're already recording... or playing a different demo. + extern cvar_t sv_demoAutoPrefix; + char timestamp[64]; + time_t tm = time(NULL); + strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", localtime(&tm)); + Cbuf_AddText(va("record %s%s%s%s_%s\n", sv_demoDir.string, *sv_demoDir.string?"/":"", sv_demoAutoPrefix.string, host_mapname.string, timestamp), RESTRICT_LOCAL); + } +#endif + TP_ExecTrigger("f_begin", true); if (cls.demoplayback) TP_ExecTrigger("f_spawndemo", true); @@ -1445,8 +1456,7 @@ void CL_Rcon_f (void) { char cryptpass[1024], crypttime[64]; const char *hex = "0123456789ABCDEF"; //must be upper-case for compat with mvdsv. - time_t clienttime; - time(&clienttime); + time_t clienttime = time(NULL); size_t digestsize; unsigned char digest[64]; const unsigned char **tokens = alloca(sizeof(*tokens)*(4+Cmd_Argc()*2)); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index a0a0a37f3..6cf1d50a7 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -829,6 +829,7 @@ void CL_DownloadFinished(qdownload_t *dl) break; } } +#ifndef NOLEGACY for (i = 0; i < MAX_VWEP_MODELS; i++) { if (!strcmp(cl.model_name_vwep[i], filename)) @@ -837,6 +838,7 @@ void CL_DownloadFinished(qdownload_t *dl) break; } } +#endif } S_ResetFailedLoad(); //okay, so this can still get a little spammy in bad places... @@ -1163,6 +1165,7 @@ static void Model_CheckDownloads (void) CL_CheckModelResources(s); } +#ifndef NOLEGACY for (i = 0; i < MAX_VWEP_MODELS; i++) { s = cl.model_name_vwep[i]; @@ -1176,6 +1179,7 @@ static void Model_CheckDownloads (void) CL_CheckOrEnqueDownloadFile(s, s, 0); CL_CheckModelResources(s); } +#endif } static int CL_LoadModels(int stage, qboolean dontactuallyload) @@ -1300,6 +1304,7 @@ static int CL_LoadModels(int stage, qboolean dontactuallyload) endstage(); } } +#ifndef NOLEGACY for (i = 0; i < MAX_VWEP_MODELS; i++) { if (!cl.model_name_vwep[i][0]) @@ -1317,6 +1322,7 @@ static int CL_LoadModels(int stage, qboolean dontactuallyload) endstage(); } } +#endif } @@ -1609,6 +1615,7 @@ void CL_RequestNextDownload (void) if (!cl.contentstage) { + int pure; stage = 0; stage = CL_LoadModels(stage, true); stage = CL_LoadSounds(stage, true); @@ -1616,8 +1623,12 @@ void CL_RequestNextDownload (void) cl.contentstage = 0; //might be safer to do it later, but kinder to do it before wasting time. - if (!FS_PureOkay()) - { + pure = FS_PureOkay(); + if (pure < 0 || (pure==0 && (cls.download || cl.downloadlist))) + return; //we're downloading something and may still be able to satisfy it. + if (pure == 0 && !cls.demoplayback) + { //failure! + Con_Printf(CON_ERROR"You are missing pure packages, and they could not be autodownloaded.\nYou may need to purchase an update.\n"); #ifdef HAVE_MEDIA_ENCODER if (cls.demoplayback && Media_Capturing()) { @@ -4108,8 +4119,10 @@ static void CL_ParseModellist (qboolean lots) cl_spikeindex = nummodels; if (!strcmp(cl.model_name[nummodels],"progs/player.mdl")) cl_playerindex = nummodels; +#ifndef NOLEGACY if (*cl.model_name_vwep[0] && !strcmp(cl.model_name[nummodels],cl.model_name_vwep[0]) && cl_playerindex == -1) cl_playerindex = nummodels; +#endif if (!strcmp(cl.model_name[nummodels],"progs/h_player.mdl")) cl_h_playerindex = nummodels; if (!strcmp(cl.model_name[nummodels],"progs/flag.mdl")) @@ -6303,6 +6316,7 @@ static void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds cl.serverpakschanged = true; CL_CheckServerPacks(); } +#ifndef NOLEGACY 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; @@ -6323,6 +6337,7 @@ static void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds } } } +#endif else if (cls.demoplayback && !strncmp(stufftext, "playdemo ", 9)) { //some demos (like speed-demos-archive's marathon runs) chain multiple demos with playdemo commands //these should still chain properly even when the demo is in some archive(like .dz) or subdir diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index a575fea41..53cb6e884 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -1077,22 +1077,33 @@ void CL_ParseTEnt (void) case TENQ_BEAM: type = TEQW_BEAM; break; - case TENQ_EXPLOSION_SPRITE: - type = TE_EXPLOSION; + case TENQ_QWEXPLOSION: + type = TEQW_QWEXPLOSION; break; - case TE_EXPLOSION: - type = TEQW_EXPLOSION_NOSPRITE; + case TENQ_NQEXPLOSION: + type = TEQW_NQEXPLOSION; break; - case TE_GUNSHOT: - type = TE_GUNSHOT_NQCOMPAT; + case TENQ_NQGUNSHOT: + type = TEQW_NQGUNSHOT; break; - case TE_GUNSHOT_NQCOMPAT: - type = TE_GUNSHOT; + case TENQ_QWGUNSHOT: + type = TEQW_QWGUNSHOT; break; + case TENQ_RAILTRAIL: + type = TEQW_RAILTRAIL; + break; + case TENQ_NEHLIGHTNING4: + type = TEQW_NEHLIGHTNING4; + break; +// case TENQ_NEHSMOKE: +// type = TEQW_NEHSMOKE; +// break; + default: break; } } + //else QW values //right, nq vs qw doesn't matter now, supposedly. @@ -1103,15 +1114,14 @@ void CL_ParseTEnt (void) "tarexplosion", "lightning1", "lightning2", "wizspike", "knightspike", "lightning3", "lavasplash", "teleport", "blood", "lightningblood", "bullet", "superbullet", //bullets deprecated - "railtrail", "beam", "explosion2", "nqexplosion", - "nqgunshot", "?", "?", "?", + "neh_explosion3", "railtrail/neh_lightning4", "beam", "explosion2", + "nqexplosion", "nqgunshot", "?", "?", #ifdef HEXEN2 "h2lightsml", "h2chain", "h2sunstf1", "h2sunstf2", "h2light", "h2cb", "h2ic", "h2gaze", "h2famine", "h2partexp" #endif }; - if (type < countof(te_names)) Con_Printf(" te_%s\n", te_names[type]); else @@ -1274,7 +1284,7 @@ void CL_ParseTEnt (void) S_StartSound (0, 0, cl_sfx_ric3, pos, NULL, 1, 1, 0, 0, 0); } break; - case TE_SUPERBULLET: + case TEQW_SUPERBULLET: pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); @@ -1342,8 +1352,8 @@ void CL_ParseTEnt (void) ex->endalpha = ex->startalpha; //don't fade out } break; - case TEQW_EXPLOSION_NOSPRITE: //nq-style, no sprite - case TE_EXPLOSION: //qw-style, with (optional) sprite + case TEQW_NQEXPLOSION: //nq-style, no sprite + case TEQW_QWEXPLOSION: //qw-style, with (optional) sprite // particles pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); @@ -1375,7 +1385,7 @@ void CL_ParseTEnt (void) S_StartSound (0, 0, cl_sfx_r_exp3, pos, NULL, 1, 1, 0, 0, 0); // sprite - if (type == TE_EXPLOSION && cl_expsprite.ival) // temp hopefully + if (type == TEQW_QWEXPLOSION && cl_expsprite.ival) // temp hopefully { explosion_t *ex = CL_AllocExplosion (pos); ex->start = cl.time; @@ -1411,6 +1421,7 @@ void CL_ParseTEnt (void) } break; + case TE_EXPLOSION3_NEH: case TEDP_EXPLOSIONRGB: pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); @@ -1420,6 +1431,18 @@ void CL_ParseTEnt (void) if (cl_legacystains.ival) Surf_AddStain(pos, -1, -1, -1, 100); + if (type == TEDP_EXPLOSIONRGB) + { + pos2[0] = MSG_ReadByte()/255.0; + pos2[1] = MSG_ReadByte()/255.0; + pos2[2] = MSG_ReadByte()/255.0; + } + else + { //TE_EXPLOSION3_NEH + pos2[0] = MSG_ReadCoord(); + pos2[1] = MSG_ReadCoord(); + pos2[2] = MSG_ReadCoord(); + } // light if (r_explosionlight.value) @@ -1430,9 +1453,9 @@ void CL_ParseTEnt (void) dl->die = cl.time + 0.5; dl->decay = 300; - dl->color[0] = 0.4f*MSG_ReadByte()/255.0f; - dl->color[1] = 0.4f*MSG_ReadByte()/255.0f; - dl->color[2] = 0.4f*MSG_ReadByte()/255.0f; + dl->color[0] = 0.4f*pos2[0]; + dl->color[1] = 0.4f*pos2[1]; + dl->color[2] = 0.4f*pos2[2]; dl->channelfade[0] = 0; dl->channelfade[1] = 0; dl->channelfade[2] = 0; @@ -1490,6 +1513,11 @@ void CL_ParseTEnt (void) case TE_LIGHTNING3: // lightning bolts CL_ParseBeam (BT_Q1LIGHTNING3); break; + case TEQW_NEHLIGHTNING4: + Con_DPrintf("TEQW_NEHLIGHTNING4 not implemented\n"); + MSG_ReadString(); + CL_ParseBeam (BT_Q1LIGHTNING2); + break; case TE_LAVASPLASH: pos[0] = MSG_ReadCoord (); @@ -1518,9 +1546,9 @@ void CL_ParseTEnt (void) break; - case TE_GUNSHOT: // bullet hitting wall - case TE_GUNSHOT_NQCOMPAT: - if (type == TE_GUNSHOT_NQCOMPAT) + case TEQW_QWGUNSHOT: // bullet hitting wall + case TEQW_NQGUNSHOT: + if (type == TEQW_NQGUNSHOT) cnt = 1; else cnt = MSG_ReadByte (); @@ -1535,7 +1563,7 @@ void CL_ParseTEnt (void) break; - case TEQW_BLOOD: // bullets hitting body + case TEQW_QWBLOOD: // bullets hitting body cnt = MSG_ReadByte (); pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); @@ -1565,7 +1593,7 @@ void CL_ParseTEnt (void) CL_ParseBeam (BT_Q1BEAM); break; - case TE_RAILTRAIL: + case TEQW_RAILTRAIL: pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); @@ -1836,6 +1864,11 @@ void CL_ParseTEnt (void) } break; +// case TEQW_NEHRAILTRAIL: +// case TEQW_NEHEXPLOSION3: +// case TEQW_NEHLIGHTNING4: +// case TEQW_NEHSMOKE: + default: Host_EndGame ("CL_ParseTEnt: bad type - %i", type); } @@ -2438,7 +2471,7 @@ void CL_SmokeAndFlash(vec3_t origin) ex = CL_AllocExplosion (origin); VectorClear(ex->angles); // ex->type = ex_flash; - ex->flags = Q2RF_FULLBRIGHT; + ex->flags = RF_FULLBRIGHT; ex->numframes = 2; ex->start = cl.time; ex->model = Mod_ForName (q2tentmodels[q2cl_mod_flash].modelname, MLV_WARN); @@ -2648,7 +2681,7 @@ void CLQ2_ParseTEnt (void) case CRTE_BLASTER_MUZZLEFLASH: MSG_ReadPos (pos); ex = CL_AllocExplosion (pos); - ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW; + ex->flags = RF_FULLBRIGHT|RF_NOSHADOW; ex->start = cl.q2frame.servertime - 100; CL_NewDlight(0, pos, 350, 0.5, 0.2*5, 0.1*5, 0*5); P_RunParticleEffectTypeString(pos, NULL, 1, "te_muzzleflash"); @@ -2656,7 +2689,7 @@ void CLQ2_ParseTEnt (void) case CRTE_BLUE_MUZZLEFLASH: MSG_ReadPos (pos); ex = CL_AllocExplosion (pos); - ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW; + ex->flags = RF_FULLBRIGHT|RF_NOSHADOW; ex->start = cl.q2frame.servertime - 100; CL_NewDlight(0, pos, 350, 0.5, 0.2*5, 0.1*5, 0*5); P_RunParticleEffectTypeString(pos, NULL, 1, "te_blue_muzzleflash"); @@ -2664,7 +2697,7 @@ void CLQ2_ParseTEnt (void) case CRTE_SMART_MUZZLEFLASH: MSG_ReadPos (pos); ex = CL_AllocExplosion (pos); - ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW; + ex->flags = RF_FULLBRIGHT|RF_NOSHADOW; ex->start = cl.q2frame.servertime - 100; CL_NewDlight(0, pos, 350, 0.5, 0.2*5, 0*5, 0.2*5); P_RunParticleEffectTypeString(pos, NULL, 1, "te_smart_muzzleflash"); @@ -2675,7 +2708,7 @@ void CLQ2_ParseTEnt (void) MSG_ReadPos (pos); ex = CL_AllocExplosion (pos); VectorCopy (pos, ex->origin); - ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW; + ex->flags = RF_FULLBRIGHT|RF_NOSHADOW; ex->start = cl.q2frame.servertime - 100; CL_NewDlight(0, pos, 350, 0.5, 0.2*5, 0*5, 0.2*5); P_RunParticleEffectTypeString(pos, NULL, 1, "te_deathfield"); diff --git a/engine/client/client.h b/engine/client/client.h index b90902bf8..f275d8c93 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -844,10 +844,17 @@ typedef struct // // information that is static for the entire time connected to a server // - char model_name_vwep[MAX_VWEP_MODELS][MAX_QPATH]; - char model_name[MAX_PRECACHE_MODELS][MAX_QPATH]; - char sound_name[MAX_PRECACHE_SOUNDS][MAX_QPATH]; - char *particle_ssname[MAX_SSPARTICLESPRE]; +#ifndef NOLEGACY + char model_name_vwep[MAX_VWEP_MODELS][MAX_QPATH]; + struct model_s *model_precache_vwep[MAX_VWEP_MODELS]; +#endif + char model_name[MAX_PRECACHE_MODELS][MAX_QPATH]; + struct model_s *model_precache[MAX_PRECACHE_MODELS]; + char sound_name[MAX_PRECACHE_SOUNDS][MAX_QPATH]; + struct sfx_s *sound_precache[MAX_PRECACHE_SOUNDS]; + char *particle_ssname[MAX_SSPARTICLESPRE]; + int particle_ssprecache[MAX_SSPARTICLESPRE]; //these are actually 1-based, so 0 can be used to lazy-init them. I cheat. + #ifdef Q2CLIENT char *configstring_general[Q2MAX_CLIENTS|Q2MAX_GENERAL]; char *image_name[Q2MAX_IMAGES]; @@ -855,11 +862,6 @@ typedef struct short inventory[MAX_SPLITS][Q2MAX_ITEMS]; #endif - struct model_s *model_precache_vwep[MAX_VWEP_MODELS]; - struct model_s *model_precache[MAX_PRECACHE_MODELS]; - struct sfx_s *sound_precache[MAX_PRECACHE_SOUNDS]; - int particle_ssprecache[MAX_SSPARTICLESPRE]; //these are actually 1-based, so 0 can be used to lazy-init them. I cheat. - char model_csqcname[MAX_CSMODELS][MAX_QPATH]; struct model_s *model_csqcprecache[MAX_CSMODELS]; char *particle_csname[MAX_CSPARTICLESPRE]; diff --git a/engine/client/console.c b/engine/client/console.c index eb7436356..d78867d28 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -2615,6 +2615,7 @@ void Con_DrawConsole (int lines, qboolean noback) { char *tiptext = NULL; shader_t *shader = NULL; + model_t *model = NULL; char *mouseover; if (!mouseconsole->mouseover || !mouseconsole->mouseover(mouseconsole, &tiptext, &shader)) { @@ -2662,13 +2663,20 @@ void Con_DrawConsole (int lines, qboolean noback) shader->height = 240; } } + key = Info_ValueForKey(info, "modelviewer"); + if (*key) + { + model = Mod_ForName(key, MLV_WARN); + if (model->loadstate != MLS_LOADED) + model = NULL; + } } tiptext = Info_ValueForKey(info, "tip"); } Z_Free(mouseover); } } - if ((tiptext && *tiptext) || shader) + if ((tiptext && *tiptext) || shader || model) { //FIXME: draw a proper background. //FIXME: support line breaks. @@ -2683,7 +2691,12 @@ void Con_DrawConsole (int lines, qboolean noback) lines = Font_LineBreaks(buffer, COM_ParseFunString(CON_WHITEMASK, tiptext, buffer, sizeof(buffer), false), (256.0 * vid.pixelwidth) / vid.width, countof(starts), starts, ends); th = (Font_CharHeight()*lines * vid.height) / vid.pixelheight; - if (shader) + if (model) + { + iw = 128; + ih = 128; + } + else if (shader) { int w, h; if (R_GetShaderSizes(shader, &w, &h, false) >= 0) @@ -2726,6 +2739,94 @@ void Con_DrawConsole (int lines, qboolean noback) } Font_EndString(font_console); + if (model) + { + playerview_t pv; + entity_t ent; + vec3_t fwd, rgt, up; + vec3_t lightpos = {0, 1, 0}; + + if (R2D_Flush) + R2D_Flush(); + + memset(&pv, 0, sizeof(pv)); + + CL_DecayLights (); + CL_ClearEntityLists(); + V_ClearRefdef(&pv); + r_refdef.drawsbar = false; + V_CalcRefdef(&pv); + + r_refdef.grect.width = iw; + r_refdef.grect.height = ih; + r_refdef.grect.x = x-8-iw; + r_refdef.grect.y = y+((th>ih)?(th-ih)/2:0); + r_refdef.time = realtime; + + r_refdef.flags = RDF_NOWORLDMODEL; + + r_refdef.afov = 60; + r_refdef.fov_x = 0; + r_refdef.fov_y = 0; + r_refdef.dirty |= RDFD_FOV; + + VectorClear(r_refdef.viewangles); + r_refdef.viewangles[0] = 20; + r_refdef.viewangles[1] = realtime * 90; + AngleVectors(r_refdef.viewangles, fwd, rgt, up); + VectorScale(fwd, -64, r_refdef.vieworg); + + memset(&ent, 0, sizeof(ent)); + ent.scale = 1; + // ent.angles[1] = realtime*45;//mods->yaw; + // ent.angles[0] = realtime*23.4;//mods->pitch; + + ent.angles[0]*=r_meshpitch.value; + AngleVectors(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]); + ent.angles[0]*=r_meshpitch.value; + VectorInverse(ent.axis[1]); + + ent.model = model; + if (!ent.model) + return; //panic! + ent.origin[2] -= (ent.model->maxs[2]-ent.model->mins[2]) * 0.5 + ent.model->mins[2]; + Vector4Set(ent.shaderRGBAf, 1, 1, 1, 1); + /*if (strstr(model->name, "player")) + { + ent.bottomcolour = genhsv(realtime*0.1 + 0, 1, 1); + ent.topcolour = genhsv(realtime*0.1 + 0.5, 1, 1); + } + else*/ + { + ent.topcolour = TOP_DEFAULT; + ent.bottomcolour = BOTTOM_DEFAULT; + } + // ent.fatness = sin(realtime)*5; + ent.playerindex = -1; + ent.skinnum = 0; + ent.shaderTime = 0;//realtime; + ent.framestate.g[FS_REG].lerpweight[0] = 1; +// ent.framestate.g[FS_REG].frame[0] = animationnum; + ent.framestate.g[FS_REG].frametime[0] = ent.framestate.g[FS_REG].frametime[1] = realtime; + ent.framestate.g[FS_REG].endbone = 0x7fffffff; +// ent.customskin = Mod_RegisterSkinFile(va("%s_0.skin", mods->modelname)); + + ent.light_avg[0] = ent.light_avg[1] = ent.light_avg[2] = 0.66; + ent.light_range[0] = ent.light_range[1] = ent.light_range[2] = 0.33; + + V_ApplyRefdef(); + + VectorNormalize(lightpos); + ent.light_dir[0] = DotProduct(lightpos, ent.axis[0]); + ent.light_dir[1] = DotProduct(lightpos, ent.axis[1]); + ent.light_dir[2] = DotProduct(lightpos, ent.axis[2]); + + ent.light_known = 2; + + V_AddEntity(&ent); + + R_RenderView(); + } if (shader) { if (th > ih) diff --git a/engine/client/m_options.c b/engine/client/m_options.c index c42d93556..b57b80027 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -229,6 +229,50 @@ void M_Menu_Options_f (void) NULL }; #endif +#if !defined(CLIENTONLY) && defined(MVD_RECORDING) + extern cvar_t sv_demoAutoRecord; + static const char *autorecordopts[] = { + "Off", + "Singleview", + "Multiview", + NULL + }; + static const char *autorecordvals[] = { + "0", + "-1", + "1", + NULL + }; + extern cvar_t cl_loopbackprotocol; + static const char *lprotopts[] = { + "Vanilla QW", + "FTE QW (recommended)", +#ifdef NQPROT + "FTE NQ", + "666", + "BJP3", +// "DP6", +// "DP7", + "Automatic (FTE NQ/QW)", + "Vanilla NQ", +#endif + NULL + }; + static const char *lprotvals[] = { + "qwid", + "qw", +#ifdef NQPROT + "nq", + "fitz", + "bjp3", +// "dp6", +// "dp7", + "auto", + "nqid", +#endif + NULL + }; +#endif menubulk_t bulk[] = { MB_CONSOLECMD("Customize controls", "menu_keys\n", "Modify keyboard and mouse inputs."), @@ -250,6 +294,10 @@ void M_Menu_Options_f (void) MB_CHECKBOXCVAR("Windowed Mouse", _windowed_mouse, 0), #if !defined(CLIENTONLY) && defined(SAVEDGAMES) MB_COMBOCVAR("Auto Save", sv_autosave, autosaveopts, autosavevals, NULL), +#endif +#if !defined(CLIENTONLY) && defined(MVD_RECORDING) + MB_COMBOCVAR("Auto Record", sv_demoAutoRecord, autorecordopts, autorecordvals, NULL), + MB_COMBOCVAR("Force Protocol", cl_loopbackprotocol, lprotopts, lprotvals, "Some protocols may impose additional limitations/breakages, and are listed only for potential demo-recording compat."), #endif MB_SPACING(4), // removed hud options (cl_sbar, cl_hudswap, old-style chat, old-style msg) @@ -904,7 +952,7 @@ const char *presetexec[] = "r_bloom 1;" "r_deluxemapping 0;" //won't be seen anyway "r_particledesc \"high tsshaft\";" - "r_waterstyle 3;" +// "r_waterstyle 3;" //too expensive. "r_glsl_offsetmapping 1;" "r_shadow_realtime_world 1;" "gl_texture_anisotropic_filtering 16;" diff --git a/engine/client/m_single.c b/engine/client/m_single.c index ba3c2faf4..881b81621 100644 --- a/engine/client/m_single.c +++ b/engine/client/m_single.c @@ -1009,6 +1009,31 @@ void M_Demo_Reselect(demomenu_t *info, const char *name) void M_Menu_Demos_f (void) { + char *demoexts[] = { + ".mvd", ".mvd.gz", + ".qwz", ".qwz.gz", +#ifdef NQPROT + ".dem", ".dem.gz", +#endif +#ifdef Q2CLIENT + ".dm2", ".dm2.gz" +#endif + //there are also qizmo demos (.qwz) out there... + //we don't support them, but if we were to ask quizmo to decode them for us, we could do. + }; + char *archiveexts[] = { +#ifdef PACKAGE_PK3 + ".zip", ".pk3", ".pk4", +#endif +#ifdef PACKAGE_Q1PAK + ".pak", +#endif +#ifdef PACKAGE_DZIP + ".dz", +#endif + NULL + }; + size_t u; demomenu_t *info; menu_t *menu; static demoloc_t mediareenterloc = {FS_GAME, "demos/"}; @@ -1038,36 +1063,20 @@ void M_Menu_Demos_f (void) } info->numext = 0; - info->command[info->numext] = "closemenu;playdemo"; - info->ext[info->numext++] = ".qwd"; - info->command[info->numext] = "closemenu;playdemo"; - info->ext[info->numext++] = ".dem"; - info->command[info->numext] = "closemenu;playdemo"; - info->ext[info->numext++] = ".dm2"; - info->command[info->numext] = "closemenu;playdemo"; - info->ext[info->numext++] = ".mvd"; - info->command[info->numext] = "closemenu;playdemo"; - info->ext[info->numext++] = ".mvd.gz"; - //there are also qizmo demos (.qwz) out there... - //we don't support them, but if we were to ask quizmo to decode them for us, we could do. + for (u = 0; u < countof(demoexts); u++) + { + info->command[info->numext] = "closemenu;playdemo"; + info->ext[info->numext++] = demoexts[u]; + } //and some archive formats... for the luls -#ifdef PACKAGE_PK3 - info->command[info->numext] = NULL; - info->ext[info->numext++] = ".zip"; - info->command[info->numext] = NULL; - info->ext[info->numext++] = ".pk3"; - info->command[info->numext] = NULL; - info->ext[info->numext++] = ".pk4"; -#endif -#ifdef PACKAGE_Q1PAK - info->command[info->numext] = NULL; - info->ext[info->numext++] = ".pak"; -#endif -#ifdef PACKAGE_DZIP - info->command[info->numext] = NULL; - info->ext[info->numext++] = ".dz"; -#endif + for (u = 0; u < countof(archiveexts); u++) + { + if (archiveexts[u]) + continue; + info->command[info->numext] = NULL; + info->ext[info->numext++] = archiveexts[u]; + } MC_AddWhiteText(menu, 24, 170, 8, "Choose a Demo", false); MC_AddWhiteText(menu, 16, 170, 24, "^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f", false); diff --git a/engine/client/merged.h b/engine/client/merged.h index f9b512111..bf4f41bcc 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -369,6 +369,16 @@ typedef struct texnums_s { texid_t fullbright; texid_t reflectcube; texid_t reflectmask; + + //the material's pushconstants. vulkan guarentees only 128 bytes. so 8 vec4s. note that lmscales should want 4 of them... + /*struct + { + vec4_t basefactors; + vec4_t specfactors; + vec4_t fullbrightfactors; + + //FIXME: envmap index, lightmap index, etc. + } factors;*/ } texnums_t; //not all modes accept meshes - STENCIL(intentional) and DEPTHONLY(not implemented) diff --git a/engine/client/p_script.c b/engine/client/p_script.c index 05659739b..b8d8b486a 100644 --- a/engine/client/p_script.c +++ b/engine/client/p_script.c @@ -1636,7 +1636,7 @@ parsefluid: else if (!Q_strncasecmp(e, "transparent", 11)) mod->rflags |= RF_TRANSLUCENT; //force blend else if (!Q_strncasecmp(e, "fullbright", 10)) - mod->rflags |= Q2RF_FULLBRIGHT; //fullbright, woo + mod->rflags |= RF_FULLBRIGHT; //fullbright, woo else if (!Q_strncasecmp(e, "shadow", 6)) mod->rflags &= ~RF_NOSHADOW; //clear noshadow else if (!Q_strncasecmp(e, "noshadow", 8)) @@ -2378,7 +2378,7 @@ qboolean PScript_Query(int typenum, int body, char *outstr, int outstrlen) Q_strncatz(outstr, " additive", outstrlen); if (ptype->models[i].rflags&RF_TRANSLUCENT) Q_strncatz(outstr, " transparent", outstrlen); - if (ptype->models[i].rflags&Q2RF_FULLBRIGHT) + if (ptype->models[i].rflags&RF_FULLBRIGHT) Q_strncatz(outstr, " fullbright", outstrlen); if (ptype->models[i].rflags&RF_NOSHADOW) Q_strncatz(outstr, " noshadow", outstrlen); diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 24361b2b6..4cfa34fd1 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -876,6 +876,8 @@ static qboolean CopyCSQCEdictToEntity(csqcedict_t *in, entity_t *out) } effects = in->v->effects; + if (effects & EF_FULLBRIGHT) + out->flags |= RF_FULLBRIGHT; if (effects & NQEF_ADDITIVE) out->flags |= RF_ADDITIVE; if (effects & EF_NOSHADOW) @@ -7022,7 +7024,7 @@ void *PDECL CSQC_PRLoadFile (const char *path, unsigned char *(PDECL *buf_get)(v //also kinda irrelevant with sv_pure. #ifndef FTE_TARGET_WEB if (file -#ifndef CLIENTONLY +#if !defined(CLIENTONLY) && defined(MVD_RECORDING) && !sv_demo_write_csqc.ival #endif ) diff --git a/engine/client/renderer.c b/engine/client/renderer.c index bf2c308bb..5b3845862 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -464,7 +464,7 @@ cvar_t gl_screenangle = CVAR("gl_screenangle", "0"); #endif #ifdef VKQUAKE -cvar_t vk_stagingbuffers = CVARD ("vk_stagingbuffers", "", "Configures which dynamic buffers are copied into gpu memory for rendering, instead of reading from shared memory. Empty for default settings.\nAccepted chars are u, e, v, 0."); +cvar_t vk_stagingbuffers = CVARFD ("vk_stagingbuffers", "", CVAR_RENDERERLATCH, "Configures which dynamic buffers are copied into gpu memory for rendering, instead of reading from shared memory. Empty for default settings.\nAccepted chars are u, e, v, 0."); cvar_t vk_submissionthread = CVARD ("vk_submissionthread", "", "Execute submits+presents on a thread dedicated to executing them. This may be a significant speedup on certain drivers."); cvar_t vk_debug = CVARFD("vk_debug", "0", CVAR_VIDEOLATCH, "Register a debug handler to display driver/layer messages. 2 enables the standard validation layers."); cvar_t vk_dualqueue = CVARFD("vk_dualqueue", "", CVAR_VIDEOLATCH, "Attempt to use a separate queue for presentation. Blank for default."); @@ -1731,6 +1731,7 @@ TRACE(("dbg: R_ApplyRenderer: reloading ALL models\n")); cl.model_precache[i] = Mod_FindName (Mod_FixName(cl.model_name[i], cl.model_name[1])); } +#ifndef NOLEGACY for (i=0; i < MAX_VWEP_MODELS; i++) { if (*cl.model_name_vwep[i]) @@ -1738,6 +1739,7 @@ TRACE(("dbg: R_ApplyRenderer: reloading ALL models\n")); else cl.model_precache_vwep[i] = NULL; } +#endif #ifdef CSQC_DAT for (i=1 ; iloadstate == MLS_LOADED) { void *t; @@ -1841,7 +1843,7 @@ void R_ReloadRenderer_f (void) R_ApplyRenderer_Load(NULL); Cvar_ApplyCallbacks(CVAR_RENDERERCALLBACK); -#ifndef CLIENTONLY +#if !defined(CLIENTONLY) && (defined(Q2BSPS) || defined(Q3BSPS)) if (portalblob) { if (sv.world.worldmodel && sv.world.worldmodel->loadstate == MLS_LOADED) @@ -2076,7 +2078,7 @@ void R_RestartRenderer (rendererstate_t *newr) return; } -#ifndef CLIENTONLY +#if !defined(CLIENTONLY) && (defined(Q2BSPS) || defined(Q3BSPS)) if (sv.state == ss_active && sv.world.worldmodel && sv.world.worldmodel->loadstate == MLS_LOADED) { void *t; @@ -2160,7 +2162,7 @@ void R_RestartRenderer (rendererstate_t *newr) } } -#ifndef CLIENTONLY +#if !defined(CLIENTONLY) && (defined(Q2BSPS) || defined(Q3BSPS)) if (portalblob) { if (sv.world.worldmodel && sv.world.worldmodel->loadstate == MLS_LOADED) diff --git a/engine/client/snd_al.c b/engine/client/snd_al.c index b0a984206..c5e3b22bb 100644 --- a/engine/client/snd_al.c +++ b/engine/client/snd_al.c @@ -972,9 +972,6 @@ static qboolean OpenAL_InitLibrary(void) firefoxstaticsounds = !!strstr(emscripten_run_script_string("navigator.userAgent"), "Firefox"); if (firefoxstaticsounds) Con_DPrintf("Firefox detected - disabling static sounds to avoid SORRY, I CAN'T HEAR YOU\n"); -#else - if (COM_CheckParm("-noopenal")) - return false; #endif #ifdef OPENAL_STATIC @@ -1019,6 +1016,9 @@ static qboolean OpenAL_InitLibrary(void) {NULL} }; + if (COM_CheckParm("-noopenal")) + return false; + if (!openallib_tried) { openallib_tried = true; diff --git a/engine/common/cmd.c b/engine/common/cmd.c index a73956369..cc722e974 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -3117,7 +3117,16 @@ static const char *If_Token_Term(const char *func, const char **end) level++; s2++; } - func = If_Token(s, end, IF_PRI_MAX); + if (!level) + { + char *t = malloc(s2-s+1); + memcpy(t, s, s2-s); + t[s2-s-((s2==s)?0:1)] = 0; + func = If_Token(t, end, IF_PRI_MAX); + free(t); + } + else + func = If_Token(s, end, IF_PRI_MAX); *end = s2; s = *end; s2 = func; diff --git a/engine/common/common.h b/engine/common/common.h index 3660b04d7..29e144700 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -612,7 +612,7 @@ void FS_UnloadPackFiles(void); void FS_ReloadPackFiles(void); char *FSQ3_GenerateClientPacksList(char *buffer, int maxlen, int basechecksum); void FS_PureMode(int mode, char *purenamelist, char *purecrclist, char *refnamelist, char *refcrclist, int seed); //implies an fs_restart. ref package names are optional, for q3 where pure names don't contain usable paths -qboolean FS_PureOkay(void); +int FS_PureOkay(void); //recursively tries to open files until it can get a zip. vfsfile_t *CL_OpenFileInPackage(searchpathfuncs_t *search, char *name); diff --git a/engine/common/config_fteqw.h b/engine/common/config_fteqw.h index 49ee46840..dd82a7fd3 100644 --- a/engine/common/config_fteqw.h +++ b/engine/common/config_fteqw.h @@ -112,6 +112,7 @@ //#define HLCLIENT 7 //we can run HL gamecode (not protocol compatible, set to 6 or 7) //#define HLSERVER 140 //we can run HL gamecode (not protocol compatible, set to 138 or 140) #define SAVEDGAMES //Can save the game. +#define MVD_RECORDING //server can record MVDs. // Networking options #define NQPROT //act as an nq client/server, with nq gamecode. diff --git a/engine/common/config_minimal.h b/engine/common/config_minimal.h index 727ce6efe..b3618679a 100644 --- a/engine/common/config_minimal.h +++ b/engine/common/config_minimal.h @@ -114,6 +114,7 @@ ////#define HLCLIENT 7 //we can run HL gamecode (not protocol compatible, set to 6 or 7) ////#define HLSERVER 140 //we can run HL gamecode (not protocol compatible, set to 138 or 140) //#define SAVEDGAMES //Can save the game. +//#define MVD_RECORDING //server can record MVDs. // Networking options //#define NQPROT //act as an nq client/server, with nq gamecode. diff --git a/engine/common/config_nocompat.h b/engine/common/config_nocompat.h index f8934eeab..bc31d97d5 100644 --- a/engine/common/config_nocompat.h +++ b/engine/common/config_nocompat.h @@ -111,6 +111,7 @@ //#define HLCLIENT 7 //we can run HL gamecode (not protocol compatible, set to 6 or 7) //#define HLSERVER 140 //we can run HL gamecode (not protocol compatible, set to 138 or 140) #define SAVEDGAMES //Can save the game. +#define MVD_RECORDING //server can record MVDs. // Networking options //#define NQPROT //act as an nq client/server, with nq gamecode. diff --git a/engine/common/config_wastes.h b/engine/common/config_wastes.h index fff798082..af03ca4a7 100644 --- a/engine/common/config_wastes.h +++ b/engine/common/config_wastes.h @@ -99,6 +99,7 @@ #undef PSKMODELS // What do we NOT want to use +#undef MVD_RECORDING //server can record MVDs. #undef D3D9QUAKE #undef D3D11QUAKE #undef D3D8QUAKE diff --git a/engine/common/fs.c b/engine/common/fs.c index 5d9da479e..3551c7507 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -3528,7 +3528,8 @@ void FS_PureMode(int puremode, char *purenamelist, char *purecrclist, char *refn } } -qboolean FS_PureOkay(void) +#ifndef SERVERONLY +int FS_PureOkay(void) { //returns true if all pure packages that we're meant to need could load. //if they couldn't then they won't override things, or the game will just be completely screwed due to having absolutely no game data @@ -3591,7 +3592,10 @@ qboolean FS_PureOkay(void) continue; else //if (!sp) { - Con_Printf("Pure package %s:%i missing\n", pname, crc); + if (!CL_CheckDLFile(va("package/%s", pname))) + if (CL_CheckOrEnqueDownloadFile(va("package/%s", pname), va("%s.%i", pname, crc), DLLF_NONGAME)) + return -1; + Con_Printf(CON_ERROR"Pure package %s:%i missing.\n", pname, crc); return false; } } @@ -3600,6 +3604,7 @@ qboolean FS_PureOkay(void) return true; } +#endif char *FSQ3_GenerateClientPacksList(char *buffer, int maxlen, int basechecksum) { //this is for q3 compatibility. diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index 210eba226..56828df6e 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -3307,7 +3307,9 @@ typedef struct ftenet_tcpconnect_stream_s { { qboolean connection_close; } httpstate; +#ifdef MVD_RECORDING qtvpendingstate_t qtvstate; +#endif struct { char resource[32]; @@ -3475,8 +3477,8 @@ qboolean FTENET_TCPConnect_HTTPResponse(ftenet_tcpconnect_stream_t *st, httparg_ char adr[256]; int i; const char *filetype = NULL; - char *resp = NULL; //response headers (no length/gap) - char *body = NULL; //response body + const char *resp = NULL; //response headers (no length/gap) + const char *body = NULL; //response body int method; if (!strcmp(arg[WCATTR_METHOD], "GET")) method = 0; @@ -3489,15 +3491,14 @@ qboolean FTENET_TCPConnect_HTTPResponse(ftenet_tcpconnect_stream_t *st, httparg_ body = NULL; } - //FIXME: demonum/ - st->dlfile = NULL; if (!resp && *arg[WCATTR_URL] == '/') { //'can't use SV_LocateDownload, as that assumes an active client. - char *name = arg[WCATTR_URL]+1; + const char *name = arg[WCATTR_URL]+1; char *extraheaders = ""; time_t modificationtime = 0; char *query = strchr(arg[WCATTR_URL]+1, '?'); + func_t func = 0; if (query) *query++ = 0; @@ -3506,33 +3507,35 @@ qboolean FTENET_TCPConnect_HTTPResponse(ftenet_tcpconnect_stream_t *st, httparg_ if (!*name) name = "index.html"; + if (sv.state && svs.gametype == GT_PROGS && svprogfuncs) + func = svprogfuncs->FindFunction(svprogfuncs, "HTTP_GeneratePage", PR_ANY); + + if (func) + { + void *pr_globals = PR_globals(svprogfuncs, PR_CURRENT); + ((string_t *)pr_globals)[OFS_PARM0] = svprogfuncs->TempString(svprogfuncs, query?va("%s?%s", name, query):name); + ((string_t *)pr_globals)[OFS_PARM1] = svprogfuncs->TempString(svprogfuncs, arg[WCATTR_METHOD]); + ((string_t *)pr_globals)[OFS_PARM2] = 0; //we don't support any postdata at this time. + ((string_t *)pr_globals)[OFS_PARM3] = 0; //we don't support any request headers at this time. + ((string_t *)pr_globals)[OFS_PARM4] = 0; //we don't have any default response headers yet. + ((string_t *)pr_globals)[OFS_PARM5] = 0; + ((string_t *)pr_globals)[OFS_PARM6] = 0; + ((string_t *)pr_globals)[OFS_PARM7] = 0; + svprogfuncs->ExecuteProgram(svprogfuncs, func); + + if (((string_t *)pr_globals)[OFS_RETURN]) + { //note that "" is not null + body = svprogfuncs->StringToNative(svprogfuncs, ((string_t *)pr_globals)[OFS_RETURN]); + resp = svprogfuncs->StringToNative(svprogfuncs, ((string_t *)pr_globals)[OFS_PARM4]); + resp = va("%s%s", *body?"HTTP/1.1 200 Ok\r\n":"HTTP/1.1 404 File Not Found\r\n", resp); + } + } + //FIXME: provide some resource->filename mapping that allows various misc files. - /*if (!strcmp(name, "live.html")) - { - resp = "HTTP/1.1 200 Ok\r\n" - "Content-Type: text/html\r\n"; - body = - "" - "" - "" - "
" - "" - "" - "" - "" - "Please install a plugin first.
" - "
" - "
" - "
" - "" - ; - } - else */ - if (!strcmp(name, "index.html")) + if (body) + ; + else if (!strcmp(name, "index.html")) { resp = "HTTP/1.1 200 Ok\r\n" "Content-Type: text/html\r\n"; @@ -3636,6 +3639,7 @@ qboolean FTENET_TCPConnect_HTTPResponse(ftenet_tcpconnect_stream_t *st, httparg_ "Content-Type: application/x-ftemanifest\r\n"; body = NULL; }*/ +#ifdef MVD_RECORDING else if (!Q_strncasecmp(name, "demolist", 8)) { filetype = "text/html"; @@ -3656,6 +3660,7 @@ qboolean FTENET_TCPConnect_HTTPResponse(ftenet_tcpconnect_stream_t *st, httparg_ body = NULL; } } +#endif else if (!SV_AllowDownload(name)) { Con_Printf("Denied download of %s to %s\n", arg[WCATTR_URL], NET_AdrToString (adr, sizeof(adr), &st->remoteaddr)); @@ -3680,9 +3685,10 @@ qboolean FTENET_TCPConnect_HTTPResponse(ftenet_tcpconnect_stream_t *st, httparg_ { flocation_t gzloc; flocation_t rawloc; - extern cvar_t sv_demoDir; +#ifdef MVD_RECORDING if (!Q_strncasecmp(name, "demos/", 6)) name = va("%s/%s", sv_demoDir.string, name+6); +#endif if (FS_FLocateFile(name, FSLF_IFFOUND, &rawloc)) { @@ -4484,6 +4490,7 @@ closesvstream: if (headerscomplete) { +#ifdef MVD_RECORDING //for QTV connections, we just need the method and a blank line. our qtv parser will parse the actual headers. if (!Q_strncasecmp(st->inbuffer, "QTV", 3)) { @@ -4503,6 +4510,7 @@ closesvstream: } } else +#endif { net_message.cursize = 0; if (!FTENET_TCP_ParseHTTPRequest(con, st)) diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 4182d1289..208921869 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -2929,9 +2929,18 @@ void QCBUILTIN PF_edict_for_num(pubprogfuncs_t *prinst, struct globalvars_s *pr_ unsigned int num = G_FLOAT(OFS_PARM0); if (num >= w->num_edicts) RETURN_EDICT(prinst, w->edicts); - + G_INT(OFS_RETURN) = num; +/* ent = (edict_t*)EDICT_NUM_PB(prinst, num); - RETURN_EDICT(prinst, ent); + if (!ent) + { + ent = ED_AllocIntoTable(progfuncs, num, false, prinst.fields_size); + ent->ereftype = ER_FREE; + if (externs->entspawn) + externs->entspawn((struct edict_s *) ent, true); + } + + RETURN_EDICT(prinst, ent);*/ } /* diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 04a83c46e..a15005cc0 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -38,12 +38,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define PEXT_ENTITYDBL2 0x00004000 //max of 1024 ents instead of 512 #define PEXT_FLOATCOORDS 0x00008000 //supports floating point origins. //#define PEXT_VWEAP 0x00010000 //cause an extra qbyte to be sent, and an extra list of models for vweaps. -#ifdef Q2BSPS -#define PEXT_Q2BSP 0x00020000 -#endif -#ifdef Q3BSPS -#define PEXT_Q3BSP 0x00040000 -#endif +#define PEXT_Q2BSP_ 0x00020000 +#define PEXT_Q3BSP_ 0x00040000 #define PEXT_COLOURMOD 0x00080000 //this replaces an older value which would rarly have caried any actual data. @@ -64,7 +60,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define PEXT_BIGUSERINFOS PEXT_CSQC //FIXME: while useful for csqc, we should include something else that isn't so often stripped, or is available in ezquake, or something. #else #define PEXT_BIGUSERINFOS 0xffffffff -#endif +#endif + +#ifdef Q2BSPS +#define PEXT_Q2BSP PEXT_Q2BSP_ +#endif +#ifdef Q3BSPS +#define PEXT_Q3BSP PEXT_Q3BSP_ +#endif +#define PEXT1_HIDEPROTOCOLS (PEXT_Q3BSP_|PEXT_Q2BSP_|PEXT_HLBSP) //These are hints for the server, and not useful to the client (they can figure stuff out themselves) #define PEXT2_PRYDONCURSOR 0x00000001 #define PEXT2_VOICECHAT 0x00000002 @@ -916,8 +920,10 @@ enum { enum { TE_SPIKE = 0, TE_SUPERSPIKE = 1, - TE_GUNSHOT = 2, //qw has count byte, nq does not - TE_EXPLOSION = 3, //remapped to TEQW_EXPLOSIONNOSPRITE for nq. + TEQW_QWGUNSHOT = 2, //qw has count byte, nq does not + TENQ_NQGUNSHOT = 2, //nq has no count byte + TEQW_QWEXPLOSION = 3, //remapped to TEQW_EXPLOSIONNOSPRITE for nq. + TENQ_NQEXPLOSION = 3, //remapped to TEQW_EXPLOSIONNOSPRITE for nq. TE_TAREXPLOSION = 4, TE_LIGHTNING1 = 5, TE_LIGHTNING2 = 6, @@ -927,25 +933,27 @@ enum { TE_LAVASPLASH = 10, TE_TELEPORT = 11, - TEQW_BLOOD = 12, //implemented as a particle() in nq + TEQW_QWBLOOD = 12, //implemented as a particle() in nq TENQ_EXPLOSION2 = 12, //remapped to TEQW_EXPLOSION2 for qw TEQW_LIGHTNINGBLOOD = 13, //implemented as a particle() in nq TENQ_BEAM = 13, //remapped to TEQW_BEAM for qw #ifdef PEXT_TE_BULLET TE_BULLET = 14, - TE_SUPERBULLET = 15, + TEQW_SUPERBULLET = 15, #endif - TENEH_RAILTRAIL = 15, //gah [vector] origin [coord] red [coord] green [coord] blue - TENEH_EXPLOSION3 = 16, //gah [vector] origin [coord] red [coord] green [coord] blue - TE_RAILTRAIL = 17, //use the builtin, luke. - TENEH_LIGHTNING4 = 17, //gah [string] model [entity] entity [vector] start [vector] end + TENQ_RAILTRAIL = 15, //gah [vector] origin [coord] red [coord] green [coord] blue + TE_EXPLOSION3_NEH = 16, //gah [vector] origin [coord] red [coord] green [coord] blue + TEQW_RAILTRAIL = 17, //use the builtin, luke. + TENQ_NEHLIGHTNING4 = 17, //gah [string] model [entity] entity [vector] start [vector] end + TEQW_NEHLIGHTNING4 = 1000, //give a real value if its ever properly implemented TEQW_BEAM = 18, //use the builtin, luke. - TENEH_SMOKE = 18, //gah [vector] origin [byte] palette + TENQ_NEHSMOKE = 18, //gah [vector] origin [byte] palette TEQW_EXPLOSION2 = 19, //use the builtin, luke. - TEQW_EXPLOSION_NOSPRITE = 20, //nq-style explosion over qw - TENQ_EXPLOSION_SPRITE = 20, //qw-style explosion over nq - TE_GUNSHOT_NQCOMPAT = 21, //nq has count byte, qw does not + TEQW_NQEXPLOSION = 20, //nq-style explosion over qw + TENQ_QWEXPLOSION = 20, //qw-style explosion over nq + TEQW_NQGUNSHOT = 21, //nq has count byte, qw does not + TENQ_QWGUNSHOT = 21, //nq has count byte, qw does not // hexen 2 TEH2_STREAM_LIGHTNING_SMALL = 24, @@ -959,7 +967,7 @@ enum { TEH2_STREAM_FAMINE = 32, TEH2_PARTICLEEXPLOSION = 33, - TEDP_BLOOD = 50, + TEDP_BLOOD = 50, // [coord*3] origin [byte*3] vel [byte] count TEDP_SPARK = 51, TEDP_BLOODSHOWER = 52, TEDP_EXPLOSIONRGB = 53, @@ -1298,7 +1306,7 @@ typedef struct q1usercmd_s #define Q2RF_MINLIGHT (1u<<0) //ni always have some light (viewmodel) #define RF_EXTERNALMODEL (1u<<1) //i don't draw through eyes, only mirrors #define RF_WEAPONMODEL (1u<<2) //i only draw through eyes -#define Q2RF_FULLBRIGHT (1u<<3) //i always draw full intensity +#define RF_FULLBRIGHT (1u<<3) //i always draw full intensity #define RF_DEPTHHACK (1u<<4) //i for view weapon Z crunching #define RF_TRANSLUCENT (1u<<5) //forces shader sort order and BEF_FORCETRANSPARENT #define Q2RF_FRAMELERP (1u<<6) //q2only diff --git a/engine/gl/gl_alias.c b/engine/gl/gl_alias.c index 3cc021adb..8ac16f8b3 100644 --- a/engine/gl/gl_alias.c +++ b/engine/gl/gl_alias.c @@ -676,7 +676,11 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e if (e->playerindex >= 0 && e->playerindex <= MAX_CLIENTS) { //heads don't get skinned, only players (and weaponless players), they do still get recoloured. - if (model==cl.model_precache[cl_playerindex] || model==cl.model_precache_vwep[0]) + if (model==cl.model_precache[cl_playerindex] +#ifndef NOLEGACY + || model==cl.model_precache_vwep[0] +#endif + ) { if (!cl.players[e->playerindex].qwskin) Skin_Find(&cl.players[e->playerindex]); @@ -1333,15 +1337,17 @@ qboolean R_CalcModelLighting(entity_t *e, model_t *clmodel) e->light_known = 2; return e->light_known-1; } + if ( #ifdef HEXEN2 - if ((e->drawflags & MLS_MASK) == MLS_FULLBRIGHT || (e->flags & Q2RF_FULLBRIGHT)) + (e->drawflags & MLS_MASK) == MLS_FULLBRIGHT || +#endif + (e->flags & RF_FULLBRIGHT)) { e->light_avg[0] = e->light_avg[1] = e->light_avg[2] = 1; e->light_range[0] = e->light_range[1] = e->light_range[2] = 0; e->light_known = 2; return e->light_known-1; } -#endif if (r_fb_models.ival == 1 && ruleset_allow_fbmodels.ival && (clmodel->engineflags & MDLF_EZQUAKEFBCHEAT) && cls.protocol == CP_QUAKEWORLD && cl.deathmatch) { e->light_avg[0] = e->light_avg[1] = e->light_avg[2] = 1; diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index a25ec1dff..894a28d53 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -7306,8 +7306,8 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) struct brushface_s faces[countof(planes)]; //patch info - brushtex_t *patch_tex; - int patch_w, patch_h; + brushtex_t *patch_tex=NULL; + int patch_w=0, patch_h=0; vec5_t patch_v[64][64]; #ifdef RUNTIMELIGHTING @@ -7344,7 +7344,7 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) brush.patch = NULL; Terr_Brush_Insert(submod, subhm, &brush); } - else + else if (patch_tex) Terr_Patch_Insert(submod, subhm, patch_tex, patch_w, patch_h, patch_v[0], countof(patch_v[0])); subhm->brushesedited = oe; } diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index aaa547d17..8b6af8f70 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -2505,6 +2505,10 @@ static shaderkey_t shaderkeys[] = {"progblendfunc", Shader_ProgBlendFunc, "fte"}, //specifies the blend mode (actually just overrides the first subpasses' blendmode. {"progmap", Shader_ProgMap, "fte"}, //avoids needing extra subpasses (actually just inserts an extra pass). + {"basefactor", NULL, "fte"}, //material scalers for glsl + {"specularfactor", NULL, "fte"}, //material scalers for glsl + {"fullbrightfactor", NULL, "fte"}, //material scalers for glsl + //dp compat {"reflectcube", Shader_ReflectCube, "dp"}, {"camera", Shader_DP_Camera, "dp"}, @@ -5847,11 +5851,10 @@ void Shader_DefaultBSPLM(const char *shortname, shader_t *s, const void *args) if (!builtin && r_lightmap.ival) builtin = ( "{\n" - "fte_program drawflat_wall\n" + "fte_program drawflat_wall#LM\n" "{\n" "map $lightmap\n" "tcgen lightmap\n" - "rgbgen srgb 255 255 255\n" "}\n" "}\n" ); diff --git a/engine/gl/r_bishaders.h b/engine/gl/r_bishaders.h index ab261f575..e744eec95 100644 --- a/engine/gl/r_bishaders.h +++ b/engine/gl/r_bishaders.h @@ -7990,7 +7990,11 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "uniform vec4 e_lmscale;\n" "void main ()\n" "{\n" +"#ifdef LM\n" +"col = vec4(1.0);\n" +"#else\n" "col = vec4(e_lmscale.rgb * ((v_normal.z < 0.73)?r_wallcolor:r_floorcolor), e_lmscale.a);\n" +"#endif\n" "lm = v_lmcoord;\n" "gl_Position = ftetransform();\n" "}\n" @@ -9805,6 +9809,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND {QR_OPENGL, 110, "postproc_ascii", "!!cvardf r_glsl_ascii_mono=0\n" "!!samps 1\n" + //derived from https://www.shadertoy.com/view/lssGDj "#include \"sys/defs.h\"\n" @@ -9838,7 +9843,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "float gray = 0.3 * col.r + 0.59 * col.g + 0.11 * col.b;\n" -"if (r_glsl_ascii_mono != 0)\n" +"if (r_glsl_ascii_mono != 0.0)\n" "gray = gray = pow(gray, 0.7); //quake is just too dark otherwise.\n" "else\n" "gray = gray = pow(gray, 0.45); //col*char is FAR too dark otherwise, and much of the colour will come from the col term anyway.\n" @@ -9854,7 +9859,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "if (gray > 0.8) n = 11512810.0; // #\n" "vec2 p = mod(uv/4.0, 2.0) - vec2(1.0);\n" -"if (r_glsl_ascii_mono != 0)\n" +"if (r_glsl_ascii_mono != 0.0)\n" "col = vec3(character(n, p));\n" "else\n" "col = col*character(n, p); //note that this is kinda cheating.\n" diff --git a/engine/http/httpclient.c b/engine/http/httpclient.c index daa90f1d0..ef76649b2 100644 --- a/engine/http/httpclient.c +++ b/engine/http/httpclient.c @@ -724,7 +724,7 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) nl = strchr(msg, '\n'); if (nl) *nl = '\0'; - Con_Printf("HTTP: %s %s (%s)\n", buffer, COM_TrimString(msg, trimmed, sizeof(trimmed)), Location); + Con_Printf("%s: %s %s (%s)\n", dl->url, buffer, COM_TrimString(msg, trimmed, sizeof(trimmed)), Location); if (!*Location) Con_Printf("Server redirected to null location\n"); else @@ -763,7 +763,7 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) if (nl>msg&&nl[-1] == '\r') nl--; *nl = '\0'; - Con_Printf("HTTP: %s%s\n", buffer, msg); + Con_Printf("%s: %s%s\n", dl->url, buffer, msg); return false; //something went wrong. } diff --git a/engine/qclib/packager.c b/engine/qclib/packager.c index 82da85ee1..9a4da7ecd 100644 --- a/engine/qclib/packager.c +++ b/engine/qclib/packager.c @@ -1,4 +1,5 @@ #include "qcc.h" +#if !defined(MINIMAL) && !defined(OMIT_QCC) #include void QCC_Canonicalize(char *fullname, size_t fullnamesize, const char *newfile, const char *base); @@ -1635,3 +1636,4 @@ void Packager_ParseFile(struct pkgctx_s *ctx, char *scriptname) void Packager_Destroy(struct pkgctx_s *ctx) { } +#endif diff --git a/engine/qclib/pr_edict.c b/engine/qclib/pr_edict.c index cbd9e4fa1..8e519080c 100644 --- a/engine/qclib/pr_edict.c +++ b/engine/qclib/pr_edict.c @@ -1247,12 +1247,11 @@ pbool PDECL ED_ParseEval (pubprogfuncs_t *ppf, eval_t *eval, int type, const cha pbool ED_ParseEpair (progfuncs_t *progfuncs, size_t qcptr, unsigned int fldofs, int fldtype, char *s) { int i; - char string[128]; fdef_t *def; - char *v, *w; string_t st; mfunction_t *func; int type = fldtype & ~DEF_SAVEGLOBAL; + double d; qcptr += fldofs*sizeof(int); switch (type) @@ -1267,37 +1266,40 @@ pbool ED_ParseEpair (progfuncs_t *progfuncs, size_t qcptr, unsigned int fldofs, break; case ev_float: - *(float *)(progfuncs->funcs.stringtable + qcptr) = (float)atof (s); + while(*s == ' ' || *s == '\t') + s++; + d = strtod(s, &s); + while(*s == ' ' || *s == '\t') + s++; + *(float *)(progfuncs->funcs.stringtable + qcptr) = d; + if (*s) + return false; //some kind of junk in there. break; + case ev_entity: //ent references are simple ints for us. case ev_integer: - *(int *)(progfuncs->funcs.stringtable + qcptr) = atoi (s); + while(*s == ' ' || *s == '\t') + s++; + i = strtol(s, &s, 0); + while(*s == ' ' || *s == '\t') + s++; + *(int *)(progfuncs->funcs.stringtable + qcptr) = i; + if (*s) + return false; //some kind of junk in there. break; case ev_vector: - strcpy (string, s); - v = string; - w = string; for (i=0 ; i<3 ; i++) { - while (*v && *v != ' ') - v++; - if (!*v) - { - ((float *)(progfuncs->funcs.stringtable + qcptr))[i] = (float)atof (w); - w = v; - } - else - { - *v = 0; - ((float *)(progfuncs->funcs.stringtable + qcptr))[i] = (float)atof (w); - w = v = v+1; - } + while(*s == ' ' || *s == '\t') + s++; + d = strtod(s, &s); + ((float *)(progfuncs->funcs.stringtable + qcptr))[i] = d; } - break; - - case ev_entity: - *(int *)(progfuncs->funcs.stringtable + qcptr) = atoi (s); + while(*s == ' ' || *s == '\t') + s++; + if (*s) + return false; //some kind of junk in there. break; case ev_field: @@ -1311,7 +1313,7 @@ pbool ED_ParseEpair (progfuncs_t *progfuncs, size_t qcptr, unsigned int fldofs, break; case ev_function: - if (s[1]==':'&&s[2]=='\0') + if (s[0] && s[1]==':'&&s[2]=='\0') //this isn't right... { *(func_t *)(progfuncs->funcs.stringtable + qcptr) = 0; return true; @@ -1326,7 +1328,7 @@ pbool ED_ParseEpair (progfuncs_t *progfuncs, size_t qcptr, unsigned int fldofs, break; default: - break; + return false; } return true; } @@ -1341,7 +1343,7 @@ Used for initial level load and for savegames. ==================== */ #if 1 -const char *ED_ParseEdict (progfuncs_t *progfuncs, const char *data, edictrun_t *ent) +static const char *ED_ParseEdict (progfuncs_t *progfuncs, const char *data, edictrun_t *ent, pbool *out_maphack) { fdef_t *key; pbool init; @@ -1432,8 +1434,19 @@ const char *ED_ParseEdict (progfuncs_t *progfuncs, const char *data, edictrun_t } cont: + switch(key->type) + { + case ev_function: + case ev_field: + case ev_entity: + case ev_pointer: + *out_maphack = true; //one of these types of fields means evil maphacks are at play. + break; + } if (!ED_ParseEpair (progfuncs, (char*)ent->fields - progfuncs->funcs.stringtable, key->ofs, key->type, qcc_token)) { + if (externs->badfield && externs->badfield(&progfuncs->funcs, (struct edict_s*)ent, keyname, qcc_token)) + continue; continue; // Sys_Error ("ED_ParseEdict: parse error on entities"); } @@ -1911,6 +1924,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD int crc = 1; int entsize = 0; int numents = 0; + pbool maphacks = false; pbool resethunk=0; pbool isloadgame; @@ -2021,7 +2035,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD ed->ereftype = ER_ENTITY; if (externs->entspawn) externs->entspawn((struct edict_s *) ed, true); - file = ED_ParseEdict(progfuncs, file, ed); + file = ED_ParseEdict(progfuncs, file, ed, &maphacks); if (entspawned) entspawned(ppf, (struct edict_s *)ed, ctx, datastart, file); @@ -2262,7 +2276,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD externs->entspawn((struct edict_s *) ed, true); ed->ereftype = ER_ENTITY; - file = ED_ParseEdict (progfuncs, file, ed); + file = ED_ParseEdict (progfuncs, file, ed, &maphacks); } sv_num_edicts = ++numents; continue; @@ -2292,7 +2306,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD ed->ereftype = ER_ENTITY; if (externs->entspawn) externs->entspawn((struct edict_s *) ed, true); - file = ED_ParseEdict(progfuncs, file, ed); + file = ED_ParseEdict(progfuncs, file, ed, &maphacks); if (entspawned) entspawned(ppf, (struct edict_s *)ed, ctx, datastart, file); @@ -2409,6 +2423,7 @@ struct edict_s *PDECL PR_RestoreEnt (pubprogfuncs_t *ppf, const char *buf, size_ progfuncs_t *progfuncs = (progfuncs_t*)ppf; edictrun_t *ent; const char *start = buf; + pbool maphacks = false; //don't really care. buf = QCC_COM_Parse(buf); //read the key if (!buf || !*qcc_token) @@ -2425,7 +2440,7 @@ struct edict_s *PDECL PR_RestoreEnt (pubprogfuncs_t *ppf, const char *buf, size_ if (ent->ereftype == ER_FREE && externs->entspawn) externs->entspawn((struct edict_s *) ent, false); - buf = ED_ParseEdict(progfuncs, buf, ent); + buf = ED_ParseEdict(progfuncs, buf, ent, &maphacks); *size = buf - start; diff --git a/engine/qclib/progsint.h b/engine/qclib/progsint.h index df66d34be..c0db2f3ae 100644 --- a/engine/qclib/progsint.h +++ b/engine/qclib/progsint.h @@ -392,7 +392,6 @@ char *PDECL ED_NewString (pubprogfuncs_t *ppf, const char *string, int minlength void PDECL ED_Print (pubprogfuncs_t *progfuncs, struct edict_s *ed); //void ED_Write (FILE *f, edictrun_t *ed); -const char *ED_ParseEdict (progfuncs_t *progfuncs, const char *data, edictrun_t *ent); //void ED_WriteGlobals (FILE *f); void ED_ParseGlobals (char *data); diff --git a/engine/server/net_preparse.c b/engine/server/net_preparse.c index b97463a4b..5dd29b120 100644 --- a/engine/server/net_preparse.c +++ b/engine/server/net_preparse.c @@ -600,7 +600,7 @@ extern qboolean ssqc_deprecated_warned; #define svcdp_showlmp 35 // [string] slotname [string] lmpfilename [short] x [short] y #define svcdp_hidelmp 36 // [string] slotname -#define TE_RAILTRAIL_NEH 15 // [vector] origin [coord] red [coord] green [coord] blue (fixme: ignored) +//#define TE_RAILTRAIL_NEH 15 // [vector] origin [coord] red [coord] green [coord] blue (fixme: ignored) #define TE_EXPLOSION3_NEH 16 // [vector] origin [coord] red [coord] green [coord] blue (fixme: ignored) #define TE_LIGHTNING4_NEH 17 // [string] model [entity] entity [vector] start [vector] end #define TE_SMOKE_NEH 18 @@ -861,7 +861,7 @@ void NPP_NQFlush(void) buffer[0] = svcfte_cgamepacket; } break; - case TE_EXPLOSION: + case TENQ_NQEXPLOSION: if (writedest == &sv.datagram) { //for old clients, use a te_explosion. //for clients that support it, use a TEQW_EXPLOSIONNOSPRITE @@ -880,7 +880,7 @@ void NPP_NQFlush(void) requireextension = PEXT_TE_BULLET; SV_MulticastProtExt(org, multicasttype, pr_global_struct->dimension_send, 0, requireextension); - buffer[1] = TEQW_EXPLOSION_NOSPRITE; + buffer[1] = TEQW_NQEXPLOSION; } break; case TENQ_BEAM: @@ -901,7 +901,7 @@ void NPP_NQFlush(void) memcpy(&cd, &buffer[2+destprim->coordsize*2], destprim->coordsize); org[2] = MSG_FromCoord(cd, destprim->coordsize); - buffer[1] = TE_EXPLOSION; //use a generic crappy explosion + buffer[1] = TEQW_QWEXPLOSION; //use a generic crappy explosion SZ_Write(&sv.multicast, buffer, bufferlen-2); //trim the two trailing colour bytes SV_MulticastProtExt(org, multicasttype, pr_global_struct->dimension_send, 0, requireextension); } @@ -1240,7 +1240,7 @@ void NPP_NQWriteByte(int dest, qbyte data) //replacement write func (nq to qw) multicasttype=MULTICAST_PHS; protocollen = destprim->coordsize*6+sizeof(short)+sizeof(qbyte)*2; break; - case TE_GUNSHOT: + case TENQ_NQGUNSHOT: multicastpos=3; multicasttype=MULTICAST_PVS; //we need to emit annother qbyte here. QuakeWorld has a number of particles. @@ -1249,7 +1249,7 @@ void NPP_NQWriteByte(int dest, qbyte data) //replacement write func (nq to qw) data = 1; protocollen = destprim->coordsize*3+sizeof(qbyte)*3; break; - case TE_EXPLOSION: + case TENQ_NQEXPLOSION: case TE_SPIKE: case TE_SUPERSPIKE: multicastpos=2; @@ -1279,12 +1279,12 @@ void NPP_NQWriteByte(int dest, qbyte data) //replacement write func (nq to qw) multicasttype=MULTICAST_PHS; break; case TE_EXPLOSIONSMALL2: - data = TE_EXPLOSION; + data = TEQW_QWEXPLOSION; protocollen = sizeof(qbyte)*2 + destprim->coordsize*3; multicastpos=2; multicasttype=MULTICAST_PHS; break; - case TE_RAILTRAIL: + case TEQW_RAILTRAIL: protocollen = destprim->coordsize*6+sizeof(qbyte)*1; multicastpos=2; multicasttype=MULTICAST_PHS; @@ -1904,7 +1904,7 @@ void NPP_QWFlush(void) } break; case TEQW_LIGHTNINGBLOOD: - case TEQW_BLOOD: //needs to be converted to a particle + case TEQW_QWBLOOD: //needs to be converted to an svc_particle { vec3_t org; qbyte count; @@ -1957,7 +1957,7 @@ void NPP_QWFlush(void) NPP_AddData(&colour, sizeof(qbyte)); } break; - case TE_GUNSHOT: //needs byte 3 removed + case TEQW_QWGUNSHOT: //needs byte 3 removed if (bufferlen >= 3) { memmove(buffer+2, buffer+3, bufferlen-3); @@ -2184,14 +2184,14 @@ void NPP_QWWriteByte(int dest, qbyte data) //replacement write func (nq to qw) multicasttype=MULTICAST_PHS; protocollen = destprim->coordsize*6+sizeof(short)+sizeof(qbyte)*2; break; - case TEQW_BLOOD: //needs to be converted to a particle - case TE_GUNSHOT: //needs qbyte 2 removed + case TEQW_QWBLOOD: //needs to be converted to a particle + case TEQW_QWGUNSHOT: //needs qbyte 2 removed multicastpos=3; multicasttype=MULTICAST_PVS; protocollen = destprim->coordsize*3+sizeof(qbyte)*3; break; case TEQW_LIGHTNINGBLOOD: - case TE_EXPLOSION: + case TEQW_QWEXPLOSION: case TE_SPIKE: case TE_SUPERSPIKE: multicastpos=2; @@ -2207,7 +2207,7 @@ void NPP_QWWriteByte(int dest, qbyte data) //replacement write func (nq to qw) multicasttype=MULTICAST_PVS; protocollen = destprim->coordsize*3+sizeof(qbyte)*2; break; - case TE_RAILTRAIL: + case TEQW_RAILTRAIL: multicastpos=1; multicasttype=MULTICAST_PVS; protocollen = destprim->coordsize*3+sizeof(qbyte)*1; diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 24010be13..8d413dda0 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -3226,7 +3226,7 @@ void QCBUILTIN PF_particle (pubprogfuncs_t *prinst, globalvars_t *pr_globals) // if (color == 73) { MSG_WriteByte (&sv.multicast, svc_temp_entity); - MSG_WriteByte (&sv.multicast, TEQW_BLOOD); + MSG_WriteByte (&sv.multicast, TEQW_QWBLOOD); MSG_WriteByte (&sv.multicast, count<10?1:(count+10)/20); MSG_WriteCoord (&sv.multicast, org[0]); MSG_WriteCoord (&sv.multicast, org[1]); @@ -3292,7 +3292,7 @@ static void QCBUILTIN PF_te_blooddp (pubprogfuncs_t *prinst, globalvars_t *pr_gl (void)dir; //FIXME: sould be sending TEDP_BLOOD MSG_WriteByte (&sv.multicast, svc_temp_entity); - MSG_WriteByte (&sv.multicast, TEQW_BLOOD); + MSG_WriteByte (&sv.multicast, TEQW_QWBLOOD); MSG_WriteByte (&sv.multicast, count<10?1:(count+10)/20); MSG_WriteCoord (&sv.multicast, org[0]); MSG_WriteCoord (&sv.multicast, org[1]); @@ -3878,6 +3878,7 @@ void PF_stuffcmd_Internal(int entnum, const char *str, unsigned int flags) else SV_StuffcmdToClient(cl, str); } +#ifdef MVD_RECORDING if (!(flags & STUFFCMD_IGNOREINDEMO)) if (sv.mvdrecording) { @@ -3885,6 +3886,7 @@ void PF_stuffcmd_Internal(int entnum, const char *str, unsigned int flags) MSG_WriteByte (msg, svc_stufftext); MSG_WriteString (msg, str); } +#endif return; } @@ -3948,6 +3950,7 @@ void PF_stuffcmd_Internal(int entnum, const char *str, unsigned int flags) SV_StuffcmdToClient(cl, str); } +#ifdef MVD_RECORDING if (!(flags & STUFFCMD_IGNOREINDEMO)) if (sv.mvdrecording) { @@ -3955,6 +3958,7 @@ void PF_stuffcmd_Internal(int entnum, const char *str, unsigned int flags) MSG_WriteByte (msg, svc_stufftext); MSG_WriteString (msg, str); } +#endif //this seems a little dangerous. v_cshift could leave a spectator's machine unusable if they switch players at unfortunate times. if (!(flags & STUFFCMD_DEMOONLY)) @@ -4444,6 +4448,7 @@ static void QCBUILTIN PF_getmodelindex (pubprogfuncs_t *prinst, struct globalvar G_FLOAT(OFS_RETURN) = PF_precache_model_Internal(prinst, s, queryonly); } +#ifndef NOLEGACY void QCBUILTIN PF_precache_vwep_model (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int i; @@ -4480,6 +4485,7 @@ void QCBUILTIN PF_precache_vwep_model (pubprogfuncs_t *prinst, struct globalvars G_FLOAT(OFS_RETURN) = 0; } } +#endif // warning: ‘PF_svcoredump’ defined but not used /* @@ -4620,6 +4626,7 @@ void QCBUILTIN PF_applylightstyle(int style, const char *val, vec3_t rgb) } } +#ifdef MVD_RECORDING if (sv.mvdrecording) { if (style < MAX_STANDARDLIGHTSTYLES || *val) @@ -4645,6 +4652,7 @@ void QCBUILTIN PF_applylightstyle(int style, const char *val, vec3_t rgb) } } } +#endif } /* @@ -5572,9 +5580,12 @@ static void QCBUILTIN PF_qtBroadcast_WriteEntity (pubprogfuncs_t *prinst, struct //====================================================== //copes with any qw point entities. -void SV_point_tempentity (vec3_t o, int type, int count) //count (usually 1) is available for some tent types. +void SV_point_tempentity (vec3_t o, int qwtype, int count) //count (usually 1) is available for some tent types. { int split=0; +#ifdef NQPROT + int nqtype = qwtype; +#endif #ifdef SERVER_DEMO_PLAYBACK if (sv.demofile) @@ -5586,49 +5597,63 @@ void SV_point_tempentity (vec3_t o, int type, int count) //count (usually 1) is #ifdef NQPROT MSG_WriteByte (&sv.nqmulticast, svc_temp_entity); #endif - switch(type) + switch(qwtype) { case TE_BULLET: MSG_WriteByte (&sv.multicast, TE_SPIKE); + qwtype = TE_BULLET; #ifdef NQPROT MSG_WriteByte (&sv.nqmulticast, TE_SPIKE); + nqtype = TE_BULLET; #endif - type = TE_BULLET; split = PEXT_TE_BULLET; break; - case TE_SUPERBULLET: + case TEQW_SUPERBULLET: MSG_WriteByte (&sv.multicast, TE_SUPERSPIKE); + qwtype = TEQW_SUPERBULLET; #ifdef NQPROT MSG_WriteByte (&sv.nqmulticast, TE_SUPERSPIKE); + nqtype = TE_SUPERSPIKE; #endif - type = TE_SUPERBULLET; split = PEXT_TE_BULLET; break; - case TEQW_BLOOD: - case TE_GUNSHOT: - MSG_WriteByte (&sv.multicast, type); + case TEQW_QWBLOOD: + case TEQW_QWGUNSHOT: + MSG_WriteByte (&sv.multicast, qwtype); MSG_WriteByte (&sv.multicast, count); #ifdef NQPROT - MSG_WriteByte (&sv.nqmulticast, type); //nq doesn't have a count. + MSG_WriteByte (&sv.nqmulticast, nqtype); //nq doesn't have a count. #endif break; - case TEQW_EXPLOSION_NOSPRITE: - MSG_WriteByte (&sv.multicast, TE_EXPLOSION); + case TEQW_QWEXPLOSION: + MSG_WriteByte (&sv.multicast, TEQW_QWEXPLOSION); + qwtype = -1; #ifdef NQPROT - MSG_WriteByte (&sv.nqmulticast, TE_EXPLOSION); + MSG_WriteByte (&sv.nqmulticast, TENQ_NQEXPLOSION); + nqtype = TENQ_QWEXPLOSION; +#endif + split = PEXT_TE_BULLET; + break; + case TEQW_NQEXPLOSION: + MSG_WriteByte (&sv.multicast, TEQW_QWEXPLOSION); + qwtype = TEQW_NQEXPLOSION; +#ifdef NQPROT + MSG_WriteByte (&sv.nqmulticast, TENQ_NQEXPLOSION); + nqtype = -1; #endif - type = TEQW_EXPLOSION_NOSPRITE; split = PEXT_TE_BULLET; break; case TE_LIGHTNING1: case TE_LIGHTNING2: case TE_LIGHTNING3: SV_Error("SV_point_tempentity - type is a beam\n"); + break; default: - MSG_WriteByte (&sv.multicast, type); + MSG_WriteByte (&sv.multicast, qwtype); #ifdef NQPROT - MSG_WriteByte (&sv.nqmulticast, type); + MSG_WriteByte (&sv.nqmulticast, nqtype); #endif + break; } MSG_WriteCoord (&sv.multicast, o[0]); MSG_WriteCoord (&sv.multicast, o[1]); @@ -5638,7 +5663,7 @@ void SV_point_tempentity (vec3_t o, int type, int count) //count (usually 1) is MSG_WriteCoord (&sv.nqmulticast, o[1]); MSG_WriteCoord (&sv.nqmulticast, o[2]); #endif - if (type == TEQW_BLOOD || type == TEQW_LIGHTNINGBLOOD) + if (qwtype == TEQW_QWBLOOD || qwtype == TEQW_LIGHTNINGBLOOD) { #ifdef NQPROT sv.nqmulticast.cursize = 0; //don't send a te_blood or lightningblood to an nq client - they'll die horribly. @@ -5653,7 +5678,7 @@ void SV_point_tempentity (vec3_t o, int type, int count) //count (usually 1) is MSG_WriteChar (&sv.nqmulticast, 0); MSG_WriteChar (&sv.nqmulticast, 0); MSG_WriteByte (&sv.nqmulticast, count*20); - if (type == TEQW_BLOOD) + if (qwtype == TEQW_QWBLOOD) MSG_WriteByte (&sv.nqmulticast, 73); else MSG_WriteByte (&sv.nqmulticast, 225); @@ -5665,13 +5690,29 @@ void SV_point_tempentity (vec3_t o, int type, int count) //count (usually 1) is if (!split) //don't bother sending again. return; -//this is for cool people (not nq users) - MSG_WriteByte (&sv.multicast, svc_temp_entity); - MSG_WriteByte (&sv.multicast, type); - MSG_WriteCoord (&sv.multicast, o[0]); - MSG_WriteCoord (&sv.multicast, o[1]); - MSG_WriteCoord (&sv.multicast, o[2]); - + //this is for people with an extension to do that effect properly + if (qwtype >= 0) + { + MSG_WriteByte (&sv.multicast, svc_temp_entity); + MSG_WriteByte (&sv.multicast, qwtype); + if (qwtype == TEQW_QWBLOOD || qwtype == TEQW_QWGUNSHOT) + MSG_WriteByte (&sv.multicast, count); + MSG_WriteCoord (&sv.multicast, o[0]); + MSG_WriteCoord (&sv.multicast, o[1]); + MSG_WriteCoord (&sv.multicast, o[2]); + } +#ifdef NQPROT + if (nqtype >= 0) + { + MSG_WriteByte (&sv.nqmulticast, svc_temp_entity); + MSG_WriteByte (&sv.nqmulticast, nqtype); + if (/*nqtype == TENQ_QWBLOOD ||*/ nqtype == TEQW_NQGUNSHOT) + MSG_WriteByte (&sv.multicast, count); + MSG_WriteCoord (&sv.nqmulticast, o[0]); + MSG_WriteCoord (&sv.nqmulticast, o[1]); + MSG_WriteCoord (&sv.nqmulticast, o[2]); + } +#endif SV_MulticastProtExt (o, MULTICAST_PHS, pr_global_struct->dimension_send, 0, split); } @@ -6947,6 +6988,7 @@ static void QCBUILTIN PF_readcmd (pubprogfuncs_t *prinst, struct globalvars_s *p SV_BeginRedirect(old, oldl); } +#ifdef MVD_RECORDING /* ================= PF_forcedemoframe @@ -6963,7 +7005,7 @@ static void QCBUILTIN PF_forcedemoframe (pubprogfuncs_t *prinst, struct globalva // if (G_FLOAT(OFS_PARM0) == 1) // SV_SendDemoMessage(); } - +#endif /* ================= @@ -8667,10 +8709,15 @@ static void QCBUILTIN PF_te_gunshot(pubprogfuncs_t *prinst, struct globalvars_s { int count; if (svprogfuncs->callargc >= 2) + { count = G_FLOAT(OFS_PARM1); + SV_point_tempentity(G_VECTOR(OFS_PARM0), TEQW_QWGUNSHOT, count); + } else + { count = 1; - SV_point_tempentity(G_VECTOR(OFS_PARM0), TE_GUNSHOT, count); + SV_point_tempentity(G_VECTOR(OFS_PARM0), TEQW_NQGUNSHOT, count); + } } //DP_TE_QUADEFFECTS1 static void QCBUILTIN PF_te_gunshotquad(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) @@ -8710,7 +8757,7 @@ static void QCBUILTIN PF_te_bloodqw(pubprogfuncs_t *prinst, struct globalvars_s count = G_FLOAT(OFS_PARM1); else count = 1; - SV_point_tempentity(G_VECTOR(OFS_PARM0), TEQW_BLOOD, count); + SV_point_tempentity(G_VECTOR(OFS_PARM0), TEQW_QWBLOOD, count); } //DP_TE_STANDARDEFFECTBUILTINS @@ -8729,10 +8776,10 @@ static void QCBUILTIN PF_te_superspikequad(pubprogfuncs_t *prinst, struct global //void(vector org) te_explosion = #421; static void QCBUILTIN PF_te_explosion(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - if (progstype != PROG_QW) - SV_point_tempentity(G_VECTOR(OFS_PARM0), TEQW_EXPLOSION_NOSPRITE, 1); + if (progstype == PROG_QW) + SV_point_tempentity(G_VECTOR(OFS_PARM0), TEQW_QWEXPLOSION, 1); else - SV_point_tempentity(G_VECTOR(OFS_PARM0), TE_EXPLOSION, 1); + SV_point_tempentity(G_VECTOR(OFS_PARM0), TEQW_NQEXPLOSION, 1); } //DP_TE_QUADEFFECTS1 static void QCBUILTIN PF_te_explosionquad(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) @@ -8789,7 +8836,7 @@ static void QCBUILTIN PF_te_explosion2(pubprogfuncs_t *prinst, struct globalvars for(;;) { MSG_WriteByte (&sv.multicast, svc_temp_entity); - MSG_WriteByte (&sv.multicast, old?TE_EXPLOSION:TEQW_EXPLOSION2); + MSG_WriteByte (&sv.multicast, old?TEQW_QWEXPLOSION:TEQW_EXPLOSION2); MSG_WriteCoord (&sv.multicast, org[0]); MSG_WriteCoord (&sv.multicast, org[1]); MSG_WriteCoord (&sv.multicast, org[2]); @@ -8799,13 +8846,16 @@ static void QCBUILTIN PF_te_explosion2(pubprogfuncs_t *prinst, struct globalvars MSG_WriteByte (&sv.multicast, length); } #ifdef NQPROT - MSG_WriteByte (&sv.nqmulticast, svc_temp_entity); - MSG_WriteByte (&sv.nqmulticast, TENQ_EXPLOSION2); - MSG_WriteCoord (&sv.nqmulticast, org[0]); - MSG_WriteCoord (&sv.nqmulticast, org[1]); - MSG_WriteCoord (&sv.nqmulticast, org[2]); - MSG_WriteByte (&sv.nqmulticast, start); - MSG_WriteByte (&sv.nqmulticast, length); + if (!old) + { + MSG_WriteByte (&sv.nqmulticast, svc_temp_entity); + MSG_WriteByte (&sv.nqmulticast, TENQ_EXPLOSION2); + MSG_WriteCoord (&sv.nqmulticast, org[0]); + MSG_WriteCoord (&sv.nqmulticast, org[1]); + MSG_WriteCoord (&sv.nqmulticast, org[2]); + MSG_WriteByte (&sv.nqmulticast, start); + MSG_WriteByte (&sv.nqmulticast, length); + } #endif if (old) @@ -10335,7 +10385,9 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"mvdstrncpy", PF_MVDSV_strncpy, 0, 0, 0, 99, D("void(string dst, string src, float count)",NULL), true}, {"logtext", PF_logtext, 0, 0, 0, 100, D("void(string name, float console, string text)",NULL), true}, {"mvdcalltimeofday",PF_calltimeofday, 0, 0, 0, 102, D("void()",NULL), true}, +#ifdef MVD_RECORDING {"forcedemoframe", PF_forcedemoframe, 0, 0, 0, 103, D("void(float now)",NULL), true}, +#endif //end of mvdsv #endif {"redirectcmd", PF_redirectcmd, 0, 0, 0, 101, D("void(entity to, string str)","Executes a single console command, and sends the text generated by it to the specified player. The command will be executed at the end of the frame once QC is no longer running - you may wish to pre/postfix it with 'echo'.")}, @@ -10996,7 +11048,9 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"setpause", PF_setpause, 0, 0, 0, 531, D("void(float pause)", "Sets whether the server should or should not be paused. This does not affect auto-paused things like when the console is down.")}, //end dp extras //begin mvdsv extras +#ifndef NOLEGACY {"precache_vwep_model",PF_precache_vwep_model,0,0, 0, 532, "float(string mname)"}, +#endif //end mvdsv extras //restart dp extras {"log", PF_Logarithm, 0, 0, 0, 532, D("float(float v, optional float base)", "Determines the logarithm of the input value according to the specified base. This can be used to calculate how much something was shifted by.")}, @@ -11621,16 +11675,16 @@ void PR_DumpPlatform_f(void) {"input_buttons", "float", CS}, {"input_impulse", "float", CS}, {"msg_entity", "entity", QW|NQ}, - {"main", "void()", QW|NQ}, - {"StartFrame", "void()", QW|NQ}, - {"PlayerPreThink", "void()", QW|NQ}, - {"PlayerPostThink", "void()", QW|NQ}, - {"ClientKill", "void()", QW|NQ}, - {"ClientConnect", "void()", QW|NQ}, - {"PutClientInServer", "void()", QW|NQ}, - {"ClientDisconnect", "void()", QW|NQ}, - {"SetNewParms", "void()", QW|NQ}, - {"SetChangeParms", "void()", QW|NQ}, + {"main", "void()", QW|NQ, D("This function is never called, and is effectively dead code.")}, + {"StartFrame", "void()", QW|NQ, D("Called at the start of each new physics frame. Player entities may think out of sequence so try not to depend upon explicit ordering too much.")}, + {"PlayerPreThink", "void()", QW|NQ, D("With Prediction(QW compat/FTE default): Called before the player's input commands are processed.\nNo Prediction(NQ compat): Called AFTER the player's movement intents have already been processed (ie: velocity will have already changed according to input_*, but before the actual position change.")}, + {"PlayerPostThink", "void()", QW|NQ, D("Called after the player's input commands are processed.")}, + {"ClientKill", "void()", QW|NQ, D("Called in response to 'cmd kill' (or just 'kill').")}, + {"ClientConnect", "void(optional float csqcactive)", QW|NQ, D("Called after the connecting client has finished loading and is ready to receive active entities. Note that this is NOT the first place that a client might be referred to.")}, + {"PutClientInServer", "void()", QW|NQ, D("Enginewise, this is only ever called immediately after ClientConnect and is thus a little redundant. Modwise, this is also called for respawning a player etc.")}, + {"ClientDisconnect", "void()", QW|NQ, D("Called once a client disconnects or times out. Not guarenteed to be called on map changes.")}, + {"SetNewParms", "void()", QW|NQ, D("Called without context when a new client initially connects (before ClientConnect is even called). This function is expected to only set the parm* globals so that they can be decoded properly later. You should not rely on 'self' being set.")}, + {"SetChangeParms", "void()", QW|NQ, D("Called for each client on map changes. Should copy various entity fields to the parm* globals.")}, {"end_sys_globals", "void", QW|NQ|CS|MENU}, @@ -11826,7 +11880,7 @@ void PR_DumpPlatform_f(void) {"GameCommand", "void(string cmdtext)", CS|MENU}, {"Cef_GeneratePage", "string(string uri, string method, string postdata, __in string requestheaders, __inout string responseheaders)", QW|NQ|CS|MENU, "Provides an entrypoint to generate pages for the CEF plugin from within QC. Headers are \n-separated key/value pairs (use tokenizebyseparator)."}, -// {"HTTP_GeneratePage", "string(string uri, string method, string postdata, __in string requestheaders, __inout string responseheaders)", QW|NQ, "Provides an entrypoint to generate pages for pages requested over http (sv_listen_tcp+net_enable_http). Headers are \n-separated key/value pairs (use tokenizebyseparator)."}, + {"HTTP_GeneratePage", "string(string uri, string method, string postdata, __in string requestheaders, __inout string responseheaders)", QW|NQ, "Provides an entrypoint to generate pages for pages requested over http (sv_port_tcp+net_enable_http). Headers are \r\n-separated key/value pairs (use tokenizebyseparator). Return __NULL__ to let the engine handle it, an empty string for a 404, and any other text for a regular 200 response."}, {"init", "void(float prevprogs)", QW|NQ|CS, "Part of FTE_MULTIPROGS. Called as soon as a progs is loaded, called at a time when entities are not valid. This is the only time when it is safe to call addprogs without field assignment. As it is also called as part of addprogs, this also gives you a chance to hook functions in modules that are already loaded (via externget+externget)."}, {"initents", "void()", QW|NQ|CS, "Part of FTE_MULTIPROGS. Called after fields have been finalized. This is the first point at which it is safe to call spawn(), and is called before any entity fields have been parsed. You can use this entrypoint to send notifications to other modules."}, diff --git a/engine/server/pr_lua.c b/engine/server/pr_lua.c index 5f84603bd..63167bfb0 100644 --- a/engine/server/pr_lua.c +++ b/engine/server/pr_lua.c @@ -2836,7 +2836,7 @@ cont: return data; } -static int QDECL Lua_LoadEnts(pubprogfuncs_t *pf, const char *mapstring, void *ctx, void (PDECL *callback) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend)) +static int QDECL Lua_LoadEnts(pubprogfuncs_t *pf, const char *mapstring, void *ctx, void (PDECL *callback) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend), pbool (PDECL *unhandledcallback)(pubprogfuncs_t *,void *,const char **)) { lua_State *L = lua.ctx; struct edict_s *ed = NULL; diff --git a/engine/server/savegame.c b/engine/server/savegame.c index 37f13202d..a8db66115 100644 --- a/engine/server/savegame.c +++ b/engine/server/savegame.c @@ -77,9 +77,7 @@ void SV_SavegameComment (char *text, size_t textsize) text[textsize-1] = '\0'; } -#ifndef QUAKETC - -pbool SV_Legacy_ExtendedSaveData(pubprogfuncs_t *progfuncs, void *loadctx, const char **ptr) +pbool SV_ExtendedSaveData(pubprogfuncs_t *progfuncs, void *loadctx, const char **ptr) { char token[8192]; com_tokentype_t tt; @@ -164,6 +162,8 @@ pbool SV_Legacy_ExtendedSaveData(pubprogfuncs_t *progfuncs, void *loadctx, const return true; } +#ifndef QUAKETC + //expects the version to have already been parsed static qboolean SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version) { @@ -409,7 +409,7 @@ static qboolean SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version) strcpy(file, "loadgame"); clnum=VFS_READ(f, file+8, filelen); file[filelen+8]='\0'; - sv.world.edict_size=svprogfuncs->load_ents(svprogfuncs, file, NULL, NULL, SV_Legacy_ExtendedSaveData); + sv.world.edict_size=svprogfuncs->load_ents(svprogfuncs, file, NULL, NULL, SV_ExtendedSaveData); BZ_Free(file); PR_LoadGlabalStruct(false); @@ -947,7 +947,7 @@ qboolean SV_LoadLevelCache(const char *savename, const char *level, const char * memset(file, 0, filelen+1); VFS_READ(f, file, filelen); file[filelen]='\0'; - sv.world.edict_size=svprogfuncs->load_ents(svprogfuncs, file, NULL, NULL, SV_Legacy_ExtendedSaveData); + sv.world.edict_size=svprogfuncs->load_ents(svprogfuncs, file, NULL, NULL, SV_ExtendedSaveData); BZ_Free(file); progstype = pt; @@ -1302,9 +1302,11 @@ void SV_SaveLevelCache(const char *savedir, qboolean dontharmgame) for (i=1 ; i= CACHEGAME_VERSION_BINARY); diff --git a/engine/server/server.h b/engine/server/server.h index cd4a28b91..20fc2186a 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -117,6 +117,7 @@ typedef struct qboolean mapchangelocked; #ifdef SAVEDGAMES + char loadgame_on_restart[MAX_QPATH]; //saved game to load on map_restart double autosave_time; #endif double time; @@ -140,8 +141,6 @@ typedef struct char mapname[256]; // text description of the map char modelname[MAX_QPATH]; // maps/.bsp, for model_precache[0] - char loadgame_on_restart[MAX_QPATH]; //saved game to load on map_restart - world_t world; union { @@ -153,7 +152,9 @@ typedef struct }; #endif struct { +#ifndef NOLEGACY const char *vw_model_precache[32]; +#endif const char *model_precache[MAX_PRECACHE_MODELS]; // NULL terminated const char *particle_precache[MAX_SSPARTICLESPRE]; // NULL terminated const char *sound_precache[MAX_PRECACHE_SOUNDS]; // NULL terminated @@ -164,8 +165,10 @@ typedef struct qboolean stringsalloced; //if true, we need to free the string pointers safely rather than just memsetting them to 0 vec3_t lightstylecolours[MAX_LIGHTSTYLES]; +#ifdef HEXEN2 char h2miditrack[MAX_QPATH]; qbyte h2cdtrack; +#endif int allocated_client_slots; //number of slots available. (used mostly to stop single player saved games cacking up) int spawned_client_slots; //number of PLAYER slots which are active (ie: putclientinserver was called) @@ -223,8 +226,6 @@ typedef struct int signon_buffer_size[MAX_SIGNON_BUFFERS]; qbyte signon_buffers[MAX_SIGNON_BUFFERS][MAX_DATAGRAM]; - qboolean msgfromdemo; - qboolean gamedirchanged; qboolean haveitems2; //use items2 field instead of serverflags for the high bits of STAT_ITEMS @@ -232,11 +233,12 @@ typedef struct - +#ifdef MVD_RECORDING qboolean mvdrecording; +#endif //==================================================== -//this lot is for serverside playback of demos +//this lot is for serverside playback of mvds. Use QTV instead. #ifdef SERVER_DEMO_PLAYBACK qboolean mvdplayback; float realtime; @@ -408,9 +410,13 @@ enum PRESPAWN_INVALID=0, PRESPAWN_PROTOCOLSWITCH, //nq drops unreliables until reliables are acked. this gives us a chance to drop any clc_move packets with formats from the previous map PRESPAWN_SERVERINFO, +#ifdef MVD_RECORDING PRESPAWN_CSPROGS, //demos contain a copy of the csprogs. +#endif PRESPAWN_SOUNDLIST, //nq skips these +#ifndef NOLEGACY PRESPAWN_VWEPMODELLIST, //qw ugly extension. +#endif PRESPAWN_MODELLIST, PRESPAWN_MAPCHECK, //wait for old prespawn command PRESPAWN_PARTICLES, @@ -676,8 +682,6 @@ typedef struct client_s int trustlevel; - qboolean wasrecorded; //this client shouldn't get any net messages sent to them - vec3_t specorigin; //mvds need to use a different origin from the one QC has. vec3_t specvelocity; @@ -737,7 +741,7 @@ typedef struct client_s //============================================================================= //mvd stuff - +#ifdef MVD_RECORDING #define MSG_BUF_SIZE 8192 typedef struct @@ -768,13 +772,13 @@ typedef struct { int to; int size; qbyte data[1]; //gcc doesn't allow [] (?) -} header_t; +} mvd_header_t; typedef struct { sizebuf_t sb; int bufsize; - header_t *h; + mvd_header_t *h; } demobuf_t; typedef struct @@ -821,7 +825,7 @@ typedef struct struct mvddest_s *dest; } demo_t; - +#endif //============================================================================= @@ -935,10 +939,6 @@ typedef struct struct netprim_s netprim; - qboolean demoplayback; - qboolean demorecording; - qboolean msgfromdemo; - int language; //the server operators language laggedpacket_t *free_lagged_packet; packet_entities_t entstatebuffer; /*just a temp buffer*/ @@ -1479,7 +1479,7 @@ void SV_ChatThink(client_t *client); #endif - +#ifdef MVD_RECORDING /* // // sv_mvd.c @@ -1551,12 +1551,16 @@ void SV_MVD_SendInitialGamestate(mvddest_t *dest); extern demo_t demo; // server demo struct +extern cvar_t sv_demoDir; +extern cvar_t sv_demoAutoRecord; extern cvar_t sv_demofps; extern cvar_t sv_demoPings; extern cvar_t sv_demoUseCache; extern cvar_t sv_demoMaxSize; extern cvar_t sv_demoMaxDirSize; +qboolean MVD_CheckSpace(qboolean broadcastwarnings); +void SV_MVD_AutoRecord (void); char *SV_Demo_CurrentOutput(void); void SV_Demo_PrintOutputs(void); void SV_MVDInit(void); @@ -1575,6 +1579,7 @@ typedef struct char challenge[32]; } qtvpendingstate_t; int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *headerend, qtvpendingstate_t *p); +#endif // savegame.c void SV_Savegame_f (void); diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index c0cc3c4a8..136229bed 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -692,8 +692,10 @@ void SV_Map_f (void) } } +#ifdef MVD_RECORDING if (sv.mvdrecording) SV_MVDStop_f(); +#endif #ifndef SERVERONLY if (!isDedicated) //otherwise, info used on map loading isn't present @@ -1999,7 +2001,9 @@ static void SV_Status_f (void) Con_Printf("gamedir : %s\n", FS_GetGamedir(true)); if (sv.csqcdebug) Con_Printf("csqc debug : true\n"); +#ifdef MVD_RECORDING SV_Demo_PrintOutputs(); +#endif NET_PrintConnectionsStatus(svs.sockets); @@ -2187,6 +2191,7 @@ void SV_ConSay_f(void) SV_ClientPrintf(client, PRINT_CHAT, "%s\n", text); } +#ifdef MVD_RECORDING if (sv.mvdrecording) { sizebuf_t *msg; @@ -2198,6 +2203,7 @@ void SV_ConSay_f(void) MSG_WriteChar(msg, '\n'); MSG_WriteChar(msg, 0); } +#endif } static void SV_ConSayOne_f (void) @@ -2927,6 +2933,7 @@ void SV_PrecacheList_f(void) { unsigned int i; char *group = Cmd_Argv(1); +#ifndef NOLEGACY if (!*group || !strncmp(group, "vwep", 4)) { for (i = 0; i < sizeof(sv.strings.vw_model_precache)/sizeof(sv.strings.vw_model_precache[0]); i++) @@ -2935,6 +2942,7 @@ void SV_PrecacheList_f(void) Con_Printf("vwep %u: %s\n", i, sv.strings.vw_model_precache[i]); } } +#endif if (!*group || !strncmp(group, "model", 5)) { for (i = 0; i < MAX_PRECACHE_MODELS; i++) diff --git a/engine/server/sv_demo.c b/engine/server/sv_demo.c index 30cecb7c2..5a972e907 100644 --- a/engine/server/sv_demo.c +++ b/engine/server/sv_demo.c @@ -6,176 +6,6 @@ void NPP_MVDWriteByte(qbyte data, client_t *to, int broadcast); void SV_New_f (void); -/* -float svdemotime; -FILE *svdemofile; - -void SV_WriteDemoMessage (sizebuf_t *msg) -{ - short len; - float time; - if (!svs.demorecording) - return; - time = LittleFloat(sv.time); - fwrite(&time, 1, sizeof(time), svdemofile); - len = LittleShort((short)msg->cursize); - fwrite(&len, 1, sizeof(len), svdemofile); - fwrite(msg->data, 1, msg->cursize, svdemofile); -} - -qboolean SV_GetDemoMessage (void) -{ - short len; - float time; - if (sv.time < svdemotime) - { - sv.msgfromdemo = false; - return NET_GetPacket(NS_SERVER); - } -sv.msgfromdemo = true; - - fread(&len, 1, sizeof(len), svdemofile); - net_message.cursize = LittleShort(len); - fread(net_message.data, 1, net_message.cursize, svdemofile); - - sv.time = svdemotime; - - if (!fread(&time, 1, sizeof(time), svdemofile)) - { - svs.demoplayback = false; - fclose(svdemofile); - } - svdemotime = LittleFloat(time); - - net_from.ip[0] = 0; - net_from.ip[1] = 0; - net_from.ip[2] = 0; - net_from.ip[3] = 0; - - return true; -} - - - - - - -qboolean SV_GetPacket (void) -{ - if (svs.demoplayback) - return SV_GetDemoMessage (); - - if (!NET_GetPacket (NS_SERVER)) - return false; - - SV_WriteDemoMessage (&net_message); - - return true; -} - -void SV_RecordDemo_f (void) -{ - client_t *c; - int clnum; - int i; - char *name; - char *mapname; - name = Cmd_Argv(1); - mapname = Cmd_Argv(2); - - svdemofile = fopen(name, "wb"); - if (!svdemofile) - { - Con_Printf("Failed to open output file\n"); - return; - } - - fwrite(mapname, 1, sizeof(char)*(strlen(mapname)+1), svdemofile); - - - for (clnum = 0; clnum < svs.allocated_client_slots; clnum++) //clear the server so the clients reconnect and send nice fresh messages. - { - c = &svs.clients[clnum]; - if (c->state < cs_connected) - continue; - ClientReliableWrite_Begin (c, svc_stufftext, 2+strlen("reconnect\n")); - ClientReliableWrite_String (c, "disconnect;wait;reconnect\n"); - c->drop = true; - } - - SV_SendMessagesToAll (); - - svs.demorecording = true; - - i = predictablerandgetseed(); - fwrite(&i, 1, sizeof(i), svdemofile); - - SV_SpawnServer(mapname, NULL, false, false); -} - -void SV_LoadClientDemo (void); -void SV_PlayDemo_f(void) -{ - client_t *c; - int clnum; - int i; - char *name; - float time; - char mapname[64]; - - name = Cmd_Argv(1); - - if (svdemofile) - fclose(svdemofile); - svs.demoplayback=false; - svs.demorecording=false; - - COM_FOpenFile(name, &svdemofile); - - if (!svdemofile) - { - Con_Printf("Failed to open input file\n"); - return; - } -#ifndef SERVERONLY - CL_Disconnect(); -#endif - i = 0; - do - { - fread(mapname+i, 1, sizeof(char), svdemofile); - i++; - } while (mapname[i-1]); - - svs.demoplayback = true; - - for (clnum = 0; clnum < svs.allocated_client_slots; clnum++) //clear the server so new clients don't conflict. - { - c = &svs.clients[clnum]; - if (c->state < cs_connected) - continue; - ClientReliableWrite_Begin (c, svc_stufftext, 2+strlen("reconnect\n")); - ClientReliableWrite_String (c, "reconnect\n"); - c->drop = true; - } - - SV_SendMessagesToAll (); - - fread(&i, 1, sizeof(i), svdemofile); - predictablesrand(i); - - - fread(&time, 1, sizeof(time), svdemofile); - svdemotime = LittleFloat(time); - - SV_SpawnServer(mapname, NULL, false, false); -} - - - -*/ - - qboolean SV_GetPacket (void) { diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index 4456c269a..ddfd70ccc 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -2363,6 +2363,7 @@ qboolean Cull_Traceline(pvscamera_t *cameras, edict_t *seen) return true; } +#ifdef MVD_RECORDING void SV_WritePlayersToMVD (client_t *client, client_frame_t *frame, sizebuf_t *msg) { int j; @@ -2449,6 +2450,7 @@ void SV_WritePlayersToMVD (client_t *client, client_frame_t *frame, sizebuf_t *m dcl->flags |= DF_GIB; } } +#endif /* ============= @@ -4034,9 +4036,11 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore // send over the players in the PVS if (svs.gametype != GT_HALFLIFE) { +#ifdef MVD_RECORDING if (client == &demo.recorder) SV_WritePlayersToMVD(client, frame, msg); else +#endif SV_WritePlayersToClient (client, frame, clent, cameras, msg); } diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 26a002dba..90766e2ba 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -605,8 +605,10 @@ void SV_UnspawnServer (void) //terminate the running server. Con_TPrintf("Server ended\n"); SV_FinalMessage("Server unspawned\n"); +#ifdef MVD_RECORDING if (sv.mvdrecording) SV_MVDStop (MVD_CLOSE_STOPPED, false); +#endif for (i = 0; i < sv.allocated_client_slots; i++) { @@ -1725,8 +1727,9 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, FS_ReferenceControl(0, 0); - +#ifdef MVD_RECORDING SV_MVD_SendInitialGamestate(NULL); +#endif SSV_UpdateAddresses(); diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 88f67ea35..41f7f5fb3 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -221,8 +221,10 @@ void SV_Shutdown (void) SV_UnspawnServer(); +#ifdef MVD_RECORDING if (sv.mvdrecording) SV_MVDStop (MVD_CLOSE_STOPPED, false); +#endif if (svs.entstatebuffer.entities) { @@ -722,7 +724,9 @@ void SV_DropClient (client_t *drop) { // send notification to all remaining clients SV_FullClientUpdate (drop, NULL); +#ifdef MVD_RECORDING SV_MVD_FullClientUpdate(NULL, drop); +#endif } if (drop->controlled) @@ -968,8 +972,10 @@ void SV_FullClientUpdate (client_t *client, client_t *to) { SV_FullClientUpdate(client, &svs.clients[i]); } +#ifdef MVD_RECORDING if (sv.mvdrecording) SV_FullClientUpdate(client, &demo.recorder); +#endif return; } @@ -2549,7 +2555,7 @@ client_t *SVC_DirectConnect(void) } } - if (sv.msgfromdemo || net_from.type == NA_LOOPBACK) //normal rules don't apply + if (net_from.type == NA_LOOPBACK) //normal rules don't apply ; else { @@ -2737,9 +2743,6 @@ client_t *SVC_DirectConnect(void) newcl->protocol = protocol; Q_strncpyz(newcl->guid, guid, sizeof(newcl->guid)); - if (sv.msgfromdemo) - newcl->wasrecorded = true; - // Con_TPrintf("%s:%s:connect\n", sv.name, NET_AdrToString (adrbuf, sizeof(adrbuf), &adr)); // if there is already a slot for this ip, drop it @@ -3127,89 +3130,82 @@ client_t *SVC_DirectConnect(void) newcl->lockedtill = 0; #ifdef SVRANKING - if (svs.demorecording || (svs.demoplayback && newcl->wasrecorded)) //disable rankings. Could cock things up. - { - SV_GetNewSpawnParms(newcl); - } - else - { //rankid is figured out in extract from user info - if (!newcl->rankid) //failed to get a userid + if (!newcl->rankid) //failed to get a userid + { + if (rank_needlogin.value) { - if (rank_needlogin.value) - { - SV_RejectMessage (protocol, "Bad password/username\nThis server requires logins. Please see the serverinfo for website and info on how to register.\n"); - newcl->state = cs_free; - return NULL; - } + SV_RejectMessage (protocol, "Bad password/username\nThis server requires logins. Please see the serverinfo for website and info on how to register.\n"); + newcl->state = cs_free; + return NULL; + } // SV_OutOfBandPrintf (isquake2client, adr, "\nWARNING: You have not got a place on the ranking system, probably because a user with the same name has already connected and your pwds differ.\n\n"); - if (!preserveparms) - SV_GetNewSpawnParms(newcl); - } - else + if (!preserveparms) + SV_GetNewSpawnParms(newcl); + } + else + { + rankstats_t rs; + if (!Rank_GetPlayerStats(newcl->rankid, &rs)) { - rankstats_t rs; - if (!Rank_GetPlayerStats(newcl->rankid, &rs)) - { - SV_RejectMessage (protocol, "Rankings/Account system failed\n"); - Con_TPrintf("banned player %s is trying to connect\n", newcl->name); - newcl->name[0] = 0; - InfoBuf_Clear(&newcl->userinfo, true); - newcl->state = cs_free; - return NULL; - } + SV_RejectMessage (protocol, "Rankings/Account system failed\n"); + Con_TPrintf("banned player %s is trying to connect\n", newcl->name); + newcl->name[0] = 0; + InfoBuf_Clear(&newcl->userinfo, true); + newcl->state = cs_free; + return NULL; + } - if (rs.flags1 & RANK_MUTED) - { - SV_BroadcastTPrintf(PRINT_MEDIUM, "%s is muted (still)\n", newcl->name); - } - if (rs.flags1 & RANK_CUFFED) - { - SV_BroadcastTPrintf(PRINT_LOW, "%s is now cuffed permanently\n", newcl->name); - } - if (rs.flags1 & RANK_CRIPPLED) - { - SV_BroadcastTPrintf(PRINT_HIGH, "%s is still crippled\n", newcl->name); - } + if (rs.flags1 & RANK_MUTED) + { + SV_BroadcastTPrintf(PRINT_MEDIUM, "%s is muted (still)\n", newcl->name); + } + if (rs.flags1 & RANK_CUFFED) + { + SV_BroadcastTPrintf(PRINT_LOW, "%s is now cuffed permanently\n", newcl->name); + } + if (rs.flags1 & RANK_CRIPPLED) + { + SV_BroadcastTPrintf(PRINT_HIGH, "%s is still crippled\n", newcl->name); + } - if (rs.timeonserver) - { - if (preserveparms) - { //do nothing. - } - else if (sv_resetparms.value) - { - SV_GetNewSpawnParms(newcl); - } - else - { - extern cvar_t rank_parms_first, rank_parms_last; - for (i=0 ; i= rank_parms_first.ival && i <= rank_parms_last.ival) - newcl->spawn_parms[i] = rs.parm[i]; - else - newcl->spawn_parms[i] = 0; - } - } - - if (rs.timeonserver > 3*60) //woo. Ages. - s = va(langtext("Welcome back %s. You have previously spent %i:%i hours connected\n", newcl->language), newcl->name, (int)(rs.timeonserver/(60*60)), (int)((int)(rs.timeonserver/60)%(60))); - else //measure this guy in minuites. - s = va(langtext("Welcome back %s. You have previously spent %i mins connected\n", newcl->language), newcl->name, (int)(rs.timeonserver/60)); - - SV_OutOfBandPrintf (protocol == SCP_QUAKE2, &adr, s); + if (rs.timeonserver) + { + if (preserveparms) + { //do nothing. } - else if (!preserveparms) + else if (sv_resetparms.value) { SV_GetNewSpawnParms(newcl); - - SV_OutOfBandTPrintf (protocol == SCP_QUAKE2, &adr, newcl->language, "Welcome %s. Your time on this server is being logged and ranked\n", newcl->name, (int)rs.timeonserver); } - //else loaded players already have their initial parms set + else + { + extern cvar_t rank_parms_first, rank_parms_last; + for (i=0 ; i= rank_parms_first.ival && i <= rank_parms_last.ival) + newcl->spawn_parms[i] = rs.parm[i]; + else + newcl->spawn_parms[i] = 0; + } + } + + if (rs.timeonserver > 3*60) //woo. Ages. + s = va(langtext("Welcome back %s. You have previously spent %i:%i hours connected\n", newcl->language), newcl->name, (int)(rs.timeonserver/(60*60)), (int)((int)(rs.timeonserver/60)%(60))); + else //measure this guy in minuites. + s = va(langtext("Welcome back %s. You have previously spent %i mins connected\n", newcl->language), newcl->name, (int)(rs.timeonserver/60)); + + SV_OutOfBandPrintf (protocol == SCP_QUAKE2, &adr, s); } + else if (!preserveparms) + { + SV_GetNewSpawnParms(newcl); + + SV_OutOfBandTPrintf (protocol == SCP_QUAKE2, &adr, newcl->language, "Welcome %s. Your time on this server is being logged and ranked\n", newcl->name, (int)rs.timeonserver); + } + //else loaded players already have their initial parms set } #else // call the progs to get default spawn parms for the new client @@ -3220,44 +3216,28 @@ client_t *SVC_DirectConnect(void) #endif - if (!newcl->wasrecorded) - { - SV_AcceptMessage (newcl); + SV_AcceptMessage (newcl); - newcl->state = cs_free; - if (ISNQCLIENT(newcl)) - { - //FIXME: we should delay this until we actually have a name, because right now they'll be called unnamed or unconnected or something - SV_BroadcastPrintf(PRINT_LOW, "New client connected\n"); - } - else if (redirect) - { - } - else if (newcl->spectator) - { - SV_BroadcastTPrintf(PRINT_LOW, "spectator %s connected\n", newcl->name); + newcl->state = cs_free; + if (ISNQCLIENT(newcl)) + { + //FIXME: we should delay this until we actually have a name, because right now they'll be called unnamed or unconnected or something + SV_BroadcastPrintf(PRINT_LOW, "New client connected\n"); + } + else if (redirect) + { + } + else if (newcl->spectator) + { + SV_BroadcastTPrintf(PRINT_LOW, "spectator %s connected\n", newcl->name); // Con_Printf ("Spectator %s connected\n", newcl->name); - } - else - { - SV_BroadcastTPrintf(PRINT_LOW, "client %s connected\n", newcl->name); -// Con_DPrintf ("Client %s connected\n", newcl->name); - } - newcl->state = cs_connected; } else { - if (newcl->spectator) - { - SV_BroadcastTPrintf(PRINT_LOW, "recorded spectator %s connected\n", newcl->name); -// Con_Printf ("Recorded spectator %s connected\n", newcl->name); - } - else - { - SV_BroadcastTPrintf(PRINT_LOW, "recorded client %s connected\n", newcl->name); -// Con_DPrintf ("Recorded client %s connected\n", newcl->name); - } + SV_BroadcastTPrintf(PRINT_LOW, "client %s connected\n", newcl->name); +// Con_DPrintf ("Client %s connected\n", newcl->name); } + newcl->state = cs_connected; newcl->sendinfo = true; if (redirect) @@ -3361,7 +3341,7 @@ int Rcon_Validate (void) const size_t digestsize = 20; size_t i, k; - unsigned char digest[digestsize]; + unsigned char digest[512]; const unsigned char **tokens = alloca(sizeof(*tokens)*(Cmd_Argc()*2+5)); //overallocation in case argc is 0. size_t *toksizes = alloca(sizeof(*toksizes)*(Cmd_Argc()*2+5)); //overallocation in case argc is 0. if (strlen(pass) > digestsize*2) @@ -4778,11 +4758,13 @@ static void SV_PauseChanged(void) ClientReliableWrite_Byte (cl, sv.paused!=0); } } +#ifdef MVD_RECORDING if (sv.mvdrecording) { ClientReliableWrite_Begin (&demo.recorder, svc_setpause, 2); ClientReliableWrite_Byte (&demo.recorder, sv.paused!=0); } +#endif } /* @@ -5046,10 +5028,9 @@ float SV_Frame (void) // send messages back to the clients that had packets read this frame SV_SendClientMessages (); -// demo_start = Sys_DoubleTime (); +#ifdef MVD_RECORDING SV_SendMVDMessage(); -// demo_end = Sys_DoubleTime (); -// svs.stats.demo += demo_end - demo_start; +#endif // send a heartbeat to the master if needed SV_Master_Heartbeat (); @@ -5097,8 +5078,10 @@ static void SV_InfoChanged(void *context, const char *key) if (context != &svs.info && *key == '_') return; //these keys are considered private to originating client/server, and are not broadcast to anyone else - if (svs.demorecording) +#ifdef MVD_RECORDING + if (sv.mvdrecording) InfoSync_Add(&demo.recorder.infosync, context, key); //make sure it gets written into mvds too. +#endif for (i = 0; i < svs.allocated_client_slots; i++) { if (svs.clients[i].state >= cs_connected) @@ -5289,7 +5272,9 @@ void SV_InitLocal (void) Cmd_AddCommandAD ("load", SV_Loadgame_f, SV_Savegame_c, "Loads an existing saved game."); #endif +#ifdef MVD_RECORDING SV_MVDInit(); +#endif svs.info.ChangeCB = SV_InfoChanged; svs.info.ChangeCTX = &svs.info; diff --git a/engine/server/sv_mvd.c b/engine/server/sv_mvd.c index 44f24ef27..9e0cc8dc3 100644 --- a/engine/server/sv_mvd.c +++ b/engine/server/sv_mvd.c @@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" +#ifdef MVD_RECORDING #ifndef CLIENTONLY #include "winquake.h" @@ -26,16 +27,19 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "netinc.h" - void SV_MVDStop_f (void); #define demo_size_padding 0x1000 static void QDECL SV_DemoDir_Callback(struct cvar_s *var, char *oldvalue); +cvar_t sv_demoAutoRecord = CVARAD("sv_demoAutoRecord", "0", "cl_autodemo", "If set, automatically record demos.\n-1: record on client connection.\n1+: record once there's this many active players on the server."); cvar_t sv_demoUseCache = CVARD("sv_demoUseCache", "", "If set, demo data will be flushed only periodically"); cvar_t sv_demoCacheSize = CVAR("sv_demoCacheSize", "0x80000"); //half a meg -cvar_t sv_demoMaxDirSize = CVARD("sv_demoMaxDirSize", "102400", "Maximum allowed serverside storage for mvds. set to blank to remove the limit. New demos cannot be recorded once this reaches 0."); //so ktpro autorecords. +cvar_t sv_demoMaxDirSize = CVARD("sv_demoMaxDirSize", "100mb", "Maximum allowed serverside storage space for mvds. set to blank to remove the limit. New demos cannot be recorded once this size is reached."); //so ktpro autorecords. +cvar_t sv_demoMaxDirCount = CVARD("sv_demoMaxDirCount", "500", "Maximum allowed serverside mvds to record. Set to 0 to remove the limit. New demos cannot be recorded once this many demos have already been recorded."); //so ktpro autorecords. +cvar_t sv_demoMaxDirAge = CVARD("sv_demoMaxDirAge", "0", "Maximum allowed age for demos, any older demos will be deleted when sv_demoClearOld is set (this doesn't prevent recording new demos)."); +cvar_t sv_demoClearOld = CVARD("sv_demoClearOld", "0", "Automatically delete demos to keep the demos count reasonable."); cvar_t sv_demoDir = CVARC("sv_demoDir", "demos", SV_DemoDir_Callback); cvar_t sv_demofps = CVAR("sv_demofps", "30"); cvar_t sv_demoPings = CVARD("sv_demoPings", "10", "Interval between ping updates in mvds"); @@ -49,6 +53,7 @@ cvar_t qtv_password = CVAR( "qtv_password", ""); cvar_t qtv_maxstreams = CVARAFD( "qtv_maxstreams", "0", "mvd_maxstreams", 0, "This is the maximum number of QTV clients/proxies that may be directly connected to the server. If empty then there is no limit. 0 disallows any streaming."); +cvar_t sv_demoAutoPrefix = CVAR("sv_demoAutoPrefix", "auto_"); cvar_t sv_demoPrefix = CVAR("sv_demoPrefix", ""); cvar_t sv_demoSuffix = CVAR("sv_demoSuffix", ""); cvar_t sv_demotxt = CVAR("sv_demotxt", "1"); @@ -656,20 +661,20 @@ void DemoWriteQTVTimePad(int msecs) //broadcast to all proxies // returns the file size // return -1 if file is not present // the file should be in BINARY mode for stupid OSs that care -#define MAX_DIRFILES 1000 #define MAX_MVD_NAME 64 typedef struct { char name[MAX_MVD_NAME]; - int size; + qofs_t size; time_t mtime; + searchpathfuncs_t *path; } file_t; typedef struct { file_t *files; - int size; + qofs_t size; int numfiles; int numdirs; @@ -690,11 +695,19 @@ static int QDECL Sys_listdirFound(const char *fname, qofs_t fsize, time_t mtime, return true; } if (dir->numfiles == dir->maxfiles) - return true; + { + int nc = dir->numfiles + 256; + file_t *n = realloc(dir->files, nc*sizeof(*dir->files)); + if (!n) + return false; + dir->files = n; + dir->maxfiles = nc; + } f = &dir->files[dir->numfiles++]; Q_strncpyz(f->name, fname, sizeof(f->name)); f->size = fsize; f->mtime = mtime; + f->path = spath; dir->size += fsize; return true; @@ -712,22 +725,29 @@ static int QDECL Sys_listdir_Sort(const void *va, const void *vb) return -1; } -static dir_t *Sys_listdir (char *path, char *ext, qboolean usesorting) +static dir_t *Sys_listdemos (char *path, int ispublic, qboolean usesorting) { + const char *exts[] = { + ".mvd", ".mvd.gz", + ".qwz", ".qwz.gz", +#ifdef NQPROT + ".dem", ".dem.gz", +#endif +#if defined(Q2SERVER) || defined(Q2CLIENT) + ".dm2", ".dm2.gz" +#endif + }; char searchterm[MAX_QPATH]; + size_t i; - unsigned int maxfiles = MAX_DIRFILES; - dir_t *dir = malloc(sizeof(*dir) + sizeof(*dir->files)*maxfiles); + dir_t *dir = malloc(sizeof(*dir)); memset(dir, 0, sizeof(*dir)); - dir->files = (file_t*)(dir+1); - dir->maxfiles = maxfiles; + dir->files = NULL; + dir->maxfiles = 0; - Q_strncpyz(searchterm, va("%s/*%s", path, ext), sizeof(searchterm)); - COM_EnumerateFiles(searchterm, Sys_listdirFound, dir); - - if (!strcmp(ext, ".mvd")) + for (i = 0; i < (ispublic?2:countof(exts)); i++) { - Q_strncpyz(searchterm, va("%s/*%s.gz", path, ext), sizeof(searchterm)); + Q_strncpyz(searchterm, va("%s/*%s", path, exts[i]), sizeof(searchterm)); COM_EnumerateFiles(searchterm, Sys_listdirFound, dir); } @@ -738,6 +758,8 @@ static dir_t *Sys_listdir (char *path, char *ext, qboolean usesorting) } static void Sys_freedir(dir_t *dir) { + if (dir) + free(dir->files); free(dir); } @@ -1175,6 +1197,9 @@ void MVD_Init (void) Cvar_Register (&sv_demoCacheSize, MVDVARGROUP); Cvar_Register (&sv_demoMaxSize, MVDVARGROUP); Cvar_Register (&sv_demoMaxDirSize, MVDVARGROUP); + Cvar_Register (&sv_demoMaxDirCount, MVDVARGROUP); + Cvar_Register (&sv_demoMaxDirAge, MVDVARGROUP); + Cvar_Register (&sv_demoClearOld, MVDVARGROUP); Cvar_Register (&sv_demoDir, MVDVARGROUP); Cvar_Register (&sv_demoPrefix, MVDVARGROUP); Cvar_Register (&sv_demoSuffix, MVDVARGROUP); @@ -1182,6 +1207,8 @@ void MVD_Init (void) Cvar_Register (&sv_demoExtraNames, MVDVARGROUP); Cvar_Register (&sv_demoExtensions, MVDVARGROUP); Cvar_Register (&sv_demoAutoCompress,MVDVARGROUP); + Cvar_Register (&sv_demoAutoRecord, MVDVARGROUP); + Cvar_Register (&sv_demoAutoPrefix, MVDVARGROUP); Cvar_Register (&sv_demo_write_csqc,MVDVARGROUP); } @@ -1261,6 +1288,7 @@ mvddest_t *SV_FindRecordFile(char *match, mvddest_t ***link_out) } return NULL; } + /* ==================== SV_InitRecord @@ -1269,12 +1297,10 @@ SV_InitRecord mvddest_t *SV_MVD_InitRecordFile (char *name) { - char *s; + char *s, *txtname; mvddest_t *dst; vfsfile_t *file; - char path[MAX_OSPATH]; - if (strlen(name) >= countof(dst->filename)) { Con_Printf ("ERROR: couldn't open \"%s\". Too long.\n", name); @@ -1289,7 +1315,7 @@ mvddest_t *SV_MVD_InitRecordFile (char *name) } #ifdef AVAIL_GZDEC - if (!Q_strcasecmp("gz", COM_FileExtension(name, path, sizeof(path)))) + if (!Q_strcasecmp(".gz", COM_GetFileExtension(name, NULL))) file = FS_GZ_WriteFilter(file, true, true); #endif @@ -1344,7 +1370,8 @@ mvddest_t *SV_MVD_InitRecordFile (char *name) SV_BroadcastPrintf (PRINT_CHAT, "Server starts recording (%s):\n%s\n", "memory", name); break; case DEST_THREADEDFILE: - SV_BroadcastPrintf (PRINT_CHAT, "Server starts recording (%s):\n%s\n", "worker thread", name); + //SV_BroadcastPrintf (PRINT_CHAT, "Server starts recording (%s):\n%s\n", "worker thread", name); + SV_BroadcastPrintf (PRINT_CHAT, "Server starts recording:\n%s\n", name); break; case DEST_FILE: SV_BroadcastPrintf (PRINT_CHAT, "Server starts recording (%s):\n%s\n", "disk", name); @@ -1352,31 +1379,39 @@ mvddest_t *SV_MVD_InitRecordFile (char *name) } Cvar_ForceSet(Cvar_Get("serverdemo", "", CVAR_NOSET, ""), SV_Demo_CurrentOutput()); - Q_strncpyz(path, name, MAX_OSPATH); - Q_strncpyz(path + strlen(path) - 3, "txt", MAX_OSPATH - strlen(path) + 3); - + txtname = SV_MVDName2Txt(name); if (sv_demotxt.value) { vfsfile_t *f; - f = FS_OpenVFS (path, "wt", FS_GAMEONLY); - if (f != NULL) + if (sv_demotxt.value == 2) { - char buf[2000]; - date_t date; + //this is a special mode for mods that want to write it instead (done via the sv_demoinfoadd command). + f = FS_OpenVFS (txtname, "wt", FS_GAMEONLY); + if (f) + VFS_CLOSE(f); + } + else + { + f = FS_OpenVFS (txtname, "wt", FS_GAMEONLY); + if (f != NULL) + { + char buf[2000]; + date_t date; - COM_TimeOfDay(&date); + COM_TimeOfDay(&date); - snprintf(buf, sizeof(buf), "date %s\nmap %s\nteamplay %d\ndeathmatch %d\ntimelimit %d\n%s",date.str, svs.name, (int)teamplay.value, (int)deathmatch.value, (int)timelimit.value, SV_PrintTeams()); - VFS_WRITE(f, buf, strlen(buf)); - VFS_FLUSH(f); - VFS_CLOSE(f); + snprintf(buf, sizeof(buf), "date %s\nmap %s\nteamplay %d\ndeathmatch %d\ntimelimit %d\n%s",date.str, svs.name, (int)teamplay.value, (int)deathmatch.value, (int)timelimit.value, SV_PrintTeams()); + VFS_WRITE(f, buf, strlen(buf)); + VFS_FLUSH(f); + VFS_CLOSE(f); + } } } else { - FS_Remove(path, FS_GAMEONLY); - FS_FlushFSHashRemoved(path); + FS_Remove(txtname, FS_GAMEONLY); + FS_FlushFSHashRemoved(txtname); } return dst; @@ -1607,13 +1642,8 @@ qboolean SV_MVD_Record (mvddest_t *dest) } //pointless extensions that are redundant with mvds - demo.recorder.fteprotocolextensions &= ~PEXT_ACCURATETIMINGS | PEXT_HLBSP | PEXT_CHUNKEDDOWNLOADS; -#ifdef PEXT_Q2BSP - demo.recorder.fteprotocolextensions &= ~PEXT_Q2BSP; -#endif -#ifdef PEXT_Q3BSP - demo.recorder.fteprotocolextensions &= ~PEXT_Q3BSP; -#endif + demo.recorder.fteprotocolextensions &= ~PEXT_ACCURATETIMINGS | PEXT_CHUNKEDDOWNLOADS; + demo.recorder.fteprotocolextensions &= ~PEXT1_HIDEPROTOCOLS; } // else // SV_WriteRecordMVDMessage(&buf, dem_read); @@ -1917,6 +1947,86 @@ char *SV_CleanName (unsigned char *name) return out; } +//figure out the actual size limit. this is somewhat approximate anyway. +qofs_t MVD_DemoMaxDirSize(void) +{ + char *e; + double maxdirsize = strtod(sv_demoMaxDirSize.string, &e); + if (*e == ' ' || *e == '\t') + e++; + //that will be trailed by g[b], m[b], k[b], or b + if (*e == 'b' || *e == 'B') + return maxdirsize; + else if (*e == 'k' || *e == 'K') + return maxdirsize * 1024; + else if (*e == 'm' || *e == 'M') + return maxdirsize * 1024*1024; + else if (*e == 'g' || *e == 'G') + return maxdirsize * 1024*1024*1024; + else + return maxdirsize * 1024; //assume kb. +} +//returns if there's enough disk space to record another demo. +qboolean MVD_CheckSpace(qboolean broadcastwarnings) +{ + dir_t *dir; + + qofs_t maxdirsize = MVD_DemoMaxDirSize(); + if (maxdirsize > 0 || sv_demoMaxDirCount.ival > 0 || sv_demoMaxDirAge.ival > 0) + { + dir = Sys_listdemos(sv_demoDir.string, false, SORT_BY_DATE); + if (sv_demoClearOld.ival && *sv_demoDir.string) + { + time_t removebeforetime = time(NULL) - sv_demoMaxDirAge.value*60*60*24; + while (dir->numfiles && ( + (maxdirsize>0 && dir->size > maxdirsize) || + (sv_demoMaxDirCount.ival>0 && dir->numfiles >= sv_demoMaxDirCount.ival) || + (sv_demoMaxDirAge.ival && dir->files[dir->numfiles-1].mtime && dir->files[dir->numfiles-1].mtime - removebeforetime < 0))) + { + file_t *f = &dir->files[dir->numfiles-1]; //this is the file we want to kill. + if (!f->path || !f->path->RemoveFile) + { //erm, can't remove it... + dir->size -= f->size; + dir->numfiles--; + continue; + } + if (f->path->RemoveFile(f->path, f->name)) + { //okay, looks like we managed to kill it. + Con_Printf(CON_WARNING"Removed demo \"%s\"\n", f->name); + dir->size -= f->size; + dir->numfiles--; + + //Try to take the .txt too. + f->path->RemoveFile(f->path, SV_MVDName2Txt(f->name)); + continue; + } + } + } + + if (dir->numfiles && sv_demoMaxDirCount.ival>0 && dir->numfiles >= sv_demoMaxDirCount.ival) + { + if (broadcastwarnings) + SV_BroadcastPrintf(PRINT_MEDIUM, CON_WARNING"insufficient directory space, increase server's sv_demoMaxDirCount\n"); + else + Con_Printf(CON_WARNING"insufficient demo space, increase sv_demoMaxDirCount\n"); + Sys_freedir(dir); + return false; + } + if (dir->numfiles && maxdirsize>0 && dir->size > maxdirsize) + { + if (broadcastwarnings) + SV_BroadcastPrintf(PRINT_MEDIUM, CON_WARNING"insufficient directory space, increase server's sv_demoMaxDirSize\n"); + else + Con_Printf(CON_WARNING"insufficient demo space, increase sv_demoMaxDirSize\n"); + Sys_freedir(dir); + return false; + } + + Sys_freedir(dir); + } + return true; +} + /* ==================== SV_Record_f @@ -1929,7 +2039,6 @@ void SV_MVD_Record_f (void) int c; char name[MAX_OSPATH+MAX_MVD_NAME]; char newname[MAX_MVD_NAME]; - dir_t *dir; c = Cmd_Argc(); if (c != 2) @@ -1943,15 +2052,8 @@ void SV_MVD_Record_f (void) return; } - dir = Sys_listdir(sv_demoDir.string, ".*", SORT_NO); - if (sv_demoMaxDirSize.value && dir->size > sv_demoMaxDirSize.value*1024) - { - Con_Printf("insufficient directory space, increase sv_demoMaxDirSize\n"); - Sys_freedir(dir); + if (!MVD_CheckSpace(Cmd_FromGamecode())) return; - } - Sys_freedir(dir); - dir = NULL; Q_strncpyz(newname, va("%s%s", sv_demoPrefix.string, SV_CleanName(Cmd_Argv(1))), sizeof(newname) - strlen(sv_demoSuffix.string) - 5); @@ -1975,6 +2077,49 @@ void SV_MVD_Record_f (void) SV_MVD_Record (SV_MVD_InitRecordFile(name)); } +//called when a connecting player becomes active. +void SV_MVD_AutoRecord (void) +{ + //not enabled (for the server) anyway. + if (sv_demoAutoRecord.ival <= 0) + return; + + //don't record multiple... + if (sv.mvdrecording) + return; + + //only do it if we're underneath our quotas. + if (!MVD_CheckSpace(true)) + return; + + if (sv_demoAutoRecord.ival > 0) + { + int playercount = 0, i; + for (i = 0; i < svs.allocated_client_slots; i++) + { + if (svs.clients[i].state >= cs_spawned) + playercount++; + } + if (playercount >= sv_demoAutoRecord.ival) + { //okay, we've reached our player count, its time to start recording now. + char name[MAX_OSPATH]; + char timestamp[64]; + time_t tm = time(NULL); + strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", localtime(&tm)); + Q_snprintfz(name, sizeof(name), "%s/%s%s_%s", sv_demoDir.string, sv_demoAutoPrefix.string, svs.name, timestamp); +#ifdef AVAIL_GZDEC + if (sv_demoAutoCompress.ival == 1 || !*sv_demoAutoCompress.string) //default is to gzip. + Q_strncatz(name, ".mvd.gz", sizeof(name)); + else +#endif + Q_strncatz(name, ".mvd", sizeof(name)); + FS_CreatePath (name, FS_GAMEONLY); + + SV_MVD_Record (SV_MVD_InitRecordFile(name)); + } + } +} + void SV_MVD_QTVReverse_f (void) { #if 1//ndef HAVE_TCP @@ -2170,7 +2315,6 @@ int Dem_CountTeamPlayers (char *t) void SV_MVDEasyRecord_f (void) { int c; - dir_t *dir; char name[1024]; char name2[MAX_OSPATH*7]; // scream //char name2[MAX_OSPATH*2]; @@ -2190,14 +2334,8 @@ void SV_MVDEasyRecord_f (void) return; } - dir = Sys_listdir(sv_demoDir.string, ".*", SORT_NO); - if (sv_demoMaxDirSize.value && dir->size > sv_demoMaxDirSize.value*1024) - { - Con_Printf("insufficient directory space, increase sv_demoMaxDirSize\n"); - Sys_freedir(dir); + if (!MVD_CheckSpace(Cmd_FromGamecode())) return; - } - Sys_freedir(dir); if (c == 2) { @@ -2286,9 +2424,10 @@ void SV_MVDList_f (void) file_t *list; float f; int i,j,show; + qofs_t maxdirsize = MVD_DemoMaxDirSize(); Con_Printf("content of %s/*.mvd\n", sv_demoDir.string); - dir = Sys_listdir(sv_demoDir.string, ".mvd", SORT_BY_DATE); + dir = Sys_listdemos(sv_demoDir.string, true, SORT_BY_DATE); list = dir->files; if (!list->name[0]) { @@ -2307,10 +2446,10 @@ void SV_MVDList_f (void) for (d = demo.dest; d; d = d->nextdest) { if (d->desttype != DEST_STREAM && !strcmp(list->name, d->simplename)) - Con_Printf("*%d: ^[^7%s\\demo\\%s/%s^] %dk\n", i, list->name, sv_demoDir.string, list->name, d->totalsize/1024); + Con_Printf("*%d: ^[^7%s\\demo\\%s/%s^] %uk\n", i, list->name, sv_demoDir.string, list->name, (unsigned int)(d->totalsize/1024)); } if (!d) - Con_Printf("%d: ^[^7%s\\demo\\%s/%s^] %dk\n", i, list->name, sv_demoDir.string, list->name, list->size/1024); + Con_Printf("%d: ^[^7%s\\demo\\%s/%s^] %uk\n", i, list->name, sv_demoDir.string, list->name, (unsigned int)(list->size/1024)); } } @@ -2318,9 +2457,9 @@ void SV_MVDList_f (void) dir->size += d->totalsize; Con_Printf("\ndirectory size: %.1fMB\n",(float)dir->size/(1024*1024)); - if (sv_demoMaxDirSize.value) + if (maxdirsize) { - f = (sv_demoMaxDirSize.value*1024 - dir->size)/(1024*1024); + f = (maxdirsize - dir->size)/(1024*1024); if ( f < 0) f = 0; Con_Printf("space available: %.1fMB\n", f); @@ -2337,9 +2476,10 @@ void SV_UserCmdMVDList_f (void) file_t *list; float f; int i,j,show; + qofs_t maxdirsize = MVD_DemoMaxDirSize(); SV_ClientPrintf(host_client, PRINT_HIGH, "available demos:\n"); - dir = Sys_listdir(sv_demoDir.string, ".mvd", SORT_BY_DATE); + dir = Sys_listdemos(sv_demoDir.string, true, SORT_BY_DATE); list = dir->files; if (!list->name[0]) { @@ -2363,23 +2503,26 @@ void SV_UserCmdMVDList_f (void) if (!d) { 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); + SV_ClientPrintf(host_client, PRINT_HIGH, "%d: ^[%s\\type\\/download demos/%s^] %dk\n", i, list->name, list->name, (unsigned int)(list->size/1024)); else - SV_ClientPrintf(host_client, PRINT_HIGH, "%d: %s %dk\n", i, list->name, list->size/1024); + SV_ClientPrintf(host_client, PRINT_HIGH, "%d: %s %dk\n", i, list->name, (unsigned int)(list->size/1024)); } } - if (host_client->num_backbuf == MAX_BACK_BUFFERS) + if (host_client->num_backbuf >= MAX_BACK_BUFFERS/2) + { SV_ClientPrintf(host_client, PRINT_HIGH, "*MORE*\n"); + break; + } } for (d = demo.dest; d; d = d->nextdest) dir->size += d->totalsize; SV_ClientPrintf(host_client, PRINT_HIGH, "\ndirectory size: %.1fMB\n",(float)dir->size/(1024*1024)); - if (sv_demoMaxDirSize.value) + if (maxdirsize) { - f = (sv_demoMaxDirSize.value*1024 - dir->size)/(1024*1024); + f = (maxdirsize - dir->size)/(1024*1024); if ( f < 0) f = 0; SV_ClientPrintf(host_client, PRINT_HIGH, "space available: %.1fMB\n", f); @@ -2395,6 +2538,7 @@ void SV_UserCmdMVDList_HTML (vfsfile_t *pipe) file_t *list; float f; int i; + qofs_t maxdirsize = MVD_DemoMaxDirSize(); VFS_PRINTF(pipe, "" @@ -2418,7 +2562,7 @@ void SV_UserCmdMVDList_HTML (vfsfile_t *pipe) , fs_manifest->formalname, hostname.string); VFS_PRINTF(pipe, "available demos:
\n"); - dir = Sys_listdir(sv_demoDir.string, ".mvd", SORT_BY_DATE); + dir = Sys_listdemos(sv_demoDir.string, true, SORT_BY_DATE); list = dir->files; if (!list->name[0]) { @@ -2436,7 +2580,7 @@ void SV_UserCmdMVDList_HTML (vfsfile_t *pipe) { char datetime[64]; strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S", localtime(&list->mtime)); - VFS_PRINTF(pipe, "%d: %s %dk play %s
\n", i, list->name, list->name, list->size/1024, list->name, datetime); + VFS_PRINTF(pipe, "%d: %s %uk play %s
\n", i, list->name, list->name, (unsigned int)(list->size/1024), list->name, datetime); } } @@ -2444,9 +2588,9 @@ void SV_UserCmdMVDList_HTML (vfsfile_t *pipe) dir->size += d->totalsize; VFS_PRINTF(pipe, "
\ndirectory size: %.1fMB
\n",(float)dir->size/(1024*1024)); - if (sv_demoMaxDirSize.value) + if (maxdirsize) { - f = (sv_demoMaxDirSize.value*1024 - dir->size)/(1024*1024); + f = (maxdirsize - dir->size)/(1024*1024); if ( f < 0) f = 0; VFS_PRINTF(pipe, "space available: %.1fMB
\n", f); @@ -2479,7 +2623,7 @@ char *SV_MVDNum(char *buffer, int bufferlen, int num) //lame number->name lookup file_t *list; dir_t *dir; - dir = Sys_listdir(sv_demoDir.string, ".mvd", SORT_BY_DATE); + dir = Sys_listdemos(sv_demoDir.string, true, SORT_BY_DATE); list = dir->files; if (num < 0) @@ -2501,16 +2645,23 @@ char *SV_MVDNum(char *buffer, int bufferlen, int num) //lame number->name lookup char *SV_MVDName2Txt(char *name) { char s[MAX_OSPATH]; + const char *ext; if (!name) return NULL; Q_strncpyz(s, name, MAX_OSPATH); - if (strstr(s, ".mvd.gz") != NULL) - Q_strncpyz(s + strlen(s) - 6, "txt", MAX_OSPATH - strlen(s) + 6); - else - Q_strncpyz(s + strlen(s) - 3, "txt", MAX_OSPATH - strlen(s) + 3); + ext = COM_GetFileExtension(s, NULL); + if (!Q_strcasecmp(ext, ".gz")) + ext = COM_GetFileExtension(s, ext); + else if (!Q_strcasecmp(ext, ".xz")) + ext = COM_GetFileExtension(s, ext); + if (!ext || !*ext) //if there's no extension on there, then make sure we're pointing to the end of the string. + ext = s+strlen(s); + if (ext > s+sizeof(s)+4) //make sure we don't overflow the buffer by truncating the base/path, ensuring that we don't write some other type of file. + ext = s+sizeof(s)+4; //should probably make this an error case and abort instead. + strcpy((char*)ext, ".txt"); return va("%s", s); } @@ -2529,7 +2680,7 @@ void SV_MVDRemove_f (void) if (Cmd_Argc() != 2) { - Con_Printf("rmdemo - removes the demo\nrmdemo * - removes demo with in the name\nrmdemo * - removes all demos\n"); + Con_Printf("%s - removes the demo\nrmdemo * - removes demo with in the name\nrmdemo * - removes all demos\n", Cmd_Argv(0)); return; } @@ -2542,7 +2693,7 @@ void SV_MVDRemove_f (void) // remove all demos with specified token ptr++; - dir = Sys_listdir(sv_demoDir.string, ".mvd", SORT_BY_DATE); + dir = Sys_listdemos(sv_demoDir.string, true, SORT_BY_DATE); list = dir->files; for (i = 0;i < dir->numfiles; list++) { @@ -2605,14 +2756,14 @@ void SV_MVDRemoveNum_f (void) if (Cmd_Argc() != 2) { - Con_Printf("rmdemonum <#>\n"); + Con_Printf("%s <#>\n", Cmd_Argv(0)); return; } val = Cmd_Argv(1); if ((num = atoi(val)) == 0 && val[0] != '0') { - Con_Printf("rmdemonum <#>\n"); + Con_Printf("%s <#>\n", Cmd_Argv(0)); return; } @@ -2644,12 +2795,13 @@ void SV_MVDInfoAdd_f (void) char *name, *args, path[MAX_OSPATH]; vfsfile_t *f; + //** is a special hack for ktx if (Cmd_Argc() < 3) { - Con_Printf("usage:MVDInfoAdd \n = * for currently recorded demo\n"); + Con_Printf("%s \n = * for currently recorded demo\n", Cmd_Argv(0)); return; } - if (!strcmp(Cmd_Argv(1), "*")) + if (!strcmp(Cmd_Argv(1), "*") || !strcmp(Cmd_Argv(1), "**")) { mvddest_t *active = SV_FindRecordFile(NULL, NULL); if (!active) @@ -2673,19 +2825,34 @@ void SV_MVDInfoAdd_f (void) snprintf(path, MAX_OSPATH, "%s/%s", sv_demoDir.string, name); } - if ((f = FS_OpenVFS(path, "a+t", FS_GAMEONLY)) == NULL) + if ((f = FS_OpenVFS(path, "ab", FS_GAMEONLY)) == NULL) { - Con_Printf("failed to open the file\n"); + Con_Printf("%s: failed to open \"%s\"\n", Cmd_Argv(0), path); return; } - // skip demonum - args = Cmd_Args(); - while (*args > 32) args++; - while (*args && *args <= 32) args++; + if (!strcmp(Cmd_Argv(1), "**")) + { + size_t fsize; + args = FS_LoadMallocFile(Cmd_Argv(2), &fsize); + if (args) + { + VFS_WRITE(f, args, fsize); + FS_FreeFile(args); + } + else + Con_Printf("%s: failed to open input file\n", Cmd_Argv(0)); + } + else + { + // skip demonum + args = Cmd_Args(); + while (*args > 32) args++; + while (*args && *args <= 32) args++; - VFS_WRITE(f, args, strlen(args)); - VFS_WRITE(f, "\n", 1); + VFS_WRITE(f, args, strlen(args)); + VFS_WRITE(f, "\n", 1); + } VFS_FLUSH(f); VFS_CLOSE(f); } @@ -2697,7 +2864,7 @@ void SV_MVDInfoRemove_f (void) if (Cmd_Argc() < 2) { - Con_Printf("usage:demoInfoRemove \n = * for currently recorded demo\n"); + Con_Printf("%s \n = * for currently recorded demo\n", Cmd_Argv(0)); return; } @@ -2744,7 +2911,7 @@ void SV_MVDInfo_f (void) if (Cmd_Argc() < 2) { - Con_Printf("usage:demoinfo \n = * for currently recorded demo\n"); + Con_Printf("%s \n = * for currently recorded demo\n", Cmd_Argv(0)); return; } @@ -2806,14 +2973,14 @@ void SV_MVDPlayNum_f(void) if (Cmd_Argc() != 2) { - Con_Printf("mvdplaynum <#>\n"); + Con_Printf("%s <#>\n", Cmd_Argv(0)); return; } val = Cmd_Argv(1); if ((num = atoi(val)) == 0 && val[0] != '0') { - Con_Printf("mvdplaynum <#>\n"); + Con_Printf("%s <#>\n", Cmd_Argv(0)); return; } @@ -2872,3 +3039,4 @@ void SV_MVDInit(void) } #endif +#endif diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index 7320335d0..4d0f4c257 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -501,6 +501,7 @@ void VARGS SV_ClientPrintf (client_t *cl, int level, const char *fmt, ...) if(strlen(string) >= sizeof(string)) Sys_Error("SV_ClientPrintf: Buffer stomped\n"); +#ifdef MVD_RECORDING if (sv.mvdrecording) { sizebuf_t *msg = MVDWrite_Begin (dem_single, cl - svs.clients, strlen(string)+3); @@ -508,6 +509,7 @@ void VARGS SV_ClientPrintf (client_t *cl, int level, const char *fmt, ...) MSG_WriteByte (msg, level); MSG_WriteString (msg, string); } +#endif if (cl->controller) SV_PrintToClient(cl->controller, level, string); @@ -531,6 +533,7 @@ void VARGS SV_ClientTPrintf (client_t *cl, int level, translation_t stringnum, . if(strlen(string) >= sizeof(string)) Sys_Error("SV_ClientTPrintf: Buffer stomped\n"); +#ifdef MVD_RECORDING if (sv.mvdrecording) { sizebuf_t *msg = MVDWrite_Begin (dem_single, cl - svs.clients, strlen(string)+3); @@ -538,6 +541,7 @@ void VARGS SV_ClientTPrintf (client_t *cl, int level, translation_t stringnum, . MSG_WriteByte (msg, level); MSG_WriteString (msg, string); } +#endif SV_PrintToClient(cl, level, string); } @@ -585,6 +589,7 @@ void VARGS SV_BroadcastPrintf (int level, const char *fmt, ...) SV_PrintToClient(cl, level, string); } +#ifdef MVD_RECORDING if (sv.mvdrecording) { sizebuf_t *msg = MVDWrite_Begin (dem_all, 0, strlen(string)+3); @@ -592,6 +597,7 @@ void VARGS SV_BroadcastPrintf (int level, const char *fmt, ...) MSG_WriteByte (msg, level); MSG_WriteString (msg, string); } +#endif } @@ -1136,6 +1142,7 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int } } +#ifdef MVD_RECORDING if (sv.mvdrecording && ((demo.recorder.fteprotocolextensions & with) == with) && !(demo.recorder.fteprotocolextensions & without)) { sizebuf_t *msg; @@ -1184,6 +1191,7 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int } SZ_Write(msg, sv.multicast.data, sv.multicast.cursize); } +#endif #ifdef NQPROT SZ_Clear (&sv.nqmulticast); @@ -1336,6 +1344,7 @@ void SV_MulticastCB(vec3_t origin, multicast_t to, int dimension_mask, void (*ca callback(client, &client->datagram, ctx); } +#ifdef MVD_RECORDING if (sv.mvdrecording) { sizebuf_t *msg; @@ -1388,6 +1397,7 @@ void SV_MulticastCB(vec3_t origin, multicast_t to, int dimension_mask, void (*ca } callback(&demo.recorder, msg, ctx); } +#endif } //version does all the work now @@ -1803,12 +1813,14 @@ void SV_WriteCenterPrint(client_t *cl, char *s) } ClientReliableWrite_String (cl, s); +#ifdef MVD_RECORDING if (sv.mvdrecording) { sizebuf_t *msg = MVDWrite_Begin (dem_single, cl - svs.clients, 2 + strlen(s)); MSG_WriteByte (msg, svc_centerprint); MSG_WriteString (msg, s); } +#endif } /* @@ -2928,8 +2940,10 @@ void SV_FlushBroadcasts (void) } } +#ifdef MVD_RECORDING if (sv.mvdrecording) SV_MVD_WriteReliables(true); +#endif SZ_Clear (&sv.reliable_datagram); SZ_Clear (&sv.datagram); @@ -3126,6 +3140,7 @@ void SV_UpdateToReliableMessages (void) ClientReliableWrite_Short(client, host_client->edict->v->frags); } +#ifdef MVD_RECORDING if (sv.mvdrecording) { sizebuf_t *msg = MVDWrite_Begin(dem_all, 0, 4); @@ -3133,6 +3148,7 @@ void SV_UpdateToReliableMessages (void) MSG_WriteByte(msg, i); MSG_WriteShort(msg, host_client->edict->v->frags); } +#endif host_client->old_frags = host_client->edict->v->frags; } @@ -3194,6 +3210,7 @@ void SV_UpdateToReliableMessages (void) ClientReliableWrite_Short(client, curfrags); } +#ifdef MVD_RECORDING if (sv.mvdrecording) { sizebuf_t *msg = MVDWrite_Begin(dem_all, 0, 4); @@ -3201,6 +3218,7 @@ void SV_UpdateToReliableMessages (void) MSG_WriteByte(msg, i); MSG_WriteShort(msg, curfrags); } +#endif host_client->old_frags = curfrags; } @@ -3384,6 +3402,7 @@ void SV_BroadcastUserinfoChange(client_t *about, qboolean isbasic, const char *k SV_SendUserinfoChange(client, about, isbasic, key, newval); } +#ifdef MVD_RECORDING if (sv.mvdrecording && (isbasic || (demo.recorder.fteprotocolextensions & PEXT_BIGUSERINFOS))) { sizebuf_t *msg = MVDWrite_Begin (dem_all, 0, strlen(key)+strlen(newval)+4); @@ -3392,6 +3411,7 @@ void SV_BroadcastUserinfoChange(client_t *about, qboolean isbasic, const char *k MSG_WriteString (msg, key); MSG_WriteString (msg, newval); } +#endif } /* @@ -3470,13 +3490,6 @@ void SV_SendClientMessages (void) SV_ChatThink(c); #endif - if (c->wasrecorded) - { - c->netchan.message.cursize = 0; - c->datagram.cursize = 0; - continue; - } - #ifdef NEWSPEEDCHEATPROT //allow the client more time for client movement. //if they're running too slowly, FORCE them to run @@ -3665,8 +3678,10 @@ void SV_SendClientMessages (void) } c->lastoutgoingphysicstime = sv.world.physicstime; } +#ifdef MVD_RECORDING if (sv.mvdrecording) SV_ProcessSendFlags(&demo.recorder); +#endif SV_CleanupEnts(); } @@ -3674,6 +3689,7 @@ void SV_SendClientMessages (void) //#pragma optimize( "", on ) //#endif +#ifdef MVD_RECORDING void SV_WriteMVDMessage (sizebuf_t *msg, int type, int to, float time); void DemoWriteQTVTimePad(int msecs); @@ -3871,7 +3887,7 @@ void SV_SendMVDMessage(void) // MVDSetMsgBuf(demo.dbuf,&demo.frames[demo.parsecount&DEMO_FRAMES_MASK].buf); } - +#endif /* diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index a1bbd0502..4afad0746 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -1050,19 +1050,30 @@ void SV_SendClientPrespawnInfo(client_t *client) else if (client->prespawn_idx == 4) { int track = 0; + const char *noise = ""; if (progstype == PROG_H2) + { track = sv.h2cdtrack; //hexen2 has a special hack + } else if (svprogfuncs) + { track = ((edict_t*)sv.world.edicts)->v->sounds; + noise = PR_GetString(svprogfuncs, ((edict_t*)sv.world.edicts)->v->noise); + } - ClientReliableWrite_Begin(client, svc_cdtrack, 2); - ClientReliableWrite_Byte (client, track); - if (ISNQCLIENT(client)) + if (track == -1 && *noise) + SV_StuffcmdToClient(client, va("cd loop \"%s\"\n", noise)); + else + { + ClientReliableWrite_Begin(client, svc_cdtrack, 2); ClientReliableWrite_Byte (client, track); + if (ISNQCLIENT(client)) + ClientReliableWrite_Byte (client, track); - if (!track && *sv.h2miditrack) - SV_StuffcmdToClient(client, va("music \"%s\"\n", sv.h2miditrack)); + if (!track && *sv.h2miditrack) + SV_StuffcmdToClient(client, va("music \"%s\"\n", sv.h2miditrack)); + } } else if (client->prespawn_idx == 5) { @@ -1080,6 +1091,7 @@ void SV_SendClientPrespawnInfo(client_t *client) } } +#ifdef MVD_RECORDING if (client->prespawn_stage == PRESPAWN_CSPROGS) { extern cvar_t sv_demo_write_csqc; @@ -1144,6 +1156,7 @@ void SV_SendClientPrespawnInfo(client_t *client) client->prespawn_stage++; client->prespawn_idx = 0; } +#endif if (client->prespawn_stage == PRESPAWN_SOUNDLIST) { @@ -1218,6 +1231,7 @@ void SV_SendClientPrespawnInfo(client_t *client) } } +#ifndef NOLEGACY if (client->prespawn_stage == PRESPAWN_VWEPMODELLIST) { //no indicies. the protocol can't cope with them. @@ -1257,6 +1271,7 @@ void SV_SendClientPrespawnInfo(client_t *client) } client->prespawn_stage++; } +#endif if (client->prespawn_stage == PRESPAWN_MODELLIST) { @@ -1702,7 +1717,7 @@ void SVQW_PreSpawn_f (void) } // handle the case of a level changing while a client was connecting - if ( atoi(Cmd_Argv(1)) != svs.spawncount && !sv.msgfromdemo) + if ( atoi(Cmd_Argv(1)) != svs.spawncount) { Con_Printf ("SV_PreSpawn_f from different level\n"); //FIXME: we shouldn't need the following line. @@ -1769,7 +1784,7 @@ void SVQW_Spawn_f (void) } // handle the case of a level changing while a client was connecting - if ( atoi(Cmd_Argv(1)) != svs.spawncount && !sv.msgfromdemo) + if ( atoi(Cmd_Argv(1)) != svs.spawncount) { Con_Printf ("SV_Spawn_f from different level\n"); SV_New_f (); @@ -1785,7 +1800,9 @@ void SVQW_Spawn_f (void) // normally this could overflow, but no need to check due to backbuf for (i=0, client = svs.clients ; itime = sv.world.physicstime; - pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, split->edict); - PR_ExecuteProgram (svprogfuncs, SpectatorConnect); + { + globalvars_t *pr_globals = PR_globals(svprogfuncs, PR_CURRENT); + pr_global_struct->time = sv.world.physicstime; + pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, split->edict); + + if (pr_globals) + G_FLOAT(OFS_PARM0) = split->csqcactive; //this arg is part of EXT_CSQC_1, but doesn't have to be supported by the mod + PR_ExecuteProgram (svprogfuncs, SpectatorConnect); + } } sv.spawned_observer_slots++; } @@ -2156,7 +2179,7 @@ void SV_Begin_f (void) split->state = cs_spawned; // handle the case of a level changing while a client was connecting - if ( atoi(Cmd_Argv(1)) != svs.spawncount && !sv.msgfromdemo) + if ( atoi(Cmd_Argv(1)) != svs.spawncount) { Con_Printf ("SV_Begin_f from different level\n"); SV_New_f (); @@ -2216,6 +2239,10 @@ void SV_Begin_f (void) MSG_WriteAngle (&host_client->netchan.message, host_client->edict->v->angles[1] ); MSG_WriteAngle (&host_client->netchan.message, 0 ); } + +#ifdef MVD_RECORDING + SV_MVD_AutoRecord(); +#endif } //============================================================================= @@ -2708,6 +2735,7 @@ void SV_VoiceReadPacket(void) ring->receiver[j>>3] |= 1<<(j&3); } +#ifdef MVD_RECORDING if (sv.mvdrecording && sv_voip_record.ival && !(sv_voip_record.ival == 2 && !host_client->spectator)) { sizebuf_t *msg; @@ -2733,6 +2761,7 @@ void SV_VoiceReadPacket(void) MSG_WriteShort(msg, ring->datalen); SZ_Write(msg, ring->data, ring->datalen); } +#endif } void SV_VoiceInitClient(client_t *client) { @@ -3041,7 +3070,6 @@ qboolean SV_AllowDownload (const char *name) 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; qboolean protectedpak; qboolean found; static char tmpname[MAX_QPATH]; @@ -3049,6 +3077,7 @@ static int SV_LocateDownload(const char *name, flocation_t *loc, char **replacem if (replacementname) *replacementname = NULL; +#ifdef MVD_RECORDING //mvdsv demo downloading support demonum/ -> demos/XXXX (sets up the client paths) if (!Q_strncasecmp(name, "demonum/", 8)) { @@ -3066,6 +3095,7 @@ static int SV_LocateDownload(const char *name, flocation_t *loc, char **replacem return DLERR_REDIRECTFILE; } } +#endif if (!SV_AllowDownload(name)) { @@ -3073,12 +3103,14 @@ static int SV_LocateDownload(const char *name, flocation_t *loc, char **replacem return DLERR_PERMISSIONS; //not permitted (even if it exists). } +#ifdef MVD_RECORDING //mvdsv demo downloading support. demos/ -> demodir (sets up the server paths) if (!Q_strncasecmp(name, "demos/", 6)) { Q_snprintfz(tmpname, sizeof(tmpname), "%s/%s", sv_demoDir.string, name+6); name = tmpname; } +#endif if (!Q_strncasecmp(name, "package/", 8)) { @@ -3241,6 +3273,7 @@ void SV_DownloadSize_f(void) } } +#ifdef MVD_RECORDING void SV_DemoDownload_f(void) { int arg; @@ -3298,6 +3331,7 @@ void SV_DemoDownload_f(void) } } } +#endif /* ================== @@ -3309,7 +3343,6 @@ void SV_BeginDownload_f(void) char *name = Cmd_Argv(1); char *redirection = NULL; extern cvar_t allow_download_anymap, allow_download_pakcontents; - extern cvar_t sv_demoDir; flocation_t loc; int result; @@ -3682,13 +3715,14 @@ void SV_Say (qboolean team) char t1[32], *t2; int cls = 0; float floodtime; +#ifdef MVD_RECORDING sizebuf_t *msg; + qboolean mvdrecording; +#endif qboolean sent[MAX_CLIENTS]; //so we don't send to the same splitscreen connection twice. (it's ugly) int cln; - qboolean mvdrecording; - char *s, *s2; if (Cmd_Argc () < 2) @@ -3780,8 +3814,10 @@ void SV_Say (qboolean team) if (!(host_client->penalties & BAN_MUTE)) Sys_Printf ("%s", text); +#ifdef MVD_RECORDING mvdrecording = sv.mvdrecording; sv.mvdrecording = false; //so that the SV_ClientPrintf doesn't send to all players. +#endif for (j = 0, client = svs.clients; j < svs.allocated_client_slots; j++, client++) { if (client->state != cs_spawned && client->state != cs_connected) @@ -3828,6 +3864,7 @@ void SV_Say (qboolean team) SV_ClientPrintf(client, PRINT_CHAT, "%s", text); } +#ifdef MVD_RECORDING sv.mvdrecording = mvdrecording; if (!sv.mvdrecording || !cls) @@ -3842,6 +3879,7 @@ void SV_Say (qboolean team) MSG_WriteByte (msg, svc_print); MSG_WriteByte (msg, PRINT_CHAT); MSG_WriteString (msg, text); +#endif } @@ -4029,7 +4067,7 @@ void SV_Pause_f (void) return; } - if (host_client->spectator && !svs.demoplayback) + if (host_client->spectator) { SV_ClientTPrintf (host_client, PRINT_HIGH, "Spectators may not pause the game\n"); return; @@ -5476,7 +5514,9 @@ static void SVNQ_Spawn_f (void) // normally this could overflow, but no need to check due to backbuf for (i=0, client = svs.clients; ifunc) u->func(); - PR_KrimzonParseCommand(s); + if (host_client->spawned) + PR_KrimzonParseCommand(s); } host_client = oldhost; return; @@ -7491,6 +7534,7 @@ void SV_ReadQCRequest(void) int i; globalvars_t *pr_globals; client_t *cl = host_client; + edict_t *ed; if (!svprogfuncs) { @@ -7550,7 +7594,14 @@ void SV_ReadQCRequest(void) e = MSGSV_ReadEntity(host_client); if (e < 0 || e >= sv.world.num_edicts) e = 0; - G_INT(OFS_PARM0+i*3) = EDICT_TO_PROG(svprogfuncs, EDICT_NUM_PB(svprogfuncs, e)); + ed = EDICT_NUM_PB(svprogfuncs, e); + if (!ed) + { + ed = sv.world.edicts; + Con_Printf("client %s sent invalid entity\n", fromclient->name); + host_client->drop = true; + } + G_INT(OFS_PARM0+i*3) = EDICT_TO_PROG(svprogfuncs, ed); break; } i++; @@ -7564,6 +7615,7 @@ done: else fname = va("CSEv_%s", rname); f = PR_FindFunction(svprogfuncs, fname, PR_ANY); +#ifndef NOLEGACY if (!f) { if (i) @@ -7572,7 +7624,10 @@ done: rname = va("Cmd_%s", rname); f = PR_FindFunction(svprogfuncs, rname, PR_ANY); } - if (!cl) +#endif + if (host_client->drop) + ; + else if (!cl) ; //bad seat! not going to warn as they might have been removed recently else if (f) { @@ -7905,6 +7960,8 @@ void SV_ExecuteClientMessage (client_t *cl) #endif case clcdp_ackframe: cl->delta_sequence = MSG_ReadLong(); + if (cl->delta_sequence == -1 && cl->pendingdeltabits) + cl->pendingdeltabits[0] = UF_REMOVE; SV_AckEntityFrame(cl, cl->delta_sequence); break; } diff --git a/engine/shaders/glsl/drawflat_wall.glsl b/engine/shaders/glsl/drawflat_wall.glsl index 070b52716..22a94d7dd 100644 --- a/engine/shaders/glsl/drawflat_wall.glsl +++ b/engine/shaders/glsl/drawflat_wall.glsl @@ -13,7 +13,11 @@ varying vec2 lm; uniform vec4 e_lmscale; void main () { +#ifdef LM + col = vec4(1.0); +#else col = vec4(e_lmscale.rgb * ((v_normal.z < 0.73)?r_wallcolor:r_floorcolor), e_lmscale.a); +#endif lm = v_lmcoord; gl_Position = ftetransform(); } diff --git a/engine/vk/vk_backend.c b/engine/vk/vk_backend.c index 557589aef..f0a44fb1c 100644 --- a/engine/vk/vk_backend.c +++ b/engine/vk/vk_backend.c @@ -3597,7 +3597,10 @@ static void BE_DrawMeshChain_Internal(void) vkCmdBindVertexBuffers(vk.rendertarg->cbuf, 0, VK_BUFF_MAX, vertexbuffers, vertexoffsets); if (BE_SetupMeshProgram(altshader->prog, altshader->passes, altshader->flags, idxcount)) + { +// vkCmdPushConstants(vk.rendertarg->cbuf, altshader->prog->layout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(shaderstate.curtexnums->factors), shaderstate.curtexnums->factors); vkCmdDrawIndexed(vk.rendertarg->cbuf, idxcount, 1, idxfirst, 0, 0); + } } else if (1) {