src/main/java/com/viper/autosortchest/Main.java aktualisiert

This commit is contained in:
2026-01-14 13:35:24 +00:00
parent f99265a4eb
commit d085ead6e9

View File

@@ -2,9 +2,13 @@ package com.viper.autosortchest;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.Particle;
import org.bukkit.Particle.DustOptions;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.Chest;
@@ -24,11 +28,14 @@ import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;
import java.io.File;
import java.io.IOException;
@@ -42,7 +49,82 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
private FileConfiguration config;
private final Map<UUID, Map<Material, Long>> fullChestMessageTracker = new HashMap<>();
private static final long MESSAGE_COOLDOWN = 5 * 60 * 1000; // 5 Minuten in Millisekunden
private static final String CONFIG_VERSION = "1.6"; // Version erhöht für das Update
private static final String CONFIG_VERSION = "1.8";
// --- NEU: Update Checker Variabben ---
private boolean updateAvailable = false;
private String latestVersion = "";
// --------------------------------------------------
// --- Hardcodierte Nachrichten (DE & EN) für Hilfe und Info (Safe in Java) ---
private static final String HELP_DE =
"&6&l=== AutoSortChest Hilfe ===\n" +
"&eEingangstruhe erstellen:\n" +
"&f1. Platziere ein Schild an einer Truhe.\n" +
"&f2. Schreibe:\n" +
" &7[asc]\n" +
" &7input\n" +
"&f3. Fülle die Truhe mit Items.\n" +
"&eZieltruhe erstellen:\n" +
"&f1. Platziere ein Schild an einer Truhe.\n" +
"&f2. Schreibe:\n" +
" &7[asc]\n" +
" &7ziel\n" +
"&f3. Rechtsklicke mit einem Item in der Hand.\n" +
"&eRest-Truhe (Fallback) erstellen:\n" +
"&f1. Platziere ein Schild an einer Truhe.\n" +
"&f2. Schreibe:\n" +
" &7[asc]\n" +
" &7rest\n" +
"&eBefehle:\n" +
"&f- &b/asc help &f- Zeigt diese Hilfe.\n" +
"&f- &b/asc info &f- Zeigt Plugin-Informationen.\n" +
"&f- &b/asc reload &f- Lädt die Konfiguration neu (OP).\n" +
"&6&l========================";
private static final String HELP_EN =
"&6&l=== AutoSortChest Help ===\n" +
"&eCreate Input Chest:\n" +
"&f1. Place a sign on a chest.\n" +
"&f2. Write:\n" +
" &7[asc]\n" +
" &7input\n" +
"&f3. Fill the chest with items.\n" +
"&eCreate Target Chest:\n" +
"&f1. Place a sign on a chest.\n" +
"&f2. Write:\n" +
" &7[asc]\n" +
" &7target\n" +
"&f3. Right-click with an item in hand.\n" +
"&eCreate Rest Chest (Fallback):\n" +
"&f1. Place a sign on a chest.\n" +
"&f2. Write:\n" +
" &7[asc]\n" +
" &7rest\n" +
"&eCommands:\n" +
"&f- &b/asc help &f- Shows this help.\n" +
"&f- &b/asc info &f- Shows plugin info.\n" +
"&f- &b/asc reload &f- Reloads the config (OP only).\n" +
"&6&l========================";
private static final String INFO_DE =
"&6&l=== AutoSortChest Info ===\n" +
"&ePlugin: &fAutoSortChest\n" +
"&eVersion: &f%version%\n" +
"&eConfig-Version: &f%config_version%\n" +
"&eErsteller: &f%author%\n" +
"&eBeschreibung: &fAutomatisches Sortieren von Items in Truhen.\n" +
"&6&l========================";
private static final String INFO_EN =
"&6&l=== AutoSortChest Info ===\n" +
"&ePlugin: &fAutoSortChest\n" +
"&eVersion: &f%version%\n" +
"&eConfig-Version: &f%config_version%\n" +
"&eAuthor: &f%author%\n" +
"&eDescription: &fAutomatically sorts items into chests.\n" +
"&6&l========================";
// ----------------------------------------------------------------------------------
@Override
public void onEnable() {
@@ -86,6 +168,29 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
// ERWEITERUNG: Migration alter Daten in das neue Listen-System
migrateInputChests();
// --- NEU: Update Checker Start (Ganz oben in onEnable) ---
new UpdateChecker(this, 131309).getVersion(version -> {
Bukkit.getScheduler().runTask(this, () -> {
if (this.getDescription().getVersion().equals(version)) {
this.updateAvailable = false;
this.latestVersion = version;
getLogger().info("Es ist kein neues Update verfügbar.");
} else {
this.updateAvailable = true;
this.latestVersion = version;
getLogger().info(" ");
getLogger().info("========================================");
getLogger().info("Eine neue Version ist verfügbar: " + version);
getLogger().info("Deine Version: " + this.getDescription().getVersion());
getLogger().info("Download: https://www.spigotmc.org/resources/" + 131309 + "/");
getLogger().info("========================================");
getLogger().info(" ");
}
});
});
// ----------------------------------------------------
// ASCII ART LOGO
getLogger().info("");
getLogger().info(" ___ _ _____ _ _____ _ _ ");
@@ -105,6 +210,32 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
getLogger().info("AutoSortChest Plugin deaktiviert!");
}
// --- NEU: In-Game Update Nachricht (Hier platziert wie gewünscht) ---
@EventHandler
public void onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent event) {
// Nur wenn ein Update verfügbar ist
if (!updateAvailable) return;
Player player = event.getPlayer();
// Nur an Admins senden
if (!isAdmin(player)) return;
// Kurze Verzögerung (1 Sekunde), damit die Chat-Nachricht nicht im "Welcome" Spam untergeht
new BukkitRunnable() {
@Override
public void run() {
player.sendMessage(ChatColor.GOLD + "========================================");
player.sendMessage(ChatColor.GOLD + "[AutoSortChest] " + ChatColor.YELLOW + "Es ist ein Update verfügbar!");
player.sendMessage(ChatColor.GRAY + "Deine Version: " + ChatColor.RED + getDescription().getVersion());
player.sendMessage(ChatColor.GRAY + "Neue Version: " + ChatColor.GREEN + latestVersion);
player.sendMessage(ChatColor.GRAY + "Download: " + ChatColor.AQUA + "https://www.spigotmc.org/resources/" + 131309 + "/");
player.sendMessage(ChatColor.GOLD + "========================================");
}
}.runTaskLater(this, 20L * 1); // 1 Sekunde Verzögerung
}
// --------------------------------------------------------------
// --- NEU: Admin Prüfung ---
private boolean isAdmin(Player player) {
return player.isOp() || player.hasPermission("autosortchest.admin");
@@ -205,12 +336,29 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
getLogger().info("Setze Standardwert: debug = " + config.getBoolean("debug"));
}
// --- NEU: Sprache ---
if (!config.contains("language")) {
config.set("language", defaultConfig.getString("language", "de"));
getLogger().info("Setze Standardwert: language = " + config.getString("language"));
}
// -------------------
// --- NEU: Effekte Konfiguration (Partikel + Sound) ---
if (!config.contains("effects")) {
config.createSection("effects");
config.set("effects.enabled", defaultConfig.getBoolean("effects.enabled", true));
config.set("effects.sound", defaultConfig.getBoolean("effects.sound", true));
config.set("effects.type", defaultConfig.getString("effects.type", "DUST"));
getLogger().info("Setze Standardwerte für effects");
}
// ----------------------------------
// Prüfe und setze sign-colors.input
if (!config.contains("sign-colors.input")) {
config.createSection("sign-colors.input");
config.set("sign-colors.input.line1", defaultConfig.getString("sign-colors.input.line1", "&6"));
config.set("sign-colors.input.line2", defaultConfig.getString("sign-colors.input.line2", "&0"));
config.set("sign-colors.input.line4", defaultConfig.getString("sign-colors.input.line4", "&1")); // Korrigiert auf &1 basierend auf user Config
config.set("sign-colors.input.line4", defaultConfig.getString("sign-colors.input.line4", "&1"));
getLogger().info("Setze Standardwerte für sign-colors.input");
} else {
if (!config.contains("sign-colors.input.line1")) {
@@ -233,7 +381,7 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
config.set("sign-colors.target.line1", defaultConfig.getString("sign-colors.target.line1", "&6"));
config.set("sign-colors.target.line2", defaultConfig.getString("sign-colors.target.line2", "&0"));
config.set("sign-colors.target.line3", defaultConfig.getString("sign-colors.target.line3", "&f"));
config.set("sign-colors.target.line4", defaultConfig.getString("sign-colors.target.line4", "&1")); // Korrigiert auf &1
config.set("sign-colors.target.line4", defaultConfig.getString("sign-colors.target.line4", "&1"));
getLogger().info("Setze Standardwerte für sign-colors.target");
} else {
if (!config.contains("sign-colors.target.line1")) {
@@ -260,7 +408,7 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
config.set("sign-colors.full.line1", defaultConfig.getString("sign-colors.full.line1", "&c"));
config.set("sign-colors.full.line2", defaultConfig.getString("sign-colors.full.line2", "&4"));
config.set("sign-colors.full.line3", defaultConfig.getString("sign-colors.full.line3", "&e"));
config.set("sign-colors.full.line4", defaultConfig.getString("sign-colors.full.line4", "&1")); // Korrigiert auf &1
config.set("sign-colors.full.line4", defaultConfig.getString("sign-colors.full.line4", "&1"));
getLogger().info("Setze Standardwerte für sign-colors.full");
} else {
if (!config.contains("sign-colors.full.line1")) {
@@ -287,7 +435,7 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
config.set("sign-colors.rest.line1", defaultConfig.getString("sign-colors.rest.line1", "&6"));
config.set("sign-colors.rest.line2", defaultConfig.getString("sign-colors.rest.line2", "&0"));
config.set("sign-colors.rest.line3", defaultConfig.getString("sign-colors.rest.line3", "&f"));
config.set("sign-colors.rest.line4", defaultConfig.getString("sign-colors.rest.line4", "&1")); // Korrigiert auf &1
config.set("sign-colors.rest.line4", defaultConfig.getString("sign-colors.rest.line4", "&1"));
getLogger().info("Setze Standardwerte für sign-colors.rest");
} else {
if (!config.contains("sign-colors.rest.line1")) {
@@ -326,7 +474,7 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
getLogger().info("Setze Standardwert: messages.input-chest-set");
}
if (!config.contains("messages.target-chest-set")) {
config.set("messages.target-chest-set", defaultConfig.getString("messages.target-chest-set", "&aZieltruhe für %item% erfolgreich gesetzt!"));
config.set("messages.target-chest-set", defaultConfig.getString("messages.target-chest-set", "&aZieltruhe erfolgreich für %item% eingerichtet!"));
getLogger().info("Setze Standardwert: messages.target-chest-set");
}
// --- NEU: Message für Rest-Truhe ---
@@ -362,44 +510,9 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
}
// ----------------------------------------------
if (!config.contains("messages.help")) {
String helpMessage = "&6&l=== AutoSortChest Hilfe ===\n" +
"&eEingangstruhe erstellen:\n" +
"&f1. Platziere ein Schild an einer Truhe.\n" +
"&f2. Schreibe:\n" +
" &7[asc]\n" +
" &7input\n" +
"&fDein Name wird automatisch in Zeile 4 eingetragen.\n" +
"&eZieltruhe erstellen:\n" +
"&f1. Platziere ein Schild an einer Truhe.\n" +
"&f2. Schreibe:\n" +
" &7[asc]\n" +
" &7ziel\n" +
"&f3. Rechtsklicke mit einem Item in der Hand.\n" +
"&eRest-Truhe (Fallback) erstellen:\n" +
"&f1. Platziere ein Schild an einer Truhe.\n" +
"&f2. Schreibe:\n" +
" &7[asc]\n" +
" &7rest\n" +
"&eBefehle:\n" +
"&f- &b/asc help &f- Zeigt diese Hilfe.\n" +
"&f- &b/asc info &f- Zeigt Plugin-Informationen.\n" +
"&f- &b/asc reload &f- Lädt die Konfiguration neu (OP).\n" +
"&6&l====================";
config.set("messages.help", defaultConfig.getString("messages.help", helpMessage));
getLogger().info("Setze Standardwert: messages.help");
}
if (!config.contains("messages.info")) {
String infoMessage = "&6&l=== AutoSortChest Info ===\n" +
"&ePlugin: &fAutoSortChest\n" +
"&eVersion: &f%version%\n" +
"&eKonfigurationsversion: &f%config_version%\n" +
"&eErsteller: &f%author%\n" +
"&eBeschreibung: &fAutomatisches Sortieren von Items in Truhen.\n" +
"&6&l====================";
config.set("messages.info", defaultConfig.getString("messages.info", infoMessage));
getLogger().info("Setze Standardwert: messages.info");
}
// HILFE UND INFO NICHT MEHR IN CONFIG SPEICHERN!
// Wir benutzen jetzt die statischen Strings HELP_DE, HELP_EN, INFO_DE, INFO_EN aus der Java-Datei.
if (!config.contains("messages.no-permission")) {
config.set("messages.no-permission", defaultConfig.getString("messages.no-permission", "&cDu hast keine Berechtigung für diesen Befehl!"));
getLogger().info("Setze Standardwert: messages.no-permission");
@@ -790,54 +903,76 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
return false;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!command.getName().equalsIgnoreCase("asc")) return false;
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!command.getName().equalsIgnoreCase("asc")) return false;
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "Dieser Befehl ist nur für Spieler!");
return true;
}
Player player = (Player) sender;
if (args.length == 0 || args[0].equalsIgnoreCase("help")) {
String helpMessage = getMessage("help");
player.sendMessage(helpMessage.split("\n"));
return true;
}
if (args[0].equalsIgnoreCase("info")) {
String infoMessage = getMessage("info")
.replace("%version%", getDescription().getVersion())
.replace("%config_version%", config != null ? config.getString("version", CONFIG_VERSION) : CONFIG_VERSION)
.replace("%author%", String.join(", ", getDescription().getAuthors()));
player.sendMessage(infoMessage.split("\n"));
return true;
}
if (args[0].equalsIgnoreCase("reload")) {
if (!player.hasPermission("autosortchest.reload")) {
player.sendMessage(getMessage("no-permission"));
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "Dieser Befehl ist nur für Spieler!");
return true;
}
reloadConfig();
config = getConfig();
loadPlayerData();
updateConfig();
updateExistingSigns();
migrateInputChests(); // ERWEITERUNG: Migration erneut aufrufen
player.sendMessage(getMessage("reload-success"));
getLogger().info("Konfiguration und Spielerdaten neu geladen durch " + player.getName());
return true;
}
String helpMessage = getMessage("help");
Player player = (Player) sender;
// --- NEU: Sprache aus Config lesen ---
String lang = config.getString("language", "de"); // Standard Deutsch
if (lang == null) lang = "de";
if (args.length == 0 || args[0].equalsIgnoreCase("help")) {
// Sprache prüfen und statischen String wählen
String helpMessage = lang.equalsIgnoreCase("en") ? HELP_EN : HELP_DE;
// --- FIX: Farben übersetzen (&6 -> §6) ---
// Bukkit versteht & nicht als Farbe, wir müssen es umwandeln
helpMessage = ChatColor.translateAlternateColorCodes('&', helpMessage);
// ----------------------------------------
player.sendMessage(helpMessage.split("\n"));
return true;
}
if (args[0].equalsIgnoreCase("info")) {
// Sprache prüfen und statischen String wählen
String infoMessage = lang.equalsIgnoreCase("en") ? INFO_EN : INFO_DE;
// Platzhalter ersetzen
infoMessage = infoMessage
.replace("%version%", getDescription().getVersion())
.replace("%config_version%", config != null ? config.getString("version", CONFIG_VERSION) : CONFIG_VERSION)
.replace("%author%", String.join(", ", getDescription().getAuthors()));
// --- FIX: Farben übersetzen (&6 -> §6) ---
infoMessage = ChatColor.translateAlternateColorCodes('&', infoMessage);
// ----------------------------------------
player.sendMessage(infoMessage.split("\n"));
return true;
}
if (args[0].equalsIgnoreCase("reload")) {
if (!player.hasPermission("autosortchest.reload")) {
player.sendMessage(getMessage("no-permission"));
return true;
}
reloadConfig();
config = getConfig();
loadPlayerData();
updateConfig();
updateExistingSigns();
migrateInputChests();
player.sendMessage(getMessage("reload-success"));
getLogger().info("Konfiguration und Spielerdaten neu geladen durch " + player.getName());
return true;
}
// Default: Hilfe zeigen
String helpMessage = lang.equalsIgnoreCase("en") ? HELP_EN : HELP_DE;
helpMessage = ChatColor.translateAlternateColorCodes('&', helpMessage);
player.sendMessage(helpMessage.split("\n"));
return true;
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) // <- Geändert: Priorität erhöht und Cancel ignoriert
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onSignChange(SignChangeEvent event) {
Player player = event.getPlayer();
UUID playerUUID = player.getUniqueId();
@@ -892,14 +1027,14 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
event.setLine(0, getSignColor("input", "line1") + "[asc]");
event.setLine(1, getSignColor("input", "line2") + "input");
event.setLine(3, getSignColor("input", "line4") + player.getName());
setInputChestLocation(playerUUID, chestBlock.getLocation()); // Ruft nun addInputChestLocation auf
setInputChestLocation(playerUUID, chestBlock.getLocation());
player.sendMessage(getMessage("input-chest-set"));
getLogger().info("Eingangstruhe für " + player.getName() + " gesetzt bei " + chestBlock.getLocation());
}
}
@EventHandler
public void onPlayerInteract(org.bukkit.event.player.PlayerInteractEvent event) {
public void onPlayerInteract(PlayerInteractEvent event) {
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return;
Player player = event.getPlayer();
@@ -940,8 +1075,8 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
boolean isOwner = pureOwnerName.equalsIgnoreCase(player.getName());
// 1. FALL: MODUS WECHSELN (Shift + Klick + Leere Hand)
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && player.isSneaking() && (itemInHand == null || itemInHand.getType() == Material.AIR)) {
// 1. FALL: MODUS WECHSELN (Shift + Klick + Leere hand)
if (player.isSneaking() && (itemInHand == null || itemInHand.getType() == Material.AIR)) {
// Admin Check hinzugefügt
if (isOwner || isAdmin(player)) {
boolean isPublic = isChestPublic(sign);
@@ -992,9 +1127,8 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
sign.setLine(0, getSignColor(colorType, "line1") + "[asc]");
sign.setLine(1, getSignColor(colorType, "line2") + "ziel");
sign.setLine(2, getSignColor(colorType, "line3") + itemInHand.getType().name()); // Item wird überschrieben
sign.setLine(2, getSignColor(colorType, "line3") + itemInHand.getType().name());
// Wenn noch kein Name da war, setze ihn
String finalLine4 = line3Raw;
if (pureOwnerName.isEmpty() || pureOwnerName.equalsIgnoreCase("Unknown")) {
finalLine4 = getSignColor("target", "line4") + player.getName();
@@ -1003,15 +1137,6 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
sign.setLine(3, finalLine4);
sign.update();
// Admins ändern Truhe für den Owner? Nein, das ist komplex.
// Wir erlauben Admins nur den Zugriff hier, oder?
// Wenn Admin Item ändert, gehört die Truhe dann ihm?
// Der Code setzt aktuell targetChestLocation für playerUUID.
// Wenn Admin es ändert, sollte er es wohl für den Owner tun lassen oder es übernehmen.
// Um es einfach zu halten: Wenn Admin es ändert, wird es "seine" Zieltruhe für dieses Item.
// (Oder man fragt sich ab, ob man das will. Der User fragte nur nach "schauen" und "entfernen").
// Ich lasse das Zuweisen wie gehabt (Owner Check oben), aber Admin darf es ändern.
setTargetChestLocation(player.getUniqueId(), chestBlock.getLocation(), itemInHand.getType());
player.sendMessage(getMessage("target-chest-set").replace("%item%", itemInHand.getType().name()));
event.setCancelled(true);
@@ -1027,8 +1152,14 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
return;
}
// Wenn Owner oder Öffentlich oder Admin -> Truhe öffnen lassen (Event nicht canceln)
// --- FIX: Zugriff erlaubt -> Event canceln und Truhe öffnen ---
// Dies verhindert das Öffnen des Schild-Editors und erzwingt das Öffnen der Truhe
event.setCancelled(true);
if (chestBlock.getState() instanceof Chest) {
player.openInventory(((Chest) chestBlock.getState()).getInventory());
}
return;
// -----------------------------------------------------------
}
// --- LOGIK FÜR EINGANGSTRUHEN (INPUT) ---
@@ -1071,6 +1202,14 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
event.setCancelled(true);
return;
}
// --- FIX: Zugriff erlaubt -> Event canceln und Truhe öffnen ---
event.setCancelled(true);
if (chestBlock.getState() instanceof Chest) {
player.openInventory(((Chest) chestBlock.getState()).getInventory());
}
return;
// -----------------------------------------------------------
}
}
return;
@@ -1096,7 +1235,7 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
String[] lines = sign.getLines();
if (lines.length >= 2 && ChatColor.stripColor((String) (lines[0] != null ? lines[0] : "")).equalsIgnoreCase("[asc]")) {
signBlock = face;
break outerLoop; // Schild gefunden
break outerLoop;
}
}
}
@@ -1111,7 +1250,7 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
if (line1Clean.equalsIgnoreCase("ziel") || line1Clean.equalsIgnoreCase("rest")) {
String line3Raw = lines[3] != null ? lines[3] : "";
String line3Clean = ChatColor.stripColor(line3Raw);
String pureOwnerName = line3Clean.replace("[public]", "").replace("[Public]", "").trim();
String pureOwnerName = line3Clean.replace("[public]", "").replace("[public]", "").trim();
boolean isOwner = pureOwnerName.equalsIgnoreCase(player.getName());
// 1. FALL: MODUS WECHSELN
@@ -1194,13 +1333,14 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
event.setCancelled(true);
return;
}
// Wenn erlaubt, machen wir nichts, damit die Truhe ganz normal geöffnet wird
}
// FALL: EINGANGSTRUHE
if (line1Clean.equalsIgnoreCase("input")) {
String line3Raw = lines[3] != null ? lines[3] : "";
String line3Clean = ChatColor.stripColor(line3Raw);
String pureOwnerName = line3Clean.replace("[public]", "").replace("[Public]", "").trim();
String pureOwnerName = line3Clean.replace("[Public]", "").replace("[public]", "").trim();
boolean isOwner = pureOwnerName.equalsIgnoreCase(player.getName());
// Moduswechsel
@@ -1237,6 +1377,7 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
event.setCancelled(true);
return;
}
// Wenn erlaubt, lassen wir die Truhe ganz normal öffnen
}
}
}
@@ -1290,7 +1431,7 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
if (lines.length >= 2 && ChatColor.stripColor((String) (lines[0] != null ? lines[0] : "")).equalsIgnoreCase("[asc]") &&
(ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")).equalsIgnoreCase("input") ||
ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")).equalsIgnoreCase("ziel") ||
ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")).equalsIgnoreCase("rest"))) { // REST hinzugefügt
ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")).equalsIgnoreCase("rest"))) {
signBlock = block;
signOwner = ChatColor.stripColor((String) (lines[3] != null ? lines[3] : ""));
if (isDebug()) {
@@ -1317,7 +1458,7 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
if (lines.length >= 2 && ChatColor.stripColor((String) (lines[0] != null ? lines[0] : "")).equalsIgnoreCase("[asc]") &&
(ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")).equalsIgnoreCase("input") ||
ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")).equalsIgnoreCase("ziel") ||
ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")).equalsIgnoreCase("rest"))) { // REST hinzugefügt
ChatColor.stripColor((String) (lines[1] != null ? lines[1] : "")).equalsIgnoreCase("rest"))) {
signBlock = face;
signOwner = ChatColor.stripColor((String) (lines[3] != null ? lines[3] : ""));
isAscChest = true;
@@ -1708,6 +1849,55 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
}
}
// --- NEU: BUNTE PARTIKEL UM DIE TRUHE (NUR ZIELTRUHE) ---
private void spawnTransferParticles(Location start, Location end) {
if (!config.getBoolean("effects.enabled", true)) return;
if (end.getWorld() == null) return;
// 1. Sound abspielen (am Zielort)
if (config.getBoolean("effects.sound", true)) {
end.getWorld().playSound(end, Sound.ENTITY_ITEM_PICKUP, 0.2f, 1.2f);
}
// 2. Bunter Kreis aus Partikeln am Zielort
Location center = end.clone().add(0.5, 0.5, 0.5); // Zentrum der Truhe (etwas erhöht)
// Wir spawnen einen Kreis aus Partikeln
int count = 12; // Anzahl der Partikel im Kreis
double radius = 0.8; // Radius um den Block herum
float size = 1.2f;
// Farbpalette (Rot, Grün, Blau, Gelb, Cyan, Magenta)
Color[] colors = {
Color.fromRGB(255, 0, 0), // Rot
Color.fromRGB(0, 255, 0), // Grün
Color.fromRGB(0, 0, 255), // Blau
Color.fromRGB(255, 255, 0), // Gelb
Color.fromRGB(0, 255, 255), // Cyan
Color.fromRGB(255, 0, 255) // Magenta
};
for (int i = 0; i < count; i++) {
double angle = (2 * Math.PI / count) * i;
double dx = Math.cos(angle) * radius;
double dz = Math.sin(angle) * radius;
// Partikel am Boden oder leicht erhöht
Location particleLoc = center.clone().add(dx, -0.1, dz);
// Zufällige Farbe aus der Liste für "Bunt"
Color color = colors[(int)(Math.random() * colors.length)];
// Wir mischen ein wenig Offset, damit es nicht so perfekt wirkt
double offsetX = (Math.random() - 0.5) * 0.2;
double offsetZ = (Math.random() - 0.5) * 0.2;
DustOptions options = new DustOptions(color, size);
end.getWorld().spawnParticle(Particle.DUST, particleLoc.clone().add(offsetX, 0, offsetZ), 1, 0, 0, 0, 0.02, options);
}
}
// -----------------------------------
private void checkInputChests() {
if (playerData == null) {
getLogger().warning("playerData ist null. Kann Eingangstruhe nicht prüfen.");
@@ -1737,8 +1927,6 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
if (playerData.contains(inputListPath)) {
chestsToCheck.addAll(playerData.getConfigurationSection(inputListPath).getKeys(false));
} else if (playerData.contains(oldInputPath)) {
// Hier führen wir keine Migration durch (nur lesen), da das im laufenden Betrieb zu Problemen führen könnte.
// Wir markieren es für den Task, damit es bei下一次 Update erledigt wird.
Location loc = getLocationFromPath(oldInputPath);
if (loc != null) {
checkSingleInputChest(ownerUUID, loc, "legacy");
@@ -1841,13 +2029,13 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
}
// Wir rufen distributeItemsForOwner auf.
distributeItemsForOwner(ownerUUID, ownerPlayer, chest.getInventory(), ownerName);
distributeItemsForOwner(ownerUUID, ownerPlayer, chest.getInventory(), ownerName, location);
return true;
}
// ----------------------------------------------------------------------
// 4. KORRIGIERTE distributeItemsForOwner Methode:
private void distributeItemsForOwner(UUID ownerUUID, Player ownerPlayer, Inventory sourceInventory, String ownerNameOverride) {
private void distributeItemsForOwner(UUID ownerUUID, Player ownerPlayer, Inventory sourceInventory, String ownerNameOverride, Location sourceLocation) {
boolean hasItems = false;
for (ItemStack item : sourceInventory.getContents()) {
if (item != null && item.getType() != Material.AIR) {
@@ -1973,6 +2161,9 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
if (leftover.isEmpty()) {
sourceInventory.setItem(slot, null);
// --- NEU: Effekte (Nur am Zielort) ---
spawnTransferParticles(null, targetChestLocation);
// ------------------------
} else {
if (ownerPlayer != null && canSendFullChestMessage(ownerUUID, item.getType())) {
String message = getMessage("target-chest-full")
@@ -1992,4 +2183,86 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
}
}
}
// --- NEU: Hopper-Automatisierung ---
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onInventoryMoveItem(InventoryMoveItemEvent event) {
Inventory destination = event.getDestination();
// Prüfen, ob das Ziel eine Truhe ist
if (!(destination.getHolder() instanceof Chest)) {
return;
}
final Chest destChest = (Chest) destination.getHolder();
// Wir müssen herausfinden, ob diese Truhe eine Input-Truhe ist.
// Wir scannen die Blöcke der Truhe nach einem [asc] input Schild.
// Unterstützung für Doppeltruhen
List<Block> blocksToScan = new ArrayList<>();
InventoryHolder holder = destination.getHolder();
if (holder instanceof DoubleChest) {
DoubleChest dc = (DoubleChest) holder;
if (dc.getLeftSide() instanceof Chest) blocksToScan.add(((Chest) dc.getLeftSide()).getBlock());
if (dc.getRightSide() instanceof Chest) blocksToScan.add(((Chest) dc.getRightSide()).getBlock());
} else if (holder instanceof Chest) {
blocksToScan.add(destChest.getBlock());
}
UUID ownerUUID = null;
String ownerName = null;
boolean isPublic = false;
outerLoop:
for (Block b : blocksToScan) {
for (Block face : new Block[] {
b.getRelative(1, 0, 0),
b.getRelative(-1, 0, 0),
b.getRelative(0, 0, 1),
b.getRelative(0, 0, -1)
}) {
if (face.getState() instanceof Sign sign && isSignAttachedToChest(face, b)) {
String[] lines = sign.getLines();
String line0 = ChatColor.stripColor((String) (lines[0] != null ? lines[0] : ""));
String line1 = ChatColor.stripColor((String) (lines[1] != null ? lines[1] : ""));
if (line0.equalsIgnoreCase("[asc]") && line1.equalsIgnoreCase("input")) {
// Schild gefunden! Ermittle Owner aus Zeile 4
String line3Raw = lines[3] != null ? lines[3] : "";
String line3Clean = ChatColor.stripColor(line3Raw);
isPublic = line3Clean.toLowerCase().endsWith("[public]");
ownerName = line3Clean.replace(" [Public]", "").replace(" [public]", "").trim();
// Name zu UUID auflösen
OfflinePlayer op = Bukkit.getOfflinePlayer(ownerName);
if (op.hasPlayedBefore()) {
ownerUUID = op.getUniqueId();
}
break outerLoop; // Wir haben alles was wir brauchen
}
}
}
}
// Wenn kein [asc] input Schild oder Owner unbekannt -> Abbruch
if (ownerUUID == null || ownerName == null || ownerName.isEmpty()) {
return;
}
// Schedule Task um sicherzustellen, dass der Hopper das Item fertig platziert hat
final UUID finalOwnerUUID = ownerUUID;
final String finalOwnerName = ownerName;
new BukkitRunnable() {
@Override
public void run() {
// Wir sortieren auch, wenn der Owner Offline ist (Automation)
// ownerPlayer wird übergeben als null, da es keinen aktiven Spielerkontext gibt
distributeItemsForOwner(finalOwnerUUID, null, destChest.getInventory(), finalOwnerName, destChest.getLocation());
}
}.runTaskLater(this, 1L);
}
// -----------------------------------
}