From eedcd9538570da9d06c5e955215a5b1871a34923 Mon Sep 17 00:00:00 2001 From: Git Manager GUI Date: Fri, 3 Apr 2026 05:19:16 +0200 Subject: [PATCH] Upload folder via GUI - src --- .../de/viper/autoworldreset/ResetManager.java | 253 ++++++++++++++++++ src/main/resources/config.yml | 36 +++ src/main/resources/plugin.yml | 3 +- 3 files changed, 291 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/viper/autoworldreset/ResetManager.java b/src/main/java/de/viper/autoworldreset/ResetManager.java index 3d8f06f..ed98850 100644 --- a/src/main/java/de/viper/autoworldreset/ResetManager.java +++ b/src/main/java/de/viper/autoworldreset/ResetManager.java @@ -1,17 +1,24 @@ package de.viper.autoworldreset; import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; import org.bukkit.World; import org.bukkit.WorldCreator; import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; import java.io.File; import java.io.IOException; +import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.UUID; public class ResetManager { @@ -22,6 +29,11 @@ public class ResetManager { } public void resetWorld() { + if (!Bukkit.isPrimaryThread()) { + Bukkit.getScheduler().runTask(plugin, this::resetWorld); + return; + } + String worldName = plugin.getConfig().getString("world-name"); if (worldName == null || worldName.isEmpty()) { @@ -29,6 +41,8 @@ public class ResetManager { return; } + executeConfiguredCommands("pre-reset-commands", "Pre-Reset", worldName); + // BUG FIX #6: Die Backup-Prüfung wurde aus resetWorld() entfernt und korrekt // platziert. Zuvor wurde backup.enabled geprüft, um einen RESTORE abzubrechen – // das ist logisch falsch. Das Flag sollte nur das automatische Erstellen eines @@ -37,7 +51,10 @@ public class ResetManager { boolean success = restoreBackup(worldName); if (!success) { plugin.getLogger().warning("Reset fehlgeschlagen: Backup konnte nicht wiederhergestellt werden."); + return; } + + executeConfiguredCommands("post-reset-commands", "Post-Reset", worldName); } public boolean createBackup() { @@ -156,6 +173,242 @@ public class ResetManager { } } + private void executeConfiguredCommands(String pathPrefix, String label, String worldName) { + if (!plugin.getConfig().getBoolean(pathPrefix + ".enabled", false)) return; + + List commands = plugin.getConfig().getStringList(pathPrefix + ".commands"); + if (commands.isEmpty()) return; + + String configuredHomeName = plugin.getConfig().getString("home-name", worldName); + String homeName = (configuredHomeName == null || configuredHomeName.isBlank()) + ? worldName + : configuredHomeName; + + for (String command : commands) { + if (command.startsWith("@cmi-deletehomes-world")) { + String resolved = command + .replace("{world}", worldName) + .replace("{home}", homeName); + + String[] parts = resolved.split("\\s+"); + String targetWorld = parts.length >= 2 ? parts[1] : worldName; + + plugin.getLogger().info("Führe " + label + "-Spezialbefehl aus: CMI Homes in Welt '" + targetWorld + "' löschen"); + boolean success = deleteCmiHomesInWorldViaApi(targetWorld); + if (!success) { + plugin.getLogger().warning("CMI API-Löschung fehlgeschlagen. Prüfe, ob CMI geladen ist und Homes vorhanden sind."); + } + continue; + } + + if (command.contains("{player}")) { + int executed = 0; + for (OfflinePlayer offlinePlayer : Bukkit.getOfflinePlayers()) { + if (!offlinePlayer.isOnline() && !offlinePlayer.hasPlayedBefore()) continue; + + String playerName = offlinePlayer.getName(); + if (playerName == null || playerName.isBlank()) continue; + + String resolved = command + .replace("{world}", worldName) + .replace("{home}", homeName) + .replace("{player}", playerName); + + plugin.getLogger().info("Führe " + label + "-Befehl aus: " + resolved); + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), resolved); + executed++; + } + plugin.getLogger().info(label + "-Befehl mit {player} für " + executed + " Spieler ausgeführt."); + continue; + } + + String resolved = command + .replace("{world}", worldName) + .replace("{home}", homeName); + plugin.getLogger().info("Führe " + label + "-Befehl aus: " + resolved); + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), resolved); + } + } + + private boolean deleteCmiHomesInWorldViaApi(String worldName) { + Plugin cmiPlugin = Bukkit.getPluginManager().getPlugin("CMI"); + if (cmiPlugin == null || !cmiPlugin.isEnabled()) { + plugin.getLogger().warning("CMI ist nicht geladen oder nicht aktiv."); + return false; + } + + try { + Class cmiClass = Class.forName("com.Zrips.CMI.CMI"); + Method getInstance = cmiClass.getMethod("getInstance"); + Object cmiInstance = getInstance.invoke(null); + if (cmiInstance == null) return false; + + Object playerManager = invokeNoArg(cmiInstance, "getPlayerManager"); + if (playerManager == null) { + plugin.getLogger().warning("CMI PlayerManager konnte nicht gefunden werden."); + return false; + } + + int removed = 0; + int checked = 0; + int usersCount = 0; + + Map users = collectCmiUsers(playerManager); + for (Map.Entry entry : users.entrySet()) { + String playerName = entry.getKey(); + Object user = entry.getValue(); + if (playerName == null || playerName.isBlank() || user == null) continue; + usersCount++; + + List homesInWorld = getHomeNamesForWorld(user, worldName); + checked += homesInWorld.size(); + + for (String home : homesInWorld) { + String cmd = "cmi removehome " + home + " " + playerName; + boolean ok = Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmd); + if (ok) removed++; + } + } + + plugin.getLogger().info("CMI API: User geprüft=" + usersCount + ", Homes in Welt '" + worldName + "' gefunden=" + checked + ", entfernt=" + removed + "."); + return true; + } catch (Throwable t) { + plugin.getLogger().warning("CMI API-Integration fehlgeschlagen: " + t.getMessage()); + return false; + } + } + + private Map collectCmiUsers(Object playerManager) { + Map result = new LinkedHashMap<>(); + + // Primär: direkt aus CMI PlayerManager lesen (umfasst mehr als Bukkit Offline-Cache) + Object usersObj = invokeNoArg(playerManager, "getAllUsers"); + if (usersObj == null) usersObj = invokeNoArg(playerManager, "getUsers"); + if (usersObj == null) usersObj = invokeNoArg(playerManager, "getUsersMap"); + + if (usersObj instanceof Map map) { + for (Object value : map.values()) { + String name = extractCmiUserName(value); + if (name != null && !name.isBlank()) { + result.putIfAbsent(name, value); + } + } + } else if (usersObj instanceof Collection collection) { + for (Object value : collection) { + String name = extractCmiUserName(value); + if (name != null && !name.isBlank()) { + result.putIfAbsent(name, value); + } + } + } + + // Fallback: Bukkit OfflinePlayer durchprobieren + for (OfflinePlayer offlinePlayer : Bukkit.getOfflinePlayers()) { + if (!offlinePlayer.isOnline() && !offlinePlayer.hasPlayedBefore()) continue; + + String playerName = offlinePlayer.getName(); + if (playerName == null || playerName.isBlank() || result.containsKey(playerName)) continue; + + Object user = getCmiUser(playerManager, offlinePlayer, playerName); + if (user != null) { + result.put(playerName, user); + } + } + + return result; + } + + private String extractCmiUserName(Object cmiUser) { + if (cmiUser == null) return null; + + Object name = invokeNoArg(cmiUser, "getName"); + if (name instanceof String s && !s.isBlank()) return s; + + Object playerName = invokeNoArg(cmiUser, "getPlayerName"); + if (playerName instanceof String s && !s.isBlank()) return s; + + Object offline = invokeNoArg(cmiUser, "getOfflinePlayer"); + if (offline instanceof OfflinePlayer op && op.getName() != null && !op.getName().isBlank()) { + return op.getName(); + } + + return null; + } + + private Object getCmiUser(Object playerManager, OfflinePlayer offlinePlayer, String playerName) { + Object user = invokeOneArg(playerManager, "getUser", UUID.class, offlinePlayer.getUniqueId()); + if (user != null) return user; + + user = invokeOneArg(playerManager, "getUser", String.class, playerName); + if (user != null) return user; + + return invokeOneArg(playerManager, "getUser", OfflinePlayer.class, offlinePlayer); + } + + private List getHomeNamesForWorld(Object cmiUser, String worldName) { + List result = new ArrayList<>(); + Object homesObj = invokeNoArg(cmiUser, "getHomes"); + if (!(homesObj instanceof Map homesMap)) return result; + + for (Map.Entry entry : homesMap.entrySet()) { + String homeName = String.valueOf(entry.getKey()); + Object homeObj = entry.getValue(); + + if (homeName == null || homeName.isBlank() || homeObj == null) continue; + if (isHomeInWorld(homeObj, worldName)) { + result.add(homeName); + } + } + + return result; + } + + private boolean isHomeInWorld(Object homeObj, String worldName) { + String lowerWorld = worldName.toLowerCase(); + + Object worldNameObj = invokeNoArg(homeObj, "getWorldName"); + if (worldNameObj instanceof String s && s.equalsIgnoreCase(worldName)) return true; + + Object worldObj = invokeNoArg(homeObj, "getWorld"); + if (worldObj instanceof World w && w.getName().equalsIgnoreCase(worldName)) return true; + if (worldObj instanceof String s && s.equalsIgnoreCase(worldName)) return true; + + Object locationObj = invokeNoArg(homeObj, "getLoc"); + if (locationObj == null) locationObj = invokeNoArg(homeObj, "getLocation"); + if (locationObj != null) { + Object locWorld = invokeNoArg(locationObj, "getWorld"); + if (locWorld instanceof World w && w.getName().equalsIgnoreCase(worldName)) return true; + + Object locWorldName = invokeNoArg(locationObj, "getWorldName"); + if (locWorldName instanceof String s && s.equalsIgnoreCase(worldName)) return true; + } + + // Fallback: Einige CMI Home-Objekte enthalten den Weltnamen in toString(). + return homeObj.toString().toLowerCase().contains(lowerWorld); + } + + private Object invokeNoArg(Object target, String methodName) { + if (target == null) return null; + try { + Method method = target.getClass().getMethod(methodName); + method.setAccessible(true); + return method.invoke(target); + } catch (Throwable ignored) { + return null; + } + } + + private Object invokeOneArg(Object target, String methodName, Class argType, Object arg) { + if (target == null) return null; + try { + Method method = target.getClass().getMethod(methodName, argType); + method.setAccessible(true); + return method.invoke(target, arg); + } catch (Throwable ignored) { + return null; + } + } + private void deleteFolder(Path path) throws IOException { if (Files.notExists(path)) return; if (Files.isDirectory(path)) { diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index cf0775d..939a55e 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,6 +1,10 @@ # Weltname, die zurückgesetzt werden soll world-name: "world" +# Home-Name für Home-Plugins (falls Home-Name nicht dem Weltnamen entspricht) +# Beispiel: Test-01 -> /cmi removehome Test-01 +home-name: "world" + # Ob beim Serverstart automatisch ein Reset durchgeführt werden soll auto-reset-on-startup: false @@ -36,6 +40,38 @@ scheduler: # "0 0 */6 * * ?" → Alle 6 Stunden cron: "0 0 4 * * ?" +# Befehle, die VOR dem Reset als Konsole ausgeführt werden. +# Nützlich um z.B. Homes in der zurückgesetzten Welt zu löschen. +# Platzhalter: {world} = Name der Welt, die zurückgesetzt wird +# {home} = Home-Name aus 'home-name' (Fallback: {world}) +# {player} = Spielername (Befehl wird für alle bekannten Spieler ausgeführt) +# +# Bekannte Befehle für verbreitete Home-Plugins: +# HuskHomes: "huskhomes delete homes {world}" +# Optional: "huskhomes delete homes {world} confirm" +# UltimateHomes: "delhome {world}" +# Für alle bekannten Spieler: "delhome {player} {world}" +# CMI: "@cmi-deletehomes-world {world}" +# (interner Spezialbefehl von AutoWorldReset, löscht alle CMI-Homes in dieser Welt) +# +# Eigenen Befehl einfach eintragen – wird als Konsole ausgeführt. +pre-reset-commands: + enabled: false + commands: + # - "huskhomes delete homes {world} confirm" # HuskHomes + # - "huskhomes delete homes {world} s1 confirm" # HuskHomes (mit Servername + confirm) + # - "uh deleteallhomesinworld {world}" # UltimateHomes Wichtig! der Befehl muss in der config von Ultimatehomes auf true gestellt werden, damit er funktioniert: enableDeleteAllHomesInWorldAdminCommand true + # - "cmi removehome {home} {player}" # CMI (besser in post-reset) + +# Befehle, die NACH dem erfolgreichen Reset ausgeführt werden. +# Nützlich für Plugin-Reloads oder Cache-Refresh (z.B. Home-GUIs). +post-reset-commands: + enabled: false + commands: + # - "@cmi-deletehomes-world {world}" # CMI API: alle Homes dieser Welt löschen + # - "cmi saveall" # CMI Daten direkt persistent speichern + # - "cmi reload" # CMI Cache/GUI neu laden + # Nachrichten – können vollständig in lang.yml angepasst werden. # lang.yml hat Vorrang vor den Werten hier. messages: diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 544ad53..85b1ea0 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,9 +1,10 @@ name: AutoWorldReset -version: 1.2 +version: 1.3 main: de.viper.autoworldreset.AutoWorldReset api-version: 1.21 description: Automatisches Welt-Reset Plugin mit Backup-System & Quartz Cron-Scheduler author: M_Viper +softdepend: [CMI] commands: autoworldreset: