Team Fortress 2 Source Code as on 22/4/2020
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.

693 lines
21 KiB

  1. //========= Copyright 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 "tier0/vcrmode.h"
  32. #include "vstdlib/jobthread.h"
  33. #include "enginethreads.h"
  34. #ifdef SWDS
  35. IClientEntityList *entitylist = NULL;
  36. #endif
  37. // memdbgon must be the last include file in a .cpp file!!!
  38. #include "tier0/memdbgon.h"
  39. ConVar sv_debugmanualmode( "sv_debugmanualmode", "0", 0, "Make sure entities correctly report whether or not their network data has changed." );
  40. // Returns false and calls Host_Error if the edict's pvPrivateData is NULL.
  41. static inline bool SV_EnsurePrivateData(edict_t *pEdict)
  42. {
  43. if(pEdict->GetUnknown())
  44. {
  45. return true;
  46. }
  47. else
  48. {
  49. Host_Error("SV_EnsurePrivateData: pEdict->pvPrivateData==NULL (ent %d).\n", pEdict - sv.edicts);
  50. return false;
  51. }
  52. }
  53. // This function makes sure that this entity class has an instance baseline.
  54. // If it doesn't have one yet, it makes a new one.
  55. void SV_EnsureInstanceBaseline( ServerClass *pServerClass, int iEdict, const void *pData, int nBytes )
  56. {
  57. edict_t *pEnt = &sv.edicts[iEdict];
  58. ErrorIfNot( pEnt->GetNetworkable(),
  59. ("SV_EnsureInstanceBaseline: edict %d missing ent", iEdict)
  60. );
  61. ServerClass *pClass = pEnt->GetNetworkable()->GetServerClass();
  62. // See if we already have a baseline for this class.
  63. if ( pClass->m_InstanceBaselineIndex == INVALID_STRING_INDEX )
  64. {
  65. AUTO_LOCK( g_svInstanceBaselineMutex );
  66. // We need this second check in case multiple instances of the same class have grabbed the lock.
  67. if ( pClass->m_InstanceBaselineIndex == INVALID_STRING_INDEX )
  68. {
  69. char idString[32];
  70. Q_snprintf( idString, sizeof( idString ), "%d", pClass->m_ClassID );
  71. // Ok, make a new instance baseline so they can reference it.
  72. int temp = sv.GetInstanceBaselineTable()->AddString(
  73. true,
  74. idString, // Note we're sending a string with the ID number, not the class name.
  75. nBytes,
  76. pData );
  77. // Insert a compiler and/or CPU memory barrier to ensure that all side-effects have
  78. // been published before the index is published. Otherwise the string index may
  79. // be visible before its initialization has finished. This potential problem is caused
  80. // by the use of double-checked locking -- the problem is that the code outside of the
  81. // lock is looking at the variable that is protected by the lock. See this article for details:
  82. // http://en.wikipedia.org/wiki/Double-checked_locking
  83. // Write-release barrier
  84. ThreadMemoryBarrier();
  85. pClass->m_InstanceBaselineIndex = temp;
  86. Assert( pClass->m_InstanceBaselineIndex != INVALID_STRING_INDEX );
  87. }
  88. }
  89. // Read-acquire barrier
  90. ThreadMemoryBarrier();
  91. }
  92. //-----------------------------------------------------------------------------
  93. // Pack the entity....
  94. //-----------------------------------------------------------------------------
  95. static inline void SV_PackEntity(
  96. int edictIdx,
  97. edict_t* edict,
  98. ServerClass* pServerClass,
  99. CFrameSnapshot *pSnapshot )
  100. {
  101. Assert( edictIdx < pSnapshot->m_nNumEntities );
  102. tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "PackEntities_Normal%s", __FUNCTION__ );
  103. int iSerialNum = pSnapshot->m_pEntities[ edictIdx ].m_nSerialNumber;
  104. // Check to see if this entity specifies its changes.
  105. // If so, then try to early out making the fullpack
  106. bool bUsedPrev = false;
  107. if ( !edict->HasStateChanged() )
  108. {
  109. // Now this may not work if we didn't previously send a packet;
  110. // if not, then we gotta compute it
  111. bUsedPrev = framesnapshotmanager->UsePreviouslySentPacket( pSnapshot, edictIdx, iSerialNum );
  112. }
  113. if ( bUsedPrev && !sv_debugmanualmode.GetInt() )
  114. {
  115. edict->ClearStateChanged();
  116. return;
  117. }
  118. // First encode the entity's data.
  119. ALIGN4 char packedData[MAX_PACKEDENTITY_DATA] ALIGN4_POST;
  120. bf_write writeBuf( "SV_PackEntity->writeBuf", packedData, sizeof( packedData ) );
  121. SendTable *pSendTable = pServerClass->m_pTable;
  122. // (avoid constructor overhead).
  123. unsigned char tempData[ sizeof( CSendProxyRecipients ) * MAX_DATATABLE_PROXIES ];
  124. CUtlMemory< CSendProxyRecipients > recip( (CSendProxyRecipients*)tempData, pSendTable->m_pPrecalc->GetNumDataTableProxies() );
  125. if( !SendTable_Encode( pSendTable, edict->GetUnknown(), &writeBuf, edictIdx, &recip, false ) )
  126. {
  127. Host_Error( "SV_PackEntity: SendTable_Encode returned false (ent %d).\n", edictIdx );
  128. }
  129. #ifndef NO_VCR
  130. // VCR mode stuff..
  131. if ( vcr_verbose.GetInt() && writeBuf.GetNumBytesWritten() > 0 )
  132. VCRGenericValueVerify( "writebuf", writeBuf.GetBasePointer(), writeBuf.GetNumBytesWritten()-1 );
  133. #endif
  134. SV_EnsureInstanceBaseline( pServerClass, edictIdx, packedData, writeBuf.GetNumBytesWritten() );
  135. int nFlatProps = SendTable_GetNumFlatProps( pSendTable );
  136. IChangeFrameList *pChangeFrame = NULL;
  137. // If this entity was previously in there, then it should have a valid IChangeFrameList
  138. // which we can delta against to figure out which properties have changed.
  139. //
  140. // If not, then we want to setup a new IChangeFrameList.
  141. PackedEntity *pPrevFrame = framesnapshotmanager->GetPreviouslySentPacket( edictIdx, pSnapshot->m_pEntities[ edictIdx ].m_nSerialNumber );
  142. if ( pPrevFrame )
  143. {
  144. // Calculate a delta.
  145. Assert( !pPrevFrame->IsCompressed() );
  146. int deltaProps[MAX_DATATABLE_PROPS];
  147. int nChanges = SendTable_CalcDelta(
  148. pSendTable,
  149. pPrevFrame->GetData(), pPrevFrame->GetNumBits(),
  150. packedData, writeBuf.GetNumBitsWritten(),
  151. deltaProps,
  152. ARRAYSIZE( deltaProps ),
  153. edictIdx
  154. );
  155. #ifndef NO_VCR
  156. if ( vcr_verbose.GetInt() )
  157. VCRGenericValueVerify( "nChanges", &nChanges, sizeof( nChanges ) );
  158. #endif
  159. // If it's non-manual-mode, but we detect that there are no changes here, then just
  160. // use the previous pSnapshot if it's available (as though the entity were manual mode).
  161. // It would be interesting to hook here and see how many non-manual-mode entities
  162. // are winding up with no changes.
  163. if ( nChanges == 0 )
  164. {
  165. if ( pPrevFrame->CompareRecipients( recip ) )
  166. {
  167. if ( framesnapshotmanager->UsePreviouslySentPacket( pSnapshot, edictIdx, iSerialNum ) )
  168. {
  169. edict->ClearStateChanged();
  170. return;
  171. }
  172. }
  173. }
  174. else
  175. {
  176. if ( !edict->HasStateChanged() )
  177. {
  178. for ( int iDeltaProp=0; iDeltaProp < nChanges; iDeltaProp++ )
  179. {
  180. Assert( pSendTable->m_pPrecalc );
  181. Assert( deltaProps[iDeltaProp] < pSendTable->m_pPrecalc->GetNumProps() );
  182. const SendProp *pProp = pSendTable->m_pPrecalc->GetProp( deltaProps[iDeltaProp] );
  183. // If a field changed, but it changed because it encoded against tickcount,
  184. // then it's just like the entity changed the underlying field, not an error, that is.
  185. if ( pProp->GetFlags() & SPROP_ENCODED_AGAINST_TICKCOUNT )
  186. continue;
  187. Msg( "Entity %d (class '%s') reported ENTITY_CHANGE_NONE but '%s' changed.\n",
  188. edictIdx,
  189. edict->GetClassName(),
  190. pProp->GetName() );
  191. }
  192. }
  193. }
  194. #ifndef _XBOX
  195. #if defined( REPLAY_ENABLED )
  196. if ( (hltv && hltv->IsActive()) || (replay && replay->IsActive()) )
  197. #else
  198. if ( hltv && hltv->IsActive() )
  199. #endif
  200. {
  201. // in HLTV or Replay mode every PackedEntity keeps it's own ChangeFrameList
  202. // we just copy the ChangeFrameList from prev frame and update it
  203. pChangeFrame = pPrevFrame->GetChangeFrameList();
  204. pChangeFrame = pChangeFrame->Copy(); // allocs and copies ChangeFrameList
  205. }
  206. else
  207. #endif
  208. {
  209. // Ok, now snag the changeframe from the previous frame and update the 'last frame changed'
  210. // for the properties in the delta.
  211. pChangeFrame = pPrevFrame->SnagChangeFrameList();
  212. }
  213. ErrorIfNot( pChangeFrame,
  214. ("SV_PackEntity: SnagChangeFrameList returned null") );
  215. ErrorIfNot( pChangeFrame->GetNumProps() == nFlatProps,
  216. ("SV_PackEntity: SnagChangeFrameList mismatched number of props[%d vs %d]", nFlatProps, pChangeFrame->GetNumProps() ) );
  217. pChangeFrame->SetChangeTick( deltaProps, nChanges, pSnapshot->m_nTickCount );
  218. }
  219. else
  220. {
  221. // Ok, init the change frames for the first time.
  222. pChangeFrame = AllocChangeFrameList( nFlatProps, pSnapshot->m_nTickCount );
  223. }
  224. // Now make a PackedEntity and store the new packed data in there.
  225. {
  226. PackedEntity *pPackedEntity = framesnapshotmanager->CreatePackedEntity( pSnapshot, edictIdx );
  227. pPackedEntity->SetChangeFrameList( pChangeFrame );
  228. pPackedEntity->SetServerAndClientClass( pServerClass, NULL );
  229. pPackedEntity->AllocAndCopyPadded( packedData, writeBuf.GetNumBytesWritten() );
  230. pPackedEntity->SetRecipients( recip );
  231. }
  232. edict->ClearStateChanged();
  233. }
  234. // in HLTV mode we ALWAYS have to store position and PVS info, even if entity didnt change
  235. void SV_FillHLTVData( CFrameSnapshot *pSnapshot, edict_t *edict, int iValidEdict )
  236. {
  237. #if !defined( _XBOX )
  238. if ( pSnapshot->m_pHLTVEntityData && edict )
  239. {
  240. CHLTVEntityData *pHLTVData = &pSnapshot->m_pHLTVEntityData[iValidEdict];
  241. PVSInfo_t *pvsInfo = edict->GetNetworkable()->GetPVSInfo();
  242. if ( pvsInfo->m_nClusterCount == 1 )
  243. {
  244. // store cluster, if entity spawns only over one cluster
  245. pHLTVData->m_nNodeCluster = pvsInfo->m_pClusters[0];
  246. }
  247. else
  248. {
  249. // otherwise save PVS head node for larger entities
  250. pHLTVData->m_nNodeCluster = pvsInfo->m_nHeadNode | (1<<31);
  251. }
  252. // remember origin
  253. pHLTVData->origin[0] = pvsInfo->m_vCenter[0];
  254. pHLTVData->origin[1] = pvsInfo->m_vCenter[1];
  255. pHLTVData->origin[2] = pvsInfo->m_vCenter[2];
  256. }
  257. #endif
  258. }
  259. // in Replay mode we ALWAYS have to store position and PVS info, even if entity didnt change
  260. void SV_FillReplayData( CFrameSnapshot *pSnapshot, edict_t *edict, int iValidEdict )
  261. {
  262. #if !defined( _XBOX )
  263. if ( pSnapshot->m_pReplayEntityData && edict )
  264. {
  265. CReplayEntityData *pReplayData = &pSnapshot->m_pReplayEntityData[iValidEdict];
  266. PVSInfo_t *pvsInfo = edict->GetNetworkable()->GetPVSInfo();
  267. if ( pvsInfo->m_nClusterCount == 1 )
  268. {
  269. // store cluster, if entity spawns only over one cluster
  270. pReplayData->m_nNodeCluster = pvsInfo->m_pClusters[0];
  271. }
  272. else
  273. {
  274. // otherwise save PVS head node for larger entities
  275. pReplayData->m_nNodeCluster = pvsInfo->m_nHeadNode | (1<<31);
  276. }
  277. // remember origin
  278. pReplayData->origin[0] = pvsInfo->m_vCenter[0];
  279. pReplayData->origin[1] = pvsInfo->m_vCenter[1];
  280. pReplayData->origin[2] = pvsInfo->m_vCenter[2];
  281. }
  282. #endif
  283. }
  284. // Returns the SendTable that should be used with the specified edict.
  285. SendTable* GetEntSendTable(edict_t *pEdict)
  286. {
  287. if ( pEdict->GetNetworkable() )
  288. {
  289. ServerClass *pClass = pEdict->GetNetworkable()->GetServerClass();
  290. if ( pClass )
  291. {
  292. return pClass->m_pTable;
  293. }
  294. }
  295. return NULL;
  296. }
  297. void PackEntities_NetworkBackDoor(
  298. int clientCount,
  299. CGameClient **clients,
  300. CFrameSnapshot *snapshot )
  301. {
  302. Assert( clientCount == 1 );
  303. VPROF_BUDGET( "PackEntities_NetworkBackDoor", VPROF_BUDGETGROUP_OTHER_NETWORKING );
  304. CGameClient *client = clients[0]; // update variables cl, pInfo, frame for current client
  305. CCheckTransmitInfo *pInfo = &client->m_PackInfo;
  306. for ( int iValidEdict=0; iValidEdict < snapshot->m_nValidEntities; iValidEdict++ )
  307. {
  308. int index = snapshot->m_pValidEntities[iValidEdict];
  309. edict_t* edict = &sv.edicts[ index ];
  310. // this is a bit of a hack to ensure that we get a "preview" of the
  311. // packet timstamp that the server will send so that things that
  312. // are encoded relative to packet time will be correct
  313. Assert( edict->m_NetworkSerialNumber != -1 );
  314. bool bShouldTransmit = pInfo->m_pTransmitEdict->Get( index ) ? true : false;
  315. //CServerDTITimer timer( pSendTable, SERVERDTI_ENCODE );
  316. // If we're using the fast path for a single-player game, just pass the entity
  317. // directly over to the client.
  318. Assert( index < snapshot->m_nNumEntities );
  319. ServerClass *pSVClass = snapshot->m_pEntities[ index ].m_pClass;
  320. g_pLocalNetworkBackdoor->EntState( index, edict->m_NetworkSerialNumber,
  321. pSVClass->m_ClassID, pSVClass->m_pTable, edict->GetUnknown(), edict->HasStateChanged(), bShouldTransmit );
  322. edict->ClearStateChanged();
  323. }
  324. // Tell the client about any entities that are now dormant.
  325. g_pLocalNetworkBackdoor->ProcessDormantEntities();
  326. InvalidateSharedEdictChangeInfos();
  327. }
  328. static ConVar sv_parallel_packentities( "sv_parallel_packentities", "1" );
  329. struct PackWork_t
  330. {
  331. int nIdx;
  332. edict_t *pEdict;
  333. CFrameSnapshot *pSnapshot;
  334. static void Process( PackWork_t &item )
  335. {
  336. SV_PackEntity( item.nIdx, item.pEdict, item.pSnapshot->m_pEntities[ item.nIdx ].m_pClass, item.pSnapshot );
  337. }
  338. };
  339. void PackEntities_Normal(
  340. int clientCount,
  341. CGameClient **clients,
  342. CFrameSnapshot *snapshot )
  343. {
  344. Assert( snapshot->m_nValidEntities >= 0 && snapshot->m_nValidEntities <= MAX_EDICTS );
  345. tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s %d", __FUNCTION__, snapshot->m_nValidEntities );
  346. CUtlVectorFixed< PackWork_t, MAX_EDICTS > workItems;
  347. // check for all active entities, if they are seen by at least on client, if
  348. // so, bit pack them
  349. for ( int iValidEdict=0; iValidEdict < snapshot->m_nValidEntities; ++iValidEdict )
  350. {
  351. int index = snapshot->m_pValidEntities[ iValidEdict ];
  352. Assert( index < snapshot->m_nNumEntities );
  353. edict_t* edict = &sv.edicts[ index ];
  354. // if HLTV is running save PVS info for each entity
  355. SV_FillHLTVData( snapshot, edict, iValidEdict );
  356. // if Replay is running save PVS info for each entity
  357. SV_FillReplayData( snapshot, edict, iValidEdict );
  358. // Check to see if the entity changed this frame...
  359. //ServerDTI_RegisterNetworkStateChange( pSendTable, ent->m_bStateChanged );
  360. for ( int iClient = 0; iClient < clientCount; ++iClient )
  361. {
  362. // entities is seen by at least this client, pack it and exit loop
  363. CGameClient *client = clients[iClient]; // update variables cl, pInfo, frame for current client
  364. CClientFrame *frame = client->m_pCurrentFrame;
  365. if( frame->transmit_entity.Get( index ) )
  366. {
  367. PackWork_t w;
  368. w.nIdx = index;
  369. w.pEdict = edict;
  370. w.pSnapshot = snapshot;
  371. workItems.AddToTail( w );
  372. break;
  373. }
  374. }
  375. }
  376. // Process work
  377. if ( sv_parallel_packentities.GetBool() )
  378. {
  379. ParallelProcess( "PackWork_t::Process", workItems.Base(), workItems.Count(), &PackWork_t::Process );
  380. }
  381. else
  382. {
  383. int c = workItems.Count();
  384. for ( int i = 0; i < c; ++i )
  385. {
  386. PackWork_t &w = workItems[ i ];
  387. SV_PackEntity( w.nIdx, w.pEdict, w.pSnapshot->m_pEntities[ w.nIdx ].m_pClass, w.pSnapshot );
  388. }
  389. }
  390. InvalidateSharedEdictChangeInfos();
  391. }
  392. //-----------------------------------------------------------------------------
  393. // Writes the compressed packet of entities to all clients
  394. //-----------------------------------------------------------------------------
  395. void SV_ComputeClientPacks(
  396. int clientCount,
  397. CGameClient **clients,
  398. CFrameSnapshot *snapshot )
  399. {
  400. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  401. MDLCACHE_CRITICAL_SECTION_(g_pMDLCache);
  402. // Do some setup for each client
  403. {
  404. VPROF_BUDGET_FLAGS( "SV_ComputeClientPacks", "CheckTransmit", BUDGETFLAG_SERVER );
  405. for (int iClient = 0; iClient < clientCount; ++iClient)
  406. {
  407. CCheckTransmitInfo *pInfo = &clients[iClient]->m_PackInfo;
  408. clients[iClient]->SetupPackInfo( snapshot );
  409. serverGameEnts->CheckTransmit( pInfo, snapshot->m_pValidEntities, snapshot->m_nValidEntities );
  410. clients[iClient]->SetupPrevPackInfo();
  411. }
  412. }
  413. VPROF_BUDGET_FLAGS( "SV_ComputeClientPacks", "ComputeClientPacks", BUDGETFLAG_SERVER );
  414. if ( g_pLocalNetworkBackdoor )
  415. {
  416. // This will force network string table updates for local client to go through the backdoor if it's active
  417. #ifdef SHARED_NET_STRING_TABLES
  418. sv.m_StringTables->TriggerCallbacks( clients[0]->m_nDeltaTick );
  419. #else
  420. sv.m_StringTables->DirectUpdate( clients[0]->GetMaxAckTickCount() );
  421. #endif
  422. g_pLocalNetworkBackdoor->StartEntityStateUpdate();
  423. #ifndef SWDS
  424. int saveClientTicks = cl.GetClientTickCount();
  425. int saveServerTicks = cl.GetServerTickCount();
  426. bool bSaveSimulation = cl.insimulation;
  427. float flSaveLastServerTickTime = cl.m_flLastServerTickTime;
  428. cl.insimulation = true;
  429. cl.SetClientTickCount( sv.m_nTickCount );
  430. cl.SetServerTickCount( sv.m_nTickCount );
  431. cl.m_flLastServerTickTime = sv.m_nTickCount * host_state.interval_per_tick;
  432. g_ClientGlobalVariables.tickcount = cl.GetClientTickCount();
  433. g_ClientGlobalVariables.curtime = cl.GetTime();
  434. #endif
  435. PackEntities_NetworkBackDoor( clientCount, clients, snapshot );
  436. g_pLocalNetworkBackdoor->EndEntityStateUpdate();
  437. #ifndef SWDS
  438. cl.SetClientTickCount( saveClientTicks );
  439. cl.SetServerTickCount( saveServerTicks );
  440. cl.insimulation = bSaveSimulation;
  441. cl.m_flLastServerTickTime = flSaveLastServerTickTime;
  442. g_ClientGlobalVariables.tickcount = cl.GetClientTickCount();
  443. g_ClientGlobalVariables.curtime = cl.GetTime();
  444. #endif
  445. PrintPartialChangeEntsList();
  446. }
  447. else
  448. {
  449. PackEntities_Normal( clientCount, clients, snapshot );
  450. }
  451. }
  452. // If the table's ID is -1, writes its info into the buffer and increments curID.
  453. void SV_MaybeWriteSendTable( SendTable *pTable, bf_write &pBuf, bool bNeedDecoder )
  454. {
  455. // Already sent?
  456. if ( pTable->GetWriteFlag() )
  457. return;
  458. pTable->SetWriteFlag( true );
  459. SVC_SendTable sndTbl;
  460. byte tmpbuf[4096];
  461. sndTbl.m_DataOut.StartWriting( tmpbuf, sizeof(tmpbuf) );
  462. // write send table properties into message buffer
  463. SendTable_WriteInfos(pTable, &sndTbl.m_DataOut );
  464. sndTbl.m_bNeedsDecoder = bNeedDecoder;
  465. // write message to stream
  466. sndTbl.WriteToBuffer( pBuf );
  467. }
  468. // Calls SV_MaybeWriteSendTable recursively.
  469. void SV_MaybeWriteSendTable_R( SendTable *pTable, bf_write &pBuf )
  470. {
  471. SV_MaybeWriteSendTable( pTable, pBuf, false );
  472. // Make sure we send child send tables..
  473. for(int i=0; i < pTable->m_nProps; i++)
  474. {
  475. SendProp *pProp = &pTable->m_pProps[i];
  476. if( pProp->m_Type == DPT_DataTable )
  477. SV_MaybeWriteSendTable_R( pProp->GetDataTable(), pBuf );
  478. }
  479. }
  480. // Sets up SendTable IDs and sends an svc_sendtable for each table.
  481. void SV_WriteSendTables( ServerClass *pClasses, bf_write &pBuf )
  482. {
  483. ServerClass *pCur;
  484. DataTable_ClearWriteFlags( pClasses );
  485. // First, we send all the leaf classes. These are the ones that will need decoders
  486. // on the client.
  487. for ( pCur=pClasses; pCur; pCur=pCur->m_pNext )
  488. {
  489. SV_MaybeWriteSendTable( pCur->m_pTable, pBuf, true );
  490. }
  491. // Now, we send their base classes. These don't need decoders on the client
  492. // because we will never send these SendTables by themselves.
  493. for ( pCur=pClasses; pCur; pCur=pCur->m_pNext )
  494. {
  495. SV_MaybeWriteSendTable_R( pCur->m_pTable, pBuf );
  496. }
  497. }
  498. //-----------------------------------------------------------------------------
  499. // Purpose:
  500. // Input : crc -
  501. //-----------------------------------------------------------------------------
  502. void SV_ComputeClassInfosCRC( CRC32_t* crc )
  503. {
  504. ServerClass *pClasses = serverGameDLL->GetAllServerClasses();
  505. for ( ServerClass *pClass=pClasses; pClass; pClass=pClass->m_pNext )
  506. {
  507. CRC32_ProcessBuffer( crc, (void *)pClass->m_pNetworkName, Q_strlen( pClass->m_pNetworkName ) );
  508. CRC32_ProcessBuffer( crc, (void *)pClass->m_pTable->GetName(), Q_strlen(pClass->m_pTable->GetName() ) );
  509. }
  510. }
  511. void CGameServer::AssignClassIds()
  512. {
  513. ServerClass *pClasses = serverGameDLL->GetAllServerClasses();
  514. // Count the number of classes.
  515. int nClasses = 0;
  516. for ( ServerClass *pCount=pClasses; pCount; pCount=pCount->m_pNext )
  517. {
  518. ++nClasses;
  519. }
  520. // These should be the same! If they're not, then it should detect an explicit create message.
  521. ErrorIfNot( nClasses <= MAX_SERVER_CLASSES,
  522. ("CGameServer::AssignClassIds: too many server classes (%i, MAX = %i).\n", nClasses, MAX_SERVER_CLASSES );
  523. );
  524. serverclasses = nClasses;
  525. serverclassbits = Q_log2( serverclasses ) + 1;
  526. bool bSpew = CommandLine()->FindParm( "-netspike" ) != 0;
  527. int curID = 0;
  528. for ( ServerClass *pClass=pClasses; pClass; pClass=pClass->m_pNext )
  529. {
  530. pClass->m_ClassID = curID++;
  531. if ( bSpew )
  532. {
  533. Msg( "%d == '%s'\n", pClass->m_ClassID, pClass->GetName() );
  534. }
  535. }
  536. }
  537. // Assign each class and ID and write an svc_classinfo for each one.
  538. void SV_WriteClassInfos(ServerClass *pClasses, bf_write &pBuf)
  539. {
  540. // Assert( sv.serverclasses < MAX_SERVER_CLASSES );
  541. SVC_ClassInfo classinfomsg;
  542. classinfomsg.m_bCreateOnClient = false;
  543. for ( ServerClass *pClass=pClasses; pClass; pClass=pClass->m_pNext )
  544. {
  545. SVC_ClassInfo::class_t svclass;
  546. svclass.classID = pClass->m_ClassID;
  547. Q_strncpy( svclass.datatablename, pClass->m_pTable->GetName(), sizeof(svclass.datatablename) );
  548. Q_strncpy( svclass.classname, pClass->m_pNetworkName, sizeof(svclass.classname) );
  549. classinfomsg.m_Classes.AddToTail( svclass ); // add all known classes to message
  550. }
  551. classinfomsg.WriteToBuffer( pBuf );
  552. }
  553. // This is implemented for the datatable code so its warnings can include an object's classname.
  554. const char* GetObjectClassName( int objectID )
  555. {
  556. if ( objectID >= 0 && objectID < sv.num_edicts )
  557. {
  558. return sv.edicts[objectID].GetClassName();
  559. }
  560. else
  561. {
  562. return "[unknown]";
  563. }
  564. }