summaryrefslogtreecommitdiff
path: root/serverbrowser/DialogGameInfo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'serverbrowser/DialogGameInfo.cpp')
-rw-r--r--serverbrowser/DialogGameInfo.cpp802
1 files changed, 802 insertions, 0 deletions
diff --git a/serverbrowser/DialogGameInfo.cpp b/serverbrowser/DialogGameInfo.cpp
new file mode 100644
index 0000000..fc0ed36
--- /dev/null
+++ b/serverbrowser/DialogGameInfo.cpp
@@ -0,0 +1,802 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "pch_serverbrowser.h"
+
+using namespace vgui;
+
+static const long RETRY_TIME = 10000; // refresh server every 10 seconds
+static const long CHALLENGE_ENTRIES = 1024;
+
+extern "C"
+{
+ DLL_EXPORT bool JoiningSecureServerCall()
+ {
+ return true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Comparison function used in query redblack tree
+//-----------------------------------------------------------------------------
+bool QueryLessFunc( const struct challenge_s &item1, const struct challenge_s &item2 )
+{
+ // compare port then ip
+ if ( item1.addr.GetPort() < item2.addr.GetPort() )
+ return true;
+ else if ( item1.addr.GetPort() > item2.addr.GetPort() )
+ return false;
+
+ int ip1 = item1.addr.GetIPNetworkByteOrder();
+ int ip2 = item2.addr.GetIPNetworkByteOrder();
+
+ return ip1 < ip2;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CDialogGameInfo::CDialogGameInfo( vgui::Panel *parent, int serverIP, int queryPort, unsigned short connectionPort, const char *pszConnectCode ) :
+ Frame(parent, "DialogGameInfo"),
+ m_CallbackPersonaStateChange( this, &CDialogGameInfo::OnPersonaStateChange ),
+ m_sConnectCode( pszConnectCode )
+{
+ SetBounds(0, 0, 512, 512);
+ SetMinimumSize(416, 340);
+ SetDeleteSelfOnClose(true);
+ m_bConnecting = false;
+ m_bServerFull = false;
+ m_bShowAutoRetryToggle = false;
+ m_bServerNotResponding = false;
+ m_bShowingExtendedOptions = false;
+ m_SteamIDFriend = 0;
+ m_hPingQuery = HSERVERQUERY_INVALID;
+ m_hPlayersQuery = HSERVERQUERY_INVALID;
+ m_bPlayerListUpdatePending = false;
+
+ m_szPassword[0] = 0;
+
+ m_pConnectButton = new Button(this, "Connect", "#ServerBrowser_JoinGame");
+ m_pCloseButton = new Button(this, "Close", "#ServerBrowser_Close");
+ m_pRefreshButton = new Button(this, "Refresh", "#ServerBrowser_Refresh");
+ m_pInfoLabel = new Label(this, "InfoLabel", "");
+ m_pAutoRetry = new ToggleButton(this, "AutoRetry", "#ServerBrowser_AutoRetry");
+ m_pAutoRetry->AddActionSignalTarget(this);
+
+ m_pAutoRetryAlert = new RadioButton(this, "AutoRetryAlert", "#ServerBrowser_AlertMeWhenSlotOpens");
+ m_pAutoRetryJoin = new RadioButton(this, "AutoRetryJoin", "#ServerBrowser_JoinWhenSlotOpens");
+ m_pPlayerList = new ListPanel(this, "PlayerList");
+ m_pPlayerList->AddColumnHeader(0, "PlayerName", "#ServerBrowser_PlayerName", 156);
+ m_pPlayerList->AddColumnHeader(1, "Score", "#ServerBrowser_Score", 64);
+ m_pPlayerList->AddColumnHeader(2, "Time", "#ServerBrowser_Time", 64);
+
+ m_pPlayerList->SetSortFunc(2, &PlayerTimeColumnSortFunc);
+
+ // set the defaults for sorting
+ // hack, need to make this more explicit functions in ListPanel
+ PostMessage(m_pPlayerList, new KeyValues("SetSortColumn", "column", 2));
+ PostMessage(m_pPlayerList, new KeyValues("SetSortColumn", "column", 1));
+ PostMessage(m_pPlayerList, new KeyValues("SetSortColumn", "column", 1));
+
+ m_pAutoRetryAlert->SetSelected(true);
+
+ m_pConnectButton->SetCommand(new KeyValues("Connect"));
+ m_pCloseButton->SetCommand(new KeyValues("Close"));
+ m_pRefreshButton->SetCommand(new KeyValues("Refresh"));
+
+ m_iRequestRetry = 0;
+
+ // create a new server to watch
+ memset(&m_Server, 0, sizeof(m_Server) );
+ m_Server.m_NetAdr.Init( serverIP, queryPort, connectionPort );
+
+ // refresh immediately
+ RequestInfo();
+
+ // let us be ticked every frame
+ ivgui()->AddTickSignal(this->GetVPanel());
+
+ LoadControlSettings("Servers/DialogGameInfo.res");
+ RegisterControlSettingsFile( "Servers/DialogGameInfo_SinglePlayer.res" );
+ RegisterControlSettingsFile( "Servers/DialogGameInfo_AutoRetry.res" );
+ MoveToCenterOfScreen();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CDialogGameInfo::~CDialogGameInfo()
+{
+ if ( !steamapicontext->SteamMatchmakingServers() )
+ return;
+
+ if ( m_hPingQuery != HSERVERQUERY_INVALID )
+ steamapicontext->SteamMatchmakingServers()->CancelServerQuery( m_hPingQuery );
+ if ( m_hPlayersQuery != HSERVERQUERY_INVALID )
+ steamapicontext->SteamMatchmakingServers()->CancelServerQuery( m_hPlayersQuery );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: send a player query to a server
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::SendPlayerQuery( uint32 unIP, uint16 usQueryPort )
+{
+ if ( !steamapicontext->SteamMatchmakingServers() )
+ return;
+
+ if ( m_hPlayersQuery != HSERVERQUERY_INVALID )
+ steamapicontext->SteamMatchmakingServers()->CancelServerQuery( m_hPlayersQuery );
+ m_hPlayersQuery = steamapicontext->SteamMatchmakingServers()->PlayerDetails( unIP, usQueryPort, this );
+ m_bPlayerListUpdatePending = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Activates the dialog
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::Run(const char *titleName)
+{
+ if ( titleName )
+ {
+ SetTitle( "#ServerBrowser_GameInfoWithNameTitle", true );
+ }
+ else
+ {
+ SetTitle( "#ServerBrowser_GameInfoWithNameTitle", true );
+ }
+ SetDialogVariable( "game", titleName );
+
+ // get the info from the user
+ RequestInfo();
+ Activate();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Changes which server to watch
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::ChangeGame( int serverIP, int queryPort, unsigned short connectionPort )
+{
+ memset( &m_Server, 0x0, sizeof(m_Server) );
+
+ m_Server.m_NetAdr.Init( serverIP, queryPort, connectionPort );
+
+ // remember the dialogs position so we can keep it the same
+ int x, y;
+ GetPos( x, y );
+
+ // see if we need to change dialog state
+ if ( !m_Server.m_NetAdr.GetIP() || !m_Server.m_NetAdr.GetQueryPort() )
+ {
+ // not in a server, load the simple settings dialog
+ SetMinimumSize(0, 0);
+ SetSizeable( false );
+ LoadControlSettings( "Servers/DialogGameInfo_SinglePlayer.res" );
+ }
+ else
+ {
+ // moving from a single-player game -> multiplayer, reset dialog
+ SetMinimumSize(416, 340);
+ SetSizeable( true );
+ LoadControlSettings( "Servers/DialogGameInfo.res" );
+ }
+ SetPos( x, y );
+
+ // Start refresh immediately
+ m_iRequestRetry = 0;
+ RequestInfo();
+ InvalidateLayout();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: updates the dialog if it's watching a friend who changes servers
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::OnPersonaStateChange( PersonaStateChange_t *pPersonaStateChange )
+{
+#if 0 // TBD delete this func
+ if ( m_SteamIDFriend && m_SteamIDFriend == pPersonaStateChange->m_ulSteamID )
+ {
+ // friend may have changed servers
+ uint64 nGameID;
+ uint32 unGameIP;
+ uint16 usGamePort;
+ uint16 usQueryPort;
+
+ if ( SteamFriends()->GetFriendGamePlayed( m_SteamIDFriend, &nGameID, &unGameIP, &usGamePort, &usQueryPort ) )
+ {
+ if ( pPersonaStateChange->m_nChangeFlags & k_EPersonaChangeGamePlayed )
+ {
+ ChangeGame( unGameIP, usQueryPort, usGamePort );
+ }
+ }
+ else
+ {
+ // bugbug johnc: change to not be in a game anymore
+ }
+ }
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Associates a user with this dialog
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::SetFriend( uint64 ulSteamIDFriend )
+{
+ // set the title to include the friends name
+ SetTitle( "#ServerBrowser_GameInfoWithNameTitle", true );
+ SetDialogVariable( "game", steamapicontext->SteamFriends()->GetFriendPersonaName( ulSteamIDFriend ) );
+ SetDialogVariable( "friend", steamapicontext->SteamFriends()->GetFriendPersonaName( ulSteamIDFriend ) );
+
+ // store the friend we're associated with
+ m_SteamIDFriend = ulSteamIDFriend;
+
+ 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;
+ ChangeGame( friendGameInfo.m_unGameIP, usConnPort, friendGameInfo.m_usGamePort );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: data access
+//-----------------------------------------------------------------------------
+uint64 CDialogGameInfo::GetAssociatedFriend()
+{
+ return m_SteamIDFriend;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: lays out the data
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ SetControlString( "ServerText", m_Server.GetName() );
+ SetControlString( "GameText", m_Server.m_szGameDescription );
+ SetControlString( "MapText", m_Server.m_szMap );
+ SetControlString( "GameTags", m_Server.m_szGameTags );
+
+
+ if ( !m_Server.m_bHadSuccessfulResponse )
+ {
+ SetControlString("SecureText", "");
+ }
+ else if ( m_Server.m_bSecure )
+ {
+ SetControlString("SecureText", "#ServerBrowser_Secure");
+ }
+ else
+ {
+ SetControlString("SecureText", "#ServerBrowser_NotSecure");
+ }
+
+ char buf[128];
+ if ( m_Server.m_nMaxPlayers > 0)
+ {
+ Q_snprintf(buf, sizeof(buf), "%d / %d", m_Server.m_nPlayers, m_Server.m_nMaxPlayers);
+ }
+ else
+ {
+ buf[0] = 0;
+ }
+ SetControlString("PlayersText", buf);
+
+ if ( m_Server.m_NetAdr.GetIP() && m_Server.m_NetAdr.GetQueryPort() )
+ {
+ SetControlString("ServerIPText", m_Server.m_NetAdr.GetConnectionAddressString() );
+ m_pConnectButton->SetEnabled(true);
+ if ( m_pAutoRetry->IsSelected() )
+ {
+ m_pAutoRetryAlert->SetVisible(true);
+ m_pAutoRetryJoin->SetVisible(true);
+ }
+ else
+ {
+ m_pAutoRetryAlert->SetVisible(false);
+ m_pAutoRetryJoin->SetVisible(false);
+ }
+ }
+ else
+ {
+ SetControlString("ServerIPText", "");
+ m_pConnectButton->SetEnabled(false);
+ }
+
+ if ( m_Server.m_bHadSuccessfulResponse )
+ {
+ Q_snprintf(buf, sizeof(buf), "%d", m_Server.m_nPing );
+ SetControlString("PingText", buf);
+ }
+ else
+ {
+ SetControlString("PingText", "");
+ }
+
+ // set the info text
+ if ( m_pAutoRetry->IsSelected() )
+ {
+ if ( m_Server.m_nPlayers < m_Server.m_nMaxPlayers )
+ {
+ m_pInfoLabel->SetText("#ServerBrowser_PressJoinToConnect");
+ }
+ else if (m_pAutoRetryJoin->IsSelected())
+ {
+ m_pInfoLabel->SetText("#ServerBrowser_JoinWhenSlotIsFree");
+ }
+ else
+ {
+ m_pInfoLabel->SetText("#ServerBrowser_AlertWhenSlotIsFree");
+ }
+ }
+ else if (m_bServerFull)
+ {
+ m_pInfoLabel->SetText("#ServerBrowser_CouldNotConnectServerFull");
+ }
+ else if (m_bServerNotResponding)
+ {
+ m_pInfoLabel->SetText("#ServerBrowser_ServerNotResponding");
+ }
+ else
+ {
+ // clear the status
+ m_pInfoLabel->SetText("");
+ }
+
+ if ( m_Server.m_bHadSuccessfulResponse && !(m_Server.m_nPlayers + m_Server.m_nBotPlayers) )
+ {
+ m_pPlayerList->SetEmptyListText("#ServerBrowser_ServerHasNoPlayers");
+ }
+ else
+ {
+ m_pPlayerList->SetEmptyListText("#ServerBrowser_ServerNotResponding");
+ }
+
+ // auto-retry layout
+ m_pAutoRetry->SetVisible(m_bShowAutoRetryToggle);
+
+ Repaint();
+}
+
+void CDialogGameInfo::OnKeyCodePressed( vgui::KeyCode code )
+{
+ if ( code == KEY_XBUTTON_B || code == KEY_XBUTTON_A || code == STEAMCONTROLLER_A || code == STEAMCONTROLLER_B )
+ {
+ m_pCloseButton->DoClick();
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed(code);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Forces the game info dialog to try and connect
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::Connect()
+{
+ OnConnect();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Connects the user to this game
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::OnConnect()
+{
+ // flag that we are attempting connection
+ m_bConnecting = true;
+
+ // reset state
+ m_bServerFull = false;
+ m_bServerNotResponding = false;
+
+ InvalidateLayout();
+
+ // need to refresh server before attempting to connect, to make sure there is enough room on the server
+ m_iRequestRetry = 0;
+ RequestInfo();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Cancel auto-retry if we connect to the game by other means
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::OnConnectToGame( int ip, int port )
+{
+ // if we just connected to the server we were looking at, close the dialog
+ // important so that we don't auto-retry a server that we are already on
+ if ( m_Server.m_NetAdr.GetIP() == (uint32)ip && m_Server.m_NetAdr.GetConnectionPort() == (uint16)port )
+ {
+ // close this dialog
+ Close();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles Refresh button press, starts a re-ping of the server
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::OnRefresh()
+{
+ m_iRequestRetry = 0;
+ // re-ask the server for the game info
+ RequestInfo();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Forces the whole dialog to redraw when the auto-retry button is toggled
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::OnButtonToggled(Panel *panel)
+{
+ if (panel == m_pAutoRetry)
+ {
+ ShowAutoRetryOptions(m_pAutoRetry->IsSelected());
+ }
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets whether the extended auto-retry options are visible or not
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::ShowAutoRetryOptions(bool state)
+{
+ // we need to extend the dialog
+ int growSize = 60;
+ if (!state)
+ {
+ growSize = -growSize;
+ }
+
+ // alter the dialog size accordingly
+ int x, y, wide, tall;
+ GetBounds( x, y, wide, tall );
+
+ // load a new layout file depending on the state
+ SetMinimumSize(416, 340);
+ if ( state )
+ LoadControlSettings( "Servers/DialogGameInfo_AutoRetry.res" );
+ else
+ LoadControlSettings( "Servers/DialogGameInfo.res" );
+
+ // restore size and position as
+ // load control settings will override them
+ SetBounds( x, y, wide, tall + growSize );
+
+ // restore other properties of the dialog
+ PerformLayout();
+
+ m_pAutoRetryAlert->SetSelected( true );
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Requests the right info from the server
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::RequestInfo()
+{
+ if ( !steamapicontext->SteamMatchmakingServers() )
+ return;
+
+ if ( m_iRequestRetry == 0 )
+ {
+ // reset the time at which we auto-refresh
+ m_iRequestRetry = system()->GetTimeMillis() + RETRY_TIME;
+ if ( m_hPingQuery != HSERVERQUERY_INVALID )
+ steamapicontext->SteamMatchmakingServers()->CancelServerQuery( m_hPingQuery );
+ m_hPingQuery = steamapicontext->SteamMatchmakingServers()->PingServer( m_Server.m_NetAdr.GetIP(), m_Server.m_NetAdr.GetQueryPort(), this );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called every frame, handles resending network messages
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::OnTick()
+{
+ // check to see if we should perform an auto-refresh
+ if ( m_iRequestRetry && m_iRequestRetry < system()->GetTimeMillis() )
+ {
+ m_iRequestRetry = 0;
+ RequestInfo();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called when the server has successfully responded
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::ServerResponded( gameserveritem_t &server )
+{
+ if( m_Server.m_NetAdr.GetQueryPort() &&
+ m_Server.m_NetAdr.GetQueryPort() != server.m_NetAdr.GetQueryPort() )
+ {
+ return; // this is not the guy we talked about
+ }
+
+ uint16 connectionPort = m_Server.m_NetAdr.GetConnectionPort();
+
+ // FIXME(johns): This is a workaround for a steam bug, where it inproperly reads signed bytes out of the
+ // message. Once the upstream fix makes it into our SteamSDK, this block can be removed.
+ server.m_nPlayers = (uint8)(int8)server.m_nPlayers;
+ server.m_nBotPlayers = (uint8)(int8)server.m_nBotPlayers;
+ server.m_nMaxPlayers = (uint8)(int8)server.m_nMaxPlayers;
+
+ m_hPingQuery = HSERVERQUERY_INVALID;
+ m_Server = server;
+
+ // Preserve our connection port, since we may be querying the sourceTV port but getting a response for the real
+ // server. This is a limitation of the steam Matchmaking API where it doesn't properly send us a sourcetv response
+ // but instead the main server's response (unless we're connecting to a proxy, THEN we get the sourcetv response!)
+ m_Server.m_NetAdr.SetConnectionPort( connectionPort );
+
+ if ( m_bConnecting )
+ {
+ ConnectToServer();
+ }
+ else if ( m_pAutoRetry->IsSelected() && server.m_nPlayers < server.m_nMaxPlayers )
+ {
+ // there is a slot free, we can join
+
+ // make the sound
+ surface()->PlaySound("Servers/game_ready.wav");
+
+ // flash this window
+ FlashWindow();
+
+ // if it's set, connect right away
+ if (m_pAutoRetryJoin->IsSelected())
+ {
+ ConnectToServer();
+ }
+ }
+ else
+ {
+ SendPlayerQuery( server.m_NetAdr.GetIP(), server.m_NetAdr.GetQueryPort() );
+ }
+
+ m_bServerNotResponding = false;
+
+ InvalidateLayout();
+ Repaint();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: called when a server response has timed out
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::ServerFailedToRespond()
+{
+ // the server didn't respond, mark that in the UI
+ // only mark if we haven't ever received a response
+ if ( !m_Server.m_bHadSuccessfulResponse )
+ {
+ m_bServerNotResponding = true;
+ }
+
+ InvalidateLayout();
+ Repaint();
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructs a command to send a running game to connect to a server,
+// based on the server type
+//
+// TODO it would be nice to push this logic into the IRunGameEngine interface; that
+// way we could ask the engine itself to construct arguments in ways that fit.
+// Might be worth the effort as we start to add more engines.
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::ApplyConnectCommand( const gameserveritem_t &server )
+{
+ char command[ 256 ];
+ // set the server password, if any
+ if ( m_szPassword[0] )
+ {
+ Q_snprintf( command, Q_ARRAYSIZE( command ), "password \"%s\"\n", m_szPassword );
+ g_pRunGameEngine->AddTextCommand( command );
+ }
+ // send engine command to change servers
+ Q_snprintf( command, Q_ARRAYSIZE( command ), "connect %s %s\n", server.m_NetAdr.GetConnectionAddressString(), m_sConnectCode.String() );
+ g_pRunGameEngine->AddTextCommand( command );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructs game options to use when running a game to connect to a server
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::ConstructConnectArgs( char *pchOptions, int cchOptions, const gameserveritem_t &server )
+{
+ Q_snprintf( pchOptions, cchOptions, " +connect %s", server.m_NetAdr.GetConnectionAddressString() );
+ if ( m_szPassword[0] )
+ {
+ Q_strcat( pchOptions, " +password \"", cchOptions );
+ Q_strcat( pchOptions, m_szPassword, cchOptions );
+ Q_strcat( pchOptions, "\"", cchOptions );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Connects to the server
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::ConnectToServer()
+{
+ m_bConnecting = false;
+
+ // check VAC status
+ if ( m_Server.m_bSecure && ServerBrowser().IsVACBannedFromGame( m_Server.m_nAppID ) )
+ {
+ // refuse the user
+ CVACBannedConnRefusedDialog *pDlg = new CVACBannedConnRefusedDialog( GetVParent(), "VACBannedConnRefusedDialog" );
+ pDlg->Activate();
+ Close();
+ return;
+ }
+
+
+ // check to see if we need a password
+ if ( m_Server.m_bPassword && !m_szPassword[0] )
+ {
+ CDialogServerPassword *box = new CDialogServerPassword(this);
+ box->AddActionSignalTarget(this);
+ box->Activate( m_Server.GetName(), 0 );
+ return;
+ }
+
+ // check the player count
+ if ( m_Server.m_nPlayers >= m_Server.m_nMaxPlayers )
+ {
+ // mark why we cannot connect
+ m_bServerFull = true;
+ // give them access to auto-retry options
+ m_bShowAutoRetryToggle = true;
+ InvalidateLayout();
+ return;
+ }
+
+ // tell the engine to connect
+ const char *gameDir = m_Server.m_szGameDir;
+ if (g_pRunGameEngine->IsRunning())
+ {
+ ApplyConnectCommand( m_Server );
+ }
+ else
+ {
+ char connectArgs[256];
+ ConstructConnectArgs( connectArgs, Q_ARRAYSIZE( connectArgs ), m_Server );
+
+ if ( ( m_Server.m_bSecure && JoiningSecureServerCall() )|| !m_Server.m_bSecure )
+ {
+ switch ( g_pRunGameEngine->RunEngine( m_Server.m_nAppID, gameDir, connectArgs ) )
+ {
+ case IRunGameEngine::k_ERunResultModNotInstalled:
+ {
+ MessageBox *dlg = new MessageBox( "#ServerBrowser_GameInfoTitle", "#ServerBrowser_ModNotInstalled" );
+ dlg->DoModal();
+ SetVisible(false);
+ return;
+ }
+ break;
+ case IRunGameEngine::k_ERunResultAppNotFound:
+ {
+ MessageBox *dlg = new MessageBox( "#ServerBrowser_GameInfoTitle", "#ServerBrowser_AppNotFound" );
+ dlg->DoModal();
+ SetVisible(false);
+ return;
+ }
+ break;
+ case IRunGameEngine::k_ERunResultNotInitialized:
+ {
+ MessageBox *dlg = new MessageBox( "#ServerBrowser_GameInfoTitle", "#ServerBrowser_NotInitialized" );
+ dlg->DoModal();
+ SetVisible(false);
+ return;
+ }
+ break;
+ case IRunGameEngine::k_ERunResultOkay:
+ default:
+ break;
+ };
+ }
+ }
+
+ // close this dialog
+ PostMessage(this, new KeyValues("Close"));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called when the current refresh list is complete
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::RefreshComplete( EMatchMakingServerResponse response )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: handles response from the get password dialog
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::OnJoinServerWithPassword(const char *password)
+{
+ // copy out the password
+ Q_strncpy(m_szPassword, password, sizeof(m_szPassword));
+
+ // retry connecting to the server again
+ OnConnect();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: player list received
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::ClearPlayerList()
+{
+ m_pPlayerList->DeleteAllItems();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: on individual player added
+//-----------------------------------------------------------------------------
+void CDialogGameInfo::AddPlayerToList(const char *playerName, int score, float timePlayedSeconds)
+{
+ if ( m_bPlayerListUpdatePending )
+ {
+ m_bPlayerListUpdatePending = false;
+ m_pPlayerList->RemoveAll();
+ }
+
+ KeyValues *player = new KeyValues("player");
+ player->SetString("PlayerName", playerName);
+ player->SetInt("Score", score);
+ player->SetInt("TimeSec", (int)timePlayedSeconds);
+
+ // construct a time string
+ int seconds = (int)timePlayedSeconds;
+ int minutes = seconds / 60;
+ int hours = minutes / 60;
+ seconds %= 60;
+ minutes %= 60;
+ char buf[64];
+ buf[0] = 0;
+ if (hours)
+ {
+ Q_snprintf(buf, sizeof(buf), "%dh %dm %ds", hours, minutes, seconds);
+ }
+ else if (minutes)
+ {
+ Q_snprintf(buf, sizeof(buf), "%dm %ds", minutes, seconds);
+ }
+ else
+ {
+ Q_snprintf(buf, sizeof(buf), "%ds", seconds);
+ }
+ player->SetString("Time", buf);
+
+ m_pPlayerList->AddItem(player, 0, false, true);
+ player->deleteThis();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sorting function for time column
+//-----------------------------------------------------------------------------
+int CDialogGameInfo::PlayerTimeColumnSortFunc(ListPanel *pPanel, const ListPanelItem &p1, const ListPanelItem &p2)
+{
+ int p1time = p1.kv->GetInt("TimeSec");
+ int p2time = p2.kv->GetInt("TimeSec");
+
+ if (p1time > p2time)
+ return -1;
+ if (p1time < p2time)
+ return 1;
+
+ return 0;
+}
+