UltiRemoteBag - 远程背包
UltiRemoteBag 是 UltiTools 框架的一个模块,为 Minecraft 服务器提供虚拟云存储(远程背包)功能。玩家可以随时随地打开自己的远程背包存取物品,支持多页背包,每页固定 45 个储物格。模块内置 Vault 经济系统集成,玩家可以花钱购买更多背包页。管理员可以查看、创建、删除、清空任意玩家的背包。背包系统带有本地锁和跨服数据库占用,当所有者正在使用背包时管理员自动进入只读模式,防止数据冲突。所有操作都有音效反馈。
安装
有两种安装方式:
方式一:通过 UltiTools 插件管理器安装
在服务器控制台或游戏内输入:
/upm install UltiRemoteBag方式二:手动安装
- 安装 UltiTools-API 6.3.0 或更高版本,下载
UltiRemoteBag-2.0.0.jar文件 - 将 JAR 文件放入
plugins/UltiTools/plugins/目录 - 重启服务器
可选依赖:如果需要使用购买背包功能,请确保安装了 Vault 和一个经济插件(如 EssentialsX)。
安装完成后,配置文件会自动生成在 plugins/UltiTools/pluginConfig/UltiRemoteBag/config/remotebag.yml。
升级到 2.0.0
先备份配置和背包数据库,安装 UltiTools-API 6.3.0 或更高版本,换入 UltiRemoteBag-2.0.0.jar 后完整重启;目录身份是 ultiremotebag,模块重载/卸载名是 UltiRemoteBag。更新检查和 /upm update 使用该目录身份。共享数据库的服务器应一起升级,不混用旧版写入者。首次启动自动创建 remote_bag_claims 表,不迁移已有表。
每页固定 45 个储物格,底行是按钮,不能存取为物品。可删除从未生效且现已移除的 rows_per_page、auto_save_interval、save_on_close、gui_title 和 messages.no_permission/page_locked/bag_saved;启动时会逐键警告,文件不会自动删键。标题来自语言键 bag_name,保存确认来自 bag_saved_manually,无权访问/占用的提示来自语言文件,不再从这些配置项读取。默认配置见下文,不要把旧配置整体覆盖成新默认值。
保存、只读与数据库故障
编辑窗口在关闭时始终尝试保存,保存按钮和自己的 /bag save 也保存当前打开页;不再有固定 300 秒整份缓存回写。没有打开页时,退出/停用/手动保存不会把显示缓存写回;/bag save 可能回复已有缓存的确认,也可能说「没有需要保存的内容」,不能拿它当数据库新写入凭证。每次仅保存本窗口这一页,存储内容已被别人改动则拒绝,归还自上次读取/保存后放入的物品,放不下的掉在脚下并记录日志。last_updated 仅在实际写入时推进,不是服务器活跃信号。
数据库未响应时会明确说「此页尚未保存」,保留内存内容、保留占用并后台重试;窗口只读,本服务器重新打开可看到保留内容。仍可能写成的保存不会提前退物品;只在确认数据库拒绝时才归还,离线时等玩家再次加入本服务器。停用会保存打开页,对待写保存再尝试五秒,无法判定/未完成的连同物品记日志,不是无限等待或强行退款。详见下方已知限制;不要承诺崩溃时也绝不丢失。
同服中所有者打开时管理员只读,现有管理员编辑期间所有者会被拒绝,不会强抢正在使用的窗口。lock.timeout_seconds 只回收已结束但未释放的本地会话;持有者在线且该页仍打开时不会因空闲超时转交。跨服占用者优先,其他服务器连所有者都只读,clear/delete 也拒绝。跨服续期在后台进行,另一服连续一个超时观察到占用未续期才接手。锁超时与只读通知开关都在 /ul reload UltiRemoteBag 后生效;通知关掉不等于允许编辑。重新打开命令先关闭旧窗口,旧页先保存/释放,即使新页随后被拒绝。
只读阻止页内点击、Shift、数字键、副手交换、拖拽;自己背包内的拖拽不受此限制。刷新会清掉已经取空的格子,释放后才尝试转编辑。工具栏不能取走,双击收集/Shift 合并遇到同款按钮会被拒绝;其他插件已取消的点击/拖拽不会被强行恢复。
页码、购买和语言
自己的主页始终提供第 1 页,但管理员 list/see 只报告已存储页,没有就说没有;管理员 create 从第 1 页开始。目标先按完整名找在线玩家,再查曾经进服的玩家,首次会话也可管理,部分名字和从未进服者不匹配。新页号是现有最高页加一,不填中间空洞;只剩 1、3 时下一页是 4,价格与上限都按 4。经济关闭或不可用时,未达上限显示免费「创建新背包」按钮;经济开关在窗口打开中变化会拒绝该次点击并刷新,不会拿免费图标扣钱。上限为权限得到的页码上限,不是已有页总数。
修改配置后 /ul reload UltiRemoteBag,新 JAR 仍需重启。十三个 economy/sound/lock 注释会随服务器语言在启动或不带参数的 /ul reload 后更新,原值与手写注释保留。自定义消息请复制本模块 lang/zh.yml 为 lang/zh-myserver.yml,修改副本并在框架 plugins/UltiTools/config.yml 设置 language: zh-myserver 后完整重载;官方文件会在启动/模块重载恢复并留 .bak。卸载 /upm uninstall UltiRemoteBag 会执行有时限保存并注销命令/监听器。第三方依赖本模块服务的插件需适配已移除的旧缓存批量保存接口,不能假定旧插件无需更新。
已知限制与服主操作
共享库要保持低延迟;MySQL 应设置连接的 socketTimeout 和适中的 wait_timeout,后者负责清理失效会话;innodb_lock_wait_timeout 只限制其他会话每次等待锁,不会释放原会话。所有共享服一起升级,运行中不要手改背包表或让其他插件写入。外部/旧版写入者可能造成已取出物品复制;打开调用很慢又快速重开同页也可能丢占用并复制取出物品。整服冻结或与数据库隔离一个超时后,其他服可接手,断开后最后保存到只读之间取出的物品可能重复(最多约半个超时的窗口)。数据库故障中停机/崩溃可能丢失内存待写内容并重复已取出物品;欠离线玩家的返还也只在内存中,重启会丢失,请按日志人工补偿。数据库完全不响应时停用可能约 16 秒。MySQL 比较遵循列排序规则;JSON 后端只用于单服,不提供跨服事务。手改/外部写入的槽位 45–53 不显示,首次保存会删除,应在打开前修复;非规范槽位 +5、05、负数或越界键会警告并跳过。不要把这些情况当作无损保证。
快速开始
安装后默认每个玩家拥有 1 页背包(45 个槽位),配置最大页号为 10;默认权限限制下仍为 1 页,先授予相应页数权限再扩容。
- 输入
/bag打开背包主页 GUI,可以看到你所有的背包 - 点击某个背包图标就能打开它,像箱子一样存放物品
- 关闭背包时自动保存
未达页号上限时,有经济系统显示「购买新背包」,经济关闭/不可用时显示免费「创建新背包」:
/bag # 打开背包主页(查看所有背包)
/bag 1 # 直接打开第 1 页背包
/bag save # 手动保存背包数据命令
所有命令都只能由玩家在游戏内执行,不支持控制台。
命令别名:/bag、/remotebag、/rb、/yunbag
玩家命令
| 命令 | 说明 | 示例 | 权限 |
|---|---|---|---|
/bag | 打开背包主页 GUI | /bag | ultibag.use |
/bag <页码> | 直接打开指定页码的背包 | /bag 2 | ultibag.use |
/bag save | 手动保存背包数据 | /bag save | ultibag.use |
管理员命令
| 命令 | 说明 | 示例 | 权限 |
|---|---|---|---|
/bag see <玩家> | 查看玩家的第一页背包 | /bag see Steve | ultibag.admin.see |
/bag see <玩家> <页码> | 查看玩家的指定页背包 | /bag see Steve 3 | ultibag.admin.see |
/bag create <玩家> | 为玩家创建新的背包页 | /bag create Steve | ultibag.admin.create |
/bag delete <玩家> <页码> | 删除玩家的指定页背包 | /bag delete Steve 3 | ultibag.admin.delete |
/bag clear <玩家> <页码> | 清空玩家的指定页背包内容 | /bag clear Steve 2 | ultibag.admin.clear |
/bag list <玩家> | 列出玩家的所有背包 | /bag list Steve | ultibag.admin.list |
权限
玩家权限
| 权限节点 | 说明 | 默认 |
|---|---|---|
ultibag.use | 使用远程背包基础功能 | OP(需显式授权普通玩家) |
ultibag.pages.1 | 页数权限示例;未授权时按 default_pages | 未声明 |
ultibag.pages.2 | 拥有 2 页背包 | 无 |
ultibag.pages.3 | 拥有 3 页背包 | 无 |
ultibag.pages.N | 拥有 N 页背包(N 为正整数,最大为 max_pages) | 无 |
权限页码是向上兼容的:拥有 ultibag.pages.5 的玩家最多可以拥有 5 页背包。系统从配置的 max_pages 开始向下检查,找到玩家拥有的最高权限数字即为上限。
管理员权限
| 权限节点 | 说明 | 默认 |
|---|---|---|
ultibag.admin.see | 查看其他玩家的背包 | OP |
ultibag.admin.create | 为其他玩家创建背包 | OP |
ultibag.admin.delete | 删除其他玩家的背包 | OP |
ultibag.admin.clear | 清空其他玩家的背包 | OP |
ultibag.admin.list | 列出其他玩家的背包 | OP |
配置
配置文件路径:plugins/UltiTools/pluginConfig/UltiRemoteBag/config/remotebag.yml
# 新玩家默认的背包页数(范围:1-100)
default_pages: 1
# 玩家可拥有的最大背包页数(范围:1-100)
max_pages: 10
# 是否启用基于权限的页数限制
# 启用后,玩家的最大页数由 ultibag.pages.N 权限决定
# 未启用时,所有玩家最大页数为 max_pages
permission_based_pages: true
# 权限前缀(用于计算页数上限)
# 例如前缀为 ultibag.pages. ,拥有 ultibag.pages.5 权限的玩家上限为 5 页
permission_prefix: "ultibag.pages."
# 经济设置(需要 Vault 插件)
economy:
enabled: true # 是否启用购买背包功能
base_price: 10000 # 购买背包的基础价格(范围:0-1000000000)
price_increase_enabled: true # 是否启用价格递增
price_increase_rate: 0.1 # 价格递增比率(范围:0.0-10.0)
# 公式:basePrice * (1 + rate)^(n-1)
# 0.1 表示每多买一个背包价格增加 10%
# 例如基础价格 10000:
# 第 1 页公式值 = 10000(通常已免费提供)
# 第 2 页 = 11000
# 第 3 页 = 12100
# 音效设置
sound:
enabled: true # 是否启用音效
open: "BLOCK_CHEST_OPEN" # 打开背包的音效
close: "BLOCK_CHEST_CLOSE" # 关闭背包的音效
purchase: "ENTITY_PLAYER_LEVELUP" # 购买成功的音效
error: "ENTITY_VILLAGER_NO" # 错误提示的音效
volume: 1.0 # 音量(范围:0.0-1.0)
pitch: 1.0 # 音调(范围:0.5-2.0)
# 锁定设置(并发访问控制)
lock:
timeout_seconds: 300 # 背包锁超时时间(单位:秒,范围:10-3600)
# 仅回收异常结束且未释放的本地会话,打开中的窗口不超时转交
notify_readonly_viewers: true # 所有者开始使用背包时,是否通知正在只读查看的管理员| 配置项 | 默认值 | 约束与行为 |
|---|---|---|
default_pages | 1 | 1–100;无页数权限时的回退上限 |
max_pages | 10 | 1–100;权限扫描/非权限模式最高页号 |
permission_based_pages | true | 启用页数权限 |
permission_prefix | ultibag.pages. | 非空页数权限前缀 |
economy.enabled | true | 经济可用时付费,否则免费创建 |
economy.base_price | 10000 | 0–1000000000 基价 |
economy.price_increase_enabled | true | 是否随实际页号递增 |
economy.price_increase_rate | 0.1 | 0–10;最后向上取整 |
sound.enabled | true | 播放音效 |
sound.open | BLOCK_CHEST_OPEN | 非空打开音效名 |
sound.close | BLOCK_CHEST_CLOSE | 非空关闭音效名 |
sound.purchase | ENTITY_PLAYER_LEVELUP | 非空购买音效名 |
sound.error | ENTITY_VILLAGER_NO | 非空错误音效名 |
sound.volume | 1.0 | 0–1 音量 |
sound.pitch | 1.0 | 0.5–2 音调 |
lock.timeout_seconds | 300 | 10–3600 秒;非活跃窗口租期 |
lock.notify_readonly_viewers | true | 所有者开始使用时通知只读管理员 |
使用教程
场景一:第一次使用远程背包
你刚进入服务器,想试试远程背包功能:
- 输入
/bag打开背包主页 - 你会看到一个箱子图标,代表你的第一个背包
- 点击箱子图标进入背包,你可以像箱子一样自由存放物品
- 关闭 GUI 时物品自动保存
- 下次输入
/bag或/bag 1就能重新打开
场景二:购买更多背包
你的第一个背包快满了,想买更多空间:
- 输入
/bag打开主页 - 在最后你会看到一个「购买新背包」按钮,显示价格和你的余额
- 如果余额充足(绿色按钮),点击即可购买
- 如果余额不足(红色按钮),需要先赚够钱
价格递增按实际页号,向上取整(基础价格 10000,递增率 10%);第 1 页通常已免费提供:
- 第 1 页公式值(通常已免费提供):10000
- 第 2 页:11000
- 第 3 页:12100
- 第 4 页:13310
场景三:管理员查看玩家背包
管理员怀疑某玩家使用了作弊手段获取物品,想检查他的背包:
- 输入
/bag see 玩家名查看该玩家的第一页背包 - 或者
/bag see 玩家名 3查看指定的第 3 页 - 如果玩家此时正在使用背包,管理员会自动进入只读模式
- 只读模式下无法移动物品,GUI 左下方会显示刷新按钮
- 等玩家关闭背包后,点击刷新按钮可以切换为编辑模式
场景四:管理员管理背包
/bag list Steve # 查看 Steve 的所有背包概览(物品数量、槽位占用)
/bag create Steve # 为 Steve 创建一个新的背包页
/bag clear Steve 2 # 清空 Steve 第 2 页背包的内容(保留页面)
/bag delete Steve 3 # 完全删除 Steve 的第 3 页背包注意:如果目标背包正在被使用(处于锁定状态),删除和清空操作会被拒绝。
场景五:使用权限控制背包页数
你可以用权限插件(如 LuckPerms)精确控制不同组的玩家能拥有几页背包:
/lp group default permission set ultibag.use true
/lp group default permission set ultibag.pages.1 true # 默认组:1 页
/lp group vip permission set ultibag.pages.3 true # VIP 组:3 页
/lp group svip permission set ultibag.pages.5 true # SVIP 组:5 页
/lp group admin permission set ultibag.pages.10 true # 管理组:10 页确保配置文件中 permission_based_pages 设为 true。
锁定机制说明
UltiRemoteBag 用本地锁和数据库占用协调编辑;下列是同服规则,跨服及故障边界见升级说明:
所有者访问自己的背包:
- 背包无人使用 -> 直接进入编辑模式
- 管理员正在编辑 -> 被阻止,提示稍后再试
管理员访问他人的背包:
- 背包无人使用 -> 进入编辑模式
- 所有者正在使用 -> 自动进入只读模式
- 其他管理员正在编辑 -> 被阻止,提示稍后再试
锁超时: 默认 300 秒,仅回收已结束且未释放的本地会话;在线打开页不会被夺取。跨服占用按后台续期和连续观察超时接手,见升级说明。
退出时清理: 玩家退出服务器时,自动释放所有持有的锁、保存背包数据、清理内存缓存。
常见问题
Q: 安装后不生效怎么办?
A: 检查以下几点:
- 确保 UltiTools-API 版本是 6.3.0 或更高
- 确认 JAR 文件放在了
plugins/UltiTools/plugins/目录,不是plugins/根目录 - 安装后需要重启服务器,
/ul reload只能重载配置,不能加载新的模块 JAR - 查看服务器控制台是否有报错信息
Q: 改了配置文件没效果?
A: 修改 plugins/UltiTools/pluginConfig/UltiRemoteBag/config/remotebag.yml 后,执行 /ul reload 重载配置即可。注意不要修改 YAML 的缩进格式。
Q: 权限怎么设置?
A: 权限节点以 ultibag. 开头,未在 plugin.yml 声明的基础 use 节点默认 OP;用 LuckPerms 明确授权普通玩家。
要控制背包页数,启用 permission_based_pages 后给不同权限组设置 ultibag.pages.N:
/lp group vip permission set ultibag.pages.5 true管理员权限独立分开,可以按需分配:
/lp group admin permission set ultibag.admin.see true
/lp group admin permission set ultibag.admin.create trueQ: 购买背包按钮看不到?
A: 有几种可能:
economy.enabled: false会改为免费创建图标- 经济不可用时改显示免费创建图标,不是完全没有图标
- 你已经达到了最大背包页数上限
Q: 管理员查看背包时为什么是只读的?
A: 当背包所有者正在使用该背包时,管理员自动进入只读模式,防止两人同时编辑导致数据冲突。等所有者关闭背包后,管理员可以点击 GUI 工具栏中的「刷新」按钮尝试切换为编辑模式。
Q: 玩家退出后背包数据会丢失吗?
A: 退出会尝试保存当前编辑窗口,再释放锁和缓存。数据库未响应时改动仅在内存中待写,崩溃/停机可能丢失;没有每 300 秒缓存整写的任务。先查看保存结果和日志,按「已知限制与服主操作」处理。
Q: 数据存储在哪里?
A: 取决于 UltiTools-API 的存储后端配置(config.yml 中的设置):
- SQLite(默认):数据存储在
plugins/UltiTools/目录下的数据库文件中 - MySQL:数据存储在远程 MySQL 数据库中
- JSON:数据存储在
plugins/UltiTools/目录下的 JSON 文件中
Q: 删除和清空有什么区别?
A: /bag clear 清空背包页的内容(物品全部删除),但保留这一页。/bag delete 彻底删除这一页背包,数据库中的记录也会被移除。
更新日志
v2.0.0
新增
多台服务器共享一个数据库时,同一背包页同一时间只能在一台服务器上编辑。 打开编辑时会在新的数据库表
remote_bag_claims中占用该页; 另一台服务器占用期间,本服务器对所有者和管理员都以只读方式打开,并提示“该背包页正在另一台服务器上编辑,当前为只读模式”,管理员对该页的/bag clear与/bag delete也会被拒绝。窗口关闭、玩家退出、模块停止时释放占用。窗口打开期间,服务器在后台任务中续期占用,主线程卡顿 不会丢失占用;另一台服务器只有在自己的时钟上连续lock.timeout_seconds看到占用未续期后才能接手,因此崩溃服务器的占用会自行释放, 各服务器时钟不一致也没有影响。若占用丢失,窗口立即变为只读,并归还自上次保存以来放入的物品。单台服务器上的规则不变。该表在首次启动时创建; 现有表不变,也没有任何迁移。已知限制:不使用占用的写入者(滚动升级期间的旧版本模块、其他插件、手动编辑数据表)仍可能在窗口打开时改动该页, 该会话中取出的物品可能出现两份(见 README“已知限制”)(UltiKits/UltiRemoteBag#54)。数据库未响应时,背包页不再丢失改动。 占用和保存的每次数据库调用最多等待
lock.timeout_seconds的六分之一(主线程等待时最多两秒), 一次挂起的调用不再耽误其他页面的占用续期。续期失败或未响应时,窗口立即变为只读,窗口中显示的内容在数据库恢复响应后立即保存。保存失败或未响应时—— 关闭窗口、点击保存按钮、退出或/bag save——保留占用,改动保留在内存中,并在后台重试同一保存直到成功;期间该页在所有服务器上只读, 在本服务器重新打开会显示保留的改动。只要这次保存仍可能完成,就不归还任何物品。 只有被数据库拒绝的保存才归还放入的物品(玩家已离开时在其下次加入时归还)。模块停止时会保存仍在编辑中打开的背包页,对保留的保存再尝试五秒, 无法写入的连同物品记录在日志中。每次保存都以占用为栅栏:同一个数据库事务确认占用仍属于本服务器并写入页面,因此在另一台服务器接手之后被耽搁的保存不会写入任何内容, 报告了错误的保存只有在其记录表明未写入时才会被归还;模块停止时,该记录会在仍在数据库上运行的保存之后读取,无法及时判定的保存只记录日志、不归还。/bag clear与/bag delete会为自身操作占用该页。已知限制:保留的保存在内存中,服务器崩溃会丢失它, 与未保存的窗口相同(见 README“已知限制”)(UltiKits/UltiRemoteBag#54)。
修复
共享同一数据库的多台服务器上的背包页。 另一台共享该数据库的服务器修改过的背包页,不会再被本服务器持有的旧副本覆盖:在那里取出的物品 不会在这里重新出现(复制),在那里放入的物品也不会丢失。此前每台服务器从首次读取起就把玩家背包的每一页保存在内存里——包括管理员只是用
/bag see或/bag list查看了别人的背包之后——并整份写回:任意一次保存都会写回该玩家所有缓存页,模块停止时还会写回所有玩家的所有缓存页。 现在不再从这份副本写入任何内容。背包页只能由它自己的窗口保存,只写这一页,并且仅当存储的内容仍是窗口打开时(或上次保存时)读到的内容时 才写入;否则不写入,控制台记录Failed to update bag data,玩家看到既有的“保存未写入数据库”提示,自窗口读取该页以来放入的物品 归还给玩家(放不下的掉落在脚下;控制台记录一行列出这些物品)。创建、清空、删除背包页都以当时存储的内容 为准,且只写这一页。停止或卸载模块、玩家退出、以及没有打开页面时的/bag save都不写入任何内容(每次改动在发生时就已写入;本服务器持有你的 背包页时,/bag save仍回复“背包已手动保存!”)。管理员查看他人背包的窗口关闭或命令结束后,该玩家背包的副本即被丢弃,除非该玩家就在本服务器上 (UltiKits/UltiRemoteBag#54)。已存储的背包行在保存时已被删除,现在会报告为未保存。多个服务器共用同一个 MySQL 数据库时,若另一台服务器上的管理员恰好在本服务器保存时 删除了某一页背包,该次保存什么也没写入,
/bag save却仍回复背包已保存;现在会记录Failed to update bag data, 并且报告保存失败(UltiKits/UltiRemoteBag#50;自 UltiKits/UltiRemoteBag#54 起一次保存只写它自己的那一页)。config/remotebag.yml的注释现在跟随服务器语言。此前有十三条注释(economy.*、sound.*、lock.*各设置)只有中文,language: en的全新安装得到的文件注释是中文。现在每条注释都是一个语言文件键,框架每次写入文件时按服务器的language解析,lang/en.yml与lang/zh.yml各有英文和中文条目。已有服务器上框架在这十三个设置上写下的注释(包括旧版本写下的注释,lock.timeout_seconds上方的两种旧注释——v1.0.0 版本的和之后的中英双语注释——也在内, UltiKits/UltiRemoteBag#52)会在下次启动时、以及你修改language并执行不带参数的/ul reload后切换为服务器语言;设置值不受影响, 你自己写的注释保持原样(UltiKits/UltiTools-Reborn#611)(UltiKits/UltiRemoteBag#48)。管理员删除了玩家中间的某一页背包后(例如玩家只剩第 1、3 页),下一页图标、价格、页数上限检查和实际创建的页码现在统一使用同一个页码: 玩家现有最高页的下一页(例子中为第 4 页)。此前图标和价格按「页数」计算,显示「第 3 页」而实际创建的是第 4 页;页数上限为 3 时,购买会先扣款、 再因无法创建第 4 页而失败。现在下一页页码超过上限时不再显示该图标,因此不会为无法创建的页扣款(UltiKits/UltiRemoteBag#46)。
本模块可再次在 UltiTools-API 6.3.0 上加载。此前启动时关于「6.3.0 已移除、但仍留在
config/remotebag.yml中的设置」的警告读取的是 6.3.0 移除的配置对象,因此在 6.3.0 上模块加载时被拒绝并抛出NoSuchMethodError。现在改为向框架询问每个已移除的键是否出现在文件中 (isPresentInFile),警告内容、顺序和语言与之前相同:文件中仍有的已移除键各一条,没有则不警告,框架无法解析的文件也不警告(UltiKits/UltiRemoteBag#49)。当
economy.enabled: false或未安装经济插件时,背包列表窗口(/bag)在玩家未达页数上限时会显示绿色的「创建新背包」图标, 点击即可免费添加下一页,并提示「已创建背包 #<n>!」。此前只有付费购买图标,且经济关闭时该图标隐藏,这样的服务器无法添加背包页(UltiKits/UltiRemoteBag#25)。 窗口打开期间经济开启或关闭时,点击任一图标都不执行,提示「新背包的价格已变化,请重新查看菜单。」并刷新窗口:免费图标不会扣费,付费图标也不会免费生效。管理员命令
/bag see、/bag create、/bag delete、/bag clear、/bag list现在接受首次进服、仍在线的玩家:先按完整名字在在线玩家中查找, 其他玩家仍按「曾经进服」记录查找,与此前相同。此前这些命令只依赖 Bukkit 的「曾经进服」记录(首次会话结束前不会写入),对这样的玩家一律回复「找不到玩家」。 从未进过服的名字仍然找不到,部分名字不会匹配在线玩家(UltiKits/UltiRemoteBag#30)。/bag list <玩家>和/bag see <玩家>现在会如实报告没有已存储背包页的玩家(「没有背包」/「玩家 <玩家> 没有背包」), 包括管理员删除全部背包页之后;此前总会列出第 1 页。玩家自己的/bag始终提供第 1 页(无论是否已存储),先添加第 2 页的玩家也不会失去第 1 页; 对没有已存储背包页的玩家执行/bag create <玩家>现在会创建第 1 页(UltiKits/UltiRemoteBag#26)。只读背包页中,在空格子上按数字键或副手交换键时,现在会像其它被拒绝的移动一样提示「只读模式,无法移动物品」并播放错误音效; 此前是静默拒绝。两种情况下物品都不会移动(UltiKits/UltiRemoteBag#35)。
config/remotebag.yml中的lock.timeout_seconds现在随/ul reload UltiRemoteBag生效:锁服务每次判断能否回收锁时都读取该值。 此前该值只在模块加载时复制一次,修改后要重启服务器才生效(UltiKits/UltiRemoteBag#39)。修复:对已打开的页再次执行打开命令不再释放该页的锁,两名玩家不能再同时编辑同一页 (UltiKits/UltiRemoteBag#41)。这两个命令以及
/bag see <玩家>现在会在判断锁之前先关闭你当前打开的窗口, 因此你原先打开的页会先保存并释放,即使你请求的页随后被拒绝也是如此。language: en现在对打开一个正被他人占用的背包页面时显示的那一行生效:「该背包正被 <名字> 使用中,当前为只读模式」 「该背包正被 <名字> 使用中,请稍后再试」「该背包正被管理员 <名字> 编辑中,请稍后再试」原先在任何语言下都是写死的中文 (UltiKits/UltiRemoteBag#20)。/bag命令的描述(由/help显示)现在也跟随language。language: zh现在也对原先写死为英文的控制台日志生效:本模块的启用与禁用日志(语言文件里本来就有这两条)、 关于本版本不再读取的设置的警告、加载背包页面时跳过无法读取的槽位,以及背包写入或读取失败。它们的英文措辞不变, 只是残留的messages.page_locked的警告现在说明「页面被他人占用」的那几行来自语言文件,而不再指向本问题仍未解决。/ul reload UltiRemoteBag现在会重载本模块的配置并刷新其语言文件;此前本模块替换了框架的重载步骤,两者都不会发生, 修改config/remotebag.yml后只有重启才会生效(UltiKits/UltiRemoteBag#12)。通过
/upm uninstall UltiRemoteBag卸载本模块时,其命令现在会被真正移除,监听器也不再触发;此前两者都会保持生效,直到服务器重启。 打开的编辑窗口通过上文所述的有时限停机流程保存,而非从背包缓存写回(UltiKits/UltiRemoteBag#12、UltiKits/UltiRemoteBag#54)。以只读方式查看他人背包(该玩家正打开该页时执行
/bag see <player>)现在真的无法移动物品:普通点击、 两个方向的 Shift 点击、数字键交换与拖拽全部被取消,置灰的保存图标也不再能被拿走。此前这些操作都会生效, 而"只读模式,无法移动物品"的拒绝提示仍会照常显示,因此可以把他人背包中的物品取走并复制 (UltiKits/UltiRemoteBag#27)。编辑自己的背包页现在对所有操作都有效:页内点击、Shift 点击、数字键交换以及拖拽。此前该页自身 45 个内容 格上的每一次点击以及每一次拖拽都会被取消,物品只能通过从自己背包 Shift 点击塞进去,且无法用点击整理或取回 (UltiKits/UltiRemoteBag#27)。
背包页底行的工具栏图标(返回、刷新、保存、模式指示、填充格与关闭)在两种模式下都不再能被当作物品拿走; 点击它们仍会执行各自的功能(UltiKits/UltiRemoteBag#27)。
/bag save现在会保存放入仍处于打开状态的背包页中的物品。此前它只保存已经进入插件缓存的内容(点击该页 自身的保存按钮,或在编辑模式下关闭该页时才会进入缓存),因此放入物品后立刻执行/bag save,会收到已保存 的提示,但该物品会在下次重启后丢失(UltiKits/UltiRemoteBag#22)。当
rows_per_page被设为小于 5 时,加载背包页不再会销毁该页。读取时会按照所存槽位实际需要的大小还原, 因此从内容界面较高槽位保存的物品能够保留;此前加载会数组越界,该失败被静默吞掉,该页中的所有物品都会悄无声息地 丢失。所存页面中单个无法读取的条目(非数字、负数,或等于及超过第 54 格)现在只会被跳过, 并输出一条指明页码与键名的WARNING,而不再让整页作废。第 45 至 53 格会被读取但不会被显示: 窗口只有 45 格,因此存在该区间的物品不可见,并会在该页的第一次保存时被删除。本模块写入的任何 内容都不会落在那里——它恰好保存 45 格——因此只有手改或由外部写入的行才会带有这样的条目, 这样的行应在打开该页之前修复。界面一直显示并保存 45 个槽位,而rows_per_page已被整个移除, 因此一页的容量就是这固定的 45 格,也再没有任何地方声称其他数字;见下文Changed与Removed条目(UltiKits/UltiRemoteBag#24)。管理员不再从正打开该页的所有者手中夺取编辑锁,无论所有者空闲多久。
lock.timeout_seconds用于回收持有者会话 异常结束而未释放的锁,并非持有者在场时仍需续期的租约。此前只要配置的超时时间到达,即使所有者仍在查看该页,锁 也会被转交,而所有者随后的保存会把管理员出现之前的快照写回该行——销毁管理员放入的物品,或把管理员取出的物品 还原,导致其存在两份。现在管理员会被告知其视图为何是只读,而不再被静默降级(UltiKits/UltiRemoteBag#34)。现在无法再把工具栏按钮从背包页中取走,也无法把自己的物品并入按钮中。有两种原版操作并不局限于你点击的那一格—— 双击会把整页中相同的物品全部收拢到光标上,从自己背包 Shift 点击则会在整页中寻找可放置的位置——因此如果你手持 与某个按钮完全相同的物品,该按钮可能被取走(重新打开页面后又会重新生成,从而复制),或者你的物品会被并入按钮行 并在关闭页面时丢失。现在两者都会被拒绝,并说明原因。从按钮可被取走的旧版本升级而来的服务器上,玩家持有这类物品 是现实存在的情况(UltiKits/UltiRemoteBag#34)。
数据库写入失败的保存现在会明确报错,而不再确认保存成功。此前只要写入内存缓存成功,页面自身的保存按钮、
/bag save以及在编辑模式下关闭页面都会报告成功,即使数据库写入并未成功,因此仅存在于内存中的改动会被 报告为已保存,并在下次重启后丢失。现在也会区分「没有需要保存的内容」与「保存失败」,因为二者要求操作者 采取的行动不同(UltiKits/UltiRemoteBag#34)。只读模式下的刷新按钮现在会清空所有者在你打开视图后已取空的槽位。此前它只绘制已存储的内容,其余槽位保持原样, 因此刷新后的视图可能仍显示所有者早已取走的物品(UltiKits/UltiRemoteBag#34)。
背包页不再覆盖其他插件取消点击或拖拽的决定。此前编辑自己的背包页会清除反作弊或领地插件已设置的取消标记 (UltiKits/UltiRemoteBag#34)。
手工编辑或由外部写入的存档页中,写成
+5或05的槽位键不再静默丢弃物品:这类键会解析成与其普通形式相同的 槽位,导致两个物品中的一个无声消失。现在它会与其他无法读取的键一样被跳过并给出警告,且这些警告会带上本模块自身 的日志前缀,与它输出的其他每一行一致(UltiKits/UltiRemoteBag#34)。以只读方式查看他人背包时,完全在自己背包内进行的拖拽不再被拒绝:只读保护的是背包本身,而不是查看者自己的 背包——点击操作原本就是如此。并且被拒绝的拖拽现在会在两种模式下都说明原因,而不再静默失败 (UltiKits/UltiRemoteBag#34)。
/bag save现在只在确实完成保存时才如此报告。当没有任何缓存内容可保存时(例如刚登录且尚未打开过背包页), 它会明确说明,而不再确认一次并未发生的写入;并且在刷新了打开的页面后,不再把每个缓存页重复保存两次 (UltiKits/UltiRemoteBag#34)。lock.notify_readonly_viewers现在真的决定以只读方式查看背包的管理员,是否会在所有者重新使用时收到提示。 此前无论该设置为何都会发送该提示,lock.notify_readonly_viewers: false毫无作用;现在它会被遵守。声明的默认值 仍为true,也就是所有服务器一直在做的事,因此在运维主动关掉它之前,没有任何服务器的行为会变化。 该设置每次通知时实时读取而非在启动时缓存,因此/ul reload UltiRemoteBag无需重启即可生效。同一区块的lock.timeout_seconds现在也随重载生效,见上文(UltiKits/UltiRemoteBag#39)(UltiKits/UltiRemoteBag#19)。
变更
本版本需要 UltiTools 6.3.0 或更高版本,并在
plugin.yml中声明api-version: 630,由上一个发布版本 1.0.0 的620提升。更早的框架会在模块的启动逻辑 运行之前拒绝加载它,并给出 UltiTools 版本过旧的警告;拒绝信息以plugin.yml的name:即UltiRemoteBag指代本模块。 README 中的框架最低版本和 UltiTools-API 徽章均为 UltiTools 6.3.0+。新增或替换模块 JAR 后需要重启服务器,/ul reload不会加载它(UltiKits/UltiTools-Reborn#544)。plugin.yml现在声明identify-string: ultiremotebag,即本模块在 UltiCloud 模块目录中的条目键。框架的更新检查和/upm update会跳过未声明该键的模块,因此本模块现在会参与两者:带有同一键的更高发布版本会在启动时提示, 并可用/upm update安装(UltiKits/UltiTools-Reborn#474)。主界面的
Slots Used: x/y那一行现在报的是背包页的真实容量,装满一页显示Slots Used: 45/45。 此前在出厂设置下它显示Slots Used: 45/54——承诺了九个玩家永远填不上的槽位——而在调低了rows_per_page的服务器上显示Slots Used: 45/18,即已用数超过它声称的容量。分母原本就是那个设置; 而一页的容量一直是固定的 45 格(UltiKits/UltiRemoteBag#24)。
移除
给插件作者: 已发布的 1.0.0 中的 public 方法
RemoteBagService#setBagPage、#saveBag与#saveAllBags已移除。第三方调用者需要修改代码并重新编译:逐页调用readPage(UUID, int),保留返回的PageRead, 再将其与编辑后的物品一起传给savePage(UUID, int, ItemStack[], PageRead)。非空返回值是下次保存的基准; 返回null表示未写入任何内容,必须按保存未成功处理。这是有条件的单页写入,并非旧缓存赋值方法或批量保存的直接等价替代; 显示缓存从不整份写回数据库(UltiKits/UltiRemoteBag#54)。移除了本模块自身的"配置已重载"控制台日志行;UltiTools 6.3.0 会为每个模块输出一行重载日志。
auto_save_interval从未控制保存周期,现已移除,可从现有配置文件中删除。固定每 300 秒重写缓存的任务也已移除: 背包页由其自身窗口保存,失败或未响应的保存会按上文所述保留并在后台重试。数据库故障期间停机或崩溃仍可能丢失保留的改动, 见 README“已知限制”。last_updated现在仅在页面真正写入时推进,不再每 300 秒重写空闲缓存页;外部工具不要将其作为活跃信号 (UltiKits/UltiRemoteBag#13、UltiKits/UltiRemoteBag#23、UltiKits/UltiRemoteBag#54)。gui_title从来没有生效,现已移除;可从现有配置文件中删除。背包页的标题来自本模块的语言文件(键bag_name)—— 这也是英文服务器本来就显示英文标题、修改该设置毫无效果的原因(UltiKits/UltiRemoteBag#14)。messages.no_permission从来没有生效,现已移除;可从现有配置文件中删除。权限拒绝由 UltiTools 自身的已翻译消息给出, 本来就会跟随服务器的language设置(UltiKits/UltiRemoteBag#15)。messages.page_locked从来没有生效,现已移除;可从现有配置文件中删除。请求一个无权打开的页面由本模块的语言文件 回答(键page_out_of_range);该拒绝所报的页数上限就是按权限推导出来的那个,因此与该设置所描述的是同一情形, 且已经是翻译过的。别人正持有该页时显示的另外几条消息,现在也来自本模块的语言文件,键为bag_read_only_in_use、bag_blocked_in_use、bag_blocked_by_admin,见上文 (UltiKits/UltiRemoteBag#16、UltiKits/UltiRemoteBag#20)。messages.bag_saved从来没有生效,现已移除;可从现有配置文件中删除。/bag save的确认消息来自本模块的 语言文件(键bag_saved_manually)(UltiKits/UltiRemoteBag#17)。save_on_close从来没有生效,现已移除;可从现有配置文件中删除。编辑模式下关闭背包页总是会保存, 这就是本模块一直在做的事,因此没有任何服务器的行为会变化。曾经改为把它接线,该决定已被推翻: 设为关闭时,玩家拖进窗口的物品会被销毁——它已经离开了玩家自己的背包,而没有任何路径把它退回。 想要一个只在手动时保存的页面是合理的,已记在 UltiKits/UltiRemoteBag#37,并附上约束:任何实现在不保存时 必须把窗口内容退还给玩家(UltiKits/UltiRemoteBag#18)。rows_per_page已移除;可从现有配置文件中删除。一个背包页固定为 45 格,运维能设的任何值 都从未改变这一点。该设置是加载页面时数组尺寸的下限、新建或清空页面的尺寸,以及Slots Used那一行的分母——从来不是上限,也从来不是窗口大小,窗口在两个方向上都被代码固定为 45。 它唯一可见的效果就是那个分母,而那个分母是错的,见上文Changed条目。运维已存的任何东西 都不受影响,因为一页能装多少并未改变。想要真正可配置的容量是合理的,已记在 UltiKits/UltiRemoteBag#38(UltiKits/UltiRemoteBag#24)。若服务器的
config/remotebag.yml中仍留有上述任何一个被移除的设置,现在启动时会逐键输出一条警告, 点名模块、文件与该键,并说明该设置的职责转到了哪里。从代码中删键并不会从任何人的文件中删键, 若无此警告,改过其中一个设置的运维将完全看不到任何移除的痕迹 (UltiKits/UltiRemoteBag#13、#14、#15、#16、#17、#18、#23)。
v1.0.0 (2026-02-13)
初始版本发布。
新增:
- 新增:多页远程背包系统,每页最多 54 个槽位
- 新增:GUI 主页,显示所有背包缩略图(物品数量、槽位占用)
- 新增:编辑模式和只读模式,完整的并发访问锁定机制
- 新增:Vault 经济集成,购买新背包页,支持价格递增公式
- 新增:基于权限的背包页数控制(
ultibag.pages.N) - 新增:管理员命令(查看、创建、删除、清空、列出玩家背包)
- 新增:自动保存功能(定时保存、关闭 GUI 时保存、退出时保存)
- 新增:可配置的音效系统(打开、关闭、购买、错误音效)
- 新增:锁超时机制,防止异常退出导致死锁
- 新增:退出时自动释放所有锁并保存数据
- 新增:中文和英文双语言支持
Ling Bao