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_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_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_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 ); } }
/* ================== 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; }
/* ===================== Delta_ReadField read fields by offsets assume 'from' and 'to' is valid ===================== */ qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, void *to, float timebase ) { qboolean bSigned = ( pField->flags & DT_SIGNED ) ? true : false; float flValue, flAngle, flTime; qboolean bChanged; uint iValue; const char *pStr; char *pOut; bChanged = BF_ReadOneBit( msg ); ASSERT( pField->multiplier != 0.0f ); if( pField->flags & DT_BYTE ) { if( bChanged ) { iValue = BF_ReadBitLong( msg, pField->bits, bSigned ); if ( pField->multiplier != 1.0f ) iValue /= pField->multiplier; } else { iValue = *(byte *)((byte *)from + pField->offset ); } *(byte *)((byte *)to + pField->offset ) = iValue; } else if( pField->flags & DT_SHORT ) { if( bChanged ) { iValue = BF_ReadBitLong( msg, pField->bits, bSigned ); if ( pField->multiplier != 1.0f ) iValue /= pField->multiplier; } else { iValue = *(word *)((byte *)from + pField->offset ); } *(word *)((byte *)to + pField->offset ) = iValue; } else if( pField->flags & DT_INTEGER ) { if( bChanged ) { iValue = BF_ReadBitLong( msg, pField->bits, bSigned ); if ( pField->multiplier != 1.0f ) iValue /= pField->multiplier; } else { iValue = *(uint *)((byte *)from + pField->offset ); } *(uint *)((byte *)to + pField->offset ) = iValue; } else if( pField->flags & DT_FLOAT ) { if( bChanged ) { iValue = BF_ReadBitLong( msg, pField->bits, bSigned ); flValue = (int)iValue * ( 1.0f / pField->multiplier ); flValue = flValue * pField->post_multiplier; } else { #ifdef __arm__ memcpy(&flValue, (byte *)from + pField->offset, sizeof(float) ); #else flValue = *(float *)((byte *)from + pField->offset ); #endif } #ifdef __arm__ memcpy((byte *)to + pField->offset, &flValue, sizeof(float)); #else *(float *)((byte *)to + pField->offset ) = flValue; #endif } else if( pField->flags & DT_ANGLE ) { if( bChanged ) { flAngle = BF_ReadBitAngle( msg, pField->bits ); } else { #ifdef __arm__ memcpy(&flAngle, (byte *)from + pField->offset, sizeof(float) ); #else flAngle = *(float *)((byte *)from + pField->offset ); #endif } #ifdef __arm__ memcpy((byte *)to + pField->offset, &flAngle, sizeof(float)); #else *(float *)((byte *)to + pField->offset ) = flAngle; #endif } else if( pField->flags & DT_TIMEWINDOW_8 ) { if( bChanged ) { iValue = BF_ReadBitLong( msg, pField->bits, bSigned ); flValue = (float)((int)(iValue * 0.01f )); flTime = timebase + flValue; } else { #ifdef __arm__ memcpy(&flTime, (byte *)from + pField->offset, sizeof(float) ); #else flTime = *(float *)((byte *)from + pField->offset ); #endif } #ifdef __arm__ memcpy((byte *)to + pField->offset, &flTime, sizeof(float)); #else *(float *)((byte *)to + pField->offset ) = flTime; #endif } else if( pField->flags & DT_TIMEWINDOW_BIG ) { if( bChanged ) { iValue = BF_ReadBitLong( msg, pField->bits, bSigned ); flValue = (float)((int)iValue) * ( 1.0f / pField->multiplier ); flTime = timebase + flValue; } else { #ifdef __arm__ memcpy(&flTime, (byte *)from + pField->offset, sizeof(float) ); #else flTime = *(float *)((byte *)from + pField->offset ); #endif } #ifdef __arm__ memcpy((byte *)to + pField->offset, &flTime, sizeof(float)); #else *(float *)((byte *)to + pField->offset ) = flTime; #endif } else if( pField->flags & DT_STRING ) { if( bChanged ) { pStr = BF_ReadString( msg ); } else { pStr = (char *)((byte *)from + pField->offset ); } pOut = (char *)((byte *)to + pField->offset ); Q_strncpy( pOut, pStr, pField->size ); } return bChanged; }
/* ================== 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; }