概览
每个启用的 .lua 文件在独立沙箱中执行。一个文件可以声明多个模块;模块与原生 Java 模块共享注册、配置和事件系统。
绘制矩形、圆形、渐变、裁剪区域、文本、实体屏幕框、世界方框、连线和追踪线。
读取完整鼠标输入、修改移动、管理客户端与服务端视角,并在限速保护下执行玩家动作。
脚本可读取真实模块和设置快照,并通过受限接口开关模块或修改设置。
快速开始
- 01打开脚本目录
在游戏聊天中执行
.lua folder,目录为当前游戏目录下的setsuna/scripts。 - 02创建 UTF-8 脚本
新建以
.lua结尾的普通文件。以.disabled结尾的示例不会自动加载。 - 03重载并启用模块
执行
.lua reload,然后在对应 ClickGUI 分类中启用脚本模块。
local module = setsuna.module({
id = "hello_lua",
name = "Hello Lua",
category = "misc"
})
module:on("enable", function()
client:notify("模块已启用", "Hello Lua", "success")
end)
module:on("tick", function()
if player:is_available() and input:is_down("left_alt") then
action:sprint(true)
end
end)
模块声明
setsuna.module(metadata) 创建动态模块并返回模块句柄。设置和事件都通过这个句柄注册。
setsuna.module(metadata: table) → Moduleidstring稳定配置 ID,转为小写 ASCII slug,最长 64 字符;所有 Java/Lua 模块之间必须唯一。
namestringClickGUI 与 Array List 中的显示名,长度 1 至 96 字符。
categorystring省略时为 misc,不区分大小写。
可用分类
combatmiscrendermovementplayerclient内部 hud 分类不能用于 Lua 模块。同一脚本可多次调用 setsuna.module 声明多个模块。
事件回调
使用 module:on(event, callback) 注册回调。第一个参数始终是模块句柄;带上下文的事件会把第二个参数作为普通 Lua 表传入。
module:on(event: string, callback: function) → Moduleenable / disable无模块启用或关闭时各调用一次。
tick无启用期间每个客户端游戏刻结束后调用。
render2dDraw2D每帧 2D 覆盖层绘制,坐标已经换算为 GUI 缩放空间。
render3dDraw3D每帧世界渲染后调用,使用绝对世界坐标。
key / char / mousetable按键、Unicode 字符或鼠标按钮状态变化。将 event.cancel = true 可吞掉本次输入。
mouse_scrolltable鼠标滚轮输入,提供水平/垂直滚动量以及光标坐标,可取消原版滚动。
inputtable可改写 forward、strafe、jump、sneak、sprint。
movetable可改写移动向量 x/y/z;设置 cancel = true 会把本次移动归零。
attackEntity本地玩家攻击前触发,第二个参数是目标实体快照。该回调内禁止再次发起攻击。
输入事件字段
keykey、scancode、action、modifiers、state、pressed、released、repeat、cancel
mousebutton、action、modifiers、state、pressed、released、GUI 坐标 x/y、raw_x/raw_y、cancel
charcodepoint、text、allowed、cancel
mouse_scrollhorizontal、vertical、x/y、raw_x/raw_y、cancel
input移动值会限制在 -1 到 1;布尔字段必须保持为布尔值。
设置 API
设置必须在脚本加载阶段声明。它们自动显示在 ClickGUI 中,并按模块 ID 与设置名写入当前配置。
module:booleanSettingname, default[, label]
module:integerSettingname, default, min, max[, step[, label]],步长必须大于 0。
module:numberSettingname, default, min, max[, step[, label]],所有数值必须有限。
module:stringSettingname, default[, label],值最长 4096 字符。
setting:get()读取当前设置值。
setting:set(value)写入并返回最终值;数字自动限制在声明范围内。
setting:reset()恢复脚本声明的默认值并返回结果。
local boxes = module:boolean("boxes", true, "显示方框")
local range = module:integer("range", 24, 4, 64, 1, "扫描范围")
local size = module:number("font_size", 14, 8, 32, 0.5, "字号")
local title = module:string("title", "Lua HUD", "标题")
通用 API
Client
client:notify(message[, title[, type]])nil发送客户端通知。类型支持 info、success、warning、error。
client:username()string当前会话用户名。
client:server()string当前服务器地址,单人游戏返回 singleplayer。
client:categories()Category[]返回 ClickGUI 可见分类的 id/name 快照。
client:modules([category[, includeHidden]])Module[]枚举真实模块;默认只含 GUI 分类,传具体分类 ID 可筛选。
client:module(id)Module | nil读取一个模块及其完整设置快照。
Player
player:is_available()boolean玩家和客户端世界都已加载时为 true。
player:x() / y() / z()number | nil本地玩家世界坐标。
player:health()number | nil本地玩家生命值。
player:yaw() / pitch()number | nil本地玩家当前视角。
player:server_yaw() / server_pitch()number | nil旋转管理器将写入正常移动包的服务端视角。
player:rotation()table | nil一次返回客户端与服务端 yaw/pitch 以及 silent 状态。
player:entity()Entity | nil返回与世界查询相同格式的本地玩家快照。
颜色
setsuna.color(value) / setsuna.color(r, g, b[, a]) → packed ARGB integer接受 #RRGGBB、#AARRGGBB、32 位整数或 {r, g, b, a} 表。setsuna.color_table(color) 可拆回通道表。
local cyan = setsuna.color("#58DCE5")
local translucent = setsuna.color("#4058DCE5")
local red = setsuna.color(255, 70, 70, 220)
local channels = setsuna.color_table(cyan)
2D 绘制
render2d 的第二个参数是当帧有效的 Draw2D 表。坐标原点在左上角,单位为 GUI 缩放后的像素。
width / height / scalenumber当前 GUI 画布尺寸与 GUI 缩放倍数;mouse_x/mouse_y/mouse_grabbed 是当帧鼠标快照。
draw:rect(x, y, w, h, color)nil填充矩形。负宽高会自动翻转原点。
draw:rounded_rect(x, y, w, h, radius, color)nil填充圆角矩形。
draw:outline_rect(x, y, w, h, color[, thickness[, radius]])nil描边矩形,默认线宽 1、圆角 0。
draw:gradient_rect(x, y, w, h, start, end[, vertical[, radius]])nil绘制水平或垂直双色渐变。
draw:circle(...) / circle_outline(...)nil按圆心和半径绘制圆或圆环。
draw:line(x1, y1, x2, y2, color[, thickness])nil绘制抗锯齿线段,默认线宽 1。
draw:text(text, x, y, color[, size[, shadow]])nil绘制客户端字体,默认字号 14、阴影开启。
draw:text_width(text[, size])number测量当前客户端字体宽度。
draw:bold_text(...) / bold_text_width(...)nil / number粗体文字绘制与测量,参数顺序和普通文字一致。
draw:clip(x, y, w, h, callback)nil只在指定矩形内执行绘制回调;裁剪状态即使报错也会恢复。
draw:world_to_screen(x, y, z)table | nil投影世界坐标,返回 x/y/depth/visible;点在相机深度范围外时返回 nil。
draw:entity_bounds(entityId[, padding])table | nil返回实体当前碰撞箱的屏幕 x/y/width/height/min/max/visible。
local accent = setsuna.color("#58DCE5")
local panel = setsuna.color("#D9101519")
module:on("render2d", function(self, draw)
local label = string.format("XYZ %.1f / %.1f / %.1f",
player:x(), player:y(), player:z())
local width = draw:text_width(label, 14) + 24
draw:rounded_rect(12, 12, width, 38, 5, panel)
draw:outline_rect(12, 12, width, 38, accent, 1, 5)
draw:text(label, 24, 21, accent, 14, true)
end)
3D 绘制
render3d 使用绝对世界坐标,底层会自动转换为相机相对坐标。颜色可以传 nil,从而只绘制填充或描边。
draw:filled_box(minX, minY, minZ, maxX, maxY, maxZ, color)nil绘制填充世界方框。
draw:outline_box(..., color[, thickness])nil绘制方框轮廓,默认线宽 1.5。
draw:box(..., fill, outline[, thickness])nil一次绘制填充和轮廓,任一颜色可为 nil。
draw:gradient_box(..., bottom, top[, outline[, thickness]])nil绘制上下双色渐变世界方框,可附加轮廓。
draw:block_box(x, y, z, fill, outline[, thickness])nil绘制一个完整方块空间。
draw:entity_box(entityId, fill, outline[, thickness])boolean按实体 ID 绘制当前碰撞箱;实体已消失时返回 false。
draw:entity_gradient_box(entityId, bottom, top[, outline[, thickness]])boolean按实体 ID 绘制上下渐变碰撞箱。
draw:line(x1, y1, z1, x2, y2, z2, color[, thickness])nil绘制世界空间线段。
draw:tracer(entityId, color[, thickness])boolean从当前相机连接到实体碰撞箱中心。
世界与实体
世界 API 是只读接口,只能在模块回调内调用。未加载区块不会被强制加载,单点方块查询会返回 nil。
world:block(x, y, z)Block | nil返回 x/y/z/id/air/solid/replaceable/fluid。
world:block_id(x, y, z)string | nil返回完整方块 ID,例如 minecraft:diamond_ore。
world:is_air(x, y, z)boolean | nil判断已加载位置是否为空气。
world:entities(radius[, filter[, limit]])Entity[]枚举本地玩家附近实体。半径最多 256、结果最多 512,默认上限 128。
world:entity(id)Entity | nil按运行时实体 ID 获取最新快照。
world:find_blocks(idOrList[, radius[, limit]])Block[]在玩家周围立方体搜索一个或多个方块 ID;半径最大 16、结果最多 512。
world:dimension()string | nil当前维度 ID。
world:time()integer | nil当前主世界时钟时间。
实体筛选与快照
allplayerslivingmobsanimalsminecraft:zombieEntity 包含 id、uuid、name、type、坐标、视角、尺寸、速度、碰撞箱、距离和类型标记;生物额外包含 health、max_health、armor。
module:on("tick", function()
local ores = world:find_blocks({
"diamond_ore", "deepslate_diamond_ore"
}, 12, 64)
local targets = world:entities(32, "players", 32)
if #ores > 0 and #targets > 0 then
print(ores[1].id, targets[1].name)
end
end)
输入与动作
输入状态
input:is_down(key)boolean读取 GLFW 键。可传数字或名称,例如 space、left_shift、F6。
input:mouse_down(button)boolean读取 GLFW 鼠标按钮,左键为 0、右键为 1。
input:cursor()table返回 GUI x/y、raw_x/raw_y、grabbed 与 inside。
input:cursor_grabbed()boolean鼠标当前是否被游戏视角抓取。
input:key_code(name)integer把键名转换为 GLFW 键码。该纯转换方法可在脚本加载阶段缓存。
受限动作
action:jump()boolean触发本地玩家起跳。
action:rotate(yaw, pitch)boolean设置视角;pitch 自动限制到 -90 至 90。
action:rotate_silent(yaw, pitch[, priority])boolean通过旋转管理器把视角写入下一次正常移动包,不改变相机;持续旋转应每 tick 续约。
action:send_rotation(yaw, pitch[, onGround[, collision]])boolean立即发送一个旋转包。它不会代替正常移动同步,不应在同 tick 重复调用。
action:look_at(x, y, z[, mode[, priority]])boolean朝向世界坐标;模式为 client、silent 或 packet。
action:release_rotation()boolean释放当前 Lua 模块持有的静默旋转。
action:cursor_grab([grabbed])boolean抓取或释放鼠标;模块关闭、报错和重载时自动清理所有权。
action:set_velocity(x, y, z)boolean设置速度,每个分量限制到 -10 至 10。
action:sprint([enabled])boolean开启或关闭疾跑,省略参数时开启。
action:swing([hand]) / use([hand])boolean挥动或使用 main/off 手。
action:attack(entityId)boolean攻击已加载且存活的实体,并挥动主手。
action:select_slot(slot)boolean选择 Lua 风格热栏槽位 1 至 9。
action:send_chat(message)boolean发送一行聊天,最长 256 字符。
action:send_command(command)boolean发送命令,可带或不带开头的斜杠。
action:module_toggle(id) / module_set(id, enabled)boolean触发或设置真实客户端模块。
action:setting_set(moduleId, settingId, value)boolean按快照中的稳定设置 ID 写入经类型与范围校验的值。
action:setting_cycle(...) / setting_press(...)boolean切换布尔/枚举/字体、步进数值或执行按钮设置。
local jumpKey = input:key_code("J")
local targetKey = input:key_code("R")
module:on("key", function(self, event)
if not event.pressed then return end
if event.key == jumpKey then
event.cancel = true
action:jump()
elseif event.key == targetKey then
local targets = world:entities(6, "living", 1)
if targets[1] then action:attack(targets[1].id) end
end
end)
module:on("input", function(self, movement)
if input:is_down("left_alt") then
movement.sprint = true
end
end)
管理命令
.lua list列出成功加载的脚本及各自模块数量。
.lua reload保存配置、卸载并重新加载所有脚本,然后恢复配置状态。
.lua errors显示最近的脚本加载或回调错误,聊天中最多显示最近 10 条。
.lua folder在系统文件管理器中打开脚本目录。
安全与限制
所有新运行时 API 只能在模块回调内使用;保存上下文并在后续回调重用会被拒绝。限制按回调或按脚本计算。
io · os · package · debug · luajava · dofile · loadfile · require
2D 最多 4096 次绘制,3D 最多 2048 次绘制;单个 3D 方框边长最多 512。
entities 与 find_blocks 共用回调限额;方块搜索半径最多 16,结果最多 512。
每回调最多 64 个动作;每脚本每秒最多 80 个动作、20 次攻击、5 条聊天或命令。
完整视觉脚本
这个示例每 10 tick 缓存附近生物,每帧只使用实体 ID 绘制方框,同时显示玩家坐标 HUD。它与首次生成的示例结构一致。
local module = setsuna.module({
id = "lua_visual_example",
name = "Lua 视觉示例",
category = "render"
})
local showBoxes = module:boolean("show_boxes", true, "显示实体方框")
local range = module:integer("range", 24, 4, 64, 1, "扫描范围")
local accent = setsuna.color("#58DCE5")
local fill = setsuna.color("#2458DCE5")
local background = setsuna.color("#D914191E")
local ticks = 0
local nearby = {}
module:on("enable", function()
client:notify("视觉示例已启用", "Lua", "success")
end)
module:on("tick", function()
ticks = ticks + 1
if ticks % 10 == 0 and player:is_available() then
nearby = world:entities(range:get(), "living", 64)
end
end)
module:on("render2d", function(self, draw)
if not player:is_available() then return end
local label = string.format("Lua HUD | XYZ %.1f %.1f %.1f",
player:x(), player:y(), player:z())
local width = draw:text_width(label, 14) + 24
draw:rounded_rect(12, 12, width, 38, 5, background)
draw:outline_rect(12, 12, width, 38, accent, 1, 5)
draw:text(label, 24, 21, accent, 14, true)
end)
module:on("render3d", function(self, draw)
if not showBoxes:get() then return end
for _, entity in ipairs(nearby) do
draw:entity_box(entity.id, fill, accent, 1.5)
end
end)
问题排查
脚本没有出现在 ClickGUI−
确认文件以 .lua 结尾、至少调用一次 setsuna.module,然后执行 .lua errors 查看加载错误。
模块启用后立即关闭+
某个回调抛出了错误。执行 .lua errors,阶段名会显示为 模块ID.事件名。
提示 API 只能在回调中使用+
world、运行时 input 和 action 不能在脚本顶层执行。把调用移入模块回调;只有 input:key_code 可在顶层缓存。
渲染回调提示禁止游戏动作+
渲染回调只负责绘制。记录状态或目标 ID,再在 tick 或输入回调中调用 action。
实体框偶尔返回 false+
缓存期间实体可能死亡、卸载或更换 ID。这是正常情况;下一次 world 查询会刷新列表。
重载后设置值丢失+
保持模块 id 与设置 name 不变,它们是配置恢复使用的稳定键;显示用 label 可以修改。