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.

3502 lines
104 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: encapsulates and implements all the accessing of the game dll from external
  4. // sources (only the engine at the time of writing)
  5. // This files ONLY contains functions and data necessary to build an interface
  6. // to external modules
  7. //===========================================================================//
  8. #include "cbase.h"
  9. #include "gamestringpool.h"
  10. #include "mapentities_shared.h"
  11. #include "game.h"
  12. #include "entityapi.h"
  13. #include "client.h"
  14. #include "saverestore.h"
  15. #include "entitylist.h"
  16. #include "gamerules.h"
  17. #include "soundent.h"
  18. #include "player.h"
  19. #include "server_class.h"
  20. #include "ai_node.h"
  21. #include "ai_link.h"
  22. #include "ai_saverestore.h"
  23. #include "ai_networkmanager.h"
  24. #include "ndebugoverlay.h"
  25. #include "ivoiceserver.h"
  26. #include <stdarg.h>
  27. #include "movehelper_server.h"
  28. #include "networkstringtable_gamedll.h"
  29. #include "filesystem.h"
  30. #include "func_areaportalwindow.h"
  31. #include "igamesystem.h"
  32. #include "init_factory.h"
  33. #include "vstdlib/random.h"
  34. #include "env_wind_shared.h"
  35. #include "engine/IEngineSound.h"
  36. #include "ispatialpartition.h"
  37. #include "textstatsmgr.h"
  38. #include "bitbuf.h"
  39. #include "saverestoretypes.h"
  40. #include "physics_saverestore.h"
  41. #include "achievement_saverestore.h"
  42. #include "tier0/vprof.h"
  43. #include "effect_dispatch_data.h"
  44. #include "engine/IStaticPropMgr.h"
  45. #include "TemplateEntities.h"
  46. #include "ai_speech.h"
  47. #include "soundenvelope.h"
  48. #include "usermessages.h"
  49. #include "physics.h"
  50. #include "igameevents.h"
  51. #include "EventLog.h"
  52. #include "datacache/idatacache.h"
  53. #include "engine/ivdebugoverlay.h"
  54. #include "shareddefs.h"
  55. #include "props.h"
  56. #include "timedeventmgr.h"
  57. #include "gameinterface.h"
  58. #include "eventqueue.h"
  59. #include "hltvdirector.h"
  60. #if defined( REPLAY_ENABLED )
  61. #include "replay/iserverreplaycontext.h"
  62. #endif
  63. #include "SoundEmitterSystem/isoundemittersystembase.h"
  64. #include "AI_ResponseSystem.h"
  65. #include "saverestore_stringtable.h"
  66. #include "util.h"
  67. #include "tier0/icommandline.h"
  68. #include "datacache/imdlcache.h"
  69. #include "engine/iserverplugin.h"
  70. #ifdef _WIN32
  71. #include "ienginevgui.h"
  72. #endif
  73. #include "ragdoll_shared.h"
  74. #include "toolframework/iserverenginetools.h"
  75. #include "sceneentity.h"
  76. #include "appframework/IAppSystemGroup.h"
  77. #include "scenefilecache/ISceneFileCache.h"
  78. #include "tier2/tier2.h"
  79. #include "particles/particles.h"
  80. #include "gamestats.h"
  81. #include "ixboxsystem.h"
  82. #include "engine/imatchmaking.h"
  83. #include "hl2orange.spa.h"
  84. #include "particle_parse.h"
  85. #ifndef NO_STEAM
  86. #include "steam/steam_gameserver.h"
  87. #endif
  88. #include "tier3/tier3.h"
  89. #include "serverbenchmark_base.h"
  90. #include "querycache.h"
  91. #ifdef TF_DLL
  92. #include "gc_clientsystem.h"
  93. #include "econ_item_inventory.h"
  94. #include "steamworks_gamestats.h"
  95. #include "tf/tf_gc_server.h"
  96. #include "tf_gamerules.h"
  97. #include "player_vs_environment/tf_population_manager.h"
  98. #include "workshop/maps_workshop.h"
  99. extern ConVar tf_mm_trusted;
  100. extern ConVar tf_mm_servermode;
  101. #endif
  102. #ifdef USE_NAV_MESH
  103. #include "nav_mesh.h"
  104. #endif
  105. #ifdef NEXT_BOT
  106. #include "NextBotManager.h"
  107. #endif
  108. #ifdef USES_ECON_ITEMS
  109. #include "econ_item_system.h"
  110. #endif // USES_ECON_ITEMS
  111. #ifdef CSTRIKE_DLL // BOTPORT: TODO: move these ifdefs out
  112. #include "bot/bot.h"
  113. #endif
  114. #ifdef PORTAL
  115. #include "prop_portal_shared.h"
  116. #include "portal_player.h"
  117. #endif
  118. #if defined( REPLAY_ENABLED )
  119. #include "replay/ireplaysystem.h"
  120. #endif
  121. extern IToolFrameworkServer *g_pToolFrameworkServer;
  122. extern IParticleSystemQuery *g_pParticleSystemQuery;
  123. extern ConVar commentary;
  124. #ifndef NO_STEAM
  125. // this context is not available on dedicated servers
  126. // WARNING! always check if interfaces are available before using
  127. static CSteamAPIContext s_SteamAPIContext;
  128. CSteamAPIContext *steamapicontext = &s_SteamAPIContext;
  129. // this context is not available on a pure client connected to a remote server.
  130. // WARNING! always check if interfaces are available before using
  131. static CSteamGameServerAPIContext s_SteamGameServerAPIContext;
  132. CSteamGameServerAPIContext *steamgameserverapicontext = &s_SteamGameServerAPIContext;
  133. #endif
  134. IUploadGameStats *gamestatsuploader = NULL;
  135. // memdbgon must be the last include file in a .cpp file!!!
  136. #include "tier0/memdbgon.h"
  137. CTimedEventMgr g_NetworkPropertyEventMgr;
  138. ISaveRestoreBlockHandler *GetEventQueueSaveRestoreBlockHandler();
  139. ISaveRestoreBlockHandler *GetCommentarySaveRestoreBlockHandler();
  140. CUtlLinkedList<CMapEntityRef, unsigned short> g_MapEntityRefs;
  141. // Engine interfaces.
  142. IVEngineServer *engine = NULL;
  143. IVoiceServer *g_pVoiceServer = NULL;
  144. #if !defined(_STATIC_LINKED)
  145. IFileSystem *filesystem = NULL;
  146. #else
  147. extern IFileSystem *filesystem;
  148. #endif
  149. INetworkStringTableContainer *networkstringtable = NULL;
  150. IStaticPropMgrServer *staticpropmgr = NULL;
  151. IUniformRandomStream *random = NULL;
  152. IEngineSound *enginesound = NULL;
  153. ISpatialPartition *partition = NULL;
  154. IVModelInfo *modelinfo = NULL;
  155. IEngineTrace *enginetrace = NULL;
  156. IGameEventManager2 *gameeventmanager = NULL;
  157. IDataCache *datacache = NULL;
  158. IVDebugOverlay * debugoverlay = NULL;
  159. ISoundEmitterSystemBase *soundemitterbase = NULL;
  160. IServerPluginHelpers *serverpluginhelpers = NULL;
  161. IServerEngineTools *serverenginetools = NULL;
  162. ISceneFileCache *scenefilecache = NULL;
  163. IXboxSystem *xboxsystem = NULL; // Xbox 360 only
  164. IMatchmaking *matchmaking = NULL; // Xbox 360 only
  165. #if defined( REPLAY_ENABLED )
  166. IReplaySystem *g_pReplay = NULL;
  167. IServerReplayContext *g_pReplayServerContext = NULL;
  168. #endif
  169. IGameSystem *SoundEmitterSystem();
  170. bool ModelSoundsCacheInit();
  171. void ModelSoundsCacheShutdown();
  172. void SceneManager_ClientActive( CBasePlayer *player );
  173. class IMaterialSystem;
  174. class IStudioRender;
  175. #ifdef _DEBUG
  176. static ConVar s_UseNetworkVars( "UseNetworkVars", "1", FCVAR_CHEAT, "For profiling, toggle network vars." );
  177. #endif
  178. extern ConVar sv_noclipduringpause;
  179. ConVar sv_massreport( "sv_massreport", "0" );
  180. ConVar sv_force_transmit_ents( "sv_force_transmit_ents", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Will transmit all entities to client, regardless of PVS conditions (will still skip based on transmit flags, however)." );
  181. ConVar sv_autosave( "sv_autosave", "1", 0, "Set to 1 to autosave game on level transition. Does not affect autosave triggers." );
  182. ConVar *sv_maxreplay = NULL;
  183. static ConVar *g_pcv_commentary = NULL;
  184. static ConVar *g_pcv_ThreadMode = NULL;
  185. static ConVar *g_pcv_hideServer = NULL;
  186. // String tables
  187. INetworkStringTable *g_pStringTableParticleEffectNames = NULL;
  188. INetworkStringTable *g_pStringTableEffectDispatch = NULL;
  189. INetworkStringTable *g_pStringTableVguiScreen = NULL;
  190. INetworkStringTable *g_pStringTableMaterials = NULL;
  191. INetworkStringTable *g_pStringTableInfoPanel = NULL;
  192. INetworkStringTable *g_pStringTableClientSideChoreoScenes = NULL;
  193. INetworkStringTable *g_pStringTableServerMapCycle = NULL;
  194. #ifdef TF_DLL
  195. INetworkStringTable *g_pStringTableServerPopFiles = NULL;
  196. INetworkStringTable *g_pStringTableServerMapCycleMvM = NULL;
  197. #endif
  198. CStringTableSaveRestoreOps g_VguiScreenStringOps;
  199. // Holds global variables shared between engine and game.
  200. CGlobalVars *gpGlobals;
  201. edict_t *g_pDebugEdictBase = 0;
  202. static int g_nCommandClientIndex = 0;
  203. // The chapter number of the current
  204. static int g_nCurrentChapterIndex = -1;
  205. #ifdef _DEBUG
  206. static ConVar sv_showhitboxes( "sv_showhitboxes", "-1", FCVAR_CHEAT, "Send server-side hitboxes for specified entity to client (NOTE: this uses lots of bandwidth, use on listen server only)." );
  207. #endif
  208. void PrecachePointTemplates();
  209. static ClientPutInServerOverrideFn g_pClientPutInServerOverride = NULL;
  210. static void UpdateChapterRestrictions( const char *mapname );
  211. static void UpdateRichPresence ( void );
  212. #if !defined( _XBOX ) // Don't doubly define this symbol.
  213. CSharedEdictChangeInfo *g_pSharedChangeInfo = NULL;
  214. #endif
  215. IChangeInfoAccessor *CBaseEdict::GetChangeAccessor()
  216. {
  217. return engine->GetChangeAccessor( (const edict_t *)this );
  218. }
  219. const IChangeInfoAccessor *CBaseEdict::GetChangeAccessor() const
  220. {
  221. return engine->GetChangeAccessor( (const edict_t *)this );
  222. }
  223. const char *GetHintTypeDescription( CAI_Hint *pHint );
  224. void ClientPutInServerOverride( ClientPutInServerOverrideFn fn )
  225. {
  226. g_pClientPutInServerOverride = fn;
  227. }
  228. ConVar ai_post_frame_navigation( "ai_post_frame_navigation", "0" );
  229. class CPostFrameNavigationHook;
  230. extern CPostFrameNavigationHook *PostFrameNavigationSystem( void );
  231. //-----------------------------------------------------------------------------
  232. // Purpose:
  233. // Output : int
  234. //-----------------------------------------------------------------------------
  235. int UTIL_GetCommandClientIndex( void )
  236. {
  237. // -1 == unknown,dedicated server console
  238. // 0 == player 1
  239. // Convert to 1 based offset
  240. return (g_nCommandClientIndex+1);
  241. }
  242. //-----------------------------------------------------------------------------
  243. // Purpose:
  244. // Output : CBasePlayer
  245. //-----------------------------------------------------------------------------
  246. CBasePlayer *UTIL_GetCommandClient( void )
  247. {
  248. int idx = UTIL_GetCommandClientIndex();
  249. if ( idx > 0 )
  250. {
  251. return UTIL_PlayerByIndex( idx );
  252. }
  253. // HLDS console issued command
  254. return NULL;
  255. }
  256. //-----------------------------------------------------------------------------
  257. // Purpose: Retrieves the MOD directory for the active game (ie. "hl2")
  258. //-----------------------------------------------------------------------------
  259. bool UTIL_GetModDir( char *lpszTextOut, unsigned int nSize )
  260. {
  261. // Must pass in a buffer at least large enough to hold the desired string
  262. const char *pGameDir = CommandLine()->ParmValue( "-game", "hl2" );
  263. Assert( strlen(pGameDir) <= nSize );
  264. if ( strlen(pGameDir) > nSize )
  265. return false;
  266. Q_strncpy( lpszTextOut, pGameDir, nSize );
  267. if ( Q_strnchr( lpszTextOut, '/', nSize ) || Q_strnchr( lpszTextOut, '\\', nSize ) )
  268. {
  269. // Strip the last directory off (which will be our game dir)
  270. Q_StripLastDir( lpszTextOut, nSize );
  271. // Find the difference in string lengths and take that difference from the original string as the mod dir
  272. int dirlen = Q_strlen( lpszTextOut );
  273. Q_strncpy( lpszTextOut, pGameDir + dirlen, Q_strlen( pGameDir ) - dirlen + 1 );
  274. }
  275. return true;
  276. }
  277. extern void InitializeCvars( void );
  278. CBaseEntity* FindPickerEntity( CBasePlayer* pPlayer );
  279. CAI_Node* FindPickerAINode( CBasePlayer* pPlayer, NodeType_e nNodeType );
  280. CAI_Link* FindPickerAILink( CBasePlayer* pPlayer );
  281. float GetFloorZ(const Vector &origin);
  282. void UpdateAllClientData( void );
  283. void DrawMessageEntities();
  284. #include "ai_network.h"
  285. // For now just using one big AI network
  286. extern ConVar think_limit;
  287. #if 0
  288. //-----------------------------------------------------------------------------
  289. // Purpose: Draw output overlays for any measure sections
  290. // Input :
  291. //-----------------------------------------------------------------------------
  292. void DrawMeasuredSections(void)
  293. {
  294. int row = 1;
  295. float rowheight = 0.025;
  296. CMeasureSection *p = CMeasureSection::GetList();
  297. while ( p )
  298. {
  299. char str[256];
  300. Q_snprintf(str,sizeof(str),"%s",p->GetName());
  301. NDebugOverlay::ScreenText( 0.01,0.51+(row*rowheight),str, 255,255,255,255, 0.0 );
  302. Q_snprintf(str,sizeof(str),"%5.2f\n",p->GetTime().GetMillisecondsF());
  303. //Q_snprintf(str,sizeof(str),"%3.3f\n",p->GetTime().GetSeconds() * 100.0 / engine->Time());
  304. NDebugOverlay::ScreenText( 0.28,0.51+(row*rowheight),str, 255,255,255,255, 0.0 );
  305. Q_snprintf(str,sizeof(str),"%5.2f\n",p->GetMaxTime().GetMillisecondsF());
  306. //Q_snprintf(str,sizeof(str),"%3.3f\n",p->GetTime().GetSeconds() * 100.0 / engine->Time());
  307. NDebugOverlay::ScreenText( 0.34,0.51+(row*rowheight),str, 255,255,255,255, 0.0 );
  308. row++;
  309. p = p->GetNext();
  310. }
  311. bool sort_reset = false;
  312. // Time to redo sort?
  313. if ( measure_resort.GetFloat() > 0.0 &&
  314. engine->Time() >= CMeasureSection::m_dNextResort )
  315. {
  316. // Redo it
  317. CMeasureSection::SortSections();
  318. // Set next time
  319. CMeasureSection::m_dNextResort = engine->Time() + measure_resort.GetFloat();
  320. // Flag to reset sort accumulator, too
  321. sort_reset = true;
  322. }
  323. // Iterate through the sections now
  324. p = CMeasureSection::GetList();
  325. while ( p )
  326. {
  327. // Update max
  328. p->UpdateMax();
  329. // Reset regular accum.
  330. p->Reset();
  331. // Reset sort accum less often
  332. if ( sort_reset )
  333. {
  334. p->SortReset();
  335. }
  336. p = p->GetNext();
  337. }
  338. }
  339. #endif
  340. //-----------------------------------------------------------------------------
  341. // Purpose:
  342. //-----------------------------------------------------------------------------
  343. void DrawAllDebugOverlays( void )
  344. {
  345. // If in debug select mode print the selection entities name or classname
  346. if (CBaseEntity::m_bInDebugSelect)
  347. {
  348. CBasePlayer* pPlayer = UTIL_PlayerByIndex( CBaseEntity::m_nDebugPlayer );
  349. if (pPlayer)
  350. {
  351. // First try to trace a hull to an entity
  352. CBaseEntity *pEntity = FindPickerEntity( pPlayer );
  353. if ( pEntity )
  354. {
  355. pEntity->DrawDebugTextOverlays();
  356. pEntity->DrawBBoxOverlay();
  357. pEntity->SendDebugPivotOverlay();
  358. }
  359. }
  360. }
  361. // --------------------------------------------------------
  362. // Draw debug overlay lines
  363. // --------------------------------------------------------
  364. UTIL_DrawOverlayLines();
  365. // ------------------------------------------------------------------------
  366. // If in wc_edit mode draw a box to highlight which node I'm looking at
  367. // ------------------------------------------------------------------------
  368. if (engine->IsInEditMode())
  369. {
  370. CBasePlayer* pPlayer = UTIL_PlayerByIndex( CBaseEntity::m_nDebugPlayer );
  371. if (pPlayer)
  372. {
  373. if (g_pAINetworkManager->GetEditOps()->m_bLinkEditMode)
  374. {
  375. CAI_Link* pAILink = FindPickerAILink(pPlayer);
  376. if (pAILink)
  377. {
  378. // For now just using one big AI network
  379. Vector startPos = g_pBigAINet->GetNode(pAILink->m_iSrcID)->GetPosition(g_pAINetworkManager->GetEditOps()->m_iHullDrawNum);
  380. Vector endPos = g_pBigAINet->GetNode(pAILink->m_iDestID)->GetPosition(g_pAINetworkManager->GetEditOps()->m_iHullDrawNum);
  381. Vector linkDir = startPos-endPos;
  382. float linkLen = VectorNormalize( linkDir );
  383. // Draw in green if link that's been turned off
  384. if (pAILink->m_LinkInfo & bits_LINK_OFF)
  385. {
  386. NDebugOverlay::BoxDirection(startPos, Vector(-4,-4,-4), Vector(-linkLen,4,4), linkDir, 0,255,0,40,0);
  387. }
  388. else
  389. {
  390. NDebugOverlay::BoxDirection(startPos, Vector(-4,-4,-4), Vector(-linkLen,4,4), linkDir, 255,0,0,40,0);
  391. }
  392. }
  393. }
  394. else
  395. {
  396. CAI_Node* pAINode;
  397. if (g_pAINetworkManager->GetEditOps()->m_bAirEditMode)
  398. {
  399. pAINode = FindPickerAINode(pPlayer,NODE_AIR);
  400. }
  401. else
  402. {
  403. pAINode = FindPickerAINode(pPlayer,NODE_GROUND);
  404. }
  405. if (pAINode)
  406. {
  407. Vector vecPos = pAINode->GetPosition(g_pAINetworkManager->GetEditOps()->m_iHullDrawNum);
  408. NDebugOverlay::Box( vecPos, Vector(-8,-8,-8), Vector(8,8,8), 255,0,0,40,0);
  409. if ( pAINode->GetHint() )
  410. {
  411. CBaseEntity *pEnt = (CBaseEntity *)pAINode->GetHint();
  412. if ( pEnt->GetEntityName() != NULL_STRING )
  413. {
  414. NDebugOverlay::Text( vecPos + Vector(0,0,6), STRING(pEnt->GetEntityName()), false, 0 );
  415. }
  416. NDebugOverlay::Text( vecPos, GetHintTypeDescription( pAINode->GetHint() ), false, 0 );
  417. }
  418. }
  419. }
  420. // ------------------------------------
  421. // If in air edit mode draw guide line
  422. // ------------------------------------
  423. if (g_pAINetworkManager->GetEditOps()->m_bAirEditMode)
  424. {
  425. UTIL_DrawPositioningOverlay(g_pAINetworkManager->GetEditOps()->m_flAirEditDistance);
  426. }
  427. else
  428. {
  429. NDebugOverlay::DrawGroundCrossHairOverlay();
  430. }
  431. }
  432. }
  433. // For not just using one big AI Network
  434. if ( g_pAINetworkManager )
  435. {
  436. g_pAINetworkManager->GetEditOps()->DrawAINetworkOverlay();
  437. }
  438. // PERFORMANCE: only do this in developer mode
  439. if ( g_pDeveloper->GetInt() && !engine->IsDedicatedServer() )
  440. {
  441. // iterate through all objects for debug overlays
  442. const CEntInfo *pInfo = gEntList.FirstEntInfo();
  443. for ( ;pInfo; pInfo = pInfo->m_pNext )
  444. {
  445. CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
  446. // HACKHACK: to flag off these calls
  447. if ( ent->m_debugOverlays || ent->m_pTimedOverlay )
  448. {
  449. MDLCACHE_CRITICAL_SECTION();
  450. ent->DrawDebugGeometryOverlays();
  451. }
  452. }
  453. }
  454. if ( sv_massreport.GetInt() )
  455. {
  456. // iterate through all objects for debug overlays
  457. const CEntInfo *pInfo = gEntList.FirstEntInfo();
  458. for ( ;pInfo; pInfo = pInfo->m_pNext )
  459. {
  460. CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
  461. if (!ent->VPhysicsGetObject())
  462. continue;
  463. char tempstr[512];
  464. Q_snprintf(tempstr, sizeof(tempstr),"%s: Mass: %.2f kg / %.2f lb (%s)",
  465. STRING( ent->GetModelName() ), ent->VPhysicsGetObject()->GetMass(),
  466. kg2lbs(ent->VPhysicsGetObject()->GetMass()),
  467. GetMassEquivalent(ent->VPhysicsGetObject()->GetMass()));
  468. ent->EntityText(0, tempstr, 0);
  469. }
  470. }
  471. // A hack to draw point_message entities w/o developer required
  472. DrawMessageEntities();
  473. }
  474. CServerGameDLL g_ServerGameDLL;
  475. // INTERFACEVERSION_SERVERGAMEDLL_VERSION_8 is compatible with the latest since we're only adding things to the end, so expose that as well.
  476. //EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerGameDLL, IServerGameDLL008, INTERFACEVERSION_SERVERGAMEDLL_VERSION_8, g_ServerGameDLL );
  477. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerGameDLL, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL, g_ServerGameDLL);
  478. // When bumping the version to this interface, check that our assumption is still valid and expose the older version in the same way
  479. COMPILE_TIME_ASSERT( INTERFACEVERSION_SERVERGAMEDLL_INT == 10 );
  480. bool CServerGameDLL::DLLInit( CreateInterfaceFn appSystemFactory,
  481. CreateInterfaceFn physicsFactory, CreateInterfaceFn fileSystemFactory,
  482. CGlobalVars *pGlobals)
  483. {
  484. ConnectTier1Libraries( &appSystemFactory, 1 );
  485. ConnectTier2Libraries( &appSystemFactory, 1 );
  486. ConnectTier3Libraries( &appSystemFactory, 1 );
  487. // Connected in ConnectTier1Libraries
  488. if ( cvar == NULL )
  489. return false;
  490. #ifndef _X360
  491. s_SteamAPIContext.Init();
  492. s_SteamGameServerAPIContext.Init();
  493. #endif
  494. // init each (seperated for ease of debugging)
  495. if ( (engine = (IVEngineServer*)appSystemFactory(INTERFACEVERSION_VENGINESERVER, NULL)) == NULL )
  496. return false;
  497. if ( (g_pVoiceServer = (IVoiceServer*)appSystemFactory(INTERFACEVERSION_VOICESERVER, NULL)) == NULL )
  498. return false;
  499. if ( (networkstringtable = (INetworkStringTableContainer *)appSystemFactory(INTERFACENAME_NETWORKSTRINGTABLESERVER,NULL)) == NULL )
  500. return false;
  501. if ( (staticpropmgr = (IStaticPropMgrServer *)appSystemFactory(INTERFACEVERSION_STATICPROPMGR_SERVER,NULL)) == NULL )
  502. return false;
  503. if ( (random = (IUniformRandomStream *)appSystemFactory(VENGINE_SERVER_RANDOM_INTERFACE_VERSION, NULL)) == NULL )
  504. return false;
  505. if ( (enginesound = (IEngineSound *)appSystemFactory(IENGINESOUND_SERVER_INTERFACE_VERSION, NULL)) == NULL )
  506. return false;
  507. if ( (::partition = (ISpatialPartition *)appSystemFactory(INTERFACEVERSION_SPATIALPARTITION, NULL)) == NULL )
  508. return false;
  509. if ( (modelinfo = (IVModelInfo *)appSystemFactory(VMODELINFO_SERVER_INTERFACE_VERSION, NULL)) == NULL )
  510. return false;
  511. if ( (enginetrace = (IEngineTrace *)appSystemFactory(INTERFACEVERSION_ENGINETRACE_SERVER,NULL)) == NULL )
  512. return false;
  513. if ( (filesystem = (IFileSystem *)fileSystemFactory(FILESYSTEM_INTERFACE_VERSION,NULL)) == NULL )
  514. return false;
  515. if ( (gameeventmanager = (IGameEventManager2 *)appSystemFactory(INTERFACEVERSION_GAMEEVENTSMANAGER2,NULL)) == NULL )
  516. return false;
  517. if ( (datacache = (IDataCache*)appSystemFactory(DATACACHE_INTERFACE_VERSION, NULL )) == NULL )
  518. return false;
  519. if ( (soundemitterbase = (ISoundEmitterSystemBase *)appSystemFactory(SOUNDEMITTERSYSTEM_INTERFACE_VERSION, NULL)) == NULL )
  520. return false;
  521. #ifndef _XBOX
  522. if ( (gamestatsuploader = (IUploadGameStats *)appSystemFactory( INTERFACEVERSION_UPLOADGAMESTATS, NULL )) == NULL )
  523. return false;
  524. #endif
  525. if ( !mdlcache )
  526. return false;
  527. if ( (serverpluginhelpers = (IServerPluginHelpers *)appSystemFactory(INTERFACEVERSION_ISERVERPLUGINHELPERS, NULL)) == NULL )
  528. return false;
  529. if ( (scenefilecache = (ISceneFileCache *)appSystemFactory( SCENE_FILE_CACHE_INTERFACE_VERSION, NULL )) == NULL )
  530. return false;
  531. if ( IsX360() && (xboxsystem = (IXboxSystem *)appSystemFactory( XBOXSYSTEM_INTERFACE_VERSION, NULL )) == NULL )
  532. return false;
  533. if ( IsX360() && (matchmaking = (IMatchmaking *)appSystemFactory( VENGINE_MATCHMAKING_VERSION, NULL )) == NULL )
  534. return false;
  535. // If not running dedicated, grab the engine vgui interface
  536. if ( !engine->IsDedicatedServer() )
  537. {
  538. #ifdef _WIN32
  539. // This interface is optional, and is only valid when running with -tools
  540. serverenginetools = ( IServerEngineTools * )appSystemFactory( VSERVERENGINETOOLS_INTERFACE_VERSION, NULL );
  541. #endif
  542. }
  543. // Yes, both the client and game .dlls will try to Connect, the soundemittersystem.dll will handle this gracefully
  544. if ( !soundemitterbase->Connect( appSystemFactory ) )
  545. return false;
  546. // cache the globals
  547. gpGlobals = pGlobals;
  548. g_pSharedChangeInfo = engine->GetSharedEdictChangeInfo();
  549. MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
  550. // save these in case other system inits need them
  551. factorylist_t factories;
  552. factories.engineFactory = appSystemFactory;
  553. factories.fileSystemFactory = fileSystemFactory;
  554. factories.physicsFactory = physicsFactory;
  555. FactoryList_Store( factories );
  556. // load used game events
  557. gameeventmanager->LoadEventsFromFile("resource/gameevents.res");
  558. // init the cvar list first in case inits want to reference them
  559. InitializeCvars();
  560. // Initialize the particle system
  561. if ( !g_pParticleSystemMgr->Init( g_pParticleSystemQuery ) )
  562. {
  563. return false;
  564. }
  565. sv_cheats = g_pCVar->FindVar( "sv_cheats" );
  566. if ( !sv_cheats )
  567. return false;
  568. g_pcv_commentary = g_pCVar->FindVar( "commentary" );
  569. g_pcv_ThreadMode = g_pCVar->FindVar( "host_thread_mode" );
  570. g_pcv_hideServer = g_pCVar->FindVar( "hide_server" );
  571. sv_maxreplay = g_pCVar->FindVar( "sv_maxreplay" );
  572. g_pGameSaveRestoreBlockSet->AddBlockHandler( GetEntitySaveRestoreBlockHandler() );
  573. g_pGameSaveRestoreBlockSet->AddBlockHandler( GetPhysSaveRestoreBlockHandler() );
  574. g_pGameSaveRestoreBlockSet->AddBlockHandler( GetAISaveRestoreBlockHandler() );
  575. g_pGameSaveRestoreBlockSet->AddBlockHandler( GetTemplateSaveRestoreBlockHandler() );
  576. g_pGameSaveRestoreBlockSet->AddBlockHandler( GetDefaultResponseSystemSaveRestoreBlockHandler() );
  577. g_pGameSaveRestoreBlockSet->AddBlockHandler( GetCommentarySaveRestoreBlockHandler() );
  578. g_pGameSaveRestoreBlockSet->AddBlockHandler( GetEventQueueSaveRestoreBlockHandler() );
  579. g_pGameSaveRestoreBlockSet->AddBlockHandler( GetAchievementSaveRestoreBlockHandler() );
  580. // The string system must init first + shutdown last
  581. IGameSystem::Add( GameStringSystem() );
  582. // Physics must occur before the sound envelope manager
  583. IGameSystem::Add( PhysicsGameSystem() );
  584. // Used to service deferred navigation queries for NPCs
  585. IGameSystem::Add( (IGameSystem *) PostFrameNavigationSystem() );
  586. // Add game log system
  587. IGameSystem::Add( GameLogSystem() );
  588. #ifndef _XBOX
  589. // Add HLTV director
  590. IGameSystem::Add( HLTVDirectorSystem() );
  591. #endif
  592. // Add sound emitter
  593. IGameSystem::Add( SoundEmitterSystem() );
  594. // load Mod specific game events ( MUST be before InitAllSystems() so it can pickup the mod specific events)
  595. gameeventmanager->LoadEventsFromFile("resource/ModEvents.res");
  596. #ifdef CSTRIKE_DLL // BOTPORT: TODO: move these ifdefs out
  597. InstallBotControl();
  598. #endif
  599. if ( !IGameSystem::InitAllSystems() )
  600. return false;
  601. #if defined( REPLAY_ENABLED )
  602. if ( gameeventmanager->LoadEventsFromFile( "resource/replayevents.res" ) <= 0 )
  603. {
  604. Warning( "\n*\n* replayevents.res MISSING.\n*\n\n" );
  605. return false;
  606. }
  607. #endif
  608. // Due to dependencies, these are not autogamesystems
  609. if ( !ModelSoundsCacheInit() )
  610. {
  611. return false;
  612. }
  613. InvalidateQueryCache();
  614. // Parse the particle manifest file & register the effects within it
  615. ParseParticleEffects( false, false );
  616. // try to get debug overlay, may be NULL if on HLDS
  617. debugoverlay = (IVDebugOverlay *)appSystemFactory( VDEBUG_OVERLAY_INTERFACE_VERSION, NULL );
  618. #ifndef _XBOX
  619. #ifdef USE_NAV_MESH
  620. // create the Navigation Mesh interface
  621. TheNavMesh = NavMeshFactory();
  622. #endif
  623. // init the gamestatsupload connection
  624. gamestatsuploader->InitConnection();
  625. #endif
  626. return true;
  627. }
  628. void CServerGameDLL::PostInit()
  629. {
  630. IGameSystem::PostInitAllSystems();
  631. }
  632. void CServerGameDLL::DLLShutdown( void )
  633. {
  634. // Due to dependencies, these are not autogamesystems
  635. ModelSoundsCacheShutdown();
  636. g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetAchievementSaveRestoreBlockHandler() );
  637. g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetCommentarySaveRestoreBlockHandler() );
  638. g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetEventQueueSaveRestoreBlockHandler() );
  639. g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetDefaultResponseSystemSaveRestoreBlockHandler() );
  640. g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetTemplateSaveRestoreBlockHandler() );
  641. g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetAISaveRestoreBlockHandler() );
  642. g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetPhysSaveRestoreBlockHandler() );
  643. g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetEntitySaveRestoreBlockHandler() );
  644. char *pFilename = g_TextStatsMgr.GetStatsFilename();
  645. if ( !pFilename || !pFilename[0] )
  646. {
  647. g_TextStatsMgr.SetStatsFilename( "stats.txt" );
  648. }
  649. g_TextStatsMgr.WriteFile( filesystem );
  650. IGameSystem::ShutdownAllSystems();
  651. #ifdef CSTRIKE_DLL // BOTPORT: TODO: move these ifdefs out
  652. RemoveBotControl();
  653. #endif
  654. #ifndef _XBOX
  655. #ifdef USE_NAV_MESH
  656. // destroy the Navigation Mesh interface
  657. if ( TheNavMesh )
  658. {
  659. delete TheNavMesh;
  660. TheNavMesh = NULL;
  661. }
  662. #endif
  663. // reset (shutdown) the gamestatsupload connection
  664. gamestatsuploader->InitConnection();
  665. #endif
  666. #ifndef _X360
  667. s_SteamAPIContext.Clear(); // Steam API context shutdown
  668. s_SteamGameServerAPIContext.Clear();
  669. #endif
  670. gameeventmanager = NULL;
  671. DisconnectTier3Libraries();
  672. DisconnectTier2Libraries();
  673. ConVar_Unregister();
  674. DisconnectTier1Libraries();
  675. }
  676. bool CServerGameDLL::ReplayInit( CreateInterfaceFn fnReplayFactory )
  677. {
  678. #if defined( REPLAY_ENABLED )
  679. if ( !IsPC() )
  680. return false;
  681. if ( (g_pReplay = ( IReplaySystem *)fnReplayFactory( REPLAY_INTERFACE_VERSION, NULL )) == NULL )
  682. return false;
  683. if ( (g_pReplayServerContext = g_pReplay->SV_GetContext()) == NULL )
  684. return false;
  685. return true;
  686. #else
  687. return false;
  688. #endif
  689. }
  690. //-----------------------------------------------------------------------------
  691. // Purpose: See shareddefs.h for redefining this. Don't even think about it, though, for HL2. Or you will pay. ywb 9/22/03
  692. // Output : float
  693. //-----------------------------------------------------------------------------
  694. float CServerGameDLL::GetTickInterval( void ) const
  695. {
  696. float tickinterval = DEFAULT_TICK_INTERVAL;
  697. //=============================================================================
  698. // HPE_BEGIN:
  699. // [Forrest] For Counter-Strike, set default tick rate of 66 and removed -tickrate command line parameter.
  700. //=============================================================================
  701. // Ignoring this for now, server ops are abusing it
  702. #if !defined( TF_DLL ) && !defined( CSTRIKE_DLL ) && !defined( DOD_DLL )
  703. //=============================================================================
  704. // HPE_END
  705. //=============================================================================
  706. // override if tick rate specified in command line
  707. if ( CommandLine()->CheckParm( "-tickrate" ) )
  708. {
  709. float tickrate = CommandLine()->ParmValue( "-tickrate", 0 );
  710. if ( tickrate > 10 )
  711. tickinterval = 1.0f / tickrate;
  712. }
  713. #endif
  714. return tickinterval;
  715. }
  716. // This is called when a new game is started. (restart, map)
  717. bool CServerGameDLL::GameInit( void )
  718. {
  719. ResetGlobalState();
  720. engine->ServerCommand( "exec game.cfg\n" );
  721. engine->ServerExecute( );
  722. CBaseEntity::sm_bAccurateTriggerBboxChecks = true;
  723. IGameEvent *event = gameeventmanager->CreateEvent( "game_init" );
  724. if ( event )
  725. {
  726. gameeventmanager->FireEvent( event );
  727. }
  728. return true;
  729. }
  730. // This is called when a game ends (server disconnect, death, restart, load)
  731. // NOT on level transitions within a game
  732. void CServerGameDLL::GameShutdown( void )
  733. {
  734. ResetGlobalState();
  735. }
  736. static bool g_OneWayTransition = false;
  737. void Game_SetOneWayTransition( void )
  738. {
  739. g_OneWayTransition = true;
  740. }
  741. static CUtlVector<EHANDLE> g_RestoredEntities;
  742. // just for debugging, assert that this is the only time this function is called
  743. static bool g_InRestore = false;
  744. void AddRestoredEntity( CBaseEntity *pEntity )
  745. {
  746. Assert(g_InRestore);
  747. if ( !pEntity )
  748. return;
  749. g_RestoredEntities.AddToTail( EHANDLE(pEntity) );
  750. }
  751. void EndRestoreEntities()
  752. {
  753. if ( !g_InRestore )
  754. return;
  755. // The entire hierarchy is restored, so we can call GetAbsOrigin again.
  756. //CBaseEntity::SetAbsQueriesValid( true );
  757. // Call all entities' OnRestore handlers
  758. for ( int i = g_RestoredEntities.Count()-1; i >=0; --i )
  759. {
  760. CBaseEntity *pEntity = g_RestoredEntities[i].Get();
  761. if ( pEntity && !pEntity->IsDormant() )
  762. {
  763. MDLCACHE_CRITICAL_SECTION();
  764. pEntity->OnRestore();
  765. }
  766. }
  767. g_RestoredEntities.Purge();
  768. IGameSystem::OnRestoreAllSystems();
  769. g_InRestore = false;
  770. gEntList.CleanupDeleteList();
  771. // HACKHACK: UNDONE: We need to redesign the main loop with respect to save/load/server activate
  772. g_ServerGameDLL.ServerActivate( NULL, 0, 0 );
  773. CBaseEntity::SetAllowPrecache( false );
  774. }
  775. void BeginRestoreEntities()
  776. {
  777. if ( g_InRestore )
  778. {
  779. DevMsg( "BeginRestoreEntities without previous EndRestoreEntities.\n" );
  780. gEntList.CleanupDeleteList();
  781. }
  782. g_RestoredEntities.Purge();
  783. g_InRestore = true;
  784. CBaseEntity::SetAllowPrecache( true );
  785. // No calls to GetAbsOrigin until the entire hierarchy is restored!
  786. //CBaseEntity::SetAbsQueriesValid( false );
  787. }
  788. //-----------------------------------------------------------------------------
  789. // Purpose: This prevents sv.tickcount/gpGlobals->tickcount from advancing during restore which
  790. // would cause a lot of the NPCs to fast forward their think times to the same
  791. // tick due to some ticks being elapsed during restore where there was no simulation going on
  792. //-----------------------------------------------------------------------------
  793. bool CServerGameDLL::IsRestoring()
  794. {
  795. return g_InRestore;
  796. }
  797. // Called any time a new level is started (after GameInit() also on level transitions within a game)
  798. bool CServerGameDLL::LevelInit( const char *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background )
  799. {
  800. VPROF("CServerGameDLL::LevelInit");
  801. #ifdef USES_ECON_ITEMS
  802. GameItemSchema_t *pItemSchema = ItemSystem()->GetItemSchema();
  803. if ( pItemSchema )
  804. {
  805. pItemSchema->BInitFromDelayedBuffer();
  806. }
  807. #endif // USES_ECON_ITEMS
  808. ResetWindspeed();
  809. UpdateChapterRestrictions( pMapName );
  810. if ( IsX360() && !background && (gpGlobals->maxClients == 1) && (g_nCurrentChapterIndex >= 0) )
  811. {
  812. // Single player games tell xbox live what game & chapter the user is playing
  813. UpdateRichPresence();
  814. }
  815. //Tony; parse custom manifest if exists!
  816. ParseParticleEffectsMap( pMapName, false );
  817. // IGameSystem::LevelInitPreEntityAllSystems() is called when the world is precached
  818. // That happens either in LoadGameState() or in MapEntity_ParseAllEntities()
  819. if ( loadGame )
  820. {
  821. if ( pOldLevel )
  822. {
  823. gpGlobals->eLoadType = MapLoad_Transition;
  824. }
  825. else
  826. {
  827. gpGlobals->eLoadType = MapLoad_LoadGame;
  828. }
  829. BeginRestoreEntities();
  830. if ( !engine->LoadGameState( pMapName, 1 ) )
  831. {
  832. if ( pOldLevel )
  833. {
  834. MapEntity_ParseAllEntities( pMapEntities );
  835. }
  836. else
  837. {
  838. // Regular save load case
  839. return false;
  840. }
  841. }
  842. if ( pOldLevel )
  843. {
  844. engine->LoadAdjacentEnts( pOldLevel, pLandmarkName );
  845. }
  846. if ( g_OneWayTransition )
  847. {
  848. engine->ClearSaveDirAfterClientLoad();
  849. }
  850. if ( pOldLevel && sv_autosave.GetBool() == true )
  851. {
  852. // This is a single-player style level transition.
  853. // Queue up an autosave one second into the level
  854. CBaseEntity *pAutosave = CBaseEntity::Create( "logic_autosave", vec3_origin, vec3_angle, NULL );
  855. if ( pAutosave )
  856. {
  857. g_EventQueue.AddEvent( pAutosave, "Save", 1.0, NULL, NULL );
  858. g_EventQueue.AddEvent( pAutosave, "Kill", 1.1, NULL, NULL );
  859. }
  860. }
  861. }
  862. else
  863. {
  864. if ( background )
  865. {
  866. gpGlobals->eLoadType = MapLoad_Background;
  867. }
  868. else
  869. {
  870. gpGlobals->eLoadType = MapLoad_NewGame;
  871. }
  872. // Clear out entity references, and parse the entities into it.
  873. g_MapEntityRefs.Purge();
  874. CMapLoadEntityFilter filter;
  875. MapEntity_ParseAllEntities( pMapEntities, &filter );
  876. g_pServerBenchmark->StartBenchmark();
  877. // Now call the mod specific parse
  878. LevelInit_ParseAllEntities( pMapEntities );
  879. }
  880. // Check low violence settings for this map
  881. g_RagdollLVManager.SetLowViolence( pMapName );
  882. // Now that all of the active entities have been loaded in, precache any entities who need point_template parameters
  883. // to be parsed (the above code has loaded all point_template entities)
  884. PrecachePointTemplates();
  885. // load MOTD from file into stringtable
  886. LoadMessageOfTheDay();
  887. // Sometimes an ent will Remove() itself during its precache, so RemoveImmediate won't happen.
  888. // This makes sure those ents get cleaned up.
  889. gEntList.CleanupDeleteList();
  890. g_AIFriendliesTalkSemaphore.Release();
  891. g_AIFoesTalkSemaphore.Release();
  892. g_OneWayTransition = false;
  893. // clear any pending autosavedangerous
  894. m_fAutoSaveDangerousTime = 0.0f;
  895. m_fAutoSaveDangerousMinHealthToCommit = 0.0f;
  896. return true;
  897. }
  898. //-----------------------------------------------------------------------------
  899. // Purpose: called after every level change and load game, iterates through all the
  900. // active entities and gives them a chance to fix up their state
  901. //-----------------------------------------------------------------------------
  902. #ifdef DEBUG
  903. bool g_bReceivedChainedActivate;
  904. bool g_bCheckForChainedActivate;
  905. #define BeginCheckChainedActivate() if (0) ; else { g_bCheckForChainedActivate = true; g_bReceivedChainedActivate = false; }
  906. #define EndCheckChainedActivate( bCheck ) \
  907. if (0) ; else \
  908. { \
  909. if ( bCheck ) \
  910. { \
  911. AssertMsg( g_bReceivedChainedActivate, "Entity (%i/%s/%s) failed to call base class Activate()\n", pClass->entindex(), pClass->GetClassname(), STRING( pClass->GetEntityName() ) ); \
  912. } \
  913. g_bCheckForChainedActivate = false; \
  914. }
  915. #else
  916. #define BeginCheckChainedActivate() ((void)0)
  917. #define EndCheckChainedActivate( bCheck ) ((void)0)
  918. #endif
  919. void CServerGameDLL::ServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
  920. {
  921. // HACKHACK: UNDONE: We need to redesign the main loop with respect to save/load/server activate
  922. if ( g_InRestore )
  923. return;
  924. if ( gEntList.ResetDeleteList() != 0 )
  925. {
  926. Msg( "%s", "ERROR: Entity delete queue not empty on level start!\n" );
  927. }
  928. for ( CBaseEntity *pClass = gEntList.FirstEnt(); pClass != NULL; pClass = gEntList.NextEnt(pClass) )
  929. {
  930. if ( pClass && !pClass->IsDormant() )
  931. {
  932. MDLCACHE_CRITICAL_SECTION();
  933. BeginCheckChainedActivate();
  934. pClass->Activate();
  935. // We don't care if it finished activating if it decided to remove itself.
  936. EndCheckChainedActivate( !( pClass->GetEFlags() & EFL_KILLME ) );
  937. }
  938. }
  939. IGameSystem::LevelInitPostEntityAllSystems();
  940. // No more precaching after PostEntityAllSystems!!!
  941. CBaseEntity::SetAllowPrecache( false );
  942. // only display the think limit when the game is run with "developer" mode set
  943. if ( !g_pDeveloper->GetInt() )
  944. {
  945. think_limit.SetValue( 0 );
  946. }
  947. #ifndef _XBOX
  948. #ifdef USE_NAV_MESH
  949. // load the Navigation Mesh for this map
  950. TheNavMesh->Load();
  951. TheNavMesh->OnServerActivate();
  952. #endif
  953. #endif
  954. #ifdef CSTRIKE_DLL // BOTPORT: TODO: move these ifdefs out
  955. TheBots->ServerActivate();
  956. #endif
  957. #ifdef NEXT_BOT
  958. TheNextBots().OnMapLoaded();
  959. #endif
  960. }
  961. //-----------------------------------------------------------------------------
  962. // Purpose: Called after the steam API has been activated post-level startup
  963. //-----------------------------------------------------------------------------
  964. void CServerGameDLL::GameServerSteamAPIActivated( void )
  965. {
  966. #ifndef NO_STEAM
  967. steamgameserverapicontext->Clear();
  968. steamgameserverapicontext->Init();
  969. if ( steamgameserverapicontext->SteamGameServer() && engine->IsDedicatedServer() )
  970. {
  971. steamgameserverapicontext->SteamGameServer()->GetGameplayStats();
  972. }
  973. #endif
  974. #ifdef TF_DLL
  975. GCClientSystem()->GameServerActivate();
  976. InventoryManager()->GameServerSteamAPIActivated();
  977. TFMapsWorkshop()->GameServerSteamAPIActivated();
  978. #endif
  979. }
  980. //-----------------------------------------------------------------------------
  981. // Purpose: Called after the steam API has been activated post-level startup
  982. //-----------------------------------------------------------------------------
  983. void CServerGameDLL::GameServerSteamAPIShutdown( void )
  984. {
  985. #if !defined( NO_STEAM )
  986. if ( steamgameserverapicontext )
  987. {
  988. steamgameserverapicontext->Clear();
  989. }
  990. #endif
  991. #ifdef TF_DLL
  992. GCClientSystem()->Shutdown();
  993. #endif
  994. }
  995. //-----------------------------------------------------------------------------
  996. // Purpose: Called at the start of every game frame
  997. //-----------------------------------------------------------------------------
  998. ConVar trace_report( "trace_report", "0" );
  999. void CServerGameDLL::GameFrame( bool simulating )
  1000. {
  1001. VPROF( "CServerGameDLL::GameFrame" );
  1002. // Don't run frames until fully restored
  1003. if ( g_InRestore )
  1004. return;
  1005. if ( CBaseEntity::IsSimulatingOnAlternateTicks() )
  1006. {
  1007. // only run simulation on even numbered ticks
  1008. if ( gpGlobals->tickcount & 1 )
  1009. {
  1010. UpdateAllClientData();
  1011. return;
  1012. }
  1013. // If we're skipping frames, then the frametime is 2x the normal tick
  1014. gpGlobals->frametime *= 2.0f;
  1015. }
  1016. float oldframetime = gpGlobals->frametime;
  1017. #ifdef _DEBUG
  1018. // For profiling.. let them enable/disable the networkvar manual mode stuff.
  1019. g_bUseNetworkVars = s_UseNetworkVars.GetBool();
  1020. #endif
  1021. extern void GameStartFrame( void );
  1022. extern void ServiceEventQueue( void );
  1023. extern void Physics_RunThinkFunctions( bool simulating );
  1024. // Delete anything that was marked for deletion
  1025. // outside of server frameloop (e.g., in response to concommand)
  1026. gEntList.CleanupDeleteList();
  1027. IGameSystem::FrameUpdatePreEntityThinkAllSystems();
  1028. GameStartFrame();
  1029. #ifndef _XBOX
  1030. #ifdef USE_NAV_MESH
  1031. TheNavMesh->Update();
  1032. #endif
  1033. #ifdef NEXT_BOT
  1034. TheNextBots().Update();
  1035. #endif
  1036. gamestatsuploader->UpdateConnection();
  1037. #endif
  1038. UpdateQueryCache();
  1039. g_pServerBenchmark->UpdateBenchmark();
  1040. Physics_RunThinkFunctions( simulating );
  1041. IGameSystem::FrameUpdatePostEntityThinkAllSystems();
  1042. // UNDONE: Make these systems IGameSystems and move these calls into FrameUpdatePostEntityThink()
  1043. // service event queue, firing off any actions whos time has come
  1044. ServiceEventQueue();
  1045. // free all ents marked in think functions
  1046. gEntList.CleanupDeleteList();
  1047. // FIXME: Should this only occur on the final tick?
  1048. UpdateAllClientData();
  1049. if ( g_pGameRules )
  1050. {
  1051. g_pGameRules->EndGameFrame();
  1052. }
  1053. if ( trace_report.GetBool() )
  1054. {
  1055. int total = 0, totals[3];
  1056. for ( int i = 0; i < 3; i++ )
  1057. {
  1058. totals[i] = enginetrace->GetStatByIndex( i, true );
  1059. if ( totals[i] > 0 )
  1060. {
  1061. total += totals[i];
  1062. }
  1063. }
  1064. if ( total )
  1065. {
  1066. Msg("Trace: %d, contents %d, enumerate %d\n", totals[0], totals[1], totals[2] );
  1067. }
  1068. }
  1069. // Any entities that detect network state changes on a timer do it here.
  1070. g_NetworkPropertyEventMgr.FireEvents();
  1071. gpGlobals->frametime = oldframetime;
  1072. }
  1073. //-----------------------------------------------------------------------------
  1074. // Purpose: Called every frame even if not ticking
  1075. // Input : simulating -
  1076. //-----------------------------------------------------------------------------
  1077. void CServerGameDLL::PreClientUpdate( bool simulating )
  1078. {
  1079. if ( !simulating )
  1080. return;
  1081. /*
  1082. if (game_speeds.GetInt())
  1083. {
  1084. DrawMeasuredSections();
  1085. }
  1086. */
  1087. //#ifdef _DEBUG - allow this in release for now
  1088. DrawAllDebugOverlays();
  1089. //#endif
  1090. IGameSystem::PreClientUpdateAllSystems();
  1091. #ifdef _DEBUG
  1092. if ( sv_showhitboxes.GetInt() == -1 )
  1093. return;
  1094. if ( sv_showhitboxes.GetInt() == 0 )
  1095. {
  1096. // assume it's text
  1097. CBaseEntity *pEntity = NULL;
  1098. while (1)
  1099. {
  1100. pEntity = gEntList.FindEntityByName( pEntity, sv_showhitboxes.GetString() );
  1101. if ( !pEntity )
  1102. break;
  1103. CBaseAnimating *anim = dynamic_cast< CBaseAnimating * >( pEntity );
  1104. if (anim)
  1105. {
  1106. anim->DrawServerHitboxes();
  1107. }
  1108. }
  1109. return;
  1110. }
  1111. CBaseAnimating *anim = dynamic_cast< CBaseAnimating * >( CBaseEntity::Instance( engine->PEntityOfEntIndex( sv_showhitboxes.GetInt() ) ) );
  1112. if ( !anim )
  1113. return;
  1114. anim->DrawServerHitboxes();
  1115. #endif
  1116. }
  1117. void CServerGameDLL::Think( bool finalTick )
  1118. {
  1119. if ( m_fAutoSaveDangerousTime != 0.0f && m_fAutoSaveDangerousTime < gpGlobals->curtime )
  1120. {
  1121. // The safety timer for a dangerous auto save has expired
  1122. CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
  1123. if ( pPlayer && ( pPlayer->GetDeathTime() == 0.0f || pPlayer->GetDeathTime() > gpGlobals->curtime )
  1124. && !pPlayer->IsSinglePlayerGameEnding()
  1125. )
  1126. {
  1127. if( pPlayer->GetHealth() >= m_fAutoSaveDangerousMinHealthToCommit )
  1128. {
  1129. // The player isn't dead, so make the dangerous auto save safe
  1130. engine->ServerCommand( "autosavedangerousissafe\n" );
  1131. }
  1132. }
  1133. m_fAutoSaveDangerousTime = 0.0f;
  1134. m_fAutoSaveDangerousMinHealthToCommit = 0.0f;
  1135. }
  1136. }
  1137. void CServerGameDLL::OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue )
  1138. {
  1139. }
  1140. // Called when a level is shutdown (including changing levels)
  1141. void CServerGameDLL::LevelShutdown( void )
  1142. {
  1143. #ifndef NO_STEAM
  1144. IGameSystem::LevelShutdownPreClearSteamAPIContextAllSystems();
  1145. steamgameserverapicontext->Clear();
  1146. #endif
  1147. g_pServerBenchmark->EndBenchmark();
  1148. MDLCACHE_CRITICAL_SECTION();
  1149. IGameSystem::LevelShutdownPreEntityAllSystems();
  1150. // YWB:
  1151. // This entity pointer is going away now and is corrupting memory on level transitions/restarts
  1152. CSoundEnt::ShutdownSoundEnt();
  1153. gEntList.Clear();
  1154. InvalidateQueryCache();
  1155. IGameSystem::LevelShutdownPostEntityAllSystems();
  1156. // In case we quit out during initial load
  1157. CBaseEntity::SetAllowPrecache( false );
  1158. g_nCurrentChapterIndex = -1;
  1159. #ifndef _XBOX
  1160. #ifdef USE_NAV_MESH
  1161. // reset the Navigation Mesh
  1162. if ( TheNavMesh )
  1163. {
  1164. TheNavMesh->Reset();
  1165. }
  1166. #endif
  1167. #endif
  1168. }
  1169. //-----------------------------------------------------------------------------
  1170. // Purpose:
  1171. // Input :
  1172. // Output : ServerClass*
  1173. //-----------------------------------------------------------------------------
  1174. ServerClass* CServerGameDLL::GetAllServerClasses()
  1175. {
  1176. return g_pServerClassHead;
  1177. }
  1178. const char *CServerGameDLL::GetGameDescription( void )
  1179. {
  1180. return ::GetGameDescription();
  1181. }
  1182. void CServerGameDLL::CreateNetworkStringTables( void )
  1183. {
  1184. // Create any shared string tables here (and only here!)
  1185. // E.g.: xxx = networkstringtable->CreateStringTable( "SceneStrings", 512 );
  1186. g_pStringTableParticleEffectNames = networkstringtable->CreateStringTable( "ParticleEffectNames", MAX_PARTICLESYSTEMS_STRINGS );
  1187. g_pStringTableEffectDispatch = networkstringtable->CreateStringTable( "EffectDispatch", MAX_EFFECT_DISPATCH_STRINGS );
  1188. g_pStringTableVguiScreen = networkstringtable->CreateStringTable( "VguiScreen", MAX_VGUI_SCREEN_STRINGS );
  1189. g_pStringTableMaterials = networkstringtable->CreateStringTable( "Materials", MAX_MATERIAL_STRINGS );
  1190. g_pStringTableInfoPanel = networkstringtable->CreateStringTable( "InfoPanel", MAX_INFOPANEL_STRINGS );
  1191. g_pStringTableClientSideChoreoScenes = networkstringtable->CreateStringTable( "Scenes", MAX_CHOREO_SCENES_STRINGS );
  1192. g_pStringTableServerMapCycle = networkstringtable->CreateStringTable( "ServerMapCycle", 128 );
  1193. #ifdef TF_DLL
  1194. g_pStringTableServerPopFiles = networkstringtable->CreateStringTable( "ServerPopFiles", 128 );
  1195. g_pStringTableServerMapCycleMvM = networkstringtable->CreateStringTable( "ServerMapCycleMvM", 128 );
  1196. #endif
  1197. bool bPopFilesValid = true;
  1198. (void)bPopFilesValid; // Avoid unreferenced variable warning
  1199. #ifdef TF_DLL
  1200. bPopFilesValid = ( g_pStringTableServerPopFiles != NULL );
  1201. #endif
  1202. Assert( g_pStringTableParticleEffectNames &&
  1203. g_pStringTableEffectDispatch &&
  1204. g_pStringTableVguiScreen &&
  1205. g_pStringTableMaterials &&
  1206. g_pStringTableInfoPanel &&
  1207. g_pStringTableClientSideChoreoScenes &&
  1208. g_pStringTableServerMapCycle &&
  1209. bPopFilesValid
  1210. );
  1211. // Need this so we have the error material always handy
  1212. PrecacheMaterial( "debug/debugempty" );
  1213. Assert( GetMaterialIndex( "debug/debugempty" ) == 0 );
  1214. PrecacheParticleSystem( "error" ); // ensure error particle system is handy
  1215. Assert( GetParticleSystemIndex( "error" ) == 0 );
  1216. CreateNetworkStringTables_GameRules();
  1217. // Set up save/load utilities for string tables
  1218. g_VguiScreenStringOps.Init( g_pStringTableVguiScreen );
  1219. }
  1220. CSaveRestoreData *CServerGameDLL::SaveInit( int size )
  1221. {
  1222. return ::SaveInit(size);
  1223. }
  1224. //-----------------------------------------------------------------------------
  1225. // Purpose: Saves data from a struct into a saverestore object, to be saved to disk
  1226. // Input : *pSaveData - the saverestore object
  1227. // char *pname - the name of the data to write
  1228. // *pBaseData - the struct into which the data is to be read
  1229. // *pFields - pointer to an array of data field descriptions
  1230. // fieldCount - the size of the array (number of field descriptions)
  1231. //-----------------------------------------------------------------------------
  1232. void CServerGameDLL::SaveWriteFields( CSaveRestoreData *pSaveData, const char *pname, void *pBaseData, datamap_t *pMap, typedescription_t *pFields, int fieldCount )
  1233. {
  1234. CSave saveHelper( pSaveData );
  1235. saveHelper.WriteFields( pname, pBaseData, pMap, pFields, fieldCount );
  1236. }
  1237. //-----------------------------------------------------------------------------
  1238. // Purpose: Reads data from a save/restore block into a structure
  1239. // Input : *pSaveData - the saverestore object
  1240. // char *pname - the name of the data to extract from
  1241. // *pBaseData - the struct into which the data is to be restored
  1242. // *pFields - pointer to an array of data field descriptions
  1243. // fieldCount - the size of the array (number of field descriptions)
  1244. //-----------------------------------------------------------------------------
  1245. //-----------------------------------------------------------------------------
  1246. void CServerGameDLL::SaveReadFields( CSaveRestoreData *pSaveData, const char *pname, void *pBaseData, datamap_t *pMap, typedescription_t *pFields, int fieldCount )
  1247. {
  1248. CRestore restoreHelper( pSaveData );
  1249. restoreHelper.ReadFields( pname, pBaseData, pMap, pFields, fieldCount );
  1250. }
  1251. //-----------------------------------------------------------------------------
  1252. void CServerGameDLL::SaveGlobalState( CSaveRestoreData *s )
  1253. {
  1254. ::SaveGlobalState(s);
  1255. }
  1256. void CServerGameDLL::RestoreGlobalState(CSaveRestoreData *s)
  1257. {
  1258. ::RestoreGlobalState(s);
  1259. }
  1260. void CServerGameDLL::Save( CSaveRestoreData *s )
  1261. {
  1262. CSave saveHelper( s );
  1263. g_pGameSaveRestoreBlockSet->Save( &saveHelper );
  1264. }
  1265. void CServerGameDLL::Restore( CSaveRestoreData *s, bool b)
  1266. {
  1267. CRestore restore(s);
  1268. g_pGameSaveRestoreBlockSet->Restore( &restore, b );
  1269. g_pGameSaveRestoreBlockSet->PostRestore();
  1270. }
  1271. //-----------------------------------------------------------------------------
  1272. // Purpose:
  1273. // Input : msg_type -
  1274. // *name -
  1275. // size -
  1276. // Output : Returns true on success, false on failure.
  1277. //-----------------------------------------------------------------------------
  1278. bool CServerGameDLL::GetUserMessageInfo( int msg_type, char *name, int maxnamelength, int& size )
  1279. {
  1280. if ( !usermessages->IsValidIndex( msg_type ) )
  1281. return false;
  1282. Q_strncpy( name, usermessages->GetUserMessageName( msg_type ), maxnamelength );
  1283. size = usermessages->GetUserMessageSize( msg_type );
  1284. return true;
  1285. }
  1286. CStandardSendProxies* CServerGameDLL::GetStandardSendProxies()
  1287. {
  1288. return &g_StandardSendProxies;
  1289. }
  1290. int CServerGameDLL::CreateEntityTransitionList( CSaveRestoreData *s, int a)
  1291. {
  1292. CRestore restoreHelper( s );
  1293. // save off file base
  1294. int base = restoreHelper.GetReadPos();
  1295. int movedCount = ::CreateEntityTransitionList(s, a);
  1296. if ( movedCount )
  1297. {
  1298. g_pGameSaveRestoreBlockSet->CallBlockHandlerRestore( GetPhysSaveRestoreBlockHandler(), base, &restoreHelper, false );
  1299. g_pGameSaveRestoreBlockSet->CallBlockHandlerRestore( GetAISaveRestoreBlockHandler(), base, &restoreHelper, false );
  1300. }
  1301. GetPhysSaveRestoreBlockHandler()->PostRestore();
  1302. GetAISaveRestoreBlockHandler()->PostRestore();
  1303. return movedCount;
  1304. }
  1305. void CServerGameDLL::PreSave( CSaveRestoreData *s )
  1306. {
  1307. g_pGameSaveRestoreBlockSet->PreSave( s );
  1308. }
  1309. #include "client_textmessage.h"
  1310. // This little hack lets me marry BSP names to messages in titles.txt
  1311. typedef struct
  1312. {
  1313. const char *pBSPName;
  1314. const char *pTitleName;
  1315. } TITLECOMMENT;
  1316. // this list gets searched for the first partial match, so some are out of order
  1317. static TITLECOMMENT gTitleComments[] =
  1318. {
  1319. #ifdef HL1_DLL
  1320. { "t0a0", "#T0A0TITLE" },
  1321. { "c0a0", "#HL1_Chapter1_Title" },
  1322. { "c1a0", "#HL1_Chapter2_Title" },
  1323. { "c1a1", "#HL1_Chapter3_Title" },
  1324. { "c1a2", "#HL1_Chapter4_Title" },
  1325. { "c1a3", "#HL1_Chapter5_Title" },
  1326. { "c1a4", "#HL1_Chapter6_Title" },
  1327. { "c2a1", "#HL1_Chapter7_Title" },
  1328. { "c2a2", "#HL1_Chapter8_Title" },
  1329. { "c2a3", "#HL1_Chapter9_Title" },
  1330. { "c2a4d", "#HL1_Chapter11_Title" }, // These must appear before "C2A4" so all other map names starting with C2A4 get that title
  1331. { "c2a4e", "#HL1_Chapter11_Title" },
  1332. { "c2a4f", "#HL1_Chapter11_Title" },
  1333. { "c2a4g", "#HL1_Chapter11_Title" },
  1334. { "c2a4", "#HL1_Chapter10_Title" },
  1335. { "c2a5", "#HL1_Chapter12_Title" },
  1336. { "c3a1", "#HL1_Chapter13_Title" },
  1337. { "c3a2", "#HL1_Chapter14_Title" },
  1338. { "c4a1a", "#HL1_Chapter17_Title" }, // Order is important, see above
  1339. { "c4a1b", "#HL1_Chapter17_Title" },
  1340. { "c4a1c", "#HL1_Chapter17_Title" },
  1341. { "c4a1d", "#HL1_Chapter17_Title" },
  1342. { "c4a1e", "#HL1_Chapter17_Title" },
  1343. { "c4a1", "#HL1_Chapter15_Title" },
  1344. { "c4a2", "#HL1_Chapter16_Title" },
  1345. { "c4a3", "#HL1_Chapter18_Title" },
  1346. { "c5a1", "#HL1_Chapter19_Title" },
  1347. #elif defined PORTAL
  1348. { "testchmb_a_00", "#Portal_Chapter1_Title" },
  1349. { "testchmb_a_01", "#Portal_Chapter1_Title" },
  1350. { "testchmb_a_02", "#Portal_Chapter2_Title" },
  1351. { "testchmb_a_03", "#Portal_Chapter2_Title" },
  1352. { "testchmb_a_04", "#Portal_Chapter3_Title" },
  1353. { "testchmb_a_05", "#Portal_Chapter3_Title" },
  1354. { "testchmb_a_06", "#Portal_Chapter4_Title" },
  1355. { "testchmb_a_07", "#Portal_Chapter4_Title" },
  1356. { "testchmb_a_08_advanced", "#Portal_Chapter5_Title" },
  1357. { "testchmb_a_08", "#Portal_Chapter5_Title" },
  1358. { "testchmb_a_09_advanced", "#Portal_Chapter6_Title" },
  1359. { "testchmb_a_09", "#Portal_Chapter6_Title" },
  1360. { "testchmb_a_10_advanced", "#Portal_Chapter7_Title" },
  1361. { "testchmb_a_10", "#Portal_Chapter7_Title" },
  1362. { "testchmb_a_11_advanced", "#Portal_Chapter8_Title" },
  1363. { "testchmb_a_11", "#Portal_Chapter8_Title" },
  1364. { "testchmb_a_13_advanced", "#Portal_Chapter9_Title" },
  1365. { "testchmb_a_13", "#Portal_Chapter9_Title" },
  1366. { "testchmb_a_14_advanced", "#Portal_Chapter10_Title" },
  1367. { "testchmb_a_14", "#Portal_Chapter10_Title" },
  1368. { "testchmb_a_15", "#Portal_Chapter11_Title" },
  1369. { "escape_", "#Portal_Chapter11_Title" },
  1370. { "background2", "#Portal_Chapter12_Title" },
  1371. #else
  1372. { "intro", "#HL2_Chapter1_Title" },
  1373. { "d1_trainstation_05", "#HL2_Chapter2_Title" },
  1374. { "d1_trainstation_06", "#HL2_Chapter2_Title" },
  1375. { "d1_trainstation_", "#HL2_Chapter1_Title" },
  1376. { "d1_canals_06", "#HL2_Chapter4_Title" },
  1377. { "d1_canals_07", "#HL2_Chapter4_Title" },
  1378. { "d1_canals_08", "#HL2_Chapter4_Title" },
  1379. { "d1_canals_09", "#HL2_Chapter4_Title" },
  1380. { "d1_canals_1", "#HL2_Chapter4_Title" },
  1381. { "d1_canals_0", "#HL2_Chapter3_Title" },
  1382. { "d1_eli_", "#HL2_Chapter5_Title" },
  1383. { "d1_town_", "#HL2_Chapter6_Title" },
  1384. { "d2_coast_09", "#HL2_Chapter8_Title" },
  1385. { "d2_coast_1", "#HL2_Chapter8_Title" },
  1386. { "d2_prison_01", "#HL2_Chapter8_Title" },
  1387. { "d2_coast_", "#HL2_Chapter7_Title" },
  1388. { "d2_prison_06", "#HL2_Chapter9a_Title" },
  1389. { "d2_prison_07", "#HL2_Chapter9a_Title" },
  1390. { "d2_prison_08", "#HL2_Chapter9a_Title" },
  1391. { "d2_prison_", "#HL2_Chapter9_Title" },
  1392. { "d3_c17_01", "#HL2_Chapter9a_Title" },
  1393. { "d3_c17_09", "#HL2_Chapter11_Title" },
  1394. { "d3_c17_1", "#HL2_Chapter11_Title" },
  1395. { "d3_c17_", "#HL2_Chapter10_Title" },
  1396. { "d3_citadel_", "#HL2_Chapter12_Title" },
  1397. { "d3_breen_", "#HL2_Chapter13_Title" },
  1398. { "credits", "#HL2_Chapter14_Title" },
  1399. { "ep1_citadel_00", "#episodic_Chapter1_Title" },
  1400. { "ep1_citadel_01", "#episodic_Chapter1_Title" },
  1401. { "ep1_citadel_02b", "#episodic_Chapter1_Title" },
  1402. { "ep1_citadel_02", "#episodic_Chapter1_Title" },
  1403. { "ep1_citadel_03", "#episodic_Chapter2_Title" },
  1404. { "ep1_citadel_04", "#episodic_Chapter2_Title" },
  1405. { "ep1_c17_00a", "#episodic_Chapter3_Title" },
  1406. { "ep1_c17_00", "#episodic_Chapter3_Title" },
  1407. { "ep1_c17_01", "#episodic_Chapter4_Title" },
  1408. { "ep1_c17_02b", "#episodic_Chapter4_Title" },
  1409. { "ep1_c17_02", "#episodic_Chapter4_Title" },
  1410. { "ep1_c17_05", "#episodic_Chapter5_Title" },
  1411. { "ep1_c17_06", "#episodic_Chapter5_Title" },
  1412. { "ep2_outland_01a", "#ep2_Chapter1_Title" },
  1413. { "ep2_outland_01", "#ep2_Chapter1_Title" },
  1414. { "ep2_outland_02", "#ep2_Chapter2_Title" },
  1415. { "ep2_outland_03", "#ep2_Chapter2_Title" },
  1416. { "ep2_outland_04", "#ep2_Chapter2_Title" },
  1417. { "ep2_outland_05", "#ep2_Chapter3_Title" },
  1418. { "ep2_outland_06a", "#ep2_Chapter4_Title" },
  1419. { "ep2_outland_06", "#ep2_Chapter3_Title" },
  1420. { "ep2_outland_07", "#ep2_Chapter4_Title" },
  1421. { "ep2_outland_08", "#ep2_Chapter4_Title" },
  1422. { "ep2_outland_09", "#ep2_Chapter5_Title" },
  1423. { "ep2_outland_10a", "#ep2_Chapter5_Title" },
  1424. { "ep2_outland_10", "#ep2_Chapter5_Title" },
  1425. { "ep2_outland_11a", "#ep2_Chapter6_Title" },
  1426. { "ep2_outland_11", "#ep2_Chapter6_Title" },
  1427. { "ep2_outland_12a", "#ep2_Chapter7_Title" },
  1428. { "ep2_outland_12", "#ep2_Chapter6_Title" },
  1429. #endif
  1430. };
  1431. #ifdef _XBOX
  1432. void CServerGameDLL::GetTitleName( const char *pMapName, char* pTitleBuff, int titleBuffSize )
  1433. {
  1434. // Try to find a matching title comment for this mapname
  1435. for ( int i = 0; i < ARRAYSIZE(gTitleComments); i++ )
  1436. {
  1437. if ( !Q_strnicmp( pMapName, gTitleComments[i].pBSPName, strlen(gTitleComments[i].pBSPName) ) )
  1438. {
  1439. Q_strncpy( pTitleBuff, gTitleComments[i].pTitleName, titleBuffSize );
  1440. return;
  1441. }
  1442. }
  1443. Q_strncpy( pTitleBuff, pMapName, titleBuffSize );
  1444. }
  1445. #endif
  1446. void CServerGameDLL::GetSaveComment( char *text, int maxlength, float flMinutes, float flSeconds, bool bNoTime )
  1447. {
  1448. char comment[64];
  1449. const char *pName;
  1450. int i;
  1451. char const *mapname = STRING( gpGlobals->mapname );
  1452. pName = NULL;
  1453. // Try to find a matching title comment for this mapname
  1454. for ( i = 0; i < ARRAYSIZE(gTitleComments) && !pName; i++ )
  1455. {
  1456. if ( !Q_strnicmp( mapname, gTitleComments[i].pBSPName, strlen(gTitleComments[i].pBSPName) ) )
  1457. {
  1458. // found one
  1459. int j;
  1460. // Got a message, post-process it to be save name friendly
  1461. Q_strncpy( comment, gTitleComments[i].pTitleName, sizeof( comment ) );
  1462. pName = comment;
  1463. j = 0;
  1464. // Strip out CRs
  1465. while ( j < 64 && comment[j] )
  1466. {
  1467. if ( comment[j] == '\n' || comment[j] == '\r' )
  1468. comment[j] = 0;
  1469. else
  1470. j++;
  1471. }
  1472. break;
  1473. }
  1474. }
  1475. // If we didn't get one, use the designer's map name, or the BSP name itself
  1476. if ( !pName )
  1477. {
  1478. pName = mapname;
  1479. }
  1480. if ( bNoTime )
  1481. {
  1482. Q_snprintf( text, maxlength, "%-64.64s", pName );
  1483. }
  1484. else
  1485. {
  1486. int minutes = flMinutes;
  1487. int seconds = flSeconds;
  1488. // Wow, this guy/gal must suck...!
  1489. if ( minutes >= 1000 )
  1490. {
  1491. minutes = 999;
  1492. seconds = 59;
  1493. }
  1494. int minutesAdd = ( seconds / 60 );
  1495. seconds %= 60;
  1496. // add the elapsed time at the end of the comment, for the ui to parse out
  1497. Q_snprintf( text, maxlength, "%-64.64s %03d:%02d", pName, (minutes + minutesAdd), seconds );
  1498. }
  1499. }
  1500. void CServerGameDLL::WriteSaveHeaders( CSaveRestoreData *s )
  1501. {
  1502. CSave saveHelper( s );
  1503. g_pGameSaveRestoreBlockSet->WriteSaveHeaders( &saveHelper );
  1504. g_pGameSaveRestoreBlockSet->PostSave();
  1505. }
  1506. void CServerGameDLL::ReadRestoreHeaders( CSaveRestoreData *s )
  1507. {
  1508. CRestore restoreHelper( s );
  1509. g_pGameSaveRestoreBlockSet->PreRestore();
  1510. g_pGameSaveRestoreBlockSet->ReadRestoreHeaders( &restoreHelper );
  1511. }
  1512. void CServerGameDLL::PreSaveGameLoaded( char const *pSaveName, bool bInGame )
  1513. {
  1514. gamestats->Event_PreSaveGameLoaded( pSaveName, bInGame );
  1515. }
  1516. //-----------------------------------------------------------------------------
  1517. // Purpose: Returns true if the game DLL wants the server not to be made public.
  1518. // Used by commentary system to hide multiplayer commentary servers from the master.
  1519. //-----------------------------------------------------------------------------
  1520. bool CServerGameDLL::ShouldHideServer( void )
  1521. {
  1522. if ( g_pcv_commentary && g_pcv_commentary->GetBool() )
  1523. return true;
  1524. if ( g_pcv_hideServer && g_pcv_hideServer->GetBool() )
  1525. return true;
  1526. if ( gpGlobals->eLoadType == MapLoad_Background )
  1527. return true;
  1528. #if defined( TF_DLL )
  1529. if ( GTFGCClientSystem()->ShouldHideServer() )
  1530. return true;
  1531. #endif
  1532. return false;
  1533. }
  1534. //-----------------------------------------------------------------------------
  1535. //
  1536. //-----------------------------------------------------------------------------
  1537. void CServerGameDLL::InvalidateMdlCache()
  1538. {
  1539. CBaseAnimating *pAnimating;
  1540. for ( CBaseEntity *pEntity = gEntList.FirstEnt(); pEntity != NULL; pEntity = gEntList.NextEnt(pEntity) )
  1541. {
  1542. pAnimating = dynamic_cast<CBaseAnimating *>(pEntity);
  1543. if ( pAnimating )
  1544. {
  1545. pAnimating->InvalidateMdlCache();
  1546. }
  1547. }
  1548. }
  1549. // interface to the new GC based lobby system
  1550. IServerGCLobby *CServerGameDLL::GetServerGCLobby()
  1551. {
  1552. #ifdef TF_DLL
  1553. return GTFGCClientSystem();
  1554. #else
  1555. return NULL;
  1556. #endif
  1557. }
  1558. void CServerGameDLL::SetServerHibernation( bool bHibernating )
  1559. {
  1560. m_bIsHibernating = bHibernating;
  1561. #ifdef INFESTED_DLL
  1562. if ( engine && engine->IsDedicatedServer() && m_bIsHibernating && ASWGameRules() )
  1563. {
  1564. ASWGameRules()->OnServerHibernating();
  1565. }
  1566. #endif
  1567. #ifdef TF_DLL
  1568. GTFGCClientSystem()->SetHibernation( bHibernating );
  1569. #endif
  1570. }
  1571. const char *CServerGameDLL::GetServerBrowserMapOverride()
  1572. {
  1573. #ifdef TF_DLL
  1574. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  1575. {
  1576. const char *pszFilenameShort = g_pPopulationManager ? g_pPopulationManager->GetPopulationFilenameShort() : NULL;
  1577. if ( pszFilenameShort && pszFilenameShort[0] )
  1578. {
  1579. return pszFilenameShort;
  1580. }
  1581. }
  1582. #endif
  1583. return NULL;
  1584. }
  1585. const char *CServerGameDLL::GetServerBrowserGameData()
  1586. {
  1587. CUtlString sResult;
  1588. #ifdef TF_DLL
  1589. sResult.Format( "tf_mm_trusted:%d,tf_mm_servermode:%d", tf_mm_trusted.GetInt(), tf_mm_servermode.GetInt() );
  1590. CMatchInfo *pMatch = GTFGCClientSystem()->GetMatch();
  1591. if ( !pMatch )
  1592. {
  1593. sResult.Append( ",lobby:0" );
  1594. }
  1595. else
  1596. {
  1597. sResult.Append( CFmtStr( ",lobby:%016llx", pMatch->m_nLobbyID ) );
  1598. }
  1599. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  1600. {
  1601. bool bMannup = pMatch && pMatch->m_eMatchGroup == k_nMatchGroup_MvM_MannUp;
  1602. sResult.Append( CFmtStr( ",mannup:%d", (int)bMannup ) );
  1603. }
  1604. #endif
  1605. static char rchResult[2048];
  1606. V_strcpy_safe( rchResult, sResult );
  1607. return rchResult;
  1608. }
  1609. //-----------------------------------------------------------------------------
  1610. void CServerGameDLL::Status( void (*print) (const char *fmt, ...) )
  1611. {
  1612. if ( g_pGameRules )
  1613. {
  1614. g_pGameRules->Status( print );
  1615. }
  1616. }
  1617. //-----------------------------------------------------------------------------
  1618. void CServerGameDLL::PrepareLevelResources( /* in/out */ char *pszMapName, size_t nMapNameSize,
  1619. /* in/out */ char *pszMapFile, size_t nMapFileSize )
  1620. {
  1621. #ifdef TF_DLL
  1622. TFMapsWorkshop()->PrepareLevelResources( pszMapName, nMapNameSize, pszMapFile, nMapFileSize );
  1623. #endif // TF_DLL
  1624. }
  1625. //-----------------------------------------------------------------------------
  1626. IServerGameDLL::ePrepareLevelResourcesResult
  1627. CServerGameDLL::AsyncPrepareLevelResources( /* in/out */ char *pszMapName, size_t nMapNameSize,
  1628. /* in/out */ char *pszMapFile, size_t nMapFileSize,
  1629. float *flProgress /* = NULL */ )
  1630. {
  1631. #ifdef TF_DLL
  1632. return TFMapsWorkshop()->AsyncPrepareLevelResources( pszMapName, nMapNameSize, pszMapFile, nMapFileSize, flProgress );
  1633. #endif // TF_DLL
  1634. if ( flProgress )
  1635. {
  1636. *flProgress = 1.f;
  1637. }
  1638. return IServerGameDLL::ePrepareLevelResources_Prepared;
  1639. }
  1640. //-----------------------------------------------------------------------------
  1641. IServerGameDLL::eCanProvideLevelResult CServerGameDLL::CanProvideLevel( /* in/out */ char *pMapName, int nMapNameMax )
  1642. {
  1643. #ifdef TF_DLL
  1644. return TFMapsWorkshop()->OnCanProvideLevel( pMapName, nMapNameMax );
  1645. #endif // TF_DLL
  1646. return IServerGameDLL::eCanProvideLevel_CannotProvide;
  1647. }
  1648. //-----------------------------------------------------------------------------
  1649. bool CServerGameDLL::IsManualMapChangeOkay( const char **pszReason )
  1650. {
  1651. if ( GameRules() )
  1652. {
  1653. return GameRules()->IsManualMapChangeOkay( pszReason );
  1654. }
  1655. return true;
  1656. }
  1657. //-----------------------------------------------------------------------------
  1658. // Purpose: Called during a transition, to build a map adjacency list
  1659. //-----------------------------------------------------------------------------
  1660. void CServerGameDLL::BuildAdjacentMapList( void )
  1661. {
  1662. // retrieve the pointer to the save data
  1663. CSaveRestoreData *pSaveData = gpGlobals->pSaveData;
  1664. if ( pSaveData )
  1665. pSaveData->levelInfo.connectionCount = BuildChangeList( pSaveData->levelInfo.levelList, MAX_LEVEL_CONNECTIONS );
  1666. }
  1667. //-----------------------------------------------------------------------------
  1668. // Purpose: Sanity-check to verify that a path is a relative path inside the game dir
  1669. // Taken From: engine/cmd.cpp
  1670. //-----------------------------------------------------------------------------
  1671. static bool IsValidPath( const char *pszFilename )
  1672. {
  1673. if ( !pszFilename )
  1674. {
  1675. return false;
  1676. }
  1677. if ( Q_strlen( pszFilename ) <= 0 ||
  1678. Q_IsAbsolutePath( pszFilename ) || // to protect absolute paths
  1679. Q_strstr( pszFilename, ".." ) ) // to protect relative paths
  1680. {
  1681. return false;
  1682. }
  1683. return true;
  1684. }
  1685. static void ValidateMOTDFilename( IConVar *pConVar, const char *oldValue, float flOldValue )
  1686. {
  1687. ConVarRef var( pConVar );
  1688. if ( !IsValidPath( var.GetString() ) )
  1689. {
  1690. var.SetValue( var.GetDefault() );
  1691. }
  1692. }
  1693. static ConVar motdfile( "motdfile", "motd.txt", 0, "The MOTD file to load.", ValidateMOTDFilename );
  1694. static ConVar motdfile_text( "motdfile_text", "motd_text.txt", 0, "The text-only MOTD file to use for clients that have disabled HTML MOTDs.", ValidateMOTDFilename );
  1695. void CServerGameDLL::LoadMessageOfTheDay()
  1696. {
  1697. LoadSpecificMOTDMsg( motdfile, "motd" );
  1698. LoadSpecificMOTDMsg( motdfile_text, "motd_text" );
  1699. }
  1700. void CServerGameDLL::LoadSpecificMOTDMsg( const ConVar &convar, const char *pszStringName )
  1701. {
  1702. #ifndef _XBOX
  1703. CUtlBuffer buf;
  1704. // Generate preferred filename, which is in the cfg folder.
  1705. char szPreferredFilename[ MAX_PATH ];
  1706. V_sprintf_safe( szPreferredFilename, "cfg/%s", convar.GetString() );
  1707. // Check the preferred filename first
  1708. char szResolvedFilename[ MAX_PATH ];
  1709. V_strcpy_safe( szResolvedFilename, szPreferredFilename );
  1710. bool bFound = filesystem->ReadFile( szResolvedFilename, "GAME", buf );
  1711. // Not found? Try in the root, which is the old place it used to go.
  1712. if ( !bFound )
  1713. {
  1714. V_strcpy_safe( szResolvedFilename, convar.GetString() );
  1715. bFound = filesystem->ReadFile( szResolvedFilename, "GAME", buf );
  1716. }
  1717. // Still not found? See if we can try the default.
  1718. if ( !bFound && !V_stricmp( convar.GetString(), convar.GetDefault() ) )
  1719. {
  1720. V_strcpy_safe( szResolvedFilename, szPreferredFilename );
  1721. char *dotTxt = V_stristr( szResolvedFilename, ".txt" );
  1722. Assert ( dotTxt != NULL );
  1723. if ( dotTxt ) V_strcpy( dotTxt, "_default.txt" );
  1724. bFound = filesystem->ReadFile( szResolvedFilename, "GAME", buf );
  1725. }
  1726. if ( !bFound )
  1727. {
  1728. Msg( "'%s' not found; not loaded\n", szPreferredFilename );
  1729. return;
  1730. }
  1731. if ( buf.TellPut() > 2048 )
  1732. {
  1733. Warning("'%s' is too big; not loaded\n", szResolvedFilename );
  1734. return;
  1735. }
  1736. buf.PutChar( '\0' );
  1737. if ( V_stricmp( szPreferredFilename, szResolvedFilename ) == 0)
  1738. {
  1739. Msg( "Set %s from file '%s'\n", pszStringName, szResolvedFilename );
  1740. }
  1741. else
  1742. {
  1743. Msg( "Set %s from file '%s'. ('%s' was not found.)\n", pszStringName, szResolvedFilename, szPreferredFilename );
  1744. }
  1745. g_pStringTableInfoPanel->AddString( CBaseEntity::IsServer(), pszStringName, buf.TellPut(), buf.Base() );
  1746. #endif
  1747. }
  1748. // keeps track of which chapters the user has unlocked
  1749. ConVar sv_unlockedchapters( "sv_unlockedchapters", "1", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX );
  1750. //-----------------------------------------------------------------------------
  1751. // Purpose: Updates which chapters are unlocked
  1752. //-----------------------------------------------------------------------------
  1753. void UpdateChapterRestrictions( const char *mapname )
  1754. {
  1755. // look at the chapter for this map
  1756. char chapterTitle[64];
  1757. chapterTitle[0] = 0;
  1758. for ( int i = 0; i < ARRAYSIZE(gTitleComments); i++ )
  1759. {
  1760. if ( !Q_strnicmp( mapname, gTitleComments[i].pBSPName, strlen(gTitleComments[i].pBSPName) ) )
  1761. {
  1762. // found
  1763. Q_strncpy( chapterTitle, gTitleComments[i].pTitleName, sizeof( chapterTitle ) );
  1764. int j = 0;
  1765. while ( j < 64 && chapterTitle[j] )
  1766. {
  1767. if ( chapterTitle[j] == '\n' || chapterTitle[j] == '\r' )
  1768. chapterTitle[j] = 0;
  1769. else
  1770. j++;
  1771. }
  1772. break;
  1773. }
  1774. }
  1775. if ( !chapterTitle[0] )
  1776. return;
  1777. // make sure the specified chapter title is unlocked
  1778. strlwr( chapterTitle );
  1779. // Get our active mod directory name
  1780. char modDir[MAX_PATH];
  1781. if ( UTIL_GetModDir( modDir, sizeof(modDir) ) == false )
  1782. return;
  1783. char chapterNumberPrefix[64];
  1784. Q_snprintf(chapterNumberPrefix, sizeof(chapterNumberPrefix), "#%s_chapter", modDir);
  1785. const char *newChapterNumber = strstr( chapterTitle, chapterNumberPrefix );
  1786. if ( newChapterNumber )
  1787. {
  1788. // cut off the front
  1789. newChapterNumber += strlen( chapterNumberPrefix );
  1790. char newChapter[32];
  1791. Q_strncpy( newChapter, newChapterNumber, sizeof(newChapter) );
  1792. // cut off the end
  1793. char *end = strstr( newChapter, "_title" );
  1794. if ( end )
  1795. {
  1796. *end = 0;
  1797. }
  1798. int nNewChapter = atoi( newChapter );
  1799. // HACK: HL2 added a zany chapter "9a" which wreaks
  1800. // havoc in this stupid atoi-based chapter code.
  1801. if ( !Q_stricmp( modDir, "hl2" ) )
  1802. {
  1803. if ( !Q_stricmp( newChapter, "9a" ) )
  1804. {
  1805. nNewChapter = 10;
  1806. }
  1807. else if ( nNewChapter > 9 )
  1808. {
  1809. nNewChapter++;
  1810. }
  1811. }
  1812. // ok we have the string, see if it's newer
  1813. const char *unlockedChapter = sv_unlockedchapters.GetString();
  1814. int nUnlockedChapter = atoi( unlockedChapter );
  1815. if ( nUnlockedChapter < nNewChapter )
  1816. {
  1817. // ok we're at a higher chapter, unlock
  1818. sv_unlockedchapters.SetValue( nNewChapter );
  1819. // HACK: Call up through a better function than this? 7/23/07 - jdw
  1820. if ( IsX360() )
  1821. {
  1822. engine->ServerCommand( "host_writeconfig\n" );
  1823. }
  1824. }
  1825. g_nCurrentChapterIndex = nNewChapter;
  1826. }
  1827. }
  1828. //-----------------------------------------------------------------------------
  1829. // Purpose: Update xbox live data for the user's presence
  1830. //-----------------------------------------------------------------------------
  1831. void UpdateRichPresence ( void )
  1832. {
  1833. // This assumes we're playing a single player game
  1834. Assert ( gpGlobals->maxClients == 1 );
  1835. // Shouldn't get here unless we're playing a map and we've updated sv_unlockedchapters
  1836. Assert ( g_nCurrentChapterIndex >= 0 );
  1837. // Get our active mod directory name
  1838. char modDir[MAX_PATH];
  1839. if ( UTIL_GetModDir( modDir, sizeof(modDir) ) == false )
  1840. return;
  1841. // Get presence data based on the game we're playing
  1842. uint iGameID, iChapterIndex, iChapterID, iGamePresenceID;
  1843. iGameID = iChapterIndex = iChapterID = iGamePresenceID = 0;
  1844. if ( Q_stristr( modDir, "hl2" ) )
  1845. {
  1846. iGameID = CONTEXT_GAME_GAME_HALF_LIFE_2;
  1847. iChapterID = CONTEXT_CHAPTER_HL2;
  1848. iChapterIndex = g_nCurrentChapterIndex - 1;
  1849. iGamePresenceID = CONTEXT_PRESENCE_HL2_INGAME;
  1850. }
  1851. else if ( Q_stristr( modDir, "episodic" ) )
  1852. {
  1853. iGameID = CONTEXT_GAME_GAME_EPISODE_ONE;
  1854. iChapterID = CONTEXT_CHAPTER_EP1;
  1855. iChapterIndex = g_nCurrentChapterIndex - 1;
  1856. iGamePresenceID = CONTEXT_PRESENCE_EP1_INGAME;
  1857. }
  1858. else if ( Q_stristr( modDir, "ep2" ) )
  1859. {
  1860. iGameID = CONTEXT_GAME_GAME_EPISODE_TWO;
  1861. iChapterID = CONTEXT_CHAPTER_EP2;
  1862. iChapterIndex = g_nCurrentChapterIndex - 1;
  1863. iGamePresenceID = CONTEXT_PRESENCE_EP2_INGAME;
  1864. }
  1865. else if ( Q_stristr( modDir, "portal" ) )
  1866. {
  1867. iGameID = CONTEXT_GAME_GAME_PORTAL;
  1868. iChapterID = CONTEXT_CHAPTER_PORTAL;
  1869. iChapterIndex = g_nCurrentChapterIndex - 1;
  1870. iGamePresenceID = CONTEXT_PRESENCE_PORTAL_INGAME;
  1871. }
  1872. else
  1873. {
  1874. Warning( "UpdateRichPresence failed in GameInterface. Didn't recognize -game parameter." );
  1875. }
  1876. #if defined( _X360 )
  1877. // Set chapter context based on mapname
  1878. if ( !xboxsystem->UserSetContext( XBX_GetPrimaryUserId(), iChapterID, iChapterIndex, true ) )
  1879. {
  1880. Warning( "GameInterface: UserSetContext failed.\n" );
  1881. }
  1882. if ( commentary.GetBool() )
  1883. {
  1884. // Set presence to show the user is playing developer commentary
  1885. if ( !xboxsystem->UserSetContext( XBX_GetPrimaryUserId(), X_CONTEXT_PRESENCE, CONTEXT_PRESENCE_COMMENTARY, true ) )
  1886. {
  1887. Warning( "GameInterface: UserSetContext failed.\n" );
  1888. }
  1889. }
  1890. else
  1891. {
  1892. // Set presence to show the user is in-game
  1893. if ( !xboxsystem->UserSetContext( XBX_GetPrimaryUserId(), X_CONTEXT_PRESENCE, iGamePresenceID, true ) )
  1894. {
  1895. Warning( "GameInterface: UserSetContext failed.\n" );
  1896. }
  1897. }
  1898. // Set which game the user is playing
  1899. if ( !xboxsystem->UserSetContext( XBX_GetPrimaryUserId(), CONTEXT_GAME, iGameID, true ) )
  1900. {
  1901. Warning( "GameInterface: UserSetContext failed.\n" );
  1902. }
  1903. if ( !xboxsystem->UserSetContext( XBX_GetPrimaryUserId(), X_CONTEXT_GAME_TYPE, X_CONTEXT_GAME_TYPE_STANDARD, true ) )
  1904. {
  1905. Warning( "GameInterface: UserSetContext failed.\n" );
  1906. }
  1907. if ( !xboxsystem->UserSetContext( XBX_GetPrimaryUserId(), X_CONTEXT_GAME_MODE, CONTEXT_GAME_MODE_SINGLEPLAYER, true ) )
  1908. {
  1909. Warning( "GameInterface: UserSetContext failed.\n" );
  1910. }
  1911. #endif
  1912. }
  1913. //-----------------------------------------------------------------------------
  1914. // Precaches a vgui screen overlay material
  1915. //-----------------------------------------------------------------------------
  1916. void PrecacheMaterial( const char *pMaterialName )
  1917. {
  1918. Assert( pMaterialName && pMaterialName[0] );
  1919. g_pStringTableMaterials->AddString( CBaseEntity::IsServer(), pMaterialName );
  1920. }
  1921. //-----------------------------------------------------------------------------
  1922. // Converts a previously precached material into an index
  1923. //-----------------------------------------------------------------------------
  1924. int GetMaterialIndex( const char *pMaterialName )
  1925. {
  1926. if (pMaterialName)
  1927. {
  1928. int nIndex = g_pStringTableMaterials->FindStringIndex( pMaterialName );
  1929. if (nIndex != INVALID_STRING_INDEX )
  1930. {
  1931. return nIndex;
  1932. }
  1933. else
  1934. {
  1935. DevMsg("Warning! GetMaterialIndex: couldn't find material %s\n ", pMaterialName );
  1936. return 0;
  1937. }
  1938. }
  1939. // This is the invalid string index
  1940. return 0;
  1941. }
  1942. //-----------------------------------------------------------------------------
  1943. // Converts a previously precached material index into a string
  1944. //-----------------------------------------------------------------------------
  1945. const char *GetMaterialNameFromIndex( int nMaterialIndex )
  1946. {
  1947. return g_pStringTableMaterials->GetString( nMaterialIndex );
  1948. }
  1949. //-----------------------------------------------------------------------------
  1950. // Precaches a vgui screen overlay material
  1951. //-----------------------------------------------------------------------------
  1952. void PrecacheParticleSystem( const char *pParticleSystemName )
  1953. {
  1954. Assert( pParticleSystemName && pParticleSystemName[0] );
  1955. g_pStringTableParticleEffectNames->AddString( CBaseEntity::IsServer(), pParticleSystemName );
  1956. }
  1957. //-----------------------------------------------------------------------------
  1958. // Converts a previously precached material into an index
  1959. //-----------------------------------------------------------------------------
  1960. int GetParticleSystemIndex( const char *pParticleSystemName )
  1961. {
  1962. if ( pParticleSystemName )
  1963. {
  1964. int nIndex = g_pStringTableParticleEffectNames->FindStringIndex( pParticleSystemName );
  1965. if (nIndex != INVALID_STRING_INDEX )
  1966. return nIndex;
  1967. DevWarning("Server: Missing precache for particle system \"%s\"!\n", pParticleSystemName );
  1968. }
  1969. // This is the invalid string index
  1970. return 0;
  1971. }
  1972. //-----------------------------------------------------------------------------
  1973. // Converts a previously precached material index into a string
  1974. //-----------------------------------------------------------------------------
  1975. const char *GetParticleSystemNameFromIndex( int nMaterialIndex )
  1976. {
  1977. if ( nMaterialIndex < g_pStringTableParticleEffectNames->GetMaxStrings() )
  1978. return g_pStringTableParticleEffectNames->GetString( nMaterialIndex );
  1979. return "error";
  1980. }
  1981. //-----------------------------------------------------------------------------
  1982. // Returns true if host_thread_mode is set to non-zero (and engine is running in threaded mode)
  1983. //-----------------------------------------------------------------------------
  1984. bool IsEngineThreaded()
  1985. {
  1986. if ( g_pcv_ThreadMode )
  1987. {
  1988. return g_pcv_ThreadMode->GetBool();
  1989. }
  1990. return false;
  1991. }
  1992. class CServerGameEnts : public IServerGameEnts
  1993. {
  1994. public:
  1995. virtual void SetDebugEdictBase(edict_t *base);
  1996. virtual void MarkEntitiesAsTouching( edict_t *e1, edict_t *e2 );
  1997. virtual void FreeContainingEntity( edict_t * );
  1998. virtual edict_t* BaseEntityToEdict( CBaseEntity *pEnt );
  1999. virtual CBaseEntity* EdictToBaseEntity( edict_t *pEdict );
  2000. virtual void CheckTransmit( CCheckTransmitInfo *pInfo, const unsigned short *pEdictIndices, int nEdicts );
  2001. };
  2002. EXPOSE_SINGLE_INTERFACE(CServerGameEnts, IServerGameEnts, INTERFACEVERSION_SERVERGAMEENTS);
  2003. void CServerGameEnts::SetDebugEdictBase(edict_t *base)
  2004. {
  2005. g_pDebugEdictBase = base;
  2006. }
  2007. //-----------------------------------------------------------------------------
  2008. // Purpose: Marks entities as touching
  2009. // Input : *e1 -
  2010. // *e2 -
  2011. //-----------------------------------------------------------------------------
  2012. void CServerGameEnts::MarkEntitiesAsTouching( edict_t *e1, edict_t *e2 )
  2013. {
  2014. CBaseEntity *entity = GetContainingEntity( e1 );
  2015. CBaseEntity *entityTouched = GetContainingEntity( e2 );
  2016. if ( entity && entityTouched )
  2017. {
  2018. // HACKHACK: UNDONE: Pass in the trace here??!?!?
  2019. trace_t tr;
  2020. UTIL_ClearTrace( tr );
  2021. tr.endpos = (entity->GetAbsOrigin() + entityTouched->GetAbsOrigin()) * 0.5;
  2022. entity->PhysicsMarkEntitiesAsTouching( entityTouched, tr );
  2023. }
  2024. }
  2025. void CServerGameEnts::FreeContainingEntity( edict_t *e )
  2026. {
  2027. ::FreeContainingEntity(e);
  2028. }
  2029. edict_t* CServerGameEnts::BaseEntityToEdict( CBaseEntity *pEnt )
  2030. {
  2031. if ( pEnt )
  2032. return pEnt->edict();
  2033. else
  2034. return NULL;
  2035. }
  2036. CBaseEntity* CServerGameEnts::EdictToBaseEntity( edict_t *pEdict )
  2037. {
  2038. if ( pEdict )
  2039. return CBaseEntity::Instance( pEdict );
  2040. else
  2041. return NULL;
  2042. }
  2043. /* Yuck.. ideally this would be in CServerNetworkProperty's header, but it requires CBaseEntity and
  2044. // inlining it gives a nice speedup.
  2045. inline void CServerNetworkProperty::CheckTransmit( CCheckTransmitInfo *pInfo )
  2046. {
  2047. // If we have a transmit proxy, let it hook our ShouldTransmit return value.
  2048. if ( m_pTransmitProxy )
  2049. {
  2050. nShouldTransmit = m_pTransmitProxy->ShouldTransmit( pInfo, nShouldTransmit );
  2051. }
  2052. if ( m_pOuter->ShouldTransmit( pInfo ) )
  2053. {
  2054. m_pOuter->SetTransmit( pInfo );
  2055. }
  2056. } */
  2057. void CServerGameEnts::CheckTransmit( CCheckTransmitInfo *pInfo, const unsigned short *pEdictIndices, int nEdicts )
  2058. {
  2059. // NOTE: for speed's sake, this assumes that all networkables are CBaseEntities and that the edict list
  2060. // is consecutive in memory. If either of these things change, then this routine needs to change, but
  2061. // ideally we won't be calling any virtual from this routine. This speedy routine was added as an
  2062. // optimization which would be nice to keep.
  2063. edict_t *pBaseEdict = engine->PEntityOfEntIndex( 0 );
  2064. // get recipient player's skybox:
  2065. CBaseEntity *pRecipientEntity = CBaseEntity::Instance( pInfo->m_pClientEnt );
  2066. Assert( pRecipientEntity && pRecipientEntity->IsPlayer() );
  2067. if ( !pRecipientEntity )
  2068. return;
  2069. MDLCACHE_CRITICAL_SECTION();
  2070. CBasePlayer *pRecipientPlayer = static_cast<CBasePlayer*>( pRecipientEntity );
  2071. const int skyBoxArea = pRecipientPlayer->m_Local.m_skybox3d.area;
  2072. #ifndef _X360
  2073. const bool bIsHLTV = pRecipientPlayer->IsHLTV();
  2074. const bool bIsReplay = pRecipientPlayer->IsReplay();
  2075. // m_pTransmitAlways must be set if HLTV client
  2076. Assert( bIsHLTV == ( pInfo->m_pTransmitAlways != NULL) ||
  2077. bIsReplay == ( pInfo->m_pTransmitAlways != NULL) );
  2078. #endif
  2079. for ( int i=0; i < nEdicts; i++ )
  2080. {
  2081. int iEdict = pEdictIndices[i];
  2082. edict_t *pEdict = &pBaseEdict[iEdict];
  2083. Assert( pEdict == engine->PEntityOfEntIndex( iEdict ) );
  2084. int nFlags = pEdict->m_fStateFlags & (FL_EDICT_DONTSEND|FL_EDICT_ALWAYS|FL_EDICT_PVSCHECK|FL_EDICT_FULLCHECK);
  2085. // entity needs no transmit
  2086. if ( nFlags & FL_EDICT_DONTSEND )
  2087. continue;
  2088. // entity is already marked for sending
  2089. if ( pInfo->m_pTransmitEdict->Get( iEdict ) )
  2090. continue;
  2091. if ( nFlags & FL_EDICT_ALWAYS )
  2092. {
  2093. // FIXME: Hey! Shouldn't this be using SetTransmit so as
  2094. // to also force network down dependent entities?
  2095. while ( true )
  2096. {
  2097. // mark entity for sending
  2098. pInfo->m_pTransmitEdict->Set( iEdict );
  2099. #ifndef _X360
  2100. if ( bIsHLTV || bIsReplay )
  2101. {
  2102. pInfo->m_pTransmitAlways->Set( iEdict );
  2103. }
  2104. #endif
  2105. CServerNetworkProperty *pEnt = static_cast<CServerNetworkProperty*>( pEdict->GetNetworkable() );
  2106. if ( !pEnt )
  2107. break;
  2108. CServerNetworkProperty *pParent = pEnt->GetNetworkParent();
  2109. if ( !pParent )
  2110. break;
  2111. pEdict = pParent->edict();
  2112. iEdict = pParent->entindex();
  2113. }
  2114. continue;
  2115. }
  2116. // FIXME: Would like to remove all dependencies
  2117. CBaseEntity *pEnt = ( CBaseEntity * )pEdict->GetUnknown();
  2118. Assert( dynamic_cast< CBaseEntity* >( pEdict->GetUnknown() ) == pEnt );
  2119. if ( nFlags == FL_EDICT_FULLCHECK )
  2120. {
  2121. // do a full ShouldTransmit() check, may return FL_EDICT_CHECKPVS
  2122. nFlags = pEnt->ShouldTransmit( pInfo );
  2123. Assert( !(nFlags & FL_EDICT_FULLCHECK) );
  2124. if ( nFlags & FL_EDICT_ALWAYS )
  2125. {
  2126. pEnt->SetTransmit( pInfo, true );
  2127. continue;
  2128. }
  2129. }
  2130. // don't send this entity
  2131. if ( !( nFlags & FL_EDICT_PVSCHECK ) )
  2132. continue;
  2133. CServerNetworkProperty *netProp = static_cast<CServerNetworkProperty*>( pEdict->GetNetworkable() );
  2134. #ifndef _X360
  2135. if ( bIsHLTV || bIsReplay )
  2136. {
  2137. // for the HLTV/Replay we don't cull against PVS
  2138. if ( netProp->AreaNum() == skyBoxArea )
  2139. {
  2140. pEnt->SetTransmit( pInfo, true );
  2141. }
  2142. else
  2143. {
  2144. pEnt->SetTransmit( pInfo, false );
  2145. }
  2146. continue;
  2147. }
  2148. #endif
  2149. // Always send entities in the player's 3d skybox.
  2150. // Sidenote: call of AreaNum() ensures that PVS data is up to date for this entity
  2151. bool bSameAreaAsSky = netProp->AreaNum() == skyBoxArea;
  2152. if ( bSameAreaAsSky )
  2153. {
  2154. pEnt->SetTransmit( pInfo, true );
  2155. continue;
  2156. }
  2157. bool bInPVS = netProp->IsInPVS( pInfo );
  2158. if ( bInPVS || sv_force_transmit_ents.GetBool() )
  2159. {
  2160. // only send if entity is in PVS
  2161. pEnt->SetTransmit( pInfo, false );
  2162. continue;
  2163. }
  2164. // If the entity is marked "check PVS" but it's in hierarchy, walk up the hierarchy looking for the
  2165. // for any parent which is also in the PVS. If none are found, then we don't need to worry about sending ourself
  2166. CBaseEntity *orig = pEnt;
  2167. CServerNetworkProperty *check = netProp->GetNetworkParent();
  2168. // BUG BUG: I think it might be better to build up a list of edict indices which "depend" on other answers and then
  2169. // resolve them in a second pass. Not sure what happens if an entity has two parents who both request PVS check?
  2170. while ( check )
  2171. {
  2172. int checkIndex = check->entindex();
  2173. // Parent already being sent
  2174. if ( pInfo->m_pTransmitEdict->Get( checkIndex ) )
  2175. {
  2176. orig->SetTransmit( pInfo, true );
  2177. break;
  2178. }
  2179. edict_t *checkEdict = check->edict();
  2180. int checkFlags = checkEdict->m_fStateFlags & (FL_EDICT_DONTSEND|FL_EDICT_ALWAYS|FL_EDICT_PVSCHECK|FL_EDICT_FULLCHECK);
  2181. if ( checkFlags & FL_EDICT_DONTSEND )
  2182. break;
  2183. if ( checkFlags & FL_EDICT_ALWAYS )
  2184. {
  2185. orig->SetTransmit( pInfo, true );
  2186. break;
  2187. }
  2188. if ( checkFlags == FL_EDICT_FULLCHECK )
  2189. {
  2190. // do a full ShouldTransmit() check, may return FL_EDICT_CHECKPVS
  2191. CBaseEntity *pCheckEntity = check->GetBaseEntity();
  2192. nFlags = pCheckEntity->ShouldTransmit( pInfo );
  2193. Assert( !(nFlags & FL_EDICT_FULLCHECK) );
  2194. if ( nFlags & FL_EDICT_ALWAYS )
  2195. {
  2196. pCheckEntity->SetTransmit( pInfo, true );
  2197. orig->SetTransmit( pInfo, true );
  2198. }
  2199. break;
  2200. }
  2201. if ( checkFlags & FL_EDICT_PVSCHECK )
  2202. {
  2203. // Check pvs
  2204. check->RecomputePVSInformation();
  2205. bool bMoveParentInPVS = check->IsInPVS( pInfo );
  2206. if ( bMoveParentInPVS )
  2207. {
  2208. orig->SetTransmit( pInfo, true );
  2209. break;
  2210. }
  2211. }
  2212. // Continue up chain just in case the parent itself has a parent that's in the PVS...
  2213. check = check->GetNetworkParent();
  2214. }
  2215. }
  2216. // Msg("A:%i, N:%i, F: %i, P: %i\n", always, dontSend, fullCheck, PVS );
  2217. }
  2218. CServerGameClients g_ServerGameClients;
  2219. // INTERFACEVERSION_SERVERGAMECLIENTS_VERSION_3 is compatible with the latest since we're only adding things to the end, so expose that as well.
  2220. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerGameClients, IServerGameClients003, INTERFACEVERSION_SERVERGAMECLIENTS_VERSION_3, g_ServerGameClients );
  2221. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerGameClients, IServerGameClients, INTERFACEVERSION_SERVERGAMECLIENTS, g_ServerGameClients );
  2222. //-----------------------------------------------------------------------------
  2223. // Purpose: called when a player tries to connect to the server
  2224. // Input : *pEdict - the new player
  2225. // char *pszName - the players name
  2226. // char *pszAddress - the IP address of the player
  2227. // reject - output - fill in with the reason why
  2228. // maxrejectlen -- sizeof output buffer
  2229. // the player was not allowed to connect.
  2230. // Output : Returns TRUE if player is allowed to join, FALSE if connection is denied.
  2231. //-----------------------------------------------------------------------------
  2232. bool CServerGameClients::ClientConnect( edict_t *pEdict, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen )
  2233. {
  2234. if ( !g_pGameRules )
  2235. return false;
  2236. return g_pGameRules->ClientConnected( pEdict, pszName, pszAddress, reject, maxrejectlen );
  2237. }
  2238. //-----------------------------------------------------------------------------
  2239. // Purpose: Called when a player is fully active (i.e. ready to receive messages)
  2240. // Input : *pEntity - the player
  2241. //-----------------------------------------------------------------------------
  2242. void CServerGameClients::ClientActive( edict_t *pEdict, bool bLoadGame )
  2243. {
  2244. MDLCACHE_CRITICAL_SECTION();
  2245. ::ClientActive( pEdict, bLoadGame );
  2246. // If we just loaded from a save file, call OnRestore on valid entities
  2247. EndRestoreEntities();
  2248. if ( gpGlobals->eLoadType != MapLoad_LoadGame )
  2249. {
  2250. // notify all entities that the player is now in the game
  2251. for ( CBaseEntity *pEntity = gEntList.FirstEnt(); pEntity != NULL; pEntity = gEntList.NextEnt(pEntity) )
  2252. {
  2253. pEntity->PostClientActive();
  2254. }
  2255. }
  2256. // Tell the sound controller to check looping sounds
  2257. CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
  2258. CSoundEnvelopeController::GetController().CheckLoopingSoundsForPlayer( pPlayer );
  2259. SceneManager_ClientActive( pPlayer );
  2260. #if defined( TF_DLL )
  2261. Assert( pPlayer );
  2262. if ( pPlayer && !pPlayer->IsFakeClient() && !pPlayer->IsHLTV() && !pPlayer->IsReplay() )
  2263. {
  2264. CSteamID steamID;
  2265. if ( pPlayer->GetSteamID( &steamID ) )
  2266. {
  2267. GTFGCClientSystem()->ClientActive( steamID );
  2268. }
  2269. else
  2270. {
  2271. if ( !pPlayer->IsReplay() && !pPlayer->IsHLTV() )
  2272. {
  2273. Log("WARNING: ClientActive, but we don't know his SteamID?\n");
  2274. }
  2275. }
  2276. }
  2277. #endif
  2278. }
  2279. //-----------------------------------------------------------------------------
  2280. // Purpose:
  2281. // Input : *pPlayer - the player
  2282. //-----------------------------------------------------------------------------
  2283. void CServerGameClients::ClientSpawned( edict_t *pPlayer )
  2284. {
  2285. if ( g_pGameRules )
  2286. {
  2287. g_pGameRules->ClientSpawned( pPlayer );
  2288. }
  2289. }
  2290. //-----------------------------------------------------------------------------
  2291. // Purpose: called when a player disconnects from a server
  2292. // Input : *pEdict - the player
  2293. //-----------------------------------------------------------------------------
  2294. void CServerGameClients::ClientDisconnect( edict_t *pEdict )
  2295. {
  2296. extern bool g_fGameOver;
  2297. CBasePlayer *player = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
  2298. if ( player )
  2299. {
  2300. if ( !g_fGameOver )
  2301. {
  2302. player->SetMaxSpeed( 0.0f );
  2303. CSound *pSound;
  2304. pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt::ClientSoundIndex( pEdict ) );
  2305. {
  2306. // since this client isn't around to think anymore, reset their sound.
  2307. if ( pSound )
  2308. {
  2309. pSound->Reset();
  2310. }
  2311. }
  2312. // since the edict doesn't get deleted, fix it so it doesn't interfere.
  2313. player->RemoveFlag( FL_AIMTARGET ); // don't attract autoaim
  2314. player->AddFlag( FL_DONTTOUCH ); // stop it touching anything
  2315. player->AddFlag( FL_NOTARGET ); // stop NPCs noticing it
  2316. player->AddSolidFlags( FSOLID_NOT_SOLID ); // nonsolid
  2317. if ( g_pGameRules )
  2318. {
  2319. g_pGameRules->ClientDisconnected( pEdict );
  2320. gamestats->Event_PlayerDisconnected( player );
  2321. }
  2322. }
  2323. // Make sure all Untouch()'s are called for this client leaving
  2324. CBaseEntity::PhysicsRemoveTouchedList( player );
  2325. CBaseEntity::PhysicsRemoveGroundList( player );
  2326. #if !defined( NO_ENTITY_PREDICTION )
  2327. // Make sure anything we "own" is simulated by the server from now on
  2328. player->ClearPlayerSimulationList();
  2329. #endif
  2330. #if defined( TF_DLL )
  2331. if ( !player->IsFakeClient() )
  2332. {
  2333. CSteamID steamID;
  2334. if ( player->GetSteamID( &steamID ) )
  2335. {
  2336. GTFGCClientSystem()->ClientDisconnected( steamID );
  2337. }
  2338. else
  2339. {
  2340. if ( !player->IsReplay() && !player->IsHLTV() )
  2341. {
  2342. Log("WARNING: ClientDisconnected, but we don't know his SteamID?\n");
  2343. }
  2344. }
  2345. }
  2346. #endif
  2347. }
  2348. }
  2349. void CServerGameClients::ClientPutInServer( edict_t *pEntity, const char *playername )
  2350. {
  2351. if ( g_pClientPutInServerOverride )
  2352. g_pClientPutInServerOverride( pEntity, playername );
  2353. else
  2354. ::ClientPutInServer( pEntity, playername );
  2355. }
  2356. void CServerGameClients::ClientCommand( edict_t *pEntity, const CCommand &args )
  2357. {
  2358. CBasePlayer *pPlayer = ToBasePlayer( GetContainingEntity( pEntity ) );
  2359. ::ClientCommand( pPlayer, args );
  2360. }
  2361. //-----------------------------------------------------------------------------
  2362. // Purpose: called after the player changes userinfo - gives dll a chance to modify
  2363. // it before it gets sent into the rest of the engine->
  2364. // Input : *pEdict - the player
  2365. // *infobuffer - their infobuffer
  2366. //-----------------------------------------------------------------------------
  2367. void CServerGameClients::ClientSettingsChanged( edict_t *pEdict )
  2368. {
  2369. // Is the client spawned yet?
  2370. if ( !pEdict->GetUnknown() )
  2371. return;
  2372. CBasePlayer *player = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
  2373. if ( !player )
  2374. return;
  2375. bool bAllowNetworkingClientSettingsChange = g_pGameRules->IsConnectedUserInfoChangeAllowed( player );
  2376. if ( bAllowNetworkingClientSettingsChange )
  2377. {
  2378. #define QUICKGETCVARVALUE(v) (engine->GetClientConVarValue( player->entindex(), v ))
  2379. // get network setting for prediction & lag compensation
  2380. // Unfortunately, we have to duplicate the code in cdll_bounded_cvars.cpp here because the client
  2381. // doesn't send the virtualized value up (because it has no way to know when the virtualized value
  2382. // changes). Possible todo: put the responsibility on the bounded cvar to notify the engine when
  2383. // its virtualized value has changed.
  2384. player->m_nUpdateRate = Q_atoi( QUICKGETCVARVALUE("cl_updaterate") );
  2385. static const ConVar *pMinUpdateRate = g_pCVar->FindVar( "sv_minupdaterate" );
  2386. static const ConVar *pMaxUpdateRate = g_pCVar->FindVar( "sv_maxupdaterate" );
  2387. if ( pMinUpdateRate && pMaxUpdateRate )
  2388. player->m_nUpdateRate = clamp( player->m_nUpdateRate, (int) pMinUpdateRate->GetFloat(), (int) pMaxUpdateRate->GetFloat() );
  2389. bool useInterpolation = Q_atoi( QUICKGETCVARVALUE("cl_interpolate") ) != 0;
  2390. if ( useInterpolation )
  2391. {
  2392. float flLerpRatio = Q_atof( QUICKGETCVARVALUE("cl_interp_ratio") );
  2393. if ( flLerpRatio == 0 )
  2394. flLerpRatio = 1.0f;
  2395. float flLerpAmount = Q_atof( QUICKGETCVARVALUE("cl_interp") );
  2396. static const ConVar *pMin = g_pCVar->FindVar( "sv_client_min_interp_ratio" );
  2397. static const ConVar *pMax = g_pCVar->FindVar( "sv_client_max_interp_ratio" );
  2398. if ( pMin && pMax && pMin->GetFloat() != -1 )
  2399. {
  2400. flLerpRatio = clamp( flLerpRatio, pMin->GetFloat(), pMax->GetFloat() );
  2401. }
  2402. else
  2403. {
  2404. if ( flLerpRatio == 0 )
  2405. flLerpRatio = 1.0f;
  2406. }
  2407. // #define FIXME_INTERP_RATIO
  2408. player->m_fLerpTime = MAX( flLerpAmount, flLerpRatio / player->m_nUpdateRate );
  2409. }
  2410. else
  2411. {
  2412. player->m_fLerpTime = 0.0f;
  2413. }
  2414. #if !defined( NO_ENTITY_PREDICTION )
  2415. bool usePrediction = Q_atoi( QUICKGETCVARVALUE("cl_predict")) != 0;
  2416. if ( usePrediction )
  2417. {
  2418. player->m_bPredictWeapons = Q_atoi( QUICKGETCVARVALUE("cl_predictweapons")) != 0;
  2419. player->m_bLagCompensation = Q_atoi( QUICKGETCVARVALUE("cl_lagcompensation")) != 0;
  2420. }
  2421. else
  2422. #endif
  2423. {
  2424. player->m_bPredictWeapons = false;
  2425. player->m_bLagCompensation = false;
  2426. }
  2427. #undef QUICKGETCVARVALUE
  2428. }
  2429. g_pGameRules->ClientSettingsChanged( player );
  2430. }
  2431. #ifdef PORTAL
  2432. //-----------------------------------------------------------------------------
  2433. // Purpose: Runs CFuncAreaPortalBase::UpdateVisibility on each portal
  2434. // Input : pAreaPortal - The Area portal to test for visibility from portals
  2435. // Output : int - 1 if any portal needs this area portal open, 0 otherwise.
  2436. //-----------------------------------------------------------------------------
  2437. int TestAreaPortalVisibilityThroughPortals ( CFuncAreaPortalBase* pAreaPortal, edict_t *pViewEntity, unsigned char *pvs, int pvssize )
  2438. {
  2439. int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
  2440. if( iPortalCount == 0 )
  2441. return 0;
  2442. CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base();
  2443. for ( int i = 0; i != iPortalCount; ++i )
  2444. {
  2445. CProp_Portal* pLocalPortal = pPortals[ i ];
  2446. if ( pLocalPortal && pLocalPortal->m_bActivated )
  2447. {
  2448. CProp_Portal* pRemotePortal = pLocalPortal->m_hLinkedPortal.Get();
  2449. // Make sure this portal's linked portal is in the PVS before we add what it can see
  2450. if ( pRemotePortal && pRemotePortal->m_bActivated && pRemotePortal->NetworkProp() &&
  2451. pRemotePortal->NetworkProp()->IsInPVS( pViewEntity, pvs, pvssize ) )
  2452. {
  2453. bool bIsOpenOnClient = true;
  2454. float fovDistanceAdjustFactor = 1.0f;
  2455. Vector portalOrg = pLocalPortal->GetAbsOrigin();
  2456. int iPortalNeedsThisPortalOpen = pAreaPortal->UpdateVisibility( portalOrg, fovDistanceAdjustFactor, bIsOpenOnClient );
  2457. // Stop checking on success, this portal needs to be open
  2458. if ( iPortalNeedsThisPortalOpen )
  2459. {
  2460. return iPortalNeedsThisPortalOpen;
  2461. }
  2462. }
  2463. }
  2464. }
  2465. return 0;
  2466. }
  2467. #endif
  2468. //-----------------------------------------------------------------------------
  2469. // Purpose: A client can have a separate "view entity" indicating that his/her view should depend on the origin of that
  2470. // view entity. If that's the case, then pViewEntity will be non-NULL and will be used. Otherwise, the current
  2471. // entity's origin is used. Either is offset by the m_vecViewOffset to get the eye position.
  2472. // From the eye position, we set up the PAS and PVS to use for filtering network messages to the client. At this point, we could
  2473. // override the actual PAS or PVS values, or use a different origin.
  2474. // NOTE: Do not cache the values of pas and pvs, as they depend on reusable memory in the engine, they are only good for this one frame
  2475. // Input : *pViewEntity -
  2476. // *pClient -
  2477. // **pvs -
  2478. // **pas -
  2479. //-----------------------------------------------------------------------------
  2480. void CServerGameClients::ClientSetupVisibility( edict_t *pViewEntity, edict_t *pClient, unsigned char *pvs, int pvssize )
  2481. {
  2482. Vector org;
  2483. // Reset the PVS!!!
  2484. engine->ResetPVS( pvs, pvssize );
  2485. g_pToolFrameworkServer->PreSetupVisibility();
  2486. // Find the client's PVS
  2487. CBaseEntity *pVE = NULL;
  2488. if ( pViewEntity )
  2489. {
  2490. pVE = GetContainingEntity( pViewEntity );
  2491. // If we have a viewentity, it overrides the player's origin
  2492. if ( pVE )
  2493. {
  2494. org = pVE->EyePosition();
  2495. engine->AddOriginToPVS( org );
  2496. }
  2497. }
  2498. float fovDistanceAdjustFactor = 1;
  2499. CBasePlayer *pPlayer = ( CBasePlayer * )GetContainingEntity( pClient );
  2500. if ( pPlayer )
  2501. {
  2502. org = pPlayer->EyePosition();
  2503. pPlayer->SetupVisibility( pVE, pvs, pvssize );
  2504. UTIL_SetClientVisibilityPVS( pClient, pvs, pvssize );
  2505. fovDistanceAdjustFactor = pPlayer->GetFOVDistanceAdjustFactorForNetworking();
  2506. }
  2507. unsigned char portalBits[MAX_AREA_PORTAL_STATE_BYTES];
  2508. memset( portalBits, 0, sizeof( portalBits ) );
  2509. int portalNums[512];
  2510. int isOpen[512];
  2511. int iOutPortal = 0;
  2512. for( unsigned short i = g_AreaPortals.Head(); i != g_AreaPortals.InvalidIndex(); i = g_AreaPortals.Next(i) )
  2513. {
  2514. CFuncAreaPortalBase *pCur = g_AreaPortals[i];
  2515. bool bIsOpenOnClient = true;
  2516. // Update our array of which portals are open and flush it if necessary.
  2517. portalNums[iOutPortal] = pCur->m_portalNumber;
  2518. isOpen[iOutPortal] = pCur->UpdateVisibility( org, fovDistanceAdjustFactor, bIsOpenOnClient );
  2519. #ifdef PORTAL
  2520. // If the client doesn't need this open, test if portals might need this area portal open
  2521. if ( isOpen[iOutPortal] == 0 )
  2522. {
  2523. isOpen[iOutPortal] = TestAreaPortalVisibilityThroughPortals( pCur, pViewEntity, pvs, pvssize );
  2524. }
  2525. #endif
  2526. ++iOutPortal;
  2527. if ( iOutPortal >= ARRAYSIZE( portalNums ) )
  2528. {
  2529. engine->SetAreaPortalStates( portalNums, isOpen, iOutPortal );
  2530. iOutPortal = 0;
  2531. }
  2532. // Version 0 portals (ie: shipping Half-Life 2 era) are always treated as open
  2533. // for purposes of the m_chAreaPortalBits array on the client.
  2534. if ( pCur->m_iPortalVersion == 0 )
  2535. bIsOpenOnClient = true;
  2536. if ( bIsOpenOnClient )
  2537. {
  2538. if ( pCur->m_portalNumber < 0 )
  2539. continue;
  2540. else if ( pCur->m_portalNumber >= sizeof( portalBits ) * 8 )
  2541. Error( "ClientSetupVisibility: portal number (%d) too large", pCur->m_portalNumber );
  2542. else
  2543. portalBits[pCur->m_portalNumber >> 3] |= (1 << (pCur->m_portalNumber & 7));
  2544. }
  2545. }
  2546. // Flush the remaining areaportal states.
  2547. engine->SetAreaPortalStates( portalNums, isOpen, iOutPortal );
  2548. if ( pPlayer )
  2549. {
  2550. // Update the area bits that get sent to the client.
  2551. pPlayer->m_Local.UpdateAreaBits( pPlayer, portalBits );
  2552. #ifdef PORTAL
  2553. // *After* the player's view has updated its area bits, add on any other areas seen by portals
  2554. CPortal_Player* pPortalPlayer = dynamic_cast<CPortal_Player*>( pPlayer );
  2555. if ( pPortalPlayer )
  2556. {
  2557. pPortalPlayer->UpdatePortalViewAreaBits( pvs, pvssize );
  2558. }
  2559. #endif //PORTAL
  2560. }
  2561. }
  2562. //-----------------------------------------------------------------------------
  2563. // Purpose:
  2564. // Input : *player -
  2565. // *buf -
  2566. // numcmds -
  2567. // totalcmds -
  2568. // dropped_packets -
  2569. // ignore -
  2570. // paused -
  2571. // Output : float
  2572. //-----------------------------------------------------------------------------
  2573. #define CMD_MAXBACKUP 64
  2574. float CServerGameClients::ProcessUsercmds( edict_t *player, bf_read *buf, int numcmds, int totalcmds,
  2575. int dropped_packets, bool ignore, bool paused )
  2576. {
  2577. int i;
  2578. CUserCmd *from, *to;
  2579. // We track last three command in case we drop some
  2580. // packets but get them back.
  2581. CUserCmd cmds[ CMD_MAXBACKUP ];
  2582. CUserCmd cmdNull; // For delta compression
  2583. Assert( numcmds >= 0 );
  2584. Assert( ( totalcmds - numcmds ) >= 0 );
  2585. CBasePlayer *pPlayer = NULL;
  2586. CBaseEntity *pEnt = CBaseEntity::Instance(player);
  2587. if ( pEnt && pEnt->IsPlayer() )
  2588. {
  2589. pPlayer = static_cast< CBasePlayer * >( pEnt );
  2590. }
  2591. // Too many commands?
  2592. if ( totalcmds < 0 || totalcmds >= ( CMD_MAXBACKUP - 1 ) )
  2593. {
  2594. const char *name = "unknown";
  2595. if ( pPlayer )
  2596. {
  2597. name = pPlayer->GetPlayerName();
  2598. }
  2599. Msg("CBasePlayer::ProcessUsercmds: too many cmds %i sent for player %s\n", totalcmds, name );
  2600. // FIXME: Need a way to drop the client from here
  2601. //SV_DropClient ( host_client, false, "CMD_MAXBACKUP hit" );
  2602. buf->SetOverflowFlag();
  2603. return 0.0f;
  2604. }
  2605. // Initialize for reading delta compressed usercmds
  2606. cmdNull.Reset();
  2607. from = &cmdNull;
  2608. for ( i = totalcmds - 1; i >= 0; i-- )
  2609. {
  2610. to = &cmds[ i ];
  2611. ReadUsercmd( buf, to, from );
  2612. from = to;
  2613. }
  2614. // Client not fully connected or server has gone inactive or is paused, just ignore
  2615. if ( ignore || !pPlayer )
  2616. {
  2617. return 0.0f;
  2618. }
  2619. MDLCACHE_CRITICAL_SECTION();
  2620. pPlayer->ProcessUsercmds( cmds, numcmds, totalcmds, dropped_packets, paused );
  2621. return TICK_INTERVAL;
  2622. }
  2623. void CServerGameClients::PostClientMessagesSent_DEPRECIATED( void )
  2624. {
  2625. }
  2626. // Sets the client index for the client who typed the command into his/her console
  2627. void CServerGameClients::SetCommandClient( int index )
  2628. {
  2629. g_nCommandClientIndex = index;
  2630. }
  2631. int CServerGameClients::GetReplayDelay( edict_t *pEdict, int &entity )
  2632. {
  2633. CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
  2634. if ( !pPlayer )
  2635. return 0;
  2636. entity = pPlayer->GetReplayEntity();
  2637. return pPlayer->GetDelayTicks();
  2638. }
  2639. //-----------------------------------------------------------------------------
  2640. // The client's userinfo data lump has changed
  2641. //-----------------------------------------------------------------------------
  2642. void CServerGameClients::ClientEarPosition( edict_t *pEdict, Vector *pEarOrigin )
  2643. {
  2644. CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
  2645. if (pPlayer)
  2646. {
  2647. *pEarOrigin = pPlayer->EarPosition();
  2648. }
  2649. else
  2650. {
  2651. // Shouldn't happen
  2652. Assert(0);
  2653. *pEarOrigin = vec3_origin;
  2654. }
  2655. }
  2656. //-----------------------------------------------------------------------------
  2657. // Purpose:
  2658. // Input : *player -
  2659. // Output : CPlayerState
  2660. //-----------------------------------------------------------------------------
  2661. CPlayerState *CServerGameClients::GetPlayerState( edict_t *player )
  2662. {
  2663. // Is the client spawned yet?
  2664. if ( !player || !player->GetUnknown() )
  2665. return NULL;
  2666. CBasePlayer *pBasePlayer = ( CBasePlayer * )CBaseEntity::Instance( player );
  2667. if ( !pBasePlayer )
  2668. return NULL;
  2669. return &pBasePlayer->pl;
  2670. }
  2671. //-----------------------------------------------------------------------------
  2672. // Purpose: Anything this game .dll wants to add to the bug reporter text (e.g., the entity/model under the picker crosshair)
  2673. // can be added here
  2674. // Input : *buf -
  2675. // buflen -
  2676. //-----------------------------------------------------------------------------
  2677. void CServerGameClients::GetBugReportInfo( char *buf, int buflen )
  2678. {
  2679. recentNPCSpeech_t speech[ SPEECH_LIST_MAX_SOUNDS ];
  2680. int num;
  2681. int i;
  2682. buf[ 0 ] = 0;
  2683. if ( gpGlobals->maxClients == 1 )
  2684. {
  2685. CBaseEntity *ent = FindPickerEntity( UTIL_PlayerByIndex(1) );
  2686. if ( ent )
  2687. {
  2688. Q_snprintf( buf, buflen, "Picker %i/%s - ent %s model %s\n",
  2689. ent->entindex(),
  2690. ent->GetClassname(),
  2691. STRING( ent->GetEntityName() ),
  2692. STRING( ent->GetModelName() ) );
  2693. }
  2694. // get any sounds that were spoken by NPCs recently
  2695. num = GetRecentNPCSpeech( speech );
  2696. if ( num > 0 )
  2697. {
  2698. Q_snprintf( buf, buflen, "%sRecent NPC speech:\n", buf );
  2699. for( i = 0; i < num; i++ )
  2700. {
  2701. Q_snprintf( buf, buflen, "%s time: %6.3f sound name: %s scene: %s\n", buf, speech[ i ].time, speech[ i ].name, speech[ i ].sceneName );
  2702. }
  2703. Q_snprintf( buf, buflen, "%sCurrent time: %6.3f\n", buf, gpGlobals->curtime );
  2704. }
  2705. }
  2706. }
  2707. //-----------------------------------------------------------------------------
  2708. // Purpose: A user has had their network id setup and validated
  2709. //-----------------------------------------------------------------------------
  2710. void CServerGameClients::NetworkIDValidated( const char *pszUserName, const char *pszNetworkID )
  2711. {
  2712. }
  2713. // The client has submitted a keyvalues command
  2714. void CServerGameClients::ClientCommandKeyValues( edict_t *pEntity, KeyValues *pKeyValues )
  2715. {
  2716. if ( !pKeyValues )
  2717. return;
  2718. if ( g_pGameRules )
  2719. {
  2720. g_pGameRules->ClientCommandKeyValues( pEntity, pKeyValues );
  2721. }
  2722. }
  2723. //-----------------------------------------------------------------------------
  2724. // Purpose:
  2725. //-----------------------------------------------------------------------------
  2726. static bf_write *g_pMsgBuffer = NULL;
  2727. void EntityMessageBegin( CBaseEntity * entity, bool reliable /*= false*/ )
  2728. {
  2729. Assert( !g_pMsgBuffer );
  2730. Assert ( entity );
  2731. g_pMsgBuffer = engine->EntityMessageBegin( entity->entindex(), entity->GetServerClass(), reliable );
  2732. }
  2733. void UserMessageBegin( IRecipientFilter& filter, const char *messagename )
  2734. {
  2735. Assert( !g_pMsgBuffer );
  2736. Assert( messagename );
  2737. int msg_type = usermessages->LookupUserMessage( messagename );
  2738. if ( msg_type == -1 )
  2739. {
  2740. Error( "UserMessageBegin: Unregistered message '%s'\n", messagename );
  2741. }
  2742. g_pMsgBuffer = engine->UserMessageBegin( &filter, msg_type );
  2743. }
  2744. void MessageEnd( void )
  2745. {
  2746. Assert( g_pMsgBuffer );
  2747. engine->MessageEnd();
  2748. g_pMsgBuffer = NULL;
  2749. }
  2750. void MessageWriteByte( int iValue)
  2751. {
  2752. if (!g_pMsgBuffer)
  2753. Error( "WRITE_BYTE called with no active message\n" );
  2754. g_pMsgBuffer->WriteByte( iValue );
  2755. }
  2756. void MessageWriteChar( int iValue)
  2757. {
  2758. if (!g_pMsgBuffer)
  2759. Error( "WRITE_CHAR called with no active message\n" );
  2760. g_pMsgBuffer->WriteChar( iValue );
  2761. }
  2762. void MessageWriteShort( int iValue)
  2763. {
  2764. if (!g_pMsgBuffer)
  2765. Error( "WRITE_SHORT called with no active message\n" );
  2766. g_pMsgBuffer->WriteShort( iValue );
  2767. }
  2768. void MessageWriteWord( int iValue )
  2769. {
  2770. if (!g_pMsgBuffer)
  2771. Error( "WRITE_WORD called with no active message\n" );
  2772. g_pMsgBuffer->WriteWord( iValue );
  2773. }
  2774. void MessageWriteLong( int iValue)
  2775. {
  2776. if (!g_pMsgBuffer)
  2777. Error( "WriteLong called with no active message\n" );
  2778. g_pMsgBuffer->WriteLong( iValue );
  2779. }
  2780. void MessageWriteFloat( float flValue)
  2781. {
  2782. if (!g_pMsgBuffer)
  2783. Error( "WriteFloat called with no active message\n" );
  2784. g_pMsgBuffer->WriteFloat( flValue );
  2785. }
  2786. void MessageWriteAngle( float flValue)
  2787. {
  2788. if (!g_pMsgBuffer)
  2789. Error( "WriteAngle called with no active message\n" );
  2790. g_pMsgBuffer->WriteBitAngle( flValue, 8 );
  2791. }
  2792. void MessageWriteCoord( float flValue)
  2793. {
  2794. if (!g_pMsgBuffer)
  2795. Error( "WriteCoord called with no active message\n" );
  2796. g_pMsgBuffer->WriteBitCoord( flValue );
  2797. }
  2798. void MessageWriteVec3Coord( const Vector& rgflValue)
  2799. {
  2800. if (!g_pMsgBuffer)
  2801. Error( "WriteVec3Coord called with no active message\n" );
  2802. g_pMsgBuffer->WriteBitVec3Coord( rgflValue );
  2803. }
  2804. void MessageWriteVec3Normal( const Vector& rgflValue)
  2805. {
  2806. if (!g_pMsgBuffer)
  2807. Error( "WriteVec3Normal called with no active message\n" );
  2808. g_pMsgBuffer->WriteBitVec3Normal( rgflValue );
  2809. }
  2810. void MessageWriteAngles( const QAngle& rgflValue)
  2811. {
  2812. if (!g_pMsgBuffer)
  2813. Error( "WriteVec3Normal called with no active message\n" );
  2814. g_pMsgBuffer->WriteBitAngles( rgflValue );
  2815. }
  2816. void MessageWriteString( const char *sz )
  2817. {
  2818. if (!g_pMsgBuffer)
  2819. Error( "WriteString called with no active message\n" );
  2820. g_pMsgBuffer->WriteString( sz );
  2821. }
  2822. void MessageWriteEntity( int iValue)
  2823. {
  2824. if (!g_pMsgBuffer)
  2825. Error( "WriteEntity called with no active message\n" );
  2826. g_pMsgBuffer->WriteShort( iValue );
  2827. }
  2828. void MessageWriteEHandle( CBaseEntity *pEntity )
  2829. {
  2830. if (!g_pMsgBuffer)
  2831. Error( "WriteEHandle called with no active message\n" );
  2832. long iEncodedEHandle;
  2833. if( pEntity )
  2834. {
  2835. EHANDLE hEnt = pEntity;
  2836. int iSerialNum = hEnt.GetSerialNumber() & ( (1 << NUM_NETWORKED_EHANDLE_SERIAL_NUMBER_BITS) - 1 );
  2837. iEncodedEHandle = hEnt.GetEntryIndex() | (iSerialNum << MAX_EDICT_BITS);
  2838. }
  2839. else
  2840. {
  2841. iEncodedEHandle = INVALID_NETWORKED_EHANDLE_VALUE;
  2842. }
  2843. g_pMsgBuffer->WriteLong( iEncodedEHandle );
  2844. }
  2845. // bitwise
  2846. void MessageWriteBool( bool bValue )
  2847. {
  2848. if (!g_pMsgBuffer)
  2849. Error( "WriteBool called with no active message\n" );
  2850. g_pMsgBuffer->WriteOneBit( bValue ? 1 : 0 );
  2851. }
  2852. void MessageWriteUBitLong( unsigned int data, int numbits )
  2853. {
  2854. if (!g_pMsgBuffer)
  2855. Error( "WriteUBitLong called with no active message\n" );
  2856. g_pMsgBuffer->WriteUBitLong( data, numbits );
  2857. }
  2858. void MessageWriteSBitLong( int data, int numbits )
  2859. {
  2860. if (!g_pMsgBuffer)
  2861. Error( "WriteSBitLong called with no active message\n" );
  2862. g_pMsgBuffer->WriteSBitLong( data, numbits );
  2863. }
  2864. void MessageWriteBits( const void *pIn, int nBits )
  2865. {
  2866. if (!g_pMsgBuffer)
  2867. Error( "WriteBits called with no active message\n" );
  2868. g_pMsgBuffer->WriteBits( pIn, nBits );
  2869. }
  2870. class CServerDLLSharedAppSystems : public IServerDLLSharedAppSystems
  2871. {
  2872. public:
  2873. CServerDLLSharedAppSystems()
  2874. {
  2875. AddAppSystem( "soundemittersystem" DLL_EXT_STRING, SOUNDEMITTERSYSTEM_INTERFACE_VERSION );
  2876. AddAppSystem( "scenefilecache" DLL_EXT_STRING, SCENE_FILE_CACHE_INTERFACE_VERSION );
  2877. }
  2878. virtual int Count()
  2879. {
  2880. return m_Systems.Count();
  2881. }
  2882. virtual char const *GetDllName( int idx )
  2883. {
  2884. return m_Systems[ idx ].m_pModuleName;
  2885. }
  2886. virtual char const *GetInterfaceName( int idx )
  2887. {
  2888. return m_Systems[ idx ].m_pInterfaceName;
  2889. }
  2890. private:
  2891. void AddAppSystem( char const *moduleName, char const *interfaceName )
  2892. {
  2893. AppSystemInfo_t sys;
  2894. sys.m_pModuleName = moduleName;
  2895. sys.m_pInterfaceName = interfaceName;
  2896. m_Systems.AddToTail( sys );
  2897. }
  2898. CUtlVector< AppSystemInfo_t > m_Systems;
  2899. };
  2900. EXPOSE_SINGLE_INTERFACE( CServerDLLSharedAppSystems, IServerDLLSharedAppSystems, SERVER_DLL_SHARED_APPSYSTEMS );
  2901. //-----------------------------------------------------------------------------
  2902. //
  2903. //-----------------------------------------------------------------------------
  2904. void CServerGameTags::GetTaggedConVarList( KeyValues *pCvarTagList )
  2905. {
  2906. if ( pCvarTagList && g_pGameRules )
  2907. {
  2908. g_pGameRules->GetTaggedConVarList( pCvarTagList );
  2909. }
  2910. }
  2911. #ifndef NO_STEAM
  2912. CSteamID GetSteamIDForPlayerIndex( int iPlayerIndex )
  2913. {
  2914. const CSteamID *pResult = engine->GetClientSteamIDByPlayerIndex( iPlayerIndex );
  2915. if ( pResult )
  2916. return *pResult;
  2917. // Return a bogus steam ID
  2918. return CSteamID();
  2919. }
  2920. #endif