18 Commits
4.0.4 ... main

Author SHA1 Message Date
202ae6ad34 LICENSE hinzugefügt 2026-01-22 18:46:35 +00:00
bf2ee6460c Upload pom.xml via GUI 2026-01-20 23:32:55 +00:00
5303cbcb95 Update from Git Manager GUI 2026-01-21 00:32:53 +01:00
bce695829e README.md aktualisiert 2026-01-18 16:08:28 +00:00
d3796a3ed9 README.md aktualisiert 2026-01-18 16:08:10 +00:00
eb77726f83 src/main/resources/plugin.yml aktualisiert 2026-01-18 15:47:46 +00:00
0c3f91fa16 pom.xml aktualisiert 2026-01-18 15:47:29 +00:00
8277758e90 src/main/java/net/viper/status/StatusAPI.java aktualisiert 2026-01-18 15:46:34 +00:00
36363f09c2 Dateien nach "src/main/java/net/viper/status/modules/commandblocker" hochladen 2026-01-18 15:46:12 +00:00
14b5a08fe2 Dateien nach "src/main/resources" hochladen 2026-01-18 15:45:22 +00:00
c549df684c src/main/resources/verify.properties aktualisiert 2026-01-18 15:44:57 +00:00
422cb9c352 Dateien nach "src/main/java/net/viper/status/modules/broadcast" hochladen 2026-01-17 18:27:34 +00:00
e60bff9c6d src/main/java/net/viper/status/StatusAPI.java aktualisiert 2026-01-17 18:26:24 +00:00
59944ece4e src/main/resources/verify.properties aktualisiert 2026-01-17 18:25:25 +00:00
b29f2e2db3 src/main/resources/plugin.yml aktualisiert 2026-01-17 18:24:39 +00:00
f37cff83fc pom.xml aktualisiert 2026-01-17 18:24:19 +00:00
2384178a3a src/main/resources/plugin.yml aktualisiert 2026-01-17 10:17:28 +00:00
5558b237bb pom.xml aktualisiert 2026-01-17 10:17:14 +00:00
15 changed files with 2271 additions and 478 deletions

55
LICENSE Normal file
View File

@@ -0,0 +1,55 @@
LIZENZ & NUTZUNGSBEDINGUNGEN
============================
StatusAPI - Modulares BungeeCord Plugin
Copyright (c) M_Viper. Alle Rechte vorbehalten.
---
NUTZUNGSBESCHRAENKUNGEN
Dieses Projekt sowie alle enthaltenen Module, Quelltexte und Ressourcen
duerfen NICHT veraendert, kopiert oder weiterverbreitet werden.
Jegliche Nutzung, Modifikation oder Weitergabe - ganz oder teilweise -
ist ausschliesslich mit vorheriger SCHRIFTLICHER GENEHMIGUNG des
Entwicklers gestattet.
---
VERBOTENE HANDLUNGEN
Ohne ausdrueckliche schriftliche Genehmigung ist es untersagt:
1. Den Quellcode zu kopieren, zu modifizieren oder abgeleitete Werke
zu erstellen
2. Das Plugin oder Teile davon weiterzuverbreiten, zu verkaufen oder
anderweitig zu uebertragen
3. Das Plugin zu dekompilieren, zu disassemblieren oder auf andere
Weise zurueckzuentwickeln (Reverse Engineering)
4. Urheberrechtshinweise, Marken oder andere Eigentumshinweise zu
entfernen oder zu veraendern
---
HAFTUNGSAUSSCHLUSS
DIESES PLUGIN WIRD "WIE BESEHEN" ZUR VERFUEGUNG GESTELLT, OHNE
JEGLICHE AUSDRUECKLICHE ODER STILLSCHWEIGENDE GEWAEHRLEISTUNG.
DER ENTWICKLER HAFTET NICHT FUER DIREKTE, INDIREKTE, ZUFAELLIGE,
BESONDERE ODER FOLGESCHAEDEN, DIE SICH AUS DER NUTZUNG ODER DER
UNMOEGLICHKEIT DER NUTZUNG DIESES PLUGINS ERGEBEN.
---
KONTAKT
Bei Fragen zur Lizenzierung oder fuer Genehmigungsanfragen wenden
Sie sich bitte an den Entwickler.
---
(c) M_Viper - Alle Rechte vorbehalten.

217
README.md
View File

@@ -1,105 +1,160 @@
# StatusAPI
![Build Status](https://img.shields.io/badge/build-passing-brightgreen)
![Version](https://img.shields.io/badge/version-4.0.1-blue)
![Version](https://img.shields.io/badge/version-4.0.6-blue)
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
- **Modulares System**: Aktiviere nur die Features, die du brauchst (Chat, Stats, Verify)
- **JSON API**: Liefert Echtzeit-Server-Statistiken (Spieleranzahl, Motd, Versionen) für deine Webseite (Port 9191)
- **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`)
- **WordPress Verify**: Verifiziere Spieler direkt mit deiner WordPress-Seite (CPT Integration)
- **Auto-Updater**: Prüft automatisch auf Updates und lädt diese herunter
- **Logging**: Speichert den kompletten Chatverlauf in Dateien
- **Modulares System**
Aktiviere nur die Module, die du benötigst (Chat, Navigation, Verify, Security).
- **JSON Status API**
Liefert Echtzeit-Serverdaten für Webseiten oder externe Dienste (Port `9191`).
- **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
1. Lade die aktuellste `StatusAPI.jar` herunter
2. Lege die Datei in den `plugins` Ordner deiner BungeeCord-Installation
3. Starte den Server neu
4. Die Config-Dateien werden automatisch erstellt
1. Lade die aktuelle `StatusAPI.jar`
2. Lege sie in den `plugins/` Ordner deiner BungeeCord-Installation
3. Starte den Proxy neu
4. Alle Konfigurationsdateien werden automatisch erstellt
---
## ⚙️ 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
Hier konfigurierst du Server-Namen, Farben und die Verbindung zu WordPress.
```properties
# ===========================
# GLOBALE EINSTELLUNGEN
# ===========================
# Aktiviert oder deaktiviert das globale Chat-Modul
chat.enabled=true
# Aktiviert die automatischen Server-Switch Befehle (z.B. /survival)
navigation.enabled=true
commandblocker.enabled=true
# ===========================
# WORDPRESS VERIFY
# ===========================
# URL deiner WordPress-Installation
wp_verify_url=https://deine-wordpress-domain.tld
# ===========================
# 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.id=1606
server.lobby.secret=DeinSecretHier
# Beispiel: Server mit Namen "bungee-server-1" (Name aus BungeeConfig)
server.bungee-server-1=&aSurvival
server.bungee-server-1.id=2
server.bungee-server-1.secret=GeheimesWortFuerSurvival
server.survival=&aSurvival
server.survival.id=2
server.survival.secret=GeheimesWortFuerSurvival
```
## 🔒 CommandBlocker Modul
# Beispiel: SkyBlock
server.skyblock=&dSkyBlock
server.skyblock.id=3
server.skyblock.secret=GeheimesWortFuerSkyBlock
Das **CommandBlockerModule** blockiert definierte Commands **netzwerkweit**, bevor sie an Backend-Server weitergeleitet werden.
### Eigenschaften
- 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
| Befehl | Beschreibung | Permission |
|--------|--------------|------------|
| `/verify <token>` | Verifiziert den Spieler mit der WordPress-Seite | - |
| `/survival` / `/lobby` | Führt dich auf den entsprechenden Server um (wird dynamisch erstellt) | - |
| `/globalmute` | Aktiviert oder deaktiviert den globalen Chat | `globalchat.mute` |
| `/globalreload` | Lädt Filter und Konfigurationen neu | `globalchat.reload` |
| `/clearchat, cc` | Löscht den Chatverlauf | `globalchat.clear` |
| `/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 | - |
|------|-------------|------------|
| `/cb add <command>` | Blockiert einen Command | `commandblocker.admin` |
| `/cb remove <command>` | Entfernt einen Block | `commandblocker.admin` |
| `/cb list` | Zeigt alle blockierten Commands | `commandblocker.admin` |
| `/cb reload` | Lädt die YAML neu | `commandblocker.admin` |
## 🌐 JSON API (Status)
---
Die API läuft unter `http://DEINE_IP:9191/`.
## Bypass
### Beispiel Request
```bash
curl http://127.0.0.1:9191/
Spieler mit folgender Permission umgehen den CommandBlocker vollständig:
```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
{
"online": true,
@@ -117,28 +172,54 @@ curl http://127.0.0.1:9191/
}
```
---
## 🛠️ 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
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
- `globalchat.bypass` - Erlaubt das Schreiben, auch wenn der Chat gemuted ist
- `globalchat.reload` - Erlaubt das Neuladen der Konfiguration
- `globalchat.clear` - Löscht den Kompletten Chatverlauf
Diese Methoden werden automatisch beim **Starten** bzw. **Stoppen** des Proxys aufgerufen.
---
## 📝 Permissions Übersicht
- `globalchat.mute`
- `globalchat.bypass`
- `globalchat.reload`
- `globalchat.clear`
- `commandblocker.admin`
- `commandblocker.bypass`
---
## 🤝 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
View File

@@ -1,52 +1,52 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.viper.bungee</groupId>
<artifactId>StatusAPI</artifactId>
<version>4.0.3</version>
<packaging>jar</packaging>
<name>StatusAPI</name>
<description>BungeeCord Status API Plugin</description>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- BungeeCord API -->
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.20</version>
<scope>provided</scope>
</dependency>
<!-- LuckPerms API (Optional) -->
<dependency>
<groupId>net.luckperms</groupId>
<artifactId>api</artifactId>
<version>5.4</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<finalName>StatusAPI</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
</resources>
</build>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.viper.bungee</groupId>
<artifactId>StatusAPI</artifactId>
<version>4.0.8</version>
<packaging>jar</packaging>
<name>StatusAPI</name>
<description>BungeeCord Status API Plugin</description>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- BungeeCord API -->
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.20</version>
<scope>provided</scope>
</dependency>
<!-- LuckPerms API (Optional) -->
<dependency>
<groupId>net.luckperms</groupId>
<artifactId>api</artifactId>
<version>5.4</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<finalName>StatusAPI</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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%"

View 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!

View File

@@ -1,6 +1,6 @@
name: StatusAPI
main: net.viper.status.StatusAPI
version: 4.0.3
version: 4.0.8
author: M_Viper
description: StatusAPI für BungeeCord inkl. Update-Checker und Modul-System

View File

@@ -1,8 +1,26 @@
# _____ __ __ ___ ____ ____
# / ___// /_____ _/ /___ _______/ | / __ \/ _/
# \__ \/ __/ __ `/ __/ / / / ___/ /| | / /_/ // /
# ___/ / /_/ /_/ / /_/ /_/ (__ ) ___ |/ ____// /
# /____/\__/\__,_/\__/\__,_/____/_/ |_/_/ /___/
# ===========================
# 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
# ===========================
@@ -46,8 +64,6 @@ server.skyblock.secret=GeheimesWortFuerSkyBlock789
# Beispiel: Deinen UUID hier einfügen
override.uuid-hier-einfügen = Owner
override.uuid-hier-einfügen = Admin
override.uuid-hier-einfügen = Developer
# ===========================
# Chat-Formate für Gruppen
@@ -58,8 +74,37 @@ override.uuid-hier-einfügen = Developer
# 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
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

View 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