跳到主要内容

交易限制

Malkuth 提供交易限制系统,支持商店级别和商品级别双层限制。当前源码中,购买链路已完整接入 item_count_limit / trade_amount_limit 校验;回收链路使用独立的 recycle.shop_level_limitrecycle/*.ymllimitation 字段进行限制校验。

限制架构

双层限制

  • 商店级别限制: 限制玩家在整个商店的交易行为(跨所有商品统计)
  • 商品级别限制: 限制玩家对特定商品的交易行为

双维度控制

  • 玩家维度 (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: custom
custom_hours: 48
cronCron 表达式(购买链路可用;回收链路当前仅解析字段)type: cron
cron_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)。customcron 目前在回收链路中会被解析,但统计窗口尚未实现对应计算逻辑。

限制检查顺序

购买时按以下顺序检查(任一失败则拒绝交易):

  1. 商店级别 - 商品数量限制(玩家维度)
  2. 商店级别 - 商品数量限制(全局维度)
  3. 商店级别 - 交易金额限制(玩家维度)
  4. 商店级别 - 交易金额限制(全局维度)
  5. 商品级别 - 商品数量限制(玩家维度)
  6. 商品级别 - 商品数量限制(全局维度)
  7. 商品级别 - 交易金额限制(玩家维度)
  8. 商品级别 - 交易金额限制(全局维度)

模板占位符

在商店的 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_expressionCron 表达式
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}"

注意事项

  1. 优先级: 商店级与商品级限制同时生效,最终可购买量取最小值
  2. 累加计算: 商店级别限制会累加所有商品的交易
  3. 实时检查: 每次交易前都会检查所有启用的限制
  4. 异步更新: 限制数据异步写入数据库,不影响交易性能
  5. 表达式缓存: Kether 表达式每次实时求值,不缓存结果
  6. Cron 验证: 无效的 Cron 表达式会在控制台输出警告
  7. 时区: Cron 表达式使用服务器时区
  8. 数据持久化: 限制数据存储在数据库中,重启后保留

下一步