mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-08 01:39:36 +00:00
1
This commit is contained in:
@@ -0,0 +1,420 @@
|
||||
//========= 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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user