Skip to content
On this page

API 方法表

ChaUI 服务端 API 供其他 Bukkit 插件控制已经打开的页面、槽位和实体组件,也可以监听玩家在 ChaUI 页面中的交互。

公开类位于以下包中:

java
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 / componentIdelements 中组件的 id标识页面中的一个组件;两个名称表达同一种组件身份
mode + binditem_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修改一个允许动态更新的组件属性,例如 visibletext.value
setComponentStates(Player player, String pageId, String elementId, Map<String, Object> properties)玩家、页面 ID、组件 ID、属性映射void一次修改同一组件的多个属性
runScript(Player player, String pageId, String script)玩家、页面 ID、ChaUI Scriptvoid在玩家当前页面会话中执行客户端脚本
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、组件 IDStringnull返回当前已加载页面中指定组件的类型;参数为空、页面未加载或组件不存在时返回 null,不会暴露内部页面定义对象
open(Player player, String pageId)玩家、页面 IDvoid从 ChaUI 已加载的页面中查找并打开普通页面或 HUD。同一玩家的相同页面会先完整关闭旧会话再打开新会话
openSubPage(Player player, String pageId)玩家、子页面 IDvoid在玩家当前普通 GUI 或容器替换 GUI 上方打开一层 screen 子页面
replaceSubPage(Player player, String pageId)玩家、目标子页面 IDCompletableFuture<String>关闭根页面上方的旧子页面链,再把目标页面直接挂到根页面;完成后返回新子页面的 sessionId。快速连续调用时最后一次请求生效
closeSubPage(Player player)玩家void只关闭当前 GUI 栈最上层子页面
openWorld(Player player, String pageId, String instanceId)玩家、世界页面 ID、实例 IDvoid使用页面 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在指定时间内移动现有实例;支持 LINEAREASE_IN_OUT
close(Player player, String pageId)玩家、页面 IDvoid关闭该玩家当前打开的普通页面或 HUD,并执行完整关闭生命周期
closeWorld(Player player, String instanceId)玩家、世界实例 IDvoid在该玩家的全部世界页面中查找并关闭指定实例,不需要页面 ID

registerPage 只能在 Bukkit 主线程调用,成功返回时页面已经可以立即打开,不需要再执行 reload。完整资源布置、三种返回结果和异常处理请阅读页面注册 API

第三方插件需要校验已注册页面或服务器已有同 ID 页面中的组件类型时,应调用 elementTypePageCache、页面定义和组件定义属于 ChaUI 内部实现,不是混淆稳定的公开 API。

调用方只需要传页面 ID,不需要读取或构造 ChaUI 内部的页面定义对象。页面 ID 非法、页面不存在、打开方式与页面显示模式不匹配,或世界页面缺少 display.world 时,打开方法会抛出 IllegalArgumentException。客户端尚未发送 client_ready 时,打开操作会安全忽略;需要在进服时立即打开业务页面的插件应监听 ChaUIClientReadyEvent

打开与关闭示例

java
// 打开普通页面或 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,也不会影响其他玩家或其他服务器。

页面状态示例

java
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");

scaleopacity 也可以通过 runScript 绑定公式,例如 component("dialog").opacity = formula("vars.fade")。API 传入的属性和值仍会按页面字段规则校验;布局组件不能设置缩放或透明度,实体不能设置根级透明度。

世界页面状态

setVariablesetComponentStaterunScript 等状态接口按 pageId 匹配页面。如果同一玩家打开了同一页面的多个世界实例,这些实例会一起应用该状态。

ChaUISlotAPI

槽位状态由服务端裁决。槽位身份是玩家 UUID 与 mode + bindId,不绑定页面或会话。操作 mapped 槽位会读写玩家的真实映射槽;操作 persistenttemporary 槽位会读写玩家级独立槽位系统。

方法参数返回值说明
getSlotItem(Player player, String mode, String bindId)玩家、模式、槽位绑定 IDItemStacknull读取玩家级槽位;玩家没有打开任何 ChaUI 运行时页面时返回 null
setSlotItem(Player player, String mode, String bindId, ItemStack item)玩家、模式、槽位绑定 ID、物品void写入槽位并同步所有已打开的相同 mode + bindId;传入 null 可清空
clearSlotItem(Player player, String mode, String bindId)玩家、模式、槽位绑定 IDvoid清空指定槽位并同步相关页面
refreshSlot(Player player, String mode, String bindId)玩家、模式、槽位绑定 IDvoid向所有已打开的相同 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 组件 IDvoid用一次原子状态补丁同时清空完整物品快照、物品 ID 和数量

修改读取结果

需要改变槽位物品时,请调用 setSlotItem。不要只修改 getSlotItem 返回的对象并期待页面自动同步。

player 不能为空;mode 只允许 persistenttemporarymappedbindId 必须是安全绑定 ID,且 mapped 只能使用明确支持的 backpack_* 映射。非法参数会抛出 IllegalArgumentException。参数校验通过后,如果玩家没有打开任何 ChaUI 运行时页面,读取返回 null,写入、清空和刷新安全忽略。

只要玩家至少打开了一个运行时页面,persistenttemporary 就可以在当前页面尚无对应组件时提前写入。之后任意页面出现相同 mode + bindId,会立即取得预存物品。预存临时槽位默认使用安全的 return_to_player 收尾策略。

API 调用不会执行组件的 itemSlot.check;该字段只是客户端点击发包前的便捷条件。插件业务仍需自行完成权限和物品安全判断。

ChaUIEntityAPI

用于查询或修改 entity 组件。状态设置接口按 pageId 下发,因此同一页面的多个世界实例会同时更新。

方法参数返回值说明
entityUuid(Player player, String pageId, String elementId)玩家、页面 ID、实体组件 IDUUIDnull查询普通页面或 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,可用值为 selfuuidentity_idvirtual
setEntityType(Player player, String pageId, String elementId, String entityType)玩家、页面 ID、实体组件 ID、实体类型 IDvoid修改虚拟实体的 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.orthographicfalse 为默认 30 度透视投影,true 为正交投影
setTrackMouse(Player player, String pageId, String elementId, boolean trackMouse)玩家、页面 ID、实体组件 ID、是否追踪鼠标void修改 entity.trackMouse

entityUuid 应在页面会话建立后调用,其返回值由实体来源决定:

entity.sourceUUID 来源
self目标玩家的真实 UUID
uuidentity.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 协议版本

客户端就绪示例

java
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()ItemStacknull操作前的服务端光标物品,返回副本
getSlotItemBefore()ItemStacknull操作前的槽位物品,返回副本
getCursorItemAfter()ItemStacknull通过 setter默认计算后的光标物品,返回副本
setCursorItemAfter(ItemStack item)void覆盖操作完成后写回的光标物品
getSlotItemAfter()ItemStacknull通过 setter默认计算后的槽位物品,返回副本
setSlotItemAfter(ItemStack item)void覆盖操作完成后写回的槽位物品
isCancelled()boolean通过 setter查询事件是否已取消
setCancelled(boolean cancelled)void取消后不写回本次操作结果

槽位事件示例

java
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 字符串,再由业务插件解析

自定义包事件示例

java
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 方式注册:

java
@Override
public void onEnable() {
    getServer().getPluginManager().registerEvents(new RewardSlotListener(), this);
    getServer().getPluginManager().registerEvents(new ChaUIPacketListener(), this);
    getServer().getPluginManager().registerEvents(new ChaUIReadyListener(), this);
}

服务端始终权威

不要根据客户端发来的字符串直接发放物品、执行命令或修改经济数据。业务插件仍需根据玩家、页面、会话和自身数据再次判断操作是否合法。