//====== Copyright (c), Valve Corporation, All rights reserved. ======= // // Purpose: // //============================================================================= #ifndef GCBASE_H #define GCBASE_H #ifdef _WIN32 #pragma once #endif #include "gamecoordinator/igamecoordinator.h" #include "gamecoordinator/igamecoordinatorhost.h" #include "tier1/utlallocation.h" #include "gcmsg.h" #include "jobmgr.h" #include "tier1/thash.h" #include "tier1/utlsortvector.h" #include "http.h" #include "language.h" #include "accountdetails.h" class CGCMsgGetSystemStatsResponse; namespace GCSDK { class CGCSession; class CGCUserSession; class CGCGSSession; class CGCSharedObjectCache; class CSharedObject; class CAccountDetails; struct PackageLicense_t { uint32 m_unPackageID; RTime32 m_rtimeCreated; }; class CGCBase : public IGameCoordinator { public: CGCBase( ); virtual ~CGCBase(); // Hooks to extend the base behaviors of the IGameCoordinator interface virtual bool OnInit() { return true; } virtual bool OnMainLoopOncePerFrame( CLimitTimer &limitTimer ) { return false; } virtual bool OnMainLoopUntilFrameCompletion( CLimitTimer &limitTimer ) { return false; } virtual void OnUninit() {} // returns true if this function handled the message virtual bool OnMessageFromClient( const CSteamID & senderID, uint32 unMsgType, void *pubData, uint32 cubData ) { return false; } virtual void OnValidate( CValidator &validator, const char *pchName ) {} virtual const char *LocalizeToken( const char *pchToken, ELanguage eLanguage, bool bReturnTokenIfNotFound = true ) { return ""; } // Life cycle management functions // Called to do any yielding initialization work before reporting as fully operational virtual bool BYieldingFinishStartup() = 0; // Called to do any yielding work immediately after becoming full operational virtual bool BYieldingPostStartup() = 0; // Call to report that we're fully operational void SetStartupComplete( bool bSuccess ); // Called to do any yielding work before reporting as ready to shutdown virtual void YieldingGracefulShutdown() = 0; virtual CGCUserSession *CreateUserSession( const CSteamID & steamID, CGCSharedObjectCache *pSOCache ) const; virtual CGCGSSession *CreateGSSession( const CSteamID & steamID, CGCSharedObjectCache *pSOCache, uint32 unServerAddr, uint16 usServerPort ) const; virtual void YieldingSessionStartPlaying( CGCUserSession *pSession ) {} virtual void YieldingSessionStopPlaying( CGCUserSession *pSession ) {} virtual void YieldingSessionStartServer( CGCGSSession *pSession ) {} virtual void YieldingSessionStopServer( CGCGSSession *pSession ) {} virtual void YieldingSOCacheLoaded( CGCSharedObjectCache *pSOCache ); virtual void YieldingPreTestSetup() {} // cache management CGCSharedObjectCache *YieldingGetLockedSOCache( const CSteamID &steamID ); CGCSharedObjectCache *YieldingFindOrLoadSOCache( const CSteamID &steamID ); CGCSharedObjectCache *FindSOCache( const CSteamID & steamID ); // non-yielding, but may return NULL if the cache exists but is not loaded bool UnloadUnusedCaches( uint32 unMaxCacheCount, CLimitTimer *pLimitTimer = NULL ); void YieldingReloadCache( CGCSharedObjectCache *pSOCache ); virtual CGCSharedObjectCache *CreateSOCache( const CSteamID &steamID ); CGCUserSession *YieldingGetLockedUserSession( const CSteamID & steamID ); CGCUserSession *FindUserSession( const CSteamID & steamID ) const; CGCGSSession *YieldingGetLockedGSSession( const CSteamID & steamID ); CGCGSSession *YieldingFindOrCreateGSSession( const CSteamID & steamID, uint32 unServerAddr, uint16 usServerPort, const uint8 *pubVarData, uint32 cubVarData ); CGCGSSession *FindGSSession( const CSteamID & steamID ) const; CGCSession *YieldingRequestSession( const CSteamID & steamID ); bool BYieldingIsOnline( const CSteamID & steamID ); bool BYieldingSendHTTPRequest( CHTTPRequest *pRequest, CHTTPResponse *pResponse ); bool BYieldingSendHTTPRequest( CHTTPRequest *pRequest, KeyValues *pkvResponse ); bool BSendWebApiRegistration(); int GetSOCacheCount() const; #ifdef DEBUG bool IsSOCached( CSharedObject *pObj, int nTypeID ); #endif int GetUserSessionCount() const; int GetGSSessionCount() const; void SetIsShuttingDown( bool bIsShuttingDown ) { m_bIsShuttingDown = true; } bool GetIsShuttingDown() const { return m_bIsShuttingDown; } // Iterate over sessions // WARNING: Don't yield between GetFirst*/GetNext*. Use caution when using // these any time you might have tens of thousands of sessions to iterate through. CGCUserSession **GetFirstUserSession() { return m_hashUserSessions.PvRecordFirst(); } CGCUserSession **GetNextUserSession( CGCUserSession **pUserSession ) { return m_hashUserSessions.PvRecordNext( pUserSession ); } CGCGSSession **GetFirstGSSession() { return m_hashGSSessions.PvRecordFirst(); } CGCGSSession **GetNextGSSession( CGCGSSession **pGSSession ) { return m_hashGSSessions.PvRecordNext( pGSSession ); } typedef CUtlSortVector< CGCGSSession *, CUtlSortVectorDefaultLess< CGCGSSession * >, CCopyableUtlVector< CGCGSSession * > > CGCGSSessionSortedVector_t; CGCGSSessionSortedVector_t const *GetGSSessionVectorByType( int serverType ); // This can cause the server pointer to change between lists, call it here void SetGSServerType( CGCGSSession *pGSSession, int serverType ); AppId_t GetAppID() const { return m_unAppID; } bool BIsStartupComplete() const { return m_bStartupComplete; } CJobMgr &GetJobMgr() { return m_JobMgr; } CSteamID YieldingGuessSteamIDFromInput( const char *pchInput ); bool BYieldingRecordSupportAction( const CSteamID & actorID, const CSteamID & targetID, const char *pchData, const char *pchNote ); void PostAlert( EAlertType eAlertType, bool bIsCritical, const char *pchAlertText, const CUtlVector< CUtlString > *pvecExtendedInfo = NULL, bool bAlsoSpew = true ); CAccountDetails *YieldingGetAccountDetails( const CSteamID & steamID ); bool BYieldingGetAccountLicenses( const CSteamID & steamID, CUtlVector< PackageLicense_t > & vecPackages ); bool BYieldingLookupAccount( EAccountFindType eFindType, const char *pchInput, CUtlVector< CSteamID > *prSteamIDs ); bool BYieldingAddFreeLicense( const CSteamID & steamID, uint32 unPackageID, uint32 unIPPublic = 0, const char *pchStoreCountryCode = NULL ); bool BSendGCMsgToClient( const CSteamID & steamIDTarget, const CGCMsgBase& msg ); bool BSendGCMsgToClient( const CSteamID & steamIDTarget, const IProtoBufMsg& msg ); bool BSendSystemMessage( const CGCMsgBase& msg ); bool BSendSystemMessage( const IProtoBufMsg& msg ); bool BSendGCInterMessage( AppId_t unAppID, const IProtoBufMsg &msg ); bool BReplyToMessage( CGCMsgBase &msgOut, const CGCMsgBase &msgIn ); bool BReplyToMessage( IProtoBufMsg &msgOut, const IProtoBufMsg &msgIn ); const char *GetPath() const { return m_sPath; } virtual const char *GetSteamAPIKey(); void QueueStartPlaying( const CSteamID & steamID, const CSteamID & gsSteamID, uint32 unServerAddr, uint16 usServerPort, const uint8 *pubVarData, uint32 cubVarData ); void YieldingExecuteNextStartPlaying(); bool BIsInLogonSurge() const; void YieldingStopPlaying( const CSteamID & steamID ); void YieldingStartGameserver( const CSteamID & steamID, uint32 unServerAddr, uint16 usServerPort, const uint8 *pubVarData, uint32 cubVarData ); void YieldingStopGameserver( const CSteamID & steamID ); bool BIsSOCacheBeingLoaded( const CSteamID & steamID ) const { return m_rbtreeSOCachesBeingLoaded.Find( steamID ) != m_rbtreeSOCachesBeingLoaded.InvalidIndex(); } bool BYieldingLockSteamID( const CSteamID &steamID ); bool BYieldingLockSteamIDPair( const CSteamID &steamIDA, const CSteamID &steamIDB ); bool BLockSteamIDImmediate( const CSteamID &steamID ); void UnlockSteamID( const CSteamID &steamID ); bool IsSteamIDLocked( const CSteamID &steamID ); bool IsSteamIDLockedByJob( const CSteamID &steamID, const CJob *pJob ); bool IsSteamIDUnlockedOrLockedByCurJob( const CSteamID &steamID ); CJob *PJobHoldingLock( const CSteamID &steamID ); const CLock *FindLock( const CSteamID &steamID ); void DumpLocks( bool bFull, int nMax = 10 ); void DumpJobs( int nMax ) const; virtual void Dump() const; void VerifySOCacheLRU(); void SetProfilingEnabled( bool bEnabled ); void SetDumpVprofImbalances( bool bEnabled ); bool GetVprofImbalances(); bool YieldingWritebackDirtyCaches( uint32 unSecondToDelayWrite ); void AddCacheToWritebackQueue( CGCSharedObjectCache *pSOCache ); bool BYieldingRetrieveCacheVersion( CGCSharedObjectCache *pSOCache ); void AddCacheToVersionChangedList( CGCSharedObjectCache *pSOCache ); const char *GetCDNURL() const; struct GCMemcachedBuffer_t { const void *m_pubData; int m_cubData; }; struct GCMemcachedGetResult_t { bool m_bKeyFound; // true if the key was found CUtlAllocation m_bufValue; // the value of the key }; // Memcached access bool BMemcachedSet( const char *pKey, const ::google::protobuf::Message &protoBufObj ); bool BMemcachedDelete( const char *pKey ); bool BYieldingMemcachedGet( const char *pKey, ::google::protobuf::Message &protoBufObj ); bool BMemcachedSet( const CUtlString &strKey, const CUtlBuffer &buf ); bool BMemcachedSet( const CUtlVector &vecKeys, const CUtlVector &vecValues ); bool BMemcachedDelete( const CUtlString &strKey ); bool BMemcachedDelete( const CUtlVector &vecKeys ); bool BYieldingMemcachedGet( const CUtlString &strKey, GCMemcachedGetResult_t &result ); bool BYieldingMemcachedGet( const CUtlVector &vecKeys, CUtlVector &vecResults ); // IP location bool BYieldingGetIPLocations( CUtlVector &vecIPs, CUtlVector &infos ); // Stats virtual void SystemStats_Update( CGCMsgGetSystemStatsResponse &msgStats ); protected: virtual bool BYieldingLoadSOCache( CGCSharedObjectCache *pSOCache ); void RemoveSOCache( const CSteamID & steamID ); void AddCacheToLRU( CGCSharedObjectCache * pSOCache ); void RemoveCacheFromLRU( CGCSharedObjectCache * pSOCache ); void SetStartupComplete() { m_bStartupComplete = true; } private: static void AssertCallbackFunc( const char *pchFile, int nLine, const char *pchMessage ); void UpdateSOCacheVersions(); // this is called from ExecuteNextStartPlaying and nowhere else void YieldingStartPlaying( const CSteamID & steamID, const CSteamID & gsSteamID, uint32 unServerAddr, uint16 usServerPort, CUtlBuffer *pVarData ); // Base behaviors of the IGameCoordinator interface. These are not overridable. // They each call the On* version below, which is overridable by subclasses virtual bool BInit( AppId_t unAppID, const char *pchAppPath, IGameCoordinatorHost *pHost ); virtual bool BMainLoopOncePerFrame( uint64 ulLimitMicroseconds ); virtual bool BMainLoopUntilFrameCompletion( uint64 ulLimitMicroseconds ); virtual void Shutdown(); virtual void Uninit(); virtual void MessageFromClient( const CSteamID & senderID, uint32 unMsgType, void *pubData, uint32 cubData ); virtual void Validate( CValidator &validator, const char *pchName ); virtual void SQLResults( GID_t gidContextID ); // profiling bool m_bStartProfiling; bool m_bStopProfiling; bool m_bDumpVprofImbalances; // local job handling CJobMgr m_JobMgr; CGCWGJobMgr m_wgJobMgr; // session tracking CTHash m_hashUserSessions; CTHash m_hashGSSessions; typedef CUtlMap< int, CGCGSSessionSortedVector_t > CGCGSSessionServerTypeMap_t; CGCGSSessionServerTypeMap_t m_GSSessionsByTypeMap; CUtlMap< uint64, CGCGSSession*, int > m_mapGSSessionsByNetAddress; // Shared object caches CUtlMap m_mapSOCache; CUtlVector< CGCSharedObjectCache * >m_vecCacheWritebacks; CUtlLinkedList< CSteamID, uint32> m_listCachesToUnload; CUtlRBTree m_rbtreeSOCachesBeingLoaded; CUtlRBTree m_rbtreeSOCachesWithDirtyVersions; // steamID locks CTHash m_hashSteamIDLocks; // Account Details CAccountDetailsManager m_AccountDetailsManager; // State AppId_t m_unAppID; CUtlString m_sPath; IGameCoordinatorHost *m_pHost; bool m_bStartupComplete; bool m_bIsShuttingDown; struct StartPlayingWork_t { CSteamID m_steamID; CSteamID m_gsSteamID; uint32 m_unServerAddr; uint16 m_usServerPort; CUtlBuffer *m_pVarData; }; CUtlLinkedList< StartPlayingWork_t, int > m_llStartPlaying; int m_nStartPlayingJobCount; // URL to use for our app's CDN'd images mutable CUtlString m_sCDNURL; }; extern CGCBase *GGCBase(); EResult YieldingSendWebAPIRequest( CSteamAPIRequest &request, KeyValues *pKVResponse, CUtlString &errMsg, bool b200MeansSuccess ); } // namespace GCSDK #endif // GCBASE_H