Upload folder via GUI - src

This commit is contained in:
Git Manager GUI
2026-05-31 12:42:53 +02:00
parent be78c34222
commit 1a75a7db33
12 changed files with 223 additions and 22 deletions

View File

@@ -529,6 +529,12 @@ public class NexusLobby extends JavaPlugin implements Listener {
if (params.equalsIgnoreCase("parkour_top")) { if (params.equalsIgnoreCase("parkour_top")) {
return parkourManager != null ? parkourManager.getTopTen() : "N/A"; 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; return null;
} }
} }

View File

@@ -53,7 +53,7 @@ public class LobbyTabCompleter implements TabCompleter {
if (sender.hasPermission("nexuslobby.admin")) { if (sender.hasPermission("nexuslobby.admin")) {
suggestions.addAll(Arrays.asList( suggestions.addAll(Arrays.asList(
"setstart", "setfinish", "setcheckpoint", "setstart", "setfinish", "setcheckpoint",
"reset", "clear", "removeall", "info" "holo", "reset", "clear", "removeall", "info"
)); ));
} else { } else {
// Normale Spieler können nur ihren Run abbrechen // Normale Spieler können nur ihren Run abbrechen
@@ -77,7 +77,8 @@ public class LobbyTabCompleter implements TabCompleter {
String parkSub = args[1].toLowerCase(); String parkSub = args[1].toLowerCase();
if (parkSub.equals("setstart") if (parkSub.equals("setstart")
|| parkSub.equals("setfinish") || parkSub.equals("setfinish")
|| parkSub.equals("setcheckpoint")) { || parkSub.equals("setcheckpoint")
|| parkSub.equals("holo")) {
suggestions.addAll(Arrays.asList("1", "2")); suggestions.addAll(Arrays.asList("1", "2"));
} }
} }

View File

@@ -2,6 +2,7 @@ package de.nexuslobby.commands;
import de.nexuslobby.NexusLobby; import de.nexuslobby.NexusLobby;
import de.nexuslobby.modules.ScoreboardModule; import de.nexuslobby.modules.ScoreboardModule;
import de.nexuslobby.modules.hologram.HologramModule;
import de.nexuslobby.modules.parkour.ParkourManager; import de.nexuslobby.modules.parkour.ParkourManager;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
@@ -206,6 +207,25 @@ public class NexusLobbyCommand implements CommandExecutor {
player.sendMessage(pm.getTrackInfo()); player.sendMessage(pm.getTrackInfo());
break; 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<String> 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: default:
player.sendMessage(de.nexuslobby.utils.LangManager.get("unknown_subcommand")); player.sendMessage(de.nexuslobby.utils.LangManager.get("unknown_subcommand"));
break; 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 setstart <1|2> §7- Start setzen");
player.sendMessage("§e/nexus parkour setcheckpoint <1|2> §7- Checkpoint 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 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 reset §7- Eigenen Run abbrechen");
player.sendMessage("§e/nexus parkour clear §7- §cTop-10 Liste löschen"); player.sendMessage("§e/nexus parkour clear §7- §cTop-10 Liste löschen");
player.sendMessage(de.nexuslobby.utils.LangManager.get("info_removeall")); player.sendMessage(de.nexuslobby.utils.LangManager.get("info_removeall"));

View File

@@ -23,6 +23,8 @@ public class ConversationManager {
// Verhindert Mehrfach-Gespräche und regelt die 60s Pause // Verhindert Mehrfach-Gespräche und regelt die 60s Pause
private final Set<UUID> activeSpeakers = new HashSet<>(); private final Set<UUID> activeSpeakers = new HashSet<>();
// Alle aktiven Bubble-Entities tracken für zuverlässiges Cleanup bei Reload/Crash
private final Set<UUID> activeBubbles = new HashSet<>();
public ConversationManager(NexusLobby plugin) { public ConversationManager(NexusLobby plugin) {
this.plugin = plugin; this.plugin = plugin;
@@ -50,6 +52,14 @@ public class ConversationManager {
* zu breit und hätte auch legitime Marker-ArmorStands anderer Systeme gelöscht. * zu breit und hätte auch legitime Marker-ArmorStands anderer Systeme gelöscht.
*/ */
public void clearHangingBubbles() { 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; int count = 0;
for (World world : Bukkit.getWorlds()) { for (World world : Bukkit.getWorlds()) {
for (ArmorStand as : world.getEntitiesByClass(ArmorStand.class)) { for (ArmorStand as : world.getEntitiesByClass(ArmorStand.class)) {
@@ -228,10 +238,15 @@ public class ConversationManager {
s.setCustomName(ChatColorTranslate(text)); s.setCustomName(ChatColorTranslate(text));
s.setCustomNameVisible(true); s.setCustomNameVisible(true);
s.setInvulnerable(true); s.setInvulnerable(true);
s.setPersistent(false); // Überlebt keinen Server-Neustart/-Absturz
s.addScoreboardTag("nexus_bubble"); 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 --- // --- Legacy Support & Config Methoden ---
@@ -275,8 +290,8 @@ public class ConversationManager {
} }
public void reload() { public void reload() {
setupFile(); clearHangingBubbles(); // Erst aufräumen, dann neu laden
activeSpeakers.clear(); activeSpeakers.clear();
clearHangingBubbles(); setupFile();
} }
} }

View File

@@ -17,6 +17,7 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.player.PlayerInteractAtEntityEvent; import org.bukkit.event.player.PlayerInteractAtEntityEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta; import org.bukkit.inventory.meta.SkullMeta;
import org.bukkit.profile.PlayerProfile; 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 VOID_THRESHOLD = -5.0;
private static final double CLEANUP_RADIUS = 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_HIGH = 0.85;
private static final double PARTICLE_SPEED_MEDIUM = 0.45; private static final double PARTICLE_SPEED_MEDIUM = 0.45;
private static final double PARTICLE_SPEED_MIN = 0.05; private static final double PARTICLE_SPEED_MIN = 0.05;
@@ -70,6 +74,7 @@ public class SoccerModule implements Module, Listener, CommandExecutor {
private Location spawnLocation; private Location spawnLocation;
private long lastMoveTime; private long lastMoveTime;
private long lastGoalTime = 0L; private long lastGoalTime = 0L;
private long lastKickTime = 0L;
// ── Tor-Erkennung: Position aus dem VORHERIGEN Tick ─────────────────────── // ── Tor-Erkennung: Position aus dem VORHERIGEN Tick ───────────────────────
// //
@@ -134,12 +139,11 @@ public class SoccerModule implements Module, Listener, CommandExecutor {
handleDribbling(); handleDribbling();
// ── SCHRITT 3: Nach Physik die neue Position lesen ───────────────── // ── SCHRITT 3: Nach Physik die neue Position lesen ─────────────────
// Minecraft hat jetzt die Position um vel verschoben. // Minecraft wendet nach setVelocity noch Drag (0.98 horizontal, Gravity ~0.08
// Da getLocation() einen Tick verzögert ist, lesen wir hier noch // + 0.98 vertikal) an. Wir approximieren: newPos = currentPos + vel * 0.98
// currentPos aber über den Velocity-Vektor wissen wir die neue Pos. // Das ist deutlich genauer als currentPos + vel und vermeidet 1-Tick-Versatz
// Wir nutzen deshalb: newPos = currentPos + velocity (vor Drag) // bei der Tor-Erkennung nach schnellen Kicks.
// Das ist die tatsächliche neue Ballposition nach diesem Tick. Location newPos = currentPos.clone().add(vel.clone().multiply(0.98));
Location newPos = currentPos.clone().add(vel);
// ── SCHRITT 4: Segment prüfen ───────────────────────────────────── // ── SCHRITT 4: Segment prüfen ─────────────────────────────────────
// Das Segment prevPos → newPos deckt den vollständigen Weg ab den // 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() { private void handleDribbling() {
if (ball == null || !ball.isValid()) return; 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)) { for (Entity nearby : ball.getNearbyEntities(DRIBBLE_DETECTION_RADIUS, DRIBBLE_HEIGHT, DRIBBLE_DETECTION_RADIUS)) {
if (nearby instanceof Player player) { if (nearby instanceof Player player) {
Vector dir = ball.getLocation().toVector().subtract(player.getLocation().toVector()); 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) { 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(); 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 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(); Block nz = loc.clone().add(0, 0.5, vel.getZ() * WALL_CHECK_DISTANCE).getBlock();
boolean bounced = false; boolean bounced = false;
@@ -531,6 +544,7 @@ public class SoccerModule implements Module, Listener, CommandExecutor {
// prevPos auf aktuelle Position setzen bevor der Ball sich bewegt // prevPos auf aktuelle Position setzen bevor der Ball sich bewegt
// so startet das nächste Segment am richtigen Punkt. // so startet das nächste Segment am richtigen Punkt.
prevPos = ball.getLocation().clone(); prevPos = ball.getLocation().clone();
lastKickTime = System.currentTimeMillis();
ball.setVelocity(dir); ball.setVelocity(dir);
ball.getWorld().playSound(ball.getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, 0.6f, 1.5f); 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); 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 // COMMANDS
// ========================================================================= // =========================================================================

View File

@@ -31,6 +31,7 @@ import org.bukkit.event.player.PlayerFishEvent;
import org.bukkit.event.player.PlayerInteractAtEntityEvent; import org.bukkit.event.player.PlayerInteractAtEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerUnleashEntityEvent; import org.bukkit.event.player.PlayerUnleashEntityEvent;
import org.bukkit.event.player.PlayerFishEvent.State; import org.bukkit.event.player.PlayerFishEvent.State;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
@@ -764,4 +765,14 @@ public class GadgetModule implements Module, Listener {
this.activeShields.clear(); this.activeShields.clear();
GadgetShield.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);
}
} }

View File

@@ -65,7 +65,9 @@ public class ParkourListener implements Listener {
Location loc = player.getLocation(); Location loc = player.getLocation();
// --- 1. ABSTURZ-CHECK --- // --- 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); Location lastCp = manager.getCheckpoint(player);
if (lastCp != null) { if (lastCp != null) {
player.teleport(lastCp); player.teleport(lastCp);

View File

@@ -63,6 +63,7 @@ public class ParkourManager {
this.plugin = plugin; this.plugin = plugin;
this.file = new File(plugin.getDataFolder(), "parkour.yml"); this.file = new File(plugin.getDataFolder(), "parkour.yml");
loadConfig(); loadConfig();
migrateLegacyBestTimes();
startParticleTask(); startParticleTask();
} }
@@ -82,6 +83,44 @@ public class ParkourManager {
config = YamlConfiguration.loadConfiguration(file); config = YamlConfiguration.loadConfiguration(file);
} }
/**
* Migriert Bestzeiten vom alten Format (besttimes.<uuid>) ins neue Format
* (besttimes.<track>.<uuid>). 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.<uuid> 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() { private void save() {
try { try {
config.save(file); config.save(file);
@@ -325,7 +364,8 @@ public class ParkourManager {
// ───────────────────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────────────────
private void saveBestTime(Player player, double time) { 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); double currentTime = config.getDouble(path, 99999.9);
if (time < currentTime) { if (time < currentTime) {
config.set(path, time); config.set(path, time);
@@ -346,21 +386,51 @@ public class ParkourManager {
save(); save();
} }
public void clearStats(int track) {
config.set("besttimes." + track, null);
save();
}
public String getTopTen() { public String getTopTen() {
if (!config.contains("besttimes") || config.getConfigurationSection("besttimes") == null) // Fallback: kombiniert beide Tracks (Legacy-Support für %nexuslobby_parkour_top%)
return "§6§l🏆 TOP 10 PARKOUR 🏆\n§7Noch keine Rekorde."; return getTopTen(0);
}
public String getTopTen(int track) {
String trackLabel = track == 1 ? " §8(Strecke 1)" : track == 2 ? " §8(Strecke 2)" : "";
Map<String, Double> allTimes = new HashMap<>(); Map<String, Double> 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<Map.Entry<String, Double>> sortedList = allTimes.entrySet().stream() List<Map.Entry<String, Double>> sortedList = allTimes.entrySet().stream()
.sorted(Map.Entry.comparingByValue()) .sorted(Map.Entry.comparingByValue())
.limit(10) .limit(10)
.toList(); .toList();
StringBuilder builder = new StringBuilder("§6§l🏆 TOP 10 PARKOUR 🏆"); StringBuilder builder = new StringBuilder("§6§l🏆 TOP 10 PARKOUR" + trackLabel);
int rank = 1; int rank = 1;
for (Map.Entry<String, Double> entry : sortedList) { for (Map.Entry<String, Double> entry : sortedList) {
String name = config.getString("names." + entry.getKey(), "Unbekannt"); String name = config.getString("names." + entry.getKey(), "Unbekannt");

View File

@@ -41,7 +41,7 @@ public class DoubleJump implements Listener {
@EventHandler @EventHandler
public void onMove(PlayerMoveEvent event) { public void onMove(PlayerMoveEvent event) {
Player player = event.getPlayer(); 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)) { if (NexusLobby.getInstance().getConfig().getBoolean("doublejump.enabled", true)) {
player.setAllowFlight(true); player.setAllowFlight(true);
} }

View File

@@ -9,6 +9,7 @@ import org.bukkit.event.Listener;
import org.bukkit.event.block.Action; import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
@@ -84,4 +85,9 @@ public class PlayerHider implements Listener {
private String colorize(String s) { private String colorize(String s) {
return s == null ? "" : s.replace("&", "§"); return s == null ? "" : s.replace("&", "§");
} }
@EventHandler
public void onQuit(PlayerQuitEvent event) {
hidden.remove(event.getPlayer().getUniqueId());
}
} }

View File

@@ -3,6 +3,8 @@ package de.nexuslobby.utils;
import de.nexuslobby.NexusLobby; import de.nexuslobby.NexusLobby;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
@@ -17,7 +19,21 @@ public class VoidProtection implements Listener {
Player player = event.getPlayer(); Player player = event.getPlayer();
if (player.getLocation().getY() < 0) { if (player.getLocation().getY() < 0) {
if (NexusLobby.getInstance().getConfig().getBoolean("void_protection.teleport_to_spawn", true)) { 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); player.teleport(spawn);
String msg = de.nexuslobby.utils.LangManager.get("void_protection_message"); String msg = de.nexuslobby.utils.LangManager.get("void_protection_message");
if (msg != null && !msg.isEmpty()) { if (msg != null && !msg.isEmpty()) {

View File

@@ -1,6 +1,6 @@
name: NexusLobby name: NexusLobby
main: de.nexuslobby.NexusLobby main: de.nexuslobby.NexusLobby
version: "1.8" version: "1.9"
api-version: "1.21" api-version: "1.21"
author: M_Viper author: M_Viper
description: Modular Lobby Plugin with an invisible Particle-Parkour system. description: Modular Lobby Plugin with an invisible Particle-Parkour system.