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