Skip to content

会话与物品安全

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 物品都能正常序列化。

TQ Minecraft Server Plugin Docs