Skip to content
On this page

内置动作、日志与音效 ​

ChaUI Script 的内置动作分为两类:客户端立即执行的本地动作,以及必须由服务端根据已保存页面确认后执行的服务端动作。

客户端只执行当前事件真实走到的分支,并把其中需要服务端参与的动作编号发出。服务端会用已经保存的页面事件和自定义方法重新确认编号,再执行对应动作;命令正文不会由客户端上传。页面 close 也遵循同样规则,reload 时使用旧页面快照确认关闭动作。

动作一览 ​

函数中文别名执行位置作用
log(message)日志(message)客户端本地向当前玩家聊天栏发送一条调试消息
sound(path)音效(path)客户端本地播放 ChaUI 资源目录中的 OGG 音效
keyPress(key)按键(key) / 模拟按键(key)客户端本地按照 Minecraft 原生键盘分发链模拟一次按下和释放
moveWorld(x, y, z)移动世界页面(x, y, z)客户端本地立即移动触发脚本的当前世界页面实例
moveWorld(x, y, z, milliseconds, type)移动世界页面(x, y, z, milliseconds, type)客户端本地使用 linear 或 ease_in_out 插值移动当前世界页面实例
stopTimer(name)停止计时器(name)客户端本地停止当前页面作用域内的同名计时器
close()关闭()服务端确认关闭当前页面
close(pageId)关闭(pageId)服务端确认关闭指定页面
open(pageId)打开(pageId)服务端确认打开另一个已存在页面
openSub(pageId)打开子页面(pageId)服务端确认在当前 GUI 上方打开一层子页面
closeAll()全部关闭()服务端确认关闭当前 GUI 的整条父子页面链
cmd(command)指令(command)服务端确认以玩家身份执行配置的业务动作
opcmd(command)OP指令(command)服务端确认临时使用更高权限执行配置的业务动作
consolecmd(command)控制台指令(command)服务端确认由服务端控制台执行配置的业务动作
setPlayerNameVisible(target, visible)设置玩家名称显隐(target, visible)服务端确认只对当前观看者设置目标玩家的原版名字显隐
setPlayerNameVisible(target, viewer, visible)设置玩家名称显隐(target, viewer, visible)服务端确认对 viewer 指定的观看者设置;viewer 写 all 时作用于所有观看者

原版菜单本地动作 ​

display.mode: vanilla 页面还可以调用 打开原版页面、加入服务器、断开服务器、退出游戏 和 确认原版操作。这些动作使用当前 Minecraft 客户端的真实页面与连接上下文;成功导航后会立即终止旧页面脚本,编辑器隔离预览不会真正执行。

参数、英文名、bypassReplacement、允许上下文以及不同目标中的 close() 行为集中维护在替换菜单:本地动作,请以该专题为准。

查询页面是否打开 ​

isPageOpen(pageId) / 界面已打开(pageId) 是返回布尔值的客户端本地查询,可以直接用于条件或赋给变量:

text
vars.shopOpen = isPageOpen("shop")
if 界面已打开("detail_popup") {
  log("详情子页面仍在显示")
}

查询覆盖普通界面、容器替换界面、GUI 子页面、HUD 和世界页面;同一 pageId 只要还有任意活动实例就返回 true,世界页面不区分 instanceId。页面从执行 open 生命周期开始算作打开,直到 close 生命周期结束并释放资源后才变为关闭。查询只读取当前客户端的活动页面,不发包、不修改状态;空白、非法或运行时不是字符串的参数安全返回 false。编辑器编辑状态和隔离预览不计入活动页面。

模拟原生按键 ​

keyPress(key)、按键(key) 和 模拟按键(key) 只在玩家客户端本地执行。它会把一次按下事件和一次释放事件交给 Minecraft 的原生 KeyboardHandler,因此效果等同于玩家实际按键:例如 keyPress("E") 会按原版逻辑打开背包,按键("ESCAPE") 会按原版逻辑关闭当前界面。事件不会发送给服务端,也不会提升权限;编辑器编辑状态和隔离预览会安全忽略此动作。

参数可以是字符串变量,例如 keyPress(vars.openKey)。键名使用大写 GLFW 风格名称,支持字母、数字、F1 至 F25、ESCAPE、ENTER、SPACE、方向键、LEFT_SHIFT、RIGHT_CONTROL 等常用键;非法键名只记录一次脚本警告,不影响后续语句。

text
keyPress("E")
按键(vars.confirmKey)

输出本地调试日志 ​

log 用于确认事件是否触发、条件是否进入预期分支,或查看变量当时的值。它只把消息显示在当前玩家的客户端聊天栏,不会发送给其他玩家,不会上传服务端,也不会写入服务端日志。

text
log("当前生命:{vals.player.health}/{vals.player.maxHealth}")
日志("当前计数:{vars.count + 1}")

消息使用与响应式文字相同的花括号规则:{vars.xxx}、{vals.player.xxx} 和 {vals.papi.xxx} 会在动作执行时读取最新值,花括号内也可以进行四则运算。连续输入两个左花括号 可以输出普通花括号;未知变量或无效表达式会保留原占位内容,方便继续排查。

log 必须且只能填写一个带引号的字符串参数,解析后的单条消息最多显示 1024 个字符。它可以用于组件事件、页面打开/关闭事件、自定义方法、repeat、timer 和编辑器预览;当前客户端玩家尚未建立时会安全忽略。

组件布局值不能直接写进日志花括号。需要查看组件位置时,可以先把值保存到页面变量:

text
vars.currentY = component("player_name_value").layout.y
log("当前 Y:{vars.currentY}")

播放本地音效 ​

脚本填写 ChaUI 素材根目录下的相对路径。普通页面从当前客户端的 resourcepacks/ChaUI/ 或 ChaAssets 读取;原版菜单替换页会先读取当前活动菜单包中的同路径素材。

text
sound("sounds/click.ogg")
音效("sounds/notice.ogg")

第一版只支持 OGG。路径不能使用绝对路径、反斜杠、路径穿越、空白路径或控制字符。音效只在触发脚本的玩家客户端播放,sound() 动作本身不会临时发送文件;原版菜单需要同步的短音效应预先放入 vanilla-menu-assets/** 并随完整页面包发布,具体见页面包与素材。

移动当前世界页面 ​

世界页面脚本可以直接移动触发脚本的当前实例,不需要先关闭再重新打开:

text
moveWorld(120.5, 65, -30)
moveWorld(125, 66, -28, 800, "ease_in_out")
移动世界页面(130, 66, -25, 500, "linear")

三参数形式立即移动。五参数形式的毫秒时长范围为 1 至 3600000,插值类型只支持匀速 linear 和平滑起停 ease_in_out。新移动会立即停止当前实例尚未完成的旧移动,并从屏幕中当时显示的位置开始。动作只修改当前会话,不写回页面 yml;普通 screen、HUD、容器页面和编辑器预览调用时会安全忽略。

固定世界页面的三个数字是世界坐标。跟随玩家或实体时,它们表示相对目标脚底的世界轴偏移,单位为格,例如 moveWorld(0, 2.4, 0) 将页面移到目标脚底上方 2.4 格,同时继续跟随目标。跟随页面的创建方式见完整页面与指令示例。

打开和关闭页面 ​

text
close()
close("detail_page")
open("detail_page")
openSub("detail_popup")
closeAll()
text
关闭()
关闭("detail_page")
打开("detail_page")
打开子页面("detail_popup")
全部关闭()

目标页面 ID 必须是服务端已经保存并可以正常加载的页面。页面 ID 建议只使用字母、数字、下划线、连字符或规范的 Unicode 字符,不要填写路径或文件扩展名。

openSub 只把 display.mode=screen 页面打开到当前普通 GUI 或容器替换 GUI 上方。父页面与子页面默认可以同时交互;若要阻止下层点击,请在子页面中放置全屏 rect 并设置 pointerEvents: block。close() 关闭触发它的当前页面,closeAll() 从栈顶到根页面关闭整条 GUI 链,但不会关闭 HUD 或世界页面。完整配置见在 GUI 上打开子页面。

普通 close() 或 closeAll() 执行后会终止当前脚本,后续语句不会运行。需要在页面关闭后继续一次有限的跳转或指令流程时,使用可跨页面关闭的一次性 task,不要把 delay 或 timeout 直接接在 close() 后面。

执行业务动作 ​

页面可以请求服务端以不同身份执行已经规划好的业务动作:

text
cmd("执行玩家确认动作")
opcmd("执行需要额外权限的页面动作")
consolecmd("执行服务端页面结算动作")

这些示例中的文字代表需要由服务器作者替换的业务内容。不要把玩家输入内容直接拼接进高权限或控制台动作,也不要让普通页面触发未经过权限设计的管理功能。

cmd 经服务端确认后,由客户端发送原版玩家命令包,处理路径与玩家在聊天框输入命令一致。因此 BungeeCord 或 Velocity 的 server、hub 等代理端命令也可以正常拦截,例如 cmd("server cz")。

opcmd 仍由当前后端服务器临时以 Bukkit OP 身份执行,只提升后端权限,不会获得代理端管理员权限。consolecmd 仍由当前后端控制台执行。需要调用无需额外权限的代理命令时使用 cmd。

控制玩家原版名字 ​

这组动作控制玩家头顶的原版名字。目标与观看者可以填写固定玩家名、完整 UUID,或代表触发脚本玩家的 self。省略观看者时只影响当前观看者;写成 all 时影响所有观看者,包括后续进服的玩家,也可以指定另一名在线观看者。

text
// Steve 点击按钮后,只让 Steve 看不到 Alex 的原版名字
setPlayerNameVisible("Alex", false)

// 让所有观看者都看不到 Alex 的原版名字
setPlayerNameVisible("Alex", "all", false)

// 只让 Steve 恢复看到 Alex 的原版名字
setPlayerNameVisible("Alex", "Steve", true)

false 表示隐藏,true 恢复 Minecraft 原有显示规则,不会强行显示原本被潜行或队伍规则隐藏的名字,也不影响 Tab 列表。全体设置会覆盖该目标已有的特定观看者设置;之后仍可单独调整某位观看者。状态相同的重复调用不会发包。

下面两个按钮可添加到普通页面的 elements 中。保存并打开页面后,点击第一个按钮会让所有观看者看不到你的原版名字,点击第二个按钮恢复:

yaml
- id: hide_my_name
  type: button
  layout:
    x: 20
    y: 20
    width: 140
    height: 24
  button:
    label: 隐藏我的原版名字
  events:
    leftClick: 'setPlayerNameVisible("self", "all", false)'
- id: show_my_name
  type: button
  layout:
    x: 20
    y: 52
    width: 140
    height: 24
  button:
    label: 恢复我的原版名字
  events:
    leftClick: 'setPlayerNameVisible("self", "all", true)'

这项状态独立于页面,关闭页面或执行 /chaui reload 不会自动恢复。目标退出会清理该目标的全部设置,观看者退出会清理其单独设置。目标或指定观看者不在线时,动作不生效。必须使用字面量字符串和布尔值,不能把输入框内容或变量传入这组动作;动作需先保存页面再执行,不支持编辑器隔离预览或页面关闭后的延迟任务。

close、open、openSub、closeAll、cmd、opcmd、consolecmd、setPlayerNameVisible 不能写在 repeat 或 timer 块内,避免循环批量触发服务端动作。计时器的启动、停止、同名重启和页面关闭清理请阅读循环与计时器。

条件动作示例 ​

yaml
events:
  leftClick: |
    if vars.ready == true {
      sound("sounds/confirm.ogg")
      open("result_page")
    } else {
      component("message").text.value = "请先完成选择"
      sound("sounds/warning.ogg")
    }

本地文字和音效由当前客户端执行;open 则会提交事件触发意图,由服务端确认后打开页面。

服务端如何确认动作 ​

客户端触发事件时,不会把指令内容或整段脚本作为可信动作直接交给服务端。服务端会校验玩家当前页面会话,再从已经保存的页面配置中找到对应组件和事件脚本,只执行其中允许的内置服务端动作。

因此,测试 close、open、cmd、opcmd、consolecmd、setPlayerNameVisible 前必须先保存页面。编辑器中尚未保存的动作不会成为服务端执行依据。所有动作都必须使用本页列出的函数写法。

需要在编辑器单行输入框中执行多个动作时,可以使用分号:

text
sound("sounds/click.ogg"); open("detail_page")

所有动作都使用“函数名 + 括号”的形式。文字参数放在引号中,无参数动作也保留空括号,这样编辑器才能准确识别动作边界。

需要与其他插件传递自定义数据时,可以在 packet() / 发包() 的数据参数中直接使用响应式模板:

text
packet("shop:select", "{vars.itemId}", "{vars.amount}", "{vals.player.health}")

第一个包 ID 必须是固定的字面量字符串,不会执行变量替换;从第二个参数开始,会在发送前按与 log() 相同的规则展开页面变量、只读常量和表达式。需要输出普通左花括号时,连续写两个左花括号。完整限制和服务端校验方式请继续阅读自定义发包与安全边界。

组件事件及其调用的自定义方法中,log 与 packet 数据参数也可以读取组件引用:

text
log("自身={self.id},父级={parent.id},首项={parent.child(0).id}")
packet("debug:component", "{self.id}", "{parent.child(0).id}")

页面级事件没有组件上下文,无法求值的 {self.id} 会原样保留;包 ID 仍不会插值。