StatusAPI/src/main/java/net/viper/status/UpdateChecker.java aktualisiert

This commit is contained in:
2026-01-03 11:30:53 +00:00
parent 3a7534c4eb
commit 0ec7710840

View File

@@ -1,211 +1,151 @@
package net.viper.status; package net.viper.status;
import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.ProxiedPlayer; import java.io.BufferedReader;
import net.md_5.bungee.api.chat.TextComponent; import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.io.BufferedReader; import java.net.URL;
import java.io.InputStreamReader; import java.util.logging.Level;
import java.net.HttpURLConnection; import java.util.regex.Matcher;
import java.net.URL; import java.util.regex.Pattern;
import java.util.logging.Level;
import java.util.regex.Matcher; public class UpdateChecker {
import java.util.regex.Pattern;
private final Plugin plugin;
/** private final String currentVersion;
* UpdateChecker: private final int intervalHours;
* - Fragt die Gitea Releases API ab
* - Sucht in den Releases nach dem Asset "StatusAPI.jar" private final String apiUrl = "https://git.viper.ipv64.net/api/v1/repos/M_Viper/Minecraft-BungeeCord-Status/releases";
* - Extrahiert die Version (tag_name bevorzugt, name als Fallback)
* - Cache die gefundene Version + URL (volatile fields) private volatile String latestVersion = "";
*/ private volatile String latestUrl = "";
public class UpdateChecker implements Runnable {
// Pattern für Dateinamen im Assets-Array
private final Plugin plugin; private static final Pattern ASSET_NAME_PATTERN = Pattern.compile("\"name\"\\s*:\\s*\"([^\"]+)\"", Pattern.CASE_INSENSITIVE);
private final String currentVersion; // Pattern für Download-URL
private final int intervalHours; private static final Pattern DOWNLOAD_PATTERN = Pattern.compile("\"browser_download_url\"\\s*:\\s*\"([^\"]+)\"", Pattern.CASE_INSENSITIVE);
// Pattern für Tag Version (WICHTIG: Wir suchen global, um das Haupt-Release zu finden)
// Gitea Releases API für dein Repo private static final Pattern TAG_NAME_PATTERN = Pattern.compile("\"tag_name\"\\s*:\\s*\"([^\"]+)\"", Pattern.CASE_INSENSITIVE);
private final String apiUrl = "https://git.viper.ipv64.net/api/v1/repos/M_Viper/Minecraft-BungeeCord-Status/releases";
public UpdateChecker(Plugin plugin, String currentVersion, int intervalHours) {
// cached results (volatile für Thread-Sichtbarkeit) this.plugin = plugin;
private volatile String latestVersion = ""; this.currentVersion = currentVersion != null ? currentVersion : "0.0.0";
private volatile String latestUrl = ""; this.intervalHours = Math.max(1, intervalHours);
}
// Patterns for quick parse (we parse JSON as text; this is lightweight and robust for our needs)
private static final Pattern ASSET_NAME_PATTERN = Pattern.compile("\"name\"\\s*:\\s*\"([^\"]+)\"", Pattern.CASE_INSENSITIVE); public void checkNow() {
private static final Pattern DOWNLOAD_PATTERN = Pattern.compile("\"browser_download_url\"\\s*:\\s*\"([^\"]+)\"", Pattern.CASE_INSENSITIVE); try {
private static final Pattern TAG_NAME_PATTERN = Pattern.compile("\"tag_name\"\\s*:\\s*\"([^\"]+)\"", Pattern.CASE_INSENSITIVE); HttpURLConnection conn = (HttpURLConnection) new URL(apiUrl).openConnection();
private static final Pattern NAME_FIELD_PATTERN = Pattern.compile("\"name\"\\s*:\\s*\"([^\"]+)\"", Pattern.CASE_INSENSITIVE); conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/json");
public UpdateChecker(Plugin plugin, String currentVersion, int intervalHours) { conn.setRequestProperty("User-Agent", "StatusAPI-UpdateChecker/2.0");
this.plugin = plugin; conn.setConnectTimeout(5000);
this.currentVersion = currentVersion != null ? currentVersion : "0.0.0"; conn.setReadTimeout(5000);
this.intervalHours = Math.max(1, intervalHours);
} int code = conn.getResponseCode();
if (code != 200) {
@Override plugin.getLogger().warning("Gitea API nicht erreichbar (HTTP " + code + ")");
public void run() { return;
// scheduled task calls checkNow }
checkNow();
} StringBuilder sb = new StringBuilder();
try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"))) {
/** String line;
* Führt einen sofortigen Check aus und updated cache (synchron for the calling thread). while ((line = br.readLine()) != null) sb.append(line).append("\n");
*/ }
public void checkNow() {
try { String body = sb.toString();
plugin.getLogger().info("Prüfe StatusAPI Releases via Gitea API...");
// 1. Die LATEST Version (Tag) finden
HttpURLConnection conn = (HttpURLConnection) new URL(apiUrl).openConnection(); // Wir suchen das erste "tag_name" im gesamten JSON, das ist meistens das neueste Release.
conn.setRequestMethod("GET"); String foundVersion = null;
conn.setRequestProperty("Accept", "application/json"); Matcher tagM = TAG_NAME_PATTERN.matcher(body);
conn.setRequestProperty("User-Agent", "StatusAPI-UpdateChecker/1.0"); if (tagM.find()) {
conn.setConnectTimeout(5000); foundVersion = tagM.group(1).trim();
conn.setReadTimeout(5000); }
int code = conn.getResponseCode(); if (foundVersion == null) {
if (code != 200) { plugin.getLogger().warning("Keine Version (Tag) im Release gefunden.");
plugin.getLogger().warning("Gitea API nicht erreichbar (HTTP " + code + ")"); return;
return; }
}
// Version säubern (v vorne entfernen)
StringBuilder sb = new StringBuilder(); if (foundVersion.startsWith("v") || foundVersion.startsWith("V")) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"))) { foundVersion = foundVersion.substring(1);
String line; }
while ((line = br.readLine()) != null) {
sb.append(line).append("\n"); // 2. Download URL für StatusAPI.jar finden
} // Wir suchen das Asset "StatusAPI.jar"
} String foundUrl = null;
String body = sb.toString(); Pattern releasePattern = Pattern.compile("(?s)\\{.*?\\}");
Matcher releaseMatcher = releasePattern.matcher(body);
// Suche Release mit Asset "StatusAPI.jar" while (releaseMatcher.find()) {
String foundVersion = null; String block = releaseMatcher.group();
String foundUrl = null; java.util.List<String> names = new java.util.ArrayList<>();
java.util.List<String> downloads = new java.util.ArrayList<>();
// Split releases by top-level objects: we simply find repeating {...} blocks and inspect them
// This is robust enough for Gitea JSON responses. Matcher nm = ASSET_NAME_PATTERN.matcher(block);
Pattern releasePattern = Pattern.compile("(?s)\\{.*?\\}"); while (nm.find()) names.add(nm.group(1));
Matcher releaseMatcher = releasePattern.matcher(body);
while (releaseMatcher.find()) { Matcher dm = DOWNLOAD_PATTERN.matcher(block);
String block = releaseMatcher.group(); while (dm.find()) downloads.add(dm.group(1));
// extract asset names & download urls inside the block int pairs = Math.min(names.size(), downloads.size());
java.util.List<String> names = new java.util.ArrayList<>(); for (int i = 0; i < pairs; i++) {
java.util.List<String> downloads = new java.util.ArrayList<>(); String name = names.get(i);
String dl = downloads.get(i);
Matcher nm = ASSET_NAME_PATTERN.matcher(block); if ("StatusAPI.jar".equalsIgnoreCase(name.trim())) {
while (nm.find()) names.add(nm.group(1)); foundUrl = dl;
break;
Matcher dm = DOWNLOAD_PATTERN.matcher(block); }
while (dm.find()) downloads.add(dm.group(1)); }
if (foundUrl != null) break;
int pairs = Math.min(names.size(), downloads.size()); }
for (int i = 0; i < pairs; i++) {
String name = names.get(i); if (foundUrl == null) {
String dl = downloads.get(i); plugin.getLogger().warning("Keine JAR-Datei für dieses Release gefunden.");
if ("StatusAPI.jar".equalsIgnoreCase(name.trim())) { return;
// find tag_name or name field in the same block }
Matcher tagM = TAG_NAME_PATTERN.matcher(block);
if (tagM.find()) { plugin.getLogger().info("Gefundene Version: " + foundVersion + " (Aktuell: " + currentVersion + ")");
foundVersion = tagM.group(1).trim();
} else { latestVersion = foundVersion;
Matcher nameFieldM = NAME_FIELD_PATTERN.matcher(block); latestUrl = foundUrl;
if (nameFieldM.find()) {
foundVersion = nameFieldM.group(1).trim(); } catch (Exception e) {
} plugin.getLogger().log(Level.SEVERE, "Fehler beim Update-Check: " + e.getMessage(), e);
} }
foundUrl = dl; }
break;
} public String getLatestVersion() {
} return latestVersion != null ? latestVersion : "";
}
if (foundUrl != null) break;
} public String getLatestUrl() {
return latestUrl != null ? latestUrl : "";
if (foundVersion == null || foundUrl == null) { }
plugin.getLogger().info("Kein Release mit Asset 'StatusAPI.jar' gefunden.");
return; public boolean isUpdateAvailable(String currentVer) {
} String lv = getLatestVersion();
if (lv.isEmpty()) return false;
// Normalize version return compareVersions(lv, currentVer) > 0;
if (foundVersion.startsWith("v") || foundVersion.startsWith("V")) { }
foundVersion = foundVersion.substring(1);
} private int compareVersions(String a, String b) {
try {
plugin.getLogger().info("Gefundene Version: " + foundVersion + " (aktuell: " + currentVersion + ")"); String[] aa = a.split("\\.");
String[] bb = b.split("\\.");
// set cache int len = Math.max(aa.length, bb.length);
latestVersion = foundVersion; for (int i = 0; i < len; i++) {
latestUrl = foundUrl; int ai = i < aa.length ? Integer.parseInt(aa[i].replaceAll("\\D","")) : 0;
int bi = i < bb.length ? Integer.parseInt(bb[i].replaceAll("\\D","")) : 0;
} catch (Exception e) { if (ai != bi) return Integer.compare(ai, bi);
plugin.getLogger().log(Level.SEVERE, "Fehler beim Update-Check: " + e.getMessage(), e); }
} return 0;
} } catch (Exception ex) {
return a.compareTo(b);
/** }
* Gibt die zuletzt gecachte Version (oder empty string). }
*/ }
public String getLatestVersion() {
return latestVersion != null ? latestVersion : "";
}
/**
* Gibt die zuletzt gecachte download-URL (oder empty string).
*/
public String getLatestUrl() {
return latestUrl != null ? latestUrl : "";
}
/**
* Prüft ob die gecachte latestVersion größer ist als übergebene version.
*/
public boolean isUpdateAvailable(String currentVer) {
String lv = getLatestVersion();
if (lv.isEmpty()) return false;
return compareVersions(lv, currentVer) > 0;
}
/**
* Einfacher SemVer-Vergleich (1.2.3). Liefert >0 wenn a>b, 0 wenn gleich, <0 wenn a<b.
*/
private int compareVersions(String a, String b) {
try {
String[] aa = a.split("\\.");
String[] bb = b.split("\\.");
int len = Math.max(aa.length, bb.length);
for (int i = 0; i < len; i++) {
int ai = i < aa.length ? Integer.parseInt(aa[i].replaceAll("\\D","")) : 0;
int bi = i < bb.length ? Integer.parseInt(bb[i].replaceAll("\\D","")) : 0;
if (ai != bi) return Integer.compare(ai, bi);
}
return 0;
} catch (Exception ex) {
return a.compareTo(b);
}
}
/**
* Convenience: prüft cached state und falls update vorhanden, notifies online players with given permission(s).
*/
public void notifyOnlineOpsIfAvailable(String[] perms) {
String lv = getLatestVersion();
String url = getLatestUrl();
if (lv.isEmpty()) return;
for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) {
try {
for (String perm : perms) {
if (p.hasPermission(perm)) {
p.sendMessage(new TextComponent("§6[StatusAPI] §eNeue Version verfügbar: §a" + lv));
p.sendMessage(new TextComponent("§eDownload: §b" + url));
break;
}
}
} catch (Exception ignored) {}
}
}
}