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:
parent
7766eed754
commit
0bcc60fbe3
3 changed files with 83 additions and 65 deletions
38
build.gradle
38
build.gradle
|
@ -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)
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue