Torque3D Documentation / _generateds / serverQuery.cpp

serverQuery.cpp

Engine/source/app/net/serverQuery.cpp

More...

Classes:

Public Functions

ConsoleFunctionGroupBegin(ServerQuery , "Functions which allow you <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> query the LAN or a master server <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> online games." )
DefineConsoleFunction(cancelServerQuery , void , () , "cancelServerQuery();" )
DefineConsoleFunction(getServerCount , int , () , "getServerCount();" )
DefineConsoleFunction(queryAllServers , void , (U32 lanPort, U32 flags, const char *gameType, const char *missionType, U32 minPlayers, U32 maxPlayers, U32 maxBots, U32 regionMask, U32 maxPing, U32 minCPU, U32 filterFlags) , "queryAllServers(...);" )
DefineConsoleFunction(queryLanServers , void , (U32 lanPort, U32 flags, const char *gameType, const char *missionType, U32 minPlayers, U32 maxPlayers, U32 maxBots, U32 regionMask, U32 maxPing, U32 minCPU, U32 filterFlags) , "queryLanServers(...);" )
DefineConsoleFunction(queryMasterServer , void , (U32 flags, const char *gameType, const char *missionType, U32 minPlayers, U32 maxPlayers, U32 maxBots, U32 regionMask, U32 maxPing, U32 minCPU, U32 filterFlags) , "queryMasterServer(...);" )
DefineConsoleFunction(querySingleServer , void , (const char *addrText, U8 flags) , (0) , "querySingleServer(address, flags);" )
DefineConsoleFunction(setServerInfo , bool , (U32 index) , "setServerInfo(index);" )
DefineConsoleFunction(startHeartbeat , void , () , "startHeartbeat();" )
DefineConsoleFunction(stopHeartbeat , void , () , "stopHeartbeat();" )
DefineConsoleFunction(stopServerQuery , void , () , "stopServerQuery();" )
processPingsAndQueries(U32 session, bool schedule)
queryLanServers(U32 port, U8 flags, const char * gameType, const char * missionType, U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, U16 minCPU, U8 filterFlags)
queryMasterServer(U8 flags, const char * gameType, const char * missionType, U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, U16 minCPU, U8 filterFlags, U8 buddyCount, U32 * buddyList)
readCString(BitStream * stream, char * buffer)
readLongCString(BitStream * stream, char * buffer)
sendPacket(U8 pType, const NetAddress * addr, U32 key, U32 session, U8 flags)
writeCString(BitStream * stream, const char * string)
writeLongCString(BitStream * stream, const char * string)

Detailed Description

Public Variables

Vector< NetAddress > gFinishedList (__FILE__, __LINE__)
bool gGotFirstListPacket 
const U32 gHeartbeatInterval 
U32 gHeartbeatSeq 
S32 gKey 
Vector< MasterInfo > gMasterServerList (__FILE__, __LINE__)
Ping gMasterServerPing 
NetAddress gMasterServerQueryAddress 
const S32 gMasterServerRetryCount 
const S32 gMasterServerTimeout 
const S32 gMaxConcurrentPings 
const S32 gMaxConcurrentQueries 
DemoNetInterface gNetInterface 
const S32 gPacketRetryCount 
Vector< PacketStatus > gPacketStatusList (__FILE__, __LINE__)
const S32 gPacketTimeout 
Vector< Ping > gPingList (__FILE__, __LINE__)
const S32 gPingRetryCount 
S32 gPingSession 
const S32 gPingTimeout 
Vector< Ping > gQueryList (__FILE__, __LINE__)
const S32 gQueryRetryCount 
const S32 gQueryTimeout 
bool gServerBrowserDirty 
Vector< ServerInfo > gServerList (__FILE__, __LINE__)
U32 gServerPingCount 
U32 gServerQueryCount 
ServerFilter sActiveFilter 
bool sgServerQueryActive 
const char * versionString 

Public Functions

addressFinished(const NetAddress * addr)

cancelServerQuery()

clearServerList()

ConsoleFunctionGroupBegin(ServerQuery , "Functions which allow you <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> query the LAN or a master server <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> online games." )

ConsoleFunctionGroupEnd(ServerQuery )

countPingRequests()

DefineConsoleFunction(cancelServerQuery , void , () , "cancelServerQuery();" )

DefineConsoleFunction(getServerCount , int , () , "getServerCount();" )

DefineConsoleFunction(queryAllServers , void , (U32 lanPort, U32 flags, const char *gameType, const char *missionType, U32 minPlayers, U32 maxPlayers, U32 maxBots, U32 regionMask, U32 maxPing, U32 minCPU, U32 filterFlags) , "queryAllServers(...);" )

DefineConsoleFunction(queryLanServers , void , (U32 lanPort, U32 flags, const char *gameType, const char *missionType, U32 minPlayers, U32 maxPlayers, U32 maxBots, U32 regionMask, U32 maxPing, U32 minCPU, U32 filterFlags) , "queryLanServers(...);" )

DefineConsoleFunction(queryMasterServer , void , (U32 flags, const char *gameType, const char *missionType, U32 minPlayers, U32 maxPlayers, U32 maxBots, U32 regionMask, U32 maxPing, U32 minCPU, U32 filterFlags) , "queryMasterServer(...);" )

DefineConsoleFunction(querySingleServer , void , (const char *addrText, U8 flags) , (0) , "querySingleServer(address, flags);" )

DefineConsoleFunction(setServerInfo , bool , (U32 index) , "setServerInfo(index);" )

DefineConsoleFunction(startHeartbeat , void , () , "startHeartbeat();" )

DefineConsoleFunction(stopHeartbeat , void , () , "stopHeartbeat();" )

DefineConsoleFunction(stopServerQuery , void , () , "stopServerQuery();" )

findOrCreateServerInfo(const NetAddress * addr)

findPingEntry(Vector< Ping > & v, const NetAddress * addr)

findServerInfo(const NetAddress * addr)

getMasterServerList()

handleExtendedMasterServerListResponse(BitStream * stream, U32 key, U8 )

handleGameInfoRequest(const NetAddress * address, U32 key, U8 flags)

handleGameInfoResponse(const NetAddress * address, BitStream * stream, U32 , U8 )

handleGameMasterInfoRequest(const NetAddress * address, U32 key, U8 flags)

handleGamePingRequest(const NetAddress * address, U32 key, U8 flags)

handleGamePingResponse(const NetAddress * address, BitStream * stream, U32 key, U8 )

handleMasterServerGameTypesResponse(BitStream * stream, U32 , U8 )

handleMasterServerListResponse(BitStream * stream, U32 key, U8 )

pickMasterServer()

processHeartbeat(U32 )

processMasterServerQuery(U32 session)

processPingsAndQueries(U32 session, bool schedule)

processServerListPackets(U32 session)

pushPingBroadcast(const NetAddress * addr)

pushPingRequest(const NetAddress * addr)

pushServerFavorites()

queryFavoriteServers(U8 )

queryLanServers(U32 port, U8 flags, const char * gameType, const char * missionType, U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, U16 minCPU, U8 filterFlags)

queryMasterGameTypes()

queryMasterServer(U8 flags, const char * gameType, const char * missionType, U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, U16 minCPU, U8 filterFlags, U8 buddyCount, U32 * buddyList)

querySingleServer(const NetAddress * addr, U8 )

readCString(BitStream * stream, char * buffer)

readLongCString(BitStream * stream, char * buffer)

removeServerInfo(const NetAddress * addr)

sendHeartbeat(U8 flags)

sendPacket(U8 pType, const NetAddress * addr, U32 key, U32 session, U8 flags)

stopServerQuery()

updatePingProgress()

updateQueryProgress()

writeCString(BitStream * stream, const char * string)

writeLongCString(BitStream * stream, const char * string)

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24//-----------------------------------------------------------------------------
  25// Server Query States:
  26//    1: Master server query status  -> wait for master response
  27//    2: Master server packet status -> wait for master packets to arrive
  28//    3: Server ping status          -> wait for servers to respond to pings
  29//    4: Server query status         -> wait for servers to respond to queries
  30//    5: Done
  31
  32// Master Server Packets:
  33// Header
  34//    message           Message id
  35//    flags             Query flags
  36//    sequenceNumber    Packet sequence id
  37
  38// Server Query Filter Packet
  39//    packetIndex       Request specific page # (rest empty)
  40//    gameType          Game type string
  41//    missionType       Mission type string
  42//    minPlayers        At least this many players
  43//    maxPlayers        No more than this many
  44//    regions           Region mask, 0 = all
  45//    version           Server version, 0 = any
  46//    filterFlags       Server flags (dedicated, etc), 0 = any
  47//    maxBots           No more than maxBots
  48//    minCPUSpeed       At least this fast
  49//    playerCount       Buddy list search
  50//    playerList[playerCount]
  51
  52// Master Server Info Packet
  53//    gameType          Game type string
  54//    missionType       Mission type string
  55//    maxPlayers        Max allowed
  56//    regions           Region mask
  57//    version           Server version #
  58//    infoFlags         Server flags (dedicated, etc)
  59//    numBots           Current bot count
  60//    CPUSpeed          Server CPU speed
  61//    playerCount       Current player count
  62//    playerList[playerCount]
  63
  64// Game Info Query Packet
  65//    gameType          Game type string
  66//    missionType       Mission type string
  67//    missionName       You get one guess...
  68//    satusFlags        Dedicated, etc.
  69//    playerCount       Current player count
  70//    maxPlayers        Max allowed
  71//    numBots           Current bot count
  72//    CPUSpeed          Server CPU speed
  73//    statusString      Server info message
  74//    statusString      Server status message
  75
  76// Accessed Environment Vars
  77//    Server::MissionType
  78//    Server::MissionName
  79//    Server::GameType
  80//    Server::ServerType
  81//    Server::PlayerCount
  82//    Server::BotCount
  83//    Server::GuidList[playerCount]
  84//    Server::Dedicated
  85//    Server::Status
  86//    Pref::Server::Name
  87//    Pref::Server::Password
  88//    Pref::Server::Info
  89//    Pref::Server::MaxPlayers
  90//    Pref::Server::RegionMask
  91//    Pref::Net::RegionMask
  92//    Pref::Client::Master[n]
  93//    Pref::Client::ServerFavoriteCount
  94//    Pref::Client::ServerFavorite[ServerFavoriteCount]
  95//-----------------------------------------------------------------------------
  96
  97#include "app/net/serverQuery.h"
  98
  99#include "platform/platform.h"
 100#include "core/dnet.h"
 101#include "core/util/tVector.h"
 102#include "core/stream/bitStream.h"
 103#include "console/console.h"
 104#include "console/simBase.h"
 105#include "app/banList.h"
 106#include "app/auth.h"
 107#include "sim/netConnection.h"
 108#include "sim/netInterface.h"
 109
 110// cafTODO: breaks T2D
 111#include "T3D/gameBase/gameConnection.h"
 112
 113// This is basically the server query protocol version now:
 114static const char* versionString = "VER1";
 115
 116struct MasterInfo
 117{
 118   NetAddress address;
 119   U32 region;
 120};
 121
 122Vector<ServerInfo> gServerList(__FILE__, __LINE__);
 123static Vector<MasterInfo> gMasterServerList(__FILE__, __LINE__);
 124static Vector<NetAddress> gFinishedList(__FILE__, __LINE__); // timed out servers and finished servers go here
 125NetAddress gMasterServerQueryAddress;
 126bool gServerBrowserDirty = false;
 127
 128static const U32 gHeartbeatInterval = 120000;
 129static const S32 gMasterServerRetryCount = 3;
 130static const S32 gMasterServerTimeout = 2000;
 131static const S32 gPacketRetryCount = 4;
 132static const S32 gPacketTimeout = 1000;
 133static const S32 gMaxConcurrentPings = 10;
 134static const S32 gMaxConcurrentQueries = 2;
 135static const S32 gPingRetryCount = 4;
 136static const S32 gPingTimeout = 800;
 137static const S32 gQueryRetryCount = 4;
 138static const S32 gQueryTimeout = 1000;
 139
 140// State variables:
 141static bool sgServerQueryActive = false;
 142static S32 gPingSession = 0;
 143static S32 gKey = 0;
 144static bool gGotFirstListPacket = false;
 145
 146// Variables used for the interface:
 147static U32 gServerPingCount = 0;
 148static U32 gServerQueryCount = 0;
 149static U32 gHeartbeatSeq = 0;
 150
 151class DemoNetInterface : public NetInterface
 152{
 153public:
 154   void handleInfoPacket(const NetAddress *address, U8 packetType, BitStream *stream);
 155};
 156
 157DemoNetInterface gNetInterface;
 158
 159ConsoleFunctionGroupBegin( ServerQuery, "Functions which allow you to query the LAN or a master server for online games.");
 160
 161//-----------------------------------------------------------------------------
 162
 163struct Ping
 164{
 165   NetAddress address;
 166   S32 session;
 167   S32 key;
 168   U32 time;
 169   U32 tryCount;
 170   bool broadcast;
 171};
 172
 173static Ping gMasterServerPing;
 174static Vector<Ping> gPingList(__FILE__, __LINE__);
 175static Vector<Ping> gQueryList(__FILE__, __LINE__);
 176
 177//-----------------------------------------------------------------------------
 178
 179struct PacketStatus
 180{
 181   U16  index;
 182   S32 key;
 183   U32 time;
 184   U32 tryCount;
 185
 186   PacketStatus() : index( 0 ), key(-1), time(0), tryCount( gPacketRetryCount ) {};
 187
 188   PacketStatus( U8 _index, S32 _key, U32 _time )
 189   {
 190      index = _index;
 191      key = _key;
 192      time = _time;
 193      tryCount = gPacketRetryCount;
 194   }
 195
 196   inline U8 getOldIndex() { return (U8)index; }
 197   inline U16 getIndex() { return index; }
 198};
 199
 200static Vector<PacketStatus> gPacketStatusList(__FILE__, __LINE__);
 201
 202//-----------------------------------------------------------------------------
 203
 204struct ServerFilter
 205{
 206   enum Type
 207   {
 208      Normal      = 0,
 209      Buddy       = 1,
 210      Offline     = 2,
 211      Favorites   = 3,
 212   };
 213
 214   enum // Query Flags
 215   {
 216      OnlineQuery       = 0,        // Authenticated with master
 217      OfflineQuery      = BIT(0),   // On our own
 218      NoStringCompress  = BIT(1),
 219      NewStyleResponse  = BIT(2),  // Include IPV6 servers
 220   };
 221
 222   enum // Filter flags:
 223   {
 224      Dedicated         = BIT(0),
 225      NotPassworded     = BIT(1),
 226      Linux             = BIT(2),
 227      CurrentVersion    = BIT(7),
 228      NotXenon          = BIT(6)
 229   };
 230
 231   enum // Region mask flags
 232   {
 233      RegionIsIPV4Address = BIT(30),
 234      RegionIsIPV6Address = BIT(31),
 235
 236      RegionAddressMask = RegionIsIPV4Address | RegionIsIPV6Address
 237   };
 238   
 239   //Rearranging the fields according to their sizes
 240   char* gameType;
 241   char* missionType;
 242   U8    queryFlags;
 243   U8    minPlayers;
 244   U8    maxPlayers;
 245   U8    maxBots;
 246   U8    filterFlags;
 247   U8    buddyCount;
 248   U16   minCPU;
 249   U32   regionMask;
 250   U32   maxPing;
 251   U32*  buddyList;
 252   Type  type;
 253
 254   ServerFilter()
 255   {
 256      type = Normal;
 257      queryFlags = NewStyleResponse;
 258      gameType = NULL;
 259      missionType = NULL;
 260      minPlayers = 0;
 261      maxPlayers = 255;
 262      maxBots = 16;
 263      regionMask = 0xFFFFFFFF;
 264      maxPing = 0;
 265      filterFlags = 0;
 266      minCPU = 0;
 267      buddyCount = 0;
 268      buddyList = NULL;
 269   }
 270
 271   ~ServerFilter()
 272   {
 273      if ( gameType )
 274         dFree( gameType );
 275      if ( missionType )
 276         dFree( missionType );
 277      if ( buddyList )
 278         dFree( buddyList );
 279   }
 280};
 281
 282static ServerFilter sActiveFilter;
 283
 284
 285//-----------------------------------------------------------------------------
 286// Forward function declarations:
 287//-----------------------------------------------------------------------------
 288
 289static void pushPingRequest( const NetAddress *addr );
 290static void pushPingBroadcast( const NetAddress *addr );
 291static void pushServerFavorites();
 292static bool pickMasterServer();
 293static S32 findPingEntry( Vector<Ping> &v, const NetAddress* addr );
 294static bool addressFinished( const NetAddress* addr );
 295static ServerInfo* findServerInfo( const NetAddress* addr );
 296static ServerInfo* findOrCreateServerInfo( const NetAddress* addr );
 297static void removeServerInfo( const NetAddress* addr );
 298static void sendPacket( U8 pType, const NetAddress* addr, U32 key, U32 session, U8 flags );
 299static void writeCString( BitStream* stream, const char* string );
 300static void readCString( BitStream* stream, char* buffer );
 301static void writeLongCString( BitStream* stream, const char* string );
 302static void readLongCString( BitStream* stream, char* buffer );
 303static void processMasterServerQuery( U32 session );
 304static void processPingsAndQueries( U32 session, bool schedule = true);
 305static void processServerListPackets( U32 session );
 306static void processHeartbeat(U32);
 307static void updatePingProgress();
 308static void updateQueryProgress();
 309Vector<MasterInfo>* getMasterServerList();
 310bool pickMasterServer();
 311void clearServerList();
 312
 313
 314//-----------------------------------------------------------------------------
 315// Events
 316//-----------------------------------------------------------------------------
 317
 318//----------------------------------------------------------------
 319class ProcessMasterQueryEvent : public SimEvent
 320{
 321   U32 session;
 322   public:
 323      ProcessMasterQueryEvent( U32 _session )
 324      {
 325         session = _session;
 326      }
 327      void process( SimObject *object )
 328      {
 329         processMasterServerQuery( session );
 330      }
 331};
 332
 333//----------------------------------------------------------------
 334class ProcessPingEvent : public SimEvent
 335{
 336   U32 session;
 337   public:
 338      ProcessPingEvent( U32 _session )
 339      {
 340         session = _session;
 341      }
 342      void process( SimObject *object )
 343      {
 344         processPingsAndQueries( session );
 345      }
 346};
 347
 348//----------------------------------------------------------------
 349class ProcessPacketEvent : public SimEvent
 350{
 351   U32 session;
 352   public:
 353      ProcessPacketEvent( U32 _session )
 354      {
 355         session = _session;
 356      }
 357
 358      void process( SimObject *object )
 359      {
 360         processServerListPackets( session );
 361      }
 362};
 363
 364//----------------------------------------------------------------
 365class HeartbeatEvent : public SimEvent
 366{
 367   U32 mSeq;
 368   public:
 369      HeartbeatEvent(U32 seq)
 370      {
 371         mSeq = seq;
 372      }
 373      void process( SimObject *object )
 374      {
 375         processHeartbeat(mSeq);
 376      }
 377};
 378
 379
 380//-----------------------------------------------------------------------------
 381// Public query methods
 382//-----------------------------------------------------------------------------
 383
 384//-----------------------------------------------------------------------------
 385
 386void queryLanServers(U32 port, U8 flags, const char* gameType, const char* missionType,
 387      U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, U16 minCPU,
 388      U8 filterFlags)
 389{
 390   sgServerQueryActive = true;
 391   pushServerFavorites();
 392
 393      sActiveFilter.type = ServerFilter::Offline;
 394
 395      // Clear the filter:
 396      if ( !sActiveFilter.gameType || dStricmp( sActiveFilter.gameType, "Any" ) != 0 )
 397      {
 398         sActiveFilter.gameType = (char*) dRealloc( sActiveFilter.gameType, 4 );
 399         dStrcpy( sActiveFilter.gameType, "Any" );
 400      }
 401      if ( !sActiveFilter.missionType || dStricmp( sActiveFilter.missionType, "Any" ) != 0 )
 402      {
 403         sActiveFilter.missionType = (char*) dRealloc( sActiveFilter.missionType, 4 );
 404         dStrcpy( sActiveFilter.missionType, "Any" );
 405      }
 406      sActiveFilter.queryFlags   = 0;
 407   sActiveFilter.minPlayers   = minPlayers;
 408   sActiveFilter.maxPlayers   = maxPlayers;
 409   sActiveFilter.maxBots      = maxBots;
 410   sActiveFilter.regionMask   = regionMask;
 411   sActiveFilter.maxPing      = maxPing;
 412   sActiveFilter.minCPU       = minCPU;
 413   sActiveFilter.filterFlags  = filterFlags;
 414
 415   NetAddress addr;
 416   char addrText[256];
 417
 418   // IPV4
 419   dSprintf( addrText, sizeof( addrText ), "IP:BROADCAST:%d", port );
 420   Net::stringToAddress( addrText, &addr );
 421   pushPingBroadcast( &addr );
 422
 423   // IPV6
 424   dSprintf(addrText, sizeof(addrText), "IP6:MULTICAST:%d", port);
 425   Net::stringToAddress(addrText, &addr);
 426   pushPingBroadcast(&addr);
 427
 428   Con::executef("onServerQueryStatus", "start", "Querying LAN servers", "0");
 429   processPingsAndQueries( gPingSession );
 430}
 431
 432//-----------------------------------------------------------------------------
 433
 434DefineConsoleFunction( queryAllServers
 435                     , void, ( U32 lanPort
 436                             , U32 flags
 437                             , const char * gameType
 438                             , const char * missionType
 439                             , U32 minPlayers
 440                             , U32 maxPlayers
 441                             , U32 maxBots
 442                             , U32 regionMask
 443                             , U32 maxPing
 444                             , U32 minCPU
 445                             , U32 filterFlags )
 446                     , , "queryAllServers(...);" )
 447{
 448   U32 buddyList = 0;
 449
 450   clearServerList();
 451
 452   queryMasterServer(flags,gameType,missionType,minPlayers,maxPlayers,
 453      maxBots,regionMask,maxPing,minCPU,filterFlags,0,&buddyList);
 454
 455   queryLanServers(lanPort, flags, gameType, missionType, minPlayers, maxPlayers, maxBots,
 456      regionMask, maxPing, minCPU, filterFlags);
 457
 458}
 459
 460DefineConsoleFunction( queryLanServers
 461                     , void, ( U32 lanPort
 462                             , U32 flags
 463                             , const char * gameType
 464                             , const char * missionType
 465                             , U32 minPlayers
 466                             , U32 maxPlayers
 467                             , U32 maxBots
 468                             , U32 regionMask
 469                             , U32 maxPing
 470                             , U32 minCPU
 471                             , U32 filterFlags )
 472                     , , "queryLanServers(...);" )
 473
 474{
 475
 476   clearServerList();
 477   queryLanServers(lanPort, flags, gameType, missionType, minPlayers, maxPlayers, maxBots,
 478      regionMask, maxPing, minCPU, filterFlags);
 479
 480}
 481
 482//-----------------------------------------------------------------------------
 483
 484void queryMasterGameTypes()
 485{
 486   Vector<MasterInfo> *masterList = getMasterServerList();
 487   if ( masterList->size() != 0 )
 488   {
 489      U32 master = Sim::getCurrentTime() % masterList->size();
 490      // Send a request to the master server for the game types:
 491      Con::printf( "Requesting game types from the master server..." );
 492      sendPacket( NetInterface::MasterServerGameTypesRequest, &(*masterList)[master].address, gKey, gPingSession, 0 );
 493   }
 494}
 495
 496//-----------------------------------------------------------------------------
 497
 498void queryMasterServer(U8 flags, const char* gameType, const char* missionType,
 499      U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing,
 500      U16 minCPU, U8 filterFlags, U8 buddyCount, U32* buddyList )
 501{
 502   // Reset the list packet flag:
 503   gGotFirstListPacket = false;
 504   sgServerQueryActive = true;
 505
 506   Con::executef( "onServerQueryStatus", "start", "Querying master server", "0");
 507
 508   if ( buddyCount == 0 )
 509   {
 510      sActiveFilter.type = ServerFilter::Normal;
 511
 512      // Update the active filter:
 513      if ( !sActiveFilter.gameType || dStrcmp( sActiveFilter.gameType, gameType ) != 0 )
 514      {
 515         sActiveFilter.gameType = (char*) dRealloc( sActiveFilter.gameType, dStrlen( gameType ) + 1 );
 516         dStrcpy( sActiveFilter.gameType, gameType );
 517      }
 518
 519      if ( !sActiveFilter.missionType || dStrcmp( sActiveFilter.missionType, missionType ) != 0 )
 520      {
 521         sActiveFilter.missionType = (char*) dRealloc( sActiveFilter.missionType, dStrlen( missionType ) + 1 );
 522         dStrcpy( sActiveFilter.missionType, missionType );
 523      }
 524
 525      sActiveFilter.queryFlags   = flags | ServerFilter::NewStyleResponse;
 526      sActiveFilter.minPlayers   = minPlayers;
 527      sActiveFilter.maxPlayers   = maxPlayers;
 528      sActiveFilter.maxBots      = maxBots;
 529      sActiveFilter.regionMask   = regionMask;
 530      sActiveFilter.maxPing      = maxPing;
 531      sActiveFilter.minCPU       = minCPU;
 532      sActiveFilter.filterFlags  = filterFlags;
 533      sActiveFilter.buddyCount   = buddyCount;
 534      dFree( sActiveFilter.buddyList );
 535      sActiveFilter.buddyList = NULL;
 536   }
 537   else
 538   {
 539      sActiveFilter.type = ServerFilter::Buddy;
 540      sActiveFilter.buddyCount = buddyCount;
 541      sActiveFilter.buddyList = (U32*) dRealloc( sActiveFilter.buddyList, buddyCount * 4 );
 542      sActiveFilter.queryFlags = ServerFilter::NewStyleResponse;
 543      dMemcpy( sActiveFilter.buddyList, buddyList, buddyCount * 4 );
 544      clearServerList();
 545   }
 546
 547   // Pick a random master server from the list:
 548   gMasterServerList.clear();
 549   Vector<MasterInfo> *masterList = getMasterServerList();
 550   for ( U32 i = 0; i < masterList->size(); i++ )
 551      gMasterServerList.push_back( (*masterList)[i] );
 552
 553   // Clear the master server ping:
 554   gMasterServerPing.time = 0;
 555   gMasterServerPing.tryCount = gMasterServerRetryCount;
 556
 557   if ( !pickMasterServer() )
 558      Con::errorf( "No master servers found!" );
 559   else
 560      processMasterServerQuery( gPingSession );
 561}
 562
 563DefineConsoleFunction( queryMasterServer
 564                     , void, (  U32 flags
 565                             , const char * gameType
 566                             , const char * missionType
 567                             , U32 minPlayers
 568                             , U32 maxPlayers
 569                             , U32 maxBots
 570                             , U32 regionMask
 571                             , U32 maxPing
 572                             , U32 minCPU
 573                             , U32 filterFlags )
 574                     , , "queryMasterServer(...);" )
 575{
 576   U32 buddyList = 0;
 577
 578   clearServerList();
 579   queryMasterServer(flags,gameType,missionType,minPlayers,maxPlayers,
 580      maxBots,regionMask,maxPing,minCPU,filterFlags,0,&buddyList);
 581}
 582
 583//-----------------------------------------------------------------------------
 584
 585DefineConsoleFunction( querySingleServer
 586                     , void, ( const char* addrText, U8 flags )
 587                     , (0), "querySingleServer(address, flags);" )
 588{
 589   NetAddress addr;
 590   Net::stringToAddress( addrText, &addr );
 591   querySingleServer(&addr,flags);
 592}
 593
 594//-----------------------------------------------------------------------------
 595
 596void queryFavoriteServers( U8 /*flags*/ )
 597{
 598   sgServerQueryActive = true;
 599   clearServerList();
 600   sActiveFilter.type = ServerFilter::Favorites;
 601   pushServerFavorites();
 602
 603   Con::executef( "onServerQueryStatus", "start", "Query favorites...", "0" );
 604   processPingsAndQueries( gPingSession );
 605}
 606
 607//-----------------------------------------------------------------------------
 608
 609void querySingleServer( const NetAddress* addr, U8 /*flags*/ )
 610{
 611   sgServerQueryActive = true;
 612   ServerInfo* si = findServerInfo( addr );
 613   if ( si )
 614      si->status = ServerInfo::Status_New | ServerInfo::Status_Updating;
 615
 616   // Remove the server from the finished list (if it's there):
 617   for ( U32 i = 0; i < gFinishedList.size(); i++ )
 618   {
 619      if ( Net::compareAddresses( addr, &gFinishedList[i] ) )
 620      {
 621         gFinishedList.erase( i );
 622         break;
 623      }
 624   }
 625
 626   Con::executef( "onServerQueryStatus", "start", "Refreshing server...", "0" );
 627   gServerPingCount = gServerQueryCount = 0;
 628   pushPingRequest( addr );
 629   processPingsAndQueries( gPingSession );
 630}
 631
 632//-----------------------------------------------------------------------------
 633
 634void cancelServerQuery()
 635{
 636   // Cancel the current query, if there is anything left
 637   // on the ping list, it's dropped.
 638   if ( sgServerQueryActive )
 639   {
 640      Con::printf( "Server query canceled." );
 641      ServerInfo* si;
 642
 643      // Clear the master server packet list:
 644      gPacketStatusList.clear();
 645
 646      // Clear the ping list:
 647      while ( gPingList.size() )
 648      {
 649         si = findServerInfo( &gPingList[0].address );
 650         if ( si && !si->status.test( ServerInfo::Status_Responded ) )
 651            si->status = ServerInfo::Status_TimedOut;
 652
 653         gPingList.erase( U32( 0 ) );
 654      }
 655
 656      // Clear the query list:
 657      while ( gQueryList.size() )
 658      {
 659         si = findServerInfo( &gQueryList[0].address );
 660         if ( si && !si->status.test( ServerInfo::Status_Responded ) )
 661            si->status = ServerInfo::Status_TimedOut;
 662
 663         gQueryList.erase( U32( 0 ) );
 664      }
 665
 666      sgServerQueryActive = false;
 667      gServerBrowserDirty = true;
 668   }
 669}
 670
 671DefineConsoleFunction( cancelServerQuery, void, (), , "cancelServerQuery();" )
 672{
 673   cancelServerQuery();
 674}
 675
 676//-----------------------------------------------------------------------------
 677
 678void stopServerQuery()
 679{
 680   // Cancel the current query, anything left on the ping
 681   // list is moved to the finished list as "done".
 682   if ( sgServerQueryActive )
 683   {
 684      gPacketStatusList.clear();
 685
 686      if ( gPingList.size() )
 687      {
 688         while ( gPingList.size() )
 689         {
 690            gFinishedList.push_back( gPingList[0].address );
 691            gPingList.erase( U32( 0 ) );
 692         }
 693      }
 694      else
 695         cancelServerQuery();
 696   }
 697}
 698
 699DefineConsoleFunction( stopServerQuery, void, (), , "stopServerQuery();" )
 700{
 701   stopServerQuery();
 702}
 703
 704//-----------------------------------------------------------------------------
 705
 706DefineConsoleFunction( startHeartbeat, void, (), , "startHeartbeat();" )
 707{
 708   if (validateAuthenticatedServer()) {
 709      gHeartbeatSeq++;
 710      processHeartbeat(gHeartbeatSeq);  // thump-thump...
 711   }
 712}
 713
 714DefineConsoleFunction( stopHeartbeat, void, (), , "stopHeartbeat();" )
 715{
 716   gHeartbeatSeq++;
 717}
 718
 719//-----------------------------------------------------------------------------
 720
 721DefineConsoleFunction( getServerCount, int, (), , "getServerCount();" )
 722{
 723   return gServerList.size();
 724}
 725
 726DefineConsoleFunction( setServerInfo, bool, (U32 index), , "setServerInfo(index);" )
 727{
 728   if (index < gServerList.size()) {
 729      ServerInfo& info = gServerList[index];
 730
 731      char addrString[256];
 732      Net::addressToString( &info.address, addrString );
 733
 734      Con::setIntVariable("ServerInfo::Status",info.status);
 735      Con::setVariable("ServerInfo::Address",addrString);
 736      Con::setVariable("ServerInfo::Name",info.name);
 737      Con::setVariable("ServerInfo::GameType",info.gameType);
 738      Con::setVariable("ServerInfo::MissionName",info.missionName);
 739      Con::setVariable("ServerInfo::MissionType",info.missionType);
 740      Con::setVariable("ServerInfo::State",info.statusString);
 741      Con::setVariable("ServerInfo::Info",info.infoString);
 742      Con::setIntVariable("ServerInfo::PlayerCount",info.numPlayers);
 743      Con::setIntVariable("ServerInfo::MaxPlayers",info.maxPlayers);
 744      Con::setIntVariable("ServerInfo::BotCount",info.numBots);
 745      Con::setIntVariable("ServerInfo::Version",info.version);
 746      Con::setIntVariable("ServerInfo::Ping",info.ping);
 747      Con::setIntVariable("ServerInfo::CPUSpeed",info.cpuSpeed);
 748      Con::setBoolVariable("ServerInfo::Favorite",info.isFavorite);
 749      Con::setBoolVariable("ServerInfo::Dedicated",info.isDedicated());
 750      Con::setBoolVariable("ServerInfo::Password",info.isPassworded());
 751      return true;
 752   }
 753   return false;
 754}
 755
 756
 757//-----------------------------------------------------------------------------
 758// Internal
 759//-----------------------------------------------------------------------------
 760
 761//-----------------------------------------------------------------------------
 762
 763ServerInfo::~ServerInfo()
 764{
 765   if ( name )
 766      dFree( name );
 767   if ( gameType )
 768      dFree( gameType );
 769   if ( missionType )
 770      dFree( missionType );
 771   if( missionName )
 772      dFree( missionName );
 773   if ( statusString )
 774      dFree( statusString );
 775   if ( infoString )
 776      dFree( infoString );
 777}
 778
 779//-----------------------------------------------------------------------------
 780
 781Vector<MasterInfo>* getMasterServerList()
 782{
 783   // This code used to get the master server list from the
 784   // WON.net directory.
 785   static Vector<MasterInfo> masterList;
 786   masterList.clear();
 787
 788   for (U32 i = 0; i < 10; i++) {
 789      char buffer[50];
 790      dSprintf(buffer,sizeof(buffer),"pref::Master%d",i);
 791      const char* master = Con::getVariable(buffer);
 792      if (master && *master) {
 793         NetAddress address;
 794         // Format for master server variable:
 795         //    regionMask:netAddress
 796         U32 region = 1; // needs to default to something > 0
 797         dSscanf(master,"%d:",&region);
 798         const char* madd = dStrchr(master,':') + 1;
 799         if (region && Net::stringToAddress(madd,&address) == Net::NoError) {
 800            masterList.increment();
 801            MasterInfo& info = masterList.last();
 802            info.address = address;
 803            info.region = region;
 804         }
 805         else
 806            Con::errorf("Bad master server address: %s",master);
 807      }
 808   }
 809
 810   if (!masterList.size())
 811      Con::errorf("No master servers found");
 812
 813   return &masterList;
 814}
 815
 816
 817//-----------------------------------------------------------------------------
 818
 819bool pickMasterServer()
 820{
 821   // Reset the master server ping:
 822   gMasterServerPing.time = 0;
 823   gMasterServerPing.key = 0;
 824   gMasterServerPing.tryCount = gMasterServerRetryCount;
 825   gMasterServerPing.session = gPingSession;
 826
 827   char addrString[256];
 828   U32 serverCount = gMasterServerList.size();
 829   if ( !serverCount )
 830   {
 831      // There are no more servers left to try...:(
 832      return( false );
 833   }
 834
 835   U32 region = Con::getIntVariable( "$pref::Net::RegionMask" );
 836   U32 index = Sim::getCurrentTime() % serverCount;
 837
 838   // First try to find a master server in the same region:
 839   for ( U32 i = 0; i < serverCount; i++ )
 840   {
 841      if ( gMasterServerList[index].region == region )
 842      {
 843         Net::addressToString( &gMasterServerList[index].address, addrString );
 844         Con::printf( "Found master server %s in same region.", addrString );
 845         gMasterServerPing.address = gMasterServerList[index].address;
 846         return( true );
 847      }
 848
 849      index = index < serverCount - 1 ? index + 1 : 0;
 850   }
 851
 852   // Settle for the one we first picked:
 853   Net::addressToString( &gMasterServerList[index].address, addrString );
 854   Con::printf( "No master servers found in this region, trying %s.", addrString );
 855   gMasterServerPing.address = gMasterServerList[index].address;
 856
 857   return( true );
 858}
 859
 860//-----------------------------------------------------------------------------
 861
 862void clearServerList()
 863{
 864   gPacketStatusList.clear();
 865   gServerList.clear();
 866   gFinishedList.clear();
 867   gPingList.clear();
 868   gQueryList.clear();
 869   gServerPingCount = gServerQueryCount = 0;
 870
 871   gPingSession++;
 872}
 873
 874//-----------------------------------------------------------------------------
 875
 876void sendHeartbeat( U8 flags )
 877{
 878   // send heartbeats to all of the master servers:
 879   Vector<MasterInfo> *masterList = getMasterServerList();
 880   for(U32 i = 0; i < masterList->size(); i++)
 881   {
 882      char buffer[256];
 883      Net::addressToString(&(*masterList)[i].address, buffer);
 884      // Send a request to the master server for the game types:
 885      Con::printf( "Sending heartbeat to master server [%s]", buffer );
 886      sendPacket( NetInterface::GameHeartbeat, &(*masterList)[i].address, 0, gPingSession, flags );
 887   }
 888}
 889
 890//-----------------------------------------------------------------------------
 891
 892static void pushPingRequest( const NetAddress* addr )
 893{
 894   if( addressFinished( addr ) )
 895      return;
 896
 897   Ping p;
 898   p.address = *addr;
 899   p.session = gPingSession;
 900   p.key = 0;
 901   p.time = 0;
 902   p.tryCount = gPingRetryCount;
 903   p.broadcast = false;
 904   gPingList.push_back( p );
 905   gServerPingCount++;
 906}
 907
 908//-----------------------------------------------------------------------------
 909
 910static void pushPingBroadcast( const NetAddress* addr )
 911{
 912   if( addressFinished( addr ) )
 913      return;
 914
 915   Ping p;
 916   p.address = *addr;
 917   p.session = gPingSession;
 918   p.key = 0;
 919   p.time = 0;
 920   p.tryCount = 1; // only try this once
 921   p.broadcast = true;
 922   gPingList.push_back( p );
 923   // Don't increment gServerPingCount, broadcasts are not
 924   // counted as requests.
 925}
 926
 927//-----------------------------------------------------------------------------
 928
 929static S32 countPingRequests()
 930{
 931   // Need a function here because the ping list also includes
 932   // broadcast pings we don't want counted.
 933   U32 pSize = gPingList.size(), count = pSize;
 934   for (U32 i = 0; i < pSize; i++)
 935      if (gPingList[i].broadcast)
 936         count--;
 937   return count;
 938}
 939
 940
 941//-----------------------------------------------------------------------------
 942
 943static void pushServerFavorites()
 944{
 945   S32 count = Con::getIntVariable( "$pref::Client::ServerFavoriteCount" );
 946   if ( count < 0 )
 947   {
 948      Con::setIntVariable( "$pref::Client::ServerFavoriteCount", 0 );
 949      return;
 950   }
 951
 952   NetAddress addr;
 953   const char* server = NULL;
 954   char buf[256], serverName[25], addrString[256];
 955   U32 sz, len;
 956   for ( S32 i = 0; i < count; i++ )
 957   {
 958      dSprintf( buf, sizeof( buf ), "pref::Client::ServerFavorite%d", i );
 959      server = Con::getVariable( buf );
 960      if ( server )
 961      {
 962         sz = dStrcspn( server, "\t" );
 963         if ( sz > 0 )
 964         {
 965            len = sz > 24 ? 24 : sz;
 966            dStrncpy( serverName, server, len );
 967            serverName[len] = 0;
 968            dStrncpy( addrString, server + ( sz + 1 ), 255 );
 969
 970            //Con::errorf( "Pushing server favorite \"%s\" - %s...", serverName, addrString );
 971            Net::stringToAddress( addrString, &addr );
 972            ServerInfo* si = findOrCreateServerInfo( &addr );
 973            AssertFatal(si, "pushServerFavorites - failed to create Server Info!" );
 974            si->name = (char*) dRealloc( (void*) si->name, dStrlen( serverName ) + 1 );
 975            dStrcpy( si->name, serverName );
 976            si->isFavorite = true;
 977            pushPingRequest( &addr );
 978         }
 979      }
 980   }
 981}
 982
 983//-----------------------------------------------------------------------------
 984
 985static S32 findPingEntry( Vector<Ping> &v, const NetAddress* addr )
 986{
 987   for ( U32 i = 0; i < v.size(); i++ )
 988      if ( Net::compareAddresses( addr, &v[i].address ) )
 989         return S32( i );
 990   return -1;
 991}
 992
 993//-----------------------------------------------------------------------------
 994
 995static bool addressFinished( const NetAddress* addr )
 996{
 997   for ( U32 i = 0; i < gFinishedList.size(); i++ )
 998      if ( Net::compareAddresses( addr, &gFinishedList[i] ) )
 999         return true;
1000   return false;
1001}
1002
1003//-----------------------------------------------------------------------------
1004
1005static ServerInfo* findServerInfo( const NetAddress* addr )
1006{
1007   for ( U32 i = 0; i < gServerList.size(); i++ )
1008      if ( Net::compareAddresses( addr, &gServerList[i].address ) )
1009         return &gServerList[i];
1010   return NULL;
1011}
1012
1013//-----------------------------------------------------------------------------
1014
1015static ServerInfo* findOrCreateServerInfo( const NetAddress* addr )
1016{
1017   ServerInfo* ret = findServerInfo( addr );
1018   if ( ret )
1019      return ret;
1020
1021   ServerInfo si;
1022   si.address = *addr;
1023   gServerList.push_back( si );
1024
1025   return &gServerList.last();
1026}
1027
1028//-----------------------------------------------------------------------------
1029
1030static void removeServerInfo( const NetAddress* addr )
1031{
1032   for ( U32 i = 0; i < gServerList.size(); i++ )
1033   {
1034      if ( Net::compareAddresses( addr, &gServerList[i].address ) )
1035      {
1036         gServerList.erase( i );
1037         gServerBrowserDirty = true;
1038      }
1039   }
1040}
1041
1042//-----------------------------------------------------------------------------
1043
1044#if defined(TORQUE_DEBUG)
1045// This function is solely for testing the functionality of the server browser
1046// with more servers in the list.
1047void addFakeServers( S32 howMany )
1048{
1049   static S32 sNumFakeServers = 1;
1050   ServerInfo newServer;
1051
1052   for ( S32 i = 0; i < howMany; i++ )
1053   {
1054      newServer.numPlayers = (U8)(Platform::getRandom() * 64.0f);
1055      newServer.maxPlayers = 64;
1056      char buf[256];
1057      dSprintf( buf, 255, "Fake server #%d", sNumFakeServers );
1058      newServer.name = (char*) dMalloc( dStrlen( buf ) + 1 );
1059      dStrcpy( newServer.name, buf );
1060      newServer.gameType = (char*) dMalloc( 5 );
1061      dStrcpy( newServer.gameType, "Fake" );
1062      newServer.missionType = (char*) dMalloc( 4 );
1063      dStrcpy( newServer.missionType, "FakeMissionType" );
1064      newServer.missionName = (char*) dMalloc( 14 );
1065      dStrcpy( newServer.missionName, "FakeMapName" );
1066      Net::stringToAddress( "IP:198.74.33.35:28000", &newServer.address );
1067      newServer.ping = (U32)( Platform::getRandom() * 200.0f );
1068      newServer.cpuSpeed = 470;
1069      newServer.status = ServerInfo::Status_Responded;
1070
1071      gServerList.push_back( newServer );
1072      sNumFakeServers++;
1073   }
1074
1075   gServerBrowserDirty = true;
1076}
1077#endif // DEBUG
1078
1079//-----------------------------------------------------------------------------
1080
1081static void sendPacket( U8 pType, const NetAddress* addr, U32 key, U32 session, U8 flags )
1082{
1083   BitStream *out = BitStream::getPacketStream();
1084   out->clearStringBuffer();
1085   out->write( pType );
1086   out->write( flags );
1087   out->write( U32( ( session << 16 ) | ( key & 0xFFFF ) ) );
1088
1089   BitStream::sendPacketStream(addr);
1090}
1091
1092//-----------------------------------------------------------------------------
1093
1094static void writeCString( BitStream* stream, const char* string )
1095{
1096   U8 strLen = ( string != NULL ) ? dStrlen( string ) : 0;
1097   stream->write( strLen );
1098   for ( U32 i = 0; i < strLen; i++ )
1099      stream->write( U8( string[i] ) );
1100}
1101
1102//-----------------------------------------------------------------------------
1103
1104static void readCString( BitStream* stream, char* buffer )
1105{
1106   U32 i;
1107   U8 strLen;
1108   stream->read( &strLen );
1109   for ( i = 0; i < strLen; i++ )
1110   {
1111      U8* ptr = (U8*) buffer;
1112      stream->read( &ptr[i] );
1113   }
1114   buffer[i] = 0;
1115}
1116
1117//-----------------------------------------------------------------------------
1118
1119static void writeLongCString( BitStream* stream, const char* string )
1120{
1121   U16 strLen = ( string != NULL ) ? dStrlen( string ) : 0;
1122   stream->write( strLen );
1123   for ( U32 i = 0; i < strLen; i++ )
1124      stream->write( U8( string[i] ) );
1125}
1126
1127//-----------------------------------------------------------------------------
1128
1129static void readLongCString( BitStream* stream, char* buffer )
1130{
1131   U32 i;
1132   U16 strLen;
1133   stream->read( &strLen );
1134   for ( i = 0; i < strLen; i++ )
1135   {
1136      U8* ptr = (U8*) buffer;
1137      stream->read( &ptr[i] );
1138   }
1139   buffer[i] = 0;
1140}
1141
1142//-----------------------------------------------------------------------------
1143// Event processing
1144//-----------------------------------------------------------------------------
1145
1146//-----------------------------------------------------------------------------
1147
1148static void processMasterServerQuery( U32 session )
1149{
1150   if ( session != gPingSession || !sgServerQueryActive )
1151      return;
1152
1153   if ( !gGotFirstListPacket )
1154   {
1155      bool keepGoing = true;
1156      U32 time = Platform::getVirtualMilliseconds();
1157      char addressString[256];
1158
1159      if ( gMasterServerPing.time + gMasterServerTimeout < time )
1160      {
1161         Net::addressToString( &gMasterServerPing.address, addressString );
1162         if ( !gMasterServerPing.tryCount )
1163         {
1164            // The query timed out.
1165            Con::printf( "Server list request to %s timed out.", addressString );
1166
1167            // Remove this server from the list:
1168            for ( U32 i = 0; i < gMasterServerList.size(); i++ )
1169            {
1170               if ( Net::compareAddresses( &gMasterServerList[i].address, &gMasterServerPing.address ) )
1171               {
1172                  gMasterServerList.erase( i );
1173                  break;
1174               }
1175            }
1176
1177            // Pick a new master server to try:
1178            keepGoing = pickMasterServer();
1179            if ( keepGoing )
1180            {
1181               Con::executef( "onServerQueryStatus", "update", "Switching master servers...", "0" );
1182               Net::addressToString( &gMasterServerPing.address, addressString );
1183            }
1184         }
1185
1186         if ( keepGoing )
1187         {
1188            gMasterServerPing.tryCount--;
1189            gMasterServerPing.time = time;
1190            gMasterServerPing.key = gKey++;
1191
1192            // Send a request to the master server for the server list:
1193            BitStream *out = BitStream::getPacketStream();
1194            out->clearStringBuffer();
1195
1196            out->write( U8( NetInterface::MasterServerListRequest ) );
1197            
1198            out->write( U8( sActiveFilter.queryFlags) );
1199            out->write( ( gMasterServerPing.session << 16 ) | ( gMasterServerPing.key & 0xFFFF ) );
1200            out->write( U8( 255 ) );
1201
1202            writeCString( out, sActiveFilter.gameType );
1203            writeCString( out, sActiveFilter.missionType );
1204            out->write( sActiveFilter.minPlayers );
1205            out->write( sActiveFilter.maxPlayers );
1206            out->write( sActiveFilter.regionMask );
1207            U32 version = ( sActiveFilter.filterFlags & ServerFilter::CurrentVersion ) ? getVersionNumber() : 0;
1208            out->write( version );
1209            out->write( sActiveFilter.filterFlags );
1210            out->write( sActiveFilter.maxBots );
1211            out->write( sActiveFilter.minCPU );
1212            out->write( sActiveFilter.buddyCount );
1213            for ( U32 i = 0; i < sActiveFilter.buddyCount; i++ )
1214               out->write( sActiveFilter.buddyList[i] );
1215
1216            BitStream::sendPacketStream( &gMasterServerPing.address );
1217
1218            Con::printf( "Requesting the server list from master server %s (%d tries left)...", addressString, gMasterServerPing.tryCount );
1219            if ( gMasterServerPing.tryCount < gMasterServerRetryCount - 1 )
1220               Con::executef( "onServerQueryStatus", "update", "Retrying the master server...", "0" );
1221         }
1222      }
1223
1224      if ( keepGoing )
1225      {
1226         // schedule another check:
1227         Sim::postEvent( Sim::getRootGroup(), new ProcessMasterQueryEvent( session ), Sim::getTargetTime() + 1 );
1228      }
1229      else
1230      {
1231         Con::errorf( "There are no more master servers to try!" );
1232         Con::executef( "onServerQueryStatus", "done", "No master servers found.", "0" );
1233      }
1234   }
1235}
1236
1237//-----------------------------------------------------------------------------
1238
1239static void processPingsAndQueries( U32 session, bool schedule )
1240{
1241   if( session != gPingSession )
1242      return;
1243
1244   U32 i = 0;
1245   U32 time = Platform::getVirtualMilliseconds();
1246   char addressString[256];
1247   U8 flags = ServerFilter::OnlineQuery;
1248   bool waitingForMaster = ( sActiveFilter.type == ServerFilter::Normal ) && !gGotFirstListPacket && sgServerQueryActive;
1249
1250   for ( i = 0; i < gPingList.size() && i < gMaxConcurrentPings; )
1251   {
1252      Ping &p = gPingList[i];
1253
1254      if ( p.time + gPingTimeout < time )
1255      {
1256         if ( !p.tryCount )
1257         {
1258            // it's timed out.
1259            if (!p.broadcast)
1260            {
1261               Net::addressToString( &p.address, addressString );
1262               Con::printf( "Ping to server %s timed out.", addressString );
1263            }
1264
1265            // If server info is in list (favorite), set its status:
1266            ServerInfo* si = findServerInfo( &p.address );
1267            if ( si )
1268            {
1269               si->status = ServerInfo::Status_TimedOut;
1270               gServerBrowserDirty = true;
1271            }
1272
1273            gFinishedList.push_back( p.address );
1274            gPingList.erase( i );
1275
1276            if ( !waitingForMaster )
1277               updatePingProgress();
1278         }
1279         else
1280         {
1281            p.tryCount--;
1282            p.time = time;
1283            p.key = gKey++;
1284
1285            Net::addressToString( &p.address, addressString );
1286
1287            if (p.broadcast)
1288               Con::printf( "LAN server ping: %s...", addressString );
1289            else
1290               Con::printf( "Pinging Server %s (%d)...", addressString, p.tryCount );
1291            sendPacket( NetInterface::GamePingRequest, &p.address, p.key, p.session, flags );
1292            i++;
1293         }
1294      }
1295      else
1296         i++;
1297   }
1298
1299   if ( !gPingList.size() && !waitingForMaster )
1300   {
1301      // Start the query phase:
1302      for ( U32 i = 0; i < gQueryList.size() && i < gMaxConcurrentQueries; )
1303      {
1304         Ping &p = gQueryList[i];
1305         if ( p.time + gPingTimeout < time )
1306         {
1307            ServerInfo* si = findServerInfo( &p.address );
1308            if ( !si )
1309            {
1310               // Server info not found, so remove the query:
1311               gQueryList.erase( i );
1312               gServerBrowserDirty = true;
1313               continue;
1314            }
1315
1316            Net::addressToString( &p.address, addressString );
1317            if ( !p.tryCount )
1318            {
1319               Con::printf( "Query to server %s timed out.", addressString );
1320               si->status = ServerInfo::Status_TimedOut;
1321               gQueryList.erase( i );
1322               gServerBrowserDirty = true;
1323            }
1324            else
1325            {
1326               p.tryCount--;
1327               p.time = time;
1328               p.key = gKey++;
1329
1330               Con::printf( "Querying Server %s (%d)...", addressString, p.tryCount );
1331               sendPacket( NetInterface::GameInfoRequest, &p.address, p.key, p.session, flags );
1332               if ( !si->isQuerying() )
1333               {
1334                  si->status |= ServerInfo::Status_Querying;
1335                  gServerBrowserDirty = true;
1336               }
1337               i++;
1338            }
1339         }
1340         else
1341            i++;
1342      }
1343   }
1344
1345   if ( gPingList.size() || gQueryList.size() || waitingForMaster )
1346   {
1347      // The LAN query function doesn't always want to schedule
1348      // the next ping.
1349      if (schedule)
1350         Sim::postEvent( Sim::getRootGroup(), new ProcessPingEvent( session ), Sim::getTargetTime() + 1 );
1351   }
1352   else
1353   {
1354      // All done!
1355      char msg[64];
1356      U32 foundCount = gServerList.size();
1357      if ( foundCount == 0 )
1358         dStrcpy( msg, "No servers found." );
1359      else if ( foundCount == 1 )
1360         dStrcpy( msg, "One server found." );
1361      else
1362         dSprintf( msg, sizeof( msg ), "%d servers found.", foundCount );
1363
1364      Con::executef( "onServerQueryStatus", "done", (const char*)msg, "1");
1365   }
1366}
1367
1368//-----------------------------------------------------------------------------
1369
1370static void processServerListPackets( U32 session )
1371{
1372   if ( session != gPingSession || !sgServerQueryActive )
1373      return;
1374
1375   U32 currentTime = Platform::getVirtualMilliseconds();
1376
1377   // Loop through the packet status list and resend packet requests where necessary:
1378   for ( U32 i = 0; i < gPacketStatusList.size(); i++ )
1379   {
1380      PacketStatus &p = gPacketStatusList[i];
1381      if ( p.time + gPacketTimeout < currentTime )
1382      {
1383         if ( !p.tryCount )
1384         {
1385            // Packet timed out :(
1386            Con::printf( "Server list packet #%d timed out.", p.getIndex() + 1 );
1387            gPacketStatusList.erase( i );
1388         }
1389         else
1390         {
1391            // Try again...
1392            Con::printf( "Rerequesting server list packet #%d...", p.getIndex() + 1 );
1393            p.tryCount--;
1394            p.time = currentTime;
1395            p.key = gKey++;
1396
1397            BitStream *out = BitStream::getPacketStream();
1398            bool extendedPacket = (sActiveFilter.queryFlags & ServerFilter::NewStyleResponse) != 0;
1399
1400            out->clearStringBuffer();
1401
1402            if ( extendedPacket )
1403               out->write( U8( NetInterface::MasterServerExtendedListRequest ) );
1404            else
1405               out->write( U8( NetInterface::MasterServerListRequest ) );
1406
1407            out->write( U8( sActiveFilter.queryFlags ) );   // flags
1408            out->write( ( session << 16) | ( p.key & 0xFFFF ) );
1409            
1410            if ( extendedPacket )
1411               out->write( p.getOldIndex() );  // packet index
1412            else
1413               out->write( p.getIndex() );  // packet index
1414
1415            out->write( U8( 0 ) );  // game type
1416            out->write( U8( 0 ) );  // mission type
1417            out->write( U8( 0 ) );  // minPlayers
1418            out->write( U8( 0 ) );  // maxPlayers
1419            out->write( U32( 0 ) ); // region mask
1420            out->write( U32( 0 ) ); // version
1421            out->write( U8( 0 ) );  // filter flags
1422            out->write( U8( 0 ) );  // max bots
1423            out->write( U16( 0 ) ); // min CPU
1424            out->write( U8( 0 ) );  // buddy count
1425
1426            BitStream::sendPacketStream(&gMasterServerQueryAddress);
1427         }
1428      }
1429   }
1430
1431   if ( gPacketStatusList.size() )
1432      Sim::postEvent( Sim::getRootGroup(), new ProcessPacketEvent( session ), Sim::getCurrentTime() + 30 );
1433   else
1434      processPingsAndQueries( gPingSession );
1435}
1436
1437//-----------------------------------------------------------------------------
1438
1439static void processHeartbeat(U32 seq)
1440{
1441   if(seq != gHeartbeatSeq)
1442      return;
1443   sendHeartbeat( 0 );
1444   Sim::postEvent( Sim::getRootGroup(), new HeartbeatEvent(seq), Sim::getCurrentTime() + gHeartbeatInterval );
1445}
1446
1447//-----------------------------------------------------------------------------
1448
1449static void updatePingProgress()
1450{
1451   if ( !gPingList.size() )
1452   {
1453      updateQueryProgress();
1454      return;
1455   }
1456
1457   char msg[64];
1458   U32 pingsLeft = countPingRequests();
1459   dSprintf( msg, sizeof(msg),
1460      (!pingsLeft && gPingList.size())?
1461         "Waiting for lan servers...":
1462         "Pinging servers: %d left...",
1463      pingsLeft );
1464
1465   // Ping progress is 0 -> 0.5
1466   F32 progress = 0.0f;
1467   if ( gServerPingCount )
1468      progress = F32( gServerPingCount - pingsLeft ) / F32( gServerPingCount * 2 );
1469
1470   //Con::errorf( ConsoleLogEntry::General, "Ping progress - %d of %d left - progress = %.2f", pingsLeft, gServerPingCount, progress );
1471   Con::executef( "onServerQueryStatus", "ping", msg, Con::getFloatArg( progress ) );
1472}
1473
1474//-----------------------------------------------------------------------------
1475
1476static void updateQueryProgress()
1477{
1478   if ( gPingList.size() )
1479      return;
1480
1481   char msg[64];
1482   U32 queriesLeft = gQueryList.size();
1483   dSprintf( msg, sizeof( msg ), "Querying servers: %d left...", queriesLeft );
1484
1485   // Query progress is 0.5 -> 1
1486   F32 progress = 0.5f;
1487   if ( gServerQueryCount )
1488      progress += ( F32( gServerQueryCount - queriesLeft ) / F32( gServerQueryCount * 2 ) );
1489
1490   //Con::errorf( ConsoleLogEntry::General, "Query progress - %d of %d left - progress = %.2f", queriesLeft, gServerQueryCount, progress );
1491   Con::executef( "onServerQueryStatus", "query", msg, Con::getFloatArg( progress ) );
1492}
1493
1494
1495//-----------------------------------------------------------------------------
1496// Server packet handlers:
1497//-----------------------------------------------------------------------------
1498
1499//-----------------------------------------------------------------------------
1500
1501static void handleMasterServerGameTypesResponse( BitStream* stream, U32 /*key*/, U8 /*flags*/ )
1502{
1503   Con::printf( "Received game type list from the master server." );
1504
1505   U32 i;
1506   U8 temp;
1507   char stringBuf[256];
1508   stream->read( &temp );
1509   Con::executef("onClearGameTypes");
1510   for ( i = 0; i < U32( temp ); i++ )
1511   {
1512      readCString( stream, stringBuf );
1513      Con::executef("onAddGameType", stringBuf);
1514   }
1515
1516   stream->read( &temp );
1517   Con::executef("onClearMissionTypes");
1518   for ( i = 0; i < U32( temp ); i++ )
1519   {
1520      readCString( stream, stringBuf );
1521      Con::executef("onAddMissionType", stringBuf);
1522   }
1523}
1524
1525//-----------------------------------------------------------------------------
1526
1527static void handleMasterServerListResponse( BitStream* stream, U32 key, U8 /*flags*/ )
1528{
1529   U8 packetIndex, packetTotal;
1530   U32 i;
1531   U16 serverCount, port;
1532   U8 netNum[4];
1533   char addressBuffer[256];
1534   NetAddress addr;
1535
1536   stream->read( &packetIndex );
1537   // Validate the packet key:
1538   U32 packetKey = gMasterServerPing.key;
1539   if ( gGotFirstListPacket )
1540   {
1541      for ( i = 0; i < gPacketStatusList.size(); i++ )
1542      {
1543         if ( gPacketStatusList[i].index == packetIndex )
1544         {
1545            packetKey = gPacketStatusList[i].key;
1546            break;
1547         }
1548      }
1549   }
1550
1551   U32 testKey = ( gPingSession << 16 ) | ( packetKey & 0xFFFF );
1552   if ( testKey != key )
1553      return;
1554
1555   stream->read( &packetTotal );
1556   stream->read( &serverCount );
1557
1558   Con::printf( "Received server list packet %d of %d from the master server (%d servers).", ( packetIndex + 1 ), packetTotal, serverCount );
1559
1560   // Enter all of the servers in this packet into the ping list:
1561   for ( i = 0; i < serverCount; i++ )
1562   {
1563      stream->read( &netNum[0] );
1564      stream->read( &netNum[1] );
1565      stream->read( &netNum[2] );
1566      stream->read( &netNum[3] );
1567      stream->read( &port );
1568
1569      dSprintf( addressBuffer, sizeof( addressBuffer ), "IP:%d.%d.%d.%d:%d", netNum[0], netNum[1], netNum[2], netNum[3], port );
1570      Net::stringToAddress( addressBuffer, &addr );
1571      pushPingRequest( &addr );
1572   }
1573
1574   // If this is the first list packet we have received, fill the packet status list
1575   // and start processing:
1576   if ( !gGotFirstListPacket )
1577   {
1578      gGotFirstListPacket = true;
1579      gMasterServerQueryAddress = gMasterServerPing.address;
1580      U32 currentTime = Platform::getVirtualMilliseconds();
1581      for ( i = 0; i < packetTotal; i++ )
1582      {
1583         if ( i != packetIndex )
1584         {
1585            PacketStatus* p = new PacketStatus( i, gMasterServerPing.key, currentTime );
1586            gPacketStatusList.push_back( *p );
1587         }
1588      }
1589
1590      processServerListPackets( gPingSession );
1591   }
1592   else
1593   {
1594      // Remove the packet we just received from the status list:
1595      for ( i = 0; i < gPacketStatusList.size(); i++ )
1596      {
1597         if ( gPacketStatusList[i].index == packetIndex )
1598         {
1599            gPacketStatusList.erase( i );
1600            break;
1601         }
1602      }
1603   }
1604}
1605
1606//-----------------------------------------------------------------------------
1607
1608static void handleExtendedMasterServerListResponse(BitStream* stream, U32 key, U8 /*flags*/)
1609{
1610   U16 packetIndex, packetTotal;
1611   U32 i;
1612   U16 serverCount, port;
1613   U8 netNum[16];
1614   char addressBuffer[256];
1615   NetAddress addr;
1616
1617   stream->read(&packetIndex);
1618   // Validate the packet key:
1619   U32 packetKey = gMasterServerPing.key;
1620   if (gGotFirstListPacket)
1621   {
1622      for (i = 0; i < gPacketStatusList.size(); i++)
1623      {
1624         if (gPacketStatusList[i].index == packetIndex)
1625         {
1626            packetKey = gPacketStatusList[i].key;
1627            break;
1628         }
1629      }
1630   }
1631
1632   U32 testKey = (gPingSession << 16) | (packetKey & 0xFFFF);
1633   if (testKey != key)
1634      return;
1635
1636   stream->read(&packetTotal);
1637   stream->read(&serverCount);
1638
1639   Con::printf("Received server list packet %d of %d from the master server (%d servers).", (packetIndex + 1), packetTotal, serverCount);
1640
1641   // Enter all of the servers in this packet into the ping list:
1642   for (i = 0; i < serverCount; i++)
1643   {
1644      U8 type;
1645      stream->read(&type);
1646      dMemset(&addr, '\0', sizeof(NetAddress));
1647
1648      if (type == 0)
1649      {
1650         // IPV4
1651         addr.type = NetAddress::IPAddress;
1652         stream->read(4, &addr.address.ipv4.netNum[0]);
1653         stream->read(&addr.port);
1654      }
1655      else
1656      {
1657         // IPV6
1658         addr.type = NetAddress::IPV6Address;
1659         stream->read(16, &addr.address.ipv6.netNum[0]);
1660         stream->read(&addr.port);
1661      }
1662
1663      pushPingRequest(&addr);
1664   }
1665
1666   // If this is the first list packet we have received, fill the packet status list
1667   // and start processing:
1668   if (!gGotFirstListPacket)
1669   {
1670      gGotFirstListPacket = true;
1671      gMasterServerQueryAddress = gMasterServerPing.address;
1672      U32 currentTime = Platform::getVirtualMilliseconds();
1673      for (i = 0; i < packetTotal; i++)
1674      {
1675         if (i != packetIndex)
1676         {
1677            PacketStatus* p = new PacketStatus(i, gMasterServerPing.key, currentTime);
1678            gPacketStatusList.push_back(*p);
1679         }
1680      }
1681
1682      processServerListPackets(gPingSession);
1683   }
1684   else
1685   {
1686      // Remove the packet we just received from the status list:
1687      for (i = 0; i < gPacketStatusList.size(); i++)
1688      {
1689         if (gPacketStatusList[i].index == packetIndex)
1690         {
1691            gPacketStatusList.erase(i);
1692            break;
1693         }
1694      }
1695   }
1696}
1697
1698//-----------------------------------------------------------------------------
1699
1700static void handleGameMasterInfoRequest( const NetAddress* address, U32 key, U8 flags )
1701{
1702   if ( GNet->doesAllowConnections() )
1703   {
1704      U8 temp8;
1705      U32 temp32;
1706
1707      char netString[256];
1708      Net::addressToString(address, netString);
1709
1710      Vector<MasterInfo> *masterList = getMasterServerList();
1711      const NetAddress *masterAddr;
1712      bool fromMaster = false;
1713      for(U32 i = 0; i < masterList->size(); i++)
1714      {
1715         masterAddr = &(*masterList)[i].address;
1716         if (masterAddr->isSameAddress(*address))
1717         {
1718            fromMaster = true;
1719            break;
1720         }
1721      }
1722
1723      Con::printf( "Received info request from %s [%s].", fromMaster?"a master server":"a machine", netString);
1724
1725      BitStream *out = BitStream::getPacketStream();
1726      out->clearStringBuffer();
1727
1728      out->write( U8( NetInterface::GameMasterInfoResponse ) );
1729      out->write( U8( flags ) );
1730      out->write( key );
1731
1732      writeCString( out, Con::getVariable( "Server::GameType" ) );
1733      writeCString( out, Con::getVariable( "Server::MissionType" ) );
1734      temp8 = U8( Con::getIntVariable( "pref::Server::MaxPlayers" ) );
1735      out->write( temp8 );
1736      temp32 = Con::getIntVariable( "pref::Server::RegionMask" );
1737      out->write( temp32 );
1738      temp32 = getVersionNumber();
1739      out->write( temp32 );
1740      temp8 = 0;
1741#if defined(TORQUE_OS_LINUX) || defined(TORQUE_OS_OPENBSD)
1742      temp8 |= ServerInfo::Status_Linux;
1743#endif
1744#if defined(TORQUE_OS_XENON)
1745      temp8 |= ServerInfo::Status_Xenon;
1746#endif
1747      if ( Con::getBoolVariable( "Server::Dedicated" ) )
1748         temp8 |= ServerInfo::Status_Dedicated;
1749      if ( dStrlen( Con::getVariable( "pref::Server::Password" ) ) > 0 )
1750         temp8 |= ServerInfo::Status_Passworded;
1751      out->write( temp8 );
1752      temp8 = U8( Con::getIntVariable( "Server::BotCount" ) );
1753      out->write( temp8 );
1754      out->write( Platform::SystemInfo.processor.mhz );
1755
1756      U8 playerCount = U8( Con::getIntVariable( "Server::PlayerCount" ) );
1757      out->write( playerCount );
1758
1759      const char* guidList = Con::getVariable( "Server::GuidList" );
1760      char* buf = new char[dStrlen( guidList ) + 1];
1761      dStrcpy( buf, guidList );
1762      char* temp = dStrtok( buf, "\t" );
1763      temp8 = 0;
1764      for ( ; temp && temp8 < playerCount; temp8++ )
1765      {
1766         out->write( U32( dAtoi( temp ) ) );
1767         temp = dStrtok( NULL, "\t" );
1768         temp8++;
1769      }
1770
1771      for ( ; temp8 < playerCount; temp8++ )
1772         out->write( U32( 0 ) );
1773
1774      delete [] buf;
1775
1776      BitStream::sendPacketStream(address);
1777   }
1778}
1779
1780//-----------------------------------------------------------------------------
1781
1782static void handleGamePingRequest( const NetAddress* address, U32 key, U8 flags )
1783{
1784   // Do not respond if a mission is not running:
1785   if ( GNet->doesAllowConnections() )
1786   {
1787      // Do not respond if this is a single-player game:
1788      if ( dStricmp( Con::getVariable( "Server::ServerType" ), "SinglePlayer" ) == 0 )
1789         return;
1790
1791      // Do not respond to offline queries if this is an online server:
1792      if (  flags & ServerFilter::OfflineQuery  )
1793         return;
1794
1795      // some banning code here (?)
1796
1797      BitStream *out = BitStream::getPacketStream();
1798      out->clearStringBuffer();
1799
1800      out->write( U8( NetInterface::GamePingResponse ) );
1801      out->write( flags );
1802      out->write( key );
1803      if ( flags & ServerFilter::NoStringCompress )
1804         writeCString( out, versionString );
1805      else
1806         out->writeString( versionString );
1807      out->write( GameConnection::CurrentProtocolVersion );
1808      out->write( GameConnection::MinRequiredProtocolVersion );
1809      out->write( getVersionNumber() );
1810
1811      // Enforce a 24-character limit on the server name:
1812      char serverName[25];
1813      dStrncpy( serverName, Con::getVariable( "pref::Server::Name" ), 24 );
1814      serverName[24] = 0;
1815      if ( flags & ServerFilter::NoStringCompress )
1816         writeCString( out, serverName );
1817      else
1818         out->writeString( serverName );
1819
1820      BitStream::sendPacketStream(address);
1821   }
1822}
1823
1824//-----------------------------------------------------------------------------
1825
1826static void handleGamePingResponse( const NetAddress* address, BitStream* stream, U32 key, U8 /*flags*/ )
1827{
1828   // Broadcast has timed out or query has been cancelled:
1829   if( !gPingList.size() )
1830      return;
1831
1832   S32 index = findPingEntry( gPingList, address );
1833   if( index == -1 )
1834   {
1835      // an anonymous ping response - if it's not already timed
1836      // out or finished, ping it.  Probably from a broadcast
1837      if( !addressFinished( address ) )
1838         pushPingRequest( address );
1839      return;
1840   }
1841   Ping &p = gPingList[index];
1842   U32 infoKey = ( p.session << 16 ) | ( p.key & 0xFFFF );
1843   if( infoKey != key )
1844      return;
1845
1846   // Find if the server info already exists (favorite or refreshing):
1847   ServerInfo* si = findServerInfo( address );
1848   bool applyFilter = false;
1849   if ( sActiveFilter.type == ServerFilter::Normal )
1850      applyFilter = si ? !si->isUpdating() : true;
1851
1852   char addrString[256];
1853   Net::addressToString( address, addrString );
1854   bool waitingForMaster = ( sActiveFilter.type == ServerFilter::Normal ) && !gGotFirstListPacket;
1855
1856   // Verify the version:
1857   char buf[256];
1858   stream->readString( buf );
1859   if ( dStrcmp( buf, versionString ) != 0 )
1860   {
1861      // Version is different, so remove it from consideration:
1862      Con::printf( "Server %s is a different version.", addrString );
1863      Con::printf( "Wanted version %s, got version %s", versionString, buf);
1864      gFinishedList.push_back( *address );
1865      gPingList.erase( index );
1866      if ( si )
1867      {
1868         si->status = ServerInfo::Status_TimedOut;
1869         gServerBrowserDirty = true;
1870      }
1871      if ( !waitingForMaster )
1872         updatePingProgress();
1873      return;
1874   }
1875
1876   // See if the server meets our minimum protocol:
1877   U32 temp32;
1878   stream->read( &temp32 );
1879   if ( temp32 < GameConnection::MinRequiredProtocolVersion )
1880   {
1881      Con::printf( "Protocol for server %s does not meet minimum protocol.", addrString );
1882      gFinishedList.push_back( *address );
1883      gPingList.erase( index );
1884      if ( si )
1885      {
1886         si->status = ServerInfo::Status_TimedOut;
1887         gServerBrowserDirty = true;
1888      }
1889      if ( !waitingForMaster )
1890         updatePingProgress();
1891      return;
1892   }
1893
1894   // See if we meet the server's minimum protocol:
1895   stream->read( &temp32 );
1896   if ( GameConnection::CurrentProtocolVersion < temp32 )
1897   {
1898      Con::printf( "You do not meet the minimum protocol for server %s.", addrString );
1899      gFinishedList.push_back( *address );
1900      gPingList.erase( index );
1901      if ( si )
1902      {
1903         si->status = ServerInfo::Status_TimedOut;
1904         gServerBrowserDirty = true;
1905      }
1906      if ( !waitingForMaster )
1907         updatePingProgress();
1908      return;
1909   }
1910
1911   // Calculate the ping:
1912   U32 time = Platform::getVirtualMilliseconds();
1913   U32 ping = ( time > p.time ) ? time - p.time : 0;
1914
1915   // Check for max ping filter:
1916   if ( applyFilter && sActiveFilter.maxPing > 0 && ping > sActiveFilter.maxPing )
1917   {
1918      // Ping is too high, so remove this server from consideration:
1919      Con::printf( "Server %s filtered out by maximum ping.", addrString );
1920      gFinishedList.push_back( *address );
1921      gPingList.erase( index );
1922      if ( si )
1923         removeServerInfo( address );
1924      if ( !waitingForMaster )
1925         updatePingProgress();
1926      return;
1927   }
1928
1929   // Get the server build version:
1930   stream->read( &temp32 );
1931   if ( applyFilter
1932     && ( sActiveFilter.filterFlags & ServerFilter::CurrentVersion )
1933     && ( temp32 != getVersionNumber() ) )
1934   {
1935      Con::printf( "Server %s filtered out by version number.", addrString );
1936      gFinishedList.push_back( *address );
1937      gPingList.erase( index );
1938      if ( si )
1939         removeServerInfo( address );
1940      if ( !waitingForMaster )
1941         updatePingProgress();
1942      return;
1943   }
1944
1945   // OK, we can finally create the server info structure:
1946   if ( !si )
1947      si = findOrCreateServerInfo( address );
1948   si->ping = ping;
1949   si->version = temp32;
1950
1951   // Get the server name:
1952   stream->readString( buf );
1953   if ( !si->name )
1954   {
1955      si->name = (char*) dMalloc( dStrlen( buf ) + 1 );
1956      dStrcpy( si->name, buf );
1957   }
1958
1959   // Set the server up to be queried:
1960   gFinishedList.push_back( *address );
1961   p.key = 0;
1962   p.time = 0;
1963   p.tryCount = gQueryRetryCount;
1964   gQueryList.push_back( p );
1965   gServerQueryCount++;
1966   gPingList.erase( index );
1967   if ( !waitingForMaster )
1968      updatePingProgress();
1969
1970   // Update the server browser gui!
1971   gServerBrowserDirty = true;
1972}
1973
1974//-----------------------------------------------------------------------------
1975
1976static void handleGameInfoRequest( const NetAddress* address, U32 key, U8 flags )
1977{
1978   // Do not respond unless there is a server running:
1979   if ( GNet->doesAllowConnections() )
1980   {
1981      // Do not respond to offline queries if this is an online server:
1982      if ( flags & ServerFilter::OfflineQuery )
1983         return;
1984
1985      bool compressStrings = !( flags & ServerFilter::NoStringCompress );
1986      BitStream *out = BitStream::getPacketStream();
1987      out->clearStringBuffer();
1988
1989      out->write( U8( NetInterface::GameInfoResponse ) );
1990      out->write( flags );
1991      out->write( key );
1992
1993      if ( compressStrings ) {
1994         out->writeString( Con::getVariable( "Server::GameType" ) );
1995         out->writeString( Con::getVariable( "Server::MissionType" ) );
1996         out->writeString( Con::getVariable( "Server::MissionName" ) );
1997      }
1998      else {
1999         writeCString( out, Con::getVariable( "Server::GameType" ) );
2000         writeCString( out, Con::getVariable( "Server::MissionType" ) );
2001         writeCString( out, Con::getVariable( "Server::MissionName" ) );
2002      }
2003
2004      U8 status = 0;
2005#if defined(TORQUE_OS_LINUX) || defined(TORQUE_OS_OPENBSD)
2006      status |= ServerInfo::Status_Linux;
2007#endif
2008#if defined(TORQUE_OS_XENON)
2009      status |= ServerInfo::Status_Xenon;
2010#endif
2011      if ( Con::getBoolVariable( "Server::Dedicated" ) )
2012         status |= ServerInfo::Status_Dedicated;
2013      if ( dStrlen( Con::getVariable( "pref::Server::Password" ) ) )
2014         status |= ServerInfo::Status_Passworded;
2015      out->write( status );
2016
2017      out->write( U8( Con::getIntVariable( "Server::PlayerCount" ) ) );
2018      out->write( U8( Con::getIntVariable( "pref::Server::MaxPlayers" ) ) );
2019      out->write( U8( Con::getIntVariable( "Server::BotCount" ) ) );
2020      out->write( U16( Platform::SystemInfo.processor.mhz ) );
2021      if ( compressStrings )
2022         out->writeString( Con::getVariable( "pref::Server::Info" ) );
2023      else
2024         writeCString( out, Con::getVariable( "pref::Server::Info" ) );
2025      writeLongCString( out, Con::evaluate( "onServerInfoQuery();" ) );
2026
2027      BitStream::sendPacketStream(address);
2028   }
2029}
2030
2031//-----------------------------------------------------------------------------
2032
2033static void handleGameInfoResponse( const NetAddress* address, BitStream* stream, U32 /*key*/, U8 /*flags*/ )
2034{
2035   if ( !gQueryList.size() )
2036      return;
2037
2038   S32 index = findPingEntry( gQueryList, address );
2039   if ( index == -1 )
2040      return;
2041
2042   // Remove the server from the query list since it has been so kind as to respond:
2043   gQueryList.erase( index );
2044   updateQueryProgress();
2045   ServerInfo *si = findServerInfo( address );
2046   if ( !si )
2047      return;
2048
2049   bool isUpdate = si->isUpdating();
2050   bool applyFilter = !isUpdate && ( sActiveFilter.type == ServerFilter::Normal );
2051   char addrString[256];
2052   Net::addressToString( address, addrString );
2053
2054   // Get the rules set:
2055   char stringBuf[2048];   // Who knows how big this should be?
2056   stream->readString( stringBuf );
2057   if ( !si->gameType || dStricmp( si->gameType, stringBuf ) != 0 )
2058   {
2059      si->gameType = (char*) dRealloc( (void*) si->gameType, dStrlen( stringBuf ) + 1 );
2060      dStrcpy( si->gameType, stringBuf );
2061
2062      // Test against the active filter:
2063      if ( applyFilter && dStricmp( sActiveFilter.gameType, "any" ) != 0
2064        && dStricmp( si->gameType, sActiveFilter.gameType ) != 0 )
2065      {
2066         Con::printf( "Server %s filtered out by rules set. (%s:%s)", addrString, sActiveFilter.gameType, si->gameType );
2067         removeServerInfo( address );
2068         return;
2069      }
2070   }
2071
2072   // Get the mission type:
2073   stream->readString( stringBuf );
2074   if ( !si->missionType || dStrcmp( si->missionType, stringBuf ) != 0 )
2075   {
2076      si->missionType = (char*) dRealloc( (void*) si->missionType, dStrlen( stringBuf ) + 1 );
2077      dStrcpy( si->missionType, stringBuf );
2078
2079      // Test against the active filter:
2080      if ( applyFilter && dStricmp( sActiveFilter.missionType, "any" ) != 0
2081        && dStricmp( si->missionType, sActiveFilter.missionType ) != 0 )
2082      {
2083         Con::printf( "Server %s filtered out by mission type. (%s:%s)", addrString, sActiveFilter.missionType, si->missionType );
2084         removeServerInfo( address );
2085         return;
2086      }
2087   }
2088
2089   // Get the mission name:
2090   stream->readString( stringBuf );
2091   // Clip the file extension off:
2092   char* temp = dStrstr( static_cast<char*>( stringBuf ), const_cast<char*>( ".mis" ) );
2093   if ( temp )
2094      *temp = '\0';
2095   if ( !si->missionName || dStrcmp( si->missionName, stringBuf ) != 0 )
2096   {
2097      si->missionName = (char*) dRealloc( (void*) si->missionName, dStrlen( stringBuf ) + 1 );
2098      dStrcpy( si->missionName, stringBuf );
2099   }
2100
2101   // Get the server status:
2102   U8 temp_U8;
2103   stream->read( &temp_U8 );
2104   si->status = temp_U8;
2105
2106   // Filter by the flags:
2107   if ( applyFilter )
2108   {
2109      if ( sActiveFilter.filterFlags & ServerFilter::Dedicated && !si->isDedicated() )
2110      {
2111         Con::printf( "Server %s filtered out by dedicated flag.", addrString );
2112         removeServerInfo( address );
2113         return;
2114      }
2115
2116      if ( sActiveFilter.filterFlags & ServerFilter::NotPassworded && si->isPassworded() )
2117      {
2118         Con::printf( "Server %s filtered out by no-password flag.", addrString );
2119         removeServerInfo( address );
2120         return;
2121      }
2122
2123      if ( sActiveFilter.filterFlags & ServerFilter::NotXenon && si->isXenon() )
2124      {
2125         Con::printf( "Server %s filtered out by no-xenon flag.", addrString );
2126         removeServerInfo( address );
2127         return;
2128      }
2129   }
2130   si->status.set( ServerInfo::Status_Responded );
2131
2132   // Get the player count:
2133   stream->read( &si->numPlayers );
2134
2135   // Test player count against active filter:
2136   if ( applyFilter && ( si->numPlayers < sActiveFilter.minPlayers || si->numPlayers > sActiveFilter.maxPlayers ) )
2137   {
2138      Con::printf( "Server %s filtered out by player count.", addrString );
2139      removeServerInfo( address );
2140      return;
2141   }
2142
2143   // Get the max players and bot count:
2144   stream->read( &si->maxPlayers );
2145   stream->read( &si->numBots );
2146
2147   // Test bot count against active filter:
2148   if ( applyFilter && ( si->numBots > sActiveFilter.maxBots ) )
2149   {
2150      Con::printf( "Server %s filtered out by maximum bot count.", addrString );
2151      removeServerInfo( address );
2152      return;
2153   }
2154
2155   // Get the CPU speed;
2156   U16 temp_U16;
2157   stream->read( &temp_U16 );
2158   si->cpuSpeed = temp_U16;
2159
2160   // Test CPU speed against active filter:
2161   if ( applyFilter && ( si->cpuSpeed < sActiveFilter.minCPU ) )
2162   {
2163      Con::printf( "Server %s filtered out by minimum CPU speed.", addrString );
2164      removeServerInfo( address );
2165      return;
2166   }
2167
2168   // Get the server info:
2169   stream->readString( stringBuf );
2170   if ( !si->statusString || ( isUpdate && dStrcmp( si->statusString, stringBuf ) != 0 ) )
2171   {
2172      si->infoString = (char*) dRealloc( (void*) si->infoString, dStrlen( stringBuf ) + 1 );
2173      dStrcpy( si->infoString, stringBuf );
2174   }
2175
2176   // Get the content string:
2177   readLongCString( stream, stringBuf );
2178   if ( !si->statusString || ( isUpdate && dStrcmp( si->statusString, stringBuf ) != 0 ) )
2179   {
2180      si->statusString = (char*) dRealloc( (void*) si->statusString, dStrlen( stringBuf ) + 1 );
2181      dStrcpy( si->statusString, stringBuf );
2182   }
2183
2184   // Update the server browser gui!
2185   gServerBrowserDirty = true;
2186}
2187
2188//-----------------------------------------------------------------------------
2189// Packet Dispatch
2190
2191void DemoNetInterface::handleInfoPacket( const NetAddress* address, U8 packetType, BitStream* stream )
2192{
2193   U8 flags;
2194   U32 key;
2195
2196   stream->read( &flags );
2197   stream->read( &key );
2198   stream->clearStringBuffer();
2199
2200   switch( packetType )
2201   {
2202      case GamePingRequest:
2203         handleGamePingRequest( address, key, flags );
2204         break;
2205
2206      case GamePingResponse:
2207         handleGamePingResponse( address, stream, key, flags );
2208         break;
2209
2210      case GameInfoRequest:
2211         handleGameInfoRequest( address, key, flags );
2212         break;
2213
2214      case GameInfoResponse:
2215         handleGameInfoResponse( address, stream, key, flags );
2216         break;
2217
2218      case MasterServerGameTypesResponse:
2219         handleMasterServerGameTypesResponse( stream, key, flags );
2220         break;
2221
2222      case MasterServerListResponse:
2223         handleMasterServerListResponse( stream, key, flags );
2224         break;
2225
2226      case GameMasterInfoRequest:
2227         handleGameMasterInfoRequest( address, key, flags );
2228         break;
2229
2230      case MasterServerExtendedListResponse:
2231         handleExtendedMasterServerListResponse(stream, key, flags);
2232         break;
2233   }
2234}
2235
2236
2237ConsoleFunctionGroupEnd( ServerQuery );
2238