Counter Strike : Global Offensive Source Code
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.

963 lines
34 KiB

  1. //========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "server_pch.h"
  8. #include "client.h"
  9. #include "sv_packedentities.h"
  10. #include "bspfile.h"
  11. #include "eiface.h"
  12. #include "dt_send_eng.h"
  13. #include "dt_common_eng.h"
  14. #include "changeframelist.h"
  15. #include "sv_main.h"
  16. #include "hltvserver.h"
  17. #if defined( REPLAY_ENABLED )
  18. #include "replayserver.h"
  19. #endif
  20. #include "dt_instrumentation_server.h"
  21. #include "LocalNetworkBackdoor.h"
  22. #include "tier0/vprof.h"
  23. #include "host.h"
  24. #include "networkstringtableserver.h"
  25. #include "networkstringtable.h"
  26. #include "utlbuffer.h"
  27. #include "dt.h"
  28. #include "con_nprint.h"
  29. #include "smooth_average.h"
  30. #include "vengineserver_impl.h"
  31. #include "vstdlib/jobthread.h"
  32. #include "enginethreads.h"
  33. #include "networkvar.h"
  34. #include "serializedentity.h"
  35. #ifdef DEDICATED
  36. IClientEntityList *entitylist = NULL;
  37. #endif
  38. // memdbgon must be the last include file in a .cpp file!!!
  39. #include "tier0/memdbgon.h"
  40. ConVar sv_validate_edict_change_infos( "sv_validate_edict_change_infos", "0", FCVAR_RELEASE, "Verify that edict changeinfos are being calculated properly (used to debug local network backdoor mode)." );
  41. ConVar sv_enable_delta_packing( "sv_enable_delta_packing", "0", FCVAR_RELEASE, "When enabled, this allows for entity packing to use the property changes for building up the data. This is many times faster, but can be disabled for error checking." );
  42. ConVar sv_debugmanualmode( "sv_debugmanualmode", "0", FCVAR_RELEASE, "Make sure entities correctly report whether or not their network data has changed." );
  43. static struct SPackedEntityStats
  44. {
  45. uint32 m_numFastPathEncodes;
  46. uint32 m_numSlowPathEncodes;
  47. } g_PackedEntityStats;
  48. // Returns false and calls Host_Error if the edict's pvPrivateData is NULL.
  49. static inline bool SV_EnsurePrivateData(edict_t *pEdict)
  50. {
  51. if(pEdict->GetUnknown())
  52. {
  53. return true;
  54. }
  55. else
  56. {
  57. Host_Error("SV_EnsurePrivateData: pEdict->pvPrivateData==NULL (ent %d).\n", pEdict - sv.edicts);
  58. return false;
  59. }
  60. }
  61. // This function makes sure that this entity class has an instance baseline.
  62. // If it doesn't have one yet, it makes a new one.
  63. void SV_EnsureInstanceBaseline( ServerClass *pServerClass, int iEdict, SerializedEntityHandle_t handle )
  64. {
  65. edict_t *pEnt = &sv.edicts[iEdict];
  66. ErrorIfNot( pEnt->GetNetworkable(),
  67. ("SV_EnsureInstanceBaseline: edict %d missing ent", iEdict)
  68. );
  69. ServerClass *pClass = pEnt->GetNetworkable()->GetServerClass();
  70. // See if we already have a baseline for this class.
  71. if ( pClass->m_InstanceBaselineIndex == INVALID_STRING_INDEX )
  72. {
  73. AUTO_LOCK_FM( g_svInstanceBaselineMutex );
  74. // We need this second check in case multiple instances of the same class have grabbed the lock.
  75. if ( pClass->m_InstanceBaselineIndex == INVALID_STRING_INDEX )
  76. {
  77. char packedData[ MAX_PACKEDENTITY_DATA ];
  78. bf_write buf( "SV_EnsureInstanceBaseline", packedData, sizeof( packedData ) );
  79. // Write all fields (NULL) change list
  80. SendTable_WritePropList( pServerClass->m_pTable, handle, &buf, iEdict, NULL );
  81. char idString[32];
  82. Q_snprintf( idString, sizeof( idString ), "%d", pClass->m_ClassID );
  83. // Ok, make a new instance baseline so they can reference it.
  84. int temp = sv.GetInstanceBaselineTable()->AddString(
  85. true, idString, // Note we're sending a string with the ID number, not the class name.
  86. buf.GetNumBytesWritten(), packedData );
  87. Assert( temp != INVALID_STRING_INDEX );
  88. // Copy the baseline data into the handles table
  89. sv.m_BaselineHandles.Insert( temp, g_pSerializedEntities->CopySerializedEntity( handle, __FILE__, __LINE__ ) );
  90. // Insert a compiler and/or CPU memory barrier to ensure that all side-effects have
  91. // been published before the index is published. Otherwise the string index may
  92. // be visible before its initialization has finished. This potential problem is caused
  93. // by the use of double-checked locking -- the problem is that the code outside of the
  94. // lock is looking at the variable that is protected by the lock. See this article for details:
  95. // http://en.wikipedia.org/wiki/Double-checked_locking
  96. // Write-release barrier
  97. ThreadMemoryBarrier();
  98. pClass->m_InstanceBaselineIndex = temp;
  99. }
  100. }
  101. // Read-acquire barrier. This should be safe to omit because of dependencies
  102. // which enforce read ordering.
  103. //ThreadMemoryBarrier();
  104. }
  105. static inline bool DoesEdictChangeInfoContainPropIndex( SendTable *pSendTable, const CEdictChangeInfo *pCI, int nProp )
  106. {
  107. CSendTablePrecalc *pPrecalc = pSendTable->m_pPrecalc;
  108. bool bFound = false;
  109. for ( int i=0; i < pCI->m_nChangeOffsets && !bFound; i++ )
  110. {
  111. unsigned short index = pPrecalc->m_PropOffsetToIndexMap.Find( pCI->m_ChangeOffsets[i] );
  112. if ( index == pPrecalc->m_PropOffsetToIndexMap.InvalidIndex() )
  113. continue;
  114. const PropIndicesCollection_t &coll = pPrecalc->m_PropOffsetToIndexMap[index];
  115. for ( int nIndex=0; nIndex < ARRAYSIZE( coll.m_Indices ); nIndex++ )
  116. {
  117. if ( coll.m_Indices[nIndex] == nProp )
  118. {
  119. bFound = true;
  120. break;
  121. }
  122. }
  123. }
  124. return bFound;
  125. }
  126. //given a previous frame and a change list of the delta properties, this will return a change list (either by taking it from the last frame if possible, or creating
  127. //a copy) with the specified delta properties with an updated tick count
  128. static inline CChangeFrameList * GetMergedChangeFrameList( PackedEntity* pPrevFrame, uint32 nNumProps, const CalcDeltaResultsList_t& ChangeList, uint32 nTick )
  129. {
  130. //if we have a previous frame, we need to try and reuse it's change list, either as a copy or a baseline
  131. CChangeFrameList *pChangeFrame = NULL;
  132. if( pPrevFrame )
  133. {
  134. CActiveHltvServerIterator hltv;
  135. #ifndef _XBOX
  136. #if defined( REPLAY_ENABLED )
  137. if ( hltv || (replay && replay->IsActive()) )
  138. #else
  139. if ( hltv )
  140. #endif
  141. {
  142. // in HLTV or Replay mode every PackedEntity keeps it's own ChangeFrameList
  143. // we just copy the ChangeFrameList from prev frame and update it
  144. pChangeFrame = pPrevFrame->GetChangeFrameList()->Copy();
  145. }
  146. else
  147. #endif
  148. {
  149. // Ok, now snag the changeframe from the previous frame and update the 'last frame changed'
  150. // for the properties in the delta.
  151. pChangeFrame = pPrevFrame->SnagChangeFrameList();
  152. }
  153. ErrorIfNot( pChangeFrame, ("SV_PackEntity: SnagChangeFrameList returned null") );
  154. ErrorIfNot( pChangeFrame->GetNumProps() == nNumProps,
  155. ("SV_PackEntity: SnagChangeFrameList mismatched number of props[%d vs %d]", nNumProps, pChangeFrame->GetNumProps() ) );
  156. pChangeFrame->SetChangeTick( ChangeList.Base(), ChangeList.Count(), nTick );
  157. }
  158. else
  159. {
  160. pChangeFrame = new CChangeFrameList( nNumProps, nTick );
  161. }
  162. return pChangeFrame;
  163. }
  164. static void ValidateIncrementalChanges( ServerClass* pServerClass, const CalcDeltaResultsList_t& incremental, edict_t* edict, int edictIdx, SerializedEntityHandle_t tPrevProps )
  165. {
  166. const SendTable *pSendTable = pServerClass->m_pTable;
  167. unsigned char tempData[ sizeof( CSendProxyRecipients ) * MAX_DATATABLE_PROXIES ];
  168. CUtlMemory< CSendProxyRecipients > recip( (CSendProxyRecipients*)tempData, pSendTable->m_pPrecalc->GetNumDataTableProxies() );
  169. //debug path, useful for detecting properties that differ from the binary diff and the change list
  170. SerializedEntityHandle_t comparePackedProps = g_pSerializedEntities->AllocateSerializedEntity(__FILE__, __LINE__);
  171. CalcDeltaResultsList_t compareDeltaProps;
  172. //generate a change list using the normal route
  173. SendTable_Encode( pSendTable, comparePackedProps, edict->GetUnknown(), edictIdx, &recip );
  174. SendTable_CalcDelta( pSendTable, tPrevProps, comparePackedProps, edictIdx, compareDeltaProps );
  175. //check to see if we have any missing properties or differing properties
  176. for( uint32 nCurrIncremental = 0; nCurrIncremental < incremental.Count(); ++nCurrIncremental )
  177. {
  178. int nPropIndex = incremental[ nCurrIncremental ];
  179. if( compareDeltaProps.Find( nPropIndex ) == -1 )
  180. {
  181. //missing a prop from our diff
  182. const SendProp* pProp = pSendTable->m_pPrecalc->GetProp( nPropIndex );
  183. Msg( "SV_PackEntity: Encountered property %s:%s for class %s (idx:%d) in incremental change that was not in the diff change. This can lead to slight network inefficiency (Index: %d, Array: %s)\n",
  184. pSendTable->GetName(), pProp->GetName(), pServerClass->GetName(), edictIdx, nPropIndex, (pProp->m_pParentArrayPropName ? pProp->m_pParentArrayPropName : "N/A") );
  185. }
  186. }
  187. for( uint32 nCurrDiff = 0; nCurrDiff < compareDeltaProps.Count(); ++nCurrDiff )
  188. {
  189. int nPropIndex = compareDeltaProps[ nCurrDiff ];
  190. if( incremental.Find( nPropIndex ) == -1 )
  191. {
  192. //missing a prop from our incremental
  193. const SendProp* pProp = pSendTable->m_pPrecalc->GetProp( nPropIndex );
  194. //some properties are encoded against the tick count, of which can change with out the property changing. However, the value on the receiving end stays constants, so we need to
  195. //be able to filter these out
  196. if( pProp->GetFlags() & SPROP_ENCODED_AGAINST_TICKCOUNT )
  197. continue;
  198. Msg( "SV_PackEntity: Encountered property %s:%s for class %s (idx:%d) in diff change that was not in the incremental change. This can lead to values not making it down to the client (Index: %d, Array: %s)\n",
  199. pSendTable->GetName(), pProp->GetName(), pServerClass->GetName(), edictIdx, nPropIndex, (pProp->m_pParentArrayPropName ? pProp->m_pParentArrayPropName : "N/A") );
  200. }
  201. }
  202. g_pSerializedEntities->ReleaseSerializedEntity( comparePackedProps );
  203. }
  204. //-----------------------------------------------------------------------------
  205. // Pack the entity....
  206. //-----------------------------------------------------------------------------
  207. static inline void SV_PackEntity(
  208. int edictIdx,
  209. edict_t* edict,
  210. ServerClass* pServerClass,
  211. CFrameSnapshot *pSnapshot )
  212. {
  213. Assert( edictIdx < pSnapshot->m_nNumEntities );
  214. MEM_ALLOC_CREDIT();
  215. const int iSerialNum = pSnapshot->m_pEntities[ edictIdx ].m_nSerialNumber;
  216. //if the entity has no state changes, we can just reuse the previously sent packet
  217. if( !edict->HasStateChanged() )
  218. {
  219. // Now this may not work if we didn't previously send a packet;
  220. // if not, then we gotta compute it
  221. bool bUsedPrev = framesnapshotmanager->UsePreviouslySentPacket( pSnapshot, edictIdx, iSerialNum );
  222. if ( bUsedPrev && !sv_debugmanualmode.GetInt() )
  223. {
  224. edict->ClearStateChanged();
  225. return;
  226. }
  227. }
  228. //try and get our last frame
  229. PackedEntity *pPrevFrame = framesnapshotmanager->GetPreviouslySentPacket( edictIdx, iSerialNum );
  230. const SendTable *pSendTable = pServerClass->m_pTable;
  231. //memory to hold onto our recipient list on the stack to avoid allocator overhead.
  232. unsigned char tempData[ sizeof( CSendProxyRecipients ) * MAX_DATATABLE_PROXIES ];
  233. CUtlMemory< CSendProxyRecipients > recip( (CSendProxyRecipients*)tempData, pSendTable->m_pPrecalc->GetNumDataTableProxies() );
  234. //the new packed up properties and the delta list from the last frame we will be building
  235. SerializedEntityHandle_t newPackedProps = SERIALIZED_ENTITY_HANDLE_INVALID;
  236. CalcDeltaResultsList_t deltaProps;
  237. //--------------------------------------
  238. // Fast path - Partial updating (catches about 95% of entities)
  239. //--------------------------------------
  240. //see if we can take the fast path (requires a previous frame)
  241. bool bCanFastPath = ( pPrevFrame && ( pPrevFrame->GetPackedData() != SERIALIZED_ENTITY_HANDLE_INVALID ) );
  242. //also, we can't fast path if the entire object has changed
  243. bCanFastPath &= !( edict->m_fStateFlags & FL_FULL_EDICT_CHANGED );
  244. //and we need a valid change list set
  245. bCanFastPath &= ( edict->GetChangeInfoSerialNumber() == g_pSharedChangeInfo->m_iSerialNumber );
  246. //and allow for disabling this through the console
  247. bCanFastPath &= ( sv_enable_delta_packing.GetBool() || sv_validate_edict_change_infos.GetBool() );
  248. //see if we can do an incremental update from our last frame
  249. if( bCanFastPath )
  250. {
  251. //see if we are in a situation where we are just doing diagnostics, meaning that we want to go through the work, but ultimately throw the results away after validating them
  252. bool bDiscardResults = !( sv_enable_delta_packing.GetBool() );
  253. //we can attempt a partial update
  254. bool bCanReuseOldData;
  255. newPackedProps = SendTable_BuildDeltaProperties( edict, edictIdx, pSendTable, pPrevFrame->GetPackedData(), deltaProps, &recip, bCanReuseOldData );
  256. //see if we were unable to merge (which may have been a result of us being able to reuse the original data)
  257. if( ( newPackedProps == SERIALIZED_ENTITY_HANDLE_INVALID ) )
  258. {
  259. //it wasn't able to overlay, so we need to fall back to the old way, unless it says nothing changed, at which point we can just reuse
  260. if( bCanReuseOldData && pPrevFrame->CompareRecipients( recip ) )
  261. {
  262. if( framesnapshotmanager->UsePreviouslySentPacket( pSnapshot, edictIdx, iSerialNum ) )
  263. {
  264. //we are going to use the previous, but make sure that there is no delta that we are missing
  265. if( sv_validate_edict_change_infos.GetBool() )
  266. ValidateIncrementalChanges( pServerClass, deltaProps, edict, edictIdx, pPrevFrame->GetPackedData() );
  267. //successfully used the last one
  268. edict->ClearStateChanged();
  269. return;
  270. }
  271. }
  272. }
  273. else
  274. {
  275. ++g_PackedEntityStats.m_numFastPathEncodes;
  276. if( sv_validate_edict_change_infos.GetBool() )
  277. {
  278. ValidateIncrementalChanges( pServerClass, deltaProps, edict, edictIdx, pPrevFrame->GetPackedData() );
  279. //we need to throw away our data if this is just for diagnostics
  280. if( bDiscardResults )
  281. {
  282. g_pSerializedEntities->ReleaseSerializedEntity( newPackedProps );
  283. newPackedProps = SERIALIZED_ENTITY_HANDLE_INVALID;
  284. }
  285. }
  286. }
  287. }
  288. //--------------------------------------
  289. // Slow path - Must build a full data block and do a binary diff to spot the changes
  290. //--------------------------------------
  291. //if we made it through the fast path with no packed data, we need to pack it via the slow path
  292. if( newPackedProps == SERIALIZED_ENTITY_HANDLE_INVALID )
  293. {
  294. ++g_PackedEntityStats.m_numSlowPathEncodes;
  295. //build all the properties for this object into a serialized entity data block
  296. newPackedProps = g_pSerializedEntities->AllocateSerializedEntity(__FILE__, __LINE__);
  297. if( !SendTable_Encode( pSendTable, newPackedProps, edict->GetUnknown(), edictIdx, &recip ) )
  298. {
  299. Host_Error( "SV_PackEntity: SendTable_Encode returned false (ent %d).\n", edictIdx );
  300. }
  301. // A "SetOnly", etc. for indicating the entity should be sent to player B who is a piggybacked split screen player sharing the network pipe for for player A, needs to be changed to go to player player A
  302. SV_EnsureInstanceBaseline( pServerClass, edictIdx, newPackedProps );
  303. // If this entity was previously in there, then it should have a valid CChangeFrameList
  304. // which we can delta against to figure out which properties have changed.
  305. if( pPrevFrame )
  306. {
  307. //do a binary diff and see which properties changed that way
  308. SendTable_CalcDelta( pSendTable, pPrevFrame->GetPackedData(), newPackedProps, edictIdx, deltaProps );
  309. // If it's non-manual-mode, but we detect that there are no changes here, then just
  310. // use the previous pSnapshot if it's available (as though the entity were manual mode).
  311. if ( ( deltaProps.Count() == 0 ) && pPrevFrame->CompareRecipients( recip ) )
  312. {
  313. if ( framesnapshotmanager->UsePreviouslySentPacket( pSnapshot, edictIdx, iSerialNum ) )
  314. {
  315. edict->ClearStateChanged();
  316. g_pSerializedEntities->ReleaseSerializedEntity( newPackedProps );
  317. return;
  318. }
  319. }
  320. }
  321. }
  322. // Now make a PackedEntity and store the new packed data in there.
  323. {
  324. //get our change frame list for our new property set
  325. int nFlatProps = SendTable_GetNumFlatProps( pSendTable );
  326. CChangeFrameList* pChangeFrame = GetMergedChangeFrameList( pPrevFrame, nFlatProps, deltaProps, pSnapshot->m_nTickCount );
  327. //and setup our packed entity to hold all of our data
  328. PackedEntity *pPackedEntity = framesnapshotmanager->CreatePackedEntity( pSnapshot, edictIdx );
  329. pPackedEntity->SetChangeFrameList( pChangeFrame );
  330. pPackedEntity->SetServerAndClientClass( pServerClass, NULL );
  331. pPackedEntity->SetPackedData( newPackedProps );
  332. pPackedEntity->SetRecipients( recip );
  333. }
  334. edict->ClearStateChanged();
  335. }
  336. CON_COMMAND( sv_dump_entity_pack_stats, "Show stats on entity packing." )
  337. {
  338. Msg("Entity Packing stats:\n");
  339. Msg(" numFastPathEncodes=%u\n", g_PackedEntityStats.m_numFastPathEncodes );
  340. Msg(" numSlowPathEncodes=%u\n", g_PackedEntityStats.m_numSlowPathEncodes );
  341. }
  342. // in HLTV mode we ALWAYS have to store position and PVS info, even if entity didnt change
  343. void SV_FillHLTVData( CFrameSnapshot *pSnapshot, edict_t *edict, int iValidEdict )
  344. {
  345. if ( pSnapshot->m_pHLTVEntityData && edict )
  346. {
  347. CHLTVEntityData *pHLTVData = &pSnapshot->m_pHLTVEntityData[iValidEdict];
  348. PVSInfo_t *pvsInfo = edict->GetNetworkable()->GetPVSInfo();
  349. if ( pvsInfo->m_nClusterCount == 1 )
  350. {
  351. // store cluster, if entity spawns only over one cluster
  352. pHLTVData->m_nNodeCluster = pvsInfo->m_pClusters[0];
  353. }
  354. else
  355. {
  356. // otherwise save PVS head node for larger entities
  357. pHLTVData->m_nNodeCluster = pvsInfo->m_nHeadNode | (1<<31);
  358. }
  359. // remember origin
  360. pHLTVData->origin[0] = pvsInfo->m_vCenter[0];
  361. pHLTVData->origin[1] = pvsInfo->m_vCenter[1];
  362. pHLTVData->origin[2] = pvsInfo->m_vCenter[2];
  363. }
  364. }
  365. #if defined( REPLAY_ENABLED )
  366. // in Replay mode we ALWAYS have to store position and PVS info, even if entity didnt change
  367. void SV_FillReplayData( CFrameSnapshot *pSnapshot, edict_t *edict, int iValidEdict )
  368. {
  369. if ( pSnapshot->m_pReplayEntityData && edict )
  370. {
  371. CReplayEntityData *pReplayData = &pSnapshot->m_pReplayEntityData[iValidEdict];
  372. PVSInfo_t *pvsInfo = edict->GetNetworkable()->GetPVSInfo();
  373. if ( pvsInfo->m_nClusterCount == 1 )
  374. {
  375. // store cluster, if entity spawns only over one cluster
  376. pReplayData->m_nNodeCluster = pvsInfo->m_pClusters[0];
  377. }
  378. else
  379. {
  380. // otherwise save PVS head node for larger entities
  381. pReplayData->m_nNodeCluster = pvsInfo->m_nHeadNode | (1<<31);
  382. }
  383. // remember origin
  384. pReplayData->origin[0] = pvsInfo->m_vCenter[0];
  385. pReplayData->origin[1] = pvsInfo->m_vCenter[1];
  386. pReplayData->origin[2] = pvsInfo->m_vCenter[2];
  387. }
  388. }
  389. #endif
  390. // Returns the SendTable that should be used with the specified edict.
  391. SendTable* GetEntSendTable(edict_t *pEdict)
  392. {
  393. if ( pEdict->GetNetworkable() )
  394. {
  395. ServerClass *pClass = pEdict->GetNetworkable()->GetServerClass();
  396. if ( pClass )
  397. {
  398. return pClass->m_pTable;
  399. }
  400. }
  401. return NULL;
  402. }
  403. void PackEntities_NetworkBackDoor(
  404. int clientCount,
  405. CGameClient **clients,
  406. CFrameSnapshot *snapshot )
  407. {
  408. Assert( clientCount == 1 );
  409. CGameClient *client = clients[0]; // update variables cl, pInfo, frame for current client
  410. CCheckTransmitInfo *pInfo = &client->m_PackInfo;
  411. //Msg ( " Using local network back door" );
  412. for ( int iValidEdict=0; iValidEdict < snapshot->m_nValidEntities; iValidEdict++ )
  413. {
  414. int index = snapshot->m_pValidEntities[iValidEdict];
  415. edict_t* edict = &sv.edicts[ index ];
  416. // this is a bit of a hack to ensure that we get a "preview" of the
  417. // packet timstamp that the server will send so that things that
  418. // are encoded relative to packet time will be correct
  419. Assert( edict->m_NetworkSerialNumber != -1 );
  420. bool bShouldTransmit = pInfo->m_pTransmitEdict->Get( index ) ? true : false;
  421. //CServerDTITimer timer( pSendTable, SERVERDTI_ENCODE );
  422. // If we're using the fast path for a single-player game, just pass the entity
  423. // directly over to the client.
  424. Assert( index < snapshot->m_nNumEntities );
  425. ServerClass *pSVClass = snapshot->m_pEntities[ index ].m_pClass;
  426. g_pLocalNetworkBackdoor->EntState( index, edict->m_NetworkSerialNumber,
  427. pSVClass->m_ClassID, pSVClass->m_pTable, edict->GetUnknown(), edict->HasStateChanged(), bShouldTransmit );
  428. edict->ClearStateChanged();
  429. }
  430. // Tell the client about any entities that are now dormant.
  431. g_pLocalNetworkBackdoor->ProcessDormantEntities();
  432. InvalidateSharedEdictChangeInfos();
  433. }
  434. static ConVar sv_parallel_packentities( "sv_parallel_packentities",
  435. #ifndef DEDICATED
  436. "1",
  437. #else //DEDICATED
  438. "1",
  439. #endif //DEDICATED
  440. FCVAR_RELEASE );
  441. struct PackWork_t
  442. {
  443. int nIdx;
  444. edict_t *pEdict;
  445. CFrameSnapshot *pSnapshot;
  446. static void Process( PackWork_t &item )
  447. {
  448. SV_PackEntity( item.nIdx, item.pEdict, item.pSnapshot->m_pEntities[ item.nIdx ].m_pClass, item.pSnapshot );
  449. }
  450. };
  451. void PackEntities_Normal(
  452. int clientCount,
  453. CGameClient **clients,
  454. CFrameSnapshot *snapshot )
  455. {
  456. SNPROF("PackEntities_Normal");
  457. Assert( snapshot->m_nValidEntities <= MAX_EDICTS );
  458. PackWork_t workItems[MAX_EDICTS];
  459. int workItemCount = 0;
  460. // check for all active entities, if they are seen by at least on client, if
  461. // so, bit pack them
  462. for ( int iValidEdict=0; iValidEdict < snapshot->m_nValidEntities; ++iValidEdict )
  463. {
  464. int index = snapshot->m_pValidEntities[ iValidEdict ];
  465. Assert( index < snapshot->m_nNumEntities );
  466. edict_t* edict = &sv.edicts[ index ];
  467. // if HLTV is running save PVS info for each entity
  468. SV_FillHLTVData( snapshot, edict, iValidEdict );
  469. #if defined( REPLAY_ENABLED )
  470. // if Replay is running save PVS info for each entity
  471. SV_FillReplayData( snapshot, edict, iValidEdict );
  472. #endif
  473. // Check to see if the entity changed this frame...
  474. //ServerDTI_RegisterNetworkStateChange( pSendTable, ent->m_bStateChanged );
  475. for ( int iClient = 0; iClient < clientCount; ++iClient )
  476. {
  477. // entities is seen by at least this client, pack it and exit loop
  478. CGameClient *client = clients[iClient]; // update variables cl, pInfo, frame for current client
  479. if ( client->IsHltvReplay() )
  480. continue; // clients in HLTV replay use HLTV stream that has already been pre-packed for them by HLTV master client. No need to do any packing while streaming HLTV contents
  481. CClientFrame *frame = client->m_pCurrentFrame;
  482. if( frame->transmit_entity.Get( index ) )
  483. {
  484. workItems[workItemCount].nIdx = index;
  485. workItems[workItemCount].pEdict = edict;
  486. workItems[workItemCount].pSnapshot = snapshot;
  487. workItemCount++;
  488. break;
  489. }
  490. }
  491. }
  492. // Process work
  493. if ( sv_parallel_packentities.GetBool() )
  494. {
  495. ParallelProcess( workItems, workItemCount, &PackWork_t::Process );
  496. }
  497. else
  498. {
  499. int c = workItemCount;
  500. for ( int i = 0; i < c; ++i )
  501. {
  502. PackWork_t &w = workItems[ i ];
  503. SV_PackEntity( w.nIdx, w.pEdict, w.pSnapshot->m_pEntities[ w.nIdx ].m_pClass, w.pSnapshot );
  504. }
  505. }
  506. InvalidateSharedEdictChangeInfos();
  507. }
  508. //-----------------------------------------------------------------------------
  509. // Writes the compressed packet of entities to all clients
  510. //-----------------------------------------------------------------------------
  511. void SV_ComputeClientPacks(
  512. int clientCount,
  513. CGameClient **clients,
  514. CFrameSnapshot *snapshot )
  515. {
  516. SNPROF( "SV_ComputeClientPacks" );
  517. MDLCACHE_CRITICAL_SECTION_(g_pMDLCache);
  518. // Do some setup for each client
  519. {
  520. for (int iClient = 0; iClient < clientCount; ++iClient)
  521. {
  522. TM_ZONE( TELEMETRY_LEVEL1, TMZF_NONE, "CheckTransmit:%d", iClient );
  523. if ( clients[ iClient ]->IsHltvReplay() )
  524. {
  525. clients[ iClient ]->SetupHltvFrame( snapshot->m_nTickCount);
  526. continue; // skip all the transmit checks if we already have packs prepared by HLTV that we'll be sending anyway
  527. }
  528. CCheckTransmitInfo *pInfo = &clients[iClient]->m_PackInfo;
  529. clients[iClient]->SetupPackInfo( snapshot );
  530. if ( clients[iClient]->m_nDeltaTick < 0 )
  531. {
  532. serverGameEnts->PrepareForFullUpdate( clients[iClient]->edict );
  533. }
  534. serverGameEnts->CheckTransmit( pInfo, snapshot->m_pValidEntities, snapshot->m_nValidEntities );
  535. clients[iClient]->SetupPrevPackInfo();
  536. }
  537. }
  538. if ( g_pLocalNetworkBackdoor )
  539. {
  540. #if !defined( DEDICATED )
  541. if ( GetBaseLocalClient().m_pServerClasses == NULL )
  542. {
  543. // Edge case - the local client has been cleaned up but we have a deferred tick to execute.
  544. // As far as I know, this can only happen if the player quits the game with a synchronous cbuf_execute while a server tick is in flight.
  545. // (e.g. user pauses the game, files a bug with vxconsole, then vxconsole unpauses the game while the user is still in the menu, then the user quits -> CRASH)
  546. Warning( "Attempting to pack entities on server with invalid local client state. Probably a result of VXConsole or con commands. Aborting SV_ComputeClientPacks.\n" );
  547. return;
  548. }
  549. #endif
  550. // This will force network string table updates for local client to go through the backdoor if it's active
  551. #ifdef SHARED_NET_STRING_TABLES
  552. sv.m_StringTables->TriggerCallbacks( clients[0]->m_nDeltaTick );
  553. #else
  554. sv.m_StringTables->DirectUpdate( clients[0]->GetMaxAckTickCount() );
  555. #endif
  556. g_pLocalNetworkBackdoor->StartEntityStateUpdate();
  557. #ifndef DEDICATED
  558. int saveClientTicks = GetBaseLocalClient().GetClientTickCount();
  559. int saveServerTicks = GetBaseLocalClient().GetServerTickCount();
  560. bool bSaveSimulation = GetBaseLocalClient().insimulation;
  561. float flSaveLastServerTickTime = GetBaseLocalClient().m_flLastServerTickTime;
  562. GetBaseLocalClient().insimulation = true;
  563. GetBaseLocalClient().SetClientTickCount( sv.m_nTickCount );
  564. GetBaseLocalClient().SetServerTickCount( sv.m_nTickCount );
  565. GetBaseLocalClient().m_flLastServerTickTime = sv.m_nTickCount * host_state.interval_per_tick;
  566. g_ClientGlobalVariables.tickcount = GetBaseLocalClient().GetClientTickCount();
  567. g_ClientGlobalVariables.curtime = GetBaseLocalClient().GetTime();
  568. #endif
  569. PackEntities_NetworkBackDoor( clientCount, clients, snapshot );
  570. g_pLocalNetworkBackdoor->EndEntityStateUpdate();
  571. #ifndef DEDICATED
  572. GetBaseLocalClient().SetClientTickCount( saveClientTicks );
  573. GetBaseLocalClient().SetServerTickCount( saveServerTicks );
  574. GetBaseLocalClient().insimulation = bSaveSimulation;
  575. GetBaseLocalClient().m_flLastServerTickTime = flSaveLastServerTickTime;
  576. g_ClientGlobalVariables.tickcount = GetBaseLocalClient().GetClientTickCount();
  577. g_ClientGlobalVariables.curtime = GetBaseLocalClient().GetTime();
  578. #endif
  579. PrintPartialChangeEntsList();
  580. }
  581. else
  582. {
  583. PackEntities_Normal( clientCount, clients, snapshot );
  584. }
  585. }
  586. // If the table's ID is -1, writes its info into the buffer and increments curID.
  587. void SV_MaybeWriteSendTable( SendTable *pTable, bf_write &pBuf, bool bNeedDecoder )
  588. {
  589. // Already sent?
  590. if ( pTable->GetWriteFlag() )
  591. return;
  592. pTable->SetWriteFlag( true );
  593. // write send table properties into stream
  594. if ( !SendTable_WriteInfos(pTable, pBuf, bNeedDecoder, false ) )
  595. {
  596. Host_Error( "Send Table buffer for class '%s' overflowed!!!\n", pTable->GetName() );
  597. }
  598. }
  599. // Calls SV_MaybeWriteSendTable recursively.
  600. void SV_MaybeWriteSendTable_R( SendTable *pTable, bf_write &pBuf )
  601. {
  602. SV_MaybeWriteSendTable( pTable, pBuf, false );
  603. // Make sure we send child send tables..
  604. for(int i=0; i < pTable->m_nProps; i++)
  605. {
  606. SendProp *pProp = &pTable->m_pProps[i];
  607. if( pProp->m_Type == DPT_DataTable )
  608. SV_MaybeWriteSendTable_R( pProp->GetDataTable(), pBuf );
  609. }
  610. }
  611. // Sets up SendTable IDs and sends an svc_sendtable for each table.
  612. void SV_WriteSendTables( ServerClass *pClasses, bf_write &pBuf )
  613. {
  614. ServerClass *pCur;
  615. DataTable_ClearWriteFlags( pClasses );
  616. // First, we send all the leaf classes. These are the ones that will need decoders
  617. // on the client.
  618. for ( pCur=pClasses; pCur; pCur=pCur->m_pNext )
  619. {
  620. SV_MaybeWriteSendTable( pCur->m_pTable, pBuf, true );
  621. }
  622. // Now, we send their base classes. These don't need decoders on the client
  623. // because we will never send these SendTables by themselves.
  624. for ( pCur=pClasses; pCur; pCur=pCur->m_pNext )
  625. {
  626. SV_MaybeWriteSendTable_R( pCur->m_pTable, pBuf );
  627. }
  628. }
  629. //-----------------------------------------------------------------------------
  630. // Purpose:
  631. // Input : crc -
  632. //-----------------------------------------------------------------------------
  633. void SV_ComputeClassInfosCRC( CRC32_t* crc )
  634. {
  635. ServerClass *pClasses = serverGameDLL->GetAllServerClasses();
  636. for ( ServerClass *pClass=pClasses; pClass; pClass=pClass->m_pNext )
  637. {
  638. CRC32_ProcessBuffer( crc, (void *)pClass->m_pNetworkName, Q_strlen( pClass->m_pNetworkName ) );
  639. CRC32_ProcessBuffer( crc, (void *)pClass->m_pTable->GetName(), Q_strlen(pClass->m_pTable->GetName() ) );
  640. }
  641. }
  642. void CGameServer::AssignClassIds()
  643. {
  644. ServerClass *pClasses = serverGameDLL->GetAllServerClasses();
  645. // Count the number of classes.
  646. int nClasses = 0;
  647. for ( ServerClass *pCount=pClasses; pCount; pCount=pCount->m_pNext )
  648. {
  649. ++nClasses;
  650. }
  651. // These should be the same! If they're not, then it should detect an explicit create message.
  652. ErrorIfNot( nClasses <= MAX_SERVER_CLASSES,
  653. ("CGameServer::AssignClassIds: too many server classes (%i, MAX = %i).\n", nClasses, MAX_SERVER_CLASSES );
  654. );
  655. serverclasses = nClasses;
  656. serverclassbits = Q_log2( serverclasses ) + 1;
  657. bool bSpew = CommandLine()->FindParm( "-netspike" ) ? true : false;
  658. int curID = 0;
  659. for ( ServerClass *pClass=pClasses; pClass; pClass=pClass->m_pNext )
  660. {
  661. pClass->m_ClassID = curID++;
  662. if ( bSpew )
  663. {
  664. Msg( "%d == '%s'\n", pClass->m_ClassID, pClass->GetName() );
  665. }
  666. }
  667. }
  668. // Assign each class and ID and write an svc_classinfo for each one.
  669. void SV_WriteClassInfos(ServerClass *pClasses, bf_write &pBuf)
  670. {
  671. // Assert( sv.serverclasses < MAX_SERVER_CLASSES );
  672. CSVCMsg_ClassInfo_t classinfomsg;
  673. classinfomsg.set_create_on_client( false );
  674. for ( ServerClass *pClass=pClasses; pClass; pClass=pClass->m_pNext )
  675. {
  676. CSVCMsg_ClassInfo::class_t *svclass = classinfomsg.add_classes();
  677. svclass->set_class_id( pClass->m_ClassID );
  678. svclass->set_data_table_name( pClass->m_pTable->GetName() );
  679. svclass->set_class_name( pClass->m_pNetworkName );
  680. }
  681. classinfomsg.WriteToBuffer( pBuf );
  682. }
  683. // This is implemented for the datatable code so its warnings can include an object's classname.
  684. const char* GetObjectClassName( int objectID )
  685. {
  686. if ( objectID >= 0 && objectID < sv.num_edicts )
  687. {
  688. return sv.edicts[objectID].GetClassName();
  689. }
  690. else
  691. {
  692. return "[unknown]";
  693. }
  694. }
  695. //-----------------------------------------------------------------------------
  696. CON_COMMAND( sv_dump_class_info, "Dump server class infos." )
  697. {
  698. ServerClass *pClasses = serverGameDLL->GetAllServerClasses();
  699. Msg( "ServerClassInfo:\n");
  700. Msg( "\tNetName(TableName):nProps PreCalcProps\n");
  701. for ( ServerClass *pCount=pClasses; pCount; pCount=pCount->m_pNext )
  702. {
  703. Msg("\t%s(%s):%d %d\n", pCount->GetName(), pCount->m_pTable->GetName(), pCount->m_pTable->m_nProps, pCount->m_pTable->m_pPrecalc ? pCount->m_pTable->m_pPrecalc->GetNumProps() : 0 );
  704. }
  705. }
  706. //-----------------------------------------------------------------------------
  707. #include "tier1/tokenset.h"
  708. const tokenset_t< SendPropType > gSendPropTypeTokenSet[] =
  709. {
  710. { "Int", DPT_Int },
  711. { "Float", DPT_Float },
  712. { "Vector", DPT_Vector },
  713. { "VectorXY", DPT_VectorXY }, // Only encodes the XY of a vector, ignores Z
  714. { "String", DPT_String },
  715. { "Array", DPT_Array }, // An array of the base types (can't be of datatables).
  716. { "DataTable", DPT_DataTable },
  717. #if 0 // We can't ship this since it changes the size of DTVariant to be 20 bytes instead of 16 and that breaks MODs!!!
  718. { "Quaternion,DPT_Quaternion },
  719. #endif
  720. { "Int64", DPT_Int64 },
  721. { NULL, (SendPropType)0 }
  722. };
  723. //-----------------------------------------------------------------------------
  724. static void PrintSendProp( int index, const SendProp *pSendProp )
  725. {
  726. CUtlString flags;
  727. if ( pSendProp->GetFlags() & SPROP_VARINT ) { flags += "|VARINT"; }
  728. if ( pSendProp->GetFlags() & SPROP_UNSIGNED ) { flags += "|UNSIGNED"; }
  729. if ( pSendProp->GetFlags() & SPROP_COORD ) { flags += "|COORD"; }
  730. if ( pSendProp->GetFlags() & SPROP_NOSCALE ) { flags += "|NOSCALE"; }
  731. if ( pSendProp->GetFlags() & SPROP_ROUNDDOWN ) { flags += "|ROUNDDOWN"; }
  732. if ( pSendProp->GetFlags() & SPROP_ROUNDUP ) { flags += "|ROUNDUP"; }
  733. if ( pSendProp->GetFlags() & SPROP_NORMAL ) { flags += "|NORMAL"; }
  734. if ( pSendProp->GetFlags() & SPROP_EXCLUDE ) { flags += "|EXCLUDE"; }
  735. if ( pSendProp->GetFlags() & SPROP_XYZE ) { flags += "|XYZE"; }
  736. if ( pSendProp->GetFlags() & SPROP_INSIDEARRAY ) { flags += "|INSIDEARRAY"; }
  737. if ( pSendProp->GetFlags() & SPROP_PROXY_ALWAYS_YES ) { flags += "|PROXY_ALWAYS_YES"; }
  738. if ( pSendProp->GetFlags() & SPROP_IS_A_VECTOR_ELEM ) { flags += "|IS_A_VECTOR_ELEM"; }
  739. if ( pSendProp->GetFlags() & SPROP_COLLAPSIBLE ) { flags += "|COLLAPSIBLE"; }
  740. if ( pSendProp->GetFlags() & SPROP_COORD_MP ) { flags += "|COORD_MP"; }
  741. if ( pSendProp->GetFlags() & SPROP_COORD_MP_LOWPRECISION ) { flags += "|COORD_MP_LOWPRECISION"; }
  742. if ( pSendProp->GetFlags() & SPROP_COORD_MP_INTEGRAL ) { flags += "|COORD_MP_INTEGRAL"; }
  743. if ( pSendProp->GetFlags() & SPROP_CELL_COORD ) { flags += "|CELL_COORD"; }
  744. if ( pSendProp->GetFlags() & SPROP_CELL_COORD_LOWPRECISION ) { flags += "|CELL_COORD_LOWPRECISION"; }
  745. if ( pSendProp->GetFlags() & SPROP_CELL_COORD_INTEGRAL ) { flags += "|CELL_COORD_INTEGRAL"; }
  746. if ( pSendProp->GetFlags() & SPROP_CHANGES_OFTEN ) { flags += "|CHANGES_OFTEN"; }
  747. if ( pSendProp->GetFlags() & SPROP_ENCODED_AGAINST_TICKCOUNT ) { flags += "|ENCODED_AGAINST_TICKCOUNT"; }
  748. const char *pFlags = flags.Get();
  749. if ( !flags.IsEmpty() && *flags.Get() == '|' )
  750. {
  751. pFlags = flags.Get() + 1;
  752. }
  753. CUtlString name;
  754. if ( pSendProp->GetParentArrayPropName() )
  755. {
  756. name.Format( "%s:%s", pSendProp->GetParentArrayPropName(), pSendProp->GetName() );
  757. }
  758. else
  759. {
  760. name = pSendProp->GetName();
  761. }
  762. Msg( "\t%d,%s,%d,%d,%s,%s,%d\n",
  763. index,
  764. name.String(),
  765. pSendProp->GetOffset(),
  766. pSendProp->m_nBits,
  767. gSendPropTypeTokenSet->GetNameByToken( pSendProp->GetType() ),
  768. pFlags,
  769. pSendProp->GetPriority()
  770. );
  771. }
  772. //-----------------------------------------------------------------------------
  773. CON_COMMAND( sv_dump_class_table, "Dump server class table matching the pattern (substr)." )
  774. {
  775. if ( args.ArgC() < 2 )
  776. {
  777. Msg( "Provide a substr to match class info against.\n" );
  778. return;
  779. }
  780. ServerClass *pClasses = serverGameDLL->GetAllServerClasses();
  781. for ( int a = 1; a < args.ArgC(); ++a )
  782. {
  783. for ( ServerClass *pCount=pClasses; pCount; pCount=pCount->m_pNext )
  784. {
  785. if ( pCount->m_pTable->m_pPrecalc && Q_stristr( pCount->GetName(), args[a] ) != NULL )
  786. {
  787. Msg( "%s:\n", pCount->GetName() );
  788. Msg( "\tIndex,Name,Offset,Bits,Type,Flags,Priority\n" );
  789. const CSendTablePrecalc *pPrecalc = pCount->m_pTable->m_pPrecalc;
  790. for ( int i = 0; i < pPrecalc->GetNumProps(); ++i )
  791. {
  792. const SendProp *pSendProp = pPrecalc->GetProp( i );
  793. PrintSendProp( i, pSendProp );
  794. }
  795. }
  796. }
  797. }
  798. }