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-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)
|
||||
|
|
|
@ -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<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()) {
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue