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.

462 lines
13 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 "swarm.spa.h"
  9. #include "matchext_swarm.h"
  10. #include "fmtstr.h"
  11. // memdbgon must be the last include file in a .cpp file!!!
  12. #include "tier0/memdbgon.h"
  13. CMatchTitle::CMatchTitle()
  14. {
  15. ;
  16. }
  17. CMatchTitle::~CMatchTitle()
  18. {
  19. ;
  20. }
  21. //
  22. // Init / shutdown
  23. //
  24. InitReturnVal_t CMatchTitle::Init()
  25. {
  26. if ( IGameEventManager2 *mgr = g_pMatchExtensions->GetIGameEventManager2() )
  27. {
  28. mgr->AddListener( this, "server_pre_shutdown", false );
  29. mgr->AddListener( this, "game_newmap", false );
  30. mgr->AddListener( this, "finale_start", false );
  31. mgr->AddListener( this, "round_start", false );
  32. mgr->AddListener( this, "round_end", false );
  33. mgr->AddListener( this, "difficulty_changed", false );
  34. }
  35. // Initialize all the campaigns
  36. g_pMatchExtSwarm->Initialize();
  37. #ifdef _X360
  38. // Initialize Title Update version
  39. extern ConVar mm_tu_string;
  40. mm_tu_string.SetValue( "00000000" );
  41. #endif
  42. return INIT_OK;
  43. }
  44. void CMatchTitle::Shutdown()
  45. {
  46. if ( IGameEventManager2 *mgr = g_pMatchExtensions->GetIGameEventManager2() )
  47. {
  48. mgr->RemoveListener( this );
  49. }
  50. }
  51. //
  52. // Implementation
  53. //
  54. uint64 CMatchTitle::GetTitleID()
  55. {
  56. #ifdef _X360
  57. #ifndef _DEMO
  58. return TITLEID_SWARM;
  59. #else
  60. return TITLEID_SWARM_DEMO;
  61. #endif
  62. #elif !defined( SWDS )
  63. static uint64 uiAppID = 0ull;
  64. if ( !uiAppID && steamapicontext && steamapicontext->SteamUtils() )
  65. {
  66. uiAppID = steamapicontext->SteamUtils()->GetAppID();
  67. }
  68. return uiAppID;
  69. #else
  70. return 0ull;
  71. #endif
  72. }
  73. uint64 CMatchTitle::GetTitleServiceID()
  74. {
  75. #ifdef _X360
  76. return 0x45410880ull; // Left 4 Dead 1 Service ID
  77. #else
  78. return 0ull;
  79. #endif
  80. }
  81. bool CMatchTitle::IsMultiplayer()
  82. {
  83. return true;
  84. }
  85. void CMatchTitle::PrepareNetStartupParams( void *pNetStartupParams )
  86. {
  87. #ifdef _X360
  88. XNetStartupParams &xnsp = *( XNetStartupParams * ) pNetStartupParams;
  89. xnsp.cfgQosDataLimitDiv4 = 128; // 512 bytes
  90. xnsp.cfgSockDefaultRecvBufsizeInK = 64; // Increase receive size for UDP to 64k
  91. xnsp.cfgSockDefaultSendBufsizeInK = 64; // Keep send size at 64k too
  92. int numGamePlayersMax = GetTotalNumPlayersSupported();
  93. int numConnections = 4 * ( numGamePlayersMax - 1 );
  94. // - the max number of connections to members of your game party
  95. // - the max number of connections to members of your social party
  96. // - the max number of connections to a pending game party (if you are joining a new one ).
  97. // - matchmakings client info structure also creates a connection per client for the lobby.
  98. // 1 - the main game session
  99. int numTotalConnections = 1 + numConnections;
  100. // 29 - total Connections (XNADDR/XNKID pairs) ,using 5 sessions (XNKID/XNKEY pairs).
  101. xnsp.cfgKeyRegMax = 16; //adding some extra room because of lazy dealocation of these pairs.
  102. xnsp.cfgSecRegMax = MAX( 64, numTotalConnections ); //adding some extra room because of lazy dealocation of these pairs.
  103. xnsp.cfgSockMaxDgramSockets = xnsp.cfgSecRegMax;
  104. xnsp.cfgSockMaxStreamSockets = xnsp.cfgSecRegMax;
  105. #endif
  106. }
  107. int CMatchTitle::GetTotalNumPlayersSupported()
  108. {
  109. // Alien Swarm is a 4-player game
  110. return 4;
  111. }
  112. // Get a guest player name
  113. char const * CMatchTitle::GetGuestPlayerName( int iUserIndex )
  114. {
  115. if ( vgui::ILocalize *pLocalize = g_pMatchExtensions->GetILocalize() )
  116. {
  117. if ( wchar_t* wStringTableEntry = pLocalize->Find( "#L4D360UI_Character_Guest" ) )
  118. {
  119. static char szName[ MAX_PLAYER_NAME_LENGTH ] = {0};
  120. pLocalize->ConvertUnicodeToANSI( wStringTableEntry, szName, ARRAYSIZE( szName ) );
  121. return szName;
  122. }
  123. }
  124. return "";
  125. }
  126. // Sets up all necessary client-side convars and user info before
  127. // connecting to server
  128. void CMatchTitle::PrepareClientForConnect( KeyValues *pSettings )
  129. {
  130. #ifndef SWDS
  131. int numPlayers = 1;
  132. #ifdef _X360
  133. numPlayers = XBX_GetNumGameUsers();
  134. #endif
  135. //
  136. // Now we set the convars
  137. //
  138. for ( int k = 0; k < numPlayers; ++ k )
  139. {
  140. int iController = k;
  141. #ifdef _X360
  142. iController = XBX_GetUserId( k );
  143. #endif
  144. IPlayerLocal *pPlayerLocal = g_pPlayerManager->GetLocalPlayer( iController );
  145. if ( !pPlayerLocal )
  146. continue;
  147. // Set "name"
  148. static SplitScreenConVarRef s_cl_name( "name" );
  149. char const *szName = pPlayerLocal->GetName();
  150. s_cl_name.SetValue( k, szName );
  151. // Set "networkid_force"
  152. if ( IsX360() )
  153. {
  154. static SplitScreenConVarRef s_networkid_force( "networkid_force" );
  155. uint64 xid = pPlayerLocal->GetXUID();
  156. s_networkid_force.SetValue( k, CFmtStr( "%08X:%08X:", uint32( xid >> 32 ), uint32( xid ) ) );
  157. }
  158. }
  159. #endif
  160. }
  161. bool CMatchTitle::StartServerMap( KeyValues *pSettings )
  162. {
  163. int numPlayers = 1;
  164. #ifdef _X360
  165. numPlayers = XBX_GetNumGameUsers();
  166. #endif
  167. KeyValues *pInfoMission = NULL;
  168. KeyValues *pInfoChapter = g_pMatchExtSwarm->GetMapInfo( pSettings, &pInfoMission );
  169. Assert( pInfoChapter );
  170. if ( !pInfoChapter || !pInfoMission )
  171. return false;
  172. // Check that we have the server interface and that the map is valid
  173. if ( !g_pMatchExtensions->GetIVEngineServer() )
  174. return false;
  175. if ( !g_pMatchExtensions->GetIVEngineServer()->IsMapValid( pInfoChapter->GetString( "map" ) ) )
  176. return false;
  177. //
  178. // Prepare game dll reservation package
  179. //
  180. KeyValues *pGameDllReserve = g_pMatchFramework->GetMatchNetworkMsgController()->PackageGameDetailsForReservation( pSettings );
  181. KeyValues::AutoDelete autodelete( pGameDllReserve );
  182. pGameDllReserve->SetString( "map/mapcommand", ( numPlayers <= 1 ) ? "map" : "ss_map" );
  183. if ( !Q_stricmp( "commentary", pSettings->GetString( "options/play", "" ) ) )
  184. pGameDllReserve->SetString( "map/mapcommand", "map_commentary" );
  185. // Run map based off the faked reservation packet
  186. g_pMatchExtensions->GetIServerGameDLL()->ApplyGameSettings( pGameDllReserve );
  187. return true;
  188. }
  189. static KeyValues * GetCurrentMatchSessionSettings()
  190. {
  191. IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession();
  192. return pIMatchSession ? pIMatchSession->GetSessionSettings() : NULL;
  193. }
  194. #ifndef SWDS
  195. static void SendPreConnectClientDataToServer( int nSlot )
  196. {
  197. // We have just connected to the server,
  198. // send our avatar information
  199. int iController = nSlot;
  200. #ifdef _X360
  201. iController = XBX_GetUserId( nSlot );
  202. #endif
  203. // Swarm for now has no preconnect data
  204. if ( 1 )
  205. return;
  206. KeyValues *pPreConnectData = new KeyValues( "preconnectdata" );
  207. //
  208. // Now we prep the keyvalues for server
  209. //
  210. if ( IPlayerLocal *pPlayerLocal = g_pPlayerManager->GetLocalPlayer( iController ) )
  211. {
  212. // Set session-specific user info
  213. XUID xuid = pPlayerLocal->GetXUID();
  214. pPreConnectData->SetUint64( "xuid", xuid );
  215. KeyValues *pSettings = GetCurrentMatchSessionSettings();
  216. KeyValues *pMachine = NULL;
  217. if ( KeyValues *pPlayer = SessionMembersFindPlayer( pSettings, xuid, &pMachine ) )
  218. {
  219. }
  220. }
  221. // Deliver the keyvalues to the server
  222. int nRestoreSlot = g_pMatchExtensions->GetIVEngineClient()->GetActiveSplitScreenPlayerSlot();
  223. g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( nSlot );
  224. g_pMatchExtensions->GetIVEngineClient()->ServerCmdKeyValues( pPreConnectData );
  225. g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( nRestoreSlot );
  226. }
  227. #endif
  228. void CMatchTitle::OnEvent( KeyValues *pEvent )
  229. {
  230. char const *szEvent = pEvent->GetName();
  231. if ( !Q_stricmp( "OnPlayerRemoved", szEvent ) ||
  232. !Q_stricmp( "OnPlayerUpdated", szEvent ) )
  233. {
  234. MM_Title_RichPresence_PlayersChanged( GetCurrentMatchSessionSettings() );
  235. }
  236. else if ( !Q_stricmp( "OnMatchSessionUpdate", szEvent ) )
  237. {
  238. if ( !Q_stricmp( pEvent->GetString( "state" ), "updated" ) )
  239. {
  240. if ( KeyValues *kvUpdate = pEvent->FindKey( "update" ) )
  241. {
  242. MM_Title_RichPresence_Update( GetCurrentMatchSessionSettings(), kvUpdate );
  243. }
  244. }
  245. else if ( !Q_stricmp( pEvent->GetString( "state" ), "created" ) ||
  246. !Q_stricmp( pEvent->GetString( "state" ), "ready" ) )
  247. {
  248. MM_Title_RichPresence_Update( GetCurrentMatchSessionSettings(), NULL );
  249. }
  250. else if ( !Q_stricmp( pEvent->GetString( "state" ), "closed" ) )
  251. {
  252. MM_Title_RichPresence_Update( NULL, NULL );
  253. }
  254. }
  255. #ifndef SWDS
  256. else if ( !Q_stricmp( "OnEngineClientSignonStateChange", szEvent ) )
  257. {
  258. int nSlot = pEvent->GetInt( "slot" );
  259. int iOldState = pEvent->GetInt( "old" );
  260. int iNewState = pEvent->GetInt( "new" );
  261. if ( iOldState < SIGNONSTATE_CONNECTED &&
  262. iNewState >= SIGNONSTATE_CONNECTED )
  263. {
  264. SendPreConnectClientDataToServer( nSlot );
  265. }
  266. }
  267. else if ( !Q_stricmp( "OnEngineSplitscreenClientAdded", szEvent ) )
  268. {
  269. int nSlot = pEvent->GetInt( "slot" );
  270. SendPreConnectClientDataToServer( nSlot );
  271. }
  272. else if ( !Q_stricmp( "Client::CmdKeyValues", szEvent ) )
  273. {
  274. KeyValues *pCmd = pEvent->GetFirstTrueSubKey();
  275. if ( !pCmd )
  276. return;
  277. char const *szCmd = pCmd->GetName();
  278. szCmd;
  279. }
  280. #endif
  281. }
  282. //
  283. //
  284. //
  285. int CMatchTitle::GetEventDebugID( void )
  286. {
  287. return EVENT_DEBUG_ID_INIT;
  288. }
  289. void CMatchTitle::FireGameEvent( IGameEvent *pIGameEvent )
  290. {
  291. // Check if the current match session is on an active game server
  292. IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
  293. if ( !pMatchSession )
  294. return;
  295. KeyValues *pSessionSettings = pMatchSession->GetSessionSettings();
  296. char const *szGameServer = pSessionSettings->GetString( "server/server", "" );
  297. char const *szSystemLock = pSessionSettings->GetString( "system/lock", "" );
  298. if ( ( !szGameServer || !*szGameServer ) &&
  299. ( !szSystemLock || !*szSystemLock ) )
  300. return;
  301. char const *szGameMode = pSessionSettings->GetString( "game/mode", "coop" );
  302. szGameMode;
  303. // Also don't run on the client when there's a host
  304. char const *szSessionType = pMatchSession->GetSessionSystemData()->GetString( "type", NULL );
  305. if ( szSessionType && !Q_stricmp( szSessionType, "client" ) )
  306. return;
  307. // Parse the game event
  308. char const *szGameEvent = pIGameEvent->GetName();
  309. if ( !szGameEvent || !*szGameEvent )
  310. return;
  311. if ( !Q_stricmp( "round_start", szGameEvent ) )
  312. {
  313. pMatchSession->UpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString(
  314. "update",
  315. " update { "
  316. " game { "
  317. " state game "
  318. " } "
  319. " } "
  320. ) ) );
  321. }
  322. else if ( !Q_stricmp( "round_end", szGameEvent ) )
  323. {
  324. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
  325. "OnProfilesWriteOpportunity", "reason", "checkpoint"
  326. ) );
  327. }
  328. else if ( !Q_stricmp( "finale_start", szGameEvent ) )
  329. {
  330. pMatchSession->UpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString(
  331. "update",
  332. " update { "
  333. " game { "
  334. " state finale "
  335. " } "
  336. " } "
  337. ) ) );
  338. }
  339. else if ( !Q_stricmp( "game_newmap", szGameEvent ) )
  340. {
  341. const char *szMapName = pIGameEvent->GetString( "mapname", "" );
  342. KeyValues *kvUpdate = KeyValues::FromString(
  343. "update",
  344. " update { "
  345. " game { "
  346. " state game "
  347. " } "
  348. " } "
  349. );
  350. KeyValues::AutoDelete autodelete( kvUpdate );
  351. KeyValues *pInfoMission = NULL;
  352. KeyValues *pInfoChapter = g_pMatchExtSwarm->GetMapInfoByBspName( pSessionSettings, szMapName, &pInfoMission );
  353. Assert( pInfoChapter && pInfoMission );
  354. if ( pInfoChapter && pInfoMission )
  355. {
  356. kvUpdate->SetString( "update/game/campaign", pInfoMission->GetString( "name" ) );
  357. kvUpdate->SetInt( "update/game/chapter", pInfoChapter->GetInt( "chapter" ) );
  358. }
  359. pMatchSession->UpdateSessionSettings( kvUpdate );
  360. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
  361. "OnProfilesWriteOpportunity", "reason", "checkpoint"
  362. ) );
  363. }
  364. else if ( !Q_stricmp( "difficulty_changed", szGameEvent ) )
  365. {
  366. char const *szDifficulty = pIGameEvent->GetString( "strDifficulty", "normal" );
  367. KeyValues *kvUpdate = KeyValues::FromString(
  368. "update",
  369. " update { "
  370. " game { "
  371. " difficulty = "
  372. " } "
  373. " } "
  374. );
  375. KeyValues::AutoDelete autodelete( kvUpdate );
  376. kvUpdate->SetString( "update/game/difficulty", szDifficulty );
  377. pMatchSession->UpdateSessionSettings( kvUpdate );
  378. }
  379. else if ( !Q_stricmp( "server_pre_shutdown", szGameEvent ) )
  380. {
  381. char const *szReason = pIGameEvent->GetString( "reason", "quit" );
  382. if ( !Q_stricmp( szReason, "quit" ) )
  383. {
  384. DevMsg( "Received server_pre_shutdown notification - server is shutting down...\n" );
  385. // Transform the server shutdown event into game end event
  386. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
  387. "OnEngineDisconnectReason", "reason", "Server shutting down"
  388. ) );
  389. }
  390. }
  391. }