主题
这页聚焦 HNMail 和 HNCore、HNEconomy、PlaceholderAPI 的关系,以及当前已经存在的对外 API。
HNMail 在整体架构里的位置
如果把 HN 系列拆层来看,HNMail 是一个 建立在 HNCore 共享能力之上的业务插件。
可以这样理解:
HNCore:数据库、共享消息、命令注册、日志、调度等底座HNEconomy:统一经济服务HNMail:邮箱与奖励投递层
HNMail 最重要的价值,不是自己再造一套奖励系统,而是把:
- 消息
- 奖励
- 附件
- 离线领取
- 模板投递
- 群发审计
统一放进一套邮件模型里。
与 HNCore 的关系
HNMail 当前最核心的依赖有这些:
1. 共享数据库
用途:
- 邮件主表
- 附件表
- 玩家设置
- 黑名单
- 审计
- 玩家目录
- 广播任务
- 广播明细
这意味着 HNMail 天然适合多服共库,而不是单服孤立运行。
2. 共享消息总线
用途:
- 新邮件实时提醒
- 多服之间的提醒同步
3. 统一消息门面与 Audience 输出
HNMail 当前的消息发送,不只是简单读取 messages.yml。
实际流程是:
- 优先尝试获取 HNCore 的
MessageFacade - 优先尝试获取 HNCore 的
AudienceService - 如果可用,则优先走 HNCore 的统一渲染与发送链路
- 如果不可用,再回退到插件本地的消息管理与基础输出
这意味着:
messages.yml仍然是 HNMail 的消息来源- 但消息渲染、占位符上下文、发送通道,会优先走 HNCore 的统一能力
- 对二开来说,HNMail 当前已经在消息层接入了 HNCore 的更高层门面,而不只是共享配置文件
4. 统一物品库网关 ItemLibraryGateway
/hnmail sendlibitem 当前不是直接对接某一个具体物品库插件,而是通过 HNCore 的统一物品库网关工作。
大致流程是:
- 先根据
source:itemId解析来源与物品 ID - 再通过
ItemLibraryGateway检查来源是否可用 - 必要时把来源别名归一化到 canonical source
- 最后由统一网关完成实际物品解析
这意味着:
- HNMail 不需要自己为每个外部物品库重复维护一套解析入口
mythic/mm、neige/ni这类别名兼容可以在网关层统一处理- 文档里应该把它描述为"统一物品库网关接入",而不是"HNMail 内置某几个来源"
5. GUI 与命令体系
用途:
- 收件箱 GUI
- 邮件详情 GUI
- 主命令与子命令注册
- 面向玩家 / 管理员的反馈输出
6. 输入会话框架(HNCore 2.3.0+)
HNMail 使用 HNCore 的统一输入会话框架处理玩家输入:
- 邮件收件人输入(铁砧)
- 邮件标题输入(铁砧)
- 邮件内容输入(聊天)
- 黑名单添加(铁砧)
这意味着:
- 统一的输入体验
- 自动超时机制
- 更健壮的事件处理
- 支持多种输入渠道(聊天/铁砧/告示牌)
与 HNEconomy 的关系
HNMail 本身不维护自己的金币账本。
正确分工是:
- HNMail 负责"金币奖励以附件形式投递"
- HNEconomy 负责"领取时真正入账"
这样可以避免绕过:
- 正式货币定义
- 最大余额限制
- 账变流水
- 统一审计
所以金币附件领取时,应始终走 HNEconomy 的正式服务层,而不是在 HNMail 里自己改数据库。
与 PlaceholderAPI 的关系
PlaceholderAPI 不影响邮箱主业务,只影响展示层。
当前版本已实现的典型占位符包括:
%hnmail_unread%%hnmail_total%%hnmail_allow_player_mail%%hnmail_allow_player_mail_text%%hnmail_template_count%%hnmail_broadcast_job_count%%hnmail_latest_broadcast_status_text%%hnmail_latest_broadcast_success_rate_text%
适合接到:
- 菜单
- 计分板
- TAB
- 全息文字
如果 PlaceholderAPI 不可用,HNMail 本体仍应能正常工作。
更完整的占位符列表,建议直接看 PlaceholderAPI 占位符。
当前对外 API
HNMail 2.0+ 采用了符合团队规范的多模块架构,提供了清晰的 API 边界。
模块结构
hnmail-api- 公共 API 模块(接口、数据模型)hnmail-plugin- 插件实现模块(业务逻辑)
依赖方式
在你的插件中添加依赖:
gradle
dependencies {
compileOnly("com.github.hnplugins:hnmail-api:2.0.0")
}核心 API
1. 获取邮件服务
java
import com.github.hnplugins.hnmail.api.HNMailAPI;
import com.github.hnplugins.hnmail.service.IMailService;
// 获取邮件服务接口
IMailService mailService = HNMailAPI.getMailService();2. 发送邮件
发送系统邮件:
java
import com.github.hnplugins.hnmail.model.MailAttachment;
import com.github.hnplugins.hnmail.model.MailAttachmentType;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
// 发送带附件的系统邮件
List<MailAttachment> attachments = List.of(
new MailAttachment(
MailAttachmentType.MONEY,
"coins", // 货币ID
1000, // 金额
null // 附加数据
)
);
String mailUuid = mailService.sendAdminMail(
sender, // 发送者(控制台或管理员)
targetPlayer, // 收件人
"每日奖励", // 标题
"感谢你的登录!", // 内容
attachments // 附件列表
);发送玩家邮件:
java
// 玩家之间发送邮件
String mailUuid = mailService.sendPlayerMail(
senderPlayer, // 发送者(必须是在线玩家)
targetPlayer, // 收件人
"问候", // 标题
"你好!" // 内容
);
// 带附件的玩家邮件
String mailUuid = mailService.sendPlayerMail(
senderPlayer,
targetPlayer,
"礼物",
"送你一些物品",
attachments
);3. 查询邮件
查询收件箱:
java
import com.github.hnplugins.hnmail.model.MailInboxFilter;
import com.github.hnplugins.hnmail.model.MailInboxPageSnapshot;
// 加载收件箱分页
MailInboxPageSnapshot inbox = mailService.loadInboxPage(
playerId,
1, // 页码
MailInboxFilter.UNREAD // 过滤器:ALL, UNREAD, HAS_ATTACHMENT
);
// 获取未读数量
int unreadCount = mailService.countUnread(playerId);查看邮件详情:
java
import com.github.hnplugins.hnmail.model.MailDetailView;
MailDetailView detail = mailService.viewMailDetail(
player,
mailUuid
);4. 领取附件
java
// 领取邮件附件
int claimedCount = mailService.claimAttachments(player, mailUuid);5. 删除邮件
java
// 删除邮件
mailService.deleteMail(player, mailUuid);模板服务
java
import com.github.hnplugins.hnmail.service.IMailTemplateService;
import com.github.hnplugins.hnmail.model.MailTemplate;
IMailTemplateService templateService = HNMailAPI.getTemplateService();
// 解析模板附件
List<MailAttachment> attachments = templateService.resolveAttachments(
template,
targetPlayer,
sender
);广播服务
java
import com.github.hnplugins.hnmail.service.IMailBroadcastService;
IMailBroadcastService broadcastService = HNMailAPI.getBroadcastService();
// 群发模板邮件
broadcastService.sendTemplateToAll(
sender,
template,
true // 仅在线玩家
);数据模型
所有数据模型都在 hnmail-api 模块中,可以直接使用:
java
import com.github.hnplugins.hnmail.model.*;
// 邮件消息
MailMessage message = ...;
// 邮件附件
MailAttachment attachment = new MailAttachment(
MailAttachmentType.ITEM, // 附件类型:ITEM, MONEY, COMMAND
"payload", // 附件数据
1, // 排序
null // 附加数据
);
// 收件箱条目
MailInboxEntry entry = ...;
// 邮件详情视图
MailDetailView detail = ...;API 设计原则
HNMail 2.0+ 的 API 遵循以下原则:
- 接口优先 - 只暴露接口,不暴露实现类
- 不可变数据 - 数据模型使用
record类型,保证线程安全 - 清晰边界 - API 模块不包含任何实现细节
- 向后兼容 - 使用
default方法保持接口兼容性
当前最实用的接入方式
对外业务模块最常用的其实是:
- 给玩家发一封系统邮件
- 附带物品 / 金币 / 命令附件
这非常适合:
- 活动系统
- 签到系统
- 商城系统
- 官网兑换系统
- 后台补发工具
推荐思路
- 外部插件负责"资格判断"
- HNMail 负责"邮件投递"
例如:
HNCDK判断玩家能不能兑换HNMail负责把奖励投进邮箱
这样职责会非常清晰。
完整示例
java
import com.github.hnplugins.hnmail.api.HNMailAPI;
import com.github.hnplugins.hnmail.service.IMailService;
import com.github.hnplugins.hnmail.model.MailAttachment;
import com.github.hnplugins.hnmail.model.MailAttachmentType;
public class RewardSystem {
private final IMailService mailService;
public RewardSystem() {
this.mailService = HNMailAPI.getMailService();
}
public void sendDailyReward(OfflinePlayer player) {
// 创建金币附件
List<MailAttachment> attachments = List.of(
new MailAttachment(
MailAttachmentType.MONEY,
"coins",
1000,
null
)
);
// 发送系统邮件
try {
String mailUuid = mailService.sendAdminMail(
Bukkit.getConsoleSender(),
player,
"每日奖励",
"感谢你的登录!这是你的每日奖励。",
attachments
);
getLogger().info("已发送每日奖励邮件给 " + player.getName() + ": " + mailUuid);
} catch (Exception e) {
getLogger().warning("发送邮件失败: " + e.getMessage());
}
}
public void checkUnreadMail(Player player) {
int unreadCount = mailService.countUnread(player.getUniqueId());
if (unreadCount > 0) {
player.sendMessage("§a你有 " + unreadCount + " 封未读邮件!");
}
}
}命令附件的集成建议
命令附件很强,但也最需要规范。
推荐约束:
- 只允许管理员创建
- 只允许后台执行受控命令
- 命令尽量幂等
- 变量替换范围尽量明确
当前常见变量包括:
{player}{server}
如果外部业务要和 HNMail 做深度联动,建议约定一套稳定的奖励命令,而不是到处临时拼命令文本。
一个实用的架构建议
如果你未来还想做 CDK、活动系统、官网后台,最优解通常是:
- 外部插件负责"资格判断"
- HNMail 负责"邮件投递"
- HNEconomy 负责"金额入账"
- PlaceholderAPI 负责"状态展示"
这样就不会让每个系统都各自实现一套发奖逻辑。
迁移指南
如果你的插件使用了旧版本的 HNMail API,需要进行以下更新:
1. 更新依赖
gradle
// 旧版本
compileOnly("com.github.hnplugins:HNMail:1.x.x")
// 新版本
compileOnly("com.github.hnplugins:hnmail-api:2.0.0")2. 更新导入语句
java
// 旧版本
import com.github.hnplugins.hnmail.service.MailService;
// 新版本
import com.github.hnplugins.hnmail.service.IMailService;3. 更新类型声明
java
// 旧版本
MailService mailService = HNMailAPI.getMailService();
// 新版本
IMailService mailService = HNMailAPI.getMailService();4. 移除插件实例引用
java
// 旧版本(已移除)
HNMail plugin = HNMailAPI.getPlugin();
// 新版本(直接使用服务接口)
IMailService mailService = HNMailAPI.getMailService();建议联动阅读
- 想理解附件业务模型:看 邮件投递、附件与领取机制
- 想理解模板、群发与广播日志:看 模板邮件、群发与广播任务
- 想查看命令入口:看 命令说明
- 想查完整
%hnmail_*%占位符:看 PlaceholderAPI 占位符 - 遇到依赖或接口问题:看 常见问题
