Skip to content

Commit

Permalink
add mod loading for jars included in plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
vectrixdevelops committed Nov 21, 2024
1 parent bcbc180 commit 062c5c0
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 8 deletions.
1 change: 1 addition & 0 deletions api/src/main/java/space/vectrix/ignite/Blackboard.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public final class Blackboard {
public static final BlackboardMap.@NotNull Key<Path> GAME_JAR = key("ignite.jar", Path.class, Paths.get("./server.jar"));
public static final BlackboardMap.@NotNull Key<String> GAME_TARGET = key("ignite.target", String.class, "org.bukkit.craftbukkit.Main");
public static final BlackboardMap.@NotNull Key<Path> GAME_LIBRARIES = key("ignite.libraries", Path.class, Paths.get("./libraries"));
public static final BlackboardMap.@NotNull Key<Path> PLUGINS_DIRECTORY = key("ignite.plugins", Path.class, Paths.get("./plugins"));
public static final BlackboardMap.@NotNull Key<Path> MODS_DIRECTORY = key("ignite.mods", Path.class, Paths.get("./mods"));
// formatting:on

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ private void run(final String@NotNull [] args) {
Blackboard.compute(Blackboard.GAME_JAR, () -> Paths.get(System.getProperty(Blackboard.GAME_JAR.name())));
Blackboard.compute(Blackboard.GAME_TARGET, () -> System.getProperty(Blackboard.GAME_TARGET.name()));
Blackboard.compute(Blackboard.GAME_LIBRARIES, () -> Paths.get(System.getProperty(Blackboard.GAME_LIBRARIES.name())));
Blackboard.compute(Blackboard.PLUGINS_DIRECTORY, () -> Paths.get(System.getProperty(Blackboard.PLUGINS_DIRECTORY.name())));
Blackboard.compute(Blackboard.MODS_DIRECTORY, () -> Paths.get(System.getProperty(Blackboard.MODS_DIRECTORY.name())));

// Get a suitable game locator and game provider.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,20 @@
package space.vectrix.ignite.mod;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
import org.tinylog.Logger;
import space.vectrix.ignite.Blackboard;
import space.vectrix.ignite.IgniteBootstrap;
import space.vectrix.ignite.util.IgniteConstants;
Expand All @@ -56,7 +61,14 @@ public final class ModResourceLocator {
resources.add(this.createLauncherResource());
resources.add(this.createGameResource());

// Retrieve the mods from the mods directory.
// Retrieve the mods from the mods directory, or inside plugins.
this.scanModsDirectory(resources);
this.scanPluginsDirectory(resources);

return resources;
}

private void scanModsDirectory(final @NotNull List<ModResourceImpl> resources) {
final Path modDirectory = Blackboard.raw(Blackboard.MODS_DIRECTORY);
try {
if(modDirectory == null) {
Expand All @@ -69,23 +81,58 @@ public final class ModResourceLocator {
}

//noinspection resource
for(final Path childDirectory : Files.walk(modDirectory).collect(Collectors.toList())) {
for(final Path childDirectory : Files.walk(modDirectory, 1).collect(Collectors.toList())) {
if(!Files.isRegularFile(childDirectory) || !childDirectory.getFileName().toString().endsWith(".jar")) {
continue;
}

this.tryLoadMod(childDirectory, resources);
}
} catch(final Throwable throwable) {
throw new RuntimeException("Failed to walk the mods directory!", throwable);
}
}

private void scanPluginsDirectory(final @NotNull List<ModResourceImpl> resources) {
final Path modDirectory = Blackboard.raw(Blackboard.MODS_DIRECTORY);
final Path pluginDirectory = Blackboard.raw(Blackboard.PLUGINS_DIRECTORY);
try {
if(modDirectory == null
|| pluginDirectory == null
|| Files.notExists(pluginDirectory)
|| Files.notExists(modDirectory)) {
return;
}

final Path destinationPath = modDirectory.resolve("_plugins");

//noinspection resource
for(final Path childDirectory : Files.walk(pluginDirectory, 1).collect(Collectors.toList())) {
if(!Files.isRegularFile(childDirectory) || !childDirectory.getFileName().toString().endsWith(".jar")) {
continue;
}

try(final JarFile jarFile = new JarFile(childDirectory.toFile())) {
final JarEntry jarEntry = jarFile.getJarEntry(IgniteConstants.MOD_CONFIG);
if(jarEntry == null) continue;
final Enumeration<JarEntry> entries = jarFile.entries();

resources.add(new ModResourceImpl(ModResourceLocator.JAVA_LOCATOR, childDirectory, jarFile.getManifest()));
while (entries.hasMoreElements()) {
final JarEntry entry = entries.nextElement();

if(entry.getName().endsWith(".mixin.jar")) {
Logger.debug("Located mixin jar '{}' in plugin '{}'. Extracting to temporary mod directory...", entry.getName(), jarFile.getName());
final Path modPath = this.extractModFromPlugin(
childDirectory.getFileName().toString(),
jarFile, entry, destinationPath
);

this.tryLoadMod(modPath, resources);
}
}
}
}
} catch(final Throwable throwable) {
throw new RuntimeException("Failed to walk the mods directory!", throwable);
throw new RuntimeException("Failed to walk the plugins directory!", throwable);
}

return resources;
}

private @NotNull ModResourceImpl createLauncherResource() {
Expand All @@ -111,4 +158,39 @@ public final class ModResourceLocator {
throw new RuntimeException("Failed to get game manifest!", exception);
}
}

private Path extractModFromPlugin(final @NotNull String jarName,
final @NotNull JarFile jarFile,
final @NotNull JarEntry jarEntry,
final @NotNull Path destinationDirectory) {
final String baseJarName = jarName.substring(0, jarName.length() - ".jar".length());
final Path outputPath = destinationDirectory.resolve(baseJarName).resolve(jarEntry.getName());

if(Files.notExists(outputPath)) {
//noinspection ResultOfMethodCallIgnored
outputPath.toFile().mkdirs();
}

try {
try(InputStream inputStream = jarFile.getInputStream(jarEntry)) {
Files.copy(inputStream, outputPath, StandardCopyOption.REPLACE_EXISTING);
}
} catch (final IOException exception) {
throw new RuntimeException(exception);
}

return outputPath;
}

private void tryLoadMod(final @NotNull Path modPath, final @NotNull List<ModResourceImpl> resources) throws IOException {
try(final JarFile jarFile = new JarFile(modPath.toFile())) {
final JarEntry jarEntry = jarFile.getJarEntry(IgniteConstants.MOD_CONFIG);
if(jarEntry == null) {
Logger.warn("'{}' was in the mods directory, but could not load due to a missing '{}'.", jarFile.getName(), IgniteConstants.MOD_CONFIG);
return;
}

resources.add(new ModResourceImpl(ModResourceLocator.JAVA_LOCATOR, modPath, jarFile.getManifest()));
}
}
}
5 changes: 5 additions & 0 deletions launcher/src/main/java/space/vectrix/ignite/mod/ModsImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ public boolean locateResources() {
final ModResource resource = container.resource();

if(!resource.locator().equals(ModResourceLocator.LAUNCHER_LOCATOR) && !resource.locator().equals(ModResourceLocator.GAME_LOCATOR)) {
if(this.containers.containsKey(container.id())) {
Logger.error("Mod container with the identifier '{}' already exists!", container.id());
continue;
}

try {
IgniteAgent.addJar(container.resource().path());
} catch(final IOException exception) {
Expand Down

0 comments on commit 062c5c0

Please sign in to comment.