X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=cl_parse.c;h=b33a23d79294976accaed6ede7ca3dcd15ad43d9;hp=fe45aeeacf64a0a27f3402fbbdfc07a90609ba75;hb=1781aceb95f4c77ffe865d1845c0d30f716f68f5;hpb=a4107d729cd599a854f238a79c038094127fad20 diff --git a/cl_parse.c b/cl_parse.c index fe45aeea..b33a23d7 100644 --- a/cl_parse.c +++ b/cl_parse.c @@ -8,7 +8,7 @@ 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. +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "quakedef.h" -char *svc_strings[] = +char *svc_strings[128] = { "svc_bad", "svc_nop", @@ -35,7 +35,7 @@ char *svc_strings[] = "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 @@ -48,11 +48,11 @@ char *svc_strings[] = "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", @@ -65,71 +65,64 @@ char *svc_strings[] = "svc_cdtrack", // [byte] track [byte] looptrack "svc_sellscreen", "svc_cutscene", - "svc_showlmp", // [string] iconlabel [string] lmpfile [byte] x [byte] y + "svc_showlmp", // [string] iconlabel [string] lmpfile [short] x [short] y "svc_hidelmp", // [string] iconlabel "svc_skybox", // [string] skyname - "?", // 38 - "?", // 39 - "?", // 40 - "?", // 41 - "?", // 42 - "?", // 43 - "?", // 44 - "?", // 45 - "?", // 46 - "?", // 47 - "?", // 48 - "?", // 49 - "svc_farclip", // [coord] size - "svc_fog", // [byte] enable [short * 4096] density [byte] red [byte] green [byte] blue - "svc_playerposition" // [float] x [float] y [float] z + "", // 38 + "", // 39 + "", // 40 + "", // 41 + "", // 42 + "", // 43 + "", // 44 + "", // 45 + "", // 46 + "", // 47 + "", // 48 + "", // 49 + "svc_cgame", // 50 // [short] length [bytes] data + "svc_unusedlh1", // 51 // unused + "svc_effect", // 52 // [vector] org [byte] modelindex [byte] startframe [byte] framecount [byte] framerate + "svc_effect2", // 53 // [vector] org [short] modelindex [short] startframe [byte] framecount [byte] framerate + "svc_sound2", // 54 // short soundindex instead of byte + "svc_spawnbaseline2", // 55 // short modelindex instead of byte + "svc_spawnstatic2", // 56 // short modelindex instead of byte + "svc_entities", // 57 // [int] deltaframe [int] thisframe [float vector] eye [variable length] entitydata + "svc_unusedlh3", // 58 + "svc_spawnstaticsound2", // 59 // [coord3] [short] samp [byte] vol [byte] aten }; //============================================================================= -qboolean Nehahrademcompatibility; // LordHavoc: to allow playback of the early Nehahra movie segments -qboolean dpprotocol; // LordHavoc: whether or not the current network stream is the enhanced DarkPlaces protocol - -/* -=============== -CL_EntityNum +cvar_t demo_nehahra = {0, "demo_nehahra", "0"}; -This error checks and tracks the total number of entities -=============== -*/ -entity_t *CL_EntityNum (int num) +void CL_Parse_Init(void) { - if (num >= cl.num_entities) - { - if (num >= MAX_EDICTS) - Host_Error ("CL_EntityNum: %i is an invalid number",num); - while (cl.num_entities<=num) - { - cl_entities[cl.num_entities].colormap = -1; // no special coloring - cl.num_entities++; - } - } - - return &cl_entities[num]; + // LordHavoc: added demo_nehahra cvar + Cvar_RegisterVariable (&demo_nehahra); + if (gamemode == GAME_NEHAHRA) + Cvar_SetValue("demo_nehahra", 1); } +qboolean Nehahrademcompatibility; // LordHavoc: to allow playback of the early Nehahra movie segments +int dpprotocol; // LordHavoc: version of network protocol, or 0 if not DarkPlaces /* ================== CL_ParseStartSoundPacket ================== */ -void CL_ParseStartSoundPacket(void) +void CL_ParseStartSoundPacket(int largesoundindex) { vec3_t pos; int channel, ent; int sound_num; int volume; int field_mask; - float attenuation; + float attenuation; int i; - field_mask = MSG_ReadByte(); + field_mask = MSG_ReadByte(); if (field_mask & SND_VOLUME) volume = MSG_ReadByte (); @@ -142,7 +135,13 @@ void CL_ParseStartSoundPacket(void) attenuation = DEFAULT_SOUND_PACKET_ATTENUATION; channel = MSG_ReadShort (); - sound_num = MSG_ReadByte (); + if (largesoundindex) + sound_num = (unsigned short) MSG_ReadShort (); + else + sound_num = MSG_ReadByte (); + + if (sound_num >= MAX_SOUNDS) + Host_Error("CL_ParseStartSoundPacket: sound_num (%i) >= MAX_SOUNDS (%i)\n", sound_num, MAX_SOUNDS); ent = channel >> 3; channel &= 7; @@ -152,9 +151,9 @@ void CL_ParseStartSoundPacket(void) for (i=0 ; i<3 ; i++) pos[i] = MSG_ReadCoord (); - + S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation); -} +} /* ================== @@ -169,8 +168,9 @@ void CL_KeepaliveMessage (void) float time; static float lastmsg; int ret; + int c; sizebuf_t old; - byte olddata[8192]; + qbyte olddata[8192]; if (sv.active) return; // no need if server is local @@ -187,14 +187,15 @@ void CL_KeepaliveMessage (void) switch (ret) { default: - Host_Error ("CL_KeepaliveMessage: CL_GetMessage failed"); + 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) + c = MSG_ReadByte(); + if (c != svc_nop) Host_Error ("CL_KeepaliveMessage: datagram wasn't a nop"); break; } @@ -204,7 +205,7 @@ void CL_KeepaliveMessage (void) memcpy (net_message.data, olddata, net_message.cursize); // check time - time = Sys_FloatTime (); + time = Sys_DoubleTime (); if (time - lastmsg < 5) return; lastmsg = time; @@ -217,31 +218,18 @@ void CL_KeepaliveMessage (void) SZ_Clear (&cls.message); } -extern qboolean isworldmodel; -extern char skyname[]; -extern float fog_density; -extern float fog_red; -extern float fog_green; -extern float fog_blue; -extern void R_SetSkyBox (char *sky); -extern void FOG_clear(); -extern cvar_t r_farclip; - void CL_ParseEntityLump(char *entdata) { char *data; - char key[128], value[1024]; - char wadname[128]; - int i, j, k; + char key[128], value[4096]; FOG_clear(); // LordHavoc: no fog until set - skyname[0] = 0; // LordHavoc: no enviroment mapped sky until set - r_farclip.value = 6144; // LordHavoc: default farclip distance + R_SetSkyBox(""); // LordHavoc: no environment mapped sky until set data = entdata; if (!data) return; data = COM_Parse(data); if (!data) - return; // valid exit + return; // error if (com_token[0] != '{') return; // error while (1) @@ -250,8 +238,11 @@ void CL_ParseEntityLump(char *entdata) if (!data) return; // error if (com_token[0] == '}') - return; // since we're just parsing the first ent (worldspawn), exit - strcpy(key, com_token); + break; // end of worldspawn + if (com_token[0] == '_') + strcpy(key, com_token + 1); + else + strcpy(key, com_token); while (key[strlen(key)-1] == ' ') // remove trailing spaces key[strlen(key)-1] = 0; data = COM_Parse(data); @@ -264,17 +255,8 @@ void CL_ParseEntityLump(char *entdata) R_SetSkyBox(value); else if (!strcmp("qlsky", key)) // non-standard, introduced by QuakeLives (EEK) R_SetSkyBox(value); - else if (!strcmp("farclip", key)) - { - r_farclip.value = atof(value); - if (r_farclip.value < 64) - r_farclip.value = 64; - } else if (!strcmp("fog", key)) - { - scanf(value, "%f %f %f %f", &fog_density, &fog_red, &fog_green, &fog_blue); - j = 0; - } + sscanf(value, "%f %f %f %f", &fog_density, &fog_red, &fog_green, &fog_blue); else if (!strcmp("fog_density", key)) fog_density = atof(value); else if (!strcmp("fog_red", key)) @@ -283,33 +265,56 @@ void CL_ParseEntityLump(char *entdata) fog_green = atof(value); else if (!strcmp("fog_blue", key)) fog_blue = atof(value); - else if (!strcmp("wad", key)) // for HalfLife maps + } +} + +/* +===================== +CL_SignonReply + +An svc_signonnum has been received, perform a client side setup +===================== +*/ +static void CL_SignonReply (void) +{ + //char str[8192]; + +Con_DPrintf ("CL_SignonReply: %i\n", cls.signon); + + switch (cls.signon) + { + case 1: + MSG_WriteByte (&cls.message, clc_stringcmd); + MSG_WriteString (&cls.message, "prespawn"); + break; + + case 2: + MSG_WriteByte (&cls.message, clc_stringcmd); + MSG_WriteString (&cls.message, va("name \"%s\"\n", cl_name.string)); + + MSG_WriteByte (&cls.message, clc_stringcmd); + MSG_WriteString (&cls.message, va("color %i %i\n", cl_color.integer >> 4, cl_color.integer & 15)); + + if (cl_pmodel.integer) { - j = 0; - for (i = 0;i < 128;i++) - if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':') - break; - if (value[i]) - { - for (;i < 128;i++) - { - // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'... - if (value[i] == '\\' || value[i] == '/' || value[i] == ':') - j = i+1; - else if (value[i] == ';' || value[i] == 0) - { - k = value[i]; - value[i] = 0; - strcpy(wadname, "textures/"); - strcat(wadname, &value[j]); - W_LoadTextureWadFile (wadname, FALSE); - j = i+1; - if (!k) - break; - } - } - } + MSG_WriteByte (&cls.message, clc_stringcmd); + MSG_WriteString (&cls.message, va("pmodel %i\n", cl_pmodel.integer)); } + + MSG_WriteByte (&cls.message, clc_stringcmd); + //sprintf (str, "spawn %s", cls.spawnparms); + //MSG_WriteString (&cls.message, str); + MSG_WriteString (&cls.message, "spawn"); + break; + + case 3: + MSG_WriteByte (&cls.message, clc_stringcmd); + MSG_WriteString (&cls.message, "begin"); + break; + + case 4: + Con_ClearNotify(); + break; } } @@ -318,15 +323,16 @@ void CL_ParseEntityLump(char *entdata) CL_ParseServerInfo ================== */ -extern cvar_t demo_nehahra; +qbyte entlife[MAX_EDICTS]; void CL_ParseServerInfo (void) { - char *str; - int i; - int nummodels, numsounds; - char model_precache[MAX_MODELS][MAX_QPATH]; - char sound_precache[MAX_SOUNDS][MAX_QPATH]; - + char *str; + int i; + int nummodels, numsounds; + char model_precache[MAX_MODELS][MAX_QPATH]; + char sound_precache[MAX_SOUNDS][MAX_QPATH]; + entity_t *ent; + Con_DPrintf ("Serverinfo packet received.\n"); // // wipe the client_state_t struct @@ -335,17 +341,19 @@ void CL_ParseServerInfo (void) // parse protocol version number i = MSG_ReadLong (); - if (i != PROTOCOL_VERSION && i != 250) + if (i != PROTOCOL_VERSION && i != DPPROTOCOL_VERSION1 && i != DPPROTOCOL_VERSION2 && i != DPPROTOCOL_VERSION3 && i != 250) { - Con_Printf ("Server returned version %i, not %i", i, PROTOCOL_VERSION); + Con_Printf ("Server is protocol %i, not %i, %i, %i or %i", i, DPPROTOCOL_VERSION1, DPPROTOCOL_VERSION2, DPPROTOCOL_VERSION3, PROTOCOL_VERSION); return; } Nehahrademcompatibility = false; if (i == 250) Nehahrademcompatibility = true; - if (cls.demoplayback && demo_nehahra.value) + if (cls.demoplayback && demo_nehahra.integer) Nehahrademcompatibility = true; - dpprotocol = i == DPPROTOCOL_VERSION; + dpprotocol = i; + if (dpprotocol != DPPROTOCOL_VERSION1 && dpprotocol != DPPROTOCOL_VERSION2 && dpprotocol != DPPROTOCOL_VERSION3) + dpprotocol = 0; // parse maxclients cl.maxclients = MSG_ReadByte (); @@ -354,7 +362,7 @@ void CL_ParseServerInfo (void) Con_Printf("Bad maxclients (%u) from server\n", cl.maxclients); return; } - cl.scores = Hunk_AllocName (cl.maxclients*sizeof(*cl.scores), "scores"); + cl.scores = Mem_Alloc(cl_scores_mempool, cl.maxclients*sizeof(*cl.scores)); // parse gametype cl.gametype = MSG_ReadByte (); @@ -376,6 +384,10 @@ void CL_ParseServerInfo (void) // needlessly purge it // + Mem_CheckSentinelsGlobal(); + + Mod_ClearUsed(); + // precache models memset (cl.model_precache, 0, sizeof(cl.model_precache)); for (nummodels=1 ; ; nummodels++) @@ -385,9 +397,11 @@ void CL_ParseServerInfo (void) break; if (nummodels==MAX_MODELS) { - Con_Printf ("Server sent too many model precaches\n"); + Host_Error ("Server sent too many model precaches\n"); return; } + if (strlen(str) >= MAX_QPATH) + Host_Error ("Server sent a precache name of %i characters (max %i)", strlen(str), MAX_QPATH - 1); strcpy (model_precache[nummodels], str); Mod_TouchModel (str); } @@ -401,24 +415,29 @@ void CL_ParseServerInfo (void) break; if (numsounds==MAX_SOUNDS) { - Con_Printf ("Server sent too many sound precaches\n"); + Host_Error ("Server sent too many sound precaches\n"); return; } + if (strlen(str) >= MAX_QPATH) + Host_Error ("Server sent a precache name of %i characters (max %i)", strlen(str), MAX_QPATH - 1); strcpy (sound_precache[numsounds], str); S_TouchSound (str); } + Mod_PurgeUnused(); + // // now we try to load everything else until a cache allocation fails // for (i=1 ; istate_current.active = true; + ent->render.model = cl.worldmodel = cl.model_precache[1]; + ent->render.scale = 1; + ent->render.alpha = 1; + VectorAdd(ent->render.origin, ent->render.model->normalmins, ent->render.mins); + VectorAdd(ent->render.origin, ent->render.model->normalmaxs, ent->render.maxs); + // clear entlife array + memset(entlife, 0, MAX_EDICTS); + + cl_num_entities = 1; + R_NewMap (); - Hunk_Check (); // make sure nothing is hurt - - noclip_anglehack = false; // noclip is turned off at start + CL_CGVM_Start(); + + Mem_CheckSentinelsGlobal(); + + noclip_anglehack = false; // noclip is turned off at start +} + +void CL_ValidateState(entity_state_t *s) +{ + model_t *model; + + if (!s->active) + return; + + if (s->modelindex >= MAX_MODELS) + Host_Error("CL_ValidateState: modelindex (%i) >= MAX_MODELS (%i)\n", s->modelindex, MAX_MODELS); + + // colormap is client index + 1 + if (s->colormap > cl.maxclients) + Host_Error ("CL_ValidateState: colormap (%i) > cl.maxclients (%i)", s->colormap, cl.maxclients); + + model = cl.model_precache[s->modelindex]; + Mod_CheckLoaded(model); + if (model && s->frame >= model->numframes) + { + Con_DPrintf("CL_ValidateState: no such frame %i in \"%s\"\n", s->frame, model->name); + s->frame = 0; + } + if (model && s->skin > 0 && s->skin >= model->numskins) + { + Con_DPrintf("CL_ValidateState: no such skin %i in \"%s\"\n", s->skin, model->name); + s->skin = 0; + } } +void CL_MoveLerpEntityStates(entity_t *ent) +{ + float odelta[3], adelta[3]; + VectorSubtract(ent->state_current.origin, ent->persistent.neworigin, odelta); + VectorSubtract(ent->state_current.angles, ent->persistent.newangles, adelta); + if (!ent->state_previous.active || cls.timedemo || DotProduct(odelta, odelta) > 1000*1000 || cl_nolerp.integer) + { + // we definitely shouldn't lerp + ent->persistent.lerpdeltatime = 0; + ent->persistent.lerpstarttime = cl.mtime[1]; + VectorCopy(ent->state_current.origin, ent->persistent.oldorigin); + VectorCopy(ent->state_current.angles, ent->persistent.oldangles); + VectorCopy(ent->state_current.origin, ent->persistent.neworigin); + VectorCopy(ent->state_current.angles, ent->persistent.newangles); + } + else// if (ent->state_current.flags & RENDER_STEP) + { + // monster interpolation + if (DotProduct(odelta, odelta) + DotProduct(adelta, adelta) > 0.01) + { + ent->persistent.lerpdeltatime = cl.time - ent->persistent.lerpstarttime; + ent->persistent.lerpstarttime = cl.mtime[1]; + VectorCopy(ent->persistent.neworigin, ent->persistent.oldorigin); + VectorCopy(ent->persistent.newangles, ent->persistent.oldangles); + VectorCopy(ent->state_current.origin, ent->persistent.neworigin); + VectorCopy(ent->state_current.angles, ent->persistent.newangles); + } + } + /* + else + { + // not a monster + ent->persistent.lerpstarttime = cl.mtime[1]; + // no lerp if it's singleplayer + //if (sv.active && svs.maxclients == 1 && !ent->state_current.flags & RENDER_STEP) + // ent->persistent.lerpdeltatime = 0; + //else + ent->persistent.lerpdeltatime = cl.mtime[0] - cl.mtime[1]; + VectorCopy(ent->persistent.neworigin, ent->persistent.oldorigin); + VectorCopy(ent->persistent.newangles, ent->persistent.oldangles); + VectorCopy(ent->state_current.origin, ent->persistent.neworigin); + VectorCopy(ent->state_current.angles, ent->persistent.newangles); + } + */ +} /* ================== @@ -453,153 +557,202 @@ If an entities model or origin changes from frame to frame, it must be relinked. Other attributes can change without relinking. ================== */ +int bitprofile[32], bitprofilecount = 0; void CL_ParseUpdate (int bits) { - int i, modnum, num, alpha, scale, glowsize, glowcolor, colormod; - model_t *model; - qboolean forcelink; - entity_t *ent; - entity_state_t *baseline; - - if (cls.signon == SIGNONS - 1) - { // first update is the final signon stage - cls.signon = SIGNONS; - CL_SignonReply (); - } + int i, num; + entity_t *ent; + entity_state_t new; if (bits & U_MOREBITS) bits |= (MSG_ReadByte()<<8); - if (bits & U_EXTEND1 && !Nehahrademcompatibility) + if ((bits & U_EXTEND1) && (!Nehahrademcompatibility)) { bits |= MSG_ReadByte() << 16; if (bits & U_EXTEND2) bits |= MSG_ReadByte() << 24; } - if (bits & U_LONGENTITY) - num = MSG_ReadShort (); + if (bits & U_LONGENTITY) + num = (unsigned) MSG_ReadShort (); else - num = MSG_ReadByte (); + num = (unsigned) MSG_ReadByte (); - ent = CL_EntityNum (num); + if (num >= MAX_EDICTS) + Host_Error("CL_ParseUpdate: entity number (%i) >= MAX_EDICTS (%i)\n", num, MAX_EDICTS); + if (num < 1) + Host_Error("CL_ParseUpdate: invalid entity number (%i)\n", num); - forcelink = ent->msgtime != cl.mtime[1]; // no previous frame to lerp from + ent = cl_entities + num; - ent->msgtime = cl.mtime[0]; - - // LordHavoc: new protocol stuff - baseline = &ent->baseline; - if (bits & U_DELTA) - baseline = &ent->deltabaseline; + for (i = 0;i < 32;i++) + if (bits & (1 << i)) + bitprofile[i]++; + bitprofilecount++; - if (forcelink) + // note: this inherits the 'active' state of the baseline chosen + // (state_baseline is always active, state_current may not be active if + // the entity was missing in the last frame) + if (bits & U_DELTA) + new = ent->state_current; + else { - ent->deltabaseline.origin[0] = ent->deltabaseline.origin[1] = ent->deltabaseline.origin[2] = 0; - ent->deltabaseline.angles[0] = ent->deltabaseline.angles[1] = ent->deltabaseline.angles[2] = 0; - ent->deltabaseline.effects = 0; - ent->deltabaseline.modelindex = 0; - ent->deltabaseline.frame = 0; - ent->deltabaseline.colormap = 0; - ent->deltabaseline.skin = 0; - ent->deltabaseline.alpha = 255; - ent->deltabaseline.scale = 16; - ent->deltabaseline.glowsize = 0; - ent->deltabaseline.glowcolor = 254; - ent->deltabaseline.colormod = 255; + new = ent->state_baseline; + new.active = true; } - modnum = bits & U_MODEL ? MSG_ReadByte() : baseline->modelindex; - if (modnum >= MAX_MODELS) - Host_Error ("CL_ParseModel: bad modnum"); - ent->deltabaseline.modelindex = modnum; - - model = cl.model_precache[modnum]; - if (model != ent->model) + new.number = num; + new.time = cl.mtime[0]; + new.flags = 0; + if (bits & U_MODEL) new.modelindex = (new.modelindex & 0xFF00) | MSG_ReadByte(); + if (bits & U_FRAME) new.frame = (new.frame & 0xFF00) | MSG_ReadByte(); + if (bits & U_COLORMAP) new.colormap = MSG_ReadByte(); + if (bits & U_SKIN) new.skin = MSG_ReadByte(); + if (bits & U_EFFECTS) new.effects = (new.effects & 0xFF00) | MSG_ReadByte(); + if (bits & U_ORIGIN1) new.origin[0] = MSG_ReadCoord(); + if (bits & U_ANGLE1) new.angles[0] = MSG_ReadAngle(); + if (bits & U_ORIGIN2) new.origin[1] = MSG_ReadCoord(); + if (bits & U_ANGLE2) new.angles[1] = MSG_ReadAngle(); + if (bits & U_ORIGIN3) new.origin[2] = MSG_ReadCoord(); + if (bits & U_ANGLE3) new.angles[2] = MSG_ReadAngle(); + if (bits & U_STEP) new.flags |= RENDER_STEP; + if (bits & U_ALPHA) new.alpha = MSG_ReadByte(); + if (bits & U_SCALE) new.scale = MSG_ReadByte(); + if (bits & U_EFFECTS2) new.effects = (new.effects & 0x00FF) | (MSG_ReadByte() << 8); + if (bits & U_GLOWSIZE) new.glowsize = MSG_ReadByte(); + if (bits & U_GLOWCOLOR) new.glowcolor = MSG_ReadByte(); + // apparently the dpcrush demo uses this (unintended, and it uses white anyway) + if (bits & U_COLORMOD) MSG_ReadByte(); + if (bits & U_GLOWTRAIL) new.flags |= RENDER_GLOWTRAIL; + if (bits & U_FRAME2) new.frame = (new.frame & 0x00FF) | (MSG_ReadByte() << 8); + if (bits & U_MODEL2) new.modelindex = (new.modelindex & 0x00FF) | (MSG_ReadByte() << 8); + if (bits & U_VIEWMODEL) new.flags |= RENDER_VIEWMODEL; + if (bits & U_EXTERIORMODEL) new.flags |= RENDER_EXTERIORMODEL; + + // LordHavoc: to allow playback of the Nehahra movie + if (Nehahrademcompatibility && (bits & U_EXTEND1)) { - ent->model = model; - // automatic animation (torches, etc) can be either all together - // or randomized - if (model) - ent->syncbase = model->synctype == ST_RAND ? (float)(rand()&0x7fff) / 0x7fff : 0.0; + // LordHavoc: evil format + int i = MSG_ReadFloat(); + int j = MSG_ReadFloat() * 255.0f; + if (i == 2) + { + i = MSG_ReadFloat(); + if (i) + new.effects |= EF_FULLBRIGHT; + } + if (j < 0) + new.alpha = 0; + else if (j == 0 || j >= 255) + new.alpha = 255; else - forcelink = true; // hack to make null model players work + new.alpha = j; } - ent->frame = ((bits & U_FRAME) ? MSG_ReadByte() : (baseline->frame & 0xFF)); + if (new.active) + CL_ValidateState(&new); - i = bits & U_COLORMAP ? MSG_ReadByte() : baseline->colormap; - ent->deltabaseline.colormap = i; - if (!i) - ent->colormap = -1; // no special coloring - else + ent->state_previous = ent->state_current; + ent->state_current = new; + if (ent->state_current.active) { - if (i > cl.maxclients) - Host_Error ("i >= cl.maxclients"); - ent->colormap = cl.scores[i-1].colors; // color it + CL_MoveLerpEntityStates(ent); + cl_entities_active[ent->state_current.number] = true; + // mark as visible (no kill this frame) + entlife[ent->state_current.number] = 2; } +} - ent->deltabaseline.skin = ent->skinnum = bits & U_SKIN ? MSG_ReadByte() : baseline->skin; - - ent->effects = ((bits & U_EFFECTS) ? MSG_ReadByte() : (baseline->effects & 0xFF)); - -// shift the known values for interpolation - VectorCopy (ent->msg_origins[0], ent->msg_origins[1]); - VectorCopy (ent->msg_angles[0], ent->msg_angles[1]); - VectorCopy (baseline->origin, ent->msg_origins[0]); - VectorCopy (baseline->angles, ent->msg_angles[0]); - - if (bits & U_ORIGIN1) ent->msg_origins[0][0] = MSG_ReadCoord (); - if (bits & U_ANGLE1) ent->msg_angles[0][0] = MSG_ReadAngle(); - if (bits & U_ORIGIN2) ent->msg_origins[0][1] = MSG_ReadCoord (); - if (bits & U_ANGLE2) ent->msg_angles[0][1] = MSG_ReadAngle(); - if (bits & U_ORIGIN3) ent->msg_origins[0][2] = MSG_ReadCoord (); - if (bits & U_ANGLE3) ent->msg_angles[0][2] = MSG_ReadAngle(); - - VectorCopy(ent->msg_origins[0], ent->deltabaseline.origin); - VectorCopy(ent->msg_angles[0], ent->deltabaseline.angles); - - alpha = bits & U_ALPHA ? MSG_ReadByte() : baseline->alpha; - scale = bits & U_SCALE ? MSG_ReadByte() : baseline->scale; - ent->effects |= ((bits & U_EFFECTS2) ? (MSG_ReadByte() << 8) : (baseline->effects & 0xFF00)); - glowsize = bits & U_GLOWSIZE ? MSG_ReadByte() : baseline->glowsize; - glowcolor = bits & U_GLOWCOLOR ? MSG_ReadByte() : baseline->glowcolor; - colormod = bits & U_COLORMOD ? MSG_ReadByte() : baseline->colormod; - ent->frame |= ((bits & U_FRAME2) ? (MSG_ReadByte() << 8) : (baseline->frame & 0xFF00)); - ent->deltabaseline.alpha = alpha; - ent->deltabaseline.scale = scale; - ent->deltabaseline.effects = ent->effects; - ent->deltabaseline.glowsize = glowsize; - ent->deltabaseline.glowcolor = glowcolor; - ent->deltabaseline.colormod = colormod; - ent->deltabaseline.frame = ent->frame; - ent->alpha = (float) alpha * (1.0 / 255.0); - ent->scale = (float) scale * (1.0 / 16.0); - ent->glowsize = glowsize * 4.0; - ent->glowcolor = glowcolor; - ent->colormod[0] = (float) ((colormod >> 5) & 7) * (1.0 / 7.0); - ent->colormod[1] = (float) ((colormod >> 2) & 7) * (1.0 / 7.0); - ent->colormod[2] = (float) (colormod & 3) * (1.0 / 3.0); - if (bits & U_EXTEND1 && Nehahrademcompatibility) // LordHavoc: to allow playback of the early Nehahra movie segments +void CL_ReadEntityFrame(void) +{ + entity_t *ent; + entity_frame_t entityframe; + int i; + EntityFrame_Read(&cl.entitydatabase); + EntityFrame_FetchFrame(&cl.entitydatabase, EntityFrame_MostRecentlyRecievedFrameNum(&cl.entitydatabase), &entityframe); + for (i = 0;i < entityframe.numentities;i++) { - i = MSG_ReadFloat(); - ent->alpha = MSG_ReadFloat(); - if (i == 2 && MSG_ReadFloat() != 0.0) - ent->effects |= EF_FULLBRIGHT; - if (ent->alpha == 0) - ent->alpha = 1; + // copy the states + ent = &cl_entities[entityframe.entitydata[i].number]; + ent->state_previous = ent->state_current; + ent->state_current = entityframe.entitydata[i]; + CL_MoveLerpEntityStates(ent); + // the entity lives again... + entlife[ent->state_current.number] = 2; + cl_entities_active[ent->state_current.number] = true; } + VectorCopy(cl.viewentoriginnew, cl.viewentoriginold); + VectorCopy(entityframe.eye, cl.viewentoriginnew); +} + +char *bitprofilenames[32] = +{ + "U_MOREBITS", + "U_ORIGIN1", + "U_ORIGIN2", + "U_ORIGIN3", + "U_ANGLE2", + "U_STEP", + "U_FRAME", + "U_SIGNAL", + "U_ANGLE1", + "U_ANGLE3", + "U_MODEL", + "U_COLORMAP", + "U_SKIN", + "U_EFFECTS", + "U_LONGENTITY", + "U_EXTEND1", + "U_DELTA", + "U_ALPHA", + "U_SCALE", + "U_EFFECTS2", + "U_GLOWSIZE", + "U_GLOWCOLOR", + "obsolete U_COLORMOD", + "U_EXTEND2", + "U_GLOWTRAIL", + "U_VIEWMODEL", + "U_FRAME2", + "U_MODEL2", + "U_EXTERIORMODEL", + "U_UNUSED29", + "U_UNUSED30", + "U_EXTEND3", +}; + +void CL_BitProfile_f(void) +{ + int i; + Con_Printf("bitprofile: %i updates\n"); + if (bitprofilecount) + for (i = 0;i < 32;i++) + Con_Printf("%s: %i %3.2f%%\n", bitprofilenames[i], bitprofile[i], bitprofile[i] * 100.0 / bitprofilecount); + Con_Printf("\n"); + for (i = 0;i < 32;i++) + bitprofile[i] = 0; + bitprofilecount = 0; +} + +void CL_EntityUpdateSetup(void) +{ +} - //if ( bits & U_NOLERP ) - // ent->forcelink = true; - //if (bits & U_STEP) // FIXME: implement clientside interpolation of monsters - - 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; +void CL_EntityUpdateEnd(void) +{ + int i; + // disable entities that disappeared this frame + for (i = 1;i < MAX_EDICTS;i++) + { + // clear only the entities that were active last frame but not this + // frame, don't waste time clearing all entities (which would cause + // cache misses) + if (entlife[i]) + { + entlife[i]--; + if (!entlife[i]) + cl_entities[i].state_previous.active = cl_entities[i].state_current.active = 0; + } } } @@ -608,24 +761,36 @@ void CL_ParseUpdate (int bits) CL_ParseBaseline ================== */ -void CL_ParseBaseline (entity_t *ent) +void CL_ParseBaseline (entity_t *ent, int large) { - int i; - - ent->baseline.modelindex = MSG_ReadByte (); - ent->baseline.frame = MSG_ReadByte (); - ent->baseline.colormap = MSG_ReadByte(); - ent->baseline.skin = MSG_ReadByte(); - for (i=0 ; i<3 ; i++) + int i; + + memset(&ent->state_baseline, 0, sizeof(entity_state_t)); + ent->state_baseline.active = true; + if (large) + { + ent->state_baseline.modelindex = (unsigned short) MSG_ReadShort (); + ent->state_baseline.frame = (unsigned short) MSG_ReadShort (); + } + else { - ent->baseline.origin[i] = MSG_ReadCoord (); - ent->baseline.angles[i] = MSG_ReadAngle (); + ent->state_baseline.modelindex = MSG_ReadByte (); + ent->state_baseline.frame = MSG_ReadByte (); } - ent->baseline.alpha = 255; - ent->baseline.scale = 16; - ent->baseline.glowsize = 0; - ent->baseline.glowcolor = 254; - ent->baseline.colormod = 255; + ent->state_baseline.colormap = MSG_ReadByte(); + ent->state_baseline.skin = MSG_ReadByte(); + for (i = 0;i < 3;i++) + { + ent->state_baseline.origin[i] = MSG_ReadCoord (); + ent->state_baseline.angles[i] = MSG_ReadAngle (); + } + ent->state_baseline.alpha = 255; + ent->state_baseline.scale = 16; + ent->state_baseline.glowsize = 0; + ent->state_baseline.glowcolor = 254; + ent->state_previous = ent->state_current = ent->state_baseline; + + CL_ValidateState(&ent->state_baseline); } @@ -638,8 +803,14 @@ Server information pertaining to this client only */ void CL_ParseClientdata (int bits) { - int i, j; - + int i, j; + + bits &= 0xFFFF; + if (bits & SU_EXTEND1) + bits |= (MSG_ReadByte() << 16); + if (bits & SU_EXTEND2) + bits |= (MSG_ReadByte() << 24); + if (bits & SU_VIEWHEIGHT) cl.viewheight = MSG_ReadChar (); else @@ -649,14 +820,23 @@ void CL_ParseClientdata (int bits) 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<= MAX_STATIC_ENTITIES) + + if (cl_num_static_entities >= cl_max_static_entities) Host_Error ("Too many static entities"); - ent = &cl_static_entities[i]; - cl.num_statics++; - CL_ParseBaseline (ent); + ent = &cl_static_entities[cl_num_static_entities++]; + CL_ParseBaseline (ent, large); // copy it to the current state - ent->model = cl.model_precache[ent->baseline.modelindex]; - ent->frame = ent->baseline.frame; - ent->colormap = -1; // no special coloring - ent->skinnum = ent->baseline.skin; - ent->effects = ent->baseline.effects; - ent->alpha = 1; - ent->scale = 1; - ent->alpha = 1; - ent->glowsize = 0; - ent->glowcolor = 254; - ent->colormod[0] = ent->colormod[1] = ent->colormod[2] = 1; - - VectorCopy (ent->baseline.origin, ent->origin); - VectorCopy (ent->baseline.angles, ent->angles); - R_AddEfrags (ent); + ent->render.model = cl.model_precache[ent->state_baseline.modelindex]; + ent->render.frame = ent->render.frame1 = ent->render.frame2 = ent->state_baseline.frame; + ent->render.framelerp = 0; + // make torchs play out of sync + ent->render.frame1time = ent->render.frame2time = lhrandom(-10, -1); + ent->render.colormap = -1; // no special coloring + ent->render.skinnum = ent->state_baseline.skin; + ent->render.effects = ent->state_baseline.effects; + ent->render.alpha = 1; + ent->render.scale = 1; + ent->render.alpha = 1; + + VectorCopy (ent->state_baseline.origin, ent->render.origin); + VectorCopy (ent->state_baseline.angles, ent->render.angles); + + if (ent->render.angles[0] || ent->render.angles[2]) + { + // pitch or roll + VectorAdd(ent->render.origin, ent->render.model->rotatedmins, ent->render.mins); + VectorAdd(ent->render.origin, ent->render.model->rotatedmaxs, ent->render.maxs); + } + else if (ent->render.angles[1]) + { + // yaw + VectorAdd(ent->render.origin, ent->render.model->yawmins, ent->render.mins); + VectorAdd(ent->render.origin, ent->render.model->yawmaxs, ent->render.maxs); + } + else + { + VectorAdd(ent->render.origin, ent->render.model->normalmins, ent->render.mins); + VectorAdd(ent->render.origin, ent->render.model->normalmaxs, ent->render.maxs); + } + + // This is definitely cheating... + if (ent->render.model == NULL) + cl_num_static_entities--; } /* @@ -734,32 +944,54 @@ void CL_ParseStatic (void) CL_ParseStaticSound =================== */ -void CL_ParseStaticSound (void) +void CL_ParseStaticSound (int large) { vec3_t org; int sound_num, vol, atten; - int i; - - for (i=0 ; i<3 ; i++) - org[i] = MSG_ReadCoord (); - sound_num = MSG_ReadByte (); + + MSG_ReadVector(org); + if (large) + sound_num = (unsigned short) MSG_ReadShort (); + else + sound_num = MSG_ReadByte (); vol = MSG_ReadByte (); atten = MSG_ReadByte (); - + S_StaticSound (cl.sound_precache[sound_num], org, vol, atten); } +void CL_ParseEffect (void) +{ + vec3_t org; + int modelindex, startframe, framecount, framerate; + + MSG_ReadVector(org); + modelindex = MSG_ReadByte (); + startframe = MSG_ReadByte (); + framecount = MSG_ReadByte (); + framerate = MSG_ReadByte (); + + CL_Effect(org, modelindex, startframe, framecount, framerate); +} + +void CL_ParseEffect2 (void) +{ + vec3_t org; + int modelindex, startframe, framecount, framerate; + + MSG_ReadVector(org); + modelindex = MSG_ReadShort (); + startframe = MSG_ReadShort (); + framecount = MSG_ReadByte (); + framerate = MSG_ReadByte (); + + CL_Effect(org, modelindex, startframe, framecount, framerate); +} -#define SHOWNET(x) if(cl_shownet.value==2)Con_Printf ("%3i:%s\n", msg_readcount-1, x); -extern void SHOWLMP_decodehide(); -extern void SHOWLMP_decodeshow(); -extern void R_SetSkyBox(char* sky); +#define SHOWNET(x) if(cl_shownet.integer==2)Con_Printf ("%3i:%s\n", msg_readcount-1, x); -extern float fog_density; -extern float fog_red; -extern float fog_green; -extern float fog_blue; +static qbyte cgamenetbuffer[65536]; /* ===================== @@ -769,22 +1001,27 @@ CL_ParseServerMessage void CL_ParseServerMessage (void) { int cmd; - int i; - + int i, entitiesupdated; + qbyte cmdlog[32]; + char *cmdlogname[32], *temp; + int cmdindex, cmdcount = 0; + // // if recording demos, copy the message out // - if (cl_shownet.value == 1) + if (cl_shownet.integer == 1) Con_Printf ("%i ",net_message.cursize); - else if (cl_shownet.value == 2) + else if (cl_shownet.integer == 2) Con_Printf ("------------------\n"); - - cl.onground = false; // unless the server says otherwise + + cl.onground = false; // unless the server says otherwise // // parse the message // MSG_BeginReading (); - + + entitiesupdated = false; + while (1) { if (msg_badread) @@ -795,145 +1032,223 @@ void CL_ParseServerMessage (void) if (cmd == -1) { SHOWNET("END OF MESSAGE"); - return; // end of message + break; // end of message } - // if the high bit of the command byte is set, it is a fast update + 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 (); + } CL_ParseUpdate (cmd&127); continue; } SHOWNET(svc_strings[cmd]); - - // other commands + 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 = ""; + cmdlogname[cmdindex] = temp; + } + + // other commands switch (cmd) { default: - Host_Error ("CL_ParseServerMessage: Illegible server message\n"); + { + 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) + { + sprintf(temp, "%3i:%s ", cmdlog[i], cmdlogname[i]); + strcat(description, temp); + count--; + i++; + i &= 31; + } + description[strlen(description)-1] = '\n'; // replace the last space with a newline + Con_Printf("%s", description); + Host_Error ("CL_ParseServerMessage: Illegible server message\n"); + } break; - + case svc_nop: -// Con_Printf ("svc_nop\n"); break; - + case svc_time: + if (!entitiesupdated) + { + // this is a new frame, we'll be seeing entities, + // so prepare for entity updates + CL_EntityUpdateSetup(); + entitiesupdated = true; + } cl.mtime[1] = cl.mtime[0]; - cl.mtime[0] = MSG_ReadFloat (); + cl.mtime[0] = MSG_ReadFloat (); break; - + case svc_clientdata: i = MSG_ReadShort (); CL_ParseClientdata (i); break; - + case svc_version: i = MSG_ReadLong (); - if (i != PROTOCOL_VERSION && i != 250) - Host_Error ("CL_ParseServerMessage: Server is protocol %i instead of %i\n", i, PROTOCOL_VERSION); - Nehahrademcompatibility = i == 250; + if (i != PROTOCOL_VERSION && i != DPPROTOCOL_VERSION1 && i != DPPROTOCOL_VERSION2 && i != DPPROTOCOL_VERSION3 && i != 250) + Host_Error ("CL_ParseServerMessage: Server is protocol %i, not %i, %i, %i or %i", i, DPPROTOCOL_VERSION1, DPPROTOCOL_VERSION2, DPPROTOCOL_VERSION3, PROTOCOL_VERSION); + Nehahrademcompatibility = false; + if (i == 250) + Nehahrademcompatibility = true; + if (cls.demoplayback && demo_nehahra.integer) + Nehahrademcompatibility = true; + dpprotocol = i; + if (dpprotocol != DPPROTOCOL_VERSION1 && dpprotocol != DPPROTOCOL_VERSION2 && dpprotocol != DPPROTOCOL_VERSION3) + dpprotocol = 0; break; - + case svc_disconnect: Host_EndGame ("Server disconnected\n"); case svc_print: Con_Printf ("%s", MSG_ReadString ()); break; - + case svc_centerprint: SCR_CenterPrint (MSG_ReadString ()); 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 (); break; - + case svc_setview: cl.viewentity = MSG_ReadShort (); + // 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 >= MAX_LIGHTSTYLES) - Host_Error ("svc_lightstyle > MAX_LIGHTSTYLES"); - strcpy (cl_lightstyle[i].map, MSG_ReadString()); + Host_Error ("svc_lightstyle >= MAX_LIGHTSTYLES"); + strncpy (cl_lightstyle[i].map, MSG_ReadString(), MAX_STYLESTRING - 1); + cl_lightstyle[i].map[MAX_STYLESTRING - 1] = 0; cl_lightstyle[i].length = strlen(cl_lightstyle[i].map); break; - + case svc_sound: - CL_ParseStartSoundPacket(); + CL_ParseStartSoundPacket(false); + break; + + case svc_sound2: + CL_ParseStartSoundPacket(true); break; - + case svc_stopsound: i = 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 > MAX_SCOREBOARD"); + Host_Error ("CL_ParseServerMessage: svc_updatename >= cl.maxclients"); strcpy (cl.scores[i].name, MSG_ReadString ()); break; - + case svc_updatefrags: i = MSG_ReadByte (); if (i >= cl.maxclients) - Host_Error ("CL_ParseServerMessage: svc_updatefrags > MAX_SCOREBOARD"); + Host_Error ("CL_ParseServerMessage: svc_updatefrags >= cl.maxclients"); cl.scores[i].frags = MSG_ReadShort (); - break; + break; case svc_updatecolors: i = MSG_ReadByte (); if (i >= cl.maxclients) - Host_Error ("CL_ParseServerMessage: svc_updatecolors > MAX_SCOREBOARD"); + Host_Error ("CL_ParseServerMessage: svc_updatecolors >= cl.maxclients"); cl.scores[i].colors = MSG_ReadByte (); + // update our color cvar if our color changed + if (i == cl.playerentity - 1) + Cvar_SetValue ("_cl_color", cl.scores[i].colors); break; - + case svc_particle: - R_ParseParticleEffect (); + CL_ParseParticleEffect (); + break; + + case svc_effect: + CL_ParseEffect (); + break; + + case svc_effect2: + CL_ParseEffect2 (); break; case svc_spawnbaseline: i = MSG_ReadShort (); - // must use CL_EntityNum() to force cl.num_entities up - CL_ParseBaseline (CL_EntityNum(i)); + if (i < 0 || i >= MAX_EDICTS) + Host_Error ("CL_ParseServerMessage: svc_spawnbaseline: invalid entity number %i", i); + CL_ParseBaseline (cl_entities + i, false); + break; + case svc_spawnbaseline2: + i = MSG_ReadShort (); + if (i < 0 || i >= MAX_EDICTS) + Host_Error ("CL_ParseServerMessage: svc_spawnbaseline2: invalid entity number %i", i); + CL_ParseBaseline (cl_entities + i, true); break; case svc_spawnstatic: - CL_ParseStatic (); - break; + CL_ParseStatic (false); + break; + case svc_spawnstatic2: + CL_ParseStatic (true); + break; case svc_temp_entity: CL_ParseTEnt (); break; case svc_setpause: - { - cl.paused = MSG_ReadByte (); - - if (cl.paused) - CDAudio_Pause (); - else - CDAudio_Resume (); - } + cl.paused = MSG_ReadByte (); + if (cl.paused) + CDAudio_Pause (); + else + CDAudio_Resume (); break; - + case svc_signonnum: i = MSG_ReadByte (); if (i <= cls.signon) @@ -954,40 +1269,41 @@ void CL_ParseServerMessage (void) i = MSG_ReadByte (); if (i < 0 || i >= MAX_CL_STATS) Host_Error ("svc_updatestat: %i is invalid", i); - cl.stats[i] = MSG_ReadLong ();; + cl.stats[i] = MSG_ReadLong (); break; - + case svc_spawnstaticsound: - CL_ParseStaticSound (); + 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 ((byte)cls.forcetrack, true); + CDAudio_Play ((qbyte)cls.forcetrack, true); else - CDAudio_Play ((byte)cl.cdtrack, true); + CDAudio_Play ((qbyte)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 - SCR_CenterPrint (MSG_ReadString ()); + SCR_CenterPrint (MSG_ReadString ()); break; case svc_cutscene: cl.intermission = 3; cl.completed_time = cl.time; - vid.recalc_refdef = true; // go to full screen - SCR_CenterPrint (MSG_ReadString ()); + SCR_CenterPrint (MSG_ReadString ()); break; case svc_sellscreen: @@ -999,25 +1315,32 @@ void CL_ParseServerMessage (void) case svc_showlmp: SHOWLMP_decodeshow(); break; - // LordHavoc: extra worldspawn fields (fog, sky, farclip) case svc_skybox: R_SetSkyBox(MSG_ReadString()); break; - case svc_farclip: - r_farclip.value = MSG_ReadCoord(); + 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_fog: - if (MSG_ReadByte()) + case svc_entities: + if (cls.signon == SIGNONS - 1) { - fog_density = MSG_ReadShort() * (1.0f / 4096.0f); - fog_red = MSG_ReadByte() * (1.0 / 255.0); - fog_green = MSG_ReadByte() * (1.0 / 255.0); - fog_blue = MSG_ReadByte() * (1.0 / 255.0); + // first update is the final signon stage + cls.signon = SIGNONS; + CL_SignonReply (); } - else - fog_density = 0.0f; + CL_ReadEntityFrame(); break; } } + + if (entitiesupdated) + CL_EntityUpdateEnd(); }