Shop Layout Provider (Developer Guide)
The Shop Layout Provider system allows developers to customize how shop signs are structured and rendered.
As of 6.2.0.11, shop layouts are no longer hardcoded. Instead, they are controlled through the IShopLayoutProvider interface, which allows:
- Custom sign line ordering
- Per-shop-type layouts (BUYING, SELLING, FROZEN, or custom types)
- Dynamic layout resolution
- Complete replacement of the default layout system
This guide explains how to access, override, and implement a custom layout provider.
Overview
The layout provider controls how shop sign lines are arranged.
Instead of hardcoding:
Line 1 → Header
Line 2 → Trading info
Line 3 → Item
Line 4 → Price
The system now allows full customization through:
- Config-based layout definitions
- Custom
IShopLayoutProviderimplementations - Runtime overrides
The default implementation is SimpleShopLayoutProvider.
Accessing the Layout Provider
To retrieve the current layout provider:
ShopManager manager = api.getShopManager();
IShopLayoutProvider provider = manager.shopLayoutProvider();
To replace the layout provider with a custom one:
ShopManager manager = api.getShopManager();
manager.shopLayoutProvider(new MyCustomLayoutProvider());
Once replaced, your implementation will control how shop layouts are generated.
Understanding IShopLayoutProvider
IShopLayoutProvider defines how layout data is retrieved and resolved.
It is responsible for:
- Providing layout configuration per shop type
- Determining which layout to use
- Supplying formatted line keys
- Allowing future extensibility
The default implementation reads layout definitions from configuration and applies them based on shop type.
Default Layout Example
The default configuration-based layout looks like this:
layout:
BUYING:
line1: "header"
line2: "trading"
line3: "item"
line4: "price"
FROZEN:
line1: "header"
line2: "trading"
line3: "item"
line4: "price"
SELLING:
line1: "header"
line2: "trading"
line3: "item"
line4: "price"
Each line maps to a layout component key.
An empty string ("") leaves that line blank.
The layout provider resolves these keys into actual sign content.
Creating a Custom Layout Provider
To override the default behavior, implement IShopLayoutProvider.
Example minimal implementation:
public class MyCustomLayoutProvider implements IShopLayoutProvider {
@Override
public ShopLayout provideLayout(Shop shop) {
ShopLayout layout = new ShopLayout();
layout.setLine(1, "Custom Header");
layout.setLine(2, "Owner: " + shop.getOwnerName());
layout.setLine(3, shop.getItem().getType().name());
layout.setLine(4, "$" + shop.getPrice());
return layout;
}
}
Then register it:
ShopManager manager = api.getShopManager();
manager.shopLayoutProvider(new MyCustomLayoutProvider());
Your layout will now control all shop sign formatting.
Per-Type Layout Control
Because the system no longer depends on the legacy ShopType enum, layouts can support:
- Built-in types (BUYING, SELLING, FROZEN)
- Custom shop types
- Addon-defined shop types
Your provider can determine layout logic dynamically:
@Override
public ShopLayout provideLayout(Shop shop) {
if (shop.getShopType().key().equals("FROZEN")) {
return frozenLayout(shop);
}
return defaultLayout(shop);
}
This allows conditional formatting, localization adjustments, or special event styling.
Advanced Customization
A custom layout provider can:
- Pull data from external plugins
- Inject dynamic placeholders
- Modify layout per player
- Apply formatting logic
- Override color schemes
- Apply region-based formatting
- Implement animated sign systems
Because the layout provider is resolved at runtime, you can compute layouts dynamically instead of reading static config.
Replacing vs Extending the Default Provider
You have two options:
Replace entirely:
manager.shopLayoutProvider(new MyCustomLayoutProvider());
Or extend the default implementation:
public class ExtendedLayoutProvider extends SimpleShopLayoutProvider {
@Override
public ShopLayout provideLayout(Shop shop) {
ShopLayout base = super.provideLayout(shop);
base.setLine(1, "[Extended] " + base.getLine(1));
return base;
}
}
Extending is safer if you only want to modify small portions.
Best Practices
Keep layout generation lightweight.
Avoid:
- Heavy database queries
- Blocking IO
- Complex network calls
Layout generation runs during shop updates and sign refresh events.
Always handle null values safely.
Ensure layout line count does not exceed sign limits.
Avoid hardcoding shop type names unless necessary.
If your layout supports custom shop types, validate their keys properly.
Example Full Registration
public class LayoutAddon {
public void register(QuickShopAPI api) {
ShopManager manager = api.getShopManager();
manager.shopLayoutProvider(new MyCustomLayoutProvider());
}
}
Conclusion
The Shop Layout Provider system allows complete control over shop sign structure.
By accessing:
ShopManager manager = api.getShopManager();
And setting:
manager.shopLayoutProvider(new MyCustomLayoutProvider());
You can redefine how every shop is displayed on the server.
This system was designed to support:
- Extensibility
- Custom shop types
- Localization
- Dynamic formatting
- Future expansion