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.
489 lines
15 KiB
489 lines
15 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//============================================================================= |
|
|
|
#include "pch_serverbrowser.h" |
|
|
|
// expose the server browser interfaces |
|
CServerBrowser g_ServerBrowserSingleton; |
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerBrowser, IServerBrowser, SERVERBROWSER_INTERFACE_VERSION, g_ServerBrowserSingleton); |
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerBrowser, IVGuiModule, "VGuiModuleServerBrowser001", g_ServerBrowserSingleton); // the interface loaded by PlatformMenu.vdf |
|
|
|
// singleton accessor |
|
CServerBrowser &ServerBrowser() |
|
{ |
|
return g_ServerBrowserSingleton; |
|
} |
|
|
|
IRunGameEngine *g_pRunGameEngine = NULL; |
|
|
|
static CSteamAPIContext g_SteamAPIContext; |
|
CSteamAPIContext *steamapicontext = &g_SteamAPIContext; |
|
|
|
IEngineReplay *g_pEngineReplay = NULL; |
|
|
|
ConVar sb_firstopentime( "sb_firstopentime", "0", FCVAR_DEVELOPMENTONLY, "Indicates the time the server browser was first opened." ); |
|
ConVar sb_numtimesopened( "sb_numtimesopened", "0", FCVAR_DEVELOPMENTONLY, "Indicates the number of times the server browser was opened this session." ); |
|
|
|
// the original author of this code felt strdup was not acceptible. |
|
inline char *CloneString( const char *str ) |
|
{ |
|
char *cloneStr = new char [ strlen(str)+1 ]; |
|
strcpy( cloneStr, str ); |
|
return cloneStr; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
CServerBrowser::CServerBrowser() |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Destructor |
|
//----------------------------------------------------------------------------- |
|
CServerBrowser::~CServerBrowser() |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CServerBrowser::CreateDialog() |
|
{ |
|
if (!m_hInternetDlg.Get()) |
|
{ |
|
m_hInternetDlg = new CServerBrowserDialog(NULL); // SetParent() call below fills this in |
|
m_hInternetDlg->Initialize(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: links to vgui and engine interfaces |
|
//----------------------------------------------------------------------------- |
|
bool CServerBrowser::Initialize(CreateInterfaceFn *factorylist, int factoryCount) |
|
{ |
|
ConnectTier1Libraries( factorylist, factoryCount ); |
|
ConVar_Register(); |
|
ConnectTier2Libraries( factorylist, factoryCount ); |
|
ConnectTier3Libraries( factorylist, factoryCount ); |
|
g_pRunGameEngine = NULL; |
|
|
|
for ( int i = 0; i < factoryCount; ++i ) |
|
{ |
|
if ( !g_pEngineReplay ) |
|
{ |
|
g_pEngineReplay = ( IEngineReplay * )factorylist[ i ]( ENGINE_REPLAY_INTERFACE_VERSION, NULL ); |
|
} |
|
} |
|
|
|
SteamAPI_InitSafe(); |
|
SteamAPI_SetTryCatchCallbacks( false ); // We don't use exceptions, so tell steam not to use try/catch in callback handlers |
|
steamapicontext->Init(); |
|
|
|
for (int i = 0; i < factoryCount; i++) |
|
{ |
|
if (!g_pRunGameEngine) |
|
{ |
|
g_pRunGameEngine = (IRunGameEngine *)(factorylist[i])(RUNGAMEENGINE_INTERFACE_VERSION, NULL); |
|
} |
|
} |
|
|
|
// load the vgui interfaces |
|
#if defined( STEAM ) || defined( HL1 ) |
|
if ( !vgui::VGuiControls_Init("ServerBrowser", factorylist, factoryCount) ) |
|
#else |
|
if ( !vgui::VGui_InitInterfacesList("ServerBrowser", factorylist, factoryCount) ) |
|
#endif |
|
return false; |
|
|
|
// load localization file |
|
g_pVGuiLocalize->AddFile( "servers/serverbrowser_%language%.txt" ); |
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: links to other modules interfaces (tracker) |
|
//----------------------------------------------------------------------------- |
|
bool CServerBrowser::PostInitialize(CreateInterfaceFn *modules, int factoryCount) |
|
{ |
|
// find the interfaces we need |
|
for (int i = 0; i < factoryCount; i++) |
|
{ |
|
if (!g_pRunGameEngine) |
|
{ |
|
g_pRunGameEngine = (IRunGameEngine *)(modules[i])(RUNGAMEENGINE_INTERFACE_VERSION, NULL); |
|
} |
|
} |
|
|
|
CreateDialog(); |
|
m_hInternetDlg->SetVisible(false); |
|
|
|
return g_pRunGameEngine; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: true if the user can't play a game due to VAC banning |
|
//----------------------------------------------------------------------------- |
|
bool CServerBrowser::IsVACBannedFromGame( int nAppID ) |
|
{ |
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Marks that the tool/game loading us intends to feed us workshop information |
|
//----------------------------------------------------------------------------- |
|
void CServerBrowser::SetWorkshopEnabled( bool bManaged ) |
|
{ |
|
m_bWorkshopEnabled = bManaged; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Add a mapname to our known user-subscribed workshop maps list |
|
//----------------------------------------------------------------------------- |
|
void CServerBrowser::AddWorkshopSubscribedMap( const char *pszMapName ) |
|
{ |
|
CUtlString strMap( pszMapName ); |
|
if ( !IsWorkshopSubscribedMap( strMap ) ) |
|
{ |
|
m_vecWorkshopSubscribedMaps.AddToTail( strMap ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: remove a mapname to our known user-subscribed workshop maps list |
|
//----------------------------------------------------------------------------- |
|
void CServerBrowser::RemoveWorkshopSubscribedMap( const char *pszMapName ) |
|
{ |
|
m_vecWorkshopSubscribedMaps.FindAndFastRemove( CUtlString( pszMapName ) ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Well, is it? |
|
//----------------------------------------------------------------------------- |
|
bool CServerBrowser::IsWorkshopEnabled() |
|
{ |
|
return m_bWorkshopEnabled; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Check if this map is in our subscribed list |
|
//----------------------------------------------------------------------------- |
|
bool CServerBrowser::IsWorkshopSubscribedMap( const char *pszMapName ) |
|
{ |
|
return m_vecWorkshopSubscribedMaps.HasElement( CUtlString( pszMapName ) ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CServerBrowser::IsValid() |
|
{ |
|
return ( g_pRunGameEngine ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CServerBrowser::Activate() |
|
{ |
|
static bool firstTimeOpening = true; |
|
if ( firstTimeOpening ) |
|
{ |
|
m_hInternetDlg->LoadUserData(); // reload the user data the first time the dialog is made visible, helps with the lag between module load and |
|
// steamui getting Deactivate() call |
|
firstTimeOpening = false; |
|
} |
|
|
|
int numTimesOpened = sb_numtimesopened.GetInt() + 1; |
|
sb_numtimesopened.SetValue( numTimesOpened ); |
|
if ( numTimesOpened == 1 ) |
|
{ |
|
time_t aclock; |
|
time( &aclock ); |
|
sb_firstopentime.SetValue( (int) aclock ); |
|
} |
|
|
|
Open(); |
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: called when the server browser gets used in the game |
|
//----------------------------------------------------------------------------- |
|
void CServerBrowser::Deactivate() |
|
{ |
|
if (m_hInternetDlg.Get()) |
|
{ |
|
m_hInternetDlg->SaveUserData(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: called when the server browser is no longer being used in the game |
|
//----------------------------------------------------------------------------- |
|
void CServerBrowser::Reactivate() |
|
{ |
|
if (m_hInternetDlg.Get()) |
|
{ |
|
m_hInternetDlg->LoadUserData(); |
|
if (m_hInternetDlg->IsVisible()) |
|
{ |
|
m_hInternetDlg->RefreshCurrentPage(); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CServerBrowser::Open() |
|
{ |
|
m_hInternetDlg->Open(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: returns direct handle to main server browser dialog |
|
//----------------------------------------------------------------------------- |
|
vgui::VPANEL CServerBrowser::GetPanel() |
|
{ |
|
return m_hInternetDlg.Get() ? m_hInternetDlg->GetVPanel() : NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: sets the parent panel of the main module panel |
|
//----------------------------------------------------------------------------- |
|
void CServerBrowser::SetParent(vgui::VPANEL parent) |
|
{ |
|
if (m_hInternetDlg.Get()) |
|
{ |
|
m_hInternetDlg->SetParent(parent); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Closes down the server browser for good |
|
//----------------------------------------------------------------------------- |
|
void CServerBrowser::Shutdown() |
|
{ |
|
if (m_hInternetDlg.Get()) |
|
{ |
|
m_hInternetDlg->Close(); |
|
m_hInternetDlg->MarkForDeletion(); |
|
} |
|
|
|
#if defined( STEAM ) |
|
vgui::VGuiControls_Shutdown(); |
|
#endif |
|
|
|
DisconnectTier3Libraries(); |
|
DisconnectTier2Libraries(); |
|
ConVar_Unregister(); |
|
DisconnectTier1Libraries(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: opens a game info dialog to watch the specified server; associated with the friend 'userName' |
|
//----------------------------------------------------------------------------- |
|
bool CServerBrowser::OpenGameInfoDialog( uint64 ulSteamIDFriend, const char *pszConnectCode ) |
|
{ |
|
#if !defined( _X360 ) // X360TBD: SteamFriends() |
|
if ( m_hInternetDlg.Get() ) |
|
{ |
|
// activate an already-existing dialog |
|
CDialogGameInfo *pDialogGameInfo = m_hInternetDlg->GetDialogGameInfoForFriend( ulSteamIDFriend ); |
|
if ( pDialogGameInfo ) |
|
{ |
|
pDialogGameInfo->Activate(); |
|
return true; |
|
} |
|
|
|
// none yet, create a new dialog |
|
FriendGameInfo_t friendGameInfo; |
|
if ( steamapicontext->SteamFriends()->GetFriendGamePlayed( ulSteamIDFriend, &friendGameInfo ) ) |
|
{ |
|
uint16 usConnPort = friendGameInfo.m_usGamePort; |
|
if ( friendGameInfo.m_usQueryPort < QUERY_PORT_ERROR ) |
|
usConnPort = friendGameInfo.m_usQueryPort; |
|
CDialogGameInfo *pDialogGameInfo = m_hInternetDlg->OpenGameInfoDialog( friendGameInfo.m_unGameIP, friendGameInfo.m_usGamePort, usConnPort, pszConnectCode ); |
|
pDialogGameInfo->SetFriend( ulSteamIDFriend ); |
|
return true; |
|
} |
|
} |
|
#endif |
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: joins a specified game - game info dialog will only be opened if the server is fully or passworded |
|
//----------------------------------------------------------------------------- |
|
bool CServerBrowser::JoinGame( uint64 ulSteamIDFriend, const char *pszConnectCode ) |
|
{ |
|
if ( OpenGameInfoDialog( ulSteamIDFriend, pszConnectCode ) ) |
|
{ |
|
CDialogGameInfo *pDialogGameInfo = m_hInternetDlg->GetDialogGameInfoForFriend( ulSteamIDFriend ); |
|
pDialogGameInfo->Connect(); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: joins a game by IP/Port |
|
//----------------------------------------------------------------------------- |
|
bool CServerBrowser::JoinGame( uint32 unGameIP, uint16 usGamePort, const char *pszConnectCode ) |
|
{ |
|
m_hInternetDlg->JoinGame( unGameIP, usGamePort, pszConnectCode ); |
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: forces the game info dialog closed |
|
//----------------------------------------------------------------------------- |
|
void CServerBrowser::CloseGameInfoDialog( uint64 ulSteamIDFriend ) |
|
{ |
|
CDialogGameInfo *pDialogGameInfo = m_hInternetDlg->GetDialogGameInfoForFriend( ulSteamIDFriend ); |
|
if ( pDialogGameInfo ) |
|
{ |
|
pDialogGameInfo->Close(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: closes all the game info dialogs |
|
//----------------------------------------------------------------------------- |
|
void CServerBrowser::CloseAllGameInfoDialogs() |
|
{ |
|
if ( m_hInternetDlg.Get() ) |
|
{ |
|
m_hInternetDlg->CloseAllGameInfoDialogs(); |
|
} |
|
} |
|
|
|
CUtlVector< gametypes_t > g_GameTypes; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void LoadGameTypes( void ) |
|
{ |
|
if ( g_GameTypes.Count() > 0 ) |
|
return; |
|
|
|
#define GAMETYPES_FILE "servers/ServerBrowserGameTypes.txt" |
|
|
|
KeyValues * kv = new KeyValues( GAMETYPES_FILE ); |
|
|
|
if ( !kv->LoadFromFile( g_pFullFileSystem, GAMETYPES_FILE, "MOD" ) ) |
|
{ |
|
kv->deleteThis(); |
|
return; |
|
} |
|
|
|
g_GameTypes.RemoveAll(); |
|
|
|
for ( KeyValues *pData = kv->GetFirstSubKey(); pData != NULL; pData = pData->GetNextKey() ) |
|
{ |
|
gametypes_t gametype; |
|
|
|
gametype.pPrefix = CloneString( pData->GetString( "prefix", "" ) ); |
|
gametype.pGametypeName = CloneString( pData->GetString( "name", "" ) ); |
|
g_GameTypes.AddToTail( gametype ); |
|
} |
|
|
|
|
|
kv->deleteThis(); |
|
} |
|
|
|
const char *GetGameTypeName( const char *pMapName ) |
|
{ |
|
LoadGameTypes(); |
|
for ( int i = 0; i < g_GameTypes.Count(); i++ ) |
|
{ |
|
int iLength = strlen( g_GameTypes[i].pPrefix ); |
|
|
|
if ( !Q_strncmp( pMapName, g_GameTypes[i].pPrefix, iLength ) ) |
|
{ |
|
return g_GameTypes[i].pGametypeName; |
|
} |
|
} |
|
|
|
return ""; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose of comments like these: none |
|
//----------------------------------------------------------------------------- |
|
const char *CServerBrowser::GetMapFriendlyNameAndGameType( const char *pszMapName, char *szFriendlyMapName, int cchFriendlyName ) |
|
{ |
|
// Make sure game types are loaded |
|
LoadGameTypes(); |
|
|
|
// Scan list |
|
const char *pszFriendlyGameTypeName = ""; |
|
for ( int i = 0; i < g_GameTypes.Count(); i++ ) |
|
{ |
|
int iLength = strlen( g_GameTypes[i].pPrefix ); |
|
|
|
if ( !Q_strnicmp( pszMapName, g_GameTypes[i].pPrefix, iLength ) ) |
|
{ |
|
pszMapName += iLength; |
|
pszFriendlyGameTypeName = g_GameTypes[i].pGametypeName; |
|
break; |
|
} |
|
} |
|
|
|
// See how many characters from the name to copy. |
|
// Start by assuming we'll copy the whole thing. |
|
// (After any prefix we just skipped) |
|
int l = V_strlen( pszMapName ); |
|
const char *pszFinal = Q_stristr( pszMapName, "_final" ); |
|
if ( pszFinal ) |
|
{ |
|
// truncate the _final (or _final1) part of the filename if it's at the end of the name |
|
const char *pszNextChar = pszFinal + Q_strlen( "_final" ); |
|
if ( ( *pszNextChar == '\0' ) || |
|
( ( *pszNextChar == '1' ) && ( *(pszNextChar+1) == '\0' ) ) ) |
|
{ |
|
l = pszFinal - pszMapName; |
|
} |
|
} |
|
|
|
// Safety check against buffer size |
|
if ( l >= cchFriendlyName ) |
|
{ |
|
Assert( !"Map name too long for buffer!" ); |
|
l = cchFriendlyName-1; |
|
} |
|
|
|
// Copy friendly portion of name only |
|
V_memcpy( szFriendlyMapName, pszMapName, l ); |
|
|
|
// It's like the Alamo. We never forget. |
|
szFriendlyMapName[l] = '\0'; |
|
|
|
// Result should be the friendly game type name |
|
return pszFriendlyGameTypeName; |
|
} |
|
|
|
|