Team Fortress 2 Source Code as on 22/4/2020
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.

269 lines
9.9 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "tf_gc_api.h"
  9. #include <checksum_md5.h>
  10. #include "econ_game_account_server.h"
  11. #include "econ_gcmessages.h"
  12. #include "tf_gcmessages.h"
  13. #include "tf_gamerules.h"
  14. #include "gc_clientsystem.h"
  15. //-----------------------------------------------------------------------------
  16. static ConVar tf_server_identity_token( "tf_server_identity_token", "", FCVAR_ARCHIVE | FCVAR_PROTECTED, "Server identity token, used to authenticate with the TF2 Game Coordinator." );
  17. static ConVar tf_server_identity_account_id( "tf_server_identity_account_id", "0", FCVAR_ARCHIVE, "Server identity account id, used to authenticate with the TF2 Game Coordinator." );
  18. static ConVar tf_server_identity_disable_quickplay( "tf_server_identity_disable_quickplay", "0", FCVAR_ARCHIVE | FCVAR_NOTIFY, "Disable this server from being chosen by the quickplay matchmaking." );
  19. static ConVar sv_registration_successful( "sv_registration_successful", "0", FCVAR_DONTRECORD | FCVAR_HIDDEN | FCVAR_NOTIFY, "Nonzero if we were able to login OK" );
  20. static ConVar sv_registration_message( "sv_registration_message", "No account specified", FCVAR_DONTRECORD | FCVAR_HIDDEN | FCVAR_NOTIFY, "Error message of other status text" );
  21. static bool BSendMessage( const GCSDK::CProtoBufMsgBase& msg )
  22. {
  23. return GCClientSystem()->BSendMessage( msg );
  24. }
  25. //-----------------------------------------------------------------------------
  26. // Public API
  27. //-----------------------------------------------------------------------------
  28. void GameCoordinator_NotifyGameState()
  29. {
  30. if ( TFGameRules() )
  31. {
  32. GCSDK::CProtoBufMsg< CMsgGC_GameServer_LevelInfo > msg( k_EMsgGC_GameServer_LevelInfo );
  33. msg.Body().set_level_loaded( true );
  34. msg.Body().set_level_name( gpGlobals->mapname.ToCStr() );
  35. BSendMessage( msg );
  36. }
  37. else
  38. {
  39. GameCoordinator_NotifyLevelShutdown();
  40. }
  41. }
  42. //-----------------------------------------------------------------------------
  43. void GameCoordinator_NotifyLevelShutdown()
  44. {
  45. GCSDK::CProtoBufMsg< CMsgGC_GameServer_LevelInfo> msg( k_EMsgGC_GameServer_LevelInfo );
  46. msg.Body().set_level_loaded( false );
  47. BSendMessage( msg );
  48. }
  49. //-----------------------------------------------------------------------------
  50. const char *GameCoordinator_GetRegistrationString()
  51. {
  52. return sv_registration_message.GetString();
  53. }
  54. //-----------------------------------------------------------------------------
  55. /**
  56. * Game Coordinator wants to know if we have a token
  57. */
  58. class CGC_GameServer_AuthChallenge : public GCSDK::CGCClientJob
  59. {
  60. public:
  61. CGC_GameServer_AuthChallenge( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
  62. virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  63. {
  64. GCSDK::CProtoBufMsg< CMsgGC_GameServer_AuthChallenge > msg( pNetPacket );
  65. // send information on what level is loaded
  66. GameCoordinator_NotifyGameState();
  67. const uint32 unGameServerAccountID = tf_server_identity_account_id.GetInt();
  68. if ( unGameServerAccountID == 0 )
  69. {
  70. Msg( "%s not set; not logging into registered account\n", tf_server_identity_account_id.GetName() );
  71. UTIL_LogPrintf( "%s not set; not logging into registered account\n", tf_server_identity_account_id.GetName() );
  72. return true;
  73. }
  74. CUtlString challenge = msg.Body().challenge_string().c_str();
  75. if ( challenge.IsEmpty() )
  76. {
  77. Warning( "Received CGC_GameServer_AuthChallenge with invalid challenge from GC!\n" );
  78. UTIL_LogPrintf( "Received CGC_GameServer_AuthChallenge with invalid challenge from GC!\n" );
  79. Assert( false );
  80. return true;
  81. }
  82. char szKeyBuffer[16];
  83. int nKeyLength = Q_snprintf( szKeyBuffer, sizeof( szKeyBuffer ), "%s", tf_server_identity_token.GetString() );
  84. if ( nKeyLength <= 0 || nKeyLength >= ARRAYSIZE( szKeyBuffer ) )
  85. {
  86. Warning( "%s is not valid, or not set! Not signing into gameserver account\n", tf_server_identity_token.GetName() );
  87. UTIL_LogPrintf( "%s is not valid, or not set! Not signing into gameserver account\n", tf_server_identity_token.GetName() );
  88. return true;
  89. }
  90. MD5Context_t ctx;
  91. unsigned char digest[16]; // The MD5 Hash
  92. memset( &ctx, 0, sizeof( ctx ) );
  93. memset( digest, 0, sizeof( digest ) );
  94. // hash together the identity token and the challenge
  95. MD5Init( &ctx );
  96. MD5Update( &ctx, (unsigned char*)szKeyBuffer, nKeyLength );
  97. MD5Update( &ctx, (unsigned char*)challenge.Get(), challenge.Length() );
  98. MD5Final( digest, &ctx );
  99. GCSDK::CProtoBufMsg< CMsgGC_GameServer_AuthChallengeResponse > msg_response( k_EMsgGC_GameServer_AuthChallengeResponse );
  100. msg_response.Body().set_game_server_account_id( unGameServerAccountID );
  101. msg_response.Body().set_hashed_challenge_string( digest, sizeof( digest ) );
  102. BSendMessage( msg_response );
  103. Msg( "Received auth challenge; signing into gameserver account...\n" );
  104. UTIL_LogPrintf( "Received auth challenge; signing into gameserver account...\n" );
  105. return true;
  106. }
  107. };
  108. GC_REG_JOB( GCSDK::CGCClient, CGC_GameServer_AuthChallenge, "CGC_GameServer_AuthChallenge", k_EMsgGC_GameServer_AuthChallenge, GCSDK::k_EServerTypeGCClient );
  109. /**
  110. * Game Coordinator tells game server whether authentication was successful or not
  111. */
  112. class CGC_GameServer_AuthResult : public GCSDK::CGCClientJob
  113. {
  114. public:
  115. CGC_GameServer_AuthResult( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
  116. virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  117. {
  118. GCSDK::CProtoBufMsg< CMsgGC_GameServer_AuthResult > msg( pNetPacket );
  119. if ( msg.Body().is_valve_server() )
  120. {
  121. // Note: I put in this cryptic message, in the hopes that if a server operator
  122. // actually sees it (when they are not supposed to), they will contact us,
  123. // as opposed to just secretly enjoying the benefits of their server being
  124. // treated as a valve server and us not knowing something is wrong.
  125. Msg( "WARNING: Game server status 'Gordon'.\n" );
  126. engine->LogPrint( "WARNING: Game server status 'Gordon'.\n" );
  127. }
  128. if ( msg.Body().authenticated() )
  129. {
  130. const char *pStanding = GameServerAccount_GetStandingString( (eGameServerScoreStanding)msg.Body().game_server_standing() );
  131. const char *pStandingTrend = GameServerAccount_GetStandingTrendString( (eGameServerScoreStandingTrend)msg.Body().game_server_standing_trend() );
  132. Msg( "Game server authentication: SUCCESS! Standing: %s. Trend: %s\n", pStanding, pStandingTrend );
  133. UTIL_LogPrintf( "Game server authentication: SUCCESS! Standing: %s. Trend: %s\n", pStanding, pStandingTrend );
  134. if ( !msg.Body().message().empty() )
  135. {
  136. Msg( " %s\n", msg.Body().message().c_str() );
  137. UTIL_LogPrintf( " %s\n", msg.Body().message().c_str() );
  138. }
  139. // What else could we save here?
  140. if ( msg.Body().is_valve_server() )
  141. {
  142. sv_registration_message.SetValue( "Status 'Gordon'" );
  143. }
  144. else
  145. {
  146. sv_registration_message.SetValue( "" );
  147. }
  148. }
  149. else
  150. {
  151. Warning( "Game server authentication: FAILURE!\n" );
  152. UTIL_LogPrintf( "Game server authentication: FAILURE!\n" );
  153. if ( !msg.Body().message().empty() )
  154. {
  155. Warning( " %s\n", msg.Body().message().c_str() );
  156. UTIL_LogPrintf( " %s\n", msg.Body().message().c_str() );
  157. sv_registration_message.SetValue( msg.Body().message().c_str() );
  158. }
  159. else
  160. {
  161. sv_registration_message.SetValue( "failed" );
  162. }
  163. }
  164. // Update the convar
  165. sv_registration_successful.SetValue( msg.Body().authenticated() );
  166. // !FIXME! How to force server to recalculate tags??
  167. return true;
  168. }
  169. };
  170. GC_REG_JOB( GCSDK::CGCClient, CGC_GameServer_AuthResult, "CGC_GameServer_AuthResult", k_EMsgGC_GameServer_AuthResult, GCSDK::k_EServerTypeGCClient );
  171. //-----------------------------------------------------------------------------
  172. // GC telling us that a player is joining via Quickplay
  173. class CGCTFQuickplay_PlayerJoining : public GCSDK::CGCClientJob
  174. {
  175. public:
  176. CGCTFQuickplay_PlayerJoining( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
  177. virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  178. {
  179. GCSDK::CProtoBufMsg<CMsgTFQuickplay_PlayerJoining> msg( pNetPacket );
  180. Log("(Quickplay) Incoming player (%d):\n", msg.Body().account_id() );
  181. return true;
  182. }
  183. };
  184. GC_REG_JOB( GCSDK::CGCClient, CGCTFQuickplay_PlayerJoining, "CGCTFQuickplay_PlayerJoining", k_EMsgGC_QP_PlayerJoining, GCSDK::k_EServerTypeGCClient );
  185. //-----------------------------------------------------------------------------
  186. // GC telling us that a player has moved an item into his or her backpack for the first time
  187. class CGCTFItemAcknowledged : public GCSDK::CGCClientJob
  188. {
  189. public:
  190. CGCTFItemAcknowledged( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
  191. virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  192. {
  193. GCSDK::CProtoBufMsg<CMsgItemAcknowledged> msg( pNetPacket );
  194. CSteamID steamID;
  195. CTFPlayer *pFoundPlayer = NULL;
  196. uint32 unAccountID = msg.Body().account_id();
  197. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  198. {
  199. CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) );
  200. if ( pPlayer == NULL )
  201. continue;
  202. if ( pPlayer->GetSteamID( &steamID ) == false )
  203. continue;
  204. if ( steamID.GetAccountID() == unAccountID )
  205. {
  206. pFoundPlayer = pPlayer;
  207. break;
  208. }
  209. }
  210. if ( pFoundPlayer )
  211. {
  212. // Crack open some attributes
  213. IGameEvent *event = gameeventmanager->CreateEvent( "item_found" );
  214. if ( event )
  215. {
  216. event->SetInt( "player", pFoundPlayer->entindex() );
  217. event->SetInt( "quality", msg.Body().quality() );
  218. event->SetInt( "method", GetUnacknowledgedReason( msg.Body().inventory() ) - 1 );
  219. event->SetInt( "itemdef", msg.Body().def_index() );
  220. event->SetInt( "isstrange", msg.Body().is_strange() );
  221. event->SetInt( "isunusual", msg.Body().is_unusual() );
  222. event->SetFloat( "wear", msg.Body().wear() );
  223. gameeventmanager->FireEvent( event );
  224. }
  225. }
  226. return true;
  227. }
  228. };
  229. GC_REG_JOB( GCSDK::CGCClient, CGCTFItemAcknowledged, "CGCTFItemAcknowledged", k_EMsgGCItemAcknowledged, GCSDK::k_EServerTypeGCClient );