认识 NMS 和 OBC
- NMS:
net.minecraft.server,存放 Minecraft 服务端的游戏逻辑代码 - OBC:
org.bukkit.craftbukkit,Bukkit API 的实现层,本质是 NMS 的封装
Bukkit API 涵盖很多功能,但不是万能的。当你想做的事 API 做不到,才需要碰 NMS。本教程以 Spigot 为例(CraftBukkit 也由 SpigotMC 维护,Bukkit 分支基本都基于 Spigot)。
用 NMS 之前:你真的需要吗?
Spigot 核心开发者 md_5 的话:Wait! 你真的需要 NMS 吗?
NMS 不是 API。遇到需求不该第一时间考虑 NMS 或发包。 令人费解的是,很多人专门用 NMS 做 ScoreBoard、BossBar、粒子这些API 早就有的事。
每次想用 NMS,先问自己:
- 我真的需要 NMS 做这个吗?
- 有没有现成 API 能实现?
- 我能不能贡献/提议一个 API?
滥用 NMS 的后果
- 插件失去版本迁移能力:NMS 混淆名每版不同,绑死单版本
- 阻碍 API 发展、树立坏榜样
能用 API 就别碰 NMS。这是社区共识。
开发环境准备
由于 DMCA 事件,Bukkit 不直接提供 NMS。你需把构建好的服务端 jar引入项目:
以 Gradle 为例:
- 项目根目录建
libs/文件夹 - 把构建好的服务端 jar(如
spigot-1.12.2.jar)拖进去 build.gradle里配置本地依赖:gradledependencies { implementation files('libs/spigot-1.12.2.jar') }- 同步 Gradle,就能 import NMS 的类了
Maven 类似
Maven 用 systemPath 或 mvn install 到本地仓库。IDEA 里也可直接 Project Structure → Libraries 加 jar。
从 API 背后拿 NMS 对象
直接打印一个 Player 对象,你会得到 CraftPlayer{name=玩家名}——不是 NMS 里的 EntityPlayer。因为 Bukkit API 背后有 OBC 这一层。
OBC 是 NMS 的封装,操作 NMS 封装成 API 就是 OBC 干的事。要拿到 API 背后的 NMS 对象,得强转 + 调对应方法:
// Player 背后其实是 CraftPlayer(OBC)
CraftPlayer craftPlayer = (CraftPlayer) player;
// 拿到 NMS 的 EntityPlayer
EntityPlayer entityPlayer = craftPlayer.getHandle();怎么知道有哪些方法?
用反编译工具(或 IDEA 直接查看类)看 CraftPlayer 源码,找它暴露了哪些 NMS 对象的访问方法。getHandle() 是最常见的入口。
反射与版本兼容
NMS 的类名、方法名是混淆的,每个版本都不一样。直接 import 写死,换个版本就崩。要做版本兼容,得用反射按版本动态调:
import org.bukkit.Bukkit;
public class NMSUtil {
private static String version;
static {
// 如 org.bukkit.craftbukkit.v1_12_R1.CraftPlayer
version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
}
public static Class<?> getCraftClass(String name) throws ClassNotFoundException {
return Class.forName("org.bukkit.craftbukkit." + version + "." + name);
}
public static String getVersion() {
return version; // v1_12_R1
}
}按 version 分支调不同版本的 NMS:
Object craftPlayer = NMSUtil.getCraftClass("entity.CraftPlayer").cast(player);
Method getHandle = craftPlayer.getClass().getMethod("getHandle");
Object entityPlayer = getHandle.invoke(craftPlayer);反射又慢又脆
反射有性能开销,且编译期查不出错(方法名拼错运行时才崩)。能用 API 就别用 NMS,必须用 NMS 就尽量封装好、覆盖目标版本。
现代方案:ProtocolLib / Paper
- ProtocolLib:封装了 NMS 发包/反射,自动处理版本兼容。绝大多数发包需求用它,别自己写反射
- Paper 1.20.5+:改用 Mojang 官方映射(mojang-mapped),NMS 类名不再是混淆名,可读性大增,但跨核心仍需注意
写在最后
NMS 是深水区。新手遇到"API 做不到"的需求,先找现成插件/ProtocolLib 是否已解决,再考虑碰 NMS。本教程只带你入门"怎么用",不教授 NMS 里有什么——那需要你反编译服务端自己探索。
下一篇自定义发包——把 NMS 用在"发数据包给客户端"这个最常见场景。
