//========= 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; }