跳到主要内容

商店模式

Malkuth 当前支持 9 种商店模式。你可以通过 shop/*.yml 顶层的 mode 字段选择模式;mode 缺省或留空时按 normal 处理,非空但无法识别时会跳过加载该商店配置。

模式一览

模式值页面currencygoods适合场景
normal普通商店必填必填固定商品商城、礼包、权限/脚本商品、物物交换
limit_time / limit-time限时商店必填必填限时活动、节日特卖、限时抢购
player玩家自制商店必填不读取玩家自己上架商品、摆摊、玩家商店方块
recycle回收商店可省略必填服务器回收物品、按规则发放奖励
recycle_shop / recycle-shop回收兑换商店必填必填复用回收会话但要求货币上下文的回收入口
auction拍卖行必填不读取玩家竞价拍卖、出价记录、拍品结算
global_market / global-market全球市场必填不读取全服寄售市场、搜索、排序、筛选、均价展示
global_request / global-request / request全球求购必填不读取玩家发布求购、供货者提交物品、求购者审核成交
decompose分解商店必填必填拖入物品后按回收规则分解成奖励

通用配置

以下字段是商店配置的通用入口,具体是否生效要看对应模式页面:

字段说明
mode商店模式,支持上表中的小写值、短横线别名,以及同名枚举写法如 LIMIT_TIME
enable商店开关,支持 Kether 表达式,例如 truefalseperm vip.shop
display商店显示名称,用于搜索、比价、市场等展示场景;不配置时通常显示配置文件名
item_cache.enabled是否在重载商店时预构建无玩家上下文的商品快照,用来减少大型配置商品商店打开时的首屏空白
on_disable商店关闭或 enable 不通过时执行的 Kether 动作
currency商店货币配置;除 recycle 外,其余模式都必须能解析出货币
title商店标题;普通分页标题常用 {page} / {max-page},限时商店还可用 {countdown}
layoutGUI 字符布局;g 通常表示商品槽位,其他字符对应 icons
icons布局中非 g 字符的图标与动作定义
confirm.open普通购买链路的确认界面开关,可被商品级 goods.*.confirm.open 覆盖
item_count_limit购买链路的商店级商品数量限制
trade_amount_limit购买链路的商店级交易金额限制
target_inventory购买发货或回收扣物的目标背包源;常用值:vanilla / legendwarehouse / soulringx
refresh随机商品刷新配置,最常用于 normal / limit_time 这类读取 goods 的购买型商店
schedule定时活动折扣配置;命中窗口后会给当前商店价格乘上 discount
decomposedecompose 模式专用配置,其他模式不会读取这部分

更多商品字段见 商品配置,货币写法见 货币系统

商品展示缓存

item_cache.enabled 主要适合固定配置商品较多的 normallimit_time 商店。开启后,插件会在加载或重载 shop/ 时构建一份不依赖具体玩家的商品 ItemStack 快照;玩家打开普通 / 限时商店时会先用这份快照占位,再异步回填限购、余额、权限、PAPI 等玩家态展示。

item_cache:
enabled: false

当前源码不会给 playerauctionglobal_marketglobal_requestdecompose 这几类数据库或交互型商店构建商品缓存;recycle / recycle_shop 虽然没有被缓存构建排除,但当前回收 UI 不读取 ShopItemCache 首屏缓存,所以不要把它当作回收性能开关使用。

商品展示变量

普通购买型商店的 template 与商品展示通常可以使用:

变量说明
{name}商品名称
{lore}商品 lore(全部)
{lore_N}商品第 N 组 lore
{price}商品价格;纯物品购买模式下会显示所需物品描述
{has_currency}玩家持有货币数量;纯物品购买模式下会显示已满足的物品数量
{limit} / {limit_remaining}当前商品可购买剩余次数
{amount}本次购买数量
{buy_mode}当前购买模式文本(货币 / 物品 / 混合)
{buy_items} / {buy_items_N}当前商品需要的物品描述;N 从 1 开始
{currency_type}当前生效货币标识;商品级货币覆盖后也会跟着变化
{condition_lore}条件展示列表;仅 normal / limit_time 的商品模板支持,需配合 template.condition_lore 配置
{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}商品级全局限制状态

回收模式还有额外的回收占位符,详见 回收商店回收系统

条件 Lore 展示

如果你已经用 goods.*.condition 做 VIP 折扣、权限价或条件展示覆写,可以在普通 / 限时商店的 template.lore 中单独写一行 {condition_lore},让玩家直接看到每条条件是否满足。这个功能只改变商品 Lore 展示,不改变真实购买判定;真实价格、展示覆写和购买覆写仍按 condition.priority 取最高优先级命中的规则。

下面节选自默认 shop/normal_full.yml

goods:
1:
id: diamond_sword_basic
amount: 1
price: 500
condition:
- id: vip-discount
priority: 100
when: 'perm vip'
text: '拥有 VIP 权限,享受 420 金币折扣价'
buy:
price: 420

template:
lore:
- '{lore}'
- '&e价格: &f{price} 金币'
- '{condition_lore}'
condition_lore:
source: goods_condition
pass: '&d条件 &a[满足] &f{text}'
fail: '&d条件 &c[未满足] &f{text}'
loading: '&d条件 &7[检查中] &f{text}'
hide_passed: false
hide_failed: false
empty: ''
字段说明
template.condition_lore.sourceinline 只读取本节点 rulesgoods_condition 读取 goods.*.conditionboth 合并两者
pass / fail / loading条件满足、未满足、异步检查中时的单行格式;可用 {id}{text}{status}{priority} 和普通商品模板变量
hide_passed / hide_failed是否隐藏已满足或未满足的条件行
empty没有可展示条件时的兜底行;留空则不显示
all_passed.enabled / all_passed.text所有条件满足时额外显示的汇总行
rulessource: inlineboth 时读取,支持 map 或 list 写法;每条规则需要 when / if

source: goods_condition 时,条件文案按 textlore_textcondition_lore 的顺序读取;如果都没写,会退回显示规则 id

随机商品

随机商品不是单独的商店模式,而是给读取 goods 的商店槽位追加随机结果。默认示例在 shop/random_daily.ymlshop/random_weekly_cron.yml

# shop/random_daily.yml
mode: NORMAL
currency: vault
title: '&6&l每日随机商店 &7{page}/{max-page}'

refresh:
window: DAILY

goods:
random_weapons:
random_pool:
count: 7
unique: true
items:
- id: 'diamond_sword_basic'
weight: 30
- id: 'diamond_sword_enhanced'
weight: 15
- id: 'mm_legendary_sword'
weight: 5
random_price:
min: 100
max: 500
amount: 1
字段说明
refresh.window刷新窗口:DAILY / WEEKLY / MONTHLY / PERMANENT / CUSTOM / CRON
refresh.custom_hoursCUSTOM 窗口的自定义小时数
refresh.cron_expressionCRON 窗口的 Cron 表达式
random_pool.count从候选池中抽取的数量
random_pool.unique是否去重,默认 true
random_pool.items[].id候选商品 ID,对应 goods/*.yml
random_pool.items[].weight权重,越大越容易被抽中
random_pool.items[].random_price候选商品级随机价格,优先于槽位级价格
random_price.min / random_price.max槽位级随机价格范围
random_price.decimal是否允许小数,默认 false

管理员可用 /malkuth random refresh <商店ID> [玩家] 手动刷新随机结果,用 /malkuth random info <商店ID> <玩家> 查看指定玩家的随机结果。

random_id 简写

如果不需要从池中抽取多个商品,只想让一个槽位随机显示一个商品,可以省略 count / unique 包装,直接写 random_id

# shop/random_weekly_cron.yml
refresh:
window: CRON
cron_expression: "0 0 18 ? * FRI"

goods:
weekend_special:
random_id:
- id: 'diamond_sword_enhanced'
weight: 10
- id: 'mm_legendary_sword'
weight: 10
random_price:
min: 3000
max: 8000
amount: 1

random_idrandom_pool 共用一个解析入口,区别在于:random_id 直接写列表(每个元素为 id + weight),固定只抽 1 个且无需去重参数。槽位级 random_price 与候选商品级 random_price 同样生效。

定时活动折扣

定时活动折扣不是独立商店模式,而是给任意商店追加一个按 Cron 生效的价格乘数。默认示例在 shop/schedule_example.yml

schedule:
cron: '0 0 20 * * ?'
duration_hours: 2
discount: 0.8
字段说明
schedule.cronQuartz Cron 表达式;命中时段开始后商店进入活动窗口
schedule.duration_hours每次活动持续小时数;必须大于 0
schedule.discount活动窗口内的价格乘数;1.0 不打折,0.8 表示八折

当前实现里,discount 会在 ShopGoods.getEffectivePrice() 的最后阶段乘到有效价格上,所以它会叠加在基础价格、条件价、随机价、动态价之后统一生效。

选择建议