//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #ifndef MATCHMAKING_H #define MATCHMAKING_H #ifdef _WIN32 #pragma once #endif #ifdef _WIN32 #include "winerror.h" #endif #include "utlmap.h" #include "inetmsghandler.h" #include "netmessages.h" #include "Session.h" #include "engine/imatchmaking.h" enum MMPACKETS { PTH_CONNECT, PTH_SYSTEMLINK_SEARCH, HTP_SYSTEMLINK_REPLY }; enum MMSTATE { MMSTATE_INITIAL, MMSTATE_IDLE, MMSTATE_CREATING, MMSTATE_MODIFYING, MMSTATE_ACCEPTING_CONNECTIONS, MMSTATE_SEARCHING, MMSTATE_WAITING_QOS, MMSTATE_BROWSING, MMSTATE_SESSION_CONNECTING, MMSTATE_SESSION_CONNECTED, MMSTATE_SESSION_DISCONNECTING, MMSTATE_HOSTMIGRATE_STARTINGMIGRATION, MMSTATE_HOSTMIGRATE_MIGRATING, MMSTATE_HOSTMIGRATE_WAITINGFORCLIENTS, MMSTATE_HOSTMIGRATE_WAITINGFORHOST, MMSTATE_GAME_LOCKED, // in ranked games, clients can't join after this point MMSTATE_PREGAME, MMSTATE_REPORTING_STATS, MMSTATE_POSTGAME, MMSTATE_GAME_ACTIVE, // clients are no longer in the lobby MMSTATE_LOADING, MMSTATE_CONNECTED_TO_SERVER, MMSTATE_INGAME, }; // For sending host data back in search results enum eGameState { GAMESTATE_INLOBBY, GAMESTATE_INPROGRESS }; #define HEARTBEAT_INTERVAL_LONG 1.0 // send a heartbeat every second during gameplay #define HEARTBEAT_INTERVAL_SHORT 0.1 // send a heartbeat ten times a second in the lobby #define HEARTBEAT_TIMEOUT 10.0 // time out if a heartbeat isn't recieved for ten seconds #if defined( _DEBUG ) #define HEARTBEAT_TIMEOUT_LOADING 300 // in debug loads take much longer #else #define HEARTBEAT_TIMEOUT_LOADING 100 // allow for longer communication gaps during map load #endif #define STARTGAME_COUNTDOWN 15.0 // start game countdown timer #define DISCONNECT_WAITTIME 1.0 // wait for the server to reply to our disconnect notification #define QOSLOOKUP_WAITTIME 20.0 // wait to get quality of service data about session hosts #define JOINREPLY_WAITTIME 15.0 // time to wait for the host to reply to our join request #define REPORTSTATS_WAITTIME 20.0 // time to wait for clients to report their stats to live // 360 TCR's require system link searches complete in less than 3 seconds #define SYSTEMLINK_RETRYINTERVAL 1.f // in seconds #define SYSTEMLINK_MAXRETRIES 3 // number of tries before giving up #define SESSIONMODIRY_MAXWAITTIME 10 // max time for clients to update their session properties #define REGISTRATION_MAXWAITTIME 10 // max time for clients to register #define HOSTMIGRATION_RETRYINTERVAL 1.0 // in seconds #define HOSTMIGRATION_MAXRETRIES 10 // max migrate sends to clients #define HOSTMIGRATION_MAXWAITTIME 10 // time to wait for a new host to contact us #define MAX_SEARCHRESULTS 20 // Maximum number of results when searching for a session. #define PING_MAX_GREEN 70 #define PING_MAX_YELLOW 140 #define PING_MAX_RED 250 #define VOICE_STATUS_OFF 0 #define VOICE_STATUS_IDLE 1 #define VOICE_STATUS_TALKING 2 // HACK: For simplicity, we know TF has two teams plus spectator. #define MAX_TEAMS 3 #define MAX_PLAYERS 16 #define VOICE_ICON_BLINK_TIME 0.5 class CMatchmaking : public IMatchmaking, public IMatchmakingMessageHandler, public IClientMessageHandler, public INetChannelHandler, public IConnectionlessPacketHandler { public: CMatchmaking(); ~CMatchmaking(); // IMatchmaking implementation virtual void SessionNotification( const SESSION_NOTIFY notification, const int param = 0 ); virtual void AddSessionProperty( const uint nType, const char *pID, const char *pValue, const char *pValueType ); virtual void SetSessionProperties( KeyValues *pPropertyKeys ); virtual void SelectSession( uint sessionIdx ); virtual void ModifySession(); virtual void UpdateMuteList(); virtual void StartHost( bool bSystemLink = false ); virtual void StartClient( bool bSystemLink = false ); virtual bool StartGame(); virtual bool CancelStartGame(); virtual void ChangeTeam( const char *pTeamName ); virtual void TellClientsToConnect(); virtual void CancelCurrentOperation(); virtual void EndStatsReporting(); virtual void JoinInviteSessionByID( XNKID nSessionID ); virtual void JoinInviteSession( XSESSION_INFO *pHostInfo ); virtual void KickPlayerFromSession( uint64 id ); // For GameUI virtual KeyValues *GetSessionProperties(); // For voice chat virtual uint64 PlayerIdToXuid( int playerId ); virtual bool IsPlayerMuted( int iUserId, XUID id ); // To determine host Quality-of-Service virtual MM_QOS_t GetQosWithLIVE(); virtual bool PreventFullServerStartup(); // IConnectionlessPacketHandler implementation (Host/Client shared) virtual bool ProcessConnectionlessPacket( netpacket_t * packet ); // INetChannelHandler implementation virtual void ConnectionStart(INetChannel *chan); // called first time network channel is established virtual void PacketEnd(); // all messages have been parsed // NetChannel message handlers PROCESS_NET_MESSAGE( Tick ) { return true; } PROCESS_NET_MESSAGE( SetConVar ) { return true; } PROCESS_NET_MESSAGE( StringCmd ) { return true; } PROCESS_NET_MESSAGE( SignonState ) { return true; } PROCESS_CLC_MESSAGE( VoiceData ); PROCESS_CLC_MESSAGE( ClientInfo ) { return true; } PROCESS_CLC_MESSAGE( Move ) { return true; } PROCESS_CLC_MESSAGE( BaselineAck ) { return true; } PROCESS_CLC_MESSAGE( ListenEvents ) { return true; } PROCESS_CLC_MESSAGE( RespondCvarValue ) { return true; } PROCESS_CLC_MESSAGE( FileCRCCheck ) { return true; } PROCESS_CLC_MESSAGE( FileMD5Check ) { return true; } PROCESS_CLC_MESSAGE( SaveReplay ) { return true; } PROCESS_CLC_MESSAGE( CmdKeyValues ) { return true; } PROCESS_MM_MESSAGE( JoinResponse ); PROCESS_MM_MESSAGE( ClientInfo ); PROCESS_MM_MESSAGE( RegisterResponse ); PROCESS_MM_MESSAGE( Migrate ); PROCESS_MM_MESSAGE( Mutelist ); PROCESS_MM_MESSAGE( Checkpoint ); PROCESS_MM_MESSAGE( Heartbeat ) { return true; } // (Not used) virtual void ConnectionClosing(const char *reason) {}; // network channel is being closed by remote site virtual void ConnectionCrashed(const char *reason) {}; // network error occurred virtual void PacketStart(int incoming_sequence, int outgoing_acknowledged) {}; // called each time a new packet arrived virtual void FileRequested(const char *fileName, unsigned int transferID ) {}; // other side request a file for download virtual void FileReceived(const char *fileName, unsigned int transferID ) {}; // we received a file virtual void FileDenied(const char *fileName, unsigned int transferID ) {}; // a file request was denied by other side virtual void FileSent(const char *fileName, unsigned int transferID ) {}; // a file was sent // Debugging helpers void ShowSessionInfo(); void SendDevMessage( const char *message ); void SetSessionSlots( const uint nSlotsTotal, const uint nSlotsPrivate ); void RunFrame(); void EndGame(); void AddLocalPlayersToTeams(); void OnLevelLoadingFinished(); void TestSendMessage(); void TestStats(); bool GameIsActive(); void PrintVoiceStatus( void ); private: // NetChannel send void SendMessage( INetMessage *msg, netadr_t *adr, bool bVoice = false ); void SendMessage( INetMessage *msg, CClientInfo *pClient, bool bVoice = false ); void SendToRemoteClients( INetMessage *msg, bool bVoice = false, XUID excludeXUID = -1 ); // Session Host void OnHostSessionCreated(); void UpdateAcceptingConnections(); void SendModifySessionMessage(); void EndSessionModify(); bool IsAcceptingConnections(); void HandleSystemLinkSearch( netpacket_t *pPacket ); void HandleJoinRequest( netpacket_t *pPacket ); void StartCountdown(); void CancelCountdown(); void UpdatePregame(); void UpdateRegistration(); void UpdateSessionModify(); void ProcessRegistrationResults(); void UpdateServerNegotiation(); void UpdateSessionReplyData( uint flags ); void SwitchToNextOpenTeam( CClientInfo *pClient ); void SetupTeams(); int ChooseTeam(); int GetPlayersNeeded(); // Session Client bool StartSystemLinkSearch(); void HandleSystemLinkReply( netpacket_t *pPacket ); bool SearchForSession(); void UpdateSearch(); void UpdateQosLookup(); void CancelSearch(); void CancelQosLookup(); void ClearSearchResults(); void SendJoinRequest( netadr_t *adr ); bool ConnectToHost(); void UpdateConnecting(); void ApplySessionProperties( int numContexts, int numProperties, XUSER_CONTEXT *pContexts, XUSER_PROPERTY *pProperties ); // Host/Client shared bool InitializeLocalClient( bool bIsHost ); void AddPlayersToSession( CClientInfo *pClient ); void SendPlayerInfoToLobby( CClientInfo *pClient, int iHostIdx = -1 ); void RemovePlayersFromSession( CClientInfo *pClient ); void ClientDropped( CClientInfo *pClient ); void PerformDisconnect(); void GenerateMutelist( MM_Mutelist *pMsg ); int FindOrCreateContext( const uint id ); int FindOrCreateProperty( const uint id ); void AddSessionPropertyInternal( KeyValues *pProperty ); // Host Migration CClientInfo *SelectNewHost(); void StartHostMigration(); void BeginHosting(); void TellClientsToMigrate(); void SwitchToNewHost(); void EndMigration(); // General utility functions void Cleanup(); void SwitchToState( int newState ); double GetTime(); void SendHeartbeat(); bool SendHeartbeat( CClientInfo *pClient ); void ClientInfoToNetMessage( MM_ClientInfo *pInfo, const CClientInfo *pClient ); void NetMessageToClientInfo( CClientInfo *pClient, const MM_ClientInfo *pInfo ); bool GameIsLocked(); bool IsInMigration(); bool IsServer(); bool ConnectedToServer(); // Netchannel handling INetChannel *CreateNetChannel( netadr_t *adr ); INetChannel *AddRemoteChannel( netadr_t *adr ); INetChannel *FindChannel( const unsigned int ip ); CClientInfo *FindClient( netadr_t *adr ); CClientInfo *FindClientByXUID( XUID xuid ); void SetChannelTimeout( netadr_t *adr, int timeout ); void RemoveRemoteChannel( netadr_t *adr, const char *pReason ); void MarkChannelForRemoval( netadr_t *adr ); void CleanupMarkedChannels(); void UpdateVoiceStatus( void ); void SetPreventFullServerStartup( bool bState, PRINTF_FORMAT_STRING char const *fmt, ... ); private: // Used by a systemlink host to reply to broadcast searches struct systemLinkInfo_s { char szHostName[MAX_PLAYER_NAME_LENGTH]; char szScenario[MAX_MAP_NAME]; int gameState; int gameTime; int iScenarioIndex; XUID xuid; XSESSION_SEARCHRESULT Result; }; hostData_s m_HostData; // pass host info back to searching clients CClientInfo m_Host; // the session host CClientInfo m_Local; // the local client CClientInfo *m_pNewHost; // new host when migrating the session CClientInfo *m_pGameServer; // the client that will act as the game server CUtlVector< CClientInfo* > m_Remote; // remote clients CUtlVector< char* > m_pSystemLinkResults; // results from a system link search XSESSION_SEARCHRESULT_HEADER *m_pSearchResults; // dynamic buffer to hold session search results // Arbitration registration XSESSION_REGISTRATION_RESULTS *m_pRegistrationResults; // QoS Data BOOL m_bQoSTesting; XNQOS m_QoSResult; XNQOS* m_pQoSResult; const XNADDR* m_QoSxnaddr[MAX_SEARCHRESULTS]; const XNKID* m_QoSxnkid[MAX_SEARCHRESULTS]; const XNKEY* m_QoSxnkey[MAX_SEARCHRESULTS]; CUtlMap< unsigned int, INetChannel* >m_Channels; CUtlVector< unsigned int > m_ChannelsToRemove; AsyncHandle_t m_hSearchHandle; CSession m_Session; int m_CurrentState; int m_PreMigrateState; double m_fNextHeartbeatTime; double m_fCountdownStartTime; double m_fRegistrationTimer; bool m_bCreatedLocalTalker; bool m_bInitialized; bool m_bCleanup; bool m_bPreventFullServerStartup; bool m_bEnteredLobby; int m_nHostOwnerId; int m_nGameSize; int m_nTotalTeams; int m_nPrivateSlots; int m_nQOSProbeCount; double m_fQOSProbeTimer; int m_nSendCount; double m_fSendTimer; double m_fWaitTimer; double m_fHeartbeatInterval; uint64 m_Nonce; // used in system link queries int CountPlayersOnTeam( int idxTeam ); // players on each team XUID m_Mutelist[MAX_PLAYERS_PER_CLIENT][MAX_PLAYERS]; CUtlVector< XUID > m_MutedBy[MAX_PLAYERS_PER_CLIENT]; // Contexts and properties CUtlVector< XUSER_CONTEXT > m_SessionContexts; // for session creation CUtlVector< XUSER_PROPERTY > m_SessionProperties; // for session creation CUtlVector< XUSER_PROPERTY > m_PlayerStats; KeyValues *m_pSessionKeys; // for GameUI lobby setup double m_flVoiceBlinkTime; XSESSION_INFO m_InviteSessionInfo; enum InviteState_t { INVITE_NONE, INVITE_PENDING, INVITE_VALIDATING, INVITE_AWAITING_STORAGE, INVITE_ACCEPTING } m_InviteState; struct InviteWaitingInfo_t { DWORD m_UserIdx; #if defined( _X360 ) XUSER_SIGNIN_INFO m_SignInInfo; #endif DWORD m_SignInState; BOOL m_PrivilegeMultiplayer; int m_InviteStorageDeviceSelected; int m_bAcceptingInvite; } m_InviteWaitingInfo; void RunFrameInvite(); void InviteCancel(); }; extern CMatchmaking *g_pMatchmaking; #endif // MATCHMAKING_H