From e5251933e144d000de71eb48261f2d5e9b1ccd4b Mon Sep 17 00:00:00 2001 From: M_Viper Date: Tue, 13 Jan 2026 21:06:39 +0100 Subject: [PATCH] Update from Git Manager GUI --- .../survivalplus/Manager/ShopManager.java | 60 ++-- .../de/viper/survivalplus/SurvivalPlus.java | 124 ++++++- .../commands/AdminToolsCommand.java | 141 ++++++++ .../survivalplus/commands/ClaimCommand.java | 260 +++++++++++--- .../survivalplus/commands/HeadCommand.java | 58 ++++ .../survivalplus/commands/ShopCommand.java | 130 ++++--- .../survivalplus/listeners/ClaimListener.java | 32 ++ .../listeners/HeadDropListener.java | 36 ++ .../listeners/SignShopListener.java | 324 ++++++++++++++++++ .../listeners/VanishListener.java | 88 +++++ src/main/resources/config.yml | 4 + src/main/resources/help.yml | 47 ++- src/main/resources/plugin.yml | 66 +++- 13 files changed, 1242 insertions(+), 128 deletions(-) create mode 100644 src/main/java/de/viper/survivalplus/commands/AdminToolsCommand.java create mode 100644 src/main/java/de/viper/survivalplus/commands/HeadCommand.java create mode 100644 src/main/java/de/viper/survivalplus/listeners/HeadDropListener.java create mode 100644 src/main/java/de/viper/survivalplus/listeners/SignShopListener.java create mode 100644 src/main/java/de/viper/survivalplus/listeners/VanishListener.java diff --git a/src/main/java/de/viper/survivalplus/Manager/ShopManager.java b/src/main/java/de/viper/survivalplus/Manager/ShopManager.java index 7716371..a64d46e 100644 --- a/src/main/java/de/viper/survivalplus/Manager/ShopManager.java +++ b/src/main/java/de/viper/survivalplus/Manager/ShopManager.java @@ -12,16 +12,28 @@ public class ShopManager { private final SurvivalPlus plugin; private final File shopFile; private final FileConfiguration shopConfig; + + // Multiplikatoren für dynamische Preise + private double buyMultiplier; + private double sellMultiplier; public ShopManager(SurvivalPlus plugin) { this.plugin = plugin; this.shopFile = new File(plugin.getDataFolder(), "shop.yml"); - + if (!shopFile.exists()) { plugin.saveResource("shop.yml", false); } - this.shopConfig = YamlConfiguration.loadConfiguration(shopFile); + + // Multiplikatoren laden + loadMultipliers(); + } + + public void loadMultipliers() { + this.buyMultiplier = plugin.getConfig().getDouble("economy.buy-multiplier", 1.05); + this.sellMultiplier = plugin.getConfig().getDouble("economy.sell-multiplier", 0.95); + plugin.getLogger().info("Shop-Multiplikatoren geladen: Buy=" + buyMultiplier + ", Sell=" + sellMultiplier); } public double getCurrentPrice(String itemKey) { @@ -33,22 +45,22 @@ public class ShopManager { } /** - * Spieler kauft Item: Lager sinkt, Preis steigt (Verknappung) + * Spieler kauft Item: Preis steigt */ public boolean buyItem(String itemKey, int amount) { int stock = shopConfig.getInt("items." + itemKey + ".stock", 0); - if (stock < amount) { - return false; + // Fallback auf Base-Price wenn noch keine Transaktion stattfand (sonst wird 1.05 auf 0 gerechnet) + double currentPrice = shopConfig.getDouble("items." + itemKey + ".current-price", 0); + if (currentPrice == 0) { + currentPrice = shopConfig.getDouble("items." + itemKey + ".base-price", 100); } - double price = shopConfig.getDouble("items." + itemKey + ".current-price", 0); + // Lager aktualisieren (optional, oder unendlich) + // shopConfig.set("items." + itemKey + ".stock", stock - amount); - // Lager aktualisieren - shopConfig.set("items." + itemKey + ".stock", stock - amount); - - // Preis erhöhen (Faktor 1.05 = +5%) - double newPrice = price * 1.05; + // Preis erhöhen (Dynamisch) + double newPrice = currentPrice * buyMultiplier; shopConfig.set("items." + itemKey + ".current-price", newPrice); saveShop(); @@ -56,19 +68,20 @@ public class ShopManager { } /** - * Spieler verkauft Item: Lager steigt, Preis sinkt (Überfluss) + * Spieler verkauft Item: Preis sinkt */ public boolean sellItem(String itemKey, int amount) { - // Hier könnte man prüfen, ob der Shop genügend Geld hat (falls nötig) + // Fallback auf Base-Price + double currentPrice = shopConfig.getDouble("items." + itemKey + ".current-price", 0); + if (currentPrice == 0) { + currentPrice = shopConfig.getDouble("items." + itemKey + ".base-price", 100); + } + + // Lager aktualisieren (optional) + // shopConfig.set("items." + itemKey + ".stock", stock + amount); - double price = shopConfig.getDouble("items." + itemKey + ".current-price", 0); - int stock = shopConfig.getInt("items." + itemKey + ".stock", 0); - - // Lager aktualisieren - shopConfig.set("items." + itemKey + ".stock", stock + amount); - - // Preis senken (Faktor 0.95 = -5%) - double newPrice = price * 0.95; + // Preis senken (Dynamisch) + double newPrice = currentPrice * sellMultiplier; shopConfig.set("items." + itemKey + ".current-price", newPrice); saveShop(); @@ -78,7 +91,10 @@ public class ShopManager { public void addOrUpdateItem(String itemKey, double basePrice, int stock) { shopConfig.set("items." + itemKey + ".base-price", basePrice); shopConfig.set("items." + itemKey + ".stock", stock); - shopConfig.set("items." + itemKey + ".current-price", basePrice); + // Wenn der Preis noch nicht existiert (neues Item), setze ihn auf Base-Preis + if (!shopConfig.contains("items." + itemKey + ".current-price")) { + shopConfig.set("items." + itemKey + ".current-price", basePrice); + } saveShop(); } diff --git a/src/main/java/de/viper/survivalplus/SurvivalPlus.java b/src/main/java/de/viper/survivalplus/SurvivalPlus.java index 6532263..72e1ded 100644 --- a/src/main/java/de/viper/survivalplus/SurvivalPlus.java +++ b/src/main/java/de/viper/survivalplus/SurvivalPlus.java @@ -33,6 +33,7 @@ import de.viper.survivalplus.commands.NightCommand; import de.viper.survivalplus.commands.TradeCommand; import de.viper.survivalplus.commands.ReportCommand; import de.viper.survivalplus.Manager.ShopManager; +import de.viper.survivalplus.commands.ShopCommand; import de.viper.survivalplus.commands.HealCommand; import de.viper.survivalplus.Manager.TablistManager; import de.viper.survivalplus.Manager.CommandBlocker; @@ -42,6 +43,19 @@ import de.viper.survivalplus.commands.WarpsCommand; import de.viper.survivalplus.fun.FunChallengeManager; import de.viper.survivalplus.listeners.ChallengeCollectListener; import de.viper.survivalplus.commands.StartFunChallengeCommand; + +// --- NEU: Imports für Heads, Vanish & Shops --- +import de.viper.survivalplus.commands.HeadCommand; +import de.viper.survivalplus.listeners.HeadDropListener; +import de.viper.survivalplus.listeners.VanishListener; +import de.viper.survivalplus.listeners.SignShopListener; + +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; + +import net.milkbowl.vault.economy.Economy; +import net.milkbowl.vault.economy.EconomyResponse; + import java.util.Map; import java.util.UUID; import java.util.HashMap; @@ -71,8 +85,9 @@ import java.io.FileWriter; import java.io.PrintWriter; import java.nio.file.Files; import java.nio.charset.StandardCharsets; - - +import org.bukkit.metadata.MetadataValue; +import org.bukkit.metadata.FixedMetadataValue; +import org.bukkit.plugin.RegisteredServiceProvider; public class SurvivalPlus extends JavaPlugin { @@ -136,6 +151,12 @@ public class SurvivalPlus extends JavaPlugin { private File consoleFile; private PrintWriter consoleWriter; + // --- NEU: Vanish & Economy Support --- + private Map vanishedPlayers = new HashMap<>(); + private Economy economy = null; + private final String VANISH_META_KEY = "vanished"; + // --------------------------------------- + public void reloadTablistConfig() { if (tablistFile == null) tablistFile = new File(getDataFolder(), "tablist.yml"); if (!tablistFile.exists()) { @@ -251,6 +272,19 @@ public class SurvivalPlus extends JavaPlugin { createNicknamesFile(); reloadBlockedCommandsConfig(); loadClaims(); + + // --- NEU: Vault Economy Setup --- + if (Bukkit.getPluginManager().getPlugin("Vault") != null) { + RegisteredServiceProvider rsp = getServer().getServicesManager().getRegistration(Economy.class); + if (rsp != null) { + economy = rsp.getProvider(); + getLogger().info("Vault Economy erfolgreich verbunden."); + } else { + getLogger().warning("Vault Plugin gefunden, aber kein Economy Provider!"); + } + } + // ------------------------------------------------- + PluginManager pluginManager = getServer().getPluginManager(); try { Permission notifyPerm = new Permission("survivalplus.notify", PermissionDefault.OP); @@ -342,9 +376,11 @@ public class SurvivalPlus extends JavaPlugin { pluginManager.registerEvents(lootChestManager, this); getCommand("lootchests").setExecutor(lootChestManager); getCommand("tploot").setExecutor(lootChestManager); + getCommand("vanish").setExecutor(new AdminToolsCommand(this)); + getCommand("freeze").setExecutor(new AdminToolsCommand(this)); getCommand("ride").setExecutor(new RideCommand(this)); getCommand("claim").setExecutor(new ClaimCommand(this)); - // HIER: (this) hinzugefügt, damit der BlockManager die Datei speichern kann + BlockManager blockManager = new BlockManager(this); FileConfiguration config = getConfig(); BackpackRecipe.register(this, langConfig); @@ -354,6 +390,12 @@ public class SurvivalPlus extends JavaPlugin { getCommand("unblock").setExecutor(new UnblockCommand(blockManager, getConfig())); getCommand("blocklist").setExecutor(new BlockListCommand(blockManager, getConfig())); + // --- NEU: NEUE LISTENER & COMMANDS --- + getCommand("head").setExecutor(new HeadCommand(this)); + pluginManager.registerEvents(new HeadDropListener(this), this); + pluginManager.registerEvents(new VanishListener(this), this); + // ------------------------------------ + pluginManager.registerEvents(new ChatBlockListener(blockManager), this); pluginManager.registerEvents(new InventoryClickListener(this), this); pluginManager.registerEvents(sitListener, this); @@ -367,6 +409,7 @@ public class SurvivalPlus extends JavaPlugin { pluginManager.registerEvents(new FirstJoinListener(), this); pluginManager.registerEvents(playerJoinListener, this); pluginManager.registerEvents(new SignColorListener(this), this); + pluginManager.registerEvents(new SignShopListener(this), this); pluginManager.registerEvents(new SleepListener(this), this); pluginManager.registerEvents(new OreAlarmListener(this), this); pluginManager.registerEvents(new MobLeashLimitListener(this, getConfig()), this); @@ -456,7 +499,7 @@ public class SurvivalPlus extends JavaPlugin { getLogger().info("Block-Regeln angewendet: CommandBlocks erlaubt=" + cmdAllowed + ", StructureBlocks erlaubt=" + structAllowed); } - private void ensureVersionAtTop(File file, String version) { + private void ensureVersionAtTop(File file, String version) { try { if (!file.exists()) return; // Datei existiert nicht List lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8); @@ -526,7 +569,6 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver } } - private void removeForbiddenBlocksFromInventories(boolean cmdAllowed, boolean structAllowed) { for (Player p : Bukkit.getOnlinePlayers()) { if (p.hasPermission("survivalplus.notify")) { @@ -567,6 +609,7 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver } } + // === Claims.yml === private void createClaimsFile() { claimsFile = new File(getDataFolder(), "claims.yml"); if (!claimsFile.exists()) { @@ -700,6 +743,30 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver return false; } + // --- NEU: Delete Claim Methode --- + public void deleteClaim(Claim claim) { + String key = getClaimKey(claim); + if (key != null) { + claims.remove(key); + playerClaimCounts.merge(claim.getOwner(), -1, Integer::sum); + saveClaims(); + } + } + + private String getClaimKey(Claim claim) { + for (Map.Entry entry : claims.entrySet()) { + if (entry.getValue() == claim) { + return entry.getKey(); + } + } + return null; + } + // ----------------------------- + + // WICHTIG: Getter für Claim Config für Ban-System + public FileConfiguration getClaimsConfig() { + return claimsConfig; + } // Methoden für nicknames.yml @@ -750,8 +817,8 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver @Override public void onDisable() { if (debugWriter != null) { - debugWriter.close(); - } + debugWriter.close(); + } if (autoClearTaskId != -1) { Bukkit.getScheduler().cancelTask(autoClearTaskId); } @@ -760,15 +827,14 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver saveMobCapConfig(); saveMobAdaptConfig(); // NEU: Adaptive Mob Config speichern - // NEU: NewbieProtection-Daten sichern - if (newbieListener != null) { - newbieListener.saveData(); - } + // NEU: NewbieProtection-Daten sichern + if (newbieListener != null) { + newbieListener.saveData(); + } getLogger().info(getMessage("plugin.disabled")); } - public void saveStats() { if (statsManager != null) { statsManager.saveStats(); @@ -1228,11 +1294,12 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver e.printStackTrace(); } } + public BannerManager getBannerManager() { return bannerManager; - } + } - // Nur Fehler/Exceptions + // Nur Fehler/Exceptions public void log(String msg) { if (getConfig().getBoolean("debug-logging", false) && debugWriter != null) { debugWriter.println("[" + new java.util.Date() + "] " + msg); @@ -1258,4 +1325,33 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver getLogger().info(msg); // Optional: zusätzlich Konsole ausgeben } + // --- NEU: HELFERMETHODEN FÜR NEUE FEATURES --- + + // Vanish + public void setVanished(UUID uuid, boolean vanished) { + if (vanished) { + vanishedPlayers.put(uuid, true); + } else { + vanishedPlayers.remove(uuid); + } + } + + public boolean isVanished(UUID uuid) { + return vanishedPlayers.containsKey(uuid) && vanishedPlayers.get(uuid); + } + + public Map getVanishedPlayers() { + return vanishedPlayers; + } + + // Economy + public Economy getEconomy() { + return economy; + } + + // ProtocolLib Bridge (für Silent Vanish) + public ProtocolManager getProtocolManager() { + return ProtocolLibrary.getProtocolManager(); + } + } \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/commands/AdminToolsCommand.java b/src/main/java/de/viper/survivalplus/commands/AdminToolsCommand.java new file mode 100644 index 0000000..5c1ff4a --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/AdminToolsCommand.java @@ -0,0 +1,141 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; // <--- WICHTIG: Das war der Fehler +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +public class AdminToolsCommand implements CommandExecutor, Listener { + + private final SurvivalPlus plugin; + private final Set vanishedPlayers = new HashSet<>(); + private final Set frozenPlayers = new HashSet<>(); + + public AdminToolsCommand(SurvivalPlus plugin) { + this.plugin = plugin; + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage("Nur Spieler können diesen Befehl nutzen!"); + return true; + } + + Player player = (Player) sender; + FileConfiguration lang = plugin.getLangConfig(); + + // --- VANISH --- + if (command.getName().equalsIgnoreCase("vanish")) { + if (!player.hasPermission("survivalplus.vanish")) { + player.sendMessage(ChatColor.RED + "Keine Berechtigung!"); + return true; + } + + if (vanishedPlayers.contains(player.getUniqueId())) { + // Ent-vanish + for (Player all : Bukkit.getOnlinePlayers()) { + all.showPlayer(plugin, player); + } + player.setPlayerListName(player.getName()); // Name wieder in Tab-Liste anzeigen + vanishedPlayers.remove(player.getUniqueId()); + player.sendMessage(ChatColor.GREEN + "Du bist nun sichtbar."); + } else { + // Vanish + for (Player all : Bukkit.getOnlinePlayers()) { + all.hidePlayer(plugin, player); + } + vanishedPlayers.add(player.getUniqueId()); + player.sendMessage(ChatColor.GREEN + "Du bist nun unsichtbar."); + } + return true; + } + + // --- FREEZE --- + if (command.getName().equalsIgnoreCase("freeze")) { + if (!player.hasPermission("survivalplus.freeze")) { + player.sendMessage(ChatColor.RED + "Keine Berechtigung!"); + return true; + } + + if (args.length == 0) { + player.sendMessage(ChatColor.RED + "Verwendung: /freeze "); + return true; + } + + Player target = Bukkit.getPlayer(args[0]); + if (target == null) { + player.sendMessage(ChatColor.RED + "Spieler nicht gefunden!"); + return true; + } + + if (frozenPlayers.contains(target.getUniqueId())) { + // Ent-freezen + frozenPlayers.remove(target.getUniqueId()); + player.sendMessage(ChatColor.GREEN + target.getName() + " wurde aufgetaut."); + target.sendMessage(ChatColor.GREEN + "Du bist nicht mehr eingefroren."); + } else { + // Einfrieren + frozenPlayers.add(target.getUniqueId()); + player.sendMessage(ChatColor.GREEN + target.getName() + " wurde eingefroren."); + target.sendMessage(ChatColor.RED + "Du wurdest von einem Admin eingefroren! " + ChatColor.BOLD + "Keine Bewegung möglich!"); + } + return true; + } + + return false; + } + + // --- EVENTS --- + + // Verhindert Bewegung wenn eingefroren + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) { + Player player = event.getPlayer(); + // Kopf drehen (YAW/PITCH) ist okay, Blocken wenn Position (X/Y/Z) ändert + if (frozenPlayers.contains(player.getUniqueId())) { + Location from = event.getFrom(); + Location to = event.getTo(); + // Wenn die Koordinaten sich ändern + if (to.getX() != from.getX() || to.getY() != from.getY() || to.getZ() != from.getZ()) { + event.setCancelled(true); + // Optional: Kleine Meldung beim Versuch zu laufen + // player.sendMessage(ChatColor.RED + "Du bist eingefroren!"); + } + } + } + + // Wenn ein Spieler joint: Hide vanished players for him + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + Player joiner = event.getPlayer(); + for (UUID vanishedUUID : vanishedPlayers) { + Player vanishedPlayer = Bukkit.getPlayer(vanishedUUID); + if (vanishedPlayer != null && vanishedPlayer.isOnline()) { + joiner.hidePlayer(plugin, vanishedPlayer); + } + } + } + + // Cleanup wenn ein Spieler geht + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + vanishedPlayers.remove(event.getPlayer().getUniqueId()); + frozenPlayers.remove(event.getPlayer().getUniqueId()); + } +} \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/commands/ClaimCommand.java b/src/main/java/de/viper/survivalplus/commands/ClaimCommand.java index 66a12e4..4a17fad 100644 --- a/src/main/java/de/viper/survivalplus/commands/ClaimCommand.java +++ b/src/main/java/de/viper/survivalplus/commands/ClaimCommand.java @@ -3,15 +3,19 @@ package de.viper.survivalplus.commands; import de.viper.survivalplus.SurvivalPlus; import de.viper.survivalplus.util.Claim; import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; -import java.util.UUID; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.HashMap; +import java.util.UUID; public class ClaimCommand implements CommandExecutor { private SurvivalPlus plugin; @@ -71,7 +75,7 @@ public class ClaimCommand implements CommandExecutor { } switch (args[0].toLowerCase()) { - case "mark": + case "mark": { if (!player.hasPermission("survivalplus.claim.use")) { player.sendMessage(plugin.getMessage("commands.no-permission")); return true; @@ -89,22 +93,13 @@ public class ClaimCommand implements CommandExecutor { player.sendMessage(plugin.getMessage("claim.point2-set").replace("%x%", String.valueOf(location.getBlockX())).replace("%z%", String.valueOf(location.getBlockZ()))); } return true; + } case "unclaim": - if (args.length == 1) { - if (!player.hasPermission("survivalplus.claim.use")) { - player.sendMessage(plugin.getMessage("commands.no-permission")); - return true; - } - Claim claim = plugin.getClaim(player.getLocation()); - if (claim == null || !claim.getOwner().equals(player.getUniqueId())) { - player.sendMessage(plugin.getMessage("claim.not-owner")); - return true; - } - plugin.removeClaim(player.getLocation(), player); - player.sendMessage(plugin.getMessage("claim.unclaimed")); - return true; - } else if (args.length == 2 && player.isOp()) { + case "del": + case "delete": { + // 1. Mass Delete (Admin): /claim unclaim (oder del/delete) + if (args.length == 2 && player.isOp()) { Player target = Bukkit.getPlayer(args[1]); UUID targetUUID; if (target != null) { @@ -127,12 +122,38 @@ public class ClaimCommand implements CommandExecutor { .replace("%player%", args[1])); } return true; - } else { - player.sendMessage(plugin.getMessage("claim.usage")); + } + + // 2. Normaler oder Admin Targeted Delete + Claim claim = plugin.getClaim(player.getLocation()); + if (claim == null) { + player.sendMessage(plugin.getMessage("claim.no-claim-at-location")); return true; } - case "trust": + // Prüfen, ob Spieler Owner ist (oder OP) + if (!claim.getOwner().equals(player.getUniqueId())) { + // NICHT Owner -> Aber Admin? + if (player.isOp()) { + plugin.deleteClaim(claim); + String ownerName = Bukkit.getOfflinePlayer(claim.getOwner()).getName(); + if (ownerName == null) ownerName = "Unbekannt"; + player.sendMessage(ChatColor.GREEN + "Claim wurde entfernt. (Besitzer war: " + ChatColor.YELLOW + ownerName + ChatColor.GREEN + ")"); + return true; + } else { + // Nicht Owner und kein Admin -> Abbruch + player.sendMessage(plugin.getMessage("claim.not-owner")); + return true; + } + } + + // Owner löscht eigenen Claim + plugin.deleteClaim(claim); + player.sendMessage(plugin.getMessage("claim.unclaimed")); + return true; + } + + case "trust": { if (!player.hasPermission("survivalplus.claim.trust")) { player.sendMessage(plugin.getMessage("commands.no-permission")); return true; @@ -146,17 +167,18 @@ public class ClaimCommand implements CommandExecutor { player.sendMessage(plugin.getMessage("commands.player-not-found")); return true; } - Claim claim = plugin.getClaim(player.getLocation()); - if (claim == null || !claim.getOwner().equals(player.getUniqueId())) { + Claim trustClaim = plugin.getClaim(player.getLocation()); + if (trustClaim == null || !trustClaim.getOwner().equals(player.getUniqueId())) { player.sendMessage(plugin.getMessage("claim.not-owner")); return true; } - claim.addTrusted(target.getUniqueId()); + trustClaim.addTrusted(target.getUniqueId()); player.sendMessage(plugin.getMessage("claim.trusted").replace("%player%", args[1])); plugin.saveClaims(); - break; + return true; + } - case "untrust": + case "untrust": { if (!player.hasPermission("survivalplus.claim.trust")) { player.sendMessage(plugin.getMessage("commands.no-permission")); return true; @@ -165,43 +187,145 @@ public class ClaimCommand implements CommandExecutor { player.sendMessage(plugin.getMessage("claim.usage-untrust")); return true; } - target = Bukkit.getPlayer(args[1]); + Player target = Bukkit.getPlayer(args[1]); if (target == null) { player.sendMessage(plugin.getMessage("commands.player-not-found")); return true; } - claim = plugin.getClaim(player.getLocation()); - if (claim == null || !claim.getOwner().equals(player.getUniqueId())) { + Claim trustClaim = plugin.getClaim(player.getLocation()); + if (trustClaim == null || !trustClaim.getOwner().equals(player.getUniqueId())) { player.sendMessage(plugin.getMessage("claim.not-owner")); return true; } - claim.removeTrusted(target.getUniqueId()); + trustClaim.removeTrusted(target.getUniqueId()); player.sendMessage(plugin.getMessage("claim.untrusted").replace("%player%", args[1])); plugin.saveClaims(); - break; + return true; + } - case "info": + // --- NEU: KICK --- + case "kick": { + if (!player.hasPermission("survivalplus.claim.kick")) { + player.sendMessage(ChatColor.RED + "Keine Berechtigung!"); + return true; + } + if (args.length < 2) { + player.sendMessage(ChatColor.RED + "Verwendung: /claim kick "); + return true; + } + Player kickTarget = Bukkit.getPlayer(args[1]); + if (kickTarget == null) { + player.sendMessage(ChatColor.RED + "Spieler nicht gefunden oder offline!"); + return true; + } + Claim currentClaim = plugin.getClaim(kickTarget.getLocation()); + if (currentClaim == null || !currentClaim.getOwner().equals(player.getUniqueId())) { + player.sendMessage(ChatColor.RED + "Du bist hier nicht Besitzer oder kein Claim."); + return true; + } + kickPlayerFromClaim(kickTarget, currentClaim); + player.sendMessage(ChatColor.GREEN + "Spieler " + kickTarget.getName() + " wurde aus dem Claim gekickt!"); + kickTarget.sendMessage(ChatColor.RED + "Du wurdest aus dem Claim geworfen!"); + return true; + } + + // --- NEU: BAN --- + case "ban": { + if (!player.hasPermission("survivalplus.claim.ban")) { + player.sendMessage(ChatColor.RED + "Keine Berechtigung!"); + return true; + } + if (args.length < 2) { + player.sendMessage(ChatColor.RED + "Verwendung: /claim ban "); + return true; + } + + String claimKey = getClaimKeyAtLocation(player.getLocation()); + if (claimKey == null) { + player.sendMessage(ChatColor.RED + "Du stehst in keinem Claim."); + return true; + } + + Player banTarget = Bukkit.getPlayer(args[1]); + UUID banUUID; + if (banTarget != null) { + banUUID = banTarget.getUniqueId(); + Claim banCheck = plugin.getClaim(player.getLocation()); + if (banCheck != null && banCheck.isInside(banTarget.getLocation())) { + kickPlayerFromClaim(banTarget, banCheck); + banTarget.sendMessage(ChatColor.RED + "Du wurdest aus dem Claim geworfen und gebannt!"); + } + } else { + try { + banUUID = UUID.fromString(args[1]); + } catch (IllegalArgumentException e) { + player.sendMessage(ChatColor.RED + "Ungültige UUID oder Spieler nicht online."); + return true; + } + } + + banPlayerInClaim(banUUID, claimKey); + player.sendMessage(ChatColor.GREEN + "Spieler wurde von diesem Claim gebannt."); + return true; + } + + // --- NEU: UNBAN --- + case "unban": { + if (!player.hasPermission("survivalplus.claim.ban")) { + player.sendMessage(ChatColor.RED + "Keine Berechtigung!"); + return true; + } + if (args.length < 2) { + player.sendMessage(ChatColor.RED + "Verwendung: /claim unban "); + return true; + } + Player unbanTarget = Bukkit.getPlayer(args[1]); + UUID unbanUUID; + if (unbanTarget != null) { + unbanUUID = unbanTarget.getUniqueId(); + } else { + try { + unbanUUID = UUID.fromString(args[1]); + } catch (IllegalArgumentException e) { + player.sendMessage(ChatColor.RED + "Ungültige UUID."); + return true; + } + } + + String claimKeyUnban = getClaimKeyAtLocation(player.getLocation()); + if (claimKeyUnban == null) { + player.sendMessage(ChatColor.RED + "Du stehst in keinem Claim."); + return true; + } + + unbanPlayerInClaim(unbanUUID, claimKeyUnban); + player.sendMessage(ChatColor.GREEN + "Spieler wurde für diesen Claim entbannt."); + return true; + } + + case "info": { if (!player.hasPermission("survivalplus.claim.use")) { player.sendMessage(plugin.getMessage("commands.no-permission")); return true; } - claim = plugin.getClaim(player.getLocation()); - if (claim == null) { + Claim infoClaim = plugin.getClaim(player.getLocation()); + if (infoClaim == null) { player.sendMessage(plugin.getMessage("claim.no-claim-at-location")); return true; } - String ownerName = Bukkit.getOfflinePlayer(claim.getOwner()).getName(); + String ownerName = Bukkit.getOfflinePlayer(infoClaim.getOwner()).getName(); if (ownerName == null) { - ownerName = claim.getOwner().toString(); // Fallback to UUID if name not found + ownerName = infoClaim.getOwner().toString(); } player.sendMessage(plugin.getMessage("claim.info") .replace("%owner%", ownerName) - .replace("%world%", claim.getWorldName()) - .replace("%x1%", String.valueOf(claim.getX1())) - .replace("%z1%", String.valueOf(claim.getZ1())) - .replace("%x2%", String.valueOf(claim.getX2())) - .replace("%z2%", String.valueOf(claim.getZ2()))); + .replace("%world%", infoClaim.getWorldName()) + .replace("%x1%", String.valueOf(infoClaim.getX1())) + .replace("%z1%", String.valueOf(infoClaim.getZ1())) + .replace("%x2%", String.valueOf(infoClaim.getX2())) + .replace("%z2%", String.valueOf(infoClaim.getZ2()))); return true; + } default: player.sendMessage(plugin.getMessage("claim.usage")); @@ -223,4 +347,62 @@ public class ClaimCommand implements CommandExecutor { } return removedCount; } + + // --- HILFSMETHODEN --- + private void kickPlayerFromClaim(Player player, Claim claim) { + Location loc = player.getLocation(); + int x = loc.getBlockX(); + int z = loc.getBlockZ(); + int minX = Math.min(claim.getX1(), claim.getX2()); + int maxX = Math.max(claim.getX1(), claim.getX2()); + int minZ = Math.min(claim.getZ1(), claim.getZ2()); + int maxZ = Math.max(claim.getZ1(), claim.getZ2()); + + int distX1 = Math.abs(x - minX); + int distX2 = Math.abs(x - maxX); + int distZ1 = Math.abs(z - minZ); + int distZ2 = Math.abs(z - maxZ); + + int minDist = Math.min(Math.min(distX1, distX2), Math.min(distZ1, distZ2)); + + Location newLoc = loc.clone(); + if (minDist == distX1) { + newLoc.setX(minX - 1); + } else if (minDist == distX2) { + newLoc.setX(maxX + 1); + } else if (minDist == distZ1) { + newLoc.setZ(minZ - 1); + } else { + newLoc.setZ(maxZ + 1); + } + newLoc.setY(newLoc.getY() + 1); + player.teleport(newLoc); + } + + private String getClaimKeyAtLocation(Location loc) { + for (String key : plugin.getClaims().keySet()) { + if (plugin.getClaims().get(key).isInside(loc)) { + return key; + } + } + return null; + } + + private void banPlayerInClaim(UUID uuid, String claimKey) { + FileConfiguration config = plugin.getClaimsConfig(); + List banned = config.getStringList(claimKey + ".banned"); + if (!banned.contains(uuid.toString())) { + banned.add(uuid.toString()); + config.set(claimKey + ".banned", banned); + plugin.saveClaims(); + } + } + + private void unbanPlayerInClaim(UUID uuid, String claimKey) { + FileConfiguration config = plugin.getClaimsConfig(); + List banned = config.getStringList(claimKey + ".banned"); + banned.remove(uuid.toString()); + config.set(claimKey + ".banned", banned); + plugin.saveClaims(); + } } \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/commands/HeadCommand.java b/src/main/java/de/viper/survivalplus/commands/HeadCommand.java new file mode 100644 index 0000000..be3d3a9 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/HeadCommand.java @@ -0,0 +1,58 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SkullMeta; + +public class HeadCommand implements CommandExecutor { + private SurvivalPlus plugin; + + public HeadCommand(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage("Nur Spieler können diesen Befehl nutzen."); + return true; + } + Player player = (Player) sender; + + if (args.length == 0) { + player.sendMessage(plugin.getMessage("commands.player-only")); + return true; + } + + if (!player.hasPermission("survivalplus.head")) { + player.sendMessage(ChatColor.RED + "Keine Berechtigung!"); + return true; + } + + String targetName = args[0]; + OfflinePlayer target = Bukkit.getOfflinePlayer(targetName); + + if (target == null) { + player.sendMessage(ChatColor.RED + "Spieler '" + targetName + "' wurde nie gefunden."); + return true; + } + + ItemStack head = new ItemStack(Material.PLAYER_HEAD); + SkullMeta meta = (SkullMeta) head.getItemMeta(); + meta.setOwningPlayer(target); + meta.setDisplayName(ChatColor.YELLOW + "Kopf von " + targetName); + head.setItemMeta(meta); + + player.getInventory().addItem(head); + player.sendMessage(ChatColor.GREEN + "Du hast den Kopf von " + targetName + " erhalten!"); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/commands/ShopCommand.java b/src/main/java/de/viper/survivalplus/commands/ShopCommand.java index a1fe2b9..79647f0 100644 --- a/src/main/java/de/viper/survivalplus/commands/ShopCommand.java +++ b/src/main/java/de/viper/survivalplus/commands/ShopCommand.java @@ -2,7 +2,7 @@ package de.viper.survivalplus.commands; import de.viper.survivalplus.Manager.ShopManager; import de.viper.survivalplus.SurvivalPlus; -import de.viper.survivalplus.gui.ShopGui; // <--- WICHTIG: Dieser Import fehlte dir +import de.viper.survivalplus.gui.ShopGui; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; @@ -11,15 +11,33 @@ import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.plugin.RegisteredServiceProvider; +import net.milkbowl.vault.economy.Economy; +import net.milkbowl.vault.economy.EconomyResponse; public class ShopCommand implements CommandExecutor { private final ShopManager shopManager; private final SurvivalPlus plugin; + private Economy economy; public ShopCommand(SurvivalPlus plugin) { this.plugin = plugin; this.shopManager = new ShopManager(plugin); + + // Economy laden (für Admin-Verkauf an Server) + try { + if (Bukkit.getPluginManager().isPluginEnabled("Vault")) { + RegisteredServiceProvider rsp = Bukkit.getServicesManager().getRegistration(Economy.class); + if (rsp != null) { + this.economy = rsp.getProvider(); + plugin.getLogger().info("Vault Economy für ShopCommand gefunden."); + } + } + } catch (Exception e) { + // Ignorieren wenn Vault fehlt + } } private String getMessage(String key) { @@ -35,78 +53,102 @@ public class ShopCommand implements CommandExecutor { Player player = (Player) sender; // Shop-GUI öffnen - if (args.length == 0 || (args.length > 0 && args[0].toLowerCase().equals("gui"))) { + if (args.length == 0 || (args.length > 0 && args[0].equalsIgnoreCase("gui"))) { ShopGui shopGui = new ShopGui(plugin, player, shopManager); Bukkit.getPluginManager().registerEvents(shopGui, plugin); player.openInventory(shopGui.getInventory()); return true; } - if (!args[0].toLowerCase().equals("add")) { - sender.sendMessage(getMessage("unknown-subcommand")); - return true; - } + // --- SHOP ADD LOGIK --- + if (args.length >= 4 && args[0].equalsIgnoreCase("add")) { + + // Economy-Check + if (economy == null) { + sender.sendMessage(ChatColor.RED + "Vault Economy ist nicht verfügbar!"); + return true; + } - // --- Neue Logik für /shop add --- - - String itemKey; - double basePrice; - int stock; - - // Fall 1: /shop add (4 Argumente) - if (args.length >= 4) { String materialName = args[1]; + String priceStr = args[2]; + String amountStr = args[3]; + Material mat = Material.matchMaterial(materialName); if (mat == null) { sender.sendMessage(ChatColor.RED + "Das Material '" + materialName + "' wurde nicht gefunden!"); return true; } - itemKey = mat.name().toLowerCase(); + double price; + int amount; + try { - basePrice = Double.parseDouble(args[2]); - stock = Integer.parseInt(args[3]); + price = Double.parseDouble(priceStr); + amount = Integer.parseInt(amountStr); } catch (NumberFormatException e) { sender.sendMessage(ChatColor.RED + "Ungültige Preis oder Bestandszahl! Beispiel: /shop add Diamond 20 64"); return true; } - } - // Fall 2: /shop add (3 Argumente) -> Nimmt Item in der Hand - else if (args.length == 3) { - ItemStack itemInHand = player.getInventory().getItemInMainHand(); - if (itemInHand == null || itemInHand.getType() == Material.AIR) { - sender.sendMessage(ChatColor.RED + "Du musst das Item, das du hinzufügen willst, in der Hand halten!"); - sender.sendMessage(ChatColor.GRAY + "Oder benutze: /shop add "); + if (amount <= 0 || price < 0) { + sender.sendMessage(ChatColor.RED + "Menge und Preis müssen positiv sein."); return true; } - itemKey = itemInHand.getType().name().toLowerCase(); - try { - basePrice = Double.parseDouble(args[1]); - stock = Integer.parseInt(args[2]); - } catch (NumberFormatException e) { - sender.sendMessage(getMessage("number-error")); + // 1. Prüfen: Hat der Spieler die Items? + int hasAmount = countItems(player.getInventory(), mat); + + if (hasAmount < amount) { + sender.sendMessage(ChatColor.RED + "Du hast nicht genug Items! (Benötigt: " + amount + ", Vorhanden: " + hasAmount + ")"); return true; } - } - // Falsche Anzahl an Argumenten - else { - sender.sendMessage(ChatColor.RED + "Falsche Benutzung!"); - sender.sendMessage(ChatColor.GRAY + "Mit Item in Hand: /shop add "); - sender.sendMessage(ChatColor.GRAY + "Mit Namen: /shop add "); + + // 2. Items aus Inventar entfernen + player.getInventory().removeItem(new ItemStack(mat, amount)); + player.updateInventory(); // Wichtig für Client-Update + + // 3. Spieler Geld geben (Verkauf an Server) + double totalCost = price * amount; + EconomyResponse response = economy.depositPlayer(player, totalCost); + + if (response.transactionSuccess()) { + sender.sendMessage(ChatColor.GREEN + "Du hast " + amount + " " + mat.name() + " für " + totalCost + " Coins an den Server verkauft!"); + + // 4. Shop-Config aktualisieren (Lagerbestand erhöhen) + String itemKey = mat.name().toLowerCase(); + shopManager.addOrUpdateItem(itemKey, price, shopManager.getStock(itemKey) + amount); + } else { + sender.sendMessage(ChatColor.RED + "Fehler bei der Auszahlung!"); + return true; + } + return true; + } + + // Fallback für falsche Argumente + if (args.length > 0 && args[0].equalsIgnoreCase("add")) { + sender.sendMessage(ChatColor.RED + "Falsche Benutzung!"); + sender.sendMessage(ChatColor.GRAY + "Mit Item in Hand: /shop add "); + sender.sendMessage(ChatColor.GRAY + "Mit Namen: /shop add "); + return true; } - shopManager.addOrUpdateItem(itemKey, basePrice, stock); - - String msg = getMessage("item-added") - .replace("{item}", itemKey) - .replace("{price}", String.valueOf(basePrice)) - .replace("{stock}", String.valueOf(stock)); - sender.sendMessage(msg); - + sender.sendMessage(ChatColor.RED + "Unbekannter Befehl! Benutze /shop [add|gui]"); return true; } + + /** + * Zählt die Anzahl der Items eines bestimmten Materials im Inventar. + * Nutzt StorageContents, um auch Rüstung/Shulker-Slots zu prüfen. + */ + private int countItems(PlayerInventory inv, Material mat) { + int count = 0; + for (ItemStack item : inv.getStorageContents()) { + if (item != null && item.getType().equals(mat)) { + count += item.getAmount(); + } + } + return count; + } } \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/listeners/ClaimListener.java b/src/main/java/de/viper/survivalplus/listeners/ClaimListener.java index 21555e7..965c260 100644 --- a/src/main/java/de/viper/survivalplus/listeners/ClaimListener.java +++ b/src/main/java/de/viper/survivalplus/listeners/ClaimListener.java @@ -2,7 +2,9 @@ package de.viper.survivalplus.listeners; import de.viper.survivalplus.SurvivalPlus; import de.viper.survivalplus.util.Claim; +import org.bukkit.ChatColor; import org.bukkit.Location; +import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Monster; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -51,6 +53,25 @@ public class ClaimListener implements Listener { Player player = event.getPlayer(); UUID playerId = player.getUniqueId(); Claim currentClaim = plugin.getClaim(event.getTo()); + Location to = event.getTo(); + + // --- NEU: BAN CHECK --- + if (currentClaim != null) { + // Checken ob Spieler in der Ban-Liste der Claims Config steht + String claimKey = getClaimKeyFromLocation(to); + if (claimKey != null) { + FileConfiguration config = plugin.getClaimsConfig(); + if (config.contains(claimKey + ".banned")) { + if (config.getStringList(claimKey + ".banned").contains(playerId.toString())) { + event.setCancelled(true); + player.sendMessage(ChatColor.RED + "Du bist aus diesem Gebiet verbannt!"); + // Optional: Knockback effect + player.teleport(event.getFrom().add(0, 0.2, 0)); + return; + } + } + } + } // Prüfe, ob sich der Claim-Status geändert hat Claim previousClaim = lastClaim.getOrDefault(playerId, null); @@ -97,4 +118,15 @@ public class ClaimListener implements Listener { } } } + + // Helper für ClaimListener (Kopiert, da wir die Methode in Command nicht sehen können) + private String getClaimKeyFromLocation(Location loc) { + // Da wir hier keinen direkten Zugriff auf den Key haben, iterieren wir kurz + for (String key : plugin.getClaims().keySet()) { + if (plugin.getClaims().get(key).isInside(loc)) { + return key; + } + } + return null; + } } \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/listeners/HeadDropListener.java b/src/main/java/de/viper/survivalplus/listeners/HeadDropListener.java new file mode 100644 index 0000000..261039e --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/HeadDropListener.java @@ -0,0 +1,36 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SkullMeta; + +public class HeadDropListener implements Listener { + private SurvivalPlus plugin; + + public HeadDropListener(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onPlayerDeath(PlayerDeathEvent event) { + if (!plugin.getConfig().getBoolean("heads.drop-on-death", true)) { + return; + } + + Player player = event.getEntity(); + ItemStack head = new ItemStack(Material.PLAYER_HEAD); + SkullMeta meta = (SkullMeta) head.getItemMeta(); + meta.setOwningPlayer(player); + meta.setDisplayName(ChatColor.YELLOW + "Kopf von " + player.getName()); + head.setItemMeta(meta); + + event.getDrops().add(head); + } +} \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/listeners/SignShopListener.java b/src/main/java/de/viper/survivalplus/listeners/SignShopListener.java new file mode 100644 index 0000000..26b2489 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/SignShopListener.java @@ -0,0 +1,324 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.SurvivalPlus; +import net.milkbowl.vault.economy.Economy; +import net.milkbowl.vault.economy.EconomyResponse; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Sign; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.SignChangeEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.RegisteredServiceProvider; + +public class SignShopListener implements Listener { + private final SurvivalPlus plugin; + private Economy economy; + + public SignShopListener(SurvivalPlus plugin) { + this.plugin = plugin; + try { + if (plugin.getServer().getPluginManager().isPluginEnabled("Vault")) { + RegisteredServiceProvider rsp = plugin.getServer().getServicesManager().getRegistration(Economy.class); + if (rsp != null) { + this.economy = rsp.getProvider(); + plugin.getLogger().info("Vault Economy für Sign Shops gefunden."); + } else { + plugin.getLogger().warning("Vault Economy Service nicht gefunden!"); + } + } else { + plugin.getLogger().warning("Vault Plugin nicht installiert! Sign Shops funktionieren nicht."); + } + } catch (Exception e) { + plugin.getLogger().warning("Fehler beim Laden der Vault API."); + } + } + + // --- Schild erstellung --- + @EventHandler(priority = EventPriority.HIGHEST) + public void onSignChange(SignChangeEvent event) { + String line0Raw = event.getLine(0); + if (!line0Raw.equalsIgnoreCase("[buy]") && !line0Raw.equalsIgnoreCase("[sell]")) { + return; + } + + Player player = event.getPlayer(); + if (!player.hasPermission("survivalplus.shop.create")) { + player.sendMessage(ChatColor.RED + "Keine Berechtigung, Shops zu erstellen."); + return; + } + + String line2 = event.getLine(1); + String line3 = event.getLine(2); + + String[] parts = line2.split(" "); + if (parts.length < 2) { + player.sendMessage(ChatColor.RED + "Format in Zeile 2 falsch! Beispiel: 64 Diamond"); + event.setCancelled(true); + return; + } + + try { + Integer.parseInt(parts[0]); + if (Material.matchMaterial(parts[1]) == null) { + player.sendMessage(ChatColor.RED + "Das Item '" + parts[1] + "' existiert nicht."); + event.setCancelled(true); + return; + } + Double.parseDouble(line3); + } catch (Exception e) { + player.sendMessage(ChatColor.RED + "Fehler im Format! Benutze: [Buy] \n 64 Diamond \n 500"); + event.setCancelled(true); + return; + } + + // --- NEU: Schild farbig machen --- + if (line0Raw.equalsIgnoreCase("[buy]")) { + event.setLine(0, ChatColor.GREEN + "[BUY]"); + event.setLine(1, ChatColor.WHITE + parts[0] + " " + ChatColor.AQUA + parts[1]); + event.setLine(2, ChatColor.GOLD + line3 + " Coins"); + } else if (line0Raw.equalsIgnoreCase("[sell]")) { + event.setLine(0, ChatColor.RED + "[SELL]"); + event.setLine(1, ChatColor.WHITE + parts[0] + " " + ChatColor.AQUA + parts[1]); + event.setLine(2, ChatColor.GOLD + line3 + " Coins"); + } + + if (event.getLine(3) != null && !event.getLine(3).isEmpty()) { + event.setLine(3, ChatColor.GRAY + event.getLine(3)); + } + } + + // --- Interaktion --- + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerInteract(PlayerInteractEvent event) { + Block block = event.getClickedBlock(); + if (block == null || !(block.getState() instanceof Sign)) { + return; + } + + Sign sign = (Sign) block.getState(); + String line0 = ChatColor.stripColor(sign.getLine(0)); + + if (!line0.equalsIgnoreCase("[buy]") && !line0.equalsIgnoreCase("[sell]")) { + return; + } + + if (economy == null) { + event.getPlayer().sendMessage(ChatColor.RED + "Shop System offline (Vault fehlt)."); + return; + } + + // --- FEATURE: SHIFT+KLICK LOGIK (LÖSCHEN) --- + if (event.getPlayer().isSneaking()) { + Player player = event.getPlayer(); + if (!player.hasPermission("survivalplus.shop.create")) { + player.sendMessage(ChatColor.RED + "Keine Berechtigung, Shops zu entfernen."); + event.setCancelled(true); + return; + } + block.breakNaturally(); + player.sendMessage(ChatColor.GREEN + "Shop entfernt."); + event.setCancelled(true); + return; + } + + // --- ROUTING --- + if (line0.equalsIgnoreCase("[buy]")) { + handleBuy(event, sign); + } else if (line0.equalsIgnoreCase("[sell]")) { + handleSell(event, sign); + } + } + + // --- KAUFS LOGIK (ROBUST) --- + private void handleBuy(PlayerInteractEvent event, Sign sign) { + event.setCancelled(true); // Interaktion stoppen + + Player player = event.getPlayer(); + String line1Raw = ChatColor.stripColor(sign.getLine(1)); // "64 Diamond" + String line2Raw = ChatColor.stripColor(sign.getLine(2)); // "500" oder "500 Coins" + + // 1. Validierung: Zeilen leer? + if (line1Raw == null || line1Raw.isEmpty()) { + player.sendMessage(ChatColor.RED + "Zeile 1 des Schildes ist leer! (Format: 64 Diamond)"); + return; + } + if (line2Raw == null || line2Raw.isEmpty()) { + player.sendMessage(ChatColor.RED + "Preiszeile ist leer! (Format: 500)"); + return; + } + + // 2. Parsing: Zeile 1 "64 Diamond" + String[] partsRaw = line1Raw.split(" "); + if (partsRaw.length < 2) { + player.sendMessage(ChatColor.RED + "Falsches Format! Benutze: [Buy] \n 64 Diamond"); + return; + } + + String amountStr = partsRaw[0].trim(); // "64" + String itemNameStr = partsRaw[1].trim(); // "Diamond" + + if (amountStr.isEmpty() || itemNameStr.isEmpty()) { + player.sendMessage(ChatColor.RED + "Format Fehler!"); + return; + } + + int amount; + Material material; + try { + amount = Integer.parseInt(amountStr); + material = Material.matchMaterial(itemNameStr); + } catch (NumberFormatException e) { + player.sendMessage(ChatColor.RED + "Angebot '" + amountStr + "' ist keine Zahl! (Beispiel: 64)"); + return; + } + + if (material == null) { + player.sendMessage(ChatColor.RED + "Item '" + itemNameStr + "' existiert nicht!"); + return; + } + + // 3. Parsing: Zeile 2 "500 Coins" (Hier passierte der Fehler!) + String[] priceParts = line2Raw.split(" "); + if (priceParts.length == 0) { + player.sendMessage(ChatColor.RED + "Preiszeile ist leer! (Beispiel: 500)"); + return; + } + + String priceStr = priceParts[0].trim(); + if (priceStr.isEmpty()) { + player.sendMessage(ChatColor.RED + "Kein Preis gefunden! (Beispiel: 500)"); + return; + } + + double price; + try { + price = Double.parseDouble(priceStr); + } catch (NumberFormatException e) { + player.sendMessage(ChatColor.RED + "Preis '" + priceStr + "' ist keine Zahl! (Beispiel: 500)"); + return; + } catch (ArrayIndexOutOfBoundsException e) { + player.sendMessage(ChatColor.RED + "Fehlerhafter Preis! (Beispiel: 500)"); + return; + } + + if (amount <= 0 || price < 0) { + player.sendMessage(ChatColor.RED + "Negative Werte sind nicht erlaubt."); + return; + } + + // 4. Transaktion + try { + ItemStack item = new ItemStack(material, amount); + + if (economy.getBalance(player) >= price) { + EconomyResponse response = economy.withdrawPlayer(player, price); + if (response.transactionSuccess()) { + player.getInventory().addItem(item); + player.sendMessage(ChatColor.GREEN + "Du hast " + amount + " " + material.name().toLowerCase() + " für " + price + " gekauft!"); + } else { + player.sendMessage(ChatColor.RED + "Fehler bei der Transaktion."); + } + } else { + player.sendMessage(ChatColor.RED + "Du hast nicht genug Geld! Du brauchst " + price + "."); + } + } catch (Exception e) { + player.sendMessage(ChatColor.RED + "Ein Fehler ist beim Lesen des Shops aufgetreten."); + // Loggen für Debugging, falls Vault spinnt + e.printStackTrace(); + } + } + + // --- VERKAUFS LOGIK --- + private void handleSell(PlayerInteractEvent event, Sign sign) { + event.setCancelled(true); + + Player player = event.getPlayer(); + String line1Raw = ChatColor.stripColor(sign.getLine(1)); + String line2Raw = ChatColor.stripColor(sign.getLine(2)); + + if (line1Raw == null || line1Raw.isEmpty()) { + player.sendMessage(ChatColor.RED + "Zeile 1 ist leer!"); + return; + } + if (line2Raw == null || line2Raw.isEmpty()) { + player.sendMessage(ChatColor.RED + "Zeile 2 ist leer!"); + return; + } + + String[] partsRaw = line1Raw.split(" "); + if (partsRaw.length < 2) { + player.sendMessage(ChatColor.RED + "Format falsch! Benutze: [Sell] \n 64 Diamond"); + return; + } + + String amountStr = partsRaw[0].trim(); + String itemNameStr = partsRaw[1].trim(); + + int amount; + Material material; + try { + amount = Integer.parseInt(amountStr); + material = Material.matchMaterial(itemNameStr); + } catch (NumberFormatException e) { + player.sendMessage(ChatColor.RED + "Menge ist keine Zahl!"); + return; + } + + if (material == null) { + player.sendMessage(ChatColor.RED + "Item existiert nicht!"); + return; + } + + // Preis Parsing (wie im Kauf, nur sicherer) + String[] priceParts = line2Raw.split(" "); + if (priceParts.length == 0) { + player.sendMessage(ChatColor.RED + "Preiszeile ist leer!"); + return; + } + String priceStr = priceParts[0].trim(); + if (priceStr.isEmpty()) { + player.sendMessage(ChatColor.RED + "Kein Preis!"); + return; + } + + double price; + try { + price = Double.parseDouble(priceStr); + } catch (NumberFormatException e) { + player.sendMessage(ChatColor.RED + "Preis '" + priceStr + "' ist keine Zahl!"); + return; + } + + if (amount <= 0 || price < 0) { + player.sendMessage(ChatColor.RED + "Negativer Wert!"); + return; + } + + ItemStack tempItem = new ItemStack(material, amount); + int hasAmount = 0; + + for (ItemStack item : player.getInventory().getStorageContents()) { + if (item != null && item.isSimilar(tempItem)) { + hasAmount += item.getAmount(); + } + } + + if (hasAmount < amount) { + player.sendMessage(ChatColor.RED + "Du hast nicht genug Items!"); + return; + } + + // Verkauf durchführen + player.getInventory().removeItem(new ItemStack(material, amount)); + economy.depositPlayer(player, price); + player.updateInventory(); + + player.sendMessage(ChatColor.GREEN + "Du hast " + amount + " " + material.name().toLowerCase() + " für " + price + " Coins verkauft!"); + } +} \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/listeners/VanishListener.java b/src/main/java/de/viper/survivalplus/listeners/VanishListener.java new file mode 100644 index 0000000..0266d6a --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/VanishListener.java @@ -0,0 +1,88 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityPickupItemEvent; // NEU: EntityPickupItemEvent +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.events.PacketContainer; + +public class VanishListener implements Listener { + private final SurvivalPlus plugin; + + public VanishListener(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + if (plugin.isVanished(player.getUniqueId())) { + // Join Message ausblenden + event.setJoinMessage(null); + + // Silent Join (Teleport-Effekt) via ProtocolLib + if (player.hasPermission("survivalplus.vanish.silent")) { + hideJoinParticles(player); + } + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerQuit(PlayerQuitEvent event) { + Player player = event.getPlayer(); + if (plugin.isVanished(player.getUniqueId())) { + // Quit Message ausblenden + event.setQuitMessage(null); + } + } + + @EventHandler + public void onTeleport(PlayerTeleportEvent event) { + Player player = event.getPlayer(); + // Silent Teleport verhindert den Endermann-Teleportations-Effekt + if (plugin.isVanished(player.getUniqueId()) && player.hasPermission("survivalplus.vanish.silent")) { + // In 4.8.0 ist das stumme Teleportieren komplex. + // Wir lassen es hier beim Abschalten der Join-Partikel bewenden. + } + } + + @EventHandler + public void onPickup(EntityPickupItemEvent event) { + if (!(event.getEntity() instanceof Player)) return; // Vorsichtshalber prüfen + + Player player = (Player) event.getEntity(); + if (plugin.isVanished(player.getUniqueId()) && player.hasPermission("survivalplus.vanish.no-pickup")) { + event.setCancelled(true); // Item nicht aufheben + } + } + + private void hideJoinParticles(Player player) { + if (!plugin.getServer().getPluginManager().isPluginEnabled("ProtocolLib")) return; + try { + // Direkter Cast auf ProtocolManager ist in 4.8.0 sicherer + ProtocolManager manager = (ProtocolManager) plugin.getServer().getPluginManager().getPlugin("ProtocolLib"); + if (manager == null) return; + + // Packet Container erstellen + PacketContainer packet = manager.createPacket(PacketType.Play.Server.ENTITY_STATUS); + packet.getIntegers().write(0, player.getEntityId()); + packet.getBytes().write(0, (byte) 0x20); // 32 = Invisibel/Unsichtbar Status Client-Side + + for (Player online : plugin.getServer().getOnlinePlayers()) { + if (online.equals(player)) continue; + manager.sendServerPacket(online, packet); + } + } catch (Exception e) { + // Ignorieren Fehler bei ProtocolLib + } + } +} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index c13fb5f..d762d17 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -4,6 +4,10 @@ version: 1.1.0 # Debug-Option debug-logging: false +# Spieler Kopf bei Tod +heads: + drop-on-death: true + # Neulings Schutz newbie-protection: enabled: true diff --git a/src/main/resources/help.yml b/src/main/resources/help.yml index 370250d..b1dbdab 100644 --- a/src/main/resources/help.yml +++ b/src/main/resources/help.yml @@ -137,6 +137,10 @@ commands: description: "&eTeleportiert dich zu einem Freund." usage: "&b/friend tp " + freeze: + description: "&eFriert einen Spieler ein (Bewegung unmöglich)." + usage: "&b/freeze " + ir: description: "&eBenennt das Item in der Hand um." usage: "&b/ir " @@ -201,6 +205,10 @@ commands: description: "&eZeigt eine Liste der blockierten Spieler." usage: "&b/blocklist" + vanish: + description: "&eMacht dich für andere Spieler unsichtbar." + usage: "&b/vanish" + kit: description: "&eHolt das Starterkit." usage: "&b/kit" @@ -213,6 +221,10 @@ commands: description: "&eÄndert deinen Nicknamen mit Farb- und Hex-Support." usage: "&b/nick " + ride: + description: "&eReite einen Spieler oder Mob." + usage: "&b/ride [spieler]" + lootchests: description: "&eZeigt eine Liste aller aktiven Loot-Kisten an. Admins können per Klick teleportieren." usage: "&b/lootchests" @@ -275,7 +287,7 @@ commands: claim: description: "&eVerwaltet Claims für den Anti-Grief-Schutz." - usage: "&b/claim [mark <1|2>| unclaim | trust | untrust ]" + usage: "&b/claim [mark <1|2>| unclaim | trust | untrust | info | kick | ban | unban ]" subcommands: mark: description: "&eMarkiert die erste oder zweite Ecke eines zu schützenden Bereichs." @@ -288,7 +300,19 @@ commands: usage: "&b/claim trust " untrust: description: "&eEntfernt die Vertrauensberechtigung eines Spielers für den Bereich." - usage: "&b/claim untrust " + usage: "&b/claim untrust " + info: + description: "&eZeigt Informationen über den aktuellen Claim an." + usage: "&b/claim info" + kick: + description: "&eWirft einen Spieler aus dem Claim." + usage: "&b/claim kick " + ban: + description: "&eVerbannt einen Spieler permanent aus dem Claim." + usage: "&b/claim ban " + unban: + description: "&eEntbannt einen Spieler aus dem Claim." + usage: "&b/claim unban " messages: header: "&6=== Befehle ===" @@ -309,7 +333,7 @@ messages: name: "&ePlugin-Name: &f" version: "&eVersion: &f" author: "&eErsteller: &f" - description: "&eBeschreibung:\\n&f" + description: "&eBeschreibung:\n&f" footer: "&7==========================" share: preview-title: "&aDeine aktuellen Koordinaten wären:" @@ -320,4 +344,19 @@ messages: cancel-hover: "&cKlicke, um das Senden abzubrechen." sent: "&aKoordinaten gesendet." cancelled: "&eSenden der Koordinaten abgebrochen." - help-not-found: "&cHilfedatei (help.yml) konnte nicht geladen werden!" \ No newline at end of file + help-not-found: "&cHilfedatei (help.yml) konnte nicht geladen werden!" + vanish: + activated: "&aDu bist nun unsichtbar." + deactivated: "&cDu bist nun sichtbar." + freeze: + frozen: "&cDu wurdest von einem Admin eingefroren! &lKeine Bewegung möglich!" + unfrozen: "&aDu bist nicht mehr eingefroren." + player-frozen: "&a%player% wurde eingefroren." + player-unfrozen: "&a%player% wurde aufgetaut." + claim: + kicked: "&cDu wurdest aus dem Claim geworfen!" + kicked-target: "&aSpieler wurde aus dem Claim geworfen." + banned: "&cDu bist aus diesem Gebiet verbannt!" + unbanned: "&aDu bist für dieses Gebiet entbannt." + admin-banned: "&aSpieler wurde von diesem Claim gebannt." + admin-unbanned: "&aSpieler wurde für diesen Claim entbannt." \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 8fde6e8..ebaefdd 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,9 +1,9 @@ name: SurvivalPlus -version: 1.1.1 +version: 1.1.2 main: de.viper.survivalplus.SurvivalPlus api-version: 1.21 -softdepend: [LuckPerms, PlaceholderAPI, ProtocolLib] +softdepend: [LuckPerms, PlaceholderAPI, ProtocolLib, Vault] author: Viper description: A plugin for enhancing survival gameplay in Minecraft. @@ -188,6 +188,16 @@ commands: usage: / [spieler] permission: survivalplus.ride permission-message: "§cDu hast keine Berechtigung, Spieler zu reiten!" + vanish: + description: Macht dich unsichtbar + usage: / + permission: survivalplus.vanish + permission-message: "§cDu hast keine Berechtigung für diesen Befehl." + freeze: + description: Friert einen Spieler ein + usage: / + permission: survivalplus.freeze + permission-message: "§cDu hast keine Berechtigung für diesen Befehl." lootchests: description: Zeigt eine Liste aller aktiven Loot-Kisten an. Admins können per Klick zu einer Kiste teleportieren. usage: / @@ -268,11 +278,19 @@ commands: usage: / [spieler] permission: survivalplus.heal permission-message: "§cDu hast keine Berechtigung, Spieler zu heilen!" - claim: description: Manages claims for anti-griefing - usage: / [unclaim | trust | untrust ] + usage: / [mark|unclaim|del|delete|trust|untrust|info|kick|ban|unban] aliases: [cl] + permission: survivalplus.claim.use + permission-message: "§cDu hast keine Berechtigung für diesen Befehl." + + # --- NEU: HEAD COMMAND --- + head: + description: Hol dir den Kopf eines Spielers. + usage: / + permission: survivalplus.head + permission-message: "§cDu hast keine Berechtigung für diesen Befehl." permissions: survivalplus.*: @@ -326,6 +344,8 @@ permissions: survivalplus.nick: true survivalplus.ride: true survivalplus.ride.exempt: true + survivalplus.vanish: true + survivalplus.freeze: true survivalplus.lootchests: true survivalplus.day: true survivalplus.night: true @@ -346,6 +366,12 @@ permissions: survivalplus.chunkanimals: true survivalplus.claim.use: true survivalplus.claim.trust: true + survivalplus.claim.kick: true + survivalplus.claim.ban: true + survivalplus.head: true + survivalplus.claim.admin: true + survivalplus.vanish.silent: true + survivalplus.vanish.no-pickup: true survivalplus.commandblocker.add: description: Erlaubt das Hinzufügen von Befehlen zur Blockierliste default: op @@ -487,6 +513,12 @@ permissions: survivalplus.ride.exempt: description: Spieler mit dieser Permission können nicht geritten werden default: op + survivalplus.vanish: + description: Erlaubt das Unsichtbar werden + default: op + survivalplus.freeze: + description: Erlaubt das Einfrieren von Spielern + default: op survivalplus.lootchests: description: Erlaubt das Verwalten und Teleportieren zu Loot-Kisten default: op @@ -540,4 +572,28 @@ permissions: default: op survivalplus.chunkanimals: description: Erlaubt das Anzeigen der Anzahl der Tiere im aktuellen Chunk - default: op \ No newline at end of file + default: op + survivalplus.claim.use: + description: Erlaubt das Erstellen von Claims + default: true + survivalplus.claim.trust: + description: Erlaubt das Verwalten von Trusted-Spielern in Claims + default: true + survivalplus.claim.kick: + description: Erlaubt das Kicken von Spielern aus Claims + default: op + survivalplus.claim.ban: + description: Erlaubt das Bannen von Spielern aus Claims + default: op + survivalplus.claim.admin: + description: Erlaubt das Löschen von fremden Claims (Admin Feature) + default: op + survivalplus.vanish.silent: + description: Erlaubt stummes Einloggen + default: op + survivalplus.vanish.no-pickup: + description: Kein Items aufheben im Vanish-Modus + default: op + survivalplus.head: + description: Erlaubt das Holen von Spielerköpfen + default: true \ No newline at end of file