package uulm.teamname.marvelous.server; import com.beust.jcommander.JCommander; import com.beust.jcommander.ParameterException; import org.tinylog.Logger; import org.tinylog.configuration.Configuration; import uulm.teamname.marvelous.gamelibrary.config.CharacterConfig; import uulm.teamname.marvelous.gamelibrary.config.FieldType; import uulm.teamname.marvelous.gamelibrary.config.PartyConfig; import uulm.teamname.marvelous.gamelibrary.config.ScenarioConfig; import uulm.teamname.marvelous.gamelibrary.json.JSON; import uulm.teamname.marvelous.gamelibrary.json.ValidationUtility; import uulm.teamname.marvelous.server.args.ServerArgs; import uulm.teamname.marvelous.server.netconnector.MarvelousServer; import java.io.*; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.Map; /** * The MainClass of the Server application. The main responsibility of this class is to parse command line arguments, * set the log level, load the configuration files, and create and manage the threads that make up the server itself. * To run the server edit your IntelliJ run config, and enter * {@code -c .\configs\marvelheros.character.json -m .\configs\matchconfig_1.game.json * -s .\configs\asgard.scenario.json -v} into the arguments field. */ public class Server { private static PartyConfig partyConfig; private static ScenarioConfig scenarioConfig; private static CharacterConfig characterConfig; private static Integer maxLobbies; public static void main(String[] args) { ServerArgs serverArgs = new ServerArgs(); JCommander jc = JCommander.newBuilder().addObject(serverArgs).build(); try { jc.parse(args); } catch (ParameterException e) { Logger.error("Invalid parameters: {}", e.getMessage()); System.exit(1); return; } if(serverArgs.isHelp()) { jc.usage(); System.exit(0); return; } maxLobbies = serverArgs.getMaxLobbies(); if (serverArgs.isVerbose() || serverArgs.isCheckConfig()) { // If checkConfig, the LogLevel is also set to max, because more information // is exactly what checking the requirements means setLogLevel(5); } else { setLogLevel(serverArgs.getLogLevel()); } ScenarioConfig scenarioConfig = readScenarioConfig(serverArgs.getScenarioConfigFile()); CharacterConfig characterConfig = readCharacterConfig(serverArgs.getCharacterConfigFile()); PartyConfig partyConfig = readPartyConfig(serverArgs.getMatchConfigFile()); // If only configurations should be checked, the server exits here if (serverArgs.isCheckConfig()) { Logger.info("Exiting as configuration file check is done"); System.exit(0); return; } Logger.info("populating static Server variables with config objects"); Server.scenarioConfig = scenarioConfig; Server.characterConfig = characterConfig; Server.partyConfig = partyConfig; InetSocketAddress address = new InetSocketAddress(serverArgs.getPort()); Logger.trace("Inet address {} created", address); Logger.trace("Instantiating MarvelousServer..."); MarvelousServer netConnector = new MarvelousServer(address); Logger.trace("Starting MarvelousServer..."); netConnector.start(); Logger.trace("End of Main reached. Exiting main thread."); } /** Function that sets the log level for {@link Logger Tinylog}. * It has to be executed BEFORE ANY LOGGING OPERATIONS . */ private static void setLogLevel(int logLevel) { Map map = new HashMap<>(); Configuration.replace(map); String logLevelDescriptor = switch (logLevel) { case 0 -> "off"; case 1 -> "error"; case 2 -> "warn"; case 3 -> "info"; case 4 -> "debug"; case 5 -> "trace"; default -> "info"; }; // Add log writer 1, a console writer (which logs to the console) map.put("writer1", "console"); map.put("writer1.level", logLevelDescriptor); map.put("writer1.format", "[{thread}] {level}: {message}"); // Add log writer 2, a file writer logging to the file server.log map.put("writer2", "file"); map.put("writer2.level", logLevelDescriptor); map.put("writer2.file", "./logs/server.log"); Configuration.replace(map); Logger.info("Log level set to '{}'", logLevelDescriptor); } private static ScenarioConfig readScenarioConfig(File source) { if (!source.exists()) { Logger.error("Scenario Configuration file not found. Exiting..."); System.exit(1); } var config = JSON.parseScenarioConfig(source); if (config.isEmpty()) { Logger.error("Scenario Configuration couldn't be parsed. Exiting..."); System.exit(1); } Logger.trace("Validating configuration file..."); var violations = ValidationUtility.validate(config.get()); if (violations.isPresent()) { Logger.error("Scenario Configuration vas invalid: {}", violations.get()); System.exit(1); } int grassFields = 0; for (FieldType[] row: config.get().scenario) { for (FieldType type: row) { if (type == FieldType.GRASS) grassFields++; } if (grassFields > 18) break; } if (grassFields <= 18) { Logger.error( "Scenario Configuration vas invalid: Only {} grass fields found, which is less than 18" , grassFields); System.exit(1); } Logger.info("Scenario Config file loaded"); return config.get(); } private static CharacterConfig readCharacterConfig(File source) { if (!source.exists()) { Logger.error("Character Configuration file not found. Exiting..."); System.exit(1); } var config = JSON.parseCharacterConfig(source); if (config.isEmpty()) { Logger.error("Character Configuration couldn't be parsed. Exiting..."); System.exit(1); } var violations = ValidationUtility.validate(config.get()); if (violations.isPresent()) { Logger.error("Scenario Configuration vas invalid: {}", violations.get()); System.exit(1); } Logger.info("Character Config file loaded"); return config.get(); } private static PartyConfig readPartyConfig(File source) { if (!source.exists()) { Logger.error("Party Configuration file not found. Exiting..."); System.exit(1); } var config = JSON.parsePartyConfig(source); if (config.isEmpty()) { Logger.error("Party Configuration couldn't be parsed. Exiting..."); System.exit(1); } var violations = ValidationUtility.validate(config.get()); if (violations.isPresent()) { Logger.error("Party Configuration vas invalid: {}", violations.get()); System.exit(1); } Logger.info("Party Config file loaded"); return config.get(); } /** Returns the party configuration the server was initialized with */ public static PartyConfig getPartyConfig() { return partyConfig; } /** Returns the scenario configuration the server was initialized with */ public static ScenarioConfig getScenarioConfig() { return scenarioConfig; } /** Returns the character configuration the server was initialized with */ public static CharacterConfig getCharacterConfig() { return characterConfig; } /** Returns the maximum amount of lobbies the server should create */ public static Integer getMaxLobbies() { return maxLobbies; } }