Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bf2ee6460c | |||
| 5303cbcb95 | |||
| bce695829e | |||
| d3796a3ed9 | |||
| eb77726f83 | |||
| 0c3f91fa16 | |||
| 8277758e90 | |||
| 36363f09c2 | |||
| 14b5a08fe2 | |||
| c549df684c | |||
| 422cb9c352 | |||
| e60bff9c6d | |||
| 59944ece4e | |||
| b29f2e2db3 | |||
| f37cff83fc | |||
| 2384178a3a | |||
| 5558b237bb | |||
| e13342a767 | |||
| 2dc4f8dd38 | |||
| 503474a991 | |||
| e3a0ea6682 | |||
| b8e8f75f90 | |||
| 12fc2b82c4 | |||
| 5706d3a446 |
217
README.md
217
README.md
@@ -1,106 +1,160 @@
|
|||||||
# StatusAPI
|
# StatusAPI
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
Ein modulares und mächtiges Plugin für BungeeCord, das einen zentralen JSON-Status, ein globales Chat-System, WordPress-Verifizierung und dynamische Server-Navigation bereitstellt.
|
Ein modulares und leistungsstarkes Plugin für **BungeeCord**, das einen zentralen JSON-Status, globalen Chat, WordPress-Verifizierung, Server-Navigation und optionale Sicherheitsmodule bereitstellt.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## ⚡ Features
|
## ⚡ Features
|
||||||
|
|
||||||
- **Modulares System**: Aktiviere nur die Features, die du brauchst (Chat, Stats, Verify)
|
- **Modulares System**
|
||||||
- **JSON API**: Liefert Echtzeit-Server-Statistiken (Spieleranzahl, Motd, Versionen) für deine Webseite (Port 9191)
|
Aktiviere nur die Module, die du benötigst (Chat, Navigation, Verify, Security).
|
||||||
- **Global Chat**: Ein einheitlicher Chat über alle Server hinweg mit Badword-Filter und LuckPerms-Support
|
|
||||||
- **Server Navigation**: Automatische Generierung von Befehlen basierend auf deiner Config (z.B. `/survival` statt `/server survival`)
|
- **JSON Status API**
|
||||||
- **WordPress Verify**: Verifiziere Spieler direkt mit deiner WordPress-Seite (CPT Integration)
|
Liefert Echtzeit-Serverdaten für Webseiten oder externe Dienste (Port `9191`).
|
||||||
- **Auto-Updater**: Prüft automatisch auf Updates und lädt diese herunter
|
|
||||||
- **Logging**: Speichert den kompletten Chatverlauf in Dateien
|
- **Global Chat**
|
||||||
|
Serverübergreifender Chat mit Badword-Filter, LuckPerms-Unterstützung und Logging.
|
||||||
|
|
||||||
|
- **Server Navigation**
|
||||||
|
Automatische Generierung von Server-Befehlen (z. B. `/survival` statt `/server survival`).
|
||||||
|
|
||||||
|
- **WordPress Verify**
|
||||||
|
Spieler-Verifizierung über WordPress (Token-basiert, CPT-kompatibel).
|
||||||
|
|
||||||
|
- **CommandBlocker (Security Modul)**
|
||||||
|
Zentrale Blockierung von Commands auf Netzwerkebene inkl. Admin-Bypass.
|
||||||
|
|
||||||
|
- **Auto-Updater**
|
||||||
|
Prüft automatisch auf Updates und lädt diese herunter.
|
||||||
|
|
||||||
|
- **Logging**
|
||||||
|
Persistente Speicherung von Chat-Nachrichten mit automatischer Bereinigung.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 📥 Installation
|
## 📥 Installation
|
||||||
|
|
||||||
1. Lade die aktuellste `StatusAPI.jar` herunter
|
1. Lade die aktuelle `StatusAPI.jar`
|
||||||
2. Lege die Datei in den `plugins` Ordner deiner BungeeCord-Installation
|
2. Lege sie in den `plugins/` Ordner deiner BungeeCord-Installation
|
||||||
3. Starte den Server neu
|
3. Starte den Proxy neu
|
||||||
4. Die Config-Dateien werden automatisch erstellt
|
4. Alle Konfigurationsdateien werden automatisch erstellt
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## ⚙️ Konfiguration
|
## ⚙️ Konfiguration
|
||||||
|
|
||||||
Alle Haupt-Einstellungen werden in der `plugins/StatusAPI/verify.properties` zentral verwaltet.
|
Alle Hauptmodule werden zentral über
|
||||||
|
`plugins/StatusAPI/verify.properties` gesteuert.
|
||||||
|
|
||||||
### verify.properties
|
### verify.properties
|
||||||
|
|
||||||
Hier konfigurierst du Server-Namen, Farben und die Verbindung zu WordPress.
|
|
||||||
```properties
|
```properties
|
||||||
# ===========================
|
# ===========================
|
||||||
# GLOBALE EINSTELLUNGEN
|
# GLOBALE EINSTELLUNGEN
|
||||||
# ===========================
|
# ===========================
|
||||||
|
|
||||||
# Aktiviert oder deaktiviert das globale Chat-Modul
|
|
||||||
chat.enabled=true
|
chat.enabled=true
|
||||||
|
|
||||||
# Aktiviert die automatischen Server-Switch Befehle (z.B. /survival)
|
|
||||||
navigation.enabled=true
|
navigation.enabled=true
|
||||||
|
commandblocker.enabled=true
|
||||||
|
|
||||||
|
# ===========================
|
||||||
|
# WORDPRESS VERIFY
|
||||||
|
# ===========================
|
||||||
|
|
||||||
# URL deiner WordPress-Installation
|
|
||||||
wp_verify_url=https://deine-wordpress-domain.tld
|
wp_verify_url=https://deine-wordpress-domain.tld
|
||||||
|
|
||||||
# ===========================
|
# ===========================
|
||||||
# SERVER KONFIGURATION
|
# SERVER KONFIGURATION
|
||||||
# ===========================
|
# ===========================
|
||||||
|
|
||||||
# Der Key-Name (z.B. bungee-server-1) MUSS exakt mit dem
|
|
||||||
# Namen in deiner BungeeCord config.yml übereinstimmen!
|
|
||||||
#
|
|
||||||
# - server.<Name>=<Anzeigename> -> Wird im Chat und als Befehl genutzt
|
|
||||||
# - server.<Name>.id=<ID> -> WordPress Server ID
|
|
||||||
# - server.<Name>.secret=<Key> -> WordPress Secret
|
|
||||||
|
|
||||||
# Beispiel: Server mit Namen "Lobby"
|
|
||||||
server.lobby=&bLobby
|
server.lobby=&bLobby
|
||||||
server.lobby.id=1606
|
server.lobby.id=1606
|
||||||
server.lobby.secret=DeinSecretHier
|
server.lobby.secret=DeinSecretHier
|
||||||
|
|
||||||
# Beispiel: Server mit Namen "bungee-server-1" (Name aus BungeeConfig)
|
server.survival=&aSurvival
|
||||||
server.bungee-server-1=&aSurvival
|
server.survival.id=2
|
||||||
server.bungee-server-1.id=2
|
server.survival.secret=GeheimesWortFuerSurvival
|
||||||
server.bungee-server-1.secret=GeheimesWortFuerSurvival
|
```
|
||||||
|
## 🔒 CommandBlocker Modul
|
||||||
|
|
||||||
# Beispiel: SkyBlock
|
Das **CommandBlockerModule** blockiert definierte Commands **netzwerkweit**, bevor sie an Backend-Server weitergeleitet werden.
|
||||||
server.skyblock=&dSkyBlock
|
|
||||||
server.skyblock.id=3
|
### Eigenschaften
|
||||||
server.skyblock.secret=GeheimesWortFuerSkyBlock
|
|
||||||
|
- Blockierung auf **BungeeCord-Ebene**
|
||||||
|
- **YAML-basierte** Konfiguration
|
||||||
|
- **Live-Verwaltung** per Command
|
||||||
|
- **Bypass-Permission** für Admins
|
||||||
|
- **Keine Abhängigkeit** von anderen Modulen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Konfigurationsdatei
|
||||||
|
|
||||||
|
**Datei:**
|
||||||
|
`plugins/StatusAPI/blocked-commands.yml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
blocked:
|
||||||
|
- plugins
|
||||||
|
- pl
|
||||||
|
- version
|
||||||
|
- about
|
||||||
```
|
```
|
||||||
|
|
||||||
### Weitere Dateien
|
Änderungen können entweder **manuell** oder **per Befehl** vorgenommen werden.
|
||||||
|
|
||||||
- **filter.yml**: Hier trägst du Badwords ein, die im Chat zensiert werden sollen
|
---
|
||||||
- **welcome.yml**: Hier definierst du Willkommensnachrichten für neue Spieler (nur sichtbar, wenn Welcome-Events aktiviert sind)
|
|
||||||
- **plugins/StatusAPI/logs/**: Hier werden alle Chatnachrichten als Textdateien gespeichert (Löschung nach 7 Tagen)
|
|
||||||
|
|
||||||
## 💻 Befehle
|
## Befehle
|
||||||
|
|
||||||
Die meisten Befehle werden dynamisch basierend auf deiner `verify.properties` erstellt.
|
|
||||||
|
|
||||||
| Befehl | Beschreibung | Permission |
|
| Befehl | Beschreibung | Permission |
|
||||||
|--------|--------------|------------|
|
|------|-------------|------------|
|
||||||
| `/verify <token>` | Verifiziert den Spieler mit der WordPress-Seite | - |
|
| `/cb add <command>` | Blockiert einen Command | `commandblocker.admin` |
|
||||||
| `/survival` / `/lobby` | Führt dich auf den entsprechenden Server um (wird dynamisch erstellt) | - |
|
| `/cb remove <command>` | Entfernt einen Block | `commandblocker.admin` |
|
||||||
| `/globalmute` | Aktiviert oder deaktiviert den globalen Chat | `globalchat.mute` |
|
| `/cb list` | Zeigt alle blockierten Commands | `commandblocker.admin` |
|
||||||
| `/globalreload` | Lädt Filter und Konfigurationen neu | `globalchat.reload` |
|
| `/cb reload` | Lädt die YAML neu | `commandblocker.admin` |
|
||||||
| `/togglechat` | schaltet für den Spieler den Chat ab | |
|
|
||||||
| `/support <msg>` | Sendet eine Nachricht an das Online-Team | - |
|
|
||||||
| `/reply <msg>` | Antwortet auf eine Support-Anfrage | - |
|
|
||||||
| `/info` | Zeigt Plugin-Informationen an | - |
|
|
||||||
|
|
||||||
## 🌐 JSON API (Status)
|
---
|
||||||
|
|
||||||
Die API läuft unter `http://DEINE_IP:9191/`.
|
## Bypass
|
||||||
|
|
||||||
### Beispiel Request
|
Spieler mit folgender Permission umgehen den CommandBlocker vollständig:
|
||||||
```bash
|
|
||||||
curl http://127.0.0.1:9191/
|
```yaml
|
||||||
|
commandblocker.bypass
|
||||||
```
|
```
|
||||||
|
|
||||||
### Beispiel Antwort
|
---
|
||||||
|
|
||||||
|
## 💻 Allgemeine Befehle
|
||||||
|
|
||||||
|
| Befehl | Beschreibung | Permission |
|
||||||
|
|------|-------------|------------|
|
||||||
|
| `/verify <token>` | WordPress-Verifizierung | - |
|
||||||
|
| `/globalmute` | Globalen Chat sperren | `globalchat.mute` |
|
||||||
|
| `/globalreload` | Config neu laden | `globalchat.reload` |
|
||||||
|
| `/clearchat`, `/cc` | Chat leeren | `globalchat.clear` |
|
||||||
|
| `/togglechat` | Chat lokal deaktivieren | - |
|
||||||
|
| `/support <msg>` | Support-Anfrage senden | - |
|
||||||
|
| `/reply <msg>` | Auf Support antworten | - |
|
||||||
|
| `/info` | Plugin-Infos anzeigen | - |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌐 JSON API
|
||||||
|
|
||||||
|
**Endpoint:**
|
||||||
|
```yaml
|
||||||
|
http://DEINE_IP:9191/
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Beispiel-Antwort
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"online": true,
|
"online": true,
|
||||||
@@ -118,27 +172,54 @@ curl http://127.0.0.1:9191/
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 🛠️ Für Entwickler
|
## 🛠️ Für Entwickler
|
||||||
|
|
||||||
Die StatusAPI ist modular aufgebaut. Du kannst eigene Module erstellen, ohne den Core-Code zu berühren.
|
Die **StatusAPI** ist vollständig modular aufgebaut und erlaubt die einfache Erweiterung um eigene Module, ohne den Core-Code zu verändern.
|
||||||
|
|
||||||
### Beispiel
|
---
|
||||||
|
|
||||||
|
### Eigenes Modul erstellen
|
||||||
|
|
||||||
|
1. Klasse implementiert das `Module`-Interface
|
||||||
|
2. Optional zusätzlich `Listener` implementieren
|
||||||
|
3. Registrierung im Core (z. B. im ModuleManager):
|
||||||
|
|
||||||
1. Erstelle eine Klasse, die `Module` implementiert
|
|
||||||
2. Registriere sie in der StatusAPI Hauptdatei:
|
|
||||||
```java
|
```java
|
||||||
moduleManager.registerModule(new MeinModul());
|
moduleManager.registerModule(new MeinModul());
|
||||||
```
|
```
|
||||||
|
### Modul-Lifecycle
|
||||||
|
|
||||||
3. Dein Modul hat Zugriff auf `onEnable` und `onDisable` des Haupt-Plugins
|
Jedes Modul besitzt Zugriff auf folgende Lifecycle-Methoden:
|
||||||
|
|
||||||
## 📝 Permissions
|
- `onEnable(Plugin plugin)`
|
||||||
|
- `onDisable(Plugin plugin)`
|
||||||
|
|
||||||
- `globalchat.mute` - Erlaubt das Stummschalten des Chats
|
Diese Methoden werden automatisch beim **Starten** bzw. **Stoppen** des Proxys aufgerufen.
|
||||||
- `globalchat.bypass` - Erlaubt das Schreiben, auch wenn der Chat gemuted ist
|
|
||||||
- `globalchat.reload` - Erlaubt das Neuladen der Konfiguration
|
---
|
||||||
|
|
||||||
|
## 📝 Permissions Übersicht
|
||||||
|
|
||||||
|
- `globalchat.mute`
|
||||||
|
- `globalchat.bypass`
|
||||||
|
- `globalchat.reload`
|
||||||
|
- `globalchat.clear`
|
||||||
|
- `commandblocker.admin`
|
||||||
|
- `commandblocker.bypass`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 🤝 Credits
|
## 🤝 Credits
|
||||||
|
|
||||||
Entwickelt von **M_Viper**.
|
---
|
||||||
Unterstützt durch BungeeCord und LuckPerms Community.
|
|
||||||
|
## 📜 Lizenz & Nutzung
|
||||||
|
|
||||||
|
Dieses Projekt sowie alle enthaltenen Module, Quelltexte und Ressourcen dürfen **nicht verändert, kopiert oder weiterverbreitet** werden.
|
||||||
|
|
||||||
|
Jegliche Nutzung, Modifikation oder Weitergabe – ganz oder teilweise – ist **ausschließlich mit vorheriger schriftlicher Genehmigung** des Entwicklers gestattet.
|
||||||
|
|
||||||
|
© Entwickelt von **M_Viper**. Alle Rechte vorbehalten.
|
||||||
|
|
||||||
|
|||||||
102
pom.xml
102
pom.xml
@@ -1,52 +1,52 @@
|
|||||||
<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
|
||||||
https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<groupId>net.viper.bungee</groupId>
|
<groupId>net.viper.bungee</groupId>
|
||||||
<artifactId>StatusAPI</artifactId>
|
<artifactId>StatusAPI</artifactId>
|
||||||
<version>4.0.2</version>
|
<version>4.0.8</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>StatusAPI</name>
|
<name>StatusAPI</name>
|
||||||
<description>BungeeCord Status API Plugin</description>
|
<description>BungeeCord Status API Plugin</description>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>8</maven.compiler.source>
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
<maven.compiler.target>8</maven.compiler.target>
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- BungeeCord API -->
|
<!-- BungeeCord API -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.md-5</groupId>
|
<groupId>net.md-5</groupId>
|
||||||
<artifactId>bungeecord-api</artifactId>
|
<artifactId>bungeecord-api</artifactId>
|
||||||
<version>1.20</version>
|
<version>1.20</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- LuckPerms API (Optional) -->
|
<!-- LuckPerms API (Optional) -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.luckperms</groupId>
|
<groupId>net.luckperms</groupId>
|
||||||
<artifactId>api</artifactId>
|
<artifactId>api</artifactId>
|
||||||
<version>5.4</version>
|
<version>5.4</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<finalName>StatusAPI</finalName>
|
<finalName>StatusAPI</finalName>
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
<resource>
|
<resource>
|
||||||
<directory>src/main/resources</directory>
|
<directory>src/main/resources</directory>
|
||||||
<filtering>false</filtering>
|
<filtering>false</filtering>
|
||||||
</resource>
|
</resource>
|
||||||
</resources>
|
</resources>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package net.viper.status;
|
|
||||||
|
|
||||||
import net.md_5.bungee.api.plugin.Plugin;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
public class FileDownloader {
|
|
||||||
private final Plugin plugin;
|
|
||||||
|
|
||||||
public FileDownloader(Plugin plugin) { this.plugin = plugin; }
|
|
||||||
|
|
||||||
public void downloadFile(String urlString, File destination, Runnable onSuccess) {
|
|
||||||
plugin.getProxy().getScheduler().runAsync(plugin, () -> {
|
|
||||||
BufferedInputStream bufferedInputStream = null;
|
|
||||||
FileOutputStream fileOutputStream = null;
|
|
||||||
try {
|
|
||||||
URL url = new URL(urlString);
|
|
||||||
bufferedInputStream = new BufferedInputStream(url.openStream());
|
|
||||||
fileOutputStream = new FileOutputStream(destination);
|
|
||||||
byte[] buffer = new byte[1024];
|
|
||||||
int count;
|
|
||||||
while ((count = bufferedInputStream.read(buffer, 0, 1024)) != -1) {
|
|
||||||
fileOutputStream.write(buffer, 0, count);
|
|
||||||
}
|
|
||||||
fileOutputStream.close();
|
|
||||||
bufferedInputStream.close();
|
|
||||||
plugin.getProxy().getScheduler().schedule(plugin, onSuccess, 1, java.util.concurrent.TimeUnit.MILLISECONDS);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
plugin.getLogger().warning("Download fehlgeschlagen: " + e.getMessage());
|
|
||||||
if (destination.exists()) destination.delete();
|
|
||||||
} finally {
|
|
||||||
if (fileOutputStream != null) try { fileOutputStream.close(); } catch (IOException ignored) {}
|
|
||||||
if (bufferedInputStream != null) try { bufferedInputStream.close(); } catch (IOException ignored) {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,115 @@
|
|||||||
|
package net.viper.status.modules.AutoMessage;
|
||||||
|
|
||||||
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
|
import net.md_5.bungee.api.plugin.Plugin;
|
||||||
|
import net.viper.status.StatusAPI;
|
||||||
|
import net.viper.status.module.Module;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class AutoMessageModule implements Module {
|
||||||
|
|
||||||
|
private int taskId = -1;
|
||||||
|
|
||||||
|
// Diese Methode fehlte bisher und ist zwingend für das Interface
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "AutoMessage";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable(Plugin plugin) {
|
||||||
|
// Hier casten wir das Plugin-Objekt zu StatusAPI, um an spezifische Methoden zu kommen
|
||||||
|
StatusAPI api = (StatusAPI) plugin;
|
||||||
|
|
||||||
|
// Konfiguration aus der zentralen verify.properties laden
|
||||||
|
Properties props = api.getVerifyProperties();
|
||||||
|
|
||||||
|
boolean enabled = Boolean.parseBoolean(props.getProperty("automessage.enabled", "false"));
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
api.getLogger().info("AutoMessage-Modul ist deaktiviert.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interval in Sekunden einlesen
|
||||||
|
int intervalSeconds;
|
||||||
|
try {
|
||||||
|
intervalSeconds = Integer.parseInt(props.getProperty("automessage.interval", "300"));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
api.getLogger().warning("Ungültiges Intervall für AutoMessage! Nutze Standard (300s).");
|
||||||
|
intervalSeconds = 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dateiname einlesen (Standard: messages.txt)
|
||||||
|
String fileName = props.getProperty("automessage.file", "messages.txt");
|
||||||
|
File messageFile = new File(api.getDataFolder(), fileName);
|
||||||
|
|
||||||
|
if (!messageFile.exists()) {
|
||||||
|
api.getLogger().warning("Die Datei '" + fileName + "' wurde nicht gefunden (" + messageFile.getAbsolutePath() + ")!");
|
||||||
|
api.getLogger().info("Erstelle eine leere Datei '" + fileName + "' als Vorlage...");
|
||||||
|
try {
|
||||||
|
messageFile.createNewFile();
|
||||||
|
} catch (IOException e) {
|
||||||
|
api.getLogger().severe("Konnte Datei nicht erstellen: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nachrichten aus der Datei lesen
|
||||||
|
List<String> messages;
|
||||||
|
try {
|
||||||
|
messages = Files.readAllLines(messageFile.toPath(), StandardCharsets.UTF_8);
|
||||||
|
} catch (IOException e) {
|
||||||
|
api.getLogger().severe("Fehler beim Lesen von '" + fileName + "': " + e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leere Zeilen und Kommentare herausfiltern
|
||||||
|
messages.removeIf(line -> line.trim().isEmpty() || line.trim().startsWith("#"));
|
||||||
|
|
||||||
|
if (messages.isEmpty()) {
|
||||||
|
api.getLogger().warning("Die Datei '" + fileName + "' enthält keine gültigen Nachrichten!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional: Prefix aus Config lesen
|
||||||
|
String prefixRaw = props.getProperty("automessage.prefix", "");
|
||||||
|
String prefix = ChatColor.translateAlternateColorCodes('&', prefixRaw);
|
||||||
|
|
||||||
|
api.getLogger().info("Starte AutoMessage-Task (" + messages.size() + " Nachrichten aus " + fileName + ")");
|
||||||
|
|
||||||
|
// Finaler Index für den Lambda-Ausdruck
|
||||||
|
final int[] currentIndex = {0};
|
||||||
|
|
||||||
|
// Task planen
|
||||||
|
taskId = ProxyServer.getInstance().getScheduler().schedule(plugin, () -> {
|
||||||
|
String msg = messages.get(currentIndex[0]);
|
||||||
|
|
||||||
|
String finalMessage = (prefix.isEmpty() ? "" : prefix + " ") + msg;
|
||||||
|
|
||||||
|
// Nachricht an alle auf dem Proxy senden
|
||||||
|
ProxyServer.getInstance().broadcast(TextComponent.fromLegacy(finalMessage));
|
||||||
|
|
||||||
|
// Index erhöhen und Loop starten
|
||||||
|
currentIndex[0] = (currentIndex[0] + 1) % messages.size();
|
||||||
|
}, intervalSeconds, intervalSeconds, TimeUnit.SECONDS).getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable(Plugin plugin) {
|
||||||
|
if (taskId != -1) {
|
||||||
|
ProxyServer.getInstance().getScheduler().cancel(taskId);
|
||||||
|
taskId = -1;
|
||||||
|
plugin.getLogger().info("AutoMessage-Task gestoppt.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,381 @@
|
|||||||
|
package net.viper.status.modules.broadcast;
|
||||||
|
|
||||||
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
import net.md_5.bungee.api.plugin.Plugin;
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
|
import net.md_5.bungee.api.plugin.Listener;
|
||||||
|
import net.viper.status.module.Module;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BroadcastModule
|
||||||
|
*
|
||||||
|
* Speichert geplante Broadcasts jetzt persistent in 'broadcasts.schedules'.
|
||||||
|
* Beim Neustart werden diese automatisch wieder geladen.
|
||||||
|
*/
|
||||||
|
public class BroadcastModule implements Module, Listener {
|
||||||
|
|
||||||
|
private Plugin plugin;
|
||||||
|
private boolean enabled = true;
|
||||||
|
private String requiredApiKey = "";
|
||||||
|
private String format = "%prefix% %message%";
|
||||||
|
private String fallbackPrefix = "[Broadcast]";
|
||||||
|
private String fallbackPrefixColor = "&c";
|
||||||
|
private String fallbackBracketColor = "&8"; // Neu
|
||||||
|
private String fallbackMessageColor = "&f";
|
||||||
|
|
||||||
|
private final Map<String, ScheduledBroadcast> scheduledByClientId = new ConcurrentHashMap<>();
|
||||||
|
private File schedulesFile;
|
||||||
|
private final SimpleDateFormat dateFormat;
|
||||||
|
|
||||||
|
public BroadcastModule() {
|
||||||
|
dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
|
||||||
|
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "BroadcastModule";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable(Plugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
schedulesFile = new File(plugin.getDataFolder(), "broadcasts.schedules");
|
||||||
|
loadConfig();
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
plugin.getLogger().info("[BroadcastModule] deaktiviert via verify.properties (broadcast.enabled=false)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
plugin.getProxy().getPluginManager().registerListener(plugin, this);
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
|
|
||||||
|
plugin.getLogger().info("[BroadcastModule] aktiviert. Format: " + format);
|
||||||
|
loadSchedules();
|
||||||
|
plugin.getProxy().getScheduler().schedule(plugin, this::processScheduled, 1, 1, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable(Plugin plugin) {
|
||||||
|
plugin.getLogger().info("[BroadcastModule] deaktiviert.");
|
||||||
|
saveSchedules();
|
||||||
|
scheduledByClientId.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadConfig() {
|
||||||
|
File file = new File(plugin.getDataFolder(), "verify.properties");
|
||||||
|
if (!file.exists()) {
|
||||||
|
enabled = true;
|
||||||
|
requiredApiKey = "";
|
||||||
|
format = "%prefix% %message%";
|
||||||
|
fallbackPrefix = "[Broadcast]";
|
||||||
|
fallbackPrefixColor = "&c";
|
||||||
|
fallbackBracketColor = "&8"; // Neu
|
||||||
|
fallbackMessageColor = "&f";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (InputStream in = new FileInputStream(file)) {
|
||||||
|
Properties props = new Properties();
|
||||||
|
props.load(new InputStreamReader(in, StandardCharsets.UTF_8));
|
||||||
|
enabled = Boolean.parseBoolean(props.getProperty("broadcast.enabled", "true"));
|
||||||
|
requiredApiKey = props.getProperty("broadcast.api_key", "").trim();
|
||||||
|
format = props.getProperty("broadcast.format", format).trim();
|
||||||
|
if (format.isEmpty()) format = "%prefix% %message%";
|
||||||
|
fallbackPrefix = props.getProperty("broadcast.prefix", fallbackPrefix).trim();
|
||||||
|
fallbackPrefixColor = props.getProperty("broadcast.prefix-color", fallbackPrefixColor).trim();
|
||||||
|
fallbackBracketColor = props.getProperty("broadcast.bracket-color", fallbackBracketColor).trim(); // Neu
|
||||||
|
fallbackMessageColor = props.getProperty("broadcast.message-color", fallbackMessageColor).trim();
|
||||||
|
} catch (IOException e) {
|
||||||
|
plugin.getLogger().warning("[BroadcastModule] Fehler beim Laden von verify.properties: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean handleBroadcast(String sourceName, String message, String type, String apiKeyHeader,
|
||||||
|
String prefix, String prefixColor, String bracketColor, String messageColor) {
|
||||||
|
loadConfig();
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
plugin.getLogger().info("[BroadcastModule] Broadcast abgelehnt: Modul ist deaktiviert.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requiredApiKey != null && !requiredApiKey.isEmpty()) {
|
||||||
|
if (apiKeyHeader == null || !requiredApiKey.equals(apiKeyHeader)) {
|
||||||
|
plugin.getLogger().warning("[BroadcastModule] Broadcast abgelehnt: API-Key fehlt oder inkorrekt.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message == null) message = "";
|
||||||
|
if (sourceName == null || sourceName.isEmpty()) sourceName = "System";
|
||||||
|
if (type == null) type = "global";
|
||||||
|
|
||||||
|
String usedPrefix = (prefix != null && !prefix.trim().isEmpty()) ? prefix.trim() : fallbackPrefix;
|
||||||
|
String usedPrefixColor = (prefixColor != null && !prefixColor.trim().isEmpty()) ? prefixColor.trim() : fallbackPrefixColor;
|
||||||
|
String usedBracketColor = (bracketColor != null && !bracketColor.trim().isEmpty()) ? bracketColor.trim() : fallbackBracketColor; // Neu
|
||||||
|
String usedMessageColor = (messageColor != null && !messageColor.trim().isEmpty()) ? messageColor.trim() : fallbackMessageColor;
|
||||||
|
|
||||||
|
String prefixColorCode = normalizeColorCode(usedPrefixColor);
|
||||||
|
String bracketColorCode = normalizeColorCode(usedBracketColor); // Neu
|
||||||
|
String messageColorCode = normalizeColorCode(usedMessageColor);
|
||||||
|
|
||||||
|
// --- KLAMMER LOGIK ---
|
||||||
|
String finalPrefix;
|
||||||
|
|
||||||
|
// Wenn eine Klammerfarbe gesetzt ist, bauen wir den Prefix neu zusammen
|
||||||
|
// Format: [BracketColor][ [PrefixColor]Text [BracketColor]]
|
||||||
|
if (!bracketColorCode.isEmpty()) {
|
||||||
|
String textContent = usedPrefix;
|
||||||
|
// Entferne manuelle Klammern, falls der User [Broadcast] in das Textfeld geschrieben hat
|
||||||
|
if (textContent.startsWith("[")) textContent = textContent.substring(1);
|
||||||
|
if (textContent.endsWith("]")) textContent = textContent.substring(0, textContent.length() - 1);
|
||||||
|
|
||||||
|
finalPrefix = bracketColorCode + "[" + prefixColorCode + textContent + bracketColorCode + "]" + ChatColor.RESET;
|
||||||
|
} else {
|
||||||
|
// Altes Verhalten: Ganzen String einfärben
|
||||||
|
finalPrefix = prefixColorCode + usedPrefix + ChatColor.RESET;
|
||||||
|
}
|
||||||
|
// ---------------------
|
||||||
|
|
||||||
|
String coloredMessage = (messageColorCode.isEmpty() ? "" : messageColorCode) + message;
|
||||||
|
|
||||||
|
String out = format.replace("%name%", sourceName)
|
||||||
|
.replace("%prefix%", finalPrefix) // Neu verwendete Variable
|
||||||
|
.replace("%prefixColored%", finalPrefix) // Fallback
|
||||||
|
.replace("%message%", message)
|
||||||
|
.replace("%messageColored%", coloredMessage)
|
||||||
|
.replace("%type%", type);
|
||||||
|
|
||||||
|
if (!out.contains("%prefixColored%") && !out.contains("%messageColored%") && !out.contains("%prefix%") && !out.contains("%message%")) {
|
||||||
|
out = finalPrefix + " " + coloredMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextComponent tc = new TextComponent(out);
|
||||||
|
int sent = 0;
|
||||||
|
for (ProxiedPlayer p : plugin.getProxy().getPlayers()) {
|
||||||
|
try { p.sendMessage(tc); sent++; } catch (Throwable ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin.getLogger().info("[BroadcastModule] Broadcast gesendet (Empfänger=" + sent + "): " + message);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeColorCode(String code) {
|
||||||
|
if (code == null) return "";
|
||||||
|
code = code.trim();
|
||||||
|
if (code.isEmpty()) return "";
|
||||||
|
return code.contains("&") ? ChatColor.translateAlternateColorCodes('&', code) : code;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveSchedules() {
|
||||||
|
Properties props = new Properties();
|
||||||
|
for (Map.Entry<String, ScheduledBroadcast> entry : scheduledByClientId.entrySet()) {
|
||||||
|
String id = entry.getKey();
|
||||||
|
ScheduledBroadcast sb = entry.getValue();
|
||||||
|
props.setProperty(id + ".nextRunMillis", String.valueOf(sb.nextRunMillis));
|
||||||
|
props.setProperty(id + ".sourceName", sb.sourceName);
|
||||||
|
props.setProperty(id + ".message", sb.message);
|
||||||
|
props.setProperty(id + ".type", sb.type);
|
||||||
|
props.setProperty(id + ".prefix", sb.prefix);
|
||||||
|
props.setProperty(id + ".prefixColor", sb.prefixColor);
|
||||||
|
props.setProperty(id + ".bracketColor", sb.bracketColor); // Neu
|
||||||
|
props.setProperty(id + ".messageColor", sb.messageColor);
|
||||||
|
props.setProperty(id + ".recur", sb.recur);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (OutputStream out = new FileOutputStream(schedulesFile)) {
|
||||||
|
props.store(out, "PulseCast Scheduled Broadcasts");
|
||||||
|
} catch (IOException e) {
|
||||||
|
plugin.getLogger().severe("[BroadcastModule] Konnte Schedules nicht speichern: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadSchedules() {
|
||||||
|
if (!schedulesFile.exists()) {
|
||||||
|
plugin.getLogger().info("[BroadcastModule] Keine bestehenden Schedules gefunden (Neustart).");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Properties props = new Properties();
|
||||||
|
try (InputStream in = new FileInputStream(schedulesFile)) {
|
||||||
|
props.load(in);
|
||||||
|
} catch (IOException e) {
|
||||||
|
plugin.getLogger().severe("[BroadcastModule] Konnte Schedules nicht laden: " + e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, ScheduledBroadcast> loaded = new HashMap<>();
|
||||||
|
for (String key : props.stringPropertyNames()) {
|
||||||
|
if (!key.contains(".")) continue;
|
||||||
|
String[] parts = key.split("\\.");
|
||||||
|
if (parts.length != 2) continue;
|
||||||
|
|
||||||
|
String id = parts[0];
|
||||||
|
String field = parts[1];
|
||||||
|
String value = props.getProperty(key);
|
||||||
|
|
||||||
|
ScheduledBroadcast sb = loaded.get(id);
|
||||||
|
if (sb == null) {
|
||||||
|
sb = new ScheduledBroadcast(id, 0, "", "", "", "", "", "", "", ""); // Ein leerer String mehr für Bracket
|
||||||
|
loaded.put(id, sb);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (field) {
|
||||||
|
case "nextRunMillis":
|
||||||
|
try { sb.nextRunMillis = Long.parseLong(value); } catch (NumberFormatException ignored) {}
|
||||||
|
break;
|
||||||
|
case "sourceName": sb.sourceName = value; break;
|
||||||
|
case "message": sb.message = value; break;
|
||||||
|
case "type": sb.type = value; break;
|
||||||
|
case "prefix": sb.prefix = value; break;
|
||||||
|
case "prefixColor": sb.prefixColor = value; break;
|
||||||
|
case "bracketColor": sb.bracketColor = value; break; // Neu
|
||||||
|
case "messageColor": sb.messageColor = value; break;
|
||||||
|
case "recur": sb.recur = value; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scheduledByClientId.putAll(loaded);
|
||||||
|
plugin.getLogger().info("[BroadcastModule] " + loaded.size() + " geplante Broadcasts aus Datei wiederhergestellt.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean scheduleBroadcast(long timestampMillis, String sourceName, String message, String type,
|
||||||
|
String apiKeyHeader, String prefix, String prefixColor, String bracketColor, String messageColor,
|
||||||
|
String recur, String clientScheduleId) {
|
||||||
|
loadConfig();
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
plugin.getLogger().info("[BroadcastModule] schedule abgelehnt: Modul deaktiviert.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requiredApiKey != null && !requiredApiKey.isEmpty()) {
|
||||||
|
if (apiKeyHeader == null || !requiredApiKey.equals(apiKeyHeader)) {
|
||||||
|
plugin.getLogger().warning("[BroadcastModule] schedule abgelehnt: API-Key fehlt oder inkorrekt.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message == null) message = "";
|
||||||
|
if (sourceName == null || sourceName.isEmpty()) sourceName = "System";
|
||||||
|
if (type == null) type = "global";
|
||||||
|
if (recur == null) recur = "none";
|
||||||
|
|
||||||
|
String id = (clientScheduleId != null && !clientScheduleId.trim().isEmpty()) ? clientScheduleId.trim() : UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
String scheduledTimeStr = dateFormat.format(new Date(timestampMillis));
|
||||||
|
|
||||||
|
plugin.getLogger().info("[BroadcastModule] Neue geplante Nachricht registriert: " + id + " @ " + scheduledTimeStr);
|
||||||
|
|
||||||
|
if (timestampMillis <= now) {
|
||||||
|
plugin.getLogger().warning("[BroadcastModule] Geplante Zeit liegt in der Vergangenheit -> sende sofort!");
|
||||||
|
return handleBroadcast(sourceName, message, type, apiKeyHeader, prefix, prefixColor, bracketColor, messageColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScheduledBroadcast sb = new ScheduledBroadcast(id, timestampMillis, sourceName, message, type, prefix, prefixColor, bracketColor, messageColor, recur);
|
||||||
|
scheduledByClientId.put(id, sb);
|
||||||
|
saveSchedules();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean cancelScheduled(String clientScheduleId) {
|
||||||
|
if (clientScheduleId == null || clientScheduleId.trim().isEmpty()) return false;
|
||||||
|
ScheduledBroadcast removed = scheduledByClientId.remove(clientScheduleId);
|
||||||
|
if (removed != null) {
|
||||||
|
plugin.getLogger().info("[BroadcastModule] Geplante Nachricht abgebrochen: id=" + clientScheduleId);
|
||||||
|
saveSchedules();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processScheduled() {
|
||||||
|
if (scheduledByClientId.isEmpty()) return;
|
||||||
|
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
List<String> toRemove = new ArrayList<>();
|
||||||
|
boolean changed = false;
|
||||||
|
|
||||||
|
for (Map.Entry<String, ScheduledBroadcast> entry : scheduledByClientId.entrySet()) {
|
||||||
|
ScheduledBroadcast sb = entry.getValue();
|
||||||
|
|
||||||
|
if (sb.nextRunMillis <= now) {
|
||||||
|
String timeStr = dateFormat.format(new Date(sb.nextRunMillis));
|
||||||
|
plugin.getLogger().info("[BroadcastModule] ⏰ Sende geplante Nachricht (ID: " + entry.getKey() + ", Zeit: " + timeStr + ")");
|
||||||
|
|
||||||
|
handleBroadcast(sb.sourceName, sb.message, sb.type, "", sb.prefix, sb.prefixColor, sb.bracketColor, sb.messageColor);
|
||||||
|
|
||||||
|
if (!"none".equalsIgnoreCase(sb.recur)) {
|
||||||
|
long next = computeNextMillis(sb.nextRunMillis, sb.recur);
|
||||||
|
if (next > 0) {
|
||||||
|
sb.nextRunMillis = next;
|
||||||
|
String nextTimeStr = dateFormat.format(new Date(next));
|
||||||
|
plugin.getLogger().info("[BroadcastModule] Nächste Wiederholung (" + sb.recur + "): " + nextTimeStr);
|
||||||
|
changed = true;
|
||||||
|
} else {
|
||||||
|
toRemove.add(entry.getKey());
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toRemove.add(entry.getKey());
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed || !toRemove.isEmpty()) {
|
||||||
|
for (String k : toRemove) {
|
||||||
|
scheduledByClientId.remove(k);
|
||||||
|
plugin.getLogger().info("[BroadcastModule] Schedule entfernt: " + k);
|
||||||
|
}
|
||||||
|
saveSchedules();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long computeNextMillis(long currentMillis, String recur) {
|
||||||
|
switch (recur.toLowerCase(Locale.ROOT)) {
|
||||||
|
case "hourly": return currentMillis + TimeUnit.HOURS.toMillis(1);
|
||||||
|
case "daily": return currentMillis + TimeUnit.DAYS.toMillis(1);
|
||||||
|
case "weekly": return currentMillis + TimeUnit.DAYS.toMillis(7);
|
||||||
|
default: return -1L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ScheduledBroadcast {
|
||||||
|
final String clientId;
|
||||||
|
long nextRunMillis;
|
||||||
|
String sourceName;
|
||||||
|
String message;
|
||||||
|
String type;
|
||||||
|
String prefix;
|
||||||
|
String prefixColor;
|
||||||
|
String bracketColor; // Neu
|
||||||
|
String messageColor;
|
||||||
|
String recur;
|
||||||
|
|
||||||
|
ScheduledBroadcast(String clientId, long nextRunMillis, String sourceName, String message, String type,
|
||||||
|
String prefix, String prefixColor, String bracketColor, String messageColor, String recur) {
|
||||||
|
this.clientId = clientId;
|
||||||
|
this.nextRunMillis = nextRunMillis;
|
||||||
|
this.sourceName = sourceName;
|
||||||
|
this.message = message;
|
||||||
|
this.type = type;
|
||||||
|
this.prefix = prefix;
|
||||||
|
this.prefixColor = prefixColor;
|
||||||
|
this.bracketColor = bracketColor; // Neu
|
||||||
|
this.messageColor = messageColor;
|
||||||
|
this.recur = (recur == null ? "none" : recur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
package net.viper.status.modules.commandblocker;
|
||||||
|
|
||||||
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
import net.md_5.bungee.api.CommandSender;
|
||||||
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
import net.md_5.bungee.api.event.ChatEvent;
|
||||||
|
import net.md_5.bungee.api.plugin.Listener;
|
||||||
|
import net.md_5.bungee.api.plugin.Plugin;
|
||||||
|
import net.md_5.bungee.event.EventHandler;
|
||||||
|
import net.viper.status.StatusAPI;
|
||||||
|
import net.viper.status.module.Module;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
|
public class CommandBlockerModule implements Module, Listener {
|
||||||
|
|
||||||
|
private StatusAPI plugin;
|
||||||
|
private boolean enabled = true; // Standardmäßig aktiv
|
||||||
|
private String bypassPermission = "commandblocker.bypass"; // Standard Permission
|
||||||
|
|
||||||
|
private File file;
|
||||||
|
private Set<String> blocked = new HashSet<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "CommandBlockerModule";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable(Plugin plugin) {
|
||||||
|
if (!(plugin instanceof StatusAPI)) return; // Sicherheit
|
||||||
|
this.plugin = (StatusAPI) plugin;
|
||||||
|
|
||||||
|
// Datei laden
|
||||||
|
file = new File(this.plugin.getDataFolder(), "blocked-commands.yml");
|
||||||
|
loadFile();
|
||||||
|
|
||||||
|
// Listener registrieren
|
||||||
|
ProxyServer.getInstance().getPluginManager().registerListener(this.plugin, this);
|
||||||
|
|
||||||
|
// /cb Befehl registrieren
|
||||||
|
ProxyServer.getInstance().getPluginManager().registerCommand(this.plugin,
|
||||||
|
new net.md_5.bungee.api.plugin.Command("cb", "commandblocker.admin") {
|
||||||
|
@Override
|
||||||
|
public void execute(CommandSender sender, String[] args) {
|
||||||
|
handleCommand(sender, args);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.plugin.getLogger().info("[CommandBlocker] aktiviert (" + blocked.size() + " Commands).");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable(Plugin plugin) {
|
||||||
|
blocked.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onCommand(ChatEvent event) {
|
||||||
|
if (!enabled || !event.isCommand()) return;
|
||||||
|
|
||||||
|
if (!(event.getSender() instanceof ProxiedPlayer)) return;
|
||||||
|
ProxiedPlayer player = (ProxiedPlayer) event.getSender();
|
||||||
|
|
||||||
|
if (player.hasPermission(bypassPermission)) return;
|
||||||
|
|
||||||
|
String msg = event.getMessage();
|
||||||
|
if (msg == null || msg.length() <= 1) return;
|
||||||
|
|
||||||
|
String cmd = msg.substring(1).toLowerCase(Locale.ROOT);
|
||||||
|
String base = cmd.split(" ")[0];
|
||||||
|
|
||||||
|
if (blocked.contains(base)) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
player.sendMessage(ChatColor.RED + "Dieser Befehl ist auf diesem Netzwerk blockiert.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleCommand(CommandSender sender, String[] args) {
|
||||||
|
if (args == null || args.length == 0) {
|
||||||
|
sender.sendMessage(ChatColor.YELLOW + "/cb add <command>");
|
||||||
|
sender.sendMessage(ChatColor.YELLOW + "/cb remove <command>");
|
||||||
|
sender.sendMessage(ChatColor.YELLOW + "/cb list");
|
||||||
|
sender.sendMessage(ChatColor.YELLOW + "/cb reload");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String action = args[0].toLowerCase();
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case "add":
|
||||||
|
if (args.length < 2) break;
|
||||||
|
blocked.add(args[1].toLowerCase());
|
||||||
|
saveFile();
|
||||||
|
sender.sendMessage(ChatColor.GREEN + "Command blockiert: " + args[1]);
|
||||||
|
break;
|
||||||
|
case "remove":
|
||||||
|
if (args.length < 2) break;
|
||||||
|
blocked.remove(args[1].toLowerCase());
|
||||||
|
saveFile();
|
||||||
|
sender.sendMessage(ChatColor.GREEN + "Command freigegeben: " + args[1]);
|
||||||
|
break;
|
||||||
|
case "list":
|
||||||
|
sender.sendMessage(ChatColor.GOLD + "Blockierte Commands:");
|
||||||
|
for (String c : blocked) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "- " + c);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "reload":
|
||||||
|
loadFile();
|
||||||
|
sender.sendMessage(ChatColor.GREEN + "CommandBlocker neu geladen.");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sender.sendMessage(ChatColor.RED + "Unbekannter Unterbefehl.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadFile() {
|
||||||
|
try {
|
||||||
|
if (!file.exists()) {
|
||||||
|
File parent = file.getParentFile();
|
||||||
|
if (parent != null && !parent.exists()) parent.mkdirs();
|
||||||
|
file.createNewFile();
|
||||||
|
saveFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
Yaml yaml = new Yaml();
|
||||||
|
Map<String, Object> data = null;
|
||||||
|
FileInputStream fis = null;
|
||||||
|
try {
|
||||||
|
fis = new FileInputStream(file);
|
||||||
|
data = yaml.loadAs(fis, Map.class);
|
||||||
|
} finally {
|
||||||
|
if (fis != null) try { fis.close(); } catch (IOException ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
blocked.clear();
|
||||||
|
if (data != null && data.containsKey("blocked")) {
|
||||||
|
Object obj = data.get("blocked");
|
||||||
|
if (obj instanceof List) {
|
||||||
|
List list = (List) obj;
|
||||||
|
for (Object o : list) {
|
||||||
|
if (o != null) blocked.add(String.valueOf(o).toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (plugin != null) plugin.getLogger().severe("[CommandBlocker] Fehler beim Laden: " + e.getMessage());
|
||||||
|
else System.err.println("[CommandBlocker] Fehler beim Laden: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveFile() {
|
||||||
|
try {
|
||||||
|
Yaml yaml = new Yaml();
|
||||||
|
Map<String, Object> out = new LinkedHashMap<>();
|
||||||
|
out.put("blocked", new ArrayList<>(blocked));
|
||||||
|
FileWriter fw = null;
|
||||||
|
try {
|
||||||
|
fw = new FileWriter(file);
|
||||||
|
yaml.dump(out, fw);
|
||||||
|
} finally {
|
||||||
|
if (fw != null) try { fw.close(); } catch (IOException ignored) {}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (plugin != null) plugin.getLogger().severe("[CommandBlocker] Fehler beim Speichern: " + e.getMessage());
|
||||||
|
else System.err.println("[CommandBlocker] Fehler beim Speichern: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
package net.viper.status.modules.customcommands;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.CopyOption;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
import net.md_5.bungee.api.CommandSender;
|
||||||
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
|
import net.md_5.bungee.api.config.ServerInfo;
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
import net.md_5.bungee.api.event.ChatEvent;
|
||||||
|
import net.md_5.bungee.api.plugin.Command;
|
||||||
|
import net.md_5.bungee.api.plugin.Listener;
|
||||||
|
import net.md_5.bungee.api.plugin.Plugin; // Import für das Interface Argument
|
||||||
|
import net.md_5.bungee.config.Configuration;
|
||||||
|
import net.md_5.bungee.config.ConfigurationProvider;
|
||||||
|
import net.md_5.bungee.config.YamlConfiguration;
|
||||||
|
import net.md_5.bungee.event.EventHandler;
|
||||||
|
import net.viper.status.StatusAPI;
|
||||||
|
import net.viper.status.module.Module;
|
||||||
|
|
||||||
|
public class CustomCommandModule implements Module, Listener {
|
||||||
|
|
||||||
|
private StatusAPI plugin;
|
||||||
|
private Configuration config;
|
||||||
|
private Command chatCommand;
|
||||||
|
|
||||||
|
public CustomCommandModule() {
|
||||||
|
// Leerer Konstruktor
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "CustomCommandModule";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable(Plugin plugin) {
|
||||||
|
// Hier casten wir 'Plugin' zu 'StatusAPI', da wir wissen, dass es das ist
|
||||||
|
this.plugin = (StatusAPI) plugin;
|
||||||
|
|
||||||
|
this.plugin.getLogger().info("Lade CustomCommandModule...");
|
||||||
|
reloadConfig();
|
||||||
|
|
||||||
|
// /bcmds Reload Befehl registrieren
|
||||||
|
this.plugin.getProxy().getPluginManager().registerCommand(this.plugin, new Command("bcmds") {
|
||||||
|
@Override
|
||||||
|
public void execute(CommandSender sender, String[] args) {
|
||||||
|
if (!sender.hasPermission("statusapi.bcmds")) {
|
||||||
|
sender.sendMessage(new TextComponent(ChatColor.RED + "You don't have permission."));
|
||||||
|
} else {
|
||||||
|
reloadConfig();
|
||||||
|
sender.sendMessage(new TextComponent(ChatColor.GREEN + "Config reloaded."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// /chat Befehl registrieren (falls aktiviert)
|
||||||
|
if (config.getBoolean("chat-command", true)) {
|
||||||
|
chatCommand = new Command("chat") {
|
||||||
|
@Override
|
||||||
|
public void execute(CommandSender sender, String[] args) {
|
||||||
|
if (sender instanceof ProxiedPlayer) {
|
||||||
|
ProxiedPlayer player = (ProxiedPlayer) sender;
|
||||||
|
String msg = String.join(" ", args);
|
||||||
|
ChatEvent e = new ChatEvent(player, player.getServer(), msg);
|
||||||
|
ProxyServer.getInstance().getPluginManager().callEvent(e);
|
||||||
|
if (!e.isCancelled()) {
|
||||||
|
if (!e.isCommand() || !ProxyServer.getInstance().getPluginManager().dispatchCommand(sender, msg.substring(1))) {
|
||||||
|
player.chat(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String msg = String.join(" ", args);
|
||||||
|
if(msg.startsWith("/")) {
|
||||||
|
ProxyServer.getInstance().getPluginManager().dispatchCommand(sender, msg.substring(1));
|
||||||
|
} else {
|
||||||
|
sender.sendMessage(new TextComponent("Console cannot send chat messages via /chat usually."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.plugin.getProxy().getPluginManager().registerCommand(this.plugin, chatCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.plugin.getProxy().getPluginManager().registerListener(this.plugin, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable(Plugin plugin) {
|
||||||
|
// Optional: Cleanup logic, falls nötig.
|
||||||
|
// Wir nutzen hier das übergebene 'plugin' Argument (oder this.plugin, ist egal)
|
||||||
|
// Listener und Commands werden automatisch entfernt, wenn das Plugin stoppt.
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reloadConfig() {
|
||||||
|
try {
|
||||||
|
if (!this.plugin.getDataFolder().exists()) {
|
||||||
|
this.plugin.getDataFolder().mkdirs();
|
||||||
|
}
|
||||||
|
File file = new File(this.plugin.getDataFolder(), "customcommands.yml");
|
||||||
|
if (!file.exists()) {
|
||||||
|
// Kopieren aus Resources
|
||||||
|
Files.copy(this.plugin.getResourceAsStream("customcommands.yml"), file.toPath(), new CopyOption[0]);
|
||||||
|
}
|
||||||
|
this.config = ConfigurationProvider.getProvider(YamlConfiguration.class).load(file);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
this.plugin.getLogger().severe("Konnte customcommands.yml nicht laden!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = 64)
|
||||||
|
public void onCommand(ChatEvent e) {
|
||||||
|
if (!e.isCommand()) return;
|
||||||
|
if (!(e.getSender() instanceof ProxiedPlayer)) return;
|
||||||
|
|
||||||
|
final ProxiedPlayer player = (ProxiedPlayer) e.getSender();
|
||||||
|
String[] split = e.getMessage().split(" ");
|
||||||
|
String label = split[0].substring(1);
|
||||||
|
|
||||||
|
final List<String> args = new ArrayList<>(Arrays.asList(split));
|
||||||
|
args.remove(0);
|
||||||
|
|
||||||
|
Configuration cmds = config.getSection("commands");
|
||||||
|
if (cmds == null) return;
|
||||||
|
|
||||||
|
Configuration section = null;
|
||||||
|
String foundKey = null;
|
||||||
|
|
||||||
|
for (String key : cmds.getKeys()) {
|
||||||
|
Configuration cmdSection = cmds.getSection(key);
|
||||||
|
if (key.equalsIgnoreCase(label)) {
|
||||||
|
section = cmdSection;
|
||||||
|
foundKey = key;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (String alias : cmdSection.getStringList("aliases")) {
|
||||||
|
if (alias.equalsIgnoreCase(label)) {
|
||||||
|
section = cmdSection;
|
||||||
|
foundKey = key;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (section != null) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (section == null) return;
|
||||||
|
|
||||||
|
String type = section.getString("type", "line");
|
||||||
|
String sendertype = section.getString("sender", "default");
|
||||||
|
String permission = section.getString("permission", "");
|
||||||
|
final List<String> commands = section.getStringList("commands");
|
||||||
|
|
||||||
|
if (!permission.isEmpty() && !player.hasPermission(permission)) {
|
||||||
|
player.sendMessage(new TextComponent(ChatColor.RED + "You don't have permission."));
|
||||||
|
e.setCancelled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.setCancelled(true);
|
||||||
|
|
||||||
|
final CommandSender target;
|
||||||
|
if (sendertype.equals("default")) {
|
||||||
|
target = player;
|
||||||
|
} else if (sendertype.equals("admin")) {
|
||||||
|
target = new ForwardSender(player, true);
|
||||||
|
} else if (sendertype.equals("console")) {
|
||||||
|
target = ProxyServer.getInstance().getConsole();
|
||||||
|
} else {
|
||||||
|
ProxiedPlayer targetPlayer = ProxyServer.getInstance().getPlayer(sendertype);
|
||||||
|
if (targetPlayer == null || !targetPlayer.isConnected()) {
|
||||||
|
player.sendMessage(new TextComponent(ChatColor.RED + "Player " + sendertype + " is not online."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
target = targetPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
String argsString = args.size() >= 1 ? String.join(" ", args) : "";
|
||||||
|
final String finalArgs = argsString;
|
||||||
|
final String senderName = player.getName();
|
||||||
|
|
||||||
|
if (type.equals("random")) {
|
||||||
|
int randomIndex = new Random().nextInt(commands.size());
|
||||||
|
String rawCommand = commands.get(randomIndex);
|
||||||
|
executeCommand(target, rawCommand, finalArgs, senderName);
|
||||||
|
} else if (type.equals("line")) {
|
||||||
|
ProxyServer.getInstance().getScheduler().runAsync(this.plugin, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
for (String rawCommand : commands) {
|
||||||
|
executeCommand(target, rawCommand, finalArgs, senderName);
|
||||||
|
try {
|
||||||
|
Thread.sleep(100L);
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.plugin.getLogger().warning("Unknown type '" + type + "' for command " + foundKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void executeCommand(CommandSender sender, String rawCommand, String args, String playerName) {
|
||||||
|
String parsed = rawCommand
|
||||||
|
.replaceAll("%args%", args)
|
||||||
|
.replaceAll("%sender%", playerName);
|
||||||
|
|
||||||
|
String commandToDispatch = parsed.startsWith("/") ? parsed.substring(1) : parsed;
|
||||||
|
ProxyServer.getInstance().getPluginManager().dispatchCommand(sender, commandToDispatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package net.viper.status.modules.customcommands;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.util.Collection;
|
||||||
|
import net.md_5.bungee.api.CommandSender;
|
||||||
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
|
import net.md_5.bungee.api.connection.Connection;
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
import net.md_5.bungee.api.connection.Connection.Unsafe;
|
||||||
|
|
||||||
|
public class ForwardSender implements CommandSender, Connection {
|
||||||
|
private ProxiedPlayer target;
|
||||||
|
private Boolean admin;
|
||||||
|
|
||||||
|
public ForwardSender(ProxiedPlayer sender, Boolean admin) {
|
||||||
|
this.target = sender;
|
||||||
|
this.admin = admin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProxiedPlayer target() {
|
||||||
|
return this.target;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return this.target.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessage(String message) {
|
||||||
|
this.target.sendMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessages(String... messages) {
|
||||||
|
this.target.sendMessages(messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessage(BaseComponent... message) {
|
||||||
|
this.target.sendMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessage(BaseComponent message) {
|
||||||
|
this.target.sendMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> getGroups() {
|
||||||
|
return this.target.getGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addGroups(String... groups) {
|
||||||
|
this.target.addGroups(groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeGroups(String... groups) {
|
||||||
|
this.target.removeGroups(groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(String permission) {
|
||||||
|
return this.admin ? true : this.target.hasPermission(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPermission(String permission, boolean value) {
|
||||||
|
this.target.setPermission(permission, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> getPermissions() {
|
||||||
|
Collection<String> perms = this.target.getPermissions();
|
||||||
|
if (this.admin) {
|
||||||
|
perms.add("*");
|
||||||
|
}
|
||||||
|
return perms;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InetSocketAddress getAddress() {
|
||||||
|
return this.target.getAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SocketAddress getSocketAddress() {
|
||||||
|
return this.target.getSocketAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnect(String reason) {
|
||||||
|
this.target.disconnect(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnect(BaseComponent... reason) {
|
||||||
|
this.target.disconnect(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnect(BaseComponent reason) {
|
||||||
|
this.target.disconnect(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConnected() {
|
||||||
|
return this.target.isConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Unsafe unsafe() {
|
||||||
|
return this.target.unsafe();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
package net.viper.status.modules.globalchat;
|
package net.viper.status.modules.globalchat;
|
||||||
|
|
||||||
import net.luckperms.api.LuckPerms;
|
|
||||||
import net.luckperms.api.LuckPermsProvider;
|
|
||||||
import net.luckperms.api.model.user.User;
|
|
||||||
import net.luckperms.api.cacheddata.CachedMetaData;
|
|
||||||
import net.md_5.bungee.api.ChatColor;
|
import net.md_5.bungee.api.ChatColor;
|
||||||
import net.md_5.bungee.api.CommandSender;
|
import net.md_5.bungee.api.CommandSender;
|
||||||
import net.md_5.bungee.api.chat.ComponentBuilder;
|
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||||
@@ -13,16 +9,20 @@ import net.md_5.bungee.api.chat.HoverEvent.Action;
|
|||||||
import net.md_5.bungee.api.config.ServerInfo;
|
import net.md_5.bungee.api.config.ServerInfo;
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
import net.md_5.bungee.api.event.ChatEvent;
|
import net.md_5.bungee.api.event.ChatEvent;
|
||||||
|
import net.md_5.bungee.api.event.PostLoginEvent;
|
||||||
|
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||||
import net.md_5.bungee.api.event.ServerConnectEvent;
|
import net.md_5.bungee.api.event.ServerConnectEvent;
|
||||||
import net.md_5.bungee.api.event.ServerSwitchEvent;
|
import net.md_5.bungee.api.event.ServerSwitchEvent;
|
||||||
import net.md_5.bungee.api.plugin.Command;
|
import net.md_5.bungee.api.plugin.Command;
|
||||||
import net.md_5.bungee.api.plugin.Listener;
|
import net.md_5.bungee.api.plugin.Listener;
|
||||||
import net.md_5.bungee.api.plugin.Plugin;
|
import net.md_5.bungee.api.plugin.Plugin;
|
||||||
import net.md_5.bungee.chat.ComponentSerializer; // Wichtig für JSON
|
import net.md_5.bungee.chat.ComponentSerializer;
|
||||||
import net.md_5.bungee.event.EventHandler;
|
import net.md_5.bungee.event.EventHandler;
|
||||||
import net.viper.status.module.Module;
|
import net.viper.status.module.Module;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.lang.Class;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
@@ -33,30 +33,34 @@ import java.util.regex.Pattern;
|
|||||||
/**
|
/**
|
||||||
* GlobalChatModule - Integriert Global Chat, Filter, Logs und Support in die StatusAPI.
|
* GlobalChatModule - Integriert Global Chat, Filter, Logs und Support in die StatusAPI.
|
||||||
* Nutzt die zentrale verify.properties für Chat- und Server-Einstellungen.
|
* Nutzt die zentrale verify.properties für Chat- und Server-Einstellungen.
|
||||||
* Sendet Nachrichten via Plugin Channel an Backend-Server, um Signatur-Probleme zu vermeiden.
|
* Ermöglicht manuelle Ränge über UUID-Overrides.
|
||||||
|
* Nutzt REFLECTION für LuckPerms (Optional).
|
||||||
*/
|
*/
|
||||||
public class GlobalChatModule implements Module, Listener {
|
public class GlobalChatModule implements Module, Listener {
|
||||||
|
|
||||||
private static final String CHANNEL_CONTROL = "global:control";
|
private static final String CHANNEL_CONTROL = "global:control";
|
||||||
private static final String CHANNEL_CHAT = "global:chat"; // NEU: Kanal für Chat-Relay
|
private static final String CHANNEL_CHAT = "global:chat";
|
||||||
|
|
||||||
private Plugin plugin;
|
private Plugin plugin;
|
||||||
|
|
||||||
private List<String> badWords = new ArrayList<>();
|
private List<String> badWords = new ArrayList<>();
|
||||||
private File logFolder;
|
private File logFolder;
|
||||||
private boolean chatMuted = false;
|
private boolean chatMuted = false;
|
||||||
private boolean isChatEnabled = true; // Status ob der Chat aktiv ist
|
private boolean isChatEnabled = true;
|
||||||
|
|
||||||
private final Map<UUID, Boolean> playerIsOp = new ConcurrentHashMap<>();
|
private final Map<UUID, Boolean> playerIsOp = new ConcurrentHashMap<>();
|
||||||
private final Map<UUID, UUID> lastSupportContact = new ConcurrentHashMap<>();
|
private final Map<UUID, UUID> lastSupportContact = new ConcurrentHashMap<>();
|
||||||
private final Set<UUID> suppressJoinQuit = ConcurrentHashMap.newKeySet();
|
private final Set<UUID> suppressJoinQuit = ConcurrentHashMap.newKeySet();
|
||||||
private final Set<UUID> chatLockPlayers = ConcurrentHashMap.newKeySet(); // Toggle Chat List
|
private final Set<UUID> chatLockPlayers = ConcurrentHashMap.newKeySet();
|
||||||
|
|
||||||
private List<String> welcomeMessages = new ArrayList<>();
|
private List<String> welcomeMessages = new ArrayList<>();
|
||||||
private Map<String, String> serverDisplayNames = new HashMap<>();
|
private Map<String, String> serverDisplayNames = new HashMap<>();
|
||||||
|
|
||||||
// NEU: Map für gruppen-spezifische Formate aus der verify.properties
|
// Map für gruppen-spezifische Formate aus der verify.properties
|
||||||
private Map<String, String> groupFormats = new HashMap<>();
|
private Map<String, String> groupFormats = new HashMap<>();
|
||||||
|
|
||||||
|
// NEU: Map für manuelle Ränge aus der verify.properties (Override)
|
||||||
|
private final Map<UUID, String> manualRanks = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@@ -70,7 +74,7 @@ public class GlobalChatModule implements Module, Listener {
|
|||||||
loadConfig();
|
loadConfig();
|
||||||
|
|
||||||
if (!isChatEnabled) {
|
if (!isChatEnabled) {
|
||||||
plugin.getLogger().info("§eGlobalChat ist in der verify.properties DEAKTIVIERT.");
|
plugin.getLogger().info("§eGlobalChat ist in der verify.properties DEAKTIVERT.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +87,6 @@ public class GlobalChatModule implements Module, Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
plugin.getProxy().getPluginManager().registerListener(plugin, this);
|
plugin.getProxy().getPluginManager().registerListener(plugin, this);
|
||||||
|
|
||||||
loadFilter();
|
loadFilter();
|
||||||
loadWelcomeMessages();
|
loadWelcomeMessages();
|
||||||
|
|
||||||
@@ -98,8 +101,11 @@ public class GlobalChatModule implements Module, Listener {
|
|||||||
plugin.getProxy().getPluginManager().registerCommand(plugin, new ReplyCommand());
|
plugin.getProxy().getPluginManager().registerCommand(plugin, new ReplyCommand());
|
||||||
plugin.getProxy().getPluginManager().registerCommand(plugin, new InfoCommand());
|
plugin.getProxy().getPluginManager().registerCommand(plugin, new InfoCommand());
|
||||||
plugin.getProxy().getPluginManager().registerCommand(plugin, new ChatToggleCommand());
|
plugin.getProxy().getPluginManager().registerCommand(plugin, new ChatToggleCommand());
|
||||||
|
|
||||||
|
// NEU: ClearChat Befehl registrieren
|
||||||
|
plugin.getProxy().getPluginManager().registerCommand(plugin, new ClearChatCommand());
|
||||||
|
|
||||||
plugin.getLogger().info("§aGlobalChatModule aktiviert (Relay-Mode aktiv)!");
|
plugin.getLogger().info("§aGlobalChatModule aktiviert (Mit Manuellen Rang-Overrides)!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -148,26 +154,41 @@ public class GlobalChatModule implements Module, Listener {
|
|||||||
isChatEnabled = Boolean.parseBoolean(props.getProperty("chat.enabled", "true"));
|
isChatEnabled = Boolean.parseBoolean(props.getProperty("chat.enabled", "true"));
|
||||||
|
|
||||||
serverDisplayNames.clear();
|
serverDisplayNames.clear();
|
||||||
// NEU: Gruppen-Formate laden
|
|
||||||
groupFormats.clear();
|
groupFormats.clear();
|
||||||
|
manualRanks.clear(); // NEU
|
||||||
|
|
||||||
for (String key : props.stringPropertyNames()) {
|
for (String key : props.stringPropertyNames()) {
|
||||||
if (key.startsWith("server.")) {
|
// 1. Manuelle Overrides laden (Höchste Priorität!)
|
||||||
|
if (key.startsWith("override.")) {
|
||||||
|
String uuidStr = key.substring("override.".length());
|
||||||
|
try {
|
||||||
|
UUID uuid = UUID.fromString(uuidStr);
|
||||||
|
String groupName = props.getProperty(key);
|
||||||
|
manualRanks.put(uuid, groupName);
|
||||||
|
plugin.getLogger().info("Manueller Override geladen: " + uuidStr + " -> " + groupName);
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
plugin.getLogger().warning("Ungültige UUID in override: " + uuidStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 2. Server Aliase
|
||||||
|
else if (key.startsWith("server.")) {
|
||||||
String[] parts = key.split("\\.");
|
String[] parts = key.split("\\.");
|
||||||
if (parts.length == 2) {
|
if (parts.length == 2) {
|
||||||
String serverName = parts[1];
|
String serverName = parts[1];
|
||||||
String displayName = props.getProperty(key);
|
String displayName = props.getProperty(key);
|
||||||
serverDisplayNames.put(serverName, displayName);
|
serverDisplayNames.put(serverName, displayName);
|
||||||
}
|
}
|
||||||
} else if (key.startsWith("groupformat.")) {
|
}
|
||||||
// Format: groupformat.Owner = &c[Owner] || &b || &d
|
// 3. Group Formate
|
||||||
|
else if (key.startsWith("groupformat.")) {
|
||||||
String groupName = key.substring("groupformat.".length());
|
String groupName = key.substring("groupformat.".length());
|
||||||
String format = props.getProperty(key);
|
String format = props.getProperty(key);
|
||||||
groupFormats.put(groupName, format);
|
groupFormats.put(groupName.toLowerCase(), format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
plugin.getLogger().info("§eGeladene Server-Displaynames: " + serverDisplayNames.size() + " (Chat aktiv: " + isChatEnabled + ")");
|
plugin.getLogger().info("§eGeladene Server-Displaynames: " + serverDisplayNames.size() + " (Chat aktiv: " + isChatEnabled + ")");
|
||||||
plugin.getLogger().info("§eGeladene Chat-Formate: " + groupFormats.size());
|
plugin.getLogger().info("§eGeladene Chat-Formate: " + groupFormats.size());
|
||||||
|
plugin.getLogger().info("§eGeladene Manuelle Overrides: " + manualRanks.size());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@@ -220,15 +241,25 @@ public class GlobalChatModule implements Module, Listener {
|
|||||||
|
|
||||||
private void sendRandomWelcomeMessage(ProxiedPlayer player) {
|
private void sendRandomWelcomeMessage(ProxiedPlayer player) {
|
||||||
if (welcomeMessages.isEmpty()) return;
|
if (welcomeMessages.isEmpty()) return;
|
||||||
|
|
||||||
Random rand = new Random();
|
Random rand = new Random();
|
||||||
String message = welcomeMessages.get(rand.nextInt(welcomeMessages.size()));
|
String message = welcomeMessages.get(rand.nextInt(welcomeMessages.size()));
|
||||||
message = message.replace("%player%", player.getName());
|
message = message.replace("%player%", player.getName());
|
||||||
message = ChatColor.translateAlternateColorCodes('&', message);
|
message = ChatColor.translateAlternateColorCodes('&', message);
|
||||||
|
|
||||||
player.sendMessage(new TextComponent(message));
|
player.sendMessage(new TextComponent(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===========================
|
||||||
|
// Global Broadcast Helper
|
||||||
|
// ===========================
|
||||||
|
private void broadcastGlobal(TextComponent component) {
|
||||||
|
if (!isChatEnabled) return;
|
||||||
|
|
||||||
|
// Korrektur: statt PluginChannel-Relay senden wir System/Plugin-Nachrichten an alle Spieler.
|
||||||
|
for (ProxiedPlayer p : plugin.getProxy().getPlayers()) {
|
||||||
|
p.sendMessage(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ===========================
|
// ===========================
|
||||||
// Chatfilter & Global-Chat (RELAY MODE)
|
// Chatfilter & Global-Chat (RELAY MODE)
|
||||||
// ===========================
|
// ===========================
|
||||||
@@ -239,83 +270,59 @@ public class GlobalChatModule implements Module, Listener {
|
|||||||
|
|
||||||
ProxiedPlayer player = (ProxiedPlayer) e.getSender();
|
ProxiedPlayer player = (ProxiedPlayer) e.getSender();
|
||||||
|
|
||||||
// ==================== CHAT LOCK / TOGGLE CHECK ====================
|
// CHAT LOCK / TOGGLE CHECK
|
||||||
if (chatLockPlayers.contains(player.getUniqueId())) {
|
if (chatLockPlayers.contains(player.getUniqueId())) {
|
||||||
return; // Nachricht geht direkt zum Server (z.B. für Lands/Quests)
|
return;
|
||||||
}
|
}
|
||||||
// ========================================================================
|
|
||||||
|
|
||||||
String originalMsg = e.getMessage();
|
String originalMsg = e.getMessage();
|
||||||
|
|
||||||
if (suppressJoinQuit.contains(player.getUniqueId()) &&
|
if (suppressJoinQuit.contains(player.getUniqueId()) &&
|
||||||
(originalMsg.contains("joined the Game") || originalMsg.contains("left the Game"))) {
|
(originalMsg.contains("joined§ Game") || originalMsg.contains("left§ Game"))) {
|
||||||
plugin.getLogger().info("Unterdrücke Join-/Quit-Nachricht für " + player.getName());
|
plugin.getLogger().info("Unterdrücke Join-/Quit-Nachricht für " + player.getName());
|
||||||
e.setCancelled(true);
|
e.setCancelled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Globaler Mute
|
|
||||||
if (chatMuted && !player.hasPermission("globalchat.bypass")) {
|
if (chatMuted && !player.hasPermission("globalchat.bypass")) {
|
||||||
player.sendMessage(new TextComponent("§cDer globale Chat ist derzeit deaktiviert!"));
|
player.sendMessage(new TextComponent("§cDer globale Chat ist derzeit deaktiviert!"));
|
||||||
e.setCancelled(true);
|
e.setCancelled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Badword-Zensur
|
|
||||||
String censoredMsg = originalMsg;
|
String censoredMsg = originalMsg;
|
||||||
for (String bad : badWords) {
|
for (String bad : badWords) {
|
||||||
if (bad == null || bad.trim().isEmpty()) continue;
|
if (bad == null || bad.trim().isEmpty()) continue;
|
||||||
censoredMsg = censoredMsg.replaceAll("(?i)" + Pattern.quote(bad), repeat("*", bad.length()));
|
censoredMsg = censoredMsg.replaceAll("(?i)" + Pattern.quote(bad), repeat("*", bad.length()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event canceln (Wichtig, damit nicht doppelt angezeigt wird)
|
|
||||||
e.setCancelled(true);
|
e.setCancelled(true);
|
||||||
|
|
||||||
String serverName = player.getServer().getInfo().getName();
|
String serverName = player.getServer().getInfo().getName();
|
||||||
String serverDisplay = getDisplayName(serverName);
|
String serverDisplay = getDisplayName(serverName);
|
||||||
|
|
||||||
// Index 0: Prefix (Rang), Index 1: Suffix, Index 2: Spielername Farbe, Index 3: Chat Farbe
|
|
||||||
String[] ps = getPrefixSuffix(player);
|
String[] ps = getPrefixSuffix(player);
|
||||||
String prefix = ps[0];
|
String prefix = ps[0];
|
||||||
String playerColor = ps[2];
|
String playerColor = ps[2];
|
||||||
String chatColor = ps[3];
|
String chatColor = ps[3];
|
||||||
|
|
||||||
String displayTag = "";
|
String displayTag = "";
|
||||||
if (!prefix.isEmpty()) {
|
if (!prefix.isEmpty()) displayTag = prefix;
|
||||||
displayTag = prefix;
|
else if (!ps[1].isEmpty()) displayTag = ps[1];
|
||||||
} else if (!ps[1].isEmpty()) {
|
|
||||||
displayTag = ps[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!displayTag.isEmpty() && !displayTag.endsWith(" ")) displayTag = displayTag + " ";
|
if (!displayTag.isEmpty() && !displayTag.endsWith(" ")) displayTag = displayTag + " ";
|
||||||
|
|
||||||
StringBuilder out = new StringBuilder();
|
StringBuilder out = new StringBuilder();
|
||||||
out.append("§7[").append(serverDisplay).append("§r§7] "); // FIX: Reset vor der Klammer
|
out.append("§7[").append(serverDisplay).append("§r§7] ");
|
||||||
if (!displayTag.isEmpty()) out.append(displayTag);
|
if (!displayTag.isEmpty()) out.append(displayTag);
|
||||||
|
|
||||||
// Spielername mit eigener Farbe
|
|
||||||
out.append(playerColor).append(player.getName());
|
out.append(playerColor).append(player.getName());
|
||||||
|
out.append("§f: ").append(chatColor).append(censoredMsg);
|
||||||
out.append("§f: ").append(chatColor).append(censoredMsg); // Chatnachricht mit eigener Farbe
|
|
||||||
|
|
||||||
String chatOut = out.toString();
|
String chatOut = out.toString();
|
||||||
|
|
||||||
// NEU: Nachricht erstellen und an alle Server senden
|
|
||||||
TextComponent chatComponent = new TextComponent(chatOut);
|
TextComponent chatComponent = new TextComponent(chatOut);
|
||||||
String jsonMessage = ComponentSerializer.toString(chatComponent);
|
broadcastGlobal(chatComponent);
|
||||||
|
|
||||||
for (ServerInfo server : plugin.getProxy().getServers().values()) {
|
|
||||||
// Wir senden nur an Server, die Spieler haben (Performance)
|
|
||||||
if (server.getPlayers().isEmpty()) continue;
|
|
||||||
|
|
||||||
try {
|
|
||||||
server.sendData(CHANNEL_CHAT, jsonMessage.getBytes(StandardCharsets.UTF_8));
|
|
||||||
} catch (Exception ex) {
|
|
||||||
plugin.getLogger().warning("Konnte Chat-Nachricht nicht an Server " + server.getName() + " senden.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loggen (lokal auf Bungee)
|
|
||||||
String logEntry = "[" + serverName + "] " +
|
String logEntry = "[" + serverName + "] " +
|
||||||
(displayTag.isEmpty() ? "" : stripColor(displayTag) + " ") +
|
(displayTag.isEmpty() ? "" : stripColor(displayTag) + " ") +
|
||||||
player.getName() +
|
player.getName() +
|
||||||
@@ -323,6 +330,58 @@ public class GlobalChatModule implements Module, Listener {
|
|||||||
logMessage(logEntry);
|
logMessage(logEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===========================
|
||||||
|
// Global Join & Quit Events
|
||||||
|
// ===========================
|
||||||
|
@EventHandler
|
||||||
|
public void onPostLogin(PostLoginEvent e) {
|
||||||
|
if (!isChatEnabled) return;
|
||||||
|
|
||||||
|
ProxiedPlayer player = e.getPlayer();
|
||||||
|
|
||||||
|
// Willkommensnachricht senden
|
||||||
|
sendRandomWelcomeMessage(player);
|
||||||
|
|
||||||
|
// Formatierung aus verify.properties nutzen
|
||||||
|
String[] ps = getPrefixSuffix(player);
|
||||||
|
String prefix = ps[0];
|
||||||
|
String playerColor = ps[2]; // Namefarbe aus Properties
|
||||||
|
|
||||||
|
String displayTag = "";
|
||||||
|
if (!prefix.isEmpty()) displayTag = prefix;
|
||||||
|
if (!displayTag.isEmpty() && !displayTag.endsWith(" ")) displayTag = displayTag + " ";
|
||||||
|
|
||||||
|
// Nachricht: "Spieler xy hat den Server betreten"
|
||||||
|
// Der Text "hat den Server betreten" bleibt Grün (Standard), Name und Prefix kommen aus der Config
|
||||||
|
TextComponent joinMsg = new TextComponent(displayTag + playerColor + player.getName() + " §a§lhat den Server betreten.");
|
||||||
|
|
||||||
|
broadcastGlobal(joinMsg);
|
||||||
|
logMessage("[JOIN] " + player.getName() + " hat den Server betreten.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerDisconnect(PlayerDisconnectEvent e) {
|
||||||
|
if (!isChatEnabled) return;
|
||||||
|
|
||||||
|
ProxiedPlayer player = e.getPlayer();
|
||||||
|
|
||||||
|
// Formatierung aus verify.properties nutzen
|
||||||
|
String[] ps = getPrefixSuffix(player);
|
||||||
|
String prefix = ps[0];
|
||||||
|
String playerColor = ps[2]; // Namefarbe aus Properties
|
||||||
|
|
||||||
|
String displayTag = "";
|
||||||
|
if (!prefix.isEmpty()) displayTag = prefix;
|
||||||
|
if (!displayTag.isEmpty() && !displayTag.endsWith(" ")) displayTag = displayTag + " ";
|
||||||
|
|
||||||
|
// Nachricht: "Spieler xy hat den Server verlassen"
|
||||||
|
// Der Text "hat den Server verlassen" bleibt Rot (Standard), Name und Prefix kommen aus der Config
|
||||||
|
TextComponent quitMsg = new TextComponent(displayTag + playerColor + player.getName() + " §c§lhat den Server verlassen.");
|
||||||
|
|
||||||
|
broadcastGlobal(quitMsg);
|
||||||
|
logMessage("[QUIT] " + player.getName() + " hat den Server verlassen.");
|
||||||
|
}
|
||||||
|
|
||||||
// ===========================
|
// ===========================
|
||||||
// Server Connect & Switch
|
// Server Connect & Switch
|
||||||
// ===========================
|
// ===========================
|
||||||
@@ -341,14 +400,12 @@ public class GlobalChatModule implements Module, Listener {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
sendSuppressJoinQuit(from, player.getUniqueId());
|
sendSuppressJoinQuit(from, player.getUniqueId());
|
||||||
plugin.getLogger().info("Sent suppress quit message for " + player.getName() + " to server " + from.getName());
|
|
||||||
} catch (Throwable ex) {
|
} catch (Throwable ex) {
|
||||||
plugin.getLogger().warning("Fehler beim Senden der Quit-Unterdrückung: " + ex.getMessage());
|
plugin.getLogger().warning("Fehler beim Senden der Quit-Unterdrückung: " + ex.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
sendSuppressJoinQuit(target, player.getUniqueId());
|
sendSuppressJoinQuit(target, player.getUniqueId());
|
||||||
plugin.getLogger().info("Sent suppress join message for " + player.getName() + " to server " + target.getName());
|
|
||||||
} catch (Throwable ex) {
|
} catch (Throwable ex) {
|
||||||
plugin.getLogger().warning("Fehler beim Senden der Join-Unterdrückung: " + ex.getMessage());
|
plugin.getLogger().warning("Fehler beim Senden der Join-Unterdrückung: " + ex.getMessage());
|
||||||
}
|
}
|
||||||
@@ -370,45 +427,32 @@ public class GlobalChatModule implements Module, Listener {
|
|||||||
|
|
||||||
String fromName = from.getName();
|
String fromName = from.getName();
|
||||||
String fromDisplay = getDisplayName(fromName);
|
String fromDisplay = getDisplayName(fromName);
|
||||||
|
|
||||||
String toName = to.getName();
|
String toName = to.getName();
|
||||||
String toDisplay = getDisplayName(toName);
|
String toDisplay = getDisplayName(toName);
|
||||||
|
|
||||||
// Hier nutzen wir nur das Prefix (Index 0), nicht die Chatfarbe oder Spielerfarbe
|
|
||||||
String[] ps = getPrefixSuffix(player);
|
String[] ps = getPrefixSuffix(player);
|
||||||
String prefix = ps[0];
|
String prefix = ps[0];
|
||||||
String suffix = ps[1];
|
String playerColor = ps[2]; // Namefarbe aus Properties für den Switch
|
||||||
|
|
||||||
String displayTag = "";
|
String displayTag = "";
|
||||||
if (!prefix.isEmpty()) displayTag = prefix;
|
if (!prefix.isEmpty()) displayTag = prefix;
|
||||||
else if (!suffix.isEmpty()) displayTag = suffix;
|
// Suffix wird hier ignoriert, da die || Syntax vorausgesetzt wird, aber es schadet nicht
|
||||||
|
else if (!ps[1].isEmpty()) displayTag = ps[1];
|
||||||
|
|
||||||
if (!displayTag.isEmpty() && !displayTag.endsWith(" ")) displayTag = displayTag + " ";
|
if (!displayTag.isEmpty() && !displayTag.endsWith(" ")) displayTag = displayTag + " ";
|
||||||
|
|
||||||
StringBuilder msg = new StringBuilder();
|
StringBuilder msg = new StringBuilder();
|
||||||
msg.append("§7[").append(toDisplay).append("§r§7] "); // FIX: Reset vor der Klammer
|
msg.append("§7[").append(toDisplay).append("§r§7] ");
|
||||||
if (!displayTag.isEmpty()) msg.append(displayTag);
|
if (!displayTag.isEmpty()) msg.append(displayTag);
|
||||||
|
// Hier wird die playerColor aus der verify.properties angewendet
|
||||||
// Switch Nachricht: Spielername weiß oder Standard
|
msg.append(playerColor).append(player.getName());
|
||||||
msg.append(player.getName());
|
|
||||||
|
|
||||||
msg.append(" §7hat den Server gewechselt: §e")
|
msg.append(" §7hat den Server gewechselt: §e")
|
||||||
.append(fromDisplay).append(" §7→ §e").append(toDisplay).append("§7.");
|
.append(fromDisplay).append(" §7→ §e").append(toDisplay).append("§7.");
|
||||||
|
|
||||||
String finalMsg = msg.toString();
|
String finalMsg = msg.toString();
|
||||||
|
|
||||||
// NEU: Switch Nachricht auch über Relay senden
|
|
||||||
TextComponent switchComponent = new TextComponent(finalMsg);
|
TextComponent switchComponent = new TextComponent(finalMsg);
|
||||||
String jsonMessage = ComponentSerializer.toString(switchComponent);
|
broadcastGlobal(switchComponent);
|
||||||
|
|
||||||
for (ServerInfo server : plugin.getProxy().getServers().values()) {
|
|
||||||
if (server.getPlayers().isEmpty()) continue;
|
|
||||||
try {
|
|
||||||
server.sendData(CHANNEL_CHAT, jsonMessage.getBytes(StandardCharsets.UTF_8));
|
|
||||||
} catch (Exception ex) {
|
|
||||||
plugin.getLogger().warning("Konnte Switch-Nachricht nicht senden.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String logEntry = "[" + toName + "] " +
|
String logEntry = "[" + toName + "] " +
|
||||||
(displayTag.isEmpty() ? "" : stripColor(displayTag) + " ") +
|
(displayTag.isEmpty() ? "" : stripColor(displayTag) + " ") +
|
||||||
@@ -435,79 +479,54 @@ public class GlobalChatModule implements Module, Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ===========================
|
// ===========================
|
||||||
// Prefix/Suffix via LuckPerms & Properties (ROBUST + CHAT COLOR + PLAYER COLOR)
|
// Prefix/Suffix (Logic: Manual > LP > Perm)
|
||||||
// ===========================
|
// ===========================
|
||||||
private String[] getPrefixSuffix(ProxiedPlayer player) {
|
private String[] getPrefixSuffix(ProxiedPlayer player) {
|
||||||
String prefix = "";
|
String prefix = "";
|
||||||
String suffix = "";
|
String suffix = "";
|
||||||
String playerColor = "§f"; // Standard Weiß für Spielername
|
String playerColor = "§f";
|
||||||
String chatColor = "§f"; // Standard Weiß für Chat
|
String chatColor = "§f";
|
||||||
|
|
||||||
// Standard Gruppe falls nichts gefunden wird
|
|
||||||
String groupName = "Spieler";
|
String groupName = "Spieler";
|
||||||
|
|
||||||
// 1. Versuch: Gruppe von LuckPerms holen
|
// 0. NEU: HÖCHSTE PRIO - Manueller Override aus verify.properties
|
||||||
try {
|
if (manualRanks.containsKey(player.getUniqueId())) {
|
||||||
LuckPerms lp = LuckPermsProvider.get();
|
groupName = manualRanks.get(player.getUniqueId());
|
||||||
if (lp != null) {
|
// plugin.getLogger().info("Nutze manuellen Override für " + player.getName() + ": " + groupName);
|
||||||
User user = lp.getUserManager().getUser(player.getUniqueId());
|
}
|
||||||
if (user == null) {
|
// 1. LuckPerms via Reflection (Bungee)
|
||||||
try {
|
else {
|
||||||
// User synchron laden
|
String lpGroup = getLuckPermsGroup(player);
|
||||||
user = lp.getUserManager().loadUser(player.getUniqueId()).join();
|
if (lpGroup != null && !lpGroup.isEmpty() && !lpGroup.equalsIgnoreCase("default")) {
|
||||||
} catch (Exception ignored) {}
|
groupName = lpGroup;
|
||||||
}
|
|
||||||
if (user != null) {
|
|
||||||
String lpGroup = user.getPrimaryGroup();
|
|
||||||
if (lpGroup != null && !lpGroup.isEmpty() && !lpGroup.equalsIgnoreCase("default")) {
|
|
||||||
groupName = lpGroup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (Throwable ignored) {}
|
}
|
||||||
|
|
||||||
// 2. FALLBACK: Permission-Check (Funktioniert auch OHNE LuckPerms Datenbank)
|
// 2. FALLBACK: Permission-Check
|
||||||
if (groupName == null || groupName.equalsIgnoreCase("default") || groupName.equalsIgnoreCase("Spieler")) {
|
if (groupName == null || groupName.equalsIgnoreCase("default") || groupName.equalsIgnoreCase("Spieler")) {
|
||||||
if (player.hasPermission("group.owner")) groupName = "Owner";
|
if (player.hasPermission("group.owner")) groupName = "Owner";
|
||||||
else if (player.hasPermission("group.admin")) groupName = "Admin";
|
else if (player.hasPermission("group.admin")) groupName = "Admin";
|
||||||
else if (player.hasPermission("group.developer")) groupName = "Developer";
|
else if (player.hasPermission("group.developer")) groupName = "Developer";
|
||||||
else if (player.hasPermission("group.premium")) groupName = "Premium";
|
else if (player.hasPermission("group.premium")) groupName = "Premium";
|
||||||
else if (player.hasPermission("group.spieler")) groupName = "Spieler";
|
|
||||||
else groupName = "Spieler";
|
else groupName = "Spieler";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Farben aus verify.properties anwenden (Höchste Priorität)
|
// 3. Farben aus verify.properties anwenden
|
||||||
if (groupName != null && groupFormats.containsKey(groupName)) {
|
if (groupName != null && groupFormats.containsKey(groupName.toLowerCase())) {
|
||||||
String rawFormat = groupFormats.get(groupName);
|
String rawFormat = groupFormats.get(groupName.toLowerCase());
|
||||||
|
|
||||||
// NEU: Check auf " || " für Spielername Farbe UND Chat Farbe
|
|
||||||
if (rawFormat.contains(" || ")) {
|
if (rawFormat.contains(" || ")) {
|
||||||
String[] parts = rawFormat.split(" \\|\\| ");
|
String[] parts = rawFormat.split(" \\|\\| ");
|
||||||
// Syntax: Prefix || PlayerColor || ChatColor
|
|
||||||
|
|
||||||
// Teil 1: Prefix (Rang)
|
|
||||||
prefix = ChatColor.translateAlternateColorCodes('&', parts[0]);
|
prefix = ChatColor.translateAlternateColorCodes('&', parts[0]);
|
||||||
|
|
||||||
// Teil 2: Spieler Name Farbe
|
|
||||||
playerColor = ChatColor.translateAlternateColorCodes('&', parts[1]);
|
playerColor = ChatColor.translateAlternateColorCodes('&', parts[1]);
|
||||||
|
|
||||||
// Teil 3: Chat Farbe
|
|
||||||
chatColor = ChatColor.translateAlternateColorCodes('&', parts[2]);
|
chatColor = ChatColor.translateAlternateColorCodes('&', parts[2]);
|
||||||
|
|
||||||
suffix = "";
|
suffix = "";
|
||||||
}
|
} else if (rawFormat.contains(": ")) {
|
||||||
// ALTER FALLBACK (Kompatibilität): Check auf ": "
|
|
||||||
else if (rawFormat.contains(": ")) {
|
|
||||||
String[] parts = rawFormat.split(": ", 2);
|
String[] parts = rawFormat.split(": ", 2);
|
||||||
// Teil 1: Prefix
|
|
||||||
prefix = ChatColor.translateAlternateColorCodes('&', parts[0]);
|
prefix = ChatColor.translateAlternateColorCodes('&', parts[0]);
|
||||||
// Teil 2: Chat Farbe
|
|
||||||
chatColor = ChatColor.translateAlternateColorCodes('&', parts[1]);
|
chatColor = ChatColor.translateAlternateColorCodes('&', parts[1]);
|
||||||
// Spielername bleibt Standard Weiß
|
|
||||||
playerColor = "§f";
|
playerColor = "§f";
|
||||||
suffix = "";
|
suffix = "";
|
||||||
} else {
|
} else {
|
||||||
// Kein Separator gefunden -> Nur Prefix
|
|
||||||
prefix = ChatColor.translateAlternateColorCodes('&', rawFormat);
|
prefix = ChatColor.translateAlternateColorCodes('&', rawFormat);
|
||||||
suffix = "";
|
suffix = "";
|
||||||
playerColor = "§f";
|
playerColor = "§f";
|
||||||
@@ -517,21 +536,29 @@ public class GlobalChatModule implements Module, Listener {
|
|||||||
return new String[]{prefix, suffix, playerColor, chatColor};
|
return new String[]{prefix, suffix, playerColor, chatColor};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Wenn nichts in Properties gefunden wurde, Fallback auf LuckPerms Meta
|
// 4. LuckPerms Meta Fallback (Nur falls kein Config-Format da war)
|
||||||
try {
|
try {
|
||||||
LuckPerms lp = LuckPermsProvider.get();
|
Class<?> providerClass = Class.forName("net.luckperms.api.LuckPermsProvider");
|
||||||
if (lp != null) {
|
Method getMethod = providerClass.getMethod("get");
|
||||||
User user = lp.getUserManager().getUser(player.getUniqueId());
|
Object api = getMethod.invoke(null);
|
||||||
if (user == null) {
|
if (api != null) {
|
||||||
try { user = lp.getUserManager().loadUser(player.getUniqueId()).join(); } catch (Exception ignored) {}
|
Method getUserManagerMethod = api.getClass().getMethod("getUserManager");
|
||||||
}
|
Object userManager = getUserManagerMethod.invoke(api);
|
||||||
|
Method loadUserMethod = userManager.getClass().getMethod("loadUser", UUID.class);
|
||||||
|
Object userFuture = loadUserMethod.invoke(userManager, player.getUniqueId());
|
||||||
|
Method joinMethod = userFuture.getClass().getMethod("join");
|
||||||
|
Object user = joinMethod.invoke(userFuture);
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
CachedMetaData meta = user.getCachedData().getMetaData();
|
Class<?> metaClass = Class.forName("net.luckperms.api.cacheddata.CachedMetaData");
|
||||||
|
Method getMetaDataMethod = user.getClass().getMethod("getCachedData");
|
||||||
|
Object meta = getMetaDataMethod.invoke(user);
|
||||||
if (meta != null) {
|
if (meta != null) {
|
||||||
String p = meta.getPrefix();
|
Method getPrefixMethod = metaClass.getMethod("getPrefix");
|
||||||
String s = meta.getSuffix();
|
Method getSuffixMethod = metaClass.getMethod("getSuffix");
|
||||||
if (p != null) prefix = ChatColor.translateAlternateColorCodes('&', p);
|
Object p = getPrefixMethod.invoke(meta);
|
||||||
if (s != null) suffix = ChatColor.translateAlternateColorCodes('&', s);
|
Object s = getSuffixMethod.invoke(meta);
|
||||||
|
if (p != null) prefix = ChatColor.translateAlternateColorCodes('&', p.toString());
|
||||||
|
if (s != null) suffix = ChatColor.translateAlternateColorCodes('&', s.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -543,14 +570,40 @@ public class GlobalChatModule implements Module, Listener {
|
|||||||
return new String[]{prefix, suffix, playerColor, chatColor};
|
return new String[]{prefix, suffix, playerColor, chatColor};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HELPER: LuckPerms Group via Reflection
|
||||||
|
private String getLuckPermsGroup(ProxiedPlayer player) {
|
||||||
|
try {
|
||||||
|
if (plugin.getProxy().getPluginManager().getPlugin("LuckPerms") == null) return null;
|
||||||
|
|
||||||
|
Class<?> providerClass = Class.forName("net.luckperms.api.LuckPermsProvider");
|
||||||
|
Method getMethod = providerClass.getMethod("get");
|
||||||
|
Object api = getMethod.invoke(null);
|
||||||
|
|
||||||
|
if (api != null) {
|
||||||
|
Method getUserManagerMethod = api.getClass().getMethod("getUserManager");
|
||||||
|
Object userManager = getUserManagerMethod.invoke(api);
|
||||||
|
Method loadUserMethod = userManager.getClass().getMethod("loadUser", UUID.class);
|
||||||
|
Object userFuture = loadUserMethod.invoke(userManager, player.getUniqueId());
|
||||||
|
Method joinMethod = userFuture.getClass().getMethod("join");
|
||||||
|
Object user = joinMethod.invoke(userFuture);
|
||||||
|
if (user != null) {
|
||||||
|
Method getPrimaryGroupMethod = user.getClass().getMethod("getPrimaryGroup");
|
||||||
|
return (String) getPrimaryGroupMethod.invoke(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
plugin.getLogger().warning("Fehler beim Auslesen von LuckPerms (Reflection): " + e.getMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private String stripColor(String s) {
|
private String stripColor(String s) {
|
||||||
if (s == null) return "";
|
if (s == null) return "";
|
||||||
return ChatColor.stripColor(s);
|
return ChatColor.stripColor(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================
|
|
||||||
// Filter einlesen
|
|
||||||
// ===========================
|
|
||||||
private void loadFilter() {
|
private void loadFilter() {
|
||||||
String fileName = "filter.yml";
|
String fileName = "filter.yml";
|
||||||
File file = new File(plugin.getDataFolder(), fileName);
|
File file = new File(plugin.getDataFolder(), fileName);
|
||||||
@@ -588,9 +641,6 @@ public class GlobalChatModule implements Module, Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================
|
|
||||||
// Logs aufräumen / schreiben
|
|
||||||
// ===========================
|
|
||||||
private void cleanupOldLogs() {
|
private void cleanupOldLogs() {
|
||||||
File[] files = logFolder.listFiles();
|
File[] files = logFolder.listFiles();
|
||||||
if (files == null) return;
|
if (files == null) return;
|
||||||
@@ -618,9 +668,6 @@ public class GlobalChatModule implements Module, Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================
|
|
||||||
// Staff-Check
|
|
||||||
// ===========================
|
|
||||||
private boolean isStaff(ProxiedPlayer p) {
|
private boolean isStaff(ProxiedPlayer p) {
|
||||||
if (p == null) return false;
|
if (p == null) return false;
|
||||||
Boolean reportedOp = playerIsOp.get(p.getUniqueId());
|
Boolean reportedOp = playerIsOp.get(p.getUniqueId());
|
||||||
@@ -639,6 +686,14 @@ public class GlobalChatModule implements Module, Listener {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Überladene Methode für CommandSender (für Console/Check)
|
||||||
|
private boolean isStaff(CommandSender sender) {
|
||||||
|
if (sender instanceof ProxiedPlayer) {
|
||||||
|
return isStaff((ProxiedPlayer) sender);
|
||||||
|
}
|
||||||
|
return sender.hasPermission("globalchat.clear"); // Console hat meist Rechte, aber sicher ist sicher
|
||||||
|
}
|
||||||
|
|
||||||
// ===========================
|
// ===========================
|
||||||
// Commands (Inner Classes)
|
// Commands (Inner Classes)
|
||||||
@@ -685,7 +740,6 @@ public class GlobalChatModule implements Module, Listener {
|
|||||||
String serverRaw = player.getServer().getInfo().getName();
|
String serverRaw = player.getServer().getInfo().getName();
|
||||||
String serverDisplay = getDisplayName(serverRaw);
|
String serverDisplay = getDisplayName(serverRaw);
|
||||||
|
|
||||||
// Support-Nachricht geht nur ans Team, nicht über den Global Chat Relay
|
|
||||||
TextComponent supportMsg = new TextComponent("§7[Support] §b" + player.getName() + " §7vom Server §e" + serverDisplay + " §7: §f" + msg);
|
TextComponent supportMsg = new TextComponent("§7[Support] §b" + player.getName() + " §7vom Server §e" + serverDisplay + " §7: §f" + msg);
|
||||||
supportMsg.setHoverEvent(new HoverEvent(Action.SHOW_TEXT, new ComponentBuilder("Klicke, um /reply " + player.getName() + " zu schreiben").create()));
|
supportMsg.setHoverEvent(new HoverEvent(Action.SHOW_TEXT, new ComponentBuilder("Klicke, um /reply " + player.getName() + " zu schreiben").create()));
|
||||||
supportMsg.setClickEvent(new net.md_5.bungee.api.chat.ClickEvent(net.md_5.bungee.api.chat.ClickEvent.Action.SUGGEST_COMMAND, "/reply " + player.getName() + " "));
|
supportMsg.setClickEvent(new net.md_5.bungee.api.chat.ClickEvent(net.md_5.bungee.api.chat.ClickEvent.Action.SUGGEST_COMMAND, "/reply " + player.getName() + " "));
|
||||||
@@ -770,4 +824,32 @@ public class GlobalChatModule implements Module, Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public class ClearChatCommand extends Command {
|
||||||
|
public ClearChatCommand() {
|
||||||
|
super("clearchat", "globalchat.clear", "cc");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(CommandSender sender, String[] args) {
|
||||||
|
// Prüfen, ob der Sender OP / Team ist
|
||||||
|
// Wir nutzen hier die überladene isStaff Methode für CommandSender
|
||||||
|
if (!isStaff(sender)) {
|
||||||
|
sender.sendMessage(new TextComponent("§cKeine Berechtigung!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 100 leere Zeilen senden zum Leeren
|
||||||
|
TextComponent emptyLine = new TextComponent(" ");
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
broadcastGlobal(emptyLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bestätigungsnachricht
|
||||||
|
TextComponent clearMsg = new TextComponent("§7[GlobalChat] §cDer Chat wurde von " + sender.getName() + " geleert.");
|
||||||
|
broadcastGlobal(clearMsg);
|
||||||
|
|
||||||
|
sender.sendMessage(new TextComponent("§aChat wurde geleert."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
41
src/main/resources/customcommands.yml
Normal file
41
src/main/resources/customcommands.yml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# If you want to enable /chat
|
||||||
|
chat-command: true
|
||||||
|
# The commands
|
||||||
|
commands:
|
||||||
|
test:
|
||||||
|
aliases:
|
||||||
|
- test2
|
||||||
|
permission: ""
|
||||||
|
type: random
|
||||||
|
sender: default
|
||||||
|
commands:
|
||||||
|
- "alert Das ist ein Random Test Befehl!"
|
||||||
|
- "glist"
|
||||||
|
anothercommand:
|
||||||
|
aliases:
|
||||||
|
- thisisacommand
|
||||||
|
permission: "myplugin.mypermission"
|
||||||
|
type: line
|
||||||
|
sender: default
|
||||||
|
commands:
|
||||||
|
- "alert Hello World!"
|
||||||
|
- "alert How're you?"
|
||||||
|
- "alert I'd like to tell you that %args%"
|
||||||
|
# - "test" # Vorsicht: Rekursion, falls 'test' in dieser Liste steht und 'test' selbst definiert ist
|
||||||
|
sudo:
|
||||||
|
aliases:
|
||||||
|
- runasadmin
|
||||||
|
permission: "commands.sudo"
|
||||||
|
type: line
|
||||||
|
sender: admin
|
||||||
|
commands:
|
||||||
|
- "alert Running as Admin: %args%"
|
||||||
|
- "%args%"
|
||||||
|
something:
|
||||||
|
aliases:
|
||||||
|
- anything
|
||||||
|
permission: "some.permission"
|
||||||
|
type: line
|
||||||
|
sender: console
|
||||||
|
commands:
|
||||||
|
- "alert %sender% would like to say that %args%"
|
||||||
18
src/main/resources/messages.txt
Normal file
18
src/main/resources/messages.txt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
§8[§2Viper-Netzwerk§8] §7Der Server läuft 24/7 – also keine Hektik beim Spielen :)
|
||||||
|
§8[§2Viper-Netzwerk§8] §7Dies ist ein privater Server – hier zählt der Zusammenhalt.
|
||||||
|
§8[§dTipp§8] §7Wenn du denkst, du bist sicher… schau nochmal nach. Creeper machen keine Geräusche beim Tippen.
|
||||||
|
§8[§2Viper-Netzwerk§8] §7Wähle einen Server, leg los – der Rest ergibt sich. Oder explodiert.
|
||||||
|
§8[§2Viper-Netzwerk§8] §7Mehr Server. Mehr Blöcke. Mehr Unfälle. Willkommen!
|
||||||
|
§8[§dTipp§8] §7Halte eine Spitzhacke mit Glück bereit. Man weiß nie, wann das nächste Erz kommt.
|
||||||
|
§8[§dTipp§8] §7Mit §e/home§7 kannst du dich jederzeit nach Hause teleportieren.
|
||||||
|
§8[§2Viper-Netzwerk§8] §7Das wichtigste Plugin? Du selbst. Spiel fair, sei kreativ!
|
||||||
|
§8[§2Viper-Netzwerk§8] §7Redstone ist keine Magie – aber fast.
|
||||||
|
§8[§dTipp§8] §7Schilde sind cool. Besonders wenn Skelette zielen.
|
||||||
|
§8[§2Viper-Netzwerk§8] §7Wenn du in Lava fällst, bist du nicht der Erste. Nur der Nächste.
|
||||||
|
§8[§dTipp§8] §7Villager sind nicht dumm – nur sehr… eigen.
|
||||||
|
§8[§2Viper-Netzwerk§8] §7Bau groß, bau sicher – oder bau eine Treppe zur Nachbarschaftsklage.
|
||||||
|
§8[§2Viper-Netzwerk§8] §7Gras wächst. Spieler auch. Gib jedem eine Chance!
|
||||||
|
§8[§2Viper-Netzwerk§8] §7Ein Creeper ist keine Begrüßung. Es sei denn, du willst es spannend machen.
|
||||||
|
§8[§dTipp§8] §7Ein voller Magen ist halbe Miete. Farmen lohnt sich!
|
||||||
|
§8[§2Viper-Netzwerk§8] §7Wir haben keine Probleme – nur Redstone-Schaltungen mit Charakter.
|
||||||
|
§8[§dTipp§8] §7Markiere dein Grundstück mit §e/p claim§7, bevor es jemand anderes tut!
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
name: StatusAPI
|
name: StatusAPI
|
||||||
main: net.viper.status.StatusAPI
|
main: net.viper.status.StatusAPI
|
||||||
version: 4.0.2
|
version: 4.0.8
|
||||||
author: M_Viper
|
author: M_Viper
|
||||||
description: StatusAPI für BungeeCord inkl. Update-Checker und Modul-System
|
description: StatusAPI für BungeeCord inkl. Update-Checker und Modul-System
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,26 @@
|
|||||||
|
# _____ __ __ ___ ____ ____
|
||||||
|
# / ___// /_____ _/ /___ _______/ | / __ \/ _/
|
||||||
|
# \__ \/ __/ __ `/ __/ / / / ___/ /| | / /_/ // /
|
||||||
|
# ___/ / /_/ /_/ / /_/ /_/ (__ ) ___ |/ ____// /
|
||||||
|
# /____/\__/\__,_/\__/\__,_/____/_/ |_/_/ /___/
|
||||||
|
|
||||||
|
|
||||||
# ===========================
|
# ===========================
|
||||||
# GLOBALCHAT AKTIVIERUNG
|
# GLOBALCHAT AKTIVIERUNG
|
||||||
# ===========================
|
# ===========================
|
||||||
chat.enabled=false
|
chat.enabled=false
|
||||||
|
|
||||||
|
# ------------------------------
|
||||||
|
# Broadcast
|
||||||
|
# ------------------------------
|
||||||
|
|
||||||
|
broadcast.enabled=false
|
||||||
|
broadcast.prefix=[Broadcast]
|
||||||
|
broadcast.prefix-color=&c
|
||||||
|
broadcast.message-color=&f
|
||||||
|
broadcast.format=%prefixColored% %messageColored%
|
||||||
|
# broadcast.format kann angepasst werden; nutze Platzhalter: %name%, %prefix%, %prefixColored%, %message%, %messageColored%, %type%
|
||||||
|
|
||||||
# ===========================
|
# ===========================
|
||||||
# NAVIGATION / SERVER SWITCHER
|
# NAVIGATION / SERVER SWITCHER
|
||||||
# ===========================
|
# ===========================
|
||||||
@@ -38,6 +56,15 @@ server.skyblock=&dSkyBlock
|
|||||||
server.skyblock.id=3
|
server.skyblock.id=3
|
||||||
server.skyblock.secret=GeheimesWortFuerSkyBlock789
|
server.skyblock.secret=GeheimesWortFuerSkyBlock789
|
||||||
|
|
||||||
|
# ===========================
|
||||||
|
# Manuelle Ränge (Overrides)
|
||||||
|
# ===========================
|
||||||
|
# Syntax: override.<Spieler-UUID> = <Gruppenname>
|
||||||
|
# WICHTIG: Die Gruppe (z.B. Owner) muss unten bei groupformat definiert sein!
|
||||||
|
|
||||||
|
# Beispiel: Deinen UUID hier einfügen
|
||||||
|
override.uuid-hier-einfügen = Owner
|
||||||
|
|
||||||
# ===========================
|
# ===========================
|
||||||
# Chat-Formate für Gruppen
|
# Chat-Formate für Gruppen
|
||||||
# ===========================
|
# ===========================
|
||||||
@@ -47,8 +74,37 @@ server.skyblock.secret=GeheimesWortFuerSkyBlock789
|
|||||||
|
|
||||||
# Ränge mit neuer Syntax: Rank || Spielerfarbe || Chatfarbe
|
# Ränge mit neuer Syntax: Rank || Spielerfarbe || Chatfarbe
|
||||||
# Beispiel: Rot (Rang) || Blau (Name) || Lila (Chat)
|
# Beispiel: Rot (Rang) || Blau (Name) || Lila (Chat)
|
||||||
groupformat.Owner=&c[Owner] || &b || &d
|
groupformat.owner=&c[Owner] || &b || &d
|
||||||
groupformat.Admin=&4[Admin] || &9 || &c
|
groupformat.admin=&4[Admin] || &9 || &c
|
||||||
groupformat.Developer=&b[Dev] || &3 || &a
|
groupformat.developer=&b[Dev] || &3 || &a
|
||||||
groupformat.Premium=&6[Premium] || &e || &7
|
groupformat.premium=&6[Premium] || &e || &7
|
||||||
groupformat.Spieler=&f[Spieler] || &7 || &8
|
groupformat.spieler=&f[Spieler] || &7 || &8
|
||||||
|
|
||||||
|
# ===========================
|
||||||
|
# AUTOMESSAGE
|
||||||
|
# ===========================
|
||||||
|
# Aktiviert den automatischen Nachrichten-Rundruf
|
||||||
|
automessage.enabled=false
|
||||||
|
|
||||||
|
# Zeitintervall in Sekunden (Standard: 300 = 5 Minuten)
|
||||||
|
automessage.interval=300
|
||||||
|
|
||||||
|
# Optional: Ein Prefix, das VOR jede Nachricht aus der Datei gesetzt wird.
|
||||||
|
# Wenn du das Prefix bereits IN der messages.txt hast, lass dieses Feld einfach leer.
|
||||||
|
automessage.prefix=
|
||||||
|
|
||||||
|
# Der Name der Datei, in der die Nachrichten stehen (liegt im Plugin-Ordner)
|
||||||
|
automessage.file=messages.txt
|
||||||
|
|
||||||
|
# ===========================
|
||||||
|
# COMMAND BLOCKER
|
||||||
|
# ===========================
|
||||||
|
commandblocker.enabled=true
|
||||||
|
commandblocker.bypass.permission=commandblocker.bypass
|
||||||
|
|
||||||
|
# ===========================
|
||||||
|
# CUSTOM COMMANDS
|
||||||
|
# ===========================
|
||||||
|
# Aktiviert das Modul für benutzerdefinierte Befehle und Aliases.
|
||||||
|
# Wenn aktiviert, wird die Datei 'customcommands.yml' geladen.
|
||||||
|
customcommands.enabled=true
|
||||||
88
src/main/resources/verify.template.properties
Normal file
88
src/main/resources/verify.template.properties
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# _____ __ __ ___ ____ ____
|
||||||
|
# / ___// /_____ _/ /___ _______/ | / __ \/ _/
|
||||||
|
# \__ \/ __/ __ `/ __/ / / / ___/ /| | / /_/ // /
|
||||||
|
# ___/ / /_/ /_/ / /_/ /_/ (__ ) ___ |/ ____// /
|
||||||
|
# /____/\__/\__,_/\__/\__,_/____/_/ |_/_/ /___/
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================
|
||||||
|
# GLOBALCHAT AKTIVIERUNG
|
||||||
|
# ===========================
|
||||||
|
chat.enabled=false
|
||||||
|
|
||||||
|
# ------------------------------
|
||||||
|
# Broadcast
|
||||||
|
# ------------------------------
|
||||||
|
|
||||||
|
broadcast.enabled=false
|
||||||
|
broadcast.prefix=[Broadcast]
|
||||||
|
broadcast.prefix-color=&c
|
||||||
|
broadcast.message-color=&f
|
||||||
|
broadcast.format=%prefixColored% %messageColored%
|
||||||
|
# broadcast.format kann angepasst werden; nutze Platzhalter: %name%, %prefix%, %prefixColored%, %message%, %messageColored%, %type%
|
||||||
|
|
||||||
|
# ===========================
|
||||||
|
# NAVIGATION / SERVER SWITCHER
|
||||||
|
# ===========================
|
||||||
|
# Hier kannst du das interne Navigationssystem aktivieren/deaktivieren.
|
||||||
|
# Wenn aktiviert, erstellt das Plugin automatisch Befehle basierend auf den Servernamen (z.B. /lobby, /survival).
|
||||||
|
navigation.enabled=false
|
||||||
|
|
||||||
|
# ===========================
|
||||||
|
# WORDPRESS / VERIFY EINSTELLUNGEN
|
||||||
|
# ===========================
|
||||||
|
wp_verify_url=https://deine-wp-domain.tld
|
||||||
|
|
||||||
|
# ===========================
|
||||||
|
# SERVER KONFIGURATION
|
||||||
|
# ===========================
|
||||||
|
# Hier legst du für jeden Server alles fest:
|
||||||
|
# 1. Den Anzeigenamen für den Chat (z.B. &bLobby)
|
||||||
|
# 2. Die Server ID für WordPress (z.B. id=1)
|
||||||
|
# 3. Das Secret für WordPress (z.B. secret=...)
|
||||||
|
|
||||||
|
# Server 1: Lobby
|
||||||
|
server.lobby=&bLobby
|
||||||
|
server.lobby.id=1
|
||||||
|
server.lobby.secret=GeheimesWortFuerLobby123
|
||||||
|
|
||||||
|
# Server 2: Survival
|
||||||
|
server.survival=&aSurvival
|
||||||
|
server.survival.id=2
|
||||||
|
server.survival.secret=GeheimesWortFuerSurvival456
|
||||||
|
|
||||||
|
# Server 3: SkyBlock
|
||||||
|
server.skyblock=&dSkyBlock
|
||||||
|
server.skyblock.id=3
|
||||||
|
server.skyblock.secret=GeheimesWortFuerSkyBlock789
|
||||||
|
|
||||||
|
# ===========================
|
||||||
|
# Manuelle Ränge (Overrides)
|
||||||
|
# ===========================
|
||||||
|
# Syntax: override.<Spieler-UUID> = <Gruppenname>
|
||||||
|
# WICHTIG: Die Gruppe (z.B. Owner) muss unten bei groupformat definiert sein!
|
||||||
|
|
||||||
|
# Beispiel: Deinen UUID hier einfügen
|
||||||
|
override.uuid-hier-einfügen = Owner
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================
|
||||||
|
# Chat-Formate für Gruppen
|
||||||
|
# ===========================
|
||||||
|
|
||||||
|
# Der Name hinter dem Punkt (z.B. Owner) muss exakt mit der LuckPerms Gruppe übereinstimmen.
|
||||||
|
# Nutze & für Farbcodes.
|
||||||
|
|
||||||
|
# Ränge mit neuer Syntax: Rank || Spielerfarbe || Chatfarbe
|
||||||
|
# Beispiel: Rot (Rang) || Blau (Name) || Lila (Chat)
|
||||||
|
groupformat.owner=&c[Owner] || &b || &d
|
||||||
|
groupformat.admin=&4[Admin] || &9 || &c
|
||||||
|
groupformat.developer=&b[Dev] || &3 || &a
|
||||||
|
groupformat.premium=&6[Premium] || &e || &7
|
||||||
|
groupformat.spieler=&f[Spieler] || &7 || &8
|
||||||
|
|
||||||
|
# ===========================
|
||||||
|
# COMMAND BLOCKER
|
||||||
|
# ===========================
|
||||||
|
commandblocker.enabled=true
|
||||||
|
commandblocker.bypass.permission=commandblocker.bypass
|
||||||
Reference in New Issue
Block a user