From 78f717fb480e468e37aa65773519b34822817e03 Mon Sep 17 00:00:00 2001 From: MrMicky Date: Sat, 21 Oct 2023 19:19:30 +0200 Subject: [PATCH] Manually download netty-codec-http when missing (fixes #29) --- build.gradle | 2 +- .../azlink/bukkit/AzLinkBukkitPlugin.java | 10 +++ .../azlink/bukkit/injector/HttpDecoder.java | 27 ------- .../bukkit/injector/NettyLibraryLoader.java | 78 +++++++++++++++++++ .../azuriom/azlink/common/AzLinkPlugin.java | 6 +- universal-legacy/build.gradle | 4 +- 6 files changed, 92 insertions(+), 35 deletions(-) create mode 100644 bukkit/src/main/java/com/azuriom/azlink/bukkit/injector/NettyLibraryLoader.java diff --git a/build.gradle b/build.gradle index 69daffc..e051917 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ allprojects { group 'com.azuriom' - version '1.3.1' + version '1.3.2' } subprojects { diff --git a/bukkit/src/main/java/com/azuriom/azlink/bukkit/AzLinkBukkitPlugin.java b/bukkit/src/main/java/com/azuriom/azlink/bukkit/AzLinkBukkitPlugin.java index 09780da..a042b87 100644 --- a/bukkit/src/main/java/com/azuriom/azlink/bukkit/AzLinkBukkitPlugin.java +++ b/bukkit/src/main/java/com/azuriom/azlink/bukkit/AzLinkBukkitPlugin.java @@ -3,6 +3,7 @@ import com.azuriom.azlink.bukkit.command.BukkitCommandExecutor; import com.azuriom.azlink.bukkit.command.BukkitCommandSender; import com.azuriom.azlink.bukkit.injector.InjectedHttpServer; +import com.azuriom.azlink.bukkit.injector.NettyLibraryLoader; import com.azuriom.azlink.bukkit.integrations.AuthMeIntegration; import com.azuriom.azlink.bukkit.integrations.FoliaSchedulerAdapter; import com.azuriom.azlink.bukkit.integrations.MoneyPlaceholderExpansion; @@ -55,6 +56,15 @@ public void onEnable() { this.plugin = new AzLinkPlugin(this) { @Override protected HttpServer createHttpServer() { + NettyLibraryLoader libraryLoader = new NettyLibraryLoader(this); + + try { + libraryLoader.loadRequiredLibraries(); + } catch (Exception e) { + getLogger().error("Unable to load required libraries for instant commands", e); + return null; + } + if (plugin.getConfig().getHttpPort() == getServer().getPort()) { return new InjectedHttpServer(AzLinkBukkitPlugin.this); } diff --git a/bukkit/src/main/java/com/azuriom/azlink/bukkit/injector/HttpDecoder.java b/bukkit/src/main/java/com/azuriom/azlink/bukkit/injector/HttpDecoder.java index aa3c417..dbd09a0 100644 --- a/bukkit/src/main/java/com/azuriom/azlink/bukkit/injector/HttpDecoder.java +++ b/bukkit/src/main/java/com/azuriom/azlink/bukkit/injector/HttpDecoder.java @@ -20,10 +20,6 @@ */ public class HttpDecoder extends ByteToMessageDecoder { - // Since Spigot 1.19, netty-codec-http is no longer included in SpigotMC. - // PaperMC must be used instead to have instant commands. - private final boolean supported = isSupported(); - private final AzLinkPlugin plugin; public HttpDecoder(AzLinkPlugin plugin) { @@ -68,11 +64,6 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { // ignore } - if (!supported) { - logUnsupported(); - return; - } - pipeline.addLast("codec-http", new HttpServerCodec()); pipeline.addLast("aggregator", new HttpObjectAggregator(65536)); pipeline.addLast("handler", new HttpHandler(this.plugin)); @@ -82,26 +73,8 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { in.release(); } - private void logUnsupported() { - this.plugin.getLogger().error("AzLink is not compatible with your server software, please use Paper instead!"); - this.plugin.getLogger().error("Paper offers significant performance improvements, bug fixes, security"); - this.plugin.getLogger().error("enhancements and optional features for server owners to enhance their server."); - this.plugin.getLogger().error(""); - this.plugin.getLogger().error("Download: https://papermc.io/downloads"); - } - private boolean isHttp(int magic1, int magic2, int magic3, int magic4) { return magic1 == 'G' && magic2 == 'E' && magic3 == 'T' && magic4 == ' ' || // GET magic1 == 'P' && magic2 == 'O' && magic3 == 'S' && magic4 == 'T'; // POST } - - private boolean isSupported() { - try { - Class.forName("io.netty.handler.codec.http.HttpServerCodec"); - - return true; - } catch (ClassNotFoundException e) { - return false; - } - } } diff --git a/bukkit/src/main/java/com/azuriom/azlink/bukkit/injector/NettyLibraryLoader.java b/bukkit/src/main/java/com/azuriom/azlink/bukkit/injector/NettyLibraryLoader.java new file mode 100644 index 0000000..6c9e542 --- /dev/null +++ b/bukkit/src/main/java/com/azuriom/azlink/bukkit/injector/NettyLibraryLoader.java @@ -0,0 +1,78 @@ +package com.azuriom.azlink.bukkit.injector; + +import com.azuriom.azlink.common.AzLinkPlugin; +import io.netty.util.Version; + +import java.io.InputStream; +import java.lang.reflect.Field; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * Since Spigot 1.19, netty-codec-http is no longer included, so we need to manually load it. + *

+ * We are not including it in the plugin.yml 'libraries' list, because it's not needed for all Minecraft versions, + * and the Netty version changes between Minecraft versions. + */ +public class NettyLibraryLoader { + + private static final String MAVEN_CENTRAL = "https://repo1.maven.org/maven2/%s/%s/%s/%s-%s.jar"; + + private final AzLinkPlugin plugin; + private final Path libsFolder; + + public NettyLibraryLoader(AzLinkPlugin plugin) { + this.plugin = plugin; + this.libsFolder = plugin.getPlatform().getDataDirectory().resolve("libs"); + } + + public void loadRequiredLibraries() throws Exception { + try { + Class.forName("io.netty.handler.codec.http.HttpServerCodec"); + // netty-codec-http is already loaded, all good + return; + } catch (ClassNotFoundException e) { + // manually load netty-codec-http below + } + + this.plugin.getLogger().info("Loading netty-codec-http..."); + + loadLibrary("io.netty", "netty-codec-http", identifyNettyVersion()); + + this.plugin.getLogger().info("Loaded netty-codec-http successfully."); + } + + private void loadLibrary(String groupId, String artifactId, String version) throws Exception { + Path jar = this.libsFolder.resolve(artifactId + "-" + version + ".jar"); + + if (!Files.exists(jar)) { + Files.createDirectory(jar.getParent()); + + this.plugin.getLogger().warn("Downloading " + artifactId + " v" + version + "..."); + + URL url = new URL(String.format(MAVEN_CENTRAL, groupId.replace('.', '/'), artifactId, version, artifactId, version)); + + try (InputStream in = url.openStream()) { + Files.copy(in, jar); + } + + this.plugin.getLogger().info("Successfully downloaded " + artifactId + "."); + } + + URL[] urls = {jar.toUri().toURL()}; + ClassLoader pluginClassLoader = plugin.getClass().getClassLoader(); + Field classLoaderField = pluginClassLoader.getClass().getDeclaredField("libraryLoader"); + ClassLoader libraryLoader = new URLClassLoader(urls, pluginClassLoader.getParent()); + + classLoaderField.setAccessible(true); + classLoaderField.set(pluginClassLoader, libraryLoader); + } + + private String identifyNettyVersion() { + Version version = Version.identify().get("netty-codec"); + + return version != null ? version.artifactVersion() : "4.1.97.Final"; + } +} diff --git a/common/src/main/java/com/azuriom/azlink/common/AzLinkPlugin.java b/common/src/main/java/com/azuriom/azlink/common/AzLinkPlugin.java index 533d74b..00e4463 100644 --- a/common/src/main/java/com/azuriom/azlink/common/AzLinkPlugin.java +++ b/common/src/main/java/com/azuriom/azlink/common/AzLinkPlugin.java @@ -88,7 +88,7 @@ public void init() { return; } - if (this.config.hasInstantCommands()) { + if (this.config.hasInstantCommands() && this.httpServer != null) { this.httpServer.start(); } @@ -186,10 +186,6 @@ public HttpClient getHttpClient() { return this.httpClient; } - public HttpServer getHttpServer() { - return this.httpServer; - } - public UserManager getUserManager() { return this.userManager; } diff --git a/universal-legacy/build.gradle b/universal-legacy/build.gradle index d7468a9..10edb6c 100644 --- a/universal-legacy/build.gradle +++ b/universal-legacy/build.gradle @@ -12,8 +12,8 @@ dependencies { implementation project(':azlink-nukkit') implementation 'com.google.code.gson:gson:2.8.9' - implementation 'io.netty:netty-handler:4.1.86.Final' - implementation 'io.netty:netty-codec-http:4.1.72.Final' + implementation 'io.netty:netty-handler:4.1.100.Final' + implementation 'io.netty:netty-codec-http:4.1.100.Final' } shadowJar {