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

672 lines
23 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "cdll_int.h"
  8. #include "gameinterface.h"
  9. #include "mapentities.h"
  10. #include "cs_gameinterface.h"
  11. #include "ai_responsesystem.h"
  12. #include "iachievementmgr.h"
  13. #include "fmtstr.h"
  14. #include "gametypes.h"
  15. #include "matchmaking/imatchframework.h"
  16. #include "cs_shareddefs.h"
  17. #include "cs_gamerules.h"
  18. #include "gametypes.h"
  19. #include "engine/inetsupport.h"
  20. #include "dedicated_server_ugc_manager.h"
  21. #include "cs_player.h"
  22. #include "server_log_http_dispatcher.h"
  23. #include "netmessages.h"
  24. #include "usermessages.h"
  25. // NOTE: This has to be the last file included!
  26. #include "tier0/memdbgon.h"
  27. //////////////////////////////////////////////////////////////////////////
  28. //
  29. // Convars
  30. //
  31. ConVar sv_workshop_allow_other_maps( "sv_workshop_allow_other_maps", "1", FCVAR_RELEASE, "When hosting a workshop collection, users can play other workshop map on this server when it is empty and then mapcycle into this server collection." );
  32. static ConVar tv_allow_camera_man_steamid( "tv_allow_camera_man_steamid", "", FCVAR_RELEASE, "Allows tournament production cameraman to run csgo.exe -interactivecaster on SteamID 7650123456XXX and be the camera man." );
  33. // #define SVGC_RESERVATION_DEBUG 1
  34. // -------------------------------------------------------------------------------------------- //
  35. // Mod-specific CServerGameClients implementation.
  36. // -------------------------------------------------------------------------------------------- //
  37. void CServerGameClients::GetPlayerLimits( int& minplayers, int& maxplayers, int &defaultMaxPlayers ) const
  38. {
  39. minplayers = 1; // allow single player for the test maps (but we default to multi)
  40. maxplayers = MAX_PLAYERS;
  41. defaultMaxPlayers = MAX_PLAYERS;
  42. }
  43. // -------------------------------------------------------------------------------------------- //
  44. // Mod-specific CServerGameDLL implementation.
  45. // -------------------------------------------------------------------------------------------- //
  46. void CServerGameDLL::LevelInit_ParseAllEntities( const char *pMapEntities )
  47. {
  48. if ( Q_strcmp( STRING(gpGlobals->mapname), "cs_" ) )
  49. {
  50. // don't precache AI responses (hostages) if it's not a hostage rescure map
  51. extern IResponseSystem *g_pResponseSystem;
  52. g_pResponseSystem->PrecacheResponses( false );
  53. }
  54. }
  55. //
  56. // Twitch.tv reservation updates
  57. //
  58. class ClientJob_EMsgGCCStrike15_v2_GC2ServerReservationUpdate : public GCSDK::CGCClientJob
  59. {
  60. public:
  61. ClientJob_EMsgGCCStrike15_v2_GC2ServerReservationUpdate( GCSDK::CGCClient *pGCClient )
  62. : GCSDK::CGCClientJob( pGCClient )
  63. {
  64. }
  65. virtual bool BYieldingRunJobFromMsg( GCSDK::IMsgNetPacket *pNetPacket )
  66. {
  67. GCSDK::CProtoBufMsg<CMsgGCCStrike15_v2_GC2ServerReservationUpdate> msg( pNetPacket );
  68. uint32 numTotalViewers = msg.Body().viewers_external_total();
  69. uint32 numSteamLinkedViewers = msg.Body().viewers_external_steam();
  70. engine->UpdateHltvExternalViewers( numTotalViewers, numSteamLinkedViewers );
  71. return true;
  72. }
  73. };
  74. GC_REG_CLIENT_JOB( ClientJob_EMsgGCCStrike15_v2_GC2ServerReservationUpdate, k_EMsgGCCStrike15_v2_GC2ServerReservationUpdate );
  75. void GCCStrikeWelcomeMessageReceived( CMsgCStrike15Welcome const &msgCStrike )
  76. {
  77. }
  78. bool Helper_FillServerReservationStateAndPlayers( CMsgGCCStrike15_v2_MatchmakingServerReservationResponse &msgbody )
  79. {
  80. if ( !engine->IsDedicatedServer() )
  81. return false;
  82. msgbody.set_server_version( ( ( INetSupport * ) g_pMatchFramework->GetMatchExtensions()->GetRegisteredExtensionInterface( INETSUPPORT_VERSION_STRING ) )->GetEngineBuildNumber() );
  83. msgbody.set_map( STRING( gpGlobals->mapname ) );
  84. static ConVarRef sv_steamdatagramtransport_port( "sv_steamdatagramtransport_port" );
  85. // Expose information about our community server GOTV port so that clients could connect
  86. static ConVarRef tv_advertise_watchable( "tv_advertise_watchable" );
  87. static int s_nTvPort = 0; // make the TV port sticky: if we reported it non-zero once then keep reporting
  88. CEngineHltvInfo_t engineHltvInfo;
  89. if ( tv_advertise_watchable.GetBool() &&
  90. engine->GetEngineHltvInfo( engineHltvInfo ) && engineHltvInfo.m_bBroadcastActive )
  91. {
  92. s_nTvPort = engineHltvInfo.m_nTvPort;
  93. }
  94. if ( s_nTvPort )
  95. {
  96. msgbody.mutable_tv_info()->set_tv_udp_port( s_nTvPort );
  97. }
  98. // Build the list of players who are actively playing on the game server
  99. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  100. {
  101. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  102. if ( pPlayer )
  103. {
  104. if ( pPlayer->IsBot() )
  105. continue;
  106. CSteamID steamIdPlayer;
  107. if ( !pPlayer->GetSteamID( &steamIdPlayer ) )
  108. continue;
  109. if ( !steamIdPlayer.IsValid() )
  110. continue;
  111. switch ( pPlayer->GetTeamNumber() )
  112. {
  113. case TEAM_CT:
  114. case TEAM_TERRORIST:
  115. msgbody.add_reward_player_accounts( steamIdPlayer.GetAccountID() );
  116. break;
  117. default:
  118. msgbody.add_idle_player_accounts( steamIdPlayer.GetAccountID() );
  119. break;
  120. }
  121. }
  122. }
  123. return true;
  124. }
  125. void CServerGameDLL::UpdateGCInformation()
  126. {
  127. /** Removed for partner depot **/
  128. }
  129. // Marks the queue matchmaking game as starting
  130. void CServerGameDLL::ReportGCQueuedMatchStart( int32 iReservationStage, uint32 *puiConfirmedAccounts, int numConfirmedAccounts )
  131. {
  132. /** Removed for partner depot **/
  133. }
  134. //-----------------------------------------------------------------------------
  135. // Purpose: A user has had their network id setup and validated
  136. //-----------------------------------------------------------------------------
  137. void CServerGameClients::NetworkIDValidated( const char *pszUserName, const char *pszNetworkID, CSteamID steamID )
  138. {
  139. /** Removed for partner depot **/
  140. }
  141. //
  142. // Order workshop maps by MRU
  143. //
  144. static int Helper_SortWorkshopMapsMRU( const DedicatedServerUGCFileInfo_t * const *a, const DedicatedServerUGCFileInfo_t * const *b )
  145. {
  146. if ( (*a)->m_dblPlatFloatTimeReceived != (*b)->m_dblPlatFloatTimeReceived )
  147. return ( (*a)->m_dblPlatFloatTimeReceived > (*b)->m_dblPlatFloatTimeReceived ) ? -1 : 1;
  148. else
  149. return 0;
  150. }
  151. //
  152. // Matchmaking game data buffer to set into SteamGameServer()->SetGameData
  153. //
  154. void CServerGameDLL::GetMatchmakingGameData( char *buf, size_t bufSize )
  155. {
  156. char * const bufBase = buf;
  157. int len = 0;
  158. extern ConVar game_type;
  159. extern ConVar game_mode;
  160. // Put the game key
  161. Q_snprintf( buf, bufSize, "g:csgo,gt:%u,gm:%u,", game_type.GetInt(), game_mode.GetInt() );
  162. len = strlen( buf );
  163. buf += len;
  164. bufSize -= len;
  165. if ( gpGlobals && !StringIsEmpty( gpGlobals->mapGroupName.ToCStr() ) )
  166. {
  167. const CUtlStringList* mapsInGroup = g_pGameTypes->GetMapGroupMapList( gpGlobals->mapGroupName.ToCStr() );
  168. if ( mapsInGroup && g_pGameTypes->IsWorkshopMapGroup( gpGlobals->mapGroupName.ToCStr() ) )
  169. {
  170. if ( sv_workshop_allow_other_maps.GetBool() && ( bufSize >= 7 ) )
  171. { // Advertise support for other maps
  172. Q_strncpy( buf, "wks:1,", 7 );
  173. buf += 6;
  174. bufSize -= 7;
  175. }
  176. CUtlVector< PublishedFileId_t > arrAdvertisedFileIds;
  177. FOR_EACH_VEC( *mapsInGroup, i )
  178. {
  179. PublishedFileId_t id = DedicatedServerWorkshop().GetUGCMapPublishedFileID((*mapsInGroup)[i]);
  180. CFmtStr szIdAsHexString( "%llx", id );
  181. size_t len = szIdAsHexString.Length();
  182. if ( bufSize <= len + 1 )
  183. {
  184. Warning( "GameData: Too many community maps installed, not advertising for map id \"%llu (0x%s)\"\n", id, szIdAsHexString.Access() );
  185. continue;
  186. }
  187. Q_strncpy( buf, szIdAsHexString.Access(), len + 1 );
  188. buf += len;
  189. *( buf ++ ) = ',';
  190. bufSize -= len + 1;
  191. arrAdvertisedFileIds.AddToTail( id );
  192. }
  193. // Advertise maps that have been recently checked and downloaded from Workshop
  194. if ( sv_workshop_allow_other_maps.GetBool() )
  195. {
  196. CUtlVector<const DedicatedServerUGCFileInfo_t *> arrInfoMaps;
  197. DedicatedServerWorkshop().GetWorkshopMasWithValidUgcInformation( arrInfoMaps );
  198. arrInfoMaps.Sort( Helper_SortWorkshopMapsMRU );
  199. FOR_EACH_VEC( arrInfoMaps, iInfoMap )
  200. {
  201. PublishedFileId_t id = arrInfoMaps[iInfoMap]->fileId;
  202. if ( arrAdvertisedFileIds.Find( id ) != arrAdvertisedFileIds.InvalidIndex() )
  203. continue; // already advertised
  204. CFmtStr szIdAsHexString( "%llx", id );
  205. size_t len = szIdAsHexString.Length();
  206. if ( bufSize <= len + 1 )
  207. break; // Advertise only as much downloaded stuff as can fit
  208. Q_strncpy( buf, szIdAsHexString.Access(), len + 1 );
  209. buf += len;
  210. *( buf ++ ) = ',';
  211. bufSize -= len + 1;
  212. }
  213. }
  214. }
  215. }
  216. // Trim the last comma if anything was written
  217. if ( buf > bufBase )
  218. buf[ -1 ] = 0;
  219. }
  220. // this returns true if they were already in the list or were successfully added
  221. // returns false if they were not added (not allowed to be a caster)
  222. bool AddAccountToActiveCasters( const CSteamID &steamID )
  223. {
  224. // first check if they are already in the list
  225. bool bAlreadyAdded = false;
  226. for ( int j = 0; j < CSGameRules()->m_arrTournamentActiveCasterAccounts.Count(); j++ )
  227. {
  228. if ( steamID.GetAccountID() == CSGameRules()->m_arrTournamentActiveCasterAccounts[ j ] )
  229. {
  230. // this caster is already in the list so skip adding them, but allow them
  231. bAlreadyAdded = true;
  232. break;
  233. }
  234. if ( CSGameRules()->m_arrTournamentActiveCasterAccounts[ j ] )
  235. {
  236. // already have an active caster, so don't allow another
  237. return false;
  238. }
  239. }
  240. if ( !bAlreadyAdded )
  241. {
  242. // not already added, so find an empty slot and put them in it
  243. for (int j = 0; j < CSGameRules()->m_arrTournamentActiveCasterAccounts.Count(); j++ )
  244. {
  245. if ( CSGameRules()->m_arrTournamentActiveCasterAccounts[ j ] == 0 )
  246. {
  247. CSGameRules()->m_arrTournamentActiveCasterAccounts.Set( j, steamID.GetAccountID() );
  248. if ( steamapicontext->SteamUser() && steamapicontext->SteamFriends() )
  249. {
  250. const char *pszName = steamapicontext->SteamFriends()->GetFriendPersonaName( steamID );
  251. ConMsg( "Adding %s (ID:%d) to active caster list!\n", pszName, steamID.GetAccountID() );
  252. }
  253. else
  254. {
  255. ConMsg( "Adding ID:%d to active caster list!\n", steamID.GetAccountID() );
  256. }
  257. break;
  258. }
  259. }
  260. }
  261. return true;
  262. }
  263. // validate if player is a caster and is not playing in the current game, then add them to the active caster list
  264. // returns false if they are not allow to be a caster
  265. bool CServerGameDLL::ValidateAndAddActiveCaster( const CSteamID &steamID )
  266. {
  267. // check if they are a player in the current game. Note: players can be casters sometimes (and might be in the casters list below), but we don't want their voice data "public" when they are playing
  268. for ( int i = 0; i < CCSGameRules::sm_QueuedServerReservation.account_ids().size(); i++ )
  269. {
  270. if ( steamID.GetAccountID() == CCSGameRules::sm_QueuedServerReservation.account_ids( i ) )
  271. {
  272. // this is a player
  273. return false;
  274. }
  275. }
  276. // they weren't in the player list, so now check the caster list
  277. for ( int i = 0; i < CCSGameRules::sm_QueuedServerReservation.tournament_casters_account_ids().size(); i++ )
  278. {
  279. if ( steamID.GetAccountID() == CCSGameRules::sm_QueuedServerReservation.tournament_casters_account_ids( i ) )
  280. {
  281. // this is a caster
  282. return AddAccountToActiveCasters( steamID );
  283. }
  284. }
  285. if ( tv_allow_camera_man_steamid.GetString()[0] && engine->IsDedicatedServer() )
  286. {
  287. CSteamID steamidCameraMan( V_atoui64( tv_allow_camera_man_steamid.GetString() ) );
  288. if ( steamidCameraMan.IsValid() && steamidCameraMan.BIndividualAccount() && steamidCameraMan.GetAccountID() &&
  289. ( steamidCameraMan.GetAccountID() == steamID.GetAccountID() ) )
  290. {
  291. return AddAccountToActiveCasters( steamID );
  292. }
  293. }
  294. return false;
  295. }
  296. // Returns which encryption key to use for messages to be encrypted for TV
  297. EncryptedMessageKeyType_t CServerGameDLL::GetMessageEncryptionKey( INetMessage *pMessage )
  298. {
  299. switch ( pMessage->GetType() )
  300. {
  301. case svc_VoiceData:
  302. {
  303. // check the voice data packets for being from an active caster and add the caster flag and use the public key
  304. CSVCMsg_VoiceData_t *pVoiceData = ( CSVCMsg_VoiceData_t * ) pMessage;
  305. CSteamID steamID( static_cast<uint64>( pVoiceData->xuid() ) );
  306. if ( steamID.GetAccountID() )
  307. {
  308. for ( int j = 0; j < CSGameRules()->m_arrTournamentActiveCasterAccounts.Count(); j++ )
  309. {
  310. if ( steamID.GetAccountID() == CSGameRules()->m_arrTournamentActiveCasterAccounts[ j ] )
  311. {
  312. pVoiceData->set_caster( true );
  313. return kEncryptedMessageKeyType_Public;
  314. }
  315. }
  316. }
  317. }
  318. return kEncryptedMessageKeyType_Private;
  319. case svc_UserMessage:
  320. {
  321. CSVCMsg_UserMessage_t *pUsrMessageHeader = ( CSVCMsg_UserMessage_t * ) pMessage;
  322. switch ( pUsrMessageHeader->msg_type() )
  323. {
  324. case CS_UM_SayText:
  325. {
  326. CCSUsrMsg_SayText usrMsg;
  327. if ( usrMsg.ParseFromArray( &pUsrMessageHeader->msg_data().at( 0 ), pUsrMessageHeader->msg_data().size() ) )
  328. {
  329. if ( usrMsg.textallchat() )
  330. return kEncryptedMessageKeyType_Public;
  331. }
  332. }
  333. return kEncryptedMessageKeyType_Private;
  334. case CS_UM_SayText2:
  335. {
  336. CCSUsrMsg_SayText2 usrMsg;
  337. if ( usrMsg.ParseFromArray( &pUsrMessageHeader->msg_data().at( 0 ), pUsrMessageHeader->msg_data().size() ) )
  338. {
  339. if ( usrMsg.textallchat() )
  340. return kEncryptedMessageKeyType_Public;
  341. }
  342. }
  343. return kEncryptedMessageKeyType_Private;
  344. case CS_UM_TextMsg:
  345. case CS_UM_RadioText:
  346. case CS_UM_RawAudio:
  347. case CS_UM_SendAudio:
  348. return kEncryptedMessageKeyType_Private;
  349. default:
  350. return kEncryptedMessageKeyType_None;
  351. }
  352. }
  353. return kEncryptedMessageKeyType_None;
  354. case svc_EncryptedData:
  355. default:
  356. return kEncryptedMessageKeyType_None;
  357. }
  358. }
  359. // If server game dll needs more time before server process quits then
  360. // it should return true to hold game server reservation from this interface method.
  361. // If this method returns false then the server process will clear the reservation
  362. // and might shutdown to meet uptime or memory limit requirements.
  363. bool CServerGameDLL::ShouldHoldGameServerReservation( float flTimeElapsedWithoutClients )
  364. {
  365. /** Removed for partner depot **/
  366. return false; // let the server get unreserved
  367. }
  368. // Pure server validation failed for the given client, client supplied
  369. // data is included in the payload
  370. void CServerGameDLL::OnPureServerFileValidationFailure( edict_t *edictClient, const char *path, const char *fileName, uint32 crc, int32 hashType, int32 len, int packNumber, int packFileID )
  371. {
  372. /** Removed for partner depot **/
  373. }
  374. // Last chance validation on connect packet for the client, non-NULL return value
  375. // causes the client connect to be aborted with the provided error
  376. char const * CServerGameDLL::ClientConnectionValidatePreNetChan( bool bGameServer, char const *adr, int nAuthProtocol, uint64 ullSteamID )
  377. {
  378. /** Removed for partner depot **/
  379. return NULL; // allow connections by default
  380. }
  381. // Network channel notification from engine to game server code
  382. void CServerGameDLL::OnEngineClientNetworkEvent( edict_t *edictClient, uint64 ullSteamID, int nEventType, void *pvParam )
  383. {
  384. /** Removed for partner depot **/
  385. }
  386. // Game server notifying GC with its sync packet
  387. void CServerGameDLL::EngineGotvSyncPacket( const CEngineGotvSyncPacket *pPkt )
  388. {
  389. /** Removed for partner depot **/
  390. }
  391. // GOTV client attempt redirect over SDR
  392. bool CServerGameDLL::OnEngineClientProxiedRedirect( uint64 ullClient, const char *adrProxiedRedirect, const char *adrRegular )
  393. {
  394. /** Removed for partner depot **/
  395. return false;
  396. }
  397. bool CServerGameDLL::LogForHTTPListeners( const char* szLogLine )
  398. {
  399. return GetServerLogHTTPDispatcher()->LogForHTTPListeners( szLogLine );
  400. }
  401. //-----------------------------------------------------------------------------
  402. // Purpose: Called to apply lobby settings to a dedicated server
  403. //-----------------------------------------------------------------------------
  404. void CServerGameDLL::ApplyGameSettings( KeyValues *pKV )
  405. {
  406. if ( !pKV )
  407. {
  408. return;
  409. }
  410. if ( engine )
  411. {
  412. DevMsg( "CServerGameDLL::ApplyGameSettings game settings payload received:\n" );
  413. KeyValuesDumpAsDevMsg( pKV, 1 );
  414. const char* pMapName = NULL;
  415. const char* pMapNameFromKV = pKV->GetString( "game/map" );
  416. const char* pGameType = pKV->GetString( "game/type" );
  417. const char* pGameMode = pKV->GetString( "game/mode" );
  418. const char* pMapGroupName = pKV->GetString( "game/mapgroupname", NULL );
  419. const char* pMapGroupNameToValidate = NULL; // pMapGroupName is ok to be NULL; this variable lets us easily use a non null pMapGroupName or gpGlobals->mapGroupName
  420. if ( !IsValveDS() &&
  421. pMapNameFromKV && StringHasPrefix( pMapNameFromKV, "workshop" ) &&
  422. pMapGroupName && Q_stristr( pMapGroupName, "@workshop" ) )
  423. {
  424. // A community server is getting reserved by a client for a workshop map,
  425. // retain our current workshop collection if we are hosting one to preserve
  426. // map rotation process
  427. pMapGroupName = engine->IsDedicatedServer() ? STRING( gpGlobals->mapGroupName ) : pMapGroupName;
  428. }
  429. if ( pMapGroupName && (pMapGroupName[0] != '\0') && !pMapNameFromKV )
  430. {
  431. // if we have a mapgroup name, then we don't care about any map name from the pKV and we just want the first map from the mapgroup
  432. pMapName = g_pGameTypes->GetRandomMap( pMapGroupName );
  433. pMapGroupNameToValidate = pMapGroupName;
  434. }
  435. else
  436. {
  437. pMapGroupNameToValidate = ( pMapGroupName && (pMapGroupName[0] != '\0') ) ? pMapGroupName : STRING( gpGlobals->mapGroupName );
  438. }
  439. // make sure we are not using a bogus mapgroup name
  440. if ( pMapGroupNameToValidate && !StringIsEmpty( pMapGroupNameToValidate ) && !g_pGameTypes->IsValidMapGroupName( pMapGroupNameToValidate ) )
  441. {
  442. Warning( "ApplyGameSettings: Invalid mapgroup name %s\n", pMapGroupNameToValidate );
  443. return;
  444. }
  445. // only use the map name from the pKV if there was no mapgroup name in the pKV
  446. if ( !pMapName )
  447. {
  448. pMapName = pMapNameFromKV;
  449. }
  450. // For team games we add the prefix "team" to the game type. This is to
  451. // eliminate team game lobbies from searches for QuickMatch and Custom Match
  452. char *teamStr = "team";
  453. const char *pTeamPrefix = Q_strstr( pGameType, teamStr);
  454. if ( pTeamPrefix == pGameType )
  455. {
  456. pGameType += Q_strlen( teamStr );
  457. }
  458. if ( pMapName && pMapName[0] != '\0' )
  459. {
  460. // validate map exists in the mapgroup
  461. if ( !g_pGameTypes->IsValidMapInMapGroup( pMapGroupNameToValidate, pMapName ) )
  462. {
  463. Warning( "ApplyGameSettings: Map %s not part of Mapgroup %s\n", pMapName, pMapGroupNameToValidate );
  464. }
  465. int extraSpectators = 2;
  466. if ( ( pGameType && pGameType[0] != '\0' ) &&
  467. ( pGameMode && pGameMode[0] != '\0' ) )
  468. {
  469. // make sure the mapgroup is in this game type & mode
  470. if ( !g_pGameTypes->IsValidMapGroupForTypeAndMode( pMapGroupNameToValidate, pGameType, pGameMode ) )
  471. {
  472. Warning( "ApplyGameSettings: MapGroup %s not part of type %s mode %s\n", pMapGroupNameToValidate, pGameType, pGameMode );
  473. }
  474. // Get the bot difficulty setting before it gets reverted.
  475. ConVarRef cvCustomBotDiff( "custom_bot_difficulty" );
  476. int customBotDiff = cvCustomBotDiff.GetInt();
  477. /*
  478. // FIXME[pmf]: We don't want to reset all replicated convars unless we also re-exec game.cfg on the server,
  479. // otherwise we'll overwrite all the game configuration convars specified in game.cfg
  480. // Reset server enforced convars
  481. g_pCVar->RevertFlaggedConVars( FCVAR_REPLICATED );
  482. */
  483. // Cheats were disabled; revert all cheat cvars to their default values.
  484. // This must be done heading into multiplayer games because people can play
  485. // demos etc and set cheat cvars with sv_cheats 0.
  486. g_pCVar->RevertFlaggedConVars( FCVAR_CHEAT );
  487. // we know that the loading screen data is correct, so let the loading screen know
  488. //g_pGameTypes->SetLoadingScreenDataIsCorrect( true );
  489. g_pGameTypes->SetRunMapWithDefaultGametype( false );
  490. // Set game_type and game_mode convars.
  491. g_pGameTypes->SetGameTypeAndMode( pGameType, pGameMode );
  492. extern ConVar game_online;
  493. if ( char const *szOnline = pKV->GetString( "system/network", NULL ) )
  494. {
  495. game_online.SetValue( ( !V_stricmp( szOnline, "LIVE" ) ) ? 1 : 0 );
  496. }
  497. extern ConVar game_public;
  498. if ( char const *szAccess = pKV->GetString( "system/access", NULL ) )
  499. {
  500. game_public.SetValue( ( !V_stricmp( szAccess, "public" ) ) ? 1 : 0 );
  501. }
  502. #ifndef CLIENT_DLL
  503. if ( engine->IsDedicatedServer() )
  504. game_online.SetValue( 1 );
  505. #endif
  506. // Special case: set the custom bot difficulty for offline games
  507. if ( !game_online.GetBool() )
  508. {
  509. g_pGameTypes->SetCustomBotDifficulty( customBotDiff );
  510. }
  511. // Make sure that correct number of slots is set for the engine
  512. {
  513. int iType, iMode;
  514. if ( g_pGameTypes->GetGameModeAndTypeIntsFromStrings( pGameType, pGameMode, iType, iMode ) )
  515. {
  516. int iMaxPlayersForTypeMode = g_pGameTypes->GetMaxPlayersForTypeAndMode( iType, iMode );
  517. pKV->SetInt( "members/numSlots", iMaxPlayersForTypeMode );
  518. }
  519. }
  520. // Make sure the settings keys have extra spectator info
  521. pKV->SetInt( "members/numExtraSpectatorSlots", extraSpectators );
  522. }
  523. CFmtStr command;
  524. if ( pMapGroupName )
  525. {
  526. command.AppendFormat( "mapgroup %s\n", pMapGroupName );
  527. }
  528. command.AppendFormat( "nextlevel %s\n", pMapName ); // gamerules will clean it up when they construct for the next map
  529. command.AppendFormat( "map %s reserved\n", pMapName );
  530. Warning( "Executing server command:\n%s\n---\n", command.Access() );
  531. engine->ServerCommand( command );
  532. if ( engine->IsDedicatedServer() )
  533. engine->ServerExecute();
  534. }
  535. }
  536. }
  537. const char * CServerGameClients::ClientNameHandler( uint64 xuid, const char *pchName )
  538. {
  539. CSteamID steamID( xuid );
  540. // In tournament mode force names for the players according to the reservation
  541. if ( steamID.IsValid() && steamID.BIndividualAccount() &&
  542. CCSGameRules::sm_QueuedServerReservation.has_tournament_event() )
  543. {
  544. for ( int32 iTeam = 0; iTeam < CCSGameRules::sm_QueuedServerReservation.tournament_teams().size(); ++iTeam )
  545. {
  546. TournamentTeam const &ttTeam = CCSGameRules::sm_QueuedServerReservation.tournament_teams( iTeam );
  547. for ( int32 iTeamPlayer = 0; iTeamPlayer < ttTeam.players().size(); ++iTeamPlayer )
  548. {
  549. TournamentPlayer const &ttPlayer = ttTeam.players( iTeamPlayer );
  550. if ( ttPlayer.account_id() && ( ttPlayer.account_id() == steamID.GetAccountID() ) )
  551. {
  552. return ( ttPlayer.player_nick().c_str() );
  553. }
  554. }
  555. }
  556. }
  557. // Throttle name changes from clients (hacked clients can set the name convar at any rate)
  558. extern CCSPlayer *ToCSPlayer( CBaseEntity *pEntity );
  559. if ( CCSPlayer* pPlayer = ToCSPlayer( CBasePlayer::GetPlayerBySteamID( steamID ) ) )
  560. {
  561. if ( !pPlayer->CanChangeName() )
  562. return pPlayer->GetPlayerName();
  563. }
  564. // Account not resolved, use whatever name they provided
  565. return pchName;
  566. }
  567. void CServerGameClients::ClientSvcUserMessage( edict_t *pEntity, int nType, int nPassthrough, uint32 cbSize, const void *pvBuffer )
  568. {
  569. CCSPlayer *pPlayer = ToCSPlayer( GetContainingEntity( pEntity ) );
  570. if ( !pPlayer )
  571. return;
  572. switch ( nType )
  573. {
  574. case CS_UM_PlayerDecalDigitalSignature:
  575. {
  576. CCSUsrMsg_PlayerDecalDigitalSignature msg;
  577. if ( msg.ParseFromArray( pvBuffer, cbSize ) )
  578. pPlayer->SprayPaint( msg );
  579. }
  580. return;
  581. }
  582. }