/* ============== CL_ParseResourceList ============== */ void CL_ParseResourceList( sizebuf_t *msg ) { int i = 0; Q_memset( &reslist, 0, sizeof( resourcelist_t )); reslist.rescount = BF_ReadWord( msg ) - 1; for( i = 0; i < reslist.rescount; i++ ) { reslist.restype[i] = BF_ReadWord( msg ); Q_strncpy( reslist.resnames[i], BF_ReadString( msg ), CS_SIZE ); } cls.downloadcount = 0; for( i = 0; i < reslist.rescount; i++ ) { if( reslist.restype[i] == t_sound ) CL_CheckingSoundResFile( reslist.resnames[i] ); else CL_CheckingResFile( reslist.resnames[i] ); } if( !cls.downloadcount ) { BF_WriteByte( &cls.netchan.message, clc_stringcmd ); BF_WriteString( &cls.netchan.message, "continueloading" ); } }
/* ================== CL_ParseSoundPacket ================== */ void CL_ParseSoundPacket( sizebuf_t *msg, qboolean is_ambient ) { vec3_t pos; int chan, sound; float volume, attn; int flags, pitch, entnum; sound_t handle = 0; flags = BF_ReadWord( msg ); if( flags & SND_LARGE_INDEX ) sound = BF_ReadWord( msg ); else sound = BF_ReadByte( msg ); chan = BF_ReadByte( msg ); if( flags & SND_VOLUME ) volume = (float)BF_ReadByte( msg ) / 255.0f; else volume = VOL_NORM; if( flags & SND_ATTENUATION ) attn = (float)BF_ReadByte( msg ) / 64.0f; else attn = ATTN_NONE; if( flags & SND_PITCH ) pitch = BF_ReadByte( msg ); else pitch = PITCH_NORM; // entity reletive entnum = BF_ReadWord( msg ); // positioned in space BF_ReadVec3Coord( msg, pos ); if( flags & SND_SENTENCE ) { char sentenceName[32]; Q_snprintf( sentenceName, sizeof( sentenceName ), "!%i", sound ); handle = S_RegisterSound( sentenceName ); } else handle = cl.sound_index[sound]; // see precached sound if( !cl.audio_prepped ) return; // too early if( is_ambient ) { S_AmbientSound( pos, entnum, handle, volume, attn, pitch, flags ); } else { S_StartSound( pos, entnum, chan, handle, volume, attn, pitch, flags ); } }
/* ================== CL_ParseRestoreSoundPacket ================== */ void CL_ParseRestoreSoundPacket( sizebuf_t *msg ) { vec3_t pos; int chan, sound; float volume, attn; int flags, pitch, entnum; double samplePos, forcedEnd; int wordIndex; sound_t handle = 0; flags = BF_ReadWord( msg ); if( flags & SND_LARGE_INDEX ) sound = BF_ReadWord( msg ); else sound = BF_ReadByte( msg ); chan = BF_ReadByte( msg ); if( flags & SND_VOLUME ) volume = (float)BF_ReadByte( msg ) / 255.0f; else volume = VOL_NORM; if( flags & SND_ATTENUATION ) attn = (float)BF_ReadByte( msg ) / 64.0f; else attn = ATTN_NONE; if( flags & SND_PITCH ) pitch = BF_ReadByte( msg ); else pitch = PITCH_NORM; if( flags & SND_SENTENCE ) { char sentenceName[32]; Q_snprintf( sentenceName, sizeof( sentenceName ), "!%i", sound ); handle = S_RegisterSound( sentenceName ); } else handle = cl.sound_index[sound]; // see precached sound // entity reletive entnum = BF_ReadWord( msg ); // positioned in space BF_ReadVec3Coord( msg, pos ); wordIndex = BF_ReadByte( msg ); // 16 bytes here BF_ReadBytes( msg, &samplePos, sizeof( samplePos )); BF_ReadBytes( msg, &forcedEnd, sizeof( forcedEnd )); S_RestoreSound( pos, entnum, chan, handle, volume, attn, pitch, flags, samplePos, forcedEnd, wordIndex ); }
/* ================== CL_ParseBaseline ================== */ void CL_ParseBaseline( sizebuf_t *msg ) { int newnum; float timebase; cl_entity_t *ent; Delta_InitClient (); // finalize client delta's newnum = BF_ReadWord( msg ); if( newnum < 0 ) Host_Error( "CL_SpawnEdict: invalid number %i\n", newnum ); if( newnum >= clgame.maxEntities ) Host_Error( "CL_AllocEdict: no free edicts\n" ); ent = CL_EDICT_NUM( newnum ); if( !ent ) Host_Error( "CL_ParseBaseline: got invalid entity"); Q_memset( &ent->prevstate, 0, sizeof( ent->prevstate )); ent->index = newnum; if( cls.state == ca_active ) timebase = cl.mtime[0]; else timebase = 1.0f; // sv.state == ss_loading MSG_ReadDeltaEntity( msg, &ent->prevstate, &ent->baseline, newnum, CL_IsPlayerIndex( newnum ), timebase ); }
/* ============= CL_ParseReliableEvent ============= */ void CL_ParseReliableEvent( sizebuf_t *msg ) { int event_index; event_args_t nullargs, args; float delay = 0.0f; cl_entity_t *pEnt; Q_memset( &nullargs, 0, sizeof( nullargs )); event_index = BF_ReadUBitLong( msg, MAX_EVENT_BITS ); if( BF_ReadOneBit( msg )) delay = (float)BF_ReadWord( msg ) * (1.0f / 100.0f); // reliable events not use delta-compression just null-compression MSG_ReadDeltaEvent( msg, &nullargs, &args ); if(( pEnt = CL_GetEntityByIndex( args.entindex )) != NULL ) { if( VectorIsNull( args.origin )) VectorCopy( pEnt->curstate.origin, args.origin ); if( VectorIsNull( args.angles )) VectorCopy( pEnt->curstate.angles, args.angles ); if( VectorIsNull( args.velocity )) VectorCopy( pEnt->curstate.velocity, args.velocity ); } CL_QueueEvent( FEV_RELIABLE|FEV_SERVER, event_index, delay, &args ); }
/* ================== CL_ParseStaticDecal ================== */ void CL_ParseStaticDecal( sizebuf_t *msg ) { vec3_t origin; int decalIndex, entityIndex, modelIndex; int flags; BF_ReadBitVec3Coord( msg, origin ); decalIndex = BF_ReadWord( msg ); entityIndex = BF_ReadShort( msg ); if( entityIndex > 0 ) modelIndex = BF_ReadWord( msg ); else modelIndex = 0; flags = BF_ReadByte( msg ); CL_DecalShoot( CL_DecalIndex( decalIndex ), entityIndex, modelIndex, origin, flags ); }
/* ============== CL_ParseResourceList ============== */ void CL_ParseResourceList( sizebuf_t *msg ) { int i = 0; Q_memset( &reslist, 0, sizeof( resourcelist_t )); reslist.rescount = BF_ReadWord( msg ) - 1; for( i = 0; i < reslist.rescount; i++ ) { reslist.restype[i] = BF_ReadWord( msg ); Q_strncpy( reslist.resnames[i], BF_ReadString( msg ), CS_SIZE ); } cls.downloadcount = 0; HTTP_ResetProcessState(); for( i = 0; i < reslist.rescount; i++ ) { // skip some types #if 0 if( reslist.restype[i] == t_model && !Q_strchr( download_types->latched_string, 'm' ) ) continue; if( reslist.restype[i] == t_sound && !Q_strchr( download_types->latched_string, 's' ) ) continue; if( reslist.restype[i] == t_eventscript && !Q_strchr( download_types->latched_string, 'e' ) ) continue; if( reslist.restype[i] == t_generic && !Q_strchr( download_types->latched_string, 'c' ) ) continue; #endif if( reslist.restype[i] == t_sound ) CL_CheckingSoundResFile( reslist.resnames[i] ); else CL_CheckingResFile( reslist.resnames[i] ); } if( !cls.downloadcount ) { BF_WriteByte( &cls.netchan.message, clc_stringcmd ); BF_WriteString( &cls.netchan.message, "continueloading" ); } }
/* ================== CL_ParseStaticDecal ================== */ void CL_ParseStaticDecal( sizebuf_t *msg ) { vec3_t origin; int decalIndex, entityIndex, modelIndex; cl_entity_t *ent = NULL; float scale; int flags; BF_ReadVec3Coord( msg, origin ); decalIndex = BF_ReadWord( msg ); entityIndex = BF_ReadShort( msg ); if( entityIndex > 0 ) modelIndex = BF_ReadWord( msg ); else modelIndex = 0; flags = BF_ReadByte( msg ); scale = (float)BF_ReadWord( msg ) / 4096.0f; CL_FireCustomDecal( CL_DecalIndex( decalIndex ), entityIndex, modelIndex, origin, flags, scale ); }
/* ============== CL_ParseStudioDecal Studio Decal message. Used by engine in case we need save\restore decals ============== */ void CL_ParseStudioDecal( sizebuf_t *msg ) { modelstate_t state; vec3_t start, pos; int decalIndex, entityIndex; int modelIndex = 0; int flags; BF_ReadVec3Coord( msg, pos ); BF_ReadVec3Coord( msg, start ); decalIndex = BF_ReadWord( msg ); entityIndex = BF_ReadWord( msg ); flags = BF_ReadByte( msg ); state.sequence = BF_ReadShort( msg ); state.frame = BF_ReadShort( msg ); state.blending[0] = BF_ReadByte( msg ); state.blending[1] = BF_ReadByte( msg ); state.controller[0] = BF_ReadByte( msg ); state.controller[1] = BF_ReadByte( msg ); state.controller[2] = BF_ReadByte( msg ); state.controller[3] = BF_ReadByte( msg ); modelIndex = BF_ReadWord( msg ); state.body = BF_ReadByte( msg ); state.skin = BF_ReadByte( msg ); if( clgame.drawFuncs.R_StudioDecalShoot != NULL ) { int decalTexture = CL_DecalIndex( decalIndex ); cl_entity_t *ent = CL_GetEntityByIndex( entityIndex ); if( ent && !ent->model && modelIndex != 0 ) ent->model = Mod_Handle( modelIndex ); clgame.drawFuncs.R_StudioDecalShoot( decalTexture, ent, start, pos, flags, &state ); } }
/* ================= CL_FlushEntityPacket ================= */ void CL_FlushEntityPacket( sizebuf_t *msg ) { int newnum; entity_state_t from, to; MsgDev( D_INFO, "FlushEntityPacket()\n" ); Q_memset( &from, 0, sizeof( from )); cl.frames[cl.parsecountmod].valid = false; cl.validsequence = 0; // can't render a frame // read it all, but ignore it while( 1 ) { newnum = BF_ReadWord( msg ); if( !newnum ) break; // done if( BF_CheckOverflow( msg )) Host_Error( "CL_FlushEntityPacket: read overflow\n" ); MSG_ReadDeltaEntity( msg, &from, &to, newnum, CL_IsPlayerIndex( newnum ), cl.mtime[0] ); } }
/* ================== CL_ParsePacketEntities An svc_packetentities has just been parsed, deal with the rest of the data stream. ================== */ void CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta ) { frame_t *newframe, *oldframe; int oldindex, newnum, oldnum; int oldpacket, newpacket; cl_entity_t *player; entity_state_t *oldent; int i, count; // save first uncompressed packet as timestamp if( cls.changelevel && !delta && cls.demorecording ) CL_WriteDemoJumpTime(); // first, allocate packet for new frame count = BF_ReadWord( msg ); newpacket = cl.parsecountmod; newframe = &cl.frames[newpacket]; // allocate parse entities newframe->first_entity = cls.next_client_entities; newframe->num_entities = 0; newframe->valid = true; // assume valid if( delta ) { int subtracted; oldpacket = BF_ReadByte( msg ); subtracted = ((( cls.netchan.incoming_sequence & 0xFF ) - oldpacket ) & 0xFF ); if( subtracted == 0 ) { Host_Error( "CL_DeltaPacketEntities: update too old, connection dropped.\n" ); return; } if( subtracted >= CL_UPDATE_MASK ) { // we can't use this, it is too old Con_NPrintf( 2, "^3Warning:^1 delta frame is too old^7\n" ); CL_FlushEntityPacket( msg ); return; } oldframe = &cl.frames[oldpacket & CL_UPDATE_MASK]; if(( cls.next_client_entities - oldframe->first_entity ) > ( cls.num_client_entities - 128 )) { Con_NPrintf( 2, "^3Warning:^1 delta frame is too old^7\n" ); CL_FlushEntityPacket( msg ); return; } } else { // this is a full update that we can start delta compressing from now oldframe = NULL; oldpacket = -1; // delta too old or is initial message cl.force_send_usercmd = true; // send reply cls.demowaiting = false; // we can start recording now } // mark current delta state cl.validsequence = cls.netchan.incoming_sequence; oldent = NULL; oldindex = 0; if( !oldframe ) { oldnum = MAX_ENTNUMBER; } else { if( oldindex >= oldframe->num_entities ) { oldnum = MAX_ENTNUMBER; } else { oldent = &cls.packet_entities[(oldframe->first_entity+oldindex) % cls.num_client_entities]; oldnum = oldent->number; } } while( 1 ) { newnum = BF_ReadWord( msg ); if( !newnum ) break; // end of packet entities if( BF_CheckOverflow( msg )) Host_Error( "CL_ParsePacketEntities: read overflow\n" ); while( oldnum < newnum ) { // one or more entities from the old packet are unchanged CL_DeltaEntity( msg, newframe, oldnum, oldent, true ); oldindex++; if( oldindex >= oldframe->num_entities ) { oldnum = MAX_ENTNUMBER; } else { oldent = &cls.packet_entities[(oldframe->first_entity+oldindex) % cls.num_client_entities]; oldnum = oldent->number; } } if( oldnum == newnum ) { // delta from previous state CL_DeltaEntity( msg, newframe, newnum, oldent, false ); oldindex++; if( oldindex >= oldframe->num_entities ) { oldnum = MAX_ENTNUMBER; } else { oldent = &cls.packet_entities[(oldframe->first_entity+oldindex) % cls.num_client_entities]; oldnum = oldent->number; } continue; } if( oldnum > newnum ) { // delta from baseline ? CL_DeltaEntity( msg, newframe, newnum, NULL, false ); continue; } } // any remaining entities in the old frame are copied over while( oldnum != MAX_ENTNUMBER ) { // one or more entities from the old packet are unchanged CL_DeltaEntity( msg, newframe, oldnum, oldent, true ); oldindex++; if( oldindex >= oldframe->num_entities ) { oldnum = MAX_ENTNUMBER; } else { oldent = &cls.packet_entities[(oldframe->first_entity+oldindex) % cls.num_client_entities]; oldnum = oldent->number; } } cl.frame = *newframe; if( !cl.frame.valid ) return; player = CL_GetLocalPlayer(); if( cls.state != ca_active ) { // client entered the game cls.state = ca_active; cl.force_refdef = true; cls.changelevel = false; // changelevel is done cls.changedemo = false; // changedemo is done SCR_MakeLevelShot(); // make levelshot if needs Cvar_SetFloat( "scr_loading", 0.0f ); // reset progress bar if(( cls.demoplayback || cls.disable_servercount != cl.servercount ) && cl.video_prepped ) SCR_EndLoadingPlaque(); // get rid of loading plaque } // update local player states clgame.dllFuncs.pfnTxferLocalOverrides( &player->curstate, &newframe->local.client ); // update state for all players for( i = 0; i < cl.maxclients; i++ ) { cl_entity_t *ent = CL_GetEntityByIndex( i + 1 ); if( !ent ) continue; clgame.dllFuncs.pfnProcessPlayerState( &newframe->playerstate[i], &ent->curstate ); newframe->playerstate[i].number = ent->index; } cl.frame = *newframe; }
/* ================== CL_ParseServerData ================== */ void CL_ParseServerData( sizebuf_t *msg ) { string gamefolder; qboolean background; int i; MsgDev( D_NOTE, "Serverdata packet received.\n" ); cls.demowaiting = false; // server is changed clgame.load_sequence++; // now all hud sprites are invalid // wipe the client_t struct if( !cls.changelevel && !cls.changedemo ) CL_ClearState (); cls.state = ca_connected; // parse protocol version number i = BF_ReadLong( msg ); cls.serverProtocol = i; if( i != PROTOCOL_VERSION ) Host_Error( "Server uses invalid protocol (%i should be %i)\n", i, PROTOCOL_VERSION ); cl.servercount = BF_ReadLong( msg ); cl.checksum = BF_ReadLong( msg ); cl.playernum = BF_ReadByte( msg ); cl.maxclients = BF_ReadByte( msg ); clgame.maxEntities = BF_ReadWord( msg ); clgame.maxEntities = bound( 600, clgame.maxEntities, 4096 ); Q_strncpy( clgame.mapname, BF_ReadString( msg ), MAX_STRING ); Q_strncpy( clgame.maptitle, BF_ReadString( msg ), MAX_STRING ); background = BF_ReadOneBit( msg ); Q_strncpy( gamefolder, BF_ReadString( msg ), MAX_STRING ); host.features = (uint)BF_ReadLong( msg ); if( cl.maxclients > 1 && host.developer < 1 ) host.developer++; // set the background state if( cls.demoplayback && ( cls.demonum != -1 )) { // re-init mouse host.mouse_visible = false; cl.background = true; } else cl.background = background; if( cl.background ) // tell the game parts about background state Cvar_FullSet( "cl_background", "1", CVAR_READ_ONLY ); else Cvar_FullSet( "cl_background", "0", CVAR_READ_ONLY ); if( !cls.changelevel ) { // continue playing if we are changing level S_StopBackgroundTrack (); } #if 0 // NOTE: this is not tested as well. Use with precaution CL_ChangeGame( gamefolder, false ); #endif if( !cls.changedemo ) UI_SetActiveMenu( cl.background ); cl.refdef.viewentity = cl.playernum + 1; // always keep viewent an actual menu.globals->maxClients = cl.maxclients; Q_strncpy( menu.globals->maptitle, clgame.maptitle, sizeof( menu.globals->maptitle )); if( cl.maxclients > 1 && r_decals->value > mp_decals->value ) Cvar_SetFloat( "r_decals", mp_decals->value ); if( !cls.changelevel && !cls.changedemo ) CL_InitEdicts (); // re-arrange edicts // get splash name if( cls.demoplayback && ( cls.demonum != -1 )) Cvar_Set( "cl_levelshot_name", va( "levelshots/%s_%s", cls.demoname, glState.wideScreen ? "16x9" : "4x3" )); else Cvar_Set( "cl_levelshot_name", va( "levelshots/%s_%s", clgame.mapname, glState.wideScreen ? "16x9" : "4x3" )); Cvar_SetFloat( "scr_loading", 0.0f ); // reset progress bar if(( cl_allow_levelshots->integer && !cls.changelevel ) || cl.background ) { if( !FS_FileExists( va( "%s.bmp", cl_levelshot_name->string ), true )) Cvar_Set( "cl_levelshot_name", "*black" ); // render a black screen cls.scrshot_request = scrshot_plaque; // request levelshot even if exist (check filetime) } if( scr_dark->integer ) { screenfade_t *sf = &clgame.fade; client_textmessage_t *title; title = CL_TextMessageGet( "GAMETITLE" ); if( title ) { // get settings from titles.txt sf->fadeEnd = title->holdtime + title->fadeout; sf->fadeReset = title->fadeout; } else sf->fadeEnd = sf->fadeReset = 4.0f; sf->fadeFlags = FFADE_IN; sf->fader = sf->fadeg = sf->fadeb = 0; sf->fadealpha = 255; sf->fadeSpeed = (float)sf->fadealpha / sf->fadeReset; sf->fadeReset += cl.time; sf->fadeEnd += sf->fadeReset; Cvar_SetFloat( "v_dark", 0.0f ); } // need to prep refresh at next oportunity cl.video_prepped = false; cl.audio_prepped = false; Q_memset( &clgame.movevars, 0, sizeof( clgame.movevars )); Q_memset( &clgame.oldmovevars, 0, sizeof( clgame.oldmovevars )); }
/* ================== CL_ParseStaticEntity static client entity ================== */ void CL_ParseStaticEntity( sizebuf_t *msg ) { entity_state_t state; cl_entity_t *ent; int i; Q_memset( &state, 0, sizeof( state )); state.modelindex = BF_ReadShort( msg ); state.sequence = BF_ReadByte( msg ); state.frame = BF_ReadByte( msg ); state.colormap = BF_ReadWord( msg ); state.skin = BF_ReadByte( msg ); for( i = 0; i < 3; i++ ) { state.origin[i] = BF_ReadCoord( msg ); state.angles[i] = BF_ReadBitAngle( msg, 16 ); } state.rendermode = BF_ReadByte( msg ); if( state.rendermode != kRenderNormal ) { state.renderamt = BF_ReadByte( msg ); state.rendercolor.r = BF_ReadByte( msg ); state.rendercolor.g = BF_ReadByte( msg ); state.rendercolor.b = BF_ReadByte( msg ); state.renderfx = BF_ReadByte( msg ); } i = clgame.numStatics; if( i >= MAX_STATIC_ENTITIES ) { MsgDev( D_ERROR, "CL_ParseStaticEntity: static entities limit exceeded!\n" ); return; } ent = &clgame.static_entities[i]; clgame.numStatics++; ent->index = 0; // ??? ent->baseline = state; ent->curstate = state; ent->prevstate = state; // statics may be respawned in game e.g. for demo recording if( cls.state == ca_connected ) ent->trivial_accept = INVALID_HANDLE; // setup the new static entity CL_UpdateEntityFields( ent ); if( Mod_GetType( state.modelindex ) == mod_studio ) { CL_UpdateStudioVars( ent, &state, true ); // animate studio model ent->curstate.animtime = cl.time; ent->curstate.framerate = 1.0f; ent->latched.prevframe = 0.0f; } R_AddEfrags( ent ); // add link }
/* ===================== CL_ParseServerMessage ===================== */ void CL_ParseServerMessage( sizebuf_t *msg ) { char *s; int i, j, cmd; int param1, param2; int bufStart; cls_message_debug.parsing = true; // begin parsing starting_count = BF_GetNumBytesRead( msg ); // updates each frame // parse the message while( 1 ) { if( BF_CheckOverflow( msg )) { Host_Error( "CL_ParseServerMessage: overflow!\n" ); return; } // mark start position bufStart = BF_GetNumBytesRead( msg ); // end of message if( BF_GetNumBitsLeft( msg ) < 8 ) break; cmd = BF_ReadByte( msg ); // record command for debugging spew on parse problem CL_Parse_RecordCommand( cmd, bufStart ); // other commands switch( cmd ) { case svc_bad: Host_Error( "svc_bad\n" ); break; case svc_nop: // this does nothing break; case svc_disconnect: MsgDev( D_INFO, "Disconnected from server\n" ); CL_Drop (); Host_AbortCurrentFrame (); break; case svc_changing: if( BF_ReadOneBit( msg )) { cls.changelevel = true; S_StopAllSounds(); if( cls.demoplayback ) { SCR_BeginLoadingPlaque( cl.background ); cls.changedemo = true; } } else MsgDev( D_INFO, "Server disconnected, reconnecting\n" ); CL_ClearState (); CL_InitEdicts (); // re-arrange edicts if( cls.demoplayback ) { cl.background = (cls.demonum != -1) ? true : false; cls.state = ca_connected; } else cls.state = ca_connecting; cls.connect_time = MAX_HEARTBEAT; // CL_CheckForResend() will fire immediately break; case svc_setview: cl.refdef.viewentity = BF_ReadWord( msg ); break; case svc_sound: CL_ParseSoundPacket( msg, false ); break; case svc_time: // shuffle timestamps cl.mtime[1] = cl.mtime[0]; cl.mtime[0] = BF_ReadFloat( msg ); break; case svc_print: i = BF_ReadByte( msg ); MsgDev( D_INFO, "^6%s", BF_ReadString( msg )); if( i == PRINT_CHAT ) S_StartLocalSound( "common/menu2.wav", VOL_NORM, false ); break; case svc_stufftext: CL_ParseStuffText( msg ); break; case svc_lightstyle: CL_ParseLightStyle( msg ); break; case svc_setangle: CL_ParseSetAngle( msg ); break; case svc_serverdata: Cbuf_Execute(); // make sure any stuffed commands are done CL_ParseServerData( msg ); break; case svc_addangle: CL_ParseAddAngle( msg ); break; case svc_clientdata: CL_ParseClientData( msg ); break; case svc_packetentities: CL_ParsePacketEntities( msg, false ); break; case svc_deltapacketentities: CL_ParsePacketEntities( msg, true ); break; case svc_updatepings: CL_UpdateUserPings( msg ); break; case svc_usermessage: CL_RegisterUserMessage( msg ); break; case svc_particle: CL_ParseParticles( msg ); break; case svc_restoresound: CL_ParseRestoreSoundPacket( msg ); break; case svc_spawnstatic: CL_ParseStaticEntity( msg ); break; case svc_ambientsound: CL_ParseSoundPacket( msg, true ); break; case svc_crosshairangle: CL_ParseCrosshairAngle( msg ); break; case svc_spawnbaseline: CL_ParseBaseline( msg ); break; case svc_temp_entity: CL_ParseTempEntity( msg ); break; case svc_setpause: cl.refdef.paused = ( BF_ReadOneBit( msg ) != 0 ); break; case svc_deltamovevars: CL_ParseMovevars( msg ); break; case svc_customization: CL_ParseCustomization( msg ); break; case svc_centerprint: CL_CenterPrint( BF_ReadString( msg ), 0.25f ); break; case svc_event: CL_ParseEvent( msg ); break; case svc_event_reliable: CL_ParseReliableEvent( msg ); break; case svc_updateuserinfo: CL_UpdateUserinfo( msg ); break; case svc_intermission: cl.refdef.intermission = true; break; case svc_modelindex: CL_PrecacheModel( msg ); break; case svc_soundindex: CL_PrecacheSound( msg ); break; case svc_soundfade: CL_ParseSoundFade( msg ); break; case svc_cdtrack: param1 = BF_ReadByte( msg ); param1 = bound( 1, param1, MAX_CDTRACKS ); // tracknum param2 = BF_ReadByte( msg ); param2 = bound( 1, param2, MAX_CDTRACKS ); // loopnum S_StartBackgroundTrack( clgame.cdtracks[param1-1], clgame.cdtracks[param2-1], 0 ); break; case svc_serverinfo: CL_ServerInfo( msg ); break; case svc_eventindex: CL_PrecacheEvent( msg ); break; case svc_deltatable: Delta_ParseTableField( msg ); break; case svc_weaponanim: param1 = BF_ReadByte( msg ); // iAnim param2 = BF_ReadByte( msg ); // body CL_WeaponAnim( param1, param2 ); break; case svc_bspdecal: CL_ParseStaticDecal( msg ); break; case svc_roomtype: param1 = BF_ReadShort( msg ); Cvar_SetFloat( "room_type", param1 ); break; case svc_chokecount: i = BF_ReadByte( msg ); j = cls.netchan.incoming_acknowledged - 1; for( ; i > 0 && j > cls.netchan.outgoing_sequence - CL_UPDATE_BACKUP; j-- ) { if( cl.frames[j & CL_UPDATE_MASK].receivedtime != -3.0 ) { cl.frames[j & CL_UPDATE_MASK].receivedtime = -2.0; i--; } } break; case svc_resourcelist: CL_ParseResourceList( msg ); break; case svc_director: CL_ParseDirector( msg ); break; case svc_studiodecal: CL_ParseStudioDecal( msg ); break; case svc_querycvarvalue: CL_ParseCvarValue( msg ); break; case svc_querycvarvalue2: CL_ParseCvarValue2( msg ); break; default: CL_ParseUserMessage( msg, cmd ); break; } } cls_message_debug.parsing = false; // done // we don't know if it is ok to save a demo message until // after we have parsed the frame if( !cls.demoplayback ) { if( cls.demorecording && !cls.demowaiting ) { CL_WriteDemoMessage( false, starting_count, msg ); } else if( cls.state != ca_active ) { CL_WriteDemoMessage( true, starting_count, msg ); } } }
/* ============= CL_ParseEvent ============= */ void CL_ParseEvent( sizebuf_t *msg ) { int event_index; int i, num_events; int packet_ent; event_args_t nullargs, args; qboolean has_update; entity_state_t *state; cl_entity_t *pEnt; float delay; Q_memset( &nullargs, 0, sizeof( nullargs )); num_events = BF_ReadUBitLong( msg, 5 ); // parse events queue for( i = 0 ; i < num_events; i++ ) { event_index = BF_ReadUBitLong( msg, MAX_EVENT_BITS ); Q_memset( &args, 0, sizeof( args )); has_update = false; if( BF_ReadOneBit( msg )) { packet_ent = BF_ReadUBitLong( msg, MAX_ENTITY_BITS ); if( BF_ReadOneBit( msg )) { MSG_ReadDeltaEvent( msg, &nullargs, &args ); has_update = true; } } else packet_ent = -1; if( packet_ent != -1 ) state = &cls.packet_entities[(cl.frame.first_entity+packet_ent)%cls.num_client_entities]; else state = NULL; // it's a client. Override some params if( args.entindex >= 1 && args.entindex <= cl.maxclients ) { if(( args.entindex - 1 ) == cl.playernum ) { if( state && !CL_IsPredicted( )) { // restore viewangles from angles args.angles[PITCH] = -state->angles[PITCH] * 3; args.angles[YAW] = state->angles[YAW]; args.angles[ROLL] = 0; // no roll } else { // get the predicted angles VectorCopy( cl.refdef.cl_viewangles, args.angles ); } VectorCopy( cl.frame.local.client.origin, args.origin ); VectorCopy( cl.frame.local.client.velocity, args.velocity ); } else if( state ) { // restore viewangles from angles args.angles[PITCH] = -state->angles[PITCH] * 3; args.angles[YAW] = state->angles[YAW]; args.angles[ROLL] = 0; // no roll // if we restore origin and velocity everytime, why don't do it here also? if( VectorIsNull( args.origin )) VectorCopy( state->origin, args.origin ); if( VectorIsNull( args.velocity )) VectorCopy( state->velocity, args.velocity ); } } else if( state ) { if( VectorIsNull( args.origin )) VectorCopy( state->origin, args.origin ); if( VectorIsNull( args.angles )) VectorCopy( state->angles, args.angles ); if( VectorIsNull( args.velocity )) VectorCopy( state->velocity, args.velocity ); } else if(( pEnt = CL_GetEntityByIndex( args.entindex )) != NULL ) { if( VectorIsNull( args.origin )) VectorCopy( pEnt->curstate.origin, args.origin ); if( VectorIsNull( args.angles )) VectorCopy( pEnt->curstate.angles, args.angles ); if( VectorIsNull( args.velocity )) VectorCopy( pEnt->curstate.velocity, args.velocity ); } if( BF_ReadOneBit( msg )) delay = (float)BF_ReadWord( msg ) * (1.0f / 100.0f); else delay = 0.0f; // g-cont. should we need find the event with same index? CL_QueueEvent( 0, event_index, delay, &args ); } }