Files
TicketSystem/src/main/java/de/ticketsystem/TicketPlugin.java
2026-05-10 22:57:53 +02:00

369 lines
18 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package de.ticketsystem;
import de.ticketsystem.bungee.BungeeMessenger;
import de.ticketsystem.cache.TicketCache;
import de.ticketsystem.commands.TicketCommand;
import de.ticketsystem.database.DatabaseManager;
import de.ticketsystem.discord.DiscordWebhook;
import de.ticketsystem.gui.FaqGUI;
import de.ticketsystem.gui.TicketGUI;
import de.ticketsystem.listeners.PlayerJoinListener;
import de.ticketsystem.manager.CategoryManager;
import de.ticketsystem.manager.FaqManager;
import de.ticketsystem.manager.LanguageManager;
import de.ticketsystem.manager.TicketManager;
import de.ticketsystem.model.Ticket;
import de.ticketsystem.model.TicketPriority;
import de.ticketsystem.web.SessionManager;
import de.ticketsystem.web.WebServer;
import org.bukkit.ChatColor;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Objects;
public class TicketPlugin extends JavaPlugin {
private static TicketPlugin instance;
private boolean debug;
/**
* Name dieses Servers im BungeeCord-Netzwerk.
* Konfigurierbar in config.yml → server-name
*/
private String serverName;
private LanguageManager languageManager;
private DatabaseManager databaseManager;
private TicketManager ticketManager;
private CategoryManager categoryManager;
private FaqManager faqManager;
private TicketGUI ticketGUI;
private FaqGUI faqGUI;
private DiscordWebhook discordWebhook;
private BungeeMessenger bungeeMessenger;
private TicketCache ticketCache;
private SessionManager sessionManager;
private WebServer webServer;
@Override
public void onEnable() {
instance = this;
saveDefaultConfig();
// Ticket-Klasse für YAML-Serialisierung registrieren
Ticket.register();
// ── Sprachdatei laden (lang.yml) ──────────────────────────────────
// Muss VOR allen anderen Managern geschehen, da diese lang() nutzen.
languageManager = new LanguageManager(this);
TicketPriority.reloadLocalizedNames(this);
// ── BungeeCord Plugin-Messaging-Kanäle registrieren ───────────────
getServer().getMessenger().registerOutgoingPluginChannel(this, BungeeMessenger.BUNGEE_CHANNEL);
getServer().getMessenger().registerOutgoingPluginChannel(this, BungeeMessenger.CUSTOM_CHANNEL);
bungeeMessenger = new BungeeMessenger(this);
getServer().getMessenger().registerIncomingPluginChannel(this, BungeeMessenger.CUSTOM_CHANNEL, bungeeMessenger);
// Server-Name aus Config lesen
serverName = getConfig().getString("server-name", "unknown");
if ("unknown".equals(serverName)) {
getLogger().warning("[BungeeCord] Kein 'server-name' in der config.yml definiert!");
}
// BungeeCord-Hinweis nur bei deaktiviertem Feature und aktivem Debug ausgeben
if (getConfig().getBoolean("debug", false) && !getConfig().getBoolean("bungeecord", false)) {
getLogger().info("[BungeeCord] Cross-Server-Features deaktiviert. Setze 'bungeecord: true' um sie zu aktivieren.");
}
// Update-Checker
int resourceId = 132757;
new UpdateChecker(this, resourceId).getVersion(version -> {
String current = getDescription().getVersion();
if (!current.equals(version)) {
String msg = lang().format("update.available-line1", "{version}", version);
getLogger().warning(lang().format("update.available-console",
"{new}", version, "{current}", current));
getServer().getScheduler().runTaskLater(this, () ->
getServer().getOnlinePlayers().stream()
.filter(p -> p.hasPermission("ticket.admin"))
.forEach(p -> p.sendMessage(msg)), 20L);
}
});
// Versionsprüfung & automatische Migration der config.yml
migrateConfig();
debug = getConfig().getBoolean("debug", false);
// ── Performance: Ticket-Cache ──────────────────────────────────────
long cacheTtl = getConfig().getLong("cache-ttl-seconds", 60) * 1000L;
ticketCache = new TicketCache(cacheTtl);
// Regelmäßige Cache-Bereinigung alle 5 Minuten
getServer().getScheduler().runTaskTimerAsynchronously(this,
() -> ticketCache.evictExpired(), 6000L, 6000L);
// Datenbankverbindung
databaseManager = new DatabaseManager(this);
if (!databaseManager.connect()) {
getLogger().severe("Konnte keine Datenbankverbindung herstellen! Plugin läuft im Datei-Modus weiter.");
}
// Manager, GUI, FAQ & Discord-Webhook initialisieren
categoryManager = new CategoryManager(this);
ticketManager = new TicketManager(this);
ticketGUI = new TicketGUI(this);
discordWebhook = new DiscordWebhook(this);
// ── FAQ-System (nur initialisieren wenn aktiviert) ─────────────────
if (isFaqEnabled()) {
faqManager = new FaqManager(this);
faqGUI = new FaqGUI(this);
getLogger().info("[FAQ] FAQ-System aktiviert.");
} else {
getLogger().info("[FAQ] FAQ-System deaktiviert (faq-enabled: false in config.yml).");
}
if (getConfig().getBoolean("discord.enabled", false)) {
String url = getConfig().getString("discord.webhook-url", "");
if (url.isEmpty()) {
getLogger().warning("[DiscordWebhook] Aktiviert, aber keine Webhook-URL in config.yml eingetragen!");
}
}
// Commands & Events
TicketCommand ticketCommand = new TicketCommand(this);
Objects.requireNonNull(getCommand("ticket")).setExecutor(ticketCommand);
Objects.requireNonNull(getCommand("ticket")).setTabCompleter(ticketCommand);
getServer().getPluginManager().registerEvents(new PlayerJoinListener(this), this);
getServer().getPluginManager().registerEvents(ticketGUI, this);
if (faqGUI != null) {
getServer().getPluginManager().registerEvents(faqGUI, this);
}
// Automatische Archivierung
int archiveIntervalH = getConfig().getInt("auto-archive-interval-hours", 24);
if (archiveIntervalH > 0) {
long ticks = archiveIntervalH * 60L * 60L * 20L;
getServer().getScheduler().runTaskTimer(this, () -> {
int archived = databaseManager.archiveClosedTickets();
if (archived > 0) {
getLogger().info("Automatische Archivierung: " + archived + " Tickets archiviert.");
}
}, ticks, ticks);
}
// Web-Panel starten
if (getConfig().getBoolean("web-panel.enabled", false)) {
sessionManager = new SessionManager(this);
webServer = new WebServer(this, sessionManager);
webServer.start();
}
// ── PlaceholderAPI-Integration ────────────────────────────────────
if (getServer().getPluginManager().getPlugin("PlaceholderAPI") != null) {
new TicketPlaceholderExpansion(this).register();
getLogger().info("[PlaceholderAPI] Placeholder erfolgreich registriert.");
} else {
getLogger().warning("[PlaceholderAPI] PlaceholderAPI nicht gefunden Placeholder nicht verfügbar.");
}
getLogger().info("TicketSystem v" + getDescription().getVersion() + " erfolgreich gestartet!");
}
@Override
public void onDisable() {
getServer().getMessenger().unregisterOutgoingPluginChannel(this);
getServer().getMessenger().unregisterIncomingPluginChannel(this);
if (webServer != null) webServer.stop();
if (ticketCache != null) ticketCache.clear();
if (databaseManager != null) databaseManager.disconnect();
// Adventure BukkitAudiences sauber schließen
if (languageManager != null && languageManager.getAudiences() != null)
languageManager.getAudiences().close();
getLogger().info("TicketSystem wurde deaktiviert.");
}
// ─────────────────────────── Hilfsmethoden ─────────────────────────────
/**
* Gibt den LanguageManager zurück bevorzugte Methode für alle Texte.
*
* Verwendung:
* plugin.lang().get("ticket.created")
* plugin.lang().format("ticket.created", "{id}", String.valueOf(id))
* plugin.lang().send(player, "ticket.created", "{id}", String.valueOf(id))
*/
public LanguageManager lang() {
return languageManager;
}
/**
* Kompatibilitätsmethode für bestehenden Code.
* Liest Pfade der Form "messages.xxx" aus lang.yml (ohne "messages."-Prefix).
*
* Beispiel: formatMessage("messages.ticket-created") → lang "ticket.created"
*
* @deprecated Direkt {@link #lang()} verwenden.
*/
@Deprecated
public String formatMessage(String path) {
// "messages.ticket-created" → "ticket.created" (legacy-Mapping)
String langKey = mapLegacyPath(path);
if (langKey != null) {
return lang().formatWithPrefix(langKey);
}
// Fallback: Direkt in lang.yml nachschlagen
String value = lang().getRaw(path);
return lang().getPrefix() + lang().color(value);
}
/**
* Übersetzt &-Farbcodes in §-Farbcodes.
* Kompatibilitätsmethode bevorzugt lang().color() verwenden.
*/
public String color(String text) {
return ChatColor.translateAlternateColorCodes('&', text);
}
/**
* Mappt alte "messages.xxx"-Pfade auf neue lang.yml-Pfade.
* Muss ergänzt werden wenn neue Schlüssel im alten Stil genutzt wurden.
*/
private String mapLegacyPath(String path) {
if (path == null) return null;
return switch (path) {
case "messages.export-success" -> "system.export-success";
case "messages.export-fail" -> "system.export-fail";
case "messages.import-success" -> "system.import-success";
case "messages.import-fail" -> "system.import-fail";
case "messages.migration-success" -> "system.migration-success";
case "messages.migration-fail" -> "system.migration-fail";
case "messages.archive-success" -> "system.archive-success";
case "messages.archive-fail" -> "system.archive-fail";
case "messages.file-not-found" -> "system.file-not-found";
case "messages.unknown-mode" -> "system.unknown-mode";
case "messages.validation-warning" -> "system.validation-warning";
case "messages.ticket-created" -> "ticket.created";
case "messages.ticket-created-category" -> "ticket.created-category";
case "messages.ticket-claimed" -> "ticket.claimed";
case "messages.ticket-claimed-notify" -> "ticket.claimed-notify";
case "messages.ticket-closed" -> "ticket.closed";
case "messages.ticket-closed-notify" -> "ticket.closed-notify";
case "messages.ticket-forwarded" -> "ticket.forwarded";
case "messages.ticket-forwarded-notify" -> "ticket.forwarded-notify";
case "messages.ticket-forwarded-creator-notify" -> "ticket.forwarded-creator";
case "messages.new-ticket-notify" -> "ticket.new-notify";
case "messages.comment-saved" -> "comment.saved";
case "messages.comment-notify" -> "comment.notify-online";
case "messages.comment-no-permission" -> "comment.no-permission";
case "messages.rating-saved-good" -> "rating.saved-good";
case "messages.rating-saved-bad" -> "rating.saved-bad";
case "messages.rating-already-rated" -> "rating.already-rated";
case "messages.rating-not-yours" -> "rating.not-yours";
case "messages.rating-disabled" -> "rating.disabled";
case "messages.rating-prompt" -> "rating.prompt-title";
case "messages.blacklist-added" -> "blacklist.added";
case "messages.blacklist-removed" -> "blacklist.removed";
case "messages.blacklist-already" -> "blacklist.already";
case "messages.blacklist-not-found" -> "blacklist.not-found";
case "messages.blacklist-blocked" -> "create.blacklist-blocked";
case "messages.no-permission" -> "general.no-permission";
case "messages.no-open-tickets" -> "general.no-open-tickets";
case "messages.join-open-tickets" -> "join.open-tickets";
case "messages.already-claimed" -> "general.already-claimed";
case "messages.ticket-not-found" -> "general.ticket-not-found";
case "messages.cooldown" -> "general.cooldown";
case "messages.category-invalid" -> "create.category-invalid";
default -> null;
};
}
// ─────────────────────────── Config-Migration ─────────────────────────
/**
* Ergänzt fehlende Schlüssel in der config.yml automatisch aus der
* Standardkonfiguration (config.yml im JAR) und aktualisiert die Version.
* Bestehende Werte des Servers werden dabei NICHT überschrieben.
*/
private void migrateConfig() {
final String EXPECTED = "2.5";
String current = getConfig().getString("version", "");
if (EXPECTED.equals(current)) return; // nichts zu tun
getLogger().info("[Config] Alte Version erkannt (" + current + "). Starte automatische Migration auf " + EXPECTED + " ...");
// Alle Werte aus der Jar-Default-Config als Fallback setzen
// (addDefault überschreibt KEINE bereits gesetzten Werte)
org.bukkit.configuration.file.YamlConfiguration defaults =
org.bukkit.configuration.file.YamlConfiguration.loadConfiguration(
new java.io.InputStreamReader(
java.util.Objects.requireNonNull(getResource("config.yml")),
java.nio.charset.StandardCharsets.UTF_8));
int added = 0;
for (String key : defaults.getKeys(true)) {
if (!getConfig().contains(key)) {
getConfig().set(key, defaults.get(key));
getLogger().info("[Config] Neuer Schlüssel ergänzt: " + key);
added++;
}
}
// Version hochsetzen
getConfig().set("version", EXPECTED);
saveConfig();
if (added > 0) {
getLogger().info("[Config] Migration abgeschlossen: " + added + " neue Schlüssel ergänzt, Version → " + EXPECTED);
} else {
getLogger().info("[Config] Migration abgeschlossen: Keine neuen Schlüssel. Version → " + EXPECTED);
}
}
// ─────────────────────────── Getter ────────────────────────────────────
/**
* Aktualisiert serverName und debug-Flag aus der (bereits neu geladenen) Config.
* Muss nach plugin.reloadConfig() aufgerufen werden.
*/
public void reloadSettings() {
serverName = getConfig().getString("server-name", "unknown");
if ("unknown".equals(serverName)) {
getLogger().warning("[BungeeCord] Kein 'server-name' in der config.yml definiert!");
}
debug = getConfig().getBoolean("debug", false);
TicketPriority.reloadLocalizedNames(this);
}
/**
* Gibt zurück ob das FAQ-System aktiviert ist.
* Konfigurierbar in config.yml → faq-enabled (Standard: true)
*/
public boolean isFaqEnabled() {
return getConfig().getBoolean("faq-enabled", true);
}
public static TicketPlugin getInstance() { return instance; }
public LanguageManager getLanguageManager() { return languageManager; }
public DatabaseManager getDatabaseManager() { return databaseManager; }
public TicketManager getTicketManager() { return ticketManager; }
public CategoryManager getCategoryManager() { return categoryManager; }
public FaqManager getFaqManager() { return faqManager; }
public TicketGUI getTicketGUI() { return ticketGUI; }
public FaqGUI getFaqGUI() { return faqGUI; }
public DiscordWebhook getDiscordWebhook() { return discordWebhook; }
public BungeeMessenger getBungeeMessenger() { return bungeeMessenger; }
public TicketCache getTicketCache() { return ticketCache; }
public boolean isDebug() { return debug; }
public String getServerName() { return serverName; }
public boolean isBungeeCordEnabled() { return getConfig().getBoolean("bungeecord", false); }
public SessionManager getSessionManager() { return sessionManager; }
public WebServer getWebServer() { return webServer; }
}