Upload folder via GUI - src

This commit is contained in:
Git Manager GUI
2026-05-05 21:08:18 +02:00
parent 1409fe5e21
commit d71f15086d
2 changed files with 388 additions and 2 deletions

View File

@@ -97,6 +97,9 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
// URL für Geschenk-Endpoint und Rückerstattungs-Endpoint
private String wpUrlGiftAccept;
private String wpUrlGiftDecline;
private String wpUrlItemAboStatus;
private String wpUrlItemAboCancel;
private String wpUrlTriggerAboDelivery;
private FlyManager flyManager;
private FlyCodeManager flyCodeManager;
@@ -142,6 +145,9 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
wpUrlSell = wpBase + "/sell_item";
wpUrlGiftAccept = wpBase + "/gift_accept";
wpUrlGiftDecline = wpBase + "/gift_decline";
wpUrlItemAboStatus = wpBase + "/item_abo_status";
wpUrlItemAboCancel = wpBase + "/item_abo_cancel";
wpUrlTriggerAboDelivery = wpBase + "/trigger_abo_delivery";
targetServer = getConfig().getString("server-name", "survival").toLowerCase();
currency = getConfig().getString("currency-name", "Coins");
@@ -208,6 +214,7 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
getCommand("plotabogive").setExecutor(new PlotAboGiveCommand());
getCommand("plotslotsgive").setExecutor(new PlotSlotsGiveCommand());
getCommand("abo").setExecutor(new AboOverviewCommand());
getCommand("itemabocancel").setExecutor(new ItemAboCancelCommand());
getCommand("sell").setExecutor(new SellCommand());
getCommand("wpis").setExecutor(new ReloadCommand());
@@ -340,6 +347,56 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
&& !order.get("gift_recipient").isJsonNull())
? order.get("gift_recipient").getAsString().trim() : "";
// ── Item-Abo Tageslieferung: automatisch ausliefern (kein Confirm-GUI) ──
boolean isAboDelivery = false;
try {
JsonObject respObj = JsonParser.parseString(jsonResponse).getAsJsonObject();
isAboDelivery = respObj.has("abo_delivery")
&& respObj.get("abo_delivery").getAsBoolean();
} catch (Exception ignored) {}
if (isAboDelivery) {
// Direkt ausliefern ohne GUI
final int deliveryId = id;
final String deliveryTitle = itemTitle;
final String deliveryJson = jsonResponse;
synchronized (activeOrderIds) {
if (activeOrderIds.containsKey(p.getUniqueId())) {
if (debug) getLogger().info(
"Abo-Delivery #" + id + " für " + p.getName()
+ " verschoben (aktive Order).");
return;
}
OrderData delivData = new OrderData(
deliveryId, "item_abo_delivery", deliveryTitle,
0.0, 1, deliveryJson, "");
orderCache.put(deliveryId, delivData);
activeOrderIds.put(p.getUniqueId(), deliveryId);
}
// execute_order sofort markieren, dann Items geben
new BukkitRunnable() {
@Override public void run() {
try {
HttpURLConnection conn = openAuthConnection(wpUrlExecute, "POST");
writeJson(conn, "{\"id\":\"" + deliveryId + "\"}");
conn.getResponseCode();
} catch (Exception ex) {
if (debug) getLogger().log(Level.WARNING,
"[AboDelivery] Execute Fehler", ex);
}
// Direkt auf Main-Thread Items geben
Bukkit.getScheduler().runTask(IngameShopSpigot.this, () -> {
OrderData dd = orderCache.get(deliveryId);
if (dd != null) {
activeOrderIds.remove(p.getUniqueId());
deliverAboItems(p, deliveryId, deliveryTitle, deliveryJson);
}
});
}
}.runTaskAsynchronously(IngameShopSpigot.this);
return; // Kein Confirm-GUI öffnen
}
// Spieler A bestätigt IMMER zuerst selbst (verhindert Missbrauch).
// giftRecipient wird in OrderData gespeichert und erst nach
// Bestätigung + Geld-Abbuchung an Spieler B weitergeleitet.
@@ -597,9 +654,12 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
// Polling für diesen Spieler sperren bis der Join-Fetch durch ist
joinFetchCooldown.put(p.getUniqueId(), System.currentTimeMillis() + 10_000L);
// Offline-Bestellungen abholen
// Offline-Bestellungen abholen + Item-Abo Lieferung triggern
new BukkitRunnable() {
@Override public void run() {
// Zuerst Item-Abo prüfen → WP legt ggf. pending-Order an
triggerItemAboDelivery(p);
// Dann abholen (findet die gerade angelegte Order)
fetchPendingOrders(p, wpUrlPendingOffline);
// Sperre aufheben sobald Join-Fetch fertig
joinFetchCooldown.remove(p.getUniqueId());
@@ -817,6 +877,27 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
ItemStack test = parseItem(id);
if (test != null) return test.getType();
}
// Wenn nur Commands vorhanden: Icon je nach Typ wählen
if (root.isJsonObject()) {
JsonObject obj2 = root.getAsJsonObject();
if (obj2.has("commands")) {
JsonArray cmds = obj2.getAsJsonArray("commands");
if (cmds.size() > 0) {
String cmdType = cmds.get(0).getAsJsonObject().has("type")
? cmds.get(0).getAsJsonObject().get("type").getAsString() : "generic";
switch (cmdType) {
case "fly": return Material.FEATHER;
case "rank": return Material.GOLDEN_HELMET;
case "fly_abo": return Material.FEATHER;
case "plot_slots": return Material.OAK_SIGN;
case "plot_abo": return Material.OAK_SIGN;
case "custom_cmd": return Material.COMMAND_BLOCK;
case "item_abo": return Material.CHEST;
default: return Material.FEATHER;
}
}
}
}
} catch (Exception e) {
if (debug) getLogger().log(Level.WARNING, "getFirstItemMaterial Fehler", e);
}
@@ -1300,6 +1381,77 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
plotSlotManager.grantAbo(player, slots, label, monthlyPrice);
anyCode = true;
} else if ("custom_cmd".equals(type)) {
// Custom Command Item beliebiger Konsolenbefehl mit Platzhaltern
String rawCommand = cmdObj.has("command")
? cmdObj.get("command").getAsString() : "";
String label = cmdObj.has("label")
? cmdObj.get("label").getAsString() : "Custom Item";
if (!rawCommand.isEmpty()) {
// Mehrere Commands per Zeilenumbruch möglich
String[] lines = rawCommand.split("\\n");
for (String line : lines) {
String trimmed = line.trim();
if (trimmed.isEmpty()) continue;
// Führenden / entfernen falls vorhanden
if (trimmed.startsWith("/")) trimmed = trimmed.substring(1);
String finalCmd = trimmed
.replace("{player}", player.getName())
.replace("{amount}", "1")
.replace("{server}", targetServer);
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), finalCmd);
if (debug) getLogger().info("[CustomCmd] Ausgeführt: " + finalCmd);
}
player.sendMessage("");
player.sendMessage(ChatColor.GOLD + "⚙ ════════════════════════════");
player.sendMessage(ChatColor.YELLOW + " " + label + " erhalten!");
player.sendMessage(ChatColor.GOLD + " ════════════════════════════");
player.sendMessage("");
player.playSound(player.getLocation(),
Sound.ENTITY_PLAYER_LEVELUP, 1.0F, 1.0F);
} else {
getLogger().warning("[CustomCmd] Kein Command konfiguriert für: " + label);
player.sendMessage(ChatColor.RED + "❌ Kein Command konfiguriert Admin kontaktieren.");
}
anyCode = true;
} else if ("item_abo".equals(type)) {
// Item-Abo: Abo wurde auf WP-Seite registriert,
// erste Tageslieferung sofort direkt geben (wie fly_abo)
String aboItemId = cmdObj.has("item_id")
? cmdObj.get("item_id").getAsString() : "?";
int dailyQty = cmdObj.has("daily_qty")
? cmdObj.get("daily_qty").getAsInt() : 1;
int durationDays = cmdObj.has("duration_days")
? cmdObj.get("duration_days").getAsInt() : 30;
String label = cmdObj.has("label")
? cmdObj.get("label").getAsString() : "Item-Abo";
// Items direkt ins Inventar geben
Material mat = Material.matchMaterial(aboItemId);
if (mat != null && mat != Material.AIR) {
ItemStack stack = new ItemStack(mat, dailyQty);
giveItems(player, stack, dailyQty);
player.sendMessage("");
player.sendMessage(ChatColor.GREEN + "📦 ════════════════════════════");
player.sendMessage(ChatColor.YELLOW + " " + label + " aktiviert!");
player.sendMessage(ChatColor.GRAY + " Item: " + ChatColor.WHITE + aboItemId);
player.sendMessage(ChatColor.GRAY + " Täglich: " + ChatColor.AQUA + dailyQty + "x");
player.sendMessage(ChatColor.GRAY + " Läuft: " + ChatColor.WHITE + durationDays + " Tage");
player.sendMessage(ChatColor.GREEN + " ✔ Erste Lieferung soeben erhalten!");
player.sendMessage(ChatColor.GRAY + " Du erhältst die Items täglich beim Login.");
player.sendMessage(ChatColor.GREEN + " ════════════════════════════");
player.sendMessage("");
player.playSound(player.getLocation(),
Sound.ENTITY_PLAYER_LEVELUP, 1.0F, 1.0F);
} else {
getLogger().warning("[ItemAbo] Unbekanntes Item: " + aboItemId);
player.sendMessage(ChatColor.RED + "⚠ Item-ID unbekannt: " + aboItemId
+ " Admin kontaktieren.");
}
anyCode = true;
} else if ("generic".equals(type) && cmdObj.has("cmd")) {
String cmd = cmdObj.get("cmd").getAsString()
.replace("{player}", player.getName());
@@ -1343,6 +1495,49 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
}
}
/**
* Liefert Item-Abo Tagesitems direkt aus ohne Confirm-GUI und ohne Vault-Abbuchung.
* Wird nur für Lieferungen mit abo_delivery=true aufgerufen.
*/
private void deliverAboItems(Player player, int orderId, String itemTitle, String jsonPayload) {
try {
JsonElement root = gson.fromJson(jsonPayload, JsonElement.class);
JsonArray items = null;
if (root != null && root.isJsonObject()) {
items = root.getAsJsonObject().getAsJsonArray("items");
}
int delivered = 0;
if (items != null) {
for (JsonElement e : items) {
JsonObject itemObj = e.getAsJsonObject();
String itemId = itemObj.get("id").getAsString();
int amount = itemObj.get("amount").getAsInt();
ItemStack stack = parseItem(itemId);
if (stack != null) {
giveItems(player, stack, amount);
delivered++;
} else {
getLogger().warning("[AboDelivery] Unbekanntes Item: " + itemId);
}
}
}
if (delivered > 0) {
player.sendMessage("");
player.sendMessage(ChatColor.GREEN + "📦 Abo-Lieferung erhalten!");
player.sendMessage(ChatColor.GRAY + " " + itemTitle);
player.sendMessage("");
player.playSound(player.getLocation(), Sound.ENTITY_ITEM_PICKUP, 0.8F, 1.0F);
}
orderCache.remove(orderId);
markOrderCompleted(orderId);
if (debug) getLogger().info("[AboDelivery] " + player.getName()
+ " " + itemTitle + " (Order #" + orderId + ")");
} catch (Exception ex) {
getLogger().log(Level.WARNING, "[AboDelivery] Fehler bei Lieferung", ex);
orderCache.remove(orderId);
}
}
private void markOrderCompleted(int orderId) {
new BukkitRunnable() {
@Override public void run() {
@@ -1703,6 +1898,7 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
int plotExtra = plotSlotManager.getExtraSlots(p.getName());
int plotAboSlots= plotSlotManager.getAboSlots(p.getName());
int plotTotal = plotBase + plotExtra + plotAboSlots;
List<JsonObject> itemAbos = fetchItemAbos(p.getName());
Bukkit.getScheduler().runTask(IngameShopSpigot.this, () -> {
String line = ChatColor.GOLD + "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━";
@@ -1772,6 +1968,33 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
}
}
// ── Item-Abos (PetFutter, etc.) ───────────────────────────
for (int i = 0; i < itemAbos.size(); i++) {
JsonObject ia = itemAbos.get(i);
hasAny = true;
int aboId = ia.has("id") ? ia.get("id").getAsInt() : -1;
String iaLabel = ia.has("label") ? ia.get("label").getAsString() : "Item-Abo";
String iaItem = ia.has("item_id") ? ia.get("item_id").getAsString() : "?";
int iaQty = ia.has("daily_qty") ? ia.get("daily_qty").getAsInt() : 1;
String iaExpires = ia.has("expires_at")? ia.get("expires_at").getAsString(): "?";
boolean iaCancelled = ia.has("cancelled") && ia.get("cancelled").getAsBoolean();
int nr = i + 1;
p.sendMessage("");
p.sendMessage(ChatColor.AQUA + "" + ChatColor.BOLD + " 📦 Item-Abo #" + nr + ": " + iaLabel);
p.sendMessage(ChatColor.GRAY + " ┌ Item: " + ChatColor.WHITE + iaItem);
p.sendMessage(ChatColor.GRAY + " ├ Täglich: " + ChatColor.AQUA + iaQty + "x");
if (iaCancelled) {
p.sendMessage(ChatColor.GRAY + " ├ Status: " + ChatColor.RED + "⚠ Gekündigt");
p.sendMessage(ChatColor.GRAY + " └ Endet am: " + ChatColor.YELLOW + iaExpires);
} else {
p.sendMessage(ChatColor.GRAY + " ├ Status: " + ChatColor.GREEN + "✔ Aktiv");
p.sendMessage(ChatColor.GRAY + " ├ Läuft bis: " + ChatColor.WHITE + iaExpires);
p.sendMessage(ChatColor.GRAY + " └ Kündigen: " + ChatColor.WHITE + "/itemabocancel " + nr);
}
}
// ── Plot-Slots Übersicht (immer anzeigen) ─────────────
p.sendMessage("");
p.sendMessage(ChatColor.AQUA + "" + ChatColor.BOLD + " 📦 Plot-Slots");
@@ -2142,6 +2365,159 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
}
}
// ===========================================================
// ITEM-ABO HELPER + COMMAND
// ===========================================================
/**
* Holt alle aktiven Item-Abos des Spielers vom WordPress-Backend.
* Muss auf einem Async-Thread aufgerufen werden.
*/
private List<JsonObject> fetchItemAbos(String playerName) {
List<JsonObject> list = new ArrayList<>();
try {
HttpURLConnection conn = openAuthConnection(
wpUrlItemAboStatus + "?player=" + playerName, "GET");
if (conn.getResponseCode() == 200) {
JsonObject resp = JsonParser.parseString(readResponse(conn)).getAsJsonObject();
JsonArray abos = resp.has("abos") ? resp.getAsJsonArray("abos") : new JsonArray();
for (JsonElement el : abos)
list.add(el.getAsJsonObject());
}
} catch (Exception e) {
if (debug) getLogger().log(Level.WARNING, "[ItemAbo] fetchItemAbos Fehler", e);
}
return list;
}
/**
* Prüft beim Login ob heute noch keine Item-Abo-Lieferung erfolgt ist.
* Falls nicht, legt WP eine pending-Order an, die dann vom normalen
* fetchPendingOrders (wpUrlPendingOffline) direkt im selben Join-Runnable abgeholt wird.
* Muss auf einem Async-Thread aufgerufen werden VOR fetchPendingOrders.
*/
private void triggerItemAboDelivery(Player player) {
try {
HttpURLConnection conn = openAuthConnection(wpUrlTriggerAboDelivery, "POST");
writeJson(conn, "{\"player\":\"" + player.getName()
+ "\",\"server\":\"" + targetServer + "\"}");
int code = conn.getResponseCode();
if (code == 200 && debug) {
String body = "";
try { body = readResponse(conn); } catch (Exception ignored) {}
JsonObject resp = JsonParser.parseString(body).getAsJsonObject();
int delivered = resp.has("delivered") ? resp.get("delivered").getAsInt() : 0;
if (delivered > 0)
getLogger().info("[ItemAbo] " + delivered
+ " Lieferung(en) für " + player.getName() + " vorbereitet.");
}
} catch (Exception e) {
if (debug) getLogger().log(Level.WARNING, "[ItemAbo] triggerDelivery Fehler", e);
}
}
/** /itemabocancel <Nr> [confirm] kündigt genau das Item-Abo mit der angezeigten Nummer */
private class ItemAboCancelCommand 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;
// Verwendung ohne Argumente → Übersicht zeigen
if (args.length == 0) {
p.sendMessage(ChatColor.YELLOW + "Verwendung: " + ChatColor.WHITE + "/itemabocancel <Nr>");
p.sendMessage(ChatColor.GRAY + " Die Nummer siehst du in " + ChatColor.WHITE + "/abo");
p.sendMessage(ChatColor.GRAY + " Beispiel: " + ChatColor.WHITE + "/itemabocancel 1");
return true;
}
// Nummer parsen
int nr;
try {
nr = Integer.parseInt(args[0]);
if (nr <= 0) throw new NumberFormatException();
} catch (NumberFormatException e) {
p.sendMessage(ChatColor.RED + "✗ Bitte eine gültige Abo-Nummer angeben (z.B. /itemabocancel 1)");
return true;
}
// Bestätigung erforderlich: /itemabocancel <Nr> confirm
if (args.length < 2 || !args[1].equalsIgnoreCase("confirm")) {
p.sendMessage(ChatColor.YELLOW + "⚠ Item-Abo #" + nr + " wirklich kündigen?");
p.sendMessage(ChatColor.GRAY + " Das Abo läuft bis zum Laufzeitende weiter,");
p.sendMessage(ChatColor.GRAY + " du erhältst weiterhin täglich deine Items bis dahin.");
p.sendMessage(ChatColor.WHITE + " Bestätigen: "
+ ChatColor.RED + "/itemabocancel " + nr + " confirm");
return true;
}
final int selectedNr = nr;
// Abos laden, dann die richtige ID heraussuchen
new BukkitRunnable() {
@Override public void run() {
List<JsonObject> abos = fetchItemAbos(p.getName());
if (abos.isEmpty()) {
Bukkit.getScheduler().runTask(IngameShopSpigot.this, () ->
p.sendMessage(ChatColor.RED + "✗ Du hast keine aktiven Item-Abos."));
return;
}
if (selectedNr > abos.size()) {
Bukkit.getScheduler().runTask(IngameShopSpigot.this, () ->
p.sendMessage(ChatColor.RED + "✗ Abo #" + selectedNr
+ " existiert nicht. Du hast " + abos.size()
+ " Item-Abo(s). Sieh dir /abo an."));
return;
}
JsonObject target = abos.get(selectedNr - 1);
if (!target.has("id") || target.get("id").getAsInt() <= 0) {
Bukkit.getScheduler().runTask(IngameShopSpigot.this, () ->
p.sendMessage(ChatColor.RED + "✗ Fehler: Abo-ID nicht gefunden. Bitte Admin kontaktieren."));
return;
}
int aboId = target.get("id").getAsInt();
String aboLabel = target.has("label") ? target.get("label").getAsString() : "Abo #" + selectedNr;
// Kündigung an WP senden
try {
HttpURLConnection conn = openAuthConnection(wpUrlItemAboCancel, "POST");
writeJson(conn, "{\"player\":\"" + p.getName()
+ "\",\"abo_id\":" + aboId + "}");
int code = conn.getResponseCode();
String body = "";
try { body = readResponse(conn); } catch (Exception ignored) {}
final String finalBody = body;
final String finalLabel = aboLabel;
Bukkit.getScheduler().runTask(IngameShopSpigot.this, () -> {
if (code == 200) {
p.sendMessage(ChatColor.YELLOW + "📦 Item-Abo »" + finalLabel + "« wurde gekündigt.");
p.sendMessage(ChatColor.GRAY + " Du erhältst deine Items bis zum Laufzeitende weiterhin täglich.");
p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1.0F, 0.8F);
} else if (code == 403) {
p.sendMessage(ChatColor.RED + "✗ Dieses Abo gehört dir nicht.");
} else if (code == 404) {
p.sendMessage(ChatColor.RED + "✗ Abo nicht gefunden oder bereits gekündigt.");
} else {
p.sendMessage(ChatColor.RED + "✗ Fehler beim Kündigen (HTTP " + code + ").");
if (debug) getLogger().warning("[ItemAbo] Cancel HTTP " + code + " " + finalBody);
}
});
} catch (Exception e) {
getLogger().log(Level.WARNING, "[ItemAbo] Cancel Fehler", e);
Bukkit.getScheduler().runTask(IngameShopSpigot.this, () ->
p.sendMessage(ChatColor.RED + "✗ Verbindungsfehler zum Shop-Server."));
}
}
}.runTaskAsynchronously(IngameShopSpigot.this);
return true;
}
}
// ===========================================================
// HTTP HILFSMETHODEN
// ===========================================================
@@ -3600,6 +3976,9 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
wpUrlSell = wpBase + "/sell_item";
wpUrlGiftAccept = wpBase + "/gift_accept";
wpUrlGiftDecline = wpBase + "/gift_decline";
wpUrlItemAboStatus = wpBase + "/item_abo_status";
wpUrlItemAboCancel = wpBase + "/item_abo_cancel";
wpUrlTriggerAboDelivery = wpBase + "/trigger_abo_delivery";
targetServer = getConfig().getString("server-name", "survival").toLowerCase();
currency = getConfig().getString("currency-name", "Coins");

View File

@@ -79,6 +79,10 @@ commands:
description: Gibt einem Spieler permanente Plot-Slots (Admin)
usage: /plotslotsgive <Spieler> <Slots> [Label]
permission: ingameshop.plotslotsgive
itemabocancel:
description: Kündigt ein bestimmtes Item-Abo anhand der Nummer aus /abo
usage: /itemabocancel <Nr> [confirm]
permission: ingameshop.itemabocancel
permissions:
ingameshop.orders:
@@ -134,4 +138,7 @@ permissions:
default: op
ingameshop.plotslotsgive:
description: Kann Spielern permanente Plot-Slots vergeben (Admin)
default: op
default: op
ingameshop.itemabocancel:
description: Kann eigenes Item-Abo kündigen
default: true