package emu.nebula; import java.io.*; import java.text.SimpleDateFormat; import java.util.Date; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import emu.nebula.command.CommandManager; import emu.nebula.data.ResourceLoader; import emu.nebula.database.DatabaseManager; import emu.nebula.game.GameContext; import emu.nebula.net.PacketHelper; import emu.nebula.server.HttpServer; import emu.nebula.util.Handbook; import emu.nebula.util.JsonUtils; import lombok.Getter; public class Nebula { private static final Logger log = LoggerFactory.getLogger(Nebula.class); // Config private static final File configFile = new File("./config.json"); @Getter private static Config config; // Database @Getter private static DatabaseManager accountDatabase; @Getter private static DatabaseManager gameDatabase; // Server @Getter private static HttpServer httpServer; @Getter private static HttpServer gameServer; // TODO @Getter private static ServerType serverType = ServerType.BOTH; @Getter private static GameContext gameContext; @Getter private static CommandManager commandManager; public static void main(String[] args) { // Start Server Nebula.getLogger().info("Starting Nebula " + getJarVersion()); Nebula.getLogger().info("Git hash: " + getGitHash()); Nebula.getLogger().info("Game version: " + GameConstants.VERSION); boolean generateHandbook = true; // Load config + commands Nebula.loadConfig(); // Parse arguments for (String arg : args) { switch (arg) { case "-login": serverType = ServerType.LOGIN; break; case "-game": serverType = ServerType.GAME; break; case "-nohandbook": case "-skiphandbook": generateHandbook = false; break; case "-database": // Database only DatabaseManager.startInternalMongoServer(Nebula.getConfig().getInternalMongoServer()); Nebula.getLogger().info("Running local Mongo server at " + DatabaseManager.getServer().getConnectionString()); return; } } // Skip these if we are only running the http server in dispatch mode if (serverType.runGame()) { // Load resources ResourceLoader.loadAll(); // Generate handbook if (generateHandbook) { Handbook.generate(); } // Cache proto methods PacketHelper.cacheProtos(); } try { // Start Database(s) Nebula.initDatabases(); } catch (Exception exception) { Nebula.getLogger().error("Unable to start the database(s).", exception); } // Start game context Nebula.gameContext = new GameContext(); Nebula.commandManager = new CommandManager(); // Start servers try { // Always run http server as it is needed by for dispatch and gateserver httpServer = new HttpServer(serverType); httpServer.start(); } catch (Exception exception) { Nebula.getLogger().error("Unable to start the HTTP server.", exception); } // Start console Nebula.startConsole(); } public static Logger getLogger() { return log; } // Database private static void initDatabases() { if (Nebula.getConfig().useSameDatabase) { // Setup account and game database accountDatabase = new DatabaseManager(Nebula.getConfig().getAccountDatabase(), serverType); // Optimization: Dont run a 2nd database manager if we are not running a gameserver if (serverType.runGame()) { gameDatabase = accountDatabase; } } else { // Run separate databases accountDatabase = new DatabaseManager(Nebula.getConfig().getAccountDatabase(), ServerType.LOGIN); // Optimization: Dont run a 2nd database manager if we are not running a gameserver if (serverType.runGame()) { gameDatabase = new DatabaseManager(Nebula.getConfig().getGameDatabase(), ServerType.GAME); } } } // Config public static void loadConfig() { // Load from file try (FileReader file = new FileReader(configFile)) { Nebula.config = JsonUtils.loadToClass(file, Config.class); } catch (Exception e) { // Ignored } // Sanity check if (Nebula.getConfig() == null) { Nebula.config = new Config(); } // Save config Nebula.saveConfig(); } public static void saveConfig() { try (FileWriter file = new FileWriter(configFile)) { Gson gson = new GsonBuilder() .setDateFormat("dd-MM-yyyy hh:mm:ss") .setPrettyPrinting() .serializeNulls() .create(); file.write(gson.toJson(config)); } catch (Exception e) { getLogger().error("Config save error"); } } // Build Config private static String getJarVersion() { // Safely get the build config class without errors even if it hasnt been generated yet try { Class buildConfig = Class.forName(Nebula.class.getPackageName() + ".BuildConfig"); return buildConfig.getField("VERSION").get(null).toString(); } catch (Exception e) { // Ignored } return ""; } public static String getGitHash() { // Use a string builder in case one of the build config fields are missing StringBuilder builder = new StringBuilder(); // Safely get the build config class without errors even if it hasnt been generated yet try { SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Class buildConfig = Class.forName(Nebula.class.getPackageName() + ".BuildConfig"); String hash = buildConfig.getField("GIT_HASH").get(null).toString(); builder.append(hash); String timestamp = buildConfig.getField("GIT_TIMESTAMP").get(null).toString(); long time = Long.parseLong(timestamp) * 1000; builder.append(" (" + sf.format(new Date(time)) + ")"); } catch (Exception e) { // Ignored } if (builder.isEmpty()) { return "UNKNOWN"; } else { return builder.toString(); } } // Utils /** * Returns the current server time in seconds */ public static long getCurrentTime() { return System.currentTimeMillis() / 1000; } // Console private static void startConsole() { String input; try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) { while ((input = br.readLine()) != null) { Nebula.getCommandManager().invoke(null, input); } } catch (Exception e) { Nebula.getLogger().error("Console error:", e); } } // Server enums public enum ServerType { LOGIN (0x1), GAME (0x2), BOTH (0x3); private final int flags; ServerType(int flags) { this.flags = flags; } public boolean runLogin() { return (this.flags & 0x1) == 0x1; } public boolean runGame() { return (this.flags & 0x2) == 0x2; } } }