Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4091 lines
124 KiB

  1. //===== Copyright © 1996-2005, 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. #if !defined (_GAMECONSOLE)
  10. #endif // _GAMECONSOLE
  11. #include "gamestringpool.h"
  12. #include "mapentities_shared.h"
  13. #include "game.h"
  14. #include "entityapi.h"
  15. #include "client.h"
  16. #include "saverestore.h"
  17. #include "entitylist.h"
  18. #include "gamerules.h"
  19. #include "soundent.h"
  20. #include "player.h"
  21. #include "server_class.h"
  22. #include "ai_node.h"
  23. #include "ai_link.h"
  24. #include "ai_saverestore.h"
  25. #include "ai_networkmanager.h"
  26. #include "ndebugoverlay.h"
  27. #include "ivoiceserver.h"
  28. #include <stdarg.h>
  29. #include "movehelper_server.h"
  30. #include "networkstringtable_gamedll.h"
  31. #include "filesystem.h"
  32. #include "func_areaportalwindow.h"
  33. #include "igamesystem.h"
  34. #include "init_factory.h"
  35. #include "vstdlib/random.h"
  36. #include "env_wind_shared.h"
  37. #include "engine/IEngineSound.h"
  38. #include "ispatialpartition.h"
  39. #include "textstatsmgr.h"
  40. #include "bitbuf.h"
  41. #include "saverestoretypes.h"
  42. #include "physics_saverestore.h"
  43. #include "tier0/vprof.h"
  44. #include "effect_dispatch_data.h"
  45. #include "engine/IStaticPropMgr.h"
  46. #include "TemplateEntities.h"
  47. #include "ai_speech.h"
  48. #include "soundenvelope.h"
  49. #include "usermessages.h"
  50. #include "physics.h"
  51. #include "igameevents.h"
  52. #include "EventLog.h"
  53. #include "datacache/idatacache.h"
  54. #include "engine/ivdebugoverlay.h"
  55. #include "shareddefs.h"
  56. #include "props.h"
  57. #include "timedeventmgr.h"
  58. #include "gameinterface.h"
  59. #include "eventqueue.h"
  60. #include "hltvdirector.h"
  61. #if defined( REPLAY_ENABLED )
  62. #include "replaydirector.h"
  63. #endif
  64. #include "SoundEmitterSystem/isoundemittersystembase.h"
  65. #include "nav_mesh.h"
  66. #include "ai_responsesystem.h"
  67. #include "saverestore_stringtable.h"
  68. #include "util.h"
  69. #include "tier0/icommandline.h"
  70. #include "datacache/imdlcache.h"
  71. #include "engine/iserverplugin.h"
  72. #include "env_debughistory.h"
  73. #include "util_shared.h"
  74. #include "player_voice_listener.h"
  75. #ifdef _WIN32
  76. #include "ienginevgui.h"
  77. #include "vgui_gamedll_int.h"
  78. #include "vgui_controls/AnimationController.h"
  79. #endif
  80. #include "ragdoll_shared.h"
  81. #include "toolframework/iserverenginetools.h"
  82. #include "sceneentity.h"
  83. #include "appframework/IAppSystemGroup.h"
  84. #include "scenefilecache/ISceneFileCache.h"
  85. #include "tier2/tier2.h"
  86. #include "particles/particles.h"
  87. #include "GameStats.h"
  88. #include "ixboxsystem.h"
  89. #include "matchmaking/imatchframework.h"
  90. #include "querycache.h"
  91. #include "particle_parse.h"
  92. #include "steam/steam_gameserver.h"
  93. #include "tier3/tier3.h"
  94. #include "serverbenchmark_base.h"
  95. #include "vscript/ivscript.h"
  96. #include "foundryhelpers_server.h"
  97. #include "entity_tools_server.h"
  98. #include "foundry/iserverfoundry.h"
  99. #include "point_template.h"
  100. #include "engine/iblackbox.h"
  101. #include "vstdlib/jobthread.h"
  102. #include "vscript_server.h"
  103. #include "tier2/tier2_logging.h"
  104. #include "fmtstr.h"
  105. #include "mathlib/aabb.h"
  106. #include "env_cascade_light.h"
  107. #if defined( CSTRIKE15 )
  108. #include "cstrike15/cs_player.h"
  109. #include "gametypes/igametypes.h"
  110. #include "cs_shareddefs.h"
  111. #endif
  112. #ifdef INFESTED_DLL
  113. #include "missionchooser/iasw_mission_chooser.h"
  114. #include "matchmaking/swarm/imatchext_swarm.h"
  115. #endif
  116. #ifdef CSTRIKE_DLL // BOTPORT: TODO: move these ifdefs out
  117. #include "bot/bot.h"
  118. #endif
  119. #ifdef PORTAL
  120. #include "portal_base2d_shared.h"
  121. #include "portal_player.h"
  122. #endif
  123. #ifdef PORTAL2
  124. #include "info_placement_helper.h"
  125. #include "paint_saverestore.h"
  126. #include "prop_portal_shared.h"
  127. #include "portal_shareddefs.h"
  128. #endif // PORTAL2
  129. #ifdef DOTA_DLL
  130. #include "dota/dota_bot_commander.h"
  131. #endif
  132. #include "dedicated_server_ugc_manager.h"
  133. #ifdef _WIN32
  134. #include "IGameUIFuncs.h"
  135. #endif
  136. #include "CegClientWrapper.h"
  137. extern IToolFrameworkServer *g_pToolFrameworkServer;
  138. extern IParticleSystemQuery *g_pParticleSystemQuery;
  139. extern ConVar commentary;
  140. extern ConVar game_type;
  141. extern ConVar game_mode;
  142. ConVar occlusion_test_shadow_length( "occlusion_test_shadow_length", "144", FCVAR_RELEASE, "Max length of completely occluded shadow to consider a player for occlusion test. If shadow provably stops at this distance, the player may be considered for occlusion test. For longer shadows, we just don't do occlusion because we are not likely to find full occlusion when one of the boxes is expanded too much." );
  143. //ConVar occlusion_test_margins( "occlusion_test_margins", "36", FCVAR_RELEASE, "Amount by which the player bounding box is expanded for occlusion test. This margin should be large enough to accommodate player movement within a frame or two, and the longest weapon they might hold. Shadow does not take this into account." ); // default: 360 (max speed) / 30 ( give it a couple of frames) + however much the biggest weapon can stick out
  144. ConVar occlusion_test_camera_margins( "occlusion_test_camera_margins", "12", FCVAR_RELEASE, "Amount by which the camera (viewer's eye) is expanded for occlusion test. This should be large enough to accommodate eye's movement within a frame or two" ); // default: 360 (max speed) / 30 ( give it a couple of frames)
  145. ConVar occlusion_test_jump_margin( "occlusion_test_jump_margin", "12", FCVAR_RELEASE, "Amount by which the player bounding box is expanded up for occlusion test to account for jumping. This margin should be large enough to accommodate player movement within a frame or two. Affects both camera box and player box." ); // default: 360 (max speed) / 30 ( give it a couple of frames) + however much the biggest weapon can stick out
  146. //ConVar occlusion_test_shadow_max_distance( "occlusion_test_shadow_max_distance", "1500", FCVAR_RELEASE, "Max distance at which to consider shadows for occlusion computations" );
  147. ConVar pvs_min_player_distance( "pvs_min_player_distance", "1500", FCVAR_RELEASE, "Min distance to player at which PVS is used. At closer distances, PVS assumes we can see a shadow or something else from the player, so it's safer to just always be \"Visible\"" );
  148. bool ShouldHideAllPlayers() { return false; }
  149. // this context is not available on dedicated servers
  150. // WARNING! always check if interfaces are available before using
  151. #if !defined(NO_STEAM)
  152. static CSteamAPIContext s_SteamAPIContext;
  153. CSteamAPIContext *steamapicontext = &s_SteamAPIContext;
  154. // this context is not available on a pure client connected to a remote server.
  155. // WARNING! always check if interfaces are available before using
  156. static CSteamGameServerAPIContext s_SteamGameServerAPIContext;
  157. CSteamGameServerAPIContext *steamgameserverapicontext = &s_SteamGameServerAPIContext;
  158. #endif
  159. IUploadGameStats *gamestatsuploader = NULL;
  160. // memdbgon must be the last include file in a .cpp file!!!
  161. #include "tier0/memdbgon.h"
  162. DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_CONSOLE, "Console" );
  163. CTimedEventMgr g_NetworkPropertyEventMgr;
  164. ISaveRestoreBlockHandler *GetEventQueueSaveRestoreBlockHandler();
  165. ISaveRestoreBlockHandler *GetCommentarySaveRestoreBlockHandler();
  166. CUtlLinkedList<CMapEntityRef, unsigned short> g_MapEntityRefs;
  167. // Engine interfaces.
  168. IVEngineServer *engine = NULL;
  169. IVoiceServer *g_pVoiceServer = NULL;
  170. #if !defined(_STATIC_LINKED)
  171. IFileSystem *filesystem = NULL;
  172. #else
  173. extern IFileSystem *filesystem;
  174. #endif
  175. INetworkStringTableContainer *networkstringtable = NULL;
  176. IStaticPropMgrServer *staticpropmgr = NULL;
  177. IUniformRandomStream *random = NULL;
  178. IEngineSound *enginesound = NULL;
  179. ISpatialPartition *partition = NULL;
  180. IVModelInfo *modelinfo = NULL;
  181. IEngineTrace *enginetrace = NULL;
  182. IFileLoggingListener *filelogginglistener = NULL;
  183. IGameEventManager2 *gameeventmanager = NULL;
  184. IDataCache *datacache = NULL;
  185. IVDebugOverlay * debugoverlay = NULL;
  186. ISoundEmitterSystemBase *soundemitterbase = NULL;
  187. IServerPluginHelpers *serverpluginhelpers = NULL;
  188. #ifdef SERVER_USES_VGUI
  189. IEngineVGui *enginevgui = NULL;
  190. #endif // SERVER_USES_VGUI
  191. IServerEngineTools *serverenginetools = NULL;
  192. IServerFoundry *serverfoundry = NULL;
  193. ISceneFileCache *scenefilecache = NULL;
  194. #ifdef SERVER_USES_VGUI
  195. IGameUIFuncs *gameuifuncs = NULL;
  196. #endif // SERVER_USES_VGUI
  197. IXboxSystem *xboxsystem = NULL; // Xbox 360 only
  198. IScriptManager *scriptmanager = NULL;
  199. IBlackBox *blackboxrecorder = NULL;
  200. #ifdef INFESTED_DLL
  201. IASW_Mission_Chooser *missionchooser = NULL;
  202. IMatchExtSwarm *g_pMatchExtSwarm = NULL;
  203. #endif
  204. IGameSystem *SoundEmitterSystem();
  205. void SoundSystemPreloadSounds( void );
  206. bool ModelSoundsCacheInit();
  207. void ModelSoundsCacheShutdown();
  208. void SceneManager_ClientActive( CBasePlayer *player );
  209. class IMaterialSystem;
  210. class IStudioRender;
  211. #ifdef _DEBUG
  212. static ConVar s_UseNetworkVars( "UseNetworkVars", "1", FCVAR_CHEAT, "For profiling, toggle network vars." );
  213. #endif
  214. extern ConVar sv_noclipduringpause;
  215. ConVar sv_massreport( "sv_massreport", "0" );
  216. ConVar sv_force_transmit_ents( "sv_force_transmit_ents", "0", FCVAR_RELEASE, "Will transmit all entities to client, regardless of PVS conditions (will still skip based on transmit flags, however)." );
  217. ConVar sv_autosave( "sv_autosave", "1", 0, "Set to 1 to autosave game on level transition. Does not affect autosave triggers." );
  218. ConVar *sv_maxreplay = NULL;
  219. ConVar sv_comp_mode_allow_dc( "sv_comp_mode_allow_dc",
  220. "0", FCVAR_DEVELOPMENTONLY,
  221. "Set this to 1 to allow direct connects in Competitive mode" );
  222. ConVar sv_dc_friends_reqd(
  223. "sv_dc_friends_reqd",
  224. "0", FCVAR_RELEASE,
  225. "Set this to 0 to allow direct connects to a game in progress even if no presents are present" );
  226. static ConVar *g_pcv_commentary = NULL;
  227. static ConVar *g_pcv_ThreadMode = NULL;
  228. #if !defined(NO_STEAM)
  229. //-----------------------------------------------------------------------------
  230. // Purpose: singleton accessor
  231. //-----------------------------------------------------------------------------
  232. static CSteam3Server s_Steam3Server;
  233. CSteam3Server &Steam3Server()
  234. {
  235. return s_Steam3Server;
  236. }
  237. //-----------------------------------------------------------------------------
  238. // Purpose: Constructor
  239. //-----------------------------------------------------------------------------
  240. CSteam3Server::CSteam3Server()
  241. {
  242. m_bInitialized = false;
  243. }
  244. #endif
  245. // String tables
  246. INetworkStringTable *g_pStringTableParticleEffectNames = NULL;
  247. INetworkStringTable *g_pStringTableEffectDispatch = NULL;
  248. INetworkStringTable *g_pStringTableVguiScreen = NULL;
  249. INetworkStringTable *g_pStringTableMaterials = NULL;
  250. INetworkStringTable *g_pStringTableInfoPanel = NULL;
  251. INetworkStringTable *g_pStringTableClientSideChoreoScenes = NULL;
  252. INetworkStringTable *g_pStringTableExtraParticleFiles = NULL;
  253. INetworkStringTable *g_pStringTableMovies = NULL;
  254. CStringTableSaveRestoreOps g_VguiScreenStringOps;
  255. // Holds global variables shared between engine and game.
  256. CGlobalVars *gpGlobals;
  257. static int g_nCommandClientIndex = -1;
  258. // The chapter number of the current
  259. static int g_nCurrentChapterIndex = -1;
  260. #ifdef _DEBUG
  261. 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)." );
  262. #endif
  263. static ClientPutInServerOverrideFn g_pClientPutInServerOverride = NULL;
  264. static void UpdateChapterRestrictions( const char *mapname );
  265. CSharedEdictChangeInfo *g_pSharedChangeInfo = NULL;
  266. IChangeInfoAccessor *CBaseEdict::GetChangeAccessor()
  267. {
  268. return engine->GetChangeAccessor( (const edict_t *)this );
  269. }
  270. const IChangeInfoAccessor *CBaseEdict::GetChangeAccessor() const
  271. {
  272. return engine->GetChangeAccessor( (const edict_t *)this );
  273. }
  274. const char *GetHintTypeDescription( CAI_Hint *pHint );
  275. void ClientPutInServerOverride( ClientPutInServerOverrideFn fn )
  276. {
  277. g_pClientPutInServerOverride = fn;
  278. }
  279. ConVar ai_post_frame_navigation( "ai_post_frame_navigation", "0" );
  280. class CPostFrameNavigationHook;
  281. extern CPostFrameNavigationHook *PostFrameNavigationSystem( void );
  282. static bool g_bHeadTrackingEnabled = false;
  283. bool IsHeadTrackingEnabled()
  284. {
  285. #if defined( HL2_DLL )
  286. return g_bHeadTrackingEnabled;
  287. #else
  288. return false;
  289. #endif
  290. }
  291. //-----------------------------------------------------------------------------
  292. // Also in cdll so linked libs can extern it
  293. //-----------------------------------------------------------------------------
  294. bool UTIL_IsDedicatedServer( void )
  295. {
  296. return engine->IsDedicatedServer();
  297. }
  298. //-----------------------------------------------------------------------------
  299. // Purpose:
  300. // Output : int
  301. //-----------------------------------------------------------------------------
  302. int UTIL_GetCommandClientIndex( void )
  303. {
  304. // -1 == unknown,dedicated server console
  305. // 0 == player 1
  306. // Convert to 1 based offset
  307. return (g_nCommandClientIndex+1);
  308. }
  309. //-----------------------------------------------------------------------------
  310. // Purpose:
  311. // Output : CBasePlayer
  312. //-----------------------------------------------------------------------------
  313. CBasePlayer *UTIL_GetCommandClient( void )
  314. {
  315. int idx = UTIL_GetCommandClientIndex();
  316. if ( idx > 0 )
  317. {
  318. return UTIL_PlayerByIndex( idx );
  319. }
  320. // HLDS console issued command
  321. return NULL;
  322. }
  323. extern void InitializeCvars( void );
  324. float GetFloorZ(const Vector &origin);
  325. void UpdateAllClientData( void );
  326. void DrawMessageEntities();
  327. #include "ai_network.h"
  328. // For now just using one big AI network
  329. extern ConVar think_limit;
  330. #if 0
  331. //-----------------------------------------------------------------------------
  332. // Purpose: Draw output overlays for any measure sections
  333. // Input :
  334. //-----------------------------------------------------------------------------
  335. void DrawMeasuredSections(void)
  336. {
  337. int row = 1;
  338. float rowheight = 0.025;
  339. CMeasureSection *p = CMeasureSection::GetList();
  340. while ( p )
  341. {
  342. char str[256];
  343. Q_snprintf(str,sizeof(str),"%s",p->GetName());
  344. NDebugOverlay::ScreenText( 0.01,0.51+(row*rowheight),str, 255,255,255,255, 0.0 );
  345. Q_snprintf(str,sizeof(str),"%5.2f\n",p->GetTime().GetMillisecondsF());
  346. //Q_snprintf(str,sizeof(str),"%3.3f\n",p->GetTime().GetSeconds() * 100.0 / Plat_FloatTime());
  347. NDebugOverlay::ScreenText( 0.28,0.51+(row*rowheight),str, 255,255,255,255, 0.0 );
  348. Q_snprintf(str,sizeof(str),"%5.2f\n",p->GetMaxTime().GetMillisecondsF());
  349. //Q_snprintf(str,sizeof(str),"%3.3f\n",p->GetTime().GetSeconds() * 100.0 / Plat_FloatTime());
  350. NDebugOverlay::ScreenText( 0.34,0.51+(row*rowheight),str, 255,255,255,255, 0.0 );
  351. row++;
  352. p = p->GetNext();
  353. }
  354. bool sort_reset = false;
  355. // Time to redo sort?
  356. if ( measure_resort.GetFloat() > 0.0 &&
  357. Plat_FloatTime() >= CMeasureSection::m_dNextResort )
  358. {
  359. // Redo it
  360. CMeasureSection::SortSections();
  361. // Set next time
  362. CMeasureSection::m_dNextResort = Plat_FloatTime() + measure_resort.GetFloat();
  363. // Flag to reset sort accumulator, too
  364. sort_reset = true;
  365. }
  366. // Iterate through the sections now
  367. p = CMeasureSection::GetList();
  368. while ( p )
  369. {
  370. // Update max
  371. p->UpdateMax();
  372. // Reset regular accum.
  373. p->Reset();
  374. // Reset sort accum less often
  375. if ( sort_reset )
  376. {
  377. p->SortReset();
  378. }
  379. p = p->GetNext();
  380. }
  381. }
  382. #endif
  383. //-----------------------------------------------------------------------------
  384. // Purpose:
  385. //-----------------------------------------------------------------------------
  386. void DrawAllDebugOverlays( void )
  387. {
  388. NDebugOverlay::PurgeTextOverlays();
  389. // If in debug select mode print the selection entities name or classname
  390. if (CBaseEntity::m_bInDebugSelect)
  391. {
  392. CBasePlayer* pPlayer = UTIL_PlayerByIndex( CBaseEntity::m_nDebugPlayer );
  393. if (pPlayer)
  394. {
  395. // First try to trace a hull to an entity
  396. CBaseEntity *pEntity = pPlayer->FindPickerEntity();
  397. if ( pEntity )
  398. {
  399. pEntity->DrawDebugTextOverlays();
  400. pEntity->DrawBBoxOverlay();
  401. pEntity->SendDebugPivotOverlay();
  402. }
  403. }
  404. }
  405. // --------------------------------------------------------
  406. // Draw debug overlay lines
  407. // --------------------------------------------------------
  408. UTIL_DrawOverlayLines();
  409. // ------------------------------------------------------------------------
  410. // If in wc_edit mode draw a box to highlight which node I'm looking at
  411. // ------------------------------------------------------------------------
  412. if (engine->IsInEditMode())
  413. {
  414. CBasePlayer* pPlayer = UTIL_PlayerByIndex( CBaseEntity::m_nDebugPlayer );
  415. if (pPlayer)
  416. {
  417. if (g_pAINetworkManager->GetEditOps()->m_bLinkEditMode)
  418. {
  419. CAI_Link* pAILink = pPlayer->FindPickerAILink();
  420. if (pAILink)
  421. {
  422. // For now just using one big AI network
  423. Vector startPos = g_pBigAINet->GetNode(pAILink->m_iSrcID)->GetPosition(g_pAINetworkManager->GetEditOps()->m_iHullDrawNum);
  424. Vector endPos = g_pBigAINet->GetNode(pAILink->m_iDestID)->GetPosition(g_pAINetworkManager->GetEditOps()->m_iHullDrawNum);
  425. Vector linkDir = startPos-endPos;
  426. float linkLen = VectorNormalize( linkDir );
  427. // Draw in green if link that's been turned off
  428. if (pAILink->m_LinkInfo & bits_LINK_OFF)
  429. {
  430. NDebugOverlay::BoxDirection(startPos, Vector(-4,-4,-4), Vector(-linkLen,4,4), linkDir, 0,255,0,40,0);
  431. }
  432. // Draw in a pukey yellowish green if link that's "bashable", which is off to everyone but a special door basher
  433. else if (pAILink->m_LinkInfo & bits_LINK_ASW_BASHABLE)
  434. {
  435. NDebugOverlay::BoxDirection(startPos, Vector(-4,-4,-4), Vector(-linkLen,4,4), linkDir, 40,255,0,40,0);
  436. }
  437. else
  438. {
  439. NDebugOverlay::BoxDirection(startPos, Vector(-4,-4,-4), Vector(-linkLen,4,4), linkDir, 255,0,0,40,0);
  440. }
  441. }
  442. }
  443. else
  444. {
  445. CAI_Node* pAINode;
  446. if (g_pAINetworkManager->GetEditOps()->m_bAirEditMode)
  447. {
  448. pAINode = pPlayer->FindPickerAINode(NODE_AIR);
  449. }
  450. else
  451. {
  452. pAINode = pPlayer->FindPickerAINode(NODE_GROUND);
  453. }
  454. if (pAINode)
  455. {
  456. Vector vecPos = pAINode->GetPosition(g_pAINetworkManager->GetEditOps()->m_iHullDrawNum);
  457. NDebugOverlay::Box( vecPos, Vector(-8,-8,-8), Vector(8,8,8), 255,0,0,40,0);
  458. if ( pAINode->GetHint() )
  459. {
  460. CBaseEntity *pEnt = (CBaseEntity *)pAINode->GetHint();
  461. if ( pEnt->GetEntityName() != NULL_STRING )
  462. {
  463. NDebugOverlay::Text( vecPos + Vector(0,0,6), STRING(pEnt->GetEntityName()), false, 0 );
  464. }
  465. NDebugOverlay::Text( vecPos, GetHintTypeDescription( pAINode->GetHint() ), false, 0 );
  466. }
  467. }
  468. }
  469. // ------------------------------------
  470. // If in air edit mode draw guide line
  471. // ------------------------------------
  472. if (g_pAINetworkManager->GetEditOps()->m_bAirEditMode)
  473. {
  474. UTIL_DrawPositioningOverlay(g_pAINetworkManager->GetEditOps()->m_flAirEditDistance);
  475. }
  476. else
  477. {
  478. NDebugOverlay::DrawGroundCrossHairOverlay();
  479. }
  480. }
  481. }
  482. // For not just using one big AI Network
  483. if ( g_pAINetworkManager )
  484. {
  485. g_pAINetworkManager->GetEditOps()->DrawAINetworkOverlay();
  486. }
  487. // PERFORMANCE: only do this in developer mode
  488. if ( g_pDeveloper->GetInt() )
  489. {
  490. // iterate through all objects for debug overlays
  491. const CEntInfo *pInfo = gEntList.FirstEntInfo();
  492. for ( ;pInfo; pInfo = pInfo->m_pNext )
  493. {
  494. CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
  495. // HACKHACK: to flag off these calls
  496. if ( ent->m_debugOverlays || ent->m_pTimedOverlay )
  497. {
  498. MDLCACHE_CRITICAL_SECTION();
  499. ent->DrawDebugGeometryOverlays();
  500. }
  501. }
  502. }
  503. if ( sv_massreport.GetInt() )
  504. {
  505. // iterate through all objects for debug overlays
  506. const CEntInfo *pInfo = gEntList.FirstEntInfo();
  507. for ( ;pInfo; pInfo = pInfo->m_pNext )
  508. {
  509. CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
  510. if (!ent->VPhysicsGetObject())
  511. continue;
  512. char tempstr[512];
  513. Q_snprintf(tempstr, sizeof(tempstr),"%s: Mass: %.2f kg / %.2f lb (%s)",
  514. STRING(ent->GetModelName()), ent->VPhysicsGetObject()->GetMass(),
  515. kg2lbs(ent->VPhysicsGetObject()->GetMass()),
  516. GetMassEquivalent(ent->VPhysicsGetObject()->GetMass()));
  517. ent->EntityText(0, tempstr, 0);
  518. }
  519. }
  520. // A hack to draw point_message entities w/o developer required
  521. DrawMessageEntities();
  522. }
  523. // enable threading of init functions on x360
  524. static ConVar sv_threaded_init("sv_threaded_init", IsGameConsole() ? "1" : "0");
  525. CEG_NOINLINE static bool InitGameSystems( CreateInterfaceFn appSystemFactory )
  526. {
  527. // The string system must init first + shutdown last
  528. IGameSystem::Add( GameStringSystem() );
  529. // Physics must occur before the sound envelope manager
  530. IGameSystem::Add( PhysicsGameSystem() );
  531. // Precache system must be next (requires physics game system)
  532. IGameSystem::Add( g_pPrecacheRegister );
  533. // Used to service deferred navigation queries for NPCs
  534. IGameSystem::Add( (IGameSystem *) PostFrameNavigationSystem() );
  535. // Add game log system
  536. IGameSystem::Add( GameLogSystem() );
  537. // Add HLTV director
  538. IGameSystem::Add( HLTVDirectorSystem() );
  539. #ifdef DOTA_DLL
  540. IGameSystem::Add( DOTAPlayerCommanderSystem() );
  541. #endif
  542. #if defined( REPLAY_ENABLED )
  543. // Add Replay director
  544. IGameSystem::Add( ReplayDirectorSystem() );
  545. #endif
  546. // Add sound emitter
  547. IGameSystem::Add( SoundEmitterSystem() );
  548. #ifdef SERVER_USES_VGUI
  549. // Startup vgui
  550. if ( enginevgui )
  551. {
  552. if(!VGui_Startup( appSystemFactory ))
  553. return false;
  554. }
  555. #endif // SERVER_USES_VGUI
  556. // load Mod specific game events ( MUST be before InitAllSystems() so it can pickup the mod specific events)
  557. gameeventmanager->LoadEventsFromFile("resource/ModEvents.res");
  558. #ifdef CSTRIKE_DLL // BOTPORT: TODO: move these ifdefs out
  559. InstallBotControl();
  560. #endif
  561. if ( !IGameSystem::InitAllSystems() )
  562. return false;
  563. // Due to dependencies, these are not autogamesystems
  564. if ( !ModelSoundsCacheInit() )
  565. {
  566. return false;
  567. }
  568. // Parse the particle manifest file & register the effects within it
  569. // ParseParticleEffects( false );
  570. InvalidateQueryCache();
  571. // create the Navigation Mesh interface
  572. TheNavMesh = NavMeshFactory();
  573. // init the gamestatsupload connection
  574. gamestatsuploader->InitConnection();
  575. return true;
  576. }
  577. CEG_PROTECT_FUNCTION( InitGameSystems );
  578. CServerGameDLL g_ServerGameDLL;
  579. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerGameDLL, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL, g_ServerGameDLL);
  580. bool CServerGameDLL::DLLInit( CreateInterfaceFn appSystemFactory,
  581. CreateInterfaceFn physicsFactory, CreateInterfaceFn fileSystemFactory,
  582. CGlobalVars *pGlobals)
  583. {
  584. COM_TimestampedLog( "ConnectTier1/2/3Libraries - Start" );
  585. ConnectTier1Libraries( &appSystemFactory, 1 );
  586. ConnectTier2Libraries( &appSystemFactory, 1 );
  587. ConnectTier3Libraries( &appSystemFactory, 1 );
  588. COM_TimestampedLog( "ConnectTier1/2/3Libraries - Finish" );
  589. // Connected in ConnectTier1Libraries
  590. if ( cvar == NULL )
  591. return false;
  592. #if !defined( SWDS ) && !defined(NO_STEAM)
  593. #ifndef _PS3
  594. SteamAPI_InitSafe();
  595. #endif
  596. s_SteamAPIContext.Init();
  597. #endif
  598. #if !defined(NO_STEAM)
  599. s_SteamGameServerAPIContext.Init();
  600. #endif
  601. COM_TimestampedLog( "Factories - Start" );
  602. // init each (seperated for ease of debugging)
  603. if ( (engine = (IVEngineServer*)appSystemFactory(INTERFACEVERSION_VENGINESERVER, NULL)) == NULL )
  604. return false;
  605. if( !STEAMWORKS_INITCEGLIBRARY() )
  606. {
  607. return false;
  608. }
  609. #if !defined(NO_STEAM)
  610. #ifndef _PS3
  611. if( SteamAPI_RestartAppIfNecessary( engine->GetAppID() ) )
  612. {
  613. STEAMWORKS_TERMCEGLIBRARY();
  614. return false;
  615. }
  616. #endif
  617. #endif
  618. STEAMWORKS_TESTSECRETALWAYS();
  619. STEAMWORKS_SELFCHECK();
  620. if ( (g_pVoiceServer = (IVoiceServer*)appSystemFactory(INTERFACEVERSION_VOICESERVER, NULL)) == NULL )
  621. return false;
  622. if ( (networkstringtable = (INetworkStringTableContainer *)appSystemFactory(INTERFACENAME_NETWORKSTRINGTABLESERVER,NULL)) == NULL )
  623. return false;
  624. if ( (staticpropmgr = (IStaticPropMgrServer *)appSystemFactory(INTERFACEVERSION_STATICPROPMGR_SERVER,NULL)) == NULL )
  625. return false;
  626. if ( (random = (IUniformRandomStream *)appSystemFactory(VENGINE_SERVER_RANDOM_INTERFACE_VERSION, NULL)) == NULL )
  627. return false;
  628. if ( (enginesound = (IEngineSound *)appSystemFactory(IENGINESOUND_SERVER_INTERFACE_VERSION, NULL)) == NULL )
  629. return false;
  630. if ( (::partition = (ISpatialPartition *)appSystemFactory(INTERFACEVERSION_SPATIALPARTITION, NULL)) == NULL )
  631. return false;
  632. if ( (modelinfo = (IVModelInfo *)appSystemFactory(VMODELINFO_SERVER_INTERFACE_VERSION, NULL)) == NULL )
  633. return false;
  634. if ( (enginetrace = (IEngineTrace *)appSystemFactory(INTERFACEVERSION_ENGINETRACE_SERVER,NULL)) == NULL )
  635. return false;
  636. if ( (filelogginglistener = (IFileLoggingListener *)appSystemFactory(FILELOGGINGLISTENER_INTERFACE_VERSION, NULL)) == NULL )
  637. return false;
  638. if ( (filesystem = (IFileSystem *)fileSystemFactory(FILESYSTEM_INTERFACE_VERSION,NULL)) == NULL )
  639. return false;
  640. if ( (gameeventmanager = (IGameEventManager2 *)appSystemFactory(INTERFACEVERSION_GAMEEVENTSMANAGER2,NULL)) == NULL )
  641. return false;
  642. if ( (datacache = (IDataCache*)appSystemFactory(DATACACHE_INTERFACE_VERSION, NULL )) == NULL )
  643. return false;
  644. if ( (soundemitterbase = (ISoundEmitterSystemBase *)appSystemFactory(SOUNDEMITTERSYSTEM_INTERFACE_VERSION, NULL)) == NULL )
  645. return false;
  646. if ( (gamestatsuploader = (IUploadGameStats *)appSystemFactory( INTERFACEVERSION_UPLOADGAMESTATS, NULL )) == NULL )
  647. return false;
  648. if ( !mdlcache )
  649. return false;
  650. if ( (serverpluginhelpers = (IServerPluginHelpers *)appSystemFactory(INTERFACEVERSION_ISERVERPLUGINHELPERS, NULL)) == NULL )
  651. return false;
  652. if ( (scenefilecache = (ISceneFileCache *)appSystemFactory( SCENE_FILE_CACHE_INTERFACE_VERSION, NULL )) == NULL )
  653. return false;
  654. if ( (blackboxrecorder = (IBlackBox *)appSystemFactory(BLACKBOX_INTERFACE_VERSION, NULL)) == NULL )
  655. return false;
  656. if ( (xboxsystem = (IXboxSystem *)appSystemFactory( XBOXSYSTEM_INTERFACE_VERSION, NULL )) == NULL )
  657. return false;
  658. if ( !CommandLine()->CheckParm( "-noscripting") )
  659. {
  660. scriptmanager = (IScriptManager *)appSystemFactory( VSCRIPT_INTERFACE_VERSION, NULL );
  661. }
  662. #if defined( CSTRIKE15 )
  663. if ( ( g_pGameTypes = (IGameTypes *)appSystemFactory( VENGINE_GAMETYPES_VERSION, NULL )) == NULL )
  664. return false;
  665. #endif
  666. #ifdef SERVER_USES_VGUI
  667. // If not running dedicated, grab the engine vgui interface
  668. if ( !engine->IsDedicatedServer() )
  669. {
  670. #ifdef _WIN32
  671. if ( ( enginevgui = ( IEngineVGui * )appSystemFactory(VENGINE_VGUI_VERSION, NULL)) == NULL )
  672. return false;
  673. // This interface is optional, and is only valid when running with -tools
  674. serverenginetools = ( IServerEngineTools * )appSystemFactory( VSERVERENGINETOOLS_INTERFACE_VERSION, NULL );
  675. gameuifuncs = (IGameUIFuncs * )appSystemFactory( VENGINE_GAMEUIFUNCS_VERSION, NULL );
  676. #endif
  677. }
  678. #endif // SERVER_USES_VGUI
  679. #ifdef INFESTED_DLL
  680. if ( (missionchooser = (IASW_Mission_Chooser *)appSystemFactory(ASW_MISSION_CHOOSER_VERSION, NULL)) == NULL )
  681. return false;
  682. if ( (g_pMatchExtSwarm = (IMatchExtSwarm *)appSystemFactory(IMATCHEXT_SWARM_INTERFACE, NULL)) == NULL )
  683. return false;
  684. #endif
  685. if ( !g_pMatchFramework )
  686. return false;
  687. if ( IMatchExtensions *pIMatchExtensions = g_pMatchFramework->GetMatchExtensions() )
  688. pIMatchExtensions->RegisterExtensionInterface(
  689. INTERFACEVERSION_SERVERGAMEDLL, static_cast< IServerGameDLL * >( this ) );
  690. COM_TimestampedLog( "Factories - Finish" );
  691. COM_TimestampedLog( "soundemitterbase->Connect" );
  692. // Yes, both the client and game .dlls will try to Connect, the soundemittersystem.dll will handle this gracefully
  693. if ( !soundemitterbase->Connect( appSystemFactory ) )
  694. return false;
  695. if ( CommandLine()->FindParm( "-headtracking" ) )
  696. g_bHeadTrackingEnabled = true;
  697. // cache the globals
  698. gpGlobals = pGlobals;
  699. g_pSharedChangeInfo = engine->GetSharedEdictChangeInfo();
  700. COM_TimestampedLog( "MathLib_Init" );
  701. MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
  702. // save these in case other system inits need them
  703. factorylist_t factories;
  704. factories.engineFactory = appSystemFactory;
  705. factories.fileSystemFactory = fileSystemFactory;
  706. factories.physicsFactory = physicsFactory;
  707. FactoryList_Store( factories );
  708. COM_TimestampedLog( "gameeventmanager->LoadEventsFromFile" );
  709. // load used game events
  710. gameeventmanager->LoadEventsFromFile("resource/gameevents.res");
  711. COM_TimestampedLog( "InitializeCvars" );
  712. // init the cvar list first in case inits want to reference them
  713. InitializeCvars();
  714. COM_TimestampedLog( "g_pParticleSystemMgr->Init" );
  715. // Initialize the particle system
  716. bool bPrecacheParticles = IsPC() && !engine->IsCreatingXboxReslist();
  717. if ( !g_pParticleSystemMgr->Init( g_pParticleSystemQuery, bPrecacheParticles ) )
  718. {
  719. return false;
  720. }
  721. sv_cheats = g_pCVar->FindVar( "sv_cheats" );
  722. if ( !sv_cheats )
  723. return false;
  724. g_pcv_commentary = g_pCVar->FindVar( "commentary" );
  725. g_pcv_ThreadMode = g_pCVar->FindVar( "host_thread_mode" );
  726. sv_maxreplay = g_pCVar->FindVar( "sv_maxreplay" );
  727. COM_TimestampedLog( "g_pGameSaveRestoreBlockSet" );
  728. g_pGameSaveRestoreBlockSet->AddBlockHandler( GetEntitySaveRestoreBlockHandler() );
  729. g_pGameSaveRestoreBlockSet->AddBlockHandler( GetPhysSaveRestoreBlockHandler() );
  730. g_pGameSaveRestoreBlockSet->AddBlockHandler( GetAISaveRestoreBlockHandler() );
  731. g_pGameSaveRestoreBlockSet->AddBlockHandler( GetTemplateSaveRestoreBlockHandler() );
  732. g_pGameSaveRestoreBlockSet->AddBlockHandler( GetDefaultResponseSystemSaveRestoreBlockHandler() );
  733. g_pGameSaveRestoreBlockSet->AddBlockHandler( GetCommentarySaveRestoreBlockHandler() );
  734. g_pGameSaveRestoreBlockSet->AddBlockHandler( GetEventQueueSaveRestoreBlockHandler() );
  735. g_pGameSaveRestoreBlockSet->AddBlockHandler( GetVScriptSaveRestoreBlockHandler() );
  736. #if defined( PORTAL2 )
  737. g_pGameSaveRestoreBlockSet->AddBlockHandler( GetPaintSaveRestoreBlockHandler() );
  738. #endif
  739. bool bInitSuccess = false;
  740. if ( sv_threaded_init.GetBool() )
  741. {
  742. CFunctorJob *pGameJob = new CFunctorJob( CreateFunctor( ParseParticleEffects, false ) );
  743. g_pThreadPool->AddJob( pGameJob );
  744. bInitSuccess = InitGameSystems( appSystemFactory );
  745. // FIXME: This method is a bit of a hack.
  746. // Try to update the screen every .06 seconds while waiting.
  747. float flLastUpdateTime = -1.0f;
  748. while( !pGameJob->IsFinished() )
  749. {
  750. float flTime = Plat_FloatTime();
  751. if ( flTime - flLastUpdateTime > .06f )
  752. {
  753. flLastUpdateTime = flTime;
  754. engine->RefreshScreenIfNecessary();
  755. }
  756. ThreadSleep( 0 );
  757. }
  758. pGameJob->Release();
  759. }
  760. else
  761. {
  762. COM_TimestampedLog( "ParseParticleEffects" );
  763. ParseParticleEffects( false );
  764. COM_TimestampedLog( "InitGameSystems - Start" );
  765. bInitSuccess = InitGameSystems( appSystemFactory );
  766. COM_TimestampedLog( "InitGameSystems - Finish" );
  767. }
  768. // try to get debug overlay, may be NULL if on HLDS
  769. debugoverlay = (IVDebugOverlay *)appSystemFactory( VDEBUG_OVERLAY_INTERFACE_VERSION, NULL );
  770. // init the gamestatsupload connection
  771. gamestatsuploader->InitConnection();
  772. #if defined( CSTRIKE15 )
  773. // Load the game types.
  774. g_pGameTypes->Initialize();
  775. #endif
  776. return true;
  777. }
  778. void CServerGameDLL::PostInit()
  779. {
  780. IGameSystem::PostInitAllSystems();
  781. Init_GCVs();
  782. #ifdef SERVER_USES_VGUI
  783. if ( !engine->IsDedicatedServer() && enginevgui )
  784. {
  785. if ( VGui_PostInit() )
  786. {
  787. // all good
  788. }
  789. }
  790. #endif // SERVER_USES_VGUI
  791. }
  792. void CServerGameDLL::PostToolsInit()
  793. {
  794. if ( serverenginetools )
  795. {
  796. serverfoundry = ( IServerFoundry * )serverenginetools->QueryInterface( VSERVERFOUNDRY_INTERFACE_VERSION );
  797. }
  798. }
  799. void CServerGameDLL::DLLShutdown( void )
  800. {
  801. // Due to dependencies, these are not autogamesystems
  802. ModelSoundsCacheShutdown();
  803. #ifdef PORTAL2
  804. g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetPaintSaveRestoreBlockHandler() );
  805. #endif
  806. g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetVScriptSaveRestoreBlockHandler() );
  807. g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetCommentarySaveRestoreBlockHandler() );
  808. g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetEventQueueSaveRestoreBlockHandler() );
  809. g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetDefaultResponseSystemSaveRestoreBlockHandler() );
  810. g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetTemplateSaveRestoreBlockHandler() );
  811. g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetAISaveRestoreBlockHandler() );
  812. g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetPhysSaveRestoreBlockHandler() );
  813. g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetEntitySaveRestoreBlockHandler() );
  814. g_pParticleSystemMgr->Shutdown();
  815. char *pFilename = g_TextStatsMgr.GetStatsFilename();
  816. if ( !pFilename || !pFilename[0] )
  817. {
  818. g_TextStatsMgr.SetStatsFilename( "stats.txt" );
  819. }
  820. g_TextStatsMgr.WriteFile( filesystem );
  821. IGameSystem::ShutdownAllSystems();
  822. #ifdef SERVER_USES_VGUI
  823. if ( enginevgui )
  824. {
  825. VGui_Shutdown();
  826. }
  827. #endif // SERVER_USES_VGUI
  828. #ifdef CSTRIKE_DLL // BOTPORT: TODO: move these ifdefs out
  829. RemoveBotControl();
  830. #endif
  831. // destroy the Navigation Mesh interface
  832. if (TheNavMesh)
  833. {
  834. delete TheNavMesh;
  835. TheNavMesh = NULL;
  836. }
  837. #if !defined(NO_STEAM)
  838. s_SteamAPIContext.Clear(); // Steam API context shutdown
  839. s_SteamGameServerAPIContext.Clear();
  840. // SteamAPI_Shutdown(); << Steam shutdown is controlled by engine
  841. #endif
  842. DisconnectTier3Libraries();
  843. DisconnectTier2Libraries();
  844. ConVar_Unregister();
  845. DisconnectTier1Libraries();
  846. STEAMWORKS_TERMCEGLIBRARY();
  847. }
  848. //-----------------------------------------------------------------------------
  849. // Purpose: See shareddefs.h for redefining this. Don't even think about it, though, for HL2. Or you will pay. ywb 9/22/03
  850. // Output : float
  851. //-----------------------------------------------------------------------------
  852. float CServerGameDLL::GetTickInterval( void ) const
  853. {
  854. float tickinterval = DEFAULT_TICK_INTERVAL;
  855. if ( engine->IsDedicatedServerForXbox() )
  856. tickinterval = DEFAULT_TICK_INTERVAL_X360;
  857. else if ( engine->IsDedicatedServerForPS3() )
  858. tickinterval = DEFAULT_TICK_INTERVAL_PS3;
  859. // Ignoring this for now, server ops are abusing it
  860. #if !defined( TF_DLL )
  861. // override if tick rate specified in command line
  862. if ( CommandLine()->CheckParm( "-tickrate" ) )
  863. {
  864. float tickrate = CommandLine()->ParmValue( "-tickrate", 0.0f );
  865. if ( tickrate > 0 )
  866. {
  867. float fDesiredTickInterval = 1.0f / tickrate;
  868. // quantize tick intervals to the nearest N / 512 fraction
  869. float fNumerator = floorf(fDesiredTickInterval * 512.0f + 0.5f);
  870. tickinterval = fNumerator / 512.0f;
  871. }
  872. tickinterval = clamp(tickinterval, MINIMUM_TICK_INTERVAL, MAXIMUM_TICK_INTERVAL);
  873. }
  874. #endif
  875. return tickinterval;
  876. }
  877. // This is called when a new game is started. (restart, map)
  878. CEG_NOINLINE bool CServerGameDLL::GameInit( void )
  879. {
  880. ResetGlobalState();
  881. const char* szConfigName = "game.cfg";
  882. if ( engine->IsDedicatedServerForXbox() || IsX360() )
  883. szConfigName = "game360.cfg";
  884. else if ( engine->IsDedicatedServerForPS3() || IsPS3() )
  885. szConfigName = "gamePS3.cfg";
  886. char szCommand[64];
  887. V_snprintf(szCommand, sizeof(szCommand), "exec %s\n", szConfigName);
  888. engine->ServerCommand( szCommand );
  889. engine->ServerExecute( );
  890. CBaseEntity::sm_bAccurateTriggerBboxChecks = true;
  891. CEG_PROTECT_VIRTUAL_FUNCTION( CServerGameDLL_GameInit );
  892. IGameEvent *event = gameeventmanager->CreateEvent( "game_init" );
  893. if ( event )
  894. {
  895. gameeventmanager->FireEvent( event );
  896. }
  897. return true;
  898. }
  899. // This is called when a game ends (server disconnect, death, restart, load)
  900. // NOT on level transitions within a game
  901. CEG_NOINLINE void CServerGameDLL::GameShutdown( void )
  902. {
  903. CEG_PROTECT_VIRTUAL_FUNCTION( CServerGameDLL_GameShutdown );
  904. ResetGlobalState();
  905. }
  906. static bool g_OneWayTransition = false;
  907. void Game_SetOneWayTransition( void )
  908. {
  909. g_OneWayTransition = true;
  910. }
  911. static CUtlVector<EHANDLE> g_RestoredEntities;
  912. // just for debugging, assert that this is the only time this function is called
  913. static bool g_InRestore = false;
  914. void AddRestoredEntity( CBaseEntity *pEntity )
  915. {
  916. Assert(g_InRestore);
  917. if ( !pEntity )
  918. return;
  919. g_RestoredEntities.AddToTail( pEntity );
  920. }
  921. void EndRestoreEntities()
  922. {
  923. if ( !g_InRestore )
  924. return;
  925. // The entire hierarchy is restored, so we can call GetAbsOrigin again.
  926. //CBaseEntity::SetAbsQueriesValid( true );
  927. // Call all entities' OnRestore handlers
  928. for ( int i = g_RestoredEntities.Count()-1; i >=0; --i )
  929. {
  930. CBaseEntity *pEntity = g_RestoredEntities[i].Get();
  931. if ( pEntity && !pEntity->IsDormant() )
  932. {
  933. MDLCACHE_CRITICAL_SECTION();
  934. pEntity->OnRestore();
  935. }
  936. }
  937. g_RestoredEntities.Purge();
  938. IGameSystem::OnRestoreAllSystems();
  939. g_InRestore = false;
  940. gEntList.CleanupDeleteList();
  941. // HACKHACK: UNDONE: We need to redesign the main loop with respect to save/load/server activate
  942. g_ServerGameDLL.ServerActivate( NULL, 0, 0 );
  943. CBaseEntity::SetAllowPrecache( false );
  944. }
  945. CEG_NOINLINE void BeginRestoreEntities()
  946. {
  947. if ( g_InRestore )
  948. {
  949. DevMsg( "BeginRestoreEntities without previous EndRestoreEntities.\n" );
  950. gEntList.CleanupDeleteList();
  951. }
  952. g_RestoredEntities.Purge();
  953. g_InRestore = true;
  954. CBaseEntity::SetAllowPrecache( true );
  955. // No calls to GetAbsOrigin until the entire hierarchy is restored!
  956. //CBaseEntity::SetAbsQueriesValid( false );
  957. }
  958. CEG_PROTECT_FUNCTION( BeginRestoreEntities );
  959. //-----------------------------------------------------------------------------
  960. // Purpose: This prevents sv.tickcount/gpGlobals->tickcount from advancing during restore which
  961. // would cause a lot of the NPCs to fast forward their think times to the same
  962. // tick due to some ticks being elapsed during restore where there was no simulation going on
  963. //-----------------------------------------------------------------------------
  964. bool CServerGameDLL::IsRestoring()
  965. {
  966. return g_InRestore;
  967. }
  968. bool CServerGameDLL::SupportsSaveRestore()
  969. {
  970. #ifdef INFESTED_DLL
  971. return false;
  972. #endif
  973. return true;
  974. }
  975. // Called any time a new level is started (after GameInit() also on level transitions within a game)
  976. CEG_NOINLINE bool CServerGameDLL::LevelInit( const char *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background )
  977. {
  978. VPROF("CServerGameDLL::LevelInit");
  979. ResetWindspeed();
  980. UpdateChapterRestrictions( pMapName );
  981. CEG_PROTECT_MEMBER_FUNCTION( CServerGameDLL_LevelInit );
  982. // IGameSystem::LevelInitPreEntityAllSystems() is called when the world is precached
  983. // That happens either in LoadGameState() or in MapEntity_ParseAllEntities()
  984. if ( loadGame )
  985. {
  986. if ( pOldLevel )
  987. {
  988. gpGlobals->eLoadType = MapLoad_Transition;
  989. }
  990. else
  991. {
  992. gpGlobals->eLoadType = MapLoad_LoadGame;
  993. }
  994. BeginRestoreEntities();
  995. if ( !engine->LoadGameState( pMapName, 1 ) )
  996. {
  997. if ( pOldLevel )
  998. {
  999. MapEntity_ParseAllEntities( pMapEntities );
  1000. }
  1001. else
  1002. {
  1003. // Regular save load case
  1004. return false;
  1005. }
  1006. }
  1007. STEAMWORKS_SELFCHECK();
  1008. if ( pOldLevel )
  1009. {
  1010. engine->LoadAdjacentEnts( pOldLevel, pLandmarkName );
  1011. }
  1012. #ifdef PORTAL
  1013. g_OneWayTransition = true;
  1014. #endif
  1015. if ( g_OneWayTransition )
  1016. {
  1017. engine->ClearSaveDirAfterClientLoad();
  1018. }
  1019. if ( pOldLevel && sv_autosave.GetBool() == true && gpGlobals->maxClients == 1 )
  1020. {
  1021. // This is a single-player style level transition.
  1022. // Queue up an autosave one second into the level
  1023. CBaseEntity *pAutosave = CBaseEntity::Create( "logic_autosave", vec3_origin, vec3_angle, NULL );
  1024. if ( pAutosave )
  1025. {
  1026. g_EventQueue.AddEvent( pAutosave, "Save", 1.0, NULL, NULL );
  1027. g_EventQueue.AddEvent( pAutosave, "Kill", 1.1, NULL, NULL );
  1028. }
  1029. }
  1030. }
  1031. else
  1032. {
  1033. if ( background )
  1034. {
  1035. gpGlobals->eLoadType = MapLoad_Background;
  1036. }
  1037. else
  1038. {
  1039. gpGlobals->eLoadType = MapLoad_NewGame;
  1040. }
  1041. STEAMWORKS_SELFCHECK();
  1042. // Clear out entity references, and parse the entities into it.
  1043. g_MapEntityRefs.Purge();
  1044. CMapLoadEntityFilter filter;
  1045. MapEntity_ParseAllEntities( pMapEntities, &filter );
  1046. g_pServerBenchmark->StartBenchmark();
  1047. // Now call the mod specific parse
  1048. LevelInit_ParseAllEntities( pMapEntities );
  1049. }
  1050. // Check low violence settings for this map
  1051. g_RagdollLVManager.SetLowViolence( pMapName );
  1052. // Now that all of the active entities have been loaded in, precache any entities who need point_template parameters
  1053. // to be parsed (the above code has loaded all point_template entities)
  1054. PrecachePointTemplates();
  1055. STEAMWORKS_TESTSECRET();
  1056. // load MOTD from file into stringtable
  1057. LoadMessageOfTheDay();
  1058. // Sometimes an ent will Remove() itself during its precache, so RemoveImmediate won't happen.
  1059. // This makes sure those ents get cleaned up.
  1060. gEntList.CleanupDeleteList();
  1061. g_AIFriendliesTalkSemaphore.Release();
  1062. g_AIFoesTalkSemaphore.Release();
  1063. g_OneWayTransition = false;
  1064. // clear any pending autosavedangerous
  1065. m_fAutoSaveDangerousTime = 0.0f;
  1066. m_fAutoSaveDangerousMinHealthToCommit = 0.0f;
  1067. // ask for the latest game rules
  1068. GameRules()->UpdateGameplayStatsFromSteam();
  1069. if ( gpGlobals->eLoadType == MapLoad_Transition )
  1070. {
  1071. static ConVarRef map_wants_save_disable( "map_wants_save_disable" );
  1072. map_wants_save_disable.SetValue( 0 );
  1073. }
  1074. return true;
  1075. }
  1076. //-----------------------------------------------------------------------------
  1077. // Purpose: called after every level change and load game, iterates through all the
  1078. // active entities and gives them a chance to fix up their state
  1079. //-----------------------------------------------------------------------------
  1080. #ifdef DEBUG
  1081. bool g_bReceivedChainedActivate;
  1082. bool g_bCheckForChainedActivate;
  1083. #define BeginCheckChainedActivate() if (0) ; else { g_bCheckForChainedActivate = true; g_bReceivedChainedActivate = false; }
  1084. #define EndCheckChainedActivate( bCheck ) \
  1085. if (0) ; else \
  1086. { \
  1087. if ( bCheck ) \
  1088. { \
  1089. char msg[ 1024 ]; \
  1090. Q_snprintf( msg, sizeof( msg ), "Entity (%i/%s/%s) failed to call base class Activate()\n", pClass->entindex(), pClass->GetClassname(), STRING( pClass->GetEntityName() ) ); \
  1091. AssertMsg( g_bReceivedChainedActivate == true, msg ); \
  1092. } \
  1093. g_bCheckForChainedActivate = false; \
  1094. }
  1095. #else
  1096. #define BeginCheckChainedActivate() ((void)0)
  1097. #define EndCheckChainedActivate( bCheck ) ((void)0)
  1098. #endif
  1099. CEG_NOINLINE void CServerGameDLL::ServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
  1100. {
  1101. // HACKHACK: UNDONE: We need to redesign the main loop with respect to save/load/server activate
  1102. if ( g_InRestore )
  1103. return;
  1104. if ( gEntList.ResetDeleteList() != 0 )
  1105. {
  1106. Msg( "ERROR: Entity delete queue not empty on level start!\n" );
  1107. }
  1108. for ( CBaseEntity *pClass = gEntList.FirstEnt(); pClass != NULL; pClass = gEntList.NextEnt(pClass) )
  1109. {
  1110. if ( pClass && !pClass->IsDormant() )
  1111. {
  1112. MDLCACHE_CRITICAL_SECTION();
  1113. BeginCheckChainedActivate();
  1114. pClass->Activate();
  1115. // We don't care if it finished activating if it decided to remove itself.
  1116. EndCheckChainedActivate( !( pClass->GetEFlags() & EFL_KILLME ) );
  1117. }
  1118. }
  1119. CEG_PROTECT_VIRTUAL_FUNCTION( CServerGameDLL_ServerActivate );
  1120. IGameSystem::LevelInitPostEntityAllSystems();
  1121. // No more precaching after PostEntityAllSystems!!!
  1122. CBaseEntity::SetAllowPrecache( false );
  1123. // only display the think limit when the game is run with "developer" mode set
  1124. if ( !g_pDeveloper->GetInt() )
  1125. {
  1126. think_limit.SetValue( 0 );
  1127. }
  1128. #ifndef _XBOX
  1129. // load the Navigation Mesh for this map
  1130. TheNavMesh->Load();
  1131. TheNavMesh->OnServerActivate();
  1132. #endif
  1133. #if defined( CSTRIKE_DLL ) // BOTPORT: TODO: move these ifdefs out
  1134. TheBots->ServerActivate();
  1135. #endif
  1136. }
  1137. //-----------------------------------------------------------------------------
  1138. // Purpose: Called after the steam API has been activated post-level startup
  1139. //-----------------------------------------------------------------------------
  1140. void CServerGameDLL::GameServerSteamAPIActivated( bool bActive )
  1141. {
  1142. if ( bActive )
  1143. {
  1144. #if !defined( NO_STEAM )
  1145. steamgameserverapicontext->Init();
  1146. #endif
  1147. #if !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
  1148. GCClientSystem()->Activate();
  1149. #endif //!defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
  1150. }
  1151. else
  1152. {
  1153. #if !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
  1154. GCClientSystem()->Shutdown();
  1155. #endif //!defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
  1156. #if !defined( NO_STEAM )
  1157. steamgameserverapicontext->Clear();
  1158. #endif
  1159. }
  1160. }
  1161. //-----------------------------------------------------------------------------
  1162. // Purpose: Called at the start of every game frame
  1163. //-----------------------------------------------------------------------------
  1164. ConVar trace_report( "trace_report", "0" );
  1165. void CServerGameDLL::GameFrame( bool simulating )
  1166. {
  1167. VPROF( "CServerGameDLL::GameFrame" );
  1168. SNPROF( "CServerGameDLL::GameFrame" );
  1169. // Don't run frames until fully restored
  1170. if ( g_InRestore )
  1171. return;
  1172. #ifndef NO_STEAM
  1173. // All the calls to us from the engine prior to gameframe (like LevelInit & ServerActivate)
  1174. // are done before the engine has got the Steam API connected, so we have to wait until now to connect ourselves.
  1175. if ( Steam3Server().CheckInitialized() )
  1176. {
  1177. GameRules()->UpdateGameplayStatsFromSteam();
  1178. }
  1179. #endif
  1180. g_bIsLogging = engine->IsLogEnabled();
  1181. if ( CBaseEntity::IsSimulatingOnAlternateTicks() )
  1182. {
  1183. // only run simulation on even numbered ticks
  1184. if ( gpGlobals->tickcount & 1 )
  1185. {
  1186. UpdateAllClientData();
  1187. return;
  1188. }
  1189. // If we're skipping frames, then the frametime is 2x the normal tick
  1190. gpGlobals->frametime *= 2.0f;
  1191. }
  1192. float oldframetime = gpGlobals->frametime;
  1193. #ifdef _DEBUG
  1194. // For profiling.. let them enable/disable the networkvar manual mode stuff.
  1195. g_bUseNetworkVars = s_UseNetworkVars.GetBool();
  1196. #endif
  1197. extern void GameStartFrame( void );
  1198. extern void ServiceEventQueue( void );
  1199. extern void Physics_RunThinkFunctions( bool simulating );
  1200. // Delete anything that was marked for deletion
  1201. // outside of server frameloop (e.g., in response to concommand)
  1202. gEntList.CleanupDeleteList();
  1203. HandleFoundryEntitySpawnRecords();
  1204. {
  1205. MDLCACHE_CRITICAL_SECTION();
  1206. IGameSystem::FrameUpdatePreEntityThinkAllSystems();
  1207. GameStartFrame();
  1208. TheNavMesh->Update();
  1209. {
  1210. VPROF( "gamestatsuploader->UpdateConnection" );
  1211. SNPROF( "gamestatsuploader->UpdateConnection" );
  1212. gamestatsuploader->UpdateConnection();
  1213. }
  1214. {
  1215. VPROF( "UpdateQueryCache" );
  1216. SNPROF( "UpdateQueryCache" );
  1217. UpdateQueryCache();
  1218. }
  1219. {
  1220. VPROF( "g_pServerBenchmark->UpdateBenchmark" );
  1221. SNPROF( "g_pServerBenchmark->UpdateBenchmark" );
  1222. g_pServerBenchmark->UpdateBenchmark();
  1223. }
  1224. Physics_RunThinkFunctions( simulating );
  1225. IGameSystem::FrameUpdatePostEntityThinkAllSystems();
  1226. // UNDONE: Make these systems IGameSystems and move these calls into FrameUpdatePostEntityThink()
  1227. // service event queue, firing off any actions whos time has come
  1228. ServiceEventQueue();
  1229. }
  1230. // free all ents marked in think functions
  1231. gEntList.CleanupDeleteList();
  1232. // FIXME: Should this only occur on the final tick?
  1233. UpdateAllClientData();
  1234. if ( g_pGameRules )
  1235. {
  1236. VPROF( "g_pGameRules->EndGameFrame" );
  1237. SNPROF( "g_pGameRules->EndGameFrame" );
  1238. g_pGameRules->EndGameFrame();
  1239. }
  1240. if ( trace_report.GetBool() )
  1241. {
  1242. int total = 0, totals[3];
  1243. for ( int i = 0; i < 3; i++ )
  1244. {
  1245. totals[i] = enginetrace->GetStatByIndex( i, true );
  1246. if ( totals[i] > 0 )
  1247. {
  1248. total += totals[i];
  1249. }
  1250. }
  1251. if ( total )
  1252. {
  1253. Msg("Trace: %d, contents %d, enumerate %d\n", totals[0], totals[1], totals[2] );
  1254. }
  1255. }
  1256. // Any entities that detect network state changes on a timer do it here.
  1257. g_NetworkPropertyEventMgr.FireEvents();
  1258. gpGlobals->frametime = oldframetime;
  1259. }
  1260. //-----------------------------------------------------------------------------
  1261. // Purpose: Called every frame even if not ticking
  1262. // Input : simulating -
  1263. //-----------------------------------------------------------------------------
  1264. void CServerGameDLL::PreClientUpdate( bool simulating )
  1265. {
  1266. if ( !simulating )
  1267. return;
  1268. /*
  1269. if (game_speeds.GetInt())
  1270. {
  1271. DrawMeasuredSections();
  1272. }
  1273. */
  1274. //#ifdef _DEBUG - allow this in release for now
  1275. DrawAllDebugOverlays();
  1276. //#endif
  1277. IGameSystem::PreClientUpdateAllSystems();
  1278. #ifdef _DEBUG
  1279. if ( sv_showhitboxes.GetInt() == -1 )
  1280. return;
  1281. if ( sv_showhitboxes.GetInt() == 0 )
  1282. {
  1283. // assume it's text
  1284. CBaseEntity *pEntity = NULL;
  1285. while (1)
  1286. {
  1287. pEntity = gEntList.FindEntityByName( pEntity, sv_showhitboxes.GetString() );
  1288. if ( !pEntity )
  1289. break;
  1290. CBaseAnimating *anim = dynamic_cast< CBaseAnimating * >( pEntity );
  1291. if (anim)
  1292. {
  1293. anim->DrawServerHitboxes();
  1294. }
  1295. }
  1296. return;
  1297. }
  1298. CBaseAnimating *anim = dynamic_cast< CBaseAnimating * >( CBaseEntity::Instance( INDEXENT( sv_showhitboxes.GetInt() ) ) );
  1299. if ( !anim )
  1300. return;
  1301. anim->DrawServerHitboxes();
  1302. #endif
  1303. }
  1304. void CServerGameDLL::Think( bool finalTick )
  1305. {
  1306. if ( m_fAutoSaveDangerousTime != 0.0f && m_fAutoSaveDangerousTime < gpGlobals->curtime )
  1307. {
  1308. // The safety timer for a dangerous auto save has expired
  1309. CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
  1310. if ( pPlayer && ( pPlayer->GetDeathTime() == 0.0f || pPlayer->GetDeathTime() > gpGlobals->curtime )
  1311. && !pPlayer->IsSinglePlayerGameEnding()
  1312. )
  1313. {
  1314. if( pPlayer->GetHealth() >= m_fAutoSaveDangerousMinHealthToCommit )
  1315. {
  1316. // The player isn't dead, so make the dangerous auto save safe
  1317. engine->ServerCommand( "autosavedangerousissafe\n" );
  1318. }
  1319. }
  1320. m_fAutoSaveDangerousTime = 0.0f;
  1321. m_fAutoSaveDangerousMinHealthToCommit = 0.0f;
  1322. }
  1323. // TODO: would have liked this to be totally event driven... currently needs a tick.
  1324. if ( engine->IsDedicatedServer() && steamgameserverapicontext->SteamHTTP() )
  1325. {
  1326. DedicatedServerWorkshop().Update();
  1327. }
  1328. }
  1329. void CServerGameDLL::OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue )
  1330. {
  1331. }
  1332. // Called when a level is shutdown (including changing levels)
  1333. void CServerGameDLL::LevelShutdown( void )
  1334. {
  1335. MDLCACHE_CRITICAL_SECTION();
  1336. IGameSystem::LevelShutdownPreEntityAllSystems();
  1337. // YWB:
  1338. // This entity pointer is going away now and is corrupting memory on level transitions/restarts
  1339. CSoundEnt::ShutdownSoundEnt();
  1340. ClearDebugHistory();
  1341. gEntList.Clear();
  1342. InvalidateQueryCache();
  1343. STEAMWORKS_SELFCHECK();
  1344. IGameSystem::LevelShutdownPostEntityAllSystems();
  1345. // In case we quit out during initial load
  1346. CBaseEntity::SetAllowPrecache( false );
  1347. // reset the Navigation Mesh
  1348. if ( TheNavMesh )
  1349. {
  1350. TheNavMesh->Reset();
  1351. }
  1352. g_nCurrentChapterIndex = -1;
  1353. CStudioHdr::CActivityToSequenceMapping::ResetMappings();
  1354. }
  1355. //-----------------------------------------------------------------------------
  1356. // Purpose:
  1357. // Input :
  1358. // Output : ServerClass*
  1359. //-----------------------------------------------------------------------------
  1360. ServerClass* CServerGameDLL::GetAllServerClasses()
  1361. {
  1362. return g_pServerClassHead;
  1363. }
  1364. const char *CServerGameDLL::GetGameDescription( void )
  1365. {
  1366. return ::GetGameDescription();
  1367. }
  1368. CEG_NOINLINE void CServerGameDLL::CreateNetworkStringTables( void )
  1369. {
  1370. CEG_PROTECT_VIRTUAL_FUNCTION( CServerGameDLL_CreateNetworksStringTables );
  1371. // Create any shared string tables here (and only here!)
  1372. // E.g.: xxx = networkstringtable->CreateStringTable( "SceneStrings", 512 );
  1373. g_pStringTableExtraParticleFiles = networkstringtable->CreateStringTable( "ExtraParticleFilesTable", MAX_PARTICLESYSTEMS_STRINGS, 0, 0, NSF_DICTIONARY_ENABLED );
  1374. g_pStringTableParticleEffectNames = networkstringtable->CreateStringTable( "ParticleEffectNames", MAX_PARTICLESYSTEMS_STRINGS, 0, 0, NSF_DICTIONARY_ENABLED );
  1375. g_pStringTableEffectDispatch = networkstringtable->CreateStringTable( "EffectDispatch", MAX_EFFECT_DISPATCH_STRINGS );
  1376. g_pStringTableVguiScreen = networkstringtable->CreateStringTable( "VguiScreen", MAX_VGUI_SCREEN_STRINGS );
  1377. g_pStringTableMaterials = networkstringtable->CreateStringTable( "Materials", MAX_MATERIAL_STRINGS, 0, 0, NSF_DICTIONARY_ENABLED );
  1378. g_pStringTableInfoPanel = networkstringtable->CreateStringTable( "InfoPanel", MAX_INFOPANEL_STRINGS );
  1379. g_pStringTableClientSideChoreoScenes = networkstringtable->CreateStringTable( "Scenes", MAX_CHOREO_SCENES_STRINGS, 0, 0, NSF_DICTIONARY_ENABLED );
  1380. g_pStringTableMovies = networkstringtable->CreateStringTable( "Movies", MAX_MOVIE_STRINGS, 0, 0, NSF_DICTIONARY_ENABLED );
  1381. Assert( g_pStringTableParticleEffectNames &&
  1382. g_pStringTableEffectDispatch &&
  1383. g_pStringTableVguiScreen &&
  1384. g_pStringTableMaterials &&
  1385. g_pStringTableInfoPanel &&
  1386. g_pStringTableClientSideChoreoScenes &&
  1387. g_pStringTableExtraParticleFiles &&
  1388. g_pStringTableMovies );
  1389. // Need this so we have the error material always handy
  1390. PrecacheMaterial( "debug/debugempty" );
  1391. Assert( GetMaterialIndex( "debug/debugempty" ) == 0 );
  1392. PrecacheParticleSystem( "error" ); // ensure error particle system is handy
  1393. Assert( GetParticleSystemIndex( "error" ) == 0 );
  1394. PrecacheEffect( "error" ); // ensure error effect is handy
  1395. Assert( GetEffectIndex( "error" ) == 0 );
  1396. CreateNetworkStringTables_GameRules();
  1397. // Set up save/load utilities for string tables
  1398. g_VguiScreenStringOps.Init( g_pStringTableVguiScreen );
  1399. }
  1400. CSaveRestoreData *CServerGameDLL::SaveInit( int size )
  1401. {
  1402. return ::SaveInit(size);
  1403. }
  1404. //-----------------------------------------------------------------------------
  1405. // Purpose: Saves data from a struct into a saverestore object, to be saved to disk
  1406. // Input : *pSaveData - the saverestore object
  1407. // char *pname - the name of the data to write
  1408. // *pBaseData - the struct into which the data is to be read
  1409. // *pFields - pointer to an array of data field descriptions
  1410. // fieldCount - the size of the array (number of field descriptions)
  1411. //-----------------------------------------------------------------------------
  1412. void CServerGameDLL::SaveWriteFields( CSaveRestoreData *pSaveData, const char *pname, void *pBaseData, datamap_t *pMap, typedescription_t *pFields, int fieldCount )
  1413. {
  1414. CSave saveHelper( pSaveData );
  1415. saveHelper.WriteFields( pname, pBaseData, pMap, pFields, fieldCount );
  1416. }
  1417. //-----------------------------------------------------------------------------
  1418. // Purpose: Reads data from a save/restore block into a structure
  1419. // Input : *pSaveData - the saverestore object
  1420. // char *pname - the name of the data to extract from
  1421. // *pBaseData - the struct into which the data is to be restored
  1422. // *pFields - pointer to an array of data field descriptions
  1423. // fieldCount - the size of the array (number of field descriptions)
  1424. //-----------------------------------------------------------------------------
  1425. //-----------------------------------------------------------------------------
  1426. void CServerGameDLL::SaveReadFields( CSaveRestoreData *pSaveData, const char *pname, void *pBaseData, datamap_t *pMap, typedescription_t *pFields, int fieldCount )
  1427. {
  1428. CRestore restoreHelper( pSaveData );
  1429. restoreHelper.ReadFields( pname, pBaseData, pMap, pFields, fieldCount );
  1430. }
  1431. //-----------------------------------------------------------------------------
  1432. void CServerGameDLL::SaveGlobalState( CSaveRestoreData *s )
  1433. {
  1434. ::SaveGlobalState(s);
  1435. }
  1436. void CServerGameDLL::RestoreGlobalState(CSaveRestoreData *s)
  1437. {
  1438. ::RestoreGlobalState(s);
  1439. }
  1440. void CServerGameDLL::Save( CSaveRestoreData *s )
  1441. {
  1442. CSave saveHelper( s );
  1443. g_pGameSaveRestoreBlockSet->Save( &saveHelper );
  1444. }
  1445. CEG_NOINLINE void CServerGameDLL::Restore( CSaveRestoreData *s, bool b)
  1446. {
  1447. if ( engine->IsOverrideLoadGameEntsOn() )
  1448. FoundryHelpers_ClearEntityHighlightEffects();
  1449. CEG_PROTECT_VIRTUAL_FUNCTION( CServerGameDLL_Restore );
  1450. CRestore restore(s);
  1451. g_pGameSaveRestoreBlockSet->Restore( &restore, b );
  1452. g_pGameSaveRestoreBlockSet->PostRestore();
  1453. if ( serverfoundry && engine->IsOverrideLoadGameEntsOn() )
  1454. serverfoundry->OnFinishedRestoreSavegame();
  1455. }
  1456. //-----------------------------------------------------------------------------
  1457. // Purpose:
  1458. // Input : msg_type -
  1459. // *name -
  1460. // size -
  1461. // Output : Returns true on success, false on failure.
  1462. //-----------------------------------------------------------------------------
  1463. CStandardSendProxies* CServerGameDLL::GetStandardSendProxies()
  1464. {
  1465. return &g_StandardSendProxies;
  1466. }
  1467. int CServerGameDLL::CreateEntityTransitionList( CSaveRestoreData *s, int a)
  1468. {
  1469. CRestore restoreHelper( s );
  1470. // save off file base
  1471. int base = restoreHelper.GetReadPos();
  1472. int movedCount = ::CreateEntityTransitionList(s, a);
  1473. if ( movedCount )
  1474. {
  1475. g_pGameSaveRestoreBlockSet->CallBlockHandlerRestore( GetPhysSaveRestoreBlockHandler(), base, &restoreHelper, false );
  1476. g_pGameSaveRestoreBlockSet->CallBlockHandlerRestore( GetAISaveRestoreBlockHandler(), base, &restoreHelper, false );
  1477. }
  1478. GetPhysSaveRestoreBlockHandler()->PostRestore();
  1479. GetAISaveRestoreBlockHandler()->PostRestore();
  1480. return movedCount;
  1481. }
  1482. void CServerGameDLL::PreSave( CSaveRestoreData *s )
  1483. {
  1484. g_pGameSaveRestoreBlockSet->PreSave( s );
  1485. }
  1486. #include "client_textmessage.h"
  1487. // This little hack lets me marry BSP names to messages in titles.txt
  1488. typedef struct
  1489. {
  1490. char *pBSPName;
  1491. char *pTitleName;
  1492. } TITLECOMMENT;
  1493. // this list gets searched for the first partial match, so some are out of order
  1494. static TITLECOMMENT gTitleComments[] =
  1495. {
  1496. #ifdef PORTAL
  1497. { "testchmb_a_00", "#Portal_Chapter1_Title" },
  1498. { "testchmb_a_01", "#Portal_Chapter1_Title" },
  1499. { "testchmb_a_02", "#Portal_Chapter2_Title" },
  1500. { "testchmb_a_03", "#Portal_Chapter2_Title" },
  1501. { "testchmb_a_04", "#Portal_Chapter3_Title" },
  1502. { "testchmb_a_05", "#Portal_Chapter3_Title" },
  1503. { "testchmb_a_06", "#Portal_Chapter4_Title" },
  1504. { "testchmb_a_07", "#Portal_Chapter4_Title" },
  1505. { "testchmb_a_08_advanced", "#Portal_Chapter5_Title" },
  1506. { "testchmb_a_08", "#Portal_Chapter5_Title" },
  1507. { "testchmb_a_09_advanced", "#Portal_Chapter6_Title" },
  1508. { "testchmb_a_09", "#Portal_Chapter6_Title" },
  1509. { "testchmb_a_10_advanced", "#Portal_Chapter7_Title" },
  1510. { "testchmb_a_10", "#Portal_Chapter7_Title" },
  1511. { "testchmb_a_11_advanced", "#Portal_Chapter8_Title" },
  1512. { "testchmb_a_11", "#Portal_Chapter8_Title" },
  1513. { "testchmb_a_13_advanced", "#Portal_Chapter9_Title" },
  1514. { "testchmb_a_13", "#Portal_Chapter9_Title" },
  1515. { "testchmb_a_14_advanced", "#Portal_Chapter10_Title" },
  1516. { "testchmb_a_14", "#Portal_Chapter10_Title" },
  1517. { "testchmb_a_15", "#Portal_Chapter11_Title" },
  1518. { "escape_", "#Portal_Chapter11_Title" },
  1519. { "background2", "#Portal_Chapter12_Title" },
  1520. #else
  1521. { "intro", "#HL2_Chapter1_Title" },
  1522. { "d1_trainstation_05", "#HL2_Chapter2_Title" },
  1523. { "d1_trainstation_06", "#HL2_Chapter2_Title" },
  1524. { "d1_trainstation_", "#HL2_Chapter1_Title" },
  1525. { "d1_canals_06", "#HL2_Chapter4_Title" },
  1526. { "d1_canals_07", "#HL2_Chapter4_Title" },
  1527. { "d1_canals_08", "#HL2_Chapter4_Title" },
  1528. { "d1_canals_09", "#HL2_Chapter4_Title" },
  1529. { "d1_canals_1", "#HL2_Chapter4_Title" },
  1530. { "d1_canals_0", "#HL2_Chapter3_Title" },
  1531. { "d1_eli_", "#HL2_Chapter5_Title" },
  1532. { "d1_town_", "#HL2_Chapter6_Title" },
  1533. { "d2_coast_09", "#HL2_Chapter8_Title" },
  1534. { "d2_coast_1", "#HL2_Chapter8_Title" },
  1535. { "d2_prison_01", "#HL2_Chapter8_Title" },
  1536. { "d2_coast_", "#HL2_Chapter7_Title" },
  1537. { "d2_prison_06", "#HL2_Chapter9a_Title" },
  1538. { "d2_prison_07", "#HL2_Chapter9a_Title" },
  1539. { "d2_prison_08", "#HL2_Chapter9a_Title" },
  1540. { "d2_prison_", "#HL2_Chapter9_Title" },
  1541. { "d3_c17_01", "#HL2_Chapter9a_Title" },
  1542. { "d3_c17_09", "#HL2_Chapter11_Title" },
  1543. { "d3_c17_1", "#HL2_Chapter11_Title" },
  1544. { "d3_c17_", "#HL2_Chapter10_Title" },
  1545. { "d3_citadel_", "#HL2_Chapter12_Title" },
  1546. { "d3_breen_", "#HL2_Chapter13_Title" },
  1547. { "credits", "#HL2_Chapter14_Title" },
  1548. { "ep1_citadel_00", "#episodic_Chapter1_Title" },
  1549. { "ep1_citadel_01", "#episodic_Chapter1_Title" },
  1550. { "ep1_citadel_02b", "#episodic_Chapter1_Title" },
  1551. { "ep1_citadel_02", "#episodic_Chapter1_Title" },
  1552. { "ep1_citadel_03", "#episodic_Chapter2_Title" },
  1553. { "ep1_citadel_04", "#episodic_Chapter2_Title" },
  1554. { "ep1_c17_00a", "#episodic_Chapter3_Title" },
  1555. { "ep1_c17_00", "#episodic_Chapter3_Title" },
  1556. { "ep1_c17_01", "#episodic_Chapter4_Title" },
  1557. { "ep1_c17_02b", "#episodic_Chapter4_Title" },
  1558. { "ep1_c17_02", "#episodic_Chapter4_Title" },
  1559. { "ep1_c17_05", "#episodic_Chapter5_Title" },
  1560. { "ep1_c17_06", "#episodic_Chapter5_Title" },
  1561. { "ep2_outland_01a", "#ep2_Chapter1_Title" },
  1562. { "ep2_outland_01", "#ep2_Chapter1_Title" },
  1563. { "ep2_outland_02", "#ep2_Chapter2_Title" },
  1564. { "ep2_outland_03", "#ep2_Chapter2_Title" },
  1565. { "ep2_outland_04", "#ep2_Chapter2_Title" },
  1566. { "ep2_outland_05", "#ep2_Chapter3_Title" },
  1567. { "ep2_outland_06a", "#ep2_Chapter4_Title" },
  1568. { "ep2_outland_06", "#ep2_Chapter3_Title" },
  1569. { "ep2_outland_07", "#ep2_Chapter4_Title" },
  1570. { "ep2_outland_08", "#ep2_Chapter4_Title" },
  1571. { "ep2_outland_09", "#ep2_Chapter5_Title" },
  1572. { "ep2_outland_10a", "#ep2_Chapter5_Title" },
  1573. { "ep2_outland_10", "#ep2_Chapter5_Title" },
  1574. { "ep2_outland_11a", "#ep2_Chapter6_Title" },
  1575. { "ep2_outland_11", "#ep2_Chapter6_Title" },
  1576. { "ep2_outland_12a", "#ep2_Chapter7_Title" },
  1577. { "ep2_outland_12", "#ep2_Chapter6_Title" },
  1578. #endif
  1579. };
  1580. void CServerGameDLL::GetSaveComment( char *text, int maxlength, float flMinutes, float flSeconds, bool bNoTime )
  1581. {
  1582. char comment[64];
  1583. const char *pName;
  1584. int i;
  1585. char const *mapname = STRING( gpGlobals->mapname );
  1586. pName = NULL;
  1587. // Try to find a matching title comment for this mapname
  1588. for ( i = 0; i < ARRAYSIZE(gTitleComments) && !pName; i++ )
  1589. {
  1590. if ( !Q_strnicmp( mapname, gTitleComments[i].pBSPName, strlen(gTitleComments[i].pBSPName) ) )
  1591. {
  1592. // found one
  1593. int j;
  1594. // Got a message, post-process it to be save name friendly
  1595. Q_strncpy( comment, gTitleComments[i].pTitleName, sizeof( comment ) );
  1596. pName = comment;
  1597. j = 0;
  1598. // Strip out CRs
  1599. while ( j < 64 && comment[j] )
  1600. {
  1601. if ( comment[j] == '\n' || comment[j] == '\r' )
  1602. comment[j] = 0;
  1603. else
  1604. j++;
  1605. }
  1606. break;
  1607. }
  1608. }
  1609. // If we didn't get one, use the designer's map name, or the BSP name itself
  1610. if ( !pName )
  1611. {
  1612. pName = mapname;
  1613. }
  1614. if ( bNoTime )
  1615. {
  1616. Q_snprintf( text, maxlength, "%-64.64s", pName );
  1617. }
  1618. else
  1619. {
  1620. int minutes = flMinutes;
  1621. int seconds = flSeconds;
  1622. // Wow, this guy/gal must suck...!
  1623. if ( minutes >= 1000 )
  1624. {
  1625. minutes = 999;
  1626. seconds = 59;
  1627. }
  1628. int minutesAdd = ( seconds / 60 );
  1629. seconds %= 60;
  1630. // add the elapsed time at the end of the comment, for the ui to parse out
  1631. Q_snprintf( text, maxlength, "%-64.64s %03d:%02d", pName, (minutes + minutesAdd), seconds );
  1632. }
  1633. }
  1634. void CServerGameDLL::WriteSaveHeaders( CSaveRestoreData *s )
  1635. {
  1636. CSave saveHelper( s );
  1637. g_pGameSaveRestoreBlockSet->WriteSaveHeaders( &saveHelper );
  1638. g_pGameSaveRestoreBlockSet->PostSave();
  1639. }
  1640. CEG_NOINLINE void CServerGameDLL::ReadRestoreHeaders( CSaveRestoreData *s )
  1641. {
  1642. CRestore restoreHelper( s );
  1643. g_pGameSaveRestoreBlockSet->PreRestore();
  1644. CEG_PROTECT_VIRTUAL_FUNCTION( CServerGameDLL_ReadRestoreHeaders );
  1645. g_pGameSaveRestoreBlockSet->ReadRestoreHeaders( &restoreHelper );
  1646. }
  1647. void CServerGameDLL::PreSaveGameLoaded( char const *pSaveName, bool bInGame )
  1648. {
  1649. #ifndef _GAMECONSOLE
  1650. gamestats->Event_PreSaveGameLoaded( pSaveName, bInGame );
  1651. #endif
  1652. }
  1653. //-----------------------------------------------------------------------------
  1654. // Purpose: Returns true if the game DLL wants the server not to be made public.
  1655. // Used by commentary system to hide multiplayer commentary servers from the master.
  1656. //-----------------------------------------------------------------------------
  1657. bool CServerGameDLL::ShouldHideServer( void )
  1658. {
  1659. if ( g_pcv_commentary && g_pcv_commentary->GetBool() )
  1660. return true;
  1661. if ( gpGlobals->eLoadType == MapLoad_Background )
  1662. return true;
  1663. return false;
  1664. }
  1665. //-----------------------------------------------------------------------------
  1666. //
  1667. //-----------------------------------------------------------------------------
  1668. void CServerGameDLL::InvalidateMdlCache()
  1669. {
  1670. CBaseAnimating *pAnimating;
  1671. for ( CBaseEntity *pEntity = gEntList.FirstEnt(); pEntity != NULL; pEntity = gEntList.NextEnt(pEntity) )
  1672. {
  1673. pAnimating = dynamic_cast<CBaseAnimating *>(pEntity);
  1674. if ( pAnimating )
  1675. {
  1676. pAnimating->InvalidateMdlCache();
  1677. }
  1678. }
  1679. CStudioHdr::CActivityToSequenceMapping::ResetMappings();
  1680. }
  1681. static KeyValues * FindLaunchOptionByValue( KeyValues *pLaunchOptions, char const *szLaunchOption )
  1682. {
  1683. if ( !pLaunchOptions || !szLaunchOption || !*szLaunchOption )
  1684. return NULL;
  1685. for ( KeyValues *val = pLaunchOptions->GetFirstSubKey(); val; val = val->GetNextKey() )
  1686. {
  1687. char const *szValue = val->GetString();
  1688. if ( szValue && *szValue && !Q_stricmp( szValue, szLaunchOption ) )
  1689. return val;
  1690. }
  1691. return NULL;
  1692. }
  1693. bool CServerGameDLL::ShouldPreferSteamAuth()
  1694. {
  1695. return true;
  1696. }
  1697. bool CServerGameDLL::ShouldAllowDirectConnect()
  1698. {
  1699. bool bAllowDC = true;
  1700. DevMsg( "Not Valve ds: Direct-connect is allowed\n");
  1701. return bAllowDC;
  1702. }
  1703. bool CServerGameDLL::FriendsReqdForDirectConnect()
  1704. {
  1705. return sv_dc_friends_reqd.GetBool();
  1706. }
  1707. bool CServerGameDLL::IsLoadTestServer()
  1708. {
  1709. if ( CommandLine()->FindParm( "-sv_load_test" ) )
  1710. {
  1711. return true;
  1712. }
  1713. return false;
  1714. }
  1715. bool CServerGameDLL::IsValveDS()
  1716. {
  1717. return IsValveDedicated();
  1718. }
  1719. KeyValues* CServerGameDLL::GetExtendedServerInfoForNewClient()
  1720. {
  1721. static KeyValues *s_pExtendedServerInfo = NULL;
  1722. static char s_szExtendedHashKey[256] = {0};
  1723. int iGameType = g_pGameTypes->GetCurrentGameType();
  1724. int iGameMode = g_pGameTypes->GetCurrentGameMode();
  1725. int nNumSlots = g_pGameTypes->GetMaxPlayersForTypeAndMode( iGameType, iGameMode );
  1726. uint32 uiOfficialReservationGameType = 0;
  1727. char szHashKey[256] = {0};
  1728. V_snprintf( szHashKey, sizeof( szHashKey ), "%u:%d:%d:%d:%s", uiOfficialReservationGameType, iGameType, iGameMode, nNumSlots, gpGlobals->mapname.ToCStr() );
  1729. if ( Q_strcmp( s_szExtendedHashKey, szHashKey ) )
  1730. {
  1731. V_strncpy( s_szExtendedHashKey, szHashKey, ARRAYSIZE( s_szExtendedHashKey ) );
  1732. if ( s_pExtendedServerInfo )
  1733. s_pExtendedServerInfo->deleteThis();
  1734. s_pExtendedServerInfo = new KeyValues( "ExtendedServerInfo" );
  1735. const char* mapGroupName = gpGlobals->mapGroupName.ToCStr();
  1736. s_pExtendedServerInfo->SetString( "map", gpGlobals->mapname.ToCStr() );
  1737. s_pExtendedServerInfo->SetString( "mapgroup", mapGroupName );
  1738. s_pExtendedServerInfo->SetString( "requires_attr", g_pGameTypes->GetRequiredAttrForMap( STRING( gpGlobals->mapname ) ) );
  1739. s_pExtendedServerInfo->SetInt( "requires_attr_value", g_pGameTypes->GetRequiredAttrValueForMap( STRING( gpGlobals->mapname ) ) );
  1740. s_pExtendedServerInfo->SetString( "requires_attr_reward", g_pGameTypes->GetRequiredAttrRewardForMap( STRING( gpGlobals->mapname ) ) );
  1741. s_pExtendedServerInfo->SetInt( "reward_drop_list", g_pGameTypes->GetRewardDropListForMap( STRING( gpGlobals->mapname ) ) );
  1742. if ( IsValveDedicated() )
  1743. {
  1744. s_pExtendedServerInfo->SetInt( "official", 1 );
  1745. }
  1746. s_pExtendedServerInfo->SetInt( "numSlots", nNumSlots );
  1747. s_pExtendedServerInfo->SetInt( "c_game_type", iGameType );
  1748. s_pExtendedServerInfo->SetInt( "c_game_mode", iGameMode );
  1749. s_pExtendedServerInfo->SetInt( "default_game_type", g_pGameTypes->GetDefaultGameTypeForMap( gpGlobals->mapname.ToCStr() ) );
  1750. s_pExtendedServerInfo->SetInt( "default_game_mode", g_pGameTypes->GetDefaultGameModeForMap( gpGlobals->mapname.ToCStr() ) );
  1751. s_pExtendedServerInfo->SetString( "ct_arms", g_pGameTypes->GetCTViewModelArmsForMap( gpGlobals->mapname.ToCStr() ) );
  1752. s_pExtendedServerInfo->SetString( "t_arms", g_pGameTypes->GetTViewModelArmsForMap( gpGlobals->mapname.ToCStr() ) );
  1753. if ( const CUtlStringList *pCTModelNames = g_pGameTypes->GetCTModelsForMap( gpGlobals->mapname.ToCStr() ) )
  1754. {
  1755. FOR_EACH_VEC( *pCTModelNames, iModel )
  1756. {
  1757. const char *modelName = (*pCTModelNames)[iModel];
  1758. if ( modelName )
  1759. {
  1760. KeyValues *val = new KeyValues( "" );
  1761. val->SetString( NULL, modelName );
  1762. s_pExtendedServerInfo->FindKey( "ct_models", true )->AddSubKey( val );
  1763. }
  1764. }
  1765. }
  1766. if ( const CUtlStringList *pTModelNames = g_pGameTypes->GetTModelsForMap( gpGlobals->mapname.ToCStr() ) )
  1767. {
  1768. FOR_EACH_VEC( *pTModelNames, iModel )
  1769. {
  1770. const char *modelName = (*pTModelNames)[iModel];
  1771. if ( modelName )
  1772. {
  1773. KeyValues *val = new KeyValues( "" );
  1774. val->SetString( NULL, modelName );
  1775. s_pExtendedServerInfo->FindKey( "t_models", true )->AddSubKey( val );
  1776. }
  1777. }
  1778. }
  1779. if ( const CUtlStringList* mapsInGroup = g_pGameTypes->GetMapGroupMapList( mapGroupName ) )
  1780. {
  1781. FOR_EACH_VEC( *mapsInGroup, i )
  1782. {
  1783. const char *sz = (*mapsInGroup)[i];
  1784. if ( sz )
  1785. {
  1786. KeyValues *val = new KeyValues( "" );
  1787. val->SetString( NULL, sz );
  1788. s_pExtendedServerInfo->FindKey( "maplist", true )->AddSubKey( val );
  1789. }
  1790. }
  1791. }
  1792. KeyValuesDumpAsDevMsg( s_pExtendedServerInfo, 1, 1 );
  1793. }
  1794. return s_pExtendedServerInfo;
  1795. }
  1796. void CServerGameDLL::GetMatchmakingTags( char *buf, size_t bufSize )
  1797. {
  1798. // Additional "tags" that matchmaking wants to know about
  1799. // AssertMsg( false, "fixme" );
  1800. buf[0] = 0;
  1801. if ( CommandLine()->FindParm( "-sv_load_test" ) )
  1802. {
  1803. Q_strncat( buf, ",sv_load_test", bufSize);
  1804. }
  1805. }
  1806. void CServerGameDLL::ServerHibernationUpdate( bool bHibernating )
  1807. {
  1808. m_bIsHibernating = bHibernating;
  1809. }
  1810. //-----------------------------------------------------------------------------
  1811. // Purpose: Called during a transition, to build a map adjacency list
  1812. //-----------------------------------------------------------------------------
  1813. void CServerGameDLL::BuildAdjacentMapList( void )
  1814. {
  1815. // retrieve the pointer to the save data
  1816. CSaveRestoreData *pSaveData = gpGlobals->pSaveData;
  1817. if ( pSaveData )
  1818. pSaveData->levelInfo.connectionCount = BuildChangeList( pSaveData->levelInfo.levelList, MAX_LEVEL_CONNECTIONS );
  1819. }
  1820. //-----------------------------------------------------------------------------
  1821. // Purpose: Sanity-check to verify that a path is a relative path inside the game dir
  1822. // Taken From: engine/cmd.cpp
  1823. //-----------------------------------------------------------------------------
  1824. static bool IsValidPath( const char *pszFilename )
  1825. {
  1826. if ( !pszFilename )
  1827. {
  1828. return false;
  1829. }
  1830. if ( Q_strlen( pszFilename ) <= 0 ||
  1831. Q_IsAbsolutePath( pszFilename ) || // to protect absolute paths
  1832. Q_strstr( pszFilename, ".." ) ) // to protect relative paths
  1833. {
  1834. return false;
  1835. }
  1836. return true;
  1837. }
  1838. static void ValidateMOTDFilename( IConVar *pConVar, const char *oldValue, float flOldValue )
  1839. {
  1840. ConVarRef var( pConVar );
  1841. if ( !IsValidPath( var.GetString() ) )
  1842. {
  1843. var.SetValue( var.GetDefault() );
  1844. }
  1845. }
  1846. static ConVar motdfile( "motdfile", "motd.txt", FCVAR_RELEASE, "The MOTD file to load.", ValidateMOTDFilename );
  1847. static ConVar hostfile( "hostfile", "host.txt", FCVAR_RELEASE, "The HOST file to load.", ValidateMOTDFilename );
  1848. void LoadMOTDFile( const char *stringname, ConVar *pConvarFilename )
  1849. {
  1850. char data[2048];
  1851. int length = filesystem->Size( pConvarFilename->GetString(), "GAME" );
  1852. if ( length <= 0 || length >= (sizeof(data)-1) )
  1853. {
  1854. DevMsg("Invalid file size for %s\n", pConvarFilename->GetString() );
  1855. return;
  1856. }
  1857. FileHandle_t hFile = filesystem->Open( pConvarFilename->GetString(), "rb", "GAME" );
  1858. if ( hFile == FILESYSTEM_INVALID_HANDLE )
  1859. return;
  1860. filesystem->Read( data, length, hFile );
  1861. filesystem->Close( hFile );
  1862. data[length] = 0;
  1863. g_pStringTableInfoPanel->AddString( CBaseEntity::IsServer(), stringname, length+1, data );
  1864. }
  1865. extern ConVar sv_server_graphic1;
  1866. extern ConVar sv_server_graphic2;
  1867. void LoadServerImageFile( const char *stringname )
  1868. {
  1869. char data[ 1 << 14 ]; // MAX_USERDATA_SIZE
  1870. if ( stringname && stringname[0] )
  1871. {
  1872. int length = filesystem->Size( stringname, "GAME" );
  1873. if ( length <= 0 || length >= ( sizeof( data )-1 ) )
  1874. {
  1875. DevMsg( "Invalid file size for %s\n", stringname );
  1876. return;
  1877. }
  1878. FileHandle_t hFile = filesystem->Open( stringname, "rb", "GAME" );
  1879. if ( hFile == FILESYSTEM_INVALID_HANDLE )
  1880. return;
  1881. filesystem->Read( data, length, hFile );
  1882. filesystem->Close( hFile );
  1883. g_pStringTableInfoPanel->AddString( CBaseEntity::IsServer(), stringname, length, data );
  1884. }
  1885. }
  1886. CEG_NOINLINE void CServerGameDLL::LoadMessageOfTheDay()
  1887. {
  1888. STEAMWORKS_TESTSECRET();
  1889. LoadMOTDFile( "motd", &motdfile );
  1890. LoadMOTDFile( "hostfile", &hostfile );
  1891. LoadServerImageFile( sv_server_graphic1.GetString() );
  1892. LoadServerImageFile( sv_server_graphic2.GetString() );
  1893. }
  1894. PublishedFileId_t CServerGameDLL::GetUGCMapFileID( const char* szMapPath )
  1895. {
  1896. return DedicatedServerWorkshop().GetUGCMapPublishedFileID( szMapPath );
  1897. }
  1898. bool CServerGameDLL::GetNewestSubscribedFiles( void )
  1899. {
  1900. DedicatedServerWorkshop().GetNewestSubscribedFiles();
  1901. return true;
  1902. }
  1903. void CServerGameDLL::UpdateUGCMap( PublishedFileId_t id )
  1904. {
  1905. DedicatedServerWorkshop().CheckForNewVersion( id );
  1906. }
  1907. bool CServerGameDLL::HasPendingMapDownloads( void ) const
  1908. {
  1909. return DedicatedServerWorkshop().HasPendingMapDownloads();
  1910. }
  1911. // keeps track of which chapters the user has unlocked
  1912. ConVar sv_unlockedchapters( "sv_unlockedchapters", "1", FCVAR_ARCHIVE );
  1913. //-----------------------------------------------------------------------------
  1914. // Purpose: Updates which chapters are unlocked
  1915. //-----------------------------------------------------------------------------
  1916. CEG_NOINLINE void UpdateChapterRestrictions( const char *mapname )
  1917. {
  1918. // look at the chapter for this map
  1919. char chapterTitle[64];
  1920. chapterTitle[0] = 0;
  1921. for ( int i = 0; i < ARRAYSIZE(gTitleComments); i++ )
  1922. {
  1923. if ( !Q_strnicmp( mapname, gTitleComments[i].pBSPName, strlen(gTitleComments[i].pBSPName) ) )
  1924. {
  1925. // found
  1926. Q_strncpy( chapterTitle, gTitleComments[i].pTitleName, sizeof( chapterTitle ) );
  1927. int j = 0;
  1928. while ( j < 64 && chapterTitle[j] )
  1929. {
  1930. if ( chapterTitle[j] == '\n' || chapterTitle[j] == '\r' )
  1931. chapterTitle[j] = 0;
  1932. else
  1933. j++;
  1934. }
  1935. break;
  1936. }
  1937. }
  1938. if ( !chapterTitle[0] )
  1939. return;
  1940. // make sure the specified chapter title is unlocked
  1941. strlwr( chapterTitle );
  1942. // Get our active mod directory name
  1943. char modDir[MAX_PATH];
  1944. if ( UTIL_GetModDir( modDir, sizeof(modDir) ) == false )
  1945. return;
  1946. char chapterNumberPrefix[64];
  1947. Q_snprintf(chapterNumberPrefix, sizeof(chapterNumberPrefix), "#%s_chapter", modDir);
  1948. const char *newChapterNumber = strstr( chapterTitle, chapterNumberPrefix );
  1949. if ( newChapterNumber )
  1950. {
  1951. // cut off the front
  1952. newChapterNumber += strlen( chapterNumberPrefix );
  1953. char newChapter[32];
  1954. Q_strncpy( newChapter, newChapterNumber, sizeof(newChapter) );
  1955. // cut off the end
  1956. char *end = strstr( newChapter, "_title" );
  1957. if ( end )
  1958. {
  1959. *end = 0;
  1960. }
  1961. int nNewChapter = atoi( newChapter );
  1962. // HACK: HL2 added a zany chapter "9a" which wreaks
  1963. // havoc in this stupid atoi-based chapter code.
  1964. if ( !Q_stricmp( modDir, "hl2" ) )
  1965. {
  1966. if ( !Q_stricmp( newChapter, "9a" ) )
  1967. {
  1968. nNewChapter = 10;
  1969. }
  1970. else if ( nNewChapter > 9 )
  1971. {
  1972. nNewChapter++;
  1973. }
  1974. }
  1975. // ok we have the string, see if it's newer
  1976. const char *unlockedChapter = sv_unlockedchapters.GetString();
  1977. int nUnlockedChapter = atoi( unlockedChapter );
  1978. if ( nUnlockedChapter < nNewChapter )
  1979. {
  1980. // ok we're at a higher chapter, unlock
  1981. sv_unlockedchapters.SetValue( nNewChapter );
  1982. // HACK: Call up through a better function than this? 7/23/07 - jdw
  1983. if ( IsGameConsole() )
  1984. {
  1985. engine->ServerCommand( "host_writeconfig\n" );
  1986. }
  1987. }
  1988. g_nCurrentChapterIndex = nNewChapter;
  1989. }
  1990. }
  1991. CEG_PROTECT_FUNCTION( UpdateChapterRestrictions );
  1992. //-----------------------------------------------------------------------------
  1993. // Precaches a vgui screen overlay material
  1994. //-----------------------------------------------------------------------------
  1995. void PrecacheMaterial( const char *pMaterialName )
  1996. {
  1997. Assert( pMaterialName && pMaterialName[0] );
  1998. g_pStringTableMaterials->AddString( CBaseEntity::IsServer(), pMaterialName );
  1999. }
  2000. //-----------------------------------------------------------------------------
  2001. // Converts a previously precached material into an index
  2002. //-----------------------------------------------------------------------------
  2003. int GetMaterialIndex( const char *pMaterialName )
  2004. {
  2005. if (pMaterialName)
  2006. {
  2007. int nIndex = g_pStringTableMaterials->FindStringIndex( pMaterialName );
  2008. if (nIndex != INVALID_STRING_INDEX )
  2009. {
  2010. return nIndex;
  2011. }
  2012. else
  2013. {
  2014. DevMsg("Warning! GetMaterialIndex: couldn't find material %s\n ", pMaterialName );
  2015. return 0;
  2016. }
  2017. }
  2018. // This is the invalid string index
  2019. return 0;
  2020. }
  2021. //-----------------------------------------------------------------------------
  2022. // Converts a previously precached material index into a string
  2023. //-----------------------------------------------------------------------------
  2024. const char *GetMaterialNameFromIndex( int nMaterialIndex )
  2025. {
  2026. return g_pStringTableMaterials->GetString( nMaterialIndex );
  2027. }
  2028. //-----------------------------------------------------------------------------
  2029. // Precaches a vgui screen overlay material
  2030. //-----------------------------------------------------------------------------
  2031. int PrecacheParticleSystem( const char *pParticleSystemName )
  2032. {
  2033. Assert( pParticleSystemName && pParticleSystemName[0] );
  2034. return g_pStringTableParticleEffectNames->AddString( CBaseEntity::IsServer(), pParticleSystemName );
  2035. }
  2036. void PrecacheParticleFileAndSystems( const char *pParticleSystemFile )
  2037. {
  2038. g_pParticleSystemMgr->ShouldLoadSheets( true );
  2039. g_pParticleSystemMgr->ReadParticleConfigFile( pParticleSystemFile, true, false );
  2040. g_pParticleSystemMgr->DecommitTempMemory();
  2041. Assert( pParticleSystemFile && pParticleSystemFile[0] );
  2042. g_pStringTableExtraParticleFiles->AddString( CBaseEntity::IsServer(), pParticleSystemFile );
  2043. CUtlVector<CUtlString> systems;
  2044. g_pParticleSystemMgr->GetParticleSystemsInFile( pParticleSystemFile, &systems );
  2045. int nCount = systems.Count();
  2046. for ( int i = 0; i < nCount; ++i )
  2047. {
  2048. PrecacheParticleSystem( systems[i] );
  2049. }
  2050. }
  2051. void CServerGameDLL::PrecacheParticleSystemFile( const char *pParticleSystemFile )
  2052. {
  2053. PrecacheParticleFileAndSystems( pParticleSystemFile );
  2054. }
  2055. // this appears to be DOTA specific and therefore bShouldPreload and bShouldAutocache are insignificant parameters
  2056. void PrecacheGameSoundsFile( const char *pSoundFile )
  2057. {
  2058. soundemitterbase->AddSoundsFromFile( pSoundFile, true, false );
  2059. SoundSystemPreloadSounds();
  2060. }
  2061. //-----------------------------------------------------------------------------
  2062. // Converts a previously precached material into an index
  2063. //-----------------------------------------------------------------------------
  2064. int GetParticleSystemIndex( const char *pParticleSystemName )
  2065. {
  2066. if ( pParticleSystemName )
  2067. {
  2068. int nIndex = g_pStringTableParticleEffectNames->FindStringIndex( pParticleSystemName );
  2069. if (nIndex != INVALID_STRING_INDEX )
  2070. return nIndex;
  2071. DevWarning("Server: Missing precache for particle system \"%s\"!\n", pParticleSystemName );
  2072. }
  2073. // This is the invalid string index
  2074. return 0;
  2075. }
  2076. //-----------------------------------------------------------------------------
  2077. // Converts a previously precached material index into a string
  2078. //-----------------------------------------------------------------------------
  2079. const char *GetParticleSystemNameFromIndex( int nMaterialIndex )
  2080. {
  2081. if ( nMaterialIndex < g_pStringTableParticleEffectNames->GetMaxStrings() )
  2082. return g_pStringTableParticleEffectNames->GetString( nMaterialIndex );
  2083. return "error";
  2084. }
  2085. //-----------------------------------------------------------------------------
  2086. // Precaches an effect (used by DispatchEffect)
  2087. //-----------------------------------------------------------------------------
  2088. void PrecacheEffect( const char *pEffectName )
  2089. {
  2090. Assert( pEffectName && pEffectName[0] );
  2091. g_pStringTableEffectDispatch->AddString( CBaseEntity::IsServer(), pEffectName );
  2092. }
  2093. //-----------------------------------------------------------------------------
  2094. // Converts a previously precached effect into an index
  2095. //-----------------------------------------------------------------------------
  2096. int GetEffectIndex( const char *pEffectName )
  2097. {
  2098. if ( pEffectName )
  2099. {
  2100. int nIndex = g_pStringTableEffectDispatch->FindStringIndex( pEffectName );
  2101. if (nIndex != INVALID_STRING_INDEX )
  2102. return nIndex;
  2103. DevWarning("Server: Missing precache for effect \"%s\"!\n", pEffectName );
  2104. }
  2105. // This is the invalid string index
  2106. return 0;
  2107. }
  2108. //-----------------------------------------------------------------------------
  2109. // Converts a previously precached effect index into a string
  2110. //-----------------------------------------------------------------------------
  2111. const char *GetEffectNameFromIndex( int nEffectIndex )
  2112. {
  2113. if ( nEffectIndex < g_pStringTableEffectDispatch->GetMaxStrings() )
  2114. return g_pStringTableEffectDispatch->GetString( nEffectIndex );
  2115. return "error";
  2116. }
  2117. //-----------------------------------------------------------------------------
  2118. // Precaches a movie
  2119. //-----------------------------------------------------------------------------
  2120. void PrecacheMovie( const char *pMovieName )
  2121. {
  2122. Assert ( CBaseEntity::IsPrecacheAllowed() );
  2123. Assert( pMovieName && pMovieName[0] );
  2124. g_pStringTableMovies->AddString( CBaseEntity::IsServer(), pMovieName );
  2125. }
  2126. //-----------------------------------------------------------------------------
  2127. // Converts a previously precached material into an index
  2128. //-----------------------------------------------------------------------------
  2129. int GetMovieIndex( const char *pMovieName )
  2130. {
  2131. if ( pMovieName )
  2132. {
  2133. int nIndex = g_pStringTableMovies->FindStringIndex( pMovieName );
  2134. if ( nIndex != INVALID_STRING_INDEX )
  2135. {
  2136. return nIndex;
  2137. }
  2138. else
  2139. {
  2140. DevMsg( "Warning! GetMovieIndex: couldn't find movie %s\n ", pMovieName );
  2141. return 0;
  2142. }
  2143. }
  2144. // This is the invalid string index
  2145. return 0;
  2146. }
  2147. //-----------------------------------------------------------------------------
  2148. // Converts a previously precached movie index into a string
  2149. //-----------------------------------------------------------------------------
  2150. const char *GetMovieNameFromIndex( int nMovieIndex )
  2151. {
  2152. return g_pStringTableMovies->GetString( nMovieIndex );
  2153. }
  2154. //-----------------------------------------------------------------------------
  2155. // Returns true if host_thread_mode is set to non-zero (and engine is running in threaded mode)
  2156. //-----------------------------------------------------------------------------
  2157. bool IsEngineThreaded()
  2158. {
  2159. if ( g_pcv_ThreadMode )
  2160. {
  2161. return g_pcv_ThreadMode->GetBool();
  2162. }
  2163. return false;
  2164. }
  2165. class CServerGameEnts : public IServerGameEnts
  2166. {
  2167. public:
  2168. virtual void MarkEntitiesAsTouching( edict_t *e1, edict_t *e2 );
  2169. virtual void FreeContainingEntity( edict_t * );
  2170. virtual edict_t* BaseEntityToEdict( CBaseEntity *pEnt );
  2171. virtual CBaseEntity* EdictToBaseEntity( edict_t *pEdict );
  2172. virtual void CheckTransmit( CCheckTransmitInfo *pInfo, const unsigned short *pEdictIndices, int nEdicts );
  2173. virtual void PrepareForFullUpdate( edict_t *pEdict );
  2174. };
  2175. EXPOSE_SINGLE_INTERFACE(CServerGameEnts, IServerGameEnts, INTERFACEVERSION_SERVERGAMEENTS);
  2176. //-----------------------------------------------------------------------------
  2177. // Purpose: Marks entities as touching
  2178. // Input : *e1 -
  2179. // *e2 -
  2180. //-----------------------------------------------------------------------------
  2181. void CServerGameEnts::MarkEntitiesAsTouching( edict_t *e1, edict_t *e2 )
  2182. {
  2183. CBaseEntity *entity = GetContainingEntity( e1 );
  2184. CBaseEntity *entityTouched = GetContainingEntity( e2 );
  2185. if ( entity && entityTouched )
  2186. {
  2187. // HACKHACK: UNDONE: Pass in the trace here??!?!?
  2188. trace_t tr;
  2189. UTIL_ClearTrace( tr );
  2190. tr.endpos = (entity->GetAbsOrigin() + entityTouched->GetAbsOrigin()) * 0.5;
  2191. entity->PhysicsMarkEntitiesAsTouching( entityTouched, tr );
  2192. }
  2193. }
  2194. void CServerGameEnts::FreeContainingEntity( edict_t *e )
  2195. {
  2196. ::FreeContainingEntity(e);
  2197. }
  2198. edict_t* CServerGameEnts::BaseEntityToEdict( CBaseEntity *pEnt )
  2199. {
  2200. if ( pEnt )
  2201. return pEnt->edict();
  2202. else
  2203. return NULL;
  2204. }
  2205. CBaseEntity* CServerGameEnts::EdictToBaseEntity( edict_t *pEdict )
  2206. {
  2207. if ( pEdict )
  2208. return CBaseEntity::Instance( pEdict );
  2209. else
  2210. return NULL;
  2211. }
  2212. /* Yuck.. ideally this would be in CServerNetworkProperty's header, but it requires CBaseEntity and
  2213. // inlining it gives a nice speedup.
  2214. inline void CServerNetworkProperty::CheckTransmit( CCheckTransmitInfo *pInfo )
  2215. {
  2216. // If we have a transmit proxy, let it hook our ShouldTransmit return value.
  2217. if ( m_pTransmitProxy )
  2218. {
  2219. nShouldTransmit = m_pTransmitProxy->ShouldTransmit( pInfo, nShouldTransmit );
  2220. }
  2221. if ( m_pOuter->ShouldTransmit( pInfo ) )
  2222. {
  2223. m_pOuter->SetTransmit( pInfo );
  2224. }
  2225. } */
  2226. ConVar sv_occlude_players( "sv_occlude_players", "1", FCVAR_RELEASE /*, "Do additional traces on the server to hide players occluded by large brushes from each other - anti-cheat"*/ );
  2227. // A bitmap which records whether players were occluded under sv_occlude_players
  2228. // rules. This is so other code areas which are interested in player-player
  2229. // PVS can also check what the occlusion rules said.
  2230. static CBitVec< (MAX_PLAYERS+1)*(MAX_PLAYERS+1) > g_occludePlayersCacheBitVec;
  2231. bool WasPlayerOccluded( int fromplayer, int toplayer )
  2232. {
  2233. if ( fromplayer > MAX_PLAYERS || toplayer > MAX_PLAYERS )
  2234. {
  2235. Error( "Player indexes too large: %d %d", fromplayer, toplayer );
  2236. return false;
  2237. }
  2238. const int index = fromplayer*( MAX_PLAYERS + 1 )+ toplayer;
  2239. return g_occludePlayersCacheBitVec.IsBitSet(index) ;
  2240. }
  2241. void SetPlayerOccluded( int index, bool newvalue )
  2242. {
  2243. if ( index > (MAX_PLAYERS + 1 ) * ( MAX_PLAYERS + 1 ) )
  2244. {
  2245. Error( "Player indexes too large: %d %d", index % (MAX_PLAYERS+1), index / (MAX_PLAYERS+1) );
  2246. return;
  2247. }
  2248. g_occludePlayersCacheBitVec.Set( index, newvalue );
  2249. }
  2250. /*
  2251. AABB_t GetOcclusionBounds( CBasePlayer *pPlayer )
  2252. {
  2253. AABB_t aabb;
  2254. // does server run at 30 fps? can player run faster than 260 (when falling? when climbing ladders?)
  2255. // what to do about falling? (if we always expand down, we'll miss a lot of cases due to looking from under floor)
  2256. Vector vUncertainty = Vector( CS_PLAYER_SPEED_RUN / 30.0f, CS_PLAYER_SPEED_RUN / 30.0f, 0 );
  2257. if ( ( pPlayer->GetFlags() & ( FL_DUCKING | FL_ANIMDUCKING ) ) == ( FL_DUCKING | FL_ANIMDUCKING ) )
  2258. {
  2259. aabb.m_vMinBounds = VEC_DUCK_HULL_MIN - vUncertainty;
  2260. aabb.m_vMaxBounds = VEC_DUCK_HULL_MAX + vUncertainty;
  2261. }
  2262. else
  2263. {
  2264. aabb.m_vMinBounds = VEC_HULL_MIN - vUncertainty;
  2265. aabb.m_vMaxBounds = VEC_HULL_MAX + vUncertainty;
  2266. }
  2267. return aabb;
  2268. }
  2269. */
  2270. AABB_t GetOcclusionBounds( CBaseEntity *pItem )
  2271. {
  2272. AABB_t aabb;
  2273. CCollisionProperty *pColl = pItem->CollisionProp();
  2274. pColl->WorldSpaceAABB( &aabb.m_vMinBounds, &aabb.m_vMaxBounds );
  2275. return aabb;
  2276. }
  2277. AABB_t GetCameraBounds( CBasePlayer *pPlayer )
  2278. {
  2279. Assert( !pPlayer->IsDead() && pPlayer->GetObserverMode() != OBS_MODE_DEATHCAM );
  2280. Vector vPlayerOrigin = pPlayer->GetAbsOrigin(); // pPlayer->GetViewOffset() varies between VEC_DUC_VIEW and VEC_VIEW for an alive player
  2281. AABB_t aabb;
  2282. float flCameraMargin = occlusion_test_camera_margins.GetFloat();
  2283. float flJumpMargin = occlusion_test_jump_margin.GetFloat();
  2284. aabb.m_vMinBounds = vPlayerOrigin + VEC_DUCK_VIEW - Vector( flCameraMargin, flCameraMargin, 0 );
  2285. aabb.m_vMaxBounds = vPlayerOrigin + VEC_VIEW + Vector( flCameraMargin, flCameraMargin, flJumpMargin );
  2286. return aabb;
  2287. }
  2288. void CServerGameEnts::CheckTransmit( CCheckTransmitInfo *pInfo, const unsigned short *pEdictIndices, int nEdicts )
  2289. {
  2290. // NOTE: for speed's sake, this assumes that all networkables are CBaseEntities and that the edict list
  2291. // is consecutive in memory. If either of these things change, then this routine needs to change, but
  2292. // ideally we won't be calling any virtual from this routine. This speedy routine was added as an
  2293. // optimization which would be nice to keep.
  2294. edict_t *pBaseEdict = gpGlobals->pEdicts;
  2295. CBaseEntity *pRecipientEntity = CBaseEntity::Instance( pInfo->m_pClientEnt );
  2296. Assert( pRecipientEntity && pRecipientEntity->IsPlayer() );
  2297. if ( !pRecipientEntity )
  2298. return;
  2299. MDLCACHE_CRITICAL_SECTION();
  2300. CBasePlayer *pRecipientPlayer = static_cast<CBasePlayer*>( pRecipientEntity );
  2301. int nRecipientPlayerEntIndexMulNumPlayers = pRecipientPlayer->entindex() * ( MAX_PLAYERS + 1 );
  2302. // If the player is freshly spawned then give it a small window in
  2303. // which it sees all entities so that it gets their basic information.
  2304. const bool bIsFreshlySpawned = pRecipientPlayer->GetInitialSpawnTime()+3.0f > gpGlobals->curtime;
  2305. const int skyBoxArea = pRecipientPlayer->m_Local.m_skybox3d.area;
  2306. #ifndef _GAMECONSOLE
  2307. const bool bIsHLTV = pRecipientPlayer->IsHLTV();
  2308. #if defined( REPLAY_ENABLED )
  2309. const bool bIsReplay = pRecipientPlayer->IsReplay();
  2310. #else
  2311. const bool bIsReplay = false;
  2312. #endif
  2313. // m_pTransmitAlways must be set if HLTV client
  2314. Assert( bIsHLTV == ( pInfo->m_pTransmitAlways != NULL) ||
  2315. bIsReplay == ( pInfo->m_pTransmitAlways != NULL) );
  2316. #endif
  2317. IEngineTrace::CAutoSuspendOcclusionTests autoSuspender( enginetrace ); // suspend the async engine traces for the time being
  2318. for ( int i=0; i < nEdicts; i++ )
  2319. {
  2320. int iEdict = pEdictIndices[i];
  2321. #ifdef _GAMECONSOLE
  2322. if ( i < nEdicts-1 )
  2323. {
  2324. PREFETCH360(&pBaseEdict[pEdictIndices[i+1]],0);
  2325. }
  2326. #endif
  2327. edict_t *pEdict = &pBaseEdict[iEdict];
  2328. int nFlags = pEdict->m_fStateFlags & (FL_EDICT_DONTSEND|FL_EDICT_ALWAYS|FL_EDICT_PVSCHECK|FL_EDICT_FULLCHECK);
  2329. // entity needs no transmit
  2330. if ( nFlags & FL_EDICT_DONTSEND )
  2331. continue;
  2332. PREFETCH360(pEdict->GetUnknown(),0);
  2333. // entity is already marked for sending
  2334. if ( pInfo->m_pTransmitEdict->Get( iEdict ) )
  2335. continue;
  2336. if ( nFlags & FL_EDICT_ALWAYS )
  2337. {
  2338. // FIXME: Hey! Shouldn't this be using SetTransmit so as
  2339. // to also force network down dependent entities?
  2340. while ( true )
  2341. {
  2342. // mark entity for sending
  2343. pInfo->m_pTransmitEdict->Set( iEdict );
  2344. #ifndef _GAMECONSOLE
  2345. if ( bIsHLTV || bIsReplay )
  2346. {
  2347. pInfo->m_pTransmitAlways->Set( iEdict );
  2348. }
  2349. #endif
  2350. CServerNetworkProperty *pEnt = static_cast<CServerNetworkProperty*>( pEdict->GetNetworkable() );
  2351. if ( !pEnt )
  2352. break;
  2353. CServerNetworkProperty *pParent = pEnt->GetNetworkParent();
  2354. if ( !pParent )
  2355. break;
  2356. pEdict = pParent->edict();
  2357. iEdict = pParent->entindex();
  2358. }
  2359. continue;
  2360. }
  2361. // FIXME: Would like to remove all dependencies
  2362. CBaseEntity *pEnt = ( CBaseEntity * )pEdict->GetUnknown();
  2363. Assert( dynamic_cast< CBaseEntity* >( pEdict->GetUnknown() ) == pEnt );
  2364. if ( pEnt->entindex() <= MAX_PLAYERS )
  2365. {
  2366. SetPlayerOccluded( pEnt->entindex() + nRecipientPlayerEntIndexMulNumPlayers, false );
  2367. }
  2368. if ( nFlags == FL_EDICT_FULLCHECK )
  2369. {
  2370. // do a full ShouldTransmit() check, may return FL_EDICT_CHECKPVS
  2371. nFlags = pEnt->ShouldTransmit( pInfo );
  2372. Assert( !(nFlags & FL_EDICT_FULLCHECK) );
  2373. if ( nFlags & FL_EDICT_ALWAYS )
  2374. {
  2375. pEnt->SetTransmit( pInfo, true );
  2376. continue;
  2377. }
  2378. }
  2379. // don't send this entity
  2380. if ( !( nFlags & FL_EDICT_PVSCHECK ) )
  2381. continue;
  2382. CServerNetworkProperty *netProp = static_cast<CServerNetworkProperty*>( pEdict->GetNetworkable() );
  2383. #ifndef _GAMECONSOLE
  2384. if ( bIsHLTV || bIsReplay )
  2385. {
  2386. // for the HLTV/Replay we don't cull against PVS
  2387. if ( netProp->AreaNum() == skyBoxArea )
  2388. {
  2389. pEnt->SetTransmit( pInfo, true );
  2390. }
  2391. else
  2392. {
  2393. pEnt->SetTransmit( pInfo, false );
  2394. }
  2395. continue;
  2396. }
  2397. #endif
  2398. // Always send entities in the player's 3d skybox.
  2399. // Sidenote: call of AreaNum() ensures that PVS data is up to date for this entity
  2400. bool bSameAreaAsSky = netProp->AreaNum() == skyBoxArea;
  2401. if ( bSameAreaAsSky )
  2402. {
  2403. pEnt->SetTransmit( pInfo, true );
  2404. continue;
  2405. }
  2406. CBasePlayer *pPlayer = dynamic_cast< CBasePlayer* >( pEnt );
  2407. #if defined( CSTRIKE15 )
  2408. // Team Lead in gungame should be always visible in counter-strike. Yes, even if he's out of PVS. Yes, even if he's behind the wall.
  2409. if ( pPlayer && static_cast< CCSPlayer* >( pPlayer )->m_isCurrentGunGameTeamLeader )
  2410. { // do not check PVS or occlusion for a gun game team leader
  2411. pEnt->SetTransmit( pInfo, false );
  2412. continue;
  2413. }
  2414. #endif
  2415. bool bInPVS;
  2416. if ( pPlayer && ( pPlayer->GetAbsOrigin() - pRecipientPlayer->GetAbsOrigin() ).LengthSqr() < Sqr( pvs_min_player_distance.GetFloat() ) )
  2417. bInPVS = true;
  2418. else
  2419. bInPVS = netProp->IsInPVS( pInfo );
  2420. CServerNetworkProperty *check = netProp->GetNetworkParent();
  2421. if ( bInPVS && sv_occlude_players.GetBool() )
  2422. {
  2423. if ( pPlayer )
  2424. {
  2425. if ( pRecipientPlayer->ShouldCheckOcclusion( pPlayer ) )
  2426. {
  2427. // if ( IsDebug() && ShouldHideAllPlayers() ) bInPVS = false;
  2428. // the other player is from an opposing team, otherwise the iBaseResult would be "Always Transmit"
  2429. AABB_t playerBounds = GetOcclusionBounds( pPlayer );
  2430. AABB_t recipientBounds = GetCameraBounds( pRecipientPlayer );
  2431. int nOcclusionKey = pPlayer->entindex() + nRecipientPlayerEntIndexMulNumPlayers;
  2432. if ( enginetrace->IsFullyOccluded( nOcclusionKey, playerBounds, recipientBounds, g_pCascadeLight ? ( g_pCascadeLight->GetShadowDirection() * occlusion_test_shadow_length.GetFloat() ) : vec3_origin ) )
  2433. {
  2434. bInPVS = false; // forget that the player is in PVS. If it's fully occluded, we just act as if he's outside of PVS.
  2435. SetPlayerOccluded( nOcclusionKey, true );
  2436. }
  2437. // if ( dump_occlusion_probes.GetBool() ) Msg( "Occlusion probe %s - %s: %s\n", pRecipientPlayer->GetPlayerName(), pPlayer->GetPlayerName(), bInPVS ? "miss" : "occluded" );
  2438. }
  2439. }
  2440. #if defined( CSTRIKE_DLL )
  2441. else if ( CServerNetworkProperty *pNetworkParent = check )
  2442. {
  2443. do
  2444. {
  2445. if ( CBaseEntity *pParent = pNetworkParent->GetBaseEntity() )
  2446. {
  2447. if ( pParent->IsPlayer() )
  2448. {
  2449. // Is the parent-owner-player marked or not market for transmission? If not, let's not transmit its children
  2450. if ( !pInfo->m_pTransmitEdict->Get( pParent->entindex() ) )
  2451. {
  2452. bInPVS = false;
  2453. check = NULL; // don't check anything, just don't transmit this entity
  2454. }
  2455. /*
  2456. if ( CBasePlayer *pParentPlayer = dynamic_cast< CBasePlayer* >( pParent ) )
  2457. {
  2458. if ( pRecipientPlayer->ShouldCheckOcclusion( pParentPlayer ) )
  2459. {
  2460. AABB_t entityBounds = GetOcclusionBounds( pEnt );
  2461. AABB_t recipientBounds = GetCameraBounds( pRecipientPlayer );
  2462. if ( IsFullyOccluded_WithShadow( entityBounds, recipientBounds ) )
  2463. {
  2464. bInPVS = false; // the weapon is fully occluded
  2465. }
  2466. if ( dump_occlusion_probes.GetBool() )
  2467. {
  2468. Msg( "Occlusion probe %s - item %s of %s: %s\n", pRecipientPlayer->GetPlayerName(), pEnt->GetDebugName(), pParentPlayer->GetPlayerName(), bInPVS ? "miss" : "occluded" );
  2469. }
  2470. }
  2471. }
  2472. */
  2473. break;
  2474. }
  2475. }
  2476. pNetworkParent = pNetworkParent->GetNetworkParent();
  2477. } while ( pNetworkParent );
  2478. }
  2479. #endif
  2480. }
  2481. // If a player has just been spawned, forcibly send it for a brief
  2482. // window so that everyone gets its basic info.
  2483. const bool bFreshlySpawnedTargetPlayer = pPlayer != NULL && pPlayer->GetInitialSpawnTime()+3.0f > gpGlobals->curtime;
  2484. if ( bInPVS || bIsFreshlySpawned || bFreshlySpawnedTargetPlayer || sv_force_transmit_ents.GetBool() )
  2485. {
  2486. // only send if entity is in PVS
  2487. pEnt->SetTransmit( pInfo, false );
  2488. continue;
  2489. }
  2490. // If the entity is marked "check PVS" but it's in hierarchy, walk up the hierarchy looking for the
  2491. // for any parent which is also in the PVS. If none are found, then we don't need to worry about sending ourself
  2492. CBaseEntity *orig = pEnt;
  2493. // BUG BUG: I think it might be better to build up a list of edict indices which "depend" on other answers and then
  2494. // resolve them in a second pass. Not sure what happens if an entity has two parents who both request PVS check?
  2495. while ( check )
  2496. {
  2497. int checkIndex = check->entindex();
  2498. // Parent already being sent
  2499. if ( pInfo->m_pTransmitEdict->Get( checkIndex ) )
  2500. {
  2501. orig->SetTransmit( pInfo, true );
  2502. break;
  2503. }
  2504. edict_t *checkEdict = check->edict();
  2505. int checkFlags = checkEdict->m_fStateFlags & (FL_EDICT_DONTSEND|FL_EDICT_ALWAYS|FL_EDICT_PVSCHECK|FL_EDICT_FULLCHECK);
  2506. if ( checkFlags & FL_EDICT_DONTSEND )
  2507. break;
  2508. if ( checkFlags & FL_EDICT_ALWAYS )
  2509. {
  2510. orig->SetTransmit( pInfo, true );
  2511. break;
  2512. }
  2513. if ( checkFlags == FL_EDICT_FULLCHECK )
  2514. {
  2515. // do a full ShouldTransmit() check, may return FL_EDICT_CHECKPVS
  2516. CBaseEntity *pCheckEntity = check->GetBaseEntity();
  2517. nFlags = pCheckEntity->ShouldTransmit( pInfo );
  2518. Assert( !(nFlags & FL_EDICT_FULLCHECK) );
  2519. if ( nFlags & FL_EDICT_ALWAYS )
  2520. {
  2521. pCheckEntity->SetTransmit( pInfo, true );
  2522. orig->SetTransmit( pInfo, true );
  2523. }
  2524. break;
  2525. }
  2526. if ( checkFlags & FL_EDICT_PVSCHECK )
  2527. {
  2528. // Check pvs
  2529. check->RecomputePVSInformation();
  2530. bool bMoveParentInPVS = check->IsInPVS( pInfo );
  2531. if ( bMoveParentInPVS )
  2532. {
  2533. orig->SetTransmit( pInfo, true );
  2534. break;
  2535. }
  2536. }
  2537. // Continue up chain just in case the parent itself has a parent that's in the PVS...
  2538. check = check->GetNetworkParent();
  2539. }
  2540. }
  2541. // Msg("A:%i, N:%i, F: %i, P: %i\n", always, dontSend, fullCheck, PVS );
  2542. }
  2543. //-----------------------------------------------------------------------------
  2544. // Purpose: called before a full update, so the server can flush any custom PVS info, etc
  2545. //-----------------------------------------------------------------------------
  2546. void CServerGameEnts::PrepareForFullUpdate( edict_t *pEdict )
  2547. {
  2548. CBaseEntity *pEntity = CBaseEntity::Instance( pEdict );
  2549. Assert( pEntity && pEntity->IsPlayer() );
  2550. if ( !pEntity )
  2551. return;
  2552. CBasePlayer *pPlayer = static_cast<CBasePlayer*>( pEntity );
  2553. pPlayer->PrepareForFullUpdate();
  2554. }
  2555. CServerGameClients g_ServerGameClients;
  2556. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerGameClients, IServerGameClients, INTERFACEVERSION_SERVERGAMECLIENTS, g_ServerGameClients );
  2557. //-----------------------------------------------------------------------------
  2558. // Purpose: called when a player tries to connect to the server
  2559. // Input : *pEdict - the new player
  2560. // char *pszName - the players name
  2561. // char *pszAddress - the IP address of the player
  2562. // reject - output - fill in with the reason why
  2563. // maxrejectlen -- sizeof output buffer
  2564. // the player was not allowed to connect.
  2565. // Output : Returns TRUE if player is allowed to join, FALSE if connection is denied.
  2566. //-----------------------------------------------------------------------------
  2567. CEG_NOINLINE bool CServerGameClients::ClientConnect( edict_t *pEdict, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen )
  2568. {
  2569. CEG_PROTECT_VIRTUAL_FUNCTION( CServerGameClient_ClientConnect );
  2570. if ( !g_pGameRules )
  2571. return false;
  2572. return g_pGameRules->ClientConnected( pEdict, pszName, pszAddress, reject, maxrejectlen );
  2573. }
  2574. //-----------------------------------------------------------------------------
  2575. // Purpose: Called when a player is fully active (i.e. ready to receive messages)
  2576. // Input : *pEntity - the player
  2577. //-----------------------------------------------------------------------------
  2578. CEG_NOINLINE void CServerGameClients::ClientActive( edict_t *pEdict, bool bLoadGame )
  2579. {
  2580. MDLCACHE_CRITICAL_SECTION();
  2581. ::ClientActive( pEdict, bLoadGame );
  2582. // If we just loaded from a save file, call OnRestore on valid entities
  2583. EndRestoreEntities();
  2584. if ( gpGlobals->eLoadType != MapLoad_LoadGame )
  2585. {
  2586. // notify all entities that the player is now in the game
  2587. for ( CBaseEntity *pEntity = gEntList.FirstEnt(); pEntity != NULL; pEntity = gEntList.NextEnt(pEntity) )
  2588. {
  2589. pEntity->PostClientActive();
  2590. }
  2591. }
  2592. CEG_PROTECT_VIRTUAL_FUNCTION( CServerGameClients_ClientActive );
  2593. // Tell the sound controller to check looping sounds
  2594. CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
  2595. CSoundEnvelopeController::GetController().CheckLoopingSoundsForPlayer( pPlayer );
  2596. SceneManager_ClientActive( pPlayer );
  2597. }
  2598. //-----------------------------------------------------------------------------
  2599. // Purpose: Called when a player is fully connect ( initial baseline entities have been received )
  2600. // Input : *pEntity - the player
  2601. //-----------------------------------------------------------------------------
  2602. void CServerGameClients::ClientFullyConnect( edict_t *pEdict )
  2603. {
  2604. ::ClientFullyConnect( pEdict );
  2605. }
  2606. //-----------------------------------------------------------------------------
  2607. // Purpose: called when a player disconnects from a server
  2608. // Input : *pEdict - the player
  2609. //-----------------------------------------------------------------------------
  2610. void CServerGameClients::ClientDisconnect( edict_t *pEdict )
  2611. {
  2612. extern bool g_fGameOver;
  2613. CBasePlayer *player = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
  2614. if ( player )
  2615. {
  2616. if ( !g_fGameOver )
  2617. {
  2618. player->SetMaxSpeed( 0.0f );
  2619. CSound *pSound;
  2620. pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt::ClientSoundIndex( pEdict ) );
  2621. {
  2622. // since this client isn't around to think anymore, reset their sound.
  2623. if ( pSound )
  2624. {
  2625. pSound->Reset();
  2626. }
  2627. }
  2628. // since the edict doesn't get deleted, fix it so it doesn't interfere.
  2629. player->RemoveFlag( FL_AIMTARGET ); // don't attract autoaim
  2630. player->AddFlag( FL_DONTTOUCH ); // stop it touching anything
  2631. player->AddFlag( FL_NOTARGET ); // stop NPCs noticing it
  2632. player->AddSolidFlags( FSOLID_NOT_SOLID ); // nonsolid
  2633. if ( g_pGameRules )
  2634. {
  2635. g_pGameRules->ClientDisconnected( pEdict );
  2636. #ifndef _GAMECONSOLE
  2637. gamestats->Event_PlayerDisconnected( player );
  2638. #endif
  2639. }
  2640. }
  2641. // Make sure all Untouch()'s are called for this client leaving
  2642. CBaseEntity::PhysicsRemoveTouchedList( player );
  2643. CBaseEntity::PhysicsRemoveGroundList( player );
  2644. #if !defined( NO_ENTITY_PREDICTION )
  2645. // Make sure anything we "own" is simulated by the server from now on
  2646. player->ClearPlayerSimulationList();
  2647. #endif
  2648. }
  2649. }
  2650. void CServerGameClients::ClientPutInServer( edict_t *pEntity, const char *playername )
  2651. {
  2652. if ( g_pClientPutInServerOverride )
  2653. g_pClientPutInServerOverride( pEntity, playername );
  2654. else
  2655. ::ClientPutInServer( pEntity, playername );
  2656. CBasePlayer *pPlayer = ToBasePlayer( GetContainingEntity( pEntity ) );
  2657. if ( pPlayer )
  2658. {
  2659. bool bIsSplitScreenPlayer = engine->IsSplitScreenPlayer( pPlayer->entindex() );
  2660. CBasePlayer *pAttachedTo = NULL;
  2661. if ( bIsSplitScreenPlayer )
  2662. {
  2663. pAttachedTo = (CBasePlayer *)::GetContainingEntity( engine->GetSplitScreenPlayerAttachToEdict( pPlayer->entindex() ) );
  2664. }
  2665. pPlayer->SetSplitScreenPlayer( bIsSplitScreenPlayer, pAttachedTo );
  2666. pPlayer->SetCrossPlayPlatform( engine->GetClientCrossPlayPlatform( pPlayer->entindex() ) );
  2667. }
  2668. }
  2669. void CServerGameClients::ClientCommand( edict_t *pEntity, const CCommand &args )
  2670. {
  2671. CBasePlayer *pPlayer = ToBasePlayer( GetContainingEntity( pEntity ) );
  2672. ::ClientCommand( pPlayer, args );
  2673. }
  2674. //-----------------------------------------------------------------------------
  2675. // Purpose: called after the player changes userinfo - gives dll a chance to modify
  2676. // it before it gets sent into the rest of the engine->
  2677. // Input : *pEdict - the player
  2678. // *infobuffer - their infobuffer
  2679. //-----------------------------------------------------------------------------
  2680. void CServerGameClients::ClientSettingsChanged( edict_t *pEdict )
  2681. {
  2682. // Is the client spawned yet?
  2683. if ( !pEdict->GetUnknown() )
  2684. return;
  2685. CBasePlayer *player = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
  2686. if ( !player )
  2687. return;
  2688. bool bAllowNetworkingClientSettingsChange = g_pGameRules->IsConnectedUserInfoChangeAllowed( player );
  2689. if ( bAllowNetworkingClientSettingsChange )
  2690. {
  2691. #define QUICKGETCVARVALUE(v) (engine->GetClientConVarValue( player->entindex(), v ))
  2692. // get network setting for prediction & lag compensation
  2693. // Unfortunately, we have to duplicate the code in cdll_bounded_cvars.cpp here because the client
  2694. // doesn't send the virtualized value up (because it has no way to know when the virtualized value
  2695. // changes). Possible todo: put the responsibility on the bounded cvar to notify the engine when
  2696. // its virtualized value has changed.
  2697. //
  2698. // Most important part in this code is to keep server computations same as client
  2699. // side evaluation of convar string values. That is why the server parses values
  2700. // as floats, then casts them to int to ensure that it yields the same result as
  2701. // when the client sets convar string value and later checks it with GetInt or
  2702. // GetBool accessors that at the moment of fixing this code were parsing original
  2703. // string value as a float and then casting it to an int.
  2704. //
  2705. float flUpdateRateValue = Q_atof( QUICKGETCVARVALUE("cl_updaterate") );
  2706. if ( !player->IsHLTV() )
  2707. {
  2708. static const ConVar *pMinUpdateRate = g_pCVar->FindVar( "sv_minupdaterate" );
  2709. static const ConVar *pMaxUpdateRate = g_pCVar->FindVar( "sv_maxupdaterate" );
  2710. if ( pMinUpdateRate && pMaxUpdateRate )
  2711. flUpdateRateValue = clamp( flUpdateRateValue, pMinUpdateRate->GetFloat(), pMaxUpdateRate->GetFloat() );
  2712. }
  2713. player->m_nUpdateRate = ( int ) flUpdateRateValue;
  2714. bool useInterpolation = !player->IsBot() || ( int( Q_atof( QUICKGETCVARVALUE("cl_interpolate") ) ) != 0 );
  2715. if ( useInterpolation )
  2716. {
  2717. float flLerpRatio = Q_atof( QUICKGETCVARVALUE("cl_interp_ratio") );
  2718. if ( flLerpRatio == 0 )
  2719. flLerpRatio = 1.0f;
  2720. float flLerpAmount = Q_atof( QUICKGETCVARVALUE("cl_interp") );
  2721. static const ConVar *pMin = g_pCVar->FindVar( "sv_client_min_interp_ratio" );
  2722. static const ConVar *pMax = g_pCVar->FindVar( "sv_client_max_interp_ratio" );
  2723. if ( pMin && pMax && pMin->GetFloat() != -1 )
  2724. {
  2725. flLerpRatio = clamp( flLerpRatio, pMin->GetFloat(), pMax->GetFloat() );
  2726. }
  2727. else
  2728. {
  2729. if ( flLerpRatio == 0 )
  2730. flLerpRatio = 1.0f;
  2731. }
  2732. // #define FIXME_INTERP_RATIO
  2733. player->m_fLerpTime = MAX( flLerpAmount, flLerpRatio / flUpdateRateValue );
  2734. }
  2735. else
  2736. {
  2737. player->m_fLerpTime = 0.0f;
  2738. }
  2739. #if !defined( NO_ENTITY_PREDICTION )
  2740. bool usePrediction = ( int( Q_atof( QUICKGETCVARVALUE("cl_predict")) ) != 0 );
  2741. if ( usePrediction )
  2742. {
  2743. player->m_bPredictionEnabled = usePrediction;
  2744. player->m_bPredictWeapons = ( int( Q_atof( QUICKGETCVARVALUE("cl_predictweapons")) ) != 0 );
  2745. player->m_bLagCompensation = ( int( Q_atof( QUICKGETCVARVALUE("cl_lagcompensation")) ) != 0 );
  2746. }
  2747. else
  2748. #endif
  2749. {
  2750. player->m_bPredictionEnabled = false;
  2751. player->m_bPredictWeapons = false;
  2752. player->m_bLagCompensation = false;
  2753. }
  2754. //HACKHACK: There's a bug in portal 2 splitscreen where the primary player will be set to non-predictive and the secondary player will be set to predictive.
  2755. // It's probably a better idea in the long run to have CBaseClient::ProcessSetConVar() in engine better handle splitscreen. But for now we're going for a surgical fix
  2756. {
  2757. //stomp attached splitscreen players to have the same prediction settings as the primary player
  2758. CUtlVector< CHandle<CBasePlayer> > &splitscreenPlayers = player->GetSplitScreenPlayers();
  2759. for( int i = 0; i < splitscreenPlayers.Count(); ++i )
  2760. {
  2761. CBasePlayer *pAttachedPlayer = splitscreenPlayers[i];
  2762. if( pAttachedPlayer )
  2763. {
  2764. pAttachedPlayer->m_bPredictionEnabled = player->m_bPredictionEnabled;
  2765. pAttachedPlayer->m_bPredictWeapons = player->m_bPredictWeapons;
  2766. pAttachedPlayer->m_bLagCompensation = player->m_bLagCompensation;
  2767. }
  2768. }
  2769. }
  2770. #undef QUICKGETCVARVALUE
  2771. }
  2772. g_pGameRules->ClientSettingsChanged( player );
  2773. }
  2774. #ifdef PORTAL
  2775. //-----------------------------------------------------------------------------
  2776. // Purpose: Runs CFuncAreaPortalBase::UpdateVisibility on each portal
  2777. // Input : pAreaPortal - The Area portal to test for visibility from portals
  2778. // Output : int - 1 if any portal needs this area portal open, 0 otherwise.
  2779. //-----------------------------------------------------------------------------
  2780. int TestAreaPortalVisibilityThroughPortals ( CFuncAreaPortalBase* pAreaPortal, edict_t *pViewEntity, unsigned char *pvs, int pvssize )
  2781. {
  2782. int iPortalCount = CPortal_Base2D_Shared::AllPortals.Count();
  2783. if( iPortalCount == 0 )
  2784. return 0;
  2785. CPortal_Base2D **pPortals = CPortal_Base2D_Shared::AllPortals.Base();
  2786. for ( int i = 0; i != iPortalCount; ++i )
  2787. {
  2788. CPortal_Base2D* pLocalPortal = pPortals[ i ];
  2789. if ( pLocalPortal && pLocalPortal->IsActive() )
  2790. {
  2791. CPortal_Base2D* pRemotePortal = pLocalPortal->m_hLinkedPortal.Get();
  2792. // Make sure this portal's linked portal is in the PVS before we add what it can see
  2793. if ( pRemotePortal && pRemotePortal->IsActive() && pRemotePortal->NetworkProp() &&
  2794. pRemotePortal->NetworkProp()->IsInPVS( pViewEntity, pvs, pvssize ) )
  2795. {
  2796. bool bIsOpenOnClient = true;
  2797. float fovDistanceAdjustFactor = 1.0f;
  2798. Vector portalOrg = pLocalPortal->GetAbsOrigin();
  2799. CUtlVector< Vector > orgs;
  2800. orgs.AddToTail( portalOrg );
  2801. int iPortalNeedsThisPortalOpen = pAreaPortal->UpdateVisibility( orgs, fovDistanceAdjustFactor, bIsOpenOnClient );
  2802. // Stop checking on success, this portal needs to be open
  2803. if ( iPortalNeedsThisPortalOpen )
  2804. {
  2805. return iPortalNeedsThisPortalOpen;
  2806. }
  2807. }
  2808. }
  2809. }
  2810. return 0;
  2811. }
  2812. #endif
  2813. //-----------------------------------------------------------------------------
  2814. // Purpose: A client can have a separate "view entity" indicating that his/her view should depend on the origin of that
  2815. // view entity. If that's the case, then pViewEntity will be non-NULL and will be used. Otherwise, the current
  2816. // entity's origin is used. Either is offset by the m_vecViewOffset to get the eye position.
  2817. // 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
  2818. // override the actual PAS or PVS values, or use a different origin.
  2819. // 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
  2820. // Input : *pViewEntity -
  2821. // *pClient -
  2822. // **pvs -
  2823. // **pas -
  2824. //-----------------------------------------------------------------------------
  2825. void CServerGameClients::ClientSetupVisibility( edict_t *pViewEntity, edict_t *pClient, unsigned char *pvs, int pvssize )
  2826. {
  2827. Vector org;
  2828. // Reset the PVS!!!
  2829. engine->ResetPVS( pvs, pvssize );
  2830. g_pToolFrameworkServer->PreSetupVisibility();
  2831. // Find the client's PVS
  2832. CBaseEntity *pVE = NULL;
  2833. if ( pViewEntity )
  2834. {
  2835. pVE = GetContainingEntity( pViewEntity );
  2836. // If we have a viewentity, it overrides the player's origin
  2837. if ( pVE )
  2838. {
  2839. org = pVE->EyePosition();
  2840. engine->AddOriginToPVS( org );
  2841. }
  2842. }
  2843. float fovDistanceAdjustFactor = 1;
  2844. CUtlVector< Vector > areaPortalOrigins;
  2845. CBasePlayer *pPlayer = ( CBasePlayer * )GetContainingEntity( pClient );
  2846. if ( pPlayer )
  2847. {
  2848. if ( !pVE )
  2849. {
  2850. org = pPlayer->EyePosition();
  2851. }
  2852. pPlayer->SetupVisibility( pVE, pvs, pvssize );
  2853. UTIL_SetClientVisibilityPVS( pClient, pvs, pvssize );
  2854. fovDistanceAdjustFactor = pPlayer->GetFOVDistanceAdjustFactorForNetworking();
  2855. areaPortalOrigins.AddToTail( org );
  2856. // Merge in areaportal "window" states from from split screen players by passing in the extra PVS origins!!!
  2857. CUtlVector< CHandle< CBasePlayer > > &list = pPlayer->GetSplitScreenAndPictureInPicturePlayers();
  2858. for ( int i = 0; i < list.Count(); ++i )
  2859. {
  2860. CBasePlayer *pl = list[ i ];
  2861. if ( !pl )
  2862. {
  2863. continue;
  2864. }
  2865. org = pl->EyePosition();
  2866. areaPortalOrigins.AddToTail( org );
  2867. }
  2868. }
  2869. else
  2870. {
  2871. Warning( "ClientSetupVisibility: No entity for edict!\n" );
  2872. areaPortalOrigins.AddToTail( org );
  2873. }
  2874. unsigned char portalBits[MAX_AREA_PORTAL_STATE_BYTES];
  2875. memset( portalBits, 0, sizeof( portalBits ) );
  2876. int portalNums[512];
  2877. int isOpen[512];
  2878. int iOutPortal = 0;
  2879. for( unsigned short i = g_AreaPortals.Head(); i != g_AreaPortals.InvalidIndex(); i = g_AreaPortals.Next(i) )
  2880. {
  2881. CFuncAreaPortalBase *pCur = g_AreaPortals[i];
  2882. bool bIsOpenOnClient = true;
  2883. // Update our array of which portals are open and flush it if necessary.
  2884. portalNums[iOutPortal] = pCur->m_portalNumber;
  2885. isOpen[iOutPortal] = pCur->UpdateVisibility( areaPortalOrigins, fovDistanceAdjustFactor, bIsOpenOnClient );
  2886. #ifdef PORTAL
  2887. // If the client doesn't need this open, test if portals might need this area portal open
  2888. if ( isOpen[iOutPortal] == 0 )
  2889. {
  2890. isOpen[iOutPortal] = TestAreaPortalVisibilityThroughPortals( pCur, pViewEntity, pvs, pvssize );
  2891. bIsOpenOnClient |= ( isOpen[iOutPortal] != 0 );
  2892. }
  2893. #endif
  2894. ++iOutPortal;
  2895. if ( iOutPortal >= ARRAYSIZE( portalNums ) )
  2896. {
  2897. engine->SetAreaPortalStates( portalNums, isOpen, iOutPortal );
  2898. iOutPortal = 0;
  2899. }
  2900. // Version 0 portals (ie: shipping Half-Life 2 era) are always treated as open
  2901. // for purposes of the m_chAreaPortalBits array on the client.
  2902. if ( pCur->m_iPortalVersion == 0 )
  2903. bIsOpenOnClient = true;
  2904. if ( bIsOpenOnClient )
  2905. {
  2906. if ( pCur->m_portalNumber < 0 )
  2907. continue;
  2908. else if ( pCur->m_portalNumber >= sizeof( portalBits ) * 8 )
  2909. Error( "ClientSetupVisibility: portal number (%d) too large", pCur->m_portalNumber );
  2910. else
  2911. portalBits[pCur->m_portalNumber >> 3] |= (1 << (pCur->m_portalNumber & 7));
  2912. }
  2913. }
  2914. // Flush the remaining areaportal states.
  2915. engine->SetAreaPortalStates( portalNums, isOpen, iOutPortal );
  2916. // Update the area bits that get sent to the client.
  2917. Assert( pPlayer );
  2918. if ( pPlayer )
  2919. {
  2920. pPlayer->m_Local.UpdateAreaBits( pPlayer, portalBits );
  2921. }
  2922. #ifdef PORTAL
  2923. // *After* the player's view has updated its area bits, add on any other areas seen by portals
  2924. CPortal_Player* pPortalPlayer = dynamic_cast<CPortal_Player*>( pPlayer );
  2925. if ( pPortalPlayer )
  2926. {
  2927. pPortalPlayer->UpdatePortalViewAreaBits( pvs, pvssize );
  2928. }
  2929. #endif //PORTAL
  2930. }
  2931. //-----------------------------------------------------------------------------
  2932. // Purpose:
  2933. // Input : *player -
  2934. // *buf -
  2935. // numcmds -
  2936. // totalcmds -
  2937. // dropped_packets -
  2938. // ignore -
  2939. // paused -
  2940. // Output : float
  2941. //-----------------------------------------------------------------------------
  2942. #define CMD_MAXBACKUP 64
  2943. static ConVar sv_max_usercmd_move_magnitude( "sv_max_usercmd_move_magnitude", "1000", 0, "Maximum move magnitude that can be requested by client." );
  2944. float CServerGameClients::ProcessUsercmds( edict_t *player, bf_read *buf, int numcmds, int totalcmds,
  2945. int dropped_packets, bool ignore, bool paused )
  2946. {
  2947. int i;
  2948. CUserCmd *from, *to;
  2949. // We track last three command in case we drop some
  2950. // packets but get them back.
  2951. CUserCmd cmds[ CMD_MAXBACKUP ];
  2952. CUserCmd cmdNull; // For delta compression
  2953. Assert( numcmds >= 0 );
  2954. Assert( ( totalcmds - numcmds ) >= 0 );
  2955. CBasePlayer *pPlayer = NULL;
  2956. CBaseEntity *pEnt = CBaseEntity::Instance(player);
  2957. if ( pEnt && pEnt->IsPlayer() )
  2958. {
  2959. pPlayer = static_cast< CBasePlayer * >( pEnt );
  2960. }
  2961. // Too many commands?
  2962. if ( totalcmds < 0 || totalcmds >= ( CMD_MAXBACKUP - 1 ) || numcmds < 0 || numcmds > totalcmds )
  2963. {
  2964. const char *name = "unknown";
  2965. if ( pPlayer )
  2966. {
  2967. name = pPlayer->GetPlayerName();
  2968. }
  2969. Msg("CBasePlayer::ProcessUsercmds: too many cmds %i sent for player %s\n", totalcmds, name );
  2970. // FIXME: Need a way to drop the client from here
  2971. //SV_DropClient ( host_client, false, "CMD_MAXBACKUP hit" );
  2972. buf->SetOverflowFlag();
  2973. return 0.0f;
  2974. }
  2975. // Initialize for reading delta compressed usercmds
  2976. cmdNull.Reset();
  2977. from = &cmdNull;
  2978. for ( i = totalcmds - 1; i >= 0; i-- )
  2979. {
  2980. to = &cmds[ i ];
  2981. ReadUsercmd( buf, to, from );
  2982. from = to;
  2983. // Validate UserCmd that we parsed:
  2984. if ( !to->viewangles.IsValid() )
  2985. {
  2986. to->viewangles.Init();
  2987. }
  2988. if ( !IsEntityQAngleReasonable( to->viewangles ) )
  2989. { // Make sure we don't crash the server even if we ban the dude
  2990. to->viewangles.Init();
  2991. }
  2992. if ( !IsFinite( to->forwardmove ) ||
  2993. !IsFinite( to->sidemove ) ||
  2994. !IsFinite( to->upmove ) )
  2995. {
  2996. to->forwardmove = 0;
  2997. to->sidemove = 0;
  2998. to->upmove = 0;
  2999. }
  3000. if ( ( fabs( to->forwardmove ) > sv_max_usercmd_move_magnitude.GetFloat() ) ||
  3001. ( fabs( to->sidemove ) > sv_max_usercmd_move_magnitude.GetFloat() ) ||
  3002. ( fabs( to->upmove ) > sv_max_usercmd_move_magnitude.GetFloat() ) )
  3003. {
  3004. to->forwardmove = 0;
  3005. to->sidemove = 0;
  3006. to->upmove = 0;
  3007. }
  3008. }
  3009. // Client not fully connected or server has gone inactive or is paused, just ignore
  3010. if ( ignore || !pPlayer )
  3011. {
  3012. return 0.0f;
  3013. }
  3014. MDLCACHE_CRITICAL_SECTION();
  3015. pPlayer->ProcessUsercmds( cmds, numcmds, totalcmds, dropped_packets, paused );
  3016. return TICK_INTERVAL;
  3017. }
  3018. void CServerGameClients::PostClientMessagesSent( void )
  3019. {
  3020. VPROF("CServerGameClients::PostClient");
  3021. SNPROF("CServerGameClients::PostClient");
  3022. gEntList.PostClientMessagesSent();
  3023. }
  3024. // Sets the client index for the client who typed the command into his/her console
  3025. void CServerGameClients::SetCommandClient( int index )
  3026. {
  3027. g_nCommandClientIndex = index;
  3028. }
  3029. int CServerGameClients::GetReplayDelay( edict_t *pEdict, int &entity )
  3030. {
  3031. CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
  3032. if ( !pPlayer )
  3033. return 0;
  3034. entity = pPlayer->GetReplayEntity();
  3035. return pPlayer->GetDelayTicks();
  3036. }
  3037. //-----------------------------------------------------------------------------
  3038. // The client's userinfo data lump has changed
  3039. //-----------------------------------------------------------------------------
  3040. void CServerGameClients::ClientEarPosition( edict_t *pEdict, Vector *pEarOrigin )
  3041. {
  3042. CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
  3043. if (pPlayer)
  3044. {
  3045. *pEarOrigin = pPlayer->EarPosition();
  3046. }
  3047. else
  3048. {
  3049. // Shouldn't happen
  3050. Assert(0);
  3051. *pEarOrigin = vec3_origin;
  3052. }
  3053. }
  3054. bool CServerGameClients::ClientReplayEvent( edict_t *pEdict, const ClientReplayEventParams_t &params )
  3055. {
  3056. CCSPlayer *pPlayer = ( CCSPlayer * )CBaseEntity::Instance( pEdict );
  3057. if ( pPlayer )
  3058. {
  3059. return pPlayer->StartHltvReplayEvent( params );
  3060. }
  3061. else
  3062. {
  3063. return 0.0f;
  3064. }
  3065. }
  3066. //-----------------------------------------------------------------------------
  3067. // Purpose:
  3068. // Input : *player -
  3069. // Output : CPlayerState
  3070. //-----------------------------------------------------------------------------
  3071. CPlayerState *CServerGameClients::GetPlayerState( edict_t *player )
  3072. {
  3073. // Is the client spawned yet?
  3074. if ( !player || !player->GetUnknown() )
  3075. return NULL;
  3076. CBasePlayer *pBasePlayer = ( CBasePlayer * )CBaseEntity::Instance( player );
  3077. if ( !pBasePlayer )
  3078. return NULL;
  3079. return &pBasePlayer->pl;
  3080. }
  3081. //-----------------------------------------------------------------------------
  3082. // Purpose: Anything this game .dll wants to add to the bug reporter text (e.g., the entity/model under the picker crosshair)
  3083. // can be added here
  3084. // Input : *buf -
  3085. // buflen -
  3086. //-----------------------------------------------------------------------------
  3087. void CServerGameClients::GetBugReportInfo( char *buf, int buflen )
  3088. {
  3089. recentNPCSpeech_t speech[ SPEECH_LIST_MAX_SOUNDS ];
  3090. int num;
  3091. int i;
  3092. buf[ 0 ] = 0;
  3093. if ( gpGlobals->maxClients == 1 )
  3094. {
  3095. CBaseEntity *ent = UTIL_PlayerByIndex(1) ? UTIL_PlayerByIndex(1)->FindPickerEntity() : NULL;
  3096. if ( ent )
  3097. {
  3098. Q_snprintf( buf, buflen, "Picker %i/%s - ent %s model %s\n",
  3099. ent->entindex(),
  3100. ent->GetClassname(),
  3101. STRING( ent->GetEntityName() ),
  3102. STRING(ent->GetModelName()) );
  3103. }
  3104. // get any sounds that were spoken by NPCs recently
  3105. num = GetRecentNPCSpeech( speech );
  3106. if ( num > 0 )
  3107. {
  3108. Q_snprintf( buf, buflen, "%sRecent NPC speech:\n", buf );
  3109. for( i = 0; i < num; i++ )
  3110. {
  3111. Q_snprintf( buf, buflen, "%s time: %6.3f sound name: %s scene: %s\n", buf, speech[ i ].time, speech[ i ].name, speech[ i ].sceneName );
  3112. }
  3113. Q_snprintf( buf, buflen, "%sCurrent time: %6.3f\n", buf, gpGlobals->curtime );
  3114. }
  3115. }
  3116. #if defined ( PORTAL2 )
  3117. int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
  3118. if( iPortalCount != 0 )
  3119. {
  3120. CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base();
  3121. Q_snprintf( buf, buflen, "%sPortal Locations:\n", buf );
  3122. for( int i = 0; i != iPortalCount; ++i )
  3123. {
  3124. CProp_Portal *pTempPortal = pPortals[i];
  3125. Q_snprintf( buf, buflen, "%slinkid:%d loc: %f %f %f\n", buf, pTempPortal->GetLinkageGroup(), XYZ( pTempPortal->GetAbsOrigin() ) );
  3126. }
  3127. }
  3128. #endif
  3129. }
  3130. //-----------------------------------------------------------------------------
  3131. // Purpose: A player sent a voice packet
  3132. //-----------------------------------------------------------------------------
  3133. void CServerGameClients::ClientVoice( edict_t *pEdict )
  3134. {
  3135. CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
  3136. if (pPlayer)
  3137. {
  3138. pPlayer->OnVoiceTransmit();
  3139. // Notify the voice listener that we've spoken
  3140. PlayerVoiceListener().AddPlayerSpeakTime( pPlayer );
  3141. }
  3142. }
  3143. int CServerGameClients::GetMaxSplitscreenPlayers()
  3144. {
  3145. return MAX_SPLITSCREEN_PLAYERS;
  3146. }
  3147. int CServerGameClients::GetMaxHumanPlayers()
  3148. {
  3149. if ( g_pGameRules )
  3150. {
  3151. return g_pGameRules->GetMaxHumanPlayers();
  3152. }
  3153. return -1;
  3154. }
  3155. // The client has submitted a keyvalues command
  3156. void CServerGameClients::ClientCommandKeyValues( edict_t *pEntity, KeyValues *pKeyValues )
  3157. {
  3158. if ( !pKeyValues )
  3159. return;
  3160. g_pGameRules->ClientCommandKeyValues( pEntity, pKeyValues );
  3161. char const *szCommand = pKeyValues->GetName();
  3162. if ( FStrEq( szCommand, "OnPlayerAward" ) )
  3163. {
  3164. // Player received an award
  3165. // On server, send to all connected clients
  3166. UTIL_SendClientCommandKVToPlayer( pKeyValues->MakeCopy() );
  3167. }
  3168. }
  3169. // Server override for supplied client name (implemented in cs_gameinterface)
  3170. // const char * CServerGameClients::ClientNameHandler( uint64 xuid, const char *pchName )
  3171. // {
  3172. // return pchName;
  3173. // }
  3174. //-----------------------------------------------------------------------------
  3175. // Purpose:
  3176. //-----------------------------------------------------------------------------
  3177. static bf_write *g_pMsgBuffer = NULL;
  3178. void EntityMessageBegin( CBaseEntity * entity, bool reliable /*= false*/ )
  3179. {
  3180. Assert( !g_pMsgBuffer );
  3181. Assert ( entity );
  3182. g_pMsgBuffer = engine->EntityMessageBegin( entity->entindex(), entity->GetServerClass(), reliable );
  3183. }
  3184. void MessageEnd( void )
  3185. {
  3186. Assert( g_pMsgBuffer );
  3187. engine->MessageEnd();
  3188. g_pMsgBuffer = NULL;
  3189. }
  3190. void MessageWriteByte( int iValue)
  3191. {
  3192. if (!g_pMsgBuffer)
  3193. Error( "WRITE_BYTE called with no active message\n" );
  3194. g_pMsgBuffer->WriteByte( iValue );
  3195. }
  3196. void MessageWriteChar( int iValue)
  3197. {
  3198. if (!g_pMsgBuffer)
  3199. Error( "WRITE_CHAR called with no active message\n" );
  3200. g_pMsgBuffer->WriteChar( iValue );
  3201. }
  3202. void MessageWriteShort( int iValue)
  3203. {
  3204. if (!g_pMsgBuffer)
  3205. Error( "WRITE_SHORT called with no active message\n" );
  3206. g_pMsgBuffer->WriteShort( iValue );
  3207. }
  3208. void MessageWriteWord( int iValue )
  3209. {
  3210. if (!g_pMsgBuffer)
  3211. Error( "WRITE_WORD called with no active message\n" );
  3212. g_pMsgBuffer->WriteWord( iValue );
  3213. }
  3214. void MessageWriteLong( int iValue)
  3215. {
  3216. if (!g_pMsgBuffer)
  3217. Error( "WriteLong called with no active message\n" );
  3218. g_pMsgBuffer->WriteLong( iValue );
  3219. }
  3220. void MessageWriteFloat( float flValue)
  3221. {
  3222. if (!g_pMsgBuffer)
  3223. Error( "WriteFloat called with no active message\n" );
  3224. g_pMsgBuffer->WriteFloat( flValue );
  3225. }
  3226. void MessageWriteAngle( float flValue)
  3227. {
  3228. if (!g_pMsgBuffer)
  3229. Error( "WriteAngle called with no active message\n" );
  3230. g_pMsgBuffer->WriteBitAngle( flValue, 8 );
  3231. }
  3232. void MessageWriteCoord( float flValue)
  3233. {
  3234. if (!g_pMsgBuffer)
  3235. Error( "WriteCoord called with no active message\n" );
  3236. g_pMsgBuffer->WriteBitCoord( flValue );
  3237. }
  3238. void MessageWriteVec3Coord( const Vector& rgflValue)
  3239. {
  3240. if (!g_pMsgBuffer)
  3241. Error( "WriteVec3Coord called with no active message\n" );
  3242. g_pMsgBuffer->WriteBitVec3Coord( rgflValue );
  3243. }
  3244. void MessageWriteVec3Normal( const Vector& rgflValue)
  3245. {
  3246. if (!g_pMsgBuffer)
  3247. Error( "WriteVec3Normal called with no active message\n" );
  3248. g_pMsgBuffer->WriteBitVec3Normal( rgflValue );
  3249. }
  3250. void MessageWriteBitVecIntegral( const Vector& vecValue )
  3251. {
  3252. if (!g_pMsgBuffer)
  3253. Error( "MessageWriteBitVecIntegral called with no active message\n" );
  3254. for ( int i = 0; i < 3; ++i )
  3255. {
  3256. g_pMsgBuffer->WriteBitCoordMP( vecValue[ i ], kCW_Integral );
  3257. }
  3258. }
  3259. void MessageWriteAngles( const QAngle& rgflValue)
  3260. {
  3261. if (!g_pMsgBuffer)
  3262. Error( "WriteVec3Normal called with no active message\n" );
  3263. g_pMsgBuffer->WriteBitAngles( rgflValue );
  3264. }
  3265. void MessageWriteString( const char *sz )
  3266. {
  3267. if (!g_pMsgBuffer)
  3268. Error( "WriteString called with no active message\n" );
  3269. g_pMsgBuffer->WriteString( sz );
  3270. }
  3271. void MessageWriteEntity( int iValue)
  3272. {
  3273. if (!g_pMsgBuffer)
  3274. Error( "WriteEntity called with no active message\n" );
  3275. g_pMsgBuffer->WriteShort( iValue );
  3276. }
  3277. void MessageWriteEHandle( CBaseEntity *pEntity )
  3278. {
  3279. if (!g_pMsgBuffer)
  3280. Error( "WriteEHandle called with no active message\n" );
  3281. long iEncodedEHandle;
  3282. if( pEntity )
  3283. {
  3284. EHANDLE hEnt = pEntity;
  3285. int iSerialNum = hEnt.GetSerialNumber() & (1 << NUM_NETWORKED_EHANDLE_SERIAL_NUMBER_BITS) - 1;
  3286. iEncodedEHandle = hEnt.GetEntryIndex() | (iSerialNum << MAX_EDICT_BITS);
  3287. }
  3288. else
  3289. {
  3290. iEncodedEHandle = INVALID_NETWORKED_EHANDLE_VALUE;
  3291. }
  3292. g_pMsgBuffer->WriteLong( iEncodedEHandle );
  3293. }
  3294. // bitwise
  3295. void MessageWriteBool( bool bValue )
  3296. {
  3297. if (!g_pMsgBuffer)
  3298. Error( "WriteBool called with no active message\n" );
  3299. g_pMsgBuffer->WriteOneBit( bValue ? 1 : 0 );
  3300. }
  3301. void MessageWriteUBitLong( unsigned int data, int numbits )
  3302. {
  3303. if (!g_pMsgBuffer)
  3304. Error( "WriteUBitLong called with no active message\n" );
  3305. g_pMsgBuffer->WriteUBitLong( data, numbits );
  3306. }
  3307. void MessageWriteSBitLong( int data, int numbits )
  3308. {
  3309. if (!g_pMsgBuffer)
  3310. Error( "WriteSBitLong called with no active message\n" );
  3311. g_pMsgBuffer->WriteSBitLong( data, numbits );
  3312. }
  3313. void MessageWriteBits( const void *pIn, int nBits )
  3314. {
  3315. if (!g_pMsgBuffer)
  3316. Error( "WriteBits called with no active message\n" );
  3317. g_pMsgBuffer->WriteBits( pIn, nBits );
  3318. }
  3319. //-----------------------------------------------------------------------------
  3320. void SendUserMessage( IRecipientFilter& filter, int message, const ::google::protobuf::Message &msg )
  3321. {
  3322. engine->SendUserMessage( filter, message, msg );
  3323. }
  3324. class CServerDLLSharedAppSystems : public IServerDLLSharedAppSystems
  3325. {
  3326. public:
  3327. CServerDLLSharedAppSystems()
  3328. {
  3329. AddAppSystem( "soundemittersystem", SOUNDEMITTERSYSTEM_INTERFACE_VERSION );
  3330. AddAppSystem( "scenefilecache", SCENE_FILE_CACHE_INTERFACE_VERSION );
  3331. #ifdef INFESTED_DLL
  3332. AddAppSystem( "missionchooser", ASW_MISSION_CHOOSER_VERSION );
  3333. #endif
  3334. }
  3335. virtual int Count()
  3336. {
  3337. return m_Systems.Count();
  3338. }
  3339. virtual char const *GetDllName( int idx )
  3340. {
  3341. return m_Systems[ idx ].m_pModuleName;
  3342. }
  3343. virtual char const *GetInterfaceName( int idx )
  3344. {
  3345. return m_Systems[ idx ].m_pInterfaceName;
  3346. }
  3347. private:
  3348. void AddAppSystem( char const *moduleName, char const *interfaceName )
  3349. {
  3350. AppSystemInfo_t sys;
  3351. sys.m_pModuleName = moduleName;
  3352. sys.m_pInterfaceName = interfaceName;
  3353. m_Systems.AddToTail( sys );
  3354. }
  3355. CUtlVector< AppSystemInfo_t > m_Systems;
  3356. };
  3357. EXPOSE_SINGLE_INTERFACE( CServerDLLSharedAppSystems, IServerDLLSharedAppSystems, SERVER_DLL_SHARED_APPSYSTEMS );
  3358. //-----------------------------------------------------------------------------
  3359. //
  3360. //-----------------------------------------------------------------------------
  3361. void CServerGameTags::GetTaggedConVarList( KeyValues *pCvarTagList )
  3362. {
  3363. if ( pCvarTagList && g_pGameRules )
  3364. {
  3365. g_pGameRules->GetTaggedConVarList( pCvarTagList );
  3366. }
  3367. }
  3368. #if defined(PORTAL2)
  3369. CON_COMMAND_F( give_promo_helmet, "Gives the gamestop promo helmets for coop bots. Requires a respawn or changelevel to start showing.", FCVAR_DEVELOPMENTONLY )
  3370. {
  3371. g_nPortal2PromoFlags |= PORTAL2_PROMO_HELMETS;
  3372. }
  3373. #endif