csgo-2018-source/matchmaking/mm_session_online_teamsearch.cpp

859 lines
23 KiB
C++
Raw Normal View History

2021-07-25 12:11:47 +08:00
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//===========================================================================//
#include "mm_framework.h"
#include "vstdlib/random.h"
#include "fmtstr.h"
#include "steam_datacenterjobs.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
ConVar mm_teamsearch_errortime( "mm_teamsearch_errortime", "3.0", FCVAR_DEVELOPMENTONLY, "Time team search is in error state until it self-cancels" );
ConVar mm_teamsearch_nostart( "mm_teamsearch_nostart", "0", FCVAR_DEVELOPMENTONLY, "Team search will fake cancel before searching for server" );
#ifndef _GAMECONSOLE
ConVar sv_search_team_key( "sv_search_team_key", "public", FCVAR_RELEASE, "When initiating team search, set this key to match with known opponents team" );
#endif
//
//
// Specialized implementation of SysSessions for team search
//
//
template < typename TBaseSession >
class CSysSessionStubForTeamSearch : public TBaseSession
{
public:
explicit CSysSessionStubForTeamSearch( KeyValues *pSettings, CMatchSessionOnlineTeamSearch *pMatchSession ) :
TBaseSession( pSettings ),
m_pMatchSession( pMatchSession )
{
}
protected:
virtual void OnSessionEvent( KeyValues *notify )
{
if ( !notify )
return;
if ( m_pMatchSession )
m_pMatchSession->OnSessionEvent( notify );
notify->deleteThis();
}
protected:
// No voice
virtual void Voice_ProcessTalkers( KeyValues *pMachine, bool bAdd ) {}
virtual void Voice_CaptureAndTransmitLocalVoiceData() {}
virtual void Voice_Playback( KeyValues *msg, XUID xuidSrc ) OVERRIDE {}
virtual void Voice_UpdateLocalHeadsetsStatus() {}
virtual void Voice_UpdateMutelist() {}
protected:
// No p2p connections, just two hosts talking
virtual void XP2P_Interconnect() {}
protected:
CMatchSessionOnlineTeamSearch *m_pMatchSession;
};
typedef CSysSessionStubForTeamSearch< CSysSessionClient > CSysTeamSearchClient;
typedef CSysSessionStubForTeamSearch< CSysSessionHost > CSysTeamSearchHost;
//
//
// CMatchSessionOnlineTeamSearch implementation
//
//
CMatchSessionOnlineTeamSearch::CMatchSessionOnlineTeamSearch( KeyValues *pSettings, CMatchSessionOnlineHost *pHost ) :
m_pHostSession( pHost ),
m_eState( STATE_SEARCHING ),
m_pSysSessionHost( NULL ),
m_pSysSessionClient( NULL ),
m_pDsSearcher( NULL ),
m_flActionTime( 0.0f ),
m_pUpdateHostSessionPacket( NULL ),
m_autodelete_pUpdateHostSessionPacket( m_pUpdateHostSessionPacket ),
m_iLinkState( 0 ),
m_xuidLinkPeer( 0ull ),
#ifdef _X360
m_pXlspConnection( NULL ),
m_pXlspCommandBatch( NULL ),
#endif
m_flCreationTime( Plat_FloatTime() )
{
m_pSettings = pSettings->MakeCopy();
m_autodelete_pSettings.Assign( m_pSettings );
#ifndef _GAMECONSOLE
m_pSettings->SetString( "options/searchteamkey", sv_search_team_key.GetString() );
#endif
if ( IsPC() )
{
// On PC limit server selection for team games to official and listen
if ( Q_stricmp( m_pSettings->GetString( "options/server" ), "listen" ) )
m_pSettings->SetString( "options/server", "official" );
}
DevMsg( "Created CMatchSessionOnlineTeamSearch:\n" );
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
}
CMatchSessionOnlineTeamSearch::~CMatchSessionOnlineTeamSearch()
{
DevMsg( "Destroying CMatchSessionOnlineTeamSearch.\n" );
}
CMatchSearcher * CMatchSessionOnlineTeamSearch::OnStartSearching()
{
return new CMatchSearcher_OnlineTeamSearch( this, m_pSettings->MakeCopy() );
}
CMatchSearcher_OnlineTeamSearch::CMatchSearcher_OnlineTeamSearch( CMatchSessionOnlineTeamSearch *pSession, KeyValues *pSettings ) :
CMatchSearcher_OnlineSearch( pSession, pSettings )
{
// Search settings cannot be locked as it will interfere with syssessions
m_pSettings->SetString( "system/lock", "" );
// Team versus searchable lobbies are public
m_pSettings->SetString( "system/access", "public" );
// If we happen to host the session it will be a special teamlink session
m_pSettings->SetString( "system/netflag", "teamlink" );
if ( IMatchSession *pMainSession = g_pMatchFramework->GetMatchSession() )
{
KeyValues *kvSystemData = pMainSession->GetSessionSystemData();
m_pSettings->SetUint64( "System/dependentlobby", kvSystemData->GetUint64( "xuidReserve" ) );
}
// Double the number of slots since we assume two teams playing
m_pSettings->SetInt( "Members/numSlots", g_pMatchFramework->GetMatchTitle()->GetTotalNumPlayersSupported() );
// Let the title extend the game settings
g_pMMF->GetMatchTitleGameSettingsMgr()->InitializeGameSettings( m_pSettings, "search_onlineteam" );
DevMsg( "CMatchSearcher_OnlineTeamSearch title adjusted settings:\n" );
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
}
void CMatchSearcher_OnlineTeamSearch::StartSearchPass( KeyValues *pSearchPass )
{
#ifndef _GAMECONSOLE
pSearchPass->SetString( "Filter=/options:searchteamkey", sv_search_team_key.GetString() );
#endif
CMatchSearcher_OnlineSearch::StartSearchPass( pSearchPass );
}
void CMatchSessionOnlineTeamSearch::OnSearchEvent( KeyValues *pNotify )
{
if ( !pNotify )
return;
if ( m_pHostSession )
m_pHostSession->OnEvent( pNotify );
pNotify->deleteThis();
}
void CMatchSessionOnlineTeamSearch::OnSessionEvent( KeyValues *pEvent )
{
OnEvent( pEvent );
// Searching state is handled entirely by the base class
if ( m_eState == STATE_SEARCHING )
return;
char const *szEvent = pEvent->GetName();
if ( !Q_stricmp( "mmF->SysSessionUpdate", szEvent ) )
{
if ( m_pSysSessionHost && pEvent->GetPtr( "syssession", NULL ) == m_pSysSessionHost )
{
// We had a session error
if ( char const *szError = pEvent->GetString( "error", NULL ) )
{
// Destroy the session
m_pSysSessionHost->Destroy();
m_pSysSessionHost = NULL;
// Handle error
m_eState = STATE_ERROR;
m_flActionTime = Plat_FloatTime() + mm_teamsearch_errortime.GetFloat();
OnSearchEvent( new KeyValues(
"OnMatchSessionUpdate",
"state", "progress",
"progress", "searcherror"
) );
return;
}
// This is our session
switch ( m_eState )
{
case STATE_CREATING:
// Session created successfully and we are awaiting peer
m_eState = STATE_AWAITING_PEER;
OnSearchEvent( new KeyValues(
"OnMatchSessionUpdate",
"state", "progress",
"progress", "searchawaitingpeer"
) );
break;
}
}
}
else if ( !Q_stricmp( "mmF->SysSessionCommand", szEvent ) )
{
if ( m_pSysSessionHost && pEvent->GetPtr( "syssession", NULL ) == m_pSysSessionHost )
{
KeyValues *pCommand = pEvent->GetFirstTrueSubKey();
if ( pCommand )
{
OnRunSessionCommand( pCommand );
}
}
if ( m_pSysSessionClient && pEvent->GetPtr( "syssession", NULL ) == m_pSysSessionClient )
{
KeyValues *pCommand = pEvent->GetFirstTrueSubKey();
if ( pCommand )
{
OnRunSessionCommand( pCommand );
}
}
}
else if ( !Q_stricmp( "OnPlayerMachinesConnected", szEvent ) )
{
// Another team is challenging us
m_eState = STATE_LINK_HOST;
m_xuidLinkPeer = pEvent->GetUint64( "id" );
OnSearchEvent( new KeyValues(
"OnMatchSessionUpdate",
"state", "progress",
"progress", "searchlinked"
) );
// Lock the session to prevent other teams challenges
m_pSettings->SetString( "system/lock", "linked" );
m_pSysSessionHost->OnUpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString(
"update",
" update { "
" system { "
" lock linked "
" } "
" } "
) ) );
LinkHost().LinkInit();
}
else if ( !Q_stricmp( "OnPlayerRemoved", szEvent ) )
{
// Assert( m_xuidLinkPeer == pEvent->GetUint64( "xuid" ) );
// the peer gave up before we finished negotiation, start the process all over
ResetAndRestartTeamSearch();
}
}
void CMatchSessionOnlineTeamSearch::ResetAndRestartTeamSearch()
{
m_eState = STATE_ERROR;
m_flActionTime = 0.0f;
OnSearchEvent( new KeyValues(
"OnMatchSessionUpdate",
"state", "progress",
"progress", "restart"
) );
}
void CMatchSessionOnlineTeamSearch::RememberHostSessionUpdatePacket( KeyValues *pPacket )
{
if ( m_pUpdateHostSessionPacket )
m_pUpdateHostSessionPacket->deleteThis();
m_pUpdateHostSessionPacket = pPacket;
m_autodelete_pUpdateHostSessionPacket.Assign( m_pUpdateHostSessionPacket );
}
void CMatchSessionOnlineTeamSearch::ApplyHostSessionUpdatePacket()
{
if ( m_pUpdateHostSessionPacket )
{
KeyValues *pUpdatePkt = m_pUpdateHostSessionPacket;
m_pUpdateHostSessionPacket = NULL;
m_autodelete_pUpdateHostSessionPacket.Assign( NULL );
m_pHostSession->UpdateSessionSettings( pUpdatePkt );
pUpdatePkt->deleteThis();
}
}
void CMatchSessionOnlineTeamSearch::OnRunSessionCommand( KeyValues *pCommand )
{
switch ( m_eState )
{
case STATE_LINK_HOST:
LinkHost().LinkCommand( pCommand );
break;
case STATE_LINK_CLIENT:
LinkClient().LinkCommand( pCommand );
break;
}
}
CMatchSessionOnlineTeamSearchLinkHost & CMatchSessionOnlineTeamSearch::LinkHost()
{
return static_cast< CMatchSessionOnlineTeamSearchLinkHost & >( *this );
}
CMatchSessionOnlineTeamSearchLinkClient & CMatchSessionOnlineTeamSearch::LinkClient()
{
return static_cast< CMatchSessionOnlineTeamSearchLinkClient & >( *this );
}
CSysSessionBase * CMatchSessionOnlineTeamSearch::LinkSysSession()
{
switch ( m_eState )
{
case STATE_LINK_HOST:
return m_pSysSessionHost;
case STATE_LINK_CLIENT:
return m_pSysSessionClient;
default:
Assert( !m_pSysSessionClient || !m_pSysSessionHost );
if ( m_pSysSessionHost )
return m_pSysSessionHost;
else if ( m_pSysSessionClient )
return m_pSysSessionClient;
else
return NULL;
}
}
CSysSessionClient * CMatchSessionOnlineTeamSearch::OnBeginJoiningSearchResult()
{
return new CSysTeamSearchClient( m_pSettings, this );
}
void CMatchSessionOnlineTeamSearch::OnSearchCompletedSuccess( CSysSessionClient *pSysSession, KeyValues *pSettings )
{
// Push the settings back into searcher since we are going to be using them
m_pSettings = pSettings;
m_autodelete_pSettings.Assign( m_pSettings );
m_pSysSessionClient = pSysSession;
// Established connection with another team
m_eState = STATE_LINK_CLIENT;
m_xuidLinkPeer = pSysSession->GetHostXuid();
OnSearchEvent( new KeyValues(
"OnMatchSessionUpdate",
"state", "progress",
"progress", "searchlinked"
) );
LinkClient().LinkInit();
}
void CMatchSessionOnlineTeamSearch::OnSearchCompletedEmpty( KeyValues *pSettings )
{
// Push the settings back into searcher since we are going to be searching again
m_pSettings = pSettings;
m_autodelete_pSettings.Assign( m_pSettings );
// Idle out for some time
m_eState = STATE_CREATING;
// Notify everybody that our search idled out
OnSearchEvent( new KeyValues(
"OnMatchSessionUpdate",
"state", "progress",
"progress", "searchidle"
) );
// Allocate the hosting object to accept matches
m_pSysSessionHost = new CSysTeamSearchHost( m_pSettings, this );
}
void CMatchSessionOnlineTeamSearch::DebugPrint()
{
DevMsg( "CMatchSessionOnlineTeamSearch::CMatchSessionOnlineSearch\n" );
CMatchSessionOnlineSearch::DebugPrint();
DevMsg( "CMatchSessionOnlineTeamSearch [ state=%d ]\n", m_eState );
DevMsg( " linkstate: %d\n", m_iLinkState );
DevMsg( " linkpeer: %llx\n", m_xuidLinkPeer );
DevMsg( " actiontime:%.3f\n", m_flActionTime ? m_flActionTime - Plat_FloatTime() : 0.0f );
if ( m_pDsSearcher )
DevMsg( "TeamSearch: Dedicated search in progress\n" );
else
DevMsg( "TeamSearch: Dedicated search not active\n" );
DevMsg( "TeamSearch: SysSession host state:\n" );
if ( m_pSysSessionHost )
m_pSysSessionHost->DebugPrint();
else
DevMsg( "SysSession is NULL\n" );
DevMsg( "TeamSearch: SysSession client state:\n" );
if ( m_pSysSessionClient )
m_pSysSessionClient->DebugPrint();
else
DevMsg( "SysSession is NULL\n" );
}
void CMatchSessionOnlineTeamSearch::OnEvent( KeyValues *pEvent )
{
CMatchSessionOnlineSearch::OnEvent( pEvent );
// Let the dedicated search handle the responses too
if ( m_pDsSearcher )
m_pDsSearcher->OnEvent( pEvent );
}
void CMatchSessionOnlineTeamSearch::Update()
{
switch ( m_eState )
{
case STATE_ERROR:
if ( m_flActionTime && Plat_FloatTime() > m_flActionTime )
{
m_flActionTime = 0.0f;
if ( m_pHostSession )
m_pHostSession->Command( KeyValues::AutoDeleteInline( new KeyValues( "Stop" ) ) );
}
return;
case STATE_AWAITING_PEER:
break;
case STATE_LINK_CLIENT:
LinkClient().LinkUpdate();
break;
case STATE_LINK_HOST:
LinkHost().LinkUpdate();
break;
}
CMatchSessionOnlineSearch::Update();
if ( CSysSessionBase *pLinkSysSession = LinkSysSession() )
pLinkSysSession->Update();
}
void CMatchSessionOnlineTeamSearch::Destroy()
{
if ( m_pSysSessionHost )
{
m_pSysSessionHost->Destroy();
m_pSysSessionHost = NULL;
}
if ( m_pSysSessionClient )
{
m_pSysSessionClient->Destroy();
m_pSysSessionClient = NULL;
}
if ( m_pDsSearcher )
{
m_pDsSearcher->Destroy();
m_pDsSearcher = NULL;
}
#ifdef _X360
if ( m_pXlspCommandBatch )
{
m_pXlspCommandBatch->Destroy();
m_pXlspCommandBatch = NULL;
}
if ( m_pXlspConnection )
{
m_pXlspConnection->Destroy();
m_pXlspConnection = NULL;
}
#endif
CMatchSessionOnlineSearch::Destroy();
}
//////////////////////////////////////////////////////////////////////////
//
// Link base implementation
//
//
void CMatchSessionOnlineTeamSearchLinkBase::LinkUpdate()
{
switch ( m_iLinkState )
{
case STATE_SEARCHING_DEDICATED:
Assert( m_pDsSearcher );
if ( m_pDsSearcher )
{
m_pDsSearcher->Update();
if ( m_pDsSearcher->IsFinished() )
{
OnDedicatedSearchFinished();
return;
}
}
break;
case STATE_HOSTING_LISTEN_SERVER:
if ( KeyValues *pServer = m_pHostSession->GetSessionSettings()->FindKey( "server" ) )
{
// Listen server has started and we should notify the link peer
if ( KeyValues *pPeerCommand = new KeyValues( "TeamSearchLink::ListenClient" ) )
{
KeyValues::AutoDelete autodelete( pPeerCommand );
pPeerCommand->SetString( "run", "xuid" );
pPeerCommand->SetUint64( "runxuid", m_xuidLinkPeer );
pPeerCommand->AddSubKey( pServer->MakeCopy() );
pPeerCommand->SetString( "server/server", "externalpeer" ); // for other team it is not a listen server, but externalpeer
pPeerCommand->SetInt( "server/team", 2 ); // other team is nominated team 2
LinkSysSession()->Command( pPeerCommand );
}
m_iLinkState = STATE_LINK_FINISHED;
}
break;
}
}
void CMatchSessionOnlineTeamSearchLinkBase::LinkCommand( KeyValues *pCommand )
{
char const *szCommand = pCommand->GetName();
if ( !Q_stricmp( "TeamSearchLink::Dedicated", szCommand ) ||
!Q_stricmp( "TeamSearchLink::ListenClient", szCommand ) )
{
Assert( m_iLinkState == STATE_WAITING_FOR_PEER_SERVER );
KeyValues *pServerInfo = pCommand->FindKey( "server", false );
if ( !pServerInfo )
{
ResetAndRestartTeamSearch();
return;
}
// Apply host session update packet
ApplyHostSessionUpdatePacket();
// Notify our team about the server
if ( KeyValues *pOurTeamNotify = new KeyValues( CFmtStr( "TeamSearchResult::%s", szCommand + strlen( "TeamSearchLink::" ) ) ) )
{
pOurTeamNotify->AddSubKey( pServerInfo->MakeCopy() );
OnSearchEvent( pOurTeamNotify );
}
return;
}
}
void CMatchSessionOnlineTeamSearchLinkBase::StartHostingListenServer()
{
if ( mm_teamsearch_nostart.GetBool() )
{
// Fake-cancel the session for debugging
m_pHostSession->Command( KeyValues::AutoDeleteInline( new KeyValues( "Cancel" ) ) );
return;
}
m_iLinkState = STATE_HOSTING_LISTEN_SERVER;
// Update host session settings packet
ApplyHostSessionUpdatePacket();
OnSearchEvent( new KeyValues( "TeamSearchResult::ListenHost" ) );
}
void CMatchSessionOnlineTeamSearchLinkBase::StartDedicatedServerSearch()
{
if ( mm_teamsearch_nostart.GetBool() )
{
// Fake-cancel the session for debugging
m_pHostSession->Command( KeyValues::AutoDeleteInline( new KeyValues( "Cancel" ) ) );
return;
}
m_iLinkState = STATE_SEARCHING_DEDICATED;
// Notify everybody that we are searching for dedicated server now
OnSearchEvent( new KeyValues(
"OnMatchSessionUpdate",
"state", "progress",
"progress", "dedicated"
) );
m_pDsSearcher = new CDsSearcher( m_pSettings, m_pHostSession->GetSessionSystemData()->GetUint64( "xuidReserve" ), NULL );
}
void CMatchSessionOnlineTeamSearchLinkBase::OnDedicatedSearchFinished()
{
Assert( m_pDsSearcher );
CDsSearcher::DsResult_t dsResult = m_pDsSearcher->GetResult();
m_pDsSearcher->Destroy();
m_pDsSearcher = NULL;
if ( !dsResult.m_bDedicated )
{
StartHostingListenServer();
return;
}
//
// Serialize the information about the dedicated server
//
KeyValues *pServerInfo = new KeyValues( "server" );
dsResult.CopyToServerKey( pServerInfo );
pServerInfo->SetUint64( "reservationid", m_pHostSession->GetSessionSystemData()->GetUint64( "xuidReserve" ) );
// Apply host session update packet
ApplyHostSessionUpdatePacket();
//
// Notify the peer team about the server
//
if ( KeyValues *pPeerCommand = new KeyValues( "TeamSearchLink::Dedicated" ) )
{
KeyValues::AutoDelete autodelete( pPeerCommand );
pPeerCommand->SetString( "run", "xuid" );
pPeerCommand->SetUint64( "runxuid", m_xuidLinkPeer );
pServerInfo->SetInt( "team", 2 );
pPeerCommand->AddSubKey( pServerInfo->MakeCopy() );
LinkSysSession()->Command( pPeerCommand );
}
// Notify our team about the server
if ( KeyValues *pOurTeamNotify = new KeyValues( "TeamSearchResult::Dedicated" ) )
{
pOurTeamNotify->SetPtr( "dsresult", &dsResult );
pServerInfo->SetInt( "team", 1 );
pOurTeamNotify->AddSubKey( pServerInfo );
OnSearchEvent( pOurTeamNotify );
}
m_iLinkState = STATE_LINK_FINISHED;
}
void CMatchSessionOnlineTeamSearchLinkBase::StartWaitingForPeerServer()
{
m_iLinkState = STATE_WAITING_FOR_PEER_SERVER;
// Notify everybody that peer is searching for game server
OnSearchEvent( new KeyValues(
"OnMatchSessionUpdate",
"state", "progress",
"progress", "peerserver"
) );
}
//////////////////////////////////////////////////////////////////////////
//
// Link host implementation
//
//
void CMatchSessionOnlineTeamSearchLinkHost::LinkInit()
{
m_iLinkState = STATE_SUBMIT_STATS;
}
void CMatchSessionOnlineTeamSearchLinkHost::LinkUpdate()
{
switch ( m_iLinkState )
{
case STATE_SUBMIT_STATS:
{
#ifdef _X360
m_pXlspConnection = new CXlspConnection( false );
CUtlVector< KeyValues * > arrCommands;
#endif
if ( KeyValues *pCmd = new KeyValues( "stat_agg" ) )
{
int numSeconds = int( 1 + Plat_FloatTime() - m_flCreationTime );
numSeconds = ClampArrayBounds( numSeconds, 30 * 60 ); // 30 minutes
pCmd->SetInt( "search_team_time", numSeconds );
#ifdef _X360
arrCommands.AddToTail( pCmd );
#elif !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
CGCClientJobUpdateStats *pJob = new CGCClientJobUpdateStats( pCmd );
pJob->StartJob( NULL );
#else
pCmd->deleteThis();
#endif
}
#ifdef _X360
m_pXlspCommandBatch = new CXlspConnectionCmdBatch( m_pXlspConnection, arrCommands );
#endif
}
m_iLinkState = STATE_REPORTING_STATS;
break;
case STATE_REPORTING_STATS:
#ifdef _X360
m_pXlspCommandBatch->Update();
if ( !m_pXlspCommandBatch->IsFinished() )
return;
m_pXlspCommandBatch->Destroy();
m_pXlspCommandBatch = NULL;
m_pXlspConnection->Destroy();
m_pXlspConnection = NULL;
#endif
m_iLinkState = STATE_CONFIRM_JOIN;
break;
case STATE_CONFIRM_JOIN:
if ( KeyValues *pPeerCommand = new KeyValues( "TeamSearchLink::HostConfirmJoinReady" ) )
{
KeyValues::AutoDelete autodelete( pPeerCommand );
pPeerCommand->SetString( "run", "xuid" );
pPeerCommand->SetUint64( "runxuid", m_xuidLinkPeer );
pPeerCommand->AddSubKey( m_pHostSession->GetSessionSettings()->MakeCopy() ); // Submit a copy of our session settings as well
m_pSysSessionHost->Command( pPeerCommand );
}
m_iLinkState = STATE_CONFIRM_JOIN_WAIT;
break;
case STATE_CONFIRM_JOIN_WAIT:
// Waiting for client to tell us how to select server
break;
}
CMatchSessionOnlineTeamSearchLinkBase::LinkUpdate();
}
void CMatchSessionOnlineTeamSearchLinkHost::LinkCommand( KeyValues *pCommand )
{
char const *szCommand = pCommand->GetName();
if ( !Q_stricmp( "TeamSearchLink::TeamLinkSessionUpdate", szCommand ) )
{
if ( KeyValues *pUpdatePkt = pCommand->GetFirstTrueSubKey() )
{
m_pSettings->MergeFrom( pUpdatePkt );
RememberHostSessionUpdatePacket( pUpdatePkt->MakeCopy() );
}
}
else if ( !Q_stricmp( "TeamSearchLink::HostHosting", szCommand ) )
{
// We are going to host
char const *szLinkHostServer = m_pSettings->GetString( "options/server", "" );
if ( !Q_stricmp( szLinkHostServer, "listen" ) )
StartHostingListenServer();
else
StartDedicatedServerSearch();
return;
}
else if ( !Q_stricmp( "TeamSearchLink::ClientHosting", szCommand ) )
{
// Our peer is going to host
StartWaitingForPeerServer();
return;
}
CMatchSessionOnlineTeamSearchLinkBase::LinkCommand( pCommand );
}
//////////////////////////////////////////////////////////////////////////
//
// Link client implementation
//
//
void CMatchSessionOnlineTeamSearchLinkClient::LinkInit()
{
m_iLinkState = STATE_WAITING_FOR_HOST_READY;
}
void CMatchSessionOnlineTeamSearchLinkClient::LinkUpdate()
{
switch ( m_iLinkState )
{
case STATE_WAITING_FOR_HOST_READY:
// We are waiting for host to report statistics and get the merged teamlink lobby ready
return;
case STATE_CONFIRM_JOIN:
// m_pSettings has been set to the aggregate of host settings
// and our members, so we can inspect host's preference of the Server to
// decide who is going to search for the game server
{
char const *szLinkHostServer = m_pSettings->GetString( "options/server", "" );
char const *szOurServer = m_pHostSession->GetSessionSettings()->GetString( "options/server", "" );
if ( Q_stricmp( szLinkHostServer, "listen" ) &&
!Q_stricmp( szOurServer, "listen" ) )
{
// Host doesn't care, but we need listen server
m_pSysSessionClient->Command( KeyValues::AutoDeleteInline( new KeyValues(
"TeamSearchLink::ClientHosting", "run", "host" ) ) );
StartHostingListenServer();
}
else
{
// Let host find the server
m_pSysSessionClient->Command( KeyValues::AutoDeleteInline( new KeyValues(
"TeamSearchLink::HostHosting", "run", "host" ) ) );
StartWaitingForPeerServer();
}
}
return;
}
CMatchSessionOnlineTeamSearchLinkBase::LinkUpdate();
}
void CMatchSessionOnlineTeamSearchLinkClient::LinkCommand( KeyValues *pCommand )
{
char const *szCommand = pCommand->GetName();
if ( !Q_stricmp( "TeamSearchLink::HostConfirmJoinReady", szCommand ) )
{
// Host has sent us full settings of the host session,
// let's try to reconcile and summarize the settings and prepare an update package if needed
KeyValues *pRemoteSettings = pCommand->GetFirstTrueSubKey();
KeyValues *pLocalSettings = m_pHostSession->GetSessionSettings();
if ( KeyValues *pUpdatePkt = g_pMMF->GetMatchTitleGameSettingsMgr()->PrepareTeamLinkForGame( pLocalSettings, pRemoteSettings ) )
{
if ( KeyValues *pHostCmd = new KeyValues( "TeamSearchLink::TeamLinkSessionUpdate", "run", "host" ) )
{
pHostCmd->AddSubKey( pUpdatePkt->MakeCopy() );
m_pSysSessionClient->Command( KeyValues::AutoDeleteInline( pHostCmd ) );
}
m_pSettings->MergeFrom( pUpdatePkt );
RememberHostSessionUpdatePacket( pUpdatePkt );
}
// Host is now ready
if ( m_iLinkState == STATE_WAITING_FOR_HOST_READY )
m_iLinkState = STATE_CONFIRM_JOIN;
return;
}
CMatchSessionOnlineTeamSearchLinkBase::LinkCommand( pCommand );
}