コード例 #1
0
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 );
	}
}
コード例 #2
0
void RunDataTableTest()
{
	RecvTable *pRecvTable = &REFERENCE_RECV_TABLE(DT_DTTest);
	SendTable *pSendTable = &REFERENCE_SEND_TABLE(DT_DTTest);


	// Initialize the send and receive modules.
	SendTable_Init( &pSendTable, 1 );
	RecvTable_Init( &pRecvTable, 1 );

	pSendTable->SetWriteFlag( false );
	
	// Send DataTable info to the client.
	unsigned char commBuf[8192];
	bf_write bfWrite( "RunDataTableTest->commBuf", commBuf, sizeof(commBuf) );
	if( !WriteSendTable_R( pSendTable, bfWrite, true ) )
	{
		Assert( !"RunDataTableTest: SendTable_SendInfo failed." );
	}	
	bfWrite.WriteOneBit(0);


	// Receive the SendTable's info.
	bf_read bfRead( "RunDataTableTest->bfRead", commBuf, sizeof(commBuf));
	while( bfRead.ReadOneBit() )
	{
		bool bNeedsDecoder = bfRead.ReadOneBit()!=0;

		if( !RecvTable_RecvClassInfos( &bfRead, bNeedsDecoder ) )
		{
			Assert( !"RunDataTableTest: RecvTable_ReadInfos failed." );
			continue;
		}
	}

	// Register our receive table.
	if( !RecvTable_CreateDecoders( NULL ) )
	{
		Assert(false);
	}


	// Setup the data with all zeros.
	DTTestServer dtServer;
	DTTestClient dtClient;

	unsigned char prevEncoded[4096];
	unsigned char fullEncoded[4096];

	memset(&dtServer, 0, sizeof(dtServer));
	memset(&dtClient, 0, sizeof(dtClient));
	memset(prevEncoded, 0, sizeof(prevEncoded));

	SetGuardBytes( &dtClient );

	// Now loop around, changing the data a little bit each time and send/recv deltas.
	int nIterations = 25;
	for( int iIteration=0; iIteration < nIterations; iIteration++ )
	{
		// Change the server's data.
		g_bSendSub = true;
		if( (iIteration % 5) == 0 )
		{
			g_bSendSub = false; // every 8th time, don't send the subtable
		}
		
		if( (iIteration & 3) == 0 )
		{
			// Every once in a while, change ALL the properties.
			for( int iChange=0; iChange < NUMVARTESTINFOS; iChange++ )
				g_VarTestInfos[iChange].m_ChangeFn( &dtServer );
		}
		else
		{
			int nChanges = 3 + rand() % NUMVARTESTINFOS;
			for( int iChange=0; iChange < nChanges; iChange++ )
			{
				int iInfo = rand() % NUMVARTESTINFOS;
				g_VarTestInfos[iInfo].m_ChangeFn( &dtServer );
			}
		}

		// Fully encode it.
		bf_write bfFullEncoded( "RunDataTableTest->bfFullEncoded", fullEncoded, sizeof(fullEncoded) );
		if( !SendTable_Encode( pSendTable, &dtServer, &bfFullEncoded, -1, NULL ) )
		{
			Assert(false);
		}


		unsigned char deltaEncoded[4096];
		bf_write bfDeltaEncoded( "RunDataTableTest->bfDeltaEncoded", deltaEncoded, sizeof(deltaEncoded) );
		
		if ( iIteration == 0 )
		{
			// On the first iteration, just write the whole state.
			if( !SendTable_Encode( pSendTable, &dtServer, &bfDeltaEncoded, -1, NULL ) )
			{
				Assert( false );
			}
		}
		else
		{
			// Figure out the delta between the newly encoded one and the previously encoded one.
			int deltaProps[MAX_DATATABLE_PROPS];

			bf_read fullEncodedRead( "RunDataTableTest->fullEncodedRead", fullEncoded, sizeof( fullEncoded ), bfFullEncoded.GetNumBitsWritten() );
			bf_read prevEncodedRead( "RunDataTableTest->prevEncodedRead", prevEncoded, sizeof( prevEncoded ) );

			int nDeltaProps = SendTable_CalcDelta( 
				pSendTable, 
				prevEncoded, sizeof( prevEncoded ) * 8, 
				fullEncoded, bfFullEncoded.GetNumBitsWritten(),
				deltaProps,
				ARRAYSIZE( deltaProps ),
				-1 );
			
			Assert( nDeltaProps != -1 ); // BAD: buffer overflow

			
			// Reencode with just the delta. This is what is actually sent to the client.
			SendTable_WritePropList( 
				pSendTable,
				fullEncoded,
				bfFullEncoded.GetNumBitsWritten(),
				&bfDeltaEncoded,
				-1111, 
				deltaProps,
				nDeltaProps );
		}

		memcpy( prevEncoded, fullEncoded, sizeof( prevEncoded ) );


		// This step isn't necessary to have the client decode the data but it's here to test
		// RecvTable_CopyEncoding (and RecvTable_MergeDeltas). This call should just make an exact
		// copy of the encoded data.
		unsigned char copyEncoded[4096];
		bf_read bfReadDeltaEncoded( "RunDataTableTest->bfReadDeltaEncoded", deltaEncoded, sizeof( deltaEncoded ) );
		bf_write bfCopyEncoded( "RunDataTableTest->bfCopyEncoded", copyEncoded, sizeof(copyEncoded) );

		RecvTable_CopyEncoding( pRecvTable, &bfReadDeltaEncoded, &bfCopyEncoded, -1 );
		
		// Decode..
		bf_read bfDecode( "RunDataTableTest->copyEncoded", copyEncoded, sizeof( copyEncoded ) );
		if(!RecvTable_Decode(pRecvTable, &dtClient, &bfDecode, 1111))
		{
			Assert(false);
		}

		
		// Make sure it didn't go into memory it shouldn't have.
		CheckGuardBytes( &dtClient );


		// Verify that only the changed properties were sent and that they were received correctly.
		CompareDTTest( &dtClient, &dtServer );
	}

	SendTable_Term();
	RecvTable_Term();
}