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.

655 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "server_pch.h"
  8. #include "sv_precache.h"
  9. #include "host.h"
  10. #include "tier0/icommandline.h"
  11. #include "MapReslistGenerator.h"
  12. #include "DownloadListGenerator.h"
  13. #include "soundchars.h"
  14. #ifndef SWDS
  15. #include "vgui_baseui_interface.h"
  16. #endif
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. static ConVar sv_forcepreload( "sv_forcepreload", "0", FCVAR_ARCHIVE, "Force server side preloading.");
  20. //-----------------------------------------------------------------------------
  21. // Purpose:
  22. // Input : *name -
  23. // Output : int SV_ModelIndex
  24. //-----------------------------------------------------------------------------
  25. int SV_ModelIndex (const char *name)
  26. {
  27. return sv.LookupModelIndex( name );
  28. }
  29. //-----------------------------------------------------------------------------
  30. // Purpose:
  31. // Input : *name -
  32. // preload -
  33. // Output : int
  34. //-----------------------------------------------------------------------------
  35. int SV_FindOrAddModel(const char *name, bool preload )
  36. {
  37. // Look for a match or an empty slot...
  38. int flags = RES_FATALIFMISSING;
  39. if ( preload )
  40. {
  41. flags |= RES_PRELOAD;
  42. }
  43. return sv.PrecacheModel( name, flags );
  44. }
  45. //-----------------------------------------------------------------------------
  46. // Purpose:
  47. // Input : *name -
  48. // Output : int
  49. //-----------------------------------------------------------------------------
  50. int SV_SoundIndex(const char *name)
  51. {
  52. return sv.LookupSoundIndex( name );
  53. }
  54. //-----------------------------------------------------------------------------
  55. // Purpose:
  56. // Input : *name -
  57. // preload -
  58. // Output : int
  59. //-----------------------------------------------------------------------------
  60. int SV_FindOrAddSound(const char *name, bool preload )
  61. {
  62. // Look for a match or an empty slot...
  63. int flags = RES_FATALIFMISSING;
  64. if ( preload )
  65. {
  66. flags |= RES_PRELOAD;
  67. }
  68. return sv.PrecacheSound( name, flags );
  69. }
  70. //-----------------------------------------------------------------------------
  71. // Purpose:
  72. // Input : *name -
  73. // Output : int
  74. //-----------------------------------------------------------------------------
  75. int SV_GenericIndex(const char *name)
  76. {
  77. return sv.LookupGenericIndex( name );
  78. }
  79. //-----------------------------------------------------------------------------
  80. // Purpose:
  81. // Input : *name -
  82. // preload -
  83. // Output : int
  84. //-----------------------------------------------------------------------------
  85. int SV_FindOrAddGeneric(const char *name, bool preload )
  86. {
  87. // Look for a match or an empty slot...
  88. int flags = RES_FATALIFMISSING;
  89. if ( preload )
  90. {
  91. flags |= RES_PRELOAD;
  92. }
  93. return sv.PrecacheGeneric( name, flags );
  94. }
  95. //-----------------------------------------------------------------------------
  96. // Purpose:
  97. // Input : *name -
  98. // Output : int
  99. //-----------------------------------------------------------------------------
  100. int SV_DecalIndex(const char *name)
  101. {
  102. return sv.LookupDecalIndex( name );
  103. }
  104. //-----------------------------------------------------------------------------
  105. // Purpose:
  106. // Input : *name -
  107. // preload -
  108. // Output : int
  109. //-----------------------------------------------------------------------------
  110. int SV_FindOrAddDecal(const char *name, bool preload )
  111. {
  112. // Look for a match or an empty slot...
  113. int flags = RES_FATALIFMISSING;
  114. if ( preload )
  115. {
  116. flags |= RES_PRELOAD;
  117. }
  118. return sv.PrecacheDecal( name, flags );
  119. }
  120. //-----------------------------------------------------------------------------
  121. // Purpose:
  122. // Input : *name -
  123. //-----------------------------------------------------------------------------
  124. void SV_ForceSimpleMaterial( const char *name )
  125. {
  126. DownloadListGenerator().ForceSimpleMaterial( name );
  127. }
  128. //-----------------------------------------------------------------------------
  129. // Purpose:
  130. // Input : *name -
  131. // &mins -
  132. // &maxs -
  133. //-----------------------------------------------------------------------------
  134. void SV_ForceModelBounds( const char *name, const Vector &mins, const Vector &maxs )
  135. {
  136. DownloadListGenerator().ForceModelBounds( name, mins, maxs );
  137. }
  138. //-----------------------------------------------------------------------------
  139. // Purpose:
  140. // Output : TABLEID
  141. //-----------------------------------------------------------------------------
  142. INetworkStringTable *CGameServer::GetModelPrecacheTable( void ) const
  143. {
  144. return m_pModelPrecacheTable;
  145. }
  146. //-----------------------------------------------------------------------------
  147. // Purpose:
  148. // Input : *name -
  149. // flags -
  150. // *model -
  151. // Output : int
  152. //-----------------------------------------------------------------------------
  153. int CGameServer::PrecacheModel( char const *name, int flags, model_t *model /*=NULL*/ )
  154. {
  155. if ( !m_pModelPrecacheTable )
  156. return -1;
  157. int idx = m_pModelPrecacheTable->AddString( true, name );
  158. if ( idx == INVALID_STRING_INDEX )
  159. {
  160. return -1;
  161. }
  162. CPrecacheUserData p;
  163. // first time, set file size & flags
  164. CPrecacheUserData const *pExisting = (CPrecacheUserData const *)m_pModelPrecacheTable->GetStringUserData( idx, NULL );
  165. if ( !pExisting )
  166. {
  167. p.flags = flags;
  168. }
  169. else
  170. {
  171. // Just or in any new flags
  172. p = *pExisting;
  173. p.flags |= flags;
  174. }
  175. m_pModelPrecacheTable->SetStringUserData( idx, sizeof( p ), &p );
  176. CPrecacheItem *slot = &model_precache[ idx ];
  177. if ( model )
  178. {
  179. slot->SetModel( model );
  180. }
  181. bool bLoadNow;
  182. bLoadNow = ( !slot->GetModel() && ( ( flags & RES_PRELOAD ) || IsX360() ) );
  183. if ( CommandLine()->FindParm( "-nopreload" ) || CommandLine()->FindParm( "-nopreloadmodels" ))
  184. {
  185. bLoadNow = false;
  186. }
  187. else if ( sv_forcepreload.GetInt() || CommandLine()->FindParm( "-preload" ) )
  188. {
  189. bLoadNow = true;
  190. }
  191. if ( idx != 0 )
  192. {
  193. if ( bLoadNow )
  194. {
  195. slot->SetModel( modelloader->GetModelForName( name, IModelLoader::FMODELLOADER_SERVER ) );
  196. #ifndef SWDS
  197. EngineVGui()->UpdateProgressBar(PROGRESS_PRECACHE);
  198. #endif
  199. MapReslistGenerator().OnModelPrecached(name);
  200. }
  201. else
  202. {
  203. modelloader->ReferenceModel( name, IModelLoader::FMODELLOADER_SERVER );
  204. slot->SetModel( NULL );
  205. }
  206. }
  207. return idx;
  208. }
  209. //-----------------------------------------------------------------------------
  210. // Purpose:
  211. // Input : index -
  212. // Output : model_t
  213. //-----------------------------------------------------------------------------
  214. model_t *CGameServer::GetModel( int index )
  215. {
  216. if ( index <= 0 || !m_pModelPrecacheTable )
  217. return NULL;
  218. if ( index >= m_pModelPrecacheTable->GetNumStrings() )
  219. {
  220. return NULL;
  221. }
  222. CPrecacheItem *slot = &model_precache[ index ];
  223. model_t *m = slot->GetModel();
  224. if ( m )
  225. {
  226. return m;
  227. }
  228. char const *modelname = m_pModelPrecacheTable->GetString( index );
  229. Assert( modelname );
  230. if ( host_showcachemiss.GetBool() )
  231. {
  232. ConDMsg( "server model cache miss on %s\n", modelname );
  233. }
  234. m = modelloader->GetModelForName( modelname, IModelLoader::FMODELLOADER_SERVER );
  235. slot->SetModel( m );
  236. return m;
  237. }
  238. //-----------------------------------------------------------------------------
  239. // Purpose:
  240. // Input : *name -
  241. // Output : int
  242. //-----------------------------------------------------------------------------
  243. int CGameServer::LookupModelIndex( char const *name )
  244. {
  245. if ( !m_pModelPrecacheTable )
  246. return -1;
  247. int idx = m_pModelPrecacheTable->FindStringIndex( name );
  248. return ( idx == INVALID_STRING_INDEX ) ? -1 : idx;
  249. }
  250. //-----------------------------------------------------------------------------
  251. // Purpose:
  252. // Output : TABLEID
  253. //-----------------------------------------------------------------------------
  254. INetworkStringTable *CGameServer::GetSoundPrecacheTable( void ) const
  255. {
  256. return m_pSoundPrecacheTable;
  257. }
  258. //-----------------------------------------------------------------------------
  259. // Purpose:
  260. // Input : *name -
  261. // flags -
  262. // Output : int
  263. //-----------------------------------------------------------------------------
  264. int CGameServer::PrecacheSound( char const *name, int flags )
  265. {
  266. if ( !m_pSoundPrecacheTable )
  267. return -1;
  268. int idx = m_pSoundPrecacheTable->AddString( true, name );
  269. if ( idx == INVALID_STRING_INDEX )
  270. {
  271. return -1;
  272. }
  273. // mark the sound as being precached, but check first that reslist generation is enabled to save on the va() call
  274. if (MapReslistGenerator().IsEnabled() && name[0])
  275. {
  276. MapReslistGenerator().OnResourcePrecached( va( "sound/%s", PSkipSoundChars( name ) ) );
  277. }
  278. // first time, set file size & flags
  279. CPrecacheUserData p;
  280. CPrecacheUserData const *pExisting = (CPrecacheUserData const *)m_pSoundPrecacheTable->GetStringUserData( idx, NULL );
  281. if ( !pExisting )
  282. {
  283. p.flags = flags;
  284. }
  285. else
  286. {
  287. // Just or in any new flags
  288. p = *pExisting;
  289. p.flags |= flags;
  290. }
  291. m_pSoundPrecacheTable->SetStringUserData( idx, sizeof( p ), &p );
  292. CPrecacheItem *slot = &sound_precache[ idx ];
  293. slot->SetName( name );
  294. return idx;
  295. }
  296. //-----------------------------------------------------------------------------
  297. // Purpose:
  298. // Input : index -
  299. // Output : char const
  300. //-----------------------------------------------------------------------------
  301. char const *CGameServer::GetSound( int index )
  302. {
  303. if ( index <= 0 || !m_pSoundPrecacheTable )
  304. {
  305. return NULL;
  306. }
  307. if ( index >= m_pSoundPrecacheTable->GetNumStrings() )
  308. {
  309. return NULL;
  310. }
  311. CPrecacheItem *slot = &sound_precache[ index ];
  312. return slot->GetName();
  313. }
  314. //-----------------------------------------------------------------------------
  315. // Purpose:
  316. // Input : *name -
  317. // Output : int
  318. //-----------------------------------------------------------------------------
  319. int CGameServer::LookupSoundIndex( char const *name )
  320. {
  321. if ( !m_pSoundPrecacheTable )
  322. return 0;
  323. int idx = m_pSoundPrecacheTable->FindStringIndex( name );
  324. return ( idx == INVALID_STRING_INDEX ) ? 0 : idx;
  325. }
  326. //-----------------------------------------------------------------------------
  327. // Purpose:
  328. // Output : TABLEID
  329. //-----------------------------------------------------------------------------
  330. INetworkStringTable *CGameServer::GetGenericPrecacheTable( void ) const
  331. {
  332. return m_pGenericPrecacheTable;
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Purpose:
  336. // Input : *name -
  337. // flags -
  338. // Output : int
  339. //-----------------------------------------------------------------------------
  340. int CGameServer::PrecacheGeneric( char const *name, int flags )
  341. {
  342. if ( !m_pGenericPrecacheTable )
  343. return -1;
  344. int idx = m_pGenericPrecacheTable->AddString( true, name );
  345. if ( idx == INVALID_STRING_INDEX )
  346. {
  347. return -1;
  348. }
  349. MapReslistGenerator().OnResourcePrecached( name );
  350. CPrecacheUserData p;
  351. // first time, set file size & flags
  352. CPrecacheUserData const *pExisting = (CPrecacheUserData const *)m_pGenericPrecacheTable->GetStringUserData( idx, NULL );
  353. if ( !pExisting )
  354. {
  355. p.flags = flags;
  356. }
  357. else
  358. {
  359. // Just or in any new flags
  360. p = *pExisting;
  361. p.flags |= flags;
  362. }
  363. m_pGenericPrecacheTable->SetStringUserData( idx, sizeof( p ), &p );
  364. CPrecacheItem *slot = &generic_precache[ idx ];
  365. slot->SetGeneric( name );
  366. return idx;
  367. }
  368. //-----------------------------------------------------------------------------
  369. // Purpose:
  370. // Input : index -
  371. // Output : char const
  372. //-----------------------------------------------------------------------------
  373. char const *CGameServer::GetGeneric( int index )
  374. {
  375. // Bogus index
  376. if ( index < 0 || !m_pGenericPrecacheTable )
  377. return "";
  378. if ( index >= m_pGenericPrecacheTable->GetNumStrings() )
  379. {
  380. return "";
  381. }
  382. CPrecacheItem *slot = &generic_precache[ index ];
  383. return slot->GetGeneric();
  384. }
  385. //-----------------------------------------------------------------------------
  386. // Purpose:
  387. // Input : *name -
  388. // Output : int
  389. //-----------------------------------------------------------------------------
  390. int CGameServer::LookupGenericIndex( char const *name )
  391. {
  392. if ( !m_pGenericPrecacheTable )
  393. return 0;
  394. int idx = m_pGenericPrecacheTable->FindStringIndex( name );
  395. return ( idx == INVALID_STRING_INDEX ) ? 0 : idx;
  396. }
  397. //-----------------------------------------------------------------------------
  398. // Purpose:
  399. // Output : TABLEID
  400. //-----------------------------------------------------------------------------
  401. INetworkStringTable *CGameServer::GetDecalPrecacheTable( void ) const
  402. {
  403. return m_pDecalPrecacheTable;
  404. }
  405. //-----------------------------------------------------------------------------
  406. // Purpose:
  407. // Input : *name -
  408. // flags -
  409. // Output : int
  410. //-----------------------------------------------------------------------------
  411. int CGameServer::PrecacheDecal( char const *name, int flags )
  412. {
  413. if ( !m_pDecalPrecacheTable )
  414. return -1;
  415. int idx = m_pDecalPrecacheTable->AddString( true, name );
  416. if ( idx == INVALID_STRING_INDEX )
  417. {
  418. return -1;
  419. }
  420. MapReslistGenerator().OnResourcePrecached(name);
  421. CPrecacheUserData p;
  422. // first time, set file size & flags
  423. CPrecacheUserData const *pExisting = (CPrecacheUserData const *)m_pDecalPrecacheTable->GetStringUserData( idx, NULL );
  424. if ( !pExisting )
  425. {
  426. p.flags = flags;
  427. }
  428. else
  429. {
  430. // Just or in any new flags
  431. p = *pExisting;
  432. p.flags |= flags;
  433. }
  434. m_pDecalPrecacheTable->SetStringUserData( idx, sizeof( p ), &p );
  435. CPrecacheItem *slot = &decal_precache[ idx ];
  436. slot->SetDecal( name );
  437. return idx;
  438. }
  439. //-----------------------------------------------------------------------------
  440. // Purpose:
  441. // Input : *name -
  442. // Output : int
  443. //-----------------------------------------------------------------------------
  444. int CGameServer::LookupDecalIndex( char const *name )
  445. {
  446. if ( !m_pDecalPrecacheTable )
  447. return -1;
  448. int idx = m_pDecalPrecacheTable->FindStringIndex( name );
  449. return ( idx == INVALID_STRING_INDEX ) ? -1 : idx;
  450. }
  451. //-----------------------------------------------------------------------------
  452. // Purpose:
  453. //-----------------------------------------------------------------------------
  454. void CGameServer::DumpPrecacheStats( INetworkStringTable *table )
  455. {
  456. if ( table == NULL )
  457. {
  458. ConMsg( "Can only dump stats when active in a level\n" );
  459. return;
  460. }
  461. CPrecacheItem *items = NULL;
  462. if ( table == m_pModelPrecacheTable )
  463. {
  464. items = model_precache;
  465. }
  466. else if ( table == m_pGenericPrecacheTable )
  467. {
  468. items = generic_precache;
  469. }
  470. else if ( table == m_pSoundPrecacheTable )
  471. {
  472. items = sound_precache;
  473. }
  474. else if ( table == m_pDecalPrecacheTable )
  475. {
  476. items = decal_precache;
  477. }
  478. if ( !items )
  479. return;
  480. int count = table->GetNumStrings();
  481. int maxcount = table->GetMaxStrings();
  482. ConMsg( "\n" );
  483. ConMsg( "Precache table %s: %i of %i slots used\n", table->GetTableName(),
  484. count, maxcount );
  485. for ( int i = 0; i < count; i++ )
  486. {
  487. char const *name = table->GetString( i );
  488. CPrecacheItem *slot = &items[ i ];
  489. int testLength;
  490. const CPrecacheUserData *p = ( const CPrecacheUserData * )table->GetStringUserData( i, &testLength );
  491. ErrorIfNot( testLength == sizeof( *p ),
  492. ("CGameServer::DumpPrecacheStats: invalid CPrecacheUserData length (%d)", testLength)
  493. );
  494. if ( !name || !slot || !p )
  495. continue;
  496. ConMsg( "%03i: %s (%s): ",
  497. i,
  498. name,
  499. GetFlagString( p->flags ) );
  500. if ( slot->GetReferenceCount() == 0 )
  501. {
  502. ConMsg( " never used\n" );
  503. }
  504. else
  505. {
  506. ConMsg( " %i refs, first %.2f mru %.2f\n",
  507. slot->GetReferenceCount(),
  508. slot->GetFirstReference(),
  509. slot->GetMostRecentReference() );
  510. }
  511. }
  512. ConMsg( "\n" );
  513. }
  514. //-----------------------------------------------------------------------------
  515. // Purpose:
  516. //-----------------------------------------------------------------------------
  517. CON_COMMAND( sv_precacheinfo, "Show precache info." )
  518. {
  519. if ( args.ArgC() == 2 )
  520. {
  521. char const *table = args[ 1 ];
  522. bool dumped = true;
  523. if ( !Q_strcasecmp( table, "generic" ) )
  524. {
  525. sv.DumpPrecacheStats( sv.GetGenericPrecacheTable() );
  526. }
  527. else if ( !Q_strcasecmp( table, "sound" ) )
  528. {
  529. sv.DumpPrecacheStats( sv.GetSoundPrecacheTable() );
  530. }
  531. else if ( !Q_strcasecmp( table, "decal" ) )
  532. {
  533. sv.DumpPrecacheStats( sv.GetDecalPrecacheTable() );
  534. }
  535. else if ( !Q_strcasecmp( table, "model" ) )
  536. {
  537. sv.DumpPrecacheStats( sv.GetModelPrecacheTable() );
  538. }
  539. else
  540. {
  541. dumped = false;
  542. }
  543. if ( dumped )
  544. {
  545. return;
  546. }
  547. }
  548. // Show all data
  549. sv.DumpPrecacheStats( sv.GetGenericPrecacheTable() );
  550. sv.DumpPrecacheStats( sv.GetDecalPrecacheTable() );
  551. sv.DumpPrecacheStats( sv.GetSoundPrecacheTable() );
  552. sv.DumpPrecacheStats( sv.GetModelPrecacheTable() );
  553. }