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 wpUrlComplete;
|
||||
private String wpUrlCancel;
|
||||
private String wpUrlSellItems;
|
||||
private String wpUrlSell;
|
||||
|
||||
private Gson gson = new Gson();
|
||||
private boolean debug = false;
|
||||
@@ -77,6 +79,14 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
private boolean flyRedeemDisabled = false;
|
||||
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<UUID, Integer> activeOrderIds = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -114,6 +124,8 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
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");
|
||||
@@ -121,6 +133,8 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
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);
|
||||
|
||||
if (apiKey.isEmpty()) {
|
||||
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("flypause").setExecutor(new FlyPauseCommand());
|
||||
getCommand("rankinfo").setExecutor(new RankInfoCommand());
|
||||
getCommand("sell").setExecutor(new SellCommand());
|
||||
getCommand("wpis").setExecutor(new ReloadCommand());
|
||||
|
||||
startPolling(pollInterval);
|
||||
flyManager.startSessionPersist();
|
||||
startSellItemPolling();
|
||||
|
||||
getLogger().info("IngameShopSpigot v6.4 aktiv.");
|
||||
}
|
||||
@@ -175,6 +192,45 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
// 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) {
|
||||
this.task = new BukkitRunnable() {
|
||||
@Override public void run() {
|
||||
@@ -459,6 +515,9 @@ public class IngameShopSpigot extends JavaPlugin implements Listener {
|
||||
public void onInventoryClick(InventoryClickEvent event) {
|
||||
String title = event.getView().getTitle();
|
||||
|
||||
// ── Sell GUI ──────────────────────────────────────────────────────
|
||||
if (handleSellClick(event)) return;
|
||||
|
||||
// ── Fly-Codes GUI ──────────────────────────────────────────────────
|
||||
if (title.equals(GUI_FLYCODES)) {
|
||||
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
|
||||
// ===========================================================
|
||||
|
||||
@@ -35,4 +35,20 @@ mysql:
|
||||
port: "3306"
|
||||
database: "minecraft"
|
||||
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
|
||||
usage: /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:
|
||||
ingameshop.orders:
|
||||
@@ -57,4 +65,10 @@ permissions:
|
||||
default: true
|
||||
ingameshop.rankinfo:
|
||||
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