PluginBase 简介
Minecraft 插件开发前置
# 简介
经过一年多 Minecraft 服务器插件的开发,我逐渐感觉到有一些步骤没有必要。
于是我创建了插件模板,但这还不够,还是太麻烦了。
这个库包含了我常用的工具类,以及惯用的设计结构。
此外,我非常讨厌一个插件还要带上前置插件这种拖家带口行为,所以,这个前置是通过 shadow 打进插件 jar 里的。并且,以我设计的模块工作原理,必须要把这个依赖 shadow 打进 jar 并 relocate 到插件自己的包。
# 开始使用
详见左侧文档目录
插件模板生成器详见 https://bukkit.mcio.dev (opens new window)
# 正在使用 PluginBase 的插件
- SweetRiceBase [闭源] NeoWorld 服务器基础玩法插件
- SweetRiceTeam [闭源] NeoWorld 服务器副本组队插件
- SweetRiceActivity [闭源] NeoWorld 服务器活动服机制
- SweetGenerator [闭源] NeoWorld 服务器肉鸽地牢生成插件
- SweetQuests [暂定闭源] NeoWorld 服务器剧情系统
- CraftItem (opens new window) 物品锻造插件
- SweetWarehouse (opens new window) 云物品仓库插件
- SweetInventory (opens new window) 自定义菜单插件
- SweetAdaptiveShop (opens new window) 动态价格商店插件
- SweetCheckout (opens new window) 充值插件
- SweetRewards (opens new window) 累积点数插件
- SweetDrops (opens new window) 方块挖掘概率事件插件
- SweetFlight (opens new window) 限时飞行插件
- SweetMessages (opens new window) 基础消息插件
- ...其它 Sweet系列插件 (opens new window)
# 项目发布于
# 开发文档
通过这篇文档,来上手使用 PluginBase 进行 Minecraft 插件开发。
请按顺序阅读并操作。
- 构建脚本 build.gradle.kts
- 插件元数据 plugin.yml
- 插件主类 PluginMain.java
- 一些工具 ColorHelper, ItemStackUtil, Util 等等
- (可选) 菜单 GuiManager
- (可选) 菜单配置文件 AbstractGuiModule
- (可选) Vault 经济 IEconomy
- (可选) HikariCP 数据库连接池 IDatabase
- (可选) Adventure 在非 Paper 服务端的富文本支持 AdventureUtil
- (可选) 本地化(语言文件)系统 LanguageManager
# shadow 去除不必要部分
适当地给插件减重,去除当前插件不需要的功能
(部分功能仅能在 1.0.8 版本后去除,否则会出现一些问题)
tasks {
// ...
shadowJar {
// ...
// 避免匿名类没删除,最好加通配符*来匹配
listOf(
// 界面配置
"top/mrxiaom/pluginbase/func/AbstractGui*",
"top/mrxiaom/pluginbase/func/gui/*",
// PAPI兼容 (注意: 界面配置依赖这个)
"top/mrxiaom/pluginbase/utils/PAPI*",
// ItemsAdder 支持
"top/mrxiaom/pluginbase/utils/IA*",
// 物品操作相关支持
"top/mrxiaom/pluginbase/utils/ItemStackUtil*",
// 界面菜单管理器
"top/mrxiaom/pluginbase/func/GuiManager*",
"top/mrxiaom/pluginbase/gui/*",
// 多语言支持
"top/mrxiaom/pluginbase/func/LanguageManager*",
"top/mrxiaom/pluginbase/func/language/*",
// Adventure (消息和物品) 支持
"top/mrxiaom/pluginbase/utils/Adventure*",
// BungeeCord 消息通道 ByteArrayDataOutput 消警告
"top/mrxiaom/pluginbase/utils/Bytes*",
).forEach(this::exclude)
}
// ...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# shadow 部分 relocate
我在做 paper 与 spigot 兼容的时候发现,因为我要把 adventure 打包到插件里并 relocate 到我的包,那么如果我要调用 paper 的 Bukkit.createInventory
来在标题使用 adventure,那么就会因为包被 relocate 了而报错。
我的解决方法是,把 paper 的操作放到了一个单独的类 PaperInventoryFactory
,里面有一个 create(InventoryHolder, int, String)
,参数 String 是标题。
执行之后,会调用 mini message 转换为 Component 对象,传给 paper 的 Bukkit.createInventory
。只要修改 shadowJar 插件让这个 PaperInventoryFactory
类不会被 relocate 即可。
我已经把 shadowJar 改好了,直接用就行:
// settings.gradle.kts
buildscript {
repositories {
mavenCentral()
gradlePluginPortal()
}
dependencies {
classpath("top.mrxiaom:shadow:7.1.3")
}
}
2
3
4
5
6
7
8
9
10
shadow 7.1.2 的工具链有点老了,发布不到 Gradle Plugin Portal,升级工具链又不支持 gradle 7.x,干脆就发 Maven Central 了。反正这样添加起来也不麻烦,多几行代码不碍事,能解决问题就行。也测试了支持在 Gradle 8.5 跑,不求在太高的 Gradle 版本能用,目前能兼容 Java 21 就行。
// build.gradle.kts
plugins {
// ...
id("top.mrxiaom.shadow")
}
// ...
tasks {
// ...
shadowJar {
// ...
ignoreRelocations("org/example/plugin/utils/PaperInventoryFactory.class")
}
}
2
3
4
5
6
7
8
9
10
11
12
13
我的需求比较简单,所以现用 String 通过 mini message 转 Component。如果复杂一点,可以用 GsonComponentSerializer
作桥梁,沟通打包到你插件里的 adventure 和 paper 的 adventure。
顺带一提,可以加 ProtocolLib、packetevents 之类的依赖来解决 spigot 不支持在容器标题使用 adventure 的问题。不过,要加依赖插件的东西就不写到这个框架里了。如有需要,请参见 SweetMail 的实现 (opens new window)。