付费扩容档位
unlock_costs.yml 定义 GUI 内付费扩容的价格档位。这是独立的业务数据文件:release_default_data 只在文件缺失时释放一次,版本升级不会覆盖管理员已有内容。
文件名由 config.yml 的 unlock.cost_file 指定,默认 unlock_costs.yml;是否开放购买由 unlock.purchase_enabled 与 emakistorage.unlock.purchase 权限共同决定,见配置详解。
unlock_costs.yml 结构
tiers:
- count_range: "1-9"
currency:
type: vault
amount: 1000
- count_range: "10-19"
item:
source: "diamond"
amount: 16
- count_range: "20-49"
currency:
type: vault
amount: 50000
item:
source: "netherite_ingot"
amount: 4
fallback:
currency:
type: vault
amount: "100000 * 1.05 ^ (%count% - 50)"
max_amount: 1000000000
batch:
enabled: true
options: [1, 5, 10, 64]| 字段 | 类型 | 说明 |
|---|---|---|
tiers[].count_range | string | 该档覆盖的「第几格」区间,如 1-9。越靠前的档优先。 |
tiers[].currency.type | enum | 货币后端:vault / excellent。 |
tiers[].currency.amount | number 或 string | 金额。字符串走 CoreLib ExpressionEngine,可用 %count%。 |
tiers[].item.source | string | CoreLib ItemSource token。 |
tiers[].item.amount | int | 所需物品数量。 |
fallback.currency | section | 超出所有 tiers 后每一格的价格。 |
fallback.max_amount | number | 单格价格上限。必需项。 |
batch.enabled | boolean | 是否开放批量购买。false 时只提供 1 格选项。 |
batch.options | list | 批量购买的档位选项。 |
逐格计价
批量购买按逐格计价再求和结算,不是「单价 × 数量」。玩家购买第 N 格时,用 N 落在哪个 count_range 决定该格价格;N 从 purchasedSlots + 1 开始递增。若按当前单价乘以数量,玩家可以在最便宜的档一次买下大批槽位,把本该属于昂贵档的格子锁定成低价。
%count% 在表达式中表示「即将购买的第几格」。
max_amount 护栏
fallback.max_amount 是必需项:缺失或非正数时兜底档整体失效,超出 tiers 的格子将不可购买(回报 no_price_defined)。它的作用是防止指数表达式溢出成 Infinity。
当某一格的计算价格超过 max_amount 时,实现明确拒绝本次报价(price_over_cap),而不是静默钳到上限:管理员的公式产出了越过自己声明的护栏的价格,这次购买就不该继续。
支付顺序与逆序补偿
购买流程:报价 → 触发可取消的 StorageUnlockEvent → 扣货币 → 扣物品 → 增加 purchasedSlots。
任何一步在扣款之后失败,都会按逆序补偿:先按倒序回滚已应用的物品扣除计划,再退还货币,然后才返回失败。purchasedSlots 在补偿路径上从未被修改过。
失败采用 fail-closed:没有匹配档位且没有可用兜底档时拒绝购买,而不是当成免费。