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 <cpw+github@weeksfamily.ca>
This commit is contained in:
cpw 2019-02-26 10:28:41 -05:00
parent 7766eed754
commit 0bcc60fbe3
No known key found for this signature in database
GPG key ID: 8EB3DF749553B1B7
3 changed files with 83 additions and 65 deletions

View file

@ -288,7 +288,7 @@ project(':forge') {
installer 'org.ow2.asm:asm:6.2' installer 'org.ow2.asm:asm:6.2'
installer 'org.ow2.asm:asm-commons:6.2' installer 'org.ow2.asm:asm-commons:6.2'
installer 'org.ow2.asm:asm-tree: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:accesstransformers:0.15.+:shadowed'
installer 'net.minecraftforge:eventbus:0.8.+:service' installer 'net.minecraftforge:eventbus:0.8.+:service'
installer 'net.minecraftforge:forgespi:0.8.+' 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-api:2.11.1'
installer 'org.apache.logging.log4j:log4j-core:2.11.1' installer 'org.apache.logging.log4j:log4j-core:2.11.1'
installer 'net.minecrell:terminalconsoleappender:1.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.guava:guava:21.0'
fmllauncherImplementation 'com.google.code.gson:gson:2.8.0' fmllauncherImplementation 'com.google.code.gson:gson:2.8.0'
testImplementation "org.junit.jupiter:junit-jupiter-api:5.0.0" testImplementation "org.junit.jupiter:junit-jupiter-api:5.0.0"
@ -670,32 +671,9 @@ project(':forge') {
} }
} }
task fmllauncherConfig(type: WriteProperties) { task launcherJar(type: Jar) {
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]) {
classifier 'launcher' classifier 'launcher'
from sourceSets.fmllauncher.output from sourceSets.fmllauncher.output
from fmllauncherConfig.outputFile
doFirst { doFirst {
def classpath = new StringBuilder() def classpath = new StringBuilder()
def artifacts = getArtifacts(project, project.configurations.installer, false) def artifacts = getArtifacts(project, project.configurations.installer, false)
@ -708,7 +686,15 @@ project(':forge') {
if (pkg == '/') { if (pkg == '/') {
manifest.attributes(values += [ manifest.attributes(values += [
'Main-Class': 'net.minecraftforge.server.ServerMain', '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 { } else {
manifest.attributes(values, pkg) manifest.attributes(values, pkg)

View file

@ -19,6 +19,7 @@
package net.minecraftforge.fml.loading; package net.minecraftforge.fml.loading;
import cpw.mods.modlauncher.ServiceLoaderStreamUtils;
import cpw.mods.modlauncher.TransformingClassLoader; import cpw.mods.modlauncher.TransformingClassLoader;
import cpw.mods.modlauncher.api.IEnvironment; import cpw.mods.modlauncher.api.IEnvironment;
import cpw.mods.modlauncher.api.ILaunchHandlerService; import cpw.mods.modlauncher.api.ILaunchHandlerService;
@ -78,7 +79,10 @@ public class FMLLoader
LOGGER.debug(CORE, "Initializing modjar URL handler"); LOGGER.debug(CORE, "Initializing modjar URL handler");
URL.setURLStreamHandlerFactory(p->p.equals("modjar") ? new ModJarURLHandler() : null); 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(); final Package atPackage = accessTransformer.getClass().getPackage();
LOGGER.debug(CORE,"FML found AccessTransformer version : {}", atPackage.getImplementationVersion()); LOGGER.debug(CORE,"FML found AccessTransformer version : {}", atPackage.getImplementationVersion());
@ -87,7 +91,10 @@ public class FMLLoader
throw new IncompatibleEnvironmentException("Incompatible accesstransformer found "+atPackage.getSpecificationVersion()); 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(); final Package eventBusPackage = eventBus.getClass().getPackage();
LOGGER.debug(CORE,"FML found EventBus version : {}", eventBusPackage.getImplementationVersion()); LOGGER.debug(CORE,"FML found EventBus version : {}", eventBusPackage.getImplementationVersion());
@ -96,11 +103,14 @@ public class FMLLoader
throw new IncompatibleEnvironmentException("Incompatible eventbus found "+eventBusPackage.getSpecificationVersion()); 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"); LOGGER.debug(CORE, "Found Runtime Dist Cleaner");
final ArrayList<ICoreModProvider> coreModProviders = new ArrayList<>(); final ArrayList<ICoreModProvider> 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()) { if (coreModProviders.isEmpty()) {
LOGGER.fatal(CORE, "Found no coremod provider. Cannot run"); LOGGER.fatal(CORE, "Found no coremod provider. Cannot run");
@ -114,6 +124,13 @@ public class FMLLoader
final Package coremodPackage = coreModProvider.getClass().getPackage(); final Package coremodPackage = coreModProvider.getClass().getPackage();
LOGGER.debug(CORE,"FML found CoreMod version : {}", coremodPackage.getImplementationVersion()); 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(); FixSSL.fixup();
} }

View file

@ -19,49 +19,64 @@
package net.minecraftforge.server; package net.minecraftforge.server;
import java.io.File; import cpw.mods.modlauncher.InvalidLauncherSetupException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import com.google.common.collect.ObjectArrays;
import cpw.mods.modlauncher.Launcher; 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 class ServerMain {
public static void main(String[] args) throws IOException { public static void main(String[] args) {
final Properties props = new Properties(); try {
try(InputStream stream = ServerMain.class.getClassLoader().getResourceAsStream("server_launcher.properties")) { Class.forName("cpw.mods.modlauncher.Launcher", false, ClassLoader.getSystemClassLoader());
if (stream == null) { Class.forName("net.minecraftforge.forgespi.Environment", false, ClassLoader.getSystemClassLoader());
System.out.println("ERROR: could not load server_launcher.properties, invalid launcher jar, you must specify all arguments"); } catch (ClassNotFoundException cnfe) {
return; System.err.println("FATAL ERROR, You need to run the installer. The libraries required to launch a server are missing");
} System.exit(1);
props.load(stream);
} }
boolean exit = false; final String launchArgs = Optional.ofNullable(ServerMain.class.getProtectionDomain()).
int lib_count = Integer.parseInt(props.getProperty("library.count")); map(ProtectionDomain::getCodeSource).
for (int x = 0; x < lib_count; x++) { map(CodeSource::getLocation).
String lib = props.getProperty(String.format("library.%03d", x)); map(ServerMain::urlToManifest).
if (lib == null) { map(Manifest::getMainAttributes).
System.out.println("Invalid server launcher properties, missing library: " + x); map(a -> a.getValue("ServerLaunchArgs")).
exit = true; orElseThrow(ServerMain::throwMissingManifest);
} else if (!new File(lib).exists()) { String[] defaultargs = launchArgs.split(" ");
System.out.println("Missing library: " + lib); String[] result = new String[args.length + defaultargs.length];
exit = true; 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) private static Manifest urlToManifest(URL url) {
return; try {
return new JarFile(new File(url.toURI())).getManifest();
String defaults = props.getProperty("args"); } catch (URISyntaxException | IOException e) {
if (defaults == null) { return null;
System.out.println("Null default argumetns found, you must specify all arguments.");
return;
} }
}
System.out.println("Appending default arguments: " + defaults); private static RuntimeException throwMissingManifest() {
final String[] argArray = ObjectArrays.concat(defaults.split(" "), args, String.class); System.err.println("This is not being run from a valid JAR file, essential data is missing.");
Launcher.main(argArray); return new RuntimeException("Missing the manifest");
} }
} }