NiuNiu - Minecraft 养(dui)成(lu)对战插件(Paper 1.21+)
NiuNiu
一个幽默风格的 Minecraft 养(dui)成(lu) + 回合制对战插件:培养你的「牛牛」,和其他玩家一决高下,冲击赛季排行榜冠军。
技术栈:Paper 1.21+ · Java 21 · Maven · HikariCP · SQLite / MySQL 双后端 软依赖:Vault(经济)、PlaceholderAPI(变量)、PlayerTitle(冠军称号)、PlayerMenu(返回主菜单)
✨ 功能特性
- 🍼 养成系统 - 「打胶」让牛牛成长,每次必成功 +N cm,带冷却
- ⚔️ 回合制对战 - 三种技能(甩击 / 筹茬 / 牛盾),怒气系统 + 克制关系
- 🏆 赛季排行榜 - 按赛季最高长度排名,冠军获得 PlayerTitle 永久称号
- 🔋 体力系统 - 每日自动回满,可花金币购买,每日限购
- 💾 数据库存储 - SQLite(默认)或 MySQL,数据唯一持久化源,支持一键迁移
- 🖥️ GUI 界面 - 主菜单 / 选人 / 排行榜 / 玩家信息,一键操作
- 📊 PlaceholderAPI - 长度、战力、评级、排行榜等变量随心展示
🔧 前置要求
| 依赖 | 必需 | 用途 |
|---|---|---|
| Vault | ✅ 必须 | 经济系统(购买体力) |
| PlaceholderAPI | 可选 | 提供 %niu_length% 等变量 |
| PlayerTitle | 可选 | 赛季冠军授予永久称号 |
| PlayerMenu | 可选 | 返回服务器主菜单 |
📥 安装步骤
- 下载 NiuNiu Releases 的
NiuNiu-1.14.5.jar - 放入服务器的
plugins/目录 - 确保已安装 Vault 及配套经济插件
- 重启服务器(或
/reload) - 编辑
plugins/NiuNiu/config.yml按需配置
🎮 玩法速览
起步:激活你的牛牛
进服后执行 /niu 打开主菜单。第一次「打胶」会随机出生一只 5~10cm 的牛牛(无需创建指令),此后每次打胶消耗 5 体力、必定成长 +2~5cm,但进入 180 秒冷却。
体力每天零点自动回满 100;花金币可额外购买(默认 1000 金币 = +50 体力,每日限购 5 次)。
对战规则
向其他玩家发起挑战(双方长度需 ≥ 10cm,各消耗 5 体力)。对局为回合制同时选招,三个技能互相克制:
| 技能 | 效果 | 怒气 |
|---|---|---|
| 猛烈甩击 | 高伤害,打断筹茬 | 50 |
| 暴力筹茬 | 中伤害,稳定输出 | 20 |
| 牛盾 | 完全格挡伤害 | 回复 +45 |
克制关系:甩击打断筹茬 → 牛盾格挡甩击 → 筹茬稳定输出,本质是"石头剪刀布"博弈。
- 对局中双方的 HP / 怒气实时广播,聊天里输入 1/2/3(或"甩 / 筹茬 / 盾")即可出招;
- 超过 30 秒未选招、中途退出或死亡均判负;
- 赢家牛牛成长
+2~6cm,输家-6~12cm(最低 5cm,谁都打不穿底裤)。
赛季
每赛季按 赛季最高长度 排名(默认 7 天一轮,可调)。赛季结束自动结算:冠军获得专属称号「S{N}打胶大王」,所有牛牛数据清零重来。
⚙️ 常用配置
存储后端(数据库是唯一数据源)
storage:
type: sqlite # sqlite(本地单文件,默认)或 mysql(远程)
sqlite-file: 'niuniu.db'
table-prefix: 'niuniu_' # 多服共用同一库时的表前缀
pool-max-size: 5
pool-min-idle: 1
mysql: # type: mysql 时生效
host: localhost
port: 3306
database: niuniu
username: root
password: ''
ssl-mode: PREFERRED # 推荐 REQUIRED(强制加密)
allow-public-key-retrieval: false
从 SQLite 迁移到 MySQL(或反向):改 storage.type → 重启(插件连上目标库)→ 控制台 /niu migrate,数据即完整搬过去(设计参考 CMI 的 /cmi migratedatabase)。
玩法参数(节选,均带注释可调)
| 配置项 | 默认 | 说明 |
|---|---|---|
brush-cooldown-seconds | 180 | 打胶冷却(秒) |
brush-growth-min/max | 2 / 5 | 每次打胶成长范围 (cm) |
win-growth-max | 6 | 对战赢家成长上限 |
lose-loss-max | 12 | 对战输家扣减上限 |
min-challenge-length | 10 | 可被挑战的最低长度(防沙包) |
hp-base / hp-per-rank | 91 / 20 | 战斗 HP 与每评级成长 |
rage-max / rage-start | 100 / 60 | 怒气上限与开局怒气 |
season-duration-days | 7 | 赛季天数 |
🔑 权限
| 权限节点 | 说明 | 默认 |
|---|---|---|
niu.admin | 管理员(强制打胶 / 重载 / 迁移) | OP |
niu.season | 开启新赛季(清空数据,破坏性,单独授予) | 无 |
niu.setlength | 设置玩家长度(可刷榜,单独授予) | 无 |
niu.bypass.cooldown | 无视打胶冷却(可刷长度,单独授予) | 无 |
敏感权限都做成独立节点而非塞进 admin,注释里直接写明用途,避免误授。
🏷️ PlaceholderAPI 变量(精选)
安装 PlaceholderAPI 后即可使用(niu 与 niuniu 等价):
| 变量 | 说明 | 示例 |
|---|---|---|
%niu_length% | 当前长度 (1 位小数) | 15.6 |
%niu_peak% | 赛季最高长度 | 26.4 |
%niu_cp% | 战斗力 | 41 |
%niu_rank_color% | 评级(带颜色) | &a小牛 |
%niu_stamina% / %niu_max_stamina% | 体力 / 上限 | 90 / 100 |
%niu_wins% / %niu_battles% / %niu_winrate% | 战绩 | 3 / 5 / 60% |
%niu_cooldown% | 打胶冷却剩余秒数 | 0 |
%niu_season% | 当前赛季号 | 2 |
%niu_top_name_1% | 排行榜第 N 名玩家(数字可换) | whohh_ |
完整变量见 README,离线玩家也可查询 length / peak / wins / battles / winrate / has_cow。
🧠 实现细节(开发复盘精华)
写的时候踩了不少坑,也抄了不少大插件的作业,挑值得记的列出来:
架构与生命周期
- 主类只做装配:按固定顺序初始化「配置/语言 → 数据库 → Vault → 玩家数据全量加载 → 战斗/赛季/GUI/命令/事件」,因为每个 Manager 都依赖前者;数据库连不上直接
disablePlugin,绝不带病运行。 onDisable顺序同样讲究:先取消定时任务(赛季检查 / 落盘 / GUI 刷新),再flushAll(),最后才关连接池。- 数据层是 EvenMoreFish 模式:内存
ConcurrentHashMap只是缓存,数据库是唯一持久化源。写入只打脏标记,每 30 秒批量 upsert,退出时单独flushOne,把磁盘写降到最低。 - 离线玩家也能查榜:内存未命中就回源数据库补读并回填缓存;排行榜全量排序带 3 秒短缓存,防止
/top或 PAPI 高频轮询拖垮服务器。
数值与状态
- 所有增长
Math.round(x * 10) / 10只留 1 位小数,避免显示 15.600000000001。 - 跨天判断用「
yyyy-MM-dd日期字符串」,任意入口调ensureDaily()幂等重置,不依赖定时任务也不会被重启打断。
对战引擎
- 回合是双方同时选招,每个选招提示带
pickGen序号:回合推进后旧超时回调直接作废,杜绝"双判负"。 - 所有结束路径(选招超时 / 挑战超时 / 中途退出 / 死亡 / 双方阵亡平局)统一收敛到
endBattle一处,状态机不会散落各处。 - 对战中双方互相对战伤害一律 cancel,死亡也能判负;聊天出招会拦截,但消息超过 6 个字符直接放行,避免误吞正常聊天。
- GUI 点击不写死格子坐标:动作名存在物品
PersistentDataContainer里再分发,跨重启依然有效;标题含「牛牛」的界面统一拦截 click/drag,物品拿不出来也拖不进去。
赛季与数据安全
- 赛季状态存数据库 KV 表;结算找
season_peak最高者,同分比胜场定冠军。 - 清库前先把内存数据自动导出成
backups/season-<号>-<时间戳>.csv,给误清库留退路。
构建发布
- Maven shade 打包 HikariCP + SQLite/MySQL 驱动,并把
com.zaxxer.hikari重定位到com.yulewqiong.niuniu.lib.hikari,防止与其他插件类冲突。 - 打好的 jar 顺手过滤
META-INF/*.SF/RSA/DSA,否则加载直接SecurityException。
❓ 常见问题
Q: 为什么默认用 SQLite?
SQLite 单文件零部署,小服足够。连接池我强制设为 1:SQLite 写锁是全库级的,多连接只会增加 database is locked 的几率。
Q: 想换 MySQL,老数据怎么办?
改 storage.type 重启后执行 /niu migrate,从源后端(另一类型)全量搬到当前后端,重启不需要删文件。
Q: 开新赛季会丢数据吗?
会清空全部玩家数据并进入新赛季——这是设计如此。但清库前插件会自动把当前数据导出成 backups/season-N-时间.csv,误操作也能恢复。
Q: 没装 Vault 还能玩吗? 能。Vault 缺失只会禁用"购买体力",养成、对战、赛季全部照常。
Q: 冷却重启后失效怎么办?
不会。冷却存的是"到期毫秒时间戳",重启后判断 cooldown - now > 0 依然生效。
Q: 如何反馈 Bug? 去 GitHub Issues 提 Issue,附上服务器版本与相关日志。
📖 项目结构
NiuNiu/
├── resources/
│ ├── plugin.yml # 插件元数据(命令 / 权限 / 依赖)
│ ├── config.yml # 默认配置
│ └── lang.yml # 文案
└── src/com/yulewqiong/niuniu/
├── NiuNiu.java # 主类:装配模块与生命周期
├── ConfigManager.java # 配置装载与重载
├── LangManager.java # 多语言文案
├── PlayerDataManager.java # 玩家数据缓存 + 脏标记落库 + 迁移
├── PlayerRow.java # 玩家数据行(与表字段一一对应)
├── BattleManager.java # 挑战流程 + 回合制对战引擎
├── SeasonManager.java # 赛季推进 / 结算 / CSV 备份 / 称号
├── DatabaseManager.java # HikariCP 双后端(SQLite / MySQL)
├── GuiManager.java # 全部 GUI 界面
├── CommandHandler.java # 命令与 Tab 补全
├── EventListener.java # 事件收口(聊天出招 / 退出判负 / GUI 点击)
└── NiuNiuExpansion.java # PlaceholderAPI 变量
构建:mvn clean package -DskipTests,产物 target/NiuNiu-1.14.5.jar(已 shade 打包 HikariCP 与双数据库驱动,并做了类重定位避免冲突)。
📜 许可证
本项目采用 MIT 许可证
🙏 致谢
- PaperMC - 服务器实现
- Vault / PlaceholderAPI - 经济与变量生态
- EvenMoreFish - 数据库唯一数据源的参考
- CMI - 数据迁移交互设计参考
⭐ 支持项目
如果你觉得这个项目不错,不妨点个 Star ⭐,或者来服务器切磋一把牛牛。
尊贵的打胶人士,冲鸭! 🐮