Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
406317e
Add RuleSet interface to API and create an implementation. Add additi…
creatorfromhell Apr 28, 2026
d4eedd4
Remove unused imports
creatorfromhell Apr 28, 2026
bed2e2a
Remove unnecessary values in the TradeFailureReason enum.
creatorfromhell Apr 28, 2026
a6a8a5b
"Refactor and modularize core shop components: add ModernShop interfa…
creatorfromhell May 4, 2026
42bf06c
Implement request system and start filling out the ShopService implem…
creatorfromhell May 5, 2026
dc4d4ec
Implement the diff methods for the Shop component classes.
creatorfromhell May 5, 2026
3642f77
Deprecate ShopTrading and mark for removal in future(2 releases).
creatorfromhell May 5, 2026
ca92457
Deprecate shop value setting methods and mark for removal in future(2…
creatorfromhell May 5, 2026
9bc3872
Implement ShopRequest interface for shop requests
creatorfromhell May 5, 2026
441ffce
Start implementing the builders and factory for the enhanced API.
creatorfromhell May 6, 2026
4b417c2
Move shop lookups to the ShopServer interface, and deprecate the shop…
creatorfromhell May 6, 2026
50ef765
Add more implementation details for some more ideas for the updated api.
creatorfromhell May 6, 2026
714c575
Implement getters inside ShopBuilderFactory and to be in the shop pac…
creatorfromhell May 6, 2026
94c938e
Add asBuilder methods to our shop objects, and start implement retrie…
creatorfromhell May 6, 2026
d4e4dd8
Build out the builder interfaces, adjust ShopService to have generic …
creatorfromhell May 6, 2026
76a2c1d
Simplify asBuilder() to builder().
creatorfromhell May 6, 2026
f1a7bb6
Simplify asBuilder() to builder().
creatorfromhell May 6, 2026
757cde7
Implement the default builder implementations, include constructors f…
creatorfromhell May 7, 2026
fc3c3de
Remove unused shop manager methods.
creatorfromhell May 7, 2026
0d5d5c7
Start removing reimplemented methods from ContainerShop. Implement mo…
creatorfromhell May 10, 2026
6ed482c
Reimplementing further methods into the new API objects. ORelocated S…
creatorfromhell May 10, 2026
127f37a
Add diff for shop interface, remove lombok from the new classes, rear…
creatorfromhell May 10, 2026
7a7944a
Start implementing the connections for WorldAdapter and ShopService.
creatorfromhell May 19, 2026
6b70c24
Continue to implement methods in the adapter and shop service.
creatorfromhell May 23, 2026
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
51 changes: 26 additions & 25 deletions .changelog/6.3.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ All comparisons are configured under matcher.components. Keys can be:
#### Added

* Modrinth update source support
* Uses Modrinth API for version checks
* Detects latest & latest stable release via SemVer
* Uses Modrinth API for version checks
* Detects latest & latest stable release via SemVer
* New UpdateProvider interface for extensible update sources
* New centralized UpdateManager
* 1-hour metadata cache
* Unified version comparison
* Paste output now shows the active provider
* 1-hour metadata cache
* Unified version comparison
* Paste output now shows the active provider

#### Changed

Expand All @@ -67,34 +67,30 @@ updater-source: "modrinth"

This lays the groundwork for future providers (CurseForge, Hangar, GitHub, etc.) without core rewrites.

### New: Shop Tagging System
## New: Shop Tagging System

A new player-driven tagging system has been introduced, allowing players to organize and track shops using custom tags.

**Highlights**
### Key Points
- Players can add custom tags to shops they are looking at.
- Tags support letters, underscores (`_`), and dashes (`-`).
- Tags are normalized and limited in length to ensure consistency and performance.
- Reserved system tags power built-in features such as:
- `@fav` — Favorites
- `@watch` — Watchlist
- `@avoid` — Avoided shops
- @fav - Favorites
- @watch - Watchlist
- @avoid - Avoided shops
- Tags are stored per-player per-shop, allowing each player to maintain their own organization system.

**Commands**
- `/qs tag <tag>` — Add a tag to the shop you are looking at
- `/qs tag remove <tag>` — Remove a tag from the current shop
- `/qs tag clear` — Remove all of your tags from the current shop
- `/qs tag tags` — List all tags you have created
- `/qs tag tagged <tag>` — List shops you have tagged with a specific tag
- `/qs tag purge <tag>` — Remove a tag from all shops
- `/qs favorite` - Add the current shop to your favorites
- `/qs watch` - Add the current shop to your watchlist
- `/qs avoid` - Add the current shop to your avoid list

**Performance**
- Uses a dual-index caching system (`shop → tags` and `player → tag → shops`) for fast lookups.
- Tag filtering and listing operations are now **O(1)** instead of scanning all shops.
### Commands
- /qs tag <tag> - Add a tag to the shop you are looking at
- /qs tag remove <tag> - Remove a tag from the current shop
- /qs tag clear - Remove all of your tags from the current shop
- /qs tag shops - List all tags you have created
- /qs tag tagged <tag> - List shops you have tagged with a specific tag
- /qs tag purge <tag> - Remove a tag from all shops
- /qs favorite - Add the current shop to your favorites
- /qs watch - Add the current shop to your watchlist
- /qs avoid - Add the current shop to your avoid list

## Minor Changes
- Replaced startup parameter "playerDBFindUUIDTask" with config option "uuid-lookup.allow-playerdb-uuid"
Expand Down Expand Up @@ -136,6 +132,10 @@ A new player-driven tagging system has been introduced, allowing players to orga
- Added bukkitLocation() to the Locatable interface, which returns a BukkitLocation object.
- Replaced internal calls to shop.getLocation with shop.bukkitLocation
- Split the Shop interface into different sub interfaces to improve readability and maintainability.
- The per-player-shop-sign option no longer requires ProtocolLib. (Warrior)
- - Added PriceLimiter methods to allow external plugins and addons to get Results for price limits without running any logical validations.
- Refactored PriceLimiterCheckResult into the shop.limit package
- Implemented a TradeService interface to handle the logical side of shop trading, and allow easier connection between external plugins and addons.

## Deprecation Removals
- Removed ShopType enum and methods
Expand All @@ -148,4 +148,5 @@ A new player-driven tagging system has been introduced, allowing players to orga
- Fix issue where limits configs were not working properly(thanks to YuanYuanOwO)
- Fix Folia thread violations in /qs info stock stats(thanks to GoodrichDev)
- Fix Towny Compat not working in Folia(thanks to GoodrichDev)
- Remove blocking in AbstractShopManager#registerShop to fix deadlock on Folia. (Warrior)
- Remove blocking in AbstractShopManager#registerShop to fix deadlock on Folia. (Warrior)
- Fix AbstractShopManager#getShops returning null while marked as not-null (Warrior)
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.ghostchu.quickshop.api.shop.ShopControlPanelManager;
import com.ghostchu.quickshop.api.shop.ShopItemBlackList;
import com.ghostchu.quickshop.api.shop.ShopManager;
import com.ghostchu.quickshop.api.shop.ShopPermissionManager;
import com.ghostchu.quickshop.api.shop.interaction.InteractionManager;
import com.ghostchu.quickshop.api.shop.tag.TagManager;
import com.vdurmont.semver4j.Semver;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package com.ghostchu.quickshop.api.shop;

/*
* QuickShop-Hikari
* Copyright (C) 2026 Daniel "creatorfromhell" Vidmar
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import com.ghostchu.quickshop.api.database.bean.DataRecord;
import com.ghostchu.quickshop.api.shop.builder.ShopBuilder;
import com.ghostchu.quickshop.api.shop.components.ShopInteraction;
import com.ghostchu.quickshop.api.shop.components.ShopItem;
import com.ghostchu.quickshop.api.shop.components.ShopLifecycle;
import com.ghostchu.quickshop.api.shop.components.ShopMeta;
import com.ghostchu.quickshop.api.shop.components.ShopPermission;
import com.ghostchu.quickshop.api.shop.components.ShopPrice;
import com.ghostchu.quickshop.api.shop.components.ShopTrading;
import com.ghostchu.quickshop.api.shop.service.result.ShopChangeType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.EnumSet;
import java.util.UUID;
import java.util.function.Consumer;

/**
* ShopModern
* T = price object, such as BigDecimal or Double
* S = Location object such as Bukkit's Location object
* U = Preview object such as Bukkit's Inventory object
* V = Inventory object such as InventoryWrapper
* @author creatorfromhell
* @since 6.3.0.0
*/
public interface ModernShop<T, S, U, V> extends Locatable<S> {

/**
* WARNING: This UUID will changed after plugin reload, shop reload or server restart DO NOT USE
* IT TO STORE DATA!
*
* @return Random UUID
*/
@NotNull
UUID getRuntimeRandomUniqueId();

ShopItem item();

ShopInteraction<U, V> interaction();

ShopLifecycle lifecycle();

ShopMeta meta();

ShopPermission permission();

ShopPrice<T> price();

ShopTrading trading();

/**
* Converts the current shop instance into a {@link ShopSignStorage}.
*
* This method provides a {@link ShopSignStorage} object that acts as a
* container for storing the shop's sign location data, such as world, x, y, and z coordinates.
*
* @return a {@link ShopSignStorage} instance that contains the location data of the shop's sign.
*/
ShopSignStorage asShopSignStorage();

/**
* Converts the current shop instance into a {@link DataRecord}.
*
* This method provides a {@link DataRecord} representation of the shop instance,
* encapsulating all relevant shop-related data such as item details, pricing,
* permissions, owner information, and additional metadata.
*
* @return a {@link DataRecord} instance representing the shop's data.
*/
@NotNull
DataRecord asDataRecord();

/**
* Getting ShopInfoStorage that you can use for storage the shop data
*
* @return ShopInfoStorage
*/
ShopInfoStorage asInfoStorage();

/**
* Gets the symbol link that created by InventoryWrapperManager
*
* @return InventoryWrapper
*/
@NotNull
String asSymbolLink();

/**
* Compares the current {@code ModernShop} instance with another provided instance and determines the set of
* differences between them. These differences are represented as a set of {@code ShopChangeType} values,
* where each type corresponds to a category of change (e.g., item, price, owner, etc.).
*
* @param compare The {@code ModernShop} instance to compare against. If {@code null}, the method assumes
* comparison with a non-existent or empty shop.
* @return An {@code EnumSet} of {@code ShopChangeType} values that represent the changes detected between
* the current shop instance and the provided shop. If no changes are detected, an empty set is returned.
*/
EnumSet<ShopChangeType> diff(final @Nullable ModernShop<T, S, U, V> compare);

/**
* Applies a series of changes to the current shop instance using a {@link ShopBuilder}.
* The specified consumer allows modification of the {@code ShopBuilder},
* after which the shop is rebuilt with the applied changes.
*
* <strong>NOTE: This does not apply the changes to the Shop cache or database, you'll need to utilize the
* {@link ShopService} to do that.</strong>
*
* @param changes a {@code Consumer} that defines the modifications to the {@link ShopBuilder}.
* @return a {@link ModernShop} instance with the applied modifications.
*/
default ModernShop<T, S, U, V> withChanges(final Consumer<ShopBuilder<T, S, U, V>> changes) {

final ShopBuilder<T, S, U, V> builder = builder();
changes.accept(builder);
return builder.build();
}

/**
* Converts the current shop instance into a {@link ShopBuilder} for applying modifications or rebuilding.
*
* @return a {@link ShopBuilder} instance initialized with the current shop's state, enabling further modifications.
*/
ShopBuilder<T, S, U, V> builder();
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,35 +1,19 @@
package com.ghostchu.quickshop.api.shop;

import com.ghostchu.quickshop.api.QuickShopAPI;
import com.ghostchu.quickshop.api.economy.benefit.BenefitProvider;
import com.ghostchu.quickshop.api.inventory.InventoryWrapper;
import com.ghostchu.quickshop.api.inventory.InventoryWrapperManager;
import com.ghostchu.quickshop.api.localization.text.ProxiedLocale;
import com.ghostchu.quickshop.api.obj.QUser;
import com.ghostchu.quickshop.api.shop.inventory.ShopInventory;
import com.ghostchu.quickshop.api.shop.meta.ShopDisplay;
import com.ghostchu.quickshop.api.shop.meta.ShopMeta;
import com.ghostchu.quickshop.api.shop.permission.BuiltInShopPermission;
import com.ghostchu.quickshop.api.shop.permission.BuiltInShopPermissionGroup;
import com.ghostchu.quickshop.api.shop.permission.ShopPermission;
import com.ghostchu.quickshop.api.shop.state.ShopState;
import com.ghostchu.quickshop.api.shop.trading.ShopTrading;
import net.kyori.adventure.text.Component;
import org.bukkit.Location;
import com.ghostchu.quickshop.api.shop.components.ShopDisplay;
import com.ghostchu.quickshop.api.shop.components.ShopMeta;
import com.ghostchu.quickshop.api.shop.components.ShopPermission;
import com.ghostchu.quickshop.api.shop.components.ShopTrading;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

/**
Expand Down Expand Up @@ -94,11 +78,6 @@ public interface Shop<U, L> extends Locatable<L>, ShopInventory, ShopMeta<U>, Sh
*/
boolean isValid();

/**
* Execute codes when player click the shop will did things
*/
void onClick(@NotNull Player clicker);


@Deprecated()
@ApiStatus.Internal
Expand Down
Loading