summaryrefslogtreecommitdiff
path: root/game/shared/gc_clientsystem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/shared/gc_clientsystem.cpp')
-rw-r--r--game/shared/gc_clientsystem.cpp551
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
+
+//-----------------------------------------------------------------------------