From 950cf97ff65a6899170fd0195f9a70c614ec98e1 Mon Sep 17 00:00:00 2001 From: Allink Date: Sat, 14 May 2022 11:19:13 +0100 Subject: [PATCH] Add BukkitTelnet compiler --- .../java/dev/plex/BukkitTelnetModule.java | 57 +++--- .../automation/PatchedTelnetCompiler.java | 168 ++++++++++++++++++ 2 files changed, 200 insertions(+), 25 deletions(-) create mode 100644 src/main/java/dev/plex/automation/PatchedTelnetCompiler.java diff --git a/src/main/java/dev/plex/BukkitTelnetModule.java b/src/main/java/dev/plex/BukkitTelnetModule.java index 2a5dcb0..e9ce4d8 100644 --- a/src/main/java/dev/plex/BukkitTelnetModule.java +++ b/src/main/java/dev/plex/BukkitTelnetModule.java @@ -1,56 +1,65 @@ package dev.plex; +import dev.plex.automation.PatchedTelnetCompiler; import dev.plex.listener.BukkitTelnetListener; import dev.plex.module.PlexModule; import dev.plex.util.PlexLog; -import java.lang.reflect.Method; import lombok.Getter; import me.totalfreedom.bukkittelnet.BukkitTelnet; import org.bukkit.Bukkit; -public class BukkitTelnetModule extends PlexModule -{ +import java.lang.reflect.Method; + +public class BukkitTelnetModule extends PlexModule { @Getter private static BukkitTelnetModule module; - + boolean failed = false; private BukkitTelnet bukkitTelnet; - boolean failed = false; - @Override - public void load() - { + public void load() { module = this; } @Override - public void enable() - { - if (getPlex().getSystem().equalsIgnoreCase("permissions") && !Bukkit.getPluginManager().isPluginEnabled("Vault")) - { + public void enable() { + if (getPlex().getSystem().equalsIgnoreCase("permissions") && !Bukkit.getPluginManager().isPluginEnabled("Vault")) { failed = true; PlexLog.error("Plex-BukkitTelnet requires the 'Vault' plugin as well as a Permissions plugin that hooks into 'Vault.' We recommend LuckPerms!"); module.disable(); return; } - if (!Bukkit.getPluginManager().isPluginEnabled("BukkitTelnet")) - { + if (!Bukkit.getPluginManager().isPluginEnabled("BukkitTelnet")) { failed = true; - PlexLog.error("The Plex-BukkitTelnet module requires the BukkitTelnet plugin to work. Please download it from: https://github.com/plexusorg/BukkitTelnet/releases"); + PlexLog.warn("The Plex-BukkitTelnet module requires the BukkitTelnet plugin to work. I am automatically compiling BukkitTelnet plugin for you, however if something fails, please download it from: https://github.com/plexusorg/BukkitTelnet/releases"); + try { + PatchedTelnetCompiler.execute(); + return; + } catch (Exception e) { + PlexLog.error("Failed to compile patched telnet."); + e.printStackTrace(); + } + module.disable(); return; } - try - { + try { failed = true; Class clazz = Class.forName("me.totalfreedom.bukkittelnet.BukkitTelnet"); Method method = clazz.getDeclaredMethod("getPlugin"); - } - catch (ClassNotFoundException | NoSuchMethodException ignored) - { - PlexLog.error("You are using an older version of BukkitTelnet that does not support Plex. Please download a version that does from: https://ci.plex.us.org/job/Plex-BukkitTelnet"); + } catch (ClassNotFoundException | NoSuchMethodException ignored) { + PlexLog.warn("You are using an older version of BukkitTelnet that does not support Plex. I am automatically compiling a build that does work for you, however if something fails, please download a version that does from: https://ci.plex.us.org/job/Plex-BukkitTelnet"); + + try { + PatchedTelnetCompiler.execute(); + return; + } catch (Exception e) { + PlexLog.error("Failed to compile patched telnet."); + e.printStackTrace(); + } + module.disable(); return; } @@ -60,10 +69,8 @@ public class BukkitTelnetModule extends PlexModule } @Override - public void disable() - { - if (failed) - { + public void disable() { + if (failed) { PlexLog.error("Disabling Module-BukkitTelnet. Please resolve the above error."); } } diff --git a/src/main/java/dev/plex/automation/PatchedTelnetCompiler.java b/src/main/java/dev/plex/automation/PatchedTelnetCompiler.java new file mode 100644 index 0000000..349f119 --- /dev/null +++ b/src/main/java/dev/plex/automation/PatchedTelnetCompiler.java @@ -0,0 +1,168 @@ +package dev.plex.automation; + +import com.google.common.collect.ImmutableList; +import dev.plex.BukkitTelnetModule; +import dev.plex.util.PlexLog; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.util.Comparator; +import java.util.List; +import java.util.Timer; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +public class PatchedTelnetCompiler { + private static final PluginManager PLUGIN_MANAGER = Bukkit.getPluginManager(); + private static final URI CODE_ARCHIVE = URI.create("https://github.com/plexusorg/BukkitTelnet/archive/refs/heads/master.zip"); + private static final Path PLUGIN_DIRECTORY = Bukkit.getServer().getPluginsFolder().toPath(); + private static final File TARGET_PLUGIN = PLUGIN_DIRECTORY.resolve("BukkitTelnet.jar").toFile(); + private static final Path WORKING_DIRECTORY = Path.of(System.getProperty("user.dir")); + private static final Path ROOT_PATH = WORKING_DIRECTORY.resolve("build"); + private static final Path EXTRACT_TARGET = ROOT_PATH.resolve("extract"); + private static final Path EXTRACT_SUBDIR = EXTRACT_TARGET.resolve("BukkitTelnet-master"); + private static final Path BINARIES_PATH = EXTRACT_SUBDIR.resolve("build").resolve("libs"); + private static final String DOWNLOADED_ARCHIVE_PATH = String.valueOf(ROOT_PATH.resolve("BukkitTelnet-master.zip")); + private static final HttpClient HTTP_CLIENT = HttpClient.newHttpClient(); + + public static void execute() throws Exception { + // Create directories + final List directories = ImmutableList.of(ROOT_PATH, EXTRACT_TARGET); + + for (Path directory : directories) { + PlexLog.debug("Checking if {0} exists...", String.valueOf(directory)); + if (Files.notExists(directory)) { + PlexLog.debug("It doesn't! Creating directory..."); + Files.createDirectory(directory); + } + } + + downloadArchive(); + } + + private static void downloadArchive() throws Exception { + PlexLog.log("Downloading archive..."); + // Create the request + final HttpRequest request = HttpRequest.newBuilder() + .GET() + // GitHub may block blank/generic user agents in the future, so we're spoofing one + .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Safari/537.36") + .uri(CODE_ARCHIVE) + .build(); + + // Send the request + final HttpResponse response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString()); + + // Get the redirect + final URI redirect = URI.create(response.headers().firstValue("location").orElseThrow()); + + // Download the file from the redirect + final HttpRequest downloadRequest = HttpRequest.newBuilder() + .GET() + .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Safari/537.36") + .uri(redirect) + .build(); + + HTTP_CLIENT.send(downloadRequest, HttpResponse.BodyHandlers.ofFileDownload(ROOT_PATH, StandardOpenOption.CREATE, StandardOpenOption.WRITE)); + + extractArchive(); + } + + private static void extractArchive() throws Exception { + PlexLog.log("Extracting archive..."); + final ZipInputStream inputStream = new ZipInputStream(new FileInputStream(DOWNLOADED_ARCHIVE_PATH)); + ZipEntry entry = inputStream.getNextEntry(); + + while (entry != null) { + final Path outputDestination = EXTRACT_TARGET.resolve(entry.getName()); + + if (entry.isDirectory()) { + if (Files.notExists(outputDestination)) { + PlexLog.debug("{0} doesn't exist, creating it!", String.valueOf(outputDestination)); + Files.createDirectory(outputDestination); + } + } else { + final FileOutputStream outputStream = new FileOutputStream(String.valueOf(outputDestination)); + int read = 0; + while ((read = inputStream.read()) != -1) { + outputStream.write(read); + } + + outputStream.close(); + } + + inputStream.closeEntry(); + entry = inputStream.getNextEntry(); + } + + executeGradleTarget(); + } + + private static void executeGradleTarget() throws Exception { + PlexLog.log("Executing gradle target..."); + boolean nix = !System.getProperty("os.name").toLowerCase().contains("win"); // Assume Windows if name contains win + + String gradlew = String.valueOf(nix ? EXTRACT_SUBDIR.resolve("gradlew") : EXTRACT_SUBDIR.resolve("gradlew.bat")); + if (nix) { + final ProcessBuilder chmodBuilder = new ProcessBuilder("chmod", "+x", gradlew); + Process chmodProcess = chmodBuilder.start(); + chmodProcess.waitFor(); + } + + final ProcessBuilder builder = new ProcessBuilder(gradlew, "--no-daemon", "clean", "build"); + builder.directory(new File(String.valueOf(EXTRACT_SUBDIR))); + PlexLog.debug("Executing compile command: {0}", builder.command()); + + //builder.redirectErrorStream(true); + //builder.redirectOutput(ProcessBuilder.Redirect.PIPE); + builder.inheritIO(); + + final Process process = builder.start(); + process.waitFor(); + PlexLog.debug("Compilation command ended with status code {0}", process.exitValue()); + copyBinary(); + } + + private static void copyBinary() throws Exception { + PlexLog.log("Copying binaries..."); + final File binaryDirectory = new File(String.valueOf(BINARIES_PATH)); + final File[] files = binaryDirectory.listFiles(); + + if (files == null) { + throw new IllegalStateException("Didn't manage to compile jars!"); + } else if (files.length == 0) { + throw new IllegalStateException("Didn't manage to compile jars!"); + } + + Files.copy(BINARIES_PATH.resolve("BukkitTelnet.jar"), TARGET_PLUGIN.toPath(), StandardCopyOption.REPLACE_EXISTING); + Files.walk(ROOT_PATH) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + + Timer timer = new Timer(); + + final Plugin plugin = PLUGIN_MANAGER.loadPlugin(TARGET_PLUGIN); + if (plugin == null) throw new IllegalStateException("BukkitTelnet cannot be null after successful compile!"); + + plugin.onLoad(); + PLUGIN_MANAGER.enablePlugin(plugin); + done(); + } + + private static void done() { + BukkitTelnetModule.getModule().enable(); + } +}