[MD] ## 📋 版本信息 - **版本号**: 1.6.0 - **发布日期**: 2026年7月8日 --- ## ✨ 新增功能 ### 1. 新增全局 actions 动作包 KaMenu 现在支持在 `plugins/KaMenu/actions/` 目录中放置全局 actions 动作包。一个 `.yml` 文件就是一个动作包,包 ID 为相对路径去掉 `.yml` 后缀。 ```text plugins/KaMenu/actions/reward/daily.yml -> reward/daily ``` 动作包文件格式: ```yaml actions: - 'toast: type=task;msg=领取成功;icon=emerald' - 'money: type=add;num={arg:0}' ``` 任意菜单动作列表、可点击文本动作、周期任务和自定义注册指令都可以调用: ```yaml - 'actions: reward/daily,100' ``` 查找优先级: 1. 当前菜单内的 `Events.Click.<动作列表名称>` 2. 全局动作包 `plugins/KaMenu/actions/<动作列表名称>.yml` 因此菜单内 `Events.Click` 与全局动作包同名时,会优先执行菜单内动作列表。 ### 2. 新增全局 JavaScript 包 KaMenu 现在支持在 `plugins/KaMenu/js/` 目录中放置全局 JavaScript 包。一个 `.js` 文件就是一个包,包 ID 为相对路径去掉 `.js` 后缀。 ```text plugins/KaMenu/js/reward/message.js -> reward/message ``` 包文件内容就是 JavaScript 代码: ```javascript var amount = args[0] || "0"; var target = args[1] || name; "恭喜 " + target + " 获得 " + amount + " 金币"; ``` 可在动作、文本、条件中调用: ```yaml - 'js: [reward/message],100,Steve' text: '提示:{js:[reward/message],100,Steve}' condition: '{js:[check/vip]} == true' ``` 查找优先级: 1. 当前菜单内的 `JavaScript.<包名>` 2. 全局 JavaScript 包 `plugins/KaMenu/js/<包名>.js` 因此菜单内 `JavaScript` 与全局 JS 包同名时,会优先执行菜单内脚本。 ### 3. 自定义注册指令支持参数 Tab 补全 `config.yml` 的 `custom-commands` 对象写法现在支持 `args` 键,用于给玩家输入指令参数时提供 Tab 补全候选项: ```yaml custom-commands: test2: args: 0: "[tp, tphere]" 1: "%kamenu_online_players%" actions: - condition: "{arg:0} == tp" allow: - "tell: 你将传送到 {arg:1}" ``` - `args` 使用从 `0` 开始的索引,与动作变量 `{arg:0}`、`{arg:1}` 保持一致 - 候选项支持 YAML 列表、逗号分隔字符串、`[a, b]` 简易列表 - 支持实时解析 PAPI,例如 `%kamenu_online_players%` - 支持实时解析 KaMenu 内置变量,例如 `{list:friends}`、`{glist:warps}` - 打开菜单的对象写法也支持 `menu: 菜单ID` + `args` --- [/MD]
[MD] ## ✨ 新增功能 ### 1. 新增输入捕获全局空格处理配置 `config.yml` 新增 `input-capture.trim-edge-spaces`: ```yaml input-capture: trim-edge-spaces: false ``` 开启后,玩家提交文本输入框时,KaMenu 会在写入 `$(输入键名)` 前自动移除内容前后的空格。 适合玩家名、指令参数、数字、ID、权限节点等对首尾空格敏感的输入场景。 ### 2. 文本输入框新增 `remove_chars` `Inputs` 的 `type: input` 文本输入框现在可以配置 `remove_chars`,用于删除玩家输入中的指定字符。 ```yaml Inputs: player_name: type: 'input' text: '&a请输入玩家名' remove_chars: '&_' ``` 也支持列表写法: ```yaml Inputs: command_arg: type: 'input' text: '&a请输入参数' remove_chars: - '&' - '_' - '"' ``` ### 3. `remove_chars` 支持特殊转义字符 为了处理不可见字符,`remove_chars` 现在支持以下转义写法: | 写法 | 含义 | |------|------| | `\s` | 普通空格 | | `\n` | 换行 | | `\r` | 回车 | | `\t` | Tab | | `\\` | 反斜杠 `\` | 例如移除颜色符号、下划线、空格和换行: ```yaml Inputs: command_arg: type: 'input' text: '&a请输入参数' remove_chars: - '&' - '_' - '\s' - '\n' ``` ### 4. 新增全局字符移除列表 `config.yml` 的 `input-capture` 下新增 `remove-char-lists`,可以集中定义常用输入过滤规则: ```yaml input-capture: remove-char-lists: global: - '&' - '_' - '\s' - '\n' ``` 菜单内可直接引用预设名: ```yaml Inputs: command_arg: type: 'input' text: '&a请输入参数' remove_chars: global ``` 如果 `remove_chars` 的字符串值匹配全局预设名,则使用该预设;如果没有匹配到预设名,则继续按旧规则作为字面字符集合处理。 --- ## 🔧 优化与加固 ### 1. 输入清洗统一进入变量上下文 输入清洗发生在 Paper Dialog 回调捕获输入后、写入 `variables` 前。 因此后续读取同一个 `$(key)` 的位置都会拿到一致的处理结果,包括: - 动作列表 - 条件判断 - JavaScript 参数 - 动作包参数 - 外部 action handler 收到的 variables ### 2. 保持旧菜单兼容 `input-capture.trim-edge-spaces` 默认关闭,旧菜单不会因为升级自动改变输入内容。 未配置 `remove_chars` 的文本输入框也会保持原样输入。 --- [/MD]
[MD] ## ✨ 新增功能 ### 1. 新增 `isNull` 条件方法 用于判断变量、PAPI 或输入值是否为空。 ```yaml condition: "isNull.%some_placeholder%" condition: "isNull.{data:nickname}" condition: "isNull.$(nickname)" condition: "!isNull.$(nickname)" ``` `isNull` 会将以下值视为空: - 空字符串 - 仅包含空格的字符串 - `null` 字符串(不区分大小写) 适合用于表单校验、数据是否存在判断、PAPI 返回值兜底等场景。 ### 2. 新增 `isPass` 条件方法 用于判断值是否为“绝对空字符串”。 ```yaml condition: "isPass.$(nickname)" condition: "!isPass.$(nickname)" ``` `isPass` 只有在字符串长度为 `0` 时才通过;如果玩家输入一个或多个空格,则不会通过。需要将空格也视为空时,请使用 `isNull`。 ### 3. 新增 `isTrue` 条件方法 用于判断变量是否为真值。 ```yaml condition: "isTrue.$(agree)" condition: "isTrue.{data:enabled}" condition: "isTrue.%some_placeholder%" condition: "!isTrue.$(agree)" ``` `isTrue` 接受: - `true`(不区分大小写) - `yes`(不区分大小写) - `1` 其他值、空值和空格字符串都视为 false。 ### 4. 新增 `getLength` 字符串长度函数 `getLength` 用于输出字符串长度,通常配合比较运算符使用。 ```yaml condition: "getLength.%player_name% >= 3" condition: "getLength.$(nickname) <= 12" condition: "getLength.{data:nickname} == 0" ``` `getLength.xxx` 会先解析变量,再计算解析后字符串的长度。适合用于限制昵称、留言、搜索关键字等输入长度。 --- ## 🔧 优化与加固 ### 1. 条件方法参数解析增强 条件引擎现在会保护 `isNull`、`isPass`、`isTrue` 和 `getLength` 的参数内容,避免空字符串或空格字符串在表达式解析阶段被丢失。 这使以下场景可以按预期工作: ```yaml condition: "isPass.$(nickname)" condition: "isNull.$(nickname)" condition: "getLength.$(nickname) > 12" ``` ### 2. 明确条件变量解析顺序 文档补充了条件判断的运行顺序:KaMenu 会先解析 `{data:*}`、`{gdata:*}`、`{meta:*}`、`%papi%`、`$(input)`、`{arg:*}` 等变量,再将解析后的结果交给条件表达式引擎和内置方法判断。 ### 3. 条件表达式注入防护 运行时变量值现在会作为完整字符串参与条件判断,不会被重新解释为 `||`、`&&`、比较符、括号等表达式语法。 这可以避免玩家通过输入内容改变条件逻辑,例如: ```yaml condition: "$(amount) <= 100" ``` 如果玩家输入 `1 || true`,该输入会作为普通字符串值处理,不会让条件表达式被改写为逻辑或判断。 [/MD]
[MD] ## ✨ 新增功能 ### 1. 新增动态按钮列表 `type: repeat` `Bottom.multi.buttons` 现在支持 `type: repeat`,可根据动态数据源生成真实 Paper Dialog 按钮,解决玩家列表、传送点列表、好友列表、邮件列表等项目数量不固定时难以预先设计按钮数量的问题。 ### 2. 支持多种 repeat 数据源格式 `source` 支持以下数据形式: - `[函数名]`:调用当前菜单 `JavaScript` 中的函数,函数返回 JSON 数组 - JSON 数组字符串:例如 `[{"id":"home","name":"家"}]` - 变量解析结果:例如 `{data:warp_list}`、`{gdata:server_list}`、PlaceholderAPI 变量 - 多行文本:每行作为一个列表项 - 简单字符串列表:配合 `split` 自定义分隔符拆分 ### 3. 新增 repeat item 变量 repeat item 模板中可使用: - `{item.xxx}`:当前对象字段 - `{item.value}`:字符串或数字项的值 - `{item.index}`:完整列表下标,从 0 开始 - `{item.number}`:完整列表序号,从 1 开始 - `{item.page_index}`:当前页下标,从 0 开始 - `{item.page_number}`:当前页序号,从 1 开始 ### 4. 新增分页状态与 `page:` 动作 KaMenu 会按玩家、菜单上下文和列表 ID 维护分页状态,并提供分页变量: - `{page:listId}`:当前页 - `{pages:listId}`:总页数 - `{total:listId}`:总项目数 - `{start:listId}`:当前页起始下标 - `{end:listId}`:当前页结束下标 分页动作: ```yaml - "page: warp_list next" - "page: warp_list prev" - "page: warp_list 1" - "page: warp_list +1" - "page: warp_list -1" ``` `page:` 动作只修改分页状态,不会自动刷新界面。通常需要紧跟 `reset`、`open` 或 `force-open`。 ### 5. 新增内置列表数据 `list` / `glist` KaMenu 现在支持持久化列表数据类型,可用于好友列表、传送点列表、收藏列表、服务器列表等动态数据。 ```yaml - 'list: type=set;key=friends;var=`Steve,Alex`;split=,' - 'list: type=add;key=friends;var=`Notch`' - 'list: type=add;key=history;var=`Notch`;unique=false' - 'list: type=remove;key=friends;var=`Alex`' - 'glist: type=set;key=servers;var=`survival,skyblock`;split=,' ``` 读取变量: - `{list:key}`:读取当前玩家列表,返回 JSON 数组字符串 - `{glist:key}`:读取全局列表,返回 JSON 数组字符串 - `%kamenu_list_key%` / `%kamenu_glist_key%`:通过 PlaceholderAPI 读取列表 JSON 列表变量可以直接作为动态按钮 `repeat.source` 使用: ```yaml source: "{list:friends}" ``` --- ## 🔧 优化与加固 ### 1. 动作变量解析增强 动作包参数和非 item 模板变量仍可使用 `$(key)`;repeat item 字段统一使用 `{item.xxx}`,可直接写入按钮文字、tooltip 和 actions。 ### 2. 外部内存菜单分页上下文兼容 repeat 分页状态会沿用当前菜单 contextId。通过 `KaMenuAPI.openYaml` / `openConfig` 打开的内存菜单,也可以正常使用 repeat 列表和 `page:` 动作。 ### 3. 周期任务动作上下文继承 `Events.Tasks` 和 `on_end` 动作现在会继承菜单 contextId,避免任务内使用 `page:` 等上下文动作时状态错位。 ### 4. 分页状态清理 玩家退出服务器或插件关闭时,会清理该玩家的动态列表分页状态,避免长期运行服务器出现无用状态累积。 ### 5. `/km action` 动作测试指令补全 `/km action` 现在使用完整动作链执行入口,并补齐服务端动作前缀 Tab 补全,包括 `chat:`、`server:`、`stock-item:`、`item:`、`js:`、`actions:`、`page:`、`run-task:`、`stop-task:`、`wait:`、`return` 等。 ### 6. 新增 `inList` / `inGlist` 条件方法 条件系统新增列表成员判断,可判断某个值是否存在于列表或组中: ```yaml condition: 'inGlist.%player_name%;{glist:vip_players}' condition: '!inList.$(name);Steve,Alex,Notch' ``` 列表参数支持 `{list:key}` / `{glist:key}` 返回的 JSON 数组、JSON 字符串数组和简易逗号列表。成员匹配为精确匹配,默认不区分大小写。 ### 7. 新增在线玩家列表 PAPI 变量 新增 `%kamenu_online_players%`,返回当前在线玩家名称的 JSON 数组,可直接用于动态按钮列表: ```yaml source: "%kamenu_online_players%" ``` ### 8. JavaScript 新增 `papi()` 辅助函数 JavaScript 内现在可以直接解析 PlaceholderAPI 变量: ```javascript var level = parseInt(papi("player_level") || "0"); var onlinePlayers = JSON.parse(papi("kamenu_online_players")); var targetLevel = papi("%player_level%", getPlayer("Steve")); ``` 支持带 `%` 和不带 `%` 的变量名。未安装 PlaceholderAPI 或解析失败时返回空字符串。 ### 9. JavaScript 新增 KaMenu 内部变量辅助函数 JavaScript 内现在可以直接读取 KaMenu 内部变量: ```javascript var coins = parseInt(data("coins") || "0"); var status = gdata("server_status"); var temp = meta("temporary_choice"); var friends = JSON.parse(list("friends")); var servers = JSON.parse(glist("servers")); var raw = kvar("{gdata:server_status}"); ``` 支持 `kvar(...)` 通用解析,以及 `data/gdata/meta/list/glist` 便捷函数。`list` 和 `glist` 返回 JSON 数组字符串。 ### 10. 新增列表长度 PAPI 变量 新增两个用于读取列表项目数量的 PlaceholderAPI 变量: - `%kamenu_list_size_key%`:当前玩家指定列表的项目数量 - `%kamenu_glist_size_key%`:指定全局列表的项目数量 可用于条件判断: ```yaml condition: "%kamenu_list_size_friends% > 0" condition: "%kamenu_glist_size_servers% >= 3" ``` ### 11. 新增首次使用入门向导 新增 `/km guide` / `/kamenu guide` 指令,可直接打开内置入门向导菜单。该菜单从插件 jar 内部加载到内存,不会写入 `menus` 目录,适合新服务器首次配置。 入门向导支持: - 引导选择插件语言 - 按语言释放示例菜单 - 展示示例菜单说明 - 通过可点击文本快捷打开示例菜单 当服务器当前没有加载任何菜单,且 OP 玩家进入服务器时,KaMenu 会发送一条可点击文本,引导用户打开入门向导。 ### 12. 新增语言与示例释放指令 新增语言设置指令: ```bash /km language zh_CN /km language en_US ``` 执行后会写入 `config.yml` 并重载语言、配置和菜单。 新增示例菜单释放指令: ```bash /km examples /km examples zh_CN /km examples en_US /km examples zh_CN overwrite ``` 示例菜单会按指定语言释放到 `plugins/KaMenu/menus/example/`。中文和英文示例使用同一目标目录,不再要求用户手动处理 `exampleEN` 目录;默认跳过已存在文件,添加 `overwrite` 后覆盖。 --- ## 📝 文档与示例 - 新增动态按钮列表示例菜单:`example/repeat_buttons_demo.yml` - 新增玩家互动示例菜单:`example/inspect_player.yml`,用于 Shift+右键玩家查看装备和执行互动动作 - 新增内置入门向导菜单:`internal/guide.yml` - 新增中文/英文示例释放流程,示例菜单按语言释放到 `menus/example/` - 更新中文文档:`docs/menu/bottom.md` - 更新英文文档:`docs-en/menu/bottom.md` - 更新快速开始和指令文档,补充 `/kamenu guide`、`/km language`、`/km examples` [/MD]
[MD] ## ✨ 新增功能 ### 1. 自定义绑定指令支持 actions 动作列表 `config.yml` 的 `custom-commands` 现在除了原有的“指令直接打开菜单”写法,也支持直接配置 actions 动作队列。 旧写法保持兼容: ```yaml custom-commands: zcd: 'example/main_menu' ``` 新增写法: ```yaml custom-commands: test: actions: - "tell: 嘿,你输入了/test指令" - "sound: entity.experience_orb.pickup;volume=1.0;pitch=1.3" - "tell: 你想测试什么内容呢?" ``` 动作队列与按钮 actions 使用同一套执行逻辑,支持: - 普通动作 - 条件判断与 `allow` / `deny` - 嵌套动作列表 - `wait` 延迟 - `return` 中断 - 目标选择器 - 复杂条件表达式 ### 2. 自定义指令动作支持命令参数变量 自定义指令动作队列可以读取玩家输入的参数: - `{arg:0}` / `$(arg:0)`:第 1 个参数 - `{arg:1}` / `$(arg:1)`:第 2 个参数 - `$(args)`:完整参数文本 - `$(arg_count)`:参数数量 - `$(command)`:实际触发的指令标签 示例: ```yaml custom-commands: greet: actions: - "tell: &a你好 {arg:0},欢迎来到 $(arg:1)" ``` --- ## 🔧 修复与优化 ### 1. 修复目标选择器解析错误 修复动作目标选择器在复杂条件中可能被错误截断的问题。 受影响的场景包括: ```yaml - "tell: 服务器公告{player: hasPerm.vip || hasPerm.admin}" - "toast: type=task;msg=VIP;icon=diamond{player: %player_level% >= 10}" ``` 现在选择器会从 `{player:` 开始正确匹配到对应的闭合 `}`,避免条件表达式中包含变量、JS 或复杂逻辑时被提前截断。 ### 2. 改进自定义指令配置容错日志 - 菜单型自定义指令指向不存在的菜单时会输出明确 warning - actions 型自定义指令配置为空或格式错误时会跳过注册并提示配置项名称 - 注册失败时会记录对应指令名与错误原因 [/MD]
[MD] ## ✨ 新增功能 ### 1. 新增外部插件菜单 API KaMenu 现在支持外部插件直接渲染内存 YAML 菜单,不需要把菜单文件写入 `menus` 目录,也不需要触发菜单重载。 新增公开 API: ```kotlin KaMenuAPI.openYaml(player, yaml, "kagamecenter:main") KaMenuAPI.openConfig(player, config, "external") KaMenuAPI.registerActionHandler("kgc") { player, action, variables, config -> true } KaMenuAPI.unregisterActionHandler("kgc") ``` 新增接口: ```kotlin fun interface KaMenuActionHandler { fun execute( player: Player, action: String, variables: Map<String, String>, rawConfig: YamlConfiguration? ): Boolean } ``` 外部动作 handler 可拦截形如 `namespace:payload` 的动作。handler 返回 `true` 表示已处理,返回 `false` 时会继续走 KaMenu 原有动作逻辑。 ### 2. 新增菜单周期任务 `Events.Tasks` 菜单现在可以在打开期间周期执行动作列表,适合用于状态刷新、超时检测、提示音、动态检查等场景。 ```yaml Events: Tasks: refresh: interval: 20 run_immediately: true skip_if_running: true actions: - 'tell: &7菜单周期刷新' - condition: '{data:warning} == true' allow: - 'sound: block.note_block.pling 1 1' ``` ### 3. 周期任务复用完整动作系统 - 支持普通动作、条件动作、`allow/deny` 分支 - 支持 `wait` 延迟动作 - 支持嵌套 `actions:` 动作组 - 支持外部插件注册的自定义 action handler - 支持内置变量、PlaceholderAPI、`{js:...}` 等文本解析能力 ### 4. 菜单实例级任务生命周期 - 菜单成功显示后才会启动周期任务 - `Events.Open` 中执行 `return` 阻止菜单打开时,不会启动周期任务 - 玩家打开新菜单、执行 `open` / `force-open` / `reset`、执行 `close` / `force-close`、退出服务器或插件重载时,会取消当前菜单任务 - 使用 `skip_if_running: true` 时,上一轮动作链未结束会跳过本轮,避免 `wait` 导致任务堆叠 ### 5. 周期任务手动控制动作 新增周期任务控制动作,用于在任意动作包中启动、停止菜单内的周期任务: ```yaml - 'run-task: countdown 10' - 'stop-task: countdown' - 'stop-task: *' - 'stop-current-task' ``` - `run-task: <任务ID>` 可手动启动 `mode: manual` 的任务 - `run-task: <任务ID> <次数>` 可为本次运行指定循环次数 - `run-task: *` / `stop-task: *` 支持通配符,批量控制当前菜单内任务 - `stop-current-task` 可在周期任务自身动作链中停止当前循环,并中断本轮后续动作 - 同一个任务已在当前菜单生命周期内运行时,不会重复创建任务 ### 6. 动作包参数传递 `actions:` 动作包调用和可点击文本的 `actions=` 参数现在支持传参。动作包内部可通过 `{arg:0}`、`{arg:1}` 或 `$(arg:0)` 读取参数。 ```yaml Body: info: type: 'message' text: - '<text="&a[点击问候]";actions=hello,玩家,生存服务器>' Events: Click: hello: - 'tell: &a你好,{arg:0}!欢迎来到 &e{arg:1}&a。' ``` --- ## 📝 兼容说明 - 不需要修改旧菜单语法 - 未配置 `Events.Tasks` 的菜单行为不变 - 现有文件菜单、`/km open`、`MenuManager` 行为保持兼容 - 外部内存菜单不要求配置来自 `MenuManager` - Paper 当前没有可靠的 Dialog 关闭事件。如果允许玩家按 ESC 关闭菜单,服务端无法立即感知该关闭动作;需要严格生命周期时,建议设置 `Settings.can_escape: false` 并通过菜单按钮执行 `close` 或 `force-close` - 周期任务没有实时输入响应,不支持实时读取 `$(input_key)` - 周期任务内的 `return` 只会结束当前这一轮动作链,不会停止整个周期任务;需要停止循环时请使用 `stop-current-task` 或 `stop-task` --- [/MD]
[MD] ## 🔧 修复与优化 ### 1. 优化动作执行链 - 统一 `wait`、条件判断和嵌套 `actions` 的执行顺序 - 修复 `Events.Open` 在包含延迟动作时可能提前打开菜单的问题 - 保证 `return` 可在嵌套动作链中正确中断后续执行 ### 2. 新增 `{js:...}` 内置变量 - 支持在任意文本位置使用 `{js:xxx}` - 可用于条件判断和普通文本拼接 - 仅接受有返回值的 JS 表达式,返回空值时按空串处理 ### 3. 优化条件与变量解析 - 保持 `allow/deny` 条件分支语法不变 - 支持输入组件变量在嵌套 `actions` 和 `condition` 内继续传递 - 统一文本解析入口,减少各处重复处理 [/MD]
新增skull_texture和skull_owner两个头颅的属性,以支持显示自定义纹理的头颅
YAML:Body: custom_icon: type: 'item' material: 'PLAYER_HEAD' skull_texture: 'eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODc5ZTU0Y2JlODc4NjdkMTRiMmZiZGYzZjE4NzA4OTQzNTIwNDhkZmVjZDk2Mjg0NmRlYTg5M2IyMTU0Yzg1In19fQ==' name: '&6自定义图标'
YAML:Body: player_avatar: type: 'item' material: 'PLAYER_HEAD' skull_owner: '%player_name%' name: '&a我的头像'