Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/discord-issues.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,23 @@ 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
- **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)

## [2.5.5] - 2026-05-18

### Fixed
Expand Down
52 changes: 51 additions & 1 deletion docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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`
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down
65 changes: 50 additions & 15 deletions docs/configuration/main-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,39 +78,74 @@ 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. Price-engine parameters (volatility, demand factor, minimum price, and save interval) are 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)
# 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 for price changes
# Demand multiplier: fraction of price change per unit bought (+) or sold (-).
demand-multiplier: 0.02

# Minimum price floor (prevents prices from going too low)
# Minimum price floor: prices cannot drop below this value.
min-price: 1.0

# Auto-update interval in minutes
update-interval: 15
# How often (in minutes) market prices are saved to disk.
update-interval: 5

# 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 |
| `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 |

### 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 = (±demand-multiplier) + random(volatility-min, volatility-max)
New price after N units = current × (1 + per-unit-change)^N, floor at min-price
```

---
Expand Down
10 changes: 10 additions & 0 deletions docs/integrations/teams-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |

Expand Down
3 changes: 3 additions & 0 deletions docs/permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) |

Expand Down
70 changes: 60 additions & 10 deletions docs/shops/pricing/stock-market.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,80 @@ 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`. All price-engine parameters are fully configurable.

```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)

# 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 for price changes

# Demand multiplier: fraction of price change per unit bought (+) or sold (-).
demand-multiplier: 0.02
# Minimum price floor (prevents prices from going too low)

# Minimum price floor: prices cannot drop below this value.
min-price: 1.0
# Auto-update interval in minutes
update-interval: 15

# How often (in minutes) market prices are saved to disk.
update-interval: 5

# 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 |
| `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 |

---

## 🧮 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.

| Value | Default | Description |
|-------|---------|-------------|
| Starting price | 100.0 | Default price for any item not yet traded |
| 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
New price = max(min-price, current price × (1 + (demand × demand-multiplier) + random volatility))
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
```
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>com.skyblockexp</groupId>
<artifactId>ezshops</artifactId>
<version>2.5.5</version>
<version>2.5.6</version>
<name>EzShops Plugin</name>
<description>Standalone plugin providing the Skyblock shop command and sign shops.</description>
<packaging>jar</packaging>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
Loading
Loading