diff --git a/src/main/java/de/teleportsuite/bungeemessaging/BungeeMessenger.java b/src/main/java/de/teleportsuite/bungeemessaging/BungeeMessenger.java index ac6a746..85efa30 100644 --- a/src/main/java/de/teleportsuite/bungeemessaging/BungeeMessenger.java +++ b/src/main/java/de/teleportsuite/bungeemessaging/BungeeMessenger.java @@ -5,6 +5,14 @@ import org.bukkit.entity.Player; import org.bukkit.plugin.messaging.PluginMessageListener; import java.io.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; /** * Handles BungeeCord Plugin Messaging. @@ -22,12 +30,15 @@ public class BungeeMessenger implements PluginMessageListener { private static final String BUNGEE_CHANNEL = "BungeeCord"; private static final String TS_CHANNEL = "teleportsuite:tp"; private final TeleportSuite plugin; + private final Map>> pendingServerLookups = new HashMap<>(); + private final Map>>> pendingPlayerListLookups = new HashMap<>(); public BungeeMessenger(TeleportSuite plugin) { this.plugin = plugin; } public void register() { plugin.getServer().getMessenger().registerOutgoingPluginChannel(plugin, BUNGEE_CHANNEL); plugin.getServer().getMessenger().registerOutgoingPluginChannel(plugin, TS_CHANNEL); + plugin.getServer().getMessenger().registerIncomingPluginChannel(plugin, BUNGEE_CHANNEL, this); plugin.getServer().getMessenger().registerIncomingPluginChannel(plugin, TS_CHANNEL, this); } @@ -36,6 +47,199 @@ public class BungeeMessenger implements PluginMessageListener { plugin.getServer().getMessenger().unregisterIncomingPluginChannel(plugin); } + public void requestPlayerServer(Player sender, String targetPlayer, Consumer callback) { + if (sender == null || !sender.isOnline()) { + callback.accept(null); + return; + } + + String key = targetPlayer.toLowerCase(Locale.ROOT); + pendingServerLookups.computeIfAbsent(key, k -> new ArrayList<>()).add(callback); + + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(bos)) { + out.writeUTF("GetPlayerServer"); + out.writeUTF(targetPlayer); + sender.sendPluginMessage(plugin, BUNGEE_CHANNEL, bos.toByteArray()); + } catch (IOException e) { + plugin.getLogger().warning("GetPlayerServer Fehler: " + e.getMessage()); + flushServerLookupCallbacks(key, null); + } + } + + public void requestPlayerList(Player sender, String serverScope, Consumer> callback) { + if (sender == null || !sender.isOnline()) { + callback.accept(List.of()); + return; + } + + String scope = serverScope == null || serverScope.isBlank() ? "ALL" : serverScope; + String key = scope.toUpperCase(Locale.ROOT); + pendingPlayerListLookups.computeIfAbsent(key, k -> new ArrayList<>()).add(callback); + + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(bos)) { + out.writeUTF("PlayerList"); + out.writeUTF(scope); + sender.sendPluginMessage(plugin, BUNGEE_CHANNEL, bos.toByteArray()); + } catch (IOException e) { + plugin.getLogger().warning("PlayerList Fehler: " + e.getMessage()); + flushPlayerListCallbacks(key, List.of()); + } + } + + public void teleportAllPlayersToLocalPlayer(Player requester) { + String requesterName = requester.getName(); + requestPlayerList(requester, "ALL", players -> { + if (players == null || players.isEmpty()) { + requester.sendMessage("§cKeine Spieler gefunden."); + return; + } + + Set seen = new HashSet<>(); + for (String playerName : players) { + if (playerName == null || playerName.isBlank()) continue; + if (playerName.equalsIgnoreCase(requesterName)) continue; + if (!seen.add(playerName.toLowerCase(Locale.ROOT))) continue; + teleportPlayerToLocalPlayer(requester, playerName, requesterName); + } + + requester.sendMessage("§aAlle verfügbaren Spieler im Netzwerk werden zu dir teleportiert."); + }); + } + + public void teleportToPlayer(Player requester, String targetPlayerName) { + requestPlayerServer(requester, targetPlayerName, server -> { + if (server == null || server.isBlank()) { + requester.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", targetPlayerName)); + return; + } + + String localServer = plugin.getConfigManager().getServerName(); + if (server.equalsIgnoreCase(localServer)) { + Player target = plugin.getServer().getPlayerExact(targetPlayerName); + if (target == null || !target.isOnline()) { + requester.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", targetPlayerName)); + return; + } + plugin.getTeleportManager().teleport( + requester, + new de.teleportsuite.models.TeleportLocation(target.getLocation(), localServer) + ); + return; + } + + sendPlayerToPlayerPayload(requester, requester.getName(), targetPlayerName, server); + connectPlayerOnly(requester, server); + requester.sendMessage(plugin.getConfigManager().getMessage("teleport-success")); + }); + } + + public void teleportPlayerToLocalPlayer(Player requester, String moverPlayerName, String localTargetPlayerName) { + requestPlayerServer(requester, moverPlayerName, moverServer -> { + if (moverServer == null || moverServer.isBlank()) { + requester.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", moverPlayerName)); + return; + } + + String localServer = plugin.getConfigManager().getServerName(); + if (moverServer.equalsIgnoreCase(localServer)) { + Player mover = plugin.getServer().getPlayerExact(moverPlayerName); + Player target = plugin.getServer().getPlayerExact(localTargetPlayerName); + if (mover == null || !mover.isOnline()) { + requester.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", moverPlayerName)); + return; + } + if (target == null || !target.isOnline()) { + requester.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", localTargetPlayerName)); + return; + } + plugin.getTeleportManager().teleport( + mover, + new de.teleportsuite.models.TeleportLocation(target.getLocation(), localServer) + ); + return; + } + + sendPlayerToPlayerPayload(requester, moverPlayerName, localTargetPlayerName, localServer); + connectOtherPlayer(requester, moverPlayerName, localServer); + requester.sendMessage(plugin.getConfigManager().getMessage("teleport-success")); + }); + } + + public void teleportAnyPlayerToAnyPlayer(Player requester, String moverPlayerName, String targetPlayerName) { + requestPlayerServer(requester, targetPlayerName, targetServer -> { + if (targetServer == null || targetServer.isBlank()) { + requester.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", targetPlayerName)); + return; + } + + requestPlayerServer(requester, moverPlayerName, moverServer -> { + if (moverServer == null || moverServer.isBlank()) { + requester.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", moverPlayerName)); + return; + } + + String localServer = plugin.getConfigManager().getServerName(); + if (moverServer.equalsIgnoreCase(localServer) && targetServer.equalsIgnoreCase(localServer)) { + Player mover = plugin.getServer().getPlayerExact(moverPlayerName); + Player target = plugin.getServer().getPlayerExact(targetPlayerName); + if (mover == null || !mover.isOnline()) { + requester.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", moverPlayerName)); + return; + } + if (target == null || !target.isOnline()) { + requester.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", targetPlayerName)); + return; + } + plugin.getTeleportManager().teleport( + mover, + new de.teleportsuite.models.TeleportLocation(target.getLocation(), localServer) + ); + return; + } + + sendPlayerToPlayerPayload(requester, moverPlayerName, targetPlayerName, targetServer); + if (!moverServer.equalsIgnoreCase(targetServer)) { + connectOtherPlayer(requester, moverPlayerName, targetServer); + } + requester.sendMessage(plugin.getConfigManager().getMessage("teleport-success")); + }); + }); + } + + public void sendTpaRequestToPlayer(Player sender, String targetPlayerName) { + sendForwardToPlayer(sender, targetPlayerName, out -> { + out.writeUTF("TPA_REQUEST"); + out.writeUTF(sender.getName()); + out.writeUTF(targetPlayerName); + }); + } + + public void sendTpaAcceptToPlayer(Player sender, String requesterName) { + sendForwardToPlayer(sender, requesterName, out -> { + out.writeUTF("TPA_ACCEPT"); + out.writeUTF(requesterName); + out.writeUTF(sender.getName()); + }); + } + + public void sendTpaDenyToPlayer(Player sender, String requesterName) { + sendForwardToPlayer(sender, requesterName, out -> { + out.writeUTF("TPA_DENY"); + out.writeUTF(requesterName); + out.writeUTF(sender.getName()); + }); + } + + public void sendTpaExpiredToPlayer(Player sender, String targetPlayerName) { + sendForwardToPlayer(sender, targetPlayerName, out -> { + out.writeUTF("TPA_EXPIRED"); + out.writeUTF(sender.getName()); + out.writeUTF(targetPlayerName); + }); + } + /** * Connect a player to another BungeeCord server. * Also sends a plugin message so the target server knows where to teleport the player. @@ -45,6 +249,10 @@ public class BungeeMessenger implements PluginMessageListener { sendTeleportPayload(player, player.getName(), server, world, x, y, z, yaw, pitch); // 2) Switch server via BungeeCord + connectPlayerOnly(player, server); + } + + private void connectPlayerOnly(Player player, String server) { try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(bos)) { out.writeUTF("Connect"); @@ -55,6 +263,18 @@ public class BungeeMessenger implements PluginMessageListener { } } + private void connectOtherPlayer(Player anchor, String playerName, String server) { + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(bos)) { + out.writeUTF("ConnectOther"); + out.writeUTF(playerName); + out.writeUTF(server); + anchor.sendPluginMessage(plugin, BUNGEE_CHANNEL, bos.toByteArray()); + } catch (IOException e) { + plugin.getLogger().warning("BungeeCord ConnectOther Fehler: " + e.getMessage()); + } + } + private void sendTeleportPayload(Player sender, String targetPlayer, String server, String world, double x, double y, double z, float yaw, float pitch) { try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); @@ -65,6 +285,7 @@ public class BungeeMessenger implements PluginMessageListener { // Sub-payload ByteArrayOutputStream sub = new ByteArrayOutputStream(); DataOutputStream subOut = new DataOutputStream(sub); + subOut.writeUTF("LOCATION"); subOut.writeUTF(targetPlayer); subOut.writeUTF(world); subOut.writeDouble(x); @@ -81,17 +302,196 @@ public class BungeeMessenger implements PluginMessageListener { } } + private void sendPlayerToPlayerPayload(Player sender, String moverPlayer, String targetPlayer, String targetServer) { + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(bos)) { + out.writeUTF("Forward"); + out.writeUTF(targetServer); + out.writeUTF(TS_CHANNEL); + + ByteArrayOutputStream sub = new ByteArrayOutputStream(); + DataOutputStream subOut = new DataOutputStream(sub); + subOut.writeUTF("TP_TO_PLAYER"); + subOut.writeUTF(moverPlayer); + subOut.writeUTF(targetPlayer); + + byte[] subBytes = sub.toByteArray(); + out.writeShort(subBytes.length); + out.write(subBytes); + sender.sendPluginMessage(plugin, BUNGEE_CHANNEL, bos.toByteArray()); + } catch (IOException e) { + plugin.getLogger().warning("TS Player->Player Forward Fehler: " + e.getMessage()); + } + } + + private void sendForwardToPlayer(Player sender, String targetPlayer, IOConsumer payloadWriter) { + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(bos)) { + out.writeUTF("ForwardToPlayer"); + out.writeUTF(targetPlayer); + out.writeUTF(TS_CHANNEL); + + ByteArrayOutputStream sub = new ByteArrayOutputStream(); + DataOutputStream subOut = new DataOutputStream(sub); + payloadWriter.accept(subOut); + + byte[] subBytes = sub.toByteArray(); + out.writeShort(subBytes.length); + out.write(subBytes); + sender.sendPluginMessage(plugin, BUNGEE_CHANNEL, bos.toByteArray()); + } catch (IOException e) { + plugin.getLogger().warning("TS ForwardToPlayer Fehler: " + e.getMessage()); + } + } + + private void schedulePlayerToPlayerTeleport(String moverName, String targetName) { + int maxAttempts = 100; + final int[] attempts = {0}; + final int[] taskId = {0}; + + taskId[0] = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, () -> { + attempts[0]++; + Player mover = plugin.getServer().getPlayerExact(moverName); + Player target = plugin.getServer().getPlayerExact(targetName); + + if (mover != null && mover.isOnline() && target != null && target.isOnline()) { + plugin.getTeleportManager().teleport( + mover, + new de.teleportsuite.models.TeleportLocation(target.getLocation(), plugin.getConfigManager().getServerName()), + false + ); + plugin.getServer().getScheduler().cancelTask(taskId[0]); + return; + } + + if (attempts[0] >= maxAttempts) { + plugin.getServer().getScheduler().cancelTask(taskId[0]); + } + }, 10L, 10L); + } + + private void flushServerLookupCallbacks(String key, String server) { + List> callbacks = pendingServerLookups.remove(key); + if (callbacks == null) return; + for (Consumer callback : callbacks) { + callback.accept(server); + } + } + + private void flushPlayerListCallbacks(String key, List players) { + List>> callbacks = pendingPlayerListLookups.remove(key); + if (callbacks == null) return; + for (Consumer> callback : callbacks) { + callback.accept(players); + } + } + @Override public void onPluginMessageReceived(String channel, Player player, byte[] message) { - if (!channel.equals(TS_CHANNEL)) return; + if (channel.equals(BUNGEE_CHANNEL)) { + handleBungeeMessage(message); + return; + } + + if (channel.equals(TS_CHANNEL)) { + handleTeleportSuiteMessage(message); + } + } + + private void handleBungeeMessage(byte[] message) { try (DataInputStream in = new DataInputStream(new ByteArrayInputStream(message))) { - String targetName = in.readUTF(); + String subChannel = in.readUTF(); + if ("GetPlayerServer".equals(subChannel)) { + String playerName = in.readUTF(); + String serverName = in.readUTF(); + if ("null".equalsIgnoreCase(serverName) || serverName.isBlank()) { + serverName = null; + } + flushServerLookupCallbacks(playerName.toLowerCase(Locale.ROOT), serverName); + return; + } + + if ("PlayerList".equals(subChannel)) { + String scope = in.readUTF(); + String playerCsv = in.readUTF(); + List players = new ArrayList<>(); + if (playerCsv != null && !playerCsv.isBlank()) { + for (String name : playerCsv.split(",")) { + String trimmed = name.trim(); + if (!trimmed.isEmpty()) players.add(trimmed); + } + } + flushPlayerListCallbacks(scope.toUpperCase(Locale.ROOT), players); + return; + } + } catch (IOException e) { + plugin.getLogger().warning("Fehler beim Lesen der Bungee-Nachricht: " + e.getMessage()); + } + } + + private void handleTeleportSuiteMessage(byte[] message) { + try (DataInputStream in = new DataInputStream(new ByteArrayInputStream(message))) { + String payloadType = in.readUTF(); + + if ("LOCATION".equals(payloadType)) { + String targetName = in.readUTF(); + String world = in.readUTF(); + double x = in.readDouble(), y = in.readDouble(), z = in.readDouble(); + float yaw = in.readFloat(), pitch = in.readFloat(); + + plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { + Player target = plugin.getServer().getPlayerExact(targetName); + if (target == null || !target.isOnline()) return; + de.teleportsuite.models.TeleportLocation loc = + new de.teleportsuite.models.TeleportLocation(world, x, y, z, yaw, pitch, + plugin.getConfigManager().getServerName()); + plugin.getTeleportManager().teleport(target, loc, false); + }, 20L); + return; + } + + if ("TP_TO_PLAYER".equals(payloadType)) { + String moverName = in.readUTF(); + String targetName = in.readUTF(); + schedulePlayerToPlayerTeleport(moverName, targetName); + return; + } + + if ("TPA_REQUEST".equals(payloadType)) { + String requesterName = in.readUTF(); + String targetName = in.readUTF(); + plugin.getTeleportManager().receiveCrossServerTpaRequest(requesterName, targetName); + return; + } + + if ("TPA_ACCEPT".equals(payloadType)) { + String requesterName = in.readUTF(); + String targetName = in.readUTF(); + plugin.getTeleportManager().receiveCrossServerTpaAccepted(requesterName, targetName); + return; + } + + if ("TPA_DENY".equals(payloadType)) { + String requesterName = in.readUTF(); + String targetName = in.readUTF(); + plugin.getTeleportManager().receiveCrossServerTpaDenied(requesterName, targetName); + return; + } + + if ("TPA_EXPIRED".equals(payloadType)) { + String requesterName = in.readUTF(); + String targetName = in.readUTF(); + plugin.getTeleportManager().receiveCrossServerTpaExpired(requesterName, targetName); + return; + } + + // Backward compatibility with old payload format + String targetName = payloadType; String world = in.readUTF(); double x = in.readDouble(), y = in.readDouble(), z = in.readDouble(); float yaw = in.readFloat(), pitch = in.readFloat(); - plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { - Player target = plugin.getServer().getPlayer(targetName); + Player target = plugin.getServer().getPlayerExact(targetName); if (target == null || !target.isOnline()) return; de.teleportsuite.models.TeleportLocation loc = new de.teleportsuite.models.TeleportLocation(world, x, y, z, yaw, pitch, @@ -102,4 +502,9 @@ public class BungeeMessenger implements PluginMessageListener { plugin.getLogger().warning("Fehler beim Lesen der TS-Nachricht: " + e.getMessage()); } } + + @FunctionalInterface + private interface IOConsumer { + void accept(T value) throws IOException; + } } diff --git a/src/main/java/de/teleportsuite/commands/TpAllCommand.java b/src/main/java/de/teleportsuite/commands/TpAllCommand.java index d1e12ff..b8be009 100644 --- a/src/main/java/de/teleportsuite/commands/TpAllCommand.java +++ b/src/main/java/de/teleportsuite/commands/TpAllCommand.java @@ -12,6 +12,12 @@ public class TpAllCommand implements CommandExecutor { if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } Player p = (Player) sender; if (!p.hasPermission("teleportsuite.tpall")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + + if (plugin.getConfigManager().isBungeeEnabled() && plugin.getBungeeMessenger() != null) { + plugin.getBungeeMessenger().teleportAllPlayersToLocalPlayer(p); + return true; + } + TeleportLocation dest = new TeleportLocation(p.getLocation(), plugin.getConfigManager().getServerName()); for (Player online : Bukkit.getOnlinePlayers()) { if (!online.equals(p)) plugin.getTeleportManager().teleport(online, dest); diff --git a/src/main/java/de/teleportsuite/commands/TpCommand.java b/src/main/java/de/teleportsuite/commands/TpCommand.java index b79a5e1..5103aa4 100644 --- a/src/main/java/de/teleportsuite/commands/TpCommand.java +++ b/src/main/java/de/teleportsuite/commands/TpCommand.java @@ -19,13 +19,27 @@ public class TpCommand implements CommandExecutor { if (args.length == 1) { Player target = Bukkit.getPlayer(args[0]); - if (target == null) { p.sendMessage(plugin.getConfigManager().getMessage("player-not-found","player",args[0])); return true; } + if (target == null) { + if (plugin.getConfigManager().isBungeeEnabled() && plugin.getBungeeMessenger() != null) { + plugin.getBungeeMessenger().teleportToPlayer(p, args[0]); + return true; + } + p.sendMessage(plugin.getConfigManager().getMessage("player-not-found","player",args[0])); + return true; + } plugin.getTeleportManager().teleport(p, new TeleportLocation(target.getLocation(), plugin.getConfigManager().getServerName())); } else { + if (!p.hasPermission("teleportsuite.admin")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } Player from = Bukkit.getPlayer(args[0]); Player to = Bukkit.getPlayer(args[1]); - if (from == null || to == null) { p.sendMessage("§cEin Spieler nicht gefunden."); return true; } - if (!p.hasPermission("teleportsuite.admin")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + if (from == null || to == null) { + if (plugin.getConfigManager().isBungeeEnabled() && plugin.getBungeeMessenger() != null) { + plugin.getBungeeMessenger().teleportAnyPlayerToAnyPlayer(p, args[0], args[1]); + return true; + } + p.sendMessage("§cEin Spieler nicht gefunden."); + return true; + } plugin.getTeleportManager().teleport(from, new TeleportLocation(to.getLocation(), plugin.getConfigManager().getServerName())); } return true; diff --git a/src/main/java/de/teleportsuite/commands/TpHereCommand.java b/src/main/java/de/teleportsuite/commands/TpHereCommand.java index fef5ba5..739cee8 100644 --- a/src/main/java/de/teleportsuite/commands/TpHereCommand.java +++ b/src/main/java/de/teleportsuite/commands/TpHereCommand.java @@ -17,7 +17,14 @@ public class TpHereCommand implements CommandExecutor { if (!p.hasPermission("teleportsuite.tphere")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } if (args.length < 1) { p.sendMessage("§cVerwendung: /tphere "); return true; } Player target = Bukkit.getPlayer(args[0]); - if (target == null) { p.sendMessage(plugin.getConfigManager().getMessage("player-not-found","player",args[0])); return true; } + if (target == null) { + if (plugin.getConfigManager().isBungeeEnabled() && plugin.getBungeeMessenger() != null) { + plugin.getBungeeMessenger().teleportPlayerToLocalPlayer(p, args[0], p.getName()); + return true; + } + p.sendMessage(plugin.getConfigManager().getMessage("player-not-found","player",args[0])); + return true; + } plugin.getTeleportManager().teleport(target, new TeleportLocation(p.getLocation(), plugin.getConfigManager().getServerName())); return true; } diff --git a/src/main/java/de/teleportsuite/commands/TpaCommand.java b/src/main/java/de/teleportsuite/commands/TpaCommand.java index fbc773b..ef4d323 100644 --- a/src/main/java/de/teleportsuite/commands/TpaCommand.java +++ b/src/main/java/de/teleportsuite/commands/TpaCommand.java @@ -15,9 +15,16 @@ public class TpaCommand implements CommandExecutor { Player p = (Player) sender; if (!p.hasPermission("teleportsuite.tpa")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } if (args.length < 1) { p.sendMessage("§cVerwendung: /tpa "); return true; } + if (p.getName().equalsIgnoreCase(args[0])) { p.sendMessage("§cDu kannst dir nicht selbst eine Anfrage senden."); return true; } Player target = Bukkit.getPlayer(args[0]); - if (target == null) { p.sendMessage(plugin.getConfigManager().getMessage("player-not-found","player",args[0])); return true; } - if (target.equals(p)) { p.sendMessage("§cDu kannst dir nicht selbst eine Anfrage senden."); return true; } + if (target == null) { + if (plugin.getConfigManager().isBungeeEnabled() && plugin.getBungeeMessenger() != null) { + plugin.getTeleportManager().sendTpaRequest(p, args[0]); + return true; + } + p.sendMessage(plugin.getConfigManager().getMessage("player-not-found","player",args[0])); + return true; + } plugin.getTeleportManager().sendTpaRequest(p, target); return true; } diff --git a/src/main/java/de/teleportsuite/managers/TeleportManager.java b/src/main/java/de/teleportsuite/managers/TeleportManager.java index 03a2638..6a6ec00 100644 --- a/src/main/java/de/teleportsuite/managers/TeleportManager.java +++ b/src/main/java/de/teleportsuite/managers/TeleportManager.java @@ -13,6 +13,9 @@ public class TeleportManager { // Pending TPA requests: requester -> target private final Map tpaRequests = new HashMap<>(); private final Map requestTimestamps = new HashMap<>(); + // Pending cross-server TPA requests: requesterName -> targetName + private final Map crossServerTpaRequests = new HashMap<>(); + private final Map crossServerRequestTimestamps = new HashMap<>(); // Cooldowns private final Map cooldowns = new HashMap<>(); // Warmup tasks @@ -122,6 +125,101 @@ public class TeleportManager { }, timeout * 20L); } + public void sendTpaRequest(Player from, String targetName) { + if (plugin.getBungeeMessenger() == null) { + from.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", targetName)); + return; + } + + plugin.getBungeeMessenger().requestPlayerServer(from, targetName, server -> { + if (server == null || server.isBlank()) { + from.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", targetName)); + return; + } + + String localServer = plugin.getConfigManager().getServerName(); + if (server.equalsIgnoreCase(localServer)) { + Player localTarget = Bukkit.getPlayerExact(targetName); + if (localTarget == null || !localTarget.isOnline()) { + from.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", targetName)); + return; + } + sendTpaRequest(from, localTarget); + return; + } + + String requesterKey = from.getName(); + crossServerTpaRequests.put(requesterKey, targetName); + crossServerRequestTimestamps.put(requesterKey, System.currentTimeMillis()); + + plugin.getBungeeMessenger().sendTpaRequestToPlayer(from, targetName); + from.sendMessage(plugin.getConfigManager().getMessage("tpa-sent", "player", targetName)); + + int timeout = plugin.getConfigManager().getRequestTimeout(); + Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> { + if (!crossServerTpaRequests.containsKey(requesterKey)) return; + crossServerTpaRequests.remove(requesterKey); + crossServerRequestTimestamps.remove(requesterKey); + + Player requester = Bukkit.getPlayerExact(from.getName()); + if (requester != null && requester.isOnline()) { + requester.sendMessage(plugin.getConfigManager().getMessage("tpa-expired")); + } + if (plugin.getBungeeMessenger() != null) { + plugin.getBungeeMessenger().sendTpaExpiredToPlayer(from, targetName); + } + }, timeout * 20L); + }); + } + + public void receiveCrossServerTpaRequest(String requesterName, String targetName) { + Player target = Bukkit.getPlayerExact(targetName); + if (target == null || !target.isOnline()) return; + + crossServerTpaRequests.put(requesterName, targetName); + crossServerRequestTimestamps.put(requesterName, System.currentTimeMillis()); + target.sendMessage(plugin.getConfigManager().getMessage("tpa-received", "player", requesterName)); + } + + public void receiveCrossServerTpaAccepted(String requesterName, String targetName) { + Player requester = Bukkit.getPlayerExact(requesterName); + if (requester == null || !requester.isOnline()) return; + + String requesterKey = findRequesterKey(requesterName); + if (requesterKey != null) { + crossServerTpaRequests.remove(requesterKey); + crossServerRequestTimestamps.remove(requesterKey); + } + requester.sendMessage(plugin.getConfigManager().getMessage("tpa-accepted", "player", targetName)); + + if (plugin.getBungeeMessenger() != null) { + plugin.getBungeeMessenger().teleportToPlayer(requester, targetName); + } + } + + public void receiveCrossServerTpaDenied(String requesterName, String targetName) { + Player requester = Bukkit.getPlayerExact(requesterName); + if (requester == null || !requester.isOnline()) return; + + String requesterKey = findRequesterKey(requesterName); + if (requesterKey != null) { + crossServerTpaRequests.remove(requesterKey); + crossServerRequestTimestamps.remove(requesterKey); + } + requester.sendMessage(plugin.getConfigManager().getMessage("tpa-denied", "player", targetName)); + } + + public void receiveCrossServerTpaExpired(String requesterName, String targetName) { + Player target = Bukkit.getPlayerExact(targetName); + if (target == null || !target.isOnline()) return; + + String requesterKey = findRequesterKey(requesterName); + if (requesterKey != null && target.getName().equalsIgnoreCase(crossServerTpaRequests.get(requesterKey))) { + crossServerTpaRequests.remove(requesterKey); + crossServerRequestTimestamps.remove(requesterKey); + } + } + public boolean acceptTpa(Player target) { UUID requester = null; for (Map.Entry entry : tpaRequests.entrySet()) { @@ -130,15 +228,27 @@ public class TeleportManager { break; } } - if (requester == null) return false; + if (requester != null) { + Player from = Bukkit.getPlayer(requester); + tpaRequests.remove(requester); + requestTimestamps.remove(requester); - Player from = Bukkit.getPlayer(requester); - tpaRequests.remove(requester); - requestTimestamps.remove(requester); + if (from != null && from.isOnline()) { + from.sendMessage(plugin.getConfigManager().getMessage("tpa-accepted", "player", target.getName())); + teleport(from, new TeleportLocation(target.getLocation(), plugin.getConfigManager().getServerName())); + } + return true; + } - if (from != null && from.isOnline()) { - from.sendMessage(plugin.getConfigManager().getMessage("tpa-accepted", "player", target.getName())); - teleport(from, new TeleportLocation(target.getLocation(), plugin.getConfigManager().getServerName())); + String requesterName = findRequesterByTarget(target.getName()); + + if (requesterName == null) return false; + + crossServerTpaRequests.remove(requesterName); + crossServerRequestTimestamps.remove(requesterName); + + if (plugin.getBungeeMessenger() != null) { + plugin.getBungeeMessenger().sendTpaAcceptToPlayer(target, requesterName); } return true; } @@ -151,14 +261,45 @@ public class TeleportManager { break; } } - if (requester == null) return false; + if (requester != null) { + Player from = Bukkit.getPlayer(requester); + tpaRequests.remove(requester); + requestTimestamps.remove(requester); - Player from = Bukkit.getPlayer(requester); - tpaRequests.remove(requester); - requestTimestamps.remove(requester); + if (from != null && from.isOnline()) { + from.sendMessage(plugin.getConfigManager().getMessage("tpa-denied", "player", target.getName())); + } + return true; + } - if (from != null && from.isOnline()) - from.sendMessage(plugin.getConfigManager().getMessage("tpa-denied", "player", target.getName())); + String requesterName = findRequesterByTarget(target.getName()); + + if (requesterName == null) return false; + + crossServerTpaRequests.remove(requesterName); + crossServerRequestTimestamps.remove(requesterName); + + if (plugin.getBungeeMessenger() != null) { + plugin.getBungeeMessenger().sendTpaDenyToPlayer(target, requesterName); + } return true; } + + private String findRequesterByTarget(String targetName) { + for (Map.Entry entry : crossServerTpaRequests.entrySet()) { + if (targetName.equalsIgnoreCase(entry.getValue())) { + return entry.getKey(); + } + } + return null; + } + + private String findRequesterKey(String requesterName) { + for (String key : crossServerTpaRequests.keySet()) { + if (key.equalsIgnoreCase(requesterName)) { + return key; + } + } + return null; + } }