263 lines
11 KiB
Java
263 lines
11 KiB
Java
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<? extends Player> online = Bukkit.getOnlinePlayers();
|
||
return online.isEmpty() ? null : online.iterator().next();
|
||
}
|
||
} |