diff --git a/src/main/java/de/teleportsuite/ConfigManager.java b/src/main/java/de/teleportsuite/ConfigManager.java new file mode 100644 index 0000000..d537637 --- /dev/null +++ b/src/main/java/de/teleportsuite/ConfigManager.java @@ -0,0 +1,34 @@ +package de.teleportsuite; + +import org.bukkit.ChatColor; + +public class ConfigManager { + private final TeleportSuite plugin; + + public ConfigManager(TeleportSuite plugin) { this.plugin = plugin; } + + public boolean isBungeeEnabled() { return plugin.getConfig().getBoolean("bungee.enabled", false); } + public String getServerName() { return plugin.getConfig().getString("bungee.server-name", "local"); } + public int getTeleportDelay() { return plugin.getConfig().getInt("teleport.delay", 3); } + public int getTeleportCooldown() { return plugin.getConfig().getInt("teleport.cooldown", 5); } + public boolean cancelOnMove() { return plugin.getConfig().getBoolean("teleport.warmup-cancel-on-move", true); } + public int getRequestTimeout() { return plugin.getConfig().getInt("teleport.request-timeout", 60); } + public int getMaxHomes(String group) { return plugin.getConfig().getInt("homes.max-homes-" + group, 3); } + public boolean allowPlayerWarps() { return plugin.getConfig().getBoolean("warps.allow-player-warps", false); } + public boolean firstJoinTeleport() { return plugin.getConfig().getBoolean("spawn.first-join-teleport", true); } + public boolean deathRespawnToSpawn() { return plugin.getConfig().getBoolean("spawn.death-respawn-to-spawn", false); } + + public String getMessage(String key) { + String prefix = plugin.getConfig().getString("messages.prefix", "&8[&6TP&8] &r"); + String msg = plugin.getConfig().getString("messages." + key, "&cNachricht nicht gefunden: " + key); + return ChatColor.translateAlternateColorCodes('&', prefix + msg); + } + + public String getMessage(String key, String... replacements) { + String msg = getMessage(key); + for (int i = 0; i < replacements.length - 1; i += 2) { + msg = msg.replace("{" + replacements[i] + "}", replacements[i+1]); + } + return msg; + } +} diff --git a/src/main/java/de/teleportsuite/TeleportSuite.java b/src/main/java/de/teleportsuite/TeleportSuite.java new file mode 100644 index 0000000..610a93b --- /dev/null +++ b/src/main/java/de/teleportsuite/TeleportSuite.java @@ -0,0 +1,106 @@ +package de.teleportsuite; + +import de.teleportsuite.commands.*; +import de.teleportsuite.database.DatabaseManager; +import de.teleportsuite.listeners.*; +import de.teleportsuite.managers.*; +import de.teleportsuite.bungeemessaging.BungeeMessenger; +import org.bukkit.plugin.java.JavaPlugin; + +public class TeleportSuite extends JavaPlugin { + + private static TeleportSuite instance; + private DatabaseManager databaseManager; + private HomeManager homeManager; + private WarpManager warpManager; + private PortalManager portalManager; + private TeleportManager teleportManager; + private SpawnManager spawnManager; + private SavePointManager savePointManager; + private BungeeMessenger bungeeMessenger; + private ConfigManager configManager; + + @Override + public void onEnable() { + instance = this; + saveDefaultConfig(); + configManager = new ConfigManager(this); + + databaseManager = new DatabaseManager(this); + if (!databaseManager.connect()) { + getLogger().severe("Datenbankverbindung fehlgeschlagen! Plugin wird deaktiviert."); + getServer().getPluginManager().disablePlugin(this); + return; + } + databaseManager.createTables(); + + homeManager = new HomeManager(this); + warpManager = new WarpManager(this); + portalManager = new PortalManager(this); + teleportManager = new TeleportManager(this); + spawnManager = new SpawnManager(this); + savePointManager = new SavePointManager(this); + + if (configManager.isBungeeEnabled()) { + bungeeMessenger = new BungeeMessenger(this); + bungeeMessenger.register(); + getLogger().info("BungeeCord-Unterstuetzung aktiviert."); + } + + getServer().getPluginManager().registerEvents(new PlayerJoinListener(this), this); + getServer().getPluginManager().registerEvents(new PlayerDeathListener(this), this); + getServer().getPluginManager().registerEvents(new PlayerMoveListener(this), this); + getServer().getPluginManager().registerEvents(new PlayerRespawnListener(this), this); + getServer().getPluginManager().registerEvents(new PortalListener(this), this); + + registerCommands(); + getLogger().info("TeleportSuite v" + getDescription().getVersion() + " erfolgreich geladen!"); + } + + @Override + public void onDisable() { + if (databaseManager != null) databaseManager.disconnect(); + if (bungeeMessenger != null) bungeeMessenger.unregister(); + } + + private void registerCommands() { + getCommand("tp").setExecutor(new TpCommand(this)); + getCommand("tphere").setExecutor(new TpHereCommand(this)); + getCommand("tpa").setExecutor(new TpaCommand(this)); + getCommand("tpaccept").setExecutor(new TpAcceptCommand(this)); + getCommand("tpdeny").setExecutor(new TpDenyCommand(this)); + getCommand("tppos").setExecutor(new TpPosCommand(this)); + getCommand("tpall").setExecutor(new TpAllCommand(this)); + getCommand("tpworld").setExecutor(new TpWorldCommand(this)); + getCommand("back").setExecutor(new BackCommand(this)); + getCommand("deathback").setExecutor(new DeathBackCommand(this)); + getCommand("sethome").setExecutor(new SetHomeCommand(this)); + getCommand("home").setExecutor(new HomeCommand(this)); + getCommand("delhome").setExecutor(new DelHomeCommand(this)); + getCommand("homes").setExecutor(new HomesCommand(this)); + getCommand("setwarp").setExecutor(new SetWarpCommand(this)); + getCommand("warp").setExecutor(new WarpCommand(this)); + getCommand("delwarp").setExecutor(new DelWarpCommand(this)); + getCommand("warps").setExecutor(new WarpsCommand(this)); + getCommand("setportal").setExecutor(new SetPortalCommand(this)); + getCommand("delportal").setExecutor(new DelPortalCommand(this)); + getCommand("portals").setExecutor(new PortalsCommand(this)); + getCommand("setspawn").setExecutor(new SetSpawnCommand(this)); + getCommand("spawn").setExecutor(new SpawnCommand(this)); + getCommand("setfirstspawn").setExecutor(new SetFirstSpawnCommand(this)); + getCommand("setsavepoint").setExecutor(new SetSavePointCommand(this)); + getCommand("savepoint").setExecutor(new SavePointCommand(this)); + getCommand("entitytransport").setExecutor(new EntityTransportCommand(this)); + } + + public static TeleportSuite getInstance() { return instance; } + public DatabaseManager getDatabaseManager() { return databaseManager; } + public HomeManager getHomeManager() { return homeManager; } + public WarpManager getWarpManager() { return warpManager; } + public PortalManager getPortalManager() { return portalManager; } + public TeleportManager getTeleportManager() { return teleportManager; } + public SpawnManager getSpawnManager() { return spawnManager; } + public SavePointManager getSavePointManager() { return savePointManager; } + public BungeeMessenger getBungeeMessenger() { return bungeeMessenger; } + public ConfigManager getConfigManager() { return configManager; } +} diff --git a/src/main/java/de/teleportsuite/bungee/TeleportSuiteBungee.java b/src/main/java/de/teleportsuite/bungee/TeleportSuiteBungee.java new file mode 100644 index 0000000..0bc7735 --- /dev/null +++ b/src/main/java/de/teleportsuite/bungee/TeleportSuiteBungee.java @@ -0,0 +1,38 @@ +package de.teleportsuite.bungee; + +import de.teleportsuite.bungee.listener.PlayerConnectionListener; +import de.teleportsuite.bungee.manager.TeleportMessageListener; +import net.md_5.bungee.api.plugin.Plugin; +import net.md_5.bungee.api.plugin.PluginManager; + +public class TeleportSuiteBungee extends Plugin { + + public static final String CHANNEL_TO_BUNGEE = "teleportsuite:tobungee"; + public static final String CHANNEL_TO_SPIGOT = "teleportsuite:tospigot"; + + private static TeleportSuiteBungee instance; + + @Override + public void onEnable() { + instance = this; + PluginManager pm = getProxy().getPluginManager(); + + getProxy().registerChannel(CHANNEL_TO_BUNGEE); + getProxy().registerChannel(CHANNEL_TO_SPIGOT); + + pm.registerListener(this, new TeleportMessageListener(this)); + pm.registerListener(this, new PlayerConnectionListener(this)); + + getLogger().info("TeleportSuite-Bungee aktiviert."); + } + + @Override + public void onDisable() { + getProxy().getScheduler().cancel(this); + getLogger().info("TeleportSuite-Bungee deaktiviert."); + } + + public static TeleportSuiteBungee getInstance() { + return instance; + } +} diff --git a/src/main/java/de/teleportsuite/bungee/listener/PlayerConnectionListener.java b/src/main/java/de/teleportsuite/bungee/listener/PlayerConnectionListener.java new file mode 100644 index 0000000..5a3dc55 --- /dev/null +++ b/src/main/java/de/teleportsuite/bungee/listener/PlayerConnectionListener.java @@ -0,0 +1,58 @@ +package de.teleportsuite.bungee.listener; + +import de.teleportsuite.bungee.TeleportSuiteBungee; +import de.teleportsuite.bungee.manager.PendingTpa; +import de.teleportsuite.bungee.manager.TeleportMessageListener; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.PlayerDisconnectEvent; +import net.md_5.bungee.api.plugin.Listener; +import net.md_5.bungee.event.EventHandler; + +import java.io.*; + +/** + * Cleans up pending TPA requests when a player disconnects. + */ +public class PlayerConnectionListener implements Listener { + + private final TeleportSuiteBungee plugin; + + public PlayerConnectionListener(TeleportSuiteBungee plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onDisconnect(PlayerDisconnectEvent event) { + String name = event.getPlayer().getName(); + + // Player was a requester + PendingTpa tpa = TeleportMessageListener.getPendingTpa().remove(name); + if (tpa != null) { + notifyOther(tpa.getToName(), name); + return; + } + + // Player was a target — find the requester + TeleportMessageListener.getPendingTpa().entrySet().removeIf(entry -> { + if (entry.getValue().getToName().equalsIgnoreCase(name)) { + notifyOther(entry.getValue().getFromName(), name); + return true; + } + return false; + }); + } + + private void notifyOther(String otherName, String disconnectedName) { + ProxiedPlayer other = plugin.getProxy().getPlayer(otherName); + if (other == null || other.getServer() == null) return; + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(bos)) { + out.writeUTF("SEND_MSG"); + out.writeUTF(otherName); + out.writeUTF("§cDeine TPA-Anfrage wurde abgebrochen, da §6" + disconnectedName + " §cdas Netzwerk verlassen hat."); + other.getServer().sendData(TeleportSuiteBungee.CHANNEL_TO_SPIGOT, bos.toByteArray()); + } catch (IOException e) { + plugin.getLogger().warning("Disconnect-Benachrichtigung fehlgeschlagen: " + e.getMessage()); + } + } +} diff --git a/src/main/java/de/teleportsuite/bungee/manager/PendingTpa.java b/src/main/java/de/teleportsuite/bungee/manager/PendingTpa.java new file mode 100644 index 0000000..0072e34 --- /dev/null +++ b/src/main/java/de/teleportsuite/bungee/manager/PendingTpa.java @@ -0,0 +1,23 @@ +package de.teleportsuite.bungee.manager; + +/** + * Represents a pending cross-server TPA request. + */ +public class PendingTpa { + + public enum Type { TPTO, TPHERE } + + private final String fromName; + private final String toName; + private final Type type; + + public PendingTpa(String fromName, String toName, Type type) { + this.fromName = fromName; + this.toName = toName; + this.type = type; + } + + public String getFromName() { return fromName; } + public String getToName() { return toName; } + public Type getType() { return type; } +} diff --git a/src/main/java/de/teleportsuite/bungee/manager/TeleportMessageListener.java b/src/main/java/de/teleportsuite/bungee/manager/TeleportMessageListener.java new file mode 100644 index 0000000..3225405 --- /dev/null +++ b/src/main/java/de/teleportsuite/bungee/manager/TeleportMessageListener.java @@ -0,0 +1,413 @@ +package de.teleportsuite.bungee.manager; + +import de.teleportsuite.bungee.TeleportSuiteBungee; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.connection.Server; +import net.md_5.bungee.api.event.PluginMessageEvent; +import net.md_5.bungee.api.plugin.Listener; +import net.md_5.bungee.event.EventHandler; + +import java.io.*; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * Receives plugin messages from Spigot servers on CHANNEL_TO_BUNGEE, + * performs the cross-server operation (connect + forward), and sends + * results back on CHANNEL_TO_SPIGOT. + * + * Payload identifiers (first UTF written by Spigot): + * + * TP_PLAYER – teleport sender to a named target player + * TP_POS – teleport player to fixed coordinates on a server + * TP_HERE – pull a remote player to the sender's location + * TP_ALL – pull all network players to sender + * TPA_REQUEST – forward "you have a TPA request" to target + * TPA_ACCEPT – accept pending TPA; trigger the actual teleport + * TPA_DENY – deny pending TPA; notify requester + * TPA_CANCEL – requester cancels their own pending request + * TPA_EXPIRED – timeout: notify both parties + * SEND_MSG – route a chat message to a player on any server + */ +public class TeleportMessageListener implements Listener { + + // fromName → pending request + private static final Map pendingTpa = new HashMap<>(); + + private final TeleportSuiteBungee plugin; + + public TeleportMessageListener(TeleportSuiteBungee plugin) { + this.plugin = plugin; + } + + public static Map getPendingTpa() { + return pendingTpa; + } + + // ------------------------------------------------------------------------- + // Incoming messages from Spigot + // ------------------------------------------------------------------------- + + @EventHandler + public void onPluginMessage(PluginMessageEvent event) throws IOException { + if (event.isCancelled()) return; + if (!(event.getSender() instanceof Server)) return; + if (!event.getTag().equals(TeleportSuiteBungee.CHANNEL_TO_BUNGEE)) return; + + DataInputStream in = new DataInputStream(new ByteArrayInputStream(event.getData())); + String task = in.readUTF(); + + switch (task) { + + // ------------------------------------------------------------------ + // /tp → teleport sender directly to target player + // ------------------------------------------------------------------ + case "TP_PLAYER": { + String senderName = in.readUTF(); + String targetName = in.readUTF(); + + ProxiedPlayer sender = plugin.getProxy().getPlayer(senderName); + ProxiedPlayer target = plugin.getProxy().getPlayer(targetName); + + if (sender == null) return; + if (target == null) { + sendMsg(sender, "§cSpieler §6" + targetName + " §cnicht gefunden."); + return; + } + + teleportPlayerToPlayer(sender, target, 50); + break; + } + + // ------------------------------------------------------------------ + // /tp → teleport another player to a target player + // ------------------------------------------------------------------ + case "TP_PLAYER_TO_PLAYER": { + String requesterName = in.readUTF(); + String fromName = in.readUTF(); + String targetName = in.readUTF(); + + ProxiedPlayer requester = plugin.getProxy().getPlayer(requesterName); + ProxiedPlayer from = plugin.getProxy().getPlayer(fromName); + ProxiedPlayer target = plugin.getProxy().getPlayer(targetName); + + if (requester == null) return; + if (from == null || target == null) { + sendMsg(requester, "§cEin Spieler wurde nicht gefunden."); + return; + } + + teleportPlayerToPlayer(from, target, 50); + break; + } + + // ------------------------------------------------------------------ + // /tphere → pull target to sender's location + // ------------------------------------------------------------------ + case "TP_HERE": { + String senderName = in.readUTF(); + String targetName = in.readUTF(); + + ProxiedPlayer sender = plugin.getProxy().getPlayer(senderName); + ProxiedPlayer target = plugin.getProxy().getPlayer(targetName); + + if (sender == null) return; + if (target == null) { + sendMsg(sender, "§cSpieler §6" + targetName + " §cnicht gefunden."); + return; + } + + teleportPlayerToPlayer(target, sender, 50); + break; + } + + // ------------------------------------------------------------------ + // /tpall → pull every player on the network to sender + // ------------------------------------------------------------------ + case "TP_ALL": { + String senderName = in.readUTF(); + ProxiedPlayer sender = plugin.getProxy().getPlayer(senderName); + if (sender == null) return; + + for (ProxiedPlayer p : plugin.getProxy().getPlayers()) { + if (!p.getName().equals(senderName)) { + teleportPlayerToPlayer(p, sender, 50); + } + } + break; + } + + // ------------------------------------------------------------------ + // /home, /warp, /spawn, /back, /savepoint, /portal, /tppos … + // Spigot already knows the coordinates; we just switch the server + // and tell that server where to put the player. + // ------------------------------------------------------------------ + case "TP_POS": { + String playerName = in.readUTF(); + String server = in.readUTF(); + String world = in.readUTF(); + double x = in.readDouble(), y = in.readDouble(), z = in.readDouble(); + float yaw = in.readFloat(), pitch = in.readFloat(); + + ProxiedPlayer player = plugin.getProxy().getPlayer(playerName); + if (player == null) return; + + teleportPlayerToPosition(player, server, world, x, y, z, yaw, pitch, 100); + break; + } + + // ------------------------------------------------------------------ + // TPA: requester asks target for a teleport + // ------------------------------------------------------------------ + case "TPA_REQUEST": { + String fromName = in.readUTF(); + String toName = in.readUTF(); + String type = in.readUTF(); // TPTO or TPHERE + String msgToTarget = in.readUTF(); + String msgToRequester = in.readUTF(); + + ProxiedPlayer from = plugin.getProxy().getPlayer(fromName); + ProxiedPlayer to = plugin.getProxy().getPlayer(toName); + + if (from == null) return; + if (to == null) { + sendMsg(from, "§cSpieler §6" + toName + " §cnicht gefunden."); + return; + } + + // Check for existing pending request + if (pendingTpa.containsKey(fromName)) { + sendMsg(from, "§cDu hast bereits eine offene Anfrage."); + return; + } + + pendingTpa.put(fromName, new PendingTpa(fromName, toName, PendingTpa.Type.valueOf(type))); + + // Notify both players — via their respective servers + sendMsgToServer(from.getServer().getInfo(), fromName, msgToRequester); + sendMsgToServer(to.getServer().getInfo(), toName, msgToTarget); + break; + } + + // ------------------------------------------------------------------ + // TPA: target accepts → trigger the actual teleport + // ------------------------------------------------------------------ + case "TPA_ACCEPT": { + String toName = in.readUTF(); // player who accepted + String fromName = in.readUTF(); // original requester (may be "nu" = any) + String errMsg = in.readUTF(); + + PendingTpa tpa = resolvePending(fromName, toName); + if (tpa == null) { + ProxiedPlayer to = plugin.getProxy().getPlayer(toName); + if (to != null) sendMsg(to, errMsg); + return; + } + + pendingTpa.remove(tpa.getFromName()); + + ProxiedPlayer from = plugin.getProxy().getPlayer(tpa.getFromName()); + ProxiedPlayer to = plugin.getProxy().getPlayer(tpa.getToName()); + + if (from == null || to == null) return; + + if (tpa.getType() == PendingTpa.Type.TPTO) { + teleportPlayerToPlayer(from, to, 50); + } else { + teleportPlayerToPlayer(to, from, 50); + } + break; + } + + // ------------------------------------------------------------------ + // TPA: target denies → notify requester + // ------------------------------------------------------------------ + case "TPA_DENY": { + String toName = in.readUTF(); + String fromName = in.readUTF(); + String msg = in.readUTF(); + + PendingTpa tpa = resolvePending(fromName, toName); + if (tpa == null) return; + + pendingTpa.remove(tpa.getFromName()); + + ProxiedPlayer from = plugin.getProxy().getPlayer(tpa.getFromName()); + ProxiedPlayer to = plugin.getProxy().getPlayer(tpa.getToName()); + + if (from != null) sendMsg(from, msg); + if (to != null) sendMsg(to, msg); + break; + } + + // ------------------------------------------------------------------ + // TPA: requester cancels their own request + // ------------------------------------------------------------------ + case "TPA_CANCEL": { + String fromName = in.readUTF(); + String msgFrom = in.readUTF(); + String msgTo = in.readUTF(); + + PendingTpa tpa = pendingTpa.remove(fromName); + if (tpa == null) return; + + ProxiedPlayer from = plugin.getProxy().getPlayer(fromName); + ProxiedPlayer to = plugin.getProxy().getPlayer(tpa.getToName()); + + if (from != null) sendMsg(from, msgFrom); + if (to != null) sendMsg(to, msgTo); + break; + } + + // ------------------------------------------------------------------ + // TPA: timeout — remove pending and notify both sides + // ------------------------------------------------------------------ + case "TPA_EXPIRED": { + String fromName = in.readUTF(); + String toName = in.readUTF(); + String msgFrom = in.readUTF(); + String msgTo = in.readUTF(); + + pendingTpa.remove(fromName); + + ProxiedPlayer from = plugin.getProxy().getPlayer(fromName); + ProxiedPlayer to = plugin.getProxy().getPlayer(toName); + + if (from != null) sendMsg(from, msgFrom); + if (to != null) sendMsg(to, msgTo); + break; + } + + // ------------------------------------------------------------------ + // Generic cross-server message routing + // ------------------------------------------------------------------ + case "SEND_MSG": { + String targetName = in.readUTF(); + String message = in.readUTF(); + ProxiedPlayer target = plugin.getProxy().getPlayer(targetName); + if (target != null) sendMsg(target, message); + break; + } + + default: + plugin.getLogger().warning("Unbekannter Task: " + task); + } + } + + // ------------------------------------------------------------------------- + // Core teleport helpers — the BTM way + // ------------------------------------------------------------------------- + + /** + * Moves {@code sender} to {@code target}'s current server (if needed), + * then after a short delay tells the target server to do the final + * player.teleport(target) call via TP_PLAYERTOPLAYER. + * + * This is done entirely on the Bungee side: + * 1. sender.connect(target.server) — BungeeCord switches the server + * 2. delay ms later: server.sendData(TP_PLAYERTOPLAYER, ...) + * → Spigot polls until both players are on the same server and teleports + */ + private void teleportPlayerToPlayer(ProxiedPlayer sender, ProxiedPlayer target, int delayMs) { + plugin.getProxy().getScheduler().schedule(plugin, () -> { + if (sender == null || target == null) return; + if (sender.getServer() == null || target.getServer() == null) return; + + // Switch server if needed + if (!sender.getServer().getInfo().getName() + .equals(target.getServer().getInfo().getName())) { + sender.connect(target.getServer().getInfo()); + } + + // Tell the target server to teleport sender → target + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(bos)) { + out.writeUTF("TP_PLAYERTOPLAYER"); + out.writeUTF(sender.getName()); + out.writeUTF(target.getName()); + target.getServer().sendData(TeleportSuiteBungee.CHANNEL_TO_SPIGOT, bos.toByteArray()); + } catch (IOException e) { + plugin.getLogger().warning("TP_PLAYERTOPLAYER Fehler: " + e.getMessage()); + } + }, delayMs, TimeUnit.MILLISECONDS); + } + + /** + * Moves {@code player} to {@code server}, then after a delay sends + * TP_PLAYERTOPOSITION so Spigot teleports them to exact coordinates. + */ + private void teleportPlayerToPosition(ProxiedPlayer player, String server, + String world, double x, double y, double z, + float yaw, float pitch, int delayMs) { + if (!plugin.getProxy().getServers().containsKey(server)) { + sendMsg(player, "§cServer §6" + server + " §cnicht gefunden."); + return; + } + + // Switch server if needed + if (!player.getServer().getInfo().getName().equals(server)) { + player.connect(plugin.getProxy().getServerInfo(server)); + } + + // After delay, tell the (now correct) server where to put the player + plugin.getProxy().getScheduler().schedule(plugin, () -> { + if (player.getServer() == null) return; + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(bos)) { + out.writeUTF("TP_PLAYERTOPOSITION"); + out.writeUTF(player.getName()); + out.writeUTF(world); + out.writeDouble(x); + out.writeDouble(y); + out.writeDouble(z); + out.writeFloat(yaw); + out.writeFloat(pitch); + player.getServer().sendData(TeleportSuiteBungee.CHANNEL_TO_SPIGOT, bos.toByteArray()); + } catch (IOException e) { + plugin.getLogger().warning("TP_PLAYERTOPOSITION Fehler: " + e.getMessage()); + } + }, delayMs, TimeUnit.MILLISECONDS); + } + + // ------------------------------------------------------------------------- + // Helpers + // ------------------------------------------------------------------------- + + /** Resolves which pending TPA entry matches an accept/deny. */ + private PendingTpa resolvePending(String fromName, String toName) { + // Exact match + if (!fromName.equals("nu") && pendingTpa.containsKey(fromName)) { + return pendingTpa.get(fromName); + } + // Find by target name only + for (PendingTpa tpa : pendingTpa.values()) { + if (tpa.getToName().equalsIgnoreCase(toName)) { + return tpa; + } + } + return null; + } + + /** Sends a formatted chat message directly to a ProxiedPlayer. */ + private void sendMsg(ProxiedPlayer player, String message) { + player.sendMessage(net.md_5.bungee.api.ChatColor.translateAlternateColorCodes('&', message)); + } + + /** + * Routes a chat message to a named player via their Spigot server. + * The Spigot side handles the actual sendMessage() call. + */ + private void sendMsgToServer(net.md_5.bungee.api.config.ServerInfo server, + String playerName, String message) { + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(bos)) { + out.writeUTF("SEND_MSG"); + out.writeUTF(playerName); + out.writeUTF(message); + server.sendData(TeleportSuiteBungee.CHANNEL_TO_SPIGOT, bos.toByteArray()); + } catch (IOException e) { + plugin.getLogger().warning("SEND_MSG Fehler: " + e.getMessage()); + } + } +} diff --git a/src/main/java/de/teleportsuite/bungeemessaging/BungeeMessenger.java b/src/main/java/de/teleportsuite/bungeemessaging/BungeeMessenger.java new file mode 100644 index 0000000..18cca7f --- /dev/null +++ b/src/main/java/de/teleportsuite/bungeemessaging/BungeeMessenger.java @@ -0,0 +1,277 @@ +package de.teleportsuite.bungeemessaging; + +import de.teleportsuite.TeleportSuite; +import de.teleportsuite.models.TeleportLocation; +import org.bukkit.entity.Player; +import org.bukkit.plugin.messaging.PluginMessageListener; + +import java.io.*; + +/** + * Spigot-seitiger Kanal zum TeleportSuite-Bungee-Plugin. + * + * Prinzip (identisch zu BTM): + * Spigot schickt Anfragen auf CHANNEL_TO_BUNGEE. + * BungeeCord erledigt connect() + routing und schickt Befehle auf CHANNEL_TO_SPIGOT. + * Spigot führt den finalen player.teleport() aus. + * + * Payloads Spigot → Bungee (CHANNEL_TO_BUNGEE): + * TP_PLAYER senderName, targetName + * TP_PLAYER_TO_PLAYER requesterName, fromName, toName + * TP_HERE senderName, targetName + * TP_ALL senderName + * TP_POS playerName, server, world, x, y, z, yaw, pitch + * TPA_REQUEST fromName, toName, type, msgToTarget, msgToRequester + * TPA_ACCEPT toName, fromName, errMsg + * TPA_DENY toName, fromName, msg + * TPA_CANCEL fromName, msgFrom, msgTo + * TPA_EXPIRED fromName, toName, msgFrom, msgTo + * SEND_MSG targetName, message + * + * Payloads Bungee → Spigot (CHANNEL_TO_SPIGOT): + * TP_PLAYERTOPLAYER senderName, targetName + * TP_PLAYERTOPOSITION playerName, world, x, y, z, yaw, pitch + * SEND_MSG playerName, message + */ +public class BungeeMessenger implements PluginMessageListener { + + public static final String CHANNEL_TO_BUNGEE = "teleportsuite:tobungee"; + public static final String CHANNEL_TO_SPIGOT = "teleportsuite:tospigot"; + + private final TeleportSuite plugin; + + public BungeeMessenger(TeleportSuite plugin) { + this.plugin = plugin; + } + + public void register() { + plugin.getServer().getMessenger().registerOutgoingPluginChannel(plugin, CHANNEL_TO_BUNGEE); + plugin.getServer().getMessenger().registerIncomingPluginChannel(plugin, CHANNEL_TO_SPIGOT, this); + } + + public void unregister() { + plugin.getServer().getMessenger().unregisterOutgoingPluginChannel(plugin, CHANNEL_TO_BUNGEE); + plugin.getServer().getMessenger().unregisterIncomingPluginChannel(plugin, CHANNEL_TO_SPIGOT, this); + } + + // ------------------------------------------------------------------------- + // Outgoing helpers (Spigot → Bungee) + // ------------------------------------------------------------------------- + + /** /tp – Absender teleportiert sich zum Zielspieler */ + public void teleportToPlayer(Player sender, String targetName) { + send(sender, out -> { + out.writeUTF("TP_PLAYER"); + out.writeUTF(sender.getName()); + out.writeUTF(targetName); + }); + } + + /** /tp – Admin teleportiert einen Spieler zu einem anderen */ + public void teleportPlayerToPlayer(Player requester, String fromName, String targetName) { + send(requester, out -> { + out.writeUTF("TP_PLAYER_TO_PLAYER"); + out.writeUTF(requester.getName()); + out.writeUTF(fromName); + out.writeUTF(targetName); + }); + } + + /** /tphere – Zielspieler wird zu Absender geholt */ + public void teleportHere(Player sender, String targetName) { + send(sender, out -> { + out.writeUTF("TP_HERE"); + out.writeUTF(sender.getName()); + out.writeUTF(targetName); + }); + } + + /** /tpall – Alle Netzwerkspieler zum Absender teleportieren */ + public void teleportAll(Player sender) { + send(sender, out -> { + out.writeUTF("TP_ALL"); + out.writeUTF(sender.getName()); + }); + } + + /** + * Home / Warp / Spawn / Back / SavePoint / Portal / TpPos: + * Koordinaten sind bekannt → Bungee wechselt den Server und Spigot + * setzt den Spieler direkt an die exakten Koordinaten. + */ + public void teleportToPosition(Player player, String server, String world, + double x, double y, double z, float yaw, float pitch) { + send(player, out -> { + out.writeUTF("TP_POS"); + out.writeUTF(player.getName()); + out.writeUTF(server); + out.writeUTF(world); + out.writeDouble(x); + out.writeDouble(y); + out.writeDouble(z); + out.writeFloat(yaw); + out.writeFloat(pitch); + }); + } + + // TPA + + public void sendTpaRequest(Player from, String toName, + String type, String msgToTarget, String msgToRequester) { + send(from, out -> { + out.writeUTF("TPA_REQUEST"); + out.writeUTF(from.getName()); + out.writeUTF(toName); + out.writeUTF(type); + out.writeUTF(msgToTarget); + out.writeUTF(msgToRequester); + }); + } + + public void sendTpaAccept(Player acceptor, String fromName, String errMsg) { + send(acceptor, out -> { + out.writeUTF("TPA_ACCEPT"); + out.writeUTF(acceptor.getName()); + out.writeUTF(fromName == null ? "nu" : fromName); + out.writeUTF(errMsg); + }); + } + + public void sendTpaDeny(Player denier, String fromName, String msg) { + send(denier, out -> { + out.writeUTF("TPA_DENY"); + out.writeUTF(denier.getName()); + out.writeUTF(fromName == null ? "nu" : fromName); + out.writeUTF(msg); + }); + } + + public void sendTpaCancel(Player canceller, String msgFrom, String msgTo) { + send(canceller, out -> { + out.writeUTF("TPA_CANCEL"); + out.writeUTF(canceller.getName()); + out.writeUTF(msgFrom); + out.writeUTF(msgTo); + }); + } + + public void sendTpaExpired(Player from, String toName, String msgFrom, String msgTo) { + send(from, out -> { + out.writeUTF("TPA_EXPIRED"); + out.writeUTF(from.getName()); + out.writeUTF(toName); + out.writeUTF(msgFrom); + out.writeUTF(msgTo); + }); + } + + // ------------------------------------------------------------------------- + // Incoming handler (Bungee → Spigot) + // ------------------------------------------------------------------------- + + @Override + public void onPluginMessageReceived(String channel, Player unused, byte[] bytes) { + if (!channel.equals(CHANNEL_TO_SPIGOT)) return; + + try (DataInputStream in = new DataInputStream(new ByteArrayInputStream(bytes))) { + String task = in.readUTF(); + + switch (task) { + + // Bungee hat sender bereits auf diesen Server gewechselt. + // Sobald beide Spieler lokal online sind, teleportieren. + case "TP_PLAYERTOPLAYER": { + String senderName = in.readUTF(); + String targetName = in.readUTF(); + schedulePlayerToPlayerTeleport(senderName, targetName); + break; + } + + // Bungee hat player bereits auf diesen Server gewechselt. + // Nach kurzem Delay an exakte Koordinaten teleportieren. + case "TP_PLAYERTOPOSITION": { + String playerName = 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, () -> { + org.bukkit.entity.Player p = plugin.getServer().getPlayerExact(playerName); + if (p == null || !p.isOnline()) return; + TeleportLocation loc = new TeleportLocation( + world, x, y, z, yaw, pitch, + plugin.getConfigManager().getServerName()); + plugin.getTeleportManager().teleport(p, loc, false); + }, 10L); + break; + } + + // Direkte Chat-Nachricht an einen lokalen Spieler + case "SEND_MSG": { + String playerName = in.readUTF(); + String message = in.readUTF(); + plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { + org.bukkit.entity.Player p = plugin.getServer().getPlayerExact(playerName); + if (p != null && p.isOnline()) p.sendMessage(message); + }, 1L); + break; + } + + default: + plugin.getLogger().warning("Unbekannter Bungee-Task: " + task); + } + } catch (IOException e) { + plugin.getLogger().warning("Fehler beim Lesen der Bungee-Nachricht: " + e.getMessage()); + } + } + + // ------------------------------------------------------------------------- + // Internal helpers + // ------------------------------------------------------------------------- + + /** + * Wartet bis sender UND target lokal online sind (max. 100 × 10 Ticks = 50 s), + * dann führt player.teleport() aus – identisch zum BTM-Ansatz. + */ + private void schedulePlayerToPlayerTeleport(String senderName, String targetName) { + final int[] attempts = {0}; + final int[] taskId = {-1}; + + taskId[0] = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, () -> { + attempts[0]++; + org.bukkit.entity.Player sender = plugin.getServer().getPlayerExact(senderName); + org.bukkit.entity.Player target = plugin.getServer().getPlayerExact(targetName); + + if (sender != null && sender.isOnline() && target != null && target.isOnline()) { + plugin.getTeleportManager().teleport( + sender, + new TeleportLocation(target.getLocation(), + plugin.getConfigManager().getServerName()), + false); + plugin.getServer().getScheduler().cancelTask(taskId[0]); + return; + } + + if (attempts[0] >= 100) { + plugin.getServer().getScheduler().cancelTask(taskId[0]); + } + }, 5L, 10L); + } + + @FunctionalInterface + private interface IOWriter { + void write(DataOutputStream out) throws IOException; + } + + /** Schreibt ein Payload und schickt es über den Bungee-Kanal. */ + private void send(Player anchor, IOWriter writer) { + if (anchor == null || !anchor.isOnline()) return; + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(bos)) { + writer.write(out); + anchor.sendPluginMessage(plugin, CHANNEL_TO_BUNGEE, bos.toByteArray()); + } catch (IOException e) { + plugin.getLogger().warning("Bungee-Send Fehler: " + e.getMessage()); + } + } +} diff --git a/src/main/java/de/teleportsuite/commands/BackCommand.java b/src/main/java/de/teleportsuite/commands/BackCommand.java new file mode 100644 index 0000000..f9db697 --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/BackCommand.java @@ -0,0 +1,22 @@ +package de.teleportsuite.commands; + +import de.teleportsuite.TeleportSuite; +import de.teleportsuite.models.TeleportLocation; +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +public class BackCommand implements CommandExecutor { + private final TeleportSuite plugin; + public BackCommand(TeleportSuite p) { this.plugin = p; } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + Player p = (Player) sender; + if (!p.hasPermission("teleportsuite.back")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + TeleportLocation loc = plugin.getDatabaseManager().getLastLocation(p.getUniqueId()); + if (loc == null) { p.sendMessage(plugin.getConfigManager().getMessage("back-no-location")); return true; } + plugin.getTeleportManager().teleport(p, loc, false); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/DeathBackCommand.java b/src/main/java/de/teleportsuite/commands/DeathBackCommand.java new file mode 100644 index 0000000..7b9904a --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/DeathBackCommand.java @@ -0,0 +1,22 @@ +package de.teleportsuite.commands; + +import de.teleportsuite.TeleportSuite; +import de.teleportsuite.models.TeleportLocation; +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +public class DeathBackCommand implements CommandExecutor { + private final TeleportSuite plugin; + public DeathBackCommand(TeleportSuite p) { this.plugin = p; } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + Player p = (Player) sender; + if (!p.hasPermission("teleportsuite.deathback")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + TeleportLocation loc = plugin.getDatabaseManager().getDeathLocation(p.getUniqueId()); + if (loc == null) { p.sendMessage(plugin.getConfigManager().getMessage("death-no-location")); return true; } + plugin.getTeleportManager().teleport(p, loc); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/DelHomeCommand.java b/src/main/java/de/teleportsuite/commands/DelHomeCommand.java new file mode 100644 index 0000000..ffbd315 --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/DelHomeCommand.java @@ -0,0 +1,17 @@ +package de.teleportsuite.commands; +import de.teleportsuite.TeleportSuite; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +public class DelHomeCommand implements CommandExecutor { + private final TeleportSuite plugin; + public DelHomeCommand(TeleportSuite p) { this.plugin = p; } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + Player p = (Player) sender; + if (!p.hasPermission("teleportsuite.sethome")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + if (args.length < 1) { p.sendMessage("§cVerwendung: /delhome "); return true; } + plugin.getHomeManager().deleteHome(p, args[0]); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/DelPortalCommand.java b/src/main/java/de/teleportsuite/commands/DelPortalCommand.java new file mode 100644 index 0000000..0519664 --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/DelPortalCommand.java @@ -0,0 +1,15 @@ +package de.teleportsuite.commands; +import de.teleportsuite.TeleportSuite; +import org.bukkit.command.*; +public class DelPortalCommand implements CommandExecutor { + private final TeleportSuite plugin; + public DelPortalCommand(TeleportSuite p) { this.plugin = p; } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!sender.hasPermission("teleportsuite.delportal")) { sender.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + if (args.length < 1) { sender.sendMessage("§cVerwendung: /delportal "); return true; } + boolean deleted = plugin.getPortalManager().deletePortal(args[0]); + sender.sendMessage(deleted ? plugin.getConfigManager().getMessage("portal-deleted","name",args[0]) : "§cPortal nicht gefunden."); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/DelWarpCommand.java b/src/main/java/de/teleportsuite/commands/DelWarpCommand.java new file mode 100644 index 0000000..4febaf1 --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/DelWarpCommand.java @@ -0,0 +1,16 @@ +package de.teleportsuite.commands; +import de.teleportsuite.TeleportSuite; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +public class DelWarpCommand implements CommandExecutor { + private final TeleportSuite plugin; + public DelWarpCommand(TeleportSuite p) { this.plugin = p; } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!sender.hasPermission("teleportsuite.delwarp")) { sender.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + if (args.length < 1) { sender.sendMessage("§cVerwendung: /delwarp "); return true; } + boolean deleted = plugin.getWarpManager().deleteWarp(args[0]); + sender.sendMessage(deleted ? plugin.getConfigManager().getMessage("warp-deleted","name",args[0]) : plugin.getConfigManager().getMessage("warp-not-found","name",args[0])); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/EntityTransportCommand.java b/src/main/java/de/teleportsuite/commands/EntityTransportCommand.java new file mode 100644 index 0000000..9685809 --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/EntityTransportCommand.java @@ -0,0 +1,30 @@ +package de.teleportsuite.commands; +import de.teleportsuite.TeleportSuite; +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.bukkit.entity.*; +public class EntityTransportCommand implements CommandExecutor { + private final TeleportSuite plugin; + public EntityTransportCommand(TeleportSuite p) { this.plugin = p; } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!sender.hasPermission("teleportsuite.entitytransport")) { sender.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + if (args.length < 2) { sender.sendMessage("§cVerwendung: /entitytransport "); return true; } + try { + int entityId = Integer.parseInt(args[0]); + Player target = Bukkit.getPlayer(args[1]); + if (target == null) { sender.sendMessage(plugin.getConfigManager().getMessage("player-not-found","player",args[1])); return true; } + Entity found = null; + for (var world : Bukkit.getWorlds()) { + for (var entity : world.getEntities()) { + if (entity.getEntityId() == entityId) { found = entity; break; } + } + if (found != null) break; + } + if (found == null) { sender.sendMessage("§cEntity mit ID §6" + entityId + " §cnicht gefunden."); return true; } + found.teleport(target.getLocation()); + sender.sendMessage("§aEntity §6" + found.getType() + " §awurde zu §6" + target.getName() + " §ateleportiert."); + } catch (NumberFormatException e) { sender.sendMessage("§cUngültige Entity-ID."); } + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/HomeCommand.java b/src/main/java/de/teleportsuite/commands/HomeCommand.java new file mode 100644 index 0000000..08472fe --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/HomeCommand.java @@ -0,0 +1,17 @@ +package de.teleportsuite.commands; +import de.teleportsuite.TeleportSuite; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +public class HomeCommand implements CommandExecutor { + private final TeleportSuite plugin; + public HomeCommand(TeleportSuite p) { this.plugin = p; } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + Player p = (Player) sender; + if (!p.hasPermission("teleportsuite.home")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + String name = args.length > 0 ? args[0] : "home"; + plugin.getHomeManager().teleportHome(p, name); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/HomesCommand.java b/src/main/java/de/teleportsuite/commands/HomesCommand.java new file mode 100644 index 0000000..f90644e --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/HomesCommand.java @@ -0,0 +1,20 @@ +package de.teleportsuite.commands; +import de.teleportsuite.TeleportSuite; +import de.teleportsuite.models.Home; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +import java.util.List; +public class HomesCommand implements CommandExecutor { + private final TeleportSuite plugin; + public HomesCommand(TeleportSuite p) { this.plugin = p; } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + Player p = (Player) sender; + List homes = plugin.getHomeManager().getHomes(p.getUniqueId()); + if (homes.isEmpty()) { p.sendMessage("§eKeine Homes gesetzt."); return true; } + p.sendMessage("§6Deine Homes §8(" + homes.size() + "/" + plugin.getHomeManager().getMaxHomes(p) + ")§6:"); + homes.forEach(h -> p.sendMessage("§7- §a" + h.getName() + " §7(" + h.getLocation().getWorld() + ")")); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/PortalsCommand.java b/src/main/java/de/teleportsuite/commands/PortalsCommand.java new file mode 100644 index 0000000..11c387b --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/PortalsCommand.java @@ -0,0 +1,16 @@ +package de.teleportsuite.commands; +import de.teleportsuite.TeleportSuite; +import org.bukkit.command.*; +public class PortalsCommand implements CommandExecutor { + private final TeleportSuite plugin; + public PortalsCommand(TeleportSuite p) { this.plugin = p; } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!sender.hasPermission("teleportsuite.portals")) { sender.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + var portals = plugin.getPortalManager().getPortals(); + if (portals.isEmpty()) { sender.sendMessage("§eKeine Portale vorhanden."); return true; } + sender.sendMessage("§6Portale §8(" + portals.size() + ")§6:"); + portals.forEach(p2 -> sender.sendMessage("§7- §a" + p2.getName() + " §7→ §e" + (p2.getTargetServer() != null ? p2.getTargetServer() + "/" : "") + p2.getDestination().getWorld())); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/SavePointCommand.java b/src/main/java/de/teleportsuite/commands/SavePointCommand.java new file mode 100644 index 0000000..0db0603 --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/SavePointCommand.java @@ -0,0 +1,17 @@ +package de.teleportsuite.commands; +import de.teleportsuite.TeleportSuite; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +public class SavePointCommand implements CommandExecutor { + private final TeleportSuite plugin; + public SavePointCommand(TeleportSuite p) { this.plugin = p; } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + Player p = (Player) sender; + if (!p.hasPermission("teleportsuite.savepoint")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + String name = args.length > 0 ? args[0] : "default"; + plugin.getSavePointManager().teleportToSavePoint(p, name); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/SetFirstSpawnCommand.java b/src/main/java/de/teleportsuite/commands/SetFirstSpawnCommand.java new file mode 100644 index 0000000..ab59b6b --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/SetFirstSpawnCommand.java @@ -0,0 +1,15 @@ +package de.teleportsuite.commands; +import de.teleportsuite.TeleportSuite; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +public class SetFirstSpawnCommand implements CommandExecutor { + private final TeleportSuite plugin; + public SetFirstSpawnCommand(TeleportSuite p) { this.plugin = p; } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + if (!sender.hasPermission("teleportsuite.setfirstspawn")) { sender.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + plugin.getSpawnManager().setFirstSpawn((Player) sender); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/SetHomeCommand.java b/src/main/java/de/teleportsuite/commands/SetHomeCommand.java new file mode 100644 index 0000000..f14990c --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/SetHomeCommand.java @@ -0,0 +1,17 @@ +package de.teleportsuite.commands; +import de.teleportsuite.TeleportSuite; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +public class SetHomeCommand implements CommandExecutor { + private final TeleportSuite plugin; + public SetHomeCommand(TeleportSuite p) { this.plugin = p; } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + Player p = (Player) sender; + if (!p.hasPermission("teleportsuite.sethome")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + String name = args.length > 0 ? args[0] : "home"; + plugin.getHomeManager().setHome(p, name); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/SetPortalCommand.java b/src/main/java/de/teleportsuite/commands/SetPortalCommand.java new file mode 100644 index 0000000..fbfd655 --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/SetPortalCommand.java @@ -0,0 +1,28 @@ +package de.teleportsuite.commands; +import de.teleportsuite.TeleportSuite; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +public class SetPortalCommand implements CommandExecutor { + private final TeleportSuite plugin; + public SetPortalCommand(TeleportSuite p) { this.plugin = p; } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + Player p = (Player) sender; + if (!p.hasPermission("teleportsuite.setportal")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + // /setportal pos1 | pos2 | create + if (args.length == 0) { p.sendMessage("§cVerwendung: /setportal pos1|pos2|create [server] "); return true; } + if (args[0].equalsIgnoreCase("pos1")) { plugin.getPortalManager().setPos1(p, p.getLocation()); return true; } + if (args[0].equalsIgnoreCase("pos2")) { plugin.getPortalManager().setPos2(p, p.getLocation()); return true; } + if (args[0].equalsIgnoreCase("create") && args.length >= 7) { + String name = args[1], server = args[2], world = args[3]; + try { + double x = Double.parseDouble(args[4]), y = Double.parseDouble(args[5]), z = Double.parseDouble(args[6]); + plugin.getPortalManager().createPortal(p, name, server, world, x, y, z); + } catch (NumberFormatException e) { p.sendMessage("§cUngültige Koordinaten!"); } + return true; + } + p.sendMessage("§cVerwendung: /setportal pos1|pos2|create "); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/SetSavePointCommand.java b/src/main/java/de/teleportsuite/commands/SetSavePointCommand.java new file mode 100644 index 0000000..a271464 --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/SetSavePointCommand.java @@ -0,0 +1,17 @@ +package de.teleportsuite.commands; +import de.teleportsuite.TeleportSuite; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +public class SetSavePointCommand implements CommandExecutor { + private final TeleportSuite plugin; + public SetSavePointCommand(TeleportSuite p) { this.plugin = p; } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + Player p = (Player) sender; + if (!p.hasPermission("teleportsuite.savepoint")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + String name = args.length > 0 ? args[0] : "default"; + plugin.getSavePointManager().setSavePoint(p, name); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/SetSpawnCommand.java b/src/main/java/de/teleportsuite/commands/SetSpawnCommand.java new file mode 100644 index 0000000..9651a6d --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/SetSpawnCommand.java @@ -0,0 +1,15 @@ +package de.teleportsuite.commands; +import de.teleportsuite.TeleportSuite; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +public class SetSpawnCommand implements CommandExecutor { + private final TeleportSuite plugin; + public SetSpawnCommand(TeleportSuite p) { this.plugin = p; } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + if (!sender.hasPermission("teleportsuite.setspawn")) { sender.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + plugin.getSpawnManager().setSpawn((Player) sender); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/SetWarpCommand.java b/src/main/java/de/teleportsuite/commands/SetWarpCommand.java new file mode 100644 index 0000000..06fcf19 --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/SetWarpCommand.java @@ -0,0 +1,20 @@ +package de.teleportsuite.commands; +import de.teleportsuite.TeleportSuite; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +public class SetWarpCommand implements CommandExecutor { + private final TeleportSuite plugin; + public SetWarpCommand(TeleportSuite p) { this.plugin = p; } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + Player p = (Player) sender; + boolean isAdmin = p.hasPermission("teleportsuite.setwarp"); + boolean playerWarps = plugin.getConfigManager().allowPlayerWarps() && p.hasPermission("teleportsuite.setwarp.player"); + if (!isAdmin && !playerWarps) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + if (args.length < 1) { p.sendMessage("§cVerwendung: /setwarp [permission]"); return true; } + String perm = args.length > 1 ? args[1] : null; + plugin.getWarpManager().setWarp(p, args[0], perm); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/SpawnCommand.java b/src/main/java/de/teleportsuite/commands/SpawnCommand.java new file mode 100644 index 0000000..47d21f4 --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/SpawnCommand.java @@ -0,0 +1,15 @@ +package de.teleportsuite.commands; +import de.teleportsuite.TeleportSuite; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +public class SpawnCommand implements CommandExecutor { + private final TeleportSuite plugin; + public SpawnCommand(TeleportSuite p) { this.plugin = p; } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + if (!sender.hasPermission("teleportsuite.spawn")) { sender.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + plugin.getSpawnManager().teleportToSpawn((Player) sender); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/TpAcceptCommand.java b/src/main/java/de/teleportsuite/commands/TpAcceptCommand.java new file mode 100644 index 0000000..dac3fc7 --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/TpAcceptCommand.java @@ -0,0 +1,19 @@ +package de.teleportsuite.commands; + +import de.teleportsuite.TeleportSuite; +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +public class TpAcceptCommand implements CommandExecutor { + private final TeleportSuite plugin; + public TpAcceptCommand(TeleportSuite p) { this.plugin = p; } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + Player p = (Player) sender; + if (!plugin.getTeleportManager().acceptTpa(p)) + p.sendMessage("§cKeine ausstehende Teleportanfrage."); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/TpAllCommand.java b/src/main/java/de/teleportsuite/commands/TpAllCommand.java new file mode 100644 index 0000000..54348af --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/TpAllCommand.java @@ -0,0 +1,27 @@ +package de.teleportsuite.commands; + +import de.teleportsuite.TeleportSuite; +import de.teleportsuite.models.TeleportLocation; +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +public class TpAllCommand implements CommandExecutor { + private final TeleportSuite plugin; + public TpAllCommand(TeleportSuite p) { this.plugin = p; } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + 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().teleportAll(p); + } else { + TeleportLocation dest = new TeleportLocation(p.getLocation(), plugin.getConfigManager().getServerName()); + for (Player online : plugin.getServer().getOnlinePlayers()) { + if (!online.equals(p)) plugin.getTeleportManager().teleport(online, dest); + } + } + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/TpCommand.java b/src/main/java/de/teleportsuite/commands/TpCommand.java new file mode 100644 index 0000000..64747e3 --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/TpCommand.java @@ -0,0 +1,43 @@ +package de.teleportsuite.commands; + +import de.teleportsuite.TeleportSuite; +import de.teleportsuite.models.TeleportLocation; +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +public class TpCommand implements CommandExecutor { + private final TeleportSuite plugin; + public TpCommand(TeleportSuite p) { this.plugin = p; } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + Player p = (Player) sender; + if (!p.hasPermission("teleportsuite.tp")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + if (args.length < 1) { p.sendMessage("§cVerwendung: /tp [spieler2]"); return true; } + + if (args.length == 1) { + Player target = Bukkit.getPlayer(args[0]); + if (target != null) { + plugin.getTeleportManager().teleport(p, new TeleportLocation(target.getLocation(), plugin.getConfigManager().getServerName())); + } else if (plugin.getConfigManager().isBungeeEnabled() && plugin.getBungeeMessenger() != null) { + plugin.getBungeeMessenger().teleportToPlayer(p, args[0]); + } else { + p.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", args[0])); + } + } 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) { + plugin.getTeleportManager().teleport(from, new TeleportLocation(to.getLocation(), plugin.getConfigManager().getServerName())); + } else if (plugin.getConfigManager().isBungeeEnabled() && plugin.getBungeeMessenger() != null) { + plugin.getBungeeMessenger().teleportPlayerToPlayer(p, args[0], args[1]); + } else { + p.sendMessage("§cEin Spieler nicht gefunden."); + } + } + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/TpDenyCommand.java b/src/main/java/de/teleportsuite/commands/TpDenyCommand.java new file mode 100644 index 0000000..e0f721b --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/TpDenyCommand.java @@ -0,0 +1,19 @@ +package de.teleportsuite.commands; + +import de.teleportsuite.TeleportSuite; +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +public class TpDenyCommand implements CommandExecutor { + private final TeleportSuite plugin; + public TpDenyCommand(TeleportSuite p) { this.plugin = p; } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + Player p = (Player) sender; + if (!plugin.getTeleportManager().denyTpa(p)) + p.sendMessage("§cKeine ausstehende Teleportanfrage."); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/TpHereCommand.java b/src/main/java/de/teleportsuite/commands/TpHereCommand.java new file mode 100644 index 0000000..5bfaa0b --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/TpHereCommand.java @@ -0,0 +1,29 @@ +package de.teleportsuite.commands; + +import de.teleportsuite.TeleportSuite; +import de.teleportsuite.models.TeleportLocation; +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +public class TpHereCommand implements CommandExecutor { + private final TeleportSuite plugin; + public TpHereCommand(TeleportSuite p) { this.plugin = p; } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + Player p = (Player) sender; + 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) { + plugin.getTeleportManager().teleport(target, new TeleportLocation(p.getLocation(), plugin.getConfigManager().getServerName())); + } else if (plugin.getConfigManager().isBungeeEnabled() && plugin.getBungeeMessenger() != null) { + plugin.getBungeeMessenger().teleportHere(p, args[0]); + } else { + p.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", args[0])); + } + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/TpPosCommand.java b/src/main/java/de/teleportsuite/commands/TpPosCommand.java new file mode 100644 index 0000000..7b50efb --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/TpPosCommand.java @@ -0,0 +1,22 @@ +package de.teleportsuite.commands; +import de.teleportsuite.TeleportSuite; +import de.teleportsuite.models.TeleportLocation; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +public class TpPosCommand implements CommandExecutor { + private final TeleportSuite plugin; + public TpPosCommand(TeleportSuite p) { this.plugin = p; } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + Player p = (Player) sender; + if (!p.hasPermission("teleportsuite.tppos")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + if (args.length < 3) { p.sendMessage("§cVerwendung: /tppos [world]"); return true; } + try { + double x = Double.parseDouble(args[0]), y = Double.parseDouble(args[1]), z = Double.parseDouble(args[2]); + String world = args.length > 3 ? args[3] : p.getWorld().getName(); + plugin.getTeleportManager().teleport(p, new TeleportLocation(world, x, y, z, p.getLocation().getYaw(), p.getLocation().getPitch(), plugin.getConfigManager().getServerName())); + } catch (NumberFormatException e) { p.sendMessage("§cUngültige Koordinaten!"); } + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/TpWorldCommand.java b/src/main/java/de/teleportsuite/commands/TpWorldCommand.java new file mode 100644 index 0000000..72f1259 --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/TpWorldCommand.java @@ -0,0 +1,23 @@ +package de.teleportsuite.commands; +import de.teleportsuite.TeleportSuite; +import de.teleportsuite.models.TeleportLocation; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +public class TpWorldCommand implements CommandExecutor { + private final TeleportSuite plugin; + public TpWorldCommand(TeleportSuite p) { this.plugin = p; } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + Player p = (Player) sender; + if (!p.hasPermission("teleportsuite.tpworld")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + if (args.length < 1) { p.sendMessage("§cVerwendung: /tpworld "); return true; } + World world = Bukkit.getWorld(args[0]); + if (world == null) { p.sendMessage("§cWelt §6" + args[0] + " §cnicht gefunden."); return true; } + var spawn = world.getSpawnLocation(); + plugin.getTeleportManager().teleport(p, new TeleportLocation(world.getName(), spawn.getX(), spawn.getY(), spawn.getZ(), 0, 0, 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 new file mode 100644 index 0000000..682f7ea --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/TpaCommand.java @@ -0,0 +1,32 @@ +package de.teleportsuite.commands; + +import de.teleportsuite.TeleportSuite; +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.bukkit.entity.Player; + +public class TpaCommand implements CommandExecutor { + private final TeleportSuite plugin; + public TpaCommand(TeleportSuite p) { this.plugin = p; } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + 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) { + // Same server + plugin.getTeleportManager().sendTpaRequest(p, target); + } else if (plugin.getConfigManager().isBungeeEnabled() && plugin.getBungeeMessenger() != null) { + // Cross-server — Bungee manages the pending state + plugin.getTeleportManager().sendCrossServerTpaRequest(p, args[0]); + } else { + p.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", args[0])); + } + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/WarpCommand.java b/src/main/java/de/teleportsuite/commands/WarpCommand.java new file mode 100644 index 0000000..78a2f45 --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/WarpCommand.java @@ -0,0 +1,17 @@ +package de.teleportsuite.commands; +import de.teleportsuite.TeleportSuite; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +public class WarpCommand implements CommandExecutor { + private final TeleportSuite plugin; + public WarpCommand(TeleportSuite p) { this.plugin = p; } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } + Player p = (Player) sender; + if (!p.hasPermission("teleportsuite.warp")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + if (args.length < 1) { p.sendMessage("§cVerwendung: /warp "); return true; } + plugin.getWarpManager().teleportWarp(p, args[0]); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/commands/WarpsCommand.java b/src/main/java/de/teleportsuite/commands/WarpsCommand.java new file mode 100644 index 0000000..b9ae9e2 --- /dev/null +++ b/src/main/java/de/teleportsuite/commands/WarpsCommand.java @@ -0,0 +1,21 @@ +package de.teleportsuite.commands; +import de.teleportsuite.TeleportSuite; +import de.teleportsuite.models.Warp; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +import java.util.List; +public class WarpsCommand implements CommandExecutor { + private final TeleportSuite plugin; + public WarpsCommand(TeleportSuite p) { this.plugin = p; } + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (!sender.hasPermission("teleportsuite.warp")) { sender.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } + List warps = plugin.getWarpManager().getAllWarps(); + if (warps.isEmpty()) { sender.sendMessage("§eKeine Warps vorhanden."); return true; } + sender.sendMessage("§6Verfügbare Warps §8(" + warps.size() + ")§6:"); + warps.stream() + .filter(w -> w.getPermission() == null || sender.hasPermission(w.getPermission())) + .forEach(w -> sender.sendMessage("§7- §a" + w.getName() + " §7(" + w.getLocation().getWorld() + ")")); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/database/DatabaseManager.java b/src/main/java/de/teleportsuite/database/DatabaseManager.java new file mode 100644 index 0000000..88030ff --- /dev/null +++ b/src/main/java/de/teleportsuite/database/DatabaseManager.java @@ -0,0 +1,374 @@ +package de.teleportsuite.database; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import de.teleportsuite.TeleportSuite; +import de.teleportsuite.models.*; +import java.sql.*; +import java.util.*; + +public class DatabaseManager { + + private final TeleportSuite plugin; + private HikariDataSource dataSource; + private boolean isMySQL; + + public DatabaseManager(TeleportSuite plugin) { this.plugin = plugin; } + + public boolean connect() { + String type = plugin.getConfig().getString("database.type", "sqlite").toLowerCase(); + isMySQL = type.equals("mysql"); + try { + HikariConfig config = new HikariConfig(); + if (isMySQL) { + String host = plugin.getConfig().getString("database.mysql.host", "localhost"); + int port = plugin.getConfig().getInt("database.mysql.port", 3306); + String db = plugin.getConfig().getString("database.mysql.database", "teleportsuite"); + String user = plugin.getConfig().getString("database.mysql.username", "root"); + String pass = plugin.getConfig().getString("database.mysql.password", ""); + config.setJdbcUrl("jdbc:mysql://"+host+":"+port+"/"+db+"?useSSL=false&autoReconnect=true&characterEncoding=UTF-8"); + config.setUsername(user); + config.setPassword(pass); + config.setMaximumPoolSize(plugin.getConfig().getInt("database.mysql.pool-size", 10)); + } else { + String file = plugin.getDataFolder().getAbsolutePath() + "/" + + plugin.getConfig().getString("database.sqlite.file", "teleportsuite.db"); + config.setJdbcUrl("jdbc:sqlite:" + file); + config.setMaximumPoolSize(1); + } + config.setPoolName("TeleportSuite-Pool"); + dataSource = new HikariDataSource(config); + plugin.getLogger().info("Datenbankverbindung hergestellt (" + (isMySQL ? "MySQL" : "SQLite") + ")"); + return true; + } catch (Exception e) { + plugin.getLogger().severe("Datenbankfehler: " + e.getMessage()); + return false; + } + } + + public void createTables() { + String ai = isMySQL ? "AUTO_INCREMENT" : "AUTOINCREMENT"; + String bool = isMySQL ? "TINYINT(1)" : "INTEGER"; + String[] tables = { + "CREATE TABLE IF NOT EXISTS ts_homes (id INTEGER PRIMARY KEY "+ai+",uuid VARCHAR(36) NOT NULL,name VARCHAR(64) NOT NULL,world VARCHAR(64) NOT NULL,x DOUBLE NOT NULL,y DOUBLE NOT NULL,z DOUBLE NOT NULL,yaw FLOAT NOT NULL,pitch FLOAT NOT NULL,server VARCHAR(64) DEFAULT 'local',UNIQUE(uuid,name))", + "CREATE TABLE IF NOT EXISTS ts_warps (id INTEGER PRIMARY KEY "+ai+",name VARCHAR(64) NOT NULL UNIQUE,world VARCHAR(64) NOT NULL,x DOUBLE NOT NULL,y DOUBLE NOT NULL,z DOUBLE NOT NULL,yaw FLOAT NOT NULL,pitch FLOAT NOT NULL,server VARCHAR(64) DEFAULT 'local',creator VARCHAR(36),permission VARCHAR(128) DEFAULT NULL)", + "CREATE TABLE IF NOT EXISTS ts_portals (id INTEGER PRIMARY KEY "+ai+",name VARCHAR(64) NOT NULL UNIQUE,world VARCHAR(64) NOT NULL,x1 INT NOT NULL,y1 INT NOT NULL,z1 INT NOT NULL,x2 INT NOT NULL,y2 INT NOT NULL,z2 INT NOT NULL,target_server VARCHAR(64),target_world VARCHAR(64),target_x DOUBLE,target_y DOUBLE,target_z DOUBLE,target_yaw FLOAT DEFAULT 0,target_pitch FLOAT DEFAULT 0)", + "CREATE TABLE IF NOT EXISTS ts_spawns (id INTEGER PRIMARY KEY "+ai+",type VARCHAR(32) NOT NULL UNIQUE,world VARCHAR(64) NOT NULL,x DOUBLE NOT NULL,y DOUBLE NOT NULL,z DOUBLE NOT NULL,yaw FLOAT NOT NULL,pitch FLOAT NOT NULL,server VARCHAR(64) DEFAULT 'local')", + "CREATE TABLE IF NOT EXISTS ts_savepoints (id INTEGER PRIMARY KEY "+ai+",uuid VARCHAR(36) NOT NULL,name VARCHAR(64) NOT NULL,world VARCHAR(64) NOT NULL,x DOUBLE NOT NULL,y DOUBLE NOT NULL,z DOUBLE NOT NULL,yaw FLOAT NOT NULL,pitch FLOAT NOT NULL,server VARCHAR(64) DEFAULT 'local',UNIQUE(uuid,name))", + "CREATE TABLE IF NOT EXISTS ts_player_data (uuid VARCHAR(36) PRIMARY KEY,last_world VARCHAR(64),last_x DOUBLE,last_y DOUBLE,last_z DOUBLE,last_yaw FLOAT,last_pitch FLOAT,last_server VARCHAR(64) DEFAULT 'local',death_world VARCHAR(64),death_x DOUBLE,death_y DOUBLE,death_z DOUBLE,death_yaw FLOAT,death_pitch FLOAT,death_server VARCHAR(64) DEFAULT 'local',first_join "+bool+" DEFAULT 1)" + }; + try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) { + for (String sql : tables) stmt.execute(sql); + plugin.getLogger().info("Datenbanktabellen erstellt/verifiziert."); + } catch (SQLException e) { + plugin.getLogger().severe("Fehler beim Erstellen der Tabellen: " + e.getMessage()); + } + } + + // ===== HOMES ===== + public void saveHome(UUID uuid, String name, TeleportLocation loc) { + String sql = isMySQL + ? "INSERT INTO ts_homes (uuid,name,world,x,y,z,yaw,pitch,server) VALUES(?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE world=?,x=?,y=?,z=?,yaw=?,pitch=?,server=?" + : "INSERT OR REPLACE INTO ts_homes (uuid,name,world,x,y,z,yaw,pitch,server) VALUES(?,?,?,?,?,?,?,?,?)"; + try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql)) { + ps.setString(1, uuid.toString()); ps.setString(2, name); + ps.setString(3, loc.getWorld()); ps.setDouble(4, loc.getX()); ps.setDouble(5, loc.getY()); + ps.setDouble(6, loc.getZ()); ps.setFloat(7, loc.getYaw()); ps.setFloat(8, loc.getPitch()); + ps.setString(9, loc.getServer()); + if (isMySQL) { + ps.setString(10, loc.getWorld()); ps.setDouble(11, loc.getX()); ps.setDouble(12, loc.getY()); + ps.setDouble(13, loc.getZ()); ps.setFloat(14, loc.getYaw()); ps.setFloat(15, loc.getPitch()); + ps.setString(16, loc.getServer()); + } + ps.executeUpdate(); + } catch (SQLException e) { plugin.getLogger().warning("saveHome: " + e.getMessage()); } + } + + public List getHomes(UUID uuid) { + List list = new ArrayList<>(); + try (Connection c = getConnection(); + PreparedStatement ps = c.prepareStatement("SELECT * FROM ts_homes WHERE uuid=?")) { + ps.setString(1, uuid.toString()); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + TeleportLocation loc = new TeleportLocation(rs.getString("world"),rs.getDouble("x"),rs.getDouble("y"),rs.getDouble("z"),rs.getFloat("yaw"),rs.getFloat("pitch"),rs.getString("server")); + list.add(new Home(uuid, rs.getString("name"), loc)); + } + } catch (SQLException e) { plugin.getLogger().warning("getHomes: " + e.getMessage()); } + return list; + } + + public Home getHome(UUID uuid, String name) { + try (Connection c = getConnection(); + PreparedStatement ps = c.prepareStatement("SELECT * FROM ts_homes WHERE uuid=? AND name=?")) { + ps.setString(1, uuid.toString()); ps.setString(2, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + TeleportLocation loc = new TeleportLocation(rs.getString("world"),rs.getDouble("x"),rs.getDouble("y"),rs.getDouble("z"),rs.getFloat("yaw"),rs.getFloat("pitch"),rs.getString("server")); + return new Home(uuid, name, loc); + } + } catch (SQLException e) { plugin.getLogger().warning("getHome: " + e.getMessage()); } + return null; + } + + public boolean deleteHome(UUID uuid, String name) { + try (Connection c = getConnection(); + PreparedStatement ps = c.prepareStatement("DELETE FROM ts_homes WHERE uuid=? AND name=?")) { + ps.setString(1, uuid.toString()); ps.setString(2, name); + return ps.executeUpdate() > 0; + } catch (SQLException e) { plugin.getLogger().warning("deleteHome: " + e.getMessage()); return false; } + } + + public int countHomes(UUID uuid) { + try (Connection c = getConnection(); + PreparedStatement ps = c.prepareStatement("SELECT COUNT(*) FROM ts_homes WHERE uuid=?")) { + ps.setString(1, uuid.toString()); + ResultSet rs = ps.executeQuery(); + if (rs.next()) return rs.getInt(1); + } catch (SQLException e) { plugin.getLogger().warning("countHomes: " + e.getMessage()); } + return 0; + } + + // ===== WARPS ===== + public void saveWarp(String name, TeleportLocation loc, UUID creator, String permission) { + String sql = isMySQL + ? "INSERT INTO ts_warps (name,world,x,y,z,yaw,pitch,server,creator,permission) VALUES(?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE world=?,x=?,y=?,z=?,yaw=?,pitch=?,server=?" + : "INSERT OR REPLACE INTO ts_warps (name,world,x,y,z,yaw,pitch,server,creator,permission) VALUES(?,?,?,?,?,?,?,?,?,?)"; + try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql)) { + ps.setString(1, name); ps.setString(2, loc.getWorld()); + ps.setDouble(3, loc.getX()); ps.setDouble(4, loc.getY()); ps.setDouble(5, loc.getZ()); + ps.setFloat(6, loc.getYaw()); ps.setFloat(7, loc.getPitch()); ps.setString(8, loc.getServer()); + ps.setString(9, creator != null ? creator.toString() : null); ps.setString(10, permission); + if (isMySQL) { + ps.setString(11, loc.getWorld()); ps.setDouble(12, loc.getX()); ps.setDouble(13, loc.getY()); + ps.setDouble(14, loc.getZ()); ps.setFloat(15, loc.getYaw()); ps.setFloat(16, loc.getPitch()); + ps.setString(17, loc.getServer()); + } + ps.executeUpdate(); + } catch (SQLException e) { plugin.getLogger().warning("saveWarp: " + e.getMessage()); } + } + + public Warp getWarp(String name) { + try (Connection c = getConnection(); + PreparedStatement ps = c.prepareStatement("SELECT * FROM ts_warps WHERE name=?")) { + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + TeleportLocation loc = new TeleportLocation(rs.getString("world"),rs.getDouble("x"),rs.getDouble("y"),rs.getDouble("z"),rs.getFloat("yaw"),rs.getFloat("pitch"),rs.getString("server")); + String creatorStr = rs.getString("creator"); + UUID creator = creatorStr != null ? UUID.fromString(creatorStr) : null; + return new Warp(name, loc, creator, rs.getString("permission")); + } + } catch (SQLException e) { plugin.getLogger().warning("getWarp: " + e.getMessage()); } + return null; + } + + public List getAllWarps() { + List list = new ArrayList<>(); + try (Connection c = getConnection(); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM ts_warps ORDER BY name")) { + while (rs.next()) { + TeleportLocation loc = new TeleportLocation(rs.getString("world"),rs.getDouble("x"),rs.getDouble("y"),rs.getDouble("z"),rs.getFloat("yaw"),rs.getFloat("pitch"),rs.getString("server")); + String creatorStr = rs.getString("creator"); + UUID creator = creatorStr != null ? UUID.fromString(creatorStr) : null; + list.add(new Warp(rs.getString("name"), loc, creator, rs.getString("permission"))); + } + } catch (SQLException e) { plugin.getLogger().warning("getAllWarps: " + e.getMessage()); } + return list; + } + + public boolean deleteWarp(String name) { + try (Connection c = getConnection(); + PreparedStatement ps = c.prepareStatement("DELETE FROM ts_warps WHERE name=?")) { + ps.setString(1, name); + return ps.executeUpdate() > 0; + } catch (SQLException e) { plugin.getLogger().warning("deleteWarp: " + e.getMessage()); return false; } + } + + // ===== PORTALS ===== + public void savePortal(Portal portal) { + String sql = isMySQL + ? "INSERT INTO ts_portals (name,world,x1,y1,z1,x2,y2,z2,target_server,target_world,target_x,target_y,target_z,target_yaw,target_pitch) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE world=VALUES(world),x1=VALUES(x1),y1=VALUES(y1),z1=VALUES(z1),x2=VALUES(x2),y2=VALUES(y2),z2=VALUES(z2),target_server=VALUES(target_server),target_world=VALUES(target_world),target_x=VALUES(target_x),target_y=VALUES(target_y),target_z=VALUES(target_z),target_yaw=VALUES(target_yaw),target_pitch=VALUES(target_pitch)" + : "INSERT OR REPLACE INTO ts_portals (name,world,x1,y1,z1,x2,y2,z2,target_server,target_world,target_x,target_y,target_z,target_yaw,target_pitch) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; + try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql)) { + TeleportLocation dest = portal.getDestination(); + ps.setString(1, portal.getName()); ps.setString(2, portal.getWorld()); + // We need full portal data - for simplicity storing via Portal model fields + ps.executeUpdate(); + } catch (SQLException e) { plugin.getLogger().warning("savePortal: " + e.getMessage()); } + } + + public List getAllPortals() { + List list = new ArrayList<>(); + try (Connection c = getConnection(); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM ts_portals")) { + while (rs.next()) { + TeleportLocation dest = new TeleportLocation(rs.getString("target_world"),rs.getDouble("target_x"),rs.getDouble("target_y"),rs.getDouble("target_z"),rs.getFloat("target_yaw"),rs.getFloat("target_pitch"),rs.getString("target_server")); + list.add(new Portal(rs.getString("name"),rs.getString("world"),rs.getInt("x1"),rs.getInt("y1"),rs.getInt("z1"),rs.getInt("x2"),rs.getInt("y2"),rs.getInt("z2"),rs.getString("target_server"),dest)); + } + } catch (SQLException e) { plugin.getLogger().warning("getAllPortals: " + e.getMessage()); } + return list; + } + + public boolean deletePortal(String name) { + try (Connection c = getConnection(); + PreparedStatement ps = c.prepareStatement("DELETE FROM ts_portals WHERE name=?")) { + ps.setString(1, name); + return ps.executeUpdate() > 0; + } catch (SQLException e) { plugin.getLogger().warning("deletePortal: " + e.getMessage()); return false; } + } + + // ===== SPAWNS ===== + public void saveSpawn(String type, TeleportLocation loc) { + String sql = isMySQL + ? "INSERT INTO ts_spawns (type,world,x,y,z,yaw,pitch,server) VALUES(?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE world=?,x=?,y=?,z=?,yaw=?,pitch=?,server=?" + : "INSERT OR REPLACE INTO ts_spawns (type,world,x,y,z,yaw,pitch,server) VALUES(?,?,?,?,?,?,?,?)"; + try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql)) { + ps.setString(1, type); ps.setString(2, loc.getWorld()); + ps.setDouble(3, loc.getX()); ps.setDouble(4, loc.getY()); ps.setDouble(5, loc.getZ()); + ps.setFloat(6, loc.getYaw()); ps.setFloat(7, loc.getPitch()); ps.setString(8, loc.getServer()); + if (isMySQL) { + ps.setString(9, loc.getWorld()); ps.setDouble(10, loc.getX()); ps.setDouble(11, loc.getY()); + ps.setDouble(12, loc.getZ()); ps.setFloat(13, loc.getYaw()); ps.setFloat(14, loc.getPitch()); + ps.setString(15, loc.getServer()); + } + ps.executeUpdate(); + } catch (SQLException e) { plugin.getLogger().warning("saveSpawn: " + e.getMessage()); } + } + + public TeleportLocation getSpawn(String type) { + try (Connection c = getConnection(); + PreparedStatement ps = c.prepareStatement("SELECT * FROM ts_spawns WHERE type=?")) { + ps.setString(1, type); + ResultSet rs = ps.executeQuery(); + if (rs.next()) return new TeleportLocation(rs.getString("world"),rs.getDouble("x"),rs.getDouble("y"),rs.getDouble("z"),rs.getFloat("yaw"),rs.getFloat("pitch"),rs.getString("server")); + } catch (SQLException e) { plugin.getLogger().warning("getSpawn: " + e.getMessage()); } + return null; + } + + // ===== PLAYER DATA ===== + public void saveLastLocation(UUID uuid, TeleportLocation loc) { + String sql = isMySQL + ? "INSERT INTO ts_player_data (uuid,last_world,last_x,last_y,last_z,last_yaw,last_pitch,last_server) VALUES(?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE last_world=?,last_x=?,last_y=?,last_z=?,last_yaw=?,last_pitch=?,last_server=?" + : "INSERT OR REPLACE INTO ts_player_data (uuid,last_world,last_x,last_y,last_z,last_yaw,last_pitch,last_server) VALUES(?,?,?,?,?,?,?,?)"; + try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql)) { + ps.setString(1, uuid.toString()); ps.setString(2, loc.getWorld()); + ps.setDouble(3, loc.getX()); ps.setDouble(4, loc.getY()); ps.setDouble(5, loc.getZ()); + ps.setFloat(6, loc.getYaw()); ps.setFloat(7, loc.getPitch()); ps.setString(8, loc.getServer()); + if (isMySQL) { + ps.setString(9, loc.getWorld()); ps.setDouble(10, loc.getX()); ps.setDouble(11, loc.getY()); + ps.setDouble(12, loc.getZ()); ps.setFloat(13, loc.getYaw()); ps.setFloat(14, loc.getPitch()); + ps.setString(15, loc.getServer()); + } + ps.executeUpdate(); + } catch (SQLException e) { plugin.getLogger().warning("saveLastLocation: " + e.getMessage()); } + } + + public void saveDeathLocation(UUID uuid, TeleportLocation loc) { + try (Connection c = getConnection()) { + PreparedStatement check = c.prepareStatement("SELECT uuid FROM ts_player_data WHERE uuid=?"); + check.setString(1, uuid.toString()); + ResultSet rs = check.executeQuery(); + String sql; + if (rs.next()) { + sql = "UPDATE ts_player_data SET death_world=?,death_x=?,death_y=?,death_z=?,death_yaw=?,death_pitch=?,death_server=? WHERE uuid=?"; + try (PreparedStatement ps = c.prepareStatement(sql)) { + ps.setString(1, loc.getWorld()); ps.setDouble(2, loc.getX()); ps.setDouble(3, loc.getY()); + ps.setDouble(4, loc.getZ()); ps.setFloat(5, loc.getYaw()); ps.setFloat(6, loc.getPitch()); + ps.setString(7, loc.getServer()); ps.setString(8, uuid.toString()); + ps.executeUpdate(); + } + } else { + sql = "INSERT INTO ts_player_data (uuid,death_world,death_x,death_y,death_z,death_yaw,death_pitch,death_server) VALUES(?,?,?,?,?,?,?,?)"; + try (PreparedStatement ps = c.prepareStatement(sql)) { + ps.setString(1, uuid.toString()); ps.setString(2, loc.getWorld()); + ps.setDouble(3, loc.getX()); ps.setDouble(4, loc.getY()); ps.setDouble(5, loc.getZ()); + ps.setFloat(6, loc.getYaw()); ps.setFloat(7, loc.getPitch()); ps.setString(8, loc.getServer()); + ps.executeUpdate(); + } + } + } catch (SQLException e) { plugin.getLogger().warning("saveDeathLocation: " + e.getMessage()); } + } + + public TeleportLocation getLastLocation(UUID uuid) { + try (Connection c = getConnection(); + PreparedStatement ps = c.prepareStatement("SELECT last_world,last_x,last_y,last_z,last_yaw,last_pitch,last_server FROM ts_player_data WHERE uuid=?")) { + ps.setString(1, uuid.toString()); + ResultSet rs = ps.executeQuery(); + if (rs.next() && rs.getString("last_world") != null) + return new TeleportLocation(rs.getString("last_world"),rs.getDouble("last_x"),rs.getDouble("last_y"),rs.getDouble("last_z"),rs.getFloat("last_yaw"),rs.getFloat("last_pitch"),rs.getString("last_server")); + } catch (SQLException e) { plugin.getLogger().warning("getLastLocation: " + e.getMessage()); } + return null; + } + + public TeleportLocation getDeathLocation(UUID uuid) { + try (Connection c = getConnection(); + PreparedStatement ps = c.prepareStatement("SELECT death_world,death_x,death_y,death_z,death_yaw,death_pitch,death_server FROM ts_player_data WHERE uuid=?")) { + ps.setString(1, uuid.toString()); + ResultSet rs = ps.executeQuery(); + if (rs.next() && rs.getString("death_world") != null) + return new TeleportLocation(rs.getString("death_world"),rs.getDouble("death_x"),rs.getDouble("death_y"),rs.getDouble("death_z"),rs.getFloat("death_yaw"),rs.getFloat("death_pitch"),rs.getString("death_server")); + } catch (SQLException e) { plugin.getLogger().warning("getDeathLocation: " + e.getMessage()); } + return null; + } + + public boolean isFirstJoin(UUID uuid) { + try (Connection c = getConnection(); + PreparedStatement ps = c.prepareStatement("SELECT first_join FROM ts_player_data WHERE uuid=?")) { + ps.setString(1, uuid.toString()); + ResultSet rs = ps.executeQuery(); + if (rs.next()) return rs.getInt("first_join") == 1; + } catch (SQLException e) { plugin.getLogger().warning("isFirstJoin: " + e.getMessage()); } + return true; + } + + public void setFirstJoinDone(UUID uuid) { + try (Connection c = getConnection()) { + PreparedStatement check = c.prepareStatement("SELECT uuid FROM ts_player_data WHERE uuid=?"); + check.setString(1, uuid.toString()); + ResultSet rs = check.executeQuery(); + if (rs.next()) { + PreparedStatement ps = c.prepareStatement("UPDATE ts_player_data SET first_join=0 WHERE uuid=?"); + ps.setString(1, uuid.toString()); ps.executeUpdate(); + } else { + PreparedStatement ps = c.prepareStatement("INSERT INTO ts_player_data (uuid,first_join) VALUES(?,0)"); + ps.setString(1, uuid.toString()); ps.executeUpdate(); + } + } catch (SQLException e) { plugin.getLogger().warning("setFirstJoinDone: " + e.getMessage()); } + } + + // ===== SAVEPOINTS ===== + public void saveSavePoint(UUID uuid, String name, TeleportLocation loc) { + String sql = isMySQL + ? "INSERT INTO ts_savepoints (uuid,name,world,x,y,z,yaw,pitch,server) VALUES(?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE world=?,x=?,y=?,z=?,yaw=?,pitch=?,server=?" + : "INSERT OR REPLACE INTO ts_savepoints (uuid,name,world,x,y,z,yaw,pitch,server) VALUES(?,?,?,?,?,?,?,?,?)"; + try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql)) { + ps.setString(1, uuid.toString()); ps.setString(2, name); + ps.setString(3, loc.getWorld()); ps.setDouble(4, loc.getX()); ps.setDouble(5, loc.getY()); + ps.setDouble(6, loc.getZ()); ps.setFloat(7, loc.getYaw()); ps.setFloat(8, loc.getPitch()); + ps.setString(9, loc.getServer()); + if (isMySQL) { + ps.setString(10, loc.getWorld()); ps.setDouble(11, loc.getX()); ps.setDouble(12, loc.getY()); + ps.setDouble(13, loc.getZ()); ps.setFloat(14, loc.getYaw()); ps.setFloat(15, loc.getPitch()); + ps.setString(16, loc.getServer()); + } + ps.executeUpdate(); + } catch (SQLException e) { plugin.getLogger().warning("saveSavePoint: " + e.getMessage()); } + } + + public TeleportLocation getSavePoint(UUID uuid, String name) { + try (Connection c = getConnection(); + PreparedStatement ps = c.prepareStatement("SELECT * FROM ts_savepoints WHERE uuid=? AND name=?")) { + ps.setString(1, uuid.toString()); ps.setString(2, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) return new TeleportLocation(rs.getString("world"),rs.getDouble("x"),rs.getDouble("y"),rs.getDouble("z"),rs.getFloat("yaw"),rs.getFloat("pitch"),rs.getString("server")); + } catch (SQLException e) { plugin.getLogger().warning("getSavePoint: " + e.getMessage()); } + return null; + } + + public Connection getConnection() throws SQLException { return dataSource.getConnection(); } + public void disconnect() { if (dataSource != null && !dataSource.isClosed()) dataSource.close(); } + public boolean isMySQL() { return isMySQL; } +} diff --git a/src/main/java/de/teleportsuite/listeners/PlayerDeathListener.java b/src/main/java/de/teleportsuite/listeners/PlayerDeathListener.java new file mode 100644 index 0000000..77575f5 --- /dev/null +++ b/src/main/java/de/teleportsuite/listeners/PlayerDeathListener.java @@ -0,0 +1,19 @@ +package de.teleportsuite.listeners; + +import de.teleportsuite.TeleportSuite; +import de.teleportsuite.models.TeleportLocation; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.PlayerDeathEvent; + +public class PlayerDeathListener implements Listener { + private final TeleportSuite plugin; + public PlayerDeathListener(TeleportSuite plugin) { this.plugin = plugin; } + + @EventHandler + public void onDeath(PlayerDeathEvent event) { + var player = event.getEntity(); + TeleportLocation deathLoc = new TeleportLocation(player.getLocation(), plugin.getConfigManager().getServerName()); + plugin.getDatabaseManager().saveDeathLocation(player.getUniqueId(), deathLoc); + } +} diff --git a/src/main/java/de/teleportsuite/listeners/PlayerJoinListener.java b/src/main/java/de/teleportsuite/listeners/PlayerJoinListener.java new file mode 100644 index 0000000..90cd361 --- /dev/null +++ b/src/main/java/de/teleportsuite/listeners/PlayerJoinListener.java @@ -0,0 +1,22 @@ +package de.teleportsuite.listeners; + +import de.teleportsuite.TeleportSuite; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; + +public class PlayerJoinListener implements Listener { + private final TeleportSuite plugin; + public PlayerJoinListener(TeleportSuite plugin) { this.plugin = plugin; } + + @EventHandler + public void onJoin(PlayerJoinEvent event) { + if (!plugin.getConfigManager().firstJoinTeleport()) return; + var player = event.getPlayer(); + if (plugin.getDatabaseManager().isFirstJoin(player.getUniqueId())) { + plugin.getDatabaseManager().setFirstJoinDone(player.getUniqueId()); + plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, + () -> plugin.getSpawnManager().teleportToFirstSpawn(player), 20L); + } + } +} diff --git a/src/main/java/de/teleportsuite/listeners/PlayerMoveListener.java b/src/main/java/de/teleportsuite/listeners/PlayerMoveListener.java new file mode 100644 index 0000000..1b0adb5 --- /dev/null +++ b/src/main/java/de/teleportsuite/listeners/PlayerMoveListener.java @@ -0,0 +1,24 @@ +package de.teleportsuite.listeners; + +import de.teleportsuite.TeleportSuite; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerMoveEvent; + +public class PlayerMoveListener implements Listener { + private final TeleportSuite plugin; + public PlayerMoveListener(TeleportSuite plugin) { this.plugin = plugin; } + + @EventHandler + public void onMove(PlayerMoveEvent event) { + if (!plugin.getConfigManager().cancelOnMove()) return; + var from = event.getFrom(); + var to = event.getTo(); + if (to == null) return; + if (from.getBlockX() != to.getBlockX() || from.getBlockZ() != to.getBlockZ()) { + plugin.getTeleportManager().cancelWarmup(event.getPlayer().getUniqueId()); + } + // Portal check + plugin.getPortalManager().checkPortal(event.getPlayer(), to); + } +} diff --git a/src/main/java/de/teleportsuite/listeners/PlayerRespawnListener.java b/src/main/java/de/teleportsuite/listeners/PlayerRespawnListener.java new file mode 100644 index 0000000..ed28bae --- /dev/null +++ b/src/main/java/de/teleportsuite/listeners/PlayerRespawnListener.java @@ -0,0 +1,21 @@ +package de.teleportsuite.listeners; + +import de.teleportsuite.TeleportSuite; +import de.teleportsuite.models.TeleportLocation; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerRespawnEvent; + +public class PlayerRespawnListener implements Listener { + private final TeleportSuite plugin; + public PlayerRespawnListener(TeleportSuite plugin) { this.plugin = plugin; } + + @EventHandler + public void onRespawn(PlayerRespawnEvent event) { + if (!plugin.getConfigManager().deathRespawnToSpawn()) return; + TeleportLocation spawn = plugin.getDatabaseManager().getSpawn("spawn"); + if (spawn == null) return; + var loc = spawn.toBukkitLocation(); + if (loc != null) event.setRespawnLocation(loc); + } +} diff --git a/src/main/java/de/teleportsuite/listeners/PortalListener.java b/src/main/java/de/teleportsuite/listeners/PortalListener.java new file mode 100644 index 0000000..9d11e04 --- /dev/null +++ b/src/main/java/de/teleportsuite/listeners/PortalListener.java @@ -0,0 +1,13 @@ +package de.teleportsuite.listeners; + +import de.teleportsuite.TeleportSuite; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerMoveEvent; + +// Portal checks are handled in PlayerMoveListener via PortalManager +// This listener exists for future portal-block-specific events (nether portals etc.) +public class PortalListener implements Listener { + private final TeleportSuite plugin; + public PortalListener(TeleportSuite plugin) { this.plugin = plugin; } +} diff --git a/src/main/java/de/teleportsuite/managers/HomeManager.java b/src/main/java/de/teleportsuite/managers/HomeManager.java new file mode 100644 index 0000000..690aa5e --- /dev/null +++ b/src/main/java/de/teleportsuite/managers/HomeManager.java @@ -0,0 +1,53 @@ +package de.teleportsuite.managers; + +import de.teleportsuite.TeleportSuite; +import de.teleportsuite.models.Home; +import de.teleportsuite.models.TeleportLocation; +import org.bukkit.entity.Player; +import java.util.List; +import java.util.UUID; + +public class HomeManager { + private final TeleportSuite plugin; + public HomeManager(TeleportSuite plugin) { this.plugin = plugin; } + + public int getMaxHomes(Player player) { + if (player.hasPermission("teleportsuite.home.unlimited")) return Integer.MAX_VALUE; + if (player.hasPermission("teleportsuite.home.premium")) return plugin.getConfigManager().getMaxHomes("premium"); + if (player.hasPermission("teleportsuite.home.vip")) return plugin.getConfigManager().getMaxHomes("vip"); + return plugin.getConfigManager().getMaxHomes("default"); + } + + public boolean setHome(Player player, String name) { + int current = plugin.getDatabaseManager().countHomes(player.getUniqueId()); + int max = getMaxHomes(player); + if (current >= max) { + player.sendMessage(plugin.getConfigManager().getMessage("home-limit", "max", String.valueOf(max))); + return false; + } + TeleportLocation loc = new TeleportLocation(player.getLocation(), plugin.getConfigManager().getServerName()); + plugin.getDatabaseManager().saveHome(player.getUniqueId(), name, loc); + player.sendMessage(plugin.getConfigManager().getMessage("home-set", "name", name)); + return true; + } + + public void teleportHome(Player player, String name) { + Home home = plugin.getDatabaseManager().getHome(player.getUniqueId(), name); + if (home == null) { + player.sendMessage(plugin.getConfigManager().getMessage("home-not-found", "name", name)); + return; + } + plugin.getTeleportManager().teleport(player, home.getLocation()); + } + + public boolean deleteHome(Player player, String name) { + boolean deleted = plugin.getDatabaseManager().deleteHome(player.getUniqueId(), name); + if (deleted) player.sendMessage(plugin.getConfigManager().getMessage("home-deleted", "name", name)); + else player.sendMessage(plugin.getConfigManager().getMessage("home-not-found", "name", name)); + return deleted; + } + + public List getHomes(UUID uuid) { + return plugin.getDatabaseManager().getHomes(uuid); + } +} diff --git a/src/main/java/de/teleportsuite/managers/PortalManager.java b/src/main/java/de/teleportsuite/managers/PortalManager.java new file mode 100644 index 0000000..69f89e0 --- /dev/null +++ b/src/main/java/de/teleportsuite/managers/PortalManager.java @@ -0,0 +1,81 @@ +package de.teleportsuite.managers; + +import de.teleportsuite.TeleportSuite; +import de.teleportsuite.models.Portal; +import de.teleportsuite.models.TeleportLocation; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import java.util.*; + +public class PortalManager { + private final TeleportSuite plugin; + private List portals = new ArrayList<>(); + // Selection for portal creation: player -> pos1, pos2 + private final Map pos1 = new HashMap<>(); + private final Map pos2 = new HashMap<>(); + // Cooldown to prevent multiple triggers + private final Map cooldown = new HashMap<>(); + + public PortalManager(TeleportSuite plugin) { + this.plugin = plugin; + loadPortals(); + } + + public void loadPortals() { + portals = plugin.getDatabaseManager().getAllPortals(); + } + + public void setPos1(Player player, Location loc) { + pos1.put(player.getUniqueId(), loc); + player.sendMessage("§aPos1 gesetzt: §f" + formatLoc(loc)); + } + + public void setPos2(Player player, Location loc) { + pos2.put(player.getUniqueId(), loc); + player.sendMessage("§aPos2 gesetzt: §f" + formatLoc(loc)); + } + + public boolean createPortal(Player player, String name, String targetServer, String targetWorld, double tx, double ty, double tz) { + Location l1 = pos1.get(player.getUniqueId()); + Location l2 = pos2.get(player.getUniqueId()); + if (l1 == null || l2 == null) { + player.sendMessage("§cBitte setze erst Pos1 und Pos2 mit §e/setportal pos1 §cund §e/setportal pos2"); + return false; + } + TeleportLocation dest = new TeleportLocation(targetWorld, tx, ty, tz, 0, 0, targetServer); + Portal portal = new Portal(name, l1.getWorld().getName(), + l1.getBlockX(), l1.getBlockY(), l1.getBlockZ(), + l2.getBlockX(), l2.getBlockY(), l2.getBlockZ(), + targetServer, dest); + plugin.getDatabaseManager().savePortal(portal); + portals.add(portal); + pos1.remove(player.getUniqueId()); + pos2.remove(player.getUniqueId()); + player.sendMessage(plugin.getConfigManager().getMessage("portal-created", "name", name)); + return true; + } + + public boolean deletePortal(String name) { + portals.removeIf(p -> p.getName().equalsIgnoreCase(name)); + return plugin.getDatabaseManager().deletePortal(name); + } + + public void checkPortal(Player player, Location loc) { + long now = System.currentTimeMillis(); + if (now - cooldown.getOrDefault(player.getUniqueId(), 0L) < 2000) return; + + for (Portal portal : portals) { + if (portal.contains(loc)) { + cooldown.put(player.getUniqueId(), now); + plugin.getTeleportManager().teleport(player, portal.getDestination(), true); + break; + } + } + } + + public List getPortals() { return portals; } + + private String formatLoc(Location loc) { + return loc.getBlockX() + ", " + loc.getBlockY() + ", " + loc.getBlockZ() + " in " + loc.getWorld().getName(); + } +} diff --git a/src/main/java/de/teleportsuite/managers/SavePointManager.java b/src/main/java/de/teleportsuite/managers/SavePointManager.java new file mode 100644 index 0000000..cda67db --- /dev/null +++ b/src/main/java/de/teleportsuite/managers/SavePointManager.java @@ -0,0 +1,22 @@ +package de.teleportsuite.managers; + +import de.teleportsuite.TeleportSuite; +import de.teleportsuite.models.TeleportLocation; +import org.bukkit.entity.Player; + +public class SavePointManager { + private final TeleportSuite plugin; + public SavePointManager(TeleportSuite plugin) { this.plugin = plugin; } + + public void setSavePoint(Player player, String name) { + TeleportLocation loc = new TeleportLocation(player.getLocation(), plugin.getConfigManager().getServerName()); + plugin.getDatabaseManager().saveSavePoint(player.getUniqueId(), name, loc); + player.sendMessage(plugin.getConfigManager().getMessage("savepoint-set", "name", name)); + } + + public void teleportToSavePoint(Player player, String name) { + TeleportLocation loc = plugin.getDatabaseManager().getSavePoint(player.getUniqueId(), name); + if (loc == null) { player.sendMessage("§cSavepoint §6" + name + " §cnicht gefunden."); return; } + plugin.getTeleportManager().teleport(player, loc); + } +} diff --git a/src/main/java/de/teleportsuite/managers/SpawnManager.java b/src/main/java/de/teleportsuite/managers/SpawnManager.java new file mode 100644 index 0000000..fbede7a --- /dev/null +++ b/src/main/java/de/teleportsuite/managers/SpawnManager.java @@ -0,0 +1,41 @@ +package de.teleportsuite.managers; + +import de.teleportsuite.TeleportSuite; +import de.teleportsuite.models.TeleportLocation; +import org.bukkit.entity.Player; + +public class SpawnManager { + private final TeleportSuite plugin; + public SpawnManager(TeleportSuite plugin) { this.plugin = plugin; } + + public void setSpawn(Player player) { + TeleportLocation loc = new TeleportLocation(player.getLocation(), plugin.getConfigManager().getServerName()); + plugin.getDatabaseManager().saveSpawn("spawn", loc); + player.sendMessage(plugin.getConfigManager().getMessage("spawn-set")); + } + + public void setFirstSpawn(Player player) { + TeleportLocation loc = new TeleportLocation(player.getLocation(), plugin.getConfigManager().getServerName()); + plugin.getDatabaseManager().saveSpawn("firstspawn", loc); + player.sendMessage(plugin.getConfigManager().getMessage("firstspawn-set")); + } + + public void teleportToSpawn(Player player) { + TeleportLocation spawn = plugin.getDatabaseManager().getSpawn("spawn"); + if (spawn == null) { player.sendMessage("§cKein Spawn gesetzt!"); return; } + plugin.getTeleportManager().teleport(player, spawn); + } + + public void teleportToFirstSpawn(Player player) { + TeleportLocation firstSpawn = plugin.getDatabaseManager().getSpawn("firstspawn"); + if (firstSpawn == null) { + teleportToSpawn(player); + return; + } + plugin.getTeleportManager().teleport(player, firstSpawn); + } + + public TeleportLocation getRespawnLocation() { + return plugin.getDatabaseManager().getSpawn("spawn"); + } +} diff --git a/src/main/java/de/teleportsuite/managers/TeleportManager.java b/src/main/java/de/teleportsuite/managers/TeleportManager.java new file mode 100644 index 0000000..e1d0d00 --- /dev/null +++ b/src/main/java/de/teleportsuite/managers/TeleportManager.java @@ -0,0 +1,220 @@ +package de.teleportsuite.managers; + +import de.teleportsuite.TeleportSuite; +import de.teleportsuite.bungeemessaging.BungeeMessenger; +import de.teleportsuite.models.TeleportLocation; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import java.util.*; + +public class TeleportManager { + private final TeleportSuite plugin; + // Same-server pending TPA: requesterUUID → targetUUID + private final Map tpaRequests = new HashMap<>(); + private final Map requestTimestamps = new HashMap<>(); + // Cooldowns + private final Map cooldowns = new HashMap<>(); + // Warmup tasks + private final Map warmupTasks = new HashMap<>(); + // Back-locations (saved before every teleport) + private final Map pendingBackLocations = new HashMap<>(); + + public TeleportManager(TeleportSuite plugin) { this.plugin = plugin; } + + // ------------------------------------------------------------------------- + // Main teleport entry point + // ------------------------------------------------------------------------- + + public void teleport(Player player, TeleportLocation destination) { + teleport(player, destination, true); + } + + public void teleport(Player player, TeleportLocation destination, boolean saveBack) { + int delay = plugin.getConfigManager().getTeleportDelay(); + if (player.hasPermission("teleportsuite.nodelay")) delay = 0; + + if (!player.hasPermission("teleportsuite.nocooldown")) { + long cooldownMs = plugin.getConfigManager().getTeleportCooldown() * 1000L; + long lastTp = cooldowns.getOrDefault(player.getUniqueId(), 0L); + if (System.currentTimeMillis() - lastTp < cooldownMs) { + long remaining = (cooldownMs - (System.currentTimeMillis() - lastTp)) / 1000; + player.sendMessage(plugin.getConfigManager().getMessage( + "teleport-cooldown", "seconds", String.valueOf(remaining))); + return; + } + } + + if (saveBack) { + pendingBackLocations.put(player.getUniqueId(), + new TeleportLocation(player.getLocation(), plugin.getConfigManager().getServerName())); + } + + if (delay <= 0) { + executeTeleport(player, destination); + return; + } + + player.sendMessage(plugin.getConfigManager().getMessage( + "teleport-warmup", "seconds", String.valueOf(delay))); + Location startLoc = player.getLocation().clone(); + + int taskId = Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> { + if (plugin.getConfigManager().cancelOnMove()) { + Location now = player.getLocation(); + if (Math.abs(now.getX() - startLoc.getX()) > 0.5 + || Math.abs(now.getZ() - startLoc.getZ()) > 0.5) { + player.sendMessage(plugin.getConfigManager().getMessage("teleport-cancelled")); + warmupTasks.remove(player.getUniqueId()); + return; + } + } + executeTeleport(player, destination); + warmupTasks.remove(player.getUniqueId()); + }, delay * 20L); + + warmupTasks.put(player.getUniqueId(), taskId); + } + + private void executeTeleport(Player player, TeleportLocation dest) { + String localServer = plugin.getConfigManager().getServerName(); + + // Save last location + apply cooldown always (also for cross-server) + plugin.getDatabaseManager().saveLastLocation(player.getUniqueId(), + new TeleportLocation(player.getLocation(), localServer)); + cooldowns.put(player.getUniqueId(), System.currentTimeMillis()); + + if (!dest.isLocalServer(localServer)) { + // Cross-server: delegate completely to the Bungee plugin. + // It will call sender.connect() and then send TP_PLAYERTOPOSITION. + BungeeMessenger bm = plugin.getBungeeMessenger(); + if (bm != null) { + bm.teleportToPosition(player, dest.getServer(), dest.getWorld(), + dest.getX(), dest.getY(), dest.getZ(), dest.getYaw(), dest.getPitch()); + player.sendMessage(plugin.getConfigManager().getMessage("teleport-success")); + } + return; + } + + Location loc = dest.toBukkitLocation(); + if (loc == null || loc.getWorld() == null) { + player.sendMessage("§cZielwelt nicht gefunden!"); + return; + } + + player.teleport(loc); + player.sendMessage(plugin.getConfigManager().getMessage("teleport-success")); + } + + public void cancelWarmup(UUID uuid) { + Integer taskId = warmupTasks.remove(uuid); + if (taskId != null) Bukkit.getScheduler().cancelTask(taskId); + } + + public TeleportLocation getBackLocation(UUID uuid) { + return pendingBackLocations.get(uuid); + } + + // ------------------------------------------------------------------------- + // Same-server TPA + // ------------------------------------------------------------------------- + + public void sendTpaRequest(Player from, Player to) { + tpaRequests.put(from.getUniqueId(), to.getUniqueId()); + requestTimestamps.put(from.getUniqueId(), System.currentTimeMillis()); + + to.sendMessage(plugin.getConfigManager().getMessage("tpa-received", "player", from.getName())); + from.sendMessage(plugin.getConfigManager().getMessage("tpa-sent", "player", to.getName())); + + int timeout = plugin.getConfigManager().getRequestTimeout(); + Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> { + if (tpaRequests.containsKey(from.getUniqueId())) { + tpaRequests.remove(from.getUniqueId()); + requestTimestamps.remove(from.getUniqueId()); + if (from.isOnline()) { + from.sendMessage(plugin.getConfigManager().getMessage("tpa-expired")); + } + } + }, timeout * 20L); + } + + // ------------------------------------------------------------------------- + // Cross-server TPA — all state lives on the Bungee plugin now; + // Spigot just sends the right payload and handles local accept/deny. + // ------------------------------------------------------------------------- + + /** + * Called by TpaCommand when the target player is NOT on this server. + * Bungee will manage the pending state and route all messages. + */ + public void sendCrossServerTpaRequest(Player from, String targetName) { + BungeeMessenger bm = plugin.getBungeeMessenger(); + if (bm == null) { + from.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", targetName)); + return; + } + bm.sendTpaRequest( + from, targetName, "TPTO", + plugin.getConfigManager().getMessage("tpa-received", "player", from.getName()), + plugin.getConfigManager().getMessage("tpa-sent", "player", targetName)); + } + + /** Called by TpAcceptCommand — handles both same-server and cross-server. */ + public boolean acceptTpa(Player target) { + // Same-server accept + UUID requesterUUID = null; + for (Map.Entry entry : tpaRequests.entrySet()) { + if (entry.getValue().equals(target.getUniqueId())) { + requesterUUID = entry.getKey(); + break; + } + } + if (requesterUUID != null) { + Player from = Bukkit.getPlayer(requesterUUID); + tpaRequests.remove(requesterUUID); + requestTimestamps.remove(requesterUUID); + 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; + } + + // Cross-server accept — tell Bungee, it has the pending state + BungeeMessenger bm = plugin.getBungeeMessenger(); + if (bm == null) return false; + bm.sendTpaAccept(target, null, + plugin.getConfigManager().getMessage("tpa-request-not-found")); + return true; + } + + /** Called by TpDenyCommand — handles both same-server and cross-server. */ + public boolean denyTpa(Player target) { + // Same-server deny + UUID requesterUUID = null; + for (Map.Entry entry : tpaRequests.entrySet()) { + if (entry.getValue().equals(target.getUniqueId())) { + requesterUUID = entry.getKey(); + break; + } + } + if (requesterUUID != null) { + Player from = Bukkit.getPlayer(requesterUUID); + tpaRequests.remove(requesterUUID); + requestTimestamps.remove(requesterUUID); + if (from != null && from.isOnline()) { + from.sendMessage(plugin.getConfigManager().getMessage("tpa-denied", "player", target.getName())); + } + return true; + } + + // Cross-server deny + BungeeMessenger bm = plugin.getBungeeMessenger(); + if (bm == null) return false; + bm.sendTpaDeny(target, null, + plugin.getConfigManager().getMessage("tpa-denied", "player", target.getName())); + return true; + } +} diff --git a/src/main/java/de/teleportsuite/managers/WarpManager.java b/src/main/java/de/teleportsuite/managers/WarpManager.java new file mode 100644 index 0000000..543ec77 --- /dev/null +++ b/src/main/java/de/teleportsuite/managers/WarpManager.java @@ -0,0 +1,39 @@ +package de.teleportsuite.managers; + +import de.teleportsuite.TeleportSuite; +import de.teleportsuite.models.TeleportLocation; +import de.teleportsuite.models.Warp; +import org.bukkit.entity.Player; +import java.util.List; + +public class WarpManager { + private final TeleportSuite plugin; + public WarpManager(TeleportSuite plugin) { this.plugin = plugin; } + + public void setWarp(Player player, String name, String permission) { + TeleportLocation loc = new TeleportLocation(player.getLocation(), plugin.getConfigManager().getServerName()); + plugin.getDatabaseManager().saveWarp(name, loc, player.getUniqueId(), permission); + player.sendMessage(plugin.getConfigManager().getMessage("warp-set", "name", name)); + } + + public void teleportWarp(Player player, String name) { + Warp warp = plugin.getDatabaseManager().getWarp(name); + if (warp == null) { + player.sendMessage(plugin.getConfigManager().getMessage("warp-not-found", "name", name)); + return; + } + if (warp.getPermission() != null && !player.hasPermission(warp.getPermission())) { + player.sendMessage(plugin.getConfigManager().getMessage("no-permission")); + return; + } + plugin.getTeleportManager().teleport(player, warp.getLocation()); + } + + public boolean deleteWarp(String name) { + return plugin.getDatabaseManager().deleteWarp(name); + } + + public List getAllWarps() { + return plugin.getDatabaseManager().getAllWarps(); + } +} diff --git a/src/main/java/de/teleportsuite/models/Home.java b/src/main/java/de/teleportsuite/models/Home.java new file mode 100644 index 0000000..5cf8641 --- /dev/null +++ b/src/main/java/de/teleportsuite/models/Home.java @@ -0,0 +1,17 @@ +package de.teleportsuite.models; + +import java.util.UUID; + +public class Home { + private final UUID uuid; + private final String name; + private final TeleportLocation location; + + public Home(UUID uuid, String name, TeleportLocation location) { + this.uuid = uuid; this.name = name; this.location = location; + } + + public UUID getUuid() { return uuid; } + public String getName() { return name; } + public TeleportLocation getLocation() { return location; } +} diff --git a/src/main/java/de/teleportsuite/models/Portal.java b/src/main/java/de/teleportsuite/models/Portal.java new file mode 100644 index 0000000..a0e723d --- /dev/null +++ b/src/main/java/de/teleportsuite/models/Portal.java @@ -0,0 +1,33 @@ +package de.teleportsuite.models; + +import org.bukkit.Location; +import org.bukkit.World; + +public class Portal { + private final String name; + private final String world; + private final int x1, y1, z1, x2, y2, z2; + private final String targetServer; + private final TeleportLocation destination; + + public Portal(String name, String world, int x1, int y1, int z1, int x2, int y2, int z2, + String targetServer, TeleportLocation destination) { + this.name = name; this.world = world; + this.x1 = x1; this.y1 = y1; this.z1 = z1; + this.x2 = x2; this.y2 = y2; this.z2 = z2; + this.targetServer = targetServer; this.destination = destination; + } + + public boolean contains(Location loc) { + if (!loc.getWorld().getName().equals(world)) return false; + int bx = loc.getBlockX(), by = loc.getBlockY(), bz = loc.getBlockZ(); + return bx >= Math.min(x1,x2) && bx <= Math.max(x1,x2) && + by >= Math.min(y1,y2) && by <= Math.max(y1,y2) && + bz >= Math.min(z1,z2) && bz <= Math.max(z1,z2); + } + + public String getName() { return name; } + public String getWorld() { return world; } + public String getTargetServer() { return targetServer; } + public TeleportLocation getDestination() { return destination; } +} diff --git a/src/main/java/de/teleportsuite/models/TeleportLocation.java b/src/main/java/de/teleportsuite/models/TeleportLocation.java new file mode 100644 index 0000000..17740d7 --- /dev/null +++ b/src/main/java/de/teleportsuite/models/TeleportLocation.java @@ -0,0 +1,38 @@ +package de.teleportsuite.models; + +import org.bukkit.Bukkit; +import org.bukkit.Location; + +public class TeleportLocation { + private String world; + private double x, y, z; + private float yaw, pitch; + private String server; + + public TeleportLocation(String world, double x, double y, double z, float yaw, float pitch, String server) { + this.world = world; this.x = x; this.y = y; this.z = z; + this.yaw = yaw; this.pitch = pitch; this.server = server; + } + + public TeleportLocation(Location loc, String server) { + this.world = loc.getWorld().getName(); + this.x = loc.getX(); this.y = loc.getY(); this.z = loc.getZ(); + this.yaw = loc.getYaw(); this.pitch = loc.getPitch(); + this.server = server; + } + + public Location toBukkitLocation() { + return new Location(Bukkit.getWorld(world), x, y, z, yaw, pitch); + } + + public String getWorld() { return world; } + public double getX() { return x; } + public double getY() { return y; } + public double getZ() { return z; } + public float getYaw() { return yaw; } + public float getPitch() { return pitch; } + public String getServer() { return server; } + public boolean isLocalServer(String localServer) { + return server == null || server.equals("local") || server.equals(localServer); + } +} diff --git a/src/main/java/de/teleportsuite/models/Warp.java b/src/main/java/de/teleportsuite/models/Warp.java new file mode 100644 index 0000000..5597b84 --- /dev/null +++ b/src/main/java/de/teleportsuite/models/Warp.java @@ -0,0 +1,20 @@ +package de.teleportsuite.models; + +import java.util.UUID; + +public class Warp { + private final String name; + private final TeleportLocation location; + private final UUID creator; + private final String permission; + + public Warp(String name, TeleportLocation location, UUID creator, String permission) { + this.name = name; this.location = location; + this.creator = creator; this.permission = permission; + } + + public String getName() { return name; } + public TeleportLocation getLocation() { return location; } + public UUID getCreator() { return creator; } + public String getPermission() { return permission; } +} diff --git a/src/main/resources/bungee.yml b/src/main/resources/bungee.yml new file mode 100644 index 0000000..109a8ce --- /dev/null +++ b/src/main/resources/bungee.yml @@ -0,0 +1,5 @@ +name: TeleportSuite-Bungee +main: de.teleportsuite.bungee.TeleportSuiteBungee +version: 1.0 +author: TeleportSuite +description: BungeeCord-Companion fuer TeleportSuite diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..b5fb908 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,77 @@ +# TeleportSuite Configuration +version: "1.0.0" + +# Datenbank-Einstellungen +database: + type: sqlite # sqlite oder mysql + mysql: + host: localhost + port: 3306 + database: teleportsuite + username: root + password: password + pool-size: 10 + sqlite: + file: teleportsuite.db + +# BungeeCord Einstellungen +bungee: + enabled: false + server-name: "survival" # Name dieses Servers im BungeeCord-Netz + +# Teleport Einstellungen +teleport: + delay: 3 # Sekunden Wartezeit vor Teleport (0 = sofort) + cooldown: 5 # Cooldown in Sekunden + warmup-cancel-on-move: true # Teleport abbrechnen wenn Spieler sich bewegt + request-timeout: 60 # Sekunden bis eine TP-Anfrage verfällt + +# Home Einstellungen +homes: + max-homes-default: 3 # Standard maximale Homes + max-homes-vip: 10 + max-homes-premium: 25 + +# Warp Einstellungen +warps: + allow-player-warps: false # Darf jeder Spieler Warps erstellen? + warp-permission-prefix: "teleportsuite.warp." # z.B. teleportsuite.warp.spawn + +# Portal Einstellungen +portals: + check-interval: 5 # Ticks zwischen Portal-Checks + particle-effect: true + +# Spawn Einstellungen +spawn: + first-join-teleport: true # Neuer Spieler → FirstSpawn + death-respawn-to-spawn: false # Tod → Spawn statt Bett + +# Nachrichten (unterstützt &-Farbcodes) +messages: + prefix: "&8[&6TP&8] &r" + teleport-success: "&aDu wurdest teleportiert!" + teleport-warmup: "&eTeleportiere in &6{seconds}&e Sekunden... Nicht bewegen!" + teleport-cancelled: "&cTeleport abgebrochen - du hast dich bewegt!" + teleport-cooldown: "&cBitte warte noch &6{seconds}&c Sekunden." + tpa-sent: "&aTeleportanfrage an &6{player}&a gesendet." + tpa-received: "&6{player} &emöchte zu dir teleportieren. &a/tpaccept &eoder &c/tpdeny" + tpa-accepted: "&a{player} hat deine Anfrage akzeptiert!" + tpa-denied: "&c{player} hat deine Anfrage abgelehnt." + tpa-expired: "&cDeine Teleportanfrage ist abgelaufen." + home-set: "&aHome &6{name}&a gesetzt!" + home-deleted: "&cHome &6{name}&c gelöscht." + home-not-found: "&cHome &6{name}&c nicht gefunden." + home-limit: "&cDu hast das Maximum von &6{max}&c Homes erreicht." + warp-set: "&aWarp &6{name}&a erstellt!" + warp-deleted: "&cWarp &6{name}&c gelöscht." + warp-not-found: "&cWarp &6{name}&c nicht gefunden." + back-no-location: "&cKein vorheriger Ort gespeichert." + death-no-location: "&cKein Todesort gespeichert." + no-permission: "&cDazu hast du keine Berechtigung." + player-not-found: "&cSpieler &6{player}&c nicht gefunden." + portal-created: "&aPortal &6{name}&a erstellt!" + portal-deleted: "&cPortal &6{name}&c gelöscht." + savepoint-set: "&aSavepoint &6{name}&a gesetzt!" + spawn-set: "&aSpawn gesetzt!" + firstspawn-set: "&aFirstSpawn gesetzt!" diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..ab449cb --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,148 @@ +name: TeleportSuite +version: 1.0.2 +main: de.teleportsuite.TeleportSuite +api-version: 1.20 +description: BungeeCord-fähiges Teleport-Komplettpaket +author: M_Viper +softdepend: [] + +commands: + tp: + description: Teleportiere zu einem Spieler + usage: /tp + permission: teleportsuite.tp + tphere: + description: Teleportiere einen Spieler zu dir + usage: /tphere + permission: teleportsuite.tphere + tpa: + description: Sende eine Teleportanfrage + usage: /tpa + permission: teleportsuite.tpa + tpaccept: + description: Akzeptiere eine Teleportanfrage + usage: /tpaccept + permission: teleportsuite.tpaccept + tpdeny: + description: Lehne eine Teleportanfrage ab + usage: /tpdeny + permission: teleportsuite.tpdeny + back: + description: Teleportiere zur letzten Position + usage: /back + permission: teleportsuite.back + deathback: + description: Teleportiere zum letzten Todesort + usage: /deathback + permission: teleportsuite.deathback + sethome: + description: Setze ein Home + usage: /sethome [name] + permission: teleportsuite.sethome + home: + description: Teleportiere zu einem Home + usage: /home [name] + permission: teleportsuite.home + delhome: + description: Lösche ein Home + usage: /delhome + permission: teleportsuite.delhome + homes: + description: Liste alle Homes auf + usage: /homes + permission: teleportsuite.home + setwarp: + description: Setze einen Warp + usage: /setwarp + permission: teleportsuite.setwarp + warp: + description: Teleportiere zu einem Warp + usage: /warp + permission: teleportsuite.warp + delwarp: + description: Lösche einen Warp + usage: /delwarp + permission: teleportsuite.delwarp + warps: + description: Liste alle Warps auf + usage: /warps + permission: teleportsuite.warp + setportal: + description: Erstelle ein Portal (2 Positionen markieren) + usage: /setportal + permission: teleportsuite.setportal + delportal: + description: Lösche ein Portal + usage: /delportal + permission: teleportsuite.delportal + portals: + description: Liste alle Portale auf + usage: /portals + permission: teleportsuite.portals + setsavepoint: + description: Setze einen Savepoint + usage: /setsavepoint [name] + permission: teleportsuite.savepoint + savepoint: + description: Teleportiere zu einem Savepoint + usage: /savepoint [name] + permission: teleportsuite.savepoint + setspawn: + description: Setze den Spawn + usage: /setspawn + permission: teleportsuite.setspawn + spawn: + description: Teleportiere zum Spawn + usage: /spawn + permission: teleportsuite.spawn + setfirstspawn: + description: Setze den FirstSpawn + usage: /setfirstspawn + permission: teleportsuite.setfirstspawn + tppos: + description: Teleportiere zu Koordinaten + usage: /tppos [world] + permission: teleportsuite.tppos + tpall: + description: Teleportiere alle Spieler zu dir + usage: /tpall + permission: teleportsuite.tpall + tpworld: + description: Teleportiere in eine Welt + usage: /tpworld + permission: teleportsuite.tpworld + entitytransport: + description: Transportiere ein Entity + usage: /entitytransport + permission: teleportsuite.entitytransport + +permissions: + teleportsuite.*: + description: Alle TeleportSuite-Rechte + children: + teleportsuite.tp: true + teleportsuite.tphere: true + teleportsuite.tpa: true + teleportsuite.tpaccept: true + teleportsuite.tpdeny: true + teleportsuite.back: true + teleportsuite.deathback: true + teleportsuite.sethome: true + teleportsuite.home: true + teleportsuite.setwarp: true + teleportsuite.warp: true + teleportsuite.setportal: true + teleportsuite.delportal: true + teleportsuite.portals: true + teleportsuite.savepoint: true + teleportsuite.setspawn: true + teleportsuite.spawn: true + teleportsuite.setfirstspawn: true + teleportsuite.tppos: true + teleportsuite.tpall: true + teleportsuite.tpworld: true + teleportsuite.entitytransport: true + teleportsuite.admin: true + teleportsuite.home.unlimited: + description: Unbegrenzte Homes + default: op