Update from Git Manager GUI

This commit is contained in:
2026-01-22 23:16:25 +01:00
parent 233639dd52
commit 9fd0690ba8
11 changed files with 709 additions and 84 deletions

View File

@@ -14,6 +14,7 @@ import de.nexuslobby.modules.portal.PortalManager;
import de.nexuslobby.modules.portal.PortalCommand;
import de.nexuslobby.modules.servers.ServerSwitcherListener;
import de.nexuslobby.modules.armorstandtools.*;
import de.nexuslobby.modules.gadgets.GadgetModule;
import de.nexuslobby.utils.VoidProtection;
import de.nexuslobby.utils.DoubleJump;
import de.nexuslobby.utils.PlayerHider;
@@ -47,6 +48,8 @@ public class NexusLobby extends JavaPlugin implements Listener {
private PortalManager portalManager;
private TablistModule tablistModule;
private LobbySettingsModule lobbySettingsModule;
private ItemsModule itemsModule;
private GadgetModule gadgetModule;
private File visualsFile;
private FileConfiguration visualsConfig;
@@ -62,6 +65,7 @@ public class NexusLobby extends JavaPlugin implements Listener {
public void onEnable() {
instance = this;
// Erst Config initialisieren
initCustomConfigs();
getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
@@ -126,7 +130,13 @@ public class NexusLobby extends JavaPlugin implements Listener {
private void registerModules() {
moduleManager.registerModule(new ProtectionModule());
moduleManager.registerModule(new ScoreboardModule());
moduleManager.registerModule(new ItemsModule());
this.itemsModule = new ItemsModule();
moduleManager.registerModule(this.itemsModule);
this.gadgetModule = new GadgetModule();
moduleManager.registerModule(this.gadgetModule);
moduleManager.registerModule(new SecurityModule());
moduleManager.registerModule(new BossBarModule());
moduleManager.registerModule(new ActionBarModule());
@@ -151,15 +161,13 @@ public class NexusLobby extends JavaPlugin implements Listener {
getServer().getPluginManager().registerEvents(new ASTListener(), this);
}
// Priorität auf LOWEST, damit das Inventar VOR den Modulen geleert wird
@EventHandler(priority = EventPriority.LOWEST)
public void onJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
event.setJoinMessage(null);
// FEHLERBEHEBUNG: Inventar leeren, um doppelte Items zu vermeiden
player.getInventory().clear();
player.getInventory().setArmorContents(null); // Auch Rüstung entfernen
player.getInventory().setArmorContents(null);
BuildCommand.removePlayerFromBuildMode(player);
@@ -192,9 +200,14 @@ public class NexusLobby extends JavaPlugin implements Listener {
File configFile = new File(getDataFolder(), "config.yml");
if (!configFile.exists()) {
// Nur speichern wenn sie fehlt
saveResource("config.yml", false);
} else {
// WICHTIG: ConfigUpdater für config.yml deaktiviert, da er Sektionen nicht korrekt erkennt!
// ConfigUpdater.updateConfig("config.yml");
}
ConfigUpdater.updateConfig("config.yml");
// Einfaches Laden reicht völlig aus
reloadConfig();
File settingsFile = new File(getDataFolder(), "settings.yml");
@@ -288,4 +301,6 @@ public class NexusLobby extends JavaPlugin implements Listener {
public ModuleManager getModuleManager() { return moduleManager; }
public TablistModule getTablistModule() { return tablistModule; }
public LobbySettingsModule getLobbySettingsModule() { return lobbySettingsModule; }
public ItemsModule getItemsModule() { return itemsModule; }
public GadgetModule getGadgetModule() { return gadgetModule; }
}

View File

@@ -12,7 +12,6 @@ import java.util.UUID;
public class BuildCommand implements CommandExecutor {
// Liste der Spieler im Baumodus
private static final ArrayList<UUID> buildModePlayers = new ArrayList<>();
@Override
@@ -30,7 +29,16 @@ public class BuildCommand implements CommandExecutor {
// BAUMODUS DEAKTIVIEREN
buildModePlayers.remove(uuid);
// Gamemode zurücksetzen (aus Config)
// Inventar leeren
player.getInventory().clear();
player.getInventory().setArmorContents(null);
// Lobby-Items über das Modul wiedergeben
if (NexusLobby.getInstance().getItemsModule() != null) {
NexusLobby.getInstance().getItemsModule().giveLobbyItems(player);
}
// Gamemode zurücksetzen
String defaultGmName = NexusLobby.getInstance().getConfig().getString("default-gamemode", "ADVENTURE");
try {
GameMode gm = GameMode.valueOf(defaultGmName.toUpperCase());
@@ -43,6 +51,9 @@ public class BuildCommand implements CommandExecutor {
} else {
// BAUMODUS AKTIVIEREN
buildModePlayers.add(uuid);
player.getInventory().clear();
player.getInventory().setArmorContents(null);
player.setGameMode(GameMode.CREATIVE);
player.sendMessage("§8» §6§lBuild §8| §7Baumodus §aaktiviert§7.");

View File

@@ -0,0 +1,47 @@
package de.nexuslobby.modules;
import de.nexuslobby.NexusLobby;
import de.nexuslobby.api.Module;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.UUID;
public class BuildModule implements Module {
private final ArrayList<UUID> buildModePlayers = new ArrayList<>();
@Override
public String getName() {
return "Build";
}
@Override
public void onEnable() {
// Falls du hier den Command registrierst, achte darauf,
// dass er nicht doppelt in der NexusLobby.java registriert wird.
}
public void toggleBuildMode(Player player) {
if (!player.hasPermission("nexuslobby.build")) return;
if (buildModePlayers.contains(player.getUniqueId())) {
buildModePlayers.remove(player.getUniqueId());
player.setGameMode(GameMode.SURVIVAL);
player.getInventory().clear();
// KORREKTUR: Hier muss getItemsModule() mit "s" stehen!
if (NexusLobby.getInstance().getItemsModule() != null) {
NexusLobby.getInstance().getItemsModule().giveLobbyItems(player);
}
} else {
buildModePlayers.add(player.getUniqueId());
player.setGameMode(GameMode.CREATIVE);
player.getInventory().clear();
}
}
@Override
public void onDisable() {}
}

View File

@@ -1,12 +1,18 @@
package de.nexuslobby.modules;
import de.nexuslobby.NexusLobby;
import de.nexuslobby.api.Module;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
public class ItemsModule implements Module, Listener {
@@ -17,12 +23,95 @@ public class ItemsModule implements Module, Listener {
@Override
public void onEnable() {
Bukkit.getPluginManager().registerEvents(this, Bukkit.getPluginManager().getPlugin("NexusLobby"));
Bukkit.getPluginManager().registerEvents(this, NexusLobby.getInstance());
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
event.getPlayer().getInventory().addItem(new ItemStack(Material.COMPASS));
Bukkit.getScheduler().runTaskLater(NexusLobby.getInstance(), () -> {
giveLobbyItems(event.getPlayer());
}, 2L);
}
@EventHandler
public void onInteract(PlayerInteractEvent event) {
Player player = event.getPlayer();
ItemStack item = event.getItem();
if (item == null || item.getType() == Material.AIR) return;
if (!item.hasItemMeta() || !item.getItemMeta().hasDisplayName()) return;
if (event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) {
FileConfiguration config = NexusLobby.getInstance().getConfig();
String displayName = item.getItemMeta().getDisplayName();
// 1. Baumodus Logik
String buildName = colorize(config.getString("items.lobby-tools.build-toggle.displayname", "&aBaumodus"));
if (displayName.equals(buildName)) {
player.performCommand("build");
event.setCancelled(true);
return;
}
// 2. Gadget GUI Logik
String gadgetName = colorize(config.getString("items.lobby-tools.gadget.displayname", "&bGadgets"));
if (displayName.equals(gadgetName)) {
// Öffnet die GUI aus dem GadgetModule
NexusLobby.getInstance().getGadgetModule().openGUI(player);
event.setCancelled(true);
return;
}
// 3. Kompass / Teleporter Logik (optional, falls du den Befehl schon hast)
String compassName = colorize(config.getString("items.lobby-tools.compass.displayname", "&eTeleporter"));
if (displayName.equals(compassName)) {
// Hier könnte z.B. player.performCommand("cp"); stehen
}
}
}
public void giveLobbyItems(Player player) {
player.getInventory().clear();
FileConfiguration config = NexusLobby.getInstance().getConfig();
// 1. Kompass (Slot 4)
if (config.getBoolean("items.lobby-tools.compass.enabled", true)) {
int slot = config.getInt("items.lobby-tools.compass.slot", 4);
String name = config.getString("items.lobby-tools.compass.displayname", "&eTeleporter");
player.getInventory().setItem(slot, createItem(Material.COMPASS, name));
}
// 2. Baumodus (Slot 0)
if (player.isOp() || player.hasPermission("nexuslobby.build")) {
if (config.getBoolean("items.lobby-tools.build-toggle.enabled", true)) {
int slot = config.getInt("items.lobby-tools.build-toggle.slot", 0);
String name = config.getString("items.lobby-tools.build-toggle.displayname", "&aBaumodus");
player.getInventory().setItem(slot, createItem(Material.REDSTONE_TORCH, name));
}
}
// 3. Gadgets (Slot 8)
if (config.getBoolean("items.lobby-tools.gadget.enabled", true)) { // Auf true gesetzt, damit es erscheint
int slot = config.getInt("items.lobby-tools.gadget.slot", 8);
String name = config.getString("items.lobby-tools.gadget.displayname", "&bGadgets");
player.getInventory().setItem(slot, createItem(Material.CHEST, name));
}
player.updateInventory();
}
private ItemStack createItem(Material mat, String name) {
ItemStack item = new ItemStack(mat);
ItemMeta meta = item.getItemMeta();
if (meta != null) {
meta.setDisplayName(colorize(name));
item.setItemMeta(meta);
}
return item;
}
private String colorize(String s) {
return (s == null) ? "" : s.replace("&", "§");
}
@Override

View File

@@ -0,0 +1,77 @@
package de.nexuslobby.modules.gadgets;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import java.util.UUID;
public class Balloon {
private final UUID playerUUID;
private final LivingEntity balloonEntity;
private final ArmorStand headStand;
public Balloon(Player player, Material balloonMaterial) {
this.playerUUID = player.getUniqueId();
Location loc = player.getLocation().add(0, 2, 0);
// Das unsichtbare Träger-Entity
this.balloonEntity = (LivingEntity) loc.getWorld().spawnEntity(loc, EntityType.PIG);
this.balloonEntity.setInvisible(true);
this.balloonEntity.setSilent(true);
this.balloonEntity.setInvulnerable(true);
this.balloonEntity.setGravity(false);
this.balloonEntity.setLeashHolder(player);
// Der ArmorStand, der den farbigen Block trägt
this.headStand = (ArmorStand) loc.getWorld().spawnEntity(loc, EntityType.ARMOR_STAND);
this.headStand.setVisible(false);
this.headStand.setGravity(false);
this.headStand.setMarker(true);
// Hier wird das übergebene Material gesetzt (z.B. RED_WOOL, BLUE_WOOL etc.)
this.headStand.setHelmet(new ItemStack(balloonMaterial));
}
public void update() {
Player player = org.bukkit.Bukkit.getPlayer(playerUUID);
if (player == null || !player.isOnline() || balloonEntity == null || !balloonEntity.isValid()) {
remove();
return;
}
if (!balloonEntity.isLeashed()) {
remove();
return;
}
Location targetLoc = player.getLocation().clone().add(0, 2.5, 0);
Vector direction = targetLoc.toVector().subtract(balloonEntity.getLocation().toVector());
double distance = direction.length();
if (distance > 5) {
balloonEntity.teleport(targetLoc);
} else if (distance > 0.1) {
balloonEntity.setVelocity(direction.multiply(0.4));
}
headStand.teleport(balloonEntity.getLocation().clone().subtract(0, 1.5, 0));
}
public void remove() {
if (balloonEntity != null) {
balloonEntity.setLeashHolder(null);
balloonEntity.remove();
}
if (headStand != null) {
headStand.remove();
}
}
}

View File

@@ -0,0 +1,54 @@
package de.nexuslobby.modules.gadgets;
import de.nexuslobby.NexusLobby;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.entity.Chicken;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.Random;
public class ChickenRain {
public static void start(Player player) {
new BukkitRunnable() {
int ticks = 0;
final Random random = new Random();
@Override
public void run() {
if (!player.isOnline() || ticks > 100) { // 100 Ticks = 5 Sekunden
this.cancel();
return;
}
// Alle 2 Ticks ein Huhn spawnen
if (ticks % 2 == 0) {
Location spawnLoc = player.getLocation().add(
(random.nextDouble() - 0.5) * 4,
4.0,
(random.nextDouble() - 0.5) * 4
);
Chicken chicken = (Chicken) spawnLoc.getWorld().spawnEntity(spawnLoc, EntityType.CHICKEN);
chicken.setBaby();
chicken.setInvulnerable(true);
// Nach 1.5 Sekunden "ploppt" das Huhn
Bukkit.getScheduler().runTaskLater(NexusLobby.getInstance(), () -> {
if (chicken.isValid()) {
chicken.getWorld().spawnParticle(Particle.CLOUD, chicken.getLocation(), 5, 0.2, 0.2, 0.2, 0.1);
chicken.getWorld().playSound(chicken.getLocation(), Sound.ENTITY_CHICKEN_EGG, 1.0f, 1.5f);
chicken.remove();
}
}, 30L);
}
ticks++;
}
}.runTaskTimer(NexusLobby.getInstance(), 0L, 1L);
}
}

View File

@@ -0,0 +1,200 @@
package de.nexuslobby.modules.gadgets;
import de.nexuslobby.NexusLobby;
import de.nexuslobby.api.Module;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class GadgetModule implements Module, Listener {
private final Map<UUID, Balloon> activeBalloons = new HashMap<>();
private final Map<UUID, ParticleEffect> activeEffects = new HashMap<>();
// Titel für die verschiedenen Inventare zur Identifikation
private final String MAIN_TITLE = "§b§lGadgets §8- §7Menü";
private final String BALLOON_TITLE = "§b§lGadgets §8- §eBallons";
private final String PARTICLE_TITLE = "§b§lGadgets §8- §dPartikel";
private final String FUN_TITLE = "§b§lGadgets §8- §6Lustiges";
@Override
public String getName() { return "Gadgets"; }
@Override
public void onEnable() {
Bukkit.getPluginManager().registerEvents(this, NexusLobby.getInstance());
Bukkit.getScheduler().runTaskTimer(NexusLobby.getInstance(), () -> {
activeBalloons.values().forEach(Balloon::update);
activeEffects.forEach((uuid, effect) -> {
Player p = Bukkit.getPlayer(uuid);
if (p != null && p.isOnline()) effect.update(p);
});
}, 1L, 1L);
}
// --- GUI ÖFFNER ---
public void openGUI(Player player) {
Inventory gui = Bukkit.createInventory(null, 27, MAIN_TITLE);
fillEdges(gui);
gui.setItem(10, createItem(Material.LEAD, "§e§lBallons", "§7Wähle einen fliegenden Begleiter"));
gui.setItem(13, createItem(Material.FIREWORK_ROCKET, "§6§lLustiges", "§7Witzige Effekte für zwischendurch"));
gui.setItem(16, createItem(Material.NETHER_STAR, "§d§lPartikel", "§7Wähle magische Effekte"));
gui.setItem(22, createItem(Material.BARRIER, "§c§lStopp", "§7Alle Gadgets entfernen"));
player.openInventory(gui);
}
private void openBalloonGUI(Player player) {
Inventory gui = Bukkit.createInventory(null, 36, BALLOON_TITLE);
fillEdges(gui);
Material[] wools = {Material.WHITE_WOOL, Material.ORANGE_WOOL, Material.MAGENTA_WOOL, Material.LIGHT_BLUE_WOOL,
Material.YELLOW_WOOL, Material.LIME_WOOL, Material.PINK_WOOL, Material.GRAY_WOOL,
Material.CYAN_WOOL, Material.PURPLE_WOOL, Material.BLUE_WOOL, Material.BROWN_WOOL,
Material.GREEN_WOOL, Material.RED_WOOL};
int slot = 10;
for (Material m : wools) {
if (slot == 17) slot = 19;
gui.setItem(slot++, createItem(m, "§fBallon: " + m.name().replace("_WOOL", ""), "§7Klicke zum Ausrüsten"));
}
gui.setItem(31, createItem(Material.ARROW, "§7Zurück", "§8Zum Hauptmenü"));
player.openInventory(gui);
}
private void openParticleGUI(Player player) {
Inventory gui = Bukkit.createInventory(null, 27, PARTICLE_TITLE);
fillEdges(gui);
gui.setItem(11, createItem(Material.POPPY, "§cHerzchen-Aura", "§7Verbreite Liebe"));
gui.setItem(13, createItem(Material.BLAZE_POWDER, "§6Flammen-Ring", "§7Werde feurig"));
gui.setItem(15, createItem(Material.WATER_BUCKET, "§bRegenwolke", "§7Lass es regnen"));
gui.setItem(22, createItem(Material.ARROW, "§7Zurück", "§8Zum Hauptmenü"));
player.openInventory(gui);
}
private void openFunGUI(Player player) {
Inventory gui = Bukkit.createInventory(null, 27, FUN_TITLE);
fillEdges(gui);
gui.setItem(13, createItem(Material.EGG, "§f§lChicken-Rain", "§7Lass es Küken regnen!"));
gui.setItem(22, createItem(Material.ARROW, "§7Zurück", "§8Zum Hauptmenü"));
player.openInventory(gui);
}
// --- EVENT HANDLER ---
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
String title = event.getView().getTitle();
if (!title.equals(MAIN_TITLE) && !title.equals(BALLOON_TITLE) && !title.equals(PARTICLE_TITLE) && !title.equals(FUN_TITLE)) return;
event.setCancelled(true);
Player player = (Player) event.getWhoClicked();
ItemStack item = event.getCurrentItem();
if (item == null || item.getType() == Material.AIR) return;
// Zurück-Button Logik
if (item.getType() == Material.ARROW) {
openGUI(player);
return;
}
// HAUPTMENÜ LOGIK
if (title.equals(MAIN_TITLE)) {
if (item.getType() == Material.LEAD) openBalloonGUI(player);
else if (item.getType() == Material.NETHER_STAR) openParticleGUI(player);
else if (item.getType() == Material.FIREWORK_ROCKET) openFunGUI(player);
else if (item.getType() == Material.BARRIER) {
removeGadgets(player);
player.closeInventory();
}
}
// BALLON MENÜ LOGIK
else if (title.equals(BALLOON_TITLE)) {
if (item.getType().toString().endsWith("_WOOL")) {
if (activeBalloons.containsKey(player.getUniqueId())) activeBalloons.get(player.getUniqueId()).remove();
activeBalloons.put(player.getUniqueId(), new Balloon(player, item.getType()));
player.sendMessage("§8[§6Nexus§8] §aBallon ausgerüstet!");
player.closeInventory();
}
}
// PARTIKEL MENÜ LOGIK
else if (title.equals(PARTICLE_TITLE)) {
if (item.getType() == Material.POPPY) activeEffects.put(player.getUniqueId(), new ParticleEffect("hearts"));
else if (item.getType() == Material.BLAZE_POWDER) activeEffects.put(player.getUniqueId(), new ParticleEffect("flames"));
else if (item.getType() == Material.WATER_BUCKET) activeEffects.put(player.getUniqueId(), new ParticleEffect("cloud"));
if (item.getType() != Material.GRAY_STAINED_GLASS_PANE) {
player.sendMessage("§8[§6Nexus§8] §aEffekt aktiviert!");
player.closeInventory();
}
}
// FUN MENÜ LOGIK
else if (title.equals(FUN_TITLE)) {
if (item.getType() == Material.EGG) {
ChickenRain.start(player);
player.sendMessage("§8[§6Nexus§8] §fEs regnet jetzt Hühner!");
player.closeInventory();
}
}
}
private void removeGadgets(Player player) {
if (activeBalloons.containsKey(player.getUniqueId())) {
activeBalloons.get(player.getUniqueId()).remove();
activeBalloons.remove(player.getUniqueId());
}
activeEffects.remove(player.getUniqueId());
player.sendMessage("§8[§6Nexus§8] §cAlle Gadgets wurden entfernt.");
}
private void fillEdges(Inventory inv) {
ItemStack glass = createItem(Material.GRAY_STAINED_GLASS_PANE, " ", null);
for (int i = 0; i < inv.getSize(); i++) {
if (i < 9 || i >= inv.getSize() - 9 || i % 9 == 0 || (i + 1) % 9 == 0) {
inv.setItem(i, glass);
}
}
}
private ItemStack createItem(Material mat, String name, String lore) {
ItemStack item = new ItemStack(mat);
ItemMeta meta = item.getItemMeta();
if (meta != null) {
meta.setDisplayName(name);
if (lore != null) {
java.util.List<String> l = new java.util.ArrayList<>();
l.add(lore);
meta.setLore(l);
}
item.setItemMeta(meta);
}
return item;
}
@Override
public void onDisable() {
activeBalloons.values().forEach(Balloon::remove);
activeBalloons.clear();
activeEffects.clear();
}
}

View File

@@ -0,0 +1,31 @@
package de.nexuslobby.modules.gadgets;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.entity.Player;
public class ParticleEffect {
private final String type;
private double angle = 0;
public ParticleEffect(String type) { this.type = type; }
public void update(Player player) {
Location loc = player.getLocation();
switch (type.toLowerCase()) {
case "hearts":
player.getWorld().spawnParticle(Particle.HEART, loc.clone().add(0, 2.2, 0), 1, 0.3, 0.3, 0.3, 0);
break;
case "flames":
double x = 0.6 * Math.cos(angle);
double z = 0.6 * Math.sin(angle);
player.getWorld().spawnParticle(Particle.FLAME, loc.clone().add(x, 0.1, z), 1, 0, 0, 0, 0);
angle += 0.2;
break;
case "cloud":
player.getWorld().spawnParticle(Particle.CLOUD, loc.clone().add(0, 2.5, 0), 2, 0.2, 0.05, 0.2, 0);
player.getWorld().spawnParticle(Particle.FALLING_WATER, loc.clone().add(0, 2.4, 0), 1, 0.1, 0, 0.1, 0);
break;
}
}
}

View File

@@ -1,210 +1,311 @@
package de.nexuslobby.modules.tablist;
import de.nexuslobby.NexusLobby;
import de.nexuslobby.api.Module;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.LuckPermsProvider;
import net.luckperms.api.model.group.Group;
import net.luckperms.api.model.user.User;
import me.clip.placeholderapi.PlaceholderAPI;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitTask;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.Team;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TablistModule implements Module {
private BukkitTask refreshTask;
private int currentHeaderIndex = 0;
private int currentFooterIndex = 0;
private LuckPerms luckPerms;
private boolean placeholderAPIEnabled;
// Pixel-Breiten Tabelle für die Standard Minecraft Schriftart
private static final Map<Character, Integer> CHAR_WIDTHS = new HashMap<>();
private static final int TARGET_PIXEL_WIDTH = 90; // Ziel-Breite für den Namensteil vor dem Ping
static {
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
for (char c : chars.toCharArray()) CHAR_WIDTHS.put(c, 6);
CHAR_WIDTHS.put('i', 2); CHAR_WIDTHS.put('l', 3); CHAR_WIDTHS.put('t', 4);
CHAR_WIDTHS.put('I', 4); CHAR_WIDTHS.put('f', 5); CHAR_WIDTHS.put('k', 5);
CHAR_WIDTHS.put('.', 2); CHAR_WIDTHS.put(',', 2); CHAR_WIDTHS.put('!', 2);
CHAR_WIDTHS.put('(', 5); CHAR_WIDTHS.put(')', 5); CHAR_WIDTHS.put('[', 4);
CHAR_WIDTHS.put(']', 4); CHAR_WIDTHS.put('{', 5); CHAR_WIDTHS.put('}', 5);
CHAR_WIDTHS.put('|', 2); CHAR_WIDTHS.put('*', 5); CHAR_WIDTHS.put(' ', 4);
CHAR_WIDTHS.put('_', 6);
}
@Override
public String getName() { return "Tablist"; }
@Override
public void onEnable() {
FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig();
if (!vConfig.getBoolean("tablist.enabled", true)) return;
if (Bukkit.getPluginManager().getPlugin("LuckPerms") != null) luckPerms = LuckPermsProvider.get();
placeholderAPIEnabled = Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null;
// Nutzt jetzt das Intervall aus der visuals.yml
long interval = vConfig.getLong("tablist.interval-ticks", 40L);
refreshTask = Bukkit.getScheduler().runTaskTimer(NexusLobby.getInstance(), this::refreshAll, 10L, interval);
}
private void refreshAll() {
updateAnimationIndices();
for (Player viewer : Bukkit.getOnlinePlayers()) {
viewer.setPlayerListHeader(getHeader(viewer));
viewer.setPlayerListFooter(getFooter(viewer));
updateTeamsAndFormatting(viewer);
updateTeams(viewer);
}
}
private void updateTeamsAndFormatting(Player viewer) {
private void updateTeams(Player viewer) {
FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig();
boolean showPrefix = vConfig.getBoolean("tablist.show-prefix-in-playerlist", true);
if (!vConfig.getBoolean("tablist.show-prefix-in-playerlist", true)) return;
Scoreboard sb = viewer.getScoreboard();
if (sb == Bukkit.getScoreboardManager().getMainScoreboard()) return;
for (Player target : Bukkit.getOnlinePlayers()) {
String sortPriority = getSortPriority(target);
String teamName = sortPriority + target.getName();
String teamName = "tab_" + target.getName();
if (teamName.length() > 16) teamName = teamName.substring(0, 16);
Team team = sb.getTeam(teamName);
if (team == null) team = sb.registerNewTeam(teamName);
String prefix = showPrefix ? getPlayerPrefix(target) : "§7";
String prefix = getPlayerPrefix(target);
String suffix = getPlayerSuffix(target);
if (!team.getPrefix().equals(prefix)) team.setPrefix(prefix);
// Hier wird der Pixel-Ausgleich berechnet, damit der Trenner | immer an der gleichen Stelle ist
String pingSuffix = getAlignedPingSuffix(target.getName(), target.getPing());
if (!team.getSuffix().equals(pingSuffix)) team.setSuffix(pingSuffix);
if (!team.getSuffix().equals(suffix)) team.setSuffix(suffix);
if (!team.hasEntry(target.getName())) {
team.addEntry(target.getName());
}
// Setzt den Listnamen zurück, damit Team-Prefix/Suffix greifen
if (target.getPlayerListName() != null) {
target.setPlayerListName(null);
}
}
}
private String getAlignedPingSuffix(String name, int ping) {
int currentWidth = 0;
for (char c : name.toCharArray()) {
currentWidth += CHAR_WIDTHS.getOrDefault(c, 6);
}
int diff = TARGET_PIXEL_WIDTH - currentWidth;
StringBuilder spacer = new StringBuilder(" ");
// Ausgleich mit fetten (5px) und normalen (4px) Leerzeichen für maximale Präzision
while (diff > 0) {
if (diff >= 5) {
spacer.append("§l ");
diff -= 5;
} else {
spacer.append(" ");
diff -= 4;
}
}
String pingColor = (ping < 50) ? "§a" : (ping < 100 ? "§e" : "§c");
// §r bricht das fettgedruckte Leerzeichen ab, damit der Ping normal aussieht
return "§r" + spacer.toString() + "§8| " + pingColor + ping + "ms";
}
private String getSortPriority(Player player) {
if (luckPerms == null) return "z_";
User user = luckPerms.getUserManager().getUser(player.getUniqueId());
if (user == null) return "z_";
Group group = luckPerms.getGroupManager().getGroup(user.getPrimaryGroup());
if (group == null) return "z_";
int weight = group.getWeight().orElse(0);
int invertedWeight = 1000 - weight;
return String.format("%03d", invertedWeight) + "_";
}
private String getPlayerPrefix(Player player) {
String prefix = "";
if (luckPerms != null) {
User user = luckPerms.getUserManager().getUser(player.getUniqueId());
if (user != null && user.getCachedData().getMetaData().getPrefix() != null) {
prefix = user.getCachedData().getMetaData().getPrefix();
}
}
if (prefix.isEmpty() && placeholderAPIEnabled) {
prefix = PlaceholderAPI.setPlaceholders(player, "%luckperms_prefix%");
}
return colorize(prefix.isEmpty() ? "&7" : prefix + " ");
}
private String getPlayerSuffix(Player player) {
// Hier könnte man später auch Suffixe aus der Config laden
return colorize(" &8[&a" + player.getPing() + "ms&8]");
}
private void updateAnimationIndices() {
FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig();
List<String> h = vConfig.getStringList("tablist.header-animations");
List<String> f = vConfig.getStringList("tablist.footer-animations");
if (!h.isEmpty()) currentHeaderIndex = (currentHeaderIndex + 1) % h.size();
if (!f.isEmpty()) currentFooterIndex = (currentFooterIndex + 1) % f.size();
}
private String getHeader(Player p) {
FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig();
List<String> list = vConfig.getStringList("tablist.header-animations");
if (list.isEmpty()) return "§6NexusLobby";
return replacePlaceholders(list.get(currentHeaderIndex), p);
}
private String getFooter(Player p) {
FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig();
List<String> list = vConfig.getStringList("tablist.footer-animations");
if (list.isEmpty()) return "§7Willkommen";
return replacePlaceholders(list.get(currentFooterIndex), p);
}
private String replacePlaceholders(String text, Player p) {
FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig();
text = text.replace("{server}", vConfig.getString("tablist.server-name", "NexusLobby"));
text = text.replace("{player}", p.getName());
text = text.replace("{online}", String.valueOf(Bukkit.getOnlinePlayers().size()));
text = text.replace("{staff}", String.valueOf(getOnlineStaffCount()));
text = text.replace("{separator}", vConfig.getString("tablist.separator-line", ""));
text = text.replace("{website}", vConfig.getBoolean("tablist.show-website") ? vConfig.getString("tablist.website", "") : "");
text = text.replace("{teamspeak}", vConfig.getBoolean("tablist.show-teamspeak") ? vConfig.getString("tablist.teamspeak-address", "") : "");
text = text.replace("{discord}", vConfig.getBoolean("tablist.show-discord") ? vConfig.getString("tablist.discord-address", "") : "");
if (placeholderAPIEnabled) {
text = PlaceholderAPI.setPlaceholders(p, text);
}
return colorize(text);
}
private int getOnlineStaffCount() {
FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig();
String permission = vConfig.getString("tablist.staff-permission", "nexuslobby.staff");
int count = 0;
for (Player p : Bukkit.getOnlinePlayers()) {
if (p.hasPermission(permission)) count++;
}
return count;
}
private String colorize(String s) {
return s == null ? "" : s.replace("&", "§");
}
@Override
public void onDisable() {
if (refreshTask != null) refreshTask.cancel();
}
}

View File

@@ -36,11 +36,11 @@ items:
build-toggle:
enabled: true
displayname: "&aBaumodus"
slot: 1
slot: 0
gadget:
enabled: true
enabled: false
displayname: "&bGadgets"
slot: 2
slot: 8
# --- Portal Einstellungen ---
portals:

View File

@@ -1,6 +1,6 @@
name: NexusLobby
main: de.nexuslobby.NexusLobby
version: "1.0.1"
version: "1.0.2"
api-version: "1.21"
author: M_Viper
description: Modular Lobby Plugin