/* * IDK Game Server by Steve Winfield * https://github.com/WinfieldSteve */ package org.stevewinfield.suja.idk; import com.google.common.io.BaseEncoding; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.magicwerk.brownies.collections.GapList; import org.stevewinfield.placeholder.services.PlaceholderNetwork; import org.stevewinfield.suja.idk.communication.MessageHandler; import org.stevewinfield.suja.idk.communication.OperationCodes; import org.stevewinfield.suja.idk.dedicated.commands.ThreadedCommandReader; import org.stevewinfield.suja.idk.dedicated.commands.DedicatedServerCommandHandler; import org.stevewinfield.suja.idk.game.Game; import org.stevewinfield.suja.idk.game.plugins.PluginManager; import org.stevewinfield.suja.idk.network.ConnectionListener; import org.stevewinfield.suja.idk.network.sessions.Session; import org.stevewinfield.suja.idk.network.sessions.SessionManager; import org.stevewinfield.suja.idk.storage.Storage; import org.stevewinfield.suja.idk.threadpools.ServerTask; import org.stevewinfield.suja.idk.threadpools.WorkerTasks; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map.Entry; import java.util.Properties; public class Bootloader { private static final Logger logger = Logger.getLogger(Bootloader.class); public static Settings getSettings() { return settings; } public static SessionManager getSessionManager() { return sessionManager; } public static Game getGame() { return game; } public static PluginManager getPluginManager() { return pluginManager; } public static Storage getStorage() { return storage; } public static void main(final String[] args) { Properties buildProperties = new Properties(); try { buildProperties.load(Bootloader.class.getResourceAsStream("/build.properties")); } catch (final Exception ignored) { // Default Values of buildProperties are given at // the second parameter of Properties::getProperty } IDK.NAME = buildProperties.getProperty("name", "IDK"); IDK.BUILD_NUMBER = buildProperties.getProperty("build", "UNKNOWN"); IDK.VERSION = buildProperties.getProperty("version", "0.0.0"); PropertyConfigurator.configure("log4j.properties"); logger.info(IDK.NAME + " (Version " + IDK.VERSION + ", Build " + IDK.BUILD_NUMBER + ") is starting."); final Properties pFile = new Properties(); File propertiesFile = new File("server.properties"); if (!propertiesFile.exists()) { File oldPropertiesFile = new File(".properties"); if (oldPropertiesFile.exists()) { logger.info("Moving old .properties to server.properties"); if (!oldPropertiesFile.renameTo(propertiesFile)) { logger.warn("Failed to move .properties to server.properties. Please do this manually."); propertiesFile = oldPropertiesFile; } } } try { pFile.load(new FileInputStream(propertiesFile)); } catch (final FileNotFoundException e) { logger.error("File \"" + propertiesFile.getName() + "\" not found.", e); return; } catch (final IOException e) { logger.error("Couldn't read file \"" + propertiesFile.getName() + "\".", e); return; } settings = new Settings(pFile); logger.info("Properties file successfully read."); dedicatedServerCommandHandler = new DedicatedServerCommandHandler(); dedicatedServerCommandHandler.registerDefaultCommands(); ThreadedCommandReader commandReader = new ThreadedCommandReader("Server console handler", dedicatedServerCommandHandler); commandReader.setDaemon(true); commandReader.start(); final List<URL> classPath = new ArrayList<>(); File directoryFile = new File("libs"); if (!directoryFile.exists()) { directoryFile.mkdirs(); } if (!directoryFile.isDirectory()) { logger.warn("File \"libs\" is not a directory."); } else { for (File pathname : directoryFile.listFiles()) { if (pathname.isFile() && pathname.toString().toLowerCase().endsWith(".jar")) { try { classPath.add(pathname.toURI().toURL()); } catch (MalformedURLException e) { logger.error("Library error", e); } } } } customClassLoader = new URLClassLoader(classPath.toArray(new URL[classPath.size()])); storage = new Storage(); if (!storage.create()) { return; } logger.info("SQL Connection created and tested."); final String key = settings.getProperty("placeholder.network.unique.key"); final String decodedKey = new String(BaseEncoding.base64().decode(key)); placeholderNetwork = new PlaceholderNetwork(key, IDK.PLACEHOLDER_NETWORK, IDK.PLACEHOLDER_NETWORK_SALT); if (!placeholderNetwork.isConnected() || !placeholderNetwork.isAuthenticated()) { logger.error("Authentication failed, your server key is invalid."); Bootloader.exitServer(); return; } logger.info("Successfully connected!"); if (decodedKey.split("-").length != 3) { logger.error(IDK.NAME + " has broken down."); return; } try { OperationCodes.loadCodes(); logger.info("OperationCode(s) loaded."); } catch (final FileNotFoundException e) { logger.error("Couldn't found op-code file.", e); return; } catch (final IOException e) { logger.error("Couldn't read op-code file.", e); return; } try { Translations.loadTranslations(); logger.info(Translations.getCount() + " Translation(s) loaded"); } catch (final FileNotFoundException e) { logger.error("Couldn't found translations file.", e); return; } catch (final IOException e) { logger.error("Couldn't read translations file.", e); return; } try { IDK.loadSettings(); logger.info("GameSetting(s) loaded."); } catch (final Exception e) { logger.error("Couldn't read the game settings.", e); return; } MessageHandler.loadMessages(); game = new Game(); game.getCatalogManager().loadCache(); pluginManager = new PluginManager(); pluginManager.load(new File(IDK.SYSTEM_PLUGINS_PATH)); game.getBotManager().load(); if (!Bootloader.placeholderNetwork.loadPlugins()) { System.exit(0); } WorkerTasks.initWorkerTasks(1); // initialize worker tasks sessionManager = new SessionManager(Integer.valueOf(settings.getProperty("idk.game.maxconns", "2000"))); listener = new ConnectionListener(settings.getProperty("idk.game.host"), Integer.valueOf(settings.getProperty("idk.game.port"))); if (listener.tryListen()) { logger.info("Ready for connections (Debugging " + (IDK.DEBUG ? "enabled" : "disabled") + ")."); /** * Online-User updater (+ keeps mysql connection up) */ WorkerTasks.addTask(new ServerTask() { @Override public void run() { final GapList<Entry<String, String>> parameters = new GapList<>(); if (Bootloader.placeholderNetwork.analyticsPing(parameters)) { Bootloader.getStorage().executeQuery("UPDATE idk_settings SET `value`='" + Bootloader.getSessionManager().getActiveSessionCount() + "' WHERE `key`='system.user.online'"); return; } Bootloader.exitServer(); } }, 0, 60000); /** * Update plugins */ WorkerTasks.addTask(new ServerTask() { @Override public void run() { if (Bootloader.placeholderNetwork.loadPlugins(false)) { return; } Bootloader.exitServer(); } }, 600000); /** * Do all kind of stuff * * Check HC/VIP membership expiration * * Check Avatar Effects expiration * * .. */ WorkerTasks.addTask(new ServerTask() { @Override public void run() { for (final Session session : Bootloader.getSessionManager().getSessions()) { if (session == null || !session.isAuthenticated()) { continue; } session.getPlayerInstance().getInventory().checkEffectExpiry(session); } } }, 0, 5000); } } public static long getTimestamp() { return System.currentTimeMillis() / 1000L; } public static Date getDateFromTimestamp(final long timestamp) { return new Date(timestamp * 1000); } public static void exitServer() { Bootloader.exitServer(0); } public static void exitServer(final int status) { System.exit(status); } public static String getHashedString(final byte[] md5Array) { MessageDigest md; try { md = MessageDigest.getInstance("MD5"); } catch (final NoSuchAlgorithmException e) { return null; } md.update(md5Array); final BigInteger hash = new BigInteger(1, md.digest()); String result = hash.toString(16); while (result.length() < 32) { // 40 for SHA-1 result = "0" + result; } return result; } public static URLClassLoader getCustomClassLoader() { return customClassLoader; } public static Logger getLogger() { return logger; } // fields private static Settings settings; private static ConnectionListener listener; private static DedicatedServerCommandHandler dedicatedServerCommandHandler; private static SessionManager sessionManager; private static PluginManager pluginManager; private static Storage storage; private static PlaceholderNetwork placeholderNetwork; private static Game game; private static URLClassLoader customClassLoader; }