/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // cl_parse.c -- parse a message received from the server #include "quakedef.h" #include "bgmusic.h" const char *svc_strings[] = { "svc_bad", "svc_nop", "svc_disconnect", "svc_updatestat", "svc_version", // [long] server version "svc_setview", // [short] entity number "svc_sound", // "svc_time", // [float] server time "svc_print", // [string] null terminated string "svc_stufftext", // [string] stuffed into client's console buffer // the string should be \n terminated "svc_setangle", // [vec3] set the view angle to this absolute value "svc_serverinfo", // [long] version // [string] signon string // [string]..[0]model cache [string]...[0]sounds cache // [string]..[0]item cache "svc_lightstyle", // [byte] [string] "svc_updatename", // [byte] [string] "svc_updatefrags", // [byte] [short] "svc_clientdata", // "svc_stopsound", // "svc_updatecolors", // [byte] [byte] "svc_particle", // [vec3] "svc_damage", // [byte] impact [byte] blood [vec3] from "svc_spawnstatic", "OBSOLETE svc_spawnbinary", "svc_spawnbaseline", "svc_temp_entity", // "svc_setpause", "svc_signonnum", "svc_centerprint", "svc_killedmonster", "svc_foundsecret", "svc_spawnstaticsound", "svc_intermission", "svc_finale", // [string] music [string] text "svc_cdtrack", // [byte] track [byte] looptrack "svc_sellscreen", "svc_cutscene", //johnfitz -- new server messages "", // 35 "", // 36 "svc_skybox", // 37 // [string] skyname "svc_botchat", // 38 (2021 RE-RELEASE) "", // 39 "svc_bf", // 40 // no data "svc_fog", // 41 // [byte] density [byte] red [byte] green [byte] blue [float] time "svc_spawnbaseline2", //42 // support for large modelindex, large framenum, alpha, using flags "svc_spawnstatic2", // 43 // support for large modelindex, large framenum, alpha, using flags "svc_spawnstaticsound2", // 44 // [coord3] [short] samp [byte] vol [byte] aten //johnfitz // 2021 RE-RELEASE: "svc_setviews", // 45 "svc_updateping", // 46 "svc_updatesocial", // 47 "svc_updateplinfo", // 48 "svc_rawprint", // 49 "svc_servervars", // 50 "svc_seq", // 51 "svc_achievement", // 52 "svc_chat", // 53 "svc_levelcompleted", // 54 "svc_backtolobby", // 55 "svc_localsound" // 56 }; #define NUM_SVC_STRINGS (sizeof(svc_strings) / sizeof(svc_strings[0])) qboolean warn_about_nehahra_protocol; //johnfitz extern vec3_t v_punchangles[2]; //johnfitz //============================================================================= /* =============== CL_EntityNum This error checks and tracks the total number of entities =============== */ entity_t *CL_EntityNum (int num) { //johnfitz -- check minimum number too if (num < 0) Host_Error ("CL_EntityNum: %i is an invalid number",num); //john if (num >= cl.num_entities) { if (num >= cl_max_edicts) //johnfitz -- no more MAX_EDICTS Host_Error ("CL_EntityNum: %i is an invalid number",num); while (cl.num_entities<=num) { cl_entities[cl.num_entities].colormap = vid.colormap; cl_entities[cl.num_entities].lerpflags |= LERP_RESETMOVE|LERP_RESETANIM; //johnfitz cl.num_entities++; } } return &cl_entities[num]; } /* ================== CL_ParseStartSoundPacket ================== */ void CL_ParseStartSoundPacket(void) { vec3_t pos; int channel, ent; int sound_num; int volume; int field_mask; float attenuation; int i; field_mask = 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; //johnfitz -- PROTOCOL_FITZQUAKE 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 (field_mask & SND_LARGESOUND) sound_num = (unsigned short) MSG_ReadShort (); else sound_num = MSG_ReadByte (); //johnfitz //johnfitz -- check soundnum if (sound_num >= MAX_SOUNDS) Host_Error ("CL_ParseStartSoundPacket: %i > MAX_SOUNDS", sound_num); //johnfitz if (ent > cl_max_edicts) //johnfitz -- no more MAX_EDICTS Host_Error ("CL_ParseStartSoundPacket: ent = %i", ent); for (i = 0; i < 3; i++) pos[i] = MSG_ReadCoord (cl.protocolflags); S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation); } /* ================== CL_ParseLocalSound - for 2021 rerelease ================== */ void CL_ParseLocalSound(void) { int field_mask, sound_num; field_mask = MSG_ReadByte(); sound_num = (field_mask&SND_LARGESOUND) ? MSG_ReadShort() : MSG_ReadByte(); if (sound_num >= MAX_SOUNDS) Host_Error ("CL_ParseLocalSound: %i > MAX_SOUNDS", sound_num); S_LocalSound (cl.sound_precache[sound_num]->name); } /* ================== CL_KeepaliveMessage When the client is taking a long time to load stuff, send keepalive messages so the server doesn't disconnect. ================== */ static byte net_olddata[NET_MAXMESSAGE]; void CL_KeepaliveMessage (void) { float time; static float lastmsg; int ret; sizebuf_t old; byte *olddata; if (sv.active) return; // no need if server is local if (cls.demoplayback) return; // read messages from server, should just be nops olddata = net_olddata; old = net_message; memcpy (olddata, net_message.data, net_message.cursize); do { ret = CL_GetMessage (); switch (ret) { default: Host_Error ("CL_KeepaliveMessage: CL_GetMessage failed"); case 0: break; // nothing waiting case 1: Host_Error ("CL_KeepaliveMessage: received a message"); break; case 2: if (MSG_ReadByte() != svc_nop) Host_Error ("CL_KeepaliveMessage: datagram wasn't a nop"); break; } } while (ret); net_message = old; memcpy (net_message.data, olddata, net_message.cursize); // check time time = Sys_DoubleTime (); if (time - lastmsg < 5) return; lastmsg = time; // write out a nop Con_Printf ("--> client to server keepalive\n"); MSG_WriteByte (&cls.message, clc_nop); NET_SendMessage (cls.netcon, &cls.message); SZ_Clear (&cls.message); } /* ================== CL_ParseServerInfo ================== */ void CL_ParseServerInfo (void) { const char *str; int i; int nummodels, numsounds; char model_precache[MAX_MODELS][MAX_QPATH]; char sound_precache[MAX_SOUNDS][MAX_QPATH]; Con_DPrintf ("Serverinfo packet received.\n"); // ericw -- bring up loading plaque for map changes within a demo. // it will be hidden in CL_SignonReply. if (cls.demoplayback) SCR_BeginLoadingPlaque(); // // wipe the client_state_t struct // CL_ClearState (); // parse protocol version number i = MSG_ReadLong (); //johnfitz -- support multiple protocols if (i != PROTOCOL_NETQUAKE && i != PROTOCOL_FITZQUAKE && i != PROTOCOL_RMQ) { Con_Printf ("\n"); //because there's no newline after serverinfo print Host_Error ("Server returned version %i, not %i or %i or %i", i, PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE, PROTOCOL_RMQ); } cl.protocol = i; //johnfitz if (cl.protocol == PROTOCOL_RMQ) { const unsigned int supportedflags = (PRFL_SHORTANGLE | PRFL_FLOATANGLE | PRFL_24BITCOORD | PRFL_FLOATCOORD | PRFL_EDICTSCALE | PRFL_INT32COORD); // mh - read protocol flags from server so that we know what protocol features to expect cl.protocolflags = (unsigned int) MSG_ReadLong (); if (0 != (cl.protocolflags & (~supportedflags))) { Con_Warning("PROTOCOL_RMQ protocolflags %i contains unsupported flags\n", cl.protocolflags); } } else cl.protocolflags = 0; // parse maxclients cl.maxclients = MSG_ReadByte (); if (cl.maxclients < 1 || cl.maxclients > MAX_SCOREBOARD) { Host_Error ("Bad maxclients (%u) from server", cl.maxclients); } cl.scores = (scoreboard_t *) Hunk_AllocName (cl.maxclients*sizeof(*cl.scores), "scores"); // parse gametype cl.gametype = MSG_ReadByte (); // parse signon message str = MSG_ReadString (); q_strlcpy (cl.levelname, str, sizeof(cl.levelname)); // seperate the printfs so the server message can have a color Con_Printf ("\n%s\n", Con_Quakebar(40)); //johnfitz Con_Printf ("%c%s\n", 2, str); //johnfitz -- tell user which protocol this is Con_Printf ("Using protocol %i\n", i); // first we go through and touch all of the precache data that still // happens to be in the cache, so precaching something else doesn't // needlessly purge it // precache models memset (cl.model_precache, 0, sizeof(cl.model_precache)); for (nummodels = 1 ; ; nummodels++) { str = MSG_ReadString (); if (!str[0]) break; if (nummodels == MAX_MODELS) { Host_Error ("Server sent too many model precaches"); } q_strlcpy (model_precache[nummodels], str, MAX_QPATH); Mod_TouchModel (str); } //johnfitz -- check for excessive models if (nummodels >= 256) Con_DWarning ("%i models exceeds standard limit of 256 (max = %d).\n", nummodels, MAX_MODELS); //johnfitz // precache sounds memset (cl.sound_precache, 0, sizeof(cl.sound_precache)); for (numsounds = 1 ; ; numsounds++) { str = MSG_ReadString (); if (!str[0]) break; if (numsounds == MAX_SOUNDS) { Host_Error ("Server sent too many sound precaches"); } q_strlcpy (sound_precache[numsounds], str, MAX_QPATH); S_TouchSound (str); } //johnfitz -- check for excessive sounds if (numsounds >= 256) Con_DWarning ("%i sounds exceeds standard limit of 256 (max = %d).\n", numsounds, MAX_SOUNDS); //johnfitz // // now we try to load everything else until a cache allocation fails // // copy the naked name of the map file to the cl structure -- O.S COM_StripExtension (COM_SkipPath(model_precache[1]), cl.mapname, sizeof(cl.mapname)); for (i = 1; i < nummodels; i++) { cl.model_precache[i] = Mod_ForName (model_precache[i], false); if (cl.model_precache[i] == NULL) { Host_Error ("Model %s not found", model_precache[i]); } CL_KeepaliveMessage (); } S_BeginPrecaching (); for (i = 1; i < numsounds; i++) { cl.sound_precache[i] = S_PrecacheSound (sound_precache[i]); CL_KeepaliveMessage (); } S_EndPrecaching (); // local state cl_entities[0].model = cl.worldmodel = cl.model_precache[1]; R_NewMap (); //johnfitz -- clear out string; we don't consider identical //messages to be duplicates if the map has changed in between con_lastcenterstring[0] = 0; //johnfitz Hunk_Check (); // make sure nothing is hurt noclip_anglehack = false; // noclip is turned off at start warn_about_nehahra_protocol = true; //johnfitz -- warn about nehahra protocol hack once per server connection //johnfitz -- reset developer stats memset(&dev_stats, 0, sizeof(dev_stats)); memset(&dev_peakstats, 0, sizeof(dev_peakstats)); memset(&dev_overflows, 0, sizeof(dev_overflows)); } /* ================== CL_ParseUpdate Parse an entity update message from the server If an entities model or origin changes from frame to frame, it must be relinked. Other attributes can change without relinking. ================== */ void CL_ParseUpdate (int bits) { int i; qmodel_t *model; int modnum; qboolean forcelink; entity_t *ent; int num; int skin; if (cls.signon == SIGNONS - 1) { // first update is the final signon stage cls.signon = SIGNONS; CL_SignonReply (); } if (bits & U_MOREBITS) { i = MSG_ReadByte (); bits |= (i<<8); } //johnfitz -- PROTOCOL_FITZQUAKE if (cl.protocol == PROTOCOL_FITZQUAKE || cl.protocol == PROTOCOL_RMQ) { if (bits & U_EXTEND1) bits |= MSG_ReadByte() << 16; if (bits & U_EXTEND2) bits |= MSG_ReadByte() << 24; } //johnfitz if (bits & U_LONGENTITY) num = MSG_ReadShort (); else num = MSG_ReadByte (); ent = CL_EntityNum (num); if (ent->msgtime != cl.mtime[1]) forcelink = true; // no previous frame to lerp from else forcelink = false; //johnfitz -- lerping if (ent->msgtime + 0.2 < cl.mtime[0]) //more than 0.2 seconds since the last message (most entities think every 0.1 sec) ent->lerpflags |= LERP_RESETANIM; //if we missed a think, we'd be lerping from the wrong frame //johnfitz ent->msgtime = cl.mtime[0]; if (bits & U_MODEL) { modnum = MSG_ReadByte (); if (modnum >= MAX_MODELS) Host_Error ("CL_ParseModel: bad modnum"); } else modnum = ent->baseline.modelindex; if (bits & U_FRAME) ent->frame = MSG_ReadByte (); else ent->frame = ent->baseline.frame; if (bits & U_COLORMAP) i = MSG_ReadByte(); else i = ent->baseline.colormap; if (!i) ent->colormap = vid.colormap; else { if (i > cl.maxclients) Sys_Error ("i >= cl.maxclients"); ent->colormap = cl.scores[i-1].translations; } if (bits & U_SKIN) skin = MSG_ReadByte(); else skin = ent->baseline.skin; if (skin != ent->skinnum) { ent->skinnum = skin; if (num > 0 && num <= cl.maxclients) R_TranslateNewPlayerSkin (num - 1); //johnfitz -- was R_TranslatePlayerSkin } if (bits & U_EFFECTS) ent->effects = MSG_ReadByte(); else ent->effects = ent->baseline.effects; // shift the known values for interpolation VectorCopy (ent->msg_origins[0], ent->msg_origins[1]); VectorCopy (ent->msg_angles[0], ent->msg_angles[1]); if (bits & U_ORIGIN1) ent->msg_origins[0][0] = MSG_ReadCoord (cl.protocolflags); else ent->msg_origins[0][0] = ent->baseline.origin[0]; if (bits & U_ANGLE1) ent->msg_angles[0][0] = MSG_ReadAngle(cl.protocolflags); else ent->msg_angles[0][0] = ent->baseline.angles[0]; if (bits & U_ORIGIN2) ent->msg_origins[0][1] = MSG_ReadCoord (cl.protocolflags); else ent->msg_origins[0][1] = ent->baseline.origin[1]; if (bits & U_ANGLE2) ent->msg_angles[0][1] = MSG_ReadAngle(cl.protocolflags); else ent->msg_angles[0][1] = ent->baseline.angles[1]; if (bits & U_ORIGIN3) ent->msg_origins[0][2] = MSG_ReadCoord (cl.protocolflags); else ent->msg_origins[0][2] = ent->baseline.origin[2]; if (bits & U_ANGLE3) ent->msg_angles[0][2] = MSG_ReadAngle(cl.protocolflags); else ent->msg_angles[0][2] = ent->baseline.angles[2]; //johnfitz -- lerping for movetype_step entities if (bits & U_STEP) { ent->lerpflags |= LERP_MOVESTEP; ent->forcelink = true; } else ent->lerpflags &= ~LERP_MOVESTEP; //johnfitz //johnfitz -- PROTOCOL_FITZQUAKE and PROTOCOL_NEHAHRA if (cl.protocol == PROTOCOL_FITZQUAKE || cl.protocol == PROTOCOL_RMQ) { if (bits & U_ALPHA) ent->alpha = MSG_ReadByte(); else ent->alpha = ent->baseline.alpha; if (bits & U_SCALE) MSG_ReadByte(); // PROTOCOL_RMQ: currently ignored if (bits & U_FRAME2) ent->frame = (ent->frame & 0x00FF) | (MSG_ReadByte() << 8); if (bits & U_MODEL2) modnum = (modnum & 0x00FF) | (MSG_ReadByte() << 8); if (bits & U_LERPFINISH) { ent->lerpfinish = ent->msgtime + ((float)(MSG_ReadByte()) / 255); ent->lerpflags |= LERP_FINISH; } else ent->lerpflags &= ~LERP_FINISH; } else if (cl.protocol == PROTOCOL_NETQUAKE) { //HACK: if this bit is set, assume this is PROTOCOL_NEHAHRA if (bits & U_TRANS) { float a, b; if (warn_about_nehahra_protocol) { Con_Warning ("nonstandard update bit, assuming Nehahra protocol\n"); warn_about_nehahra_protocol = false; } a = MSG_ReadFloat(); b = MSG_ReadFloat(); //alpha if (a == 2) MSG_ReadFloat(); //fullbright (not using this yet) ent->alpha = ENTALPHA_ENCODE(b); } else ent->alpha = ent->baseline.alpha; } //johnfitz //johnfitz -- moved here from above model = cl.model_precache[modnum]; if (model != ent->model) { ent->model = model; // automatic animation (torches, etc) can be either all together // or randomized if (model) { if (model->synctype == ST_RAND) ent->syncbase = (float)(rand()&0x7fff) / 0x7fff; else ent->syncbase = 0.0; } else forcelink = true; // hack to make null model players work if (num > 0 && num <= cl.maxclients) R_TranslateNewPlayerSkin (num - 1); //johnfitz -- was R_TranslatePlayerSkin ent->lerpflags |= LERP_RESETANIM; //johnfitz -- don't lerp animation across model changes } //johnfitz if ( forcelink ) { // didn't have an update last message VectorCopy (ent->msg_origins[0], ent->msg_origins[1]); VectorCopy (ent->msg_origins[0], ent->origin); VectorCopy (ent->msg_angles[0], ent->msg_angles[1]); VectorCopy (ent->msg_angles[0], ent->angles); ent->forcelink = true; } } /* ================== CL_ParseBaseline ================== */ void CL_ParseBaseline (entity_t *ent, int version) //johnfitz -- added argument { int i; int bits; //johnfitz //johnfitz -- PROTOCOL_FITZQUAKE bits = (version == 2) ? MSG_ReadByte() : 0; ent->baseline.modelindex = (bits & B_LARGEMODEL) ? MSG_ReadShort() : MSG_ReadByte(); ent->baseline.frame = (bits & B_LARGEFRAME) ? MSG_ReadShort() : MSG_ReadByte(); //johnfitz ent->baseline.colormap = MSG_ReadByte(); ent->baseline.skin = MSG_ReadByte(); for (i = 0; i < 3; i++) { ent->baseline.origin[i] = MSG_ReadCoord (cl.protocolflags); ent->baseline.angles[i] = MSG_ReadAngle (cl.protocolflags); } ent->baseline.alpha = (bits & B_ALPHA) ? MSG_ReadByte() : ENTALPHA_DEFAULT; //johnfitz -- PROTOCOL_FITZQUAKE } /* ================== CL_ParseClientdata Server information pertaining to this client only ================== */ void CL_ParseClientdata (void) { int i, j; int bits; //johnfitz bits = (unsigned short)MSG_ReadShort (); //johnfitz -- read bits here isntead of in CL_ParseServerMessage() //johnfitz -- PROTOCOL_FITZQUAKE if (bits & SU_EXTEND1) bits |= (MSG_ReadByte() << 16); if (bits & SU_EXTEND2) bits |= (MSG_ReadByte() << 24); //johnfitz if (bits & SU_VIEWHEIGHT) cl.viewheight = MSG_ReadChar (); else cl.viewheight = DEFAULT_VIEWHEIGHT; if (bits & SU_IDEALPITCH) cl.idealpitch = MSG_ReadChar (); else cl.idealpitch = 0; VectorCopy (cl.mvelocity[0], cl.mvelocity[1]); for (i = 0; i < 3; i++) { if (bits & (SU_PUNCH1< cl.maxclients) Sys_Error ("CL_NewTranslation: slot > cl.maxclients"); dest = cl.scores[slot].translations; source = vid.colormap; memcpy (dest, vid.colormap, sizeof(cl.scores[slot].translations)); top = cl.scores[slot].colors & 0xf0; bottom = (cl.scores[slot].colors &15)<<4; R_TranslatePlayerSkin (slot); for (i = 0; i < VID_GRADES; i++, dest += 256, source+=256) { if (top < 128) // the artists made some backwards ranges. sigh. memcpy (dest + TOP_RANGE, source + top, 16); else { for (j = 0; j < 16; j++) dest[TOP_RANGE+j] = source[top+15-j]; } if (bottom < 128) memcpy (dest + BOTTOM_RANGE, source + bottom, 16); else { for (j = 0; j < 16; j++) dest[BOTTOM_RANGE+j] = source[bottom+15-j]; } } } /* ===================== CL_ParseStatic ===================== */ void CL_ParseStatic (int version) //johnfitz -- added a parameter { entity_t *ent; int i; i = cl.num_statics; if (i >= MAX_STATIC_ENTITIES) Host_Error ("Too many static entities"); ent = &cl_static_entities[i]; cl.num_statics++; CL_ParseBaseline (ent, version); //johnfitz -- added second parameter // copy it to the current state ent->model = cl.model_precache[ent->baseline.modelindex]; ent->lerpflags |= LERP_RESETANIM; //johnfitz -- lerping ent->frame = ent->baseline.frame; ent->colormap = vid.colormap; ent->skinnum = ent->baseline.skin; ent->effects = ent->baseline.effects; ent->alpha = ent->baseline.alpha; //johnfitz -- alpha VectorCopy (ent->baseline.origin, ent->origin); VectorCopy (ent->baseline.angles, ent->angles); R_AddEfrags (ent); } /* =================== CL_ParseStaticSound =================== */ void CL_ParseStaticSound (int version) //johnfitz -- added argument { vec3_t org; int sound_num, vol, atten; int i; for (i = 0; i < 3; i++) org[i] = MSG_ReadCoord (cl.protocolflags); //johnfitz -- PROTOCOL_FITZQUAKE if (version == 2) sound_num = MSG_ReadShort (); else sound_num = MSG_ReadByte (); //johnfitz vol = MSG_ReadByte (); atten = MSG_ReadByte (); S_StaticSound (cl.sound_precache[sound_num], org, vol, atten); } #if 0 /* for debugging. from fteqw. */ static void CL_DumpPacket (void) { int i, pos; unsigned char *packet = net_message.data; Con_Printf("CL_DumpPacket, BEGIN:\n"); pos = 0; while (pos < net_message.cursize) { Con_Printf("%5i ", pos); for (i = 0; i < 16; i++) { if (pos >= net_message.cursize) Con_Printf(" X "); else Con_Printf("%2x ", packet[pos]); pos++; } pos -= 16; for (i = 0; i < 16; i++) { if (pos >= net_message.cursize) Con_Printf("X"); else if (packet[pos] == 0) Con_Printf("."); else Con_Printf("%c", packet[pos]); pos++; } Con_Printf("\n"); } Con_Printf("CL_DumpPacket, --- END ---\n"); } #endif /* CL_DumpPacket */ #define SHOWNET(x) if(cl_shownet.value==2)Con_Printf ("%3i:%s\n", msg_readcount-1, x); /* ===================== CL_ParseServerMessage ===================== */ void CL_ParseServerMessage (void) { int cmd; int i; const char *str; //johnfitz int total, j, lastcmd; //johnfitz // // if recording demos, copy the message out // if (cl_shownet.value == 1) Con_Printf ("%i ",net_message.cursize); else if (cl_shownet.value == 2) Con_Printf ("------------------\n"); // cl.onground = false; // unless the server says otherwise // // parse the message // MSG_BeginReading (); lastcmd = 0; while (1) { if (msg_badread) Host_Error ("CL_ParseServerMessage: Bad server message"); cmd = MSG_ReadByte (); if (cmd == -1) { SHOWNET("END OF MESSAGE"); return; // end of message } // if the high bit of the command byte is set, it is a fast update if (cmd & U_SIGNAL) //johnfitz -- was 128, changed for clarity { SHOWNET("fast update"); CL_ParseUpdate (cmd&127); continue; } if (cmd < (int)NUM_SVC_STRINGS) { SHOWNET(svc_strings[cmd]); } // other commands switch (cmd) { default: // CL_DumpPacket (); Host_Error ("Illegible server message %d (previous was %s)", cmd, svc_strings[lastcmd]); //johnfitz -- added svc_strings[lastcmd] break; case svc_nop: // Con_Printf ("svc_nop\n"); break; case svc_time: cl.mtime[1] = cl.mtime[0]; cl.mtime[0] = MSG_ReadFloat (); break; case svc_clientdata: CL_ParseClientdata (); //johnfitz -- removed bits parameter, we will read this inside CL_ParseClientdata() break; case svc_version: i = MSG_ReadLong (); //johnfitz -- support multiple protocols if (i != PROTOCOL_NETQUAKE && i != PROTOCOL_FITZQUAKE && i != PROTOCOL_RMQ) Host_Error ("Server returned version %i, not %i or %i or %i", i, PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE, PROTOCOL_RMQ); cl.protocol = i; //johnfitz break; case svc_disconnect: Host_EndGame ("Server disconnected\n"); case svc_print: Con_Printf ("%s", MSG_ReadString ()); break; case svc_centerprint: //johnfitz -- log centerprints to console str = MSG_ReadString (); SCR_CenterPrint (str); Con_LogCenterPrint (str); //johnfitz break; case svc_stufftext: Cbuf_AddText (MSG_ReadString ()); break; case svc_damage: V_ParseDamage (); break; case svc_serverinfo: CL_ParseServerInfo (); vid.recalc_refdef = true; // leave intermission full screen break; case svc_setangle: for (i=0 ; i<3 ; i++) cl.viewangles[i] = MSG_ReadAngle (cl.protocolflags); break; case svc_setview: cl.viewentity = MSG_ReadShort (); break; case svc_lightstyle: i = MSG_ReadByte (); if (i >= MAX_LIGHTSTYLES) Sys_Error ("svc_lightstyle > MAX_LIGHTSTYLES"); q_strlcpy (cl_lightstyle[i].map, MSG_ReadString(), MAX_STYLESTRING); cl_lightstyle[i].length = Q_strlen(cl_lightstyle[i].map); //johnfitz -- save extra info if (cl_lightstyle[i].length) { total = 0; cl_lightstyle[i].peak = 'a'; for (j=0; j>3, i&7); break; case svc_updatename: Sbar_Changed (); i = MSG_ReadByte (); if (i >= cl.maxclients) Host_Error ("CL_ParseServerMessage: svc_updatename > MAX_SCOREBOARD"); q_strlcpy (cl.scores[i].name, MSG_ReadString(), MAX_SCOREBOARDNAME); break; case svc_updatefrags: Sbar_Changed (); i = MSG_ReadByte (); if (i >= cl.maxclients) Host_Error ("CL_ParseServerMessage: svc_updatefrags > MAX_SCOREBOARD"); cl.scores[i].frags = MSG_ReadShort (); break; case svc_updatecolors: Sbar_Changed (); i = MSG_ReadByte (); if (i >= cl.maxclients) Host_Error ("CL_ParseServerMessage: svc_updatecolors > MAX_SCOREBOARD"); cl.scores[i].colors = MSG_ReadByte (); CL_NewTranslation (i); break; case svc_particle: R_ParseParticleEffect (); break; case svc_spawnbaseline: i = MSG_ReadShort (); // must use CL_EntityNum() to force cl.num_entities up CL_ParseBaseline (CL_EntityNum(i), 1); // johnfitz -- added second parameter break; case svc_spawnstatic: CL_ParseStatic (1); //johnfitz -- added parameter break; case svc_temp_entity: CL_ParseTEnt (); break; case svc_setpause: cl.paused = MSG_ReadByte (); if (cl.paused) { CDAudio_Pause (); BGM_Pause (); } else { CDAudio_Resume (); BGM_Resume (); } break; case svc_signonnum: i = MSG_ReadByte (); if (i <= cls.signon) Host_Error ("Received signon %i when at %i", i, cls.signon); cls.signon = i; //johnfitz -- if signonnum==2, signon packet has been fully parsed, so check for excessive static ents and efrags if (i == 2) { if (cl.num_statics > 128) Con_DWarning ("%i static entities exceeds standard limit of 128 (max = %d).\n", cl.num_statics, MAX_STATIC_ENTITIES); R_CheckEfrags (); } //johnfitz 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) Sys_Error ("svc_updatestat: %i is invalid", i); cl.stats[i] = MSG_ReadLong ();; break; case svc_spawnstaticsound: CL_ParseStaticSound (1); //johnfitz -- added parameter break; case svc_cdtrack: cl.cdtrack = MSG_ReadByte (); cl.looptrack = MSG_ReadByte (); if ( (cls.demoplayback || cls.demorecording) && (cls.forcetrack != -1) ) BGM_PlayCDtrack ((byte)cls.forcetrack, true); else BGM_PlayCDtrack ((byte)cl.cdtrack, true); break; case svc_intermission: cl.intermission = 1; cl.completed_time = cl.time; vid.recalc_refdef = true; // go to full screen V_RestoreAngles (); break; case svc_finale: cl.intermission = 2; cl.completed_time = cl.time; vid.recalc_refdef = true; // go to full screen //johnfitz -- log centerprints to console str = MSG_ReadString (); SCR_CenterPrint (str); Con_LogCenterPrint (str); //johnfitz V_RestoreAngles (); break; case svc_cutscene: cl.intermission = 3; cl.completed_time = cl.time; vid.recalc_refdef = true; // go to full screen //johnfitz -- log centerprints to console str = MSG_ReadString (); SCR_CenterPrint (str); Con_LogCenterPrint (str); //johnfitz V_RestoreAngles (); break; case svc_sellscreen: Cmd_ExecuteString ("help", src_command); break; //johnfitz -- new svc types case svc_skybox: Sky_LoadSkyBox (MSG_ReadString()); break; case svc_bf: Cmd_ExecuteString ("bf", src_command); break; case svc_fog: Fog_ParseServerMessage (); break; case svc_spawnbaseline2: //PROTOCOL_FITZQUAKE i = MSG_ReadShort (); // must use CL_EntityNum() to force cl.num_entities up CL_ParseBaseline (CL_EntityNum(i), 2); break; case svc_spawnstatic2: //PROTOCOL_FITZQUAKE CL_ParseStatic (2); break; case svc_spawnstaticsound2: //PROTOCOL_FITZQUAKE CL_ParseStaticSound (2); break; //johnfitz //used by the 2021 rerelease case svc_achievement: str = MSG_ReadString(); Con_DPrintf("Ignoring svc_achievement (%s)\n", str); break; case svc_localsound: CL_ParseLocalSound(); break; } lastcmd = cmd; //johnfitz } }