From f2094dd057da1f1ee60189c7b92ec0a654c9326b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 17 May 2026 20:12:56 +0000 Subject: [PATCH] fix shulker box selling and add README - Fix isShulkerBox to match SHULKER_BOX (the default purple shulker box was never recognized because the check used endsWith("_SHULKER_BOX")) - Fix GUIListener.onClose: clone shulker before selling its contents and only return the modified box after a successful deposit; on deposit failure the original clone (full contents intact) is returned instead - Add README.md with full feature overview, install steps, commands, permissions, config reference, and build instructions Agent-Logs-Url: https://github.com/Faboit1/sell-plugin/sessions/c172a735-65a5-4363-8864-5b461c4f774b Co-authored-by: Faboit1 <177459774+Faboit1@users.noreply.github.com> --- README.md | 106 ++++++++++++++++++ .../yourname/sellplugin/gui/GUIListener.java | 27 ++++- .../sellplugin/manager/SellManager.java | 6 +- 3 files changed, 134 insertions(+), 5 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..b06c940 --- /dev/null +++ b/README.md @@ -0,0 +1,106 @@ +# SellPlugin + +A Minecraft Paper plugin that lets players sell items for in-game currency through a polished GUI system with per-category multipliers, daily bonuses, and a leaderboard. + +## Features + +- **Category shop** (`/sell`) — browse categories, view prices, and sell items one category at a time. +- **Quick sell-all** (`/sellall`) — sell every sellable item in your inventory (including shulker box contents) with a single confirm click. +- **Shulker box support** — place a shulker box in the sell GUI or run `/sellall`; the sellable items inside are sold and the (now-emptied) shulker box is returned to you automatically. +- **Per-category multipliers** — players unlock higher sell multipliers by earning money in each category. Multipliers are persistent across sessions. +- **Daily category bonuses** — a configurable number of random categories receive a flat bonus multiplier each day, encouraging varied play. +- **Top sellers leaderboard** (`/topsell`) — paginated GUI showing top earners per category. +- **Economy support** — works with Vault (any Vault-compatible economy plugin) or CoinsEngine. +- **Fully configurable** — GUI titles, item slots, filler materials, sounds, messages, category icons, and item prices are all driven by config files. + +## Requirements + +| Dependency | Version | Notes | +|------------|---------|-------| +| Paper | 1.21.1+ | Spigot is not supported | +| Vault | any | For standard economy plugins | +| CoinsEngine | any | Alternative economy (optional) | + +## Installation + +1. Drop `SellPlugin.jar` into your server's `plugins/` folder. +2. Make sure at least one of Vault (+ an economy plugin such as EssentialsX) or CoinsEngine is installed. +3. Restart the server — `plugins/SellPlugin/` will be created with default config files. +4. Edit `config.yml` and `price.yml` to your liking. +5. Run `/sell reload` or restart to apply changes. + +## Commands + +| Command | Permission | Description | +|---------|-----------|-------------| +| `/sell` | `sellplugin.use` | Opens the main category shop GUI | +| `/sell reload` | `sellplugin.reload` | Reloads config and price files in-place | +| `/sellall` | `sellplugin.use` | Opens the quick sell-all GUI | +| `/topsell` | `sellplugin.topsell` | Opens the top sellers leaderboard | + +Aliases for `/sell`: `sellmenu`, `sellgui`, `shop` +Aliases for `/topsell`: `sellertop`, `leaderboard` + +## Permissions + +| Node | Default | Description | +|------|---------|-------------| +| `sellplugin.use` | everyone | Use `/sell` and `/sellall` | +| `sellplugin.topsell` | everyone | Use `/topsell` | +| `sellplugin.reload` | op | Use `/sell reload` | + +## Configuration + +### `config.yml` — key options + +```yaml +# Economy backend: VAULT or COINSENGINE +economy-mode: VAULT + +# Multiplier unlock thresholds +start-multiplier: 1000.0 # cost of first tier (1.1×) +multiplier: 1.6 # each tier costs (previous × this) +max-multiplier: 3.0 # display cap for the progress bar + +# Daily bonus +daily-bonus: + boosted-count: 2 # how many categories get the bonus each day + bonus-amount: 0.4 # flat bonus added to the multiplier +``` + +### `price.yml` — item prices + +Items are grouped by category. Each top-level key is a category ID that must also appear in `category-order` inside `config.yml`. + +```yaml +ores: + DIAMOND: 50 + EMERALD: 80 + IRON_INGOT: 5 + GOLD_INGOT: 10 +``` + +Potion prices use a `MATERIAL:POTION_TYPE` key: + +```yaml +potions: + POTION:NIGHT_VISION: 12 + SPLASH_POTION:HEALING: 8 +``` + +## Shulker Box Selling + +Any shulker box colour — including the default purple `SHULKER_BOX` and all sixteen dyed variants — is supported everywhere: + +- **`/sell` GUI** — place a shulker box in the item area and close the inventory. The sellable items inside are sold, and the now-emptied shulker box is returned to your inventory. +- **`/sellall`** — shulker box contents are sold along with your regular inventory items. The box itself stays in your inventory. + +If the economy transaction fails, the shulker box is returned **with its original contents intact** (no items are lost). + +## Building from source + +```bash +mvn clean package +``` + +The shaded jar is produced in `target/SellPlugin-.jar`. The `libs/` directory must contain `NightCore.jar` and `CoinsEngine.jar` (provided separately) for the build to succeed. diff --git a/src/main/java/com/yourname/sellplugin/gui/GUIListener.java b/src/main/java/com/yourname/sellplugin/gui/GUIListener.java index 2b24944..d8ebda0 100644 --- a/src/main/java/com/yourname/sellplugin/gui/GUIListener.java +++ b/src/main/java/com/yourname/sellplugin/gui/GUIListener.java @@ -248,18 +248,24 @@ public void onClose(InventoryCloseEvent e) { List sellableItems = new ArrayList<>(); List nonSellableItems = new ArrayList<>(); + // Track shulker boxes as [modifiedItem, originalClone] pairs so we can + // return the correct version depending on whether the deposit succeeds. + List shulkerBoxPairs = new ArrayList<>(); + for (int i = 0; i < ShopMainGUI.BOTTOM_ROW_START; i++) { ItemStack item = top.getItem(i); if (item == null || item.getType() == Material.AIR) continue; - // Shulker box: sell its contents, return the (now empty/partially-empty) shulker + // Shulker box: sell its contents but defer returning the box until + // after the deposit so we can roll back properly on failure. if (SellManager.isShulkerBox(item)) { + ItemStack originalClone = item.clone(); SellManager.ShulkerSellData data = pl.getSellManager().sellShulkerContents(player, item, null, null); totalEarned += data.earned; totalItems += data.items; data.categoryEarnings.forEach((cat, val) -> categoryEarnings.merge(cat, val, Double::sum)); - // Return the shulker box (now with sold items removed) to the player - returnItem(player, item); + // [0] = modified item (contents removed), [1] = original clone + shulkerBoxPairs.add(new ItemStack[]{item, originalClone}); continue; } @@ -280,6 +286,7 @@ public void onClose(InventoryCloseEvent e) { sellableItems.add(item); } + // Always return non-sellable items immediately. for (ItemStack item : nonSellableItems) { returnItem(player, item); } @@ -290,12 +297,26 @@ public void onClose(InventoryCloseEvent e) { for (Map.Entry entry : categoryEarnings.entrySet()) { pl.getMultiplierManager().addEarnings(player, entry.getKey(), entry.getValue()); } + // Return the modified shulker boxes (sellable contents removed). + for (ItemStack[] pair : shulkerBoxPairs) { + returnItem(player, pair[0]); + } pl.getSellManager().sendSellNotification(player, totalEarned, totalItems); } else { player.sendMessage(pl.getConfigManager().getMessage("economy-error")); + // Deposit failed – return everything in its original state. for (ItemStack item : sellableItems) { returnItem(player, item); } + // Restore shulker boxes to their original state (undo content removal). + for (ItemStack[] pair : shulkerBoxPairs) { + returnItem(player, pair[1]); + } + } + } else { + // Nothing was sold – return shulker boxes unchanged. + for (ItemStack[] pair : shulkerBoxPairs) { + returnItem(player, pair[0]); } } } diff --git a/src/main/java/com/yourname/sellplugin/manager/SellManager.java b/src/main/java/com/yourname/sellplugin/manager/SellManager.java index 70a382c..2cb78fa 100644 --- a/src/main/java/com/yourname/sellplugin/manager/SellManager.java +++ b/src/main/java/com/yourname/sellplugin/manager/SellManager.java @@ -289,9 +289,11 @@ public void sendSellNotification(Player player, double amount, int itemCount) { // Shulker box helpers // --------------------------------------------------------------- - /** Returns true if the item is any colour of shulker box. */ + /** Returns true if the item is any colour of shulker box (including the default purple one). */ public static boolean isShulkerBox(ItemStack item) { - return item != null && item.getType().name().endsWith("_SHULKER_BOX"); + if (item == null) return false; + String name = item.getType().name(); + return name.equals("SHULKER_BOX") || name.endsWith("_SHULKER_BOX"); } /**