Dateien nach "src/main/java/net/viper/status" hochladen

This commit is contained in:
2026-01-08 14:57:53 +00:00
parent 841b726842
commit 5597f99a87
3 changed files with 496 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
package net.viper.status;
import net.md_5.bungee.api.plugin.Plugin;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.function.Consumer;
public class FileDownloader {
private final Plugin plugin;
public FileDownloader(Plugin plugin) { this.plugin = plugin; }
public void downloadFile(String urlString, File destination, Runnable onSuccess) {
plugin.getProxy().getScheduler().runAsync(plugin, () -> {
BufferedInputStream bufferedInputStream = null;
FileOutputStream fileOutputStream = null;
try {
URL url = new URL(urlString);
bufferedInputStream = new BufferedInputStream(url.openStream());
fileOutputStream = new FileOutputStream(destination);
byte[] buffer = new byte[1024];
int count;
while ((count = bufferedInputStream.read(buffer, 0, 1024)) != -1) {
fileOutputStream.write(buffer, 0, count);
}
fileOutputStream.close();
bufferedInputStream.close();
plugin.getProxy().getScheduler().schedule(plugin, onSuccess, 1, java.util.concurrent.TimeUnit.MILLISECONDS);
} catch (Throwable e) {
plugin.getLogger().warning("Download fehlgeschlagen: " + e.getMessage());
if (destination.exists()) destination.delete();
} finally {
if (fileOutputStream != null) try { fileOutputStream.close(); } catch (IOException ignored) {}
if (bufferedInputStream != null) try { bufferedInputStream.close(); } catch (IOException ignored) {}
}
});
}
}

View File

@@ -0,0 +1,310 @@
package net.viper.status;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Plugin;
import net.viper.status.module.ModuleManager;
import net.viper.status.stats.PlayerStats;
import net.viper.status.stats.StatsModule;
import net.viper.status.modules.verify.VerifyModule;
import net.viper.status.modules.globalchat.GlobalChatModule;
import net.viper.status.modules.navigation.NavigationModule;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.TimeUnit;
public class StatusAPI extends Plugin implements Runnable {
private Thread thread;
private int port = 9191;
// Das neue Modul-System
private ModuleManager moduleManager;
// Alte Komponenten (UpdateChecker/FileDownloader bleiben hier, da sie Core-Updates steuern)
private UpdateChecker updateChecker;
private FileDownloader fileDownloader;
@Override
public void onEnable() {
getLogger().info("StatusAPI Core wird initialisiert...");
// 1. Ordner sicherstellen
if (!getDataFolder().exists()) {
getDataFolder().mkdirs();
}
// 2. Modul-System starten
moduleManager = new ModuleManager();
// 3. MODULE REGISTRIEREN (Hier erweiterst du die API in Zukunft!)
// Um ein neues Feature hinzuzufügen, erstelle eine Klasse, die 'Module' implementiert
// und füge hier eine Zeile hinzu: moduleManager.registerModule(new MeinNeuesModul());
moduleManager.registerModule(new StatsModule()); // Statistik System laden
moduleManager.registerModule(new VerifyModule()); // Verify Modul
moduleManager.registerModule(new GlobalChatModule()); // GlobalChat
moduleManager.registerModule(new NavigationModule()); //Server Switcher
// 4. Alle Module aktivieren
moduleManager.enableAll(this);
// 5. WebServer Thread starten
getLogger().info("Starte Web-Server auf Port " + port + "...");
thread = new Thread(this, "StatusAPI-HTTP-Server");
thread.start();
// 6. Update System Initialisieren
String currentVersion = getDescription() != null ? getDescription().getVersion() : "0.0.0";
updateChecker = new UpdateChecker(this, currentVersion, 6);
fileDownloader = new FileDownloader(this);
File pluginFile = getFile();
File backupFile = new File(pluginFile.getParentFile(), "StatusAPI.jar.bak");
// Backup Cleanup
if (backupFile.exists()) {
ProxyServer.getInstance().getScheduler().schedule(this, () -> {
if (backupFile.exists()) backupFile.delete();
}, 1, TimeUnit.MINUTES);
}
// Sofortiger Check
checkAndMaybeUpdate();
// Regelmäßiger Check
ProxyServer.getInstance().getScheduler().schedule(this, this::checkAndMaybeUpdate, 6, 6, TimeUnit.HOURS);
}
@Override
public void onDisable() {
getLogger().info("Stoppe Module...");
if (moduleManager != null) {
moduleManager.disableAll(this);
}
getLogger().info("Stoppe Web-Server...");
if (thread != null) {
thread.interrupt();
try { thread.join(1000); } catch (InterruptedException ignored) {}
}
}
// --- Update Logik (unverändert) ---
private void checkAndMaybeUpdate() {
try {
updateChecker.checkNow();
String currentVersion = getDescription() != null ? getDescription().getVersion() : "0.0.0";
if (updateChecker.isUpdateAvailable(currentVersion)) {
String newVersion = updateChecker.getLatestVersion();
String url = updateChecker.getLatestUrl();
getLogger().warning("----------------------------------------");
getLogger().warning("Neue Version verfügbar: " + newVersion);
getLogger().warning("Starte automatisches Update...");
getLogger().warning("----------------------------------------");
File pluginFile = getFile();
File newFile = new File(pluginFile.getParentFile(), "StatusAPI.jar.new");
fileDownloader.downloadFile(url, newFile, () -> triggerUpdateScript(pluginFile, newFile));
}
} catch (Exception e) {
getLogger().severe("Fehler beim Update-Check: " + e.getMessage());
}
}
private void triggerUpdateScript(File currentFile, File newFile) {
try {
File pluginsFolder = currentFile.getParentFile();
File rootFolder = pluginsFolder.getParentFile();
File batFile = new File(rootFolder, "StatusAPI_Update_" + System.currentTimeMillis() + ".bat");
String batContent = "@echo off\n" +
"echo Bitte warten, der Server fährt herunter...\n" +
"timeout /t 5 /nobreak >nul\n" +
"cd /d \"" + pluginsFolder.getAbsolutePath().replace("\\", "/") + "\"\n" +
"echo Fuehre Datei-Tausch durch...\n" +
"if exist StatusAPI.jar.bak del StatusAPI.jar.bak\n" +
"if exist StatusAPI.jar (\n" +
" ren StatusAPI.jar StatusAPI.jar.bak\n" +
")\n" +
"if exist StatusAPI.new.jar (\n" +
" ren StatusAPI.new.jar StatusAPI.jar\n" +
" echo Update erfolgreich!\n" +
") else (\n" +
" echo FEHLER: StatusAPI.new.jar nicht gefunden!\n" +
" pause\n" +
")\n" +
"del \"%~f0\"";
try (PrintWriter out = new PrintWriter(batFile)) { out.println(batContent); }
for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) {
p.disconnect("§cServer fährt für ein Update neu herunter. Bitte etwas warten.");
}
getLogger().info("Starte Update-Skript...");
Runtime.getRuntime().exec("cmd /c start \"Update_Proc\" \"" + batFile.getAbsolutePath() + "\"");
ProxyServer.getInstance().stop();
} catch (Exception e) {
getLogger().severe("Fehler beim Vorbereiten des Updates: " + e.getMessage());
}
}
// --- WebServer & JSON ---
@Override
public void run() {
try (ServerSocket serverSocket = new ServerSocket(port)) {
serverSocket.setSoTimeout(1000);
while (!Thread.interrupted()) {
try {
Socket clientSocket = serverSocket.accept();
handleConnection(clientSocket);
} catch (java.net.SocketTimeoutException e) {}
catch (IOException e) {
getLogger().warning("Fehler beim Akzeptieren einer Verbindung: " + e.getMessage());
}
}
} catch (IOException e) {
getLogger().severe("Konnte ServerSocket nicht starten: " + e.getMessage());
}
}
private void handleConnection(Socket clientSocket) {
try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(), "UTF-8"));
OutputStream out = clientSocket.getOutputStream()) {
String inputLine = in.readLine();
if (inputLine != null && inputLine.startsWith("GET")) {
Map<String, Object> data = new LinkedHashMap<>();
data.put("online", true);
// Version & Info
String versionRaw = ProxyServer.getInstance().getVersion();
String versionClean = (versionRaw != null && versionRaw.contains(":")) ? versionRaw.split(":")[2].trim() : versionRaw;
data.put("version", versionClean);
data.put("max_players", String.valueOf(ProxyServer.getInstance().getConfig().getPlayerLimit()));
String motd = "BungeeCord";
try {
Iterator<ListenerInfo> it = ProxyServer.getInstance().getConfig().getListeners().iterator();
if (it.hasNext()) motd = it.next().getMotd();
} catch (Exception ignored) {}
data.put("motd", motd);
// StatsModul holen (Service Locator)
StatsModule statsModule = (StatsModule) moduleManager.getModule("StatsModule");
boolean luckPermsEnabled = ProxyServer.getInstance().getPluginManager().getPlugin("LuckPerms") != null;
List<Map<String, Object>> playersList = new ArrayList<>();
for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) {
Map<String, Object> playerInfo = new LinkedHashMap<>();
playerInfo.put("name", p.getName());
try { playerInfo.put("uuid", p.getUniqueId().toString()); } catch (Exception ignored) {}
String prefix = "";
if (luckPermsEnabled) {
try {
Class<?> providerClass = Class.forName("net.luckperms.api.LuckPermsProvider");
Object luckPermsApi = providerClass.getMethod("get").invoke(null);
Object userManager = luckPermsApi.getClass().getMethod("getUserManager").invoke(luckPermsApi);
Object user = userManager.getClass().getMethod("getUser", UUID.class).invoke(userManager, p.getUniqueId());
if (user != null) {
Class<?> queryOptionsClass = Class.forName("net.luckperms.api.query.QueryOptions");
Object queryOptions = queryOptionsClass.getMethod("defaultContextualOptions").invoke(null);
Object cachedData = user.getClass().getMethod("getCachedData").invoke(user);
Object metaData = cachedData.getClass().getMethod("getMetaData", queryOptionsClass).invoke(cachedData, queryOptions);
Object result = metaData.getClass().getMethod("getPrefix").invoke(metaData);
if (result != null) prefix = (String) result;
}
} catch (Exception ignored) {}
}
playerInfo.put("prefix", prefix);
// Stats Integration via Modul
if (statsModule != null) {
PlayerStats ps = statsModule.getManager().getIfPresent(p.getUniqueId());
if (ps != null) {
playerInfo.put("playtime", ps.getPlaytimeWithCurrentSession());
playerInfo.put("joins", ps.joins);
playerInfo.put("first_seen", ps.firstSeen);
playerInfo.put("last_seen", ps.lastSeen);
}
}
playersList.add(playerInfo);
}
data.put("players", playersList);
String json = buildJsonString(data);
byte[] jsonBytes = json.getBytes("UTF-8");
StringBuilder response = new StringBuilder();
response.append("HTTP/1.1 200 OK\r\n");
response.append("Content-Type: application/json; charset=UTF-8\r\n");
response.append("Access-Control-Allow-Origin: *\r\n");
response.append("Content-Length: ").append(jsonBytes.length).append("\r\n");
response.append("Connection: close\r\n\r\n");
out.write(response.toString().getBytes("UTF-8"));
out.write(jsonBytes);
out.flush();
}
} catch (Exception e) {
getLogger().severe("Fehler beim Verarbeiten der Anfrage: " + e.getMessage());
}
}
private String buildJsonString(Map<String, Object> data) {
StringBuilder sb = new StringBuilder("{");
boolean first = true;
for (Map.Entry<String, Object> entry : data.entrySet()) {
if (!first) sb.append(",");
first = false;
sb.append("\"").append(escapeJson(entry.getKey())).append("\":").append(valueToString(entry.getValue()));
}
sb.append("}");
return sb.toString();
}
private String valueToString(Object value) {
if (value == null) return "null";
else if (value instanceof Boolean) return value.toString();
else if (value instanceof Number) return value.toString();
else if (value instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> m = (Map<String, Object>) value;
return buildJsonString(m);
} else if (value instanceof List) {
StringBuilder sb = new StringBuilder("[");
List<?> list = (List<?>) value;
for (int i = 0; i < list.size(); i++) {
if (i > 0) sb.append(",");
Object item = list.get(i);
if (item instanceof Map) sb.append(buildJsonString((Map<String, Object>) item));
else if (item instanceof Number) sb.append(item.toString());
else sb.append("\"").append(escapeJson(String.valueOf(item))).append("\"");
}
sb.append("]");
return sb.toString();
}
else return "\"" + escapeJson(String.valueOf(value)) + "\"";
}
private String escapeJson(String s) {
if (s == null) return "";
return s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "\\r");
}
}

View File

@@ -0,0 +1,144 @@
package net.viper.status;
import net.md_5.bungee.api.plugin.Plugin;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class UpdateChecker {
private final Plugin plugin;
private final String currentVersion;
private final int intervalHours;
// Neue Domain und korrekter API-Pfad für Releases
private final String apiUrl = "https://git.viper.ipv64.net/api/v1/repos/M_Viper/StatusAPI/releases";
private volatile String latestVersion = "";
private volatile String latestUrl = "";
private static final Pattern ASSET_NAME_PATTERN = Pattern.compile("\"name\"\\s*:\\s*\"([^\"]+)\"", Pattern.CASE_INSENSITIVE);
private static final Pattern DOWNLOAD_PATTERN = Pattern.compile("\"browser_download_url\"\\s*:\\s*\"([^\"]+)\"", Pattern.CASE_INSENSITIVE);
private static final Pattern TAG_NAME_PATTERN = Pattern.compile("\"tag_name\"\\s*:\\s*\"([^\"]+)\"", Pattern.CASE_INSENSITIVE);
public UpdateChecker(Plugin plugin, String currentVersion, int intervalHours) {
this.plugin = plugin;
this.currentVersion = currentVersion != null ? currentVersion : "0.0.0";
this.intervalHours = Math.max(1, intervalHours);
}
public void checkNow() {
try {
HttpURLConnection conn = (HttpURLConnection) new URL(apiUrl).openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/json");
conn.setRequestProperty("User-Agent", "StatusAPI-UpdateChecker/2.0");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
int code = conn.getResponseCode();
if (code != 200) {
plugin.getLogger().warning("Gitea/Forgejo API nicht erreichbar (HTTP " + code + ")");
return;
}
StringBuilder sb = new StringBuilder();
try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"))) {
String line;
while ((line = br.readLine()) != null) sb.append(line).append("\n");
}
String body = sb.toString();
// Neu: Da die API ein JSON-Array von Releases zurückgibt, nehmen wir das erste (neueste) Release
// Wir suchen den ersten Block mit tag_name
String foundVersion = null;
Matcher tagM = TAG_NAME_PATTERN.matcher(body);
if (tagM.find()) {
foundVersion = tagM.group(1).trim();
}
if (foundVersion == null) {
plugin.getLogger().warning("Keine Version (Tag) im Release gefunden.");
return;
}
if (foundVersion.startsWith("v") || foundVersion.startsWith("V")) {
foundVersion = foundVersion.substring(1);
}
String foundUrl = null;
// Wir suchen im gesamten Body nach der JAR-Datei "StatusAPI.jar"
// Da das neueste Release zuerst kommt, brechen wir ab, sobald wir eine passende JAR finden
Matcher nameMatcher = ASSET_NAME_PATTERN.matcher(body);
Matcher downloadMatcher = DOWNLOAD_PATTERN.matcher(body);
java.util.List<String> names = new java.util.ArrayList<>();
java.util.List<String> urls = new java.util.ArrayList<>();
while (nameMatcher.find()) {
names.add(nameMatcher.group(1));
}
while (downloadMatcher.find()) {
urls.add(downloadMatcher.group(1));
}
int pairs = Math.min(names.size(), urls.size());
for (int i = 0; i < pairs; i++) {
String name = names.get(i).trim();
String url = urls.get(i);
if ("StatusAPI.jar".equalsIgnoreCase(name)) {
foundUrl = url;
break; // Erste (also neueste) passende JAR nehmen
}
}
if (foundUrl == null) {
plugin.getLogger().warning("Keine StatusAPI.jar im neuesten Release gefunden.");
return;
}
plugin.getLogger().info("Gefundene Version: " + foundVersion + " (Aktuell: " + currentVersion + ")");
latestVersion = foundVersion;
latestUrl = foundUrl;
} catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Fehler beim Update-Check: " + e.getMessage(), e);
}
}
public String getLatestVersion() {
return latestVersion != null ? latestVersion : "";
}
public String getLatestUrl() {
return latestUrl != null ? latestUrl : "";
}
public boolean isUpdateAvailable(String currentVer) {
String lv = getLatestVersion();
if (lv.isEmpty()) return false;
return compareVersions(lv, currentVer) > 0;
}
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);
}
}
}