64 Commits
1.0.0 ... main

Author SHA1 Message Date
Git Manager GUI
e29e91fc51 Upload folder via GUI - src 2026-06-01 21:19:04 +02:00
Git Manager GUI
54bf949763 Upload via Git Manager GUI 2026-06-01 21:18:57 +02:00
Git Manager GUI
99a326ce2e Upload folder via GUI - src 2026-05-31 12:52:00 +02:00
Git Manager GUI
3cce4477cb Upload via Git Manager GUI 2026-05-31 12:51:53 +02:00
Git Manager GUI
9e74ab28fb Upload folder via GUI - src 2026-05-24 01:16:54 +02:00
Git Manager GUI
3b7a1556d1 Upload via Git Manager GUI 2026-05-24 01:16:49 +02:00
Git Manager GUI
f5b8717ad4 Upload folder via GUI - src 2026-05-23 20:08:41 +02:00
Git Manager GUI
23e77d7204 Upload folder via GUI - lib 2026-05-23 20:08:39 +02:00
Git Manager GUI
27b5ca3c13 Upload via Git Manager GUI 2026-05-23 20:08:36 +02:00
37a82ebd47 Delete src/main/resources/plugin.yml via Git Manager GUI 2026-05-23 18:08:14 +00:00
c52ef617e9 Delete src/main/java/de/teleportsuite/models/Warp.java via Git Manager GUI 2026-05-23 18:08:14 +00:00
b7ec18cf21 Delete src/main/java/de/teleportsuite/models/Portal.java via Git Manager GUI 2026-05-23 18:08:13 +00:00
15f09af83a Delete src/main/java/de/teleportsuite/managers/TeleportManager.java via Git Manager GUI 2026-05-23 18:08:13 +00:00
326ddaa9af Delete src/main/java/de/teleportsuite/managers/SpawnManager.java via Git Manager GUI 2026-05-23 18:08:12 +00:00
ee1d705447 Delete src/main/java/de/teleportsuite/managers/HomeManager.java via Git Manager GUI 2026-05-23 18:08:12 +00:00
87fd77666a Delete src/main/java/de/teleportsuite/listeners/PortalListener.java via Git Manager GUI 2026-05-23 18:08:12 +00:00
c1154f93ae Delete src/main/java/de/teleportsuite/listeners/PlayerMoveListener.java via Git Manager GUI 2026-05-23 18:08:11 +00:00
342fa394ca Delete src/main/java/de/teleportsuite/listeners/PlayerDeathListener.java via Git Manager GUI 2026-05-23 18:08:11 +00:00
b73a5950c7 Delete src/main/java/de/teleportsuite/commands/WarpsCommand.java via Git Manager GUI 2026-05-23 18:08:10 +00:00
918ad4e6bf Delete src/main/java/de/teleportsuite/commands/TpaCommand.java via Git Manager GUI 2026-05-23 18:08:10 +00:00
0e5bc86455 Delete src/main/java/de/teleportsuite/commands/TpPosCommand.java via Git Manager GUI 2026-05-23 18:08:10 +00:00
57d0ad1a6c Delete src/main/java/de/teleportsuite/commands/TpDenyCommand.java via Git Manager GUI 2026-05-23 18:08:09 +00:00
d2ce86a5d5 Delete src/main/java/de/teleportsuite/commands/TpAllCommand.java via Git Manager GUI 2026-05-23 18:08:09 +00:00
7bb29b396b Delete src/main/java/de/teleportsuite/commands/SpawnCommand.java via Git Manager GUI 2026-05-23 18:08:08 +00:00
dd1bb37886 Delete src/main/java/de/teleportsuite/commands/SetSpawnCommand.java via Git Manager GUI 2026-05-23 18:08:08 +00:00
32df7f142a Delete src/main/java/de/teleportsuite/commands/SetPortalCommand.java via Git Manager GUI 2026-05-23 18:08:08 +00:00
e48e687520 Delete src/main/java/de/teleportsuite/commands/SetFirstSpawnCommand.java via Git Manager GUI 2026-05-23 18:08:07 +00:00
1d7c9e854b Delete src/main/java/de/teleportsuite/commands/PortalsCommand.java via Git Manager GUI 2026-05-23 18:08:07 +00:00
093d31725a Delete src/main/java/de/teleportsuite/commands/HomeCommand.java via Git Manager GUI 2026-05-23 18:08:06 +00:00
8edc4885db Delete src/main/java/de/teleportsuite/commands/DelWarpCommand.java via Git Manager GUI 2026-05-23 18:08:06 +00:00
4edbcc2386 Delete src/main/java/de/teleportsuite/commands/DeathBackCommand.java via Git Manager GUI 2026-05-23 18:08:06 +00:00
51db120a1e Delete src/main/java/de/teleportsuite/commands/BackCommand.java via Git Manager GUI 2026-05-23 18:08:05 +00:00
95763ea9a6 Delete src/main/java/de/teleportsuite/TeleportSuite.java via Git Manager GUI 2026-05-23 18:08:05 +00:00
2dc8511844 Delete src/main/resources/config.yml via Git Manager GUI 2026-05-23 18:08:03 +00:00
048c3eda30 Delete src/main/java/de/teleportsuite/models/TeleportLocation.java via Git Manager GUI 2026-05-23 18:08:02 +00:00
2394ebe031 Delete src/main/java/de/teleportsuite/models/Home.java via Git Manager GUI 2026-05-23 18:08:02 +00:00
7808ecef97 Delete src/main/java/de/teleportsuite/managers/WarpManager.java via Git Manager GUI 2026-05-23 18:08:01 +00:00
a4e884420b Delete src/main/java/de/teleportsuite/managers/SavePointManager.java via Git Manager GUI 2026-05-23 18:08:01 +00:00
4402439ad4 Delete src/main/java/de/teleportsuite/managers/PortalManager.java via Git Manager GUI 2026-05-23 18:08:01 +00:00
11a49e9c58 Delete src/main/java/de/teleportsuite/listeners/PlayerRespawnListener.java via Git Manager GUI 2026-05-23 18:08:00 +00:00
5afde181d8 Delete src/main/java/de/teleportsuite/listeners/PlayerJoinListener.java via Git Manager GUI 2026-05-23 18:08:00 +00:00
6f50a03c70 Delete src/main/java/de/teleportsuite/database/DatabaseManager.java via Git Manager GUI 2026-05-23 18:07:59 +00:00
e690f23c4e Delete src/main/java/de/teleportsuite/commands/WarpCommand.java via Git Manager GUI 2026-05-23 18:07:59 +00:00
bf231a39ad Delete src/main/java/de/teleportsuite/commands/TpWorldCommand.java via Git Manager GUI 2026-05-23 18:07:59 +00:00
3864f40bac Delete src/main/java/de/teleportsuite/commands/TpHereCommand.java via Git Manager GUI 2026-05-23 18:07:58 +00:00
d169f008f5 Delete src/main/java/de/teleportsuite/commands/TpCommand.java via Git Manager GUI 2026-05-23 18:07:58 +00:00
06414edeb4 Delete src/main/java/de/teleportsuite/commands/TpAcceptCommand.java via Git Manager GUI 2026-05-23 18:07:57 +00:00
76f143d44e Delete src/main/java/de/teleportsuite/commands/SetWarpCommand.java via Git Manager GUI 2026-05-23 18:07:57 +00:00
deea73b19d Delete src/main/java/de/teleportsuite/commands/SetSavePointCommand.java via Git Manager GUI 2026-05-23 18:07:57 +00:00
7c9e599150 Delete src/main/java/de/teleportsuite/commands/SetHomeCommand.java via Git Manager GUI 2026-05-23 18:07:56 +00:00
c951969812 Delete src/main/java/de/teleportsuite/commands/SavePointCommand.java via Git Manager GUI 2026-05-23 18:07:56 +00:00
b680355fe9 Delete src/main/java/de/teleportsuite/commands/HomesCommand.java via Git Manager GUI 2026-05-23 18:07:55 +00:00
46c8a6b3e1 Delete src/main/java/de/teleportsuite/commands/EntityTransportCommand.java via Git Manager GUI 2026-05-23 18:07:55 +00:00
b9f1e224e3 Delete src/main/java/de/teleportsuite/commands/DelPortalCommand.java via Git Manager GUI 2026-05-23 18:07:55 +00:00
2584abbd95 Delete src/main/java/de/teleportsuite/commands/DelHomeCommand.java via Git Manager GUI 2026-05-23 18:07:54 +00:00
f7979b5359 Delete src/main/java/de/teleportsuite/bungeemessaging/BungeeMessenger.java via Git Manager GUI 2026-05-23 18:07:54 +00:00
d272e709e1 Delete src/main/java/de/teleportsuite/ConfigManager.java via Git Manager GUI 2026-05-23 18:07:53 +00:00
2609f783b2 Delete pom.xml via Git Manager GUI 2026-05-23 18:07:49 +00:00
a534e6445b Delete lib/BungeeCord.jar via Git Manager GUI 2026-05-23 18:07:45 +00:00
b7dd32493b README.md aktualisiert 2026-05-23 18:06:21 +00:00
Git Manager GUI
71534e7e06 Upload folder via GUI - src 2026-05-23 15:56:58 +02:00
Git Manager GUI
d703187fae Upload via Git Manager GUI 2026-05-23 15:56:53 +02:00
Git Manager GUI
90cca7c559 Upload folder via GUI - src 2026-05-22 22:11:35 +02:00
7c8bf27628 README.md aktualisiert 2026-05-22 19:07:12 +00:00
21 changed files with 1298 additions and 278 deletions

275
README.md
View File

@@ -2,41 +2,127 @@
TeleportSuite ist ein umfassendes Teleport-Plugin für Paper/Spigot mit Homes, Warps, Portalen, Spawn-System, Savepoints, Back/Deathback, Teleportanfragen und BungeeCord-Unterstützung. TeleportSuite ist ein umfassendes Teleport-Plugin für Paper/Spigot mit Homes, Warps, Portalen, Spawn-System, Savepoints, Back/Deathback, Teleportanfragen und BungeeCord-Unterstützung.
---
## Features ## Features
- Homes mit konfigurierbaren Limits - Homes mit konfigurierbaren Limits pro Rang
- Warps mit optionalen Berechtigungen - Warps mit optionalen Berechtigungen
- Portale für Server- oder Weltwechsel - Portale für Server- oder Weltwechsel
- Spawn, FirstSpawn und Deathback - Spawn, FirstSpawn und Deathback
- Teleportanfragen mit Accept/Deny - Teleportanfragen (TPA) mit Accept/Deny
- Back- und Cooldown-/Warmup-System - Back- und Cooldown-/Warmup-System
- Teleport zu Koordinaten, Welten und Spielern - Teleport zu Koordinaten, Welten und Spielern
- Entity-Transport - Entity-Transport
- SQLite- und MySQL-Unterstützung - SQLite- und MySQL-Unterstützung
- BungeeCord-Unterstützung für verteilte Servernetzwerke - BungeeCord-Unterstützung für verteilte Servernetzwerke
---
## Voraussetzungen ## Voraussetzungen
- Paper oder Spigot ab Minecraft 1.20 - Paper oder Spigot ab Minecraft 1.20
- Java 21 oder neuer - Java 21 oder neuer
- Optional: MySQL, falls du nicht SQLite verwenden möchtest - Optional: MySQL, falls mehrere Server auf dieselbe Datenbank zugreifen sollen
- Optional: BungeeCord-Proxy, falls du mehrere Server vernetzt hast
---
## Installation ## Installation
1. Die fertige JAR-Datei in den Ordner plugins deines Servers kopieren. ### Einzelner Server (kein BungeeCord)
2. Den Server einmal starten, damit die Konfigurationsdatei und die Datenbank angelegt werden.
3. Den Server stoppen und die Datei config.yml nach Bedarf anpassen.
4. Den Server neu starten.
## Konfiguration 1. Die JAR-Datei in den `plugins`-Ordner deines Servers kopieren.
2. Server starten → Plugin legt `config.yml` und Datenbank automatisch an.
3. Server stoppen und `config.yml` nach Bedarf anpassen.
4. Server neu starten.
Die Datei config.yml wird beim ersten Start im Plugin-Ordner angelegt. ---
### Datenbank ### BungeeCord-Netzwerk ⚠️
> Wenn du mehrere Server mit BungeeCord verbunden hast und Spieler **serverübergreifend** teleportieren sollen, musst du das Plugin auf **jedem Spigot-Server UND auf dem BungeeCord-Proxy** installieren.
**Schritt-für-Schritt:**
**1. JAR auf jeden Spigot-Server kopieren**
Kopiere die `TeleportSuite.jar` in den `plugins`-Ordner von **jedem einzelnen** Spigot-Server in deinem Netzwerk (z.B. Lobby, Survival, Creative, ...).
**2. JAR auf den BungeeCord-Proxy kopieren**
Kopiere dieselbe `TeleportSuite.jar` auch in den `plugins`-Ordner deines **BungeeCord-Proxys**. Das Plugin erkennt automatisch ob es auf Spigot oder BungeeCord läuft.
**3. Jeden Server einmal starten**
Starte jeden Server einmal kurz, damit die `config.yml` angelegt wird. Dann stoppe ihn wieder.
**4. config.yml auf jedem Spigot-Server anpassen**
Öffne die `plugins/TeleportSuite/config.yml` auf **jedem Spigot-Server** und passe folgende Einstellungen an:
```yaml
bungee:
enabled: true # ← MUSS auf true stehen, sonst kein Cross-Server-Teleport!
server-name: "survival" # ← Den Namen dieses Servers eintragen (siehe unten)
```
> **Woher bekomme ich den `server-name`?**
> Öffne die `config.yml` deines **BungeeCord-Proxys** (nicht des Spigot-Servers!).
> Dort findest du einen Abschnitt wie diesen:
> ```yaml
> servers:
> lobby:
> address: localhost:25566
> survival:
> address: localhost:25567
> creative:
> address: localhost:25568
> ```
> Der `server-name` in der TeleportSuite-Config muss **exakt** dem Namen aus dieser Liste entsprechen.
> Beispiel: Auf dem Survival-Server trägst du `server-name: "survival"` ein.
> Auf dem Lobby-Server trägst du `server-name: "lobby"` ein. Und so weiter.
**5. Datenbank einrichten (empfohlen: MySQL)**
Damit alle Server auf dieselben Homes, Warps usw. zugreifen, solltest du MySQL verwenden und auf jedem Spigot-Server dieselben Zugangsdaten eintragen:
```yaml ```yaml
database: database:
type: sqlite type: mysql
mysql:
host: localhost # Adresse deines MySQL-Servers
port: 3306
database: teleportsuite
username: root
password: deinPasswort
```
**6. Alle Server neu starten**
Starte jetzt alle Spigot-Server und den BungeeCord-Proxy neu. Wenn alles korrekt konfiguriert ist, siehst du beim Start des jeweiligen Spigot-Servers in der Konsole:
```
[TeleportSuite] BungeeCord-Unterstuetzung aktiviert. Server: survival
```
Falls du stattdessen diese Warnung siehst:
```
[TeleportSuite] BungeeCord deaktiviert (bungee.enabled=false).
```
→ Dann hast du in der `config.yml` dieses Servers `enabled: false` stehen gelassen. Auf `true` ändern und neu starten.
---
## Konfiguration
Die vollständige `config.yml` mit allen Optionen:
```yaml
database:
type: sqlite # sqlite oder mysql
mysql: mysql:
host: localhost host: localhost
port: 3306 port: 3306
@@ -46,144 +132,101 @@ database:
pool-size: 10 pool-size: 10
sqlite: sqlite:
file: teleportsuite.db file: teleportsuite.db
```
- `sqlite` ist die Standard-Einstellung und benötigt keine zusätzliche Serverdatenbank.
- `mysql` ist sinnvoll, wenn mehrere Server auf dieselben Teleport-Daten zugreifen sollen.
### BungeeCord
```yaml
bungee: bungee:
enabled: false enabled: true
server-name: "survival" server-name: "survival" # Name dieses Servers in der BungeeCord config.yml
```
- Aktiviere `enabled: true`, wenn das Plugin im BungeeCord-Setup genutzt werden soll.
- `server-name` muss dem Namen des Servers im Netzwerk entsprechen.
### Teleport
```yaml
teleport: teleport:
delay: 3 delay: 3 # Wartezeit in Sekunden vor dem Teleport (0 = sofort)
cooldown: 5 cooldown: 5 # Cooldown in Sekunden zwischen Teleports
warmup-cancel-on-move: true warmup-cancel-on-move: true # Teleport abbrechen wenn Spieler sich bewegt
request-timeout: 60 request-timeout: 60 # Sekunden bis eine TPA-Anfrage automatisch verfällt
```
- `delay` ist die Wartezeit vor einem Teleport.
- `cooldown` verhindert zu häufige Teleports.
- `warmup-cancel-on-move` bricht den Teleport ab, wenn sich der Spieler bewegt.
- `request-timeout` legt fest, wie lange eine TPA-Anfrage gültig bleibt.
### Homes
```yaml
homes: homes:
max-homes-default: 3 max-homes-default: 3
max-homes-vip: 10 max-homes-vip: 10
max-homes-premium: 25 max-homes-premium: 25
```
### Warps
```yaml
warps: warps:
allow-player-warps: false allow-player-warps: false
warp-permission-prefix: "teleportsuite.warp." warp-permission-prefix: "teleportsuite.warp."
```
### Portale
```yaml
portals: portals:
check-interval: 5 check-interval: 5
particle-effect: true particle-effect: true
```
### Spawn
```yaml
spawn: spawn:
first-join-teleport: true first-join-teleport: true
death-respawn-to-spawn: false death-respawn-to-spawn: false
``` ```
### Nachrichten ---
Alle Nachrichten unterstützen `&`-Farbcodes und Platzhalter wie `{player}`, `{name}`, `{seconds}` und `{max}`.
## Commands ## Commands
| Command | Beschreibung | Permission | | Command | Beschreibung | Permission |
| --- | --- | --- | |---|---|---|
| /tp <player> | Teleportiere zu einem Spieler | teleportsuite.tp | | `/tp <spieler>` | Teleportiere zu einem Spieler | `teleportsuite.tp` |
| /tphere <player> | Teleportiere einen Spieler zu dir | teleportsuite.tphere | | `/tphere <spieler>` | Teleportiere einen Spieler zu dir | `teleportsuite.tphere` |
| /tpa <player> | Sende eine Teleportanfrage | teleportsuite.tpa | | `/tpa <spieler>` | Sende eine Teleportanfrage | `teleportsuite.tpa` |
| /tpaccept | Akzeptiere eine Teleportanfrage | teleportsuite.tpaccept | | `/tpaccept` | Akzeptiere eine Teleportanfrage | `teleportsuite.tpaccept` |
| /tpdeny | Lehne eine Teleportanfrage ab | teleportsuite.tpdeny | | `/tpdeny` | Lehne eine Teleportanfrage ab | `teleportsuite.tpdeny` |
| /back | Teleportiere zur letzten Position | teleportsuite.back | | `/back` | Teleportiere zur letzten Position | `teleportsuite.back` |
| /deathback | Teleportiere zum letzten Todesort | teleportsuite.deathback | | `/deathback` | Teleportiere zum letzten Todesort | `teleportsuite.deathback` |
| /sethome [name] | Setze ein Home | teleportsuite.sethome | | `/sethome [name]` | Setze ein Home | `teleportsuite.sethome` |
| /home [name] | Teleportiere zu einem Home | teleportsuite.home | | `/home [name]` | Teleportiere zu einem Home | `teleportsuite.home` |
| /delhome <name> | Lösche ein Home | teleportsuite.delhome | | `/delhome <name>` | Lösche ein Home | `teleportsuite.delhome` |
| /homes | Liste alle Homes auf | teleportsuite.home | | `/homes` | Liste alle Homes auf | `teleportsuite.home` |
| /setwarp <name> | Setze einen Warp | teleportsuite.setwarp | | `/setwarp <name>` | Setze einen Warp | `teleportsuite.setwarp` |
| /warp <name> | Teleportiere zu einem Warp | teleportsuite.warp | | `/warp <name>` | Teleportiere zu einem Warp | `teleportsuite.warp` |
| /delwarp <name> | Lösche einen Warp | teleportsuite.delwarp | | `/delwarp <name>` | Lösche einen Warp | `teleportsuite.delwarp` |
| /warps | Liste alle Warps auf | teleportsuite.warp | | `/warps` | Liste alle Warps auf | `teleportsuite.warp` |
| /setportal <name> <target-server/world> | Erstelle ein Portal | teleportsuite.setportal | | `/setportal <name> <server/welt>` | Erstelle ein Portal | `teleportsuite.setportal` |
| /delportal <name> | Lösche ein Portal | teleportsuite.delportal | | `/delportal <name>` | Lösche ein Portal | `teleportsuite.delportal` |
| /portals | Liste alle Portale auf | teleportsuite.portals | | `/portals` | Liste alle Portale auf | `teleportsuite.portals` |
| /setsavepoint [name] | Setze einen Savepoint | teleportsuite.savepoint | | `/setsavepoint [name]` | Setze einen Savepoint | `teleportsuite.savepoint` |
| /savepoint [name] | Teleportiere zu einem Savepoint | teleportsuite.savepoint | | `/savepoint [name]` | Teleportiere zu einem Savepoint | `teleportsuite.savepoint` |
| /setspawn | Setze den Spawn | teleportsuite.setspawn | | `/setspawn` | Setze den Spawn | `teleportsuite.setspawn` |
| /spawn | Teleportiere zum Spawn | teleportsuite.spawn | | `/spawn` | Teleportiere zum Spawn | `teleportsuite.spawn` |
| /setfirstspawn | Setze den FirstSpawn | teleportsuite.setfirstspawn | | `/setfirstspawn` | Setze den FirstSpawn (für neue Spieler) | `teleportsuite.setfirstspawn` |
| /tppos <x> <y> <z> [world] | Teleportiere zu Koordinaten | teleportsuite.tppos | | `/tppos <x> <y> <z> [welt]` | Teleportiere zu Koordinaten | `teleportsuite.tppos` |
| /tpall | Teleportiere alle Spieler zu dir | teleportsuite.tpall | | `/tpall` | Teleportiere alle Spieler zu dir | `teleportsuite.tpall` |
| /tpworld <world> | Teleportiere in eine Welt | teleportsuite.tpworld | | `/tpworld <welt>` | Teleportiere in eine Welt | `teleportsuite.tpworld` |
| /entitytransport <entity-id> <player/world> | Transportiere ein Entity | teleportsuite.entitytransport | | `/entitytransport <id> <spieler/welt>` | Transportiere ein Entity | `teleportsuite.entitytransport` |
---
## Permissions ## Permissions
### Sammelrechte | Permission | Beschreibung |
|---|---|
| `teleportsuite.*` | Alle Rechte des Plugins |
| `teleportsuite.home.unlimited` | Unbegrenzte Homes (Standard: nur OPs) |
| `teleportsuite.nodelay` | Kein Teleport-Warmup |
| `teleportsuite.nocooldown` | Kein Teleport-Cooldown |
| `teleportsuite.admin` | Andere Spieler teleportieren (`/tp <von> <zu>`) |
- `teleportsuite.*` - Alle Rechte des Plugins Alle weiteren Einzelrechte entsprechen dem jeweiligen Command-Namen, z.B. `teleportsuite.tp`, `teleportsuite.home`, `teleportsuite.warp` usw.
- `teleportsuite.home.unlimited` - Unbegrenzte Homes, standardmäßig nur für OPs
### Wichtige Einzelrechte ---
- `teleportsuite.tp` ## Häufige Probleme
- `teleportsuite.tphere`
- `teleportsuite.tpa` **„Spieler XY nicht gefunden" beim serverübergreifenden `/tp`**
- `teleportsuite.tpaccept` `bungee.enabled` in der `config.yml` steht auf `false`. Auf `true` setzen und neu starten.
- `teleportsuite.tpdeny`
- `teleportsuite.back` **Spieler landet am Spawn statt beim Zielspieler**
- `teleportsuite.deathback` `server-name` in der `config.yml` stimmt nicht mit dem Namen in der BungeeCord `config.yml` überein.
- `teleportsuite.sethome`
- `teleportsuite.home` **TPA-Nachrichten kommen nicht an**
- `teleportsuite.delhome` → Das Plugin läuft nicht auf dem BungeeCord-Proxy. Die JAR muss auch in den `plugins`-Ordner des Proxys.
- `teleportsuite.setwarp`
- `teleportsuite.warp` **Homes/Warps sind auf anderen Servern nicht verfügbar**
- `teleportsuite.delwarp` → Alle Server müssen dieselbe MySQL-Datenbank verwenden. SQLite funktioniert nur lokal auf einem Server.
- `teleportsuite.setportal`
- `teleportsuite.delportal` ---
- `teleportsuite.portals`
- `teleportsuite.savepoint`
- `teleportsuite.setspawn`
- `teleportsuite.spawn`
- `teleportsuite.setfirstspawn`
- `teleportsuite.tppos`
- `teleportsuite.tpall`
- `teleportsuite.tpworld`
- `teleportsuite.entitytransport`
## Support ## Support
Wenn du Probleme, Wünsche oder Fehlerberichte hast, eröffne ein Issue auf GitHub und füge wenn möglich den vollständigen Server-Log sowie deine `config.yml` bei. Probleme, Wünsche oder Fehlerberichte bitte als Issue auf GitHub melden. Füge wenn möglich den vollständigen Server-Log sowie deine `config.yml` bei.
## Lizenz
Falls du eine Lizenz verwenden möchtest, ergänze sie bitte in diesem Abschnitt oder als separate LICENSE-Datei im Repository.

View File

@@ -7,7 +7,7 @@
<groupId>de.teleportsuite</groupId> <groupId>de.teleportsuite</groupId>
<artifactId>TeleportSuite</artifactId> <artifactId>TeleportSuite</artifactId>
<version>1.0.0</version> <version>1.0.5</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<properties> <properties>

View File

@@ -19,6 +19,7 @@ public class TeleportSuite extends JavaPlugin {
private SavePointManager savePointManager; private SavePointManager savePointManager;
private BungeeMessenger bungeeMessenger; private BungeeMessenger bungeeMessenger;
private ConfigManager configManager; private ConfigManager configManager;
private TeleportSuiteTabCompleter tabCompleter;
@Override @Override
public void onEnable() { public void onEnable() {
@@ -42,11 +43,22 @@ public class TeleportSuite extends JavaPlugin {
savePointManager = new SavePointManager(this); savePointManager = new SavePointManager(this);
if (configManager.isBungeeEnabled()) { if (configManager.isBungeeEnabled()) {
String serverName = configManager.getServerName();
if (serverName == null || serverName.isBlank() || serverName.equals("survival")) {
getLogger().warning("==================================================");
getLogger().warning("BUNGEE: bungee.server-name in config.yml ist noch");
getLogger().warning("auf dem Default-Wert 'survival'. Bitte auf den");
getLogger().warning("tatsaechlichen BungeeCord-Servernamen aendern!");
getLogger().warning("==================================================");
}
bungeeMessenger = new BungeeMessenger(this); bungeeMessenger = new BungeeMessenger(this);
bungeeMessenger.register(); bungeeMessenger.register();
getLogger().info("BungeeCord-Unterstuetzung aktiviert."); getLogger().info("BungeeCord-Unterstuetzung aktiviert. Server: " + serverName);
} else {
getLogger().warning("BungeeCord deaktiviert (bungee.enabled=false). Cross-Server-Teleport nicht verfuegbar.");
} }
tabCompleter = new TeleportSuiteTabCompleter(this);
getServer().getPluginManager().registerEvents(new PlayerJoinListener(this), this); getServer().getPluginManager().registerEvents(new PlayerJoinListener(this), this);
getServer().getPluginManager().registerEvents(new PlayerDeathListener(this), this); getServer().getPluginManager().registerEvents(new PlayerDeathListener(this), this);
getServer().getPluginManager().registerEvents(new PlayerMoveListener(this), this); getServer().getPluginManager().registerEvents(new PlayerMoveListener(this), this);
@@ -64,33 +76,43 @@ public class TeleportSuite extends JavaPlugin {
} }
private void registerCommands() { private void registerCommands() {
getCommand("tp").setExecutor(new TpCommand(this)); registerCommand("tp", new TpCommand(this));
getCommand("tphere").setExecutor(new TpHereCommand(this)); registerCommand("tphere", new TpHereCommand(this));
getCommand("tpa").setExecutor(new TpaCommand(this)); registerCommand("tpa", new TpaCommand(this));
getCommand("tpaccept").setExecutor(new TpAcceptCommand(this)); registerCommand("tpaccept", new TpAcceptCommand(this));
getCommand("tpdeny").setExecutor(new TpDenyCommand(this)); registerCommand("tpdeny", new TpDenyCommand(this));
getCommand("tppos").setExecutor(new TpPosCommand(this)); registerCommand("tppos", new TpPosCommand(this));
getCommand("tpall").setExecutor(new TpAllCommand(this)); registerCommand("tpall", new TpAllCommand(this));
getCommand("tpworld").setExecutor(new TpWorldCommand(this)); registerCommand("tpworld", new TpWorldCommand(this));
getCommand("back").setExecutor(new BackCommand(this)); registerCommand("back", new BackCommand(this));
getCommand("deathback").setExecutor(new DeathBackCommand(this)); registerCommand("deathback", new DeathBackCommand(this));
getCommand("sethome").setExecutor(new SetHomeCommand(this)); registerCommand("sethome", new SetHomeCommand(this));
getCommand("home").setExecutor(new HomeCommand(this)); registerCommand("home", new HomeCommand(this));
getCommand("delhome").setExecutor(new DelHomeCommand(this)); registerCommand("delhome", new DelHomeCommand(this));
getCommand("homes").setExecutor(new HomesCommand(this)); registerCommand("homes", new HomesCommand(this));
getCommand("setwarp").setExecutor(new SetWarpCommand(this)); registerCommand("setwarp", new SetWarpCommand(this));
getCommand("warp").setExecutor(new WarpCommand(this)); registerCommand("warp", new WarpCommand(this));
getCommand("delwarp").setExecutor(new DelWarpCommand(this)); registerCommand("delwarp", new DelWarpCommand(this));
getCommand("warps").setExecutor(new WarpsCommand(this)); registerCommand("warps", new WarpsCommand(this));
getCommand("setportal").setExecutor(new SetPortalCommand(this)); registerCommand("setportal", new SetPortalCommand(this));
getCommand("delportal").setExecutor(new DelPortalCommand(this)); registerCommand("delportal", new DelPortalCommand(this));
getCommand("portals").setExecutor(new PortalsCommand(this)); registerCommand("portals", new PortalsCommand(this));
getCommand("setspawn").setExecutor(new SetSpawnCommand(this)); registerCommand("setspawn", new SetSpawnCommand(this));
getCommand("spawn").setExecutor(new SpawnCommand(this)); registerCommand("spawn", new SpawnCommand(this));
getCommand("setfirstspawn").setExecutor(new SetFirstSpawnCommand(this)); registerCommand("setfirstspawn", new SetFirstSpawnCommand(this));
getCommand("setsavepoint").setExecutor(new SetSavePointCommand(this)); registerCommand("setsavepoint", new SetSavePointCommand(this));
getCommand("savepoint").setExecutor(new SavePointCommand(this)); registerCommand("savepoint", new SavePointCommand(this));
getCommand("entitytransport").setExecutor(new EntityTransportCommand(this)); registerCommand("entitytransport", new EntityTransportCommand(this));
}
private void registerCommand(String name, org.bukkit.command.CommandExecutor executor) {
org.bukkit.command.PluginCommand command = getCommand(name);
if (command == null) {
getLogger().warning("Befehl '" + name + "' ist in plugin.yml nicht registriert.");
return;
}
command.setExecutor(executor);
command.setTabCompleter(tabCompleter);
} }
public static TeleportSuite getInstance() { return instance; } public static TeleportSuite getInstance() { return instance; }
@@ -103,4 +125,5 @@ public class TeleportSuite extends JavaPlugin {
public SavePointManager getSavePointManager() { return savePointManager; } public SavePointManager getSavePointManager() { return savePointManager; }
public BungeeMessenger getBungeeMessenger() { return bungeeMessenger; } public BungeeMessenger getBungeeMessenger() { return bungeeMessenger; }
public ConfigManager getConfigManager() { return configManager; } public ConfigManager getConfigManager() { return configManager; }
public TeleportSuiteTabCompleter getTabCompleter() { return tabCompleter; }
} }

View File

@@ -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;
}
}

View File

@@ -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());
}
}
}

View File

@@ -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; }
}

View File

@@ -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<String, PendingTpa> pendingTpa = new HashMap<>();
private final TeleportSuiteBungee plugin;
public TeleportMessageListener(TeleportSuiteBungee plugin) {
this.plugin = plugin;
}
public static Map<String, PendingTpa> 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 <target> → 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 <from> <to> → 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 <target> → 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());
}
}
}

View File

@@ -1,105 +1,285 @@
package de.teleportsuite.bungeemessaging; package de.teleportsuite.bungeemessaging;
import de.teleportsuite.TeleportSuite; import de.teleportsuite.TeleportSuite;
import de.teleportsuite.models.TeleportLocation;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageListener; import org.bukkit.plugin.messaging.PluginMessageListener;
import java.io.*; import java.io.*;
/** /**
* Handles BungeeCord Plugin Messaging. * Spigot-seitiger Kanal zum TeleportSuite-Bungee-Plugin.
* Sends players to other servers and transmits target coordinates
* so a receiving TeleportSuite instance can teleport them on arrival.
* *
* Channel "teleportsuite:tp" payload format (DataOutputStream): * Prinzip (identisch zu BTM):
* String targetPlayer * Spigot schickt Anfragen auf CHANNEL_TO_BUNGEE.
* String world * BungeeCord erledigt connect() + routing und schickt Befehle auf CHANNEL_TO_SPIGOT.
* Double x, y, z * Spigot führt den finalen player.teleport() aus.
* Float yaw, pitch *
* 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 class BungeeMessenger implements PluginMessageListener {
private static final String BUNGEE_CHANNEL = "BungeeCord"; public static final String CHANNEL_TO_BUNGEE = "teleportsuite:tobungee";
private static final String TS_CHANNEL = "teleportsuite:tp"; public static final String CHANNEL_TO_SPIGOT = "teleportsuite:tospigot";
private final TeleportSuite plugin; private final TeleportSuite plugin;
public BungeeMessenger(TeleportSuite plugin) { this.plugin = plugin; } public BungeeMessenger(TeleportSuite plugin) {
this.plugin = plugin;
}
public void register() { public void register() {
plugin.getServer().getMessenger().registerOutgoingPluginChannel(plugin, BUNGEE_CHANNEL); plugin.getServer().getMessenger().registerOutgoingPluginChannel(plugin, CHANNEL_TO_BUNGEE);
plugin.getServer().getMessenger().registerOutgoingPluginChannel(plugin, TS_CHANNEL); plugin.getServer().getMessenger().registerIncomingPluginChannel(plugin, CHANNEL_TO_SPIGOT, this);
plugin.getServer().getMessenger().registerIncomingPluginChannel(plugin, TS_CHANNEL, this);
} }
public void unregister() { public void unregister() {
plugin.getServer().getMessenger().unregisterOutgoingPluginChannel(plugin); plugin.getServer().getMessenger().unregisterOutgoingPluginChannel(plugin, CHANNEL_TO_BUNGEE);
plugin.getServer().getMessenger().unregisterIncomingPluginChannel(plugin); plugin.getServer().getMessenger().unregisterIncomingPluginChannel(plugin, CHANNEL_TO_SPIGOT, this);
}
// -------------------------------------------------------------------------
// Outgoing helpers (Spigot → Bungee)
// -------------------------------------------------------------------------
/** /tp <target> 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 <from> <to> 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 <target> 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());
});
} }
/** /**
* Connect a player to another BungeeCord server. * Home / Warp / Spawn / Back / SavePoint / Portal / TpPos:
* Also sends a plugin message so the target server knows where to teleport the player. * Koordinaten sind bekannt → Bungee wechselt den Server und Spigot
* setzt den Spieler direkt an die exakten Koordinaten.
*/ */
public void connectToServer(Player player, String server, String world, double x, double y, double z, float yaw, float pitch) { public void teleportToPosition(Player player, String server, String world,
// 1) Notify the target server about the pending teleport double x, double y, double z, float yaw, float pitch) {
sendTeleportPayload(player, player.getName(), server, world, x, y, z, yaw, pitch); send(player, out -> {
out.writeUTF("TP_POS");
// 2) Switch server via BungeeCord out.writeUTF(player.getName());
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(bos)) {
out.writeUTF("Connect");
out.writeUTF(server); out.writeUTF(server);
player.sendPluginMessage(plugin, BUNGEE_CHANNEL, bos.toByteArray()); out.writeUTF(world);
} catch (IOException e) { out.writeDouble(x);
plugin.getLogger().warning("BungeeCord Connect Fehler: " + e.getMessage()); out.writeDouble(y);
} out.writeDouble(z);
out.writeFloat(yaw);
out.writeFloat(pitch);
});
} }
private void sendTeleportPayload(Player sender, String targetPlayer, String server, String world, // TPA
double x, double y, double z, float yaw, float pitch) {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); public void sendTpaRequest(Player from, String toName,
DataOutputStream out = new DataOutputStream(bos)) { String type, String msgToTarget, String msgToRequester) {
out.writeUTF("Forward"); send(from, out -> {
out.writeUTF(server); out.writeUTF("TPA_REQUEST");
out.writeUTF(TS_CHANNEL); out.writeUTF(from.getName());
// Sub-payload out.writeUTF(toName);
ByteArrayOutputStream sub = new ByteArrayOutputStream(); out.writeUTF(type);
DataOutputStream subOut = new DataOutputStream(sub); out.writeUTF(msgToTarget);
subOut.writeUTF(targetPlayer); out.writeUTF(msgToRequester);
subOut.writeUTF(world); });
subOut.writeDouble(x);
subOut.writeDouble(y);
subOut.writeDouble(z);
subOut.writeFloat(yaw);
subOut.writeFloat(pitch);
byte[] subBytes = sub.toByteArray();
out.writeShort(subBytes.length);
out.write(subBytes);
sender.sendPluginMessage(plugin, BUNGEE_CHANNEL, bos.toByteArray());
} catch (IOException e) {
plugin.getLogger().warning("TS Forward Fehler: " + e.getMessage());
}
} }
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 @Override
public void onPluginMessageReceived(String channel, Player player, byte[] message) { public void onPluginMessageReceived(String channel, Player unused, byte[] bytes) {
if (!channel.equals(TS_CHANNEL)) return; if (!channel.equals(CHANNEL_TO_SPIGOT)) return;
try (DataInputStream in = new DataInputStream(new ByteArrayInputStream(message))) {
String targetName = in.readUTF();
String world = in.readUTF();
double x = in.readDouble(), y = in.readDouble(), z = in.readDouble();
float yaw = in.readFloat(), pitch = in.readFloat();
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { try (DataInputStream in = new DataInputStream(new ByteArrayInputStream(bytes))) {
Player target = plugin.getServer().getPlayer(targetName); String task = in.readUTF();
if (target == null || !target.isOnline()) return;
de.teleportsuite.models.TeleportLocation loc = switch (task) {
new de.teleportsuite.models.TeleportLocation(world, x, y, z, yaw, pitch,
plugin.getConfigManager().getServerName()); // Bungee hat sender bereits auf diesen Server gewechselt.
plugin.getTeleportManager().teleport(target, loc, false); // Sobald beide Spieler lokal online sind, teleportieren.
}, 20L); case "TP_PLAYERTOPLAYER": {
String senderName = in.readUTF();
String targetName = in.readUTF();
schedulePlayerToPlayerTeleportWithBack(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;
// Save back location BEFORE teleporting (teleport() with saveBack=false
// skips this, so we do it manually here for cross-server teleports)
plugin.getDatabaseManager().saveLastLocation(p.getUniqueId(),
new TeleportLocation(p.getLocation(), plugin.getConfigManager().getServerName()));
TeleportLocation dest = new TeleportLocation(
world, x, y, z, yaw, pitch,
plugin.getConfigManager().getServerName());
plugin.getTeleportManager().teleport(p, dest, 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) { } catch (IOException e) {
plugin.getLogger().warning("Fehler beim Lesen der TS-Nachricht: " + e.getMessage()); 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),
* speichert Back-Location und führt dann player.teleport() aus.
*/
private void schedulePlayerToPlayerTeleportWithBack(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()) {
// Save back location before teleport so /back works cross-server
plugin.getDatabaseManager().saveLastLocation(sender.getUniqueId(),
new TeleportLocation(sender.getLocation(),
plugin.getConfigManager().getServerName()));
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());
} }
} }
} }

View File

@@ -9,7 +9,7 @@ public class DelHomeCommand implements CommandExecutor {
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { 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 instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; }
Player p = (Player) sender; Player p = (Player) sender;
if (!p.hasPermission("teleportsuite.sethome")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } if (!p.hasPermission("teleportsuite.delhome")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; }
if (args.length < 1) { p.sendMessage("§cVerwendung: /delhome <name>"); return true; } if (args.length < 1) { p.sendMessage("§cVerwendung: /delhome <name>"); return true; }
plugin.getHomeManager().deleteHome(p, args[0]); plugin.getHomeManager().deleteHome(p, args[0]);
return true; return true;

View File

@@ -10,7 +10,10 @@ public class HomeCommand implements CommandExecutor {
if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; }
Player p = (Player) sender; Player p = (Player) sender;
if (!p.hasPermission("teleportsuite.home")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } if (!p.hasPermission("teleportsuite.home")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; }
String name = args.length > 0 ? args[0] : "home"; String name = args.length > 0 ? args[0] : p.getWorld().getName();
if (args.length == 0 && plugin.getDatabaseManager().getHome(p.getUniqueId(), name) == null) {
name = "home";
}
plugin.getHomeManager().teleportHome(p, name); plugin.getHomeManager().teleportHome(p, name);
return true; return true;
} }

View File

@@ -10,7 +10,7 @@ public class SetHomeCommand implements CommandExecutor {
if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; } if (!(sender instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; }
Player p = (Player) sender; Player p = (Player) sender;
if (!p.hasPermission("teleportsuite.sethome")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } if (!p.hasPermission("teleportsuite.sethome")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; }
String name = args.length > 0 ? args[0] : "home"; String name = args.length > 0 ? args[0] : p.getWorld().getName();
plugin.getHomeManager().setHome(p, name); plugin.getHomeManager().setHome(p, name);
return true; return true;
} }

View File

@@ -0,0 +1,122 @@
package de.teleportsuite.commands;
import de.teleportsuite.TeleportSuite;
import de.teleportsuite.models.Home;
import de.teleportsuite.models.Warp;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TeleportSuiteTabCompleter implements TabCompleter {
private final TeleportSuite plugin;
public TeleportSuiteTabCompleter(TeleportSuite plugin) {
this.plugin = plugin;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
String name = command.getName().toLowerCase();
List<String> suggestions = new ArrayList<>();
switch (name) {
case "tp":
case "tphere":
case "tpa":
case "tpall":
if (args.length == 1) {
suggestOnlinePlayers(args[0], suggestions);
} else if ("tp".equals(name) && args.length == 2 && sender.hasPermission("teleportsuite.admin")) {
suggestOnlinePlayers(args[1], suggestions);
}
break;
case "home":
case "delhome":
case "sethome":
if (sender instanceof Player) {
Player player = (Player) sender;
if (args.length == 1) {
suggestHomeNames(player, args[0], suggestions);
if ("sethome".equals(name)) {
String worldName = player.getWorld().getName();
StringUtil.copyPartialMatches(args[0], Collections.singletonList(worldName), suggestions);
}
}
}
break;
case "warp":
case "delwarp":
case "setwarp":
if (args.length == 1) {
suggestWarpNames(args[0], suggestions);
}
break;
case "delportal":
case "setportal":
if (args.length == 1) {
suggestPortalNames(args[0], suggestions);
}
break;
case "tpworld":
if (args.length == 1) {
suggestWorlds(args[0], suggestions);
}
break;
default:
break;
}
Collections.sort(suggestions, String.CASE_INSENSITIVE_ORDER);
return suggestions;
}
private void suggestOnlinePlayers(String token, List<String> suggestions) {
List<String> names = new ArrayList<>();
Bukkit.getOnlinePlayers().forEach(player -> names.add(player.getName()));
StringUtil.copyPartialMatches(token, names, suggestions);
}
private void suggestHomeNames(Player player, String token, List<String> suggestions) {
List<String> names = new ArrayList<>();
for (Home home : plugin.getHomeManager().getHomes(player.getUniqueId())) {
names.add(home.getName());
}
StringUtil.copyPartialMatches(token, names, suggestions);
}
private void suggestWarpNames(String token, List<String> suggestions) {
List<String> names = new ArrayList<>();
for (Warp warp : plugin.getWarpManager().getAllWarps()) {
names.add(warp.getName());
}
StringUtil.copyPartialMatches(token, names, suggestions);
}
private void suggestPortalNames(String token, List<String> suggestions) {
List<String> names = new ArrayList<>();
plugin.getPortalManager().getPortals().forEach(portal -> names.add(portal.getName()));
StringUtil.copyPartialMatches(token, names, suggestions);
}
private void suggestWorlds(String token, List<String> suggestions) {
List<String> names = new ArrayList<>();
for (World world : Bukkit.getWorlds()) {
names.add(world.getName());
}
StringUtil.copyPartialMatches(token, names, suggestions);
}
}

View File

@@ -1,22 +1,27 @@
package de.teleportsuite.commands; package de.teleportsuite.commands;
import de.teleportsuite.TeleportSuite; import de.teleportsuite.TeleportSuite;
import de.teleportsuite.models.TeleportLocation; import de.teleportsuite.models.TeleportLocation;
import org.bukkit.Bukkit;
import org.bukkit.command.*; import org.bukkit.command.*;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
public class TpAllCommand implements CommandExecutor { public class TpAllCommand implements CommandExecutor {
private final TeleportSuite plugin; private final TeleportSuite plugin;
public TpAllCommand(TeleportSuite p) { this.plugin = p; } public TpAllCommand(TeleportSuite p) { this.plugin = p; }
@Override @Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { 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 instanceof Player)) { sender.sendMessage("§cNur für Spieler!"); return true; }
Player p = (Player) sender; Player p = (Player) sender;
if (!p.hasPermission("teleportsuite.tpall")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } if (!p.hasPermission("teleportsuite.tpall")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; }
TeleportLocation dest = new TeleportLocation(p.getLocation(), plugin.getConfigManager().getServerName()); if (plugin.getConfigManager().isBungeeEnabled() && plugin.getBungeeMessenger() != null) {
for (Player online : Bukkit.getOnlinePlayers()) { plugin.getBungeeMessenger().teleportAll(p);
if (!online.equals(p)) plugin.getTeleportManager().teleport(online, dest); } 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);
}
} }
p.sendMessage("§aAlle Spieler wurden zu dir teleportiert.");
return true; return true;
} }
} }

View File

@@ -19,14 +19,24 @@ public class TpCommand implements CommandExecutor {
if (args.length == 1) { if (args.length == 1) {
Player target = Bukkit.getPlayer(args[0]); Player target = Bukkit.getPlayer(args[0]);
if (target == null) { p.sendMessage(plugin.getConfigManager().getMessage("player-not-found","player",args[0])); return true; } if (target != null) {
plugin.getTeleportManager().teleport(p, new TeleportLocation(target.getLocation(), plugin.getConfigManager().getServerName())); 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 { } else {
if (!p.hasPermission("teleportsuite.admin")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; }
Player from = Bukkit.getPlayer(args[0]); Player from = Bukkit.getPlayer(args[0]);
Player to = Bukkit.getPlayer(args[1]); Player to = Bukkit.getPlayer(args[1]);
if (from == null || to == null) { p.sendMessage("§cEin Spieler nicht gefunden."); return true; } if (from != null && to != null) {
if (!p.hasPermission("teleportsuite.admin")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } plugin.getTeleportManager().teleport(from, new TeleportLocation(to.getLocation(), plugin.getConfigManager().getServerName()));
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; return true;
} }

View File

@@ -17,8 +17,13 @@ public class TpHereCommand implements CommandExecutor {
if (!p.hasPermission("teleportsuite.tphere")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } if (!p.hasPermission("teleportsuite.tphere")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; }
if (args.length < 1) { p.sendMessage("§cVerwendung: /tphere <spieler>"); return true; } if (args.length < 1) { p.sendMessage("§cVerwendung: /tphere <spieler>"); return true; }
Player target = Bukkit.getPlayer(args[0]); Player target = Bukkit.getPlayer(args[0]);
if (target == null) { p.sendMessage(plugin.getConfigManager().getMessage("player-not-found","player",args[0])); return true; } if (target != null) {
plugin.getTeleportManager().teleport(target, new TeleportLocation(p.getLocation(), plugin.getConfigManager().getServerName())); 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; return true;
} }
} }

View File

@@ -15,10 +15,18 @@ public class TpaCommand implements CommandExecutor {
Player p = (Player) sender; Player p = (Player) sender;
if (!p.hasPermission("teleportsuite.tpa")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; } if (!p.hasPermission("teleportsuite.tpa")) { p.sendMessage(plugin.getConfigManager().getMessage("no-permission")); return true; }
if (args.length < 1) { p.sendMessage("§cVerwendung: /tpa <spieler>"); return true; } if (args.length < 1) { p.sendMessage("§cVerwendung: /tpa <spieler>"); 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]); Player target = Bukkit.getPlayer(args[0]);
if (target == null) { p.sendMessage(plugin.getConfigManager().getMessage("player-not-found","player",args[0])); return true; } if (target != null) {
if (target.equals(p)) { p.sendMessage("§cDu kannst dir nicht selbst eine Anfrage senden."); return true; } // Same server
plugin.getTeleportManager().sendTpaRequest(p, target); 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; return true;
} }
} }

View File

@@ -2,7 +2,9 @@ package de.teleportsuite.listeners;
import de.teleportsuite.TeleportSuite; import de.teleportsuite.TeleportSuite;
import de.teleportsuite.models.TeleportLocation; import de.teleportsuite.models.TeleportLocation;
import org.bukkit.Location;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.player.PlayerRespawnEvent;
@@ -10,12 +12,33 @@ public class PlayerRespawnListener implements Listener {
private final TeleportSuite plugin; private final TeleportSuite plugin;
public PlayerRespawnListener(TeleportSuite plugin) { this.plugin = plugin; } public PlayerRespawnListener(TeleportSuite plugin) { this.plugin = plugin; }
@EventHandler @EventHandler(priority = EventPriority.HIGH)
public void onRespawn(PlayerRespawnEvent event) { public void onRespawn(PlayerRespawnEvent event) {
if (!plugin.getConfigManager().deathRespawnToSpawn()) return; if (!plugin.getConfigManager().deathRespawnToSpawn()) return;
TeleportLocation spawn = plugin.getDatabaseManager().getSpawn("spawn"); TeleportLocation spawn = plugin.getDatabaseManager().getSpawn("spawn");
if (spawn == null) return; if (spawn == null) {
var loc = spawn.toBukkitLocation(); plugin.getLogger().warning("death-respawn-to-spawn ist aktiv, aber kein Spawn gesetzt!");
if (loc != null) event.setRespawnLocation(loc); return;
}
// Spawn ist auf einem anderen Server → nach dem Respawn teleportieren
String localServer = plugin.getConfigManager().getServerName();
if (!spawn.isLocalServer(localServer)) {
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> {
if (event.getPlayer().isOnline()) {
plugin.getTeleportManager().teleport(event.getPlayer(), spawn, false);
}
}, 5L);
return;
}
// Spawn ist lokal → direkt als Respawn-Location setzen
Location loc = spawn.toBukkitLocation();
if (loc == null || loc.getWorld() == null) {
plugin.getLogger().warning("Spawn-Welt \"" + spawn.getWorld() + "\" nicht gefunden!");
return;
}
event.setRespawnLocation(loc);
} }
} }

View File

@@ -1,6 +1,7 @@
package de.teleportsuite.managers; package de.teleportsuite.managers;
import de.teleportsuite.TeleportSuite; import de.teleportsuite.TeleportSuite;
import de.teleportsuite.bungeemessaging.BungeeMessenger;
import de.teleportsuite.models.TeleportLocation; import de.teleportsuite.models.TeleportLocation;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
@@ -10,18 +11,22 @@ import java.util.*;
public class TeleportManager { public class TeleportManager {
private final TeleportSuite plugin; private final TeleportSuite plugin;
// Pending TPA requests: requester -> target // Same-server pending TPA: requesterUUID → targetUUID
private final Map<UUID, UUID> tpaRequests = new HashMap<>(); private final Map<UUID, UUID> tpaRequests = new HashMap<>();
private final Map<UUID, Long> requestTimestamps = new HashMap<>(); private final Map<UUID, Long> requestTimestamps = new HashMap<>();
// Cooldowns // Cooldowns
private final Map<UUID, Long> cooldowns = new HashMap<>(); private final Map<UUID, Long> cooldowns = new HashMap<>();
// Warmup tasks // Warmup tasks
private final Map<UUID, Integer> warmupTasks = new HashMap<>(); private final Map<UUID, Integer> warmupTasks = new HashMap<>();
// Saved "before teleport" locations (for /back) // Back-locations (saved before every teleport)
private final Map<UUID, TeleportLocation> pendingBackLocations = new HashMap<>(); private final Map<UUID, TeleportLocation> pendingBackLocations = new HashMap<>();
public TeleportManager(TeleportSuite plugin) { this.plugin = plugin; } public TeleportManager(TeleportSuite plugin) { this.plugin = plugin; }
// -------------------------------------------------------------------------
// Main teleport entry point
// -------------------------------------------------------------------------
public void teleport(Player player, TeleportLocation destination) { public void teleport(Player player, TeleportLocation destination) {
teleport(player, destination, true); teleport(player, destination, true);
} }
@@ -30,31 +35,36 @@ public class TeleportManager {
int delay = plugin.getConfigManager().getTeleportDelay(); int delay = plugin.getConfigManager().getTeleportDelay();
if (player.hasPermission("teleportsuite.nodelay")) delay = 0; if (player.hasPermission("teleportsuite.nodelay")) delay = 0;
// Check cooldown
if (!player.hasPermission("teleportsuite.nocooldown")) { if (!player.hasPermission("teleportsuite.nocooldown")) {
long cooldownMs = plugin.getConfigManager().getTeleportCooldown() * 1000L; long cooldownMs = plugin.getConfigManager().getTeleportCooldown() * 1000L;
long lastTp = cooldowns.getOrDefault(player.getUniqueId(), 0L); long lastTp = cooldowns.getOrDefault(player.getUniqueId(), 0L);
if (System.currentTimeMillis() - lastTp < cooldownMs) { if (System.currentTimeMillis() - lastTp < cooldownMs) {
long remaining = (cooldownMs - (System.currentTimeMillis() - lastTp)) / 1000; long remaining = (cooldownMs - (System.currentTimeMillis() - lastTp)) / 1000;
player.sendMessage(plugin.getConfigManager().getMessage("teleport-cooldown", "seconds", String.valueOf(remaining))); player.sendMessage(plugin.getConfigManager().getMessage(
"teleport-cooldown", "seconds", String.valueOf(remaining)));
return; return;
} }
} }
if (saveBack) pendingBackLocations.put(player.getUniqueId(), new TeleportLocation(player.getLocation(), plugin.getConfigManager().getServerName())); if (saveBack) {
pendingBackLocations.put(player.getUniqueId(),
new TeleportLocation(player.getLocation(), plugin.getConfigManager().getServerName()));
}
if (delay <= 0) { if (delay <= 0) {
executeTeleport(player, destination); executeTeleport(player, destination);
return; return;
} }
player.sendMessage(plugin.getConfigManager().getMessage("teleport-warmup", "seconds", String.valueOf(delay))); player.sendMessage(plugin.getConfigManager().getMessage(
"teleport-warmup", "seconds", String.valueOf(delay)));
Location startLoc = player.getLocation().clone(); Location startLoc = player.getLocation().clone();
int taskId = Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> { int taskId = Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> {
if (plugin.getConfigManager().cancelOnMove()) { if (plugin.getConfigManager().cancelOnMove()) {
Location now = player.getLocation(); Location now = player.getLocation();
if (Math.abs(now.getX()-startLoc.getX()) > 0.5 || Math.abs(now.getZ()-startLoc.getZ()) > 0.5) { if (Math.abs(now.getX() - startLoc.getX()) > 0.5
|| Math.abs(now.getZ() - startLoc.getZ()) > 0.5) {
player.sendMessage(plugin.getConfigManager().getMessage("teleport-cancelled")); player.sendMessage(plugin.getConfigManager().getMessage("teleport-cancelled"));
warmupTasks.remove(player.getUniqueId()); warmupTasks.remove(player.getUniqueId());
return; return;
@@ -70,10 +80,17 @@ public class TeleportManager {
private void executeTeleport(Player player, TeleportLocation dest) { private void executeTeleport(Player player, TeleportLocation dest) {
String localServer = plugin.getConfigManager().getServerName(); 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)) { if (!dest.isLocalServer(localServer)) {
// BungeeCord-Teleport // Cross-server: delegate completely to the Bungee plugin.
if (plugin.getBungeeMessenger() != null) { // It will call sender.connect() and then send TP_PLAYERTOPOSITION.
plugin.getBungeeMessenger().connectToServer(player, dest.getServer(), dest.getWorld(), BungeeMessenger bm = plugin.getBungeeMessenger();
if (bm != null) {
bm.teleportToPosition(player, dest.getServer(), dest.getWorld(),
dest.getX(), dest.getY(), dest.getZ(), dest.getYaw(), dest.getPitch()); dest.getX(), dest.getY(), dest.getZ(), dest.getYaw(), dest.getPitch());
player.sendMessage(plugin.getConfigManager().getMessage("teleport-success")); player.sendMessage(plugin.getConfigManager().getMessage("teleport-success"));
} }
@@ -82,16 +99,14 @@ public class TeleportManager {
Location loc = dest.toBukkitLocation(); Location loc = dest.toBukkitLocation();
if (loc == null || loc.getWorld() == null) { if (loc == null || loc.getWorld() == null) {
player.sendMessage("§cZielwelt nicht gefunden!"); player.sendMessage("§cFehler: Zielwelt §6" + dest.getWorld() + "§c nicht gefunden!"
+ " Ist der Spawn auf diesem Server gesetzt?");
plugin.getLogger().warning("Teleport fehlgeschlagen: Welt '" + dest.getWorld()
+ "' fuer Spieler " + player.getName() + " nicht geladen.");
return; return;
} }
// Save last location to database
plugin.getDatabaseManager().saveLastLocation(player.getUniqueId(),
new TeleportLocation(player.getLocation(), localServer));
player.teleport(loc); player.teleport(loc);
cooldowns.put(player.getUniqueId(), System.currentTimeMillis());
player.sendMessage(plugin.getConfigManager().getMessage("teleport-success")); player.sendMessage(plugin.getConfigManager().getMessage("teleport-success"));
} }
@@ -104,7 +119,10 @@ public class TeleportManager {
return pendingBackLocations.get(uuid); return pendingBackLocations.get(uuid);
} }
// TPA // -------------------------------------------------------------------------
// Same-server TPA
// -------------------------------------------------------------------------
public void sendTpaRequest(Player from, Player to) { public void sendTpaRequest(Player from, Player to) {
tpaRequests.put(from.getUniqueId(), to.getUniqueId()); tpaRequests.put(from.getUniqueId(), to.getUniqueId());
requestTimestamps.put(from.getUniqueId(), System.currentTimeMillis()); requestTimestamps.put(from.getUniqueId(), System.currentTimeMillis());
@@ -117,48 +135,89 @@ public class TeleportManager {
if (tpaRequests.containsKey(from.getUniqueId())) { if (tpaRequests.containsKey(from.getUniqueId())) {
tpaRequests.remove(from.getUniqueId()); tpaRequests.remove(from.getUniqueId());
requestTimestamps.remove(from.getUniqueId()); requestTimestamps.remove(from.getUniqueId());
if (from.isOnline()) from.sendMessage(plugin.getConfigManager().getMessage("tpa-expired")); if (from.isOnline()) {
from.sendMessage(plugin.getConfigManager().getMessage("tpa-expired"));
}
} }
}, timeout * 20L); }, 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) { public boolean acceptTpa(Player target) {
UUID requester = null; // Same-server accept
UUID requesterUUID = null;
for (Map.Entry<UUID, UUID> entry : tpaRequests.entrySet()) { for (Map.Entry<UUID, UUID> entry : tpaRequests.entrySet()) {
if (entry.getValue().equals(target.getUniqueId())) { if (entry.getValue().equals(target.getUniqueId())) {
requester = entry.getKey(); requesterUUID = entry.getKey();
break; break;
} }
} }
if (requester == null) return false; if (requesterUUID != null) {
Player from = Bukkit.getPlayer(requesterUUID);
Player from = Bukkit.getPlayer(requester); tpaRequests.remove(requesterUUID);
tpaRequests.remove(requester); requestTimestamps.remove(requesterUUID);
requestTimestamps.remove(requester); if (from != null && from.isOnline()) {
from.sendMessage(plugin.getConfigManager().getMessage("tpa-accepted", "player", target.getName()));
if (from != null && from.isOnline()) { teleport(from, new TeleportLocation(target.getLocation(),
from.sendMessage(plugin.getConfigManager().getMessage("tpa-accepted", "player", target.getName())); plugin.getConfigManager().getServerName()));
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; return true;
} }
/** Called by TpDenyCommand — handles both same-server and cross-server. */
public boolean denyTpa(Player target) { public boolean denyTpa(Player target) {
UUID requester = null; // Same-server deny
UUID requesterUUID = null;
for (Map.Entry<UUID, UUID> entry : tpaRequests.entrySet()) { for (Map.Entry<UUID, UUID> entry : tpaRequests.entrySet()) {
if (entry.getValue().equals(target.getUniqueId())) { if (entry.getValue().equals(target.getUniqueId())) {
requester = entry.getKey(); requesterUUID = entry.getKey();
break; break;
} }
} }
if (requester == null) return false; 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;
}
Player from = Bukkit.getPlayer(requester); // Cross-server deny
tpaRequests.remove(requester); BungeeMessenger bm = plugin.getBungeeMessenger();
requestTimestamps.remove(requester); if (bm == null) return false;
bm.sendTpaDeny(target, null,
if (from != null && from.isOnline()) plugin.getConfigManager().getMessage("tpa-denied", "player", target.getName()));
from.sendMessage(plugin.getConfigManager().getMessage("tpa-denied", "player", target.getName()));
return true; return true;
} }
} }

View File

@@ -0,0 +1,5 @@
name: TeleportSuite-Bungee
main: de.teleportsuite.bungee.TeleportSuiteBungee
version: 1.0.5
author: TeleportSuite
description: BungeeCord-Companion fuer TeleportSuite

View File

@@ -15,9 +15,10 @@ database:
file: teleportsuite.db file: teleportsuite.db
# BungeeCord Einstellungen # BungeeCord Einstellungen
# WICHTIG: server-name muss dem Namen dieses Servers in der BungeeCord config.yml entsprechen!
bungee: bungee:
enabled: false enabled: true
server-name: "survival" # Name dieses Servers im BungeeCord-Netz server-name: "survival" # Name dieses Servers im BungeeCord-Netz (z.B. "lobby", "survival", "creative")
# Teleport Einstellungen # Teleport Einstellungen
teleport: teleport:
@@ -70,6 +71,7 @@ messages:
death-no-location: "&cKein Todesort gespeichert." death-no-location: "&cKein Todesort gespeichert."
no-permission: "&cDazu hast du keine Berechtigung." no-permission: "&cDazu hast du keine Berechtigung."
player-not-found: "&cSpieler &6{player}&c nicht gefunden." player-not-found: "&cSpieler &6{player}&c nicht gefunden."
tpa-request-not-found: "&cKeine offene Teleportanfrage gefunden."
portal-created: "&aPortal &6{name}&a erstellt!" portal-created: "&aPortal &6{name}&a erstellt!"
portal-deleted: "&cPortal &6{name}&c gelöscht." portal-deleted: "&cPortal &6{name}&c gelöscht."
savepoint-set: "&aSavepoint &6{name}&a gesetzt!" savepoint-set: "&aSavepoint &6{name}&a gesetzt!"

View File

@@ -1,5 +1,5 @@
name: TeleportSuite name: TeleportSuite
version: 1.0.0 version: 1.0.5
main: de.teleportsuite.TeleportSuite main: de.teleportsuite.TeleportSuite
api-version: 1.20 api-version: 1.20
description: BungeeCord-fähiges Teleport-Komplettpaket description: BungeeCord-fähiges Teleport-Komplettpaket