Skip to content

EmakiStorage ​

EmakiStorage 是 Emaki 系列的 GUI 仓库模块. 你可以把物品存进一个按槽位分格的仓库界面, 每个槽位能放下的数量远超原版堆叠上限, 而且存进去的物品在组件, 附魔, PDC (物品上由插件写入的隐藏数据) 这些额外数据上全部原样保留

仓库界面由 gui/storage_gui.yml 模板描述; 容量由 config.yml, 权限档位, 命令授予和付费购买共同决定; 其他插件还能通过 CoreLib 动作, 公开 API 和 Bukkit 事件来存取物品或读取仓库信息

基本信息 ​

项目值
模块版本1.1.5
API Jaremaki-storage-api-1.1.3.jar
主类emaki.jiuwu.craft.storage.EmakiStoragePlugin
主命令/emakistorage
别名/estorage
硬依赖EmakiCoreLib
软依赖PlaceholderAPI
描述符基线api-version: "26.1", folia-supported: true
JavaJava 25
主要权限emakistorage.use (默认 true), emakistorage.unlock.purchase (默认 true), emakistorage.autopickup (默认 true), emakistorage.reload (op), emakistorage.debug (op), emakistorage.admin (op)

功能页面 ​

页面内容
命令与权限/emakistorage, /estorage 的子命令, 权限节点与示例
配置详解config.yml 的 unlock, behavior, search, persistence, logging 等键
容量与缩容capacity 段, 四来源容量, 三级 stackLimit 与缩容溢出策略
界面与展示gui 与 display 段, gui/storage_gui.yml 模板结构与点击处理规则
付费扩容档位unlock_costs.yml 的档位, 逐格计价与支付顺序
CoreLib 动作EmakiStorage 注册到 CoreLib ActionRegistry 的 5 个动作
API 与集成第三方插件通过 emaki-storage-api 调用仓库的方式与结果模型
事件 APIStorageDepositEvent, StorageWithdrawEvent, StorageUnlockEvent, StorageBatchEvent 四个 Bukkit 事件
PlaceholderAPI 占位符emakistorage 扩展的 10 个占位符与求值线程约束

核心能力 ​

  • 单个槽位能存的量很大: 一个槽位只放一种物品, 数量上限来自三级 stackLimit (单槽上限, 分条目级, 玩家级, 配置级三层), 而不是原版的 64 或 99 堆叠
  • 物品原样保留: 判断两个物品是否相同, 用的是完整的 ItemStack#equals 比较 (组件, 附魔, 自定义 PDC 都参与比对), 没有另外引入哈希签名层, 所以材质相同但组件不同的两个物品不会被并成一条
  • 槽位数量是容量的唯一来源: 有效槽位 effectiveSlots 由 base_slots, 权限档位, 命令授予, 付费购买这四部分相加后再受上限限制算出, 页数再由槽位数推出来 (ceil(effectiveSlots / 每页格数)), 不会反过来先定页数再算槽位
  • 两种存入方式走同一套逻辑: 在展示区任意槽位点击 (光标上带着物品) 和点固定投入口 deposit_slot, 这两条路共用同一套存取逻辑, 容量检查, 过滤, 事件和流水记录都只有一份
  • 占用率用 1–99 显示: display.amount_mode: percent 时, 用槽位物品的 amount 字段在 1–99 之间表示占用百分比, 精确数量写进 lore. max_stack_size 组件本身就只接受 1–99, 所以不需要任何数据包技巧
  • 搜索与排序: 搜索是子串匹配, 永远不会去编译正则表达式; 排序是一个明确的"整理"操作, 会重新写下条目的先后顺序
  • 缩容不丢数据: 你把容量调小导致存的物品超出上限时, 有四种处理策略, 它们都不会造成无法恢复的数据丢失, 也没有提供 drop / delete
  • 流水日志只写不读: 插件的业务逻辑从不读取日志文件; ADMIN_SET, ADMIN_GIVE, ADMIN_CLEAR 这三类管理员操作一定会被记录

存档设计 ​

主题实现
条目数据条目数据存在一个二进制文件 storage.dat 里, 文件头魔数是 EMSTOR, 当前格式版本 2. 文件末尾还带一段预留 (reservation) 记录, 让 API 的预留能跨重启保留下来
写入策略每次保存都把整个文件重写, 不是往后追加. 所以每次存完文件都是紧凑的, 也就不用压实工具, 不用统计垃圾比例, 不用修复悬空的 id
写入顺序先在同一个目录写一个 .tmp, 再读回来核对记录条数, 最后一步才整体替换. 如果写入失败, 旧文件保留不动, 并把 tmp 删掉
逐条目分帧每条记录单独打包, 用 Paper 的 ItemStack#serializeAsBytes() / deserializeBytes(byte[]), 记录里带上 DataVersion, 读取时会经过 DataFixer 升级
损坏隔离解不开的记录被单独写到 corrupt/<uuid>-<时间戳>.dat, 剩下的记录照常载入
可手改数据meta.yml (文本 YAML, version: 1.0.0) 保存 player_name, granted_slots, purchased_slots, default_stack_limit, sort_mode, auto_pickup 这些可以直接手改的数据; 物品本身还是二进制, 因为 YAML 表达不了一个完整的 ItemStack

文件结构 ​

服务器跑起来后, EmakiStorage 主要用到下面这些文件:

text
plugins/EmakiStorage/
├─ config.yml
├─ unlock_costs.yml
├─ gui/
│  └─ storage_gui.yml
├─ lang/
│  ├─ zh_CN.yml
│  └─ en_US.yml
├─ data/
│  └─ <uuid>/
│     ├─ storage.dat
│     └─ meta.yml
├─ logs/
│  └─ <uuid>/
│     └─ <yyyy-MM-dd>.log
├─ corrupt/
└─ exports/
路径说明
config.yml主配置, 管界面, 容量, 扩容缩容, 展示, 存取行为, 搜索, 持久化和日志
unlock_costs.yml付费扩容的价格档位. 属于业务数据文件, 只在文件缺失时释放一次; 升级版本时不会覆盖管理员改过的内容
gui/storage_gui.yml仓库界面模板. 功能区只需要配 offset, 实际槽位号由 gui.storage_rows 换算出来
lang/*.yml界面, 命令, 扩容和控制台提示的文案
data/<uuid>/storage.dat这个玩家的条目数据, 二进制, 每次保存整份重写
data/<uuid>/meta.yml这个玩家的槽位数, stackLimit, 排序模式和自动拾取开关等可以手改的元数据
logs/<uuid>/<yyyy-MM-dd>.log存取流水, 每个玩家一个目录, 每天一个文件, 只写不读
corrupt/解不开的记录放在这里隔离
exports//estorage export 生成的可读 YAML 导出文件

快速开始 ​

  1. 把 EmakiCoreLib-*.jar 与 EmakiStorage-*.jar 放入服务器 plugins/
  2. 不要把 emaki-storage-api-*.jar 放进服务器的 plugins/ 目录; API Jar 只是给开发者写代码时用的编译依赖
  3. 首次启动后检查 plugins/EmakiStorage/config.yml 与 unlock_costs.yml
  4. 打开自己的仓库:
text
/estorage
  1. 修改配置后重载配置, 语言与 GUI 模板:
text
/estorage reload
  1. 给在线玩家追加槽位并查看容量拆解:
text
/estorage slot grant Steve 10
/estorage info Steve

使用提示 ​

  • gui.deposit_feedback 建议不要设为 none. 两种存入方式混用时, 物品按条目顺序摆放, 可能落到别的页上; 没有反馈玩家会以为物品消失了
  • emakistorage.slots.<n> 与 emakistorage.stacklimit.<n> 都取所有生效档位里的最大值, 并且不支持通配
  • 想缩减容量的话, 把 capacity.base_slots 调小不会吃掉玩家已经授予或购买的槽位, 这三个来源各自单独存档
  • 缩容溢出的状态不会存进文件, 它是根据容量和占用推算出来的; 目前只有刷新 GUI 视图时才会去算它, 所以在打开界面, 以及每次存取, 翻页, 排序, 搜索之后刷新时会重新算一次
  • config.yml 注释里出现的 /storage 是历史写法, 命令并未注册该别名, 见 命令与权限
  • 会改动数据的那些管理命令要求目标玩家在线, 见 命令与权限