From a7506bd3c6394d59239ebefff64bd32946b5d081 Mon Sep 17 00:00:00 2001 From: Minecon724 Date: Tue, 26 Nov 2024 20:25:13 +0100 Subject: [PATCH] commit --- .../java/eu/m724/tweaks/TweaksConfig.java | 7 +- .../java/eu/m724/tweaks/TweaksPlugin.java | 8 ++ .../eu/m724/tweaks/updater/PluginScanner.java | 58 ++++++++++ .../eu/m724/tweaks/updater/UpdateChecker.java | 59 ++++++++++ .../m724/tweaks/updater/UpdaterManager.java | 44 +++++++ .../tweaks/updater/VersionCheckCache.java | 75 ++++++++++++ .../eu/m724/tweaks/updater/VersionFinder.java | 109 ++++++++++++++++++ .../tweaks/updater/cache/ResourceVersion.java | 12 ++ .../tweaks/updater/cache/SpigotResource.java | 9 ++ .../updater/cache/VersionedResource.java | 11 ++ src/main/resources/installed_plugins.yml | 3 + src/main/resources/plugin.yml | 1 + 12 files changed, 394 insertions(+), 2 deletions(-) create mode 100644 src/main/java/eu/m724/tweaks/updater/PluginScanner.java create mode 100644 src/main/java/eu/m724/tweaks/updater/UpdateChecker.java create mode 100644 src/main/java/eu/m724/tweaks/updater/UpdaterManager.java create mode 100644 src/main/java/eu/m724/tweaks/updater/VersionCheckCache.java create mode 100644 src/main/java/eu/m724/tweaks/updater/VersionFinder.java create mode 100644 src/main/java/eu/m724/tweaks/updater/cache/ResourceVersion.java create mode 100644 src/main/java/eu/m724/tweaks/updater/cache/SpigotResource.java create mode 100644 src/main/java/eu/m724/tweaks/updater/cache/VersionedResource.java create mode 100644 src/main/resources/installed_plugins.yml diff --git a/src/main/java/eu/m724/tweaks/TweaksConfig.java b/src/main/java/eu/m724/tweaks/TweaksConfig.java index 1955390..f847a03 100644 --- a/src/main/java/eu/m724/tweaks/TweaksConfig.java +++ b/src/main/java/eu/m724/tweaks/TweaksConfig.java @@ -29,7 +29,9 @@ public record TweaksConfig( int compassPrecision, boolean pomodoroEnabled, - boolean pomodoroForce + boolean pomodoroForce, + + boolean updaterEnabled ) { public static final int CONFIG_VERSION = 1; private static TweaksConfig config; @@ -87,7 +89,8 @@ public record TweaksConfig( motdEnabled, motdSet, chatEnabled, chatLocalEvents, chatDefaultName, compassEnabled, compassWidth, compassPrecision, - pomodoroEnabled, pomodoroForce + pomodoroEnabled, pomodoroForce, + true // TODO ); return TweaksConfig.config; diff --git a/src/main/java/eu/m724/tweaks/TweaksPlugin.java b/src/main/java/eu/m724/tweaks/TweaksPlugin.java index e38ce93..df5c886 100644 --- a/src/main/java/eu/m724/tweaks/TweaksPlugin.java +++ b/src/main/java/eu/m724/tweaks/TweaksPlugin.java @@ -10,6 +10,7 @@ import eu.m724.tweaks.ping.PingChecker; import eu.m724.tweaks.ping.PingCommands; import eu.m724.tweaks.pomodoro.PomodoroCommands; import eu.m724.tweaks.pomodoro.PomodoroManager; +import eu.m724.tweaks.updater.UpdaterManager; import eu.m724.tweaks.worldborder.WorldBorderManager; import org.bukkit.plugin.java.JavaPlugin; @@ -66,5 +67,12 @@ public class TweaksPlugin extends JavaPlugin { getCommand("pomodoro").setExecutor(new PomodoroCommands()); } + if (config.updaterEnabled()) { + try { + new UpdaterManager(this).init(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } } } diff --git a/src/main/java/eu/m724/tweaks/updater/PluginScanner.java b/src/main/java/eu/m724/tweaks/updater/PluginScanner.java new file mode 100644 index 0000000..8b06749 --- /dev/null +++ b/src/main/java/eu/m724/tweaks/updater/PluginScanner.java @@ -0,0 +1,58 @@ +package eu.m724.tweaks.updater; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.annotations.JsonAdapter; +import eu.m724.tweaks.updater.cache.SpigotResource; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.Plugin; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +public class PluginScanner { + private final Plugin thisPlugin; + + PluginScanner(Plugin thisPlugin) { + this.thisPlugin = thisPlugin; + } + + public Set load() throws IOException { + File installedPluginsYml = new File(thisPlugin.getDataFolder(), "installed_plugins.yml"); + + if (!installedPluginsYml.exists()) { + thisPlugin.saveResource("installed_plugins.yml", false); + } + + YamlConfiguration configuration = YamlConfiguration.loadConfiguration(installedPluginsYml); + + + Plugin[] plugins = thisPlugin.getServer().getPluginManager().getPlugins(); + Set spigotResources = new HashSet<>(); + + for (Plugin plugin : plugins) { + System.out.println("Found " + plugin.getName()); + String pluginName = plugin.getName(); + + if (!configuration.isSet(pluginName)) { + configuration.set(pluginName, -1); + continue; + } + + int pluginId = configuration.getInt(pluginName); + if (pluginId != -1) { + spigotResources.add( + new SpigotResource(plugin, pluginId) + ); + } + } + + configuration.save(installedPluginsYml); + + return spigotResources; + + } +} diff --git a/src/main/java/eu/m724/tweaks/updater/UpdateChecker.java b/src/main/java/eu/m724/tweaks/updater/UpdateChecker.java new file mode 100644 index 0000000..8889fde --- /dev/null +++ b/src/main/java/eu/m724/tweaks/updater/UpdateChecker.java @@ -0,0 +1,59 @@ +package eu.m724.tweaks.updater; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import eu.m724.tweaks.updater.cache.ResourceVersion; +import eu.m724.tweaks.updater.cache.SpigotResource; +import eu.m724.tweaks.updater.cache.VersionedResource; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; +import org.eclipse.aether.impl.UpdateCheck; +import org.slf4j.LoggerFactory; + +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.logging.Logger; + +public class UpdateChecker extends BukkitRunnable { + private final Set resources; + private final Logger logger; + + UpdateChecker(Plugin plugin, Set resources) { + this.logger = Logger.getLogger(plugin.getLogger().getName() + "." + getClass().getSimpleName()); + this.resources = resources; // TODO make a copy? + } + + private void checkAll() { + logger.info("Now checking all plugins"); + for (VersionedResource versionedResource : Set.copyOf(resources)) { + logger.info(versionedResource.resource().resourceId() + " " + versionedResource.resource().plugin().getName()); + int page = versionedResource.running() != null ? versionedResource.running().page() : 1; + + try { + VersionedResource newResource = new VersionFinder(versionedResource.resource(), page).join(); // this runs async so it's ok + if (!versionedResource.equals(newResource)) { + resources.remove(versionedResource); + resources.add(newResource); + // TODO + } + } catch (CompletionException e) { + logger.severe("Unable to refresh %s: %s".formatted(versionedResource.resource().plugin().getName(), e.getMessage())); + } + } + } + + @Override + public void run() { + checkAll(); + } +} diff --git a/src/main/java/eu/m724/tweaks/updater/UpdaterManager.java b/src/main/java/eu/m724/tweaks/updater/UpdaterManager.java new file mode 100644 index 0000000..a827dc1 --- /dev/null +++ b/src/main/java/eu/m724/tweaks/updater/UpdaterManager.java @@ -0,0 +1,44 @@ +package eu.m724.tweaks.updater; + +import eu.m724.tweaks.updater.cache.ResourceVersion; +import eu.m724.tweaks.updater.cache.SpigotResource; +import eu.m724.tweaks.updater.cache.VersionedResource; +import org.bukkit.plugin.Plugin; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Set; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +public class UpdaterManager { + private final Plugin plugin; + + public UpdaterManager(Plugin plugin) { + this.plugin = plugin; + } + + public void init() throws IOException { + // scan installed plugins + Set resources = new PluginScanner(plugin).load(); + + // load installed versions from cache + File cacheFile = new File(plugin.getDataFolder(), "version cache"); + Set installedVersions; + try (FileInputStream inputStream = new FileInputStream(cacheFile)) { + installedVersions = VersionCheckCache.loadAll(inputStream); + } + + Set versionedResources = installedVersions.stream() + .map(rv -> new VersionedResource( + resources.stream().filter(r -> r.resourceId() == rv.resourceId()).findFirst().get(), + rv, + null + )).collect(Collectors.toSet()); + + + new UpdateChecker(plugin, versionedResources) + .runTaskTimerAsynchronously(plugin, 600, 12 * 3600 * 20); + } +} diff --git a/src/main/java/eu/m724/tweaks/updater/VersionCheckCache.java b/src/main/java/eu/m724/tweaks/updater/VersionCheckCache.java new file mode 100644 index 0000000..cfc61d8 --- /dev/null +++ b/src/main/java/eu/m724/tweaks/updater/VersionCheckCache.java @@ -0,0 +1,75 @@ +package eu.m724.tweaks.updater; + +import eu.m724.tweaks.updater.cache.ResourceVersion; +import eu.m724.tweaks.updater.cache.VersionedResource; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashSet; +import java.util.Set; + +public class VersionCheckCache { + private static final byte FILE_VERSION = 1; + + public static Set loadAll(FileInputStream inputStream) throws IOException { + byte fileVersion = (byte) inputStream.read(); + if (fileVersion != FILE_VERSION) throw new FileVersionMismatchException(fileVersion, FILE_VERSION); + + Set versions = new HashSet<>(); + int i = 0; + + byte[] buffer = new byte[8]; // 3 + 2 + 3 + while (inputStream.available() > 0) { + int read = inputStream.read(buffer); + if (read < 8) break; // end of file + if (++i > 10000) throw new RuntimeException("File is too large"); + + ResourceVersion resourceVersion = getResourceVersion(buffer); + versions.add(resourceVersion); + } + + return versions; + } + + private static ResourceVersion getResourceVersion(byte[] bytes) { + int resourceId = ((bytes[0] & 0xFF) << 16) | ((bytes[1] & 0xFF) << 8) | (bytes[2] & 0xFF); + int page = ((bytes[3] & 0xFF) << 8) | (bytes[4] & 0xFF); + int versionId = ((bytes[5] & 0xFF) << 16) | ((bytes[6] & 0xFF) << 8) | (bytes[7] & 0xFF); + + return new ResourceVersion(resourceId, page, versionId); + } + + private static void writeResourceVersion(ResourceVersion resourceVersion, OutputStream outputStream) throws IOException { + int resourceId = resourceVersion.resourceId(); + outputStream.write((resourceId >> 16) & 0xFF); + outputStream.write((resourceId >> 8) & 0xFF); + outputStream.write(resourceId & 0xFF); + + int page = resourceVersion.page(); + outputStream.write((page >> 8) & 0xFF); + outputStream.write(page & 0xFF); + + int versionId = resourceVersion.updateId(); + outputStream.write((versionId >> 16) & 0xFF); + outputStream.write((versionId >> 8) & 0xFF); + outputStream.write(versionId & 0xFF); + } + + public static void writeAll(FileOutputStream outputStream, Set versions) throws IOException { + outputStream.write(FILE_VERSION); + for (ResourceVersion version : versions) { + writeResourceVersion(version, outputStream); + } + } + + public static class FileVersionMismatchException extends RuntimeException { + public final byte fileVersion, expectedVersion; + + public FileVersionMismatchException(byte fileVersion, byte expectedVersion) { + this.fileVersion = fileVersion; + this.expectedVersion = expectedVersion; + } + } +} diff --git a/src/main/java/eu/m724/tweaks/updater/VersionFinder.java b/src/main/java/eu/m724/tweaks/updater/VersionFinder.java new file mode 100644 index 0000000..92892be --- /dev/null +++ b/src/main/java/eu/m724/tweaks/updater/VersionFinder.java @@ -0,0 +1,109 @@ +package eu.m724.tweaks.updater; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import eu.m724.tweaks.updater.cache.SpigotResource; +import eu.m724.tweaks.updater.cache.ResourceVersion; +import eu.m724.tweaks.updater.cache.VersionedResource; + +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +// TODO optimize + +public class VersionFinder extends CompletableFuture { + private final SpigotResource resource; + private final int fromPage; + + VersionFinder(SpigotResource resource, int fromPage) { + this.resource = resource; + this.fromPage = fromPage; + + start(); + } + + VersionFinder(SpigotResource resource) { + this(resource, 1); + } + + private void start() { + System.out.printf("STarting for %d %s\n", resource.resourceId(), resource.plugin().getName()); + try (ExecutorService executor = Executors.newSingleThreadExecutor()) { + executor.execute(() -> { + try { + ResourceVersion runningVersion = null; + ResourceVersion latestVersion = null; + + int page; + for (page = fromPage; page < 1000; page++) { + System.out.println("Page " + page); + String url = "https://api.spigotmc.org/simple/0.2/index.php?action=getResourceUpdates&page=%d&id=%d".formatted(page, resource.resourceId()); + + HttpRequest request; + try { + request = HttpRequest.newBuilder(new URI(url)) + .header("User-Agent", "twu/1") + .build(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } // this will never happen + + try (HttpClient client = HttpClient.newHttpClient()) { + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + String body = response.body(); + if (body.isBlank()) { + page--; + break; + } + + JsonArray jsonArray = JsonParser.parseString(body).getAsJsonArray(); + for (JsonElement ele : jsonArray) { + JsonObject versionJson = ele.getAsJsonObject(); + if (isRunningVersion(versionJson)) { + runningVersion = new ResourceVersion( + resource.resourceId(), + page, + versionJson.get("id").getAsInt() + ); + } + latestVersion = new ResourceVersion( + resource.resourceId(), + page, + versionJson.get("id").getAsInt() + ); + System.out.printf("%d %d %s\n", page, versionJson.get("id").getAsInt(), versionJson.get("resource_version").getAsString()); + } + + if (jsonArray.size() < 10) break; + } + + } + System.out.println("Done"); + + if (page > 999) { + throw new Exception("Too many pages"); + } else { + this.complete(new VersionedResource(resource, runningVersion, latestVersion)); + } + } catch (Exception e) { + this.completeExceptionally(e); + } + + }); + } + } + + private boolean isRunningVersion(JsonObject versionJson) { + // TODO + return versionJson.get("resource_version").getAsString().equals(resource.plugin().getDescription().getVersion()); + } +} diff --git a/src/main/java/eu/m724/tweaks/updater/cache/ResourceVersion.java b/src/main/java/eu/m724/tweaks/updater/cache/ResourceVersion.java new file mode 100644 index 0000000..b88f2e1 --- /dev/null +++ b/src/main/java/eu/m724/tweaks/updater/cache/ResourceVersion.java @@ -0,0 +1,12 @@ +package eu.m724.tweaks.updater.cache; + +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.List; + +public record ResourceVersion( + int resourceId, + int page, + int updateId +) { } diff --git a/src/main/java/eu/m724/tweaks/updater/cache/SpigotResource.java b/src/main/java/eu/m724/tweaks/updater/cache/SpigotResource.java new file mode 100644 index 0000000..5142a48 --- /dev/null +++ b/src/main/java/eu/m724/tweaks/updater/cache/SpigotResource.java @@ -0,0 +1,9 @@ +package eu.m724.tweaks.updater.cache; + +import org.bukkit.plugin.Plugin; + +public record SpigotResource( + Plugin plugin, + int resourceId +) { +} diff --git a/src/main/java/eu/m724/tweaks/updater/cache/VersionedResource.java b/src/main/java/eu/m724/tweaks/updater/cache/VersionedResource.java new file mode 100644 index 0000000..0474376 --- /dev/null +++ b/src/main/java/eu/m724/tweaks/updater/cache/VersionedResource.java @@ -0,0 +1,11 @@ +package eu.m724.tweaks.updater.cache; + +public record VersionedResource( + SpigotResource resource, + ResourceVersion running, + ResourceVersion latest +) { + public VersionedResource update(ResourceVersion latest) { + return new VersionedResource(resource, running, latest); + } +} diff --git a/src/main/resources/installed_plugins.yml b/src/main/resources/installed_plugins.yml new file mode 100644 index 0000000..d354b32 --- /dev/null +++ b/src/main/resources/installed_plugins.yml @@ -0,0 +1,3 @@ +# Here go installed plugins and their SpigotMC resource IDs +# To ignore a plugin, set it to -1 + diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 070b6ad..ef2e442 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,4 +1,5 @@ name: Tweaks724 +author: Minecon724 version: ${project.version} main: eu.m724.tweaks.TweaksPlugin