コード例 #1
0
bool SendTable_Encode(
	const SendTable *pTable,
	const void *pStruct, 
	bf_write *pOut, 
	int objectID,
	CUtlMemory<CSendProxyRecipients> *pRecipients,
	bool bNonZeroOnly
	)
{
	CSendTablePrecalc *pPrecalc = pTable->m_pPrecalc;
	ErrorIfNot( pPrecalc, ("SendTable_Encode: Missing m_pPrecalc for SendTable %s.", pTable->m_pNetTableName) );
	if ( pRecipients )
	{
		ErrorIfNot(	pRecipients->NumAllocated() >= pPrecalc->GetNumDataTableProxies(), ("SendTable_Encode: pRecipients array too small.") );
	}

	VPROF( "SendTable_Encode" );

	CServerDTITimer timer( pTable, SERVERDTI_ENCODE );

	// This writes and delta-compresses the delta bits.
	CDeltaBitsWriter deltaBitsWriter( pOut );

	// Setup all the info we'll be walking the tree with.
	CEncodeInfo info( pPrecalc, (unsigned char*)pStruct, objectID );
	
	info.m_pOut = pOut;
	info.m_ObjectID = objectID;
	info.m_nDataBits = 0;
	info.m_nOverheadBits = 0; // unused
	info.m_pDeltaBitsWriter = &deltaBitsWriter;
	info.m_pRecipients = pRecipients;	// optional buffer to store the bits for which clients get what data.

	info.Init();
	
	int iNumProps = pPrecalc->GetNumProps();

	for ( int iProp=0; iProp < iNumProps; iProp++ )
	{
		// skip if we don't have a valid prop proxy
		if ( !info.IsPropProxyValid( iProp ) )
			continue;

		info.SeekToProp( iProp );
        
		// skip empty prop if we only encode non-zero values
		if ( bNonZeroOnly && SendTable_IsPropZero(&info, iProp) )
			continue;

		SendTable_EncodeProp( &info, iProp );
	}

	return !pOut->IsOverflowed();
}
コード例 #2
0
int SendTable_CullPropsFromProxies( 
	const SendTable *pTable,
	
	const int *pStartProps,
	int nStartProps,

	const int iClient,
	
	const CSendProxyRecipients *pOldStateProxies,
	const int nOldStateProxies, 
	
	const CSendProxyRecipients *pNewStateProxies,
	const int nNewStateProxies,
	
	int *pOutProps,
	int nMaxOutProps
	)
{
	Assert( !( nNewStateProxies && !pNewStateProxies ) );
	CPropCullStack stack( pTable->m_pPrecalc, iClient, pOldStateProxies, nOldStateProxies, pNewStateProxies, nNewStateProxies );
	
	stack.CullPropsFromProxies( pStartProps, nStartProps, pOutProps, nMaxOutProps );

	ErrorIfNot( stack.GetNumOutProps() <= nMaxOutProps, ("CullPropsFromProxies: overflow in '%s'.", pTable->GetName()) );
	return stack.GetNumOutProps();
}
コード例 #3
0
bool SendTable_Init( SendTable **pTables, int nTables )
{
	ErrorIfNot( g_SendTables.Count() == 0,
		("SendTable_Init: called twice.")
	);

	// Initialize them all.
	for ( int i=0; i < nTables; i++ )
	{
		if ( !SendTable_InitTable( pTables[i] ) )
			return false;
	}

	// Store off the SendTable list.
	g_SendTables.CopyArray( pTables, nTables );

	g_SendTableCRC = SendTable_ComputeCRC( );

	if ( CommandLine()->FindParm("-dti" ) )
	{
		SendTable_PrintStats();
	}

	return true;	
}
コード例 #4
0
int SendTable_GetNumFlatProps( SendTable *pSendTable )
{
	CSendTablePrecalc *pPrecalc = pSendTable->m_pPrecalc;
	ErrorIfNot( pPrecalc,
		("SendTable_GetNumFlatProps: missing pPrecalc.")
	);
	return pPrecalc->GetNumProps();
}
コード例 #5
0
void ExportCoreDispAllowedVertList( const CCoreDispInfo *pIn, ddispinfo_t *pOut )
{
	ErrorIfNot( 
		pIn->GetAllowedVerts().GetNumDWords() == sizeof( pOut->m_AllowedVerts ) / 4,
		("ExportCoreDispAllowedVertList: size mismatch")
		);
	for ( int i=0; i < pIn->GetAllowedVerts().GetNumDWords(); i++ )
		pOut->m_AllowedVerts[i] = pIn->GetAllowedVerts().GetDWord( i );
}
コード例 #6
0
CDeltaCalculator::~CDeltaCalculator()
{
	// Make sure we didn't overflow.
	ErrorIfNot( 
		m_nDeltaProps <= m_nMaxDeltaProps && !m_bfFromState.IsOverflowed() && !m_bfToState.IsOverflowed(), 
		( "SendTable_CalcDelta: overflowed on datatable '%s'.", m_pPrecalc->GetSendTable()->GetName() ) 
		);

	// We may not have read to the end of our input bits, but we don't care.
	m_FromBitsReader.ForceFinished();
	m_ToBitsReader.ForceFinished();
}
コード例 #7
0
void LocalTransfer_TransferEntity( 
	const SendTable *pSendTable, 
	const void *pSrcEnt, 
	RecvTable *pRecvTable, 
	void *pDestEnt,
	int objectID )
{
	// Setup the structure to traverse the source tree.
	CSendTablePrecalc *pPrecalc = pSendTable->m_pPrecalc;
	ErrorIfNot( pPrecalc, ("SendTable_Encode: Missing m_pPrecalc for SendTable %s.", pSendTable->m_pNetTableName) );
	CServerDatatableStack serverStack( pPrecalc, (unsigned char*)pSrcEnt, objectID );

	// Setup the structure to traverse the dest tree.
	CRecvDecoder *pDecoder = pRecvTable->m_pDecoder;
	ErrorIfNot( pDecoder, ("RecvTable_Decode: table '%s' missing a decoder.", pRecvTable->GetName()) );
	CClientDatatableStack clientStack( pDecoder, (unsigned char*)pDestEnt, objectID );

	// Walk through each property in each tree and transfer the data.
	int iEndProp = pPrecalc->m_Root.GetLastPropIndex();
	for ( int iProp=0; iProp <= iEndProp; iProp++ )
	{
		serverStack.SeekToProp( iProp );
		clientStack.SeekToProp( iProp );

		const SendProp *pSendProp = serverStack.GetCurProp();
		const RecvProp *pRecvProp = pDecoder->GetProp( iProp );
		if ( pRecvProp )
		{
			unsigned char *pSendBase = serverStack.GetCurStructBase();
			unsigned char *pRecvBase = clientStack.GetCurStructBase();
			if ( pSendBase && pRecvBase )
			{
				Assert( stricmp( pSendProp->GetName(), pRecvProp->GetName() ) == 0 );

				g_PropTypeFns[pRecvProp->GetType()].FastCopy( pSendProp, pRecvProp, pSendBase, pRecvBase, objectID );
			}
		}
	}
}
コード例 #8
0
bool SV_GetInstanceBaseline( ServerClass *pClass, void const **pData, int *pDatalen )
{
	if ( sv_instancebaselines.GetInt() )
	{
		ErrorIfNot( pClass->m_InstanceBaselineIndex != INVALID_STRING_INDEX,
			("SV_GetInstanceBaseline: missing instance baseline for class '%s'", pClass->m_pNetworkName)
		);
		
		*pData = networkStringTableContainerServer->GetStringUserData(
			sv.GetInstanceBaselineTable(),
			pClass->m_InstanceBaselineIndex,
			pDatalen );
		
		return *pData != NULL;
	}
	else
	{
		static char dummy[1] = {0};
		*pData = dummy;
		*pDatalen = 1;
		return true;
	}
}
コード例 #9
0
// Spits out warnings for invalid properties and forces property values to
// be in valid ranges for the encoders and decoders.
static void SendTable_Validate( CSendTablePrecalc *pPrecalc )
{
	SendTable *pTable = pPrecalc->m_pSendTable;
	for( int i=0; i < pTable->m_nProps; i++ )
	{
		SendProp *pProp = &pTable->m_pProps[i];
		
		if ( pProp->GetArrayProp() )
		{
			if ( pProp->GetArrayProp()->GetType() == DPT_DataTable )
			{
				Error( "Invalid property: %s/%s (array of datatables) [on prop %d of %d (%s)].", pTable->m_pNetTableName, pProp->GetName(), i, pTable->m_nProps, pProp->GetArrayProp()->GetName() );
			}
		}
		else
		{
			ErrorIfNot( pProp->GetNumElements() == 1, ("Prop %s/%s has an invalid element count for a non-array.", pTable->m_pNetTableName, pProp->GetName()) );
		}
			
		// Check for 1-bit signed properties (their value doesn't get down to the client).
		if ( pProp->m_nBits == 1 && !(pProp->GetFlags() & SPROP_UNSIGNED) )
		{
			DataTable_Warning("SendTable prop %s::%s is a 1-bit signed property. Use SPROP_UNSIGNED or the client will never receive a value.\n", pTable->m_pNetTableName, pProp->GetName());
		}
	}

	for ( int i = 0; i < pPrecalc->GetNumProps(); ++i )
	{
		const SendProp *pProp = pPrecalc->GetProp( i );
		if ( pProp->GetFlags() & SPROP_ENCODED_AGAINST_TICKCOUNT )
		{
			pTable->SetHasPropsEncodedAgainstTickcount( true );
			break;
		}
	}
}
コード例 #10
0
// This function makes sure that this entity class has an instance baseline.
// If it doesn't have one yet, it makes a new one.
void SV_EnsureInstanceBaseline( int iEdict, const void *pData, int nBytes )
{
	edict_t *pEnt = &sv.edicts[iEdict];
	ErrorIfNot( pEnt->m_pEnt,
		("SV_EnsureInstanceBaseline: edict %d missing ent", iEdict)
	);

	ServerClass *pClass = pEnt->m_pEnt->GetServerClass();

	if ( pClass->m_InstanceBaselineIndex == INVALID_STRING_INDEX )
	{
		char idString[32];
		Q_snprintf( idString, sizeof( idString ), "%d", pClass->m_ClassID );

		// Ok, make a new instance baseline so they can reference it.
		pClass->m_InstanceBaselineIndex = networkStringTableContainerServer->AddString( 
			sv.GetInstanceBaselineTable(),
			idString,	// Note we're sending a string with the ID number, not the class name.
			nBytes,
			pData );

		Assert( pClass->m_InstanceBaselineIndex != INVALID_STRING_INDEX );
	}
}
コード例 #11
0
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 );
}
コード例 #12
0
//-----------------------------------------------------------------------------
// Purpose: Parse string update
//-----------------------------------------------------------------------------
void CNetworkStringTable::ParseUpdate( bf_read &buf, int entries )
{
	int lastEntry = -1;

	CUtlVector< StringHistoryEntry > history;

	for (int i=0; i<entries; i++)
	{
		int entryIndex = lastEntry + 1;

		if ( !buf.ReadOneBit() )
		{
			entryIndex = buf.ReadUBitLong( GetEntryBits() );
		}

		lastEntry = entryIndex;
		
		if ( entryIndex < 0 || entryIndex >= GetMaxStrings() )
		{
			Host_Error( "Server sent bogus string index %i for table %s\n", entryIndex, GetTableName() );
		}

		const char *pEntry = NULL;
		char entry[ 1024 ]; 
		char substr[ 1024 ];

		if ( buf.ReadOneBit() )
		{
			bool substringcheck = buf.ReadOneBit() ? true : false;

			if ( substringcheck )
			{
				int index = buf.ReadUBitLong( 5 );
				int bytestocopy = buf.ReadUBitLong( SUBSTRING_BITS );
				Q_strncpy( entry, history[ index ].string, bytestocopy + 1 );
				buf.ReadString( substr, sizeof(substr) );
				Q_strncat( entry, substr, sizeof(entry), COPY_ALL_CHARACTERS );
			}
			else
			{
				buf.ReadString( entry, sizeof( entry ) );
			}

			pEntry = entry;
		}
		
		// Read in the user data.
		unsigned char tempbuf[ CNetworkStringTableItem::MAX_USERDATA_SIZE ];
		memset( tempbuf, 0, sizeof( tempbuf ) );
		const void *pUserData = NULL;
		int nBytes = 0;

		if ( buf.ReadOneBit() )
		{
			if ( IsUserDataFixedSize() )
			{
				// Don't need to read length, it's fixed length and the length was networked down already.
				nBytes = GetUserDataSize();
				Assert( nBytes > 0 );
				tempbuf[nBytes-1] = 0; // be safe, clear last byte
				buf.ReadBits( tempbuf, GetUserDataSizeBits() );
			}
			else
			{
				nBytes = buf.ReadUBitLong( CNetworkStringTableItem::MAX_USERDATA_BITS );
				ErrorIfNot( nBytes <= sizeof( tempbuf ),
					("CNetworkStringTableClient::ParseUpdate: message too large (%d bytes).", nBytes)
				);

				buf.ReadBytes( tempbuf, nBytes );
			}

			pUserData = tempbuf;
		}

		// Check if we are updating an old entry or adding a new one
		if ( entryIndex < GetNumStrings() )
		{
			SetStringUserData( entryIndex, nBytes, pUserData );
#ifdef _DEBUG
			if ( pEntry )
			{
				Assert( !Q_strcmp( pEntry, GetString( entryIndex ) ) ); // make sure string didn't change
			}
#endif
			pEntry = GetString( entryIndex ); // string didn't change
		}
		else
		{
			// Grow the table (entryindex must be the next empty slot)
			Assert( (entryIndex == GetNumStrings()) && (pEntry != NULL) );
				
			if ( pEntry == NULL )
			{
				Msg("CNetworkStringTable::ParseUpdate: NULL pEntry, table %s, index %i\n", GetTableName(), entryIndex );
				pEntry = "";// avoid crash because of NULL strings
			}

			AddString( true, pEntry, nBytes, pUserData );
		}

		if ( history.Count() > 31 )
		{
			history.Remove( 0 );
		}

		StringHistoryEntry she;
		Q_strncpy( she.string, pEntry, sizeof( she.string ) );
		history.AddToTail( she );
	}
}
コード例 #13
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 );
	}
}
コード例 #14
0
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CGameServer::DumpPrecacheStats( INetworkStringTable *table )
{
	if ( table == NULL )
	{
		ConMsg( "Can only dump stats when active in a level\n" );
		return;
	}

	CPrecacheItem *items = NULL;
	if ( table == m_pModelPrecacheTable )
	{
		items = model_precache;
	}
	else if ( table == m_pGenericPrecacheTable )
	{
		items = generic_precache;
	}
	else if ( table == m_pSoundPrecacheTable )
	{
		items = sound_precache;
	}
	else if ( table == m_pDecalPrecacheTable )
	{
		items = decal_precache;
	}

	if ( !items )
		return;

	int count = table->GetNumStrings();
	int maxcount = table->GetMaxStrings();

	ConMsg( "\n" );
	ConMsg( "Precache table %s:  %i of %i slots used\n", table->GetTableName(),
		count, maxcount );

	for ( int i = 0; i < count; i++ )
	{
		char const *name = table->GetString( i );
		CPrecacheItem *slot = &items[ i ];
		
		int testLength;
		const CPrecacheUserData *p = ( const CPrecacheUserData * )table->GetStringUserData( i, &testLength );
		ErrorIfNot( testLength == sizeof( *p ),
			("CGameServer::DumpPrecacheStats: invalid CPrecacheUserData length (%d)", testLength)
		);

		if ( !name || !slot || !p )
			continue;

		ConMsg( "%03i:  %s (%s):   ",
			i,
			name, 
			GetFlagString( p->flags ) );

		if ( slot->GetReferenceCount() == 0 )
		{
			ConMsg( " never used\n" );
		}
		else
		{
			ConMsg( " %i refs, first %.2f mru %.2f\n",
				slot->GetReferenceCount(), 
				slot->GetFirstReference(), 
				slot->GetMostRecentReference() );
		}
	}

	ConMsg( "\n" );
}