]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - cl_parse.c
use checkdisk flag on model loading after ingame download, this should
[xonotic/darkplaces.git] / cl_parse.c
index 0b8af8ed6165c6ba897d714e529f395a9ea3a743..dcde8eb1607e060f1bd748eb06eb6076f8c71152 100644 (file)
@@ -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)"};
@@ -187,8 +194,8 @@ static void QW_CL_NextUpload(void);
 void QW_CL_StartUpload(unsigned char *data, int size);
 //static qboolean QW_CL_IsUploading(void);
 static void QW_CL_StopUpload(void);
-void CL_VM_UpdateIntermissionState(int intermission);
-qboolean CL_VM_Event_Sound(int sound_num, float volume, int channel, float attenuation, int ent, vec3_t pos);
+extern void CL_VM_UpdateIntermissionState(int intermission);
+extern qboolean CL_VM_Event_Sound(int sound_num, float volume, int channel, float attenuation, int ent, vec3_t pos, int flags, float speed);
 
 /*
 ==================
@@ -203,6 +210,8 @@ void CL_ParseStartSoundPacket(int largesoundindex)
        int     volume;
        int     field_mask;
        float   attenuation;
+       float   speed;
+       int             fflags = CHANNELFLAG_NONE;
 
        if (cls.protocol == PROTOCOL_QUAKEWORLD)
        {
@@ -217,6 +226,8 @@ void CL_ParseStartSoundPacket(int largesoundindex)
                        attenuation = MSG_ReadByte () / 64.0;
                else
                        attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
+       
+               speed = 1.0f;
 
                ent = (channel>>3)&1023;
                channel &= 7;
@@ -237,10 +248,15 @@ void CL_ParseStartSoundPacket(int largesoundindex)
                else
                        attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
 
+               if (field_mask & SND_SPEEDUSHORT4000)
+                       speed = ((unsigned short)MSG_ReadShort ()) / 4000.0f;
+               else
+                       speed = 1.0f;
+
                if (field_mask & SND_LARGEENTITY)
                {
                        ent = (unsigned short) MSG_ReadShort ();
-                       channel = MSG_ReadByte ();
+                       channel = MSG_ReadChar ();
                }
                else
                {
@@ -255,6 +271,8 @@ void CL_ParseStartSoundPacket(int largesoundindex)
                        sound_num = MSG_ReadByte ();
        }
 
+       channel = CHAN_NET2ENGINE(channel);
+
        MSG_ReadVector(pos, cls.protocol);
 
        if (sound_num >= MAX_SOUNDS)
@@ -272,8 +290,8 @@ void CL_ParseStartSoundPacket(int largesoundindex)
        if (ent >= cl.max_entities)
                CL_ExpandEntities(ent);
 
-       if( !CL_VM_Event_Sound(sound_num, volume / 255.0f, channel, attenuation, ent, pos) )
-               S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0f, attenuation);
+       if( !CL_VM_Event_Sound(sound_num, volume / 255.0f, channel, attenuation, ent, pos, fflags, speed) )
+               S_StartSound_StartPosition_Flags (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0f, attenuation, 0, fflags, speed);
 }
 
 /*
@@ -288,15 +306,40 @@ so the server doesn't disconnect.
 static unsigned char olddata[NET_MAXMESSAGE];
 void CL_KeepaliveMessage (qboolean readmessages)
 {
+       static qboolean recursive = false;
        float time;
        static double nextmsg = -1;
+       static double nextupdate = -1;
+#if 0
+       static double lasttime = -1;
+#endif
        int oldreadcount;
        qboolean oldbadread;
        sizebuf_t old;
 
+       qboolean thisrecursive;
+
+       thisrecursive = recursive;
+       recursive = true;
+
+       if(!thisrecursive)
+       {
+               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)
+       if (!cls.netcon || cls.protocol == PROTOCOL_QUAKEWORLD || cls.signon >= SIGNONS)
+       {
+               recursive = thisrecursive;
                return;
+       }
 
        if (readmessages)
        {
@@ -314,6 +357,14 @@ void CL_KeepaliveMessage (qboolean readmessages)
                memcpy(net_message.data, olddata, net_message.cursize);
        }
 
+#if 0
+       if((time = Sys_DoubleTime()) >= lasttime + 1)
+       {
+               Con_Printf("long delta: %f\n", time - lasttime);
+       }
+       lasttime = Sys_DoubleTime();
+#endif
+
        if (cls.netcon && (time = Sys_DoubleTime()) >= nextmsg)
        {
                sizebuf_t       msg;
@@ -328,6 +379,8 @@ void CL_KeepaliveMessage (qboolean readmessages)
                MSG_WriteChar(&msg, clc_nop);
                NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, false);
        }
+
+       recursive = thisrecursive;
 }
 
 void CL_ParseEntityLump(char *entdata)
@@ -367,13 +420,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);
@@ -389,6 +445,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;
+               }
        }
 }
 
@@ -399,27 +469,52 @@ 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();
 
+       // make sure we send enough keepalives
+       CL_KeepaliveMessage(false);
+
        // reset particles and other per-level things
        R_Modules_NewMap();
 
+       // make sure we send enough keepalives
+       CL_KeepaliveMessage(false);
+
        // load the team chat beep if possible
        cl.foundtalk2wav = FS_FileExists("sound/misc/talk2.wav");
 
        // check memory integrity
        Mem_CheckSentinelsGlobal();
 
+       // make menu know
+       MR_NewMap();
+
        // load the csqc now
        if (cl.loadcsqc)
        {
@@ -543,24 +638,26 @@ static void QW_CL_RequestNextDownload(void)
                if (!sv.active)
                        Mod_ClearUsed();
                for (i = 1;i < MAX_MODELS && cl.model_name[i][0];i++)
-                       Mod_FindName(cl.model_name[i]);
+                       Mod_FindName(cl.model_name[i], cl.model_name[i][0] == '*' ? cl.model_name[1] : NULL);
                // precache any models used by the client (this also marks them used)
-               cl.model_bolt = Mod_ForName("progs/bolt.mdl", false, false, false);
-               cl.model_bolt2 = Mod_ForName("progs/bolt2.mdl", false, false, false);
-               cl.model_bolt3 = Mod_ForName("progs/bolt3.mdl", false, false, false);
-               cl.model_beam = Mod_ForName("progs/beam.mdl", false, false, false);
-               Mod_PurgeUnused();
+               cl.model_bolt = Mod_ForName("progs/bolt.mdl", false, false, NULL);
+               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);
+
+               // we purge the models and sounds later in CL_SignonReply
+               //Mod_PurgeUnused();
 
                // now we try to load everything that is new
 
                // world model
-               cl.model_precache[1] = Mod_ForName(cl.model_name[1], false, false, true);
+               cl.model_precache[1] = Mod_ForName(cl.model_name[1], false, false, NULL);
                if (cl.model_precache[1]->Draw == NULL)
                        Con_Printf("Map %s could not be found or downloaded\n", cl.model_name[1]);
 
                // normal models
                for (i = 2;i < MAX_MODELS && cl.model_name[i][0];i++)
-                       if ((cl.model_precache[i] = Mod_ForName(cl.model_name[i], false, false, false))->Draw == NULL)
+                       if ((cl.model_precache[i] = Mod_ForName(cl.model_name[i], false, false, cl.model_name[i][0] == '*' ? cl.model_name[1] : NULL))->Draw == NULL)
                                Con_Printf("Model %s could not be found or downloaded\n", cl.model_name[i]);
 
                // check memory integrity
@@ -603,9 +700,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);
@@ -616,12 +712,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();
@@ -960,6 +1056,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
@@ -975,6 +1076,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
@@ -1006,18 +1109,73 @@ void CL_BeginDownloads(qboolean aborteddownload)
        if (cl.loadmodel_current < cl.loadmodel_total)
        {
                // loading models
-
+               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 - 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(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);
+                               if(cl.loadmodel_current == 1)
+                               {
+                                       SCR_PushLoadingScreen(false, cl.model_name[cl.loadmodel_current], 1.0 / cl.loadmodel_total);
+                                       SCR_PopLoadingScreen(false);
+                               }
                                continue;
-                       if (cls.signon < SIGNONS)
-                               CL_KeepaliveMessage(true);
-                       cl.model_precache[cl.loadmodel_current] = Mod_ForName(cl.model_name[cl.loadmodel_current], false, false, cl.loadmodel_current == 1);
+                       }
+                       CL_KeepaliveMessage(true);
+
+                       // 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",
+                                       (
+                                               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)
                                {
                                        cl.loadfinished = true;
@@ -1027,34 +1185,47 @@ void CL_BeginDownloads(qboolean aborteddownload)
                                }
                        }
                }
-
+               SCR_PopLoadingScreen(false);
                // finished loading models
        }
 
        if (cl.loadsound_current < cl.loadsound_total)
        {
                // loading sounds
-
+               if(cl.loadsound_current == 1)
+                       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(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;
-                       if (cls.signon < SIGNONS)
-                               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_KeepaliveMessage(true);
+                       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
 
@@ -1069,9 +1240,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)
                                        {
@@ -1086,9 +1259,8 @@ void CL_BeginDownloads(qboolean aborteddownload)
                        }
                        if (cl.model_precache[cl.downloadmodel_current] && cl.model_precache[cl.downloadmodel_current]->Draw)
                                continue;
-                       if (cls.signon < SIGNONS)
-                               CL_KeepaliveMessage(true);
-                       if (strcmp(cl.model_name[cl.downloadmodel_current], "null") && !FS_FileExists(cl.model_name[cl.downloadmodel_current]))
+                       CL_KeepaliveMessage(true);
+                       if (cl.model_name[cl.downloadmodel_current][0] != '*' && strcmp(cl.model_name[cl.downloadmodel_current], "null") && !FS_FileExists(cl.model_name[cl.downloadmodel_current]))
                        {
                                if (cl.downloadmodel_current == 1)
                                        Con_Printf("Map %s not found\n", cl.model_name[cl.downloadmodel_current]);
@@ -1102,7 +1274,14 @@ void CL_BeginDownloads(qboolean aborteddownload)
                                        return;
                                }
                        }
-                       cl.model_precache[cl.downloadmodel_current] = Mod_ForName(cl.model_name[cl.downloadmodel_current], false, false, cl.downloadmodel_current == 1);
+
+                       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, true, cl.model_name[cl.downloadmodel_current][0] == '*' ? cl.model_name[1] : NULL);
                        if (cl.downloadmodel_current == 1)
                        {
                                // we now have the worldmodel so we can set up the game world
@@ -1136,8 +1315,7 @@ void CL_BeginDownloads(qboolean aborteddownload)
                        }
                        if (cl.sound_precache[cl.downloadsound_current] && S_IsSoundPrecached(cl.sound_precache[cl.downloadsound_current]))
                                continue;
-                       if (cls.signon < SIGNONS)
-                               CL_KeepaliveMessage(true);
+                       CL_KeepaliveMessage(true);
                        dpsnprintf(soundname, sizeof(soundname), "sound/%s", cl.sound_name[cl.downloadsound_current]);
                        if (!FS_FileExists(soundname) && !FS_FileExists(cl.sound_name[cl.downloadsound_current]))
                        {
@@ -1149,13 +1327,14 @@ 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
        }
 
+       SCR_PopLoadingScreen(false);
+
        if (!cl.loadfinished)
        {
                cl.loadfinished = true;
@@ -1193,9 +1372,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)
@@ -1210,8 +1398,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)
                                {
@@ -1258,7 +1446,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();
 
@@ -1386,8 +1574,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)
        {
@@ -1432,6 +1619,13 @@ static void CL_SignonReply (void)
                break;
 
        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"))
                        Sys_AllowProfiling(true);
@@ -1451,13 +1645,19 @@ 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)
        {
                SCR_BeginLoadingPlaque();
                S_StopAllSounds();
+               // free q3 shaders so that any newly downloaded shaders will be active
+               Mod_FreeQ3Shaders();
        }
 
        // check memory integrity
@@ -1480,7 +1680,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));
@@ -1513,7 +1713,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();
@@ -1538,7 +1738,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();
@@ -1558,6 +1758,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
        {
@@ -1579,11 +1790,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();
@@ -1613,22 +1824,33 @@ 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)
                        Mod_ClearUsed();
                for (i = 1;i < nummodels;i++)
-                       Mod_FindName(cl.model_name[i]);
+                       Mod_FindName(cl.model_name[i], cl.model_name[i][0] == '*' ? cl.model_name[1] : NULL);
                // precache any models used by the client (this also marks them used)
-               cl.model_bolt = Mod_ForName("progs/bolt.mdl", false, false, false);
-               cl.model_bolt2 = Mod_ForName("progs/bolt2.mdl", false, false, false);
-               cl.model_bolt3 = Mod_ForName("progs/bolt3.mdl", false, false, false);
-               cl.model_beam = Mod_ForName("progs/beam.mdl", false, false, false);
-               Mod_PurgeUnused();
+               cl.model_bolt = Mod_ForName("progs/bolt.mdl", false, false, NULL);
+               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);
+
+               // we purge the models and sounds later in CL_SignonReply
+               //Mod_PurgeUnused();
+               //S_PurgeUnused();
 
-               // do the same for sounds
-               // FIXME: S_ServerSounds does not know about cl.sfx_ sounds
-               S_ServerSounds (cl.sound_name, numsounds);
+               // 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);
@@ -1639,6 +1861,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;
@@ -1650,43 +1876,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");
        }
 }
 
@@ -1709,11 +1938,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)
@@ -1731,14 +1963,49 @@ void CL_MoveLerpEntityStates(entity_t *ent)
                VectorCopy(ent->state_current.origin, ent->persistent.neworigin);
                VectorCopy(ent->state_current.angles, ent->persistent.newangles);
                // reset animation interpolation as well
-               ent->render.frame1 = ent->render.frame2 = ent->state_current.frame;
-               ent->render.frame1time = ent->render.frame2time = cl.time;
-               ent->render.framelerp = 1;
+               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;
+               ent->render.shadertime = cl.time;
                // reset various persistent stuff
                ent->persistent.muzzleflash = 0;
                ent->persistent.trail_allowed = false;
        }
-       else if (DotProduct(odelta, odelta) > 1000*1000 || (cl.fixangle[0] && !cl.fixangle[1]))
+       else if ((ent->state_previous.effects & EF_TELEPORT_BIT) != (ent->state_current.effects & EF_TELEPORT_BIT))
+       {
+               // don't interpolate the move
+               ent->persistent.lerpdeltatime = 0;
+               ent->persistent.lerpstarttime = cl.mtime[1];
+               VectorCopy(ent->state_current.origin, ent->persistent.oldorigin);
+               VectorCopy(ent->state_current.angles, ent->persistent.oldangles);
+               VectorCopy(ent->state_current.origin, ent->persistent.neworigin);
+               VectorCopy(ent->state_current.angles, ent->persistent.newangles);
+               ent->persistent.trail_allowed = false;
+
+               // 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
+               {
+                       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;
+               }
+
+               // 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)
+               || (ent->state_previous.tagentity != ent->state_current.tagentity))
        {
                // don't interpolate the move
                // (the fixangle[] check detects teleports, but not constant fixangles
@@ -1781,6 +2048,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;
+       }
 }
 
 /*
@@ -1967,11 +2244,11 @@ void CL_ParseStatic (int large)
        }
 
 // copy it to the current state
-       ent->render.model = cl.model_precache[ent->state_baseline.modelindex];
-       ent->render.frame1 = ent->render.frame2 = ent->state_baseline.frame;
-       ent->render.framelerp = 0;
+       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
-       ent->render.frame1time = ent->render.frame2time = lhrandom(-10, -1);
+       ent->render.framegroupblend[0].start = lhrandom(-10, -1);
        ent->render.skinnum = ent->state_baseline.skin;
        ent->render.effects = ent->state_baseline.effects;
        ent->render.alpha = 1;
@@ -1980,6 +2257,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);
 }
 
@@ -2554,7 +2832,7 @@ void CL_ParseTempEntity(void)
 
        // LordHavoc: for compatibility with the Nehahra movie...
                case TE_LIGHTNING4NEH:
-                       CL_ParseBeam(Mod_ForName(MSG_ReadString(), true, false, false), false);
+                       CL_ParseBeam(Mod_ForName(MSG_ReadString(), true, false, NULL), false);
                        break;
 
                case TE_LAVASPLASH:
@@ -2632,7 +2910,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)
@@ -2675,8 +2953,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)
        {
@@ -2684,8 +2962,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;
                        }
                }
@@ -2716,8 +2994,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);
        }
 }
 
@@ -2837,7 +3115,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;
@@ -2949,13 +3227,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)
        {
@@ -2968,12 +3247,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;
@@ -3029,6 +3308,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);
@@ -3053,9 +3349,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
@@ -3065,8 +3362,7 @@ void CL_ParseServerMessage(void)
 
        cl.last_received_message = realtime;
 
-       if (cls.netcon && cls.signon < SIGNONS)
-               CL_KeepaliveMessage(false);
+       CL_KeepaliveMessage(false);
 
 //
 // if recording demos, copy the message out
@@ -3322,7 +3618,7 @@ void CL_ParseServerMessage(void)
                                break;
 
                        case qw_svc_sellscreen:
-                               Cmd_ExecuteString ("help", src_command);
+                               Cmd_ExecuteString ("help", src_command, true);
                                break;
 
                        case qw_svc_smallkick:
@@ -3420,7 +3716,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
@@ -3449,6 +3745,7 @@ void CL_ParseServerMessage(void)
 
                        if (cmd == -1)
                        {
+//                             R_TimeReport("END OF MESSAGE");
                                SHOWNET("END OF MESSAGE");
                                break;          // end of message
                        }
@@ -3529,7 +3826,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;
@@ -3553,7 +3850,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:
@@ -3621,7 +3953,7 @@ void CL_ParseServerMessage(void)
                                        {
                                                if (i >= 1 && i < MAX_MODELS)
                                                {
-                                                       dp_model_t *model = Mod_ForName(s, false, false, i == 1);
+                                                       dp_model_t *model = Mod_ForName(s, false, false, s[0] == '*' ? cl.model_name[1] : NULL);
                                                        if (!model)
                                                                Con_DPrintf("svc_precache: Mod_ForName(\"%s\") failed\n", s);
                                                        cl.model_precache[i] = model;
@@ -3634,7 +3966,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;
@@ -3711,7 +4043,7 @@ void CL_ParseServerMessage(void)
                                break;
 
                        case svc_setpause:
-                               cl.paused = MSG_ReadByte ();
+                               cl.paused = MSG_ReadByte () != 0;
                                if (cl.paused)
                                        CDAudio_Pause ();
                                else
@@ -3792,7 +4124,7 @@ void CL_ParseServerMessage(void)
                                break;
 
                        case svc_sellscreen:
-                               Cmd_ExecuteString ("help", src_command);
+                               Cmd_ExecuteString ("help", src_command, true);
                                break;
                        case svc_hidelmp:
                                if (gamemode == GAME_TENEBRAE)
@@ -3804,7 +4136,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();
@@ -3819,7 +4151,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
@@ -3858,22 +4190,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)
@@ -3893,10 +4232,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);