3 Commits
4.0.7 ... 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
12 changed files with 802 additions and 181 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.

View File

@@ -7,7 +7,7 @@
<groupId>net.viper.bungee</groupId> <groupId>net.viper.bungee</groupId>
<artifactId>StatusAPI</artifactId> <artifactId>StatusAPI</artifactId>
<version>4.0.6</version> <version>4.0.8</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>StatusAPI</name> <name>StatusAPI</name>

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

View File

@@ -1,8 +1,14 @@
package net.viper.status; package net.viper.status;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.Plugin;
import net.viper.status.module.ModuleManager; import net.viper.status.module.ModuleManager;
import net.viper.status.stats.PlayerStats; import net.viper.status.stats.PlayerStats;
@@ -12,15 +18,15 @@ import net.viper.status.modules.globalchat.GlobalChatModule;
import net.viper.status.modules.navigation.NavigationModule; import net.viper.status.modules.navigation.NavigationModule;
import net.viper.status.modules.commandblocker.CommandBlockerModule; import net.viper.status.modules.commandblocker.CommandBlockerModule;
import net.viper.status.modules.broadcast.BroadcastModule; import net.viper.status.modules.broadcast.BroadcastModule;
import net.viper.status.modules.AutoMessage.AutoMessageModule;
import net.viper.status.modules.customcommands.CustomCommandModule;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringReader; import java.io.StringReader;
import java.net.ServerSocket; import java.net.ServerSocket;
@@ -29,24 +35,31 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import net.md_5.bungee.event.EventHandler;
/** /**
* StatusAPI - zentraler Bungee HTTP-Status- und Broadcast-Endpunkt * StatusAPI - zentraler Bungee HTTP-Status- und Broadcast-Endpunkt
*/ */
public class StatusAPI extends Plugin implements Runnable { public class StatusAPI extends Plugin implements Runnable, Listener {
private Thread thread; private Thread thread;
private int port = 9191; private int port = 9191;
private ModuleManager moduleManager; private ModuleManager moduleManager;
private UpdateChecker updateChecker;
private FileDownloader fileDownloader;
private Properties verifyProperties; private Properties verifyProperties;
// Speicher für Updates beim Join
private boolean updatePending = false;
private String latestVersionString = "";
private final Set<UUID> informedAdmins = new HashSet<>();
@Override @Override
public void onEnable() { public void onEnable() {
getLogger().info("StatusAPI Core wird initialisiert..."); getLogger().info("StatusAPI Core wird initialisiert...");
// Event Listener registrieren
getProxy().getPluginManager().registerListener(this, this);
if (!getDataFolder().exists()) { if (!getDataFolder().exists()) {
getDataFolder().mkdirs(); getDataFolder().mkdirs();
} }
@@ -63,6 +76,16 @@ public class StatusAPI extends Plugin implements Runnable {
moduleManager.registerModule(new NavigationModule()); moduleManager.registerModule(new NavigationModule());
moduleManager.registerModule(new BroadcastModule()); moduleManager.registerModule(new BroadcastModule());
moduleManager.registerModule(new CommandBlockerModule()); moduleManager.registerModule(new CommandBlockerModule());
moduleManager.registerModule(new AutoMessageModule());
// CustomCommandModule nur laden, wenn aktiviert
boolean customCommandsEnabled = Boolean.parseBoolean(getVerifyProperties().getProperty("customcommands.enabled", "false"));
if (customCommandsEnabled) {
moduleManager.registerModule(new CustomCommandModule()); // Korrigiert: Kein Argument im Konstruktor
getLogger().info("CustomCommandModule aktiviert.");
} else {
getLogger().info("CustomCommandModule deaktiviert (siehe verify.properties).");
}
moduleManager.enableAll(this); moduleManager.enableAll(this);
@@ -71,22 +94,114 @@ public class StatusAPI extends Plugin implements Runnable {
thread = new Thread(this, "StatusAPI-HTTP-Server"); thread = new Thread(this, "StatusAPI-HTTP-Server");
thread.start(); thread.start();
// Update System // Einfacher Update-Check (Nur Benachrichtigung für Admins beim Start oder beim Join)
String currentVersion = getDescription() != null ? getDescription().getVersion() : "0.0.0"; checkForUpdatesAndNotify();
updateChecker = new UpdateChecker(this, currentVersion, 6); }
fileDownloader = new FileDownloader(this);
File pluginFile = getFile(); @EventHandler
File backupFile = new File(pluginFile.getParentFile(), "StatusAPI.jar.bak"); public void onPlayerJoin(PostLoginEvent event) {
ProxiedPlayer player = event.getPlayer();
if (backupFile.exists()) { // Wir prüfen hier nur, ob ein Update aussteht. Der Rest wird im Scheduler verzögert geprüft.
ProxyServer.getInstance().getScheduler().schedule(this, () -> { if (updatePending && isAdmin(player)) {
if (backupFile.exists()) backupFile.delete(); // Verzögerung um 2 Sekunden
},1, TimeUnit.MINUTES); getProxy().getScheduler().schedule(this, () -> {
// Prüfen, ob Spieler noch online ist und noch nicht informiert wurde
if (player.isConnected() && !informedAdmins.contains(player.getUniqueId()) && isAdmin(player)) {
sendStyledUpdateMessage(player);
informedAdmins.add(player.getUniqueId());
getLogger().info("Admin " + player.getName() + " wurde über das neue Update informiert.");
}
}, 2, TimeUnit.SECONDS);
} }
}
checkAndMaybeUpdate(); /**
ProxyServer.getInstance().getScheduler().schedule(this, this::checkAndMaybeUpdate, 6, 6, TimeUnit.HOURS); * Sendet eine formatierte Nachricht mit klickbarem Link.
*/
private void sendStyledUpdateMessage(ProxiedPlayer player) {
String currentVersion = getDescription() != null ? getDescription().getVersion() : "Unbekannt";
String downloadUrl = "https://git.viper.ipv64.net/M_Viper/StatusAPI/releases";
ComponentBuilder builder = new ComponentBuilder("");
// Zeile 1: Update Info
// Wir konvertieren Legacy-Strings zu BaseComponents und fügen sie an den Builder an
builder.append(TextComponent.fromLegacyText("§a[StatusAPI] "))
.append(TextComponent.fromLegacyText("§eEine neue Version ist verfügbar: "))
.append(TextComponent.fromLegacyText("§c" + latestVersionString))
.append(TextComponent.fromLegacyText(" §7(Deine Version: "))
.append(TextComponent.fromLegacyText("§c" + currentVersion))
.append(TextComponent.fromLegacyText("§7)\n"));
// Zeile 2: Download
builder.append(TextComponent.fromLegacyText("§7Download: "));
// Der Klickbare Teil
TextComponent link = new TextComponent("§e§nLink");
link.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, downloadUrl));
link.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText("§aKlicke hier um die Release-Seite zu öffnen")));
builder.append(link);
// Korrektur: Nur die Components senden, nicht den Spieler als Argument
player.sendMessage(builder.create());
}
/**
* Überprüft auf Updates und benachrichtigt Spieler mit Admin-Rechten (OP/Owner).
*/
private void checkForUpdatesAndNotify() {
String currentVersion = getDescription() != null ? getDescription().getVersion() : "0.0.0";
// Der UpdateChecker wird nur lokal instanziiert für den Check
UpdateChecker checker = new UpdateChecker(this, currentVersion, 6);
try {
checker.checkNow();
if (checker.isUpdateAvailable(currentVersion)) {
String newVersion = checker.getLatestVersion();
String url = checker.getLatestUrl();
getLogger().warning("------------------------------------------------");
getLogger().warning("Neue Version verfügbar: " + newVersion);
getLogger().warning("Aktuelle Version: " + currentVersion);
getLogger().warning("Download: " + url);
getLogger().warning("------------------------------------------------");
// Info speichern (nur Version, URL ist statisch für den Chat-Link)
this.updatePending = true;
this.latestVersionString = newVersion;
// Benachrichtigung an online Admins / OPs (mit Verzögerung, um konsistent mit Join zu sein)
boolean notifiedSomeone = false;
for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) {
if (isAdmin(p)) {
final ProxiedPlayer player = p;
getProxy().getScheduler().schedule(this, () -> {
if (player.isConnected() && !informedAdmins.contains(player.getUniqueId())) {
sendStyledUpdateMessage(player);
informedAdmins.add(player.getUniqueId());
}
}, 2, TimeUnit.SECONDS);
notifiedSomeone = true;
}
}
if (!notifiedSomeone) {
getLogger().info("Keine Admins waren online, um benachrichtigt zu werden. Sie werden beim Join informiert.");
}
} else {
getLogger().info("Keine Updates verfügbar.");
}
} catch (Exception e) {
getLogger().severe("Fehler beim Update-Check: " + e.getMessage());
}
}
// Hilfsmethode zum Prüfen von Admin-Rechten
private boolean isAdmin(ProxiedPlayer player) {
return player.hasPermission("statusapi.admin") || player.hasPermission("bungeecord.command.server");
} }
@Override @Override
@@ -190,8 +305,6 @@ public class StatusAPI extends Plugin implements Runnable {
"", "",
"# Beispiel: Deinen UUID hier einfügen", "# Beispiel: Deinen UUID hier einfügen",
"override.uuid-hier-einfügen = Owner", "override.uuid-hier-einfügen = Owner",
"override.uuid-hier-einfügen-2 = Admin",
"override.uuid-hier-einfügen-3 = Developer",
"", "",
"# ===========================", "# ===========================",
"# Chat-Formate für Gruppen", "# Chat-Formate für Gruppen",
@@ -209,10 +322,33 @@ public class StatusAPI extends Plugin implements Runnable {
"groupformat.spieler=&f[Spieler] || &7 || &8", "groupformat.spieler=&f[Spieler] || &7 || &8",
"", "",
"# ===========================", "# ===========================",
"# AUTOMESSAGE",
"# ===========================",
"# Aktiviert den automatischen Nachrichten-Rundruf",
"automessage.enabled=false",
"",
"# Zeitintervall in Sekunden (Standard: 300 = 5 Minuten)",
"automessage.interval=300",
"",
"# Optional: Ein Prefix, das VOR jede Nachricht aus der Datei gesetzt wird.",
"# Wenn du das Prefix bereits IN der messages.txt hast, lass dieses Feld einfach leer.",
"automessage.prefix=",
"",
"# Der Name der Datei, in der die Nachrichten stehen (liegt im Plugin-Ordner)",
"automessage.file=messages.txt",
"",
"# ===========================",
"# COMMAND BLOCKER", "# COMMAND BLOCKER",
"# ===========================", "# ===========================",
"commandblocker.enabled=true", "commandblocker.enabled=true",
"commandblocker.bypass.permission=commandblocker.bypass" "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"
); );
// 2. Existierende Werte einlesen // 2. Existierende Werte einlesen
@@ -321,69 +457,6 @@ public class StatusAPI extends Plugin implements Runnable {
} }
} }
// --- Update Logik ---
private void checkAndMaybeUpdate() {
try {
updateChecker.checkNow();
String currentVersion = getDescription() != null ? getDescription().getVersion() : "0.0.0";
if (updateChecker.isUpdateAvailable(currentVersion)) {
String newVersion = updateChecker.getLatestVersion();
String url = updateChecker.getLatestUrl();
getLogger().warning("----------------------------------------");
getLogger().warning("Neue Version verfügbar: " + newVersion);
getLogger().warning("Starte automatisches Update...");
getLogger().warning("----------------------------------------");
File pluginFile = getFile();
File newFile = new File(pluginFile.getParentFile(), "StatusAPI.jar.new");
fileDownloader.downloadFile(url, newFile, () -> triggerUpdateScript(pluginFile, newFile));
}
} catch (Exception e) {
getLogger().severe("Fehler beim Update-Check: " + e.getMessage());
}
}
private void triggerUpdateScript(File currentFile, File newFile) {
try {
File pluginsFolder = currentFile.getParentFile();
File rootFolder = pluginsFolder.getParentFile();
File batFile = new File(rootFolder, "StatusAPI_Update_" + System.currentTimeMillis() + ".bat");
String batContent = "@echo off\n" +
"echo Bitte warten, der Server fährt herunter...\n" +
"timeout /t 5 /nobreak >nul\n" +
"cd /d \"" + pluginsFolder.getAbsolutePath().replace("\\", "/") + "\"\n" +
"echo Fuehre Datei-Tausch durch...\n" +
"if exist StatusAPI.jar.bak del StatusAPI.jar.bak\n" +
"if exist StatusAPI.jar (\n" +
" ren StatusAPI.jar StatusAPI.jar.bak\n" +
")\n" +
"if exist StatusAPI.new.jar (\n" +
" ren StatusAPI.new.jar StatusAPI.jar\n" +
" echo Update erfolgreich!\n" +
") else (\n" +
" echo FEHLER: StatusAPI.new.jar nicht gefunden!\n" +
" pause\n" +
")\n" +
"del \"%~f0\"";
try (PrintWriter out = new PrintWriter(batFile)) { out.println(batContent); }
for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) {
p.disconnect("§cServer fährt für ein Update neu herunter. Bitte etwas warten.");
}
getLogger().info("Starte Update-Skript...");
Runtime.getRuntime().exec("cmd /c start \"Update_Proc\" \"" + batFile.getAbsolutePath() + "\"");
ProxyServer.getInstance().stop();
} catch (Exception e) {
getLogger().severe("Fehler beim Vorbereiten des Updates: " + e.getMessage());
}
}
// --- WebServer & JSON --- // --- WebServer & JSON ---
@Override @Override
public void run() { public void run() {

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,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 name: StatusAPI
main: net.viper.status.StatusAPI main: net.viper.status.StatusAPI
version: 4.0.6 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

View File

@@ -64,8 +64,6 @@ server.skyblock.secret=GeheimesWortFuerSkyBlock789
# Beispiel: Deinen UUID hier einfügen # Beispiel: Deinen UUID hier einfügen
override.uuid-hier-einfügen = Owner override.uuid-hier-einfügen = Owner
override.uuid-hier-einfügen = Admin
override.uuid-hier-einfügen = Developer
# =========================== # ===========================
# Chat-Formate für Gruppen # Chat-Formate für Gruppen
@@ -82,8 +80,31 @@ groupformat.developer=&b[Dev] || &3 || &a
groupformat.premium=&6[Premium] || &e || &7 groupformat.premium=&6[Premium] || &e || &7
groupformat.spieler=&f[Spieler] || &7 || &8 groupformat.spieler=&f[Spieler] || &7 || &8
# ===========================
# AUTOMESSAGE
# ===========================
# Aktiviert den automatischen Nachrichten-Rundruf
automessage.enabled=false
# Zeitintervall in Sekunden (Standard: 300 = 5 Minuten)
automessage.interval=300
# Optional: Ein Prefix, das VOR jede Nachricht aus der Datei gesetzt wird.
# Wenn du das Prefix bereits IN der messages.txt hast, lass dieses Feld einfach leer.
automessage.prefix=
# Der Name der Datei, in der die Nachrichten stehen (liegt im Plugin-Ordner)
automessage.file=messages.txt
# =========================== # ===========================
# COMMAND BLOCKER # COMMAND BLOCKER
# =========================== # ===========================
commandblocker.enabled=true commandblocker.enabled=true
commandblocker.bypass.permission=commandblocker.bypass 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

@@ -63,7 +63,7 @@ server.skyblock.secret=GeheimesWortFuerSkyBlock789
# WICHTIG: Die Gruppe (z.B. Owner) muss unten bei groupformat definiert sein! # WICHTIG: Die Gruppe (z.B. Owner) muss unten bei groupformat definiert sein!
# Beispiel: Deinen UUID hier einfügen # Beispiel: Deinen UUID hier einfügen
# override.uuid-hier-einfügen = Owner override.uuid-hier-einfügen = Owner
# =========================== # ===========================