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.

468 lines
16 KiB

  1. //===== Copyright � 1996-2009, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "mm_framework.h"
  7. #include "filesystem.h"
  8. // memdbgon must be the last include file in a .cpp file!!!
  9. #include "tier0/memdbgon.h"
  10. #ifdef _X360
  11. //-----------------------------------------------------------------------------
  12. // Purpose: Adjust our rate based on our quality of service
  13. //-----------------------------------------------------------------------------
  14. static ConVar mm_clientrateupdate_enabled( "mm_clientrateupdate_enabled", "1", 0, "Automatically update the client rate based on Xbox LIVE QoS" );
  15. static ConVar mm_clientrateupdate_adjust( "mm_clientrateupdate_adjust", "0.6", 0, "Downstream rate adjustment" );
  16. static ConVar mm_clientrateupdate_minimum( "mm_clientrateupdate_minimum", "20000", 0, "Minimum supported rate, Xbox TCR requires 40kbps" );
  17. static ConVar mm_clientrateupdate_maximum( "mm_clientrateupdate_maximum", "30000", 0, "Maximum supported rate" );
  18. static ConVar mm_clientrateupdate_qos_timeout( "mm_clientrateupdate_qos_timeout", "20", 0, "How long to wait for QOS to be determined" );
  19. static void AdjustClientRateBasedOnQoS( DWORD dwDnBitsPerSec )
  20. {
  21. if ( !mm_clientrateupdate_enabled.GetBool() )
  22. return;
  23. static ConVarRef cl_rate( "rate" );
  24. int desiredRate = (int)( ( dwDnBitsPerSec / 8.0f ) * mm_clientrateupdate_adjust.GetFloat() );
  25. desiredRate = clamp( desiredRate, mm_clientrateupdate_minimum.GetInt(), mm_clientrateupdate_maximum.GetInt() );
  26. // Update the client rate
  27. ConColorMsg( Color( 255, 0, 255, 255 ), "[QoS] Bandwidth %d bps, Updating client rate to %d\n", dwDnBitsPerSec, desiredRate );
  28. cl_rate.SetValue( desiredRate );
  29. }
  30. struct RateAdjustmentAsyncCall
  31. {
  32. // X360 peer
  33. XNADDR apxna;
  34. XNADDR const *papxna;
  35. XNKID apxnkid;
  36. XNKID const *papxnkid;
  37. XNKEY apxnkey;
  38. XNKEY const *papxnkey;
  39. // XLSP server
  40. IN_ADDR ina;
  41. DWORD dwServiceId;
  42. // QOS handle
  43. XNQOS *pQOS;
  44. // Time when QOS probe started
  45. float flTimeStarted;
  46. }
  47. *g_pRateAdjustmentAsyncCall = NULL;
  48. void MatchSession_RateAdjustmentUpdate_Release()
  49. {
  50. if ( !g_pRateAdjustmentAsyncCall )
  51. return;
  52. if ( g_pRateAdjustmentAsyncCall->pQOS )
  53. g_pMatchExtensions->GetIXOnline()->XNetQosRelease( g_pRateAdjustmentAsyncCall->pQOS );
  54. delete g_pRateAdjustmentAsyncCall;
  55. g_pRateAdjustmentAsyncCall = NULL;
  56. }
  57. // Keeps adjusting client side rate setting based on QOS with server
  58. void MatchSession_RateAdjustmentUpdate()
  59. {
  60. if ( !g_pRateAdjustmentAsyncCall )
  61. return;
  62. if ( g_pRateAdjustmentAsyncCall->pQOS->cxnqosPending &&
  63. Plat_FloatTime() < g_pRateAdjustmentAsyncCall->flTimeStarted + mm_clientrateupdate_qos_timeout.GetFloat() )
  64. return;
  65. ConColorMsg( Color( 255, 0, 255, 255 ), "[QoS] Rate adjustment query %s\n", g_pRateAdjustmentAsyncCall->pQOS->cxnqosPending ? "timed out" : "completed" );
  66. // QOS finished or timed out
  67. XNQOSINFO &xni = g_pRateAdjustmentAsyncCall->pQOS->axnqosinfo[0];
  68. AdjustClientRateBasedOnQoS( xni.dwDnBitsPerSec );
  69. MatchSession_RateAdjustmentUpdate_Release();
  70. }
  71. void MatchSession_RateAdjustmentUpdate_Start( IN_ADDR const &ina )
  72. {
  73. MatchSession_RateAdjustmentUpdate_Release();
  74. g_pRateAdjustmentAsyncCall = new RateAdjustmentAsyncCall;
  75. ZeroMemory( g_pRateAdjustmentAsyncCall, sizeof( *g_pRateAdjustmentAsyncCall ) );
  76. g_pRateAdjustmentAsyncCall->ina = ina;
  77. g_pRateAdjustmentAsyncCall->dwServiceId = g_pMatchFramework->GetMatchTitle()->GetTitleServiceID();
  78. g_pRateAdjustmentAsyncCall->flTimeStarted = Plat_FloatTime();
  79. ConColorMsg( Color( 255, 0, 255, 255 ), "[QoS] Rate adjustment query scheduled for XLSP server: %08X\n", ina.s_addr );
  80. INT ret = g_pMatchExtensions->GetIXOnline()->XNetQosLookup(
  81. 0, NULL, NULL, NULL,
  82. 1, &g_pRateAdjustmentAsyncCall->ina, &g_pRateAdjustmentAsyncCall->dwServiceId,
  83. 2, 0, 0, NULL, &g_pRateAdjustmentAsyncCall->pQOS );
  84. if ( ret != ERROR_SUCCESS )
  85. {
  86. g_pRateAdjustmentAsyncCall->flTimeStarted = 0.0f;
  87. }
  88. }
  89. void MatchSession_RateAdjustmentUpdate_Start( XSESSION_INFO const &xsi )
  90. {
  91. MatchSession_RateAdjustmentUpdate_Release();
  92. g_pRateAdjustmentAsyncCall = new RateAdjustmentAsyncCall;
  93. ZeroMemory( g_pRateAdjustmentAsyncCall, sizeof( *g_pRateAdjustmentAsyncCall ) );
  94. g_pRateAdjustmentAsyncCall->apxna = xsi.hostAddress;
  95. g_pRateAdjustmentAsyncCall->papxna = &g_pRateAdjustmentAsyncCall->apxna;
  96. g_pRateAdjustmentAsyncCall->apxnkid = xsi.sessionID;
  97. g_pRateAdjustmentAsyncCall->papxnkid = &g_pRateAdjustmentAsyncCall->apxnkid;
  98. g_pRateAdjustmentAsyncCall->apxnkey = xsi.keyExchangeKey;
  99. g_pRateAdjustmentAsyncCall->papxnkey = &g_pRateAdjustmentAsyncCall->apxnkey;
  100. g_pRateAdjustmentAsyncCall->flTimeStarted = Plat_FloatTime();
  101. ConColorMsg( Color( 255, 0, 255, 255 ), "[QoS] Rate adjustment query scheduled for Xbox 360 peer: %08X/%08X\n", xsi.hostAddress.ina.s_addr, xsi.hostAddress.inaOnline.s_addr );
  102. INT ret = g_pMatchExtensions->GetIXOnline()->XNetQosLookup(
  103. 1, &g_pRateAdjustmentAsyncCall->papxna, &g_pRateAdjustmentAsyncCall->papxnkid, &g_pRateAdjustmentAsyncCall->papxnkey,
  104. 0, NULL, NULL,
  105. 2, 0, 0, NULL, &g_pRateAdjustmentAsyncCall->pQOS );
  106. if ( ret != ERROR_SUCCESS )
  107. {
  108. g_pRateAdjustmentAsyncCall->flTimeStarted = 0.0f;
  109. }
  110. }
  111. #endif
  112. void MatchSession_BroadcastSessionSettingsUpdate( KeyValues *pUpdateDeletePackage )
  113. {
  114. KeyValues *notify = new KeyValues( "OnMatchSessionUpdate" );
  115. notify->SetString( "state", "updated" );
  116. if ( KeyValues *kvUpdate = pUpdateDeletePackage->FindKey( "update" ) )
  117. notify->AddSubKey( kvUpdate->MakeCopy() );
  118. if ( KeyValues *kvDelete = pUpdateDeletePackage->FindKey( "delete" ) )
  119. notify->AddSubKey( kvDelete->MakeCopy() );
  120. g_pMatchEventsSubscription->BroadcastEvent( notify );
  121. }
  122. ConVar cl_session( "cl_session", "", FCVAR_USERINFO | FCVAR_HIDDEN | FCVAR_SERVER_CAN_EXECUTE | FCVAR_DEVELOPMENTONLY );
  123. void MatchSession_PrepareClientForConnect( KeyValues *pSettings, uint64 uiReservationCookieOverride )
  124. {
  125. char chSession[64];
  126. sprintf( chSession, "$%llx", uiReservationCookieOverride ? uiReservationCookieOverride :
  127. g_pMatchFramework->GetMatchSession()->GetSessionSystemData()->
  128. GetUint64( "xuidReserve", 0ull ) );
  129. cl_session.SetValue( chSession );
  130. g_pMatchFramework->GetMatchTitle()->PrepareClientForConnect( pSettings );
  131. }
  132. static bool MatchSession_ResolveServerInfo_Helper_DsResult( KeyValues *pSettings, CSysSessionBase *pSysSession,
  133. MatchSessionServerInfo_t &info, uint uiResolveFlags, uint64 ullCrypt )
  134. {
  135. #ifdef _X360
  136. // On dedicated servers host should have given us an insecure
  137. // address representing our Title Server
  138. char const *szInsecureServerAddr = pSettings->GetString( "server/adrInsecure" );
  139. netadr_t inetInsecure;
  140. inetInsecure.SetFromString( szInsecureServerAddr );
  141. IN_ADDR inaddrInsecure;
  142. inaddrInsecure.s_addr = inetInsecure.GetIPNetworkByteOrder();
  143. if ( ( uiResolveFlags & ( info.RESOLVE_DSRESULT | info.RESOLVE_QOS_RATE_PROBE ) ) == info.RESOLVE_QOS_RATE_PROBE )
  144. {
  145. // We are not required to resolve the DSRESULT, just submit the QOS rate probe
  146. MatchSession_RateAdjustmentUpdate_Start( inaddrInsecure );
  147. return true;
  148. }
  149. char const *szServerType = pSettings->GetString( "server/server", "listen" );
  150. if ( !Q_stricmp( szServerType, "listen" ) )
  151. {
  152. info.m_dsResult.m_bDedicated = false;
  153. return true;
  154. }
  155. if ( !Q_stricmp( szServerType, "externalpeer" ) )
  156. {
  157. info.m_dsResult.m_bDedicated = false;
  158. return true;
  159. }
  160. Q_strncpy( info.m_dsResult.m_szInsecureSendableServerAddress,
  161. szInsecureServerAddr,
  162. ARRAYSIZE( info.m_dsResult.m_szInsecureSendableServerAddress ) );
  163. // Map it to a secure address
  164. IN_ADDR inaddrSecure;
  165. DWORD ret = ERROR_FUNCTION_FAILED;
  166. if ( CommandLine()->FindParm( "-xlsp_fake_gateway" ) )
  167. {
  168. inaddrSecure = inaddrInsecure;
  169. ret = ERROR_SUCCESS;
  170. }
  171. else
  172. {
  173. ret = g_pMatchExtensions->GetIXOnline()->XNetServerToInAddr( inaddrInsecure, g_pMatchFramework->GetMatchTitle()->GetTitleServiceID(), &inaddrSecure );
  174. }
  175. if ( ret != ERROR_SUCCESS )
  176. {
  177. DevWarning( "Failed to resolve XLSP secure address (code = 0x%08X, insecure = %s/%s)!\n",
  178. ret, inetInsecure.ToString(), szInsecureServerAddr );
  179. return false;
  180. }
  181. else
  182. {
  183. netadr_t inetSecure = inetInsecure;
  184. inetSecure.SetIP( inaddrSecure.s_addr );
  185. DevMsg( "Resolved XLSP secure address %s, insecure address was %s.\n",
  186. inetSecure.ToString(), szInsecureServerAddr );
  187. Q_strncpy( info.m_dsResult.m_szConnectionString,
  188. inetSecure.ToString(),
  189. ARRAYSIZE( info.m_dsResult.m_szConnectionString ) );
  190. info.m_dsResult.m_bDedicated = true;
  191. // Start QOS rate calculation for the dedicated XLSP server
  192. MatchSession_RateAdjustmentUpdate_Start( inaddrInsecure );
  193. }
  194. #elif !defined( NO_STEAM )
  195. char const *szAddress = pSettings->GetString( "server/adronline", "0.0.0.0" );
  196. if ( char const *szDecrypted = MatchSession_DecryptAddressString( szAddress, ullCrypt ) )
  197. szAddress = szDecrypted;
  198. Q_strncpy( info.m_dsResult.m_szPublicConnectionString, szAddress,
  199. ARRAYSIZE( info.m_dsResult.m_szPublicConnectionString ) );
  200. szAddress = pSettings->GetString( "server/adrlocal", "0.0.0.0" );
  201. if ( char const *szDecrypted = MatchSession_DecryptAddressString( szAddress, ullCrypt ) )
  202. szAddress = szDecrypted;
  203. Q_strncpy( info.m_dsResult.m_szPrivateConnectionString, szAddress,
  204. ARRAYSIZE( info.m_dsResult.m_szPrivateConnectionString ) );
  205. #endif
  206. return true;
  207. }
  208. static bool MatchSession_ResolveServerInfo_Helper_ConnectString( KeyValues *pSettings, CSysSessionBase *pSysSession, MatchSessionServerInfo_t &info, uint uiResolveFlags )
  209. {
  210. //
  211. // Prepare the connect command
  212. //
  213. #ifdef _X360
  214. char const *szServerType = pSettings->GetString( "server/server", "listen" );
  215. if ( !Q_stricmp( "externalpeer", szServerType ) && !( uiResolveFlags & info.RESOLVE_ALLOW_EXTPEER ) )
  216. pSysSession = NULL;
  217. char const *szConnectionString = info.m_dsResult.m_szConnectionString;
  218. if ( info.m_dsResult.m_bDedicated )
  219. {
  220. info.m_szSecureServerAddress = info.m_dsResult.m_szConnectionString;
  221. }
  222. else if ( CSysSessionClient *pSysSessionClient = dynamic_cast< CSysSessionClient * >( pSysSession ) )
  223. {
  224. XSESSION_INFO xsi = {0};
  225. szConnectionString = pSysSessionClient->GetHostNetworkAddress( xsi );
  226. if ( !szConnectionString )
  227. {
  228. DevWarning( "MatchSession_ResolveServerInfo_Helper_ConnectString::GetHostNetworkAddress failed!\n" );
  229. return false;
  230. }
  231. // Start QOS rate calculation for our session host X360 xnaddr
  232. MatchSession_RateAdjustmentUpdate_Start( xsi );
  233. }
  234. else if ( char const *szSessionInfo = pSettings->GetString( "server/sessioninfo", NULL ) )
  235. {
  236. // We don't have a dedicated server and don't allow to use external peer directly,
  237. // register security keys
  238. XSESSION_INFO xsi = {0};
  239. MMX360_SessionInfoFromString( xsi, szSessionInfo );
  240. // Resolve XNADDR
  241. IN_ADDR inaddrRemote;
  242. g_pMatchExtensions->GetIXOnline()->XNetRegisterKey( &xsi.sessionID, &xsi.keyExchangeKey );
  243. if ( int err = g_pMatchExtensions->GetIXOnline()->XNetXnAddrToInAddr( &xsi.hostAddress, &xsi.sessionID, &inaddrRemote ) )
  244. {
  245. DevWarning( "MatchSession_ResolveServerInfo_Helper_ConnectString::XNetXnAddrToInAddr"
  246. " failed to resolve XNADDR ( code 0x%08X, sessioninfo = %s )\n",
  247. err, szSessionInfo );
  248. g_pMatchExtensions->GetIXOnline()->XNetUnregisterKey( &xsi.sessionID );
  249. return false;
  250. }
  251. // Initiate secure connection and key exchange
  252. if ( int err = g_pMatchExtensions->GetIXOnline()->XNetConnect( inaddrRemote ) )
  253. {
  254. DevWarning( "MatchSession_ResolveServerInfo_Helper_ConnectString::XNetConnect"
  255. " failed to start key exchange ( code 0x%08X, sessioninfo = %s )\n",
  256. err, szSessionInfo );
  257. // Secure IN_ADDR associations are removed implicitly when their key gets unregistered
  258. g_pMatchExtensions->GetIXOnline()->XNetUnregisterKey( &xsi.sessionID );
  259. return false;
  260. }
  261. //
  262. // Prepare connection string
  263. //
  264. netadr_t inetAddr;
  265. inetAddr.SetType( NA_IP );
  266. inetAddr.SetIPAndPort( inaddrRemote.s_addr, 0 );
  267. // Now we know the address for the game to connect
  268. Q_strncpy( info.m_dsResult.m_szConnectionString, inetAddr.ToString( true ), ARRAYSIZE( info.m_dsResult.m_szConnectionString ) );
  269. //
  270. // Remember all the settings needed to deallocate the secure association
  271. //
  272. info.m_szSecureServerAddress = info.m_dsResult.m_szInsecureSendableServerAddress;
  273. Q_snprintf( info.m_dsResult.m_szInsecureSendableServerAddress,
  274. ARRAYSIZE( info.m_dsResult.m_szInsecureSendableServerAddress ),
  275. "SESSIONINFO %s", szSessionInfo );
  276. // Start QOS rate calculation for opponents session host X360 remote xnaddr
  277. MatchSession_RateAdjustmentUpdate_Start( xsi );
  278. }
  279. else
  280. return false;
  281. Q_snprintf( info.m_szConnectCmd, sizeof( info.m_szConnectCmd ),
  282. "connect_splitscreen %s %s %d\n",
  283. szConnectionString,
  284. szConnectionString,
  285. XBX_GetNumGameUsers() );
  286. #elif !defined( NO_STEAM )
  287. Q_snprintf( info.m_szConnectCmd, sizeof( info.m_szConnectCmd ),
  288. "connect %s %s\n",
  289. info.m_dsResult.m_szPublicConnectionString,
  290. info.m_dsResult.m_szPrivateConnectionString );
  291. #endif
  292. info.m_xuidJingle = pSettings->GetUint64( "server/xuid", 0ull );
  293. if ( uint64 uiReservationCookieOverride = pSettings->GetUint64( "server/reservationid", 0ull ) )
  294. info.m_uiReservationCookie = uiReservationCookieOverride;
  295. else if ( pSysSession )
  296. info.m_uiReservationCookie = pSysSession->GetReservationCookie();
  297. else
  298. info.m_uiReservationCookie = 0ull;
  299. return true;
  300. }
  301. bool MatchSession_ResolveServerInfo( KeyValues *pSettings, CSysSessionBase *pSysSession, MatchSessionServerInfo_t &info, uint uiResolveFlags, uint64 ullCrypt )
  302. {
  303. if ( ( uiResolveFlags & ( info.RESOLVE_DSRESULT | info.RESOLVE_QOS_RATE_PROBE ) ) &&
  304. !MatchSession_ResolveServerInfo_Helper_DsResult( pSettings, pSysSession, info, uiResolveFlags, ullCrypt ) )
  305. return false;
  306. if ( ( uiResolveFlags & info.RESOLVE_CONNECTSTRING ) &&
  307. !MatchSession_ResolveServerInfo_Helper_ConnectString( pSettings, pSysSession, info, uiResolveFlags ) )
  308. return false;
  309. return true;
  310. }
  311. ConVar mm_tu_string( "mm_tu_string", "00000000" );
  312. uint64 MatchSession_GetMachineFlags()
  313. {
  314. uint64 uiFlags = 0;
  315. if ( IsPS3() )
  316. uiFlags |= MACHINE_PLATFORM_PS3;
  317. return uiFlags;
  318. }
  319. char const * MatchSession_GetTuInstalledString()
  320. {
  321. return mm_tu_string.GetString();
  322. }
  323. char const * MatchSession_EncryptAddressString( char const *szAddress, uint64 ullCrypt )
  324. {
  325. if ( !szAddress || !*szAddress )
  326. return NULL;
  327. if ( !ullCrypt )
  328. return NULL;
  329. if ( szAddress[0] == ':' )
  330. return NULL;
  331. if ( szAddress[ 0 ] == '$' )
  332. return NULL;
  333. static unsigned char s_chData[256];
  334. int nLen = Q_strlen( szAddress );
  335. if ( nLen >= ARRAYSIZE( s_chData )/2 - 1 )
  336. return NULL;
  337. // Copy the address
  338. s_chData[0] = '$';
  339. for ( int j = 0; j < nLen; ++ j )
  340. {
  341. uint8 uiVal = uint8( szAddress[j] ) ^ uint8( reinterpret_cast< uint8 * >(&ullCrypt)[ j % sizeof( uint64 ) ] );
  342. Q_snprintf( (char*)( s_chData + 1 + 2*j ), 3, "%02X", ( uint32 ) uiVal );
  343. }
  344. return (char*) s_chData;
  345. }
  346. char const * MatchSession_DecryptAddressString( char const *szAddress, uint64 ullCrypt )
  347. {
  348. if ( !szAddress || !*szAddress )
  349. return NULL;
  350. if ( !ullCrypt )
  351. return NULL;
  352. if ( szAddress[ 0 ] != '$' )
  353. return NULL;
  354. static unsigned char s_chData[ 256 ];
  355. int nLen = Q_strlen( szAddress );
  356. if ( nLen*2 + 2 >= ARRAYSIZE( s_chData ) )
  357. return NULL;
  358. // Copy the address
  359. for ( int j = 0; j < nLen/2; ++j )
  360. {
  361. uint32 uiVal;
  362. if ( !sscanf( szAddress + 1 + 2*j, "%02X", &uiVal ) )
  363. return NULL;
  364. if ( uiVal > 0xFF )
  365. return NULL;
  366. uiVal = uint8( uiVal ) ^ uint8( reinterpret_cast< uint8 * >(&ullCrypt)[ j % sizeof( uint64 ) ] );
  367. if ( !uiVal )
  368. return NULL;
  369. s_chData[j] = uiVal;
  370. }
  371. s_chData[nLen/2] = 0;
  372. return (char*) s_chData;
  373. }
  374. CON_COMMAND( mm_debugprint, "Show debug information about current matchmaking session" )
  375. {
  376. if ( IMatchSession *pIMatchSession = g_pMMF->GetMatchSession() )
  377. {
  378. ( ( IMatchSessionInternal * ) pIMatchSession )->DebugPrint();
  379. }
  380. else
  381. {
  382. DevMsg( "No match session.\n" );
  383. }
  384. }