You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
420 lines
11 KiB
420 lines
11 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//=============================================================================// |
|
#include "LocalNetworkBackdoor.h" |
|
#include "server_class.h" |
|
#include "client_class.h" |
|
#include "server.h" |
|
#include "eiface.h" |
|
#include "cdll_engine_int.h" |
|
#include "dt_localtransfer.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
CLocalNetworkBackdoor *g_pLocalNetworkBackdoor = NULL; |
|
|
|
#ifndef SWDS |
|
// This is called |
|
void CLocalNetworkBackdoor::InitFastCopy() |
|
{ |
|
if ( !cl.m_NetChannel->IsLoopback() ) |
|
return; |
|
|
|
|
|
const CStandardSendProxies *pSendProxies = NULL; |
|
|
|
// If the game server is greater than v4, then it is using the new proxy format. |
|
if ( g_iServerGameDLLVersion >= 5 ) // check server version |
|
{ |
|
pSendProxies = serverGameDLL->GetStandardSendProxies(); |
|
} |
|
else |
|
{ |
|
// If the game server is older than v4, it is using the old proxy; we set the new proxy members to the |
|
// engine's copy. |
|
static CStandardSendProxies compatSendProxy = *serverGameDLL->GetStandardSendProxies(); |
|
|
|
compatSendProxy.m_DataTableToDataTable = g_StandardSendProxies.m_DataTableToDataTable; |
|
compatSendProxy.m_SendLocalDataTable = g_StandardSendProxies.m_SendLocalDataTable; |
|
compatSendProxy.m_ppNonModifiedPointerProxies = g_StandardSendProxies.m_ppNonModifiedPointerProxies; |
|
|
|
pSendProxies = &compatSendProxy; |
|
} |
|
|
|
const CStandardRecvProxies *pRecvProxies = g_ClientDLL->GetStandardRecvProxies(); |
|
|
|
int nFastCopyProps = 0; |
|
int nSlowCopyProps = 0; |
|
|
|
for ( int iClass=0; iClass < cl.m_nServerClasses; iClass++ ) |
|
{ |
|
ClientClass *pClientClass = cl.GetClientClass(iClass); |
|
if ( !pClientClass ) |
|
Error( "InitFastCopy - missing client class %d (Should be equivelent of server class: %s)", iClass, cl.m_pServerClasses[iClass].m_ClassName ); |
|
|
|
ServerClass *pServerClass = SV_FindServerClass( pClientClass->GetName() ); |
|
if ( !pServerClass ) |
|
Error( "InitFastCopy - missing server class %s", pClientClass->GetName() ); |
|
|
|
LocalTransfer_InitFastCopy( |
|
pServerClass->m_pTable, |
|
pSendProxies, |
|
pClientClass->m_pRecvTable, |
|
pRecvProxies, |
|
nSlowCopyProps, |
|
nFastCopyProps |
|
); |
|
} |
|
|
|
int percentFast = (nFastCopyProps * 100 ) / (nSlowCopyProps + nFastCopyProps + 1); |
|
if ( percentFast <= 55 ) |
|
{ |
|
// This may not be a real problem, but at the time this code was added, 67% of the |
|
// properties were able to be copied without proxies. If percentFast goes to 0 or some |
|
// really low number suddenly, then something probably got screwed up. |
|
Assert( false ); |
|
Warning( "InitFastCopy: only %d%% fast props. Bug?\n", percentFast ); |
|
} |
|
} |
|
#endif |
|
|
|
void CLocalNetworkBackdoor::StartEntityStateUpdate() |
|
{ |
|
m_EntsAlive.ClearAll(); |
|
m_nEntsCreated = 0; |
|
m_nEntsChanged = 0; |
|
|
|
// signal client that we start updating entities |
|
ClientDLL_FrameStageNotify( FRAME_NET_UPDATE_START ); |
|
} |
|
|
|
void CLocalNetworkBackdoor::EndEntityStateUpdate() |
|
{ |
|
ClientDLL_FrameStageNotify( FRAME_NET_UPDATE_POSTDATAUPDATE_START ); |
|
|
|
// Handle entities created. |
|
int i; |
|
for ( i=0; i < m_nEntsCreated; i++ ) |
|
{ |
|
MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); |
|
|
|
int iEdict = m_EntsCreatedIndices[i]; |
|
CCachedEntState *pCached = &m_CachedEntState[iEdict]; |
|
IClientNetworkable *pNet = pCached->m_pNetworkable; |
|
|
|
pNet->PostDataUpdate( DATA_UPDATE_CREATED ); |
|
pNet->NotifyShouldTransmit( SHOULDTRANSMIT_START ); |
|
pCached->m_bDormant = false; |
|
} |
|
|
|
// Handle entities changed. |
|
for ( i=0; i < m_nEntsChanged; i++ ) |
|
{ |
|
MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); |
|
|
|
int iEdict = m_EntsChangedIndices[i]; |
|
m_CachedEntState[iEdict].m_pNetworkable->PostDataUpdate( DATA_UPDATE_DATATABLE_CHANGED ); |
|
} |
|
|
|
ClientDLL_FrameStageNotify( FRAME_NET_UPDATE_POSTDATAUPDATE_END ); |
|
|
|
// Handle entities removed (= SV_WriteDeletions() in normal mode) |
|
int nDWords = m_PrevEntsAlive.GetNumDWords(); |
|
|
|
// Handle entities removed. |
|
for ( i=0; i < nDWords; i++ ) |
|
{ |
|
unsigned long prevEntsAlive = m_PrevEntsAlive.GetDWord( i ); |
|
unsigned long entsAlive = m_EntsAlive.GetDWord( i ); |
|
unsigned long toDelete = (prevEntsAlive ^ entsAlive) & prevEntsAlive; |
|
|
|
if ( toDelete ) |
|
{ |
|
for ( int iBit=0; iBit < 32; iBit++ ) |
|
{ |
|
if ( toDelete & (1 << iBit) ) |
|
{ |
|
int iEdict = (i<<5) + iBit; |
|
if ( iEdict >= 0 && iEdict < MAX_EDICTS ) |
|
{ |
|
if ( m_CachedEntState[iEdict].m_pNetworkable ) |
|
{ |
|
m_CachedEntState[iEdict].m_pNetworkable->Release(); |
|
m_CachedEntState[iEdict].m_pNetworkable = NULL; |
|
} |
|
else |
|
{ |
|
AssertOnce( !"EndEntityStateUpdate: Would have crashed with NULL m_pNetworkable\n" ); |
|
} |
|
} |
|
else |
|
{ |
|
AssertOnce( !"EndEntityStateUpdate: Would have crashed with entity out of range\n" ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
// Remember the previous state of which entities were around. |
|
m_PrevEntsAlive = m_EntsAlive; |
|
|
|
// end of all entity update activity |
|
ClientDLL_FrameStageNotify( FRAME_NET_UPDATE_END ); |
|
|
|
/* |
|
#ifdef _DEBUG |
|
for ( i=0; i <= highest_index; i++ ) |
|
{ |
|
if ( !( m_EntsAlive[i>>5] & (1 << (i & 31)) ) ) |
|
Assert( !m_CachedEntState[i].m_pNetworkable ); |
|
|
|
if ( ( m_EntsAlive[i>>5] & (1 << (i & 31)) ) && |
|
( m_EntsCreated[i>>5] & (1 << (i & 31)) ) ) |
|
{ |
|
Assert( FindInList( m_EntsCreatedIndices, m_nEntsCreated, i ) ); |
|
} |
|
|
|
if ( (m_EntsAlive[i>>5] & (1 << (i & 31))) && |
|
!(m_EntsCreated[i>>5] & (1 << (i & 31))) && |
|
(m_EntsChanged[i>>5] & (1 << (i & 31))) |
|
) |
|
{ |
|
Assert( FindInList( m_EntsChangedIndices, m_nEntsChanged, i ) ); |
|
} |
|
} |
|
#endif |
|
*/ |
|
} |
|
|
|
void CLocalNetworkBackdoor::EntityDormant( int iEnt, int iSerialNum ) |
|
{ |
|
CCachedEntState *pCached = &m_CachedEntState[iEnt]; |
|
|
|
IClientNetworkable *pNet = pCached->m_pNetworkable; |
|
Assert( pNet == entitylist->GetClientNetworkable( iEnt ) ); |
|
if ( pNet ) |
|
{ |
|
Assert( pCached->m_iSerialNumber == pNet->GetIClientUnknown()->GetRefEHandle().GetSerialNumber() ); |
|
if ( pCached->m_iSerialNumber == iSerialNum ) |
|
{ |
|
m_EntsAlive.Set( iEnt ); |
|
|
|
// Tell the game code that this guy is now dormant. |
|
Assert( pCached->m_bDormant == pNet->IsDormant() ); |
|
if ( !pCached->m_bDormant ) |
|
{ |
|
pNet->NotifyShouldTransmit( SHOULDTRANSMIT_END ); |
|
pCached->m_bDormant = true; |
|
} |
|
} |
|
else |
|
{ |
|
pNet->Release(); |
|
pCached->m_pNetworkable = NULL; |
|
m_PrevEntsAlive.Clear( iEnt ); |
|
} |
|
} |
|
} |
|
|
|
|
|
void CLocalNetworkBackdoor::AddToPendingDormantEntityList( unsigned short iEdict ) |
|
{ |
|
edict_t *e = &sv.edicts[iEdict]; |
|
if ( !( e->m_fStateFlags & FL_EDICT_PENDING_DORMANT_CHECK ) ) |
|
{ |
|
e->m_fStateFlags |= FL_EDICT_PENDING_DORMANT_CHECK; |
|
m_PendingDormantEntities.AddToTail( iEdict ); |
|
} |
|
} |
|
|
|
void CLocalNetworkBackdoor::ProcessDormantEntities() |
|
{ |
|
FOR_EACH_LL( m_PendingDormantEntities, i ) |
|
{ |
|
int iEdict = m_PendingDormantEntities[i]; |
|
edict_t *e = &sv.edicts[iEdict]; |
|
|
|
// Make sure the entity still exists and stil has the dontsend flag set. |
|
if ( e->IsFree() || !(e->m_fStateFlags & FL_EDICT_DONTSEND) ) |
|
{ |
|
e->m_fStateFlags &= ~FL_EDICT_PENDING_DORMANT_CHECK; |
|
continue; |
|
} |
|
|
|
EntityDormant( iEdict, e->m_NetworkSerialNumber ); |
|
e->m_fStateFlags &= ~FL_EDICT_PENDING_DORMANT_CHECK; |
|
} |
|
m_PendingDormantEntities.Purge(); |
|
} |
|
|
|
void CLocalNetworkBackdoor::EntState( |
|
int iEnt, |
|
int iSerialNum, |
|
int iClass, |
|
const SendTable *pSendTable, |
|
const void *pSourceEnt, |
|
bool bChanged, |
|
bool bShouldTransmit ) |
|
{ |
|
CCachedEntState *pCached = &m_CachedEntState[iEnt]; |
|
|
|
// Remember that this ent is alive. |
|
m_EntsAlive.Set(iEnt); |
|
|
|
ClientClass *pClientClass = cl.GetClientClass(iClass); |
|
if ( !pClientClass ) |
|
Error( "CLocalNetworkBackdoor::EntState - missing client class %d", iClass ); |
|
|
|
IClientNetworkable *pNet = pCached->m_pNetworkable; |
|
Assert( pNet == entitylist->GetClientNetworkable( iEnt ) ); |
|
|
|
if ( !bShouldTransmit ) |
|
{ |
|
if ( pNet ) |
|
{ |
|
Assert( pCached->m_iSerialNumber == pNet->GetIClientUnknown()->GetRefEHandle().GetSerialNumber() ); |
|
if ( pCached->m_iSerialNumber == iSerialNum ) |
|
{ |
|
// Tell the game code that this guy is now dormant. |
|
Assert( pCached->m_bDormant == pNet->IsDormant() ); |
|
if ( !pCached->m_bDormant ) |
|
{ |
|
pNet->NotifyShouldTransmit( SHOULDTRANSMIT_END ); |
|
pCached->m_bDormant = true; |
|
} |
|
} |
|
else |
|
{ |
|
pNet->Release(); |
|
pNet = NULL; |
|
pCached->m_pNetworkable = NULL; |
|
// Since we set this above, need to clear it now to avoid assertion in EndEntityStateUpdate() |
|
m_EntsAlive.Clear(iEnt); |
|
m_PrevEntsAlive.Clear( iEnt ); |
|
} |
|
} |
|
else |
|
{ |
|
m_EntsAlive.Clear( iEnt ); |
|
} |
|
return; |
|
} |
|
// Do we have an entity here already? |
|
bool bExistedAndWasDormant = false; |
|
if ( pNet ) |
|
{ |
|
// If the serial numbers are different, make it recreate the ent. |
|
Assert( pCached->m_iSerialNumber == pNet->GetIClientUnknown()->GetRefEHandle().GetSerialNumber() ); |
|
if ( iSerialNum == pCached->m_iSerialNumber ) |
|
{ |
|
bExistedAndWasDormant = pCached->m_bDormant; |
|
} |
|
else |
|
{ |
|
pNet->Release(); |
|
pNet = NULL; |
|
m_PrevEntsAlive.Clear(iEnt); |
|
} |
|
} |
|
|
|
// Create the entity? |
|
bool bCreated = false; |
|
DataUpdateType_t updateType; |
|
if ( pNet ) |
|
{ |
|
updateType = DATA_UPDATE_DATATABLE_CHANGED; |
|
} |
|
else |
|
{ |
|
updateType = DATA_UPDATE_CREATED; |
|
pNet = pClientClass->m_pCreateFn( iEnt, iSerialNum ); |
|
bCreated = true; |
|
m_EntsCreatedIndices[m_nEntsCreated++] = iEnt; |
|
|
|
pCached->m_iSerialNumber = iSerialNum; |
|
pCached->m_pDataPointer = pNet->GetDataTableBasePtr(); |
|
pCached->m_pNetworkable = pNet; |
|
// Tracker 73192: ywb 8/1/07: We used to get an assertion that the pCached->m_bDormant was not equal to pNet->IsDormant() in ProcessDormantEntities. |
|
// This appears to be the case if when we get here, the entity is set for Transmit still, but is a dormant entity on the server. |
|
// Seems safe to go ahead an fill in the cache with the correct data. Probably was just an oversight. |
|
pCached->m_bDormant = pNet->IsDormant(); |
|
} |
|
|
|
if ( bChanged || bCreated || bExistedAndWasDormant ) |
|
{ |
|
pNet->PreDataUpdate( updateType ); |
|
|
|
Assert( pCached->m_pDataPointer == pNet->GetDataTableBasePtr() ); |
|
|
|
LocalTransfer_TransferEntity( |
|
&sv.edicts[iEnt], |
|
pSendTable, |
|
pSourceEnt, |
|
pClientClass->m_pRecvTable, |
|
pCached->m_pDataPointer, |
|
bCreated, |
|
bExistedAndWasDormant, |
|
iEnt ); |
|
|
|
if ( bExistedAndWasDormant ) |
|
{ |
|
// Set this so we use DATA_UPDATE_CREATED logic |
|
m_EntsCreatedIndices[m_nEntsCreated++] = iEnt; |
|
} |
|
else |
|
{ |
|
if ( !bCreated ) |
|
{ |
|
m_EntsChangedIndices[m_nEntsChanged++] = iEnt; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
void CLocalNetworkBackdoor::ClearState() |
|
{ |
|
// Clear the cache for all the entities. |
|
for ( int i=0; i < MAX_EDICTS; i++ ) |
|
{ |
|
CCachedEntState &ces = m_CachedEntState[i]; |
|
|
|
ces.m_pNetworkable = NULL; |
|
ces.m_iSerialNumber = -1; |
|
ces.m_bDormant = false; |
|
ces.m_pDataPointer = NULL; |
|
} |
|
|
|
m_PrevEntsAlive.ClearAll(); |
|
} |
|
|
|
void CLocalNetworkBackdoor::StartBackdoorMode() |
|
{ |
|
ClearState(); |
|
|
|
for ( int i=0; i < MAX_EDICTS; i++ ) |
|
{ |
|
IClientNetworkable *pNet = entitylist->GetClientNetworkable( i ); |
|
|
|
CCachedEntState &ces = m_CachedEntState[i]; |
|
|
|
if ( pNet ) |
|
{ |
|
ces.m_pNetworkable = pNet; |
|
ces.m_iSerialNumber = pNet->GetIClientUnknown()->GetRefEHandle().GetSerialNumber(); |
|
ces.m_bDormant = pNet->IsDormant(); |
|
ces.m_pDataPointer = pNet->GetDataTableBasePtr(); |
|
m_PrevEntsAlive.Set( i ); |
|
} |
|
} |
|
} |
|
|
|
void CLocalNetworkBackdoor::StopBackdoorMode() |
|
{ |
|
ClearState(); |
|
} |
|
|
|
|