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-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)

View file

@ -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();
}

View file

@ -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");
}
}