Upload folder via GUI - src
This commit is contained in:
@@ -921,13 +921,11 @@ 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;
|
||||
// Fly-Abo: monatliche Abbuchung, Preis des Shop-Artikels = Monatsbeitrag
|
||||
String label = cmdObj.has("label")
|
||||
? cmdObj.get("label").getAsString()
|
||||
: "Fly-Abo " + days + " Tage";
|
||||
flyAboManager.grantAbo(player, days, label);
|
||||
? cmdObj.get("label").getAsString() : "Fly-Abo";
|
||||
int monthlyPrice = (int) data.price;
|
||||
flyAboManager.grantAbo(player, label, monthlyPrice);
|
||||
anyCode = true;
|
||||
|
||||
} else if ("generic".equals(type) && cmdObj.has("cmd")) {
|
||||
@@ -1329,11 +1327,20 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
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");
|
||||
p.sendMessage(ChatColor.AQUA + " Paket: " + ChatColor.WHITE + abo.label);
|
||||
p.sendMessage(ChatColor.AQUA + " Preis: " + ChatColor.WHITE + abo.monthlyPrice + " " + currency + " / Monat");
|
||||
if (abo.cancelled) {
|
||||
String reason = "payment_failed".equals(abo.cancellationReason)
|
||||
? " (Zahlung fehlgeschlagen)" : " (durch dich)";
|
||||
p.sendMessage(ChatColor.RED + " Status: ⚠ Gekündigt" + reason);
|
||||
p.sendMessage(ChatColor.AQUA + " Aktiv bis: " + ChatColor.YELLOW + abo.periodEnd);
|
||||
p.sendMessage(ChatColor.GRAY + " Danach kein Fly-Abo mehr aktiv.");
|
||||
} else {
|
||||
p.sendMessage(ChatColor.AQUA + " Status: " + ChatColor.GREEN + "✔ Aktiv");
|
||||
p.sendMessage(ChatColor.AQUA + " Aktiv bis: " + ChatColor.YELLOW + abo.periodEnd);
|
||||
p.sendMessage(ChatColor.AQUA + " Nächste Abbuchung: " + ChatColor.WHITE + abo.nextBillingDate);
|
||||
p.sendMessage(ChatColor.GRAY + " Kündigen zum Monatsende: " + ChatColor.WHITE + "/flyabocancel");
|
||||
}
|
||||
int usedSec = flyAboManager.getUsedTodaySec(p.getName());
|
||||
int maxSec = flyAboMaxDailySec;
|
||||
int remSec = Math.max(0, maxSec - usedSec);
|
||||
@@ -1353,17 +1360,24 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
/** /flyabocancel – Abo zum Ablauf kündigen */
|
||||
/** /flyabocancel – Abo zum Ende des laufenden Monats 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;
|
||||
|
||||
// Letzten Tag des laufenden Monats berechnen
|
||||
java.time.LocalDate today = java.time.LocalDate.now();
|
||||
java.time.LocalDate monthEnd = today.withDayOfMonth(today.lengthOfMonth());
|
||||
String endDateStr = monthEnd.format(java.time.format.DateTimeFormatter.ofPattern("dd.MM.yyyy"));
|
||||
|
||||
// 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.GRAY + " Das Abo läuft noch bis zum " + ChatColor.WHITE + endDateStr + ChatColor.GRAY + " weiter.");
|
||||
p.sendMessage(ChatColor.GRAY + " Ab dem 1. des Folgemonats wird es nicht mehr verlängert.");
|
||||
p.sendMessage(ChatColor.WHITE + " Bestätigen: " + ChatColor.RED + "/flyabocancel confirm");
|
||||
return true;
|
||||
}
|
||||
@@ -1372,11 +1386,11 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
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.sendMessage(ChatColor.YELLOW + "✈ Dein Fly-Abo wurde zum " + ChatColor.WHITE + endDateStr + ChatColor.YELLOW + " gekündigt.");
|
||||
p.sendMessage(ChatColor.GRAY + " Bis dahin kannst du es wie gewohnt nutzen.");
|
||||
p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1.0F, 0.8F);
|
||||
} else {
|
||||
p.sendMessage(ChatColor.RED + "✗ Kein aktives Fly-Abo gefunden.");
|
||||
p.sendMessage(ChatColor.RED + "✗ Kein aktives Fly-Abo gefunden oder bereits gekündigt.");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1385,7 +1399,7 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
/** /flyabogive <Spieler> <Tage> [Label] – Admin gibt Abo manuell */
|
||||
/** /flyabogive <Spieler> <Monatspreis> [Label] – Admin gibt Abo manuell */
|
||||
private class FlyAboGiveCommand implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command,
|
||||
@@ -1395,33 +1409,37 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
return true;
|
||||
}
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage(ChatColor.YELLOW + "Verwendung: /flyabogive <Spieler> <Tage> [Label]");
|
||||
sender.sendMessage(ChatColor.YELLOW + "Verwendung: /flyabogive <Spieler> <Monatspreis> [Label]");
|
||||
sender.sendMessage(ChatColor.GRAY + " Preis = 0 → kostenloses Abo (kein Vault-Abzug)");
|
||||
return true;
|
||||
}
|
||||
String targetName = args[0];
|
||||
int days;
|
||||
try { days = Integer.parseInt(args[1]); }
|
||||
int monthlyPrice;
|
||||
try { monthlyPrice = Integer.parseInt(args[1]); }
|
||||
catch (NumberFormatException e) {
|
||||
sender.sendMessage(ChatColor.RED + "✗ Tage muss eine Zahl sein.");
|
||||
sender.sendMessage(ChatColor.RED + "✗ Monatspreis muss eine Zahl sein.");
|
||||
return true;
|
||||
}
|
||||
String aboLabel = args.length >= 3
|
||||
? String.join(" ", Arrays.copyOfRange(args, 2, args.length))
|
||||
: "Fly-Abo " + days + " Tage";
|
||||
: "Fly-Abo";
|
||||
Player target = Bukkit.getPlayer(targetName);
|
||||
new BukkitRunnable() {
|
||||
@Override public void run() {
|
||||
flyAboManager.grantAboByName(targetName, days, aboLabel);
|
||||
flyAboManager.grantAboByName(targetName, aboLabel, monthlyPrice);
|
||||
Bukkit.getScheduler().runTask(IngameShopSpigot.this, () -> {
|
||||
sender.sendMessage(ChatColor.GREEN + "✔ Fly-Abo (" + days + " Tage) an "
|
||||
+ ChatColor.YELLOW + targetName + ChatColor.GREEN + " vergeben.");
|
||||
sender.sendMessage(ChatColor.GREEN + "✔ Fly-Abo an "
|
||||
+ ChatColor.YELLOW + targetName + ChatColor.GREEN + " vergeben."
|
||||
+ ChatColor.GRAY + " (Monatspreis: " + monthlyPrice + " " + currency + ")");
|
||||
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 + " Paket: " + ChatColor.WHITE + aboLabel);
|
||||
target.sendMessage(ChatColor.GRAY + " Monatspreis: " + ChatColor.WHITE + monthlyPrice + " " + currency);
|
||||
target.sendMessage(ChatColor.GRAY + " Tägl. Limit: " + ChatColor.WHITE
|
||||
+ flyManager.formatTime(flyAboMaxDailySec));
|
||||
target.sendMessage(ChatColor.GRAY + " Nächste Abbuchung: " + ChatColor.YELLOW + "1. des Folgemonats");
|
||||
target.sendMessage(ChatColor.GRAY + " Status: /flyabo");
|
||||
target.sendMessage(ChatColor.GOLD + " ════════════════════════════");
|
||||
target.sendMessage("");
|
||||
@@ -2191,16 +2209,27 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
// ── Fly-Abo CRUD ──────────────────────────────────────────────────
|
||||
|
||||
void createAboTable() {
|
||||
// Migration: alte expires_at-Tabelle auf neues Schema upgraden
|
||||
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,"
|
||||
+ " id INT AUTO_INCREMENT PRIMARY KEY,"
|
||||
+ " player_name VARCHAR(64) NOT NULL COLLATE utf8mb4_general_ci,"
|
||||
+ " label VARCHAR(128) NOT NULL,"
|
||||
+ " monthly_price INT NOT NULL DEFAULT 0,"
|
||||
+ " cancelled TINYINT(1) NOT NULL DEFAULT 0,"
|
||||
+ " cancellation_reason VARCHAR(32) DEFAULT NULL,"
|
||||
+ " next_billing_date DATE NOT NULL,"
|
||||
+ " period_end DATE NOT NULL,"
|
||||
+ " granted_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,"
|
||||
+ " UNIQUE KEY uq_player (player_name)"
|
||||
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
|
||||
// Migrations: Spalten ergänzen falls Tabelle schon in alter Form existiert
|
||||
String[] migrations = {
|
||||
"ALTER TABLE wis_fly_abos ADD COLUMN IF NOT EXISTS monthly_price INT NOT NULL DEFAULT 0 AFTER label",
|
||||
"ALTER TABLE wis_fly_abos ADD COLUMN IF NOT EXISTS cancellation_reason VARCHAR(32) DEFAULT NULL AFTER cancelled",
|
||||
"ALTER TABLE wis_fly_abos ADD COLUMN IF NOT EXISTS next_billing_date DATE NOT NULL DEFAULT (CURDATE()) AFTER cancellation_reason",
|
||||
"ALTER TABLE wis_fly_abos ADD COLUMN IF NOT EXISTS period_end DATE NOT NULL DEFAULT (LAST_DAY(CURDATE())) AFTER next_billing_date",
|
||||
};
|
||||
String sqlUsage =
|
||||
"CREATE TABLE IF NOT EXISTS wis_fly_abo_usage ("
|
||||
+ " id INT AUTO_INCREMENT PRIMARY KEY,"
|
||||
@@ -2212,38 +2241,53 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
try (Statement stmt = connection.createStatement()) {
|
||||
stmt.execute(sqlAbo);
|
||||
stmt.execute(sqlUsage);
|
||||
for (String m : migrations) {
|
||||
try { stmt.execute(m); } catch (SQLException ignored) {}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Abo-Tabellen-Erstellung fehlgeschlagen", e);
|
||||
}
|
||||
}
|
||||
|
||||
void saveAbo(String playerName, int days, String label) {
|
||||
/**
|
||||
* Neues Abo aktivieren oder bestehendes verlängern.
|
||||
* next_billing_date = 1. des Folgemonats
|
||||
* period_end = letzter Tag des laufenden Monats (oder des aktuellen Abomonats)
|
||||
*/
|
||||
void saveAbo(String playerName, String label, int monthlyPrice) {
|
||||
String sql =
|
||||
"INSERT INTO wis_fly_abos (player_name, label, cancelled, expires_at)"
|
||||
+ " VALUES (?, ?, 0, DATE_ADD(NOW(), INTERVAL ? DAY))"
|
||||
"INSERT INTO wis_fly_abos"
|
||||
+ " (player_name, label, monthly_price, cancelled, cancellation_reason,"
|
||||
+ " next_billing_date, period_end)"
|
||||
+ " VALUES (?, ?, ?, 0, NULL,"
|
||||
+ " DATE_FORMAT(DATE_ADD(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01'),"
|
||||
+ " LAST_DAY(CURDATE()))"
|
||||
+ " ON DUPLICATE KEY UPDATE"
|
||||
+ " label = ?, cancelled = 0,"
|
||||
+ " expires_at = IF(expires_at > NOW(),"
|
||||
+ " DATE_ADD(expires_at, INTERVAL ? DAY),"
|
||||
+ " DATE_ADD(NOW(), INTERVAL ? DAY)),"
|
||||
+ " label = ?, monthly_price = ?, cancelled = 0, cancellation_reason = NULL,"
|
||||
+ " next_billing_date = DATE_FORMAT(DATE_ADD(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01'),"
|
||||
+ " period_end = LAST_DAY(CURDATE()),"
|
||||
+ " granted_at = NOW()";
|
||||
try (PreparedStatement ps = connection.prepareStatement(sql)) {
|
||||
ps.setString(1, playerName);
|
||||
ps.setString(2, label);
|
||||
ps.setInt(3, days);
|
||||
ps.setInt(3, monthlyPrice);
|
||||
ps.setString(4, label);
|
||||
ps.setInt(5, days);
|
||||
ps.setInt(6, days);
|
||||
ps.setInt(5, monthlyPrice);
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "saveAbo SQL Fehler", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abo durch Spieler kündigen – bleibt bis period_end aktiv.
|
||||
* period_end wird auf LAST_DAY(CURDATE()) gesetzt (Ende des laufenden Monats).
|
||||
*/
|
||||
boolean cancelAbo(String playerName) {
|
||||
String sql =
|
||||
"UPDATE wis_fly_abos SET cancelled = 1"
|
||||
+ " WHERE player_name = ? AND expires_at > NOW() AND cancelled = 0";
|
||||
"UPDATE wis_fly_abos SET cancelled = 1, cancellation_reason = 'user',"
|
||||
+ " period_end = LAST_DAY(CURDATE())"
|
||||
+ " WHERE player_name = ? AND period_end >= CURDATE() AND cancelled = 0";
|
||||
try (PreparedStatement ps = connection.prepareStatement(sql)) {
|
||||
ps.setString(1, playerName);
|
||||
return ps.executeUpdate() == 1;
|
||||
@@ -2253,20 +2297,88 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abo wegen Zahlungsausfall kündigen – sofort zum period_end des laufenden Monats.
|
||||
*/
|
||||
void cancelAboPaymentFailed(String playerName) {
|
||||
String sql =
|
||||
"UPDATE wis_fly_abos SET cancelled = 1, cancellation_reason = 'payment_failed',"
|
||||
+ " period_end = LAST_DAY(CURDATE())"
|
||||
+ " WHERE player_name = ? AND cancelled = 0";
|
||||
try (PreparedStatement ps = connection.prepareStatement(sql)) {
|
||||
ps.setString(1, playerName);
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "cancelAboPaymentFailed SQL Fehler", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Billing verlängern: next_billing_date auf 1. des übernächsten Monats,
|
||||
* period_end auf letzten Tag des neuen Monats setzen.
|
||||
*/
|
||||
void renewAbo(String playerName) {
|
||||
String sql =
|
||||
"UPDATE wis_fly_abos"
|
||||
+ " SET next_billing_date = DATE_FORMAT(DATE_ADD(next_billing_date, INTERVAL 1 MONTH), '%Y-%m-01'),"
|
||||
+ " period_end = LAST_DAY(next_billing_date)"
|
||||
+ " WHERE player_name = ? AND cancelled = 0";
|
||||
try (PreparedStatement ps = connection.prepareStatement(sql)) {
|
||||
ps.setString(1, playerName);
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "renewAbo SQL Fehler", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alle Abos holen die heute fällig sind (next_billing_date = heute, nicht gekündigt).
|
||||
*/
|
||||
List<FlyAboManager.AboEntry> getDueBillings() {
|
||||
List<FlyAboManager.AboEntry> list = new ArrayList<>();
|
||||
String sql =
|
||||
"SELECT player_name, label, monthly_price, cancelled, cancellation_reason,"
|
||||
+ " DATE_FORMAT(next_billing_date, '%d.%m.%Y') AS billing_fmt,"
|
||||
+ " DATE_FORMAT(period_end, '%d.%m.%Y') AS period_fmt"
|
||||
+ " FROM wis_fly_abos"
|
||||
+ " WHERE next_billing_date <= CURDATE() AND cancelled = 0";
|
||||
try (PreparedStatement ps = connection.prepareStatement(sql)) {
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next())
|
||||
list.add(new FlyAboManager.AboEntry(
|
||||
rs.getString("player_name"),
|
||||
rs.getString("label"),
|
||||
rs.getInt("monthly_price"),
|
||||
false,
|
||||
null,
|
||||
rs.getString("billing_fmt"),
|
||||
rs.getString("period_fmt")));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "getDueBillings SQL Fehler", e);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
FlyAboManager.AboEntry getActiveAbo(String playerName) {
|
||||
String sql =
|
||||
"SELECT label, cancelled,"
|
||||
+ " DATE_FORMAT(expires_at, '%d.%m.%Y') AS expires_fmt"
|
||||
"SELECT label, monthly_price, cancelled, cancellation_reason,"
|
||||
+ " DATE_FORMAT(next_billing_date, '%d.%m.%Y') AS billing_fmt,"
|
||||
+ " DATE_FORMAT(period_end, '%d.%m.%Y') AS period_fmt"
|
||||
+ " FROM wis_fly_abos"
|
||||
+ " WHERE player_name = ? AND expires_at > NOW()";
|
||||
+ " WHERE player_name = ? AND period_end >= CURDATE()";
|
||||
try (PreparedStatement ps = connection.prepareStatement(sql)) {
|
||||
ps.setString(1, playerName);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next())
|
||||
return new FlyAboManager.AboEntry(
|
||||
rs.getString("player_name"),
|
||||
rs.getString("label"),
|
||||
rs.getString("expires_fmt"),
|
||||
rs.getInt("cancelled") == 1);
|
||||
rs.getInt("monthly_price"),
|
||||
rs.getInt("cancelled") == 1,
|
||||
rs.getString("cancellation_reason"),
|
||||
rs.getString("billing_fmt"),
|
||||
rs.getString("period_fmt"));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "getActiveAbo SQL Fehler", e);
|
||||
@@ -2275,7 +2387,7 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
}
|
||||
|
||||
void cleanupExpiredAbos(String playerName) {
|
||||
String sql = "DELETE FROM wis_fly_abos WHERE player_name = ? AND expires_at <= NOW()";
|
||||
String sql = "DELETE FROM wis_fly_abos WHERE player_name = ? AND period_end < CURDATE()";
|
||||
try (PreparedStatement ps = connection.prepareStatement(sql)) {
|
||||
ps.setString(1, playerName);
|
||||
ps.executeUpdate();
|
||||
@@ -2957,36 +3069,37 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
db.createAboTable();
|
||||
}
|
||||
|
||||
// ── Abo vergeben (über Order) ──────────────────────────────────
|
||||
// ── Abo aktivieren (über Order) ────────────────────────────────
|
||||
|
||||
void grantAbo(Player player, int days, String label) {
|
||||
grantAboByName(player.getName(), days, label);
|
||||
void grantAbo(Player player, String label, int monthlyPrice) {
|
||||
db.saveAbo(player.getName(), label, monthlyPrice);
|
||||
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
|
||||
player.sendMessage(ChatColor.GRAY + " Paket: " + ChatColor.WHITE + label);
|
||||
player.sendMessage(ChatColor.GRAY + " Monatspreis: " + ChatColor.WHITE + monthlyPrice + " " + currency);
|
||||
player.sendMessage(ChatColor.GRAY + " Tägl. Limit: " + ChatColor.WHITE
|
||||
+ flyManager.formatTime(flyAboMaxDailySec));
|
||||
player.sendMessage(ChatColor.GRAY + " Status: " + ChatColor.WHITE + "/flyabo");
|
||||
player.sendMessage(ChatColor.GRAY + " Nächste Abbuchung: " + ChatColor.YELLOW + "1. des Folgemonats");
|
||||
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);
|
||||
void grantAboByName(String playerName, String label, int monthlyPrice) {
|
||||
db.saveAbo(playerName, label, monthlyPrice);
|
||||
}
|
||||
|
||||
// ── Abo kündigen ───────────────────────────────────────────────
|
||||
// ── Kündigung durch Spieler ────────────────────────────────────
|
||||
|
||||
boolean cancelAbo(String playerName) {
|
||||
return db.cancelAbo(playerName);
|
||||
}
|
||||
|
||||
// ── Abo-Status lesen ──────────────────────────────────────────
|
||||
// ── Status lesen ──────────────────────────────────────────────
|
||||
|
||||
AboEntry getActiveAbo(String playerName) {
|
||||
return db.getActiveAbo(playerName);
|
||||
@@ -2996,21 +3109,37 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
return db.getUsedTodaySec(playerName);
|
||||
}
|
||||
|
||||
// ── Beim Join: Abo prüfen ─────────────────────────────────────
|
||||
// ── Beim Join: Abo prüfen + ggf. Zahlung-fehlgeschlagen-Meldung ──
|
||||
|
||||
void onPlayerJoin(Player player) {
|
||||
AboEntry abo = db.getActiveAbo(player.getName());
|
||||
if (abo == null) return;
|
||||
|
||||
// Abgelaufenes Abo bereinigen
|
||||
// Abgelaufene Einträge 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;
|
||||
|
||||
if (abo == null) return;
|
||||
|
||||
// Zahlungsausfall-Hinweis
|
||||
final AboEntry finalAbo = abo;
|
||||
if ("payment_failed".equals(abo.cancellationReason)) {
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
player.sendMessage("");
|
||||
player.sendMessage(ChatColor.RED + "✈ ════════════════════════════");
|
||||
player.sendMessage(ChatColor.RED + " Fly-Abo: Zahlung fehlgeschlagen!");
|
||||
player.sendMessage(ChatColor.GRAY + " Dein Abo konnte nicht verlängert werden,");
|
||||
player.sendMessage(ChatColor.GRAY + " da dein Kontostand nicht ausreichte.");
|
||||
player.sendMessage(ChatColor.GRAY + " Es läuft am " + ChatColor.YELLOW + finalAbo.periodEnd
|
||||
+ ChatColor.GRAY + " aus.");
|
||||
player.sendMessage(ChatColor.WHITE + " Neues Abo: " + ChatColor.AQUA + "viper-network.de");
|
||||
player.sendMessage(ChatColor.RED + " ════════════════════════════");
|
||||
player.sendMessage("");
|
||||
player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1.0F, 0.5F);
|
||||
});
|
||||
}
|
||||
|
||||
if (abo.cancelled) return; // Gekündigtes Abo: kein Fly mehr gewähren wenn period_end heute
|
||||
|
||||
int usedToday = db.getUsedTodaySec(player.getName());
|
||||
int remaining = Math.max(0, flyAboMaxDailySec - usedToday);
|
||||
if (remaining <= 0) {
|
||||
@@ -3023,27 +3152,23 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
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);
|
||||
});
|
||||
final int grantSec = remaining;
|
||||
Bukkit.getScheduler().runTask(plugin, () ->
|
||||
flyManager.grantFlyWithAboTracking(player, grantSec, finalAbo.label,
|
||||
player.getName(), flyAboMaxDailySec));
|
||||
}
|
||||
|
||||
// ── Täglicher Reset-Check ─────────────────────────────────────
|
||||
// ── Monatlicher Billing-Check (läuft täglich) ─────────────────
|
||||
|
||||
void startDailyResetChecker() {
|
||||
// Alle 5 Minuten prüfen ob Tagesdatum gewechselt hat → online Spieler neu versorgen
|
||||
// Alle 5 Minuten: online Spieler mit Abo ohne aktiven Fly 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
|
||||
if (abo == null || abo.cancelled) return;
|
||||
int usedToday = db.getUsedTodaySec(p.getName());
|
||||
int remaining = Math.max(0, flyAboMaxDailySec - usedToday);
|
||||
if (remaining > 0
|
||||
@@ -3056,18 +3181,119 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
}.runTaskAsynchronously(plugin);
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(plugin, 20L * 300, 20L * 300); // alle 5 Minuten
|
||||
}.runTaskTimer(plugin, 20L * 300, 20L * 300);
|
||||
|
||||
// Täglich um ~00:05 Uhr: Billing-Check für den 1. des Monats
|
||||
// Berechne Ticks bis 00:05 Uhr (approximiert: sofort starten, dann täglich wiederholen)
|
||||
new BukkitRunnable() {
|
||||
@Override public void run() {
|
||||
java.time.LocalDate today = java.time.LocalDate.now();
|
||||
if (today.getDayOfMonth() != 1) return; // Nur am 1. des Monats ausführen
|
||||
|
||||
new BukkitRunnable() {
|
||||
@Override public void run() {
|
||||
List<FlyAboManager.AboEntry> due = db.getDueBillings();
|
||||
if (due.isEmpty()) return;
|
||||
|
||||
getLogger().info("[FlyAbo] Billing-Tag: " + due.size() + " Abo(s) zu verlängern.");
|
||||
|
||||
for (FlyAboManager.AboEntry entry : due) {
|
||||
String playerName = entry.playerName;
|
||||
int price = entry.monthlyPrice;
|
||||
|
||||
// Vault-Abbuchung auf Main-Thread
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
@SuppressWarnings("deprecation")
|
||||
org.bukkit.OfflinePlayer op = Bukkit.getOfflinePlayer(playerName);
|
||||
if (op == null || !op.hasPlayedBefore()) {
|
||||
// Spieler unbekannt → überspringen (nächste Nacht nochmal)
|
||||
return;
|
||||
}
|
||||
boolean hasBalance = (price == 0) || econ.getBalance(op) >= price;
|
||||
if (hasBalance) {
|
||||
if (price > 0) {
|
||||
econ.withdrawPlayer(op, price);
|
||||
if (!incomeReceiver.isEmpty()) {
|
||||
@SuppressWarnings("deprecation")
|
||||
org.bukkit.OfflinePlayer recv =
|
||||
Bukkit.getOfflinePlayer(incomeReceiver);
|
||||
if (recv != null && recv.hasPlayedBefore())
|
||||
econ.depositPlayer(recv, price);
|
||||
}
|
||||
}
|
||||
// DB-Verlängerung async
|
||||
new BukkitRunnable() {
|
||||
@Override public void run() {
|
||||
db.renewAbo(playerName);
|
||||
getLogger().info("[FlyAbo] ✔ Abo verlängert: "
|
||||
+ playerName + " (" + price + " " + currency + ")");
|
||||
}
|
||||
}.runTaskAsynchronously(plugin);
|
||||
|
||||
// Online-Benachrichtigung
|
||||
Player online = Bukkit.getPlayer(playerName);
|
||||
if (online != null && online.isOnline()) {
|
||||
online.sendMessage("");
|
||||
online.sendMessage(ChatColor.GOLD + "✈ ════════════════════════════");
|
||||
online.sendMessage(ChatColor.GREEN + " Fly-Abo verlängert!");
|
||||
online.sendMessage(ChatColor.GRAY + " Abgebucht: "
|
||||
+ ChatColor.WHITE + price + " " + currency);
|
||||
online.sendMessage(ChatColor.GRAY + " Läuft bis: "
|
||||
+ ChatColor.YELLOW + "Ende des Monats");
|
||||
online.sendMessage(ChatColor.GOLD + " ════════════════════════════");
|
||||
online.sendMessage("");
|
||||
online.playSound(online.getLocation(),
|
||||
Sound.ENTITY_PLAYER_LEVELUP, 1.0F, 1.0F);
|
||||
}
|
||||
} else {
|
||||
// Zahlung fehlgeschlagen → Abo kündigen
|
||||
new BukkitRunnable() {
|
||||
@Override public void run() {
|
||||
db.cancelAboPaymentFailed(playerName);
|
||||
getLogger().warning("[FlyAbo] ✘ Zahlung fehlgeschlagen: "
|
||||
+ playerName + " (benötigt: " + price + " " + currency + ")");
|
||||
}
|
||||
}.runTaskAsynchronously(plugin);
|
||||
|
||||
Player online = Bukkit.getPlayer(playerName);
|
||||
if (online != null && online.isOnline()) {
|
||||
online.sendMessage("");
|
||||
online.sendMessage(ChatColor.RED + "✈ ════════════════════════════");
|
||||
online.sendMessage(ChatColor.RED + " Fly-Abo: Zahlung fehlgeschlagen!");
|
||||
online.sendMessage(ChatColor.GRAY + " Benötigt: "
|
||||
+ ChatColor.WHITE + price + " " + currency);
|
||||
online.sendMessage(ChatColor.GRAY + " Dein Abo läuft Ende des Monats aus.");
|
||||
online.sendMessage(ChatColor.WHITE + " Verlängern: " + ChatColor.AQUA + "viper-network.de");
|
||||
online.sendMessage(ChatColor.RED + " ════════════════════════════");
|
||||
online.sendMessage("");
|
||||
online.playSound(online.getLocation(),
|
||||
Sound.BLOCK_NOTE_BLOCK_BASS, 1.0F, 0.5F);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}.runTaskAsynchronously(plugin);
|
||||
}
|
||||
}.runTaskTimer(plugin, 20L * 60 * 5, 20L * 60 * 60 * 24); // Start nach 5 min, dann täglich
|
||||
}
|
||||
|
||||
// ── Data class ────────────────────────────────────────────────
|
||||
|
||||
static class AboEntry {
|
||||
final String label, expiresAt;
|
||||
final String playerName, label, cancellationReason, nextBillingDate, periodEnd;
|
||||
final int monthlyPrice;
|
||||
final boolean cancelled;
|
||||
AboEntry(String label, String expiresAt, boolean cancelled) {
|
||||
this.label = label;
|
||||
this.expiresAt = expiresAt;
|
||||
this.cancelled = cancelled;
|
||||
AboEntry(String playerName, String label, int monthlyPrice,
|
||||
boolean cancelled, String cancellationReason,
|
||||
String nextBillingDate, String periodEnd) {
|
||||
this.playerName = playerName;
|
||||
this.label = label;
|
||||
this.monthlyPrice = monthlyPrice;
|
||||
this.cancelled = cancelled;
|
||||
this.cancellationReason = cancellationReason;
|
||||
this.nextBillingDate = nextBillingDate;
|
||||
this.periodEnd = periodEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ sell:
|
||||
# Hinweis: Den Basis-Ankaufspreis konfigurierst du im WP-Admin
|
||||
# unter Items → <Item bearbeiten> → "Ankauf aktivieren"
|
||||
price-offset: -10.0
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# Fly-Abo Einstellungen
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user