From b36bdaa3ece70feaecb500b5436dfb6b3a0731c8 Mon Sep 17 00:00:00 2001 From: M_Viper Date: Sun, 1 Feb 2026 22:36:30 +0100 Subject: [PATCH] Update from Git Manager GUI --- .../java/de/velocityfall/VelocityFall.java | 113 ++++-- .../commands/VelocityCommand.java | 29 +- .../velocityfall/listeners/FloorListener.java | 28 +- .../listeners/FloorLoseListener.java | 47 ++- .../velocityfall/listeners/GameListener.java | 7 +- .../velocityfall/listeners/SetupListener.java | 2 +- .../velocityfall/listeners/SignListener.java | 9 +- .../de/velocityfall/manager/ArenaManager.java | 32 +- .../velocityfall/manager/ConfigManager.java | 2 +- .../java/de/velocityfall/manager/Configs.java | 2 +- .../de/velocityfall/manager/DisableReset.java | 121 +++++- .../de/velocityfall/manager/GameManager.java | 357 ++++++++++-------- .../de/velocityfall/manager/JoinManager.java | 9 +- .../de/velocityfall/manager/Messages.java | 2 +- .../velocityfall/manager/MySQLConnector.java | 83 +++- .../de/velocityfall/manager/SignManager.java | 27 +- .../java/de/velocityfall/manager/Stats.java | 2 +- .../velocityfall/utils/ScoreboardHelper.java | 168 +++++++-- .../de/velocityfall/utils/StatsHandler.java | 111 +++++- .../de/velocityfall/utils/UpdateChecker.java | 33 ++ src/main/resources/config.yml | 148 ++++++-- src/main/resources/plugin.yml | 2 +- 22 files changed, 988 insertions(+), 346 deletions(-) create mode 100644 src/main/java/de/velocityfall/utils/UpdateChecker.java diff --git a/src/main/java/de/velocityfall/VelocityFall.java b/src/main/java/de/velocityfall/VelocityFall.java index bb9e1b9..e2f1452 100644 --- a/src/main/java/de/velocityfall/VelocityFall.java +++ b/src/main/java/de/velocityfall/VelocityFall.java @@ -4,37 +4,47 @@ import de.velocityfall.commands.VelocityCommand; import de.velocityfall.listeners.*; import de.velocityfall.manager.*; import de.velocityfall.utils.*; +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.hover.content.Text; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.block.Block; import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.plugin.java.JavaPlugin; import java.util.ArrayList; -import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; -public class VelocityFall extends JavaPlugin { +public class VelocityFall extends JavaPlugin implements Listener { - private static VelocityFall instance; - public static VelocityFall getInstance() { return instance; } + private static volatile VelocityFall instance; + + public static VelocityFall getInstance() { + return instance; + } public static String prefix; public static String noperms; - // Spiel-Daten + // ===== SPIEL-DATEN - THREAD-SAFE COLLECTIONS ===== public ArrayList ingamePlayers = new ArrayList<>(); - public HashMap arenaPlayersCount = new HashMap<>(); - public HashMap playerArena = new HashMap<>(); + public ConcurrentHashMap arenaPlayersCount = new ConcurrentHashMap<>(); + public ConcurrentHashMap playerArena = new ConcurrentHashMap<>(); public ArrayList ingameArenas = new ArrayList<>(); - public HashMap cdstarted = new HashMap<>(); - public HashMap arenaCountdown = new HashMap<>(); - public HashMap arenaBlocks = new HashMap<>(); - public HashMap gameStarted = new HashMap<>(); + public ConcurrentHashMap cdstarted = new ConcurrentHashMap<>(); + public ConcurrentHashMap arenaCountdown = new ConcurrentHashMap<>(); + public ConcurrentHashMap arenaBlocks = new ConcurrentHashMap<>(); + public ConcurrentHashMap gameStarted = new ConcurrentHashMap<>(); public ArrayList cooldown = new ArrayList<>(); - public HashMap spectators = new HashMap<>(); - public HashMap checkint = new HashMap<>(); + public ConcurrentHashMap spectators = new ConcurrentHashMap<>(); + public ConcurrentHashMap checkint = new ConcurrentHashMap<>(); - // Manager + // ===== MANAGER ===== private ArenaManager arenaManager; private JoinManager joinManager; private SignManager signManager; @@ -44,9 +54,14 @@ public class VelocityFall extends JavaPlugin { private Stats stats; private DisableReset disablereset; - // Listener-Referenzen (für GameManager-Zugriff) + // ===== LISTENER-REFERENZEN ===== private FloorLoseListener floorLoseListener; + // ===== UPDATE CHECKER DATEN ===== + private String latestVersionFound; + private final int RESOURCE_ID = 132255; + private final String releaseUrl = "https://www.spigotmc.org/resources/" + RESOURCE_ID; + @Override public void onEnable() { instance = this; @@ -55,8 +70,11 @@ public class VelocityFall extends JavaPlugin { getConfig().options().copyDefaults(true); saveConfig(); - prefix = ChatColor.translateAlternateColorCodes('&', getConfig().getString("Prefix", "&6Velocity&fFall &8» ")); - noperms = ChatColor.translateAlternateColorCodes('&', getConfig().getString("NoPermission", "&cKeine Rechte!")); + // Encoding-Fixed: Korrekte Umlaute + prefix = ChatColor.translateAlternateColorCodes('&', + getConfig().getString("Prefix", "&6Velocity&fFall &8» ")); + noperms = ChatColor.translateAlternateColorCodes('&', + getConfig().getString("NoPermission", "&cKeine Rechte!")); ConfigManager.initializeFiles(this); @@ -79,9 +97,9 @@ public class VelocityFall extends JavaPlugin { getCommand("velocityfall").setExecutor(new VelocityCommand(this)); // Listener registrieren + Bukkit.getPluginManager().registerEvents(this, this); Bukkit.getPluginManager().registerEvents(new FloorListener(this), this); - // FloorLoseListener mit Referenz speichern (für GameManager) this.floorLoseListener = new FloorLoseListener(this); Bukkit.getPluginManager().registerEvents(floorLoseListener, this); @@ -89,9 +107,44 @@ public class VelocityFall extends JavaPlugin { Bukkit.getPluginManager().registerEvents(new SetupListener(this), this); Bukkit.getPluginManager().registerEvents(new SignListener(this), this); + // Update Check starten + checkForUpdates(); + getLogger().info("VelocityFall aktiviert – Version " + getDescription().getVersion()); } + private void checkForUpdates() { + new UpdateChecker(this, RESOURCE_ID).getLatestVersion(version -> { + this.latestVersionFound = version; + if (!this.getDescription().getVersion().equalsIgnoreCase(version)) { + getLogger().warning("===================================================="); + getLogger().warning("[VelocityFall] Eine neue Version ist verfügbar: v" + version); + getLogger().warning("[VelocityFall] Download: " + releaseUrl); + getLogger().warning("===================================================="); + } + }); + } + + @EventHandler + public void onJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + if (player.hasPermission("velocityfall.admin") || player.isOp()) { + if (latestVersionFound != null && !this.getDescription().getVersion().equalsIgnoreCase(latestVersionFound)) { + player.sendMessage(" "); + player.sendMessage(prefix + "§eEine neue Version (§b" + latestVersionFound + "§e) ist verfügbar!"); + + TextComponent message = new TextComponent(prefix + "§eDownload: "); + TextComponent link = new TextComponent("§6§l[KLICK HIER]"); + link.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, releaseUrl)); + link.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("§7Öffnet die Spigot-Seite"))); + + message.addExtra(link); + player.spigot().sendMessage(message); + player.sendMessage(" "); + } + } + } + private void setupMySQL() { var cfg = stats.getStatsConfig(); String host = cfg.getString("MySQL.HOST", "localhost"); @@ -114,8 +167,12 @@ public class VelocityFall extends JavaPlugin { @Override public void onDisable() { - if (disablereset != null) disablereset.disableReset(); - if (mysqlConnector != null) mysqlConnector.close(); + if (disablereset != null) { + disablereset.disableReset(); + } + if (mysqlConnector != null) { + mysqlConnector.close(); + } getLogger().info("VelocityFall deaktiviert"); } @@ -141,7 +198,7 @@ public class VelocityFall extends JavaPlugin { return list; } - // Manager-Getter + // ===== MANAGER-GETTER ===== public ArenaManager getArenaManager() { return arenaManager; } public JoinManager getJoinManager() { return joinManager; } public SignManager getSignManager() { return signManager; } @@ -149,13 +206,11 @@ public class VelocityFall extends JavaPlugin { public Configs getConfigs() { return configs; } public Messages getMessages() { return messages; } public Stats getStats() { return stats; } - - // Listener-Getter (für GameManager) public FloorLoseListener getFloorLoseListener() { return floorLoseListener; } - // Map-Getter - public HashMap getPlayerArenaMap() { return playerArena; } - public HashMap getArenaPlayersCountMap() { return arenaPlayersCount; } - public HashMap getGameStartedMap() { return gameStarted; } - public HashMap getArenaBlocksMap() { return arenaBlocks; } -} \ No newline at end of file + // ===== MAP-GETTER ===== + public ConcurrentHashMap getPlayerArenaMap() { return playerArena; } + public ConcurrentHashMap getArenaPlayersCountMap() { return arenaPlayersCount; } + public ConcurrentHashMap getGameStartedMap() { return gameStarted; } + public ConcurrentHashMap getArenaBlocksMap() { return arenaBlocks; } +} diff --git a/src/main/java/de/velocityfall/commands/VelocityCommand.java b/src/main/java/de/velocityfall/commands/VelocityCommand.java index 9689200..b9f4db0 100644 --- a/src/main/java/de/velocityfall/commands/VelocityCommand.java +++ b/src/main/java/de/velocityfall/commands/VelocityCommand.java @@ -2,6 +2,7 @@ package de.velocityfall.commands; import de.velocityfall.VelocityFall; import de.velocityfall.manager.GameManager; +import de.velocityfall.utils.ScoreboardHelper; import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.command.Command; @@ -35,7 +36,7 @@ public class VelocityCommand implements CommandExecutor { String action = args[0].toLowerCase(); - // --- Spieler-Befehle --- + // === SPIELER-BEFEHLE === if (action.equals("join") && args.length == 2) { plugin.getJoinManager().join(p, args[1]); @@ -49,22 +50,20 @@ public class VelocityCommand implements CommandExecutor { return true; } - // Entfernen aus Registern plugin.getPlayerArenaMap().remove(p); plugin.ingamePlayers.remove(p); - // Zähler dekrementieren plugin.getArenaPlayersCountMap().compute(arena, (k, v) -> Math.max(0, (v == null ? 0 : v) - 1)); - // Siegprüfung (korrekt über GameManager) GameManager.checkForWinner(arena); - // Reset Spieler p.teleport(p.getWorld().getSpawnLocation()); p.setGameMode(GameMode.SURVIVAL); p.setAllowFlight(false); p.setFlying(false); p.getInventory().clear(); + + ScoreboardHelper.clearBoard(p); p.sendMessage(VelocityFall.prefix + "§eArena verlassen."); @@ -72,7 +71,7 @@ public class VelocityCommand implements CommandExecutor { return true; } - // --- Admin-Befehle --- + // === ADMIN-BEFEHLE === if (!p.hasPermission("velocityfall.admin")) { p.sendMessage(VelocityFall.prefix + "§cKeine Rechte."); @@ -95,7 +94,7 @@ public class VelocityCommand implements CommandExecutor { wand.setItemMeta(meta); } p.getInventory().addItem(wand); - p.sendMessage(VelocityFall.prefix + "§aSetup-Wand erhalten! Nutze Links-/Rechtsklick auf Blöcke."); + p.sendMessage(VelocityFall.prefix + "§aSetup-Wand erhalten!"); break; case "savearea": @@ -184,13 +183,13 @@ public class VelocityCommand implements CommandExecutor { if (p.hasPermission("velocityfall.admin")) { p.sendMessage("§6Admin-Befehle:"); p.sendMessage("§6/vfall wand §7- Setup-Wand holen"); - p.sendMessage("§6/vfall create §7- Arena erstellen"); - p.sendMessage("§6/vfall savearea §7- Bereich speichern"); - p.sendMessage("§6/vfall setlobby §7- Lobby setzen"); - p.sendMessage("§6/vfall setspawn §7- Spawn setzen"); - p.sendMessage("§6/vfall setlose §7- Verlust-Höhe setzen"); - p.sendMessage("§6/vfall setmax §7- Max-Spieler setzen"); - p.sendMessage("§6/vfall start §7- Spiel sofort starten"); + p.sendMessage("§6/vfall create §7- Arena erstellen"); + p.sendMessage("§6/vfall savearea §7- Bereich speichern"); + p.sendMessage("§6/vfall setlobby §7- Lobby setzen"); + p.sendMessage("§6/vfall setspawn §7- Spawn setzen"); + p.sendMessage("§6/vfall setlose §7- Verlust-Höhe setzen"); + p.sendMessage("§6/vfall setmax §7- Max-Spieler setzen"); + p.sendMessage("§6/vfall start §7- Spiel sofort starten"); } } -} \ No newline at end of file +} diff --git a/src/main/java/de/velocityfall/listeners/FloorListener.java b/src/main/java/de/velocityfall/listeners/FloorListener.java index 6dc6ecc..c5f6da6 100644 --- a/src/main/java/de/velocityfall/listeners/FloorListener.java +++ b/src/main/java/de/velocityfall/listeners/FloorListener.java @@ -9,12 +9,13 @@ import org.bukkit.entity.Player; import org.bukkit.event.Listener; import org.bukkit.scheduler.BukkitRunnable; -import java.util.HashSet; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; public class FloorListener implements Listener { private final VelocityFall plugin; - private final Set blocksInProcess = new HashSet<>(); + // THREAD-SAFE: ConcurrentHashMap.newKeySet() statt HashSet + private final Set blocksInProcess = ConcurrentHashMap.newKeySet(); public FloorListener(VelocityFall plugin) { this.plugin = plugin; @@ -29,21 +30,29 @@ public class FloorListener implements Listener { if (!plugin.ingamePlayers.contains(p)) continue; String arena = plugin.getPlayerArenaMap().get(p); - if (arena == null || !plugin.getGameStartedMap().getOrDefault(arena, false)) continue; + if (arena == null) continue; + + // Nur wenn Spiel läuft UND Spieler nicht Spectator + if (!plugin.getGameStartedMap().getOrDefault(arena, false)) continue; + if (p.getGameMode() == GameMode.SPECTATOR) continue; + // Nur Adventure/Survival Spieler if (p.getGameMode() != GameMode.ADVENTURE && p.getGameMode() != GameMode.SURVIVAL) continue; Block b = p.getLocation().subtract(0, 0.1, 0).getBlock(); - if (b.getType() == Material.AIR || blocksInProcess.contains(b)) continue; + if (b.getType() == Material.AIR) continue; + + // THREAD-SAFE: putIfAbsent() ist atomar + if (!blocksInProcess.add(b)) continue; if (plugin.getArenaManager().isInArenaRegion(arena, b.getLocation())) { - blocksInProcess.add(b); - Bukkit.getScheduler().runTaskLater(plugin, () -> { updateBlock(b); blocksInProcess.remove(b); }, 10L); // 0.5 Sekunden Delay + } else { + blocksInProcess.remove(b); } } } @@ -62,9 +71,8 @@ public class FloorListener implements Listener { }; if (next != null) { - if (!plugin.getArenaBlocksMap().containsKey(b)) { - plugin.getArenaBlocksMap().put(b, current.name()); - } + // Originalblock merken (falls noch nicht vorhanden) + plugin.getArenaBlocksMap().putIfAbsent(b, current.name()); b.setType(next); @@ -75,4 +83,4 @@ public class FloorListener implements Listener { } } } -} \ No newline at end of file +} diff --git a/src/main/java/de/velocityfall/listeners/FloorLoseListener.java b/src/main/java/de/velocityfall/listeners/FloorLoseListener.java index ff50935..4814f21 100644 --- a/src/main/java/de/velocityfall/listeners/FloorLoseListener.java +++ b/src/main/java/de/velocityfall/listeners/FloorLoseListener.java @@ -3,27 +3,24 @@ package de.velocityfall.listeners; import de.velocityfall.VelocityFall; import de.velocityfall.manager.GameManager; import de.velocityfall.utils.ScoreboardHelper; -import de.velocityfall.utils.StatsHandler; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.GameMode; 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.player.PlayerMoveEvent; -import org.bukkit.inventory.ItemStack; -import java.io.IOException; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; public class FloorLoseListener implements Listener { private final VelocityFall plugin; - // Tracking: welche Spieler bereits als "verloren" markiert wurden - private final Set alreadyLost = new HashSet<>(); + // THREAD-SAFE: ConcurrentHashMap.newKeySet() statt HashSet + private final Set alreadyLost = ConcurrentHashMap.newKeySet(); public FloorLoseListener(VelocityFall plugin) { this.plugin = plugin; @@ -59,21 +56,20 @@ public class FloorLoseListener implements Listener { } /** - * Markiert Spieler als "verloren" - reduziert nur den Counter, entfernt ihn aber - * NICHT aus playerArenaMap oder ingamePlayers (das macht später der GameManager) + * Markiert Spieler als "verloren" - reduziert nur den Counter + * THREAD-SAFE durch ConcurrentHashMap.newKeySet() */ private void markPlayerAsLost(Player p, String arena) { - // Verhindere Mehrfach-Verarbeitung - if (alreadyLost.contains(p)) return; - alreadyLost.add(p); + // Verhindere Mehrfach-Verarbeitung (atomar durch ConcurrentHashSet) + if (!alreadyLost.add(p)) return; var arenaPlayersCountMap = plugin.getArenaPlayersCountMap(); // Lose-Nachricht - p.sendMessage(VelocityFall.prefix + ChatColor.translateAlternateColorCodes('&', - plugin.getMessages().getMessagesConfig().getString("LoseMessage", "&cDu bist rausgefallen!"))); + String loseMsg = plugin.getMessages().getMessagesConfig().getString("LoseMessage", "&cDu bist rausgefallen!"); + p.sendMessage(VelocityFall.prefix + ChatColor.translateAlternateColorCodes('&', loseMsg)); - // Setze Spieler sofort in Spectator, damit er nicht weiter interagieren kann + // Setze Spieler sofort in Spectator p.setGameMode(GameMode.SPECTATOR); // Counter reduzieren (wichtig für checkForWinner) @@ -81,34 +77,37 @@ public class FloorLoseListener implements Listener { // Scoreboard leeren try { - p.setScoreboard(Bukkit.getScoreboardManager().getNewScoreboard()); - } catch (Exception ignored) {} + ScoreboardHelper.clearBoard(p); + } catch (Exception e) { + plugin.getLogger().warning("Scoreboard Clear fehlgeschlagen: " + e.getMessage()); + } // Scoreboard für andere updaten try { ScoreboardHelper.updateAllInArena(arena, true); - } catch (Exception ignored) {} + } catch (Exception e) { + plugin.getLogger().warning("Scoreboard Update fehlgeschlagen: " + e.getMessage()); + } // Sign updaten try { plugin.getSignManager().updateSign(arena); - } catch (Exception ignored) {} - - // WICHTIG: Spieler bleibt in ingamePlayers und playerArenaMap! - // Er wird erst in performSafeResetAndLobbyTeleport entfernt + } catch (Exception e) { + plugin.getLogger().warning("Sign Update fehlgeschlagen: " + e.getMessage()); + } } /** - * Cleanup-Methode für den GameManager - wird nach Arena-Reset aufgerufen + * Cleanup-Methode für den GameManager */ public void clearLostPlayers() { alreadyLost.clear(); } /** - * Gibt die Menge der verlorenen Spieler zurück (für GameManager) + * Gibt eine Kopie der verlorenen Spieler zurück */ public Set getAlreadyLost() { return new HashSet<>(alreadyLost); } -} \ No newline at end of file +} diff --git a/src/main/java/de/velocityfall/listeners/GameListener.java b/src/main/java/de/velocityfall/listeners/GameListener.java index 846852f..c028b49 100644 --- a/src/main/java/de/velocityfall/listeners/GameListener.java +++ b/src/main/java/de/velocityfall/listeners/GameListener.java @@ -1,6 +1,7 @@ package de.velocityfall.listeners; import de.velocityfall.VelocityFall; +import de.velocityfall.utils.ScoreboardHelper; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -25,9 +26,13 @@ public class GameListener implements Listener { if (plugin.ingamePlayers.contains(p)) { plugin.ingamePlayers.remove(p); + plugin.getPlayerArenaMap().remove(p); + var countMap = plugin.getArenaPlayersCountMap(); countMap.compute(arena, (k, v) -> Math.max(0, (v == null ? 0 : v) - 1)); + ScoreboardHelper.clearBoard(p); + Bukkit.getScheduler().runTaskLater(plugin, () -> { de.velocityfall.manager.GameManager.checkForWinner(arena); }, 1L); @@ -48,4 +53,4 @@ public class GameListener implements Listener { p.setFoodLevel(20); } } -} \ No newline at end of file +} diff --git a/src/main/java/de/velocityfall/listeners/SetupListener.java b/src/main/java/de/velocityfall/listeners/SetupListener.java index 5c29d8e..31a7966 100644 --- a/src/main/java/de/velocityfall/listeners/SetupListener.java +++ b/src/main/java/de/velocityfall/listeners/SetupListener.java @@ -39,4 +39,4 @@ public class SetupListener implements Listener { e.getPlayer().sendMessage(VelocityFall.prefix + "§ePos2 gesetzt."); } } -} \ No newline at end of file +} diff --git a/src/main/java/de/velocityfall/listeners/SignListener.java b/src/main/java/de/velocityfall/listeners/SignListener.java index 652d32f..475c70e 100644 --- a/src/main/java/de/velocityfall/listeners/SignListener.java +++ b/src/main/java/de/velocityfall/listeners/SignListener.java @@ -1,6 +1,7 @@ package de.velocityfall.listeners; import de.velocityfall.VelocityFall; +import org.bukkit.ChatColor; import org.bukkit.block.Sign; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -19,7 +20,7 @@ public class SignListener implements Listener { @EventHandler public void onSignChange(SignChangeEvent e) { String line0 = e.getLine(0); - if (line0 == null || !(line0.equalsIgnoreCase("[VelocityFall]") || line0.equalsIgnoreCase("[LKF]"))) return; + if (line0 == null || !(line0.equalsIgnoreCase("[VelocityFall]") || line0.equalsIgnoreCase("[VF]"))) return; String arenaName = e.getLine(1); if (arenaName == null || arenaName.trim().isEmpty()) return; @@ -55,10 +56,10 @@ public class SignListener implements Listener { e.setCancelled(true); - String arenaName = sign.getLine(1).replaceAll("§.", ""); - if (arenaName.trim().isEmpty()) return; + String arenaName = ChatColor.stripColor(sign.getLine(1)); + if (arenaName == null || arenaName.trim().isEmpty()) return; plugin.getJoinManager().join(e.getPlayer(), arenaName); plugin.getSignManager().updateSign(arenaName); } -} \ No newline at end of file +} diff --git a/src/main/java/de/velocityfall/manager/ArenaManager.java b/src/main/java/de/velocityfall/manager/ArenaManager.java index e9babe1..3bedb46 100644 --- a/src/main/java/de/velocityfall/manager/ArenaManager.java +++ b/src/main/java/de/velocityfall/manager/ArenaManager.java @@ -66,7 +66,7 @@ public class ArenaManager { arenaConfig.set(path + "Pos2", pos2); arenaConfig.set(path + "World", pos1.getWorld().getName()); - // Automatisch MinY berechnen und speichern (wichtig für mehrstöckige Arenen) + // Automatisch MinY berechnen und speichern double minY = Math.min(pos1.getY(), pos2.getY()); arenaConfig.set(path + "MinY", minY); @@ -82,6 +82,10 @@ public class ArenaManager { saveArenaRegion(player, arenaName); } + /** + * Überprüft, ob eine Location innerhalb der Arena ist + * VERBESSERT: Konsistente Buffer-Werte (0.5 statt 0.1 / 2.0) + */ public boolean isInArenaRegion(String arenaName, Location loc) { if (loc == null || loc.getWorld() == null) return false; @@ -92,23 +96,35 @@ public class ArenaManager { if (p1 == null || p2 == null) return false; if (!loc.getWorld().equals(p1.getWorld())) return false; - double minX = Math.min(p1.getX(), p2.getX()); - double maxX = Math.max(p1.getX(), p2.getX()); - double minY = Math.min(p1.getY(), p2.getY()); - double maxY = Math.max(p1.getY(), p2.getY()); - double minZ = Math.min(p1.getZ(), p2.getZ()); - double maxZ = Math.max(p1.getZ(), p2.getZ()); + // KONSISTENTER BUFFER: 0.5 Blöcke Toleranz (horizontal UND vertikal) + // Konfigurierbar machen über Config falls gewünscht + double buffer = plugin.getConfig().getDouble("ArenaRegionBuffer", 0.5); + + double minX = Math.min(p1.getX(), p2.getX()) - buffer; + double maxX = Math.max(p1.getX(), p2.getX()) + buffer; + + double minY = Math.min(p1.getY(), p2.getY()) - buffer; + double maxY = Math.max(p1.getY(), p2.getY()) + buffer; + + double minZ = Math.min(p1.getZ(), p2.getZ()) - buffer; + double maxZ = Math.max(p1.getZ(), p2.getZ()) + buffer; return loc.getX() >= minX && loc.getX() <= maxX && loc.getY() >= minY && loc.getY() <= maxY && loc.getZ() >= minZ && loc.getZ() <= maxZ; } + /** + * Gibt die minimale Y-Koordinate der Arena zurück + */ public double getMinY(String arenaName) { return arenaConfig.getDouble("Arenas." + arenaName + ".MinY", -100.0); } + /** + * Gibt die Arena-Config zurück + */ public FileConfiguration getArenaConfig() { return arenaConfig; } -} \ No newline at end of file +} diff --git a/src/main/java/de/velocityfall/manager/ConfigManager.java b/src/main/java/de/velocityfall/manager/ConfigManager.java index e253ef8..1ade6a8 100644 --- a/src/main/java/de/velocityfall/manager/ConfigManager.java +++ b/src/main/java/de/velocityfall/manager/ConfigManager.java @@ -34,4 +34,4 @@ public class ConfigManager { plugin.getLogger().severe("Konnte " + filename + " nicht erstellen: " + e.getMessage()); } } -} \ No newline at end of file +} diff --git a/src/main/java/de/velocityfall/manager/Configs.java b/src/main/java/de/velocityfall/manager/Configs.java index bbf5a7d..4214362 100644 --- a/src/main/java/de/velocityfall/manager/Configs.java +++ b/src/main/java/de/velocityfall/manager/Configs.java @@ -61,4 +61,4 @@ public class Configs { public File getSaveFile() { return saveFile; } -} \ No newline at end of file +} diff --git a/src/main/java/de/velocityfall/manager/DisableReset.java b/src/main/java/de/velocityfall/manager/DisableReset.java index 17b4673..9dc3c3c 100644 --- a/src/main/java/de/velocityfall/manager/DisableReset.java +++ b/src/main/java/de/velocityfall/manager/DisableReset.java @@ -1,6 +1,14 @@ package de.velocityfall.manager; import de.velocityfall.VelocityFall; +import de.velocityfall.utils.ScoreboardHelper; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; + +import java.util.Map; public class DisableReset { @@ -10,7 +18,116 @@ public class DisableReset { this.plugin = plugin; } + /** + * Wird beim Plugin-Shutdown aufgerufen + * Resettet alle Arenas und teleportiert Spieler zurück + */ public void disableReset() { - plugin.getLogger().info("DisableReset ausgeführt – Plugin heruntergefahren."); + plugin.getLogger().info("DisableReset gestartet – Räume Arenas auf..."); + + try { + resetAllArenas(); + teleportAllPlayersToSpawn(); + clearAllScoreboards(); + } catch (Exception e) { + plugin.getLogger().severe("Fehler beim DisableReset: " + e.getMessage()); + e.printStackTrace(); + } + + plugin.getLogger().info("DisableReset abgeschlossen – Plugin heruntergefahren."); } -} \ No newline at end of file + + /** + * Setzt alle Arena-Blöcke zurück + */ + private void resetAllArenas() { + if (plugin.getArenaBlocksMap().isEmpty()) { + plugin.getLogger().info("Keine Blöcke zum Zurücksetzen gefunden."); + return; + } + + int resetCount = 0; + for (Map.Entry entry : plugin.getArenaBlocksMap().entrySet()) { + try { + Block block = entry.getKey(); + String materialName = entry.getValue(); + + Material material = Material.valueOf(materialName); + block.setType(material); + resetCount++; + } catch (Exception e) { + plugin.getLogger().warning("Fehler beim Block-Reset: " + e.getMessage()); + } + } + + plugin.getArenaBlocksMap().clear(); + plugin.getLogger().info(resetCount + " Blöcke zurückgesetzt."); + } + + /** + * Teleportiert alle Spieler aus Arenas zurück zum Spawn + */ + private void teleportAllPlayersToSpawn() { + if (plugin.getPlayerArenaMap().isEmpty()) { + plugin.getLogger().info("Keine Spieler in Arenas gefunden."); + return; + } + + int teleportCount = 0; + for (Map.Entry entry : plugin.getPlayerArenaMap().entrySet()) { + try { + Player player = entry.getKey(); + String arenaName = entry.getValue(); + + if (player == null || !player.isOnline()) continue; + + // Versuche Lobby-Location zu finden + Location lobby = plugin.getArenaManager() + .getArenaConfig().getLocation("Arenas." + arenaName + ".Lobby"); + + if (lobby != null) { + player.teleport(lobby); + } else if (player.getWorld() != null) { + // Fallback: Welt-Spawn + player.teleport(player.getWorld().getSpawnLocation()); + } + + // Spieler zurücksetzen + player.setGameMode(GameMode.SURVIVAL); + player.setAllowFlight(false); + player.setFlying(false); + player.getInventory().clear(); + player.getInventory().setArmorContents(null); + player.setHealth(20.0); + player.setFoodLevel(20); + player.resetTitle(); + + player.sendMessage(VelocityFall.prefix + "§eServer wird heruntergefahren – Du wurdest aus der Arena teleportiert."); + + teleportCount++; + } catch (Exception e) { + plugin.getLogger().warning("Fehler beim Spieler-Teleport: " + e.getMessage()); + } + } + + plugin.getPlayerArenaMap().clear(); + plugin.ingamePlayers.clear(); + plugin.getLogger().info(teleportCount + " Spieler aus Arenas teleportiert."); + } + + /** + * Entfernt alle Scoreboards + */ + private void clearAllScoreboards() { + int cleared = 0; + for (Player player : plugin.getServer().getOnlinePlayers()) { + try { + ScoreboardHelper.clearBoard(player); + cleared++; + } catch (Exception e) { + plugin.getLogger().warning("Fehler beim Scoreboard-Clear für " + player.getName()); + } + } + plugin.getLogger().info(cleared + " Scoreboards entfernt."); + } +} diff --git a/src/main/java/de/velocityfall/manager/GameManager.java b/src/main/java/de/velocityfall/manager/GameManager.java index b1f8c4d..b19cf5b 100644 --- a/src/main/java/de/velocityfall/manager/GameManager.java +++ b/src/main/java/de/velocityfall/manager/GameManager.java @@ -1,6 +1,7 @@ package de.velocityfall.manager; import de.velocityfall.VelocityFall; +import de.velocityfall.utils.ScoreboardHelper; import de.velocityfall.utils.StatsHandler; import org.bukkit.Bukkit; import org.bukkit.ChatColor; @@ -18,7 +19,6 @@ import org.bukkit.scheduler.BukkitRunnable; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; public class GameManager { @@ -27,6 +27,7 @@ public class GameManager { * --------------------------------------------------- */ public static void startArenaCountdown(String arenaName) { final VelocityFall plugin = VelocityFall.getInstance(); + if (plugin == null) return; if (plugin.getGameStartedMap().getOrDefault(arenaName, false)) return; new BukkitRunnable() { @@ -56,12 +57,24 @@ public class GameManager { Location spawn = plugin.getArenaManager() .getArenaConfig().getLocation("Arenas." + arenaName + ".Spawn"); + // PLAYED Stats erhöhen für alle Spieler plugin.getPlayerArenaMap().forEach((p, a) -> { if (arenaName.equals(a)) { + // Stats tracken + StatsHandler.addPlayed(p.getUniqueId().toString()); + + // Teleport & Setup if (spawn != null) p.teleport(spawn); p.setGameMode(GameMode.ADVENTURE); p.setAllowFlight(false); p.playSound(p.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, 1f, 1f); + + // Scoreboard auf InGame umschalten + try { + ScoreboardHelper.updateBoard(p, arenaName, true); + } catch (Exception e) { + plugin.getLogger().warning("Scoreboard Update fehlgeschlagen: " + e.getMessage()); + } } }); cancel(); @@ -76,22 +89,24 @@ public class GameManager { } /* --------------------------------------------------- - * WIN HANDLING + * WIN HANDLING - MIT STATS TRACKING * --------------------------------------------------- */ public static void checkForWinner(String arenaName) { final VelocityFall plugin = VelocityFall.getInstance(); + if (plugin == null) return; + int remaining = plugin.getArenaPlayersCountMap().getOrDefault(arenaName, 0); if (remaining != 1) return; - // 1) Versuche Gewinner zuverlässig über playerArenaMap (Single source) + // 1) Gewinner finden (Nicht-Spectator) Player winner = plugin.getPlayerArenaMap().entrySet().stream() .filter(e -> arenaName.equals(e.getValue())) .map(Map.Entry::getKey) - .filter(p -> p.getGameMode() != GameMode.SPECTATOR) // Nicht-Spectator = Gewinner + .filter(p -> p.getGameMode() != GameMode.SPECTATOR) .findFirst() .orElse(null); - // 2) Fallback: VelocityFall.getArenaPlayers(arenaName) + // Fallback if (winner == null) { List arenaPlayers = VelocityFall.getArenaPlayers(arenaName); for (Player p : arenaPlayers) { @@ -102,40 +117,155 @@ public class GameManager { } } - if (winner == null) return; // kein Gewinner auffindbar → exit + if (winner == null) return; final Player finalWinner = winner; - // Nachricht + Stats + Scoreboard - try { - finalWinner.sendMessage(VelocityFall.prefix + ChatColor.translateAlternateColorCodes('&', - plugin.getMessages().getMessagesConfig() - .getString("WinMessage", "&aDu hast gewonnen!"))); - } catch (Exception ignored) {} + // 2) Lose-Location ermitteln + Location loseLocation = findLoseLocation(arenaName); + final Location finalLoseLocation = loseLocation; + + // 3) Verlierer sammeln (alle Spectators) + List losers = new ArrayList<>(); + plugin.getPlayerArenaMap().forEach((p, a) -> { + if (arenaName.equals(a) && !p.equals(finalWinner) && p.getGameMode() == GameMode.SPECTATOR) { + losers.add(p); + } + }); + + final List finalLosers = new ArrayList<>(losers); + + // 4) STATS TRACKING - GEWINNER try { StatsHandler.addWin(finalWinner.getUniqueId().toString()); - } catch (Exception ignored) {} + } catch (Exception e) { + plugin.getLogger().warning("Win-Stats konnten nicht gespeichert werden: " + e.getMessage()); + } + + // 5) STATS TRACKING - VERLIERER + for (Player loser : finalLosers) { + try { + StatsHandler.addLose(loser.getUniqueId().toString()); + } catch (Exception e) { + plugin.getLogger().warning("Lose-Stats konnten nicht gespeichert werden: " + e.getMessage()); + } + } + + // 6) GEWINNER VORBEREITEN try { - finalWinner.setScoreboard(Bukkit.getScoreboardManager().getNewScoreboard()); - } catch (Exception ignored) {} + finalWinner.setAllowFlight(true); + finalWinner.setFlying(true); + + Location winnerSafeLoc = plugin.getArenaManager() + .getArenaConfig().getLocation("Arenas." + arenaName + ".Spawn"); + if (winnerSafeLoc != null) { + finalWinner.teleport(winnerSafeLoc); + } + + finalWinner.sendMessage(VelocityFall.prefix + ChatColor.translateAlternateColorCodes('&', + plugin.getMessages().getMessagesConfig().getString("WinMessage", "&aDu hast gewonnen!"))); + + finalWinner.sendTitle( + ChatColor.translateAlternateColorCodes('&', "&6&lSIEG!"), + ChatColor.translateAlternateColorCodes('&', "&eDu hast gewonnen!"), + 10, 70, 20 + ); + + finalWinner.playSound(finalWinner.getLocation(), Sound.UI_TOAST_CHALLENGE_COMPLETE, 1f, 1f); + ScoreboardHelper.clearBoard(finalWinner); + + } catch (Exception e) { + plugin.getLogger().warning("Gewinner-Setup fehlgeschlagen: " + e.getMessage()); + } - // Lose-Location: mehrere mögliche Keys supporten + YLose fallback - Location loseLocation = plugin.getArenaManager() - .getArenaConfig().getLocation("Arenas." + arenaName + ".Lose"); - if (loseLocation == null) loseLocation = plugin.getArenaManager() - .getArenaConfig().getLocation("Arenas." + arenaName + ".LoseLocation"); - if (loseLocation == null) loseLocation = plugin.getArenaManager() - .getArenaConfig().getLocation("Arenas." + arenaName + ".LosePoint"); - if (loseLocation == null) loseLocation = plugin.getArenaManager() - .getArenaConfig().getLocation("Arenas." + arenaName + ".SetLose"); + // 7) VERLIERER BENACHRICHTIGEN + for (Player loser : finalLosers) { + try { + if (finalLoseLocation != null && loser.getGameMode() == GameMode.SPECTATOR) { + loser.teleport(finalLoseLocation); + } + + loser.sendTitle( + ChatColor.translateAlternateColorCodes('&', "&c&lNIEDERLAGE"), + ChatColor.translateAlternateColorCodes('&', "&e" + finalWinner.getName() + " &7hat gewonnen!"), + 10, 70, 20 + ); + + loser.playSound(loser.getLocation(), Sound.ENTITY_VILLAGER_NO, 1f, 1f); + ScoreboardHelper.clearBoard(loser); + + } catch (Exception e) { + plugin.getLogger().warning("Verlierer-Setup fehlgeschlagen: " + e.getMessage()); + } + } - // Spezial-Fallback: YLose vorhanden? Dann baue Location auf Basis von Spawn oder Lobby X/Z + // 8) FEUERWERK MIT DELAY + int delaySeconds = plugin.getConfig().getInt("PostWinDelay", 4); + delaySeconds = Math.min(5, Math.max(3, delaySeconds)); + final long delayTicks = delaySeconds * 20L; + + new BukkitRunnable() { + int fireworksSpawned = 0; + + @Override + public void run() { + if (fireworksSpawned < 5) { + try { + spawnFireworks(finalWinner.getLocation().clone().add(0, 1, 0), 1); + + finalWinner.playSound(finalWinner.getLocation(), Sound.ENTITY_FIREWORK_ROCKET_LAUNCH, 1f, 1f); + for (Player loser : finalLosers) { + if (loser != null && loser.isOnline()) { + loser.playSound(loser.getLocation(), Sound.ENTITY_FIREWORK_ROCKET_LAUNCH, 1f, 1f); + } + } + } catch (Exception e) { + plugin.getLogger().warning("Feuerwerk-Spawn fehlgeschlagen: " + e.getMessage()); + } + fireworksSpawned++; + } else { + cancel(); + + // Nach Feuerwerk: Arena resetten + Bukkit.getScheduler().runTaskLater(plugin, () -> { + try { + performSafeResetAndLobbyTeleport(arenaName, finalWinner, finalLosers); + } catch (Exception e) { + plugin.getLogger().severe("Arena-Reset fehlgeschlagen: " + e.getMessage()); + } + + try { + plugin.ingameArenas.remove(arenaName); + plugin.getSignManager().updateSign(arenaName); + } catch (Exception ignored) {} + }, 40L); + } + } + }.runTaskTimer(plugin, delayTicks, 10L); + } + + /* --------------------------------------------------- + * LOSE LOCATION FINDER - HELPER + * --------------------------------------------------- */ + private static Location findLoseLocation(String arenaName) { + final VelocityFall plugin = VelocityFall.getInstance(); + if (plugin == null) return null; + + var arenaConfig = plugin.getArenaManager().getArenaConfig(); + + // Verschiedene mögliche Keys probieren + Location loseLocation = arenaConfig.getLocation("Arenas." + arenaName + ".Lose"); + if (loseLocation == null) loseLocation = arenaConfig.getLocation("Arenas." + arenaName + ".LoseLocation"); + if (loseLocation == null) loseLocation = arenaConfig.getLocation("Arenas." + arenaName + ".LosePoint"); + if (loseLocation == null) loseLocation = arenaConfig.getLocation("Arenas." + arenaName + ".SetLose"); + + // YLose Fallback if (loseLocation == null) { - Double yLose = plugin.getArenaManager().getArenaConfig().getDouble("Arenas." + arenaName + ".YLose", Double.NaN); + Double yLose = arenaConfig.getDouble("Arenas." + arenaName + ".YLose", Double.NaN); if (!yLose.isNaN()) { - // Priorität: Spawn -> Lobby -> arena center (Pos1/Pos2 midpoint) - Location spawn = plugin.getArenaManager().getArenaConfig().getLocation("Arenas." + arenaName + ".Spawn"); - Location lobby = plugin.getArenaManager().getArenaConfig().getLocation("Arenas." + arenaName + ".Lobby"); + Location spawn = arenaConfig.getLocation("Arenas." + arenaName + ".Spawn"); + Location lobby = arenaConfig.getLocation("Arenas." + arenaName + ".Lobby"); + if (spawn != null) { loseLocation = spawn.clone(); loseLocation.setY(yLose); @@ -143,9 +273,9 @@ public class GameManager { loseLocation = lobby.clone(); loseLocation.setY(yLose); } else { - // midpoint of Pos1 and Pos2 if available - Location pos1 = plugin.getArenaManager().getArenaConfig().getLocation("Arenas." + arenaName + ".Pos1"); - Location pos2 = plugin.getArenaManager().getArenaConfig().getLocation("Arenas." + arenaName + ".Pos2"); + Location pos1 = arenaConfig.getLocation("Arenas." + arenaName + ".Pos1"); + Location pos2 = arenaConfig.getLocation("Arenas." + arenaName + ".Pos2"); + if (pos1 != null && pos2 != null && pos1.getWorld() != null && pos1.getWorld().equals(pos2.getWorld())) { double midX = (pos1.getX() + pos2.getX()) / 2.0; double midZ = (pos1.getZ() + pos2.getZ()) / 2.0; @@ -155,112 +285,23 @@ public class GameManager { } } - final Location finalLoseLocation = loseLocation; // final für Lambdas - - // Ermittlung der Verlierer: alle Spectator-Spieler in dieser Arena - List losers = new ArrayList<>(); - - // Strategie A: playerArenaMap - alle Spectators sind Verlierer - plugin.getPlayerArenaMap().forEach((p, a) -> { - if (arenaName.equals(a) && !p.equals(finalWinner) && p.getGameMode() == GameMode.SPECTATOR) { - losers.add(p); - } - }); - - // Strategie B: Falls keine Verlierer gefunden -> ingamePlayers Liste - if (losers.isEmpty()) { - try { - for (Player p : plugin.ingamePlayers) { - if (p != null && !p.equals(finalWinner) && p.getGameMode() == GameMode.SPECTATOR) { - losers.add(p); - } - } - } catch (Exception ignored) {} - } - - // Strategie C: VelocityFall.getArenaPlayers(arenaName) - if (losers.isEmpty()) { - List arenaPlayers = VelocityFall.getArenaPlayers(arenaName); - if (!arenaPlayers.isEmpty()) { - for (Player p : arenaPlayers) { - if (p != null && !p.equals(finalWinner) && p.getGameMode() == GameMode.SPECTATOR) { - losers.add(p); - } - } - } - } - - // Erstelle finale Liste der Verlierer (damit sie auch nach Delays verfügbar ist) - final List finalLosers = new ArrayList<>(losers); - - // Teleportiere Verlierer SOFORT zum Lose-Punkt (sie sind bereits in GM3 durch FloorLoseListener) - for (Player loser : finalLosers) { - try { - // Sicherstellen, dass Spectator-Modus gesetzt ist - if (loser.getGameMode() != GameMode.SPECTATOR) { - loser.setGameMode(GameMode.SPECTATOR); - } - - if (finalLoseLocation != null) { - loser.teleport(finalLoseLocation); - } else { - // fallback: direkt neben Gewinner - Location fallback = finalWinner.getLocation().clone().add(1.0, 0.0, 0.0); - loser.teleport(fallback); - } - } catch (Exception e) { - // Falls Teleport fehlschlägt, loggen wir es serverseitig - e.printStackTrace(); - } - } - - // Delay 3-5 Sekunden (konfigurierbar) - int delaySeconds = plugin.getConfig().getInt("PostWinDelay", 4); - delaySeconds = Math.min(5, Math.max(3, delaySeconds)); - final long delayTicks = delaySeconds * 20L; - - // Nach Delay: Feuerwerk beim Gewinner und dann Arena resetten / Lobby teleport - new BukkitRunnable() { - @Override - public void run() { - try { - spawnFireworks(finalWinner.getLocation(), 2); - finalWinner.playSound(finalWinner.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1f, 1f); - } catch (Exception ignored) {} - - // 1 Sekunde später: Reset & Teleport aller zur Lobby (GM1) - new BukkitRunnable() { - @Override - public void run() { - try { - // Robustes Reset: teleportiere Gewinner + finale Verlierer-Liste zur Lobby - performSafeResetAndLobbyTeleport(arenaName, finalWinner, finalLosers); - } catch (Exception ignored) {} - - // Aufräumen maps / Signs - try { - plugin.ingameArenas.remove(arenaName); - } catch (Exception ignored) {} - try { - plugin.getSignManager().updateSign(arenaName); - } catch (Exception ignored) {} - } - }.runTaskLater(plugin, 20L); // 1 Sekunde nach Feuerwerk - } - }.runTaskLater(plugin, delayTicks); + return loseLocation; } /* --------------------------------------------------- - * SAFE RESET & LOBBY TELEPORT (robust) + * SAFE RESET & LOBBY TELEPORT * --------------------------------------------------- */ private static void performSafeResetAndLobbyTeleport(String arenaName, Player winner, List losers) { final VelocityFall plugin = VelocityFall.getInstance(); + if (plugin == null) return; // Blöcke zurücksetzen plugin.getArenaBlocksMap().forEach((block, matName) -> { try { block.setType(Material.valueOf(matName)); - } catch (Exception ignored) {} + } catch (Exception e) { + plugin.getLogger().warning("Block-Reset fehlgeschlagen: " + e.getMessage()); + } }); plugin.getArenaBlocksMap().clear(); @@ -268,65 +309,42 @@ public class GameManager { plugin.getGameStartedMap().put(arenaName, false); plugin.getArenaPlayersCountMap().remove(arenaName); - // Lobby-Location (kann null sein) + // Lobby-Location Location lobby = plugin.getArenaManager().getArenaConfig().getLocation("Arenas." + arenaName + ".Lobby"); - // Sammle alle Spieler, die teleportiert werden müssen + // Alle Spieler sammeln List toHandle = new ArrayList<>(); - - // Gewinner hinzufügen - if (winner != null) { - toHandle.add(winner); - } - - // Verlierer hinzufügen (bereits als finale Liste übergeben) + if (winner != null) toHandle.add(winner); for (Player loser : losers) { if (loser != null && !toHandle.contains(loser)) { toHandle.add(loser); } } - // Zusätzlich: alle restlichen Spieler aus playerArenaMap (Sicherheit) + // Sicherheit: Alle aus playerArenaMap plugin.getPlayerArenaMap().forEach((p, a) -> { if (arenaName.equals(a) && !toHandle.contains(p)) { toHandle.add(p); } }); - // Fallback: ingamePlayers - try { - for (Player p : new ArrayList<>(plugin.ingamePlayers)) { - if (p != null && !toHandle.contains(p)) { - // Prüfe ob Spieler möglicherweise zu dieser Arena gehört - String playerArena = plugin.getPlayerArenaMap().get(p); - if (arenaName.equals(playerArena)) { - toHandle.add(p); - } - } - } - } catch (Exception ignored) {} - - // Fallback: VelocityFall.getArenaPlayers - try { - List arenaPlayers = VelocityFall.getArenaPlayers(arenaName); - for (Player p : arenaPlayers) { - if (p != null && !toHandle.contains(p)) toHandle.add(p); - } - } catch (Exception ignored) {} - - // Verarbeite alle gesammelten Spieler: GM1 (SURVIVAL), Inventar clear, teleport zur Lobby, entferne aus Maps + // Alle Spieler resetten for (Player p : toHandle) { try { - p.setGameMode(GameMode.SURVIVAL); // GM1 + p.setGameMode(GameMode.SURVIVAL); + p.setAllowFlight(false); + p.setFlying(false); p.getInventory().clear(); p.getInventory().setArmorContents(null); p.setHealth(20.0); p.setFoodLevel(20); + p.resetTitle(); + + ScoreboardHelper.clearBoard(p); if (lobby != null) { p.teleport(lobby); } else { - // Fallback: Spawn-Location oder World-Spawn Location spawn = plugin.getArenaManager().getArenaConfig().getLocation("Arenas." + arenaName + ".Spawn"); if (spawn != null) { p.teleport(spawn); @@ -335,11 +353,10 @@ public class GameManager { } } - // Jetzt erst aus den Maps entfernen plugin.ingamePlayers.remove(p); plugin.getPlayerArenaMap().remove(p); } catch (Exception e) { - e.printStackTrace(); + plugin.getLogger().warning("Spieler-Reset fehlgeschlagen für " + p.getName() + ": " + e.getMessage()); } } @@ -364,12 +381,16 @@ public class GameManager { for (int i = 0; i < Math.max(1, count); i++) { try { - Firework fw = loc.getWorld().spawn(loc.clone().add(0, 1 + i * 0.5, 0), Firework.class); + Firework fw = loc.getWorld().spawn(loc.clone().add(0, i * 0.5, 0), Firework.class); FireworkMeta meta = fw.getFireworkMeta(); + Color[] colors = {Color.LIME, Color.YELLOW, Color.ORANGE, Color.RED, Color.AQUA, Color.FUCHSIA}; + Color randomColor1 = colors[(int)(Math.random() * colors.length)]; + Color randomColor2 = colors[(int)(Math.random() * colors.length)]; + FireworkEffect effect = FireworkEffect.builder() .with(FireworkEffect.Type.BALL_LARGE) - .withColor(Color.LIME, Color.YELLOW) + .withColor(randomColor1, randomColor2) .withFade(Color.WHITE) .trail(true) .flicker(true) @@ -379,7 +400,7 @@ public class GameManager { meta.setPower(1); fw.setFireworkMeta(meta); } catch (Throwable t) { - t.printStackTrace(); + VelocityFall.getInstance().getLogger().warning("Firework-Spawn fehlgeschlagen: " + t.getMessage()); } } } @@ -389,10 +410,12 @@ public class GameManager { * --------------------------------------------------- */ private static void broadcast(String arena, String msg) { final VelocityFall plugin = VelocityFall.getInstance(); + if (plugin == null) return; + plugin.getPlayerArenaMap().forEach((player, a) -> { if (arena.equals(a)) { player.sendMessage(VelocityFall.prefix + msg); } }); } -} \ No newline at end of file +} diff --git a/src/main/java/de/velocityfall/manager/JoinManager.java b/src/main/java/de/velocityfall/manager/JoinManager.java index d34b35e..5efa772 100644 --- a/src/main/java/de/velocityfall/manager/JoinManager.java +++ b/src/main/java/de/velocityfall/manager/JoinManager.java @@ -1,6 +1,7 @@ package de.velocityfall.manager; import de.velocityfall.VelocityFall; +import de.velocityfall.utils.ScoreboardHelper; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -54,6 +55,9 @@ public class JoinManager { player.sendMessage(VelocityFall.prefix + "§aBeigetreten: §e" + arenaName); + // Scoreboard setzen (Lobby-Scoreboard) + ScoreboardHelper.updateBoard(player, arenaName, false); + int min = plugin.getConfig().getInt("MinPlayers", 2); if (current + 1 >= min && !plugin.getGameStartedMap().getOrDefault(arenaName, false)) { GameManager.startArenaCountdown(arenaName); @@ -62,6 +66,9 @@ public class JoinManager { } plugin.getSignManager().updateSign(arenaName); + + // Scoreboard für alle anderen in der Arena updaten + ScoreboardHelper.updateAllInArena(arenaName, false); } public void join(Player player, String arenaName) { @@ -73,4 +80,4 @@ public class JoinManager { if (arena.equals(a)) p.sendMessage(VelocityFall.prefix + msg); }); } -} \ No newline at end of file +} diff --git a/src/main/java/de/velocityfall/manager/Messages.java b/src/main/java/de/velocityfall/manager/Messages.java index 17edb29..5239c2d 100644 --- a/src/main/java/de/velocityfall/manager/Messages.java +++ b/src/main/java/de/velocityfall/manager/Messages.java @@ -44,4 +44,4 @@ public class Messages { public YamlConfiguration getMessagesConfig() { return messagesConfig; } -} \ No newline at end of file +} diff --git a/src/main/java/de/velocityfall/manager/MySQLConnector.java b/src/main/java/de/velocityfall/manager/MySQLConnector.java index d8bf2b2..87d5be9 100644 --- a/src/main/java/de/velocityfall/manager/MySQLConnector.java +++ b/src/main/java/de/velocityfall/manager/MySQLConnector.java @@ -34,6 +34,9 @@ public class MySQLConnector { } } + /** + * Führt ein UPDATE, INSERT oder DELETE Statement aus + */ public void executeUpdate(String sql, Object... params) { try (PreparedStatement ps = connection.prepareStatement(sql)) { for (int i = 0; i < params.length; i++) { @@ -42,23 +45,85 @@ public class MySQLConnector { ps.executeUpdate(); } catch (SQLException e) { plugin.getLogger().warning("Update fehlgeschlagen: " + e.getMessage()); - connect(); + reconnect(); } } - public ResultSet executeQuery(String sql, Object... params) throws SQLException { - PreparedStatement ps = connection.prepareStatement(sql); - for (int i = 0; i < params.length; i++) { - ps.setObject(i + 1, params[i]); + /** + * Gibt einen einzelnen Integer-Wert zurück (ohne Connection Leak) + */ + public Integer executeQuerySingleInt(String sql, String columnName, Object... params) { + try (PreparedStatement ps = connection.prepareStatement(sql)) { + for (int i = 0; i < params.length; i++) { + ps.setObject(i + 1, params[i]); + } + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + return rs.getInt(columnName); + } + } + } catch (SQLException e) { + plugin.getLogger().warning("Query fehlgeschlagen: " + e.getMessage()); + reconnect(); } - return ps.executeQuery(); + return null; } + /** + * Gibt einen einzelnen String-Wert zurück + */ + public String executeQuerySingleString(String sql, String columnName, Object... params) { + try (PreparedStatement ps = connection.prepareStatement(sql)) { + for (int i = 0; i < params.length; i++) { + ps.setObject(i + 1, params[i]); + } + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + return rs.getString(columnName); + } + } + } catch (SQLException e) { + plugin.getLogger().warning("Query fehlgeschlagen: " + e.getMessage()); + reconnect(); + } + return null; + } + + /** + * Prüft ob Verbindung noch aktiv ist und reconnected falls nötig + */ + private void reconnect() { + try { + if (connection == null || connection.isClosed()) { + connect(); + } + } catch (SQLException e) { + plugin.getLogger().severe("Reconnect fehlgeschlagen: " + e.getMessage()); + } + } + + /** + * Prüft ob MySQL verbunden ist + */ + public boolean isConnected() { + try { + return connection != null && !connection.isClosed(); + } catch (SQLException e) { + return false; + } + } + + /** + * Schließt die Verbindung sauber + */ public void close() { try { - if (connection != null && !connection.isClosed()) connection.close(); + if (connection != null && !connection.isClosed()) { + connection.close(); + plugin.getLogger().info("MySQL Verbindung geschlossen."); + } } catch (SQLException e) { - e.printStackTrace(); + plugin.getLogger().warning("Fehler beim Schließen der MySQL Verbindung: " + e.getMessage()); } } -} \ No newline at end of file +} diff --git a/src/main/java/de/velocityfall/manager/SignManager.java b/src/main/java/de/velocityfall/manager/SignManager.java index d68fa1b..56b40c3 100644 --- a/src/main/java/de/velocityfall/manager/SignManager.java +++ b/src/main/java/de/velocityfall/manager/SignManager.java @@ -12,19 +12,24 @@ public class SignManager { } public void updateSign(String arenaName) { - var arenaConfig = plugin.getArenaManager().getArenaConfig(); - Location signLoc = arenaConfig.getLocation("Arenas." + arenaName + ".SignLoc"); + try { + var arenaConfig = plugin.getArenaManager().getArenaConfig(); + Location signLoc = arenaConfig.getLocation("Arenas." + arenaName + ".SignLoc"); - if (signLoc == null || !(signLoc.getBlock().getState() instanceof Sign sign)) return; + if (signLoc == null) return; + if (!(signLoc.getBlock().getState() instanceof Sign sign)) return; - int count = plugin.getArenaPlayersCountMap().getOrDefault(arenaName, 0); - int max = arenaConfig.getInt("Arenas." + arenaName + ".Max", 0); + int count = plugin.getArenaPlayersCountMap().getOrDefault(arenaName, 0); + int max = arenaConfig.getInt("Arenas." + arenaName + ".Max", 0); - sign.setLine(0, "§8§l- §cVelocityFall §8§l-"); - sign.setLine(1, "§b" + arenaName); - sign.setLine(2, plugin.getGameStartedMap().getOrDefault(arenaName, false) ? "§cINGAME" : "§aLOBBY"); - sign.setLine(3, "§8" + count + "§7/§8" + max); + sign.setLine(0, "§8§l- §cVelocityFall §8§l-"); + sign.setLine(1, "§b" + arenaName); + sign.setLine(2, plugin.getGameStartedMap().getOrDefault(arenaName, false) ? "§cINGAME" : "§aLOBBY"); + sign.setLine(3, "§8" + count + "§7/§8" + max); - sign.update(true, false); + sign.update(true, false); + } catch (Exception e) { + plugin.getLogger().warning("Sign Update fehlgeschlagen für Arena " + arenaName + ": " + e.getMessage()); + } } -} \ No newline at end of file +} diff --git a/src/main/java/de/velocityfall/manager/Stats.java b/src/main/java/de/velocityfall/manager/Stats.java index 5fc0fdd..1e9a5bc 100644 --- a/src/main/java/de/velocityfall/manager/Stats.java +++ b/src/main/java/de/velocityfall/manager/Stats.java @@ -48,4 +48,4 @@ public class Stats { public YamlConfiguration getStatsConfig() { return statsConfig; } -} \ No newline at end of file +} diff --git a/src/main/java/de/velocityfall/utils/ScoreboardHelper.java b/src/main/java/de/velocityfall/utils/ScoreboardHelper.java index 32ed748..efb3e48 100644 --- a/src/main/java/de/velocityfall/utils/ScoreboardHelper.java +++ b/src/main/java/de/velocityfall/utils/ScoreboardHelper.java @@ -6,40 +6,164 @@ import org.bukkit.ChatColor; import org.bukkit.entity.Player; import org.bukkit.scoreboard.*; +import java.util.ArrayList; import java.util.List; public class ScoreboardHelper { public static void updateBoard(Player player, String arenaName, boolean isIngame) { - VelocityFall plugin = VelocityFall.getInstance(); + try { + VelocityFall plugin = VelocityFall.getInstance(); + if (plugin == null) return; - Scoreboard board = Bukkit.getScoreboardManager().getNewScoreboard(); - String titlePath = isIngame ? "InGameScoreBoardTitle" : "LobbyScoreBoardTitle"; - String title = ChatColor.translateAlternateColorCodes('&', plugin.getConfig().getString(titlePath, "&6VelocityFall")); + Scoreboard board = Bukkit.getScoreboardManager().getNewScoreboard(); + String titlePath = isIngame ? "InGameScoreBoardTitle" : "LobbyScoreBoardTitle"; + String rawTitle = plugin.getConfig().getString(titlePath, "&6&lVELOCITY FALL"); + String title = ChatColor.translateAlternateColorCodes('&', rawTitle); - Objective objective = board.registerNewObjective("vfall", Criteria.DUMMY, title); - objective.setDisplaySlot(DisplaySlot.SIDEBAR); + // Längen-Check OHNE ChatColor Codes + String strippedTitle = ChatColor.stripColor(title); + if (strippedTitle.length() > 32) { + // Intelligentes Kürzen: Behalte Farben bei + title = title.substring(0, Math.min(title.length(), 32)); + } - String listPath = isIngame ? "InGameScoreBoard" : "LobbyScoreBoard"; - List lines = plugin.getConfig().getStringList(listPath); + Objective objective = board.registerNewObjective("vfall", "dummy", title); + objective.setDisplaySlot(DisplaySlot.SIDEBAR); - int scoreValue = lines.size(); - for (String rawLine : lines) { - String line = ChatColor.translateAlternateColorCodes('&', rawLine - .replace("%arena%", arenaName != null ? arenaName : "???") - .replace("%players%", String.valueOf(plugin.getArenaPlayersCountMap().getOrDefault(arenaName, 0))) - .replace("%max%", String.valueOf(plugin.getArenaManager().getArenaConfig().getInt("Arenas." + arenaName + ".Max", 0)))); + String listPath = isIngame ? "InGameScoreBoard" : "LobbyScoreBoard"; + List configLines = plugin.getConfig().getStringList(listPath); - objective.getScore(line).setScore(scoreValue--); + // Fallback falls Config leer ist + if (configLines == null || configLines.isEmpty()) { + configLines = getDefaultLines(isIngame); + } + + // Variablen für Replacements + int currentPlayers = plugin.getArenaPlayersCountMap().getOrDefault(arenaName, 0); + int maxPlayers = 0; + if (arenaName != null) { + maxPlayers = plugin.getArenaManager().getArenaConfig().getInt("Arenas." + arenaName + ".Max", 12); + } + + // Status-Anzeige + String statusColor = isIngame ? "&c" : "&a"; + String statusText = isIngame ? "IM SPIEL" : "LOBBY"; + + // Spieler-Farbe basierend auf Füllstand + String playerColor = getPlayerCountColor(currentPlayers, maxPlayers); + + List processedLines = new ArrayList<>(); + int scoreValue = configLines.size(); + + for (String rawLine : configLines) { + String line = ChatColor.translateAlternateColorCodes('&', rawLine + .replace("%arena%", arenaName != null ? arenaName : "???") + .replace("%players%", String.valueOf(currentPlayers)) + .replace("%max%", String.valueOf(maxPlayers)) + .replace("%player%", player.getName()) + .replace("%status%", statusText) + .replace("%status_color%", statusColor) + .replace("%player_color%", playerColor)); + + // Zeilen-Länge prüfen + if (line.length() > 40) { + line = line.substring(0, 40); + } + + // Duplikate verhindern + while (processedLines.contains(line)) { + line = line + ChatColor.RESET; + } + processedLines.add(line); + + // Score setzen + Score score = objective.getScore(line); + score.setScore(scoreValue--); + } + + player.setScoreboard(board); + + } catch (Exception e) { + VelocityFall plugin = VelocityFall.getInstance(); + if (plugin != null) { + plugin.getLogger().warning("Scoreboard Update fehlgeschlagen: " + e.getMessage()); + } } - - player.setScoreboard(board); } + /** + * Gibt die passende Farbe basierend auf der Spieleranzahl zurück + */ + private static String getPlayerCountColor(int current, int max) { + if (max == 0) return "&7"; + + double percentage = (double) current / max; + + if (percentage >= 0.8) return "&c"; // 80%+ = rot (fast voll) + if (percentage >= 0.5) return "&e"; // 50%+ = gelb + return "&a"; // <50% = grün (viel Platz) + } + + /** + * Updated Scoreboard für alle Spieler in einer Arena + */ public static void updateAllInArena(String arenaName, boolean isIngame) { - VelocityFall plugin = VelocityFall.getInstance(); - plugin.getPlayerArenaMap().forEach((p, a) -> { - if (arenaName.equals(a)) updateBoard(p, arenaName, isIngame); - }); + try { + VelocityFall plugin = VelocityFall.getInstance(); + if (plugin == null) return; + + plugin.getPlayerArenaMap().forEach((p, a) -> { + if (arenaName.equals(a)) { + updateBoard(p, arenaName, isIngame); + } + }); + } catch (Exception e) { + VelocityFall plugin = VelocityFall.getInstance(); + if (plugin != null) { + plugin.getLogger().warning("Scoreboard Batch-Update fehlgeschlagen: " + e.getMessage()); + } + } } -} \ No newline at end of file + + /** + * Entfernt Scoreboard von einem Spieler + */ + public static void clearBoard(Player player) { + try { + if (player != null && player.isOnline()) { + player.setScoreboard(Bukkit.getScoreboardManager().getNewScoreboard()); + } + } catch (Exception e) { + // Ignoriere Fehler beim Cleanup + } + } + + /** + * Standard-Zeilen falls Config leer ist (MODERN DESIGN) + */ + private static List getDefaultLines(boolean isIngame) { + List lines = new ArrayList<>(); + if (isIngame) { + lines.add("&8&m--------------------"); + lines.add(" "); + lines.add(" &6⚔ &eArena&8: &f%arena%"); + lines.add(" &c❤ &eÜbrig&8: %player_color%%players%"); + lines.add(" "); + lines.add(" &a✓ &7Überlebe so lange"); + lines.add(" &7wie möglich!"); + lines.add(" "); + lines.add("&8&m--------------------"); + } else { + lines.add("&8&m--------------------"); + lines.add(" "); + lines.add(" &6⚔ &eArena&8: &f%arena%"); + lines.add(" &b👥 &eSpieler&8: %player_color%%players%&8/&7%max%"); + lines.add(" "); + lines.add(" &7Warte auf Spieler..."); + lines.add(" "); + lines.add("&8&m--------------------"); + } + return lines; + } +} diff --git a/src/main/java/de/velocityfall/utils/StatsHandler.java b/src/main/java/de/velocityfall/utils/StatsHandler.java index 88ae875..c21af73 100644 --- a/src/main/java/de/velocityfall/utils/StatsHandler.java +++ b/src/main/java/de/velocityfall/utils/StatsHandler.java @@ -5,53 +5,138 @@ import org.bukkit.configuration.file.YamlConfiguration; import java.io.File; import java.io.IOException; -import java.sql.ResultSet; -import java.sql.SQLException; public class StatsHandler { + /** + * Erhöht den Win-Counter für einen Spieler + */ public static void addWin(String uuid) { addStat(uuid, "WINS"); } + /** + * Erhöht den Lose-Counter für einen Spieler + */ public static void addLose(String uuid) { addStat(uuid, "LOSE"); } + /** + * Erhöht den Played-Counter für einen Spieler + * NEU: Wird jetzt beim Game-Start aufgerufen + */ + public static void addPlayed(String uuid) { + addStat(uuid, "PLAYED"); + } + + /** + * Interne Methode zum Erhöhen eines Stats + */ private static void addStat(String uuid, String column) { VelocityFall plugin = VelocityFall.getInstance(); + + if (plugin == null) { + return; + } + if (plugin.getStats().getStatsConfig().getBoolean("EnableMySQL", false)) { - plugin.getMySQLConnector().executeUpdate( - "UPDATE VelocityFallStats SET " + column + " = " + column + " + 1 WHERE UUID = ?", - uuid - ); + // MySQL: INSERT ... ON DUPLICATE KEY UPDATE + // Erstellt automatisch einen Eintrag falls UUID noch nicht existiert + if (plugin.getMySQLConnector() != null && plugin.getMySQLConnector().isConnected()) { + plugin.getMySQLConnector().executeUpdate( + "INSERT INTO VelocityFallStats (UUID, " + column + ") VALUES (?, 1) " + + "ON DUPLICATE KEY UPDATE " + column + " = " + column + " + 1", + uuid + ); + } } else { + // YAML Fallback File file = new File(plugin.getDataFolder(), "stats.yml"); YamlConfiguration cfg = YamlConfiguration.loadConfiguration(file); cfg.set(uuid + "." + column, cfg.getInt(uuid + "." + column, 0) + 1); try { cfg.save(file); } catch (IOException e) { - e.printStackTrace(); + plugin.getLogger().warning("Konnte stats.yml nicht speichern: " + e.getMessage()); } } } + /** + * Holt die Anzahl der Wins + */ public static int getWins(String uuid) { return getStat(uuid, "WINS"); } + /** + * Holt die Anzahl der Loses + */ + public static int getLoses(String uuid) { + return getStat(uuid, "LOSE"); + } + + /** + * Holt die Anzahl der gespielten Runden + */ + public static int getPlayed(String uuid) { + return getStat(uuid, "PLAYED"); + } + + /** + * Interne Methode zum Abrufen eines Stats + */ private static int getStat(String uuid, String column) { VelocityFall plugin = VelocityFall.getInstance(); + + if (plugin == null) { + return 0; + } + if (plugin.getStats().getStatsConfig().getBoolean("EnableMySQL", false)) { - try (ResultSet rs = plugin.getMySQLConnector().executeQuery( - "SELECT " + column + " FROM VelocityFallStats WHERE UUID = ?", uuid)) { - if (rs.next()) return rs.getInt(column); - } catch (SQLException e) { - e.printStackTrace(); + // MySQL + if (plugin.getMySQLConnector() != null && plugin.getMySQLConnector().isConnected()) { + Integer result = plugin.getMySQLConnector().executeQuerySingleInt( + "SELECT " + column + " FROM VelocityFallStats WHERE UUID = ?", + column, + uuid + ); + return result != null ? result : 0; } } + + // YAML Fallback File file = new File(plugin.getDataFolder(), "stats.yml"); return YamlConfiguration.loadConfiguration(file).getInt(uuid + "." + column, 0); } -} \ No newline at end of file + + /** + * Setzt alle Stats für einen Spieler zurück + */ + public static void resetStats(String uuid) { + VelocityFall plugin = VelocityFall.getInstance(); + + if (plugin == null) { + return; + } + + if (plugin.getStats().getStatsConfig().getBoolean("EnableMySQL", false)) { + if (plugin.getMySQLConnector() != null && plugin.getMySQLConnector().isConnected()) { + plugin.getMySQLConnector().executeUpdate( + "DELETE FROM VelocityFallStats WHERE UUID = ?", + uuid + ); + } + } else { + File file = new File(plugin.getDataFolder(), "stats.yml"); + YamlConfiguration cfg = YamlConfiguration.loadConfiguration(file); + cfg.set(uuid, null); + try { + cfg.save(file); + } catch (IOException e) { + plugin.getLogger().warning("Konnte stats.yml nicht speichern: " + e.getMessage()); + } + } + } +} diff --git a/src/main/java/de/velocityfall/utils/UpdateChecker.java b/src/main/java/de/velocityfall/utils/UpdateChecker.java new file mode 100644 index 0000000..288b1c7 --- /dev/null +++ b/src/main/java/de/velocityfall/utils/UpdateChecker.java @@ -0,0 +1,33 @@ +package de.velocityfall.utils; + +import de.velocityfall.VelocityFall; +import org.bukkit.Bukkit; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Scanner; +import java.util.function.Consumer; + +public class UpdateChecker { + + private final VelocityFall plugin; + private final int resourceId; + + public UpdateChecker(VelocityFall plugin, int resourceId) { + this.plugin = plugin; + this.resourceId = resourceId; + } + + public void getLatestVersion(final Consumer consumer) { + Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> { + try (InputStream inputStream = new URL("https://api.spigotmc.org/legacy/update.php?resource=" + this.resourceId).openStream(); + Scanner scanner = new Scanner(inputStream)) { + if (scanner.hasNext()) { + consumer.accept(scanner.next()); + } + } catch (IOException exception) { + plugin.getLogger().info("Update-Check fehlgeschlagen: " + exception.getMessage()); + } + }); + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index ca26055..2dbdd51 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,27 +1,127 @@ -Prefix: '&c[VelocityFall] ' +# _ __ __ _ __ ______ ____ +# | | / /__ / /___ _____(_) /___ __/ ____/___ _/ / / +# | | / / _ \/ / __ \/ ___/ / __/ / / / /_ / __ `/ / / +# | |/ / __/ / /_/ / /__/ / /_/ /_/ / __/ / /_/ / / / +# |___/\___/_/\____/\___/_/\__/\__, /_/ \__,_/_/_/ +# /____/ + + +# VelocityFall - Minecraft Minigame Plugin +# Beispiel-Konfiguration + +Prefix: "&6Velocity&fFall &8» " +NoPermission: "&cKeine Rechte!" + +# Minimale Spieleranzahl für Start +MinPlayers: 2 + +# Countdown vor Spielstart (Sekunden) BeforeGameCD: 15 -CountdownSound: ENTITY_EXPERIENCE_ORB_PICKUP -SaveInv: true + +# Delay nach Sieg bevor Reset (Sekunden, 3-5) PostWinDelay: 4 -WinCommands: - - eco give %player% 10 - - msg %player% Du hast gewonnen! -CmdWhitelist: - - /vfall leave - - /tell -MySQL: - Enable: false - Host: localhost - Port: '3306' - Database: vfall - User: root - Password: '' -LobbyScoreBoardTitle: '&6&lVelocityFall' -InGameScoreBoardTitle: '&6&lVelocityFall' + +# Inventar speichern? +SaveInv: false + +# ======================================== +# LOBBY SCOREBOARD (vor Spielstart) +# ======================================== +LobbyScoreBoardTitle: "&6&lVELOCITY &f&lFALL" + LobbyScoreBoard: - - '&7---' - - '&eArena:' - - '&f%arena%' - - '&7' - - '&eSpieler:' - - '&f%players%' \ No newline at end of file + - "&8&m━━━━━━━━━━━━━━━━━━━━━" + - " " + - " &6⚔ &eArena&8: &f%arena%" + - " &b👥 &eSpieler&8: %player_color%%players%&8/&7%max%" + - " " + - " &7Warte auf Spieler..." + - " " + - "&8&m━━━━━━━━━━━━━━━━━━━━━" + +# ======================================== +# INGAME SCOREBOARD (während des Spiels) +# ======================================== +InGameScoreBoardTitle: "&c&lIM &f&lSPIEL" + +InGameScoreBoard: + - "&8&m━━━━━━━━━━━━━━━━━━━━━" + - " " + - " &6⚔ &eArena&8: &f%arena%" + - " &c❤ &eÜbrig&8: %player_color%%players%" + - " " + - " &a✓ &7Überlebe so lange" + - " &7wie möglich!" + - " " + - " &e⚠ &7Pass auf die" + - " &7Blöcke auf!" + - " " + - "&8&m━━━━━━━━━━━━━━━━━━━━━" + +# ======================================== +# VERFÜGBARE PLATZHALTER +# ======================================== +# %arena% - Arena-Name +# %players% - Aktuelle Spielerzahl +# %max% - Maximale Spielerzahl +# %player% - Name des Spielers +# %status% - Status-Text (LOBBY/IM SPIEL) +# %status_color% - Farbe basierend auf Status +# %player_color% - Farbe basierend auf Füllstand (grün/gelb/rot) + +# ======================================== +# ALTERNATIVE DESIGNS (kopiere diese in die Scoreboard-Sektion) +# ======================================== + +# DESIGN 1: Minimalistisch +# LobbyScoreBoard: +# - "&7┌─────────────────┐" +# - " &6⚡ &e%arena%" +# - " &b🎮 %player_color%%players%&7/%max%" +# - "&7└─────────────────┘" + +# DESIGN 2: Retro Style +# LobbyScoreBoard: +# - "&8╔═══════════════════╗" +# - " " +# - " &e► Arena: &f%arena%" +# - " &e► Spieler: %player_color%%players%&7/%max%" +# - " " +# - "&8╚═══════════════════╝" + +# DESIGN 3: Modern Gradient +# LobbyScoreBoard: +# - "&6▬▬▬&e▬▬▬&f▬▬▬&e▬▬▬&6▬▬▬" +# - " " +# - " &6⚡ &eArena &f%arena%" +# - " &b👤 %player_color%%players%&8/&7%max% &eSpieler" +# - " " +# - " &7Bereit? Warte..." +# - " " +# - "&6▬▬▬&e▬▬▬&f▬▬▬&e▬▬▬&6▬▬▬" + +# DESIGN 4: Kompakt +# LobbyScoreBoard: +# - "&8━━━━━━━━━━━━━━━━" +# - "&6⚔ &e%arena%" +# - "&b👥 %player_color%%players%&7/%max%" +# - "&7Warte..." +# - "&8━━━━━━━━━━━━━━━━" + +# ======================================== +# FARB-CODES REFERENZ +# ======================================== +# &0 = Schwarz &8 = Dunkelgrau +# &1 = Dunkelblau &9 = Blau +# &2 = Dunkelgrün &a = Grün +# &3 = Türkis &b = Hellblau +# &4 = Dunkelrot &c = Rot +# &5 = Lila &d = Pink +# &6 = Gold &e = Gelb +# &7 = Grau &f = Weiß +# +# &l = Fett +# &m = Durchgestrichen +# &n = Unterstrichen +# &o = Kursiv +# &r = Reset \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index dad2995..ee0512b 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: VelocityFall -version: 1.0.0 +version: 1.0.1 main: de.velocityfall.VelocityFall api-version: 1.21 author: M_Viper