Update from Git Manager GUI
This commit is contained in:
135
src/main/java/de/ticketsystem/TicketPlugin.java
Normal file
135
src/main/java/de/ticketsystem/TicketPlugin.java
Normal file
@@ -0,0 +1,135 @@
|
||||
|
||||
package de.ticketsystem;
|
||||
|
||||
import de.ticketsystem.commands.TicketCommand;
|
||||
import de.ticketsystem.database.DatabaseManager;
|
||||
import de.ticketsystem.gui.TicketGUI;
|
||||
import de.ticketsystem.listeners.PlayerJoinListener;
|
||||
import de.ticketsystem.manager.TicketManager;
|
||||
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;
|
||||
private DatabaseManager databaseManager;
|
||||
private TicketManager ticketManager;
|
||||
private TicketGUI ticketGUI;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
|
||||
// Config speichern falls nicht vorhanden
|
||||
saveDefaultConfig();
|
||||
|
||||
// Update-Checker (Spigot Resource-ID anpassen!)
|
||||
int resourceId = 132757;
|
||||
new UpdateChecker(this, resourceId).getVersion(version -> {
|
||||
String current = getDescription().getVersion();
|
||||
if (!current.equals(version)) {
|
||||
String msg = ChatColor.translateAlternateColorCodes('&',
|
||||
"&6[TicketSystem] &eEs ist eine neue Version verfügbar: &a" + version + " &7(aktuell: " + current + ")");
|
||||
getLogger().info("Es ist eine neue Version verfügbar: " + version + " (aktuell: " + current + ")");
|
||||
// Sende Nachricht an alle Admins (online) mit 1 Sekunde Verzögerung
|
||||
getServer().getScheduler().runTaskLater(this, () -> {
|
||||
getServer().getOnlinePlayers().stream()
|
||||
.filter(p -> p.hasPermission("ticket.admin"))
|
||||
.forEach(p -> p.sendMessage(msg));
|
||||
}, 20L); // 20 Ticks = 1 Sekunde
|
||||
} else {
|
||||
getLogger().info("TicketSystem ist aktuell (Version " + current + ")");
|
||||
}
|
||||
});
|
||||
|
||||
// Versionsprüfung
|
||||
String configVersion = getConfig().getString("version", "");
|
||||
String expectedVersion = "2.0";
|
||||
if (!expectedVersion.equals(configVersion)) {
|
||||
getLogger().warning("[WARNUNG] Die Version deiner config.yml (" + configVersion + ") stimmt nicht mit der erwarteten Version (" + expectedVersion + ") überein! Bitte prüfe, ob deine Konfiguration aktuell ist.");
|
||||
}
|
||||
|
||||
// Debug-Status aus Config lesen
|
||||
debug = getConfig().getBoolean("debug", false);
|
||||
|
||||
// Datenbankverbindung aufbauen
|
||||
databaseManager = new DatabaseManager(this);
|
||||
if (!databaseManager.connect()) {
|
||||
getLogger().severe("Konnte keine Datenbankverbindung herstellen! Plugin läuft im Datei-Modus weiter.");
|
||||
if (isDebug()) getLogger().warning("[DEBUG] DatabaseManager.connect() fehlgeschlagen, Datei-Modus aktiviert.");
|
||||
// Plugin bleibt aktiv, DatabaseManager wechselt auf Datei-Storage
|
||||
}
|
||||
|
||||
// Manager und GUI initialisieren
|
||||
ticketManager = new TicketManager(this);
|
||||
ticketGUI = new TicketGUI(this);
|
||||
|
||||
// Commands registrieren
|
||||
TicketCommand ticketCommand = new TicketCommand(this);
|
||||
Objects.requireNonNull(getCommand("ticket")).setExecutor(ticketCommand);
|
||||
Objects.requireNonNull(getCommand("ticket")).setTabCompleter(ticketCommand);
|
||||
|
||||
// Events registrieren
|
||||
getServer().getPluginManager().registerEvents(new PlayerJoinListener(this), this);
|
||||
getServer().getPluginManager().registerEvents(ticketGUI, this);
|
||||
|
||||
// Automatische Archivierung nach Zeitplan (Intervall in Stunden, Standard: 24h)
|
||||
int archiveIntervalH = getConfig().getInt("auto-archive-interval-hours", 24);
|
||||
if (archiveIntervalH > 0) {
|
||||
long ticks = archiveIntervalH * 60L * 60L * 20L; // Stunden → Ticks
|
||||
getServer().getScheduler().runTaskTimer(this, () -> {
|
||||
int archived = databaseManager.archiveClosedTickets();
|
||||
if (archived > 0) {
|
||||
getLogger().info("Automatische Archivierung: " + archived + " Tickets archiviert.");
|
||||
if (isDebug()) getLogger().info("[DEBUG] Archivierung ausgeführt, " + archived + " Tickets verschoben.");
|
||||
}
|
||||
}, ticks, ticks);
|
||||
getLogger().info("Automatische Archivierung alle " + archiveIntervalH + " Stunden aktiviert.");
|
||||
if (isDebug()) getLogger().info("[DEBUG] Archivierungs-Timer gesetzt: alle " + archiveIntervalH + " Stunden.");
|
||||
}
|
||||
|
||||
getLogger().info("TicketSystem erfolgreich gestartet!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (databaseManager != null) {
|
||||
databaseManager.disconnect();
|
||||
}
|
||||
getLogger().info("TicketSystem wurde deaktiviert.");
|
||||
}
|
||||
|
||||
// ─────────────────────────── Hilfsmethoden ─────────────────────────────
|
||||
|
||||
/**
|
||||
* Formatiert eine Nachricht aus der Config mit Prefix und Farben.
|
||||
*/
|
||||
public String formatMessage(String path) {
|
||||
String prefix = color(getConfig().getString("prefix", "&8[&6Ticket&8] &r"));
|
||||
String message = getConfig().getString(path, "&cNachricht nicht gefunden: " + path);
|
||||
return prefix + color(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Konvertiert Farbcodes (&x → §x).
|
||||
*/
|
||||
public String color(String text) {
|
||||
return ChatColor.translateAlternateColorCodes('&', text);
|
||||
}
|
||||
|
||||
// ─────────────────────────── Getter ────────────────────────────────────
|
||||
|
||||
public static TicketPlugin getInstance() { return instance; }
|
||||
public DatabaseManager getDatabaseManager() { return databaseManager; }
|
||||
public TicketManager getTicketManager() { return ticketManager; }
|
||||
public TicketGUI getTicketGUI() { return ticketGUI; }
|
||||
|
||||
/**
|
||||
* Gibt zurück, ob der Debug-Modus aktiv ist (aus config.yml)
|
||||
*/
|
||||
public boolean isDebug() { return debug; }
|
||||
}
|
||||
36
src/main/java/de/ticketsystem/UpdateChecker.java
Normal file
36
src/main/java/de/ticketsystem/UpdateChecker.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package de.ticketsystem;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.Scanner;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* UpdateChecker für SpigotMC-Plugins.
|
||||
* Prüft asynchron, ob eine neue Version verfügbar ist.
|
||||
* Quelle: https://www.spigotmc.org/wiki/creating-an-update-checker-that-checks-for-updates
|
||||
*/
|
||||
public class UpdateChecker {
|
||||
private final JavaPlugin plugin;
|
||||
private final int resourceId;
|
||||
|
||||
public UpdateChecker(JavaPlugin plugin, int resourceId) {
|
||||
this.plugin = plugin;
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
|
||||
public void getVersion(final Consumer<String> consumer) {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> {
|
||||
try (InputStream is = new URL("https://api.spigotmc.org/legacy/update.php?resource=" + this.resourceId).openStream(); Scanner scann = new Scanner(is)) {
|
||||
if (scann.hasNext()) {
|
||||
consumer.accept(scann.next());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().info("Unable to check for updates: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
422
src/main/java/de/ticketsystem/commands/TicketCommand.java
Normal file
422
src/main/java/de/ticketsystem/commands/TicketCommand.java
Normal file
@@ -0,0 +1,422 @@
|
||||
|
||||
package de.ticketsystem.commands;
|
||||
|
||||
import de.ticketsystem.TicketPlugin;
|
||||
import de.ticketsystem.model.Ticket;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
// Platzhalter für Admin-Kommandos
|
||||
private void handleMigrate(Player player, String[] args) {
|
||||
if (!player.hasPermission("ticket.admin")) {
|
||||
player.sendMessage(plugin.formatMessage("messages.no-permission"));
|
||||
return;
|
||||
}
|
||||
if (args.length < 2) {
|
||||
player.sendMessage(plugin.color("&cBenutzung: /ticket migrate <tomysql|tofile>"));
|
||||
return;
|
||||
}
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
int migrated = 0;
|
||||
String mode = args[1].toLowerCase();
|
||||
if (mode.equals("tomysql")) {
|
||||
migrated = plugin.getDatabaseManager().migrateToMySQL();
|
||||
} else if (mode.equals("tofile")) {
|
||||
migrated = plugin.getDatabaseManager().migrateToFile();
|
||||
} else {
|
||||
player.sendMessage(plugin.formatMessage("messages.unknown-mode"));
|
||||
return;
|
||||
}
|
||||
int finalMigrated = migrated;
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
if (finalMigrated > 0) {
|
||||
player.sendMessage(plugin.formatMessage("messages.migration-success")
|
||||
.replace("{count}", String.valueOf(finalMigrated)));
|
||||
} else {
|
||||
player.sendMessage(plugin.formatMessage("messages.migration-fail"));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void handleExport(Player player, String[] args) {
|
||||
if (!player.hasPermission("ticket.admin")) {
|
||||
player.sendMessage(plugin.formatMessage("messages.no-permission"));
|
||||
return;
|
||||
}
|
||||
if (args.length < 2) {
|
||||
player.sendMessage(plugin.color("&cBenutzung: /ticket export <Dateiname>"));
|
||||
return;
|
||||
}
|
||||
String filename = args[1];
|
||||
File exportFile = new File(plugin.getDataFolder(), filename);
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
int count = plugin.getDatabaseManager().exportTickets(exportFile);
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
if (count > 0) {
|
||||
player.sendMessage(plugin.formatMessage("messages.export-success")
|
||||
.replace("{count}", String.valueOf(count)).replace("{file}", filename));
|
||||
} else {
|
||||
player.sendMessage(plugin.formatMessage("messages.export-fail"));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void handleImport(Player player, String[] args) {
|
||||
if (!player.hasPermission("ticket.admin")) {
|
||||
player.sendMessage(plugin.formatMessage("messages.no-permission"));
|
||||
return;
|
||||
}
|
||||
if (args.length < 2) {
|
||||
player.sendMessage(plugin.color("&cBenutzung: /ticket import <Dateiname>"));
|
||||
return;
|
||||
}
|
||||
String filename = args[1];
|
||||
File importFile = new File(plugin.getDataFolder(), filename);
|
||||
if (!importFile.exists()) {
|
||||
player.sendMessage(plugin.formatMessage("messages.file-not-found").replace("{file}", filename));
|
||||
return;
|
||||
}
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
int count = plugin.getDatabaseManager().importTickets(importFile);
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
if (count > 0) {
|
||||
player.sendMessage(plugin.formatMessage("messages.import-success")
|
||||
.replace("{count}", String.valueOf(count)));
|
||||
} else {
|
||||
player.sendMessage(plugin.formatMessage("messages.import-fail"));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void handleStats(Player player) {
|
||||
if (!player.hasPermission("ticket.admin")) {
|
||||
player.sendMessage(plugin.formatMessage("messages.no-permission"));
|
||||
return;
|
||||
}
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
var stats = plugin.getDatabaseManager().getTicketStats();
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
player.sendMessage("§6--- Ticket Statistik ---");
|
||||
player.sendMessage("§eGesamt: §a" + stats.total + " §7| §eOffen: §a" + stats.open + " §7| §eGeschlossen: §a" + stats.closed + " §7| §eWeitergeleitet: §a" + stats.forwarded);
|
||||
player.sendMessage("§6Top Ersteller:");
|
||||
stats.byPlayer.entrySet().stream().sorted((a,b)->b.getValue()-a.getValue()).limit(5).forEach(e ->
|
||||
player.sendMessage("§e" + e.getKey() + ": §a" + e.getValue())
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ─────────────────────────── /ticket archive ────────────────────────────
|
||||
private void handleArchive(Player player) {
|
||||
if (!player.hasPermission("ticket.admin")) {
|
||||
player.sendMessage(plugin.formatMessage("messages.no-permission"));
|
||||
return;
|
||||
}
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
int count = plugin.getDatabaseManager().archiveClosedTickets();
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
if (count > 0) {
|
||||
player.sendMessage(plugin.formatMessage("messages.archive-success")
|
||||
.replace("{count}", String.valueOf(count)));
|
||||
} else {
|
||||
player.sendMessage(plugin.formatMessage("messages.archive-fail"));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private final TicketPlugin plugin;
|
||||
|
||||
public TicketCommand(TicketPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command,
|
||||
String label, String[] args) {
|
||||
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage("Dieser Befehl kann nur von Spielern ausgeführt werden.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
plugin.getTicketManager().sendHelpMessage(player);
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (args[0].toLowerCase()) {
|
||||
case "create" -> handleCreate(player, args);
|
||||
case "list" -> handleList(player);
|
||||
case "claim" -> handleClaim(player, args);
|
||||
case "close" -> handleClose(player, args);
|
||||
case "forward" -> handleForward(player, args);
|
||||
case "reload" -> handleReload(player);
|
||||
case "migrate" -> handleMigrate(player, args);
|
||||
case "export" -> handleExport(player, args);
|
||||
case "import" -> handleImport(player, args);
|
||||
case "stats" -> handleStats(player);
|
||||
case "archive" -> handleArchive(player);
|
||||
default -> plugin.getTicketManager().sendHelpMessage(player);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Methoden wie handleMigrate, handleCreate, handleList, handleClaim, handleClose, handleForward, handleReload, handleStats müssen auf Klassenebene stehen und dürfen nicht innerhalb von onCommand oder anderen Methoden verschachtelt sein.
|
||||
// Entferne alle verschachtelten Methoden und stelle sicher, dass jede Methode nur einmal und auf Klassenebene existiert.
|
||||
|
||||
// ─────────────────────────── /ticket create ────────────────────────────
|
||||
|
||||
private void handleCreate(Player player, String[] args) {
|
||||
if (!player.hasPermission("ticket.create")) {
|
||||
player.sendMessage(plugin.formatMessage("messages.no-permission"));
|
||||
return;
|
||||
}
|
||||
if (args.length < 2) {
|
||||
player.sendMessage(plugin.color("&cBenutzung: /ticket create <Beschreibung>"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Cooldown-Check
|
||||
if (plugin.getTicketManager().hasCooldown(player.getUniqueId())) {
|
||||
long remaining = plugin.getTicketManager().getRemainingCooldown(player.getUniqueId());
|
||||
player.sendMessage(plugin.formatMessage("messages.cooldown")
|
||||
.replace("{seconds}", String.valueOf(remaining)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Ticket-Limit-Check
|
||||
if (plugin.getTicketManager().hasReachedTicketLimit(player.getUniqueId())) {
|
||||
int max = plugin.getConfig().getInt("max-open-tickets-per-player", 2);
|
||||
player.sendMessage(plugin.color("&cDu hast bereits &e" + max + " &coffene Ticket(s). Bitte warte, bis dein Ticket bearbeitet wurde."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Nachricht zusammenbauen
|
||||
String message = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
|
||||
int maxLen = plugin.getConfig().getInt("max-description-length", 100);
|
||||
if (message.length() > maxLen) {
|
||||
player.sendMessage(plugin.color("&cDeine Beschreibung ist zu lang! Maximal " + maxLen + " Zeichen."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Ticket asynchron in DB speichern
|
||||
Ticket ticket = new Ticket(player.getUniqueId(), player.getName(), message, player.getLocation());
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
int id = plugin.getDatabaseManager().createTicket(ticket);
|
||||
if (id == -1) {
|
||||
player.sendMessage(plugin.color("&cFehler beim Erstellen des Tickets! Bitte wende dich an einen Admin."));
|
||||
return;
|
||||
}
|
||||
ticket.setId(id);
|
||||
plugin.getTicketManager().setCooldown(player.getUniqueId());
|
||||
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
player.sendMessage(plugin.formatMessage("messages.ticket-created")
|
||||
.replace("{id}", String.valueOf(id)));
|
||||
|
||||
// Team benachrichtigen
|
||||
plugin.getTicketManager().notifyTeam(ticket);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ─────────────────────────── /ticket list ──────────────────────────────
|
||||
|
||||
private void handleList(Player player) {
|
||||
if (!player.hasPermission("ticket.support") && !player.hasPermission("ticket.admin")) {
|
||||
player.sendMessage(plugin.formatMessage("messages.no-permission"));
|
||||
return;
|
||||
}
|
||||
// GUI öffnen (synchron, Datenbankabfrage läuft darin async)
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () ->
|
||||
Bukkit.getScheduler().runTask(plugin, () ->
|
||||
plugin.getTicketGUI().openGUI(player)));
|
||||
}
|
||||
|
||||
// ─────────────────────────── /ticket claim ─────────────────────────────
|
||||
|
||||
private void handleClaim(Player player, String[] args) {
|
||||
if (!player.hasPermission("ticket.support") && !player.hasPermission("ticket.admin")) {
|
||||
player.sendMessage(plugin.formatMessage("messages.no-permission"));
|
||||
return;
|
||||
}
|
||||
if (args.length < 2) {
|
||||
player.sendMessage(plugin.color("&cBenutzung: /ticket claim <ID>"));
|
||||
return;
|
||||
}
|
||||
|
||||
int id;
|
||||
try { id = Integer.parseInt(args[1]); }
|
||||
catch (NumberFormatException e) {
|
||||
player.sendMessage(plugin.color("&cUngültige ID!"));
|
||||
return;
|
||||
}
|
||||
|
||||
final int ticketId = id;
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
boolean success = plugin.getDatabaseManager().claimTicket(
|
||||
ticketId, player.getUniqueId(), player.getName());
|
||||
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
if (!success) {
|
||||
player.sendMessage(plugin.formatMessage("messages.already-claimed"));
|
||||
return;
|
||||
}
|
||||
Ticket ticket = plugin.getDatabaseManager().getTicketById(ticketId);
|
||||
if (ticket == null) return;
|
||||
|
||||
player.sendMessage(plugin.formatMessage("messages.ticket-claimed")
|
||||
.replace("{id}", String.valueOf(ticketId))
|
||||
.replace("{player}", ticket.getCreatorName()));
|
||||
|
||||
plugin.getTicketManager().notifyCreatorClaimed(ticket);
|
||||
|
||||
// Zur Ticket-Position teleportieren
|
||||
if (ticket.getLocation() != null) {
|
||||
player.teleport(ticket.getLocation());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ─────────────────────────── /ticket close ─────────────────────────────
|
||||
|
||||
private void handleClose(Player player, String[] args) {
|
||||
if (!player.hasPermission("ticket.support") && !player.hasPermission("ticket.admin")) {
|
||||
player.sendMessage(plugin.formatMessage("messages.no-permission"));
|
||||
return;
|
||||
}
|
||||
if (args.length < 2) {
|
||||
player.sendMessage(plugin.color("&cBenutzung: /ticket close <ID>"));
|
||||
return;
|
||||
}
|
||||
|
||||
int id;
|
||||
try { id = Integer.parseInt(args[1]); }
|
||||
catch (NumberFormatException e) {
|
||||
player.sendMessage(plugin.color("&cUngültige ID!"));
|
||||
return;
|
||||
}
|
||||
|
||||
final int ticketId = id;
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
boolean success = plugin.getDatabaseManager().closeTicket(ticketId);
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
if (success) {
|
||||
player.sendMessage(plugin.formatMessage("messages.ticket-closed")
|
||||
.replace("{id}", String.valueOf(ticketId)));
|
||||
} else {
|
||||
player.sendMessage(plugin.formatMessage("messages.ticket-not-found"));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ─────────────────────────── /ticket forward ───────────────────────────
|
||||
|
||||
private void handleForward(Player player, String[] args) {
|
||||
if (!player.hasPermission("ticket.admin")) {
|
||||
player.sendMessage(plugin.formatMessage("messages.no-permission"));
|
||||
return;
|
||||
}
|
||||
if (args.length < 3) {
|
||||
player.sendMessage(plugin.color("&cBenutzung: /ticket forward <ID> <Spieler>"));
|
||||
return;
|
||||
}
|
||||
|
||||
int id;
|
||||
try { id = Integer.parseInt(args[1]); }
|
||||
catch (NumberFormatException e) {
|
||||
player.sendMessage(plugin.color("&cUngültige ID!"));
|
||||
return;
|
||||
}
|
||||
|
||||
Player target = Bukkit.getPlayer(args[2]);
|
||||
if (target == null || !target.isOnline()) {
|
||||
player.sendMessage(plugin.color("&cSpieler &e" + args[2] + " &cist nicht online!"));
|
||||
return;
|
||||
}
|
||||
|
||||
final int ticketId = id;
|
||||
final Player finalTarget = target;
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
boolean success = plugin.getDatabaseManager().forwardTicket(
|
||||
ticketId, finalTarget.getUniqueId(), finalTarget.getName());
|
||||
|
||||
if (success) {
|
||||
Ticket ticket = plugin.getDatabaseManager().getTicketById(ticketId);
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
player.sendMessage(plugin.formatMessage("messages.ticket-forwarded")
|
||||
.replace("{id}", String.valueOf(ticketId))
|
||||
.replace("{player}", finalTarget.getName()));
|
||||
if (ticket != null) plugin.getTicketManager().notifyForwardedTo(ticket);
|
||||
});
|
||||
} else {
|
||||
Bukkit.getScheduler().runTask(plugin, () ->
|
||||
player.sendMessage(plugin.formatMessage("messages.ticket-not-found")));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ─────────────────────────── /ticket reload ────────────────────────────
|
||||
|
||||
private void handleReload(Player player) {
|
||||
if (!player.hasPermission("ticket.admin")) {
|
||||
player.sendMessage(plugin.formatMessage("messages.no-permission"));
|
||||
return;
|
||||
}
|
||||
plugin.reloadConfig();
|
||||
player.sendMessage(plugin.color("&aKonfiguration wurde neu geladen."));
|
||||
}
|
||||
|
||||
// ─────────────────────────── Tab-Completion ────────────────────────────
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command,
|
||||
String label, String[] args) {
|
||||
List<String> completions = new ArrayList<>();
|
||||
if (!(sender instanceof Player player)) return completions;
|
||||
|
||||
if (args.length == 1) {
|
||||
List<String> subs = new ArrayList<>();
|
||||
subs.add("create");
|
||||
if (player.hasPermission("ticket.support") || player.hasPermission("ticket.admin")) {
|
||||
subs.addAll(List.of("list", "claim", "close"));
|
||||
}
|
||||
if (player.hasPermission("ticket.admin")) {
|
||||
subs.addAll(List.of("forward", "reload"));
|
||||
}
|
||||
for (String s : subs) {
|
||||
if (s.startsWith(args[0].toLowerCase())) completions.add(s);
|
||||
}
|
||||
} else if (args.length == 3 && args[0].equalsIgnoreCase("forward")) {
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (p.getName().toLowerCase().startsWith(args[2].toLowerCase()))
|
||||
completions.add(p.getName());
|
||||
}
|
||||
}
|
||||
return completions;
|
||||
}
|
||||
}
|
||||
763
src/main/java/de/ticketsystem/database/DatabaseManager.java
Normal file
763
src/main/java/de/ticketsystem/database/DatabaseManager.java
Normal file
@@ -0,0 +1,763 @@
|
||||
|
||||
package de.ticketsystem.database;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import de.ticketsystem.TicketPlugin;
|
||||
import de.ticketsystem.model.Ticket;
|
||||
import de.ticketsystem.model.TicketStatus;
|
||||
import java.sql.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
public class DatabaseManager {
|
||||
// Test-Konstruktor für Unit-Tests (ohne Bukkit/Plugin)
|
||||
public DatabaseManager(File dataFile, YamlConfiguration dataConfig) {
|
||||
this.plugin = null;
|
||||
this.useMySQL = false;
|
||||
this.useJson = false;
|
||||
this.dataFileName = dataFile.getName();
|
||||
this.archiveFileName = "archive.json";
|
||||
this.dataFile = dataFile;
|
||||
this.dataConfig = dataConfig;
|
||||
validateLoadedTickets();
|
||||
}
|
||||
/**
|
||||
* Archiviert alle geschlossenen Tickets in eine separate Datei und entfernt sie aus dem aktiven Speicher.
|
||||
* @return Anzahl archivierter Tickets
|
||||
*/
|
||||
public int archiveClosedTickets() {
|
||||
List<Ticket> all = getAllTickets();
|
||||
List<Ticket> toArchive = new ArrayList<>();
|
||||
for (Ticket t : all) {
|
||||
if (t.getStatus() == TicketStatus.CLOSED) toArchive.add(t);
|
||||
}
|
||||
if (toArchive.isEmpty()) return 0;
|
||||
File archiveFile = new File(plugin.getDataFolder(), archiveFileName);
|
||||
JSONArray arr = new JSONArray();
|
||||
// Bestehendes Archiv laden
|
||||
if (archiveFile.exists()) {
|
||||
try (FileReader fr = new FileReader(archiveFile)) {
|
||||
JSONParser parser = new JSONParser();
|
||||
Object parsed = parser.parse(fr);
|
||||
if (parsed instanceof JSONArray oldArr) arr.addAll(oldArr);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
for (Ticket t : toArchive) arr.add(ticketToJson(t));
|
||||
try (FileWriter fw = new FileWriter(archiveFile)) {
|
||||
fw.write(arr.toJSONString());
|
||||
} catch (Exception e) {
|
||||
sendError("Fehler beim Archivieren: " + e.getMessage());
|
||||
return 0;
|
||||
}
|
||||
// Entferne archivierte Tickets aus aktivem Speicher
|
||||
int removed = 0;
|
||||
if (useMySQL) {
|
||||
try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement("DELETE FROM tickets WHERE id = ?")) {
|
||||
for (Ticket t : toArchive) {
|
||||
ps.setInt(1, t.getId());
|
||||
ps.executeUpdate();
|
||||
removed++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sendError("Fehler beim Entfernen archivierter Tickets: " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
for (Ticket t : toArchive) {
|
||||
dataConfig.set("tickets." + t.getId(), null);
|
||||
removed++;
|
||||
}
|
||||
try { dataConfig.save(dataFile); } catch (Exception e) { sendError("Fehler beim Speichern nach Archivierung: " + e.getMessage()); }
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
/**
|
||||
* Liefert Statistiken über Tickets.
|
||||
*/
|
||||
public TicketStats getTicketStats() {
|
||||
List<Ticket> all = getAllTickets();
|
||||
int open = 0, closed = 0, forwarded = 0;
|
||||
java.util.Map<String, Integer> byPlayer = new java.util.HashMap<>();
|
||||
for (Ticket t : all) {
|
||||
switch (t.getStatus()) {
|
||||
case OPEN -> open++;
|
||||
case CLOSED -> closed++;
|
||||
case FORWARDED -> forwarded++;
|
||||
}
|
||||
byPlayer.merge(t.getCreatorName(), 1, Integer::sum);
|
||||
}
|
||||
return new TicketStats(all.size(), open, closed, forwarded, byPlayer);
|
||||
}
|
||||
|
||||
public static class TicketStats {
|
||||
public final int total, open, closed, forwarded;
|
||||
public final java.util.Map<String, Integer> byPlayer;
|
||||
public TicketStats(int total, int open, int closed, int forwarded, java.util.Map<String, Integer> byPlayer) {
|
||||
this.total = total; this.open = open; this.closed = closed; this.forwarded = forwarded; this.byPlayer = byPlayer;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Exportiert alle Tickets als JSON-Datei.
|
||||
* @param exportFile Ziel-Datei
|
||||
* @return Anzahl exportierter Tickets
|
||||
*/
|
||||
public int exportTickets(File exportFile) {
|
||||
List<Ticket> tickets = getAllTickets();
|
||||
JSONArray arr = new JSONArray();
|
||||
for (Ticket t : tickets) {
|
||||
arr.add(ticketToJson(t));
|
||||
}
|
||||
try (FileWriter fw = new FileWriter(exportFile)) {
|
||||
fw.write(arr.toJSONString());
|
||||
return tickets.size();
|
||||
} catch (IOException e) {
|
||||
sendError("Fehler beim Export: " + e.getMessage());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Importiert Tickets aus einer JSON-Datei.
|
||||
* @param importFile Quell-Datei
|
||||
* @return Anzahl importierter Tickets
|
||||
*/
|
||||
public int importTickets(File importFile) {
|
||||
int imported = 0;
|
||||
try (FileReader fr = new FileReader(importFile)) {
|
||||
JSONParser parser = new JSONParser();
|
||||
JSONArray arr = (JSONArray) parser.parse(fr);
|
||||
for (Object o : arr) {
|
||||
JSONObject obj = (JSONObject) o;
|
||||
Ticket t = ticketFromJson(obj);
|
||||
if (t != null) {
|
||||
int id = createTicket(t);
|
||||
if (id != -1) imported++;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sendError("Fehler beim Import: " + e.getMessage());
|
||||
}
|
||||
return imported;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle Tickets (egal welcher Status) zurück.
|
||||
*/
|
||||
public List<Ticket> getAllTickets() {
|
||||
List<Ticket> list = new ArrayList<>();
|
||||
if (useMySQL) {
|
||||
try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) {
|
||||
ResultSet rs = stmt.executeQuery("SELECT * FROM tickets");
|
||||
while (rs.next()) list.add(mapRow(rs));
|
||||
} catch (SQLException e) {
|
||||
sendError("Fehler beim Abrufen aller Tickets: " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
if (dataConfig.contains("tickets")) {
|
||||
for (String key : dataConfig.getConfigurationSection("tickets").getKeys(false)) {
|
||||
Ticket t = (Ticket) dataConfig.get("tickets." + key);
|
||||
if (t != null) list.add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
// Hilfsmethoden für JSON-Konvertierung
|
||||
private JSONObject ticketToJson(Ticket t) {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("id", t.getId());
|
||||
obj.put("creatorUUID", t.getCreatorUUID().toString());
|
||||
obj.put("creatorName", t.getCreatorName());
|
||||
obj.put("message", t.getMessage());
|
||||
obj.put("world", t.getWorldName());
|
||||
obj.put("x", t.getX());
|
||||
obj.put("y", t.getY());
|
||||
obj.put("z", t.getZ());
|
||||
obj.put("yaw", t.getYaw());
|
||||
obj.put("pitch", t.getPitch());
|
||||
obj.put("status", t.getStatus().name());
|
||||
obj.put("createdAt", t.getCreatedAt() != null ? t.getCreatedAt().getTime() : null);
|
||||
obj.put("claimedAt", t.getClaimedAt() != null ? t.getClaimedAt().getTime() : null);
|
||||
obj.put("closedAt", t.getClosedAt() != null ? t.getClosedAt().getTime() : null);
|
||||
if (t.getClaimerUUID() != null) obj.put("claimerUUID", t.getClaimerUUID().toString());
|
||||
if (t.getClaimerName() != null) obj.put("claimerName", t.getClaimerName());
|
||||
if (t.getForwardedToUUID() != null) obj.put("forwardedToUUID", t.getForwardedToUUID().toString());
|
||||
if (t.getForwardedToName() != null) obj.put("forwardedToName", t.getForwardedToName());
|
||||
return obj;
|
||||
}
|
||||
|
||||
private Ticket ticketFromJson(JSONObject obj) {
|
||||
try {
|
||||
Ticket t = new Ticket();
|
||||
t.setId(((Long)obj.get("id")).intValue());
|
||||
t.setCreatorUUID(UUID.fromString((String)obj.get("creatorUUID")));
|
||||
t.setCreatorName((String)obj.get("creatorName"));
|
||||
t.setMessage((String)obj.get("message"));
|
||||
t.setWorldName((String)obj.get("world"));
|
||||
t.setX((Double)obj.get("x"));
|
||||
t.setY((Double)obj.get("y"));
|
||||
t.setZ((Double)obj.get("z"));
|
||||
t.setYaw(((Double)obj.get("yaw")).floatValue());
|
||||
t.setPitch(((Double)obj.get("pitch")).floatValue());
|
||||
t.setStatus(TicketStatus.valueOf((String)obj.get("status")));
|
||||
if (obj.get("createdAt") != null) t.setCreatedAt(new java.sql.Timestamp((Long)obj.get("createdAt")));
|
||||
if (obj.get("claimedAt") != null) t.setClaimedAt(new java.sql.Timestamp((Long)obj.get("claimedAt")));
|
||||
if (obj.get("closedAt") != null) t.setClosedAt(new java.sql.Timestamp((Long)obj.get("closedAt")));
|
||||
if (obj.get("claimerUUID") != null) t.setClaimerUUID(UUID.fromString((String)obj.get("claimerUUID")));
|
||||
if (obj.get("claimerName") != null) t.setClaimerName((String)obj.get("claimerName"));
|
||||
if (obj.get("forwardedToUUID") != null) t.setForwardedToUUID(UUID.fromString((String)obj.get("forwardedToUUID")));
|
||||
if (obj.get("forwardedToName") != null) t.setForwardedToName((String)obj.get("forwardedToName"));
|
||||
return t;
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().severe("Fehler beim Parsen eines Tickets: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Migriert alle Tickets aus data.yml nach MySQL.
|
||||
*/
|
||||
public int migrateToMySQL() {
|
||||
if (useMySQL || dataConfig == null) return 0;
|
||||
int migrated = 0;
|
||||
try {
|
||||
for (String key : dataConfig.getConfigurationSection("tickets").getKeys(false)) {
|
||||
Ticket t = (Ticket) dataConfig.get("tickets." + key);
|
||||
if (t != null) {
|
||||
// Ticket in MySQL speichern
|
||||
useMySQL = true;
|
||||
int id = createTicket(t);
|
||||
useMySQL = false;
|
||||
if (id != -1) migrated++;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().severe("Fehler bei Migration zu MySQL: " + e.getMessage());
|
||||
}
|
||||
return migrated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migriert alle Tickets aus MySQL nach data.yml.
|
||||
*/
|
||||
public int migrateToFile() {
|
||||
if (!useMySQL) return 0;
|
||||
int migrated = 0;
|
||||
try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) {
|
||||
ResultSet rs = stmt.executeQuery("SELECT * FROM tickets");
|
||||
while (rs.next()) {
|
||||
Ticket t = mapRow(rs);
|
||||
if (t != null) {
|
||||
useMySQL = false;
|
||||
int id = createTicket(t);
|
||||
useMySQL = true;
|
||||
if (id != -1) migrated++;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().severe("Fehler bei Migration zu Datei: " + e.getMessage());
|
||||
}
|
||||
return migrated;
|
||||
}
|
||||
private String dataFileName;
|
||||
private String archiveFileName;
|
||||
// Prüft geladene Tickets auf Korrektheit (Platzhalter)
|
||||
private void validateLoadedTickets() {
|
||||
if (dataConfig == null || !dataConfig.contains("tickets")) return;
|
||||
int invalid = 0;
|
||||
for (String key : dataConfig.getConfigurationSection("tickets").getKeys(false)) {
|
||||
Object obj = dataConfig.get("tickets." + key);
|
||||
if (!(obj instanceof Ticket t)) {
|
||||
sendError("Ungültiges Ticket-Objekt bei ID: " + key);
|
||||
invalid++;
|
||||
continue;
|
||||
}
|
||||
if (t.getCreatorUUID() == null || t.getCreatorName() == null || t.getMessage() == null || t.getStatus() == null) {
|
||||
sendError("Ticket mit fehlenden Pflichtfeldern: ID " + key);
|
||||
invalid++;
|
||||
}
|
||||
try { UUID.fromString(t.getCreatorUUID().toString()); } catch (Exception e) {
|
||||
sendError("Ungültige UUID bei Ticket ID: " + key);
|
||||
invalid++;
|
||||
}
|
||||
try { TicketStatus.valueOf(t.getStatus().name()); } catch (Exception e) {
|
||||
sendError("Ungültiger Status bei Ticket ID: " + key);
|
||||
invalid++;
|
||||
}
|
||||
}
|
||||
if (invalid > 0) {
|
||||
String msg = plugin != null ? plugin.formatMessage("messages.validation-warning").replace("{count}", String.valueOf(invalid)) : (invalid + " ungültige Tickets beim Laden gefunden.");
|
||||
sendError(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Backup der MySQL-Datenbank (Platzhalter)
|
||||
private void backupMySQL() {
|
||||
// TODO: Implementiere Backup-Logik für MySQL
|
||||
}
|
||||
|
||||
// Backup der Datei-basierten Daten (Platzhalter)
|
||||
private void backupDataFile() {
|
||||
// TODO: Implementiere Backup-Logik für data.yml/data.json
|
||||
}
|
||||
private final TicketPlugin plugin;
|
||||
private HikariDataSource dataSource;
|
||||
private boolean useMySQL;
|
||||
private boolean useJson;
|
||||
private File dataFile;
|
||||
private YamlConfiguration dataConfig;
|
||||
private JSONArray dataJson;
|
||||
|
||||
public DatabaseManager(TicketPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.useMySQL = plugin.getConfig().getBoolean("use-mysql", true);
|
||||
this.useJson = plugin.getConfig().getBoolean("use-json", false);
|
||||
if (plugin.isDebug()) plugin.getLogger().info("[DEBUG] DatabaseManager initialisiert. useMySQL=" + useMySQL + ", useJson=" + useJson);
|
||||
// Speicherpfade aus config.yml (absolut oder relativ zum Plugin-Ordner)
|
||||
String dataPath = plugin.getConfig().getString("data-file", useJson ? "data.json" : "data.yml");
|
||||
String archivePath = plugin.getConfig().getString("archive-file", "archive.json");
|
||||
this.dataFileName = dataPath;
|
||||
this.archiveFileName = archivePath;
|
||||
if (!useMySQL) {
|
||||
if (plugin.isDebug()) plugin.getLogger().info("[DEBUG] Datei-Speicher wird verwendet: " + dataPath);
|
||||
if (useJson) {
|
||||
dataFile = resolvePath(dataPath);
|
||||
if (plugin.isDebug()) plugin.getLogger().info("[DEBUG] JSON-Datei: " + dataFile.getAbsolutePath());
|
||||
if (!dataFile.exists()) {
|
||||
try {
|
||||
dataFile.getParentFile().mkdirs();
|
||||
dataFile.createNewFile();
|
||||
dataJson = new JSONArray();
|
||||
} catch (IOException e) {
|
||||
sendError("Konnte " + dataPath + " nicht erstellen: " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
JSONParser parser = new JSONParser();
|
||||
dataJson = (JSONArray) parser.parse(new java.io.FileReader(dataFile));
|
||||
} catch (Exception e) {
|
||||
sendError("Konnte " + dataPath + " nicht laden: " + e.getMessage());
|
||||
dataJson = new JSONArray();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dataFile = resolvePath(dataPath);
|
||||
if (plugin.isDebug()) plugin.getLogger().info("[DEBUG] YAML-Datei: " + dataFile.getAbsolutePath());
|
||||
if (!dataFile.exists()) {
|
||||
try {
|
||||
dataFile.getParentFile().mkdirs();
|
||||
dataFile.createNewFile();
|
||||
} catch (IOException e) {
|
||||
sendError("Konnte " + dataPath + " nicht erstellen: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
dataConfig = YamlConfiguration.loadConfiguration(dataFile);
|
||||
}
|
||||
validateLoadedTickets();
|
||||
}
|
||||
}
|
||||
|
||||
// Hilfsfunktion: Absoluten oder relativen Pfad auflösen
|
||||
private File resolvePath(String path) {
|
||||
File f = new File(path);
|
||||
if (f.isAbsolute()) return f;
|
||||
return new File(plugin.getDataFolder(), path);
|
||||
}
|
||||
|
||||
// Fehlerausgabe im Chat und Log
|
||||
private void sendError(String msg) {
|
||||
if (plugin != null) plugin.getLogger().severe(msg);
|
||||
// Fehler an alle Admins im Chat senden
|
||||
Bukkit.getOnlinePlayers().stream().filter(p -> p.hasPermission("ticket.admin")).forEach(p -> p.sendMessage("§c[TicketSystem] " + msg));
|
||||
}
|
||||
|
||||
// ─────────────────────────── Verbindung ────────────────────────────────
|
||||
|
||||
public boolean connect() {
|
||||
if (useMySQL) {
|
||||
try {
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setJdbcUrl(String.format("jdbc:mysql://%s:%d/%s?useSSL=false&autoReconnect=true&characterEncoding=UTF-8",
|
||||
plugin.getConfig().getString("mysql.host"),
|
||||
plugin.getConfig().getInt("mysql.port"),
|
||||
plugin.getConfig().getString("mysql.database")));
|
||||
config.setUsername(plugin.getConfig().getString("mysql.username"));
|
||||
config.setPassword(plugin.getConfig().getString("mysql.password"));
|
||||
config.setMaximumPoolSize(plugin.getConfig().getInt("mysql.pool-size", 10));
|
||||
config.setConnectionTimeout(plugin.getConfig().getLong("mysql.connection-timeout", 30000));
|
||||
config.setPoolName("TicketSystem-Pool");
|
||||
config.addDataSourceProperty("cachePrepStmts", "true");
|
||||
config.addDataSourceProperty("prepStmtCacheSize", "250");
|
||||
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
|
||||
|
||||
dataSource = new HikariDataSource(config);
|
||||
createTables();
|
||||
plugin.getLogger().info("MySQL-Verbindung erfolgreich hergestellt.");
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Fehler beim Verbinden mit MySQL: " + e.getMessage(), e);
|
||||
plugin.getLogger().warning("Weiche auf Datei-Speicherung (data.yml) aus!");
|
||||
useMySQL = false;
|
||||
// Datei-Storage initialisieren
|
||||
dataFile = new File(plugin.getDataFolder(), "data.yml");
|
||||
if (!dataFile.exists()) {
|
||||
try {
|
||||
dataFile.getParentFile().mkdirs();
|
||||
dataFile.createNewFile();
|
||||
} catch (IOException ex) {
|
||||
plugin.getLogger().severe("Konnte data.yml nicht erstellen: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
dataConfig = YamlConfiguration.loadConfiguration(dataFile);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
plugin.getLogger().info("MySQL deaktiviert. Verwende Datei-Speicherung (data.yml).");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
if (useMySQL && dataSource != null && !dataSource.isClosed()) {
|
||||
dataSource.close();
|
||||
plugin.getLogger().info("MySQL-Verbindung getrennt.");
|
||||
}
|
||||
// Bei Datei-Storage nichts zu tun
|
||||
}
|
||||
|
||||
private Connection getConnection() throws SQLException {
|
||||
return dataSource.getConnection();
|
||||
}
|
||||
|
||||
// ─────────────────────────── Tabellen erstellen ────────────────────────
|
||||
|
||||
private void createTables() {
|
||||
String sql = """
|
||||
CREATE TABLE IF NOT EXISTS tickets (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
creator_uuid VARCHAR(36) NOT NULL,
|
||||
creator_name VARCHAR(16) NOT NULL,
|
||||
message VARCHAR(255) NOT NULL,
|
||||
world VARCHAR(64) NOT NULL,
|
||||
x DOUBLE NOT NULL,
|
||||
y DOUBLE NOT NULL,
|
||||
z DOUBLE NOT NULL,
|
||||
yaw FLOAT NOT NULL DEFAULT 0,
|
||||
pitch FLOAT NOT NULL DEFAULT 0,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'OPEN',
|
||||
claimer_uuid VARCHAR(36),
|
||||
claimer_name VARCHAR(16),
|
||||
forwarded_to_uuid VARCHAR(36),
|
||||
forwarded_to_name VARCHAR(16),
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
claimed_at TIMESTAMP,
|
||||
closed_at TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
""";
|
||||
try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) {
|
||||
stmt.execute(sql);
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Fehler beim Erstellen der Tabellen: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────── CRUD ──────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Speichert ein neues Ticket in der DB und gibt die generierte ID zurück.
|
||||
*/
|
||||
public int createTicket(Ticket ticket) {
|
||||
if (useMySQL) {
|
||||
String sql = """
|
||||
INSERT INTO tickets (creator_uuid, creator_name, message, world, x, y, z, yaw, pitch)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""";
|
||||
try (Connection conn = getConnection();
|
||||
PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
|
||||
ps.setString(1, ticket.getCreatorUUID().toString());
|
||||
ps.setString(2, ticket.getCreatorName());
|
||||
ps.setString(3, ticket.getMessage());
|
||||
ps.setString(4, ticket.getWorldName());
|
||||
ps.setDouble(5, ticket.getX());
|
||||
ps.setDouble(6, ticket.getY());
|
||||
ps.setDouble(7, ticket.getZ());
|
||||
ps.setFloat(8, ticket.getYaw());
|
||||
ps.setFloat(9, ticket.getPitch());
|
||||
ps.executeUpdate();
|
||||
|
||||
ResultSet rs = ps.getGeneratedKeys();
|
||||
if (rs.next()) {
|
||||
backupMySQL();
|
||||
return rs.getInt(1);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Fehler beim Erstellen des Tickets: " + e.getMessage(), e);
|
||||
}
|
||||
return -1;
|
||||
} else {
|
||||
// Datei-Storage: Ticket-ID generieren
|
||||
int id = dataConfig.getInt("lastId", 0) + 1;
|
||||
ticket.setId(id);
|
||||
dataConfig.set("lastId", id);
|
||||
dataConfig.set("tickets." + id, ticket);
|
||||
try {
|
||||
dataConfig.save(dataFile);
|
||||
backupDataFile();
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().severe("Fehler beim Speichern von data.yml: " + e.getMessage());
|
||||
Bukkit.getOnlinePlayers().stream().filter(p -> p.hasPermission("ticket.admin")).forEach(p -> p.sendMessage("§c[TicketSystem] Fehler beim Speichern von data.yml: " + e.getMessage()));
|
||||
}
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Claimt ein Ticket (Status → CLAIMED).
|
||||
*/
|
||||
public boolean claimTicket(int ticketId, UUID claimerUUID, String claimerName) {
|
||||
if (useMySQL) {
|
||||
String sql = """
|
||||
UPDATE tickets SET status = 'CLAIMED', claimer_uuid = ?, claimer_name = ?, claimed_at = NOW()
|
||||
WHERE id = ? AND status = 'OPEN'
|
||||
""";
|
||||
try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||
ps.setString(1, claimerUUID.toString());
|
||||
ps.setString(2, claimerName);
|
||||
ps.setInt(3, ticketId);
|
||||
return ps.executeUpdate() > 0;
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Fehler beim Claimen des Tickets: " + e.getMessage(), e);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
Ticket t = getTicketById(ticketId);
|
||||
if (t == null || t.getStatus() != TicketStatus.OPEN) return false;
|
||||
t.setStatus(TicketStatus.CLAIMED);
|
||||
t.setClaimerUUID(claimerUUID);
|
||||
t.setClaimerName(claimerName);
|
||||
t.setClaimedAt(new java.sql.Timestamp(System.currentTimeMillis()));
|
||||
dataConfig.set("tickets." + ticketId, t);
|
||||
try {
|
||||
dataConfig.save(dataFile);
|
||||
backupDataFile();
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().severe("Fehler beim Speichern von data.yml: " + e.getMessage());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schließt ein Ticket (Status → CLOSED).
|
||||
*/
|
||||
public boolean closeTicket(int ticketId) {
|
||||
if (useMySQL) {
|
||||
String sql = "UPDATE tickets SET status = 'CLOSED', closed_at = NOW() WHERE id = ? AND status != 'CLOSED'";
|
||||
try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||
ps.setInt(1, ticketId);
|
||||
return ps.executeUpdate() > 0;
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Fehler beim Schließen des Tickets: " + e.getMessage(), e);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
Ticket t = getTicketById(ticketId);
|
||||
if (t == null || t.getStatus() == TicketStatus.CLOSED) return false;
|
||||
t.setStatus(TicketStatus.CLOSED);
|
||||
t.setClosedAt(new java.sql.Timestamp(System.currentTimeMillis()));
|
||||
dataConfig.set("tickets." + ticketId, t);
|
||||
try {
|
||||
dataConfig.save(dataFile);
|
||||
backupDataFile();
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().severe("Fehler beim Speichern von data.yml: " + e.getMessage());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Leitet ein Ticket an einen anderen Supporter weiter (Status → FORWARDED).
|
||||
*/
|
||||
public boolean forwardTicket(int ticketId, UUID toUUID, String toName) {
|
||||
if (useMySQL) {
|
||||
String sql = """
|
||||
UPDATE tickets SET status = 'FORWARDED', forwarded_to_uuid = ?, forwarded_to_name = ?
|
||||
WHERE id = ? AND status != 'CLOSED'
|
||||
""";
|
||||
try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||
ps.setString(1, toUUID.toString());
|
||||
ps.setString(2, toName);
|
||||
ps.setInt(3, ticketId);
|
||||
return ps.executeUpdate() > 0;
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Fehler beim Weiterleiten des Tickets: " + e.getMessage(), e);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
Ticket t = getTicketById(ticketId);
|
||||
if (t == null || t.getStatus() == TicketStatus.CLOSED) return false;
|
||||
t.setStatus(TicketStatus.FORWARDED);
|
||||
t.setForwardedToUUID(toUUID);
|
||||
t.setForwardedToName(toName);
|
||||
dataConfig.set("tickets." + ticketId, t);
|
||||
try {
|
||||
dataConfig.save(dataFile);
|
||||
backupDataFile();
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().severe("Fehler beim Speichern von data.yml: " + e.getMessage());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle Tickets mit einem bestimmten Status zurück.
|
||||
*/
|
||||
public List<Ticket> getTicketsByStatus(TicketStatus... statuses) {
|
||||
List<Ticket> list = new ArrayList<>();
|
||||
if (statuses.length == 0) return list;
|
||||
if (useMySQL) {
|
||||
StringBuilder placeholders = new StringBuilder("?");
|
||||
for (int i = 1; i < statuses.length; i++) placeholders.append(",?");
|
||||
String sql = "SELECT * FROM tickets WHERE status IN (" + placeholders + ") ORDER BY created_at ASC";
|
||||
try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||
for (int i = 0; i < statuses.length; i++) ps.setString(i + 1, statuses[i].name());
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while (rs.next()) list.add(mapRow(rs));
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Fehler beim Abrufen der Tickets: " + e.getMessage(), e);
|
||||
}
|
||||
return list;
|
||||
} else {
|
||||
// Datei-Storage: Alle Tickets filtern
|
||||
if (dataConfig.contains("tickets")) {
|
||||
for (String key : dataConfig.getConfigurationSection("tickets").getKeys(false)) {
|
||||
Ticket t = (Ticket) dataConfig.get("tickets." + key);
|
||||
for (TicketStatus status : statuses) {
|
||||
if (t != null && t.getStatus() == status) list.add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt ein einzelnes Ticket anhand der ID zurück.
|
||||
*/
|
||||
public Ticket getTicketById(int id) {
|
||||
if (useMySQL) {
|
||||
String sql = "SELECT * FROM tickets WHERE id = ?";
|
||||
try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||
ps.setInt(1, id);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
if (rs.next()) return mapRow(rs);
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Fehler beim Abrufen des Tickets: " + e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
if (dataConfig.contains("tickets." + id)) {
|
||||
return (Ticket) dataConfig.get("tickets." + id);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Anzahl offener Tickets (OPEN + FORWARDED) – für Join-Benachrichtigung.
|
||||
*/
|
||||
public int countOpenTickets() {
|
||||
if (useMySQL) {
|
||||
String sql = "SELECT COUNT(*) FROM tickets WHERE status IN ('OPEN', 'FORWARDED', 'CLAIMED')";
|
||||
try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) {
|
||||
ResultSet rs = stmt.executeQuery(sql);
|
||||
if (rs.next()) return rs.getInt(1);
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Fehler beim Zählen der Tickets: " + e.getMessage(), e);
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
int count = 0;
|
||||
if (dataConfig.contains("tickets")) {
|
||||
for (String key : dataConfig.getConfigurationSection("tickets").getKeys(false)) {
|
||||
Ticket t = (Ticket) dataConfig.get("tickets." + key);
|
||||
if (t != null && (t.getStatus() == TicketStatus.OPEN || t.getStatus() == TicketStatus.FORWARDED || t.getStatus() == TicketStatus.CLAIMED)) count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Anzahl offener Tickets eines bestimmten Spielers.
|
||||
*/
|
||||
public int countOpenTicketsByPlayer(UUID uuid) {
|
||||
if (useMySQL) {
|
||||
String sql = "SELECT COUNT(*) FROM tickets WHERE creator_uuid = ? AND status IN ('OPEN', 'CLAIMED', 'FORWARDED')";
|
||||
try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||
ps.setString(1, uuid.toString());
|
||||
ResultSet rs = ps.executeQuery();
|
||||
if (rs.next()) return rs.getInt(1);
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Fehler: " + e.getMessage(), e);
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
int count = 0;
|
||||
if (dataConfig.contains("tickets")) {
|
||||
for (String key : dataConfig.getConfigurationSection("tickets").getKeys(false)) {
|
||||
Ticket t = (Ticket) dataConfig.get("tickets." + key);
|
||||
if (t != null && uuid.equals(t.getCreatorUUID()) && (t.getStatus() == TicketStatus.OPEN || t.getStatus() == TicketStatus.CLAIMED || t.getStatus() == TicketStatus.FORWARDED)) count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────── Mapping ───────────────────────────────────
|
||||
|
||||
private Ticket mapRow(ResultSet rs) throws SQLException {
|
||||
File archiveFile = new File(plugin.getDataFolder(), archiveFileName);
|
||||
Ticket t = new Ticket();
|
||||
t.setId(rs.getInt("id"));
|
||||
t.setCreatorUUID(UUID.fromString(rs.getString("creator_uuid")));
|
||||
t.setCreatorName(rs.getString("creator_name"));
|
||||
t.setMessage(rs.getString("message"));
|
||||
t.setWorldName(rs.getString("world"));
|
||||
t.setX(rs.getDouble("x"));
|
||||
t.setY(rs.getDouble("y"));
|
||||
t.setZ(rs.getDouble("z"));
|
||||
t.setYaw(rs.getFloat("yaw"));
|
||||
t.setPitch(rs.getFloat("pitch"));
|
||||
t.setStatus(TicketStatus.valueOf(rs.getString("status")));
|
||||
t.setCreatedAt(rs.getTimestamp("created_at"));
|
||||
t.setClaimedAt(rs.getTimestamp("claimed_at"));
|
||||
t.setClosedAt(rs.getTimestamp("closed_at"));
|
||||
|
||||
String claimerUUID = rs.getString("claimer_uuid");
|
||||
if (claimerUUID != null) {
|
||||
t.setClaimerUUID(UUID.fromString(claimerUUID));
|
||||
t.setClaimerName(rs.getString("claimer_name"));
|
||||
}
|
||||
String fwdUUID = rs.getString("forwarded_to_uuid");
|
||||
if (fwdUUID != null) {
|
||||
t.setForwardedToUUID(UUID.fromString(fwdUUID));
|
||||
t.setForwardedToName(rs.getString("forwarded_to_name"));
|
||||
}
|
||||
return t;
|
||||
}
|
||||
}
|
||||
188
src/main/java/de/ticketsystem/gui/TicketGUI.java
Normal file
188
src/main/java/de/ticketsystem/gui/TicketGUI.java
Normal file
@@ -0,0 +1,188 @@
|
||||
package de.ticketsystem.gui;
|
||||
|
||||
import de.ticketsystem.TicketPlugin;
|
||||
import de.ticketsystem.model.Ticket;
|
||||
import de.ticketsystem.model.TicketStatus;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class TicketGUI implements Listener {
|
||||
|
||||
private static final String GUI_TITLE = "§8§lTicket-Übersicht";
|
||||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd.MM.yyyy HH:mm");
|
||||
|
||||
private final TicketPlugin plugin;
|
||||
|
||||
// Speichert welcher Spieler welches Ticket an welchem Slot hat
|
||||
private final Map<UUID, Map<Integer, Ticket>> playerSlotMap = new HashMap<>();
|
||||
|
||||
public TicketGUI(TicketPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
// ─────────────────────────── GUI öffnen ────────────────────────────────
|
||||
|
||||
public void openGUI(Player player) {
|
||||
List<Ticket> tickets = plugin.getDatabaseManager().getTicketsByStatus(
|
||||
TicketStatus.OPEN, TicketStatus.CLAIMED, TicketStatus.FORWARDED);
|
||||
|
||||
if (tickets.isEmpty()) {
|
||||
player.sendMessage(plugin.formatMessage("messages.no-open-tickets"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Inventar-Größe: nächste Vielfaches von 9 (max. 54 Slots)
|
||||
int size = Math.min(54, (int) (Math.ceil(tickets.size() / 9.0) * 9));
|
||||
if (size < 9) size = 9;
|
||||
|
||||
Inventory inv = Bukkit.createInventory(null, size, GUI_TITLE);
|
||||
Map<Integer, Ticket> slotMap = new HashMap<>();
|
||||
|
||||
for (int i = 0; i < tickets.size() && i < 54; i++) {
|
||||
Ticket ticket = tickets.get(i);
|
||||
ItemStack item = buildTicketItem(ticket);
|
||||
inv.setItem(i, item);
|
||||
slotMap.put(i, ticket);
|
||||
}
|
||||
|
||||
// Trennlinie am Ende, wenn Platz
|
||||
fillEmpty(inv);
|
||||
|
||||
playerSlotMap.put(player.getUniqueId(), slotMap);
|
||||
player.openInventory(inv);
|
||||
}
|
||||
|
||||
// ─────────────────────────── Item bauen ────────────────────────────────
|
||||
|
||||
private ItemStack buildTicketItem(Ticket ticket) {
|
||||
// Material je nach Status
|
||||
Material mat;
|
||||
switch (ticket.getStatus()) {
|
||||
case OPEN -> mat = Material.PAPER;
|
||||
case CLAIMED -> mat = Material.YELLOW_DYE;
|
||||
case FORWARDED -> mat = Material.ORANGE_DYE;
|
||||
default -> mat = Material.PAPER;
|
||||
}
|
||||
|
||||
ItemStack item = new ItemStack(mat);
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta == null) return item;
|
||||
|
||||
// Display-Name
|
||||
meta.setDisplayName("§6§lTicket #" + ticket.getId() + " §r" + ticket.getStatus().getColored());
|
||||
|
||||
// Lore aufbauen
|
||||
List<String> lore = new ArrayList<>();
|
||||
lore.add("§8§m ");
|
||||
lore.add("§7Ersteller: §e" + ticket.getCreatorName());
|
||||
lore.add("§7Anliegen: §f" + ticket.getMessage());
|
||||
lore.add("§7Erstellt: §e" + DATE_FORMAT.format(ticket.getCreatedAt()));
|
||||
lore.add("§7Welt: §e" + ticket.getWorldName());
|
||||
lore.add(String.format("§7Position: §e%.0f, %.0f, %.0f", ticket.getX(), ticket.getY(), ticket.getZ()));
|
||||
|
||||
if (ticket.getClaimerName() != null) {
|
||||
lore.add("§8§m ");
|
||||
lore.add("§7Geclaimt von: §a" + ticket.getClaimerName());
|
||||
if (ticket.getClaimedAt() != null)
|
||||
lore.add("§7Geclaimt am: §a" + DATE_FORMAT.format(ticket.getClaimedAt()));
|
||||
}
|
||||
if (ticket.getForwardedToName() != null) {
|
||||
lore.add("§7Weitergeleitet an: §6" + ticket.getForwardedToName());
|
||||
}
|
||||
|
||||
lore.add("§8§m ");
|
||||
if (ticket.getStatus() == TicketStatus.OPEN) {
|
||||
lore.add("§a§l» KLICKEN zum Claimen & Teleportieren");
|
||||
} else {
|
||||
lore.add("§e§l» KLICKEN zum Teleportieren");
|
||||
}
|
||||
|
||||
meta.setLore(lore);
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
}
|
||||
|
||||
private void fillEmpty(Inventory inv) {
|
||||
ItemStack glass = new ItemStack(Material.GRAY_STAINED_GLASS_PANE);
|
||||
ItemMeta meta = glass.getItemMeta();
|
||||
if (meta != null) { meta.setDisplayName(" "); glass.setItemMeta(meta); }
|
||||
for (int i = 0; i < inv.getSize(); i++) {
|
||||
if (inv.getItem(i) == null) inv.setItem(i, glass);
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────── Klick-Event ───────────────────────────────
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryClick(InventoryClickEvent event) {
|
||||
if (!(event.getWhoClicked() instanceof Player player)) return;
|
||||
if (!event.getView().getTitle().equals(GUI_TITLE)) return;
|
||||
|
||||
event.setCancelled(true);
|
||||
|
||||
Map<Integer, Ticket> slotMap = playerSlotMap.get(player.getUniqueId());
|
||||
if (slotMap == null) return;
|
||||
|
||||
int slot = event.getRawSlot();
|
||||
Ticket ticket = slotMap.get(slot);
|
||||
if (ticket == null) return;
|
||||
|
||||
player.closeInventory();
|
||||
|
||||
// Asynchron aus DB neu laden (aktuelle Daten)
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
Ticket fresh = plugin.getDatabaseManager().getTicketById(ticket.getId());
|
||||
if (fresh == null) {
|
||||
player.sendMessage(plugin.formatMessage("messages.ticket-not-found"));
|
||||
return;
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().runTask(plugin, () -> handleTicketClick(player, fresh));
|
||||
});
|
||||
}
|
||||
|
||||
private void handleTicketClick(Player player, Ticket ticket) {
|
||||
// Versuche zu claimen, wenn noch OPEN
|
||||
if (ticket.getStatus() == TicketStatus.OPEN) {
|
||||
boolean success = plugin.getDatabaseManager().claimTicket(
|
||||
ticket.getId(), player.getUniqueId(), player.getName());
|
||||
|
||||
if (success) {
|
||||
ticket.setStatus(TicketStatus.CLAIMED);
|
||||
ticket.setClaimerUUID(player.getUniqueId());
|
||||
ticket.setClaimerName(player.getName());
|
||||
|
||||
player.sendMessage(plugin.formatMessage("messages.ticket-claimed")
|
||||
.replace("{id}", String.valueOf(ticket.getId()))
|
||||
.replace("{player}", ticket.getCreatorName()));
|
||||
|
||||
plugin.getTicketManager().notifyCreatorClaimed(ticket);
|
||||
} else {
|
||||
player.sendMessage(plugin.formatMessage("messages.already-claimed"));
|
||||
}
|
||||
}
|
||||
|
||||
// Teleportation zur Ticket-Position
|
||||
if (ticket.getLocation() != null) {
|
||||
player.teleport(ticket.getLocation());
|
||||
player.sendMessage(plugin.color("&7Du wurdest zu Ticket &e#" + ticket.getId() + " &7teleportiert."));
|
||||
} else {
|
||||
player.sendMessage(plugin.color("&cDie Welt des Tickets ist nicht geladen!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package de.ticketsystem.listeners;
|
||||
|
||||
import de.ticketsystem.TicketPlugin;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
|
||||
public class PlayerJoinListener implements Listener {
|
||||
|
||||
private final TicketPlugin plugin;
|
||||
|
||||
public PlayerJoinListener(TicketPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
// Nur Supporter und Admins erhalten die Join-Benachrichtigung
|
||||
if (!player.hasPermission("ticket.support") && !player.hasPermission("ticket.admin")) return;
|
||||
|
||||
// Verzögerung von 2 Sekunden damit die Join-Sequenz abgeschlossen ist
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
int count = plugin.getDatabaseManager().countOpenTickets();
|
||||
|
||||
if (count > 0) {
|
||||
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||
String msg = plugin.formatMessage("messages.join-open-tickets")
|
||||
.replace("{count}", String.valueOf(count));
|
||||
player.sendMessage(msg);
|
||||
player.sendMessage(plugin.color("&7» Tippe &e/ticket list &7für die Übersicht."));
|
||||
}, 40L); // 40 Ticks = 2 Sekunden
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
116
src/main/java/de/ticketsystem/manager/TicketManager.java
Normal file
116
src/main/java/de/ticketsystem/manager/TicketManager.java
Normal file
@@ -0,0 +1,116 @@
|
||||
package de.ticketsystem.manager;
|
||||
|
||||
import de.ticketsystem.TicketPlugin;
|
||||
import de.ticketsystem.model.Ticket;
|
||||
import de.ticketsystem.model.TicketStatus;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class TicketManager {
|
||||
|
||||
private final TicketPlugin plugin;
|
||||
|
||||
// Cooldown Map: UUID → Zeit in Millis, wann das letzte Ticket erstellt wurde
|
||||
private final Map<UUID, Long> cooldowns = new HashMap<>();
|
||||
|
||||
public TicketManager(TicketPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
// ─────────────────────────── Cooldown ──────────────────────────────────
|
||||
|
||||
public boolean hasCooldown(UUID uuid) {
|
||||
if (!cooldowns.containsKey(uuid)) return false;
|
||||
long cooldownSeconds = plugin.getConfig().getLong("ticket-cooldown", 60) * 1000L;
|
||||
return (System.currentTimeMillis() - cooldowns.get(uuid)) < cooldownSeconds;
|
||||
}
|
||||
|
||||
public long getRemainingCooldown(UUID uuid) {
|
||||
long cooldownMillis = plugin.getConfig().getLong("ticket-cooldown", 60) * 1000L;
|
||||
long elapsed = System.currentTimeMillis() - cooldowns.getOrDefault(uuid, 0L);
|
||||
return Math.max(0, (cooldownMillis - elapsed) / 1000);
|
||||
}
|
||||
|
||||
public void setCooldown(UUID uuid) {
|
||||
cooldowns.put(uuid, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
// ─────────────────────────── Benachrichtigungen ────────────────────────
|
||||
|
||||
/**
|
||||
* Benachrichtigt alle Online-Supporter und Admins über ein neues Ticket.
|
||||
*/
|
||||
public void notifyTeam(Ticket ticket) {
|
||||
String msg = plugin.formatMessage("messages.new-ticket-notify")
|
||||
.replace("{player}", ticket.getCreatorName())
|
||||
.replace("{message}", ticket.getMessage())
|
||||
.replace("{id}", String.valueOf(ticket.getId()));
|
||||
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (p.hasPermission("ticket.support") || p.hasPermission("ticket.admin")) {
|
||||
p.sendMessage(msg);
|
||||
|
||||
// Klickbaren Hinweis senden (Bukkit Chat-Component)
|
||||
p.sendMessage(plugin.color("&7» Klicke &e/ticket list &7um die GUI zu öffnen."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Benachrichtigt den Ersteller des Tickets, wenn es geclaimt wurde.
|
||||
*/
|
||||
public void notifyCreatorClaimed(Ticket ticket) {
|
||||
Player creator = Bukkit.getPlayer(ticket.getCreatorUUID());
|
||||
if (creator != null && creator.isOnline()) {
|
||||
String msg = plugin.formatMessage("messages.ticket-claimed-notify")
|
||||
.replace("{id}", String.valueOf(ticket.getId()))
|
||||
.replace("{claimer}", ticket.getClaimerName());
|
||||
creator.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sendet dem weitergeleiteten Supporter eine Benachrichtigung.
|
||||
*/
|
||||
public void notifyForwardedTo(Ticket ticket) {
|
||||
Player target = Bukkit.getPlayer(ticket.getForwardedToUUID());
|
||||
if (target != null && target.isOnline()) {
|
||||
String msg = plugin.formatMessage("messages.ticket-forwarded-notify")
|
||||
.replace("{player}", ticket.getCreatorName())
|
||||
.replace("{id}", String.valueOf(ticket.getId()));
|
||||
target.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────── Hilfsmethoden ─────────────────────────────
|
||||
|
||||
/**
|
||||
* Prüft, ob ein Spieler zu viele offene Tickets hat.
|
||||
*/
|
||||
public boolean hasReachedTicketLimit(UUID uuid) {
|
||||
int max = plugin.getConfig().getInt("max-open-tickets-per-player", 2);
|
||||
if (max <= 0) return false;
|
||||
return plugin.getDatabaseManager().countOpenTicketsByPlayer(uuid) >= max;
|
||||
}
|
||||
|
||||
public void sendHelpMessage(Player player) {
|
||||
player.sendMessage(plugin.color("&8&m "));
|
||||
player.sendMessage(plugin.color("&6TicketSystem &7– Befehle"));
|
||||
player.sendMessage(plugin.color("&8&m "));
|
||||
player.sendMessage(plugin.color("&e/ticket create <Text> &7– Neues Ticket erstellen"));
|
||||
if (player.hasPermission("ticket.support") || player.hasPermission("ticket.admin")) {
|
||||
player.sendMessage(plugin.color("&e/ticket list &7– Ticket-Übersicht (GUI)"));
|
||||
player.sendMessage(plugin.color("&e/ticket claim <ID> &7– Ticket annehmen"));
|
||||
player.sendMessage(plugin.color("&e/ticket close <ID> &7– Ticket schließen"));
|
||||
}
|
||||
if (player.hasPermission("ticket.admin")) {
|
||||
player.sendMessage(plugin.color("&e/ticket forward <ID> <Spieler> &7– Ticket weiterleiten"));
|
||||
player.sendMessage(plugin.color("&e/ticket reload &7– Konfiguration neu laden"));
|
||||
}
|
||||
player.sendMessage(plugin.color("&8&m "));
|
||||
}
|
||||
}
|
||||
108
src/main/java/de/ticketsystem/model/Ticket.java
Normal file
108
src/main/java/de/ticketsystem/model/Ticket.java
Normal file
@@ -0,0 +1,108 @@
|
||||
package de.ticketsystem.model;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.UUID;
|
||||
|
||||
public class Ticket {
|
||||
|
||||
private int id;
|
||||
private UUID creatorUUID;
|
||||
private String creatorName;
|
||||
private String message;
|
||||
|
||||
// Location-Felder (werden separat gespeichert)
|
||||
private String worldName;
|
||||
private double x, y, z;
|
||||
private float yaw, pitch;
|
||||
|
||||
private TicketStatus status;
|
||||
private UUID claimerUUID;
|
||||
private String claimerName;
|
||||
private UUID forwardedToUUID;
|
||||
private String forwardedToName;
|
||||
private Timestamp createdAt;
|
||||
private Timestamp claimedAt;
|
||||
private Timestamp closedAt;
|
||||
|
||||
public Ticket() {}
|
||||
|
||||
public Ticket(UUID creatorUUID, String creatorName, String message, Location location) {
|
||||
this.creatorUUID = creatorUUID;
|
||||
this.creatorName = creatorName;
|
||||
this.message = message;
|
||||
this.worldName = location.getWorld().getName();
|
||||
this.x = location.getX();
|
||||
this.y = location.getY();
|
||||
this.z = location.getZ();
|
||||
this.yaw = location.getYaw();
|
||||
this.pitch = location.getPitch();
|
||||
this.status = TicketStatus.OPEN;
|
||||
this.createdAt = new Timestamp(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
World world = Bukkit.getWorld(worldName);
|
||||
if (world == null) return null;
|
||||
return new Location(world, x, y, z, yaw, pitch);
|
||||
}
|
||||
|
||||
// ─────────────────────────── Getter & Setter ────────────────────────────
|
||||
|
||||
public int getId() { return id; }
|
||||
public void setId(int id) { this.id = id; }
|
||||
|
||||
public UUID getCreatorUUID() { return creatorUUID; }
|
||||
public void setCreatorUUID(UUID creatorUUID) { this.creatorUUID = creatorUUID; }
|
||||
|
||||
public String getCreatorName() { return creatorName; }
|
||||
public void setCreatorName(String creatorName) { this.creatorName = creatorName; }
|
||||
|
||||
public String getMessage() { return message; }
|
||||
public void setMessage(String message) { this.message = message; }
|
||||
|
||||
public String getWorldName() { return worldName; }
|
||||
public void setWorldName(String worldName) { this.worldName = worldName; }
|
||||
|
||||
public double getX() { return x; }
|
||||
public void setX(double x) { this.x = x; }
|
||||
|
||||
public double getY() { return y; }
|
||||
public void setY(double y) { this.y = y; }
|
||||
|
||||
public double getZ() { return z; }
|
||||
public void setZ(double z) { this.z = z; }
|
||||
|
||||
public float getYaw() { return yaw; }
|
||||
public void setYaw(float yaw) { this.yaw = yaw; }
|
||||
|
||||
public float getPitch() { return pitch; }
|
||||
public void setPitch(float pitch) { this.pitch = pitch; }
|
||||
|
||||
public TicketStatus getStatus() { return status; }
|
||||
public void setStatus(TicketStatus status) { this.status = status; }
|
||||
|
||||
public UUID getClaimerUUID() { return claimerUUID; }
|
||||
public void setClaimerUUID(UUID claimerUUID) { this.claimerUUID = claimerUUID; }
|
||||
|
||||
public String getClaimerName() { return claimerName; }
|
||||
public void setClaimerName(String claimerName) { this.claimerName = claimerName; }
|
||||
|
||||
public UUID getForwardedToUUID() { return forwardedToUUID; }
|
||||
public void setForwardedToUUID(UUID forwardedToUUID) { this.forwardedToUUID = forwardedToUUID; }
|
||||
|
||||
public String getForwardedToName() { return forwardedToName; }
|
||||
public void setForwardedToName(String forwardedToName) { this.forwardedToName = forwardedToName; }
|
||||
|
||||
public Timestamp getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Timestamp createdAt) { this.createdAt = createdAt; }
|
||||
|
||||
public Timestamp getClaimedAt() { return claimedAt; }
|
||||
public void setClaimedAt(Timestamp claimedAt) { this.claimedAt = claimedAt; }
|
||||
|
||||
public Timestamp getClosedAt() { return closedAt; }
|
||||
public void setClosedAt(Timestamp closedAt) { this.closedAt = closedAt; }
|
||||
}
|
||||
20
src/main/java/de/ticketsystem/model/TicketStatus.java
Normal file
20
src/main/java/de/ticketsystem/model/TicketStatus.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package de.ticketsystem.model;
|
||||
|
||||
public enum TicketStatus {
|
||||
OPEN("Offen", "§a"),
|
||||
CLAIMED("Angenommen", "§e"),
|
||||
FORWARDED("Weitergeleitet", "§6"),
|
||||
CLOSED("Geschlossen", "§c");
|
||||
|
||||
private final String displayName;
|
||||
private final String color;
|
||||
|
||||
TicketStatus(String displayName, String color) {
|
||||
this.displayName = displayName;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public String getDisplayName() { return displayName; }
|
||||
public String getColor() { return color; }
|
||||
public String getColored() { return color + displayName; }
|
||||
}
|
||||
94
src/main/resources/config.yml
Normal file
94
src/main/resources/config.yml
Normal file
@@ -0,0 +1,94 @@
|
||||
# ============================================================
|
||||
# _____ _ _ _ ____ _
|
||||
# |_ _(_) ___| | _____| |_/ ___| _ _ ___| |_ ___ _ __ ___
|
||||
# | | | |/ __| |/ / _ \ __\___ \| | | / __| __/ _ \ '_ ` _ \
|
||||
# | | | | (__| < __/ |_ ___) | |_| \__ \ || __/ | | | | |
|
||||
# |_| |_|\___|_|\_\___|\__|____/ \__, |___/\__\___|_| |_| |_|
|
||||
# |___/
|
||||
#
|
||||
# TicketSystem - Ein einfaches und effizientes Ticketsystem für Minecraft-Server
|
||||
# Entwickelt von M_Viper
|
||||
# ============================================================
|
||||
|
||||
# --- GRUNDLEGEND ---
|
||||
# Version der Konfigurationsdatei. Nicht ändern!
|
||||
version: "2.0"
|
||||
|
||||
# Debug-Modus (true = Logs in der Konsole)
|
||||
debug: false
|
||||
|
||||
# ----------------------------------------------------
|
||||
# SPEICHERPFAD & ARCHIV
|
||||
# ----------------------------------------------------
|
||||
data-file: "data.yml" # Datei für Tickets (YAML/JSON)
|
||||
archive-file: "archive.yml" # Datei für Archiv (YAML/JSON)
|
||||
|
||||
# ----------------------------------------------------
|
||||
# SPEICHER-MODUS
|
||||
# ----------------------------------------------------
|
||||
use-mysql: false # true = MySQL, false = Datei
|
||||
use-json: false # true = JSON, false = YAML (nur bei Datei)
|
||||
|
||||
# ----------------------------------------------------
|
||||
# MYSQL-DATENBANK (Optional)
|
||||
# ----------------------------------------------------
|
||||
mysql:
|
||||
enabled: false
|
||||
host: "localhost"
|
||||
port: 3306
|
||||
database: "ticketsystem"
|
||||
username: "root"
|
||||
password: "password"
|
||||
pool-size: 10 # HikariCP Poolgröße
|
||||
connection-timeout: 30000 # Timeout in ms
|
||||
|
||||
# ----------------------------------------------------
|
||||
# PLUGIN-PRÄFIX (Chat)
|
||||
# ----------------------------------------------------
|
||||
prefix: "&8[&6Ticket&8] &r" # Präfix für Chat-Ausgaben
|
||||
|
||||
# ----------------------------------------------------
|
||||
# LIMITS & OPTIONEN
|
||||
# ----------------------------------------------------
|
||||
ticket-cooldown: 60 # Cooldown in Sekunden zwischen Ticket-Erstellungen
|
||||
max-description-length: 100 # Maximale Ticket-Beschreibungslänge
|
||||
max-open-tickets-per-player: 2 # Maximale offene Tickets pro Spieler (0 = unbegrenzt)
|
||||
|
||||
# ----------------------------------------------------
|
||||
# AUTOMATISCHE ARCHIVIERUNG
|
||||
# ----------------------------------------------------
|
||||
auto-archive-interval-hours: 24 # Intervall in Stunden (0 = aus)
|
||||
|
||||
# ----------------------------------------------------
|
||||
# SYSTEM-NACHRICHTEN (mit &-Farbcodes)
|
||||
# ----------------------------------------------------
|
||||
messages:
|
||||
# --- SYSTEM ---
|
||||
export-success: "&aExport erfolgreich: &e{count} &aTickets nach &e{file} &aexportiert."
|
||||
export-fail: "&cExport fehlgeschlagen oder keine Tickets gefunden."
|
||||
import-success: "&aImport erfolgreich: &e{count} &aTickets importiert."
|
||||
import-fail: "&cImport fehlgeschlagen oder keine Tickets gefunden."
|
||||
migration-success: "&aMigration abgeschlossen: &e{count} &aTickets migriert."
|
||||
migration-fail: "&cKeine Tickets migriert oder Fehler aufgetreten."
|
||||
archive-success: "&aArchivierung abgeschlossen: &e{count} &aTickets archiviert."
|
||||
archive-fail: "&cKeine geschlossenen Tickets zum Archivieren gefunden."
|
||||
file-not-found: "&cDatei nicht gefunden: &e{file}"
|
||||
unknown-mode: "&cUnbekannter Modus! Benutze: tomysql oder tofile"
|
||||
validation-warning: "&cEs wurden &e{count} &cungültige Tickets beim Laden gefunden."
|
||||
|
||||
# --- TICKET-AKTIONEN ---
|
||||
ticket-created: "&aTicket &e#{id} &awurde erfolgreich erstellt!"
|
||||
ticket-claimed: "&aDu hast Ticket &e#{id} &avon &e{player} &ageclaimt."
|
||||
ticket-claimed-notify: "&aDein Ticket &e#{id} &awurde von &e{claimer} &aangenommen."
|
||||
ticket-closed: "&aTicket &e#{id} &awurde geschlossen."
|
||||
ticket-forwarded: "&aTicket &e#{id} &awurde an &e{player} &aweitergeleitet."
|
||||
ticket-forwarded-notify: "&eDu hast ein Ticket von &6{player} &eweitergeleitet bekommen."
|
||||
|
||||
# --- FEHLER & HINWEISE ---
|
||||
no-permission: "&cDu hast keine Berechtigung!"
|
||||
no-open-tickets: "&aAktuell gibt es keine offenen Tickets."
|
||||
join-open-tickets: "&eEs gibt noch &6{count} &eoffene Ticket(s)!"
|
||||
new-ticket-notify: "&e{player} &ahat ein neues Ticket erstellt: &7{message}"
|
||||
already-claimed: "&cDieses Ticket wurde bereits geclaimt!"
|
||||
ticket-not-found: "&cTicket nicht gefunden!"
|
||||
cooldown: "&cBitte warte &e{seconds} Sekunden &cbevor du ein neues Ticket erstellst."
|
||||
25
src/main/resources/plugin.yml
Normal file
25
src/main/resources/plugin.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
name: TicketSystem
|
||||
version: 1.0.1
|
||||
main: de.ticketsystem.TicketPlugin
|
||||
api-version: 1.20
|
||||
author: M_Viper
|
||||
description: Ingame Support Ticket System with MySQL
|
||||
|
||||
commands:
|
||||
ticket:
|
||||
description: Ticket System Hauptbefehl
|
||||
usage: /ticket <create|list|claim|close|forward|reload>
|
||||
aliases: [t, support]
|
||||
|
||||
permissions:
|
||||
ticket.create:
|
||||
description: Spieler kann Tickets erstellen
|
||||
default: true
|
||||
|
||||
ticket.support:
|
||||
description: Supporter kann Tickets einsehen und claimen
|
||||
default: false
|
||||
|
||||
ticket.admin:
|
||||
description: Admin hat vollen Zugriff inkl. Weiterleitung und Reload
|
||||
default: op
|
||||
@@ -0,0 +1,74 @@
|
||||
package de.ticketsystem.database;
|
||||
|
||||
import de.ticketsystem.TicketPlugin;
|
||||
import de.ticketsystem.model.Ticket;
|
||||
import de.ticketsystem.model.TicketStatus;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.junit.jupiter.api.*;
|
||||
import java.io.File;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class DatabaseManagerFileTest {
|
||||
// Dummy-Plugin-Implementierung für Tests (vermeidet Mockito)
|
||||
private DatabaseManager db;
|
||||
private File testFile;
|
||||
private org.bukkit.configuration.file.YamlConfiguration config;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
testFile = new File("build/testdata/testdata.yml");
|
||||
if (testFile.exists()) testFile.delete();
|
||||
config = new org.bukkit.configuration.file.YamlConfiguration();
|
||||
db = new DatabaseManager(testFile, config);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateAndGetTicket() {
|
||||
Ticket t = new Ticket();
|
||||
t.setCreatorUUID(UUID.randomUUID());
|
||||
t.setCreatorName("Tester");
|
||||
t.setMessage("Testnachricht");
|
||||
t.setWorldName("world");
|
||||
t.setX(1.0);
|
||||
t.setY(2.0);
|
||||
t.setZ(3.0);
|
||||
t.setYaw(0);
|
||||
t.setPitch(0);
|
||||
t.setStatus(TicketStatus.OPEN);
|
||||
t.setCreatedAt(new java.sql.Timestamp(System.currentTimeMillis()));
|
||||
int id = db.createTicket(t);
|
||||
assertTrue(id > 0);
|
||||
Ticket loaded = db.getTicketById(id);
|
||||
assertNotNull(loaded);
|
||||
assertEquals("Tester", loaded.getCreatorName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExportImportTickets() {
|
||||
Ticket t = new Ticket();
|
||||
t.setCreatorUUID(UUID.randomUUID());
|
||||
t.setCreatorName("ExportUser");
|
||||
t.setMessage("ExportTest");
|
||||
t.setWorldName("world");
|
||||
t.setX(1.0);
|
||||
t.setY(2.0);
|
||||
t.setZ(3.0);
|
||||
t.setYaw(0);
|
||||
t.setPitch(0);
|
||||
t.setStatus(TicketStatus.OPEN);
|
||||
t.setCreatedAt(new java.sql.Timestamp(System.currentTimeMillis()));
|
||||
db.createTicket(t);
|
||||
File exportFile = new File("build/testdata/export.json");
|
||||
int exported = db.exportTickets(exportFile);
|
||||
assertTrue(exported > 0);
|
||||
db.importTickets(exportFile); // sollte keine Exception werfen
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
if (testFile.exists()) testFile.delete();
|
||||
new File("build/testdata/export.json").delete();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user