/*
* 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 <http://www.gnu.org/licenses/>.
*/
package silentium.authserver;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.math.BigInteger;
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.SQLException;
import java.sql.Statement;
import java.util.Map;
import java.util.Map.Entry;
import javolution.io.UTF8StreamReader;
import javolution.util.FastMap;
import javolution.xml.stream.XMLStreamConstants;
import javolution.xml.stream.XMLStreamReaderImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import silentium.authserver.network.gameserverpackets.ServerStatus;
import silentium.commons.database.DatabaseFactory;
import silentium.commons.utils.Rnd;
/**
* @author KenM
*/
public class GameServerTable {
private static final Logger _log = LoggerFactory.getLogger(GameServerTable.class.getName());
// Server Names Config
private static final Map<Integer, String> _serverNames = new FastMap<>();
// Game Server Table
private final Map<Integer, GameServerInfo> _gameServerTable = new FastMap<Integer, GameServerInfo>().shared();
// RSA Config
private static final int KEYS_SIZE = 10;
private KeyPair[] _keyPairs;
protected GameServerTable() {
loadServerNames();
_log.info("Loaded " + _serverNames.size() + " server names.");
loadRegisteredGameServers();
_log.info("Loaded " + _gameServerTable.size() + " registered gameserver(s).");
initRSAKeys();
_log.info("Cached " + _keyPairs.length + " RSA keys for gameserver communication.");
}
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.error(getClass().getSimpleName() + ": Error loading RSA keys for Game Server communication!", e);
}
}
private static void loadServerNames() {
try (InputStream in = new FileInputStream("./config/servername.xml");
InputStream bis = new BufferedInputStream(in);
UTF8StreamReader utf8 = new UTF8StreamReader()) {
final XMLStreamReaderImpl xpp = new XMLStreamReaderImpl();
xpp.setInput(utf8.setInput(bis));
for (int e = xpp.getEventType(); e != XMLStreamConstants.END_DOCUMENT; e = xpp.next()) {
if (e == XMLStreamConstants.START_ELEMENT) {
if ("server".equals(xpp.getLocalName().toString())) {
final int id = Integer.parseInt(xpp.getAttributeValue(null, "id").toString());
final String name = xpp.getAttributeValue(null, "name").toString();
_serverNames.put(id, name);
}
}
}
xpp.close();
} catch (Exception e) {
_log.error("servername.xml could not be loaded.");
}
}
private void loadRegisteredGameServers() {
try (Connection con = DatabaseFactory.getConnection();
final Statement statement = con.createStatement();
final ResultSet rset = statement.executeQuery("SELECT * FROM gameservers")) {
int id;
while (rset.next()) {
id = rset.getInt("server_id");
_gameServerTable.put(id, new GameServerInfo(id, stringToHex(rset.getString("hexid"))));
}
} catch (Exception e) {
_log.error(getClass().getSimpleName() + ": Error loading registered game servers!", e);
}
}
public Map<Integer, GameServerInfo> getRegisteredGameServers() {
return _gameServerTable;
}
public GameServerInfo getRegisteredGameServerById(final int id) {
return _gameServerTable.get(id);
}
public boolean hasRegisteredGameServerOnId(final int id) {
return _gameServerTable.containsKey(id);
}
public boolean registerWithFirstAvaliableId(final GameServerInfo gsi) {
// avoid two servers registering with the same "free" id
synchronized (_gameServerTable) {
for (final Entry<Integer, String> entry : _serverNames.entrySet()) {
if (!_gameServerTable.containsKey(entry.getKey())) {
_gameServerTable.put(entry.getKey(), gsi);
gsi.setId(entry.getKey());
return true;
}
}
}
return false;
}
public boolean register(final int id, final 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;
}
public void registerServerOnDB(final GameServerInfo gsi) {
registerServerOnDB(gsi.getHexId(), gsi.getId(), gsi.getExternalHost());
}
public void registerServerOnDB(final byte[] hexId, final int id, final String externalHost) {
try (Connection con = DatabaseFactory.getConnection();
final PreparedStatement statement = con.prepareStatement("INSERT INTO gameservers (hexid,server_id,host) values (?,?,?)");) {
statement.setString(1, hexToString(hexId));
statement.setInt(2, id);
statement.setString(3, externalHost);
statement.executeUpdate();
} catch (SQLException e) {
_log.warn("SQL error while saving gameserver: " + e);
}
}
public String getServerNameById(final int id) {
return _serverNames.get(id);
}
public Map<Integer, String> getServerNames() {
return _serverNames;
}
public KeyPair getKeyPair() {
return _keyPairs[Rnd.nextInt(10)];
}
private static byte[] stringToHex(final String string) {
return new BigInteger(string, 16).toByteArray();
}
private static String hexToString(final byte... hex) {
if (hex == null)
return "null";
return new BigInteger(hex).toString(16);
}
public static class GameServerInfo {
// auth
private int _id;
private final byte[] _hexId;
private boolean _isAuthed;
// status
private GameServerThread _gst;
private int _status;
// network
private String _internalIp;
private String _externalIp;
private String _externalHost;
private int _port;
// config
private final boolean _isPvp = true;
private boolean _isTestServer;
private boolean _isShowingClock;
private boolean _isShowingBrackets;
private int _maxPlayers;
public GameServerInfo(final int id, final byte[] hexId, final GameServerThread gst) {
_id = id;
_hexId = hexId;
_gst = gst;
_status = ServerStatus.STATUS_DOWN;
}
public GameServerInfo(final int id, final byte... hexId) {
this(id, hexId, null);
}
public void setId(final int id) {
_id = id;
}
public int getId() {
return _id;
}
public byte[] getHexId() {
return _hexId;
}
public void setAuthed(final boolean isAuthed) {
_isAuthed = isAuthed;
}
public boolean isAuthed() {
return _isAuthed;
}
public void setGameServerThread(final GameServerThread gst) {
_gst = gst;
}
public GameServerThread getGameServerThread() {
return _gst;
}
public void setStatus(final int status) {
_status = status;
}
public int getStatus() {
return _status;
}
public int getCurrentPlayerCount() {
if (_gst == null)
return 0;
return _gst.getPlayerCount();
}
public void setInternalIp(final String internalIp) {
_internalIp = internalIp;
}
public String getInternalHost() {
return _internalIp;
}
public void setExternalIp(final String externalIp) {
_externalIp = externalIp;
}
public String getExternalIp() {
return _externalIp;
}
public void setExternalHost(final String externalHost) {
_externalHost = externalHost;
}
public String getExternalHost() {
return _externalHost;
}
public int getPort() {
return _port;
}
public void setPort(final int port) {
_port = port;
}
public void setMaxPlayers(final int maxPlayers) {
_maxPlayers = maxPlayers;
}
public int getMaxPlayers() {
return _maxPlayers;
}
public boolean isPvp() {
return _isPvp;
}
public void setTestServer(final boolean val) {
_isTestServer = val;
}
public boolean isTestServer() {
return _isTestServer;
}
public void setShowingClock(final boolean clock) {
_isShowingClock = clock;
}
public boolean isShowingClock() {
return _isShowingClock;
}
public void setShowingBrackets(final boolean val) {
_isShowingBrackets = val;
}
public boolean isShowingBrackets() {
return _isShowingBrackets;
}
public void setDown() {
_isAuthed = false;
_port = 0;
_gst = null;
_status = ServerStatus.STATUS_DOWN;
}
}
/**
* 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();
}
}