Upload folder via GUI - src
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"));
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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()) {
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user