Upload folder via GUI - src
This commit is contained in:
@@ -66,6 +66,8 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
|||||||
private String wpUrlExecute;
|
private String wpUrlExecute;
|
||||||
private String wpUrlComplete;
|
private String wpUrlComplete;
|
||||||
private String wpUrlCancel;
|
private String wpUrlCancel;
|
||||||
|
private String wpUrlSellItems;
|
||||||
|
private String wpUrlSell;
|
||||||
|
|
||||||
private Gson gson = new Gson();
|
private Gson gson = new Gson();
|
||||||
private boolean debug = false;
|
private boolean debug = false;
|
||||||
@@ -77,6 +79,14 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
|||||||
private boolean flyRedeemDisabled = false;
|
private boolean flyRedeemDisabled = false;
|
||||||
private String incomeReceiver = "";
|
private String incomeReceiver = "";
|
||||||
|
|
||||||
|
// Sell-Feature
|
||||||
|
private double sellPriceOffset = 0.0; // z.B. -10.0 = -10 % vom WordPress-Ankaufspreis
|
||||||
|
private boolean sellEnabled = true;
|
||||||
|
|
||||||
|
private final Map<UUID, List<SellManager.SellEntry>> sellCache = new HashMap<>();
|
||||||
|
private final Map<UUID, Integer> sellAmountPage = new HashMap<>();
|
||||||
|
private static final String GUI_SELL_TITLE = ChatColor.GOLD + "💰 Items verkaufen";
|
||||||
|
|
||||||
private Map<Integer, OrderData> orderCache = new HashMap<>();
|
private Map<Integer, OrderData> orderCache = new HashMap<>();
|
||||||
private Map<UUID, Integer> activeOrderIds = new ConcurrentHashMap<>();
|
private Map<UUID, Integer> activeOrderIds = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@@ -114,6 +124,8 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
|||||||
wpUrlExecute = wpBase + "/execute_order";
|
wpUrlExecute = wpBase + "/execute_order";
|
||||||
wpUrlComplete = wpBase + "/complete_order";
|
wpUrlComplete = wpBase + "/complete_order";
|
||||||
wpUrlCancel = wpBase + "/cancel_order";
|
wpUrlCancel = wpBase + "/cancel_order";
|
||||||
|
wpUrlSellItems = wpBase + "/sell_items";
|
||||||
|
wpUrlSell = wpBase + "/sell_item";
|
||||||
|
|
||||||
targetServer = getConfig().getString("server-name", "survival").toLowerCase();
|
targetServer = getConfig().getString("server-name", "survival").toLowerCase();
|
||||||
currency = getConfig().getString("currency-name", "Coins");
|
currency = getConfig().getString("currency-name", "Coins");
|
||||||
@@ -121,6 +133,8 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
|||||||
debug = getConfig().getBoolean("debug-mode", false);
|
debug = getConfig().getBoolean("debug-mode", false);
|
||||||
flyRedeemDisabled = getConfig().getBoolean("fly-redeem-disabled", false);
|
flyRedeemDisabled = getConfig().getBoolean("fly-redeem-disabled", false);
|
||||||
incomeReceiver = getConfig().getString("income-receiver", "");
|
incomeReceiver = getConfig().getString("income-receiver", "");
|
||||||
|
sellEnabled = getConfig().getBoolean("sell.enabled", true);
|
||||||
|
sellPriceOffset = getConfig().getDouble("sell.price-offset", 0.0);
|
||||||
|
|
||||||
if (apiKey.isEmpty()) {
|
if (apiKey.isEmpty()) {
|
||||||
getLogger().warning("⚠️ Kein api-key in config.yml gesetzt!");
|
getLogger().warning("⚠️ Kein api-key in config.yml gesetzt!");
|
||||||
@@ -148,9 +162,12 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
|||||||
getCommand("flygive").setExecutor(new FlyGiveCommand());
|
getCommand("flygive").setExecutor(new FlyGiveCommand());
|
||||||
getCommand("flypause").setExecutor(new FlyPauseCommand());
|
getCommand("flypause").setExecutor(new FlyPauseCommand());
|
||||||
getCommand("rankinfo").setExecutor(new RankInfoCommand());
|
getCommand("rankinfo").setExecutor(new RankInfoCommand());
|
||||||
|
getCommand("sell").setExecutor(new SellCommand());
|
||||||
|
getCommand("wpis").setExecutor(new ReloadCommand());
|
||||||
|
|
||||||
startPolling(pollInterval);
|
startPolling(pollInterval);
|
||||||
flyManager.startSessionPersist();
|
flyManager.startSessionPersist();
|
||||||
|
startSellItemPolling();
|
||||||
|
|
||||||
getLogger().info("IngameShopSpigot v6.4 aktiv.");
|
getLogger().info("IngameShopSpigot v6.4 aktiv.");
|
||||||
}
|
}
|
||||||
@@ -175,6 +192,45 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
|||||||
// POLLING
|
// POLLING
|
||||||
// ===========================================================
|
// ===========================================================
|
||||||
|
|
||||||
|
/** Lädt die Ankaufliste initial und alle 5 Minuten neu aus WordPress */
|
||||||
|
private void startSellItemPolling() {
|
||||||
|
Runnable refresh = () -> new BukkitRunnable() {
|
||||||
|
@Override public void run() {
|
||||||
|
try {
|
||||||
|
HttpURLConnection conn = openAuthConnection(
|
||||||
|
wpUrlSellItems + "?server=" + targetServer, "GET");
|
||||||
|
if (conn.getResponseCode() == 200) {
|
||||||
|
String body = readResponse(conn);
|
||||||
|
JsonObject json = JsonParser.parseString(body).getAsJsonObject();
|
||||||
|
JsonArray items = json.getAsJsonArray("items");
|
||||||
|
List<SellManager.SellEntry> list = new ArrayList<>();
|
||||||
|
if (items != null) {
|
||||||
|
for (JsonElement el : items) {
|
||||||
|
JsonObject o = el.getAsJsonObject();
|
||||||
|
list.add(new SellManager.SellEntry(
|
||||||
|
o.get("item_id").getAsString(),
|
||||||
|
o.get("name").getAsString(),
|
||||||
|
o.get("buy_price").getAsDouble(),
|
||||||
|
o.get("sell_price").getAsDouble()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SellManager.getInstance().load(list);
|
||||||
|
if (debug) getLogger().info("[Sell] " + list.size() + " Items geladen.");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (debug) getLogger().log(Level.WARNING, "[Sell] Ladefehler", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.runTaskAsynchronously(this);
|
||||||
|
|
||||||
|
refresh.run();
|
||||||
|
// Alle 5 Minuten neu laden
|
||||||
|
new BukkitRunnable() {
|
||||||
|
@Override public void run() { refresh.run(); }
|
||||||
|
}.runTaskTimer(this, 20L * 300, 20L * 300);
|
||||||
|
}
|
||||||
|
|
||||||
private void startPolling(long intervalTicks) {
|
private void startPolling(long intervalTicks) {
|
||||||
this.task = new BukkitRunnable() {
|
this.task = new BukkitRunnable() {
|
||||||
@Override public void run() {
|
@Override public void run() {
|
||||||
@@ -459,6 +515,9 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
|||||||
public void onInventoryClick(InventoryClickEvent event) {
|
public void onInventoryClick(InventoryClickEvent event) {
|
||||||
String title = event.getView().getTitle();
|
String title = event.getView().getTitle();
|
||||||
|
|
||||||
|
// ── Sell GUI ──────────────────────────────────────────────────────
|
||||||
|
if (handleSellClick(event)) return;
|
||||||
|
|
||||||
// ── Fly-Codes GUI ──────────────────────────────────────────────────
|
// ── Fly-Codes GUI ──────────────────────────────────────────────────
|
||||||
if (title.equals(GUI_FLYCODES)) {
|
if (title.equals(GUI_FLYCODES)) {
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
@@ -2061,6 +2120,470 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ===========================================================
|
||||||
|
// RELOAD COMMAND
|
||||||
|
// ===========================================================
|
||||||
|
|
||||||
|
private class ReloadCommand implements CommandExecutor {
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command,
|
||||||
|
String label, String[] args) {
|
||||||
|
// /wpis reload
|
||||||
|
if (args.length == 0 || !args[0].equalsIgnoreCase("reload")) {
|
||||||
|
sender.sendMessage(ChatColor.YELLOW + "» IngameShop Pro");
|
||||||
|
sender.sendMessage(ChatColor.GRAY + " /wpis reload " + ChatColor.WHITE + "– Config & Sell-Items neu laden");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!sender.hasPermission("ingameshop.reload")) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "✗ Keine Berechtigung.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
sender.sendMessage(ChatColor.YELLOW + "⟳ IngameShop wird neu geladen...");
|
||||||
|
|
||||||
|
// Config neu laden
|
||||||
|
reloadConfig();
|
||||||
|
|
||||||
|
String domain = getConfig().getString("wordpress-url", "http://localhost");
|
||||||
|
if (domain.endsWith("/")) domain = domain.substring(0, domain.length() - 1);
|
||||||
|
wpBase = domain + "/wp-json/wis/v1";
|
||||||
|
|
||||||
|
wpUrlPending = wpBase + "/pending_orders";
|
||||||
|
wpUrlPendingOffline = wpBase + "/pending_offline";
|
||||||
|
wpUrlExecute = wpBase + "/execute_order";
|
||||||
|
wpUrlComplete = wpBase + "/complete_order";
|
||||||
|
wpUrlCancel = wpBase + "/cancel_order";
|
||||||
|
wpUrlSellItems = wpBase + "/sell_items";
|
||||||
|
wpUrlSell = wpBase + "/sell_item";
|
||||||
|
|
||||||
|
targetServer = getConfig().getString("server-name", "survival").toLowerCase();
|
||||||
|
currency = getConfig().getString("currency-name", "Coins");
|
||||||
|
apiKey = getConfig().getString("api-key", "");
|
||||||
|
debug = getConfig().getBoolean("debug-mode", false);
|
||||||
|
flyRedeemDisabled = getConfig().getBoolean("fly-redeem-disabled", false);
|
||||||
|
incomeReceiver = getConfig().getString("income-receiver", "");
|
||||||
|
sellEnabled = getConfig().getBoolean("sell.enabled", true);
|
||||||
|
sellPriceOffset = getConfig().getDouble("sell.price-offset", 0.0);
|
||||||
|
|
||||||
|
// Sell-Cache leeren und Items neu laden
|
||||||
|
sellCache.clear();
|
||||||
|
sellAmountPage.clear();
|
||||||
|
startSellItemPolling();
|
||||||
|
|
||||||
|
sender.sendMessage(ChatColor.GREEN + "✔ Config neu geladen!"
|
||||||
|
+ ChatColor.GRAY + " (Server: " + targetServer
|
||||||
|
+ " | Sell: " + (sellEnabled ? "aktiv" : "deaktiviert")
|
||||||
|
+ " | Offset: " + sellPriceOffset + " %)");
|
||||||
|
|
||||||
|
getLogger().info("[IngameShop] Config-Reload durch: " + sender.getName());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================
|
||||||
|
// SELL COMMAND
|
||||||
|
// ===========================================================
|
||||||
|
|
||||||
|
/** /sell [hand|all] */
|
||||||
|
private class SellCommand 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; }
|
||||||
|
if (!sellEnabled) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "✗ Der Ankauf ist auf diesem Server deaktiviert.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Player p = (Player) sender;
|
||||||
|
|
||||||
|
// /sell hand → sofort Item in der Hand verkaufen (1 Stack)
|
||||||
|
if (args.length > 0 && args[0].equalsIgnoreCase("hand")) {
|
||||||
|
sellHandItem(p);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// /sell all → alle verkaufbaren Items aus dem Inventar verkaufen
|
||||||
|
if (args.length > 0 && args[0].equalsIgnoreCase("all")) {
|
||||||
|
sellAllItems(p);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// /sell → GUI öffnen
|
||||||
|
openSellGUI(p, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================
|
||||||
|
// SELL GUI
|
||||||
|
// ===========================================================
|
||||||
|
|
||||||
|
private void openSellGUI(Player player, int page) {
|
||||||
|
new BukkitRunnable() {
|
||||||
|
@Override public void run() {
|
||||||
|
List<SellManager.SellEntry> entries = SellManager.getInstance().getEntries();
|
||||||
|
Bukkit.getScheduler().runTask(IngameShopSpigot.this,
|
||||||
|
() -> renderSellGUI(player, entries, page));
|
||||||
|
}
|
||||||
|
}.runTaskAsynchronously(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderSellGUI(Player player, List<SellManager.SellEntry> entries, int page) {
|
||||||
|
final int PAGE_SIZE = 45;
|
||||||
|
int totalPages = Math.max(1, (int) Math.ceil(entries.size() / (double) PAGE_SIZE));
|
||||||
|
page = Math.max(0, Math.min(page, totalPages - 1));
|
||||||
|
|
||||||
|
sellAmountPage.put(player.getUniqueId(), page);
|
||||||
|
sellCache.put(player.getUniqueId(), entries);
|
||||||
|
|
||||||
|
Inventory gui = Bukkit.createInventory(null, 54, GUI_SELL_TITLE);
|
||||||
|
|
||||||
|
// Pane-Reihe unten
|
||||||
|
ItemStack pane = new ItemStack(Material.BLACK_STAINED_GLASS_PANE);
|
||||||
|
ItemMeta pM = pane.getItemMeta(); pM.setDisplayName(" "); pane.setItemMeta(pM);
|
||||||
|
for (int i = 45; i < 54; i++) gui.setItem(i, pane);
|
||||||
|
|
||||||
|
int start = page * PAGE_SIZE;
|
||||||
|
int end = Math.min(start + PAGE_SIZE, entries.size());
|
||||||
|
|
||||||
|
for (int i = start; i < end; i++) {
|
||||||
|
SellManager.SellEntry e = entries.get(i);
|
||||||
|
ItemStack display = parseItem(e.itemId);
|
||||||
|
if (display == null) display = new ItemStack(Material.BARRIER);
|
||||||
|
|
||||||
|
// Wie viel hat der Spieler davon?
|
||||||
|
int playerHas = countItemInInventory(player, e.itemId);
|
||||||
|
double totalValue = playerHas * applyOffset(e.sellPrice);
|
||||||
|
|
||||||
|
ItemMeta meta = display.getItemMeta();
|
||||||
|
meta.setDisplayName(ChatColor.YELLOW + "" + ChatColor.BOLD + e.name);
|
||||||
|
List<String> lore = new ArrayList<>();
|
||||||
|
lore.add(ChatColor.GRAY + "──────────────────────");
|
||||||
|
lore.add(ChatColor.WHITE + "Ankauf: " + ChatColor.GREEN
|
||||||
|
+ String.format("%.2f", applyOffset(e.sellPrice)) + " " + currency + " /Stück");
|
||||||
|
lore.add(ChatColor.WHITE + "Kaufpreis: " + ChatColor.GRAY
|
||||||
|
+ e.buyPrice + " " + currency);
|
||||||
|
lore.add(ChatColor.GRAY + "──────────────────────");
|
||||||
|
if (playerHas > 0) {
|
||||||
|
lore.add(ChatColor.AQUA + "Du hast: " + ChatColor.WHITE + playerHas + "x");
|
||||||
|
lore.add(ChatColor.GREEN + "Wert: " + ChatColor.WHITE
|
||||||
|
+ String.format("%.2f", totalValue) + " " + currency);
|
||||||
|
lore.add(ChatColor.GRAY + "──────────────────────");
|
||||||
|
lore.add(ChatColor.YELLOW + "▶ Linksklick: " + ChatColor.WHITE + "Alles verkaufen");
|
||||||
|
lore.add(ChatColor.YELLOW + "▶ Rechtsklick: " + ChatColor.WHITE + "1 Stack verkaufen");
|
||||||
|
} else {
|
||||||
|
lore.add(ChatColor.RED + "✗ Nicht in deinem Inventar");
|
||||||
|
}
|
||||||
|
meta.setLore(lore);
|
||||||
|
display.setItemMeta(meta);
|
||||||
|
gui.setItem(i - start, display);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nav
|
||||||
|
if (page > 0) {
|
||||||
|
ItemStack prev = new ItemStack(Material.ARROW);
|
||||||
|
ItemMeta pm = prev.getItemMeta();
|
||||||
|
pm.setDisplayName(ChatColor.YELLOW + "◀ Vorherige Seite");
|
||||||
|
prev.setItemMeta(pm);
|
||||||
|
gui.setItem(45, prev);
|
||||||
|
}
|
||||||
|
if (page < totalPages - 1) {
|
||||||
|
ItemStack next = new ItemStack(Material.ARROW);
|
||||||
|
ItemMeta nm = next.getItemMeta();
|
||||||
|
nm.setDisplayName(ChatColor.YELLOW + "Nächste Seite ▶");
|
||||||
|
next.setItemMeta(nm);
|
||||||
|
gui.setItem(53, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info
|
||||||
|
ItemStack info = new ItemStack(Material.GOLD_INGOT);
|
||||||
|
ItemMeta im = info.getItemMeta();
|
||||||
|
im.setDisplayName(ChatColor.GOLD + "" + ChatColor.BOLD + "Item-Ankauf");
|
||||||
|
im.setLore(Arrays.asList(
|
||||||
|
ChatColor.GRAY + "Seite " + (page + 1) + " / " + totalPages,
|
||||||
|
ChatColor.GRAY + "" + entries.size() + " ankaufbare Items",
|
||||||
|
ChatColor.GRAY + "──────────────────────",
|
||||||
|
ChatColor.WHITE + "/sell all " + ChatColor.GRAY + "→ alles auf einmal verkaufen",
|
||||||
|
ChatColor.WHITE + "/sell hand " + ChatColor.GRAY + "→ Item in der Hand verkaufen"
|
||||||
|
));
|
||||||
|
info.setItemMeta(im);
|
||||||
|
gui.setItem(49, info);
|
||||||
|
|
||||||
|
if (entries.isEmpty()) {
|
||||||
|
ItemStack empty = new ItemStack(Material.BARRIER);
|
||||||
|
ItemMeta em = empty.getItemMeta();
|
||||||
|
em.setDisplayName(ChatColor.RED + "Keine ankaufbaren Items konfiguriert");
|
||||||
|
empty.setItemMeta(em);
|
||||||
|
gui.setItem(22, empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
player.openInventory(gui);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================
|
||||||
|
// SELL INVENTORY CLICK
|
||||||
|
// ===========================================================
|
||||||
|
|
||||||
|
// Wird in onInventoryClick() aufgerufen – dort einhaken
|
||||||
|
private boolean handleSellClick(InventoryClickEvent event) {
|
||||||
|
String title = event.getView().getTitle();
|
||||||
|
if (!GUI_SELL_TITLE.equals(title)) return false;
|
||||||
|
|
||||||
|
event.setCancelled(true);
|
||||||
|
if (!(event.getWhoClicked() instanceof Player)) return true;
|
||||||
|
Player p = (Player) event.getWhoClicked();
|
||||||
|
int slot = event.getRawSlot();
|
||||||
|
|
||||||
|
List<SellManager.SellEntry> entries = sellCache.get(p.getUniqueId());
|
||||||
|
if (entries == null) return true;
|
||||||
|
|
||||||
|
int page = sellAmountPage.getOrDefault(p.getUniqueId(), 0);
|
||||||
|
int total = (int) Math.ceil(entries.size() / 45.0);
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
if (slot == 45 && page > 0) { renderSellGUI(p, entries, page - 1); return true; }
|
||||||
|
if (slot == 53 && page < total - 1) { renderSellGUI(p, entries, page + 1); return true; }
|
||||||
|
if (slot < 0 || slot > 44) return true;
|
||||||
|
|
||||||
|
int idx = page * 45 + slot;
|
||||||
|
if (idx >= entries.size()) return true;
|
||||||
|
|
||||||
|
SellManager.SellEntry entry = entries.get(idx);
|
||||||
|
boolean rightClick = event.getClick().isRightClick();
|
||||||
|
|
||||||
|
// Stack (64) oder alles verkaufen
|
||||||
|
int sellAmount = rightClick
|
||||||
|
? Math.min(64, countItemInInventory(p, entry.itemId))
|
||||||
|
: countItemInInventory(p, entry.itemId);
|
||||||
|
|
||||||
|
if (sellAmount <= 0) {
|
||||||
|
p.sendMessage(ChatColor.RED + "✗ Du hast kein " + entry.name + " im Inventar.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
processSell(p, entry, sellAmount, () -> {
|
||||||
|
// GUI neu rendern nach Verkauf
|
||||||
|
List<SellManager.SellEntry> fresh = sellCache.get(p.getUniqueId());
|
||||||
|
if (fresh != null) renderSellGUI(p, fresh, sellAmountPage.getOrDefault(p.getUniqueId(), 0));
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================
|
||||||
|
// SELL LOGIC
|
||||||
|
// ===========================================================
|
||||||
|
|
||||||
|
private void sellHandItem(Player player) {
|
||||||
|
ItemStack hand = player.getInventory().getItemInMainHand();
|
||||||
|
if (hand == null || hand.getType() == Material.AIR) {
|
||||||
|
player.sendMessage(ChatColor.RED + "✗ Halte ein Item in der Hand.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String materialName = hand.getType().name().toLowerCase();
|
||||||
|
SellManager.SellEntry entry = SellManager.getInstance().findByMaterial(materialName);
|
||||||
|
if (entry == null) {
|
||||||
|
player.sendMessage(ChatColor.RED + "✗ Dieses Item wird nicht angekauft.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int amount = hand.getAmount();
|
||||||
|
processSell(player, entry, amount, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sellAllItems(Player player) {
|
||||||
|
List<SellManager.SellEntry> entries = SellManager.getInstance().getEntries();
|
||||||
|
if (entries.isEmpty()) {
|
||||||
|
player.sendMessage(ChatColor.RED + "✗ Keine ankaufbaren Items konfiguriert.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double totalEarned = 0;
|
||||||
|
int totalItems = 0;
|
||||||
|
for (SellManager.SellEntry entry : entries) {
|
||||||
|
int count = countItemInInventory(player, entry.itemId);
|
||||||
|
if (count <= 0) continue;
|
||||||
|
double price = applyOffset(entry.sellPrice);
|
||||||
|
double earned = price * count;
|
||||||
|
removeItemsFromInventory(player, entry.itemId, count);
|
||||||
|
totalEarned += earned;
|
||||||
|
totalItems += count;
|
||||||
|
}
|
||||||
|
if (totalItems == 0) {
|
||||||
|
player.sendMessage(ChatColor.YELLOW + "✗ Keine ankaufbaren Items im Inventar.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final double finalEarned = totalEarned;
|
||||||
|
final int finalItems = totalItems;
|
||||||
|
new BukkitRunnable() {
|
||||||
|
@Override public void run() {
|
||||||
|
// Batch: kein individueller WP-Call, direkt Vault
|
||||||
|
Bukkit.getScheduler().runTask(IngameShopSpigot.this, () -> {
|
||||||
|
econ.depositPlayer(player, finalEarned);
|
||||||
|
player.sendMessage(ChatColor.GREEN + "💰 /sell all: "
|
||||||
|
+ ChatColor.WHITE + finalItems + " Items"
|
||||||
|
+ ChatColor.GREEN + " verkauft für "
|
||||||
|
+ ChatColor.YELLOW + String.format("%.2f", finalEarned)
|
||||||
|
+ " " + currency + ChatColor.GREEN + "!");
|
||||||
|
player.playSound(player.getLocation(),
|
||||||
|
Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 0.8F, 1.0F);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}.runTaskAsynchronously(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processSell(Player player, SellManager.SellEntry entry,
|
||||||
|
int amount, Runnable afterCallback) {
|
||||||
|
double pricePerItem = applyOffset(entry.sellPrice);
|
||||||
|
double total = pricePerItem * amount;
|
||||||
|
|
||||||
|
// Items aus dem Inventar nehmen (sync)
|
||||||
|
int removed = removeItemsFromInventory(player, entry.itemId, amount);
|
||||||
|
if (removed == 0) {
|
||||||
|
player.sendMessage(ChatColor.RED + "✗ Keine " + entry.name + " im Inventar.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double actualTotal = pricePerItem * removed;
|
||||||
|
|
||||||
|
new BukkitRunnable() {
|
||||||
|
@Override public void run() {
|
||||||
|
try {
|
||||||
|
HttpURLConnection conn = openAuthConnection(wpUrlSell, "POST");
|
||||||
|
String body = "{\"player\":\"" + player.getName()
|
||||||
|
+ "\",\"server\":\"" + targetServer
|
||||||
|
+ "\",\"item_id\":\"" + entry.itemId
|
||||||
|
+ "\",\"quantity\":" + removed + "}";
|
||||||
|
writeJson(conn, body);
|
||||||
|
int code = conn.getResponseCode();
|
||||||
|
if (code == 200) {
|
||||||
|
String resp = readResponse(conn);
|
||||||
|
JsonObject json = JsonParser.parseString(resp).getAsJsonObject();
|
||||||
|
double wpTotal = json.has("total") ? json.get("total").getAsDouble() : actualTotal;
|
||||||
|
// Offset nochmals anwenden falls config abweicht
|
||||||
|
double finalPay = wpTotal + (wpTotal * sellPriceOffset / 100.0);
|
||||||
|
finalPay = Math.max(0, finalPay);
|
||||||
|
final double pay = finalPay;
|
||||||
|
Bukkit.getScheduler().runTask(IngameShopSpigot.this, () -> {
|
||||||
|
econ.depositPlayer(player, pay);
|
||||||
|
player.sendMessage(ChatColor.GREEN + "💰 "
|
||||||
|
+ ChatColor.WHITE + removed + "x " + entry.name
|
||||||
|
+ ChatColor.GREEN + " verkauft → "
|
||||||
|
+ ChatColor.YELLOW + String.format("%.2f", pay)
|
||||||
|
+ " " + currency + ChatColor.GREEN + "!");
|
||||||
|
player.playSound(player.getLocation(),
|
||||||
|
Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 0.8F, 1.1F);
|
||||||
|
if (afterCallback != null) afterCallback.run();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// WP nicht erreichbar → Fallback: direkt auszahlen
|
||||||
|
final double fallback = actualTotal;
|
||||||
|
Bukkit.getScheduler().runTask(IngameShopSpigot.this, () -> {
|
||||||
|
econ.depositPlayer(player, fallback);
|
||||||
|
player.sendMessage(ChatColor.GREEN + "💰 "
|
||||||
|
+ ChatColor.WHITE + removed + "x " + entry.name
|
||||||
|
+ ChatColor.GREEN + " verkauft → "
|
||||||
|
+ ChatColor.YELLOW + String.format("%.2f", fallback)
|
||||||
|
+ " " + currency + ChatColor.GREEN + "!");
|
||||||
|
if (debug) getLogger().warning("WP sell_item HTTP " + code + " – Fallback genutzt");
|
||||||
|
if (afterCallback != null) afterCallback.run();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
final double fallback = actualTotal;
|
||||||
|
Bukkit.getScheduler().runTask(IngameShopSpigot.this, () -> {
|
||||||
|
econ.depositPlayer(player, fallback);
|
||||||
|
player.sendMessage(ChatColor.GREEN + "💰 "
|
||||||
|
+ removed + "x " + entry.name + " → "
|
||||||
|
+ String.format("%.2f", fallback) + " " + currency);
|
||||||
|
if (debug) getLogger().log(Level.WARNING, "sell_item Fehler", ex);
|
||||||
|
if (afterCallback != null) afterCallback.run();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.runTaskAsynchronously(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Wendet den config sell.price-offset an (z.B. -10.0 = -10 %) */
|
||||||
|
private double applyOffset(double basePrice) {
|
||||||
|
if (sellPriceOffset == 0.0) return basePrice;
|
||||||
|
return Math.max(0, basePrice + basePrice * sellPriceOffset / 100.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Zählt wie viele Items des Typs im Inventar sind */
|
||||||
|
private int countItemInInventory(Player player, String itemId) {
|
||||||
|
ItemStack template = parseItem(itemId);
|
||||||
|
if (template == null) return 0;
|
||||||
|
Material mat = template.getType();
|
||||||
|
int count = 0;
|
||||||
|
for (ItemStack stack : player.getInventory().getContents()) {
|
||||||
|
if (stack != null && stack.getType() == mat) count += stack.getAmount();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Entfernt bis zu `amount` Items aus dem Inventar, gibt tatsächlich entfernten Betrag zurück */
|
||||||
|
private int removeItemsFromInventory(Player player, String itemId, int amount) {
|
||||||
|
ItemStack template = parseItem(itemId);
|
||||||
|
if (template == null) return 0;
|
||||||
|
Material mat = template.getType();
|
||||||
|
int removed = 0;
|
||||||
|
ItemStack[] contents = player.getInventory().getContents();
|
||||||
|
for (int i = 0; i < contents.length; i++) {
|
||||||
|
if (removed >= amount) break;
|
||||||
|
ItemStack stack = contents[i];
|
||||||
|
if (stack == null || stack.getType() != mat) continue;
|
||||||
|
int take = Math.min(stack.getAmount(), amount - removed);
|
||||||
|
removed += take;
|
||||||
|
if (take == stack.getAmount()) contents[i] = null;
|
||||||
|
else stack.setAmount(stack.getAmount() - take);
|
||||||
|
}
|
||||||
|
player.getInventory().setContents(contents);
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================
|
||||||
|
// SELL MANAGER
|
||||||
|
// ===========================================================
|
||||||
|
|
||||||
|
private static class SellManager {
|
||||||
|
|
||||||
|
private static SellManager instance;
|
||||||
|
private final List<SellEntry> entries = new ArrayList<>();
|
||||||
|
|
||||||
|
private SellManager() {}
|
||||||
|
|
||||||
|
static SellManager getInstance() {
|
||||||
|
if (instance == null) instance = new SellManager();
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void load(List<SellEntry> fresh) {
|
||||||
|
entries.clear();
|
||||||
|
entries.addAll(fresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized List<SellEntry> getEntries() {
|
||||||
|
return new ArrayList<>(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized SellEntry findByMaterial(String materialName) {
|
||||||
|
String clean = materialName.toLowerCase().replace("minecraft:", "");
|
||||||
|
for (SellEntry e : entries) {
|
||||||
|
String eid = e.itemId.toLowerCase().replace("minecraft:", "");
|
||||||
|
if (eid.equals(clean)) return e;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class SellEntry {
|
||||||
|
final String itemId, name;
|
||||||
|
final double buyPrice, sellPrice;
|
||||||
|
|
||||||
|
SellEntry(String itemId, String name, double buyPrice, double sellPrice) {
|
||||||
|
this.itemId = itemId;
|
||||||
|
this.name = name;
|
||||||
|
this.buyPrice = buyPrice;
|
||||||
|
this.sellPrice = sellPrice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ===========================================================
|
// ===========================================================
|
||||||
// DATA CLASS
|
// DATA CLASS
|
||||||
// ===========================================================
|
// ===========================================================
|
||||||
|
|||||||
@@ -36,3 +36,19 @@ mysql:
|
|||||||
database: "minecraft"
|
database: "minecraft"
|
||||||
username: "root"
|
username: "root"
|
||||||
password: "DEIN_PASSWORT"
|
password: "DEIN_PASSWORT"
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# Ankauf-Einstellungen (Spieler verkaufen Items)
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
sell:
|
||||||
|
# Ankauf auf diesem Server aktivieren
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Preiskorrektur relativ zum WP-Ankaufspreis (in Prozent)
|
||||||
|
# Beispiele:
|
||||||
|
# 0.0 → exakt den WP-Ankaufspreis zahlen
|
||||||
|
# -10.0 → 10 % weniger als der WP-Ankaufspreis
|
||||||
|
# +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
|
||||||
@@ -35,6 +35,14 @@ commands:
|
|||||||
description: Zeigt deine aktiven (zeitbasierten) Ränge an
|
description: Zeigt deine aktiven (zeitbasierten) Ränge an
|
||||||
usage: /rankinfo
|
usage: /rankinfo
|
||||||
permission: ingameshop.rankinfo
|
permission: ingameshop.rankinfo
|
||||||
|
sell:
|
||||||
|
description: Verkauft Items an den Shop (GUI, /sell hand, /sell all)
|
||||||
|
usage: /sell [hand|all]
|
||||||
|
permission: ingameshop.sell
|
||||||
|
wpis:
|
||||||
|
description: IngameShop Admin-Befehle
|
||||||
|
usage: /wpis <reload>
|
||||||
|
permission: ingameshop.reload
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
ingameshop.orders:
|
ingameshop.orders:
|
||||||
@@ -58,3 +66,9 @@ permissions:
|
|||||||
ingameshop.rankinfo:
|
ingameshop.rankinfo:
|
||||||
description: Kann eigene aktive Ränge einsehen
|
description: Kann eigene aktive Ränge einsehen
|
||||||
default: true
|
default: true
|
||||||
|
ingameshop.sell:
|
||||||
|
description: Kann Items an den Shop verkaufen
|
||||||
|
default: true
|
||||||
|
ingameshop.reload:
|
||||||
|
description: Kann die IngameShop-Config live neu laden
|
||||||
|
default: op
|
||||||
Reference in New Issue
Block a user