正在载入

等待时间过长时请刷新页面

编写于

最近更新

界面服务

界面服务(LinView)用于创建和管理 Bukkit 容器 GUI。它将界面布局、图标和默认动作写在资源文件中,将数据查询和点击业务写在 Java 代码中。

LinView 当前只支持 Bukkit 玩家和 9 列箱子式容器,行数建议为 1 至 6。它不依赖 NMS,而是通过 Bukkit Inventory API 创建和更新界面。

基本思想

LinView 将一个界面分为 View 与 Session:

  1. View:从 .yml.yaml.json 资源文件解析出的界面定义,可以被缓存和重载。
  2. Session:某个玩家打开 View 后产生的运行实例,保存状态、静态控件覆盖和动态行绑定。

布局和图标由资源文件定义,显示的数据和点击后的操作由代码提供。

核心概念

  1. 视图(View):一个通过 viewId 打开的界面定义。
  2. 会话(GuiSession):某个玩家当前打开的界面实例。
  3. 静态控件:由 layoutlegend 固定定位的图标或按钮。
  4. 动态区:由同一布局字符组成的一组有序槽位。
  5. 状态(GuiState):当前会话共享的分页、筛选等临时数据。
  6. 数据源(Source):打开或刷新动态区时为其提供 GuiRow 列表。
  7. Hook:按钮点击后执行的开发者回调。
  8. 变体(Variant):根据 rowstate 条件覆盖动态模板。

实现

flowchart LR
    File["gui/<viewId>.yml"] --> Parse["解析并编译布局"]
    Source["注册 Source"] --> Session["创建 GuiSession"]
    Parse --> Session
    Session --> Render["渲染 Bukkit Inventory"]
    Click["玩家点击"] --> Route["定位静态 UID 或动态 Row"]
    Route --> Hook["执行 Hook"]
    Hook --> Render

视图文件默认位于:

plugins/<YourPlugin>/gui/<viewId>.yml

当代码调用 lin.linView().open(player, "demo.shop") 时,LinView 会读取 gui/demo.shop.yml、编译布局、创建会话、加载动态数据源并打开 Bukkit Inventory。

最小示例

资源文件 plugins/MyPlugin/gui/demo.hello.yml

id: "demo.hello"
type: "inventory"
allowManualClose: true
rows: 1
title: "§fHello"

layout:
  - "....A...."

legend:
  "A":
    kind: "static"
    uid: "hello"
    icon:
      kind: "vanilla"
      key: "PAPER"
      name: "§a点击问候"
      lore: []
    action:
      type: "hook"
      args:
        hookId: "demo.hello"
      refresh: ""

dynamicAreas: []

代码侧先注册 Hook,再打开视图:

LinView view = lin.linView();

view.hook("demo.hello", ctx -> {
    Player player = (Player) ctx.viewer();
    player.sendMessage("§aHello!");
});

GuiSession session = view.open(player, "demo.hello");

同一个 LinView 实例管理当前插件的所有 Hook、Source 和玩家会话。建议在插件启用阶段完成注册,不要在每次点击时重复注册。

会话状态

打开界面时可以初始化状态:

GuiSession session = lin.linView().open(player, "demo.shop", state -> {
    state.put("page", 0);
    state.put("category", "all");
});

状态可以通过 {state.page} 等占位符参与标题、图标与动作参数渲染,也可以在 Hook 中通过 ctx.state() 修改。

使用说明

使用当前版本时,需要注意:

  1. Bukkit 侧只接受 Player 作为 viewer。
  2. 容器始终按 9 列 Inventory 创建;type 必须为 inventoryrows 必须为 1 至 6。
  3. 玩家对 LinView 顶层容器的点击和拖动会被统一取消。
  4. 动作支持 hookopenbackclosestatecommand
  5. refreshArea(...) 会重新调用该区域的 Source,并只更新该区域对应的 Bukkit 槽位。
  6. overflow 支持 truncatepagination;分页由状态中的页码驱动。
  7. ItemsAdder 图标需要服务器提供对应插件和有效 ID;可以在 icon.meta 中声明原版材质作为回退。
  8. reload()reload(viewId) 先准备新定义,再刷新受影响的活动会话并保留状态。定义或 Source 加载失败时保留对应旧会话,其他会话继续尝试刷新,最后汇总报告错误。

讨论

请登录账号