QW support getting closer
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 25 Feb 2006 10:11:36 +0000 (10:11 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 25 Feb 2006 10:11:36 +0000 (10:11 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@6029 d7cf8633-e32d-0410-b094-e92efae38249

cl_main.c
cl_parse.c
cl_particles.c
client.h
clvm_cmds.c
host.c
host_cmd.c
netconn.c
protocol.c
protocol.h

index 8386938..db35eac 100644 (file)
--- a/cl_main.c
+++ b/cl_main.c
@@ -674,6 +674,61 @@ void CL_UpdateLights(void)
        }
 }
 
+void CL_AddQWCTFFlagModel(entity_t *player, int skin)
+{
+       float f;
+       entity_t *flag;
+       matrix4x4_t flagmatrix;
+
+       // this code taken from QuakeWorld
+       f = 14;
+       if (player->render.frame2 >= 29 && player->render.frame2 <= 40)
+       {
+               if (player->render.frame2 >= 29 && player->render.frame2 <= 34)
+               { //axpain
+                       if      (player->render.frame2 == 29) f = f + 2;
+                       else if (player->render.frame2 == 30) f = f + 8;
+                       else if (player->render.frame2 == 31) f = f + 12;
+                       else if (player->render.frame2 == 32) f = f + 11;
+                       else if (player->render.frame2 == 33) f = f + 10;
+                       else if (player->render.frame2 == 34) f = f + 4;
+               }
+               else if (player->render.frame2 >= 35 && player->render.frame2 <= 40)
+               { // pain
+                       if      (player->render.frame2 == 35) f = f + 2;
+                       else if (player->render.frame2 == 36) f = f + 10;
+                       else if (player->render.frame2 == 37) f = f + 10;
+                       else if (player->render.frame2 == 38) f = f + 8;
+                       else if (player->render.frame2 == 39) f = f + 4;
+                       else if (player->render.frame2 == 40) f = f + 2;
+               }
+       }
+       else if (player->render.frame2 >= 103 && player->render.frame2 <= 118)
+       {
+               if      (player->render.frame2 >= 103 && player->render.frame2 <= 104) f = f + 6;  //nailattack
+               else if (player->render.frame2 >= 105 && player->render.frame2 <= 106) f = f + 6;  //light
+               else if (player->render.frame2 >= 107 && player->render.frame2 <= 112) f = f + 7;  //rocketattack
+               else if (player->render.frame2 >= 112 && player->render.frame2 <= 118) f = f + 7;  //shotattack
+       }
+       // end of code taken from QuakeWorld
+
+       flag = CL_NewTempEntity();
+       if (!flag)
+               return;
+
+       flag->render.model = cl.model_precache[cl.qw_modelindex_flag];
+       flag->render.skinnum = skin;
+       flag->render.colormap = -1; // no special coloring
+       flag->render.alpha = 1;
+       VectorSet(flag->render.colormod, 1, 1, 1);
+       // attach the flag to the player matrix
+       Matrix4x4_CreateFromQuakeEntity(&flagmatrix, -f, -22, 0, 0, 0, -45, 1);
+       Matrix4x4_Concat(&flag->render.matrix, &flagmatrix, &player->render.matrix);
+       Matrix4x4_Invert_Simple(&flag->render.inversematrix, &flag->render.matrix);
+       R_LerpAnimation(&flag->render);
+       CL_BoundingBoxForEntity(&flag->render);
+}
+
 #define MAXVIEWMODELS 32
 entity_t *viewmodels[MAXVIEWMODELS];
 int numviewmodels;
@@ -994,6 +1049,11 @@ void CL_LinkNetworkEntity(entity_t *e)
                                dlightcolor[1] += 0.7f;
                                dlightcolor[2] += 0.3f;
                        }
+                       if (e->render.effects & (EF_FLAG1QW | EF_FLAG2QW))
+                       {
+                               // these are only set on player entities
+                               CL_AddQWCTFFlagModel(e, (e->render.effects & EF_FLAG2QW) != 0);
+                       }
                }
                // muzzleflash fades over time, and is offset a bit
                if (e->persistent.muzzleflash > 0)
@@ -1421,6 +1481,34 @@ void CL_RelinkBeams(void)
        }
 }
 
+static void CL_RelinkQWNails(void)
+{
+       int i;
+       vec_t *v;
+       entity_t *ent;
+
+       for (i = 0;i < cl.qw_num_nails;i++)
+       {
+               v = cl.qw_nails[i];
+
+               // if we're drawing effects, get a new temp entity
+               // (NewTempEntity adds it to the render entities list for us)
+               if (!(ent = CL_NewTempEntity()))
+                       continue;
+
+               // normal stuff
+               ent->render.model = cl.model_precache[cl.qw_modelindex_spike];
+               ent->render.colormap = -1; // no special coloring
+               ent->render.alpha = 1;
+               VectorSet(ent->render.colormod, 1, 1, 1);
+
+               Matrix4x4_CreateFromQuakeEntity(&ent->render.matrix, v[0], v[1], v[2], v[3], v[4], v[5], 1);
+               Matrix4x4_Invert_Simple(&ent->render.inversematrix, &ent->render.matrix);
+               R_LerpAnimation(&ent->render);
+               CL_BoundingBoxForEntity(&ent->render);
+       }
+}
+
 void CL_LerpPlayer(float frac)
 {
        int i;
@@ -1467,6 +1555,7 @@ void CSQC_RelinkAllEntities (int drawmask)
                CL_RelinkStaticEntities();
                CL_RelinkBeams();
                CL_RelinkEffects();
+               CL_RelinkQWNails();
        }
 }
 
index de1dbe4..c0aab1f 100644 (file)
@@ -158,6 +158,13 @@ char *qw_svc_strings[128] =
 cvar_t demo_nehahra = {0, "demo_nehahra", "0", "reads all quake demos as nehahra movie protocol"};
 cvar_t developer_networkentities = {0, "developer_networkentities", "0", "prints received entities, value is 0-4 (higher for more info)"};
 
+static qboolean QW_CL_CheckOrDownloadFile(const char *filename);
+static void QW_CL_RequestNextDownload(void);
+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);
+
 /*
 ==================
 CL_ParseStartSoundPacket
@@ -172,34 +179,56 @@ void CL_ParseStartSoundPacket(int largesoundindex)
        int     field_mask;
        float   attenuation;
 
-       field_mask = MSG_ReadByte();
+       if (cls.protocol == PROTOCOL_QUAKEWORLD)
+       {
+               channel = MSG_ReadShort();
 
-       if (field_mask & SND_VOLUME)
-               volume = MSG_ReadByte ();
-       else
-               volume = DEFAULT_SOUND_PACKET_VOLUME;
+               if (channel & (1<<15))
+                       volume = MSG_ReadByte ();
+               else
+                       volume = DEFAULT_SOUND_PACKET_VOLUME;
 
-       if (field_mask & SND_ATTENUATION)
-               attenuation = MSG_ReadByte () / 64.0;
-       else
-               attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
+               if (channel & (1<<14))
+                       attenuation = MSG_ReadByte () / 64.0;
+               else
+                       attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
 
-       if (field_mask & SND_LARGEENTITY)
-       {
-               ent = (unsigned short) MSG_ReadShort ();
-               channel = MSG_ReadByte ();
+               ent = (channel>>3)&1023;
+               channel &= 7;
+
+               sound_num = MSG_ReadByte ();
        }
        else
        {
-               channel = (unsigned short) MSG_ReadShort ();
-               ent = channel >> 3;
-               channel &= 7;
-       }
+               field_mask = MSG_ReadByte();
 
-       if (largesoundindex || field_mask & SND_LARGESOUND)
-               sound_num = (unsigned short) MSG_ReadShort ();
-       else
-               sound_num = MSG_ReadByte ();
+               if (field_mask & SND_VOLUME)
+                       volume = MSG_ReadByte ();
+               else
+                       volume = DEFAULT_SOUND_PACKET_VOLUME;
+
+               if (field_mask & SND_ATTENUATION)
+                       attenuation = MSG_ReadByte () / 64.0;
+               else
+                       attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
+
+               if (field_mask & SND_LARGEENTITY)
+               {
+                       ent = (unsigned short) MSG_ReadShort ();
+                       channel = MSG_ReadByte ();
+               }
+               else
+               {
+                       channel = (unsigned short) MSG_ReadShort ();
+                       ent = channel >> 3;
+                       channel &= 7;
+               }
+
+               if (largesoundindex || field_mask & SND_LARGESOUND)
+                       sound_num = (unsigned short) MSG_ReadShort ();
+               else
+                       sound_num = MSG_ReadByte ();
+       }
 
        MSG_ReadVector(pos, cls.protocol);
 
@@ -317,6 +346,466 @@ void CL_ParseEntityLump(char *entdata)
        }
 }
 
+static qboolean QW_CL_CheckOrDownloadFile(const char *filename)
+{
+       qfile_t *file;
+
+       // see if the file already exists
+       file = FS_Open(filename, "rb", true, false);
+       if (file)
+       {
+               FS_Close(file);
+               return true;
+       }
+
+       // download messages in a demo would be bad
+       if (cls.demorecording)
+       {
+               Con_Printf("Unable to download \"%s\" when recording.\n", filename);
+               return true;
+       }
+
+       // don't try to download when playing a demo
+       if (!cls.netcon)
+               return true;
+
+       strlcpy(cls.qw_downloadname, filename, sizeof(cls.qw_downloadname));
+       Con_Printf("Downloading %s\n", filename);
+
+       if (!cls.qw_downloadmemory)
+       {
+               cls.qw_downloadmemory = NULL;
+               cls.qw_downloadmemorycursize = 0;
+               cls.qw_downloadmemorymaxsize = 1024*1024; // start out with a 1MB buffer
+       }
+
+       MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
+       MSG_WriteString(&cls.netcon->message, va("download %s", filename));
+
+       cls.qw_downloadnumber++;
+       cls.qw_downloadpercent = 0;
+
+       return false;
+}
+
+static void QW_CL_RequestNextDownload(void)
+{
+       int i;
+
+       // clear name of file that just finished
+       cls.qw_downloadname[0] = 0;
+
+       switch (cls.qw_downloadtype)
+       {
+       case dl_single:
+               break;
+       case dl_skin:
+               // TODO
+               break;
+       case dl_model:
+               if (cls.qw_downloadnumber == 0)
+               {
+                       Con_Printf("Checking models...\n");
+                       cls.qw_downloadnumber = 1;
+               }
+
+               cls.qw_downloadtype = dl_model;
+               for (;cls.qw_downloadnumber < MAX_MODELS && cl.model_name[cls.qw_downloadnumber][0];cls.qw_downloadnumber++)
+               {
+                       // skip submodels
+                       if (cl.model_name[cls.qw_downloadnumber][0] == '*')
+                               continue;
+                       if (!strcmp(cl.model_name[cls.qw_downloadnumber], "progs/spike.mdl"))
+                               cl.qw_modelindex_spike = cls.qw_downloadnumber;
+                       if (!strcmp(cl.model_name[cls.qw_downloadnumber], "progs/player.mdl"))
+                               cl.qw_modelindex_player = cls.qw_downloadnumber;
+                       if (!strcmp(cl.model_name[cls.qw_downloadnumber], "progs/flag.mdl"))
+                               cl.qw_modelindex_flag = cls.qw_downloadnumber;
+                       if (!strcmp(cl.model_name[cls.qw_downloadnumber], "progs/s_explod.spr"))
+                               cl.qw_modelindex_s_explod = cls.qw_downloadnumber;
+                       // check if we need to download the file, and return if so
+                       if (!QW_CL_CheckOrDownloadFile(cl.model_name[cls.qw_downloadnumber]))
+                               return;
+               }
+
+               // touch all of the precached models that are still loaded so we can free
+               // anything that isn't needed
+               Mod_ClearUsed();
+               for (i = 1;i < MAX_MODELS && cl.model_name[i][0];i++)
+                       Mod_FindName(cl.model_name[i]);
+               // 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();
+
+               // now we try to load everything that is new
+
+               // world model
+               cl.model_precache[1] = Mod_ForName(cl.model_name[1], false, false, true);
+               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)
+                               Con_Printf("Model %s could not be found or downloaded\n", cl.model_name[i]);
+
+               // done checking sounds and models, send a prespawn command now
+               MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
+               // FIXME: calculate the checksum2 this wants
+               //MSG_WriteString(&cls.netcon->message, va(qw_prespawn_name, cl.qw_servercount, cl.worldmodel->checksum2));
+               MSG_WriteString(&cls.netcon->message, va(qw_prespawn_name, cl.qw_servercount, 0));
+
+               if (cls.qw_downloadmemory)
+               {
+                       Mem_Free(cls.qw_downloadmemory);
+                       cls.qw_downloadmemory = NULL;
+               }
+               break;
+       case dl_sound:
+               if (cls.qw_downloadnumber == 0)
+               {
+                       Con_Printf("Checking sounds...\n");
+                       cls.qw_downloadnumber = 1;
+               }
+
+               cls.qw_downloadtype = dl_sound;
+               for (;cl.sound_name[cls.qw_downloadnumber][0];cls.qw_downloadnumber++)
+               {
+                       // skip subsounds
+                       if (cl.sound_name[cls.qw_downloadnumber][0] == '*')
+                               continue;
+                       // check if we need to download the file, and return if so
+                       if (!QW_CL_CheckOrDownloadFile(cl.sound_name[cls.qw_downloadnumber]))
+                               return;
+               }
+
+               // 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);
+
+               // precache any sounds used by the client
+               cl.sfx_wizhit = S_PrecacheSound("sound/wizard/hit.wav", false, true);
+               cl.sfx_knighthit = S_PrecacheSound("sound/hknight/hit.wav", false, true);
+               cl.sfx_tink1 = S_PrecacheSound("sound/weapons/tink1.wav", false, true);
+               cl.sfx_ric1 = S_PrecacheSound("sound/weapons/ric1.wav", false, true);
+               cl.sfx_ric2 = S_PrecacheSound("sound/weapons/ric2.wav", false, true);
+               cl.sfx_ric3 = S_PrecacheSound("sound/weapons/ric3.wav", false, true);
+               cl.sfx_r_exp3 = S_PrecacheSound("sound/weapons/r_exp3.wav", false, true);
+
+               // sounds
+               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);
+               }
+
+               // done with sound downloads, next we check models
+               MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
+               MSG_WriteString(&cls.netcon->message, va(qw_modellist_name, cl.qw_servercount, 0));
+               break;
+       case dl_none:
+       default:
+               Con_Printf("Unknown download type.\n");
+       }
+}
+
+static void QW_CL_ParseDownload(void)
+{
+       int size = MSG_ReadShort();
+       int percent = MSG_ReadByte();
+
+       // skip the download fragment if playing a demo
+       if (!cls.netcon)
+       {
+               if (size > 0)
+                       msg_readcount += size;
+               return;
+       }
+
+       if (size == -1)
+       {
+               Con_Printf("File not found.\n");
+               QW_CL_RequestNextDownload();
+               return;
+       }
+
+       if (msg_readcount + (unsigned short)size > net_message.cursize)
+               Host_Error("corrupt download message\n");
+
+       // make sure the buffer is big enough to include this new fragment
+       if (!cls.qw_downloadmemory || cls.qw_downloadmemorymaxsize < cls.qw_downloadmemorycursize + size)
+       {
+               unsigned char *old;
+               while (cls.qw_downloadmemorymaxsize < cls.qw_downloadmemorycursize + size)
+                       cls.qw_downloadmemorymaxsize *= 2;
+               old = cls.qw_downloadmemory;
+               cls.qw_downloadmemory = Mem_Alloc(cl_mempool, cls.qw_downloadmemorymaxsize);
+               if (old)
+               {
+                       memcpy(cls.qw_downloadmemory, old, cls.qw_downloadmemorycursize);
+                       Mem_Free(old);
+               }
+       }
+
+       // read the fragment out of the packet
+       MSG_ReadBytes(size, cls.qw_downloadmemory + cls.qw_downloadmemorycursize);
+       cls.qw_downloadmemorycursize += size;
+
+       cls.qw_downloadpercent = percent;
+
+       if (percent != 100)
+       {
+               // request next fragment
+               MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
+               MSG_WriteString(&cls.netcon->message, "nextdl");
+       }
+       else
+       {
+               // finished file
+               Con_Printf("Downloaded \"%s\"\n", cls.qw_downloadname);
+
+               FS_WriteFile(cls.qw_downloadname, cls.qw_downloadmemory, cls.qw_downloadmemorycursize);
+
+               cls.qw_downloadpercent = 0;
+
+               // start downloading the next file (or join the game)
+               QW_CL_RequestNextDownload();
+       }
+}
+
+static void QW_CL_ParseModelList(void)
+{
+       int n;
+       int nummodels = MSG_ReadByte();
+       char *str;
+
+       // parse model precache list
+       for (;;)
+       {
+               str = MSG_ReadString();
+               if (!str[0])
+                       break;
+               nummodels++;
+               if (nummodels==MAX_MODELS)
+                       Host_Error("Server sent too many model precaches");
+               if (strlen(str) >= MAX_QPATH)
+                       Host_Error("Server sent a precache name of %i characters (max %i)", strlen(str), MAX_QPATH - 1);
+               strlcpy(cl.model_name[nummodels], str, sizeof (cl.model_name[nummodels]));
+       }
+
+       n = MSG_ReadByte();
+       {
+               MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
+               MSG_WriteString(&cls.netcon->message, va(qw_modellist_name, cl.qw_servercount, n));
+               return;
+       }
+
+       cls.qw_downloadnumber = 0;
+       cls.qw_downloadtype = dl_model;
+       QW_CL_RequestNextDownload();
+}
+
+static void QW_CL_ParseSoundList(void)
+{
+       int n;
+       int numsounds = MSG_ReadByte();
+       char *str;
+
+       // parse sound precache list
+       for (;;)
+       {
+               str = MSG_ReadString();
+               if (!str[0])
+                       break;
+               numsounds++;
+               if (numsounds==MAX_SOUNDS)
+                       Host_Error("Server sent too many sound precaches");
+               if (strlen(str) >= MAX_QPATH)
+                       Host_Error("Server sent a precache name of %i characters (max %i)", strlen(str), MAX_QPATH - 1);
+               strlcpy(cl.sound_name[numsounds], str, sizeof (cl.sound_name[numsounds]));
+       }
+
+       n = MSG_ReadByte();
+
+       if (n)
+       {
+               MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
+               MSG_WriteString(&cls.netcon->message, va(qw_soundlist_name, cl.qw_servercount, n));
+               return;
+       }
+
+       cls.qw_downloadnumber = 0;
+       cls.qw_downloadtype = dl_sound;
+       QW_CL_RequestNextDownload();
+}
+
+void QW_CL_NextUpload(void)
+{
+       int r, percent, size;
+
+       if (!cls.qw_uploaddata)
+               return;
+
+       r = cls.qw_uploadsize - cls.qw_uploadpos;
+       if (r > 768)
+               r = 768;
+       size = min(1, cls.qw_uploadsize);
+       percent = (cls.qw_uploadpos+r)*100/size;
+
+       MSG_WriteByte(&cls.netcon->message, qw_clc_upload);
+       MSG_WriteShort(&cls.netcon->message, r);
+       MSG_WriteByte(&cls.netcon->message, percent);
+       SZ_Write(&cls.netcon->message, cls.qw_uploaddata + cls.qw_uploadpos, r);
+
+       Con_DPrintf("UPLOAD: %6d: %d written\n", cls.qw_uploadpos, r);
+
+       cls.qw_uploadpos += r;
+
+       if (cls.qw_uploadpos < cls.qw_uploadsize)
+               return;
+
+       Con_Printf("Upload completed\n");
+
+       QW_CL_StopUpload();
+}
+
+void QW_CL_StartUpload(unsigned char *data, int size)
+{
+       // do nothing in demos or if not connected
+       if (!cls.netcon)
+               return;
+
+       // abort existing upload if in progress
+       QW_CL_StopUpload();
+
+       Con_DPrintf("Starting upload of %d bytes...\n", size);
+
+       cls.qw_uploaddata = Mem_Alloc(cl_mempool, size);
+       memcpy(cls.qw_uploaddata, data, size);
+       cls.qw_uploadsize = size;
+       cls.qw_uploadpos = 0;
+
+       QW_CL_NextUpload();
+}
+
+#if 0
+qboolean QW_CL_IsUploading(void)
+{
+       return cls.qw_uploaddata != NULL;
+}
+#endif
+
+void QW_CL_StopUpload(void)
+{
+       if (cls.qw_uploaddata)
+               Mem_Free(cls.qw_uploaddata);
+       cls.qw_uploaddata = NULL;
+       cls.qw_uploadsize = 0;
+       cls.qw_uploadpos = 0;
+}
+
+static void QW_CL_ProcessUserInfo(int slot)
+{
+       int topcolor, bottomcolor;
+       char temp[2048];
+       InfoString_GetValue(cl.scores[slot].qw_userinfo, "name", cl.scores[slot].name, sizeof(cl.scores[slot].name));
+       InfoString_GetValue(cl.scores[slot].qw_userinfo, "topcolor", temp, sizeof(temp));topcolor = atoi(temp);
+       InfoString_GetValue(cl.scores[slot].qw_userinfo, "bottomcolor", temp, sizeof(temp));bottomcolor = atoi(temp);
+       cl.scores[slot].colors = topcolor * 16 + bottomcolor;
+       InfoString_GetValue(cl.scores[slot].qw_userinfo, "*spectator", temp, sizeof(temp));
+       cl.scores[slot].qw_spectator = temp[0] != 0;
+       InfoString_GetValue(cl.scores[slot].qw_userinfo, "skin", cl.scores[slot].qw_skin, sizeof(cl.scores[slot].qw_skin));
+       // LordHavoc: abusing Draw_CachePic for caching skins...
+       cl.scores[slot].qw_skin_cachepic = Draw_CachePic(cl.scores[slot].qw_skin, true);
+}
+
+static void QW_CL_UpdateUserInfo(void)
+{
+       int slot;
+       slot = MSG_ReadByte();
+       if (slot >= cl.maxclients)
+       {
+               Con_Printf("svc_updateuserinfo >= cl.maxclients\n");
+               MSG_ReadLong();
+               MSG_ReadString();
+               return;
+       }
+       cl.scores[slot].qw_userid = MSG_ReadLong();
+       strlcpy(cl.scores[slot].qw_userinfo, MSG_ReadString(), sizeof(cl.scores[slot].qw_userinfo));
+
+       QW_CL_ProcessUserInfo(slot);
+}
+
+static void QW_CL_SetInfo(void)
+{
+       int slot;
+       char key[2048];
+       char value[2048];
+       slot = MSG_ReadByte();
+       strlcpy(key, MSG_ReadString(), sizeof(key));
+       strlcpy(value, MSG_ReadString(), sizeof(value));
+       if (slot >= cl.maxclients)
+       {
+               Con_Printf("svc_setinfo >= cl.maxclients\n");
+               return;
+       }
+       InfoString_SetValue(cl.scores[slot].qw_userinfo, sizeof(cl.scores[slot].qw_userinfo), key, value);
+
+       QW_CL_ProcessUserInfo(slot);
+}
+
+static void QW_CL_ServerInfo(void)
+{
+       char key[2048];
+       char value[2048];
+       strlcpy(key, MSG_ReadString(), sizeof(key));
+       strlcpy(value, MSG_ReadString(), sizeof(value));
+       Con_DPrintf("SERVERINFO: %s=%s\n", key, value);
+       InfoString_SetValue(cl.qw_serverinfo, sizeof(cl.qw_serverinfo), key, value);
+}
+
+static void QW_CL_ParseNails(void)
+{
+       int i, j;
+       int numnails = MSG_ReadByte();
+       vec_t *v;
+       unsigned char bits[6];
+       cl.qw_num_nails = 0;
+       for (i = 0;i < numnails;i++)
+       {
+               v = cl.qw_nails[cl.qw_num_nails++];
+               for (j = 0;j < 6;j++)
+                       bits[j] = MSG_ReadByte();
+               v[0] = ( ( bits[0] + ((bits[1]&15)<<8) ) <<1) - 4096;
+               v[1] = ( ( (bits[1]>>4) + (bits[2]<<4) ) <<1) - 4096;
+               v[2] = ( ( bits[3] + ((bits[4]&15)<<8) ) <<1) - 4096;
+               v[3] = 360*(bits[4]>>4)/16;
+               v[4] = 360*bits[5]/256;
+               v[5] = 0;
+       }
+}
+
+static void QW_CL_UpdateItemsAndWeapon(void)
+{
+       int j;
+       // check for important changes
+
+       // set flash times
+       if (cl.olditems != cl.stats[STAT_ITEMS])
+               for (j = 0;j < 32;j++)
+                       if ((cl.stats[STAT_ITEMS] & (1<<j)) && !(cl.olditems & (1<<j)))
+                               cl.item_gettime[j] = cl.time;
+       cl.olditems = cl.stats[STAT_ITEMS];
+
+       // GAME_NEXUIZ hud needs weapon change time
+       if (cl.activeweapon != cl.stats[STAT_ACTIVEWEAPON])
+               cl.weapontime = cl.time;
+       cl.activeweapon = cl.stats[STAT_ACTIVEWEAPON];
+}
+
 /*
 =====================
 CL_SignonReply
@@ -422,123 +911,204 @@ void CL_ParseServerInfo (void)
        cls.protocol = protocol;
        Con_DPrintf("Server protocol is %s\n", Protocol_NameForEnum(cls.protocol));
 
-// parse maxclients
-       cl.maxclients = MSG_ReadByte ();
-       if (cl.maxclients < 1 || cl.maxclients > MAX_SCOREBOARD)
+       if (protocol == PROTOCOL_QUAKEWORLD)
        {
-               Host_Error("Bad maxclients (%u) from server", cl.maxclients);
-               return;
-       }
-       cl.scores = (scoreboard_t *)Mem_Alloc(cl_mempool, cl.maxclients*sizeof(*cl.scores));
+               cl.qw_servercount = MSG_ReadLong();
+
+               str = MSG_ReadString();
+               Con_Printf("server gamedir is %s\n", str);
+#if 0
+               // FIXME: change gamedir if needed!
+               if (strcasecmp(gamedirfile, str))
+               {
+                       Host_SaveConfig_f();
+                       cflag = 1;
+               }
 
-// parse gametype
-       cl.gametype = MSG_ReadByte ();
+               Com_Gamedir(str); // change gamedir
 
-// parse signon message
-       str = MSG_ReadString ();
-       strlcpy (cl.levelname, str, sizeof(cl.levelname));
+               if (cflag)
+               {
+                       // exec the new config stuff
+               }
+#endif
 
-// seperate the printfs so the server message can have a color
-       if (cls.protocol != PROTOCOL_NEHAHRAMOVIE) // no messages when playing the Nehahra movie
+       // parse maxclients
+               cl.maxclients = MSG_ReadByte ();
+               if (cl.maxclients & 128)
+               {
+                       cl.qw_spectator = true;
+                       cl.maxclients &= ~128;
+               }
+               if (cl.maxclients < 1 || cl.maxclients > MAX_SCOREBOARD)
+               {
+                       Host_Error("Bad maxclients (%u) from server", cl.maxclients);
+                       return;
+               }
+               cl.scores = (scoreboard_t *)Mem_Alloc(cl_mempool, cl.maxclients*sizeof(*cl.scores));
+
+               cl.gametype = GAME_DEATHMATCH;
+
+               // get the full level name
+               str = MSG_ReadString ();
+               strlcpy (cl.levelname, str, sizeof(cl.levelname));
+
+               // get the movevars
+               cl.qw_movevars_gravity            = MSG_ReadFloat();
+               cl.qw_movevars_stopspeed          = MSG_ReadFloat();
+               cl.qw_movevars_maxspeed           = MSG_ReadFloat();
+               cl.qw_movevars_spectatormaxspeed  = MSG_ReadFloat();
+               cl.qw_movevars_accelerate         = MSG_ReadFloat();
+               cl.qw_movevars_airaccelerate      = MSG_ReadFloat();
+               cl.qw_movevars_wateraccelerate    = MSG_ReadFloat();
+               cl.qw_movevars_friction           = MSG_ReadFloat();
+               cl.qw_movevars_waterfriction      = MSG_ReadFloat();
+               cl.qw_movevars_entgravity         = MSG_ReadFloat();
+
+               // 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);
 
-       // check memory integrity
-       Mem_CheckSentinelsGlobal();
+               // check memory integrity
+               Mem_CheckSentinelsGlobal();
 
-       S_StopAllSounds();
-       // if server is active, we already began a loading plaque
-       if (!sv.active)
-               SCR_BeginLoadingPlaque();
+               S_StopAllSounds();
+               // if server is active, we already began a loading plaque
+               if (!sv.active)
+                       SCR_BeginLoadingPlaque();
 
-       // disable until we get textures for it
-       R_ResetSkyBox();
+               // disable until we get textures for it
+               R_ResetSkyBox();
 
-       memset(cl.csqc_model_precache, 0, sizeof(cl.csqc_model_precache));      //[515]: csqc
-       memset(cl.model_precache, 0, sizeof(cl.model_precache));
-       memset(cl.sound_precache, 0, sizeof(cl.sound_precache));
+               memset(cl.csqc_model_precache, 0, sizeof(cl.csqc_model_precache));      //[515]: csqc
+               memset(cl.model_precache, 0, sizeof(cl.model_precache));
+               memset(cl.sound_precache, 0, sizeof(cl.sound_precache));
 
-       // parse model precache list
-       for (nummodels=1 ; ; nummodels++)
-       {
-               str = MSG_ReadString();
-               if (!str[0])
-                       break;
-               if (nummodels==MAX_MODELS)
-                       Host_Error ("Server sent too many model precaches");
-               if (strlen(str) >= MAX_QPATH)
-                       Host_Error ("Server sent a precache name of %i characters (max %i)", strlen(str), MAX_QPATH - 1);
-               strlcpy (cl.model_name[nummodels], str, sizeof (cl.model_name[nummodels]));
+               MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
+               MSG_WriteString(&cls.netcon->message, va(qw_soundlist_name, cl.qw_servercount, 0));
+
+               cls.state = ca_connected;
+               cls.signon = 1;
        }
-       // parse sound precache list
-       for (numsounds=1 ; ; numsounds++)
+       else
        {
-               str = MSG_ReadString();
-               if (!str[0])
-                       break;
-               if (numsounds==MAX_SOUNDS)
-                       Host_Error("Server sent too many sound precaches");
-               if (strlen(str) >= MAX_QPATH)
-                       Host_Error("Server sent a precache name of %i characters (max %i)", strlen(str), MAX_QPATH - 1);
-               strlcpy (cl.sound_name[numsounds], str, sizeof (cl.sound_name[numsounds]));
-       }
+       // parse maxclients
+               cl.maxclients = MSG_ReadByte ();
+               if (cl.maxclients < 1 || cl.maxclients > MAX_SCOREBOARD)
+               {
+                       Host_Error("Bad maxclients (%u) from server", cl.maxclients);
+                       return;
+               }
+               cl.scores = (scoreboard_t *)Mem_Alloc(cl_mempool, cl.maxclients*sizeof(*cl.scores));
 
-       // touch all of the precached models that are still loaded so we can free
-       // anything that isn't needed
-       Mod_ClearUsed();
-       for (i = 1;i < nummodels;i++)
-               Mod_FindName(cl.model_name[i]);
-       // 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();
-
-       // do the same for sounds
-       // FIXME: S_ServerSounds does not know about cl.sfx_ sounds
-       S_ServerSounds (cl.sound_name, numsounds);
-
-       // precache any sounds used by the client
-       cl.sfx_wizhit = S_PrecacheSound("sound/wizard/hit.wav", false, true);
-       cl.sfx_knighthit = S_PrecacheSound("sound/hknight/hit.wav", false, true);
-       cl.sfx_tink1 = S_PrecacheSound("sound/weapons/tink1.wav", false, true);
-       cl.sfx_ric1 = S_PrecacheSound("sound/weapons/ric1.wav", false, true);
-       cl.sfx_ric2 = S_PrecacheSound("sound/weapons/ric2.wav", false, true);
-       cl.sfx_ric3 = S_PrecacheSound("sound/weapons/ric3.wav", false, true);
-       cl.sfx_r_exp3 = S_PrecacheSound("sound/weapons/r_exp3.wav", false, true);
-
-       // now we try to load everything that is new
-
-       // world model
-       CL_KeepaliveMessage ();
-       cl.model_precache[1] = Mod_ForName(cl.model_name[1], false, false, true);
-       if (cl.model_precache[1]->Draw == NULL)
-               Con_Printf("Map %s not found\n", cl.model_name[1]);
-
-       // normal models
-       for (i=2 ; i<nummodels ; i++)
-       {
-               CL_KeepaliveMessage();
-               if ((cl.model_precache[i] = Mod_ForName(cl.model_name[i], false, false, false))->Draw == NULL)
-                       Con_Printf("Model %s not found\n", cl.model_name[i]);
-       }
+       // parse gametype
+               cl.gametype = MSG_ReadByte ();
 
-       // sounds
-       for (i=1 ; i<numsounds ; i++)
-       {
-               CL_KeepaliveMessage();
+       // parse signon message
+               str = MSG_ReadString ();
+               strlcpy (cl.levelname, str, sizeof(cl.levelname));
 
-               // Don't lock the sfx here, S_ServerSounds already did that
-               cl.sound_precache[i] = S_PrecacheSound (cl.sound_name[i], true, false);
-       }
+       // 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);
 
-       // local state
-       ent = &cl_entities[0];
-       // entire entity array was cleared, so just fill in a few fields
-       ent->state_current.active = true;
-       ent->render.model = cl.worldmodel = cl.model_precache[1];
-       ent->render.scale = 1; // some of the renderer still relies on scale
-       ent->render.alpha = 1;
+               // check memory integrity
+               Mem_CheckSentinelsGlobal();
+
+               S_StopAllSounds();
+               // if server is active, we already began a loading plaque
+               if (!sv.active)
+                       SCR_BeginLoadingPlaque();
+
+               // disable until we get textures for it
+               R_ResetSkyBox();
+
+               memset(cl.csqc_model_precache, 0, sizeof(cl.csqc_model_precache));      //[515]: csqc
+               memset(cl.model_precache, 0, sizeof(cl.model_precache));
+               memset(cl.sound_precache, 0, sizeof(cl.sound_precache));
+
+               // parse model precache list
+               for (nummodels=1 ; ; nummodels++)
+               {
+                       str = MSG_ReadString();
+                       if (!str[0])
+                               break;
+                       if (nummodels==MAX_MODELS)
+                               Host_Error ("Server sent too many model precaches");
+                       if (strlen(str) >= MAX_QPATH)
+                               Host_Error ("Server sent a precache name of %i characters (max %i)", strlen(str), MAX_QPATH - 1);
+                       strlcpy (cl.model_name[nummodels], str, sizeof (cl.model_name[nummodels]));
+               }
+               // parse sound precache list
+               for (numsounds=1 ; ; numsounds++)
+               {
+                       str = MSG_ReadString();
+                       if (!str[0])
+                               break;
+                       if (numsounds==MAX_SOUNDS)
+                               Host_Error("Server sent too many sound precaches");
+                       if (strlen(str) >= MAX_QPATH)
+                               Host_Error("Server sent a precache name of %i characters (max %i)", strlen(str), MAX_QPATH - 1);
+                       strlcpy (cl.sound_name[numsounds], str, sizeof (cl.sound_name[numsounds]));
+               }
+
+               // touch all of the precached models that are still loaded so we can free
+               // anything that isn't needed
+               Mod_ClearUsed();
+               for (i = 1;i < nummodels;i++)
+                       Mod_FindName(cl.model_name[i]);
+               // 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();
+
+               // do the same for sounds
+               // FIXME: S_ServerSounds does not know about cl.sfx_ sounds
+               S_ServerSounds (cl.sound_name, numsounds);
+
+               // precache any sounds used by the client
+               cl.sfx_wizhit = S_PrecacheSound("sound/wizard/hit.wav", false, true);
+               cl.sfx_knighthit = S_PrecacheSound("sound/hknight/hit.wav", false, true);
+               cl.sfx_tink1 = S_PrecacheSound("sound/weapons/tink1.wav", false, true);
+               cl.sfx_ric1 = S_PrecacheSound("sound/weapons/ric1.wav", false, true);
+               cl.sfx_ric2 = S_PrecacheSound("sound/weapons/ric2.wav", false, true);
+               cl.sfx_ric3 = S_PrecacheSound("sound/weapons/ric3.wav", false, true);
+               cl.sfx_r_exp3 = S_PrecacheSound("sound/weapons/r_exp3.wav", false, true);
+
+               // now we try to load everything that is new
+
+               // world model
+               CL_KeepaliveMessage ();
+               cl.model_precache[1] = Mod_ForName(cl.model_name[1], false, false, true);
+               if (cl.model_precache[1]->Draw == NULL)
+                       Con_Printf("Map %s not found\n", cl.model_name[1]);
+
+               // normal models
+               for (i=2 ; i<nummodels ; i++)
+               {
+                       CL_KeepaliveMessage();
+                       if ((cl.model_precache[i] = Mod_ForName(cl.model_name[i], false, false, false))->Draw == NULL)
+                               Con_Printf("Model %s not found\n", cl.model_name[i]);
+               }
+
+               // sounds
+               for (i=1 ; i<numsounds ; i++)
+               {
+                       CL_KeepaliveMessage();
+
+                       // Don't lock the sfx here, S_ServerSounds already did that
+                       cl.sound_precache[i] = S_PrecacheSound (cl.sound_name[i], true, false);
+               }
+       }
+
+       // local state
+       ent = &cl_entities[0];
+       // entire entity array was cleared, so just fill in a few fields
+       ent->state_current.active = true;
+       ent->render.model = cl.worldmodel = cl.model_precache[1];
+       ent->render.scale = 1; // some of the renderer still relies on scale
+       ent->render.alpha = 1;
        ent->render.colormap = -1; // no special coloring
        ent->render.flags = RENDER_SHADOW | RENDER_LIGHT;
        Matrix4x4_CreateFromQuakeEntity(&ent->render.matrix, 0, 0, 0, 0, 0, 0, 1);
@@ -983,414 +1553,578 @@ void CL_ParseTempEntity(void)
        unsigned char *tempcolor;
        matrix4x4_t tempmatrix;
 
-       type = MSG_ReadByte();
-       switch (type)
+       if (cls.protocol == PROTOCOL_QUAKEWORLD)
        {
-       case TE_WIZSPIKE:
-               // spike hitting wall
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 4);
-               Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-               //CL_AllocDlight(NULL, &tempmatrix, 100, 0.12f, 0.50f, 0.12f, 500, 0.2, 0, -1, false, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
-               CL_RunParticleEffect(pos, vec3_origin, 20, 30);
-               S_StartSound(-1, 0, cl.sfx_wizhit, pos, 1, 1);
-               break;
+               type = MSG_ReadByte();
+               switch (type)
+               {
+               case QW_TE_WIZSPIKE:
+                       // spike hitting wall
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       //CL_AllocDlight(NULL, &tempmatrix, 100, 0.12f, 0.50f, 0.12f, 500, 0.2, 0, -1, false, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       CL_RunParticleEffect(pos, vec3_origin, 20, 30);
+                       S_StartSound(-1, 0, cl.sfx_wizhit, pos, 1, 1);
+                       break;
 
-       case TE_KNIGHTSPIKE:
-               // spike hitting wall
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 4);
-               Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-               //CL_AllocDlight(NULL, &tempmatrix, 100, 0.50f, 0.30f, 0.10f, 500, 0.2, 0, -1, false, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
-               CL_RunParticleEffect(pos, vec3_origin, 226, 20);
-               S_StartSound(-1, 0, cl.sfx_knighthit, pos, 1, 1);
-               break;
+               case QW_TE_KNIGHTSPIKE:
+                       // spike hitting wall
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       //CL_AllocDlight(NULL, &tempmatrix, 100, 0.50f, 0.30f, 0.10f, 500, 0.2, 0, -1, false, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       CL_RunParticleEffect(pos, vec3_origin, 226, 20);
+                       S_StartSound(-1, 0, cl.sfx_knighthit, pos, 1, 1);
+                       break;
 
-       case TE_SPIKE:
-               // spike hitting wall
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 4);
-               if (cl_particles_quake.integer)
-                       CL_RunParticleEffect(pos, vec3_origin, 0, 10);
-               else if (cl_particles_bulletimpacts.integer)
-               {
-                       CL_SparkShower(pos, vec3_origin, 15, 1);
-                       CL_Smoke(pos, vec3_origin, 15);
-               }
-               CL_BulletMark(pos);
-               if (rand() % 5)
-                       S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
-               else
-               {
-                       rnd = rand() & 3;
-                       if (rnd == 1)
-                               S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
-                       else if (rnd == 2)
-                               S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
+               case QW_TE_SPIKE:
+                       // spike hitting wall
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       if (cl_particles_quake.integer)
+                               CL_RunParticleEffect(pos, vec3_origin, 0, 10);
+                       else if (cl_particles_bulletimpacts.integer)
+                       {
+                               CL_SparkShower(pos, vec3_origin, 15, 1, 0);
+                               CL_Smoke(pos, vec3_origin, 15, 0);
+                       }
+                       CL_BulletMark(pos);
+                       if (rand() % 5)
+                               S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
                        else
-                               S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
-               }
-               break;
-       case TE_SPIKEQUAD:
-               // quad spike hitting wall
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 4);
-               if (cl_particles_quake.integer)
-                       CL_RunParticleEffect(pos, vec3_origin, 0, 10);
-               else if (cl_particles_bulletimpacts.integer)
-               {
-                       CL_SparkShower(pos, vec3_origin, 15, 1);
-                       CL_Smoke(pos, vec3_origin, 15);
-               }
-               CL_BulletMark(pos);
-               Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-               CL_AllocDlight(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
-               if (rand() % 5)
-                       S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
-               else
-               {
-                       rnd = rand() & 3;
-                       if (rnd == 1)
-                               S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
-                       else if (rnd == 2)
-                               S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
+                       {
+                               rnd = rand() & 3;
+                               if (rnd == 1)
+                                       S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
+                               else if (rnd == 2)
+                                       S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
+                               else
+                                       S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
+                       }
+                       break;
+               case QW_TE_SUPERSPIKE:
+                       // super spike hitting wall
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       if (cl_particles_quake.integer)
+                               CL_RunParticleEffect(pos, vec3_origin, 0, 20);
+                       else if (cl_particles_bulletimpacts.integer)
+                       {
+                               CL_SparkShower(pos, vec3_origin, 30, 1, 0);
+                               CL_Smoke(pos, vec3_origin, 30, 0);
+                       }
+                       CL_BulletMark(pos);
+                       if (rand() % 5)
+                               S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
                        else
-                               S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
-               }
-               break;
-       case TE_SUPERSPIKE:
-               // super spike hitting wall
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 4);
-               if (cl_particles_quake.integer)
-                       CL_RunParticleEffect(pos, vec3_origin, 0, 20);
-               else if (cl_particles_bulletimpacts.integer)
-               {
-                       CL_SparkShower(pos, vec3_origin, 30, 1);
-                       CL_Smoke(pos, vec3_origin, 30);
-               }
-               CL_BulletMark(pos);
-               if (rand() % 5)
-                       S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
-               else
-               {
-                       rnd = rand() & 3;
-                       if (rnd == 1)
-                               S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
-                       else if (rnd == 2)
-                               S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
+                       {
+                               rnd = rand() & 3;
+                               if (rnd == 1)
+                                       S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
+                               else if (rnd == 2)
+                                       S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
+                               else
+                                       S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
+                       }
+                       break;
+
+               case QW_TE_EXPLOSION:
+                       // rocket explosion
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 10);
+                       CL_ParticleExplosion(pos);
+                       // LordHavoc: boosted color from 1.0, 0.8, 0.4 to 1.25, 1.0, 0.5
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       CL_AllocDlight(NULL, &tempmatrix, 350, 4.0f, 2.0f, 0.50f, 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       if (gamemode != GAME_NEXUIZ)
+                               S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
+                       CL_Effect(pos, cl.qw_modelindex_s_explod, 0, 6, 10);
+                       break;
+
+               case QW_TE_TAREXPLOSION:
+                       // tarbaby explosion
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 10);
+                       CL_BlobExplosion(pos);
+
+                       if (gamemode != GAME_NEXUIZ)
+                               S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       CL_AllocDlight(NULL, &tempmatrix, 600, 1.6f, 0.8f, 2.0f, 1200, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       break;
+
+               case QW_TE_LIGHTNING1:
+                       // lightning bolts
+                       CL_ParseBeam(cl.model_bolt, true);
+                       break;
+
+               case QW_TE_LIGHTNING2:
+                       // lightning bolts
+                       CL_ParseBeam(cl.model_bolt2, true);
+                       break;
+
+               case QW_TE_LIGHTNING3:
+                       // lightning bolts
+                       CL_ParseBeam(cl.model_bolt3, false);
+                       break;
+
+               case QW_TE_LAVASPLASH:
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_LavaSplash(pos);
+                       break;
+
+               case QW_TE_TELEPORT:
+                       MSG_ReadVector(pos, cls.protocol);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       CL_AllocDlight(NULL, &tempmatrix, 200, 1.0f, 1.0f, 1.0f, 600, 99.0f, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       CL_TeleportSplash(pos);
+                       break;
+
+               case QW_TE_GUNSHOT:
+                       // bullet hitting wall
+                       radius = MSG_ReadByte();
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       if (cl_particles_quake.integer)
+                               CL_RunParticleEffect(pos, vec3_origin, 0, 20*radius);
                        else
-                               S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
-               }
-               break;
-       case TE_SUPERSPIKEQUAD:
-               // quad super spike hitting wall
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 4);
-               if (cl_particles_quake.integer)
-                       CL_RunParticleEffect(pos, vec3_origin, 0, 20);
-               else if (cl_particles_bulletimpacts.integer)
-               {
-                       CL_SparkShower(pos, vec3_origin, 30, 1);
-                       CL_Smoke(pos, vec3_origin, 30);
+                       {
+                               CL_SparkShower(pos, vec3_origin, 15*radius, 1, radius);
+                               CL_Smoke(pos, vec3_origin, 15*radius, radius);
+                       }
+                       // TODO: scatter bullet marks throughout the sphere?
+                       CL_BulletMark(pos);
+                       break;
+
+               case QW_TE_BLOOD:
+                       count = MSG_ReadByte();
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       CL_BloodPuff(pos, vec3_origin, 20*count);
+                       break;
+
+               case QW_TE_LIGHTNINGBLOOD:
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       CL_BloodPuff(pos, vec3_origin, 50);
+                       break;
+
+               default:
+                       Host_Error("CL_ParseTempEntity: bad type %d (hex %02X)", type, type);
                }
-               CL_BulletMark(pos);
-               Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-               CL_AllocDlight(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
-               if (rand() % 5)
-                       S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
-               else
+       }
+       else
+       {
+               type = MSG_ReadByte();
+               switch (type)
                {
-                       rnd = rand() & 3;
-                       if (rnd == 1)
-                               S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
-                       else if (rnd == 2)
-                               S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
+               case TE_WIZSPIKE:
+                       // spike hitting wall
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       //CL_AllocDlight(NULL, &tempmatrix, 100, 0.12f, 0.50f, 0.12f, 500, 0.2, 0, -1, false, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       CL_RunParticleEffect(pos, vec3_origin, 20, 30);
+                       S_StartSound(-1, 0, cl.sfx_wizhit, pos, 1, 1);
+                       break;
+
+               case TE_KNIGHTSPIKE:
+                       // spike hitting wall
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       //CL_AllocDlight(NULL, &tempmatrix, 100, 0.50f, 0.30f, 0.10f, 500, 0.2, 0, -1, false, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       CL_RunParticleEffect(pos, vec3_origin, 226, 20);
+                       S_StartSound(-1, 0, cl.sfx_knighthit, pos, 1, 1);
+                       break;
+
+               case TE_SPIKE:
+                       // spike hitting wall
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       if (cl_particles_quake.integer)
+                               CL_RunParticleEffect(pos, vec3_origin, 0, 10);
+                       else if (cl_particles_bulletimpacts.integer)
+                       {
+                               CL_SparkShower(pos, vec3_origin, 15, 1, 0);
+                               CL_Smoke(pos, vec3_origin, 15, 0);
+                       }
+                       CL_BulletMark(pos);
+                       if (rand() % 5)
+                               S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
                        else
-                               S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
-               }
-               break;
-               // LordHavoc: added for improved blood splatters
-       case TE_BLOOD:
-               // blood puff
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 4);
-               dir[0] = MSG_ReadChar();
-               dir[1] = MSG_ReadChar();
-               dir[2] = MSG_ReadChar();
-               count = MSG_ReadByte();
-               CL_BloodPuff(pos, dir, count);
-               break;
-       case TE_SPARK:
-               // spark shower
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 4);
-               dir[0] = MSG_ReadChar();
-               dir[1] = MSG_ReadChar();
-               dir[2] = MSG_ReadChar();
-               count = MSG_ReadByte();
-               CL_SparkShower(pos, dir, count, 1);
-               break;
-       case TE_PLASMABURN:
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 4);
-               Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-               CL_AllocDlight(NULL, &tempmatrix, 200, 1, 1, 1, 1000, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
-               CL_PlasmaBurn(pos);
-               break;
-               // LordHavoc: added for improved gore
-       case TE_BLOODSHOWER:
-               // vaporized body
-               MSG_ReadVector(pos, cls.protocol); // mins
-               MSG_ReadVector(pos2, cls.protocol); // maxs
-               velspeed = MSG_ReadCoord(cls.protocol); // speed
-               count = (unsigned short) MSG_ReadShort(); // number of particles
-               CL_BloodShower(pos, pos2, velspeed, count);
-               break;
-       case TE_PARTICLECUBE:
-               // general purpose particle effect
-               MSG_ReadVector(pos, cls.protocol); // mins
-               MSG_ReadVector(pos2, cls.protocol); // maxs
-               MSG_ReadVector(dir, cls.protocol); // dir
-               count = (unsigned short) MSG_ReadShort(); // number of particles
-               colorStart = MSG_ReadByte(); // color
-               colorLength = MSG_ReadByte(); // gravity (1 or 0)
-               velspeed = MSG_ReadCoord(cls.protocol); // randomvel
-               CL_ParticleCube(pos, pos2, dir, count, colorStart, colorLength, velspeed);
-               break;
+                       {
+                               rnd = rand() & 3;
+                               if (rnd == 1)
+                                       S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
+                               else if (rnd == 2)
+                                       S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
+                               else
+                                       S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
+                       }
+                       break;
+               case TE_SPIKEQUAD:
+                       // quad spike hitting wall
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       if (cl_particles_quake.integer)
+                               CL_RunParticleEffect(pos, vec3_origin, 0, 10);
+                       else if (cl_particles_bulletimpacts.integer)
+                       {
+                               CL_SparkShower(pos, vec3_origin, 15, 1, 0);
+                               CL_Smoke(pos, vec3_origin, 15, 0);
+                       }
+                       CL_BulletMark(pos);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       CL_AllocDlight(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       if (rand() % 5)
+                               S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
+                       else
+                       {
+                               rnd = rand() & 3;
+                               if (rnd == 1)
+                                       S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
+                               else if (rnd == 2)
+                                       S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
+                               else
+                                       S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
+                       }
+                       break;
+               case TE_SUPERSPIKE:
+                       // super spike hitting wall
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       if (cl_particles_quake.integer)
+                               CL_RunParticleEffect(pos, vec3_origin, 0, 20);
+                       else if (cl_particles_bulletimpacts.integer)
+                       {
+                               CL_SparkShower(pos, vec3_origin, 30, 1, 0);
+                               CL_Smoke(pos, vec3_origin, 30, 0);
+                       }
+                       CL_BulletMark(pos);
+                       if (rand() % 5)
+                               S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
+                       else
+                       {
+                               rnd = rand() & 3;
+                               if (rnd == 1)
+                                       S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
+                               else if (rnd == 2)
+                                       S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
+                               else
+                                       S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
+                       }
+                       break;
+               case TE_SUPERSPIKEQUAD:
+                       // quad super spike hitting wall
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       if (cl_particles_quake.integer)
+                               CL_RunParticleEffect(pos, vec3_origin, 0, 20);
+                       else if (cl_particles_bulletimpacts.integer)
+                       {
+                               CL_SparkShower(pos, vec3_origin, 30, 1, 0);
+                               CL_Smoke(pos, vec3_origin, 30, 0);
+                       }
+                       CL_BulletMark(pos);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       CL_AllocDlight(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       if (rand() % 5)
+                               S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
+                       else
+                       {
+                               rnd = rand() & 3;
+                               if (rnd == 1)
+                                       S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
+                               else if (rnd == 2)
+                                       S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
+                               else
+                                       S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
+                       }
+                       break;
+                       // LordHavoc: added for improved blood splatters
+               case TE_BLOOD:
+                       // blood puff
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       dir[0] = MSG_ReadChar();
+                       dir[1] = MSG_ReadChar();
+                       dir[2] = MSG_ReadChar();
+                       count = MSG_ReadByte();
+                       CL_BloodPuff(pos, dir, count);
+                       break;
+               case TE_SPARK:
+                       // spark shower
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       dir[0] = MSG_ReadChar();
+                       dir[1] = MSG_ReadChar();
+                       dir[2] = MSG_ReadChar();
+                       count = MSG_ReadByte();
+                       CL_SparkShower(pos, dir, count, 1, 0);
+                       break;
+               case TE_PLASMABURN:
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       CL_AllocDlight(NULL, &tempmatrix, 200, 1, 1, 1, 1000, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       CL_PlasmaBurn(pos);
+                       break;
+                       // LordHavoc: added for improved gore
+               case TE_BLOODSHOWER:
+                       // vaporized body
+                       MSG_ReadVector(pos, cls.protocol); // mins
+                       MSG_ReadVector(pos2, cls.protocol); // maxs
+                       velspeed = MSG_ReadCoord(cls.protocol); // speed
+                       count = (unsigned short) MSG_ReadShort(); // number of particles
+                       CL_BloodShower(pos, pos2, velspeed, count);
+                       break;
+               case TE_PARTICLECUBE:
+                       // general purpose particle effect
+                       MSG_ReadVector(pos, cls.protocol); // mins
+                       MSG_ReadVector(pos2, cls.protocol); // maxs
+                       MSG_ReadVector(dir, cls.protocol); // dir
+                       count = (unsigned short) MSG_ReadShort(); // number of particles
+                       colorStart = MSG_ReadByte(); // color
+                       colorLength = MSG_ReadByte(); // gravity (1 or 0)
+                       velspeed = MSG_ReadCoord(cls.protocol); // randomvel
+                       CL_ParticleCube(pos, pos2, dir, count, colorStart, colorLength, velspeed);
+                       break;
 
-       case TE_PARTICLERAIN:
-               // general purpose particle effect
-               MSG_ReadVector(pos, cls.protocol); // mins
-               MSG_ReadVector(pos2, cls.protocol); // maxs
-               MSG_ReadVector(dir, cls.protocol); // dir
-               count = (unsigned short) MSG_ReadShort(); // number of particles
-               colorStart = MSG_ReadByte(); // color
-               CL_ParticleRain(pos, pos2, dir, count, colorStart, 0);
-               break;
+               case TE_PARTICLERAIN:
+                       // general purpose particle effect
+                       MSG_ReadVector(pos, cls.protocol); // mins
+                       MSG_ReadVector(pos2, cls.protocol); // maxs
+                       MSG_ReadVector(dir, cls.protocol); // dir
+                       count = (unsigned short) MSG_ReadShort(); // number of particles
+                       colorStart = MSG_ReadByte(); // color
+                       CL_ParticleRain(pos, pos2, dir, count, colorStart, 0);
+                       break;
 
-       case TE_PARTICLESNOW:
-               // general purpose particle effect
-               MSG_ReadVector(pos, cls.protocol); // mins
-               MSG_ReadVector(pos2, cls.protocol); // maxs
-               MSG_ReadVector(dir, cls.protocol); // dir
-               count = (unsigned short) MSG_ReadShort(); // number of particles
-               colorStart = MSG_ReadByte(); // color
-               CL_ParticleRain(pos, pos2, dir, count, colorStart, 1);
-               break;
+               case TE_PARTICLESNOW:
+                       // general purpose particle effect
+                       MSG_ReadVector(pos, cls.protocol); // mins
+                       MSG_ReadVector(pos2, cls.protocol); // maxs
+                       MSG_ReadVector(dir, cls.protocol); // dir
+                       count = (unsigned short) MSG_ReadShort(); // number of particles
+                       colorStart = MSG_ReadByte(); // color
+                       CL_ParticleRain(pos, pos2, dir, count, colorStart, 1);
+                       break;
 
-       case TE_GUNSHOT:
-               // bullet hitting wall
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 4);
-               if (cl_particles_quake.integer)
-                       CL_RunParticleEffect(pos, vec3_origin, 0, 20);
-               else
-               {
-                       CL_SparkShower(pos, vec3_origin, 15, 1);
-                       CL_Smoke(pos, vec3_origin, 15);
-               }
-               CL_BulletMark(pos);
-               break;
+               case TE_GUNSHOT:
+                       // bullet hitting wall
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       if (cl_particles_quake.integer)
+                               CL_RunParticleEffect(pos, vec3_origin, 0, 20);
+                       else
+                       {
+                               CL_SparkShower(pos, vec3_origin, 15, 1, 0);
+                               CL_Smoke(pos, vec3_origin, 15, 0);
+                       }
+                       CL_BulletMark(pos);
+                       break;
 
-       case TE_GUNSHOTQUAD:
-               // quad bullet hitting wall
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 4);
-               if (cl_particles_quake.integer)
-                       CL_RunParticleEffect(pos, vec3_origin, 0, 20);
-               else
-               {
-                       CL_SparkShower(pos, vec3_origin, 15, 1);
-                       CL_Smoke(pos, vec3_origin, 15);
-               }
-               CL_BulletMark(pos);
-               Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-               CL_AllocDlight(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
-               break;
+               case TE_GUNSHOTQUAD:
+                       // quad bullet hitting wall
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       if (cl_particles_quake.integer)
+                               CL_RunParticleEffect(pos, vec3_origin, 0, 20);
+                       else
+                       {
+                               CL_SparkShower(pos, vec3_origin, 15, 1, 0);
+                               CL_Smoke(pos, vec3_origin, 15, 0);
+                       }
+                       CL_BulletMark(pos);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       CL_AllocDlight(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       break;
 
-       case TE_EXPLOSION:
-               // rocket explosion
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 10);
-               CL_ParticleExplosion(pos);
-               // LordHavoc: boosted color from 1.0, 0.8, 0.4 to 1.25, 1.0, 0.5
-               Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-               CL_AllocDlight(NULL, &tempmatrix, 350, 4.0f, 2.0f, 0.50f, 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
-               if (gamemode != GAME_NEXUIZ)
-                       S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
-               break;
+               case TE_EXPLOSION:
+                       // rocket explosion
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 10);
+                       CL_ParticleExplosion(pos);
+                       // LordHavoc: boosted color from 1.0, 0.8, 0.4 to 1.25, 1.0, 0.5
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       CL_AllocDlight(NULL, &tempmatrix, 350, 4.0f, 2.0f, 0.50f, 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       if (gamemode != GAME_NEXUIZ)
+                               S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
+                       break;
 
-       case TE_EXPLOSIONQUAD:
-               // quad rocket explosion
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 10);
-               CL_ParticleExplosion(pos);
-               Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-               CL_AllocDlight(NULL, &tempmatrix, 350, 2.5f, 2.0f, 4.0f, 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
-               if (gamemode != GAME_NEXUIZ)
-                       S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
-               break;
+               case TE_EXPLOSIONQUAD:
+                       // quad rocket explosion
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 10);
+                       CL_ParticleExplosion(pos);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       CL_AllocDlight(NULL, &tempmatrix, 350, 2.5f, 2.0f, 4.0f, 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       if (gamemode != GAME_NEXUIZ)
+                               S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
+                       break;
 
-       case TE_EXPLOSION3:
-               // Nehahra movie colored lighting explosion
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 10);
-               CL_ParticleExplosion(pos);
-               Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-               color[0] = MSG_ReadCoord(cls.protocol) * (2.0f / 1.0f);
-               color[1] = MSG_ReadCoord(cls.protocol) * (2.0f / 1.0f);
-               color[2] = MSG_ReadCoord(cls.protocol) * (2.0f / 1.0f);
-               CL_AllocDlight(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
-               if (gamemode != GAME_NEXUIZ)
-                       S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
-               break;
+               case TE_EXPLOSION3:
+                       // Nehahra movie colored lighting explosion
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 10);
+                       CL_ParticleExplosion(pos);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       color[0] = MSG_ReadCoord(cls.protocol) * (2.0f / 1.0f);
+                       color[1] = MSG_ReadCoord(cls.protocol) * (2.0f / 1.0f);
+                       color[2] = MSG_ReadCoord(cls.protocol) * (2.0f / 1.0f);
+                       CL_AllocDlight(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       if (gamemode != GAME_NEXUIZ)
+                               S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
+                       break;
 
-       case TE_EXPLOSIONRGB:
-               // colored lighting explosion
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 10);
-               CL_ParticleExplosion(pos);
-               color[0] = MSG_ReadByte() * (2.0f / 255.0f);
-               color[1] = MSG_ReadByte() * (2.0f / 255.0f);
-               color[2] = MSG_ReadByte() * (2.0f / 255.0f);
-               Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-               CL_AllocDlight(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
-               if (gamemode != GAME_NEXUIZ)
-                       S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
-               break;
+               case TE_EXPLOSIONRGB:
+                       // colored lighting explosion
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 10);
+                       CL_ParticleExplosion(pos);
+                       color[0] = MSG_ReadByte() * (2.0f / 255.0f);
+                       color[1] = MSG_ReadByte() * (2.0f / 255.0f);
+                       color[2] = MSG_ReadByte() * (2.0f / 255.0f);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       CL_AllocDlight(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       if (gamemode != GAME_NEXUIZ)
+                               S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
+                       break;
 
-       case TE_TAREXPLOSION:
-               // tarbaby explosion
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 10);
-               CL_BlobExplosion(pos);
+               case TE_TAREXPLOSION:
+                       // tarbaby explosion
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 10);
+                       CL_BlobExplosion(pos);
 
-               if (gamemode != GAME_NEXUIZ)
-                       S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
-               Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-               CL_AllocDlight(NULL, &tempmatrix, 600, 1.6f, 0.8f, 2.0f, 1200, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
-               break;
+                       if (gamemode != GAME_NEXUIZ)
+                               S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       CL_AllocDlight(NULL, &tempmatrix, 600, 1.6f, 0.8f, 2.0f, 1200, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       break;
 
-       case TE_SMALLFLASH:
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 10);
-               Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-               CL_AllocDlight(NULL, &tempmatrix, 200, 2, 2, 2, 1000, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
-               break;
+               case TE_SMALLFLASH:
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 10);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       CL_AllocDlight(NULL, &tempmatrix, 200, 2, 2, 2, 1000, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       break;
 
-       case TE_CUSTOMFLASH:
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 4);
-               radius = (MSG_ReadByte() + 1) * 8;
-               velspeed = (MSG_ReadByte() + 1) * (1.0 / 256.0);
-               color[0] = MSG_ReadByte() * (2.0f / 255.0f);
-               color[1] = MSG_ReadByte() * (2.0f / 255.0f);
-               color[2] = MSG_ReadByte() * (2.0f / 255.0f);
-               Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-               CL_AllocDlight(NULL, &tempmatrix, radius, color[0], color[1], color[2], radius / velspeed, velspeed, 0, -1, true, 1, 0.25, 1, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
-               break;
+               case TE_CUSTOMFLASH:
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       radius = (MSG_ReadByte() + 1) * 8;
+                       velspeed = (MSG_ReadByte() + 1) * (1.0 / 256.0);
+                       color[0] = MSG_ReadByte() * (2.0f / 255.0f);
+                       color[1] = MSG_ReadByte() * (2.0f / 255.0f);
+                       color[2] = MSG_ReadByte() * (2.0f / 255.0f);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       CL_AllocDlight(NULL, &tempmatrix, radius, color[0], color[1], color[2], radius / velspeed, velspeed, 0, -1, true, 1, 0.25, 1, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       break;
 
-       case TE_FLAMEJET:
-               MSG_ReadVector(pos, cls.protocol);
-               MSG_ReadVector(dir, cls.protocol);
-               count = MSG_ReadByte();
-               CL_Flames(pos, dir, count);
-               break;
+               case TE_FLAMEJET:
+                       MSG_ReadVector(pos, cls.protocol);
+                       MSG_ReadVector(dir, cls.protocol);
+                       count = MSG_ReadByte();
+                       CL_Flames(pos, dir, count);
+                       break;
 
-       case TE_LIGHTNING1:
-               // lightning bolts
-               CL_ParseBeam(cl.model_bolt, true);
-               break;
+               case TE_LIGHTNING1:
+                       // lightning bolts
+                       CL_ParseBeam(cl.model_bolt, true);
+                       break;
 
-       case TE_LIGHTNING2:
-               // lightning bolts
-               CL_ParseBeam(cl.model_bolt2, true);
-               break;
+               case TE_LIGHTNING2:
+                       // lightning bolts
+                       CL_ParseBeam(cl.model_bolt2, true);
+                       break;
 
-       case TE_LIGHTNING3:
-               // lightning bolts
-               CL_ParseBeam(cl.model_bolt3, false);
-               break;
+               case TE_LIGHTNING3:
+                       // lightning bolts
+                       CL_ParseBeam(cl.model_bolt3, false);
+                       break;
 
-// PGM 01/21/97
-       case TE_BEAM:
-               // grappling hook beam
-               CL_ParseBeam(cl.model_beam, false);
-               break;
-// PGM 01/21/97
+       // PGM 01/21/97
+               case TE_BEAM:
+                       // grappling hook beam
+                       CL_ParseBeam(cl.model_beam, false);
+                       break;
+       // PGM 01/21/97
 
-// LordHavoc: for compatibility with the Nehahra movie...
-       case TE_LIGHTNING4NEH:
-               CL_ParseBeam(Mod_ForName(MSG_ReadString(), true, false, false), false);
-               break;
+       // LordHavoc: for compatibility with the Nehahra movie...
+               case TE_LIGHTNING4NEH:
+                       CL_ParseBeam(Mod_ForName(MSG_ReadString(), true, false, false), false);
+                       break;
 
-       case TE_LAVASPLASH:
-               MSG_ReadVector(pos, cls.protocol);
-               CL_LavaSplash(pos);
-               break;
+               case TE_LAVASPLASH:
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_LavaSplash(pos);
+                       break;
 
-       case TE_TELEPORT:
-               MSG_ReadVector(pos, cls.protocol);
-               Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-               CL_AllocDlight(NULL, &tempmatrix, 200, 1.0f, 1.0f, 1.0f, 600, 99.0f, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
-               CL_TeleportSplash(pos);
-               break;
+               case TE_TELEPORT:
+                       MSG_ReadVector(pos, cls.protocol);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       CL_AllocDlight(NULL, &tempmatrix, 200, 1.0f, 1.0f, 1.0f, 600, 99.0f, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       CL_TeleportSplash(pos);
+                       break;
 
-       case TE_EXPLOSION2:
-               // color mapped explosion
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 10);
-               colorStart = MSG_ReadByte();
-               colorLength = MSG_ReadByte();
-               CL_ParticleExplosion2(pos, colorStart, colorLength);
-               tempcolor = (unsigned char *)&palette_complete[(rand()%colorLength) + colorStart];
-               color[0] = tempcolor[0] * (2.0f / 255.0f);
-               color[1] = tempcolor[1] * (2.0f / 255.0f);
-               color[2] = tempcolor[2] * (2.0f / 255.0f);
-               Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-               CL_AllocDlight(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
-               if (gamemode != GAME_NEXUIZ)
-                       S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
-               break;
+               case TE_EXPLOSION2:
+                       // color mapped explosion
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 10);
+                       colorStart = MSG_ReadByte();
+                       colorLength = MSG_ReadByte();
+                       CL_ParticleExplosion2(pos, colorStart, colorLength);
+                       tempcolor = (unsigned char *)&palette_complete[(rand()%colorLength) + colorStart];
+                       color[0] = tempcolor[0] * (2.0f / 255.0f);
+                       color[1] = tempcolor[1] * (2.0f / 255.0f);
+                       color[2] = tempcolor[2] * (2.0f / 255.0f);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       CL_AllocDlight(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       if (gamemode != GAME_NEXUIZ)
+                               S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
+                       break;
 
-       case TE_TEI_G3:
-               MSG_ReadVector(pos, cls.protocol);
-               MSG_ReadVector(pos2, cls.protocol);
-               MSG_ReadVector(dir, cls.protocol);
-               CL_BeamParticle(pos, pos2, 8, 1, 1, 1, 1, 1);
-               break;
+               case TE_TEI_G3:
+                       MSG_ReadVector(pos, cls.protocol);
+                       MSG_ReadVector(pos2, cls.protocol);
+                       MSG_ReadVector(dir, cls.protocol);
+                       CL_BeamParticle(pos, pos2, 8, 1, 1, 1, 1, 1);
+                       break;
 
-       case TE_TEI_SMOKE:
-               MSG_ReadVector(pos, cls.protocol);
-               MSG_ReadVector(dir, cls.protocol);
-               count = MSG_ReadByte();
-               CL_FindNonSolidLocation(pos, pos, 4);
-               CL_Tei_Smoke(pos, dir, count);
-               break;
+               case TE_TEI_SMOKE:
+                       MSG_ReadVector(pos, cls.protocol);
+                       MSG_ReadVector(dir, cls.protocol);
+                       count = MSG_ReadByte();
+                       CL_FindNonSolidLocation(pos, pos, 4);
+                       CL_Tei_Smoke(pos, dir, count);
+                       break;
 
-       case TE_TEI_BIGEXPLOSION:
-               MSG_ReadVector(pos, cls.protocol);
-               CL_FindNonSolidLocation(pos, pos, 10);
-               CL_ParticleExplosion(pos);
-               Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-               CL_AllocDlight(NULL, &tempmatrix, 500, 2.5f, 2.0f, 1.0f, 500, 9999, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
-               if (gamemode != GAME_NEXUIZ)
-                       S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
-               break;
+               case TE_TEI_BIGEXPLOSION:
+                       MSG_ReadVector(pos, cls.protocol);
+                       CL_FindNonSolidLocation(pos, pos, 10);
+                       CL_ParticleExplosion(pos);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       CL_AllocDlight(NULL, &tempmatrix, 500, 2.5f, 2.0f, 1.0f, 500, 9999, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       if (gamemode != GAME_NEXUIZ)
+                               S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
+                       break;
 
-       case TE_TEI_PLASMAHIT:
-               MSG_ReadVector(pos, cls.protocol);
-               MSG_ReadVector(dir, cls.protocol);
-               count = MSG_ReadByte();
-               CL_FindNonSolidLocation(pos, pos, 5);
-               CL_Tei_PlasmaHit(pos, dir, count);
-               Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
-               CL_AllocDlight(NULL, &tempmatrix, 500, 0.6, 1.2, 2.0f, 2000, 9999, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
-               break;
+               case TE_TEI_PLASMAHIT:
+                       MSG_ReadVector(pos, cls.protocol);
+                       MSG_ReadVector(dir, cls.protocol);
+                       count = MSG_ReadByte();
+                       CL_FindNonSolidLocation(pos, pos, 5);
+                       CL_Tei_PlasmaHit(pos, dir, count);
+                       Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
+                       CL_AllocDlight(NULL, &tempmatrix, 500, 0.6, 1.2, 2.0f, 2000, 9999, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       break;
 
-       default:
-               Host_Error("CL_ParseTempEntity: bad type %d (hex %02X)", type, type);
+               default:
+                       Host_Error("CL_ParseTempEntity: bad type %d (hex %02X)", type, type);
+               }
        }
 }
 
@@ -1442,405 +2176,719 @@ void CL_ParseServerMessage(void)
 
        parsingerror = true;
 
-       while (1)
+       if (cls.protocol == PROTOCOL_QUAKEWORLD)
        {
-               if (msg_badread)
-                       Host_Error ("CL_ParseServerMessage: Bad server message");
-
-               cmd = MSG_ReadByte ();
+               cl.mtime[1] = cl.mtime[0];
+               cl.mtime[0] = realtime; // qw has no clock
 
-               if (cmd == -1)
+               while (1)
                {
-                       SHOWNET("END OF MESSAGE");
-                       break;          // end of message
-               }
+                       if (msg_badread)
+                               Host_Error ("CL_ParseServerMessage: Bad QW server message");
 
-               cmdindex = cmdcount & 31;
-               cmdcount++;
-               cmdlog[cmdindex] = cmd;
+                       cmd = MSG_ReadByte ();
 
-               // if the high bit of the command byte is set, it is a fast update
-               if (cmd & 128)
-               {
-                       // LordHavoc: fix for bizarre problem in MSVC that I do not understand (if I assign the string pointer directly it ends up storing a NULL pointer)
-                       temp = "entity";
-                       cmdlogname[cmdindex] = temp;
-                       SHOWNET("fast update");
-                       if (cls.signon == SIGNONS - 1)
+                       if (cmd == -1)
                        {
-                               // first update is the final signon stage
-                               cls.signon = SIGNONS;
-                               CL_SignonReply ();
+                               SHOWNET("END OF MESSAGE");
+                               break;          // end of message
                        }
-                       EntityFrameQuake_ReadEntity (cmd&127);
-                       continue;
-               }
 
-               SHOWNET(svc_strings[cmd]);
-               cmdlogname[cmdindex] = svc_strings[cmd];
-               if (!cmdlogname[cmdindex])
-               {
-                       // LordHavoc: fix for bizarre problem in MSVC that I do not understand (if I assign the string pointer directly it ends up storing a NULL pointer)
-                       temp = "<unknown>";
-                       cmdlogname[cmdindex] = temp;
-               }
+                       cmdindex = cmdcount & 31;
+                       cmdcount++;
+                       cmdlog[cmdindex] = cmd;
 
-               // other commands
-               switch (cmd)
-               {
-               default:
+                       SHOWNET(qw_svc_strings[cmd]);
+                       cmdlogname[cmdindex] = qw_svc_strings[cmd];
+                       if (!cmdlogname[cmdindex])
                        {
-                               char description[32*64], temp[64];
-                               int count;
-                               strcpy (description, "packet dump: ");
-                               i = cmdcount - 32;
-                               if (i < 0)
-                                       i = 0;
-                               count = cmdcount - i;
-                               i &= 31;
-                               while(count > 0)
+                               // LordHavoc: fix for bizarre problem in MSVC that I do not understand (if I assign the string pointer directly it ends up storing a NULL pointer)
+                               temp = "<unknown>";
+                               cmdlogname[cmdindex] = temp;
+                       }
+
+                       // other commands
+                       switch (cmd)
+                       {
+                       default:
                                {
-                                       dpsnprintf (temp, sizeof (temp), "%3i:%s ", cmdlog[i], cmdlogname[i]);
-                                       strlcat (description, temp, sizeof (description));
-                                       count--;
-                                       i++;
+                                       char description[32*64], temp[64];
+                                       int count;
+                                       strcpy(description, "packet dump: ");
+                                       i = cmdcount - 32;
+                                       if (i < 0)
+                                               i = 0;
+                                       count = cmdcount - i;
                                        i &= 31;
+                                       while(count > 0)
+                                       {
+                                               dpsnprintf(temp, sizeof(temp), "%3i:%s ", cmdlog[i], cmdlogname[i]);
+                                               strlcat(description, temp, sizeof(description));
+                                               count--;
+                                               i++;
+                                               i &= 31;
+                                       }
+                                       description[strlen(description)-1] = '\n'; // replace the last space with a newline
+                                       Con_Print(description);
+                                       Host_Error("CL_ParseServerMessage: Illegible server message");
                                }
-                               description[strlen(description)-1] = '\n'; // replace the last space with a newline
-                               Con_Print(description);
-                               Host_Error ("CL_ParseServerMessage: Illegible server message");
-                       }
-                       break;
+                               break;
 
-               case svc_nop:
-                       if (cls.signon < SIGNONS)
-                               Con_Print("<-- server to client keepalive\n");
-                       break;
+                       case qw_svc_nop:
+                               //Con_Printf("qw_svc_nop\n");
+                               break;
 
-               case svc_time:
-                       cl.mtime[1] = cl.mtime[0];
-                       cl.mtime[0] = MSG_ReadFloat ();
-                       cl.movement_needupdate = true;
-                       break;
+                       case qw_svc_disconnect:
+                               Con_Printf("Server disconnected\n");
+                               if (cls.demonum != -1)
+                                       CL_NextDemo();
+                               else
+                                       CL_Disconnect();
+                               break;
 
-               case svc_clientdata:
-                       CL_ParseClientdata();
-                       break;
+                       case qw_svc_print:
+                               i = MSG_ReadByte();
+                               if (i == 3) // chat
+                                       CSQC_AddPrintText(va("\1%s", MSG_ReadString()));        //[515]: csqc
+                               else
+                                       CSQC_AddPrintText(MSG_ReadString());
+                               break;
 
-               case svc_version:
-                       i = MSG_ReadLong ();
-                       protocol = Protocol_EnumForNumber(i);
-                       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)
-                               protocol = PROTOCOL_NEHAHRAMOVIE;
-                       cls.protocol = protocol;
-                       break;
+                       case qw_svc_centerprint:
+                               CL_VM_Parse_CenterPrint(MSG_ReadString ());     //[515]: csqc
+                               break;
 
-               case svc_disconnect:
-                       Con_Printf ("Server disconnected\n");
-                       if (cls.demonum != -1)
-                               CL_NextDemo ();
-                       else
-                               CL_Disconnect ();
-                       break;
+                       case qw_svc_stufftext:
+                               CL_VM_Parse_StuffCmd(MSG_ReadString ());        //[515]: csqc
+                               break;
 
-               case svc_print:
-                       CSQC_AddPrintText(MSG_ReadString());    //[515]: csqc
-                       break;
+                       case qw_svc_damage:
+                               // svc_damage protocol is identical to nq
+                               V_ParseDamage ();
+                               break;
 
-               case svc_centerprint:
-                       CL_VM_Parse_CenterPrint(MSG_ReadString ());     //[515]: csqc
-                       break;
+                       case qw_svc_serverdata:
+                               //Cbuf_Execute(); // make sure any stuffed commands are done
+                               CL_ParseServerInfo();
+                               CL_VM_Init();   //[515]: init csqc
+                               break;
 
-               case svc_stufftext:
-                       CL_VM_Parse_StuffCmd(MSG_ReadString ());        //[515]: csqc
-                       break;
+                       case qw_svc_setangle:
+                               for (i=0 ; i<3 ; i++)
+                                       cl.viewangles[i] = MSG_ReadAngle (cls.protocol);
+                               break;
 
-               case svc_damage:
-                       V_ParseDamage ();
-                       break;
+                       case qw_svc_lightstyle:
+                               i = MSG_ReadByte ();
+                               if (i >= cl_max_lightstyle)
+                               {
+                                       Con_Printf ("svc_lightstyle >= MAX_LIGHTSTYLES");
+                                       break;
+                               }
+                               strlcpy (cl_lightstyle[i].map,  MSG_ReadString(), sizeof (cl_lightstyle[i].map));
+                               cl_lightstyle[i].map[MAX_STYLESTRING - 1] = 0;
+                               cl_lightstyle[i].length = (int)strlen(cl_lightstyle[i].map);
+                               break;
 
-               case svc_serverinfo:
-                       CL_ParseServerInfo ();
-                       CL_VM_Init();   //[515]: init csqc
-                       break;
+                       case qw_svc_sound:
+                               CL_ParseStartSoundPacket(false);
+                               break;
 
-               case svc_setangle:
-                       for (i=0 ; i<3 ; i++)
-                               cl.viewangles[i] = MSG_ReadAngle (cls.protocol);
-                       break;
+                       case qw_svc_stopsound:
+                               i = (unsigned short) MSG_ReadShort();
+                               S_StopSound(i>>3, i&7);
+                               break;
 
-               case svc_setview:
-                       cl.viewentity = (unsigned short)MSG_ReadShort ();
-                       if (cl.viewentity >= MAX_EDICTS)
-                               Host_Error("svc_setview >= MAX_EDICTS");
-                       if (cl.viewentity >= cl_max_entities)
-                               CL_ExpandEntities(cl.viewentity);
-                       // LordHavoc: assume first setview recieved is the real player entity
-                       if (!cl.playerentity)
-                               cl.playerentity = cl.viewentity;
-                       break;
+                       case qw_svc_updatefrags:
+                               i = MSG_ReadByte();
+                               if (i >= cl.maxclients)
+                                       Host_Error("CL_ParseServerMessage: svc_updatefrags >= cl.maxclients");
+                               cl.scores[i].frags = (signed short) MSG_ReadShort();
+                               break;
 
-               case svc_lightstyle:
-                       i = MSG_ReadByte ();
-                       if (i >= cl_max_lightstyle)
-                       {
-                               Con_Printf ("svc_lightstyle >= MAX_LIGHTSTYLES");
+                       case qw_svc_updateping:
+                               i = MSG_ReadByte();
+                               if (i >= cl.maxclients)
+                                       Host_Error("CL_ParseServerMessage: svc_updateping >= cl.maxclients");
+                               cl.scores[i].qw_ping = MSG_ReadShort();
+                               break;
+
+                       case qw_svc_updatepl:
+                               i = MSG_ReadByte();
+                               if (i >= cl.maxclients)
+                                       Host_Error("CL_ParseServerMessage: svc_updatepl >= cl.maxclients");
+                               cl.scores[i].qw_packetloss = MSG_ReadByte();
+                               break;
+
+                       case qw_svc_updateentertime:
+                               i = MSG_ReadByte();
+                               if (i >= cl.maxclients)
+                                       Host_Error("CL_ParseServerMessage: svc_updateentertime >= cl.maxclients");
+                               // seconds ago
+                               cl.scores[i].qw_entertime = realtime - MSG_ReadFloat();
+                               break;
+
+                       case qw_svc_spawnbaseline:
+                               i = (unsigned short) MSG_ReadShort();
+                               if (i < 0 || i >= MAX_EDICTS)
+                                       Host_Error ("CL_ParseServerMessage: svc_spawnbaseline: invalid entity number %i", i);
+                               if (i >= cl_max_entities)
+                                       CL_ExpandEntities(i);
+                               CL_ParseBaseline(cl_entities + i, false);
+                               break;
+                       case qw_svc_spawnstatic:
+                               CL_ParseStatic(false);
+                               break;
+                       case qw_svc_temp_entity:
+                               if(!CL_VM_Parse_TempEntity())
+                                       CL_ParseTempEntity ();
+                               break;
+
+                       case qw_svc_killedmonster:
+                               cl.stats[STAT_MONSTERS]++;
+                               break;
+
+                       case qw_svc_foundsecret:
+                               cl.stats[STAT_SECRETS]++;
+                               break;
+
+                       case qw_svc_updatestat:
+                               i = MSG_ReadByte ();
+                               if (i < 0 || i >= MAX_CL_STATS)
+                                       Host_Error ("svc_updatestat: %i is invalid", i);
+                               cl.stats[i] = MSG_ReadByte ();
+                               break;
+
+                       case qw_svc_updatestatlong:
+                               i = MSG_ReadByte ();
+                               if (i < 0 || i >= MAX_CL_STATS)
+                                       Host_Error ("svc_updatestatlong: %i is invalid", i);
+                               cl.stats[i] = MSG_ReadLong ();
+                               break;
+
+                       case qw_svc_spawnstaticsound:
+                               CL_ParseStaticSound (false);
+                               break;
+
+                       case qw_svc_cdtrack:
+                               cl.cdtrack = MSG_ReadByte ();
+                               cl.looptrack = MSG_ReadByte ();
+                               if ( (cls.demoplayback || cls.demorecording) && (cls.forcetrack != -1) )
+                                       CDAudio_Play ((unsigned char)cls.forcetrack, true);
+                               else
+                                       CDAudio_Play ((unsigned char)cl.cdtrack, true);
+                               break;
+
+                       case qw_svc_intermission:
+                               cl.intermission = 1;
+                               cl.completed_time = cl.time;
+                               MSG_ReadVector(cl.qw_intermission_origin, cls.protocol);
+                               for (i = 0;i < 3;i++)
+                                       cl.qw_intermission_angles[i] = MSG_ReadAngle(cls.protocol);
+                               break;
+
+                       case qw_svc_finale:
+                               cl.intermission = 2;
+                               cl.completed_time = cl.time;
+                               SCR_CenterPrint(MSG_ReadString ());
+                               break;
+
+                       case qw_svc_sellscreen:
+                               Cmd_ExecuteString ("help", src_command);
+                               break;
+
+                       case qw_svc_smallkick:
+                               Con_Printf("TODO: qw_svc_smallkick\n");
+                               break;
+                       case qw_svc_bigkick:
+                               Con_Printf("TODO: qw_svc_bigkick\n");
+                               break;
+
+                       case qw_svc_muzzleflash:
+                               i = (unsigned short) MSG_ReadShort();
+                               // NOTE: in QW this only worked on clients
+                               if (i < 0 || i >= MAX_EDICTS)
+                                       Host_Error("CL_ParseServerMessage: svc_spawnbaseline: invalid entity number %i", i);
+                               if (i >= cl_max_entities)
+                                       CL_ExpandEntities(i);
+                               cl_entities[i].persistent.muzzleflash = 1.0f;
+                               break;
+
+                       case qw_svc_updateuserinfo:
+                               QW_CL_UpdateUserInfo();
+                               break;
+
+                       case qw_svc_setinfo:
+                               QW_CL_SetInfo();
+                               break;
+
+                       case qw_svc_serverinfo:
+                               QW_CL_ServerInfo();
+                               break;
+
+                       case qw_svc_download:
+                               QW_CL_ParseDownload();
+                               break;
+
+                       case qw_svc_playerinfo:
+                               EntityStateQW_ReadPlayerUpdate();
+                               break;
+
+                       case qw_svc_nails:
+                               QW_CL_ParseNails();
+                               break;
+
+                       case qw_svc_chokecount:
+                               i = MSG_ReadByte();
+                               // FIXME: apply to netgraph
+                               //for (j = 0;j < i;j++)
+                               //      cl.frames[(cls.netcon->qw.incoming_acknowledged-1-j)&QW_UPDATE_MASK].receivedtime = -2;
+                               break;
+
+                       case qw_svc_modellist:
+                               QW_CL_ParseModelList();
+                               break;
+
+                       case qw_svc_soundlist:
+                               QW_CL_ParseSoundList();
+                               break;
+
+                       case qw_svc_packetentities:
+                               EntityFrameQW_CL_ReadFrame(false);
+                               // first update is the final signon stage
+                               if (cls.signon == SIGNONS - 1)
+                                       cls.signon = SIGNONS;
+                               break;
+
+                       case qw_svc_deltapacketentities:
+                               EntityFrameQW_CL_ReadFrame(true);
+                               // first update is the final signon stage
+                               if (cls.signon == SIGNONS - 1)
+                                       cls.signon = SIGNONS;
+                               break;
+
+                       case qw_svc_maxspeed:
+                               cl.qw_movevars_maxspeed = MSG_ReadFloat();
+                               break;
+
+                       case qw_svc_entgravity:
+                               cl.qw_movevars_entgravity = MSG_ReadFloat();
+                               break;
+
+                       case qw_svc_setpause:
+                               cl.paused = MSG_ReadByte ();
+                               if (cl.paused)
+                                       CDAudio_Pause ();
+                               else
+                                       CDAudio_Resume ();
+                               S_PauseGameSounds (cl.paused);
                                break;
                        }
-                       strlcpy (cl_lightstyle[i].map,  MSG_ReadString(), sizeof (cl_lightstyle[i].map));
-                       cl_lightstyle[i].map[MAX_STYLESTRING - 1] = 0;
-                       cl_lightstyle[i].length = (int)strlen(cl_lightstyle[i].map);
-                       break;
+               }
+               QW_CL_UpdateItemsAndWeapon();
+       }
+       else
+       {
+               while (1)
+               {
+                       if (msg_badread)
+                               Host_Error ("CL_ParseServerMessage: Bad server message");
 
-               case svc_sound:
-                       CL_ParseStartSoundPacket(false);
-                       break;
+                       cmd = MSG_ReadByte ();
 
-               case svc_precache:
-                       if (cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3)
+                       if (cmd == -1)
                        {
-                               // was svc_sound2 in protocols 1, 2, 3, removed in 4, 5, changed to svc_precache in 6
-                               CL_ParseStartSoundPacket(true);
+                               SHOWNET("END OF MESSAGE");
+                               break;          // end of message
                        }
-                       else
+
+                       cmdindex = cmdcount & 31;
+                       cmdcount++;
+                       cmdlog[cmdindex] = cmd;
+
+                       // if the high bit of the command byte is set, it is a fast update
+                       if (cmd & 128)
+                       {
+                               // LordHavoc: fix for bizarre problem in MSVC that I do not understand (if I assign the string pointer directly it ends up storing a NULL pointer)
+                               temp = "entity";
+                               cmdlogname[cmdindex] = temp;
+                               SHOWNET("fast update");
+                               if (cls.signon == SIGNONS - 1)
+                               {
+                                       // first update is the final signon stage
+                                       cls.signon = SIGNONS;
+                                       CL_SignonReply ();
+                               }
+                               EntityFrameQuake_ReadEntity (cmd&127);
+                               continue;
+                       }
+
+                       SHOWNET(svc_strings[cmd]);
+                       cmdlogname[cmdindex] = svc_strings[cmd];
+                       if (!cmdlogname[cmdindex])
                        {
-                               int i = (unsigned short)MSG_ReadShort();
-                               char *s = MSG_ReadString();
-                               if (i < 32768)
+                               // LordHavoc: fix for bizarre problem in MSVC that I do not understand (if I assign the string pointer directly it ends up storing a NULL pointer)
+                               temp = "<unknown>";
+                               cmdlogname[cmdindex] = temp;
+                       }
+
+                       // other commands
+                       switch (cmd)
+                       {
+                       default:
                                {
-                                       if (i >= 1 && i < MAX_MODELS)
+                                       char description[32*64], temp[64];
+                                       int count;
+                                       strcpy (description, "packet dump: ");
+                                       i = cmdcount - 32;
+                                       if (i < 0)
+                                               i = 0;
+                                       count = cmdcount - i;
+                                       i &= 31;
+                                       while(count > 0)
                                        {
-                                               model_t *model = Mod_ForName(s, false, false, i == 1);
-                                               if (!model)
-                                                       Con_Printf("svc_precache: Mod_ForName(\"%s\") failed\n", s);
-                                               cl.model_precache[i] = model;
+                                               dpsnprintf (temp, sizeof (temp), "%3i:%s ", cmdlog[i], cmdlogname[i]);
+                                               strlcat (description, temp, sizeof (description));
+                                               count--;
+                                               i++;
+                                               i &= 31;
                                        }
-                                       else
-                                               Con_Printf("svc_precache: index %i outside range %i...%i\n", i, 1, MAX_MODELS);
+                                       description[strlen(description)-1] = '\n'; // replace the last space with a newline
+                                       Con_Print(description);
+                                       Host_Error ("CL_ParseServerMessage: Illegible server message");
+                               }
+                               break;
+
+                       case svc_nop:
+                               if (cls.signon < SIGNONS)
+                                       Con_Print("<-- server to client keepalive\n");
+                               break;
+
+                       case svc_time:
+                               cl.mtime[1] = cl.mtime[0];
+                               cl.mtime[0] = MSG_ReadFloat ();
+                               cl.movement_needupdate = true;
+                               break;
+
+                       case svc_clientdata:
+                               CL_ParseClientdata();
+                               break;
+
+                       case svc_version:
+                               i = MSG_ReadLong ();
+                               protocol = Protocol_EnumForNumber(i);
+                               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)
+                                       protocol = PROTOCOL_NEHAHRAMOVIE;
+                               cls.protocol = protocol;
+                               break;
+
+                       case svc_disconnect:
+                               Con_Printf ("Server disconnected\n");
+                               if (cls.demonum != -1)
+                                       CL_NextDemo ();
+                               else
+                                       CL_Disconnect ();
+                               break;
+
+                       case svc_print:
+                               CSQC_AddPrintText(MSG_ReadString());    //[515]: csqc
+                               break;
+
+                       case svc_centerprint:
+                               CL_VM_Parse_CenterPrint(MSG_ReadString ());     //[515]: csqc
+                               break;
+
+                       case svc_stufftext:
+                               CL_VM_Parse_StuffCmd(MSG_ReadString ());        //[515]: csqc
+                               break;
+
+                       case svc_damage:
+                               V_ParseDamage ();
+                               break;
+
+                       case svc_serverinfo:
+                               CL_ParseServerInfo ();
+                               CL_VM_Init();   //[515]: init csqc
+                               break;
+
+                       case svc_setangle:
+                               for (i=0 ; i<3 ; i++)
+                                       cl.viewangles[i] = MSG_ReadAngle (cls.protocol);
+                               break;
+
+                       case svc_setview:
+                               cl.viewentity = (unsigned short)MSG_ReadShort ();
+                               if (cl.viewentity >= MAX_EDICTS)
+                                       Host_Error("svc_setview >= MAX_EDICTS");
+                               if (cl.viewentity >= cl_max_entities)
+                                       CL_ExpandEntities(cl.viewentity);
+                               // LordHavoc: assume first setview recieved is the real player entity
+                               if (!cl.playerentity)
+                                       cl.playerentity = cl.viewentity;
+                               break;
+
+                       case svc_lightstyle:
+                               i = MSG_ReadByte ();
+                               if (i >= cl_max_lightstyle)
+                               {
+                                       Con_Printf ("svc_lightstyle >= MAX_LIGHTSTYLES");
+                                       break;
+                               }
+                               strlcpy (cl_lightstyle[i].map,  MSG_ReadString(), sizeof (cl_lightstyle[i].map));
+                               cl_lightstyle[i].map[MAX_STYLESTRING - 1] = 0;
+                               cl_lightstyle[i].length = (int)strlen(cl_lightstyle[i].map);
+                               break;
+
+                       case svc_sound:
+                               CL_ParseStartSoundPacket(false);
+                               break;
+
+                       case svc_precache:
+                               if (cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3)
+                               {
+                                       // was svc_sound2 in protocols 1, 2, 3, removed in 4, 5, changed to svc_precache in 6
+                                       CL_ParseStartSoundPacket(true);
                                }
                                else
                                {
-                                       i -= 32768;
-                                       if (i >= 1 && i < MAX_SOUNDS)
+                                       int i = (unsigned short)MSG_ReadShort();
+                                       char *s = MSG_ReadString();
+                                       if (i < 32768)
                                        {
-                                               sfx_t *sfx = S_PrecacheSound (s, true, false);
-                                               if (!sfx && snd_initialized.integer)
-                                                       Con_Printf("svc_precache: S_PrecacheSound(\"%s\") failed\n", s);
-                                               cl.sound_precache[i] = sfx;
+                                               if (i >= 1 && i < MAX_MODELS)
+                                               {
+                                                       model_t *model = Mod_ForName(s, false, false, i == 1);
+                                                       if (!model)
+                                                               Con_Printf("svc_precache: Mod_ForName(\"%s\") failed\n", s);
+                                                       cl.model_precache[i] = model;
+                                               }
+                                               else
+                                                       Con_Printf("svc_precache: index %i outside range %i...%i\n", i, 1, MAX_MODELS);
                                        }
                                        else
-                                               Con_Printf("svc_precache: index %i outside range %i...%i\n", i, 1, MAX_SOUNDS);
+                                       {
+                                               i -= 32768;
+                                               if (i >= 1 && i < MAX_SOUNDS)
+                                               {
+                                                       sfx_t *sfx = S_PrecacheSound (s, true, false);
+                                                       if (!sfx && snd_initialized.integer)
+                                                               Con_Printf("svc_precache: S_PrecacheSound(\"%s\") failed\n", s);
+                                                       cl.sound_precache[i] = sfx;
+                                               }
+                                               else
+                                                       Con_Printf("svc_precache: index %i outside range %i...%i\n", i, 1, MAX_SOUNDS);
+                                       }
                                }
-                       }
-                       break;
+                               break;
 
-               case svc_stopsound:
-                       i = (unsigned short) MSG_ReadShort();
-                       S_StopSound(i>>3, i&7);
-                       break;
+                       case svc_stopsound:
+                               i = (unsigned short) MSG_ReadShort();
+                               S_StopSound(i>>3, i&7);
+                               break;
 
-               case svc_updatename:
-                       i = MSG_ReadByte ();
-                       if (i >= cl.maxclients)
-                               Host_Error ("CL_ParseServerMessage: svc_updatename >= cl.maxclients");
-                       strlcpy (cl.scores[i].name, MSG_ReadString (), sizeof (cl.scores[i].name));
-                       break;
+                       case svc_updatename:
+                               i = MSG_ReadByte ();
+                               if (i >= cl.maxclients)
+                                       Host_Error ("CL_ParseServerMessage: svc_updatename >= cl.maxclients");
+                               strlcpy (cl.scores[i].name, MSG_ReadString (), sizeof (cl.scores[i].name));
+                               break;
 
-               case svc_updatefrags:
-                       i = MSG_ReadByte ();
-                       if (i >= cl.maxclients)
-                               Host_Error ("CL_ParseServerMessage: svc_updatefrags >= cl.maxclients");
-                       cl.scores[i].frags = (signed short) MSG_ReadShort ();
-                       break;
+                       case svc_updatefrags:
+                               i = MSG_ReadByte ();
+                               if (i >= cl.maxclients)
+                                       Host_Error ("CL_ParseServerMessage: svc_updatefrags >= cl.maxclients");
+                               cl.scores[i].frags = (signed short) MSG_ReadShort ();
+                               break;
 
-               case svc_updatecolors:
-                       i = MSG_ReadByte ();
-                       if (i >= cl.maxclients)
-                               Host_Error ("CL_ParseServerMessage: svc_updatecolors >= cl.maxclients");
-                       cl.scores[i].colors = MSG_ReadByte ();
-                       break;
+                       case svc_updatecolors:
+                               i = MSG_ReadByte ();
+                               if (i >= cl.maxclients)
+                                       Host_Error ("CL_ParseServerMessage: svc_updatecolors >= cl.maxclients");
+                               cl.scores[i].colors = MSG_ReadByte ();
+                               break;
 
-               case svc_particle:
-                       CL_ParseParticleEffect ();
-                       break;
+                       case svc_particle:
+                               CL_ParseParticleEffect ();
+                               break;
 
-               case svc_effect:
-                       CL_ParseEffect ();
-                       break;
+                       case svc_effect:
+                               CL_ParseEffect ();
+                               break;
 
-               case svc_effect2:
-                       CL_ParseEffect2 ();
-                       break;
+                       case svc_effect2:
+                               CL_ParseEffect2 ();
+                               break;
 
-               case svc_spawnbaseline:
-                       i = (unsigned short) MSG_ReadShort ();
-                       if (i < 0 || i >= MAX_EDICTS)
-                               Host_Error ("CL_ParseServerMessage: svc_spawnbaseline: invalid entity number %i", i);
-                       if (i >= cl_max_entities)
-                               CL_ExpandEntities(i);
-                       CL_ParseBaseline (cl_entities + i, false);
-                       break;
-               case svc_spawnbaseline2:
-                       i = (unsigned short) MSG_ReadShort ();
-                       if (i < 0 || i >= MAX_EDICTS)
-                               Host_Error ("CL_ParseServerMessage: svc_spawnbaseline2: invalid entity number %i", i);
-                       if (i >= cl_max_entities)
-                               CL_ExpandEntities(i);
-                       CL_ParseBaseline (cl_entities + i, true);
-                       break;
-               case svc_spawnstatic:
-                       CL_ParseStatic (false);
-                       break;
-               case svc_spawnstatic2:
-                       CL_ParseStatic (true);
-                       break;
-               case svc_temp_entity:
-                       if(!CL_VM_Parse_TempEntity())
-                               CL_ParseTempEntity ();
-                       break;
+                       case svc_spawnbaseline:
+                               i = (unsigned short) MSG_ReadShort ();
+                               if (i < 0 || i >= MAX_EDICTS)
+                                       Host_Error ("CL_ParseServerMessage: svc_spawnbaseline: invalid entity number %i", i);
+                               if (i >= cl_max_entities)
+                                       CL_ExpandEntities(i);
+                               CL_ParseBaseline (cl_entities + i, false);
+                               break;
+                       case svc_spawnbaseline2:
+                               i = (unsigned short) MSG_ReadShort ();
+                               if (i < 0 || i >= MAX_EDICTS)
+                                       Host_Error ("CL_ParseServerMessage: svc_spawnbaseline2: invalid entity number %i", i);
+                               if (i >= cl_max_entities)
+                                       CL_ExpandEntities(i);
+                               CL_ParseBaseline (cl_entities + i, true);
+                               break;
+                       case svc_spawnstatic:
+                               CL_ParseStatic (false);
+                               break;
+                       case svc_spawnstatic2:
+                               CL_ParseStatic (true);
+                               break;
+                       case svc_temp_entity:
+                               if(!CL_VM_Parse_TempEntity())
+                                       CL_ParseTempEntity ();
+                               break;
 
-               case svc_setpause:
-                       cl.paused = MSG_ReadByte ();
-                       if (cl.paused)
-                               CDAudio_Pause ();
-                       else
-                               CDAudio_Resume ();
-                       S_PauseGameSounds (cl.paused);
-                       break;
+                       case svc_setpause:
+                               cl.paused = MSG_ReadByte ();
+                               if (cl.paused)
+                                       CDAudio_Pause ();
+                               else
+                                       CDAudio_Resume ();
+                               S_PauseGameSounds (cl.paused);
+                               break;
 
-               case svc_signonnum:
-                       i = MSG_ReadByte ();
-                       // LordHavoc: it's rude to kick off the client if they missed the
-                       // reconnect somehow, so allow signon 1 even if at signon 1
-                       if (i <= cls.signon && i != 1)
-                               Host_Error ("Received signon %i when at %i", i, cls.signon);
-                       cls.signon = i;
-                       CL_SignonReply ();
-                       break;
+                       case svc_signonnum:
+                               i = MSG_ReadByte ();
+                               // LordHavoc: it's rude to kick off the client if they missed the
+                               // reconnect somehow, so allow signon 1 even if at signon 1
+                               if (i <= cls.signon && i != 1)
+                                       Host_Error ("Received signon %i when at %i", i, cls.signon);
+                               cls.signon = i;
+                               CL_SignonReply ();
+                               break;
 
-               case svc_killedmonster:
-                       cl.stats[STAT_MONSTERS]++;
-                       break;
+                       case svc_killedmonster:
+                               cl.stats[STAT_MONSTERS]++;
+                               break;
 
-               case svc_foundsecret:
-                       cl.stats[STAT_SECRETS]++;
-                       break;
+                       case svc_foundsecret:
+                               cl.stats[STAT_SECRETS]++;
+                               break;
 
-               case svc_updatestat:
-                       i = MSG_ReadByte ();
-                       if (i < 0 || i >= MAX_CL_STATS)
-                               Host_Error ("svc_updatestat: %i is invalid", i);
-                       cl.stats[i] = MSG_ReadLong ();
-                       break;
+                       case svc_updatestat:
+                               i = MSG_ReadByte ();
+                               if (i < 0 || i >= MAX_CL_STATS)
+                                       Host_Error ("svc_updatestat: %i is invalid", i);
+                               cl.stats[i] = MSG_ReadLong ();
+                               break;
 
-               case svc_updatestatubyte:
-                       i = MSG_ReadByte ();
-                       if (i < 0 || i >= MAX_CL_STATS)
-                               Host_Error ("svc_updatestat: %i is invalid", i);
-                       cl.stats[i] = MSG_ReadByte ();
-                       break;
+                       case svc_updatestatubyte:
+                               i = MSG_ReadByte ();
+                               if (i < 0 || i >= MAX_CL_STATS)
+                                       Host_Error ("svc_updatestat: %i is invalid", i);
+                               cl.stats[i] = MSG_ReadByte ();
+                               break;
 
-               case svc_spawnstaticsound:
-                       CL_ParseStaticSound (false);
-                       break;
+                       case svc_spawnstaticsound:
+                               CL_ParseStaticSound (false);
+                               break;
 
-               case svc_spawnstaticsound2:
-                       CL_ParseStaticSound (true);
-                       break;
+                       case svc_spawnstaticsound2:
+                               CL_ParseStaticSound (true);
+                               break;
 
-               case svc_cdtrack:
-                       cl.cdtrack = MSG_ReadByte ();
-                       cl.looptrack = MSG_ReadByte ();
-                       if ( (cls.demoplayback || cls.demorecording) && (cls.forcetrack != -1) )
-                               CDAudio_Play ((unsigned char)cls.forcetrack, true);
-                       else
-                               CDAudio_Play ((unsigned char)cl.cdtrack, true);
-                       break;
+                       case svc_cdtrack:
+                               cl.cdtrack = MSG_ReadByte ();
+                               cl.looptrack = MSG_ReadByte ();
+                               if ( (cls.demoplayback || cls.demorecording) && (cls.forcetrack != -1) )
+                                       CDAudio_Play ((unsigned char)cls.forcetrack, true);
+                               else
+                                       CDAudio_Play ((unsigned char)cl.cdtrack, true);
+                               break;
 
-               case svc_intermission:
-                       cl.intermission = 1;
-                       cl.completed_time = cl.time;
-                       break;
+                       case svc_intermission:
+                               cl.intermission = 1;
+                               cl.completed_time = cl.time;
+                               break;
 
-               case svc_finale:
-                       cl.intermission = 2;
-                       cl.completed_time = cl.time;
-                       SCR_CenterPrint(MSG_ReadString ());
-                       break;
+                       case svc_finale:
+                               cl.intermission = 2;
+                               cl.completed_time = cl.time;
+                               SCR_CenterPrint(MSG_ReadString ());
+                               break;
 
-               case svc_cutscene:
-                       cl.intermission = 3;
-                       cl.completed_time = cl.time;
-                       SCR_CenterPrint(MSG_ReadString ());
-                       break;
+                       case svc_cutscene:
+                               cl.intermission = 3;
+                               cl.completed_time = cl.time;
+                               SCR_CenterPrint(MSG_ReadString ());
+                               break;
 
-               case svc_sellscreen:
-                       Cmd_ExecuteString ("help", src_command);
-                       break;
-               case svc_hidelmp:
-                       if (gamemode == GAME_TENEBRAE)
-                       {
-                               // repeating particle effect
-                               MSG_ReadCoord(cls.protocol);
-                               MSG_ReadCoord(cls.protocol);
-                               MSG_ReadCoord(cls.protocol);
-                               MSG_ReadCoord(cls.protocol);
-                               MSG_ReadCoord(cls.protocol);
-                               MSG_ReadCoord(cls.protocol);
-                               MSG_ReadByte();
-                               MSG_ReadLong();
-                               MSG_ReadLong();
-                               MSG_ReadString();
-                       }
-                       else
-                               SHOWLMP_decodehide();
-                       break;
-               case svc_showlmp:
-                       if (gamemode == GAME_TENEBRAE)
-                       {
-                               // particle effect
-                               MSG_ReadCoord(cls.protocol);
-                               MSG_ReadCoord(cls.protocol);
-                               MSG_ReadCoord(cls.protocol);
-                               MSG_ReadByte();
-                               MSG_ReadString();
-                       }
-                       else
-                               SHOWLMP_decodeshow();
-                       break;
-               case svc_skybox:
-                       R_SetSkyBox(MSG_ReadString());
-                       break;
-               case svc_cgame:
-                       {
-                               int length;
-                               length = (int) ((unsigned short) MSG_ReadShort());
-                               for (i = 0;i < length;i++)
-                                       cgamenetbuffer[i] = MSG_ReadByte();
-                               if (!msg_badread)
-                                       CL_CGVM_ParseNetwork(cgamenetbuffer, length);
-                       }
-                       break;
-               case svc_entities:
-                       if (cls.signon == SIGNONS - 1)
-                       {
-                               // first update is the final signon stage
-                               cls.signon = SIGNONS;
-                               CL_SignonReply ();
+                       case svc_sellscreen:
+                               Cmd_ExecuteString ("help", src_command);
+                               break;
+                       case svc_hidelmp:
+                               if (gamemode == GAME_TENEBRAE)
+                               {
+                                       // repeating particle effect
+                                       MSG_ReadCoord(cls.protocol);
+                                       MSG_ReadCoord(cls.protocol);
+                                       MSG_ReadCoord(cls.protocol);
+                                       MSG_ReadCoord(cls.protocol);
+                                       MSG_ReadCoord(cls.protocol);
+                                       MSG_ReadCoord(cls.protocol);
+                                       MSG_ReadByte();
+                                       MSG_ReadLong();
+                                       MSG_ReadLong();
+                                       MSG_ReadString();
+                               }
+                               else
+                                       SHOWLMP_decodehide();
+                               break;
+                       case svc_showlmp:
+                               if (gamemode == GAME_TENEBRAE)
+                               {
+                                       // particle effect
+                                       MSG_ReadCoord(cls.protocol);
+                                       MSG_ReadCoord(cls.protocol);
+                                       MSG_ReadCoord(cls.protocol);
+                                       MSG_ReadByte();
+                                       MSG_ReadString();
+                               }
+                               else
+                                       SHOWLMP_decodeshow();
+                               break;
+                       case svc_skybox:
+                               R_SetSkyBox(MSG_ReadString());
+                               break;
+                       case svc_cgame:
+                               {
+                                       int length;
+                                       length = (int) ((unsigned short) MSG_ReadShort());
+                                       for (i = 0;i < length;i++)
+                                               cgamenetbuffer[i] = MSG_ReadByte();
+                                       if (!msg_badread)
+                                               CL_CGVM_ParseNetwork(cgamenetbuffer, length);
+                               }
+                               break;
+                       case svc_entities:
+                               if (cls.signon == SIGNONS - 1)
+                               {
+                                       // first update is the final signon stage
+                                       cls.signon = SIGNONS;
+                                       CL_SignonReply ();
+                               }
+                               if (cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3)
+                                       EntityFrame_CL_ReadFrame();
+                               else if (cls.protocol == PROTOCOL_DARKPLACES4)
+                                       EntityFrame4_CL_ReadFrame();
+                               else
+                                       EntityFrame5_CL_ReadFrame();
+                               break;
+                       case svc_csqcentities:
+                               CSQC_ReadEntities();
+                               break;
                        }
-                       if (cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3)
-                               EntityFrame_CL_ReadFrame();
-                       else if (cls.protocol == PROTOCOL_DARKPLACES4)
-                               EntityFrame4_CL_ReadFrame();
-                       else
-                               EntityFrame5_CL_ReadFrame();
-                       break;
-               case svc_csqcentities:
-                       CSQC_ReadEntities();
-                       break;
                }
        }
 
@@ -1858,6 +2906,17 @@ void CL_Parse_DumpPacket(void)
        parsingerror = false;
 }
 
+void CL_Parse_ErrorCleanUp(void)
+{
+       if (cls.qw_downloadmemory)
+       {
+               Mem_Free(cls.qw_downloadmemory);
+               cls.qw_downloadmemory = NULL;
+       }
+       cls.qw_downloadpercent = 0;
+       QW_CL_StopUpload();
+}
+
 void CL_Parse_Init(void)
 {
        // LordHavoc: added demo_nehahra cvar
@@ -1865,6 +2924,9 @@ void CL_Parse_Init(void)
        if (gamemode == GAME_NEHAHRA)
                Cvar_SetValue("demo_nehahra", 1);
        Cvar_RegisterVariable(&developer_networkentities);
+
+       Cmd_AddCommand("nextul", QW_CL_NextUpload, "sends next fragment of current upload buffer (screenshot for example)");
+       Cmd_AddCommand("stopul", QW_CL_StopUpload, "aborts current upload (screenshot for example)");
 }
 
 void CL_Parse_Shutdown(void)
index e00b070..a7ea214 100644 (file)
@@ -815,7 +815,7 @@ void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
 CL_SparkShower
 ===============
 */
-void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
+void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale, vec_t radius)
 {
        int k;
 
@@ -828,12 +828,12 @@ void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
                while(count--)
                {
                        k = particlepalette[0x68 + (rand() & 7)];
-                       particle(particletype + pt_spark, k, k, tex_particle, 0.4f, (1.0f / cl_particles_quality.value) * lhrandom(64, 255), (1.0f / cl_particles_quality.value) * 512, gravityscale, 0, org[0], org[1], org[2], dir[0], dir[1], dir[2] + sv_gravity.value * 0.1, 0, 0, 64);
+                       particle(particletype + pt_spark, k, k, tex_particle, 0.4f, (1.0f / cl_particles_quality.value) * lhrandom(64, 255), (1.0f / cl_particles_quality.value) * 512, gravityscale, 0, org[0], org[1], org[2], dir[0], dir[1], dir[2] + sv_gravity.value * 0.1, 0, radius, 64);
                }
        }
 }
 
-void CL_Smoke (vec3_t org, vec3_t dir, int count)
+void CL_Smoke (vec3_t org, vec3_t dir, int count, vec_t radius)
 {
        vec3_t org2;
        int k;
@@ -851,7 +851,7 @@ void CL_Smoke (vec3_t org, vec3_t dir, int count)
                        org2[1] = org[1] + 0.125f * lhrandom(-count, count);
                        org2[2] = org[2] + 0.125f * lhrandom(-count, count);
                        trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
-                       particle(particletype + pt_smoke, 0x101010, 0x202020, tex_smoke[rand()&7], 3, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 1024, 0, 0, trace.endpos[0], trace.endpos[1], trace.endpos[2], 0, 0, 0, 0, 0, 8);
+                       particle(particletype + pt_smoke, 0x101010, 0x202020, tex_smoke[rand()&7], 3, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 1024, 0, 0, trace.endpos[0], trace.endpos[1], trace.endpos[2], 0, 0, 0, 0, radius, 8);
                }
        }
 }
index 58d0dba..053365a 100644 (file)
--- a/client.h
+++ b/client.h
@@ -370,13 +370,14 @@ typedef struct scoreboard_s
        int             frags;
        int             colors; // two 4 bit fields
        // QW fields:
-       int             userid;
-       char    userinfo[MAX_USERINFO_STRING];
-       float   entertime;
-       int             ping;
-       int             packetloss;
-       int             spectator;
-       // TODO: QW skin support
+       int             qw_userid;
+       char    qw_userinfo[MAX_USERINFO_STRING];
+       float   qw_entertime;
+       int             qw_ping;
+       int             qw_packetloss;
+       int             qw_spectator;
+       char    qw_skin[MAX_QPATH];
+       cachepic_t *qw_skin_cachepic; // skins are loaded as cachepics
 } scoreboard_t;
 
 typedef struct cshift_s
@@ -404,7 +405,7 @@ typedef struct cshift_s
 #define        MAX_DEMOS               8
 #define        MAX_DEMONAME    16
 
-typedef enum
+typedef enum cactive_e
 {
        ca_dedicated,           // a dedicated server with no ability to start a client
        ca_disconnected,        // full screen console with no connection
@@ -412,6 +413,16 @@ typedef enum
 }
 cactive_t;
 
+typedef enum qw_downloadtype_e
+{
+       dl_none,
+       dl_single,
+       dl_skin,
+       dl_model,
+       dl_sound
+}
+qw_downloadtype_t;
+
 //
 // the client_static_t structure is persistent through an arbitrary number
 // of server connections
@@ -420,9 +431,6 @@ typedef struct client_static_s
 {
        cactive_t state;
 
-       // value of "qport" cvar at time of connection
-       int qport;
-
 // demo loop control
        // -1 = don't play demos
        int demonum;
@@ -469,6 +477,23 @@ typedef struct client_static_s
 
        // quakeworld stuff below
 
+       // value of "qport" cvar at time of connection
+       int qw_qport;
+
+       // current file download buffer (only saved when file is completed)
+       char qw_downloadname[MAX_QPATH];
+       unsigned char *qw_downloadmemory;
+       int qw_downloadmemorycursize;
+       int qw_downloadmemorymaxsize;
+       int qw_downloadnumber;
+       int qw_downloadpercent;
+       qw_downloadtype_t qw_downloadtype;
+
+       // current file upload buffer (for uploading screenshots to server)
+       unsigned char *qw_uploaddata;
+       int qw_uploadsize;
+       int qw_uploadpos;
+
        // user infostring
        // this normally contains the following keys in quakeworld:
        // password spectator name team skin topcolor bottomcolor rate noaim msg *ver *ip
@@ -662,15 +687,49 @@ typedef struct client_state_s
        // [cl.maxclients]
        scoreboard_t *scores;
 
-       // local copy of the server infostring
-       char serverinfo[MAX_SERVERINFO_STRING];
-
        // entity database stuff
        // latest received entity frame numbers
 #define LATESTFRAMENUMS 3
        int latestframenums[LATESTFRAMENUMS];
        entityframe_database_t *entitydatabase;
        entityframe4_database_t *entitydatabase4;
+       entityframeqw_database_t *entitydatabaseqw;
+
+       // quakeworld stuff
+
+       // local copy of the server infostring
+       char qw_serverinfo[MAX_SERVERINFO_STRING];
+
+       // used during connect
+       int qw_servercount;
+
+       // indicates whether the player is spectating
+       qboolean qw_spectator;
+
+       // movement parameters for client prediction
+       float qw_movevars_gravity;
+       float qw_movevars_stopspeed;
+       float qw_movevars_maxspeed; // can change during play
+       float qw_movevars_spectatormaxspeed;
+       float qw_movevars_accelerate;
+       float qw_movevars_airaccelerate;
+       float qw_movevars_wateraccelerate;
+       float qw_movevars_friction;
+       float qw_movevars_waterfriction;
+       float qw_movevars_entgravity; // can change during play
+
+       // models used by qw protocol
+       int qw_modelindex_spike;
+       int qw_modelindex_player;
+       int qw_modelindex_flag;
+       int qw_modelindex_s_explod;
+
+       vec3_t qw_intermission_origin;
+       vec3_t qw_intermission_angles;
+
+       // 255 is the most nails the QW protocol could send
+       int qw_num_nails;
+       vec_t qw_nails[255][6];
 }
 client_state_t;
 
@@ -768,6 +827,7 @@ extern int cl_num_static_entities;
 extern int cl_num_temp_entities;
 extern int cl_num_brushmodel_entities;
 
+extern char qw_emodel_name[], qw_pmodel_name[], qw_prespawn_name[], qw_modellist_name[], qw_soundlist_name[];
 
 extern client_state_t cl;
 
@@ -855,6 +915,8 @@ void CL_Parse_Init(void);
 void CL_Parse_Shutdown(void);
 void CL_ParseServerMessage(void);
 void CL_Parse_DumpPacket(void);
+void CL_Parse_ErrorCleanUp(void);
+void QW_CL_StartUpload(unsigned char *data, int size);
 extern cvar_t qport;
 
 //
@@ -901,8 +963,8 @@ void CL_Particles_Shutdown(void);
 void CL_ParseParticleEffect (void);
 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count);
 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent);
-void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale);
-void CL_Smoke (vec3_t org, vec3_t dir, int count);
+void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale, vec_t radius);
+void CL_Smoke (vec3_t org, vec3_t dir, int count, vec_t radius);
 void CL_BulletMark (vec3_t org);
 void CL_PlasmaBurn (vec3_t org);
 void CL_BloodPuff (vec3_t org, vec3_t vel, int count);
index 435e75a..67bb45e 100644 (file)
@@ -1222,15 +1222,15 @@ void VM_CL_pointparticles (void)
        case TE_SPIKEQUAD:
        case TE_GUNSHOT:
        case TE_GUNSHOTQUAD:
-               CL_SparkShower(f, v, 15, 1);
-               CL_Smoke(f, v, 15);
+               CL_SparkShower(f, v, 15, 1, 0);
+               CL_Smoke(f, v, 15, 0);
                if (cl_particles_bulletimpacts.integer)
                        CL_BulletMark(f);
                break;
        case TE_SUPERSPIKE:
        case TE_SUPERSPIKEQUAD:
-               CL_SparkShower(f, v, 30, 1);
-               CL_Smoke(f, v, 30);
+               CL_SparkShower(f, v, 30, 1, 0);
+               CL_Smoke(f, v, 30, 0);
                if (cl_particles_bulletimpacts.integer)
                        CL_BulletMark(f);
                break;
@@ -1263,7 +1263,7 @@ void VM_CL_pointparticles (void)
                CL_BloodPuff(f, v, n);
                break;
        case TE_SPARK:
-               CL_SparkShower(f, v, n, 1);
+               CL_SparkShower(f, v, n, 1, 0);
                break;
        case TE_FLAMEJET:
                CL_Flames(f, v, n);
@@ -1589,7 +1589,7 @@ void VM_CL_te_spark (void)
                return;
        pos = PRVM_G_VECTOR(OFS_PARM0);
        CL_FindNonSolidLocation(pos, pos2, 4);
-       CL_SparkShower(pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_FLOAT(OFS_PARM2), 1);
+       CL_SparkShower(pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_FLOAT(OFS_PARM2), 1, 0);
 }
 
 // #412 void(vector org) te_gunshotquad (DP_QUADEFFECTS1)
@@ -1602,8 +1602,8 @@ void VM_CL_te_gunshotquad (void)
 
        pos = PRVM_G_VECTOR(OFS_PARM0);
        CL_FindNonSolidLocation(pos, pos2, 4);
-       CL_SparkShower(pos2, vec3_origin, 15, 1);
-       CL_Smoke(pos2, vec3_origin, 15);
+       CL_SparkShower(pos2, vec3_origin, 15, 1, 0);
+       CL_Smoke(pos2, vec3_origin, 15, 0);
        CL_BulletMark(pos2);
        Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
        CL_AllocDlight(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
@@ -1622,8 +1622,8 @@ void VM_CL_te_spikequad (void)
        CL_FindNonSolidLocation(pos, pos2, 4);
        if (cl_particles_bulletimpacts.integer)
        {
-               CL_SparkShower(pos2, vec3_origin, 15, 1);
-               CL_Smoke(pos2, vec3_origin, 15);
+               CL_SparkShower(pos2, vec3_origin, 15, 1, 0);
+               CL_Smoke(pos2, vec3_origin, 15, 0);
                CL_BulletMark(pos2);
        }
        Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
@@ -1651,8 +1651,8 @@ void VM_CL_te_superspikequad (void)
        CL_FindNonSolidLocation(pos, pos2, 4);
        if (cl_particles_bulletimpacts.integer)
        {
-               CL_SparkShower(pos2, vec3_origin, 30, 1);
-               CL_Smoke(pos2, vec3_origin, 30);
+               CL_SparkShower(pos2, vec3_origin, 30, 1, 0);
+               CL_Smoke(pos2, vec3_origin, 30, 0);
                CL_BulletMark(pos2);
        }
        Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
@@ -1721,8 +1721,8 @@ void VM_CL_te_gunshot (void)
 
        pos = PRVM_G_VECTOR(OFS_PARM0);
        CL_FindNonSolidLocation(pos, pos2, 4);
-       CL_SparkShower(pos2, vec3_origin, 15, 1);
-       CL_Smoke(pos2, vec3_origin, 15);
+       CL_SparkShower(pos2, vec3_origin, 15, 1, 0);
+       CL_Smoke(pos2, vec3_origin, 15, 0);
        CL_BulletMark(pos2);
 }
 
@@ -1738,8 +1738,8 @@ void VM_CL_te_spike (void)
        CL_FindNonSolidLocation(pos, pos2, 4);
        if (cl_particles_bulletimpacts.integer)
        {
-               CL_SparkShower(pos2, vec3_origin, 15, 1);
-               CL_Smoke(pos2, vec3_origin, 15);
+               CL_SparkShower(pos2, vec3_origin, 15, 1, 0);
+               CL_Smoke(pos2, vec3_origin, 15, 0);
                CL_BulletMark(pos2);
        }
        if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
@@ -1764,8 +1764,8 @@ void VM_CL_te_superspike (void)
        CL_FindNonSolidLocation(pos, pos2, 4);
        if (cl_particles_bulletimpacts.integer)
        {
-               CL_SparkShower(pos2, vec3_origin, 30, 1);
-               CL_Smoke(pos2, vec3_origin, 30);
+               CL_SparkShower(pos2, vec3_origin, 30, 1, 0);
+               CL_Smoke(pos2, vec3_origin, 30, 0);
                CL_BulletMark(pos2);
        }
        if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
diff --git a/host.c b/host.c
index 34d1417..49dcad4 100644 (file)
--- a/host.c
+++ b/host.c
@@ -144,6 +144,8 @@ void Host_Error (const char *error, ...)
 
        CL_Parse_DumpPacket();
 
+       CL_Parse_ErrorCleanUp();
+
        //PR_Crash();
 
        // print out where the crash happened, if it was caused by QC (and do a cleanup)
index 865ecb0..f10203a 100644 (file)
@@ -1957,17 +1957,17 @@ static void MaxPlayers_f(void)
 
 // QuakeWorld commands
 
-char emodel_name[] =
+char qw_emodel_name[] =
        { 'e' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 };
-char pmodel_name[] =
+char qw_pmodel_name[] =
        { 'p' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 };
-char prespawn_name[] =
+char qw_prespawn_name[] =
        { 'p'^0xff, 'r'^0xff, 'e'^0xff, 's'^0xff, 'p'^0xff, 'a'^0xff, 'w'^0xff, 'n'^0xff,
                ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '0'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 };
-char modellist_name[] =
+char qw_modellist_name[] =
        { 'm'^0xff, 'o'^0xff, 'd'^0xff, 'e'^0xff, 'l'^0xff, 'l'^0xff, 'i'^0xff, 's'^0xff, 't'^0xff,
                ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 };
-char soundlist_name[] =
+char qw_soundlist_name[] =
        { 's'^0xff, 'o'^0xff, 'u'^0xff, 'n'^0xff, 'd'^0xff, 'l'^0xff, 'i'^0xff, 's'^0xff, 't'^0xff,
                ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 };
 
@@ -2045,9 +2045,9 @@ void Host_User_f (void) // credit: taken from QuakeWorld
        {
                if (!cl.scores[i].name[0])
                        continue;
-               if (cl.scores[i].userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1)))
+               if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1)))
                {
-                       InfoString_Print(cl.scores[i].userinfo);
+                       InfoString_Print(cl.scores[i].qw_userinfo);
                        return;
                }
        }
@@ -2073,7 +2073,7 @@ void Host_Users_f (void) // credit: taken from QuakeWorld
        {
                if (cl.scores[i].name[0])
                {
-                       Con_Printf ("%6i %4i %s\n", cl.scores[i].userid, cl.scores[i].frags, cl.scores[i].name);
+                       Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
                        c++;
                }
        }
@@ -2097,7 +2097,7 @@ void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
                return;
        }
 
-       strlcpy (cl.serverinfo, Cmd_Argv(1), sizeof(cl.serverinfo));
+       strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo));
 }
 
 /*
@@ -2146,7 +2146,7 @@ void Host_FullInfo_f (void) // credit: taken from QuakeWorld
                if (*s)
                        s++;
 
-               if (!strcasecmp(key, pmodel_name) || !strcasecmp(key, emodel_name))
+               if (!strcasecmp(key, qw_pmodel_name) || !strcasecmp(key, qw_emodel_name))
                        continue;
 
                if (key[0] == '*')
@@ -2178,7 +2178,7 @@ void Host_SetInfo_f (void) // credit: taken from QuakeWorld
                Con_Printf ("usage: setinfo [ <key> <value> ]\n");
                return;
        }
-       if (!strcasecmp(Cmd_Argv(1), pmodel_name) || !strcasecmp(Cmd_Argv(1), emodel_name))
+       if (!strcasecmp(Cmd_Argv(1), qw_pmodel_name) || !strcasecmp(Cmd_Argv(1), qw_emodel_name))
                return;
        if (Cmd_Argv(1)[0] == '*')
        {
index 0ce9c74..1904813 100755 (executable)
--- a/netconn.c
+++ b/netconn.c
@@ -466,7 +466,7 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                // client sends qport in every packet
                if (conn == cls.netcon)
                {
-                       *((short *)(sendbuffer + 8)) = LittleShort(cls.qport);
+                       *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
                        packetLen += 2;
                }
                if (packetLen + (sendreliable ? conn->sendMessageLength : 0) + data->cursize > (int)sizeof(sendbuffer))
@@ -1075,8 +1075,8 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
                        Con_Printf("\"%s\" received, sending QuakeWorld connect request back to %s\n", string, addressstring2);
                        M_Update_Return_Reason("Got QuakeWorld challenge response");
-                       cls.qport = qport.integer;
-                       NetConn_WriteString(mysocket, va("\377\377\377\377connect 28 %i %i \"%s\"\n", cls.qport, atoi(string + 1), cls.userinfo), peeraddress);
+                       cls.qw_qport = qport.integer;
+                       NetConn_WriteString(mysocket, va("\377\377\377\377connect 28 %i %i \"%s\"\n", cls.qw_qport, atoi(string + 1), cls.userinfo), peeraddress);
                }
                if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
                {
@@ -1239,7 +1239,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
                        Con_Printf("challenge %s received, sending connect request back to %s\n", string + 1, addressstring2);
                        M_Update_Return_Reason("Got challenge response");
-                       cls.qport = qport.integer;
+                       cls.qw_qport = qport.integer;
                        InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
                        InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "name", cl_name.string);
                        InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "topcolor", va("%i", (cl_color.integer >> 4) & 15));
@@ -1247,7 +1247,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "rate", va("%i", cl_rate.integer));
                        InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "msg", "1");
                        InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ver", engineversion);
-                       NetConn_WriteString(mysocket, va("\377\377\377\377connect %i %i %i \"%s\"\n", 28, cls.qport, atoi(string + 1), cls.userinfo), peeraddress);
+                       NetConn_WriteString(mysocket, va("\377\377\377\377connect %i %i %i \"%s\"\n", 28, cls.qw_qport, atoi(string + 1), cls.userinfo), peeraddress);
                        return true;
                }
                if (string[0] == 'n')
index ad5fe83..6b10412 100644 (file)
@@ -2339,3 +2339,345 @@ void EntityFrame5_WriteFrame(sizebuf_t *msg, entityframe5_database_t *d, int num
        MSG_WriteShort(msg, 0x8000);
 }
 
+
+
+void EntityStateQW_ReadPlayerUpdate(void)
+{
+       int slot = MSG_ReadByte();
+       int enumber = slot + 1;
+       int weaponframe;
+       int msec;
+       int playerflags;
+       int bits;
+       entity_state_t *s;
+       // look up the entity
+       entity_t *ent = cl_entities + enumber;
+       vec3_t viewangles;
+       vec3_t velocity;
+
+       // slide the current state into the previous
+       ent->state_previous = ent->state_current;
+
+       // read the update
+       s = &ent->state_current;
+       *s = defaultstate;
+       s->active = true;
+       playerflags = MSG_ReadShort();
+       MSG_ReadVector(s->origin, cls.protocol);
+       s->frame = MSG_ReadByte();
+       if (playerflags & QW_PF_MSEC)
+       {
+               // time difference between last update this player sent to the server,
+               // and last input we sent to the server (this packet is in response to
+               // our input, so msec is how long ago the last update of this player
+               // entity occurred, compared to our input being received)
+               msec = MSG_ReadByte();
+       }
+       else
+               msec = 0;
+       if (playerflags & QW_PF_COMMAND)
+       {
+               bits = MSG_ReadByte();
+               if (bits & QW_CM_ANGLE1)
+                       viewangles[0] = MSG_ReadAngle16i();
+               if (bits & QW_CM_ANGLE2)
+                       viewangles[1] = MSG_ReadAngle16i();
+               if (bits & QW_CM_ANGLE3)
+                       viewangles[2] = MSG_ReadAngle16i();
+               if (bits & QW_CM_FORWARD)
+                       MSG_ReadShort();
+               if (bits & QW_CM_SIDE)
+                       MSG_ReadShort();
+               if (bits & QW_CM_UP)
+                       MSG_ReadShort();
+               if (bits & QW_CM_BUTTONS)
+                       MSG_ReadByte();
+               if (bits & QW_CM_IMPULSE)
+                       MSG_ReadByte();
+       }
+       VectorClear(velocity);
+       if (playerflags & QW_PF_VELOCITY1)
+               velocity[0] = MSG_ReadShort();
+       if (playerflags & QW_PF_VELOCITY2)
+               velocity[1] = MSG_ReadShort();
+       if (playerflags & QW_PF_VELOCITY3)
+               velocity[2] = MSG_ReadShort();
+       if (playerflags & QW_PF_MODEL)
+               s->modelindex = MSG_ReadByte();
+       else
+               s->modelindex = cl.qw_modelindex_player;
+       if (playerflags & QW_PF_SKINNUM)
+               s->skin = MSG_ReadByte();
+       if (playerflags & QW_PF_EFFECTS)
+               s->effects = MSG_ReadByte();
+       if (playerflags & QW_PF_WEAPONFRAME)
+               weaponframe = MSG_ReadByte();
+
+       // calculate the entity angles from the viewangles
+       s->angles[0] = viewangles[0] * -0.0333;
+       s->angles[1] = viewangles[1];
+       s->angles[2] = 0;
+       s->angles[2] = V_CalcRoll(s->angles, velocity)*4;
+
+       // if this is an update on our player, update interpolation state
+       if (enumber == cl.playerentity)
+       {
+               VectorCopy (cl.mpunchangle[0], cl.mpunchangle[1]);
+               VectorCopy (cl.mpunchvector[0], cl.mpunchvector[1]);
+               VectorCopy (cl.mvelocity[0], cl.mvelocity[1]);
+               cl.mviewzoom[1] = cl.mviewzoom[0];
+
+               cl.idealpitch = 0;
+               cl.mpunchangle[0][0] = 0;
+               cl.mpunchangle[0][1] = 0;
+               cl.mpunchangle[0][2] = 0;
+               cl.mpunchvector[0][0] = 0;
+               cl.mpunchvector[0][1] = 0;
+               cl.mpunchvector[0][2] = 0;
+               cl.mvelocity[0][0] = 0;
+               cl.mvelocity[0][1] = 0;
+               cl.mvelocity[0][2] = 0;
+               cl.mviewzoom[0] = 1;
+
+               VectorCopy(velocity, cl.mvelocity[0]);
+               cl.stats[STAT_WEAPONFRAME] = weaponframe;
+       }
+
+       // set the cl_entities_active flag
+       cl_entities_active[enumber] = s->active;
+       // set the update time
+       s->time = cl.mtime[0] - msec * 0.001; // qw has no clock
+       // fix the number (it gets wiped occasionally by copying from defaultstate)
+       s->number = enumber;
+       // check if we need to update the lerp stuff
+       if (s->active)
+               CL_MoveLerpEntityStates(&cl_entities[enumber]);
+}
+
+static void EntityStateQW_ReadEntityUpdate(entity_state_t *s, int bits)
+{
+       int qweffects = 0;
+       s->active = true;
+       s->number = bits & 511;
+       bits &= ~511;
+       if (bits & QW_U_MOREBITS)
+               bits |= MSG_ReadByte();
+
+       // store the QW_U_SOLID bit here?
+
+       if (bits & QW_U_MODEL)
+               s->modelindex = MSG_ReadByte();
+       if (bits & QW_U_FRAME)
+               s->frame = MSG_ReadByte();
+       if (bits & QW_U_COLORMAP)
+               s->colormap = MSG_ReadByte();
+       if (bits & QW_U_SKIN)
+               s->skin = MSG_ReadByte();
+       if (bits & QW_U_EFFECTS)
+       {
+               s->effects = 0;
+               qweffects = MSG_ReadByte();
+               if (qweffects & QW_EF_BRIGHTFIELD)
+                       s->effects |= EF_BRIGHTFIELD;
+               if (qweffects & QW_EF_MUZZLEFLASH)
+                       s->effects |= EF_MUZZLEFLASH;
+               if (qweffects & QW_EF_FLAG1)
+               {
+                       // mimic FTEQW's interpretation of EF_FLAG1 as EF_NODRAW on non-player entities
+                       if (s->number > cl.maxclients)
+                               s->effects |= EF_NODRAW;
+                       else
+                               s->effects |= EF_FLAG1QW;
+               }
+               if (qweffects & QW_EF_FLAG2)
+               {
+                       // mimic FTEQW's interpretation of EF_FLAG2 as EF_ADDITIVE on non-player entities
+                       if (s->number > cl.maxclients)
+                               s->effects |= EF_ADDITIVE;
+                       else
+                               s->effects |= EF_FLAG2QW;
+               }
+               if (qweffects & QW_EF_RED)
+               {
+                       if (qweffects & QW_EF_BLUE)
+                               s->effects |= EF_RED | EF_BLUE;
+                       else
+                               s->effects |= EF_RED;
+               }
+               else if (qweffects & QW_EF_BLUE)
+                       s->effects |= EF_BLUE;
+               else if (qweffects & QW_EF_BRIGHTLIGHT)
+                       s->effects |= EF_BRIGHTLIGHT;
+               else if (qweffects & QW_EF_DIMLIGHT)
+                       s->effects |= EF_DIMLIGHT;
+       }
+       if (bits & QW_U_ORIGIN1)
+               s->origin[0] = MSG_ReadCoord13i();
+       if (bits & QW_U_ANGLE1)
+               s->angles[0] = MSG_ReadAngle8i();
+       if (bits & QW_U_ORIGIN2)
+               s->origin[1] = MSG_ReadCoord13i();
+       if (bits & QW_U_ANGLE2)
+               s->angles[1] = MSG_ReadAngle8i();
+       if (bits & QW_U_ORIGIN3)
+               s->origin[2] = MSG_ReadCoord13i();
+       if (bits & QW_U_ANGLE3)
+               s->angles[2] = MSG_ReadAngle8i();
+
+       if (developer_networkentities.integer >= 2)
+       {
+               Con_Printf("ReadFields e%i", s->number);
+               if (bits & QW_U_MODEL)
+                       Con_Printf(" U_MODEL %i", s->modelindex);
+               if (bits & QW_U_FRAME)
+                       Con_Printf(" U_FRAME %i", s->frame);
+               if (bits & QW_U_COLORMAP)
+                       Con_Printf(" U_COLORMAP %i", s->colormap);
+               if (bits & QW_U_SKIN)
+                       Con_Printf(" U_SKIN %i", s->skin);
+               if (bits & QW_U_EFFECTS)
+                       Con_Printf(" U_EFFECTS %i", qweffects);
+               if (bits & QW_U_ORIGIN1)
+                       Con_Printf(" U_ORIGIN1 %i", s->origin[0]);
+               if (bits & QW_U_ANGLE1)
+                       Con_Printf(" U_ANGLE1 %i", s->angles[0]);
+               if (bits & QW_U_ORIGIN2)
+                       Con_Printf(" U_ORIGIN2 %i", s->origin[1]);
+               if (bits & QW_U_ANGLE2)
+                       Con_Printf(" U_ANGLE2 %i", s->angles[1]);
+               if (bits & QW_U_ORIGIN3)
+                       Con_Printf(" U_ORIGIN3 %i", s->origin[2]);
+               if (bits & QW_U_ANGLE3)
+                       Con_Printf(" U_ANGLE3 %i", s->angles[2]);
+               if (bits & QW_U_SOLID)
+                       Con_Printf(" U_SOLID");
+               Con_Print("\n");
+       }
+}
+
+void EntityFrameQW_CL_ReadFrame(qboolean delta)
+{
+       qboolean invalid = false;
+       int i, number, oldsnapindex, newsnapindex, oldindex, newindex, oldnum, newnum;
+       entity_t *ent;
+       entityframeqw_database_t *d = cl.entitydatabaseqw;
+       entityframeqw_snapshot_t *oldsnap, *newsnap;
+
+       newsnapindex = cls.netcon->qw.incoming_sequence & QW_UPDATE_MASK;
+       newsnap = d->snapshot + newsnapindex;
+       memset(newsnap, 0, sizeof(*newsnap));
+       if (delta)
+       {
+               oldsnapindex = MSG_ReadByte() & QW_UPDATE_MASK;
+               oldsnap = d->snapshot + oldsnapindex;
+               if (cls.netcon->qw.outgoing_sequence - oldsnapindex >= QW_UPDATE_BACKUP-1)
+               {
+                       Con_DPrintf("delta update too old\n");
+                       newsnap->invalid = invalid = true; // too old
+                       delta = false;
+               }
+       }
+       else
+       {
+               oldsnapindex = -1;
+               oldsnap = NULL;
+       }
+
+       // read the number of this frame to echo back in next input packet
+       for (i = 0;i < LATESTFRAMENUMS-1;i++)
+               cl.latestframenums[i] = cl.latestframenums[i+1];
+       cl.latestframenums[LATESTFRAMENUMS-1] = cls.netcon->qw.incoming_sequence;
+       if (invalid)
+               cl.latestframenums[LATESTFRAMENUMS-1] = 0;
+
+       // read entity numbers until we find a 0x0000
+       // (which would be an empty update on world entity, but is actually a terminator)
+       newsnap->num_entities = 0;
+       oldindex = 0;
+       for (;;)
+       {
+               int word = (unsigned short)MSG_ReadShort();
+               if (msg_badread)
+                       return; // just return, the main parser will print an error
+               newnum = word == 0 ? 512 : (word & 511);
+               oldnum = delta ? (oldindex >= oldsnap->num_entities ? 9999 : oldsnap->entities[oldindex].number) : 9999;
+
+               // copy unmodified oldsnap entities
+               while (newnum > oldnum) // delta only
+               {
+                       if (developer_networkentities.integer >= 2)
+                               Con_Printf("copy %i\n", oldnum);
+                       // copy one of the old entities
+                       if (newsnap->num_entities >= QW_MAX_PACKET_ENTITIES)
+                               Host_Error("EntityFrameQW_CL_ReadFrame: newsnap->num_entities == MAX_PACKETENTITIES");
+                       newsnap->entities[newsnap->num_entities] = oldsnap->entities[oldindex++];
+                       newsnap->num_entities++;
+                       oldnum = oldindex >= oldsnap->num_entities ? 9999 : oldsnap->entities[oldindex].number;
+               }
+
+               if (word == 0)
+                       break;
+
+               if (developer_networkentities.integer >= 2)
+               {
+                       if (word & QW_U_REMOVE)
+                               Con_Printf("remove %i\n", newnum);
+                       else if (newnum == oldnum)
+                               Con_Printf("delta %i\n", newnum);
+                       else
+                               Con_Printf("baseline %i\n", newnum);
+               }
+
+               if (word & QW_U_REMOVE)
+               {
+                       if (newnum != oldnum && !delta && !invalid)
+                       {
+                               cl.latestframenums[LATESTFRAMENUMS-1] = 0;
+                               Con_Printf("WARNING: U_REMOVE %i on full update\n", newnum);
+                       }
+               }
+               else
+               {
+                       if (newsnap->num_entities >= QW_MAX_PACKET_ENTITIES)
+                               Host_Error("EntityFrameQW_CL_ReadFrame: newsnap->num_entities == MAX_PACKETENTITIES");
+                       newsnap->entities[newsnap->num_entities] = (newnum == oldnum) ? oldsnap->entities[oldindex++] : cl_entities[newnum].state_baseline;
+                       EntityStateQW_ReadEntityUpdate(newsnap->entities + newsnap->num_entities, word);
+                       newsnap->num_entities++;
+               }
+       }
+
+       // expand cl_num_entities to include every entity we've seen this game
+       newnum = newsnap->num_entities ? newsnap->entities[newsnap->num_entities - 1].number : 1;
+       if (cl_num_entities <= newnum)
+       {
+               cl_num_entities = newnum + 1;
+               if (cl_max_entities < newnum + 1)
+                       CL_ExpandEntities(newnum);
+       }
+
+       // now update the entities from the snapshot states
+       number = 1;
+       for (newindex = 0;;newindex++)
+       {
+               newnum = newindex >= newsnap->num_entities ? cl_num_entities : newsnap->entities[newindex].number;
+               // kill any missing entities
+               for (;number < newnum;number++)
+               {
+                       if (cl_entities_active[number])
+                       {
+                               cl_entities_active[number] = false;
+                               cl_entities[number].state_current.active = false;
+                       }
+               }
+               if (number >= cl_num_entities)
+                       break;
+               // update the entity
+               ent = &cl_entities[number];
+               ent->state_previous = ent->state_current;
+               ent->state_current = newsnap->entities[newindex];
+               CL_MoveLerpEntityStates(ent);
+               // the entity lives again...
+               cl_entities_active[number] = true;
+               number++;
+       }
+}
index 20f655d..e92337b 100644 (file)
@@ -60,6 +60,8 @@ void Protocol_Names(char *buffer, size_t buffersize);
 #define EF_SELECTABLE                  16384   // LordHavoc: highlights when PRYDON_CLIENTCURSOR mouse is over it
 #define EF_DOUBLESIDED                 32768   //[515]: disable cull face for this entity
 
+#define EF_FLAG1QW                             16777216 // internal client use only
+#define EF_FLAG2QW                             33554432 // internal client use only
 #define EF_STEP                                        0x80000000 // internal client use only - present on MOVETYPE_STEP entities, not QC accessible (too many bits)
 
 // flags for the pflags field of entities
@@ -818,19 +820,19 @@ extern cvar_t developer_networkentities;
 #define qw_clc_upload          7               // teleport request, spectator only
 // QUAKEWORLD
 // playerinfo flags from server
-// playerinfo allways sends: playernum, flags, origin[] and framenumber
-#define        PF_MSEC                 (1<<0)
-#define        PF_COMMAND              (1<<1)
-#define        PF_VELOCITY1    (1<<2)
-#define        PF_VELOCITY2    (1<<3)
-#define        PF_VELOCITY3    (1<<4)
-#define        PF_MODEL                (1<<5)
-#define        PF_SKINNUM              (1<<6)
-#define        PF_EFFECTS              (1<<7)
-#define        PF_WEAPONFRAME  (1<<8)          // only sent for view player
-#define        PF_DEAD                 (1<<9)          // don't block movement any more
-#define        PF_GIB                  (1<<10)         // offset the view height differently
-#define        PF_NOGRAV               (1<<11)         // don't apply gravity for prediction
+// playerinfo always sends: playernum, flags, origin[] and framenumber
+#define        QW_PF_MSEC                      (1<<0)
+#define        QW_PF_COMMAND           (1<<1)
+#define        QW_PF_VELOCITY1 (1<<2)
+#define        QW_PF_VELOCITY2 (1<<3)
+#define        QW_PF_VELOCITY3 (1<<4)
+#define        QW_PF_MODEL             (1<<5)
+#define        QW_PF_SKINNUM           (1<<6)
+#define        QW_PF_EFFECTS           (1<<7)
+#define        QW_PF_WEAPONFRAME       (1<<8)          // only sent for view player
+#define        QW_PF_DEAD                      (1<<9)          // don't block movement any more
+#define        QW_PF_GIB                       (1<<10)         // offset the view height differently
+#define        QW_PF_NOGRAV            (1<<11)         // don't apply gravity for prediction
 // QUAKEWORLD
 // if the high bit of the client to server byte is set, the low bits are
 // client move cmd bits
@@ -877,6 +879,60 @@ extern cvar_t developer_networkentities;
 #define QW_TE_TELEPORT                 11
 #define QW_TE_BLOOD                            12
 #define QW_TE_LIGHTNINGBLOOD   13
+// QUAKEWORLD
+// effect flags
+#define QW_EF_BRIGHTFIELD              1
+#define QW_EF_MUZZLEFLASH              2
+#define QW_EF_BRIGHTLIGHT              4
+#define QW_EF_DIMLIGHT                         8
+#define QW_EF_FLAG1                            16
+#define QW_EF_FLAG2                            32
+#define QW_EF_BLUE                             64
+#define QW_EF_RED                              128
+
+#define QW_UPDATE_BACKUP 64
+#define QW_UPDATE_MASK (QW_UPDATE_BACKUP - 1)
+#define QW_MAX_PACKET_ENTITIES 64
+
+// note: QW stats are directly compatible with NQ
+// (but FRAGS, WEAPONFRAME, and VIEWHEIGHT are unused)
+// so these defines are not actually used by darkplaces, but kept for reference
+#define QW_STAT_HEALTH                 0
+//#define QW_STAT_FRAGS                        1
+#define QW_STAT_WEAPON                 2
+#define QW_STAT_AMMO                   3
+#define QW_STAT_ARMOR                  4
+//#define QW_STAT_WEAPONFRAME          5
+#define QW_STAT_SHELLS                 6
+#define QW_STAT_NAILS                  7
+#define QW_STAT_ROCKETS                        8
+#define QW_STAT_CELLS                  9
+#define QW_STAT_ACTIVEWEAPON   10
+#define QW_STAT_TOTALSECRETS   11
+#define QW_STAT_TOTALMONSTERS  12
+#define QW_STAT_SECRETS                        13 // bumped on client side by svc_foundsecret
+#define QW_STAT_MONSTERS               14 // bumped by svc_killedmonster
+#define QW_STAT_ITEMS                  15
+//#define QW_STAT_VIEWHEIGHT           16
+
+// build entity data in this, to pass to entity read/write functions
+typedef struct entityframeqw_snapshot_s
+{
+       double time;
+       qboolean invalid;
+       int num_entities;
+       entity_state_t entities[QW_MAX_PACKET_ENTITIES];
+}
+entityframeqw_snapshot_t;
+
+typedef struct entityframeqw_database_s
+{
+       entityframeqw_snapshot_t snapshot[QW_UPDATE_BACKUP];
+}
+entityframeqw_database_t;
+
+void EntityStateQW_ReadPlayerUpdate(void);
+void EntityFrameQW_CL_ReadFrame(qboolean delta);
 
 #endif