736 lines
21 KiB
C++
736 lines
21 KiB
C++
|
//========= Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ============//
|
|||
|
//
|
|||
|
// Purpose:
|
|||
|
//
|
|||
|
//=====================================================================================//
|
|||
|
|
|||
|
#ifndef _X360
|
|||
|
#include "xbox/xboxstubs.h"
|
|||
|
#endif
|
|||
|
|
|||
|
#include "mm_framework.h"
|
|||
|
|
|||
|
#if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS )
|
|||
|
#include "steam/matchmakingtypes.h"
|
|||
|
#endif
|
|||
|
|
|||
|
#include "proto_oob.h"
|
|||
|
#include "fmtstr.h"
|
|||
|
|
|||
|
// NOTE: This has to be the last file included!
|
|||
|
#include "tier0/memdbgon.h"
|
|||
|
|
|||
|
#pragma warning (disable : 4355 )
|
|||
|
|
|||
|
size_t const mm_filter_max_size = 128; // Maximum size of a filter key, it's key+value+3
|
|||
|
|
|||
|
static ConVar mm_server_search_update_interval( "mm_server_search_update_interval", "60", FCVAR_DEVELOPMENTONLY, "Interval between servers updates." );
|
|||
|
|
|||
|
static ConVar mm_server_search_inet_ping_interval( "mm_server_search_inet_ping_interval", "1.0", FCVAR_DEVELOPMENTONLY, "How long to wait between pinging internet server details." );
|
|||
|
static ConVar mm_server_search_inet_ping_timeout( "mm_server_search_inet_ping_timeout", "3.0", FCVAR_DEVELOPMENTONLY, "How long to wait for internet server details." );
|
|||
|
static ConVar mm_server_search_inet_ping_window( "mm_server_search_inet_ping_window", "10", FCVAR_DEVELOPMENTONLY, "How many servers can be pinged for server details in a batch." );
|
|||
|
static ConVar mm_server_search_inet_ping_refresh( "mm_server_search_inet_ping_refresh", "15", FCVAR_DEVELOPMENTONLY, "How often to refresh a listed server." );
|
|||
|
static ConVar mm_server_search_server_lifetime( "mm_server_search_server_lifetime", "180", FCVAR_DEVELOPMENTONLY, "How long until a server is no longer returned by the master till we remove it." );
|
|||
|
|
|||
|
static ConVar mm_server_search_lan_ping_interval( "mm_server_search_lan_ping_interval", "0.4", FCVAR_DEVELOPMENTONLY, "Interval between LAN discovery pings." );
|
|||
|
static ConVar mm_server_search_lan_ping_duration( "mm_server_search_lan_ping_duration", "1.0", FCVAR_DEVELOPMENTONLY, "Duration of LAN discovery ping phase." );
|
|||
|
|
|||
|
static ConVar mm_server_search_lan_ports( "mm_server_search_lan_ports",
|
|||
|
"27015,27016,27017,27018,27019,27020",
|
|||
|
FCVAR_RELEASE | FCVAR_ARCHIVE,
|
|||
|
"Ports to scan during LAN games discovery. Also used to discover and correctly connect to dedicated LAN servers behind NATs." );
|
|||
|
|
|||
|
//
|
|||
|
// Server implementation
|
|||
|
//
|
|||
|
|
|||
|
class CServerPinging : public CServer
|
|||
|
{
|
|||
|
public:
|
|||
|
CServerPinging() : m_flTimeout( 0 ) {}
|
|||
|
public:
|
|||
|
#if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS )
|
|||
|
gameserveritem_t m_gsi;
|
|||
|
#endif
|
|||
|
float m_flTimeout;
|
|||
|
};
|
|||
|
|
|||
|
CServer::CServer() :
|
|||
|
m_flLastRefresh( Plat_FloatTime() ),
|
|||
|
m_xuid( 0ull ),
|
|||
|
m_pGameDetails( NULL )
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
CServer::~CServer()
|
|||
|
{
|
|||
|
if ( m_pGameDetails )
|
|||
|
m_pGameDetails->deleteThis();
|
|||
|
m_pGameDetails = NULL;
|
|||
|
}
|
|||
|
|
|||
|
XUID CServer::GetOnlineId()
|
|||
|
{
|
|||
|
return m_xuid;
|
|||
|
}
|
|||
|
|
|||
|
KeyValues * CServer::GetGameDetails()
|
|||
|
{
|
|||
|
return m_pGameDetails;
|
|||
|
}
|
|||
|
|
|||
|
bool CServer::IsJoinable()
|
|||
|
{
|
|||
|
return m_pGameDetails != NULL;
|
|||
|
}
|
|||
|
|
|||
|
void CServer::Join()
|
|||
|
{
|
|||
|
char const *szConnectString = m_pGameDetails->GetString( "server/connectstring", NULL );
|
|||
|
if ( !szConnectString || !*szConnectString )
|
|||
|
return;
|
|||
|
|
|||
|
g_pMatchExtensions->GetIVEngineClient()->ClientCmd( CFmtStr( "connect %s\n", szConnectString ) );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Server manager implementation
|
|||
|
//
|
|||
|
|
|||
|
CServerManager::CServerManager()
|
|||
|
{
|
|||
|
#if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS )
|
|||
|
m_hRequest = NULL;
|
|||
|
#endif
|
|||
|
m_bUpdateEnabled = false;
|
|||
|
m_flNextUpdateTime = 0.0f;
|
|||
|
m_flNextServerUpdateTime = 0.0f;
|
|||
|
m_eState = STATE_IDLE;
|
|||
|
}
|
|||
|
|
|||
|
CServerManager::~CServerManager()
|
|||
|
{
|
|||
|
m_Servers.PurgeAndDeleteElements();
|
|||
|
m_ServersPinging.PurgeAndDeleteElements();
|
|||
|
|
|||
|
#if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS )
|
|||
|
if ( m_hRequest )
|
|||
|
steamapicontext->SteamMatchmakingServers()->ReleaseRequest( m_hRequest );
|
|||
|
m_hRequest = NULL;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
static CServerManager g_ServerManager;
|
|||
|
CServerManager *g_pServerManager = &g_ServerManager;
|
|||
|
|
|||
|
void CServerManager::EnableServersUpdate( bool bEnable )
|
|||
|
{
|
|||
|
if ( bEnable &&
|
|||
|
( g_pMatchFramework->GetMatchTitle()->GetTitleSettingsFlags() & MATCHTITLE_SERVERMGR_DISABLED ) )
|
|||
|
bEnable = false;
|
|||
|
|
|||
|
m_bUpdateEnabled = bEnable;
|
|||
|
m_flNextUpdateTime = 0.0f;
|
|||
|
|
|||
|
// If enabled the search, we'll pick it up next update
|
|||
|
if ( bEnable )
|
|||
|
return;
|
|||
|
|
|||
|
// Otherwise search is being disabled
|
|||
|
m_Servers.PurgeAndDeleteElements();
|
|||
|
m_ServersPinging.PurgeAndDeleteElements();
|
|||
|
|
|||
|
#if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS )
|
|||
|
if ( m_eState == STATE_FETCHING_SERVERS && m_hRequest )
|
|||
|
{
|
|||
|
steamapicontext->SteamMatchmakingServers()->ReleaseRequest( m_hRequest );
|
|||
|
m_hRequest = NULL;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
// Will clean up all servers and go IDLE
|
|||
|
OnAllDataFetched();
|
|||
|
}
|
|||
|
|
|||
|
int CServerManager::GetNumServers()
|
|||
|
{
|
|||
|
return m_Servers.Count();
|
|||
|
}
|
|||
|
|
|||
|
IMatchServer * CServerManager::GetServerByIndex( int iServerIdx )
|
|||
|
{
|
|||
|
return m_Servers.IsValidIndex( iServerIdx ) ? m_Servers[ iServerIdx ] : NULL;
|
|||
|
}
|
|||
|
|
|||
|
IMatchServer * CServerManager::GetServerByOnlineId( XUID xuidServerOnline )
|
|||
|
{
|
|||
|
return GetServerRecordByOnlineId( m_Servers, xuidServerOnline );
|
|||
|
}
|
|||
|
|
|||
|
CServer * CServerManager::GetServerRecordByOnlineId( CUtlVector< CServer * > &arr, XUID xuidServerOnline )
|
|||
|
{
|
|||
|
for ( int k = 0; k < arr.Count(); ++ k )
|
|||
|
{
|
|||
|
CServer *pServer = arr[ k ];
|
|||
|
if ( pServer && pServer->GetOnlineId() == xuidServerOnline )
|
|||
|
return pServer;
|
|||
|
}
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
void CServerManager::OnEvent( KeyValues *pEvent )
|
|||
|
{
|
|||
|
if ( IsX360() )
|
|||
|
return;
|
|||
|
|
|||
|
char const *szName = pEvent->GetName();
|
|||
|
|
|||
|
if ( !Q_stricmp( szName, "OnNetLanConnectionlessPacket" ) )
|
|||
|
{
|
|||
|
char const * arrServerProbeKeys[] = { "LanSearchServerPing", "ConnectServerDetailsRequest", "InetSearchServerDetails" };
|
|||
|
|
|||
|
for ( int k = 0; k < ARRAYSIZE( arrServerProbeKeys ); ++ k )
|
|||
|
{
|
|||
|
KeyValues *pProbeKey = pEvent->FindKey( arrServerProbeKeys[k] );
|
|||
|
if ( !pProbeKey )
|
|||
|
continue;
|
|||
|
|
|||
|
KeyValues *pDetails = g_pMatchFramework->GetMatchNetworkMsgController()->GetActiveServerGameDetails( pEvent );
|
|||
|
KeyValues::AutoDelete autodelete_pDetails( pDetails );
|
|||
|
if ( !pDetails )
|
|||
|
return;
|
|||
|
|
|||
|
if ( !pDetails->FindKey( "server" ) )
|
|||
|
return;
|
|||
|
|
|||
|
pDetails->FindKey( arrServerProbeKeys[k], true )->MergeFrom( pProbeKey, KeyValues::MERGE_KV_UPDATE );
|
|||
|
|
|||
|
g_pConnectionlessLanMgr->SendPacket( pDetails, pEvent->GetString( "from" ), INetSupport::NS_SOCK_SERVER );
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if ( KeyValues *pGameDetailsServer = pEvent->FindKey( "GameDetailsServer" ) )
|
|||
|
{
|
|||
|
// Incoming data:
|
|||
|
//
|
|||
|
// System
|
|||
|
// Game
|
|||
|
// Server
|
|||
|
// Members
|
|||
|
// LanSearchServerPing
|
|||
|
// timestamp = holds the time packet was sent for ping
|
|||
|
|
|||
|
// Server ping
|
|||
|
int nPing = 0;
|
|||
|
if ( float flTimeSent = pGameDetailsServer->GetFloat( "LanSearchServerPing/timestamp" ) )
|
|||
|
{
|
|||
|
float flSeconds = Plat_FloatTime() - flTimeSent;
|
|||
|
nPing = flSeconds * 1000;
|
|||
|
if ( nPing < 0 )
|
|||
|
nPing = 0;
|
|||
|
if ( nPing >= 1000 )
|
|||
|
nPing = 999;
|
|||
|
}
|
|||
|
else if ( uint64 xuidInetPing = pGameDetailsServer->GetUint64( "InetSearchServerDetails/pingxuid" ) )
|
|||
|
{
|
|||
|
CServerPinging *pServerPinging = ( CServerPinging * ) GetServerRecordByOnlineId( m_ServersPinging, xuidInetPing );
|
|||
|
if ( !pServerPinging )
|
|||
|
return;
|
|||
|
|
|||
|
if ( float flTimeSent = pGameDetailsServer->GetFloat( "InetSearchServerDetails/timestamp" ) )
|
|||
|
{
|
|||
|
float flSeconds = Plat_FloatTime() - flTimeSent;
|
|||
|
nPing = flSeconds * 1000;
|
|||
|
if ( nPing < 0 )
|
|||
|
nPing = 0;
|
|||
|
if ( nPing >= 1000 )
|
|||
|
nPing = 999;
|
|||
|
}
|
|||
|
#if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS )
|
|||
|
else
|
|||
|
{
|
|||
|
nPing = pServerPinging->m_gsi.m_nPing;
|
|||
|
}
|
|||
|
#endif
|
|||
|
// Remove the server from outstanding pings list
|
|||
|
m_ServersPinging.FindAndFastRemove( pServerPinging );
|
|||
|
delete pServerPinging;
|
|||
|
}
|
|||
|
else if ( char const *szDetailsAdr = pGameDetailsServer->GetString( "ConnectServerDetailsRequest/server" ) )
|
|||
|
{
|
|||
|
g_pMatchExtensions->GetINetSupport()->OnMatchEvent( pEvent );
|
|||
|
return;
|
|||
|
}
|
|||
|
else
|
|||
|
return;
|
|||
|
|
|||
|
// Server address
|
|||
|
char const *szAddr = pGameDetailsServer->GetString( "server/adronline", NULL );
|
|||
|
if ( !szAddr || !*szAddr )
|
|||
|
return;
|
|||
|
|
|||
|
// Determine server network address
|
|||
|
netadr_t netAddress;
|
|||
|
netAddress.SetType( NA_IP );
|
|||
|
netAddress.SetPort( PORT_SERVER );
|
|||
|
netAddress.SetFromString( szAddr );
|
|||
|
|
|||
|
// Check if this is not our local server
|
|||
|
INetSupport::ServerInfo_t si;
|
|||
|
g_pMatchExtensions->GetINetSupport()->GetServerInfo( &si );
|
|||
|
if ( si.m_bActive && ( si.m_netAdr.CompareAdr( netAddress ) || si.m_netAdrOnline.CompareAdr( netAddress ) ) )
|
|||
|
return;
|
|||
|
|
|||
|
// Coalesce it into its XUID online id
|
|||
|
XUID xuidOnline = uint64( netAddress.GetIPNetworkByteOrder() ) | ( uint64( netAddress.GetPort() ) << 32ull );
|
|||
|
|
|||
|
// Prepare the settings
|
|||
|
KeyValues *pSettings = pGameDetailsServer->MakeCopy();
|
|||
|
pSettings->SetName( "settings" );
|
|||
|
if ( KeyValues *kvLanSearch = pSettings->FindKey( "LanSearch" ) )
|
|||
|
{
|
|||
|
pSettings->RemoveSubKey( kvLanSearch );
|
|||
|
kvLanSearch->deleteThis();
|
|||
|
}
|
|||
|
pSettings->SetInt( "server/ping", nPing );
|
|||
|
|
|||
|
if ( char const *szPacketFrom = pEvent->GetString( "from", NULL ) )
|
|||
|
pSettings->SetString( "server/connectstring", szPacketFrom );
|
|||
|
|
|||
|
//
|
|||
|
// Find the server or create a new one
|
|||
|
//
|
|||
|
IMatchServer *pExistingServer = GetServerByOnlineId( xuidOnline );
|
|||
|
CServer *pServer = ( CServer * ) pExistingServer;
|
|||
|
if ( !pServer )
|
|||
|
{
|
|||
|
pServer = new CServer();
|
|||
|
pServer->m_xuid = xuidOnline;
|
|||
|
#if !defined( NO_STEAM ) && !defined( SWDS )
|
|||
|
servernetadr_t serverNetAddr;
|
|||
|
serverNetAddr.Init( netAddress.GetIPHostByteOrder(), netAddress.GetPort(), netAddress.GetPort() );
|
|||
|
pServer->m_netAdr = serverNetAddr;
|
|||
|
#endif
|
|||
|
m_Servers.AddToTail( pServer );
|
|||
|
}
|
|||
|
|
|||
|
if ( pServer->m_pGameDetails )
|
|||
|
{
|
|||
|
// Average out the ping value
|
|||
|
int nKnownPing = pServer->m_pGameDetails->GetInt( "server/ping", 0 );
|
|||
|
if ( nKnownPing < nPing )
|
|||
|
{
|
|||
|
// we got a high ping, try to display the ping as low as possible
|
|||
|
nPing = ( nKnownPing * 9 + nPing * 1 ) / 10;
|
|||
|
pSettings->SetInt( "server/ping", nPing );
|
|||
|
}
|
|||
|
|
|||
|
pServer->m_pGameDetails->deleteThis();
|
|||
|
}
|
|||
|
|
|||
|
pServer->m_pGameDetails = pSettings;
|
|||
|
pServer->m_flLastRefresh = Plat_FloatTime();
|
|||
|
|
|||
|
// Signal that we have updated a server
|
|||
|
KeyValues *kvEvent = new KeyValues( "OnMatchServerMgrUpdate", "update", "server" );
|
|||
|
kvEvent->SetUint64( "xuid", xuidOnline );
|
|||
|
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( kvEvent );
|
|||
|
}
|
|||
|
}
|
|||
|
else if ( !Q_stricmp( "Client::ResendGameDetailsRequest", szName ) )
|
|||
|
{
|
|||
|
KeyValues *kv = new KeyValues( "ConnectServerDetailsRequest" );
|
|||
|
KeyValues::AutoDelete autodelete( kv );
|
|||
|
kv->SetString( "server", pEvent->GetString( "to", "" ) );
|
|||
|
|
|||
|
g_pConnectionlessLanMgr->SendPacket( kv, pEvent->GetString( "to", "" ) );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS )
|
|||
|
void CServerManager::ServerResponded( HServerListRequest hReq, int iServer )
|
|||
|
{
|
|||
|
gameserveritem_t *gsi = steamapicontext->SteamMatchmakingServers()->GetServerDetails( hReq, iServer );
|
|||
|
if ( !gsi )
|
|||
|
return;
|
|||
|
|
|||
|
// Every time a server responds during groups query, bump this groups last request timeout
|
|||
|
m_lanSearchData.m_flLastBroadcastTime = Plat_FloatTime();
|
|||
|
|
|||
|
// Determine server network address
|
|||
|
netadr_t netAddress;
|
|||
|
netAddress.SetType( NA_IP );
|
|||
|
netAddress.SetPort( PORT_SERVER );
|
|||
|
netAddress.SetFromString( gsi->m_NetAdr.GetConnectionAddressString() );
|
|||
|
|
|||
|
// Coalesce it into its XUID online id
|
|||
|
XUID xuidOnline = uint64( netAddress.GetIPNetworkByteOrder() ) | ( uint64( netAddress.GetPort() ) << 32ull );
|
|||
|
|
|||
|
// Check if we have a pinging record for this server
|
|||
|
CServerPinging *pServer = ( CServerPinging * ) GetServerRecordByOnlineId( m_ServersPinging, xuidOnline );
|
|||
|
if ( pServer )
|
|||
|
{
|
|||
|
// We already have a pinging record for this server, just update its info
|
|||
|
pServer->m_gsi = *gsi;
|
|||
|
pServer->m_netAdr = gsi->m_NetAdr;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Create a new pinging record
|
|||
|
pServer = new CServerPinging();
|
|||
|
pServer->m_xuid = xuidOnline;
|
|||
|
pServer->m_gsi = *gsi;
|
|||
|
pServer->m_netAdr = gsi->m_NetAdr;
|
|||
|
m_ServersPinging.AddToTail( pServer );
|
|||
|
}
|
|||
|
|
|||
|
void CServerManager::RefreshComplete( HServerListRequest hReq, EMatchMakingServerResponse response )
|
|||
|
{
|
|||
|
if ( m_eState == STATE_FETCHING_SERVERS )
|
|||
|
m_eState = STATE_GROUP_FETCHED;
|
|||
|
|
|||
|
steamapicontext->SteamMatchmakingServers()->ReleaseRequest( m_hRequest );
|
|||
|
m_hRequest = NULL;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
void CServerManager::Update()
|
|||
|
{
|
|||
|
#if !( !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS ) )
|
|||
|
return;
|
|||
|
#endif
|
|||
|
|
|||
|
float now = Plat_FloatTime();
|
|||
|
|
|||
|
switch ( m_eState )
|
|||
|
{
|
|||
|
case STATE_IDLE:
|
|||
|
if ( m_bUpdateEnabled && !IsLocalClientConnectedToServer() )
|
|||
|
{
|
|||
|
if ( now > m_flNextUpdateTime )
|
|||
|
{
|
|||
|
MarkOldServersAndBeginSearch();
|
|||
|
}
|
|||
|
else if ( now > m_flNextServerUpdateTime )
|
|||
|
{
|
|||
|
float nextUpdatePeriod = mm_server_search_inet_ping_refresh.GetFloat();
|
|||
|
|
|||
|
// any servers not in the pinging list but haven't been refreshed in a while should be refreshed
|
|||
|
for ( int i = 0; i < m_Servers.Count(); ++i )
|
|||
|
{
|
|||
|
float timePassed = now - m_Servers[i]->m_flLastRefresh;
|
|||
|
if ( timePassed > mm_server_search_inet_ping_refresh.GetFloat() )
|
|||
|
{
|
|||
|
CServerPinging *pServerPinging = ( CServerPinging * ) GetServerRecordByOnlineId( m_ServersPinging, m_Servers[i]->GetOnlineId() );
|
|||
|
if ( !pServerPinging )
|
|||
|
{
|
|||
|
pServerPinging = new CServerPinging();
|
|||
|
pServerPinging->m_xuid = m_Servers[i]->GetOnlineId();
|
|||
|
#if !defined( NO_STEAM ) && !defined( SWDS )
|
|||
|
pServerPinging->m_netAdr = m_Servers[i]->m_netAdr;
|
|||
|
#endif
|
|||
|
m_ServersPinging.AddToTail( pServerPinging );
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
nextUpdatePeriod = MIN( nextUpdatePeriod, mm_server_search_inet_ping_refresh.GetFloat() - timePassed );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
m_flNextServerUpdateTime = now + nextUpdatePeriod;
|
|||
|
|
|||
|
m_eState = STATE_REQUESTING_DETAILS;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case STATE_LAN_SEARCH:
|
|||
|
UpdateLanSearch();
|
|||
|
break;
|
|||
|
|
|||
|
case STATE_GROUP_SEARCH:
|
|||
|
if ( StartFetchingGroupServersData() )
|
|||
|
break;
|
|||
|
// else -> // fall through
|
|||
|
|
|||
|
case STATE_GROUP_FETCHED:
|
|||
|
OnGroupFetched();
|
|||
|
break;
|
|||
|
|
|||
|
#if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS )
|
|||
|
case STATE_FETCHING_SERVERS:
|
|||
|
if ( Plat_FloatTime() > m_lanSearchData.m_flLastBroadcastTime + mm_server_search_inet_ping_timeout.GetFloat() )
|
|||
|
{
|
|||
|
steamapicontext->SteamMatchmakingServers()->ReleaseRequest( m_hRequest );
|
|||
|
m_hRequest = NULL;
|
|||
|
|
|||
|
m_eState = STATE_GROUP_FETCHED;
|
|||
|
}
|
|||
|
break;
|
|||
|
#endif
|
|||
|
|
|||
|
case STATE_REQUESTING_DETAILS:
|
|||
|
UpdateRequestingDetails();
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void CServerManager::MarkOldServersAndBeginSearch()
|
|||
|
{
|
|||
|
DevMsg( 2, "Server manager refreshing...\n" );
|
|||
|
|
|||
|
// Signal that we are starting a search
|
|||
|
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
|
|||
|
"OnMatchServerMgrUpdate", "update", "searchstarted" ) );
|
|||
|
|
|||
|
m_ServersPinging.PurgeAndDeleteElements();
|
|||
|
|
|||
|
m_groupSearchData.Reset();
|
|||
|
m_lanSearchData = SLanSearchData_t();
|
|||
|
m_eState = STATE_LAN_SEARCH;
|
|||
|
|
|||
|
// If broadcasts are disallowed, then go straight to group search state
|
|||
|
extern ConVar net_allow_multicast;
|
|||
|
if ( !net_allow_multicast.GetBool() )
|
|||
|
m_eState = STATE_GROUP_SEARCH;
|
|||
|
}
|
|||
|
|
|||
|
void CServerManager::UpdateLanSearch()
|
|||
|
{
|
|||
|
if ( m_lanSearchData.m_flStartTime && m_lanSearchData.m_flLastBroadcastTime )
|
|||
|
{
|
|||
|
if ( Plat_FloatTime() > m_lanSearchData.m_flStartTime + mm_server_search_lan_ping_duration.GetFloat() )
|
|||
|
{
|
|||
|
m_eState = STATE_GROUP_SEARCH;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if ( Plat_FloatTime() < m_lanSearchData.m_flLastBroadcastTime + mm_server_search_lan_ping_interval.GetFloat() )
|
|||
|
{
|
|||
|
// waiting out interval between pings
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Initialize the start time of the lan broadcast
|
|||
|
m_lanSearchData.m_flStartTime = Plat_FloatTime();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Send the packet
|
|||
|
//
|
|||
|
m_lanSearchData.m_flLastBroadcastTime = Plat_FloatTime();
|
|||
|
|
|||
|
KeyValues *kv = new KeyValues( "LanSearchServerPing" );
|
|||
|
KeyValues::AutoDelete autodelete( kv );
|
|||
|
kv->SetFloat( "timestamp", Plat_FloatTime() );
|
|||
|
|
|||
|
if ( mm_server_search_lan_ports.GetString()[0] )
|
|||
|
{
|
|||
|
// Build the list of ports to scan
|
|||
|
CSplitString arrPorts( mm_server_search_lan_ports.GetString(), "," );
|
|||
|
|
|||
|
for ( int i = 0; i < arrPorts.Count(); i++ )
|
|||
|
{
|
|||
|
// Port number
|
|||
|
int nPort = Q_atoi( arrPorts[i] );
|
|||
|
if ( nPort <= 0 )
|
|||
|
continue;
|
|||
|
|
|||
|
g_pConnectionlessLanMgr->SendPacket( kv, CFmtStr( "*:%d", nPort ) );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void CServerManager::RemoveOldServers()
|
|||
|
{
|
|||
|
float now = Plat_FloatTime();
|
|||
|
|
|||
|
for ( int k = 0; k < m_Servers.Count(); ++ k )
|
|||
|
{
|
|||
|
CServer *pServer = m_Servers[ k ];
|
|||
|
if ( pServer && now - pServer->m_flLastRefresh < mm_server_search_server_lifetime.GetFloat() )
|
|||
|
continue;
|
|||
|
|
|||
|
m_Servers.Remove( k -- );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
bool CServerManager::StartFetchingGroupServersData()
|
|||
|
{
|
|||
|
#if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS )
|
|||
|
ISteamUser *pUser = steamapicontext->SteamUser();
|
|||
|
ISteamFriends *pFriends = steamapicontext->SteamFriends();
|
|||
|
if ( !pUser || !pFriends )
|
|||
|
return false;
|
|||
|
|
|||
|
int iGroupCount = pFriends->GetClanCount();
|
|||
|
if ( !iGroupCount )
|
|||
|
return false;
|
|||
|
|
|||
|
m_groupSearchData.m_UserGroupAccountIDs.SetCount( iGroupCount );
|
|||
|
for ( int k = 0; k < iGroupCount; ++ k )
|
|||
|
{
|
|||
|
m_groupSearchData.m_UserGroupAccountIDs[ k ] = pFriends->GetClanByIndex( k ).GetAccountID();
|
|||
|
}
|
|||
|
|
|||
|
return FetchGroupServers();
|
|||
|
#else
|
|||
|
return false;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
bool CServerManager::FetchGroupServers()
|
|||
|
{
|
|||
|
#if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS )
|
|||
|
|
|||
|
static const char gamedataFilterType[] = "gamedataor";
|
|||
|
|
|||
|
char gamedataFitler[ mm_filter_max_size ];
|
|||
|
|
|||
|
*gamedataFitler = 0;
|
|||
|
|
|||
|
size_t roomLeft = mm_filter_max_size - strlen( gamedataFilterType ) - 3;
|
|||
|
|
|||
|
// Add as many groups as will fit
|
|||
|
while ( m_groupSearchData.m_idxSearchGroupId < m_groupSearchData.m_UserGroupAccountIDs.Count() )
|
|||
|
{
|
|||
|
const char *tag = CFmtStr( *gamedataFitler ? ",grp:%ui" : "grp:%ui", m_groupSearchData.m_UserGroupAccountIDs[ m_groupSearchData.m_idxSearchGroupId ] );
|
|||
|
if ( roomLeft < strlen( tag ) )
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
Q_strncat( gamedataFitler, tag, mm_filter_max_size );
|
|||
|
roomLeft -= strlen( tag );
|
|||
|
|
|||
|
++m_groupSearchData.m_idxSearchGroupId;
|
|||
|
}
|
|||
|
|
|||
|
MatchMakingKeyValuePair_t filters[ 2 ] = {
|
|||
|
// filter by game
|
|||
|
MatchMakingKeyValuePair_t( "gamedir", COM_GetModDirectory() ),
|
|||
|
// look for group servers
|
|||
|
MatchMakingKeyValuePair_t( gamedataFilterType, gamedataFitler )
|
|||
|
};
|
|||
|
|
|||
|
// request the server list. We will get called back at ServerResponded, ServerFailedToRespond, and RefreshComplete
|
|||
|
m_eState = STATE_FETCHING_SERVERS;
|
|||
|
m_lanSearchData.m_flLastBroadcastTime = Plat_FloatTime();
|
|||
|
|
|||
|
MatchMakingKeyValuePair_t *pFilter = filters;
|
|||
|
DevMsg( 2, "Requesting group server list for groups %s...\n", gamedataFitler );
|
|||
|
|
|||
|
if ( m_hRequest )
|
|||
|
steamapicontext->SteamMatchmakingServers()->ReleaseRequest( m_hRequest );
|
|||
|
m_hRequest = steamapicontext->SteamMatchmakingServers()->RequestInternetServerList(
|
|||
|
( AppId_t ) g_pMatchFramework->GetMatchTitle()->GetTitleID(), &pFilter, ARRAYSIZE( filters ), this );
|
|||
|
|
|||
|
return true;
|
|||
|
|
|||
|
#else
|
|||
|
return false;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
void CServerManager::OnGroupFetched()
|
|||
|
{
|
|||
|
if ( m_bUpdateEnabled &&
|
|||
|
m_groupSearchData.m_UserGroupAccountIDs.IsValidIndex( m_groupSearchData.m_idxSearchGroupId ) &&
|
|||
|
FetchGroupServers() )
|
|||
|
return;
|
|||
|
|
|||
|
OnAllGroupsFetched();
|
|||
|
}
|
|||
|
|
|||
|
void CServerManager::OnAllGroupsFetched()
|
|||
|
{
|
|||
|
m_flNextUpdateTime = Plat_FloatTime() + mm_server_search_update_interval.GetInt();
|
|||
|
|
|||
|
if ( !m_ServersPinging.Count() )
|
|||
|
{
|
|||
|
OnAllDataFetched();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
m_eState = STATE_REQUESTING_DETAILS;
|
|||
|
m_lanSearchData.m_flStartTime = Plat_FloatTime();
|
|||
|
|
|||
|
RequestPingingDetails();
|
|||
|
}
|
|||
|
|
|||
|
void CServerManager::RequestPingingDetails()
|
|||
|
{
|
|||
|
// Ping every server that we deferred to ping
|
|||
|
KeyValues *kv = new KeyValues( "InetSearchServerDetails" );
|
|||
|
KeyValues::AutoDelete autodelete( kv );
|
|||
|
|
|||
|
for ( int k = 0; k < m_ServersPinging.Count() && k < mm_server_search_inet_ping_window.GetInt(); ++ k )
|
|||
|
{
|
|||
|
CServerPinging *pServerPinging = ( CServerPinging * ) m_ServersPinging[k];
|
|||
|
if ( pServerPinging->m_flTimeout && Plat_FloatTime() > pServerPinging->m_flTimeout )
|
|||
|
{
|
|||
|
m_ServersPinging.FastRemove( k -- ); // server timed out
|
|||
|
delete pServerPinging;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
kv->SetFloat( "timestamp", Plat_FloatTime() );
|
|||
|
kv->SetUint64( "pingxuid", pServerPinging->m_xuid );
|
|||
|
#if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS )
|
|||
|
g_pConnectionlessLanMgr->SendPacket( kv, pServerPinging->m_netAdr.GetConnectionAddressString() );
|
|||
|
#else
|
|||
|
DevWarning( "Cannot request internet pinging server details.\n" );
|
|||
|
#endif
|
|||
|
if ( !pServerPinging->m_flTimeout )
|
|||
|
pServerPinging->m_flTimeout = Plat_FloatTime() + mm_server_search_inet_ping_timeout.GetFloat();
|
|||
|
}
|
|||
|
|
|||
|
m_lanSearchData.m_flLastBroadcastTime = Plat_FloatTime();
|
|||
|
|
|||
|
DevMsg( 2, "Server manager waiting for game details from %d servers...\n", m_ServersPinging.Count() );
|
|||
|
}
|
|||
|
|
|||
|
void CServerManager::UpdateRequestingDetails()
|
|||
|
{
|
|||
|
if ( !m_ServersPinging.Count() )
|
|||
|
{
|
|||
|
// We have no more servers to ping
|
|||
|
m_ServersPinging.PurgeAndDeleteElements();
|
|||
|
OnAllDataFetched();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if ( m_lanSearchData.m_flLastBroadcastTime + mm_server_search_inet_ping_interval.GetFloat() < Plat_FloatTime() )
|
|||
|
{
|
|||
|
RequestPingingDetails();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void CServerManager::OnAllDataFetched()
|
|||
|
{
|
|||
|
DevMsg( 2, "Server manager refresh completed.\n" );
|
|||
|
|
|||
|
m_eState = STATE_IDLE;
|
|||
|
|
|||
|
if ( !m_bUpdateEnabled )
|
|||
|
{
|
|||
|
m_Servers.PurgeAndDeleteElements();
|
|||
|
m_ServersPinging.PurgeAndDeleteElements();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
RemoveOldServers();
|
|||
|
}
|
|||
|
|
|||
|
if ( g_pMatchFramework )
|
|||
|
{
|
|||
|
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
|
|||
|
"OnMatchServerMgrUpdate", "update", "searchfinished" ) );
|
|||
|
}
|
|||
|
}
|
|||
|
|