diff options
Diffstat (limited to 'game/client/tf/tf_gc_client.h')
| -rw-r--r-- | game/client/tf/tf_gc_client.h | 526 |
1 files changed, 526 insertions, 0 deletions
diff --git a/game/client/tf/tf_gc_client.h b/game/client/tf/tf_gc_client.h new file mode 100644 index 0000000..6fb6b87 --- /dev/null +++ b/game/client/tf/tf_gc_client.h @@ -0,0 +1,526 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +#ifndef _INCLUDED_TF_GC_CLIENT_H +#define _INCLUDED_TF_GC_CLIENT_H +#ifdef _WIN32 +#pragma once +#endif + +#if !defined( _X360 ) && !defined( NO_STEAM ) +#include "steam/steam_api.h" +#endif + +//#include "dota_gc_common.h" +#include "gcsdk/gcclientsdk.h" +//#include "dota_gamerules.h" +#include "tf_gcmessages.pb.h" +#include "../clientsteamcontext.h" +#include "../gc_clientsystem.h" +#include "GameEventListener.h" +#include "tf_quickplay_shared.h" +#include "confirm_dialog.h" +#include "econ_game_account_client.h" +#include "tf_matchmaking_shared.h" +#include "tf_match_join_handlers.h" +#include "netadr.h" + +class CTFParty; +class CTFGSLobby; +class CMvMMissionSet; +class IMatchJoiningHandler; +//class CDOTAGameAccountClient; +//class CDOTABetaParticipation; + +#if !defined( TF_GC_PING_DEBUG ) && ( defined( STAGING_ONLY ) || defined( _DEBUG ) ) + #define TF_GC_PING_DEBUG +#endif + +namespace GCSDK +{ + typedef uint64 PlayerGroupID_t; +} + +/// High level matchmaking UI flow. This represents the state that we show to the player, +/// and might not reflect all underlying asynchronous operations. +enum EMatchmakingUIState +{ + eMatchmakingUIState_Inactive, //< At the main menu or in a regular game. No lobby exists + eMatchmakingUIState_Chat, //< Setting options, chatting, not in search queue + eMatchmakingUIState_InQueue, //< In matchmaking queue, awaiting to be matched with compatible players and a gameserver. Game could start at any moment + eMatchmakingUIState_Connecting, //< Matched with other players and assigned a gameerver, trying to connect to a game server + eMatchmakingUIState_InGame, //< In a game +}; + +enum EAbandonGameStatus +{ + k_EAbandonGameStatus_Safe, //< It's totally safe to leave + k_EAbandonGameStatus_AbandonWithoutPenalty, //< Leaving right now would be considered "abandoning", but there will be no penalty right now + k_EAbandonGameStatus_AbandonWithPenalty, //< Leaving right now would be considered "abandoning", and you will be penalized +}; + +class CLoalPlayerSOCacheListener; + +class CSendCreateOrUpdatePartyMsgJob; + +class CTFGCClientSystem : public CGCClientSystem, public GCSDK::ISharedObjectListener, public CGameEventListener +{ + friend class CTFMatchmakingPopup; + friend class CLoalPlayerSOCacheListener; + friend class CSendCreateOrUpdatePartyMsgJob; + DECLARE_CLASS_GAMEROOT( CTFGCClientSystem, CGCClientSystem ); +public: + CTFGCClientSystem( void ); + ~CTFGCClientSystem( void ); + + // CAutoGameSystemPerFrame + virtual bool Init() OVERRIDE; + virtual void PostInit() OVERRIDE; + virtual void LevelInitPreEntity() OVERRIDE; + virtual void LevelShutdownPostEntity() OVERRIDE; + virtual void Shutdown() OVERRIDE; + virtual void Update( float frametime ) OVERRIDE; + + // Force discard all current ping data, forcing it to be refreshed, and causing BHavePingData to be false until it + // completes. + // + // Normally, the client think will idly refresh this data, so this is only valuable for debug or cases where we know + // the network changed and our previous data is worse than no data. + void InvalidatePingData(); + + bool BHavePingData() { return m_rtLastPingFix > 0; } + // If !BHavePingData() this will have no datacenters in it. + CMsgGCDataCenterPing_Update GetPingData() { return m_msgCachedPingUpdate; } + + // ISharedObjectListener + virtual void SOCreated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE; + virtual void PreSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { /* do nothing */ } + virtual void SOUpdated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE; + virtual void PostSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { /* do nothing */ } + virtual void SODestroyed( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE; + virtual void SOCacheSubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE; + virtual void SOCacheUnsubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { m_pSOCache = NULL; } + + // IGameEventListener2 + virtual void FireGameEvent( IGameEvent *event ) OVERRIDE; + + enum SOChangeType_t + { + SOChanged_Create, + SOChanged_Update, + SOChanged_Destroy + }; + void SOChanged( const GCSDK::CSharedObject *pObject, SOChangeType_t changeType, GCSDK::ESOCacheEvent eEvent ); +// uint32 GetWins() { return m_unWinCount; } +// uint32 GetLosses() { return m_unLossCount; } + +// int GetHeroRecordCount() { return m_aHeroRecords.Count(); } +// GCHeroRecord_t* GetHeroRecord( int nIndex ) { return &m_aHeroRecords[ nIndex ]; } +// KeyValues* GetNewsKeys() { return m_pNewsKeys; } +// KeyValues* GetNewsStory( uint64 unNewsID ); +// KeyValues* GetNewsStoryByIndex( int nNewsIndex ); +// void SetGetNewsTime( float flGetNewsTime ) { m_flGetNewsTime = flGetNewsTime; } + +// void GameRules_State_Enter( DOTA_GameState newState ); + +// void SetCurrentMatchID( uint32 unMatchID ) { m_unCurrentMatchID = unMatchID; } +// uint32 GetCurrentMatchID() { return m_unCurrentMatchID; } + + void OnGCUserSessionCreated() { } + bool HasGCUserSessionBeenCreated(); + +// CDOTAGameAccountClient* GetGameAccountClient(); +// void DumpGameAccountClient(); +// CDOTABetaParticipation* GetBetaParticipation(); +// void DumpBetaParticipation(); + + CTFParty* GetParty(); + void CreateNewParty(); + + CTFGSLobby* GetLobby(); + void DumpParty(); + void DumpLobby(); + void DumpInvites(); + void DumpPing(); + + /// Request to jump to a particular step + void RequestSelectWizardStep( TF_Matchmaking_WizardStep eWizardStep ); + + /// Fetch current high-level logical UI state + EMatchmakingUIState GetMatchmakingUIState(); + + /// Fetch current wizard step. + TF_Matchmaking_WizardStep GetWizardStep() const { return m_eLocalWizardStep; } + + /// Activate matchmaking system. Doesn't necessarily do any network activity + void BeginMatchmaking( TF_MatchmakingMode mode ); + bool BAllowMatchMakingInGame( void ) const; + /// Quit the current game and lobby, if any and go back to the main menu + void EndMatchmaking( bool bSendAbandonLobby = false ); + bool BExitMatchmakingAfterDisconnect( void ); + + /// Called to active the invite UI + void RequestActivateInvite(); + + void ConnectToServer( const char *connect ); + +// void StopFindingMatch(); +// void StartWatchingGame( const CSteamID &gameServerSteamID ); +// void StartWatchingGame( const CSteamID &gameServerSteamID, const CSteamID &watchServerSteamID ); +// void CancelWatchGameRequest(); + +// GCSDK::CGCClientSharedObjectCache *GetSOCache() { return m_pSOCache; } + +// void SetAutoSpectateCheckTime( float flAutoSpectateCheckTime ) { m_flAutoSpectateCheckTime = flAutoSpectateCheckTime; } +// +// // downloading files +// +// struct CDownloadingFile +// { +// uint32 m_unFileID; +// HTTPRequestHandle m_hRequestHandle; +// CCallResult< CTFGCClientSystem, HTTPRequestCompleted_t > m_Callback; +// char m_szLocalFilename[MAX_PATH]; +// char m_szRemoteURL[MAX_PATH]; +// }; +// CUtlVector<CDownloadingFile*> m_DownloadingFiles; +// +// void DownloadFile( const char *pszRemoteURL, const char *pszLocalFilename, bool bForceDownload = false ); +// void OnDownloadCompleted( HTTPRequestCompleted_t *arg, bool bFailed ); +// +// void SetTodayMessages( CMsgDOTATodayMessages *pMessages ) { m_TodayMessages = *pMessages; } +// CMsgDOTATodayMessages* GetTodayMessages() { return &m_TodayMessages; } +// void StartWatchingGameResponse( const CMsgWatchGameResponse &response ); + + // + // Search criteria + // + TF_MatchmakingMode GetSearchMode(); + + // What MvM challenges? + void GetSearchChallenges( CMvMMissionSet &challenges ); + void SetSearchChallenges( const CMvMMissionSet &challenges ); + + // Willing to join the game late? + bool GetSearchJoinLate(); + void SetSearchJoinLate( bool bJoinLate ); + + // Quickplay + EGameCategory GetQuickplayGameType(); + void SetQuickplayGameType( EGameCategory type ); + + // "Play for loot" - requires a ticket. If the challenge is beaten, then the winners + // get some loot + bool GetSearchPlayForBraggingRights(); + void SetSearchPlayForBraggingRights( bool bPlayForBraggingRights ); + +#ifdef USE_MVM_TOUR + int GetSearchMannUpTourIndex(); + void SetSearchMannUpTourIndex( int idxTour ); +#endif // USE_MVM_TOUR + + // Casual matchmaking groups and categories + void SelectCasualMap( uint32 nMapDefIndex, bool bSelected ); + bool IsCasualMapSelected( uint32 nMapDefIndex ) const; + void ClearCasualSearchCriteria(); + void SaveCasualSearchCriteriaToDisk(); + void LoadCasualSearchCriteria(); + + // Update custom MM ping setting from the convar + void UpdateCustomPingTolerance(); + + // Check if the local player is doubling down + bool GetLocalPlayerSquadSurplus(); + void SetLocalPlayerSquadSurplus( bool bSquadSurplus ); + + // Ladders + uint32 GetLadderType(); + void SetLadderType( uint32 nType ); + + // World status + const CMsgTFWorldStatus &WorldStatus() const { return m_WorldStatus; } + + static const char *k_pszSteamLobbyKey_PartyID; + + /// Accept the invite, join the specified lobby + void AcceptFriendInviteToJoinLobby( const CSteamID &steamIDLobby ); + + /// Return true if we're the leader of the party. + /// NOTE: Returns true if we don't have a party! + bool BIsPartyLeader(); + + bool BHasOutstandingMatchmakingPartyMessage() const; + + enum ELobbyMsgType + { + k_eLobbyMsg_UserChat, + k_eLobbyMsg_SystemMsgFromLeader, + }; + + /// Chat (though the steam lobby) + void SendSteamLobbyChat( ELobbyMsgType eType, const char *pszText ); + + /// See if we've got a ticket + static bool BLocalPlayerInventoryHasMvmTicket( void ); + static int GetLocalPlayerInventoryMvmTicketCount( void ); + + /// See if we've got a double-down + static bool BLocalPlayerInventoryHasSquadSurplusVoucher( void ); + static int GetLocalPlayerInventorySquadSurplusVoucherCount( void ); + +#ifdef USE_MVM_TOUR + /// Get info about the local player's badge. Returns false if we can't + /// find his inventory or he doesn't own a badge + static bool BGetLocalPlayerBadgeInfoForTour( int iTourIndex, uint32 *pnBadgeLevel, uint32 *pnCompletedChallenges ); +#endif // USE_MVM_TOUR + + struct MatchMakerHealthData_t + { + float m_flRatio; + Color m_colorBar; + CUtlString m_strLocToken; + }; + + MatchMakerHealthData_t GetHealthBracketForRatio( float flRatio ) const; + + uint32 GetMostSearchedCount() const { return m_nMostSearchedMapCount; } + MatchMakerHealthData_t GetOverallHealthDataForLocalCriteria() const; + MatchMakerHealthData_t GetHealthDataForMap( uint32 nMapIndex ) const; + void RequestMatchMakerStats() const; + void SetMatchMakerStats( const CMsgGCMatchMakerStatsResponse newStats ); + const CMsgGCMatchMakerStatsResponse &GetMatchMakerStats() { return m_MatchMakerStats; } + const CUtlDict< float > &GetDataCenterPopulationRatioDict( EMatchGroup eMatchGroup ) { return m_dictDataCenterPopulationRatio[ eMatchGroup ]; } + + void AcknowledgePendingXPSources( EMatchGroup eMatchGroup ) const; + void AcknowledgeNotification( uint32 nAccountID, uint64 ulNotificationID ) const; + + void SetSurveyRequest( const CMsgGCSurveyRequest& msgSurveyRequest ); + const CMsgGCSurveyRequest& GetSurveyRequest() const { return m_msgSurveyRequest; } + void SendSurveyResponse( int32 nResponse ); + void ClearSurveyRequest(); + + /// Most recent matchmaking progress stats received + CMsgMatchmakingProgress m_msgMatchmakingProgress; + + bool BConnectedToMatchServer( bool bLiveMatch ); + + // !! Does NOT mean you're *in* this match. See Above. + bool BHaveLiveMatch() const; + EAbandonGameStatus GetAssignedMatchAbandonStatus(); + bool BUserWantsToBeInMatchmaking() const { return m_bUserWantsToBeInMatchmaking; } + + EMatchGroup GetLiveMatchGroup() const; + + // Helper that combines GetMatchAbandonStatus and BConnectedToMatch as this is usually what you're asking. + EAbandonGameStatus GetCurrentServerAbandonStatus() + { + return BConnectedToMatchServer( true ) ? GetAssignedMatchAbandonStatus() : k_EAbandonGameStatus_Safe; + } + + void RejoinLobby( bool bConfirmed ); + bool JoinMMMatch(); + + void LeaveGameAndPrepareToJoinParty( GCSDK::PlayerGroupID_t nPartyID ); + bool BIsPhoneVerified( void ); + bool BIsPhoneIdentifying( void ); + bool BHasCompetitiveAccess( void ); + + bool BIsIPRecentMatchServer( netadr_t ip ) { return m_vecMatchServerHistory.Find( ip ) != m_vecMatchServerHistory.InvalidIndex(); } + + void AddLocalPlayerSOListener( ISharedObjectListener* pListener, bool bImmedately = true ); + void RemoveLocalPlayerSOListener( ISharedObjectListener* pListener ); + +#ifdef TF_GC_PING_DEBUG + void SetPingOverride( const char *pszDataCenter, uint32 nPing, CMsgGCDataCenterPing_Update_Status eStatus ); + void ClearPingOverrides(); +#endif + +protected: + + // CGCClientSystem + virtual void PreInitGC() OVERRIDE; + virtual void PostInitGC() OVERRIDE; + + virtual void ReceivedClientWelcome( const CMsgClientWelcome &msg ) OVERRIDE; + +private: + friend class CGCClientAcceptInviteResponse; + friend class CGCWorldStatusBroadcast; +// void CreateSourceTVProxy( uint32 source_tv_public_addr, uint32 source_tv_private_addr, uint32 source_tv_port ); + + void PingThink(); + + CMsgCreateOrUpdateParty *GetCreateOrUpdatePartyMsg(); + CSendCreateOrUpdatePartyMsgJob *m_pPendingCreateOrUpdatePartyMsg; + float m_flSendPartyUpdateMessageTime; + + void SetWorldStatus( CMsgTFWorldStatus &status ) { m_WorldStatus = status; } + + CMsgGCMatchMakerStatsResponse m_MatchMakerStats; + uint32 m_nMostSearchedMapCount; + + CMsgTFWorldStatus m_WorldStatus; + +// uint32 m_unCurrentMatchID; + bool m_bRegisteredSharedObjects; + bool m_bInittedGC; + + EMatchmakingUIState m_eMatchmakingUIState; + + /// The lobby we joined/created (presumably) for matchmaking purposes + CSteamID m_steamIDLobby; + + /// The lobby we have accepted the invite for, but not yet joined. + /// (We'll do it when there's a good opportunity) + CSteamID m_steamIDLobbyInviteAccepted; + + enum EAcceptInviteStep + { + eAcceptInviteStep_None, + eAcceptInviteStep_ReadyToJoinSteamLobby, + eAcceptInviteStep_JoinSteamLobby, + eAcceptInviteStep_GetLobbyMetadata, + eAcceptInviteStep_JoinParty, + }; + EAcceptInviteStep m_eAcceptInviteStep; + + /// Status of creating lobby. + int m_eCreateLobbyStatus; + + /// Check if we're in a steam lobby, then leave it + void LeaveSteamLobby(); + + /// Should we active the invite UI at the next opportunity? + bool m_bWantToActivateInviteUI; + + // The gameserver is authoritative on matches once we are assigned, so even if the lobby is lost or stale, these + // control: Where our assigned match is, and if we consider ourselves absolved of it. + CSteamID m_steamIDGCAssignedMatch; + // So we can consider the match over, based on the gameserver telling us so (or us abandoning). Once the lobby state + // via the GC agrees, SOChanged will clear. + bool m_bAssignedMatchEnded; + EMatchGroup m_eAssignedMatchGroup; + uint64 m_uAssignedMatchID; + // History of assigned matches so things like the server browser can reason about our connect history. + CUtlVector< netadr_t > m_vecMatchServerHistory; + + // Set when m_steamIDAssignedServer changes for the next Update() + bool m_bServerAssignmentChanged; + + // SDR ping system + RTime32 m_rtLastPingFix; + bool m_bPendingPingRefresh; + bool m_bSentInitialPingFix; + // Cached ping data message as of rtLastPingFix + CMsgGCDataCenterPing_Update m_msgCachedPingUpdate; + +#ifdef TF_GC_PING_DEBUG + CMsgGCDataCenterPing_Update m_msgPingOverrides; +#endif + + // Asks user if they want to rejoin an existing lobby + float m_flCheckForRejoinTime; // Due to network race conditions, delay for a bit before we respond + void RejoinActiveMatch( void ); + +// float m_flGetNewsTime; +// float m_flAutoSpectateCheckTime; + + GCSDK::CGCClientSharedObjectCache *m_pSOCache; +// uint32 m_unWinCount; +// uint32 m_unLossCount; +// int m_nSignOnState; + + enum EConnectState + { + eConnectState_Disconnected, + eConnectState_ConnectingToMatchmade, + eConnectState_ConnectedToMatchmade, + eConnectState_NonmatchmadeServer, + }; + EConnectState m_eConnectState; + + bool IsConnectStateDisconnected() + { + if ( BAllowMatchMakingInGame() ) + { + return m_eConnectState != eConnectState_ConnectingToMatchmade && + m_eConnectState != eConnectState_ConnectedToMatchmade; + } + + return m_eConnectState == eConnectState_Disconnected; + } +// CUtlSortVector<GCHeroRecord_t, CGCHeroRecordLess> m_aHeroRecords; + +// KeyValues *m_pNewsKeys; + bool m_bGCUserSessionCreated; + bool m_bUserWantsToBeInMatchmaking; + GCSDK::PlayerGroupID_t m_nPendingAutoJoinPartyID; + + // Are we connected, and to whom + CSteamID m_steamIDCurrentServer; + + +// CMsgDOTATodayMessages m_TodayMessages; +// +// DOTAGameVersion m_GameVersion; + + void SendCreateOrUpdatePartyMsg( TF_Matchmaking_WizardStep eWizardStep ); + void SendExitMatchmaking( bool bExplicitAbandon ); + void FireGameEventLobbyUpdated(); + void FireGameEventPartyUpdated(); + + CMsgMatchSearchCriteria m_msgLocalSearchCriteria; + TF_Matchmaking_WizardStep m_eLocalWizardStep; + bool m_bLocalSquadSurplus; +// void CheckSendAdjustSearchCriteria(); + + void AssertMakesSenseToReadSearchCriteria(); + bool BAllowMatchmakingSearch(); +#ifdef USE_MVM_TOUR + bool BInternalSetSearchMannUpTourIndex( int idxTour ); +#endif // USE_MVM_TOUR + bool BInternalSetSearchChallenges( const CMvMMissionSet &challenges ); + + CCallback<CTFGCClientSystem, LobbyCreated_t, false> m_callbackSteamLobbyCreated; + CCallback<CTFGCClientSystem, LobbyEnter_t, false> m_callbackSteamLobbyEnter; + CCallback<CTFGCClientSystem, LobbyChatMsg_t, false> m_callbackSteamLobbyChatMsg; + CCallback<CTFGCClientSystem, GameLobbyJoinRequested_t, false> m_callbackSteamGameLobbyJoinRequested; + CCallback<CTFGCClientSystem, LobbyDataUpdate_t, false > m_callbackSteamLobbyDataUpdate; + CCallback<CTFGCClientSystem, LobbyChatUpdate_t, false > m_callbackSteamLobbyChatUpdate; + + void OnSteamLobbyCreated( LobbyCreated_t *pInfo ); + void OnSteamLobbyEnter( LobbyEnter_t *pInfo ); + void OnSteamLobbyChatMsg( LobbyChatMsg_t *pInfo ); + void OnSteamGameLobbyJoinRequested( GameLobbyJoinRequested_t *pInfo ); + void OnSteamLobbyDataUpdate( LobbyDataUpdate_t *pInfo ); + void OnSteamLobbyChatUpdate( LobbyChatUpdate_t *pInfo ); + + /// Check if we have a steam lobby. If we have one (and it's not the wrong one!) then return true. + /// Otherwise, initiate creation, if possible + /// + /// Returns: + /// -1 error + /// 0 in progress + /// 1 OK + int CheckSteamLobbyCreated(); + + /// Check if we need to associate the party and steam lobby with each other + void CheckAssociatePartyAndSteamLobby(); + + /// if we want to active the invite UI, and we're ready, then do it now! + void CheckReadyToActivateInvite(); + + /// Called when we fail to accept the invite + void OnFailedToAcceptInvite(); + + CUtlVector< ISharedObjectListener* > m_vecDelayedLocalPlayerSOListenersToAdd; + + CTFMatchMakingPopupPrompJoinHandler m_PromptJoinHandler; + CTFImmediateAutoJoinHandler m_AutoJoinHandler; + + CMsgGCSurveyRequest m_msgSurveyRequest; + + CUtlDict< float > m_dictDataCenterPopulationRatio[ k_nMatchGroup_Count ]; +}; + +CTFGCClientSystem* GTFGCClientSystem(); + +#endif // _INCLUDED_TF_GC_CLIENT_H |