本帖最后由 berry64 于 2019-12-29 19:17 编辑


第11章:NMS的另类食用方法

什么是NMS?
众所周知,现在各大服务端核心(spigot, craftbukkit, cauldron)都是基于Mojang官方开发的服务器内核(sponge除外)搭建的。
虽然这些服务端核心给我们提供了很多便利的API,但在这之中难免会有一些功能没有完全地被api继承,所以有时候开发者们会需要通过直接调用Mojang的代码才能达到某些特殊效果(比如向玩家发送特定数据包)。

而这些Mojang代码大部分都属于net.minecraft.server包,便有了NMS简称。
しかし(但是)

Mojang的代码不光混淆过,还有一个很烦人的尿性:每个mc版本的包名都不一样
比如1.7.10的包名就是net.minecraft.server.v1_7_R4, 而1.14.4的包名就是net.minecraft.server.v1_14_R4
不过虽然里面的代码会根据版本改变,但很多时候要调用的方法都不会变

然鹅这时候如果你的包名不一样的话,JVM就懵逼了:就算这个类存在也有这个方法,JVM却找不到该版本对应的类,然后你的插件就不能跨版本了。

普通的解决办法
面对这个棘手的问题,大家出了很多不同的解决办法:
办法1:反射

方法2:代理

方法3:Libs64

Libs64NMSPatcher食用方法
Libs64使用了最前沿最高端的Java字节码修改技术(其实就是ASM)通过修改字节码达到兼容版本的效果。
而且使用方法又简单,只需在插件启动的时候(onEnable)里加入一行代码:
  1. Libs64NMSPatcher.registerPatch(插件, 类名, 插件开发时的版本)
复制代码

这里的插件就用你插件的实例,比如如果是在主类里调用这个方法的话就是this
类名就是需要打补丁的类,比如如果这个类是net.berry64.test.NMSClass那么这里就是"net.berry64.test.NMSClass"
切记这里不能用NMSClass.class.getName()! 因为这样的话JVM会加载你本身的类
而插件版本就是你开发时使用的API版本,比如你用的是1.7.10的话那么你的版本就是v1_7_R4,那么你这里就可以写new ServerVersion(1,7,4)


NMS实用案例

现在我们可以跨版本调用NMS了,那我们就可以愉快的使用NMS带来的便利了:
NMS最常用的用法是用来向玩家发包,这里我直接放上代码:
  1. package net.berry64.nms;

  2. import net.minecraft.server.v1_12_R1.PacketPlayOutWorldParticles;
  3. import net.minecraft.server.v1_12_R1.PlayerConnection;
  4. import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer;
  5. import org.bukkit.entity.Player;

  6. public class NMSClass{
  7.         public static void sendPacket(Player player) { //Player是Bukkit的API
  8.                 CraftPlayer cp = (CraftPlayer)player; //获取bukkit内部玩家实例
  9.                 PlayerConnection pc = cp.getHandle().playerConnection; //获取NMS内部玩家接口
  10.                 pc.sendPacket(new PacketPlayOutWorldParticles());//发送数据包
  11.         }
  12. }
复制代码
至此,你会发现这里很多都用了指定版本的API(这里用的是1.12.2)不过没关系,因为我们这里主类使用了Libs64黑科技(淘宝宣传词?)
主类:
  1. public void onEnable(){
  2.         Libs64NMSPatcher.registerPatch(this, "net.berry64.nms.NMSClass", new ServerVersion(1,12,2));
  3. }

  4. public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args){
  5.         if(cmd.getName().equalsIgnoreCase("test") && sender instanceof Player){
  6.                 NMSClass.sendPacket((Player)sender);
  7.         }
  8. }
复制代码
现在这个插件就可以在非1.12.2的版本上运行了


哇塞,这么方便,哪里下载?
不要急不要急,人人都有份
现在下载Libs64 -> 这里 获取属于你的快乐让开发变简单

读到这里你可能发现了,嘿嘿,这是不是像个广告