PageRenderTime 60ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 1ms

/java/src/main/java/com/github/koraktor/steamcondenser/steam/servers/MasterServer.java

https://github.com/HyperionRiaz/steam-condenser
Java | 341 lines | 116 code | 36 blank | 189 comment | 9 complexity | e1b38500606e4cb5d09a61f452b0d676 MD5 | raw file
  1. /**
  2. * This code is free software; you can redistribute it and/or modify it under
  3. * the terms of the new BSD License.
  4. *
  5. * Copyright (c) 2008-2011, Sebastian Staudt
  6. */
  7. package com.github.koraktor.steamcondenser.steam.servers;
  8. import java.net.InetAddress;
  9. import java.net.InetSocketAddress;
  10. import java.util.ArrayList;
  11. import java.util.List;
  12. import java.util.Map;
  13. import java.util.Vector;
  14. import java.util.concurrent.TimeoutException;
  15. import com.github.koraktor.steamcondenser.exceptions.SteamCondenserException;
  16. import com.github.koraktor.steamcondenser.steam.packets.A2M_GET_SERVERS_BATCH2_Paket;
  17. import com.github.koraktor.steamcondenser.steam.packets.C2M_CHECKMD5_Packet;
  18. import com.github.koraktor.steamcondenser.steam.packets.M2A_SERVER_BATCH_Paket;
  19. import com.github.koraktor.steamcondenser.steam.packets.M2C_ISVALIDMD5_Packet;
  20. import com.github.koraktor.steamcondenser.steam.packets.S2M_HEARTBEAT2_Packet;
  21. import com.github.koraktor.steamcondenser.steam.packets.SteamPacket;
  22. import com.github.koraktor.steamcondenser.steam.sockets.MasterServerSocket;
  23. /**
  24. * This class represents a Steam master server and can be used to get game
  25. * servers which are publicly available
  26. * <p/>
  27. * An intance of this class can be used much like Steam's server browser to get
  28. * a list of available game servers, including filters to narrow down the
  29. * search results.
  30. *
  31. * @author Sebastian Staudt
  32. */
  33. public class MasterServer extends Server {
  34. /**
  35. * The master server address to query for GoldSrc game servers
  36. */
  37. public static final String GOLDSRC_MASTER_SERVER = "hl1master.steampowered.com:27010";
  38. /**
  39. * The master server address to query for GoldSrc game servers
  40. */
  41. public static final String SOURCE_MASTER_SERVER = "hl2master.steampowered.com:27011";
  42. /**
  43. * The region code for the US east coast
  44. */
  45. public static final byte REGION_US_EAST_COAST = 0x00;
  46. /**
  47. * The region code for the US west coast
  48. */
  49. public static final byte REGION_US_WEST_COAST = 0x01;
  50. /**
  51. * The region code for South America
  52. */
  53. public static final byte REGION_SOUTH_AMERICA = 0x02;
  54. /**
  55. * The region code for Europe
  56. */
  57. public static final byte REGION_EUROPE = 0x03;
  58. /**
  59. * The region code for Asia
  60. */
  61. public static final byte REGION_ASIA = 0x04;
  62. /**
  63. * The region code for Australia
  64. */
  65. public static final byte REGION_AUSTRALIA = 0x05;
  66. /**
  67. * The region code for the Middle East
  68. */
  69. public static final byte REGION_MIDDLE_EAST = 0x06;
  70. /**
  71. * The region code for Africa
  72. */
  73. public static final byte REGION_AFRICA = 0x07;
  74. /**
  75. * The region code for the whole world
  76. */
  77. public static final byte REGION_ALL = (byte)0xFF;
  78. public static int retries = 3;
  79. private MasterServerSocket socket;
  80. /**
  81. * Sets the number of consecutive requests that may fail, before getting
  82. * the server list is cancelled (default: 3)
  83. *
  84. * @param newRetries The number of allowed retries
  85. */
  86. public static void setRetries(int newRetries) {
  87. retries = newRetries;
  88. }
  89. /**
  90. * Creates a new instance of a master server object
  91. *
  92. * @param address Either an IP address, a DNS name or one of them combined
  93. * with the port number. If a port number is given, e.g.
  94. * 'server.example.com:27016' it will override the second argument.
  95. * @throws SteamCondenserException if initializing the socket fails
  96. */
  97. public MasterServer(String address) throws SteamCondenserException {
  98. super(address, null);
  99. }
  100. /**
  101. * Creates a new instance of a master server object
  102. *
  103. * @param address Either an IP address, a DNS name or one of them combined
  104. * with the port number. If a port number is given, e.g.
  105. * 'server.example.com:27016' it will override the second argument.
  106. * @param port The port the server is listening on
  107. * @throws SteamCondenserException if initializing the socket fails
  108. */
  109. public MasterServer(String address, Integer port)
  110. throws SteamCondenserException {
  111. super(address, port);
  112. }
  113. /**
  114. * Creates a new instance of a GoldSrc server object
  115. *
  116. * @param address Either an IP address, a DNS name or one of them combined
  117. * with the port number. If a port number is given, e.g.
  118. * 'server.example.com:27016' it will override the second argument.
  119. * @throws SteamCondenserException if initializing the socket fails
  120. */
  121. public MasterServer(InetAddress address) throws SteamCondenserException {
  122. super(address.toString(), null);
  123. }
  124. /**
  125. * Creates a new instance of a master server object
  126. *
  127. * @param address Either an IP address, a DNS name or one of them combined
  128. * with the port number. If a port number is given, e.g.
  129. * 'server.example.com:27016' it will override the second argument.
  130. * @param port The port the server is listening on
  131. * @throws SteamCondenserException if initializing the socket fails
  132. */
  133. public MasterServer(InetAddress address, Integer port)
  134. throws SteamCondenserException {
  135. super(address.toString(), port);
  136. }
  137. /**
  138. * Request a challenge number from the master server.
  139. * <p/>
  140. * This is used for further communication with the master server.
  141. * <p/>
  142. * Please note that this is <strong>not</strong> needed for finding servers
  143. * using {@link #getServers}.
  144. *
  145. * @return The challenge number returned from the master server
  146. * @see #sendHeartbeat
  147. * @throws SteamCondenserException if the request fails
  148. * @throws TimeoutException if the request times out
  149. */
  150. public int getChallenge()
  151. throws SteamCondenserException, TimeoutException {
  152. this.socket.send(new C2M_CHECKMD5_Packet());
  153. return ((M2C_ISVALIDMD5_Packet) this.socket.getReply()).getChallenge();
  154. }
  155. /**
  156. * Returns a list of all available game servers
  157. * <p/>
  158. * <strong>Note:</strong> Receiving all servers from the master server is
  159. * taking quite some time.
  160. *
  161. * @return A list of game servers matching the given
  162. * region and filters
  163. * @see A2M_GET_SERVERS_BATCH2_Paket
  164. * @throws SteamCondenserException if the request fails
  165. * @throws TimeoutException if too many timeouts occur while querying the
  166. * master server
  167. */
  168. public Vector<InetSocketAddress> getServers()
  169. throws SteamCondenserException, TimeoutException {
  170. return this.getServers(MasterServer.REGION_ALL, "", false);
  171. }
  172. /**
  173. * Returns a list of game server matching the given region and filters
  174. * <p/>
  175. * Filtering:
  176. * Instead of filtering the results sent by the master server locally, you
  177. * should at least use the filters listed at {@link
  178. * MasterServer#getServers(byte, String, boolean)} to narrow down the
  179. * results sent by the master server.
  180. * <p/>
  181. * <strong>Note:</strong> Receiving all servers from the master server is
  182. * taking quite some time.
  183. *
  184. * @param regionCode The region code to specify a location of the game
  185. * servers
  186. * @param filter The filters that game servers should match
  187. * @return A list of game servers matching the given
  188. * region and filters
  189. * @see A2M_GET_SERVERS_BATCH2_Paket
  190. * @throws SteamCondenserException if the request fails
  191. * @throws TimeoutException if too many timeouts occur while querying the
  192. * master server
  193. */
  194. public Vector<InetSocketAddress> getServers(byte regionCode, String filter)
  195. throws SteamCondenserException, TimeoutException {
  196. return this.getServers(regionCode, filter, false);
  197. }
  198. /**
  199. * Returns a list of game server matching the given region and filters
  200. * <p/>
  201. * Filtering:
  202. * Instead of filtering the results sent by the master server locally, you
  203. * should at least use the following filters to narrow down the results
  204. * sent by the master server.
  205. * <p/>
  206. * <strong>Note:</strong> Receiving all servers from the master server is
  207. * taking quite some time.
  208. *
  209. * Available filters:
  210. *
  211. * <ul>
  212. * <li><code>\type\d</code>: Request only dedicated servers
  213. * <li><code>\secure\1</code>: Request only secure servers
  214. * <li><code>\gamedir\[mod]</code>: Request only servers of a specific mod
  215. * <li><code>\map\[mapname]</code>: Request only servers running a specific
  216. * map
  217. * <li><code>\linux\1</code>: Request only linux servers
  218. * <li><code>\emtpy\1</code>: Request only **non**-empty servers
  219. * <li><code>\full\</code>: Request only servers **not** full
  220. * <li><code>\proxy\1</code>: Request only spectator proxy servers
  221. * </ul>
  222. *
  223. * @param regionCode The region code to specify a location of the game
  224. * servers
  225. * @param filter The filters that game servers should match
  226. * @return A list of game servers matching the given
  227. * region and filters
  228. * @see A2M_GET_SERVERS_BATCH2_Paket
  229. * @throws SteamCondenserException if the request fails
  230. * @throws TimeoutException if too many timeouts occur while querying the
  231. * master server
  232. */
  233. public Vector<InetSocketAddress> getServers(byte regionCode, String filter, boolean force)
  234. throws SteamCondenserException, TimeoutException {
  235. int failCount = 0;
  236. boolean finished = false;
  237. int portNumber = 0;
  238. String hostName = "0.0.0.0";
  239. Vector<String> serverStringArray;
  240. Vector<InetSocketAddress> serverArray = new Vector<InetSocketAddress>();
  241. while(true) {
  242. try {
  243. do {
  244. this.socket.send(new A2M_GET_SERVERS_BATCH2_Paket(regionCode, hostName + ":" + portNumber, filter));
  245. try {
  246. serverStringArray = ((M2A_SERVER_BATCH_Paket) this.socket.getReply()).getServers();
  247. for(String serverString : serverStringArray) {
  248. hostName = serverString.substring(0, serverString.lastIndexOf(":"));
  249. portNumber = Integer.valueOf(serverString.substring(serverString.lastIndexOf(":") + 1));
  250. if(!hostName.equals("0.0.0.0") && portNumber != 0) {
  251. serverArray.add(new InetSocketAddress(hostName, portNumber));
  252. }
  253. else {
  254. finished = true;
  255. }
  256. }
  257. failCount = 0;
  258. } catch(TimeoutException e) {
  259. failCount ++;
  260. if(failCount == retries) {
  261. throw e;
  262. }
  263. }
  264. } while(!finished);
  265. break;
  266. } catch(TimeoutException e) {
  267. if(this.rotateIp() && !force) {
  268. throw e;
  269. }
  270. }
  271. }
  272. return serverArray;
  273. }
  274. /**
  275. * Initializes the socket to communicate with the master server
  276. *
  277. * @see MasterServerSocket
  278. */
  279. public void initSocket() throws SteamCondenserException {
  280. this.socket = new MasterServerSocket(this.ipAddress, this.port);
  281. }
  282. /**
  283. * Sends a constructed heartbeat to the master server
  284. * <p/>
  285. * This can be used to check server versions externally.
  286. *
  287. * @param data The heartbeat data to send to the master server
  288. * @return The reply from the master server – usually zero or more packets.
  289. * Zero means either the heartbeat was accepted by the master or
  290. * there was a timeout. So usually it's best to repeat a heartbeat
  291. * a few times when not receiving any packets.
  292. * @throws SteamCondenserException if the request fails
  293. */
  294. public List<SteamPacket> sendHeartbeat(Map<String, Object> data)
  295. throws SteamCondenserException {
  296. this.socket.send(new S2M_HEARTBEAT2_Packet(data));
  297. List<SteamPacket> replyPackets = new ArrayList<SteamPacket>();
  298. try {
  299. do {
  300. replyPackets.add(this.socket.getReply());
  301. } while(true);
  302. } catch(TimeoutException e) {}
  303. return replyPackets;
  304. }
  305. }