From 1a75a7db3391130529cc1797b76bb3ee039e6a51 Mon Sep 17 00:00:00 2001 From: Git Manager GUI Date: Sun, 31 May 2026 12:42:53 +0200 Subject: [PATCH] Upload folder via GUI - src --- src/main/java/de/nexuslobby/NexusLobby.java | 6 ++ .../commands/LobbyTabCompleter.java | 5 +- .../commands/NexusLobbyCommand.java | 21 +++++ .../armorstandtools/ConversationManager.java | 21 ++++- .../nexuslobby/modules/ball/SoccerModule.java | 67 +++++++++++++-- .../modules/gadgets/GadgetModule.java | 11 +++ .../modules/parkour/ParkourListener.java | 4 +- .../modules/parkour/ParkourManager.java | 82 +++++++++++++++++-- .../java/de/nexuslobby/utils/DoubleJump.java | 2 +- .../java/de/nexuslobby/utils/PlayerHider.java | 6 ++ .../de/nexuslobby/utils/VoidProtection.java | 18 +++- src/main/resources/plugin.yml | 2 +- 12 files changed, 223 insertions(+), 22 deletions(-) diff --git a/src/main/java/de/nexuslobby/NexusLobby.java b/src/main/java/de/nexuslobby/NexusLobby.java index e2c6f3c..b0ef8e3 100644 --- a/src/main/java/de/nexuslobby/NexusLobby.java +++ b/src/main/java/de/nexuslobby/NexusLobby.java @@ -529,6 +529,12 @@ public class NexusLobby extends JavaPlugin implements Listener { if (params.equalsIgnoreCase("parkour_top")) { return parkourManager != null ? parkourManager.getTopTen() : "N/A"; } + if (params.equalsIgnoreCase("parkour_top_1")) { + return parkourManager != null ? parkourManager.getTopTen(1) : "N/A"; + } + if (params.equalsIgnoreCase("parkour_top_2")) { + return parkourManager != null ? parkourManager.getTopTen(2) : "N/A"; + } return null; } } diff --git a/src/main/java/de/nexuslobby/commands/LobbyTabCompleter.java b/src/main/java/de/nexuslobby/commands/LobbyTabCompleter.java index c175a61..0fe0e8e 100644 --- a/src/main/java/de/nexuslobby/commands/LobbyTabCompleter.java +++ b/src/main/java/de/nexuslobby/commands/LobbyTabCompleter.java @@ -53,7 +53,7 @@ public class LobbyTabCompleter implements TabCompleter { if (sender.hasPermission("nexuslobby.admin")) { suggestions.addAll(Arrays.asList( "setstart", "setfinish", "setcheckpoint", - "reset", "clear", "removeall", "info" + "holo", "reset", "clear", "removeall", "info" )); } else { // Normale Spieler können nur ihren Run abbrechen @@ -77,7 +77,8 @@ public class LobbyTabCompleter implements TabCompleter { String parkSub = args[1].toLowerCase(); if (parkSub.equals("setstart") || parkSub.equals("setfinish") - || parkSub.equals("setcheckpoint")) { + || parkSub.equals("setcheckpoint") + || parkSub.equals("holo")) { suggestions.addAll(Arrays.asList("1", "2")); } } diff --git a/src/main/java/de/nexuslobby/commands/NexusLobbyCommand.java b/src/main/java/de/nexuslobby/commands/NexusLobbyCommand.java index 6856c8c..4f3479a 100644 --- a/src/main/java/de/nexuslobby/commands/NexusLobbyCommand.java +++ b/src/main/java/de/nexuslobby/commands/NexusLobbyCommand.java @@ -2,6 +2,7 @@ package de.nexuslobby.commands; import de.nexuslobby.NexusLobby; import de.nexuslobby.modules.ScoreboardModule; +import de.nexuslobby.modules.hologram.HologramModule; import de.nexuslobby.modules.parkour.ParkourManager; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -206,6 +207,25 @@ public class NexusLobbyCommand implements CommandExecutor { player.sendMessage(pm.getTrackInfo()); break; + // /nexus parkour holo <1|2> – Leaderboard-Hologramm an aktueller Position setzen + case "holo": { + int track = parseTrack(args, 2, player); + if (track == -1) return; + HologramModule hm = NexusLobby.getInstance().getHologramModule(); + if (hm == null) { + player.sendMessage("§cHologram-Modul nicht verfügbar!"); + return; + } + String holoId = "parkour_top_" + track; + List pages = new java.util.ArrayList<>(); + pages.add("%nexuslobby_parkour_top_" + track + "%"); + hm.createHologram(holoId, player.getLocation(), pages); + player.sendMessage("§8[§6Parkour§8] §aLeaderboard-Hologramm für §eStrecke " + track + + " §agesetzt! §8(ID: §7" + holoId + "§8)"); + player.sendMessage("§8[§6Parkour§8] §7Das Hologramm aktualisiert sich automatisch via PlaceholderAPI."); + break; + } + default: player.sendMessage(de.nexuslobby.utils.LangManager.get("unknown_subcommand")); break; @@ -318,6 +338,7 @@ public class NexusLobbyCommand implements CommandExecutor { player.sendMessage("§e/nexus parkour setstart <1|2> §7- Start setzen"); player.sendMessage("§e/nexus parkour setcheckpoint <1|2> §7- Checkpoint setzen"); player.sendMessage("§e/nexus parkour setfinish <1|2> §7- Ziel setzen"); + player.sendMessage("§e/nexus parkour holo <1|2> §7- Top-10 Hologramm setzen"); player.sendMessage("§e/nexus parkour reset §7- Eigenen Run abbrechen"); player.sendMessage("§e/nexus parkour clear §7- §cTop-10 Liste löschen"); player.sendMessage(de.nexuslobby.utils.LangManager.get("info_removeall")); diff --git a/src/main/java/de/nexuslobby/modules/armorstandtools/ConversationManager.java b/src/main/java/de/nexuslobby/modules/armorstandtools/ConversationManager.java index 870305a..7fd9f45 100644 --- a/src/main/java/de/nexuslobby/modules/armorstandtools/ConversationManager.java +++ b/src/main/java/de/nexuslobby/modules/armorstandtools/ConversationManager.java @@ -23,6 +23,8 @@ public class ConversationManager { // Verhindert Mehrfach-Gespräche und regelt die 60s Pause private final Set activeSpeakers = new HashSet<>(); + // Alle aktiven Bubble-Entities tracken – für zuverlässiges Cleanup bei Reload/Crash + private final Set activeBubbles = new HashSet<>(); public ConversationManager(NexusLobby plugin) { this.plugin = plugin; @@ -50,6 +52,14 @@ public class ConversationManager { * zu breit und hätte auch legitime Marker-ArmorStands anderer Systeme gelöscht. */ public void clearHangingBubbles() { + // Zuerst: Alle getrackte Bubble-Entities direkt entfernen (schnell, kein World-Scan nötig) + for (UUID uuid : new HashSet<>(activeBubbles)) { + Entity e = Bukkit.getEntity(uuid); + if (e != null) e.remove(); + } + activeBubbles.clear(); + + // Danach: Vollständiger World-Scan als Fallback (fängt Bubbles nach Crash/Reload ab) int count = 0; for (World world : Bukkit.getWorlds()) { for (ArmorStand as : world.getEntitiesByClass(ArmorStand.class)) { @@ -228,10 +238,15 @@ public class ConversationManager { s.setCustomName(ChatColorTranslate(text)); s.setCustomNameVisible(true); s.setInvulnerable(true); + s.setPersistent(false); // Überlebt keinen Server-Neustart/-Absturz s.addScoreboardTag("nexus_bubble"); }); - Bukkit.getScheduler().runTaskLater(plugin, bubble::remove, 80L); // 4s Sichtbarkeit + activeBubbles.add(bubble.getUniqueId()); + Bukkit.getScheduler().runTaskLater(plugin, () -> { + bubble.remove(); + activeBubbles.remove(bubble.getUniqueId()); + }, 80L); // 4s Sichtbarkeit } // --- Legacy Support & Config Methoden --- @@ -275,8 +290,8 @@ public class ConversationManager { } public void reload() { - setupFile(); + clearHangingBubbles(); // Erst aufräumen, dann neu laden activeSpeakers.clear(); - clearHangingBubbles(); + setupFile(); } } \ No newline at end of file diff --git a/src/main/java/de/nexuslobby/modules/ball/SoccerModule.java b/src/main/java/de/nexuslobby/modules/ball/SoccerModule.java index ba09f54..9850a17 100644 --- a/src/main/java/de/nexuslobby/modules/ball/SoccerModule.java +++ b/src/main/java/de/nexuslobby/modules/ball/SoccerModule.java @@ -17,6 +17,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.SkullMeta; import org.bukkit.profile.PlayerProfile; @@ -47,6 +48,9 @@ public class SoccerModule implements Module, Listener, CommandExecutor { private static final double VOID_THRESHOLD = -5.0; private static final double CLEANUP_RADIUS = 5.0; + // Verhindert dass Dribbling einen Kick sofort überschreibt. + private static final long KICK_DRIBBLE_LOCK_MS = 300L; + private static final double PARTICLE_SPEED_HIGH = 0.85; private static final double PARTICLE_SPEED_MEDIUM = 0.45; private static final double PARTICLE_SPEED_MIN = 0.05; @@ -70,6 +74,7 @@ public class SoccerModule implements Module, Listener, CommandExecutor { private Location spawnLocation; private long lastMoveTime; private long lastGoalTime = 0L; + private long lastKickTime = 0L; // ── Tor-Erkennung: Position aus dem VORHERIGEN Tick ─────────────────────── // @@ -134,12 +139,11 @@ public class SoccerModule implements Module, Listener, CommandExecutor { handleDribbling(); // ── SCHRITT 3: Nach Physik die neue Position lesen ───────────────── - // Minecraft hat jetzt die Position um vel verschoben. - // Da getLocation() einen Tick verzögert ist, lesen wir hier noch - // currentPos – aber über den Velocity-Vektor wissen wir die neue Pos. - // Wir nutzen deshalb: newPos = currentPos + velocity (vor Drag) - // Das ist die tatsächliche neue Ballposition nach diesem Tick. - Location newPos = currentPos.clone().add(vel); + // Minecraft wendet nach setVelocity noch Drag (0.98 horizontal, Gravity ~0.08 + // + 0.98 vertikal) an. Wir approximieren: newPos = currentPos + vel * 0.98 + // Das ist deutlich genauer als currentPos + vel und vermeidet 1-Tick-Versatz + // bei der Tor-Erkennung nach schnellen Kicks. + Location newPos = currentPos.clone().add(vel.clone().multiply(0.98)); // ── SCHRITT 4: Segment prüfen ───────────────────────────────────── // Das Segment prevPos → newPos deckt den vollständigen Weg ab den @@ -277,6 +281,7 @@ public class SoccerModule implements Module, Listener, CommandExecutor { private void handleDribbling() { if (ball == null || !ball.isValid()) return; + if (System.currentTimeMillis() - lastKickTime < KICK_DRIBBLE_LOCK_MS) return; for (Entity nearby : ball.getNearbyEntities(DRIBBLE_DETECTION_RADIUS, DRIBBLE_HEIGHT, DRIBBLE_DETECTION_RADIUS)) { if (nearby instanceof Player player) { Vector dir = ball.getLocation().toVector().subtract(player.getLocation().toVector()); @@ -290,8 +295,16 @@ public class SoccerModule implements Module, Listener, CommandExecutor { } private void handleWallBounce(Vector vel) { - if (vel.lengthSquared() < 0.001) return; + // Nur prüfen wenn genug horizontale Bewegung vorhanden ist. + // Y-Komponente absichtlich ignoriert – der Bounce gilt nur für Wände (X/Z), + // nicht für Boden/Decke. So verhindert man dass liegende Boden-Blöcke + // fälschlicherweise als Wand erkannt werden und den Ball dauerhaft blockieren. + double hSpeed = vel.getX() * vel.getX() + vel.getZ() * vel.getZ(); + if (hSpeed < 0.001) return; + Location loc = ball.getLocation(); + // Y + 0.5 anheben damit wir auf Wand-Höhe prüfen, nicht auf Boden-Ebene. + // Ohne den Offset würde loc.getY() (Boden des ArmorStands) den Bodenblock selbst treffen. Block nx = loc.clone().add(vel.getX() * WALL_CHECK_DISTANCE, 0.5, 0).getBlock(); Block nz = loc.clone().add(0, 0.5, vel.getZ() * WALL_CHECK_DISTANCE).getBlock(); boolean bounced = false; @@ -531,6 +544,7 @@ public class SoccerModule implements Module, Listener, CommandExecutor { // prevPos auf aktuelle Position setzen bevor der Ball sich bewegt – // so startet das nächste Segment am richtigen Punkt. prevPos = ball.getLocation().clone(); + lastKickTime = System.currentTimeMillis(); ball.setVelocity(dir); ball.getWorld().playSound(ball.getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, 0.6f, 1.5f); @@ -542,6 +556,45 @@ public class SoccerModule implements Module, Listener, CommandExecutor { if (ball != null && event.getRightClicked().equals(ball)) event.setCancelled(true); } + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + // Wenn ein Spieler joinт: Ball-State prüfen und ggf. resynchronisieren. + // Passiert z.B. nach Server-Wechsel via BungeeCord: der Chunk wird neu geladen, + // alte Ball-Entities tauchen wieder auf, die interne Referenz zeigt evtl. auf + // die falsche oder eine ungültige Instanz. + Bukkit.getScheduler().runTaskLater(NexusLobby.getInstance(), () -> { + if (spawnLocation == null || spawnLocation.getWorld() == null) return; + + // Alle alten nexusball-Entities in der Welt suchen + boolean foundValid = false; + for (Entity e : spawnLocation.getWorld().getEntities()) { + if (!(e instanceof ArmorStand as)) continue; + if (!isBallEntity(as)) continue; + + if (ball != null && ball.isValid() && as.getUniqueId().equals(ball.getUniqueId())) { + // Referenz ist noch korrekt und gültig – alles gut + foundValid = true; + } else if (ball == null || !ball.isValid()) { + // Unsere Referenz ist verloren – die gefundene Entity übernehmen + ball = as; + prevPos = as.getLocation().clone(); + lastMoveTime = System.currentTimeMillis(); + foundValid = true; + Bukkit.getLogger().info("[Soccer] Ball-Referenz nach Join wiederhergestellt."); + } else { + // Duplikat – entfernen + as.remove(); + } + } + + if (!foundValid) { + // Kein Ball in der Welt – neu spawnen + respawnBall(); + Bukkit.getLogger().info("[Soccer] Ball nach Join neu gespawnt."); + } + }, 20L); // 1s warten damit der Chunk sicher geladen ist + } + // ========================================================================= // COMMANDS // ========================================================================= diff --git a/src/main/java/de/nexuslobby/modules/gadgets/GadgetModule.java b/src/main/java/de/nexuslobby/modules/gadgets/GadgetModule.java index a720419..63476d3 100644 --- a/src/main/java/de/nexuslobby/modules/gadgets/GadgetModule.java +++ b/src/main/java/de/nexuslobby/modules/gadgets/GadgetModule.java @@ -31,6 +31,7 @@ import org.bukkit.event.player.PlayerFishEvent; import org.bukkit.event.player.PlayerInteractAtEntityEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerUnleashEntityEvent; import org.bukkit.event.player.PlayerFishEvent.State; import org.bukkit.inventory.Inventory; @@ -764,4 +765,14 @@ public class GadgetModule implements Module, Listener { this.activeShields.clear(); GadgetShield.clear(); } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + Player player = event.getPlayer(); + removeGadgets(player); + UUID uuid = player.getUniqueId(); + meteorCooldowns.remove(uuid); + freezeCooldowns.remove(uuid); + grapplingCooldowns.remove(uuid); + } } \ No newline at end of file diff --git a/src/main/java/de/nexuslobby/modules/parkour/ParkourListener.java b/src/main/java/de/nexuslobby/modules/parkour/ParkourListener.java index 818a6df..cafe5cd 100644 --- a/src/main/java/de/nexuslobby/modules/parkour/ParkourListener.java +++ b/src/main/java/de/nexuslobby/modules/parkour/ParkourListener.java @@ -65,7 +65,9 @@ public class ParkourListener implements Listener { Location loc = player.getLocation(); // --- 1. ABSTURZ-CHECK --- - if (loc.getY() < 50) { + // Absturz-Y aus config lesen (Standard: 50), damit Maps unter Y=50 korrekt funktionieren + double fallY = NexusLobby.getInstance().getConfig().getDouble("parkour.fall_y", 50.0); + if (loc.getY() < fallY) { Location lastCp = manager.getCheckpoint(player); if (lastCp != null) { player.teleport(lastCp); diff --git a/src/main/java/de/nexuslobby/modules/parkour/ParkourManager.java b/src/main/java/de/nexuslobby/modules/parkour/ParkourManager.java index 40a0968..e3584fe 100644 --- a/src/main/java/de/nexuslobby/modules/parkour/ParkourManager.java +++ b/src/main/java/de/nexuslobby/modules/parkour/ParkourManager.java @@ -63,6 +63,7 @@ public class ParkourManager { this.plugin = plugin; this.file = new File(plugin.getDataFolder(), "parkour.yml"); loadConfig(); + migrateLegacyBestTimes(); startParticleTask(); } @@ -82,6 +83,44 @@ public class ParkourManager { config = YamlConfiguration.loadConfiguration(file); } + /** + * Migriert Bestzeiten vom alten Format (besttimes.) ins neue Format + * (besttimes..). Alte Zeiten werden Track 1 zugeordnet, da vor + * dieser Änderung nur ein Track existierte. + * Läuft einmalig beim Start; tut nichts wenn keine alten Daten vorhanden sind. + */ + private void migrateLegacyBestTimes() { + if (!config.contains("besttimes")) return; + + // Prüfen ob es direkte UUID-Keys unter besttimes gibt (altes Format) + // Im neuen Format wären hier nur "1" und "2" als Keys + var section = config.getConfigurationSection("besttimes"); + if (section == null) return; + + int migrated = 0; + for (String key : new java.util.HashSet<>(section.getKeys(false))) { + // Neue Keys sind "1" oder "2" – alte Keys sind UUID-Strings (36 Zeichen, enthalten "-") + if (!key.equals("1") && !key.equals("2") && key.contains("-")) { + double time = config.getDouble("besttimes." + key); + // Nur migrieren wenn unter besttimes.1. noch kein besserer Wert steht + String newPath = "besttimes.1." + key; + double existingTime = config.getDouble(newPath, 99999.9); + if (time < existingTime) { + config.set(newPath, time); + } + // Alten Key entfernen + config.set("besttimes." + key, null); + migrated++; + } + } + + if (migrated > 0) { + save(); + plugin.getLogger().info("[Parkour] Migration: " + migrated + + " Bestzeit(en) von altem Format nach Strecke 1 übertragen."); + } + } + private void save() { try { config.save(file); @@ -325,7 +364,8 @@ public class ParkourManager { // ───────────────────────────────────────────────────────────────────────── private void saveBestTime(Player player, double time) { - String path = "besttimes." + player.getUniqueId(); + int track = activeTrack.getOrDefault(player.getUniqueId(), 1); + String path = "besttimes." + track + "." + player.getUniqueId(); double currentTime = config.getDouble(path, 99999.9); if (time < currentTime) { config.set(path, time); @@ -346,21 +386,51 @@ public class ParkourManager { save(); } + public void clearStats(int track) { + config.set("besttimes." + track, null); + save(); + } + public String getTopTen() { - if (!config.contains("besttimes") || config.getConfigurationSection("besttimes") == null) - return "§6§l🏆 TOP 10 PARKOUR 🏆\n§7Noch keine Rekorde."; + // Fallback: kombiniert beide Tracks (Legacy-Support für %nexuslobby_parkour_top%) + return getTopTen(0); + } + + public String getTopTen(int track) { + String trackLabel = track == 1 ? " §8(Strecke 1)" : track == 2 ? " §8(Strecke 2)" : ""; Map allTimes = new HashMap<>(); - for (String uuidStr : config.getConfigurationSection("besttimes").getKeys(false)) { - allTimes.put(uuidStr, config.getDouble("besttimes." + uuidStr)); + + if (track == 0) { + // Alle Tracks kombiniert (nimmt die beste Zeit je Spieler über alle Tracks) + for (int t = 1; t <= 2; t++) { + String section = "besttimes." + t; + if (config.contains(section) && config.getConfigurationSection(section) != null) { + for (String uuidStr : config.getConfigurationSection(section).getKeys(false)) { + double time = config.getDouble(section + "." + uuidStr); + allTimes.merge(uuidStr, time, Math::min); + } + } + } + } else { + String section = "besttimes." + track; + if (!config.contains(section) || config.getConfigurationSection(section) == null) { + return "§6§l🏆 TOP 10 PARKOUR" + trackLabel + "\n§7Noch keine Rekorde."; + } + for (String uuidStr : config.getConfigurationSection(section).getKeys(false)) { + allTimes.put(uuidStr, config.getDouble(section + "." + uuidStr)); + } } + if (allTimes.isEmpty()) + return "§6§l🏆 TOP 10 PARKOUR" + trackLabel + "\n§7Noch keine Rekorde."; + List> sortedList = allTimes.entrySet().stream() .sorted(Map.Entry.comparingByValue()) .limit(10) .toList(); - StringBuilder builder = new StringBuilder("§6§l🏆 TOP 10 PARKOUR 🏆"); + StringBuilder builder = new StringBuilder("§6§l🏆 TOP 10 PARKOUR" + trackLabel); int rank = 1; for (Map.Entry entry : sortedList) { String name = config.getString("names." + entry.getKey(), "Unbekannt"); diff --git a/src/main/java/de/nexuslobby/utils/DoubleJump.java b/src/main/java/de/nexuslobby/utils/DoubleJump.java index c1abce8..e6c7f7f 100644 --- a/src/main/java/de/nexuslobby/utils/DoubleJump.java +++ b/src/main/java/de/nexuslobby/utils/DoubleJump.java @@ -41,7 +41,7 @@ public class DoubleJump implements Listener { @EventHandler public void onMove(PlayerMoveEvent event) { Player player = event.getPlayer(); - if (player.getGameMode() != GameMode.CREATIVE && player.getLocation().subtract(0, 0.1, 0).getBlock().getType() != Material.AIR) { + if (player.getGameMode() != GameMode.CREATIVE && player.getGameMode() != GameMode.SPECTATOR && player.getLocation().subtract(0, 0.1, 0).getBlock().getType() != Material.AIR) { if (NexusLobby.getInstance().getConfig().getBoolean("doublejump.enabled", true)) { player.setAllowFlight(true); } diff --git a/src/main/java/de/nexuslobby/utils/PlayerHider.java b/src/main/java/de/nexuslobby/utils/PlayerHider.java index 184418d..af55296 100644 --- a/src/main/java/de/nexuslobby/utils/PlayerHider.java +++ b/src/main/java/de/nexuslobby/utils/PlayerHider.java @@ -9,6 +9,7 @@ import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; @@ -84,4 +85,9 @@ public class PlayerHider implements Listener { private String colorize(String s) { return s == null ? "" : s.replace("&", "§"); } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + hidden.remove(event.getPlayer().getUniqueId()); + } } \ No newline at end of file diff --git a/src/main/java/de/nexuslobby/utils/VoidProtection.java b/src/main/java/de/nexuslobby/utils/VoidProtection.java index 00bdb5f..d4b4329 100644 --- a/src/main/java/de/nexuslobby/utils/VoidProtection.java +++ b/src/main/java/de/nexuslobby/utils/VoidProtection.java @@ -3,6 +3,8 @@ package de.nexuslobby.utils; import de.nexuslobby.NexusLobby; import org.bukkit.Bukkit; import org.bukkit.Location; +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; @@ -17,7 +19,21 @@ public class VoidProtection implements Listener { Player player = event.getPlayer(); if (player.getLocation().getY() < 0) { if (NexusLobby.getInstance().getConfig().getBoolean("void_protection.teleport_to_spawn", true)) { - Location spawn = player.getWorld().getSpawnLocation(); + FileConfiguration config = NexusLobby.getInstance().getConfig(); + Location spawn; + if (config.contains("spawn.world")) { + String worldName = config.getString("spawn.world"); + World world = Bukkit.getWorld(worldName); + if (world != null) { + spawn = new Location(world, + config.getDouble("spawn.x"), config.getDouble("spawn.y"), config.getDouble("spawn.z"), + (float) config.getDouble("spawn.yaw"), (float) config.getDouble("spawn.pitch")); + } else { + spawn = player.getWorld().getSpawnLocation(); + } + } else { + spawn = player.getWorld().getSpawnLocation(); + } player.teleport(spawn); String msg = de.nexuslobby.utils.LangManager.get("void_protection_message"); if (msg != null && !msg.isEmpty()) { diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index b97c225..28e11fe 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,6 +1,6 @@ name: NexusLobby main: de.nexuslobby.NexusLobby -version: "1.8" +version: "1.9" api-version: "1.21" author: M_Viper description: Modular Lobby Plugin with an invisible Particle-Parkour system.