diff --git a/src/main/java/dev/aoqia/leaf/proxy/ProxyPaths.java b/src/main/java/dev/aoqia/leaf/proxy/ProxyPaths.java new file mode 100644 index 0000000..71b0cb7 --- /dev/null +++ b/src/main/java/dev/aoqia/leaf/proxy/ProxyPaths.java @@ -0,0 +1,79 @@ +package dev.aoqia.leaf.proxy; + +import dev.aoqia.leaf.proxy.entrypoints.ClientEntrypoint; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Comparator; +import java.util.List; + +public final class ProxyPaths { + private static final List GAME_JAR_PATHS = List.of( + "projectzomboid.jar", "projectzomboid/projectzomboid.jar", "Contents/Java/projectzomboid.jar"); + + public static Path gameJar(final Path proxyJar, final Path workingDirectory) throws IOException { + for (Path directory = proxyJar.toAbsolutePath().normalize().getParent(); directory != null; + directory = directory.getParent()) { + for (String relativePath : GAME_JAR_PATHS) { + final Path candidate = directory.resolve(relativePath); + if (Files.isRegularFile(candidate)) { + return candidate.toRealPath(); + } + } + } + + final Path candidate = workingDirectory.toAbsolutePath().normalize().resolve("projectzomboid.jar"); + if (Files.isRegularFile(candidate)) { + return candidate.toRealPath(); + } + + throw new FileNotFoundException("Cannot find projectzomboid.jar relative to " + proxyJar + + " or in " + workingDirectory.toAbsolutePath()); + } + + public static Path loaderDirectory(final Path gameJar, final String override) + throws IOException { + if (override != null) { + if (override.isBlank()) { + throw new IOException("-loaderPath must name the directory containing the loader and dependency JARs"); + } + return Path.of(override).toAbsolutePath().normalize(); + } + + for (Path directory = gameJar.getParent(); directory != null; directory = directory.getParent()) { + if (directory.getFileName() != null + && directory.getFileName().toString().equalsIgnoreCase("steamapps")) { + return directory.resolve("workshop/content/108600").resolve(ClientEntrypoint.WORKSHOP_ID) + .resolve("mods/LeafLoader/common/media/java"); + } + } + + throw new IOException("Cannot locate Steam Workshop relative to " + gameJar + + ". Use the proxy argument -loaderPath=."); + } + + public static List loaderJars(final Path directory) throws IOException { + if (!Files.isDirectory(directory)) { + throw new IOException("Loader directory does not exist: " + directory + + ". Subscribe to Leaf Loader (Workshop " + ClientEntrypoint.WORKSHOP_ID + "), finish its download, or set -loaderPath."); + } + + try (var files = Files.list(directory)) { + final List jars = files.filter(Files::isRegularFile) + .filter(path -> { + final String name = path.getFileName().toString(); + return name.endsWith(".jar") && !name.endsWith("-sources.jar"); + }) + .sorted(Comparator.comparing(path -> path.getFileName().toString())) + .toList(); + + if (jars.isEmpty()) { + throw new RuntimeException("No loader JARs found in " + directory); + } + + return jars; + } + } +} diff --git a/src/main/java/dev/aoqia/leaf/proxy/Utils.java b/src/main/java/dev/aoqia/leaf/proxy/Utils.java index 037995e..17af93f 100644 --- a/src/main/java/dev/aoqia/leaf/proxy/Utils.java +++ b/src/main/java/dev/aoqia/leaf/proxy/Utils.java @@ -22,7 +22,7 @@ public class Utils { public static void initLog(final File gameDir) throws IOException { File leafDir = new File(gameDir, ".leaf/"); if (!leafDir.exists()) { - Files.createDirectory(leafDir.toPath()); + Files.createDirectories(leafDir.toPath()); } File logFile = new File(leafDir, "proxy.log"); diff --git a/src/main/java/dev/aoqia/leaf/proxy/entrypoints/ClientEntrypoint.java b/src/main/java/dev/aoqia/leaf/proxy/entrypoints/ClientEntrypoint.java index 017f2a0..d40b431 100644 --- a/src/main/java/dev/aoqia/leaf/proxy/entrypoints/ClientEntrypoint.java +++ b/src/main/java/dev/aoqia/leaf/proxy/entrypoints/ClientEntrypoint.java @@ -16,6 +16,7 @@ import dev.aoqia.leaf.proxy.Constants; import dev.aoqia.leaf.proxy.Main; +import dev.aoqia.leaf.proxy.ProxyPaths; import dev.aoqia.leaf.proxy.Utils; import dev.aoqia.leaf.proxy.json.InstallerJson; @@ -23,24 +24,26 @@ public class ClientEntrypoint { private static final DslJson JSON = new DslJson<>(Settings.basicSetup()); + public static final String WORKSHOP_ID = "3776625738"; static void main(final String[] args) { try { final File proxyJar = getProxyJarPath(); - Utils.initLog(proxyJar.getParentFile()); + final Path gameJar = ProxyPaths.gameJar(proxyJar.toPath(), Path.of("")); + Utils.initLog(gameJar.getParent().toFile()); log("Initialised logging for leaf-loader-proxy."); log("Proxy args: " + Arrays.toString(Main.args.toArray())); - - final File gameJar = proxyJar.toPath().getParent().resolve("projectzomboid.jar").toFile(); + log("Proxy JAR: " + proxyJar); + log("Game JAR: " + gameJar); // If there's a better way to not hardcode this ID... - final File[] loaderJars = getLoaderJars(proxyJar, "3776625738"); + final File[] loaderJars = getLoaderJars(gameJar); final URL[] urls = new URL[loaderJars.length + 1]; for (int i = 0; i < loaderJars.length; ++i) { urls[i] = loaderJars[i].toURI().toURL(); } - urls[urls.length - 1] = gameJar.toURI().toURL(); + urls[urls.length - 1] = gameJar.toUri().toURL(); // Remove the proxy jar from the classpath ASAP Utils.removeFromClasspath(s -> s.endsWith(proxyJar.getName())); @@ -55,7 +58,7 @@ static void main(final String[] args) { log("Classpath is currently: " + System.getProperty(Constants.Properties.CLASS_PATH)); try (final URLClassLoader cl = new URLClassLoader(urls, - ClientEntrypoint.class.getClassLoader().getParent())) { + ClassLoader.getPlatformClassLoader())) { log("Classloader was created successfully."); final Class entrypoint = Class.forName(getLoaderEntrypoint(cl), true, cl); @@ -78,41 +81,14 @@ static void main(final String[] args) { } private static File getProxyJarPath() throws URISyntaxException { - return new File(ClientEntrypoint.class.getProtectionDomain().getCodeSource().getLocation().toURI()); + return new File(Main.class.getProtectionDomain().getCodeSource().getLocation().toURI()); } - private static File[] getLoaderJars(final File proxyJar, final String workshopId) { + private static File[] getLoaderJars(final Path gameJar) throws IOException { log("Attempting to get loader jars..."); - - final Path jarDir; - if (Main.args.containsKey("loaderPath")) { - jarDir = Path.of(Main.args.get("loaderPath")); - } else { - final Path workshopPath = getWorkshopPath(proxyJar); - final Path modPath = workshopPath.resolve(workshopId, "mods", "LeafLoader"); - jarDir = modPath.resolve("common", "media", "java"); - } - - final File[] jars = jarDir - .toFile() - .listFiles((_, name) -> name.endsWith(".jar") && !name.endsWith("-sources.jar")); - - if (jars == null || jars.length == 0) { - final var e = new RuntimeException("Loader JAR doesn't exist in the workshop mod"); - log(e); - throw e; - } - - return jars; - } - - private static Path getWorkshopPath(final File proxyJar) { - Path gamePath = proxyJar.toPath().getParent(); - if ("projectzomboid".equals(gamePath.getFileName().toString())) { - gamePath = gamePath.getParent(); - } - - return gamePath.getParent().getParent().resolve("workshop", "content", "108600"); + final Path jarDir = ProxyPaths.loaderDirectory(gameJar, Main.args.get("loaderPath")); + log("Loader directory: " + jarDir); + return ProxyPaths.loaderJars(jarDir).stream().map(Path::toFile).toArray(File[]::new); } private static String getLoaderEntrypoint(final URLClassLoader cl) throws IOException {