From cf84695ab2c55a00d5d4574b1cb9b39240da1f7d Mon Sep 17 00:00:00 2001 From: ez-plugins Date: Fri, 22 May 2026 05:28:30 +0200 Subject: [PATCH 1/5] fix(docs): document /sell, /shopadmin, /teamshop market and missing permissions - Add /sell (Quick Sell GUI) to Commands reference - Add /shopadmin [browse|market] admin command section - Add /teamshop market subcommand to Commands and TeamsAPI integration page - Fix /teamshop tab-completion list to include 'market' - Add ezshops.shop.admin, ezshops.teamshop.market, ezshops.pricing.admin.set, ezshops.pricing.admin.disable, ezshops.pricing.admin.list to Permissions reference - Add ezshops.teamshop.market to TeamsAPI integration permissions table --- CHANGELOG.md | 9 ++++++ docs/commands.md | 52 +++++++++++++++++++++++++++++++++- docs/integrations/teams-api.md | 10 +++++++ docs/permissions.md | 3 ++ 4 files changed, 73 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8ecc20..25634fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +- **`/sell` command missing from documentation** — the Quick Sell GUI command (`/sell`) is now documented in the Commands reference with its behavior and permission node. +- **`/shopadmin` command missing from documentation** — `/shopadmin [browse|market]` is now documented under Admin Commands including both the player-shops and team-market views. +- **`/teamshop market` subcommand missing from documentation** — the team P2P market subcommand is now documented in Commands, the TeamsAPI integration page, and the tab-completion list for `/teamshop`. +- **Missing permission nodes in documentation** — the following nodes were present in `plugin.yml` but absent from the Permissions reference; they are now documented: + - `ezshops.shop.admin` (open `/shopadmin` GUI) + - `ezshops.teamshop.market` (access team P2P market) + - `ezshops.pricing.admin.set`, `ezshops.pricing.admin.disable`, `ezshops.pricing.admin.list` (granular pricing-admin nodes) + ## [2.5.5] - 2026-05-18 ### Fixed diff --git a/docs/commands.md b/docs/commands.md index 6c79580..b5bd4ad 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -66,6 +66,20 @@ You can also use the category display name (case-insensitive, color codes ignore ### Trading Commands +#### `/sell` +Opens the Quick Sell GUI, allowing you to drag items in and sell them all at once. + +**Usage:** `/sell` +**Permission:** `ezshops.shop.sell` +**Aliases:** None + +**Behavior:** +- Opens a chest-style GUI where you can place items to sell +- Confirm to sell all items at once and receive combined earnings +- Can be disabled per-server via the configuration + +--- + #### `/sellhand` Sells the item currently held in your hand to the shop. @@ -200,6 +214,36 @@ Quick overview of cached stock quotes with pagination. ## Admin Commands +### Shop Administration + +#### `/shopadmin` +Opens the admin moderation GUI for inspecting and removing any active player shop or team market listing. + +**Usage:** +``` +/shopadmin +/shopadmin browse +/shopadmin market +``` +**Permission:** `ezshops.shop.admin` +**Aliases:** None + +**Subcommands:** + +##### `/shopadmin` / `/shopadmin browse` +Opens the paginated **Player Shops** view listing all active player-owned chest shops. Admins can remove any listing directly from the GUI. + +**Permission:** `ezshops.shop.admin` +**Example:** `/shopadmin browse` + +##### `/shopadmin market` +Switches the browse GUI to the **Team Market** view, showing all active team market listings across every team. + +**Permission:** `ezshops.shop.admin` +**Example:** `/shopadmin market` + +--- + ### Sign Shop Setup #### `/signshop` @@ -344,7 +388,7 @@ These commands require [TeamsAPI ≥ 1.4.1](integrations/teams-api) to be instal Opens the **Team Shop Dashboard** showing your team's name, current role bonuses and quick links to the treasury and stock browser. **Permission:** `ezshops.teamshop` (default: true) -**Tab completion:** `treasury`, `stocks` +**Tab completion:** `treasury`, `stocks`, `market` ##### `/teamshop treasury` Opens the **Team Treasury GUI**. View the shared balance, deposit, and (with the appropriate permission) withdraw funds. @@ -358,6 +402,12 @@ Opens the **Team Stock GUI**, listing all items currently in the team's shared s **Permission:** `ezshops.teamshop` (default: true) **Example:** `/teamshop stocks` +##### `/teamshop market` +Opens the **Team P2P Market GUI** where players can list items for sale and purchase listings from teammates. + +**Permission:** `ezshops.teamshop.market` (default: true) +**Example:** `/teamshop market` + --- ## Command Notes diff --git a/docs/integrations/teams-api.md b/docs/integrations/teams-api.md index cd9fb7e..053e12a 100644 --- a/docs/integrations/teams-api.md +++ b/docs/integrations/teams-api.md @@ -135,11 +135,21 @@ Opens the **Team Stock GUI**, listing all items your team currently has in the s --- +### `/teamshop market` + +Opens the **Team P2P Market GUI** where players can list items for sale and purchase listings from teammates. + +**Permission:** `ezshops.teamshop.market` +**Default:** true + +--- + ## Permissions | Node | Default | Description | |------|---------|-------------| | `ezshops.teamshop` | true | Access `/teamshop` and sub-GUIs | +| `ezshops.teamshop.market` | true | Access the team P2P market via `/teamshop market` | | `ezshops.teamshop.treasury.withdraw` | true | Withdraw funds from the team treasury | | `ezshops.teamshop.admin` | op | Administrative access to team stock data | diff --git a/docs/permissions.md b/docs/permissions.md index a54b2b8..c52a622 100644 --- a/docs/permissions.md +++ b/docs/permissions.md @@ -84,10 +84,12 @@ EzShops uses a hierarchical permission system. Permissions are organized into ca | Permission Node | Default | Description | |-----------------------------|---------|--------------------------------------------------| | `ezshops.reload` | op | Reload shop configuration with `/shop reload` | +| `ezshops.shop.admin` | op | Open `/shopadmin` moderation GUI | | `ezshops.shop.admin.minionhead` | op | Purchase minion heads directly (bypass restrictions) | **Details:** - `ezshops.reload` - Allows reloading all shop configurations, menus, categories, and pricing without server restart +- `ezshops.shop.admin` - Opens the admin browse GUI to inspect and remove any player shop or team market listing - `ezshops.shop.admin.minionhead` - Bypasses normal restrictions on minion heads (usually crate-only items) ### Sign Shop Management @@ -233,6 +235,7 @@ Permissions for the [TeamsAPI integration](integrations/teams-api.md). These nod | Permission | Default | Description | |------------|---------|-------------| | `ezshops.teamshop` | true | Access `/teamshop`, `/teamshop stocks`, and view the treasury GUI | +| `ezshops.teamshop.market` | true | Access the team P2P market via `/teamshop market` | | `ezshops.teamshop.treasury.withdraw` | true | Withdraw funds from the team treasury | | `ezshops.teamshop.admin` | op | Administrative access to team stock data (view/clear any team's stock) | From 5b387eb13a532b52f23d7e12cce6929bcb924f30 Mon Sep 17 00:00:00 2001 From: ez-plugins Date: Fri, 22 May 2026 05:31:59 +0200 Subject: [PATCH 2/5] fix(docs): replace fictional stock-market config block with real stock: schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documented 'stock-market:' key with volatility-min/max, demand-multiplier, min-price and update-interval never existed in the plugin. The real config key is 'stock:' with enabled, cooldown-millis, blocked, overrides and categories. Volatility (±10%) and demand factor (2% per unit) are hardcoded engine constants in StockMarketManager and are not user-configurable. Updated: - docs/configuration/main-settings.md - docs/shops/pricing/stock-market.md - CHANGELOG.md (2.5.5 bugfix entry) --- CHANGELOG.md | 1 + docs/configuration/main-settings.md | 69 ++++++++++++++++++++--------- docs/shops/pricing/stock-market.md | 60 +++++++++++++++++++------ 3 files changed, 96 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25634fc..b7e564c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - **`on-sell` commands not executing for `item-type: COMMAND` items** — `sell()` no longer checks or removes physical items from the player's inventory when the item's delivery type is `COMMAND`. Previously the transaction exited early with "insufficient items" because the player had no material to hand over, preventing sell commands from running at all. - **`on-sell execute-as` overridden by `on-buy execute-as`** — `ShopPricingManager` now tracks `execute-as` independently for the `on-buy` and `on-sell` blocks. Previously a single shared flag meant that setting `on-buy: execute-as: player` would silently override `on-sell: execute-as: console`, causing sell commands to run as the player instead of the console. +- **Incorrect stock-market configuration documented** — `docs/configuration/main-settings.md` and `docs/shops/pricing/stock-market.md` previously documented a non-existent `stock-market:` config block with keys `volatility-min`, `volatility-max`, `demand-multiplier`, `min-price`, and `update-interval`. None of these keys are read by the plugin. Both pages now document the real `stock:` section (`enabled`, `cooldown-millis`, `blocked`, `overrides`, `categories`) and clarify that volatility/demand constants are hardcoded engine values, not configurable options. ### Added - **Code coverage reporting** — JaCoCo is now configured in the Maven build (`jacoco-maven-plugin 0.8.12`). Coverage reports (`jacoco.xml`) are generated on every `mvn test` run and uploaded to Codecov by the CI workflow for both unit-test and feature-test jobs. diff --git a/docs/configuration/main-settings.md b/docs/configuration/main-settings.md index 2bee791..7185a32 100644 --- a/docs/configuration/main-settings.md +++ b/docs/configuration/main-settings.md @@ -78,39 +78,68 @@ player-shops: --- -## 📉 Stock Market & Price Calculation +## 📉 Stock Market *(For a full deep-dive into the Stock Market system, check out the [Stock Market Documentation](../shops/pricing/stock-market.md))* -**Version 2.0.0+ Security Improvements:** -- All stock sales now require confirmation through a GUI -- Fixed infinite money glitch vulnerability -- Enhanced transaction validation +**All stock sales require confirmation through a GUI before the transaction is finalised.** + +### Configuration (`config.yml`) + +The stock section controls which items are tradeable and how the GUI presents them. Volatility and demand parameters are internal engine constants and are **not configurable** via `config.yml`. -### Available Settings Overview ```yaml -stock-market: - # Enable stock market system +stock: + # Master switch – set to false to disable all /stock commands and GUIs. enabled: true - # Price volatility range (-10% to +10% by default) - volatility-min: -0.10 - volatility-max: 0.10 + # Per-player cooldown between trades in milliseconds. 0 = no cooldown. + cooldown-millis: 10000 + + # Materials that cannot be traded on the stock market. + blocked: + - BEDROCK + - COMMAND_BLOCK + + # Custom items to expose in the stock market. + # 'display' sets the GUI label; 'base-price' is the reference price shown + # in the all-stocks listing (the live trading price evolves independently). + overrides: + - id: "DIAMOND" + display: "&bDiamond" + base-price: 100.0 + + # Optional: group items under named category tabs in the GUI. + categories: + gems: + - DIAMOND + - EMERALD +``` + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| `enabled` | boolean | `true` | Enable/disable all stock features | +| `cooldown-millis` | integer | `0` | Milliseconds between player trades | +| `blocked` | list | `[]` | Materials blocked from trading | +| `overrides` | list | `[]` | Custom items with display names and reference prices | +| `categories` | map | `{}` | Named category → list of item groupings | - # Demand multiplier for price changes - demand-multiplier: 0.02 +### Internal Price Engine - # Minimum price floor (prevents prices from going too low) - min-price: 1.0 +The following values are hardcoded engine constants and **cannot be changed in `config.yml`**: - # Auto-update interval in minutes - update-interval: 15 -``` +| Constant | Value | Description | +|----------|-------|-------------| +| Starting price | 100.0 | Default price for any item not yet traded | +| Demand factor | ±2 % per unit | Price rises 2 % per unit bought, falls 2 % per unit sold | +| Random volatility | ±10 % per trade | Random noise added on top of demand factor | +| Price floor | 1.0 | Prices cannot drop below this value | +| Auto-save interval | 5 minutes | How often market prices are persisted to disk | ### Price Calculation Formula -The stock market calculates price fluctuations using the following formula: ```text -New price = max(min-price, current price × (1 + (demand × demand-multiplier) + random volatility)) +Per-unit change = (±2% demand factor) + random(−10%, +10%) +New price after N units = current × (1 + per-unit-change)^N, floor at 1.0 ``` --- diff --git a/docs/shops/pricing/stock-market.md b/docs/shops/pricing/stock-market.md index 69f1240..62f9ce3 100644 --- a/docs/shops/pricing/stock-market.md +++ b/docs/shops/pricing/stock-market.md @@ -12,30 +12,62 @@ The Stock Market system introduces a global economic layer where item prices flu --- -## ⚙️ Available Settings Overview +## ⚙️ Configuration -Configure the stock market behavior in your `config.yml`. +Configure the stock market in the `stock:` section of `config.yml`. Volatility and demand constants are **built into the engine** and are not exposed as config options. ```yaml -stock-market: +stock: + # Master switch. Set to false to disable all /stock commands and GUIs. enabled: true - # Price volatility range (-10% to +10% by default) - volatility-min: -0.10 - volatility-max: 0.10 - # Demand multiplier for price changes - demand-multiplier: 0.02 - # Minimum price floor (prevents prices from going too low) - min-price: 1.0 - # Auto-update interval in minutes - update-interval: 15 + + # Per-player cooldown between trades in milliseconds. 0 = no cooldown. + cooldown-millis: 10000 + + # Materials that cannot be traded on the stock market. + blocked: + - BEDROCK + - COMMAND_BLOCK + + # Custom items to expose in the stock market. + # 'display' – label shown in the all-stocks GUI listing. + # 'base-price' – reference price shown in the all-stocks listing; + # the live trading price evolves independently. + overrides: + - id: "DIAMOND" + display: "&bDiamond" + base-price: 100.0 + + # Optional: group items under named category tabs in the GUI. + categories: + gems: + - DIAMOND + - EMERALD ``` +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| `enabled` | boolean | `true` | Enable/disable all stock features | +| `cooldown-millis` | integer | `0` | Milliseconds between player trades | +| `blocked` | list | `[]` | Materials blocked from trading | +| `overrides` | list | `[]` | Custom display names and reference prices | +| `categories` | map | `{}` | Named category → list of item groupings | + --- ## 🧮 Price Calculation Formula -The engine calculates the next price point using this exact logic: +The engine applies a **per-unit multiplicative update** each time an item is bought or sold. The following constants are hardcoded and cannot be changed in `config.yml`: + +| Constant | Value | Description | +|----------|-------|-------------| +| Starting price | 100.0 | Default price for any item not yet traded | +| Demand factor | ±2 % per unit | +2 % per unit bought, −2 % per unit sold | +| Random volatility | ±10 % per trade | Random noise added on top of the demand factor | +| Price floor | 1.0 | Prices cannot drop below this value | +| Auto-save interval | 5 minutes | How often prices are written to disk | ```text -New price = max(min-price, current price × (1 + (demand × demand-multiplier) + random volatility)) +Per-unit change = (±2% demand factor) + random(−10%, +10%) +New price after N units = current × (1 + per-unit-change)^N, floor at 1.0 ``` \ No newline at end of file From 9ed3cc33e1d8d43c423b8f911a9ec7dd9fd7eec4 Mon Sep 17 00:00:00 2001 From: ez-plugins Date: Fri, 22 May 2026 05:39:41 +0200 Subject: [PATCH 3/5] feat(stock): make volatility, demand factor, min price and save interval configurable Add volatility-min, volatility-max, demand-multiplier, min-price, and update-interval as real config.yml options under the stock: section. - StockMarketConfig: reads five new keys with backward-compatible defaults - StockMarketManager: replaces static constants with instance fields; add configure() method; setPrice/updatePrice/estimateBulkTotal all use the configurable fields - StockComponent: calls configure() after construction; derives persistence interval from getSaveIntervalMinutes() - config.yml: documents and ships the five new keys with defaults - Docs: main-settings.md and stock-market.md updated to list the options as configurable (not hardcoded) - CHANGELOG: [Unreleased] Added section lists the five new options --- CHANGELOG.md | 17 ++++++--- docs/configuration/main-settings.md | 36 ++++++++++-------- docs/shops/pricing/stock-market.md | 38 ++++++++++++++----- .../ezshops/bootstrap/StockComponent.java | 11 +++++- .../ezshops/config/StockMarketConfig.java | 24 ++++++++++++ .../ezshops/stock/StockMarketManager.java | 33 +++++++++++----- src/main/resources/config.yml | 9 +++++ 7 files changed, 127 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7e564c..e85e4d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.5.6] - 2026-05-22 + +Through time we've made the stock market more stable, by doing this the documentation got outdated and configuration options that existed before got phased out. This version there was focus on updating the documentation and adding back options that got phased out in a more stable way. + +### Added +- **Configurable stock price-engine parameters** — `volatility-min`, `volatility-max`, `demand-multiplier`, `min-price`, and `update-interval` are now real `config.yml` options under the `stock:` section. The plugin reads them on startup and applies them to the price engine. All defaults match the previously hardcoded values so existing behaviour is preserved. + ### Fixed -- **`/sell` command missing from documentation** — the Quick Sell GUI command (`/sell`) is now documented in the Commands reference with its behavior and permission node. -- **`/shopadmin` command missing from documentation** — `/shopadmin [browse|market]` is now documented under Admin Commands including both the player-shops and team-market views. -- **`/teamshop market` subcommand missing from documentation** — the team P2P market subcommand is now documented in Commands, the TeamsAPI integration page, and the tab-completion list for `/teamshop`. -- **Missing permission nodes in documentation** — the following nodes were present in `plugin.yml` but absent from the Permissions reference; they are now documented: +- **Incorrect stock-market configuration documented** - `docs/configuration/main-settings.md` and `docs/shops/pricing/stock-market.md` previously documented a non-existent `stock-market:` config block. Both pages now document the real `stock:` section (`enabled`, `cooldown-millis`, `blocked`, `overrides`, `categories`). Price-engine parameters (`volatility-min`, `volatility-max`, `demand-multiplier`, `min-price`, `update-interval`) are now implemented as real config options (see Unreleased → Added). +- **`/sell` command missing from documentation** - the Quick Sell GUI command (`/sell`) is now documented in the Commands reference with its behavior and permission node. +- **`/shopadmin` command missing from documentation** - `/shopadmin [browse|market]` is now documented under Admin Commands including both the player-shops and team-market views. +- **`/teamshop market` subcommand missing from documentation** - the team P2P market subcommand is now documented in Commands, the TeamsAPI integration page, and the tab-completion list for `/teamshop`. +- **Missing permission nodes in documentation** - the following nodes were present in `plugin.yml` but absent from the Permissions reference; they are now documented: - `ezshops.shop.admin` (open `/shopadmin` GUI) - `ezshops.teamshop.market` (access team P2P market) - `ezshops.pricing.admin.set`, `ezshops.pricing.admin.disable`, `ezshops.pricing.admin.list` (granular pricing-admin nodes) @@ -21,7 +29,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - **`on-sell` commands not executing for `item-type: COMMAND` items** — `sell()` no longer checks or removes physical items from the player's inventory when the item's delivery type is `COMMAND`. Previously the transaction exited early with "insufficient items" because the player had no material to hand over, preventing sell commands from running at all. - **`on-sell execute-as` overridden by `on-buy execute-as`** — `ShopPricingManager` now tracks `execute-as` independently for the `on-buy` and `on-sell` blocks. Previously a single shared flag meant that setting `on-buy: execute-as: player` would silently override `on-sell: execute-as: console`, causing sell commands to run as the player instead of the console. -- **Incorrect stock-market configuration documented** — `docs/configuration/main-settings.md` and `docs/shops/pricing/stock-market.md` previously documented a non-existent `stock-market:` config block with keys `volatility-min`, `volatility-max`, `demand-multiplier`, `min-price`, and `update-interval`. None of these keys are read by the plugin. Both pages now document the real `stock:` section (`enabled`, `cooldown-millis`, `blocked`, `overrides`, `categories`) and clarify that volatility/demand constants are hardcoded engine values, not configurable options. ### Added - **Code coverage reporting** — JaCoCo is now configured in the Maven build (`jacoco-maven-plugin 0.8.12`). Coverage reports (`jacoco.xml`) are generated on every `mvn test` run and uploaded to Codecov by the CI workflow for both unit-test and feature-test jobs. diff --git a/docs/configuration/main-settings.md b/docs/configuration/main-settings.md index 7185a32..ef1bc49 100644 --- a/docs/configuration/main-settings.md +++ b/docs/configuration/main-settings.md @@ -86,7 +86,7 @@ player-shops: ### Configuration (`config.yml`) -The stock section controls which items are tradeable and how the GUI presents them. Volatility and demand parameters are internal engine constants and are **not configurable** via `config.yml`. +The stock section controls which items are tradeable and how the GUI presents them. Price-engine parameters (volatility, demand factor, minimum price, and save interval) are configurable via `config.yml`. ```yaml stock: @@ -96,6 +96,19 @@ stock: # Per-player cooldown between trades in milliseconds. 0 = no cooldown. cooldown-millis: 10000 + # Price volatility range: random noise applied per trade (fraction of price). + volatility-min: -0.10 + volatility-max: 0.10 + + # Demand multiplier: fraction of price change per unit bought (+) or sold (-). + demand-multiplier: 0.02 + + # Minimum price floor: prices cannot drop below this value. + min-price: 1.0 + + # How often (in minutes) market prices are saved to disk. + update-interval: 5 + # Materials that cannot be traded on the stock market. blocked: - BEDROCK @@ -120,26 +133,19 @@ stock: |-----|------|---------|-------------| | `enabled` | boolean | `true` | Enable/disable all stock features | | `cooldown-millis` | integer | `0` | Milliseconds between player trades | +| `volatility-min` | decimal | `-0.10` | Lower bound of random volatility per trade (-10 %) | +| `volatility-max` | decimal | `0.10` | Upper bound of random volatility per trade (+10 %) | +| `demand-multiplier` | decimal | `0.02` | Price change per unit traded (2 % per unit) | +| `min-price` | decimal | `1.0` | Absolute price floor | +| `update-interval` | integer | `5` | Minutes between automatic price saves to disk | | `blocked` | list | `[]` | Materials blocked from trading | | `overrides` | list | `[]` | Custom items with display names and reference prices | | `categories` | map | `{}` | Named category → list of item groupings | -### Internal Price Engine - -The following values are hardcoded engine constants and **cannot be changed in `config.yml`**: - -| Constant | Value | Description | -|----------|-------|-------------| -| Starting price | 100.0 | Default price for any item not yet traded | -| Demand factor | ±2 % per unit | Price rises 2 % per unit bought, falls 2 % per unit sold | -| Random volatility | ±10 % per trade | Random noise added on top of demand factor | -| Price floor | 1.0 | Prices cannot drop below this value | -| Auto-save interval | 5 minutes | How often market prices are persisted to disk | - ### Price Calculation Formula ```text -Per-unit change = (±2% demand factor) + random(−10%, +10%) -New price after N units = current × (1 + per-unit-change)^N, floor at 1.0 +Per-unit change = (±demand-multiplier) + random(volatility-min, volatility-max) +New price after N units = current × (1 + per-unit-change)^N, floor at min-price ``` --- diff --git a/docs/shops/pricing/stock-market.md b/docs/shops/pricing/stock-market.md index 62f9ce3..33bb021 100644 --- a/docs/shops/pricing/stock-market.md +++ b/docs/shops/pricing/stock-market.md @@ -14,7 +14,7 @@ The Stock Market system introduces a global economic layer where item prices flu ## ⚙️ Configuration -Configure the stock market in the `stock:` section of `config.yml`. Volatility and demand constants are **built into the engine** and are not exposed as config options. +Configure the stock market in the `stock:` section of `config.yml`. All price-engine parameters are fully configurable. ```yaml stock: @@ -24,6 +24,19 @@ stock: # Per-player cooldown between trades in milliseconds. 0 = no cooldown. cooldown-millis: 10000 + # Price volatility range: random noise applied per trade (fraction of price). + volatility-min: -0.10 + volatility-max: 0.10 + + # Demand multiplier: fraction of price change per unit bought (+) or sold (-). + demand-multiplier: 0.02 + + # Minimum price floor: prices cannot drop below this value. + min-price: 1.0 + + # How often (in minutes) market prices are saved to disk. + update-interval: 5 + # Materials that cannot be traded on the stock market. blocked: - BEDROCK @@ -49,6 +62,11 @@ stock: |-----|------|---------|-------------| | `enabled` | boolean | `true` | Enable/disable all stock features | | `cooldown-millis` | integer | `0` | Milliseconds between player trades | +| `volatility-min` | decimal | `-0.10` | Lower bound of random volatility per trade (-10 %) | +| `volatility-max` | decimal | `0.10` | Upper bound of random volatility per trade (+10 %) | +| `demand-multiplier` | decimal | `0.02` | Price change per unit traded (2 % per unit) | +| `min-price` | decimal | `1.0` | Absolute price floor | +| `update-interval` | integer | `5` | Minutes between automatic price saves to disk | | `blocked` | list | `[]` | Materials blocked from trading | | `overrides` | list | `[]` | Custom display names and reference prices | | `categories` | map | `{}` | Named category → list of item groupings | @@ -57,17 +75,17 @@ stock: ## 🧮 Price Calculation Formula -The engine applies a **per-unit multiplicative update** each time an item is bought or sold. The following constants are hardcoded and cannot be changed in `config.yml`: +The engine applies a **per-unit multiplicative update** each time an item is bought or sold. -| Constant | Value | Description | -|----------|-------|-------------| +| Value | Default | Description | +|-------|---------|-------------| | Starting price | 100.0 | Default price for any item not yet traded | -| Demand factor | ±2 % per unit | +2 % per unit bought, −2 % per unit sold | -| Random volatility | ±10 % per trade | Random noise added on top of the demand factor | -| Price floor | 1.0 | Prices cannot drop below this value | -| Auto-save interval | 5 minutes | How often prices are written to disk | +| Demand factor | ±2 % per unit | `demand-multiplier` — +2 % per unit bought, −2 % per unit sold | +| Random volatility | ±10 % per trade | Drawn from `[volatility-min, volatility-max]` | +| Price floor | 1.0 | `min-price` — prices cannot drop below this value | +| Auto-save interval | 5 minutes | `update-interval` — how often prices are written to disk | ```text -Per-unit change = (±2% demand factor) + random(−10%, +10%) -New price after N units = current × (1 + per-unit-change)^N, floor at 1.0 +Per-unit change = (±demand-multiplier) + random(volatility-min, volatility-max) +New price after N units = current × (1 + per-unit-change)^N, floor at min-price ``` \ No newline at end of file diff --git a/src/main/java/com/skyblockexp/ezshops/bootstrap/StockComponent.java b/src/main/java/com/skyblockexp/ezshops/bootstrap/StockComponent.java index 59b022a..3a7d2d6 100644 --- a/src/main/java/com/skyblockexp/ezshops/bootstrap/StockComponent.java +++ b/src/main/java/com/skyblockexp/ezshops/bootstrap/StockComponent.java @@ -45,9 +45,16 @@ public void enable(EzShopsPlugin plugin) { StockMarketRepository repository = new YmlStockMarketRepository(plugin.getDataFolder()); this.frozenStore = new StockMarketFrozenStore(repository); this.stockMarketManager = new StockMarketManager(); + this.stockMarketManager.configure( + stockMarketConfig.getVolatilityMin(), + stockMarketConfig.getVolatilityMax(), + stockMarketConfig.getDemandFactor(), + stockMarketConfig.getMinPrice() + ); this.stockMarketManager.setStockMarketRepository(repository); - // Enable async periodic persistence (every 5 minutes = 6000 ticks) - this.stockMarketManager.enablePersistence(plugin, 6000L); + // Enable async periodic persistence using the configured interval. + long saveIntervalTicks = stockMarketConfig.getSaveIntervalMinutes() * 60L * 20L; + this.stockMarketManager.enablePersistence(plugin, saveIntervalTicks); this.cooldownMillis = config.getConfigurationSection("stock") != null ? config.getLong("stock.cooldown-millis", 0L) : 0L; registerCommand("stock", new StockCommand(plugin, stockMarketManager, cooldownMillis, stockMarketConfig, frozenStore)); registerCommand("stockadmin", new StockAdminCommand(stockMarketManager, frozenStore, stockMarketConfig)); diff --git a/src/main/java/com/skyblockexp/ezshops/config/StockMarketConfig.java b/src/main/java/com/skyblockexp/ezshops/config/StockMarketConfig.java index 91eb05e..13f3ea5 100644 --- a/src/main/java/com/skyblockexp/ezshops/config/StockMarketConfig.java +++ b/src/main/java/com/skyblockexp/ezshops/config/StockMarketConfig.java @@ -9,6 +9,13 @@ public class StockMarketConfig { private final Set blocked; private final Map overrides; + // Price-engine parameters (configurable, defaults match original hardcoded values) + private final double volatilityMin; + private final double volatilityMax; + private final double demandFactor; + private final double minPrice; + private final int saveIntervalMinutes; + public StockMarketConfig(FileConfiguration config) { this.blocked = new HashSet<>(); this.overrides = new HashMap<>(); @@ -41,9 +48,26 @@ public StockMarketConfig(FileConfiguration config) { } } } + this.volatilityMin = stockSection.getDouble("volatility-min", -0.10); + this.volatilityMax = stockSection.getDouble("volatility-max", 0.10); + this.demandFactor = stockSection.getDouble("demand-multiplier", 0.02); + this.minPrice = stockSection.getDouble("min-price", 1.0); + this.saveIntervalMinutes = stockSection.getInt("update-interval", 5); + } else { + this.volatilityMin = -0.10; + this.volatilityMax = 0.10; + this.demandFactor = 0.02; + this.minPrice = 1.0; + this.saveIntervalMinutes = 5; } } + public double getVolatilityMin() { return volatilityMin; } + public double getVolatilityMax() { return volatilityMax; } + public double getDemandFactor() { return demandFactor; } + public double getMinPrice() { return minPrice; } + public int getSaveIntervalMinutes(){ return saveIntervalMinutes; } + public boolean isBlocked(String id) { return blocked.contains(id.toUpperCase(Locale.ROOT)); } diff --git a/src/main/java/com/skyblockexp/ezshops/stock/StockMarketManager.java b/src/main/java/com/skyblockexp/ezshops/stock/StockMarketManager.java index b6c73af..74f5049 100644 --- a/src/main/java/com/skyblockexp/ezshops/stock/StockMarketManager.java +++ b/src/main/java/com/skyblockexp/ezshops/stock/StockMarketManager.java @@ -25,12 +25,27 @@ public class StockMarketManager { private final Map prices = new HashMap<>(); private final Random random = new Random(); private static final double BASE_PRICE = 100.0; - private static final double MAX_CHANGE = 0.10; - // per-unit deterministic demand factor (matches previous aggregated 0.02 per unit) - private static final double PER_UNIT_DEMAND_FACTOR = 0.02; + // Engine parameters – defaults match original hardcoded values; override via configure(). + private double volatilityMin = -0.10; + private double volatilityMax = 0.10; + private double demandFactor = 0.02; + private double minPrice = 1.0; private StockMarketRepository stockMarketRepository; private final StockHistoryManager historyManager = new StockHistoryManager(); + /** + * Apply configurable price-engine parameters. + * Call this before {@link #enablePersistence} so the loaded prices are + * immediately governed by the configured floor. + */ + public void configure(double volatilityMin, double volatilityMax, + double demandFactor, double minPrice) { + this.volatilityMin = volatilityMin; + this.volatilityMax = volatilityMax; + this.demandFactor = Math.max(0.0, demandFactor); + this.minPrice = Math.max(0.0, minPrice); + } + // Persistence private TaskHandle saveTask; private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); @@ -116,12 +131,12 @@ public void updatePrice(String productId, int demand) { return; } // Compute a single random component for the entire bulk operation (preserves similar randomness scale) - double randomComponent = (random.nextDouble() * 2 - 1) * MAX_CHANGE; + double randomComponent = volatilityMin + random.nextDouble() * (volatilityMax - volatilityMin); // per-unit change (positive for buys, negative for sells) plus shared random - double perUnitChange = (demand > 0 ? PER_UNIT_DEMAND_FACTOR : -PER_UNIT_DEMAND_FACTOR) + randomComponent; + double perUnitChange = (demand > 0 ? demandFactor : -demandFactor) + randomComponent; int steps = Math.abs(demand); for (int i = 0; i < steps; i++) { - current = Math.max(1.0, current * (1.0 + perUnitChange)); + current = Math.max(minPrice, current * (1.0 + perUnitChange)); } prices.put(productId, current); historyManager.recordPrice(productId, current); @@ -156,15 +171,15 @@ public double estimateBulkTotal(String productId, int amount, ShopTransactionTyp boolean isBuy = type == ShopTransactionType.BUY; for (int i = 0; i < amount; i++) { total += sim; - double change = isBuy ? PER_UNIT_DEMAND_FACTOR : -PER_UNIT_DEMAND_FACTOR; - sim = Math.max(1.0, sim * (1.0 + change)); + double change = isBuy ? demandFactor : -demandFactor; + sim = Math.max(minPrice, sim * (1.0 + change)); } return total; } public void setPrice(String productId, double price) { - double p = Math.max(1.0, price); + double p = Math.max(minPrice, price); lock.writeLock().lock(); try { prices.put(productId, p); diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 6dbeb15..a67e837 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -137,6 +137,15 @@ stock: # Use /stock overview to see all available stocks and their prices. enabled: true cooldown-millis: 10000 + # Price volatility range: random noise added per trade (as a fraction of price). + volatility-min: -0.10 + volatility-max: 0.10 + # Demand multiplier: fraction of price applied per unit bought (+) or sold (-). + demand-multiplier: 0.02 + # Minimum price floor: prices cannot drop below this value. + min-price: 1.0 + # How often (in minutes) market prices are saved to disk. + update-interval: 5 # List of item names (Material) to block from stock trading blocked: - BEDROCK From 949af98e5fa0a1fb309df40ce4c88498915f410c Mon Sep 17 00:00:00 2001 From: ez-plugins Date: Fri, 22 May 2026 05:40:45 +0200 Subject: [PATCH 4/5] chore: bump version to 2.5.6 --- CHANGELOG.md | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e85e4d7..7d8ba68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Through time we've made the stock market more stable, by doing this the documentation got outdated and configuration options that existed before got phased out. This version there was focus on updating the documentation and adding back options that got phased out in a more stable way. ### Added -- **Configurable stock price-engine parameters** — `volatility-min`, `volatility-max`, `demand-multiplier`, `min-price`, and `update-interval` are now real `config.yml` options under the `stock:` section. The plugin reads them on startup and applies them to the price engine. All defaults match the previously hardcoded values so existing behaviour is preserved. +- **Configurable stock price-engine parameters** - `volatility-min`, `volatility-max`, `demand-multiplier`, `min-price`, and `update-interval` are now real `config.yml` options under the `stock:` section. The plugin reads them on startup and applies them to the price engine. All defaults match the previously hardcoded values so existing behaviour is preserved. ### Fixed - **Incorrect stock-market configuration documented** - `docs/configuration/main-settings.md` and `docs/shops/pricing/stock-market.md` previously documented a non-existent `stock-market:` config block. Both pages now document the real `stock:` section (`enabled`, `cooldown-millis`, `blocked`, `overrides`, `categories`). Price-engine parameters (`volatility-min`, `volatility-max`, `demand-multiplier`, `min-price`, `update-interval`) are now implemented as real config options (see Unreleased → Added). diff --git a/pom.xml b/pom.xml index d04aa0b..2c587f9 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.skyblockexp ezshops - 2.5.5 + 2.5.6 EzShops Plugin Standalone plugin providing the Skyblock shop command and sign shops. jar From 86f491b64fb27b0883fc62dada2c38168ac92df2 Mon Sep 17 00:00:00 2001 From: ez-plugins Date: Fri, 22 May 2026 05:44:22 +0200 Subject: [PATCH 5/5] ci: skip Discord notification for codecov-commenter comments --- .github/workflows/discord-issues.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/discord-issues.yml b/.github/workflows/discord-issues.yml index 83ee205..bac3231 100644 --- a/.github/workflows/discord-issues.yml +++ b/.github/workflows/discord-issues.yml @@ -9,6 +9,7 @@ on: jobs: notify: runs-on: ubuntu-latest + if: "!(github.event_name == 'issue_comment' && github.event.comment.user.login == 'codecov-commenter')" steps: - name: Send issue or comment to Discord