LoginSignup
14
3

More than 3 years have passed since last update.

SpigotプラグインでProtocolLibを使ってみる

Posted at

ProtocolLibとはなんぞや

マインクラフトのサーバーとクライアント間で送受信されるパケットの操作を簡単にするプラグイン。

パケット操作の例

  • 偽のモブのスポーン
  • 偽のブロックの設置
  • 別の種類のMobに変身(応用)

他にも色々できる。

通常の場合

ProtocolLibを使わない通常の場合、BukkitAPIのみでパケット操作はできないので、NMSを扱わないといけない。

送信時

Packet packet = new PacketPlayOutBlockChange(new BlockPosition(0, 0, 0), Blocks.STONE.getBlockData());
((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);

受信時

    private void injectPlayer(Player player) {
        ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {

            @Override
            public void channelRead(ChannelHandlerContext channelHandlerContext, Object packet) throws Exception {
                // 受信時の処理
                super.channelRead(channelHandlerContext, packet);
            }

            @Override
            public void write(ChannelHandlerContext channelHandlerContext, Object packet, ChannelPromise channelPromise) throws Exception {
                // 送信時の処理
                super.write(channelHandlerContext, packet, channelPromise);
            }

        };

        ChannelPipeline pipeline = ((CraftPlayer) player).getHandle().playerConnection.networkManager.channel.pipeline();
        pipeline.addBefore("packet_handler", player.getName(), channelDuplexHandler);

    }

PlayerConnectionクラスを継承する方法もある

public class CustomPlayerConnection extends PlayerConnection {

    public CustomPlayerConnection(EntityPlayer entityPlayer) {
        super(MinecraftServer.getServer(), entityPlayer.playerConnection.networkManager, entityPlayer);
    }

    @Override
    public void a(PacketPlayInBlockPlace packetplayinblockplace) {
        // ブロック設置のパケットが送られてきたときの処理
        super.a(packetplayinblockplace);
    }

}
// 参加時にplayerConnectionを自作クラスに書き換える
@EventHandler
public void onJoin(PlayerJoinEvent playerJoinEvent) {
    EntityPlayer entityPlayer = ((CraftPlayer) playerJoinEvent.getPlayer()).getHandle();
    entityPlayer.playerConnection = new CustomPlayerConnection(entityPlayer);
}

NMSは触りたくない!!!!!

ということでProtoclLibを使ってみよう!

導入

plugin.ymlのdependにProtocolを追加

plugin.yml
name: PluginName
version: 1.0
main: plugin.yourplugin
api-version: 1.16
depend: [ProtocolLib]

依存関係の追加(Maven)

pom.xml
<repository>
  <id>dmulloy2-repo</id>
  <url>https://repo.dmulloy2.net/repository/public/</url>
</repository>

<dependency>
  <groupId>com.comphenix.protocol</groupId>
  <artifactId>ProtocolLib</artifactId>
  <version>4.6.0</version>
</dependency>

依存関係の追加(Gradle)

build.gradle
repositories {
    maven { url "https://repo.dmulloy2.net/repository/public/" }
}

dependencies {
   compileOnly group: "com.comphenix.protocol", name: "ProtocolLib", version: "4.6.0";
}

※2021/4/22現在、4.6.0が最新

パケットを送信する

試しに、ブロックを設置するパケットを送信してみます。

ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();

// パケットコンテナを作成
PacketContainer packetContainer = protocolManager.createPacket(PacketType.Play.Server.BLOCK_CHANGE);

// パケットのフィールドへ書き込み
packetContainer.getBlockPositionModifier().write(0, new BlockPosition(x, y, z));
packetContainer.getBlockData().write(0, WrappedBlockData.createData(Material.STONE));

// 送信
try {
    Player player; // 送信したいプレイヤー
    protocolManager.sendServerPacket(player, packetContainer);
} catch (InvocationTargetException e) {
    e.printStackTrace();
}

※BlockPositionクラスはNMSのではなく、ProtocolLibのもの

送信すると、サーバーにブロックが置かれず、クライアントのみにブロックが置かれる! (更新されるとで元に戻る)

パケットの受信する

パケットの受信とは、サーバーにプレイヤーからの偽のパケットを送信するイメージ。

試しにプレイヤーからチャットが送られてきた、というパケットをサーバーに受信させてみる。

ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();

// パケットコンテナを作成
PacketContainer packetContainer = protocolManager.createPacket(PacketType.Play.Client.CHAT);

// パケットのフィールドへ書き込み
packetContainer.getChatComponents().write(0, WrappedChatComponent.fromText("こんにちは"));
packetContainer.getChatTypes().write(0, EnumWrappers.ChatType.CHAT);

// 送信
try {
    Player player; // 送り主を指定
    protocolManager.recieveClientPacket(player, packetContainer);
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (InvocationTargetException e) {
    e.printStackTrace();
}

受信すると、プレイヤーから送られてもないチャットがサーバーに受け取られる

パケットの送信、受信時に処理を加える

BukkitAPIのイベントリスナーのパケット版的なことができる

クラスの定義

public class PacketListener extend PacketAdapter {

    public PacketListener(Plugin plugin, PacketType... types) {
        super(plugin, types);
    }

    @Override
    public void onPacketReceiving(PacketEvent event) {
        // パケットを受け取った時の処理
    }

    @Override
    public void onPacketSending(PacketEvent event) {
        // パケットを送った時の処理
    }
}

リスナー登録

ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
protocolManager.addPacketListener(new PacketListener(plugin, types));

コンストラクタの第二引数でイベントを呼ぶパケットの種類を指定できる。

まとめ

気が向いたらもうちょっと詳しい記事を出します。

14
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
3