From 0bcc60fbe3842861092508589fef89a3b3d9014b Mon Sep 17 00:00:00 2001 From: cpw Date: Tue, 26 Feb 2019 10:28:41 -0500 Subject: [PATCH] Tidy up server launch. The JAR will resolve it's libraries from a relative path. We just need to verify what we need is on the classpath, and try and handle when it's not, cleanly (by recommending running the installer). The manifest now embeds the correct launch arguments to allow FML to resolve - these were _never_ editable, and belong somewhere out of the way. Signed-off-by: cpw --- build.gradle | 38 +++------ .../minecraftforge/fml/loading/FMLLoader.java | 25 +++++- .../net/minecraftforge/server/ServerMain.java | 85 +++++++++++-------- 3 files changed, 83 insertions(+), 65 deletions(-) diff --git a/build.gradle b/build.gradle index 9c78b7a2b..a3956dc91 100644 --- a/build.gradle +++ b/build.gradle @@ -288,7 +288,7 @@ project(':forge') { installer 'org.ow2.asm:asm:6.2' installer 'org.ow2.asm:asm-commons:6.2' installer 'org.ow2.asm:asm-tree:6.2' - installer 'cpw.mods:modlauncher:0.10.+' + installer 'cpw.mods:modlauncher:0.11.+' installer 'net.minecraftforge:accesstransformers:0.15.+:shadowed' installer 'net.minecraftforge:eventbus:0.8.+:service' installer 'net.minecraftforge:forgespi:0.8.+' @@ -303,6 +303,7 @@ project(':forge') { installer 'org.apache.logging.log4j:log4j-api:2.11.1' installer 'org.apache.logging.log4j:log4j-core:2.11.1' installer 'net.minecrell:terminalconsoleappender:1.1.+' + installer 'net.sf.jopt-simple:jopt-simple:5.0.4' fmllauncherImplementation 'com.google.guava:guava:21.0' fmllauncherImplementation 'com.google.code.gson:gson:2.8.0' testImplementation "org.junit.jupiter:junit-jupiter-api:5.0.0" @@ -670,32 +671,9 @@ project(':forge') { } } - task fmllauncherConfig(type: WriteProperties) { - outputFile file('build/server_launcher.properties') - encoding 'UTF8' - doFirst { - def artifacts = getArtifacts(project, project.configurations.installer, false).values().stream().map{a -> a.downloads.artifact.path}.sorted().collect() + [ - "net/minecraft/server/${MC_VERSION}/server-${MC_VERSION}-extra.jar" - ] - for (int x = 0; x < artifacts.size(); x++) { - property 'library.' + x.toString().padLeft(3, '0'), "libraries/${artifacts.get(x)}" - } - property 'library.count', artifacts.size() - property 'args', [ - '--gameDir', '.', - '--launchTarget', 'fmlserver', - '--fml.forgeVersion', "${project.version.substring(MC_VERSION.length() + 1)}".toString(), - '--fml.mcpVersion', MCP_VERSION, - '--fml.mcVersion', MC_VERSION, - '--fml.forgeGroup', project.group - ].join(' ') - } - } - - task launcherJar(type: Jar, dependsOn: [fmllauncherConfig]) { + task launcherJar(type: Jar) { classifier 'launcher' from sourceSets.fmllauncher.output - from fmllauncherConfig.outputFile doFirst { def classpath = new StringBuilder() def artifacts = getArtifacts(project, project.configurations.installer, false) @@ -708,7 +686,15 @@ project(':forge') { if (pkg == '/') { manifest.attributes(values += [ 'Main-Class': 'net.minecraftforge.server.ServerMain', - 'Class-Path': classpath.toString() + 'Class-Path': classpath.toString(), + 'ServerLaunchArgs': [ + '--gameDir', '.', + '--launchTarget', 'fmlserver', + '--fml.forgeVersion', "${project.version.substring(MC_VERSION.length() + 1)}".toString(), + '--fml.mcpVersion', MCP_VERSION, + '--fml.mcVersion', MC_VERSION, + '--fml.forgeGroup', project.group + ].join(' ') ]) } else { manifest.attributes(values, pkg) diff --git a/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLLoader.java b/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLLoader.java index 7c4f216ee..7ecd41651 100644 --- a/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLLoader.java +++ b/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLLoader.java @@ -19,6 +19,7 @@ package net.minecraftforge.fml.loading; +import cpw.mods.modlauncher.ServiceLoaderStreamUtils; import cpw.mods.modlauncher.TransformingClassLoader; import cpw.mods.modlauncher.api.IEnvironment; import cpw.mods.modlauncher.api.ILaunchHandlerService; @@ -78,7 +79,10 @@ public class FMLLoader LOGGER.debug(CORE, "Initializing modjar URL handler"); URL.setURLStreamHandlerFactory(p->p.equals("modjar") ? new ModJarURLHandler() : null); - accessTransformer = environment.findLaunchPlugin("accesstransformer").orElseThrow(()-> new IncompatibleEnvironmentException("Missing AccessTransformer, cannot run")); + accessTransformer = environment.findLaunchPlugin("accesstransformer").orElseThrow(()-> { + LOGGER.fatal(CORE,"Access Transformer library is missing, we need this to run"); + return new IncompatibleEnvironmentException("Missing AccessTransformer, cannot run"); + }); final Package atPackage = accessTransformer.getClass().getPackage(); LOGGER.debug(CORE,"FML found AccessTransformer version : {}", atPackage.getImplementationVersion()); @@ -87,7 +91,10 @@ public class FMLLoader throw new IncompatibleEnvironmentException("Incompatible accesstransformer found "+atPackage.getSpecificationVersion()); } - eventBus = environment.findLaunchPlugin("eventbus").orElseThrow(()-> new IncompatibleEnvironmentException("Missing EventBus, cannot run")); + eventBus = environment.findLaunchPlugin("eventbus").orElseThrow(()-> { + LOGGER.fatal(CORE,"Event Bus library is missing, we need this to run"); + return new IncompatibleEnvironmentException("Missing EventBus, cannot run"); + }); final Package eventBusPackage = eventBus.getClass().getPackage(); LOGGER.debug(CORE,"FML found EventBus version : {}", eventBusPackage.getImplementationVersion()); @@ -96,11 +103,14 @@ public class FMLLoader throw new IncompatibleEnvironmentException("Incompatible eventbus found "+eventBusPackage.getSpecificationVersion()); } - runtimeDistCleaner = (RuntimeDistCleaner)environment.findLaunchPlugin("runtimedistcleaner").orElseThrow(()->new IncompatibleEnvironmentException("Missing RuntimeDistCleaner, cannot run!")); + runtimeDistCleaner = (RuntimeDistCleaner)environment.findLaunchPlugin("runtimedistcleaner").orElseThrow(()-> { + LOGGER.fatal(CORE,"Dist Cleaner is missing, we need this to run"); + return new IncompatibleEnvironmentException("Missing DistCleaner, cannot run!"); + }); LOGGER.debug(CORE, "Found Runtime Dist Cleaner"); final ArrayList coreModProviders = new ArrayList<>(); - ServiceLoader.load(ICoreModProvider.class).forEach(coreModProviders::add); + ServiceLoaderStreamUtils.errorHandlingServiceLoader(ICoreModProvider.class, serviceConfigurationError -> LOGGER.fatal(CORE, "Failed to load a coremod library, expect problems", serviceConfigurationError)).forEach(coreModProviders::add); if (coreModProviders.isEmpty()) { LOGGER.fatal(CORE, "Found no coremod provider. Cannot run"); @@ -114,6 +124,13 @@ public class FMLLoader final Package coremodPackage = coreModProvider.getClass().getPackage(); LOGGER.debug(CORE,"FML found CoreMod version : {}", coremodPackage.getImplementationVersion()); + try { + Class.forName("com.electronwill.nightconfig.core.Config", false, environment.getClass().getClassLoader()); + Class.forName("com.electronwill.nightconfig.toml.TomlFormat", false, environment.getClass().getClassLoader()); + } catch (ClassNotFoundException e) { + LOGGER.fatal(CORE, "Failed to load NightConfig"); + throw new IncompatibleEnvironmentException("Missing NightConfig"); + } FixSSL.fixup(); } diff --git a/src/fmllauncher/java/net/minecraftforge/server/ServerMain.java b/src/fmllauncher/java/net/minecraftforge/server/ServerMain.java index 232f46533..45276f7c8 100644 --- a/src/fmllauncher/java/net/minecraftforge/server/ServerMain.java +++ b/src/fmllauncher/java/net/minecraftforge/server/ServerMain.java @@ -19,49 +19,64 @@ package net.minecraftforge.server; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; - -import com.google.common.collect.ObjectArrays; +import cpw.mods.modlauncher.InvalidLauncherSetupException; import cpw.mods.modlauncher.Launcher; +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.security.CodeSource; +import java.security.ProtectionDomain; +import java.util.Optional; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + public class ServerMain { - public static void main(String[] args) throws IOException { - final Properties props = new Properties(); - try(InputStream stream = ServerMain.class.getClassLoader().getResourceAsStream("server_launcher.properties")) { - if (stream == null) { - System.out.println("ERROR: could not load server_launcher.properties, invalid launcher jar, you must specify all arguments"); - return; - } - props.load(stream); + public static void main(String[] args) { + try { + Class.forName("cpw.mods.modlauncher.Launcher", false, ClassLoader.getSystemClassLoader()); + Class.forName("net.minecraftforge.forgespi.Environment", false, ClassLoader.getSystemClassLoader()); + } catch (ClassNotFoundException cnfe) { + System.err.println("FATAL ERROR, You need to run the installer. The libraries required to launch a server are missing"); + System.exit(1); } - boolean exit = false; - int lib_count = Integer.parseInt(props.getProperty("library.count")); - for (int x = 0; x < lib_count; x++) { - String lib = props.getProperty(String.format("library.%03d", x)); - if (lib == null) { - System.out.println("Invalid server launcher properties, missing library: " + x); - exit = true; - } else if (!new File(lib).exists()) { - System.out.println("Missing library: " + lib); - exit = true; + final String launchArgs = Optional.ofNullable(ServerMain.class.getProtectionDomain()). + map(ProtectionDomain::getCodeSource). + map(CodeSource::getLocation). + map(ServerMain::urlToManifest). + map(Manifest::getMainAttributes). + map(a -> a.getValue("ServerLaunchArgs")). + orElseThrow(ServerMain::throwMissingManifest); + String[] defaultargs = launchArgs.split(" "); + String[] result = new String[args.length + defaultargs.length]; + System.arraycopy(defaultargs, 0, result, 0, defaultargs.length); + System.arraycopy(args, 0, result, defaultargs.length, args.length); + // separate class, so the exception can resolve + new Runner().runLauncher(result); + } + + private static class Runner { + private void runLauncher(final String[] result) { + try { + Launcher.main(result); + } catch (InvalidLauncherSetupException e) { + System.err.println("The server is missing critical libraries and cannot load. Please run the installer to correct this"); + System.exit(1); } } - - if (exit) - return; - - String defaults = props.getProperty("args"); - if (defaults == null) { - System.out.println("Null default argumetns found, you must specify all arguments."); - return; + } + private static Manifest urlToManifest(URL url) { + try { + return new JarFile(new File(url.toURI())).getManifest(); + } catch (URISyntaxException | IOException e) { + return null; } + } - System.out.println("Appending default arguments: " + defaults); - final String[] argArray = ObjectArrays.concat(defaults.split(" "), args, String.class); - Launcher.main(argArray); + private static RuntimeException throwMissingManifest() { + System.err.println("This is not being run from a valid JAR file, essential data is missing."); + return new RuntimeException("Missing the manifest"); } }