13 Commits
1.1.1 ... 1.1.3

Author SHA1 Message Date
d2fd8c496d Upload readme.md via GUI 2026-02-05 21:15:10 +00:00
1ca544fadc Upload pom.xml via GUI 2026-02-05 21:15:09 +00:00
253515862e Update from Git Manager GUI 2026-02-05 22:15:08 +01:00
8693ef41af readme.md aktualisiert 2026-01-22 20:09:44 +00:00
52f00f9b93 readme.md aktualisiert 2026-01-22 20:09:27 +00:00
fb05868e65 readme.md aktualisiert 2026-01-22 20:09:02 +00:00
6943022cd3 readme.md aktualisiert 2026-01-22 20:08:32 +00:00
da5adc8034 readme.md aktualisiert 2026-01-22 20:05:13 +00:00
197acc6750 readme.md aktualisiert 2026-01-22 20:00:13 +00:00
40295f3711 Upload readme.md via GUI 2026-01-13 20:06:41 +00:00
1638ad4e8a Upload pom.xml via GUI 2026-01-13 20:06:41 +00:00
e5251933e1 Update from Git Manager GUI 2026-01-13 21:06:39 +01:00
08b05167d8 readme.md aktualisiert 2026-01-13 17:58:57 +00:00
21 changed files with 2373 additions and 579 deletions

29
pom.xml
View File

@@ -1,13 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>de.viper</groupId> <groupId>de.viper</groupId>
<artifactId>SurvivalPlus</artifactId> <artifactId>SurvivalPlus</artifactId>
<version>1.1.1-Beta</version> <version>1.1.3</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>SurvivalPlus</name> <name>SurvivalPlus</name>
@@ -26,6 +25,10 @@
<id>codemc-repo</id> <id>codemc-repo</id>
<url>https://repo.codemc.io/repository/maven-public/</url> <url>https://repo.codemc.io/repository/maven-public/</url>
</repository> </repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories> </repositories>
<dependencies> <dependencies>
@@ -61,13 +64,21 @@
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- ProtocolLib 4.8.0 (für WrapperPlayServerScoreboardTeam) --> <!-- ProtocolLib 4.8.0 -->
<dependency> <dependency>
<groupId>com.comphenix.protocol</groupId> <groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId> <artifactId>ProtocolLib</artifactId>
<version>4.8.0</version> <version>4.8.0</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- Vault API (JitPack) -->
<dependency>
<groupId>com.github.MilkBowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7.1</version>
<scope>provided</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
@@ -78,8 +89,8 @@
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version> <version>3.8.1</version>
<configuration> <configuration>
<source>17</source> <!-- Hier KEINE expliziten source/target Werte setzen. -->
<target>17</target> <!-- Maven nutzt jetzt automatisch die 21 aus den Properties oben. -->
</configuration> </configuration>
</plugin> </plugin>
@@ -87,7 +98,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version> <version>3.5.0</version>
<executions> <executions>
<execution> <execution>
<phase>package</phase> <phase>package</phase>
@@ -130,8 +141,10 @@
<include>lootchests.yml</include> <include>lootchests.yml</include>
<include>blocks.yml</include> <include>blocks.yml</include>
<include>nicknames.yml</include> <include>nicknames.yml</include>
<include>mobadapt.yml</include>
<include>heads.yml</include>
</includes> </includes>
</resource> </resource>
</resources> </resources>
</build> </build>
</project> </project>

528
readme.md
View File

@@ -1,174 +1,354 @@
# SurvivalPlus
# SurvivalPlus
![Minecraft Plugin](https://img.shields.io/badge/Minecraft-Plugin-green)
![Version](https://img.shields.io/badge/version-1.1.1-blue) **Minecraft Plugin für 1.21+**
![Author](https://img.shields.io/badge/author-M_Viper-yellow)
**Version:** 1.1.3
**SurvivalPlus** ist ein Minecraft-Plugin zur Verbesserung des Survival-Erlebnisses. **Autor:** M_Viper
Es bietet Homes, Teleports, Inventar-/Enderchest-Verwaltung, Claims (Anti-Grief), Freundeslisten, Shop-System, Loot-Kisten, Tablist-Anpassungen und viele Komfort-Utilities.
---
---
## 📦 Übersicht & Features
## ⚡ Highlights / Features
**SurvivalPlus** erweitert das klassische Minecraft-Survival mit Komfort, Schutz und Admin-Tools:
- Homes & Warps (persönliche Warps, Home-Management)
- Teleportation & Anfrage-System (`/tpa`, `/tpaccept`, `/tpdeny`) - Homes, Warps, Teleportsystem (Tpa, Back, Spawn)
- Claim-System zur Land-Sicherung (trust/untrust, create/delete, info, list) - Claim-System (Anti-Grief, Trust, Ban, Kick, Claim-Infos)
- Inventar- & Enderchest-Verwaltung (öffnen von Fremd-Inventaren, Admin-Tools) - Inventar- & Enderchest-Management (auch für Admins)
- Shop-System & Lootchests (Verwaltung, Teleport zu Lootchests) - Freundesliste, Block- & Report-System
- CommandBlocker & Server-Utilities (clearchat, clearitems, closedoors, lock) - Shop-System, Lootchests, Sign Shops (Vault)
- Debug-Logging (optionale `debug-logging` in config, Debug/debug.log & Debug/console.log) - Player Heads, Vanish, Freeze, Ride, Sitzfunktion
- Tablist (animiert / konfigurierbar über `tablist.yml`) - CommandBlocker, Clearchat, Clearitems, Closedoors, Lock-System
- bStats Unterstützung (Statistiken) - Animierte Tablist, bStats, Debug-Logging, umfangreiche Konfiguration
--- ---
## 🛠 Komplette Befehlsübersicht ## 🛠 Befehle (Commands)
> Hinweis: In `plugin.yml` sind alle Commands definiert — hier die Übersicht gruppiert nach Kategorie. Alle Befehle und Permissions findest du auch in der **plugin.yml**. Hier die wichtigsten:
### Allgemein / Haupt | Befehl | Beschreibung | Permission |
| Befehl | Nutzung | Permission | |--------|--------------|------------|
|---|---:|:---| | /sp | Hauptbefehl (Info, Reload, Help, Share, CommandBlocker) | survivalplus.sp |
| `/sp` | Hauptbefehl (CommandBlocker, reload, info, share, help u.v.m.) | `survivalplus.sp` | | /help | Hilfe & Übersicht | survivalplus.sp |
| `/help` (alias über `/sp help`) | Hilfe / Übersicht | `survivalplus.sp` | | /ir <Name> | Item umbenennen | survivalplus.itemrename |
| /workbench | Werkbank-GUI öffnen | survivalplus.workbench |
### Item & Utility | /anvil | Amboss-GUI öffnen | survivalplus.anvil |
| Befehl | Usage | Permission | | /trash | Mülleimer öffnen | survivalplus.trash |
|---|---:|:---| | /showarmorstands | Debug: ArmorStands sichtbar | survivalplus.showarmorstands |
| `/ir <neuer_name>` | Item umbenennen (Item in Hand) | `survivalplus.itemrename` | | /cleardebugarmorstands | Debug-ArmorStands entfernen | survivalplus.cleardebugarmorstands |
| `/workbench` | Öffnet Werkbank-GUI | `survivalplus.workbench` | | /leashcount | Geleinte Tiere anzeigen | survivalplus.leashcount |
| `/anvil` | Öffnet Amboss-GUI | `survivalplus.anvil` | | /nick <Name> | Nickname setzen | survivalplus.nick |
| `/trash` | Öffnet Mülleimer | `survivalplus.trash` | | /head <Spieler> | Spieler-Kopf erhalten | survivalplus.head |
| `/showarmorstands` | Alle (debug) ArmorStands sichtbar machen | `survivalplus.showarmorstands` | | /vanish | Unsichtbar werden | survivalplus.vanish |
| `/cleardebugarmorstands` | Entfernt Debug-ArmorStands | `survivalplus.cleardebugarmorstands` | | /freeze <Spieler> | Spieler einfrieren | survivalplus.freeze |
| `/leashcount` | Anzahl geleinter Tiere anzeigen | `survivalplus.leashcount` | | /ride [Spieler] | Reite einen Spieler | survivalplus.ride |
| `/nick <Name>` | Nickname ändern (Farben/Hex möglich) | `survivalplus.nick` | | /tp <Spieler> | Teleport zu Spieler | survivalplus.tp |
| `/nick off` | Entfernt den Nickname wieder (Zurücksetzen zum echten Namen). | `survivalplus.nick` | | /tphere <Spieler> | Spieler zu dir teleportieren | survivalplus.tphere |
| `/nick reset` | Alias für /nick off | `survivalplus.nick` | | /tpa <Spieler> | Teleportanfrage senden | survivalplus.tpa |
| `/nick remove` | Alias für /nick off | `survivalplus.nick` | | /tpaccept | Anfrage akzeptieren | survivalplus.tpaccept |
| /tpdeny | Anfrage ablehnen | survivalplus.tpdeny |
### Teleportation | /back | Zum letzten Todespunkt | survivalplus.back |
| Befehl | Usage | Permission | | /spawn | Zum Weltspawn | survivalplus.spawn |
|---|---:|:---| | /setspawn | Server-Spawn setzen | survivalplus.setspawn |
| `/tp <Spieler>` | Teleport zu Spieler | `survivalplus.tp` | | /setworldspawn | Welt-Spawn setzen | survivalplus.setworldspawn |
| `/tphere <Spieler>` | Teleportiert Spieler zu dir | `survivalplus.tphere` | | /sethome <Name> | Home setzen | survivalplus.homes.set |
| `/tpa <Spieler>` | Teleportanfrage senden | `survivalplus.tpa` | | /delhome <Name> | Home löschen | survivalplus.homes.delete |
| `/tpaccept` | Teleportanfrage annehmen | `survivalplus.tpaccept` | | /home <Name> | Zu Home teleportieren | survivalplus.homes |
| `/tpdeny` | Teleportanfrage ablehnen | `survivalplus.tpdeny` | | /homelist | GUI mit allen Homes | survivalplus.homes.list |
| `/back` | Zum letzten Todespunkt teleportieren | `survivalplus.back` | | /setwarp <Name> | Persönlichen Warp setzen | survivalplus.setwarp |
| `/spawn` | Teleport zum Weltspawn | `survivalplus.spawn` | | /delwarp <Name> | Warp löschen | survivalplus.delwarp |
| `/setspawn` | Setzt Server-Spawn | `survivalplus.setspawn` | | /warps | Warp-GUI öffnen | survivalplus.warps |
| `/setworldspawn` | Setzt Weltspawn (aktuelle Welt) | `survivalplus.setworldspawn` | | /claim ... | Claim-System verwalten | survivalplus.claim.* |
| /sp cb ... | CommandBlocker verwalten | survivalplus.commandblocker.* |
### Homes & Warps | /clearchat | Chat leeren | survivalplus.clearchat |
| Befehl | Usage | Permission | | /clearitems | Items entfernen | survivalplus.clearitems |
|---|---:|:---| | /closedoors <radius> | Türen schließen | survivalplus.closedoors |
| `/sethome <name>` | Setzt Home | `survivalplus.homes.set` | | /sp lock | Kisten/Türen sperren | survivalplus.lock |
| `/delhome <name>` | Löscht Home | `survivalplus.homes.delete` | | /shop ... | Shop verwalten | survivalplus.shop |
| `/home <name>` | Teleport zu Home | `survivalplus.homes` | | /lootchests | Loot-Kisten auflisten | survivalplus.lootchests |
| `/homelist` | GUI mit Homes öffnen | `survivalplus.homes.list` | | /tploot <welt> <x> <y> <z> | Zu Loot-Kiste teleportieren | survivalplus.lootchests |
| `/setwarp <name>` | Persönlichen Warp setzen (Item in Hand) | `survivalplus.setwarp` | | /trade <Spieler> | Handel starten | survivalplus.trade |
| `/delwarp <name>` | Persönlichen Warp löschen | `survivalplus.delwarp` | | /tradeaccept <Spieler> | Handel annehmen | survivalplus.tradeaccept |
| `/warps` | GUI aller Warps öffnen | `survivalplus.warps` | | /day | Tag setzen | survivalplus.day |
| /night | Nacht setzen | survivalplus.night |
### Claim (Anti-Grief) | /gm <Modus> | Gamemode ändern | survivalplus.gamemode |
| Befehl | Usage | Permission | | /heal [Spieler] | Spieler heilen | survivalplus.heal |
|---|---:|:---| | /friend ... | Freundesverwaltung | survivalplus.friend |
| `/claim [unclaim / trust <player> / untrust <player>]` | Claim-Management (Trust/Untrust/Unclaim) | `survivalplus.claim.use` | | /block <Spieler> | Spieler blockieren | survivalplus.block |
| `/claim create <name>` (falls implementiert) | Neues Claim anlegen | `survivalplus.claim.use` | | /unblock <Spieler> | Blockierung aufheben | survivalplus.unblock |
| `/claim delete <name>` (falls implementiert) | Claim löschen | `survivalplus.claim.use` | | /blocklist | Blockliste anzeigen | survivalplus.blocklist |
| `/claim addmember <player>` | Spieler zum Claim hinzufügen | `survivalplus.claim.trust` | | /report <Spieler> [Grund] | Spieler melden | survivalplus.report |
| `/claim removemember <player>` | Spieler entfernen | `survivalplus.claim.trust` | | /showreport <Spieler> | Reports anzeigen | survivalplus.report.show |
| `/claim info` | Claim-Informationen anzeigen | `survivalplus.claim.use` | | /clearreport <Spieler> | Reports löschen | survivalplus.report.clear |
| `/claim list` | Eigene Claims auflisten | `survivalplus.claim.use` | | /stats | Spielerstatistiken | survivalplus.stats |
| /kit | Starterkit | survivalplus.kit |
### CommandBlocker / Server-Management | /startchallenge <Name> | Fun-Challenge starten | survivalplus.startchallenge |
| Befehl | Usage | Permission | | /sit | Hinsetzen | survivalplus.sit |
|---|---:|:---|
| `/sp cb add <cmd>` | Befehl zur Blockliste hinzufügen | `survivalplus.commandblocker.add` | ---
| `/sp cb remove <cmd>` | Befehl entfernen | `survivalplus.commandblocker.remove` |
| `/sp cb list` | Blockierte Befehle anzeigen | `survivalplus.commandblocker.list` | ## 🔐 Permissions
| `/clearchat` | Chat löschen | `survivalplus.clearchat` |
| `/clearitems` | Items aufsammeln/entfernen | `survivalplus.clearitems` | - `survivalplus.*` — Vollzugriff (OP)
| `/closedoors <radius>` | Türen in Radius schließen | `survivalplus.closedoors` | - `survivalplus.sp` — Zugriff auf Hauptbefehl
| `/sp lock /unlock/friendadd/friendremove [player]` | Kisten/Türen sperren / freigeben | `survivalplus.lock` | - `survivalplus.claim.use / trust / admin` — Claim-System
- `survivalplus.vanish.silent` — Silent Join
### Shop, Loot & Trade - `survivalplus.vanish.no-pickup` — Keine Item-Aufnahme
| Befehl | Usage | Permission | - `survivalplus.head``/head` nutzen
|---|---:|:---| - `survivalplus.notify` — Admin-Benachrichtigungen
| `/shop add <item> <basispreis> <lagerbestand>` | Shop verwalten | `survivalplus.shop` |
| `/lootchests` | Listet Loot-Kisten auf (Admins teleportieren) | `survivalplus.lootchests` | ➡ Vollständige Liste: **plugin.yml**
| `/tploot <welt> <x> <y> <z>` | Teleportiere zu Loot-Kiste (Admin) | `survivalplus.lootchests` |
| `/trade <Spieler>` | Startet Handel | `survivalplus.trade` | ---
| `/tradeaccept <Spieler>` | Akzeptiert Handel | `survivalplus.tradeaccept` |
## ⚙️ Installation & Setup
### Zeit, Gamemode & Admin
| Befehl | Usage | Permission | 1. Aktuelle `.jar` herunterladen
|---|---:|:---| 2. In den `plugins/` Ordner legen
| `/day` | Setzt Zeit auf Tag | `survivalplus.day` | 3. **LuckPerms** und **Vault** installieren (empfohlen)
| `/night` | Setzt Zeit auf Nacht | `survivalplus.night` | 4. Server neu starten oder `/reload`
| `/gm <modus> [spieler]` (alias `gamemode`) | Spielmodus ändern | `survivalplus.gamemode` | 5. Konfigurationsdateien (`config.yml`, `lang.yml`, `help.yml`, etc.) nach Wunsch anpassen
| `/heal [spieler]` | Heilt Spieler (oder andere) | `survivalplus.heal`, `survivalplus.heal.others` |
---
### Freundes-, Block- & Report-System
| Befehl | Usage | Permission | ## ⚙️ Konfiguration & Platzhalter
|---|---:|:---|
| `/friend [add/accept/deny/list/del/tp] [Spieler]` | Freundschaften verwalten | `survivalplus.friend` | - **Alle Nachrichten**: In `lang.yml` anpassbar (außer feste Plugin-Infos)
| `/block <Spieler>` | Spieler blockieren | `survivalplus.block` | - **Hilfe & Gruppen**: In `help.yml` strukturierbar
| `/unblock <Spieler>` | Unblock | `survivalplus.unblock` | - **Tablist**: In `tablist.yml` animierbar
| `/blocklist` | Blockierte Spieler anzeigen | `survivalplus.blocklist` | - **Shop, Claims, Homes, etc.**: Eigene YAML-Dateien
| `/report <Spieler> [Grund]` | Spieler melden | `survivalplus.report` | - **Platzhalter**: `%player%`, `%x%`, `%y%`, `%z%`, `%world%`, `{item}`, `{price}`, `{stock}` u.v.m.
| `/showreport <Spieler>` | Reports anzeigen | `survivalplus.report.show` |
| `/clearreport <Spieler>` | Reports löschen | `survivalplus.report.clear` | ---
### Sonstiges ## 🐞 Debug & Fehlerberichte
| Befehl | Usage | Permission |
|---|---:|:---| - In `config.yml` aktivieren:
| `/stats` | Spielerstatistiken anzeigen | `survivalplus.stats` | ```yml
| `/kit` | Starterkit erhalten | `survivalplus.kit` | debug-logging: true
| `/startchallenge <name>` | Fun-Challenge starten | `survivalplus.startchallenge` | ```
| `/lootchests` | Übersicht über Lootkisten | `survivalplus.lootchests` | - Logs: `Debug/debug.log` (Fehler, Stacktraces), `Debug/console.log` (Konsolenausgabe)
| `/sit` | Hinsetzen | `survivalplus.sit` |
| `/ride` | Auf spieler Reiten | `survivalplus.ride` | ---
--- ## 📊 Statistiken & bStats
## 🔐 Permissions (Kurzüberblick) - Anonyme Nutzungsstatistiken via [bStats](https://bstats.org/plugin/bukkit/SurvivalPlus)
Vollständige Permission-Deklaration findest du in `plugin.yml`.
Wichtige Permissions: ---
- `survivalplus.*` — Vollzugriff (OP) ## 📜 Lizenz & Support
- `survivalplus.sp` — Zugriff auf `/sp` (Hauptbefehl)
- `survivalplus.homes.*` — Homes verwalten Dieses Projekt ist **frei für privaten Gebrauch**.
- `survivalplus.claim.use` / `survivalplus.claim.trust` — Claim-Management & Trust
- `survivalplus.shop` — Shopverwaltung **Kontakt / Support / Feature-Wünsche:**
- `survivalplus.lootchests` — Lootchest-Adminrechte - [SpigotMC-Profil](https://www.spigotmc.org/resources/authors/m-lukas-17.618600/)
- `survivalplus.notify` — Admin-Benachrichtigung bei Besitz von Command/Structure-Blocks - [Bugtracker](https://git.viper.ipv64.net/M_Viper/Survival-Plus/issues)
- uvm. — siehe `plugin.yml` für die vollständige Liste
---
---
## ❓ FAQ & Hinweise
## ⚙ Installation
- **Welche Minecraft-Version?** 1.21+ (Paper/Spigot/Bukkit)
1. Lade die aktuelle `.jar` Datei herunter. - **Welche Plugins werden unterstützt?** LuckPerms, Vault, PlaceholderAPI, ProtocolLib (softdepend)
2. Kopiere sie in den `plugins`-Ordner deines Servers. - **Wie kann ich alle Texte anpassen?** Über `lang.yml` und `help.yml`
3. Starte den Server neu oder nutze `/reload`. - **Wie kann ich Claims, Shops, Homes sichern?** YAML-Dateien im `plugins/SurvivalPlus/`-Ordner
4. Stelle sicher, dass **LuckPerms** und **PlaceholderAPI** installiert sind. - **Wie kann ich Fehler melden?** Siehe Support-Links oben
--- ---
## 🐞 Debug & Fehlerberichte > **Hinweis:**
> Alle Commands sind vollständig in der `plugin.yml` definiert.
- Aktiviere in `config.yml` `debug-logging: true` wenn du Probleme hast. > Nachfolgend eine Übersicht nach Kategorien.
- `Debug/debug.log` — enthält Plugin-Fehler/Stacktraces (nur wenn aktiviert).
- `Debug/console.log` — dupliziert den Konsolenoutput (komplette Ausgabe), damit du diesen als Datei an Entwickler schicken kannst. ---
--- ### Allgemein / Haupt
## 📜 Lizenz & Kontakt | Befehl | Nutzung | Permission |
Dieses Projekt ist frei für den privaten Gebrauch. Für Fragen, Bug-Reports oder Feature-Wünsche: **M_Viper** (Repo-Owner / Gitea). |------|------|-----------|
| `/sp` | Hauptbefehl (Reload, Info, Help, Share, CommandBlocker u.v.m.) | `survivalplus.sp` |
--- | `/help` | Hilfe / Übersicht (Alias: `/sp help`) | `survivalplus.sp` |
---
### Item & Utility
| Befehl | Nutzung | Permission |
|------|------|-----------|
| `/ir <name>` | Item umbenennen (Item in Hand) | `survivalplus.itemrename` |
| `/workbench` | Öffnet Werkbank-GUI | `survivalplus.workbench` |
| `/anvil` | Öffnet Amboss-GUI | `survivalplus.anvil` |
| `/trash` | Öffnet Mülleimer | `survivalplus.trash` |
| `/showarmorstands` | Debug: ArmorStands sichtbar | `survivalplus.showarmorstands` |
| `/cleardebugarmorstands` | Entfernt Debug-ArmorStands | `survivalplus.cleardebugarmorstands` |
| `/leashcount` | Zeigt geleinte Tiere | `survivalplus.leashcount` |
| `/nick <Name>` | Nickname setzen (Farben/Hex) | `survivalplus.nick` |
| `/nick off` | Nickname entfernen | `survivalplus.nick` |
| `/head <Spieler>` | Spieler-Kopf erhalten | `survivalplus.head` |
---
### Admin & Tools
| Befehl | Nutzung | Permission |
|------|------|-----------|
| `/vanish` | Unsichtbar (Silent, No-Pickup optional) | `survivalplus.vanish` |
| `/freeze <Spieler>` | Spieler einfrieren | `survivalplus.freeze` |
| `/ride [Spieler]` | Reite einen Spieler | `survivalplus.ride` |
---
### Teleportation
| Befehl | Nutzung | Permission |
|------|------|-----------|
| `/tp <Spieler>` | Teleport zu Spieler | `survivalplus.tp` |
| `/tphere <Spieler>` | Spieler zu dir teleportieren | `survivalplus.tphere` |
| `/tpa <Spieler>` | Teleportanfrage senden | `survivalplus.tpa` |
| `/tpaccept` | Anfrage akzeptieren | `survivalplus.tpaccept` |
| `/tpdeny` | Anfrage ablehnen | `survivalplus.tpdeny` |
| `/back` | Zum letzten Todespunkt | `survivalplus.back` |
| `/spawn` | Zum Weltspawn | `survivalplus.spawn` |
| `/setspawn` | Server-Spawn setzen | `survivalplus.setspawn` |
| `/setworldspawn` | Welt-Spawn setzen | `survivalplus.setworldspawn` |
---
### Homes & Warps
| Befehl | Nutzung | Permission |
|------|------|-----------|
| `/sethome <name>` | Home setzen | `survivalplus.homes.set` |
| `/delhome <name>` | Home löschen | `survivalplus.homes.delete` |
| `/home <name>` | Zu Home teleportieren | `survivalplus.homes` |
| `/homelist` | GUI mit allen Homes | `survivalplus.homes.list` |
| `/setwarp <name>` | Persönlichen Warp setzen | `survivalplus.setwarp` |
| `/delwarp <name>` | Warp löschen | `survivalplus.delwarp` |
| `/warps` | Warp-GUI öffnen | `survivalplus.warps` |
---
### Claim (Anti-Grief)
| Befehl | Nutzung | Permission |
|------|------|-----------|
| `/claim mark 1|2` | Auswahlpunkt setzen | — |
| `/claim` | Gebiet claimen | `survivalplus.claim.use` |
| `/claim unclaim` | Claim löschen | `survivalplus.claim.use` |
| `/claim unclaim <Spieler>` | Alle Claims löschen (Admin) | `survivalplus.claim.admin` |
| `/claim trust <Spieler>` | Spieler hinzufügen | `survivalplus.claim.trust` |
| `/claim untrust <Spieler>` | Spieler entfernen | `survivalplus.claim.trust` |
| `/claim ban <Spieler>` | Spieler bannen | `survivalplus.claim.ban` |
| `/claim kick <Spieler>` | Spieler kicken | `survivalplus.claim.kick` |
| `/claim info` | Claim-Infos anzeigen | `survivalplus.claim.use` |
---
### CommandBlocker / Server-Management
| Befehl | Nutzung | Permission |
|------|------|-----------|
| `/sp cb add <cmd>` | Befehl blockieren | `survivalplus.commandblocker.add` |
| `/sp cb remove <cmd>` | Blockierung entfernen | `survivalplus.commandblocker.remove` |
| `/sp cb list` | Blockierte Befehle | `survivalplus.commandblocker.list` |
| `/clearchat` | Chat leeren | `survivalplus.clearchat` |
| `/clearitems` | Items entfernen | `survivalplus.clearitems` |
| `/closedoors <radius>` | Türen schließen | `survivalplus.closedoors` |
| `/sp lock` | Kisten/Türen sperren | `survivalplus.lock` |
---
### Shop, Loot & Trade
| Befehl | Nutzung | Permission |
|------|------|-----------|
| **Sign Shops** | `[Buy] Menge Item Preis` | automatisch |
| `/shop add <item> <preis> <bestand>` | Shop verwalten | `survivalplus.shop` |
| `/lootchests` | Loot-Kisten auflisten | `survivalplus.lootchests` |
| `/tploot <welt> <x> <y> <z>` | Zu Loot-Kiste teleportieren | `survivalplus.lootchests` |
| `/trade <Spieler>` | Handel starten | `survivalplus.trade` |
| `/tradeaccept <Spieler>` | Handel annehmen | `survivalplus.tradeaccept` |
---
### Zeit, Gamemode & Admin
| Befehl | Nutzung | Permission |
|------|------|-----------|
| `/day` | Tag setzen | `survivalplus.day` |
| `/night` | Nacht setzen | `survivalplus.night` |
| `/gm <modus>` | Gamemode ändern | `survivalplus.gamemode` |
| `/heal [Spieler]` | Spieler heilen | `survivalplus.heal` |
---
### Freundes-, Block- & Report-System
| Befehl | Nutzung | Permission |
|------|------|-----------|
| `/friend add|accept|deny` | Freundesverwaltung | — |
| `/block <Spieler>` | Spieler blockieren | `survivalplus.block` |
| `/unblock <Spieler>` | Blockierung aufheben | `survivalplus.unblock` |
| `/blocklist` | Blockliste anzeigen | `survivalplus.blocklist` |
| `/report <Spieler> [Grund]` | Spieler melden | `survivalplus.report` |
| `/showreport <Spieler>` | Reports anzeigen | `survivalplus.report.show` |
| `/clearreport <Spieler>` | Reports löschen | `survivalplus.report.clear` |
---
### Sonstiges
| Befehl | Nutzung | Permission |
|------|------|-----------|
| `/stats` | Spielerstatistiken | `survivalplus.stats` |
| `/kit` | Starterkit | `survivalplus.kit` |
| `/startchallenge <name>` | Fun-Challenge starten | `survivalplus.startchallenge` |
| `/sit` | Hinsetzen | `survivalplus.sit` |
---
## 🔐 Permissions (Kurzüberblick)
- `survivalplus.*` — Vollzugriff (OP)
- `survivalplus.sp` — Zugriff auf Hauptbefehl
- `survivalplus.claim.use / trust / admin` — Claim-System
- `survivalplus.vanish.silent` — Silent Join
- `survivalplus.vanish.no-pickup` — Keine Item-Aufnahme
- `survivalplus.head` — `/head` nutzen
- `survivalplus.notify` — Admin-Benachrichtigungen
➡ Vollständige Liste: **plugin.yml**
---
## ⚙ Installation
1. Aktuelle `.jar` herunterladen
2. In den `plugins/` Ordner legen
3. **LuckPerms** und **Vault** installieren
4. Server neu starten oder `/reload`
---
## 🐞 Debug & Fehlerberichte
- In `config.yml` setzen:
```yml
debug-logging: true
```
## 🐞 Debug & Logs
- `Debug/debug.log` — Enthält Plugin-Fehler und Stacktraces (nur bei aktiviertem Debug-Logging)
- `Debug/console.log` — Kompletter Konsolenoutput zur Weitergabe an Entwickler
---
## 📜 Lizenz & Kontakt
Dieses Projekt ist frei für den **privaten Gebrauch**.
**Kontakt / Support / Feature-Wünsche:**
**M_Viper** (Repo-Owner / GitHub)

View File

@@ -12,16 +12,28 @@ public class ShopManager {
private final SurvivalPlus plugin; private final SurvivalPlus plugin;
private final File shopFile; private final File shopFile;
private final FileConfiguration shopConfig; private final FileConfiguration shopConfig;
// Multiplikatoren für dynamische Preise
private double buyMultiplier;
private double sellMultiplier;
public ShopManager(SurvivalPlus plugin) { public ShopManager(SurvivalPlus plugin) {
this.plugin = plugin; this.plugin = plugin;
this.shopFile = new File(plugin.getDataFolder(), "shop.yml"); this.shopFile = new File(plugin.getDataFolder(), "shop.yml");
if (!shopFile.exists()) { if (!shopFile.exists()) {
plugin.saveResource("shop.yml", false); plugin.saveResource("shop.yml", false);
} }
this.shopConfig = YamlConfiguration.loadConfiguration(shopFile); this.shopConfig = YamlConfiguration.loadConfiguration(shopFile);
// Multiplikatoren laden
loadMultipliers();
}
public void loadMultipliers() {
this.buyMultiplier = plugin.getConfig().getDouble("economy.buy-multiplier", 1.05);
this.sellMultiplier = plugin.getConfig().getDouble("economy.sell-multiplier", 0.95);
plugin.getLogger().info("Shop-Multiplikatoren geladen: Buy=" + buyMultiplier + ", Sell=" + sellMultiplier);
} }
public double getCurrentPrice(String itemKey) { public double getCurrentPrice(String itemKey) {
@@ -33,22 +45,22 @@ public class ShopManager {
} }
/** /**
* Spieler kauft Item: Lager sinkt, Preis steigt (Verknappung) * Spieler kauft Item: Preis steigt
*/ */
public boolean buyItem(String itemKey, int amount) { public boolean buyItem(String itemKey, int amount) {
int stock = shopConfig.getInt("items." + itemKey + ".stock", 0); int stock = shopConfig.getInt("items." + itemKey + ".stock", 0);
if (stock < amount) { // Fallback auf Base-Price wenn noch keine Transaktion stattfand (sonst wird 1.05 auf 0 gerechnet)
return false; double currentPrice = shopConfig.getDouble("items." + itemKey + ".current-price", 0);
if (currentPrice == 0) {
currentPrice = shopConfig.getDouble("items." + itemKey + ".base-price", 100);
} }
double price = shopConfig.getDouble("items." + itemKey + ".current-price", 0); // Lager aktualisieren (optional, oder unendlich)
// shopConfig.set("items." + itemKey + ".stock", stock - amount);
// Lager aktualisieren // Preis erhöhen (Dynamisch)
shopConfig.set("items." + itemKey + ".stock", stock - amount); double newPrice = currentPrice * buyMultiplier;
// Preis erhöhen (Faktor 1.05 = +5%)
double newPrice = price * 1.05;
shopConfig.set("items." + itemKey + ".current-price", newPrice); shopConfig.set("items." + itemKey + ".current-price", newPrice);
saveShop(); saveShop();
@@ -56,19 +68,20 @@ public class ShopManager {
} }
/** /**
* Spieler verkauft Item: Lager steigt, Preis sinkt (Überfluss) * Spieler verkauft Item: Preis sinkt
*/ */
public boolean sellItem(String itemKey, int amount) { public boolean sellItem(String itemKey, int amount) {
// Hier könnte man prüfen, ob der Shop genügend Geld hat (falls nötig) // Fallback auf Base-Price
double currentPrice = shopConfig.getDouble("items." + itemKey + ".current-price", 0);
if (currentPrice == 0) {
currentPrice = shopConfig.getDouble("items." + itemKey + ".base-price", 100);
}
// Lager aktualisieren (optional)
// shopConfig.set("items." + itemKey + ".stock", stock + amount);
double price = shopConfig.getDouble("items." + itemKey + ".current-price", 0); // Preis senken (Dynamisch)
int stock = shopConfig.getInt("items." + itemKey + ".stock", 0); double newPrice = currentPrice * sellMultiplier;
// Lager aktualisieren
shopConfig.set("items." + itemKey + ".stock", stock + amount);
// Preis senken (Faktor 0.95 = -5%)
double newPrice = price * 0.95;
shopConfig.set("items." + itemKey + ".current-price", newPrice); shopConfig.set("items." + itemKey + ".current-price", newPrice);
saveShop(); saveShop();
@@ -78,7 +91,10 @@ public class ShopManager {
public void addOrUpdateItem(String itemKey, double basePrice, int stock) { public void addOrUpdateItem(String itemKey, double basePrice, int stock) {
shopConfig.set("items." + itemKey + ".base-price", basePrice); shopConfig.set("items." + itemKey + ".base-price", basePrice);
shopConfig.set("items." + itemKey + ".stock", stock); shopConfig.set("items." + itemKey + ".stock", stock);
shopConfig.set("items." + itemKey + ".current-price", basePrice); // Wenn der Preis noch nicht existiert (neues Item), setze ihn auf Base-Preis
if (!shopConfig.contains("items." + itemKey + ".current-price")) {
shopConfig.set("items." + itemKey + ".current-price", basePrice);
}
saveShop(); saveShop();
} }

View File

@@ -33,6 +33,7 @@ import de.viper.survivalplus.commands.NightCommand;
import de.viper.survivalplus.commands.TradeCommand; import de.viper.survivalplus.commands.TradeCommand;
import de.viper.survivalplus.commands.ReportCommand; import de.viper.survivalplus.commands.ReportCommand;
import de.viper.survivalplus.Manager.ShopManager; import de.viper.survivalplus.Manager.ShopManager;
import de.viper.survivalplus.commands.ShopCommand;
import de.viper.survivalplus.commands.HealCommand; import de.viper.survivalplus.commands.HealCommand;
import de.viper.survivalplus.Manager.TablistManager; import de.viper.survivalplus.Manager.TablistManager;
import de.viper.survivalplus.Manager.CommandBlocker; import de.viper.survivalplus.Manager.CommandBlocker;
@@ -42,6 +43,20 @@ import de.viper.survivalplus.commands.WarpsCommand;
import de.viper.survivalplus.fun.FunChallengeManager; import de.viper.survivalplus.fun.FunChallengeManager;
import de.viper.survivalplus.listeners.ChallengeCollectListener; import de.viper.survivalplus.listeners.ChallengeCollectListener;
import de.viper.survivalplus.commands.StartFunChallengeCommand; import de.viper.survivalplus.commands.StartFunChallengeCommand;
import de.viper.survivalplus.commands.SurvivalPlusTabCompleter;
// --- NEU: Imports für Heads, Vanish & Shops ---
import de.viper.survivalplus.commands.HeadCommand;
import de.viper.survivalplus.listeners.HeadDropListener;
import de.viper.survivalplus.listeners.VanishListener;
import de.viper.survivalplus.listeners.SignShopListener;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import net.milkbowl.vault.economy.Economy;
import net.milkbowl.vault.economy.EconomyResponse;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.HashMap; import java.util.HashMap;
@@ -71,8 +86,9 @@ import java.io.FileWriter;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.plugin.RegisteredServiceProvider;
public class SurvivalPlus extends JavaPlugin { public class SurvivalPlus extends JavaPlugin {
@@ -136,6 +152,12 @@ public class SurvivalPlus extends JavaPlugin {
private File consoleFile; private File consoleFile;
private PrintWriter consoleWriter; private PrintWriter consoleWriter;
// --- NEU: Vanish & Economy Support ---
private Map<UUID, Boolean> vanishedPlayers = new HashMap<>();
private Economy economy = null;
private final String VANISH_META_KEY = "vanished";
// ---------------------------------------
public void reloadTablistConfig() { public void reloadTablistConfig() {
if (tablistFile == null) tablistFile = new File(getDataFolder(), "tablist.yml"); if (tablistFile == null) tablistFile = new File(getDataFolder(), "tablist.yml");
if (!tablistFile.exists()) { if (!tablistFile.exists()) {
@@ -251,6 +273,19 @@ public class SurvivalPlus extends JavaPlugin {
createNicknamesFile(); createNicknamesFile();
reloadBlockedCommandsConfig(); reloadBlockedCommandsConfig();
loadClaims(); loadClaims();
// --- NEU: Vault Economy Setup ---
if (Bukkit.getPluginManager().getPlugin("Vault") != null) {
RegisteredServiceProvider<Economy> rsp = getServer().getServicesManager().getRegistration(Economy.class);
if (rsp != null) {
economy = rsp.getProvider();
getLogger().info("Vault Economy erfolgreich verbunden.");
} else {
getLogger().warning("Vault Plugin gefunden, aber kein Economy Provider!");
}
}
// -------------------------------------------------
PluginManager pluginManager = getServer().getPluginManager(); PluginManager pluginManager = getServer().getPluginManager();
try { try {
Permission notifyPerm = new Permission("survivalplus.notify", PermissionDefault.OP); Permission notifyPerm = new Permission("survivalplus.notify", PermissionDefault.OP);
@@ -305,6 +340,7 @@ public class SurvivalPlus extends JavaPlugin {
getCommand("sethome").setExecutor(new HomeCommand(this)); getCommand("sethome").setExecutor(new HomeCommand(this));
getCommand("delhome").setExecutor(new HomeCommand(this)); getCommand("delhome").setExecutor(new HomeCommand(this));
getCommand("homelist").setExecutor(new HomeCommand(this)); getCommand("homelist").setExecutor(new HomeCommand(this));
getCommand("home").setExecutor(new HomeCommand(this));
getCommand("inv").setExecutor(new InventoryCommand(this)); getCommand("inv").setExecutor(new InventoryCommand(this));
getCommand("ec").setExecutor(new EnderchestCommand(this)); getCommand("ec").setExecutor(new EnderchestCommand(this));
getCommand("setspawn").setExecutor(new SetSpawnCommand(this)); getCommand("setspawn").setExecutor(new SetSpawnCommand(this));
@@ -342,9 +378,11 @@ public class SurvivalPlus extends JavaPlugin {
pluginManager.registerEvents(lootChestManager, this); pluginManager.registerEvents(lootChestManager, this);
getCommand("lootchests").setExecutor(lootChestManager); getCommand("lootchests").setExecutor(lootChestManager);
getCommand("tploot").setExecutor(lootChestManager); getCommand("tploot").setExecutor(lootChestManager);
getCommand("vanish").setExecutor(new AdminToolsCommand(this));
getCommand("freeze").setExecutor(new AdminToolsCommand(this));
getCommand("ride").setExecutor(new RideCommand(this)); getCommand("ride").setExecutor(new RideCommand(this));
getCommand("claim").setExecutor(new ClaimCommand(this)); getCommand("claim").setExecutor(new ClaimCommand(this));
// HIER: (this) hinzugefügt, damit der BlockManager die Datei speichern kann
BlockManager blockManager = new BlockManager(this); BlockManager blockManager = new BlockManager(this);
FileConfiguration config = getConfig(); FileConfiguration config = getConfig();
BackpackRecipe.register(this, langConfig); BackpackRecipe.register(this, langConfig);
@@ -354,6 +392,12 @@ public class SurvivalPlus extends JavaPlugin {
getCommand("unblock").setExecutor(new UnblockCommand(blockManager, getConfig())); getCommand("unblock").setExecutor(new UnblockCommand(blockManager, getConfig()));
getCommand("blocklist").setExecutor(new BlockListCommand(blockManager, getConfig())); getCommand("blocklist").setExecutor(new BlockListCommand(blockManager, getConfig()));
// --- NEU: NEUE LISTENER & COMMANDS ---
getCommand("head").setExecutor(new HeadCommand(this));
pluginManager.registerEvents(new HeadDropListener(this), this);
pluginManager.registerEvents(new VanishListener(this), this);
// ------------------------------------
pluginManager.registerEvents(new ChatBlockListener(blockManager), this); pluginManager.registerEvents(new ChatBlockListener(blockManager), this);
pluginManager.registerEvents(new InventoryClickListener(this), this); pluginManager.registerEvents(new InventoryClickListener(this), this);
pluginManager.registerEvents(sitListener, this); pluginManager.registerEvents(sitListener, this);
@@ -367,6 +411,7 @@ public class SurvivalPlus extends JavaPlugin {
pluginManager.registerEvents(new FirstJoinListener(), this); pluginManager.registerEvents(new FirstJoinListener(), this);
pluginManager.registerEvents(playerJoinListener, this); pluginManager.registerEvents(playerJoinListener, this);
pluginManager.registerEvents(new SignColorListener(this), this); pluginManager.registerEvents(new SignColorListener(this), this);
pluginManager.registerEvents(new SignShopListener(this), this);
pluginManager.registerEvents(new SleepListener(this), this); pluginManager.registerEvents(new SleepListener(this), this);
pluginManager.registerEvents(new OreAlarmListener(this), this); pluginManager.registerEvents(new OreAlarmListener(this), this);
pluginManager.registerEvents(new MobLeashLimitListener(this, getConfig()), this); pluginManager.registerEvents(new MobLeashLimitListener(this, getConfig()), this);
@@ -386,7 +431,7 @@ public class SurvivalPlus extends JavaPlugin {
lockSystem = new LockSystem(this); lockSystem = new LockSystem(this);
pluginManager.registerEvents(lockSystem, this); pluginManager.registerEvents(lockSystem, this);
getCommand("sp").setExecutor(lockSystem); getCommand("splock").setExecutor(lockSystem);
getCommand("sp").setExecutor(new CommandExecutor() { getCommand("sp").setExecutor(new CommandExecutor() {
@Override @Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
@@ -408,6 +453,7 @@ public class SurvivalPlus extends JavaPlugin {
}); });
pluginManager.registerEvents(new RepairSignListener(getConfig(), getLangConfig()), this); pluginManager.registerEvents(new RepairSignListener(getConfig(), getLangConfig()), this);
pluginManager.registerEvents(new ToolUpgradeListener(this), this); pluginManager.registerEvents(new ToolUpgradeListener(this), this);
registerTabCompleters();
startAutoClearTask(); startAutoClearTask();
spawnArmorStandExample(); spawnArmorStandExample();
getLogger().info(getMessage("plugin.enabled")); getLogger().info(getMessage("plugin.enabled"));
@@ -416,13 +462,13 @@ public class SurvivalPlus extends JavaPlugin {
if (Bukkit.getWorlds().isEmpty()) return; if (Bukkit.getWorlds().isEmpty()) return;
Bukkit.getWorlds().forEach(world -> { Bukkit.getWorlds().forEach(world -> {
world.setGameRule(GameRule.KEEP_INVENTORY, false); world.setGameRule(GameRule.KEEP_INVENTORY, false);
getLogger().info("Gamerule keepInventory in Welt '" + world.getName() + "' wurde auf false gesetzt."); logDebug("Gamerule keepInventory in Welt '" + world.getName() + "' wurde auf false gesetzt.");
}); });
}, 20L); }, 20L);
} }
if (getConfig().getBoolean("force-survival", true)) { if (getConfig().getBoolean("force-survival", true)) {
pluginManager.registerEvents(new ForceSurvivalListener(this), this); pluginManager.registerEvents(new ForceSurvivalListener(this), this);
getLogger().info("Force-Survival ist aktiv. Spieler werden beim Joinen in Survival gesetzt."); logDebug("Force-Survival ist aktiv. Spieler werden beim Joinen in Survival gesetzt.");
} }
try { try {
if (tablistFile == null) tablistFile = new File(getDataFolder(), "tablist.yml"); if (tablistFile == null) tablistFile = new File(getDataFolder(), "tablist.yml");
@@ -434,7 +480,7 @@ public class SurvivalPlus extends JavaPlugin {
if (defStream != null) { if (defStream != null) {
tablistConfig.setDefaults(YamlConfiguration.loadConfiguration(new InputStreamReader(defStream))); tablistConfig.setDefaults(YamlConfiguration.loadConfiguration(new InputStreamReader(defStream)));
} }
getLogger().info("Animierte Tablist wurde geladen!"); logDebug("Animierte Tablist wurde geladen!");
} catch (Exception e) { } catch (Exception e) {
getLogger().log(Level.WARNING, "Fehler beim Laden der Tablist-Konfiguration", e); getLogger().log(Level.WARNING, "Fehler beim Laden der Tablist-Konfiguration", e);
} }
@@ -450,13 +496,13 @@ public class SurvivalPlus extends JavaPlugin {
} catch (NoSuchMethodError | NoClassDefFoundError e) { } catch (NoSuchMethodError | NoClassDefFoundError e) {
world.setGameRuleValue(GameRule.COMMAND_BLOCK_OUTPUT.getName(), Boolean.toString(cmdAllowed)); world.setGameRuleValue(GameRule.COMMAND_BLOCK_OUTPUT.getName(), Boolean.toString(cmdAllowed));
} }
getLogger().info("Gamerule commandBlockOutput in Welt '" + world.getName() + "' gesetzt auf " + cmdAllowed); logDebug("Gamerule commandBlockOutput in Welt '" + world.getName() + "' gesetzt auf " + cmdAllowed);
} }
removeForbiddenBlocksFromInventories(cmdAllowed, structAllowed); removeForbiddenBlocksFromInventories(cmdAllowed, structAllowed);
getLogger().info("Block-Regeln angewendet: CommandBlocks erlaubt=" + cmdAllowed + ", StructureBlocks erlaubt=" + structAllowed); logDebug("Block-Regeln angewendet: CommandBlocks erlaubt=" + cmdAllowed + ", StructureBlocks erlaubt=" + structAllowed);
} }
private void ensureVersionAtTop(File file, String version) { private void ensureVersionAtTop(File file, String version) {
try { try {
if (!file.exists()) return; // Datei existiert nicht if (!file.exists()) return; // Datei existiert nicht
List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8); List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
@@ -520,13 +566,12 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
} }
Files.write(file.toPath(), lines, StandardCharsets.UTF_8); Files.write(file.toPath(), lines, StandardCharsets.UTF_8);
getLogger().info("config.yml: Version oben gesetzt auf " + version); logDebug("config.yml: Version oben gesetzt auf " + version);
} catch (IOException e) { } catch (IOException e) {
getLogger().warning("Konnte Version in config.yml nicht setzen: " + e.getMessage()); getLogger().warning("Konnte Version in config.yml nicht setzen: " + e.getMessage());
} }
} }
private void removeForbiddenBlocksFromInventories(boolean cmdAllowed, boolean structAllowed) { private void removeForbiddenBlocksFromInventories(boolean cmdAllowed, boolean structAllowed) {
for (Player p : Bukkit.getOnlinePlayers()) { for (Player p : Bukkit.getOnlinePlayers()) {
if (p.hasPermission("survivalplus.notify")) { if (p.hasPermission("survivalplus.notify")) {
@@ -567,6 +612,7 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
} }
} }
// === Claims.yml ===
private void createClaimsFile() { private void createClaimsFile() {
claimsFile = new File(getDataFolder(), "claims.yml"); claimsFile = new File(getDataFolder(), "claims.yml");
if (!claimsFile.exists()) { if (!claimsFile.exists()) {
@@ -700,6 +746,30 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
return false; return false;
} }
// --- NEU: Delete Claim Methode ---
public void deleteClaim(Claim claim) {
String key = getClaimKey(claim);
if (key != null) {
claims.remove(key);
playerClaimCounts.merge(claim.getOwner(), -1, Integer::sum);
saveClaims();
}
}
private String getClaimKey(Claim claim) {
for (Map.Entry<String, Claim> entry : claims.entrySet()) {
if (entry.getValue() == claim) {
return entry.getKey();
}
}
return null;
}
// -----------------------------
// WICHTIG: Getter für Claim Config für Ban-System
public FileConfiguration getClaimsConfig() {
return claimsConfig;
}
// Methoden für nicknames.yml // Methoden für nicknames.yml
@@ -750,8 +820,8 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
@Override @Override
public void onDisable() { public void onDisable() {
if (debugWriter != null) { if (debugWriter != null) {
debugWriter.close(); debugWriter.close();
} }
if (autoClearTaskId != -1) { if (autoClearTaskId != -1) {
Bukkit.getScheduler().cancelTask(autoClearTaskId); Bukkit.getScheduler().cancelTask(autoClearTaskId);
} }
@@ -760,15 +830,14 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
saveMobCapConfig(); saveMobCapConfig();
saveMobAdaptConfig(); // NEU: Adaptive Mob Config speichern saveMobAdaptConfig(); // NEU: Adaptive Mob Config speichern
// NEU: NewbieProtection-Daten sichern // NEU: NewbieProtection-Daten sichern
if (newbieListener != null) { if (newbieListener != null) {
newbieListener.saveData(); newbieListener.saveData();
} }
getLogger().info(getMessage("plugin.disabled")); getLogger().info(getMessage("plugin.disabled"));
} }
public void saveStats() { public void saveStats() {
if (statsManager != null) { if (statsManager != null) {
statsManager.saveStats(); statsManager.saveStats();
@@ -819,7 +888,7 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
// Save updated config // Save updated config
try { try {
currentConfig.save(file); currentConfig.save(file);
getLogger().info(fileName + " erfolgreich aktualisiert."); logDebug(fileName + " erfolgreich aktualisiert.");
} catch (IOException e) { } catch (IOException e) {
getLogger().severe("Fehler beim Speichern der " + fileName + ": " + e.getMessage()); getLogger().severe("Fehler beim Speichern der " + fileName + ": " + e.getMessage());
} }
@@ -1047,7 +1116,7 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
if (!leashesFile.exists()) { if (!leashesFile.exists()) {
try { try {
leashesFile.createNewFile(); leashesFile.createNewFile();
getLogger().info("leashes.yml wurde erstellt."); logDebug("leashes.yml wurde erstellt.");
} catch (IOException e) { } catch (IOException e) {
getLogger().severe("Fehler beim Erstellen der leashes.yml: " + e.getMessage()); getLogger().severe("Fehler beim Erstellen der leashes.yml: " + e.getMessage());
} }
@@ -1062,7 +1131,7 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
public void saveLeashesConfig() { public void saveLeashesConfig() {
try { try {
leashesConfig.save(leashesFile); leashesConfig.save(leashesFile);
getLogger().info("leashes.yml erfolgreich gespeichert."); logDebug("leashes.yml erfolgreich gespeichert.");
} catch (IOException e) { } catch (IOException e) {
getLogger().log(Level.SEVERE, "Fehler beim Speichern der leashes.yml: " + e.getMessage()); getLogger().log(Level.SEVERE, "Fehler beim Speichern der leashes.yml: " + e.getMessage());
} }
@@ -1070,7 +1139,7 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
public void reloadLeashesConfig() { public void reloadLeashesConfig() {
leashesConfig = YamlConfiguration.loadConfiguration(leashesFile); leashesConfig = YamlConfiguration.loadConfiguration(leashesFile);
getLogger().info("leashes.yml erfolgreich neu geladen."); logDebug("leashes.yml erfolgreich neu geladen.");
} }
// === MobCap.yml === // === MobCap.yml ===
@@ -1079,7 +1148,7 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
if (!mobCapFile.exists()) { if (!mobCapFile.exists()) {
try { try {
mobCapFile.createNewFile(); mobCapFile.createNewFile();
getLogger().info("mobcap.yml wurde erstellt."); logDebug("mobcap.yml wurde erstellt.");
} catch (IOException e) { } catch (IOException e) {
getLogger().severe("Fehler beim Erstellen der mobcap.yml: " + e.getMessage()); getLogger().severe("Fehler beim Erstellen der mobcap.yml: " + e.getMessage());
} }
@@ -1094,7 +1163,7 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
public void saveMobCapConfig() { public void saveMobCapConfig() {
try { try {
mobCapConfig.save(mobCapFile); mobCapConfig.save(mobCapFile);
getLogger().info("mobcap.yml erfolgreich gespeichert."); logDebug("mobcap.yml erfolgreich gespeichert.");
} catch (IOException e) { } catch (IOException e) {
getLogger().log(Level.SEVERE, "Fehler beim Speichern der mobcap.yml: " + e.getMessage()); getLogger().log(Level.SEVERE, "Fehler beim Speichern der mobcap.yml: " + e.getMessage());
} }
@@ -1102,7 +1171,7 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
public void reloadMobCapConfig() { public void reloadMobCapConfig() {
mobCapConfig = YamlConfiguration.loadConfiguration(mobCapFile); mobCapConfig = YamlConfiguration.loadConfiguration(mobCapFile);
getLogger().info("mobcap.yml erfolgreich neu geladen."); logDebug("mobcap.yml erfolgreich neu geladen.");
} }
// === NEU: MobAdapt.yml === // === NEU: MobAdapt.yml ===
@@ -1144,10 +1213,10 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
int intervalTicks = intervalMinutes * 60 * 20; // Minuten in Ticks umrechnen int intervalTicks = intervalMinutes * 60 * 20; // Minuten in Ticks umrechnen
if (intervalTicks > 0) { if (intervalTicks > 0) {
autoClearTaskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new AutoClearTask(this), intervalTicks, intervalTicks); autoClearTaskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new AutoClearTask(this), intervalTicks, intervalTicks);
getLogger().info("AutoClearTask gestartet mit Intervall von " + intervalMinutes + " Minuten."); logDebug("AutoClearTask gestartet mit Intervall von " + intervalMinutes + " Minuten.");
} }
} else { } else {
getLogger().info("AutoClearTask deaktiviert, da auto-clear-enabled auf false gesetzt ist."); logDebug("AutoClearTask deaktiviert, da auto-clear-enabled auf false gesetzt ist.");
} }
} }
@@ -1217,22 +1286,33 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
LockSystem lockSystem = new LockSystem(this); LockSystem lockSystem = new LockSystem(this);
pm.registerEvents(lockSystem, this); pm.registerEvents(lockSystem, this);
getCommand("lock").setExecutor(lockSystem); getCommand("splock").setExecutor(lockSystem);
// Commands neu registrieren // Commands neu registrieren
getCommand("friend").setExecutor(friendCommand); getCommand("friend").setExecutor(friendCommand);
registerTabCompleters();
getLogger().info(getMessage("plugin.reloaded")); getLogger().info(getMessage("plugin.reloaded"));
} catch (Exception e) { } catch (Exception e) {
getLogger().severe("Fehler beim Neuladen des Plugins: " + e.getMessage()); getLogger().severe("Fehler beim Neuladen des Plugins: " + e.getMessage());
e.printStackTrace(); e.printStackTrace();
} }
} }
public BannerManager getBannerManager() { public BannerManager getBannerManager() {
return bannerManager; return bannerManager;
} }
// Nur Fehler/Exceptions public WarpManager getWarpManager() {
return warpManager;
}
public FunChallengeManager getFunChallengeManager() {
return funChallengeManager;
}
// Nur Fehler/Exceptions
public void log(String msg) { public void log(String msg) {
if (getConfig().getBoolean("debug-logging", false) && debugWriter != null) { if (getConfig().getBoolean("debug-logging", false) && debugWriter != null) {
debugWriter.println("[" + new java.util.Date() + "] " + msg); debugWriter.println("[" + new java.util.Date() + "] " + msg);
@@ -1241,6 +1321,25 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
} }
} }
private void registerTabCompleters() {
SurvivalPlusTabCompleter completer = new SurvivalPlusTabCompleter(this);
String[] commands = {
"sp", "ir", "gm", "sethome", "delhome", "homelist", "home", "inv", "ec",
"setworldspawn", "setspawn", "clearchat", "clearitems", "closedoors", "sit",
"back", "friend", "stats", "showarmorstands", "cleardebugarmorstands", "trash",
"workbench", "anvil", "leashcount", "splock", "tp", "tphere", "tpa",
"tpaccept", "tpdeny", "block", "unblock", "blocklist", "kit", "nick", "ride",
"vanish", "freeze", "lootchests", "tploot", "day", "night", "trade", "tradeaccept",
"report", "showreport", "clearreport", "shop", "spawn", "setwarp", "delwarp",
"warps", "startchallenge", "heal", "claim", "head"
};
for (String name : commands) {
if (getCommand(name) != null) {
getCommand(name).setTabCompleter(completer);
}
}
}
public void logError(String msg, Throwable t) { public void logError(String msg, Throwable t) {
if (getConfig().getBoolean("debug-logging", false) && debugWriter != null) { if (getConfig().getBoolean("debug-logging", false) && debugWriter != null) {
debugWriter.println("[" + new java.util.Date() + "] ERROR: " + msg); debugWriter.println("[" + new java.util.Date() + "] ERROR: " + msg);
@@ -1250,6 +1349,16 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
} }
} }
public void logDebug(String msg) {
if (!getConfig().getBoolean("debug-logging", false)) {
return;
}
if (debugWriter != null) {
debugWriter.println("[" + new java.util.Date() + "] " + msg);
}
getLogger().info(msg);
}
// Komplette Plugin-Logs in console.log // Komplette Plugin-Logs in console.log
public void logConsole(String msg) { public void logConsole(String msg) {
if (getConfig().getBoolean("debug-logging", false) && consoleWriter != null) { if (getConfig().getBoolean("debug-logging", false) && consoleWriter != null) {
@@ -1258,4 +1367,33 @@ private void ensureConfigVersion(FileConfiguration config, File file, String ver
getLogger().info(msg); // Optional: zusätzlich Konsole ausgeben getLogger().info(msg); // Optional: zusätzlich Konsole ausgeben
} }
// --- NEU: HELFERMETHODEN FÜR NEUE FEATURES ---
// Vanish
public void setVanished(UUID uuid, boolean vanished) {
if (vanished) {
vanishedPlayers.put(uuid, true);
} else {
vanishedPlayers.remove(uuid);
}
}
public boolean isVanished(UUID uuid) {
return vanishedPlayers.containsKey(uuid) && vanishedPlayers.get(uuid);
}
public Map<UUID, Boolean> getVanishedPlayers() {
return vanishedPlayers;
}
// Economy
public Economy getEconomy() {
return economy;
}
// ProtocolLib Bridge (für Silent Vanish)
public ProtocolManager getProtocolManager() {
return ProtocolLibrary.getProtocolManager();
}
} }

View File

@@ -0,0 +1,141 @@
package de.viper.survivalplus.commands;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location; // <--- WICHTIG: Das war der Fehler
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class AdminToolsCommand implements CommandExecutor, Listener {
private final SurvivalPlus plugin;
private final Set<UUID> vanishedPlayers = new HashSet<>();
private final Set<UUID> frozenPlayers = new HashSet<>();
public AdminToolsCommand(SurvivalPlus plugin) {
this.plugin = plugin;
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage("Nur Spieler können diesen Befehl nutzen!");
return true;
}
Player player = (Player) sender;
FileConfiguration lang = plugin.getLangConfig();
// --- VANISH ---
if (command.getName().equalsIgnoreCase("vanish")) {
if (!player.hasPermission("survivalplus.vanish")) {
player.sendMessage(ChatColor.RED + "Keine Berechtigung!");
return true;
}
if (vanishedPlayers.contains(player.getUniqueId())) {
// Ent-vanish
for (Player all : Bukkit.getOnlinePlayers()) {
all.showPlayer(plugin, player);
}
player.setPlayerListName(player.getName()); // Name wieder in Tab-Liste anzeigen
vanishedPlayers.remove(player.getUniqueId());
player.sendMessage(ChatColor.GREEN + "Du bist nun sichtbar.");
} else {
// Vanish
for (Player all : Bukkit.getOnlinePlayers()) {
all.hidePlayer(plugin, player);
}
vanishedPlayers.add(player.getUniqueId());
player.sendMessage(ChatColor.GREEN + "Du bist nun unsichtbar.");
}
return true;
}
// --- FREEZE ---
if (command.getName().equalsIgnoreCase("freeze")) {
if (!player.hasPermission("survivalplus.freeze")) {
player.sendMessage(ChatColor.RED + "Keine Berechtigung!");
return true;
}
if (args.length == 0) {
player.sendMessage(ChatColor.RED + "Verwendung: /freeze <Spieler>");
return true;
}
Player target = Bukkit.getPlayer(args[0]);
if (target == null) {
player.sendMessage(ChatColor.RED + "Spieler nicht gefunden!");
return true;
}
if (frozenPlayers.contains(target.getUniqueId())) {
// Ent-freezen
frozenPlayers.remove(target.getUniqueId());
player.sendMessage(ChatColor.GREEN + target.getName() + " wurde aufgetaut.");
target.sendMessage(ChatColor.GREEN + "Du bist nicht mehr eingefroren.");
} else {
// Einfrieren
frozenPlayers.add(target.getUniqueId());
player.sendMessage(ChatColor.GREEN + target.getName() + " wurde eingefroren.");
target.sendMessage(ChatColor.RED + "Du wurdest von einem Admin eingefroren! " + ChatColor.BOLD + "Keine Bewegung möglich!");
}
return true;
}
return false;
}
// --- EVENTS ---
// Verhindert Bewegung wenn eingefroren
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
Player player = event.getPlayer();
// Kopf drehen (YAW/PITCH) ist okay, Blocken wenn Position (X/Y/Z) ändert
if (frozenPlayers.contains(player.getUniqueId())) {
Location from = event.getFrom();
Location to = event.getTo();
// Wenn die Koordinaten sich ändern
if (to.getX() != from.getX() || to.getY() != from.getY() || to.getZ() != from.getZ()) {
event.setCancelled(true);
// Optional: Kleine Meldung beim Versuch zu laufen
// player.sendMessage(ChatColor.RED + "Du bist eingefroren!");
}
}
}
// Wenn ein Spieler joint: Hide vanished players for him
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
Player joiner = event.getPlayer();
for (UUID vanishedUUID : vanishedPlayers) {
Player vanishedPlayer = Bukkit.getPlayer(vanishedUUID);
if (vanishedPlayer != null && vanishedPlayer.isOnline()) {
joiner.hidePlayer(plugin, vanishedPlayer);
}
}
}
// Cleanup wenn ein Spieler geht
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
vanishedPlayers.remove(event.getPlayer().getUniqueId());
frozenPlayers.remove(event.getPlayer().getUniqueId());
}
}

View File

@@ -3,15 +3,19 @@ package de.viper.survivalplus.commands;
import de.viper.survivalplus.SurvivalPlus; import de.viper.survivalplus.SurvivalPlus;
import de.viper.survivalplus.util.Claim; import de.viper.survivalplus.util.Claim;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.UUID; import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
import java.util.UUID;
public class ClaimCommand implements CommandExecutor { public class ClaimCommand implements CommandExecutor {
private SurvivalPlus plugin; private SurvivalPlus plugin;
@@ -71,7 +75,7 @@ public class ClaimCommand implements CommandExecutor {
} }
switch (args[0].toLowerCase()) { switch (args[0].toLowerCase()) {
case "mark": case "mark": {
if (!player.hasPermission("survivalplus.claim.use")) { if (!player.hasPermission("survivalplus.claim.use")) {
player.sendMessage(plugin.getMessage("commands.no-permission")); player.sendMessage(plugin.getMessage("commands.no-permission"));
return true; return true;
@@ -89,22 +93,13 @@ public class ClaimCommand implements CommandExecutor {
player.sendMessage(plugin.getMessage("claim.point2-set").replace("%x%", String.valueOf(location.getBlockX())).replace("%z%", String.valueOf(location.getBlockZ()))); player.sendMessage(plugin.getMessage("claim.point2-set").replace("%x%", String.valueOf(location.getBlockX())).replace("%z%", String.valueOf(location.getBlockZ())));
} }
return true; return true;
}
case "unclaim": case "unclaim":
if (args.length == 1) { case "del":
if (!player.hasPermission("survivalplus.claim.use")) { case "delete": {
player.sendMessage(plugin.getMessage("commands.no-permission")); // 1. Mass Delete (Admin): /claim unclaim <Spieler> (oder del/delete)
return true; if (args.length == 2 && player.isOp()) {
}
Claim claim = plugin.getClaim(player.getLocation());
if (claim == null || !claim.getOwner().equals(player.getUniqueId())) {
player.sendMessage(plugin.getMessage("claim.not-owner"));
return true;
}
plugin.removeClaim(player.getLocation(), player);
player.sendMessage(plugin.getMessage("claim.unclaimed"));
return true;
} else if (args.length == 2 && player.isOp()) {
Player target = Bukkit.getPlayer(args[1]); Player target = Bukkit.getPlayer(args[1]);
UUID targetUUID; UUID targetUUID;
if (target != null) { if (target != null) {
@@ -127,12 +122,38 @@ public class ClaimCommand implements CommandExecutor {
.replace("%player%", args[1])); .replace("%player%", args[1]));
} }
return true; return true;
} else { }
player.sendMessage(plugin.getMessage("claim.usage"));
// 2. Normaler oder Admin Targeted Delete
Claim claim = plugin.getClaim(player.getLocation());
if (claim == null) {
player.sendMessage(plugin.getMessage("claim.no-claim-at-location"));
return true; return true;
} }
case "trust": // Prüfen, ob Spieler Owner ist (oder OP)
if (!claim.getOwner().equals(player.getUniqueId())) {
// NICHT Owner -> Aber Admin?
if (player.isOp()) {
plugin.deleteClaim(claim);
String ownerName = Bukkit.getOfflinePlayer(claim.getOwner()).getName();
if (ownerName == null) ownerName = "Unbekannt";
player.sendMessage(ChatColor.GREEN + "Claim wurde entfernt. (Besitzer war: " + ChatColor.YELLOW + ownerName + ChatColor.GREEN + ")");
return true;
} else {
// Nicht Owner und kein Admin -> Abbruch
player.sendMessage(plugin.getMessage("claim.not-owner"));
return true;
}
}
// Owner löscht eigenen Claim
plugin.deleteClaim(claim);
player.sendMessage(plugin.getMessage("claim.unclaimed"));
return true;
}
case "trust": {
if (!player.hasPermission("survivalplus.claim.trust")) { if (!player.hasPermission("survivalplus.claim.trust")) {
player.sendMessage(plugin.getMessage("commands.no-permission")); player.sendMessage(plugin.getMessage("commands.no-permission"));
return true; return true;
@@ -146,17 +167,18 @@ public class ClaimCommand implements CommandExecutor {
player.sendMessage(plugin.getMessage("commands.player-not-found")); player.sendMessage(plugin.getMessage("commands.player-not-found"));
return true; return true;
} }
Claim claim = plugin.getClaim(player.getLocation()); Claim trustClaim = plugin.getClaim(player.getLocation());
if (claim == null || !claim.getOwner().equals(player.getUniqueId())) { if (trustClaim == null || !trustClaim.getOwner().equals(player.getUniqueId())) {
player.sendMessage(plugin.getMessage("claim.not-owner")); player.sendMessage(plugin.getMessage("claim.not-owner"));
return true; return true;
} }
claim.addTrusted(target.getUniqueId()); trustClaim.addTrusted(target.getUniqueId());
player.sendMessage(plugin.getMessage("claim.trusted").replace("%player%", args[1])); player.sendMessage(plugin.getMessage("claim.trusted").replace("%player%", args[1]));
plugin.saveClaims(); plugin.saveClaims();
break; return true;
}
case "untrust": case "untrust": {
if (!player.hasPermission("survivalplus.claim.trust")) { if (!player.hasPermission("survivalplus.claim.trust")) {
player.sendMessage(plugin.getMessage("commands.no-permission")); player.sendMessage(plugin.getMessage("commands.no-permission"));
return true; return true;
@@ -165,43 +187,145 @@ public class ClaimCommand implements CommandExecutor {
player.sendMessage(plugin.getMessage("claim.usage-untrust")); player.sendMessage(plugin.getMessage("claim.usage-untrust"));
return true; return true;
} }
target = Bukkit.getPlayer(args[1]); Player target = Bukkit.getPlayer(args[1]);
if (target == null) { if (target == null) {
player.sendMessage(plugin.getMessage("commands.player-not-found")); player.sendMessage(plugin.getMessage("commands.player-not-found"));
return true; return true;
} }
claim = plugin.getClaim(player.getLocation()); Claim trustClaim = plugin.getClaim(player.getLocation());
if (claim == null || !claim.getOwner().equals(player.getUniqueId())) { if (trustClaim == null || !trustClaim.getOwner().equals(player.getUniqueId())) {
player.sendMessage(plugin.getMessage("claim.not-owner")); player.sendMessage(plugin.getMessage("claim.not-owner"));
return true; return true;
} }
claim.removeTrusted(target.getUniqueId()); trustClaim.removeTrusted(target.getUniqueId());
player.sendMessage(plugin.getMessage("claim.untrusted").replace("%player%", args[1])); player.sendMessage(plugin.getMessage("claim.untrusted").replace("%player%", args[1]));
plugin.saveClaims(); plugin.saveClaims();
break; return true;
}
case "info": // --- NEU: KICK ---
case "kick": {
if (!player.hasPermission("survivalplus.claim.kick")) {
player.sendMessage(ChatColor.RED + "Keine Berechtigung!");
return true;
}
if (args.length < 2) {
player.sendMessage(ChatColor.RED + "Verwendung: /claim kick <Spieler>");
return true;
}
Player kickTarget = Bukkit.getPlayer(args[1]);
if (kickTarget == null) {
player.sendMessage(ChatColor.RED + "Spieler nicht gefunden oder offline!");
return true;
}
Claim currentClaim = plugin.getClaim(kickTarget.getLocation());
if (currentClaim == null || !currentClaim.getOwner().equals(player.getUniqueId())) {
player.sendMessage(ChatColor.RED + "Du bist hier nicht Besitzer oder kein Claim.");
return true;
}
kickPlayerFromClaim(kickTarget, currentClaim);
player.sendMessage(ChatColor.GREEN + "Spieler " + kickTarget.getName() + " wurde aus dem Claim gekickt!");
kickTarget.sendMessage(ChatColor.RED + "Du wurdest aus dem Claim geworfen!");
return true;
}
// --- NEU: BAN ---
case "ban": {
if (!player.hasPermission("survivalplus.claim.ban")) {
player.sendMessage(ChatColor.RED + "Keine Berechtigung!");
return true;
}
if (args.length < 2) {
player.sendMessage(ChatColor.RED + "Verwendung: /claim ban <Spieler>");
return true;
}
String claimKey = getClaimKeyAtLocation(player.getLocation());
if (claimKey == null) {
player.sendMessage(ChatColor.RED + "Du stehst in keinem Claim.");
return true;
}
Player banTarget = Bukkit.getPlayer(args[1]);
UUID banUUID;
if (banTarget != null) {
banUUID = banTarget.getUniqueId();
Claim banCheck = plugin.getClaim(player.getLocation());
if (banCheck != null && banCheck.isInside(banTarget.getLocation())) {
kickPlayerFromClaim(banTarget, banCheck);
banTarget.sendMessage(ChatColor.RED + "Du wurdest aus dem Claim geworfen und gebannt!");
}
} else {
try {
banUUID = UUID.fromString(args[1]);
} catch (IllegalArgumentException e) {
player.sendMessage(ChatColor.RED + "Ungültige UUID oder Spieler nicht online.");
return true;
}
}
banPlayerInClaim(banUUID, claimKey);
player.sendMessage(ChatColor.GREEN + "Spieler wurde von diesem Claim gebannt.");
return true;
}
// --- NEU: UNBAN ---
case "unban": {
if (!player.hasPermission("survivalplus.claim.ban")) {
player.sendMessage(ChatColor.RED + "Keine Berechtigung!");
return true;
}
if (args.length < 2) {
player.sendMessage(ChatColor.RED + "Verwendung: /claim unban <Spieler>");
return true;
}
Player unbanTarget = Bukkit.getPlayer(args[1]);
UUID unbanUUID;
if (unbanTarget != null) {
unbanUUID = unbanTarget.getUniqueId();
} else {
try {
unbanUUID = UUID.fromString(args[1]);
} catch (IllegalArgumentException e) {
player.sendMessage(ChatColor.RED + "Ungültige UUID.");
return true;
}
}
String claimKeyUnban = getClaimKeyAtLocation(player.getLocation());
if (claimKeyUnban == null) {
player.sendMessage(ChatColor.RED + "Du stehst in keinem Claim.");
return true;
}
unbanPlayerInClaim(unbanUUID, claimKeyUnban);
player.sendMessage(ChatColor.GREEN + "Spieler wurde für diesen Claim entbannt.");
return true;
}
case "info": {
if (!player.hasPermission("survivalplus.claim.use")) { if (!player.hasPermission("survivalplus.claim.use")) {
player.sendMessage(plugin.getMessage("commands.no-permission")); player.sendMessage(plugin.getMessage("commands.no-permission"));
return true; return true;
} }
claim = plugin.getClaim(player.getLocation()); Claim infoClaim = plugin.getClaim(player.getLocation());
if (claim == null) { if (infoClaim == null) {
player.sendMessage(plugin.getMessage("claim.no-claim-at-location")); player.sendMessage(plugin.getMessage("claim.no-claim-at-location"));
return true; return true;
} }
String ownerName = Bukkit.getOfflinePlayer(claim.getOwner()).getName(); String ownerName = Bukkit.getOfflinePlayer(infoClaim.getOwner()).getName();
if (ownerName == null) { if (ownerName == null) {
ownerName = claim.getOwner().toString(); // Fallback to UUID if name not found ownerName = infoClaim.getOwner().toString();
} }
player.sendMessage(plugin.getMessage("claim.info") player.sendMessage(plugin.getMessage("claim.info")
.replace("%owner%", ownerName) .replace("%owner%", ownerName)
.replace("%world%", claim.getWorldName()) .replace("%world%", infoClaim.getWorldName())
.replace("%x1%", String.valueOf(claim.getX1())) .replace("%x1%", String.valueOf(infoClaim.getX1()))
.replace("%z1%", String.valueOf(claim.getZ1())) .replace("%z1%", String.valueOf(infoClaim.getZ1()))
.replace("%x2%", String.valueOf(claim.getX2())) .replace("%x2%", String.valueOf(infoClaim.getX2()))
.replace("%z2%", String.valueOf(claim.getZ2()))); .replace("%z2%", String.valueOf(infoClaim.getZ2())));
return true; return true;
}
default: default:
player.sendMessage(plugin.getMessage("claim.usage")); player.sendMessage(plugin.getMessage("claim.usage"));
@@ -223,4 +347,62 @@ public class ClaimCommand implements CommandExecutor {
} }
return removedCount; return removedCount;
} }
// --- HILFSMETHODEN ---
private void kickPlayerFromClaim(Player player, Claim claim) {
Location loc = player.getLocation();
int x = loc.getBlockX();
int z = loc.getBlockZ();
int minX = Math.min(claim.getX1(), claim.getX2());
int maxX = Math.max(claim.getX1(), claim.getX2());
int minZ = Math.min(claim.getZ1(), claim.getZ2());
int maxZ = Math.max(claim.getZ1(), claim.getZ2());
int distX1 = Math.abs(x - minX);
int distX2 = Math.abs(x - maxX);
int distZ1 = Math.abs(z - minZ);
int distZ2 = Math.abs(z - maxZ);
int minDist = Math.min(Math.min(distX1, distX2), Math.min(distZ1, distZ2));
Location newLoc = loc.clone();
if (minDist == distX1) {
newLoc.setX(minX - 1);
} else if (minDist == distX2) {
newLoc.setX(maxX + 1);
} else if (minDist == distZ1) {
newLoc.setZ(minZ - 1);
} else {
newLoc.setZ(maxZ + 1);
}
newLoc.setY(newLoc.getY() + 1);
player.teleport(newLoc);
}
private String getClaimKeyAtLocation(Location loc) {
for (String key : plugin.getClaims().keySet()) {
if (plugin.getClaims().get(key).isInside(loc)) {
return key;
}
}
return null;
}
private void banPlayerInClaim(UUID uuid, String claimKey) {
FileConfiguration config = plugin.getClaimsConfig();
List<String> banned = config.getStringList(claimKey + ".banned");
if (!banned.contains(uuid.toString())) {
banned.add(uuid.toString());
config.set(claimKey + ".banned", banned);
plugin.saveClaims();
}
}
private void unbanPlayerInClaim(UUID uuid, String claimKey) {
FileConfiguration config = plugin.getClaimsConfig();
List<String> banned = config.getStringList(claimKey + ".banned");
banned.remove(uuid.toString());
config.set(claimKey + ".banned", banned);
plugin.saveClaims();
}
} }

View File

@@ -0,0 +1,58 @@
package de.viper.survivalplus.commands;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
public class HeadCommand implements CommandExecutor {
private SurvivalPlus plugin;
public HeadCommand(SurvivalPlus plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage("Nur Spieler können diesen Befehl nutzen.");
return true;
}
Player player = (Player) sender;
if (args.length == 0) {
player.sendMessage(plugin.getMessage("commands.player-only"));
return true;
}
if (!player.hasPermission("survivalplus.head")) {
player.sendMessage(ChatColor.RED + "Keine Berechtigung!");
return true;
}
String targetName = args[0];
OfflinePlayer target = Bukkit.getOfflinePlayer(targetName);
if (target == null) {
player.sendMessage(ChatColor.RED + "Spieler '" + targetName + "' wurde nie gefunden.");
return true;
}
ItemStack head = new ItemStack(Material.PLAYER_HEAD);
SkullMeta meta = (SkullMeta) head.getItemMeta();
meta.setOwningPlayer(target);
meta.setDisplayName(ChatColor.YELLOW + "Kopf von " + targetName);
head.setItemMeta(meta);
player.getInventory().addItem(head);
player.sendMessage(ChatColor.GREEN + "Du hast den Kopf von " + targetName + " erhalten!");
return true;
}
}

View File

@@ -30,20 +30,32 @@ public class PluginCommand implements CommandExecutor {
// /sp oder /sp help // /sp oder /sp help
if (args.length == 0 || args[0].equalsIgnoreCase("help")) { if (args.length == 0 || args[0].equalsIgnoreCase("help")) {
String mode = "all";
int page = 1; int page = 1;
if (args.length > 1 && args[0].equalsIgnoreCase("help")) { if (args.length > 1 && args[0].equalsIgnoreCase("help")) {
try { String arg1 = args[1];
page = Integer.parseInt(args[1]); if (isInteger(arg1)) {
if (page < 1) page = 1; page = Integer.parseInt(arg1);
} catch (NumberFormatException e) { } else {
sender.sendMessage(color(lang.getString( mode = arg1.toLowerCase();
"sp.invalid-subcommand", if (args.length > 2) {
"§cUngültiger Unterbefehl! Verwendung: /sp [reload|help|info|share]" String arg2 = args[2];
))); if (isInteger(arg2)) {
return true; page = Integer.parseInt(arg2);
} else {
sender.sendMessage(color(lang.getString(
"sp.invalid-subcommand",
"§cUngültiger Unterbefehl! Verwendung: /sp help [player|admin|system|all] [seite]"
)));
return true;
}
}
} }
} }
showHelp(sender, page);
if (page < 1) page = 1;
showHelp(sender, mode, page);
return true; return true;
} }
@@ -175,7 +187,7 @@ public class PluginCommand implements CommandExecutor {
return true; return true;
} }
private void showHelp(CommandSender sender, int page) { private void showHelp(CommandSender sender, String mode, int page) {
FileConfiguration help = plugin.getHelpConfig(); FileConfiguration help = plugin.getHelpConfig();
FileConfiguration lang = plugin.getLangConfig(); FileConfiguration lang = plugin.getLangConfig();
@@ -184,7 +196,30 @@ public class PluginCommand implements CommandExecutor {
return; return;
} }
List<String> commands = new ArrayList<>(help.getConfigurationSection("commands").getKeys(false)); List<String> orderedCommands = new ArrayList<>(help.getConfigurationSection("commands").getKeys(false));
if (orderedCommands.isEmpty()) {
sender.sendMessage(color(lang.getString("sp.help-not-found", "&cKeine Befehle verfügbar.")));
return;
}
String normalizedMode = mode == null ? "all" : mode.toLowerCase();
if (!normalizedMode.equals("all") && !normalizedMode.equals("player")
&& !normalizedMode.equals("admin") && !normalizedMode.equals("system")) {
sender.sendMessage(color(help.getString(
"help-usage",
"&cVerwendung: /sp help [player|admin|system|all] [seite]"
)));
return;
}
List<String> commands = new ArrayList<>();
for (String key : orderedCommands) {
String group = help.getString("commands." + key + ".group", "player").toLowerCase();
if (normalizedMode.equals("all") || group.equals(normalizedMode)) {
commands.add(key);
}
}
if (commands.isEmpty()) { if (commands.isEmpty()) {
sender.sendMessage(color(lang.getString("sp.help-not-found", "&cKeine Befehle verfügbar."))); sender.sendMessage(color(lang.getString("sp.help-not-found", "&cKeine Befehle verfügbar.")));
return; return;
@@ -199,19 +234,30 @@ public class PluginCommand implements CommandExecutor {
int startIndex = (page - 1) * COMMANDS_PER_PAGE; int startIndex = (page - 1) * COMMANDS_PER_PAGE;
int endIndex = Math.min(startIndex + COMMANDS_PER_PAGE, commands.size()); int endIndex = Math.min(startIndex + COMMANDS_PER_PAGE, commands.size());
String lastGroup = null;
for (int i = startIndex; i < endIndex; i++) { for (int i = startIndex; i < endIndex; i++) {
String cmd = commands.get(i); String cmd = commands.get(i);
String group = help.getString("commands." + cmd + ".group", "player").toLowerCase();
if (lastGroup == null || !lastGroup.equals(group)) {
String groupTitle = help.getString("groups." + group + ".title", "&6-- " + group + " --");
sender.sendMessage(color(groupTitle));
lastGroup = group;
}
String usage = help.getString("commands." + cmd + ".usage", ""); String usage = help.getString("commands." + cmd + ".usage", "");
String description = help.getString("commands." + cmd + ".description", ""); String description = help.getString("commands." + cmd + ".description", "");
sender.sendMessage(color(usage + " §7- " + description)); sender.sendMessage(color(usage + " §7- " + description));
} }
if (sender instanceof Player player) { if (sender instanceof Player player) {
String modeSuffix = normalizedMode.equals("all") ? "" : " " + normalizedMode;
String navBase = "/sp help" + modeSuffix + " ";
TextComponent navigation = new TextComponent(); TextComponent navigation = new TextComponent();
if (page > 1) { if (page > 1) {
TextComponent prev = new TextComponent(color(help.getString("navigation.prev", "« Vorherige "))); TextComponent prev = new TextComponent(color(help.getString("navigation.prev", "« Vorherige ")));
prev.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/sp help " + (page - 1))); prev.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, navBase + (page - 1)));
prev.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
new ComponentBuilder("§7Klicke für Seite " + (page - 1)).create()));
navigation.addExtra(prev); navigation.addExtra(prev);
} else { } else {
navigation.addExtra(color(help.getString("navigation.prev-disabled", "« "))); navigation.addExtra(color(help.getString("navigation.prev-disabled", "« ")));
@@ -224,7 +270,9 @@ public class PluginCommand implements CommandExecutor {
if (page < totalPages) { if (page < totalPages) {
TextComponent next = new TextComponent(color(help.getString("navigation.next", " Nächste »"))); TextComponent next = new TextComponent(color(help.getString("navigation.next", " Nächste »")));
next.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/sp help " + (page + 1))); next.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, navBase + (page + 1)));
next.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
new ComponentBuilder("§7Klicke für Seite " + (page + 1)).create()));
navigation.addExtra(next); navigation.addExtra(next);
} else { } else {
navigation.addExtra(color(help.getString("navigation.next-disabled", " »"))); navigation.addExtra(color(help.getString("navigation.next-disabled", " »")));
@@ -242,6 +290,18 @@ public class PluginCommand implements CommandExecutor {
sender.sendMessage(color(help.getString("footer", "&7=========================="))); sender.sendMessage(color(help.getString("footer", "&7==========================")));
} }
private boolean isInteger(String value) {
if (value == null || value.isEmpty()) {
return false;
}
for (int i = 0; i < value.length(); i++) {
if (!Character.isDigit(value.charAt(i))) {
return false;
}
}
return true;
}
private String color(String input) { private String color(String input) {
return ChatColor.translateAlternateColorCodes('&', input == null ? "" : input); return ChatColor.translateAlternateColorCodes('&', input == null ? "" : input);
} }

View File

@@ -2,7 +2,7 @@ package de.viper.survivalplus.commands;
import de.viper.survivalplus.Manager.ShopManager; import de.viper.survivalplus.Manager.ShopManager;
import de.viper.survivalplus.SurvivalPlus; import de.viper.survivalplus.SurvivalPlus;
import de.viper.survivalplus.gui.ShopGui; // <--- WICHTIG: Dieser Import fehlte dir import de.viper.survivalplus.gui.ShopGui;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.Material; import org.bukkit.Material;
@@ -11,15 +11,33 @@ import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.plugin.RegisteredServiceProvider;
import net.milkbowl.vault.economy.Economy;
import net.milkbowl.vault.economy.EconomyResponse;
public class ShopCommand implements CommandExecutor { public class ShopCommand implements CommandExecutor {
private final ShopManager shopManager; private final ShopManager shopManager;
private final SurvivalPlus plugin; private final SurvivalPlus plugin;
private Economy economy;
public ShopCommand(SurvivalPlus plugin) { public ShopCommand(SurvivalPlus plugin) {
this.plugin = plugin; this.plugin = plugin;
this.shopManager = new ShopManager(plugin); this.shopManager = new ShopManager(plugin);
// Economy laden (für Admin-Verkauf an Server)
try {
if (Bukkit.getPluginManager().isPluginEnabled("Vault")) {
RegisteredServiceProvider<Economy> rsp = Bukkit.getServicesManager().getRegistration(Economy.class);
if (rsp != null) {
this.economy = rsp.getProvider();
plugin.getLogger().info("Vault Economy für ShopCommand gefunden.");
}
}
} catch (Exception e) {
// Ignorieren wenn Vault fehlt
}
} }
private String getMessage(String key) { private String getMessage(String key) {
@@ -35,78 +53,102 @@ public class ShopCommand implements CommandExecutor {
Player player = (Player) sender; Player player = (Player) sender;
// Shop-GUI öffnen // Shop-GUI öffnen
if (args.length == 0 || (args.length > 0 && args[0].toLowerCase().equals("gui"))) { if (args.length == 0 || (args.length > 0 && args[0].equalsIgnoreCase("gui"))) {
ShopGui shopGui = new ShopGui(plugin, player, shopManager); ShopGui shopGui = new ShopGui(plugin, player, shopManager);
Bukkit.getPluginManager().registerEvents(shopGui, plugin); Bukkit.getPluginManager().registerEvents(shopGui, plugin);
player.openInventory(shopGui.getInventory()); player.openInventory(shopGui.getInventory());
return true; return true;
} }
if (!args[0].toLowerCase().equals("add")) { // --- SHOP ADD LOGIK ---
sender.sendMessage(getMessage("unknown-subcommand")); if (args.length >= 4 && args[0].equalsIgnoreCase("add")) {
return true;
} // Economy-Check
if (economy == null) {
sender.sendMessage(ChatColor.RED + "Vault Economy ist nicht verfügbar!");
return true;
}
// --- Neue Logik für /shop add ---
String itemKey;
double basePrice;
int stock;
// Fall 1: /shop add <Material> <Preis> <Stock> (4 Argumente)
if (args.length >= 4) {
String materialName = args[1]; String materialName = args[1];
String priceStr = args[2];
String amountStr = args[3];
Material mat = Material.matchMaterial(materialName); Material mat = Material.matchMaterial(materialName);
if (mat == null) { if (mat == null) {
sender.sendMessage(ChatColor.RED + "Das Material '" + materialName + "' wurde nicht gefunden!"); sender.sendMessage(ChatColor.RED + "Das Material '" + materialName + "' wurde nicht gefunden!");
return true; return true;
} }
itemKey = mat.name().toLowerCase();
double price;
int amount;
try { try {
basePrice = Double.parseDouble(args[2]); price = Double.parseDouble(priceStr);
stock = Integer.parseInt(args[3]); amount = Integer.parseInt(amountStr);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
sender.sendMessage(ChatColor.RED + "Ungültige Preis oder Bestandszahl! Beispiel: /shop add Diamond 20 64"); sender.sendMessage(ChatColor.RED + "Ungültige Preis oder Bestandszahl! Beispiel: /shop add Diamond 20 64");
return true; return true;
} }
} if (amount <= 0 || price < 0) {
// Fall 2: /shop add <Preis> <Stock> (3 Argumente) -> Nimmt Item in der Hand sender.sendMessage(ChatColor.RED + "Menge und Preis müssen positiv sein.");
else if (args.length == 3) {
ItemStack itemInHand = player.getInventory().getItemInMainHand();
if (itemInHand == null || itemInHand.getType() == Material.AIR) {
sender.sendMessage(ChatColor.RED + "Du musst das Item, das du hinzufügen willst, in der Hand halten!");
sender.sendMessage(ChatColor.GRAY + "Oder benutze: /shop add <Material> <Preis> <Bestand>");
return true; return true;
} }
itemKey = itemInHand.getType().name().toLowerCase();
try { // 1. Prüfen: Hat der Spieler die Items?
basePrice = Double.parseDouble(args[1]); int hasAmount = countItems(player.getInventory(), mat);
stock = Integer.parseInt(args[2]);
} catch (NumberFormatException e) { if (hasAmount < amount) {
sender.sendMessage(getMessage("number-error")); sender.sendMessage(ChatColor.RED + "Du hast nicht genug Items! (Benötigt: " + amount + ", Vorhanden: " + hasAmount + ")");
return true; return true;
} }
}
// Falsche Anzahl an Argumenten // 2. Items aus Inventar entfernen
else { player.getInventory().removeItem(new ItemStack(mat, amount));
sender.sendMessage(ChatColor.RED + "Falsche Benutzung!"); player.updateInventory(); // Wichtig für Client-Update
sender.sendMessage(ChatColor.GRAY + "Mit Item in Hand: /shop add <Preis> <Bestand>");
sender.sendMessage(ChatColor.GRAY + "Mit Namen: /shop add <Material> <Preis> <Bestand>"); // 3. Spieler Geld geben (Verkauf an Server)
double totalCost = price * amount;
EconomyResponse response = economy.depositPlayer(player, totalCost);
if (response.transactionSuccess()) {
sender.sendMessage(ChatColor.GREEN + "Du hast " + amount + " " + mat.name() + " für " + totalCost + " Coins an den Server verkauft!");
// 4. Shop-Config aktualisieren (Lagerbestand erhöhen)
String itemKey = mat.name().toLowerCase();
shopManager.addOrUpdateItem(itemKey, price, shopManager.getStock(itemKey) + amount);
} else {
sender.sendMessage(ChatColor.RED + "Fehler bei der Auszahlung!");
return true;
}
return true; return true;
}
// Fallback für falsche Argumente
if (args.length > 0 && args[0].equalsIgnoreCase("add")) {
sender.sendMessage(ChatColor.RED + "Falsche Benutzung!");
sender.sendMessage(ChatColor.GRAY + "Mit Item in Hand: /shop add <Preis> <Bestand>");
sender.sendMessage(ChatColor.GRAY + "Mit Namen: /shop add <Material> <Preis> <Bestand>");
return true;
} }
shopManager.addOrUpdateItem(itemKey, basePrice, stock); sender.sendMessage(ChatColor.RED + "Unbekannter Befehl! Benutze /shop [add|gui]");
String msg = getMessage("item-added")
.replace("{item}", itemKey)
.replace("{price}", String.valueOf(basePrice))
.replace("{stock}", String.valueOf(stock));
sender.sendMessage(msg);
return true; return true;
} }
/**
* Zählt die Anzahl der Items eines bestimmten Materials im Inventar.
* Nutzt StorageContents, um auch Rüstung/Shulker-Slots zu prüfen.
*/
private int countItems(PlayerInventory inv, Material mat) {
int count = 0;
for (ItemStack item : inv.getStorageContents()) {
if (item != null && item.getType().equals(mat)) {
count += item.getAmount();
}
}
return count;
}
} }

View File

@@ -0,0 +1,369 @@
package de.viper.survivalplus.commands;
import de.viper.survivalplus.SurvivalPlus;
import de.viper.survivalplus.Manager.Warp;
import de.viper.survivalplus.Manager.WarpManager;
import de.viper.survivalplus.fun.FunChallenge;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;
public class SurvivalPlusTabCompleter implements TabCompleter {
private static final List<String> SP_SUBCOMMANDS = List.of(
"help", "reload", "info", "share", "shareconfirm", "sharecancel", "cb",
"lock", "unlock", "friendadd", "friendremove"
);
private static final List<String> SP_CB_SUBCOMMANDS = List.of("add", "remove", "list");
private static final List<String> FRIEND_SUBCOMMANDS = List.of("add", "accept", "deny", "list", "del", "confirm", "tp");
private static final List<String> CLAIM_SUBCOMMANDS = List.of("mark", "unclaim", "del", "delete", "trust", "untrust", "info", "kick", "ban", "unban");
private static final List<String> SPLOCK_SUBCOMMANDS = List.of("lock", "unlock", "friendadd", "friendremove");
private static final List<String> SHOP_SUBCOMMANDS = List.of("gui", "add");
private static final List<String> NICK_SUBCOMMANDS = List.of("off", "remove", "reset");
private static final List<String> GAMEMODE_SUBCOMMANDS = List.of("0", "1", "2", "3", "survival", "creative", "adventure", "spectator");
private final SurvivalPlus plugin;
public SurvivalPlusTabCompleter(SurvivalPlus plugin) {
this.plugin = plugin;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
String cmd = command.getName().toLowerCase(Locale.ROOT);
if (args.length == 0) {
return Collections.emptyList();
}
switch (cmd) {
case "sp":
return completeSp(sender, args);
case "friend":
return completeFriend(sender, args);
case "claim":
return completeClaim(sender, args);
case "splock":
return completeSplock(sender, args);
case "shop":
return completeShop(sender, args);
case "nick":
return filterPrefix(NICK_SUBCOMMANDS, args[0]);
case "gm":
return completeGamemode(sender, args);
case "tp":
case "tphere":
case "tpa":
case "trade":
case "tradeaccept":
case "report":
case "showreport":
case "clearreport":
case "block":
case "unblock":
case "head":
case "freeze":
return completePlayerArg(sender, args);
case "heal":
case "inv":
case "ec":
case "ride":
return completeOptionalPlayerArg(sender, args);
case "tploot":
return completeTploot(args);
case "startchallenge":
return completeChallenges(args);
case "delwarp":
return completePlayerWarpNames(sender, args);
case "home":
case "delhome":
return completePlayerHomeNames(sender, args);
default:
return Collections.emptyList();
}
}
private List<String> completeSp(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(SP_SUBCOMMANDS, args[0]);
}
if (args.length == 2 && args[0].equalsIgnoreCase("cb")) {
return filterPrefix(SP_CB_SUBCOMMANDS, args[1]);
}
if (args.length == 3 && args[0].equalsIgnoreCase("cb") && args[1].equalsIgnoreCase("remove")) {
return filterPrefix(getBlockedCommands(), args[2]);
}
if (args.length == 2 && (args[0].equalsIgnoreCase("friendadd") || args[0].equalsIgnoreCase("friendremove"))) {
return filterPrefix(getOnlinePlayerNames(sender), args[1]);
}
return Collections.emptyList();
}
private List<String> completeFriend(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(FRIEND_SUBCOMMANDS, args[0]);
}
if (args.length == 2) {
String sub = args[0].toLowerCase(Locale.ROOT);
if (sub.equals("add") || sub.equals("accept") || sub.equals("deny") || sub.equals("tp")) {
return filterPrefix(getOnlinePlayerNames(sender), args[1]);
}
if (sub.equals("del") || sub.equals("confirm")) {
return filterPrefix(getFriendNames(sender), args[1]);
}
}
return Collections.emptyList();
}
private List<String> completeClaim(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(CLAIM_SUBCOMMANDS, args[0]);
}
if (args.length == 2) {
String sub = args[0].toLowerCase(Locale.ROOT);
if (sub.equals("mark")) {
return filterPrefix(List.of("1", "2"), args[1]);
}
if (sub.equals("trust") || sub.equals("untrust") || sub.equals("kick") || sub.equals("ban") || sub.equals("unban")
|| sub.equals("unclaim") || sub.equals("del") || sub.equals("delete")) {
return filterPrefix(getOnlinePlayerNames(sender), args[1]);
}
}
return Collections.emptyList();
}
private List<String> completeSplock(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(SPLOCK_SUBCOMMANDS, args[0]);
}
if (args.length == 2) {
String sub = args[0].toLowerCase(Locale.ROOT);
if (sub.equals("friendadd") || sub.equals("friendremove")) {
return filterPrefix(getOnlinePlayerNames(sender), args[1]);
}
}
return Collections.emptyList();
}
private List<String> completeShop(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(SHOP_SUBCOMMANDS, args[0]);
}
if (args.length == 2 && args[0].equalsIgnoreCase("add")) {
String prefix = args[1];
if (prefix.length() < 2) {
return Collections.emptyList();
}
return getMaterialNames(prefix);
}
return Collections.emptyList();
}
private List<String> completeGamemode(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(GAMEMODE_SUBCOMMANDS, args[0]);
}
if (args.length == 2) {
return filterPrefix(getOnlinePlayerNames(sender), args[1]);
}
return Collections.emptyList();
}
private List<String> completePlayerArg(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(getOnlinePlayerNames(sender), args[0]);
}
return Collections.emptyList();
}
private List<String> completeOptionalPlayerArg(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(getOnlinePlayerNames(sender), args[0]);
}
return Collections.emptyList();
}
private List<String> completeTploot(String[] args) {
if (args.length == 1) {
return filterPrefix(getWorldNames(), args[0]);
}
return Collections.emptyList();
}
private List<String> completeChallenges(String[] args) {
if (args.length == 1) {
return filterPrefix(getChallengeNames(), args[0]);
}
return Collections.emptyList();
}
private List<String> completePlayerHomeNames(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(getHomeNames(sender), args[0]);
}
return Collections.emptyList();
}
private List<String> completePlayerWarpNames(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(getWarpNames(sender), args[0]);
}
return Collections.emptyList();
}
private List<String> getOnlinePlayerNames(CommandSender sender) {
List<String> names = new ArrayList<>();
for (Player player : Bukkit.getOnlinePlayers()) {
if (!player.getName().equalsIgnoreCase(sender.getName())) {
names.add(player.getName());
}
}
return names;
}
private List<String> getBlockedCommands() {
FileConfiguration blocked = plugin.getBlockedCommandsConfig();
if (blocked == null) {
return Collections.emptyList();
}
List<String> commands = blocked.getStringList("blocked-commands");
return commands == null ? Collections.emptyList() : commands;
}
private List<String> getFriendNames(CommandSender sender) {
if (!(sender instanceof Player player)) {
return Collections.emptyList();
}
FileConfiguration friends = plugin.getFriendsConfig();
if (friends == null) {
return Collections.emptyList();
}
List<String> friendIds = friends.getStringList(player.getUniqueId().toString() + ".friends");
if (friendIds == null || friendIds.isEmpty()) {
return Collections.emptyList();
}
List<String> names = new ArrayList<>();
for (String id : friendIds) {
try {
UUID uuid = UUID.fromString(id);
OfflinePlayer offline = Bukkit.getOfflinePlayer(uuid);
if (offline.getName() != null) {
names.add(offline.getName());
continue;
}
String storedName = friends.getString(id + ".name");
if (storedName != null && !storedName.isEmpty()) {
names.add(storedName);
}
} catch (IllegalArgumentException ignored) {
}
}
return names;
}
private List<String> getHomeNames(CommandSender sender) {
if (!(sender instanceof Player player)) {
return Collections.emptyList();
}
FileConfiguration homes = plugin.getHomesConfig();
if (homes == null) {
return Collections.emptyList();
}
String path = "homes." + player.getUniqueId();
if (homes.getConfigurationSection(path) == null) {
return Collections.emptyList();
}
return new ArrayList<>(homes.getConfigurationSection(path).getKeys(false));
}
private List<String> getWarpNames(CommandSender sender) {
if (!(sender instanceof Player player)) {
return Collections.emptyList();
}
WarpManager warpManager = plugin.getWarpManager();
if (warpManager == null) {
return Collections.emptyList();
}
List<String> warps = new ArrayList<>();
for (Warp warp : warpManager.getWarps().values()) {
if (warp.getOwner().equalsIgnoreCase(player.getName())) {
warps.add(warp.getName());
}
}
return warps;
}
private List<String> getChallengeNames() {
if (plugin.getFunChallengeManager() == null) {
return Collections.emptyList();
}
List<String> names = new ArrayList<>();
for (FunChallenge challenge : plugin.getFunChallengeManager().getChallenges()) {
names.add(challenge.getName());
}
return names;
}
private List<String> getWorldNames() {
List<String> names = new ArrayList<>();
for (World world : Bukkit.getWorlds()) {
names.add(world.getName());
}
return names;
}
private List<String> getMaterialNames(String prefix) {
List<String> names = new ArrayList<>();
String normalized = prefix == null ? "" : prefix.toUpperCase(Locale.ROOT);
for (Material material : Material.values()) {
if (!material.isItem() || material == Material.AIR) {
continue;
}
String name = material.name();
if (name.startsWith(normalized)) {
names.add(name);
}
}
Collections.sort(names);
if (names.size() > 25) {
return new ArrayList<>(names.subList(0, 25));
}
return names;
}
private List<String> filterPrefix(Collection<String> options, String prefix) {
if (options == null || options.isEmpty()) {
return Collections.emptyList();
}
String normalized = prefix == null ? "" : prefix.toLowerCase(Locale.ROOT);
List<String> results = new ArrayList<>();
Set<String> dedup = new HashSet<>();
for (String option : options) {
if (option == null) {
continue;
}
String value = option.toString();
if (value.toLowerCase(Locale.ROOT).startsWith(normalized)) {
if (dedup.add(value)) {
results.add(value);
}
}
}
Collections.sort(results);
return results;
}
}

View File

@@ -2,7 +2,9 @@ package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus; import de.viper.survivalplus.SurvivalPlus;
import de.viper.survivalplus.util.Claim; import de.viper.survivalplus.util.Claim;
import org.bukkit.ChatColor;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Monster; import org.bukkit.entity.Monster;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@@ -51,6 +53,25 @@ public class ClaimListener implements Listener {
Player player = event.getPlayer(); Player player = event.getPlayer();
UUID playerId = player.getUniqueId(); UUID playerId = player.getUniqueId();
Claim currentClaim = plugin.getClaim(event.getTo()); Claim currentClaim = plugin.getClaim(event.getTo());
Location to = event.getTo();
// --- NEU: BAN CHECK ---
if (currentClaim != null) {
// Checken ob Spieler in der Ban-Liste der Claims Config steht
String claimKey = getClaimKeyFromLocation(to);
if (claimKey != null) {
FileConfiguration config = plugin.getClaimsConfig();
if (config.contains(claimKey + ".banned")) {
if (config.getStringList(claimKey + ".banned").contains(playerId.toString())) {
event.setCancelled(true);
player.sendMessage(ChatColor.RED + "Du bist aus diesem Gebiet verbannt!");
// Optional: Knockback effect
player.teleport(event.getFrom().add(0, 0.2, 0));
return;
}
}
}
}
// Prüfe, ob sich der Claim-Status geändert hat // Prüfe, ob sich der Claim-Status geändert hat
Claim previousClaim = lastClaim.getOrDefault(playerId, null); Claim previousClaim = lastClaim.getOrDefault(playerId, null);
@@ -97,4 +118,15 @@ public class ClaimListener implements Listener {
} }
} }
} }
// Helper für ClaimListener (Kopiert, da wir die Methode in Command nicht sehen können)
private String getClaimKeyFromLocation(Location loc) {
// Da wir hier keinen direkten Zugriff auf den Key haben, iterieren wir kurz
for (String key : plugin.getClaims().keySet()) {
if (plugin.getClaims().get(key).isInside(loc)) {
return key;
}
}
return null;
}
} }

View File

@@ -0,0 +1,36 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
public class HeadDropListener implements Listener {
private SurvivalPlus plugin;
public HeadDropListener(SurvivalPlus plugin) {
this.plugin = plugin;
}
@EventHandler
public void onPlayerDeath(PlayerDeathEvent event) {
if (!plugin.getConfig().getBoolean("heads.drop-on-death", true)) {
return;
}
Player player = event.getEntity();
ItemStack head = new ItemStack(Material.PLAYER_HEAD);
SkullMeta meta = (SkullMeta) head.getItemMeta();
meta.setOwningPlayer(player);
meta.setDisplayName(ChatColor.YELLOW + "Kopf von " + player.getName());
head.setItemMeta(meta);
event.getDrops().add(head);
}
}

View File

@@ -32,7 +32,7 @@ public class MobCapListener implements Listener {
public void reloadConfig(FileConfiguration config) { public void reloadConfig(FileConfiguration config) {
this.enabled = config.getBoolean("mob-cap.enabled", true); this.enabled = config.getBoolean("mob-cap.enabled", true);
this.maxAnimalsPerChunk = config.getInt("mob-cap.max-animals-per-chunk", 10); this.maxAnimalsPerChunk = config.getInt("mob-cap.max-animals-per-chunk", 10);
plugin.getLogger().info("MobCapListener: enabled=" + enabled + ", maxAnimalsPerChunk=" + maxAnimalsPerChunk); plugin.logDebug("MobCapListener: enabled=" + enabled + ", maxAnimalsPerChunk=" + maxAnimalsPerChunk);
// Bereinige bestehende Daten // Bereinige bestehende Daten
chunkAnimalMap.clear(); chunkAnimalMap.clear();
@@ -40,7 +40,7 @@ public class MobCapListener implements Listener {
if (!enabled) { if (!enabled) {
plugin.getMobCapConfig().set("mobcap", null); // Bereinige mobcap.yml plugin.getMobCapConfig().set("mobcap", null); // Bereinige mobcap.yml
plugin.saveMobCapConfig(); plugin.saveMobCapConfig();
plugin.getLogger().info("MobCapListener: Daten bereinigt, da enabled=false"); plugin.logDebug("MobCapListener: Daten bereinigt, da enabled=false");
return; return;
} }
@@ -89,7 +89,7 @@ public class MobCapListener implements Listener {
// Speichere aktualisierte mobcap.yml // Speichere aktualisierte mobcap.yml
saveChunkAnimalMap(); saveChunkAnimalMap();
plugin.getLogger().info("MobCapListener: Nach Reload " + chunkAnimalMap.size() + " Chunks mit Tieren gefunden."); plugin.logDebug("MobCapListener: Nach Reload " + chunkAnimalMap.size() + " Chunks mit Tieren gefunden.");
} }
@EventHandler @EventHandler
@@ -106,7 +106,7 @@ public class MobCapListener implements Listener {
int animalCount = animalToPlayer.size(); int animalCount = animalToPlayer.size();
if (animalCount >= maxAnimalsPerChunk) { if (animalCount >= maxAnimalsPerChunk) {
event.setCancelled(true); event.setCancelled(true);
plugin.getLogger().info("Spawn von " + entity.getType() + " in Chunk " + chunkKey + " verhindert: Limit von " + maxAnimalsPerChunk + " erreicht."); plugin.logDebug("Spawn von " + entity.getType() + " in Chunk " + chunkKey + " verhindert: Limit von " + maxAnimalsPerChunk + " erreicht.");
// HINWEIS: Wenn du dem Züchter eine Nachricht schicken willst, nutze EntityBreedEvent // HINWEIS: Wenn du dem Züchter eine Nachricht schicken willst, nutze EntityBreedEvent
// Hier ist das nicht möglich, weil man beim Spawn keinen Spielerzugriff hat // Hier ist das nicht möglich, weil man beim Spawn keinen Spielerzugriff hat
@@ -132,7 +132,7 @@ public class MobCapListener implements Listener {
if (animalCount >= maxAnimalsPerChunk) { if (animalCount >= maxAnimalsPerChunk) {
event.setCancelled(true); event.setCancelled(true);
player.sendMessage(plugin.getMessage("mob-cap.limit-reached").replace("%max%", String.valueOf(maxAnimalsPerChunk))); player.sendMessage(plugin.getMessage("mob-cap.limit-reached").replace("%max%", String.valueOf(maxAnimalsPerChunk)));
plugin.getLogger().info("Interaktion mit " + entity.getType() + " in Chunk " + chunkKey + " verhindert: Limit von " + maxAnimalsPerChunk + " erreicht."); plugin.logDebug("Interaktion mit " + entity.getType() + " in Chunk " + chunkKey + " verhindert: Limit von " + maxAnimalsPerChunk + " erreicht.");
} else { } else {
animalToPlayer.put(entity.getUniqueId(), player.getUniqueId()); animalToPlayer.put(entity.getUniqueId(), player.getUniqueId());
saveChunkAnimalMap(); saveChunkAnimalMap();
@@ -157,7 +157,7 @@ public class MobCapListener implements Listener {
chunkAnimalMap.remove(chunkKey); chunkAnimalMap.remove(chunkKey);
} }
saveChunkAnimalMap(); saveChunkAnimalMap();
plugin.getLogger().info("Tier " + entity.getType() + " in Chunk " + chunkKey + " gestorben. Verbleibende Tiere: " + animalToPlayer.size()); plugin.logDebug("Tier " + entity.getType() + " in Chunk " + chunkKey + " gestorben. Verbleibende Tiere: " + animalToPlayer.size());
} }
} }

View File

@@ -30,7 +30,7 @@ public class MobLeashLimitListener implements Listener {
public void reloadConfig(FileConfiguration config) { public void reloadConfig(FileConfiguration config) {
this.enabled = config.getBoolean("mob-leash-limit.enabled", true); this.enabled = config.getBoolean("mob-leash-limit.enabled", true);
this.maxLeashCount = config.getInt("mob-leash-limit.max-leash-count", 5); this.maxLeashCount = config.getInt("mob-leash-limit.max-leash-count", 5);
plugin.getLogger().info("MobLeashLimitListener: enabled=" + enabled + ", maxLeashCount=" + maxLeashCount); plugin.logDebug("MobLeashLimitListener: enabled=" + enabled + ", maxLeashCount=" + maxLeashCount);
// Bereinige bestehende Daten // Bereinige bestehende Daten
leashedEntities.clear(); leashedEntities.clear();
@@ -40,7 +40,7 @@ public class MobLeashLimitListener implements Listener {
if (!enabled) { if (!enabled) {
plugin.getLeashesConfig().set("leashes", null); // Bereinige leashes.yml plugin.getLeashesConfig().set("leashes", null); // Bereinige leashes.yml
plugin.saveLeashesConfig(); plugin.saveLeashesConfig();
plugin.getLogger().info("MobLeashLimitListener: Daten bereinigt, da enabled=false"); plugin.logDebug("MobLeashLimitListener: Daten bereinigt, da enabled=false");
return; return;
} }
@@ -119,7 +119,7 @@ public class MobLeashLimitListener implements Listener {
// Speichere aktualisierte leashes.yml // Speichere aktualisierte leashes.yml
plugin.saveLeashesConfig(); plugin.saveLeashesConfig();
plugin.getLogger().info("MobLeashLimitListener: Nach Reload " + leashedEntities.size() + " Spieler mit angeleinten Tieren gefunden."); plugin.logDebug("MobLeashLimitListener: Nach Reload " + leashedEntities.size() + " Spieler mit angeleinten Tieren gefunden.");
} }
@EventHandler @EventHandler
@@ -135,7 +135,7 @@ public class MobLeashLimitListener implements Listener {
if (itemInHand != null && itemInHand.getType() == Material.LEAD) { if (itemInHand != null && itemInHand.getType() == Material.LEAD) {
Set<UUID> playerLeashedEntities = leashedEntities.computeIfAbsent(playerId, k -> new HashSet<>()); Set<UUID> playerLeashedEntities = leashedEntities.computeIfAbsent(playerId, k -> new HashSet<>());
int currentCount = playerLeashedEntities.size(); int currentCount = playerLeashedEntities.size();
plugin.getLogger().info("PlayerInteractEntity: player=" + player.getName() + ", currentCount=" + currentCount + ", maxLeashCount=" + maxLeashCount); plugin.logDebug("PlayerInteractEntity: player=" + player.getName() + ", currentCount=" + currentCount + ", maxLeashCount=" + maxLeashCount);
if (playerLeashedEntities.contains(entityId)) { if (playerLeashedEntities.contains(entityId)) {
// Ableinen // Ableinen

View File

@@ -0,0 +1,324 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
import net.milkbowl.vault.economy.Economy;
import net.milkbowl.vault.economy.EconomyResponse;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.RegisteredServiceProvider;
public class SignShopListener implements Listener {
private final SurvivalPlus plugin;
private Economy economy;
public SignShopListener(SurvivalPlus plugin) {
this.plugin = plugin;
try {
if (plugin.getServer().getPluginManager().isPluginEnabled("Vault")) {
RegisteredServiceProvider<Economy> rsp = plugin.getServer().getServicesManager().getRegistration(Economy.class);
if (rsp != null) {
this.economy = rsp.getProvider();
plugin.getLogger().info("Vault Economy für Sign Shops gefunden.");
} else {
plugin.getLogger().warning("Vault Economy Service nicht gefunden!");
}
} else {
plugin.getLogger().warning("Vault Plugin nicht installiert! Sign Shops funktionieren nicht.");
}
} catch (Exception e) {
plugin.getLogger().warning("Fehler beim Laden der Vault API.");
}
}
// --- Schild erstellung ---
@EventHandler(priority = EventPriority.HIGHEST)
public void onSignChange(SignChangeEvent event) {
String line0Raw = event.getLine(0);
if (!line0Raw.equalsIgnoreCase("[buy]") && !line0Raw.equalsIgnoreCase("[sell]")) {
return;
}
Player player = event.getPlayer();
if (!player.hasPermission("survivalplus.shop.create")) {
player.sendMessage(ChatColor.RED + "Keine Berechtigung, Shops zu erstellen.");
return;
}
String line2 = event.getLine(1);
String line3 = event.getLine(2);
String[] parts = line2.split(" ");
if (parts.length < 2) {
player.sendMessage(ChatColor.RED + "Format in Zeile 2 falsch! Beispiel: 64 Diamond");
event.setCancelled(true);
return;
}
try {
Integer.parseInt(parts[0]);
if (Material.matchMaterial(parts[1]) == null) {
player.sendMessage(ChatColor.RED + "Das Item '" + parts[1] + "' existiert nicht.");
event.setCancelled(true);
return;
}
Double.parseDouble(line3);
} catch (Exception e) {
player.sendMessage(ChatColor.RED + "Fehler im Format! Benutze: [Buy] \n 64 Diamond \n 500");
event.setCancelled(true);
return;
}
// --- NEU: Schild farbig machen ---
if (line0Raw.equalsIgnoreCase("[buy]")) {
event.setLine(0, ChatColor.GREEN + "[BUY]");
event.setLine(1, ChatColor.WHITE + parts[0] + " " + ChatColor.AQUA + parts[1]);
event.setLine(2, ChatColor.GOLD + line3 + " Coins");
} else if (line0Raw.equalsIgnoreCase("[sell]")) {
event.setLine(0, ChatColor.RED + "[SELL]");
event.setLine(1, ChatColor.WHITE + parts[0] + " " + ChatColor.AQUA + parts[1]);
event.setLine(2, ChatColor.GOLD + line3 + " Coins");
}
if (event.getLine(3) != null && !event.getLine(3).isEmpty()) {
event.setLine(3, ChatColor.GRAY + event.getLine(3));
}
}
// --- Interaktion ---
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerInteract(PlayerInteractEvent event) {
Block block = event.getClickedBlock();
if (block == null || !(block.getState() instanceof Sign)) {
return;
}
Sign sign = (Sign) block.getState();
String line0 = ChatColor.stripColor(sign.getLine(0));
if (!line0.equalsIgnoreCase("[buy]") && !line0.equalsIgnoreCase("[sell]")) {
return;
}
if (economy == null) {
event.getPlayer().sendMessage(ChatColor.RED + "Shop System offline (Vault fehlt).");
return;
}
// --- FEATURE: SHIFT+KLICK LOGIK (LÖSCHEN) ---
if (event.getPlayer().isSneaking()) {
Player player = event.getPlayer();
if (!player.hasPermission("survivalplus.shop.create")) {
player.sendMessage(ChatColor.RED + "Keine Berechtigung, Shops zu entfernen.");
event.setCancelled(true);
return;
}
block.breakNaturally();
player.sendMessage(ChatColor.GREEN + "Shop entfernt.");
event.setCancelled(true);
return;
}
// --- ROUTING ---
if (line0.equalsIgnoreCase("[buy]")) {
handleBuy(event, sign);
} else if (line0.equalsIgnoreCase("[sell]")) {
handleSell(event, sign);
}
}
// --- KAUFS LOGIK (ROBUST) ---
private void handleBuy(PlayerInteractEvent event, Sign sign) {
event.setCancelled(true); // Interaktion stoppen
Player player = event.getPlayer();
String line1Raw = ChatColor.stripColor(sign.getLine(1)); // "64 Diamond"
String line2Raw = ChatColor.stripColor(sign.getLine(2)); // "500" oder "500 Coins"
// 1. Validierung: Zeilen leer?
if (line1Raw == null || line1Raw.isEmpty()) {
player.sendMessage(ChatColor.RED + "Zeile 1 des Schildes ist leer! (Format: 64 Diamond)");
return;
}
if (line2Raw == null || line2Raw.isEmpty()) {
player.sendMessage(ChatColor.RED + "Preiszeile ist leer! (Format: 500)");
return;
}
// 2. Parsing: Zeile 1 "64 Diamond"
String[] partsRaw = line1Raw.split(" ");
if (partsRaw.length < 2) {
player.sendMessage(ChatColor.RED + "Falsches Format! Benutze: [Buy] \n 64 Diamond");
return;
}
String amountStr = partsRaw[0].trim(); // "64"
String itemNameStr = partsRaw[1].trim(); // "Diamond"
if (amountStr.isEmpty() || itemNameStr.isEmpty()) {
player.sendMessage(ChatColor.RED + "Format Fehler!");
return;
}
int amount;
Material material;
try {
amount = Integer.parseInt(amountStr);
material = Material.matchMaterial(itemNameStr);
} catch (NumberFormatException e) {
player.sendMessage(ChatColor.RED + "Angebot '" + amountStr + "' ist keine Zahl! (Beispiel: 64)");
return;
}
if (material == null) {
player.sendMessage(ChatColor.RED + "Item '" + itemNameStr + "' existiert nicht!");
return;
}
// 3. Parsing: Zeile 2 "500 Coins" (Hier passierte der Fehler!)
String[] priceParts = line2Raw.split(" ");
if (priceParts.length == 0) {
player.sendMessage(ChatColor.RED + "Preiszeile ist leer! (Beispiel: 500)");
return;
}
String priceStr = priceParts[0].trim();
if (priceStr.isEmpty()) {
player.sendMessage(ChatColor.RED + "Kein Preis gefunden! (Beispiel: 500)");
return;
}
double price;
try {
price = Double.parseDouble(priceStr);
} catch (NumberFormatException e) {
player.sendMessage(ChatColor.RED + "Preis '" + priceStr + "' ist keine Zahl! (Beispiel: 500)");
return;
} catch (ArrayIndexOutOfBoundsException e) {
player.sendMessage(ChatColor.RED + "Fehlerhafter Preis! (Beispiel: 500)");
return;
}
if (amount <= 0 || price < 0) {
player.sendMessage(ChatColor.RED + "Negative Werte sind nicht erlaubt.");
return;
}
// 4. Transaktion
try {
ItemStack item = new ItemStack(material, amount);
if (economy.getBalance(player) >= price) {
EconomyResponse response = economy.withdrawPlayer(player, price);
if (response.transactionSuccess()) {
player.getInventory().addItem(item);
player.sendMessage(ChatColor.GREEN + "Du hast " + amount + " " + material.name().toLowerCase() + " für " + price + " gekauft!");
} else {
player.sendMessage(ChatColor.RED + "Fehler bei der Transaktion.");
}
} else {
player.sendMessage(ChatColor.RED + "Du hast nicht genug Geld! Du brauchst " + price + ".");
}
} catch (Exception e) {
player.sendMessage(ChatColor.RED + "Ein Fehler ist beim Lesen des Shops aufgetreten.");
// Loggen für Debugging, falls Vault spinnt
e.printStackTrace();
}
}
// --- VERKAUFS LOGIK ---
private void handleSell(PlayerInteractEvent event, Sign sign) {
event.setCancelled(true);
Player player = event.getPlayer();
String line1Raw = ChatColor.stripColor(sign.getLine(1));
String line2Raw = ChatColor.stripColor(sign.getLine(2));
if (line1Raw == null || line1Raw.isEmpty()) {
player.sendMessage(ChatColor.RED + "Zeile 1 ist leer!");
return;
}
if (line2Raw == null || line2Raw.isEmpty()) {
player.sendMessage(ChatColor.RED + "Zeile 2 ist leer!");
return;
}
String[] partsRaw = line1Raw.split(" ");
if (partsRaw.length < 2) {
player.sendMessage(ChatColor.RED + "Format falsch! Benutze: [Sell] \n 64 Diamond");
return;
}
String amountStr = partsRaw[0].trim();
String itemNameStr = partsRaw[1].trim();
int amount;
Material material;
try {
amount = Integer.parseInt(amountStr);
material = Material.matchMaterial(itemNameStr);
} catch (NumberFormatException e) {
player.sendMessage(ChatColor.RED + "Menge ist keine Zahl!");
return;
}
if (material == null) {
player.sendMessage(ChatColor.RED + "Item existiert nicht!");
return;
}
// Preis Parsing (wie im Kauf, nur sicherer)
String[] priceParts = line2Raw.split(" ");
if (priceParts.length == 0) {
player.sendMessage(ChatColor.RED + "Preiszeile ist leer!");
return;
}
String priceStr = priceParts[0].trim();
if (priceStr.isEmpty()) {
player.sendMessage(ChatColor.RED + "Kein Preis!");
return;
}
double price;
try {
price = Double.parseDouble(priceStr);
} catch (NumberFormatException e) {
player.sendMessage(ChatColor.RED + "Preis '" + priceStr + "' ist keine Zahl!");
return;
}
if (amount <= 0 || price < 0) {
player.sendMessage(ChatColor.RED + "Negativer Wert!");
return;
}
ItemStack tempItem = new ItemStack(material, amount);
int hasAmount = 0;
for (ItemStack item : player.getInventory().getStorageContents()) {
if (item != null && item.isSimilar(tempItem)) {
hasAmount += item.getAmount();
}
}
if (hasAmount < amount) {
player.sendMessage(ChatColor.RED + "Du hast nicht genug Items!");
return;
}
// Verkauf durchführen
player.getInventory().removeItem(new ItemStack(material, amount));
economy.depositPlayer(player, price);
player.updateInventory();
player.sendMessage(ChatColor.GREEN + "Du hast " + amount + " " + material.name().toLowerCase() + " für " + price + " Coins verkauft!");
}
}

View File

@@ -196,7 +196,7 @@ public class SitListener implements Listener {
} }
} }
} }
plugin.getLogger().info("Ghost Stands (Sit) bereinigt."); plugin.logDebug("Ghost Stands (Sit) bereinigt.");
}, 20L); }, 20L);
} }
} }

View File

@@ -0,0 +1,88 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityPickupItemEvent; // NEU: EntityPickupItemEvent
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
public class VanishListener implements Listener {
private final SurvivalPlus plugin;
public VanishListener(SurvivalPlus plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
if (plugin.isVanished(player.getUniqueId())) {
// Join Message ausblenden
event.setJoinMessage(null);
// Silent Join (Teleport-Effekt) via ProtocolLib
if (player.hasPermission("survivalplus.vanish.silent")) {
hideJoinParticles(player);
}
}
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
if (plugin.isVanished(player.getUniqueId())) {
// Quit Message ausblenden
event.setQuitMessage(null);
}
}
@EventHandler
public void onTeleport(PlayerTeleportEvent event) {
Player player = event.getPlayer();
// Silent Teleport verhindert den Endermann-Teleportations-Effekt
if (plugin.isVanished(player.getUniqueId()) && player.hasPermission("survivalplus.vanish.silent")) {
// In 4.8.0 ist das stumme Teleportieren komplex.
// Wir lassen es hier beim Abschalten der Join-Partikel bewenden.
}
}
@EventHandler
public void onPickup(EntityPickupItemEvent event) {
if (!(event.getEntity() instanceof Player)) return; // Vorsichtshalber prüfen
Player player = (Player) event.getEntity();
if (plugin.isVanished(player.getUniqueId()) && player.hasPermission("survivalplus.vanish.no-pickup")) {
event.setCancelled(true); // Item nicht aufheben
}
}
private void hideJoinParticles(Player player) {
if (!plugin.getServer().getPluginManager().isPluginEnabled("ProtocolLib")) return;
try {
// Direkter Cast auf ProtocolManager ist in 4.8.0 sicherer
ProtocolManager manager = (ProtocolManager) plugin.getServer().getPluginManager().getPlugin("ProtocolLib");
if (manager == null) return;
// Packet Container erstellen
PacketContainer packet = manager.createPacket(PacketType.Play.Server.ENTITY_STATUS);
packet.getIntegers().write(0, player.getEntityId());
packet.getBytes().write(0, (byte) 0x20); // 32 = Invisibel/Unsichtbar Status Client-Side
for (Player online : plugin.getServer().getOnlinePlayers()) {
if (online.equals(player)) continue;
manager.sendServerPacket(online, packet);
}
} catch (Exception e) {
// Ignorieren Fehler bei ProtocolLib
}
}
}

View File

@@ -4,6 +4,10 @@ version: 1.1.0
# Debug-Option # Debug-Option
debug-logging: false debug-logging: false
# Spieler Kopf bei Tod
heads:
drop-on-death: true
# Neulings Schutz # Neulings Schutz
newbie-protection: newbie-protection:
enabled: true enabled: true

View File

@@ -1,323 +1,364 @@
header: "&6=== SurvivalPlus Hilfe ===" header: "&6=== SurvivalPlus Hilfe ==="
footer: "&6===========================" footer: "&6==========================="
help-usage: "&cVerwendung: /sp help [player|admin|system|all] [seite]"
navigation:
prev: "&7« Vorherige Seite"
next: "&7Nächste Seite »"
prev-disabled: "&8« Vorherige Seite"
next-disabled: "&8Nächste Seite »"
page: "&fSeite {current} von {total}"
groups:
system:
title: "&6-- System --"
player:
title: "&6-- Spieler --"
admin:
title: "&6-- Admin --"
commands: commands:
gm:
description: "&eÄndert den Spielmodus eines Spielers (survival, creative, adventure, spectator)."
usage: "&b/gm <modus> [spieler]"
sp: sp:
description: "&eHauptbefehl für SurvivalPlus mit Unterbefehlen." group: system
usage: "&b/sp [reload | help | info | share | lock]" description: "&eHauptbefehl für SurvivalPlus."
usage: "&b/sp [help|info|share|cb|reload]"
sp_reload:
description: "&eLädt das Plugin neu."
usage: "&b/sp reload"
sp_help: sp_help:
description: "&eZeigt die Hilfe für SurvivalPlus-Befehle an." group: system
usage: "&b/sp help" description: "&eZeigt die Hilfe an (Spieler/Admin/System)."
usage: "&b/sp help [player|admin|system|all] [seite]"
sp_info: sp_info:
description: "&eZeigt Informationen über das Plugin an." group: system
description: "&eZeigt Plugin-Infos und Links."
usage: "&b/sp info" usage: "&b/sp info"
sp_share: sp_share:
description: "&eTeilt deine Koordinaten nach Bestätigung mit allen Spielern." group: player
description: "&eTeilt deine Koordinaten nach Bestätigung."
usage: "&b/sp share" usage: "&b/sp share"
shareconfirm:
group: player
description: "&eBestätigt das Teilen der Koordinaten."
usage: "&b/sp shareconfirm"
sharecancel:
group: player
description: "&eBricht das Teilen der Koordinaten ab."
usage: "&b/sp sharecancel"
sp_cb_add: sp_cb_add:
group: admin
description: "&eFügt einen Befehl zur Blockierliste hinzu." description: "&eFügt einen Befehl zur Blockierliste hinzu."
usage: "&b/sp cb add <befehl>" usage: "&b/sp cb add <befehl>"
sp_cb_remove: sp_cb_remove:
group: admin
description: "&eEntfernt einen Befehl aus der Blockierliste." description: "&eEntfernt einen Befehl aus der Blockierliste."
usage: "&b/sp cb remove <befehl>" usage: "&b/sp cb remove <befehl>"
sp_cb_list: sp_cb_list:
group: admin
description: "&eZeigt die Liste der blockierten Befehle an." description: "&eZeigt die Liste der blockierten Befehle an."
usage: "&b/sp cb list" usage: "&b/sp cb list"
sp_reload:
group: admin
description: "&eLädt das Plugin neu."
usage: "&b/sp reload"
splock:
group: player
description: "&eSperrt/entsperrt Container und verwaltet Freunde."
usage: "&b/splock lock|unlock|friendadd|friendremove [spieler]"
sp_lock_lock: sp_lock_lock:
description: "&eSperrt einen Container (z.B. Kiste oder Tür)." group: player
usage: "&b/sp lock " description: "&eStartet den Lock-Modus (Block anklicken)."
usage: "&b/sp lock"
sp_lock_unlock: sp_lock_unlock:
description: "&eEntsperrt einen Container (z.B. Kiste oder Tür)." group: player
usage: "&b/sp lock " description: "&eEntsperrt einen Block, den du ansiehst."
usage: "&b/sp unlock"
sp_lock_friendadd: sp_lock_friendadd:
description: "&eFügt einen Freund zum Container-Sperrsystem hinzu." group: player
usage: "&b/sp lock friendadd <Spieler>" description: "&eFügt einen Freund zum Lock-System hinzu."
usage: "&b/sp friendadd <spieler>"
sp_lock_friendremove: sp_lock_friendremove:
description: "&eEntfernt einen Freund aus dem Container-Sperrsystem." group: player
usage: "&b/sp lock friendremove <Spieler>" description: "&eEntfernt einen Freund aus dem Lock-System."
usage: "&b/sp friendremove <spieler>"
shareconfirm: home:
description: "&eBestätigt das Teilen deiner Koordinaten mit allen Spielern." group: player
usage: "&b/sp shareconfirm" description: "&eTeleportiert dich zu einem Home."
usage: "&b/home <name>"
sharecancel:
description: "&eBricht das Teilen deiner Koordinaten ab."
usage: "&b/sp sharecancel"
sethome: sethome:
description: "&eSetzt ein Home mit dem angegebenen Namen." group: player
description: "&eSetzt ein Home mit Namen."
usage: "&b/sethome <name>" usage: "&b/sethome <name>"
delhome: delhome:
description: "&eLöscht ein Home mit dem angegebenen Namen." group: player
description: "&eLöscht ein Home."
usage: "&b/delhome <name>" usage: "&b/delhome <name>"
homelist: homelist:
description: "&eÖffnet eine GUI mit allen Homes." group: player
description: "&eÖffnet die Homes-Übersicht."
usage: "&b/homelist" usage: "&b/homelist"
home: warps:
description: "&eTeleportiert zu einem Home." group: player
usage: "&b/home <name>" description: "&eÖffnet die Warps-Übersicht."
usage: "&b/warps"
inv: setwarp:
description: "&eÖffnet das Inventar (eigenes oder das eines anderen Spielers)." group: player
usage: "&b/inv [spieler]" description: "&eSetzt einen persönlichen Warp."
usage: "&b/setwarp <name>"
ec: delwarp:
description: "&eÖffnet die Endertruhe (eigene oder die eines anderen Spielers)." group: player
usage: "&b/ec [spieler]" description: "&eLöscht einen persönlichen Warp."
usage: "&b/delwarp <name>"
setworldspawn: spawn:
description: "&eSetzt den Weltspawnpunkt auf die Position des Spielers." group: player
usage: "&b/setworldspawn" description: "&eTeleportiert dich zum Weltspawn."
usage: "&b/spawn"
setspawn:
description: "&eSetzt den Server-Spawnpunkt auf die Position des Spielers."
usage: "&b/setspawn"
clearchat:
description: "&eLöscht den Chat für alle Spieler."
usage: "&b/clearchat"
clearitems:
description: "&eLöscht alle herumliegenden Items."
usage: "&b/clearitems"
closedoors:
description: "&eSchließt alle Türen im angegebenen Radius."
usage: "&b/closedoors <radius>"
sit:
description: "&eLässt den Spieler sich hinsetzen oder aufstehen."
usage: "&b/sit"
back: back:
group: player
description: "&eTeleportiert zum letzten Todespunkt." description: "&eTeleportiert zum letzten Todespunkt."
usage: "&b/back" usage: "&b/back"
friend:
description: "&eVerwaltet die Freundesliste (hinzufügen, entfernen, anzeigen, teleportieren)."
usage: "&b/friend [add | accept | deny | list | del | tp] [Spieler]"
subcommands:
add:
description: "&eFügt einen Spieler zur Freundesliste hinzu."
usage: "&b/friend add <Spieler>"
accept:
description: "&eAkzeptiert eine Freundschaftsanfrage."
usage: "&b/friend accept <Spieler>"
deny:
description: "&eLehnt eine Freundschaftsanfrage ab."
usage: "&b/friend deny <Spieler>"
list:
description: "&eZeigt die Freundesliste an."
usage: "&b/friend list"
del:
description: "&eEntfernt einen Spieler aus der Freundesliste."
usage: "&b/friend del <Spieler>"
tp:
description: "&eTeleportiert dich zu einem Freund."
usage: "&b/friend tp <Spieler>"
ir:
description: "&eBenennt das Item in der Hand um."
usage: "&b/ir <neuer_name>"
showarmorstands:
description: "&eMacht alle unsichtbaren Armor Stands sichtbar."
usage: "&b/showarmorstands"
cleardebugarmorstands:
description: "&eEntfernt alle Debug-ArmorStands."
usage: "&b/cleardebugarmorstands"
trash:
description: "&eÖffnet den Mülleimer."
usage: "&b/trash"
workbench:
description: "&eÖffnet eine Werkbank-GUI."
usage: "&b/workbench"
anvil:
description: "&eÖffnet eine Amboss-GUI."
usage: "&b/anvil"
stats:
description: "&eZeigt deine Statistiken an."
usage: "&b/stats"
spawn:
description: "&eTeleportiert dich zum Weltspawnpunkt."
usage: "&b/spawn"
tp:
description: "&eTeleportiert dich zu einem Spieler."
usage: "&b/tp <Spieler>"
tphere:
description: "&eTeleportiert einen Spieler zu dir."
usage: "&b/tphere <Spieler>"
tpa: tpa:
description: "&eSendet eine Teleportanfrage an einen Spieler." group: player
usage: "&b/tpa <Spieler>" description: "&eSendet eine Teleportanfrage."
usage: "&b/tpa <spieler>"
tpaccept: tpaccept:
group: player
description: "&eAkzeptiert eine Teleportanfrage." description: "&eAkzeptiert eine Teleportanfrage."
usage: "&b/tpaccept" usage: "&b/tpaccept"
tpdeny: tpdeny:
group: player
description: "&eLehnt eine Teleportanfrage ab." description: "&eLehnt eine Teleportanfrage ab."
usage: "&b/tpdeny" usage: "&b/tpdeny"
friend:
group: player
description: "&eFreundesliste verwalten."
usage: "&b/friend [add|accept|deny|list|del|confirm|tp] [spieler]"
block: block:
description: "&eBlockiert einen Spieler." group: player
usage: "&b/block <Spieler>" description: "&eBlockiert einen Spieler im Chat."
usage: "&b/block <spieler>"
unblock: unblock:
description: "&eEntblockt einen Spieler." group: player
usage: "&b/unblock <Spieler>" description: "&eEntblockt einen Spieler im Chat."
usage: "&b/unblock <spieler>"
blocklist: blocklist:
description: "&eZeigt eine Liste der blockierten Spieler." group: player
description: "&eZeigt deine Blockliste."
usage: "&b/blocklist" usage: "&b/blocklist"
trade:
group: player
description: "&eStartet einen Handel."
usage: "&b/trade <spieler>"
tradeaccept:
group: player
description: "&eAkzeptiert einen Handel."
usage: "&b/tradeaccept <spieler>"
report:
group: player
description: "&eMeldet einen Spieler an Admins."
usage: "&b/report <spieler> [grund]"
stats:
group: player
description: "&eZeigt deine Statistiken."
usage: "&b/stats"
kit: kit:
group: player
description: "&eHolt das Starterkit." description: "&eHolt das Starterkit."
usage: "&b/kit" usage: "&b/kit"
nick:
group: player
description: "&eSetzt deinen Nickname."
usage: "&b/nick <name> | off"
ride:
group: player
description: "&eReite einen Spieler oder Mob."
usage: "&b/ride [spieler]"
sit:
group: player
description: "&eHinsetzen/Aufstehen."
usage: "&b/sit"
ir:
group: player
description: "&eBenennt das Item in der Hand um."
usage: "&b/ir <neuer_name>"
trash:
group: player
description: "&eÖffnet den Mülleimer."
usage: "&b/trash"
workbench:
group: player
description: "&eÖffnet eine Werkbank-GUI."
usage: "&b/workbench"
anvil:
group: player
description: "&eÖffnet eine Amboss-GUI."
usage: "&b/anvil"
leashcount: leashcount:
description: "&eZeigt die Anzahl der geleinten Tiere an." group: player
description: "&eZeigt die Anzahl geleinter Tiere."
usage: "&b/leashcount" usage: "&b/leashcount"
nick:
description: "&eÄndert deinen Nicknamen mit Farb- und Hex-Support."
usage: "&b/nick <Name>"
lootchests: lootchests:
description: "&eZeigt eine Liste aller aktiven Loot-Kisten an. Admins können per Klick teleportieren." group: admin
description: "&eListet aktive Loot-Kisten."
usage: "&b/lootchests" usage: "&b/lootchests"
tploot: tploot:
description: "&eTeleportiert dich zu einer Loot-Kiste (nur Admins)." group: admin
description: "&eTeleportiert zu einer Loot-Kiste."
usage: "&b/tploot <welt> <x> <y> <z>" usage: "&b/tploot <welt> <x> <y> <z>"
shop:
group: player
description: "&eShop GUI (Admin: /shop add ...)."
usage: "&b/shop [gui|add]"
claim:
group: player
description: "&eClaim-System verwalten."
usage: "&b/claim [mark|unclaim|trust|untrust|info|kick|ban|unban]"
startchallenge:
group: admin
description: "&eStartet eine Fun-Challenge."
usage: "&b/startchallenge <name>"
head:
group: player
description: "&eGibt dir den Kopf eines Spielers."
usage: "&b/head <spieler>"
heal:
group: admin
description: "&eHeilt einen Spieler vollständig."
usage: "&b/heal [spieler]"
gm:
group: admin
description: "&eÄndert den Spielmodus."
usage: "&b/gm <modus> [spieler]"
tp:
group: admin
description: "&eTeleportiert dich zu einem Spieler."
usage: "&b/tp <spieler>"
tphere:
group: admin
description: "&eTeleportiert einen Spieler zu dir."
usage: "&b/tphere <spieler>"
inv:
group: admin
description: "&eÖffnet ein Inventar (eigen/anderer)."
usage: "&b/inv [spieler]"
ec:
group: admin
description: "&eÖffnet eine Endertruhe (eigene/andere)."
usage: "&b/ec [spieler]"
setworldspawn:
group: admin
description: "&eSetzt den Weltspawn auf deine Position."
usage: "&b/setworldspawn"
setspawn:
group: admin
description: "&eSetzt den Serverspawn auf deine Position."
usage: "&b/setspawn"
clearchat:
group: admin
description: "&eLeert den Chat."
usage: "&b/clearchat"
clearitems:
group: admin
description: "&eEntfernt herumliegende Items."
usage: "&b/clearitems"
closedoors:
group: admin
description: "&eSchließt Türen im Radius."
usage: "&b/closedoors <radius>"
showarmorstands:
group: admin
description: "&eMacht Debug-ArmorStands sichtbar."
usage: "&b/showarmorstands"
cleardebugarmorstands:
group: admin
description: "&eEntfernt Debug-ArmorStands."
usage: "&b/cleardebugarmorstands"
vanish:
group: admin
description: "&eMacht dich unsichtbar."
usage: "&b/vanish"
freeze:
group: admin
description: "&eFriert einen Spieler ein."
usage: "&b/freeze <spieler>"
day: day:
group: admin
description: "&eSetzt die Zeit auf Tag." description: "&eSetzt die Zeit auf Tag."
usage: "&b/day" usage: "&b/day"
night: night:
group: admin
description: "&eSetzt die Zeit auf Nacht." description: "&eSetzt die Zeit auf Nacht."
usage: "&b/night" usage: "&b/night"
trade:
description: "&eStartet einen Handel mit einem Spieler."
usage: "&b/trade <Spieler>"
tradeaccept:
description: "&eAkzeptiert eine Handelsanfrage."
usage: "&b/tradeaccept <Spieler>"
report:
description: "&eMeldet einen Spieler an die Admins."
usage: "&b/report <Spieler> [Grund]"
showreport: showreport:
description: "&eZeigt alle Reports eines Spielers an." group: admin
usage: "&b/showreport <Spieler>" description: "&eZeigt Reports eines Spielers."
usage: "&b/showreport <spieler>"
clearreport: clearreport:
description: "&eLöscht alle Reports eines Spielers." group: admin
usage: "&b/clearreport <Spieler>" description: "&eLöscht Reports eines Spielers."
usage: "&b/clearreport <spieler>"
shop:
description: "&eVerwaltet den Server-Shop (z.B. Items hinzufügen)."
usage: "&b/shop add <item> <basispreis> <lagerbestand>"
setwarp:
description: "&eSetzt einen persönlichen Warp mit dem Item in der Hand."
usage: "&b/setwarp <name>"
delwarp:
description: "&eLöscht einen persönlichen Warp."
usage: "&b/delwarp <name>"
warps:
description: "&eÖffnet die GUI mit allen Spieler-Warps."
usage: "&b/warps"
startchallenge:
description: "&eStartet eine Fun-Challenge."
usage: "&b/startchallenge <name>"
heal:
description: "&eHeilt einen Spieler vollständig."
usage: "&b/heal [spieler]"
claim:
description: "&eVerwaltet Claims für den Anti-Grief-Schutz."
usage: "&b/claim [mark <1|2>| unclaim | trust <spieler> | untrust <spieler>]"
subcommands:
mark:
description: "&eMarkiert die erste oder zweite Ecke eines zu schützenden Bereichs."
usage: "&b/claim mark <1|2>"
unclaim:
description: "&eGibt den geschützten Bereich frei, in dem du stehst."
usage: "&b/claim unclaim"
trust:
description: "&eFügt einen Spieler als vertrauenswürdig hinzu, sodass er im Bereich bauen kann."
usage: "&b/claim trust <spieler>"
untrust:
description: "&eEntfernt die Vertrauensberechtigung eines Spielers für den Bereich."
usage: "&b/claim untrust <spieler>"
messages:
header: "&6=== Befehle ==="
footer: "&6================"
navigation:
prev: "&7« Vorherige Seite"
next: "&7Nächste Seite »"
prev-disabled: "&8« Vorherige Seite"
next-disabled: "&8Nächste Seite »"
page: "&fSeite {current} von {total}"
sp:
invalid-subcommand: "&cUngültiger Unterbefehl! Verwendung: /sp [reload|help|info|share|lock]"
no-permission: "&cDu hast keine Berechtigung für diesen Befehl!"
plugin:
reloaded: "&aSurvivalPlus wurde erfolgreich neu geladen!"
info:
header: "&7===== SurvivalPlus Info ====="
name: "&ePlugin-Name: &f"
version: "&eVersion: &f"
author: "&eErsteller: &f"
description: "&eBeschreibung:\\n&f"
footer: "&7=========================="
share:
preview-title: "&aDeine aktuellen Koordinaten wären:"
preview-format: "&e%player% &7teilt Koordinaten: &eX: %x%, Y: %y%, Z: %z% &7in Welt &e%world%"
send-button: "&a[✅ Senden]"
cancel-button: "&c[❌ Abbrechen]"
send-hover: "&aKlicke, um deine Koordinaten an alle zu senden."
cancel-hover: "&cKlicke, um das Senden abzubrechen."
sent: "&aKoordinaten gesendet."
cancelled: "&eSenden der Koordinaten abgebrochen."
help-not-found: "&cHilfedatei (help.yml) konnte nicht geladen werden!"

View File

@@ -1,7 +1,21 @@
reload:
success: "§aSurvivalPlus wurde erfolgreich neu geladen!"
invalid-subcommand: "§cUngültiger Unterbefehl! Verwendung: /sp [reload|help|info|share]"
player-only: "§cDieser Befehl ist nur für Spieler!"
not-found: "§cKeine Befehle verfügbar."
groups:
system: "&6-- System --"
player: "&6-- Spieler --"
admin: "&6-- Admin --"
sp: sp:
no-permission: "§cDu hast keine Berechtigung für diesen Befehl!" no-permission: "§cDu hast keine Berechtigung für diesen Befehl!"
plugin.reloaded: "§aSurvivalPlus wurde erfolgreich neu geladen!" plugin.reloaded: "§aSurvivalPlus wurde erfolgreich neu geladen!"
invalid-subcommand: "§cUngültiger Unterbefehl! Verwendung: /sp [ reload | help | info | share ]" invalid-subcommand: "§cUngültiger Unterbefehl! Verwendung: /sp [help|info|share|shareconfirm|sharecancel|cb|reload]"
help-not-found: "§cHilfedatei (help.yml) konnte nicht geladen werden!" help-not-found: "§cHilfedatei (help.yml) konnte nicht geladen werden!"
share: share:

View File

@@ -1,9 +1,9 @@
name: SurvivalPlus name: SurvivalPlus
version: 1.1.1 version: 1.1.3
main: de.viper.survivalplus.SurvivalPlus main: de.viper.survivalplus.SurvivalPlus
api-version: 1.21 api-version: 1.21
softdepend: [LuckPerms, PlaceholderAPI, ProtocolLib] softdepend: [LuckPerms, PlaceholderAPI, ProtocolLib, Vault]
author: Viper author: Viper
description: A plugin for enhancing survival gameplay in Minecraft. description: A plugin for enhancing survival gameplay in Minecraft.
@@ -188,6 +188,16 @@ commands:
usage: /<command> [spieler] usage: /<command> [spieler]
permission: survivalplus.ride permission: survivalplus.ride
permission-message: "§cDu hast keine Berechtigung, Spieler zu reiten!" permission-message: "§cDu hast keine Berechtigung, Spieler zu reiten!"
vanish:
description: Macht dich unsichtbar
usage: /<command>
permission: survivalplus.vanish
permission-message: "§cDu hast keine Berechtigung für diesen Befehl."
freeze:
description: Friert einen Spieler ein
usage: /<command> <Spieler>
permission: survivalplus.freeze
permission-message: "§cDu hast keine Berechtigung für diesen Befehl."
lootchests: lootchests:
description: Zeigt eine Liste aller aktiven Loot-Kisten an. Admins können per Klick zu einer Kiste teleportieren. description: Zeigt eine Liste aller aktiven Loot-Kisten an. Admins können per Klick zu einer Kiste teleportieren.
usage: /<command> usage: /<command>
@@ -268,11 +278,19 @@ commands:
usage: /<command> [spieler] usage: /<command> [spieler]
permission: survivalplus.heal permission: survivalplus.heal
permission-message: "§cDu hast keine Berechtigung, Spieler zu heilen!" permission-message: "§cDu hast keine Berechtigung, Spieler zu heilen!"
claim: claim:
description: Manages claims for anti-griefing description: Manages claims for anti-griefing
usage: /<command> [unclaim | trust <player> | untrust <player>] usage: /<command> [mark|unclaim|del|delete|trust|untrust|info|kick|ban|unban]
aliases: [cl] aliases: [cl]
permission: survivalplus.claim.use
permission-message: "§cDu hast keine Berechtigung für diesen Befehl."
# --- NEU: HEAD COMMAND ---
head:
description: Hol dir den Kopf eines Spielers.
usage: /<command> <name>
permission: survivalplus.head
permission-message: "§cDu hast keine Berechtigung für diesen Befehl."
permissions: permissions:
survivalplus.*: survivalplus.*:
@@ -326,6 +344,8 @@ permissions:
survivalplus.nick: true survivalplus.nick: true
survivalplus.ride: true survivalplus.ride: true
survivalplus.ride.exempt: true survivalplus.ride.exempt: true
survivalplus.vanish: true
survivalplus.freeze: true
survivalplus.lootchests: true survivalplus.lootchests: true
survivalplus.day: true survivalplus.day: true
survivalplus.night: true survivalplus.night: true
@@ -346,6 +366,12 @@ permissions:
survivalplus.chunkanimals: true survivalplus.chunkanimals: true
survivalplus.claim.use: true survivalplus.claim.use: true
survivalplus.claim.trust: true survivalplus.claim.trust: true
survivalplus.claim.kick: true
survivalplus.claim.ban: true
survivalplus.head: true
survivalplus.claim.admin: true
survivalplus.vanish.silent: true
survivalplus.vanish.no-pickup: true
survivalplus.commandblocker.add: survivalplus.commandblocker.add:
description: Erlaubt das Hinzufügen von Befehlen zur Blockierliste description: Erlaubt das Hinzufügen von Befehlen zur Blockierliste
default: op default: op
@@ -487,6 +513,12 @@ permissions:
survivalplus.ride.exempt: survivalplus.ride.exempt:
description: Spieler mit dieser Permission können nicht geritten werden description: Spieler mit dieser Permission können nicht geritten werden
default: op default: op
survivalplus.vanish:
description: Erlaubt das Unsichtbar werden
default: op
survivalplus.freeze:
description: Erlaubt das Einfrieren von Spielern
default: op
survivalplus.lootchests: survivalplus.lootchests:
description: Erlaubt das Verwalten und Teleportieren zu Loot-Kisten description: Erlaubt das Verwalten und Teleportieren zu Loot-Kisten
default: op default: op
@@ -540,4 +572,28 @@ permissions:
default: op default: op
survivalplus.chunkanimals: survivalplus.chunkanimals:
description: Erlaubt das Anzeigen der Anzahl der Tiere im aktuellen Chunk description: Erlaubt das Anzeigen der Anzahl der Tiere im aktuellen Chunk
default: op default: op
survivalplus.claim.use:
description: Erlaubt das Erstellen von Claims
default: true
survivalplus.claim.trust:
description: Erlaubt das Verwalten von Trusted-Spielern in Claims
default: true
survivalplus.claim.kick:
description: Erlaubt das Kicken von Spielern aus Claims
default: op
survivalplus.claim.ban:
description: Erlaubt das Bannen von Spielern aus Claims
default: op
survivalplus.claim.admin:
description: Erlaubt das Löschen von fremden Claims (Admin Feature)
default: op
survivalplus.vanish.silent:
description: Erlaubt stummes Einloggen
default: op
survivalplus.vanish.no-pickup:
description: Kein Items aufheben im Vanish-Modus
default: op
survivalplus.head:
description: Erlaubt das Holen von Spielerköpfen
default: true