X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=cl_parse.c;h=3d56d11199524f718b880249a9707b05a3952524;hp=80d11efb6650450ae95ebd4bca61da09d5f4c76c;hb=386d8ad395308eb4977346110b348562f3eadfb8;hpb=4375a7b463205f634c3e5e38be253e1b61b911ed diff --git a/cl_parse.c b/cl_parse.c index 80d11efb..3d56d111 100644 --- a/cl_parse.c +++ b/cl_parse.c @@ -24,8 +24,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "cl_collision.h" #include "csprogs.h" #include "libcurl.h" +#include "utf8lib.h" +#include "menu.h" +#include "cl_video.h" -char *svc_strings[128] = +const char *svc_strings[128] = { "svc_bad", "svc_nop", @@ -99,7 +102,7 @@ char *svc_strings[128] = "svc_pointparticles1", // 62 // [short] effectnum [vector] start, same as svc_pointparticles except velocity is zero and count is 1 }; -char *qw_svc_strings[128] = +const char *qw_svc_strings[128] = { "qw_svc_bad", // 0 "qw_svc_nop", // 1 @@ -159,7 +162,11 @@ char *qw_svc_strings[128] = //============================================================================= -cvar_t demo_nehahra = {0, "demo_nehahra", "0", "reads all quake demos as nehahra movie protocol"}; +cvar_t cl_worldmessage = {CVAR_READONLY, "cl_worldmessage", "", "title of current level"}; +cvar_t cl_worldname = {CVAR_READONLY, "cl_worldname", "", "name of current worldmodel"}; +cvar_t cl_worldnamenoextension = {CVAR_READONLY, "cl_worldnamenoextension", "", "name of current worldmodel without extension"}; +cvar_t cl_worldbasename = {CVAR_READONLY, "cl_worldbasename", "", "name of current worldmodel without maps/ prefix or extension"}; + cvar_t developer_networkentities = {0, "developer_networkentities", "0", "prints received entities, value is 0-4 (higher for more info)"}; cvar_t cl_gameplayfix_soundsmovewithentities = {0, "cl_gameplayfix_soundsmovewithentities", "1", "causes sounds made by lifts, players, projectiles, and any other entities, to move with the entity, so for example a rocket noise follows the rocket rather than staying at the starting position"}; cvar_t cl_sound_wizardhit = {0, "cl_sound_wizardhit", "wizard/hit.wav", "sound to play during TE_WIZSPIKE (empty cvar disables sound)"}; @@ -240,7 +247,7 @@ void CL_ParseStartSoundPacket(int largesoundindex) if (field_mask & SND_LARGEENTITY) { ent = (unsigned short) MSG_ReadShort (); - channel = MSG_ReadByte (); + channel = MSG_ReadChar (); } else { @@ -255,6 +262,8 @@ void CL_ParseStartSoundPacket(int largesoundindex) sound_num = MSG_ReadByte (); } + channel = CHAN_NET2ENGINE(channel); + MSG_ReadVector(pos, cls.protocol); if (sound_num >= MAX_SOUNDS) @@ -290,6 +299,7 @@ void CL_KeepaliveMessage (qboolean readmessages) { float time; static double nextmsg = -1; + static double nextupdate = -1; #if 0 static double lasttime = -1; #endif @@ -297,6 +307,15 @@ void CL_KeepaliveMessage (qboolean readmessages) qboolean oldbadread; sizebuf_t old; + if(cls.state != ca_dedicated) + { + if((time = Sys_DoubleTime()) >= nextupdate) + { + SCR_UpdateLoadingScreenIfShown(); + nextupdate = time + 2; + } + } + // no need if server is local and definitely not if this is a demo if (!cls.netcon || cls.protocol == PROTOCOL_QUAKEWORLD || cls.signon >= SIGNONS) return; @@ -378,13 +397,16 @@ void CL_ParseEntityLump(char *entdata) R_SetSkyBox(value); else if (!strcmp("fog", key)) { + FOG_clear(); // so missing values get good defaults r_refdef.fog_start = 0; r_refdef.fog_alpha = 1; r_refdef.fog_end = 16384; + r_refdef.fog_height = 1<<30; + r_refdef.fog_fadedepth = 128; #if _MSC_VER >= 1400 #define sscanf sscanf_s #endif - sscanf(value, "%f %f %f %f %f %f %f", &r_refdef.fog_density, &r_refdef.fog_red, &r_refdef.fog_green, &r_refdef.fog_blue, &r_refdef.fog_alpha, &r_refdef.fog_start, &r_refdef.fog_end); + sscanf(value, "%f %f %f %f %f %f %f %f %f", &r_refdef.fog_density, &r_refdef.fog_red, &r_refdef.fog_green, &r_refdef.fog_blue, &r_refdef.fog_alpha, &r_refdef.fog_start, &r_refdef.fog_end, &r_refdef.fog_height, &r_refdef.fog_fadedepth); } else if (!strcmp("fog_density", key)) r_refdef.fog_density = atof(value); @@ -400,6 +422,20 @@ void CL_ParseEntityLump(char *entdata) r_refdef.fog_start = atof(value); else if (!strcmp("fog_end", key)) r_refdef.fog_end = atof(value); + else if (!strcmp("fog_height", key)) + r_refdef.fog_height = atof(value); + else if (!strcmp("fog_fadedepth", key)) + r_refdef.fog_fadedepth = atof(value); + else if (!strcmp("fog_heighttexture", key)) + { + FOG_clear(); // so missing values get good defaults +#if _MSC_VER >= 1400 + sscanf_s(value, "%f %f %f %f %f %f %f %f %f %s", &r_refdef.fog_density, &r_refdef.fog_red, &r_refdef.fog_green, &r_refdef.fog_blue, &r_refdef.fog_alpha, &r_refdef.fog_start, &r_refdef.fog_end, &r_refdef.fog_height, &r_refdef.fog_fadedepth, r_refdef.fog_height_texturename, (unsigned int)sizeof(r_refdef.fog_height_texturename)); +#else + sscanf(value, "%f %f %f %f %f %f %f %f %f %63s", &r_refdef.fog_density, &r_refdef.fog_red, &r_refdef.fog_green, &r_refdef.fog_blue, &r_refdef.fog_alpha, &r_refdef.fog_start, &r_refdef.fog_end, &r_refdef.fog_height, &r_refdef.fog_fadedepth, r_refdef.fog_height_texturename); +#endif + r_refdef.fog_height_texturename[63] = 0; + } } } @@ -410,14 +446,30 @@ static const vec3_t defaultmaxs = {4096, 4096, 4096}; static void CL_SetupWorldModel(void) { // update the world model - cl.entities[0].render.model = cl.worldmodel = cl.model_precache[1]; + cl.entities[0].render.model = cl.worldmodel = CL_GetModelByIndex(1); CL_UpdateRenderEntity(&cl.entities[0].render); + // make sure the cl.worldname and related cvars are set up now that we know the world model name // set up csqc world for collision culling if (cl.worldmodel) - World_SetSize(&cl.world, cl.worldmodel->name, cl.worldmodel->normalmins, cl.worldmodel->normalmaxs); + { + strlcpy(cl.worldname, cl.worldmodel->name, sizeof(cl.worldname)); + FS_StripExtension(cl.worldname, cl.worldnamenoextension, sizeof(cl.worldnamenoextension)); + strlcpy(cl.worldbasename, !strncmp(cl.worldnamenoextension, "maps/", 5) ? cl.worldnamenoextension + 5 : cl.worldnamenoextension, sizeof(cl.worldbasename)); + Cvar_SetQuick(&cl_worldmessage, cl.worldmessage); + Cvar_SetQuick(&cl_worldname, cl.worldname); + Cvar_SetQuick(&cl_worldnamenoextension, cl.worldnamenoextension); + Cvar_SetQuick(&cl_worldbasename, cl.worldbasename); + World_SetSize(&cl.world, cl.worldname, cl.worldmodel->normalmins, cl.worldmodel->normalmaxs); + } else + { + Cvar_SetQuick(&cl_worldmessage, cl.worldmessage); + Cvar_SetQuick(&cl_worldnamenoextension, ""); + Cvar_SetQuick(&cl_worldbasename, ""); World_SetSize(&cl.world, "", defaultmins, defaultmaxs); + } + World_Start(&cl.world); // load or reload .loc file for team chat messages CL_Locs_Reload_f(); @@ -437,6 +489,9 @@ static void CL_SetupWorldModel(void) // check memory integrity Mem_CheckSentinelsGlobal(); + // make menu know + MR_NewMap(); + // load the csqc now if (cl.loadcsqc) { @@ -566,7 +621,9 @@ static void QW_CL_RequestNextDownload(void) cl.model_bolt2 = Mod_ForName("progs/bolt2.mdl", false, false, NULL); cl.model_bolt3 = Mod_ForName("progs/bolt3.mdl", false, false, NULL); cl.model_beam = Mod_ForName("progs/beam.mdl", false, false, NULL); - Mod_PurgeUnused(); + + // we purge the models and sounds later in CL_SignonReply + //Mod_PurgeUnused(); // now we try to load everything that is new @@ -620,9 +677,8 @@ static void QW_CL_RequestNextDownload(void) cls.qw_downloadtype = dl_none; - // load new sounds and unload old ones - // FIXME: S_ServerSounds does not know about cl.sfx_ sounds - S_ServerSounds(cl.sound_name, cls.qw_downloadnumber); + // clear sound usage flags for purging of unused sounds + S_ClearUsed(); // precache any sounds used by the client cl.sfx_wizhit = S_PrecacheSound(cl_sound_wizardhit.string, false, true); @@ -633,12 +689,12 @@ static void QW_CL_RequestNextDownload(void) cl.sfx_ric3 = S_PrecacheSound(cl_sound_ric3.string, false, true); cl.sfx_r_exp3 = S_PrecacheSound(cl_sound_r_exp3.string, false, true); - // sounds + // sounds used by the game for (i = 1;i < MAX_SOUNDS && cl.sound_name[i][0];i++) - { - // Don't lock the sfx here, S_ServerSounds already did that - cl.sound_precache[i] = S_PrecacheSound(cl.sound_name[i], true, false); - } + cl.sound_precache[i] = S_PrecacheSound(cl.sound_name[i], true, true); + + // we purge the models and sounds later in CL_SignonReply + //S_PurgeUnused(); // check memory integrity Mem_CheckSentinelsGlobal(); @@ -977,6 +1033,11 @@ static void CL_UpdateItemsAndWeapon(void) cl.activeweapon = cl.stats[STAT_ACTIVEWEAPON]; } +#define LOADPROGRESSWEIGHT_SOUND 1.0 +#define LOADPROGRESSWEIGHT_MODEL 4.0 +#define LOADPROGRESSWEIGHT_WORLDMODEL 30.0 +#define LOADPROGRESSWEIGHT_WORLDMODEL_INIT 2.0 + void CL_BeginDownloads(qboolean aborteddownload) { // quakeworld works differently @@ -992,6 +1053,8 @@ void CL_BeginDownloads(qboolean aborteddownload) // if we got here... // curl is done, so let's start with the business + if(!cl.loadbegun) + SCR_PushLoadingScreen(false, "Loading precaches", 1); cl.loadbegun = true; // if already downloading something from the previous level, don't stop it @@ -1026,12 +1089,31 @@ void CL_BeginDownloads(qboolean aborteddownload) if(cl.loadmodel_current == 1) { // worldmodel counts as 16 models (15 + world model setup), for better progress bar - SCR_PushLoadingScreen(false, "Loading precached models", (cl.loadmodel_total + 15) / (float) (cl.loadmodel_total + cl.loadsound_total + 15)); + SCR_PushLoadingScreen(false, "Loading precached models", + ( + (cl.loadmodel_total - 1) * LOADPROGRESSWEIGHT_MODEL + + LOADPROGRESSWEIGHT_WORLDMODEL + + LOADPROGRESSWEIGHT_WORLDMODEL_INIT + ) / ( + (cl.loadmodel_total - 1) * LOADPROGRESSWEIGHT_MODEL + + LOADPROGRESSWEIGHT_WORLDMODEL + + LOADPROGRESSWEIGHT_WORLDMODEL_INIT + + cl.loadsound_total * LOADPROGRESSWEIGHT_SOUND + ) + ); SCR_BeginLoadingPlaque(); } for (;cl.loadmodel_current < cl.loadmodel_total;cl.loadmodel_current++) { - SCR_PushLoadingScreen(true, cl.model_name[cl.loadmodel_current], (cl.loadmodel_current == 1 ? 15.0 : 1.0) / cl.loadmodel_total); + SCR_PushLoadingScreen(false, cl.model_name[cl.loadmodel_current], + ( + (cl.loadmodel_current == 1) ? LOADPROGRESSWEIGHT_WORLDMODEL : LOADPROGRESSWEIGHT_MODEL + ) / ( + (cl.loadmodel_total - 1) * LOADPROGRESSWEIGHT_MODEL + + LOADPROGRESSWEIGHT_WORLDMODEL + + LOADPROGRESSWEIGHT_WORLDMODEL_INIT + ) + ); if (cl.model_precache[cl.loadmodel_current] && cl.model_precache[cl.loadmodel_current]->Draw) { SCR_PopLoadingScreen(false); @@ -1043,12 +1125,32 @@ void CL_BeginDownloads(qboolean aborteddownload) continue; } CL_KeepaliveMessage(true); - cl.model_precache[cl.loadmodel_current] = Mod_ForName(cl.model_name[cl.loadmodel_current], false, false, cl.model_name[cl.loadmodel_current][0] == '*' ? cl.model_name[1] : NULL); + + // if running a local game, calling Mod_ForName is a completely wasted effort... + if (sv.active) + cl.model_precache[cl.loadmodel_current] = sv.models[cl.loadmodel_current]; + else + { + if(cl.loadmodel_current == 1) + { + // they'll be soon loaded, but make sure we apply freshly downloaded shaders from a curled pk3 + Mod_FreeQ3Shaders(); + } + cl.model_precache[cl.loadmodel_current] = Mod_ForName(cl.model_name[cl.loadmodel_current], false, false, cl.model_name[cl.loadmodel_current][0] == '*' ? cl.model_name[1] : NULL); + } SCR_PopLoadingScreen(false); if (cl.model_precache[cl.loadmodel_current] && cl.model_precache[cl.loadmodel_current]->Draw && cl.loadmodel_current == 1) { // we now have the worldmodel so we can set up the game world - SCR_PushLoadingScreen(true, "world model setup", 1.0 / cl.loadmodel_total); + SCR_PushLoadingScreen(true, "world model setup", + ( + LOADPROGRESSWEIGHT_WORLDMODEL_INIT + ) / ( + (cl.loadmodel_total - 1) * LOADPROGRESSWEIGHT_MODEL + + LOADPROGRESSWEIGHT_WORLDMODEL + + LOADPROGRESSWEIGHT_WORLDMODEL_INIT + ) + ); CL_SetupWorldModel(); SCR_PopLoadingScreen(true); if (!cl.loadfinished && cl_joinbeforedownloadsfinish.integer) @@ -1068,31 +1170,39 @@ void CL_BeginDownloads(qboolean aborteddownload) { // loading sounds if(cl.loadsound_current == 1) - SCR_PushLoadingScreen(false, "Loading precached sounds", cl.loadsound_total / (float) (cl.loadmodel_total + cl.loadsound_total + 15)); + SCR_PushLoadingScreen(false, "Loading precached sounds", + ( + cl.loadsound_total * LOADPROGRESSWEIGHT_SOUND + ) / ( + (cl.loadmodel_total - 1) * LOADPROGRESSWEIGHT_MODEL + + LOADPROGRESSWEIGHT_WORLDMODEL + + LOADPROGRESSWEIGHT_WORLDMODEL_INIT + + cl.loadsound_total * LOADPROGRESSWEIGHT_SOUND + ) + ); for (;cl.loadsound_current < cl.loadsound_total;cl.loadsound_current++) { - SCR_PushLoadingScreen(true, cl.sound_name[cl.loadsound_current], 1.0 / cl.loadsound_total); + SCR_PushLoadingScreen(false, cl.sound_name[cl.loadsound_current], 1.0 / cl.loadsound_total); if (cl.sound_precache[cl.loadsound_current] && S_IsSoundPrecached(cl.sound_precache[cl.loadsound_current])) { SCR_PopLoadingScreen(false); continue; } CL_KeepaliveMessage(true); - // Don't lock the sfx here, S_ServerSounds already did that - cl.sound_precache[cl.loadsound_current] = S_PrecacheSound(cl.sound_name[cl.loadsound_current], false, false); + cl.sound_precache[cl.loadsound_current] = S_PrecacheSound(cl.sound_name[cl.loadsound_current], false, true); SCR_PopLoadingScreen(false); } SCR_PopLoadingScreen(false); // finished loading sounds } - if(gamemode == GAME_NEXUIZ) + if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC) Cvar_SetValueQuick(&cl_serverextension_download, false); - // in Nexuiz, the built in download protocol is kinda broken (misses lots + // in Nexuiz/Xonotic, the built in download protocol is kinda broken (misses lots // of dependencies) anyway, and can mess around with the game directory; // until this is fixed, only support pk3 downloads via curl, and turn off // individual file downloads other than for CSQC - // on the other end of the download protocol, GAME_NEXUIZ enforces writing + // on the other end of the download protocol, GAME_NEXUIZ/GAME_XONOTIC enforces writing // to dlcache only // idea: support download of pk3 files using this protocol later @@ -1107,9 +1217,11 @@ void CL_BeginDownloads(qboolean aborteddownload) { if (aborteddownload) { + if (cl.downloadmodel_current == 1) { // the worldmodel failed, but we need to set up anyway + Mod_FreeQ3Shaders(); CL_SetupWorldModel(); if (!cl.loadfinished && cl_joinbeforedownloadsfinish.integer) { @@ -1139,6 +1251,13 @@ void CL_BeginDownloads(qboolean aborteddownload) return; } } + + if(cl.downloadmodel_current == 1) + { + // they'll be soon loaded, but make sure we apply freshly downloaded shaders from a curled pk3 + Mod_FreeQ3Shaders(); + } + cl.model_precache[cl.downloadmodel_current] = Mod_ForName(cl.model_name[cl.downloadmodel_current], false, false, cl.model_name[cl.downloadmodel_current][0] == '*' ? cl.model_name[1] : NULL); if (cl.downloadmodel_current == 1) { @@ -1185,8 +1304,7 @@ void CL_BeginDownloads(qboolean aborteddownload) return; } } - // Don't lock the sfx here, S_ServerSounds already did that - cl.sound_precache[cl.downloadsound_current] = S_PrecacheSound(cl.sound_name[cl.downloadsound_current], false, false); + cl.sound_precache[cl.downloadsound_current] = S_PrecacheSound(cl.sound_name[cl.downloadsound_current], false, true); } // finished loading sounds @@ -1214,10 +1332,7 @@ void CL_BeginDownloads_f(void) if(cl.loadbegun) Con_Printf("cl_begindownloads is only valid once per match\n"); else - { - SCR_PushLoadingScreen(false, "Loading precaches", 1); CL_BeginDownloads(false); - } } void CL_StopDownload(int size, int crc) @@ -1234,9 +1349,18 @@ void CL_StopDownload(int size, int crc) size_t inflated_size; out = FS_Inflate(cls.qw_downloadmemory, cls.qw_downloadmemorycursize, &inflated_size, tempmempool); Mem_Free(cls.qw_downloadmemory); - Con_Printf("Inflated download: new size: %u (%g%%)\n", (unsigned)inflated_size, 100.0 - 100.0*(cls.qw_downloadmemorycursize / (float)inflated_size)); - cls.qw_downloadmemory = out; - cls.qw_downloadmemorycursize = inflated_size; + if(out) + { + Con_Printf("Inflated download: new size: %u (%g%%)\n", (unsigned)inflated_size, 100.0 - 100.0*(cls.qw_downloadmemorycursize / (float)inflated_size)); + cls.qw_downloadmemory = out; + cls.qw_downloadmemorycursize = inflated_size; + } + else + { + cls.qw_downloadmemory = NULL; + cls.qw_downloadmemorycursize = 0; + Con_Printf("Cannot inflate download, possibly corrupt or zlib not present\n"); + } } if(!cls.qw_downloadmemory) @@ -1251,8 +1375,8 @@ void CL_StopDownload(int size, int crc) // save to disk only if we don't already have it // (this is mainly for playing back demos) existingcrc = FS_CRCFile(cls.qw_downloadname, &existingsize); - if (existingsize || gamemode == GAME_NEXUIZ || !strcmp(cls.qw_downloadname, csqc_progname.string)) - // let csprogs ALWAYS go to dlcache, to prevent "viral csprogs"; also, never put files outside dlcache for Nexuiz + if (existingsize || gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC || !strcmp(cls.qw_downloadname, csqc_progname.string)) + // let csprogs ALWAYS go to dlcache, to prevent "viral csprogs"; also, never put files outside dlcache for Nexuiz/Xonotic { if ((int)existingsize != size || existingcrc != crc) { @@ -1299,7 +1423,7 @@ void CL_StopDownload(int size, int crc) void CL_ParseDownload(void) { int i, start, size; - unsigned char data[65536]; + static unsigned char data[NET_MAXMESSAGE]; start = MSG_ReadLong(); size = (unsigned short)MSG_ReadShort(); @@ -1427,8 +1551,7 @@ An svc_signonnum has been received, perform a client side setup */ static void CL_SignonReply (void) { - if (developer.integer >= 100) - Con_Printf("CL_SignonReply: %i\n", cls.signon); + Con_DPrintf("CL_SignonReply: %i\n", cls.signon); switch (cls.signon) { @@ -1448,10 +1571,7 @@ static void CL_SignonReply (void) //MSG_WriteString (&cls.netcon->message, "prespawn"); } else // playing a demo... make sure loading occurs as soon as possible - { - SCR_PushLoadingScreen(false, "Loading precaches", 1); CL_BeginDownloads(false); - } break; case 2: @@ -1478,7 +1598,10 @@ static void CL_SignonReply (void) case 4: // after the level has been loaded, we shouldn't need the shaders, and // if they are needed again they will be automatically loaded... + // we also don't need the unused models or sounds from the last level Mod_FreeQ3Shaders(); + Mod_PurgeUnused(); + S_PurgeUnused(); Con_ClearNotify(); if (COM_CheckParm("-profilegameonly")) @@ -1499,7 +1622,11 @@ void CL_ParseServerInfo (void) protocolversion_t protocol; int nummodels, numsounds; + // if we start loading a level and a video is still playing, stop it + CL_VideoStop(); + Con_DPrint("Serverinfo packet received.\n"); + Collision_Cache_Reset(true); // if server is active, we already began a loading plaque if (!sv.active) @@ -1530,7 +1657,7 @@ void CL_ParseServerInfo (void) return; } // hack for unmarked Nehahra movie demos which had a custom protocol - if (protocol == PROTOCOL_QUAKEDP && cls.demoplayback && demo_nehahra.integer) + if (protocol == PROTOCOL_QUAKEDP && cls.demoplayback && gamemode == GAME_NEHAHRA) protocol = PROTOCOL_NEHAHRAMOVIE; cls.protocol = protocol; Con_DPrintf("Server protocol is %s\n", Protocol_NameForEnum(cls.protocol)); @@ -1563,7 +1690,7 @@ void CL_ParseServerInfo (void) // get the full level name str = MSG_ReadString (); - strlcpy (cl.levelname, str, sizeof(cl.levelname)); + strlcpy (cl.worldmessage, str, sizeof(cl.worldmessage)); // get the movevars that are defined in the qw protocol cl.movevars_gravity = MSG_ReadFloat(); @@ -1588,7 +1715,7 @@ void CL_ParseServerInfo (void) cl.movevars_airaccel_sideways_friction = 0; // seperate the printfs so the server message can have a color - Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n\2%s\n", str); + Con_Printf("\n\n<===================================>\n\n\2%s\n", str); // check memory integrity Mem_CheckSentinelsGlobal(); @@ -1608,6 +1735,17 @@ void CL_ParseServerInfo (void) // note: on QW protocol we can't set up the gameworld until after // downloads finish... // (we don't even know the name of the map yet) + // this also means cl_autodemo does not work on QW protocol... + + strlcpy(cl.worldname, "", sizeof(cl.worldname)); + strlcpy(cl.worldnamenoextension, "", sizeof(cl.worldnamenoextension)); + strlcpy(cl.worldbasename, "qw", sizeof(cl.worldbasename)); + Cvar_SetQuick(&cl_worldname, cl.worldname); + Cvar_SetQuick(&cl_worldnamenoextension, cl.worldnamenoextension); + Cvar_SetQuick(&cl_worldbasename, cl.worldbasename); + + // check memory integrity + Mem_CheckSentinelsGlobal(); } else { @@ -1629,11 +1767,11 @@ void CL_ParseServerInfo (void) // parse signon message str = MSG_ReadString (); - strlcpy (cl.levelname, str, sizeof(cl.levelname)); + strlcpy (cl.worldmessage, str, sizeof(cl.worldmessage)); // seperate the printfs so the server message can have a color if (cls.protocol != PROTOCOL_NEHAHRAMOVIE) // no messages when playing the Nehahra movie - Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n\2%s\n", str); + Con_Printf("\n<===================================>\n\n\2%s\n", str); // check memory integrity Mem_CheckSentinelsGlobal(); @@ -1663,6 +1801,15 @@ void CL_ParseServerInfo (void) strlcpy (cl.sound_name[numsounds], str, sizeof (cl.sound_name[numsounds])); } + // set the base name for level-specific things... this gets updated again by CL_SetupWorldModel later + strlcpy(cl.worldname, cl.model_name[1], sizeof(cl.worldname)); + FS_StripExtension(cl.worldname, cl.worldnamenoextension, sizeof(cl.worldnamenoextension)); + strlcpy(cl.worldbasename, !strncmp(cl.worldnamenoextension, "maps/", 5) ? cl.worldnamenoextension + 5 : cl.worldnamenoextension, sizeof(cl.worldbasename)); + Cvar_SetQuick(&cl_worldmessage, cl.worldmessage); + Cvar_SetQuick(&cl_worldname, cl.worldname); + Cvar_SetQuick(&cl_worldnamenoextension, cl.worldnamenoextension); + Cvar_SetQuick(&cl_worldbasename, cl.worldbasename); + // touch all of the precached models that are still loaded so we can free // anything that isn't needed if (!sv.active) @@ -1674,11 +1821,13 @@ void CL_ParseServerInfo (void) cl.model_bolt2 = Mod_ForName("progs/bolt2.mdl", false, false, NULL); cl.model_bolt3 = Mod_ForName("progs/bolt3.mdl", false, false, NULL); cl.model_beam = Mod_ForName("progs/beam.mdl", false, false, NULL); - Mod_PurgeUnused(); - // do the same for sounds - // FIXME: S_ServerSounds does not know about cl.sfx_ sounds - S_ServerSounds (cl.sound_name, numsounds); + // we purge the models and sounds later in CL_SignonReply + //Mod_PurgeUnused(); + //S_PurgeUnused(); + + // clear sound usage flags for purging of unused sounds + S_ClearUsed(); // precache any sounds used by the client cl.sfx_wizhit = S_PrecacheSound(cl_sound_wizardhit.string, false, true); @@ -1689,6 +1838,10 @@ void CL_ParseServerInfo (void) cl.sfx_ric3 = S_PrecacheSound(cl_sound_ric3.string, false, true); cl.sfx_r_exp3 = S_PrecacheSound(cl_sound_r_exp3.string, false, true); + // sounds used by the game + for (i = 1;i < MAX_SOUNDS && cl.sound_name[i][0];i++) + cl.sound_precache[i] = S_PrecacheSound(cl.sound_name[i], true, true); + // now we try to load everything that is new cl.loadmodel_current = 1; cl.downloadmodel_current = 1; @@ -1700,43 +1853,46 @@ void CL_ParseServerInfo (void) cl.loadbegun = false; cl.loadfinished = false; cl.loadcsqc = true; - } - // check memory integrity - Mem_CheckSentinelsGlobal(); - -// if cl_autodemo is set, automatically start recording a demo if one isn't being recorded already - if (cl_autodemo.integer && cls.netcon && cls.protocol != PROTOCOL_QUAKEWORLD) - { - char demofile[MAX_OSPATH]; - char levelname[MAX_QPATH]; + // check memory integrity + Mem_CheckSentinelsGlobal(); - if (cls.demorecording) + // if cl_autodemo is set, automatically start recording a demo if one isn't being recorded already + if (cl_autodemo.integer && cls.netcon && cls.protocol != PROTOCOL_QUAKEWORLD) { - // finish the previous level's demo file - CL_Stop_f(); - } + char demofile[MAX_OSPATH]; - // start a new demo file - strlcpy(levelname, FS_FileWithoutPath(cl.model_name[1]), sizeof(levelname)); - if (strrchr(levelname, '.')) - *(strrchr(levelname, '.')) = 0; - dpsnprintf (demofile, sizeof(demofile), "%s_%s.dem", Sys_TimeString (cl_autodemo_nameformat.string), levelname); + if (cls.demorecording) + { + // finish the previous level's demo file + CL_Stop_f(); + } - Con_Printf ("Auto-recording to %s.\n", demofile); + // start a new demo file + dpsnprintf (demofile, sizeof(demofile), "%s_%s.dem", Sys_TimeString (cl_autodemo_nameformat.string), cl.worldbasename); - cls.demofile = FS_OpenRealFile(demofile, "wb", false); - if (cls.demofile) - { - cls.forcetrack = -1; - FS_Printf (cls.demofile, "%i\n", cls.forcetrack); - cls.demorecording = true; - strlcpy(cls.demoname, demofile, sizeof(cls.demoname)); - cls.demo_lastcsprogssize = -1; - cls.demo_lastcsprogscrc = -1; + Con_Printf ("Auto-recording to %s.\n", demofile); + + // Reset bit 0 for every new demo + Cvar_SetValueQuick(&cl_autodemo_delete, + (cl_autodemo_delete.integer & ~0x1) + | + ((cl_autodemo_delete.integer & 0x2) ? 0x1 : 0) + ); + + cls.demofile = FS_OpenRealFile(demofile, "wb", false); + if (cls.demofile) + { + cls.forcetrack = -1; + FS_Printf (cls.demofile, "%i\n", cls.forcetrack); + cls.demorecording = true; + strlcpy(cls.demoname, demofile, sizeof(cls.demoname)); + cls.demo_lastcsprogssize = -1; + cls.demo_lastcsprogscrc = -1; + } + else + Con_Print ("ERROR: couldn't open.\n"); } - else - Con_Print ("ERROR: couldn't open.\n"); } } @@ -1759,11 +1915,14 @@ void CL_ValidateState(entity_state_t *s) if (!(s->flags & RENDER_COLORMAPPED) && s->colormap > cl.maxclients) Con_DPrintf("CL_ValidateState: colormap (%i) > cl.maxclients (%i)\n", s->colormap, cl.maxclients); - model = cl.model_precache[s->modelindex]; - if (model && model->type && s->frame >= model->numframes) - Con_DPrintf("CL_ValidateState: no such frame %i in \"%s\" (which has %i frames)\n", s->frame, model->name, model->numframes); - if (model && model->type && s->skin > 0 && s->skin >= model->numskins && !(s->lightpflags & PFLAGS_FULLDYNAMIC)) - Con_DPrintf("CL_ValidateState: no such skin %i in \"%s\" (which has %i skins)\n", s->skin, model->name, model->numskins); + if (developer_extra.integer) + { + model = CL_GetModelByIndex(s->modelindex); + if (model && model->type && s->frame >= model->numframes) + Con_DPrintf("CL_ValidateState: no such frame %i in \"%s\" (which has %i frames)\n", s->frame, model->name, model->numframes); + if (model && model->type && s->skin > 0 && s->skin >= model->numskins && !(s->lightpflags & PFLAGS_FULLDYNAMIC)) + Con_DPrintf("CL_ValidateState: no such skin %i in \"%s\" (which has %i skins)\n", s->skin, model->name, model->numskins); + } } void CL_MoveLerpEntityStates(entity_t *ent) @@ -1800,10 +1959,11 @@ void CL_MoveLerpEntityStates(entity_t *ent) VectorCopy(ent->state_current.angles, ent->persistent.newangles); ent->persistent.trail_allowed = false; - if(ent->state_current.frame != ent->state_previous.frame) + // if(ent->state_current.frame != ent->state_previous.frame) + // do this even if we did change the frame + // teleport bit is only used if an animation restart, or a jump, is necessary + // so it should be always harmless to do this { - // if we ALSO changed animation frame in the process (but ONLY then!) - // then let's reset the animation interpolation too ent->render.framegroupblend[0].frame = ent->render.framegroupblend[1].frame = ent->state_current.frame; ent->render.framegroupblend[0].start = ent->render.framegroupblend[1].start = cl.time; ent->render.framegroupblend[0].lerp = 1;ent->render.framegroupblend[1].lerp = 0; @@ -1811,6 +1971,14 @@ void CL_MoveLerpEntityStates(entity_t *ent) // note that this case must do everything the following case does too } + else if ((ent->state_previous.effects & EF_RESTARTANIM_BIT) != (ent->state_current.effects & EF_RESTARTANIM_BIT)) + { + ent->render.framegroupblend[1] = ent->render.framegroupblend[0]; + ent->render.framegroupblend[1].lerp = 1; + ent->render.framegroupblend[0].frame = ent->state_current.frame; + ent->render.framegroupblend[0].start = cl.time; + ent->render.framegroupblend[0].lerp = 0; + } else if (DotProduct(odelta, odelta) > 1000*1000 || (cl.fixangle[0] && !cl.fixangle[1]) || (ent->state_previous.tagindex != ent->state_current.tagindex) @@ -1857,6 +2025,16 @@ void CL_MoveLerpEntityStates(entity_t *ent) // trigger muzzleflash effect if necessary if (ent->state_current.effects & EF_MUZZLEFLASH) ent->persistent.muzzleflash = 1; + + // restart animation bit + if ((ent->state_previous.effects & EF_RESTARTANIM_BIT) != (ent->state_current.effects & EF_RESTARTANIM_BIT)) + { + ent->render.framegroupblend[1] = ent->render.framegroupblend[0]; + ent->render.framegroupblend[1].lerp = 1; + ent->render.framegroupblend[0].frame = ent->state_current.frame; + ent->render.framegroupblend[0].start = cl.time; + ent->render.framegroupblend[0].lerp = 0; + } } /* @@ -2043,7 +2221,7 @@ void CL_ParseStatic (int large) } // copy it to the current state - ent->render.model = cl.model_precache[ent->state_baseline.modelindex]; + ent->render.model = CL_GetModelByIndex(ent->state_baseline.modelindex); ent->render.framegroupblend[0].frame = ent->state_baseline.frame; ent->render.framegroupblend[0].lerp = 1; // make torchs play out of sync @@ -2056,6 +2234,7 @@ void CL_ParseStatic (int large) //VectorCopy (ent->state_baseline.angles, ent->render.angles); Matrix4x4_CreateFromQuakeEntity(&ent->render.matrix, ent->state_baseline.origin[0], ent->state_baseline.origin[1], ent->state_baseline.origin[2], ent->state_baseline.angles[0], ent->state_baseline.angles[1], ent->state_baseline.angles[2], 1); + ent->render.allowdecals = true; CL_UpdateRenderEntity(&ent->render); } @@ -2708,7 +2887,7 @@ void CL_ParseTrailParticles(void) effectindex = (unsigned short)MSG_ReadShort(); MSG_ReadVector(start, cls.protocol); MSG_ReadVector(end, cls.protocol); - CL_ParticleEffect(effectindex, VectorDistance(start, end), start, end, vec3_origin, vec3_origin, entityindex > 0 ? cl.entities + entityindex : NULL, 0); + CL_ParticleEffect(effectindex, 1, start, end, vec3_origin, vec3_origin, entityindex > 0 ? cl.entities + entityindex : NULL, 0); } void CL_ParsePointParticles(void) @@ -2751,8 +2930,8 @@ static void CL_IPLog_Add(const char *address, const char *name, qboolean checkex return; if (!cl_iplog_loaded) CL_IPLog_Load(); - if (developer.integer >= 100) - Con_Printf("CL_IPLog_Add(\"%s\", \"%s\", %i, %i);\n", address, name, checkexisting, addtofile); + if (developer_extra.integer) + Con_DPrintf("CL_IPLog_Add(\"%s\", \"%s\", %i, %i);\n", address, name, checkexisting, addtofile); // see if it already exists if (checkexisting) { @@ -2760,8 +2939,8 @@ static void CL_IPLog_Add(const char *address, const char *name, qboolean checkex { if (!strcmp(cl_iplog_items[i].address, address) && !strcmp(cl_iplog_items[i].name, name)) { - if (developer.integer >= 100) - Con_Printf("... found existing \"%s\" \"%s\"\n", cl_iplog_items[i].address, cl_iplog_items[i].name); + if (developer_extra.integer) + Con_DPrintf("... found existing \"%s\" \"%s\"\n", cl_iplog_items[i].address, cl_iplog_items[i].name); return; } } @@ -2792,8 +2971,8 @@ static void CL_IPLog_Add(const char *address, const char *name, qboolean checkex // TODO: this ought to open the one in the userpath version of the base // gamedir, not the current gamedir Log_Printf(cl_iplog_name.string, "%s %s\n", address, name); - if (developer.integer >= 100) - Con_Printf("CL_IPLog_Add: appending this line to %s: %s %s\n", cl_iplog_name.string, address, name); + if (developer_extra.integer) + Con_DPrintf("CL_IPLog_Add: appending this line to %s: %s %s\n", cl_iplog_name.string, address, name); } } @@ -2913,7 +3092,7 @@ qboolean CL_ExaminePrintString(const char *text) if (cl.parsingtextmode == CL_PARSETEXTMODE_PING) { // if anything goes wrong, we'll assume this is not a ping report - qboolean expected = cl.parsingtextexpectingpingforscores; + qboolean expected = cl.parsingtextexpectingpingforscores != 0; cl.parsingtextexpectingpingforscores = 0; cl.parsingtextmode = CL_PARSETEXTMODE_NONE; t = text; @@ -3025,13 +3204,14 @@ qboolean CL_ExaminePrintString(const char *text) } extern cvar_t slowmo; +extern cvar_t cl_lerpexcess; extern void CSQC_UpdateNetworkTimes(double newtime, double oldtime); static void CL_NetworkTimeReceived(double newtime) { double timehigh; cl.mtime[1] = cl.mtime[0]; cl.mtime[0] = newtime; - if (cls.timedemo || (cl.islocalgame && !sv_fixedframeratesingleplayer.integer) || cl.mtime[1] == cl.mtime[0] || cls.signon < SIGNONS) + if (cl_nolerp.integer || cls.timedemo || (cl.islocalgame && !sv_fixedframeratesingleplayer.integer) || cl.mtime[1] == cl.mtime[0] || cls.signon < SIGNONS) cl.time = cl.mtime[1] = newtime; else if (cls.demoplayback) { @@ -3044,12 +3224,12 @@ static void CL_NetworkTimeReceived(double newtime) else if (cls.protocol != PROTOCOL_QUAKEWORLD) { cl.mtime[1] = max(cl.mtime[1], cl.mtime[0] - 0.1); - if (developer.integer >= 100 && vid_activewindow) + if (developer_extra.integer && vid_activewindow) { if (cl.time < cl.mtime[1] - (cl.mtime[0] - cl.mtime[1])) - Con_Printf("--- cl.time < cl.mtime[1] (%f < %f ... %f)\n", cl.time, cl.mtime[1], cl.mtime[0]); + Con_DPrintf("--- cl.time < cl.mtime[1] (%f < %f ... %f)\n", cl.time, cl.mtime[1], cl.mtime[0]); else if (cl.time > cl.mtime[0] + (cl.mtime[0] - cl.mtime[1])) - Con_Printf("--- cl.time > cl.mtime[0] (%f > %f ... %f)\n", cl.time, cl.mtime[1], cl.mtime[0]); + Con_DPrintf("--- cl.time > cl.mtime[0] (%f > %f ... %f)\n", cl.time, cl.mtime[1], cl.mtime[0]); } cl.time += (cl.mtime[1] - cl.time) * bound(0, cl_nettimesyncfactor.value, 1); timehigh = cl.mtime[1] + (cl.mtime[0] - cl.mtime[1]) * cl_nettimesyncboundtolerance.value; @@ -3105,6 +3285,23 @@ static void CL_NetworkTimeReceived(double newtime) VectorCopy(cl.mviewangles[0], cl.mviewangles[1]); // update the csqc's server timestamps, critical for proper sync CSQC_UpdateNetworkTimes(cl.mtime[0], cl.mtime[1]); + + if (cl.mtime[0] > cl.mtime[1]) + World_Physics_Frame(&cl.world, cl.mtime[0] - cl.mtime[1], cl.movevars_gravity); + + // only lerp entities that also get an update in this frame, when lerp excess is used + if(cl_lerpexcess.value > 0) + { + int i; + for (i = 1;i < cl.num_entities;i++) + { + if (cl.entities_active[i]) + { + entity_t *ent = cl.entities + i; + ent->persistent.lerpdeltatime = 0; + } + } + } } #define SHOWNET(x) if(cl_shownet.integer==2)Con_Printf("%3i:%s(%i)\n", msg_readcount-1, x, cmd); @@ -3129,9 +3326,10 @@ void CL_ParseServerMessage(void) int i; protocolversion_t protocol; unsigned char cmdlog[32]; - char *cmdlogname[32], *temp; + const char *cmdlogname[32], *temp; int cmdindex, cmdcount = 0; qboolean qwplayerupdatereceived; + qboolean strip_pqc; // LordHavoc: moved demo message writing from before the packet parse to // after the packet parse so that CL_Stop_f can be called by cl_autodemo @@ -3495,7 +3693,7 @@ void CL_ParseServerMessage(void) break; case qw_svc_setpause: - cl.paused = MSG_ReadByte (); + cl.paused = MSG_ReadByte () != 0; if (cl.paused) CDAudio_Pause (); else @@ -3524,6 +3722,7 @@ void CL_ParseServerMessage(void) if (cmd == -1) { +// R_TimeReport("END OF MESSAGE"); SHOWNET("END OF MESSAGE"); break; // end of message } @@ -3604,7 +3803,7 @@ void CL_ParseServerMessage(void) if (protocol == PROTOCOL_UNKNOWN) Host_Error("CL_ParseServerMessage: Server is unrecognized protocol number (%i)", i); // hack for unmarked Nehahra movie demos which had a custom protocol - if (protocol == PROTOCOL_QUAKEDP && cls.demoplayback && demo_nehahra.integer) + if (protocol == PROTOCOL_QUAKEDP && cls.demoplayback && gamemode == GAME_NEHAHRA) protocol = PROTOCOL_NEHAHRAMOVIE; cls.protocol = protocol; break; @@ -3628,7 +3827,42 @@ void CL_ParseServerMessage(void) break; case svc_stufftext: - CL_VM_Parse_StuffCmd(MSG_ReadString ()); //[515]: csqc + temp = MSG_ReadString(); + /* if(utf8_enable.integer) + { + strip_pqc = true; + // we can safely strip and even + // interpret these in utf8 mode + } + else */ switch(cls.protocol) + { + case PROTOCOL_QUAKE: + case PROTOCOL_QUAKEDP: + // maybe add other protocols if + // so desired, but not DP7 + strip_pqc = true; + break; + case PROTOCOL_DARKPLACES7: + default: + // ProQuake does not support + // these protocols + strip_pqc = false; + break; + } + if(strip_pqc) + { + // skip over ProQuake messages, + // TODO actually interpret them + // (they are sbar team score + // updates), see proquake cl_parse.c + if(*temp == 0x01) + { + ++temp; + while(*temp >= 0x01 && *temp <= 0x1F) + ++temp; + } + } + CL_VM_Parse_StuffCmd(temp); //[515]: csqc break; case svc_damage: @@ -3709,7 +3943,7 @@ void CL_ParseServerMessage(void) i -= 32768; if (i >= 1 && i < MAX_SOUNDS) { - sfx_t *sfx = S_PrecacheSound (s, true, false); + sfx_t *sfx = S_PrecacheSound (s, true, true); if (!sfx && snd_initialized.integer) Con_DPrintf("svc_precache: S_PrecacheSound(\"%s\") failed\n", s); cl.sound_precache[i] = sfx; @@ -3786,7 +4020,7 @@ void CL_ParseServerMessage(void) break; case svc_setpause: - cl.paused = MSG_ReadByte (); + cl.paused = MSG_ReadByte () != 0; if (cl.paused) CDAudio_Pause (); else @@ -3879,7 +4113,7 @@ void CL_ParseServerMessage(void) MSG_ReadCoord(cls.protocol); MSG_ReadCoord(cls.protocol); MSG_ReadCoord(cls.protocol); - MSG_ReadByte(); + (void) MSG_ReadByte(); MSG_ReadLong(); MSG_ReadLong(); MSG_ReadString(); @@ -3894,7 +4128,7 @@ void CL_ParseServerMessage(void) MSG_ReadCoord(cls.protocol); MSG_ReadCoord(cls.protocol); MSG_ReadCoord(cls.protocol); - MSG_ReadByte(); + (void) MSG_ReadByte(); MSG_ReadString(); } else @@ -3933,22 +4167,29 @@ void CL_ParseServerMessage(void) CL_ParsePointParticles1(); break; } +// R_TimeReport(svc_strings[cmd]); } } if (cls.signon == SIGNONS) CL_UpdateItemsAndWeapon(); +// R_TimeReport("UpdateItems"); EntityFrameQuake_ISeeDeadEntities(); +// R_TimeReport("ISeeDeadEntities"); CL_UpdateMoveVars(); +// R_TimeReport("UpdateMoveVars"); parsingerror = false; // LordHavoc: this was at the start of the function before cl_autodemo was // implemented if (cls.demorecording) + { CL_WriteDemoMessage (&net_message); +// R_TimeReport("WriteDemo"); + } } void CL_Parse_DumpPacket(void) @@ -3968,10 +4209,11 @@ void CL_Parse_ErrorCleanUp(void) void CL_Parse_Init(void) { - // LordHavoc: added demo_nehahra cvar - Cvar_RegisterVariable (&demo_nehahra); - if (gamemode == GAME_NEHAHRA) - Cvar_SetValue("demo_nehahra", 1); + Cvar_RegisterVariable(&cl_worldmessage); + Cvar_RegisterVariable(&cl_worldname); + Cvar_RegisterVariable(&cl_worldnamenoextension); + Cvar_RegisterVariable(&cl_worldbasename); + Cvar_RegisterVariable(&developer_networkentities); Cvar_RegisterVariable(&cl_gameplayfix_soundsmovewithentities);