game/server/gameinterface.cpp
changeset 0 5476a15192a9
child 5 9002caf71a06
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/game/server/gameinterface.cpp	Thu Jul 22 01:46:14 2010 -0500
     1.3 @@ -0,0 +1,3409 @@
     1.4 +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
     1.5 +//
     1.6 +// Purpose: encapsulates and implements all the accessing of the game dll from external
     1.7 +//			sources (only the engine at the time of writing)
     1.8 +//			This files ONLY contains functions and data necessary to build an interface
     1.9 +//			to external modules
    1.10 +//===========================================================================//
    1.11 +
    1.12 +#include "cbase.h"
    1.13 +#include "gamestringpool.h"
    1.14 +#include "mapentities_shared.h"
    1.15 +#include "game.h"
    1.16 +#include "entityapi.h"
    1.17 +#include "client.h"
    1.18 +#include "saverestore.h"
    1.19 +#include "entitylist.h"
    1.20 +#include "gamerules.h"
    1.21 +#include "soundent.h"
    1.22 +#include "player.h"
    1.23 +#include "server_class.h"
    1.24 +#include "ai_node.h"
    1.25 +#include "ai_link.h"
    1.26 +#include "ai_saverestore.h"
    1.27 +#include "ai_networkmanager.h"
    1.28 +#include "ndebugoverlay.h"
    1.29 +#include "ivoiceserver.h"
    1.30 +#include <stdarg.h>
    1.31 +#include "movehelper_server.h"
    1.32 +#include "networkstringtable_gamedll.h"
    1.33 +#include "filesystem.h"
    1.34 +#include "func_areaportalwindow.h"
    1.35 +#include "igamesystem.h"
    1.36 +#include "init_factory.h"
    1.37 +#include "vstdlib/random.h"
    1.38 +#include "env_wind_shared.h"
    1.39 +#include "engine/IEngineSound.h"
    1.40 +#include "ispatialpartition.h"
    1.41 +#include "textstatsmgr.h"
    1.42 +#include "bitbuf.h"
    1.43 +#include "saverestoretypes.h"
    1.44 +#include "physics_saverestore.h"
    1.45 +#include "achievement_saverestore.h"
    1.46 +#include "tier0/vprof.h"
    1.47 +#include "effect_dispatch_data.h"
    1.48 +#include "engine/IStaticPropMgr.h"
    1.49 +#include "TemplateEntities.h"
    1.50 +#include "ai_speech.h"
    1.51 +#include "soundenvelope.h"
    1.52 +#include "usermessages.h"
    1.53 +#include "physics.h"
    1.54 +#include "igameevents.h"
    1.55 +#include "EventLog.h"
    1.56 +#include "datacache/idatacache.h"
    1.57 +#include "engine/ivdebugoverlay.h"
    1.58 +#include "shareddefs.h"
    1.59 +#include "props.h"
    1.60 +#include "timedeventmgr.h"
    1.61 +#include "gameinterface.h"
    1.62 +#include "eventqueue.h"
    1.63 +#include "hltvdirector.h"
    1.64 +#if defined( REPLAY_ENABLED )
    1.65 +#include "replaydirector.h"
    1.66 +#endif
    1.67 +#include "SoundEmitterSystem/isoundemittersystembase.h"
    1.68 +#include "nav_mesh.h"
    1.69 +#include "AI_ResponseSystem.h"
    1.70 +#include "saverestore_stringtable.h"
    1.71 +#include "util.h"
    1.72 +#include "tier0/icommandline.h"
    1.73 +#include "datacache/imdlcache.h"
    1.74 +#include "engine/iserverplugin.h"
    1.75 +#include "env_debughistory.h"
    1.76 +#include "util_shared.h"
    1.77 +#include "player_voice_listener.h"
    1.78 +
    1.79 +#ifdef _WIN32
    1.80 +#include "ienginevgui.h"
    1.81 +#include "vgui_gamedll_int.h"
    1.82 +#include "vgui_controls/AnimationController.h"
    1.83 +#endif
    1.84 +
    1.85 +#include "ragdoll_shared.h"
    1.86 +#include "toolframework/iserverenginetools.h"
    1.87 +#include "sceneentity.h"
    1.88 +#include "appframework/IAppSystemGroup.h"
    1.89 +#include "scenefilecache/ISceneFileCache.h"
    1.90 +#include "tier2/tier2.h"
    1.91 +#include "particles/particles.h"
    1.92 +#include "GameStats.h"
    1.93 +#include "ixboxsystem.h"
    1.94 +#include "matchmaking/imatchframework.h"
    1.95 +#include "querycache.h"
    1.96 +#include "particle_parse.h"
    1.97 +#include "steam/steam_gameserver.h"
    1.98 +#include "tier3/tier3.h"
    1.99 +#include "serverbenchmark_base.h"
   1.100 +#include "vscript/ivscript.h"
   1.101 +#include "foundryhelpers_server.h"
   1.102 +#include "entity_tools_server.h"
   1.103 +#include "foundry/iserverfoundry.h"
   1.104 +#include "point_template.h"
   1.105 +#include "../../engine/iblackbox.h"
   1.106 +#include "vstdlib/jobthread.h"
   1.107 +#include "vscript_server.h"
   1.108 +#include "tier2/tier2_logging.h"
   1.109 +#include "fmtstr.h"
   1.110 +
   1.111 +#ifdef INFESTED_DLL
   1.112 +#include "missionchooser/iasw_mission_chooser.h"
   1.113 +#include "missionchooser/iasw_mission_chooser_source.h"
   1.114 +#include "matchmaking/swarm/imatchext_swarm.h"
   1.115 +#include "asw_gamerules.h"
   1.116 +#endif
   1.117 +
   1.118 +
   1.119 +
   1.120 +
   1.121 +
   1.122 +
   1.123 +
   1.124 +#ifdef _WIN32
   1.125 +#include "IGameUIFuncs.h"
   1.126 +#endif
   1.127 +
   1.128 +extern IToolFrameworkServer *g_pToolFrameworkServer;
   1.129 +extern IParticleSystemQuery *g_pParticleSystemQuery;
   1.130 +
   1.131 +extern ConVar commentary;
   1.132 +
   1.133 +// this context is not available on dedicated servers
   1.134 +// WARNING! always check if interfaces are available before using
   1.135 +#if !defined(NO_STEAM)
   1.136 +static CSteamAPIContext s_SteamAPIContext;	
   1.137 +CSteamAPIContext *steamapicontext = &s_SteamAPIContext;
   1.138 +
   1.139 +// this context is not available on a pure client connected to a remote server.
   1.140 +// WARNING! always check if interfaces are available before using
   1.141 +static CSteamGameServerAPIContext s_SteamGameServerAPIContext;
   1.142 +CSteamGameServerAPIContext *steamgameserverapicontext = &s_SteamGameServerAPIContext;
   1.143 +#endif
   1.144 +
   1.145 +
   1.146 +IUploadGameStats *gamestatsuploader = NULL;
   1.147 +
   1.148 +// memdbgon must be the last include file in a .cpp file!!!
   1.149 +#include "tier0/memdbgon.h"
   1.150 +
   1.151 +CTimedEventMgr g_NetworkPropertyEventMgr;
   1.152 +
   1.153 +ISaveRestoreBlockHandler *GetEventQueueSaveRestoreBlockHandler();
   1.154 +ISaveRestoreBlockHandler *GetCommentarySaveRestoreBlockHandler();
   1.155 +
   1.156 +CUtlLinkedList<CMapEntityRef, unsigned short> g_MapEntityRefs;
   1.157 +
   1.158 +// Engine interfaces.
   1.159 +IVEngineServer	*engine = NULL;
   1.160 +IVoiceServer	*g_pVoiceServer = NULL;
   1.161 +#if !defined(_STATIC_LINKED)
   1.162 +IFileSystem		*filesystem = NULL;
   1.163 +#else
   1.164 +extern IFileSystem *filesystem;
   1.165 +#endif
   1.166 +INetworkStringTableContainer *networkstringtable = NULL;
   1.167 +IStaticPropMgrServer *staticpropmgr = NULL;
   1.168 +IUniformRandomStream *random = NULL;
   1.169 +IEngineSound *enginesound = NULL;
   1.170 +ISpatialPartition *partition = NULL;
   1.171 +IVModelInfo *modelinfo = NULL;
   1.172 +IEngineTrace *enginetrace = NULL;
   1.173 +IFileLoggingListener *filelogginglistener = NULL;
   1.174 +IGameEventManager2 *gameeventmanager = NULL;
   1.175 +IDataCache *datacache = NULL;
   1.176 +IVDebugOverlay * debugoverlay = NULL;
   1.177 +ISoundEmitterSystemBase *soundemitterbase = NULL;
   1.178 +IServerPluginHelpers *serverpluginhelpers = NULL;
   1.179 +#ifdef SERVER_USES_VGUI
   1.180 +IEngineVGui *enginevgui = NULL;
   1.181 +#endif // SERVER_USES_VGUI
   1.182 +IServerEngineTools *serverenginetools = NULL;
   1.183 +IServerFoundry *serverfoundry = NULL;
   1.184 +ISceneFileCache *scenefilecache = NULL;
   1.185 +#ifdef SERVER_USES_VGUI
   1.186 +IGameUIFuncs *gameuifuncs = NULL;
   1.187 +#endif // SERVER_USES_VGUI
   1.188 +IXboxSystem *xboxsystem = NULL;	// Xbox 360 only
   1.189 +IScriptManager *scriptmanager = NULL;
   1.190 +IBlackBox *blackboxrecorder = NULL;
   1.191 +
   1.192 +#ifdef INFESTED_DLL
   1.193 +IASW_Mission_Chooser *missionchooser = NULL;
   1.194 +IMatchExtSwarm *g_pMatchExtSwarm = NULL;
   1.195 +#endif
   1.196 +
   1.197 +IGameSystem *SoundEmitterSystem();
   1.198 +void SoundSystemPreloadSounds( void );
   1.199 +
   1.200 +bool ModelSoundsCacheInit();
   1.201 +void ModelSoundsCacheShutdown();
   1.202 +
   1.203 +void SceneManager_ClientActive( CBasePlayer *player );
   1.204 +
   1.205 +class IMaterialSystem;
   1.206 +class IStudioRender;
   1.207 +
   1.208 +#ifdef _DEBUG
   1.209 +static ConVar s_UseNetworkVars( "UseNetworkVars", "1", FCVAR_CHEAT, "For profiling, toggle network vars." );
   1.210 +#endif
   1.211 +
   1.212 +extern ConVar sv_noclipduringpause;
   1.213 +ConVar sv_massreport( "sv_massreport", "0" );
   1.214 +ConVar sv_force_transmit_ents( "sv_force_transmit_ents", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Will transmit all entities to client, regardless of PVS conditions (will still skip based on transmit flags, however)." );
   1.215 +
   1.216 +ConVar sv_autosave( "sv_autosave", "1", 0, "Set to 1 to autosave game on level transition. Does not affect autosave triggers." );
   1.217 +ConVar *sv_maxreplay = NULL;
   1.218 +
   1.219 +static ConVar  *g_pcv_commentary = NULL;
   1.220 +static ConVar *g_pcv_ThreadMode = NULL;
   1.221 +
   1.222 +#if !defined(NO_STEAM)
   1.223 +//-----------------------------------------------------------------------------
   1.224 +// Purpose: singleton accessor
   1.225 +//-----------------------------------------------------------------------------
   1.226 +static CSteam3Server s_Steam3Server;
   1.227 +CSteam3Server  &Steam3Server()
   1.228 +{
   1.229 +	return s_Steam3Server;
   1.230 +}
   1.231 +
   1.232 +//-----------------------------------------------------------------------------
   1.233 +// Purpose: Constructor
   1.234 +//-----------------------------------------------------------------------------
   1.235 +CSteam3Server::CSteam3Server() 
   1.236 +{
   1.237 +	m_bInitialized = false;
   1.238 +}
   1.239 +#endif
   1.240 +
   1.241 +// String tables
   1.242 +INetworkStringTable *g_pStringTableParticleEffectNames = NULL;
   1.243 +INetworkStringTable *g_pStringTableEffectDispatch = NULL;
   1.244 +INetworkStringTable *g_pStringTableVguiScreen = NULL;
   1.245 +INetworkStringTable *g_pStringTableMaterials = NULL;
   1.246 +INetworkStringTable *g_pStringTableInfoPanel = NULL;
   1.247 +INetworkStringTable *g_pStringTableClientSideChoreoScenes = NULL;
   1.248 +INetworkStringTable *g_pStringTableExtraParticleFiles = NULL;
   1.249 +
   1.250 +CStringTableSaveRestoreOps g_VguiScreenStringOps;
   1.251 +
   1.252 +// Holds global variables shared between engine and game.
   1.253 +CGlobalVars *gpGlobals;
   1.254 +static int		g_nCommandClientIndex = 0;
   1.255 +
   1.256 +// The chapter number of the current
   1.257 +static int		g_nCurrentChapterIndex = -1;
   1.258 +
   1.259 +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)." );
   1.260 +
   1.261 +static ClientPutInServerOverrideFn g_pClientPutInServerOverride = NULL;
   1.262 +static void UpdateChapterRestrictions( const char *mapname );
   1.263 +
   1.264 +CSharedEdictChangeInfo *g_pSharedChangeInfo = NULL;
   1.265 +
   1.266 +IChangeInfoAccessor *CBaseEdict::GetChangeAccessor()
   1.267 +{
   1.268 +	return engine->GetChangeAccessor( (const edict_t *)this );
   1.269 +}
   1.270 +
   1.271 +const IChangeInfoAccessor *CBaseEdict::GetChangeAccessor() const
   1.272 +{
   1.273 +	return engine->GetChangeAccessor( (const edict_t *)this );
   1.274 +}
   1.275 +
   1.276 +const char *GetHintTypeDescription( CAI_Hint *pHint );
   1.277 +
   1.278 +void ClientPutInServerOverride( ClientPutInServerOverrideFn fn )
   1.279 +{
   1.280 +	g_pClientPutInServerOverride = fn;
   1.281 +}
   1.282 +
   1.283 +ConVar ai_post_frame_navigation( "ai_post_frame_navigation", "0" );
   1.284 +class CPostFrameNavigationHook;
   1.285 +extern CPostFrameNavigationHook *PostFrameNavigationSystem( void );
   1.286 +
   1.287 +static bool g_bHeadTrackingEnabled = false;
   1.288 +
   1.289 +bool IsHeadTrackingEnabled()
   1.290 +{
   1.291 +#if defined( HL2_DLL )
   1.292 +	return g_bHeadTrackingEnabled;
   1.293 +#else
   1.294 +	return false;
   1.295 +#endif
   1.296 +}
   1.297 +
   1.298 +//-----------------------------------------------------------------------------
   1.299 +// Purpose: 
   1.300 +// Output : int
   1.301 +//-----------------------------------------------------------------------------
   1.302 +int UTIL_GetCommandClientIndex( void )
   1.303 +{
   1.304 +	// -1 == unknown,dedicated server console
   1.305 +	// 0  == player 1
   1.306 +
   1.307 +	// Convert to 1 based offset
   1.308 +	return (g_nCommandClientIndex+1);
   1.309 +}
   1.310 +
   1.311 +//-----------------------------------------------------------------------------
   1.312 +// Purpose: 
   1.313 +// Output : CBasePlayer
   1.314 +//-----------------------------------------------------------------------------
   1.315 +CBasePlayer *UTIL_GetCommandClient( void )
   1.316 +{
   1.317 +	int idx = UTIL_GetCommandClientIndex();
   1.318 +	if ( idx > 0 )
   1.319 +	{
   1.320 +		return UTIL_PlayerByIndex( idx );
   1.321 +	}
   1.322 +
   1.323 +	// HLDS console issued command
   1.324 +	return NULL;
   1.325 +}
   1.326 +
   1.327 +extern void InitializeCvars( void );
   1.328 +
   1.329 +float			GetFloorZ(const Vector &origin);
   1.330 +void			UpdateAllClientData( void );
   1.331 +void			DrawMessageEntities();
   1.332 +
   1.333 +#include "ai_network.h"
   1.334 +
   1.335 +// For now just using one big AI network
   1.336 +extern ConVar think_limit;
   1.337 +
   1.338 +
   1.339 +#if 0
   1.340 +//-----------------------------------------------------------------------------
   1.341 +// Purpose: Draw output overlays for any measure sections
   1.342 +// Input  : 
   1.343 +//-----------------------------------------------------------------------------
   1.344 +void DrawMeasuredSections(void)
   1.345 +{
   1.346 +	int		row = 1;
   1.347 +	float	rowheight = 0.025;
   1.348 +
   1.349 +	CMeasureSection *p = CMeasureSection::GetList();
   1.350 +	while ( p )
   1.351 +	{
   1.352 +		char str[256];
   1.353 +		Q_snprintf(str,sizeof(str),"%s",p->GetName());
   1.354 +		NDebugOverlay::ScreenText( 0.01,0.51+(row*rowheight),str, 255,255,255,255, 0.0 );
   1.355 +		
   1.356 +		Q_snprintf(str,sizeof(str),"%5.2f\n",p->GetTime().GetMillisecondsF());
   1.357 +		//Q_snprintf(str,sizeof(str),"%3.3f\n",p->GetTime().GetSeconds() * 100.0 / Plat_FloatTime());
   1.358 +		NDebugOverlay::ScreenText( 0.28,0.51+(row*rowheight),str, 255,255,255,255, 0.0 );
   1.359 +
   1.360 +		Q_snprintf(str,sizeof(str),"%5.2f\n",p->GetMaxTime().GetMillisecondsF());
   1.361 +		//Q_snprintf(str,sizeof(str),"%3.3f\n",p->GetTime().GetSeconds() * 100.0 / Plat_FloatTime());
   1.362 +		NDebugOverlay::ScreenText( 0.34,0.51+(row*rowheight),str, 255,255,255,255, 0.0 );
   1.363 +
   1.364 +
   1.365 +		row++;
   1.366 +
   1.367 +		p = p->GetNext();
   1.368 +	}
   1.369 +
   1.370 +	bool sort_reset = false;
   1.371 +
   1.372 +	// Time to redo sort?
   1.373 +	if ( measure_resort.GetFloat() > 0.0 &&
   1.374 +		Plat_FloatTime() >= CMeasureSection::m_dNextResort )
   1.375 +	{
   1.376 +		// Redo it
   1.377 +		CMeasureSection::SortSections();
   1.378 +		// Set next time
   1.379 +		CMeasureSection::m_dNextResort = Plat_FloatTime() + measure_resort.GetFloat();
   1.380 +		// Flag to reset sort accumulator, too
   1.381 +		sort_reset = true;
   1.382 +	}
   1.383 +
   1.384 +	// Iterate through the sections now
   1.385 +	p = CMeasureSection::GetList();
   1.386 +	while ( p )
   1.387 +	{
   1.388 +		// Update max 
   1.389 +		p->UpdateMax();
   1.390 +
   1.391 +		// Reset regular accum.
   1.392 +		p->Reset();
   1.393 +		// Reset sort accum less often
   1.394 +		if ( sort_reset )
   1.395 +		{
   1.396 +			p->SortReset();
   1.397 +		}
   1.398 +		p = p->GetNext();
   1.399 +	}
   1.400 +
   1.401 +}
   1.402 +#endif
   1.403 +
   1.404 +//-----------------------------------------------------------------------------
   1.405 +// Purpose:
   1.406 +//-----------------------------------------------------------------------------
   1.407 +void DrawAllDebugOverlays( void ) 
   1.408 +{
   1.409 +	NDebugOverlay::PurgeTextOverlays();
   1.410 +
   1.411 +	// If in debug select mode print the selection entities name or classname
   1.412 +	if (CBaseEntity::m_bInDebugSelect)
   1.413 +	{
   1.414 +		CBasePlayer* pPlayer =  UTIL_PlayerByIndex( CBaseEntity::m_nDebugPlayer );
   1.415 +
   1.416 +		if (pPlayer)
   1.417 +		{
   1.418 +			// First try to trace a hull to an entity
   1.419 +			CBaseEntity *pEntity = pPlayer->FindPickerEntity();
   1.420 +
   1.421 +			if ( pEntity ) 
   1.422 +			{
   1.423 +				pEntity->DrawDebugTextOverlays();
   1.424 +				pEntity->DrawBBoxOverlay();
   1.425 +				pEntity->SendDebugPivotOverlay();
   1.426 +			}
   1.427 +		}
   1.428 +	}
   1.429 +
   1.430 +	// --------------------------------------------------------
   1.431 +	//  Draw debug overlay lines 
   1.432 +	// --------------------------------------------------------
   1.433 +	UTIL_DrawOverlayLines();
   1.434 +
   1.435 +	// ------------------------------------------------------------------------
   1.436 +	// If in wc_edit mode draw a box to highlight which node I'm looking at
   1.437 +	// ------------------------------------------------------------------------
   1.438 +	if (engine->IsInEditMode())
   1.439 +	{
   1.440 +		CBasePlayer* pPlayer = UTIL_PlayerByIndex( CBaseEntity::m_nDebugPlayer );
   1.441 +		if (pPlayer) 
   1.442 +		{
   1.443 +			if (g_pAINetworkManager->GetEditOps()->m_bLinkEditMode)
   1.444 +			{
   1.445 +				CAI_Link* pAILink = pPlayer->FindPickerAILink();
   1.446 +				if (pAILink)
   1.447 +				{
   1.448 +					// For now just using one big AI network
   1.449 +					Vector startPos = g_pBigAINet->GetNode(pAILink->m_iSrcID)->GetPosition(g_pAINetworkManager->GetEditOps()->m_iHullDrawNum);
   1.450 +					Vector endPos	= g_pBigAINet->GetNode(pAILink->m_iDestID)->GetPosition(g_pAINetworkManager->GetEditOps()->m_iHullDrawNum);
   1.451 +					Vector linkDir	= startPos-endPos;
   1.452 +					float linkLen = VectorNormalize( linkDir );
   1.453 +					
   1.454 +					// Draw in green if link that's been turned off
   1.455 +					if (pAILink->m_LinkInfo & bits_LINK_OFF)
   1.456 +					{
   1.457 +						NDebugOverlay::BoxDirection(startPos, Vector(-4,-4,-4), Vector(-linkLen,4,4), linkDir, 0,255,0,40,0);
   1.458 +					}
   1.459 +					// Draw in a pukey yellowish green if link that's "bashable", which is off to everyone but a special door basher
   1.460 +					else if (pAILink->m_LinkInfo & bits_LINK_ASW_BASHABLE)
   1.461 +					{
   1.462 +						NDebugOverlay::BoxDirection(startPos, Vector(-4,-4,-4), Vector(-linkLen,4,4), linkDir, 40,255,0,40,0);
   1.463 +					}
   1.464 +					else
   1.465 +					{
   1.466 +						NDebugOverlay::BoxDirection(startPos, Vector(-4,-4,-4), Vector(-linkLen,4,4), linkDir, 255,0,0,40,0);
   1.467 +					}
   1.468 +				}
   1.469 +			}
   1.470 +			else
   1.471 +			{
   1.472 +				CAI_Node* pAINode;
   1.473 +				if (g_pAINetworkManager->GetEditOps()->m_bAirEditMode)
   1.474 +				{
   1.475 +					pAINode = pPlayer->FindPickerAINode(NODE_AIR);
   1.476 +				}
   1.477 +				else
   1.478 +				{
   1.479 +					pAINode = pPlayer->FindPickerAINode(NODE_GROUND);
   1.480 +				}
   1.481 +
   1.482 +				if (pAINode)
   1.483 +				{
   1.484 +					Vector vecPos = pAINode->GetPosition(g_pAINetworkManager->GetEditOps()->m_iHullDrawNum);
   1.485 +					NDebugOverlay::Box( vecPos, Vector(-8,-8,-8), Vector(8,8,8), 255,0,0,40,0);
   1.486 +
   1.487 +					if ( pAINode->GetHint() )
   1.488 +					{
   1.489 +						CBaseEntity *pEnt = (CBaseEntity *)pAINode->GetHint();
   1.490 +						if ( pEnt->GetEntityName() != NULL_STRING )
   1.491 +						{
   1.492 +							NDebugOverlay::Text( vecPos + Vector(0,0,6), STRING(pEnt->GetEntityName()), false, 0 );
   1.493 +						}
   1.494 +						NDebugOverlay::Text( vecPos, GetHintTypeDescription( pAINode->GetHint() ), false, 0 );
   1.495 +					}
   1.496 +				}
   1.497 +			}
   1.498 +			// ------------------------------------
   1.499 +			// If in air edit mode draw guide line
   1.500 +			// ------------------------------------
   1.501 +			if (g_pAINetworkManager->GetEditOps()->m_bAirEditMode)
   1.502 +			{
   1.503 +				UTIL_DrawPositioningOverlay(g_pAINetworkManager->GetEditOps()->m_flAirEditDistance);
   1.504 +			}
   1.505 +			else
   1.506 +			{
   1.507 +				NDebugOverlay::DrawGroundCrossHairOverlay();
   1.508 +			}
   1.509 +		}
   1.510 +	}
   1.511 +
   1.512 +	// For not just using one big AI Network
   1.513 +	if ( g_pAINetworkManager )
   1.514 +	{
   1.515 +		g_pAINetworkManager->GetEditOps()->DrawAINetworkOverlay();
   1.516 +	}
   1.517 +
   1.518 +	// PERFORMANCE: only do this in developer mode
   1.519 +	if ( g_pDeveloper->GetInt() )
   1.520 +	{
   1.521 +		// iterate through all objects for debug overlays
   1.522 +		const CEntInfo *pInfo = gEntList.FirstEntInfo();
   1.523 +
   1.524 +		for ( ;pInfo; pInfo = pInfo->m_pNext )
   1.525 +		{
   1.526 +			CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
   1.527 +			// HACKHACK: to flag off these calls
   1.528 +			if ( ent->m_debugOverlays || ent->m_pTimedOverlay )
   1.529 +			{
   1.530 +				MDLCACHE_CRITICAL_SECTION();
   1.531 +				ent->DrawDebugGeometryOverlays();
   1.532 +			}
   1.533 +		}
   1.534 +	}
   1.535 +
   1.536 +	if ( sv_massreport.GetInt() )
   1.537 +	{
   1.538 +		// iterate through all objects for debug overlays
   1.539 +		const CEntInfo *pInfo = gEntList.FirstEntInfo();
   1.540 +
   1.541 +		for ( ;pInfo; pInfo = pInfo->m_pNext )
   1.542 +		{
   1.543 +			CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
   1.544 +			if (!ent->VPhysicsGetObject())
   1.545 +				continue;
   1.546 +
   1.547 +			char tempstr[512];
   1.548 +			Q_snprintf(tempstr, sizeof(tempstr),"%s: Mass: %.2f kg / %.2f lb (%s)", 
   1.549 +				STRING(ent->GetModelName()), ent->VPhysicsGetObject()->GetMass(), 
   1.550 +				kg2lbs(ent->VPhysicsGetObject()->GetMass()), 
   1.551 +				GetMassEquivalent(ent->VPhysicsGetObject()->GetMass()));
   1.552 +			ent->EntityText(0, tempstr, 0);
   1.553 +		}
   1.554 +	}
   1.555 +
   1.556 +	// A hack to draw point_message entities w/o developer required
   1.557 +	DrawMessageEntities();
   1.558 +}
   1.559 +
   1.560 +// enable threading of init functions on x360
   1.561 +static ConVar sv_threaded_init("sv_threaded_init", IsX360() ? "1" : "0");
   1.562 +
   1.563 +static bool InitGameSystems( CreateInterfaceFn appSystemFactory )
   1.564 +{
   1.565 +	// The string system must init first + shutdown last
   1.566 +	IGameSystem::Add( GameStringSystem() );
   1.567 +
   1.568 +	// Physics must occur before the sound envelope manager
   1.569 +	IGameSystem::Add( PhysicsGameSystem() );
   1.570 +
   1.571 +	// Precache system must be next (requires physics game system)
   1.572 +	IGameSystem::Add( g_pPrecacheRegister );
   1.573 +
   1.574 +	// Used to service deferred navigation queries for NPCs
   1.575 +	IGameSystem::Add( (IGameSystem *) PostFrameNavigationSystem() );
   1.576 +
   1.577 +	// Add game log system
   1.578 +	IGameSystem::Add( GameLogSystem() );
   1.579 +
   1.580 +	// Add HLTV director 
   1.581 +	IGameSystem::Add( HLTVDirectorSystem() );
   1.582 +
   1.583 +#if defined( REPLAY_ENABLED )
   1.584 +	// Add Replay director
   1.585 +	IGameSystem::Add( ReplayDirectorSystem() );
   1.586 +#endif
   1.587 +
   1.588 +	// Add sound emitter
   1.589 +	IGameSystem::Add( SoundEmitterSystem() );
   1.590 +
   1.591 +#ifdef SERVER_USES_VGUI
   1.592 +	// Startup vgui
   1.593 +	if ( enginevgui )
   1.594 +	{
   1.595 +		if(!VGui_Startup( appSystemFactory ))
   1.596 +			return false;
   1.597 +	}
   1.598 +#endif // SERVER_USES_VGUI
   1.599 +
   1.600 +	// load Mod specific game events ( MUST be before InitAllSystems() so it can pickup the mod specific events)
   1.601 +	gameeventmanager->LoadEventsFromFile("resource/ModEvents.res");
   1.602 +
   1.603 +
   1.604 +
   1.605 +	if ( !IGameSystem::InitAllSystems() )
   1.606 +		return false;
   1.607 +
   1.608 +	// Due to dependencies, these are not autogamesystems
   1.609 +	if ( !ModelSoundsCacheInit() )
   1.610 +	{
   1.611 +		return false;
   1.612 +	}
   1.613 +
   1.614 +	// Parse the particle manifest file & register the effects within it
   1.615 +//	ParseParticleEffects( false );
   1.616 +
   1.617 +	InvalidateQueryCache();
   1.618 +
   1.619 +	// create the Navigation Mesh interface
   1.620 +	TheNavMesh = NavMeshFactory();
   1.621 +
   1.622 +	// init the gamestatsupload connection
   1.623 +	gamestatsuploader->InitConnection();
   1.624 +
   1.625 +	return true;
   1.626 +}
   1.627 +
   1.628 +CServerGameDLL g_ServerGameDLL;
   1.629 +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerGameDLL, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL, g_ServerGameDLL);
   1.630 +
   1.631 +bool CServerGameDLL::DLLInit( CreateInterfaceFn appSystemFactory, 
   1.632 +		CreateInterfaceFn physicsFactory, CreateInterfaceFn fileSystemFactory, 
   1.633 +		CGlobalVars *pGlobals)
   1.634 +{
   1.635 +
   1.636 +	COM_TimestampedLog( "ConnectTier1/2/3Libraries - Start" );
   1.637 +
   1.638 +	ConnectTier1Libraries( &appSystemFactory, 1 );
   1.639 +	ConnectTier2Libraries( &appSystemFactory, 1 );
   1.640 +	ConnectTier3Libraries( &appSystemFactory, 1 );
   1.641 +
   1.642 +	COM_TimestampedLog( "ConnectTier1/2/3Libraries - Finish" );
   1.643 +
   1.644 +	// Connected in ConnectTier1Libraries
   1.645 +	if ( cvar == NULL )
   1.646 +		return false;
   1.647 +
   1.648 +#if !defined( SWDS ) && !defined(NO_STEAM)
   1.649 +	SteamAPI_InitSafe();
   1.650 +	s_SteamAPIContext.Init();
   1.651 +#endif
   1.652 +#if !defined(NO_STEAM)
   1.653 +	s_SteamGameServerAPIContext.Init();
   1.654 +#endif
   1.655 +
   1.656 +	COM_TimestampedLog( "Factories - Start" );
   1.657 +
   1.658 +	// init each (seperated for ease of debugging)
   1.659 +	if ( (engine = (IVEngineServer*)appSystemFactory(INTERFACEVERSION_VENGINESERVER, NULL)) == NULL )
   1.660 +		return false;
   1.661 +	if ( (g_pVoiceServer = (IVoiceServer*)appSystemFactory(INTERFACEVERSION_VOICESERVER, NULL)) == NULL )
   1.662 +		return false;
   1.663 +	if ( (networkstringtable = (INetworkStringTableContainer *)appSystemFactory(INTERFACENAME_NETWORKSTRINGTABLESERVER,NULL)) == NULL )
   1.664 +		return false;
   1.665 +	if ( (staticpropmgr = (IStaticPropMgrServer *)appSystemFactory(INTERFACEVERSION_STATICPROPMGR_SERVER,NULL)) == NULL )
   1.666 +		return false;
   1.667 +	if ( (random = (IUniformRandomStream *)appSystemFactory(VENGINE_SERVER_RANDOM_INTERFACE_VERSION, NULL)) == NULL )
   1.668 +		return false;
   1.669 +	if ( (enginesound = (IEngineSound *)appSystemFactory(IENGINESOUND_SERVER_INTERFACE_VERSION, NULL)) == NULL )
   1.670 +		return false;
   1.671 +	if ( (partition = (ISpatialPartition *)appSystemFactory(INTERFACEVERSION_SPATIALPARTITION, NULL)) == NULL )
   1.672 +		return false;
   1.673 +	if ( (modelinfo = (IVModelInfo *)appSystemFactory(VMODELINFO_SERVER_INTERFACE_VERSION, NULL)) == NULL )
   1.674 +		return false;
   1.675 +	if ( (enginetrace = (IEngineTrace *)appSystemFactory(INTERFACEVERSION_ENGINETRACE_SERVER,NULL)) == NULL )
   1.676 +		return false;
   1.677 +	if ( (filelogginglistener = (IFileLoggingListener *)appSystemFactory(FILELOGGINGLISTENER_INTERFACE_VERSION, NULL)) == NULL )
   1.678 +		return false;
   1.679 +	if ( (filesystem = (IFileSystem *)fileSystemFactory(FILESYSTEM_INTERFACE_VERSION,NULL)) == NULL )
   1.680 +		return false;
   1.681 +	if ( (gameeventmanager = (IGameEventManager2 *)appSystemFactory(INTERFACEVERSION_GAMEEVENTSMANAGER2,NULL)) == NULL )
   1.682 +		return false;
   1.683 +	if ( (datacache = (IDataCache*)appSystemFactory(DATACACHE_INTERFACE_VERSION, NULL )) == NULL )
   1.684 +		return false;
   1.685 +	if ( (soundemitterbase = (ISoundEmitterSystemBase *)appSystemFactory(SOUNDEMITTERSYSTEM_INTERFACE_VERSION, NULL)) == NULL )
   1.686 +		return false;
   1.687 +	if ( (gamestatsuploader = (IUploadGameStats *)appSystemFactory( INTERFACEVERSION_UPLOADGAMESTATS, NULL )) == NULL )
   1.688 +		return false;
   1.689 +	if ( !mdlcache )
   1.690 +		return false;
   1.691 +	if ( (serverpluginhelpers = (IServerPluginHelpers *)appSystemFactory(INTERFACEVERSION_ISERVERPLUGINHELPERS, NULL)) == NULL )
   1.692 +		return false;
   1.693 +	if ( (scenefilecache = (ISceneFileCache *)appSystemFactory( SCENE_FILE_CACHE_INTERFACE_VERSION, NULL )) == NULL )
   1.694 +		return false;
   1.695 +	if ( (blackboxrecorder = (IBlackBox *)appSystemFactory(BLACKBOX_INTERFACE_VERSION, NULL)) == NULL )
   1.696 +		return false;
   1.697 +	if ( (xboxsystem = (IXboxSystem *)appSystemFactory( XBOXSYSTEM_INTERFACE_VERSION, NULL )) == NULL )
   1.698 +		return false;
   1.699 +
   1.700 +	if ( !CommandLine()->CheckParm( "-noscripting") )
   1.701 +	{
   1.702 +		scriptmanager = (IScriptManager *)appSystemFactory( VSCRIPT_INTERFACE_VERSION, NULL );
   1.703 +	}
   1.704 +
   1.705 +
   1.706 +#ifdef SERVER_USES_VGUI
   1.707 +	// If not running dedicated, grab the engine vgui interface
   1.708 +	if ( !engine->IsDedicatedServer() )
   1.709 +	{
   1.710 +#ifdef _WIN32
   1.711 +		if ( ( enginevgui = ( IEngineVGui * )appSystemFactory(VENGINE_VGUI_VERSION, NULL)) == NULL )
   1.712 +			return false;
   1.713 +		
   1.714 +		// This interface is optional, and is only valid when running with -tools
   1.715 +		serverenginetools = ( IServerEngineTools * )appSystemFactory( VSERVERENGINETOOLS_INTERFACE_VERSION, NULL );
   1.716 +		
   1.717 +		gameuifuncs = (IGameUIFuncs * )appSystemFactory( VENGINE_GAMEUIFUNCS_VERSION, NULL );
   1.718 +#endif
   1.719 +	}
   1.720 +#endif // SERVER_USES_VGUI
   1.721 +
   1.722 +#ifdef INFESTED_DLL
   1.723 +	if ( (missionchooser = (IASW_Mission_Chooser *)appSystemFactory(ASW_MISSION_CHOOSER_VERSION, NULL)) == NULL )
   1.724 +		return false;
   1.725 +	if ( (g_pMatchExtSwarm = (IMatchExtSwarm *)appSystemFactory(IMATCHEXT_SWARM_INTERFACE, NULL)) == NULL )
   1.726 +		return false;
   1.727 +#endif
   1.728 +
   1.729 +	if ( !g_pMatchFramework )
   1.730 +		return false;
   1.731 +	if ( IMatchExtensions *pIMatchExtensions = g_pMatchFramework->GetMatchExtensions() )
   1.732 +		pIMatchExtensions->RegisterExtensionInterface(
   1.733 +			INTERFACEVERSION_SERVERGAMEDLL, static_cast< IServerGameDLL * >( this ) );
   1.734 +
   1.735 +	COM_TimestampedLog( "Factories - Finish" );
   1.736 +
   1.737 +	COM_TimestampedLog( "soundemitterbase->Connect" );
   1.738 +
   1.739 +	// Yes, both the client and game .dlls will try to Connect, the soundemittersystem.dll will handle this gracefully
   1.740 +	if ( !soundemitterbase->Connect( appSystemFactory ) )
   1.741 +		return false;
   1.742 +
   1.743 +	if ( CommandLine()->FindParm( "-headtracking" ) )
   1.744 +		g_bHeadTrackingEnabled = true;
   1.745 +
   1.746 +	// cache the globals
   1.747 +	gpGlobals = pGlobals;
   1.748 +
   1.749 +	g_pSharedChangeInfo = engine->GetSharedEdictChangeInfo();
   1.750 +	
   1.751 +	COM_TimestampedLog( "MathLib_Init" );
   1.752 +
   1.753 +	MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
   1.754 +
   1.755 +	// save these in case other system inits need them
   1.756 +	factorylist_t factories;
   1.757 +	factories.engineFactory = appSystemFactory;
   1.758 +	factories.fileSystemFactory = fileSystemFactory;
   1.759 +	factories.physicsFactory = physicsFactory;
   1.760 +	FactoryList_Store( factories );
   1.761 +
   1.762 +	COM_TimestampedLog( "gameeventmanager->LoadEventsFromFile" );
   1.763 +	// load used game events  
   1.764 +	gameeventmanager->LoadEventsFromFile("resource/gameevents.res");
   1.765 +
   1.766 +	COM_TimestampedLog( "InitializeCvars" );
   1.767 +	// init the cvar list first in case inits want to reference them
   1.768 +	InitializeCvars();
   1.769 +	
   1.770 +	COM_TimestampedLog( "g_pParticleSystemMgr->Init" );
   1.771 +	// Initialize the particle system
   1.772 +	bool bPrecacheParticles = IsPC() && !engine->IsCreatingXboxReslist();
   1.773 +	if ( !g_pParticleSystemMgr->Init( g_pParticleSystemQuery, bPrecacheParticles ) )
   1.774 +	{
   1.775 +		return false;
   1.776 +	}
   1.777 +
   1.778 +	sv_cheats = g_pCVar->FindVar( "sv_cheats" );
   1.779 +	if ( !sv_cheats )
   1.780 +		return false;
   1.781 +
   1.782 +	g_pcv_commentary = g_pCVar->FindVar( "commentary" );
   1.783 +	g_pcv_ThreadMode = g_pCVar->FindVar( "host_thread_mode" );
   1.784 +
   1.785 +	sv_maxreplay = g_pCVar->FindVar( "sv_maxreplay" );
   1.786 +
   1.787 +	COM_TimestampedLog( "g_pGameSaveRestoreBlockSet" );
   1.788 +
   1.789 +	g_pGameSaveRestoreBlockSet->AddBlockHandler( GetEntitySaveRestoreBlockHandler() );
   1.790 +	g_pGameSaveRestoreBlockSet->AddBlockHandler( GetPhysSaveRestoreBlockHandler() );
   1.791 +	g_pGameSaveRestoreBlockSet->AddBlockHandler( GetAISaveRestoreBlockHandler() );
   1.792 +	g_pGameSaveRestoreBlockSet->AddBlockHandler( GetTemplateSaveRestoreBlockHandler() );
   1.793 +	g_pGameSaveRestoreBlockSet->AddBlockHandler( GetDefaultResponseSystemSaveRestoreBlockHandler() );
   1.794 +	g_pGameSaveRestoreBlockSet->AddBlockHandler( GetCommentarySaveRestoreBlockHandler() );
   1.795 +	g_pGameSaveRestoreBlockSet->AddBlockHandler( GetEventQueueSaveRestoreBlockHandler() );
   1.796 +	g_pGameSaveRestoreBlockSet->AddBlockHandler( GetAchievementSaveRestoreBlockHandler() );
   1.797 +	g_pGameSaveRestoreBlockSet->AddBlockHandler( GetVScriptSaveRestoreBlockHandler() );
   1.798 +
   1.799 +
   1.800 +
   1.801 +	bool bInitSuccess = false;
   1.802 +	if ( sv_threaded_init.GetBool() )
   1.803 +	{
   1.804 +		CFunctorJob *pGameJob = new CFunctorJob( CreateFunctor( ParseParticleEffects, false ) );
   1.805 +		g_pThreadPool->AddJob( pGameJob );
   1.806 +		bInitSuccess = InitGameSystems( appSystemFactory );
   1.807 +
   1.808 +		// FIXME: This method is a bit of a hack.
   1.809 +		// Try to update the screen every .06 seconds while waiting.
   1.810 +		float flLastUpdateTime = -1.0f;
   1.811 +
   1.812 +		while( !pGameJob->IsFinished() )
   1.813 +		{
   1.814 +			float flTime = Plat_FloatTime();
   1.815 +
   1.816 +			if ( flTime - flLastUpdateTime > .06f )
   1.817 +			{
   1.818 +				flLastUpdateTime = flTime;
   1.819 +				engine->RefreshScreenIfNecessary();
   1.820 +			}
   1.821 +
   1.822 +			ThreadSleep( 0 );
   1.823 +		}
   1.824 +		pGameJob->Release();
   1.825 +	}
   1.826 +	else
   1.827 +	{
   1.828 +		COM_TimestampedLog( "ParseParticleEffects" );
   1.829 +		ParseParticleEffects( false );
   1.830 +		COM_TimestampedLog( "InitGameSystems - Start" );
   1.831 +		bInitSuccess = InitGameSystems( appSystemFactory );
   1.832 +		COM_TimestampedLog( "InitGameSystems - Finish" );
   1.833 +	}
   1.834 +	// try to get debug overlay, may be NULL if on HLDS
   1.835 +	debugoverlay = (IVDebugOverlay *)appSystemFactory( VDEBUG_OVERLAY_INTERFACE_VERSION, NULL );
   1.836 +
   1.837 +	// init the gamestatsupload connection
   1.838 +	gamestatsuploader->InitConnection();
   1.839 +
   1.840 +
   1.841 +	return true;
   1.842 +}
   1.843 +
   1.844 +void CServerGameDLL::PostInit()
   1.845 +{
   1.846 +	IGameSystem::PostInitAllSystems();
   1.847 +
   1.848 +#ifdef SERVER_USES_VGUI
   1.849 +	if ( !engine->IsDedicatedServer() && enginevgui )
   1.850 +	{
   1.851 +		if ( VGui_PostInit() )
   1.852 +		{
   1.853 +			// all good
   1.854 +		}
   1.855 +	}
   1.856 +#endif // SERVER_USES_VGUI
   1.857 +}
   1.858 +
   1.859 +void CServerGameDLL::PostToolsInit()
   1.860 +{
   1.861 +	if ( serverenginetools )
   1.862 +	{
   1.863 +		serverfoundry = ( IServerFoundry * )serverenginetools->QueryInterface( VSERVERFOUNDRY_INTERFACE_VERSION );
   1.864 +	}
   1.865 +}
   1.866 +
   1.867 +void CServerGameDLL::DLLShutdown( void )
   1.868 +{
   1.869 +
   1.870 +	// Due to dependencies, these are not autogamesystems
   1.871 +	ModelSoundsCacheShutdown();
   1.872 +
   1.873 +
   1.874 +
   1.875 +	g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetVScriptSaveRestoreBlockHandler() );
   1.876 +	g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetAchievementSaveRestoreBlockHandler() );
   1.877 +	g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetCommentarySaveRestoreBlockHandler() );
   1.878 +	g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetEventQueueSaveRestoreBlockHandler() );
   1.879 +	g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetDefaultResponseSystemSaveRestoreBlockHandler() );
   1.880 +	g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetTemplateSaveRestoreBlockHandler() );
   1.881 +	g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetAISaveRestoreBlockHandler() );
   1.882 +	g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetPhysSaveRestoreBlockHandler() );
   1.883 +	g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetEntitySaveRestoreBlockHandler() );
   1.884 +
   1.885 +
   1.886 +	char *pFilename = g_TextStatsMgr.GetStatsFilename();
   1.887 +	if ( !pFilename || !pFilename[0] )
   1.888 +	{
   1.889 +		g_TextStatsMgr.SetStatsFilename( "stats.txt" );
   1.890 +	}
   1.891 +	g_TextStatsMgr.WriteFile( filesystem );
   1.892 +
   1.893 +	IGameSystem::ShutdownAllSystems();
   1.894 +
   1.895 +#ifdef SERVER_USES_VGUI
   1.896 +	if ( enginevgui )
   1.897 +	{
   1.898 +		VGui_Shutdown();
   1.899 +	}
   1.900 +#endif // SERVER_USES_VGUI
   1.901 +
   1.902 +
   1.903 +
   1.904 +
   1.905 +	// destroy the Navigation Mesh interface
   1.906 +	if (TheNavMesh)
   1.907 +	{
   1.908 +		delete TheNavMesh;
   1.909 +		TheNavMesh = NULL;
   1.910 +	}
   1.911 +
   1.912 +#if !defined(NO_STEAM)
   1.913 +	s_SteamAPIContext.Clear(); // Steam API context shutdown
   1.914 +	s_SteamGameServerAPIContext.Clear();	
   1.915 +	// SteamAPI_Shutdown(); << Steam shutdown is controlled by engine
   1.916 +#endif
   1.917 +	
   1.918 +	DisconnectTier3Libraries();
   1.919 +	DisconnectTier2Libraries();
   1.920 +	ConVar_Unregister();
   1.921 +	DisconnectTier1Libraries();
   1.922 +}
   1.923 +
   1.924 +
   1.925 +//-----------------------------------------------------------------------------
   1.926 +// Purpose: See shareddefs.h for redefining this.  Don't even think about it, though, for HL2.  Or you will pay.  ywb 9/22/03
   1.927 +// Output : float
   1.928 +//-----------------------------------------------------------------------------
   1.929 +float CServerGameDLL::GetTickInterval( void ) const
   1.930 +{
   1.931 +	float tickinterval = DEFAULT_TICK_INTERVAL;
   1.932 +
   1.933 +
   1.934 +
   1.935 +
   1.936 +	// override if tick rate specified in command line
   1.937 +	if ( CommandLine()->CheckParm( "-tickrate" ) )
   1.938 +	{
   1.939 +		float tickrate = CommandLine()->ParmValue( "-tickrate", 0 );
   1.940 +		if ( tickrate > 10 )
   1.941 +			tickinterval = 1.0f / tickrate;
   1.942 +	}
   1.943 +
   1.944 +
   1.945 +	return tickinterval;
   1.946 +}
   1.947 +
   1.948 +// This is called when a new game is started. (restart, map)
   1.949 +bool CServerGameDLL::GameInit( void )
   1.950 +{
   1.951 +	ResetGlobalState();
   1.952 +	engine->ServerCommand( "exec game.cfg\n" );
   1.953 +	engine->ServerExecute( );
   1.954 +	CBaseEntity::sm_bAccurateTriggerBboxChecks = true;
   1.955 +
   1.956 +	IGameEvent *event = gameeventmanager->CreateEvent( "game_init" );
   1.957 +	if ( event )
   1.958 +	{
   1.959 +		gameeventmanager->FireEvent( event );
   1.960 +	}
   1.961 +
   1.962 +	return true;
   1.963 +}
   1.964 +
   1.965 +// This is called when a game ends (server disconnect, death, restart, load)
   1.966 +// NOT on level transitions within a game
   1.967 +void CServerGameDLL::GameShutdown( void )
   1.968 +{
   1.969 +	ResetGlobalState();
   1.970 +}
   1.971 +
   1.972 +static bool g_OneWayTransition = false;
   1.973 +void Game_SetOneWayTransition( void )
   1.974 +{
   1.975 +	g_OneWayTransition = true;
   1.976 +}
   1.977 +
   1.978 +static CUtlVector<EHANDLE> g_RestoredEntities;
   1.979 +// just for debugging, assert that this is the only time this function is called
   1.980 +static bool g_InRestore = false;
   1.981 +
   1.982 +void AddRestoredEntity( CBaseEntity *pEntity )
   1.983 +{
   1.984 +	Assert(g_InRestore);
   1.985 +	if ( !pEntity )
   1.986 +		return;
   1.987 +
   1.988 +	g_RestoredEntities.AddToTail( EHANDLE(pEntity) );
   1.989 +}
   1.990 +
   1.991 +void EndRestoreEntities()
   1.992 +{
   1.993 +	if ( !g_InRestore )
   1.994 +		return;
   1.995 +		
   1.996 +	// The entire hierarchy is restored, so we can call GetAbsOrigin again.
   1.997 +	//CBaseEntity::SetAbsQueriesValid( true );
   1.998 +
   1.999 +	// Call all entities' OnRestore handlers
  1.1000 +	for ( int i = g_RestoredEntities.Count()-1; i >=0; --i )
  1.1001 +	{
  1.1002 +		CBaseEntity *pEntity = g_RestoredEntities[i].Get();
  1.1003 +		if ( pEntity && !pEntity->IsDormant() )
  1.1004 +		{
  1.1005 +			MDLCACHE_CRITICAL_SECTION();
  1.1006 +			pEntity->OnRestore();
  1.1007 +		}
  1.1008 +	}
  1.1009 +
  1.1010 +	g_RestoredEntities.Purge();
  1.1011 +
  1.1012 +	IGameSystem::OnRestoreAllSystems();
  1.1013 +
  1.1014 +	g_InRestore = false;
  1.1015 +	gEntList.CleanupDeleteList();
  1.1016 +
  1.1017 +	// HACKHACK: UNDONE: We need to redesign the main loop with respect to save/load/server activate
  1.1018 +	g_ServerGameDLL.ServerActivate( NULL, 0, 0 );
  1.1019 +	CBaseEntity::SetAllowPrecache( false );
  1.1020 +}
  1.1021 +
  1.1022 +void BeginRestoreEntities()
  1.1023 +{
  1.1024 +	if ( g_InRestore )
  1.1025 +	{
  1.1026 +		DevMsg( "BeginRestoreEntities without previous EndRestoreEntities.\n" );
  1.1027 +		gEntList.CleanupDeleteList();
  1.1028 +	}
  1.1029 +	g_RestoredEntities.Purge();
  1.1030 +	g_InRestore = true;
  1.1031 +
  1.1032 +	CBaseEntity::SetAllowPrecache( true );
  1.1033 +
  1.1034 +	// No calls to GetAbsOrigin until the entire hierarchy is restored!
  1.1035 +	//CBaseEntity::SetAbsQueriesValid( false );
  1.1036 +}
  1.1037 +
  1.1038 +//-----------------------------------------------------------------------------
  1.1039 +// Purpose: This prevents sv.tickcount/gpGlobals->tickcount from advancing during restore which
  1.1040 +//  would cause a lot of the NPCs to fast forward their think times to the same
  1.1041 +//  tick due to some ticks being elapsed during restore where there was no simulation going on
  1.1042 +//-----------------------------------------------------------------------------
  1.1043 +bool CServerGameDLL::IsRestoring()
  1.1044 +{
  1.1045 +	return g_InRestore;
  1.1046 +}
  1.1047 +
  1.1048 +bool CServerGameDLL::SupportsSaveRestore()
  1.1049 +{
  1.1050 +#ifdef INFESTED_DLL
  1.1051 +	return false;
  1.1052 +#endif
  1.1053 +
  1.1054 +	return true;
  1.1055 +}
  1.1056 +
  1.1057 +// Called any time a new level is started (after GameInit() also on level transitions within a game)
  1.1058 +bool CServerGameDLL::LevelInit( const char *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background )
  1.1059 +{
  1.1060 +	VPROF("CServerGameDLL::LevelInit");
  1.1061 +	ResetWindspeed();
  1.1062 +	UpdateChapterRestrictions( pMapName );
  1.1063 +
  1.1064 +	// IGameSystem::LevelInitPreEntityAllSystems() is called when the world is precached
  1.1065 +	// That happens either in LoadGameState() or in MapEntity_ParseAllEntities()
  1.1066 +	if ( loadGame )
  1.1067 +	{
  1.1068 +		if ( pOldLevel )
  1.1069 +		{
  1.1070 +			gpGlobals->eLoadType = MapLoad_Transition;
  1.1071 +		}
  1.1072 +		else
  1.1073 +		{
  1.1074 +			gpGlobals->eLoadType = MapLoad_LoadGame;
  1.1075 +		}
  1.1076 +
  1.1077 +		BeginRestoreEntities();
  1.1078 +		if ( !engine->LoadGameState( pMapName, 1 ) )
  1.1079 +		{
  1.1080 +			if ( pOldLevel )
  1.1081 +			{
  1.1082 +				MapEntity_ParseAllEntities( pMapEntities );
  1.1083 +			}
  1.1084 +			else
  1.1085 +			{
  1.1086 +				// Regular save load case
  1.1087 +				return false;
  1.1088 +			}
  1.1089 +		}
  1.1090 +
  1.1091 +		if ( pOldLevel )
  1.1092 +		{
  1.1093 +			engine->LoadAdjacentEnts( pOldLevel, pLandmarkName );
  1.1094 +		}
  1.1095 +
  1.1096 +		if ( g_OneWayTransition )
  1.1097 +		{
  1.1098 +			engine->ClearSaveDirAfterClientLoad();
  1.1099 +		}
  1.1100 +
  1.1101 +		if ( pOldLevel && sv_autosave.GetBool() == true && gpGlobals->maxClients == 1 )
  1.1102 +		{
  1.1103 +			// This is a single-player style level transition.
  1.1104 +			// Queue up an autosave one second into the level
  1.1105 +			CBaseEntity *pAutosave = CBaseEntity::Create( "logic_autosave", vec3_origin, vec3_angle, NULL );
  1.1106 +			if ( pAutosave )
  1.1107 +			{
  1.1108 +				g_EventQueue.AddEvent( pAutosave, "Save", 1.0, NULL, NULL );
  1.1109 +				g_EventQueue.AddEvent( pAutosave, "Kill", 1.1, NULL, NULL );
  1.1110 +			}
  1.1111 +		}
  1.1112 +	}
  1.1113 +	else
  1.1114 +	{
  1.1115 +		if ( background )
  1.1116 +		{
  1.1117 +			gpGlobals->eLoadType = MapLoad_Background;
  1.1118 +		}
  1.1119 +		else
  1.1120 +		{
  1.1121 +			gpGlobals->eLoadType = MapLoad_NewGame;
  1.1122 +		}
  1.1123 +
  1.1124 +		// Clear out entity references, and parse the entities into it.
  1.1125 +		g_MapEntityRefs.Purge();
  1.1126 +		CMapLoadEntityFilter filter;
  1.1127 +		MapEntity_ParseAllEntities( pMapEntities, &filter );
  1.1128 +
  1.1129 +		g_pServerBenchmark->StartBenchmark();
  1.1130 +
  1.1131 +		// Now call the mod specific parse
  1.1132 +		LevelInit_ParseAllEntities( pMapEntities );
  1.1133 +	}
  1.1134 +
  1.1135 +	// Check low violence settings for this map
  1.1136 +	g_RagdollLVManager.SetLowViolence( pMapName );
  1.1137 +
  1.1138 +	// Now that all of the active entities have been loaded in, precache any entities who need point_template parameters
  1.1139 +	//  to be parsed (the above code has loaded all point_template entities)
  1.1140 +	PrecachePointTemplates();
  1.1141 +
  1.1142 +	// load MOTD from file into stringtable
  1.1143 +	LoadMessageOfTheDay();
  1.1144 +
  1.1145 +	// Sometimes an ent will Remove() itself during its precache, so RemoveImmediate won't happen.
  1.1146 +	// This makes sure those ents get cleaned up.
  1.1147 +	gEntList.CleanupDeleteList();
  1.1148 +
  1.1149 +	g_AIFriendliesTalkSemaphore.Release();
  1.1150 +	g_AIFoesTalkSemaphore.Release();
  1.1151 +	g_OneWayTransition = false;
  1.1152 +
  1.1153 +	// clear any pending autosavedangerous
  1.1154 +	m_fAutoSaveDangerousTime = 0.0f;
  1.1155 +	m_fAutoSaveDangerousMinHealthToCommit = 0.0f;
  1.1156 +
  1.1157 +	// ask for the latest game rules
  1.1158 +	GameRules()->UpdateGameplayStatsFromSteam();
  1.1159 +
  1.1160 +	return true;
  1.1161 +}
  1.1162 +
  1.1163 +//-----------------------------------------------------------------------------
  1.1164 +// Purpose: called after every level change and load game, iterates through all the
  1.1165 +//			active entities and gives them a chance to fix up their state
  1.1166 +//-----------------------------------------------------------------------------
  1.1167 +#ifdef DEBUG
  1.1168 +bool g_bReceivedChainedActivate;
  1.1169 +bool g_bCheckForChainedActivate;
  1.1170 +#define BeginCheckChainedActivate() if (0) ; else { g_bCheckForChainedActivate = true; g_bReceivedChainedActivate = false; }
  1.1171 +#define EndCheckChainedActivate( bCheck )	\
  1.1172 +	if (0) ; else \
  1.1173 +	{ \
  1.1174 +		if ( bCheck ) \
  1.1175 +		{ \
  1.1176 +			char msg[ 1024 ];	\
  1.1177 +			Q_snprintf( msg, sizeof( msg ),  "Entity (%i/%s/%s) failed to call base class Activate()\n", pClass->entindex(), pClass->GetClassname(), STRING( pClass->GetEntityName() ) );	\
  1.1178 +			AssertMsg( g_bReceivedChainedActivate == true, msg ); \
  1.1179 +		} \
  1.1180 +		g_bCheckForChainedActivate = false; \
  1.1181 +	}
  1.1182 +#else
  1.1183 +#define BeginCheckChainedActivate()			((void)0)
  1.1184 +#define EndCheckChainedActivate( bCheck )	((void)0)
  1.1185 +#endif
  1.1186 +
  1.1187 +void CServerGameDLL::ServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
  1.1188 +{
  1.1189 +	// HACKHACK: UNDONE: We need to redesign the main loop with respect to save/load/server activate
  1.1190 +	if ( g_InRestore )
  1.1191 +		return;
  1.1192 +
  1.1193 +	if ( gEntList.ResetDeleteList() != 0 )
  1.1194 +	{
  1.1195 +		Msg( "ERROR: Entity delete queue not empty on level start!\n" );
  1.1196 +	}
  1.1197 +
  1.1198 +	for ( CBaseEntity *pClass = gEntList.FirstEnt(); pClass != NULL; pClass = gEntList.NextEnt(pClass) )
  1.1199 +	{
  1.1200 +		if ( pClass && !pClass->IsDormant() )
  1.1201 +		{
  1.1202 +			MDLCACHE_CRITICAL_SECTION();
  1.1203 +
  1.1204 +			BeginCheckChainedActivate();
  1.1205 +			pClass->Activate();
  1.1206 +			
  1.1207 +			// We don't care if it finished activating if it decided to remove itself.
  1.1208 +			EndCheckChainedActivate( !( pClass->GetEFlags() & EFL_KILLME ) ); 
  1.1209 +		}
  1.1210 +	}
  1.1211 +
  1.1212 +	IGameSystem::LevelInitPostEntityAllSystems();
  1.1213 +	// No more precaching after PostEntityAllSystems!!!
  1.1214 +	CBaseEntity::SetAllowPrecache( false );
  1.1215 +
  1.1216 +	// only display the think limit when the game is run with "developer" mode set
  1.1217 +	if ( !g_pDeveloper->GetInt() )
  1.1218 +	{
  1.1219 +		think_limit.SetValue( 0 );
  1.1220 +	}
  1.1221 +
  1.1222 +#ifndef _XBOX
  1.1223 +	// load the Navigation Mesh for this map
  1.1224 +	TheNavMesh->Load();
  1.1225 +#endif
  1.1226 +
  1.1227 +
  1.1228 +}
  1.1229 +
  1.1230 +//-----------------------------------------------------------------------------
  1.1231 +// Purpose: Called after the steam API has been activated post-level startup
  1.1232 +//-----------------------------------------------------------------------------
  1.1233 +void CServerGameDLL::GameServerSteamAPIActivated( void )
  1.1234 +{
  1.1235 +#if !defined( NO_STEAM )
  1.1236 +	steamgameserverapicontext->Init();
  1.1237 +#endif
  1.1238 +	
  1.1239 +}
  1.1240 +
  1.1241 +//-----------------------------------------------------------------------------
  1.1242 +// Purpose: Called at the start of every game frame
  1.1243 +//-----------------------------------------------------------------------------
  1.1244 +ConVar  trace_report( "trace_report", "0" );
  1.1245 +
  1.1246 +void CServerGameDLL::GameFrame( bool simulating )
  1.1247 +{
  1.1248 +	VPROF( "CServerGameDLL::GameFrame" );
  1.1249 +
  1.1250 +	// Don't run frames until fully restored
  1.1251 +	if ( g_InRestore )
  1.1252 +		return;
  1.1253 +
  1.1254 +#ifndef NO_STEAM
  1.1255 +	// All the calls to us from the engine prior to gameframe (like LevelInit & ServerActivate)
  1.1256 +	// are done before the engine has got the Steam API connected, so we have to wait until now to connect ourselves.
  1.1257 +	if ( Steam3Server().CheckInitialized() )
  1.1258 +	{
  1.1259 +		GameRules()->UpdateGameplayStatsFromSteam();
  1.1260 +	}
  1.1261 +#endif
  1.1262 +
  1.1263 +	g_bIsLogging = engine->IsLogEnabled();
  1.1264 +	if ( CBaseEntity::IsSimulatingOnAlternateTicks() )
  1.1265 +	{
  1.1266 +		// only run simulation on even numbered ticks
  1.1267 +		if ( gpGlobals->tickcount & 1 )
  1.1268 +		{
  1.1269 +			UpdateAllClientData();
  1.1270 +			return;
  1.1271 +		}
  1.1272 +		// If we're skipping frames, then the frametime is 2x the normal tick
  1.1273 +		gpGlobals->frametime *= 2.0f;
  1.1274 +	}
  1.1275 +
  1.1276 +	float oldframetime = gpGlobals->frametime;
  1.1277 +
  1.1278 +#ifdef _DEBUG
  1.1279 +	// For profiling.. let them enable/disable the networkvar manual mode stuff.
  1.1280 +	g_bUseNetworkVars = s_UseNetworkVars.GetBool();
  1.1281 +#endif
  1.1282 +
  1.1283 +	extern void GameStartFrame( void );
  1.1284 +	extern void ServiceEventQueue( void );
  1.1285 +	extern void Physics_RunThinkFunctions( bool simulating );
  1.1286 +
  1.1287 +	// Delete anything that was marked for deletion
  1.1288 +	//  outside of server frameloop (e.g., in response to concommand)
  1.1289 +	gEntList.CleanupDeleteList();
  1.1290 +	HandleFoundryEntitySpawnRecords();
  1.1291 +
  1.1292 +	IGameSystem::FrameUpdatePreEntityThinkAllSystems();
  1.1293 +	GameStartFrame();
  1.1294 +
  1.1295 +
  1.1296 +	TheNavMesh->Update();
  1.1297 +
  1.1298 +	{
  1.1299 +		VPROF( "gamestatsuploader->UpdateConnection" );
  1.1300 +		gamestatsuploader->UpdateConnection();
  1.1301 +	}
  1.1302 +	{
  1.1303 +		VPROF( "UpdateQueryCache" );
  1.1304 +		UpdateQueryCache();
  1.1305 +	}
  1.1306 +	
  1.1307 +	{
  1.1308 +		VPROF( "g_pServerBenchmark->UpdateBenchmark" );
  1.1309 +		g_pServerBenchmark->UpdateBenchmark();
  1.1310 +	}
  1.1311 +	
  1.1312 +
  1.1313 +	Physics_RunThinkFunctions( simulating );
  1.1314 +	
  1.1315 +	IGameSystem::FrameUpdatePostEntityThinkAllSystems();
  1.1316 +
  1.1317 +	// UNDONE: Make these systems IGameSystems and move these calls into FrameUpdatePostEntityThink()
  1.1318 +	// service event queue, firing off any actions whos time has come
  1.1319 +	ServiceEventQueue();
  1.1320 +
  1.1321 +	// free all ents marked in think functions
  1.1322 +	gEntList.CleanupDeleteList();
  1.1323 +
  1.1324 +	// FIXME:  Should this only occur on the final tick?
  1.1325 +	UpdateAllClientData();
  1.1326 +
  1.1327 +	if ( g_pGameRules )
  1.1328 +	{
  1.1329 +		VPROF( "g_pGameRules->EndGameFrame" );
  1.1330 +		g_pGameRules->EndGameFrame();
  1.1331 +	}
  1.1332 +
  1.1333 +	if ( trace_report.GetBool() )
  1.1334 +	{
  1.1335 +		int total = 0, totals[3];
  1.1336 +		for ( int i = 0; i < 3; i++ )
  1.1337 +		{
  1.1338 +			totals[i] = enginetrace->GetStatByIndex( i, true );
  1.1339 +			if ( totals[i] > 0 )
  1.1340 +			{
  1.1341 +				total += totals[i];
  1.1342 +			}
  1.1343 +		}
  1.1344 +
  1.1345 +		if ( total )
  1.1346 +		{
  1.1347 +			Msg("Trace: %d, contents %d, enumerate %d\n", totals[0], totals[1], totals[2] );
  1.1348 +		}
  1.1349 +	}
  1.1350 +
  1.1351 +	// Any entities that detect network state changes on a timer do it here.
  1.1352 +	g_NetworkPropertyEventMgr.FireEvents();
  1.1353 +
  1.1354 +	gpGlobals->frametime = oldframetime;
  1.1355 +}
  1.1356 +
  1.1357 +//-----------------------------------------------------------------------------
  1.1358 +// Purpose: Called every frame even if not ticking
  1.1359 +// Input  : simulating - 
  1.1360 +//-----------------------------------------------------------------------------
  1.1361 +void CServerGameDLL::PreClientUpdate( bool simulating )
  1.1362 +{
  1.1363 +	if ( !simulating )
  1.1364 +		return;
  1.1365 +
  1.1366 +	/*
  1.1367 +	if (game_speeds.GetInt())
  1.1368 +	{
  1.1369 +		DrawMeasuredSections();
  1.1370 +	}
  1.1371 +	*/
  1.1372 +
  1.1373 +//#ifdef _DEBUG  - allow this in release for now
  1.1374 +	DrawAllDebugOverlays();
  1.1375 +//#endif
  1.1376 +	
  1.1377 +	IGameSystem::PreClientUpdateAllSystems();
  1.1378 +
  1.1379 +	if ( sv_showhitboxes.GetInt() == -1 )
  1.1380 +		return;
  1.1381 +
  1.1382 +	if ( sv_showhitboxes.GetInt() == 0 )
  1.1383 +	{
  1.1384 +		// assume it's text
  1.1385 +		CBaseEntity *pEntity = NULL;
  1.1386 +
  1.1387 +		while (1)
  1.1388 +		{
  1.1389 +			pEntity = gEntList.FindEntityByName( pEntity, sv_showhitboxes.GetString() );
  1.1390 +			if ( !pEntity )
  1.1391 +				break;
  1.1392 +
  1.1393 +			CBaseAnimating *anim = dynamic_cast< CBaseAnimating * >( pEntity );
  1.1394 +
  1.1395 +			if (anim)
  1.1396 +			{
  1.1397 +				anim->DrawServerHitboxes();
  1.1398 +			}
  1.1399 +		}
  1.1400 +		return;
  1.1401 +	}
  1.1402 +
  1.1403 +	CBaseAnimating *anim = dynamic_cast< CBaseAnimating * >( CBaseEntity::Instance( INDEXENT( sv_showhitboxes.GetInt() ) ) );
  1.1404 +	if ( !anim )
  1.1405 +		return;
  1.1406 +
  1.1407 +	anim->DrawServerHitboxes();
  1.1408 +}
  1.1409 +
  1.1410 +void CServerGameDLL::Think( bool finalTick )
  1.1411 +{
  1.1412 +	if ( m_fAutoSaveDangerousTime != 0.0f && m_fAutoSaveDangerousTime < gpGlobals->curtime )
  1.1413 +	{
  1.1414 +		// The safety timer for a dangerous auto save has expired
  1.1415 +		CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
  1.1416 +
  1.1417 +		if ( pPlayer && ( pPlayer->GetDeathTime() == 0.0f || pPlayer->GetDeathTime() > gpGlobals->curtime )
  1.1418 +			&& !pPlayer->IsSinglePlayerGameEnding()
  1.1419 +			)
  1.1420 +		{
  1.1421 +			if( pPlayer->GetHealth() >= m_fAutoSaveDangerousMinHealthToCommit )
  1.1422 +			{
  1.1423 +				// The player isn't dead, so make the dangerous auto save safe
  1.1424 +				engine->ServerCommand( "autosavedangerousissafe\n" );
  1.1425 +			}
  1.1426 +		}
  1.1427 +
  1.1428 +		m_fAutoSaveDangerousTime = 0.0f;
  1.1429 +		m_fAutoSaveDangerousMinHealthToCommit = 0.0f;
  1.1430 +	}
  1.1431 +}
  1.1432 +
  1.1433 +void CServerGameDLL::OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue )
  1.1434 +{
  1.1435 +}
  1.1436 +
  1.1437 +// Called when a level is shutdown (including changing levels)
  1.1438 +void CServerGameDLL::LevelShutdown( void )
  1.1439 +{
  1.1440 +#if !defined( NO_STEAM )
  1.1441 +	steamgameserverapicontext->Clear();
  1.1442 +#endif
  1.1443 +
  1.1444 +	MDLCACHE_CRITICAL_SECTION();
  1.1445 +	IGameSystem::LevelShutdownPreEntityAllSystems();
  1.1446 +
  1.1447 +	// YWB:
  1.1448 +	// This entity pointer is going away now and is corrupting memory on level transitions/restarts
  1.1449 +	CSoundEnt::ShutdownSoundEnt();
  1.1450 +
  1.1451 +	ClearDebugHistory();
  1.1452 +
  1.1453 +	gEntList.Clear();
  1.1454 +
  1.1455 +	InvalidateQueryCache();
  1.1456 +
  1.1457 +
  1.1458 +	IGameSystem::LevelShutdownPostEntityAllSystems();
  1.1459 +
  1.1460 +	// In case we quit out during initial load
  1.1461 +	CBaseEntity::SetAllowPrecache( false );
  1.1462 +
  1.1463 +	TheNavMesh->Reset();
  1.1464 +
  1.1465 +	g_nCurrentChapterIndex = -1;
  1.1466 +	CStudioHdr::CActivityToSequenceMapping::ResetMappings();
  1.1467 +}
  1.1468 +
  1.1469 +//-----------------------------------------------------------------------------
  1.1470 +// Purpose: 
  1.1471 +// Input  : 
  1.1472 +// Output : ServerClass*
  1.1473 +//-----------------------------------------------------------------------------
  1.1474 +ServerClass* CServerGameDLL::GetAllServerClasses()
  1.1475 +{
  1.1476 +	return g_pServerClassHead;
  1.1477 +}
  1.1478 +
  1.1479 +
  1.1480 +const char *CServerGameDLL::GetGameDescription( void )
  1.1481 +{
  1.1482 +	return ::GetGameDescription();
  1.1483 +}
  1.1484 +
  1.1485 +void CServerGameDLL::CreateNetworkStringTables( void )
  1.1486 +{
  1.1487 +	// Create any shared string tables here (and only here!)
  1.1488 +	// E.g.:  xxx = networkstringtable->CreateStringTable( "SceneStrings", 512 );
  1.1489 +	g_pStringTableExtraParticleFiles = networkstringtable->CreateStringTable( "ExtraParticleFilesTable", MAX_PARTICLESYSTEMS_STRINGS, 0, 0, NSF_DICTIONARY_ENABLED );
  1.1490 +	g_pStringTableParticleEffectNames = networkstringtable->CreateStringTable( "ParticleEffectNames", MAX_PARTICLESYSTEMS_STRINGS, 0, 0, NSF_DICTIONARY_ENABLED );
  1.1491 +	g_pStringTableEffectDispatch = networkstringtable->CreateStringTable( "EffectDispatch", MAX_EFFECT_DISPATCH_STRINGS );
  1.1492 +	g_pStringTableVguiScreen = networkstringtable->CreateStringTable( "VguiScreen", MAX_VGUI_SCREEN_STRINGS );
  1.1493 +	g_pStringTableMaterials = networkstringtable->CreateStringTable( "Materials", MAX_MATERIAL_STRINGS, 0, 0, NSF_DICTIONARY_ENABLED );
  1.1494 +	g_pStringTableInfoPanel = networkstringtable->CreateStringTable( "InfoPanel", MAX_INFOPANEL_STRINGS );
  1.1495 +	g_pStringTableClientSideChoreoScenes = networkstringtable->CreateStringTable( "Scenes", MAX_CHOREO_SCENES_STRINGS, 0, 0, NSF_DICTIONARY_ENABLED );
  1.1496 +
  1.1497 +	Assert( g_pStringTableParticleEffectNames &&
  1.1498 +			g_pStringTableEffectDispatch &&
  1.1499 +			g_pStringTableVguiScreen &&
  1.1500 +			g_pStringTableMaterials &&
  1.1501 +			g_pStringTableInfoPanel &&
  1.1502 +			g_pStringTableClientSideChoreoScenes &&
  1.1503 +			g_pStringTableExtraParticleFiles );
  1.1504 +
  1.1505 +	// Need this so we have the error material always handy
  1.1506 +	PrecacheMaterial( "debug/debugempty" );
  1.1507 +	Assert( GetMaterialIndex( "debug/debugempty" ) == 0 );
  1.1508 +
  1.1509 +	PrecacheParticleSystem( "error" );	// ensure error particle system is handy
  1.1510 +	Assert( GetParticleSystemIndex( "error" ) == 0 );
  1.1511 +
  1.1512 +	PrecacheEffect( "error" );	// ensure error effect is handy
  1.1513 +	Assert( GetEffectIndex( "error" ) == 0 );
  1.1514 +
  1.1515 +	CreateNetworkStringTables_GameRules();
  1.1516 +
  1.1517 +	// Set up save/load utilities for string tables
  1.1518 +	g_VguiScreenStringOps.Init( g_pStringTableVguiScreen );
  1.1519 +}
  1.1520 +
  1.1521 +CSaveRestoreData *CServerGameDLL::SaveInit( int size )
  1.1522 +{
  1.1523 +	return ::SaveInit(size);
  1.1524 +}
  1.1525 +
  1.1526 +//-----------------------------------------------------------------------------
  1.1527 +// Purpose: Saves data from a struct into a saverestore object, to be saved to disk
  1.1528 +// Input  : *pSaveData - the saverestore object
  1.1529 +//			char *pname - the name of the data to write
  1.1530 +//			*pBaseData - the struct into which the data is to be read
  1.1531 +//			*pFields - pointer to an array of data field descriptions
  1.1532 +//			fieldCount - the size of the array (number of field descriptions)
  1.1533 +//-----------------------------------------------------------------------------
  1.1534 +void CServerGameDLL::SaveWriteFields( CSaveRestoreData *pSaveData, const char *pname, void *pBaseData, datamap_t *pMap, typedescription_t *pFields, int fieldCount )
  1.1535 +{
  1.1536 +	CSave saveHelper( pSaveData );
  1.1537 +	saveHelper.WriteFields( pname, pBaseData, pMap, pFields, fieldCount );
  1.1538 +}
  1.1539 +
  1.1540 +
  1.1541 +//-----------------------------------------------------------------------------
  1.1542 +// Purpose: Reads data from a save/restore block into a structure
  1.1543 +// Input  : *pSaveData - the saverestore object
  1.1544 +//			char *pname - the name of the data to extract from
  1.1545 +//			*pBaseData - the struct into which the data is to be restored
  1.1546 +//			*pFields - pointer to an array of data field descriptions
  1.1547 +//			fieldCount - the size of the array (number of field descriptions)
  1.1548 +//-----------------------------------------------------------------------------
  1.1549 +
  1.1550 +//-----------------------------------------------------------------------------
  1.1551 +
  1.1552 +void CServerGameDLL::SaveReadFields( CSaveRestoreData *pSaveData, const char *pname, void *pBaseData, datamap_t *pMap, typedescription_t *pFields, int fieldCount )
  1.1553 +{
  1.1554 +	CRestore restoreHelper( pSaveData );
  1.1555 +	restoreHelper.ReadFields( pname, pBaseData, pMap, pFields, fieldCount );
  1.1556 +}
  1.1557 +
  1.1558 +//-----------------------------------------------------------------------------
  1.1559 +
  1.1560 +void CServerGameDLL::SaveGlobalState( CSaveRestoreData *s )
  1.1561 +{
  1.1562 +	::SaveGlobalState(s);
  1.1563 +}
  1.1564 +
  1.1565 +void CServerGameDLL::RestoreGlobalState(CSaveRestoreData *s)
  1.1566 +{
  1.1567 +	::RestoreGlobalState(s);
  1.1568 +}
  1.1569 +
  1.1570 +void CServerGameDLL::Save( CSaveRestoreData *s )
  1.1571 +{
  1.1572 +	CSave saveHelper( s );
  1.1573 +	g_pGameSaveRestoreBlockSet->Save( &saveHelper );
  1.1574 +}
  1.1575 +
  1.1576 +void CServerGameDLL::Restore( CSaveRestoreData *s, bool b)
  1.1577 +{
  1.1578 +	if ( engine->IsOverrideLoadGameEntsOn() )
  1.1579 +		FoundryHelpers_ClearEntityHighlightEffects();
  1.1580 +
  1.1581 +	CRestore restore(s);
  1.1582 +	g_pGameSaveRestoreBlockSet->Restore( &restore, b );
  1.1583 +	g_pGameSaveRestoreBlockSet->PostRestore();
  1.1584 +
  1.1585 +	if ( serverfoundry && engine->IsOverrideLoadGameEntsOn() )
  1.1586 +		serverfoundry->OnFinishedRestoreSavegame();
  1.1587 +}
  1.1588 +
  1.1589 +//-----------------------------------------------------------------------------
  1.1590 +// Purpose: 
  1.1591 +// Input  : msg_type - 
  1.1592 +//			*name - 
  1.1593 +//			size - 
  1.1594 +// Output : Returns true on success, false on failure.
  1.1595 +//-----------------------------------------------------------------------------
  1.1596 +
  1.1597 +bool CServerGameDLL::GetUserMessageInfo( int msg_type, char *name, int maxnamelength, int& size )
  1.1598 +{
  1.1599 +	if ( !usermessages->IsValidIndex( msg_type ) )
  1.1600 +		return false;
  1.1601 +
  1.1602 +	Q_strncpy( name, usermessages->GetUserMessageName( msg_type ), maxnamelength );
  1.1603 +	size = usermessages->GetUserMessageSize( msg_type );
  1.1604 +	return true;
  1.1605 +}
  1.1606 +
  1.1607 +CStandardSendProxies* CServerGameDLL::GetStandardSendProxies()
  1.1608 +{
  1.1609 +	return &g_StandardSendProxies;
  1.1610 +}
  1.1611 +
  1.1612 +int	CServerGameDLL::CreateEntityTransitionList( CSaveRestoreData *s, int a)
  1.1613 +{
  1.1614 +	CRestore restoreHelper( s );
  1.1615 +	// save off file base
  1.1616 +	int base = restoreHelper.GetReadPos();
  1.1617 +
  1.1618 +	int movedCount = ::CreateEntityTransitionList(s, a);
  1.1619 +	if ( movedCount )
  1.1620 +	{
  1.1621 +		g_pGameSaveRestoreBlockSet->CallBlockHandlerRestore( GetPhysSaveRestoreBlockHandler(), base, &restoreHelper, false );
  1.1622 +		g_pGameSaveRestoreBlockSet->CallBlockHandlerRestore( GetAISaveRestoreBlockHandler(), base, &restoreHelper, false );
  1.1623 +	}
  1.1624 +
  1.1625 +	GetPhysSaveRestoreBlockHandler()->PostRestore();
  1.1626 +	GetAISaveRestoreBlockHandler()->PostRestore();
  1.1627 +
  1.1628 +	return movedCount;
  1.1629 +}
  1.1630 +
  1.1631 +void CServerGameDLL::PreSave( CSaveRestoreData *s )
  1.1632 +{
  1.1633 +	g_pGameSaveRestoreBlockSet->PreSave( s );
  1.1634 +}
  1.1635 +
  1.1636 +#include "client_textmessage.h"
  1.1637 +
  1.1638 +// This little hack lets me marry BSP names to messages in titles.txt
  1.1639 +typedef struct
  1.1640 +{
  1.1641 +	char *pBSPName;
  1.1642 +	char *pTitleName;
  1.1643 +} TITLECOMMENT;
  1.1644 +
  1.1645 +// this list gets searched for the first partial match, so some are out of order
  1.1646 +static TITLECOMMENT gTitleComments[] =
  1.1647 +{
  1.1648 +
  1.1649 +	{ "intro", "#HL2_Chapter1_Title" },
  1.1650 +
  1.1651 +	{ "d1_trainstation_05", "#HL2_Chapter2_Title" },
  1.1652 +	{ "d1_trainstation_06", "#HL2_Chapter2_Title" },
  1.1653 +	
  1.1654 +	{ "d1_trainstation_", "#HL2_Chapter1_Title" },
  1.1655 +
  1.1656 +	{ "d1_canals_06", "#HL2_Chapter4_Title" },
  1.1657 +	{ "d1_canals_07", "#HL2_Chapter4_Title" },
  1.1658 +	{ "d1_canals_08", "#HL2_Chapter4_Title" },
  1.1659 +	{ "d1_canals_09", "#HL2_Chapter4_Title" },
  1.1660 +	{ "d1_canals_1", "#HL2_Chapter4_Title" },
  1.1661 +	
  1.1662 +	{ "d1_canals_0", "#HL2_Chapter3_Title" },
  1.1663 +
  1.1664 +	{ "d1_eli_", "#HL2_Chapter5_Title" },
  1.1665 +
  1.1666 +	{ "d1_town_", "#HL2_Chapter6_Title" },
  1.1667 +
  1.1668 +	{ "d2_coast_09", "#HL2_Chapter8_Title" },
  1.1669 +	{ "d2_coast_1", "#HL2_Chapter8_Title" },
  1.1670 +	{ "d2_prison_01", "#HL2_Chapter8_Title" },
  1.1671 +
  1.1672 +	{ "d2_coast_", "#HL2_Chapter7_Title" },
  1.1673 +
  1.1674 +	{ "d2_prison_06", "#HL2_Chapter9a_Title" },
  1.1675 +	{ "d2_prison_07", "#HL2_Chapter9a_Title" },
  1.1676 +	{ "d2_prison_08", "#HL2_Chapter9a_Title" },
  1.1677 +
  1.1678 +	{ "d2_prison_", "#HL2_Chapter9_Title" },
  1.1679 +
  1.1680 +	{ "d3_c17_01", "#HL2_Chapter9a_Title" },
  1.1681 +	{ "d3_c17_09", "#HL2_Chapter11_Title" },
  1.1682 +	{ "d3_c17_1", "#HL2_Chapter11_Title" },
  1.1683 +
  1.1684 +	{ "d3_c17_", "#HL2_Chapter10_Title" },
  1.1685 +
  1.1686 +	{ "d3_citadel_", "#HL2_Chapter12_Title" },
  1.1687 +
  1.1688 +	{ "d3_breen_", "#HL2_Chapter13_Title" },
  1.1689 +	{ "credits", "#HL2_Chapter14_Title" },
  1.1690 +
  1.1691 +	{ "ep1_citadel_00", "#episodic_Chapter1_Title" },
  1.1692 +	{ "ep1_citadel_01", "#episodic_Chapter1_Title" },
  1.1693 +	{ "ep1_citadel_02b", "#episodic_Chapter1_Title" },
  1.1694 +	{ "ep1_citadel_02", "#episodic_Chapter1_Title" },
  1.1695 +	{ "ep1_citadel_03", "#episodic_Chapter2_Title" },
  1.1696 +	{ "ep1_citadel_04", "#episodic_Chapter2_Title" },
  1.1697 +	{ "ep1_c17_00a", "#episodic_Chapter3_Title" },
  1.1698 +	{ "ep1_c17_00", "#episodic_Chapter3_Title" },
  1.1699 +	{ "ep1_c17_01", "#episodic_Chapter4_Title" },
  1.1700 +	{ "ep1_c17_02b", "#episodic_Chapter4_Title" },
  1.1701 +	{ "ep1_c17_02", "#episodic_Chapter4_Title" },
  1.1702 +	{ "ep1_c17_05", "#episodic_Chapter5_Title" },
  1.1703 +	{ "ep1_c17_06", "#episodic_Chapter5_Title" },
  1.1704 +
  1.1705 +	{ "ep2_outland_01a", "#ep2_Chapter1_Title" },
  1.1706 +	{ "ep2_outland_01", "#ep2_Chapter1_Title" },
  1.1707 +	{ "ep2_outland_02", "#ep2_Chapter2_Title" },
  1.1708 +	{ "ep2_outland_03", "#ep2_Chapter2_Title" },
  1.1709 +	{ "ep2_outland_04", "#ep2_Chapter2_Title" },
  1.1710 +	{ "ep2_outland_05", "#ep2_Chapter3_Title" },
  1.1711 +	
  1.1712 +	{ "ep2_outland_06a", "#ep2_Chapter4_Title" },
  1.1713 +	{ "ep2_outland_06", "#ep2_Chapter3_Title" },
  1.1714 +
  1.1715 +	{ "ep2_outland_07", "#ep2_Chapter4_Title" },
  1.1716 +	{ "ep2_outland_08", "#ep2_Chapter4_Title" },
  1.1717 +	{ "ep2_outland_09", "#ep2_Chapter5_Title" },
  1.1718 +	
  1.1719 +	{ "ep2_outland_10a", "#ep2_Chapter5_Title" },
  1.1720 +	{ "ep2_outland_10", "#ep2_Chapter5_Title" },
  1.1721 +
  1.1722 +	{ "ep2_outland_11a", "#ep2_Chapter6_Title" },
  1.1723 +	{ "ep2_outland_11", "#ep2_Chapter6_Title" },
  1.1724 +	
  1.1725 +	{ "ep2_outland_12a", "#ep2_Chapter7_Title" },
  1.1726 +	{ "ep2_outland_12", "#ep2_Chapter6_Title" },
  1.1727 +
  1.1728 +};
  1.1729 +
  1.1730 +void CServerGameDLL::GetSaveComment( char *text, int maxlength, float flMinutes, float flSeconds, bool bNoTime )
  1.1731 +{
  1.1732 +	char comment[64];
  1.1733 +	const char	*pName;
  1.1734 +	int		i;
  1.1735 +
  1.1736 +	char const *mapname = STRING( gpGlobals->mapname );
  1.1737 +
  1.1738 +	pName = NULL;
  1.1739 +
  1.1740 +	// Try to find a matching title comment for this mapname
  1.1741 +	for ( i = 0; i < ARRAYSIZE(gTitleComments) && !pName; i++ )
  1.1742 +	{
  1.1743 +		if ( !Q_strnicmp( mapname, gTitleComments[i].pBSPName, strlen(gTitleComments[i].pBSPName) ) )
  1.1744 +		{
  1.1745 +			// found one
  1.1746 +			int j;
  1.1747 +
  1.1748 +			// Got a message, post-process it to be save name friendly
  1.1749 +			Q_strncpy( comment, gTitleComments[i].pTitleName, sizeof( comment ) );
  1.1750 +			pName = comment;
  1.1751 +			j = 0;
  1.1752 +			// Strip out CRs
  1.1753 +			while ( j < 64 && comment[j] )
  1.1754 +			{
  1.1755 +				if ( comment[j] == '\n' || comment[j] == '\r' )
  1.1756 +					comment[j] = 0;
  1.1757 +				else
  1.1758 +					j++;
  1.1759 +			}
  1.1760 +			break;
  1.1761 +		}
  1.1762 +	}
  1.1763 +	
  1.1764 +	// If we didn't get one, use the designer's map name, or the BSP name itself
  1.1765 +	if ( !pName )
  1.1766 +	{
  1.1767 +		pName = mapname;
  1.1768 +	}
  1.1769 +
  1.1770 +	if ( bNoTime )
  1.1771 +	{
  1.1772 +		Q_snprintf( text, maxlength, "%-64.64s", pName );
  1.1773 +	}
  1.1774 +	else
  1.1775 +	{
  1.1776 +		int minutes = flMinutes;
  1.1777 +		int seconds = flSeconds;
  1.1778 +
  1.1779 +		// Wow, this guy/gal must suck...!
  1.1780 +		if ( minutes >= 1000 )
  1.1781 +		{
  1.1782 +			minutes = 999;
  1.1783 +			seconds = 59;
  1.1784 +		}
  1.1785 +
  1.1786 +		int minutesAdd = ( seconds / 60 );
  1.1787 +		seconds %= 60;
  1.1788 +
  1.1789 +		// add the elapsed time at the end of the comment, for the ui to parse out
  1.1790 +		Q_snprintf( text, maxlength, "%-64.64s %03d:%02d", pName, (minutes + minutesAdd), seconds );
  1.1791 +	}
  1.1792 +}
  1.1793 +
  1.1794 +void CServerGameDLL::WriteSaveHeaders( CSaveRestoreData *s )
  1.1795 +{
  1.1796 +	CSave saveHelper( s );
  1.1797 +	g_pGameSaveRestoreBlockSet->WriteSaveHeaders( &saveHelper );
  1.1798 +	g_pGameSaveRestoreBlockSet->PostSave();
  1.1799 +}
  1.1800 +
  1.1801 +void CServerGameDLL::ReadRestoreHeaders( CSaveRestoreData *s )
  1.1802 +{
  1.1803 +	CRestore restoreHelper( s );
  1.1804 +	g_pGameSaveRestoreBlockSet->PreRestore();
  1.1805 +	g_pGameSaveRestoreBlockSet->ReadRestoreHeaders( &restoreHelper );
  1.1806 +}
  1.1807 +
  1.1808 +void CServerGameDLL::PreSaveGameLoaded( char const *pSaveName, bool bInGame )
  1.1809 +{
  1.1810 +	gamestats->Event_PreSaveGameLoaded( pSaveName, bInGame );
  1.1811 +}
  1.1812 +
  1.1813 +//-----------------------------------------------------------------------------
  1.1814 +// Purpose: Returns true if the game DLL wants the server not to be made public.
  1.1815 +//			Used by commentary system to hide multiplayer commentary servers from the master.
  1.1816 +//-----------------------------------------------------------------------------
  1.1817 +bool CServerGameDLL::ShouldHideServer( void )
  1.1818 +{
  1.1819 +	if ( g_pcv_commentary && g_pcv_commentary->GetBool() )
  1.1820 +		return true;
  1.1821 +
  1.1822 +	if ( gpGlobals && gpGlobals->eLoadType == MapLoad_Background )
  1.1823 +		return true;
  1.1824 +
  1.1825 +	return false;
  1.1826 +}
  1.1827 +
  1.1828 +//-----------------------------------------------------------------------------
  1.1829 +//
  1.1830 +//-----------------------------------------------------------------------------
  1.1831 +void CServerGameDLL::InvalidateMdlCache()
  1.1832 +{
  1.1833 +	CBaseAnimating *pAnimating;
  1.1834 +	for ( CBaseEntity *pEntity = gEntList.FirstEnt(); pEntity != NULL; pEntity = gEntList.NextEnt(pEntity) )
  1.1835 +	{
  1.1836 +		pAnimating = dynamic_cast<CBaseAnimating *>(pEntity);
  1.1837 +		if ( pAnimating )
  1.1838 +		{
  1.1839 +			pAnimating->InvalidateMdlCache();
  1.1840 +		}
  1.1841 +	}
  1.1842 +	CStudioHdr::CActivityToSequenceMapping::ResetMappings();
  1.1843 +}
  1.1844 +
  1.1845 +KeyValues * CServerGameDLL::FindLaunchOptionByValue( KeyValues *pLaunchOptions, char const *szLaunchOption )
  1.1846 +{
  1.1847 +	if ( !pLaunchOptions || !szLaunchOption || !*szLaunchOption )
  1.1848 +		return NULL;
  1.1849 +
  1.1850 +	for ( KeyValues *val = pLaunchOptions->GetFirstSubKey(); val; val = val->GetNextKey() )
  1.1851 +	{
  1.1852 +		char const *szValue = val->GetString();
  1.1853 +		if ( szValue && *szValue && !Q_stricmp( szValue, szLaunchOption ) )
  1.1854 +			return val;
  1.1855 +	}
  1.1856 +
  1.1857 +	return NULL;
  1.1858 +}
  1.1859 +
  1.1860 +bool CServerGameDLL::ShouldPreferSteamAuth()
  1.1861 +{
  1.1862 +	return true;
  1.1863 +}
  1.1864 +
  1.1865 +bool CServerGameDLL::SupportsRandomMaps()
  1.1866 +{
  1.1867 +#ifdef INFESTED_DLL
  1.1868 +	return true;
  1.1869 +#else
  1.1870 +	return false;
  1.1871 +#endif
  1.1872 +}
  1.1873 +
  1.1874 +// return true to disconnect client due to timeout (used to do stricter timeouts when the game is sure the client isn't loading a map)
  1.1875 +bool CServerGameDLL::ShouldTimeoutClient( int nUserID, float flTimeSinceLastReceived )
  1.1876 +{
  1.1877 +	if ( !g_pGameRules )
  1.1878 +		return false;
  1.1879 +
  1.1880 +	return g_pGameRules->ShouldTimeoutClient( nUserID, flTimeSinceLastReceived );
  1.1881 +}
  1.1882 +
  1.1883 +void CServerGameDLL::GetMatchmakingTags( char *buf, size_t bufSize )
  1.1884 +{
  1.1885 +#ifdef TERROR
  1.1886 +	// Additional "tags" that L4D matchmaking wants to know about
  1.1887 +	// static ConVarRef mp_gamemode( "mp_gamemode" );
  1.1888 +	extern ConVar mp_gamemode;
  1.1889 +
  1.1890 +	if ( g_pMatchExtL4D )
  1.1891 +	{
  1.1892 +		char const *szBaseGameMode = g_pMatchExtL4D->GetGameModeInfo( mp_gamemode.GetString() )->GetString( "base", mp_gamemode.GetString() );
  1.1893 +		if ( szBaseGameMode && szBaseGameMode[0] )
  1.1894 +		{
  1.1895 +			Q_strncpy( buf, szBaseGameMode, bufSize );
  1.1896 +			return;
  1.1897 +		}
  1.1898 +	}
  1.1899 +
  1.1900 +	Q_strncpy( buf, mp_gamemode.GetString(), bufSize );
  1.1901 +#endif
  1.1902 +}
  1.1903 +
  1.1904 +void CServerGameDLL::GetMatchmakingGameData( char *buf, size_t bufSize )
  1.1905 +{
  1.1906 +	char * const bufBase = buf;
  1.1907 +
  1.1908 +#ifdef TERROR
  1.1909 +	int len = 0;
  1.1910 +
  1.1911 +	// Put the game key
  1.1912 +	Q_snprintf( buf, bufSize, "g:l4d2," );
  1.1913 +	len = strlen( buf );
  1.1914 +	buf += len;
  1.1915 +	bufSize -= len;
  1.1916 +
  1.1917 +	// Supported l4d2 game types
  1.1918 +	static ConVarRef sv_gametypes( "sv_gametypes" );
  1.1919 +	if ( sv_gametypes.IsValid() && sv_gametypes.GetString()[0] )
  1.1920 +	{
  1.1921 +		Q_snprintf( buf, bufSize, "%s,", sv_gametypes.GetString() );
  1.1922 +		len = strlen( buf );
  1.1923 +		buf += len;
  1.1924 +		bufSize -= len;
  1.1925 +	}
  1.1926 +
  1.1927 +	// Additional "game data" that L4D matchmaking wants to know about
  1.1928 +	KeyValues *pAllMissions = g_pMatchExtL4D->GetAllMissions();
  1.1929 +
  1.1930 +	// Build a list of missions
  1.1931 +	int numMissions = 0;
  1.1932 +	for ( KeyValues *pMission = pAllMissions ? pAllMissions->GetFirstTrueSubKey() : NULL;
  1.1933 +		  pMission;
  1.1934 +		  pMission = pMission->GetNextTrueSubKey() )
  1.1935 +	{
  1.1936 +		if ( pMission->GetInt( "BuiltIn" ) )
  1.1937 +			continue;
  1.1938 +
  1.1939 +		char const *szMission = pMission->GetString( "CfgTag" );
  1.1940 +		int len = strlen( szMission );
  1.1941 +
  1.1942 +		if ( bufSize <= len + 1 )
  1.1943 +		{
  1.1944 +			Warning( "GameData: Too many missions installed, not advertising for mission \"%s\"\n", pMission->GetString( "Name" ) );
  1.1945 +			continue;
  1.1946 +		}
  1.1947 +
  1.1948 +		Q_strncpy( buf, szMission, len + 1 );
  1.1949 +		buf += len;
  1.1950 +		*( buf ++ ) = ',';
  1.1951 +		bufSize -= len + 1;
  1.1952 +
  1.1953 +		++ numMissions;
  1.1954 +	}
  1.1955 +#endif
  1.1956 +
  1.1957 +	// Trim the last comma if anything was written
  1.1958 +	if ( buf > bufBase )
  1.1959 +		buf[ -1 ] = 0;
  1.1960 +}
  1.1961 +
  1.1962 +void CServerGameDLL::ServerHibernationUpdate( bool bHibernating )
  1.1963 +{
  1.1964 +	m_bIsHibernating = bHibernating;
  1.1965 +
  1.1966 +#ifdef INFESTED_DLL
  1.1967 +	if ( engine && engine->IsDedicatedServer() && m_bIsHibernating && ASWGameRules() )
  1.1968 +	{
  1.1969 +		ASWGameRules()->OnServerHibernating();
  1.1970 +	}
  1.1971 +#endif
  1.1972 +}
  1.1973 +
  1.1974 +//-----------------------------------------------------------------------------
  1.1975 +// Purpose: Called during a transition, to build a map adjacency list
  1.1976 +//-----------------------------------------------------------------------------
  1.1977 +void CServerGameDLL::BuildAdjacentMapList( void )
  1.1978 +{
  1.1979 +	// retrieve the pointer to the save data
  1.1980 +	CSaveRestoreData *pSaveData = gpGlobals->pSaveData;
  1.1981 +
  1.1982 +	if ( pSaveData )
  1.1983 +		pSaveData->levelInfo.connectionCount = BuildChangeList( pSaveData->levelInfo.levelList, MAX_LEVEL_CONNECTIONS );
  1.1984 +}
  1.1985 +
  1.1986 +//-----------------------------------------------------------------------------
  1.1987 +// Purpose: Sanity-check to verify that a path is a relative path inside the game dir
  1.1988 +// Taken From: engine/cmd.cpp
  1.1989 +//-----------------------------------------------------------------------------
  1.1990 +static bool IsValidPath( const char *pszFilename )
  1.1991 +{
  1.1992 +	if ( !pszFilename )
  1.1993 +	{
  1.1994 +		return false;
  1.1995 +	}
  1.1996 +
  1.1997 +	if ( Q_strlen( pszFilename ) <= 0    ||
  1.1998 +		 Q_IsAbsolutePath( pszFilename ) || // to protect absolute paths
  1.1999 +		 Q_strstr( pszFilename, ".." ) )    // to protect relative paths
  1.2000 +	{
  1.2001 +		return false;
  1.2002 +	}
  1.2003 +
  1.2004 +	return true;
  1.2005 +}
  1.2006 +
  1.2007 +static void ValidateMOTDFilename( IConVar *pConVar, const char *oldValue, float flOldValue )
  1.2008 +{
  1.2009 +	ConVarRef var( pConVar );
  1.2010 +	if ( !IsValidPath( var.GetString() ) )
  1.2011 +	{
  1.2012 +		var.SetValue( var.GetDefault() );
  1.2013 +	}
  1.2014 +}
  1.2015 +
  1.2016 +static ConVar motdfile( "motdfile", "motd.txt", FCVAR_RELEASE, "The MOTD file to load.", ValidateMOTDFilename );
  1.2017 +static ConVar hostfile( "hostfile", "host.txt", FCVAR_RELEASE, "The HOST file to load.", ValidateMOTDFilename );
  1.2018 +void LoadMOTDFile( const char *stringname, ConVar *pConvarFilename )
  1.2019 +{
  1.2020 +	char data[2048];
  1.2021 +
  1.2022 +	int length = filesystem->Size( pConvarFilename->GetString(), "GAME" );
  1.2023 +	if ( length <= 0 || length >= (sizeof(data)-1) )
  1.2024 +	{
  1.2025 +		DevMsg("Invalid file size for %s\n", pConvarFilename->GetString() );
  1.2026 +		return;
  1.2027 +	}
  1.2028 +
  1.2029 +	FileHandle_t hFile = filesystem->Open( pConvarFilename->GetString(), "rb", "GAME" );
  1.2030 +	if ( hFile == FILESYSTEM_INVALID_HANDLE )
  1.2031 +		return;
  1.2032 +
  1.2033 +	filesystem->Read( data, length, hFile );
  1.2034 +	filesystem->Close( hFile );
  1.2035 +
  1.2036 +	data[length] = 0;
  1.2037 +
  1.2038 +	g_pStringTableInfoPanel->AddString( CBaseEntity::IsServer(), stringname, length+1, data );
  1.2039 +}
  1.2040 +
  1.2041 +void CServerGameDLL::LoadMessageOfTheDay()
  1.2042 +{
  1.2043 +	LoadMOTDFile( "motd", &motdfile );
  1.2044 +	LoadMOTDFile( "hostfile", &hostfile );
  1.2045 +}
  1.2046 +
  1.2047 +// keeps track of which chapters the user has unlocked
  1.2048 +ConVar sv_unlockedchapters( "sv_unlockedchapters", "1", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX );
  1.2049 +
  1.2050 +//-----------------------------------------------------------------------------
  1.2051 +// Purpose: Updates which chapters are unlocked
  1.2052 +//-----------------------------------------------------------------------------
  1.2053 +void UpdateChapterRestrictions( const char *mapname )
  1.2054 +{
  1.2055 +	// look at the chapter for this map
  1.2056 +	char chapterTitle[64];
  1.2057 +	chapterTitle[0] = 0;
  1.2058 +	for ( int i = 0; i < ARRAYSIZE(gTitleComments); i++ )
  1.2059 +	{
  1.2060 +		if ( !Q_strnicmp( mapname, gTitleComments[i].pBSPName, strlen(gTitleComments[i].pBSPName) ) )
  1.2061 +		{
  1.2062 +			// found
  1.2063 +			Q_strncpy( chapterTitle, gTitleComments[i].pTitleName, sizeof( chapterTitle ) );
  1.2064 +			int j = 0;
  1.2065 +			while ( j < 64 && chapterTitle[j] )
  1.2066 +			{
  1.2067 +				if ( chapterTitle[j] == '\n' || chapterTitle[j] == '\r' )
  1.2068 +					chapterTitle[j] = 0;
  1.2069 +				else
  1.2070 +					j++;
  1.2071 +			}
  1.2072 +
  1.2073 +			break;
  1.2074 +		}
  1.2075 +	}
  1.2076 +
  1.2077 +	if ( !chapterTitle[0] )
  1.2078 +		return;
  1.2079 +
  1.2080 +	// make sure the specified chapter title is unlocked
  1.2081 +	strlwr( chapterTitle );
  1.2082 +	
  1.2083 +	// Get our active mod directory name
  1.2084 +	char modDir[MAX_PATH];
  1.2085 +	if ( UTIL_GetModDir( modDir, sizeof(modDir) ) == false )
  1.2086 +		return;
  1.2087 +
  1.2088 +	char chapterNumberPrefix[64];
  1.2089 +	Q_snprintf(chapterNumberPrefix, sizeof(chapterNumberPrefix), "#%s_chapter", modDir);
  1.2090 +
  1.2091 +	const char *newChapterNumber = strstr( chapterTitle, chapterNumberPrefix );
  1.2092 +	if ( newChapterNumber )
  1.2093 +	{
  1.2094 +		// cut off the front
  1.2095 +		newChapterNumber += strlen( chapterNumberPrefix );
  1.2096 +		char newChapter[32];
  1.2097 +		Q_strncpy( newChapter, newChapterNumber, sizeof(newChapter) );
  1.2098 +
  1.2099 +		// cut off the end
  1.2100 +		char *end = strstr( newChapter, "_title" );
  1.2101 +		if ( end )
  1.2102 +		{
  1.2103 +			*end = 0;
  1.2104 +		}
  1.2105 +
  1.2106 +		int nNewChapter = atoi( newChapter );
  1.2107 +
  1.2108 +		// HACK: HL2 added a zany chapter "9a" which wreaks
  1.2109 +		//       havoc in this stupid atoi-based chapter code.
  1.2110 +		if ( !Q_stricmp( modDir, "hl2" ) )
  1.2111 +		{
  1.2112 +			if ( !Q_stricmp( newChapter, "9a" ) )
  1.2113 +			{
  1.2114 +				nNewChapter = 10;
  1.2115 +			}
  1.2116 +			else if ( nNewChapter > 9 )
  1.2117 +			{
  1.2118 +				nNewChapter++;
  1.2119 +			}
  1.2120 +		}
  1.2121 +
  1.2122 +		// ok we have the string, see if it's newer
  1.2123 +		const char *unlockedChapter = sv_unlockedchapters.GetString();
  1.2124 +		int nUnlockedChapter = atoi( unlockedChapter );
  1.2125 +
  1.2126 +		if ( nUnlockedChapter < nNewChapter )
  1.2127 +		{
  1.2128 +			// ok we're at a higher chapter, unlock
  1.2129 +			sv_unlockedchapters.SetValue( nNewChapter );
  1.2130 +
  1.2131 +			// HACK: Call up through a better function than this? 7/23/07 - jdw
  1.2132 +			if ( IsX360() )
  1.2133 +			{
  1.2134 +				engine->ServerCommand( "host_writeconfig\n" );
  1.2135 +			}
  1.2136 +		}
  1.2137 +
  1.2138 +		g_nCurrentChapterIndex = nNewChapter;
  1.2139 +	}
  1.2140 +}
  1.2141 +
  1.2142 +//-----------------------------------------------------------------------------
  1.2143 +// Precaches a vgui screen overlay material
  1.2144 +//-----------------------------------------------------------------------------
  1.2145 +void PrecacheMaterial( const char *pMaterialName )
  1.2146 +{
  1.2147 +	Assert( pMaterialName && pMaterialName[0] );
  1.2148 +	g_pStringTableMaterials->AddString( CBaseEntity::IsServer(), pMaterialName );
  1.2149 +}
  1.2150 +
  1.2151 +
  1.2152 +//-----------------------------------------------------------------------------
  1.2153 +// Converts a previously precached material into an index
  1.2154 +//-----------------------------------------------------------------------------
  1.2155 +int GetMaterialIndex( const char *pMaterialName )
  1.2156 +{
  1.2157 +	if (pMaterialName)
  1.2158 +	{
  1.2159 +		int nIndex = g_pStringTableMaterials->FindStringIndex( pMaterialName );
  1.2160 +		
  1.2161 +		if (nIndex != INVALID_STRING_INDEX )
  1.2162 +		{
  1.2163 +			return nIndex;
  1.2164 +		}
  1.2165 +		else
  1.2166 +		{
  1.2167 +			DevMsg("Warning! GetMaterialIndex: couldn't find material %s\n ", pMaterialName );
  1.2168 +			return 0;
  1.2169 +		}
  1.2170 +	}
  1.2171 +
  1.2172 +	// This is the invalid string index
  1.2173 +	return 0;
  1.2174 +}
  1.2175 +
  1.2176 +//-----------------------------------------------------------------------------
  1.2177 +// Converts a previously precached material index into a string
  1.2178 +//-----------------------------------------------------------------------------
  1.2179 +const char *GetMaterialNameFromIndex( int nMaterialIndex )
  1.2180 +{
  1.2181 +	return g_pStringTableMaterials->GetString( nMaterialIndex );
  1.2182 +}
  1.2183 +
  1.2184 +
  1.2185 +//-----------------------------------------------------------------------------
  1.2186 +// Precaches a vgui screen overlay material
  1.2187 +//-----------------------------------------------------------------------------
  1.2188 +int PrecacheParticleSystem( const char *pParticleSystemName )
  1.2189 +{
  1.2190 +	Assert( pParticleSystemName && pParticleSystemName[0] );
  1.2191 +	return g_pStringTableParticleEffectNames->AddString( CBaseEntity::IsServer(), pParticleSystemName );
  1.2192 +}
  1.2193 +
  1.2194 +void PrecacheParticleFileAndSystems( const char *pParticleSystemFile )
  1.2195 +{
  1.2196 +	g_pParticleSystemMgr->ShouldLoadSheets( true );
  1.2197 +	g_pParticleSystemMgr->ReadParticleConfigFile( pParticleSystemFile, true, false );
  1.2198 +	g_pParticleSystemMgr->DecommitTempMemory();
  1.2199 +
  1.2200 +	Assert( pParticleSystemFile && pParticleSystemFile[0] );
  1.2201 +	g_pStringTableExtraParticleFiles->AddString( CBaseEntity::IsServer(), pParticleSystemFile );
  1.2202 +
  1.2203 +	CUtlVector<CUtlString> systems;
  1.2204 +	g_pParticleSystemMgr->GetParticleSystemsInFile( pParticleSystemFile, &systems );
  1.2205 +
  1.2206 +	int nCount = systems.Count();
  1.2207 +	for ( int i = 0; i < nCount; ++i )
  1.2208 +	{
  1.2209 +		PrecacheParticleSystem( systems[i] );
  1.2210 +	}
  1.2211 +}
  1.2212 +
  1.2213 +void PrecacheGameSoundsFile( const char *pSoundFile )
  1.2214 +{
  1.2215 +	soundemitterbase->AddSoundsFromFile( pSoundFile, true );
  1.2216 +	SoundSystemPreloadSounds();
  1.2217 +}
  1.2218 +
  1.2219 +//-----------------------------------------------------------------------------
  1.2220 +// Converts a previously precached material into an index
  1.2221 +//-----------------------------------------------------------------------------
  1.2222 +int GetParticleSystemIndex( const char *pParticleSystemName )
  1.2223 +{
  1.2224 +	if ( pParticleSystemName )
  1.2225 +	{
  1.2226 +		int nIndex = g_pStringTableParticleEffectNames->FindStringIndex( pParticleSystemName );
  1.2227 +		if (nIndex != INVALID_STRING_INDEX )
  1.2228 +			return nIndex;
  1.2229 +
  1.2230 +		DevWarning("Server: Missing precache for particle system \"%s\"!\n", pParticleSystemName );
  1.2231 +	}
  1.2232 +
  1.2233 +	// This is the invalid string index
  1.2234 +	return 0;
  1.2235 +}
  1.2236 +
  1.2237 +//-----------------------------------------------------------------------------
  1.2238 +// Converts a previously precached material index into a string
  1.2239 +//-----------------------------------------------------------------------------
  1.2240 +const char *GetParticleSystemNameFromIndex( int nMaterialIndex )
  1.2241 +{
  1.2242 +	if ( nMaterialIndex < g_pStringTableParticleEffectNames->GetMaxStrings() )
  1.2243 +		return g_pStringTableParticleEffectNames->GetString( nMaterialIndex );
  1.2244 +	return "error";
  1.2245 +}
  1.2246 +
  1.2247 +
  1.2248 +//-----------------------------------------------------------------------------
  1.2249 +// Precaches an effect (used by DispatchEffect)
  1.2250 +//-----------------------------------------------------------------------------
  1.2251 +void PrecacheEffect( const char *pEffectName )
  1.2252 +{
  1.2253 +	Assert( pEffectName && pEffectName[0] );
  1.2254 +	g_pStringTableEffectDispatch->AddString( CBaseEntity::IsServer(), pEffectName );
  1.2255 +}
  1.2256 +
  1.2257 +
  1.2258 +//-----------------------------------------------------------------------------
  1.2259 +// Converts a previously precached effect into an index
  1.2260 +//-----------------------------------------------------------------------------
  1.2261 +int GetEffectIndex( const char *pEffectName )
  1.2262 +{
  1.2263 +	if ( pEffectName )
  1.2264 +	{
  1.2265 +		int nIndex = g_pStringTableEffectDispatch->FindStringIndex( pEffectName );
  1.2266 +		if (nIndex != INVALID_STRING_INDEX )
  1.2267 +			return nIndex;
  1.2268 +
  1.2269 +		DevWarning("Server: Missing precache for effect \"%s\"!\n", pEffectName );
  1.2270 +	}
  1.2271 +
  1.2272 +	// This is the invalid string index
  1.2273 +	return 0;
  1.2274 +}
  1.2275 +
  1.2276 +//-----------------------------------------------------------------------------
  1.2277 +// Converts a previously precached effect index into a string
  1.2278 +//-----------------------------------------------------------------------------
  1.2279 +const char *GetEffectNameFromIndex( int nEffectIndex )
  1.2280 +{
  1.2281 +	if ( nEffectIndex < g_pStringTableEffectDispatch->GetMaxStrings() )
  1.2282 +		return g_pStringTableEffectDispatch->GetString( nEffectIndex );
  1.2283 +	return "error";
  1.2284 +}
  1.2285 +
  1.2286 +
  1.2287 +
  1.2288 +//-----------------------------------------------------------------------------
  1.2289 +// Returns true if host_thread_mode is set to non-zero (and engine is running in threaded mode)
  1.2290 +//-----------------------------------------------------------------------------
  1.2291 +bool IsEngineThreaded()
  1.2292 +{
  1.2293 +	if ( g_pcv_ThreadMode )
  1.2294 +	{
  1.2295 +		return g_pcv_ThreadMode->GetBool();
  1.2296 +	}
  1.2297 +	return false;
  1.2298 +}
  1.2299 +
  1.2300 +class CServerGameEnts : public IServerGameEnts
  1.2301 +{
  1.2302 +public:
  1.2303 +	virtual void			MarkEntitiesAsTouching( edict_t *e1, edict_t *e2 );
  1.2304 +	virtual void			FreeContainingEntity( edict_t * ); 
  1.2305 +	virtual edict_t*		BaseEntityToEdict( CBaseEntity *pEnt );
  1.2306 +	virtual CBaseEntity*	EdictToBaseEntity( edict_t *pEdict );
  1.2307 +	virtual void			CheckTransmit( CCheckTransmitInfo *pInfo, const unsigned short *pEdictIndices, int nEdicts );
  1.2308 +	virtual void			PrepareForFullUpdate( edict_t *pEdict );
  1.2309 +};
  1.2310 +EXPOSE_SINGLE_INTERFACE(CServerGameEnts, IServerGameEnts, INTERFACEVERSION_SERVERGAMEENTS);
  1.2311 +
  1.2312 +//-----------------------------------------------------------------------------
  1.2313 +// Purpose: Marks entities as touching
  1.2314 +// Input  : *e1 - 
  1.2315 +//			*e2 - 
  1.2316 +//-----------------------------------------------------------------------------
  1.2317 +void CServerGameEnts::MarkEntitiesAsTouching( edict_t *e1, edict_t *e2 )
  1.2318 +{
  1.2319 +	CBaseEntity *entity = GetContainingEntity( e1 );
  1.2320 +	CBaseEntity *entityTouched = GetContainingEntity( e2 );
  1.2321 +	if ( entity && entityTouched )
  1.2322 +	{
  1.2323 +		// HACKHACK: UNDONE: Pass in the trace here??!?!?
  1.2324 +		trace_t tr;
  1.2325 +		UTIL_ClearTrace( tr );
  1.2326 +		tr.endpos = (entity->GetAbsOrigin() + entityTouched->GetAbsOrigin()) * 0.5;
  1.2327 +		entity->PhysicsMarkEntitiesAsTouching( entityTouched, tr );
  1.2328 +	}
  1.2329 +}
  1.2330 +
  1.2331 +void CServerGameEnts::FreeContainingEntity( edict_t *e )
  1.2332 +{
  1.2333 +	::FreeContainingEntity(e);
  1.2334 +}
  1.2335 +
  1.2336 +edict_t* CServerGameEnts::BaseEntityToEdict( CBaseEntity *pEnt )
  1.2337 +{
  1.2338 +	if ( pEnt )
  1.2339 +		return pEnt->edict();
  1.2340 +	else
  1.2341 +		return NULL;
  1.2342 +}
  1.2343 +
  1.2344 +CBaseEntity* CServerGameEnts::EdictToBaseEntity( edict_t *pEdict )
  1.2345 +{
  1.2346 +	if ( pEdict )
  1.2347 +		return CBaseEntity::Instance( pEdict );
  1.2348 +	else
  1.2349 +		return NULL;
  1.2350 +}
  1.2351 +
  1.2352 +
  1.2353 +/* Yuck.. ideally this would be in CServerNetworkProperty's header, but it requires CBaseEntity and
  1.2354 +// inlining it gives a nice speedup.
  1.2355 +inline void CServerNetworkProperty::CheckTransmit( CCheckTransmitInfo *pInfo )
  1.2356 +{
  1.2357 +	// If we have a transmit proxy, let it hook our ShouldTransmit return value.
  1.2358 +	if ( m_pTransmitProxy )
  1.2359 +	{
  1.2360 +		nShouldTransmit = m_pTransmitProxy->ShouldTransmit( pInfo, nShouldTransmit );
  1.2361 +	}
  1.2362 +
  1.2363 +	if ( m_pOuter->ShouldTransmit( pInfo ) )
  1.2364 +	{
  1.2365 +		m_pOuter->SetTransmit( pInfo );
  1.2366 +	}
  1.2367 +} */
  1.2368 +
  1.2369 +void CServerGameEnts::CheckTransmit( CCheckTransmitInfo *pInfo, const unsigned short *pEdictIndices, int nEdicts )
  1.2370 +{
  1.2371 +	// NOTE: for speed's sake, this assumes that all networkables are CBaseEntities and that the edict list
  1.2372 +	// is consecutive in memory. If either of these things change, then this routine needs to change, but
  1.2373 +	// ideally we won't be calling any virtual from this routine. This speedy routine was added as an
  1.2374 +	// optimization which would be nice to keep.
  1.2375 +	edict_t *pBaseEdict = gpGlobals->pEdicts;
  1.2376 +
  1.2377 +	CBaseEntity *pRecipientEntity = CBaseEntity::Instance( pInfo->m_pClientEnt );
  1.2378 +	Assert( pRecipientEntity && pRecipientEntity->IsPlayer() );
  1.2379 +	if ( !pRecipientEntity )
  1.2380 +		return;
  1.2381 +	
  1.2382 +	MDLCACHE_CRITICAL_SECTION();
  1.2383 +	CBasePlayer *pRecipientPlayer = static_cast<CBasePlayer*>( pRecipientEntity );
  1.2384 +	const int skyBoxArea = pRecipientPlayer->m_Local.m_skybox3d.area;
  1.2385 +#ifndef _X360
  1.2386 +	const bool bIsHLTV = pRecipientPlayer->IsHLTV();
  1.2387 +#if defined( REPLAY_ENABLED )
  1.2388 +	const bool bIsReplay = pRecipientPlayer->IsReplay();
  1.2389 +#else
  1.2390 +	const bool bIsReplay = false;
  1.2391 +#endif
  1.2392 +
  1.2393 +	// m_pTransmitAlways must be set if HLTV client
  1.2394 +	Assert( bIsHLTV == ( pInfo->m_pTransmitAlways != NULL) ||
  1.2395 +		    bIsReplay == ( pInfo->m_pTransmitAlways != NULL) );
  1.2396 +#endif
  1.2397 +
  1.2398 +	for ( int i=0; i < nEdicts; i++ )
  1.2399 +	{
  1.2400 +		int iEdict = pEdictIndices[i];
  1.2401 +#if _X360
  1.2402 +		if ( i < nEdicts-1 )
  1.2403 +		{
  1.2404 +			PREFETCH360(&pBaseEdict[pEdictIndices[i+1]],0);
  1.2405 +		}
  1.2406 +#endif
  1.2407 +
  1.2408 +		edict_t *pEdict = &pBaseEdict[iEdict];
  1.2409 +		int nFlags = pEdict->m_fStateFlags & (FL_EDICT_DONTSEND|FL_EDICT_ALWAYS|FL_EDICT_PVSCHECK|FL_EDICT_FULLCHECK);
  1.2410 +
  1.2411 +		// entity needs no transmit
  1.2412 +		if ( nFlags & FL_EDICT_DONTSEND )
  1.2413 +			continue;
  1.2414 +		PREFETCH360(pEdict->GetUnknown(),0);
  1.2415 +
  1.2416 +		// entity is already marked for sending
  1.2417 +		if ( pInfo->m_pTransmitEdict->Get( iEdict ) )
  1.2418 +			continue;
  1.2419 +		
  1.2420 +		if ( nFlags & FL_EDICT_ALWAYS )
  1.2421 +		{
  1.2422 +			// FIXME: Hey! Shouldn't this be using SetTransmit so as 
  1.2423 +			// to also force network down dependent entities?
  1.2424 +			while ( true )
  1.2425 +			{
  1.2426 +				// mark entity for sending
  1.2427 +				pInfo->m_pTransmitEdict->Set( iEdict );
  1.2428 +	
  1.2429 +#ifndef _X360
  1.2430 +				if ( bIsHLTV || bIsReplay )
  1.2431 +				{
  1.2432 +					pInfo->m_pTransmitAlways->Set( iEdict );
  1.2433 +				}
  1.2434 +#endif	
  1.2435 +				CServerNetworkProperty *pEnt = static_cast<CServerNetworkProperty*>( pEdict->GetNetworkable() );
  1.2436 +				if ( !pEnt )
  1.2437 +					break;
  1.2438 +
  1.2439 +				CServerNetworkProperty *pParent = pEnt->GetNetworkParent();
  1.2440 +				if ( !pParent )
  1.2441 +					break;
  1.2442 +
  1.2443 +				pEdict = pParent->edict();
  1.2444 +				iEdict = pParent->entindex();
  1.2445 +			}
  1.2446 +			continue;
  1.2447 +		}
  1.2448 +
  1.2449 +		// FIXME: Would like to remove all dependencies
  1.2450 +		CBaseEntity *pEnt = ( CBaseEntity * )pEdict->GetUnknown();
  1.2451 +		Assert( dynamic_cast< CBaseEntity* >( pEdict->GetUnknown() ) == pEnt );
  1.2452 +
  1.2453 +			if ( nFlags == FL_EDICT_FULLCHECK )
  1.2454 +		{
  1.2455 +			// do a full ShouldTransmit() check, may return FL_EDICT_CHECKPVS
  1.2456 +			nFlags = pEnt->ShouldTransmit( pInfo );
  1.2457 +
  1.2458 +			Assert( !(nFlags & FL_EDICT_FULLCHECK) );
  1.2459 +
  1.2460 +			if ( nFlags & FL_EDICT_ALWAYS )
  1.2461 +			{
  1.2462 +				pEnt->SetTransmit( pInfo, true );
  1.2463 +				continue;
  1.2464 +			}	
  1.2465 +		}
  1.2466 +
  1.2467 +		// don't send this entity
  1.2468 +		if ( !( nFlags & FL_EDICT_PVSCHECK ) )
  1.2469 +			continue;
  1.2470 +
  1.2471 +		CServerNetworkProperty *netProp = static_cast<CServerNetworkProperty*>( pEdict->GetNetworkable() );
  1.2472 +
  1.2473 +#ifndef _X360
  1.2474 +		if ( bIsHLTV || bIsReplay )
  1.2475 +		{
  1.2476 +			// for the HLTV/Replay we don't cull against PVS
  1.2477 +			if ( netProp->AreaNum() == skyBoxArea )
  1.2478 +			{
  1.2479 +				pEnt->SetTransmit( pInfo, true );
  1.2480 +			}
  1.2481 +			else
  1.2482 +			{
  1.2483 +				pEnt->SetTransmit( pInfo, false );
  1.2484 +			}
  1.2485 +			continue;
  1.2486 +		}
  1.2487 +#endif
  1.2488 +
  1.2489 +		// Always send entities in the player's 3d skybox.
  1.2490 +		// Sidenote: call of AreaNum() ensures that PVS data is up to date for this entity
  1.2491 +		bool bSameAreaAsSky = netProp->AreaNum() == skyBoxArea;
  1.2492 +		if ( bSameAreaAsSky )
  1.2493 +		{
  1.2494 +			pEnt->SetTransmit( pInfo, true );
  1.2495 +			continue;
  1.2496 +		}
  1.2497 +
  1.2498 +		bool bInPVS = netProp->IsInPVS( pInfo );
  1.2499 +		if ( bInPVS || sv_force_transmit_ents.GetBool() )
  1.2500 +		{
  1.2501 +			// only send if entity is in PVS
  1.2502 +			pEnt->SetTransmit( pInfo, false );
  1.2503 +			continue;
  1.2504 +		}
  1.2505 +
  1.2506 +		// If the entity is marked "check PVS" but it's in hierarchy, walk up the hierarchy looking for the
  1.2507 +		//  for any parent which is also in the PVS.  If none are found, then we don't need to worry about sending ourself
  1.2508 +		CBaseEntity *orig = pEnt;
  1.2509 +		CServerNetworkProperty *check = netProp->GetNetworkParent();
  1.2510 +
  1.2511 +		// BUG BUG:  I think it might be better to build up a list of edict indices which "depend" on other answers and then
  1.2512 +		// resolve them in a second pass.  Not sure what happens if an entity has two parents who both request PVS check?
  1.2513 +        while ( check )
  1.2514 +		{
  1.2515 +			int checkIndex = check->entindex();
  1.2516 +
  1.2517 +			// Parent already being sent
  1.2518 +			if ( pInfo->m_pTransmitEdict->Get( checkIndex ) )
  1.2519 +			{
  1.2520 +				orig->SetTransmit( pInfo, true );
  1.2521 +				break;
  1.2522 +			}
  1.2523 +
  1.2524 +			edict_t *checkEdict = check->edict();
  1.2525 +			int checkFlags = checkEdict->m_fStateFlags & (FL_EDICT_DONTSEND|FL_EDICT_ALWAYS|FL_EDICT_PVSCHECK|FL_EDICT_FULLCHECK);
  1.2526 +			if ( checkFlags & FL_EDICT_DONTSEND )
  1.2527 +				break;
  1.2528 +
  1.2529 +			if ( checkFlags & FL_EDICT_ALWAYS )
  1.2530 +			{
  1.2531 +				orig->SetTransmit( pInfo, true );
  1.2532 +				break;
  1.2533 +			}
  1.2534 +
  1.2535 +			if ( checkFlags == FL_EDICT_FULLCHECK )
  1.2536 +			{
  1.2537 +				// do a full ShouldTransmit() check, may return FL_EDICT_CHECKPVS
  1.2538 +				CBaseEntity *pCheckEntity = check->GetBaseEntity();
  1.2539 +				nFlags = pCheckEntity->ShouldTransmit( pInfo );
  1.2540 +				Assert( !(nFlags & FL_EDICT_FULLCHECK) );
  1.2541 +				if ( nFlags & FL_EDICT_ALWAYS )
  1.2542 +				{
  1.2543 +					pCheckEntity->SetTransmit( pInfo, true );
  1.2544 +					orig->SetTransmit( pInfo, true );
  1.2545 +				}
  1.2546 +				break;
  1.2547 +			}
  1.2548 +
  1.2549 +			if ( checkFlags & FL_EDICT_PVSCHECK )
  1.2550 +			{
  1.2551 +				// Check pvs
  1.2552 +				check->RecomputePVSInformation();
  1.2553 +				bool bMoveParentInPVS = check->IsInPVS( pInfo );
  1.2554 +				if ( bMoveParentInPVS )
  1.2555 +				{
  1.2556 +					orig->SetTransmit( pInfo, true );
  1.2557 +					break;
  1.2558 +				}
  1.2559 +			}
  1.2560 +
  1.2561 +			// Continue up chain just in case the parent itself has a parent that's in the PVS...
  1.2562 +			check = check->GetNetworkParent();
  1.2563 +		}
  1.2564 +	}
  1.2565 +
  1.2566 +//	Msg("A:%i, N:%i, F: %i, P: %i\n", always, dontSend, fullCheck, PVS );
  1.2567 +}
  1.2568 +
  1.2569 +//-----------------------------------------------------------------------------
  1.2570 +// Purpose: called before a full update, so the server can flush any custom PVS info, etc
  1.2571 +//-----------------------------------------------------------------------------
  1.2572 +void CServerGameEnts::PrepareForFullUpdate( edict_t *pEdict )
  1.2573 +{
  1.2574 +	CBaseEntity *pEntity = CBaseEntity::Instance( pEdict );
  1.2575 +
  1.2576 +	Assert( pEntity && pEntity->IsPlayer() );
  1.2577 +
  1.2578 +	if ( !pEntity )
  1.2579 +		return;
  1.2580 +
  1.2581 +	CBasePlayer *pPlayer = static_cast<CBasePlayer*>( pEntity );
  1.2582 +	pPlayer->PrepareForFullUpdate();
  1.2583 +}
  1.2584 +
  1.2585 +
  1.2586 +CServerGameClients g_ServerGameClients;
  1.2587 +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerGameClients, IServerGameClients, INTERFACEVERSION_SERVERGAMECLIENTS, g_ServerGameClients );
  1.2588 +
  1.2589 +
  1.2590 +//-----------------------------------------------------------------------------
  1.2591 +// Purpose: called when a player tries to connect to the server
  1.2592 +// Input  : *pEdict - the new player
  1.2593 +//			char *pszName - the players name
  1.2594 +//			char *pszAddress - the IP address of the player
  1.2595 +//			reject - output - fill in with the reason why
  1.2596 +//			maxrejectlen -- sizeof output buffer
  1.2597 +//			the player was not allowed to connect.
  1.2598 +// Output : Returns TRUE if player is allowed to join, FALSE if connection is denied.
  1.2599 +//-----------------------------------------------------------------------------
  1.2600 +bool CServerGameClients::ClientConnect( edict_t *pEdict, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen )
  1.2601 +{	
  1.2602 +	return g_pGameRules->ClientConnected( pEdict, pszName, pszAddress, reject, maxrejectlen );
  1.2603 +}
  1.2604 +
  1.2605 +//-----------------------------------------------------------------------------
  1.2606 +// Purpose: Called when a player is fully active (i.e. ready to receive messages)
  1.2607 +// Input  : *pEntity - the player
  1.2608 +//-----------------------------------------------------------------------------
  1.2609 +void CServerGameClients::ClientActive( edict_t *pEdict, bool bLoadGame )
  1.2610 +{
  1.2611 +	MDLCACHE_CRITICAL_SECTION();
  1.2612 +	
  1.2613 +	::ClientActive( pEdict, bLoadGame );
  1.2614 +
  1.2615 +	// If we just loaded from a save file, call OnRestore on valid entities
  1.2616 +	EndRestoreEntities();
  1.2617 +
  1.2618 +	if ( gpGlobals->eLoadType != MapLoad_LoadGame )
  1.2619 +	{
  1.2620 +		// notify all entities that the player is now in the game
  1.2621 +		for ( CBaseEntity *pEntity = gEntList.FirstEnt(); pEntity != NULL; pEntity = gEntList.NextEnt(pEntity) )
  1.2622 +		{
  1.2623 +			pEntity->PostClientActive();
  1.2624 +		}
  1.2625 +	}
  1.2626 +
  1.2627 +	// Tell the sound controller to check looping sounds
  1.2628 +	CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
  1.2629 +	CSoundEnvelopeController::GetController().CheckLoopingSoundsForPlayer( pPlayer );
  1.2630 +	SceneManager_ClientActive( pPlayer );
  1.2631 +}
  1.2632 +
  1.2633 +
  1.2634 +//-----------------------------------------------------------------------------
  1.2635 +// Purpose: Called when a player is fully connect ( initial baseline entities have been received )
  1.2636 +// Input  : *pEntity - the player
  1.2637 +//-----------------------------------------------------------------------------
  1.2638 +void CServerGameClients::ClientFullyConnect( edict_t *pEdict )
  1.2639 +{
  1.2640 +	::ClientFullyConnect( pEdict );
  1.2641 +}
  1.2642 +
  1.2643 +
  1.2644 +//-----------------------------------------------------------------------------
  1.2645 +// Purpose: called when a player disconnects from a server
  1.2646 +// Input  : *pEdict - the player
  1.2647 +//-----------------------------------------------------------------------------
  1.2648 +void CServerGameClients::ClientDisconnect( edict_t *pEdict )
  1.2649 +{
  1.2650 +	extern bool	g_fGameOver;
  1.2651 +
  1.2652 +	CBasePlayer *player = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
  1.2653 +	if ( player )
  1.2654 +	{
  1.2655 +		if ( !g_fGameOver )
  1.2656 +		{
  1.2657 +			player->SetMaxSpeed( 0.0f );
  1.2658 +
  1.2659 +			CSound *pSound;
  1.2660 +			pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt::ClientSoundIndex( pEdict ) );
  1.2661 +			{
  1.2662 +				// since this client isn't around to think anymore, reset their sound. 
  1.2663 +				if ( pSound )
  1.2664 +				{
  1.2665 +					pSound->Reset();
  1.2666 +				}
  1.2667 +			}
  1.2668 +
  1.2669 +		// since the edict doesn't get deleted, fix it so it doesn't interfere.
  1.2670 +			player->RemoveFlag( FL_AIMTARGET ); // don't attract autoaim
  1.2671 +			player->AddFlag( FL_DONTTOUCH );	// stop it touching anything
  1.2672 +			player->AddFlag( FL_NOTARGET );	// stop NPCs noticing it
  1.2673 +			player->AddSolidFlags( FSOLID_NOT_SOLID );		// nonsolid
  1.2674 +
  1.2675 +			if ( g_pGameRules )
  1.2676 +			{
  1.2677 +				g_pGameRules->ClientDisconnected( pEdict );
  1.2678 +				gamestats->Event_PlayerDisconnected( player );
  1.2679 +			}
  1.2680 +		}
  1.2681 +
  1.2682 +		// Make sure all Untouch()'s are called for this client leaving
  1.2683 +		CBaseEntity::PhysicsRemoveTouchedList( player );
  1.2684 +		CBaseEntity::PhysicsRemoveGroundList( player );
  1.2685 +
  1.2686 +#if !defined( NO_ENTITY_PREDICTION )
  1.2687 +		// Make sure anything we "own" is simulated by the server from now on
  1.2688 +		player->ClearPlayerSimulationList();
  1.2689 +#endif
  1.2690 +	}
  1.2691 +}
  1.2692 +
  1.2693 +void CServerGameClients::ClientPutInServer( edict_t *pEntity, const char *playername )
  1.2694 +{
  1.2695 +	if ( g_pClientPutInServerOverride )
  1.2696 +		g_pClientPutInServerOverride( pEntity, playername );
  1.2697 +	else
  1.2698 +		::ClientPutInServer( pEntity, playername );
  1.2699 +
  1.2700 +	CBasePlayer *pPlayer = ToBasePlayer( GetContainingEntity( pEntity ) );
  1.2701 +	if ( pPlayer )
  1.2702 +	{
  1.2703 +		bool bIsSplitScreenPlayer = engine->IsSplitScreenPlayer( pPlayer->entindex() );
  1.2704 +		CBasePlayer *pAttachedTo = NULL;
  1.2705 +		if ( bIsSplitScreenPlayer )
  1.2706 +		{
  1.2707 +			pAttachedTo = (CBasePlayer *)::GetContainingEntity( engine->GetSplitScreenPlayerAttachToEdict( pPlayer->entindex() ) );
  1.2708 +		}
  1.2709 +
  1.2710 +		pPlayer->SetSplitScreenPlayer( bIsSplitScreenPlayer, pAttachedTo );
  1.2711 +	}
  1.2712 +}
  1.2713 +
  1.2714 +void CServerGameClients::ClientCommand( edict_t *pEntity, const CCommand &args )
  1.2715 +{
  1.2716 +	CBasePlayer *pPlayer = ToBasePlayer( GetContainingEntity( pEntity ) );
  1.2717 +	::ClientCommand( pPlayer, args );
  1.2718 +}
  1.2719 +
  1.2720 +//-----------------------------------------------------------------------------
  1.2721 +// Purpose: called after the player changes userinfo - gives dll a chance to modify 
  1.2722 +//			it before it gets sent into the rest of the engine->
  1.2723 +// Input  : *pEdict - the player
  1.2724 +//			*infobuffer - their infobuffer
  1.2725 +//-----------------------------------------------------------------------------
  1.2726 +void CServerGameClients::ClientSettingsChanged( edict_t *pEdict )
  1.2727 +{
  1.2728 +	// Is the client spawned yet?
  1.2729 +	if ( !pEdict->GetUnknown() )
  1.2730 +		return;
  1.2731 +
  1.2732 +	CBasePlayer *player = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
  1.2733 +	
  1.2734 +	if ( !player )
  1.2735 +		return;
  1.2736 +
  1.2737 +#define QUICKGETCVARVALUE(v) (engine->GetClientConVarValue( player->entindex(), v ))
  1.2738 +
  1.2739 +	// get network setting for prediction & lag compensation
  1.2740 +	
  1.2741 +	// Unfortunately, we have to duplicate the code in cdll_bounded_cvars.cpp here because the client
  1.2742 +	// doesn't send the virtualized value up (because it has no way to know when the virtualized value
  1.2743 +	// changes). Possible todo: put the responsibility on the bounded cvar to notify the engine when
  1.2744 +	// its virtualized value has changed.		
  1.2745 +	
  1.2746 +	player->m_nUpdateRate = Q_atoi( QUICKGETCVARVALUE("cl_updaterate") );
  1.2747 +	static const ConVar *pMinUpdateRate = g_pCVar->FindVar( "sv_minupdaterate" );
  1.2748 +	static const ConVar *pMaxUpdateRate = g_pCVar->FindVar( "sv_maxupdaterate" );
  1.2749 +	if ( pMinUpdateRate && pMaxUpdateRate )
  1.2750 +		player->m_nUpdateRate = (int)clamp( player->m_nUpdateRate, pMinUpdateRate->GetFloat(), pMaxUpdateRate->GetFloat() );
  1.2751 +
  1.2752 +	bool useInterpolation = Q_atoi( QUICKGETCVARVALUE("cl_interpolate") ) != 0;
  1.2753 +	if ( useInterpolation )
  1.2754 +	{
  1.2755 +		float flLerpRatio = Q_atof( QUICKGETCVARVALUE("cl_interp_ratio") );
  1.2756 +		if ( flLerpRatio == 0 )
  1.2757 +			flLerpRatio = 1.0f;
  1.2758 +		float flLerpAmount = Q_atof( QUICKGETCVARVALUE("cl_interp") );
  1.2759 +
  1.2760 +		static const ConVar *pMin = g_pCVar->FindVar( "sv_client_min_interp_ratio" );
  1.2761 +		static const ConVar *pMax = g_pCVar->FindVar( "sv_client_max_interp_ratio" );
  1.2762 +		if ( pMin && pMax && pMin->GetFloat() != -1 )
  1.2763 +		{
  1.2764 +			flLerpRatio = clamp( flLerpRatio, pMin->GetFloat(), pMax->GetFloat() );
  1.2765 +		}
  1.2766 +		else
  1.2767 +		{
  1.2768 +			if ( flLerpRatio == 0 )
  1.2769 +				flLerpRatio = 1.0f;
  1.2770 +		}
  1.2771 +		// #define FIXME_INTERP_RATIO
  1.2772 +		player->m_fLerpTime = MAX( flLerpAmount, flLerpRatio / player->m_nUpdateRate );
  1.2773 +	}
  1.2774 +	else
  1.2775 +	{
  1.2776 +		player->m_fLerpTime = 0.0f;
  1.2777 +	}
  1.2778 +	
  1.2779 +#if !defined( NO_ENTITY_PREDICTION )
  1.2780 +	bool usePrediction = Q_atoi( QUICKGETCVARVALUE("cl_predict")) != 0;
  1.2781 +
  1.2782 +	if ( usePrediction )
  1.2783 +	{
  1.2784 +		player->m_bPredictWeapons  = Q_atoi( QUICKGETCVARVALUE("cl_predictweapons")) != 0;
  1.2785 +		player->m_bLagCompensation = Q_atoi( QUICKGETCVARVALUE("cl_lagcompensation")) != 0;
  1.2786 +	}
  1.2787 +	else
  1.2788 +#endif
  1.2789 +	{
  1.2790 +		player->m_bPredictWeapons  = false;
  1.2791 +		player->m_bLagCompensation = false;
  1.2792 +	}
  1.2793 +	
  1.2794 +
  1.2795 +#undef QUICKGETCVARVALUE
  1.2796 +
  1.2797 +	g_pGameRules->ClientSettingsChanged( player );
  1.2798 +}
  1.2799 +
  1.2800 +
  1.2801 +
  1.2802 +
  1.2803 +//-----------------------------------------------------------------------------
  1.2804 +// Purpose: A client can have a separate "view entity" indicating that his/her view should depend on the origin of that
  1.2805 +//  view entity.  If that's the case, then pViewEntity will be non-NULL and will be used.  Otherwise, the current
  1.2806 +//  entity's origin is used.  Either is offset by the m_vecViewOffset to get the eye position.
  1.2807 +// 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
  1.2808 +//  override the actual PAS or PVS values, or use a different origin.
  1.2809 +// 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
  1.2810 +// Input  : *pViewEntity - 
  1.2811 +//			*pClient - 
  1.2812 +//			**pvs - 
  1.2813 +//			**pas - 
  1.2814 +//-----------------------------------------------------------------------------
  1.2815 +void CServerGameClients::ClientSetupVisibility( edict_t *pViewEntity, edict_t *pClient, unsigned char *pvs, int pvssize )
  1.2816 +{
  1.2817 +	Vector org;
  1.2818 +
  1.2819 +	// Reset the PVS!!!
  1.2820 +	engine->ResetPVS( pvs, pvssize );
  1.2821 +
  1.2822 +	g_pToolFrameworkServer->PreSetupVisibility();
  1.2823 +
  1.2824 +	// Find the client's PVS
  1.2825 +	CBaseEntity *pVE = NULL;
  1.2826 +	if ( pViewEntity )
  1.2827 +	{
  1.2828 +		pVE = GetContainingEntity( pViewEntity );
  1.2829 +		// If we have a viewentity, it overrides the player's origin
  1.2830 +		if ( pVE )
  1.2831 +		{
  1.2832 +			org = pVE->EyePosition();
  1.2833 +			engine->AddOriginToPVS( org );
  1.2834 +		}
  1.2835 +	}
  1.2836 +
  1.2837 +	float fovDistanceAdjustFactor = 1;
  1.2838 +
  1.2839 +	CUtlVector< Vector > areaPortalOrigins;
  1.2840 +
  1.2841 +	CBasePlayer *pPlayer = ( CBasePlayer * )GetContainingEntity( pClient );
  1.2842 +	if ( pPlayer )
  1.2843 +	{
  1.2844 +		if ( !pVE )
  1.2845 +		{
  1.2846 +			org = pPlayer->EyePosition();
  1.2847 +		}
  1.2848 +		pPlayer->SetupVisibility( pVE, pvs, pvssize );
  1.2849 +		UTIL_SetClientVisibilityPVS( pClient, pvs, pvssize );
  1.2850 +		fovDistanceAdjustFactor = pPlayer->GetFOVDistanceAdjustFactorForNetworking();
  1.2851 +
  1.2852 +		areaPortalOrigins.AddToTail( org );
  1.2853 +
  1.2854 +		// Merge in areaportal "window" states from from split screen players by passing in the extra PVS origins!!!
  1.2855 +		for ( int i = 1; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  1.2856 +		{
  1.2857 +			CBasePlayer *pl = (CBasePlayer *)ToBasePlayer( GetContainingEntity( engine->GetSplitScreenPlayerForEdict( pPlayer->entindex(), i ) ) );
  1.2858 +			if ( !pl )
  1.2859 +				continue;
  1.2860 +			org = pl->EyePosition();
  1.2861 +			areaPortalOrigins.AddToTail( org );
  1.2862 +		}
  1.2863 +	}
  1.2864 +	else
  1.2865 +	{
  1.2866 +		Warning( "ClientSetupVisibility: No entity for edict!\n" );
  1.2867 +		areaPortalOrigins.AddToTail( org );
  1.2868 +	}
  1.2869 +
  1.2870 +	unsigned char portalBits[MAX_AREA_PORTAL_STATE_BYTES];
  1.2871 +	memset( portalBits, 0, sizeof( portalBits ) );
  1.2872 +	
  1.2873 +	int portalNums[512];
  1.2874 +	int isOpen[512];
  1.2875 +	int iOutPortal = 0;
  1.2876 +
  1.2877 +	for( unsigned short i = g_AreaPortals.Head(); i != g_AreaPortals.InvalidIndex(); i = g_AreaPortals.Next(i) )
  1.2878 +	{
  1.2879 +		CFuncAreaPortalBase *pCur = g_AreaPortals[i];
  1.2880 +
  1.2881 +		bool bIsOpenOnClient = true;
  1.2882 +		
  1.2883 +		// Update our array of which portals are open and flush it if necessary.		
  1.2884 +		portalNums[iOutPortal] = pCur->m_portalNumber;
  1.2885 +		isOpen[iOutPortal] = pCur->UpdateVisibility( areaPortalOrigins, fovDistanceAdjustFactor, bIsOpenOnClient );
  1.2886 +
  1.2887 +
  1.2888 +
  1.2889 +		++iOutPortal;
  1.2890 +		if ( iOutPortal >= ARRAYSIZE( portalNums ) )
  1.2891 +		{
  1.2892 +			engine->SetAreaPortalStates( portalNums, isOpen, iOutPortal );
  1.2893 +			iOutPortal = 0;
  1.2894 +		}
  1.2895 +
  1.2896 +		// Version 0 portals (ie: shipping Half-Life 2 era) are always treated as open
  1.2897 +		// for purposes of the m_chAreaPortalBits array on the client.
  1.2898 +		if ( pCur->m_iPortalVersion == 0 )
  1.2899 +			bIsOpenOnClient = true;
  1.2900 +
  1.2901 +		if ( bIsOpenOnClient )
  1.2902 +		{
  1.2903 +			if ( pCur->m_portalNumber < 0 )
  1.2904 +				continue;
  1.2905 +			else if ( pCur->m_portalNumber >= sizeof( portalBits ) * 8 )
  1.2906 +				Error( "ClientSetupVisibility: portal number (%d) too large", pCur->m_portalNumber );
  1.2907 +			else
  1.2908 +				portalBits[pCur->m_portalNumber >> 3] |= (1 << (pCur->m_portalNumber & 7));
  1.2909 +		}	
  1.2910 +	}
  1.2911 +
  1.2912 +	// Flush the remaining areaportal states.
  1.2913 +	engine->SetAreaPortalStates( portalNums, isOpen, iOutPortal );
  1.2914 +
  1.2915 +	// Update the area bits that get sent to the client.
  1.2916 +	Assert( pPlayer );
  1.2917 +	if ( pPlayer )
  1.2918 +	{
  1.2919 +		pPlayer->m_Local.UpdateAreaBits( pPlayer, portalBits );
  1.2920 +	}
  1.2921 +
  1.2922 +
  1.2923 +}
  1.2924 +
  1.2925 +
  1.2926 +
  1.2927 +
  1.2928 +//-----------------------------------------------------------------------------
  1.2929 +// Purpose: 
  1.2930 +// Input  : *player - 
  1.2931 +//			*buf - 
  1.2932 +//			numcmds - 
  1.2933 +//			totalcmds - 
  1.2934 +//			dropped_packets - 
  1.2935 +//			ignore - 
  1.2936 +//			paused - 
  1.2937 +// Output : float
  1.2938 +//-----------------------------------------------------------------------------
  1.2939 +#define CMD_MAXBACKUP 64
  1.2940 +
  1.2941 +float CServerGameClients::ProcessUsercmds( edict_t *player, bf_read *buf, int numcmds, int totalcmds,
  1.2942 +	int dropped_packets, bool ignore, bool paused )
  1.2943 +{
  1.2944 +	int				i;
  1.2945 +	CUserCmd		*from, *to;
  1.2946 +
  1.2947 +	// We track last three command in case we drop some 
  1.2948 +	//  packets but get them back.
  1.2949 +	CUserCmd cmds[ CMD_MAXBACKUP ];  
  1.2950 +
  1.2951 +	CUserCmd		cmdNull;  // For delta compression
  1.2952 +	
  1.2953 +	Assert( numcmds >= 0 );
  1.2954 +	Assert( ( totalcmds - numcmds ) >= 0 );
  1.2955 +
  1.2956 +	CBasePlayer *pPlayer = NULL;
  1.2957 +	CBaseEntity *pEnt = CBaseEntity::Instance(player);
  1.2958 +	if ( pEnt && pEnt->IsPlayer() )
  1.2959 +	{
  1.2960 +		pPlayer = static_cast< CBasePlayer * >( pEnt );
  1.2961 +	}
  1.2962 +	// Too many commands?
  1.2963 +	if ( totalcmds < 0 || totalcmds >= ( CMD_MAXBACKUP - 1 ) )
  1.2964 +	{
  1.2965 +		const char *name = "unknown";
  1.2966 +		if ( pPlayer )
  1.2967 +		{
  1.2968 +			name = pPlayer->GetPlayerName();
  1.2969 +		}
  1.2970 +
  1.2971 +		Msg("CBasePlayer::ProcessUsercmds: too many cmds %i sent for player %s\n", totalcmds, name );
  1.2972 +		// FIXME:  Need a way to drop the client from here
  1.2973 +		//SV_DropClient ( host_client, false, "CMD_MAXBACKUP hit" );
  1.2974 +		buf->SetOverflowFlag();
  1.2975 +		return 0.0f;
  1.2976 +	}
  1.2977 +
  1.2978 +	// Initialize for reading delta compressed usercmds
  1.2979 +	cmdNull.Reset();
  1.2980 +	from = &cmdNull;
  1.2981 +	for ( i = totalcmds - 1; i >= 0; i-- )
  1.2982 +	{
  1.2983 +		to = &cmds[ i ];
  1.2984 +		ReadUsercmd( buf, to, from );
  1.2985 +		from = to;
  1.2986 +	}
  1.2987 +
  1.2988 +	// Client not fully connected or server has gone inactive  or is paused, just ignore
  1.2989 +	if ( ignore || !pPlayer )
  1.2990 +	{
  1.2991 +		return 0.0f;
  1.2992 +	}
  1.2993 +
  1.2994 +	MDLCACHE_CRITICAL_SECTION();
  1.2995 +	pPlayer->ProcessUsercmds( cmds, numcmds, totalcmds, dropped_packets, paused );
  1.2996 +
  1.2997 +	return TICK_INTERVAL;
  1.2998 +}
  1.2999 +
  1.3000 +
  1.3001 +void CServerGameClients::PostClientMessagesSent( void )
  1.3002 +{
  1.3003 +	VPROF("CServerGameClients::PostClient");
  1.3004 +	gEntList.PostClientMessagesSent();
  1.3005 +}
  1.3006 +
  1.3007 +// Sets the client index for the client who typed the command into his/her console
  1.3008 +void CServerGameClients::SetCommandClient( int index )
  1.3009 +{
  1.3010 +	g_nCommandClientIndex = index;
  1.3011 +}
  1.3012 +
  1.3013 +int	CServerGameClients::GetReplayDelay( edict_t *pEdict, int &entity )
  1.3014 +{
  1.3015 +	CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
  1.3016 +
  1.3017 +	if ( !pPlayer )
  1.3018 +		return 0;
  1.3019 +
  1.3020 +	entity = pPlayer->GetReplayEntity();
  1.3021 +
  1.3022 +	return pPlayer->GetDelayTicks();
  1.3023 +}
  1.3024 +
  1.3025 +
  1.3026 +//-----------------------------------------------------------------------------
  1.3027 +// The client's userinfo data lump has changed
  1.3028 +//-----------------------------------------------------------------------------
  1.3029 +void CServerGameClients::ClientEarPosition( edict_t *pEdict, Vector *pEarOrigin )
  1.3030 +{
  1.3031 +	CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
  1.3032 +	if (pPlayer)
  1.3033 +	{
  1.3034 +		*pEarOrigin = pPlayer->EarPosition();
  1.3035 +	}
  1.3036 +	else
  1.3037 +	{
  1.3038 +		// Shouldn't happen
  1.3039 +		Assert(0);
  1.3040 +		*pEarOrigin = vec3_origin;
  1.3041 +	}
  1.3042 +}
  1.3043 +
  1.3044 +//-----------------------------------------------------------------------------
  1.3045 +// Purpose: 
  1.3046 +// Input  : *player - 
  1.3047 +// Output : CPlayerState
  1.3048 +//-----------------------------------------------------------------------------
  1.3049 +CPlayerState *CServerGameClients::GetPlayerState( edict_t *player )
  1.3050 +{
  1.3051 +	// Is the client spawned yet?
  1.3052 +	if ( !player || !player->GetUnknown() )
  1.3053 +		return NULL;
  1.3054 +
  1.3055 +	CBasePlayer *pBasePlayer = ( CBasePlayer * )CBaseEntity::Instance( player );
  1.3056 +	if ( !pBasePlayer )
  1.3057 +		return NULL;
  1.3058 +
  1.3059 +	return &pBasePlayer->pl;
  1.3060 +}
  1.3061 +
  1.3062 +//-----------------------------------------------------------------------------
  1.3063 +// Purpose: Anything this game .dll wants to add to the bug reporter text (e.g., the entity/model under the picker crosshair)
  1.3064 +//  can be added here
  1.3065 +// Input  : *buf - 
  1.3066 +//			buflen - 
  1.3067 +//-----------------------------------------------------------------------------
  1.3068 +void CServerGameClients::GetBugReportInfo( char *buf, int buflen )
  1.3069 +{
  1.3070 +	recentNPCSpeech_t speech[ SPEECH_LIST_MAX_SOUNDS ];
  1.3071 +	int  num;
  1.3072 +	int  i;
  1.3073 +
  1.3074 +	buf[ 0 ] = 0;
  1.3075 +
  1.3076 +	if ( gpGlobals->maxClients == 1 )
  1.3077 +	{
  1.3078 +		CBaseEntity *ent = UTIL_PlayerByIndex(1) ? UTIL_PlayerByIndex(1)->FindPickerEntity() : NULL;
  1.3079 +		if ( ent )
  1.3080 +		{
  1.3081 +			Q_snprintf( buf, buflen, "Picker %i/%s - ent %s model %s\n",
  1.3082 +				ent->entindex(),
  1.3083 +				ent->GetClassname(),
  1.3084 +				STRING( ent->GetEntityName() ),
  1.3085 +				STRING(ent->GetModelName()) );
  1.3086 +		}
  1.3087 +
  1.3088 +		// get any sounds that were spoken by NPCs recently
  1.3089 +		num = GetRecentNPCSpeech( speech );
  1.3090 +		if ( num > 0 )
  1.3091 +		{
  1.3092 +			Q_snprintf( buf, buflen, "%sRecent NPC speech:\n", buf );
  1.3093 +			for( i = 0; i < num; i++ )
  1.3094 +			{
  1.3095 +				Q_snprintf( buf, buflen, "%s   time: %6.3f   sound name: %s   scene: %s\n", buf, speech[ i ].time, speech[ i ].name, speech[ i ].sceneName );
  1.3096 +			}
  1.3097 +			Q_snprintf( buf, buflen, "%sCurrent time: %6.3f\n", buf, gpGlobals->curtime );
  1.3098 +		}
  1.3099 +	}
  1.3100 +}
  1.3101 +
  1.3102 +//-----------------------------------------------------------------------------
  1.3103 +// Purpose: A player sent a voice packet
  1.3104 +//-----------------------------------------------------------------------------
  1.3105 +void CServerGameClients::ClientVoice( edict_t *pEdict )
  1.3106 +{
  1.3107 +	CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict );
  1.3108 +	if (pPlayer)
  1.3109 +	{
  1.3110 +		pPlayer->OnVoiceTransmit();
  1.3111 +		
  1.3112 +		// Notify the voice listener that we've spoken
  1.3113 +		PlayerVoiceListener().AddPlayerSpeakTime( pPlayer );
  1.3114 +	}
  1.3115 +}
  1.3116 +
  1.3117 +//-----------------------------------------------------------------------------
  1.3118 +// Purpose: A user has had their network id setup and validated 
  1.3119 +//-----------------------------------------------------------------------------
  1.3120 +void CServerGameClients::NetworkIDValidated( const char *pszUserName, const char *pszNetworkID )
  1.3121 +{
  1.3122 +}
  1.3123 +
  1.3124 +int CServerGameClients::GetMaxSplitscreenPlayers()
  1.3125 +{
  1.3126 +	return MAX_SPLITSCREEN_PLAYERS;
  1.3127 +}
  1.3128 +
  1.3129 +int CServerGameClients::GetMaxHumanPlayers()
  1.3130 +{
  1.3131 +	if ( g_pGameRules )
  1.3132 +	{
  1.3133 +		return g_pGameRules->GetMaxHumanPlayers();
  1.3134 +	}
  1.3135 +	return -1;
  1.3136 +}
  1.3137 +
  1.3138 +// The client has submitted a keyvalues command
  1.3139 +void CServerGameClients::ClientCommandKeyValues( edict_t *pEntity, KeyValues *pKeyValues )
  1.3140 +{
  1.3141 +	if ( !pKeyValues )
  1.3142 +		return;
  1.3143 +
  1.3144 +	char const *szCommand = pKeyValues->GetName();
  1.3145 +
  1.3146 +	if ( FStrEq( szCommand, "avatarinfo" ) )
  1.3147 +	{
  1.3148 +		// Player is communicating team and avatar setting
  1.3149 +		//TheDirector->PlayerAvatarSet( pEntity, pKeyValues );
  1.3150 +	}
  1.3151 +
  1.3152 +	g_pGameRules->ClientCommandKeyValues( pEntity, pKeyValues );
  1.3153 +}
  1.3154 +
  1.3155 +//-----------------------------------------------------------------------------
  1.3156 +// Purpose: 
  1.3157 +//-----------------------------------------------------------------------------
  1.3158 +static bf_write *g_pMsgBuffer = NULL;
  1.3159 +
  1.3160 +void EntityMessageBegin( CBaseEntity * entity, bool reliable /*= false*/ ) 
  1.3161 +{
  1.3162 +	Assert( !g_pMsgBuffer );
  1.3163 +
  1.3164 +	Assert ( entity );
  1.3165 +
  1.3166 +	g_pMsgBuffer = engine->EntityMessageBegin( entity->entindex(), entity->GetServerClass(), reliable );
  1.3167 +}
  1.3168 +
  1.3169 +void UserMessageBegin( IRecipientFilter& filter, const char *messagename )
  1.3170 +{
  1.3171 +	Assert( !g_pMsgBuffer );
  1.3172 +
  1.3173 +	Assert( messagename );
  1.3174 +
  1.3175 +	int msg_type = usermessages->LookupUserMessage( messagename );
  1.3176 +	
  1.3177 +	if ( msg_type == -1 )
  1.3178 +	{
  1.3179 +		Error( "UserMessageBegin:  Unregistered message '%s'\n", messagename );
  1.3180 +	}
  1.3181 +
  1.3182 +	g_pMsgBuffer = engine->UserMessageBegin( &filter, msg_type, messagename );
  1.3183 +}
  1.3184 +
  1.3185 +void MessageEnd( void )
  1.3186 +{
  1.3187 +	Assert( g_pMsgBuffer );
  1.3188 +
  1.3189 +	engine->MessageEnd();
  1.3190 +
  1.3191 +	g_pMsgBuffer = NULL;
  1.3192 +}
  1.3193 +
  1.3194 +void MessageWriteByte( int iValue)
  1.3195 +{
  1.3196 +	if (!g_pMsgBuffer)
  1.3197 +		Error( "WRITE_BYTE called with no active message\n" );
  1.3198 +
  1.3199 +	g_pMsgBuffer->WriteByte( iValue );
  1.3200 +}
  1.3201 +
  1.3202 +void MessageWriteChar( int iValue)
  1.3203 +{
  1.3204 +	if (!g_pMsgBuffer)
  1.3205 +		Error( "WRITE_CHAR called with no active message\n" );
  1.3206 +
  1.3207 +	g_pMsgBuffer->WriteChar( iValue );
  1.3208 +}
  1.3209 +
  1.3210 +void MessageWriteShort( int iValue)
  1.3211 +{
  1.3212 +	if (!g_pMsgBuffer)
  1.3213 +		Error( "WRITE_SHORT called with no active message\n" );
  1.3214 +
  1.3215 +	g_pMsgBuffer->WriteShort( iValue );
  1.3216 +}
  1.3217 +
  1.3218 +void MessageWriteWord( int iValue )
  1.3219 +{
  1.3220 +	if (!g_pMsgBuffer)
  1.3221 +		Error( "WRITE_WORD called with no active message\n" );
  1.3222 +
  1.3223 +	g_pMsgBuffer->WriteWord( iValue );
  1.3224 +}
  1.3225 +
  1.3226 +void MessageWriteLong( int iValue)
  1.3227 +{
  1.3228 +	if (!g_pMsgBuffer)
  1.3229 +		Error( "WriteLong called with no active message\n" );
  1.3230 +
  1.3231 +	g_pMsgBuffer->WriteLong( iValue );
  1.3232 +}
  1.3233 +
  1.3234 +void MessageWriteFloat( float flValue)
  1.3235 +{
  1.3236 +	if (!g_pMsgBuffer)
  1.3237 +		Error( "WriteFloat called with no active message\n" );
  1.3238 +
  1.3239 +	g_pMsgBuffer->WriteFloat( flValue );
  1.3240 +}
  1.3241 +
  1.3242 +void MessageWriteAngle( float flValue)
  1.3243 +{
  1.3244 +	if (!g_pMsgBuffer)
  1.3245 +		Error( "WriteAngle called with no active message\n" );
  1.3246 +
  1.3247 +	g_pMsgBuffer->WriteBitAngle( flValue, 8 );
  1.3248 +}
  1.3249 +
  1.3250 +void MessageWriteCoord( float flValue)
  1.3251 +{
  1.3252 +	if (!g_pMsgBuffer)
  1.3253 +		Error( "WriteCoord called with no active message\n" );
  1.3254 +
  1.3255 +	g_pMsgBuffer->WriteBitCoord( flValue );
  1.3256 +}
  1.3257 +
  1.3258 +void MessageWriteVec3Coord( const Vector& rgflValue)
  1.3259 +{
  1.3260 +	if (!g_pMsgBuffer)
  1.3261 +		Error( "WriteVec3Coord called with no active message\n" );
  1.3262 +
  1.3263 +	g_pMsgBuffer->WriteBitVec3Coord( rgflValue );
  1.3264 +}
  1.3265 +
  1.3266 +void MessageWriteVec3Normal( const Vector& rgflValue)
  1.3267 +{
  1.3268 +	if (!g_pMsgBuffer)
  1.3269 +		Error( "WriteVec3Normal called with no active message\n" );
  1.3270 +
  1.3271 +	g_pMsgBuffer->WriteBitVec3Normal( rgflValue );
  1.3272 +}
  1.3273 +
  1.3274 +void MessageWriteBitVecIntegral( const Vector& vecValue )
  1.3275 +{
  1.3276 +	if (!g_pMsgBuffer)
  1.3277 +		Error( "MessageWriteBitVecIntegral called with no active message\n" );
  1.3278 +
  1.3279 +	for ( int i = 0; i < 3; ++i )
  1.3280 +	{
  1.3281 +		g_pMsgBuffer->WriteBitCoordMP( vecValue[ i ], kCW_Integral );
  1.3282 +	}
  1.3283 +}
  1.3284 +
  1.3285 +void MessageWriteAngles( const QAngle& rgflValue)
  1.3286 +{
  1.3287 +	if (!g_pMsgBuffer)
  1.3288 +		Error( "WriteVec3Normal called with no active message\n" );
  1.3289 +
  1.3290 +	g_pMsgBuffer->WriteBitAngles( rgflValue );
  1.3291 +}
  1.3292 +
  1.3293 +void MessageWriteString( const char *sz )
  1.3294 +{
  1.3295 +	if (!g_pMsgBuffer)
  1.3296 +		Error( "WriteString called with no active message\n" );
  1.3297 +
  1.3298 +	g_pMsgBuffer->WriteString( sz );
  1.3299 +}
  1.3300 +
  1.3301 +void MessageWriteEntity( int iValue)
  1.3302 +{
  1.3303 +	if (!g_pMsgBuffer)
  1.3304 +		Error( "WriteEntity called with no active message\n" );
  1.3305 +
  1.3306 +	g_pMsgBuffer->WriteShort( iValue );
  1.3307 +}
  1.3308 +
  1.3309 +void MessageWriteEHandle( CBaseEntity *pEntity )
  1.3310 +{
  1.3311 +	if (!g_pMsgBuffer)
  1.3312 +		Error( "WriteEHandle called with no active message\n" );
  1.3313 +
  1.3314 +	long iEncodedEHandle;
  1.3315 +	
  1.3316 +	if( pEntity )
  1.3317 +	{
  1.3318 +		EHANDLE hEnt = pEntity;
  1.3319 +
  1.3320 +		int iSerialNum = hEnt.GetSerialNumber() & (1 << NUM_NETWORKED_EHANDLE_SERIAL_NUMBER_BITS) - 1;
  1.3321 +		iEncodedEHandle = hEnt.GetEntryIndex() | (iSerialNum << MAX_EDICT_BITS);
  1.3322 +	}
  1.3323 +	else
  1.3324 +	{
  1.3325 +		iEncodedEHandle = INVALID_NETWORKED_EHANDLE_VALUE;
  1.3326 +	}
  1.3327 +	
  1.3328 +	g_pMsgBuffer->WriteLong( iEncodedEHandle );
  1.3329 +}
  1.3330 +
  1.3331 +// bitwise
  1.3332 +void MessageWriteBool( bool bValue )
  1.3333 +{
  1.3334 +	if (!g_pMsgBuffer)
  1.3335 +		Error( "WriteBool called with no active message\n" );
  1.3336 +
  1.3337 +	g_pMsgBuffer->WriteOneBit( bValue ? 1 : 0 );
  1.3338 +}
  1.3339 +
  1.3340 +void MessageWriteUBitLong( unsigned int data, int numbits )
  1.3341 +{
  1.3342 +	if (!g_pMsgBuffer)
  1.3343 +		Error( "WriteUBitLong called with no active message\n" );
  1.3344 +
  1.3345 +	g_pMsgBuffer->WriteUBitLong( data, numbits );
  1.3346 +}
  1.3347 +
  1.3348 +void MessageWriteSBitLong( int data, int numbits )
  1.3349 +{
  1.3350 +	if (!g_pMsgBuffer)
  1.3351 +		Error( "WriteSBitLong called with no active message\n" );
  1.3352 +
  1.3353 +	g_pMsgBuffer->WriteSBitLong( data, numbits );
  1.3354 +}
  1.3355 +
  1.3356 +void MessageWriteBits( const void *pIn, int nBits )
  1.3357 +{
  1.3358 +	if (!g_pMsgBuffer)
  1.3359 +		Error( "WriteBits called with no active message\n" );
  1.3360 +
  1.3361 +	g_pMsgBuffer->WriteBits( pIn, nBits );
  1.3362 +}
  1.3363 +
  1.3364 +class CServerDLLSharedAppSystems : public IServerDLLSharedAppSystems
  1.3365 +{
  1.3366 +public:
  1.3367 +	CServerDLLSharedAppSystems()
  1.3368 +	{
  1.3369 +		AddAppSystem( "soundemittersystem", SOUNDEMITTERSYSTEM_INTERFACE_VERSION );
  1.3370 +		AddAppSystem( "scenefilecache", SCENE_FILE_CACHE_INTERFACE_VERSION );
  1.3371 +#ifdef INFESTED_DLL
  1.3372 +		AddAppSystem( "missionchooser", ASW_MISSION_CHOOSER_VERSION );
  1.3373 +#endif
  1.3374 +	}
  1.3375 +
  1.3376 +	virtual int	Count()
  1.3377 +	{
  1.3378 +		return m_Systems.Count();
  1.3379 +	}
  1.3380 +	virtual char const *GetDllName( int idx )
  1.3381 +	{
  1.3382 +		return m_Systems[ idx ].m_pModuleName;
  1.3383 +	}
  1.3384 +	virtual char const *GetInterfaceName( int idx )
  1.3385 +	{
  1.3386 +		return m_Systems[ idx ].m_pInterfaceName;
  1.3387 +	}
  1.3388 +private:
  1.3389 +	void AddAppSystem( char const *moduleName, char const *interfaceName )
  1.3390 +	{
  1.3391 +		AppSystemInfo_t sys;
  1.3392 +		sys.m_pModuleName = moduleName;
  1.3393 +		sys.m_pInterfaceName = interfaceName;
  1.3394 +		m_Systems.AddToTail( sys );
  1.3395 +	}
  1.3396 +
  1.3397 +	CUtlVector< AppSystemInfo_t >	m_Systems;
  1.3398 +};
  1.3399 +
  1.3400 +EXPOSE_SINGLE_INTERFACE( CServerDLLSharedAppSystems, IServerDLLSharedAppSystems, SERVER_DLL_SHARED_APPSYSTEMS );
  1.3401 +
  1.3402 +
  1.3403 +//-----------------------------------------------------------------------------
  1.3404 +//
  1.3405 +//-----------------------------------------------------------------------------
  1.3406 +void CServerGameTags::GetTaggedConVarList( KeyValues *pCvarTagList )
  1.3407 +{
  1.3408 +	if ( pCvarTagList && g_pGameRules )
  1.3409 +	{
  1.3410 +		g_pGameRules->GetTaggedConVarList( pCvarTagList );
  1.3411 +	}
  1.3412 +}