会话与物品安全
ItemMaster 的会话 = 一次 GUI 打开到销毁之间的完整上下文(槽位内物品、变量、Toggle 状态、锁定槽位)。会话的生命周期决定了玩家放进 GUI 的物品在异常情况下是否会丢,以及延迟动画能否在玩家关窗/下线后继续跑。
session_timeout 配置
yaml
# 秒
session_timeout: 0 # 关闭 GUI 立即销毁会话(非后台模式)
session_timeout: 100 # 关闭 GUI 后保持 100 秒(后台模式)
session_timeout: -1 # 永不超时(后台模式)| 值 | 行为 |
|---|---|
0 | 非后台模式:玩家关闭 GUI 的瞬间销毁会话、归还物品、中断 delay 动画 |
>0 | 后台模式:关闭 GUI 后会话继续运行 N 秒(delay 继续推进),玩家可重新打开查看进度,超时自动销毁并归还 |
-1 | 永不超时:适合"离线制造"类场景,配合时间戳判断完成状态 |
物品归还机制
| 场景 | 处理方式 | 状态 |
|---|---|---|
| 玩家正常关闭 GUI | 直接归还到背包 | ✅ |
| 后台模式超时 | 在线时归还,离线时保存到文件 | ✅ |
| 玩家下线(后台模式) | 保存到文件,上线后恢复会话 | ✅ |
| 玩家下线(非后台模式) | 尝试归还,失败则保存到文件 | ✅ |
服务器正常关闭(stop) | 保存所有有物品的会话到文件 | ✅ |
服务器崩溃(kill -9 / OOM) | ⚠️ 物品可能丢失 | 未处理 |
服务器正常关闭会调用
onDisable,插件会把所有会话落盘。崩溃则无法执行该钩子,建议关键物品用vault.store存到物品库。
离线会话
存储位置
plugins/ItemMaster/offline-sessions/{玩家UUID}.yml存储内容
- GUI ID(恢复时重新打开对应 GUI)
- 所有槽位物品(Base64 序列化)
- 变量快照(基本类型 + ItemStack)
- Toggle 开关状态
- 锁定槽位列表
- 保存时间戳
过期规则
- 使用配方 / 全局
session_timeout。 - 非后台模式(
session_timeout: 0)保存时默认设为 1 小时过期(避免死留文件)。 -1表示永不过期。
后台模式特性
- 玩家关闭 GUI 后,会话继续运行(
delay任务继续倒计时)。 - 玩家可以重新打开 GUI 查看进度。
- 超时后自动销毁会话、归还物品。
- 玩家下线后脚本继续执行,但跳过需要玩家在线的动作(
effect.message/item.give等,详见 效果动作 - 玩家离线行为)。
锁定槽位与物品安全
slot.lock() 锁定的槽位物品不会在关闭 GUI 时归还。这是转盘 / 展示类动画的关键机制——不会因为玩家一关窗就把所有奖品都发出去。
yaml
execute: |
# 展示槽位全部锁定
slot.lock("s1,s2,s3,s4,s5,s6,s7,s8,result,spin")
gui.lock()
# ... 动画 ...
# 只解锁中奖槽位:关闭 GUI 时只会归还这一个
slot.unlock("result,spin")
gui.unlock()closeable: false vs gui.lock()
| 方式 | 生效时机 | 典型用途 |
|---|---|---|
顶层 closeable: false | 整个 GUI 生命周期默认锁定 | 关键流程不允许中途 ESC |
gui.lock() | 按需动态锁定(动画开始前调用) | 仅动画期间锁定 |
两者都可以用 gui.unlock() 解除。gui.lock() 后必须在某个时机 gui.unlock(),否则玩家永远关不掉 GUI。
离线制造系统模式
目标:玩家提交材料后,10 分钟后可来取成品,中间可以下线。
yaml
session_timeout: -1 # 永不超时
variables: |
完成时间 = @input.nbt("制造完成时间") ?: 0
当前时间 = time()
剩余秒 = max(0, 完成时间 - 当前时间)
已完成 = 当前时间 >= 完成时间 && 完成时间 > 0
execute: |
if !已完成 {
# 提交阶段
item.take(@material, 1)
装备 = @input
装备.nbt.set("制造完成时间", time() + 600) # 10 分钟后
slot.set("input", 装备)
effect.message("&a材料已提交,10 分钟后回来领取")
} else {
# 领取阶段
成品 = item("mm:LegendaryWeapon")
item.give(成品)
item.clear(@input)
effect.console("broadcast &6{player.name} 的传说武器锻造完成!")
}要点:
time()取 Unix 秒级时间戳,存入物品 NBT。effect.console在玩家离线时仍会执行,所以最终发放放 console 命令最稳妥。session_timeout: -1让玩家下线期间也能继续推进(尽管此例主要靠 NBT 时间戳判断,不依赖 delay)。
跨服 / BungeeCord / Velocity
- 会话仅存在于当前服务器。
- 玩家切服会触发正常下线流程,物品会被保存到当前服的离线会话文件。
- 现版本不支持跨服会话同步,已在更新计划中。
- 需要跨服传物品时用 Vault 物品存储 + 数据库 / Redis 自行同步 ID。
其他边缘情况
服务器崩溃
onDisable 不会被调用,内存中的物品丢失。缓解:
- 重要物品先
vault.store存库。 - 让玩家少在 GUI 里挂大量物品。
物品序列化失败
IM 使用 BukkitObjectOutputStream,以下情况可能失败:
- 模组物品(非原版 Minecraft 物品)。
- 跨 MC 版本不兼容(例如 1.12 保存的物品在 1.20 读取)。
- 某些插件的自定义 NBT 序列化器未注册。
原版物品和大多数 MM / NI 物品都能正常序列化。