Refactor updater
Some checks failed
/ build (push) Has been cancelled

This commit is contained in:
Minecon724 2025-01-20 08:10:38 +01:00
parent 2430416915
commit e5d2938845
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
10 changed files with 137 additions and 142 deletions

View file

@ -1,59 +0,0 @@
/*
* Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.updater;
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.HashSet;
import java.util.Set;
public class PluginScanner {
private final Plugin thisPlugin;
PluginScanner(Plugin thisPlugin) {
this.thisPlugin = thisPlugin;
}
public Set<SpigotResource> 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<SpigotResource> spigotResources = new HashSet<>();
for (Plugin plugin : plugins) {
String pluginName = plugin.getName();
if (!configuration.isSet(pluginName)) {
configuration.set(pluginName, -1);
continue;
}
int pluginId = configuration.getInt(pluginName);
if (pluginId > 0) {
spigotResources.add(
new SpigotResource(plugin, pluginId, pluginName)
);
}
}
configuration.save(installedPluginsYml);
return spigotResources;
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2024 Minecon724
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
@ -7,7 +7,8 @@
package eu.m724.tweaks.updater;
import eu.m724.tweaks.Language;
import eu.m724.tweaks.updater.cache.VersionedResource;
import eu.m724.tweaks.updater.backend.UpdateChecker;
import eu.m724.tweaks.updater.object.VersionedResource;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
@ -20,18 +21,25 @@ import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
public class UpdaterCommands implements CommandExecutor {
private final UpdateChecker updateChecker;
public UpdaterCommands(UpdateChecker updateChecker) {
this.updateChecker = updateChecker;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (UpdateChecker.lastChecked == null) {
var lastChecked = updateChecker.getLastChecked();
if (updateChecker.getLastChecked() == -1) {
sender.sendMessage(Language.getString("updatesNotChecked"));
return true;
}
String lastChecked = UpdateChecker.lastChecked.format(DateTimeFormatter.ofPattern("HH:mm"));
int n = UpdateChecker.availableUpdates.size();
int n = updateChecker.getAvailableUpdates().size();
if (n > 0) {
sender.spigot().sendMessage(
@ -39,13 +47,14 @@ public class UpdaterCommands implements CommandExecutor {
);
int i = 0;
for (VersionedResource v : UpdateChecker.availableUpdates) {
for (VersionedResource v : updateChecker.getAvailableUpdates()) {
sender.spigot().sendMessage(
new ComponentBuilder(++i + ". ").color(ChatColor.GRAY).build(), resourceToBaseComponent(v)
);
}
} else {
sender.spigot().sendMessage(Language.getComponent("updatesNoUpdates", ChatColor.GREEN, lastChecked));
var lastCheckedFormat = DateTimeFormatter.ofPattern("HH:mm").format(Instant.ofEpochMilli(lastChecked));
sender.spigot().sendMessage(Language.getComponent("updatesNoUpdates", ChatColor.GREEN, lastCheckedFormat));
}
return true;

View file

@ -8,9 +8,13 @@ package eu.m724.tweaks.updater;
import eu.m724.tweaks.DebugLogger;
import eu.m724.tweaks.TweaksModule;
import eu.m724.tweaks.updater.cache.ResourceVersion;
import eu.m724.tweaks.updater.cache.SpigotResource;
import eu.m724.tweaks.updater.cache.VersionedResource;
import eu.m724.tweaks.updater.backend.UpdateChecker;
import eu.m724.tweaks.updater.backend.VersionCache;
import eu.m724.tweaks.updater.object.ResourceVersion;
import eu.m724.tweaks.updater.object.SpigotResource;
import eu.m724.tweaks.updater.object.VersionedResource;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
import java.io.File;
import java.io.FileInputStream;
@ -23,38 +27,72 @@ import java.util.stream.Collectors;
public class UpdaterModule extends TweaksModule {
@Override
protected void onInit() {
// scan installed plugins
Set<SpigotResource> resources;
try {
resources = new PluginScanner(getPlugin()).load();
} catch (IOException e) {
throw new RuntimeException("Loading plugins", e);
}
var resources = loadInstalledPlugins();
// load installed versions from cache
var cacheFile = new File(getPlugin().getDataFolder(), "storage/cache/updater");
Set<ResourceVersion> installedVersions;
final var installedVersions = loadInstalledVersionCache(cacheFile);
var versionedResources = resources.stream()
.map(res -> new VersionedResource(
res, installedVersions.stream().filter(iv -> iv.resourceId() == res.resourceId()).findFirst().orElse(null), null
))
.collect(Collectors.toSet());
var updateChecker = new UpdateChecker(getPlugin().getLogger(), cacheFile, versionedResources);
updateChecker.runTaskTimerAsynchronously(getPlugin(), 600, 12 * 3600 * 20); // 12 hours
registerCommand("updates", new UpdaterCommands(updateChecker));
}
private Set<ResourceVersion> loadInstalledVersionCache(File cacheFile) {
try (FileInputStream inputStream = new FileInputStream(cacheFile)) {
installedVersions = VersionCheckCache.loadAll(inputStream);
} catch (FileNotFoundException e) {
installedVersions = new HashSet<>();
return VersionCache.loadAll(inputStream);
} catch (FileNotFoundException ignored) {
} catch (IOException e) {
DebugLogger.warning("Error loading installed version cache, starting fresh. " + e.getMessage());
installedVersions = new HashSet<>();
}
final Set<ResourceVersion> ivf = installedVersions;
Set<VersionedResource> versionedResources = resources.stream()
.map(res -> new VersionedResource(
res, ivf.stream().filter(iv -> iv.resourceId() == res.resourceId()).findFirst().orElse(null), null
)).collect(Collectors.toSet());
return new HashSet<>();
}
private Set<SpigotResource> loadInstalledPlugins() {
File installedPluginsYml = new File(getPlugin().getDataFolder(), "installed_plugins.yml");
new UpdateChecker(getPlugin().getLogger(), cacheFile, versionedResources)
.runTaskTimerAsynchronously(getPlugin(), 600, 12 * 3600 * 20); // 12 hours
if (!installedPluginsYml.exists()) {
getPlugin().saveResource("installed_plugins.yml", false);
}
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(installedPluginsYml);
Plugin[] plugins = getPlugin().getServer().getPluginManager().getPlugins();
Set<SpigotResource> spigotResources = new HashSet<>();
for (Plugin plugin : plugins) {
String pluginName = plugin.getName();
if (!configuration.isSet(pluginName)) {
configuration.set(pluginName, -1);
continue;
}
int pluginId = configuration.getInt(pluginName);
if (pluginId > 0) {
spigotResources.add(
new SpigotResource(plugin, pluginId, pluginName)
);
}
}
try {
configuration.save(installedPluginsYml);
} catch (IOException e) {
throw new RuntimeException("Failed to update installed_plugins.yml", e);
}
return spigotResources;
registerCommand("updates", new UpdaterCommands());
}
}

View file

@ -4,18 +4,16 @@
* in the project root for the full license text.
*/
package eu.m724.tweaks.updater;
package eu.m724.tweaks.updater.backend;
import eu.m724.tweaks.DebugLogger;
import eu.m724.tweaks.Language;
import eu.m724.tweaks.updater.cache.VersionedResource;
import eu.m724.tweaks.updater.object.VersionedResource;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@ -28,10 +26,10 @@ public class UpdateChecker extends BukkitRunnable {
private final File cacheFile;
private final Set<VersionedResource> resources;
static final Set<VersionedResource> availableUpdates = new HashSet<>();
static LocalTime lastChecked = null;
private final Set<VersionedResource> availableUpdates = new HashSet<>();
private long lastChecked = -1;
UpdateChecker(Logger logger, File cacheFile, Set<VersionedResource> resources) {
public UpdateChecker(Logger logger, File cacheFile, Set<VersionedResource> resources) {
this.logger = logger;
this.cacheFile = cacheFile;
this.resources = resources; // TODO make a copy?
@ -39,7 +37,7 @@ public class UpdateChecker extends BukkitRunnable {
private void checkAll() {
DebugLogger.fine("Checking for updates");
lastChecked = LocalTime.now(ZoneOffset.UTC);
lastChecked = System.currentTimeMillis();
availableUpdates.clear();
for (VersionedResource versionedResource : Set.copyOf(resources)) {
@ -47,19 +45,21 @@ public class UpdateChecker extends BukkitRunnable {
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
VersionedResource newResource = new VersionScanner(versionedResource.resource(), page).join(); // this runs async so it's ok
if (!versionedResource.equals(newResource)) {
resources.remove(versionedResource);
if (newResource.running() == null) {
logger.warning("Unable to find installed version of %s".formatted(pluginName));
DebugLogger.warning("Unable to find installed version of %s", pluginName);
if (versionedResource.running() != null) {
logger.warning("Did you downgrade %s? If so, clear cache".formatted(pluginName));
DebugLogger.warning("Did you downgrade %s? If so, clear cache", pluginName);
}
} else {
if (!newResource.running().equals(newResource.latest())) {
availableUpdates.add(newResource);
}
}
resources.add(newResource);
}
} catch (CompletionException e) {
@ -85,11 +85,19 @@ public class UpdateChecker extends BukkitRunnable {
DebugLogger.finer("Done checking, now saving");
cacheFile.getParentFile().mkdirs();
try (FileOutputStream outputStream = new FileOutputStream(cacheFile)) {
VersionCheckCache.writeAll(outputStream, resources.stream().map(VersionedResource::running).filter(Objects::nonNull).collect(Collectors.toSet()));
VersionCache.writeAll(outputStream, resources.stream().map(VersionedResource::running).filter(Objects::nonNull).collect(Collectors.toSet()));
} catch (IOException e) {
throw new RuntimeException(e);
}
alert();
}
public long getLastChecked() {
return lastChecked;
}
public Set<VersionedResource> getAvailableUpdates() {
return availableUpdates;
}
}

View file

@ -1,12 +1,12 @@
/*
* Copyright (C) 2024 Minecon724
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.updater;
package eu.m724.tweaks.updater.backend;
import eu.m724.tweaks.updater.cache.ResourceVersion;
import eu.m724.tweaks.updater.object.ResourceVersion;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@ -15,7 +15,7 @@ import java.io.OutputStream;
import java.util.HashSet;
import java.util.Set;
public class VersionCheckCache {
public class VersionCache {
private static final byte FILE_VERSION = 1;
public static Set<ResourceVersion> loadAll(FileInputStream inputStream) throws IOException {

View file

@ -1,19 +1,20 @@
/*
* Copyright (C) 2024 Minecon724
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.updater;
package eu.m724.tweaks.updater.backend;
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.UpdateDescription;
import eu.m724.tweaks.updater.cache.VersionedResource;
import eu.m724.tweaks.DebugLogger;
import eu.m724.tweaks.updater.object.SpigotResource;
import eu.m724.tweaks.updater.object.ResourceVersion;
import eu.m724.tweaks.updater.object.UpdateDescription;
import eu.m724.tweaks.updater.object.VersionedResource;
import java.net.URI;
import java.net.URISyntaxException;
@ -26,23 +27,25 @@ import java.util.concurrent.Executors;
// TODO optimize
public class VersionFinder extends CompletableFuture<VersionedResource> {
public class VersionScanner extends CompletableFuture<VersionedResource> {
private final SpigotResource resource;
private final int fromPage;
VersionFinder(SpigotResource resource, int fromPage) {
VersionScanner(SpigotResource resource, int fromPage) {
this.resource = resource;
this.fromPage = fromPage;
start();
}
VersionFinder(SpigotResource resource) {
VersionScanner(SpigotResource resource) {
this(resource, 1);
}
private void start() {
//System.out.printf("STarting for %d %s\n", resource.resourceId(), resource.plugin().getName());
DebugLogger.finer("Scanning %s (#%d) from page %d", resource.name(), resource.resourceId(), fromPage);
try (ExecutorService executor = Executors.newSingleThreadExecutor()) {
executor.execute(() -> {
try {
@ -51,13 +54,14 @@ public class VersionFinder extends CompletableFuture<VersionedResource> {
int page;
for (page = fromPage; page < 1000; page++) {
//System.out.println("Page " + page);
DebugLogger.finer("Scan %s now at page %d", resource.name(), fromPage);
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")
.header("User-Agent", "twu/1") // tweaks updater v1
.build();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
@ -68,6 +72,7 @@ public class VersionFinder extends CompletableFuture<VersionedResource> {
String body = response.body();
if (body.isBlank()) {
DebugLogger.finer("Body is blank, stopping");
page--;
break;
}
@ -75,15 +80,7 @@ public class VersionFinder extends CompletableFuture<VersionedResource> {
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(),
versionJson.get("resource_version").getAsString(),
null // no need for changelog of running version
);
}
latestVersion = new ResourceVersion(
resource.resourceId(),
page,
@ -94,14 +91,17 @@ public class VersionFinder extends CompletableFuture<VersionedResource> {
versionJson.get("message").getAsString()
)
);
//System.out.printf("%d %d %s\n", page, versionJson.get("id").getAsInt(), versionJson.get("resource_version").getAsString());
if (isRunningVersion(versionJson))
runningVersion = latestVersion;
DebugLogger.finer("%s - %s #%d", resource.name(), latestVersion.updateId(), latestVersion.updateId());
}
if (jsonArray.size() < 10) break;
}
}
//System.out.println("Done");
if (page > 999) {
throw new Exception("Too many pages");
@ -117,7 +117,8 @@ public class VersionFinder extends CompletableFuture<VersionedResource> {
}
private boolean isRunningVersion(JsonObject versionJson) {
// TODO
return versionJson.get("resource_version").getAsString().equals(resource.plugin().getDescription().getVersion());
// TODO make it work with more advanced strings
return versionJson.get("resource_version").getAsString()
.equals(resource.plugin().getDescription().getVersion());
}
}

View file

@ -1,10 +1,10 @@
/*
* Copyright (C) 2024 Minecon724
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.updater.cache;
package eu.m724.tweaks.updater.object;
import java.util.Objects;

View file

@ -1,10 +1,10 @@
/*
* Copyright (C) 2024 Minecon724
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.updater.cache;
package eu.m724.tweaks.updater.object;
import org.bukkit.plugin.Plugin;
@ -12,5 +12,4 @@ public record SpigotResource(
Plugin plugin,
int resourceId,
String name
) {
}
) { }

View file

@ -1,13 +1,12 @@
/*
* Copyright (C) 2024 Minecon724
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.updater.cache;
package eu.m724.tweaks.updater.object;
public record UpdateDescription(
String title,
String description
) {
}
) { }

View file

@ -1,10 +1,10 @@
/*
* Copyright (C) 2024 Minecon724
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.updater.cache;
package eu.m724.tweaks.updater.object;
public record VersionedResource(
SpigotResource resource,