面向 Paper 服务端的 Lua 活动流程控制插件 。
为大型庆典 / 活动流程设计的「脚本式」控制方案:每个 .lua 脚本就是一个完整的事件流程,可以按时间 、按条件 、按管理员手动指令 自动执行控制台命令、喊话、传送、发物品等操作,管理员不再需要人工喊话和逐个传送玩家。
内置 Lua 解释器 :集成 luaj,随插件打包(已重定位,不污染服务器类路径),开箱即用。
脚本式流程控制 :一个脚本 = 一个活动流程,可随时 /flow start / /flow stop 启停。
四种触发方式 :
控制台命令 —— 脚本内直接执行服务器控制台级命令。
时间触发 —— 每天定点 / 指定日期单次 / 游戏内时间。
条件触发 —— 周期性检查自定义条件,满足时执行(一次性或边沿触发)。
手动触发 —— 管理员 /flow fire 手动触发脚本内注册的触发点。
自动运维 :全服广播 / Title / ActionBar / 音效 / 集体传送 / 集体发物品。
可靠错误处理 :定时任务出错自动取消防止刷屏;命令触发出错即时反馈给执行者。
权限组 :mcprocesscontrol.admin 权限组及细化子权限。
Paper 26.2+(api-version: 26.2)
Java 25+
gradle shadowJar
# 产物:build/libs/MCProcessControl-1.0.0.jar
将 build/libs/MCProcessControl-1.0.0.jar 放入服务器的 plugins/ 目录。
启动服务器。首次启动会自动生成示例脚本 plugins/MCProcessControl/scripts/example.lua。
编辑脚本,执行 /flow reload 生效。
命令
说明
/flow reload
重载全部脚本(有错误会逐个列出)
/flow list
列出已加载脚本及其触发点
/flow start <脚本>
启动指定脚本
/flow stop <脚本>
停止指定脚本(取消其所有定时任务)
/flow fire <脚本> <触发点>
手动触发脚本内的触发点
权限节点
说明
默认
mcprocesscontrol.admin
管理员权限组,包含以下全部子权限
op
mcprocesscontrol.reload
重载脚本
op
mcprocesscontrol.list
查看脚本列表
op
mcprocesscontrol.start
启动脚本
op
mcprocesscontrol.stop
停止脚本
op
mcprocesscontrol.fire
手动触发触发点
op
控制台不受权限限制;玩家拥有 mcprocesscontrol.admin 或对应子权限即可执行对应子命令。
脚本内通过全局对象 flow 调用所有接口。每个脚本必须定义 main() 函数,插件加载脚本时会执行它(在其中注册触发点、定时任务等)。
function main ()
-- 在这里注册触发点、定时任务、条件检测
end
方法
说明
flow.command(cmd)
以控制台身份执行服务器命令(flow.console 为别名)
flow.broadcast(msg)
全服广播消息
flow.tellraw(json)
按 Minecraft tellraw JSON 格式向全服发送富文本消息(支持颜色 / 加粗 / 点击 / 悬浮等,支持对象或数组)
flow.title(title, subtitle)
全服发送 Title
flow.actionbar(msg)
全服发送 ActionBar
flow.sound(name)
全服播放音效(如 entity_firework_rocket_launch)
flow.log(msg)
输出到服务器控制台日志
方法
说明
flow.after(ticks, fn)
延迟 ticks(20 tick = 1 秒)后执行一次,返回任务 id
flow.every(ticks, fn)
每 ticks 重复执行,返回任务 id
flow.at("HH:MM", fn)
每天该时刻执行一次
flow.atOnce("yyyy-MM-dd HH:mm", fn)
指定日期时间单次执行
flow.atMinecraft(hour, fn)
游戏内时间(Minecraft 时,0-23)执行一次
flow.cancel(id)
取消任务
flow.cancelAll()
取消本脚本全部任务
方法
说明
flow.when(condFn, checkTicks, actionFn)
每 checkTicks 检查一次 condFn,为真时执行 actionFn 一次 并自动取消
flow.whenever(condFn, checkTicks, actionFn)
边沿触发:条件由假变真时执行 actionFn
方法
说明
flow.trigger(name, fn)
注册触发点,管理员用 /flow fire <脚本> <name> 触发
flow.fire(name)
脚本内触发另一个触发点
方法
说明
flow.onlineCount()
在线玩家数
flow.players()
在线玩家名数组(可用 ipairs 遍历)
flow.player(name)
获取玩家对象(见下)
方法
说明
flow.teleportAll(world, x, y, z)
传送所有在线玩家到指定坐标
flow.giveAll(material, count)
给所有在线玩家发放物品(如 firework_rocket)
玩家对象 flow.player(name) 返回
方法
说明
p.name()
玩家名
p.online()
是否在线
p.send(msg)
向该玩家发送消息
p.teleport(world, x, y, z)
传送该玩家
p.execute(cmd)
以该玩家身份执行命令
p.x() / p.y() / p.z() / p.world()
坐标与世界,离线时返回 nil
function main ()
-- 手动触发点:/flow fire example start
flow .trigger (" start" , function ()
flow .broadcast (" §6活动正式开始!" )
flow .title (" 活动开始" , " §a欢迎参加" )
flow .sound (" entity_firework_rocket_launch" )
flow .teleportAll (" world" , 0 , 64 , 0 )
flow .giveAll (" firework_rocket" , 10 )
end )
-- 倒计时 10 秒后自动开始(注意 local 声明与赋值要分行,见注意事项)
flow .trigger (" countdown" , function ()
local n = 10
local task
task = flow .every (20 , function ()
n = n - 1
flow .actionbar (" §e活动将在 §b" .. n .. " §e秒后开始" )
if n <= 0 then
flow .cancel (task )
flow .fire (" start" )
end
end )
end )
-- 每天晚上 19:00 自动开始倒计时
flow .at (" 19:00" , function ()
flow .fire (" countdown" )
end )
-- 在线人数达到 20 人时提醒一次
flow .when (function ()
return flow .onlineCount () >= 20
end , 40 , function ()
flow .broadcast (" §e在线人数已达 20 人!" )
end )
-- VIP 玩家上线时提醒(边沿触发)
flow .whenever (function ()
return flow .player (" Starset_" ).online ()
end , 20 , function ()
flow .broadcast (" §bVIP 玩家上线了!" )
end )
-- tellraw 富文本广播(可点击跳转官网)
flow .trigger (" announce" , function ()
flow .tellraw (' {"text":"点击进入官网","color":"aqua","bold":true,"clickEvent":{"action":"open_url","value":"https://example.com"}}' )
end )
end
脚本位置 :plugins/MCProcessControl/scripts/*.lua,改完执行 /flow reload。
luaj upvalue 陷阱 :闭包引用局部变量时,local x 的声明与赋值必须分行 写,否则闭包内读到 nil:
local task -- 先声明
task = flow .every (20 , function () ... flow .cancel (task ) ... end ) -- 再赋值
错误处理 :定时 / 条件任务内部出错会自动取消该任务并写控制台日志;/flow fire 触发出错会把错误消息直接反馈给执行者。
线程模型 :所有 Lua 回调均在服务器主线程串行执行,无需担心并发问题,但请勿在回调中执行阻塞操作。
世界名 :flow.teleportAll / flow.player(...):teleport 中的世界名请使用真实世界名称(如默认主世界 world)。
本项目基于 GNU General Public License v3 发布,详见 LICENSE 。