diff --git a/IngameShopSpigot/src/main/java/de/mviper/spigot/IngameShopSpigot.java b/IngameShopSpigot/src/main/java/de/mviper/spigot/IngameShopSpigot.java new file mode 100644 index 0000000..f34aa84 --- /dev/null +++ b/IngameShopSpigot/src/main/java/de/mviper/spigot/IngameShopSpigot.java @@ -0,0 +1,495 @@ +package de.mviper.spigot; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.Sound; +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 org.bukkit.plugin.RegisteredServiceProvider; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; + +import net.milkbowl.vault.economy.Economy; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.logging.Level; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +public class IngameShopSpigot extends JavaPlugin implements Listener { + + private static Economy econ = null; + private String wpBase; + private String wpUrlPending; + private String wpUrlExecute; + private String wpUrlComplete; + private String wpUrlCancel; // NEU + + private Gson gson = new Gson(); + private boolean debug = false; + private BukkitTask task; + + private String currency = "Coins"; + private String targetServer = "survival"; + + private Map orderCache = new HashMap<>(); + private Map activeOrderIds = new HashMap<>(); + + @Override + public void onEnable() { + if (!setupEconomy()) { + getLogger().severe("Kein Economy-Plugin (Vault) gefunden! Shop deaktiviert."); + getServer().getPluginManager().disablePlugin(this); + return; + } + + saveDefaultConfig(); + reloadConfig(); + + String domain = getConfig().getString("wordpress-url", "http://localhost/Windelgeschichten.org"); + if (domain.endsWith("/")) { + domain = domain.substring(0, domain.length() - 1); + } + wpBase = domain + "/wp-json/wis/v1"; + + wpUrlPending = wpBase + "/pending_orders"; + wpUrlExecute = wpBase + "/execute_order"; + wpUrlComplete = wpBase + "/complete_order"; + wpUrlCancel = wpBase + "/cancel_order"; // NEU + + this.targetServer = getConfig().getString("server-name", "survival").toLowerCase(); + this.currency = getConfig().getString("currency-name", "Coins"); + + int intervalSeconds = getConfig().getInt("check-interval", 10); + long pollInterval = intervalSeconds * 20L; + debug = getConfig().getBoolean("debug-mode", false); + + getServer().getPluginManager().registerEvents(this, this); + startPolling(pollInterval); + + getLogger().info("=== IngameShopSpigot v6.2 (Cancel Logic) CONFIG ==="); + getLogger().info("Domain: " + domain); + getLogger().info("Target Server: " + this.targetServer); + getLogger().info("Currency: " + this.currency); + } + + private boolean setupEconomy() { + RegisteredServiceProvider rsp = getServer().getServicesManager().getRegistration(Economy.class); + if (rsp == null) return false; + econ = rsp.getProvider(); + return econ != null; + } + + private void startPolling(long intervalTicks) { + this.task = new BukkitRunnable() { + @Override + public void run() { + new BukkitRunnable() { + @Override + public void run() { + for (Player p : Bukkit.getOnlinePlayers()) { + try { + String urlString = wpUrlPending + "?player=" + p.getName(); + URL url = new URL(urlString); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty("Content-Type", "application/json"); + conn.setConnectTimeout(5000); + conn.setReadTimeout(5000); + + if (conn.getResponseCode() == 200) { + BufferedReader reader = new BufferedReader( + new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8) + ); + StringBuilder response = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + response.append(line); + } + reader.close(); + + JsonObject json = JsonParser.parseString(response.toString()).getAsJsonObject(); + JsonArray orders = json.getAsJsonArray("orders"); + + if (orders != null && orders.size() > 0) { + for (int i = 0; i < orders.size(); i++) { + JsonObject order = orders.get(i).getAsJsonObject(); + + int id = order.get("id").getAsInt(); + String orderServer = order.has("server") ? order.get("server").getAsString().toLowerCase() : ""; + + if (!orderServer.equals(targetServer)) { + if (debug) getLogger().info("Order #" + id + " ist für Server '" + orderServer + "'. Ignoriere."); + continue; + } + + String jsonResponse = order.has("response") ? order.get("response").getAsString() : "[]"; + String itemTitle = order.get("item_title").getAsString(); + double price = order.get("price").getAsDouble(); + String status = order.get("status").getAsString(); + + if ("pending".equals(status)) { + OrderData data = new OrderData(id, "multi_item", itemTitle, price, 1, jsonResponse); + orderCache.put(id, data); + activeOrderIds.put(p.getUniqueId(), id); + + Bukkit.getScheduler().runTask(IngameShopSpigot.this, () -> { + openConfirmGUI(p, id, itemTitle, price); + }); + } + } + } + } + } catch (Exception e) { + if (debug) Bukkit.getLogger().log(Level.WARNING, "Polling Fehler", e); + } + } + } + }.runTaskAsynchronously(IngameShopSpigot.this); + } + }.runTaskTimer(this, 20L, intervalTicks); + } + + private void openConfirmGUI(Player player, int orderId, String itemTitle, double price) { + Inventory gui = Bukkit.createInventory(null, 27, "§eKauf bestätigen?"); + + ItemStack info = new ItemStack(Material.WRITTEN_BOOK); + ItemMeta infoMeta = info.getItemMeta(); + infoMeta.setDisplayName(ChatColor.GOLD + itemTitle); + infoMeta.setLore(java.util.Arrays.asList( + ChatColor.WHITE + "Preis: " + price + " " + currency + )); + info.setItemMeta(infoMeta); + + ItemStack yes = new ItemStack(Material.LIME_WOOL); + ItemMeta yesMeta = yes.getItemMeta(); + yesMeta.setDisplayName(ChatColor.GREEN + "§lJA, kaufen!"); + yes.setItemMeta(yesMeta); + + ItemStack no = new ItemStack(Material.RED_WOOL); + ItemMeta noMeta = no.getItemMeta(); + noMeta.setDisplayName(ChatColor.RED + "§lNEIN, abbrechen"); + no.setItemMeta(noMeta); + + ItemStack pane = new ItemStack(Material.BLACK_STAINED_GLASS_PANE); + ItemMeta paneMeta = pane.getItemMeta(); + paneMeta.setDisplayName(" "); + pane.setItemMeta(paneMeta); + + for (int i = 0; i < 27; i++) gui.setItem(i, pane); + gui.setItem(13, info); + gui.setItem(11, yes); + gui.setItem(15, no); + + player.openInventory(gui); + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + if (!event.getView().getTitle().contains("Kauf bestätigen?")) return; + event.setCancelled(true); + if (!(event.getWhoClicked() instanceof Player)) return; + + Player p = (Player) event.getWhoClicked(); + int slot = event.getRawSlot(); + + if (slot == 11) { // JA + Integer orderId = activeOrderIds.get(p.getUniqueId()); + if (orderId != null) { + processOrder(p, orderId); + p.closeInventory(); + activeOrderIds.remove(p.getUniqueId()); + } else { + p.sendMessage(ChatColor.RED + "❌ Fehler: Kauf abgelaufen."); + p.closeInventory(); + } + } else if (slot == 15) { // NEIN + Integer orderId = activeOrderIds.get(p.getUniqueId()); + if (orderId != null) { + cancelOrder(p, orderId); // NEU + p.closeInventory(); + activeOrderIds.remove(p.getUniqueId()); + } + } + } + + private void processOrder(Player player, int orderId) { + OrderData data = orderCache.get(orderId); + if (data == null) { + player.sendMessage(ChatColor.RED + "❌ Fehler: Daten nicht gefunden."); + return; + } + + new BukkitRunnable() { + @Override + public void run() { + try { + String jsonInputString = "{\"id\":\"" + orderId + "\"}"; + URL url = new URL(wpUrlExecute); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", "application/json"); + conn.setDoOutput(true); + + try(OutputStream os = conn.getOutputStream()) { + byte[] input = jsonInputString.getBytes("utf-8"); + os.write(input, 0, input.length); + } + + int responseCode = conn.getResponseCode(); + if (responseCode == 200) { + Bukkit.getScheduler().runTask(IngameShopSpigot.this, () -> executeShopLogic(player, data, orderId)); + } else { + if (debug) getLogger().warning("Execute Order API Code: " + responseCode); + Bukkit.getScheduler().runTask(IngameShopSpigot.this, () -> { + player.sendMessage(ChatColor.RED + "❌ Server-Fehler beim Starten des Kaufs."); + }); + } + } catch (Exception e) { + getLogger().log(Level.SEVERE, "Fehler bei /execute_order", e); + Bukkit.getScheduler().runTask(IngameShopSpigot.this, () -> { + player.sendMessage(ChatColor.RED + "❌ Interner Fehler beim Kauf."); + }); + } + } + }.runTaskAsynchronously(this); + } + + // =========================================================== + // NEU: ABBRECHEN LOGIC + // =========================================================== + private void cancelOrder(Player player, int orderId) { + new BukkitRunnable() { + @Override + public void run() { + try { + String jsonInputString = "{\"id\":\"" + orderId + "\"}"; + URL url = new URL(wpUrlCancel); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", "application/json"); + conn.setDoOutput(true); + + try(OutputStream os = conn.getOutputStream()) { + byte[] input = jsonInputString.getBytes("utf-8"); + os.write(input, 0, input.length); + } + + int responseCode = conn.getResponseCode(); + + if (responseCode == 200) { + orderCache.remove(orderId); + player.sendMessage(ChatColor.YELLOW + "❌ Kauf abgebrochen."); + if (debug) getLogger().info("✅ Order #" + orderId + " successfully cancelled"); + } else { + if (debug) getLogger().warning("⚠️ Cancel Order API returned code: " + responseCode); + player.sendMessage(ChatColor.RED + "❌ Fehler beim Abbrechen des Kaufs."); + // Fallback: Cache leeren damit es nicht als Loop erscheint + orderCache.remove(orderId); + activeOrderIds.remove(player.getUniqueId()); + } + } catch (Exception e) { + getLogger().log(Level.WARNING, "Fehler bei /cancel_order", e); + // Fallback: Cache leeren + orderCache.remove(orderId); + activeOrderIds.remove(player.getUniqueId()); + player.sendMessage(ChatColor.YELLOW + "❌ Kauf abgebrochen (lokale Bestätigung)."); + } + } + }.runTaskAsynchronously(this); + } + + private void executeShopLogic(Player player, OrderData data, int orderId) { + try { + if (econ.getBalance(player) < data.price) { + player.sendMessage(ChatColor.RED + "❌ Du hast nicht genug Geld! (Benötigt: " + data.price + " " + currency + ")"); + // Wenn der Kauf fehlschagt (Geld), sollte die Bestellung abgebrochen werden + cancelOrder(player, orderId); + return; + } + + econ.withdrawPlayer(player, data.price); + player.sendMessage(ChatColor.GREEN + "💰 " + data.price + " " + currency + " abgezogen."); + + try { + JsonElement root = gson.fromJson(data.jsonPayload, JsonElement.class); + JsonArray items; + + if (root.isJsonObject()) { + items = root.getAsJsonObject().getAsJsonArray("items"); + } else { + items = root.getAsJsonArray(); + } + + int totalItemsGiven = 0; + + for (JsonElement e : items) { + JsonObject itemObj = e.getAsJsonObject(); + String itemId = itemObj.get("id").getAsString(); + int amount = itemObj.get("amount").getAsInt(); + + ItemStack item = parseItem(itemId); + if (item != null) { + int remaining = amount; + while (remaining > 0) { + int stackSize = Math.min(remaining, item.getMaxStackSize()); + ItemStack stack = item.clone(); + stack.setAmount(stackSize); + + if (player.getInventory().firstEmpty() == -1) { + player.getWorld().dropItemNaturally(player.getLocation(), stack); + } else { + player.getInventory().addItem(stack); + } + remaining -= stackSize; + } + totalItemsGiven++; + } else { + player.sendMessage(ChatColor.RED + "❌ Item '" + itemId + "' konnte nicht gefunden werden."); + } + } + + if (totalItemsGiven > 0) { + player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1.0F, 1.0F); + player.sendMessage(ChatColor.GREEN + "✅ Kauf erfolgreich abgeschlossen!"); + } + + markOrderCompleted(orderId); + orderCache.remove(orderId); + + } catch (Exception e) { + getLogger().log(Level.SEVERE, "Fehler beim Verarbeiten der JSON-Items", e); + player.sendMessage(ChatColor.RED + "❌ Fehler beim Verteilen der Items. Bitte Admin kontaktieren."); + markOrderCompleted(orderId); + } + + } catch (Exception e) { + getLogger().log(Level.SEVERE, "Fehler bei Shop-Logik", e); + player.sendMessage(ChatColor.RED + "❌ Interner Fehler."); + } + } + + private void markOrderCompleted(int orderId) { + new BukkitRunnable() { + @Override + public void run() { + try { + String jsonInputString = "{\"id\":\"" + orderId + "\"}"; + + if (debug) getLogger().info("🔄 Marking order #" + orderId + " as completed..."); + + URL url = new URL(wpUrlComplete); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", "application/json"); + conn.setDoOutput(true); + + try(OutputStream os = conn.getOutputStream()) { + byte[] input = jsonInputString.getBytes("utf-8"); + os.write(input, 0, input.length); + } + + int responseCode = conn.getResponseCode(); + + if (debug) { + if (responseCode == 200) { + getLogger().info("✅ Order #" + orderId + " successfully marked as completed"); + } else { + getLogger().warning("⚠️ Complete Order API returned code: " + responseCode); + } + } + } catch (Exception e) { + getLogger().log(Level.WARNING, "Complete Order Error for #" + orderId, e); + } + } + }.runTaskAsynchronously(this); + } + + private ItemStack parseItem(String itemId) { + try { + String cleanId = itemId.toUpperCase().trim().replace("MINECRAFT:", "").replace("MC:", ""); + String materialName = numericIdToMaterial(cleanId); + if (materialName == null) materialName = cleanId; + + if (materialName.equalsIgnoreCase("VIP") || materialName.startsWith("VIP")) { + return null; + } + + Material material = Material.matchMaterial(materialName); + if (material != null && material.isItem()) { + return new ItemStack(material,1); + } + return null; + } catch (Exception e) { + getLogger().log(Level.WARNING, "parseItem Fehler: " + itemId, e); + return null; + } + } + + private String numericIdToMaterial(String id) { + if (!id.matches("\\d+")) return null; + switch (id) { + case "352": return "BONE_MEAL"; + case "264": return "EMERALD"; + case "388": return "EMERALD_BLOCK"; + case "357": return "GOLDEN_APPLE"; + case "322": return "GOLDEN_CARROT"; + case "348": return "GOLD_INGOT"; + case "133": return "EMERALD_ORE"; + case "41": return "GOLD_ORE"; + case "14": return "GOLD_BLOCK"; + case "289": return "COOKIE"; + case "1": return "STONE"; + case "5": return "PLANKS"; + case "17": return "OAK_LOG"; + case "260": return "BONE_BLOCK"; + case "287": return "TOTEM_OF_UNDYING"; + default: return null; + } + } + + private static class OrderData { + int id; + String itemId; + String itemTitle; + double price; + int quantity; + String jsonPayload; + + OrderData(int id, String itemId, String itemTitle, double price, int quantity, String jsonPayload) { + this.id = id; + this.itemId = itemId; + this.itemTitle = itemTitle; + this.price = price; + this.quantity = quantity; + this.jsonPayload = jsonPayload; + } + } + + @Override + public void onDisable() { + if (task != null) { + task.cancel(); + } + getLogger().info("IngameShopSpigot gestoppt"); + } +} \ No newline at end of file