/* 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 "q_defs.h" #include "bgmusic.h" const char *svc_strings[] = { "svc_bad", "svc_nop", "svc_disconnect", "svc_updatestat", "svc_version", // [long] server version "svc_setview", // [int16_t] 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] [int16_t] "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 "", // 38 "", // 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] [int16_t] samp [byte] vol [byte] aten "", // 44 "", // 45 "", // 46 "", // 47 "", // 48 "", // 49 //johnfitz }; bool 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(int32_t num) { //johnfitz -- check minimum number too if(num < 0) Host_Error("CL_EntityNum: %" PRIi32 " 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: %" PRIi32 " 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; int32_t channel, ent; int32_t sound_num; int32_t volume; int32_t field_mask; float attenuation; int32_t 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 = (uint16_t) MSG_ReadShort(); channel = MSG_ReadByte(); } else { channel = (uint16_t) MSG_ReadShort(); ent = channel >> 3; channel &= 7; } if(field_mask & SND_LARGESOUND) sound_num = (uint16_t) MSG_ReadShort(); else sound_num = MSG_ReadByte(); //johnfitz //johnfitz -- check soundnum if(sound_num >= MAX_SOUNDS) Host_Error("CL_ParseStartSoundPacket: %" PRIi32 " > MAX_SOUNDS", sound_num); //johnfitz if(ent > cl_max_edicts) //johnfitz -- no more MAX_EDICTS Host_Error("CL_ParseStartSoundPacket: ent = %" PRIi32, 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_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; int32_t 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; int32_t i; int32_t 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 %" PRIi32 ", not %" PRIi32 " or %" PRIi32 " or %" PRIi32, i, PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE, PROTOCOL_RMQ); } cl.protocol = i; //johnfitz if(cl.protocol == PROTOCOL_RMQ) { const uint32_t 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 = (uint32_t) MSG_ReadLong(); if(0 != (cl.protocolflags & (~supportedflags))) { Con_Warning("PROTOCOL_RMQ protocolflags %" PRIi32 " 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 (%" PRIu32 ") 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("\x02%s\n", str); //johnfitz -- tell user which protocol this is Con_Printf("Using protocol %" PRIi32 "\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("%" PRIi32 " models exceeds standard limit of 256 (max = %" PRIi32 ").\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("%" PRIi32 " sounds exceeds standard limit of 256 (max = %" PRIi32 ").\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(int32_t bits) { int32_t i; qmodel_t *model; int32_t modnum; bool forcelink; entity_t *ent; int32_t num; int32_t 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, int32_t version) //johnfitz -- added argument { int32_t i; int32_t 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) { int32_t i, j; int32_t bits; //johnfitz bits = (uint16_t)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 << i)) cl.punchangle[i] = MSG_ReadChar(); else cl.punchangle[i] = 0; if(bits & (SU_VELOCITY1 << i)) cl.mvelocity[0][i] = MSG_ReadChar() * 16; else cl.mvelocity[0][i] = 0; } //johnfitz -- update v_punchangles if(v_punchangles[0][0] != cl.punchangle[0] || v_punchangles[0][1] != cl.punchangle[1] || v_punchangles[0][2] != cl.punchangle[2]) { VectorCopy(v_punchangles[0], v_punchangles[1]); VectorCopy(cl.punchangle, v_punchangles[0]); } //johnfitz // [always sent] if (bits & SU_ITEMS) i = MSG_ReadLong(); if(cl.items != i) { // set flash times Sbar_Changed(); for(j = 0; j < 32; j++) if((i & (1 << j)) && !(cl.items & (1 << j))) cl.item_gettime[j] = cl.time; cl.items = i; } cl.onground = (bits & SU_ONGROUND) != 0; cl.inwater = (bits & SU_INWATER) != 0; if(bits & SU_WEAPONFRAME) cl.stats[STAT_WEAPONFRAME] = MSG_ReadByte(); else cl.stats[STAT_WEAPONFRAME] = 0; if(bits & SU_ARMOR) i = MSG_ReadByte(); else i = 0; if(cl.stats[STAT_ARMOR] != i) { cl.stats[STAT_ARMOR] = i; Sbar_Changed(); } if(bits & SU_WEAPON) i = MSG_ReadByte(); else i = 0; if(cl.stats[STAT_WEAPON] != i) { cl.stats[STAT_WEAPON] = i; Sbar_Changed(); } i = MSG_ReadShort(); if(cl.stats[STAT_HEALTH] != i) { cl.stats[STAT_HEALTH] = i; Sbar_Changed(); } i = MSG_ReadByte(); if(cl.stats[STAT_AMMO] != i) { cl.stats[STAT_AMMO] = i; Sbar_Changed(); } for(i = 0; i < 4; i++) { j = MSG_ReadByte(); if(cl.stats[STAT_SHELLS + i] != j) { cl.stats[STAT_SHELLS + i] = j; Sbar_Changed(); } } i = MSG_ReadByte(); if(standard_quake) { if(cl.stats[STAT_ACTIVEWEAPON] != i) { cl.stats[STAT_ACTIVEWEAPON] = i; Sbar_Changed(); } } else { if(cl.stats[STAT_ACTIVEWEAPON] != (1 << i)) { cl.stats[STAT_ACTIVEWEAPON] = (1 << i); Sbar_Changed(); } } //johnfitz -- PROTOCOL_FITZQUAKE if(bits & SU_WEAPON2) cl.stats[STAT_WEAPON] |= (MSG_ReadByte() << 8); if(bits & SU_ARMOR2) cl.stats[STAT_ARMOR] |= (MSG_ReadByte() << 8); if(bits & SU_AMMO2) cl.stats[STAT_AMMO] |= (MSG_ReadByte() << 8); if(bits & SU_SHELLS2) cl.stats[STAT_SHELLS] |= (MSG_ReadByte() << 8); if(bits & SU_NAILS2) cl.stats[STAT_NAILS] |= (MSG_ReadByte() << 8); if(bits & SU_ROCKETS2) cl.stats[STAT_ROCKETS] |= (MSG_ReadByte() << 8); if(bits & SU_CELLS2) cl.stats[STAT_CELLS] |= (MSG_ReadByte() << 8); if(bits & SU_WEAPONFRAME2) cl.stats[STAT_WEAPONFRAME] |= (MSG_ReadByte() << 8); if(bits & SU_WEAPONALPHA) cl.viewent.alpha = MSG_ReadByte(); else cl.viewent.alpha = ENTALPHA_DEFAULT; //johnfitz //johnfitz -- lerping //ericw -- this was done before the upper 8 bits of cl.stats[STAT_WEAPON] were filled in, breaking on large maps like zendar.bsp if(cl.viewent.model != cl.model_precache[cl.stats[STAT_WEAPON]]) { cl.viewent.lerpflags |= LERP_RESETANIM; //don't lerp animation across model changes } //johnfitz } /* ===================== CL_NewTranslation ===================== */ void CL_NewTranslation(int32_t slot) { int32_t i, j; int32_t top, bottom; byte *dest, *source; if(slot > 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(int32_t version) //johnfitz -- added a parameter { entity_t *ent; int32_t 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(int32_t version) //johnfitz -- added argument { vec3_t org; int32_t sound_num, vol, atten; int32_t 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); } #define SHOWNET(x) if(cl_shownet.value==2)Con_Printf ("%3" PRIi32 ":%s\n", msg_readcount-1, x); /* ===================== CL_ParseServerMessage ===================== */ void CL_ParseServerMessage(void) { int32_t cmd; int32_t i; const char *str; //johnfitz int32_t total, j, lastcmd; //johnfitz // // if recording demos, copy the message out // if(cl_shownet.value == 1) Con_Printf("%" PRIi32 " ", 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; } SHOWNET(svc_strings[cmd]); // other commands switch(cmd) { default: Host_Error("Illegible server message, previous was %s", 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 %" PRIi32 ", not %" PRIi32 " or %" PRIi32 " or %" PRIi32, 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 = 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 < cl_lightstyle[i].length; j++) { total += cl_lightstyle[i].map[j] - 'a'; cl_lightstyle[i].peak = q_max(cl_lightstyle[i].peak, cl_lightstyle[i].map[j]); } cl_lightstyle[i].average = total / cl_lightstyle[i].length + 'a'; } else cl_lightstyle[i].average = cl_lightstyle[i].peak = 'm'; //johnfitz break; case svc_sound: CL_ParseStartSoundPacket(); break; case svc_stopsound: i = MSG_ReadShort(); S_StopSound(i >> 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) BGM_Pause(); else BGM_Resume(); break; case svc_signonnum: i = MSG_ReadByte(); if(i <= cls.signon) Host_Error("Received signon %" PRIi32 " when at %" PRIi32, 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("%" PRIi32 " static entities exceeds standard limit of 128 (max = %" PRIi32 ").\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: %" PRIi32 " 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 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 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 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 } lastcmd = cmd; //johnfitz } }