/* * Copyright (C) 1997-2001 Id Software, Inc. * * 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. * * ======================================================================= * * This file implements the entity and network protocol parsing * * ======================================================================= */ #include "header/client.h" void CL_DownloadFileName(char *dest, int destlen, char *fn); void CL_ParseDownload (void); int bitcounts[32]; /* just for protocol profiling */ char *svc_strings[256] = { "svc_bad", "svc_muzzleflash", "svc_muzzlflash2", "svc_temp_entity", "svc_layout", "svc_inventory", "svc_nop", "svc_disconnect", "svc_reconnect", "svc_sound", "svc_print", "svc_stufftext", "svc_serverdata", "svc_configstring", "svc_spawnbaseline", "svc_centerprint", "svc_download", "svc_playerinfo", "svc_packetentities", "svc_deltapacketentities", "svc_frame" }; void CL_RegisterSounds (void) { int i; S_BeginRegistration (); CL_RegisterTEntSounds (); for (i=1 ; iorigin, to->old_origin); to->number = number; if (bits & U_MODEL) to->modelindex = MSG_ReadByte (&net_message); if (bits & U_MODEL2) to->modelindex2 = MSG_ReadByte (&net_message); if (bits & U_MODEL3) to->modelindex3 = MSG_ReadByte (&net_message); if (bits & U_MODEL4) to->modelindex4 = MSG_ReadByte (&net_message); if (bits & U_FRAME8) to->frame = MSG_ReadByte (&net_message); if (bits & U_FRAME16) to->frame = MSG_ReadShort (&net_message); /* used for laser colors */ if ((bits & U_SKIN8) && (bits & U_SKIN16)) to->skinnum = MSG_ReadLong(&net_message); else if (bits & U_SKIN8) to->skinnum = MSG_ReadByte(&net_message); else if (bits & U_SKIN16) to->skinnum = MSG_ReadShort(&net_message); if ( (bits & (U_EFFECTS8|U_EFFECTS16)) == (U_EFFECTS8|U_EFFECTS16) ) to->effects = MSG_ReadLong(&net_message); else if (bits & U_EFFECTS8) to->effects = MSG_ReadByte(&net_message); else if (bits & U_EFFECTS16) to->effects = MSG_ReadShort(&net_message); if ( (bits & (U_RENDERFX8|U_RENDERFX16)) == (U_RENDERFX8|U_RENDERFX16) ) to->renderfx = MSG_ReadLong(&net_message); else if (bits & U_RENDERFX8) to->renderfx = MSG_ReadByte(&net_message); else if (bits & U_RENDERFX16) to->renderfx = MSG_ReadShort(&net_message); if (bits & U_ORIGIN1) to->origin[0] = MSG_ReadCoord (&net_message); if (bits & U_ORIGIN2) to->origin[1] = MSG_ReadCoord (&net_message); if (bits & U_ORIGIN3) to->origin[2] = MSG_ReadCoord (&net_message); if (bits & U_ANGLE1) to->angles[0] = MSG_ReadAngle(&net_message); if (bits & U_ANGLE2) to->angles[1] = MSG_ReadAngle(&net_message); if (bits & U_ANGLE3) to->angles[2] = MSG_ReadAngle(&net_message); if (bits & U_OLDORIGIN) MSG_ReadPos (&net_message, to->old_origin); if (bits & U_SOUND) to->sound = MSG_ReadByte (&net_message); if (bits & U_EVENT) to->event = MSG_ReadByte (&net_message); else to->event = 0; if (bits & U_SOLID) to->solid = MSG_ReadShort (&net_message); } /* * Parses deltas from the given base and adds the resulting entity to * the current frame */ void CL_DeltaEntity (frame_t *frame, int newnum, entity_state_t *old, int bits) { centity_t *ent; entity_state_t *state; ent = &cl_entities[newnum]; state = &cl_parse_entities[cl.parse_entities & (MAX_PARSE_ENTITIES-1)]; cl.parse_entities++; frame->num_entities++; CL_ParseDelta (old, state, newnum, bits); /* some data changes will force no lerping */ if (state->modelindex != ent->current.modelindex || state->modelindex2 != ent->current.modelindex2 || state->modelindex3 != ent->current.modelindex3 || state->modelindex4 != ent->current.modelindex4 || state->event == EV_PLAYER_TELEPORT || state->event == EV_OTHER_TELEPORT || abs((int)(state->origin[0] - ent->current.origin[0])) > 512 || abs((int)(state->origin[1] - ent->current.origin[1])) > 512 || abs((int)(state->origin[2] - ent->current.origin[2])) > 512 ) { ent->serverframe = -99; } /* wasn't in last update, so initialize some things */ if (ent->serverframe != cl.frame.serverframe - 1) { ent->trailcount = 1024; /* for diminishing rocket / grenade trails */ /* duplicate the current state so * lerping doesn't hurt anything */ ent->prev = *state; if (state->event == EV_OTHER_TELEPORT) { VectorCopy (state->origin, ent->prev.origin); VectorCopy (state->origin, ent->lerp_origin); } else { VectorCopy (state->old_origin, ent->prev.origin); VectorCopy (state->old_origin, ent->lerp_origin); } } else { /* shuffle the last state to previous */ ent->prev = ent->current; } ent->serverframe = cl.frame.serverframe; ent->current = *state; } /* * An svc_packetentities has just been parsed, deal with the rest of the * data stream. */ void CL_ParsePacketEntities (frame_t *oldframe, frame_t *newframe) { unsigned int newnum; unsigned bits; entity_state_t *oldstate = NULL; int oldindex, oldnum; newframe->parse_entities = cl.parse_entities; newframe->num_entities = 0; /* delta from the entities present in oldframe */ oldindex = 0; if (!oldframe) oldnum = 99999; else { if (oldindex >= oldframe->num_entities) oldnum = 99999; else { oldstate = &cl_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)]; oldnum = oldstate->number; } } while (1) { newnum = CL_ParseEntityBits (&bits); if (newnum >= MAX_EDICTS) Com_Error (ERR_DROP,"CL_ParsePacketEntities: bad number:%i", newnum); if (net_message.readcount > net_message.cursize) Com_Error (ERR_DROP,"CL_ParsePacketEntities: end of message"); if (!newnum) break; while (oldnum < newnum) { /* one or more entities from the old packet are unchanged */ if (cl_shownet->value == 3) Com_Printf (" unchanged: %i\n", oldnum); CL_DeltaEntity (newframe, oldnum, oldstate, 0); oldindex++; if (oldindex >= oldframe->num_entities) oldnum = 99999; else { oldstate = &cl_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)]; oldnum = oldstate->number; } } if (bits & U_REMOVE) { /* the entity present in oldframe is not in the current frame */ if (cl_shownet->value == 3) Com_Printf (" remove: %i\n", newnum); if (oldnum != newnum) Com_Printf ("U_REMOVE: oldnum != newnum\n"); oldindex++; if (oldindex >= oldframe->num_entities) oldnum = 99999; else { oldstate = &cl_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)]; oldnum = oldstate->number; } continue; } if (oldnum == newnum) { /* delta from previous state */ if (cl_shownet->value == 3) Com_Printf (" delta: %i\n", newnum); CL_DeltaEntity (newframe, newnum, oldstate, bits); oldindex++; if (oldindex >= oldframe->num_entities) oldnum = 99999; else { oldstate = &cl_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)]; oldnum = oldstate->number; } continue; } if (oldnum > newnum) { /* delta from baseline */ if (cl_shownet->value == 3) Com_Printf (" baseline: %i\n", newnum); CL_DeltaEntity (newframe, newnum, &cl_entities[newnum].baseline, bits); continue; } } /* any remaining entities in the old frame are copied over */ while (oldnum != 99999) { /* one or more entities from the old packet are unchanged */ if (cl_shownet->value == 3) Com_Printf (" unchanged: %i\n", oldnum); CL_DeltaEntity (newframe, oldnum, oldstate, 0); oldindex++; if (oldindex >= oldframe->num_entities) oldnum = 99999; else { oldstate = &cl_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)]; oldnum = oldstate->number; } } } void CL_ParsePlayerstate (frame_t *oldframe, frame_t *newframe) { int flags; player_state_t *state; int i; int statbits; state = &newframe->playerstate; /* clear to old value before delta parsing */ if (oldframe) *state = oldframe->playerstate; else memset (state, 0, sizeof(*state)); flags = MSG_ReadShort (&net_message); /* parse the pmove_state_t */ if (flags & PS_M_TYPE) state->pmove.pm_type = MSG_ReadByte (&net_message); if (flags & PS_M_ORIGIN) { state->pmove.origin[0] = MSG_ReadShort (&net_message); state->pmove.origin[1] = MSG_ReadShort (&net_message); state->pmove.origin[2] = MSG_ReadShort (&net_message); } if (flags & PS_M_VELOCITY) { state->pmove.velocity[0] = MSG_ReadShort (&net_message); state->pmove.velocity[1] = MSG_ReadShort (&net_message); state->pmove.velocity[2] = MSG_ReadShort (&net_message); } if (flags & PS_M_TIME) state->pmove.pm_time = MSG_ReadByte (&net_message); if (flags & PS_M_FLAGS) state->pmove.pm_flags = MSG_ReadByte (&net_message); if (flags & PS_M_GRAVITY) state->pmove.gravity = MSG_ReadShort (&net_message); if (flags & PS_M_DELTA_ANGLES) { state->pmove.delta_angles[0] = MSG_ReadShort (&net_message); state->pmove.delta_angles[1] = MSG_ReadShort (&net_message); state->pmove.delta_angles[2] = MSG_ReadShort (&net_message); } if (cl.attractloop) state->pmove.pm_type = PM_FREEZE; /* demo playback */ /* parse the rest of the player_state_t */ if (flags & PS_VIEWOFFSET) { state->viewoffset[0] = MSG_ReadChar (&net_message) * 0.25f; state->viewoffset[1] = MSG_ReadChar (&net_message) * 0.25f; state->viewoffset[2] = MSG_ReadChar (&net_message) * 0.25f; } if (flags & PS_VIEWANGLES) { state->viewangles[0] = MSG_ReadAngle16 (&net_message); state->viewangles[1] = MSG_ReadAngle16 (&net_message); state->viewangles[2] = MSG_ReadAngle16 (&net_message); } if (flags & PS_KICKANGLES) { state->kick_angles[0] = MSG_ReadChar (&net_message) * 0.25f; state->kick_angles[1] = MSG_ReadChar (&net_message) * 0.25f; state->kick_angles[2] = MSG_ReadChar (&net_message) * 0.25f; } if (flags & PS_WEAPONINDEX) { state->gunindex = MSG_ReadByte (&net_message); } if (flags & PS_WEAPONFRAME) { state->gunframe = MSG_ReadByte (&net_message); state->gunoffset[0] = MSG_ReadChar (&net_message)*0.25f; state->gunoffset[1] = MSG_ReadChar (&net_message)*0.25f; state->gunoffset[2] = MSG_ReadChar (&net_message)*0.25f; state->gunangles[0] = MSG_ReadChar (&net_message)*0.25f; state->gunangles[1] = MSG_ReadChar (&net_message)*0.25f; state->gunangles[2] = MSG_ReadChar (&net_message)*0.25f; } if (flags & PS_BLEND) { state->blend[0] = MSG_ReadByte (&net_message)/255.0f; state->blend[1] = MSG_ReadByte (&net_message)/255.0f; state->blend[2] = MSG_ReadByte (&net_message)/255.0f; state->blend[3] = MSG_ReadByte (&net_message)/255.0f; } if (flags & PS_FOV) state->fov = (float)MSG_ReadByte (&net_message); if (flags & PS_RDFLAGS) state->rdflags = MSG_ReadByte (&net_message); /* parse stats */ statbits = MSG_ReadLong (&net_message); for (i=0 ; istats[i] = MSG_ReadShort(&net_message); } void CL_FireEntityEvents (frame_t *frame) { entity_state_t *s1; int pnum, num; for (pnum = 0 ; pnumnum_entities ; pnum++) { num = (frame->parse_entities + pnum)&(MAX_PARSE_ENTITIES-1); s1 = &cl_parse_entities[num]; if (s1->event) CL_EntityEvent (s1); if (s1->effects & EF_TELEPORTER) CL_TeleporterParticles (s1); } } void CL_ParseFrame (void) { int cmd; int len; frame_t *old; memset (&cl.frame, 0, sizeof(cl.frame)); cl.frame.serverframe = MSG_ReadLong (&net_message); cl.frame.deltaframe = MSG_ReadLong (&net_message); cl.frame.servertime = cl.frame.serverframe*100; /* BIG HACK to let old demos continue to work */ if (cls.serverProtocol != 26) cl.surpressCount = MSG_ReadByte (&net_message); if (cl_shownet->value == 3) Com_Printf (" frame:%i delta:%i\n", cl.frame.serverframe, cl.frame.deltaframe); /* * If the frame is delta compressed from data that we * no longer have available, we must suck up the rest of * the frame, but not use it, then ask for a non-compressed * message */ if (cl.frame.deltaframe <= 0) { cl.frame.valid = true; /* uncompressed frame */ old = NULL; cls.demowaiting = false; /* we can start recording now */ } else { old = &cl.frames[cl.frame.deltaframe & UPDATE_MASK]; if (!old->valid) { /* should never happen */ Com_Printf ("Delta from invalid frame (not supposed to happen!).\n"); } if (old->serverframe != cl.frame.deltaframe) { /* The frame that the server did the delta from * is too old, so we can't reconstruct it properly. */ Com_Printf ("Delta frame too old.\n"); } else if (cl.parse_entities - old->parse_entities > MAX_PARSE_ENTITIES-128) { Com_Printf ("Delta parse_entities too old.\n"); } else cl.frame.valid = true; /* valid delta parse */ } /* clamp time */ if (cl.time > cl.frame.servertime) cl.time = cl.frame.servertime; else if (cl.time < cl.frame.servertime - 100) cl.time = cl.frame.servertime - 100; /* read areabits */ len = MSG_ReadByte (&net_message); MSG_ReadData (&net_message, &cl.frame.areabits, len); /* read playerinfo */ cmd = MSG_ReadByte (&net_message); SHOWNET(svc_strings[cmd]); if (cmd != svc_playerinfo) Com_Error (ERR_DROP, "CL_ParseFrame: 0x%X not playerinfo", cmd); CL_ParsePlayerstate (old, &cl.frame); /* read packet entities */ cmd = MSG_ReadByte (&net_message); SHOWNET(svc_strings[cmd]); if (cmd != svc_packetentities) Com_Error (ERR_DROP, "CL_ParseFrame: 0x%X not packetentities", cmd); CL_ParsePacketEntities (old, &cl.frame); /* save the frame off in the backup array for later delta comparisons */ cl.frames[cl.frame.serverframe & UPDATE_MASK] = cl.frame; if (cl.frame.valid) { /* getting a valid frame message ends the connection process */ if (cls.state != ca_active) { cls.state = ca_active; cl.force_refdef = true; cl.predicted_origin[0] = cl.frame.playerstate.pmove.origin[0]*0.125f; cl.predicted_origin[1] = cl.frame.playerstate.pmove.origin[1]*0.125f; cl.predicted_origin[2] = cl.frame.playerstate.pmove.origin[2]*0.125f; VectorCopy (cl.frame.playerstate.viewangles, cl.predicted_angles); if (cls.disable_servercount != cl.servercount && cl.refresh_prepped) SCR_EndLoadingPlaque (); /* get rid of loading plaque */ cl.sound_prepped = true; } /* fire entity events */ CL_FireEntityEvents (&cl.frame); if (!(!cl_predict->value || (cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION))) CL_CheckPredictionError(); } } void CL_ParseServerData (void) { extern cvar_t *fs_gamedirvar; char *str; int i; Com_DPrintf ("Serverdata packet received.\n"); /* wipe the client_state_t struct */ CL_ClearState (); cls.state = ca_connected; /* parse protocol version number */ i = MSG_ReadLong (&net_message); cls.serverProtocol = i; /* another demo hack */ if (Com_ServerState() && PROTOCOL_VERSION == 34) { } else if (i != PROTOCOL_VERSION) Com_Error (ERR_DROP,"Server returned version %i, not %i", i, PROTOCOL_VERSION); cl.servercount = MSG_ReadLong (&net_message); cl.attractloop = MSG_ReadByte (&net_message); /* game directory */ str = MSG_ReadString (&net_message); strncpy (cl.gamedir, str, sizeof(cl.gamedir)-1); /* set gamedir */ if ((*str && (!fs_gamedirvar->string || !*fs_gamedirvar->string || strcmp(fs_gamedirvar->string, str))) || (!*str && (fs_gamedirvar->string || *fs_gamedirvar->string))) Cvar_Set("game", str); /* parse player entity number */ cl.playernum = MSG_ReadShort (&net_message); /* get the full level name */ str = MSG_ReadString (&net_message); if (cl.playernum == -1) { /* playing a cinematic or showing a pic, not a level */ SCR_PlayCinematic (str); } else { /* seperate the printfs so the server message can have a color */ Com_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n"); Com_Printf ("%c%s\n", 2, str); /* need to prep refresh at next oportunity */ cl.refresh_prepped = false; } } void CL_ParseBaseline (void) { entity_state_t *es; unsigned bits; int newnum; entity_state_t nullstate; memset (&nullstate, 0, sizeof(nullstate)); newnum = CL_ParseEntityBits (&bits); es = &cl_entities[newnum].baseline; CL_ParseDelta (&nullstate, es, newnum, bits); } void CL_LoadClientinfo (clientinfo_t *ci, char *s) { int i; char *t; char model_name[MAX_QPATH]; char skin_name[MAX_QPATH]; char model_filename[MAX_QPATH]; char skin_filename[MAX_QPATH]; char weapon_filename[MAX_QPATH]; strncpy(ci->cinfo, s, sizeof(ci->cinfo)); ci->cinfo[sizeof(ci->cinfo)-1] = 0; /* isolate the player's name */ strncpy(ci->name, s, sizeof(ci->name)); ci->name[sizeof(ci->name)-1] = 0; t = strstr (s, "\\"); if (t) { ci->name[t-s] = 0; s = t+1; } if (cl_noskins->value || *s == 0) { strcpy (model_filename, "players/male/tris.md2"); strcpy (weapon_filename, "players/male/weapon.md2"); strcpy (skin_filename, "players/male/grunt.pcx"); strcpy (ci->iconname, "/players/male/grunt_i.pcx"); ci->model = re.RegisterModel (model_filename); memset(ci->weaponmodel, 0, sizeof(ci->weaponmodel)); ci->weaponmodel[0] = re.RegisterModel (weapon_filename); ci->skin = re.RegisterSkin (skin_filename); ci->icon = re.RegisterPic (ci->iconname); } else { /* isolate the model name */ strcpy (model_name, s); t = strstr(model_name, "/"); if (!t) t = strstr(model_name, "\\"); if (!t) t = model_name; *t = 0; /* isolate the skin name */ strcpy (skin_name, s + strlen(model_name) + 1); /* model file */ Com_sprintf (model_filename, sizeof(model_filename), "players/%s/tris.md2", model_name); ci->model = re.RegisterModel (model_filename); if (!ci->model) { strcpy(model_name, "male"); Com_sprintf (model_filename, sizeof(model_filename), "players/male/tris.md2"); ci->model = re.RegisterModel (model_filename); } /* skin file */ Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name); ci->skin = re.RegisterSkin (skin_filename); /* if we don't have the skin and the model wasn't male, see if the male has it (this is for CTF's skins) */ if (!ci->skin && Q_stricmp(model_name, "male")) { /* change model to male */ strcpy(model_name, "male"); Com_sprintf (model_filename, sizeof(model_filename), "players/male/tris.md2"); ci->model = re.RegisterModel (model_filename); /* see if the skin exists for the male model */ Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name); ci->skin = re.RegisterSkin (skin_filename); } /* if we still don't have a skin, it means that the male model didn't have it, so default to grunt */ if (!ci->skin) { /* see if the skin exists for the male model */ Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/grunt.pcx", model_name, skin_name); ci->skin = re.RegisterSkin (skin_filename); } /* weapon file */ for (i = 0; i < num_cl_weaponmodels; i++) { Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/%s/%s", model_name, cl_weaponmodels[i]); ci->weaponmodel[i] = re.RegisterModel(weapon_filename); if (!ci->weaponmodel[i] && strcmp(model_name, "cyborg") == 0) { /* try male */ Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/male/%s", cl_weaponmodels[i]); ci->weaponmodel[i] = re.RegisterModel(weapon_filename); } if (!cl_vwep->value) break; /* only one when vwep is off */ } /* icon file */ Com_sprintf (ci->iconname, sizeof(ci->iconname), "/players/%s/%s_i.pcx", model_name, skin_name); ci->icon = re.RegisterPic (ci->iconname); } /* must have loaded all data types to be valid */ if (!ci->skin || !ci->icon || !ci->model || !ci->weaponmodel[0]) { ci->skin = NULL; ci->icon = NULL; ci->model = NULL; ci->weaponmodel[0] = NULL; return; } } /* * Load the skin, icon, and model for a client */ void CL_ParseClientinfo (int player) { char *s; clientinfo_t *ci; s = cl.configstrings[player+CS_PLAYERSKINS]; ci = &cl.clientinfo[player]; CL_LoadClientinfo (ci, s); } void CL_ParseConfigString (void) { int i; char *s; char olds[MAX_QPATH]; i = MSG_ReadShort (&net_message); if (i < 0 || i >= MAX_CONFIGSTRINGS) Com_Error (ERR_DROP, "configstring > MAX_CONFIGSTRINGS"); s = MSG_ReadString(&net_message); strncpy (olds, cl.configstrings[i], sizeof(olds)); olds[sizeof(olds) - 1] = 0; strcpy (cl.configstrings[i], s); /* do something apropriate */ if (i >= CS_LIGHTS && i < CS_LIGHTS+MAX_LIGHTSTYLES) CL_SetLightstyle (i - CS_LIGHTS); else if (i == CS_CDTRACK) { if (cl.refresh_prepped) { CDAudio_Play (atoi(cl.configstrings[CS_CDTRACK]), true); OGG_ResumeCmd(); } } else if (i >= CS_MODELS && i < CS_MODELS+MAX_MODELS) { if (cl.refresh_prepped) { cl.model_draw[i-CS_MODELS] = re.RegisterModel (cl.configstrings[i]); if (cl.configstrings[i][0] == '*') cl.model_clip[i-CS_MODELS] = CM_InlineModel (cl.configstrings[i]); else cl.model_clip[i-CS_MODELS] = NULL; } } else if (i >= CS_SOUNDS && i < CS_SOUNDS+MAX_MODELS) { if (cl.refresh_prepped) cl.sound_precache[i-CS_SOUNDS] = S_RegisterSound (cl.configstrings[i]); } else if (i >= CS_IMAGES && i < CS_IMAGES+MAX_MODELS) { if (cl.refresh_prepped) cl.image_precache[i-CS_IMAGES] = re.RegisterPic (cl.configstrings[i]); } else if (i >= CS_PLAYERSKINS && i < CS_PLAYERSKINS+MAX_CLIENTS) { if (cl.refresh_prepped && strcmp(olds, s)) CL_ParseClientinfo (i-CS_PLAYERSKINS); } } void CL_ParseStartSoundPacket(void) { vec3_t pos_v; float *pos; int channel, ent; int sound_num; float volume; float attenuation; int flags; float ofs; flags = MSG_ReadByte (&net_message); sound_num = MSG_ReadByte (&net_message); if (flags & SND_VOLUME) volume = MSG_ReadByte (&net_message) / 255.0f; else volume = DEFAULT_SOUND_PACKET_VOLUME; if (flags & SND_ATTENUATION) attenuation = MSG_ReadByte (&net_message) / 64.0f; else attenuation = DEFAULT_SOUND_PACKET_ATTENUATION; if (flags & SND_OFFSET) ofs = MSG_ReadByte (&net_message) / 1000.0f; else ofs = 0; if (flags & SND_ENT) { /* entity reletive */ channel = MSG_ReadShort(&net_message); ent = channel>>3; if (ent > MAX_EDICTS) Com_Error (ERR_DROP,"CL_ParseStartSoundPacket: ent = %i", ent); channel &= 7; } else { ent = 0; channel = 0; } if (flags & SND_POS) { /* positioned in space */ MSG_ReadPos (&net_message, pos_v); pos = pos_v; } else /* use entity number */ pos = NULL; if (!cl.sound_precache[sound_num]) return; S_StartSound (pos, ent, channel, cl.sound_precache[sound_num], volume, attenuation, ofs); } void SHOWNET(char *s) { if (cl_shownet->value>=2) Com_Printf ("%3i:%s\n", net_message.readcount-1, s); } void CL_ParseServerMessage (void) { int cmd; char *s; int i; /* if recording demos, copy the message out */ if (cl_shownet->value == 1) Com_Printf ("%i ",net_message.cursize); else if (cl_shownet->value >= 2) Com_Printf ("------------------\n"); /* parse the message */ while (1) { if (net_message.readcount > net_message.cursize) { Com_Error (ERR_DROP,"CL_ParseServerMessage: Bad server message"); break; } cmd = MSG_ReadByte (&net_message); if (cmd == -1) { SHOWNET("END OF MESSAGE"); break; } if (cl_shownet->value>=2) { if (!svc_strings[cmd]) Com_Printf ("%3i:BAD CMD %i\n", net_message.readcount-1,cmd); else SHOWNET(svc_strings[cmd]); } /* other commands */ switch (cmd) { default: Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n"); break; case svc_nop: break; case svc_disconnect: Com_Error (ERR_DISCONNECT,"Server disconnected\n"); break; case svc_reconnect: Com_Printf ("Server disconnected, reconnecting\n"); if (cls.download) { /* close download */ fclose (cls.download); cls.download = NULL; } cls.state = ca_connecting; cls.connect_time = -99999; /* CL_CheckForResend() will fire immediately */ break; case svc_print: i = MSG_ReadByte (&net_message); if (i == PRINT_CHAT) { S_StartLocalSound ("misc/talk.wav"); con.ormask = 128; } Com_Printf ("%s", MSG_ReadString (&net_message)); con.ormask = 0; break; case svc_centerprint: SCR_CenterPrint (MSG_ReadString (&net_message)); break; case svc_stufftext: s = MSG_ReadString (&net_message); Com_DPrintf ("stufftext: %s\n", s); Cbuf_AddText (s); break; case svc_serverdata: Cbuf_Execute (); /* make sure any stuffed commands are done */ CL_ParseServerData (); break; case svc_configstring: CL_ParseConfigString (); break; case svc_sound: CL_ParseStartSoundPacket(); break; case svc_spawnbaseline: CL_ParseBaseline (); break; case svc_temp_entity: CL_ParseTEnt (); break; case svc_muzzleflash: CL_ParseMuzzleFlash (); break; case svc_muzzleflash2: CL_ParseMuzzleFlash2 (); break; case svc_download: CL_ParseDownload (); break; case svc_frame: CL_ParseFrame (); break; case svc_inventory: CL_ParseInventory (); break; case svc_layout: s = MSG_ReadString (&net_message); strncpy (cl.layout, s, sizeof(cl.layout)-1); break; case svc_playerinfo: case svc_packetentities: case svc_deltapacketentities: Com_Error (ERR_DROP, "Out of place frame data"); break; } } CL_AddNetgraph (); /* we don't know if it is ok to save a demo message until after we have parsed the frame */ if (cls.demorecording && !cls.demowaiting) CL_WriteDemoMessage (); }