正在载入

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

编写于

最近更新

资源文件(界面服务)

LinView 使用资源文件描述视图的尺寸、标题、布局、静态控件和动态区模板。业务数据与点击逻辑不应写入资源文件,而应通过 Source 与 Hook 接入。

标题、图标名称和整段 lore 也可以引用语言文件,随当前插件语言切换和重载更新。写法与刷新规则见文件服务的在文件中引用 ⇱

文件位置

视图文件位于当前插件数据目录的 gui 子目录:

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

加载顺序为 .yml.yaml.json。例如:

lin.linView().open(player, "demo.shop");

会依次查找 gui/demo.shop.ymlgui/demo.shop.yamlgui/demo.shop.json。通常建议使用 YAML,以便保持布局的可读性。

文件中的 id 应与打开时使用的 viewId 一致。实际文件查找以传给 open(...)viewId 为准。

基本结构

当前可用的顶层字段如下:

  1. id:视图 ID。
  2. type:视图类型,当前填写 inventory
  3. allowManualClose:是否允许玩家手动关闭。
  4. rows:Inventory 行数,建议为 1 至 6。
  5. title:界面标题。
  6. layout:每行 9 个字符的布局矩阵。
  7. legend:静态字符定义。
  8. dynamicAreas:动态区定义列表。

建议显式填写顶层字段以便阅读。省略 allowManualClose 时默认为 true

id: "demo.shop"
type: "inventory"
allowManualClose: true
rows: 3
title: "§fShop - {state.category}"

layout:
  - "#########"
  - "#LLLLLLL#"
  - "#P.....N#"

legend: {}
dynamicAreas: []

allowManualClose: false 时,玩家尝试关闭 Inventory 后,Bukkit 运行时会在下一 Tick 重新打开该界面。代码可以在打开时通过状态覆盖此值:

lin.linView().open(player, "demo.shop", state -> {
    state.put("_allowManualClose", true);
});

布局

layout 按从上到下、从左到右的顺序映射 Inventory 槽位。每行应当包含 9 个字符,行数应与 rows 一致。

推荐约定:

  1. .:空槽位。
  2. #:边框或装饰。
  3. 大写字母:按钮或动态区。

编译器会对过短、过长或行数不符的布局进行补齐与截断,但不应依赖这种容错行为。明确写出正确矩阵更容易检查槽位位置。

静态控件

legend 将布局字符映射为静态图标或按钮:

legend:
  "#":
    kind: "static"
    icon:
      kind: "vanilla"
      key: "BLACK_STAINED_GLASS_PANE"
      amount: 1
      name: " "
      lore: []

  "N":
    kind: "static"
    uid: "next"
    icon:
      kind: "vanilla"
      key: "ARROW"
      name: "§a下一页"
      lore: []
    action:
      type: "hook"
      args:
        hookId: "shop.next"
      refresh: ""

静态字段:

  1. kind:当前使用 static
  2. uid:代码操作控件时使用的稳定 ID,可省略。
  3. icon:图标定义。
  4. action:点击动作,可省略。

带有 uid 的 legend 字符在布局中只能出现一次。装饰字符可以重复出现,但不应为它声明 UID。

图标

图标字段包括:

  1. kindvanillaminecraftitemsadder
  2. key:Bukkit Material 名称或 ItemsAdder ID。
  3. amount:物品数量。
  4. name:显示名称。
  5. lore:Lore 字符串列表。
  6. meta:附加数据。其字符串值支持占位符;ItemsAdder 图标可使用 fallbackfallbackMaterial 声明原版回退材质。

原版图标示例:

icon:
  kind: "vanilla"
  key: "DIAMOND"
  amount: 1
  name: "§b钻石"
  lore:
    - "§7价格:§e{row.price}"

ItemsAdder 图标使用 kind: itemsadderkey 填写 namespace:id。服务器未安装 ItemsAdder 或 ID 无效时,可以回退到原版材质:

icon:
  kind: "itemsadder"
  key: "myplugin:coin"
  name: "§e金币"
  meta:
    fallback: "GOLD_NUGGET"

动作

动作字段包括:

  1. type:动作类型。
  2. args:传给动作或 Hook 的参数。
  3. refresh:动作完成后的刷新策略。

当前 Bukkit 运行时可执行:

  1. hook:读取 args.hookId 并调用注册的 Hook。
  2. open:读取 args.viewId 打开目标视图,并将 args.state 写入新会话;当前视图会进入导航栈。
  3. back:返回导航栈中的上一视图,并恢复离开时的状态。
  4. close:关闭当前会话。
  5. state:把 args.state 中的值写入当前状态;也可以直接把 args 作为状态补丁。值为 null 时删除该状态。
  6. command:读取 args.command,以当前玩家身份执行命令。

当前刷新策略可用:

  1. 空字符串:不刷新。
  2. viewsession:重新加载所有 Source 并渲染整个视图。
  3. area:<id>:重新加载指定动态区的 Source,并更新该区域槽位。
  4. widget:<uid>:只更新指定静态控件的槽位。

openbackclose 会结束当前动作流程,不再执行该动作上的额外刷新策略。

动态区

动态区通过 dynamicAreas 声明:

dynamicAreas:
  - id: "items"
    areaChar: "L"
    overflow: "truncate"
    emptyFill:
      icon:
        kind: "vanilla"
        key: "GRAY_STAINED_GLASS_PANE"
        name: " "
        lore: []
    source:
      id: "shop.items"
      args: {}
    template:
      icon:
        kind: "vanilla"
        key: "{row.material}"
        name: "§f{row.name}"
        lore:
          - "§7价格:§e{row.price}"
      action:
        type: "hook"
        args:
          hookId: "shop.buy"
          itemId: "{row.id}"
        refresh: ""
      variants: []

source.args 会在加载 Source 前解析占位符,并通过 GuiContext.args() 传入。需要由按钮持续修改的分页和筛选值仍适合保存在 GuiState 中,再由 source.args 引用:

source:
  id: "shop.items"
  args:
    category: "{state.category}"

重载

lin.linView().reload();
lin.linView().reload("demo.shop");

reload() 会先加载和编译新定义,准备成功后再更新缓存及活动会话。reload(viewId) 只处理指定视图。重新打开时保留原会话状态,但重新执行 Source 并重新建立静态区与动态区绑定。

讨论

请登录账号