diff --git a/README.md b/README.md index f677638..aaefb9b 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,11 @@ Modern solution for minecraft resource pack development ## Features -- [Resource pack obfuscation](#obfuscation) -- [Auto CustomModelData Mapping](#autocmd) -- [Auto fancyPants custom armor](#customarmor) -- And much more... +- [Models generating](#meshes--models) +- [Auto CustomModelData](#auto-custommodeldata) +- [Auto sounds FFmpeg conversion](#sounds) +- [Auto fancyPants (custom armor)](#armor-textures) +- [Obfuscation](#obfuscation) ## Example Project Look at the [example project](https://github.com/Nelonn/ProPack/blob/master/propack-core/src/main/resources/example) @@ -20,10 +21,11 @@ Json is used in lenient mode, possible file extensions: Some futures from json 5 will not work due to [GSON](https://github.com/google/gson) flaws ## Meshes & Models -[Java Block/Item models](https://minecraft.fandom.com/wiki/Model) in the ProPack are called meshes. -Their files should end with `.mesh.json` +[Java Block/Item models](https://minecraft.fandom.com/wiki/Model) in the ProPack are called *meshes*. +Their files should end with `.mesh.json`. + +Models configuration file name should end with `.model.json`. -Models file name should end with `.model.json`. Currently implemented model types:
@@ -85,9 +87,9 @@ SlotItemModel, example: } ``` -`Target` - this is an indication of the items for which you need to `Auto CustomModelData Mapping` of the specified model +`Target` - this is an indication of the items for which you need to [Auto CustomModelData](#auto-custommodeldata) of the specified model. -## Auto CustomModelData Mapping +## Auto CustomModelData When building resource pack ProPack takes the default model from the folder `include/assets/minecraft/models/item/.json` and adds the necessary elements to override. @@ -106,18 +108,17 @@ Example NBT tag for SlotItemModel: `{CustomModel:"example:models/example_slotmod ## Sounds -Sound files must ends with `.sound.json` -- Example: `scream.sound.json` +Sound files must ends with `.sound.json`, eg. `scream.sound.json` Sound file paths can be relative, example `../folder/sound`. FFmpeg auto conversion is supported. Use `-Dpropack.ffmpeg=path` to indicate its location, by default it `ffmpeg` -## Armor Textures +## Armor Textures Added an automatic builder of custom textures for leather armor for the [shader `fancyPants`](https://github.com/Ancientkingg/fancyPants) -Armor file must ends with `.armor.json` +Armor file must ends with `.armor.json`, eg. `emerald.armor.json` [Example file](): ```json @@ -146,32 +147,36 @@ The second animated version: } } ``` -`.png` is not required to be specified +`.png` is not required to be specified. + For more information, see [README.md of fancyPants](https://github.com/Ancientkingg/fancyPants/blob/master/README.md) `SaveImage` allows you to determine whether to save the specified `Image` in the output resource pack ## Languages -Language file must ends with `.lang.json` -- Example: `en_us.lang.json` +Language file must ends with `.lang.json`, eg. `en_us.lang.json`. + +Path in content doesn't matter. -There is also a placeholder ``. Multiple languages in the same namespace will be merged into one. +There is also a placeholder ``. + ## Fonts Fonts work unchanged except for the file extension `.font.json` -## Obfuscation +## Obfuscation It just obfuscates the entire resource pack, except for translations. The settings for this function are in [`config/build.json5`](https://github.com/Nelonn/ProPack/blob/master/propack-core/src/main/resources/example/config/build.json5) ## Planned in the future +- [ ] Rewrite builder to C++ or Golang as binary executable. - [ ] Integration into [ItemsAdder](https://www.spigotmc.org/resources/%E2%9C%A8itemsadder%E2%AD%90emotes-mobs-items-armors-hud-gui-emojis-blocks-wings-hats-liquids.73355/), [Oraxen](https://www.spigotmc.org/resources/%E2%98%84%EF%B8%8F-oraxen-add-items-blocks-armors-hats-food-furnitures-plants-and-gui.72448/), [Model Engine](https://www.spigotmc.org/resources/conxeptworks-model-engine—ultimate-custom-entity-model-manager-1-16-5-1-19-3.79477/), etc. -- [ ] Fonts generating -- [ ] CI/CD using [Redis](https://redis.io/), etc. -- [ ] Improve the quality of the code and API -- [ ] Mod for [Fabric](https://fabricmc.net/) +- [ ] Fonts generating. +- [ ] Resource pack CI/CD using [Redis](https://redis.io/), etc. (for multi server) +- [ ] Improve the quality of the code and API. +- [ ] Mod for [Fabric](https://fabricmc.net/). ## License *Click here to read [the entire license](https://github.com/Nelonn/ProPack/blob/master/LICENSE.txt).* \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 2e296a0..fb663fe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group=me.nelonn.propack -version=0.0.7 +version=0.0.8 org.gradle.jvmargs=-Xmx2G org.gradle.parallel=true diff --git a/libs/flint-path-0.0.1.jar b/libs/lib-flint-path-0.0.1.jar similarity index 76% rename from libs/flint-path-0.0.1.jar rename to libs/lib-flint-path-0.0.1.jar index f43f0c9..5ebb571 100644 Binary files a/libs/flint-path-0.0.1.jar and b/libs/lib-flint-path-0.0.1.jar differ diff --git a/propack-api/build.gradle.kts b/propack-api/build.gradle.kts index d61b2e3..6db0184 100644 --- a/propack-api/build.gradle.kts +++ b/propack-api/build.gradle.kts @@ -12,7 +12,7 @@ repositories { } dependencies { - "compileOnly"(files("../libs/flint-path-0.0.1.jar")) + "compileOnly"(files("../libs/lib-flint-path-0.0.1.jar")) "compileOnly"("net.kyori:adventure-api:4.12.0") "compileOnly"("org.jetbrains:annotations:23.1.0") } diff --git a/propack-bukkit/adapters/adapter-1.17.1/build.gradle.kts b/propack-bukkit/adapters/adapter-1.17.1/build.gradle.kts index de034a0..f8731e3 100644 --- a/propack-bukkit/adapters/adapter-1.17.1/build.gradle.kts +++ b/propack-bukkit/adapters/adapter-1.17.1/build.gradle.kts @@ -11,7 +11,7 @@ java.toolchain.languageVersion.set(JavaLanguageVersion.of(17)) dependencies { // https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ paperDevBundle("1.17.1-R0.1-20220414.034903-210") - "compileOnly"(files("../../../libs/flint-path-0.0.1.jar")) + "compileOnly"(files("../../../libs/lib-flint-path-0.0.1.jar")) "compileOnly"(project(":propack-api")) "compileOnly"(project(":propack-core")) "compileOnly"(project(":propack-bukkit")) diff --git a/propack-bukkit/adapters/adapter-1.18.2/build.gradle.kts b/propack-bukkit/adapters/adapter-1.18.2/build.gradle.kts index 1147ff6..a920785 100644 --- a/propack-bukkit/adapters/adapter-1.18.2/build.gradle.kts +++ b/propack-bukkit/adapters/adapter-1.18.2/build.gradle.kts @@ -11,7 +11,7 @@ java.toolchain.languageVersion.set(JavaLanguageVersion.of(17)) dependencies { // https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ paperDevBundle("1.18.2-R0.1-20220304.102823-4") - "compileOnly"(files("../../../libs/flint-path-0.0.1.jar")) + "compileOnly"(files("../../../libs/lib-flint-path-0.0.1.jar")) "compileOnly"(project(":propack-api")) "compileOnly"(project(":propack-core")) "compileOnly"(project(":propack-bukkit")) diff --git a/propack-bukkit/adapters/adapter-1.19.3/build.gradle.kts b/propack-bukkit/adapters/adapter-1.19.3/build.gradle.kts index 2278b22..c65b1e0 100644 --- a/propack-bukkit/adapters/adapter-1.19.3/build.gradle.kts +++ b/propack-bukkit/adapters/adapter-1.19.3/build.gradle.kts @@ -11,7 +11,7 @@ java.toolchain.languageVersion.set(JavaLanguageVersion.of(17)) dependencies { // https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ paperDevBundle("1.19.3-R0.1-20221226.180038-55") - "compileOnly"(files("../../../libs/flint-path-0.0.1.jar")) + "compileOnly"(files("../../../libs/lib-flint-path-0.0.1.jar")) "compileOnly"(project(":propack-api")) "compileOnly"(project(":propack-core")) "compileOnly"(project(":propack-bukkit")) diff --git a/propack-bukkit/adapters/adapter-1.19.4/build.gradle.kts b/propack-bukkit/adapters/adapter-1.19.4/build.gradle.kts index 0409584..3bfea74 100644 --- a/propack-bukkit/adapters/adapter-1.19.4/build.gradle.kts +++ b/propack-bukkit/adapters/adapter-1.19.4/build.gradle.kts @@ -11,7 +11,7 @@ java.toolchain.languageVersion.set(JavaLanguageVersion.of(17)) dependencies { // https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ paperDevBundle("1.19.4-R0.1-20230315.180636-1") - "compileOnly"(files("../../../libs/flint-path-0.0.1.jar")) + "compileOnly"(files("../../../libs/lib-flint-path-0.0.1.jar")) "compileOnly"(project(":propack-api")) "compileOnly"(project(":propack-core")) "compileOnly"(project(":propack-bukkit")) diff --git a/propack-bukkit/adapters/adapter-1.19/build.gradle.kts b/propack-bukkit/adapters/adapter-1.19/build.gradle.kts index 30ee9db..f4ab445 100644 --- a/propack-bukkit/adapters/adapter-1.19/build.gradle.kts +++ b/propack-bukkit/adapters/adapter-1.19/build.gradle.kts @@ -11,7 +11,7 @@ java.toolchain.languageVersion.set(JavaLanguageVersion.of(17)) dependencies { // https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ paperDevBundle("1.19-R0.1-20220609.175204-1") - "compileOnly"(files("../../../libs/flint-path-0.0.1.jar")) + "compileOnly"(files("../../../libs/lib-flint-path-0.0.1.jar")) "compileOnly"(project(":propack-api")) "compileOnly"(project(":propack-core")) "compileOnly"(project(":propack-bukkit")) diff --git a/propack-bukkit/build.gradle.kts b/propack-bukkit/build.gradle.kts index 4ddc3ce..17f4eca 100644 --- a/propack-bukkit/build.gradle.kts +++ b/propack-bukkit/build.gradle.kts @@ -37,7 +37,7 @@ dependencies { "implementation"(project(":propack-core")) { exclude(group = "net.kyori") } - "compileOnly"(files("../libs/flint-path-0.0.1.jar")) + "compileOnly"(files("../libs/lib-flint-path-0.0.1.jar")) /*"compileOnly"("org.spigotmc:spigot-api:1.17-R0.1-SNAPSHOT") { exclude("junit", "junit") diff --git a/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/ProPackPlugin.java b/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/ProPackPlugin.java index 0628267..bb723cf 100644 --- a/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/ProPackPlugin.java +++ b/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/ProPackPlugin.java @@ -35,7 +35,7 @@ public final class ProPackPlugin extends JavaPlugin { private static final Logger LOGGER = LogManagerCompat.getLogger(); - private static final SharedLoader.Library library = new SharedLoader.Library("flint-path-0.0.1.jar", "me.nelonn.flint.path.Path"); + private static final SharedLoader.Library library = new SharedLoader.Library("lib-flint-path-0.0.1.jar", "me.nelonn.flint.path.Path"); public static ProPackPlugin getInstance() { return (ProPackPlugin) Bukkit.getPluginManager().getPlugin("ProPack"); diff --git a/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/SharedLoader.java b/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/SharedLoader.java index 52f7f9b..7999b36 100644 --- a/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/SharedLoader.java +++ b/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/SharedLoader.java @@ -25,8 +25,6 @@ import java.io.File; import java.io.InputStream; import java.io.OutputStream; -import java.net.URL; -import java.net.URLClassLoader; import java.nio.file.Files; public final class SharedLoader { @@ -56,8 +54,8 @@ public void loadIfNotExists(@NotNull Library library) { in.transferTo(out); } } - URLClassLoader loader = new URLClassLoader(new URL[]{file.toURI().toURL()}, Bukkit.class.getClassLoader()); - Class.forName(library.getCheckClass(), true, loader); + Bukkit.getPluginManager().loadPlugin(file); + Class.forName(library.getCheckClass()); } catch (Exception e) { throw new RuntimeException("Unable to load shared library '" + library.getFileName() + "'", e); } diff --git a/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/command/BuildCommand.java b/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/command/BuildCommand.java index 8085b76..85569d6 100644 --- a/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/command/BuildCommand.java +++ b/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/command/BuildCommand.java @@ -24,7 +24,6 @@ import me.nelonn.propack.bukkit.Util; import me.nelonn.propack.bukkit.definition.PackDefinition; import me.nelonn.propack.bukkit.definition.ProjectDefinition; -import me.nelonn.propack.core.builder.InternalProject; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -56,9 +55,8 @@ protected void onCommand(@NotNull CommandSender sender, @NotNull String s, @NotN } new Thread(() -> { try { - InternalProject internalProject = (InternalProject) projectDefinition.getProject(); - internalProject.build(); - ResourcePack resourcePack = internalProject.getResourcePack().orElseThrow(); + projectDefinition.build(); + ResourcePack resourcePack = projectDefinition.getResourcePack().orElseThrow(); if (resourcePack.isUploaded()) { for (Player player : Bukkit.getOnlinePlayers()) { Optional playerPack = ProPack.getCore().getDispatcher().getAppliedResourcePack(player); diff --git a/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/command/HelpCommand.java b/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/command/HelpCommand.java index e74b278..a432399 100644 --- a/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/command/HelpCommand.java +++ b/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/command/HelpCommand.java @@ -35,5 +35,6 @@ public HelpCommand(@NotNull ProPackPlugin plugin) { protected void onCommand(@NotNull CommandSender sender, @NotNull String command, @NotNull String[] args) { Util.send(sender, "" + plugin.getDescription().getName() + " v" + plugin.getDescription().getVersion()); Util.send(sender, "/propack build "); + Util.send(sender, "/propack reload [config|modules|packs]"); } } diff --git a/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/definition/PackManager.java b/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/definition/PackManager.java index 4b77cf2..a4c84c2 100644 --- a/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/definition/PackManager.java +++ b/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/definition/PackManager.java @@ -20,7 +20,6 @@ import com.google.gson.JsonObject; import me.nelonn.propack.core.ProPackCore; -import me.nelonn.propack.core.builder.InternalProject; import me.nelonn.propack.core.loader.ProjectLoader; import me.nelonn.propack.core.util.GsonHelper; import me.nelonn.propack.core.util.LogManagerCompat; @@ -65,12 +64,8 @@ public void loadAll() { if (type.equalsIgnoreCase("Project")) { boolean buildAtStartup = GsonHelper.getBoolean(jsonObject, "BuildAtStartup", false); File projectFile = new File(directory, name + File.separatorChar + "project.json5"); - InternalProject internalProject = projectLoader.load(projectFile, !buildAtStartup); - if (buildAtStartup || internalProject.getResourcePack().isEmpty()) { - internalProject.build(); // TODO: improve - } - ProjectDefinition resourcePackDefinition = new ProjectDefinition(internalProject); - definitions.put(name, resourcePackDefinition); + ProjectDefinition projectDefinition = new ProjectDefinition(projectFile, projectLoader, !buildAtStartup); + definitions.put(name, projectDefinition); } else if (type.equalsIgnoreCase("File")) { throw new UnsupportedOperationException("Resource pack definition type 'File' currently not supported"); } diff --git a/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/definition/ProjectDefinition.java b/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/definition/ProjectDefinition.java index e71b67d..43f15ae 100644 --- a/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/definition/ProjectDefinition.java +++ b/propack-bukkit/src/main/java/me/nelonn/propack/bukkit/definition/ProjectDefinition.java @@ -19,16 +19,26 @@ package me.nelonn.propack.bukkit.definition; import me.nelonn.propack.ResourcePack; -import me.nelonn.propack.builder.Project; +import me.nelonn.propack.core.builder.InternalProject; +import me.nelonn.propack.core.loader.ProjectLoader; import org.jetbrains.annotations.NotNull; +import java.io.File; import java.util.Optional; public class ProjectDefinition implements PackDefinition { - private final Project project; + private final File file; + private InternalProject project; + private ProjectLoader projectLoader; - public ProjectDefinition(@NotNull Project project) { - this.project = project; + public ProjectDefinition(@NotNull File file, @NotNull ProjectLoader projectLoader, boolean tryLoadBuilt) { + this.file = file; + this.projectLoader = projectLoader; + if (tryLoadBuilt) { + loadOrBuild(); + } else { + build(); + } } @Override @@ -41,7 +51,35 @@ public ProjectDefinition(@NotNull Project project) { return project.getResourcePack(); } - public @NotNull Project getProject() { + public @NotNull File getFile() { + return file; + } + + public @NotNull InternalProject getProject() { return project; } + + public @NotNull ProjectLoader getProjectLoader() { + return projectLoader; + } + + public void setProjectLoader(@NotNull ProjectLoader projectLoader) { + this.projectLoader = projectLoader; + } + + public void loadOrBuild() { + project = projectLoader.load(file, true); + if (project.getResourcePack().isEmpty()) { + build0(); + } + } + + public void build() { + project = projectLoader.load(file, false); + build0(); + } + + private void build0() { + project.build(); // TODO: improve + } } diff --git a/propack-bukkit/src/main/resources/flint-path-0.0.1.jar.shared b/propack-bukkit/src/main/resources/lib-flint-path-0.0.1.jar.shared similarity index 76% rename from propack-bukkit/src/main/resources/flint-path-0.0.1.jar.shared rename to propack-bukkit/src/main/resources/lib-flint-path-0.0.1.jar.shared index f43f0c9..5ebb571 100644 Binary files a/propack-bukkit/src/main/resources/flint-path-0.0.1.jar.shared and b/propack-bukkit/src/main/resources/lib-flint-path-0.0.1.jar.shared differ diff --git a/propack-cli/build.gradle.kts b/propack-cli/build.gradle.kts index ab03829..19a3995 100644 --- a/propack-cli/build.gradle.kts +++ b/propack-cli/build.gradle.kts @@ -16,7 +16,7 @@ repositories { dependencies { "implementation"(project(":propack-api")) "implementation"(project(":propack-core")) - "implementation"(files("../libs/flint-path-0.0.1.jar")) + "implementation"(files("../libs/lib-flint-path-0.0.1.jar")) "implementation"("org.eclipse.jgit:org.eclipse.jgit:6.4.0.202211300538-r") diff --git a/propack-core/build.gradle.kts b/propack-core/build.gradle.kts index 6e35b5e..4374f58 100644 --- a/propack-core/build.gradle.kts +++ b/propack-core/build.gradle.kts @@ -16,7 +16,7 @@ repositories { dependencies { "implementation"(project(":propack-api")) - "compileOnly"(files("../libs/flint-path-0.0.1.jar")) + "compileOnly"(files("../libs/lib-flint-path-0.0.1.jar")) "compileOnly"("org.eclipse.jgit:org.eclipse.jgit:6.4.0.202211300538-r") diff --git a/propack-core/src/main/java/me/nelonn/propack/core/loader/ProjectLoader.java b/propack-core/src/main/java/me/nelonn/propack/core/loader/ProjectLoader.java index 29b5620..4272b04 100644 --- a/propack-core/src/main/java/me/nelonn/propack/core/loader/ProjectLoader.java +++ b/propack-core/src/main/java/me/nelonn/propack/core/loader/ProjectLoader.java @@ -81,7 +81,7 @@ public List getTextLoaders() { return textLoaders; } - public @NotNull InternalProject load(@NotNull File projectFile, boolean loadBuilt) { + public @NotNull InternalProject load(@NotNull File projectFile, boolean tryLoadBuilt) { String name; VirtualFile packMeta; VirtualFile packIcon; @@ -287,7 +287,7 @@ public List getTextLoaders() { ResourcePack resourcePack = null; File builtResourcePack = new File(projectFile.getParentFile(), "build/" + name + ".propack"); - if (builtResourcePack.exists() && loadBuilt) { + if (builtResourcePack.exists() && tryLoadBuilt) { try { ResourcePackLoader resourcePackLoader = new ResourcePackLoader(); resourcePack = resourcePackLoader.load(builtResourcePack); diff --git a/propack-core/src/main/java/me/nelonn/propack/module/JavaModuleManager.java b/propack-core/src/main/java/me/nelonn/propack/module/JavaModuleManager.java index 704b702..9497cf7 100644 --- a/propack-core/src/main/java/me/nelonn/propack/module/JavaModuleManager.java +++ b/propack-core/src/main/java/me/nelonn/propack/module/JavaModuleManager.java @@ -19,6 +19,7 @@ package me.nelonn.propack.module; import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; import me.nelonn.propack.core.util.GsonHelper; import me.nelonn.propack.core.util.LogManagerCompat; import org.apache.commons.io.IOUtils; @@ -27,6 +28,7 @@ import org.jetbrains.annotations.Nullable; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -37,6 +39,8 @@ import java.nio.file.Path; import java.util.HashMap; import java.util.Map; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; public class JavaModuleManager implements ModuleManager { private static final Logger LOGGER = LogManagerCompat.getLogger(); @@ -65,31 +69,72 @@ public void loadAll() throws IOException { }); for (Path path : stream) { try { - URLClassLoader child = new URLClassLoader(new URL[]{path.toUri().toURL()}, getClass().getClassLoader()); - InputStream inputStream = child.getResourceAsStream("module.json"); - if (inputStream == null) continue; - String moduleDescriptionString = IOUtils.toString(inputStream, StandardCharsets.UTF_8); - JsonObject jsonObject = GsonHelper.deserialize(moduleDescriptionString); - JsonModuleDescription moduleMeta = JsonModuleDescription.deserialize(jsonObject); - try { - Class bootstrapper = Class.forName(moduleMeta.getBootstrapper(), true, child); - if (!ModuleBootstrap.class.isAssignableFrom(bootstrapper)) { - throw new IllegalArgumentException("Bootstrapper class must implement '" + ModuleBootstrap.class.getName() + "'"); - } - ModuleBootstrap moduleBootstrap = (ModuleBootstrap) bootstrapper.getDeclaredConstructor().newInstance(); - File dataDir = new File(path.getParent().toFile(), moduleMeta.getName()); - ModuleProviderContext context = new ModuleProviderContextImpl(moduleMeta, dataDir.toPath()); - moduleBootstrap.bootstrap(context); - JavaModule module = moduleBootstrap.createModule(context); - module.enable(); - modules.put(moduleMeta.getName(), module); - } catch (Exception e) { - LOGGER.error("Error occurred while enabling " + moduleMeta.getDisplayName() + " (Is it up to date?)", e); - } + loadModule(path.toFile()); } catch (Exception e) { LOGGER.error("Unable to load module '" + path.getFileName() + "'", e); } } + stream.close(); + } + + public @NotNull Module loadModule(@NotNull File file) { + JsonModuleDescription description; + try { + description = getModuleDescription(file); + } catch (InvalidDescriptionException e) { + throw new IllegalArgumentException("Invalid module", e); + } + File dataFolder = new File(modulesDir, description.getName()); + try { + URLClassLoader child = new URLClassLoader(new URL[]{file.toURI().toURL()}, getClass().getClassLoader()); + Class bootstrapper = Class.forName(description.getBootstrapper(), true, child); + if (!ModuleBootstrap.class.isAssignableFrom(bootstrapper)) { + throw new IllegalArgumentException("Bootstrapper class must implement '" + ModuleBootstrap.class.getName() + "'"); + } + ModuleBootstrap moduleBootstrap = (ModuleBootstrap) bootstrapper.getDeclaredConstructor().newInstance(); + ModuleProviderContext context = new ModuleProviderContextImpl(description, dataFolder.toPath()); + moduleBootstrap.bootstrap(context); + JavaModule module = moduleBootstrap.createModule(context); + module.enable(); + modules.put(description.getName(), module); + return module; + } catch (Exception e) { + throw new IllegalArgumentException("Invalid module", e); + } + } + + public @NotNull JsonModuleDescription getModuleDescription(@NotNull File file) throws InvalidDescriptionException { + JarFile jar = null; + InputStream stream = null; + try { + jar = new JarFile(file); + JarEntry entry = jar.getJarEntry("module.json"); + + if (entry == null) { + throw new InvalidDescriptionException(new FileNotFoundException("Jar does not contain module.json")); + } + + stream = jar.getInputStream(entry); + + String moduleDescriptionString = IOUtils.toString(stream, StandardCharsets.UTF_8); + JsonObject jsonObject = GsonHelper.deserialize(moduleDescriptionString); + return JsonModuleDescription.deserialize(jsonObject); + } catch (IOException | JsonParseException ex) { + throw new InvalidDescriptionException(ex); + } finally { + if (jar != null) { + try { + jar.close(); + } catch (IOException ignored) { + } + } + if (stream != null) { + try { + stream.close(); + } catch (IOException ignored) { + } + } + } } @Override