Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions src/main/java/dev/aoqia/leaf/proxy/ProxyPaths.java
Original file line number Diff line number Diff line change
@@ -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<String> 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=<directory containing the loader JARs>.");
}

public static List<Path> 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<Path> 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;
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/dev/aoqia/leaf/proxy/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,34 @@

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;

import static dev.aoqia.leaf.proxy.Utils.log;

public class ClientEntrypoint {
private static final DslJson<Object> 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()));
Expand All @@ -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);
Expand All @@ -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 {
Expand Down
Loading