+#define SHOWNET(x) if(cl_shownet.integer==2)Con_Printf("%3i:%s\n", msg_readcount-1, x);
+
+//[515]: csqc
+void CL_VM_Init (void);
+qboolean CL_VM_Parse_TempEntity (void);
+void CL_VM_Parse_StuffCmd (const char *msg);
+void CL_VM_Parse_CenterPrint (const char *msg);
+void CSQC_AddPrintText (const char *msg);
+void CSQC_ReadEntities (void);
+
+/*
+=====================
+CL_ParseServerMessage
+=====================
+*/
+int parsingerror = false;
+void CL_ParseServerMessage(void)
+{
+ int cmd;
+ int i;
+ protocolversion_t protocol;
+ unsigned char cmdlog[32];
+ char *cmdlogname[32], *temp;
+ int cmdindex, cmdcount = 0;
+
+ if (cls.demorecording)
+ CL_WriteDemoMessage ();
+
+ cl.last_received_message = realtime;
+
+//
+// if recording demos, copy the message out
+//
+ if (cl_shownet.integer == 1)
+ Con_Printf("%f %i\n", realtime, net_message.cursize);
+ else if (cl_shownet.integer == 2)
+ Con_Print("------------------\n");
+
+//
+// parse the message
+//
+ //MSG_BeginReading ();
+
+ parsingerror = true;
+
+ if (cls.protocol == PROTOCOL_QUAKEWORLD)
+ {
+ cl.mtime[1] = cl.mtime[0];
+ cl.mtime[0] = realtime; // qw has no clock
+ cl.movement_needupdate = true;
+ cl.onground = false; // since there's no clientdata parsing, clear the onground flag here
+ // if true the cl.viewangles are interpolated from cl.mviewangles[]
+ // during this frame
+ // (makes spectating players much smoother and prevents mouse movement from turning)
+ cl.fixangle[1] = cl.fixangle[0];
+ cl.fixangle[0] = false;
+ if (!cls.demoplayback)
+ VectorCopy(cl.mviewangles[0], cl.mviewangles[1]);
+
+ // force a recalculation of the player prediction
+ cl.movement_replay = true;
+
+ // slightly kill qw player entities each frame
+ for (i = 1;i < cl.maxclients;i++)
+ cl.entities_active[i] = false;
+
+ // kill all qw nails
+ cl.qw_num_nails = 0;
+
+ // fade weapon view kick
+ cl.qw_weaponkick = min(cl.qw_weaponkick + 10 * (cl.time - cl.oldtime), 0);
+
+ while (1)
+ {
+ if (msg_badread)
+ Host_Error ("CL_ParseServerMessage: Bad QW server message");
+
+ cmd = MSG_ReadByte ();
+
+ if (cmd == -1)
+ {
+ SHOWNET("END OF MESSAGE");
+ break; // end of message
+ }
+
+ cmdindex = cmdcount & 31;
+ cmdcount++;
+ cmdlog[cmdindex] = cmd;
+
+ SHOWNET(qw_svc_strings[cmd]);
+ cmdlogname[cmdindex] = qw_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;
+ }
+
+ // other commands
+ switch (cmd)
+ {
+ default:
+ {
+ char description[32*64], temp[64];
+ int count;
+ strlcpy(description, "packet dump: ", sizeof(description));
+ 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");
+ }
+ break;
+
+ case qw_svc_nop:
+ //Con_Printf("qw_svc_nop\n");
+ break;
+
+ case qw_svc_disconnect:
+ Con_Printf("Server disconnected\n");
+ if (cls.demonum != -1)
+ CL_NextDemo();
+ else
+ CL_Disconnect();
+ return;
+
+ case qw_svc_print:
+ i = MSG_ReadByte();
+ temp = MSG_ReadString();
+ if (CL_ExaminePrintString(temp)) // look for anything interesting like player IP addresses or ping reports
+ {
+ if (i == 3) // chat
+ CSQC_AddPrintText(va("\1%s", temp)); //[515]: csqc
+ else
+ CSQC_AddPrintText(temp);
+ }
+ break;
+
+ case qw_svc_centerprint:
+ CL_VM_Parse_CenterPrint(MSG_ReadString ()); //[515]: csqc
+ break;
+
+ case qw_svc_stufftext:
+ CL_VM_Parse_StuffCmd(MSG_ReadString ()); //[515]: csqc
+ break;
+
+ case qw_svc_damage:
+ // svc_damage protocol is identical to nq
+ V_ParseDamage ();
+ break;
+
+ case qw_svc_serverdata:
+ //Cbuf_Execute(); // make sure any stuffed commands are done
+ CL_ParseServerInfo();
+ CL_VM_Init(); //[515]: init csqc
+ break;
+
+ case qw_svc_setangle:
+ for (i=0 ; i<3 ; i++)
+ cl.viewangles[i] = MSG_ReadAngle (cls.protocol);
+ if (!cls.demoplayback)
+ {
+ cl.fixangle[0] = true;
+ VectorCopy(cl.viewangles, cl.mviewangles[0]);
+ // disable interpolation if this is new
+ if (!cl.fixangle[1])
+ VectorCopy(cl.viewangles, cl.mviewangles[1]);
+ }
+ 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 qw_svc_sound:
+ CL_ParseStartSoundPacket(false);
+ break;
+
+ case qw_svc_stopsound:
+ i = (unsigned short) MSG_ReadShort();
+ S_StopSound(i>>3, i&7);
+ 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 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 = cl.time - 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 = 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:
+ cl.qw_weaponkick = -2;
+ break;
+ case qw_svc_bigkick:
+ cl.qw_weaponkick = -4;
+ 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;
+ }
+ }
+
+ // fully kill the still slightly dead qw player entities each frame
+ for (i = 1;i < cl.maxclients;i++)
+ if (!cl.entities_active[i])
+ cl.entities[i].state_current.active = false;
+ }
+ else
+ {
+ while (1)
+ {
+ if (msg_badread)
+ Host_Error ("CL_ParseServerMessage: Bad server message");
+
+ cmd = MSG_ReadByte ();
+
+ if (cmd == -1)
+ {
+ SHOWNET("END OF MESSAGE");
+ break; // end of message
+ }
+
+ 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])
+ {
+ // 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:
+ {
+ char description[32*64], temp[64];
+ int count;
+ strlcpy (description, "packet dump: ", sizeof(description));
+ 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");
+ }
+ 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;
+ // if true the cl.viewangles are interpolated from cl.mviewangles[]
+ // during this frame
+ // (makes spectating players much smoother and prevents mouse movement from turning)
+ cl.fixangle[1] = cl.fixangle[0];
+ cl.fixangle[0] = false;
+ if (!cls.demoplayback)
+ VectorCopy(cl.mviewangles[0], cl.mviewangles[1]);
+ 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:
+ temp = MSG_ReadString();
+ if (CL_ExaminePrintString(temp)) // look for anything interesting like player IP addresses or ping reports
+ CSQC_AddPrintText(temp); //[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);
+ if (!cls.demoplayback)
+ {
+ cl.fixangle[0] = true;
+ VectorCopy(cl.viewangles, cl.mviewangles[0]);
+ // disable interpolation if this is new
+ if (!cl.fixangle[1])
+ VectorCopy(cl.viewangles, cl.mviewangles[1]);
+ }
+ 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
+ {
+ int i = (unsigned short)MSG_ReadShort();
+ char *s = MSG_ReadString();
+ if (i < 32768)
+ {
+ 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
+ {
+ 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;
+
+ 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_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_particle:
+ CL_ParseParticleEffect ();
+ break;
+
+ case svc_effect:
+ CL_ParseEffect ();
+ 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_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_killedmonster:
+ cl.stats[STAT_MONSTERS]++;
+ 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_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_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_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_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_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;
+ }
+ }
+ }
+
+ CL_UpdateItemsAndWeapon();
+
+ EntityFrameQuake_ISeeDeadEntities();
+
+ parsingerror = false;
+}
+
+void CL_Parse_DumpPacket(void)
+{
+ if (!parsingerror)
+ return;
+ Con_Print("Packet dump:\n");
+ SZ_HexDumpToConsole(&net_message);
+ 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
+ Cvar_RegisterVariable (&demo_nehahra);
+ if (gamemode == GAME_NEHAHRA)
+ Cvar_SetValue("demo_nehahra", 1);
+ Cvar_RegisterVariable(&developer_networkentities);
+
+ Cvar_RegisterVariable(&cl_sound_wizardhit);
+ Cvar_RegisterVariable(&cl_sound_hknighthit);
+ Cvar_RegisterVariable(&cl_sound_tink1);
+ Cvar_RegisterVariable(&cl_sound_ric1);
+ Cvar_RegisterVariable(&cl_sound_ric2);
+ Cvar_RegisterVariable(&cl_sound_ric3);
+ Cvar_RegisterVariable(&cl_sound_r_exp3);
+
+ 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)");
+ Cmd_AddCommand("skins", QW_CL_Skins_f, "downloads missing qw skins from server");
+ Cmd_AddCommand("changing", QW_CL_Changing_f, "sent by qw servers to tell client to wait for level change");
+}
+
+void CL_Parse_Shutdown(void)
+{