serverQuery.cc 75 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. //-----------------------------------------------------------------------------
  23. // Server Query States:
  24. // 1: Master server query status -> wait for master response
  25. // 2: Master server packet status -> wait for master packets to arrive
  26. // 3: Server ping status -> wait for servers to respond to pings
  27. // 4: Server query status -> wait for servers to respond to queries
  28. // 5: Done
  29. // Master Server Packets:
  30. // Header
  31. // message Message id
  32. // flags Query flags
  33. // sequenceNumber Packet sequence id
  34. // Server Query Filter Packet
  35. // packetIndex Request specific page # (rest empty)
  36. // gameType Game type string
  37. // missionType Mission type string
  38. // minPlayers At least this many players
  39. // maxPlayers No more than this many
  40. // regions Region mask, 0 = all
  41. // version Server version, 0 = any
  42. // filterFlags Server flags (dedicated, etc), 0 = any
  43. // maxBots No more than maxBots
  44. // minCPUSpeed At least this fast
  45. // playerCount Buddy list search
  46. // playerList[playerCount]
  47. // Master Server Info Packet
  48. // gameType Game type string
  49. // missionType Mission type string
  50. // maxPlayers Max allowed
  51. // regions Region mask
  52. // version Server version #
  53. // infoFlags Server flags (dedicated, etc)
  54. // numBots Current bot count
  55. // CPUSpeed Server CPU speed
  56. // playerCount Current player count
  57. // playerList[playerCount]
  58. // Game Info Query Packet
  59. // gameType Game type string
  60. // missionType Mission type string
  61. // missionName You get one guess...
  62. // satusFlags Dedicated, etc.
  63. // playerCount Current player count
  64. // maxPlayers Max allowed
  65. // numBots Current bot count
  66. // CPUSpeed Server CPU speed
  67. // statusString Server info message
  68. // statusString Server status message
  69. // Accessed Environment Vars
  70. // Server::MissionType
  71. // Server::MissionName
  72. // Server::GameType
  73. // Server::ServerType
  74. // Server::PlayerCount
  75. // Server::BotCount
  76. // Server::GuidList[playerCount]
  77. // Server::Dedicated
  78. // Server::Status
  79. // Pref::Server::Name
  80. // Pref::Server::Password
  81. // Pref::Server::Info
  82. // Pref::Server::MaxPlayers
  83. // Pref::Server::RegionMask
  84. // Pref::Net::RegionMask
  85. // Pref::Client::Master[n]
  86. // Pref::Client::ServerFavoriteCount
  87. // Pref::Client::ServerFavorite[ServerFavoriteCount]
  88. //-----------------------------------------------------------------------------
  89. #include "network/serverQuery.h"
  90. #include "platform/platform.h"
  91. #include "platform/event.h"
  92. #include "network/connectionProtocol.h"
  93. #include "collection/vector.h"
  94. #include "io/resource/resourceManager.h"
  95. #include "io/bitStream.h"
  96. #include "console/console.h"
  97. #include "sim/simBase.h"
  98. #include "game/version.h"
  99. #include "game/gameConnection.h"
  100. // This is basically the server query protocol version now:
  101. static const char* versionString = "VER1";
  102. Vector<ServerInfo> gServerList(__FILE__, __LINE__);
  103. static Vector<MasterInfo> gMasterServerList(__FILE__, __LINE__);
  104. static Vector<NetAddress> gFinishedList(__FILE__, __LINE__); // timed out servers and finished servers go here
  105. NetAddress gMasterServerQueryAddress;
  106. bool gServerBrowserDirty = false;
  107. static const U32 gHeartbeatInterval = 120000;
  108. static const S32 gMasterServerRetryCount = 3;
  109. static const S32 gMasterServerTimeout = 2000;
  110. static const S32 gPacketRetryCount = 4;
  111. static const S32 gPacketTimeout = 1000;
  112. static const S32 gMaxConcurrentPings = 10;
  113. static const S32 gMaxConcurrentQueries = 2;
  114. static const S32 gPingRetryCount = 4;
  115. static const S32 gPingTimeout = 800;
  116. static const S32 gQueryRetryCount = 4;
  117. static const S32 gQueryTimeout = 1000;
  118. // State variables:
  119. static bool sgServerQueryActive = false;
  120. static S32 gPingSession = 0;
  121. static S32 gKey = 0;
  122. static bool gGotFirstListPacket = false;
  123. // Variables used for the interface:
  124. static U32 gServerPingCount = 0;
  125. static U32 gServerQueryCount = 0;
  126. static U32 gHeartbeatSeq = 0;
  127. ConsoleFunctionGroupBegin( ServerQuery, "Functions which allow you to query the LAN or a master server for online games.");
  128. //-----------------------------------------------------------------------------
  129. struct Ping
  130. {
  131. NetAddress address;
  132. S32 session;
  133. S32 key;
  134. U32 time;
  135. U32 tryCount;
  136. bool broadcast;
  137. };
  138. static Ping gMasterServerPing;
  139. static Vector<Ping> gPingList(__FILE__, __LINE__);
  140. static Vector<Ping> gQueryList(__FILE__, __LINE__);
  141. //-----------------------------------------------------------------------------
  142. struct PacketStatus
  143. {
  144. U8 index;
  145. S32 key;
  146. U32 time;
  147. U32 tryCount;
  148. PacketStatus( U8 _index, S32 _key, U32 _time )
  149. {
  150. index = _index;
  151. key = _key;
  152. time = _time;
  153. tryCount = gPacketRetryCount;
  154. }
  155. };
  156. static Vector<PacketStatus> gPacketStatusList(__FILE__, __LINE__);
  157. //-----------------------------------------------------------------------------
  158. struct ServerFilter
  159. {
  160. enum Type
  161. {
  162. Normal = 0,
  163. Buddy = 1,
  164. Offline = 2,
  165. Favorites = 3,
  166. };
  167. Type type;
  168. char* gameType;
  169. char* missionType;
  170. enum // Query Flags
  171. {
  172. OnlineQuery = 0, // Authenticated with master
  173. OfflineQuery = BIT(0), // On our own
  174. NoStringCompress = BIT(1),
  175. };
  176. enum // Filter flags:
  177. {
  178. Dedicated = BIT(0),
  179. NotPassworded = BIT(1),
  180. Linux = BIT(2),
  181. CurrentVersion = BIT(7),
  182. };
  183. U8 queryFlags;
  184. U8 minPlayers;
  185. U8 maxPlayers;
  186. U8 maxBots;
  187. U32 regionMask;
  188. U32 maxPing;
  189. U8 filterFlags;
  190. U16 minCPU;
  191. U8 buddyCount;
  192. U32* buddyList;
  193. ServerFilter()
  194. {
  195. queryFlags = 0;
  196. gameType = NULL;
  197. missionType = NULL;
  198. minPlayers = 0;
  199. maxPlayers = 255;
  200. maxBots = 16;
  201. regionMask = 0xFFFFFFFF;
  202. maxPing = 0;
  203. filterFlags = 0;
  204. minCPU = 0;
  205. buddyCount = 0;
  206. buddyList = NULL;
  207. }
  208. ~ServerFilter()
  209. {
  210. if ( gameType )
  211. dFree( gameType );
  212. if ( missionType )
  213. dFree( missionType );
  214. if ( buddyList )
  215. dFree( buddyList );
  216. }
  217. };
  218. static ServerFilter sActiveFilter;
  219. //-----------------------------------------------------------------------------
  220. // Forward function declarations:
  221. //-----------------------------------------------------------------------------
  222. static void pushPingRequest( const NetAddress *addr );
  223. static void pushPingBroadcast( const NetAddress *addr );
  224. static void pushServerFavorites();
  225. static bool pickMasterServer();
  226. static S32 findPingEntry( Vector<Ping> &v, const NetAddress* addr );
  227. static bool addressFinished( const NetAddress* addr );
  228. static ServerInfo* findServerInfo( const NetAddress* addr );
  229. static ServerInfo* findOrCreateServerInfo( const NetAddress* addr );
  230. static void removeServerInfo( const NetAddress* addr );
  231. static void sendPacket( U8 pType, const NetAddress* addr, U32 key, U32 session, U8 flags );
  232. static void writeCString( BitStream* stream, const char* string );
  233. static void readCString( BitStream* stream, char* buffer );
  234. static void writeLongCString( BitStream* stream, const char* string );
  235. static void readLongCString( BitStream* stream, char* buffer );
  236. static void processMasterServerQuery( U32 session );
  237. static void processPingsAndQueries( U32 session, bool schedule = true);
  238. static void processServerListPackets( U32 session );
  239. static void processHeartbeat(U32);
  240. static void updatePingProgress();
  241. static void updateQueryProgress();
  242. Vector<MasterInfo>* getMasterServerList();
  243. bool pickMasterServer();
  244. void clearServerList();
  245. //-----------------------------------------------------------------------------
  246. // Events
  247. //-----------------------------------------------------------------------------
  248. //----------------------------------------------------------------
  249. class ProcessMasterQueryEvent : public SimEvent
  250. {
  251. U32 session;
  252. public:
  253. ProcessMasterQueryEvent( U32 _session )
  254. {
  255. session = _session;
  256. }
  257. void process( SimObject *object )
  258. {
  259. processMasterServerQuery( session );
  260. }
  261. };
  262. //----------------------------------------------------------------
  263. class ProcessPingEvent : public SimEvent
  264. {
  265. U32 session;
  266. public:
  267. ProcessPingEvent( U32 _session )
  268. {
  269. session = _session;
  270. }
  271. void process( SimObject *object )
  272. {
  273. processPingsAndQueries( session );
  274. }
  275. };
  276. //----------------------------------------------------------------
  277. class ProcessPacketEvent : public SimEvent
  278. {
  279. U32 session;
  280. public:
  281. ProcessPacketEvent( U32 _session )
  282. {
  283. session = _session;
  284. }
  285. void process( SimObject *object )
  286. {
  287. processServerListPackets( session );
  288. }
  289. };
  290. //----------------------------------------------------------------
  291. class HeartbeatEvent : public SimEvent
  292. {
  293. U32 mSeq;
  294. public:
  295. HeartbeatEvent(U32 seq)
  296. {
  297. mSeq = seq;
  298. }
  299. void process( SimObject *object )
  300. {
  301. processHeartbeat(mSeq);
  302. }
  303. };
  304. //-----------------------------------------------------------------------------
  305. // Public query methods
  306. //-----------------------------------------------------------------------------
  307. //-----------------------------------------------------------------------------
  308. void queryLanServers(U32 port, U8 flags, const char* gameType, const char* missionType,
  309. U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, U16 minCPU,
  310. U8 filterFlags)
  311. {
  312. sgServerQueryActive = true;
  313. clearServerList();
  314. pushServerFavorites();
  315. sActiveFilter.type = ServerFilter::Offline;
  316. // Clear the filter:
  317. if ( !sActiveFilter.gameType || dStricmp( sActiveFilter.gameType, "Any" ) != 0 )
  318. {
  319. sActiveFilter.gameType = (char*) dRealloc( sActiveFilter.gameType, 4 );
  320. dStrcpy( sActiveFilter.gameType, "Any" );
  321. }
  322. if ( !sActiveFilter.missionType || dStricmp( sActiveFilter.missionType, "Any" ) != 0 )
  323. {
  324. sActiveFilter.missionType = (char*) dRealloc( sActiveFilter.missionType, 4 );
  325. dStrcpy( sActiveFilter.missionType, "Any" );
  326. }
  327. sActiveFilter.queryFlags = 0;
  328. sActiveFilter.minPlayers = minPlayers;
  329. sActiveFilter.maxPlayers = maxPlayers;
  330. sActiveFilter.maxBots = maxBots;
  331. sActiveFilter.regionMask = regionMask;
  332. sActiveFilter.maxPing = maxPing;
  333. sActiveFilter.minCPU = minCPU;
  334. sActiveFilter.filterFlags = filterFlags;
  335. NetAddress addr;
  336. char addrText[256];
  337. dSprintf( addrText, sizeof( addrText ), "IP:BROADCAST:%d", port );
  338. Net::stringToAddress( addrText, &addr );
  339. pushPingBroadcast( &addr );
  340. Con::executef( 4, "onServerQueryStatus", "start", "Querying LAN servers", "0");
  341. processPingsAndQueries( gPingSession );
  342. }
  343. //-----------------------------------------------------------------------------
  344. ConsoleFunction( queryLanServers, void, 12, 12, "( port , flags , gametype , missiontype , minplayers , maxplayers , maxbots , regionmask , maxping , mincpu , filterflags ) Use the queryLANServers function to establish whether any game servers of the required specification(s) are available on the local area network (LAN).\n"
  345. "@param port Look for any game servers advertising at this port. Set to 0 if you don't care what port the game server is using.\n"
  346. "@param flags Look for any game servers with these special flags set. Set to 0 for no flags.\n"
  347. "@param gametype Look for any game servers playing a game type that matches this string. Set to the NULL string to look for any game type.\n"
  348. "@param missiontype Look for any game servers playing a mission type that matches this string. Set to the NULL string to look for any mission type.\n"
  349. "@param minplayers Look for any game servers with this number of players or more. Set to 0 for no lower limit.\n"
  350. "@param maxplayers Look for any game servers with this number of players or fewer. Set to 0 for no upper limit.\n"
  351. "@param maxbots Look for any game servers with this number of AI controlled players or fewer. Set to 0 for no limit.\n"
  352. "@param regionmask Look for any master servers, on our master server list, in this region. Set to 0 to examine all regions.\n"
  353. "@param maxping Look for any game servers with a PING rate equal to or lower than this. Set to 0 for no upper PING limit.\n"
  354. "@param mincpu Look for any game servers with a CPU (clock speed) equal or greater than this. Set to 0 for no CPU (clock speed) limit.\n"
  355. "@param filterflags Look for any game servers with this game version number or higher. Set to 0 to find all versions.\n"
  356. "@return No return value.\n"
  357. "@sa getServerCount, queryMasterServer, setServerInfo, stopServerQuery")
  358. {
  359. U32 lanPort = dAtoi(argv[1]);
  360. U8 flags = dAtoi(argv[2]);
  361. // It's not a good idea to hold onto args, recursive calls to
  362. // console exec will trash them.
  363. char* gameType = dStrdup(argv[3]);
  364. char* missionType = dStrdup(argv[4]);
  365. U8 minPlayers = dAtoi(argv[5]);
  366. U8 maxPlayers = dAtoi(argv[6]);
  367. U8 maxBots = dAtoi(argv[7]);
  368. U32 regionMask = dAtoi(argv[8]);
  369. U32 maxPing = dAtoi(argv[9]);
  370. U16 minCPU = dAtoi(argv[10]);
  371. U8 filterFlags = dAtoi(argv[11]);
  372. queryLanServers(lanPort, flags, gameType, missionType, minPlayers, maxPlayers, maxBots,
  373. regionMask, maxPing, minCPU, filterFlags);
  374. dFree(gameType);
  375. dFree(missionType);
  376. }
  377. //-----------------------------------------------------------------------------
  378. void queryMasterGameTypes()
  379. {
  380. Vector<MasterInfo> *masterList = getMasterServerList();
  381. if ( masterList->size() != 0 )
  382. {
  383. U32 master = Sim::getCurrentTime() % masterList->size();
  384. // Send a request to the master server for the game types:
  385. Con::printf( "Requesting game types from the master server..." );
  386. sendPacket( NetInterface::MasterServerGameTypesRequest, &(*masterList)[master].address, gKey, gPingSession, 0 );
  387. }
  388. }
  389. //-----------------------------------------------------------------------------
  390. void queryMasterServer(U8 flags, const char* gameType, const char* missionType,
  391. U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing,
  392. U16 minCPU, U8 filterFlags, U8 buddyCount, U32* buddyList )
  393. {
  394. // Reset the list packet flag:
  395. gGotFirstListPacket = false;
  396. sgServerQueryActive = true;
  397. clearServerList();
  398. Con::executef( 4, "onServerQueryStatus", "start", "Querying master server", "0");
  399. if ( buddyCount == 0 )
  400. {
  401. sActiveFilter.type = ServerFilter::Normal;
  402. // Update the active filter:
  403. if ( !sActiveFilter.gameType || dStrcmp( sActiveFilter.gameType, gameType ) != 0 )
  404. {
  405. sActiveFilter.gameType = (char*) dRealloc( sActiveFilter.gameType, dStrlen( gameType ) + 1 );
  406. dStrcpy( sActiveFilter.gameType, gameType );
  407. }
  408. if ( !sActiveFilter.missionType || dStrcmp( sActiveFilter.missionType, missionType ) != 0 )
  409. {
  410. sActiveFilter.missionType = (char*) dRealloc( sActiveFilter.missionType, dStrlen( missionType ) + 1 );
  411. dStrcpy( sActiveFilter.missionType, missionType );
  412. }
  413. sActiveFilter.queryFlags = flags;
  414. sActiveFilter.minPlayers = minPlayers;
  415. sActiveFilter.maxPlayers = maxPlayers;
  416. sActiveFilter.maxBots = maxBots;
  417. sActiveFilter.regionMask = regionMask;
  418. sActiveFilter.maxPing = maxPing;
  419. sActiveFilter.minCPU = minCPU;
  420. sActiveFilter.filterFlags = filterFlags;
  421. sActiveFilter.buddyCount = buddyCount;
  422. dFree( sActiveFilter.buddyList );
  423. sActiveFilter.buddyList = NULL;
  424. }
  425. else
  426. {
  427. sActiveFilter.type = ServerFilter::Buddy;
  428. sActiveFilter.buddyCount = buddyCount;
  429. sActiveFilter.buddyList = (U32*) dRealloc( sActiveFilter.buddyList, buddyCount * 4 );
  430. dMemcpy( sActiveFilter.buddyList, buddyList, buddyCount * 4 );
  431. clearServerList();
  432. }
  433. // Pick a random master server from the list:
  434. gMasterServerList.clear();
  435. Vector<MasterInfo> *masterList = getMasterServerList();
  436. for ( U32 i = 0; i < (U32)masterList->size(); i++ )
  437. gMasterServerList.push_back( (*masterList)[i] );
  438. // Clear the master server ping:
  439. gMasterServerPing.time = 0;
  440. gMasterServerPing.tryCount = gMasterServerRetryCount;
  441. //Luma: Make sure to spit out to the script that the search is "done" now
  442. if ( !pickMasterServer() )
  443. {
  444. Con::errorf( "No master servers found!" );
  445. Con::executef( 4, "onServerQueryStatus", "done", "No master servers found.", "0" );
  446. }
  447. else
  448. processMasterServerQuery( gPingSession );
  449. }
  450. ConsoleFunction( queryMasterServer, void, 11, 11, "( flags , gametype , missiontype , minplayers , maxplayers , maxbots , regionmask , maxping , mincpu , filterflags ) Use the queryMasterServer function to query all master servers in the master server list and to establish if they are aware of any game servers that meet the specified requirements, as established by the arguments passed to this function.\n"
  451. "In order for this function to do anything, a list of master servers must have been previously specified. This list may contain one or more server addresses. A call to this function will search all servers in the list. To specify a list, simply create a set of array entries like this:\n$pref::Master[0] = \"2:192.168.123.15:28002\";\n$pref::Master[1] = \"2:192.168.123.2:28002\";\nThe format of these values is ==> Region Number : IP Address : Port Number\nThese values should be specified in either the client's or the server's preferences file (prefs.cs). You may specifiy it elsewhere, however be sure that it is specified prior to this function being called and before any other functions that rely on it.\n"
  452. "@param flags Look for any game servers with these special flags set. Set to 0 for no flags.\n"
  453. "@param gametype Look for any game servers playing a game type that matches this string. Set to the NULL string to look for any game type.\n"
  454. "@param missiontype Look for any game servers playing a mission type that matches this string. Set to the NULL string to look for any mission type.\n"
  455. "@param minplayers Look for any game servers with this number of players or more. Set to 0 for no lower limit.\n"
  456. "@param maxplayers Look for any game servers with this number of players or fewer. Set to 0 for no upper limit.\n"
  457. "@param maxbots Look for any game servers with this number of AI controlled players or fewer. Set to 0 for no limit.\n"
  458. "@param regionmask Look for any master servers, on our master server list, in this region. Set to 0 to examine all regions.\n"
  459. "@param maxping Look for any game servers with a PING rate equal to or lower than this. Set to 0 for no upper PING limit.\n"
  460. "@param mincpu Look for any game servers with a CPU (clock speed) equal or greater than this. Set to 0 for no CPU (clock speed) limit.\n"
  461. "@param filterflags Look for any game servers with this game version number or higher. Set to 0 to find all versions.\n"
  462. "@return No return value.\n"
  463. "@sa getServerCount, queryLANServers, setServerInfo, startHeartbeat, stopServerQuery")
  464. {
  465. U8 flags = dAtoi(argv[1]);
  466. // It's not a good idea to hold onto args, recursive calls to
  467. // console exec will trash them.
  468. char* gameType = dStrdup(argv[2]);
  469. char* missionType = dStrdup(argv[3]);
  470. U8 minPlayers = dAtoi(argv[4]);
  471. U8 maxPlayers = dAtoi(argv[5]);
  472. U8 maxBots = dAtoi(argv[6]);
  473. U32 regionMask = dAtoi(argv[7]);
  474. U32 maxPing = dAtoi(argv[8]);
  475. U16 minCPU = dAtoi(argv[9]);
  476. U8 filterFlags = dAtoi(argv[10]);
  477. U32 buddyList = 0;
  478. queryMasterServer(flags,gameType,missionType,minPlayers,maxPlayers,
  479. maxBots,regionMask,maxPing,minCPU,filterFlags,0,&buddyList);
  480. dFree(gameType);
  481. dFree(missionType);
  482. }
  483. //-----------------------------------------------------------------------------
  484. ConsoleFunction( querySingleServer, void, 3, 3, "( address [ , flags ] ) Use the querySingleServer function to re-query a previously queried lan server, OR a game server found with queryLANServers or with queryMasterServer and selected with setServerInfo. This will refresh the information stored by TGE about this server. It will not however modify the values of the $ServerInfo::* global variables.\n"
  485. "@param address The IP address and Port to re-query, i.e. \"192.168.123.2:28000\".\n"
  486. "@param flags No longer used.\n"
  487. "@return No return value.\n"
  488. "@sa getServerCount, queryLANServers, queryMasterServer, setServerInfo, stopServerQuery")
  489. {
  490. NetAddress addr;
  491. char* addrText;
  492. addrText = dStrdup(argv[1]);
  493. U8 flags = dAtoi(argv[2]);
  494. Net::stringToAddress( addrText, &addr );
  495. querySingleServer(&addr,flags);
  496. }
  497. //-----------------------------------------------------------------------------
  498. void queryFavoriteServers( U8 /*flags*/ )
  499. {
  500. sgServerQueryActive = true;
  501. clearServerList();
  502. sActiveFilter.type = ServerFilter::Favorites;
  503. pushServerFavorites();
  504. Con::executef( 4, "onServerQueryStatus", "start", "Query favorites...", "0" );
  505. processPingsAndQueries( gPingSession );
  506. }
  507. //-----------------------------------------------------------------------------
  508. void querySingleServer( const NetAddress* addr, U8 /*flags*/ )
  509. {
  510. sgServerQueryActive = true;
  511. ServerInfo* si = findServerInfo( addr );
  512. if ( si )
  513. si->status = ServerInfo::Status_New | ServerInfo::Status_Updating;
  514. // Remove the server from the finished list (if it's there):
  515. for ( U32 i = 0; i < (U32)gFinishedList.size(); i++ )
  516. {
  517. if ( Net::compareAddresses( addr, &gFinishedList[i] ) )
  518. {
  519. gFinishedList.erase( i );
  520. break;
  521. }
  522. }
  523. Con::executef( 4, "onServerQueryStatus", "start", "Refreshing server...", "0" );
  524. gServerPingCount = gServerQueryCount = 0;
  525. pushPingRequest( addr );
  526. processPingsAndQueries( gPingSession );
  527. }
  528. //-----------------------------------------------------------------------------
  529. void cancelServerQuery()
  530. {
  531. // Cancel the current query, if there is anything left
  532. // on the ping list, it's dropped.
  533. if ( sgServerQueryActive )
  534. {
  535. Con::printf( "Server query canceled." );
  536. ServerInfo* si;
  537. // Clear the master server packet list:
  538. gPacketStatusList.clear();
  539. // Clear the ping list:
  540. while ( gPingList.size() )
  541. {
  542. si = findServerInfo( &gPingList[0].address );
  543. if ( si && !si->status.test( ServerInfo::Status_Responded ) )
  544. si->status = ServerInfo::Status_TimedOut;
  545. gPingList.erase( U32( 0 ) );
  546. }
  547. // Clear the query list:
  548. while ( gQueryList.size() )
  549. {
  550. si = findServerInfo( &gQueryList[0].address );
  551. if ( si && !si->status.test( ServerInfo::Status_Responded ) )
  552. si->status = ServerInfo::Status_TimedOut;
  553. gQueryList.erase( U32( 0 ) );
  554. }
  555. sgServerQueryActive = false;
  556. gServerBrowserDirty = true;
  557. }
  558. }
  559. ConsoleFunction( cancelServerQuery, void, 1, 1, "() Use the cancelServerQuery function to cancel a previous query*() call.\n"
  560. "@return No return value.\n"
  561. "@sa queryLANServers, queryMasterServer, querySingleServer")
  562. {
  563. cancelServerQuery();
  564. }
  565. //-----------------------------------------------------------------------------
  566. void stopServerQuery()
  567. {
  568. // Cancel the current query, anything left on the ping
  569. // list is moved to the finished list as "done".
  570. if ( sgServerQueryActive )
  571. {
  572. gPacketStatusList.clear();
  573. if ( gPingList.size() )
  574. {
  575. while ( gPingList.size() )
  576. {
  577. gFinishedList.push_back( gPingList[0].address );
  578. gPingList.erase( U32( 0 ) );
  579. }
  580. }
  581. else
  582. cancelServerQuery();
  583. }
  584. }
  585. ConsoleFunction( stopServerQuery, void, 1, 1, "() Use the stopServerQuery function to cancel any outstanding server queries.\n"
  586. "@return No return value.\n"
  587. "@sa queryLANServers, queryMasterServer, querySingleServer")
  588. {
  589. stopServerQuery();
  590. }
  591. //-----------------------------------------------------------------------------
  592. ConsoleFunction(startHeartbeat, void, 1, 1, "() Use the startHeartbeat function to start advertising this game serer to any master servers on the master server list.\n"
  593. "In order for this function to do anything, a list of master servers must have been previously specified. This list may contain one or more server addresses. Once this function is called, the game server will re-advertise itself to all the master servers on its master server lits every two minutes. To specify a list, simply create a set of array entries like this:\n$pref::Master[0] = \"2:192.168.123.15:28002\";\n$pref::Master[1] = \"2:192.168.123.2:28002\";\nThe format of these values is ==> Region Number : IP Address : Port Number\nThese values should be specified in either the client's or the server's preferences file (prefs.cs). You may specifiy it elsewhere, however be sure that it is specified prior to this function being called and before any other functions that rely on it.\n"
  594. "@return No return value.\n"
  595. "@sa queryMasterServer, stopHeartbeat")
  596. {
  597. if (validateAuthenticatedServer())
  598. {
  599. gHeartbeatSeq++;
  600. processHeartbeat(gHeartbeatSeq); // thump-thump...
  601. }
  602. }
  603. ConsoleFunction(stopHeartbeat, void, 1, 1, "() Use the startHeartbeat function to stop advertising this game serer to any master servers on the master server list.\n"
  604. "@return No return value.\n"
  605. "@sa queryMasterServer, startHeartbeat")
  606. {
  607. gHeartbeatSeq++;
  608. }
  609. //-----------------------------------------------------------------------------
  610. ConsoleFunction( getServerCount, int, 1, 1, "() Use the getServerCount function to determine the number of game servers found on the last queryLANServers() or queryMasterServer() call.\n"
  611. "This value is important because it allows us to properly index when calling setServerInfo().\n"
  612. "@return Returns a numeric value equal to the number of game servers found on the last queryLANServers() or queryMasterServer() call. Returns 0 if the function was not called, or none were found.\n"
  613. "@sa queryLANServers, queryMasterServer, setServerInfo")
  614. {
  615. return gServerList.size();
  616. }
  617. ConsoleFunction( setServerInfo, bool, 2, 2, "( index ) Use the setServerInfo function to set the values of the $ServerInfo::* global variables with information for a server found with queryLANServers or with queryMasterServer.\n"
  618. "@param index The index of the server to get information about.\n"
  619. "@return Will return true if the information was successfully set, false otherwise.\n"
  620. "@sa getServerCount, queryLANServers, queryMasterServer, querySingleServer")
  621. {
  622. S32 index = dAtoi(argv[1]);
  623. if (index >= 0 && index < gServerList.size())
  624. {
  625. ServerInfo& info = gServerList[index];
  626. char addrString[256];
  627. Net::addressToString( &info.address, addrString );
  628. Con::setIntVariable("ServerInfo::Status",info.status);
  629. Con::setVariable("ServerInfo::Address",addrString);
  630. Con::setVariable("ServerInfo::Name",info.name);
  631. Con::setVariable("ServerInfo::GameType",info.gameType);
  632. Con::setVariable("ServerInfo::MissionName",info.missionName);
  633. Con::setVariable("ServerInfo::MissionType",info.missionType);
  634. Con::setVariable("ServerInfo::State",info.statusString);
  635. Con::setVariable("ServerInfo::Info",info.infoString);
  636. Con::setIntVariable("ServerInfo::PlayerCount",info.numPlayers);
  637. Con::setIntVariable("ServerInfo::MaxPlayers",info.maxPlayers);
  638. Con::setIntVariable("ServerInfo::BotCount",info.numBots);
  639. Con::setIntVariable("ServerInfo::Version",info.version);
  640. Con::setIntVariable("ServerInfo::Ping",info.ping);
  641. Con::setIntVariable("ServerInfo::CPUSpeed",info.cpuSpeed);
  642. Con::setBoolVariable("ServerInfo::Favorite",info.isFavorite);
  643. Con::setBoolVariable("ServerInfo::Dedicated",info.isDedicated());
  644. Con::setBoolVariable("ServerInfo::Password",info.isPassworded());
  645. return true;
  646. }
  647. return false;
  648. }
  649. //-----------------------------------------------------------------------------
  650. // Internal
  651. //-----------------------------------------------------------------------------
  652. //-----------------------------------------------------------------------------
  653. ServerInfo::~ServerInfo()
  654. {
  655. if ( name )
  656. dFree( name );
  657. if ( gameType )
  658. dFree( gameType );
  659. if ( missionType )
  660. dFree( missionType );
  661. if ( statusString )
  662. dFree( statusString );
  663. if ( infoString )
  664. dFree( infoString );
  665. }
  666. //-----------------------------------------------------------------------------
  667. Vector<MasterInfo>* getMasterServerList()
  668. {
  669. // This code used to get the master server list from the
  670. // WON.net directory.
  671. static Vector<MasterInfo> masterList;
  672. masterList.clear();
  673. for (U32 i = 0; i < 10; i++) {
  674. char buffer[50];
  675. dSprintf(buffer,sizeof(buffer),"Pref::Master%d",i);
  676. const char* master = Con::getVariable(buffer);
  677. if (master && *master) {
  678. NetAddress address;
  679. // Format for master server variable:
  680. // regionMask:netAddress
  681. U32 region = 1; // needs to default to something > 0
  682. dSscanf(master,"%d:",&region);
  683. const char* madd = dStrchr(master,':') + 1;
  684. if (region && Net::stringToAddress(madd,&address)) {
  685. masterList.increment();
  686. MasterInfo& info = masterList.last();
  687. info.address = address;
  688. info.region = region;
  689. }
  690. else
  691. Con::errorf("Bad master server address: %s",master);
  692. }
  693. }
  694. if (!masterList.size())
  695. Con::errorf("No master servers found");
  696. return &masterList;
  697. }
  698. //-----------------------------------------------------------------------------
  699. bool pickMasterServer()
  700. {
  701. // Reset the master server ping:
  702. gMasterServerPing.time = 0;
  703. gMasterServerPing.key = 0;
  704. gMasterServerPing.tryCount = gMasterServerRetryCount;
  705. gMasterServerPing.session = gPingSession;
  706. char addrString[256];
  707. U32 serverCount = gMasterServerList.size();
  708. if ( !serverCount )
  709. {
  710. // There are no more servers left to try...:(
  711. return( false );
  712. }
  713. U32 region = Con::getIntVariable( "$pref::Net::RegionMask" );
  714. U32 index = Sim::getCurrentTime() % serverCount;
  715. // First try to find a master server in the same region:
  716. for ( U32 i = 0; i < serverCount; i++ )
  717. {
  718. if ( gMasterServerList[index].region == region )
  719. {
  720. Net::addressToString( &gMasterServerList[index].address, addrString );
  721. Con::printf( "Found master server %s in same region.", addrString );
  722. gMasterServerPing.address = gMasterServerList[index].address;
  723. return( true );
  724. }
  725. index = index < serverCount - 1 ? index + 1 : 0;
  726. }
  727. // Settle for the one we first picked:
  728. Net::addressToString( &gMasterServerList[index].address, addrString );
  729. Con::printf( "No master servers found in this region, trying %s.", addrString );
  730. gMasterServerPing.address = gMasterServerList[index].address;
  731. return( true );
  732. }
  733. //-----------------------------------------------------------------------------
  734. void clearServerList()
  735. {
  736. gPacketStatusList.clear();
  737. gServerList.clear();
  738. gFinishedList.clear();
  739. gPingList.clear();
  740. gQueryList.clear();
  741. gServerPingCount = gServerQueryCount = 0;
  742. gPingSession++;
  743. }
  744. //-----------------------------------------------------------------------------
  745. void sendHeartbeat( U8 flags )
  746. {
  747. // send heartbeats to all of the master servers:
  748. Vector<MasterInfo> *masterList = getMasterServerList();
  749. for(U32 i = 0; i < (U32)masterList->size(); i++)
  750. {
  751. char buffer[256];
  752. Net::addressToString(&(*masterList)[i].address, buffer);
  753. // Send a request to the master server for the game types:
  754. Con::printf( "Sending heartbeat to master server [%s]", buffer );
  755. sendPacket( NetInterface::GameHeartbeat, &(*masterList)[i].address, 0, gPingSession, flags );
  756. }
  757. }
  758. //-----------------------------------------------------------------------------
  759. static void pushPingRequest( const NetAddress* addr )
  760. {
  761. if( addressFinished( addr ) )
  762. return;
  763. Ping p;
  764. p.address = *addr;
  765. p.session = gPingSession;
  766. p.key = 0;
  767. p.time = 0;
  768. p.tryCount = gPingRetryCount;
  769. p.broadcast = false;
  770. gPingList.push_back( p );
  771. gServerPingCount++;
  772. }
  773. //-----------------------------------------------------------------------------
  774. static void pushPingBroadcast( const NetAddress* addr )
  775. {
  776. if( addressFinished( addr ) )
  777. return;
  778. Ping p;
  779. p.address = *addr;
  780. p.session = gPingSession;
  781. p.key = 0;
  782. p.time = 0;
  783. p.tryCount = 1; // only try this once
  784. p.broadcast = true;
  785. gPingList.push_back( p );
  786. // Don't increment gServerPingCount, broadcasts are not
  787. // counted as requests.
  788. }
  789. //-----------------------------------------------------------------------------
  790. static S32 countPingRequests()
  791. {
  792. // Need a function here because the ping list also includes
  793. // broadcast pings we don't want counted.
  794. U32 pSize = gPingList.size(), count = pSize;
  795. for (U32 i = 0; i < pSize; i++)
  796. if (gPingList[i].broadcast)
  797. count--;
  798. return count;
  799. }
  800. //-----------------------------------------------------------------------------
  801. static void pushServerFavorites()
  802. {
  803. S32 count = Con::getIntVariable( "$pref::Client::ServerFavoriteCount" );
  804. if ( count < 0 )
  805. {
  806. Con::setIntVariable( "$pref::Client::ServerFavoriteCount", 0 );
  807. return;
  808. }
  809. NetAddress addr;
  810. const char* server = NULL;
  811. char buf[256], serverName[25], addrString[256];
  812. U32 sz, len;
  813. for ( S32 i = 0; i < count; i++ )
  814. {
  815. dSprintf( buf, sizeof( buf ), "Pref::Client::ServerFavorite%d", i );
  816. server = Con::getVariable( buf );
  817. if ( server )
  818. {
  819. sz = dStrcspn( server, "\t" );
  820. if ( sz > 0 )
  821. {
  822. len = sz > 24 ? 24 : sz;
  823. dStrncpy( serverName, server, len );
  824. serverName[len] = 0;
  825. dStrncpy( addrString, server + ( sz + 1 ), 255 );
  826. //Con::errorf( "Pushing server favorite \"%s\" - %s...", serverName, addrString );
  827. Net::stringToAddress( addrString, &addr );
  828. ServerInfo* si = findOrCreateServerInfo( &addr );
  829. AssertFatal(si, "pushServerFavorites - failed to create Server Info!" );
  830. si->name = (char*) dRealloc( (void*) si->name, dStrlen( serverName ) + 1 );
  831. dStrcpy( si->name, serverName );
  832. si->isFavorite = true;
  833. pushPingRequest( &addr );
  834. }
  835. }
  836. }
  837. }
  838. //-----------------------------------------------------------------------------
  839. static S32 findPingEntry( Vector<Ping> &v, const NetAddress* addr )
  840. {
  841. for ( U32 i = 0; i < (U32)v.size(); i++ )
  842. if ( Net::compareAddresses( addr, &v[i].address ) )
  843. return S32( i );
  844. return -1;
  845. }
  846. //-----------------------------------------------------------------------------
  847. static bool addressFinished( const NetAddress* addr )
  848. {
  849. for ( U32 i = 0; i < (U32)gFinishedList.size(); i++ )
  850. if ( Net::compareAddresses( addr, &gFinishedList[i] ) )
  851. return true;
  852. return false;
  853. }
  854. //-----------------------------------------------------------------------------
  855. static ServerInfo* findServerInfo( const NetAddress* addr )
  856. {
  857. for ( U32 i = 0; i < (U32)gServerList.size(); i++ )
  858. if ( Net::compareAddresses( addr, &gServerList[i].address ) )
  859. return &gServerList[i];
  860. return NULL;
  861. }
  862. //-----------------------------------------------------------------------------
  863. static ServerInfo* findOrCreateServerInfo( const NetAddress* addr )
  864. {
  865. ServerInfo* ret = findServerInfo( addr );
  866. if ( ret )
  867. return ret;
  868. ServerInfo si;
  869. si.address = *addr;
  870. gServerList.push_back( si );
  871. return &gServerList.last();
  872. }
  873. //-----------------------------------------------------------------------------
  874. static void removeServerInfo( const NetAddress* addr )
  875. {
  876. for ( U32 i = 0; i < (U32)gServerList.size(); i++ )
  877. {
  878. if ( Net::compareAddresses( addr, &gServerList[i].address ) )
  879. {
  880. gServerList.erase( i );
  881. gServerBrowserDirty = true;
  882. }
  883. }
  884. }
  885. //-----------------------------------------------------------------------------
  886. #if defined(TORQUE_DEBUG)
  887. // This function is solely for testing the functionality of the server browser
  888. // with more servers in the list.
  889. void addFakeServers( S32 howMany )
  890. {
  891. static S32 sNumFakeServers = 1;
  892. ServerInfo newServer;
  893. for ( S32 i = 0; i < howMany; i++ )
  894. {
  895. newServer.numPlayers = (U8)(Platform::getRandom() * 64);
  896. newServer.maxPlayers = 64;
  897. char buf[256];
  898. dSprintf( buf, 255, "Fake server #%d", sNumFakeServers );
  899. newServer.name = (char*) dMalloc( dStrlen( buf ) + 1 );
  900. dStrcpy( newServer.name, buf );
  901. newServer.gameType = (char*) dMalloc( 5 );
  902. dStrcpy( newServer.gameType, "Fake" );
  903. newServer.missionType = (char*) dMalloc( 4 );
  904. dStrcpy( newServer.missionType, "FakeMissionType" );
  905. newServer.missionName = (char*) dMalloc( 14 );
  906. dStrcpy( newServer.missionName, "FakeMapName" );
  907. Net::stringToAddress( "IP:198.74.33.35:28000", &newServer.address );
  908. newServer.ping = U32( Platform::getRandom() * 200 );
  909. newServer.cpuSpeed = 470;
  910. newServer.status = ServerInfo::Status_Responded;
  911. gServerList.push_back( newServer );
  912. sNumFakeServers++;
  913. }
  914. gServerBrowserDirty = true;
  915. }
  916. #endif // DEBUG
  917. //-----------------------------------------------------------------------------
  918. static void sendPacket( U8 pType, const NetAddress* addr, U32 key, U32 session, U8 flags )
  919. {
  920. BitStream *out = BitStream::getPacketStream();
  921. out->write( pType );
  922. out->write( flags );
  923. out->write( U32( ( session << 16 ) | ( key & 0xFFFF ) ) );
  924. BitStream::sendPacketStream(addr);
  925. }
  926. //-----------------------------------------------------------------------------
  927. static void writeCString( BitStream* stream, const char* string )
  928. {
  929. U8 strLen = ( string != NULL ) ? dStrlen( string ) : 0;
  930. stream->write( strLen );
  931. for ( U32 i = 0; i < strLen; i++ )
  932. stream->write( U8( string[i] ) );
  933. }
  934. //-----------------------------------------------------------------------------
  935. static void readCString( BitStream* stream, char* buffer )
  936. {
  937. U32 i;
  938. U8 strLen;
  939. stream->read( &strLen );
  940. for ( i = 0; i < strLen; i++ )
  941. {
  942. U8* ptr = (U8*) buffer;
  943. stream->read( &ptr[i] );
  944. }
  945. buffer[i] = 0;
  946. }
  947. //-----------------------------------------------------------------------------
  948. static void writeLongCString( BitStream* stream, const char* string )
  949. {
  950. U16 strLen = ( string != NULL ) ? dStrlen( string ) : 0;
  951. stream->write( strLen );
  952. for ( U32 i = 0; i < strLen; i++ )
  953. stream->write( U8( string[i] ) );
  954. }
  955. //-----------------------------------------------------------------------------
  956. static void readLongCString( BitStream* stream, char* buffer )
  957. {
  958. U32 i;
  959. U16 strLen;
  960. stream->read( &strLen );
  961. for ( i = 0; i < strLen; i++ )
  962. {
  963. U8* ptr = (U8*) buffer;
  964. stream->read( &ptr[i] );
  965. }
  966. buffer[i] = 0;
  967. }
  968. //-----------------------------------------------------------------------------
  969. // Event processing
  970. //-----------------------------------------------------------------------------
  971. //-----------------------------------------------------------------------------
  972. static void processMasterServerQuery( U32 session )
  973. {
  974. if ( session != gPingSession || !sgServerQueryActive )
  975. return;
  976. if ( !gGotFirstListPacket )
  977. {
  978. bool keepGoing = true;
  979. U32 time = Platform::getVirtualMilliseconds();
  980. char addressString[256];
  981. if ( gMasterServerPing.time + gMasterServerTimeout < time )
  982. {
  983. Net::addressToString( &gMasterServerPing.address, addressString );
  984. if ( !gMasterServerPing.tryCount )
  985. {
  986. // The query timed out.
  987. Con::printf( "Server list request to %s timed out.", addressString );
  988. // Remove this server from the list:
  989. for ( U32 i = 0; i < (U32)gMasterServerList.size(); i++ )
  990. {
  991. if ( Net::compareAddresses( &gMasterServerList[i].address, &gMasterServerPing.address ) )
  992. {
  993. gMasterServerList.erase( i );
  994. break;
  995. }
  996. }
  997. // Pick a new master server to try:
  998. keepGoing = pickMasterServer();
  999. if ( keepGoing )
  1000. {
  1001. Con::executef( 4, "onServerQueryStatus", "update", "Switching master servers...", "0" );
  1002. Net::addressToString( &gMasterServerPing.address, addressString );
  1003. }
  1004. }
  1005. if ( keepGoing )
  1006. {
  1007. gMasterServerPing.tryCount--;
  1008. gMasterServerPing.time = time;
  1009. gMasterServerPing.key = gKey++;
  1010. // Send a request to the master server for the server list:
  1011. BitStream *out = BitStream::getPacketStream();
  1012. out->write( U8( NetInterface::MasterServerListRequest ) );
  1013. out->write( U8( sActiveFilter.queryFlags) );
  1014. out->write( ( gMasterServerPing.session << 16 ) | ( gMasterServerPing.key & 0xFFFF ) );
  1015. out->write( U8( 255 ) );
  1016. writeCString( out, sActiveFilter.gameType );
  1017. writeCString( out, sActiveFilter.missionType );
  1018. out->write( sActiveFilter.minPlayers );
  1019. out->write( sActiveFilter.maxPlayers );
  1020. out->write( sActiveFilter.regionMask );
  1021. U32 version = ( sActiveFilter.filterFlags & ServerFilter::CurrentVersion ) ? getVersionNumber() : 0;
  1022. out->write( version );
  1023. out->write( sActiveFilter.filterFlags );
  1024. out->write( sActiveFilter.maxBots );
  1025. out->write( sActiveFilter.minCPU );
  1026. out->write( sActiveFilter.buddyCount );
  1027. for ( U32 i = 0; i < sActiveFilter.buddyCount; i++ )
  1028. out->write( sActiveFilter.buddyList[i] );
  1029. BitStream::sendPacketStream( &gMasterServerPing.address );
  1030. Con::printf( "Requesting the server list from master server %s (%d tries left)...", addressString, gMasterServerPing.tryCount );
  1031. if ( gMasterServerPing.tryCount < gMasterServerRetryCount - 1 )
  1032. Con::executef( 4, "onServerQueryStatus", "update", "Retrying the master server...", "0" );
  1033. }
  1034. }
  1035. if ( keepGoing )
  1036. {
  1037. // schedule another check:
  1038. Sim::postEvent( Sim::getRootGroup(), new ProcessMasterQueryEvent( session ), Sim::getTargetTime() + 1 );
  1039. }
  1040. else
  1041. {
  1042. Con::errorf( "There are no more master servers to try!" );
  1043. Con::executef( 4, "onServerQueryStatus", "done", "No master servers found.", "0" );
  1044. }
  1045. }
  1046. }
  1047. //-----------------------------------------------------------------------------
  1048. static void processPingsAndQueries( U32 session, bool schedule )
  1049. {
  1050. if( session != gPingSession )
  1051. return;
  1052. U32 i = 0;
  1053. U32 time = Platform::getVirtualMilliseconds();
  1054. char addressString[256];
  1055. U8 flags = ServerFilter::OnlineQuery;
  1056. bool waitingForMaster = ( sActiveFilter.type == ServerFilter::Normal ) && !gGotFirstListPacket && sgServerQueryActive;
  1057. for ( i = 0; i < (U32)gPingList.size() && i < (U32)gMaxConcurrentPings; )
  1058. {
  1059. Ping &p = gPingList[i];
  1060. if ( p.time + gPingTimeout < time )
  1061. {
  1062. if ( !p.tryCount )
  1063. {
  1064. // it's timed out.
  1065. if (!p.broadcast)
  1066. {
  1067. Net::addressToString( &p.address, addressString );
  1068. Con::printf( "Ping to server %s timed out.", addressString );
  1069. }
  1070. // If server info is in list (favorite), set its status:
  1071. ServerInfo* si = findServerInfo( &p.address );
  1072. if ( si )
  1073. {
  1074. si->status = ServerInfo::Status_TimedOut;
  1075. gServerBrowserDirty = true;
  1076. }
  1077. gFinishedList.push_back( p.address );
  1078. gPingList.erase( i );
  1079. if ( !waitingForMaster )
  1080. updatePingProgress();
  1081. }
  1082. else
  1083. {
  1084. p.tryCount--;
  1085. p.time = time;
  1086. p.key = gKey++;
  1087. Net::addressToString( &p.address, addressString );
  1088. if (p.broadcast)
  1089. Con::printf( "LAN server ping: %s...", addressString );
  1090. else
  1091. Con::printf( "Pinging Server %s (%d)...", addressString, p.tryCount );
  1092. sendPacket( NetInterface::GamePingRequest, &p.address, p.key, p.session, flags );
  1093. i++;
  1094. }
  1095. }
  1096. else
  1097. i++;
  1098. }
  1099. if ( !gPingList.size() && !waitingForMaster )
  1100. {
  1101. // Start the query phase:
  1102. for ( U32 i = 0; i < (U32)gQueryList.size() && i < (U32)gMaxConcurrentQueries; )
  1103. {
  1104. Ping &p = gQueryList[i];
  1105. if ( p.time + gPingTimeout < time )
  1106. {
  1107. ServerInfo* si = findServerInfo( &p.address );
  1108. if ( !si )
  1109. {
  1110. // Server info not found, so remove the query:
  1111. gQueryList.erase( i );
  1112. gServerBrowserDirty = true;
  1113. continue;
  1114. }
  1115. Net::addressToString( &p.address, addressString );
  1116. if ( !p.tryCount )
  1117. {
  1118. Con::printf( "Query to server %s timed out.", addressString );
  1119. si->status = ServerInfo::Status_TimedOut;
  1120. gQueryList.erase( i );
  1121. gServerBrowserDirty = true;
  1122. }
  1123. else
  1124. {
  1125. p.tryCount--;
  1126. p.time = time;
  1127. p.key = gKey++;
  1128. Con::printf( "Querying Server %s (%d)...", addressString, p.tryCount );
  1129. sendPacket( NetInterface::GameInfoRequest, &p.address, p.key, p.session, flags );
  1130. if ( !si->isQuerying() )
  1131. {
  1132. si->status |= ServerInfo::Status_Querying;
  1133. gServerBrowserDirty = true;
  1134. }
  1135. i++;
  1136. }
  1137. }
  1138. else
  1139. i++;
  1140. }
  1141. }
  1142. if ( gPingList.size() || gQueryList.size() || waitingForMaster )
  1143. {
  1144. // The LAN query function doesn't always want to schedule
  1145. // the next ping.
  1146. if (schedule)
  1147. Sim::postEvent( Sim::getRootGroup(), new ProcessPingEvent( session ), Sim::getTargetTime() + 1 );
  1148. }
  1149. else
  1150. {
  1151. // All done!
  1152. char msg[64];
  1153. U32 foundCount = gServerList.size();
  1154. if ( foundCount == 0 )
  1155. dStrcpy( msg, "No servers found." );
  1156. else if ( foundCount == 1 )
  1157. dStrcpy( msg, "One server found." );
  1158. else
  1159. dSprintf( msg, sizeof( msg ), "%d servers found.", foundCount );
  1160. Con::executef( 4, "onServerQueryStatus", "done", msg, "1");
  1161. }
  1162. }
  1163. //-----------------------------------------------------------------------------
  1164. static void processServerListPackets( U32 session )
  1165. {
  1166. if ( session != gPingSession || !sgServerQueryActive )
  1167. return;
  1168. U32 currentTime = Platform::getVirtualMilliseconds();
  1169. // Loop through the packet status list and resend packet requests where necessary:
  1170. for ( U32 i = 0; i < (U32)gPacketStatusList.size(); i++ )
  1171. {
  1172. PacketStatus &p = gPacketStatusList[i];
  1173. if ( p.time + gPacketTimeout < currentTime )
  1174. {
  1175. if ( !p.tryCount )
  1176. {
  1177. // Packet timed out :(
  1178. Con::printf( "Server list packet #%d timed out.", p.index + 1 );
  1179. gPacketStatusList.erase( i );
  1180. }
  1181. else
  1182. {
  1183. // Try again...
  1184. Con::printf( "Rerequesting server list packet #%d...", p.index + 1 );
  1185. p.tryCount--;
  1186. p.time = currentTime;
  1187. p.key = gKey++;
  1188. BitStream *out = BitStream::getPacketStream();
  1189. out->write( U8( NetInterface::MasterServerListRequest ) );
  1190. out->write( U8( sActiveFilter.queryFlags ) ); // flags
  1191. out->write( ( session << 16) | ( p.key & 0xFFFF ) );
  1192. out->write( p.index ); // packet index
  1193. out->write( U8( 0 ) ); // game type
  1194. out->write( U8( 0 ) ); // mission type
  1195. out->write( U8( 0 ) ); // minPlayers
  1196. out->write( U8( 0 ) ); // maxPlayers
  1197. out->write( U32( 0 ) ); // region mask
  1198. out->write( U32( 0 ) ); // version
  1199. out->write( U8( 0 ) ); // filter flags
  1200. out->write( U8( 0 ) ); // max bots
  1201. out->write( U16( 0 ) ); // min CPU
  1202. out->write( U8( 0 ) ); // buddy count
  1203. BitStream::sendPacketStream(&gMasterServerQueryAddress);
  1204. }
  1205. }
  1206. }
  1207. if ( gPacketStatusList.size() )
  1208. Sim::postEvent( Sim::getRootGroup(), new ProcessPacketEvent( session ), Sim::getCurrentTime() + 30 );
  1209. else
  1210. processPingsAndQueries( gPingSession );
  1211. }
  1212. //-----------------------------------------------------------------------------
  1213. static void processHeartbeat(U32 seq)
  1214. {
  1215. if(seq != gHeartbeatSeq)
  1216. return;
  1217. sendHeartbeat( 0 );
  1218. Sim::postEvent( Sim::getRootGroup(), new HeartbeatEvent(seq), Sim::getCurrentTime() + gHeartbeatInterval );
  1219. }
  1220. //-----------------------------------------------------------------------------
  1221. static void updatePingProgress()
  1222. {
  1223. if ( !gPingList.size() )
  1224. {
  1225. updateQueryProgress();
  1226. return;
  1227. }
  1228. char msg[64];
  1229. U32 pingsLeft = countPingRequests();
  1230. dSprintf( msg, sizeof(msg),
  1231. (!pingsLeft && gPingList.size())?
  1232. "Waiting for lan servers...":
  1233. "Pinging servers: %d left...",
  1234. pingsLeft );
  1235. // Ping progress is 0 -> 0.5
  1236. F32 progress = 0.0f;
  1237. if ( gServerPingCount )
  1238. progress = F32( gServerPingCount - pingsLeft ) / F32( gServerPingCount * 2 );
  1239. //Con::errorf( ConsoleLogEntry::General, "Ping progress - %d of %d left - progress = %.2f", pingsLeft, gServerPingCount, progress );
  1240. Con::executef( 4, "onServerQueryStatus", "ping", msg, Con::getFloatArg( progress ) );
  1241. }
  1242. //-----------------------------------------------------------------------------
  1243. static void updateQueryProgress()
  1244. {
  1245. if ( gPingList.size() )
  1246. return;
  1247. char msg[64];
  1248. U32 queriesLeft = gQueryList.size();
  1249. dSprintf( msg, sizeof( msg ), "Querying servers: %d left...", queriesLeft );
  1250. // Query progress is 0.5 -> 1
  1251. F32 progress = 0.5f;
  1252. if ( gServerQueryCount )
  1253. progress += ( F32( gServerQueryCount - queriesLeft ) / F32( gServerQueryCount * 2 ) );
  1254. //Con::errorf( ConsoleLogEntry::General, "Query progress - %d of %d left - progress = %.2f", queriesLeft, gServerQueryCount, progress );
  1255. Con::executef( 4, "onServerQueryStatus", "query", msg, Con::getFloatArg( progress ) );
  1256. }
  1257. //-----------------------------------------------------------------------------
  1258. // Server packet handlers:
  1259. //-----------------------------------------------------------------------------
  1260. //-----------------------------------------------------------------------------
  1261. static void handleMasterServerGameTypesResponse( BitStream* stream, U32 /*key*/, U8 /*flags*/ )
  1262. {
  1263. Con::printf( "Received game type list from the master server." );
  1264. U32 i;
  1265. U8 temp;
  1266. char stringBuf[256];
  1267. stream->read( &temp );
  1268. Con::executef(1, "onClearGameTypes");
  1269. for ( i = 0; i < U32( temp ); i++ )
  1270. {
  1271. readCString( stream, stringBuf );
  1272. Con::executef(2, "onAddGameType", stringBuf);
  1273. }
  1274. stream->read( &temp );
  1275. Con::executef(1, "onClearMissionTypes");
  1276. for ( i = 0; i < U32( temp ); i++ )
  1277. {
  1278. readCString( stream, stringBuf );
  1279. Con::executef(2, "onAddMissionType", stringBuf);
  1280. }
  1281. }
  1282. //-----------------------------------------------------------------------------
  1283. static void handleMasterServerListResponse( BitStream* stream, U32 key, U8 /*flags*/ )
  1284. {
  1285. U8 packetIndex, packetTotal;
  1286. U32 i;
  1287. U16 serverCount, port;
  1288. U8 netNum[4];
  1289. char addressBuffer[256];
  1290. NetAddress addr;
  1291. stream->read( &packetIndex );
  1292. // Validate the packet key:
  1293. U32 packetKey = gMasterServerPing.key;
  1294. if ( gGotFirstListPacket )
  1295. {
  1296. for ( i = 0; i < (U32)gPacketStatusList.size(); i++ )
  1297. {
  1298. if ( gPacketStatusList[i].index == packetIndex )
  1299. {
  1300. packetKey = gPacketStatusList[i].key;
  1301. break;
  1302. }
  1303. }
  1304. }
  1305. U32 testKey = ( gPingSession << 16 ) | ( packetKey & 0xFFFF );
  1306. if ( testKey != key )
  1307. return;
  1308. stream->read( &packetTotal );
  1309. stream->read( &serverCount );
  1310. Con::printf( "Received server list packet %d of %d from the master server (%d servers).", ( packetIndex + 1 ), packetTotal, serverCount );
  1311. // Enter all of the servers in this packet into the ping list:
  1312. for ( i = 0; i < serverCount; i++ )
  1313. {
  1314. stream->read( &netNum[0] );
  1315. stream->read( &netNum[1] );
  1316. stream->read( &netNum[2] );
  1317. stream->read( &netNum[3] );
  1318. stream->read( &port );
  1319. dSprintf( addressBuffer, sizeof( addressBuffer ), "IP:%d.%d.%d.%d:%d", netNum[0], netNum[1], netNum[2], netNum[3], port );
  1320. Net::stringToAddress( addressBuffer, &addr );
  1321. pushPingRequest( &addr );
  1322. }
  1323. // If this is the first list packet we have received, fill the packet status list
  1324. // and start processing:
  1325. if ( !gGotFirstListPacket )
  1326. {
  1327. gGotFirstListPacket = true;
  1328. gMasterServerQueryAddress = gMasterServerPing.address;
  1329. U32 currentTime = Platform::getVirtualMilliseconds();
  1330. for ( i = 0; i < packetTotal; i++ )
  1331. {
  1332. if ( i != packetIndex )
  1333. {
  1334. PacketStatus* p = new PacketStatus( i, gMasterServerPing.key, currentTime );
  1335. gPacketStatusList.push_back( *p );
  1336. }
  1337. }
  1338. processServerListPackets( gPingSession );
  1339. }
  1340. else
  1341. {
  1342. // Remove the packet we just received from the status list:
  1343. for ( i = 0; i < (U32)gPacketStatusList.size(); i++ )
  1344. {
  1345. if ( gPacketStatusList[i].index == packetIndex )
  1346. {
  1347. gPacketStatusList.erase( i );
  1348. break;
  1349. }
  1350. }
  1351. }
  1352. }
  1353. //-----------------------------------------------------------------------------
  1354. static void handleGameMasterInfoRequest( const NetAddress* address, U32 key, U8 flags )
  1355. {
  1356. if ( GNet->doesAllowConnections() )
  1357. {
  1358. U8 temp8;
  1359. U32 temp32;
  1360. char netString[256];
  1361. Net::addressToString(address, netString);
  1362. Vector<MasterInfo> *masterList = getMasterServerList();
  1363. const NetAddress *masterAddr;
  1364. bool fromMaster = false;
  1365. for(U32 i = 0; i < (U32)masterList->size(); i++)
  1366. {
  1367. masterAddr = &(*masterList)[i].address;
  1368. if (*(U32*)(masterAddr->netNum) == *(U32*)(address->netNum))
  1369. {
  1370. fromMaster = true;
  1371. break;
  1372. }
  1373. }
  1374. Con::printf( "Received info request from %s [%s].", fromMaster?"a master server":"a machine", netString);
  1375. BitStream *out = BitStream::getPacketStream();
  1376. out->write( U8( NetInterface::GameMasterInfoResponse ) );
  1377. out->write( U8( flags ) );
  1378. out->write( key );
  1379. writeCString( out, Con::getVariable( "Server::GameType" ) );
  1380. writeCString( out, Con::getVariable( "Server::MissionType" ) );
  1381. temp8 = U8( Con::getIntVariable( "Pref::Server::MaxPlayers" ) );
  1382. out->write( temp8 );
  1383. temp32 = Con::getIntVariable( "Pref::Server::RegionMask" );
  1384. out->write( temp32 );
  1385. temp32 = getVersionNumber();
  1386. out->write( temp32 );
  1387. temp8 = 0;
  1388. #if defined(TORQUE_OS_LINUX) || defined(TORQUE_OS_OPENBSD)
  1389. temp8 |= ServerInfo::Status_Linux;
  1390. #endif
  1391. if ( Con::getBoolVariable( "Server::Dedicated" ) )
  1392. temp8 |= ServerInfo::Status_Dedicated;
  1393. if ( dStrlen( Con::getVariable( "Pref::Server::Password" ) ) > 0 )
  1394. temp8 |= ServerInfo::Status_Passworded;
  1395. out->write( temp8 );
  1396. temp8 = U8( Con::getIntVariable( "Server::BotCount" ) );
  1397. out->write( temp8 );
  1398. out->write( PlatformSystemInfo.processor.mhz );
  1399. U8 playerCount = U8( Con::getIntVariable( "Server::PlayerCount" ) );
  1400. out->write( playerCount );
  1401. const char* guidList = Con::getVariable( "Server::GuidList" );
  1402. char* buf = new char[dStrlen( guidList ) + 1];
  1403. dStrcpy( buf, guidList );
  1404. char* temp = dStrtok( buf, "\t" );
  1405. temp8 = 0;
  1406. for ( ; temp && temp8 < playerCount; temp8++ )
  1407. {
  1408. out->write( U32( dAtoi( temp ) ) );
  1409. temp = dStrtok( NULL, "\t" );
  1410. temp8++;
  1411. }
  1412. for ( ; temp8 < playerCount; temp8++ )
  1413. out->write( U32( 0 ) );
  1414. delete [] buf;
  1415. BitStream::sendPacketStream(address);
  1416. }
  1417. }
  1418. //-----------------------------------------------------------------------------
  1419. static void handleGamePingRequest( const NetAddress* address, U32 key, U8 flags )
  1420. {
  1421. // Do not respond if a mission is not running:
  1422. if ( GNet->doesAllowConnections() )
  1423. {
  1424. // Do not respond if this is a single-player game:
  1425. if ( dStricmp( Con::getVariable( "Server::ServerType" ), "SinglePlayer" ) == 0 )
  1426. return;
  1427. // Do not respond to offline queries if this is an online server:
  1428. if ( flags & ServerFilter::OfflineQuery )
  1429. return;
  1430. // some banning code here (?)
  1431. BitStream *out = BitStream::getPacketStream();
  1432. out->write( U8( NetInterface::GamePingResponse ) );
  1433. out->write( flags );
  1434. out->write( key );
  1435. if ( flags & ServerFilter::NoStringCompress )
  1436. writeCString( out, versionString );
  1437. else
  1438. out->writeString( versionString );
  1439. out->write( GameConnection::CurrentProtocolVersion );
  1440. out->write( GameConnection::MinRequiredProtocolVersion );
  1441. out->write( getVersionNumber() );
  1442. // Enforce a 24-character limit on the server name:
  1443. char serverName[25];
  1444. dStrncpy( serverName, Con::getVariable( "Pref::Server::Name" ), 24 );
  1445. serverName[24] = 0;
  1446. if ( flags & ServerFilter::NoStringCompress )
  1447. writeCString( out, serverName );
  1448. else
  1449. out->writeString( serverName );
  1450. BitStream::sendPacketStream(address);
  1451. }
  1452. }
  1453. //-----------------------------------------------------------------------------
  1454. static void handleGamePingResponse( const NetAddress* address, BitStream* stream, U32 key, U8 /*flags*/ )
  1455. {
  1456. // Broadcast has timed out or query has been cancelled:
  1457. if( !gPingList.size() )
  1458. return;
  1459. S32 index = findPingEntry( gPingList, address );
  1460. if( index == -1 )
  1461. {
  1462. // an anonymous ping response - if it's not already timed
  1463. // out or finished, ping it. Probably from a broadcast
  1464. if( !addressFinished( address ) )
  1465. pushPingRequest( address );
  1466. return;
  1467. }
  1468. Ping &p = gPingList[index];
  1469. U32 infoKey = ( p.session << 16 ) | ( p.key & 0xFFFF );
  1470. if( infoKey != key )
  1471. return;
  1472. // Find if the server info already exists (favorite or refreshing):
  1473. ServerInfo* si = findServerInfo( address );
  1474. bool applyFilter = false;
  1475. if ( sActiveFilter.type == ServerFilter::Normal )
  1476. applyFilter = si ? !si->isUpdating() : true;
  1477. char addrString[256];
  1478. Net::addressToString( address, addrString );
  1479. bool waitingForMaster = ( sActiveFilter.type == ServerFilter::Normal ) && !gGotFirstListPacket;
  1480. // Verify the version:
  1481. char buf[256];
  1482. stream->readString( buf );
  1483. if ( dStrcmp( buf, versionString ) != 0 )
  1484. {
  1485. // Version is different, so remove it from consideration:
  1486. Con::printf( "Server %s is a different version.", addrString );
  1487. gFinishedList.push_back( *address );
  1488. gPingList.erase( index );
  1489. if ( si )
  1490. {
  1491. si->status = ServerInfo::Status_TimedOut;
  1492. gServerBrowserDirty = true;
  1493. }
  1494. if ( !waitingForMaster )
  1495. updatePingProgress();
  1496. return;
  1497. }
  1498. // See if the server meets our minimum protocol:
  1499. U32 temp32;
  1500. stream->read( &temp32 );
  1501. if ( temp32 < GameConnection::MinRequiredProtocolVersion )
  1502. {
  1503. Con::printf( "Protocol for server %s does not meet minimum protocol.", addrString );
  1504. gFinishedList.push_back( *address );
  1505. gPingList.erase( index );
  1506. if ( si )
  1507. {
  1508. si->status = ServerInfo::Status_TimedOut;
  1509. gServerBrowserDirty = true;
  1510. }
  1511. if ( !waitingForMaster )
  1512. updatePingProgress();
  1513. return;
  1514. }
  1515. // See if we meet the server's minimum protocol:
  1516. stream->read( &temp32 );
  1517. if ( GameConnection::CurrentProtocolVersion < temp32 )
  1518. {
  1519. Con::printf( "You do not meet the minimum protocol for server %s.", addrString );
  1520. gFinishedList.push_back( *address );
  1521. gPingList.erase( index );
  1522. if ( si )
  1523. {
  1524. si->status = ServerInfo::Status_TimedOut;
  1525. gServerBrowserDirty = true;
  1526. }
  1527. if ( !waitingForMaster )
  1528. updatePingProgress();
  1529. return;
  1530. }
  1531. // Calculate the ping:
  1532. U32 time = Platform::getVirtualMilliseconds();
  1533. U32 ping = ( time > p.time ) ? time - p.time : 0;
  1534. // Check for max ping filter:
  1535. if ( applyFilter && sActiveFilter.maxPing > 0 && ping > sActiveFilter.maxPing )
  1536. {
  1537. // Ping is too high, so remove this server from consideration:
  1538. Con::printf( "Server %s filtered out by maximum ping.", addrString );
  1539. gFinishedList.push_back( *address );
  1540. gPingList.erase( index );
  1541. if ( si )
  1542. removeServerInfo( address );
  1543. if ( !waitingForMaster )
  1544. updatePingProgress();
  1545. return;
  1546. }
  1547. // Get the server build version:
  1548. stream->read( &temp32 );
  1549. if ( applyFilter
  1550. && ( sActiveFilter.filterFlags & ServerFilter::CurrentVersion )
  1551. && ( temp32 != getVersionNumber() ) )
  1552. {
  1553. Con::printf( "Server %s filtered out by version number.", addrString );
  1554. gFinishedList.push_back( *address );
  1555. gPingList.erase( index );
  1556. if ( si )
  1557. removeServerInfo( address );
  1558. if ( !waitingForMaster )
  1559. updatePingProgress();
  1560. return;
  1561. }
  1562. // OK, we can finally create the server info structure:
  1563. if ( !si )
  1564. si = findOrCreateServerInfo( address );
  1565. si->ping = ping;
  1566. si->version = temp32;
  1567. // Get the server name:
  1568. stream->readString( buf );
  1569. if ( !si->name )
  1570. {
  1571. si->name = (char*) dMalloc( dStrlen( buf ) + 1 );
  1572. dStrcpy( si->name, buf );
  1573. }
  1574. // Set the server up to be queried:
  1575. gFinishedList.push_back( *address );
  1576. p.key = 0;
  1577. p.time = 0;
  1578. p.tryCount = gQueryRetryCount;
  1579. gQueryList.push_back( p );
  1580. gServerQueryCount++;
  1581. gPingList.erase( index );
  1582. if ( !waitingForMaster )
  1583. updatePingProgress();
  1584. // Update the server browser gui!
  1585. gServerBrowserDirty = true;
  1586. }
  1587. //-----------------------------------------------------------------------------
  1588. static void handleGameInfoRequest( const NetAddress* address, U32 key, U8 flags )
  1589. {
  1590. // Do not respond unless there is a server running:
  1591. if ( GNet->doesAllowConnections() )
  1592. {
  1593. // Do not respond to offline queries if this is an online server:
  1594. if ( flags & ServerFilter::OfflineQuery )
  1595. return;
  1596. bool compressStrings = !( flags & ServerFilter::NoStringCompress );
  1597. BitStream *out = BitStream::getPacketStream();
  1598. out->write( U8( NetInterface::GameInfoResponse ) );
  1599. out->write( flags );
  1600. out->write( key );
  1601. if ( compressStrings ) {
  1602. out->writeString( Con::getVariable( "Server::GameType" ) );
  1603. out->writeString( Con::getVariable( "Server::MissionType" ) );
  1604. out->writeString( Con::getVariable( "Server::MissionName" ) );
  1605. }
  1606. else {
  1607. writeCString( out, Con::getVariable( "Server::GameType" ) );
  1608. writeCString( out, Con::getVariable( "Server::MissionType" ) );
  1609. writeCString( out, Con::getVariable( "Server::MissionName" ) );
  1610. }
  1611. U8 status = 0;
  1612. #if defined(TORQUE_OS_LINUX) || defined(TORQUE_OS_OPENBSD)
  1613. status |= ServerInfo::Status_Linux;
  1614. #endif
  1615. if ( Con::getBoolVariable( "Server::Dedicated" ) )
  1616. status |= ServerInfo::Status_Dedicated;
  1617. if ( dStrlen( Con::getVariable( "Pref::Server::Password" ) ) )
  1618. status |= ServerInfo::Status_Passworded;
  1619. out->write( status );
  1620. out->write( U8( Con::getIntVariable( "Server::PlayerCount" ) ) );
  1621. out->write( U8( Con::getIntVariable( "Pref::Server::MaxPlayers" ) ) );
  1622. out->write( U8( Con::getIntVariable( "Server::BotCount" ) ) );
  1623. out->write( U16( PlatformSystemInfo.processor.mhz ) );
  1624. if ( compressStrings )
  1625. out->writeString( Con::getVariable( "Pref::Server::Info" ) );
  1626. else
  1627. writeCString( out, Con::getVariable( "Pref::Server::Info" ) );
  1628. writeLongCString( out, Con::evaluate( "onServerInfoQuery();" ) );
  1629. BitStream::sendPacketStream(address);
  1630. }
  1631. }
  1632. //-----------------------------------------------------------------------------
  1633. static void handleGameInfoResponse( const NetAddress* address, BitStream* stream, U32 /*key*/, U8 /*flags*/ )
  1634. {
  1635. if ( !gQueryList.size() )
  1636. return;
  1637. S32 index = findPingEntry( gQueryList, address );
  1638. if ( index == -1 )
  1639. return;
  1640. // Remove the server from the query list since it has been so kind as to respond:
  1641. gQueryList.erase( index );
  1642. updateQueryProgress();
  1643. ServerInfo *si = findServerInfo( address );
  1644. if ( !si )
  1645. return;
  1646. bool isUpdate = si->isUpdating();
  1647. bool applyFilter = !isUpdate && ( sActiveFilter.type == ServerFilter::Normal );
  1648. char addrString[256];
  1649. Net::addressToString( address, addrString );
  1650. // Get the rules set:
  1651. char stringBuf[2048]; // Who knows how big this should be?
  1652. stream->readString( stringBuf );
  1653. if ( !si->gameType || dStricmp( si->gameType, stringBuf ) != 0 )
  1654. {
  1655. si->gameType = (char*) dRealloc( (void*) si->gameType, dStrlen( stringBuf ) + 1 );
  1656. dStrcpy( si->gameType, stringBuf );
  1657. // Test against the active filter:
  1658. if ( applyFilter && dStricmp( sActiveFilter.gameType, "any" ) != 0
  1659. && dStricmp( si->gameType, sActiveFilter.gameType ) != 0 )
  1660. {
  1661. Con::printf( "Server %s filtered out by rules set. (%s:%s)", addrString, sActiveFilter.gameType, si->gameType );
  1662. removeServerInfo( address );
  1663. return;
  1664. }
  1665. }
  1666. // Get the mission type:
  1667. stream->readString( stringBuf );
  1668. if ( !si->missionType || dStrcmp( si->missionType, stringBuf ) != 0 )
  1669. {
  1670. si->missionType = (char*) dRealloc( (void*) si->missionType, dStrlen( stringBuf ) + 1 );
  1671. dStrcpy( si->missionType, stringBuf );
  1672. // Test against the active filter:
  1673. if ( applyFilter && dStricmp( sActiveFilter.missionType, "any" ) != 0
  1674. && dStricmp( si->missionType, sActiveFilter.missionType ) != 0 )
  1675. {
  1676. Con::printf( "Server %s filtered out by mission type. (%s:%s)", addrString, sActiveFilter.missionType, si->missionType );
  1677. removeServerInfo( address );
  1678. return;
  1679. }
  1680. }
  1681. // Get the mission name:
  1682. stream->readString( stringBuf );
  1683. // Clip the file extension off:
  1684. char* temp = dStrstr( static_cast<char*>( stringBuf ), const_cast<char*>( ".mis" ) );
  1685. if ( temp )
  1686. *temp = '\0';
  1687. if ( !si->missionName || dStrcmp( si->missionName, stringBuf ) != 0 )
  1688. {
  1689. si->missionName = (char*) dRealloc( (void*) si->missionName, dStrlen( stringBuf ) + 1 );
  1690. dStrcpy( si->missionName, stringBuf );
  1691. }
  1692. // Get the server status:
  1693. U8 temp_U8;
  1694. stream->read( &temp_U8 );
  1695. si->status = temp_U8;
  1696. // Filter by the flags:
  1697. if ( applyFilter )
  1698. {
  1699. if ( sActiveFilter.filterFlags & ServerFilter::Dedicated && !si->isDedicated() )
  1700. {
  1701. Con::printf( "Server %s filtered out by dedicated flag.", addrString );
  1702. removeServerInfo( address );
  1703. return;
  1704. }
  1705. if ( sActiveFilter.filterFlags & ServerFilter::NotPassworded && si->isPassworded() )
  1706. {
  1707. Con::printf( "Server %s filtered out by no-password flag.", addrString );
  1708. removeServerInfo( address );
  1709. return;
  1710. }
  1711. }
  1712. si->status.set( ServerInfo::Status_Responded );
  1713. // Get the player count:
  1714. stream->read( &si->numPlayers );
  1715. // Test player count against active filter:
  1716. if ( applyFilter && ( si->numPlayers < sActiveFilter.minPlayers || si->numPlayers > sActiveFilter.maxPlayers ) )
  1717. {
  1718. Con::printf( "Server %s filtered out by player count.", addrString );
  1719. removeServerInfo( address );
  1720. return;
  1721. }
  1722. // Get the max players and bot count:
  1723. stream->read( &si->maxPlayers );
  1724. stream->read( &si->numBots );
  1725. // Test bot count against active filter:
  1726. if ( applyFilter && ( si->numBots > sActiveFilter.maxBots ) )
  1727. {
  1728. Con::printf( "Server %s filtered out by maximum bot count.", addrString );
  1729. removeServerInfo( address );
  1730. return;
  1731. }
  1732. // Get the CPU speed;
  1733. U16 temp_U16;
  1734. stream->read( &temp_U16 );
  1735. si->cpuSpeed = temp_U16;
  1736. // Test CPU speed against active filter:
  1737. if ( applyFilter && ( si->cpuSpeed < sActiveFilter.minCPU ) )
  1738. {
  1739. Con::printf( "Server %s filtered out by minimum CPU speed.", addrString );
  1740. removeServerInfo( address );
  1741. return;
  1742. }
  1743. // Get the server info:
  1744. stream->readString( stringBuf );
  1745. if ( !si->statusString || ( isUpdate && dStrcmp( si->statusString, stringBuf ) != 0 ) )
  1746. {
  1747. si->infoString = (char*) dRealloc( (void*) si->infoString, dStrlen( stringBuf ) + 1 );
  1748. dStrcpy( si->infoString, stringBuf );
  1749. }
  1750. // Get the content string:
  1751. readLongCString( stream, stringBuf );
  1752. if ( !si->statusString || ( isUpdate && dStrcmp( si->statusString, stringBuf ) != 0 ) )
  1753. {
  1754. si->statusString = (char*) dRealloc( (void*) si->statusString, dStrlen( stringBuf ) + 1 );
  1755. dStrcpy( si->statusString, stringBuf );
  1756. }
  1757. // Update the server browser gui!
  1758. gServerBrowserDirty = true;
  1759. }
  1760. //-----------------------------------------------------------------------------
  1761. // Packet Dispatch
  1762. void DemoNetInterface::handleInfoPacket( const NetAddress* address, U8 packetType, BitStream* stream )
  1763. {
  1764. U8 flags;
  1765. U32 key;
  1766. stream->read( &flags );
  1767. stream->read( &key );
  1768. switch( packetType )
  1769. {
  1770. case GamePingRequest:
  1771. handleGamePingRequest( address, key, flags );
  1772. break;
  1773. case GamePingResponse:
  1774. handleGamePingResponse( address, stream, key, flags );
  1775. break;
  1776. case GameInfoRequest:
  1777. handleGameInfoRequest( address, key, flags );
  1778. break;
  1779. case GameInfoResponse:
  1780. handleGameInfoResponse( address, stream, key, flags );
  1781. break;
  1782. case MasterServerGameTypesResponse:
  1783. handleMasterServerGameTypesResponse( stream, key, flags );
  1784. break;
  1785. case MasterServerListResponse:
  1786. handleMasterServerListResponse( stream, key, flags );
  1787. break;
  1788. case GameMasterInfoRequest:
  1789. handleGameMasterInfoRequest( address, key, flags );
  1790. break;
  1791. }
  1792. }
  1793. ConsoleFunctionGroupEnd( ServerQuery );