GameServerTable.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. /*
  2. * This program is free software: you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation, either version 3 of the License, or (at your option) any later
  5. * version.
  6. *
  7. * This program is distributed in the hope that it will be useful, but WITHOUT
  8. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  9. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  10. * details.
  11. *
  12. * You should have received a copy of the GNU General Public License along with
  13. * this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. package com.l2jserver.loginserver;
  16. import java.io.FileInputStream;
  17. import java.io.FileNotFoundException;
  18. import java.io.InputStream;
  19. import java.math.BigInteger;
  20. import java.net.InetAddress;
  21. import java.net.UnknownHostException;
  22. import java.security.GeneralSecurityException;
  23. import java.security.InvalidAlgorithmParameterException;
  24. import java.security.KeyPair;
  25. import java.security.KeyPairGenerator;
  26. import java.security.NoSuchAlgorithmException;
  27. import java.security.spec.RSAKeyGenParameterSpec;
  28. import java.sql.Connection;
  29. import java.sql.PreparedStatement;
  30. import java.sql.ResultSet;
  31. import java.sql.SQLException;
  32. import java.util.ArrayList;
  33. import java.util.Map;
  34. import java.util.Map.Entry;
  35. import java.util.logging.Level;
  36. import java.util.logging.Logger;
  37. import javolution.io.UTF8StreamReader;
  38. import javolution.util.FastMap;
  39. import javolution.xml.stream.XMLStreamConstants;
  40. import javolution.xml.stream.XMLStreamException;
  41. import javolution.xml.stream.XMLStreamReaderImpl;
  42. import com.l2jserver.L2DatabaseFactory;
  43. import com.l2jserver.loginserver.network.gameserverpackets.ServerStatus;
  44. import com.l2jserver.util.IPSubnet;
  45. import com.l2jserver.util.Rnd;
  46. /**
  47. *
  48. * @author KenM
  49. */
  50. public class GameServerTable
  51. {
  52. private static Logger _log = Logger.getLogger(GameServerTable.class.getName());
  53. private static GameServerTable _instance;
  54. // Server Names Config
  55. private static Map<Integer, String> _serverNames = new FastMap<Integer, String>();
  56. // Game Server Table
  57. private Map<Integer, GameServerInfo> _gameServerTable = new FastMap<Integer, GameServerInfo>().shared();
  58. // RSA Config
  59. private static final int KEYS_SIZE = 10;
  60. private KeyPair[] _keyPairs;
  61. public static void load() throws SQLException, GeneralSecurityException
  62. {
  63. synchronized (GameServerTable.class)
  64. {
  65. if (_instance == null)
  66. {
  67. _instance = new GameServerTable();
  68. }
  69. else
  70. {
  71. throw new IllegalStateException("Load can only be invoked a single time.");
  72. }
  73. }
  74. }
  75. public static GameServerTable getInstance()
  76. {
  77. return _instance;
  78. }
  79. public GameServerTable() throws SQLException, NoSuchAlgorithmException, InvalidAlgorithmParameterException
  80. {
  81. loadServerNames();
  82. _log.info("Loaded " + _serverNames.size() + " server names");
  83. loadRegisteredGameServers();
  84. _log.info("Loaded " + _gameServerTable.size() + " registered Game Servers");
  85. loadRSAKeys();
  86. _log.info("Cached " + _keyPairs.length + " RSA keys for Game Server communication.");
  87. }
  88. private void loadRSAKeys() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException
  89. {
  90. KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
  91. RSAKeyGenParameterSpec spec = new RSAKeyGenParameterSpec(512, RSAKeyGenParameterSpec.F4);
  92. keyGen.initialize(spec);
  93. _keyPairs = new KeyPair[KEYS_SIZE];
  94. for (int i = 0; i < KEYS_SIZE; i++)
  95. {
  96. _keyPairs[i] = keyGen.genKeyPair();
  97. }
  98. }
  99. private void loadServerNames()
  100. {
  101. InputStream in = null;
  102. try
  103. {
  104. in = new FileInputStream("data/servername.xml");
  105. XMLStreamReaderImpl xpp = new XMLStreamReaderImpl();
  106. xpp.setInput(new UTF8StreamReader().setInput(in));
  107. for (int e = xpp.getEventType(); e != XMLStreamConstants.END_DOCUMENT; e = xpp.next())
  108. {
  109. if (e == XMLStreamConstants.START_ELEMENT)
  110. {
  111. if (xpp.getLocalName().toString().equals("server"))
  112. {
  113. Integer id = Integer.valueOf(xpp.getAttributeValue(null, "id").toString());
  114. String name = xpp.getAttributeValue(null, "name").toString();
  115. _serverNames.put(id, name);
  116. }
  117. }
  118. }
  119. }
  120. catch (FileNotFoundException e)
  121. {
  122. _log.warning("servername.xml could not be loaded: file not found");
  123. }
  124. catch (XMLStreamException xppe)
  125. {
  126. xppe.printStackTrace();
  127. }
  128. finally
  129. {
  130. try
  131. {
  132. in.close();
  133. }
  134. catch (Exception e)
  135. {
  136. }
  137. }
  138. }
  139. private void loadRegisteredGameServers() throws SQLException
  140. {
  141. Connection con = null;
  142. PreparedStatement statement = null;
  143. int id;
  144. con = L2DatabaseFactory.getInstance().getConnection();
  145. statement = con.prepareStatement("SELECT * FROM gameservers");
  146. ResultSet rset = statement.executeQuery();
  147. GameServerInfo gsi;
  148. while (rset.next())
  149. {
  150. id = rset.getInt("server_id");
  151. gsi = new GameServerInfo(id, stringToHex(rset.getString("hexid")));
  152. _gameServerTable.put(id, gsi);
  153. }
  154. rset.close();
  155. statement.close();
  156. L2DatabaseFactory.close(con);
  157. }
  158. public Map<Integer, GameServerInfo> getRegisteredGameServers()
  159. {
  160. return _gameServerTable;
  161. }
  162. public GameServerInfo getRegisteredGameServerById(int id)
  163. {
  164. return _gameServerTable.get(id);
  165. }
  166. public boolean hasRegisteredGameServerOnId(int id)
  167. {
  168. return _gameServerTable.containsKey(id);
  169. }
  170. public boolean registerWithFirstAvaliableId(GameServerInfo gsi)
  171. {
  172. // avoid two servers registering with the same "free" id
  173. synchronized (_gameServerTable)
  174. {
  175. for (Entry<Integer, String> entry : _serverNames.entrySet())
  176. {
  177. if (!_gameServerTable.containsKey(entry.getKey()))
  178. {
  179. _gameServerTable.put(entry.getKey(), gsi);
  180. gsi.setId(entry.getKey());
  181. return true;
  182. }
  183. }
  184. }
  185. return false;
  186. }
  187. public boolean register(int id, GameServerInfo gsi)
  188. {
  189. // avoid two servers registering with the same id
  190. synchronized (_gameServerTable)
  191. {
  192. if (!_gameServerTable.containsKey(id))
  193. {
  194. _gameServerTable.put(id, gsi);
  195. gsi.setId(id);
  196. return true;
  197. }
  198. }
  199. return false;
  200. }
  201. public void registerServerOnDB(GameServerInfo gsi)
  202. {
  203. this.registerServerOnDB(gsi.getHexId(), gsi.getId(), gsi.getExternalHost());
  204. }
  205. public void registerServerOnDB(byte[] hexId, int id, String externalHost)
  206. {
  207. Connection con = null;
  208. PreparedStatement statement = null;
  209. try
  210. {
  211. con = L2DatabaseFactory.getInstance().getConnection();
  212. statement = con.prepareStatement("INSERT INTO gameservers (hexid,server_id,host) values (?,?,?)");
  213. statement.setString(1, hexToString(hexId));
  214. statement.setInt(2, id);
  215. statement.setString(3, externalHost);
  216. statement.executeUpdate();
  217. statement.close();
  218. this.register(id, new GameServerInfo(id, hexId));
  219. }
  220. catch (SQLException e)
  221. {
  222. _log.log(Level.SEVERE, "SQL error while saving gameserver.", e);
  223. }
  224. finally
  225. {
  226. L2DatabaseFactory.close(con);
  227. }
  228. }
  229. public String getServerNameById(int id)
  230. {
  231. return getServerNames().get(id);
  232. }
  233. public Map<Integer, String> getServerNames()
  234. {
  235. return _serverNames;
  236. }
  237. public KeyPair getKeyPair()
  238. {
  239. return _keyPairs[Rnd.nextInt(10)];
  240. }
  241. private byte[] stringToHex(String string)
  242. {
  243. return new BigInteger(string, 16).toByteArray();
  244. }
  245. private String hexToString(byte[] hex)
  246. {
  247. if (hex == null)
  248. {
  249. return "null";
  250. }
  251. return new BigInteger(hex).toString(16);
  252. }
  253. public static class GameServerInfo
  254. {
  255. // auth
  256. private int _id;
  257. private byte[] _hexId;
  258. private boolean _isAuthed;
  259. // status
  260. private GameServerThread _gst;
  261. private int _status;
  262. // network
  263. private ArrayList<GameServerAddress> _addrs = new ArrayList<GameServerAddress>(5);
  264. private int _port;
  265. // config
  266. private boolean _isPvp = true;
  267. private int _serverType;
  268. private int _ageLimit;
  269. private boolean _isShowingBrackets;
  270. private int _maxPlayers;
  271. public GameServerInfo(int id, byte[] hexId, GameServerThread gst)
  272. {
  273. _id = id;
  274. _hexId = hexId;
  275. _gst = gst;
  276. _status = ServerStatus.STATUS_DOWN;
  277. }
  278. public GameServerInfo(int id, byte[] hexId)
  279. {
  280. this(id, hexId, null);
  281. }
  282. public void setId(int id)
  283. {
  284. _id = id;
  285. }
  286. public int getId()
  287. {
  288. return _id;
  289. }
  290. public byte[] getHexId()
  291. {
  292. return _hexId;
  293. }
  294. public void setAuthed(boolean isAuthed)
  295. {
  296. _isAuthed = isAuthed;
  297. }
  298. public boolean isAuthed()
  299. {
  300. return _isAuthed;
  301. }
  302. public void setGameServerThread(GameServerThread gst)
  303. {
  304. _gst = gst;
  305. }
  306. public GameServerThread getGameServerThread()
  307. {
  308. return _gst;
  309. }
  310. public void setStatus(int status)
  311. {
  312. _status = status;
  313. }
  314. public int getStatus()
  315. {
  316. return _status;
  317. }
  318. public int getCurrentPlayerCount()
  319. {
  320. if (_gst == null)
  321. return 0;
  322. return _gst.getPlayerCount();
  323. }
  324. public String getExternalHost()
  325. {
  326. try
  327. {
  328. return getServerAddress(InetAddress.getByName("0.0.0.0"));
  329. }
  330. catch (Exception e)
  331. {
  332. }
  333. return null;
  334. }
  335. public int getPort()
  336. {
  337. return _port;
  338. }
  339. public void setPort(int port)
  340. {
  341. _port = port;
  342. }
  343. public void setMaxPlayers(int maxPlayers)
  344. {
  345. _maxPlayers = maxPlayers;
  346. }
  347. public int getMaxPlayers()
  348. {
  349. return _maxPlayers;
  350. }
  351. public boolean isPvp()
  352. {
  353. return _isPvp;
  354. }
  355. public void setAgeLimit(int val)
  356. {
  357. _ageLimit = val;
  358. }
  359. public int getAgeLimit()
  360. {
  361. return _ageLimit;
  362. }
  363. public void setServerType(int val)
  364. {
  365. _serverType = val;
  366. }
  367. public int getServerType()
  368. {
  369. return _serverType;
  370. }
  371. public void setShowingBrackets(boolean val)
  372. {
  373. _isShowingBrackets = val;
  374. }
  375. public boolean isShowingBrackets()
  376. {
  377. return _isShowingBrackets;
  378. }
  379. public void setDown()
  380. {
  381. setAuthed(false);
  382. setPort(0);
  383. setGameServerThread(null);
  384. setStatus(ServerStatus.STATUS_DOWN);
  385. }
  386. public void addServerAddress(String subnet, String addr) throws UnknownHostException
  387. {
  388. _addrs.add(new GameServerAddress(subnet, addr));
  389. }
  390. public String getServerAddress(InetAddress addr)
  391. {
  392. for (GameServerAddress a : _addrs)
  393. {
  394. if (a.equals(addr))
  395. return a.getServerAddress();
  396. }
  397. return null; // should not happens
  398. }
  399. public String[] getServerAddresses()
  400. {
  401. String[] result = new String[_addrs.size()];
  402. for (int i = 0; i < result.length; i++)
  403. result[i] = _addrs.get(i).toString();
  404. return result;
  405. }
  406. public void clearServerAddresses()
  407. {
  408. _addrs.clear();
  409. }
  410. private class GameServerAddress extends IPSubnet
  411. {
  412. private String _serverAddress;
  413. public GameServerAddress(String subnet, String address) throws UnknownHostException
  414. {
  415. super(subnet);
  416. _serverAddress = address;
  417. }
  418. public String getServerAddress()
  419. {
  420. return _serverAddress;
  421. }
  422. @Override
  423. public String toString()
  424. {
  425. return _serverAddress + super.toString();
  426. }
  427. }
  428. }
  429. }