NiuNiu - Minecraft 养(dui)成(lu)对战插件(Paper 1.21+)
MC插件

NiuNiu - Minecraft 养(dui)成(lu)对战插件(Paper 1.21+)

约 17 分钟6446 字

NiuNiu

license: MITGitHub starsJava 21Minecraft 1.21+version 1.14.5

一个幽默风格的 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可选返回服务器主菜单

📥 安装步骤

  1. 下载 NiuNiu Releases 的 NiuNiu-1.14.5.jar
  2. 放入服务器的 plugins/ 目录
  3. 确保已安装 Vault 及配套经济插件
  4. 重启服务器(或 /reload)
  5. 编辑 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-seconds180打胶冷却(秒)
brush-growth-min/max2 / 5每次打胶成长范围 (cm)
win-growth-max6对战赢家成长上限
lose-loss-max12对战输家扣减上限
min-challenge-length10可被挑战的最低长度(防沙包)
hp-base / hp-per-rank91 / 20战斗 HP 与每评级成长
rage-max / rage-start100 / 60怒气上限与开局怒气
season-duration-days7赛季天数

🔑 权限

权限节点说明默认
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 许可证


🙏 致谢


⭐ 支持项目

如果你觉得这个项目不错,不妨点个 Star ⭐,或者来服务器切磋一把牛牛。

尊贵的打胶人士,冲鸭! 🐮