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.

526 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. #ifndef _INCLUDED_TF_GC_CLIENT_H
  3. #define _INCLUDED_TF_GC_CLIENT_H
  4. #ifdef _WIN32
  5. #pragma once
  6. #endif
  7. #if !defined( _X360 ) && !defined( NO_STEAM )
  8. #include "steam/steam_api.h"
  9. #endif
  10. //#include "dota_gc_common.h"
  11. #include "gcsdk/gcclientsdk.h"
  12. //#include "dota_gamerules.h"
  13. #include "tf_gcmessages.pb.h"
  14. #include "../clientsteamcontext.h"
  15. #include "../gc_clientsystem.h"
  16. #include "GameEventListener.h"
  17. #include "tf_quickplay_shared.h"
  18. #include "confirm_dialog.h"
  19. #include "econ_game_account_client.h"
  20. #include "tf_matchmaking_shared.h"
  21. #include "tf_match_join_handlers.h"
  22. #include "netadr.h"
  23. class CTFParty;
  24. class CTFGSLobby;
  25. class CMvMMissionSet;
  26. class IMatchJoiningHandler;
  27. //class CDOTAGameAccountClient;
  28. //class CDOTABetaParticipation;
  29. #if !defined( TF_GC_PING_DEBUG ) && ( defined( STAGING_ONLY ) || defined( _DEBUG ) )
  30. #define TF_GC_PING_DEBUG
  31. #endif
  32. namespace GCSDK
  33. {
  34. typedef uint64 PlayerGroupID_t;
  35. }
  36. /// High level matchmaking UI flow. This represents the state that we show to the player,
  37. /// and might not reflect all underlying asynchronous operations.
  38. enum EMatchmakingUIState
  39. {
  40. eMatchmakingUIState_Inactive, //< At the main menu or in a regular game. No lobby exists
  41. eMatchmakingUIState_Chat, //< Setting options, chatting, not in search queue
  42. eMatchmakingUIState_InQueue, //< In matchmaking queue, awaiting to be matched with compatible players and a gameserver. Game could start at any moment
  43. eMatchmakingUIState_Connecting, //< Matched with other players and assigned a gameerver, trying to connect to a game server
  44. eMatchmakingUIState_InGame, //< In a game
  45. };
  46. enum EAbandonGameStatus
  47. {
  48. k_EAbandonGameStatus_Safe, //< It's totally safe to leave
  49. k_EAbandonGameStatus_AbandonWithoutPenalty, //< Leaving right now would be considered "abandoning", but there will be no penalty right now
  50. k_EAbandonGameStatus_AbandonWithPenalty, //< Leaving right now would be considered "abandoning", and you will be penalized
  51. };
  52. class CLoalPlayerSOCacheListener;
  53. class CSendCreateOrUpdatePartyMsgJob;
  54. class CTFGCClientSystem : public CGCClientSystem, public GCSDK::ISharedObjectListener, public CGameEventListener
  55. {
  56. friend class CTFMatchmakingPopup;
  57. friend class CLoalPlayerSOCacheListener;
  58. friend class CSendCreateOrUpdatePartyMsgJob;
  59. DECLARE_CLASS_GAMEROOT( CTFGCClientSystem, CGCClientSystem );
  60. public:
  61. CTFGCClientSystem( void );
  62. ~CTFGCClientSystem( void );
  63. // CAutoGameSystemPerFrame
  64. virtual bool Init() OVERRIDE;
  65. virtual void PostInit() OVERRIDE;
  66. virtual void LevelInitPreEntity() OVERRIDE;
  67. virtual void LevelShutdownPostEntity() OVERRIDE;
  68. virtual void Shutdown() OVERRIDE;
  69. virtual void Update( float frametime ) OVERRIDE;
  70. // Force discard all current ping data, forcing it to be refreshed, and causing BHavePingData to be false until it
  71. // completes.
  72. //
  73. // Normally, the client think will idly refresh this data, so this is only valuable for debug or cases where we know
  74. // the network changed and our previous data is worse than no data.
  75. void InvalidatePingData();
  76. bool BHavePingData() { return m_rtLastPingFix > 0; }
  77. // If !BHavePingData() this will have no datacenters in it.
  78. CMsgGCDataCenterPing_Update GetPingData() { return m_msgCachedPingUpdate; }
  79. // ISharedObjectListener
  80. virtual void SOCreated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
  81. virtual void PreSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { /* do nothing */ }
  82. virtual void SOUpdated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
  83. virtual void PostSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { /* do nothing */ }
  84. virtual void SODestroyed( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
  85. virtual void SOCacheSubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
  86. virtual void SOCacheUnsubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { m_pSOCache = NULL; }
  87. // IGameEventListener2
  88. virtual void FireGameEvent( IGameEvent *event ) OVERRIDE;
  89. enum SOChangeType_t
  90. {
  91. SOChanged_Create,
  92. SOChanged_Update,
  93. SOChanged_Destroy
  94. };
  95. void SOChanged( const GCSDK::CSharedObject *pObject, SOChangeType_t changeType, GCSDK::ESOCacheEvent eEvent );
  96. // uint32 GetWins() { return m_unWinCount; }
  97. // uint32 GetLosses() { return m_unLossCount; }
  98. // int GetHeroRecordCount() { return m_aHeroRecords.Count(); }
  99. // GCHeroRecord_t* GetHeroRecord( int nIndex ) { return &m_aHeroRecords[ nIndex ]; }
  100. // KeyValues* GetNewsKeys() { return m_pNewsKeys; }
  101. // KeyValues* GetNewsStory( uint64 unNewsID );
  102. // KeyValues* GetNewsStoryByIndex( int nNewsIndex );
  103. // void SetGetNewsTime( float flGetNewsTime ) { m_flGetNewsTime = flGetNewsTime; }
  104. // void GameRules_State_Enter( DOTA_GameState newState );
  105. // void SetCurrentMatchID( uint32 unMatchID ) { m_unCurrentMatchID = unMatchID; }
  106. // uint32 GetCurrentMatchID() { return m_unCurrentMatchID; }
  107. void OnGCUserSessionCreated() { }
  108. bool HasGCUserSessionBeenCreated();
  109. // CDOTAGameAccountClient* GetGameAccountClient();
  110. // void DumpGameAccountClient();
  111. // CDOTABetaParticipation* GetBetaParticipation();
  112. // void DumpBetaParticipation();
  113. CTFParty* GetParty();
  114. void CreateNewParty();
  115. CTFGSLobby* GetLobby();
  116. void DumpParty();
  117. void DumpLobby();
  118. void DumpInvites();
  119. void DumpPing();
  120. /// Request to jump to a particular step
  121. void RequestSelectWizardStep( TF_Matchmaking_WizardStep eWizardStep );
  122. /// Fetch current high-level logical UI state
  123. EMatchmakingUIState GetMatchmakingUIState();
  124. /// Fetch current wizard step.
  125. TF_Matchmaking_WizardStep GetWizardStep() const { return m_eLocalWizardStep; }
  126. /// Activate matchmaking system. Doesn't necessarily do any network activity
  127. void BeginMatchmaking( TF_MatchmakingMode mode );
  128. bool BAllowMatchMakingInGame( void ) const;
  129. /// Quit the current game and lobby, if any and go back to the main menu
  130. void EndMatchmaking( bool bSendAbandonLobby = false );
  131. bool BExitMatchmakingAfterDisconnect( void );
  132. /// Called to active the invite UI
  133. void RequestActivateInvite();
  134. void ConnectToServer( const char *connect );
  135. // void StopFindingMatch();
  136. // void StartWatchingGame( const CSteamID &gameServerSteamID );
  137. // void StartWatchingGame( const CSteamID &gameServerSteamID, const CSteamID &watchServerSteamID );
  138. // void CancelWatchGameRequest();
  139. // GCSDK::CGCClientSharedObjectCache *GetSOCache() { return m_pSOCache; }
  140. // void SetAutoSpectateCheckTime( float flAutoSpectateCheckTime ) { m_flAutoSpectateCheckTime = flAutoSpectateCheckTime; }
  141. //
  142. // // downloading files
  143. //
  144. // struct CDownloadingFile
  145. // {
  146. // uint32 m_unFileID;
  147. // HTTPRequestHandle m_hRequestHandle;
  148. // CCallResult< CTFGCClientSystem, HTTPRequestCompleted_t > m_Callback;
  149. // char m_szLocalFilename[MAX_PATH];
  150. // char m_szRemoteURL[MAX_PATH];
  151. // };
  152. // CUtlVector<CDownloadingFile*> m_DownloadingFiles;
  153. //
  154. // void DownloadFile( const char *pszRemoteURL, const char *pszLocalFilename, bool bForceDownload = false );
  155. // void OnDownloadCompleted( HTTPRequestCompleted_t *arg, bool bFailed );
  156. //
  157. // void SetTodayMessages( CMsgDOTATodayMessages *pMessages ) { m_TodayMessages = *pMessages; }
  158. // CMsgDOTATodayMessages* GetTodayMessages() { return &m_TodayMessages; }
  159. // void StartWatchingGameResponse( const CMsgWatchGameResponse &response );
  160. //
  161. // Search criteria
  162. //
  163. TF_MatchmakingMode GetSearchMode();
  164. // What MvM challenges?
  165. void GetSearchChallenges( CMvMMissionSet &challenges );
  166. void SetSearchChallenges( const CMvMMissionSet &challenges );
  167. // Willing to join the game late?
  168. bool GetSearchJoinLate();
  169. void SetSearchJoinLate( bool bJoinLate );
  170. // Quickplay
  171. EGameCategory GetQuickplayGameType();
  172. void SetQuickplayGameType( EGameCategory type );
  173. // "Play for loot" - requires a ticket. If the challenge is beaten, then the winners
  174. // get some loot
  175. bool GetSearchPlayForBraggingRights();
  176. void SetSearchPlayForBraggingRights( bool bPlayForBraggingRights );
  177. #ifdef USE_MVM_TOUR
  178. int GetSearchMannUpTourIndex();
  179. void SetSearchMannUpTourIndex( int idxTour );
  180. #endif // USE_MVM_TOUR
  181. // Casual matchmaking groups and categories
  182. void SelectCasualMap( uint32 nMapDefIndex, bool bSelected );
  183. bool IsCasualMapSelected( uint32 nMapDefIndex ) const;
  184. void ClearCasualSearchCriteria();
  185. void SaveCasualSearchCriteriaToDisk();
  186. void LoadCasualSearchCriteria();
  187. // Update custom MM ping setting from the convar
  188. void UpdateCustomPingTolerance();
  189. // Check if the local player is doubling down
  190. bool GetLocalPlayerSquadSurplus();
  191. void SetLocalPlayerSquadSurplus( bool bSquadSurplus );
  192. // Ladders
  193. uint32 GetLadderType();
  194. void SetLadderType( uint32 nType );
  195. // World status
  196. const CMsgTFWorldStatus &WorldStatus() const { return m_WorldStatus; }
  197. static const char *k_pszSteamLobbyKey_PartyID;
  198. /// Accept the invite, join the specified lobby
  199. void AcceptFriendInviteToJoinLobby( const CSteamID &steamIDLobby );
  200. /// Return true if we're the leader of the party.
  201. /// NOTE: Returns true if we don't have a party!
  202. bool BIsPartyLeader();
  203. bool BHasOutstandingMatchmakingPartyMessage() const;
  204. enum ELobbyMsgType
  205. {
  206. k_eLobbyMsg_UserChat,
  207. k_eLobbyMsg_SystemMsgFromLeader,
  208. };
  209. /// Chat (though the steam lobby)
  210. void SendSteamLobbyChat( ELobbyMsgType eType, const char *pszText );
  211. /// See if we've got a ticket
  212. static bool BLocalPlayerInventoryHasMvmTicket( void );
  213. static int GetLocalPlayerInventoryMvmTicketCount( void );
  214. /// See if we've got a double-down
  215. static bool BLocalPlayerInventoryHasSquadSurplusVoucher( void );
  216. static int GetLocalPlayerInventorySquadSurplusVoucherCount( void );
  217. #ifdef USE_MVM_TOUR
  218. /// Get info about the local player's badge. Returns false if we can't
  219. /// find his inventory or he doesn't own a badge
  220. static bool BGetLocalPlayerBadgeInfoForTour( int iTourIndex, uint32 *pnBadgeLevel, uint32 *pnCompletedChallenges );
  221. #endif // USE_MVM_TOUR
  222. struct MatchMakerHealthData_t
  223. {
  224. float m_flRatio;
  225. Color m_colorBar;
  226. CUtlString m_strLocToken;
  227. };
  228. MatchMakerHealthData_t GetHealthBracketForRatio( float flRatio ) const;
  229. uint32 GetMostSearchedCount() const { return m_nMostSearchedMapCount; }
  230. MatchMakerHealthData_t GetOverallHealthDataForLocalCriteria() const;
  231. MatchMakerHealthData_t GetHealthDataForMap( uint32 nMapIndex ) const;
  232. void RequestMatchMakerStats() const;
  233. void SetMatchMakerStats( const CMsgGCMatchMakerStatsResponse newStats );
  234. const CMsgGCMatchMakerStatsResponse &GetMatchMakerStats() { return m_MatchMakerStats; }
  235. const CUtlDict< float > &GetDataCenterPopulationRatioDict( EMatchGroup eMatchGroup ) { return m_dictDataCenterPopulationRatio[ eMatchGroup ]; }
  236. void AcknowledgePendingXPSources( EMatchGroup eMatchGroup ) const;
  237. void AcknowledgeNotification( uint32 nAccountID, uint64 ulNotificationID ) const;
  238. void SetSurveyRequest( const CMsgGCSurveyRequest& msgSurveyRequest );
  239. const CMsgGCSurveyRequest& GetSurveyRequest() const { return m_msgSurveyRequest; }
  240. void SendSurveyResponse( int32 nResponse );
  241. void ClearSurveyRequest();
  242. /// Most recent matchmaking progress stats received
  243. CMsgMatchmakingProgress m_msgMatchmakingProgress;
  244. bool BConnectedToMatchServer( bool bLiveMatch );
  245. // !! Does NOT mean you're *in* this match. See Above.
  246. bool BHaveLiveMatch() const;
  247. EAbandonGameStatus GetAssignedMatchAbandonStatus();
  248. bool BUserWantsToBeInMatchmaking() const { return m_bUserWantsToBeInMatchmaking; }
  249. EMatchGroup GetLiveMatchGroup() const;
  250. // Helper that combines GetMatchAbandonStatus and BConnectedToMatch as this is usually what you're asking.
  251. EAbandonGameStatus GetCurrentServerAbandonStatus()
  252. {
  253. return BConnectedToMatchServer( true ) ? GetAssignedMatchAbandonStatus() : k_EAbandonGameStatus_Safe;
  254. }
  255. void RejoinLobby( bool bConfirmed );
  256. bool JoinMMMatch();
  257. void LeaveGameAndPrepareToJoinParty( GCSDK::PlayerGroupID_t nPartyID );
  258. bool BIsPhoneVerified( void );
  259. bool BIsPhoneIdentifying( void );
  260. bool BHasCompetitiveAccess( void );
  261. bool BIsIPRecentMatchServer( netadr_t ip ) { return m_vecMatchServerHistory.Find( ip ) != m_vecMatchServerHistory.InvalidIndex(); }
  262. void AddLocalPlayerSOListener( ISharedObjectListener* pListener, bool bImmedately = true );
  263. void RemoveLocalPlayerSOListener( ISharedObjectListener* pListener );
  264. #ifdef TF_GC_PING_DEBUG
  265. void SetPingOverride( const char *pszDataCenter, uint32 nPing, CMsgGCDataCenterPing_Update_Status eStatus );
  266. void ClearPingOverrides();
  267. #endif
  268. protected:
  269. // CGCClientSystem
  270. virtual void PreInitGC() OVERRIDE;
  271. virtual void PostInitGC() OVERRIDE;
  272. virtual void ReceivedClientWelcome( const CMsgClientWelcome &msg ) OVERRIDE;
  273. private:
  274. friend class CGCClientAcceptInviteResponse;
  275. friend class CGCWorldStatusBroadcast;
  276. // void CreateSourceTVProxy( uint32 source_tv_public_addr, uint32 source_tv_private_addr, uint32 source_tv_port );
  277. void PingThink();
  278. CMsgCreateOrUpdateParty *GetCreateOrUpdatePartyMsg();
  279. CSendCreateOrUpdatePartyMsgJob *m_pPendingCreateOrUpdatePartyMsg;
  280. float m_flSendPartyUpdateMessageTime;
  281. void SetWorldStatus( CMsgTFWorldStatus &status ) { m_WorldStatus = status; }
  282. CMsgGCMatchMakerStatsResponse m_MatchMakerStats;
  283. uint32 m_nMostSearchedMapCount;
  284. CMsgTFWorldStatus m_WorldStatus;
  285. // uint32 m_unCurrentMatchID;
  286. bool m_bRegisteredSharedObjects;
  287. bool m_bInittedGC;
  288. EMatchmakingUIState m_eMatchmakingUIState;
  289. /// The lobby we joined/created (presumably) for matchmaking purposes
  290. CSteamID m_steamIDLobby;
  291. /// The lobby we have accepted the invite for, but not yet joined.
  292. /// (We'll do it when there's a good opportunity)
  293. CSteamID m_steamIDLobbyInviteAccepted;
  294. enum EAcceptInviteStep
  295. {
  296. eAcceptInviteStep_None,
  297. eAcceptInviteStep_ReadyToJoinSteamLobby,
  298. eAcceptInviteStep_JoinSteamLobby,
  299. eAcceptInviteStep_GetLobbyMetadata,
  300. eAcceptInviteStep_JoinParty,
  301. };
  302. EAcceptInviteStep m_eAcceptInviteStep;
  303. /// Status of creating lobby.
  304. int m_eCreateLobbyStatus;
  305. /// Check if we're in a steam lobby, then leave it
  306. void LeaveSteamLobby();
  307. /// Should we active the invite UI at the next opportunity?
  308. bool m_bWantToActivateInviteUI;
  309. // The gameserver is authoritative on matches once we are assigned, so even if the lobby is lost or stale, these
  310. // control: Where our assigned match is, and if we consider ourselves absolved of it.
  311. CSteamID m_steamIDGCAssignedMatch;
  312. // So we can consider the match over, based on the gameserver telling us so (or us abandoning). Once the lobby state
  313. // via the GC agrees, SOChanged will clear.
  314. bool m_bAssignedMatchEnded;
  315. EMatchGroup m_eAssignedMatchGroup;
  316. uint64 m_uAssignedMatchID;
  317. // History of assigned matches so things like the server browser can reason about our connect history.
  318. CUtlVector< netadr_t > m_vecMatchServerHistory;
  319. // Set when m_steamIDAssignedServer changes for the next Update()
  320. bool m_bServerAssignmentChanged;
  321. // SDR ping system
  322. RTime32 m_rtLastPingFix;
  323. bool m_bPendingPingRefresh;
  324. bool m_bSentInitialPingFix;
  325. // Cached ping data message as of rtLastPingFix
  326. CMsgGCDataCenterPing_Update m_msgCachedPingUpdate;
  327. #ifdef TF_GC_PING_DEBUG
  328. CMsgGCDataCenterPing_Update m_msgPingOverrides;
  329. #endif
  330. // Asks user if they want to rejoin an existing lobby
  331. float m_flCheckForRejoinTime; // Due to network race conditions, delay for a bit before we respond
  332. void RejoinActiveMatch( void );
  333. // float m_flGetNewsTime;
  334. // float m_flAutoSpectateCheckTime;
  335. GCSDK::CGCClientSharedObjectCache *m_pSOCache;
  336. // uint32 m_unWinCount;
  337. // uint32 m_unLossCount;
  338. // int m_nSignOnState;
  339. enum EConnectState
  340. {
  341. eConnectState_Disconnected,
  342. eConnectState_ConnectingToMatchmade,
  343. eConnectState_ConnectedToMatchmade,
  344. eConnectState_NonmatchmadeServer,
  345. };
  346. EConnectState m_eConnectState;
  347. bool IsConnectStateDisconnected()
  348. {
  349. if ( BAllowMatchMakingInGame() )
  350. {
  351. return m_eConnectState != eConnectState_ConnectingToMatchmade &&
  352. m_eConnectState != eConnectState_ConnectedToMatchmade;
  353. }
  354. return m_eConnectState == eConnectState_Disconnected;
  355. }
  356. // CUtlSortVector<GCHeroRecord_t, CGCHeroRecordLess> m_aHeroRecords;
  357. // KeyValues *m_pNewsKeys;
  358. bool m_bGCUserSessionCreated;
  359. bool m_bUserWantsToBeInMatchmaking;
  360. GCSDK::PlayerGroupID_t m_nPendingAutoJoinPartyID;
  361. // Are we connected, and to whom
  362. CSteamID m_steamIDCurrentServer;
  363. // CMsgDOTATodayMessages m_TodayMessages;
  364. //
  365. // DOTAGameVersion m_GameVersion;
  366. void SendCreateOrUpdatePartyMsg( TF_Matchmaking_WizardStep eWizardStep );
  367. void SendExitMatchmaking( bool bExplicitAbandon );
  368. void FireGameEventLobbyUpdated();
  369. void FireGameEventPartyUpdated();
  370. CMsgMatchSearchCriteria m_msgLocalSearchCriteria;
  371. TF_Matchmaking_WizardStep m_eLocalWizardStep;
  372. bool m_bLocalSquadSurplus;
  373. // void CheckSendAdjustSearchCriteria();
  374. void AssertMakesSenseToReadSearchCriteria();
  375. bool BAllowMatchmakingSearch();
  376. #ifdef USE_MVM_TOUR
  377. bool BInternalSetSearchMannUpTourIndex( int idxTour );
  378. #endif // USE_MVM_TOUR
  379. bool BInternalSetSearchChallenges( const CMvMMissionSet &challenges );
  380. CCallback<CTFGCClientSystem, LobbyCreated_t, false> m_callbackSteamLobbyCreated;
  381. CCallback<CTFGCClientSystem, LobbyEnter_t, false> m_callbackSteamLobbyEnter;
  382. CCallback<CTFGCClientSystem, LobbyChatMsg_t, false> m_callbackSteamLobbyChatMsg;
  383. CCallback<CTFGCClientSystem, GameLobbyJoinRequested_t, false> m_callbackSteamGameLobbyJoinRequested;
  384. CCallback<CTFGCClientSystem, LobbyDataUpdate_t, false > m_callbackSteamLobbyDataUpdate;
  385. CCallback<CTFGCClientSystem, LobbyChatUpdate_t, false > m_callbackSteamLobbyChatUpdate;
  386. void OnSteamLobbyCreated( LobbyCreated_t *pInfo );
  387. void OnSteamLobbyEnter( LobbyEnter_t *pInfo );
  388. void OnSteamLobbyChatMsg( LobbyChatMsg_t *pInfo );
  389. void OnSteamGameLobbyJoinRequested( GameLobbyJoinRequested_t *pInfo );
  390. void OnSteamLobbyDataUpdate( LobbyDataUpdate_t *pInfo );
  391. void OnSteamLobbyChatUpdate( LobbyChatUpdate_t *pInfo );
  392. /// Check if we have a steam lobby. If we have one (and it's not the wrong one!) then return true.
  393. /// Otherwise, initiate creation, if possible
  394. ///
  395. /// Returns:
  396. /// -1 error
  397. /// 0 in progress
  398. /// 1 OK
  399. int CheckSteamLobbyCreated();
  400. /// Check if we need to associate the party and steam lobby with each other
  401. void CheckAssociatePartyAndSteamLobby();
  402. /// if we want to active the invite UI, and we're ready, then do it now!
  403. void CheckReadyToActivateInvite();
  404. /// Called when we fail to accept the invite
  405. void OnFailedToAcceptInvite();
  406. CUtlVector< ISharedObjectListener* > m_vecDelayedLocalPlayerSOListenersToAdd;
  407. CTFMatchMakingPopupPrompJoinHandler m_PromptJoinHandler;
  408. CTFImmediateAutoJoinHandler m_AutoJoinHandler;
  409. CMsgGCSurveyRequest m_msgSurveyRequest;
  410. CUtlDict< float > m_dictDataCenterPopulationRatio[ k_nMatchGroup_Count ];
  411. };
  412. CTFGCClientSystem* GTFGCClientSystem();
  413. #endif // _INCLUDED_TF_GC_CLIENT_H