diff options
Diffstat (limited to 'game/shared/gc_clientsystem.cpp')
| -rw-r--r-- | game/shared/gc_clientsystem.cpp | 551 |
1 files changed, 551 insertions, 0 deletions
diff --git a/game/shared/gc_clientsystem.cpp b/game/shared/gc_clientsystem.cpp new file mode 100644 index 0000000..94be67e --- /dev/null +++ b/game/shared/gc_clientsystem.cpp @@ -0,0 +1,551 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// +// +//============================================================================= +#include "cbase.h" +#include "gc_clientsystem.h" +#include "econ_item_system.h" +#include "econ_item_inventory.h" +#include "quest_objective_manager.h" +#ifdef GAME_DLL +#include "tf_wartracker.h" +#endif +//#include "gcsdk/msgprotobuf.h" + +// +// TODO: NO_STEAM support! +// + +using namespace GCSDK; + +// Retry for sending a ClientHello if we don't hear back from the GC. Generally this shouldn't be necessary, as the GC +// should be aware of our session via the GCH and should not need us to pester it. +const float k_flClientHelloRetry = 30.f; + +// Client GC System. +//static CGCClientSystem s_CGCClientSystem; +static CGCClientSystem* s_pCGCGameSpecificClientSystem = NULL; // set this in the game specific derived version if needed +void SetGCClientSystem( CGCClientSystem* pGCClientSystem ) +{ + s_pCGCGameSpecificClientSystem = pGCClientSystem; +} + +CGCClientSystem *GCClientSystem() +{ + AssertMsg( s_pCGCGameSpecificClientSystem, "GCClientSystem is not initialized in the game specific client system constructor" ); + return s_pCGCGameSpecificClientSystem; +} + +#ifdef STAGING_ONLY +CON_COMMAND( dump_cache, "Dump the contents of a user's SOCache" ) +{ + if( args.ArgC() < 2 ) + { + Msg( "Usage: dump_cache <steamID>\n" ); + return; + } + + CSteamID userSteamID( V_atoui64( args[1] ), 1, GetUniverse(), k_EAccountTypeIndividual ); + + CGCClientSharedObjectCache* pSOCache = GCClientSystem()->GetSOCache( userSteamID ); + if ( pSOCache ) + { + pSOCache->Dump(); + } +} +#endif + +#ifdef GAME_DLL +CON_COMMAND( dump_all_caches, "Dump the contents all subsribed SOCaches" ) +{ + GCClientSystem()->GetGCClient()->Dump(); +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: Constructor. +//----------------------------------------------------------------------------- +CGCClientSystem::CGCClientSystem() +: CAutoGameSystemPerFrame( "CGCClientSystem" ) +#ifdef CLIENT_DLL + , m_GCClient( NULL, false ) +#else + , m_GCClient( NULL, true ) + , m_CallbackLogonSuccess( this, &CGCClientSystem::OnLogonSuccess ) +#endif +{ + m_bInittedGC = false; + m_bConnectedToGC = false; + m_bLoggedOn = false; + m_timeLastSendHello = 0.0; +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor. +//----------------------------------------------------------------------------- +CGCClientSystem::~CGCClientSystem() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +GCSDK::CGCClient *CGCClientSystem::GetGCClient() +{ + Assert ( this != NULL ); + return &m_GCClient; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CGCClientSystem::BSendMessage( uint32 unMsgType, const uint8 *pubData, uint32 cubData ) +{ + return m_GCClient.BSendMessage( unMsgType, pubData, cubData ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CGCClientSystem::BSendMessage( const GCSDK::CGCMsgBase& msg ) +{ + return m_GCClient.BSendMessage( msg ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CGCClientSystem::BSendMessage( const GCSDK::CProtoBufMsgBase& msg ) +{ + return m_GCClient.BSendMessage( msg ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +GCSDK::CGCClientSharedObjectCache *CGCClientSystem::GetSOCache( const CSteamID &steamID ) +{ + return m_GCClient.FindSOCache( steamID, false ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +GCSDK::CGCClientSharedObjectCache *CGCClientSystem::FindOrAddSOCache( const CSteamID &steamID ) +{ + return m_GCClient.FindSOCache( steamID, true ); +} + + + +//----------------------------------------------------------------------------- +void CGCClientSystem::PostInit() +{ + // Call into the BaseClass. + CAutoGameSystemPerFrame::PostInit(); + + #ifdef CLIENT_DLL + // Install callback to be notified when our steam logged on status changes. + ClientSteamContext().InstallCallback( UtlMakeDelegate( this, &CGCClientSystem::SteamLoggedOnCallback ) ); + + // Except when debugging internally, we really should never launch the game + // while not logged on! + AssertMsg( ClientSteamContext().BLoggedOn(), "No Steam logged on for GC setup!" ); + + ThinkConnection(); + #endif +} + +//----------------------------------------------------------------------------- +#ifndef CLIENT_DLL +void CGCClientSystem::GameServerActivate() +{ + ThinkConnection(); +} +#endif + + +//----------------------------------------------------------------------------- +#ifdef CLIENT_DLL +void CGCClientSystem::SteamLoggedOnCallback( const SteamLoggedOnChange_t &loggedOnState ) +{ + ThinkConnection(); +} + +#else + +//----------------------------------------------------------------------------- +void CGCClientSystem::OnLogonSuccess( SteamServersConnected_t *pLogonSuccess ) +{ + ThinkConnection(); +} + +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGCClientSystem::LevelInitPreEntity() +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGCClientSystem::LevelShutdownPostEntity() +{ +#ifdef GAME_DLL + QuestObjectiveManager()->Shutdown(); +#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGCClientSystem::Shutdown() +{ + // Shutdown the GC. + m_GCClient.Uninit(); + + // Reset the init flag. + m_bInittedGC = false; + m_bConnectedToGC = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGCClientSystem::SetupGC() +{ + // Pre-Init. + PreInitGC(); + InventoryManager()->PreInitGC(); + + // Init. + InitGC(); + + // Post-Init. + PostInitGC(); + InventoryManager()->PostInitGC(); + QuestObjectiveManager()->Initialize(); + +#ifdef GAME_DLL + GetWarTrackerManager()->Initialize(); +#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGCClientSystem::InitGC() +{ + // Check to see if we have already initialized the GCClient. + if ( m_bInittedGC ) + return; + + // Locate our steam client interface. + #ifdef CLIENT_DLL + ISteamClient *pSteamClient = SteamClient(); + HSteamUser hSteamUser = SteamAPI_GetHSteamUser(); + HSteamPipe hSteamPipe = SteamAPI_GetHSteamPipe(); + #else + ISteamClient *pSteamClient = g_pSteamClientGameServer; + HSteamUser hSteamUser = SteamGameServer_GetHSteamUser(); + HSteamPipe hSteamPipe = SteamGameServer_GetHSteamPipe(); + #endif + if ( pSteamClient == NULL ) + { + Warning( "CGCClientSystem - no ISteamClient interface!\n" ); + Assert( pSteamClient ); + return; + } + + // Get the SteamGameCoordinator and initialize the GCClient + void *pGenericInterface = pSteamClient->GetISteamGenericInterface( hSteamUser, hSteamPipe, STEAMGAMECOORDINATOR_INTERFACE_VERSION ); + + ISteamGameCoordinator *pGameCoordinator = static_cast<ISteamGameCoordinator*>( pGenericInterface ); + if ( pGameCoordinator ) + { + m_GCClient.BInit( pGameCoordinator ); + + // Initialized the GCClient + m_bInittedGC = true; + } +} + + +//----------------------------------------------------------------------------- +#ifdef CLIENT_DLL +void CGCClientSystem::Update( float frametime ) +{ + ThinkConnection(); + if ( m_bInittedGC ) + m_GCClient.BMainLoop( k_nThousand, (uint64)( frametime * 1000000.0f ) ); +} +#else +void CGCClientSystem::PreClientUpdate() +{ + ThinkConnection(); + if ( m_bInittedGC ) + m_GCClient.BMainLoop( k_nThousand, ( uint64 )( gpGlobals->frametime * 1000000.0f ) ); +} +#endif + +//----------------------------------------------------------------------------- +void CGCClientSystem::ThinkConnection() +{ + + // Currently logged on? + #ifdef CLIENT_DLL + bool bLoggedOn = ClientSteamContext().BLoggedOn(); + #else + bool bLoggedOn = steamgameserverapicontext && steamgameserverapicontext->SteamGameServer() && steamgameserverapicontext->SteamGameServer()->BLoggedOn(); + #endif + if ( bLoggedOn ) + { + + // We're logged on. Is this a rising edge? + if ( !m_bLoggedOn ) + { + + // Re-init logon + m_bLoggedOn = true; + + // server should automatically send us a HELLO pretty quickly after it detects us logon. Give it some time to send + m_timeLastSendHello = Plat_FloatTime() + 2.0f; + Assert( !m_bConnectedToGC ); + SetupGC(); + } + + // Check if we need to send a HELLO message to re-sync connection with the GC + Assert( m_bInittedGC ); + if ( !m_bConnectedToGC ) + { + if ( m_timeLastSendHello < Plat_FloatTime() - k_flClientHelloRetry ) + { + m_timeLastSendHello = Plat_FloatTime(); + + #ifdef CLIENT_DLL + GCSDK::CProtoBufMsg<CMsgClientHello> msg( k_EMsgGCClientHello ); + msg.Body().set_version( engine->GetClientVersion() ); + + //CGCClientSharedObjectCache *pSOCache = m_GCClient.FindSOCache( ClientSteamContext().GetLocalPlayerSteamID(), false ); + //if ( pSOCache != NULL && pSOCache->BIsInitialized() ) + //{ + // msg.Body().set_socache_version( pSOCache->GetVersion() ); + //} + + #else + GCSDK::CProtoBufMsg<CMsgServerHello> msg( k_EMsgGCServerHello ); + msg.Body().set_version( engine->GetServerVersion() ); + + //CGCClientSharedObjectCache *pSOCache = m_GCClient.FindSOCache( steamgameserverapicontext->SteamGameServer()->GetSteamID(), false ); + //if ( pSOCache != NULL && pSOCache->BIsInitialized() ) + //{ + // msg.Body().set_socache_version( pSOCache->GetVersion() ); + //} + #endif + BSendMessage( msg ); + } + } + + } + else + { + + // We're not logged on. Clear all connection state flags + m_bLoggedOn = false; + m_bConnectedToGC = false; + m_timeLastSendHello = -999.9; + } +} + +#ifdef CLIENT_DLL + +void CGCClientSystem::ReceivedClientWelcome( const CMsgClientWelcome &msg ) +{ + m_bConnectedToGC = true; + Msg( "Connection to game coordinator established.\n" ); + + IGameEvent *pEvent = gameeventmanager->CreateEvent( "gc_new_session" ); + if ( pEvent ) + { + gameeventmanager->FireEventClientSide( pEvent ); + } + +// GTFGCClientSystem()->UpdateGCServerInfo(); +// +// // Validate version +// int engineServerVersion = engine->GetServerVersion(); +// g_gcServerVersion = (int)msg.Body().version(); +// +// // Version checking is enforced if both sides do not report zero as their version +// if ( engineServerVersion && g_gcServerVersion && engineServerVersion != g_gcServerVersion ) +// { +// // If we're out of date exit +// Msg("Version out of date (GC wants %d, we are %d)!\n", g_gcServerVersion, engine->GetServerVersion() ); +// +// // If we hibernating, quit now, otherwise we will quit on hibernation +// if ( g_ServerGameDLL.m_bIsHibernating ) +// { +// engine->ServerCommand( "quit\n" ); +// } +// } +// else +// { +// Msg("GC Connection established for server version %d\n", engine->GetServerVersion() ); +// } + +} + +class CGCClientJobClientWelcome : public GCSDK::CGCClientJob +{ +public: + CGCClientJobClientWelcome( GCSDK::CGCClient *pGCClient ) : GCSDK::CGCClientJob( pGCClient ) { } + + virtual bool BYieldingRunJobFromMsg( IMsgNetPacket *pNetPacket ) + { + CProtoBufMsg<CMsgClientWelcome> msg( pNetPacket ); + GCClientSystem()->ReceivedClientWelcome( msg.Body() ); + return true; + } +}; +GC_REG_JOB( GCSDK::CGCClient, CGCClientJobClientWelcome, "CGCClientJobClientWelcome", k_EMsgGCClientWelcome, k_EServerTypeGCClient ); + +void CGCClientSystem::ReceivedClientGoodbye( const CMsgClientGoodbye &msg ) +{ + switch ( msg.reason() ) + { + case GCGoodbyeReason_GC_GOING_DOWN: + Warning( "The item server is shutting down. Items will be unavailable temporarily.\n" ); + break; + + case GCGoodbyeReason_NO_SESSION: + if ( m_bConnectedToGC ) + { + Warning( "The connection to the item server has been interrupted. Attempting to re-negotiate connection now.\n" ); + } + break; + + default: + Warning( "Received goodbye message from the item server with unknown reason code %d.\n", (int)msg.reason() ); + break; + } + + m_bConnectedToGC = false; +} + +class CGCClientJobClientGoodbye : public GCSDK::CGCClientJob +{ +public: + CGCClientJobClientGoodbye( GCSDK::CGCClient *pGCClient ) : GCSDK::CGCClientJob( pGCClient ) { } + + virtual bool BYieldingRunJobFromMsg( IMsgNetPacket *pNetPacket ) + { + CProtoBufMsg<CMsgClientGoodbye> msg( pNetPacket ); + GCClientSystem()->ReceivedClientGoodbye( msg.Body() ); + return true; + } +}; +GC_REG_JOB( GCSDK::CGCClient, CGCClientJobClientGoodbye, "CGCClientJobClientGoodbye", k_EMsgGCClientGoodbye, k_EServerTypeGCClient ); + +#else + +void CGCClientSystem::ReceivedServerWelcome( const CMsgServerWelcome &msg ) +{ + if ( !m_bConnectedToGC ) + { + m_bConnectedToGC = true; + Msg( "Connection to game coordinator established.\n" ); + } + +// GTFGCClientSystem()->UpdateGCServerInfo(); +// +// // Validate version +// int engineServerVersion = engine->GetServerVersion(); +// g_gcServerVersion = (int)msg.Body().version(); +// +// // Version checking is enforced if both sides do not report zero as their version +// if ( engineServerVersion && g_gcServerVersion && engineServerVersion != g_gcServerVersion ) +// { +// // If we're out of date exit +// Msg("Version out of date (GC wants %d, we are %d)!\n", g_gcServerVersion, engine->GetServerVersion() ); +// +// // If we hibernating, quit now, otherwise we will quit on hibernation +// if ( g_ServerGameDLL.m_bIsHibernating ) +// { +// engine->ServerCommand( "quit\n" ); +// } +// } +// else +// { +// Msg("GC Connection established for server version %d\n", engine->GetServerVersion() ); +// } + +} + +class CGCClientJobServerWelcome : public GCSDK::CGCClientJob +{ +public: + CGCClientJobServerWelcome( GCSDK::CGCClient *pGCServer ) : GCSDK::CGCClientJob( pGCServer ) { } + + virtual bool BYieldingRunJobFromMsg( IMsgNetPacket *pNetPacket ) + { + CProtoBufMsg<CMsgServerWelcome> msg( pNetPacket ); + GCClientSystem()->ReceivedServerWelcome( msg.Body() ); + return true; + } +}; +GC_REG_JOB( GCSDK::CGCClient, CGCClientJobServerWelcome, "CGCClientJobServerWelcome", k_EMsgGCServerWelcome, k_EServerTypeGCClient ); + +void CGCClientSystem::ReceivedServerGoodbye( const CMsgServerGoodbye &msg ) +{ + switch ( msg.reason() ) + { + case GCGoodbyeReason_GC_GOING_DOWN: + Warning( "The item server is shutting down. Items will be unavailable temporarily.\n" ); + break; + + case GCGoodbyeReason_NO_SESSION: + if ( m_bConnectedToGC ) + { + Warning( "The connection to the game coordinator has been interrupted. Attempting to re-negotiate connection now.\n" ); + } + break; + + default: + Warning( "Received goodbye message from game coordinator with unknown reason code %d.\n", (int)msg.reason() ); + break; + } + + m_bConnectedToGC = false; +} + +class CGCClientJobServerGoodbye : public GCSDK::CGCClientJob +{ +public: + CGCClientJobServerGoodbye( GCSDK::CGCClient *pGCClient ) : GCSDK::CGCClientJob( pGCClient ) { } + + virtual bool BYieldingRunJobFromMsg( IMsgNetPacket *pNetPacket ) + { + CProtoBufMsg<CMsgServerGoodbye> msg( pNetPacket ); + GCClientSystem()->ReceivedServerGoodbye( msg.Body() ); + return true; + } +}; +GC_REG_JOB( GCSDK::CGCClient, CGCClientJobServerGoodbye, "CGCClientJobServerGoodbye", k_EMsgGCServerGoodbye, k_EServerTypeGCClient ); + +#endif // #ifdef CLIENT_DLL, #else + +//----------------------------------------------------------------------------- |