/* ================== 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_ParsePacketEntities An svc_packetentities has just been parsed, deal with the rest of the data stream. ================== */ void CL_ParsePacketEntities (frame_t *oldframe, frame_t *newframe) { int newnum; int bits; entity_state_t *oldstate; 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; } } }
/* ================== CL_ParsePacketEntities ================== */ void CL_ParsePacketEntities( msg_t *msg, clSnapshot_t *oldframe, clSnapshot_t *newframe ) { int newnum; entityState_t *oldstate; int oldindex, oldnum; newframe->parseEntitiesNum = cl.parseEntitiesNum; newframe->numEntities = 0; // delta from the entities present in oldframe oldindex = 0; oldstate = NULL; if ( !oldframe ) { oldnum = 99999; } else { if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = &cl.parseEntities[ ( oldframe->parseEntitiesNum + oldindex ) & ( MAX_PARSE_ENTITIES - 1 ) ]; oldnum = oldstate->number; } } while ( 1 ) { // read the entity index number newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum == ( MAX_GENTITIES - 1 ) ) { break; } if ( msg->readcount > msg->cursize ) { Com_Error( ERR_DROP, "CL_ParsePacketEntities: end of message" ); } while ( oldnum < newnum ) { // one or more entities from the old packet are unchanged if ( cl_shownet->integer == 3 ) { Com_Printf( "%3i: unchanged: %i\n", msg->readcount, oldnum ); } CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue ); oldindex++; if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = &cl.parseEntities[ ( oldframe->parseEntitiesNum + oldindex ) & ( MAX_PARSE_ENTITIES - 1 ) ]; oldnum = oldstate->number; } } if ( oldnum == newnum ) { // delta from previous state if ( cl_shownet->integer == 3 ) { Com_Printf( "%3i: delta: %i\n", msg->readcount, newnum ); } CL_DeltaEntity( msg, newframe, newnum, oldstate, qfalse ); oldindex++; if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = &cl.parseEntities[ ( oldframe->parseEntitiesNum + oldindex ) & ( MAX_PARSE_ENTITIES - 1 ) ]; oldnum = oldstate->number; } continue; } if ( oldnum > newnum ) { // delta from baseline if ( cl_shownet->integer == 3 ) { Com_Printf( "%3i: baseline: %i\n", msg->readcount, newnum ); } CL_DeltaEntity( msg, newframe, newnum, &cl.entityBaselines[ newnum ], qfalse ); 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->integer == 3 ) { Com_Printf( "%3i: unchanged: %i\n", msg->readcount, oldnum ); } CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue ); oldindex++; if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = &cl.parseEntities[ ( oldframe->parseEntitiesNum + oldindex ) & ( MAX_PARSE_ENTITIES - 1 ) ]; oldnum = oldstate->number; } } if ( cl_shownuments->integer ) { Com_Printf( "Entities in packet: %i\n", newframe->numEntities ); } }
/* ================== CL_ParsePacketEntities ================== */ void CL_ParsePacketEntities( msg_t *msg, const clSnapshot_t *oldSnapshot, clSnapshot_t *newSnapshot ) { // The entity packet contains the delta between the two snapshots, with data only // for entities that were created, changed or removed. Entities entries are in // order of increasing entity number, as are entities in a snapshot. Using this we // have an efficient algorithm to create the new snapshot, that goes over the old // snapshot once from the beginning to the end. // If we don't have an old snapshot or it is empty, we'll recreate all entities // from the baseline entities as setting oldEntityNum to MAX_GENTITIES will force // us to only do step (3) below. unsigned int oldEntityNum = MAX_GENTITIES; if (oldSnapshot && oldSnapshot->entities.size() > 0){ oldEntityNum = oldSnapshot->entities[0].number; } // Likewise when we don't have an old snapshot, oldEntities just has to be an empty // vector so that we skip step (4) std::vector<entityState_t> dummyEntities; auto& oldEntities = oldSnapshot? oldSnapshot->entities : dummyEntities; auto& newEntities = newSnapshot->entities; unsigned int numEntities = MSG_ReadShort(msg); newEntities.reserve(numEntities); unsigned oldIndex = 0; while (true) { unsigned int newEntityNum = MSG_ReadBits(msg, GENTITYNUM_BITS); if (msg->readcount > msg->cursize) { Sys::Drop("CL_ParsePacketEntities: Unexpected end of message"); } if (newEntityNum == MAX_GENTITIES - 1) { break; } // (1) all entities that weren't specified between the previous newEntityNum and // the current one are unchanged and just copied over. while (oldEntityNum < newEntityNum) { newEntities.push_back(oldEntities[oldIndex]); oldIndex ++; if (oldIndex >= oldEntities.size()) { oldEntityNum = MAX_GENTITIES; } else { oldEntityNum = oldEntities[oldIndex].number; } } // (2) there is an entry for an entity in the old snapshot, apply the delta if (oldEntityNum == newEntityNum) { CL_DeltaEntity(msg, newSnapshot, newEntityNum, oldEntities[oldIndex]); oldIndex ++; if (oldIndex >= oldEntities.size()) { oldEntityNum = MAX_GENTITIES; } else { oldEntityNum = oldEntities[oldIndex].number; } } else { // (3) the entry isn't in the old snapshot, so the entity will be specified // from the baseline ASSERT_GT(oldEntityNum, newEntityNum); CL_DeltaEntity(msg, newSnapshot, newEntityNum, cl.entityBaselines[newEntityNum]); } } // (4) All remaining entities in the oldSnapshot are unchanged and copied over while (oldIndex < oldEntities.size()) { newEntities.push_back(oldEntities[oldIndex]); oldIndex ++; } ASSERT_EQ(numEntities, newEntities.size()); }