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.

510 lines
14 KiB

  1. //===== Copyright � 1996-2009, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "mm_title.h"
  7. #include "mm_title_richpresence.h"
  8. #include "csgo.spa.h"
  9. #ifdef _PS3
  10. #include <netex/net.h>
  11. #include <netex/libnetctl.h>
  12. #endif
  13. #include "fmtstr.h"
  14. #include "gametypes/igametypes.h"
  15. #include "netmessages_signon.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. extern IGameTypes *g_pGameTypes;
  19. CMatchTitle::CMatchTitle()
  20. {
  21. }
  22. CMatchTitle::~CMatchTitle()
  23. {
  24. }
  25. //
  26. // Init / shutdown
  27. //
  28. InitReturnVal_t CMatchTitle::Init()
  29. {
  30. if ( IGameEventManager2 *mgr = g_pMatchExtensions->GetIGameEventManager2() )
  31. {
  32. mgr->AddListener( this, "server_pre_shutdown", false );
  33. mgr->AddListener( this, "game_newmap", false );
  34. mgr->AddListener( this, "finale_start", false );
  35. mgr->AddListener( this, "round_start", false );
  36. mgr->AddListener( this, "round_end", false );
  37. mgr->AddListener( this, "difficulty_changed", false );
  38. mgr->AddListener( this, "player_connect", false );
  39. mgr->AddListener( this, "player_disconnect", false );
  40. }
  41. #ifdef _GAMECONSOLE
  42. // Initialize Title Update version
  43. extern ConVar mm_tu_string;
  44. mm_tu_string.SetValue( "00000000" );
  45. #endif
  46. g_pGameTypes->Initialize();
  47. return INIT_OK;
  48. }
  49. void CMatchTitle::Shutdown()
  50. {
  51. if ( IGameEventManager2 *mgr = g_pMatchExtensions->GetIGameEventManager2() )
  52. {
  53. mgr->RemoveListener( this );
  54. }
  55. }
  56. //
  57. // Implementation
  58. //
  59. uint64 CMatchTitle::GetTitleID()
  60. {
  61. #ifdef _X360
  62. return TITLEID_COUNTER_STRIKE__GO;
  63. #elif !defined( SWDS ) && !defined( NO_STEAM )
  64. static uint64 uiAppID = 0ull;
  65. if ( !uiAppID && steamapicontext && steamapicontext->SteamUtils() )
  66. {
  67. uiAppID = steamapicontext->SteamUtils()->GetAppID();
  68. }
  69. return uiAppID;
  70. #else
  71. return 0ull;
  72. #endif
  73. }
  74. uint64 CMatchTitle::GetTitleServiceID()
  75. {
  76. #ifdef _X360
  77. return 0x45410880ull; // Left 4 Dead 1 Service ID
  78. #else
  79. return 0ull;
  80. #endif
  81. }
  82. #ifdef _PS3
  83. uint64 CMatchTitle::GetTitleSettingsFlags()
  84. {
  85. return MATCHTITLE_SETTING_MULTIPLAYER
  86. | MATCHTITLE_VOICE_INGAME
  87. | MATCHTITLE_FORCE_PSN_NAMES
  88. | MATCHTITLE_PLAYERMGR_DISABLED
  89. | MATCHTITLE_SERVERMGR_DISABLED;
  90. }
  91. #else
  92. uint64 CMatchTitle::GetTitleSettingsFlags()
  93. {
  94. return MATCHTITLE_SETTING_MULTIPLAYER
  95. | MATCHTITLE_VOICE_INGAME
  96. | MATCHTITLE_PLAYERMGR_ALLFRIENDS
  97. #if !defined( CSTRIKE15 )
  98. | MATCHTITLE_SETTING_NODEDICATED
  99. | MATCHTITLE_PLAYERMGR_DISABLED
  100. | MATCHTITLE_SERVERMGR_DISABLED
  101. | MATCHTITLE_INVITE_ONLY_SINGLE_USER
  102. #else
  103. | MATCHTITLE_PLAYERMGR_FRIENDREQS
  104. #endif // !CSTRIKE15
  105. ;
  106. }
  107. #endif
  108. #ifdef _PS3
  109. void *g_pMatchTitle_NetMemory;
  110. #endif
  111. void CMatchTitle::PrepareNetStartupParams( void *pNetStartupParams )
  112. {
  113. #ifdef _X360
  114. XNetStartupParams &xnsp = *( XNetStartupParams * ) pNetStartupParams;
  115. xnsp.cfgQosDataLimitDiv4 = 64; // 256 bytes
  116. // Increase outstanding QoS responses significantly
  117. // See: https://forums.xboxlive.com/AnswerPage.aspx?qid=493b207b-66b9-42bc-b23d-ddc306e09749&tgt=1
  118. xnsp.cfgQosSrvMaxSimultaneousResponses = 255;
  119. xnsp.cfgSockDefaultRecvBufsizeInK = 64; // Increase receive size for UDP to 64k
  120. xnsp.cfgSockDefaultSendBufsizeInK = 64; // Keep send size at 64k too
  121. int numGamePlayersMax = GetTotalNumPlayersSupported();
  122. int numConnections = 4 * ( numGamePlayersMax - 1 );
  123. // - the max number of connections to members of your game party
  124. // - the max number of connections to members of your social party
  125. // - the max number of connections to a pending game party (if you are joining a new one ).
  126. // - matchmakings client info structure also creates a connection per client for the lobby.
  127. // 1 - the main game session
  128. int numTotalConnections = 1 + numConnections;
  129. // 29 - total Connections (XNADDR/XNKID pairs) ,using 5 sessions (XNKID/XNKEY pairs).
  130. xnsp.cfgKeyRegMax = 16; //adding some extra room because of lazy dealocation of these pairs.
  131. xnsp.cfgSecRegMax = MAX( 64, numTotalConnections ); //adding some extra room because of lazy dealocation of these pairs.
  132. xnsp.cfgSockMaxDgramSockets = xnsp.cfgSecRegMax;
  133. xnsp.cfgSockMaxStreamSockets = xnsp.cfgSecRegMax;
  134. #endif
  135. #if defined( _PS3 ) && defined( NO_STEAM )
  136. MEM_ALLOC_CREDIT_( "NO_STEAM: CMatchTitle::PrepareNetStartupParams" );
  137. sys_net_initialize_parameter_t &snip = *( sys_net_initialize_parameter_t * ) pNetStartupParams;
  138. snip.memory_size = 512 * 1024;
  139. snip.memory = malloc( snip.memory_size ); // alternatively this can be a global array
  140. g_pMatchTitle_NetMemory = snip.memory; // bookmark the memory address for later inspection if necessary
  141. #endif
  142. }
  143. int CMatchTitle::GetTotalNumPlayersSupported()
  144. {
  145. #ifdef _GAMECONSOLE
  146. // [jason] Rounding this up to 16, since this was the value used on cstrike15 xbla. Among other things, this
  147. // controls how many remote talker voice channels are reserved for each client in XHV
  148. return 16;
  149. #else
  150. return 64; // On PC this is not limited, return max number dedicated servers can ever run with
  151. #endif
  152. }
  153. // Get a guest player name
  154. char const * CMatchTitle::GetGuestPlayerName( int iUserIndex )
  155. {
  156. if ( vgui::ILocalize *pLocalize = g_pMatchExtensions->GetILocalize() )
  157. {
  158. if ( wchar_t* wStringTableEntry = pLocalize->Find( "#SFUI_LocalPlayer" ) )
  159. {
  160. static char szName[ MAX_PLAYER_NAME_LENGTH ] = {0};
  161. pLocalize->ConvertUnicodeToANSI( wStringTableEntry, szName, ARRAYSIZE( szName ) );
  162. return szName;
  163. }
  164. }
  165. return "";
  166. }
  167. // Sets up all necessary client-side convars and user info before
  168. // connecting to server
  169. void CMatchTitle::PrepareClientForConnect( KeyValues *pSettings )
  170. {
  171. #ifndef SWDS
  172. int numPlayers = 1;
  173. #ifdef _GAMECONSOLE
  174. numPlayers = XBX_GetNumGameUsers();
  175. #endif
  176. //
  177. // Now we set the convars
  178. //
  179. for ( int k = 0; k < numPlayers; ++ k )
  180. {
  181. int iController = k;
  182. #ifdef _GAMECONSOLE
  183. iController = XBX_GetUserId( k );
  184. #endif
  185. IPlayerLocal *pPlayerLocal = g_pPlayerManager->GetLocalPlayer( iController );
  186. if ( !pPlayerLocal )
  187. continue;
  188. // Set "name"
  189. static SplitScreenConVarRef s_cl_name( "name" );
  190. char const *szName = pPlayerLocal->GetName();
  191. s_cl_name.SetValue( k, szName );
  192. // Set "networkid_force"
  193. if ( IsX360() )
  194. {
  195. static SplitScreenConVarRef s_networkid_force( "networkid_force" );
  196. uint64 xid = pPlayerLocal->GetXUID();
  197. s_networkid_force.SetValue( k, CFmtStr( "%08X:%08X:", uint32( xid >> 32 ), uint32( xid ) ) );
  198. }
  199. }
  200. #endif
  201. }
  202. bool CMatchTitle::StartServerMap( KeyValues *pSettings )
  203. {
  204. int numPlayers = 1;
  205. #ifdef _GAMECONSOLE
  206. numPlayers = XBX_GetNumGameUsers();
  207. #endif
  208. char const *szMap = pSettings->GetString( "game/map", NULL );
  209. if ( !szMap )
  210. return false;
  211. // Check that we have the server interface and that the map is valid
  212. if ( !g_pMatchExtensions->GetIVEngineServer() )
  213. return false;
  214. if ( !g_pMatchExtensions->GetIVEngineServer()->IsMapValid( szMap ) )
  215. return false;
  216. //
  217. // Prepare game dll reservation package
  218. //
  219. KeyValues *pGameDllReserve = g_pMatchFramework->GetMatchNetworkMsgController()->PackageGameDetailsForReservation( pSettings );
  220. KeyValues::AutoDelete autodelete( pGameDllReserve );
  221. pGameDllReserve->SetString( "map/mapcommand", ( numPlayers <= 1 ) ? "map" : "ss_map" );
  222. if ( !Q_stricmp( "commentary", pSettings->GetString( "options/play", "" ) ) )
  223. pGameDllReserve->SetString( "map/mapcommand", "map_commentary" );
  224. // Run map based off the faked reservation packet
  225. g_pMatchExtensions->GetIVEngineClient()->StartLoadingScreenForKeyValues( pGameDllReserve );
  226. return true;
  227. }
  228. static KeyValues * GetCurrentMatchSessionSettings()
  229. {
  230. IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession();
  231. return pIMatchSession ? pIMatchSession->GetSessionSettings() : NULL;
  232. }
  233. //
  234. // <Vitaliy> July-2012
  235. // CS:GO hack: training map has been implemented storing progress in convars instead of
  236. // properly storing it in game profile data, force storing the convars into profile
  237. // when the training session finishes here
  238. // Please, do not use this approach in future code.
  239. //
  240. bool g_bPlayingTrainingMap = false;
  241. void CMatchTitle::OnEvent( KeyValues *pEvent )
  242. {
  243. char const *szEvent = pEvent->GetName();
  244. if ( !Q_stricmp( "OnPlayerRemoved", szEvent ) ||
  245. !Q_stricmp( "OnPlayerUpdated", szEvent ) )
  246. {
  247. MM_Title_RichPresence_Update( GetCurrentMatchSessionSettings(), NULL );
  248. }
  249. else if ( !Q_stricmp( "OnPlayerMachinesConnected", szEvent ) ||
  250. !Q_stricmp( "OnPlayerMachinesDisconnected", szEvent ) )
  251. {
  252. // Player counts changed on host, update aggregate fields
  253. IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
  254. if ( !pMatchSession )
  255. return;
  256. KeyValues *kvPackage = new KeyValues( "Update" );
  257. if ( KeyValues *kvUpdate = kvPackage->FindKey( "update", true ) )
  258. {
  259. extern void UpdateAggregateMembersSettings( KeyValues *pFullGameSettings, KeyValues *pUpdate );
  260. UpdateAggregateMembersSettings( pMatchSession->GetSessionSettings(), kvUpdate );
  261. }
  262. pMatchSession->UpdateSessionSettings( KeyValues::AutoDeleteInline( kvPackage ) );
  263. }
  264. else if ( !Q_stricmp( "OnMatchSessionUpdate", szEvent ) )
  265. {
  266. if ( !Q_stricmp( pEvent->GetString( "state" ), "updated" ) )
  267. {
  268. if ( KeyValues *kvUpdate = pEvent->FindKey( "update" ) )
  269. {
  270. MM_Title_RichPresence_Update( GetCurrentMatchSessionSettings(), kvUpdate );
  271. }
  272. }
  273. else if ( !Q_stricmp( pEvent->GetString( "state" ), "created" ) ||
  274. !Q_stricmp( pEvent->GetString( "state" ), "ready" ) )
  275. {
  276. MM_Title_RichPresence_Update( GetCurrentMatchSessionSettings(), NULL );
  277. if ( IMatchSession *pSession = g_pMatchFramework->GetMatchSession() )
  278. {
  279. if ( !Q_stricmp( "training", pSession->GetSessionSettings()->GetString( "game/type" ) ) )
  280. g_bPlayingTrainingMap = true;
  281. }
  282. }
  283. else if ( !Q_stricmp( pEvent->GetString( "state" ), "closed" ) )
  284. {
  285. if ( g_bPlayingTrainingMap )
  286. {
  287. g_bPlayingTrainingMap = false;
  288. g_pMatchExtensions->GetIVEngineClient()->ClientCmd_Unrestricted( CFmtStr( "host_writeconfig_ss %d", XBX_GetPrimaryUserId() ) );
  289. }
  290. MM_Title_RichPresence_Update( NULL, NULL );
  291. }
  292. }
  293. else if ( !Q_stricmp( szEvent, "Client::CmdKeyValues" ) )
  294. {
  295. KeyValues *pCmd = pEvent->GetFirstTrueSubKey();
  296. if ( !pCmd )
  297. return;
  298. char const *szCmd = pCmd->GetName();
  299. if ( !Q_stricmp( "ExtendedServerInfo", szCmd ) )
  300. {
  301. KeyValuesDumpAsDevMsg( pCmd, 2, 1 );
  302. g_pGameTypes->SetAndParseExtendedServerInfo( pCmd );
  303. }
  304. }
  305. else if ( !Q_stricmp( "OnEngineClientSignonStateChange", szEvent ) )
  306. {
  307. int iOldState = pEvent->GetInt( "old", 0 );
  308. int iNewState = pEvent->GetInt( "new", 0 );
  309. if (
  310. ( iOldState >= SIGNONSTATE_CONNECTED && // disconnect
  311. iNewState < SIGNONSTATE_CONNECTED ) ||
  312. ( iOldState < SIGNONSTATE_FULL && // full connect
  313. iNewState >= SIGNONSTATE_FULL )
  314. )
  315. {
  316. MM_Title_RichPresence_Update( NULL, NULL );
  317. }
  318. }
  319. else if ( !Q_stricmp( "OnEngineDisconnectReason", szEvent ) )
  320. {
  321. MM_Title_RichPresence_Update( NULL, NULL );
  322. }
  323. else if ( !Q_stricmp( "OnEngineEndGame", szEvent ) )
  324. {
  325. MM_Title_RichPresence_Update( NULL, NULL );
  326. }
  327. }
  328. //
  329. //
  330. //
  331. int CMatchTitle::GetEventDebugID( void )
  332. {
  333. return EVENT_DEBUG_ID_INIT;
  334. }
  335. void CMatchTitle::FireGameEvent( IGameEvent *pIGameEvent )
  336. {
  337. // Parse the game event
  338. char const *szGameEvent = pIGameEvent->GetName();
  339. if ( !szGameEvent || !*szGameEvent )
  340. return;
  341. if ( !Q_stricmp( "round_start", szGameEvent ) ||
  342. !Q_stricmp( "round_end", szGameEvent ) ||
  343. !Q_stricmp( "game_newmap", szGameEvent ) ||
  344. !Q_stricmp( "player_connect", szGameEvent ) ||
  345. !Q_stricmp( "player_disconnect", szGameEvent ) )
  346. { // Update rich presence
  347. MM_Title_RichPresence_Update( NULL, NULL );
  348. }
  349. // Check if the current match session is on an active game server
  350. IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
  351. if ( !pMatchSession )
  352. return;
  353. KeyValues *pSessionSettings = pMatchSession->GetSessionSettings();
  354. char const *szGameServer = pSessionSettings->GetString( "server/server", "" );
  355. char const *szSystemLock = pSessionSettings->GetString( "system/lock", "" );
  356. if ( ( !szGameServer || !*szGameServer ) &&
  357. ( !szSystemLock || !*szSystemLock ) )
  358. return;
  359. // Also don't run on the client when there's a host
  360. char const *szSessionType = pMatchSession->GetSessionSystemData()->GetString( "type", NULL );
  361. if ( szSessionType && !Q_stricmp( szSessionType, "client" ) )
  362. return;
  363. if ( !Q_stricmp( "round_start", szGameEvent ) )
  364. {
  365. KeyValues *kvUpdate = KeyValues::FromString(
  366. "update",
  367. " update { "
  368. " game { "
  369. " state game "
  370. " } "
  371. " } "
  372. );
  373. KeyValues::AutoDelete autodelete( kvUpdate );
  374. pMatchSession->UpdateSessionSettings( kvUpdate );
  375. }
  376. else if ( !Q_stricmp( "round_end", szGameEvent ) )
  377. {
  378. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
  379. "OnProfilesWriteOpportunity", "reason", "checkpoint"
  380. ) );
  381. }
  382. else if ( !Q_stricmp( "finale_start", szGameEvent ) )
  383. {
  384. pMatchSession->UpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString(
  385. "update",
  386. " update { "
  387. " game { "
  388. " state finale "
  389. " } "
  390. " } "
  391. ) ) );
  392. }
  393. else if ( !Q_stricmp( "game_newmap", szGameEvent ) )
  394. {
  395. const char *szMapName = pIGameEvent->GetString( "mapname", "" );
  396. KeyValues *kvUpdate = KeyValues::FromString(
  397. "update",
  398. " update { "
  399. " game { "
  400. " state game "
  401. " } "
  402. " } "
  403. );
  404. KeyValues::AutoDelete autodelete( kvUpdate );
  405. Assert( szMapName && *szMapName );
  406. if ( szMapName && *szMapName )
  407. {
  408. kvUpdate->SetString( "update/game/map", szMapName );
  409. char const *szWorkshopMap = g_pMatchExtensions->GetIVEngineClient()->GetLevelNameShort();
  410. if ( StringHasPrefix( szWorkshopMap, "workshop" ) )
  411. {
  412. size_t nLenMapName = Q_strlen( szMapName );
  413. size_t nShortMapName = Q_strlen( szWorkshopMap );
  414. if ( ( nShortMapName >= nLenMapName ) &&
  415. !Q_stricmp( szWorkshopMap + nShortMapName - nLenMapName, szMapName ) )
  416. // Use the name of the workshop map
  417. kvUpdate->SetString( "update/game/map", szWorkshopMap );
  418. }
  419. }
  420. pMatchSession->UpdateSessionSettings( kvUpdate );
  421. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
  422. "OnProfilesWriteOpportunity", "reason", "checkpoint"
  423. ) );
  424. }
  425. else if ( !Q_stricmp( "server_pre_shutdown", szGameEvent ) )
  426. {
  427. char const *szReason = pIGameEvent->GetString( "reason", "quit" );
  428. if ( !Q_stricmp( szReason, "quit" ) )
  429. {
  430. DevMsg( "Received server_pre_shutdown notification - server is shutting down...\n" );
  431. // Transform the server shutdown event into game end event
  432. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
  433. "OnEngineDisconnectReason", "reason", "Server shutting down"
  434. ) );
  435. }
  436. }
  437. }