七空幻音LOGO

七空幻音

Minecraft使用ProtocolLib向玩家发送木牌包2017-06-01 06:45:51

好久没动过bukkit了,很久没玩过mc了....前几天一个需求所以折腾了一下bukkit,使用bukkitapi向玩家发送sign的open和Update包也就是打开sign和修改sign的信息,然后我找了一些插件研究.

最后2种方法,1.使用ProtocolLib发包,2.艹nms

bukkitapi貌似就是不能发...据我所知,,我就用过bukkitapi修改过sign木牌信息..让玩家打开sigin到没怎么做过

最后当然是选择用ProtocolLib发包咯,nms太麻烦了(nms发包见最下面)


上图是之前插件一张图片...懒得开游戏弄一张...无视木牌内容就行....


先自行下载protocolib的插件吧:https://www.spigotmc.org/resources/protocollib.1997/

自行在plugin.yml里面添加

depend: [ProtocolLib]

先获取ProtocolManager

ProtocolManager pm = ProtocolLibrary.getProtocolManager();

注意:v1_8_3的核心及以上的版本和v1_8_3以下的核心版本发包是不同的所以需要区分一下

另外,游戏版本1.10以上的PacketPlayOutUpdateSign这个包没有了,所以无法在游戏1.10及以上使用

v1_8_3版本以下的发包代码

public static void send17Pack(Player p,String[] lines){
	ProtocolManager pm = ProtocolLibrary.getProtocolManager();
	try {
	    int x = p.getLocation().getBlockX();
	    int z = p.getLocation().getBlockZ();
	    PacketContainer change = pm.createPacket(PacketType.Play.Server.BLOCK_CHANGE);
	    change.getIntegers().write(0,x);
	    change.getIntegers().write(1,0);
	    change.getIntegers().write(2,z);
	    change.getIntegers().write(3,0);
	    change.getBlocks().write(0, Material.SIGN_POST);
	    pm.sendServerPacket(p, change);

	    PacketContainer update = pm.createPacket(PacketType.Play.Server.UPDATE_SIGN);
	    update.getIntegers().write(0,x);
	    update.getIntegers().write(1,0);
	    update.getIntegers().write(2,z);
	    update.getStringArrays().write(0,lines);
	    pm.sendServerPacket(p, update);

	    PacketContainer open = pm.createPacket(PacketType.Play.Server.OPEN_SIGN_ENTITY);
	    open.getIntegers().write(0,x);
	    open.getIntegers().write(1,0);
	    open.getIntegers().write(2,z);
	    pm.sendServerPacket(p, open);
	} catch (Exception e) {
            e.printStackTrace();
	}
}

v1_8_3版本及以上的发包代码

public static void send18Pack(Player p,String[] lines){
	ProtocolManager pm = ProtocolLibrary.getProtocolManager();
	try {
	    int x = p.getLocation().getBlockX();
	    int z = p.getLocation().getBlockZ();
	    PacketContainer change = pm.createPacket(PacketType.Play.Server.BLOCK_CHANGE);
	    change.getBlockPositionModifier().write(0, new BlockPosition(x, 0, z));
	    change.getBlockData().write(0, WrappedBlockData.createData(Material.SIGN_POST));
	    pm.sendServerPacket(p, change);

	    PacketContainer update = pm.createPacket(PacketType.Play.Server.UPDATE_SIGN);
	    update.getBlockPositionModifier().write(0, new BlockPosition(x, 0, z));
	    update.getChatComponentArrays().write(0, new WrappedChatComponent[] { 
	        WrappedChatComponent.fromText(lines[0]), 
	        WrappedChatComponent.fromText(lines[1]), 
	        WrappedChatComponent.fromText(lines[2]), 
	        WrappedChatComponent.fromText(lines[3]) });
	     pm.sendServerPacket(p, update);

	     PacketContainer open = pm.createPacket(PacketType.Play.Server.OPEN_SIGN_ENTITY);
	     open.getBlockPositionModifier().write(0, new BlockPosition(x, 0, z));
	     pm.sendServerPacket(p, open);
	} catch (Exception e) {
	    e.printStackTrace();
	}
}

上面的代码是发了3个包,一个修改方块,一个修改sign,一个发送sign

对应sign的2个包在nms里面是PacketPlayOutUpdateSignPacketPlayOutOpenSignEditor 

对应的参数可以反编译查看(写这篇的时候我是linux,,,,无奈jd-gui一直下载不下来...)

捕捉sign修改的包也可以用protocolib捕捉,先创建一个类用于监听

//自行导包(纯手打...)
public class SignUpdate extends PacketAdapter implements PacketListener{
    public SignUpdate(Plugin plugin, PacketType[] packets) {
	super(plugin,packets);
    }
    public void onPacketReceiving(PacketEvent e){
        PacketContainer pack = e.getPacket();
        if(pack.getType()==PacketType.Play.Client.UPDATE_SIGN){
            //sign修改触发
            //如果需要获取修改的内容,那么v1_8_3版本也会有区分
            //v1_8_3以下版本
            String[] signLine = (String[])pack.getStringArrays().getValues().get(0);
            //v1_8_3以上版本
            WrappedChatComponent[] signLine = (WrappedChatComponent[])pack.getChatComponentArrays().read(0);
            
            //然后这个signLine就是木牌修改的值了
            //signLine[0] signLine[1] signLine[2] signLine[3]
        }
    }
    
}

注册

//主类中添加
ProtocolManager pm = ProtocolLibrary.getProtocolManager();
PacketType[] packs = { PacketType.Play.Client.UPDATE_SIGN };
pm.addPacketListener(new SignUpdateEvent(主类,packs));

最后向玩家发送包

String[] lines = {"第一行文字","第二行文字","第三行文字","第四行文字"};
Player p = Bukkit.getServer().getPlayer("xxxx");
this.send18Pack(p,lines);//v1_8_3版本
this.send17Pack(p,lines);//v1_8_3以下版本

如何区分呢?自行判断当前版本号然后比较就ok了

关于nms发包的方法,我这里只写1.8.3以上的发包方法

v1_8_3版本及以上使用NMS的发包代码

public void openSign(Player player,String[] lines){
	//获取nms和craftbukkit的一些东西
	CraftPlayer p = (CraftPlayer)player;
	CraftWorld cw = (CraftWorld)p.getWorld();
	PlayerConnection pc = p.getHandle().playerConnection;
	//这里的World类是nms里面的World类  net.minecraft.server.v1_8_3.World
	World nmsWorld = (World)cw.getHandle();
	//获取一个方块的坐标
	BlockPosition block = new BlockPosition(p.getLocation().getBlockX(),0,p.getLocation().getBlockZ());
	//修改方块(因为后面需要修改内容)
	PacketPlayOutBlockChange change = new PacketPlayOutBlockChange(nmsWorld,block);
	change.block = Blocks.STANDING_SIGN.getBlockData();
	pc.sendPacket(change);
	//修改木牌
	PacketPlayOutUpdateSign update = new PacketPlayOutUpdateSign(nmsWorld,block,new IChatBaseComponent[]{
		ChatSerializer.a(lines[0]),
                ChatSerializer.a(lines[1]),
		ChatSerializer.a(lines[2]),
		ChatSerializer.a(lines[3])
        });
	pc.sendPacket(update);
	//打开
	PacketPlayOutOpenSignEditor open = new PacketPlayOutOpenSignEditor(block);
	pc.sendPacket(open);
}

其实方法和ProtocolLib发包区别不是很大,但是nms每个版本的包名都不同啊,而且有些类的方法名称也是改了的,比如这个版本是v1_8_3另一个版本是v1_8_2那么包名就完全不同的

以上就是ProtocolLib和nms的发包方法

注:因为修改木牌需要修改方块,所以一般都是把木牌放到基岩底层也就是0的位置,如果不放心,可以在修改完成的时候删除这个方块

详细图片
载入中...