不只是另一个聊天轮播器。这是一个完整的公告引擎。
为现代 Minecraft 服务器和网络提供定向、本地化且感知事件的通知广播。
6 个运行时 · 8 种通知展示面 · 8 类受众规则 · 3 种事件触发器 · 3 种内置语言 · 每种语言一次渲染 · 无数据库
|
🧭 为什么选择 AutoMessage |
⚙️ 安装与配置 |
📣 广播应当赢得关注
当每条消息都发送给所有人时,玩家很快就会学会忽略公告。AutoMessage 让服务器沟通变得深思熟虑:每个流都有自己的时间表,每条消息都能选择自己的受众,文本会以每位玩家实际使用的语言呈现——这一切都源自一个运行在 Bukkit、Folia、Sponge、Velocity、Fabric 和 Minestom 上的共享核心。
同一套公告,在同一台服务器的游戏中展示:Boss 栏倒计时、全屏标题和一行聊天消息。
一条完整的公告,从始到终
在老玩家加入三秒后向他们致以问候——绝不依赖定时器,绝不打扰新人,并使用他们自己的语言。只需两个文件即可实现:
# scheduledMessages.yml - 谁接收,何时接收
messages:
- name: welcome-back
trigger:
type: JOIN
delay: 3s
rules:
- type: PLAYTIME
min: 10h
# lang/en.yml - 说什么
announcements:
welcome-back:
- chat: "<gray>欢迎回来,<aqua>{PLAYER}<gray>。"
sound: "entity.player.levelup MASTER 1.0 1.2"
服务器将如此处理它们:
这就是功能集的缩影:基于事件而非时钟,针对特定受众而非所有人,文本与其他所有翻译并存。
⚖️ 不仅仅是轮播列表
这句标语并非营销噱头。普通的轮播器只回答一个问题——接下来该说什么?——然后用一种语言、按一个时钟把答案发给所有人。
| 普通轮播器 | AutoMessage | |
|---|---|---|
| 谁接收 | 所有在线玩家 | 权限、组别、世界、在线人数、游戏时长——可通过 ANY_OF、NONE_OF、NOT 组合 |
| 何时触发 | 一个全局间隔 | 每个频道独立调度,外加 JOIN、FIRST_JOIN 和可重新武装的 PLAYER_COUNT 触发器 |
| 呈现形式 | 一行聊天消息 | 聊天、动作栏、标题、副标题、Boss 栏和音效,可在一条公告中组合使用 |
| 内容表述 | 所有人看到同一段文本 | 玩家自己的语言,每条消息均有独立的回退机制 |
| 运行环境 | 一种服务器类型 | Bukkit、Folia、Sponge、Velocity、Fabric 和 Minestom,各自拥有原生构建物 |
底层细节
🔀 四种轮播策略SHUFFLE(洗牌)、SEQUENTIAL(顺序)、RANDOM(随机)或加权,按频道选择。洗牌会在任何内容重复前发完整副牌。 | 🧩 适配运行时的配置 代理服务器永远不会看到世界规则或首次加入触发器。平台无法支持的选项会从文件中省略,而非写入后被静默忽略。 | 👁 发布前预览/automessage view <name> 仅向你渲染真实的本地化消息,忽略其受众规则。 |
| ↗ 超越服务器 将聊天内容镜像到 Discord Webhook,自动剥离仅限玩家的占位符并禁用自动提及。 | ♻ 重载而非重启/automessage reload 就地应用调度、消息和语言——包括在服务器运行时新增的翻译文件。 | 🪶 精简的分发路径 无数据库。空服务器会短路处理,占位符每条消息仅扫描一次而非每条公告扫描一次,所有以相同语言阅读相同文本的玩家共享单次渲染结果。 |
🎯 精准触达应接收者
规则可组合。每个顶层规则都必须通过,而当受众需要比简单的“与”逻辑更复杂的条件时,可在其中嵌套 ANY_OF、NONE_OF 和 NOT。
同样的规则也适用于由事件发送的消息,因此限制特定权限的加入问候绝不会发送给缺乏该权限的人,哪怕他们刚刚走进大门。
⚡ 由事件触发,而非时钟
带有触发器的消息会完全脱离其频道的轮播,静待某事发生。
PLAYER_COUNT 值得细读两遍:它在上升过程中仅触发一次,且仅在服务器人数回落至阈值以下后才重新武装,因此当人口在边界徘徊时,不会因每次有人登录就重复宣布同一里程碑。
🪶 几乎不消耗服务器资源
格式化是公告中最耗时的部分。颜色、渐变、悬停文本和占位符都必须在任何字符送达任何人之前进行解析——而直观的做法是为每位接收者解析一次。五百名玩家在线意味着同一句话要在主线程上构建五百次才能说出一件事,而其余的刻只能等待轮转。
AutoMessage 每种语言仅构建一次,并将完成的结果交给所有阅读该语言的玩家。
| 每位玩家单独格式化 | AutoMessage | |
|---|---|---|
| 每条公告的解析次数 | 每位玩家一次,共 500 次 | 每种实际被阅读的语言一次 |
| 主线程耗时 | 5.16 毫秒 | 0.34 毫秒 |
| 占 50 毫秒刻的比例 | ~10%,每条公告 | ~0.7% |
这意味着每条公告的主线程耗时减少 15 倍,这是在真实发送路径上、500 名观众在线时测得的——而非针对单一函数的微基准测试。剩下的工作只是将完成的文本传递给每个连接,这是任何插件都无法避免的。
随着玩家数量增加,节省效果愈发显著,且无需任何配置成本:无需调整缓存大小,无需调优设置,也无需牺牲任何行为。
它从不以正确性换取速度。 对于每位玩家阅读内容不同的消息——例如包含 {PLAYER} 或任何 PlaceholderAPI 令牌的消息——仍会为每位玩家单独构建,因为只有该令牌背后的扩展才知道两位玩家是否会看到相同的内容。分组仅在文本真正完全相同时应用,除此之外绝不应用。
分发路径在另外三个地方拒绝执行不必要的工作:
- 占位符每条消息仅扫描一次,而非每条公告扫描一次。 消息包含哪些令牌由文本决定,因此在读取文件时确定并记忆直到重载——而不是每次频道触发时重新推导。
- 空服务器会短路处理。 无人在线意味着无需选择、无需格式化、无需工作。
- 公告期间不触碰磁盘。 无数据库、无玩家记录、热路径上无 I/O。
🌐 一个引擎。六种运行时
AutoMessage 使用一个共享的平台无关核心,并为每种服务器类型配备小型运行时适配器。这意味着行为保持一致,而调度、观众、权限和生命周期钩子则在底层保持对各平台的原生支持。
| 构建物 | 运行时 | 支持版本 | Java | 平台特定说明 |
|---|---|---|---|---|
| Bukkit | Spigot、Paper、Purpur 及其他基于 Bukkit 的服务器 | Minecraft 1.21–26.2 | 21+ | 完整功能集;可选 PlaceholderAPI 集成 |
| Folia | Folia | Minecraft 1.21–26.2 | 21+ | 专用区域化调度;可选 PlaceholderAPI 集成 |
| Sponge | Sponge API 17 | Minecraft 1.21.10 | 21+ | 完整功能集,但不支持 PlaceholderAPI |
| Velocity | Velocity 3.5.1 代理 | 代理运行时 | 21+ | 无世界、游戏时长、首次加入或 PlaceholderAPI 能力 |
| Fabric | 专用 Fabric 服务器 | Minecraft 1.21.11 | 21+ | 服务端模组;需 Fabric API;无 PlaceholderAPI |
| Minestom | 嵌入式 Minestom 库 | 协议 1.21.11 | 25+ | 类路径库;无世界、游戏时长、首次加入或 PlaceholderAPI;权限/组别规则需你提供回调 |
[!NOTE] Java 21 是插件构建物的字节码底线,并非承诺每个 Minecraft 服务器版本都能在 Java 21 上运行。请使用你的服务器所需的 JVM;Minestom 本身需要 Java 25。
每个构建物都会生成感知平台的配置。运行时无法支持的选项会被省略,而非生成后被静默忽略。
Quilt 呢?
没有单独的 Quilt 构建物。Quilt 可以读取 Fabric 元数据,但此分支不声称拥有经过测试的 Quilt 版本。请将 Quilt 上的 Fabric jar 视为实验性,并自行携带兼容的 Fabric API。
🗂 配置模型
AutoMessage 将 何时、谁 和 什么 分离。添加语言从不需要复制调度规则,更改频道间隔也从不触及消息文本。
flowchart LR
schedule["config.yml<br/>何时<br/>频道 · 周期 · 选择器"]
audience["scheduledMessages.yml<br/>谁<br/>规则 · 权重 · 触发器"]
content["lang/*.yml<br/>什么<br/>本地化 MiniMessage 通知"]
minecraft["Minecraft<br/>玩家输出"]
discord["discordWebhook.yml<br/>可选镜像"]
schedule --> audience --> content --> minecraft
content -. "聊天文本" .-> discord
classDef config fill:#11182d,stroke:#8b7cf6,color:#eef0ff,stroke-width:1px;
classDef output fill:#0d2028,stroke:#45d7e8,color:#ecfeff,stroke-width:1px;
class schedule,audience,content config;
class minecraft,discord output;
| 文件 | 职责 |
|---|---|
config.yml | 总开关、回退语言、频道、启动延迟、周期和选择器 |
scheduledMessages.yml | 消息名称、频道分配、权重、受众规则和事件触发器 |
lang/<code>.yml | 本地化命令回复及实际的 MiniMessage 公告负载 |
discordWebhook.yml | 可选的 Discord Webhook、显示名称和头像 |
📥 获取 AutoMessage
| 你需要什么 | 在哪里获取 |
|---|---|
| 当前公开的 Bukkit 构建 | Modrinth · SpigotMC · Hangar |
| 发布历史与变更日志 | GitHub Releases |
| 来自此分支的多平台构建物 | 从源码构建全部六个 |
[!IMPORTANT] 发布状态: 此 README 记录的是本地多平台分支。当前的公开列表仍代表较早的专注于 Bukkit 的发布线。在新的 GitHub 发布版公布全部六个构建物之前,请使用
./gradlew dist构建多平台 jar。
🚀 快速开始
- 获取为你的运行时构建的构建物。平台 jar 有意分开。
- 将其放入你平台的
plugins/或mods/目录。对于 Minestom,将 jar 添加到应用程序类路径,并使用下方所示的构建器。 - 启动服务器一次。AutoMessage 会生成带注释的配置以及可用的示例公告。
- 编辑
config.yml、scheduledMessages.yml和lang/内的文件。 - 运行
/automessage reload,然后使用/automessage view <name>测试一条消息。
[!TIP] 从生成的文件开始。它们不是空模板:每个可用字段都在可用示例旁进行了解释,且内容会适应创建它们的平台。
🛠 创建你的第一条公告
以下示例创建一个独立的事件流,向其中添加一条公告,并从单个本地化负载渲染多种通知类型。
1. 调度流
# config.yml
enabled: true
fallbackLanguage: en
channels:
- name: default
enabled: true
initialDelay: 1m
period: 5m
selector: SHUFFLE
- name: events
enabled: true
initialDelay: 30s
period: 15m
selector: SEQUENTIAL
2. 注册公告
# scheduledMessages.yml
messages:
- name: event-night
channel: events
3. 设计玩家接收的内容
# lang/en.yml
announcements:
event-night:
- chat:
- "<dark_gray>[<gold>活动<dark_gray>] <gray>竞技场将在 <gold>5 分钟<gray>后开放。"
- "<gray>点击 <click:run_command:'/warp arena'><hover:show_text:'<yellow>传送'><aqua><u>此处</u></aqua></hover></click> 加入。"
actionbar: "<yellow>准备好你的装备——活动即将开始"
title: "<gradient:#ffd166:#ff6b6b><bold>活动之夜</bold></gradient>"
subtitle: "<gray>竞技场将在 05:00 开放"
times: "500ms 3s 500ms"
bossbar:
message: "<light_purple>活动倒计时"
duration: 10s
color: PURPLE
overlay: PROGRESS
progress: 0.75
sound: "block.note_block.pling MASTER 1.0 1.0"
仅使用适合消息的部分。简单的聊天公告可以是一个字符串;展示型公告可以组合上述所有表面。
📖 参考文档
公告频道与选择器
每个频道都有自己独立的日程和选择器状态:
channels:
- name: tips
enabled: true
initialDelay: 1m
period: 5m
selector: SHUFFLE
- name: promotions
enabled: true
initialDelay: 2m
period: 20m
selector: WEIGHTED
| 选择器 | 行为 |
|---|---|
SHUFFLE | 随机化顺序,在任何内容重复前显示每条消息一次 |
SEQUENTIAL | 直接遍历配置列表 |
RANDOM | 独立抽取;同一条消息可能连续出现两次 |
WEIGHTED | 按每条消息的 weight 比例随机抽取 |
SEQUENTIAL 和 SHUFFLE 在正常配置重载时会保持其位置。权重为 0 仅在 WEIGHTED 频道中暂停消息;其他选择器不咨询权重。
受众规则
顶层规则表现为逻辑 AND。当受众需要更具表现力的逻辑时,可嵌套 ANY_OF、NONE_OF 和 NOT。
| 规则 | 匹配对象 |
|---|---|
PERMISSION | 持有权限节点的观众 |
GROUP | 持有 group.<name> 的观众;这是基于权限的,而非直接的 LuckPerms/Vault 查找 |
WORLD | 位于配置世界之一的观众 |
PLAYER_COUNT | 实时在线人数处于配置范围内时 |
PLAYTIME | 原生平台游戏时长处于配置时长范围内的观众 |
ANY_OF | 至少一个嵌套规则 |
NONE_OF | 无任何嵌套规则 |
NOT | 一个嵌套规则的逆反 |
messages:
- name: vip-event
channel: events
rules:
- type: ANY_OF
rules:
- type: PERMISSION
permission: rank.vip
- type: PERMISSION
permission: rank.staff
- type: PLAYER_COUNT
min: 10
max: 100
规则仍适用于由事件触发器发送的消息。平台特定规则仅在运行时可供应所需数据时生成。
事件触发器
触发的消息会完全脱离其定时频道轮播:
messages:
- name: welcome-back
trigger:
type: JOIN
delay: 3s
- name: first-join-welcome
trigger:
type: FIRST_JOIN
delay: 3s
- name: one-hundred-online
trigger:
type: PLAYER_COUNT
threshold: 100
PLAYER_COUNT 在达到阈值时触发一次,且仅在在线人数回落至阈值以下后才重新武装。FIRST_JOIN 在未暴露持久化首次加入信号的运行时上不可用。
[!NOTE]
/automessage enable和/automessage disable控制计划频道。事件触发的公告独立运作。
语言与回退行为
AutoMessage 内置 en、pl 和 de。通过复制文件到 lang/<code>.yml、翻译它并运行 /automessage reload 来添加另一种语言。
对于使用 pt_br 的玩家,AutoMessage 会查找:
lang/pt_br.ymllang/pt.ymlconfig.yml中的fallbackLanguage
缺失的公告会 individually 回退,因此新翻译无需完整即可有用。
MiniMessage、占位符与通知部件
所有文本均使用 Kyori MiniMessage,包括颜色、渐变、装饰、悬停文本和点击动作。
内置占位符:
| 玩家范围 | 服务器范围 |
|---|---|
{PLAYER} · {DISPLAY_NAME} · {UUID} · {WORLD} | {ONLINE} · {MAX_PLAYERS} · {DATE} · {TIME} |
在 Bukkit 和 Folia 上,已安装的 PlaceholderAPI 会被自动检测。随后标准 %placeholder_name% 令牌(包含字母、数字和下划线)可与内置占位符一同使用。
可用通知键:
| 键 | 值 |
|---|---|
纯字符串 / chat | 一行或多行聊天消息 |
actionbar | 显示在快捷栏上方的 MiniMessage 文本 |
title / subtitle | 标题层;任一均可独立使用 |
times | 三个持续时间:淡入、停留、淡出 |
hideTitle | 显式清除当前标题 |
bossbar | 消息、持续时间、颜色、覆盖层及可选进度 0.0–1.0 |
sound | 音效键,或 key source volume pitch |
需要视觉起点?试试外部的 EternalCode 通知生成器,并将生成的通知适配到上述语言文件结构。
时间格式
每个持续时间接受显式单位,且单位可组合:
| 单位 | 含义 | 示例 |
|---|---|---|
ms | 毫秒 | 500ms |
s | 秒 | 30s |
m | 分钟 | 5m |
h | 小时 | 2h |
d | 天 | 1d |
1m30s 有效。纯数字被解释为秒。短于 50ms 的频道周期会被标准化,基于刻的运行时最终在刻边界执行。
Discord Webhook 镜像
在 discordWebhook.yml 中设置 enabled: true 并提供 Discord Webhook URL,以镜像计划和触发的公告。
集成刻意保守:
- 仅镜像聊天部分;
- MiniMessage 被展平为纯文本;
- 使用配置的回退语言;
- 解析服务器占位符,同时移除玩家范围的值;
- 禁用自动 Discord 提及;
- Webhook 必须使用 HTTPS 及 Discord Webhook 主机/路径;
- 速率限制响应会通过退避重试。
Webhook 是可选的,在禁用或缺少有效 URL 时不会发出任何出站请求。更改 Webhook 的启用状态或 URL 后请重启 AutoMessage,以便重建连接。
命令与权限
| 命令 | 权限 | 描述 |
|---|---|---|
/automessage enable | command.automessage.enable | 恢复计划公告频道 |
/automessage disable | command.automessage.disable | 暂停计划公告频道 |
/automessage reload | command.automessage.reload | 重载调度、消息定义和语言 |
/automessage view <name> | command.automessage.view | 预览一条本地化消息;仅限玩家,带标签补全 |
/automessage next | command.automessage.next | 显示每个频道接下来将宣布什么及距离触发还有多久,而不消耗它 |
/automessage send <channel> | command.automessage.send | 立即发送该频道的下一条公告;其间隔从此时刻重新开始 |
/automessage toggle | command.automessage.toggle | 玩家为自己关闭或重新开启公告;重启后仍被记住 |
/automessage stats | command.automessage.stats | 按频道:已承载多少、最后一条何时发出、下一条何时到期;然后按消息同样统计 |
在 Minestom 中嵌入 AutoMessage
Minestom 没有插件目录。初始化它,将 AutoMessage 放在类路径上,并在 Minestom 自身初始化后启用该库:
MinecraftServer.init();
AutoMessageMinestom autoMessage = AutoMessageMinestom.builder()
.dataDirectory(Path.of("automessage"))
.permissions((sender, node) -> myPermissions.check(sender, node)) // 可选
.enable();
// 在你的服务器关闭期间:
autoMessage.shutdown();
若无权限回调,AutoMessage 会使用 Minestom 的操作员等级来保护其命令。此时权限和组别受众规则会被省略,因为运行时无法准确回答它们。
🔨 从源码构建
构建所有运行时需要 JDK 25。从仓库根目录运行:
./gradlew dist
该任务运行所有检查,构建每个平台特定的 jar,并将六个可安装构建物同步到 build/dist:
- Bukkit
- Folia
- Sponge
- Velocity
- Fabric
- Minestom
若任何验证失败,则不会生成分发包。普通的 ./gradlew build 执行相同的项目检查,但将构建物保留在各自的模块内。
💛 支持本项目
AutoMessage 是免费、开源且独立维护的。
| 发现问题? | 想贡献代码? | 想支持开发? |
|---|---|---|
| 提交 bug 报告或功能请求 | 阅读 贡献指南 并提交 pull request | GitHub Sponsors · PayPal |
请在所有社区空间中遵循项目的 行为准则。 AutoMessage 根据 GNU 通用公共许可证 v3.0 分发。
如果 AutoMessage 让你的服务器沟通更好,请给仓库点个星。
这有助于其他服务器所有者发现本项目。
选择礼物
使用社区积分,为喜欢的作品送上一份支持
打赏记录
感谢每一位送出礼物的社区成员


