mirror of
https://github.com/tribes2/engine.git
synced 2026-01-21 12:14:46 +00:00
905 lines
27 KiB
C++
905 lines
27 KiB
C++
|
|
//-----------------------------------------------------------------------------
|
||
|
|
// V12 Engine
|
||
|
|
//
|
||
|
|
// Copyright (c) 2001 GarageGames.Com
|
||
|
|
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||
|
|
//-----------------------------------------------------------------------------
|
||
|
|
|
||
|
|
#include "dgl/dgl.h"
|
||
|
|
#include "game/guiServerBrowser.h"
|
||
|
|
#include "gui/guiCanvas.h"
|
||
|
|
#include "console/consoleTypes.h"
|
||
|
|
#include "game/serverQuery.h"
|
||
|
|
#include "game/version.h"
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
// Static stuff for sorting:
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
static S32 sSortColumnKey;
|
||
|
|
static bool sSortInc;
|
||
|
|
static S32 sSecondarySortColumnKey;
|
||
|
|
static bool sSecondarySortInc;
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
static int FN_CDECL serverListRowCompare( const void* a, const void* b )
|
||
|
|
{
|
||
|
|
ServerInfo* siA = (ServerInfo*) a;
|
||
|
|
ServerInfo* siB = (ServerInfo*) b;
|
||
|
|
|
||
|
|
enum SortStage
|
||
|
|
{
|
||
|
|
FirstSort,
|
||
|
|
SecondSort,
|
||
|
|
Done,
|
||
|
|
};
|
||
|
|
|
||
|
|
SortStage stage = FirstSort;
|
||
|
|
//bool done = false;
|
||
|
|
bool AEqualB = false;
|
||
|
|
bool ALessB = true;
|
||
|
|
bool sortInc = true;
|
||
|
|
S32 cmpResult;
|
||
|
|
|
||
|
|
// First push timed-out servers to the end:
|
||
|
|
if ( siA->isTimedOut() )
|
||
|
|
{
|
||
|
|
if ( !siB->isTimedOut() )
|
||
|
|
{
|
||
|
|
ALessB = false;
|
||
|
|
stage = Done;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else if ( siB->isTimedOut() )
|
||
|
|
stage = Done;
|
||
|
|
|
||
|
|
// Now bring servers that have already responded to the top:
|
||
|
|
if ( stage != Done )
|
||
|
|
{
|
||
|
|
if ( !siA->hasResponded() )
|
||
|
|
{
|
||
|
|
if ( siB->hasResponded() )
|
||
|
|
{
|
||
|
|
ALessB = false;
|
||
|
|
stage = Done;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else if ( !siB->hasResponded() )
|
||
|
|
stage = Done;
|
||
|
|
}
|
||
|
|
|
||
|
|
while ( stage != Done )
|
||
|
|
{
|
||
|
|
sortInc = ( stage == FirstSort ) ? sSortInc : sSecondarySortInc;
|
||
|
|
switch ( ( stage == FirstSort ) ? sSortColumnKey : sSecondarySortColumnKey )
|
||
|
|
{
|
||
|
|
case GuiServerBrowser::Name_Column:
|
||
|
|
if ( siA->name && siB->name )
|
||
|
|
{
|
||
|
|
cmpResult = dStricmp( siA->name, siB->name );
|
||
|
|
if ( cmpResult == 0 )
|
||
|
|
AEqualB = true;
|
||
|
|
else
|
||
|
|
ALessB = ( cmpResult < 0 );
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case GuiServerBrowser::Status_Column:
|
||
|
|
{
|
||
|
|
U32 numIconsA = 0, numIconsB = 0;
|
||
|
|
U32 iconMaskA = 0, iconMaskB = 0;
|
||
|
|
if ( siA->isPassworded() )
|
||
|
|
{
|
||
|
|
numIconsA++;
|
||
|
|
iconMaskA += 4;
|
||
|
|
}
|
||
|
|
if ( siA->isDedicated() )
|
||
|
|
{
|
||
|
|
numIconsA++;
|
||
|
|
iconMaskA += 2;
|
||
|
|
}
|
||
|
|
if ( siA->isTournament() )
|
||
|
|
{
|
||
|
|
numIconsA++;
|
||
|
|
iconMaskA++;
|
||
|
|
}
|
||
|
|
if ( siA->isLinux() )
|
||
|
|
numIconsA++;
|
||
|
|
|
||
|
|
if ( siB->isPassworded() )
|
||
|
|
{
|
||
|
|
numIconsB++;
|
||
|
|
iconMaskB += 4;
|
||
|
|
}
|
||
|
|
if ( siB->isDedicated() )
|
||
|
|
{
|
||
|
|
numIconsB++;
|
||
|
|
iconMaskB += 2;
|
||
|
|
}
|
||
|
|
if ( siB->isTournament() )
|
||
|
|
{
|
||
|
|
numIconsB++;
|
||
|
|
iconMaskB++;
|
||
|
|
}
|
||
|
|
if ( siB->isLinux() )
|
||
|
|
numIconsB++;
|
||
|
|
|
||
|
|
if ( numIconsA == numIconsB )
|
||
|
|
{
|
||
|
|
if ( iconMaskA == iconMaskB )
|
||
|
|
AEqualB = true;
|
||
|
|
else
|
||
|
|
ALessB = iconMaskA < iconMaskB;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
ALessB = numIconsA < numIconsB;
|
||
|
|
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
case GuiServerBrowser::Favorite_Column:
|
||
|
|
if ( siA->isFavorite == siB->isFavorite )
|
||
|
|
AEqualB = true;
|
||
|
|
else
|
||
|
|
ALessB = !siB->isFavorite;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case GuiServerBrowser::Ping_Column:
|
||
|
|
if ( siA->ping == siB->ping )
|
||
|
|
AEqualB = true;
|
||
|
|
else
|
||
|
|
ALessB = ( siA->ping < siB->ping );
|
||
|
|
break;
|
||
|
|
|
||
|
|
case GuiServerBrowser::MissionType_Column:
|
||
|
|
if ( siA->missionType && siB->missionType )
|
||
|
|
{
|
||
|
|
cmpResult = dStricmp( siA->missionType, siB->missionType );
|
||
|
|
if ( cmpResult == 0 )
|
||
|
|
AEqualB = true;
|
||
|
|
else
|
||
|
|
ALessB = ( cmpResult < 0 );
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case GuiServerBrowser::Map_Column:
|
||
|
|
if ( siA->missionName && siB->missionName )
|
||
|
|
{
|
||
|
|
cmpResult = dStricmp( siA->missionName, siB->missionName );
|
||
|
|
if ( cmpResult == 0 )
|
||
|
|
AEqualB = true;
|
||
|
|
else
|
||
|
|
ALessB = ( cmpResult < 0 );
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case GuiServerBrowser::GameType_Column:
|
||
|
|
if ( siA->gameType && siB->gameType )
|
||
|
|
{
|
||
|
|
cmpResult = dStricmp( siA->gameType, siB->gameType );
|
||
|
|
if ( cmpResult == 0 )
|
||
|
|
AEqualB = true;
|
||
|
|
else
|
||
|
|
ALessB = ( cmpResult < 0 );
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case GuiServerBrowser::Players_Column:
|
||
|
|
if ( siA->numPlayers == siB->numPlayers )
|
||
|
|
AEqualB = true;
|
||
|
|
else
|
||
|
|
ALessB = ( siA->numPlayers < siB->numPlayers );
|
||
|
|
break;
|
||
|
|
|
||
|
|
case GuiServerBrowser::CPU_Column:
|
||
|
|
if ( siA->cpuSpeed == siB->cpuSpeed )
|
||
|
|
AEqualB = true;
|
||
|
|
else
|
||
|
|
ALessB = ( siA->cpuSpeed < siB->cpuSpeed );
|
||
|
|
break;
|
||
|
|
|
||
|
|
case GuiServerBrowser::IP_Column:
|
||
|
|
{
|
||
|
|
char addrA[256], addrB[256];
|
||
|
|
Net::addressToString( &siA->address, addrA );
|
||
|
|
Net::addressToString( &siB->address, addrB );
|
||
|
|
cmpResult = dStricmp( addrA, addrB );
|
||
|
|
if ( cmpResult == 0 )
|
||
|
|
AEqualB = true;
|
||
|
|
else
|
||
|
|
ALessB = ( cmpResult < 0 );
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
case GuiServerBrowser::Version_Column:
|
||
|
|
if ( siA->version == siB->version )
|
||
|
|
AEqualB = true;
|
||
|
|
else
|
||
|
|
ALessB = ( siA->version < siB->version );
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( stage == FirstSort && AEqualB )
|
||
|
|
stage = SecondSort;
|
||
|
|
else
|
||
|
|
stage = Done;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( AEqualB )
|
||
|
|
{
|
||
|
|
cmpResult = dStricmp( siA->name, siB->name );
|
||
|
|
if ( cmpResult == 0 )
|
||
|
|
return 0;
|
||
|
|
else
|
||
|
|
ALessB = ( cmpResult < 0 );
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( ALessB )
|
||
|
|
return ( sortInc ? -1 : 1 );
|
||
|
|
else
|
||
|
|
return ( sortInc ? 1 : -1 );
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
IMPLEMENT_CONOBJECT(GuiServerBrowser);
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
GuiServerBrowser::GuiServerBrowser() : ShellFancyArray()
|
||
|
|
{
|
||
|
|
mIconBase = StringTable->insert( "gui/shll_icon" );
|
||
|
|
mTexFavorite = NULL;
|
||
|
|
mTexFavoriteHI = NULL;
|
||
|
|
mTexNotQueried = NULL;
|
||
|
|
mTexNotQueriedHI = NULL;
|
||
|
|
mTexQuerying = NULL;
|
||
|
|
mTexQueryingHI = NULL;
|
||
|
|
mTexTimedOut = NULL;
|
||
|
|
mTexDedicated = NULL;
|
||
|
|
mTexDedicatedHI = NULL;
|
||
|
|
mTexPassworded = NULL;
|
||
|
|
mTexPasswordedHI = NULL;
|
||
|
|
mTexTournament = NULL;
|
||
|
|
mTexTournamentHI = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
ConsoleFunction( queryLanServers, void, 2, 3, "queryLanServers( port{, flags} )" )
|
||
|
|
{
|
||
|
|
queryLanServers( dAtoi( argv[1] ), ( argc == 2 ? U8( dAtoi( argv[2] ) ) : 0 ) );
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
ConsoleFunction( queryMasterGameTypes, void, 1, 1, "queryMasterGameTypes()" )
|
||
|
|
{
|
||
|
|
argc;
|
||
|
|
argv;
|
||
|
|
queryMasterGameTypes();
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
ConsoleFunction( queryMasterServer, void, 2, 13, "queryMasterServer( port{, flags{, rulesSet{, missionType{, minPlayers{, maxPlayers{, maxBots{, regionMask{, maxPing{, minCPUSpeed{, filterFlags{, buddyList }}}}}}}}}}} )" )
|
||
|
|
{
|
||
|
|
char rulesSet[64];
|
||
|
|
char missionType[64];
|
||
|
|
dStrncpy( rulesSet, ( argc > 3 ? argv[3] : "any" ), sizeof( rulesSet ) );
|
||
|
|
dStrncpy( missionType, ( argc > 4 ? argv[4] : "any" ), sizeof( missionType ) );
|
||
|
|
U8 buddyCount = 0;
|
||
|
|
U32* guidArray = NULL;
|
||
|
|
if ( argc == 13 )
|
||
|
|
{
|
||
|
|
// fill the guid array:
|
||
|
|
char* buf = new char[dStrlen( argv[11] ) + 1];
|
||
|
|
dStrcpy( buf, argv[11] );
|
||
|
|
|
||
|
|
char* slave = buf;
|
||
|
|
U8 last = 0;
|
||
|
|
while ( *slave )
|
||
|
|
{
|
||
|
|
last = *slave++;
|
||
|
|
if ( last == '\t' )
|
||
|
|
{
|
||
|
|
buddyCount++;
|
||
|
|
last = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if ( last )
|
||
|
|
buddyCount++;
|
||
|
|
|
||
|
|
if ( buddyCount )
|
||
|
|
{
|
||
|
|
guidArray = new U32[buddyCount];
|
||
|
|
|
||
|
|
slave = dStrtok( buf, "\t" );
|
||
|
|
for ( U32 i = 0; i < buddyCount && slave; i++ )
|
||
|
|
{
|
||
|
|
guidArray[i] = U32( dAtoi( slave ) );
|
||
|
|
slave = dStrtok( NULL, "\t" );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
delete [] buf;
|
||
|
|
}
|
||
|
|
|
||
|
|
queryMasterServer( dAtoi(argv[1]),
|
||
|
|
( argc > 2 ? U8( dAtoi( argv[2] ) ) : 0 ),
|
||
|
|
rulesSet,
|
||
|
|
missionType,
|
||
|
|
( argc > 5 ? U8( dAtoi( argv[5] ) ) : 0 ),
|
||
|
|
( argc > 6 ? U8( dAtoi( argv[6] ) ) : 255 ),
|
||
|
|
( argc > 7 ? U8( dAtoi( argv[7] ) ) : 16 ),
|
||
|
|
( argc > 8 ? U32( dAtoi( argv[8] ) ) : 0xFFFFFFFF ),
|
||
|
|
( argc > 9 ? U32( dAtoi( argv[9] ) ) : 0 ),
|
||
|
|
( argc > 10 ? U32( dAtoi( argv[10] ) ) : 0 ),
|
||
|
|
( argc > 11 ? U32( dAtoi( argv[11] ) ) : 0 ),
|
||
|
|
buddyCount,
|
||
|
|
guidArray );
|
||
|
|
|
||
|
|
if ( guidArray )
|
||
|
|
delete [] guidArray;
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
ConsoleFunction( queryFavoriteServers, void, 1, 2, "queryFavoriteServers( {, flags} )" )
|
||
|
|
{
|
||
|
|
queryFavoriteServers( argc == 2 ? U8( dAtoi( argv[1] ) ) : 0 );
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
ConsoleFunction( querySingleServer, void, 2, 3, "querySingleServer( address{, flags} )" )
|
||
|
|
{
|
||
|
|
NetAddress addr;
|
||
|
|
Net::stringToAddress( argv[1], &addr );
|
||
|
|
querySingleServer( &addr, ( argc == 3 ? U8( dAtoi( argv[2] ) ) : 0 ) );
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
ConsoleMethod( GuiServerBrowser, sort, void, 2, 2, "browser.sort()" )
|
||
|
|
{
|
||
|
|
argc; argv;
|
||
|
|
static_cast<GuiServerBrowser*>( object )->sort();
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
ConsoleMethod( GuiServerBrowser, getServerStatus, const char*, 2, 2, "browser.getServerStatus()" )
|
||
|
|
{
|
||
|
|
argc; argv;
|
||
|
|
return( static_cast<GuiServerBrowser*>( object )->getSelectedServerStatus() );
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
ConsoleMethod( GuiServerBrowser, getServerInfoString, const char*, 2, 2, "browser.getServerInfoString()" )
|
||
|
|
{
|
||
|
|
argc; argv;
|
||
|
|
return( static_cast<GuiServerBrowser*>( object )->getSelectedServerInfoString() );
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
ConsoleMethod( GuiServerBrowser, getServerContentString, const char*, 2, 2, "browser.getServerContentString()" )
|
||
|
|
{
|
||
|
|
argc; argv;
|
||
|
|
return( static_cast<GuiServerBrowser*>( object )->getSelectedServerContentString() );
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
#ifdef DEBUG
|
||
|
|
ConsoleFunction( addFakeServers, void, 2, 2, "addFakeServers( howMany )" )
|
||
|
|
{
|
||
|
|
argc;
|
||
|
|
addFakeServers( dAtoi( argv[1] ) );
|
||
|
|
}
|
||
|
|
#endif // DEBUG
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
void GuiServerBrowser::initPersistFields()
|
||
|
|
{
|
||
|
|
Parent::initPersistFields();
|
||
|
|
addField( "iconBase", TypeString, Offset( mIconBase, GuiServerBrowser ) );
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
const char* GuiServerBrowser::getScriptValue()
|
||
|
|
{
|
||
|
|
if ( mSelectedRow < 0 || mSelectedRow >= gServerList.size() )
|
||
|
|
return "";
|
||
|
|
|
||
|
|
static char buf[256];
|
||
|
|
char address[256];
|
||
|
|
|
||
|
|
ServerInfo &si = gServerList[mSelectedRow];
|
||
|
|
Net::addressToString( &si.address, address );
|
||
|
|
dSprintf( buf, sizeof( buf ), "%s\t%s\t%d\t%s\t%s", si.name ? si.name : "?", address, si.ping, si.missionName ? si.missionName : "?", si.missionType ? si.missionType : "?" );
|
||
|
|
return buf;
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
bool GuiServerBrowser::onWake()
|
||
|
|
{
|
||
|
|
if ( !Parent::onWake() )
|
||
|
|
return false;
|
||
|
|
|
||
|
|
// Set the row height based on the font:
|
||
|
|
if ( mFont )
|
||
|
|
mRowHeight = mFont->getHeight() + 3;
|
||
|
|
|
||
|
|
// Get the browser icons:
|
||
|
|
if ( mIconBase[0] )
|
||
|
|
{
|
||
|
|
char buf[256];
|
||
|
|
dSprintf( buf, sizeof( buf ), "%s_favorite.png", mIconBase );
|
||
|
|
mTexFavorite = TextureHandle( buf, BitmapTexture );
|
||
|
|
dSprintf( buf, sizeof( buf ), "%s_favorite_hi.png", mIconBase );
|
||
|
|
mTexFavoriteHI = TextureHandle( buf, BitmapTexture );
|
||
|
|
dSprintf( buf, sizeof( buf ), "%s_notqueried.png", mIconBase );
|
||
|
|
mTexNotQueried = TextureHandle( buf, BitmapTexture );
|
||
|
|
dSprintf( buf, sizeof( buf ), "%s_notqueried_hi.png", mIconBase );
|
||
|
|
mTexNotQueriedHI = TextureHandle( buf, BitmapTexture );
|
||
|
|
dSprintf( buf, sizeof( buf ), "%s_querying.png", mIconBase );
|
||
|
|
mTexQuerying = TextureHandle( buf, BitmapTexture );
|
||
|
|
dSprintf( buf, sizeof( buf ), "%s_querying_hi.png", mIconBase );
|
||
|
|
mTexQueryingHI = TextureHandle( buf, BitmapTexture );
|
||
|
|
dSprintf( buf, sizeof( buf ), "%s_timedout.png", mIconBase );
|
||
|
|
mTexTimedOut = TextureHandle( buf, BitmapTexture );
|
||
|
|
dSprintf( buf, sizeof( buf ), "%s_dedicated.png", mIconBase );
|
||
|
|
mTexDedicated = TextureHandle( buf, BitmapTexture );
|
||
|
|
dSprintf( buf, sizeof( buf ), "%s_dedicated_hi.png", mIconBase );
|
||
|
|
mTexDedicatedHI = TextureHandle( buf, BitmapTexture );
|
||
|
|
dSprintf( buf, sizeof( buf ), "%s_passworded.png", mIconBase );
|
||
|
|
mTexPassworded = TextureHandle( buf, BitmapTexture );
|
||
|
|
dSprintf( buf, sizeof( buf ), "%s_passworded_hi.png", mIconBase );
|
||
|
|
mTexPasswordedHI = TextureHandle( buf, BitmapTexture );
|
||
|
|
dSprintf( buf, sizeof( buf ), "%s_tourney.png", mIconBase );
|
||
|
|
mTexTournament = TextureHandle( buf, BitmapTexture );
|
||
|
|
dSprintf( buf, sizeof( buf ), "%s_tourney_hi.png", mIconBase );
|
||
|
|
mTexTournamentHI = TextureHandle( buf, BitmapTexture );
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
void GuiServerBrowser::onSleep()
|
||
|
|
{
|
||
|
|
Parent::onSleep();
|
||
|
|
|
||
|
|
mTexFavorite = NULL;
|
||
|
|
mTexFavoriteHI = NULL;
|
||
|
|
mTexNotQueried = NULL;
|
||
|
|
mTexNotQueriedHI = NULL;
|
||
|
|
mTexQuerying = NULL;
|
||
|
|
mTexQueryingHI = NULL;
|
||
|
|
mTexTimedOut = NULL;
|
||
|
|
mTexDedicated = NULL;
|
||
|
|
mTexDedicatedHI = NULL;
|
||
|
|
mTexPassworded = NULL;
|
||
|
|
mTexPasswordedHI = NULL;
|
||
|
|
mTexTournament = NULL;
|
||
|
|
mTexTournamentHI = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
void GuiServerBrowser::onPreRender()
|
||
|
|
{
|
||
|
|
if ( gServerBrowserDirty )
|
||
|
|
{
|
||
|
|
sort();
|
||
|
|
gServerBrowserDirty = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
void GuiServerBrowser::updateList()
|
||
|
|
{
|
||
|
|
if ( mNumRows != gServerList.size() )
|
||
|
|
setNumRows( gServerList.size() );
|
||
|
|
|
||
|
|
setUpdate();
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
void GuiServerBrowser::sort()
|
||
|
|
{
|
||
|
|
mNumRows = gServerList.size();
|
||
|
|
sSortColumnKey = mSortColumnKey;
|
||
|
|
sSortInc = mSortInc;
|
||
|
|
sSecondarySortColumnKey = mSecondarySortColumnKey;
|
||
|
|
sSecondarySortInc = mSecondarySortInc;
|
||
|
|
|
||
|
|
if ( gServerList.size() > 1 )
|
||
|
|
dQsort( (void*) &(gServerList[0]), gServerList.size(), sizeof(ServerInfo), serverListRowCompare );
|
||
|
|
|
||
|
|
selectRowByAddress();
|
||
|
|
setUpdate();
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
void GuiServerBrowser::onCellSelected( S32 row, S32 column )
|
||
|
|
{
|
||
|
|
AssertFatal( ( column >= 0 && column < mColumnInfoList.size() ), "Invalid column selected!" );
|
||
|
|
AssertFatal( ( row >= 0 && row < mNumRows ), "Invalid row selected!" );
|
||
|
|
|
||
|
|
char addrString[256];
|
||
|
|
Net::addressToString( &gServerList[row].address, addrString );
|
||
|
|
if ( mColumnInfoList[column].key == Favorite_Column )
|
||
|
|
{
|
||
|
|
gServerList[row].isFavorite = !gServerList[row].isFavorite;
|
||
|
|
setUpdate();
|
||
|
|
|
||
|
|
// Execute the script function:
|
||
|
|
if ( gServerList[row].isFavorite )
|
||
|
|
Con::executef( this, 3, "addFavorite", gServerList[row].name, (const char*) addrString );
|
||
|
|
else
|
||
|
|
Con::executef( this, 2, "removeFavorite", (const char*) addrString );
|
||
|
|
}
|
||
|
|
|
||
|
|
mSelectedAddress = gServerList[row].address;
|
||
|
|
Con::executef( this, 2, "onSelect", (const char*) addrString );
|
||
|
|
|
||
|
|
// Call the console function:
|
||
|
|
if ( mConsoleCommand[0] )
|
||
|
|
Con::evaluate( mConsoleCommand, false );
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
void GuiServerBrowser::onRenderCell( Point2I offset, Point2I cell, bool selected, bool mouseOver )
|
||
|
|
{
|
||
|
|
ServerInfo &si = gServerList[cell.y];
|
||
|
|
ColumnInfo* ci = &mColumnInfoList[cell.x];
|
||
|
|
|
||
|
|
// Let the parent take care of the basics:
|
||
|
|
Parent::onRenderCell( offset, cell, selected, mouseOver );
|
||
|
|
|
||
|
|
bool drawText = true;
|
||
|
|
const char* text = NULL;
|
||
|
|
char buffer[256];
|
||
|
|
S32 bmpOffsetX = 0;
|
||
|
|
Point2I drawPos;
|
||
|
|
TextureHandle hTex = NULL;
|
||
|
|
dglClearBitmapModulation();
|
||
|
|
|
||
|
|
switch( ci->key )
|
||
|
|
{
|
||
|
|
case Name_Column:
|
||
|
|
text = si.name ? si.name : "--";
|
||
|
|
break;
|
||
|
|
|
||
|
|
case Status_Column:
|
||
|
|
// Draw bitmap(s):
|
||
|
|
if ( si.isNew() )
|
||
|
|
{
|
||
|
|
hTex = selected ? mTexNotQueriedHI : mTexNotQueried;
|
||
|
|
if ( hTex )
|
||
|
|
{
|
||
|
|
drawPos.x = offset.x + ( ( ci->width - hTex.getWidth() ) / 2 );
|
||
|
|
drawPos.y = offset.y;
|
||
|
|
if ( mRowHeight > hTex.getHeight() )
|
||
|
|
drawPos.y += ( ( mRowHeight - hTex.getHeight() ) / 2 );
|
||
|
|
dglDrawBitmap( hTex, drawPos );
|
||
|
|
}
|
||
|
|
else
|
||
|
|
text = "?";
|
||
|
|
}
|
||
|
|
else if ( si.isQuerying() )
|
||
|
|
{
|
||
|
|
hTex = selected ? mTexQueryingHI : mTexQuerying;
|
||
|
|
if ( hTex )
|
||
|
|
{
|
||
|
|
drawPos.x = offset.x + ( ( ci->width - hTex.getWidth() ) / 2 );
|
||
|
|
drawPos.y = offset.y;
|
||
|
|
if ( mRowHeight > hTex.getHeight() )
|
||
|
|
drawPos.y += ( ( mRowHeight - hTex.getHeight() ) / 2 );
|
||
|
|
dglDrawBitmap( hTex, drawPos );
|
||
|
|
}
|
||
|
|
else
|
||
|
|
text = "Q";
|
||
|
|
}
|
||
|
|
else if ( si.hasResponded() )
|
||
|
|
{
|
||
|
|
TextureHandle hTexDedicated = NULL, hTexTournament = NULL;
|
||
|
|
U32 numIcons = 0;
|
||
|
|
U32 width = 0, height = 0;
|
||
|
|
if ( si.isPassworded() )
|
||
|
|
{
|
||
|
|
hTex = selected ? mTexPasswordedHI : mTexPassworded;
|
||
|
|
if ( hTex )
|
||
|
|
{
|
||
|
|
width += hTex.getWidth();
|
||
|
|
height = hTex.getHeight();
|
||
|
|
numIcons++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( si.isDedicated() )
|
||
|
|
{
|
||
|
|
hTexDedicated = selected ? mTexDedicatedHI : mTexDedicated;
|
||
|
|
if ( hTexDedicated )
|
||
|
|
{
|
||
|
|
width += hTexDedicated.getWidth();
|
||
|
|
if ( hTexDedicated.getHeight() > height )
|
||
|
|
height = hTexDedicated.getHeight();
|
||
|
|
numIcons++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( si.isTournament() )
|
||
|
|
{
|
||
|
|
hTexTournament = selected ? mTexTournamentHI : mTexTournament;
|
||
|
|
if ( hTexTournament )
|
||
|
|
{
|
||
|
|
width += hTexTournament.getWidth();
|
||
|
|
if ( hTexTournament.getHeight() > height )
|
||
|
|
height = hTexTournament.getHeight();
|
||
|
|
numIcons++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( si.isLinux() )
|
||
|
|
{
|
||
|
|
width += mFont->getStrWidth( "L" );
|
||
|
|
numIcons++;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Draw icons if there are any:
|
||
|
|
if ( numIcons > 0 )
|
||
|
|
{
|
||
|
|
// Try to center the icons:
|
||
|
|
drawPos = offset;
|
||
|
|
if ( ci->width > width )
|
||
|
|
drawPos.x += ( ( ci->width - width ) / 2 );
|
||
|
|
|
||
|
|
if ( mRowHeight > height )
|
||
|
|
drawPos.y += ( ( mRowHeight - height ) / 2 );
|
||
|
|
|
||
|
|
if ( hTex )
|
||
|
|
{
|
||
|
|
dglDrawBitmap( hTex, drawPos );
|
||
|
|
drawPos.x += hTex.getWidth();
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( hTexDedicated )
|
||
|
|
{
|
||
|
|
dglDrawBitmap( hTexDedicated, drawPos );
|
||
|
|
drawPos.x += hTexDedicated.getWidth();
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( hTexTournament )
|
||
|
|
{
|
||
|
|
dglDrawBitmap( hTexTournament, drawPos );
|
||
|
|
drawPos.x += hTexTournament.getWidth();
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( si.isLinux() )
|
||
|
|
{
|
||
|
|
dglSetBitmapModulation( selected ? ColorI( 4, 40, 5 ) : ColorI( 0, 255, 0 ) );
|
||
|
|
drawPos.y = offset.y + ( ( mRowHeight - mFont->getHeight() ) / 2 );
|
||
|
|
dglDrawText( mFont, drawPos, "L" );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else if ( mTexTimedOut )
|
||
|
|
{
|
||
|
|
drawPos.x = offset.x + ( ( ci->width - mTexTimedOut.getWidth() ) / 2 );
|
||
|
|
drawPos.y = offset.y;
|
||
|
|
if ( mRowHeight > mTexTimedOut.getHeight() )
|
||
|
|
drawPos.y += ( ( mRowHeight - mTexTimedOut.getHeight() ) / 2 );
|
||
|
|
dglDrawBitmap( mTexTimedOut, drawPos );
|
||
|
|
}
|
||
|
|
else
|
||
|
|
text = "X";
|
||
|
|
break;
|
||
|
|
|
||
|
|
case Favorite_Column:
|
||
|
|
if ( si.isFavorite )
|
||
|
|
{
|
||
|
|
hTex = selected ? mTexFavoriteHI : mTexFavorite;
|
||
|
|
if ( hTex )
|
||
|
|
{
|
||
|
|
drawPos.x = offset.x + ( ( ci->width - hTex.getWidth() ) / 2 );
|
||
|
|
drawPos.y = offset.y;
|
||
|
|
if ( mRowHeight > hTex.getHeight() )
|
||
|
|
drawPos.y += ( ( mRowHeight - hTex.getHeight() ) / 2 );
|
||
|
|
dglDrawBitmap( hTex, drawPos );
|
||
|
|
}
|
||
|
|
else
|
||
|
|
text = "X";
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case Ping_Column:
|
||
|
|
dSprintf( buffer, 255, "%d", si.ping );
|
||
|
|
text = buffer;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case IP_Column:
|
||
|
|
Net::addressToString( &si.address, buffer );
|
||
|
|
dStrcpy( buffer, buffer + 3 );
|
||
|
|
text = buffer;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case Players_Column:
|
||
|
|
dSprintf( buffer, 255, "%d/%d (%d)", si.numPlayers, si.maxPlayers, si.numBots );
|
||
|
|
text = buffer;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case GameType_Column:
|
||
|
|
text = si.gameType ? si.gameType : "--";
|
||
|
|
break;
|
||
|
|
|
||
|
|
case MissionType_Column:
|
||
|
|
text = si.missionType ? si.missionType : "--";
|
||
|
|
break;
|
||
|
|
|
||
|
|
case Map_Column:
|
||
|
|
if ( si.missionName )
|
||
|
|
{
|
||
|
|
dStrcpy( buffer, si.missionName );
|
||
|
|
// Clip off the file extension:
|
||
|
|
char* temp = dStrstr( buffer, const_cast<char*>( ".mis" ) );
|
||
|
|
if ( temp )
|
||
|
|
*temp = 0;
|
||
|
|
text = buffer;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
text = "--";
|
||
|
|
break;
|
||
|
|
|
||
|
|
case CPU_Column:
|
||
|
|
if ( si.cpuSpeed )
|
||
|
|
{
|
||
|
|
dSprintf( buffer, 255, "%d MHz", si.cpuSpeed );
|
||
|
|
text = buffer;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
text = "--";
|
||
|
|
break;
|
||
|
|
|
||
|
|
case Version_Column:
|
||
|
|
if ( si.version )
|
||
|
|
{
|
||
|
|
dSprintf( buffer, 255, "%d", si.version );
|
||
|
|
text = buffer;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
text = "--";
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
Con::errorf( ConsoleLogEntry::General, "Invalid column key encountered!" );
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Finally, draw the text ( if there is any ):
|
||
|
|
if ( drawText && text && *text )
|
||
|
|
{
|
||
|
|
// Determine the font color:
|
||
|
|
ColorI color;
|
||
|
|
if ( si.gameType && dStricmp( si.gameType, "base" ) != 0 ) // Mod colors
|
||
|
|
color = selected ? mProfile->mFontColors[6] : ( mouseOver ? mProfile->mFontColors[5] : mProfile->mFontColors[4] );
|
||
|
|
else if ( si.version != getVersionNumber() ) // Disparate build version colors
|
||
|
|
color = selected ? mProfile->mFontColors[9] : ( mouseOver ? mProfile->mFontColors[8] : mProfile->mFontColors[7] );
|
||
|
|
else // Base colors
|
||
|
|
color = selected ? mProfile->mFontColorSEL : ( mouseOver ? mProfile->mFontColorHL : mProfile->mFontColor );
|
||
|
|
|
||
|
|
S32 textWidth = mFont->getStrWidth( text );
|
||
|
|
Point2I textStart;
|
||
|
|
textStart.x = offset.x + getMax( 4, ( ci->width - textWidth ) / 2 );
|
||
|
|
textStart.y = offset.y + ( ( mRowHeight - ( mFont->getHeight() - 2 ) ) / 2 );
|
||
|
|
dglSetBitmapModulation( color );
|
||
|
|
dglDrawText( mFont, textStart, text );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
void GuiServerBrowser::selectRowByAddress()
|
||
|
|
{
|
||
|
|
if ( mSelectedRow >= 0 || mSelectedRow < gServerList.size() )
|
||
|
|
{
|
||
|
|
bool foundIt = false;
|
||
|
|
for ( S32 i = 0; i < gServerList.size(); i++ )
|
||
|
|
{
|
||
|
|
if ( Net::compareAddresses( &mSelectedAddress, &gServerList[i].address ) )
|
||
|
|
{
|
||
|
|
selectCell( i, 0 );
|
||
|
|
foundIt = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( !foundIt )
|
||
|
|
mSelectedRow = -1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
const char* GuiServerBrowser::getSelectedServerStatus()
|
||
|
|
{
|
||
|
|
if ( mSelectedRow < 0 || mSelectedRow >= gServerList.size() )
|
||
|
|
return( "invalid" );
|
||
|
|
|
||
|
|
ServerInfo* si = &gServerList[mSelectedRow];
|
||
|
|
if ( si->isNew() )
|
||
|
|
return( "new" );
|
||
|
|
if ( si->isQuerying() )
|
||
|
|
return( "querying" );
|
||
|
|
if ( si->hasResponded() )
|
||
|
|
return( "responded" );
|
||
|
|
if ( si->isUpdating() )
|
||
|
|
return( "updating" );
|
||
|
|
|
||
|
|
return( "timedOut" );
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
const char* GuiServerBrowser::getSelectedServerInfoString()
|
||
|
|
{
|
||
|
|
if ( mSelectedRow < 0 || mSelectedRow >= gServerList.size() )
|
||
|
|
return( "" );
|
||
|
|
|
||
|
|
ServerInfo* si = &gServerList[mSelectedRow];
|
||
|
|
U32 bufSize = 0;
|
||
|
|
if ( si->name )
|
||
|
|
bufSize += ( dStrlen( si->name ) + 1 );
|
||
|
|
char addrString[256];
|
||
|
|
Net::addressToString( &si->address, addrString );
|
||
|
|
bufSize += dStrlen( addrString ) - 2;
|
||
|
|
if ( si->gameType )
|
||
|
|
bufSize += ( dStrlen( si->gameType ) + 1 );
|
||
|
|
|
||
|
|
// Build the flag string:
|
||
|
|
char flagString[256];
|
||
|
|
flagString[0] = 0;
|
||
|
|
bool firstFlag = true;
|
||
|
|
if ( si->isDedicated() )
|
||
|
|
{
|
||
|
|
dStrcpy( flagString, "Dedicated" );
|
||
|
|
firstFlag = false;
|
||
|
|
}
|
||
|
|
if ( si->isPassworded() )
|
||
|
|
{
|
||
|
|
dStrcat( flagString, firstFlag ? "Passworded" : "; Passworded" );
|
||
|
|
firstFlag = false;
|
||
|
|
}
|
||
|
|
if ( si->isTournament() )
|
||
|
|
{
|
||
|
|
dStrcat( flagString, firstFlag ? "Tournament Mode" : "; Tournament Mode" );
|
||
|
|
firstFlag = false;
|
||
|
|
}
|
||
|
|
if ( si->isLinux() )
|
||
|
|
{
|
||
|
|
dStrcat( flagString, firstFlag ? "Linux" : "; Linux" );
|
||
|
|
firstFlag = false;
|
||
|
|
}
|
||
|
|
if ( !si->areSmurfsAllowed() )
|
||
|
|
dStrcat( flagString, firstFlag ? "No Aliases" : "; No Aliases" );
|
||
|
|
bufSize += ( dStrlen( flagString ) + 1 );
|
||
|
|
|
||
|
|
if ( si->missionType )
|
||
|
|
bufSize += ( dStrlen( si->missionType ) + 1 );
|
||
|
|
if ( si->missionName )
|
||
|
|
bufSize += ( dStrlen( si->missionName ) + 1 );
|
||
|
|
if ( si->infoString )
|
||
|
|
bufSize += ( dStrlen( si->infoString ) + 1 );
|
||
|
|
|
||
|
|
char* returnString = Con::getReturnBuffer( bufSize );
|
||
|
|
dSprintf( returnString, bufSize, "%s\n%s\n%s\n%s\n%s\n%s\n%s",
|
||
|
|
( si->name ? si->name : "" ),
|
||
|
|
&addrString[3],
|
||
|
|
( si->gameType ? si->gameType : "" ),
|
||
|
|
flagString,
|
||
|
|
( si->missionType ? si->missionType : "" ),
|
||
|
|
( si->missionName ? si->missionName : "" ),
|
||
|
|
( si->infoString ? si->infoString : "" ) );
|
||
|
|
|
||
|
|
return( returnString );
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------------------------------
|
||
|
|
const char* GuiServerBrowser::getSelectedServerContentString()
|
||
|
|
{
|
||
|
|
if ( mSelectedRow < 0 || mSelectedRow >= gServerList.size() )
|
||
|
|
return( "" );
|
||
|
|
|
||
|
|
return( gServerList[mSelectedRow].contentString ? gServerList[mSelectedRow].contentString : "" );
|
||
|
|
}
|