跳转到内容

图片渲染与高级模板 ​

ShitBot 提供两条互不混装的图片路径。默认模式面向普通服务器,高级模式面向需要自由布局、真实数据预览和插件 API 的服务器。

两种模式 ​

模式配置运行内容网络下载适用场景
内置 Java 图片image.renderer: "java"平台插件内已有的 Java2D 在线图;外观来自 templates/*.yml不下载高级渲染组件希望占用低、配置简单的服务器
高级场景模板custom-image-templates.enabled: true独立的 ShitBotRenderer 组件、image-templates/ 场景、数据提供器和可选编辑器正常模式下本地无有效缓存时下载;debug 模式只加载本地 JAR需要自由图层、条件/循环、PAPI 或第三方插件调用的服务器

当前高级渲染器仍然使用 Java2D,不包含 Chromium、Node.js、React 运行时,也不会执行模板上传的 HTML、JavaScript 或服务器命令。平台插件 JAR 与可选渲染器 JAR 分开发布,模板图片和资源只放在插件数据目录。

只使用内置图片 ​

保持默认值即可:

yaml
image:
  renderer: "java"
  template: "default"

custom-image-templates:
  enabled: false

此时不会创建高级模板引擎、不会启动编辑器,也不会访问渲染组件下载地址。在线图继续读取 templates/<名称>.yml;背包图也继续使用该轻量主题格式。

原生图片外观和底稿 ​

新安装使用低饱和的深灰蓝与薄荷绿主题,在线图和背包图采用统一的渐变卡片、柔和阴影、细边框和状态色。在线图使用等宽玩家卡片,按名字实际宽度和 image.players-per-row 上限决定列数;标题、在线总人数、子服人数和时间分开排版。头像使用最近邻缩放,保留 Minecraft 像素边缘。

背包图把主背包、快捷栏和装备区分开,快捷栏显示 1–9 编号;数量使用独立底标,附魔物品带淡色高亮,耐久条采用圆角。inventory.layout.hotbar-gap 控制快捷栏前的额外间距。底部摘要和数据时间放不下一行时会分成两行,长名字按可用空间省略。

上述布局和绘制变化同样用于已有主题。已有 templates/default.yml 的配色和自定义值会保留;升级后希望完整采用新默认主题时,可先备份原文件,再使用新版本的同名资源,并执行 /shitbot reload。

每次启动和 /shitbot reload,在线图与背包图都会在图片线程预生成静态底稿。后续渲染复制底稿再填入动态数据:在线人数、头像、名称、物品、耐久和时间不会写回底稿,因此玩家离开或物品消失时不会留下旧内容。

在线图按子服名称、面板高度和玩家卡片布局缓存底稿;人数或名称宽度改变布局时生成新的底稿。背包图复用固定格子和装备标签。每种原生图片的底稿缓存最多保留 4 项、合计 8,388,608 像素,采用内存缓存;大于此限的图片仍可渲染,但底稿不常驻缓存。重载与重启创建新缓存,自动使用最新主题、语言和尺寸。现有成品 PNG 缓存仍按 image.cache-seconds 与背包渲染缓存配置生效。

让内置“服务器状态”指令使用高级模板 ​

yaml
image:
  renderer: "custom"
  custom-template: "online-status"

custom-image-templates:
  enabled: true

image.renderer: "custom" 但 custom-image-templates.enabled: false 属于无效配置,插件会明确拒绝加载,避免配置看起来启用了高级模板但实际静默回退。

也可以保持 image.renderer: "java",只把 custom-image-templates.enabled 设为 true。这样默认在线图仍走轻量路径,而编辑器、自定义群命令和其他插件的 ShitBotApi 可以使用高级模板。

按需下载与缓存 ​

高级模板总开关关闭时,下载逻辑不会运行。开启后,插件按以下顺序加载:

  1. 读取 custom-image-templates.component.version;留空时使用当前平台插件版本;
  2. debug: true 时,只加载插件数据目录下的 components/image-renderer/<版本>/ShitBotRenderer-<版本>.jar,跳过 checksum 和签名校验;文件缺失或无效时直接报错,不会下载;
  3. debug: false 时,检查 components/image-renderer/<版本>/ 下的缓存;
  4. 正常模式同时校验 JAR 大小、Release SHA-256、RSA 独立签名、组件内嵌版本和服务入口;
  5. 缓存缺失或校验失败时,才从官方 ShitBot GitHub Release 下载 JAR、.sha256 和 .sig;
  6. 下载完成且全部校验通过后,使用隔离类加载器启动组件。

默认配置:

yaml
custom-image-templates:
  enabled: false
  directory: "image-templates"
  component:
    version: ""
    download-url: "https://github.com/hutuyee/ShitBot/releases/download/%version%/ShitBotRenderer-%version%.jar"
    maximum-download-bytes: 4194304
    connect-timeout-ms: 5000
    read-timeout-ms: 30000

正常模式下,下载器只接受 HTTPS 的官方 ShitBot Release 地址及 GitHub 的 Release 资源重定向。缓存损坏时不会加载损坏 JAR。某个版本的 Release 必须同时包含同版本 ShitBotRenderer、checksum 和签名;否则高级模板启动失败,但关闭总开关后仍可使用内置 Java 图片。

调试模式的本地 JAR 仍会检查大小,并且必须包含 META-INF/services/haaa.shitbot.api.spi.ImageTemplateEngineFactory 和匹配版本的 META-INF/shitbot-renderer.version,但不读取旁边的 .sha256 和 .sig 文件,也不进行公钥校验。缺少 JAR 时,错误信息会给出需要放置文件的完整路径。

模板目录 ​

高级组件启动或随插件重载时会补全内置 online-status(在线状态)、player-profile(个人信息)和 inventory(背包)模板,即使模板根目录中已有其他模板也会生成缺失的默认模板。首次生成时会发布示例,三者的目录结构相同:

text
image-templates/
├─ player-profile/
├─ inventory/
└─ online-status/
   ├─ manifest.yml
   ├─ scene.yml
   ├─ assets/
   ├─ published.yml
   └─ versions/
      └─ 1/
         ├─ manifest.yml
         ├─ scene.yml
         └─ assets/
  • 根目录的 manifest.yml、scene.yml 和 assets/ 是草稿;
  • versions/<编号>/ 是发布时复制出的生产快照;
  • published.yml 指向当前生产版本并记录快照 SHA-256;
  • 正常渲染只读取并复核已发布快照,不读取正在编辑的草稿;
  • 回滚只切换生产指针,不覆盖草稿,也不修改历史版本。

不要手工修改 versions/ 或 published.yml。要修改模板,应编辑草稿后重新发布一个新版本。

这三个内置模板的草稿缺少 manifest.yml、scene.yml 或 assets/ 目录时会自动恢复。空 YAML 文件、缺失键和 null 值会按内置模板补齐并保存;已有画布设置、图层列表和提供器列表会保留,列表不会按位置合并。补全已有草稿不会改动历史快照或自动发布新版本,需通过编辑器发布后才能影响现有生产模板。published.yml 丢失且历史版本仍在时,会从最新历史版本恢复发布指针,该版本必须通过模板校验。

默认 online-status 使用 1200 × 960 的白灰色卡片:背景为纯浅灰 #F3F4F6,主卡片为不透明白色,标题与玩家名称使用深灰 #20262E,提示和页脚使用 #59636E。少量灰蓝色 #355874 配合浅蓝灰 #EDF2F6,只用于突出总在线人数;不再使用粉紫渐变和大面积装饰色块。顶部展示标题、配置的服务器名和总在线人数。Bukkit/Spigot、Nukkit 等单服平台直接排列玩家名片,不显示 CraftBukkit 等实现名称或子服分组框;BungeeCord、Velocity 从代理自身的在线快照取得玩家所在的服务器 ID,再按两列排列子服面板,即使只有一个子服也保留 ID。每位玩家显示圆角方形的 Minecraft 像素头像与名字,页脚只展示 ShitBot 标识和数据生成时间。头像直接读取 online-players 提供的 ${player.avatar},沿用 image.avatar.url-template;image.avatar.enabled: false 时不请求玩家头像。

默认配色参考 WCAG 2.2 的文字对比度要求:普通文字至少为 4.5:1,大字至少为 3:1。按模板指定的 sRGB 色值计算,次要文字在白底上约为 6.11:1,子服人数文字在浅灰标签上约为 5.45:1,总在线人数及其说明约为 6.66:1。人数指示点在白底上的对比度高于有意义图形的 3:1 要求,并始终配有人数文字,避免仅用颜色传递信息。白灰大面积、强调色小面积是本模板的视觉选择,WCAG 不规定颜色面积比例;这些色值计算也不代表完整的无障碍评估。

单服按四列直接展示最多 40 位玩家;代理最多展示 4 个子服,每服最多展示 8 位玩家。人数统计始终使用完整在线数据,超出展示范围时会显示对应提示。总在线人数为 0 时显示整页空状态,代理数据中的空子服也有独立提示。两种布局沿用默认图层和画布限制,无需提高资源上限。

升级只补齐内置模板草稿中缺失的内容,保留已经生成或修改过的值和图层列表。编辑器中新建模板会使用新版默认布局;要更新原有 online-status 的完整布局,可把发行源码中 ShitBotRenderer/src/main/resources/defaults/online-status/scene.yml 的内容放入该模板的 scene.yml 草稿,再通过编辑器保存、预览并发布。

模板 ID 只能使用小写字母、数字、下划线和连字符,最长 64 个字符。所有文件必须位于插件数据目录内;绝对路径、目录穿越和符号链接越界都会被拒绝。

个人信息与背包模板 ​

player-profile 是 960 × 600 的个人信息卡片,左侧展示玩家皮肤,右侧展示名字、在线状态、权限组、点券和累计在线时长。皮肤沿用 profile.skin-url-template,权限组和点券沿用 profile 中的占位符配置。没有值的可选字段会隐藏,点券为 0 时仍然显示。网络皮肤需要开启 custom-image-templates.remote-images.enabled。

inventory 是 1000 × 840 的背包卡片,包含头盔、胸甲、护腿、靴子、副手、27 格主背包和 9 格快捷栏。物品图标、数量、附魔边框和耐久条都是可编辑图层。数据沿用现有背包服务:可在本服采集时读取在线背包,否则读取保留期内的最近快照;没有快照时显示提示。材质沿用 inventory.icons 与 item-icons/,具体部署见背包说明。

启用编辑器,在 config.yml 中设置:

yaml
custom-image-templates:
  enabled: true
  editor:
    enabled: true

执行 /shitbot reload,再用 /shitbot editor 打开编辑器,选择 player-profile 或 inventory。在预览上下文的 player 中填入实际玩家名,保存、预览后发布即可应用草稿。背包模板要求 inventory.enabled: true;代理端需要能读取后端保存的共享快照。

四个平台的 commands.yml 都提供以下群命令入口,默认关闭;要使用时将对应的 enabled 改为 true 后重载:

yaml
image-templates:
  commands:
    player-profile:
      enabled: true
    inventory:
      enabled: true

默认别名为 自定义个人信息 / custom profile 和 自定义背包 / custom inventory,使用发送者已绑定的角色。要通过参数指定角色,将该入口的 player-source 改为 argument 并调整 usage;指定角色仍必须属于发送者。内置的 个人资料、背包 指令继续使用原生图片,这些入口独立使用外置模板。其他插件可通过 API 渲染相同模板 ID,并在上下文传入 player。

manifest.yml ​

yaml
schema-version: template-v1
id: player-card
name: Player card
suggested-file-name: player-card.png
providers:
  - id: shitbot
  - id: player-avatar
    player: "${context.player}"
  - id: papi
    player: "${context.player}"
    server: "${context.server}"
    placeholders:
      display_name: "%player_displayname%"
      health: "%player_health%"

providers 只声明模板确实需要的数据。模板不能动态调用任意 Java 类、网络接口或控制台命令。数据先在平台安全线程或独立数据线程收集,完成后才把普通 Map/List/字符串/数字传给图片线程。

内置提供器:

ID输出位置内容
shitbot${data.shitbot.*}平台名、插件版本、生成时间
online-players${data.online-players.*}total 总在线数、players 平铺玩家列表、servers 分组列表及 group-by-server 代理分组标记;头像配置开启时还提供头像 URL
player-avatar${data.player-avatar.*}指定玩家的 player、url,以及头像服务地址模板 url-template;template-only: true 仅提供地址模板,供头像图层分别绑定玩家
papi${data.papi.*}显式声明的 PlaceholderAPI 值、values 映射和逐项 errors 映射
player-profile${data.player-profile.*}指定玩家的皮肤 URL、权限组、点券、累计在线秒数/格式化时长、在线状态和语言标签;可用 options.player 或上下文 player 指定玩家
inventory${data.inventory.*}指定玩家的背包快照、分区物品列表、PNG 图标、统计和语言标签;可用 options.player 或上下文 player 指定玩家,QQ 请求会再次校验绑定归属

个人信息提供器的 has-permission-group 和 has-points 表示对应字段是否有值,适合控制条件图层;online 是布尔值,status 是本地化状态,labels 提供 skin-title、skin-unavailable、permission-group、points、online-time 和 footer。

背包提供器的常用字段:

字段内容
player、available、live玩家名、是否有快照、是否为本次在线采集;没有快照时各物品列表为空
title、status、unavailable、labels本地化标题、数据状态、无快照提示及分区标题
equipment、storage、hotbar装备与副手 5 格、主背包 27 格、快捷栏 9 格;包含空格
slots按槽位编号排列的完整 41 格:0–8 快捷栏、9–35 主背包、36–39 靴子至头盔、40 副手
occupied、total-items、summary已用格数、物品总数、本地化摘要
server、captured-at、captured-time、data-time快照服务器、毫秒时间戳、格式化时间及带标签的时间

每格包含 slot、empty、icon、amount、amount-text、enchanted、border-color 和 has-durability;非空格还包含 registry-id、material-name、name、damage、maximum-durability、durability(剩余耐久)。icon 是可直接绑定到 image.source 的 PNG data URI,空格为空字符串;数量不超过 1 时 amount-text 为空。渲染快照字段前应判断 available,显示耐久条前应判断 has-durability。

online-players.group-by-server 由运行平台决定:BungeeCord、Velocity 为 true,单服平台为 false。代理分组的 servers[].id 是代理提供的服务器 ID,servers[].name 保留原有名称字段;各分组仍提供 players 和 count。手写单服模板可直接循环 ${data.online-players.players},不必显示分组标题。

custom-image-templates.remote-images.enabled 现在默认为 true,允许 player-avatar 和其他 HTTPS 图片 URL,同时仍支持模板自己的 assets/。旧配置中显式写出的 false 会继续生效;需要网络头像时,将它改为 true 后重载。HTTPS、地址、响应大小和像素限制仍然有效。

scene.yml ​

最小场景:

yaml
schema-version: scene-v1
canvas:
  width: 900
  height: 500
  background: "#172033"
layers:
  - type: text
    x: 40
    y: 32
    width: 820
    text: "Hello ${context.player}"
    font-size: 36
    font-style: bold
    color: "#FFFFFFFF"

颜色支持 #RRGGBB 和 #AARRGGBB。画布还支持 gradient-start 与 gradient-end。

所有普通图层可使用 x、y、width、height、opacity、visible 和 when。主要节点如下:

节点常用属性
texttext、font-family、font-size、font-style、color、align、line-height、maximum-lines
imagesource、fit: cover/contain/stretch、radius、描边;本地资源写 assets/name.png
avatarplayer(玩家名或变量)、source(直接图片来源)、shape: square/circle、pixelated、宽高、圆角和描边;未指定形状的旧头像图层仍使用圆形
rectanglefill、radius、stroke-color、stroke-width
circlefill、diameter 或宽高、描边
linex2/y2 或宽高、color、stroke-width
progressvalue、maximum、background、fill、radius
groupchildren;可用 clip: true 按组宽高裁剪
stackchildren、direction: vertical/horizontal、gap
gridchildren、columns、cell-width、cell-height、行列间距
conditioncondition、可选 equals、then、else
loopitems、as、maximum-items、children;普通布局还可使用单项偏移

放置玩家头像 ​

在编辑器顶部选择「玩家头像」并添加图层,然后在右侧设置头像来源:

  • 指定玩家名 / 自定义变量:填写玩家名,或 ${player.name} 等变量。同一模板中的不同头像可以分别使用不同玩家。画布根级新增头像默认填入示例名 Steve,可以直接改成自己的玩家名。
  • 当前玩家:使用 ${context.player},适合玩家资料卡或绑定玩家的自定义命令。在「预览数据」的 context 中填写 player 作为预览玩家;实际渲染由调用方提供,没有玩家值时不绘制该头像。
  • 所在循环中的玩家:将头像添加到玩家循环或其内部容器中时,会自动绑定该循环变量的 name,例如 ${player.name};支持按 server.players 遍历在线玩家。
  • 图片地址 / 头像图片变量:直接填写 assets/head.png、HTTPS 图片地址或 ${player.avatar}。这也兼容已有的头像图片来源写法。

头像可以拖动、缩放,支持方形、圆角方形和圆形。新头像默认使用圆角方形与清晰像素缩放,保留 Minecraft 皮肤的像素边缘;需要时可以切换为平滑缩放。画布显示头像位置及玩家标记,真实头像通过「预览」查看。

网络头像使用 image.avatar.url-template 配置的服务。新配置已默认开启远程图片;如果旧配置中关闭了此项,需要改为下面的值后重载:

yaml
custom-image-templates:
  remote-images:
    enabled: true

编辑器添加按玩家名绑定的头像时,会自动补充 player-avatar 数据声明,并将图层和声明一起纳入撤销。已有且指定了 player 的 player-avatar 声明会原样保留;没有选项的简写会自动改成仅提供地址模板的声明。手写模板时,在 manifest.yml 的 providers 中添加:

yaml
providers:
  - id: player-avatar
    template-only: true

随后在 scene.yml 的 layers 中放置头像:

yaml
layers:
  - type: avatar
    name: 玩家头像
    player: Steve  # 可改成 ${context.player} 或玩家循环中的 ${player.name}
    x: 40
    y: 40
    width: 64
    height: 64
    shape: square  # circle 为圆形;square 配合 radius 为圆角方形
    radius: 8
    pixelated: true

player 模式会根据每个头像图层当前绑定的玩家生成地址,仍通过渲染器已有的图片加载与缓存处理。显式写出 source 或兼容字段 avatar 时优先使用该图片来源,避免与 player 混用。旧的 player-avatar 声明仍可使用 player: "${context.player}",并通过 ${data.player-avatar.url} 读取单个玩家的头像地址。

变量绑定 ​

绑定规则:

  • ${context.player} 读取调用方上下文;
  • ${data.papi.health} 读取数据提供器;
  • 表达式独占整个值时保留原始类型,因此 ${data.online-players.servers} 可以直接交给 loop.items;
  • 表达式嵌入文字时转换为字符串;
  • 循环变量直接作为根使用,例如 as: player 后读取 ${player.name};
  • 循环还提供 ${index}、${first} 和 ${last}。

PAPI 表达式只能写在 manifest 的 papi.placeholders 声明中。场景只读取 ${data.papi.<别名>},不会扫描任意文本并隐式执行 PlaceholderAPI。

PlaceholderAPI 与代理后端 ​

Spigot/Paper/Folia 在玩家所属的正确调度线程批量解析声明的占位符。PlaceholderAPI 未安装、玩家不在线、表达式未解析或请求超时时都会返回明确错误。maximum-queries、timeout-ms 和 cache-seconds 控制数量、单次等待和短期缓存。

BungeeCord/Velocity 本身不解析 Bukkit PlaceholderAPI。模板的 papi.server 或群命令的 target-server 会通过已有的认证代理—后端通道把请求发送到指定 Spigot 后端;协议双方必须使用相同 ShitBot 版本。后端需要安装 PlaceholderAPI,并配置 backend-transport.listener。

Nukkit-MOT 不会冒充 Bukkit PlaceholderAPI。shitbot、online-players、player-avatar 和第三方注册的数据提供器可以使用;声明 papi 会得到不支持错误。

自定义 QQ 图片命令 ​

高级模板本身不会注册命令。在 commands.yml 中显式配置每个入口:

yaml
image-templates:
  commands:
    player-card:
      enabled: true
      aliases:
        - "我的名片"
      template: "player-card"
      player-source: "bound"
      target-server: "survival"
      permission: "shitbot.image.player-card"
      cooldown-seconds: 10
      usage: "%at% 用法:我的名片 [已绑定角色名]"
      failed: "%at% 图片生成失败:%result%"

player-source 可选:

值行为
bound使用发送者 QQ 最新绑定的角色;命令后的其余文字保留在 ${context.arguments}
argument命令后必须提供精确角色名,并确认该角色属于发送者 QQ
none不读取绑定,${context.player} 为空;若配置非空 permission,因为没有角色可校验而不会通过

可用上下文包括 title、server-name、qq、group、sender、player、players、server、arguments、command 和 platform。权限请求只执行权限判断,不执行控制台命令。别名冲突时,先匹配内置 OneBot 指令和已有控制台快捷命令,因此应使用唯一别名。

可视化编辑器 ​

编辑器默认关闭:

yaml
custom-image-templates:
  editor:
    enabled: true
    bind-address: "127.0.0.1"
    port: 0
    login-seconds: 60
    maximum-upload-bytes: 2097152

重载后,由有 shitbot.admin 权限的管理员执行:

text
/shitbot editor

命令返回短时、一次性登录地址,login-seconds 只控制该地址的登录有效期。登录后建立可反复使用的会话,保存草稿、预览和发布不会使会话失效。会话空闲期限为 24 小时,每次请求都会续期;页面打开期间每 5 分钟自动续期,返回标签页时也会立即续期,因此长时间编辑画布或 YAML 无需依靠保存来维持登录。自动续期不会保存或改动草稿。

编辑器采用中文深色界面:左侧查找模板和图层,中间编辑画布,右侧调整设计属性;YAML、预览数据与渲染预览各有独立的编辑区域。

在画布或图层列表中选中元素后,可以直接按 Delete 或 Backspace 删除,包括分组、堆叠、网格等布局及其子图层。删除立即生效,可用 Ctrl+Z 恢复。输入框内的删除、全选、复制和撤销保持正常的文字编辑行为,中文输入法组词时也不会触发图层快捷键。

操作快捷键
保存草稿Ctrl+S
删除选中图层或布局Delete / Backspace
撤销 / 重做Ctrl+Z / Ctrl+Shift+Z 或 Ctrl+Y
复制所选图层Ctrl+D
加选或取消加选Shift、Ctrl 或 ⌘ + 点击
全选图层Ctrl+A
移动 1 像素 / 10 像素方向键 / Shift + 方向键
上移 / 下移一层Ctrl+] / Ctrl+[
平移画布按住空格拖动,或中键拖动
缩放画布Ctrl + 滚轮,或画布右下角的缩放控件
适应画布Ctrl+0
保持比例调整图层大小Shift + 拖动右下角
临时关闭 5 像素吸附Alt + 拖动
取消选择 / 取消当前拖动Esc
打开快捷键说明?,或右上角的问号

macOS 使用 ⌘ 代替 Ctrl。编辑器保留当前模板最近 100 步编辑历史;一次拖动对应一次撤销,保存草稿后仍可撤销。切换模板或刷新页面后会重新开始记录。

属性面板按位置与尺寸、文字、外观、布局和显示条件分组。没有选中图层时可以编辑画布尺寸、背景和渐变;颜色选择器兼容模板的 #AARRGGBB 格式,并保留原有透明度。单个图层可以相对画布对齐,多个图层可以彼此对齐。选中布局容器后,在顶部的添加位置中选择「选中容器内」「条件成立时」或「条件不成立时」,即可直接添加子图层。位置或尺寸使用变量绑定时,拖动和快捷微调会提示先改为固定数值。

切换模板前会保存当前修改;进入 YAML 视图前会保存并同步设计修改,离开 YAML 视图前会保存并解析源码。保存失败或源码有误时会保留当前视图和修改。页面顶部显示草稿保存状态,关闭或刷新有未保存修改的页面时会提醒。保存草稿与发布模板分开进行,只有点击「发布模板」才会生成新的发布版本。

布局视图用于编辑结构和位置,条件分支及循环只展示结构示例。实际数据、图片和服务端字体效果通过「预览」查看,预览结果可直接下载为 PNG。编辑器还提供图片资源上传、完整图层 JSON、数据提供器解析、发布历史和版本回滚;回滚切换正在使用的发布版本,保留当前草稿。

Java 版玩家在 Spigot/Paper/Folia、BungeeCord 和 Velocity 的聊天框中会收到「打开编辑器」和「复制链接到输入框」按钮。前者交给客户端在浏览器打开,后者把完整地址填入聊天输入框,按 Ctrl+A、Ctrl+C 即可复制,无需发送聊天消息。Nukkit-MOT 玩家会收到预填完整地址的表单,可以选中地址复制到浏览器。控制台仍输出完整地址。

打开时必须保留完整的 /login?token=…,并允许站点 Cookie。登录后可以刷新页面,也可以在同一浏览器里再次点击已使用的链接进入当前会话。不同编辑器端口使用独立 Cookie;浏览器已有其他本地服务的 Cookie 不会影响登录。连续 24 小时未成功续期、浏览器丢失 Cookie 或编辑器服务重启后需要重新登录。未登录或会话失效时,页面会显示重新登录的操作说明;编辑过程中失效可以在新标签页登录,再回到原标签页继续保存草稿。

监听地址必须解析为回环地址。需要远程使用时,应由管理员自行配置 HTTPS 反向代理,并保留原始 Host;不要直接把编辑器端口暴露到公网。编辑器请求还受会话 Cookie、同源检查、自定义请求头、CSP、上传大小和模板路径限制保护。

资源限制 ​

yaml
custom-image-templates:
  limits:
    maximum-width: 2400
    maximum-height: 2400
    maximum-pixels: 8388608
    maximum-layers: 256
    maximum-loop-items: 200
    maximum-asset-bytes: 2097152
    maximum-template-asset-bytes: 16777216
    render-timeout-ms: 5000
  render:
    threads: 2
    maximum-queued: 16
  remote-images:
    enabled: true
  data:
    maximum-queries: 32
    timeout-ms: 3000
    cache-seconds: 10

图层上限同时限制循环展开后的实际节点数。渲染线程和等待队列都是有界的;队列满或超时会快速失败。远程图片仅支持 HTTPS,并检查端口、重定向、内网地址、响应类型、字节数和解码后的像素尺寸。

插件 API ​

在线状态、原生个人信息、原生背包和高级模板均有公共渲染入口,并统一支持 PNG、流式读取、按需 Base64 / data URI 与异步本地保存。完整方法、返回状态、文件地址和并发约定见图片接口。本节介绍高级模板和数据提供器。

第三方插件以 provided 方式依赖轻量模块:

xml
<dependency>
  <groupId>haaa</groupId>
  <artifactId>ShitBotApi</artifactId>
  <version>${shitbot.version}</version>
  <scope>provided</scope>
</dependency>

以 Bukkit 为例,可从插件主类取得 API:

java
Plugin plugin = Bukkit.getPluginManager().getPlugin("ShitBotSpigot");
if (!(plugin instanceof ShitBotApiProvider)) {
    return;
}
ShitBotApi api = ((ShitBotApiProvider) plugin).getShitBotApi();
if (api == null || !api.isCustomImageTemplatesEnabled()) {
    return;
}

Map<String, Object> context = new LinkedHashMap<String, Object>();
context.put("player", player.getName());
context.put("server", "survival");
api.renderImage("player-card", ImageRenderRequest.of(context))
        .thenAccept(result -> {
            byte[] png = result.getBytes();
            String fileName = result.getSuggestedFileName();
            long version = result.getTemplateVersion();
            // 调用插件自行决定发送、缓存或保存。
        });

ImageRenderResult 包含 PNG 字节、宽、高、内容类型、建议文件名、模板 ID 和模板版本。渲染返回 Future,字节复制、流式输出和 Base64 编码辅助方法是同步的;缓存命中时回调可能在调用线程运行。访问平台 API 时应切换到平台要求的主线程/区域线程,大图编码和上传则使用调用插件自己的有界执行器。

注册自定义提供器:

java
api.registerImageDataProvider(new ImageDataProvider() {
    public String getId() {
        return "my-plugin";
    }

    public CompletableFuture<Map<String, Object>> provide(ImageDataRequest request) {
        Map<String, Object> data = new LinkedHashMap<String, Object>();
        data.put("value", "example");
        return CompletableFuture.completedFuture(data);
    }
});

模板在 manifest 中声明 id: my-plugin 后,从 ${data.my-plugin.value} 读取。提供器 ID 必须唯一;卸载或重载第三方插件时,应使用同一个实例调用 unregisterImageDataProvider。

安装 · 配置 · 开发