void Delta_ParseTableField( sizebuf_t *msg ) { int tableIndex, nameIndex; float mul = 1.0f, post_mul = 1.0f; int flags, bits; const char *pName; delta_info_t *dt; tableIndex = BF_ReadUBitLong( msg, 4 ); dt = Delta_FindStructByIndex( tableIndex ); if( !dt ) Host_Error( "Delta_ParseTableField: not initialized" ); nameIndex = BF_ReadUBitLong( msg, 8 ); // read field name index if( !( nameIndex >= 0 && nameIndex < dt->maxFields ) ) Host_Error( "Delta_ParseTableField: wrong nameIndex" ); pName = dt->pInfo[nameIndex].name; flags = BF_ReadUBitLong( msg, 10 ); bits = BF_ReadUBitLong( msg, 5 ) + 1; // read the multipliers if( BF_ReadOneBit( msg )) mul = BF_ReadFloat( msg ); if( BF_ReadOneBit( msg )) post_mul = BF_ReadFloat( msg ); // delta encoders it's already initialized on this machine (local game) if( delta_init ) return; // add field to table Delta_AddField( dt->pName, pName, flags, bits, mul, post_mul ); }
/* ================ CL_UpdateUserinfo collect userinfo from all players ================ */ void CL_UpdateUserinfo( sizebuf_t *msg ) { int slot; qboolean active; player_info_t *player; slot = BF_ReadUBitLong( msg, MAX_CLIENT_BITS ); if( slot >= MAX_CLIENTS ) Host_Error( "CL_ParseServerMessage: svc_updateuserinfo > MAX_CLIENTS\n" ); player = &cl.players[slot]; active = BF_ReadOneBit( msg ) ? true : false; if( active ) { Q_strncpy( player->userinfo, BF_ReadString( msg ), sizeof( player->userinfo )); Q_strncpy( player->name, Info_ValueForKey( player->userinfo, "name" ), sizeof( player->name )); Q_strncpy( player->model, Info_ValueForKey( player->userinfo, "model" ), sizeof( player->model )); cl.playermodels[slot] = 0; player->topcolor = Q_atoi( Info_ValueForKey( player->userinfo, "topcolor" )); player->bottomcolor = Q_atoi( Info_ValueForKey( player->userinfo, "bottomcolor" )); if( slot == cl.playernum ) Q_memcpy( &menu.playerinfo, player, sizeof( player_info_t )); } else Q_memset( player, 0, sizeof( *player )); }
/* ============= 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_UpdateUserPings collect pings and packet lossage from clients ================ */ void CL_UpdateUserPings( sizebuf_t *msg ) { int i, slot; player_info_t *player; for( i = 0; i < MAX_CLIENTS; i++ ) { if( !BF_ReadOneBit( msg )) break; // end of message slot = BF_ReadUBitLong( msg, MAX_CLIENT_BITS ); if( slot >= MAX_CLIENTS ) Host_Error( "CL_ParseServerMessage: svc_updatepings > MAX_CLIENTS\n" ); player = &cl.players[slot]; player->ping = BF_ReadUBitLong( msg, 12 ); player->packet_loss = BF_ReadUBitLong( msg, 7 ); } }
/* ================ CL_PrecacheEvent prceache event from server ================ */ void CL_PrecacheEvent( sizebuf_t *msg ) { int eventIndex; eventIndex = BF_ReadUBitLong( msg, MAX_EVENT_BITS ); if( eventIndex < 0 || eventIndex >= MAX_EVENTS ) Host_Error( "CL_PrecacheEvent: bad eventindex %i\n", eventIndex ); Q_strncpy( cl.event_precache[eventIndex], BF_ReadString( msg ), sizeof( cl.event_precache[0] )); // can be set now CL_SetEventIndex( cl.event_precache[eventIndex], eventIndex ); }
/* ================ CL_PrecacheSound prceache sound from server ================ */ void CL_PrecacheSound( sizebuf_t *msg ) { int soundIndex; soundIndex = BF_ReadUBitLong( msg, MAX_SOUND_BITS ); if( soundIndex < 0 || soundIndex >= MAX_SOUNDS ) Host_Error( "CL_PrecacheSound: bad soundindex %i\n", soundIndex ); Q_strncpy( cl.sound_precache[soundIndex], BF_ReadString( msg ), sizeof( cl.sound_precache[0] )); // when we loading map all resources is precached sequentially if( !cl.audio_prepped ) return; cl.sound_index[soundIndex] = S_RegisterSound( cl.sound_precache[soundIndex] ); }
/* ================ CL_PrecacheModel prceache model from server ================ */ void CL_PrecacheModel( sizebuf_t *msg ) { int modelIndex; modelIndex = BF_ReadUBitLong( msg, MAX_MODEL_BITS ); if( modelIndex < 0 || modelIndex >= MAX_MODELS ) Host_Error( "CL_PrecacheModel: bad modelindex %i\n", modelIndex ); Q_strncpy( cl.model_precache[modelIndex], BF_ReadString( msg ), sizeof( cl.model_precache[0] )); // when we loading map all resources is precached sequentially if( !cl.video_prepped ) return; Mod_RegisterModel( cl.model_precache[modelIndex], modelIndex ); }
/* =================== CL_ParseClientData =================== */ void CL_ParseClientData( sizebuf_t *msg ) { int i, j; clientdata_t *from_cd, *to_cd; weapon_data_t *from_wd, *to_wd; weapon_data_t nullwd[32]; clientdata_t nullcd; frame_t *frame; int idx; // this is the frame update that this message corresponds to i = cls.netchan.incoming_sequence; // did we drop some frames? if( i > cl.last_incoming_sequence + 1 ) { // mark as dropped for( j = cl.last_incoming_sequence + 1; j < i; j++ ) { if( cl.frames[j & CL_UPDATE_MASK].receivedtime >= 0.0 ) { cl.frames[j & CL_UPDATE_MASK].receivedtime = -1; cl.frames[j & CL_UPDATE_MASK].latency = 0; } } } cl.parsecount = i; // ack'd incoming messages. cl.parsecountmod = cl.parsecount & CL_UPDATE_MASK; // index into window. frame = &cl.frames[cl.parsecountmod]; // frame at index. frame->time = cl.mtime[0]; // mark network received time frame->receivedtime = host.realtime; // time now that we are parsing. if( cl.last_command_ack != -1 ) { int last_predicted; entity_state_t * ps; entity_state_t * pps; clientdata_t * pcd; clientdata_t * ppcd; weapon_data_t * wd; weapon_data_t * pwd; last_predicted = ( cl.last_incoming_sequence + ( cls.netchan.incoming_acknowledged - cl.last_command_ack)) & CL_UPDATE_MASK; pps = &cl.predict[last_predicted].playerstate; ppcd = &cl.predict[last_predicted].client; pwd = cl.predict[last_predicted].weapondata; ps = &frame->playerstate[cl.playernum]; pcd = &frame->local.client; wd = frame->local.weapondata; clgame.dllFuncs.pfnTxferPredictionData( ps, pps, pcd, ppcd, wd, pwd ); } // do this after all packets read for this frame? cl.last_command_ack = cls.netchan.incoming_acknowledged; cl.last_incoming_sequence = cls.netchan.incoming_sequence; if( hltv->integer ) return; // clientdata for spectators ends here to_cd = &frame->local.client; to_wd = frame->local.weapondata; // clear to old value before delta parsing if( !BF_ReadOneBit( msg )) { Q_memset( &nullcd, 0, sizeof( nullcd )); Q_memset( nullwd, 0, sizeof( nullwd )); from_cd = &nullcd; from_wd = nullwd; } else { int delta_sequence = BF_ReadByte( msg ); from_cd = &cl.frames[delta_sequence & CL_UPDATE_MASK].local.client; from_wd = cl.frames[delta_sequence & CL_UPDATE_MASK].local.weapondata; } MSG_ReadClientData( msg, from_cd, to_cd, cl.mtime[0] ); for( i = 0; i < MAX_WEAPONS; i++ ) { // check for end of weapondata (and clientdata_t message) if( !BF_ReadOneBit( msg )) break; // read the weapon idx idx = BF_ReadUBitLong( msg, MAX_WEAPON_BITS ); MSG_ReadWeaponData( msg, &from_wd[idx], &to_wd[idx], cl.mtime[0] ); } }
/* ============= 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 ); } }
/* ================== MSG_ReadDeltaEntity The entity number has already been read from the message, which is how the from state is identified. If the delta removes the entity, entity_state_t->number will be set to MAX_EDICTS Can go from either a baseline or a previous packet_entity ================== */ qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state_t *to, int number, qboolean player, float timebase ) { delta_info_t *dt = NULL; delta_t *pField; int i, fRemoveType; #ifndef XASH_DEDICATED if( number < 0 || number >= clgame.maxEntities ) { // broken packet, try to skip it MsgDev( D_ERROR, "MSG_ReadDeltaEntity: bad delta entity number: %i\n", number ); return false; } *to = *from; to->number = number; fRemoveType = BF_ReadUBitLong( msg, 2 ); if( fRemoveType ) { // check for a remove Q_memset( to, 0, sizeof( *to )); if( fRemoveType & 1 ) { // removed from delta-message return false; } if( fRemoveType & 2 ) { // entity was removed from server to->number = -1; return false; } MsgDev( D_ERROR, "MSG_ReadDeltaEntity: unknown update type %i\n", fRemoveType ); return false; } if( BF_ReadOneBit( msg )) to->entityType = BF_ReadUBitLong( msg, 2 ); if( to->entityType == ENTITY_BEAM ) { dt = Delta_FindStruct( "custom_entity_state_t" ); } else // ENTITY_NORMAL or other (try predict type) { /* Omit connection drop on wromg data from server. * I know that it is very dirty, * but i don't know how to do it better.*/ if( to->entityType != ENTITY_NORMAL ) MsgDev( D_NOTE, "MSG_ReadDeltaEntity: broken delta: entityType = %d\n", to->entityType ); if( player ) { dt = Delta_FindStruct( "entity_state_player_t" ); } else { dt = Delta_FindStruct( "entity_state_t" ); } } if( !(dt && dt->bInitialized) ) // Broken delta? { MsgDev( D_ERROR, "MSG_ReadDeltaEntity: broken delta\n"); return true; } pField = dt->pFields; ASSERT( pField ); // process fields for( i = 0; i < dt->numFields; i++, pField++ ) { Delta_ReadField( msg, pField, from, to, timebase ); } #endif // message parsed return true; }
/* ================== MSG_ReadDeltaEntity The entity number has already been read from the message, which is how the from state is identified. If the delta removes the entity, entity_state_t->number will be set to MAX_EDICTS Can go from either a baseline or a previous packet_entity ================== */ qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state_t *to, int number, qboolean player, float timebase ) { delta_info_t *dt = NULL; delta_t *pField; int i, fRemoveType; #ifndef _DEDICATED if( number < 0 || number >= clgame.maxEntities ) Host_Error( "MSG_ReadDeltaEntity: bad delta entity number: %i\n", number ); #endif *to = *from; to->number = number; fRemoveType = BF_ReadUBitLong( msg, 2 ); if( fRemoveType ) { // check for a remove Q_memset( to, 0, sizeof( *to )); if( fRemoveType & 1 ) { // removed from delta-message return false; } if( fRemoveType & 2 ) { // entity was removed from server to->number = -1; return false; } Host_Error( "MSG_ReadDeltaEntity: unknown update type %i\n", fRemoveType ); } if( BF_ReadOneBit( msg )) to->entityType = BF_ReadUBitLong( msg, 2 ); if( to->entityType == ENTITY_NORMAL ) { if( player ) { dt = Delta_FindStruct( "entity_state_player_t" ); } else { dt = Delta_FindStruct( "entity_state_t" ); } } else if( to->entityType == ENTITY_BEAM ) { dt = Delta_FindStruct( "custom_entity_state_t" ); } ASSERT( dt && dt->bInitialized ); pField = dt->pFields; ASSERT( pField ); // process fields for( i = 0; i < dt->numFields; i++, pField++ ) { Delta_ReadField( msg, pField, from, to, timebase ); } // message parsed return true; }