Compare commits
33 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 | |||
| aa5b1a2572 | |||
| 27ac00308a | |||
| c0ea8c0cd8 | |||
| a5d54b8141 | |||
| 52ae8c8c5a | |||
| fba27d6c6f | |||
| af7c86ef49 | |||
| 81c6991d49 | |||
| 2a4b8d6bce |
216
README.md
216
README.md
@@ -1,105 +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` |
|
||||||
| `/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,
|
||||||
@@ -117,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.
|
||||||
|
|
||||||
|
|||||||
2
pom.xml
2
pom.xml
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<groupId>net.viper.bungee</groupId>
|
<groupId>net.viper.bungee</groupId>
|
||||||
<artifactId>StatusAPI</artifactId>
|
<artifactId>StatusAPI</artifactId>
|
||||||
<version>4.0.0</version>
|
<version>4.0.8</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>StatusAPI</name>
|
<name>StatusAPI</name>
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
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,56 +1,56 @@
|
|||||||
name: StatusAPI
|
name: StatusAPI
|
||||||
main: net.viper.status.StatusAPI
|
main: net.viper.status.StatusAPI
|
||||||
version: 4.0.0
|
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
|
||||||
|
|
||||||
softdepend:
|
softdepend:
|
||||||
- LuckPerms
|
- LuckPerms
|
||||||
|
|
||||||
commands:
|
commands:
|
||||||
# Verify Modul Befehle
|
# Verify Modul Befehle
|
||||||
verify:
|
verify:
|
||||||
description: Verifiziere dich mit einem Token
|
description: Verifiziere dich mit einem Token
|
||||||
usage: /verify <token>
|
usage: /verify <token>
|
||||||
|
|
||||||
# GlobalChat Modul Befehle
|
# GlobalChat Modul Befehle
|
||||||
globalreload:
|
globalreload:
|
||||||
description: Lädt den Chat-Filter neu
|
description: Lädt den Chat-Filter neu
|
||||||
usage: /globalreload
|
usage: /globalreload
|
||||||
permission: globalchat.reload
|
permission: globalchat.reload
|
||||||
|
|
||||||
globalmute:
|
globalmute:
|
||||||
description: Schaltet den globalen Chat an/aus
|
description: Schaltet den globalen Chat an/aus
|
||||||
usage: /globalmute
|
usage: /globalmute
|
||||||
permission: globalchat.mute
|
permission: globalchat.mute
|
||||||
|
|
||||||
support:
|
support:
|
||||||
description: Sendet eine Support-Nachricht an das Team
|
description: Sendet eine Support-Nachricht an das Team
|
||||||
usage: /support <Nachricht>
|
usage: /support <Nachricht>
|
||||||
|
|
||||||
reply:
|
reply:
|
||||||
description: Antwortet auf eine Support-Nachricht
|
description: Antwortet auf eine Support-Nachricht
|
||||||
usage: /reply <Nachricht>
|
usage: /reply <Nachricht>
|
||||||
|
|
||||||
info:
|
info:
|
||||||
description: Zeigt Plugin-Informationen an
|
description: Zeigt Plugin-Informationen an
|
||||||
usage: /info
|
usage: /info
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
# StatusAPI Core Permissions
|
# StatusAPI Core Permissions
|
||||||
statusapi.update.notify:
|
statusapi.update.notify:
|
||||||
description: 'Erlaubt Update-Benachrichtigungen'
|
description: 'Erlaubt Update-Benachrichtigungen'
|
||||||
default: op
|
default: op
|
||||||
|
|
||||||
# GlobalChat Permissions
|
# GlobalChat Permissions
|
||||||
globalchat.reload:
|
globalchat.reload:
|
||||||
description: Erlaubt das Neuladen des Chat-Filters
|
description: Erlaubt das Neuladen des Chat-Filters
|
||||||
default: op
|
default: op
|
||||||
|
|
||||||
globalchat.mute:
|
globalchat.mute:
|
||||||
description: Erlaubt das Aktivieren/Deaktivieren des globalen Mutes
|
description: Erlaubt das Aktivieren/Deaktivieren des globalen Mutes
|
||||||
default: op
|
default: op
|
||||||
|
|
||||||
globalchat.bypass:
|
globalchat.bypass:
|
||||||
description: Umgeht den globalen Mute
|
description: Umgeht den globalen Mute
|
||||||
default: op
|
default: op
|
||||||
@@ -1,39 +1,110 @@
|
|||||||
# ===========================
|
# _____ __ __ ___ ____ ____
|
||||||
# GLOBALCHAT AKTIVIERUNG
|
# / ___// /_____ _/ /___ _______/ | / __ \/ _/
|
||||||
# ===========================
|
# \__ \/ __/ __ `/ __/ / / / ___/ /| | / /_/ // /
|
||||||
chat.enabled=false
|
# ___/ / /_/ /_/ / /_/ /_/ (__ ) ___ |/ ____// /
|
||||||
|
# /____/\__/\__,_/\__/\__,_/____/_/ |_/_/ /___/
|
||||||
# ===========================
|
|
||||||
# NAVIGATION / SERVER SWITCHER
|
|
||||||
# ===========================
|
# ===========================
|
||||||
# Hier kannst du das interne Navigationssystem aktivieren/deaktivieren.
|
# GLOBALCHAT AKTIVIERUNG
|
||||||
# Wenn aktiviert, erstellt das Plugin automatisch Befehle basierend auf den Servernamen (z.B. /lobby, /survival).
|
# ===========================
|
||||||
navigation.enabled=false
|
chat.enabled=false
|
||||||
|
|
||||||
# ===========================
|
# ------------------------------
|
||||||
# WORDPRESS / VERIFY EINSTELLUNGEN
|
# Broadcast
|
||||||
# ===========================
|
# ------------------------------
|
||||||
wp_verify_url=https://deine-wp-domain.tld
|
|
||||||
|
broadcast.enabled=false
|
||||||
# ===========================
|
broadcast.prefix=[Broadcast]
|
||||||
# SERVER KONFIGURATION
|
broadcast.prefix-color=&c
|
||||||
# ===========================
|
broadcast.message-color=&f
|
||||||
# Hier legst du für jeden Server alles fest:
|
broadcast.format=%prefixColored% %messageColored%
|
||||||
# 1. Den Anzeigenamen für den Chat (z.B. &bLobby)
|
# broadcast.format kann angepasst werden; nutze Platzhalter: %name%, %prefix%, %prefixColored%, %message%, %messageColored%, %type%
|
||||||
# 2. Die Server ID für WordPress (z.B. id=1)
|
|
||||||
# 3. Das Secret für WordPress (z.B. secret=...)
|
# ===========================
|
||||||
|
# NAVIGATION / SERVER SWITCHER
|
||||||
# Server 1: Lobby
|
# ===========================
|
||||||
server.lobby=&bLobby
|
# Hier kannst du das interne Navigationssystem aktivieren/deaktivieren.
|
||||||
server.lobby.id=1
|
# Wenn aktiviert, erstellt das Plugin automatisch Befehle basierend auf den Servernamen (z.B. /lobby, /survival).
|
||||||
server.lobby.secret=GeheimesWortFuerLobby123
|
navigation.enabled=false
|
||||||
|
|
||||||
# Server 2: Survival
|
# ===========================
|
||||||
server.survival=&aSurvival
|
# WORDPRESS / VERIFY EINSTELLUNGEN
|
||||||
server.survival.id=2
|
# ===========================
|
||||||
server.survival.secret=GeheimesWortFuerSurvival456
|
wp_verify_url=https://deine-wp-domain.tld
|
||||||
|
|
||||||
# Server 3: SkyBlock
|
# ===========================
|
||||||
server.skyblock=&dSkyBlock
|
# SERVER KONFIGURATION
|
||||||
server.skyblock.id=3
|
# ===========================
|
||||||
server.skyblock.secret=GeheimesWortFuerSkyBlock789
|
# 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
|
||||||
|
|
||||||
|
# ===========================
|
||||||
|
# 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