交易限制
Malkuth 提供交易限制系统,支持商店级别和商品级别双层限制。当前源码中,购买链路已完整接入 item_count_limit / trade_amount_limit 校验;回收链路使用独立的 recycle.shop_level_limit 与 recycle/*.yml 中 limitation 字段进行限制校验。
限制架构
双层限制
- 商店级别限制: 限制玩家在整个商店的交易行为(跨所有商品统计)
- 商品级别限制: 限制玩家对特定商品的交易行为
双维度控制
- 玩家维度 (
player): 单个玩家的限制(每个玩家独立计算) - 全局维度 (
global): 所有玩家共享的限制(全服统计)
双类型限制
- 购买限制 (
item_count_limit/trade_amount_limit): 限制购买数量或购买总金额 - 回收限制 (
recycle.shop_level_limit/recycle/*.yml limitation): 限制回收次数或回收收益
时间窗口
支持 6 种时间窗口类型:
| 类型 | 说明 | 配置示例 |
|---|---|---|
daily | 每日重置 | type: daily |
weekly | 每周重置 | type: weekly |
monthly | 每月重置 | type: monthly |
permanent | 永久限制(不重置) | type: permanent |
custom | 自定义小时数(购买链路可用;回收链路当前仅解析字段) | type: customcustom_hours: 48 |
cron | Cron 表达式(购买链路可用;回收链路当前仅解析字段) | type: croncron_expression: "0 0 0 ? * MON" |
商店级别限制
在商店配置文件中添加限制,限制玩家在整个商店的交易行为:
# shops/weapon_shop.yml
# 商品数量限制(跨所有商品统计)
item_count_limit:
player: # 玩家维度
enabled: true
type: daily # 每日重置
max_count: 100 # 每人每日最多购买 100 件商品
global: # 全局维度
enabled: true
type: weekly # 每周重置
max_count: 10000 # 全服每周最多售出 10000 件商品
# 交易金额限制(跨所有商品统计)
trade_amount_limit:
player:
enabled: true
type: daily
max_amount: 50000 # 每人每日最多消费 50000
global:
enabled: false
效果: 玩家在该商店购买任何商品时,都会累计到商店级别的限制中。
商品级别限制
在 goods/ 文件夹的商品配置文件中添加限制,限制玩家对特定商品的交易:
# goods/weapons.yml
diamond_sword_limited:
give:
mode: source
display:
material: "diamond sword"
name: '&6限量钻石剑'
lore:
- '&7限量供应的钻石剑'
- '&c每人每日限购 5 把'
- '&c全服每日限量 100 把'
- ''
- '&e价格: {price} 金币'
# 商品级别 - 数量限制
item_count_limit:
player:
enabled: true
type: daily
max_count: 5 # 每人每日最多购买 5 把
global:
enabled: true
type: daily
max_count: 100 # 全服每日最多售出 100 把
# 商品级别 - 金额限制
trade_amount_limit:
player:
enabled: true
type: weekly
max_amount: 50000 # 每周在该商品上最多消费 50000
global:
enabled: false
然后在商店配置中引用该商品:
# shops/weapon_shop.yml
goods:
1:
id: diamond_sword_limited # 引用 goods/weapons.yml 中的商品
amount: 1
price: 1000
Kether 表达式支持
限制值支持 Kether 表达式,实现基于权限、等级等条件的动态限制:
基于权限的动态限制
在 goods/ 文件夹的商品配置中:
# goods/vip_items.yml
vip_special_item:
give:
mode: source
display:
material: "nether star"
name: '&6VIP 特供商品'
item_count_limit:
player:
enabled: true
type: daily
# VIP 玩家 200,普通玩家 100
max_count: "{{ perm vip ? 200 : 100 }}"
多级权限
在 goods/ 文件夹的商品配置中:
# goods/vip_items.yml
premium_item:
give:
mode: source
display:
material: "diamond"
name: '&b高级商品'
trade_amount_limit:
player:
enabled: true
type: weekly
# 金牌 VIP: 500000,银牌 VIP: 300000,普通: 100000
max_amount: "{{ check perm vip.gold then 500000 else check perm vip.silver then 300000 else 100000 }}"
基于玩家数据
在 goods/ 文件夹的商品配置中:
# goods/level_items.yml
level_based_item:
give:
mode: source
display:
material: "experience bottle"
name: '&a等级商品'
item_count_limit:
player:
enabled: true
type: daily
# 根据玩家等级动态调整
max_count: "{{ player level * 10 }}"
注意:
- 表达式在每次检查时实时求值
- 无效表达式会在控制台输出警告并使用默认值
- 支持所有 Kether 语法和变量
Cron 表达式
使用 Cron 表达式实现复杂的时间规则:
Cron 格式
格式: 秒 分 时 日 月 星期 [年]
字段说明:
秒: 0-59
分: 0-59
时: 0-23
日: 1-31
月: 1-12 或 JAN-DEC
星期: 1-7 或 SUN-SAT (1=周日)
年: 可选,1970-2099
特殊字符:
* : 任意值
? : 不指定(仅用于日和星期)
- : 范围(如 1-5)
, : 列举(如 1,3,5)
/ : 增量(如 0/15 表示每 15 分钟)
常用示例
在 goods/ 文件夹的商品配置中:
# goods/time_limited.yml
# 每天 00:00 重置
daily_reset_item:
give:
mode: source
display:
material: "gold ingot"
name: '&e每日商品'
item_count_limit:
player:
enabled: true
type: cron
max_count: 50
cron_expression: "0 0 0 * * ?"
# 每周一 00:00 重置
weekly_item:
give:
mode: source
display:
material: "emerald"
name: '&a周一刷新'
item_count_limit:
player:
enabled: true
type: cron
max_count: 100
cron_expression: "0 0 0 ? * MON"
# 每周一、三、五 00:00 重置
multi_day_item:
give:
mode: source
display:
material: "diamond"
name: '&b多日刷新'
item_count_limit:
player:
enabled: true
type: cron
max_count: 50
cron_expression: "0 0 0 ? * MON,WED,FRI"
# 每月 1 号和 15 号 00:00 重置
monthly_item:
give:
mode: source
display:
material: "nether star"
name: '&d月度商品'
trade_amount_limit:
player:
enabled: true
type: cron
max_amount: 100000
cron_expression: "0 0 0 1,15 * ?"
# 每 6 小时重置
frequent_item:
give:
mode: source
display:
material: "iron ingot"
name: '&7频繁刷新'
item_count_limit:
player:
enabled: true
type: cron
max_count: 20
cron_expression: "0 0 0/6 * * ?"
# 每月最后一天 23:59 重置
month_end_item:
give:
mode: source
display:
material: "gold block"
name: '&6月末特供'
trade_amount_limit:
player:
enabled: true
type: cron
max_amount: 500000
cron_expression: "0 59 23 L * ?"
自定义时间窗口
使用 custom 类型指定任意小时数,在 goods/ 文件夹的商品配置中:
# goods/custom_time.yml
two_day_item:
give:
mode: source
display:
material: "redstone"
name: '&c48小时商品'
item_count_limit:
player:
enabled: true
type: custom
max_count: 50
custom_hours: 48 # 每 48 小时重置
three_day_item:
give:
mode: source
display:
material: "lapis lazuli"
name: '&93天商品'
trade_amount_limit:
player:
enabled: true
type: custom
max_amount: 100000
custom_hours: 72 # 每 3 天重置
回收限制
回收限制使用独立字段,不走购买链路的 item_count_limit / trade_amount_limit。
# shop/recycle_shop.yml
mode: 'recycle'
recycle:
# 商店级回收限制(整店维度)
shop_level_limit:
count_limit:
player:
enabled: true
type: daily
max_count: 64
currency_limit:
player:
enabled: true
type: daily
max_amount: 10000
# recycle/recycle1.yml
diamond_rule:
check:
material:
match: "DIAMOND"
rewards:
- type: currency
amount: 100
# 规则级回收限制(单规则维度)
limitation:
window: daily
player: 32
global: 500
回收限制不写入 {prefix}_trade_limit,而是走回收统计链路({prefix}_recycle_stats / {prefix}_global_recycle_stats)。custom 与 cron 目前在回收链路中会被解析,但统计窗口尚未实现对应计算逻辑。
限制检查顺序
购买时按以下顺序检查(任一失败则拒绝交易):
- 商店级别 - 商品数量限制(玩家维度)
- 商店级别 - 商品数量限制(全局维度)
- 商店级别 - 交易金额限制(玩家维度)
- 商店级别 - 交易金额限制(全局维度)
- 商品级别 - 商品数量限制(玩家维度)
- 商品级别 - 商品数量限制(全局维度)
- 商品级别 - 交易金额限制(玩家维度)
- 商品级别 - 交易金额限制(全局维度)
模板占位符
在商店的 template 配置中,你可以使用以下占位符显示限制详情:
基础占位符
| 占位符 | 说明 |
|---|---|
{limit} / {limit_remaining} | 剩余限购数量(所有限制中的最小值;未配置任何限制时返回 999) |
商店级别限制占位符
| 占位符 | 说明 |
|---|---|
{shop_player_current} | 商店级别 - 个人已购买数量 |
{shop_player_max} | 商店级别 - 个人最大限制 |
{shop_player_remaining} | 商店级别 - 个人剩余限制 |
{shop_global_current} | 商店级别 - 全局已购买数量 |
{shop_global_max} | 商店级别 - 全局最大限制 |
{shop_global_remaining} | 商店级别 - 全局剩余限制 |
商品级别限制占位符
| 占位符 | 说明 |
|---|---|
{goods_player_current} | 商品级别 - 个人已购买数量 |
{goods_player_max} | 商品级别 - 个人最大限制 |
{goods_player_remaining} | 商品级别 - 个人剩余限制 |
{goods_global_current} | 商品级别 - 全局已购买数量 |
{goods_global_max} | 商品级别 - 全局最大限制 |
{goods_global_remaining} | 商品级别 - 全局剩余限制 |
使用示例
在商店配置中使用这些占位符:
# shops/weapon_shop.yml
template:
name: '&f{name}'
lore:
- '{lore}'
- '&7&m―――――――――――――――'
- '&e价格: &f{price} 金币'
- '&7持有: &f{has_currency}'
- '&7剩余: &f{limit}'
- ''
- '&8商店限制: {shop_player_current}/{shop_player_max}'
- '&8商品限制: {goods_player_current}/{goods_player_max}'
效果: 玩家在查看商品时,可以看到自己在该商店和该商品上的购买进度。
注意:
- 如果某个维度的限制未启用,对应明细占位符会显示为
0;若完全未配置限购,{limit}/{limit_remaining}返回999 - 占位符会根据配置的时间窗口自动更新
- 全局限制占位符显示的是所有玩家的累计数据
提示消息
玩家触发限制时会收到相应提示:
# lang/zh_CN.yml
# 商店级别限制
limit-shop-item-count-player: '&c你已达到该商店的商品数量限制,剩余可购买: &e{0}'
limit-shop-item-count-global: '&c该商店的商品库存已达全局限制'
limit-shop-trade-amount-player: '&c你已达到该商店的交易金额限制,剩余额度: &e{0}'
limit-shop-trade-amount-global: '&c该商店的交易金额已达全局限制'
# 商品级别限制
limit-goods-item-count-player: '&c你已达到商品 &f{0} &c的购买数量限制,剩余可购买: &e{1}'
limit-goods-item-count-global: '&c商品 &f{0} &c的库存已达全局限制'
limit-goods-trade-amount-player: '&c你已达到商品 &f{0} &c的交易金额限制,剩余额度: &e{1}'
limit-goods-trade-amount-global: '&c商品 &f{0} &c的交易金额已达全局限制'
# 回收限制
limit-recycle-item-count-player: '&c你已达到回收规则 &f{0} &c的数量限制,剩余可回收: &e{1}'
limit-recycle-trade-amount-player: '&c你已达到回收规则 &f{0} &c的金额限制,剩余额度: &e{1}'
你可以在语言文件中自定义这些消息。
数据存储
购买限制数据存储在数据库表 {prefix}_trade_limit 中;回收限制使用 {prefix}_recycle_stats 与 {prefix}_global_recycle_stats 统计表。
| 字段 | 说明 |
|---|---|
limit_id | 唯一标识符 |
player_uuid | 玩家 UUID(null 表示全局) |
shop_id | 商店 ID |
goods_id | 商品 ID(null 表示商店级别) |
limit_type | 限制类型(item_count/trade_amount) |
trade_type | 交易类型(当前购买链路为 buy) |
time_window | 时间窗口类型 |
custom_hours | 自定义小时数 |
cron_expression | Cron 表达式 |
current_value | 当前累计值 |
window_start | 窗口起始时间 |
last_update | 最后更新时间 |
自动清理: 插件会定期清理 trade_limit 中过期的购买限制记录。
实战示例
限时抢购活动
# goods/limited_event.yml
limited_item:
give:
mode: source
display:
material: "nether star"
name: '&6限时抢购'
lore:
- '&7每周一刷新'
- '&c全服限量 100 个'
item_count_limit:
global:
enabled: true
type: cron
max_count: 100
cron_expression: "0 0 0 ? * MON"
然后在商店中引用:
# shops/event_shop.yml
goods:
1:
id: limited_item
amount: 1
price: 5000
VIP 特权商店
# goods/vip_goods.yml
vip_exclusive:
give:
mode: source
display:
material: "diamond block"
name: '&6VIP 专属'
# VIP 玩家每日可购买更多
item_count_limit:
player:
enabled: true
type: daily
max_count: "{{ check perm vip.gold then 200 else check perm vip.silver then 150 else 100 }}"
trade_amount_limit:
player:
enabled: true
type: daily
max_amount: "{{ perm vip ? 100000 : 50000 }}"
然后在商店中引用:
# shops/vip_shop.yml
goods:
1:
id: vip_exclusive
amount: 1
price: 1000
新手保护商店
# goods/newbie_goods.yml
newbie_kit:
give:
mode: source
display:
material: "chest"
name: '&a新手礼包'
lore:
- '&7每人仅可购买一次'
item_count_limit:
player:
enabled: true
type: permanent
max_count: 1
然后在商店中引用:
# shops/newbie_shop.yml
goods:
1:
id: newbie_kit
amount: 1
price: 100
每日任务奖励
# goods/daily_goods.yml
daily_reward:
give:
mode: source
display:
material: "diamond"
name: '&b每日奖励'
item_count_limit:
player:
enabled: true
type: daily
max_count: 3
然后在商店中引用:
# shops/daily_shop.yml
goods:
1:
id: daily_reward
amount: 1
price: 500
周末特惠
# goods/weekend_goods.yml
weekend_special:
give:
mode: source
display:
material: "gold ingot"
name: '&e周末特惠'
item_count_limit:
player:
enabled: true
type: cron
max_count: 10
cron_expression: "0 0 0 ? * SAT,SUN"
然后在商店中引用:
# shops/weekend_shop.yml
goods:
1:
id: weekend_special
amount: 1
price: 800
防刷回收限制
# shop/recycle_shop.yml
mode: 'recycle'
recycle:
shop_level_limit:
# 商店级别:每日回收收益上限
currency_limit:
player:
enabled: true
type: daily
max_amount: 50000
如果需要对特定回收物品限制,在 recycle/ 文件夹创建规则配置:
# recycle/recycle_items.yml
diamond_recycle:
check:
material:
match: "DIAMOND"
rewards:
- type: currency
amount: 100
limitation:
window: daily
player: 64 # 每日最多回收 64 个钻石
然后在回收商店中引用:
# shop/recycle_shop.yml
mode: 'recycle'
goods:
1:
id: diamond_recycle
price: "{price}"
注意事项
- 优先级: 商店级与商品级限制同时生效,最终可购买量取最小值
- 累加计算: 商店级别限制会累加所有商品的交易
- 实时检查: 每次交易前都会检查所有启用的限制
- 异步更新: 限制数据异步写入数据库,不影响交易性能
- 表达式缓存: Kether 表达式每次实时求值,不缓存结果
- Cron 验证: 无效的 Cron 表达式会在控制台输出警告
- 时区: Cron 表达式使用服务器时区
- 数据持久化: 限制数据存储在数据库中,重启后保留