Upload folder via GUI - src
This commit is contained in:
@@ -50,6 +50,7 @@ import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.time.LocalDate;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
@@ -78,6 +79,7 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
private String apiKey = "";
|
||||
private boolean flyRedeemDisabled = false;
|
||||
private String incomeReceiver = "";
|
||||
private int flyAboMaxDailySec = 21600; // 6 Stunden Standardlimit
|
||||
|
||||
// Sell-Feature
|
||||
private double sellPriceOffset = 0.0; // z.B. -10.0 = -10 % vom WordPress-Ankaufspreis
|
||||
@@ -93,6 +95,7 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
private FlyManager flyManager;
|
||||
private FlyCodeManager flyCodeManager;
|
||||
private RankManager rankManager;
|
||||
private FlyAboManager flyAboManager;
|
||||
|
||||
private static final String GUI_FLYCODES = ChatColor.GOLD + "✈ Deine Fly-Gutscheine";
|
||||
private final Map<UUID, Integer> flyCodesPage = new HashMap<>();
|
||||
@@ -135,6 +138,7 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
incomeReceiver = getConfig().getString("income-receiver", "");
|
||||
sellEnabled = getConfig().getBoolean("sell.enabled", true);
|
||||
sellPriceOffset = getConfig().getDouble("sell.price-offset", 0.0);
|
||||
flyAboMaxDailySec = getConfig().getInt("fly-abo.max-daily-hours", 6) * 3600;
|
||||
|
||||
if (apiKey.isEmpty()) {
|
||||
getLogger().warning("⚠️ Kein api-key in config.yml gesetzt!");
|
||||
@@ -150,6 +154,8 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
flyManager = new FlyManager(this);
|
||||
rankManager = new RankManager(this, flyCodeManager);
|
||||
rankManager.startExpiryChecker();
|
||||
flyAboManager = new FlyAboManager(this, flyCodeManager);
|
||||
flyAboManager.startDailyResetChecker();
|
||||
|
||||
int intervalSeconds = getConfig().getInt("check-interval", 10);
|
||||
long pollInterval = intervalSeconds * 20L;
|
||||
@@ -162,6 +168,9 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
getCommand("flygive").setExecutor(new FlyGiveCommand());
|
||||
getCommand("flypause").setExecutor(new FlyPauseCommand());
|
||||
getCommand("rankinfo").setExecutor(new RankInfoCommand());
|
||||
getCommand("flyabo").setExecutor(new FlyAboCommand());
|
||||
getCommand("flyabocancel").setExecutor(new FlyAboCancelCommand());
|
||||
getCommand("flyabogive").setExecutor(new FlyAboGiveCommand());
|
||||
getCommand("sell").setExecutor(new SellCommand());
|
||||
getCommand("wpis").setExecutor(new ReloadCommand());
|
||||
|
||||
@@ -344,6 +353,11 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
@Override public void run() { rankManager.checkExpiredRanksForPlayer(p); }
|
||||
}.runTaskAsynchronously(this);
|
||||
|
||||
// Fly-Abo prüfen und ggf. Fly für heute bereitstellen
|
||||
new BukkitRunnable() {
|
||||
@Override public void run() { flyAboManager.onPlayerJoin(p); }
|
||||
}.runTaskAsynchronously(this);
|
||||
|
||||
// Ausstehende Fly-Codes anzeigen
|
||||
new BukkitRunnable() {
|
||||
@Override public void run() {
|
||||
@@ -906,6 +920,16 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
}
|
||||
anyCode = true;
|
||||
|
||||
} else if ("fly_abo".equals(type)) {
|
||||
// Fly-Abo: Tage-basiertes Abonnement mit tägl. Stunden-Limit
|
||||
int days = cmdObj.has("days")
|
||||
? cmdObj.get("days").getAsInt() : 30;
|
||||
String label = cmdObj.has("label")
|
||||
? cmdObj.get("label").getAsString()
|
||||
: "Fly-Abo " + days + " Tage";
|
||||
flyAboManager.grantAbo(player, days, label);
|
||||
anyCode = true;
|
||||
|
||||
} else if ("generic".equals(type) && cmdObj.has("cmd")) {
|
||||
String cmd = cmdObj.get("cmd").getAsString()
|
||||
.replace("{player}", player.getName());
|
||||
@@ -1283,6 +1307,134 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// FLY-ABO COMMANDS
|
||||
// ===========================================================
|
||||
|
||||
/** /flyabo – Abo-Status anzeigen */
|
||||
private class FlyAboCommand implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command,
|
||||
String label, String[] args) {
|
||||
if (!(sender instanceof Player)) { sender.sendMessage("Nur für Spieler."); return true; }
|
||||
Player p = (Player) sender;
|
||||
new BukkitRunnable() {
|
||||
@Override public void run() {
|
||||
FlyAboManager.AboEntry abo = flyAboManager.getActiveAbo(p.getName());
|
||||
Bukkit.getScheduler().runTask(IngameShopSpigot.this, () -> {
|
||||
p.sendMessage(ChatColor.GOLD + "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
||||
p.sendMessage(ChatColor.YELLOW + "✈ Dein Fly-Abo:");
|
||||
p.sendMessage(ChatColor.GOLD + "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
||||
if (abo == null) {
|
||||
p.sendMessage(ChatColor.GRAY + " Kein aktives Fly-Abo.");
|
||||
p.sendMessage(ChatColor.GRAY + " Im Shop erhältlich: " + ChatColor.AQUA + "viper-network.de");
|
||||
} else {
|
||||
p.sendMessage(ChatColor.AQUA + " Status: " + ChatColor.GREEN + "✔ Aktiv");
|
||||
p.sendMessage(ChatColor.AQUA + " Paket: " + ChatColor.WHITE + abo.label);
|
||||
p.sendMessage(ChatColor.AQUA + " Läuft ab: " + ChatColor.YELLOW + abo.expiresAt);
|
||||
if (abo.cancelled)
|
||||
p.sendMessage(ChatColor.RED + " ⚠ Kündigung vorgemerkt – läuft zum o.g. Datum aus");
|
||||
int usedSec = flyAboManager.getUsedTodaySec(p.getName());
|
||||
int maxSec = flyAboMaxDailySec;
|
||||
int remSec = Math.max(0, maxSec - usedSec);
|
||||
p.sendMessage(ChatColor.AQUA + " Heute geflogen: "
|
||||
+ ChatColor.WHITE + flyManager.formatTime(usedSec)
|
||||
+ ChatColor.GRAY + " / "
|
||||
+ ChatColor.WHITE + flyManager.formatTime(maxSec));
|
||||
p.sendMessage(ChatColor.AQUA + " Heute noch verfügbar: "
|
||||
+ (remSec > 0 ? ChatColor.GREEN : ChatColor.RED)
|
||||
+ flyManager.formatTime(remSec));
|
||||
}
|
||||
p.sendMessage(ChatColor.GOLD + "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
||||
});
|
||||
}
|
||||
}.runTaskAsynchronously(IngameShopSpigot.this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/** /flyabocancel – Abo zum Ablauf kündigen */
|
||||
private class FlyAboCancelCommand implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command,
|
||||
String label, String[] args) {
|
||||
if (!(sender instanceof Player)) { sender.sendMessage("Nur für Spieler."); return true; }
|
||||
Player p = (Player) sender;
|
||||
// Bestätigung erforderlich
|
||||
if (args.length == 0 || !args[0].equalsIgnoreCase("confirm")) {
|
||||
p.sendMessage(ChatColor.YELLOW + "⚠ Möchtest du dein Fly-Abo wirklich kündigen?");
|
||||
p.sendMessage(ChatColor.GRAY + " Das Abo läuft bis zum Ablaufdatum weiter.");
|
||||
p.sendMessage(ChatColor.WHITE + " Bestätigen: " + ChatColor.RED + "/flyabocancel confirm");
|
||||
return true;
|
||||
}
|
||||
new BukkitRunnable() {
|
||||
@Override public void run() {
|
||||
boolean ok = flyAboManager.cancelAbo(p.getName());
|
||||
Bukkit.getScheduler().runTask(IngameShopSpigot.this, () -> {
|
||||
if (ok) {
|
||||
p.sendMessage(ChatColor.YELLOW + "✈ Dein Fly-Abo wurde zur Kündigung vorgemerkt.");
|
||||
p.sendMessage(ChatColor.GRAY + " Es bleibt bis zum Ablaufdatum aktiv.");
|
||||
p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1.0F, 0.8F);
|
||||
} else {
|
||||
p.sendMessage(ChatColor.RED + "✗ Kein aktives Fly-Abo gefunden.");
|
||||
}
|
||||
});
|
||||
}
|
||||
}.runTaskAsynchronously(IngameShopSpigot.this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/** /flyabogive <Spieler> <Tage> [Label] – Admin gibt Abo manuell */
|
||||
private class FlyAboGiveCommand implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command,
|
||||
String label, String[] args) {
|
||||
if (!sender.hasPermission("ingameshop.flyabogive")) {
|
||||
sender.sendMessage(ChatColor.RED + "✗ Keine Berechtigung.");
|
||||
return true;
|
||||
}
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage(ChatColor.YELLOW + "Verwendung: /flyabogive <Spieler> <Tage> [Label]");
|
||||
return true;
|
||||
}
|
||||
String targetName = args[0];
|
||||
int days;
|
||||
try { days = Integer.parseInt(args[1]); }
|
||||
catch (NumberFormatException e) {
|
||||
sender.sendMessage(ChatColor.RED + "✗ Tage muss eine Zahl sein.");
|
||||
return true;
|
||||
}
|
||||
String aboLabel = args.length >= 3
|
||||
? String.join(" ", Arrays.copyOfRange(args, 2, args.length))
|
||||
: "Fly-Abo " + days + " Tage";
|
||||
Player target = Bukkit.getPlayer(targetName);
|
||||
new BukkitRunnable() {
|
||||
@Override public void run() {
|
||||
flyAboManager.grantAboByName(targetName, days, aboLabel);
|
||||
Bukkit.getScheduler().runTask(IngameShopSpigot.this, () -> {
|
||||
sender.sendMessage(ChatColor.GREEN + "✔ Fly-Abo (" + days + " Tage) an "
|
||||
+ ChatColor.YELLOW + targetName + ChatColor.GREEN + " vergeben.");
|
||||
if (target != null && target.isOnline()) {
|
||||
target.sendMessage("");
|
||||
target.sendMessage(ChatColor.GOLD + "✈ ════════════════════════════");
|
||||
target.sendMessage(ChatColor.YELLOW + " Fly-Abo aktiviert!");
|
||||
target.sendMessage(ChatColor.GRAY + " Paket: " + ChatColor.WHITE + aboLabel);
|
||||
target.sendMessage(ChatColor.GRAY + " Tägl. Limit: " + ChatColor.WHITE
|
||||
+ flyManager.formatTime(flyAboMaxDailySec));
|
||||
target.sendMessage(ChatColor.GRAY + " Status: /flyabo");
|
||||
target.sendMessage(ChatColor.GOLD + " ════════════════════════════");
|
||||
target.sendMessage("");
|
||||
target.playSound(target.getLocation(),
|
||||
Sound.ENTITY_PLAYER_LEVELUP, 1.0F, 1.2F);
|
||||
}
|
||||
});
|
||||
}
|
||||
}.runTaskAsynchronously(IngameShopSpigot.this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// HTTP HILFSMETHODEN
|
||||
// ===========================================================
|
||||
@@ -1667,6 +1819,133 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
}
|
||||
return seconds + "s";
|
||||
}
|
||||
|
||||
/**
|
||||
* Wie grantFly, aber schreibt jede Sekunde die Nutzung in die Abo-Usage-Tabelle.
|
||||
* Stoppt automatisch wenn dailyLimitSec gesamt verbraucht ist.
|
||||
*/
|
||||
void grantFlyWithAboTracking(Player player, int seconds, String aboLabel,
|
||||
String playerName, int dailyLimitSec) {
|
||||
UUID uuid = player.getUniqueId();
|
||||
int existing = flySeconds.getOrDefault(uuid, 0);
|
||||
int total = existing + seconds;
|
||||
flySeconds.put(uuid, total);
|
||||
flyStartSecs.put(uuid, total);
|
||||
paused.remove(uuid);
|
||||
|
||||
player.setAllowFlight(true);
|
||||
player.setFlying(true);
|
||||
player.sendMessage(ChatColor.AQUA + "✈ Fly-Abo aktiv! Heute noch: "
|
||||
+ ChatColor.YELLOW + formatTime(seconds));
|
||||
|
||||
BossBar bar = flyBossBars.get(uuid);
|
||||
if (bar == null) {
|
||||
bar = Bukkit.createBossBar(
|
||||
buildAboBarTitle(seconds, dailyLimitSec, false),
|
||||
BarColor.GREEN, BarStyle.SEGMENTED_10);
|
||||
bar.addPlayer(player);
|
||||
flyBossBars.put(uuid, bar);
|
||||
} else {
|
||||
bar.setTitle(buildAboBarTitle(seconds, dailyLimitSec, false));
|
||||
bar.setProgress(1.0);
|
||||
bar.setColor(BarColor.GREEN);
|
||||
}
|
||||
|
||||
if (flyTasks.containsKey(uuid)) flyTasks.get(uuid).cancel();
|
||||
|
||||
final BossBar finalBar = bar;
|
||||
// Jede 30s Usage in DB schreiben
|
||||
final int[] dbBuffer = {0};
|
||||
|
||||
BukkitTask t = new BukkitRunnable() {
|
||||
@Override public void run() {
|
||||
Player p = Bukkit.getPlayer(uuid);
|
||||
if (p == null || !p.isOnline()) {
|
||||
// DB-Puffer flushen
|
||||
if (dbBuffer[0] > 0) {
|
||||
final int toFlush = dbBuffer[0];
|
||||
dbBuffer[0] = 0;
|
||||
new BukkitRunnable() {
|
||||
@Override public void run() {
|
||||
flyCodeManager.addUsedTodaySec(playerName, toFlush);
|
||||
}
|
||||
}.runTaskAsynchronously(plugin);
|
||||
}
|
||||
removeBossBar(uuid);
|
||||
flyTasks.remove(uuid);
|
||||
paused.remove(uuid);
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
if (paused.contains(uuid)) {
|
||||
int rem = flySeconds.getOrDefault(uuid, 0);
|
||||
finalBar.setTitle(buildAboBarTitle(rem, dailyLimitSec, true));
|
||||
finalBar.setColor(BarColor.WHITE);
|
||||
return;
|
||||
}
|
||||
int remaining = flySeconds.getOrDefault(uuid, 0) - 1;
|
||||
dbBuffer[0]++;
|
||||
// Alle 30s in DB flushen
|
||||
if (dbBuffer[0] >= 30) {
|
||||
final int toFlush = dbBuffer[0];
|
||||
dbBuffer[0] = 0;
|
||||
new BukkitRunnable() {
|
||||
@Override public void run() {
|
||||
flyCodeManager.addUsedTodaySec(playerName, toFlush);
|
||||
}
|
||||
}.runTaskAsynchronously(plugin);
|
||||
}
|
||||
if (remaining <= 0) {
|
||||
// Letzten Rest flushen
|
||||
if (dbBuffer[0] > 0) {
|
||||
final int toFlush = dbBuffer[0];
|
||||
new BukkitRunnable() {
|
||||
@Override public void run() {
|
||||
flyCodeManager.addUsedTodaySec(playerName, toFlush);
|
||||
}
|
||||
}.runTaskAsynchronously(plugin);
|
||||
}
|
||||
flySeconds.remove(uuid);
|
||||
flyTasks.remove(uuid);
|
||||
flyStartSecs.remove(uuid);
|
||||
removeBossBar(uuid);
|
||||
p.setFlying(false);
|
||||
p.setAllowFlight(false);
|
||||
p.sendMessage(ChatColor.RED + "✈ Fly-Abo Tageslimit erreicht!");
|
||||
p.sendMessage(ChatColor.GRAY + " Morgen stehen dir wieder "
|
||||
+ ChatColor.WHITE + formatTime(dailyLimitSec)
|
||||
+ ChatColor.GRAY + " zur Verfügung.");
|
||||
p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1.0F, 0.5F);
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
flySeconds.put(uuid, remaining);
|
||||
int start = flyStartSecs.getOrDefault(uuid, remaining);
|
||||
double progress = Math.max(0.0, Math.min(1.0, (double) remaining / start));
|
||||
finalBar.setProgress(progress);
|
||||
finalBar.setTitle(buildAboBarTitle(remaining, dailyLimitSec, false));
|
||||
if (remaining <= 300) finalBar.setColor(BarColor.RED);
|
||||
else if (remaining <= 1800) finalBar.setColor(BarColor.YELLOW);
|
||||
else finalBar.setColor(BarColor.GREEN);
|
||||
if (remaining == 3600 || remaining == 1800
|
||||
|| remaining == 600 || remaining == 300
|
||||
|| remaining == 60 || remaining == 30 || remaining == 10) {
|
||||
p.sendMessage(ChatColor.YELLOW + "✈ Fly-Abo noch " + formatTime(remaining) + " heute!");
|
||||
p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 0.5F, 1.2F);
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(plugin, 20L, 20L);
|
||||
flyTasks.put(uuid, t);
|
||||
}
|
||||
|
||||
private String buildAboBarTitle(int seconds, int dailyMax, boolean isPaused) {
|
||||
if (isPaused)
|
||||
return ChatColor.GRAY + "⏸ Fly-Abo pausiert: "
|
||||
+ ChatColor.WHITE + formatTime(seconds);
|
||||
return ChatColor.GREEN + "✈ Fly-Abo: "
|
||||
+ ChatColor.YELLOW + formatTime(seconds)
|
||||
+ ChatColor.GRAY + " / " + formatTime(dailyMax);
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
@@ -1909,6 +2188,132 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Fly-Abo CRUD ──────────────────────────────────────────────────
|
||||
|
||||
void createAboTable() {
|
||||
String sqlAbo =
|
||||
"CREATE TABLE IF NOT EXISTS wis_fly_abos ("
|
||||
+ " id INT AUTO_INCREMENT PRIMARY KEY,"
|
||||
+ " player_name VARCHAR(64) NOT NULL COLLATE utf8mb4_general_ci,"
|
||||
+ " label VARCHAR(128) NOT NULL,"
|
||||
+ " cancelled TINYINT(1) NOT NULL DEFAULT 0,"
|
||||
+ " expires_at DATETIME NOT NULL,"
|
||||
+ " granted_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,"
|
||||
+ " UNIQUE KEY uq_player (player_name)"
|
||||
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
|
||||
String sqlUsage =
|
||||
"CREATE TABLE IF NOT EXISTS wis_fly_abo_usage ("
|
||||
+ " id INT AUTO_INCREMENT PRIMARY KEY,"
|
||||
+ " player_name VARCHAR(64) NOT NULL COLLATE utf8mb4_general_ci,"
|
||||
+ " usage_date DATE NOT NULL,"
|
||||
+ " used_sec INT NOT NULL DEFAULT 0,"
|
||||
+ " UNIQUE KEY uq_player_date (player_name, usage_date)"
|
||||
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
|
||||
try (Statement stmt = connection.createStatement()) {
|
||||
stmt.execute(sqlAbo);
|
||||
stmt.execute(sqlUsage);
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Abo-Tabellen-Erstellung fehlgeschlagen", e);
|
||||
}
|
||||
}
|
||||
|
||||
void saveAbo(String playerName, int days, String label) {
|
||||
String sql =
|
||||
"INSERT INTO wis_fly_abos (player_name, label, cancelled, expires_at)"
|
||||
+ " VALUES (?, ?, 0, DATE_ADD(NOW(), INTERVAL ? DAY))"
|
||||
+ " ON DUPLICATE KEY UPDATE"
|
||||
+ " label = ?, cancelled = 0,"
|
||||
+ " expires_at = IF(expires_at > NOW(),"
|
||||
+ " DATE_ADD(expires_at, INTERVAL ? DAY),"
|
||||
+ " DATE_ADD(NOW(), INTERVAL ? DAY)),"
|
||||
+ " granted_at = NOW()";
|
||||
try (PreparedStatement ps = connection.prepareStatement(sql)) {
|
||||
ps.setString(1, playerName);
|
||||
ps.setString(2, label);
|
||||
ps.setInt(3, days);
|
||||
ps.setString(4, label);
|
||||
ps.setInt(5, days);
|
||||
ps.setInt(6, days);
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "saveAbo SQL Fehler", e);
|
||||
}
|
||||
}
|
||||
|
||||
boolean cancelAbo(String playerName) {
|
||||
String sql =
|
||||
"UPDATE wis_fly_abos SET cancelled = 1"
|
||||
+ " WHERE player_name = ? AND expires_at > NOW() AND cancelled = 0";
|
||||
try (PreparedStatement ps = connection.prepareStatement(sql)) {
|
||||
ps.setString(1, playerName);
|
||||
return ps.executeUpdate() == 1;
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "cancelAbo SQL Fehler", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
FlyAboManager.AboEntry getActiveAbo(String playerName) {
|
||||
String sql =
|
||||
"SELECT label, cancelled,"
|
||||
+ " DATE_FORMAT(expires_at, '%d.%m.%Y') AS expires_fmt"
|
||||
+ " FROM wis_fly_abos"
|
||||
+ " WHERE player_name = ? AND expires_at > NOW()";
|
||||
try (PreparedStatement ps = connection.prepareStatement(sql)) {
|
||||
ps.setString(1, playerName);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next())
|
||||
return new FlyAboManager.AboEntry(
|
||||
rs.getString("label"),
|
||||
rs.getString("expires_fmt"),
|
||||
rs.getInt("cancelled") == 1);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "getActiveAbo SQL Fehler", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void cleanupExpiredAbos(String playerName) {
|
||||
String sql = "DELETE FROM wis_fly_abos WHERE player_name = ? AND expires_at <= NOW()";
|
||||
try (PreparedStatement ps = connection.prepareStatement(sql)) {
|
||||
ps.setString(1, playerName);
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "cleanupExpiredAbos SQL Fehler", e);
|
||||
}
|
||||
}
|
||||
|
||||
int getUsedTodaySec(String playerName) {
|
||||
String sql =
|
||||
"SELECT used_sec FROM wis_fly_abo_usage"
|
||||
+ " WHERE player_name = ? AND usage_date = CURDATE()";
|
||||
try (PreparedStatement ps = connection.prepareStatement(sql)) {
|
||||
ps.setString(1, playerName);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) return rs.getInt("used_sec");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "getUsedTodaySec SQL Fehler", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void addUsedTodaySec(String playerName, int seconds) {
|
||||
String sql =
|
||||
"INSERT INTO wis_fly_abo_usage (player_name, usage_date, used_sec)"
|
||||
+ " VALUES (?, CURDATE(), ?)"
|
||||
+ " ON DUPLICATE KEY UPDATE used_sec = used_sec + ?";
|
||||
try (PreparedStatement ps = connection.prepareStatement(sql)) {
|
||||
ps.setString(1, playerName);
|
||||
ps.setInt(2, seconds);
|
||||
ps.setInt(3, seconds);
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "addUsedTodaySec SQL Fehler", e);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Fly-Code CRUD ────────────────────────────────────────────────
|
||||
|
||||
String generateCode(String playerName, int durationSec, String label) {
|
||||
@@ -2537,6 +2942,136 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
return removed;
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// FLY ABO MANAGER
|
||||
// ===========================================================
|
||||
|
||||
private class FlyAboManager {
|
||||
|
||||
private final IngameShopSpigot plugin;
|
||||
private final FlyCodeManager db;
|
||||
|
||||
FlyAboManager(IngameShopSpigot plugin, FlyCodeManager db) {
|
||||
this.plugin = plugin;
|
||||
this.db = db;
|
||||
db.createAboTable();
|
||||
}
|
||||
|
||||
// ── Abo vergeben (über Order) ──────────────────────────────────
|
||||
|
||||
void grantAbo(Player player, int days, String label) {
|
||||
grantAboByName(player.getName(), days, label);
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
player.sendMessage("");
|
||||
player.sendMessage(ChatColor.GOLD + "✈ ════════════════════════════");
|
||||
player.sendMessage(ChatColor.YELLOW + " Fly-Abo aktiviert!");
|
||||
player.sendMessage(ChatColor.GRAY + " Paket: " + ChatColor.WHITE + label);
|
||||
player.sendMessage(ChatColor.GRAY + " Laufzeit: " + ChatColor.WHITE + days + " Tag(e)");
|
||||
player.sendMessage(ChatColor.GRAY + " Tägl. Limit: " + ChatColor.WHITE
|
||||
+ flyManager.formatTime(flyAboMaxDailySec));
|
||||
player.sendMessage(ChatColor.GRAY + " Status: " + ChatColor.WHITE + "/flyabo");
|
||||
player.sendMessage(ChatColor.GOLD + " ════════════════════════════");
|
||||
player.sendMessage("");
|
||||
player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1.0F, 1.2F);
|
||||
});
|
||||
}
|
||||
|
||||
void grantAboByName(String playerName, int days, String label) {
|
||||
db.saveAbo(playerName, days, label);
|
||||
}
|
||||
|
||||
// ── Abo kündigen ───────────────────────────────────────────────
|
||||
|
||||
boolean cancelAbo(String playerName) {
|
||||
return db.cancelAbo(playerName);
|
||||
}
|
||||
|
||||
// ── Abo-Status lesen ──────────────────────────────────────────
|
||||
|
||||
AboEntry getActiveAbo(String playerName) {
|
||||
return db.getActiveAbo(playerName);
|
||||
}
|
||||
|
||||
int getUsedTodaySec(String playerName) {
|
||||
return db.getUsedTodaySec(playerName);
|
||||
}
|
||||
|
||||
// ── Beim Join: Abo prüfen ─────────────────────────────────────
|
||||
|
||||
void onPlayerJoin(Player player) {
|
||||
AboEntry abo = db.getActiveAbo(player.getName());
|
||||
if (abo == null) return;
|
||||
|
||||
// Abgelaufenes Abo bereinigen
|
||||
db.cleanupExpiredAbos(player.getName());
|
||||
abo = db.getActiveAbo(player.getName());
|
||||
if (abo == null) {
|
||||
Bukkit.getScheduler().runTask(plugin, () ->
|
||||
player.sendMessage(ChatColor.RED + "✈ Dein Fly-Abo ist abgelaufen."));
|
||||
return;
|
||||
}
|
||||
|
||||
int usedToday = db.getUsedTodaySec(player.getName());
|
||||
int remaining = Math.max(0, flyAboMaxDailySec - usedToday);
|
||||
if (remaining <= 0) {
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
player.sendMessage(ChatColor.YELLOW + "✈ Fly-Abo aktiv – Tageslimit heute bereits aufgebraucht.");
|
||||
player.sendMessage(ChatColor.GRAY + " Morgen stehen dir wieder "
|
||||
+ ChatColor.WHITE + flyManager.formatTime(flyAboMaxDailySec)
|
||||
+ ChatColor.GRAY + " zur Verfügung.");
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Fly mit verbleibender Tageszeit gewähren, aber in Abo-Modus
|
||||
final int grantSec = remaining;
|
||||
final String aboLabel = abo.label;
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
flyManager.grantFlyWithAboTracking(player, grantSec, aboLabel,
|
||||
player.getName(), flyAboMaxDailySec);
|
||||
});
|
||||
}
|
||||
|
||||
// ── Täglicher Reset-Check ─────────────────────────────────────
|
||||
|
||||
void startDailyResetChecker() {
|
||||
// Alle 5 Minuten prüfen ob Tagesdatum gewechselt hat → online Spieler neu versorgen
|
||||
new BukkitRunnable() {
|
||||
@Override public void run() {
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
new BukkitRunnable() {
|
||||
@Override public void run() {
|
||||
AboEntry abo = db.getActiveAbo(p.getName());
|
||||
if (abo == null) return;
|
||||
// Wenn Tageslimit noch nicht erreicht und kein Fly aktiv → erneut gewähren
|
||||
int usedToday = db.getUsedTodaySec(p.getName());
|
||||
int remaining = Math.max(0, flyAboMaxDailySec - usedToday);
|
||||
if (remaining > 0
|
||||
&& flyManager.getRemainingSeconds(p.getUniqueId()) <= 0) {
|
||||
Bukkit.getScheduler().runTask(plugin, () ->
|
||||
flyManager.grantFlyWithAboTracking(p, remaining, abo.label,
|
||||
p.getName(), flyAboMaxDailySec));
|
||||
}
|
||||
}
|
||||
}.runTaskAsynchronously(plugin);
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(plugin, 20L * 300, 20L * 300); // alle 5 Minuten
|
||||
}
|
||||
|
||||
// ── Data class ────────────────────────────────────────────────
|
||||
|
||||
static class AboEntry {
|
||||
final String label, expiresAt;
|
||||
final boolean cancelled;
|
||||
AboEntry(String label, String expiresAt, boolean cancelled) {
|
||||
this.label = label;
|
||||
this.expiresAt = expiresAt;
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// SELL MANAGER
|
||||
// ===========================================================
|
||||
|
||||
@@ -51,4 +51,11 @@ sell:
|
||||
# +5.0 → 5 % mehr als der WP-Ankaufspreis
|
||||
# Hinweis: Den Basis-Ankaufspreis konfigurierst du im WP-Admin
|
||||
# unter Items → <Item bearbeiten> → "Ankauf aktivieren"
|
||||
price-offset: -10.0
|
||||
price-offset: -10.0
|
||||
# ──────────────────────────────────────────────
|
||||
# Fly-Abo Einstellungen
|
||||
# ──────────────────────────────────────────────
|
||||
fly-abo:
|
||||
# Maximale Fly-Zeit pro Tag in Stunden
|
||||
# Standard: 6 (= 6 Stunden = 21.600 Sekunden)
|
||||
max-daily-hours: 6
|
||||
@@ -43,6 +43,18 @@ commands:
|
||||
description: IngameShop Admin-Befehle
|
||||
usage: /wpis <reload>
|
||||
permission: ingameshop.reload
|
||||
flyabo:
|
||||
description: Zeigt deinen Fly-Abo Status und Tageslimit an
|
||||
usage: /flyabo
|
||||
permission: ingameshop.flyabo
|
||||
flyabocancel:
|
||||
description: Kündigt dein Fly-Abo zum Ablaufdatum
|
||||
usage: /flyabocancel [confirm]
|
||||
permission: ingameshop.flyabocancel
|
||||
flyabogive:
|
||||
description: Gibt einem Spieler ein Fly-Abo (Admin)
|
||||
usage: /flyabogive <Spieler> <Tage> [Label]
|
||||
permission: ingameshop.flyabogive
|
||||
|
||||
permissions:
|
||||
ingameshop.orders:
|
||||
@@ -71,4 +83,13 @@ permissions:
|
||||
default: true
|
||||
ingameshop.reload:
|
||||
description: Kann die IngameShop-Config live neu laden
|
||||
default: op
|
||||
ingameshop.flyabo:
|
||||
description: Kann eigenen Fly-Abo-Status einsehen
|
||||
default: true
|
||||
ingameshop.flyabocancel:
|
||||
description: Kann eigenes Fly-Abo kündigen
|
||||
default: true
|
||||
ingameshop.flyabogive:
|
||||
description: Kann Spielern Fly-Abos manuell vergeben (Admin)
|
||||
default: op
|
||||
Reference in New Issue
Block a user