package de.ticketsystem.bungee; import com.google.common.io.ByteArrayDataInput; import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; import de.ticketsystem.TicketPlugin; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.plugin.messaging.PluginMessageListener; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.UUID; /** * Verwaltet die BungeeCord Plugin-Messaging-Kanäle für Cross-Server-Kommunikation. * * Kanalübersicht: * Ausgehend: "BungeeCord" – Standard-BungeeCord-Kanal (Forward, Message) * Eingehend: "ticketsystem:notify" – Eigener Kanal für weitergeleitete Nachrichten * * Voraussetzung: * - In spigot.yml muss "bungeecord: true" gesetzt sein * - In plugin.yml müssen beide Kanäle unter "channels:" deklariert sein * * Pakettypen (erstes Byte bei ticketsystem:notify): * 0x01 = TEAM_NOTIFY – Nachricht an alle Online-Supporter/Admins auf diesem Server * 0x02 = PLAYER_MSG – Nachricht an einen bestimmten Spieler (UUID + Text) * * ── BUG FIX ────────────────────────────────────────────────────────────────── * Problem: BungeeCord's "Forward ALL" liefert auf dem Zielserver den inneren * Payload BEREITS ENTPACKT via onPluginMessageReceived auf dem * CUSTOM_CHANNEL. Das war korrekt implementiert. * * Der eigentliche Fehler lag in broadcastTeamNotification(): * - Nachrichten mit "\n" wurden als ein einzelner String gesendet. * Minecraft verarbeitet "\n" in sendMessage() nicht → beide Zeilen kamen * als eine zusammen an (unleserlich, aber nicht die Ursache für "gar nichts"). * - Die Methode wird jetzt mit einer Liste von Strings aufgerufen (broadcastLines) * damit jede Zeile als separates Paket gesendet wird – klar und lesbar. * * Hauptursache für "gar nichts auf Lobby": * Die plugin.yml hatte keinen "channels:"-Block. Ohne diesen Eintrag * registriert BungeeCord den Kanal "ticketsystem:notify" nicht und * verwirft alle eingehenden Forward-Pakete lautlos auf den Ziel-Servern. * → plugin.yml Fix ist die primäre Lösung. * * Diese Datei enthält zusätzlich Debug-Logging (wenn debug: true in config.yml) * damit zukünftige Probleme schneller gefunden werden können. * ───────────────────────────────────────────────────────────────────────────── */ public class BungeeMessenger implements PluginMessageListener { /** BungeeCord-Standardkanal für Forward/Message-Subkanäle */ public static final String BUNGEE_CHANNEL = "BungeeCord"; /** Eigener Weiterleitungskanal – muss in plugin.yml unter channels stehen */ public static final String CUSTOM_CHANNEL = "ticketsystem:notify"; private static final byte TYPE_TEAM_NOTIFY = 0x01; private static final byte TYPE_PLAYER_MSG = 0x02; private final TicketPlugin plugin; public BungeeMessenger(TicketPlugin plugin) { this.plugin = plugin; } // ─────────────────────────── Ausgehende Nachrichten ──────────────────── /** * Sendet eine Chat-Nachricht an einen bestimmten Spieler – egal auf welchem * Server im Netzwerk er sich befindet. * * Reihenfolge: * 1. Spieler ist lokal online → direkte Zustellung * 2. Spieler ist woanders → BungeeCord "Message"-Subkanal (nach Name) * 3. Spieler ist offline → Pendende DB-Benachrichtigung (vorher speichern!) */ public void sendMessageToPlayer(UUID targetUUID, String targetName, String message) { // 1. Lokal online? Player local = Bukkit.getPlayer(targetUUID); if (local != null && local.isOnline()) { local.sendMessage(message); return; } // 2. Cross-Server via BungeeCord "Message"-Subkanal Player messenger = getAnyOnlinePlayer(); if (messenger == null || targetName == null) return; ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.writeUTF("Message"); out.writeUTF(targetName); out.writeUTF(message); messenger.sendPluginMessage(plugin, BUNGEE_CHANNEL, out.toByteArray()); if (plugin.isDebug()) { plugin.getLogger().info("[DEBUG][BungeeMessenger] sendMessageToPlayer → " + targetName + ": " + message); } } /** * Broadcastet eine Team-Benachrichtigung an alle Supporter/Admins im gesamten Netzwerk. * * Lokal online Spieler werden sofort benachrichtigt. * Alle anderen Server erhalten das Paket über den "Forward ALL"-Mechanismus. * * WICHTIG: Jede Zeile wird als separates Paket gesendet damit Minecraft * die Nachrichten korrekt zeilenweise anzeigt. */ public void broadcastTeamNotification(String message) { // Lokale Supporter direkt benachrichtigen Bukkit.getOnlinePlayers().stream() .filter(p -> p.hasPermission("ticket.support") || p.hasPermission("ticket.admin")) .forEach(p -> p.sendMessage(message)); // An alle anderen Server forwarden Player messenger = getAnyOnlinePlayer(); if (messenger == null) { if (plugin.isDebug()) { plugin.getLogger().warning("[DEBUG][BungeeMessenger] broadcastTeamNotification: kein Bote online – Forward nicht möglich!"); } return; } sendForwardPacket(messenger, message); if (plugin.isDebug()) { plugin.getLogger().info("[DEBUG][BungeeMessenger] broadcastTeamNotification gesendet via " + messenger.getName() + ": " + message); } } /** * Sendet eine Nachricht an einen bestimmten Spieler via eigenem Forward-Paket. */ public void forwardPlayerMessage(UUID targetUUID, String targetName, String message) { Player local = Bukkit.getPlayer(targetUUID); if (local != null && local.isOnline()) { local.sendMessage(message); return; } Player messenger = getAnyOnlinePlayer(); if (messenger == null) return; byte[] uuidBytes = targetUUID.toString().getBytes(StandardCharsets.UTF_8); byte[] msgBytes = message.getBytes(StandardCharsets.UTF_8); ByteArrayDataOutput inner = ByteStreams.newDataOutput(); inner.writeByte(TYPE_PLAYER_MSG); inner.writeShort(uuidBytes.length); inner.write(uuidBytes); inner.writeShort(msgBytes.length); inner.write(msgBytes); byte[] innerBytes = inner.toByteArray(); ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.writeUTF("Forward"); out.writeUTF("ALL"); out.writeUTF(CUSTOM_CHANNEL); out.writeShort(innerBytes.length); out.write(innerBytes); messenger.sendPluginMessage(plugin, BUNGEE_CHANNEL, out.toByteArray()); } // ─────────────────────────── Eingehende Nachrichten ──────────────────── @Override public void onPluginMessageReceived(String channel, Player player, byte[] data) { if (!CUSTOM_CHANNEL.equals(channel)) return; if (plugin.isDebug()) { plugin.getLogger().info("[DEBUG][BungeeMessenger] Paket empfangen auf " + channel + ", " + data.length + " Bytes"); } try { ByteArrayDataInput in = ByteStreams.newDataInput(data); byte type = in.readByte(); if (type == TYPE_TEAM_NOTIFY) { // Rest der Bytes = UTF-8-kodierte Nachricht int len = data.length - 1; byte[] msgBytes = new byte[len]; in.readFully(msgBytes); String message = new String(msgBytes, StandardCharsets.UTF_8); if (plugin.isDebug()) { plugin.getLogger().info("[DEBUG][BungeeMessenger] TEAM_NOTIFY empfangen: " + message); } // Im Hauptthread an lokale Supporter zustellen Bukkit.getScheduler().runTask(plugin, () -> Bukkit.getOnlinePlayers().stream() .filter(p -> p.hasPermission("ticket.support") || p.hasPermission("ticket.admin")) .forEach(p -> p.sendMessage(message)) ); } else if (type == TYPE_PLAYER_MSG) { int uuidLen = in.readShort(); byte[] uuidBytes = new byte[uuidLen]; in.readFully(uuidBytes); UUID targetUUID = UUID.fromString(new String(uuidBytes, StandardCharsets.UTF_8)); int msgLen = in.readShort(); byte[] msgBytes = new byte[msgLen]; in.readFully(msgBytes); String message = new String(msgBytes, StandardCharsets.UTF_8); if (plugin.isDebug()) { plugin.getLogger().info("[DEBUG][BungeeMessenger] PLAYER_MSG empfangen für: " + targetUUID); } Bukkit.getScheduler().runTask(plugin, () -> { Player target = Bukkit.getPlayer(targetUUID); if (target != null && target.isOnline()) { target.sendMessage(message); } }); } else { plugin.getLogger().warning("[BungeeMessenger] Unbekannter Pakettyp: " + type); } } catch (Exception e) { plugin.getLogger().warning("[BungeeMessenger] Fehler beim Verarbeiten einer Plugin-Message: " + e.getMessage()); if (plugin.isDebug()) e.printStackTrace(); } } // ─────────────────────────── Hilfsmethoden ───────────────────────────── /** * Baut und sendet ein Forward-ALL-Paket mit TYPE_TEAM_NOTIFY. */ private void sendForwardPacket(Player messenger, String message) { byte[] msgBytes = message.getBytes(StandardCharsets.UTF_8); ByteArrayDataOutput inner = ByteStreams.newDataOutput(); inner.writeByte(TYPE_TEAM_NOTIFY); inner.write(msgBytes); byte[] innerBytes = inner.toByteArray(); ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.writeUTF("Forward"); out.writeUTF("ALL"); out.writeUTF(CUSTOM_CHANNEL); out.writeShort(innerBytes.length); out.write(innerBytes); messenger.sendPluginMessage(plugin, BUNGEE_CHANNEL, out.toByteArray()); } /** * Gibt einen beliebigen online Spieler zurück der als "Bote" für Plugin-Messages * verwendet werden kann. BungeeCord verlangt einen Spieler als Absender. */ private Player getAnyOnlinePlayer() { Collection online = Bukkit.getOnlinePlayers(); return online.isEmpty() ? null : online.iterator().next(); } }