本帖最后由 trychen 于 2017-7-15 08:34 编辑

用处:
  • 通过 Sign 作为输入密码的方式
  • 作为高级的输入方式
  • 显示提示

知识前提:
  • 掌握 Bukkit API
  • 知道关于“包”的基本概念
  • ProtocolLib 的基本使用

基本实现原理:
  • 创建包 (BLOCK_CHANGE),让客户端把玩家的最底下 (new Location(player.getLocation().getX(), 0, player.getLocation().getZ())) 基岩误设置为一个牌子;
  • 创建包 (UPDATE_SIGN),更新牌子的内容为我们想显示的内容;
  • 创建包 (OPEN_SIGN_ENTITY),让服务器打开牌子的UI
  • 发送上面的全部包,完成打开 Sign GUI

准备工作:
  • 创建一个 Bukkit 的项目,并导入 ProtocolLib 插件作为依赖库
  • 创建一个类用于存储我们生成的 Sign 归属者 (signLocation2Player)
  • 在上述类中创建方法 public boolean openSignGUI(Player player, String[] lines) 或 fun Player.openSignGUI():boolean
  • Go!


初始化变量:
  1. ProtocolManager manager = ProtocolLibrary.getProtocolManager();
复制代码
组为了让大家看的更清晰,代码尽量拆分的更短!下面开始创建 BLOCK_CHANGE 包:
  1. PacketContainer blockChange = manager.createPacket(PacketType.Play.Server.BLOCK_CHANGE);
  2. blockChange.getBlockPositionModifier().write(0, blockPosition);
  3. blockChange.getBlockData().write(0, WrappedBlockData.createData(Material.SIGN_POST));
复制代码
这样,ProtocolLib 就会创建一个 BLOCK_CHANGE 包,并修改里面的 BlockPosition 等变量为我们需要的内容,达到伪造一个牌子的效果。接着我们要创建 UPDATE_SIGN 包来更新我们需要的内容:
  1. PacketContainer updateSign = manager.createPacket(UPDATE_SIGN);
  2. updateSign.getBlockPositionModifier().write(0, blockPosition);
  3. updateSign.getChatComponentArrays().write(0, new WrappedChatComponent[]{WrappedChatComponent.fromText(lines[0]), WrappedChatComponent.fromText(lines[1]), WrappedChatComponent.fromText(lines[2]), WrappedChatComponent.fromText(lines[3])});
  4.                     
复制代码
这个包需要注意的是,版本差异较大,旧版本(1.8 || Older)可能是通过NBT来修改,这就需要以下方法:
  1. PacketContainer updateSign = manager.createPacket(UPDATE_SIGN);
  2. updateSign.getBlockPositionModifier().write(0, blockPosition);
复制代码
然后我们发送一个打开Sign GUI的包即可完成创建包了:
  1. PacketContainer open = manager.createPacket(OPEN_SIGN_ENTITY);
  2. open.getBlockPositionModifier().write(0, blockPosition);
复制代码
最后我们就可以把上述包都发送给玩家:
  1. manager.sendServerPacket(player, blockChange);
复制代码
这样我们就完成了打开牌子的UI

监听关闭我们打开的牌子
如果你是想通过牌子来修改某些东西,那我们就需要读取牌子修改后的内容,这时我们就需要通过监听包。
一般的实现原理:
  • 如果你不想监听,也要把客户端发过来的属于你打开的牌子的 UPDATE_SIGN 取消掉,否则服务器会发现对应方块不是一个牌子
  • 玩家在完成修改任意一个牌子后,客户端会发送包 (UPDATE_SIGN) 给服务器,其中包含了牌子修改后的数据(ESC 或 完成 都将发送)
  • 客户端发送的 UPDATE_SIGN 包 (PlayInUpdateSign) 中,包含了牌子的坐标,我们可以通过牌子的坐标和玩家确定是否是我们创建的牌子

开始监听
  • 在发送牌子后,存储玩家和坐标到一个 Map<Player, BlockPosition> map = new HashMap<>() ; //留意线程是否安全
  • 通过以下代码在 onEnable 中注册监听 UPDATE_SIGN
  1. <blockquote>manager.addPacketListener(this.packetListener = new PacketAdapter(plugin, new PacketType[]{PacketType.Play.Client.UPDATE_SIGN}) {
复制代码
以上代码纯手撸(这编辑器还吞格式,恶心),可能有写错,欢迎提出。

总结
这样你就使用 ProtocolLib 打开了 Sign GUI,本教程更希望你能掌握 ProtocolLib 的基本使用。

本文未仔细校验


[groupid=1181]Unknown Domain[/groupid]