diff options
Diffstat (limited to 'game/server/tf/tf_gc_api.cpp')
| -rw-r--r-- | game/server/tf/tf_gc_api.cpp | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/game/server/tf/tf_gc_api.cpp b/game/server/tf/tf_gc_api.cpp new file mode 100644 index 0000000..cd347e9 --- /dev/null +++ b/game/server/tf/tf_gc_api.cpp @@ -0,0 +1,269 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +#include "tf_gc_api.h" + +#include <checksum_md5.h> + +#include "econ_game_account_server.h" +#include "econ_gcmessages.h" +#include "tf_gcmessages.h" +#include "tf_gamerules.h" +#include "gc_clientsystem.h" + +//----------------------------------------------------------------------------- + +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." ); +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." ); +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." ); +static ConVar sv_registration_successful( "sv_registration_successful", "0", FCVAR_DONTRECORD | FCVAR_HIDDEN | FCVAR_NOTIFY, "Nonzero if we were able to login OK" ); +static ConVar sv_registration_message( "sv_registration_message", "No account specified", FCVAR_DONTRECORD | FCVAR_HIDDEN | FCVAR_NOTIFY, "Error message of other status text" ); + +static bool BSendMessage( const GCSDK::CProtoBufMsgBase& msg ) +{ + return GCClientSystem()->BSendMessage( msg ); +} + +//----------------------------------------------------------------------------- +// Public API +//----------------------------------------------------------------------------- +void GameCoordinator_NotifyGameState() +{ + if ( TFGameRules() ) + { + GCSDK::CProtoBufMsg< CMsgGC_GameServer_LevelInfo > msg( k_EMsgGC_GameServer_LevelInfo ); + msg.Body().set_level_loaded( true ); + msg.Body().set_level_name( gpGlobals->mapname.ToCStr() ); + BSendMessage( msg ); + } + else + { + GameCoordinator_NotifyLevelShutdown(); + } +} + +//----------------------------------------------------------------------------- +void GameCoordinator_NotifyLevelShutdown() +{ + GCSDK::CProtoBufMsg< CMsgGC_GameServer_LevelInfo> msg( k_EMsgGC_GameServer_LevelInfo ); + msg.Body().set_level_loaded( false ); + BSendMessage( msg ); +} + +//----------------------------------------------------------------------------- +const char *GameCoordinator_GetRegistrationString() +{ + return sv_registration_message.GetString(); +} + + +//----------------------------------------------------------------------------- + +/** + * Game Coordinator wants to know if we have a token + */ +class CGC_GameServer_AuthChallenge : public GCSDK::CGCClientJob +{ +public: + CGC_GameServer_AuthChallenge( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} + virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) + { + GCSDK::CProtoBufMsg< CMsgGC_GameServer_AuthChallenge > msg( pNetPacket ); + + // send information on what level is loaded + GameCoordinator_NotifyGameState(); + + const uint32 unGameServerAccountID = tf_server_identity_account_id.GetInt(); + if ( unGameServerAccountID == 0 ) + { + Msg( "%s not set; not logging into registered account\n", tf_server_identity_account_id.GetName() ); + UTIL_LogPrintf( "%s not set; not logging into registered account\n", tf_server_identity_account_id.GetName() ); + return true; + } + + CUtlString challenge = msg.Body().challenge_string().c_str(); + if ( challenge.IsEmpty() ) + { + Warning( "Received CGC_GameServer_AuthChallenge with invalid challenge from GC!\n" ); + UTIL_LogPrintf( "Received CGC_GameServer_AuthChallenge with invalid challenge from GC!\n" ); + Assert( false ); + return true; + } + + char szKeyBuffer[16]; + int nKeyLength = Q_snprintf( szKeyBuffer, sizeof( szKeyBuffer ), "%s", tf_server_identity_token.GetString() ); + + if ( nKeyLength <= 0 || nKeyLength >= ARRAYSIZE( szKeyBuffer ) ) + { + Warning( "%s is not valid, or not set! Not signing into gameserver account\n", tf_server_identity_token.GetName() ); + UTIL_LogPrintf( "%s is not valid, or not set! Not signing into gameserver account\n", tf_server_identity_token.GetName() ); + return true; + } + + MD5Context_t ctx; + unsigned char digest[16]; // The MD5 Hash + memset( &ctx, 0, sizeof( ctx ) ); + memset( digest, 0, sizeof( digest ) ); + + // hash together the identity token and the challenge + MD5Init( &ctx ); + MD5Update( &ctx, (unsigned char*)szKeyBuffer, nKeyLength ); + MD5Update( &ctx, (unsigned char*)challenge.Get(), challenge.Length() ); + MD5Final( digest, &ctx ); + + GCSDK::CProtoBufMsg< CMsgGC_GameServer_AuthChallengeResponse > msg_response( k_EMsgGC_GameServer_AuthChallengeResponse ); + msg_response.Body().set_game_server_account_id( unGameServerAccountID ); + msg_response.Body().set_hashed_challenge_string( digest, sizeof( digest ) ); + BSendMessage( msg_response ); + + Msg( "Received auth challenge; signing into gameserver account...\n" ); + UTIL_LogPrintf( "Received auth challenge; signing into gameserver account...\n" ); + return true; + } +}; +GC_REG_JOB( GCSDK::CGCClient, CGC_GameServer_AuthChallenge, "CGC_GameServer_AuthChallenge", k_EMsgGC_GameServer_AuthChallenge, GCSDK::k_EServerTypeGCClient ); + +/** + * Game Coordinator tells game server whether authentication was successful or not + */ +class CGC_GameServer_AuthResult : public GCSDK::CGCClientJob +{ +public: + CGC_GameServer_AuthResult( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} + virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) + { + GCSDK::CProtoBufMsg< CMsgGC_GameServer_AuthResult > msg( pNetPacket ); + + if ( msg.Body().is_valve_server() ) + { + // Note: I put in this cryptic message, in the hopes that if a server operator + // actually sees it (when they are not supposed to), they will contact us, + // as opposed to just secretly enjoying the benefits of their server being + // treated as a valve server and us not knowing something is wrong. + Msg( "WARNING: Game server status 'Gordon'.\n" ); + engine->LogPrint( "WARNING: Game server status 'Gordon'.\n" ); + } + if ( msg.Body().authenticated() ) + { + const char *pStanding = GameServerAccount_GetStandingString( (eGameServerScoreStanding)msg.Body().game_server_standing() ); + const char *pStandingTrend = GameServerAccount_GetStandingTrendString( (eGameServerScoreStandingTrend)msg.Body().game_server_standing_trend() ); + Msg( "Game server authentication: SUCCESS! Standing: %s. Trend: %s\n", pStanding, pStandingTrend ); + UTIL_LogPrintf( "Game server authentication: SUCCESS! Standing: %s. Trend: %s\n", pStanding, pStandingTrend ); + if ( !msg.Body().message().empty() ) + { + Msg( " %s\n", msg.Body().message().c_str() ); + UTIL_LogPrintf( " %s\n", msg.Body().message().c_str() ); + } + // What else could we save here? + if ( msg.Body().is_valve_server() ) + { + sv_registration_message.SetValue( "Status 'Gordon'" ); + } + else + { + sv_registration_message.SetValue( "" ); + } + } + else + { + Warning( "Game server authentication: FAILURE!\n" ); + UTIL_LogPrintf( "Game server authentication: FAILURE!\n" ); + if ( !msg.Body().message().empty() ) + { + Warning( " %s\n", msg.Body().message().c_str() ); + UTIL_LogPrintf( " %s\n", msg.Body().message().c_str() ); + sv_registration_message.SetValue( msg.Body().message().c_str() ); + } + else + { + sv_registration_message.SetValue( "failed" ); + } + } + + // Update the convar + sv_registration_successful.SetValue( msg.Body().authenticated() ); + + // !FIXME! How to force server to recalculate tags?? + + return true; + } +}; +GC_REG_JOB( GCSDK::CGCClient, CGC_GameServer_AuthResult, "CGC_GameServer_AuthResult", k_EMsgGC_GameServer_AuthResult, GCSDK::k_EServerTypeGCClient ); + +//----------------------------------------------------------------------------- + +// GC telling us that a player is joining via Quickplay +class CGCTFQuickplay_PlayerJoining : public GCSDK::CGCClientJob +{ +public: + CGCTFQuickplay_PlayerJoining( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} + + virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) + { + GCSDK::CProtoBufMsg<CMsgTFQuickplay_PlayerJoining> msg( pNetPacket ); + Log("(Quickplay) Incoming player (%d):\n", msg.Body().account_id() ); + return true; + } +}; +GC_REG_JOB( GCSDK::CGCClient, CGCTFQuickplay_PlayerJoining, "CGCTFQuickplay_PlayerJoining", k_EMsgGC_QP_PlayerJoining, GCSDK::k_EServerTypeGCClient ); + +//----------------------------------------------------------------------------- + +// GC telling us that a player has moved an item into his or her backpack for the first time +class CGCTFItemAcknowledged : public GCSDK::CGCClientJob +{ +public: + CGCTFItemAcknowledged( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {} + + virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket ) + { + GCSDK::CProtoBufMsg<CMsgItemAcknowledged> msg( pNetPacket ); + + CSteamID steamID; + CTFPlayer *pFoundPlayer = NULL; + uint32 unAccountID = msg.Body().account_id(); + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) ); + if ( pPlayer == NULL ) + continue; + + if ( pPlayer->GetSteamID( &steamID ) == false ) + continue; + + if ( steamID.GetAccountID() == unAccountID ) + { + pFoundPlayer = pPlayer; + break; + } + } + + if ( pFoundPlayer ) + { + // Crack open some attributes + + IGameEvent *event = gameeventmanager->CreateEvent( "item_found" ); + if ( event ) + { + event->SetInt( "player", pFoundPlayer->entindex() ); + event->SetInt( "quality", msg.Body().quality() ); + event->SetInt( "method", GetUnacknowledgedReason( msg.Body().inventory() ) - 1 ); + event->SetInt( "itemdef", msg.Body().def_index() ); + event->SetInt( "isstrange", msg.Body().is_strange() ); + event->SetInt( "isunusual", msg.Body().is_unusual() ); + event->SetFloat( "wear", msg.Body().wear() ); + + gameeventmanager->FireEvent( event ); + } + } + + return true; + } +}; +GC_REG_JOB( GCSDK::CGCClient, CGCTFItemAcknowledged, "CGCTFItemAcknowledged", k_EMsgGCItemAcknowledged, GCSDK::k_EServerTypeGCClient ); |