WIP: Add ServerShop plugin — server-run global market for Paper/Purpur 1.21 #3

Draft
Copilot wants to merge 3 commits from copilot/create-server-shop-plugin into main
Copilot commented 2026-02-22 10:50:27 +00:00 (Migrated from github.com)

Adds a new servershop/ Maven module implementing a server-run global market designed to coexist with CommunityMarket. The shop maintains a deliberately large buy/sell spread (default 25% sell multiplier → 75% spread) so player-to-player trades remain economically attractive.

Module structure

  • category/Category data class + CategoryRegistry loads 11 categories from prices.yml, maps every Material to a category with MISC fallback
  • pricing/ItemPrice (buy/sell per unit, spread %) + PricingService resolves prices with per-item → per-category → global multiplier priority chain
  • economy/ — Vault wrapper; disables the plugin gracefully if no economy provider is found
  • gui/ — Three 54-slot GUIs (MainCategoryGui, CategoryGui, ItemDetailGui), a GuiController per-player state tracker, and ShopGuiListener which cancels all non-intended interactions (shift-click, drag, number-swap)
  • i18n/LangManager with en_US and pt_PT; missing keys fall back to en_US
  • storage/ — Async SQLite TransactionLogger (schema: uuid, player, type, material, amount, unit_price, total, timestamp)
  • command//shop opens the GUI; /shop reload (op) reloads config/prices/lang without restart

Pricing model

# config.yml — global spread
pricing:
  global-sell-multiplier: 0.25   # server pays 25% of buy price

# prices.yml — per-item override
categories:
  ORES:
    items:
      NETHERITE_INGOT:
        buy-price: 500.00
        sell-enabled: false      # can't sell back to server
      DIAMOND:
        buy-price: 50.00
        sell-price: 12.50        # explicit override

Anti-exploit

  • Balance and inventory counts re-validated at confirm-click time (not at GUI open time)
  • ItemUtil.canFit() checked before charging when full-inventory-behavior: CANCEL
  • All InventoryClickEvent and InventoryDragEvent inside shop GUIs are cancelled by default; only whitelisted slots trigger actions

Resources

  • prices.yml — 300+ items across 11 categories with sensible defaults
  • lang/en_US.yml + lang/pt_PT.yml — full i18n coverage
  • servershop/README.md — setup, pricing spread explanation, config reference, known limitations

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • jitpack.io
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/CommunityMarket/CommunityMarket/servershop org.codehaus.plexus.classworlds.launcher.Launcher compile -q (dns block)
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/CommunityMarket/CommunityMarket org.codehaus.plexus.classworlds.launcher.Launcher compile -q (dns block)
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/CommunityMarket/CommunityMarket org.codehaus.plexus.classworlds.launcher.Launcher -f pom.xml -B -V -e -Dfindbugs.skip -Dcheckstyle.skip -Dpmd.skip=true -Dspotbugs.skip -Denforcer.skip -Dmaven.javadoc.skip (dns block)
  • repo.papermc.io
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/CommunityMarket/CommunityMarket/servershop org.codehaus.plexus.classworlds.launcher.Launcher compile -q (dns block)
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/CommunityMarket/CommunityMarket org.codehaus.plexus.classworlds.launcher.Launcher compile -q (dns block)
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/CommunityMarket/CommunityMarket org.codehaus.plexus.classworlds.launcher.Launcher -f pom.xml -B -V -e -Dfindbugs.skip -Dcheckstyle.skip -Dpmd.skip=true -Dspotbugs.skip -Denforcer.skip -Dmaven.javadoc.skip (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

You are an expert Paper/Purpur plugin developer. Build a professional Server Shop / Global Market plugin for Paper/Purpur 1.21.11 using Java 21 and Maven.

Project goal:
This is a server-run market (NOT player-to-player). It exists alongside my player marketplace plugin (CommunityMarket) and must be designed to support it economically by having a large spread between buy and sell prices (so players still prefer selling to other players).

Core concept:

  • Every item/block in the game is available for buy and/or sell.
  • Prices are configurable and category-based.
  • Fully GUI-driven with categories, search, pagination, and quantity selection.
  • Uses Vault economy (required).
  • Clean, documented, Modrinth-ready.

================================================================================

  1. Technical requirements (MUST)
    ================================================================================
  • Target: Paper/Purpur 1.21.11
  • Java: 21
  • Maven project (pom.xml + full source)
  • Vault economy integration (required). Fail gracefully if missing.
  • SQLite for optional logging/stats (optional). YAML is ok if simpler.
  • Async where appropriate; no main-thread blocking for file/DB I/O.
  • Robust GUI handling (no dupes, cancel illegal clicks, validate everything server-side).
  • Strong code documentation (JavaDoc for public classes, comments for tricky logic).

================================================================================
2) GUI-only UX (MUST)

Commands:

  • Only /shop (alias /servershop) opens the main GUI.
    No other required commands for players; all interactions in GUI.

Main GUI:

  • Category buttons (e.g., Blocks, Ores, Farming, Food, Mob Drops, Redstone, Decoration, Tools, Combat, Brewing, Misc).
  • Search button (GUI-driven; anvil input allowed).
  • “Sell inventory” / “Sell hand” buttons (optional, but must confirm).

Category GUI:

  • Paginated grid of items.
  • Sorting options (optional): price asc/desc, alphabetical.
  • Each item shows:
    • Buy price
    • Sell price
    • Spread indicator (e.g., “Server spread: 60%”)

Item Detail GUI:

  • Shows item, buy/sell prices, wallet balance.
  • Quantity selector:
    • -1/-8/-16/-32 on left
    • +1/+8/+16/+32 on right
    • MIN / MAX buttons
  • Buy confirm and Sell confirm buttons (separate)
  • Clear messages on success/failure.

================================================================================
3) Pricing model (MUST)

We want a large gap between buy and sell prices to keep CommunityMarket valuable.

Implement pricing rules:

  • Each item has:
    • buyPrice (server sells to player)
    • sellPrice (server buys from player)
  • Default behavior:
    • sellPrice = buyPrice * sellMultiplier where multiplier is LOW (e.g., 0.25) — configurable globally and per-category.
  • Allow overrides per item:
    • explicit buy/sell prices
    • ability to disable selling or buying for certain items.

Important:

  • Do NOT use dynamic supply/demand pricing in v1 unless it’s simple and safe.
  • Keep it deterministic and config-driven.

================================================================================
4) Item coverage (MUST)

  • Include ALL items/blocks from the game:
    • Use Material enumeration for 1.21.11.
  • Provide sensible default categories mapping:
    • A category map in config that assigns Materials to categories.
  • The plugin must still function if new items are added (fallback category: Misc).

Exclude/handle:

  • Air and invalid materials should not appear.
  • Items with special meta (enchanted books, potions) can be:
    • either excluded by default OR included with base pricing (configurable).
      State the decision clearly and make it configurable.

================================================================================
5) Buy and Sell mechanics (MUST)

Buying:

  • Validate player has enough money.
  • Add items to inventory; if full:
    • either drop safely at player feet OR prevent purchase (configurable).
  • Charge via Vault.

Selling:

  • Validate player has the required quantity in inventory (exact material match).
  • Remove items safely server-side.
  • Pay via Vault.
  • Optionally apply tax/fee (configurable).
  • Provide “sell all of this material” shortcut (optional) with confirmation.

Anti-exploit:

  • Handle shift-click, number key swaps, drag events, double-click.
  • Re-check balance and inventory at confirm time.
  • No duplication vulnerabilities.

================================================================================
6) Configurability (MUST)

Provide config.yml with:

  • language: en_US default + pt_PT opti...

🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Adds a new `servershop/` Maven module implementing a server-run global market designed to coexist with CommunityMarket. The shop maintains a deliberately large buy/sell spread (default 25% sell multiplier → 75% spread) so player-to-player trades remain economically attractive. ## Module structure - **`category/`** — `Category` data class + `CategoryRegistry` loads 11 categories from `prices.yml`, maps every `Material` to a category with MISC fallback - **`pricing/`** — `ItemPrice` (buy/sell per unit, spread %) + `PricingService` resolves prices with per-item → per-category → global multiplier priority chain - **`economy/`** — Vault wrapper; disables the plugin gracefully if no economy provider is found - **`gui/`** — Three 54-slot GUIs (`MainCategoryGui`, `CategoryGui`, `ItemDetailGui`), a `GuiController` per-player state tracker, and `ShopGuiListener` which cancels all non-intended interactions (shift-click, drag, number-swap) - **`i18n/`** — `LangManager` with `en_US` and `pt_PT`; missing keys fall back to `en_US` - **`storage/`** — Async SQLite `TransactionLogger` (schema: uuid, player, type, material, amount, unit_price, total, timestamp) - **`command/`** — `/shop` opens the GUI; `/shop reload` (op) reloads config/prices/lang without restart ## Pricing model ```yaml # config.yml — global spread pricing: global-sell-multiplier: 0.25 # server pays 25% of buy price # prices.yml — per-item override categories: ORES: items: NETHERITE_INGOT: buy-price: 500.00 sell-enabled: false # can't sell back to server DIAMOND: buy-price: 50.00 sell-price: 12.50 # explicit override ``` ## Anti-exploit - Balance and inventory counts re-validated at confirm-click time (not at GUI open time) - `ItemUtil.canFit()` checked before charging when `full-inventory-behavior: CANCEL` - All `InventoryClickEvent` and `InventoryDragEvent` inside shop GUIs are cancelled by default; only whitelisted slots trigger actions ## Resources - `prices.yml` — 300+ items across 11 categories with sensible defaults - `lang/en_US.yml` + `lang/pt_PT.yml` — full i18n coverage - `servershop/README.md` — setup, pricing spread explanation, config reference, known limitations > [!WARNING] > > <details> > <summary>Firewall rules blocked me from connecting to one or more addresses (expand for details)</summary> > > #### I tried to connect to the following addresses, but was blocked by firewall rules: > > - `jitpack.io` > - Triggering command: `/usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/CommunityMarket/CommunityMarket/servershop org.codehaus.plexus.classworlds.launcher.Launcher compile -q` (dns block) > - Triggering command: `/usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/CommunityMarket/CommunityMarket org.codehaus.plexus.classworlds.launcher.Launcher compile -q` (dns block) > - Triggering command: `/usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/CommunityMarket/CommunityMarket org.codehaus.plexus.classworlds.launcher.Launcher -f pom.xml -B -V -e -Dfindbugs.skip -Dcheckstyle.skip -Dpmd.skip=true -Dspotbugs.skip -Denforcer.skip -Dmaven.javadoc.skip` (dns block) > - `repo.papermc.io` > - Triggering command: `/usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/CommunityMarket/CommunityMarket/servershop org.codehaus.plexus.classworlds.launcher.Launcher compile -q` (dns block) > - Triggering command: `/usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/CommunityMarket/CommunityMarket org.codehaus.plexus.classworlds.launcher.Launcher compile -q` (dns block) > - Triggering command: `/usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/CommunityMarket/CommunityMarket org.codehaus.plexus.classworlds.launcher.Launcher -f pom.xml -B -V -e -Dfindbugs.skip -Dcheckstyle.skip -Dpmd.skip=true -Dspotbugs.skip -Denforcer.skip -Dmaven.javadoc.skip` (dns block) > > If you need me to access, download, or install something from one of these locations, you can either: > > - Configure [Actions setup steps](https://gh.io/copilot/actions-setup-steps) to set up my environment, which run before the firewall is enabled > - Add the appropriate URLs or hosts to the custom allowlist in this repository's [Copilot coding agent settings](https://github.com/henriquescrrrr/CommunityMarket/settings/copilot/coding_agent) (admins only) > > </details> <!-- START COPILOT CODING AGENT SUFFIX --> <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> You are an expert Paper/Purpur plugin developer. Build a professional **Server Shop / Global Market** plugin for **Paper/Purpur 1.21.11** using **Java 21** and **Maven**. Project goal: This is a **server-run market** (NOT player-to-player). It exists alongside my player marketplace plugin (CommunityMarket) and must be designed to **support** it economically by having a **large spread** between buy and sell prices (so players still prefer selling to other players). Core concept: - Every item/block in the game is available for **buy** and/or **sell**. - Prices are configurable and category-based. - Fully GUI-driven with categories, search, pagination, and quantity selection. - Uses Vault economy (required). - Clean, documented, Modrinth-ready. ================================================================================ 1) Technical requirements (MUST) ================================================================================ - Target: Paper/Purpur 1.21.11 - Java: 21 - Maven project (pom.xml + full source) - Vault economy integration (required). Fail gracefully if missing. - SQLite for optional logging/stats (optional). YAML is ok if simpler. - Async where appropriate; no main-thread blocking for file/DB I/O. - Robust GUI handling (no dupes, cancel illegal clicks, validate everything server-side). - Strong code documentation (JavaDoc for public classes, comments for tricky logic). ================================================================================ 2) GUI-only UX (MUST) ================================================================================ Commands: - Only `/shop` (alias `/servershop`) opens the main GUI. No other required commands for players; all interactions in GUI. Main GUI: - Category buttons (e.g., Blocks, Ores, Farming, Food, Mob Drops, Redstone, Decoration, Tools, Combat, Brewing, Misc). - Search button (GUI-driven; anvil input allowed). - “Sell inventory” / “Sell hand” buttons (optional, but must confirm). Category GUI: - Paginated grid of items. - Sorting options (optional): price asc/desc, alphabetical. - Each item shows: - Buy price - Sell price - Spread indicator (e.g., “Server spread: 60%”) Item Detail GUI: - Shows item, buy/sell prices, wallet balance. - Quantity selector: - -1/-8/-16/-32 on left - +1/+8/+16/+32 on right - MIN / MAX buttons - Buy confirm and Sell confirm buttons (separate) - Clear messages on success/failure. ================================================================================ 3) Pricing model (MUST) ================================================================================ We want a **large gap** between buy and sell prices to keep CommunityMarket valuable. Implement pricing rules: - Each item has: - `buyPrice` (server sells to player) - `sellPrice` (server buys from player) - Default behavior: - `sellPrice = buyPrice * sellMultiplier` where multiplier is LOW (e.g., 0.25) — configurable globally and per-category. - Allow overrides per item: - explicit buy/sell prices - ability to disable selling or buying for certain items. Important: - Do NOT use dynamic supply/demand pricing in v1 unless it’s simple and safe. - Keep it deterministic and config-driven. ================================================================================ 4) Item coverage (MUST) ================================================================================ - Include ALL items/blocks from the game: - Use Material enumeration for 1.21.11. - Provide sensible default categories mapping: - A category map in config that assigns Materials to categories. - The plugin must still function if new items are added (fallback category: Misc). Exclude/handle: - Air and invalid materials should not appear. - Items with special meta (enchanted books, potions) can be: - either excluded by default OR included with base pricing (configurable). State the decision clearly and make it configurable. ================================================================================ 5) Buy and Sell mechanics (MUST) ================================================================================ Buying: - Validate player has enough money. - Add items to inventory; if full: - either drop safely at player feet OR prevent purchase (configurable). - Charge via Vault. Selling: - Validate player has the required quantity in inventory (exact material match). - Remove items safely server-side. - Pay via Vault. - Optionally apply tax/fee (configurable). - Provide “sell all of this material” shortcut (optional) with confirmation. Anti-exploit: - Handle shift-click, number key swaps, drag events, double-click. - Re-check balance and inventory at confirm time. - No duplication vulnerabilities. ================================================================================ 6) Configurability (MUST) ================================================================================ Provide `config.yml` with: - language: en_US default + pt_PT opti... </details> <!-- START COPILOT CODING AGENT TIPS --> --- 🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. [Learn more about Advanced Security.](https://gh.io/cca-advanced-security)
henriquescrrrr (Migrated from github.com) reviewed 2026-02-22 10:50:27 +00:00
This pull request is marked as a work in progress.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin copilot/create-server-shop-plugin:copilot/create-server-shop-plugin
git checkout copilot/create-server-shop-plugin
Sign in to join this conversation.