void CHLTVClientState::ReadDeltaEnt( CEntityReadInfo &u ) { const int i = u.m_nNewEntity; CFrameSnapshot *pFromSnapshot = u.m_pFrom->GetSnapshot(); CFrameSnapshot *pSnapshot = u.m_pTo->GetSnapshot(); Assert( i < pFromSnapshot->m_nNumEntities ); pSnapshot->m_pEntities[i] = pFromSnapshot->m_pEntities[i]; PackedEntity *pToPackedEntity = framesnapshotmanager->CreatePackedEntity( pSnapshot, i ); // WARNING! get pFromPackedEntity after new pPackedEntity has been created, otherwise pointer may be wrong PackedEntity *pFromPackedEntity = framesnapshotmanager->GetPackedEntity( pFromSnapshot, i ); pToPackedEntity->SetServerAndClientClass( pFromPackedEntity->m_pServerClass, pFromPackedEntity->m_pClientClass ); // create a copy of the pFromSnapshot ChangeFrameList IChangeFrameList* pChangeFrame = NULL; if ( !m_bSaveMemory ) { pChangeFrame = pFromPackedEntity->GetChangeFrameList()->Copy(); pToPackedEntity->SetChangeFrameList( pChangeFrame ); } // Make space for the baseline data. char packedData[MAX_PACKEDENTITY_DATA]; const void *pFromData; int nFromBits; if ( pFromPackedEntity->IsCompressed() ) { pFromData = m_pHLTV->UncompressPackedEntity( pFromPackedEntity, nFromBits ); } else { pFromData = pFromPackedEntity->GetData(); nFromBits = pFromPackedEntity->GetNumBits(); } bf_read fromBuf( "HLTV_ReadEnterPVS1", pFromData, Bits2Bytes( nFromBits ), nFromBits ); bf_write writeBuf( "HLTV_ReadEnterPVS2", packedData, sizeof( packedData ) ); int changedProps[MAX_DATATABLE_PROPS]; // decode baseline, is compressed against zero values int nChangedProps = RecvTable_MergeDeltas( pToPackedEntity->m_pClientClass->m_pRecvTable, &fromBuf, u.m_pBuf, &writeBuf, -1, false, changedProps ); // update change tick in ChangeFrameList if ( pChangeFrame ) { pChangeFrame->SetChangeTick( changedProps, nChangedProps, pSnapshot->m_nTickCount ); } if ( m_bSaveMemory ) { int bits = writeBuf.GetNumBitsWritten(); const char *compressedData = m_pHLTV->CompressPackedEntity( pToPackedEntity->m_pServerClass, (char*)writeBuf.GetData(), bits ); // store as compressed data and don't use mem pools pToPackedEntity->AllocAndCopyPadded( compressedData, Bits2Bytes(bits) ); pToPackedEntity->SetCompressed(); } else { // store as normal pToPackedEntity->AllocAndCopyPadded( packedData, writeBuf.GetNumBytesWritten() ); } u.m_pTo->last_entity = u.m_nNewEntity; u.m_pTo->transmit_entity.Set( u.m_nNewEntity ); u.NextOldEntity(); }
void CHLTVClientState::CopyNewEntity( CEntityReadInfo &u, int iClass, int iSerialNum ) { ServerClass *pServerClass = SV_FindServerClass( iClass ); Assert( pServerClass ); ClientClass *pClientClass = GetClientClass( iClass ); Assert( pClientClass ); const int ent = u.m_nNewEntity; // copy class & serial CFrameSnapshot *pSnapshot = u.m_pTo->GetSnapshot(); pSnapshot->m_pEntities[ent].m_nSerialNumber = iSerialNum; pSnapshot->m_pEntities[ent].m_pClass = pServerClass; // Get either the static or instance baseline. const void *pFromData = NULL; int nFromBits = 0; int nFromTick = 0; // MOTODO get tick when baseline last changed PackedEntity *baseline = u.m_bAsDelta ? GetEntityBaseline( u.m_nBaseline, ent ) : NULL; if ( baseline && baseline->m_pClientClass == pClientClass ) { Assert( !baseline->IsCompressed() ); pFromData = baseline->GetData(); nFromBits = baseline->GetNumBits(); } else { // Every entity must have a static or an instance baseline when we get here. ErrorIfNot( GetClassBaseline( iClass, &pFromData, &nFromBits ), ("HLTV_CopyNewEntity: GetDynamicBaseline(%d) failed.", iClass) ); nFromBits *= 8; // convert to bits } // create new ChangeFrameList containing all properties set as changed int nFlatProps = SendTable_GetNumFlatProps( pServerClass->m_pTable ); IChangeFrameList *pChangeFrame = NULL; if ( !m_bSaveMemory ) { pChangeFrame = AllocChangeFrameList( nFlatProps, nFromTick ); } // Now make a PackedEntity and store the new packed data in there. PackedEntity *pPackedEntity = framesnapshotmanager->CreatePackedEntity( pSnapshot, ent ); pPackedEntity->SetChangeFrameList( pChangeFrame ); pPackedEntity->SetServerAndClientClass( pServerClass, pClientClass ); // Make space for the baseline data. char packedData[MAX_PACKEDENTITY_DATA]; bf_read fromBuf( "HLTV_ReadEnterPVS1", pFromData, Bits2Bytes( nFromBits ), nFromBits ); bf_write writeBuf( "HLTV_ReadEnterPVS2", packedData, sizeof( packedData ) ); int changedProps[MAX_DATATABLE_PROPS]; // decode basline, is compressed against zero values int nChangedProps = RecvTable_MergeDeltas( pClientClass->m_pRecvTable, &fromBuf, u.m_pBuf, &writeBuf, -1, false, changedProps ); // update change tick in ChangeFrameList if ( pChangeFrame ) { pChangeFrame->SetChangeTick( changedProps, nChangedProps, pSnapshot->m_nTickCount ); } if ( u.m_bUpdateBaselines ) { SetEntityBaseline( (u.m_nBaseline==0)?1:0, pClientClass, u.m_nNewEntity, packedData, writeBuf.GetNumBytesWritten() ); } pPackedEntity->AllocAndCopyPadded( packedData, writeBuf.GetNumBytesWritten() ); // If ent doesn't think it's in PVS, signal that it is Assert( u.m_pTo->last_entity <= ent ); u.m_pTo->last_entity = ent; u.m_pTo->transmit_entity.Set( ent ); }
static inline void SV_PackEntity( int edictIdx, edict_t* ent, SendTable* pSendTable, EntityChange_t changeType, CFrameSnapshot *pSnapshot ) { int iSerialNum = pSnapshot->m_Entities[ edictIdx ].m_nSerialNumber; // Check to see if this entity specifies its changes. // If so, then try to early out making the fullpack bool bUsedPrev = false; if ( changeType == ENTITY_CHANGE_NONE ) { // Now this may not work if we didn't previously send a packet; // if not, then we gotta compute it bUsedPrev = framesnapshot->UsePreviouslySentPacket( pSnapshot, edictIdx, iSerialNum ); } if ( !bUsedPrev || sv_debugmanualmode.GetInt() ) { // First encode the entity's data. char packedData[MAX_PACKEDENTITY_DATA]; bf_write writeBuf( "SV_PackEntity->writeBuf", packedData, sizeof( packedData ) ); // (avoid constructor overhead). unsigned char tempData[ sizeof( CSendProxyRecipients ) * MAX_DATATABLE_PROXIES ]; CUtlMemory< CSendProxyRecipients > recip( (CSendProxyRecipients*)tempData, pSendTable->GetNumDataTableProxies() ); if( !SendTable_Encode( pSendTable, ent->m_pEnt, &writeBuf, NULL, edictIdx, &recip ) ) { Host_Error( "SV_PackEntity: SendTable_Encode returned false (ent %d).\n", edictIdx ); } SV_EnsureInstanceBaseline( edictIdx, packedData, writeBuf.GetNumBytesWritten() ); int nFlatProps = SendTable_GetNumFlatProps( pSendTable ); IChangeFrameList *pChangeFrame; // If this entity was previously in there, then it should have a valid IChangeFrameList // which we can delta against to figure out which properties have changed. // // If not, then we want to setup a new IChangeFrameList. PackedEntity *pPrevFrame = framesnapshot->GetPreviouslySentPacket( edictIdx, pSnapshot->m_Entities[ edictIdx ].m_nSerialNumber ); if ( pPrevFrame ) { // Calculate a delta. bf_read bfPrev( "SV_PackEntity->bfPrev", pPrevFrame->LockData(), pPrevFrame->GetNumBytes() ); bf_read bfNew( "SV_PackEntity->bfNew", packedData, writeBuf.GetNumBytesWritten() ); int deltaProps[MAX_DATATABLE_PROPS]; int nChanges = SendTable_CalcDelta( pSendTable, pPrevFrame->LockData(), pPrevFrame->GetNumBits(), packedData, writeBuf.GetNumBitsWritten(), deltaProps, ARRAYSIZE( deltaProps ), edictIdx ); // If it's non-manual-mode, but we detect that there are no changes here, then just // use the previous pSnapshot if it's available (as though the entity were manual mode). // It would be interesting to hook here and see how many non-manual-mode entities // are winding up with no changes. if ( nChanges == 0 ) { if ( changeType == ENTITY_CHANGE_NONE ) { for ( int iDeltaProp=0; iDeltaProp < nChanges; iDeltaProp++ ) { Msg( "Entity %d (class '%s') reported ENTITY_CHANGE_NONE but '%s' changed.\n", edictIdx, STRING( ent->classname ), pSendTable->GetProp( deltaProps[iDeltaProp] )->GetName() ); } } else { if ( pPrevFrame->CompareRecipients( recip ) ) { if ( framesnapshot->UsePreviouslySentPacket( pSnapshot, edictIdx, iSerialNum ) ) return; } } } // Ok, now snag the changeframe from the previous frame and update the 'last frame changed' // for the properties in the delta. pChangeFrame = pPrevFrame->SnagChangeFrameList(); ErrorIfNot( pChangeFrame && pChangeFrame->GetNumProps() == nFlatProps, ("SV_PackEntity: SnagChangeFrameList returned null") ); pChangeFrame->SetChangeTick( deltaProps, nChanges, pSnapshot->m_nTickNumber ); } else { // Ok, init the change frames for the first time. pChangeFrame = AllocChangeFrameList( nFlatProps, pSnapshot->m_nTickNumber ); } // Now make a PackedEntity and store the new packed data in there. PackedEntity *pCurFrame = framesnapshot->CreatePackedEntity( pSnapshot, edictIdx ); pCurFrame->SetChangeFrameList( pChangeFrame ); pCurFrame->m_nEntityIndex = edictIdx; pCurFrame->m_pSendTable = pSendTable; pCurFrame->AllocAndCopyPadded( packedData, writeBuf.GetNumBytesWritten(), &g_PackedDataAllocator ); pCurFrame->SetRecipients( recip ); } }