package org.bukkit.craftbukkit; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import joptsimple.OptionParser; import joptsimple.OptionSet; import joptsimple.util.PathConverter; import org.fusesource.jansi.AnsiConsole; public class Main { public static boolean useJline = true; public static boolean useConsole = true; public static void main(String[] args) { // Paper start final String warnWhenLegacyFormattingDetected = String.join(".", "net", "kyori", "adventure", "text", "warnWhenLegacyFormattingDetected"); if (false && System.getProperty(warnWhenLegacyFormattingDetected) == null) { System.setProperty(warnWhenLegacyFormattingDetected, String.valueOf(true)); } // Paper end // Todo: Installation script OptionParser parser = new OptionParser() { { this.acceptsAll(Main.asList("?", "help"), "Show the help"); this.acceptsAll(Main.asList("c", "config"), "Properties file to use") .withRequiredArg() .ofType(File.class) .defaultsTo(new File("server.properties")) .describedAs("Properties file"); this.acceptsAll(Main.asList("P", "plugins"), "Plugin directory to use") .withRequiredArg() .ofType(File.class) .defaultsTo(new File("plugins")) .describedAs("Plugin directory"); this.acceptsAll(Main.asList("h", "host", "server-ip"), "Host to listen on") .withRequiredArg() .ofType(String.class) .describedAs("Hostname or IP"); this.acceptsAll(Main.asList("W", "world-dir", "universe", "world-container"), "World container") .withRequiredArg() .ofType(File.class) .defaultsTo(new File(".")) .describedAs("Directory containing worlds"); this.acceptsAll(Main.asList("w", "world", "level-name"), "World name") .withRequiredArg() .ofType(String.class) .describedAs("World name"); this.acceptsAll(Main.asList("p", "port", "server-port"), "Port to listen on") .withRequiredArg() .ofType(Integer.class) .describedAs("Port"); this.accepts("serverId", "Server ID") .withRequiredArg(); this.accepts("jfrProfile", "Enable JFR profiling"); this.accepts("pidFile", "pid File") .withRequiredArg() .withValuesConvertedBy(new PathConverter()); this.acceptsAll(Main.asList("o", "online-mode"), "Whether to use online authentication") .withRequiredArg() .ofType(Boolean.class) .describedAs("Authentication"); this.acceptsAll(Main.asList("s", "size", "max-players"), "Maximum amount of players") .withRequiredArg() .ofType(Integer.class) .describedAs("Server size"); this.acceptsAll(Main.asList("d", "date-format"), "Format of the date to display in the console (for log entries)") .withRequiredArg() .ofType(SimpleDateFormat.class) .describedAs("Log date format"); this.acceptsAll(Main.asList("log-pattern"), "Specfies the log filename pattern") .withRequiredArg() .ofType(String.class) .defaultsTo("server.log") .describedAs("Log filename"); this.acceptsAll(Main.asList("log-limit"), "Limits the maximum size of the log file (0 = unlimited)") .withRequiredArg() .ofType(Integer.class) .defaultsTo(0) .describedAs("Max log size"); this.acceptsAll(Main.asList("log-count"), "Specified how many log files to cycle through") .withRequiredArg() .ofType(Integer.class) .defaultsTo(1) .describedAs("Log count"); this.acceptsAll(Main.asList("log-append"), "Whether to append to the log file") .withRequiredArg() .ofType(Boolean.class) .defaultsTo(true) .describedAs("Log append"); this.acceptsAll(Main.asList("log-strip-color"), "Strips color codes from log file"); this.acceptsAll(Main.asList("b", "bukkit-settings"), "File for bukkit settings") .withRequiredArg() .ofType(File.class) .defaultsTo(new File("bukkit.yml")) .describedAs("Yml file"); this.acceptsAll(Main.asList("C", "commands-settings"), "File for command settings") .withRequiredArg() .ofType(File.class) .defaultsTo(new File("commands.yml")) .describedAs("Yml file"); this.acceptsAll(Main.asList("forceUpgrade"), "Whether to force a world upgrade"); this.acceptsAll(Main.asList("eraseCache"), "Whether to force cache erase during world upgrade"); this.acceptsAll(Main.asList("recreateRegionFiles"), "Whether to recreate region files during world upgrade"); this.accepts("safeMode", "Loads level with vanilla datapack only"); // Paper this.acceptsAll(Main.asList("nogui"), "Disables the graphical console"); this.acceptsAll(Main.asList("nojline"), "Disables jline and emulates the vanilla console"); this.acceptsAll(Main.asList("noconsole"), "Disables the console"); this.acceptsAll(Main.asList("v", "version"), "Show the CraftBukkit Version"); this.acceptsAll(Main.asList("demo"), "Demo mode"); this.acceptsAll(Main.asList("initSettings"), "Only create configuration files and then exit"); // SPIGOT-5761: Add initSettings option // Spigot Start this.acceptsAll(Main.asList("S", "spigot-settings"), "File for spigot settings") .withRequiredArg() .ofType(File.class) .defaultsTo(new File("spigot.yml")) .describedAs("Yml file"); // Spigot End // Paper start acceptsAll(asList("paper-dir", "paper-settings-directory"), "Directory for Paper settings") .withRequiredArg() .ofType(File.class) .defaultsTo(new File(io.papermc.paper.configuration.PaperConfigurations.CONFIG_DIR)) .describedAs("Config directory"); acceptsAll(asList("paper", "paper-settings"), "File for Paper settings") .withRequiredArg() .ofType(File.class) .defaultsTo(new File("paper.yml")) .describedAs("Yml file"); // Paper end } }; OptionSet options = null; try { options = parser.parse(args); } catch (joptsimple.OptionException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, ex.getLocalizedMessage()); } if ((options == null) || (options.has("?"))) { try { parser.printHelpOn(System.out); } catch (IOException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } } else if (options.has("v")) { System.out.println(CraftServer.class.getPackage().getImplementationVersion()); } else { // Do you love Java using + and ! as string based identifiers? I sure do! String path = new File(".").getAbsolutePath(); if (path.contains("!") || path.contains("+")) { System.err.println("Cannot run server in a directory with ! or + in the pathname. Please rename the affected folders and try again."); return; } float javaVersion = Float.parseFloat(System.getProperty("java.class.version")); if (javaVersion > 67.0) { System.err.println("Unsupported Java detected (" + javaVersion + "). Only up to Java 23 is supported."); return; } try { // This trick bypasses Maven Shade's clever rewriting of our getProperty call when using String literals String jline_UnsupportedTerminal = new String(new char[]{'j', 'l', 'i', 'n', 'e', '.', 'U', 'n', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd', 'T', 'e', 'r', 'm', 'i', 'n', 'a', 'l'}); String jline_terminal = new String(new char[]{'j', 'l', 'i', 'n', 'e', '.', 't', 'e', 'r', 'm', 'i', 'n', 'a', 'l'}); Main.useJline = !(jline_UnsupportedTerminal).equals(System.getProperty(jline_terminal)); if (options.has("nojline")) { System.setProperty("user.language", "en"); Main.useJline = false; } if (Main.useJline) { AnsiConsole.systemInstall(); } else { // This ensures the terminal literal will always match the jline implementation System.setProperty(jline.TerminalFactory.JLINE_TERMINAL, jline.UnsupportedTerminal.class.getName()); } if (options.has("noconsole")) { Main.useConsole = false; } if (Main.class.getPackage().getImplementationVendor() != null && System.getProperty("IReallyKnowWhatIAmDoingISwear") == null) { Date buildDate = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z").parse(Main.class.getPackage().getImplementationVendor()); // Paper Calendar deadline = Calendar.getInstance(); deadline.add(Calendar.DAY_OF_YEAR, -2); if (buildDate.before(deadline.getTime())) { System.err.println("*** Error, this build is outdated ***"); System.err.println("*** Please download a new build as per instructions from https://www.spigotmc.org/go/outdated-spigot ***"); System.err.println("*** Server will start in 20 seconds ***"); Thread.sleep(TimeUnit.SECONDS.toMillis(20)); } } System.out.println("Loading libraries, please wait..."); net.minecraft.server.Main.main(options); } catch (Throwable t) { t.printStackTrace(); } } } private static List asList(String... params) { return Arrays.asList(params); } }