API 方法表
ChaUI 服务端 API 供其他 Bukkit 插件控制已经打开的页面、槽位和实体组件,也可以监听玩家在 ChaUI 页面中的交互。
公开类位于以下包中:
import com.github.ginirohikocha.chaui.api.ChaUIAPI;
import com.github.ginirohikocha.chaui.api.ChaUIEntityAPI;
import com.github.ginirohikocha.chaui.api.ChaUISlotAPI;
import com.github.ginirohikocha.chaui.api.PageRegistrationResult;
import com.github.ginirohikocha.chaui.api.WorldMoveType;
import com.github.ginirohikocha.chaui.api.event.ChaUIClientReadyEvent;
import com.github.ginirohikocha.chaui.api.event.ChaUIPacketEvent;
import com.github.ginirohikocha.chaui.api.event.ChaUISlotClickEvent;身份字段速查
| 字段 | 含义 | 作用范围 |
|---|---|---|
pageId | 页面配置的 id | 标识页面种类。按 pageId 下发的状态会应用到该玩家当前打开的同 ID 页面;同一页面存在多个世界实例时会一起收到状态 |
sessionId | 页面每次打开时生成的会话 ID | 精确标识一次打开会话,关闭并重新打开后会变化 |
elementId / componentId | elements 中组件的 id | 标识页面中的一个组件;两个名称表达同一种组件身份 |
mode + bind | item_slot.itemSlot.mode + bind | 标识玩家的一份槽位状态;不同页面使用相同组合时共享,同 bind 的不同模式彼此隔离 |
instanceId | 打开世界页面时指定的实例 ID | 在同一玩家当前打开的全部世界页面中全局唯一;关闭世界实例时不需要 pageId |
不要混用
槽位 API 使用 mode + bind,不接收页面或 sessionId;普通页面状态修改通常使用 pageId + elementId,世界实例关闭只使用 instanceId。
ChaUIAPI
用于打开、关闭和修改页面运行时状态。状态修改只影响当前客户端页面会话,不会改写服务端的页面 yml。
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
setVariable(Player player, String pageId, String name, Object value) | 玩家、页面 ID、变量名、变量值 | void | 修改一个 vars 变量。变量名和变量值必须符合页面变量规则 |
setVariables(Player player, String pageId, Map<String, Object> variables) | 玩家、页面 ID、变量映射 | void | 一次修改多个 vars 变量 |
setComponentState(Player player, String pageId, String elementId, String property, Object value) | 玩家、页面 ID、组件 ID、属性路径、值 | void | 修改一个允许动态更新的组件属性,例如 visible 或 text.value |
setComponentStates(Player player, String pageId, String elementId, Map<String, Object> properties) | 玩家、页面 ID、组件 ID、属性映射 | void | 一次修改同一组件的多个属性 |
runScript(Player player, String pageId, String script) | 玩家、页面 ID、ChaUI Script | void | 在玩家当前页面会话中执行客户端脚本 |
playSound(Player player, String pageId, String soundPath) | 玩家、页面 ID、ChaUI 素材目录下的 .ogg 相对路径 | void | 在玩家客户端播放本地 ChaUI 音效;非法路径会抛出 IllegalArgumentException |
resetRememberedComponentPositions(Player player) | 玩家 | void | 重置该玩家在当前服务器下记住的全部组件位置;只接受在线且客户端已就绪的玩家 |
registerPage(Plugin sourcePlugin, String resourcePath) | 来源插件、Jar 内页面资源路径 | PageRegistrationResult | 不覆盖地注册并立即加载第三方页面 |
registerPage(Plugin sourcePlugin, YamlConfiguration configuration) | 来源插件、完整页面配置 | PageRegistrationResult | 直接从 Bukkit YamlConfiguration 注册并立即加载页面 |
elementType(String pageId, String elementId) | 页面 ID、组件 ID | String 或 null | 返回当前已加载页面中指定组件的类型;参数为空、页面未加载或组件不存在时返回 null,不会暴露内部页面定义对象 |
open(Player player, String pageId) | 玩家、页面 ID | void | 从 ChaUI 已加载的页面中查找并打开普通页面或 HUD。同一玩家的相同页面会先完整关闭旧会话再打开新会话 |
openSubPage(Player player, String pageId) | 玩家、子页面 ID | void | 在玩家当前普通 GUI 或容器替换 GUI 上方打开一层 screen 子页面 |
replaceSubPage(Player player, String pageId) | 玩家、目标子页面 ID | CompletableFuture<String> | 关闭根页面上方的旧子页面链,再把目标页面直接挂到根页面;完成后返回新子页面的 sessionId。快速连续调用时最后一次请求生效 |
closeSubPage(Player player) | 玩家 | void | 只关闭当前 GUI 栈最上层子页面 |
openWorld(Player player, String pageId, String instanceId) | 玩家、世界页面 ID、实例 ID | void | 使用页面 display.world.x/y/z/yaw 的默认值打开世界页面 |
openWorld(Player player, String pageId, String instanceId, double x, double y, double z, String yaw) | 玩家、世界页面 ID、实例 ID、世界坐标、朝向 | void | 使用调用方给出的坐标和朝向打开世界页面。yaw 可填写有限数字字符串或 follow_player |
moveWorld(Player player, String instanceId, double x, double y, double z) | 玩家、世界实例 ID、目标坐标 | void | 立即把已经打开的世界页面实例移动到目标坐标,不关闭或重新打开页面 |
moveWorld(Player player, String instanceId, double x, double y, double z, long durationMillis, WorldMoveType type) | 玩家、世界实例 ID、目标坐标、毫秒时长、插值类型 | void | 在指定时间内移动现有实例;支持 LINEAR 与 EASE_IN_OUT |
close(Player player, String pageId) | 玩家、页面 ID | void | 关闭该玩家当前打开的普通页面或 HUD,并执行完整关闭生命周期 |
closeWorld(Player player, String instanceId) | 玩家、世界实例 ID | void | 在该玩家的全部世界页面中查找并关闭指定实例,不需要页面 ID |
registerPage 只能在 Bukkit 主线程调用,成功返回时页面已经可以立即打开,不需要再执行 reload。完整资源布置、三种返回结果和异常处理请阅读页面注册 API。
第三方插件需要校验已注册页面或服务器已有同 ID 页面中的组件类型时,应调用 elementType。PageCache、页面定义和组件定义属于 ChaUI 内部实现,不是混淆稳定的公开 API。
调用方只需要传页面 ID,不需要读取或构造 ChaUI 内部的页面定义对象。页面 ID 非法、页面不存在、打开方式与页面显示模式不匹配,或世界页面缺少 display.world 时,打开方法会抛出 IllegalArgumentException。客户端尚未发送 client_ready 时,打开操作会安全忽略;需要在进服时立即打开业务页面的插件应监听 ChaUIClientReadyEvent。
打开与关闭示例
// 打开普通页面或 HUD
ChaUIAPI.open(player, "welcome");
// 使用页面 yml 中配置的世界坐标和朝向
ChaUIAPI.openWorld(player, "world_welcome", "主城欢迎牌");
// 临时覆盖世界坐标,并让页面朝向跟随玩家
ChaUIAPI.openWorld(player, "world_welcome", "副本入口", 120.5, 65.0, -30.5, "follow_player");
// 立即移动现有实例,不触发 close/open 生命周期
ChaUIAPI.moveWorld(player, "副本入口", 125.0, 66.0, -28.0);
// 在 800 毫秒内平滑移动;新移动会立即取代尚未完成的旧移动
ChaUIAPI.moveWorld(player, "副本入口", 130.0, 66.0, -25.0,
800L, WorldMoveType.EASE_IN_OUT);
// 普通页面和 HUD 按页面 ID 关闭
ChaUIAPI.close(player, "welcome");
// 把当前内容子页面替换为商店页,并在真正打开后发送初始变量
ChaUIAPI.replaceSubPage(player, "barn_shop").thenAccept(sessionId ->
ChaUIAPI.setVariables(player, "barn_shop", Map.of("quantity", 1))
);
// 重置该玩家在当前服务器下记住的全部组件位置
ChaUIAPI.resetRememberedComponentPositions(player);
// 世界页面按全局唯一的实例 ID 关闭,不填写页面 ID
ChaUIAPI.closeWorld(player, "主城欢迎牌");replaceSubPage 适合顶部 Tab 和同级内容区。它会等待旧页面完成关闭与临时槽收尾,不需要调用方添加 tick 延迟。请不要用“先 closeSubPage、再立即 openSubPage”模拟替换。
instanceId 是玩家当前全部世界页面中的实例名称,可以使用中文或纯数字。重复使用同一个实例 ID 打开页面时,旧世界实例会先完整关闭,再由新实例替换。
moveWorld 只修改现有世界实例的运行时坐标,不改写页面 yml,也不会重新初始化布局、素材、离屏纹理或页面生命周期。任意新移动请求都会终止旧移动,并从客户端当前实际显示位置开始;LINEAR 是匀速移动,EASE_IN_OUT 会在起点与终点处平滑减速。
resetRememberedComponentPositions 需要在 Bukkit 主线程调用。目标玩家必须在线并已经完成 ChaUI 客户端就绪握手;否则会明确拒绝,不会排队到下次登录。重置只影响当前服务器身份下该玩家的本地位置记忆,不会修改页面 yml,也不会影响其他玩家或其他服务器。
页面状态示例
ChaUIAPI.setVariable(player, "welcome", "progress", 0.75);
ChaUIAPI.setComponentState(player, "welcome", "tip", "visible", true);
ChaUIAPI.setComponentState(player, "welcome", "dialog", "pointerEvents", "block");
ChaUIAPI.setComponentState(player, "welcome", "dialog", "scale", 1.05);
ChaUIAPI.setComponentState(player, "welcome", "dialog", "opacity", 0.9);
ChaUIAPI.runScript(player, "welcome", "vars.ready = true");scale 与 opacity 也可以通过 runScript 绑定公式,例如 component("dialog").opacity = formula("vars.fade")。API 传入的属性和值仍会按页面字段规则校验;布局组件不能设置缩放或透明度,实体不能设置根级透明度。
世界页面状态
setVariable、setComponentState、runScript 等状态接口按 pageId 匹配页面。如果同一玩家打开了同一页面的多个世界实例,这些实例会一起应用该状态。
ChaUISlotAPI
槽位状态由服务端裁决。槽位身份是玩家 UUID 与 mode + bindId,不绑定页面或会话。操作 mapped 槽位会读写玩家的真实映射槽;操作 persistent 或 temporary 槽位会读写玩家级独立槽位系统。
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
getSlotItem(Player player, String mode, String bindId) | 玩家、模式、槽位绑定 ID | ItemStack 或 null | 读取玩家级槽位;玩家没有打开任何 ChaUI 运行时页面时返回 null |
setSlotItem(Player player, String mode, String bindId, ItemStack item) | 玩家、模式、槽位绑定 ID、物品 | void | 写入槽位并同步所有已打开的相同 mode + bindId;传入 null 可清空 |
clearSlotItem(Player player, String mode, String bindId) | 玩家、模式、槽位绑定 ID | void | 清空指定槽位并同步相关页面 |
refreshSlot(Player player, String mode, String bindId) | 玩家、模式、槽位绑定 ID | void | 向所有已打开的相同 mode + bindId 组件刷新权威状态 |
refreshSlots(Player player, String mode) | 玩家、模式 | void | 刷新玩家当前打开页面中引用的该模式全部槽位 |
setItemDisplay(Player player, String pageId, String elementId, ItemStack item) | 玩家、页面 ID、item_display 组件 ID、展示物品 | void | 用一次原子状态补丁同步完整物品快照、物品 ID 和数量;空气、数量不大于 0 或 null 都按清空处理 |
clearItemDisplay(Player player, String pageId, String elementId) | 玩家、页面 ID、item_display 组件 ID | void | 用一次原子状态补丁同时清空完整物品快照、物品 ID 和数量 |
修改读取结果
需要改变槽位物品时,请调用 setSlotItem。不要只修改 getSlotItem 返回的对象并期待页面自动同步。
player 不能为空;mode 只允许 persistent、temporary、mapped;bindId 必须是安全绑定 ID,且 mapped 只能使用明确支持的 backpack_* 映射。非法参数会抛出 IllegalArgumentException。参数校验通过后,如果玩家没有打开任何 ChaUI 运行时页面,读取返回 null,写入、清空和刷新安全忽略。
只要玩家至少打开了一个运行时页面,persistent 与 temporary 就可以在当前页面尚无对应组件时提前写入。之后任意页面出现相同 mode + bindId,会立即取得预存物品。预存临时槽位默认使用安全的 return_to_player 收尾策略。
API 调用不会执行组件的 itemSlot.check;该字段只是客户端点击发包前的便捷条件。插件业务仍需自行完成权限和物品安全判断。
ChaUIEntityAPI
用于查询或修改 entity 组件。状态设置接口按 pageId 下发,因此同一页面的多个世界实例会同时更新。
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
entityUuid(Player player, String pageId, String elementId) | 玩家、页面 ID、实体组件 ID | UUID 或 null | 查询普通页面或 HUD 当前会话中实体组件所显示来源实体的 UUID。页面未打开、组件不存在、组件类型不是 entity、来源实体无法解析或参数为空时返回 null;世界页面实例当前不能通过此方法查询 |
setDisplayName(Player player, String pageId, String elementId, String displayName) | 玩家、页面 ID、实体组件 ID、显示名 | void | 修改 entity.displayName |
setSource(Player player, String pageId, String elementId, String source) | 玩家、页面 ID、实体组件 ID、来源 | void | 修改 entity.source,可用值为 self、uuid、entity_id、virtual |
setEntityType(Player player, String pageId, String elementId, String entityType) | 玩家、页面 ID、实体组件 ID、实体类型 ID | void | 修改虚拟实体的 entity.entityType,例如 minecraft:zombie |
setYaw(Player player, String pageId, String elementId, Object yaw) | 玩家、页面 ID、实体组件 ID、有限数值 | void | 修改实体整体 yaw;无法转换为有限数值时抛出 IllegalArgumentException |
setPitch(Player player, String pageId, String elementId, Object pitch) | 玩家、页面 ID、实体组件 ID、有限数值 | void | 修改实体整体 pitch;无法转换为有限数值时抛出 IllegalArgumentException |
setRoll(Player player, String pageId, String elementId, Object roll) | 玩家、页面 ID、实体组件 ID、有限数值 | void | 修改实体整体 roll;无法转换为有限数值时抛出 IllegalArgumentException |
setOrthographic(Player player, String pageId, String elementId, boolean orthographic) | 玩家、页面 ID、实体组件 ID、是否正交投影 | void | 修改 entity.orthographic;false 为默认 30 度透视投影,true 为正交投影 |
setTrackMouse(Player player, String pageId, String elementId, boolean trackMouse) | 玩家、页面 ID、实体组件 ID、是否追踪鼠标 | void | 修改 entity.trackMouse |
entityUuid 应在页面会话建立后调用,其返回值由实体来源决定:
entity.source | UUID 来源 |
|---|---|
self | 目标玩家的真实 UUID |
uuid | entity.uuid 指定实体的 UUID |
entity_id | 目标玩家当前世界中对应网络实体 ID 的真实 UUID |
virtual | 合法的 entity.virtualUuid;未配置或无效时按页面 ID、会话 ID 和组件 ID 生成确定性虚拟 UUID |
ChaUIClientReadyEvent
客户端确认本地玩家、世界和 Play 发包通道均已可用后,会向服务端发送一次 client_ready。服务端验证协议版本并完成本次连接的幂等登记后,先触发 ChaUIClientReadyEvent,随后才统一同步页面定义、槽位会话、原版容器替换注册表和常驻 HUD。
该事件不可取消,同一玩家同一次在线连接只触发一次;玩家退出并重新进入后会再次触发。
| 方法 | 返回值 | 说明 |
|---|---|---|
getPlayer() | Player | 已完成 ChaUI 客户端就绪握手的玩家 |
getProtocolVersion() | int | 客户端已通过验证的 ChaUI 协议版本 |
客户端就绪示例
public final class ChaUIReadyListener implements Listener {
@EventHandler
public void onClientReady(ChaUIClientReadyEvent event) {
ChaUIAPI.open(event.getPlayer(), "welcome");
}
}不要再用 PlayerJoinEvent + runTaskLater 猜测客户端加载时间。这个事件就是第三方插件在玩家进服后调用 ChaUI 打开、槽位和状态 API 的统一起点。
ChaUISlotClickEvent
玩家点击 item_slot 后,ChaUI 会先校验会话和槽位版本、计算默认交换结果,再触发该事件。监听器可以取消操作,也可以修改最终的光标物品和槽位物品。
事件实现了 Bukkit Cancellable。
| 方法 | 返回值或参数 | 可修改 | 说明 |
|---|---|---|---|
getPlayer() | Player | 否 | 触发操作的玩家 |
getSessionId() | String | 否 | 被点击页面的会话 ID |
getPageId() | String | 否 | 页面 ID |
getComponentId() | String | 否 | 被点击的槽位组件 ID |
getBind() | String | 否 | 槽位绑定 ID |
getAction() | String | 否 | 槽位动作名称,当前点击流程为 click |
getSlotDefinition() | ItemSlotDefinition | 否 | 当前槽位配置 |
getCursorItemBefore() | ItemStack 或 null | 否 | 操作前的服务端光标物品,返回副本 |
getSlotItemBefore() | ItemStack 或 null | 否 | 操作前的槽位物品,返回副本 |
getCursorItemAfter() | ItemStack 或 null | 通过 setter | 默认计算后的光标物品,返回副本 |
setCursorItemAfter(ItemStack item) | void | 是 | 覆盖操作完成后写回的光标物品 |
getSlotItemAfter() | ItemStack 或 null | 通过 setter | 默认计算后的槽位物品,返回副本 |
setSlotItemAfter(ItemStack item) | void | 是 | 覆盖操作完成后写回的槽位物品 |
isCancelled() | boolean | 通过 setter | 查询事件是否已取消 |
setCancelled(boolean cancelled) | void | 是 | 取消后不写回本次操作结果 |
槽位事件示例
public final class RewardSlotListener implements Listener {
@EventHandler
public void onSlotClick(ChaUISlotClickEvent event) {
if (!"reward_input".equals(event.getBind())) {
return;
}
ItemStack result = event.getSlotItemAfter();
if (result != null && result.getAmount() > 16) {
event.setCancelled(true);
}
}
}ChaUIPacketEvent
真实运行时脚本执行 packet(...) 或 发包(...) 后,服务端完成会话与页面校验,再触发该事件。该动作可以来自页面事件、组件全部事件、自定义方法、计时器回调或 API 下发脚本,不限于点击和悬浮。编辑器编辑状态与隔离预览不会发包。ChaUI 不会替业务插件处理 data 内容。
该事件不可取消,全部字段只读。
| 方法 | 返回值 | 说明 |
|---|---|---|
getPlayer() | Player | 触发发包的玩家 |
getActionId() | String | 本次已授权脚本动作的稳定 ID,可用于业务去重或追踪 |
getSessionId() | String | 触发动作的页面会话 ID |
getPageId() | String | 页面 ID |
getElementId() | String | 触发脚本的组件 ID;页面级事件固定为空字符串 |
getTrigger() | String | 原始组件事件或页面事件;方法和计时器继承创建它们的上下文,缺少明确事件来源时为 script |
getPacketId() | String | 页面脚本填写的业务包 ID |
getData() | 只读 List<String> | 页面脚本传入的数据列表。复杂内容可由页面作者放入某一项 JSON 字符串,再由业务插件解析 |
自定义包事件示例
public final class ChaUIPacketListener implements Listener {
@EventHandler
public void onPacket(ChaUIPacketEvent event) {
if (!"treasure.claim".equals(event.getPacketId())) {
return;
}
Player player = event.getPlayer();
List<String> data = event.getData();
// 在这里校验业务状态,再执行奖励逻辑。
}
}注册事件监听器
监听器仍按标准 Bukkit 方式注册:
@Override
public void onEnable() {
getServer().getPluginManager().registerEvents(new RewardSlotListener(), this);
getServer().getPluginManager().registerEvents(new ChaUIPacketListener(), this);
getServer().getPluginManager().registerEvents(new ChaUIReadyListener(), this);
}服务端始终权威
不要根据客户端发来的字符串直接发放物品、执行命令或修改经济数据。业务插件仍需根据玩家、页面、会话和自身数据再次判断操作是否合法。
猹件开发组