/* * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ package com.l2jserver.loginserver; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.math.BigInteger; import java.net.InetAddress; import java.net.UnknownHostException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.spec.RSAKeyGenParameterSpec; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.logging.Logger; import javolution.io.UTF8StreamReader; import javolution.xml.stream.XMLStreamConstants; import javolution.xml.stream.XMLStreamReaderImpl; import com.l2jserver.Config; import com.l2jserver.L2DatabaseFactory; import com.l2jserver.loginserver.network.gameserverpackets.ServerStatus; import com.l2jserver.util.IPSubnet; import com.l2jserver.util.Rnd; /** * The Class GameServerTable loads the game server names and initialize the game server tables. * @author KenM, Zoey76 */ public final class GameServerTable { private static final Logger _log = Logger.getLogger(GameServerTable.class.getName()); // Server Names Config private static final Map _serverNames = new HashMap<>(); // Game Server Table private static final Map _gameServerTable = new HashMap<>(); // RSA Config private static final int KEYS_SIZE = 10; private KeyPair[] _keyPairs; /** * Instantiates a new game server table. */ public GameServerTable() { loadGameServerNames(); _log.info(getClass().getSimpleName() + ": Loaded " + _serverNames.size() + " server names"); loadRegisteredGameServers(); _log.info(getClass().getSimpleName() + ": Loaded " + _gameServerTable.size() + " registered Game Servers"); initRSAKeys(); _log.info(getClass().getSimpleName() + ": Cached " + _keyPairs.length + " RSA keys for Game Server communication."); } /** * Load game server names. */ private void loadGameServerNames() { final File xml = new File(Config.DATAPACK_ROOT, "data/servername.xml"); try (InputStream in = new FileInputStream(xml); UTF8StreamReader utf8 = new UTF8StreamReader()) { final XMLStreamReaderImpl xpp = new XMLStreamReaderImpl(); xpp.setInput(utf8.setInput(in)); for (int e = xpp.getEventType(); e != XMLStreamConstants.END_DOCUMENT; e = xpp.next()) { if (e == XMLStreamConstants.START_ELEMENT) { if (xpp.getLocalName().toString().equals("server")) { Integer id = Integer.valueOf(xpp.getAttributeValue(null, "id").toString()); String name = xpp.getAttributeValue(null, "name").toString(); _serverNames.put(id, name); } } } xpp.close(); } catch (Exception e) { _log.info(getClass().getSimpleName() + ": Cannot load " + xml.getAbsolutePath() + "!"); } } /** * Inits the RSA keys. */ private void initRSAKeys() { try { final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(new RSAKeyGenParameterSpec(512, RSAKeyGenParameterSpec.F4)); _keyPairs = new KeyPair[KEYS_SIZE]; for (int i = 0; i < KEYS_SIZE; i++) { _keyPairs[i] = keyGen.genKeyPair(); } } catch (Exception e) { _log.severe(getClass().getSimpleName() + ": Error loading RSA keys for Game Server communication!"); } } /** * Load registered game servers. */ private void loadRegisteredGameServers() { Connection con = null; try { con = L2DatabaseFactory.getInstance().getConnection(); try (Statement ps = con.createStatement(); ResultSet rs = ps.executeQuery("SELECT * FROM gameservers")) { int id; while (rs.next()) { id = rs.getInt("server_id"); _gameServerTable.put(id, new GameServerInfo(id, stringToHex(rs.getString("hexid")))); } } } catch (Exception e) { _log.severe(getClass().getSimpleName() + ": Error loading registered game servers!"); } finally { L2DatabaseFactory.close(con); } } /** * Gets the registered game servers. * @return the registered game servers */ public Map getRegisteredGameServers() { return _gameServerTable; } /** * Gets the registered game server by id. * @param id the game server Id * @return the registered game server by id */ public GameServerInfo getRegisteredGameServerById(int id) { return _gameServerTable.get(id); } /** * Checks for registered game server on id. * @param id the id * @return true, if successful */ public boolean hasRegisteredGameServerOnId(int id) { return _gameServerTable.containsKey(id); } /** * Register with first available id. * @param gsi the game server information DTO * @return true, if successful */ public boolean registerWithFirstAvaliableId(GameServerInfo gsi) { // avoid two servers registering with the same "free" id synchronized (_gameServerTable) { for (Entry entry : _serverNames.entrySet()) { if (!_gameServerTable.containsKey(entry.getKey())) { _gameServerTable.put(entry.getKey(), gsi); gsi.setId(entry.getKey()); return true; } } } return false; } /** * Register a game server. * @param id the id * @param gsi the gsi * @return true, if successful */ public boolean register(int id, GameServerInfo gsi) { // avoid two servers registering with the same id synchronized (_gameServerTable) { if (!_gameServerTable.containsKey(id)) { _gameServerTable.put(id, gsi); gsi.setId(id); return true; } } return false; } /** * Wrapper method. * @param gsi the game server info DTO. */ public void registerServerOnDB(GameServerInfo gsi) { registerServerOnDB(gsi.getHexId(), gsi.getId(), gsi.getExternalHost()); } /** * Register server on db. * @param hexId the hex id * @param id the id * @param externalHost the external host */ public void registerServerOnDB(byte[] hexId, int id, String externalHost) { Connection con = null; try { con = L2DatabaseFactory.getInstance().getConnection(); try (PreparedStatement ps = con.prepareStatement("INSERT INTO gameservers (hexid,server_id,host) values (?,?,?)")) { ps.setString(1, hexToString(hexId)); ps.setInt(2, id); ps.setString(3, externalHost); ps.executeUpdate(); } register(id, new GameServerInfo(id, hexId)); } catch (Exception e) { _log.severe(getClass().getSimpleName() + ": Error while saving gameserver!"); } finally { L2DatabaseFactory.close(con); } } /** * Gets the server name by id. * @param id the id * @return the server name by id */ public String getServerNameById(int id) { return _serverNames.get(id); } /** * Gets the server names. * @return the game server names map. */ public Map getServerNames() { return _serverNames; } /** * Gets the key pair. * @return a random key pair. */ public KeyPair getKeyPair() { return _keyPairs[Rnd.nextInt(10)]; } /** * String to hex. * @param string the string to convert. * @return return the hex representation. */ private byte[] stringToHex(String string) { return new BigInteger(string, 16).toByteArray(); } /** * Hex to string. * @param hex the hex value to convert. * @return the string representation. */ private String hexToString(byte[] hex) { if (hex == null) { return "null"; } return new BigInteger(hex).toString(16); } /** * The Class GameServerInfo. */ public static class GameServerInfo { // auth private int _id; private final byte[] _hexId; private boolean _isAuthed; // status private GameServerThread _gst; private int _status; // network private final ArrayList _addrs = new ArrayList<>(5); private int _port; // config private final boolean _isPvp = true; private int _serverType; private int _ageLimit; private boolean _isShowingBrackets; private int _maxPlayers; /** * Instantiates a new game server info. * @param id the id * @param hexId the hex id * @param gst the gst */ public GameServerInfo(int id, byte[] hexId, GameServerThread gst) { _id = id; _hexId = hexId; _gst = gst; _status = ServerStatus.STATUS_DOWN; } /** * Instantiates a new game server info. * @param id the id * @param hexId the hex id */ public GameServerInfo(int id, byte[] hexId) { this(id, hexId, null); } /** * Sets the id. * @param id the new id */ public void setId(int id) { _id = id; } /** * Gets the id. * @return the id */ public int getId() { return _id; } /** * Gets the hex id. * @return the hex id */ public byte[] getHexId() { return _hexId; } /** * Sets the authed. * @param isAuthed the new authed */ public void setAuthed(boolean isAuthed) { _isAuthed = isAuthed; } /** * Checks if is authed. * @return true, if is authed */ public boolean isAuthed() { return _isAuthed; } /** * Sets the game server thread. * @param gst the new game server thread */ public void setGameServerThread(GameServerThread gst) { _gst = gst; } /** * Gets the game server thread. * @return the game server thread */ public GameServerThread getGameServerThread() { return _gst; } /** * Sets the status. * @param status the new status */ public void setStatus(int status) { _status = status; } /** * Gets the status. * @return the status */ public int getStatus() { return _status; } /** * Gets the current player count. * @return the current player count */ public int getCurrentPlayerCount() { if (_gst == null) { return 0; } return _gst.getPlayerCount(); } /** * Gets the external host. * @return the external host */ public String getExternalHost() { try { return getServerAddress(InetAddress.getByName("0.0.0.0")); } catch (Exception e) { } return null; } /** * Gets the port. * @return the port */ public int getPort() { return _port; } /** * Sets the port. * @param port the new port */ public void setPort(int port) { _port = port; } /** * Sets the max players. * @param maxPlayers the new max players */ public void setMaxPlayers(int maxPlayers) { _maxPlayers = maxPlayers; } /** * Gets the max players. * @return the max players */ public int getMaxPlayers() { return _maxPlayers; } /** * Checks if is pvp. * @return true, if is pvp */ public boolean isPvp() { return _isPvp; } /** * Sets the age limit. * @param val the new age limit */ public void setAgeLimit(int val) { _ageLimit = val; } /** * Gets the age limit. * @return the age limit */ public int getAgeLimit() { return _ageLimit; } /** * Sets the server type. * @param val the new server type */ public void setServerType(int val) { _serverType = val; } /** * Gets the server type. * @return the server type */ public int getServerType() { return _serverType; } /** * Sets the showing brackets. * @param val the new showing brackets */ public void setShowingBrackets(boolean val) { _isShowingBrackets = val; } /** * Checks if is showing brackets. * @return true, if is showing brackets */ public boolean isShowingBrackets() { return _isShowingBrackets; } /** * Sets the down. */ public void setDown() { setAuthed(false); setPort(0); setGameServerThread(null); setStatus(ServerStatus.STATUS_DOWN); } /** * Adds the server address. * @param subnet the subnet * @param addr the addr * @throws UnknownHostException the unknown host exception */ public void addServerAddress(String subnet, String addr) throws UnknownHostException { _addrs.add(new GameServerAddress(subnet, addr)); } /** * Gets the server address. * @param addr the addr * @return the server address */ public String getServerAddress(InetAddress addr) { for (GameServerAddress a : _addrs) { if (a.equals(addr)) { return a.getServerAddress(); } } return null; // should not happens } /** * Gets the server addresses. * @return the server addresses */ public String[] getServerAddresses() { String[] result = new String[_addrs.size()]; for (int i = 0; i < result.length; i++) { result[i] = _addrs.get(i).toString(); } return result; } /** * Clear server addresses. */ public void clearServerAddresses() { _addrs.clear(); } /** * The Class GameServerAddress. */ private class GameServerAddress extends IPSubnet { private final String _serverAddress; /** * Instantiates a new game server address. * @param subnet the subnet * @param address the address * @throws UnknownHostException the unknown host exception */ public GameServerAddress(String subnet, String address) throws UnknownHostException { super(subnet); _serverAddress = address; } /** * Gets the server address. * @return the server address */ public String getServerAddress() { return _serverAddress; } @Override public String toString() { return _serverAddress + super.toString(); } } } /** * Gets the single instance of GameServerTable. * @return single instance of GameServerTable */ public static GameServerTable getInstance() { return SingletonHolder._instance; } /** * The Class SingletonHolder. */ private static class SingletonHolder { protected static final GameServerTable _instance = new GameServerTable(); } }