serverQuery.cpp
Engine/source/app/net/serverQuery.cpp
Classes:
class
class
class
class
class
class
class
class
Public Variables
Vector< NetAddress >
gFinishedList (__FILE__, __LINE__)
bool
Vector< MasterInfo >
gMasterServerList (__FILE__, __LINE__)
gPacketStatusList (__FILE__, __LINE__)
gQueryList (__FILE__, __LINE__)
bool
Vector< ServerInfo >
gServerList (__FILE__, __LINE__)
bool
const char *
Public Functions
bool
addressFinished(const NetAddress * addr)
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 )
DefineEngineFunction(cancelServerQuery , void , () , "cancelServerQuery();" )
DefineEngineFunction(getServerCount , int , () , "getServerCount();" )
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)
Vector< MasterInfo > *
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 )
bool
processMasterServerQuery(U32 session)
processPingsAndQueries(U32 session, bool schedule)
processServerListPackets(U32 session)
pushPingBroadcast(const NetAddress * addr)
pushPingRequest(const NetAddress * addr)
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)
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:",®ion); 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