diff --git a/src/main/java/de/nexuslobby/NexusLobby.java b/src/main/java/de/nexuslobby/NexusLobby.java index fb16113..fae2dc7 100644 --- a/src/main/java/de/nexuslobby/NexusLobby.java +++ b/src/main/java/de/nexuslobby/NexusLobby.java @@ -19,7 +19,12 @@ import de.nexuslobby.utils.DoubleJump; import de.nexuslobby.utils.PlayerHider; import de.nexuslobby.utils.MaintenanceListener; import de.nexuslobby.utils.ConfigUpdater; +import de.nexuslobby.utils.UpdateChecker; import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.command.PluginCommand; @@ -27,6 +32,7 @@ import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.plugin.java.JavaPlugin; @@ -45,6 +51,9 @@ public class NexusLobby extends JavaPlugin implements Listener { private File visualsFile; private FileConfiguration visualsConfig; + private boolean updateAvailable = false; + private String latestVersion = ""; + public static NexusLobby getInstance() { return instance; } @@ -56,10 +65,8 @@ public class NexusLobby extends JavaPlugin implements Listener { initCustomConfigs(); getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); - moduleManager = new ModuleManager(this); - // Initialisierung der GUI-Werkzeuge ArmorStandGUI.init(); registerModules(); @@ -72,31 +79,48 @@ public class NexusLobby extends JavaPlugin implements Listener { } registerCommands(); + checkUpdates(); + getLogger().info("NexusLobby wurde erfolgreich gestartet."); } + private void checkUpdates() { + new UpdateChecker(this).getVersion(version -> { + if (!this.getDescription().getVersion().equalsIgnoreCase(version)) { + this.updateAvailable = true; + this.latestVersion = version; + getLogger().warning("===================================================="); + getLogger().warning("Update gefunden! v" + getDescription().getVersion() + " -> v" + version); + getLogger().warning("Autor: M_Viper"); + getLogger().warning("===================================================="); + } else { + getLogger().info("NexusLobby ist aktuell (v" + version + ")."); + } + }); + } + public void reloadPlugin() { getLogger().info("Plugin Reload wird gestartet..."); - + if (moduleManager != null) { moduleManager.disableAll(); } reloadConfig(); - initCustomConfigs(); + visualsConfig = null; + reloadVisualsConfig(); + Config.load(); if (portalManager != null) { portalManager.loadPortals(); } + ArmorStandGUI.init(); if (moduleManager != null) { moduleManager.enableAll(); } - ArmorStandGUI.init(); - Config.load(); - - getLogger().info("Plugin Reload abgeschlossen."); + getLogger().info("Plugin Reload abgeschlossen. Änderungen wurden übernommen."); } private void registerModules() { @@ -127,24 +151,49 @@ public class NexusLobby extends JavaPlugin implements Listener { getServer().getPluginManager().registerEvents(new ASTListener(), this); } - @EventHandler + // 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 + BuildCommand.removePlayerFromBuildMode(player); String defaultGmName = getConfig().getString("default-gamemode", "ADVENTURE"); try { - GameMode gm = GameMode.valueOf(defaultGmName.toUpperCase()); - player.setGameMode(gm); + player.setGameMode(GameMode.valueOf(defaultGmName.toUpperCase())); } catch (IllegalArgumentException e) { player.setGameMode(GameMode.ADVENTURE); } + + if (player.hasPermission("nexuslobby.admin") && updateAvailable) { + player.sendMessage(" "); + player.sendMessage("§8[§6Nexus§8] §aEin neues §6Update §afür §eNexusLobby §aist verfügbar!"); + player.sendMessage("§8» §7Version: §c" + getDescription().getVersion() + " §8-> §a" + latestVersion); + + TextComponent link = new TextComponent("§8» §6Klicke §e§l[HIER] §6zum Herunterladen."); + link.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://git.viper.ipv64.net/M_Viper/NexusLobby/releases")); + link.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, + new ComponentBuilder("§7Öffnet die Gitea Release-Seite").create())); + + player.spigot().sendMessage(link); + player.sendMessage(" "); + } } private void initCustomConfigs() { - saveDefaultConfig(); + if (!getDataFolder().exists()) { + getDataFolder().mkdirs(); + } + + File configFile = new File(getDataFolder(), "config.yml"); + if (!configFile.exists()) { + saveResource("config.yml", false); + } ConfigUpdater.updateConfig("config.yml"); reloadConfig(); @@ -152,13 +201,11 @@ public class NexusLobby extends JavaPlugin implements Listener { if (!settingsFile.exists()) { saveResource("settings.yml", false); } - ConfigUpdater.updateConfig("settings.yml"); visualsFile = new File(getDataFolder(), "visuals.yml"); if (!visualsFile.exists()) { saveResource("visuals.yml", false); } - ConfigUpdater.updateConfig("visuals.yml"); reloadVisualsConfig(); Config.load(); @@ -169,6 +216,7 @@ public class NexusLobby extends JavaPlugin implements Listener { visualsFile = new File(getDataFolder(), "visuals.yml"); } visualsConfig = YamlConfiguration.loadConfiguration(visualsFile); + getLogger().info("visuals.yml erfolgreich vom Speicher geladen."); } public FileConfiguration getVisualsConfig() { @@ -181,9 +229,7 @@ public class NexusLobby extends JavaPlugin implements Listener { @Override public void onDisable() { getServer().getMessenger().unregisterOutgoingPluginChannel(this, "BungeeCord"); - if (moduleManager != null) { - moduleManager.disableAll(); - } + if (moduleManager != null) moduleManager.disableAll(); getLogger().info("NexusLobby disabled"); } @@ -206,13 +252,11 @@ public class NexusLobby extends JavaPlugin implements Listener { if (getCommand("settings") != null) getCommand("settings").setExecutor(new LobbySettingsCommand(lobbySettingsModule)); if (getCommand("build") != null) getCommand("build").setExecutor(new BuildCommand()); - // --- NEXUS TOOLS & CMD REGISTRIERUNG --- if (getCommand("nexustools") != null) { getCommand("nexustools").setExecutor(new ArmorStandCommand()); getCommand("nexustools").setTabCompleter(tabCompleter); } - // Wir registrieren nexuscmd (ehemals ascmd) if (getCommand("nexuscmd") != null) { getCommand("nexuscmd").setExecutor(new ArmorStandCmdExecutor()); getCommand("nexuscmd").setTabCompleter(tabCompleter); @@ -226,27 +270,17 @@ public class NexusLobby extends JavaPlugin implements Listener { } public class NexusLobbyExpansion extends PlaceholderExpansion { - @Override - public @NotNull String getIdentifier() { return "nexuslobby"; } - @Override - public @NotNull String getAuthor() { return String.join(", ", NexusLobby.this.getDescription().getAuthors()); } - @Override - public @NotNull String getVersion() { return NexusLobby.this.getDescription().getVersion(); } - @Override - public boolean persist() { return true; } + @Override public @NotNull String getIdentifier() { return "nexuslobby"; } + @Override public @NotNull String getAuthor() { return String.join(", ", NexusLobby.this.getDescription().getAuthors()); } + @Override public @NotNull String getVersion() { return NexusLobby.this.getDescription().getVersion(); } + @Override public boolean persist() { return true; } @Override public String onPlaceholderRequest(Player player, @NotNull String params) { if (player == null) return ""; - if (params.equalsIgnoreCase("maintenance_status")) { - return MaintenanceListener.isMaintenance() ? "§cAktiv" : "§aDeaktiviert"; - } - if (params.equalsIgnoreCase("version")) { - return NexusLobby.this.getDescription().getVersion(); - } - if (params.equalsIgnoreCase("build_mode")) { - return BuildCommand.isInBuildMode(player) ? "§aAktiv" : "§cInaktiv"; - } + if (params.equalsIgnoreCase("maintenance_status")) return MaintenanceListener.isMaintenance() ? "§cAktiv" : "§aDeaktiviert"; + if (params.equalsIgnoreCase("version")) return NexusLobby.this.getDescription().getVersion(); + if (params.equalsIgnoreCase("build_mode")) return BuildCommand.isInBuildMode(player) ? "§aAktiv" : "§cInaktiv"; return null; } } diff --git a/src/main/java/de/nexuslobby/modules/tablist/TablistModule.java b/src/main/java/de/nexuslobby/modules/tablist/TablistModule.java index 21141f4..6e89e91 100644 --- a/src/main/java/de/nexuslobby/modules/tablist/TablistModule.java +++ b/src/main/java/de/nexuslobby/modules/tablist/TablistModule.java @@ -4,6 +4,7 @@ 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; @@ -13,7 +14,9 @@ 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 { @@ -23,6 +26,22 @@ public class TablistModule implements Module { private LuckPerms luckPerms; private boolean placeholderAPIEnabled; + // Pixel-Breiten Tabelle für die Standard Minecraft Schriftart + private static final Map 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"; } @@ -34,7 +53,6 @@ public class TablistModule implements Module { 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); } @@ -44,36 +62,80 @@ public class TablistModule implements Module { for (Player viewer : Bukkit.getOnlinePlayers()) { viewer.setPlayerListHeader(getHeader(viewer)); viewer.setPlayerListFooter(getFooter(viewer)); - updateTeams(viewer); + updateTeamsAndFormatting(viewer); } } - private void updateTeams(Player viewer) { + private void updateTeamsAndFormatting(Player viewer) { FileConfiguration vConfig = NexusLobby.getInstance().getVisualsConfig(); - if (!vConfig.getBoolean("tablist.show-prefix-in-playerlist", true)) return; + boolean showPrefix = vConfig.getBoolean("tablist.show-prefix-in-playerlist", true); Scoreboard sb = viewer.getScoreboard(); if (sb == Bukkit.getScoreboardManager().getMainScoreboard()) return; for (Player target : Bukkit.getOnlinePlayers()) { - String teamName = "tab_" + target.getName(); + String sortPriority = getSortPriority(target); + String teamName = sortPriority + 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 = getPlayerPrefix(target); - String suffix = getPlayerSuffix(target); - + String prefix = showPrefix ? getPlayerPrefix(target) : "§7"; if (!team.getPrefix().equals(prefix)) team.setPrefix(prefix); - if (!team.getSuffix().equals(suffix)) team.setSuffix(suffix); + + // 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.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) { @@ -88,11 +150,6 @@ public class TablistModule implements Module { 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 h = vConfig.getStringList("tablist.header-animations"); @@ -117,13 +174,11 @@ public class TablistModule implements Module { 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", "") : ""); @@ -131,7 +186,6 @@ public class TablistModule implements Module { if (placeholderAPIEnabled) { text = PlaceholderAPI.setPlaceholders(p, text); } - return colorize(text); } diff --git a/src/main/java/de/nexuslobby/utils/UpdateChecker.java b/src/main/java/de/nexuslobby/utils/UpdateChecker.java new file mode 100644 index 0000000..d3ead38 --- /dev/null +++ b/src/main/java/de/nexuslobby/utils/UpdateChecker.java @@ -0,0 +1,42 @@ +package de.nexuslobby.utils; + +import de.nexuslobby.NexusLobby; +import org.bukkit.Bukkit; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Scanner; +import java.util.function.Consumer; + +public class UpdateChecker { + + private final NexusLobby plugin; + // URL zur Gitea API für das neueste Release + private final String url = "https://git.viper.ipv64.net/api/v1/repos/M_Viper/NexusLobby/releases/latest"; + + public UpdateChecker(NexusLobby plugin) { + this.plugin = plugin; + } + + public void getVersion(final Consumer consumer) { + Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> { + try (InputStream inputStream = new URL(url).openStream(); Scanner scanner = new Scanner(inputStream)) { + StringBuilder response = new StringBuilder(); + while (scanner.hasNextLine()) { + response.append(scanner.nextLine()); + } + + String content = response.toString(); + // Einfaches Parsing des JSON "tag_name" Feldes + if (content.contains("\"tag_name\":")) { + String version = content.split("\"tag_name\":\"")[1].split("\"")[0]; + // Entferne ein eventuelles 'v' Präfix (z.B. v1.0.0 -> 1.0.0) + version = version.replace("v", ""); + consumer.accept(version); + } + } catch (IOException exception) { + this.plugin.getLogger().warning("Update-Check fehlgeschlagen: " + exception.getMessage()); + } + }); + } +} \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 5e143fc..d8b6532 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.0.0" +version: "1.0.1" api-version: "1.21" author: M_Viper description: Modular Lobby Plugin