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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> master server <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> online games." )
DefineEngineFunction(cancelServerQuery , void , () , "cancelServerQuery();" )
DefineEngineFunction(getServerCount , int , () , "getServerCount();" )
DefineEngineFunction(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(...);" )
DefineEngineFunction(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(...);" )
DefineEngineFunction(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(...);" )
DefineEngineFunction(querySingleServer , void , (const char *addrText, U8 flags) , (0) , "querySingleServer(address, flags);" )
DefineEngineFunction(setServerInfo , bool , (U32 index) , "setServerInfo(index);" )
DefineEngineFunction(startHeartbeat , void , () , "startHeartbeat();" )
DefineEngineFunction(stopHeartbeat , void , () , "stopHeartbeat();" )
DefineEngineFunction(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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> master server <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> online games." )

ConsoleFunctionGroupEnd(ServerQuery )

countPingRequests()

DefineEngineFunction(cancelServerQuery , void , () , "cancelServerQuery();" )

DefineEngineFunction(getServerCount , int , () , "getServerCount();" )

DefineEngineFunction(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(...);" )

DefineEngineFunction(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(...);" )

DefineEngineFunction(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(...);" )

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

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

DefineEngineFunction(startHeartbeat , void , () , "startHeartbeat();" )

DefineEngineFunction(stopHeartbeat , void , () , "stopHeartbeat();" )

DefineEngineFunction(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(6)
 228   };
 229
 230   enum // Region mask flags
 231   {
 232      RegionIsIPV4Address = BIT(30),
 233      RegionIsIPV6Address = BIT(31),
 234
 235      RegionAddressMask = RegionIsIPV4Address | RegionIsIPV6Address
 236   };
 237   
 238   //Rearranging the fields according to their sizes
 239   char* gameType;
 240   char* missionType;
 241   U8    queryFlags;
 242   U8    minPlayers;
 243   U8    maxPlayers;
 244   U8    maxBots;
 245   U8    filterFlags;
 246   U8    buddyCount;
 247   U16   minCPU;
 248   U32   regionMask;
 249   U32   maxPing;
 250   U32*  buddyList;
 251   Type  type;
 252
 253   ServerFilter()
 254   {
 255      type = Normal;
 256      queryFlags = NewStyleResponse;
 257      gameType = NULL;
 258      missionType = NULL;
 259      minPlayers = 0;
 260      maxPlayers = 255;
 261      maxBots = 16;
 262      regionMask = 0xFFFFFFFF;
 263      maxPing = 0;
 264      filterFlags = 0;
 265      minCPU = 0;
 266      buddyCount = 0;
 267      buddyList = NULL;
 268   }
 269
 270   ~ServerFilter()
 271   {
 272      if ( gameType )
 273         dFree( gameType );
 274      if ( missionType )
 275         dFree( missionType );
 276      if ( buddyList )
 277         dFree( buddyList );
 278   }
 279};
 280
 281static ServerFilter sActiveFilter;
 282
 283
 284//-----------------------------------------------------------------------------
 285// Forward function declarations:
 286//-----------------------------------------------------------------------------
 287
 288static void pushPingRequest( const NetAddress *addr );
 289static void pushPingBroadcast( const NetAddress *addr );
 290static void pushServerFavorites();
 291static bool pickMasterServer();
 292static S32 findPingEntry( Vector<Ping> &v, const NetAddress* addr );
 293static bool addressFinished( const NetAddress* addr );
 294static ServerInfo* findServerInfo( const NetAddress* addr );
 295static ServerInfo* findOrCreateServerInfo( const NetAddress* addr );
 296static void removeServerInfo( const NetAddress* addr );
 297static void sendPacket( U8 pType, const NetAddress* addr, U32 key, U32 session, U8 flags );
 298static void writeCString( BitStream* stream, const char* string );
 299static void readCString( BitStream* stream, char* buffer );
 300static void writeLongCString( BitStream* stream, const char* string );
 301static void readLongCString( BitStream* stream, char* buffer );
 302static void processMasterServerQuery( U32 session );
 303static void processPingsAndQueries( U32 session, bool schedule = true);
 304static void processServerListPackets( U32 session );
 305static void processHeartbeat(U32);
 306static void updatePingProgress();
 307static void updateQueryProgress();
 308Vector<MasterInfo>* getMasterServerList();
 309bool pickMasterServer();
 310void clearServerList();
 311
 312
 313//-----------------------------------------------------------------------------
 314// Events
 315//-----------------------------------------------------------------------------
 316
 317//----------------------------------------------------------------
 318class ProcessMasterQueryEvent : public SimEvent
 319{
 320   U32 session;
 321   public:
 322      ProcessMasterQueryEvent( U32 _session )
 323      {
 324         session = _session;
 325      }
 326      void process( SimObject *object )
 327      {
 328         processMasterServerQuery( session );
 329      }
 330};
 331
 332//----------------------------------------------------------------
 333class ProcessPingEvent : public SimEvent
 334{
 335   U32 session;
 336   public:
 337      ProcessPingEvent( U32 _session )
 338      {
 339         session = _session;
 340      }
 341      void process( SimObject *object )
 342      {
 343         processPingsAndQueries( session );
 344      }
 345};
 346
 347//----------------------------------------------------------------
 348class ProcessPacketEvent : public SimEvent
 349{
 350   U32 session;
 351   public:
 352      ProcessPacketEvent( U32 _session )
 353      {
 354         session = _session;
 355      }
 356
 357      void process( SimObject *object )
 358      {
 359         processServerListPackets( session );
 360      }
 361};
 362
 363//----------------------------------------------------------------
 364class HeartbeatEvent : public SimEvent
 365{
 366   U32 mSeq;
 367   public:
 368      HeartbeatEvent(U32 seq)
 369      {
 370         mSeq = seq;
 371      }
 372      void process( SimObject *object )
 373      {
 374         processHeartbeat(mSeq);
 375      }
 376};
 377
 378
 379//-----------------------------------------------------------------------------
 380// Public query methods
 381//-----------------------------------------------------------------------------
 382
 383//-----------------------------------------------------------------------------
 384
 385void queryLanServers(U32 port, U8 flags, const char* gameType, const char* missionType,
 386      U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, U16 minCPU,
 387      U8 filterFlags)
 388{
 389   sgServerQueryActive = true;
 390   pushServerFavorites();
 391
 392      sActiveFilter.type = ServerFilter::Offline;
 393
 394      // Clear the filter:
 395      if ( !sActiveFilter.gameType || dStricmp( sActiveFilter.gameType, "Any" ) != 0 )
 396      {
 397         sActiveFilter.gameType = (char*) dRealloc( sActiveFilter.gameType, 4 );
 398         dStrcpy( sActiveFilter.gameType, "Any", 4 );
 399      }
 400      if ( !sActiveFilter.missionType || dStricmp( sActiveFilter.missionType, "Any" ) != 0 )
 401      {
 402         sActiveFilter.missionType = (char*) dRealloc( sActiveFilter.missionType, 4 );
 403         dStrcpy( sActiveFilter.missionType, "Any", 4 );
 404      }
 405      sActiveFilter.queryFlags   = 0;
 406   sActiveFilter.minPlayers   = minPlayers;
 407   sActiveFilter.maxPlayers   = maxPlayers;
 408   sActiveFilter.maxBots      = maxBots;
 409   sActiveFilter.regionMask   = regionMask;
 410   sActiveFilter.maxPing      = maxPing;
 411   sActiveFilter.minCPU       = minCPU;
 412   sActiveFilter.filterFlags  = filterFlags;
 413
 414   NetAddress addr;
 415   char addrText[256];
 416
 417   // IPV4
 418   dSprintf( addrText, sizeof( addrText ), "IP:BROADCAST:%d", port );
 419   Net::stringToAddress( addrText, &addr );
 420   pushPingBroadcast( &addr );
 421
 422   // IPV6
 423   dSprintf(addrText, sizeof(addrText), "IP6:MULTICAST:%d", port);
 424   Net::stringToAddress(addrText, &addr);
 425   pushPingBroadcast(&addr);
 426
 427   Con::executef("onServerQueryStatus", "start", "Querying LAN servers", "0");
 428   processPingsAndQueries( gPingSession );
 429}
 430
 431//-----------------------------------------------------------------------------
 432
 433DefineEngineFunction( queryAllServers
 434                     , void, ( U32 lanPort
 435                             , U32 flags
 436                             , const char * gameType
 437                             , const char * missionType
 438                             , U32 minPlayers
 439                             , U32 maxPlayers
 440                             , U32 maxBots
 441                             , U32 regionMask
 442                             , U32 maxPing
 443                             , U32 minCPU
 444                             , U32 filterFlags )
 445                     , , "queryAllServers(...);" )
 446{
 447   U32 buddyList = 0;
 448
 449   clearServerList();
 450
 451   queryMasterServer(flags,gameType,missionType,minPlayers,maxPlayers,
 452      maxBots,regionMask,maxPing,minCPU,filterFlags,0,&buddyList);
 453
 454   queryLanServers(lanPort, flags, gameType, missionType, minPlayers, maxPlayers, maxBots,
 455      regionMask, maxPing, minCPU, filterFlags);
 456
 457}
 458
 459DefineEngineFunction( queryLanServers
 460                     , void, ( U32 lanPort
 461                             , U32 flags
 462                             , const char * gameType
 463                             , const char * missionType
 464                             , U32 minPlayers
 465                             , U32 maxPlayers
 466                             , U32 maxBots
 467                             , U32 regionMask
 468                             , U32 maxPing
 469                             , U32 minCPU
 470                             , U32 filterFlags )
 471                     , , "queryLanServers(...);" )
 472
 473{
 474
 475   clearServerList();
 476   queryLanServers(lanPort, flags, gameType, missionType, minPlayers, maxPlayers, maxBots,
 477      regionMask, maxPing, minCPU, filterFlags);
 478
 479}
 480
 481//-----------------------------------------------------------------------------
 482
 483void queryMasterGameTypes()
 484{
 485   Vector<MasterInfo> *masterList = getMasterServerList();
 486   if ( masterList->size() != 0 )
 487   {
 488      U32 master = Sim::getCurrentTime() % masterList->size();
 489      // Send a request to the master server for the game types:
 490      Con::printf( "Requesting game types from the master server..." );
 491      sendPacket( NetInterface::MasterServerGameTypesRequest, &(*masterList)[master].address, gKey, gPingSession, 0 );
 492   }
 493}
 494
 495//-----------------------------------------------------------------------------
 496
 497void queryMasterServer(U8 flags, const char* gameType, const char* missionType,
 498      U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing,
 499      U16 minCPU, U8 filterFlags, U8 buddyCount, U32* buddyList )
 500{
 501   // Reset the list packet flag:
 502   gGotFirstListPacket = false;
 503   sgServerQueryActive = true;
 504
 505   Con::executef( "onServerQueryStatus", "start", "Querying master server", "0");
 506
 507   if ( buddyCount == 0 )
 508   {
 509      sActiveFilter.type = ServerFilter::Normal;
 510
 511      // Update the active filter:
 512      if ( !sActiveFilter.gameType || String::compare( sActiveFilter.gameType, gameType ) != 0 )
 513      {
 514         dsize_t gameTypeLen = dStrlen(gameType) + 1;
 515         sActiveFilter.gameType = (char*) dRealloc( sActiveFilter.gameType, gameTypeLen );
 516         dStrcpy( sActiveFilter.gameType, gameType, gameTypeLen );
 517      }
 518
 519      if ( !sActiveFilter.missionType || String::compare( sActiveFilter.missionType, missionType ) != 0 )
 520      {
 521         dsize_t missionTypeLen = dStrlen(missionType) + 1;
 522         sActiveFilter.missionType = (char*) dRealloc( sActiveFilter.missionType, missionTypeLen );
 523         dStrcpy( sActiveFilter.missionType, missionType, missionTypeLen );
 524      }
 525
 526      sActiveFilter.queryFlags   = flags | ServerFilter::NewStyleResponse;
 527      sActiveFilter.minPlayers   = minPlayers;
 528      sActiveFilter.maxPlayers   = maxPlayers;
 529      sActiveFilter.maxBots      = maxBots;
 530      sActiveFilter.regionMask   = regionMask;
 531      sActiveFilter.maxPing      = maxPing;
 532      sActiveFilter.minCPU       = minCPU;
 533      sActiveFilter.filterFlags  = filterFlags;
 534      sActiveFilter.buddyCount   = buddyCount;
 535      dFree( sActiveFilter.buddyList );
 536      sActiveFilter.buddyList = NULL;
 537   }
 538   else
 539   {
 540      sActiveFilter.type = ServerFilter::Buddy;
 541      sActiveFilter.buddyCount = buddyCount;
 542      sActiveFilter.buddyList = (U32*) dRealloc( sActiveFilter.buddyList, buddyCount * 4 );
 543      sActiveFilter.queryFlags = ServerFilter::NewStyleResponse;
 544      dMemcpy( sActiveFilter.buddyList, buddyList, buddyCount * 4 );
 545      clearServerList();
 546   }
 547
 548   // Pick a random master server from the list:
 549   gMasterServerList.clear();
 550   Vector<MasterInfo> *masterList = getMasterServerList();
 551   for ( U32 i = 0; i < masterList->size(); i++ )
 552      gMasterServerList.push_back( (*masterList)[i] );
 553
 554   // Clear the master server ping:
 555   gMasterServerPing.time = 0;
 556   gMasterServerPing.tryCount = gMasterServerRetryCount;
 557
 558   if ( !pickMasterServer() )
 559      Con::errorf( "No master servers found!" );
 560   else
 561      processMasterServerQuery( gPingSession );
 562}
 563
 564DefineEngineFunction( queryMasterServer
 565                     , void, (  U32 flags
 566                             , const char * gameType
 567                             , const char * missionType
 568                             , U32 minPlayers
 569                             , U32 maxPlayers
 570                             , U32 maxBots
 571                             , U32 regionMask
 572                             , U32 maxPing
 573                             , U32 minCPU
 574                             , U32 filterFlags )
 575                     , , "queryMasterServer(...);" )
 576{
 577   U32 buddyList = 0;
 578
 579   clearServerList();
 580   queryMasterServer(flags,gameType,missionType,minPlayers,maxPlayers,
 581      maxBots,regionMask,maxPing,minCPU,filterFlags,0,&buddyList);
 582}
 583
 584//-----------------------------------------------------------------------------
 585
 586DefineEngineFunction( querySingleServer
 587                     , void, ( const char* addrText, U8 flags )
 588                     , (0), "querySingleServer(address, flags);" )
 589{
 590   NetAddress addr;
 591   Net::stringToAddress( addrText, &addr );
 592   querySingleServer(&addr,flags);
 593}
 594
 595//-----------------------------------------------------------------------------
 596
 597void queryFavoriteServers( U8 /*flags*/ )
 598{
 599   sgServerQueryActive = true;
 600   clearServerList();
 601   sActiveFilter.type = ServerFilter::Favorites;
 602   pushServerFavorites();
 603
 604   Con::executef( "onServerQueryStatus", "start", "Query favorites...", "0" );
 605   processPingsAndQueries( gPingSession );
 606}
 607
 608//-----------------------------------------------------------------------------
 609
 610void querySingleServer( const NetAddress* addr, U8 /*flags*/ )
 611{
 612   sgServerQueryActive = true;
 613   ServerInfo* si = findServerInfo( addr );
 614   if ( si )
 615      si->status = ServerInfo::Status_New | ServerInfo::Status_Updating;
 616
 617   // Remove the server from the finished list (if it's there):
 618   for ( U32 i = 0; i < gFinishedList.size(); i++ )
 619   {
 620      if ( Net::compareAddresses( addr, &gFinishedList[i] ) )
 621      {
 622         gFinishedList.erase( i );
 623         break;
 624      }
 625   }
 626
 627   Con::executef( "onServerQueryStatus", "start", "Refreshing server...", "0" );
 628   gServerPingCount = gServerQueryCount = 0;
 629   pushPingRequest( addr );
 630   processPingsAndQueries( gPingSession );
 631}
 632
 633//-----------------------------------------------------------------------------
 634
 635void cancelServerQuery()
 636{
 637   // Cancel the current query, if there is anything left
 638   // on the ping list, it's dropped.
 639   if ( sgServerQueryActive )
 640   {
 641      Con::printf( "Server query canceled." );
 642      ServerInfo* si;
 643
 644      // Clear the master server packet list:
 645      gPacketStatusList.clear();
 646
 647      // Clear the ping list:
 648      while ( gPingList.size() )
 649      {
 650         si = findServerInfo( &gPingList[0].address );
 651         if ( si && !si->status.test( ServerInfo::Status_Responded ) )
 652            si->status = ServerInfo::Status_TimedOut;
 653
 654         gPingList.erase( U32( 0 ) );
 655      }
 656
 657      // Clear the query list:
 658      while ( gQueryList.size() )
 659      {
 660         si = findServerInfo( &gQueryList[0].address );
 661         if ( si && !si->status.test( ServerInfo::Status_Responded ) )
 662            si->status = ServerInfo::Status_TimedOut;
 663
 664         gQueryList.erase( U32( 0 ) );
 665      }
 666
 667      sgServerQueryActive = false;
 668      gServerBrowserDirty = true;
 669   }
 670}
 671
 672DefineEngineFunction( cancelServerQuery, void, (), , "cancelServerQuery();" )
 673{
 674   cancelServerQuery();
 675}
 676
 677//-----------------------------------------------------------------------------
 678
 679void stopServerQuery()
 680{
 681   // Cancel the current query, anything left on the ping
 682   // list is moved to the finished list as "done".
 683   if ( sgServerQueryActive )
 684   {
 685      gPacketStatusList.clear();
 686
 687      if ( gPingList.size() )
 688      {
 689         while ( gPingList.size() )
 690         {
 691            gFinishedList.push_back( gPingList[0].address );
 692            gPingList.erase( U32( 0 ) );
 693         }
 694      }
 695      else
 696         cancelServerQuery();
 697   }
 698}
 699
 700DefineEngineFunction( stopServerQuery, void, (), , "stopServerQuery();" )
 701{
 702   stopServerQuery();
 703}
 704
 705//-----------------------------------------------------------------------------
 706
 707DefineEngineFunction( startHeartbeat, void, (), , "startHeartbeat();" )
 708{
 709   if (validateAuthenticatedServer()) {
 710      gHeartbeatSeq++;
 711      processHeartbeat(gHeartbeatSeq);  // thump-thump...
 712   }
 713}
 714
 715DefineEngineFunction( stopHeartbeat, void, (), , "stopHeartbeat();" )
 716{
 717   gHeartbeatSeq++;
 718}
 719
 720//-----------------------------------------------------------------------------
 721
 722DefineEngineFunction( getServerCount, int, (), , "getServerCount();" )
 723{
 724   return gServerList.size();
 725}
 726
 727DefineEngineFunction( setServerInfo, bool, (U32 index), , "setServerInfo(index);" )
 728{
 729   if (index < gServerList.size()) {
 730      ServerInfo& info = gServerList[index];
 731
 732      char addrString[256];
 733      Net::addressToString( &info.address, addrString );
 734
 735      Con::setIntVariable("ServerInfo::Status",info.status);
 736      Con::setVariable("ServerInfo::Address",addrString);
 737      Con::setVariable("ServerInfo::Name",info.name);
 738      Con::setVariable("ServerInfo::GameType",info.gameType);
 739      Con::setVariable("ServerInfo::MissionName",info.missionName);
 740      Con::setVariable("ServerInfo::MissionType",info.missionType);
 741      Con::setVariable("ServerInfo::State",info.statusString);
 742      Con::setVariable("ServerInfo::Info",info.infoString);
 743      Con::setIntVariable("ServerInfo::PlayerCount",info.numPlayers);
 744      Con::setIntVariable("ServerInfo::MaxPlayers",info.maxPlayers);
 745      Con::setIntVariable("ServerInfo::BotCount",info.numBots);
 746      Con::setIntVariable("ServerInfo::Version",info.version);
 747      Con::setIntVariable("ServerInfo::Ping",info.ping);
 748      Con::setIntVariable("ServerInfo::CPUSpeed",info.cpuSpeed);
 749      Con::setBoolVariable("ServerInfo::Favorite",info.isFavorite);
 750      Con::setBoolVariable("ServerInfo::Dedicated",info.isDedicated());
 751      Con::setBoolVariable("ServerInfo::Password",info.isPassworded());
 752      return true;
 753   }
 754   return false;
 755}
 756
 757
 758//-----------------------------------------------------------------------------
 759// Internal
 760//-----------------------------------------------------------------------------
 761
 762//-----------------------------------------------------------------------------
 763
 764ServerInfo::~ServerInfo()
 765{
 766   if ( name )
 767      dFree( name );
 768   if ( gameType )
 769      dFree( gameType );
 770   if ( missionType )
 771      dFree( missionType );
 772   if( missionName )
 773      dFree( missionName );
 774   if ( statusString )
 775      dFree( statusString );
 776   if ( infoString )
 777      dFree( infoString );
 778}
 779
 780//-----------------------------------------------------------------------------
 781
 782Vector<MasterInfo>* getMasterServerList()
 783{
 784   // This code used to get the master server list from the
 785   // WON.net directory.
 786   static Vector<MasterInfo> masterList;
 787   masterList.clear();
 788
 789   for (U32 i = 0; i < 10; i++) {
 790      char buffer[50];
 791      dSprintf(buffer,sizeof(buffer),"pref::Master%d",i);
 792      const char* master = Con::getVariable(buffer);
 793      if (master && *master) {
 794         NetAddress address;
 795         // Format for master server variable:
 796         //    regionMask:netAddress
 797         U32 region = 1; // needs to default to something > 0
 798         dSscanf(master,"%d:",&region);
 799         const char* madd = dStrchr(master,':') + 1;
 800         if (region && Net::stringToAddress(madd,&address) == Net::NoError) {
 801            masterList.increment();
 802            MasterInfo& info = masterList.last();
 803            info.address = address;
 804            info.region = region;
 805         }
 806         else
 807            Con::errorf("Bad master server address: %s",master);
 808      }
 809   }
 810
 811   if (!masterList.size())
 812      Con::errorf("No master servers found");
 813
 814   return &masterList;
 815}
 816
 817
 818//-----------------------------------------------------------------------------
 819
 820bool pickMasterServer()
 821{
 822   // Reset the master server ping:
 823   gMasterServerPing.time = 0;
 824   gMasterServerPing.key = 0;
 825   gMasterServerPing.tryCount = gMasterServerRetryCount;
 826   gMasterServerPing.session = gPingSession;
 827
 828   char addrString[256];
 829   U32 serverCount = gMasterServerList.size();
 830   if ( !serverCount )
 831   {
 832      // There are no more servers left to try...:(
 833      return( false );
 834   }
 835
 836   U32 region = Con::getIntVariable( "$pref::Net::RegionMask" );
 837   U32 index = Sim::getCurrentTime() % serverCount;
 838
 839   // First try to find a master server in the same region:
 840   for ( U32 i = 0; i < serverCount; i++ )
 841   {
 842      if ( gMasterServerList[index].region == region )
 843      {
 844         Net::addressToString( &gMasterServerList[index].address, addrString );
 845         Con::printf( "Found master server %s in same region.", addrString );
 846         gMasterServerPing.address = gMasterServerList[index].address;
 847         return( true );
 848      }
 849
 850      index = index < serverCount - 1 ? index + 1 : 0;
 851   }
 852
 853   // Settle for the one we first picked:
 854   Net::addressToString( &gMasterServerList[index].address, addrString );
 855   Con::printf( "No master servers found in this region, trying %s.", addrString );
 856   gMasterServerPing.address = gMasterServerList[index].address;
 857
 858   return( true );
 859}
 860
 861//-----------------------------------------------------------------------------
 862
 863void clearServerList()
 864{
 865   gPacketStatusList.clear();
 866   gServerList.clear();
 867   gFinishedList.clear();
 868   gPingList.clear();
 869   gQueryList.clear();
 870   gServerPingCount = gServerQueryCount = 0;
 871
 872   gPingSession++;
 873}
 874
 875//-----------------------------------------------------------------------------
 876
 877void sendHeartbeat( U8 flags )
 878{
 879   // send heartbeats to all of the master servers:
 880   Vector<MasterInfo> *masterList = getMasterServerList();
 881   for(U32 i = 0; i < masterList->size(); i++)
 882   {
 883      char buffer[256];
 884      Net::addressToString(&(*masterList)[i].address, buffer);
 885      // Send a request to the master server for the game types:
 886      Con::printf( "Sending heartbeat to master server [%s]", buffer );
 887      sendPacket( NetInterface::GameHeartbeat, &(*masterList)[i].address, 0, gPingSession, flags );
 888   }
 889}
 890
 891//-----------------------------------------------------------------------------
 892
 893static void pushPingRequest( const NetAddress* addr )
 894{
 895   if( addressFinished( addr ) )
 896      return;
 897
 898   Ping p;
 899   p.address = *addr;
 900   p.session = gPingSession;
 901   p.key = 0;
 902   p.time = 0;
 903   p.tryCount = gPingRetryCount;
 904   p.broadcast = false;
 905   gPingList.push_back( p );
 906   gServerPingCount++;
 907}
 908
 909//-----------------------------------------------------------------------------
 910
 911static void pushPingBroadcast( const NetAddress* addr )
 912{
 913   if( addressFinished( addr ) )
 914      return;
 915
 916   Ping p;
 917   p.address = *addr;
 918   p.session = gPingSession;
 919   p.key = 0;
 920   p.time = 0;
 921   p.tryCount = 1; // only try this once
 922   p.broadcast = true;
 923   gPingList.push_back( p );
 924   // Don't increment gServerPingCount, broadcasts are not
 925   // counted as requests.
 926}
 927
 928//-----------------------------------------------------------------------------
 929
 930static S32 countPingRequests()
 931{
 932   // Need a function here because the ping list also includes
 933   // broadcast pings we don't want counted.
 934   U32 pSize = gPingList.size(), count = pSize;
 935   for (U32 i = 0; i < pSize; i++)
 936      if (gPingList[i].broadcast)
 937         count--;
 938   return count;
 939}
 940
 941
 942//-----------------------------------------------------------------------------
 943
 944static void pushServerFavorites()
 945{
 946   S32 count = Con::getIntVariable( "$pref::Client::ServerFavoriteCount" );
 947   if ( count < 0 )
 948   {
 949      Con::setIntVariable( "$pref::Client::ServerFavoriteCount", 0 );
 950      return;
 951   }
 952
 953   NetAddress addr;
 954   const char* server = NULL;
 955   char buf[256], serverName[25], addrString[256];
 956   U32 sz, len;
 957   for ( S32 i = 0; i < count; i++ )
 958   {
 959      dSprintf( buf, sizeof( buf ), "pref::Client::ServerFavorite%d", i );
 960      server = Con::getVariable( buf );
 961      if ( server )
 962      {
 963         sz = dStrcspn( server, "\t" );
 964         if ( sz > 0 )
 965         {
 966            len = sz > 24 ? 24 : sz;
 967            dStrncpy( serverName, server, len );
 968            serverName[len] = 0;
 969            dStrncpy( addrString, server + ( sz + 1 ), 255 );
 970
 971            //Con::errorf( "Pushing server favorite \"%s\" - %s...", serverName, addrString );
 972            Net::stringToAddress( addrString, &addr );
 973            ServerInfo* si = findOrCreateServerInfo( &addr );
 974            AssertFatal(si, "pushServerFavorites - failed to create Server Info!" );
 975            dsize_t nameLen = dStrlen(serverName) + 1;
 976            si->name = (char*) dRealloc( (void*) si->name, nameLen );
 977            dStrcpy( si->name, serverName, nameLen );
 978            si->isFavorite = true;
 979            pushPingRequest( &addr );
 980         }
 981      }
 982   }
 983}
 984
 985//-----------------------------------------------------------------------------
 986
 987static S32 findPingEntry( Vector<Ping> &v, const NetAddress* addr )
 988{
 989   for ( U32 i = 0; i < v.size(); i++ )
 990      if ( Net::compareAddresses( addr, &v[i].address ) )
 991         return S32( i );
 992   return -1;
 993}
 994
 995//-----------------------------------------------------------------------------
 996
 997static bool addressFinished( const NetAddress* addr )
 998{
 999   for ( U32 i = 0; i < gFinishedList.size(); i++ )
1000      if ( Net::compareAddresses( addr, &gFinishedList[i] ) )
1001         return true;
1002   return false;
1003}
1004
1005//-----------------------------------------------------------------------------
1006
1007static ServerInfo* findServerInfo( const NetAddress* addr )
1008{
1009   for ( U32 i = 0; i < gServerList.size(); i++ )
1010      if ( Net::compareAddresses( addr, &gServerList[i].address ) )
1011         return &gServerList[i];
1012   return NULL;
1013}
1014
1015//-----------------------------------------------------------------------------
1016
1017static ServerInfo* findOrCreateServerInfo( const NetAddress* addr )
1018{
1019   ServerInfo* ret = findServerInfo( addr );
1020   if ( ret )
1021      return ret;
1022
1023   ServerInfo si;
1024   si.address = *addr;
1025   gServerList.push_back( si );
1026
1027   return &gServerList.last();
1028}
1029
1030//-----------------------------------------------------------------------------
1031
1032static void removeServerInfo( const NetAddress* addr )
1033{
1034   for ( U32 i = 0; i < gServerList.size(); i++ )
1035   {
1036      if ( Net::compareAddresses( addr, &gServerList[i].address ) )
1037      {
1038         gServerList.erase( i );
1039         gServerBrowserDirty = true;
1040      }
1041   }
1042}
1043
1044//-----------------------------------------------------------------------------
1045
1046#if defined(TORQUE_DEBUG)
1047// This function is solely for testing the functionality of the server browser
1048// with more servers in the list.
1049void addFakeServers( S32 howMany )
1050{
1051   static S32 sNumFakeServers = 1;
1052   ServerInfo newServer;
1053
1054   for ( S32 i = 0; i < howMany; i++ )
1055   {
1056      newServer.numPlayers = (U8)(Platform::getRandom() * 64.0f);
1057      newServer.maxPlayers = 64;
1058      char buf[256];
1059      dSprintf( buf, 255, "Fake server #%d", sNumFakeServers );
1060      dsize_t nameLen = dStrlen(buf) + 1;
1061      newServer.name = (char*) dMalloc( nameLen );
1062      dStrcpy( newServer.name, buf, nameLen );
1063      newServer.gameType = (char*) dMalloc( 5 );
1064      dStrcpy( newServer.gameType, "Fake", 5 );
1065      newServer.missionType = (char*) dMalloc( 16 );
1066      dStrcpy( newServer.missionType, "FakeMissionType", 16 );
1067      newServer.missionName = (char*) dMalloc( 14 );
1068      dStrcpy( newServer.missionName, "FakeMapName", 14 );
1069      Net::stringToAddress( "IP:198.74.33.35:28000", &newServer.address );
1070      newServer.ping = (U32)( Platform::getRandom() * 200.0f );
1071      newServer.cpuSpeed = 470;
1072      newServer.status = ServerInfo::Status_Responded;
1073
1074      gServerList.push_back( newServer );
1075      sNumFakeServers++;
1076   }
1077
1078   gServerBrowserDirty = true;
1079}
1080#endif // DEBUG
1081
1082//-----------------------------------------------------------------------------
1083
1084static void sendPacket( U8 pType, const NetAddress* addr, U32 key, U32 session, U8 flags )
1085{
1086   BitStream *out = BitStream::getPacketStream();
1087   out->clearStringBuffer();
1088   out->write( pType );
1089   out->write( flags );
1090   out->write( U32( ( session << 16 ) | ( key & 0xFFFF ) ) );
1091
1092   BitStream::sendPacketStream(addr);
1093}
1094
1095//-----------------------------------------------------------------------------
1096
1097static void writeCString( BitStream* stream, const char* string )
1098{
1099   U8 strLen = ( string != NULL ) ? dStrlen( string ) : 0;
1100   stream->write( strLen );
1101   for ( U32 i = 0; i < strLen; i++ )
1102      stream->write( U8( string[i] ) );
1103}
1104
1105//-----------------------------------------------------------------------------
1106
1107static void readCString( BitStream* stream, char* buffer )
1108{
1109   U32 i;
1110   U8 strLen;
1111   stream->read( &strLen );
1112   for ( i = 0; i < strLen; i++ )
1113   {
1114      U8* ptr = (U8*) buffer;
1115      stream->read( &ptr[i] );
1116   }
1117   buffer[i] = 0;
1118}
1119
1120//-----------------------------------------------------------------------------
1121
1122static void writeLongCString( BitStream* stream, const char* string )
1123{
1124   U16 strLen = ( string != NULL ) ? dStrlen( string ) : 0;
1125   stream->write( strLen );
1126   for ( U32 i = 0; i < strLen; i++ )
1127      stream->write( U8( string[i] ) );
1128}
1129
1130//-----------------------------------------------------------------------------
1131
1132static void readLongCString( BitStream* stream, char* buffer )
1133{
1134   U32 i;
1135   U16 strLen;
1136   stream->read( &strLen );
1137   for ( i = 0; i < strLen; i++ )
1138   {
1139      U8* ptr = (U8*) buffer;
1140      stream->read( &ptr[i] );
1141   }
1142   buffer[i] = 0;
1143}
1144
1145//-----------------------------------------------------------------------------
1146// Event processing
1147//-----------------------------------------------------------------------------
1148
1149//-----------------------------------------------------------------------------
1150
1151static void processMasterServerQuery( U32 session )
1152{
1153   if ( session != gPingSession || !sgServerQueryActive )
1154      return;
1155
1156   if ( !gGotFirstListPacket )
1157   {
1158      bool keepGoing = true;
1159      U32 time = Platform::getVirtualMilliseconds();
1160      char addressString[256];
1161
1162      if ( gMasterServerPing.time + gMasterServerTimeout < time )
1163      {
1164         Net::addressToString( &gMasterServerPing.address, addressString );
1165         if ( !gMasterServerPing.tryCount )
1166         {
1167            // The query timed out.
1168            Con::printf( "Server list request to %s timed out.", addressString );
1169
1170            // Remove this server from the list:
1171            for ( U32 i = 0; i < gMasterServerList.size(); i++ )
1172            {
1173               if ( Net::compareAddresses( &gMasterServerList[i].address, &gMasterServerPing.address ) )
1174               {
1175                  gMasterServerList.erase( i );
1176                  break;
1177               }
1178            }
1179
1180            // Pick a new master server to try:
1181            keepGoing = pickMasterServer();
1182            if ( keepGoing )
1183            {
1184               Con::executef( "onServerQueryStatus", "update", "Switching master servers...", "0" );
1185               Net::addressToString( &gMasterServerPing.address, addressString );
1186            }
1187         }
1188
1189         if ( keepGoing )
1190         {
1191            gMasterServerPing.tryCount--;
1192            gMasterServerPing.time = time;
1193            gMasterServerPing.key = gKey++;
1194
1195            // Send a request to the master server for the server list:
1196            BitStream *out = BitStream::getPacketStream();
1197            out->clearStringBuffer();
1198
1199            out->write( U8( NetInterface::MasterServerListRequest ) );
1200            
1201            out->write( U8( sActiveFilter.queryFlags) );
1202            out->write( ( gMasterServerPing.session << 16 ) | ( gMasterServerPing.key & 0xFFFF ) );
1203            out->write( U8( 255 ) );
1204
1205            writeCString( out, sActiveFilter.gameType );
1206            writeCString( out, sActiveFilter.missionType );
1207            out->write( sActiveFilter.minPlayers );
1208            out->write( sActiveFilter.maxPlayers );
1209            out->write( sActiveFilter.regionMask );
1210            U32 version = ( sActiveFilter.filterFlags & ServerFilter::CurrentVersion ) ? getVersionNumber() : 0;
1211            out->write( version );
1212            out->write( sActiveFilter.filterFlags );
1213            out->write( sActiveFilter.maxBots );
1214            out->write( sActiveFilter.minCPU );
1215            out->write( sActiveFilter.buddyCount );
1216            for ( U32 i = 0; i < sActiveFilter.buddyCount; i++ )
1217               out->write( sActiveFilter.buddyList[i] );
1218
1219            BitStream::sendPacketStream( &gMasterServerPing.address );
1220
1221            Con::printf( "Requesting the server list from master server %s (%d tries left)...", addressString, gMasterServerPing.tryCount );
1222            if ( gMasterServerPing.tryCount < gMasterServerRetryCount - 1 )
1223               Con::executef( "onServerQueryStatus", "update", "Retrying the master server...", "0" );
1224         }
1225      }
1226
1227      if ( keepGoing )
1228      {
1229         // schedule another check:
1230         Sim::postEvent( Sim::getRootGroup(), new ProcessMasterQueryEvent( session ), Sim::getTargetTime() + 1 );
1231      }
1232      else
1233      {
1234         Con::errorf( "There are no more master servers to try!" );
1235         Con::executef( "onServerQueryStatus", "done", "No master servers found.", "0" );
1236      }
1237   }
1238}
1239
1240//-----------------------------------------------------------------------------
1241
1242static void processPingsAndQueries( U32 session, bool schedule )
1243{
1244   if( session != gPingSession )
1245      return;
1246
1247   U32 i = 0;
1248   U32 time = Platform::getVirtualMilliseconds();
1249   char addressString[256];
1250   U8 flags = ServerFilter::OnlineQuery;
1251   bool waitingForMaster = ( sActiveFilter.type == ServerFilter::Normal ) && !gGotFirstListPacket && sgServerQueryActive;
1252
1253   for ( i = 0; i < gPingList.size() && i < gMaxConcurrentPings; )
1254   {
1255      Ping &p = gPingList[i];
1256
1257      if ( p.time + gPingTimeout < time )
1258      {
1259         if ( !p.tryCount )
1260         {
1261            // it's timed out.
1262            if (!p.broadcast)
1263            {
1264               Net::addressToString( &p.address, addressString );
1265               Con::printf( "Ping to server %s timed out.", addressString );
1266            }
1267
1268            // If server info is in list (favorite), set its status:
1269            ServerInfo* si = findServerInfo( &p.address );
1270            if ( si )
1271            {
1272               si->status = ServerInfo::Status_TimedOut;
1273               gServerBrowserDirty = true;
1274            }
1275
1276            gFinishedList.push_back( p.address );
1277            gPingList.erase( i );
1278
1279            if ( !waitingForMaster )
1280               updatePingProgress();
1281         }
1282         else
1283         {
1284            p.tryCount--;
1285            p.time = time;
1286            p.key = gKey++;
1287
1288            Net::addressToString( &p.address, addressString );
1289
1290            if (p.broadcast)
1291               Con::printf( "LAN server ping: %s...", addressString );
1292            else
1293               Con::printf( "Pinging Server %s (%d)...", addressString, p.tryCount );
1294            sendPacket( NetInterface::GamePingRequest, &p.address, p.key, p.session, flags );
1295            i++;
1296         }
1297      }
1298      else
1299         i++;
1300   }
1301
1302   if ( !gPingList.size() && !waitingForMaster )
1303   {
1304      // Start the query phase:
1305      for ( i = 0; i < gQueryList.size() && i < gMaxConcurrentQueries; )
1306      {
1307         Ping &p = gQueryList[i];
1308         if ( p.time + gPingTimeout < time )
1309         {
1310            ServerInfo* si = findServerInfo( &p.address );
1311            if ( !si )
1312            {
1313               // Server info not found, so remove the query:
1314               gQueryList.erase( i );
1315               gServerBrowserDirty = true;
1316               continue;
1317            }
1318
1319            Net::addressToString( &p.address, addressString );
1320            if ( !p.tryCount )
1321            {
1322               Con::printf( "Query to server %s timed out.", addressString );
1323               si->status = ServerInfo::Status_TimedOut;
1324               gQueryList.erase( i );
1325               gServerBrowserDirty = true;
1326            }
1327            else
1328            {
1329               p.tryCount--;
1330               p.time = time;
1331               p.key = gKey++;
1332
1333               Con::printf( "Querying Server %s (%d)...", addressString, p.tryCount );
1334               sendPacket( NetInterface::GameInfoRequest, &p.address, p.key, p.session, flags );
1335               if ( !si->isQuerying() )
1336               {
1337                  si->status |= ServerInfo::Status_Querying;
1338                  gServerBrowserDirty = true;
1339               }
1340               i++;
1341            }
1342         }
1343         else
1344            i++;
1345      }
1346   }
1347
1348   if ( gPingList.size() || gQueryList.size() || waitingForMaster )
1349   {
1350      // The LAN query function doesn't always want to schedule
1351      // the next ping.
1352      if (schedule)
1353         Sim::postEvent( Sim::getRootGroup(), new ProcessPingEvent( session ), Sim::getTargetTime() + 1 );
1354   }
1355   else
1356   {
1357      // All done!
1358      char msg[64];
1359      U32 foundCount = gServerList.size();
1360      if ( foundCount == 0 )
1361         dStrcpy( msg, "No servers found.", 64 );
1362      else if ( foundCount == 1 )
1363         dStrcpy( msg, "One server found.", 64 );
1364      else
1365         dSprintf( msg, sizeof( msg ), "%d servers found.", foundCount );
1366
1367      Con::executef( "onServerQueryStatus", "done", (const char*)msg, "1");
1368   }
1369}
1370
1371//-----------------------------------------------------------------------------
1372
1373static void processServerListPackets( U32 session )
1374{
1375   if ( session != gPingSession || !sgServerQueryActive )
1376      return;
1377
1378   U32 currentTime = Platform::getVirtualMilliseconds();
1379
1380   // Loop through the packet status list and resend packet requests where necessary:
1381   for ( U32 i = 0; i < gPacketStatusList.size(); i++ )
1382   {
1383      PacketStatus &p = gPacketStatusList[i];
1384      if ( p.time + gPacketTimeout < currentTime )
1385      {
1386         if ( !p.tryCount )
1387         {
1388            // Packet timed out :(
1389            Con::printf( "Server list packet #%d timed out.", p.getIndex() + 1 );
1390            gPacketStatusList.erase( i );
1391         }
1392         else
1393         {
1394            // Try again...
1395            Con::printf( "Rerequesting server list packet #%d...", p.getIndex() + 1 );
1396            p.tryCount--;
1397            p.time = currentTime;
1398            p.key = gKey++;
1399
1400            BitStream *out = BitStream::getPacketStream();
1401            bool extendedPacket = (sActiveFilter.queryFlags & ServerFilter::NewStyleResponse) != 0;
1402
1403            out->clearStringBuffer();
1404
1405            if ( extendedPacket )
1406               out->write( U8( NetInterface::MasterServerExtendedListRequest ) );
1407            else
1408               out->write( U8( NetInterface::MasterServerListRequest ) );
1409
1410            out->write( U8( sActiveFilter.queryFlags ) );   // flags
1411            out->write( ( session << 16) | ( p.key & 0xFFFF ) );
1412            
1413            if ( extendedPacket )
1414               out->write( p.getOldIndex() );  // packet index
1415            else
1416               out->write( p.getIndex() );  // packet index
1417
1418            out->write( U8( 0 ) );  // game type
1419            out->write( U8( 0 ) );  // mission type
1420            out->write( U8( 0 ) );  // minPlayers
1421            out->write( U8( 0 ) );  // maxPlayers
1422            out->write( U32( 0 ) ); // region mask
1423            out->write( U32( 0 ) ); // version
1424            out->write( U8( 0 ) );  // filter flags
1425            out->write( U8( 0 ) );  // max bots
1426            out->write( U16( 0 ) ); // min CPU
1427            out->write( U8( 0 ) );  // buddy count
1428
1429            BitStream::sendPacketStream(&gMasterServerQueryAddress);
1430         }
1431      }
1432   }
1433
1434   if ( gPacketStatusList.size() )
1435      Sim::postEvent( Sim::getRootGroup(), new ProcessPacketEvent( session ), Sim::getCurrentTime() + 30 );
1436   else
1437      processPingsAndQueries( gPingSession );
1438}
1439
1440//-----------------------------------------------------------------------------
1441
1442static void processHeartbeat(U32 seq)
1443{
1444   if(seq != gHeartbeatSeq)
1445      return;
1446   sendHeartbeat( 0 );
1447   Sim::postEvent( Sim::getRootGroup(), new HeartbeatEvent(seq), Sim::getCurrentTime() + gHeartbeatInterval );
1448}
1449
1450//-----------------------------------------------------------------------------
1451
1452static void updatePingProgress()
1453{
1454   if ( !gPingList.size() )
1455   {
1456      updateQueryProgress();
1457      return;
1458   }
1459
1460   char msg[64];
1461   U32 pingsLeft = countPingRequests();
1462   dSprintf( msg, sizeof(msg),
1463      (!pingsLeft && gPingList.size())?
1464         "Waiting for lan servers...":
1465         "Pinging servers: %d left...",
1466      pingsLeft );
1467
1468   // Ping progress is 0 -> 0.5
1469   F32 progress = 0.0f;
1470   if ( gServerPingCount )
1471      progress = F32( gServerPingCount - pingsLeft ) / F32( gServerPingCount * 2 );
1472
1473   //Con::errorf( ConsoleLogEntry::General, "Ping progress - %d of %d left - progress = %.2f", pingsLeft, gServerPingCount, progress );
1474   Con::executef( "onServerQueryStatus", "ping", msg, Con::getFloatArg( progress ) );
1475}
1476
1477//-----------------------------------------------------------------------------
1478
1479static void updateQueryProgress()
1480{
1481   if ( gPingList.size() )
1482      return;
1483
1484   char msg[64];
1485   U32 queriesLeft = gQueryList.size();
1486   dSprintf( msg, sizeof( msg ), "Querying servers: %d left...", queriesLeft );
1487
1488   // Query progress is 0.5 -> 1
1489   F32 progress = 0.5f;
1490   if ( gServerQueryCount )
1491      progress += ( F32( gServerQueryCount - queriesLeft ) / F32( gServerQueryCount * 2 ) );
1492
1493   //Con::errorf( ConsoleLogEntry::General, "Query progress - %d of %d left - progress = %.2f", queriesLeft, gServerQueryCount, progress );
1494   Con::executef( "onServerQueryStatus", "query", msg, Con::getFloatArg( progress ) );
1495}
1496
1497
1498//-----------------------------------------------------------------------------
1499// Server packet handlers:
1500//-----------------------------------------------------------------------------
1501
1502//-----------------------------------------------------------------------------
1503
1504static void handleMasterServerGameTypesResponse( BitStream* stream, U32 /*key*/, U8 /*flags*/ )
1505{
1506   Con::printf( "Received game type list from the master server." );
1507
1508   U32 i;
1509   U8 temp;
1510   char stringBuf[256];
1511   stream->read( &temp );
1512   Con::executef("onClearGameTypes");
1513   for ( i = 0; i < U32( temp ); i++ )
1514   {
1515      readCString( stream, stringBuf );
1516      Con::executef("onAddGameType", stringBuf);
1517   }
1518
1519   stream->read( &temp );
1520   Con::executef("onClearMissionTypes");
1521   for ( i = 0; i < U32( temp ); i++ )
1522   {
1523      readCString( stream, stringBuf );
1524      Con::executef("onAddMissionType", stringBuf);
1525   }
1526}
1527
1528//-----------------------------------------------------------------------------
1529
1530static void handleMasterServerListResponse( BitStream* stream, U32 key, U8 /*flags*/ )
1531{
1532   U8 packetIndex, packetTotal;
1533   U32 i;
1534   U16 serverCount, port;
1535   U8 netNum[4];
1536   char addressBuffer[256];
1537   NetAddress addr;
1538
1539   stream->read( &packetIndex );
1540   // Validate the packet key:
1541   U32 packetKey = gMasterServerPing.key;
1542   if ( gGotFirstListPacket )
1543   {
1544      for ( i = 0; i < gPacketStatusList.size(); i++ )
1545      {
1546         if ( gPacketStatusList[i].index == packetIndex )
1547         {
1548            packetKey = gPacketStatusList[i].key;
1549            break;
1550         }
1551      }
1552   }
1553
1554   U32 testKey = ( gPingSession << 16 ) | ( packetKey & 0xFFFF );
1555   if ( testKey != key )
1556      return;
1557
1558   stream->read( &packetTotal );
1559   stream->read( &serverCount );
1560
1561   Con::printf( "Received server list packet %d of %d from the master server (%d servers).", ( packetIndex + 1 ), packetTotal, serverCount );
1562
1563   // Enter all of the servers in this packet into the ping list:
1564   for ( i = 0; i < serverCount; i++ )
1565   {
1566      stream->read( &netNum[0] );
1567      stream->read( &netNum[1] );
1568      stream->read( &netNum[2] );
1569      stream->read( &netNum[3] );
1570      stream->read( &port );
1571
1572      dSprintf( addressBuffer, sizeof( addressBuffer ), "IP:%d.%d.%d.%d:%d", netNum[0], netNum[1], netNum[2], netNum[3], port );
1573      Net::stringToAddress( addressBuffer, &addr );
1574      pushPingRequest( &addr );
1575   }
1576
1577   // If this is the first list packet we have received, fill the packet status list
1578   // and start processing:
1579   if ( !gGotFirstListPacket )
1580   {
1581      gGotFirstListPacket = true;
1582      gMasterServerQueryAddress = gMasterServerPing.address;
1583      U32 currentTime = Platform::getVirtualMilliseconds();
1584      for ( i = 0; i < packetTotal; i++ )
1585      {
1586         if ( i != packetIndex )
1587         {
1588            PacketStatus p = PacketStatus( i, gMasterServerPing.key, currentTime );
1589            gPacketStatusList.push_back( p );
1590         }
1591      }
1592
1593      processServerListPackets( gPingSession );
1594   }
1595   else
1596   {
1597      // Remove the packet we just received from the status list:
1598      for ( i = 0; i < gPacketStatusList.size(); i++ )
1599      {
1600         if ( gPacketStatusList[i].index == packetIndex )
1601         {
1602            gPacketStatusList.erase( i );
1603            break;
1604         }
1605      }
1606   }
1607}
1608
1609//-----------------------------------------------------------------------------
1610
1611static void handleExtendedMasterServerListResponse(BitStream* stream, U32 key, U8 /*flags*/)
1612{
1613   U16 packetIndex, packetTotal;
1614   U32 i;
1615   U16 serverCount;
1616   NetAddress addr;
1617
1618   stream->read(&packetIndex);
1619   // Validate the packet key:
1620   U32 packetKey = gMasterServerPing.key;
1621   if (gGotFirstListPacket)
1622   {
1623      for (i = 0; i < gPacketStatusList.size(); i++)
1624      {
1625         if (gPacketStatusList[i].index == packetIndex)
1626         {
1627            packetKey = gPacketStatusList[i].key;
1628            break;
1629         }
1630      }
1631   }
1632
1633   U32 testKey = (gPingSession << 16) | (packetKey & 0xFFFF);
1634   if (testKey != key)
1635      return;
1636
1637   stream->read(&packetTotal);
1638   stream->read(&serverCount);
1639
1640   Con::printf("Received server list packet %d of %d from the master server (%d servers).", (packetIndex + 1), packetTotal, serverCount);
1641
1642   // Enter all of the servers in this packet into the ping list:
1643   for (i = 0; i < serverCount; i++)
1644   {
1645      U8 type;
1646      stream->read(&type);
1647      dMemset(&addr, '\0', sizeof(NetAddress));
1648
1649      if (type == 0)
1650      {
1651         // IPV4
1652         addr.type = NetAddress::IPAddress;
1653         stream->read(4, &addr.address.ipv4.netNum[0]);
1654         stream->read(&addr.port);
1655      }
1656      else
1657      {
1658         // IPV6
1659         addr.type = NetAddress::IPV6Address;
1660         stream->read(16, &addr.address.ipv6.netNum[0]);
1661         stream->read(&addr.port);
1662      }
1663
1664      pushPingRequest(&addr);
1665   }
1666
1667   // If this is the first list packet we have received, fill the packet status list
1668   // and start processing:
1669   if (!gGotFirstListPacket)
1670   {
1671      gGotFirstListPacket = true;
1672      gMasterServerQueryAddress = gMasterServerPing.address;
1673      U32 currentTime = Platform::getVirtualMilliseconds();
1674      for (i = 0; i < packetTotal; i++)
1675      {
1676         if (i != packetIndex)
1677         {
1678            PacketStatus p = PacketStatus(i, gMasterServerPing.key, currentTime);
1679            gPacketStatusList.push_back(p);
1680         }
1681      }
1682
1683      processServerListPackets(gPingSession);
1684   }
1685   else
1686   {
1687      // Remove the packet we just received from the status list:
1688      for (i = 0; i < gPacketStatusList.size(); i++)
1689      {
1690         if (gPacketStatusList[i].index == packetIndex)
1691         {
1692            gPacketStatusList.erase(i);
1693            break;
1694         }
1695      }
1696   }
1697}
1698
1699//-----------------------------------------------------------------------------
1700
1701static void handleGameMasterInfoRequest( const NetAddress* address, U32 key, U8 flags )
1702{
1703   if ( GNet->doesAllowConnections() )
1704   {
1705      U8 temp8;
1706      U32 temp32;
1707
1708      char netString[256];
1709      Net::addressToString(address, netString);
1710
1711      Vector<MasterInfo> *masterList = getMasterServerList();
1712      const NetAddress *masterAddr;
1713      bool fromMaster = false;
1714      for(U32 i = 0; i < masterList->size(); i++)
1715      {
1716         masterAddr = &(*masterList)[i].address;
1717         if (masterAddr->isSameAddress(*address))
1718         {
1719            fromMaster = true;
1720            break;
1721         }
1722      }
1723
1724      Con::printf( "Received info request from %s [%s].", fromMaster?"a master server":"a machine", netString);
1725
1726      BitStream *out = BitStream::getPacketStream();
1727      out->clearStringBuffer();
1728
1729      out->write( U8( NetInterface::GameMasterInfoResponse ) );
1730      out->write( U8( flags ) );
1731      out->write( key );
1732
1733      writeCString( out, Con::getVariable( "Server::GameType" ) );
1734      writeCString( out, Con::getVariable( "Server::MissionType" ) );
1735      temp8 = U8( Con::getIntVariable( "pref::Server::MaxPlayers" ) );
1736      out->write( temp8 );
1737      temp32 = Con::getIntVariable( "pref::Server::RegionMask" );
1738      out->write( temp32 );
1739      temp32 = getVersionNumber();
1740      out->write( temp32 );
1741      temp8 = 0;
1742#if defined(TORQUE_OS_LINUX) || defined(TORQUE_OS_OPENBSD)
1743      temp8 |= ServerInfo::Status_Linux;
1744#endif
1745
1746      if ( Con::getBoolVariable( "Server::Dedicated" ) )
1747         temp8 |= ServerInfo::Status_Dedicated;
1748      if ( dStrlen( Con::getVariable( "pref::Server::Password" ) ) > 0 )
1749         temp8 |= ServerInfo::Status_Passworded;
1750      out->write( temp8 );
1751      temp8 = U8( Con::getIntVariable( "Server::BotCount" ) );
1752      out->write( temp8 );
1753      out->write( Platform::SystemInfo.processor.mhz );
1754
1755      U8 playerCount = U8( Con::getIntVariable( "Server::PlayerCount" ) );
1756      out->write( playerCount );
1757
1758      const char* guidList = Con::getVariable( "Server::GuidList" );
1759      dsize_t bufLen = dStrlen(guidList) + 1;
1760      char* buf = new char[bufLen];
1761      dStrcpy( buf, guidList, bufLen );
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 ( String::compare( 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      dsize_t bufLen = dStrlen(buf) + 1;
1956      si->name = (char*) dMalloc(bufLen);
1957      dStrcpy( si->name, buf, bufLen );
1958   }
1959
1960   // Set the server up to be queried:
1961   gFinishedList.push_back( *address );
1962   p.key = 0;
1963   p.time = 0;
1964   p.tryCount = gQueryRetryCount;
1965   gQueryList.push_back( p );
1966   gServerQueryCount++;
1967   gPingList.erase( index );
1968   if ( !waitingForMaster )
1969      updatePingProgress();
1970
1971   // Update the server browser gui!
1972   gServerBrowserDirty = true;
1973}
1974
1975//-----------------------------------------------------------------------------
1976
1977static void handleGameInfoRequest( const NetAddress* address, U32 key, U8 flags )
1978{
1979   // Do not respond unless there is a server running:
1980   if ( GNet->doesAllowConnections() )
1981   {
1982      // Do not respond to offline queries if this is an online server:
1983      if ( flags & ServerFilter::OfflineQuery )
1984         return;
1985
1986      bool compressStrings = !( flags & ServerFilter::NoStringCompress );
1987      BitStream *out = BitStream::getPacketStream();
1988      out->clearStringBuffer();
1989
1990      out->write( U8( NetInterface::GameInfoResponse ) );
1991      out->write( flags );
1992      out->write( key );
1993
1994      if ( compressStrings ) {
1995         out->writeString( Con::getVariable( "Server::GameType" ) );
1996         out->writeString( Con::getVariable( "Server::MissionType" ) );
1997         out->writeString( Con::getVariable( "Server::MissionName" ) );
1998      }
1999      else {
2000         writeCString( out, Con::getVariable( "Server::GameType" ) );
2001         writeCString( out, Con::getVariable( "Server::MissionType" ) );
2002         writeCString( out, Con::getVariable( "Server::MissionName" ) );
2003      }
2004
2005      U8 status = 0;
2006#if defined(TORQUE_OS_LINUX) || defined(TORQUE_OS_OPENBSD)
2007      status |= ServerInfo::Status_Linux;
2008#endif
2009
2010      if ( Con::getBoolVariable( "Server::Dedicated" ) )
2011         status |= ServerInfo::Status_Dedicated;
2012      if ( dStrlen( Con::getVariable( "pref::Server::Password" ) ) )
2013         status |= ServerInfo::Status_Passworded;
2014      out->write( status );
2015
2016      out->write( U8( Con::getIntVariable( "Server::PlayerCount" ) ) );
2017      out->write( U8( Con::getIntVariable( "pref::Server::MaxPlayers" ) ) );
2018      out->write( U8( Con::getIntVariable( "Server::BotCount" ) ) );
2019      out->write( U16( Platform::SystemInfo.processor.mhz ) );
2020      if ( compressStrings )
2021         out->writeString( Con::getVariable( "pref::Server::Info" ) );
2022      else
2023         writeCString( out, Con::getVariable( "pref::Server::Info" ) );
2024      writeLongCString( out, Con::evaluate( "onServerInfoQuery();" ) );
2025
2026      BitStream::sendPacketStream(address);
2027   }
2028}
2029
2030//-----------------------------------------------------------------------------
2031
2032static void handleGameInfoResponse( const NetAddress* address, BitStream* stream, U32 /*key*/, U8 /*flags*/ )
2033{
2034   if ( !gQueryList.size() )
2035      return;
2036
2037   S32 index = findPingEntry( gQueryList, address );
2038   if ( index == -1 )
2039      return;
2040
2041   // Remove the server from the query list since it has been so kind as to respond:
2042   gQueryList.erase( index );
2043   updateQueryProgress();
2044   ServerInfo *si = findServerInfo( address );
2045   if ( !si )
2046      return;
2047
2048   bool isUpdate = si->isUpdating();
2049   bool applyFilter = !isUpdate && ( sActiveFilter.type == ServerFilter::Normal );
2050   char addrString[256];
2051   Net::addressToString( address, addrString );
2052
2053   // Get the rules set:
2054   char stringBuf[2048];   // Who knows how big this should be?
2055   stream->readString( stringBuf );
2056   if ( !si->gameType || dStricmp( si->gameType, stringBuf ) != 0 )
2057   {
2058      dsize_t gameTypeLen = dStrlen(stringBuf) + 1;
2059      si->gameType = (char*) dRealloc( (void*) si->gameType, gameTypeLen );
2060      dStrcpy( si->gameType, stringBuf, gameTypeLen );
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 || String::compare( si->missionType, stringBuf ) != 0 )
2075   {
2076      dsize_t missionTypeLen = dStrlen(stringBuf) + 1;
2077      si->missionType = (char*) dRealloc( (void*) si->missionType, missionTypeLen );
2078      dStrcpy( si->missionType, stringBuf, missionTypeLen );
2079
2080      // Test against the active filter:
2081      if ( applyFilter && dStricmp( sActiveFilter.missionType, "any" ) != 0
2082        && dStricmp( si->missionType, sActiveFilter.missionType ) != 0 )
2083      {
2084         Con::printf( "Server %s filtered out by mission type. (%s:%s)", addrString, sActiveFilter.missionType, si->missionType );
2085         removeServerInfo( address );
2086         return;
2087      }
2088   }
2089
2090   // Get the mission name:
2091   stream->readString( stringBuf );
2092   // Clip the file extension off:
2093   char* temp = dStrstr( static_cast<char*>( stringBuf ), const_cast<char*>( ".mis" ) );
2094   if ( temp )
2095      *temp = '\0';
2096   if ( !si->missionName || String::compare( si->missionName, stringBuf ) != 0 )
2097   {
2098      dsize_t missionNameLen = dStrlen(stringBuf) + 1;
2099      si->missionName = (char*) dRealloc( (void*) si->missionName, missionNameLen );
2100      dStrcpy( si->missionName, stringBuf, missionNameLen );
2101   }
2102
2103   // Get the server status:
2104   U8 temp_U8;
2105   stream->read( &temp_U8 );
2106   si->status = temp_U8;
2107
2108   // Filter by the flags:
2109   if ( applyFilter )
2110   {
2111      if ( sActiveFilter.filterFlags & ServerFilter::Dedicated && !si->isDedicated() )
2112      {
2113         Con::printf( "Server %s filtered out by dedicated flag.", addrString );
2114         removeServerInfo( address );
2115         return;
2116      }
2117
2118      if ( sActiveFilter.filterFlags & ServerFilter::NotPassworded && si->isPassworded() )
2119      {
2120         Con::printf( "Server %s filtered out by no-password flag.", addrString );
2121         removeServerInfo( address );
2122         return;
2123      }
2124
2125   }
2126   si->status.set( ServerInfo::Status_Responded );
2127
2128   // Get the player count:
2129   stream->read( &si->numPlayers );
2130
2131   // Test player count against active filter:
2132   if ( applyFilter && ( si->numPlayers < sActiveFilter.minPlayers || si->numPlayers > sActiveFilter.maxPlayers ) )
2133   {
2134      Con::printf( "Server %s filtered out by player count.", addrString );
2135      removeServerInfo( address );
2136      return;
2137   }
2138
2139   // Get the max players and bot count:
2140   stream->read( &si->maxPlayers );
2141   stream->read( &si->numBots );
2142
2143   // Test bot count against active filter:
2144   if ( applyFilter && ( si->numBots > sActiveFilter.maxBots ) )
2145   {
2146      Con::printf( "Server %s filtered out by maximum bot count.", addrString );
2147      removeServerInfo( address );
2148      return;
2149   }
2150
2151   // Get the CPU speed;
2152   U16 temp_U16;
2153   stream->read( &temp_U16 );
2154   si->cpuSpeed = temp_U16;
2155
2156   // Test CPU speed against active filter:
2157   if ( applyFilter && ( si->cpuSpeed < sActiveFilter.minCPU ) )
2158   {
2159      Con::printf( "Server %s filtered out by minimum CPU speed.", addrString );
2160      removeServerInfo( address );
2161      return;
2162   }
2163
2164   // Get the server info:
2165   stream->readString( stringBuf );
2166   if ( !si->statusString || ( isUpdate && String::compare( si->statusString, stringBuf ) != 0 ) )
2167   {
2168      dsize_t infoLen = dStrlen(stringBuf) + 1;
2169      si->infoString = (char*) dRealloc( (void*) si->infoString, infoLen );
2170      dStrcpy( si->infoString, stringBuf, infoLen );
2171   }
2172
2173   // Get the content string:
2174   readLongCString( stream, stringBuf );
2175   if ( !si->statusString || ( isUpdate && String::compare( si->statusString, stringBuf ) != 0 ) )
2176   {
2177      dsize_t statusLen = dStrlen(stringBuf) + 1;
2178      si->statusString = (char*) dRealloc( (void*) si->statusString, statusLen );
2179      dStrcpy( si->statusString, stringBuf, statusLen );
2180   }
2181
2182   // Update the server browser gui!
2183   gServerBrowserDirty = true;
2184}
2185
2186//-----------------------------------------------------------------------------
2187// Packet Dispatch
2188
2189void DemoNetInterface::handleInfoPacket( const NetAddress* address, U8 packetType, BitStream* stream )
2190{
2191   U8 flags;
2192   U32 key;
2193
2194   stream->read( &flags );
2195   stream->read( &key );
2196   stream->clearStringBuffer();
2197
2198   switch( packetType )
2199   {
2200      case GamePingRequest:
2201         handleGamePingRequest( address, key, flags );
2202         break;
2203
2204      case GamePingResponse:
2205         handleGamePingResponse( address, stream, key, flags );
2206         break;
2207
2208      case GameInfoRequest:
2209         handleGameInfoRequest( address, key, flags );
2210         break;
2211
2212      case GameInfoResponse:
2213         handleGameInfoResponse( address, stream, key, flags );
2214         break;
2215
2216      case MasterServerGameTypesResponse:
2217         handleMasterServerGameTypesResponse( stream, key, flags );
2218         break;
2219
2220      case MasterServerListResponse:
2221         handleMasterServerListResponse( stream, key, flags );
2222         break;
2223
2224      case GameMasterInfoRequest:
2225         handleGameMasterInfoRequest( address, key, flags );
2226         break;
2227
2228      case MasterServerExtendedListResponse:
2229         handleExtendedMasterServerListResponse(stream, key, flags);
2230         break;
2231   }
2232}
2233
2234
2235ConsoleFunctionGroupEnd( ServerQuery );
2236