diff --git a/src/main/java/de/teleportsuite/bungeemessaging/BungeeMessenger.java b/src/main/java/de/teleportsuite/bungeemessaging/BungeeMessenger.java index 85efa30..5e2bb4e 100644 --- a/src/main/java/de/teleportsuite/bungeemessaging/BungeeMessenger.java +++ b/src/main/java/de/teleportsuite/bungeemessaging/BungeeMessenger.java @@ -16,14 +16,16 @@ import java.util.function.Consumer; /** * Handles BungeeCord Plugin Messaging. - * Sends players to other servers and transmits target coordinates - * so a receiving TeleportSuite instance can teleport them on arrival. * - * Channel "teleportsuite:tp" payload format (DataOutputStream): - * String targetPlayer - * String world - * Double x, y, z - * Float yaw, pitch + * Cross-server teleport flow (fixed): + * 1. Query the target server for the exact coordinates of the target player (QUERY_LOCATION). + * 2. Target server replies with LOCATION_RESPONSE (world, x, y, z, yaw, pitch). + * 3. We send a LOCATION payload to the target server so it teleports the mover on arrival. + * 4. We send the BungeeCord Connect/ConnectOther to actually switch the server. + * + * Step 3 happens *before* step 4 to avoid the race condition where the player + * arrives on the target server before the teleport payload does. + * A 1-second timeout falls back to the TP_TO_PLAYER polling mechanism. */ public class BungeeMessenger implements PluginMessageListener { @@ -32,6 +34,8 @@ public class BungeeMessenger implements PluginMessageListener { private final TeleportSuite plugin; private final Map>> pendingServerLookups = new HashMap<>(); private final Map>>> pendingPlayerListLookups = new HashMap<>(); + // key = targetPlayer.toLowerCase() + private final Map>> pendingLocationLookups = new HashMap<>(); public BungeeMessenger(TeleportSuite plugin) { this.plugin = plugin; } @@ -47,6 +51,10 @@ public class BungeeMessenger implements PluginMessageListener { plugin.getServer().getMessenger().unregisterIncomingPluginChannel(plugin); } + // ------------------------------------------------------------------------- + // Player-server & player-list lookups (unchanged) + // ------------------------------------------------------------------------- + public void requestPlayerServer(Player sender, String targetPlayer, Consumer callback) { if (sender == null || !sender.isOnline()) { callback.accept(null); @@ -88,6 +96,61 @@ public class BungeeMessenger implements PluginMessageListener { } } + // ------------------------------------------------------------------------- + // Cross-server location query + // ------------------------------------------------------------------------- + + /** + * Sends a QUERY_LOCATION to {@code targetServer} asking for the exact + * coordinates of {@code targetPlayer}. The callback receives a Bukkit + * Location on success, or null if the player is not found / timeout fires. + */ + private void requestPlayerLocationOnServer(Player anchor, String targetPlayer, + String targetServer, + Consumer callback) { + String key = targetPlayer.toLowerCase(Locale.ROOT); + pendingLocationLookups.computeIfAbsent(key, k -> new ArrayList<>()).add(callback); + + 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("QUERY_LOCATION"); + subOut.writeUTF(targetPlayer); + subOut.writeUTF(plugin.getConfigManager().getServerName()); // reply-to + + byte[] subBytes = sub.toByteArray(); + out.writeShort(subBytes.length); + out.write(subBytes); + anchor.sendPluginMessage(plugin, BUNGEE_CHANNEL, bos.toByteArray()); + } catch (IOException e) { + plugin.getLogger().warning("QUERY_LOCATION Fehler: " + e.getMessage()); + flushLocationLookupCallbacks(key, null); + return; + } + + // 1-second timeout → fall back to TP_TO_PLAYER polling + plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { + if (pendingLocationLookups.containsKey(key)) { + flushLocationLookupCallbacks(key, null); + } + }, 20L); + } + + private void flushLocationLookupCallbacks(String key, org.bukkit.Location loc) { + List> callbacks = pendingLocationLookups.remove(key); + if (callbacks == null) return; + for (Consumer cb : callbacks) cb.accept(loc); + } + + // ------------------------------------------------------------------------- + // Public teleport helpers + // ------------------------------------------------------------------------- + public void teleportAllPlayersToLocalPlayer(Player requester) { String requesterName = requester.getName(); requestPlayerList(requester, "ALL", players -> { @@ -108,6 +171,15 @@ public class BungeeMessenger implements PluginMessageListener { }); } + /** + * Teleports {@code requester} directly to {@code targetPlayerName}. + * + * If the target is on another server: + * 1. Ask that server for the target's exact position (QUERY_LOCATION). + * 2. Send a LOCATION payload → player spawns at the right spot immediately. + * 3. Switch the requester via BungeeCord Connect. + * Fallback: if the query times out, send TP_TO_PLAYER (polling mechanism). + */ public void teleportToPlayer(Player requester, String targetPlayerName) { requestPlayerServer(requester, targetPlayerName, server -> { if (server == null || server.isBlank()) { @@ -117,6 +189,7 @@ public class BungeeMessenger implements PluginMessageListener { String localServer = plugin.getConfigManager().getServerName(); if (server.equalsIgnoreCase(localServer)) { + // Same server — direct teleport Player target = plugin.getServer().getPlayerExact(targetPlayerName); if (target == null || !target.isOnline()) { requester.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", targetPlayerName)); @@ -129,12 +202,28 @@ public class BungeeMessenger implements PluginMessageListener { return; } - sendPlayerToPlayerPayload(requester, requester.getName(), targetPlayerName, server); - connectPlayerOnly(requester, server); - requester.sendMessage(plugin.getConfigManager().getMessage("teleport-success")); + // Cross-server: get exact coordinates first, then switch + requestPlayerLocationOnServer(requester, targetPlayerName, server, loc -> { + if (loc != null) { + // Precise LOCATION payload → no intermediate spawn + sendTeleportPayload(requester, requester.getName(), server, + loc.getWorld().getName(), loc.getX(), loc.getY(), loc.getZ(), + loc.getYaw(), loc.getPitch()); + } else { + // Fallback: target server will poll for both players + sendPlayerToPlayerPayload(requester, requester.getName(), targetPlayerName, server); + } + connectPlayerOnly(requester, server); + requester.sendMessage(plugin.getConfigManager().getMessage("teleport-success")); + }); }); } + /** + * Teleports {@code moverPlayerName} (on a remote server) to the local player + * {@code localTargetPlayerName}. The local player's current coordinates are + * known, so we can send a precise LOCATION payload immediately. + */ public void teleportPlayerToLocalPlayer(Player requester, String moverPlayerName, String localTargetPlayerName) { requestPlayerServer(requester, moverPlayerName, moverServer -> { if (moverServer == null || moverServer.isBlank()) { @@ -144,7 +233,8 @@ public class BungeeMessenger implements PluginMessageListener { String localServer = plugin.getConfigManager().getServerName(); if (moverServer.equalsIgnoreCase(localServer)) { - Player mover = plugin.getServer().getPlayerExact(moverPlayerName); + // Same server — direct teleport + 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)); @@ -161,12 +251,27 @@ public class BungeeMessenger implements PluginMessageListener { return; } - sendPlayerToPlayerPayload(requester, moverPlayerName, localTargetPlayerName, localServer); + // Cross-server: the *target* is local, so we already know their position + Player localTarget = plugin.getServer().getPlayerExact(localTargetPlayerName); + if (localTarget == null || !localTarget.isOnline()) { + requester.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", localTargetPlayerName)); + return; + } + + org.bukkit.Location dest = localTarget.getLocation(); + // Send LOCATION payload to mover's current server first, then switch them + sendTeleportPayload(requester, moverPlayerName, localServer, + dest.getWorld().getName(), dest.getX(), dest.getY(), dest.getZ(), + dest.getYaw(), dest.getPitch()); connectOtherPlayer(requester, moverPlayerName, localServer); requester.sendMessage(plugin.getConfigManager().getMessage("teleport-success")); }); } + /** + * Teleports {@code moverPlayerName} to {@code targetPlayerName} when both + * may be on different servers. + */ public void teleportAnyPlayerToAnyPlayer(Player requester, String moverPlayerName, String targetPlayerName) { requestPlayerServer(requester, targetPlayerName, targetServer -> { if (targetServer == null || targetServer.isBlank()) { @@ -181,8 +286,10 @@ public class BungeeMessenger implements PluginMessageListener { } String localServer = plugin.getConfigManager().getServerName(); + + // Both on the same local server if (moverServer.equalsIgnoreCase(localServer) && targetServer.equalsIgnoreCase(localServer)) { - Player mover = plugin.getServer().getPlayerExact(moverPlayerName); + 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)); @@ -199,15 +306,46 @@ public class BungeeMessenger implements PluginMessageListener { return; } - sendPlayerToPlayerPayload(requester, moverPlayerName, targetPlayerName, targetServer); - if (!moverServer.equalsIgnoreCase(targetServer)) { - connectOtherPlayer(requester, moverPlayerName, targetServer); + // Target is on this local server — we know the coordinates + if (targetServer.equalsIgnoreCase(localServer)) { + Player localTarget = plugin.getServer().getPlayerExact(targetPlayerName); + if (localTarget == null || !localTarget.isOnline()) { + requester.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", targetPlayerName)); + return; + } + org.bukkit.Location dest = localTarget.getLocation(); + sendTeleportPayload(requester, moverPlayerName, localServer, + dest.getWorld().getName(), dest.getX(), dest.getY(), dest.getZ(), + dest.getYaw(), dest.getPitch()); + if (!moverServer.equalsIgnoreCase(localServer)) { + connectOtherPlayer(requester, moverPlayerName, localServer); + } + requester.sendMessage(plugin.getConfigManager().getMessage("teleport-success")); + return; } - requester.sendMessage(plugin.getConfigManager().getMessage("teleport-success")); + + // Target is on a third (remote) server — query their location first + requestPlayerLocationOnServer(requester, targetPlayerName, targetServer, loc -> { + if (loc != null) { + sendTeleportPayload(requester, moverPlayerName, targetServer, + loc.getWorld().getName(), loc.getX(), loc.getY(), loc.getZ(), + loc.getYaw(), loc.getPitch()); + } else { + sendPlayerToPlayerPayload(requester, moverPlayerName, targetPlayerName, targetServer); + } + if (!moverServer.equalsIgnoreCase(targetServer)) { + connectOtherPlayer(requester, moverPlayerName, targetServer); + } + requester.sendMessage(plugin.getConfigManager().getMessage("teleport-success")); + }); }); }); } + // ------------------------------------------------------------------------- + // TPA forwarding (unchanged) + // ------------------------------------------------------------------------- + public void sendTpaRequestToPlayer(Player sender, String targetPlayerName) { sendForwardToPlayer(sender, targetPlayerName, out -> { out.writeUTF("TPA_REQUEST"); @@ -240,15 +378,14 @@ public class BungeeMessenger implements PluginMessageListener { }); } - /** - * Connect a player to another BungeeCord server. - * Also sends a plugin message so the target server knows where to teleport the player. - */ - public void connectToServer(Player player, String server, String world, double x, double y, double z, float yaw, float pitch) { - // 1) Notify the target server about the pending teleport - sendTeleportPayload(player, player.getName(), server, world, x, y, z, yaw, pitch); + // ------------------------------------------------------------------------- + // Low-level BungeeCord helpers + // ------------------------------------------------------------------------- - // 2) Switch server via BungeeCord + /** Send player to another server AND pre-register their destination coordinates. */ + public void connectToServer(Player player, String server, String world, + double x, double y, double z, float yaw, float pitch) { + sendTeleportPayload(player, player.getName(), server, world, x, y, z, yaw, pitch); connectPlayerOnly(player, server); } @@ -275,14 +412,16 @@ public class BungeeMessenger implements PluginMessageListener { } } - private void sendTeleportPayload(Player sender, String targetPlayer, String server, String world, - double x, double y, double z, float yaw, float pitch) { + /** Forward a LOCATION payload to {@code server} for {@code targetPlayer}. */ + 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(); DataOutputStream out = new DataOutputStream(bos)) { out.writeUTF("Forward"); out.writeUTF(server); out.writeUTF(TS_CHANNEL); - // Sub-payload + ByteArrayOutputStream sub = new ByteArrayOutputStream(); DataOutputStream subOut = new DataOutputStream(sub); subOut.writeUTF("LOCATION"); @@ -293,6 +432,7 @@ public class BungeeMessenger implements PluginMessageListener { subOut.writeDouble(z); subOut.writeFloat(yaw); subOut.writeFloat(pitch); + byte[] subBytes = sub.toByteArray(); out.writeShort(subBytes.length); out.write(subBytes); @@ -302,7 +442,12 @@ public class BungeeMessenger implements PluginMessageListener { } } - private void sendPlayerToPlayerPayload(Player sender, String moverPlayer, String targetPlayer, String targetServer) { + /** + * Fallback: forward a TP_TO_PLAYER payload. + * The target server will poll until both players are online. + */ + private void sendPlayerToPlayerPayload(Player sender, String moverPlayer, + String targetPlayer, String targetServer) { try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(bos)) { out.writeUTF("Forward"); @@ -324,7 +469,8 @@ public class BungeeMessenger implements PluginMessageListener { } } - private void sendForwardToPlayer(Player sender, String targetPlayer, IOConsumer payloadWriter) { + private void sendForwardToPlayer(Player sender, String targetPlayer, + IOConsumer payloadWriter) { try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(bos)) { out.writeUTF("ForwardToPlayer"); @@ -344,6 +490,10 @@ public class BungeeMessenger implements PluginMessageListener { } } + // ------------------------------------------------------------------------- + // Polling fallback (kept for TP_TO_PLAYER fallback path) + // ------------------------------------------------------------------------- + private void schedulePlayerToPlayerTeleport(String moverName, String targetName) { int maxAttempts = 100; final int[] attempts = {0}; @@ -351,13 +501,14 @@ public class BungeeMessenger implements PluginMessageListener { taskId[0] = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, () -> { attempts[0]++; - Player mover = plugin.getServer().getPlayerExact(moverName); + 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()), + new de.teleportsuite.models.TeleportLocation( + target.getLocation(), plugin.getConfigManager().getServerName()), false ); plugin.getServer().getScheduler().cancelTask(taskId[0]); @@ -370,49 +521,51 @@ public class BungeeMessenger implements PluginMessageListener { }, 10L, 10L); } + // ------------------------------------------------------------------------- + // Callback flush helpers + // ------------------------------------------------------------------------- + private void flushServerLookupCallbacks(String key, String server) { List> callbacks = pendingServerLookups.remove(key); if (callbacks == null) return; - for (Consumer callback : callbacks) { - callback.accept(server); - } + 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); - } + for (Consumer> callback : callbacks) callback.accept(players); } + // ------------------------------------------------------------------------- + // Incoming message handler + // ------------------------------------------------------------------------- + @Override public void onPluginMessageReceived(String channel, Player player, byte[] message) { if (channel.equals(BUNGEE_CHANNEL)) { handleBungeeMessage(message); return; } - if (channel.equals(TS_CHANNEL)) { - handleTeleportSuiteMessage(message); + handleTeleportSuiteMessage(player, message); } } private void handleBungeeMessage(byte[] message) { try (DataInputStream in = new DataInputStream(new ByteArrayInputStream(message))) { String subChannel = in.readUTF(); + if ("GetPlayerServer".equals(subChannel)) { - String playerName = in.readUTF(); - String serverName = in.readUTF(); - if ("null".equalsIgnoreCase(serverName) || serverName.isBlank()) { - serverName = null; - } + 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 scope = in.readUTF(); String playerCsv = in.readUTF(); List players = new ArrayList<>(); if (playerCsv != null && !playerCsv.isBlank()) { @@ -422,22 +575,22 @@ public class BungeeMessenger implements PluginMessageListener { } } 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) { + private void handleTeleportSuiteMessage(Player channelPlayer, byte[] message) { try (DataInputStream in = new DataInputStream(new ByteArrayInputStream(message))) { String payloadType = in.readUTF(); + // ── LOCATION: teleport a named player to fixed coordinates ────────── if ("LOCATION".equals(payloadType)) { String targetName = in.readUTF(); - String world = in.readUTF(); + String world = in.readUTF(); double x = in.readDouble(), y = in.readDouble(), z = in.readDouble(); - float yaw = in.readFloat(), pitch = in.readFloat(); + float yaw = in.readFloat(), pitch = in.readFloat(); plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { Player target = plugin.getServer().getPlayerExact(targetName); @@ -450,46 +603,102 @@ public class BungeeMessenger implements PluginMessageListener { return; } + // ── QUERY_LOCATION: remote server asks for a player's coordinates ─── + if ("QUERY_LOCATION".equals(payloadType)) { + String targetName = in.readUTF(); + String replyServer = in.readUTF(); + + Player target = plugin.getServer().getPlayerExact(targetName); + if (target == null || !target.isOnline()) return; // can't answer + + // We need a live player to send the reply message through + Player anchor = plugin.getServer().getOnlinePlayers().stream().findFirst().orElse(null); + if (anchor == null) return; + + org.bukkit.Location loc = target.getLocation(); + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(bos)) { + out.writeUTF("Forward"); + out.writeUTF(replyServer); + out.writeUTF(TS_CHANNEL); + + ByteArrayOutputStream sub = new ByteArrayOutputStream(); + DataOutputStream subOut = new DataOutputStream(sub); + subOut.writeUTF("LOCATION_RESPONSE"); + subOut.writeUTF(targetName); + subOut.writeUTF(loc.getWorld().getName()); + subOut.writeDouble(loc.getX()); + subOut.writeDouble(loc.getY()); + subOut.writeDouble(loc.getZ()); + subOut.writeFloat(loc.getYaw()); + subOut.writeFloat(loc.getPitch()); + + byte[] subBytes = sub.toByteArray(); + out.writeShort(subBytes.length); + out.write(subBytes); + anchor.sendPluginMessage(plugin, BUNGEE_CHANNEL, bos.toByteArray()); + } catch (IOException e) { + plugin.getLogger().warning("LOCATION_RESPONSE Fehler: " + e.getMessage()); + } + return; + } + + // ── LOCATION_RESPONSE: answer to our QUERY_LOCATION ───────────────── + if ("LOCATION_RESPONSE".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(); + + org.bukkit.World w = plugin.getServer().getWorld(world); + // w may be null here (different server), that's fine — callers only need coords + org.bukkit.Location loc = new org.bukkit.Location(w, x, y, z, yaw, pitch); + flushLocationLookupCallbacks(targetName.toLowerCase(Locale.ROOT), loc); + return; + } + + // ── TP_TO_PLAYER: fallback polling mechanism ───────────────────────── if ("TP_TO_PLAYER".equals(payloadType)) { - String moverName = in.readUTF(); + String moverName = in.readUTF(); String targetName = in.readUTF(); schedulePlayerToPlayerTeleport(moverName, targetName); return; } + // ── TPA messages ───────────────────────────────────────────────────── if ("TPA_REQUEST".equals(payloadType)) { String requesterName = in.readUTF(); - String targetName = 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(); + String targetName = in.readUTF(); plugin.getTeleportManager().receiveCrossServerTpaAccepted(requesterName, targetName); return; } if ("TPA_DENY".equals(payloadType)) { String requesterName = in.readUTF(); - String targetName = 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(); + String targetName = in.readUTF(); plugin.getTeleportManager().receiveCrossServerTpaExpired(requesterName, targetName); return; } - // Backward compatibility with old payload format - String targetName = payloadType; + // ── Backward compatibility: old payload had no type prefix ──────────── + String targetName = payloadType; // first field WAS the player name String world = in.readUTF(); double x = in.readDouble(), y = in.readDouble(), z = in.readDouble(); - float yaw = in.readFloat(), pitch = in.readFloat(); + float yaw = in.readFloat(), pitch = in.readFloat(); plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { Player target = plugin.getServer().getPlayerExact(targetName); if (target == null || !target.isOnline()) return; @@ -498,6 +707,7 @@ public class BungeeMessenger implements PluginMessageListener { plugin.getConfigManager().getServerName()); plugin.getTeleportManager().teleport(target, loc, false); }, 20L); + } catch (IOException e) { plugin.getLogger().warning("Fehler beim Lesen der TS-Nachricht: " + e.getMessage()); } diff --git a/src/main/java/de/teleportsuite/managers/TeleportManager.java b/src/main/java/de/teleportsuite/managers/TeleportManager.java index 6a6ec00..9090913 100644 --- a/src/main/java/de/teleportsuite/managers/TeleportManager.java +++ b/src/main/java/de/teleportsuite/managers/TeleportManager.java @@ -73,8 +73,15 @@ public class TeleportManager { private void executeTeleport(Player player, TeleportLocation dest) { String localServer = plugin.getConfigManager().getServerName(); + // Always save last location and apply cooldown, regardless of server + plugin.getDatabaseManager().saveLastLocation(player.getUniqueId(), + new TeleportLocation(player.getLocation(), localServer)); + cooldowns.put(player.getUniqueId(), System.currentTimeMillis()); + if (!dest.isLocalServer(localServer)) { - // BungeeCord-Teleport + // BungeeCord cross-server teleport: + // connectToServer sends the LOCATION payload BEFORE the Connect message + // so the player arrives directly at the target coordinates. if (plugin.getBungeeMessenger() != null) { plugin.getBungeeMessenger().connectToServer(player, dest.getServer(), dest.getWorld(), dest.getX(), dest.getY(), dest.getZ(), dest.getYaw(), dest.getPitch()); @@ -85,16 +92,11 @@ public class TeleportManager { Location loc = dest.toBukkitLocation(); if (loc == null || loc.getWorld() == null) { - player.sendMessage("§cZielwelt nicht gefunden!"); + player.sendMessage("\u00a7cZielwelt nicht gefunden!"); return; } - // Save last location to database - plugin.getDatabaseManager().saveLastLocation(player.getUniqueId(), - new TeleportLocation(player.getLocation(), localServer)); - player.teleport(loc); - cooldowns.put(player.getUniqueId(), System.currentTimeMillis()); player.sendMessage(plugin.getConfigManager().getMessage("teleport-success")); } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 2e6ca3e..2b438ea 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: TeleportSuite -version: 1.0.0 +version: 1.0.1 main: de.teleportsuite.TeleportSuite api-version: 1.20 description: BungeeCord-fähiges Teleport-Komplettpaket