diff --git a/IngameShopSpigot/src/main/java/de/mviper/spigot/IngameShopSpigot.java b/IngameShopSpigot/src/main/java/de/mviper/spigot/IngameShopSpigot.java index c0466c8..42df21b 100644 --- a/IngameShopSpigot/src/main/java/de/mviper/spigot/IngameShopSpigot.java +++ b/IngameShopSpigot/src/main/java/de/mviper/spigot/IngameShopSpigot.java @@ -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> sellCache = new HashMap<>(); + private final Map sellAmountPage = new HashMap<>(); + private static final String GUI_SELL_TITLE = ChatColor.GOLD + "πŸ’° Items verkaufen"; + private Map orderCache = new HashMap<>(); private Map 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 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 entries = SellManager.getInstance().getEntries(); + Bukkit.getScheduler().runTask(IngameShopSpigot.this, + () -> renderSellGUI(player, entries, page)); + } + }.runTaskAsynchronously(this); + } + + private void renderSellGUI(Player player, List 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 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 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 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 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 entries = new ArrayList<>(); + + private SellManager() {} + + static SellManager getInstance() { + if (instance == null) instance = new SellManager(); + return instance; + } + + synchronized void load(List fresh) { + entries.clear(); + entries.addAll(fresh); + } + + synchronized List 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 // =========================================================== diff --git a/IngameShopSpigot/src/main/resources/config.yml b/IngameShopSpigot/src/main/resources/config.yml index aecfc91..422e14b 100644 --- a/IngameShopSpigot/src/main/resources/config.yml +++ b/IngameShopSpigot/src/main/resources/config.yml @@ -35,4 +35,20 @@ mysql: port: "3306" database: "minecraft" username: "root" - password: "DEIN_PASSWORT" \ No newline at end of file + 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 β†’ β†’ "Ankauf aktivieren" + price-offset: -10.0 \ No newline at end of file diff --git a/IngameShopSpigot/src/main/resources/plugin.yml b/IngameShopSpigot/src/main/resources/plugin.yml index 95346b5..b8d8614 100644 --- a/IngameShopSpigot/src/main/resources/plugin.yml +++ b/IngameShopSpigot/src/main/resources/plugin.yml @@ -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 + permission: ingameshop.reload permissions: ingameshop.orders: @@ -57,4 +65,10 @@ permissions: default: true ingameshop.rankinfo: description: Kann eigene aktive RΓ€nge einsehen - default: true \ No newline at end of file + 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 \ No newline at end of file