diff --git a/launcher-builder/src/main/java/com/skcraft/launcher/builder/loaders/ModernForgeLoaderProcessor.java b/launcher-builder/src/main/java/com/skcraft/launcher/builder/loaders/ModernForgeLoaderProcessor.java index cd8648b..b21b63f 100644 --- a/launcher-builder/src/main/java/com/skcraft/launcher/builder/loaders/ModernForgeLoaderProcessor.java +++ b/launcher-builder/src/main/java/com/skcraft/launcher/builder/loaders/ModernForgeLoaderProcessor.java @@ -18,10 +18,7 @@ import com.skcraft.launcher.util.FileUtils; import lombok.extern.java.Log; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; +import java.io.*; import java.net.URL; import java.util.List; import java.util.jar.JarFile; @@ -61,6 +58,12 @@ version.getArguments().getGameArguments().addAll(gameArguments); } + // Copy JVM arguments + List jvmArguments = info.getArguments().getJvmArguments(); + if (jvmArguments != null) { + version.getArguments().getJvmArguments().addAll(jvmArguments); + } + // Add libraries List libraries = info.getLibraries(); if (libraries != null) { @@ -101,11 +104,13 @@ // Extract the data files List extraFiles = Lists.newArrayList(); + File objectsDir = new File(baseDir, manifest.getObjectsLocation()); + ZipEntry clientBinpatch = BuilderUtils.getZipEntry(jarFile, "data/client.lzma"); if (clientBinpatch != null) { DownloadableFile entry = FileUtils.saveStreamToObjectsDir( closer.register(jarFile.getInputStream(clientBinpatch)), - new File(baseDir, manifest.getObjectsLocation())); + objectsDir); entry.setName("client.lzma"); entry.setSide(Side.CLIENT); @@ -117,7 +122,7 @@ if (serverBinpatch != null) { DownloadableFile entry = FileUtils.saveStreamToObjectsDir( closer.register(jarFile.getInputStream(serverBinpatch)), - new File(baseDir, manifest.getObjectsLocation())); + objectsDir); entry.setName("server.lzma"); entry.setSide(Side.SERVER); @@ -125,6 +130,20 @@ profile.getData().get("BINPATCH").setServer("&" + entry.getName() + "&"); } + // Forge install profile spec version 1 and above. + if (profile.getSpec() >= 1) { + // Add the installer itself to the extra files. + // This is for a server-only task like above, but hey. + DownloadableFile entry = FileUtils.saveStreamToObjectsDir( + closer.register(new FileInputStream(loaderJar)), objectsDir); + + entry.setName(loaderJar.getName()); + entry.setSide(Side.SERVER); + extraFiles.add(entry); + + profile.getData().put("INSTALLER", SidedData.of("&" + entry.getName() + "&")); + } + // Add extra sided data profile.getData().put("SIDE", SidedData.create("client", "server")); diff --git a/launcher/src/main/java/com/skcraft/launcher/install/ProcessorTask.java b/launcher/src/main/java/com/skcraft/launcher/install/ProcessorTask.java index 4c7cf65..86d43ac 100644 --- a/launcher/src/main/java/com/skcraft/launcher/install/ProcessorTask.java +++ b/launcher/src/main/java/com/skcraft/launcher/install/ProcessorTask.java @@ -42,11 +42,16 @@ @Override public void execute(Launcher launcher) throws Exception { VersionManifest versionManifest = manifest.getVersionManifest(); - loaderManifest.getSidedData().put("MINECRAFT_JAR", SidedData.of(launcher.getJarPath(versionManifest).getAbsolutePath())); LoaderSubResolver resolver = new LoaderSubResolver(manifest, loaderManifest, Environment.getInstance(), Side.CLIENT, launcher.getBaseDir(), localFiles); + Map> sidedData = loaderManifest.getSidedData(); + sidedData.put("ROOT", SidedData.of(launcher.getInstallerDir().getAbsolutePath())); + sidedData.put("MINECRAFT_JAR", SidedData.of(launcher.getJarPath(versionManifest).getAbsolutePath())); + sidedData.put("LIBRARY_DIR", SidedData.of(resolver.getPathOf(manifest.getLibrariesLocation()))); + sidedData.put("MINECRAFT_VERSION", SidedData.of(versionManifest.getId())); + message = "Resolving parameters"; List programArgs = processor.resolveArgs(resolver); Map outputs = processor.resolveOutputs(resolver); @@ -83,6 +88,7 @@ message = "Executing"; log.info(String.format("Running processor '%s' with %d args", processor.getJar(), programArgs.size())); + log.info("Arguments: [" + String.join(", ", programArgs) + "]"); ClassLoader parent; try { @@ -94,13 +100,19 @@ parent = null; } + ClassLoader prev = Thread.currentThread().getContextClassLoader(); ClassLoader cl = new URLClassLoader(classpath.toArray(new URL[0]), parent); try { Class mainClazz = Class.forName(mainClass, true, cl); Method main = mainClazz.getDeclaredMethod("main", String[].class); + + // engage spicy mode + Thread.currentThread().setContextClassLoader(cl); main.invoke(null, (Object) programArgs.toArray(new String[0])); } catch (Throwable e) { throw new RuntimeException(e); + } finally { + Thread.currentThread().setContextClassLoader(prev); } message = "Verifying"; @@ -118,6 +130,7 @@ } if (!FileUtils.getShaHash(artifact).equals(output.getValue())) { + log.warning("Invalid hash, expected " + output.getValue()); throw new RuntimeException(String.format("Artifact '%s' has invalid hash!", output.getKey())); } diff --git a/launcher/src/main/java/com/skcraft/launcher/launch/Runner.java b/launcher/src/main/java/com/skcraft/launcher/launch/Runner.java index 241e7b8..96602f5 100644 --- a/launcher/src/main/java/com/skcraft/launcher/launch/Runner.java +++ b/launcher/src/main/java/com/skcraft/launcher/launch/Runner.java @@ -429,6 +429,10 @@ map.put("classpath", builder.buildClassPath()); map.put("natives_directory", extractDir.getAbsolutePath()); + // Forge additions + map.put("library_directory", launcher.getLibrariesDir().getAbsolutePath()); + map.put("classpath_separator", System.getProperty("path.separator")); + return map; } diff --git a/launcher/src/main/java/com/skcraft/launcher/model/loader/InstallProcessor.java b/launcher/src/main/java/com/skcraft/launcher/model/loader/InstallProcessor.java index 505a67b..45e18f7 100644 --- a/launcher/src/main/java/com/skcraft/launcher/model/loader/InstallProcessor.java +++ b/launcher/src/main/java/com/skcraft/launcher/model/loader/InstallProcessor.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.google.common.collect.Lists; +import com.skcraft.launcher.model.minecraft.Side; import lombok.Data; import java.util.Collections; @@ -16,6 +17,7 @@ private List classpath; private List args; private Map outputs; + private List sides; public List resolveArgs(LoaderSubResolver resolver) { return Lists.transform(getArgs(), resolver); @@ -32,4 +34,19 @@ return result; } + + public boolean shouldRunOn(Side side) { + if (sides == null) { + return true; + } + + switch (side) { + case CLIENT: + return sides.contains("client"); + case SERVER: + return sides.contains("server"); + } + + return false; + } } diff --git a/launcher/src/main/java/com/skcraft/launcher/model/loader/LoaderSubResolver.java b/launcher/src/main/java/com/skcraft/launcher/model/loader/LoaderSubResolver.java index 299aea7..3cbe657 100644 --- a/launcher/src/main/java/com/skcraft/launcher/model/loader/LoaderSubResolver.java +++ b/launcher/src/main/java/com/skcraft/launcher/model/loader/LoaderSubResolver.java @@ -33,6 +33,8 @@ public String apply(String arg) { if (arg == null) return null; + arg = replaceTokens(arg); + while (true) { char start = arg.charAt(0); int bound = arg.length() - 1; @@ -66,4 +68,55 @@ } } } + + private String replaceTokens(String arg) { + StringBuilder buf = new StringBuilder(); + + int length = arg.length(); + for (int i = 0; i < length; i++) { + char c = arg.charAt(i); + + if (c == '\\') { + buf.append(arg.charAt(i + 1)); + i++; + } else if (c == '{' || c == '\'') { + StringBuilder keyBuf = new StringBuilder(); + + for (int j = i + 1; j <= length; j++) { + if (j == length) { + throw new IllegalArgumentException("Illegal pattern: unclosed " + c); + } + + char d = arg.charAt(j); + + if (d == '\\') { + keyBuf.append(arg.charAt(j + 1)); + j++; + } else if (c == '{' && d == '}') { + String key = keyBuf.toString(); + SidedData sidedData = loader.getSidedData().get(key); + + if (sidedData != null) { + buf.append(sidedData.resolveFor(side)); + } else { + throw new IllegalArgumentException("Missing key: " + key); + } + + i = j; + break; + } else if (c == '\'' && d == '\'') { + buf.append(keyBuf.toString()); + i = j; + break; + } else { + keyBuf.append(d); + } + } + } else { + buf.append(c); + } + } + + return buf.toString(); + } } diff --git a/launcher/src/main/java/com/skcraft/launcher/model/loader/ProcessorEntry.java b/launcher/src/main/java/com/skcraft/launcher/model/loader/ProcessorEntry.java index dbceece..33e7319 100644 --- a/launcher/src/main/java/com/skcraft/launcher/model/loader/ProcessorEntry.java +++ b/launcher/src/main/java/com/skcraft/launcher/model/loader/ProcessorEntry.java @@ -1,6 +1,7 @@ package com.skcraft.launcher.model.loader; import com.skcraft.launcher.install.*; +import com.skcraft.launcher.model.minecraft.Side; import com.skcraft.launcher.model.modpack.ManifestEntry; import lombok.AllArgsConstructor; import lombok.Data; @@ -19,6 +20,8 @@ public void install(Installer installer, InstallLog log, UpdateCache cache, InstallExtras extras) throws Exception { LocalLoader loader = extras.getLoader(loaderName); - installer.queueLate(new ProcessorTask(processor, loader.getManifest(), getManifest(), loader.getLocalFiles())); + if (processor.shouldRunOn(Side.CLIENT)) { + installer.queueLate(new ProcessorTask(processor, loader.getManifest(), getManifest(), loader.getLocalFiles())); + } } } diff --git a/launcher/src/main/java/com/skcraft/launcher/model/loader/profiles/ModernForgeInstallProfile.java b/launcher/src/main/java/com/skcraft/launcher/model/loader/profiles/ModernForgeInstallProfile.java index da79aca..b6b9478 100644 --- a/launcher/src/main/java/com/skcraft/launcher/model/loader/profiles/ModernForgeInstallProfile.java +++ b/launcher/src/main/java/com/skcraft/launcher/model/loader/profiles/ModernForgeInstallProfile.java @@ -21,6 +21,7 @@ @Data @JsonIgnoreProperties(ignoreUnknown = true) public class ModernForgeInstallProfile { + private int spec; private List libraries; private List processors; private Map> data;