commit 0cec42b44d3b125ecd7da0b9140555fa6f198745 Author: M_Viper Date: Sat Aug 23 03:00:10 2025 +0200 Initial commit of SurvivalPlus plugin diff --git a/Survival Plus.zip b/Survival Plus.zip new file mode 100644 index 0000000..813eae3 Binary files /dev/null and b/Survival Plus.zip differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..c314278 --- /dev/null +++ b/pom.xml @@ -0,0 +1,118 @@ + + + 4.0.0 + + de.viper + SurvivalPlus + 1.0.5-Beta + jar + + SurvivalPlus + + + 17 + 17 + UTF-8 + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + codemc-repo + https://repo.codemc.io/repository/maven-public/ + + + + + + org.spigotmc + spigot-api + 1.21-R0.1-SNAPSHOT + provided + + + + org.bstats + bstats-bukkit + 3.0.2 + compile + + + + me.clip + placeholderapi + 2.11.6 + provided + + + + net.luckperms + luckperms-api + 5.5.10 + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 17 + 17 + + + + org.apache.maven.plugins + maven-shade-plugin + 3.4.1 + + + package + + shade + + + false + + + org.bstats + de.viper.survivalplus.bstats + + + + + + + + + + + src/main/resources + true + + plugin.yml + lang.yml + config.yml + homes.yml + inventory.yml + help.yml + graves.yml + stats.yml + leashes.yml + shop.yml + warps.yml + tablist.yml + + + + + \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/Manager/AFKManager.java b/src/main/java/de/viper/survivalplus/Manager/AFKManager.java new file mode 100644 index 0000000..d8a1061 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/Manager/AFKManager.java @@ -0,0 +1,148 @@ +package de.viper.survivalplus.tasks; + +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarFlag; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.*; + +public class AFKManager { + + private final Plugin plugin; + private final long afkAfterMillis; + private final long kickAfterMillis; + private final long joinGracePeriodMillis = 5000; // 5 Sekunden Schonfrist + + private final Map lastActivity = new HashMap<>(); + private final Map joinTimes = new HashMap<>(); + private final Set afkPlayers = new HashSet<>(); + private final Map bossBars = new WeakHashMap<>(); + private final Map colorIndex = new HashMap<>(); + private final Map colorTasks = new HashMap<>(); + + private final List colorCycle = List.of( + BarColor.RED, BarColor.YELLOW, BarColor.GREEN, + BarColor.BLUE, BarColor.PURPLE, BarColor.PINK + ); + + public AFKManager(Plugin plugin, long afkAfterSeconds, long kickAfterSeconds) { + this.plugin = plugin; + this.afkAfterMillis = afkAfterSeconds * 1000; + this.kickAfterMillis = kickAfterSeconds * 1000; + } + + public void updateActivity(Player player) { + UUID uuid = player.getUniqueId(); + long now = System.currentTimeMillis(); + lastActivity.put(uuid, now); + // Optional: Schonfrist erneuern + joinTimes.put(uuid, now); + if (afkPlayers.remove(uuid)) { + removeAFKBossBar(player); + plugin.getLogger().fine("AFK-Status entfernt für " + player.getName()); + } + } + + public void reset(Player player) { + UUID uuid = player.getUniqueId(); + lastActivity.remove(uuid); + joinTimes.remove(uuid); + afkPlayers.remove(uuid); + removeAFKBossBar(player); + } + + public boolean isAFK(Player player) { + return afkPlayers.contains(player.getUniqueId()); + } + + public void checkAFKStatus(Player player) { + UUID uuid = player.getUniqueId(); + long currentTime = System.currentTimeMillis(); + + // Schonfrist nach Join + if (joinTimes.containsKey(uuid) && (currentTime - joinTimes.get(uuid) < joinGracePeriodMillis)) { + return; + } + + // Kreativ- oder Zuschauermodus ausschließen + if (player.getGameMode() == GameMode.CREATIVE || + player.getGameMode() == GameMode.SPECTATOR) { + return; + } + + if (!lastActivity.containsKey(uuid)) { + lastActivity.put(uuid, currentTime); + return; + } + + long diff = currentTime - lastActivity.get(uuid); + + if (diff >= afkAfterMillis) { + if (afkPlayers.add(uuid)) { + showAFKBossBar(player, "§cDu bist AFK!"); + } + if (kickAfterMillis > 0 && diff >= kickAfterMillis) { + plugin.getLogger().info("Spieler " + player.getName() + " wird wegen AFK gekickt. Inaktiv seit: " + (diff / 1000) + " Sekunden"); + player.kickPlayer("§cDu wurdest wegen AFK gekickt."); + removeAFKBossBar(player); + } + } else if (afkPlayers.remove(uuid)) { + removeAFKBossBar(player); + } + } + + public void showAFKBossBar(Player player, String message) { + UUID uuid = player.getUniqueId(); + removeAFKBossBar(player); // nur eine BossBar pro Spieler + + BossBar bar = Bukkit.createBossBar(message, BarColor.YELLOW, BarStyle.SOLID, BarFlag.CREATE_FOG); + bar.addPlayer(player); + bar.setVisible(true); + + bossBars.put(uuid, bar); + colorIndex.put(uuid, 0); + + // ggf. alten Farbwechsel-Task stoppen + if (colorTasks.containsKey(uuid)) { + colorTasks.get(uuid).cancel(); + colorTasks.remove(uuid); + } + + // Farben wechseln + BukkitRunnable task = new BukkitRunnable() { + @Override + public void run() { + BossBar activeBar = bossBars.get(uuid); + if (activeBar == null) { + this.cancel(); + colorTasks.remove(uuid); + return; + } + int index = colorIndex.getOrDefault(uuid, 0); + activeBar.setColor(colorCycle.get(index)); + colorIndex.put(uuid, (index + 1) % colorCycle.size()); + } + }; + task.runTaskTimer(plugin, 0L, 20L); + colorTasks.put(uuid, task); + } + + public void removeAFKBossBar(Player player) { + UUID uuid = player.getUniqueId(); + if (colorTasks.containsKey(uuid)) { + colorTasks.get(uuid).cancel(); + colorTasks.remove(uuid); + } + BossBar bar = bossBars.remove(uuid); + if (bar != null) { + bar.removeAll(); + } + colorIndex.remove(uuid); + } +} diff --git a/src/main/java/de/viper/survivalplus/Manager/BlockManager.java b/src/main/java/de/viper/survivalplus/Manager/BlockManager.java new file mode 100644 index 0000000..6e0f312 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/Manager/BlockManager.java @@ -0,0 +1,37 @@ +package de.viper.survivalplus.Manager; + +import java.util.*; + +import org.bukkit.entity.Player; + +public class BlockManager { + + private final Map> blockedPlayers = new HashMap<>(); + + public void blockPlayer(Player blocker, Player toBlock) { + blockedPlayers.computeIfAbsent(blocker.getUniqueId(), k -> new HashSet<>()).add(toBlock.getUniqueId()); + } + + public void unblockPlayer(Player blocker, Player toUnblock) { + Set blocked = blockedPlayers.get(blocker.getUniqueId()); + if (blocked != null) { + blocked.remove(toUnblock.getUniqueId()); + if (blocked.isEmpty()) { + blockedPlayers.remove(blocker.getUniqueId()); + } + } +} + + public boolean hasBlocked(Player blocker, Player potentialBlocked) { + return blockedPlayers.getOrDefault(blocker.getUniqueId(), Collections.emptySet()) + .contains(potentialBlocked.getUniqueId()); + } + + public Set getBlockedPlayers(Player player) { + return blockedPlayers.getOrDefault(player.getUniqueId(), Collections.emptySet()); + } + + public void clear(Player player) { + blockedPlayers.remove(player.getUniqueId()); + } +} diff --git a/src/main/java/de/viper/survivalplus/Manager/LootChestManager.java b/src/main/java/de/viper/survivalplus/Manager/LootChestManager.java new file mode 100644 index 0000000..1d48176 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/Manager/LootChestManager.java @@ -0,0 +1,323 @@ +package de.viper.survivalplus.Manager; + +import de.viper.survivalplus.SurvivalPlus; +import net.md_5.bungee.api.ChatColor; +import org.bukkit.*; +import org.bukkit.block.Chest; +import org.bukkit.command.*; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.event.inventory.InventoryOpenEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; + +public class LootChestManager implements Listener, CommandExecutor { + + private final SurvivalPlus plugin; + private final List activeChests = new ArrayList<>(); + private final Map chestSpawnTimes = new HashMap<>(); + private final List lootItems = new ArrayList<>(); + private final Map playerLootCount = new HashMap<>(); + + private final long despawnMillis; + private final int maxActiveChests; + private final int maxLootPerPlayer; + private final long lootLimitResetMillis; + + public LootChestManager(SurvivalPlus plugin) { + this.plugin = plugin; + loadLootFromConfig(); + + FileConfiguration cfg = plugin.getConfig(); + this.despawnMillis = cfg.getLong("lootchest.despawn-minutes", 10) * 60 * 1000; + this.maxActiveChests = cfg.getInt("lootchest.max-active-chests", 10); + this.maxLootPerPlayer = cfg.getInt("lootchest.max-loot-per-player", 3); + this.lootLimitResetMillis = cfg.getLong("lootchest.loot-limit-reset-minutes", 60) * 60 * 1000; + + startChestSpawnTask(); + startChestDespawnTask(); + startLootLimitResetTask(); + } + + private void loadLootFromConfig() { + FileConfiguration cfg = plugin.getConfig(); + lootItems.clear(); + if (cfg.isConfigurationSection("lootchest.items")) { + for (String key : cfg.getConfigurationSection("lootchest.items").getKeys(false)) { + lootItems.add(new LootItem(cfg, "lootchest.items." + key)); + } + } + } + + private void startLootLimitResetTask() { + new BukkitRunnable() { + @Override + public void run() { + playerLootCount.clear(); + } + }.runTaskTimer(plugin, lootLimitResetMillis / 50, lootLimitResetMillis / 50); + } + + private void startChestSpawnTask() { + long intervalTicks = plugin.getConfig().getLong("lootchest.interval-minutes", 30) * 60 * 20; + int spawnCount = plugin.getConfig().getInt("lootchest.spawn-count", 3); + + new BukkitRunnable() { + @Override + public void run() { + if (!plugin.getConfig().getBoolean("lootchest.enabled", true)) return; + clearAllActiveChests(); + for (int i = 0; i < spawnCount; i++) { + spawnRandomLootChest(); + } + Bukkit.broadcastMessage(color( + plugin.getLangConfig().getString("lootchest.spawn-simple", "&aNeue Loot-Kisten sind gespawnt!") + )); + } + }.runTaskTimer(plugin, 0L, intervalTicks); + } + + private void startChestDespawnTask() { + new BukkitRunnable() { + @Override + public void run() { + long now = System.currentTimeMillis(); + Iterator it = activeChests.iterator(); + while (it.hasNext()) { + Location loc = it.next(); + Long spawnTime = chestSpawnTimes.get(loc); + if (spawnTime != null && now - spawnTime >= despawnMillis) { + if (loc.getBlock().getType() == Material.CHEST) { + loc.getBlock().setType(Material.AIR); + } + it.remove(); + chestSpawnTimes.remove(loc); + Bukkit.broadcastMessage(color( + plugin.getLangConfig().getString("lootchest.despawn-msg", + "&eEine Lootkiste ist von selbst verschwunden.") + )); + } + } + } + }.runTaskTimer(plugin, 20L * 60, 20L * 60); + } + + private void clearAllActiveChests() { + for (Location loc : new ArrayList<>(activeChests)) { + if (loc.getBlock().getType() == Material.CHEST) { + loc.getBlock().setType(Material.AIR); + } + } + activeChests.clear(); + chestSpawnTimes.clear(); + } + + private void spawnRandomLootChest() { + if (activeChests.size() >= maxActiveChests) { + return; + } + + World world = Bukkit.getWorld(plugin.getConfig().getString("lootchest.world", "world")); + if (world == null) return; + + int radius = plugin.getConfig().getInt("lootchest.spawn-radius", 500); + ThreadLocalRandom r = ThreadLocalRandom.current(); + + int x = r.nextInt(-radius, radius); + int z = r.nextInt(-radius, radius); + int y = world.getHighestBlockYAt(x, z); + + Location loc = new Location(world, x, y, z); + + // Kein Spawn auf Wasser, Lava, Kelp, Seegras + Material below = world.getBlockAt(x, y - 1, z).getType(); + if (below == Material.WATER || below == Material.LAVA + || below == Material.KELP || below == Material.SEAGRASS + || below == Material.TALL_SEAGRASS) { + return; + } + + if (loc.getBlock().getType() != Material.AIR) { + loc.add(0, 1, 0); + } + + loc.getBlock().setType(Material.CHEST); + Chest chest = (Chest) loc.getBlock().getState(); + fillChestWithRandomLoot(chest.getInventory()); + + activeChests.add(loc); + chestSpawnTimes.put(loc, System.currentTimeMillis()); + + world.spawnParticle(Particle.FIREWORK, loc.clone().add(0.5, 1.0, 0.5), + 20, 0.3, 0.5, 0.3); + world.playSound(loc, Sound.BLOCK_CHEST_OPEN, 1.0f, 1.0f); + } + + private void fillChestWithRandomLoot(Inventory inv) { + inv.clear(); + Random random = new Random(); + for (LootItem lootItem : lootItems) { + if (lootItem.shouldSpawn(random)) { + inv.addItem(lootItem.createStack(random)); + } + } + } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player p)) { + sender.sendMessage(color(plugin.getLangConfig().getString("lootchest.player-only", ""))); + return true; + } + if (!p.hasPermission("survivalplus.lootchests")) { + p.sendMessage(color(plugin.getLangConfig().getString("lootchest.no-permission", ""))); + return true; + } + + if (cmd.getName().equalsIgnoreCase("lootchests")) { + if (activeChests.isEmpty()) { + p.sendMessage(color(plugin.getLangConfig().getString("lootchest.no-chests", ""))); + return true; + } + p.sendMessage(color(plugin.getLangConfig().getString("lootchest.list-header", ""))); + for (Location loc : activeChests) { + String coords = loc.getBlockX() + " " + loc.getBlockY() + " " + loc.getBlockZ(); + net.md_5.bungee.api.chat.TextComponent msg = + new net.md_5.bungee.api.chat.TextComponent(ChatColor.AQUA + " - " + coords + " " + ChatColor.GREEN + "[TP]"); + msg.setClickEvent(new net.md_5.bungee.api.chat.ClickEvent( + net.md_5.bungee.api.chat.ClickEvent.Action.RUN_COMMAND, + "/tploot " + loc.getWorld().getName() + " " + loc.getBlockX() + + " " + loc.getBlockY() + " " + loc.getBlockZ() + )); + msg.setHoverEvent(new net.md_5.bungee.api.chat.HoverEvent( + net.md_5.bungee.api.chat.HoverEvent.Action.SHOW_TEXT, + new net.md_5.bungee.api.chat.ComponentBuilder(color( + plugin.getLangConfig().getString("lootchest.list-tp-hover", "") + )).create() + )); + p.spigot().sendMessage(msg); + } + return true; + } + + if (cmd.getName().equalsIgnoreCase("tploot")) { + if (!p.isOp() && !p.hasPermission("survivalplus.lootchests")) return true; + if (args.length != 4) { + p.sendMessage(color(plugin.getLangConfig().getString("lootchest.tp-usage", ""))); + return true; + } + World w = Bukkit.getWorld(args[0]); + if (w == null) { + p.sendMessage(color(plugin.getLangConfig().getString("lootchest.tp-world-not-found", ""))); + return true; + } + try { + double x = Double.parseDouble(args[1]); + double y = Double.parseDouble(args[2]); + double z = Double.parseDouble(args[3]); + p.teleport(new Location(w, x + 0.5, y, z + 0.5)); + p.sendMessage(color(plugin.getLangConfig().getString("lootchest.tp-success", ""))); + } catch (NumberFormatException e) { + p.sendMessage(color(plugin.getLangConfig().getString("lootchest.tp-invalid-coords", ""))); + } + return true; + } + + return false; + } + + @EventHandler + public void onInventoryOpen(InventoryOpenEvent event) { + if (!(event.getPlayer() instanceof Player player)) return; + InventoryHolder holder = event.getInventory().getHolder(); + if (!(holder instanceof Chest chest)) return; + + if (activeChests.contains(chest.getLocation())) { + UUID uuid = player.getUniqueId(); + int looted = playerLootCount.getOrDefault(uuid, 0); + if (looted >= maxLootPerPlayer) { + player.sendMessage(color(plugin.getLangConfig().getString("lootchest.limit-reached", "&cDu hast dein Loot-Limit erreicht!"))); + event.setCancelled(true); + } + } + } + + @EventHandler + public void onInventoryClose(InventoryCloseEvent event) { + if (!(event.getPlayer() instanceof Player player)) return; + InventoryHolder holder = event.getInventory().getHolder(); + if (!(holder instanceof Chest chest)) return; + + Location chestLoc = chest.getLocation(); + if (activeChests.contains(chestLoc) && isInventoryEmpty(event.getInventory())) { + UUID uuid = player.getUniqueId(); + int looted = playerLootCount.getOrDefault(uuid, 0); + if (looted >= maxLootPerPlayer) return; + playerLootCount.put(uuid, looted + 1); + chestLoc.getBlock().setType(Material.AIR); + activeChests.remove(chestLoc); + chestSpawnTimes.remove(chestLoc); + Bukkit.broadcastMessage(color( + plugin.getLangConfig().getString("lootchest.removed-msg", "&aEine Lootkiste wurde geleert und entfernt.") + )); + } + } + + private boolean isInventoryEmpty(Inventory inventory) { + for (ItemStack item : inventory.getContents()) { + if (item != null && item.getType() != Material.AIR) return false; + } + return true; + } + + private String color(String msg) { + return ChatColor.translateAlternateColorCodes('&', msg == null ? "" : msg); + } + + private static class LootItem { + private final Material material; + private final String name; + private final Map enchantments = new HashMap<>(); + private final double chance; + private final int min; + private final int max; + + LootItem(FileConfiguration cfg, String path) { + material = Material.valueOf(cfg.getString(path + ".material", "STONE")); + name = cfg.getString(path + ".name", null); + chance = cfg.getDouble(path + ".chance", 1.0); + min = cfg.getInt(path + ".min", 1); + max = cfg.getInt(path + ".max", 1); + if (cfg.isConfigurationSection(path + ".enchantments")) { + for (String enchKey : cfg.getConfigurationSection(path + ".enchantments").getKeys(false)) { + Enchantment ench = Enchantment.getByName(enchKey.toUpperCase()); + if (ench != null) { + enchantments.put(ench, cfg.getInt(path + ".enchantments." + enchKey)); + } + } + } + } + boolean shouldSpawn(Random r) { return r.nextDouble() <= chance; } + ItemStack createStack(Random r) { + int amt = min + r.nextInt(max - min + 1); + ItemStack stack = new ItemStack(material, amt); + ItemMeta meta = stack.getItemMeta(); + if (name != null) meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', name)); + stack.setItemMeta(meta); + for (Map.Entry e : enchantments.entrySet()) { + stack.addUnsafeEnchantment(e.getKey(), e.getValue()); + } + return stack; + } + } +} diff --git a/src/main/java/de/viper/survivalplus/Manager/ReportManager.java b/src/main/java/de/viper/survivalplus/Manager/ReportManager.java new file mode 100644 index 0000000..379a5a8 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/Manager/ReportManager.java @@ -0,0 +1,56 @@ +package de.viper.survivalplus.report; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.UUID; + +public class ReportManager { + + private final SurvivalPlus plugin; + private final File reportFile; + private FileConfiguration reportConfig; + + public ReportManager(SurvivalPlus plugin) { + this.plugin = plugin; + this.reportFile = new File(plugin.getDataFolder(), "reports.yml"); + if (!reportFile.exists()) { + try { + reportFile.createNewFile(); + } catch (IOException e) { + plugin.getLogger().severe("Konnte reports.yml nicht erstellen!"); + e.printStackTrace(); + } + } + this.reportConfig = YamlConfiguration.loadConfiguration(reportFile); + } + + public void addReport(String reportedPlayer, String reporter, String reason, String time) { + String path = "reports." + reportedPlayer; + List reports = reportConfig.getStringList(path); + reports.add(time + " - Von: " + reporter + " | Grund: " + reason); + reportConfig.set(path, reports); + saveReports(); + } + + public List getReports(String reportedPlayer) { + return reportConfig.getStringList("reports." + reportedPlayer); + } + + public void clearReports(String reportedPlayer) { + reportConfig.set("reports." + reportedPlayer, null); + saveReports(); + } + + private void saveReports() { + try { + reportConfig.save(reportFile); + } catch (IOException e) { + plugin.getLogger().severe("Fehler beim Speichern der reports.yml: " + e.getMessage()); + } + } +} diff --git a/src/main/java/de/viper/survivalplus/Manager/ShopManager.java b/src/main/java/de/viper/survivalplus/Manager/ShopManager.java new file mode 100644 index 0000000..01ec6b2 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/Manager/ShopManager.java @@ -0,0 +1,70 @@ +package de.viper.survivalplus.Manager; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; + +public class ShopManager { + + private final SurvivalPlus plugin; + private final File shopFile; + private final FileConfiguration shopConfig; + + 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); + } + + public double getCurrentPrice(String itemKey) { + return shopConfig.getDouble("items." + itemKey + ".current-price", 0); + } + + public boolean buyItem(String itemKey, int amount) { + int stock = shopConfig.getInt("items." + itemKey + ".stock", 0); + if (stock < amount) { + return false; + } + double price = shopConfig.getDouble("items." + itemKey + ".current-price", 0); + shopConfig.set("items." + itemKey + ".stock", stock - amount); + shopConfig.set("items." + itemKey + ".current-price", price * 0.95); + saveShop(); + return true; + } + + public void sellItem(String itemKey, int amount) { + int stock = shopConfig.getInt("items." + itemKey + ".stock", 0); + double price = shopConfig.getDouble("items." + itemKey + ".current-price", 0); + shopConfig.set("items." + itemKey + ".stock", stock + amount); + shopConfig.set("items." + itemKey + ".current-price", price * 0.95); + saveShop(); + } + + 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); + saveShop(); + } + + private void saveShop() { + try { + shopConfig.save(shopFile); + } catch (IOException e) { + plugin.getLogger().severe("Fehler beim Speichern von shop.yml"); + e.printStackTrace(); + } + } + + public FileConfiguration getShopConfig() { + return shopConfig; + } +} diff --git a/src/main/java/de/viper/survivalplus/Manager/StatsManager.java b/src/main/java/de/viper/survivalplus/Manager/StatsManager.java new file mode 100644 index 0000000..f6cdde1 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/Manager/StatsManager.java @@ -0,0 +1,92 @@ +package de.viper.survivalplus.Manager; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.UUID; + +public class StatsManager { + + private final SurvivalPlus plugin; + private File statsFile; + private FileConfiguration statsConfig; + + public StatsManager(SurvivalPlus plugin) { + this.plugin = plugin; + createStatsFile(); + } + + private void createStatsFile() { + statsFile = new File(plugin.getDataFolder(), "stats.yml"); + if (!statsFile.exists()) { + plugin.saveResource("stats.yml", false); + } + statsConfig = YamlConfiguration.loadConfiguration(statsFile); + } + + public void saveStats() { + try { + statsConfig.save(statsFile); + } catch (IOException e) { + plugin.getLogger().severe("Fehler beim Speichern der stats.yml: " + e.getMessage()); + } + } + + // Spielzeit in Sekunden addieren + public void addPlayTime(UUID playerUUID, long seconds) { + long current = statsConfig.getLong(playerUUID + ".playtime", 0); + statsConfig.set(playerUUID + ".playtime", current + seconds); + } + + // Kills addieren + public void addKills(UUID playerUUID, int amount) { + int current = statsConfig.getInt(playerUUID + ".kills", 0); + statsConfig.set(playerUUID + ".kills", current + amount); + } + + // Tode addieren + public void addDeaths(UUID playerUUID, int amount) { + int current = statsConfig.getInt(playerUUID + ".deaths", 0); + statsConfig.set(playerUUID + ".deaths", current + amount); + } + + // Abgebaute Blöcke addieren + public void addBlocksBroken(UUID playerUUID, int amount) { + int current = statsConfig.getInt(playerUUID + ".blocks_broken", 0); + statsConfig.set(playerUUID + ".blocks_broken", current + amount); + } + + // Platzierte Blöcke addieren + public void addBlocksPlaced(UUID playerUUID, int amount) { + int current = statsConfig.getInt(playerUUID + ".blocks_placed", 0); + statsConfig.set(playerUUID + ".blocks_placed", current + amount); + } + + // Getter für Statistikwerte + + public long getPlayTime(UUID playerUUID) { + return statsConfig.getLong(playerUUID + ".playtime", 0); + } + + public int getKills(UUID playerUUID) { + return statsConfig.getInt(playerUUID + ".kills", 0); + } + + public int getDeaths(UUID playerUUID) { + return statsConfig.getInt(playerUUID + ".deaths", 0); + } + + public int getBlocksBroken(UUID playerUUID) { + return statsConfig.getInt(playerUUID + ".blocks_broken", 0); + } + + public int getBlocksPlaced(UUID playerUUID) { + return statsConfig.getInt(playerUUID + ".blocks_placed", 0); + } + +} diff --git a/src/main/java/de/viper/survivalplus/Manager/TablistManager.java b/src/main/java/de/viper/survivalplus/Manager/TablistManager.java new file mode 100644 index 0000000..9a3d117 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/Manager/TablistManager.java @@ -0,0 +1,327 @@ +package de.viper.survivalplus.Manager; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; +import me.clip.placeholderapi.PlaceholderAPI; +import net.luckperms.api.LuckPerms; +import net.luckperms.api.LuckPermsProvider; +import net.luckperms.api.model.user.User; + +import java.lang.reflect.Method; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.logging.Level; + +public class TablistManager { + + private final SurvivalPlus plugin; + private final List headerAnim = new ArrayList<>(); + private final List footerAnim = new ArrayList<>(); + private int headerIndex = 0; + private int footerIndex = 0; + private boolean enabled; + private String serverName; + private String website; + private String teamspeakAddress; + private String discordAddress; + private boolean showTeamspeak; + private boolean showDiscord; + private String staffPermission; + private String separatorLine; + private LuckPerms luckPerms; + + public TablistManager(SurvivalPlus plugin) { + this.plugin = plugin; + + // Resource sicherstellen, Config laden + try { plugin.saveResource("tablist.yml", false); } catch (Exception ignored) {} + try { plugin.reloadTablistConfig(); } catch (Throwable ignored) {} + FileConfiguration config = plugin.getTablistConfig(); + + // Konfigurationswerte laden + this.enabled = config.getBoolean("enabled", true); + this.serverName = config.getString("server-name", "SurvivalPlus"); + this.website = config.getString("website", "www.example.com"); + this.teamspeakAddress = config.getString("teamspeak-address", "ts.example.com"); + this.discordAddress = config.getString("discord-address", "discord.gg/example"); + this.showTeamspeak = config.getBoolean("show-teamspeak", true); + this.showDiscord = config.getBoolean("show-discord", true); + this.staffPermission = config.getString("staff-permission", "survivalplus.staff"); + this.separatorLine = config.getString("separator-line", "&8&l&m================"); + + // LuckPerms API initialisieren + try { + this.luckPerms = LuckPermsProvider.get(); + } catch (IllegalStateException e) { + plugin.getLogger().warning("LuckPerms nicht gefunden! Versuche Fallback auf PlaceholderAPI."); + } + + // Prüfen, ob PlaceholderAPI verfügbar ist + if (!Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { + plugin.getLogger().warning("PlaceholderAPI nicht gefunden! Verwende Standard-Prefix als Fallback."); + } + + if (!enabled) { + plugin.getLogger().info("Tablist ist deaktiviert (tablist.yml -> enabled: false)"); + return; + } + + // Header- und Footer-Animationen füllen + List configHeader = config.getStringList("header-animations"); + List configFooter = config.getStringList("footer-animations"); + + if (configHeader != null && !configHeader.isEmpty()) { + headerAnim.addAll(configHeader); + } else { + headerAnim.add("&6&l{server}\n&7Willkommen, &a{player}\n&7Online Player: &e{online}\n&6Online Staff: &e{staff}\n"); + } + + if (configFooter != null && !configFooter.isEmpty()) { + footerAnim.addAll(configFooter); + } else { + footerAnim.add("&7SurvivalPlus &8| &eDein Abenteuer!"); + } + + startAnimation(); + } + + private void startAnimation() { + if (!enabled) return; + + int interval = Math.max(1, plugin.getTablistConfig().getInt("interval-ticks", 20)); + + new BukkitRunnable() { + @Override + public void run() { + if (headerAnim.isEmpty() || footerAnim.isEmpty()) return; + + // Online-Spieler und Staff zählen + int onlinePlayers = Bukkit.getOnlinePlayers().size(); + int onlineStaff = (int) Bukkit.getOnlinePlayers().stream() + .filter(p -> p.hasPermission(staffPermission)) + .count(); + + // Aktuelles Datum und Uhrzeit formatieren + SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss"); + String currentTime = dateFormat.format(new Date()); + + for (Player player : Bukkit.getOnlinePlayers()) { + try { + // Spieler-Prefix abrufen (LuckPerms primär, PlaceholderAPI als Fallback) + String prefix = getPlayerPrefix(player); + int ping = getPlayerPing(player); + // Korrigierte Formatierung: Gesamten String durch color-Methode leiten + String prefixedName = color(prefix + player.getName() + "&8 | &e" + ping + "ms"); + player.setPlayerListName(prefixedName); + + // Header mit Spielername und Statistiken + String headerRaw = headerAnim.get(headerIndex) + .replace("{server}", serverName) + .replace("{player}", player.getName()) + .replace("{online}", String.valueOf(onlinePlayers)) + .replace("{staff}", String.valueOf(onlineStaff)); + String footerRaw = footerAnim.get(footerIndex); + + // Footer zusammenstellen: TS und Discord nebeneinander, Webseite zentriert + StringBuilder footerBuilder = new StringBuilder(); + footerBuilder.append("\n"); // Extra Abstand + footerBuilder.append(color(footerRaw)).append("\n"); + + footerBuilder.append(color(separatorLine)).append("\n"); + if (showTeamspeak || showDiscord) { + StringBuilder socialLine = new StringBuilder(); + if (showTeamspeak) { + socialLine.append("&7TS: &a").append(teamspeakAddress); + } + if (showTeamspeak && showDiscord) { + socialLine.append(" &8| "); + } + if (showDiscord) { + socialLine.append("&7Discord: &d").append(discordAddress); + } + footerBuilder.append(color(socialLine.toString())).append("\n"); + } + footerBuilder.append(" &7Webseite: &b").append(website).append("\n"); + footerBuilder.append(color(separatorLine)).append("\n"); + footerBuilder.append("&7Datum & Uhrzeit: &e").append(currentTime); + + String header = color(headerRaw); + String footer = color(footerBuilder.toString()); + + // 1) Adventure (Component) Variante + boolean done = tryAdventureComponent(player, headerRaw, footerBuilder.toString()); + + // 2) String-Variante fallback + if (!done) { + done = tryStringMethod(player, header, footer); + } + + // 3) Wenn alles fehlschlägt -> Log + if (!done) { + plugin.getLogger().warning("Tablist: Keine geeignete Methode gefunden für Spieler " + player.getName()); + } + } catch (Throwable t) { + plugin.getLogger().log(Level.FINE, "Fehler beim Setzen der Tablist für Spieler " + player.getName(), t); + } + } + + headerIndex = (headerIndex + 1) % headerAnim.size(); + footerIndex = (footerIndex + 1) % footerAnim.size(); + } + }.runTaskTimer(plugin, 0L, interval); + } + + /** + * Spieler-Prefix abrufen (LuckPerms primär, PlaceholderAPI als Fallback) + */ + private String getPlayerPrefix(Player player) { + // Versuche LuckPerms-API zuerst + if (luckPerms != null) { + try { + User user = luckPerms.getPlayerAdapter(Player.class).getUser(player); + String prefix = user.getCachedData().getMetaData().getPrefix(); + return (prefix == null || prefix.isEmpty()) ? "&7[Spieler] " : prefix + " "; + } catch (Exception e) { + plugin.getLogger().log(Level.FINE, "Fehler beim Abrufen des Prefix aus LuckPerms für Spieler " + player.getName(), e); + } + } + + // Fallback auf PlaceholderAPI + if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { + String prefix = PlaceholderAPI.setPlaceholders(player, "%luckperms_prefix%"); + return (prefix == null || prefix.isEmpty()) ? "&7[Spieler] " : prefix + " "; + } + + // Letzter Fallback: Standard-Prefix + return "&7[Spieler] "; + } + + /** + * Spieler-Ping abrufen + */ + private int getPlayerPing(Player player) { + try { + Method getHandle = player.getClass().getMethod("getHandle"); + Object entityPlayer = getHandle.invoke(player); + return entityPlayer.getClass().getField("ping").getInt(entityPlayer); + } catch (Exception e) { + plugin.getLogger().log(Level.FINE, "Fehler beim Abrufen des Pings für Spieler " + player.getName(), e); + return -1; + } + } + + /** + * Adventure-Variante mit Components + */ + private boolean tryAdventureComponent(Player player, String headerRaw, String footerRaw) { + try { + Class compClass = Class.forName("net.kyori.adventure.text.Component"); + Method textMethod = null; + try { + textMethod = compClass.getMethod("text", CharSequence.class); + } catch (NoSuchMethodException ignored) { + try { textMethod = compClass.getMethod("text", String.class); } catch (NoSuchMethodException ignored2) {} + } + + Object headerComp = null; + Object footerComp = null; + + if (textMethod != null) { + headerComp = textMethod.invoke(null, color(headerRaw)); + footerComp = textMethod.invoke(null, color(footerRaw)); + } else { + // Fallback: MiniMessage + try { + Class miniMsgClass = Class.forName("net.kyori.adventure.text.minimessage.MiniMessage"); + Method miniMsgSingleton = miniMsgClass.getMethod("miniMessage"); + Object miniMsg = miniMsgSingleton.invoke(null); + Method deserialize = miniMsgClass.getMethod("deserialize", String.class); + headerComp = deserialize.invoke(miniMsg, headerRaw); + footerComp = deserialize.invoke(miniMsg, footerRaw); + } catch (Throwable t) { + // kein MiniMessage + } + } + + if (headerComp == null || footerComp == null) return false; + + Method m = findMethod(player.getClass(), "sendPlayerListHeaderAndFooter", compClass, compClass); + if (m == null) m = findMethod(player.getClass(), "setPlayerListHeaderFooter", compClass, compClass); + + if (m != null) { + m.invoke(player, headerComp, footerComp); + return true; + } else { + Method mh = findMethod(player.getClass(), "sendPlayerListHeader", compClass); + Method mf = findMethod(player.getClass(), "sendPlayerListFooter", compClass); + if (mh != null && mf != null) { + mh.invoke(player, headerComp); + mf.invoke(player, footerComp); + return true; + } + } + } catch (ClassNotFoundException cnf) { + return false; + } catch (Throwable t) { + plugin.getLogger().log(Level.FINER, "Adventure-Variante fehlgeschlagen: " + t.getMessage()); + return false; + } + return false; + } + + /** + * String-Variante + */ + private boolean tryStringMethod(Player player, String header, String footer) { + try { + Method m = findMethod(player.getClass(), "sendPlayerListHeaderFooter", String.class, String.class); + if (m == null) { + m = findMethod(player.getClass(), "setPlayerListHeaderFooter", String.class, String.class); + } + if (m != null) { + m.invoke(player, header, footer); + return true; + } + + Method mh = findMethod(player.getClass(), "sendPlayerListHeader", String.class); + Method mf = findMethod(player.getClass(), "sendPlayerListFooter", String.class); + if (mh != null && mf != null) { + mh.invoke(player, header); + mf.invoke(player, footer); + return true; + } + } catch (Throwable t) { + plugin.getLogger().log(Level.FINER, "String-Variante fehlgeschlagen: " + t.getMessage()); + return false; + } + return false; + } + + /** + * Hilfsmethode für Reflection + */ + private Method findMethod(Class clazz, String name, Class... paramTypes) { + Class current = clazz; + while (current != null) { + try { + Method m = current.getMethod(name, paramTypes); + if (m != null) return m; + } catch (NoSuchMethodException ignored) { } + current = current.getSuperclass(); + } + return null; + } + + /** + * Farbcode-Konvertierung (& -> §) + */ + private String color(String msg) { + if (msg == null) return ""; + return msg.replace("&", "§"); + } +} \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/Manager/Warp.java b/src/main/java/de/viper/survivalplus/Manager/Warp.java new file mode 100644 index 0000000..e6b863b --- /dev/null +++ b/src/main/java/de/viper/survivalplus/Manager/Warp.java @@ -0,0 +1,44 @@ +package de.viper.survivalplus.Manager; + +import org.bukkit.inventory.ItemStack; + +public class Warp { + + private final String owner; + private final String name; + private final ItemStack item; + private final String worldName; + private final double x, y, z; + + public Warp(String owner, String name, ItemStack item, String worldName, double x, double y, double z) { + this.owner = owner; + this.name = name; + this.item = item; + this.worldName = worldName; + this.x = x; + this.y = y; + this.z = z; + } + + public String getOwner() { + return owner; + } + public String getName() { + return name; + } + public ItemStack getItem() { + return item; + } + public String getWorldName() { + return worldName; + } + public double getX() { + return x; + } + public double getY() { + return y; + } + public double getZ() { + return z; + } +} diff --git a/src/main/java/de/viper/survivalplus/Manager/WarpManager.java b/src/main/java/de/viper/survivalplus/Manager/WarpManager.java new file mode 100644 index 0000000..41078e0 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/Manager/WarpManager.java @@ -0,0 +1,113 @@ +package de.viper.survivalplus.Manager; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class WarpManager { + + private final SurvivalPlus plugin; + private File warpsFile; + private FileConfiguration warpsConfig; + + private final Map warps = new HashMap<>(); // key: owner:name + + public WarpManager(SurvivalPlus plugin) { + this.plugin = plugin; + loadWarpsFile(); + loadWarps(); + } + + private void loadWarpsFile() { + warpsFile = new File(plugin.getDataFolder(), "warps.yml"); + if (!warpsFile.exists()) { + plugin.saveResource("warps.yml", false); + } + warpsConfig = YamlConfiguration.loadConfiguration(warpsFile); + } + + public void saveWarpsFile() { + try { + warpsConfig.save(warpsFile); + } catch (IOException e) { + plugin.getLogger().severe("Konnte warps.yml nicht speichern!"); + e.printStackTrace(); + } + } + + private void loadWarps() { + warps.clear(); + if (warpsConfig.contains("warps")) { + for (String key : warpsConfig.getConfigurationSection("warps").getKeys(false)) { + String path = "warps." + key; + String owner = warpsConfig.getString(path + ".owner"); + String name = warpsConfig.getString(path + ".name"); + ItemStack item = warpsConfig.getItemStack(path + ".item"); + double x = warpsConfig.getDouble(path + ".location.x"); + double y = warpsConfig.getDouble(path + ".location.y"); + double z = warpsConfig.getDouble(path + ".location.z"); + String worldName = warpsConfig.getString(path + ".location.world"); + + Warp warp = new Warp(owner, name, item, worldName, x, y, z); + warps.put(owner + ":" + name, warp); + } + } + } + + public void addWarp(Warp warp) { + warps.put(warp.getOwner() + ":" + warp.getName(), warp); + saveWarpToConfig(warp); + saveWarpsFile(); + } + + private void saveWarpToConfig(Warp warp) { + String path = "warps." + warp.getOwner() + "_" + warp.getName(); + warpsConfig.set(path + ".owner", warp.getOwner()); + warpsConfig.set(path + ".name", warp.getName()); + warpsConfig.set(path + ".item", warp.getItem()); + warpsConfig.set(path + ".location.x", warp.getX()); + warpsConfig.set(path + ".location.y", warp.getY()); + warpsConfig.set(path + ".location.z", warp.getZ()); + warpsConfig.set(path + ".location.world", warp.getWorldName()); + } + + public Map getWarps() { + return warps; + } + + public Warp getWarp(String owner, String name) { + return warps.get(owner + ":" + name); + } + + // Neu hinzugefügt: Anzahl der Warps eines Spielers zählen + public int getWarpCountForPlayer(String playerName) { + int count = 0; + for (Warp warp : warps.values()) { + if (warp.getOwner().equalsIgnoreCase(playerName)) { + count++; + } + } + return count; + } + + // Neu hinzugefügt: Warp eines Spielers löschen + public boolean removeWarp(String owner, String name) { + String key = owner + ":" + name; + if (!warps.containsKey(key)) { + return false; + } + warps.remove(key); + + String path = "warps." + owner + "_" + name; + warpsConfig.set(path, null); // Eintrag in Config entfernen + saveWarpsFile(); + + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/SurvivalPlus.java b/src/main/java/de/viper/survivalplus/SurvivalPlus.java new file mode 100644 index 0000000..9cdf3f1 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/SurvivalPlus.java @@ -0,0 +1,931 @@ +package de.viper.survivalplus; + +import de.viper.survivalplus.commands.*; +import de.viper.survivalplus.listeners.*; +import de.viper.survivalplus.report.ReportManager; +import de.viper.survivalplus.trade.TradeManager; +import de.viper.survivalplus.listeners.DebugArmorStandListener; +import de.viper.survivalplus.listeners.ArmorStandDestroyListener; +import de.viper.survivalplus.tasks.AutoClearTask; +import de.viper.survivalplus.recipe.BackpackRecipe; +import de.viper.survivalplus.Manager.StatsManager; +import de.viper.survivalplus.Manager.BlockManager; +import de.viper.survivalplus.util.LockSystem; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.ArmorStand; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.event.HandlerList; +import de.viper.survivalplus.listeners.RepairSignListener; +import org.bstats.bukkit.Metrics; +import org.bstats.charts.SimplePie; +import org.bstats.charts.SingleLineChart; +import de.viper.survivalplus.listeners.NickLoadListener; +import de.viper.survivalplus.Manager.LootChestManager; +import de.viper.survivalplus.commands.DayCommand; +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.HealCommand; +import de.viper.survivalplus.Manager.TablistManager; + +import de.viper.survivalplus.Manager.WarpManager; +import de.viper.survivalplus.commands.SetWarpCommand; +import de.viper.survivalplus.commands.WarpsCommand; + +import de.viper.survivalplus.fun.FunChallengeManager; +import de.viper.survivalplus.listeners.ChallengeCollectListener; +import de.viper.survivalplus.commands.StartFunChallengeCommand; +import java.util.Map; +import java.util.UUID; +import java.util.HashMap; + +import org.bukkit.NamespacedKey; +import org.bukkit.plugin.PluginManager; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.logging.Level; + +// Zusätzliche Imports für Block-/Permission-Handling +import org.bukkit.GameRule; +import org.bukkit.Material; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; + + +public class SurvivalPlus extends JavaPlugin { + + private File langFile; + private FileConfiguration langConfig; + private File homesFile; + private FileConfiguration homesConfig; + private File gravesFile; + private FileConfiguration gravesConfig; + private File helpFile; + private FileConfiguration helpConfig; + private File backpackFile; + private FileConfiguration backpackConfig; + private File friendsFile; + private FileConfiguration friendsConfig; + private File leashesFile; + private FileConfiguration leashesConfig; + private File mobCapFile; + private FileConfiguration mobCapConfig; + private FileConfiguration tablistConfig; + private File tablistFile; + + private File nicknamesFile; + private FileConfiguration nicknamesConfig; + private SpawnProtectionListener spawnProtectionListener; + private int autoClearTaskId = -1; + private StatsManager statsManager; + private NewbieProtectionListener newbieListener; + + // Listener als Felder speichern + private MobLeashLimitListener mobLeashLimitListener; + private MobCapListener mobCapListener; + private SleepListener sleepListener; + private OreAlarmListener oreAlarmListener; + private AFKListener afkListener; + private GraveListener graveListener; + private SitListener sitListener; + private PlayerJoinListener playerJoinListener; + private FunChallengeManager challengeManager; + private NamespacedKey droppedKey; + private Map progressMap = new HashMap<>(); + + // WarpManager als Feld + private WarpManager warpManager; + + // FunChallengeManager als Feld + private FunChallengeManager funChallengeManager; + + // ------------------- Tablist Config ------------------- + public void reloadTablistConfig() { + // Lädt die tablist.yml aus dem Plugin-Ordner neu + if (tablistFile == null) tablistFile = new File(getDataFolder(), "tablist.yml"); + if (!tablistFile.exists()) { + saveResource("tablist.yml", false); // kopiert die default tablist.yml, falls sie fehlt + } + tablistConfig = YamlConfiguration.loadConfiguration(tablistFile); + } + + public FileConfiguration getTablistConfig() { + // Stellt sicher, dass die Config geladen ist + if (tablistConfig == null) { + reloadTablistConfig(); + } + return tablistConfig; + } + + +@Override +public void onEnable() { + // Default-Config sicherstellen + saveDefaultConfig(); + + // bStats initialisieren + int pluginId = 26886; + Metrics metrics = new Metrics(this, pluginId); + + metrics.addCustomChart(new SimplePie( + "default_gamemode", + () -> Bukkit.getDefaultGameMode().toString() + )); + metrics.addCustomChart(new SingleLineChart( + "online_players", + () -> Bukkit.getOnlinePlayers().size() + )); + metrics.addCustomChart(new SimplePie( + "plugin_language", + () -> getConfig().getString("language", "default") + )); + + // Konfigurationen/Dateien laden + updateConfigFiles(); + createHomesFile(); + createGravesFile(); + createBackpackFile(); + createFriendsFile(); + createLeashesFile(); + createMobCapFile(); + createNicknamesFile(); + + // PluginManager holen (vor Listener-Registrierung!) + PluginManager pluginManager = getServer().getPluginManager(); + + // Permission registrieren: survivalplus.notify für OPs standardmäßig aktiv + try { + Permission notifyPerm = new Permission("survivalplus.notify", PermissionDefault.OP); + if (pluginManager.getPermission("survivalplus.notify") == null) { + pluginManager.addPermission(notifyPerm); + getLogger().info("Permission survivalplus.notify erfolgreich registriert."); + } + } catch (Exception e) { + getLogger().warning("Fehler beim Registrieren der Permission survivalplus.notify: " + e.getMessage()); + } + + // NamespacedKey für Item-Drop-Markierung erzeugen + droppedKey = new NamespacedKey(this, "droppedItem"); + + // FunChallengeManager initialisieren und laden + funChallengeManager = new FunChallengeManager(); + funChallengeManager.load(getConfig()); + + // Listener registrieren mit droppedKey + pluginManager.registerEvents(new ChallengeCollectListener(funChallengeManager, droppedKey), this); + pluginManager.registerEvents(new ChallengeSmeltListener(funChallengeManager), this); + + // FriendCommand + FriendCommand friendCommand = new FriendCommand(this, friendsConfig, langConfig, getLogger()); + + getCommand("day").setExecutor(new DayCommand(this)); + getCommand("night").setExecutor(new NightCommand(this)); + + // TradeManager und ReportManager initialisieren + TradeManager tradeManager = new TradeManager(this); + ReportManager reportManager = new ReportManager(this); + + // Report Commands registrieren + getCommand("report").setExecutor(new ReportCommand(this, reportManager)); + getCommand("showreport").setExecutor(new ShowReportCommand(this, reportManager)); + getCommand("clearreport").setExecutor(new ClearReportCommand(this, reportManager)); + + // Trade Commands registrieren + getCommand("trade").setExecutor(new TradeCommand(this, tradeManager)); + getCommand("tradeaccept").setExecutor(new TradeAcceptCommand(this, tradeManager)); + + // StatsManager initialisieren + statsManager = new StatsManager(this); + + // warpManager initialisieren + warpManager = new WarpManager(this); + + // TablistManager starten + TablistManager tablistManager = new TablistManager(this); + + // Listener-Instanzen + sitListener = new SitListener(this); + afkListener = new AFKListener(this); + graveListener = new GraveListener(this); + playerJoinListener = new PlayerJoinListener(friendCommand); + + // Commands registrieren (Rest) + getCommand("gm").setExecutor(new GamemodeCommand(this)); + getCommand("sp").setExecutor(new PluginCommand(this)); + getCommand("sethome").setExecutor(new HomeCommand(this)); + getCommand("delhome").setExecutor(new HomeCommand(this)); + getCommand("homelist").setExecutor(new HomeCommand(this)); + getCommand("inv").setExecutor(new InventoryCommand(this)); + getCommand("ec").setExecutor(new EnderchestCommand(this)); + getCommand("setspawn").setExecutor(new SetSpawnCommand(this)); + getCommand("setworldspawn").setExecutor(new SetWorldSpawnCommand(this)); + getCommand("clearchat").setExecutor(new ClearChatCommand()); + getCommand("clearitems").setExecutor(new ClearItemsCommand(this)); + getCommand("closedoors").setExecutor(new CloseDoorsCommand(this)); + getCommand("sit").setExecutor(new SitCommand(this, sitListener)); + getCommand("back").setExecutor(new BackCommand(this)); + getCommand("friend").setExecutor(friendCommand); + getCommand("ir").setExecutor(new ItemRenameCommand(this)); + getCommand("showarmorstands").setExecutor(new ShowArmorStandsCommand(this)); + getCommand("cleardebugarmorstands").setExecutor(new ClearDebugArmorStandsCommand(this)); + getCommand("trash").setExecutor(new TrashCommand()); + getCommand("workbench").setExecutor(new WorkbenchCommand()); + getCommand("anvil").setExecutor(new AnvilCommand()); + getCommand("nick").setExecutor(new NickCommand(this)); + getCommand("shop").setExecutor(new ShopCommand(this)); + getCommand("stats").setExecutor(new StatsCommand(this, statsManager)); + getCommand("spawn").setExecutor(new SpawnCommand(this)); + getCommand("setwarp").setExecutor(new SetWarpCommand(this, warpManager)); + getCommand("warps").setExecutor(new WarpsCommand(this, warpManager)); + getCommand("delwarp").setExecutor(new DelWarpCommand(this, warpManager)); + TeleportCommands teleportCommands = new TeleportCommands(this); + getCommand("tp").setExecutor(teleportCommands); + getCommand("tphere").setExecutor(teleportCommands); + getCommand("tpa").setExecutor(teleportCommands); + getCommand("tpaccept").setExecutor(teleportCommands); + getCommand("tpdeny").setExecutor(teleportCommands); + getCommand("startchallenge").setExecutor(new StartFunChallengeCommand(this, funChallengeManager)); + getCommand("kit").setExecutor(new KitCommand(this)); + getCommand("heal").setExecutor(new HealCommand(this)); + + // LootChestManager + Befehle + LootChestManager lootChestManager = new LootChestManager(this); + pluginManager.registerEvents(lootChestManager, this); + getCommand("lootchests").setExecutor(lootChestManager); + getCommand("tploot").setExecutor(lootChestManager); + + // BlockManager + BlockManager blockManager = new BlockManager(); + FileConfiguration config = getConfig(); + + // Listener registrieren + BackpackRecipe.register(this, langConfig); + pluginManager.registerEvents(new ChatBlockListener(blockManager), this); + pluginManager.registerEvents(new InventoryClickListener(this), this); + pluginManager.registerEvents(sitListener, this); + pluginManager.registerEvents(afkListener, this); + pluginManager.registerEvents(graveListener, this); + pluginManager.registerEvents(new BackpackListener(backpackConfig, langConfig, getLogger(), backpackFile), this); + pluginManager.registerEvents(new StatsListener(this, statsManager), this); + pluginManager.registerEvents(new LoginListener(this), this); + pluginManager.registerEvents(new DebugArmorStandListener(), this); + pluginManager.registerEvents(new ArmorStandDestroyListener(), this); + pluginManager.registerEvents(new FirstJoinListener(), this); + pluginManager.registerEvents(playerJoinListener, this); + pluginManager.registerEvents(new SignColorListener(this), this); + pluginManager.registerEvents(new SleepListener(this), this); + pluginManager.registerEvents(new OreAlarmListener(this), this); + pluginManager.registerEvents(new MobLeashLimitListener(this, getConfig()), this); + pluginManager.registerEvents(new MobCapListener(this, getConfig()), this); + pluginManager.registerEvents(new SpawnProtectionListener(this), this); + pluginManager.registerEvents(new NewbieProtectionListener(this), this); + pluginManager.registerEvents(new WarpInventoryListener(this, warpManager), this); + pluginManager.registerEvents(new ChallengeSmeltListener(funChallengeManager), this); + pluginManager.registerEvents(new BlockDetectionListener(this), this); + + LockSystem lockSystem = new LockSystem(this); + pluginManager.registerEvents(lockSystem, this); + getCommand("sp").setExecutor(lockSystem); + + + pluginManager.registerEvents(new RepairSignListener(getConfig(), getLangConfig()), this); + pluginManager.registerEvents(new ToolUpgradeListener(this), this); + pluginManager.registerEvents(new NickLoadListener(this), this); + + // AutoClear starten + startAutoClearTask(); + spawnArmorStandExample(); + getLogger().info(getMessage("plugin.enabled")); + + // === Gamerule keepInventory aus Config setzen === + if (getConfig().getBoolean("set-keepinventory", true)) { + Bukkit.getScheduler().runTaskLater(this, () -> { + if (Bukkit.getWorlds().isEmpty()) return; + + Bukkit.getWorlds().forEach(world -> { + world.setGameRule(GameRule.KEEP_INVENTORY, false); + getLogger().info("Gamerule keepInventory in Welt '" + world.getName() + "' wurde auf false gesetzt."); + }); + }, 20L); + } + + // === Force Survival Mode aktivieren === + if (getConfig().getBoolean("force-survival", true)) { + pluginManager.registerEvents(new ForceSurvivalListener(this), this); + getLogger().info("Force-Survival ist aktiv. Spieler werden beim Joinen in Survival gesetzt."); + } + + // === Animierte Tablist konfigurieren und starten === + try { + // tablist.yml laden / defaults setzen + if (tablistFile == null) tablistFile = new File(getDataFolder(), "tablist.yml"); + if (!tablistFile.exists()) { + saveResource("tablist.yml", false); + } + tablistConfig = YamlConfiguration.loadConfiguration(tablistFile); + + // Standardwerte aus Resource als Defaults setzen (falls vorhanden) + InputStream defStream = getResource("tablist.yml"); + if (defStream != null) { + tablistConfig.setDefaults(YamlConfiguration.loadConfiguration(new InputStreamReader(defStream))); + } + + getLogger().info("Animierte Tablist wurde geladen!"); + } catch (Exception e) { + getLogger().log(Level.WARNING, "Fehler beim Laden der Tablist-Konfiguration", e); + } + + // === Gamerules anwenden (kurze Verzögerung, damit Welten geladen sind) === + Bukkit.getScheduler().runTaskLater(this, this::applyBlockRules, 20L); +} + +/** + * Liest Config und setzt GameRule sowie entfernt verbotene Blöcke aus Inventaren online + */ +public void applyBlockRules() { + boolean cmdAllowed = getConfig().getBoolean("blocks.command-blocks.enabled", true); + boolean structAllowed = getConfig().getBoolean("blocks.structure-blocks.enabled", true); + + for (World world : Bukkit.getWorlds()) { + try { + // moderner API-Aufruf + world.setGameRule(GameRule.COMMAND_BLOCK_OUTPUT, cmdAllowed); + } catch (NoSuchMethodError | NoClassDefFoundError e) { + // Fallback für ältere Server-Implementationen + world.setGameRuleValue(GameRule.COMMAND_BLOCK_OUTPUT.getName(), Boolean.toString(cmdAllowed)); + } + getLogger().info("Gamerule commandBlockOutput in Welt '" + world.getName() + "' gesetzt auf " + cmdAllowed); + } + + // Entferne verbotenes Zeug aus Inventaren aller online-Spieler direkt beim Start + removeForbiddenBlocksFromInventories(cmdAllowed, structAllowed); + + getLogger().info("Block-Regeln angewendet: CommandBlocks erlaubt=" + cmdAllowed + ", StructureBlocks erlaubt=" + structAllowed); +} + +/** Entfernt Command-/Structure-Blöcke aus Inventaren falls deaktiviert, ausgenommen Admins */ +private void removeForbiddenBlocksFromInventories(boolean cmdAllowed, boolean structAllowed) { + for (Player p : Bukkit.getOnlinePlayers()) { + if (p.hasPermission("survivalplus.notify")) { + continue; // Admins dürfen Blöcke behalten + } + Inventory inv = p.getInventory(); + boolean removedCommandBlock = false; + boolean removedStructureBlock = false; + if (!cmdAllowed && inv.contains(Material.COMMAND_BLOCK)) { + inv.remove(Material.COMMAND_BLOCK); + removedCommandBlock = true; + p.sendMessage(ChatColor.RED + "[SurvivalPlus] Command-Blöcke wurden aus deinem Inventar entfernt, da sie deaktiviert sind."); + } + if (!structAllowed && inv.contains(Material.STRUCTURE_BLOCK)) { + inv.remove(Material.STRUCTURE_BLOCK); + removedStructureBlock = true; + p.sendMessage(ChatColor.RED + "[SurvivalPlus] Structure-Blöcke wurden aus deinem Inventar entfernt, da sie deaktiviert sind."); + } + // Benachrichtige Admins über entfernte Blöcke + if ((removedCommandBlock || removedStructureBlock) && getConfig().getBoolean("blocks.notify-admins-on-possession", true)) { + notifyAdmins(p.getName(), removedCommandBlock, removedStructureBlock); + } + } +} + +/** Benachrichtigt Admins über entfernte Blöcke */ +private void notifyAdmins(String playerName, boolean hadCommandBlock, boolean hadStructureBlock) { + String baseTag = ChatColor.GRAY + "[" + ChatColor.GREEN + "SurvivalPlus" + ChatColor.GRAY + "] "; + for (Player admin : Bukkit.getOnlinePlayers()) { + if (admin.hasPermission("survivalplus.notify")) { + if (hadCommandBlock) { + admin.sendMessage(baseTag + ChatColor.RED + "Spieler " + ChatColor.YELLOW + playerName + + ChatColor.RED + " hatte einen Command-Block im Inventar, der entfernt wurde."); + } + if (hadStructureBlock) { + admin.sendMessage(baseTag + ChatColor.RED + "Spieler " + ChatColor.YELLOW + playerName + + ChatColor.RED + " hatte einen Structure-Block im Inventar, der entfernt wurde."); + } + } + } +} + + + + + + + + + + + + + + // Methoden für nicknames.yml + public void createNicknamesFile() { + nicknamesFile = new File(getDataFolder(), "nicknames.yml"); + if (!nicknamesFile.exists()) { + try { + nicknamesFile.getParentFile().mkdirs(); + nicknamesFile.createNewFile(); + } catch (IOException e) { + getLogger().severe("Konnte nicknames.yml nicht erstellen!"); + e.printStackTrace(); + } + } + nicknamesConfig = YamlConfiguration.loadConfiguration(nicknamesFile); + } + + public FileConfiguration getNicknamesConfig() { + return nicknamesConfig; + } + + public void saveNicknamesConfig() { + try { + nicknamesConfig.save(nicknamesFile); + } catch (IOException e) { + getLogger().severe("Konnte nicknames.yml nicht speichern!"); + e.printStackTrace(); + } + } + + private void spawnArmorStandExample() { + World world = Bukkit.getWorld("world"); + if (world == null) { + getLogger().warning("Welt 'world' nicht gefunden, ArmorStand wird nicht gespawnt."); + return; + } + + Location loc = new Location(world, 0.5, 100, 0.5); + ArmorStand armorStand = world.spawn(loc, ArmorStand.class); + armorStand.setVisible(false); + armorStand.setCustomName(ChatColor.GREEN + "Mein ArmorStand"); + armorStand.setCustomNameVisible(true); + armorStand.setGravity(false); + armorStand.setMarker(true); + armorStand.addScoreboardTag("debugArmorStand"); + } + + @Override + public void onDisable() { + if (autoClearTaskId != -1) { + Bukkit.getScheduler().cancelTask(autoClearTaskId); + } + saveStats(); + saveLeashesConfig(); + saveMobCapConfig(); + + // NEU: NewbieProtection-Daten sichern + if (newbieListener != null) { + newbieListener.saveData(); + } + + getLogger().info(getMessage("plugin.disabled")); + } + + + public void saveStats() { + if (statsManager != null) { + statsManager.saveStats(); + } + } + + public String getPluginInfo() { + return ChatColor.translateAlternateColorCodes('&', getMessage("plugin.info")); + } + + // === Config Updating Logic === + private void updateConfigFiles() { + updateConfigFile("config.yml"); + updateConfigFile("lang.yml"); + updateConfigFile("help.yml"); + } + + private void updateConfigFile(String fileName) { + File file = new File(getDataFolder(), fileName); + FileConfiguration currentConfig = YamlConfiguration.loadConfiguration(file); + InputStream defaultStream = getResource(fileName); + + if (defaultStream == null) { + getLogger().warning(fileName + " nicht im JAR gefunden. Erstelle leere Datei."); + if (!file.exists()) { + try { + file.createNewFile(); + if (fileName.equals("config.yml")) { + saveDefaultConfig(); + } else if (fileName.equals("lang.yml")) { + createLangFile(); + } else if (fileName.equals("help.yml")) { + createHelpFile(); + } + } catch (IOException e) { + getLogger().severe("Fehler beim Erstellen der " + fileName + ": " + e.getMessage()); + } + return; + } + } + + FileConfiguration defaultConfig = defaultStream != null ? + YamlConfiguration.loadConfiguration(new InputStreamReader(defaultStream)) : new YamlConfiguration(); + + // Merge default config into current config + mergeConfigs(defaultConfig, currentConfig); + + // Save updated config + try { + currentConfig.save(file); + getLogger().info(fileName + " erfolgreich aktualisiert."); + } catch (IOException e) { + getLogger().severe("Fehler beim Speichern der " + fileName + ": " + e.getMessage()); + } + + // Update instance variables + if (fileName.equals("config.yml")) { + reloadConfig(); + } else if (fileName.equals("lang.yml")) { + langFile = file; + langConfig = currentConfig; + } else if (fileName.equals("help.yml")) { + helpFile = file; + helpConfig = currentConfig; + } + } + + private void mergeConfigs(FileConfiguration defaultConfig, FileConfiguration currentConfig) { + for (String key : defaultConfig.getKeys(true)) { + if (!currentConfig.contains(key)) { + currentConfig.set(key, defaultConfig.get(key)); + } else if (defaultConfig.isConfigurationSection(key) && currentConfig.isConfigurationSection(key)) { + // Rekursiv für Untersektionen + mergeConfigs(defaultConfig.getConfigurationSection(key), currentConfig.getConfigurationSection(key)); + } + } + } + + private void mergeConfigs(ConfigurationSection defaultSection, ConfigurationSection currentSection) { + for (String key : defaultSection.getKeys(false)) { + if (!currentSection.contains(key)) { + currentSection.set(key, defaultSection.get(key)); + } else if (defaultSection.isConfigurationSection(key) && currentSection.isConfigurationSection(key)) { + mergeConfigs(defaultSection.getConfigurationSection(key), currentSection.getConfigurationSection(key)); + } + } + } + + // === Lang.yml === + private void createLangFile() { + langFile = new File(getDataFolder(), "lang.yml"); + if (!langFile.exists()) { + saveResource("lang.yml", false); + } + langConfig = YamlConfiguration.loadConfiguration(langFile); + } + + public String getMessage(String key) { + String message = langConfig.getString(key); + if (message == null) { + getLogger().warning("Fehlender Sprachschlüssel: " + key); + return ChatColor.RED + "[Missing lang key: " + key + "]"; + } + return ChatColor.translateAlternateColorCodes('&', message); + } + + public void reloadLangConfig() { + updateConfigFile("lang.yml"); + } + + public FileConfiguration getLangConfig() { + return langConfig; + } + + // === Homes.yml === + private void createHomesFile() { + homesFile = new File(getDataFolder(), "homes.yml"); + if (!homesFile.exists()) { + try { + homesFile.createNewFile(); + getLogger().info("homes.yml wurde erstellt."); + } catch (IOException e) { + getLogger().severe("Fehler beim Erstellen der homes.yml: " + e.getMessage()); + } + } + homesConfig = YamlConfiguration.loadConfiguration(homesFile); + } + + public FileConfiguration getHomesConfig() { + return homesConfig; + } + + public void saveHomesConfig() { + try { + homesConfig.save(homesFile); + getLogger().info("homes.yml erfolgreich gespeichert."); + } catch (IOException e) { + getLogger().log(Level.SEVERE, "Fehler beim Speichern der homes.yml: " + e.getMessage()); + } + } + + public void reloadHomesConfig() { + homesConfig = YamlConfiguration.loadConfiguration(homesFile); + getLogger().info("homes.yml erfolgreich neu geladen."); + } + + // === Graves.yml === + private void createGravesFile() { + gravesFile = new File(getDataFolder(), "graves.yml"); + if (!gravesFile.exists()) { + try { + gravesFile.createNewFile(); + getLogger().info("graves.yml wurde erstellt."); + } catch (IOException e) { + getLogger().severe("Fehler beim Erstellen der graves.yml: " + e.getMessage()); + } + } + gravesConfig = YamlConfiguration.loadConfiguration(gravesFile); + } + + public FileConfiguration getGravesConfig() { + return gravesConfig; + } + + public void saveGravesConfig() { + try { + gravesConfig.save(gravesFile); + getLogger().info("graves.yml erfolgreich gespeichert."); + } catch (IOException e) { + getLogger().log(Level.SEVERE, "Fehler beim Speichern der graves.yml: " + e.getMessage()); + } + } + + public void reloadGravesConfig() { + gravesConfig = YamlConfiguration.loadConfiguration(gravesFile); + getLogger().info("graves.yml erfolgreich neu geladen."); + } + + // === Help.yml === + private void createHelpFile() { + helpFile = new File(getDataFolder(), "help.yml"); + if (!helpFile.exists()) { + try { + InputStream resource = getResource("help.yml"); + if (resource != null) { + saveResource("help.yml", false); + } else { + getLogger().warning("help.yml nicht im JAR gefunden. Erstelle leere Datei."); + helpConfig = new YamlConfiguration(); + helpConfig.set("header", "§6=== SurvivalPlus Hilfe ==="); + helpConfig.set("footer", "§6====================="); + helpConfig.save(helpFile); + } + } catch (IOException e) { + getLogger().severe("Fehler beim Erstellen der help.yml: " + e.getMessage()); + } + } + helpConfig = YamlConfiguration.loadConfiguration(helpFile); + } + + public FileConfiguration getHelpConfig() { + return helpConfig; + } + + public void reloadHelpConfig() { + updateConfigFile("help.yml"); + } + + // === Backpack.yml === + private void createBackpackFile() { + backpackFile = new File(getDataFolder(), "backpacks.yml"); + if (!backpackFile.exists()) { + try { + backpackFile.createNewFile(); + getLogger().info("backpacks.yml wurde erstellt."); + } catch (IOException e) { + getLogger().severe("Fehler beim Erstellen der backpacks.yml: " + e.getMessage()); + } + } + backpackConfig = YamlConfiguration.loadConfiguration(backpackFile); + } + + public FileConfiguration getBackpackConfig() { + return backpackConfig; + } + + public void saveBackpackConfig() { + try { + backpackConfig.save(backpackFile); + getLogger().info("backpacks.yml erfolgreich gespeichert."); + } catch (IOException e) { + getLogger().log(Level.SEVERE, "Fehler beim Speichern der backpacks.yml: " + e.getMessage()); + } + } + + public void reloadBackpackConfig() { + backpackConfig = YamlConfiguration.loadConfiguration(backpackFile); + getLogger().info("backpacks.yml erfolgreich neu geladen."); + } + + // === Friends.yml === + private void createFriendsFile() { + friendsFile = new File(getDataFolder(), "friends.yml"); + if (!friendsFile.exists()) { + try { + friendsFile.createNewFile(); + getLogger().info("friends.yml wurde erstellt."); + } catch (IOException e) { + getLogger().severe("Fehler beim Erstellen der friends.yml: " + e.getMessage()); + } + } + friendsConfig = YamlConfiguration.loadConfiguration(friendsFile); + } + + public FileConfiguration getFriendsConfig() { + return friendsConfig; + } + + public void saveFriendsConfig() { + try { + friendsConfig.save(friendsFile); + getLogger().info("friends.yml erfolgreich gespeichert."); + } catch (IOException e) { + getLogger().log(Level.SEVERE, "Fehler beim Speichern der friends.yml: " + e.getMessage()); + } + } + + public void reloadFriendsConfig() { + friendsConfig = YamlConfiguration.loadConfiguration(friendsFile); + getLogger().info("friends.yml erfolgreich neu geladen."); + } + + // === Leashes.yml === + private void createLeashesFile() { + leashesFile = new File(getDataFolder(), "leashes.yml"); + if (!leashesFile.exists()) { + try { + leashesFile.createNewFile(); + getLogger().info("leashes.yml wurde erstellt."); + } catch (IOException e) { + getLogger().severe("Fehler beim Erstellen der leashes.yml: " + e.getMessage()); + } + } + leashesConfig = YamlConfiguration.loadConfiguration(leashesFile); + } + + public FileConfiguration getLeashesConfig() { + return leashesConfig; + } + + public void saveLeashesConfig() { + try { + leashesConfig.save(leashesFile); + getLogger().info("leashes.yml erfolgreich gespeichert."); + } catch (IOException e) { + getLogger().log(Level.SEVERE, "Fehler beim Speichern der leashes.yml: " + e.getMessage()); + } + } + + public void reloadLeashesConfig() { + leashesConfig = YamlConfiguration.loadConfiguration(leashesFile); + getLogger().info("leashes.yml erfolgreich neu geladen."); + } + + // === MobCap.yml === + private void createMobCapFile() { + mobCapFile = new File(getDataFolder(), "mobcap.yml"); + if (!mobCapFile.exists()) { + try { + mobCapFile.createNewFile(); + getLogger().info("mobcap.yml wurde erstellt."); + } catch (IOException e) { + getLogger().severe("Fehler beim Erstellen der mobcap.yml: " + e.getMessage()); + } + } + mobCapConfig = YamlConfiguration.loadConfiguration(mobCapFile); + } + + public FileConfiguration getMobCapConfig() { + return mobCapConfig; + } + + public void saveMobCapConfig() { + try { + mobCapConfig.save(mobCapFile); + getLogger().info("mobcap.yml erfolgreich gespeichert."); + } catch (IOException e) { + getLogger().log(Level.SEVERE, "Fehler beim Speichern der mobcap.yml: " + e.getMessage()); + } + } + + public void reloadMobCapConfig() { + mobCapConfig = YamlConfiguration.loadConfiguration(mobCapFile); + getLogger().info("mobcap.yml erfolgreich neu geladen."); + } + + // === AutoClearTask === + private void startAutoClearTask() { + if (autoClearTaskId != -1) { + Bukkit.getScheduler().cancelTask(autoClearTaskId); + autoClearTaskId = -1; + } + + if (getConfig().getBoolean("auto-clear-enabled", true)) { + int intervalMinutes = getConfig().getInt("auto-clear-interval-minutes", 15); + int intervalTicks = intervalMinutes * 60 * 20; // Minuten in Ticks umrechnen + if (intervalTicks > 0) { + autoClearTaskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new AutoClearTask(this), intervalTicks, intervalTicks); + getLogger().info("AutoClearTask gestartet mit Intervall von " + intervalMinutes + " Minuten."); + } + } else { + getLogger().info("AutoClearTask deaktiviert, da auto-clear-enabled auf false gesetzt ist."); + } + } + + // === Reload Plugin === + public void reloadPlugin() { + try { + // Config-Dateien aktualisieren + updateConfigFiles(); + reloadBackpackConfig(); + reloadFriendsConfig(); + reloadHomesConfig(); + reloadGravesConfig(); + reloadLeashesConfig(); + reloadMobCapConfig(); + + // AutoClearTask neu starten + startAutoClearTask(); + + // Bestehende Listener abmelden + PluginManager pm = getServer().getPluginManager(); + HandlerList.unregisterAll(this); + + // FriendCommand instanzieren für PlayerJoinListener + FriendCommand friendCommand = new FriendCommand(this, friendsConfig, langConfig, getLogger()); + + // Listener neu erstellen und registrieren + mobLeashLimitListener = new MobLeashLimitListener(this, getConfig()); + mobLeashLimitListener.reloadConfig(getConfig()); + pm.registerEvents(mobLeashLimitListener, this); + + mobCapListener = new MobCapListener(this, getConfig()); + mobCapListener.reloadConfig(getConfig()); + pm.registerEvents(mobCapListener, this); + + sleepListener = new SleepListener(this); + sleepListener.reloadConfig(getConfig()); + pm.registerEvents(sleepListener, this); + + oreAlarmListener = new OreAlarmListener(this); + oreAlarmListener.reloadConfig(getConfig()); + pm.registerEvents(oreAlarmListener, this); + + afkListener = new AFKListener(this); + afkListener.reloadConfig(getConfig()); + pm.registerEvents(afkListener, this); + + graveListener = new GraveListener(this); + graveListener.reloadConfig(getConfig()); + pm.registerEvents(graveListener, this); + + sitListener = new SitListener(this); + pm.registerEvents(sitListener, this); + + playerJoinListener = new PlayerJoinListener(friendCommand); + pm.registerEvents(playerJoinListener, this); + + pm.registerEvents(new InventoryClickListener(this), this); + pm.registerEvents(new BackpackListener(backpackConfig, langConfig, getLogger(), backpackFile), this); + pm.registerEvents(new StatsListener(this, statsManager), this); + pm.registerEvents(new LoginListener(this), this); + pm.registerEvents(new DebugArmorStandListener(), this); + pm.registerEvents(new ArmorStandDestroyListener(), this); + pm.registerEvents(new FirstJoinListener(), this); + + spawnProtectionListener = new SpawnProtectionListener(this); + pm.registerEvents(spawnProtectionListener, this); + + LockSystem lockSystem = new LockSystem(this); + pm.registerEvents(lockSystem, this); + getCommand("lock").setExecutor(lockSystem); + + // Commands neu registrieren + getCommand("friend").setExecutor(friendCommand); + + getLogger().info(getMessage("plugin.reloaded")); + } catch (Exception e) { + getLogger().severe("Fehler beim Neuladen des Plugins: " + e.getMessage()); + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/commands/AnvilCommand.java b/src/main/java/de/viper/survivalplus/commands/AnvilCommand.java new file mode 100644 index 0000000..cd03219 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/AnvilCommand.java @@ -0,0 +1,24 @@ +package de.viper.survivalplus.commands; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; + +public class AnvilCommand implements CommandExecutor { + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage("Nur Spieler können diesen Befehl ausführen."); + return true; + } + + Inventory anvil = Bukkit.createInventory(player, InventoryType.ANVIL, "Amboss"); + player.openInventory(anvil); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/BackCommand.java b/src/main/java/de/viper/survivalplus/commands/BackCommand.java new file mode 100644 index 0000000..8e07f21 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/BackCommand.java @@ -0,0 +1,67 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.configuration.file.FileConfiguration; + +public class BackCommand implements CommandExecutor { + private final SurvivalPlus plugin; + + public BackCommand(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage(plugin.getLangConfig().getString("player-only", "§cDieser Befehl ist nur für Spieler!")); + return true; + } + + Player player = (Player) sender; + FileConfiguration lang = plugin.getLangConfig(); + FileConfiguration gravesConfig = plugin.getGravesConfig(); + + if (!player.hasPermission("survivalplus.back")) { + player.sendMessage(lang.getString("no-permission", "§cDu hast keine Berechtigung für diesen Befehl!")); + return true; + } + + if (args.length != 0) { + player.sendMessage(lang.getString("back.usage", "§cVerwendung: /back")); + return true; + } + + // Lade Todesposition + String path = "graves." + player.getUniqueId(); + if (!gravesConfig.contains(path)) { + player.sendMessage(lang.getString("back.no-death-point", "§cDu hast keinen Todespunkt!")); + return true; + } + + String worldName = gravesConfig.getString(path + ".world"); + double x = gravesConfig.getDouble(path + ".x"); + double y = gravesConfig.getDouble(path + ".y"); + double z = gravesConfig.getDouble(path + ".z"); + + World world = plugin.getServer().getWorld(worldName); + if (world == null) { + player.sendMessage(lang.getString("back.no-death-point", "§cDu hast keinen Todespunkt!")); + return true; + } + + Location location = new Location(world, x, y, z); + player.teleport(location); + + player.sendMessage(String.format( + lang.getString("back.success", "§aTeleportiert zum Todespunkt bei x=%.2f, y=%.2f, z=%.2f!"), + x, y, z + )); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/BlockCommand.java b/src/main/java/de/viper/survivalplus/commands/BlockCommand.java new file mode 100644 index 0000000..5f1b731 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/BlockCommand.java @@ -0,0 +1,47 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.Manager.BlockManager; +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +import org.bukkit.configuration.file.FileConfiguration; + +public class BlockCommand implements CommandExecutor { + + private final BlockManager blockManager; + private final FileConfiguration config; + + public BlockCommand(BlockManager blockManager, FileConfiguration config) { + this.blockManager = blockManager; + this.config = config; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + + if (!(sender instanceof Player player)) { + sender.sendMessage(config.getString("messages.general.only_players")); + return true; + } + + if (args.length != 1) { + player.sendMessage(config.getString("messages.block.usage")); + return true; + } + + Player target = Bukkit.getPlayerExact(args[0]); + if (target == null || target == player) { + player.sendMessage(config.getString("messages.block.invalid_player")); + return true; + } + + if (blockManager.hasBlocked(player, target)) { + player.sendMessage(config.getString("messages.block.already_blocked").replace("%player%", target.getName())); + } else { + blockManager.blockPlayer(player, target); + player.sendMessage(config.getString("messages.block.blocked").replace("%player%", target.getName())); + } + + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/BlockListCommand.java b/src/main/java/de/viper/survivalplus/commands/BlockListCommand.java new file mode 100644 index 0000000..3184c43 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/BlockListCommand.java @@ -0,0 +1,43 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.Manager.BlockManager; +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +import org.bukkit.configuration.file.FileConfiguration; + +import java.util.stream.Collectors; + +public class BlockListCommand implements CommandExecutor { + + private final BlockManager blockManager; + private final FileConfiguration config; + + public BlockListCommand(BlockManager blockManager, FileConfiguration config) { + this.blockManager = blockManager; + this.config = config; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + + if (!(sender instanceof Player player)) { + sender.sendMessage(config.getString("messages.general.only_players")); + return true; + } + + var blocked = blockManager.getBlockedPlayers(player); + if (blocked.isEmpty()) { + player.sendMessage(config.getString("messages.blocklist.no_blocked_players")); + return true; + } + + String list = blocked.stream() + .map(Bukkit::getOfflinePlayer) + .map(p -> p.getName() != null ? p.getName() : "Unbekannt") + .collect(Collectors.joining(", ")); + + player.sendMessage(config.getString("messages.blocklist.blocked_players").replace("%list%", list)); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/ClearChatCommand.java b/src/main/java/de/viper/survivalplus/commands/ClearChatCommand.java new file mode 100644 index 0000000..54f05e1 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/ClearChatCommand.java @@ -0,0 +1,26 @@ +package de.viper.survivalplus.commands; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; + +public class ClearChatCommand implements CommandExecutor { + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!sender.hasPermission("survivalplus.clearchat")) { + sender.sendMessage("§cDu hast keine Rechte, den Chat zu löschen."); + return true; + } + + // Chat für alle "leeren" (100 leere Zeilen senden) + for (int i = 0; i < 100; i++) { + Bukkit.broadcastMessage(" "); + } + + Bukkit.broadcastMessage("§aDer Chat wurde von §e" + sender.getName() + " §agelöscht."); + + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/ClearDebugArmorStandsCommand.java b/src/main/java/de/viper/survivalplus/commands/ClearDebugArmorStandsCommand.java new file mode 100644 index 0000000..07e9e5e --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/ClearDebugArmorStandsCommand.java @@ -0,0 +1,43 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; + +public class ClearDebugArmorStandsCommand implements CommandExecutor { + + private final SurvivalPlus plugin; + + public ClearDebugArmorStandsCommand(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage("Dieser Befehl kann nur von einem Spieler ausgeführt werden."); + return true; + } + + Player player = (Player) sender; + int removedCount = 0; + + for (Entity entity : player.getWorld().getEntities()) { + if (entity instanceof ArmorStand) { + ArmorStand armorStand = (ArmorStand) entity; + armorStand.setInvulnerable(false); + armorStand.setMarker(false); + armorStand.setGravity(true); + armorStand.remove(); + removedCount++; + } + } + + player.sendMessage("Es wurden " + removedCount + " ArmorStands entfernt."); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/ClearItemsCommand.java b/src/main/java/de/viper/survivalplus/commands/ClearItemsCommand.java new file mode 100644 index 0000000..5b861a7 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/ClearItemsCommand.java @@ -0,0 +1,32 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Item; + +public class ClearItemsCommand implements CommandExecutor { + + private final SurvivalPlus plugin; + + public ClearItemsCommand(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + int totalRemoved = 0; + for (World world : Bukkit.getWorlds()) { + for (Item item : world.getEntitiesByClass(Item.class)) { + item.remove(); + totalRemoved++; + } + } + sender.sendMessage(ChatColor.RED + "Alle Items wurden entfernt (" + totalRemoved + " Stück)."); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/ClearReportCommand.java b/src/main/java/de/viper/survivalplus/commands/ClearReportCommand.java new file mode 100644 index 0000000..3103ea2 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/ClearReportCommand.java @@ -0,0 +1,44 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import de.viper.survivalplus.report.ReportManager; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class ClearReportCommand implements CommandExecutor { + + private final SurvivalPlus plugin; + private final ReportManager reportManager; + + public ClearReportCommand(SurvivalPlus plugin, ReportManager reportManager) { + this.plugin = plugin; + this.reportManager = reportManager; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage(plugin.getLangConfig().getString("clearreport.only-players", "§cNur Spieler können diesen Befehl ausführen!")); + return true; + } + + if (!player.hasPermission("survivalplus.report.clear")) { + player.sendMessage(plugin.getLangConfig().getString("no-permission", "§cDu hast keine Berechtigung!")); + return true; + } + + if (args.length != 1) { + player.sendMessage(plugin.getLangConfig().getString("clearreport.usage", "§cVerwendung: /clearreport ")); + return true; + } + + String targetName = args[0]; + reportManager.clearReports(targetName); + + player.sendMessage(plugin.getLangConfig().getString("clearreport.cleared", "§aReports von %player% wurden gelöscht.").replace("%player%", targetName)); + + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/CloseDoorsCommand.java b/src/main/java/de/viper/survivalplus/commands/CloseDoorsCommand.java new file mode 100644 index 0000000..5982069 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/CloseDoorsCommand.java @@ -0,0 +1,88 @@ +package de.viper.survivalplus.commands; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.data.Openable; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import de.viper.survivalplus.SurvivalPlus; + +public class CloseDoorsCommand implements CommandExecutor { + + private final SurvivalPlus plugin; + + public CloseDoorsCommand(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage(plugin.getLangConfig().getString("commands.closedoors.noplayer", "Nur Spieler können diesen Befehl nutzen!")); + return true; + } + + if (!player.hasPermission("survivalplus.closedoors")) { + player.sendMessage(plugin.getLangConfig().getString("commands.closedoors.nopermission", "Dafür hast du keine Rechte!")); + return true; + } + + if (args.length != 1) { + player.sendMessage(plugin.getLangConfig().getString("commands.closedoors.usage", "Benutzung: /closedoors ")); + return true; + } + + int radius; + try { + radius = Integer.parseInt(args[0]); + if (radius <= 0) { + player.sendMessage(plugin.getLangConfig().getString("commands.closedoors.invalidradius", "Der Radius muss größer als 0 sein!")); + return true; + } + } catch (NumberFormatException e) { + player.sendMessage(plugin.getLangConfig().getString("commands.closedoors.invalidradius", "Der Radius muss eine Zahl sein!")); + return true; + } + + int closedCount = 0; + var center = player.getLocation().getBlock(); + + for (int x = -radius; x <= radius; x++) { + for (int y = -radius; y <= radius; y++) { + for (int z = -radius; z <= radius; z++) { + Block block = center.getRelative(x, y, z); + Material type = block.getType(); + + if (type == Material.OAK_DOOR || + type == Material.SPRUCE_DOOR || + type == Material.BIRCH_DOOR || + type == Material.JUNGLE_DOOR || + type == Material.ACACIA_DOOR || + type == Material.DARK_OAK_DOOR || + type == Material.CRIMSON_DOOR || + type == Material.WARPED_DOOR || + type == Material.IRON_DOOR) { + + var blockData = block.getBlockData(); + if (blockData instanceof Openable openable) { + if (openable.isOpen()) { + openable.setOpen(false); + block.setBlockData(openable); + closedCount++; + } + } + } + } + } + } + + player.sendMessage(plugin.getLangConfig() + .getString("commands.closedoors.success", "Es wurden %count% Türen geschlossen.") + .replace("%count%", String.valueOf(closedCount))); + + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/DayCommand.java b/src/main/java/de/viper/survivalplus/commands/DayCommand.java new file mode 100644 index 0000000..b3a7938 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/DayCommand.java @@ -0,0 +1,33 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.configuration.file.FileConfiguration; + +public class DayCommand implements CommandExecutor { + private final SurvivalPlus plugin; + + public DayCommand(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage("§cNur Spieler können diesen Befehl ausführen!"); + return true; + } + + if (!player.hasPermission("survivalplus.day")) { + player.sendMessage(plugin.getLangConfig().getString("no-permission", "§cDu hast keine Berechtigung!")); + return true; + } + + player.getWorld().setTime(1000); // Tag setzen + player.sendMessage("§aEs ist jetzt Tag!"); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/DelWarpCommand.java b/src/main/java/de/viper/survivalplus/commands/DelWarpCommand.java new file mode 100644 index 0000000..1e6b70f --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/DelWarpCommand.java @@ -0,0 +1,45 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.Manager.WarpManager; +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class DelWarpCommand implements CommandExecutor { + + private final SurvivalPlus plugin; + private final WarpManager warpManager; + + public DelWarpCommand(SurvivalPlus plugin, WarpManager warpManager) { + this.plugin = plugin; + this.warpManager = warpManager; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage(plugin.getMessage("warp.only_players")); + return true; + } + + Player player = (Player) sender; + + if (args.length < 1) { + player.sendMessage(ChatColor.RED + "Benutze: /delwarp "); + return true; + } + + String warpName = args[0]; + boolean removed = warpManager.removeWarp(player.getName(), warpName); + + if (removed) { + player.sendMessage(ChatColor.GREEN + "Warp '" + warpName + "' wurde gelöscht."); + } else { + player.sendMessage(ChatColor.RED + "Du hast keinen Warp mit dem Namen '" + warpName + "'."); + } + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/EnderchestCommand.java b/src/main/java/de/viper/survivalplus/commands/EnderchestCommand.java new file mode 100644 index 0000000..2184284 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/EnderchestCommand.java @@ -0,0 +1,123 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +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.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.configuration.file.FileConfiguration; +import java.io.File; +import java.util.UUID; +import java.util.logging.Level; + +public class EnderchestCommand implements CommandExecutor { + private final SurvivalPlus plugin; + + public EnderchestCommand(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage(plugin.getLangConfig().getString("player-only", "§cDieser Befehl ist nur für Spieler!")); + return true; + } + + Player player = (Player) sender; + FileConfiguration lang = plugin.getLangConfig(); + + if (!player.hasPermission("survivalplus.enderchest.own")) { + player.sendMessage(lang.getString("no-permission", "§cDu hast keine Berechtigung für diesen Befehl!")); + return true; + } + + OfflinePlayer target; + String targetName; + + if (args.length == 0) { + // Öffne die eigene Enderchest + target = player; + targetName = player.getName(); + } else if (args.length == 1) { + // Öffne die Enderchest eines anderen Spielers + if (!player.hasPermission("survivalplus.enderchest.others")) { + player.sendMessage(lang.getString("no-permission-others-ec", "§cDu hast keine Berechtigung, die Enderchest anderer Spieler anzusehen!")); + return true; + } + targetName = args[0]; + Player onlineTarget = Bukkit.getPlayerExact(targetName); + if (onlineTarget != null) { + target = onlineTarget; + } else { + target = Bukkit.getOfflinePlayer(targetName); + if (target.getName() == null || !target.hasPlayedBefore()) { + player.sendMessage(lang.getString("player-not-found", "§cSpieler nicht gefunden!")); + return true; + } + } + } else { + player.sendMessage(lang.getString("enderchest.usage", "§cVerwendung: /ec [spieler]")); + return true; + } + + // Erstelle die GUI + Inventory gui = Bukkit.createInventory(null, 27, lang.getString("enderchest.gui-title", "Enderchest von ") + targetName); + + // Lade die Enderchest + if (target.isOnline()) { + Player onlineTarget = target.getPlayer(); + Inventory enderChest = onlineTarget.getEnderChest(); + for (int i = 0; i < 27; i++) { + gui.setItem(i, enderChest.getItem(i)); + } + plugin.getLogger().log(Level.INFO, "Enderchest für " + targetName + " (online) erfolgreich geladen"); + } else { + UUID uuid = target.getUniqueId(); + try { + // Lade Offline-Spieler-Enderchest aus playerdata + File dataFolder = new File(plugin.getServer().getWorlds().get(0).getWorldFolder(), "playerdata"); + File playerFile = new File(dataFolder, uuid.toString() + ".dat"); + + if (!playerFile.exists()) { + player.sendMessage(lang.getString("enderchest.data-not-found", "§cDie Enderchest-Daten des Spielers konnten nicht gefunden werden! Der Spieler war möglicherweise nie auf diesem Server.")); + return true; + } + + String version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; + plugin.getLogger().log(Level.INFO, "Versuche, Enderchest für " + targetName + " zu laden (UUID: " + uuid + ", Version: " + version + ")"); + + // Verwende Reflection, um die Enderchest-Daten zu laden + Class nbtIoClass = Class.forName("net.minecraft.nbt.NbtIo"); + Class nbtCompoundClass = Class.forName("net.minecraft.nbt.NbtCompound"); + Class nbtListClass = Class.forName("net.minecraft.nbt.NbtList"); + Class craftInventoryClass = Class.forName("org.bukkit.craftbukkit." + version + ".inventory.CraftInventory"); + + Object nbtCompound = nbtIoClass.getMethod("readCompressed", java.nio.file.Path.class).invoke(null, playerFile.toPath()); + Object enderItemsList = nbtCompoundClass.getMethod("getList", String.class, int.class).invoke(nbtCompound, "EnderItems", 10); + Object craftInventory = craftInventoryClass.getConstructor(nbtListClass).newInstance(enderItemsList); + + ItemStack[] items = (ItemStack[]) craftInventoryClass.getMethod("getContents").invoke(craftInventory); + for (int i = 0; i < Math.min(items.length, 27); i++) { + gui.setItem(i, items[i]); + } + plugin.getLogger().log(Level.INFO, "Enderchest für " + targetName + " erfolgreich geladen"); + } catch (Exception e) { + plugin.getLogger().log(Level.SEVERE, "Fehler beim Laden der Enderchest für " + targetName + " (UUID: " + uuid + ")", e); + String errorMessage = e.getMessage() != null ? e.getMessage() : e.toString(); + player.sendMessage(lang.getString("enderchest.load-error", "§cFehler beim Laden der Enderchest: %error%") + .replace("%error%", errorMessage)); + return true; + } + } + + player.openInventory(gui); + player.sendMessage(lang.getString("enderchest.opened", "§aEnderchest von %player% geöffnet!") + .replace("%player%", targetName)); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/commands/FriendCommand.java b/src/main/java/de/viper/survivalplus/commands/FriendCommand.java new file mode 100644 index 0000000..d0e8eba --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/FriendCommand.java @@ -0,0 +1,423 @@ +package de.viper.survivalplus.commands; + +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +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.plugin.java.JavaPlugin; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.UUID; +import java.util.logging.Logger; + +public class FriendCommand implements CommandExecutor { + private final JavaPlugin plugin; + private final FileConfiguration friendsConfig; + private final FileConfiguration langConfig; + private final Logger logger; + + public FriendCommand(JavaPlugin plugin, FileConfiguration friendsConfig, FileConfiguration langConfig, Logger logger) { + this.plugin = plugin; + this.friendsConfig = friendsConfig; + this.langConfig = langConfig; + this.logger = logger; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.player-only", "&cDieser Befehl ist nur für Spieler!"))); + return true; + } + + Player player = (Player) sender; + UUID playerUUID = player.getUniqueId(); + + if (args.length == 0) { + sendHelpMessage(player); + return true; + } + + String subCommand = args[0].toLowerCase(); + + switch (subCommand) { + case "add": + if (args.length != 2) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.add-usage", "&cVerwendung: /friend add "))); + return true; + } + handleFriendAdd(player, args[1]); + break; + + case "accept": + if (args.length != 2) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.accept-usage", "&cVerwendung: /friend accept "))); + return true; + } + handleFriendAccept(player, args[1]); + break; + + case "deny": + if (args.length != 2) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.deny-usage", "&cVerwendung: /friend deny "))); + return true; + } + handleFriendDeny(player, args[1]); + break; + + case "list": + if (args.length != 1) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.list-usage", "&cVerwendung: /friend list"))); + return true; + } + handleFriendList(player); + break; + + case "del": + if (args.length != 2) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.del-usage", "&cVerwendung: /friend del "))); + return true; + } + handleFriendDelete(player, args[1]); + break; + + case "confirm": + if (args.length != 2) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.confirm-usage", "&cVerwendung: /friend confirm "))); + return true; + } + handleFriendConfirmDelete(player, args[1]); + break; + + case "tp": + if (args.length != 2) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.tp-usage", "&cVerwendung: /friend tp "))); + return true; + } + handleFriendTeleport(player, args[1]); + break; + + default: + sendHelpMessage(player); + break; + } + + return true; + } + + private void sendHelpMessage(Player player) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.header", "&6=== Freundesliste Hilfe ==="))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.add", "&e/friend add &7- Freundschaftsanfrage senden"))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.accept", "&e/friend accept &7- Freundschaftsanfrage akzeptieren"))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.deny", "&e/friend deny &7- Freundschaftsanfrage ablehnen"))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.list", "&e/friend list &7- Liste deiner Freunde anzeigen"))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.del", "&e/friend del &7- Freund aus der Liste entfernen"))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.tp", "&e/friend tp &7- Zu einem Freund teleportieren"))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.footer", "&6====================="))); + } + + private void handleFriendAdd(Player player, String targetName) { + Player target = Bukkit.getPlayerExact(targetName); + if (target == null) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.player-not-found", "&cSpieler %s nicht gefunden!").replace("%s", targetName))); + return; + } + if (target.getUniqueId().equals(player.getUniqueId())) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.self", "&cDu kannst dich nicht selbst hinzufügen!"))); + return; + } + + UUID playerUUID = player.getUniqueId(); + UUID targetUUID = target.getUniqueId(); + List playerFriends = friendsConfig.getStringList(playerUUID + ".friends"); + List pendingRequests = friendsConfig.getStringList(targetUUID + ".pending_requests"); + + if (playerFriends.contains(targetUUID.toString())) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.already-friends", "&cDu bist bereits mit %s befreundet!").replace("%s", targetName))); + return; + } + + if (pendingRequests.contains(playerUUID.toString())) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.request-pending", "&cDu hast bereits eine Anfrage an %s gesendet!").replace("%s", targetName))); + return; + } + + pendingRequests.add(playerUUID.toString()); + friendsConfig.set(targetUUID + ".pending_requests", pendingRequests); + friendsConfig.set(targetUUID + ".name", targetName); + saveFriendsConfig(); + + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.add.sent", "&aFreundschaftsanfrage an %s gesendet!").replace("%s", targetName))); + + TextComponent message = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.add.received", "&aDu hast eine Freundschaftsanfrage von %s erhalten! ").replace("%s", player.getName()))); + TextComponent accept = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.add.accept-button", "&a[Accept]"))); + accept.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/friend accept " + player.getName())); + TextComponent deny = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.add.deny-button", "&c [Deny]"))); + deny.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/friend deny " + player.getName())); + message.addExtra(accept); + message.addExtra(deny); + target.spigot().sendMessage(message); + + logger.info("Freundschaftsanfrage von " + player.getName() + " an " + targetName + " gesendet."); + } + + private void handleFriendAccept(Player player, String requesterName) { + Player requester = Bukkit.getPlayerExact(requesterName); + if (requester == null) { + UUID requesterUUID = getUUIDFromName(requesterName); + if (requesterUUID == null) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.player-not-found", "&cSpieler %s nicht gefunden!").replace("%s", requesterName))); + return; + } + acceptFriendRequest(player, requesterUUID, requesterName); + } else { + acceptFriendRequest(player, requester.getUniqueId(), requesterName); + } + } + + private void acceptFriendRequest(Player player, UUID requesterUUID, String requesterName) { + UUID playerUUID = player.getUniqueId(); + List pendingRequests = friendsConfig.getStringList(playerUUID + ".pending_requests"); + + if (!pendingRequests.contains(requesterUUID.toString())) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.no-request", "&cKeine Anfrage von %s gefunden!").replace("%s", requesterName))); + return; + } + + pendingRequests.remove(requesterUUID.toString()); + friendsConfig.set(playerUUID + ".pending_requests", pendingRequests); + + List playerFriends = friendsConfig.getStringList(playerUUID + ".friends"); + List requesterFriends = friendsConfig.getStringList(requesterUUID + ".friends"); + + playerFriends.add(requesterUUID.toString()); + requesterFriends.add(playerUUID.toString()); + + friendsConfig.set(playerUUID + ".friends", playerFriends); + friendsConfig.set(playerUUID + ".name", player.getName()); + friendsConfig.set(requesterUUID + ".friends", requesterFriends); + friendsConfig.set(requesterUUID + ".name", requesterName); + + saveFriendsConfig(); + + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.accept.success", "&aDu bist jetzt mit %s befreundet!").replace("%s", requesterName))); + Player requester = Bukkit.getPlayer(requesterUUID); + if (requester != null) { + requester.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.accept.notify", "&a%s hat deine Freundschaftsanfrage akzeptiert!").replace("%s", player.getName()))); + } + logger.info(player.getName() + " hat die Freundschaftsanfrage von " + requesterName + " akzeptiert."); + } + + private void handleFriendDeny(Player player, String requesterName) { + UUID requesterUUID = getUUIDFromName(requesterName); + if (requesterUUID == null) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.player-not-found", "&cSpieler %s nicht gefunden!").replace("%s", requesterName))); + return; + } + + UUID playerUUID = player.getUniqueId(); + List pendingRequests = friendsConfig.getStringList(playerUUID + ".pending_requests"); + + if (!pendingRequests.contains(requesterUUID.toString())) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.no-request", "&cKeine Anfrage von %s gefunden!").replace("%s", requesterName))); + return; + } + + pendingRequests.remove(requesterUUID.toString()); + friendsConfig.set(playerUUID + ".pending_requests", pendingRequests); + saveFriendsConfig(); + + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.deny.success", "&aFreundschaftsanfrage von %s abgelehnt.").replace("%s", requesterName))); + Player requester = Bukkit.getPlayer(requesterUUID); + if (requester != null) { + requester.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.deny.notify", "&c%s hat deine Freundschaftsanfrage abgelehnt.").replace("%s", player.getName()))); + } + logger.info(player.getName() + " hat die Freundschaftsanfrage von " + requesterName + " abgelehnt."); + } + + private void handleFriendList(Player player) { + UUID playerUUID = player.getUniqueId(); + List friendUUIDs = friendsConfig.getStringList(playerUUID + ".friends"); + + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.list.header", "&6=== Deine Freundesliste ==="))); + if (friendUUIDs.isEmpty()) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.list.empty", "&7Du hast keine Freunde."))); + } else { + SimpleDateFormat dateFormat = new SimpleDateFormat(langConfig.getString("friend.list.date-format", "dd.MM.yyyy HH:mm:ss")); + for (String friendUUID : friendUUIDs) { + String friendName = getNameFromUUID(UUID.fromString(friendUUID)); + Player friend = Bukkit.getPlayer(UUID.fromString(friendUUID)); + TextComponent entry = new TextComponent(); + + if (friend != null) { + entry.addExtra(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.list.entry", "&e%s: %s").replaceFirst("%s", friendName).replaceFirst("%s", langConfig.getString("friend.list.online", "&aOnline")))); + } else { + long lastOnline = friendsConfig.getLong(friendUUID + ".last-online", 0); + String lastOnlineStr = lastOnline > 0 ? dateFormat.format(new Date(lastOnline)) : langConfig.getString("friend.list.unknown", "&7Unbekannt"); + entry.addExtra(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.list.entry-offline", "&e%s: %s &7(Zuletzt online: %s)").replaceFirst("%s", friendName).replaceFirst("%s", langConfig.getString("friend.list.offline", "&7Offline")).replace("%s", lastOnlineStr))); + } + + TextComponent removeButton = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.list.remove-button", "&c[X]"))); + removeButton.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/friend del " + friendName)); + entry.addExtra(" "); + entry.addExtra(removeButton); + + player.spigot().sendMessage(entry); + } + } + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.list.footer", "&6====================="))); + logger.info("Freundesliste für " + player.getName() + " angezeigt."); + } + + private void handleFriendDelete(Player player, String friendName) { + UUID friendUUID = getUUIDFromName(friendName); + if (friendUUID == null) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.player-not-found", "&cSpieler %s nicht gefunden!").replace("%s", friendName))); + return; + } + + UUID playerUUID = player.getUniqueId(); + List playerFriends = friendsConfig.getStringList(playerUUID + ".friends"); + + if (!playerFriends.contains(friendUUID.toString())) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.not-friends", "&c%s ist nicht in deiner Freundesliste!").replace("%s", friendName))); + return; + } + + TextComponent confirmMessage = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.del.confirm", "&cMöchtest du %s wirklich aus deiner Freundesliste entfernen? ").replace("%s", friendName))); + TextComponent confirmButton = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.del.confirm-button", "&a[Confirm]"))); + confirmButton.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/friend confirm " + friendName)); + TextComponent cancelButton = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.del.cancel-button", "&c[Cancel]"))); + cancelButton.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/friend list")); + confirmMessage.addExtra(confirmButton); + confirmMessage.addExtra(" "); + confirmMessage.addExtra(cancelButton); + player.spigot().sendMessage(confirmMessage); + + logger.info(player.getName() + " wurde zur Bestätigung aufgefordert, " + friendName + " aus der Freundesliste zu entfernen."); + } + + private void handleFriendConfirmDelete(Player player, String friendName) { + UUID friendUUID = getUUIDFromName(friendName); + if (friendUUID == null) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.player-not-found", "&cSpieler %s nicht gefunden!").replace("%s", friendName))); + return; + } + + UUID playerUUID = player.getUniqueId(); + List playerFriends = friendsConfig.getStringList(playerUUID + ".friends"); + List friendFriends = friendsConfig.getStringList(friendUUID + ".friends"); + + if (!playerFriends.contains(friendUUID.toString())) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.not-friends", "&c%s ist nicht in deiner Freundesliste!").replace("%s", friendName))); + return; + } + + playerFriends.remove(friendUUID.toString()); + friendFriends.remove(playerUUID.toString()); + + friendsConfig.set(playerUUID + ".friends", playerFriends); + friendsConfig.set(friendUUID + ".friends", friendFriends); + saveFriendsConfig(); + + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.del.success", "&a%s wurde aus deiner Freundesliste entfernt.").replace("%s", friendName))); + Player friend = Bukkit.getPlayer(friendUUID); + if (friend != null) { + friend.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.del.notify", "&c%s hat dich aus seiner Freundesliste entfernt.").replace("%s", player.getName()))); + } + logger.info(player.getName() + " hat " + friendName + " aus der Freundesliste entfernt."); + } + + private void handleFriendTeleport(Player player, String friendName) { + Player friend = Bukkit.getPlayerExact(friendName); + if (friend == null) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.player-not-found", "&cSpieler %s nicht gefunden!").replace("%s", friendName))); + return; + } + + UUID playerUUID = player.getUniqueId(); + UUID friendUUID = friend.getUniqueId(); + List playerFriends = friendsConfig.getStringList(playerUUID + ".friends"); + + if (!playerFriends.contains(friendUUID.toString())) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.not-friends", "&c%s ist nicht in deiner Freundesliste!").replace("%s", friendName))); + return; + } + + if (!player.getWorld().equals(friend.getWorld())) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.error.different-world", "&cIhr müsst in derselben Welt sein, um zu teleportieren!"))); + return; + } + + player.teleport(friend.getLocation()); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.tp.success", "&aDu wurdest zu %s teleportiert!").replace("%s", friendName))); + friend.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.tp.notify", "&a%s hat sich zu dir teleportiert.").replace("%s", player.getName()))); + logger.info(player.getName() + " hat sich zu " + friendName + " teleportiert."); + } + + private UUID getUUIDFromName(String name) { + Player target = Bukkit.getPlayerExact(name); + if (target != null) { + return target.getUniqueId(); + } + for (String key : friendsConfig.getKeys(false)) { + try { + UUID uuid = UUID.fromString(key); + String storedName = friendsConfig.getString(key + ".name"); + if (storedName != null && storedName.equalsIgnoreCase(name)) { + return uuid; + } + } catch (IllegalArgumentException ignored) { + } + } + return null; + } + + private String getNameFromUUID(UUID uuid) { + Player player = Bukkit.getPlayer(uuid); + if (player != null) { + return player.getName(); + } + String storedName = friendsConfig.getString(uuid + ".name"); + if (storedName != null) { + return storedName; + } + return uuid.toString(); + } + + private void saveFriendsConfig() { + try { + friendsConfig.save(new File(plugin.getDataFolder(), "friends.yml")); + logger.fine("friends.yml erfolgreich gespeichert."); + } catch (IOException e) { + logger.severe("Fehler beim Speichern der friends.yml: " + e.getMessage()); + e.printStackTrace(); + } + } + + public void notifyFriendsOfJoin(Player player) { + UUID playerUUID = player.getUniqueId(); + for (String friendUUIDStr : friendsConfig.getStringList(playerUUID + ".friends")) { + Player friend = Bukkit.getPlayer(UUID.fromString(friendUUIDStr)); + if (friend != null) { + friend.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.join.notify", "&aDein Freund %s ist dem Server beigetreten.").replace("%s", player.getName()))); + } + } + } + + public void updateLastOnline(Player player) { + UUID playerUUID = player.getUniqueId(); + friendsConfig.set(playerUUID + ".last-online", System.currentTimeMillis()); + saveFriendsConfig(); + } +} \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/commands/GamemodeCommand.java b/src/main/java/de/viper/survivalplus/commands/GamemodeCommand.java new file mode 100644 index 0000000..2a22bc3 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/GamemodeCommand.java @@ -0,0 +1,83 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.GameMode; +import org.bukkit.configuration.file.FileConfiguration; + +public class GamemodeCommand implements CommandExecutor { + private final SurvivalPlus plugin; + + public GamemodeCommand(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + FileConfiguration lang = plugin.getLangConfig(); + + if (!sender.hasPermission("survivalplus.gamemode")) { + sender.sendMessage(lang.getString("no-permission", "§cDu hast keine Berechtigung für diesen Befehl!")); + return true; + } + + if (args.length == 0) { + sender.sendMessage(lang.getString("gamemode.usage", "§cVerwendung: /gm <0|1|2|3> [spieler]")); + return true; + } + + Player target = (args.length > 1) ? plugin.getServer().getPlayer(args[1]) : (sender instanceof Player ? (Player) sender : null); + + if (args.length > 1 && !sender.hasPermission("survivalplus.gamemode.others")) { + sender.sendMessage(lang.getString("no-permission-others", "§cDu hast keine Berechtigung, den Spielmodus anderer zu ändern!")); + return true; + } + + if (target == null) { + sender.sendMessage(lang.getString("player-not-found", "§cSpieler nicht gefunden!")); + return true; + } + + GameMode gameMode; + String modeName; + switch (args[0].toLowerCase()) { + case "0": + case "survival": + gameMode = GameMode.SURVIVAL; + modeName = lang.getString("gamemode.survival", "Überleben"); + break; + case "1": + case "creative": + gameMode = GameMode.CREATIVE; + modeName = lang.getString("gamemode.creative", "Kreativ"); + break; + case "2": + case "adventure": + gameMode = GameMode.ADVENTURE; + modeName = lang.getString("gamemode.adventure", "Abenteuer"); + break; + case "3": + case "spectator": + gameMode = GameMode.SPECTATOR; + modeName = lang.getString("gamemode.spectator", "Zuschauer"); + break; + default: + sender.sendMessage(lang.getString("invalid-gamemode", "§cUngültiger Spielmodus! Verwende 0, 1, 2 oder 3")); + return true; + } + + target.setGameMode(gameMode); + String message = sender == target ? + lang.getString("gamemode.changed-self", "§aDein Spielmodus wurde zu %mode% geändert!") + .replace("%mode%", modeName) : + lang.getString("gamemode.changed-other", "§aSpielmodus von %player% zu %mode% geändert!") + .replace("%player%", target.getName()) + .replace("%mode%", modeName); + + sender.sendMessage(message); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/commands/HealCommand.java b/src/main/java/de/viper/survivalplus/commands/HealCommand.java new file mode 100644 index 0000000..5696a1f --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/HealCommand.java @@ -0,0 +1,62 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class HealCommand implements CommandExecutor { + + private final SurvivalPlus plugin; + + public HealCommand(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + + if (!(sender instanceof Player player)) { + sender.sendMessage("§cDieser Befehl kann nur von Spielern verwendet werden!"); + return true; + } + + // /heal ohne Argument → eigener Spieler + if (args.length == 0) { + if (!player.hasPermission("survivalplus.heal")) { + player.sendMessage("§cDu hast keine Berechtigung, dich zu heilen!"); + return true; + } + player.setHealth(player.getMaxHealth()); + player.setFoodLevel(20); + player.sendMessage("§aDu wurdest vollständig geheilt!"); + return true; + } + + // /heal → anderen Spieler heilen + if (args.length == 1) { + if (!player.hasPermission("survivalplus.heal.others")) { + player.sendMessage("§cDu hast keine Berechtigung, andere Spieler zu heilen!"); + return true; + } + + Player target = Bukkit.getPlayerExact(args[0]); + if (target == null || !target.isOnline()) { + player.sendMessage("§cDieser Spieler ist nicht online!"); + return true; + } + + target.setHealth(target.getMaxHealth()); + target.setFoodLevel(20); + target.sendMessage("§aDu wurdest von §e" + player.getName() + " §ageheilt!"); + player.sendMessage("§aDu hast §e" + target.getName() + " §ageheilt!"); + return true; + } + + // Falsche Benutzung + player.sendMessage("§eBenutzung: /heal [Spieler]"); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/HomeCommand.java b/src/main/java/de/viper/survivalplus/commands/HomeCommand.java new file mode 100644 index 0000000..72f01c1 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/HomeCommand.java @@ -0,0 +1,146 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.Material; +import org.bukkit.configuration.file.FileConfiguration; + +import java.util.ArrayList; +import java.util.List; + +public class HomeCommand implements CommandExecutor { + private final SurvivalPlus plugin; + + public HomeCommand(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage(plugin.getLangConfig().getString("player-only", "§cDieser Befehl ist nur für Spieler!")); + return true; + } + + Player player = (Player) sender; + FileConfiguration lang = plugin.getLangConfig(); + FileConfiguration homes = plugin.getHomesConfig(); + String commandName = command.getName().toLowerCase(); + + if (commandName.equals("sethome")) { + if (!player.hasPermission("survivalplus.homes.set")) { + player.sendMessage(lang.getString("no-permission", "§cDu hast keine Berechtigung für diesen Befehl!")); + return true; + } + + if (args.length != 1) { + player.sendMessage(lang.getString("sethome.usage", "§cVerwendung: /sethome ")); + return true; + } + + String homeName = args[0].toLowerCase(); + if (!homeName.matches("[a-z0-9_]+")) { + player.sendMessage(lang.getString("sethome.invalid-name", "§cDer Home-Name darf nur Buchstaben, Zahlen und Unterstriche enthalten!")); + return true; + } + + String path = "homes." + player.getUniqueId() + "." + homeName; + if (homes.contains(path)) { + player.sendMessage(lang.getString("sethome.already-exists", "§cEin Home mit diesem Namen existiert bereits!")); + return true; + } + + int maxHomes = player.hasPermission("survivalplus.homes.unlimited") ? Integer.MAX_VALUE : plugin.getConfig().getInt("max-homes", 3); + int currentHomes = homes.getConfigurationSection("homes." + player.getUniqueId()) != null ? + homes.getConfigurationSection("homes." + player.getUniqueId()).getKeys(false).size() : 0; + + if (currentHomes >= maxHomes) { + player.sendMessage(lang.getString("sethome.limit-reached", "§cDu hast die maximale Anzahl an Homes erreicht!")); + return true; + } + + Location loc = player.getLocation(); + homes.set(path + ".world", loc.getWorld().getName()); + homes.set(path + ".x", loc.getX()); + homes.set(path + ".y", loc.getY()); + homes.set(path + ".z", loc.getZ()); + homes.set(path + ".yaw", loc.getYaw()); + homes.set(path + ".pitch", loc.getPitch()); + plugin.saveHomesConfig(); + + player.sendMessage(lang.getString("sethome.success", "§aHome %name% wurde gesetzt!") + .replace("%name%", homeName)); + return true; + } + + if (commandName.equals("delhome")) { + if (!player.hasPermission("survivalplus.homes.delete")) { + player.sendMessage(lang.getString("no-permission", "§cDu hast keine Berechtigung für diesen Befehl!")); + return true; + } + + if (args.length != 1) { + player.sendMessage(lang.getString("delhome.usage", "§cVerwendung: /delhome ")); + return true; + } + + String homeName = args[0].toLowerCase(); + String path = "homes." + player.getUniqueId() + "." + homeName; + if (!homes.contains(path)) { + player.sendMessage(lang.getString("delhome.not-found", "§cDieses Home existiert nicht!")); + return true; + } + + homes.set(path, null); + plugin.saveHomesConfig(); + player.sendMessage(lang.getString("delhome.success", "§aHome %name% wurde gelöscht!") + .replace("%name%", homeName)); + return true; + } + + if (commandName.equals("homelist")) { + if (!player.hasPermission("survivalplus.homes.list")) { + player.sendMessage(lang.getString("no-permission", "§cDu hast keine Berechtigung für diesen Befehl!")); + return true; + } + + String path = "homes." + player.getUniqueId(); + if (!homes.contains(path) || homes.getConfigurationSection(path).getKeys(false).isEmpty()) { + player.sendMessage(lang.getString("homelist.no-homes", "§cDu hast keine Homes gesetzt!")); + return true; + } + + Inventory gui = Bukkit.createInventory(null, 27, lang.getString("homelist.gui-title", "Deine Homes")); + List homeNames = new ArrayList<>(homes.getConfigurationSection(path).getKeys(false)); + + for (int i = 0; i < Math.min(homeNames.size(), 27); i++) { + String homeName = homeNames.get(i); + String homePath = path + "." + homeName; + ItemStack item = new ItemStack(Material.RED_BED); + ItemMeta meta = item.getItemMeta(); + meta.setDisplayName("§a" + homeName); + List lore = new ArrayList<>(); + lore.add("§7Welt: " + homes.getString(homePath + ".world")); + lore.add("§7X: " + String.format("%.2f", homes.getDouble(homePath + ".x"))); + lore.add("§7Y: " + String.format("%.2f", homes.getDouble(homePath + ".y"))); + lore.add("§7Z: " + String.format("%.2f", homes.getDouble(homePath + ".z"))); + meta.setLore(lore); + item.setItemMeta(meta); + gui.setItem(i, item); + } + + player.openInventory(gui); + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/commands/InventoryCommand.java b/src/main/java/de/viper/survivalplus/commands/InventoryCommand.java new file mode 100644 index 0000000..a348d04 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/InventoryCommand.java @@ -0,0 +1,140 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +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.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.configuration.file.FileConfiguration; + +import java.util.UUID; +import java.util.logging.Level; + +public class InventoryCommand implements CommandExecutor, Listener { + + private final SurvivalPlus plugin; + + public InventoryCommand(SurvivalPlus plugin) { + this.plugin = plugin; + Bukkit.getPluginManager().registerEvents(this, plugin); + } + + public static class SnapshotHolder implements InventoryHolder { + private final UUID targetUuid; + private final boolean readOnly; + + public SnapshotHolder(UUID targetUuid, boolean readOnly) { + this.targetUuid = targetUuid; + this.readOnly = readOnly; + } + + public UUID getTargetUuid() { + return targetUuid; + } + + public boolean isReadOnly() { + return readOnly; + } + + @Override + public Inventory getInventory() { + return null; + } + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + FileConfiguration lang = plugin.getLangConfig(); + + if (!(sender instanceof Player)) { + sender.sendMessage(lang.getString("player-only")); + return true; + } + + Player player = (Player) sender; + + if (!player.hasPermission("survivalplus.inventory.own")) { + player.sendMessage(lang.getString("no-permission")); + return true; + } + + if (args.length != 1) { + player.sendMessage(lang.getString("inventory.usage")); + return true; + } + + String targetName = args[0]; + OfflinePlayer target = Bukkit.getOfflinePlayer(targetName); + + Player onlineTarget = Bukkit.getPlayerExact(targetName); + if (onlineTarget != null && onlineTarget.isOnline()) { + if (!player.getName().equalsIgnoreCase(targetName) && !player.hasPermission("survivalplus.inventory.others")) { + player.sendMessage(lang.getString("no-permission-others-inv")); + return true; + } + + PlayerInventory targetInv = onlineTarget.getInventory(); + player.openInventory(targetInv); + player.sendMessage(lang.getString("inventory.opened").replace("%player%", targetName)); + plugin.getLogger().log(Level.INFO, "Inventar von " + targetName + " (online) wurde von " + player.getName() + " geöffnet."); + return true; + } + + if (target.getName() == null || !target.hasPlayedBefore()) { + player.sendMessage(lang.getString("player-not-found")); + return true; + } + + if (!player.getName().equalsIgnoreCase(targetName) && !player.hasPermission("survivalplus.inventory.others")) { + player.sendMessage(lang.getString("no-permission-others-inv")); + return true; + } + + // Offline-Inventare nicht editierbar + player.sendMessage(lang.getString("inventory.offline-not-editable")); + plugin.getLogger().log(Level.INFO, "Inventar von " + targetName + " ist offline — Live-Bearbeitung nicht möglich."); + + return true; + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + InventoryHolder holder = event.getInventory().getHolder(); + if (holder instanceof SnapshotHolder) { + SnapshotHolder sh = (SnapshotHolder) holder; + if (sh.isReadOnly()) { + if (event.getWhoClicked() instanceof Player) { + Player p = (Player) event.getWhoClicked(); + FileConfiguration lang = plugin.getLangConfig(); + p.sendMessage(lang.getString("inventory.read-only")); + } + event.setCancelled(true); + } + } + } + + @EventHandler + public void onInventoryDrag(InventoryDragEvent event) { + InventoryHolder holder = event.getInventory().getHolder(); + if (holder instanceof SnapshotHolder) { + SnapshotHolder sh = (SnapshotHolder) holder; + if (sh.isReadOnly()) { + if (event.getWhoClicked() instanceof Player) { + Player p = (Player) event.getWhoClicked(); + FileConfiguration lang = plugin.getLangConfig(); + p.sendMessage(lang.getString("inventory.read-only")); + } + event.setCancelled(true); + } + } + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/ItemRenameCommand.java b/src/main/java/de/viper/survivalplus/commands/ItemRenameCommand.java new file mode 100644 index 0000000..83c31a2 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/ItemRenameCommand.java @@ -0,0 +1,63 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.ChatColor; +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.ItemMeta; + +public class ItemRenameCommand implements CommandExecutor { + + private final SurvivalPlus plugin; + + public ItemRenameCommand(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage(ChatColor.RED + plugin.getMessage("ir.only-player")); + return true; + } + + if (!player.hasPermission("survivalplus.itemrename")) { + player.sendMessage(ChatColor.RED + plugin.getMessage("ir.no-permission")); + return true; + } + + if (args.length == 0) { + player.sendMessage(ChatColor.RED + plugin.getMessage("ir.no-name")); + player.sendMessage(ChatColor.YELLOW + plugin.getMessage("ir.usage")); + return true; + } + + ItemStack item = player.getInventory().getItemInMainHand(); + if (item == null || item.getType().isAir()) { + player.sendMessage(ChatColor.RED + plugin.getMessage("ir.no-item")); + return true; + } + + StringBuilder newNameBuilder = new StringBuilder(); + for (String arg : args) { + newNameBuilder.append(arg).append(" "); + } + String newName = newNameBuilder.toString().trim(); + newName = ChatColor.translateAlternateColorCodes('&', newName); + + ItemMeta meta = item.getItemMeta(); + if (meta == null) { + player.sendMessage(ChatColor.RED + plugin.getMessage("ir.cant-rename")); + return true; + } + + meta.setDisplayName(newName); + item.setItemMeta(meta); + + player.sendMessage(ChatColor.GREEN + plugin.getMessage("ir.success").replace("{name}", newName)); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/KitCommand.java b/src/main/java/de/viper/survivalplus/commands/KitCommand.java new file mode 100644 index 0000000..870b386 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/KitCommand.java @@ -0,0 +1,65 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.List; + +public class KitCommand implements CommandExecutor { + + private final SurvivalPlus plugin; + + public KitCommand(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (sender instanceof Player) { + Player player = (Player) sender; + + // Hole die Konfiguration + FileConfiguration config = plugin.getConfig(); + + // Hole die Items aus der Konfiguration + List items = config.getStringList("first-join-kit.items"); + + for (String itemString : items) { + // Teile den Item-String auf + String[] itemParts = itemString.split(","); + String materialName = itemParts[0].toUpperCase(); + int amount = Integer.parseInt(itemParts[1]); + String displayName = itemParts.length > 2 ? itemParts[2] : ""; + + // Erstelle das Item + Material material = Material.getMaterial(materialName); + if (material == null) { + player.sendMessage("Unbekanntes Item: " + materialName); + continue; // Falls das Item ungültig ist, überspringe es + } + + ItemStack item = new ItemStack(material, amount); + ItemMeta meta = item.getItemMeta(); + + // Setze den Display-Namen (falls vorhanden) + if (meta != null && !displayName.isEmpty()) { + meta.setDisplayName(displayName); + } + + item.setItemMeta(meta); + player.getInventory().addItem(item); // Gib das Item an den Spieler + + } + player.sendMessage("Du hast dein Kit erhalten!"); + return true; + } + return false; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/LeashCountCommand.java b/src/main/java/de/viper/survivalplus/commands/LeashCountCommand.java new file mode 100644 index 0000000..b8e059e --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/LeashCountCommand.java @@ -0,0 +1,30 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import de.viper.survivalplus.listeners.MobLeashLimitListener; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class LeashCountCommand implements CommandExecutor { + private final SurvivalPlus plugin; + private final MobLeashLimitListener listener; + + public LeashCountCommand(SurvivalPlus plugin, MobLeashLimitListener listener) { + this.plugin = plugin; + this.listener = listener; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage(plugin.getMessage("player-only")); + return true; + } + Player player = (Player) sender; + int count = listener.getLeashCount(player); + player.sendMessage(plugin.getMessage("leashcount.message").replace("%count%", String.valueOf(count))); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/NickCommand.java b/src/main/java/de/viper/survivalplus/commands/NickCommand.java new file mode 100644 index 0000000..fd7d79c --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/NickCommand.java @@ -0,0 +1,90 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import net.md_5.bungee.api.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class NickCommand implements CommandExecutor { + + private static final Pattern HEX_PATTERN = Pattern.compile("#[a-fA-F0-9]{6}"); + private final SurvivalPlus plugin; + + public NickCommand(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage("§cDieser Befehl ist nur für Spieler!"); + return true; + } + + if (!player.hasPermission("survivalplus.nick")) { + player.sendMessage("§cDu hast keine Berechtigung für diesen Befehl!"); + return true; + } + + if (args.length == 0) { + player.sendMessage("§eBenutzung: /nick "); + return true; + } + + // Name zusammensetzen + StringBuilder sb = new StringBuilder(); + for (String arg : args) { + sb.append(arg).append(" "); + } + String rawNick = sb.toString().trim(); + + // Verbotene Nicks prüfen + List forbidden = plugin.getConfig().getStringList("forbidden-nicks"); + for (String word : forbidden) { + if (rawNick.toLowerCase().contains(word.toLowerCase())) { + player.sendMessage("§cDieser Nickname ist nicht erlaubt!"); + return true; + } + } + + // Farben anwenden + String coloredNick = translateColors(rawNick); + + // Format: [Nickname] (Klammern weiß, Nickname farbig) + String finalNick = "§f[" + coloredNick + "§f]"; + + // Anwenden + player.setDisplayName(finalNick); + player.setPlayerListName(finalNick); + + // Speichern (ungefärbten Namen) + plugin.getNicknamesConfig().set(player.getUniqueId().toString(), rawNick); + plugin.saveNicknamesConfig(); + + player.sendMessage("§aDein Nickname wurde zu " + finalNick + " geändert."); + return true; + } + + private String translateColors(String input) { + String withLegacy = org.bukkit.ChatColor.translateAlternateColorCodes('&', input); + return replaceHexColors(withLegacy); + } + + private String replaceHexColors(String input) { + Matcher matcher = HEX_PATTERN.matcher(input); + StringBuffer sb = new StringBuffer(); + while (matcher.find()) { + String hexCode = matcher.group(); + String replacement = ChatColor.of(hexCode).toString(); + matcher.appendReplacement(sb, replacement); + } + matcher.appendTail(sb); + return sb.toString(); + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/NightCommand.java b/src/main/java/de/viper/survivalplus/commands/NightCommand.java new file mode 100644 index 0000000..6590cea --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/NightCommand.java @@ -0,0 +1,32 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class NightCommand implements CommandExecutor { + private final SurvivalPlus plugin; + + public NightCommand(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage("§cNur Spieler können diesen Befehl ausführen!"); + return true; + } + + if (!player.hasPermission("survivalplus.night")) { + player.sendMessage(plugin.getLangConfig().getString("no-permission", "§cDu hast keine Berechtigung!")); + return true; + } + + player.getWorld().setTime(13000); // Nacht setzen + player.sendMessage("§aEs ist jetzt Nacht!"); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/PluginCommand.java b/src/main/java/de/viper/survivalplus/commands/PluginCommand.java new file mode 100644 index 0000000..ca5e0a2 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/PluginCommand.java @@ -0,0 +1,248 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +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.ArrayList; +import java.util.List; + +public class PluginCommand implements CommandExecutor { + private final SurvivalPlus plugin; + private static final int COMMANDS_PER_PAGE = 5; + + public PluginCommand(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + FileConfiguration lang = plugin.getLangConfig(); + + // /sp oder /sp help + if (args.length == 0 || args[0].equalsIgnoreCase("help")) { + int page = 1; + if (args.length > 1 && args[0].equalsIgnoreCase("help")) { + try { + page = Integer.parseInt(args[1]); + if (page < 1) page = 1; + } catch (NumberFormatException e) { + sender.sendMessage(color(lang.getString( + "sp.invalid-subcommand", + "§cUngültiger Unterbefehl! Verwendung: /sp [reload|help|info|share]" + ))); + return true; + } + } + showHelp(sender, page); + return true; + } + + // /sp reload + if (args[0].equalsIgnoreCase("reload")) { + if (!sender.hasPermission("survivalplus.reload")) { + sender.sendMessage(color(lang.getString("sp.no-permission", "§cDu hast keine Berechtigung für diesen Befehl!"))); + return true; + } + plugin.reloadPlugin(); + sender.sendMessage(color(lang.getString("sp.plugin.reloaded", "§aSurvivalPlus wurde erfolgreich neu geladen!"))); + return true; + } + + // /sp info + if (args[0].equalsIgnoreCase("info")) { + sender.sendMessage(color(lang.getString("sp.info.header", "&7===== SurvivalPlus Info ====="))); + sender.sendMessage(color(lang.getString("sp.info.name", "&ePlugin-Name: &f") + plugin.getDescription().getName())); + sender.sendMessage(color(lang.getString("sp.info.version", "&eVersion: &f") + plugin.getDescription().getVersion())); + sender.sendMessage(color(lang.getString("sp.info.author", "&eErsteller: &f") + "M_Viper")); + sender.sendMessage(color(lang.getString("sp.info.description", "&eBeschreibung:\n&f") + plugin.getDescription().getDescription())); + + if (sender instanceof Player player) { + // Überschrift über die Links + player.sendMessage(ChatColor.GOLD + "Links:"); + + // Button: Spigot-Seite + TextComponent spigotLink = new TextComponent(ChatColor.GREEN + "[Spigot-Seite]"); + spigotLink.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, + "https://www.spigotmc.org/resources/authors/m-lukas-17.618600/")); + spigotLink.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, + new ComponentBuilder(ChatColor.YELLOW + "Klicke, um die Spigot-Seite zu öffnen").create() + )); + + // Button: Bugs melden (Git) + TextComponent gitLink = new TextComponent(ChatColor.GREEN + " [Bugs melden]"); + gitLink.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, + "https://git.viper.ipv64.net/M_Viper/Survival-Plus/issues")); + gitLink.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, + new ComponentBuilder(ChatColor.YELLOW + "Klicke, um den Bug-Tracker zu öffnen").create() + )); + + // Beide nebeneinander + spigotLink.addExtra(gitLink); + player.spigot().sendMessage(spigotLink); + + } else { + sender.sendMessage(ChatColor.GOLD + "Links:"); + sender.sendMessage(ChatColor.GREEN + "[Spigot-Seite] " + + "https://www.spigotmc.org/resources/authors/m-lukas-17.618600/" + + ChatColor.GREEN + " [Bugs melden] " + + "https://git.viper.ipv64.net/M_Viper/Survival-Plus/issues"); + } + + sender.sendMessage(color(lang.getString("sp.info.footer", "&7=========================="))); + return true; + } + + // /sp share → Vorschau mit Buttons + if (args[0].equalsIgnoreCase("share")) { + if (!(sender instanceof Player player)) { + sender.sendMessage(color(lang.getString("player-only", "§cDieser Befehl ist nur für Spieler!"))); + return true; + } + + org.bukkit.Location loc = player.getLocation(); + String coordsMsg = color(lang.getString("sp.share.preview-format", "") + .replace("%player%", player.getName()) + .replace("%x%", String.valueOf(loc.getBlockX())) + .replace("%y%", String.valueOf(loc.getBlockY())) + .replace("%z%", String.valueOf(loc.getBlockZ())) + .replace("%world%", loc.getWorld().getName()) + ); + + player.sendMessage(color(lang.getString("sp.share.preview-title", "§aDeine aktuellen Koordinaten wären:"))); + player.sendMessage(coordsMsg); + + TextComponent sendBtn = new TextComponent(color(lang.getString("sp.share.send-button", "§a[✅ Senden]"))); + sendBtn.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/sp shareconfirm")); + sendBtn.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, + new ComponentBuilder(color(lang.getString("sp.share.send-hover", + "§aKlicke, um deine Koordinaten an alle zu senden."))).create() + )); + + TextComponent cancelBtn = new TextComponent(color(lang.getString("sp.share.cancel-button", "§c [❌ Abbrechen]"))); + cancelBtn.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/sp sharecancel")); + cancelBtn.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, + new ComponentBuilder(color(lang.getString("sp.share.cancel-hover", + "§cKlicke, um das Senden abzubrechen."))).create() + )); + + sendBtn.addExtra(cancelBtn); + player.spigot().sendMessage(sendBtn); + return true; + } + + // /sp shareconfirm → bestätigt und sendet + if (args[0].equalsIgnoreCase("shareconfirm")) { + if (!(sender instanceof Player player)) { + sender.sendMessage(color(lang.getString("player-only", "§cDieser Befehl ist nur für Spieler!"))); + return true; + } + org.bukkit.Location loc = player.getLocation(); + String message = color(lang.getString("sp.share.preview-format", "") + .replace("%player%", player.getName()) + .replace("%x%", String.valueOf(loc.getBlockX())) + .replace("%y%", String.valueOf(loc.getBlockY())) + .replace("%z%", String.valueOf(loc.getBlockZ())) + .replace("%world%", loc.getWorld().getName()) + ); + Bukkit.broadcastMessage(message); + player.sendMessage(color(lang.getString("sp.share.sent", "§aKoordinaten gesendet."))); + return true; + } + + // /sp sharecancel → bricht ab + if (args[0].equalsIgnoreCase("sharecancel")) { + if (sender instanceof Player p) { + p.sendMessage(color(lang.getString("sp.share.cancelled", "§eSenden der Koordinaten abgebrochen."))); + } + return true; + } + + // Ungültiger Unterbefehl + sender.sendMessage(color(lang.getString( + "sp.invalid-subcommand", + "§cUngültiger Unterbefehl! Verwendung: /sp [reload|help|info|share]" + ))); + return true; + } + + private void showHelp(CommandSender sender, int page) { + FileConfiguration help = plugin.getHelpConfig(); + FileConfiguration lang = plugin.getLangConfig(); + + if (help == null || help.getConfigurationSection("commands") == null) { + sender.sendMessage(color(lang.getString("sp.help-not-found", "&cHilfedatei (help.yml) konnte nicht geladen werden!"))); + return; + } + + List commands = new ArrayList<>(help.getConfigurationSection("commands").getKeys(false)); + if (commands.isEmpty()) { + sender.sendMessage(color(lang.getString("sp.help-not-found", "&cKeine Befehle verfügbar."))); + return; + } + + int totalPages = (int) Math.ceil((double) commands.size() / COMMANDS_PER_PAGE); + if (page > totalPages) page = totalPages; + if (page < 1) page = 1; + + sender.sendMessage(color(help.getString("header", "&7===== &eSurvivalPlus Hilfe &7====="))); + + int startIndex = (page - 1) * COMMANDS_PER_PAGE; + int endIndex = Math.min(startIndex + COMMANDS_PER_PAGE, commands.size()); + + for (int i = startIndex; i < endIndex; i++) { + String cmd = commands.get(i); + String usage = help.getString("commands." + cmd + ".usage", ""); + String description = help.getString("commands." + cmd + ".description", ""); + sender.sendMessage(color(usage + " §7- " + description)); + } + + if (sender instanceof Player player) { + TextComponent navigation = new TextComponent(); + + if (page > 1) { + TextComponent prev = new TextComponent(color(help.getString("navigation.prev", "« Vorherige "))); + prev.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/sp help " + (page - 1))); + navigation.addExtra(prev); + } else { + navigation.addExtra(color(help.getString("navigation.prev-disabled", "« "))); + } + + TextComponent pageInfo = new TextComponent(color(help.getString("navigation.page", " §fSeite {current} von {total} ")) + .replace("{current}", String.valueOf(page)) + .replace("{total}", String.valueOf(totalPages))); + navigation.addExtra(pageInfo); + + if (page < totalPages) { + TextComponent next = new TextComponent(color(help.getString("navigation.next", " Nächste »"))); + next.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/sp help " + (page + 1))); + navigation.addExtra(next); + } else { + navigation.addExtra(color(help.getString("navigation.next-disabled", " »"))); + } + + player.spigot().sendMessage(navigation); + } else { + for (String cmd : commands) { + String usage = help.getString("commands." + cmd + ".usage", ""); + String description = help.getString("commands." + cmd + ".description", ""); + sender.sendMessage(color(usage + " §7- " + description)); + } + } + + sender.sendMessage(color(help.getString("footer", "&7=========================="))); + } + + private String color(String input) { + return ChatColor.translateAlternateColorCodes('&', input == null ? "" : input); + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/ReportCommand.java b/src/main/java/de/viper/survivalplus/commands/ReportCommand.java new file mode 100644 index 0000000..44c6ce8 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/ReportCommand.java @@ -0,0 +1,65 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import de.viper.survivalplus.report.ReportManager; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class ReportCommand implements CommandExecutor { + + private final SurvivalPlus plugin; + private final ReportManager reportManager; + + public ReportCommand(SurvivalPlus plugin, ReportManager reportManager) { + this.plugin = plugin; + this.reportManager = reportManager; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + + if (!(sender instanceof Player player)) { + sender.sendMessage(plugin.getLangConfig().getString("report.only-players", "§cNur Spieler können diesen Befehl ausführen!")); + return true; + } + + if (args.length < 1) { + player.sendMessage(plugin.getLangConfig().getString("report.usage", "§cVerwendung: /report [Grund]")); + return true; + } + + String targetName = args[0]; + String reason = "Kein Grund angegeben"; + if (args.length > 1) { + reason = String.join(" ", java.util.Arrays.copyOfRange(args, 1, args.length)); + } + + if (Bukkit.getPlayer(targetName) == null) { + player.sendMessage(plugin.getLangConfig().getString("report.player-not-online", "§cDieser Spieler ist nicht online!")); + return true; + } + + String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + reportManager.addReport(targetName, player.getName(), reason, time); + + player.sendMessage(plugin.getLangConfig().getString("report.success", "§aDein Report wurde erfolgreich gesendet.")); + + // Nachricht an Admins + String msg = plugin.getLangConfig().getString("report.notify-admin", + "§cREPORT§r: %reporter% hat %target% gemeldet. Grund: %reason%") + .replace("%reporter%", player.getName()) + .replace("%target%", targetName) + .replace("%reason%", reason); + Bukkit.getOnlinePlayers().stream() + .filter(p -> p.hasPermission("survivalplus.report.receive")) + .forEach(p -> p.sendMessage(msg)); + + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/SetSpawnCommand.java b/src/main/java/de/viper/survivalplus/commands/SetSpawnCommand.java new file mode 100644 index 0000000..675e1c3 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/SetSpawnCommand.java @@ -0,0 +1,41 @@ +package de.viper.survivalplus.commands; + +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import de.viper.survivalplus.SurvivalPlus; + +public class SetSpawnCommand implements CommandExecutor { + private final SurvivalPlus plugin; + + public SetSpawnCommand(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage("Nur Spieler können diesen Befehl verwenden!"); + return true; + } + + if (!player.hasPermission("survivalplus.setspawn")) { + player.sendMessage("§cDu hast keine Rechte für diesen Befehl."); + return true; + } + + Location loc = player.getLocation(); + plugin.getConfig().set("spawn.world", loc.getWorld().getName()); + plugin.getConfig().set("spawn.x", loc.getX()); + plugin.getConfig().set("spawn.y", loc.getY()); + plugin.getConfig().set("spawn.z", loc.getZ()); + plugin.getConfig().set("spawn.yaw", loc.getYaw()); + plugin.getConfig().set("spawn.pitch", loc.getPitch()); + plugin.saveConfig(); + + player.sendMessage("§aSpawnpunkt erfolgreich gesetzt!"); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/SetWarpCommand.java b/src/main/java/de/viper/survivalplus/commands/SetWarpCommand.java new file mode 100644 index 0000000..023e3b7 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/SetWarpCommand.java @@ -0,0 +1,68 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.Manager.Warp; +import de.viper.survivalplus.Manager.WarpManager; +import de.viper.survivalplus.SurvivalPlus; +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.entity.Player; +import org.bukkit.inventory.ItemStack; + +public class SetWarpCommand implements CommandExecutor { + + private final SurvivalPlus plugin; + private final WarpManager warpManager; + + public SetWarpCommand(SurvivalPlus plugin, WarpManager warpManager) { + this.plugin = plugin; + this.warpManager = warpManager; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage(plugin.getMessage("warp.only_players")); + return true; + } + Player player = (Player) sender; + + if (args.length < 1) { + player.sendMessage(ChatColor.RED + "Benutze: /setwarp "); + return true; + } + + String warpName = args[0]; + + int maxWarps = plugin.getConfig().getInt("maxWarpsPerPlayer", 5); + int currentWarpCount = warpManager.getWarpCountForPlayer(player.getName()); + + Warp existingWarp = warpManager.getWarp(player.getName(), warpName); + + // Limit prüfen - Falls Warp mit dem Namen schon existiert, Update erlaubt + if (currentWarpCount >= maxWarps && existingWarp == null) { + player.sendMessage(ChatColor.RED + "Du hast das maximale Warp-Limit von " + maxWarps + " erreicht."); + return true; + } + + ItemStack item = player.getInventory().getItemInMainHand(); + + // Default Item wenn leer (aus config oder Schild) + if (item == null || item.getType().isAir()) { + String defaultItemName = plugin.getConfig().getString("defaultWarpItem", "OAK_SIGN"); + try { + item = new org.bukkit.inventory.ItemStack(org.bukkit.Material.valueOf(defaultItemName)); + } catch (IllegalArgumentException e) { + item = new org.bukkit.inventory.ItemStack(org.bukkit.Material.OAK_SIGN); + } + } + + Location loc = player.getLocation(); + Warp warp = new Warp(player.getName(), warpName, item, loc.getWorld().getName(), loc.getX(), loc.getY(), loc.getZ()); + warpManager.addWarp(warp); + player.sendMessage(plugin.getMessage("warp.set_success").replace("%warp%", warpName)); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/SetWorldSpawnCommand.java b/src/main/java/de/viper/survivalplus/commands/SetWorldSpawnCommand.java new file mode 100644 index 0000000..d890578 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/SetWorldSpawnCommand.java @@ -0,0 +1,40 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class SetWorldSpawnCommand implements CommandExecutor { + + private final SurvivalPlus plugin; + + public SetWorldSpawnCommand(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage(ChatColor.RED + "Dieser Befehl kann nur von einem Spieler verwendet werden."); + return true; + } + + if (!player.hasPermission("survivalplus.setworldspawn")) { + player.sendMessage(ChatColor.RED + "Du hast keine Rechte für diesen Befehl."); + return true; + } + + Location loc = player.getLocation(); + World world = player.getWorld(); + world.setSpawnLocation(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); + player.sendMessage(ChatColor.GREEN + "Spawnpunkt gesetzt bei §e" + + loc.getBlockX() + " " + loc.getBlockY() + " " + loc.getBlockZ()); + + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/ShopCommand.java b/src/main/java/de/viper/survivalplus/commands/ShopCommand.java new file mode 100644 index 0000000..57b5df8 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/ShopCommand.java @@ -0,0 +1,84 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.Manager.ShopManager; +import de.viper.survivalplus.SurvivalPlus; +import de.viper.survivalplus.gui.ShopGui; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +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; + +public class ShopCommand implements CommandExecutor { + + private final ShopManager shopManager; + private final SurvivalPlus plugin; + + public ShopCommand(SurvivalPlus plugin) { + this.plugin = plugin; + this.shopManager = new ShopManager(plugin); + } + + private String getMessage(String key) { + return plugin.getMessage("shop." + key); + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage(getMessage("only-players")); + return true; + } + Player player = (Player) sender; + + // Shop-GUI öffnen, wenn kein Argument oder "gui" + if (args.length == 0 || (args.length > 0 && args[0].toLowerCase().equals("gui"))) { + ShopGui shopGui = new ShopGui(plugin, player, shopManager); + Bukkit.getPluginManager().registerEvents(shopGui, plugin); + player.openInventory(shopGui.getInventory()); + return true; + } + + if (args.length < 3) { + sender.sendMessage(getMessage("usage-add")); + return false; + } + + if (!args[0].toLowerCase().equals("add")) { + sender.sendMessage(getMessage("unknown-subcommand")); + return false; + } + + 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!"); + return true; + } + + String itemKey = itemInHand.getType().name().toLowerCase(); + + double basePrice; + int stock; + + try { + basePrice = Double.parseDouble(args[1]); + stock = Integer.parseInt(args[2]); + } catch (NumberFormatException e) { + sender.sendMessage(getMessage("number-error")); + return false; + } + + shopManager.addOrUpdateItem(itemKey, basePrice, stock); + + String msg = getMessage("item-added") + .replace("{item}", itemKey) + .replace("{price}", args[1]) + .replace("{stock}", args[2]); + sender.sendMessage(msg); + + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/ShowArmorStandsCommand.java b/src/main/java/de/viper/survivalplus/commands/ShowArmorStandsCommand.java new file mode 100644 index 0000000..8681e65 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/ShowArmorStandsCommand.java @@ -0,0 +1,43 @@ +package de.viper.survivalplus.commands; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Entity; +import org.bukkit.plugin.java.JavaPlugin; + +public class ShowArmorStandsCommand implements CommandExecutor { + + private final JavaPlugin plugin; + + public ShowArmorStandsCommand(JavaPlugin plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!sender.hasPermission("survivalplus.showarmorstands")) { + sender.sendMessage("§cDu hast keine Rechte für diesen Befehl."); + return true; + } + + int count = 0; + for (World world : Bukkit.getWorlds()) { + for (Entity entity : world.getEntities()) { + if (entity instanceof ArmorStand) { + ArmorStand armorStand = (ArmorStand) entity; + if (!armorStand.isVisible()) { + armorStand.setVisible(true); + count++; + } + } + } + } + + sender.sendMessage("§a" + count + " unsichtbare Armor Stands wurden sichtbar gemacht."); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/ShowReportCommand.java b/src/main/java/de/viper/survivalplus/commands/ShowReportCommand.java new file mode 100644 index 0000000..3c79179 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/ShowReportCommand.java @@ -0,0 +1,52 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import de.viper.survivalplus.report.ReportManager; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; + +public class ShowReportCommand implements CommandExecutor { + + private final SurvivalPlus plugin; + private final ReportManager reportManager; + + public ShowReportCommand(SurvivalPlus plugin, ReportManager reportManager) { + this.plugin = plugin; + this.reportManager = reportManager; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage(plugin.getLangConfig().getString("showreport.only-players", "§cNur Spieler können diesen Befehl ausführen!")); + return true; + } + + if (!player.hasPermission("survivalplus.report.show")) { + player.sendMessage(plugin.getLangConfig().getString("no-permission", "§cDu hast keine Berechtigung!")); + return true; + } + + if (args.length != 1) { + player.sendMessage(plugin.getLangConfig().getString("showreport.usage", "§cVerwendung: /showreport ")); + return true; + } + + String targetName = args[0]; + List reports = reportManager.getReports(targetName); + + if (reports.isEmpty()) { + player.sendMessage(plugin.getLangConfig().getString("showreport.no-reports", "§aEs wurden keine Reports für diesen Spieler gefunden.")); + return true; + } + + player.sendMessage("§6--- Reports für Spieler " + targetName + " ---"); + reports.forEach(r -> player.sendMessage("§7" + r)); + + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/SitCommand.java b/src/main/java/de/viper/survivalplus/commands/SitCommand.java new file mode 100644 index 0000000..a21454b --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/SitCommand.java @@ -0,0 +1,57 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import de.viper.survivalplus.listeners.SitListener; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.configuration.file.FileConfiguration; + +public class SitCommand implements CommandExecutor { + private final SurvivalPlus plugin; + private final SitListener sitListener; + + public SitCommand(SurvivalPlus plugin, SitListener sitListener) { + this.plugin = plugin; + this.sitListener = sitListener; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage(plugin.getLangConfig().getString("player-only", "§cDieser Befehl ist nur für Spieler!")); + return true; + } + + Player player = (Player) sender; + FileConfiguration lang = plugin.getLangConfig(); + + if (!player.hasPermission("survivalplus.sit")) { + player.sendMessage(lang.getString("no-permission", "§cDu hast keine Berechtigung für diesen Befehl!")); + return true; + } + + if (args.length != 0) { + player.sendMessage(lang.getString("sit.usage", "§cVerwendung: /sit")); + return true; + } + + // Prüfe, ob der Spieler bereits sitzt + if (sitListener.isSitting(player)) { + sitListener.standUp(player); + player.sendMessage(lang.getString("sit.stand-up", "§aDu bist aufgestanden!")); + return true; + } + + // Setze den Spieler mittig auf den Block und leicht oberhalb (Fix) + Location location = player.getLocation(); + location.setX(location.getBlockX() + 0.5); + location.setY(location.getBlockY() + 0.25); + location.setZ(location.getBlockZ() + 0.5); + + sitListener.sitPlayer(player, location); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/SpawnCommand.java b/src/main/java/de/viper/survivalplus/commands/SpawnCommand.java new file mode 100644 index 0000000..851d72f --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/SpawnCommand.java @@ -0,0 +1,39 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.Location; +import org.bukkit.World; + +public class SpawnCommand implements CommandExecutor { + + private final SurvivalPlus plugin; + + public SpawnCommand(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage(plugin.getMessage("spawn.only_player")); + return true; + } + + Player player = (Player) sender; + World world = player.getWorld(); + Location spawnLocation = world.getSpawnLocation(); + + if (spawnLocation == null) { + player.sendMessage(plugin.getMessage("spawn.no_spawn_found")); + return true; + } + + player.teleport(spawnLocation); + player.sendMessage(plugin.getMessage("spawn.teleported")); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/StartFunChallengeCommand.java b/src/main/java/de/viper/survivalplus/commands/StartFunChallengeCommand.java new file mode 100644 index 0000000..a89cf1a --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/StartFunChallengeCommand.java @@ -0,0 +1,50 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.fun.FunChallenge; +import de.viper.survivalplus.fun.FunChallengeManager; +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class StartFunChallengeCommand implements CommandExecutor { + + private final SurvivalPlus plugin; + private final FunChallengeManager challengeManager; + + public StartFunChallengeCommand(SurvivalPlus plugin, FunChallengeManager challengeManager) { + this.plugin = plugin; + this.challengeManager = challengeManager; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage(plugin.getLangConfig().getString("challenge.only_players")); + return true; + } + if (args.length < 1) { + sender.sendMessage(plugin.getLangConfig().getString("challenge.usage")); + return true; + } + String challengeName = args[0]; + for (FunChallenge fc : challengeManager.getChallenges()) { + if (fc.getName().equalsIgnoreCase(challengeName)) { + fc.setActive(true); + String msg = plugin.getLangConfig() + .getString("challenge.start_success") + .replace("%challenge%", fc.getDescription()); + sender.sendMessage(msg); + // Hier ggf. Timer und Tracking starten! + plugin.saveConfig(); + return true; + } + } + String failMsg = plugin.getLangConfig() + .getString("challenge.start_fail") + .replace("%challenge%", challengeName); + sender.sendMessage(failMsg); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/StatsCommand.java b/src/main/java/de/viper/survivalplus/commands/StatsCommand.java new file mode 100644 index 0000000..0bd6a37 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/StatsCommand.java @@ -0,0 +1,69 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.Manager.StatsManager; +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class StatsCommand implements CommandExecutor { + + private final SurvivalPlus plugin; + private final StatsManager statsManager; + + public StatsCommand(SurvivalPlus plugin, StatsManager statsManager) { + this.plugin = plugin; + this.statsManager = statsManager; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + + if (!(sender instanceof Player)) { + sender.sendMessage(plugin.getMessage("stats.only_player")); + return true; + } + + Player player = (Player) sender; + + long time = statsManager.getPlayTime(player.getUniqueId()); + int kills = statsManager.getKills(player.getUniqueId()); + int deaths = statsManager.getDeaths(player.getUniqueId()); + int placed = statsManager.getBlocksPlaced(player.getUniqueId()); + int broken = statsManager.getBlocksBroken(player.getUniqueId()); + + if (time == 0 && kills == 0 && deaths == 0 && placed == 0 && broken == 0) { + player.sendMessage(plugin.getMessage("stats.no_stats")); + return true; + } + + // K/D Ratio berechnen (vermeide Division durch 0) + String kdRatio = deaths > 0 ? String.format("%.2f", (double) kills / deaths) : "∞"; + + // Zusätzliche Minecraft Statistik + int jumps = player.getStatistic(org.bukkit.Statistic.JUMP); + double walkKm = player.getStatistic(org.bukkit.Statistic.WALK_ONE_CM) / 100_000.0; // cm zu km + + player.sendMessage(plugin.getMessage("stats.header").replace("{player}", player.getName())); + player.sendMessage(plugin.getMessage("stats.playtime").replace("{time}", formatPlayTime(time))); + player.sendMessage(plugin.getMessage("stats.kills").replace("{kills}", String.valueOf(kills))); + player.sendMessage(plugin.getMessage("stats.deaths").replace("{deaths}", String.valueOf(deaths))); + player.sendMessage(plugin.getMessage("stats.kd_ratio").replace("{kd}", kdRatio)); + player.sendMessage(plugin.getMessage("stats.blocks_placed").replace("{placed}", String.valueOf(placed))); + player.sendMessage(plugin.getMessage("stats.blocks_broken").replace("{broken}", String.valueOf(broken))); + player.sendMessage(plugin.getMessage("stats.jumps").replace("{jumps}", String.valueOf(jumps))); + player.sendMessage(plugin.getMessage("stats.walked").replace("{walked}", String.format("%.2f", walkKm))); + player.sendMessage(plugin.getMessage("stats.footer")); + + return true; + } + + private String formatPlayTime(long seconds) { + long hours = seconds / 3600; + long minutes = (seconds % 3600) / 60; + long secs = seconds % 60; + + return String.format("%02dh %02dm %02ds", hours, minutes, secs); + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/TeleportCommands.java b/src/main/java/de/viper/survivalplus/commands/TeleportCommands.java new file mode 100644 index 0000000..84caa4c --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/TeleportCommands.java @@ -0,0 +1,157 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class TeleportCommands implements CommandExecutor { + + private final SurvivalPlus plugin; + private final Map teleportRequests = new HashMap<>(); + + public TeleportCommands(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage(plugin.getMessage("only-players")); + return true; + } + + Player player = (Player) sender; + String cmd = label.toLowerCase(); + + switch (cmd) { + case "tp": + return handleTp(player, args); + case "tphere": + return handleTphere(player, args); + case "tpa": + return handleTpa(player, args); + case "tpaccept": + return handleTpAccept(player); + case "tpdeny": + return handleTpDeny(player); + default: + return false; + } + } + + private boolean handleTp(Player sender, String[] args) { + if (!sender.hasPermission("survivalplus.tp")) { + sender.sendMessage(plugin.getMessage("no-permission")); + return true; + } + if (args.length != 1) { + sender.sendMessage(plugin.getMessage("teleport-usage")); + return true; + } + + Player target = Bukkit.getPlayer(args[0]); + if (target == null) { + sender.sendMessage(plugin.getMessage("player-not-found").replace("%player%", args[0])); + return true; + } + + sender.teleport(target); + sender.sendMessage(plugin.getMessage("teleport-success").replace("%player%", target.getName())); + return true; + } + + private boolean handleTphere(Player sender, String[] args) { + if (!sender.hasPermission("survivalplus.tphere")) { + sender.sendMessage(plugin.getMessage("no-permission")); + return true; + } + if (args.length != 1) { + sender.sendMessage(plugin.getMessage("tphere-usage")); + return true; + } + + Player target = Bukkit.getPlayer(args[0]); + if (target == null) { + sender.sendMessage(plugin.getMessage("player-not-found").replace("%player%", args[0])); + return true; + } + + target.teleport(sender); + sender.sendMessage(plugin.getMessage("tphere-success").replace("%player%", target.getName())); + return true; + } + + private boolean handleTpa(Player sender, String[] args) { + if (!sender.hasPermission("survivalplus.tpa")) { + sender.sendMessage(plugin.getMessage("no-permission")); + return true; + } + if (args.length != 1) { + sender.sendMessage(plugin.getMessage("tpa-usage")); + return true; + } + + Player target = Bukkit.getPlayer(args[0]); + if (target == null) { + sender.sendMessage(plugin.getMessage("player-not-found").replace("%player%", args[0])); + return true; + } + + teleportRequests.put(target.getUniqueId(), sender.getUniqueId()); + sender.sendMessage(plugin.getMessage("tpa-sent").replace("%player%", target.getName())); + target.sendMessage(plugin.getMessage("tpa-received").replace("%player%", sender.getName())); + return true; + } + + private boolean handleTpAccept(Player target) { + if (!target.hasPermission("survivalplus.tpaccept")) { + target.sendMessage(plugin.getMessage("no-permission")); + return true; + } + + UUID senderId = teleportRequests.remove(target.getUniqueId()); + if (senderId == null) { + target.sendMessage(plugin.getMessage("no-tpa-request")); + return true; + } + + Player sender = Bukkit.getPlayer(senderId); + if (sender == null) { + target.sendMessage(plugin.getMessage("player-not-found")); + return true; + } + + sender.teleport(target); + sender.sendMessage(plugin.getMessage("tpa-accepted").replace("%player%", target.getName())); + target.sendMessage(plugin.getMessage("tpaccept-success").replace("%player%", sender.getName())); + return true; + } + + private boolean handleTpDeny(Player target) { + if (!target.hasPermission("survivalplus.tpdeny")) { + target.sendMessage(plugin.getMessage("no-permission")); + return true; + } + + UUID senderId = teleportRequests.remove(target.getUniqueId()); + if (senderId == null) { + target.sendMessage(plugin.getMessage("no-tpa-request")); + return true; + } + + Player sender = Bukkit.getPlayer(senderId); + if (sender != null) { + sender.sendMessage(plugin.getMessage("tpa-denied").replace("%player%", target.getName())); + } + + target.sendMessage(plugin.getMessage("tpdeny-success")); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/TradeAcceptCommand.java b/src/main/java/de/viper/survivalplus/commands/TradeAcceptCommand.java new file mode 100644 index 0000000..5f1f5c4 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/TradeAcceptCommand.java @@ -0,0 +1,35 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import de.viper.survivalplus.trade.TradeManager; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class TradeAcceptCommand implements CommandExecutor { + + private final TradeManager tradeManager; + private final SurvivalPlus plugin; + + public TradeAcceptCommand(SurvivalPlus plugin, TradeManager tradeManager) { + this.plugin = plugin; + this.tradeManager = tradeManager; + } + + @Override + public boolean onCommand(CommandSender senderS, Command command, String label, String[] args) { + if (!(senderS instanceof Player target)) { + senderS.sendMessage(plugin.getLangConfig().getString("only-players", "§cNur Spieler können diesen Befehl ausführen!")); + return true; + } + + if (args.length != 1) { + target.sendMessage(plugin.getLangConfig().getString("trade.accept.usage", "§cVerwendung: /tradeaccept ")); + return true; + } + + tradeManager.acceptTrade(target, args[0]); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/TradeCommand.java b/src/main/java/de/viper/survivalplus/commands/TradeCommand.java new file mode 100644 index 0000000..e31d69e --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/TradeCommand.java @@ -0,0 +1,42 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.SurvivalPlus; +import de.viper.survivalplus.trade.TradeManager; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class TradeCommand implements CommandExecutor { + + private final SurvivalPlus plugin; + private final TradeManager tradeManager; + + public TradeCommand(SurvivalPlus plugin, TradeManager tradeManager) { + this.plugin = plugin; + this.tradeManager = tradeManager; + } + + @Override + public boolean onCommand(CommandSender senderS, Command command, String label, String[] args) { + if (!(senderS instanceof Player sender)) { + senderS.sendMessage(plugin.getLangConfig().getString("only-players", "§cNur Spieler können diesen Befehl ausführen!")); + return true; + } + + if (args.length != 1) { + sender.sendMessage(plugin.getLangConfig().getString("trade.usage", "§cVerwendung: /trade ")); + return true; + } + + Player target = Bukkit.getPlayerExact(args[0]); + if (target == null || !target.isOnline()) { + sender.sendMessage(plugin.getLangConfig().getString("trade.player-not-online", "§cDieser Spieler ist nicht online!")); + return true; + } + + tradeManager.requestTrade(sender, target); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/TrashCommand.java b/src/main/java/de/viper/survivalplus/commands/TrashCommand.java new file mode 100644 index 0000000..b3e373d --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/TrashCommand.java @@ -0,0 +1,23 @@ +package de.viper.survivalplus.commands; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; + +public class TrashCommand implements CommandExecutor { + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage("Nur Spieler können diesen Befehl ausführen."); + return true; + } + + Inventory trash = Bukkit.createInventory(player, 9 * 3, "Trash"); + player.openInventory(trash); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/UnblockCommand.java b/src/main/java/de/viper/survivalplus/commands/UnblockCommand.java new file mode 100644 index 0000000..460fc72 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/UnblockCommand.java @@ -0,0 +1,48 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.Manager.BlockManager; +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +import org.bukkit.configuration.file.FileConfiguration; + +public class UnblockCommand implements CommandExecutor { + + private final BlockManager blockManager; + private final FileConfiguration config; + + public UnblockCommand(BlockManager blockManager, FileConfiguration config) { + this.blockManager = blockManager; + this.config = config; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + + if (!(sender instanceof Player player)) { + sender.sendMessage(config.getString("messages.general.only_players")); + return true; + } + + if (args.length != 1) { + player.sendMessage(config.getString("messages.unblock.usage")); + return true; + } + + Player target = Bukkit.getPlayerExact(args[0]); + if (target == null || target == player) { + player.sendMessage(config.getString("messages.unblock.invalid_player")); + return true; + } + + if (blockManager.hasBlocked(player, target)) { + blockManager.unblockPlayer(player, target); + player.sendMessage(config.getString("messages.unblock.unblocked").replace("%player%", target.getName())); + target.sendMessage(config.getString("messages.unblock.unblocked_by").replace("%player%", player.getName())); + } else { + player.sendMessage(config.getString("messages.unblock.not_blocked").replace("%player%", target.getName())); + } + + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/WarpsCommand.java b/src/main/java/de/viper/survivalplus/commands/WarpsCommand.java new file mode 100644 index 0000000..08cc80b --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/WarpsCommand.java @@ -0,0 +1,75 @@ +package de.viper.survivalplus.commands; + +import de.viper.survivalplus.Manager.Warp; +import de.viper.survivalplus.Manager.WarpManager; +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.*; +import java.util.stream.Collectors; + +public class WarpsCommand implements CommandExecutor { + + private final SurvivalPlus plugin; + private final WarpManager warpManager; + + public WarpsCommand(SurvivalPlus plugin, WarpManager warpManager) { + this.plugin = plugin; + this.warpManager = warpManager; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage(plugin.getMessage("warp.only_players")); + return true; + } + Player player = (Player) sender; + + Map> warpsByOwner = warpManager.getWarps().values().stream() + .collect(Collectors.groupingBy(Warp::getOwner)); + + List ownersSorted = new ArrayList<>(warpsByOwner.keySet()); + Collections.sort(ownersSorted); + + int totalWarps = warpManager.getWarps().size(); + int size = ((totalWarps + 8) / 9) * 9; + if (size < 27) size = 27; // mindestens 27 Slots + + Inventory inv = Bukkit.createInventory(null, size, ChatColor.GOLD + "Player Warps"); + + int slot = 0; + for (String owner : ownersSorted) { + for (Warp warp : warpsByOwner.get(owner)) { + if (slot >= size) break; + + ItemStack item = warp.getItem().clone(); + item.setAmount(1); + ItemMeta meta = item.getItemMeta(); + if (meta != null) { + meta.setDisplayName(ChatColor.YELLOW + warp.getName()); + List lore = new ArrayList<>(); + lore.add(ChatColor.GRAY + "Owner: " + warp.getOwner()); + lore.add(ChatColor.GRAY + "World: " + warp.getWorldName()); + meta.setLore(lore); + meta.setUnbreakable(true); + item.setItemMeta(meta); + } + + inv.setItem(slot++, item); + } + if (slot >= size) break; + } + + player.openInventory(inv); + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/commands/WorkbenchCommand.java b/src/main/java/de/viper/survivalplus/commands/WorkbenchCommand.java new file mode 100644 index 0000000..48df4c4 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/commands/WorkbenchCommand.java @@ -0,0 +1,20 @@ +package de.viper.survivalplus.commands; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class WorkbenchCommand implements CommandExecutor { + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage("Nur Spieler können diesen Befehl ausführen."); + return true; + } + + player.openWorkbench(null, true); // Öffnet die Werkbank-GUI für den Spieler + return true; + } +} diff --git a/src/main/java/de/viper/survivalplus/fun/FunChallenge.java b/src/main/java/de/viper/survivalplus/fun/FunChallenge.java new file mode 100644 index 0000000..959ceaf --- /dev/null +++ b/src/main/java/de/viper/survivalplus/fun/FunChallenge.java @@ -0,0 +1,39 @@ +package de.viper.survivalplus.fun; + +public class FunChallenge { + + private String name; + private String description; + private String type; + private String item; + private int amount; + private int timeLimitMinutes; + private boolean active; + private String reward; + + public FunChallenge() {} + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + + public String getDescription() { return description; } + public void setDescription(String description) { this.description = description; } + + public String getType() { return type; } + public void setType(String type) { this.type = type; } + + public String getItem() { return item; } + public void setItem(String item) { this.item = item; } + + public int getAmount() { return amount; } + public void setAmount(int amount) { this.amount = amount; } + + public int getTimeLimitMinutes() { return timeLimitMinutes; } + public void setTimeLimitMinutes(int timeLimitMinutes) { this.timeLimitMinutes = timeLimitMinutes; } + + public boolean isActive() { return active; } + public void setActive(boolean active) { this.active = active; } + + public String getReward() { return reward; } + public void setReward(String reward) { this.reward = reward; } +} diff --git a/src/main/java/de/viper/survivalplus/fun/FunChallengeManager.java b/src/main/java/de/viper/survivalplus/fun/FunChallengeManager.java new file mode 100644 index 0000000..62989fd --- /dev/null +++ b/src/main/java/de/viper/survivalplus/fun/FunChallengeManager.java @@ -0,0 +1,122 @@ +package de.viper.survivalplus.fun; + +import org.bukkit.Bukkit; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class FunChallengeManager { + + private final List challenges = new ArrayList<>(); + private final Map globalProgress = new HashMap<>(); // Fortschritt global für alle Spieler + private final Map playerBossBars = new HashMap<>(); + private BukkitRunnable bossBarTask; + + public void load(FileConfiguration config) { + challenges.clear(); + globalProgress.clear(); + if (config.contains("funChallenges")) { + for (Map entry : config.getMapList("funChallenges")) { + FunChallenge fc = new FunChallenge(); + fc.setName((String) entry.get("name")); + fc.setDescription((String) entry.get("description")); + fc.setType((String) entry.get("type")); + fc.setItem((String) entry.get("item")); + fc.setAmount((Integer) entry.get("amount")); + fc.setTimeLimitMinutes((Integer) entry.get("timeLimitMinutes")); + fc.setActive(Boolean.parseBoolean(entry.get("active").toString())); + fc.setReward((String) entry.get("reward")); + challenges.add(fc); + globalProgress.put(fc.getName(), 0); + } + } + startBossBar(); // automatisch BossBar starten, falls eine Challenge aktiv ist + } + + public List getChallenges() { return challenges; } + + public FunChallenge getActiveChallenge() { + for (FunChallenge c : challenges) { + if (c.isActive()) return c; + } + return null; + } + + public int getGlobalProgress(String challengeName) { + return globalProgress.getOrDefault(challengeName, 0); + } + + public void setGlobalProgress(String challengeName, int amount) { + globalProgress.put(challengeName, amount); + } + + public void resetProgress(String challengeName) { + globalProgress.put(challengeName, 0); + } + + // ====================== BossBar-Funktion ====================== + + public void startBossBar() { + FunChallenge active = getActiveChallenge(); + if (active == null) return; + + stopBossBar(); // alte Bars entfernen, falls vorhanden + + int totalTime = active.getTimeLimitMinutes() * 60; // Gesamtzeit in Sekunden + final int[] timeLeft = {totalTime}; + + bossBarTask = new BukkitRunnable() { + @Override + public void run() { + if (timeLeft[0] <= 0 || getActiveChallenge() == null) { + stopBossBar(); + return; + } + + double progress = (double) timeLeft[0] / totalTime; + String formattedTime = formatTime(timeLeft[0]); + + for (Player player : Bukkit.getOnlinePlayers()) { + BossBar bar = playerBossBars.computeIfAbsent(player, p -> { + BossBar newBar = Bukkit.createBossBar( + active.getName() + " (" + formattedTime + ")", + BarColor.BLUE, + BarStyle.SOLID + ); + newBar.addPlayer(p); + return newBar; + }); + + bar.setProgress(progress); + bar.setTitle(active.getName() + " (" + formattedTime + ")"); + } + + timeLeft[0]--; + } + }; + + bossBarTask.runTaskTimer(Bukkit.getPluginManager().getPlugin("SurvivalPlus"), 0L, 20L); + } + + public void stopBossBar() { + if (bossBarTask != null) bossBarTask.cancel(); + for (BossBar bar : playerBossBars.values()) { + bar.removeAll(); + } + playerBossBars.clear(); + } + + private String formatTime(int seconds) { + int minutes = seconds / 60; + int sec = seconds % 60; + return String.format("%02d:%02d", minutes, sec); + } +} diff --git a/src/main/java/de/viper/survivalplus/gui/ShopGui.java b/src/main/java/de/viper/survivalplus/gui/ShopGui.java new file mode 100644 index 0000000..2e7b02a --- /dev/null +++ b/src/main/java/de/viper/survivalplus/gui/ShopGui.java @@ -0,0 +1,129 @@ +package de.viper.survivalplus.gui; + +import de.viper.survivalplus.Manager.ShopManager; +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.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.Arrays; + +public class ShopGui implements Listener { + + private final ShopManager shopManager; + private final Player player; + private Inventory inv; + private final SurvivalPlus plugin; + + public ShopGui(SurvivalPlus plugin, Player player, ShopManager shopManager) { + this.plugin = plugin; + this.player = player; + this.shopManager = shopManager; + createInventory(); + } + + private void createInventory() { + inv = Bukkit.createInventory(null, 27, "Shop"); + + if (shopManager.getShopConfig().getConfigurationSection("items") == null) { + player.sendMessage(ChatColor.RED + "Der Shop ist momentan leer."); + return; + } + + // Alle Items aus shop.yml einlesen + for (String itemKey : shopManager.getShopConfig().getConfigurationSection("items").getKeys(false)) { + Material mat = getMaterialFromKey(itemKey); + if (mat == null) { + player.sendMessage(ChatColor.RED + "Material für '" + itemKey + "' konnte nicht gefunden werden."); + continue; + } + + addShopItem(itemKey, mat, 64); + addShopItem(itemKey, mat, 16); + addShopItem(itemKey, mat, 1); + } + + player.openInventory(inv); + } + + private Material getMaterialFromKey(String key) { + try { + return Material.valueOf(key.toUpperCase()); + } catch (IllegalArgumentException e) { + return null; + } + } + + private void addShopItem(String itemKey, Material material, int amount) { + double pricePerUnit = shopManager.getCurrentPrice(itemKey); + double totalPrice = pricePerUnit * amount; + + ItemStack item = new ItemStack(material, amount); + ItemMeta meta = item.getItemMeta(); + if (meta != null) { + meta.setDisplayName(ChatColor.GREEN.toString() + amount + "x " + material.name()); + meta.setLore(Arrays.asList( + ChatColor.YELLOW + "Preis pro Stück: " + pricePerUnit, + ChatColor.YELLOW + "Gesamtpreis: " + totalPrice, + ChatColor.GRAY + "Klicke, um zu kaufen" + )); + item.setItemMeta(meta); + } + + for (int i = 0; i < inv.getSize(); i++) { + if (inv.getItem(i) == null) { + inv.setItem(i, item); + break; + } + } + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent e) { + if (!e.getView().getTitle().equals("Shop")) return; + if (e.getClickedInventory() == null) return; + if (!e.getWhoClicked().equals(player)) return; + + e.setCancelled(true); + + ItemStack clicked = e.getCurrentItem(); + if (clicked == null || clicked.getType() == Material.AIR) return; + + int amount = clicked.getAmount(); + Material mat = clicked.getType(); + String itemKey = mat.name().toLowerCase(); + + if (!shopManager.buyItem(itemKey, amount)) { + player.sendMessage(ChatColor.RED + "Nicht genügend Bestand im Shop."); + return; + } + + double totalPrice = shopManager.getCurrentPrice(itemKey) * amount; + + // TODO: Economy Abzug einfügen + + player.getInventory().addItem(new ItemStack(mat, amount)); + player.sendMessage(ChatColor.GREEN + "Du hast " + amount + "x " + mat.name() + " für " + totalPrice + " Coins gekauft."); + + player.closeInventory(); + } + + @EventHandler + public void onInventoryClose(InventoryCloseEvent e) { + if (e.getView().getTitle().equals("Shop") && e.getPlayer().equals(player)) { + // Optional: Listener entfernen oder Aufräumarbeiten hier + } + } + + public Inventory getInventory() { + return inv; + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/AFKListener.java b/src/main/java/de/viper/survivalplus/listeners/AFKListener.java new file mode 100644 index 0000000..515d303 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/AFKListener.java @@ -0,0 +1,115 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.SurvivalPlus; +import de.viper.survivalplus.tasks.AFKManager; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.event.Listener; +import org.bukkit.event.player.*; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.scheduler.BukkitRunnable; + +public class AFKListener implements Listener { + + private final SurvivalPlus plugin; + private AFKManager afkManager; + private boolean afkEnabled; + private FileConfiguration config; + + private BukkitRunnable afkTask; + + public AFKListener(SurvivalPlus plugin) { + this.plugin = plugin; + this.config = plugin.getConfig(); + loadSettings(); + if (afkEnabled) { + startTask(); + } + } + + private void loadSettings() { + this.afkEnabled = config.getBoolean("afk.enabled", true); + long afkAfter = config.getLong("afk.afk-after-seconds", 300); + long kickAfter = config.getLong("afk.kick-after-seconds", 0); + this.afkManager = new AFKManager(plugin, afkAfter, kickAfter); + } + + private void startTask() { + if (afkTask != null) { + afkTask.cancel(); + } + afkTask = new BukkitRunnable() { + @Override + public void run() { + for (Player player : Bukkit.getOnlinePlayers()) { + afkManager.checkAFKStatus(player); + } + } + }; + afkTask.runTaskTimer(plugin, 60L, 100L); + } + + private void handleActivity(Player player) { + if (!afkEnabled) return; + afkManager.updateActivity(player); + } + + @org.bukkit.event.EventHandler + public void onMove(PlayerMoveEvent event) { + if (!event.getFrom().toVector().equals(event.getTo().toVector())) { + handleActivity(event.getPlayer()); + } + } + + @org.bukkit.event.EventHandler + public void onChat(AsyncPlayerChatEvent event) { + handleActivity(event.getPlayer()); + } + + @org.bukkit.event.EventHandler + public void onInteract(PlayerInteractEvent event) { + handleActivity(event.getPlayer()); + } + + @org.bukkit.event.EventHandler + public void onInventoryClick(InventoryClickEvent event) { + if (event.getWhoClicked() instanceof Player player) { + handleActivity(player); + } + } + + @org.bukkit.event.EventHandler + public void onBlockPlace(BlockPlaceEvent event) { + handleActivity(event.getPlayer()); + } + + @org.bukkit.event.EventHandler + public void onBlockBreak(BlockBreakEvent event) { + handleActivity(event.getPlayer()); + } + + @org.bukkit.event.EventHandler + public void onJoin(PlayerJoinEvent event) { + afkManager.updateActivity(event.getPlayer()); + } + + @org.bukkit.event.EventHandler + public void onQuit(PlayerQuitEvent event) { + afkManager.reset(event.getPlayer()); + } + + public void reloadConfig(FileConfiguration config) { + this.config = config; + loadSettings(); + if (afkTask != null) { + afkTask.cancel(); + afkTask = null; + } + if (afkEnabled) { + startTask(); + } + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/ArmorStandDestroyListener.java b/src/main/java/de/viper/survivalplus/listeners/ArmorStandDestroyListener.java new file mode 100644 index 0000000..0a1738a --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/ArmorStandDestroyListener.java @@ -0,0 +1,27 @@ +package de.viper.survivalplus.listeners; + +import org.bukkit.ChatColor; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; + +public class ArmorStandDestroyListener implements Listener { + + @EventHandler + public void onArmorStandInteract(PlayerInteractAtEntityEvent event) { + if (!(event.getRightClicked() instanceof ArmorStand armorStand)) return; + + Player player = event.getPlayer(); + + if (!player.hasPermission("survivalplus.armorstand.destroy")) { + player.sendMessage(ChatColor.RED + "Du hast keine Berechtigung, diesen ArmorStand zu entfernen."); + return; + } + + armorStand.remove(); + player.sendMessage(ChatColor.YELLOW + "ArmorStand wurde erfolgreich entfernt."); + event.setCancelled(true); + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/BackpackListener.java b/src/main/java/de/viper/survivalplus/listeners/BackpackListener.java new file mode 100644 index 0000000..7e4382e --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/BackpackListener.java @@ -0,0 +1,111 @@ +package de.viper.survivalplus.listeners; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +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.inventory.InventoryCloseEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.io.File; +import java.io.IOException; +import java.util.UUID; +import java.util.logging.Logger; + +public class BackpackListener implements Listener { + + private final FileConfiguration backpackConfig; + private final FileConfiguration langConfig; + private final Logger logger; + private final File backpackFile; + + public BackpackListener(FileConfiguration backpackConfig, FileConfiguration langConfig, Logger logger, File backpackFile) { + this.backpackConfig = backpackConfig; + this.langConfig = langConfig; + this.logger = logger; + this.backpackFile = backpackFile; + } + + @EventHandler + public void onRightClick(PlayerInteractEvent event) { + if (!event.hasItem() || !event.getAction().toString().contains("RIGHT")) return; + + Player player = event.getPlayer(); + ItemStack item = event.getItem(); + + if (item == null || item.getType() != Material.CHEST) { + logger.fine("Kein Rucksack-Item: " + (item != null ? item.getType() : "null")); + return; + } + if (!item.hasItemMeta()) { + logger.fine("Item hat kein ItemMeta: " + item.getType()); + return; + } + + ItemMeta meta = item.getItemMeta(); + if (!meta.hasDisplayName()) { + logger.fine("Item hat keinen Display-Namen: " + item.getType()); + return; + } + + String expectedName = ChatColor.translateAlternateColorCodes('&', langConfig.getString("backpack.name", "&eRucksack")); + if (!meta.getDisplayName().equals(expectedName)) { + logger.fine("Item hat falschen Namen: " + meta.getDisplayName() + ", erwartet: " + expectedName); + return; + } + + event.setCancelled(true); + + UUID uuid = player.getUniqueId(); + String inventoryTitle = ChatColor.translateAlternateColorCodes('&', langConfig.getString("backpack.inventory-title", "&eDein Rucksack")); + + Inventory backpackInv = Bukkit.createInventory(null, 27, inventoryTitle); + logger.info("Öffne Rucksack-Inventar für Spieler " + player.getName() + " (" + uuid + ")"); + + for (int i = 0; i < 27; i++) { + String path = uuid + ".slot" + i; + if (backpackConfig.contains(path)) { + backpackInv.setItem(i, backpackConfig.getItemStack(path)); + } + } + + player.openInventory(backpackInv); + } + + @EventHandler + public void onClose(InventoryCloseEvent event) { + String expectedTitle = ChatColor.translateAlternateColorCodes('&', langConfig.getString("backpack.inventory-title", "&eDein Rucksack")); + if (!event.getView().getTitle().equals(expectedTitle)) return; + + Player player = (Player) event.getPlayer(); + UUID uuid = player.getUniqueId(); + Inventory inv = event.getInventory(); + + logger.info("Speichere Rucksack-Inventar für Spieler " + player.getName() + " (" + uuid + ")"); + + for (int i = 0; i < inv.getSize(); i++) { + String path = uuid + ".slot" + i; + ItemStack item = inv.getItem(i); + backpackConfig.set(path, item); + } + + try { + if (!backpackFile.exists()) { + backpackFile.createNewFile(); + logger.info("backpacks.yml wurde erstellt."); + } + backpackConfig.save(backpackFile); + logger.info("Rucksack-Inventar gespeichert für " + player.getName()); + } catch (IOException e) { + player.sendMessage(ChatColor.RED + "Fehler beim Speichern deines Rucksacks."); + logger.severe("Fehler beim Speichern der backpacks.yml: " + e.getMessage()); + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/listeners/BlockDetectionListener.java b/src/main/java/de/viper/survivalplus/listeners/BlockDetectionListener.java new file mode 100644 index 0000000..5796d0a --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/BlockDetectionListener.java @@ -0,0 +1,115 @@ +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.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityPickupItemEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +public class BlockDetectionListener implements Listener { + + private final SurvivalPlus plugin; + + public BlockDetectionListener(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + scanAndHandle(player, false); + } + + @EventHandler + public void onPickUp(EntityPickupItemEvent event) { + if (!(event.getEntity() instanceof Player)) return; + Player player = (Player) event.getEntity(); + scanAndHandle(player, true); + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + if (!(event.getWhoClicked() instanceof Player)) return; + Player player = (Player) event.getWhoClicked(); + scanAndHandle(player, true); + } + + @EventHandler + public void onBlockPlace(BlockPlaceEvent event) { + Player player = event.getPlayer(); + if (player.hasPermission("survivalplus.notify")) { + return; // Admins dürfen Blöcke platzieren + } + Material material = event.getBlock().getType(); + boolean cmdAllowed = plugin.getConfig().getBoolean("blocks.command-blocks.enabled", true); + boolean structAllowed = plugin.getConfig().getBoolean("blocks.structure-blocks.enabled", true); + + if ((!cmdAllowed && material == Material.COMMAND_BLOCK) || + (!structAllowed && material == Material.STRUCTURE_BLOCK)) { + event.setCancelled(true); + player.sendMessage(ChatColor.RED + "[SurvivalPlus] Du darfst diesen Block hier nicht platzieren!"); + } + } + + private void scanAndHandle(Player player, boolean notifyPlayer) { + if (player.hasPermission("survivalplus.notify")) { + return; // Admins dürfen Blöcke behalten + } + Inventory inventory = player.getInventory(); + boolean hasCommandBlock = false; + boolean hasStructureBlock = false; + boolean cmdAllowed = plugin.getConfig().getBoolean("blocks.command-blocks.enabled", true); + boolean structAllowed = plugin.getConfig().getBoolean("blocks.structure-blocks.enabled", true); + boolean notifyAdmins = plugin.getConfig().getBoolean("blocks.notify-admins-on-possession", true); + + for (ItemStack item : inventory.getContents()) { + if (item == null) continue; + Material material = item.getType(); + if (material == Material.COMMAND_BLOCK) hasCommandBlock = true; + if (material == Material.STRUCTURE_BLOCK) hasStructureBlock = true; + } + + if (!cmdAllowed && hasCommandBlock) { + inventory.remove(Material.COMMAND_BLOCK); + if (notifyPlayer) { + player.sendMessage(ChatColor.RED + "[SurvivalPlus] Command-Blöcke wurden aus deinem Inventar entfernt, da sie deaktiviert sind."); + } + } + if (!structAllowed && hasStructureBlock) { + inventory.remove(Material.STRUCTURE_BLOCK); + if (notifyPlayer) { + player.sendMessage(ChatColor.RED + "[SurvivalPlus] Structure-Blöcke wurden aus deinem Inventar entfernt, da sie deaktiviert sind."); + } + } + + if (notifyAdmins && (hasCommandBlock || hasStructureBlock)) { + notifyOnlineAdmins(player.getName(), hasCommandBlock, hasStructureBlock, cmdAllowed, structAllowed); + } + } + + private void notifyOnlineAdmins(String playerName, boolean hadCommandBlock, boolean hadStructureBlock, boolean cmdAllowed, boolean structAllowed) { + String baseTag = ChatColor.GRAY + "[" + ChatColor.GREEN + "SurvivalPlus" + ChatColor.GRAY + "] "; + for (Player admin : Bukkit.getOnlinePlayers()) { + if (admin.hasPermission("survivalplus.notify")) { + if (hadCommandBlock && !cmdAllowed) { + String message = baseTag + ChatColor.RED + "Spieler " + ChatColor.YELLOW + playerName + + ChatColor.RED + " hatte einen Command-Block im Inventar, der entfernt wurde."; + admin.sendMessage(message); + } + if (hadStructureBlock && !structAllowed) { + String message = baseTag + ChatColor.RED + "Spieler " + ChatColor.YELLOW + playerName + + ChatColor.RED + " hatte einen Structure-Block im Inventar, der entfernt wurde."; + admin.sendMessage(message); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/listeners/ChallengeCollectListener.java b/src/main/java/de/viper/survivalplus/listeners/ChallengeCollectListener.java new file mode 100644 index 0000000..45c96e4 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/ChallengeCollectListener.java @@ -0,0 +1,86 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.fun.FunChallenge; +import de.viper.survivalplus.fun.FunChallengeManager; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerPickupItemEvent; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataType; + +public class ChallengeCollectListener implements Listener { + + private final FunChallengeManager challengeManager; + private final NamespacedKey droppedKey; + + public ChallengeCollectListener(FunChallengeManager challengeManager, NamespacedKey droppedKey) { + this.challengeManager = challengeManager; + this.droppedKey = droppedKey; + } + + @EventHandler + public void onItemDrop(PlayerDropItemEvent event) { + ItemStack dropped = event.getItemDrop().getItemStack(); + ItemMeta meta = dropped.getItemMeta(); + if (meta != null) { + meta.getPersistentDataContainer().set(droppedKey, PersistentDataType.INTEGER, 1); + dropped.setItemMeta(meta); + } + } + + @EventHandler + public void onPlayerPickup(PlayerPickupItemEvent event) { + FunChallenge challenge = challengeManager.getActiveChallenge(); + if (challenge == null) return; + + Player player = event.getPlayer(); + ItemStack item = event.getItem().getItemStack(); + ItemMeta meta = item.getItemMeta(); + + if (meta != null && meta.getPersistentDataContainer().has(droppedKey, PersistentDataType.INTEGER)) { + return; // Item aus Inventar gedroppt, nicht zählen + } + + if (item.getType().name().equalsIgnoreCase(challenge.getItem())) { + int current = challengeManager.getGlobalProgress(challenge.getName()); + current += item.getAmount(); + challengeManager.setGlobalProgress(challenge.getName(), current); + + if (current >= challenge.getAmount()) { + player.getServer().broadcastMessage("§aChallenge \"" + challenge.getName() + "\" geschafft!"); + giveReward(player, challenge.getReward()); + challenge.setActive(false); + challengeManager.resetProgress(challenge.getName()); + } else { + player.sendMessage("§eFortschritt: " + current + "/" + challenge.getAmount() + " " + challenge.getItem()); + } + } + } + + private void giveReward(Player player, String reward) { + if (reward == null || reward.isEmpty()) return; + + String[] parts = reward.split(","); + if (parts.length != 2) return; + + String itemName = parts[0]; + int amount; + try { + amount = Integer.parseInt(parts[1]); + } catch (NumberFormatException e) { + return; + } + + Material material = Material.matchMaterial(itemName); + if (material == null) return; + + ItemStack rewardItem = new ItemStack(material, amount); + player.getInventory().addItem(rewardItem); + player.sendMessage("§aDu hast deine Belohnung erhalten: " + amount + "x " + itemName); + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/ChallengeSmeltListener.java b/src/main/java/de/viper/survivalplus/listeners/ChallengeSmeltListener.java new file mode 100644 index 0000000..d0d6c38 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/ChallengeSmeltListener.java @@ -0,0 +1,70 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.fun.FunChallenge; +import de.viper.survivalplus.fun.FunChallengeManager; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.FurnaceExtractEvent; +import org.bukkit.inventory.ItemStack; + +public class ChallengeSmeltListener implements Listener { + + private final FunChallengeManager challengeManager; + + public ChallengeSmeltListener(FunChallengeManager challengeManager) { + this.challengeManager = challengeManager; + } + + @EventHandler + public void onFurnaceExtract(FurnaceExtractEvent event) { + Player player = (Player) event.getPlayer(); + + FunChallenge active = challengeManager.getActiveChallenge(); + if (active == null) return; + + if (!"COLLECT".equalsIgnoreCase(active.getType())) return; + + String requiredItem = active.getItem(); + int requiredAmount = active.getAmount(); + + String smeltedItem = event.getItemType().name(); + if (!smeltedItem.equalsIgnoreCase(requiredItem)) return; + + int current = challengeManager.getGlobalProgress(active.getName()); + current += event.getItemAmount(); + challengeManager.setGlobalProgress(active.getName(), current); + + if (current >= requiredAmount) { + player.getServer().broadcastMessage("§aChallenge \"" + active.getName() + "\" geschafft!"); + giveReward(player, active.getReward()); + active.setActive(false); + challengeManager.resetProgress(active.getName()); + } else { + player.sendMessage("§eFortschritt: " + current + "/" + requiredAmount + " " + requiredItem); + } + } + + private void giveReward(Player player, String reward) { + if (reward == null || reward.isEmpty()) return; + + String[] parts = reward.split(","); + if (parts.length != 2) return; + + String itemName = parts[0]; + int amount; + try { + amount = Integer.parseInt(parts[1]); + } catch (NumberFormatException e) { + return; + } + + Material material = Material.matchMaterial(itemName); + if (material == null) return; + + ItemStack rewardItem = new ItemStack(material, amount); + player.getInventory().addItem(rewardItem); + player.sendMessage("§aDu hast deine Belohnung erhalten: " + amount + "x " + itemName); + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/ChatBlockListener.java b/src/main/java/de/viper/survivalplus/listeners/ChatBlockListener.java new file mode 100644 index 0000000..f19012c --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/ChatBlockListener.java @@ -0,0 +1,26 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.Manager.BlockManager; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerChatEvent; + +public class ChatBlockListener implements Listener { + + private final BlockManager blockManager; + + public ChatBlockListener(BlockManager blockManager) { + this.blockManager = blockManager; + } + + @EventHandler + public void onChat(AsyncPlayerChatEvent event) { + Player sender = event.getPlayer(); + + event.getRecipients().removeIf(recipient -> + blockManager.hasBlocked(recipient, sender) || blockManager.hasBlocked(sender, recipient) + ); + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/DebugArmorStandListener.java b/src/main/java/de/viper/survivalplus/listeners/DebugArmorStandListener.java new file mode 100644 index 0000000..17324bd --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/DebugArmorStandListener.java @@ -0,0 +1,46 @@ +package de.viper.survivalplus.listeners; + +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; + +public class DebugArmorStandListener implements Listener { + + private static final double MAX_DISTANCE = 3.0; + + @EventHandler + public void onPlayerInteractAtEntity(PlayerInteractAtEntityEvent event) { + if (event.getRightClicked() instanceof ArmorStand armorStand) { + Player player = event.getPlayer(); + if (armorStand.getScoreboardTags().contains("debugArmorStand")) { + if (armorStand.getLocation().distance(player.getLocation()) <= MAX_DISTANCE) { + armorStand.remove(); + player.sendMessage("§aDebug-ArmorStand wurde entfernt."); + event.setCancelled(true); + } else { + player.sendMessage("§cDieser Debug-ArmorStand ist zu weit entfernt."); + } + } + } + } + + @EventHandler + public void onEntityDamageByEntity(EntityDamageByEntityEvent event) { + if (event.getEntity() instanceof ArmorStand armorStand) { + if (armorStand.getScoreboardTags().contains("debugArmorStand")) { + if (event.getDamager() instanceof Player player) { + if (armorStand.getLocation().distance(player.getLocation()) <= MAX_DISTANCE) { + armorStand.remove(); + player.sendMessage("§aDebug-ArmorStand wurde zerstört."); + event.setCancelled(true); + } else { + player.sendMessage("§cDieser Debug-ArmorStand ist zu weit entfernt."); + } + } + } + } + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/FirstJoinListener.java b/src/main/java/de/viper/survivalplus/listeners/FirstJoinListener.java new file mode 100644 index 0000000..0d6f75e --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/FirstJoinListener.java @@ -0,0 +1,35 @@ +package de.viper.survivalplus.listeners; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.Material; +import org.bukkit.inventory.PlayerInventory; + +public class FirstJoinListener implements Listener { + + @EventHandler + public void onPlayerFirstJoin(PlayerJoinEvent event) { + if (event.getPlayer().hasPlayedBefore()) { + return; // Nur für den ersten Join + } + + // Erstelle ein ItemStack für einen Trank (z.B. Heiltrank) + ItemStack potion = new ItemStack(Material.POTION, 1); + + if (potion.getItemMeta() instanceof PotionMeta) { + PotionMeta potionMeta = (PotionMeta) potion.getItemMeta(); + PotionEffect effect = new PotionEffect(PotionEffectType.REGENERATION, 600, 1); + potionMeta.addCustomEffect(effect, true); + potion.setItemMeta(potionMeta); + } + + // Füge das Trank-Item zum Inventar des Spielers hinzu + PlayerInventory inventory = event.getPlayer().getInventory(); + inventory.addItem(potion); + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/ForceSurvivalListener.java b/src/main/java/de/viper/survivalplus/listeners/ForceSurvivalListener.java new file mode 100644 index 0000000..86ff5a4 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/ForceSurvivalListener.java @@ -0,0 +1,26 @@ +package de.viper.survivalplus; + +import org.bukkit.GameMode; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; + +public class ForceSurvivalListener implements Listener { + + private final SurvivalPlus plugin; + + public ForceSurvivalListener(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + // Spieler in Survival setzen + if (player.getGameMode() != GameMode.SURVIVAL) { + player.setGameMode(GameMode.SURVIVAL); + player.sendMessage(plugin.getMessage("force-survival.join-message")); + } + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/GraveListener.java b/src/main/java/de/viper/survivalplus/listeners/GraveListener.java new file mode 100644 index 0000000..0dd0e2c --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/GraveListener.java @@ -0,0 +1,186 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.*; +import org.bukkit.block.Block; +import org.bukkit.block.Chest; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Entity; +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.scheduler.BukkitRunnable; + +import java.util.HashMap; +import java.util.List; +import java.util.UUID; + +public class GraveListener implements Listener { + + private final SurvivalPlus plugin; + private final HashMap graveOwners = new HashMap<>(); + private final HashMap graveArmorStands = new HashMap<>(); + private long despawnTime; + + public GraveListener(SurvivalPlus plugin) { + this.plugin = plugin; + reloadConfig(plugin.getConfig()); + } + + public void reloadConfig(FileConfiguration config) { + this.despawnTime = config.getLong("graves.despawn-time", 1800); + } + + @EventHandler + public void onPlayerDeath(PlayerDeathEvent event) { + if (event.getKeepInventory()) return; + + Player player = event.getEntity(); + UUID playerId = player.getUniqueId(); + FileConfiguration gravesConfig = plugin.getGravesConfig(); + FileConfiguration lang = plugin.getLangConfig(); + + Location deathLocation = player.getLocation().clone(); + Block block = deathLocation.getBlock(); + + // Save to graves.yml + gravesConfig.set("graves." + playerId + ".world", deathLocation.getWorld().getName()); + gravesConfig.set("graves." + playerId + ".x", deathLocation.getX()); + gravesConfig.set("graves." + playerId + ".y", deathLocation.getY()); + gravesConfig.set("graves." + playerId + ".z", deathLocation.getZ()); + plugin.saveGravesConfig(); + + // Suche Platz für Truhe + if (block.isEmpty() || block.getType().isAir()) { + block.setType(Material.CHEST); + } else { + for (int i = 1; i <= 10; i++) { + Block above = block.getRelative(0, i, 0); + if (above.isEmpty() || above.getType().isAir()) { + above.setType(Material.CHEST); + block = above; + deathLocation = block.getLocation(); + break; + } + } + } + + if (block.getType() == Material.CHEST) { + Chest chest = (Chest) block.getState(); + for (ItemStack item : event.getDrops()) { + if (item != null && item.getType() != Material.AIR) { + chest.getInventory().addItem(item); + } + } + event.getDrops().clear(); + + graveOwners.put(block.getLocation(), playerId); + + // Nachricht an Spieler + player.sendMessage(String.format( + lang.getString("graves.created", "§aDein Grab wurde bei x=%.2f, y=%.2f, z=%.2f erstellt!"), + deathLocation.getX(), + deathLocation.getY(), + deathLocation.getZ() + )); + + // ArmorStand mit "Grab von " + Location standLocation = deathLocation.clone().add(0.5, 1.2, 0.5); + ArmorStand stand = deathLocation.getWorld().spawn(standLocation, ArmorStand.class); + stand.setVisible(false); + stand.setCustomName("§cGrab von " + player.getName()); + stand.setCustomNameVisible(true); + stand.setMarker(true); + stand.setGravity(false); + stand.setInvulnerable(true); + stand.setSilent(true); + stand.setSmall(true); + + // Speichere ArmorStand UUID + graveArmorStands.put(block.getLocation(), stand.getUniqueId()); + + // Entfernen nach Zeit + if (despawnTime >= 0) { + final Block graveBlock = block; + final Location graveLocation = graveBlock.getLocation(); + final Player deadPlayer = player; + final FileConfiguration langConfigFinal = lang; + + new BukkitRunnable() { + @Override + public void run() { + if (graveBlock.getType() == Material.CHEST) { + graveBlock.setType(Material.AIR); + graveOwners.remove(graveLocation); + removeArmorStand(graveLocation); + + deadPlayer.sendMessage(String.format( + langConfigFinal.getString("graves.despawned", "§cDein Grab bei x=%.2f, y=%.2f, z=%.2f ist verschwunden!"), + graveLocation.getX(), + graveLocation.getY(), + graveLocation.getZ() + )); + } + } + }.runTaskLater(plugin, despawnTime * 20L); + } + + // Überwache, ob Truhe geleert wurde + final Block chestBlockFinal = block; + final Player playerFinal = player; + final FileConfiguration langFinal = lang; + + new BukkitRunnable() { + @Override + public void run() { + if (chestBlockFinal.getType() != Material.CHEST) { + cancel(); + return; + } + + Chest c = (Chest) chestBlockFinal.getState(); + boolean empty = true; + for (ItemStack item : c.getInventory().getContents()) { + if (item != null && item.getType() != Material.AIR) { + empty = false; + break; + } + } + + if (empty) { + c.getBlock().setType(Material.AIR); + graveOwners.remove(c.getLocation()); + removeArmorStand(c.getLocation()); + + playerFinal.sendMessage(String.format( + langFinal.getString("graves.emptied", "§7Dein Grab bei x=%.2f, y=%.2f, z=%.2f wurde geleert."), + (double)c.getX(), + (double)c.getY(), + (double)c.getZ() + )); + + cancel(); + } + } + }.runTaskTimer(plugin, 20L, 60L); + } + } + + private void removeArmorStand(Location chestLocation) { + UUID standId = graveArmorStands.remove(chestLocation); + if (standId == null) return; + + Entity entity = Bukkit.getEntity(standId); + if (entity != null && entity instanceof ArmorStand) { + entity.remove(); + } + } + + public boolean isOwner(Player player, Location location) { + return graveOwners.getOrDefault(location, null) != null && + graveOwners.get(location).equals(player.getUniqueId()); + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/InventoryClickListener.java b/src/main/java/de/viper/survivalplus/listeners/InventoryClickListener.java new file mode 100644 index 0000000..bdab30e --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/InventoryClickListener.java @@ -0,0 +1,69 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.configuration.file.FileConfiguration; +import java.util.logging.Level; + +public class InventoryClickListener implements Listener { + private final SurvivalPlus plugin; + + public InventoryClickListener(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + FileConfiguration lang = plugin.getLangConfig(); + String guiTitle = event.getView().getTitle(); + String homeGuiTitle = lang.getString("homelist.gui-title", "Deine Homes"); + if (!guiTitle.equals(homeGuiTitle)) { + plugin.getLogger().log(Level.FINE, "Ignoriere Klick in GUI mit Titel: " + guiTitle); + return; + } + + plugin.getLogger().log(Level.FINE, "Verarbeite Klick in Home-Liste GUI für Spieler: " + event.getWhoClicked().getName()); + event.setCancelled(true); + if (!(event.getWhoClicked() instanceof Player) || event.getCurrentItem() == null) { + return; + } + + Player player = (Player) event.getWhoClicked(); + String homeName = event.getCurrentItem().getItemMeta().getDisplayName().replace("§a", ""); + String path = "homes." + player.getUniqueId() + "." + homeName.toLowerCase(); + + FileConfiguration homes = plugin.getHomesConfig(); + if (!homes.contains(path)) { + player.sendMessage(lang.getString("homelist.home-deleted", "§cDieses Home existiert nicht mehr!")); + player.closeInventory(); + return; + } + + String worldName = homes.getString(path + ".world"); + World world = plugin.getServer().getWorld(worldName); + if (world == null) { + player.sendMessage(lang.getString("homelist.world-not-found", "§cDie Welt dieses Homes existiert nicht!")); + player.closeInventory(); + return; + } + + Location loc = new Location( + world, + homes.getDouble(path + ".x"), + homes.getDouble(path + ".y"), + homes.getDouble(path + ".z"), + (float) homes.getDouble(path + ".yaw"), + (float) homes.getDouble(path + ".pitch") + ); + + player.teleport(loc); + player.sendMessage(lang.getString("homelist.teleported", "§aZum Home %name% teleportiert!") + .replace("%name%", homeName)); + player.closeInventory(); + } +} \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/listeners/LoginListener.java b/src/main/java/de/viper/survivalplus/listeners/LoginListener.java new file mode 100644 index 0000000..c8d7669 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/LoginListener.java @@ -0,0 +1,53 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.ChatColor; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; + +public class LoginListener implements Listener { + + private final SurvivalPlus plugin; + + public LoginListener(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + + if (!player.hasPlayedBefore()) { + // Erster Login + sendWelcomeMessage(player, true); + } else { + // Wiederkehrer + sendWelcomeMessage(player, false); + } + } + + private void sendWelcomeMessage(Player player, boolean firstJoin) { + String messageKey = firstJoin ? "welcome.first-join-message" : "welcome.return-message"; + String soundKey = firstJoin ? "welcome.first-join-sound" : "welcome.return-sound"; + String volumeKey = firstJoin ? "welcome.first-join-sound-volume" : "welcome.return-sound-volume"; + String pitchKey = firstJoin ? "welcome.first-join-sound-pitch" : "welcome.return-sound-pitch"; + + String rawMessage = plugin.getLangConfig().getString(messageKey, "&6Willkommen, &e{player}&6!"); + String message = ChatColor.translateAlternateColorCodes('&', rawMessage.replace("{player}", player.getName())); + player.sendMessage(message); + + String soundName = plugin.getLangConfig().getString(soundKey, "ENTITY_PLAYER_LEVELUP"); + float volume = (float) plugin.getLangConfig().getDouble(volumeKey, 1.0); + float pitch = (float) plugin.getLangConfig().getDouble(pitchKey, 1.0); + + try { + Sound sound = Sound.valueOf(soundName); + player.playSound(player.getLocation(), sound, volume, pitch); + } catch (IllegalArgumentException e) { + plugin.getLogger().warning("Ungültiger Sound-Name in lang.yml: " + soundName); + } + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/MobCapListener.java b/src/main/java/de/viper/survivalplus/listeners/MobCapListener.java new file mode 100644 index 0000000..c0e5cc3 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/MobCapListener.java @@ -0,0 +1,181 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Chunk; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Animals; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Entity; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.entity.EntityDeathEvent; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class MobCapListener implements Listener { + + private final SurvivalPlus plugin; + private boolean enabled; + private int maxAnimalsPerChunk; + private final Map> chunkAnimalMap = new HashMap<>(); // Chunk -> Tier-UUID -> Spieler-UUID + + public MobCapListener(SurvivalPlus plugin, FileConfiguration config) { + this.plugin = plugin; + reloadConfig(config); + } + + public void reloadConfig(FileConfiguration config) { + this.enabled = config.getBoolean("mob-cap.enabled", true); + this.maxAnimalsPerChunk = config.getInt("mob-cap.max-animals-per-chunk", 10); + plugin.getLogger().info("MobCapListener: enabled=" + enabled + ", maxAnimalsPerChunk=" + maxAnimalsPerChunk); + + // Bereinige bestehende Daten + chunkAnimalMap.clear(); + + if (!enabled) { + plugin.getMobCapConfig().set("mobcap", null); // Bereinige mobcap.yml + plugin.saveMobCapConfig(); + plugin.getLogger().info("MobCapListener: Daten bereinigt, da enabled=false"); + return; + } + + // Lade Zuordnungen aus mobcap.yml + FileConfiguration mobCapConfig = plugin.getMobCapConfig(); + if (mobCapConfig.contains("mobcap")) { + for (String chunkKey : mobCapConfig.getConfigurationSection("mobcap").getKeys(false)) { + Map animalToPlayer = new HashMap<>(); + for (String animalIdStr : mobCapConfig.getConfigurationSection("mobcap." + chunkKey).getKeys(false)) { + try { + UUID animalId = UUID.fromString(animalIdStr); + String playerIdStr = mobCapConfig.getString("mobcap." + chunkKey + "." + animalIdStr); + if (playerIdStr != null) { + UUID playerId = UUID.fromString(playerIdStr); + Entity entity = plugin.getServer().getEntity(animalId); + if (entity instanceof Animals) { + animalToPlayer.put(animalId, playerId); + } else { + // Entferne ungültige Einträge + mobCapConfig.set("mobcap." + chunkKey + "." + animalIdStr, null); + } + } + } catch (IllegalArgumentException e) { + plugin.getLogger().warning("Ungültige UUID in mobcap.yml: " + animalIdStr); + } + } + chunkAnimalMap.put(chunkKey, animalToPlayer); + } + } + + // Scanne alle Welten nach Tieren + for (org.bukkit.World world : plugin.getServer().getWorlds()) { + for (Chunk chunk : world.getLoadedChunks()) { + String chunkKey = getChunkKey(chunk); + Map animalToPlayer = chunkAnimalMap.computeIfAbsent(chunkKey, k -> new HashMap<>()); + for (Entity entity : chunk.getEntities()) { + if (entity instanceof Animals) { + UUID id = entity.getUniqueId(); + if (!animalToPlayer.containsKey(id)) { + animalToPlayer.put(id, null); // Kein Spieler zugeordnet + } + } + } + } + } + + // Speichere aktualisierte mobcap.yml + saveChunkAnimalMap(); + plugin.getLogger().info("MobCapListener: Nach Reload " + chunkAnimalMap.size() + " Chunks mit Tieren gefunden."); + } + + @EventHandler + public void onCreatureSpawn(CreatureSpawnEvent event) { + if (!enabled) return; + + LivingEntity entity = event.getEntity(); + if (!(entity instanceof Animals)) return; + + Chunk chunk = entity.getLocation().getChunk(); + String chunkKey = getChunkKey(chunk); + Map animalToPlayer = chunkAnimalMap.computeIfAbsent(chunkKey, k -> new HashMap<>()); + + int animalCount = animalToPlayer.size(); + if (animalCount >= maxAnimalsPerChunk) { + event.setCancelled(true); + plugin.getLogger().info("Spawn von " + entity.getType() + " in Chunk " + chunkKey + " verhindert: Limit von " + maxAnimalsPerChunk + " erreicht."); + + // HINWEIS: Wenn du dem Züchter eine Nachricht schicken willst, nutze EntityBreedEvent + // Hier ist das nicht möglich, weil man beim Spawn keinen Spielerzugriff hat + } else { + animalToPlayer.put(entity.getUniqueId(), null); // Kein Spieler zugeordnet + saveChunkAnimalMap(); + } + } + + @EventHandler + public void onPlayerInteractEntity(PlayerInteractEntityEvent event) { + if (!enabled) return; + + if (!(event.getRightClicked() instanceof Animals)) return; + + Player player = event.getPlayer(); + Entity entity = event.getRightClicked(); + Chunk chunk = entity.getLocation().getChunk(); + String chunkKey = getChunkKey(chunk); + Map animalToPlayer = chunkAnimalMap.computeIfAbsent(chunkKey, k -> new HashMap<>()); + + int animalCount = animalToPlayer.size(); + if (animalCount >= maxAnimalsPerChunk) { + event.setCancelled(true); + player.sendMessage(plugin.getMessage("mob-cap.limit-reached").replace("%max%", String.valueOf(maxAnimalsPerChunk))); + plugin.getLogger().info("Interaktion mit " + entity.getType() + " in Chunk " + chunkKey + " verhindert: Limit von " + maxAnimalsPerChunk + " erreicht."); + } else { + animalToPlayer.put(entity.getUniqueId(), player.getUniqueId()); + saveChunkAnimalMap(); + } + } + + @EventHandler + public void onEntityDeath(EntityDeathEvent event) { + if (!enabled) return; + + if (!(event.getEntity() instanceof Animals)) return; + + LivingEntity entity = event.getEntity(); + Chunk chunk = entity.getLocation().getChunk(); + String chunkKey = getChunkKey(chunk); + Map animalToPlayer = chunkAnimalMap.get(chunkKey); + + if (animalToPlayer != null) { + UUID animalId = entity.getUniqueId(); + animalToPlayer.remove(animalId); + if (animalToPlayer.isEmpty()) { + chunkAnimalMap.remove(chunkKey); + } + saveChunkAnimalMap(); + plugin.getLogger().info("Tier " + entity.getType() + " in Chunk " + chunkKey + " gestorben. Verbleibende Tiere: " + animalToPlayer.size()); + } + } + + private String getChunkKey(Chunk chunk) { + return chunk.getWorld().getName() + "," + chunk.getX() + "," + chunk.getZ(); + } + + private void saveChunkAnimalMap() { + FileConfiguration mobCapConfig = plugin.getMobCapConfig(); + mobCapConfig.set("mobcap", null); // Bereinige vorherige Daten + for (Map.Entry> entry : chunkAnimalMap.entrySet()) { + String chunkKey = entry.getKey(); + Map animalToPlayer = entry.getValue(); + for (Map.Entry animalEntry : animalToPlayer.entrySet()) { + mobCapConfig.set("mobcap." + chunkKey + "." + animalEntry.getKey().toString(), + animalEntry.getValue() != null ? animalEntry.getValue().toString() : null); + } + } + plugin.saveMobCapConfig(); + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/MobLeashLimitListener.java b/src/main/java/de/viper/survivalplus/listeners/MobLeashLimitListener.java new file mode 100644 index 0000000..2ab8fe3 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/MobLeashLimitListener.java @@ -0,0 +1,253 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.Material; + +import java.util.*; + +public class MobLeashLimitListener implements Listener { + + private final SurvivalPlus plugin; + private boolean enabled; + private int maxLeashCount; + private final Map> leashedEntities = new HashMap<>(); + private final Map leashCounts = new HashMap<>(); + private final Map entityToPlayer = new HashMap<>(); + + public MobLeashLimitListener(SurvivalPlus plugin, FileConfiguration config) { + this.plugin = plugin; + reloadConfig(config); + } + + public void reloadConfig(FileConfiguration config) { + this.enabled = config.getBoolean("mob-leash-limit.enabled", true); + this.maxLeashCount = config.getInt("mob-leash-limit.max-leash-count", 5); + plugin.getLogger().info("MobLeashLimitListener: enabled=" + enabled + ", maxLeashCount=" + maxLeashCount); + + // Bereinige bestehende Daten + leashedEntities.clear(); + leashCounts.clear(); + entityToPlayer.clear(); + + if (!enabled) { + plugin.getLeashesConfig().set("leashes", null); // Bereinige leashes.yml + plugin.saveLeashesConfig(); + plugin.getLogger().info("MobLeashLimitListener: Daten bereinigt, da enabled=false"); + return; + } + + // Lade Zuordnungen aus leashes.yml + FileConfiguration leashesConfig = plugin.getLeashesConfig(); + if (leashesConfig.contains("leashes")) { + for (String entityIdStr : leashesConfig.getConfigurationSection("leashes").getKeys(false)) { + UUID entityId; + try { + entityId = UUID.fromString(entityIdStr); + } catch (IllegalArgumentException e) { + plugin.getLogger().warning("Ungültige UUID in leashes.yml: " + entityIdStr); + continue; + } + String playerIdStr = leashesConfig.getString("leashes." + entityIdStr); + if (playerIdStr != null) { + try { + UUID playerId = UUID.fromString(playerIdStr); + LivingEntity entity = (LivingEntity) plugin.getServer().getEntity(entityId); + if (entity != null && entity.isLeashed()) { + Set playerLeashedEntities = leashedEntities.computeIfAbsent(playerId, k -> new HashSet<>()); + playerLeashedEntities.add(entityId); + leashCounts.put(playerId, playerLeashedEntities.size()); + entityToPlayer.put(entityId, playerId); + } else { + // Entferne ungültige Einträge aus leashes.yml + leashesConfig.set("leashes." + entityIdStr, null); + } + } catch (IllegalArgumentException e) { + plugin.getLogger().warning("Ungültige Spieler-UUID in leashes.yml für Entity " + entityIdStr + ": " + playerIdStr); + } + } + } + } + + // Scanne alle Welten nach angeleinten Tieren, die noch nicht in leashes.yml sind + for (org.bukkit.World world : plugin.getServer().getWorlds()) { + for (LivingEntity entity : world.getLivingEntities()) { + if (entity.isLeashed() && !entityToPlayer.containsKey(entity.getUniqueId())) { + // Nur Tiere zählen, die von einem Spieler gehalten werden (für neue Leashes) + if (entity.getLeashHolder() instanceof Player) { + Player player = (Player) entity.getLeashHolder(); + UUID playerId = player.getUniqueId(); + UUID entityId = entity.getUniqueId(); + + Set playerLeashedEntities = leashedEntities.computeIfAbsent(playerId, k -> new HashSet<>()); + playerLeashedEntities.add(entityId); + leashCounts.put(playerId, playerLeashedEntities.size()); + entityToPlayer.put(entityId, playerId); + + // Speichere in leashes.yml + leashesConfig.set("leashes." + entityId.toString(), playerId.toString()); + } + } + } + } + + // Prüfe, ob das neue Limit überschritten wird + for (Map.Entry> entry : leashedEntities.entrySet()) { + UUID playerId = entry.getKey(); + Set playerLeashedEntities = entry.getValue(); + if (playerLeashedEntities.size() > maxLeashCount) { + Player player = plugin.getServer().getPlayer(playerId); + if (player != null && player.isOnline()) { + player.sendMessage(plugin.getMessage("mob-leash-limit.limit-adjusted") + .replace("%count%", String.valueOf(playerLeashedEntities.size())) + .replace("%max%", String.valueOf(maxLeashCount))); + } + } + } + + // Entferne leere Einträge + leashedEntities.entrySet().removeIf(entry -> entry.getValue().isEmpty()); + leashCounts.entrySet().removeIf(entry -> entry.getValue() == 0); + + // Speichere aktualisierte leashes.yml + plugin.saveLeashesConfig(); + + plugin.getLogger().info("MobLeashLimitListener: Nach Reload " + leashedEntities.size() + " Spieler mit angeleinten Tieren gefunden."); + } + + @EventHandler + public void onPlayerInteractEntity(PlayerInteractEntityEvent event) { + if (!enabled) return; + + Player player = event.getPlayer(); + ItemStack itemInHand = player.getInventory().getItemInMainHand(); + UUID playerId = player.getUniqueId(); + LivingEntity entity = (LivingEntity) event.getRightClicked(); + UUID entityId = entity.getUniqueId(); + + if (itemInHand != null && itemInHand.getType() == Material.LEAD) { + Set playerLeashedEntities = leashedEntities.computeIfAbsent(playerId, k -> new HashSet<>()); + int currentCount = playerLeashedEntities.size(); + plugin.getLogger().info("PlayerInteractEntity: player=" + player.getName() + ", currentCount=" + currentCount + ", maxLeashCount=" + maxLeashCount); + + if (playerLeashedEntities.contains(entityId)) { + // Ableinen + playerLeashedEntities.remove(entityId); + leashCounts.put(playerId, currentCount - 1); + entityToPlayer.remove(entityId); + plugin.getLeashesConfig().set("leashes." + entityId.toString(), null); + plugin.saveLeashesConfig(); + player.sendMessage(plugin.getMessage("mob-leash-limit.unleashed") + .replace("%count%", String.valueOf(currentCount - 1))); + + // Entferne leere Einträge + if (playerLeashedEntities.isEmpty()) { + leashedEntities.remove(playerId); + leashCounts.remove(playerId); + } + } else { + // Anleinen + if (currentCount >= maxLeashCount) { + player.sendMessage(plugin.getMessage("mob-leash-limit.max-reached") + .replace("%count%", String.valueOf(maxLeashCount))); + event.setCancelled(true); + } else { + playerLeashedEntities.add(entityId); + leashCounts.put(playerId, currentCount + 1); + entityToPlayer.put(entityId, playerId); + plugin.getLeashesConfig().set("leashes." + entityId.toString(), playerId.toString()); + plugin.saveLeashesConfig(); + player.sendMessage(plugin.getMessage("mob-leash-limit.leashed") + .replace("%count%", String.valueOf(currentCount + 1))); + } + } + } + } + + @EventHandler + public void onEntityDeath(EntityDeathEvent event) { + if (!enabled) { + UUID entityId = event.getEntity().getUniqueId(); + entityToPlayer.remove(entityId); + plugin.getLeashesConfig().set("leashes." + entityId.toString(), null); + plugin.saveLeashesConfig(); + return; + } + + LivingEntity entity = event.getEntity(); + UUID entityId = entity.getUniqueId(); + UUID playerId = entityToPlayer.remove(entityId); + + if (playerId != null) { + Set playerLeashedEntities = leashedEntities.getOrDefault(playerId, new HashSet<>()); + playerLeashedEntities.remove(entityId); + int newCount = playerLeashedEntities.size(); + leashCounts.put(playerId, newCount); + + // Entferne aus leashes.yml + plugin.getLeashesConfig().set("leashes." + entityId.toString(), null); + plugin.saveLeashesConfig(); + + // Entferne leere Einträge + if (playerLeashedEntities.isEmpty()) { + leashedEntities.remove(playerId); + leashCounts.remove(playerId); + } + + Player player = plugin.getServer().getPlayer(playerId); + if (player != null && player.isOnline()) { + player.sendMessage(plugin.getMessage("mob-leash-limit.entity-died") + .replace("%count%", String.valueOf(newCount))); + } + } + } + + public void updateLeashCount(Player player) { + UUID playerId = player.getUniqueId(); + Set playerLeashedEntities = leashedEntities.getOrDefault(playerId, new HashSet<>()); + // Entferne nicht existierende oder nicht angeleinte Entities + playerLeashedEntities.removeIf(entityId -> { + LivingEntity entity = (LivingEntity) plugin.getServer().getEntity(entityId); + if (entity == null || !entity.isLeashed()) { + plugin.getLeashesConfig().set("leashes." + entityId.toString(), null); + return true; + } + return false; + }); + int currentCount = playerLeashedEntities.size(); + leashCounts.put(playerId, currentCount); + + // Aktualisiere entityToPlayer-Map + entityToPlayer.entrySet().removeIf(entry -> entry.getValue().equals(playerId) && !playerLeashedEntities.contains(entry.getKey())); + + // Entferne leere Einträge + if (playerLeashedEntities.isEmpty()) { + leashedEntities.remove(playerId); + leashCounts.remove(playerId); + } + + // Falls disabled, bereinige die Daten für diesen Spieler + if (!enabled) { + playerLeashedEntities.clear(); + leashedEntities.remove(playerId); + leashCounts.remove(playerId); + entityToPlayer.entrySet().removeIf(entry -> entry.getValue().equals(playerId)); + plugin.getLeashesConfig().set("leashes", null); + plugin.saveLeashesConfig(); + } + + plugin.getLogger().info("updateLeashCount: player=" + player.getName() + ", currentCount=" + currentCount); + } + + public int getLeashCount(Player player) { + updateLeashCount(player); + return leashCounts.getOrDefault(player.getUniqueId(), 0); + } +} \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/listeners/NewbieProtectionListener.java b/src/main/java/de/viper/survivalplus/listeners/NewbieProtectionListener.java new file mode 100644 index 0000000..631f7d6 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/NewbieProtectionListener.java @@ -0,0 +1,179 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.scheduler.BukkitRunnable; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +public class NewbieProtectionListener implements Listener { + + private final SurvivalPlus plugin; + private final boolean enabled; + private final int durationMinutes; + + // Maps für Zeiten und BossBars + private final Map remainingSeconds = new HashMap<>(); + private final Map bossBars = new HashMap<>(); + + // YAML Datei + private File dataFile; + private FileConfiguration dataConfig; + + public NewbieProtectionListener(SurvivalPlus plugin) { + this.plugin = plugin; + this.enabled = plugin.getConfig().getBoolean("newbie-protection.enabled", true); + this.durationMinutes = plugin.getConfig().getInt("newbie-protection.duration-minutes", 30); + + if (!plugin.getDataFolder().exists()) { + plugin.getDataFolder().mkdirs(); + } + loadData(); + + if (enabled) { + startTimer(); + } + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + if (!enabled) return; + + Player player = event.getPlayer(); + UUID uuid = player.getUniqueId(); + + // Falls nicht gespeichert, neue Zeit einstellen + remainingSeconds.putIfAbsent(uuid, durationMinutes * 60); + + // Bossbar erstellen/anzeigen + BossBar bar = Bukkit.createBossBar( + ChatColor.GREEN + "Neulingsschutz: " + formatTime(remainingSeconds.get(uuid)), + BarColor.GREEN, + BarStyle.SOLID + ); + bar.addPlayer(player); + bossBars.put(uuid, bar); + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + if (!enabled) return; + Player player = event.getPlayer(); + UUID uuid = player.getUniqueId(); + // Bossbar entfernen, aber Zeit bleibt in Map bestehen + BossBar bar = bossBars.remove(uuid); + if (bar != null) { + bar.removeAll(); + } + saveData(); // Beim Verlassen direkt speichern + } + + @EventHandler + public void onEntityDamageByEntity(EntityDamageByEntityEvent event) { + if (!enabled) return; + if (!(event.getEntity() instanceof Player)) return; + + Player victim = (Player) event.getEntity(); + UUID vid = victim.getUniqueId(); + Integer timeLeft = remainingSeconds.get(vid); + + if (timeLeft != null && timeLeft > 0) { + event.setCancelled(true); + victim.sendMessage(ChatColor.GREEN + "Du bist noch im Neulingsschutz!"); + if (event.getDamager() instanceof Player) { + ((Player) event.getDamager()).sendMessage(ChatColor.RED + victim.getName() + " ist noch geschützt!"); + } + } + } + + private void startTimer() { + new BukkitRunnable() { + @Override + public void run() { + for (UUID uuid : new HashSet<>(remainingSeconds.keySet())) { + Player p = Bukkit.getPlayer(uuid); + + if (p == null || !p.isOnline()) { + // Spieler offline → Zeit pausiert + continue; + } + + int timeLeft = remainingSeconds.getOrDefault(uuid, 0); + if (timeLeft <= 0) { + // Ablauf: Bossbar weg + Map clean + BossBar bar = bossBars.remove(uuid); + if (bar != null) bar.removeAll(); + remainingSeconds.remove(uuid); + continue; + } + + // Zeit runterzählen + timeLeft--; + remainingSeconds.put(uuid, timeLeft); + + // Bossbar updaten + BossBar bar = bossBars.get(uuid); + if (bar != null) { + bar.setTitle(ChatColor.GREEN + "Neulingsschutz: " + formatTime(timeLeft)); + bar.setProgress(Math.max(0, timeLeft / (float) (durationMinutes * 60))); + } + } + saveData(); // zyklisch speichern + } + }.runTaskTimer(plugin, 20L, 20L); + } + + // ---------- Datei Handling ---------- + private void loadData() { + dataFile = new File(plugin.getDataFolder(), "newbieprotection.yml"); + if (!dataFile.exists()) { + try { + dataFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + dataConfig = YamlConfiguration.loadConfiguration(dataFile); + for (String key : dataConfig.getKeys(false)) { + try { + UUID uuid = UUID.fromString(key); + int sec = dataConfig.getInt(key); + remainingSeconds.put(uuid, sec); + } catch (IllegalArgumentException ignored) {} + } + } + + // WICHTIG: Public gemacht, damit von SurvivalPlus.java aufrufbar + public void saveData() { + if (dataConfig == null) return; + for (Map.Entry entry : remainingSeconds.entrySet()) { + dataConfig.set(entry.getKey().toString(), entry.getValue()); + } + try { + dataConfig.save(dataFile); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private String formatTime(int totalSec) { + int min = totalSec / 60; + int sec = totalSec % 60; + return String.format("%02d:%02d", min, sec); + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/NickLoadListener.java b/src/main/java/de/viper/survivalplus/listeners/NickLoadListener.java new file mode 100644 index 0000000..4e6e65c --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/NickLoadListener.java @@ -0,0 +1,51 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.SurvivalPlus; +import net.md_5.bungee.api.ChatColor; +import org.bukkit.event.Listener; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.entity.Player; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class NickLoadListener implements Listener { + + private final SurvivalPlus plugin; + private static final Pattern HEX_PATTERN = Pattern.compile("#[a-fA-F0-9]{6}"); + + public NickLoadListener(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + String rawNick = plugin.getNicknamesConfig().getString(player.getUniqueId().toString()); + + if (rawNick != null && !rawNick.isEmpty()) { + String coloredNick = translateColors(rawNick); + String finalNick = "§f[" + coloredNick + "§f]"; + player.setDisplayName(finalNick); + player.setPlayerListName(finalNick); + } + } + + private String translateColors(String input) { + String withLegacy = org.bukkit.ChatColor.translateAlternateColorCodes('&', input); + return replaceHexColors(withLegacy); + } + + private String replaceHexColors(String input) { + Matcher matcher = HEX_PATTERN.matcher(input); + StringBuffer sb = new StringBuffer(); + while (matcher.find()) { + String hexCode = matcher.group(); + String replacement = ChatColor.of(hexCode).toString(); + matcher.appendReplacement(sb, replacement); + } + matcher.appendTail(sb); + return sb.toString(); + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/OreAlarmListener.java b/src/main/java/de/viper/survivalplus/listeners/OreAlarmListener.java new file mode 100644 index 0000000..e16fd8d --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/OreAlarmListener.java @@ -0,0 +1,139 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +import org.bukkit.Material; +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.block.BlockBreakEvent; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class OreAlarmListener implements Listener { + + private static final long TIME_FRAME_MILLIS = 30 * 1000; + + private final SurvivalPlus plugin; + + private final Map> playerOreData = new HashMap<>(); + + private FileConfiguration config; + + // Schwellenwert wird jetzt aus der Config geladen (Fallback DEFAULT_THRESHOLD) + private int threshold; + + public OreAlarmListener(SurvivalPlus plugin) { + this.plugin = plugin; + this.config = plugin.getConfig(); + loadSettings(); + } + + private void loadSettings() { + this.threshold = config.getInt("ore-alarm.threshold", 10); + } + + /** + * Reloadet die Config, lädt die Einstellungen neu und leert gespeicherte Daten. + */ + public void reloadConfig(FileConfiguration newConfig) { + this.config = newConfig; + loadSettings(); + playerOreData.clear(); + } + + @EventHandler + public void onBlockBreak(BlockBreakEvent event) { + Player player = event.getPlayer(); + Material blockType = event.getBlock().getType(); + + if (!isMonitoredOre(blockType)) { + return; + } + + UUID playerId = player.getUniqueId(); + long now = System.currentTimeMillis(); + + playerOreData.putIfAbsent(playerId, new HashMap<>()); + Map oreMap = playerOreData.get(playerId); + + oreMap.putIfAbsent(blockType, new OreData(0, now)); + OreData data = oreMap.get(blockType); + + if (now - data.startTime > TIME_FRAME_MILLIS) { + data.count = 1; + data.startTime = now; + } else { + data.count++; + } + + oreMap.put(blockType, data); + + int threshold = calculateDynamicThreshold(playerId, blockType); + if (data.count >= threshold) { + String oreName = prettifyMaterialName(blockType); + + String rawMessage = plugin.getLangConfig().getString("orealarm.message", + "§c[Erz-Alarm] Spieler §e%player% §chat in %seconds% Sekunden ungewöhnlich viele %ore% abgebaut: §e%count%"); + + String message = rawMessage + .replace("%player%", player.getName()) + .replace("%ore%", oreName) + .replace("%count%", Integer.toString(data.count)) + .replace("%seconds%", Long.toString(TIME_FRAME_MILLIS / 1000)); + + Bukkit.getOnlinePlayers().stream() + .filter(p -> p.isOp() || p.hasPermission("survivalplus.orealarm")) + .forEach(p -> p.sendMessage(message)); + + data.count = 0; + data.startTime = now; + } + } + + private boolean isMonitoredOre(Material material) { + switch (material) { + case DIAMOND_ORE: + case DEEPSLATE_DIAMOND_ORE: + case REDSTONE_ORE: + case DEEPSLATE_REDSTONE_ORE: + case GOLD_ORE: + case DEEPSLATE_GOLD_ORE: + case IRON_ORE: + case DEEPSLATE_IRON_ORE: + case LAPIS_ORE: + case DEEPSLATE_LAPIS_ORE: + case EMERALD_ORE: + case DEEPSLATE_EMERALD_ORE: + return true; + default: + return false; + } + } + + private int calculateDynamicThreshold(UUID playerId, Material ore) { + // Nutzt jetzt das geladene threshold, kann später erweitert werden + return threshold; + } + + private String prettifyMaterialName(Material material) { + String name = material.name().toLowerCase(); + name = name.replace("_ore", " Erz"); + name = name.replace("deepslate ", "Tiefgeschichtetes "); + name = Character.toUpperCase(name.charAt(0)) + name.substring(1); + return name; + } + + private static class OreData { + int count; + long startTime; + + OreData(int count, long startTime) { + this.count = count; + this.startTime = startTime; + } + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/PlayerJoinListener.java b/src/main/java/de/viper/survivalplus/listeners/PlayerJoinListener.java new file mode 100644 index 0000000..b01935c --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/PlayerJoinListener.java @@ -0,0 +1,28 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.commands.FriendCommand; +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.PlayerQuitEvent; + +public class PlayerJoinListener implements Listener { + private final FriendCommand friendCommand; + + public PlayerJoinListener(FriendCommand friendCommand) { + this.friendCommand = friendCommand; + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + friendCommand.notifyFriendsOfJoin(player); + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + Player player = event.getPlayer(); + friendCommand.updateLastOnline(player); + } +} \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/listeners/RepairSignListener.java b/src/main/java/de/viper/survivalplus/listeners/RepairSignListener.java new file mode 100644 index 0000000..4e5215d --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/RepairSignListener.java @@ -0,0 +1,157 @@ +package de.viper.survivalplus.listeners; + +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Sign; +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.block.BlockBreakEvent; +import org.bukkit.event.block.SignChangeEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.Damageable; + +public class RepairSignListener implements Listener { + + private final FileConfiguration config; + private final FileConfiguration lang; + + public RepairSignListener(FileConfiguration config, FileConfiguration lang) { + this.config = config; + this.lang = lang; + } + + private String msg(String key) { + String raw = lang.getString(key, "&c[Missing lang key: " + key + "]"); + return ChatColor.translateAlternateColorCodes('&', raw); + } + + private String msg(String key, String... replacements) { + String raw = lang.getString(key, "&c[Missing lang key: " + key + "]"); + for (int i = 0; i < replacements.length - 1; i += 2) { + raw = raw.replace(replacements[i], replacements[i + 1]); + } + return ChatColor.translateAlternateColorCodes('&', raw); + } + + @EventHandler + public void onSignCreate(SignChangeEvent event) { + Player player = event.getPlayer(); + if (event.getLine(0) != null && event.getLine(0).equalsIgnoreCase("[Repair]")) { + if (player.hasPermission("survivalplus.createrepairsign")) { + event.setLine(0, ChatColor.DARK_GREEN + "[Repair]"); + event.setLine(1, ChatColor.GRAY + config.getString("repair.currency")); + event.setLine(2, ChatColor.GOLD + String.valueOf(config.getInt("repair.price"))); + player.sendMessage(msg("repair.sign_created")); + } else { + player.sendMessage(msg("repair.no_permission_create")); + event.setCancelled(true); + } + } + } + + // Verhindert Bearbeitung bestehender Repair-Schilder + @EventHandler + public void onSignEdit(SignChangeEvent event) { + Block block = event.getBlock(); + if (block.getState() instanceof Sign) { + Sign sign = (Sign) block.getState(); + String line0 = ChatColor.stripColor(sign.getLine(0)); + if (line0.equalsIgnoreCase("[Repair]") && !event.getPlayer().isOp()) { + event.getPlayer().sendMessage(msg("repair.no_edit")); + event.setCancelled(true); + } + } + } + + // Nur OP darf Schild abbauen + @EventHandler + public void onSignBreak(BlockBreakEvent event) { + if (event.getBlock().getState() instanceof Sign) { + Sign sign = (Sign) event.getBlock().getState(); + String line0 = ChatColor.stripColor(sign.getLine(0)); + if (line0.equalsIgnoreCase("[Repair]") && !event.getPlayer().isOp()) { + event.getPlayer().sendMessage(msg("repair.no_break")); + event.setCancelled(true); + } + } + } + + @EventHandler + public void onSignClick(PlayerInteractEvent event) { + if (event.getClickedBlock() == null) return; + if (!(event.getClickedBlock().getState() instanceof Sign)) return; + + Sign sign = (Sign) event.getClickedBlock().getState(); + String line0 = ChatColor.stripColor(sign.getLine(0)); + if (!line0.equalsIgnoreCase("[Repair]")) return; + + Player player = event.getPlayer(); + ItemStack item = player.getInventory().getItemInMainHand(); + + if (!(item.getItemMeta() instanceof Damageable)) { + player.sendMessage(msg("repair.not_repairable")); + return; + } + + Damageable meta = (Damageable) item.getItemMeta(); + int currentDamage = meta.getDamage(); + + // Prüfen ob bereits voll repariert + if (currentDamage <= 0) { + player.sendMessage(msg("repair.already_fully_repaired")); + return; + } + + String currencyType = config.getString("repair.currency", "GOLD_INGOT"); + int price = config.getInt("repair.price", 5); + + Material currencyMaterial; + try { + currencyMaterial = Material.valueOf(currencyType.toUpperCase()); + } catch (IllegalArgumentException e) { + player.sendMessage(msg("repair.invalid_currency")); + return; + } + + // Prüfung ob Währung erlaubt ist + if (!isAllowedCurrency(currencyMaterial)) { + player.sendMessage(msg("repair.invalid_currency")); + return; + } + + if (!player.getInventory().containsAtLeast(new ItemStack(currencyMaterial), price)) { + player.sendMessage(msg("repair.not_enough_currency", "%currency%", currencyType)); + return; + } + + // Zahlung abbuchen + player.getInventory().removeItem(new ItemStack(currencyMaterial, price)); + + // Item reparieren + meta.setDamage(0); + item.setItemMeta(meta); + + player.sendMessage(msg("repair.success", + "%price%", String.valueOf(price), + "%currency%", currencyType)); + } + + private boolean isAllowedCurrency(Material mat) { + switch (mat) { + case GOLD_INGOT: + case IRON_INGOT: + case DIAMOND: + case NETHERITE_INGOT: + case COPPER_INGOT: + case EMERALD: + case LAPIS_LAZULI: + return true; + default: + return false; + } + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/SignColorListener.java b/src/main/java/de/viper/survivalplus/listeners/SignColorListener.java new file mode 100644 index 0000000..8beb62c --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/SignColorListener.java @@ -0,0 +1,89 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.ChatColor; // Für &-Codes +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.SignChangeEvent; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class SignColorListener implements Listener { + + private final SurvivalPlus plugin; + + // Muster für Hex-Farbcodes (#FFFFFF) + private static final Pattern HEX_PATTERN = Pattern.compile("#[a-fA-F0-9]{6}"); + // Muster für RGB-Farben rgb(255,255,255) + private static final Pattern RGB_PATTERN = Pattern.compile("rgb\\((\\d{1,3}),(\\d{1,3}),(\\d{1,3})\\)"); + + public SignColorListener(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onSignChange(SignChangeEvent event) { + // Optional: Permission-Check einfügen + // if (!event.getPlayer().hasPermission("survivalplus.signcolor")) return; + + for (int i = 0; i < event.getLines().length; i++) { + String line = event.getLine(i); + if (line != null && !line.isEmpty()) { + + // 1. &-Codes übersetzen (z.B. &a -> §a) + String translated = ChatColor.translateAlternateColorCodes('&', line); + + // 2. Hex-Farben ersetzen ( #RRGGBB ) + translated = replaceHexColors(translated); + + // 3. RGB-Farben ersetzen ( rgb(r,g,b) ) + translated = replaceRgbColors(translated); + + // 4. Final auf dem Schild setzen + event.setLine(i, translated); + } + } + } + + private String replaceHexColors(String input) { + Matcher matcher = HEX_PATTERN.matcher(input); + StringBuffer sb = new StringBuffer(); + + while (matcher.find()) { + String hexCode = matcher.group(); + // Mit Bungee ChatColor.of in §x-Format umwandeln + String color = net.md_5.bungee.api.ChatColor.of(hexCode).toString(); + matcher.appendReplacement(sb, color); + } + matcher.appendTail(sb); + return sb.toString(); + } + + private String replaceRgbColors(String input) { + Matcher matcher = RGB_PATTERN.matcher(input); + StringBuffer sb = new StringBuffer(); + + while (matcher.find()) { + int r = clampColorValue(matcher.group(1)); + int g = clampColorValue(matcher.group(2)); + int b = clampColorValue(matcher.group(3)); + + String hex = String.format("#%02X%02X%02X", r, g, b); + String color = net.md_5.bungee.api.ChatColor.of(hex).toString(); + + matcher.appendReplacement(sb, color); + } + matcher.appendTail(sb); + return sb.toString(); + } + + private int clampColorValue(String val) { + try { + int v = Integer.parseInt(val); + return Math.min(255, Math.max(0, v)); + } catch (NumberFormatException e) { + return 0; + } + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/SitListener.java b/src/main/java/de/viper/survivalplus/listeners/SitListener.java new file mode 100644 index 0000000..a816ca5 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/SitListener.java @@ -0,0 +1,128 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.event.block.Action; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.logging.Level; + +public class SitListener implements Listener { + private final SurvivalPlus plugin; + private final Map sittingPlayers = new HashMap<>(); + + public SitListener(SurvivalPlus plugin) { + this.plugin = plugin; + } + + public boolean isSitting(Player player) { + return sittingPlayers.containsKey(player.getUniqueId()); + } + + public void sitPlayer(Player player, Location location) { + if (sittingPlayers.containsKey(player.getUniqueId())) { + return; + } + + // Erstelle einen unsichtbaren ArmorStand als Sitz + ArmorStand armorStand = player.getWorld().spawn(location, ArmorStand.class); + armorStand.setGravity(false); + armorStand.setMarker(true); + armorStand.setInvisible(true); + armorStand.setInvulnerable(true); + armorStand.addPassenger(player); + + sittingPlayers.put(player.getUniqueId(), armorStand); + FileConfiguration lang = plugin.getLangConfig(); + player.sendMessage(lang.getString("sit.success", "§aDu hast dich hingesetzt!")); + plugin.getLogger().log(Level.FINE, "Spieler " + player.getName() + " sitzt bei " + locationToString(location)); + } + + public void standUp(Player player) { + UUID playerId = player.getUniqueId(); + ArmorStand armorStand = sittingPlayers.remove(playerId); + if (armorStand != null) { + armorStand.remove(); + FileConfiguration lang = plugin.getLangConfig(); + player.sendMessage(lang.getString("sit.stand-up", "§aDu bist aufgestanden!")); + plugin.getLogger().log(Level.FINE, "Spieler " + player.getName() + " ist aufgestanden"); + } + } + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent event) { + if (event.getHand() != EquipmentSlot.HAND || event.getAction() != Action.RIGHT_CLICK_BLOCK) { + return; + } + + Player player = event.getPlayer(); + FileConfiguration lang = plugin.getLangConfig(); + + if (!player.hasPermission("survivalplus.sit")) { + player.sendMessage(lang.getString("no-permission", "§cDu hast keine Berechtigung für diesen Befehl!")); + return; + } + + Block block = event.getClickedBlock(); + if (block == null || !isStair(block.getType())) { + return; + } + + // Wenn der Spieler bereits sitzt, stehe auf + if (sittingPlayers.containsKey(player.getUniqueId())) { + standUp(player); + event.setCancelled(true); + return; + } + + // Setze den Spieler genau auf der Treppenstufe + Location location = block.getLocation(); + location.setX(location.getX() + 0.5); + location.setY(location.getY() + 0.5); // Genau auf der Treppenstufe (halbe Blockhöhe) + location.setZ(location.getZ() + 0.5); + sitPlayer(player, location); + event.setCancelled(true); // Verhindere andere Interaktionen mit der Treppe + } + + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) { + Player player = event.getPlayer(); + UUID playerId = player.getUniqueId(); + if (!sittingPlayers.containsKey(playerId)) { + return; + } + + // Prüfe, ob der Spieler sich bewegt hat (nur Positionsänderung, nicht Kopfbewegung) + Location from = event.getFrom(); + Location to = event.getTo(); + if (from.getX() != to.getX() || from.getY() != to.getY() || from.getZ() != to.getZ()) { + standUp(player); + } + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + Player player = event.getPlayer(); + standUp(player); + } + + private boolean isStair(Material material) { + return material.name().endsWith("_STAIRS"); + } + + private String locationToString(Location loc) { + return String.format("x=%.2f, y=%.2f, z=%.2f, world=%s", loc.getX(), loc.getY(), loc.getZ(), loc.getWorld().getName()); + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/SleepListener.java b/src/main/java/de/viper/survivalplus/listeners/SleepListener.java new file mode 100644 index 0000000..790e551 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/SleepListener.java @@ -0,0 +1,68 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +import org.bukkit.World; +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.PlayerBedEnterEvent; +import org.bukkit.event.player.PlayerBedLeaveEvent; + +public class SleepListener implements Listener { + + private final SurvivalPlus plugin; + private double requiredPercentage; + + public SleepListener(SurvivalPlus plugin) { + this.plugin = plugin; + reloadConfig(plugin.getConfig()); + } + + public void reloadConfig(FileConfiguration config) { + this.requiredPercentage = config.getDouble("sleep.required-percentage", 50.0); + } + + @EventHandler + public void onPlayerBedEnter(PlayerBedEnterEvent event) { + Bukkit.getScheduler().runTaskLater(plugin, () -> checkSleepProgress(event.getPlayer().getWorld()), 1L); + } + + @EventHandler + public void onPlayerBedLeave(PlayerBedLeaveEvent event) { + Bukkit.getScheduler().runTaskLater(plugin, () -> checkSleepProgress(event.getPlayer().getWorld()), 1L); + } + + private void checkSleepProgress(World world) { + long time = world.getTime(); + if (time < 12541 || time > 23458) return; // Nur bei Nacht prüfen + + int sleeping = 0; + int total = 0; + + for (Player player : world.getPlayers()) { + if (player.isSleepingIgnored()) continue; + total++; + if (player.isSleeping()) sleeping++; + } + + if (total == 0) return; + + double percent = (sleeping * 100.0) / total; + + if (percent >= requiredPercentage) { + world.setTime(0); + world.setStorm(false); + world.setThundering(false); + Bukkit.broadcastMessage(plugin.getMessage("sleep.skipped") + .replace("%sleeping%", String.valueOf(sleeping)) + .replace("%total%", String.valueOf(total))); + } else { + Bukkit.broadcastMessage(plugin.getMessage("sleep.progress") + .replace("%sleeping%", String.valueOf(sleeping)) + .replace("%total%", String.valueOf(total)) + .replace("%required%", String.valueOf(requiredPercentage))); + } + } +} \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/listeners/SpawnProtectionListener.java b/src/main/java/de/viper/survivalplus/listeners/SpawnProtectionListener.java new file mode 100644 index 0000000..1cf4b73 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/SpawnProtectionListener.java @@ -0,0 +1,75 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; + +public class SpawnProtectionListener implements Listener { + + private final SurvivalPlus plugin; + private final int radius; + private final boolean protectBlockBreak; + private final boolean protectBlockPlace; + private final boolean protectPvP; + private final String bypassPermission; + + public SpawnProtectionListener(SurvivalPlus plugin) { + this.plugin = plugin; + this.radius = plugin.getConfig().getInt("spawnprotection.radius", 20); + this.protectBlockBreak = plugin.getConfig().getBoolean("spawnprotection.protect-block-break", true); + this.protectBlockPlace = plugin.getConfig().getBoolean("spawnprotection.protect-block-place", true); + this.protectPvP = plugin.getConfig().getBoolean("spawnprotection.protect-pvp", true); + this.bypassPermission = plugin.getConfig().getString("spawnprotection.bypass-permission", "survivalplus.spawnprotection.bypass"); + } + + private boolean isInSpawnProtection(Location loc) { + if (loc == null) return false; + Location spawn = loc.getWorld().getSpawnLocation(); + return spawn.distance(loc) <= radius; + } + + private boolean canBypass(Player player) { + // Nur OPs oder Spieler mit spezieller Berechtigung dürfen bypassen + return player.isOp() || player.hasPermission(bypassPermission); + } + + @EventHandler + public void onBlockBreak(BlockBreakEvent event) { + if (!protectBlockBreak) return; + Player player = event.getPlayer(); + if (isInSpawnProtection(event.getBlock().getLocation()) && !canBypass(player)) { + player.sendMessage(plugin.getMessage("spawnprotection.blockbreak-denied")); + event.setCancelled(true); + } + } + + @EventHandler + public void onBlockPlace(BlockPlaceEvent event) { + if (!protectBlockPlace) return; + Player player = event.getPlayer(); + if (isInSpawnProtection(event.getBlock().getLocation()) && !canBypass(player)) { + player.sendMessage(plugin.getMessage("spawnprotection.blockplace-denied")); + event.setCancelled(true); + } + } + + @EventHandler + public void onEntityDamageByEntity(EntityDamageByEntityEvent event) { + if (!protectPvP) return; + if (!(event.getEntity() instanceof Player)) return; + if (!(event.getDamager() instanceof Player)) return; + + Player damaged = (Player) event.getEntity(); + Player damager = (Player) event.getDamager(); + + if (isInSpawnProtection(damaged.getLocation()) && !canBypass(damager)) { + damager.sendMessage(plugin.getMessage("spawnprotection.pvp-denied")); + event.setCancelled(true); + } + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/StatsListener.java b/src/main/java/de/viper/survivalplus/listeners/StatsListener.java new file mode 100644 index 0000000..66ad0e3 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/StatsListener.java @@ -0,0 +1,81 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.Manager.StatsManager; +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class StatsListener implements Listener { + + private final SurvivalPlus plugin; + private final StatsManager statsManager; + + // Spielzeit-Tracking + private final Map joinTimes = new HashMap<>(); + + public StatsListener(SurvivalPlus plugin, StatsManager statsManager) { + this.plugin = plugin; + this.statsManager = statsManager; + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + joinTimes.put(event.getPlayer().getUniqueId(), System.currentTimeMillis()); + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + UUID uuid = event.getPlayer().getUniqueId(); + long joinTime = joinTimes.getOrDefault(uuid, System.currentTimeMillis()); + long now = System.currentTimeMillis(); + long secondsPlayed = (now - joinTime) / 1000; + + statsManager.addPlayTime(uuid, secondsPlayed); + statsManager.saveStats(); + + joinTimes.remove(uuid); + } + + @EventHandler + public void onPlayerDeath(PlayerDeathEvent event) { + Player player = event.getEntity(); + UUID uuid = player.getUniqueId(); + statsManager.addDeaths(uuid, 1); + statsManager.saveStats(); + + // Optional: Kill-Stat beim Killer erhöhen + if (player.getKiller() != null) { + Player killer = player.getKiller(); + statsManager.addKills(killer.getUniqueId(), 1); + statsManager.saveStats(); + } + } + + @EventHandler + public void onBlockBreak(BlockBreakEvent event) { + Player player = event.getPlayer(); + UUID uuid = player.getUniqueId(); + statsManager.addBlocksBroken(uuid, 1); + statsManager.saveStats(); + } + + @EventHandler + public void onBlockPlace(BlockPlaceEvent event) { + Player player = event.getPlayer(); + UUID uuid = player.getUniqueId(); + statsManager.addBlocksPlaced(uuid, 1); + statsManager.saveStats(); + } +} diff --git a/src/main/java/de/viper/survivalplus/listeners/ToolUpgradeListener.java b/src/main/java/de/viper/survivalplus/listeners/ToolUpgradeListener.java new file mode 100644 index 0000000..e66c663 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/ToolUpgradeListener.java @@ -0,0 +1,250 @@ +package de.viper.survivalplus.listeners; + +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Sound; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.PrepareItemCraftEvent; +import org.bukkit.inventory.CraftingInventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class ToolUpgradeListener implements Listener { + + private final JavaPlugin plugin; + private final NamespacedKey levelKey; + private final int maxLevel; + private final Map levelMaterials = new HashMap<>(); + private static final int REQUIRED_AMOUNT = 2; // immer 2 pro Slot + + public ToolUpgradeListener(JavaPlugin plugin) { + this.plugin = plugin; + this.levelKey = new NamespacedKey(plugin, "tool_level"); + this.maxLevel = plugin.getConfig().getInt("tool-upgrades.max-level", 5); + + // Material aus Config laden + for (int i = 1; i <= maxLevel; i++) { + String matName = plugin.getConfig().getString("tool-upgrades.levels." + i + ".material"); + if (matName != null) { + try { + levelMaterials.put(i, Material.valueOf(matName.toUpperCase())); + } catch (IllegalArgumentException e) { + plugin.getLogger().warning("Ungültiges Material für Level " + i + ": " + matName); + } + } + } + } + + /** Vorschau-Ergebnis setzen */ + @EventHandler + public void onPrepareCraft(PrepareItemCraftEvent event) { + if (!(event.getInventory() instanceof CraftingInventory)) return; + CraftingInventory inv = (CraftingInventory) event.getInventory(); + ItemStack[] matrix = inv.getMatrix(); + if (matrix.length < 9) { + inv.setResult(null); + return; + } + + ItemStack center = matrix[4]; + if (center == null || center.getType() == Material.AIR) { + inv.setResult(null); + return; + } + if (!isUpgradeableTool(center.getType()) || isWoodTool(center.getType())) { + inv.setResult(null); + return; + } + + int currentLevel = getToolLevel(center); + if (currentLevel >= maxLevel || currentLevel >= getMaxAllowedLevel(center.getType())) { + inv.setResult(null); + return; + } + + Material requiredMat = levelMaterials.get(currentLevel + 1); + if (requiredMat == null) { + inv.setResult(null); + return; + } + + // Prüfen ob alle Slots (außer Mitte) mind. 2 Stück vom richtigen Material haben + for (int i = 0; i < matrix.length; i++) { + if (i == 4) continue; + if (matrix[i] == null || matrix[i].getType() != requiredMat || matrix[i].getAmount() < REQUIRED_AMOUNT) { + inv.setResult(null); + return; + } + } + inv.setResult(createUpgradedTool(center, currentLevel + 1)); + } + + /** Klick auf Ergebnis-Slot handhaben + Items verbrauchen */ + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + if (!(event.getWhoClicked() instanceof Player)) return; + Player player = (Player) event.getWhoClicked(); + if (!(event.getView().getTopInventory() instanceof CraftingInventory)) return; + CraftingInventory inv = (CraftingInventory) event.getView().getTopInventory(); + if (event.getRawSlot() != 0) return; // Nur Ergebnis-Slot (Slot 0 in Werkbank) + if (event.getCurrentItem() == null || event.getCurrentItem().getType() == Material.AIR) return; + + ItemStack[] matrix = inv.getMatrix(); + ItemStack center = matrix[4]; + if (center == null || !isUpgradeableTool(center.getType()) || isWoodTool(center.getType())) return; + + int currentLevel = getToolLevel(center); + if (currentLevel >= maxLevel || currentLevel >= getMaxAllowedLevel(center.getType())) return; + + Material requiredMat = levelMaterials.get(currentLevel + 1); + if (requiredMat == null) return; + + for (int i = 0; i < matrix.length; i++) { + if (i == 4) continue; + if (matrix[i] == null || matrix[i].getType() != requiredMat || matrix[i].getAmount() < REQUIRED_AMOUNT) return; + } + + // Gültig: Event abbrechen, Upgrade geben, Items verbrauchen + event.setCancelled(true); + ItemStack result = createUpgradedTool(center, currentLevel + 1); + + // Upgrade dem Spieler geben (Einzel- oder Shift-Klick) + if (event.isShiftClick()) { + player.getInventory().addItem(result); + } else { + if (event.getCursor() == null || event.getCursor().getType() == Material.AIR) { + event.setCursor(result); + } else { + player.getInventory().addItem(result); + } + } + + // Items verbrauchen: 2 pro Slot (außer Mitte), Mitte leeren + for (int i = 0; i < matrix.length; i++) { + if (i == 4) { + matrix[i] = null; // Mitte leeren + } else if (matrix[i] != null) { + int newAmount = matrix[i].getAmount() - REQUIRED_AMOUNT; + if (newAmount <= 0) { + matrix[i] = null; + } else { + matrix[i].setAmount(newAmount); + } + } + } + inv.setMatrix(matrix); + inv.setResult(null); // Ergebnis-Slot leeren + + // Inventar-Update verzögert senden + new BukkitRunnable() { + @Override + public void run() { + player.updateInventory(); + } + }.runTaskLater(plugin, 2L); // 2 Ticks Verzögerung + } + + /** Effekte beim Benutzen */ + @EventHandler + public void onBlockBreak(BlockBreakEvent event) { + Player player = event.getPlayer(); + if (player.getGameMode() == GameMode.CREATIVE) return; + + ItemStack tool = player.getInventory().getItemInMainHand(); + if (tool == null || !isUpgradeableTool(tool.getType()) || isWoodTool(tool.getType())) return; + + int level = getToolLevel(tool); + if (level <= 0) return; + + // Drop-Multiplikator + int multiplier = getDropMultiplier(tool.getType()); + if (multiplier > 1) { + event.setDropItems(false); + for (ItemStack drop : event.getBlock().getDrops(tool, player)) { + for (int i = 0; i < multiplier; i++) { + event.getBlock().getWorld().dropItemNaturally(event.getBlock().getLocation(), drop.clone()); + } + } + } + + // Haste-Effekt + int hasteLevel = Math.min(5, level); + player.addPotionEffect(new PotionEffect(PotionEffectType.HASTE, 40, hasteLevel - 1, true, false)); + + // Sound + player.playSound(player.getLocation(), Sound.BLOCK_ANVIL_USE, 0.4f, 1.6f); + } + + /** Upgrade-Item erstellen */ + private ItemStack createUpgradedTool(ItemStack baseTool, int newLevel) { + ItemStack upgraded = baseTool.clone(); + ItemMeta meta = upgraded.getItemMeta(); + if (meta != null) { + meta.getPersistentDataContainer().set(levelKey, PersistentDataType.INTEGER, newLevel); + Enchantment eff = Enchantment.getByKey(NamespacedKey.minecraft("efficiency")); + Enchantment fortune = Enchantment.getByKey(NamespacedKey.minecraft("fortune")); + if (eff != null) meta.addEnchant(eff, Math.min(5, newLevel), true); + int fLevel = getFortuneForMaterial(baseTool.getType()); + if (fLevel > 0 && fortune != null) meta.addEnchant(fortune, fLevel, true); + meta.setLore(Collections.singletonList("§7Upgrade-Level: §e" + newLevel)); + upgraded.setItemMeta(meta); + } + return upgraded; + } + + /** Hilfsmethoden */ + private boolean isUpgradeableTool(Material mat) { + String n = mat.name(); + return n.endsWith("_AXE") || n.endsWith("_PICKAXE") || + n.endsWith("_SHOVEL") || n.endsWith("_HOE") || + n.endsWith("_SWORD"); + } + + private boolean isWoodTool(Material mat) { return mat.name().startsWith("WOODEN"); } + + private int getMaxAllowedLevel(Material mat) { + String s = mat.name(); + if (s.startsWith("IRON")) return 3; + if (s.startsWith("GOLDEN")) return 3; + if (s.startsWith("DIAMOND")) return maxLevel; + if (s.startsWith("NETHERITE")) return maxLevel; + return 0; + } + + private int getFortuneForMaterial(Material mat) { + String s = mat.name(); + if (s.startsWith("GOLDEN")) return 1; + if (s.startsWith("DIAMOND")) return 2; + if (s.startsWith("NETHERITE")) return 3; + return 0; + } + + private int getDropMultiplier(Material mat) { + String s = mat.name(); + if (s.startsWith("GOLDEN")) return 2; + if (s.startsWith("DIAMOND")) return 3; + if (s.startsWith("NETHERITE")) return 6; + return 1; + } + + private int getToolLevel(ItemStack item) { + if (item == null || !item.hasItemMeta()) return 0; + Integer lvl = item.getItemMeta().getPersistentDataContainer().get(levelKey, PersistentDataType.INTEGER); + return lvl != null ? lvl : 0; + } +} \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/listeners/WarpInventoryListener.java b/src/main/java/de/viper/survivalplus/listeners/WarpInventoryListener.java new file mode 100644 index 0000000..1496002 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/listeners/WarpInventoryListener.java @@ -0,0 +1,80 @@ +package de.viper.survivalplus.listeners; + +import de.viper.survivalplus.Manager.Warp; +import de.viper.survivalplus.Manager.WarpManager; +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.inventory.ItemStack; + +public class WarpInventoryListener implements Listener { + + private final SurvivalPlus plugin; + private final WarpManager warpManager; + private final String inventoryTitle; + + public WarpInventoryListener(SurvivalPlus plugin, WarpManager warpManager) { + this.plugin = plugin; + this.warpManager = warpManager; + this.inventoryTitle = ChatColor.GOLD + "Player Warps"; + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + if (event.getView() == null) return; + if (event.getView().getTitle() == null) return; + if (!event.getView().getTitle().equalsIgnoreCase(inventoryTitle)) return; + + // Verhindert Verschieben und Entnehmen + event.setCancelled(true); + + ItemStack clickedItem = event.getCurrentItem(); + if (clickedItem == null || clickedItem.getType().isAir()) return; + if (!(event.getWhoClicked() instanceof Player)) return; + Player player = (Player) event.getWhoClicked(); + + if (!clickedItem.hasItemMeta() || !clickedItem.getItemMeta().hasDisplayName()) return; + String warpName = ChatColor.stripColor(clickedItem.getItemMeta().getDisplayName()); + + Warp warpFound = null; + for (Warp warp : warpManager.getWarps().values()) { + if (warp.getName().equalsIgnoreCase(warpName)) { + warpFound = warp; + break; + } + } + + if (warpFound == null) { + player.sendMessage(ChatColor.RED + "Warp nicht gefunden."); + player.closeInventory(); + return; + } + + if (Bukkit.getWorld(warpFound.getWorldName()) == null) { + player.sendMessage(ChatColor.RED + "Die Welt dieses Warps existiert nicht."); + player.closeInventory(); + return; + } + + player.teleport( + Bukkit.getWorld(warpFound.getWorldName()) + .getBlockAt((int) warpFound.getX(), (int) warpFound.getY(), (int) warpFound.getZ()) + .getLocation().add(0.5, 1.0, 0.5) // leicht über dem Block für sicheren Spawn + ); + player.sendMessage(ChatColor.GREEN + "Teleportiert zu Warp: " + warpName); + player.closeInventory(); + } + + @EventHandler + public void onInventoryDrag(InventoryDragEvent event) { + if (event.getView() == null) return; + if (event.getView().getTitle() == null) return; + if (!event.getView().getTitle().equalsIgnoreCase(inventoryTitle)) return; + event.setCancelled(true); + } +} diff --git a/src/main/java/de/viper/survivalplus/recipe/BackpackRecipe.java b/src/main/java/de/viper/survivalplus/recipe/BackpackRecipe.java new file mode 100644 index 0000000..6620eb9 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/recipe/BackpackRecipe.java @@ -0,0 +1,38 @@ +package de.viper.survivalplus.recipe; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.ShapedRecipe; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.java.JavaPlugin; + +public class BackpackRecipe { + + public static void register(JavaPlugin plugin, org.bukkit.configuration.file.FileConfiguration langConfig) { + // Ergebnis-Item: Truhe mit Namen "Rucksack" + ItemStack backpack = new ItemStack(Material.CHEST); + ItemMeta meta = backpack.getItemMeta(); + if (meta != null) { + meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', langConfig.getString("backpack.name", "&eRucksack"))); + backpack.setItemMeta(meta); + } + + NamespacedKey key = new NamespacedKey(plugin, "backpack"); + + ShapedRecipe recipe = new ShapedRecipe(key, backpack); + recipe.shape( + "S L", // Faden, leer, Leder + " C ", // leer, Truhe, leer + "S L" // Faden, leer, Leder + ); + recipe.setIngredient('S', Material.STRING); // Faden + recipe.setIngredient('L', Material.LEATHER); // Leder + recipe.setIngredient('C', Material.CHEST); // Truhe + + Bukkit.addRecipe(recipe); + plugin.getLogger().info("Backpack Rezept wurde registriert."); + } +} \ No newline at end of file diff --git a/src/main/java/de/viper/survivalplus/tasks/AutoClearTask.java b/src/main/java/de/viper/survivalplus/tasks/AutoClearTask.java new file mode 100644 index 0000000..abce24d --- /dev/null +++ b/src/main/java/de/viper/survivalplus/tasks/AutoClearTask.java @@ -0,0 +1,45 @@ +package de.viper.survivalplus.tasks; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.ChatColor; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Item; + +public class AutoClearTask implements Runnable { + + private final SurvivalPlus plugin; + + public AutoClearTask(SurvivalPlus plugin) { + this.plugin = plugin; + } + + @Override + public void run() { + FileConfiguration lang = plugin.getLangConfig(); + + // Vorwarnung + String warningMessage = ChatColor.translateAlternateColorCodes('&', + lang.getString("autoclear.warning", "&e&lAchtung! &rIn 10 Sekunden werden alle Items gelöscht!")); + Bukkit.broadcastMessage(warningMessage); + + // Nach 10 Sekunden: Items löschen + Bukkit.getScheduler().runTaskLater(plugin, () -> { + int totalRemoved = 0; + for (World world : Bukkit.getWorlds()) { + for (Item item : world.getEntitiesByClass(Item.class)) { + item.remove(); + totalRemoved++; + } + } + + // Bestätigung + String resultMessage = ChatColor.translateAlternateColorCodes('&', + lang.getString("autoclear.cleared", "&c&l%count% Items wurden automatisch gelöscht.")); + resultMessage = resultMessage.replace("%count%", String.valueOf(totalRemoved)); + Bukkit.broadcastMessage(resultMessage); + + }, 20L * 10); // 10 Sekunden Verzögerung + } +} diff --git a/src/main/java/de/viper/survivalplus/trade/TradeManager.java b/src/main/java/de/viper/survivalplus/trade/TradeManager.java new file mode 100644 index 0000000..5178aa6 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/trade/TradeManager.java @@ -0,0 +1,106 @@ +package de.viper.survivalplus.trade; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class TradeManager { + + private final SurvivalPlus plugin; + private final Map activeTrades = new HashMap<>(); + private final Map pendingRequests = new HashMap<>(); + + public TradeManager(SurvivalPlus plugin) { + this.plugin = plugin; + } + + public void requestTrade(Player sender, Player target) { + if (sender == null || target == null) return; + if (isTrading(sender) || isTrading(target)) { + sender.sendMessage(plugin.getLangConfig().getString("trade.already-trading", "§cEiner von euch ist bereits im Handel.")); + return; + } + + if (sender.getUniqueId().equals(target.getUniqueId())) { + sender.sendMessage(plugin.getLangConfig().getString("trade.with-yourself-not-allowed", "§cHandel mit dir selbst ist nicht erlaubt!")); + return; + } + + pendingRequests.put(target.getUniqueId(), sender.getUniqueId()); + + String requestText = plugin.getLangConfig().getString("trade.request-text", "%player% möchte mit dir handeln. Klicke hier, um anzunehmen!") + .replace("%player%", sender.getName()); + + try { + net.md_5.bungee.api.chat.TextComponent message = new net.md_5.bungee.api.chat.TextComponent(requestText); + message.setColor(net.md_5.bungee.api.ChatColor.GREEN); + message.setClickEvent(new net.md_5.bungee.api.chat.ClickEvent(net.md_5.bungee.api.chat.ClickEvent.Action.RUN_COMMAND, "/tradeaccept " + sender.getName())); + target.spigot().sendMessage(message); + } catch (Throwable ex) { + target.sendMessage(requestText + " (/tradeaccept " + sender.getName() + ")"); + } + + sender.sendMessage(plugin.getLangConfig().getString("trade.request-sent", "§aHandelsanfrage an %player% gesendet.").replace("%player%", target.getName())); + } + + public void acceptTrade(Player target, String requesterName) { + if (target == null || requesterName == null) return; + + UUID pendingSenderUUID = pendingRequests.get(target.getUniqueId()); + if (pendingSenderUUID == null) { + target.sendMessage(plugin.getLangConfig().getString("trade.no-pending-request", "§cKeine Handelsanfrage gefunden.")); + return; + } + + Player sender = Bukkit.getPlayer(pendingSenderUUID); + if (sender == null || !sender.isOnline()) { + target.sendMessage(plugin.getLangConfig().getString("trade.player-not-online", "§cDieser Spieler ist nicht online!")); + pendingRequests.remove(target.getUniqueId()); + return; + } + + pendingRequests.remove(target.getUniqueId()); + startTrade(sender, target); + } + + public void startTrade(Player sender, Player receiver) { + if (isTrading(sender) || isTrading(receiver)) { + sender.sendMessage(plugin.getLangConfig().getString("trade.already-trading", "§cEiner von euch ist bereits im Handel.")); + return; + } + + TradeSession session = new TradeSession(plugin, sender, receiver); + Bukkit.getPluginManager().registerEvents(session, plugin); + + activeTrades.put(sender.getUniqueId(), session); + activeTrades.put(receiver.getUniqueId(), session); + + session.openInventories(); + + sender.sendMessage(plugin.getLangConfig().getString("trade.started-sender", "§aTrade gestartet mit %player%").replace("%player%", receiver.getName())); + receiver.sendMessage(plugin.getLangConfig().getString("trade.started-receiver", "§a%player% hat dich zu einem Trade eingeladen.").replace("%player%", sender.getName())); + } + + public void endTrade(TradeSession session) { + if (session == null) return; + Player s = session.getSender(); + Player r = session.getReceiver(); + if (s != null) activeTrades.remove(s.getUniqueId()); + if (r != null) activeTrades.remove(r.getUniqueId()); + session.endSession(); + } + + public TradeSession getTrade(Player player) { + if (player == null) return null; + return activeTrades.get(player.getUniqueId()); + } + + public boolean isTrading(Player player) { + if (player == null) return false; + return activeTrades.containsKey(player.getUniqueId()); + } +} diff --git a/src/main/java/de/viper/survivalplus/trade/TradeSession.java b/src/main/java/de/viper/survivalplus/trade/TradeSession.java new file mode 100644 index 0000000..78c00d5 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/trade/TradeSession.java @@ -0,0 +1,165 @@ +package de.viper.survivalplus.trade; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +public class TradeSession implements Listener { + + private final SurvivalPlus plugin; + private final Player sender; + private final Player receiver; + + private final Inventory invSender; + private final Inventory invReceiver; + + private boolean senderConfirmed = false; + private boolean receiverConfirmed = false; + private boolean ended = false; // Flag gegen Rekursion + + public TradeSession(SurvivalPlus plugin, Player sender, Player receiver) { + this.plugin = plugin; + this.sender = sender; + this.receiver = receiver; + + String titleForSender = plugin.getLangConfig().getString("trade.inventory-title", "Handel mit %player%") + .replace("%player%", receiver.getName()); + String titleForReceiver = plugin.getLangConfig().getString("trade.inventory-title", "Handel mit %player%") + .replace("%player%", sender.getName()); + + this.invSender = Bukkit.createInventory(sender, 27, titleForSender); + this.invReceiver = Bukkit.createInventory(receiver, 27, titleForReceiver); + + addConfirmButton(invSender); + addConfirmButton(invReceiver); + } + + private void addConfirmButton(Inventory inv) { + ItemStack confirm = new ItemStack(Material.LIME_CONCRETE); + ItemMeta meta = confirm.getItemMeta(); + meta.setDisplayName(plugin.getLangConfig().getString("trade.confirm-button", "§aBestätigen")); + confirm.setItemMeta(meta); + inv.setItem(26, confirm); + } + + public Player getSender() { return sender; } + public Player getReceiver() { return receiver; } + + public void openInventories() { + if (sender.isOnline()) sender.openInventory(invSender); + if (receiver.isOnline()) receiver.openInventory(invReceiver); + } + + private void returnItems(Player player, Inventory inventory) { + for (int i = 0; i < 26; i++) { + ItemStack item = inventory.getItem(i); + if (item != null) player.getInventory().addItem(item); + } + } + + public void endSession() { + if (ended) return; // Rekursion verhindern + ended = true; + + // Items zurückgeben + if (sender.isOnline()) returnItems(sender, invSender); + if (receiver.isOnline()) returnItems(receiver, invReceiver); + + // Inventories schließen + if (sender.isOnline() && sender.getOpenInventory().getTopInventory() == invSender) { + sender.closeInventory(); + } + if (receiver.isOnline() && receiver.getOpenInventory().getTopInventory() == invReceiver) { + receiver.closeInventory(); + } + + HandlerList.unregisterAll(this); + } + + private Inventory getOwnInventory(Player p) { + return p.getUniqueId().equals(sender.getUniqueId()) ? invSender : invReceiver; + } + + private Inventory getOtherInventory(Player p) { + return p.getUniqueId().equals(sender.getUniqueId()) ? invReceiver : invSender; + } + + private void updateOtherView(Player p) { + Inventory own = getOwnInventory(p); + Inventory other = getOtherInventory(p); + for (int i = 0; i < 26; i++) { + other.setItem(i, own.getItem(i)); + } + } + + private void executeTrade() { + for (int i = 0; i < 26; i++) { + ItemStack itemFromSender = invSender.getItem(i); + ItemStack itemFromReceiver = invReceiver.getItem(i); + + if (itemFromSender != null) sender.getInventory().addItem(itemFromSender); + if (itemFromReceiver != null) receiver.getInventory().addItem(itemFromReceiver); + } + + String success = plugin.getLangConfig().getString("trade.success", "§aHandel erfolgreich abgeschlossen!"); + if (sender.isOnline()) sender.sendMessage(success); + if (receiver.isOnline()) receiver.sendMessage(success); + + endSession(); + } + + private void resetConfirmations() { + senderConfirmed = false; + receiverConfirmed = false; + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent e) { + if (!(e.getWhoClicked() instanceof Player p)) return; + if (!p.getUniqueId().equals(sender.getUniqueId()) && !p.getUniqueId().equals(receiver.getUniqueId())) return; + + Inventory top = e.getView().getTopInventory(); + Inventory clicked = e.getClickedInventory(); + int slot = e.getSlot(); + + if (clicked == null) return; + + // Bestätigen-Button + if (slot == 26 && clicked.equals(top)) { + e.setCancelled(true); + if (p.getUniqueId().equals(sender.getUniqueId())) senderConfirmed = true; + if (p.getUniqueId().equals(receiver.getUniqueId())) receiverConfirmed = true; + + p.sendMessage(plugin.getLangConfig().getString("trade.confirmed", "§aDu hast den Handel bestätigt!")); + + if (senderConfirmed && receiverConfirmed) executeTrade(); + return; + } + + // Nur das eigene Trade-Inventar editierbar + if (clicked.equals(top)) { + Bukkit.getScheduler().runTaskLater(plugin, () -> { + updateOtherView(p); + resetConfirmations(); + }, 1L); + } + } + + @EventHandler + public void onInventoryClose(InventoryCloseEvent e) { + if (!(e.getPlayer() instanceof Player p)) return; + if (p.getUniqueId().equals(sender.getUniqueId()) || p.getUniqueId().equals(receiver.getUniqueId())) { + // Kleine Verzögerung, um StackOverflow zu vermeiden + Bukkit.getScheduler().runTaskLater(plugin, this::endSession, 1L); + } + } +} diff --git a/src/main/java/de/viper/survivalplus/util/LockSystem.java b/src/main/java/de/viper/survivalplus/util/LockSystem.java new file mode 100644 index 0000000..028a8e1 --- /dev/null +++ b/src/main/java/de/viper/survivalplus/util/LockSystem.java @@ -0,0 +1,292 @@ +package de.viper.survivalplus.util; + +import de.viper.survivalplus.SurvivalPlus; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.scheduler.BukkitRunnable; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +public class LockSystem implements Listener, CommandExecutor { + + private final SurvivalPlus plugin; + private final Map lockedBlocks = new HashMap<>(); + private final Set lockMode = new HashSet<>(); + private final Map lockTimeoutTasks = new HashMap<>(); + + private final File lockFile; + private FileConfiguration lockConfig; + + public LockSystem(SurvivalPlus plugin) { + this.plugin = plugin; + this.lockFile = new File(plugin.getDataFolder(), "locks.yml"); + this.lockConfig = YamlConfiguration.loadConfiguration(lockFile); + loadLocks(); + } + + public static class LockData { + private final String ownerUUID; + private final Set friendsUUID; + + public LockData(String ownerUUID) { + this.ownerUUID = ownerUUID; + this.friendsUUID = new HashSet<>(); + } + + public String getOwnerUUID() { + return ownerUUID; + } + + public Set getFriendsUUID() { + return friendsUUID; + } + + public void addFriend(String friendUUID) { + friendsUUID.add(friendUUID); + } + + public void removeFriend(String friendUUID) { + friendsUUID.remove(friendUUID); + } + + public boolean isFriend(String uuid) { + return friendsUUID.contains(uuid); + } + } + + private void loadLocks() { + lockedBlocks.clear(); + if (!lockFile.exists()) return; + + for (String key : lockConfig.getKeys(false)) { + String path = key + "."; + String owner = lockConfig.getString(path + "owner"); + List friends = lockConfig.getStringList(path + "friends"); + + String[] parts = key.split("_"); + if (parts.length != 4) continue; + + String worldName = parts[0]; + double x = Double.parseDouble(parts[1]); + double y = Double.parseDouble(parts[2]); + double z = Double.parseDouble(parts[3]); + + Location loc = new Location(plugin.getServer().getWorld(worldName), x, y, z); + LockData lock = new LockData(owner); + lock.getFriendsUUID().addAll(friends); + + lockedBlocks.put(loc, lock); + } + } + + private void saveLocks() { + lockConfig = new YamlConfiguration(); + + for (Map.Entry entry : lockedBlocks.entrySet()) { + Location loc = entry.getKey(); + LockData lock = entry.getValue(); + String key = loc.getWorld().getName() + "_" + loc.getBlockX() + "_" + loc.getBlockY() + "_" + loc.getBlockZ(); + + lockConfig.set(key + ".owner", lock.getOwnerUUID()); + lockConfig.set(key + ".friends", new ArrayList<>(lock.getFriendsUUID())); + } + + try { + lockConfig.save(lockFile); + } catch (IOException e) { + plugin.getLogger().severe("Konnte locks.yml nicht speichern!"); + e.printStackTrace(); + } + } + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent event) { + if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; + + Block block = event.getClickedBlock(); + if (block == null) return; + + Player player = event.getPlayer(); + UUID uuid = player.getUniqueId(); + Location loc = block.getLocation(); + boolean isLockable = block.getState() instanceof InventoryHolder || block.getType().name().contains("DOOR"); + + if (lockMode.contains(uuid)) { + lockMode.remove(uuid); + + BukkitRunnable timeout = lockTimeoutTasks.remove(uuid); + if (timeout != null) timeout.cancel(); + + if (!isLockable) { + player.sendMessage(plugin.getMessage("lock.not-lockable")); + return; + } + + if (lockedBlocks.containsKey(loc)) { + player.sendMessage(plugin.getMessage("lock.already-locked")); + } else { + lockedBlocks.put(loc, new LockData(uuid.toString())); + saveLocks(); + player.sendMessage(plugin.getMessage("lock.locked")); + } + + event.setCancelled(true); + return; + } + + if (!isLockable) return; + if (!lockedBlocks.containsKey(loc)) return; + + LockData lock = lockedBlocks.get(loc); + String playerUUID = uuid.toString(); + + if (lock.getOwnerUUID().equals(playerUUID) || lock.isFriend(playerUUID) || player.isOp()) { + return; + } + + player.sendMessage(plugin.getMessage("lock.block-denied")); + event.setCancelled(true); + } + + @EventHandler + public void onBlockBreak(BlockBreakEvent event) { + Player player = event.getPlayer(); + Block block = event.getBlock(); + Location loc = block.getLocation(); + + if (!lockedBlocks.containsKey(loc)) return; + + LockData lock = lockedBlocks.get(loc); + String playerUUID = player.getUniqueId().toString(); + + if (lock.getOwnerUUID().equals(playerUUID) || lock.isFriend(playerUUID) || player.isOp()) { + return; + } + + player.sendMessage(plugin.getMessage("lock.break-denied")); + event.setCancelled(true); + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage(plugin.getMessage("lock.only-players")); + return true; + } + + Player player = (Player) sender; + UUID uuid = player.getUniqueId(); + + // NEU: Lock als Subcommand von /sp + if (args.length == 0 || args[0].equalsIgnoreCase("lock")) { + if (lockMode.contains(uuid)) { + player.sendMessage(plugin.getMessage("lock.mode-already")); + return true; + } + + lockMode.add(uuid); + player.sendMessage(plugin.getMessage("lock.mode-start")); + + BukkitRunnable timeout = new BukkitRunnable() { + @Override + public void run() { + if (lockMode.remove(uuid)) { + player.sendMessage(plugin.getMessage("lock.mode-timeout")); + lockTimeoutTasks.remove(uuid); + } + } + }; + timeout.runTaskLater(plugin, 20 * 30); // 30 Sekunden + lockTimeoutTasks.put(uuid, timeout); + return true; + } + + String sub = args[0].toLowerCase(); + Block targetBlock = player.getTargetBlockExact(5); + if (targetBlock == null) { + player.sendMessage(plugin.getMessage("lock.no-target-block")); + return true; + } + + Location loc = targetBlock.getLocation(); + + switch (sub) { + case "unlock": + LockData lock = lockedBlocks.get(loc); + if (lock == null) { + player.sendMessage(plugin.getMessage("lock.not-locked")); + } else if (!lock.getOwnerUUID().equals(uuid.toString()) && !player.isOp()) { + player.sendMessage(plugin.getMessage("lock.no-permission-unlock")); + } else { + lockedBlocks.remove(loc); + saveLocks(); + player.sendMessage(plugin.getMessage("lock.unlocked")); + } + break; + + case "friendadd": + if (args.length < 2) { + player.sendMessage(plugin.getMessage("lock.friendadd.usage")); + return true; + } + lock = lockedBlocks.get(loc); + if (lock == null) { + player.sendMessage(plugin.getMessage("lock.not-locked")); + } else if (!lock.getOwnerUUID().equals(uuid.toString()) && !player.isOp()) { + player.sendMessage(plugin.getMessage("lock.no-permission-friends")); + } else { + Player friend = player.getServer().getPlayer(args[1]); + if (friend == null) { + player.sendMessage(plugin.getMessage("lock.friendadd.not-found")); + } else { + lock.addFriend(friend.getUniqueId().toString()); + saveLocks(); + player.sendMessage(plugin.getMessage("lock.friendadd.success").replace("{player}", friend.getName())); + } + } + break; + + case "friendremove": + if (args.length < 2) { + player.sendMessage(plugin.getMessage("lock.friendremove.usage")); + return true; + } + lock = lockedBlocks.get(loc); + if (lock == null) { + player.sendMessage(plugin.getMessage("lock.not-locked")); + } else if (!lock.getOwnerUUID().equals(uuid.toString()) && !player.isOp()) { + player.sendMessage(plugin.getMessage("lock.no-permission-friends")); + } else { + Player friend = player.getServer().getPlayer(args[1]); + if (friend == null) { + player.sendMessage(plugin.getMessage("lock.friendremove.not-found")); + } else { + lock.removeFriend(friend.getUniqueId().toString()); + saveLocks(); + player.sendMessage(plugin.getMessage("lock.friendremove.success").replace("{player}", friend.getName())); + } + } + break; + + default: + player.sendMessage(plugin.getMessage("lock.unknown-subcommand")); + break; + } + + return true; + } +} diff --git a/src/main/resources/backpacks.yml b/src/main/resources/backpacks.yml new file mode 100644 index 0000000..970b634 --- /dev/null +++ b/src/main/resources/backpacks.yml @@ -0,0 +1,2 @@ +# Backpacks configuration file +backpacks: {} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..9db582b --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,246 @@ +# Neulings Schutz +newbie-protection: + enabled: true + duration-minutes: 30 + +# Soll keepInventory automatisch beim Start gesetzt werden? +set-keepinventory: true + +# SurvivalPlus - Block Kontrolle +blocks: + command-blocks: + enabled: false # true = Command Blocks sind erlaubt, false = verboten und werden entfernt + structure-blocks: + enabled: false # true = Structure Blocks sind erlaubt, false = verboten und werden entfernt + notify-admins-on-possession: true + +# Sollen alle Spieler beim Joinen automatisch in Survival gesetzt werden? +force-survival: true + +# Warp Default Item +defaultWarpItem: OAK_SIGN +# Anzahl der erlaubten Warps für Member +maxWarpsPerPlayer: 5 + +# Anzahl der erlaubten Homes für Member +max-homes: 3 + +# Item Reparaturkosten (GOLD_INGOT - IRON_INGOT - DIAMOND - NETHERITE_INGOT - COPPER_INGOT - SMARAGD - LAPIS_LAZULI) +repair: + currency: GOLD_INGOT + price: 5 + +# Aktivieren oder Deaktivieren des automatischen Löschens von Items +auto-clear-enabled: true +# Intervall in Minuten für das automatische Löschen herumliegender Items +auto-clear-interval-minutes: 15 + +# Zeit in Sekunden, nach der Gräber verschwinden (Standard: 30 Minuten) +graves: + despawn-time: 1800 + +# AFK Aktivieren/Deaktivieren (Wenn du kick-after-seconds auf 0 setzt, wird kein Spieler gekickt, nur AFK markiert.) +afk: + enabled: true + # nach 5 Minuten Inaktivität als AFK markieren + afk-after-seconds: 300 + # nach 15 Minuten wird Spieler gekickt + kick-after-seconds: 900 + +# Nacht/Schlafmodus +sleep: + required-percentage: 50.0 + +# Mob-Leash-Limit +mob-leash-limit: + enabled: true + max-leash-count: 5 + +# Mobcap-Begrenzung +mob-cap: + enabled: true + max-animals-per-chunk: 10 + +# Spawn Schutz Radius +spawnprotection: + enabled: true + radius: 40 + protect-block-break: true + protect-block-place: true + protect-pvp: true + bypass-permission: survivalplus.spawnprotection.bypass + +# Start Kit (Minecraft item, Menge, Custom-Name) +first-join-kit: + items: + - "bread,2,§6Bernd das Brot" + - "apple,1,§7Dornröschen Apfel" + - "wooden_sword,1,§eSchwert" + - "suspicious_stew,1,§6Heldensuppe" + +# Werkzeug Upgrade +tool-upgrades: + max-level: 5 + levels: + 1: + material: COPPER_INGOT + 2: + material: IRON_INGOT + 3: + material: GOLD_INGOT + 4: + material: DIAMOND + 5: + material: NETHERITE_INGOT + +# Verbotene Nicknamen +forbidden-nicks: + - admin + - mod + - owner + - staff + - Hitler + - Adolf + - Nazi + - Führer + - SS + - KZ + - Konzentrationslager + - Nazi Deutschland + - Holocaust + - Drittes Reich + - Juden + - Holocaust + - Shoah + - Antisemitismus + +# LootChest Konfiguration +lootchest: + enabled: false + interval-minutes: 30 + despawn-minutes: 10 + spawn-count: 5 + max-active-chests: 10 + max-loot-per-player: 3 + loot-limit-reset-minutes: 60 + world: world + spawn-radius: 500 + items: + # --- Essen & Trinken --- + bread: + material: BREAD + name: "&eFrisches Brot" + chance: 1.0 + min: 2 + max: 4 + apple: + material: APPLE + name: "&cRoter Apfel" + chance: 0.9 + min: 1 + max: 3 + cooked_beef: + material: COOKED_BEEF + name: "&6Gebratenes Steak" + chance: 0.8 + min: 2 + max: 5 + baked_potato: + material: BAKED_POTATO + name: "&eOfenkartoffel" + chance: 0.8 + min: 1 + max: 2 + mushroom_stew: + material: MUSHROOM_STEW + name: "&6Pilzsuppe" + chance: 0.6 + min: 1 + max: 2 + suspicious_stew: + material: SUSPICIOUS_STEW + name: "&dGeheime Suppe" + chance: 0.4 + min: 1 + max: 1 + honey_bottle: + material: HONEY_BOTTLE + name: "&6Honigflasche" + chance: 0.5 + min: 1 + max: 2 + + # --- Werkzeuge --- + wooden_sword: + material: WOODEN_SWORD + name: "&6Einfaches Holzschwert" + chance: 0.4 + min: 1 + max: 1 + wooden_pickaxe: + material: WOODEN_PICKAXE + name: "&6Einfache Holzspitzhacke" + chance: 0.8 + min: 1 + max: 1 + wooden_axe: + material: WOODEN_AXE + name: "&6Einfache Holzaxt" + chance: 0.7 + min: 1 + max: 1 + stone_sword: + material: STONE_SWORD + name: "&7Steinschwert" + chance: 0.5 + min: 1 + max: 1 + stone_pickaxe: + material: STONE_PICKAXE + name: "&7Steinspitzhacke" + chance: 0.5 + min: 1 + max: 1 + stone_axe: + material: STONE_AXE + name: "&7Steinaxt" + chance: 0.4 + min: 1 + max: 1 + +funChallenges: + - name: "Karottenjäger" + description: "Sammle 100 Karotten in 1 Stunde" + type: "COLLECT" + item: "CARROT" + amount: 100 + timeLimitMinutes: 60 + active: false # per Command/Script auf true setzen! + reward: "DIAMOND,1" # Item und Menge des Rewards + + - name: "Eisen-Farmer" + description: "Sammle 50 Eisenbarren in 30 Minuten" + type: "COLLECT" + item: "IRON_INGOT" + amount: 50 + timeLimitMinutes: 30 + active: false + reward: "EMERALD,2" + + - name: "Holzsammler" + description: "Sammle 200 Holzbretter in 45 Minuten" + type: "COLLECT" + item: "OAK_PLANKS" # Minecraft Material-Name für Holzbretter + amount: 200 + timeLimitMinutes: 45 + active: false + reward: "GOLD_INGOT,5" + + - name: "Schlaf-Challenge" + description: "Schlafe 10 Mal in 1 Stunde" + type: "ACTION" + action: "SLEEP" + amount: 10 + timeLimitMinutes: 60 + active: false + reward: "SLEEPING_BAG,1" \ No newline at end of file diff --git a/src/main/resources/friends.yml b/src/main/resources/friends.yml new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/graves.yml b/src/main/resources/graves.yml new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/help.yml b/src/main/resources/help.yml new file mode 100644 index 0000000..36b687a --- /dev/null +++ b/src/main/resources/help.yml @@ -0,0 +1,223 @@ +header: "&6=== SurvivalPlus Hilfe ===" +footer: "&6===========================" + +commands: + gm: + description: "&eWechselt den Spielmodus (survival, creative, adventure, spectator)." + usage: "&b/gm [spieler]" + + sp: + description: "&eHauptbefehl für SurvivalPlus mit Unterbefehlen." + usage: "&b/sp " + + share: + description: "&eTeilt deine aktuellen Koordinaten nach Bestätigung mit allen Spielern." + usage: "&b/sp share" + + shareconfirm: + description: "&eBestätigt das Teilen deiner Koordinaten mit allen Spielern." + usage: "&b/sp shareconfirm" + + sharecancel: + description: "&eBricht das Teilen deiner Koordinaten ab." + usage: "&b/sp sharecancel" + + sethome: + description: "&eSetzt einen neuen Homepunkt." + usage: "&b/sethome " + + delhome: + description: "&eLöscht einen Homepunkt." + usage: "&b/delhome " + + homelist: + description: "&eZeigt alle deine Homes." + usage: "&b/homelist" + + home: + description: "&eTeleportiert dich zu einem gespeicherten Home." + usage: "&b/home " + + inv: + description: "&eÖffnet dein Inventar oder das eines anderen Spielers." + usage: "&b/inv " + + ec: + description: "&eÖffnet deine Endertruhe oder die eines anderen Spielers." + usage: "&b/ec [spieler]" + + setspawn: + description: "&eSetzt den Spawnpunkt der Welt." + usage: "&b/setspawn" + + setworldspawn: + description: "&eSetzt den globalen Weltspawnpunkt." + usage: "&b/setworldspawn" + + clearchat: + description: "&eLöscht den Chat für alle Spieler." + usage: "&b/clearchat" + + clearitems: + description: "&eEntfernt alle Items auf dem Boden." + usage: "&b/clearitems" + + closedoors: + description: "&eSchließt alle Türen im angegebenen Radius." + usage: "&b/closedoors " + + sit: + description: "&eSetzt dich hin oder steht wieder auf." + usage: "&b/sit" + + back: + description: "&eTeleportiert dich zurück zum letzten Todespunkt." + usage: "&b/back" + + friend: + description: "&eVerwalte deine Freundesliste (hinzufügen, entfernen, anzeigen, teleportieren). Unterstützt anklickbare Anfragen und Bestätigungen." + usage: "&b/friend [Spieler]" + + ir: + description: "&eBenennt Items um." + usage: "&b/ir " + + showarmorstands: + description: "&eZeigt unsichtbare ArmorStands an." + usage: "&b/showarmorstands" + + cleardebugarmorstands: + description: "&eLöscht alle Debug ArmorStands." + usage: "&b/cleardebugarmorstands" + + trash: + description: "&eÖffnet den Mülleimer." + usage: "&b/trash" + + workbench: + description: "&eÖffnet eine Werkbank GUI." + usage: "&b/workbench" + + anvil: + description: "&eÖffnet eine Amboss GUI." + usage: "&b/anvil" + + stats: + description: "&eZeigt deine persönlichen Statistiken an." + usage: "&b/stats" + + spawn: + description: "&eTeleportiert dich zum Welt-Spawnpunkt." + usage: "&b/spawn" + + lock: + description: "&eSchützt Container mit dem LockSystem." + usage: "&b/lock [Spieler]" + + tp: + description: "&eTeleportiert dich zu einem anderen Spieler." + usage: "&b/tp " + + tphere: + description: "&eTeleportiert einen Spieler zu dir." + usage: "&b/tphere " + + tpa: + description: "&eSendet eine Teleport-Anfrage an einen Spieler." + usage: "&b/tpa " + + tpaccept: + description: "&eAkzeptiert eine Teleport-Anfrage." + usage: "&b/tpaccept" + + tpdeny: + description: "&eLehnt eine Teleport-Anfrage ab." + usage: "&b/tpdeny" + + block: + description: "&eBlockiert Nachrichten eines Spielers." + usage: "&b/block " + + unblock: + description: "&eHebt eine Blockierung auf." + usage: "&b/unblock " + + blocklist: + description: "&eZeigt eine Liste blockierter Spieler." + usage: "&b/blocklist" + + kit: + description: "&eErhalte dein Starterkit." + usage: "&b/kit" + + leashcount: + description: "&eZeigt die Anzahl der angeleinten Tiere an." + usage: "&b/leashcount" + + nick: + description: "&eÄndert deinen angezeigten Namen mit Farben (&) und Hex-Farben (#RRGGBB)." + usage: "&b/nick " + + trade: + description: "&eStartet einen Handel mit einem Spieler." + usage: "&b/trade " + + tradeaccept: + description: "&eAkzeptiert eine Handelsanfrage." + usage: "&b/tradeaccept " + + day: + description: "&eSetzt die Zeit auf Tag." + usage: "&b/day" + + night: + description: "&eSetzt die Zeit auf Nacht." + usage: "&b/night" + + report: + description: "&eMeldet einen Spieler an die Admins." + usage: "&b/report [Grund]" + + showreport: + description: "&eZeigt alle Reports eines Spielers an." + usage: "&b/showreport " + + clearreport: + description: "&eLöscht alle Reports eines Spielers." + usage: "&b/clearreport " + + shop: + description: "&eVerwaltet den Server-Shop (z.B. Items hinzufügen)." + usage: "&b/shop add " + +messages: + header: "&6=== Befehle ===" + footer: "&6================" + navigation: + prev: "&7« Vorherige " + next: "&7 Nächste »" + prev-disabled: "&8« " + next-disabled: "&8 »" + page: "&fSeite {current} von {total} " + sp: + invalid-subcommand: "&cUngültiger Unterbefehl! Verwendung: /sp [reload|help|info|share]" + no-permission: "&cDu hast keine Berechtigung für diesen Befehl!" + plugin: + reloaded: "&aSurvivalPlus wurde erfolgreich neu geladen!" + info: + header: "&7===== SurvivalPlus Info =====" + name: "&ePlugin-Name: &f" + version: "&eVersion: &f" + author: "&eErsteller: &f" + description: "&eBeschreibung:\\n&f" + footer: "&7==========================" + share: + preview-title: "&aDeine aktuellen Koordinaten wären:" + preview-format: "&e%player% &7teilt Koordinaten: &eX: %x%, Y: %y%, Z: %z% &7in Welt &e%world%" + send-button: "&a[✅ Senden]" + cancel-button: "&c [❌ Abbrechen]" + send-hover: "&aKlicke, um deine Koordinaten an alle zu senden." + 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!" diff --git a/src/main/resources/homes.yml b/src/main/resources/homes.yml new file mode 100644 index 0000000..427555b --- /dev/null +++ b/src/main/resources/homes.yml @@ -0,0 +1,3 @@ +# Diese Datei speichert die Home-Daten der Spieler +# Format: homes...{world,x,y,z,yaw,pitch} +homes: {} \ No newline at end of file diff --git a/src/main/resources/lang.yml b/src/main/resources/lang.yml new file mode 100644 index 0000000..1d6b32e --- /dev/null +++ b/src/main/resources/lang.yml @@ -0,0 +1,406 @@ +sp: + no-permission: "§cDu hast keine Berechtigung für diesen Befehl!" + plugin.reloaded: "§aSurvivalPlus wurde erfolgreich neu geladen!" + invalid-subcommand: "§cUngültiger Unterbefehl! Verwendung: /sp [ reload | help | info | share ]" + help-not-found: "§cHilfedatei (help.yml) konnte nicht geladen werden!" + + share: + preview-title: "§aDeine aktuellen Koordinaten wären:" + preview-format: "§6%player% §eist bei X: §b%x% §eY: §b%y% §eZ: §b%z% §ein Welt: §b%world%" + send-button: "§a[✅ Senden]" + send-hover: "§aKlicke, um deine Koordinaten an alle zu senden." + cancel-button: "§c [❌ Abbrechen]" + cancel-hover: "§cKlicke, um das Senden abzubrechen." + sent: "§aKoordinaten gesendet." + cancelled: "§eSenden der Koordinaten abgebrochen." + +plugin: + enabled: "&aSurvivalPlus wurde erfolgreich aktiviert!" + reloaded: "&aSurvivalPlus wurde erfolgreich neu geladen!" + disabled: "&cSurvivalPlus wurde deaktiviert." + info: "&6SurvivalPlus Plugin-Info: Version %version%" + +no-permission: "§cDu hast keine Berechtigung für diesen Befehl!" +no-permission-others: "§cDu hast keine Berechtigung, den Spielmodus anderer Spieler zu ändern!" +no-permission-others-ec: "§cDu hast keine Berechtigung, die Enderchest anderer Spieler anzusehen!" +no-permission-others-inv: "§cDu hast keine Berechtigung, das Inventar anderer Spieler anzusehen!" +player-not-found: "§cSpieler nicht gefunden!" +player-only: "§cDieser Befehl ist nur für Spieler!" + + +autoclear: + cleared: "&c&l%count% Items wurden automatisch gelöscht." + warning: "&e&lAchtung! &rIn 10 Sekunden werden alle Items gelöscht!" + +clearchat: + cleared: "§aDer Chat wurde erfolgreich gelöscht." + no-permission: "§cDu hast keine Rechte, den Chat zu löschen." + +clearitems: + no-permission: "§cDu hast keine Rechte, Items zu löschen." + +closedoors: + invalidradius: "Der Radius muss eine gültige positive Zahl sein!" + noplayer: "Nur Spieler können diesen Befehl nutzen!" + nopermission: "Dafür hast du keine Rechte!" + success: "Es wurden %count% Türen geschlossen." + usage: "Benutzung: /closedoors " + +delhome: + not-found: "§cDieses Home existiert nicht!" + success: "§aHome %name% wurde gelöscht!" + usage: "§cVerwendung: /delhome " + +enderchest: + data-not-found: "§cDie Enderchest-Daten des Spielers konnten nicht gefunden werden! Der Spieler war möglicherweise nie auf diesem Server." + gui-title: "Enderchest von " + load-error: "§cFehler beim Laden der Enderchest: %error%" + opened: "§aEnderchest von %player% geöffnet!" + usage: "§cVerwendung: /ec [spieler]" + +gamemode: + adventure: "Abenteuer" + changed-other: "§aSpielmodus von %player% zu %mode% geändert!" + changed-self: "§aDein Spielmodus wurde zu %mode% geändert!" + creative: "Kreativ" + invalid-gamemode: "§cUngültiger Spielmodus! Verwende 0, 1, 2 oder 3" + spectator: "Zuschauer" + survival: "Überleben" + usage: "§cVerwendung: /gm <0|1|2|3> [spieler]" + +homelist: + gui-title: "Deine Homes" + home-deleted: "§cDieses Home existiert nicht mehr!" + no-homes: "§cDu hast keine Homes gesetzt!" + teleported: "§aZum Home %name% teleportiert!" + +inventory: + data-not-found: "§cDie Inventardaten des Spielers konnten nicht gefunden werden! Der Spieler war möglicherweise nie auf diesem Server." + gui-title: "Inventar von " + load-error: "§cFehler beim Laden des Inventars: %error%" + opened: "§aInventar von %player% geöffnet!" + usage: "§cVerwendung: /inv " + +sethome: + already-exists: "§cEin Home mit diesem Namen existiert bereits!" + invalid-name: "§cDer Home-Name darf nur Buchstaben, Zahlen und Unterstriche enthalten!" + limit-reached: "§cDu hast die maximale Anzahl an Homes erreicht!" + success: "§aHome %name% wurde gesetzt!" + usage: "§cVerwendung: /sethome " + +setspawn: + no-permission: "§cDu hast keine Berechtigung, den Spawnpunkt zu setzen!" + success: "§aSpawnpunkt wurde erfolgreich gesetzt!" + +setworldspawn: + no-permission: "§cDu hast keine Berechtigung, den Weltspawn zu setzen!" + success: "§aWeltspawn wurde erfolgreich gesetzt!" + +sit: + stand-up: "§aDu bist aufgestanden!" + success: "§aDu hast dich hingesetzt!" + usage: "§cVerwendung: /sit" + +graves: + created: "§aDein Grab wurde bei x=%.2f, y=%.2f, z=%.2f erstellt!" + despawned: "§cDein Grab bei x=%.2f, y=%.2f, z=%.2f ist verschwunden!" + access-denied: "§cNur der Eigentümer kann dieses Grab öffnen!" + +back: + usage: "§cVerwendung: /back" + no-death-point: "§cDu hast keinen Todespunkt!" + success: "§aTeleportiert zum Todespunkt bei x=%.2f, y=%.2f, z=%.2f!" + +msg: + description: "Sende eine private Nachricht an einen Spieler." + usage: "/msg " + +r: + description: "Antworte auf die letzte private Nachricht." + usage: "/r " + +pm: + description: "Schalte private Nachrichten an oder aus." + usage: "/pm toggle" + +backpack: + name: "&eRucksack" + inventory-title: "&eDein Rucksack" + +friend: + error: + player-only: "&cDieser Befehl ist nur für Spieler!" + player-not-found: "&cSpieler %s nicht gefunden!" + self: "&cDu kannst dich nicht selbst hinzufügen!" + already-friends: "&cDu bist bereits mit %s befreundet!" + request-pending: "&cDu hast bereits eine Anfrage an %s gesendet!" + no-request: "&cKeine Anfrage von %s gefunden!" + not-friends: "&c%s ist nicht in deiner Freundesliste!" + different-world: "&cIhr müsst in derselben Welt sein, um zu teleportieren!" + add-usage: "&cVerwendung: /friend add " + accept-usage: "&cVerwendung: /friend accept " + deny-usage: "&cVerwendung: /friend deny " + list-usage: "&cVerwendung: /friend list" + del-usage: "&cVerwendung: /friend del " + confirm-usage: "&cVerwendung: /friend confirm " + tp-usage: "&cVerwendung: /friend tp " + + add: + sent: "&aFreundschaftsanfrage an %s gesendet!" + received: "&aDu hast eine Freundschaftsanfrage von %s erhalten! " + accept-button: "&a[Accept]" + deny-button: "&c [Deny]" + + accept: + success: "&aDu bist jetzt mit %s befreundet!" + notify: "&a%s hat deine Freundschaftsanfrage akzeptiert!" + + deny: + success: "&aFreundschaftsanfrage von %s abgelehnt." + notify: "&c%s hat deine Freundschaftsanfrage abgelehnt." + + list: + header: "&6=== Deine Freundesliste ===" + entry: "&e%s: %s" + entry-offline: "&e%s: %s &7(Zuletzt online: %s)" + online: "&aOnline" + offline: "&7Offline" + unknown: "&7Unbekannt" + date-format: "dd.MM.yyyy HH:mm:ss" + remove-button: "&c[X]" + footer: "&6=====================" + + del: + success: "&a%s wurde aus deiner Freundesliste entfernt." + notify: "&c%s hat dich aus seiner Freundesliste entfernt." + confirm: "&cMöchtest du %s wirklich aus deiner Freundesliste entfernen? " + confirm-button: "&a[Confirm]" + cancel-button: "&c[Cancel]" + + tp: + success: "&aDu wurdest zu %s teleportiert!" + notify: "&a%s hat sich zu dir teleportiert." + + join: + notify: "&aDein Freund %s ist dem Server beigetreten." + +ir: + only-player: "&cDieser Befehl kann nur von Spielern ausgeführt werden." + no-permission: "&cDu hast keine Berechtigung, diesen Befehl zu benutzen." + no-name: "&cBitte gib einen neuen Namen an." + usage: "&eBenutze: /ir " + no-item: "&cDu hältst kein Item in der Hand." + cant-rename: "&cDieses Item kann nicht umbenannt werden." + success: "&aDas Item wurde erfolgreich in {name} umbenannt!" + +afk: + set: "&7Du bist jetzt AFK." + unset: "&7Du bist nicht mehr AFK." + +sleep: + skipped: "&aDie Nacht wurde übersprungen! (%sleeping% von %total% Spielern haben geschlafen)" + progress: "&e%sleeping%/%total% Spielern schlafen... (%required%% benötigt zum Überspringen der Nacht)" + +orealarm: + message: "§c[Erz-Alarm] Spieler §e%player% §chat in %seconds% Sekunden ungewöhnlich viele %ore% abgebaut: §e%count%" + +stats: + header: "§6§l========== §e§lStatistik von {player} §6§l==========" + playtime: "§7Spielzeit: §a{time}" + kills: "§7Kills: §a{kills}" + deaths: "§7Tode: §a{deaths}" + kd_ratio: "§7K/D Ratio: §a{kd}" + blocks_placed: "§7Blöcke platziert: §a{placed}" + blocks_broken: "§7Blöcke abgebaut: §a{broken}" + jumps: "§7Gesprungen: §a{jumps}" + walked: "§7Gelaufene Strecke: §a{walked} km" + footer: "§6§l=====================================" + no_stats: "§cEs sind noch keine Statistiken für dich vorhanden." + only_player: "§cNur Spieler können diesen Befehl nutzen!" + +spawn: + only_player: "§cNur Spieler können diesen Befehl verwenden." + no_spawn_found: "§cKein Spawnpunkt in deiner Welt gefunden." + teleported: "§aDu wurdest zum Spawnpunkt teleportiert." + +welcome: + first-join-message: "&aWillkommen auf dem Server, &e{player}&a! Viel Spaß!" + welcome.first-join-sound: ENTITY_FIREWORK_ROCKET_LAUNCH + welcome.first-join-sound-volume: 1.0 + welcome.first-join-sound-pitch: 1.0 + return-message: "&6Willkommen zurück, &e{player}&6!" + welcome.return-sound: ENTITY_FIREWORK_ROCKET_BLAST + welcome.return-sound-volume: 1.0 + welcome.return-sound-pitch: 1.0 + +mob-leash-limit: + max-reached: "§cDu hast das maximale Leinen-Limit von §e%count%§c erreicht!" + leashed: "§aTier angeleint. Du hast jetzt §e%count%§a geleinte Tiere." + unleashed: "§aTier abgeleint. Du hast jetzt §e%count%§a geleinte Tiere." + entity-died: "§cEin angeleintes Tier ist gestorben. Du hast jetzt §e%count%§c geleinte Tiere." + +mob-cap: + limit-reached: "&cZu viele Tiere in diesem Chunk! Maximal %max% Tiere erlaubt." + animal-removed: "&cEin Tier wurde entfernt, da das Chunk-Limit von %max% erreicht wurde." + +spawnprotection: + blockbreak-denied: "&cDu kannst im Spawnbereich keine Blöcke abbauen." + blockplace-denied: "&cDu kannst im Spawnbereich keine Blöcke platzieren." + pvp-denied: "&cPvP ist im Spawnbereich deaktiviert." + +lock: + not-lockable: "§cDieser Block kann nicht gesperrt werden." + already-locked: "§cDieser Block ist bereits gesperrt." + locked: "§aDer Block wurde erfolgreich gesperrt." + block-denied: "§cDu darfst diesen Block nicht benutzen, er ist gesperrt." + break-denied: "§cDu darfst diesen Block nicht abbauen, er ist gesperrt." + only-players: "§cNur Spieler können diesen Befehl ausführen." + mode-already: "§cDu bist bereits im Lock-Modus." + mode-start: "§aLock-Modus aktiviert. Klicke auf einen Block, um ihn zu sperren." + mode-timeout: "§cDer Lock-Modus wurde beendet." + usage: "§cVerwendung: /lock [unlock|friendadd|friendremove]" + + not-locked: "§cDieser Block ist nicht gesperrt." + no-permission-unlock: "§cDu darfst diesen Block nicht entsperren." + unlocked: "§aBlock erfolgreich entsperrt." + + friendadd: + usage: "§cVerwendung: /lock friendadd " + not-found: "§cSpieler nicht gefunden." + success: "§a{player} wurde als Freund hinzugefügt." + + friendremove: + usage: "§cVerwendung: /lock friendremove " + not-found: "§cSpieler nicht gefunden." + success: "§a{player} wurde als Freund entfernt." + + no-permission-friends: "§cDu darfst Freunde nur für deine eigenen Blöcke verwalten." + unknown-subcommand: "§cUnbekannter Unterbefehl." + no-target-block: "§cDu musst auf einen Block schauen, um diesen Befehl auszuführen." + +teleport-usage: "§cVerwendung: /tp " +teleport-success: "§aDu wurdest zu %player% teleportiert!" + +tphere-usage: "§cVerwendung: /tphere " +tphere-success: "§a%player% wurde zu dir teleportiert!" + +tpa-usage: "§cVerwendung: /tpa " +tpa-sent: "§aTeleportanfrage an %player% gesendet!" +tpa-received: "§e%player% möchte sich zu dir teleportieren. Nutze /tpaccept oder /tpdeny." + +tpaccept-success: "§aTeleportanfrage von %player% akzeptiert!" +tpa-accepted: "§aDeine Teleportanfrage wurde von %player% angenommen." + +tpdeny-success: "§cTeleportanfrage abgelehnt!" +tpa-denied: "§cDeine Teleportanfrage wurde von %player% abgelehnt." + +no-tpa-request: "§cDu hast keine ausstehende Teleportanfrage." + +no-permission: "§cDu hast keine Berechtigung für diesen Befehl!" +only-players: "§cDieser Befehl kann nur von Spielern ausgeführt werden!" +player-not-found: "§cSpieler %player% nicht gefunden!" + +block: + usage: "Benutze: /block " + invalid_player: "Ungültiger Spieler." + blocked: "Du hast §e%player%§c blockiert." + already_blocked: "Du hast §e%player%§c schon blockiert." + unblocked: "Du hast §e%player%§a entblockt." + +unblock: + usage: "Benutze: /unblock " + invalid_player: "Ungültiger Spieler." + not_blocked: "Du hast §e%player%§c nicht blockiert." + unblocked: "Du hast §e%player%§a entblockt." + unblocked_by: "Du wurdest von §e%player%§a entblockt." + +blocklist: + no_blocked_players: "§7Du hast aktuell niemanden blockiert." + blocked_players: "§7Blockierte Spieler: §e%list%" + +repair: + sign_created: "&aReparaturschild erstellt!" + no_permission_create: "&cDu hast keine Berechtigung, Reparaturschilder zu erstellen!" + no_edit: "&cDu darfst dieses Reparaturschild nicht bearbeiten!" + no_break: "&cNur Operatoren dürfen dieses Reparaturschild abbauen!" + not_repairable: "&cDas Item in deiner Hand kann nicht repariert werden!" + invalid_currency: "&cUngültige Währung in der Config!" + not_enough_currency: "&cDu hast nicht genug %currency%!" + success: "&aDein Item wurde erfolgreich repariert mit %price% %currency%!" + already_fully_repaired: "&cDas Item ist bereits vollständig repariert!" + +lootchest: + spawn-simple: "&aNeue Loot-Kisten sind gespawnt!" + despawn-msg: "&eEine Lootkiste ist von selbst verschwunden." + removed-msg: "&aEine Lootkiste wurde geleert und entfernt." + limit-reached: "&cDu hast dein Loot-Limit erreicht!" + player-only: "&cNur Spieler können diesen Befehl nutzen." + no-permission: "&cDazu hast du keine Berechtigung." + no-chests: "&eZur Zeit sind keine Loot-Kisten aktiv." + list-header: "&6Aktive Loot-Kisten:" + list-tp-hover: "&eKlicke zum Teleportieren!" + tp-usage: "&cBenutzung: /tploot " + tp-world-not-found: "&cWelt nicht gefunden." + tp-success: "&aTeleportiert!" + tp-invalid-coords: "&cUngültige Koordinaten!" + +trade: + only-players: "§cNur Spieler können diesen Befehl ausführen!" + usage: "§cVerwendung: /trade " + player-not-online: "§cDieser Spieler ist nicht online!" + with-yourself-not-allowed: "§cHandel mit dir selbst ist nicht erlaubt!" + already-trading: "§cEiner von euch ist bereits im Handel." + + request-text: "%player% möchte mit dir handeln. Klicke hier, um anzunehmen!" + request-sent: "§aHandelsanfrage an %player% gesendet." + + inventory-title: "Handel mit %player%" + confirm-button: "§aBestätigen" + confirmed: "§aDu hast den Handel bestätigt!" + success: "§aHandel erfolgreich abgeschlossen!" + +report: + only-players: "§cNur Spieler können diesen Befehl ausführen!" + usage: "§cVerwendung: /report [Grund]" + player-not-online: "§cDieser Spieler ist nicht online!" + success: "§aDein Report wurde erfolgreich gesendet." + notify-admin: "§cREPORT§r: %reporter% hat %target% gemeldet. Grund: %reason%" + +showreport: + only-players: "§cNur Spieler können diesen Befehl ausführen!" + usage: "§cVerwendung: /showreport " + no-reports: "§aEs wurden keine Reports für diesen Spieler gefunden." + +clearreport: + only-players: "§cNur Spieler können diesen Befehl ausführen!" + usage: "§cVerwendung: /clearreport " + cleared: "§aReports von %player% wurden gelöscht." + +shop: + only-players: "&cDieser Befehl kann nur von Spielern genutzt werden." + usage-add: "&eVerwendung: /shop add " + unknown-subcommand: "&cUnbekannter Unterbefehl." + number-error: "&cPreis und Lagerbestand müssen gültige Zahlen sein." + item-added: "&aItem &e{item}&a mit Basispreis &e{price}&a und Lagerbestand &e{stock}&a hinzugefügt/aktualisiert." + +warp: + only_players: "&cNur Spieler können diesen Befehl benutzen." + set_success: "&aWarp \"%warp%\" wurde erfolgreich gesetzt." + +challenge: + only_players: "§cDieser Befehl kann nur von Spielern ausgeführt werden." + usage: "&eBenutze: /startchallenge " + start_success: "§aDie Challenge \"%challenge%\" wurde gestartet!" + start_fail: "§cChallenge \"%challenge%\" nicht gefunden." + +inventory: + data-not-found: "§cDie Inventardaten des Spielers konnten nicht gefunden werden! Der Spieler war möglicherweise nie auf diesem Server." + gui-title: "Inventar von " + load-error: "§cFehler beim Laden des Inventars: %error%" + opened: "§aInventar von %player% geöffnet!" + usage: "§cVerwendung: /inv " + no-permission-others: "§cDu hast keine Berechtigung, das Inventar anderer Spieler anzusehen!" + player-only: "§cDieser Befehl ist nur für Spieler!" + +force-survival: + join-message: "§aDu wurdest in den Survivalmodus gesetzt!" \ No newline at end of file diff --git a/src/main/resources/leashes.yml b/src/main/resources/leashes.yml new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..84ee1af --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,508 @@ +name: SurvivalPlus +version: 1.0.5 +main: de.viper.survivalplus.SurvivalPlus +api-version: 1.21 +softdepend: [LuckPerms, PlaceholderAPI] +author: Viper +description: A plugin for enhancing survival gameplay in Minecraft. + +commands: + ir: + description: Benennt das Item in der Hand um. + usage: /ir + permission: survivalplus.itemrename + permission-message: "§cDu hast keine Berechtigung für diesen Befehl." + + gm: + description: Ändert den Spielmodus eines Spielers + usage: / [spieler] + aliases: [gamemode] + permission: survivalplus.gamemode + + sp: + description: Zeigt Plugin-Informationen oder führt weitere Befehle aus + usage: / [reload|help|info|share] [seite] + permission: survivalplus.sp + + sethome: + description: Setzt ein Home mit dem angegebenen Namen + usage: / + permission: survivalplus.homes.set + + delhome: + description: Löscht ein Home mit dem angegebenen Namen + usage: / + permission: survivalplus.homes.delete + + homelist: + description: Öffnet eine GUI mit allen Homes + usage: / + permission: survivalplus.homes.list + + home: + description: Teleportiert zu einem Home + usage: / + permission: survivalplus.homes + + inv: + description: Öffnet das Inventar (eigenes oder das eines anderen Spielers) + usage: / [spieler] + permission: survivalplus.inventory.own + + ec: + description: Öffnet die Endertruhe (eigene oder die eines anderen Spielers) + usage: / [spieler] + permission: survivalplus.enderchest.own + + setworldspawn: + description: Setzt den Weltspawnpunkt auf die Position des Spielers + usage: / + permission: survivalplus.setworldspawn + + setspawn: + description: Setzt den Server-Spawnpunkt auf die Position des Spielers + usage: / + permission: survivalplus.setspawn + + clearchat: + description: Löscht den Chat für alle Spieler + usage: / + permission: survivalplus.clearchat + + clearitems: + description: Löscht alle herumliegenden Items + usage: / + permission: survivalplus.clearitems + + closedoors: + description: Schließt alle Türen im angegebenen Radius + usage: / + permission: survivalplus.closedoors + + sit: + description: Lässt den Spieler sich hinsetzen + usage: / + permission: survivalplus.sit + + back: + description: Teleportiert zum letzten Todespunkt + usage: / + permission: survivalplus.back + + friend: + description: Verwaltet die Freundesliste + usage: / [add|accept|deny|list|del|tp] [Spielername] + permission: survivalplus.friend + + stats: + description: Zeigt deine Statistiken an + usage: /stats + permission: survivalplus.stats + + showarmorstands: + description: Macht alle unsichtbaren Armor Stands sichtbar. + usage: /showarmorstands + permission: survivalplus.showarmorstands + permission-message: "§cDu hast keine Rechte für diesen Befehl." + + cleardebugarmorstands: + description: Entfernt alle Debug-ArmorStands + usage: /cleardebugarmorstands + permission: survivalplus.cleardebugarmorstands + permission-message: "§cDu hast keine Rechte für diesen Befehl." + + trash: + description: Öffnet den Mülleimer + usage: /trash + permission: survivalplus.trash + permission-message: "§cDu hast keine Berechtigung für diesen Befehl." + + workbench: + description: Öffnet eine Werkbank GUI + usage: /workbench + permission: survivalplus.workbench + permission-message: "§cDu hast keine Berechtigung für diesen Befehl." + + anvil: + description: Öffnet eine Amboss GUI + usage: /anvil + permission: survivalplus.anvil + permission-message: "§cDu hast keine Berechtigung für diesen Befehl." + + leashcount: + description: Zeigt die Anzahl der geleinten Tiere an. + usage: /leashcount + permission: survivalplus.leashcount + permission-message: "§cDu hast keine Berechtigung für diesen Befehl." + + splock: + description: Verwaltet das Sperrsystem für Kisten und Türen + usage: /sp lock|unlock|friendadd|friendremove [Spieler] + permission: survivalplus.lock + permission-message: "§cDu hast keine Berechtigung für diesen Befehl." + + tp: + description: Teleportiere dich zu einem Spieler + usage: /tp + + tphere: + description: Teleportiere einen Spieler zu dir + usage: /tphere + + tpa: + description: Sende eine Teleportanfrage an einen Spieler + usage: /tpa + + tpaccept: + description: Akzeptiere eine Teleportanfrage + usage: /tpaccept + + tpdeny: + description: Lehne eine Teleportanfrage ab + usage: /tpdeny + + block: + description: Blockiere einen Spieler + usage: /block + permission: survivalplus.block + + unblock: + description: Entblocke einen Spieler + usage: /unblock + permission: survivalplus.unlock + + blocklist: + description: Zeige eine Liste der blockierten Spieler + usage: /blocklist + permission: survivalplus.blocklist + + kit: + description: Hol dir das Starterkit! + usage: /kit + permission: survivalplus.kit + + nick: + description: Ändert deinen Nicknamen mit Farb- und Hex-Support. + usage: /nick + permission: survivalplus.nick + permission-message: "§cDu hast keine Berechtigung, deinen Nick zu ändern!" + + lootchests: + description: Zeigt eine Liste aller aktiven Loot-Kisten an. Admins können per Klick zu einer Kiste teleportieren. + usage: /lootchests + permission: survivalplus.lootchests + permission-message: "§cDu hast keine Berechtigung für diesen Befehl!" + + tploot: + description: Teleportiere dich zu einer Loot-Kiste (nur Admins) + usage: /tploot + permission: survivalplus.lootchests + permission-message: "§cDu hast keine Berechtigung für diesen Befehl!" + + day: + description: Setzt die Zeit auf Tag + usage: /day + permission: survivalplus.day + + night: + description: Setzt die Zeit auf Nacht + usage: /night + permission: survivalplus.night + + trade: + description: Startet einen Handel mit einem Spieler + usage: /trade + permission: survivalplus.trade + + tradeaccept: + description: Akzeptiert eine Handelsanfrage + usage: /tradeaccept + permission: survivalplus.tradeaccept + + report: + description: Meldet einen Spieler an die Admins + usage: /report [Grund] + permission: survivalplus.report + + showreport: + description: Zeigt alle Reports eines Spielers an + usage: /showreport + permission: survivalplus.report.show + + clearreport: + description: Löscht alle Reports eines Spielers + usage: /clearreport + permission: survivalplus.report.clear + + shop: + description: Verwalten des Server-Shops (z.B. Items hinzufügen) + usage: /shop add + permission: survivalplus.shop + permission-message: "§cDu hast keine Berechtigung für diesen Befehl." + + spawn: + description: Teleportiert dich zum Weltspawnpunkt. + usage: /spawn + permission: survivalplus.spawn + permission-message: "§cDu hast keine Berechtigung für diesen Befehl." + + setwarp: + description: Setzt einen persönlichen Warp mit dem Item in der Hand. + usage: /setwarp + permission: survivalplus.setwarp + permission-message: "§cDu hast keine Berechtigung für diesen Befehl." + + delwarp: + description: Löscht einen persönlichen Warp . + usage: /delwarp + permission: survivalplus.delwarp + permission-message: "§cDu hast keine Berechtigung für diesen Befehl." + + warps: + description: Öffnet die GUI mit allen Spieler-Warps. + usage: /warps + permission: survivalplus.warps + permission-message: "§cDu hast keine Berechtigung für diesen Befehl." + + startchallenge: + description: Startet eine Fun-Challenge + usage: /startchallenge + permission: survivalplus.startchallenge + permission-message: "§cDu hast keine Berechtigung für diesen Befehl." + + heal: + description: Heilt einen Spieler vollständig + usage: /heal [spieler] + permission: survivalplus.heal + permission-message: "§cDu hast keine Berechtigung, Spieler zu heilen!" + + survivalplus.notify: + description: Erhält Benachrichtigungen, wenn ein Spieler einen Command- oder Structure-Block besitzt + default: op + +permissions: + survivalplus.*: + description: Gibt Zugriff auf alle SurvivalPlus-Befehle + default: op + children: + survivalplus.gamemode: true + survivalplus.gamemode.others: true + survivalplus.sp: true + survivalplus.share: true + survivalplus.homes.set: true + survivalplus.homes.delete: true + survivalplus.homes.list: true + survivalplus.homes: true + survivalplus.homes.unlimited: true + survivalplus.inventory.own: true + survivalplus.inventory.others: true + survivalplus.enderchest.own: true + survivalplus.enderchest.others: true + survivalplus.setworldspawn: true + survivalplus.setspawn: true + survivalplus.clearchat: true + survivalplus.clearitems: true + survivalplus.closedoors: true + survivalplus.sit: true + survivalplus.back: true + survivalplus.graves: true + survivalplus.friend: true + survivalplus.itemrename: true + survivalplus.stats: true + survivalplus.showarmorstands: true + survivalplus.cleardebugarmorstands: true + survivalplus.trash: true + survivalplus.workbench: true + survivalplus.anvil: true + survivalplus.leashcount: true + survivalplus.nick: true + survivalplus.lootchests: true + survivalplus.startchallenge: true + survivalplus.heal: true + survivalplus.heal.others: true + survivalplus.notify: true + + survivalplus.lootchests: + description: Erlaubt das Verwalten und Teleportieren zu Loot-Kisten + default: op + + survivalplus.gamemode: + description: Erlaubt das Ändern des eigenen Spielmodus + default: op + + survivalplus.gamemode.others: + description: Erlaubt das Ändern des Spielmodus anderer Spieler + default: op + + survivalplus.sp: + description: Erlaubt den Zugriff auf den /sp-Befehl + default: op + + survivalplus.share: + description: Erlaubt das Teilen der eigenen Koordinaten mit /sp share + default: true + + survivalplus.homes.set: + description: Erlaubt das Setzen von Homes + default: true + + survivalplus.homes.delete: + description: Erlaubt das Löschen von Homes + default: true + + survivalplus.homes.list: + description: Erlaubt das Öffnen der Home-Liste GUI + default: true + + survivalplus.homes: + description: Erlaubt das Teleportieren zu Homes + default: true + + survivalplus.homes.unlimited: + description: Erlaubt unbegrenzte Homes + default: op + + survivalplus.inventory.own: + description: Erlaubt das Ansehen des eigenen Inventars + default: true + + survivalplus.inventory.others: + description: Erlaubt das Ansehen des Inventars anderer Spieler + default: op + + survivalplus.enderchest.own: + description: Erlaubt das Ansehen der eigenen Endertruhe + default: true + + survivalplus.enderchest.others: + description: Erlaubt das Ansehen der Endertruhen anderer Spieler + default: op + + survivalplus.setworldspawn: + description: Erlaubt das Setzen des Weltspawnpunkts + default: op + + survivalplus.setspawn: + description: Erlaubt das Setzen des Server-Spawnpunkts + default: op + + survivalplus.clearchat: + description: Erlaubt das Löschen des Chats + default: op + + survivalplus.clearitems: + description: Erlaubt das manuelle Löschen der herumliegenden Items + default: op + + survivalplus.closedoors: + description: Erlaubt das Schließen von Türen mit /closedoors + default: op + + survivalplus.sit: + description: Erlaubt das Sitzen auf Treppen oder mit /sit + default: true + + survivalplus.graves: + description: Erlaubt das Erstellen von Gräbern bei Tod + default: true + + survivalplus.back: + description: Erlaubt das Teleportieren zum letzten Todespunkt + default: true + + survivalplus.friend: + description: Erlaubt die Verwaltung der Freundesliste + default: true + + survivalplus.itemrename: + description: Erlaubt das Umbenennen von Items mit /ir + default: true + + survivalplus.stats: + description: Erlaubt den Zugriff auf den /stats-Befehl + default: true + + survivalplus.showarmorstands: + description: Erlaubt das Sichtbarmachen von Armor Stands mit /showarmorstands + default: op + + survivalplus.cleardebugarmorstands: + description: Erlaubt das Entfernen von Debug-ArmorStands + default: op + + survivalplus.trash: + description: Erlaubt die Nutzung von /trash + default: true + + survivalplus.workbench: + description: Erlaubt die Nutzung von /workbench + default: true + + survivalplus.anvil: + description: Erlaubt die Nutzung von /anvil + default: true + + survivalplus.leashcount: + description: Erlaubt die Nutzung von /leashcount + default: true + + survivalplus.chunkanimals: + description: Erlaubt das Anzeigen der Anzahl der Tiere im aktuellen Chunk + default: op + + survivalplus.lock: + description: Erlaubt das Verwenden von /lock-Befehlen + default: true + + survivalplus.tp: + description: Erlaube das Teleportieren zu anderen Spielern + default: op + + survivalplus.tphere: + description: Erlaube das Teleportieren anderer Spieler zu dir + default: op + + survivalplus.tpa: + description: Erlaube das Senden von Teleportanfragen + default: true + + survivalplus.tpaccept: + description: Erlaube das Annehmen von Teleportanfragen + default: true + + survivalplus.tpdeny: + description: Erlaube das Ablehnen von Teleportanfragen + default: true + + survivalplus.block: + description: Erlaubt das Blockieren anderer Spieler im Chat + default: true + + survivalplus.info: + description: Erlaubt den Zugriff auf /sp info + default: true + + survivalplus.nick: + description: Erlaubt es, den eigenen Nicknamen zu ändern (mit Farben & Hex) + default: op + + survivalplus.shop: + description: Erlaubt die Nutzung des Shop-Befehls + default: op + + survivalplus.spawn: + description: Erlaubt die Nutzung des /spawn Befehls + default: true + + survivalplus.setwarp: + description: Erlaubt das Setzen von persönlichen Warps + default: true + + survivalplus.warps: + description: Erlaubt das Öffnen der Warps-GUI + default: true + + delwarp: + description: Erlaubt das Löschen von persönlichen Warps + default: true + \ No newline at end of file diff --git a/src/main/resources/shop.yml b/src/main/resources/shop.yml new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/stats.yml b/src/main/resources/stats.yml new file mode 100644 index 0000000..175b22c --- /dev/null +++ b/src/main/resources/stats.yml @@ -0,0 +1,2 @@ +# stats.yml + diff --git a/src/main/resources/tablist.yml b/src/main/resources/tablist.yml new file mode 100644 index 0000000..c4cad89 --- /dev/null +++ b/src/main/resources/tablist.yml @@ -0,0 +1,42 @@ +# =============================== +# Tablist-Konfiguration für SurvivalPlus +# =============================== + +# Tablist aktivieren/deaktivieren +enabled: true + +# Servername für den Header +server-name: "&l&6✦✦ SURVIVALPLUS ✦✦" + +# Webseite des Servers +website: "www.example.com" + +# TeamSpeak-Adresse +teamspeak-address: "ts.example.com" +show-teamspeak: true + +# Discord-Adresse +discord-address: "discord.gg/example" +show-discord: true + +# Berechtigung für Staff-Zählung +staff-permission: "survivalplus.staff" + +# Trennlinie für den Footer +separator-line: "&8&l&m================================" + +# Kopf-Animationen +# Platzhalter: {server} = Servername, {player} = Spielername, {online} = Online-Spieler, {staff} = Online-Staff +header-animations: + - "&6&l{server}\n&7Willkommen, &a{player}\n&7Online Player: &e{online}\n&6Online Staff: &e{staff}\n" + - "&e&l{server}\n&7Willkommen, &a{player}\n&7Online Player: &e{online}\n&6Online Staff: &e{staff}\n" + - "&b&l{server}\n&7Willkommen, &a{player}\n&7Online Player: &e{online}\n&6Online Staff: &e{staff}\n" + +# Fuß-Animationen +footer-animations: + - "&7SurvivalPlus &8| &eDein Abenteuer!" + - "&7SurvivalPlus &8| &bEntdecke die Welt!" + - "&7SurvivalPlus &8| &aBaue, kämpfe, überlebe!" + +# Intervall der Animation in Ticks (20 = 1 Sekunde) +interval-ticks: 40 diff --git a/src/main/resources/warps.yml b/src/main/resources/warps.yml new file mode 100644 index 0000000..e69de29