提供: Minecraft Modding Wiki
移動先: 案内検索
(新規作成)
 
(FML仕様のパケットシステムに変更。)
1行目: 1行目:
{{前提MOD|reqmod="Minecraft Forge Universal 10.12.0.xxx~"}}
+
{{前提MOD|reqmod="Minecraft Forge Universal 10.12.1.1090~"}}
  
 
==パケットについて==
 
==パケットについて==
 
<p>1.7.2でパケット関連のシステムが大幅に変更され、nettyと呼ばれるオープンプロジェクトのシステムを利用したHandshake方式になりました。<br>
 
<p>1.7.2でパケット関連のシステムが大幅に変更され、nettyと呼ばれるオープンプロジェクトのシステムを利用したHandshake方式になりました。<br>
[http://www.minecraftforge.net/wiki/Netty_Packet_Handling Sirgingalot氏によるチュートリアル]が良いものなので、それの和訳ベースで幾つか注釈をつけて解説したいと思います。</p>
+
以前ここで解説したパケットシステムはメモリリークを誘発するので、こちらの方法が推奨されています。</p>
  
==はじめに==
+
==ソースコード==
これは、FMLで用意されているSimpleChannelHandlerの代替手段です。(注:というのも、SimpleChannelHandler は現状上手く動作しない)<br>
+
===PacketHander===
この手法では、自動でパケットの識別子が生成され、クライアント、サーバーの両方の処理を1つのパケットクラス内で処理することが出来ます。
 
 
 
==パケット構造の例==
 
今回は、以下に記すAbstract Classをパケットをすべてのパケットで継承し、その上で、必要な拡張を行う形を取ります。 <br>
 
サイド別のパケット受け取り処理はそれぞれ'handle'メソッドで行うことが出来ます。<br>
 
注意:このクラスを継承した子クラスには必ず、空のコンストラクタを設定して下さい。<br>
 
(注:つまり、引数を持つコンストラクタを作ったらな、必ず、引数なしのものも作る必要が有るということです。)<br>
 
(注:あとで、パケットクラスの例を載せますが、引数ありのコンストラクタを用意するのが色々便利なので、空のコンストラクタを付けることを忘れないようにして下さい。)<br>
 
===AbstractPacket Class===
 
AbstractPacket.java
 
<source lang = "java">
 
package あなたのMODのパッケージ;
 
 
 
import io.netty.buffer.ByteBuf;
 
import io.netty.channel.ChannelHandlerContext;
 
import net.minecraft.entity.player.EntityPlayer;
 
 
 
 
 
/**
 
* AbstractPacket クラス PacketPipelineで扱うパケットはすべてこのクラスを継承する.
 
* @author sirgingalot
 
*/
 
public abstract class AbstractPacket {
 
 
 
/**
 
* パケットのデータをByteBufに変換する. 複雑なデータ(NBTとか)はハンドラーが用意されている ( @link{cpw.mods.fml.common.network.ByteBuffUtils}を見てくれ)
 
*
 
* @param ctx    channel context(和訳注:殆ど使わない)
 
* @param buffer 変換するByteBufの変数。これにwriteしていく。
 
*/
 
public abstract void encodeInto(ChannelHandlerContext ctx, ByteBuf buffer);
 
 
 
/**
 
* ByteBufからデータを取り出す. 複雑なデータはハンドラーが用意されている ( @link{cpw.mods.fml.common.network.ByteBuffUtils}を見てくれ)
 
*
 
* @param ctx    channel context(普段は使わない)
 
* @param buffer データを取り出すByteBuf。これからreadしていく。
 
*/
 
public abstract void decodeInto(ChannelHandlerContext ctx, ByteBuf buffer);
 
 
 
/**
 
* クライアント側でパケットを受け取った後処理するメソッド。decodeIntoでByteBufが読み出されたあとに実行される。
 
*
 
* @param player the player reference
 
*/
 
public abstract void handleClientSide(EntityPlayer player);
 
 
 
/**
 
* サーバー側でパケットを受け取った後処理するメソッド。decodeIntoでByteBufが読み出されたあとに実行される。
 
*
 
* @param player the player reference
 
*/
 
public abstract void handleServerSide(EntityPlayer player);
 
}
 
</source>
 
==パケットハンドラー==
 
パケットを処理するメインのクラスです。
 
インラインエンコード/デコード処理を可能とする、パケットの識別子は自動的に生成されます。
 
また、再度別処理をパケットクラスで行うことも可能となります。
 
注:チャンネル名称を必ず変更して下さい。(現在"チャンネル名称"になっている)
 
 
 
===PacketPipeline Class===
 
 
<source lang = "java">
 
<source lang = "java">
package あなたのMODのパッケージ;
 
  
import io.netty.buffer.ByteBuf;
+
package mods.samplepacketmod;
import io.netty.buffer.Unpooled;
 
import io.netty.channel.ChannelHandler;
 
import io.netty.channel.ChannelHandlerContext;
 
import io.netty.handler.codec.MessageToMessageCodec;
 
  
import java.util.Collections;
 
import java.util.Comparator;
 
import java.util.EnumMap;
 
import java.util.LinkedList;
 
import java.util.List;
 
 
import net.minecraft.client.Minecraft;
 
import net.minecraft.entity.player.EntityPlayer;
 
import net.minecraft.entity.player.EntityPlayerMP;
 
import net.minecraft.network.INetHandler;
 
import net.minecraft.network.NetHandlerPlayServer;
 
import cpw.mods.fml.common.FMLCommonHandler;
 
import cpw.mods.fml.common.network.FMLEmbeddedChannel;
 
import cpw.mods.fml.common.network.FMLOutboundHandler;
 
 
import cpw.mods.fml.common.network.NetworkRegistry;
 
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.network.internal.FMLProxyPacket;
+
import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper;
 
import cpw.mods.fml.relauncher.Side;
 
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
 
  
/**
 
* Packet pipeline クラス。パケットの登録と、パケットのSendToを行う。
 
* @author sirgingalot
 
* some code from: cpw
 
*/
 
@ChannelHandler.Sharable
 
public class PacketPipeline extends MessageToMessageCodec<FMLProxyPacket, AbstractPacket> {
 
  
  //こちらで使わないので、知る必要はないが、登録するChannelの変数。基本的に1MOD(1 packetPipelineインスタンス)に1Channel
+
public class PacketHandler {
private EnumMap<Side, FMLEmbeddedChannel>          channels;
 
//こちらも使わない。Packetクラスの保存先。1Channelに付き256種類のPacketが登録できる。
 
private LinkedList<Class<? extends AbstractPacket>> packets          = new LinkedList<Class<? extends AbstractPacket>>();
 
//使わない。PostInitされたかどうか。
 
private boolean                                    isPostInitialised = false;
 
  
/**
+
    //このMOD用のSimpleNetworkWrapperを生成。チャンネルの文字列は固有であれば何でも良い。MODIDの利用を推奨。
* pipelineにpacketを登録するメソッド。識別子は自動でセットされる。256個までしか登録できない。
+
    public static final SimpleNetworkWrapper INSTANCE = NetworkRegistry.INSTANCE.newSimpleChannel("SamplePacketMod");
*
 
* @param clazz the class to register
 
*
 
* @return 登録が成功したかどうか。256個以上の登録、同クラス登録、postInitialise内で登録するとfalseになる。
 
*/
 
public boolean registerPacket(Class<? extends AbstractPacket> clazz) {
 
if (this.packets.size() > 256) {
 
  //256個以上登録しようとした。
 
// You should log here!!(logを出力すべき。)
 
return false;
 
}
 
  
if (this.packets.contains(clazz)) {
 
  //同じクラスを登録しようとした。
 
// You should log here!!
 
return false;
 
}
 
  
if (this.isPostInitialised) {
+
    public static void init() {
  //postinit処理で登録しようとした。
 
// You should log here!!
 
return false;
 
}
 
  
this.packets.add(clazz);
+
        /*IMesssageHandlerクラスとMessageクラスの登録。今回同じクラスにしているが、別々でもよい。
return true;
+
        *第三引数:MessageクラスのMOD内での登録ID。256個登録できる
}
+
        *第四引数:送り先指定。クライアントかサーバーか、Side.CLIENT Side.SERVER*/
 +
        INSTANCE.registerMessage(MessageSample.class, MessageSample.class, 0, Side.SERVER);
 +
    }
 +
}</source>
  
// packetのエンコード処理。ここで、識別子が設定されている。
+
===MessageSample===
@Override
+
<source lang = "java">
protected void encode(ChannelHandlerContext ctx, AbstractPacket msg, List<Object> out) throws Exception {
+
package mods.samplepacketmod;
ByteBuf buffer = Unpooled.buffer();
 
Class<? extends AbstractPacket> clazz = msg.getClass();
 
if (!this.packets.contains(msg.getClass())) {
 
throw new NullPointerException("No Packet Registered for: " + msg.getClass().getCanonicalName());
 
}
 
  
byte discriminator = (byte) this.packets.indexOf(clazz);
+
import cpw.mods.fml.client.FMLClientHandler;
buffer.writeByte(discriminator);
+
import cpw.mods.fml.common.FMLCommonHandler;
msg.encodeInto(ctx, buffer);
+
import cpw.mods.fml.common.network.simpleimpl.IMessage;
FMLProxyPacket proxyPacket = new FMLProxyPacket(buffer.copy(), ctx.channel().attr(NetworkRegistry.FML_CHANNEL).get());
+
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;
out.add(proxyPacket);
+
import cpw.mods.fml.common.network.simpleimpl.MessageContext;
}
+
import io.netty.buffer.ByteBuf;
 
 
// packetのデコード処理。
 
@Override
 
protected void decode(ChannelHandlerContext ctx, FMLProxyPacket msg, List<Object> out) throws Exception {
 
ByteBuf payload = msg.payload();
 
byte discriminator = payload.readByte();
 
Class<? extends AbstractPacket> clazz = this.packets.get(discriminator);
 
if (clazz == null) {
 
throw new NullPointerException("No packet registered for discriminator: " + discriminator);
 
}
 
 
 
AbstractPacket pkt = clazz.newInstance();
 
pkt.decodeInto(ctx, payload.slice());
 
 
 
EntityPlayer player;
 
switch (FMLCommonHandler.instance().getEffectiveSide()) {
 
case CLIENT:
 
player = this.getClientPlayer();
 
pkt.handleClientSide(player);
 
break;
 
 
 
case SERVER:
 
INetHandler netHandler = ctx.channel().attr(NetworkRegistry.NET_HANDLER).get();
 
player = ((NetHandlerPlayServer) netHandler).playerEntity;
 
pkt.handleServerSide(player);
 
break;
 
 
 
default:
 
}
 
 
 
out.add(pkt);
 
}
 
 
 
// 初期化メソッド。FMLInitializationEventで呼び出す。
 
public void initialise() {
 
this.channels = NetworkRegistry.INSTANCE.newChannel("チャンネル名称", this);
 
}
 
  
// post初期化メソッド。FMLPostInitializationEventで呼び出す。
+
public class MessageSample implements IMessage, IMessageHandler<MessageSample, IMessage> {
// packetの識別子がクライントとサーバーで同一なものか確認している。
 
public void postInitialise() {
 
if (this.isPostInitialised) {
 
return;
 
}
 
 
 
this.isPostInitialised = true;
 
Collections.sort(this.packets, new Comparator<Class<? extends AbstractPacket>>() {
 
 
 
@Override
 
public int compare(Class<? extends AbstractPacket> clazz1, Class<? extends AbstractPacket> clazz2) {
 
int com = String.CASE_INSENSITIVE_ORDER.compare(clazz1.getCanonicalName(), clazz2.getCanonicalName());
 
if (com == 0) {
 
com = clazz1.getCanonicalName().compareTo(clazz2.getCanonicalName());
 
}
 
 
 
return com;
 
}
 
});
 
}
 
  
@SideOnly(Side.CLIENT)
+
    private byte data;
private EntityPlayer getClientPlayer() {
 
return Minecraft.getMinecraft().thePlayer;
 
}
 
  
/**
+
    public MessageSample(){}
* プレイヤー全員にpacketを送る。
 
* <p/>
 
* Adapted from CPW's code in cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper
 
*
 
* @param message The message to send
 
*/
 
public void sendToAll(AbstractPacket message) {
 
this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.ALL);
 
this.channels.get(Side.SERVER).writeAndFlush(message);
 
}
 
  
/**
+
    public MessageSample(byte par1) {
* あるプレイヤーにpacketを送る。
+
        this.data= par1;
* <p/>
+
    }
* Adapted from CPW's code in cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper
 
*
 
* @param message The message to send
 
* @param player  The player to send it to
 
*/
 
public void sendTo(AbstractPacket message, EntityPlayerMP player) {
 
this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.PLAYER);
 
this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(player);
 
this.channels.get(Side.SERVER).writeAndFlush(message);
 
}
 
  
/**
+
    @Override//IMessageのメソッド。ByteBufからデータを読み取る。
* ある一定範囲内にいるプレイヤーにpacketを送る。
+
    public void fromBytes(ByteBuf buf) {
* <p/>
+
        this.data= buf.readByte();
* Adapted from CPW's code in cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper
+
    }
*
 
* @param message The message to send
 
* @param point  The {@link cpw.mods.fml.common.network.NetworkRegistry.TargetPoint} around which to send
 
*/
 
public void sendToAllAround(AbstractPacket message, NetworkRegistry.TargetPoint point) {
 
this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.ALLAROUNDPOINT);
 
this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(point);
 
this.channels.get(Side.SERVER).writeAndFlush(message);
 
}
 
  
/**
+
    @Override//IMessageのメソッド。ByteBufにデータを書き込む。
* 指定ディメンションにいるプレイヤー全員にpacketを送る。
+
     public void toBytes(ByteBuf buf) {
* <p/>
+
        buf.writeByte(this.data);
* Adapted from CPW's code in cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper
+
    }
*
 
* @param message     The message to send
 
* @param dimensionId The dimension id to target
 
*/
 
public void sendToDimension(AbstractPacket message, int dimensionId) {
 
this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.DIMENSION);
 
this.channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(dimensionId);
 
this.channels.get(Side.SERVER).writeAndFlush(message);
 
}
 
  
/**
+
    @Override//IMessageHandlerのメソッド
* サーバーにpacketを送る。
+
    public IMessage onMessage(MessageKeyPressed message, MessageContext ctx) {
* <p/>
+
        //クライアントへ送った際に、Worldインスタンスはこのように取れる。
* Adapted from CPW's code in cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper
+
        //FMLClientHandler.instance().getClient().theWorld
*
+
        //サーバーへ送った際に、EntityPlayerインスタンス(EntityPlayerMPインスタンス)はこのように取れる。
* @param message The message to send
+
        //EntityPlayer entityPlayer = ctx.getServerHandler().playerEntity;
*/
+
        //Do something.
public void sendToServer(AbstractPacket message) {
+
        return null;//本来は返答用IMessageインスタンスを返すのだが、旧来のパケットの使い方をするなら必要ない。
this.channels.get(Side.CLIENT).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.TOSERVER);
+
    }
this.channels.get(Side.CLIENT).writeAndFlush(message);
 
}
 
 
}
 
}
 
</source>
 
</source>
==Pipelineの登録==
+
===@Modクラス内の記述===
上のクラスを以下の方法でFMLに登録します。
 
===@Modクラス内での記述===
 
 
<source lang = "java">
 
<source lang = "java">
public static final PacketPipeline packetPipeline = new PacketPipeline();
+
package mods.samplepacketmod;
  
@EventHandler
+
import cpw.mods.fml.common.Mod;
public void initialise(FMLInitializationEvent evt) {
+
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
packetPipeline.initialise();
 
//ここでパケットクラスの登録をする。
 
packetPipeline.registerPacket(KeyHandlingPacket.class);
 
}
 
  
@EventHandler
+
@Mod(modid="SamplePacketMod", name="SamplePacketMod", version="1.0.0",dependencies="required-after:Forge@[10.12.1.1090,)")
public void postInitialise(FMLPostInitializationEvent evt) {
+
public class SamplePacketMod {
packetPipeline.postInitialise();
+
@Mod.Instance("SamplePacketMod")
 +
public static SamplePacketMod instance;
 +
@Mod.EventHandler
 +
public void preInit(FMLPreInitializationEvent event)
 +
{
 +
        PacketHandler.init();
 +
}
 
}
 
}
 
</source>
 
</source>
==パケットの登録==
+
===パケットを送る際の記述===
パケットの登録は、先ほどのpacketPipelineクラスのinitialise()メソッド内か、例のソースのようにinitialise()メソッド後に行って下さい。
 
==パケットクラスの例==
 
私が動作確認したパケットクラスを載せておきます。
 
また、Tinker's Constructの例は
 
[https://github.com/SlimeKnights/TinkersConstruct/tree/1.7/src/main/java/tconstruct/util/network/packet Tinker's Construct Packets]
 
で見ることが出来ます。
 
 
<source lang = "java">
 
<source lang = "java">
package ak.ChainDestruction;
+
PacketHandler.INSTANCE.sendToServer(new MessageSample(data));
 
+
</source>
import io.netty.buffer.ByteBuf;
+
==解説==
import io.netty.channel.ChannelHandlerContext;
+
Nettyを利用していることには変わりないので、パケット(Message)の書き方が1.6とは大分違ったものとなっている。<br>
import net.minecraft.entity.player.EntityPlayer;
+
ソース内に必要なコメントは載せているので、パケットの流れを解説する。<br>
//クライアント側でキー入力によって変化したboolean変数をサーバー側に伝達するパケット。AbstractPacketを継承
 
public class KeyHandlingPacket extends AbstractPacket
 
{
 
  //保持しておくboolean型変数
 
  boolean toggle;
 
  boolean digUnderToggle;
 
  //引数を持つコンストラクタを追加する場合は、空のコンストラクタを用意してくれとのこと。
 
  public KeyHandlingPacket() {
 
  }
 
  //パケット生成を簡略化するために、boolean型変数を引数に取るコンストラクタを追加。
 
  public KeyHandlingPacket(boolean ver1, boolean ver2) {
 
  toggle = ver1;
 
  digUnderToggle = ver2;
 
  }
 
  
  @Override
+
PacketHandler.INSTANCE.sendToServer<br>
  public void encodeInto(ChannelHandlerContext ctx, ByteBuf buffer) {
+
↓<br>
  //ByteBufに変数を代入。基本的にsetメソッドではなく、writeメソッドを使う。
+
IMessageのtoBytesメソッド<br>
  buffer.writeBoolean(toggle);
+
↓<br>
  buffer.writeBoolean(digUnderToggle);
+
サーバーorクライアントへ<br>
  }
+
↓<br>
 
+
IMessageのfromBytesメソッド<br>
  @Override
+
↓<br>
  public void decodeInto(ChannelHandlerContext ctx, ByteBuf buffer) {
+
IMessageHandlerのonMessageメソッド<br>
  //ByteBufから変数を取得。こちらもgetメソッドではなく、readメソッドを使う。
+
IMessageクラスに引数を指定したコンストラクタを用意する必要はないが、例のように、用意したほうが利便性は高い。
  toggle = buffer.readBoolean();
 
  digUnderToggle = buffer.readBoolean();
 
 
 
  }
 
 
 
  @Override
 
  public void handleClientSide(EntityPlayer player) {
 
  //今回はクライアントの情報をサーバーに送るので、こちらはなにもしない。
 
  //NO OP
 
 
 
  }
 
 
 
  @Override
 
  public void handleServerSide(EntityPlayer player) {
 
  //代入したいクラスの変数に代入。Worldインスタンスはplayerから取得できる。
 
  ChainDestruction.interactblockhook.toggle = toggle;
 
  ChainDestruction.interactblockhook.digUnderToggle = digUnderToggle;
 
 
 
  }
 
 
 
}
 
</source>
 

2014年5月31日 (土) 21:43時点における版

この記事は"Minecraft Forge Universal 10.12.1.1090~"を前提MODとしています。

パケットについて

1.7.2でパケット関連のシステムが大幅に変更され、nettyと呼ばれるオープンプロジェクトのシステムを利用したHandshake方式になりました。
以前ここで解説したパケットシステムはメモリリークを誘発するので、こちらの方法が推奨されています。

ソースコード

PacketHander

package mods.samplepacketmod;

import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper;
import cpw.mods.fml.relauncher.Side;


public class PacketHandler {

    //このMOD用のSimpleNetworkWrapperを生成。チャンネルの文字列は固有であれば何でも良い。MODIDの利用を推奨。
    public static final SimpleNetworkWrapper INSTANCE = NetworkRegistry.INSTANCE.newSimpleChannel("SamplePacketMod");


    public static void init() {

        /*IMesssageHandlerクラスとMessageクラスの登録。今回同じクラスにしているが、別々でもよい。
        *第三引数:MessageクラスのMOD内での登録ID。256個登録できる
        *第四引数:送り先指定。クライアントかサーバーか、Side.CLIENT Side.SERVER*/
        INSTANCE.registerMessage(MessageSample.class, MessageSample.class, 0, Side.SERVER);
    }
}

MessageSample

package mods.samplepacketmod;

import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.network.simpleimpl.IMessage;
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;
import cpw.mods.fml.common.network.simpleimpl.MessageContext;
import io.netty.buffer.ByteBuf;

public class MessageSample implements IMessage, IMessageHandler<MessageSample, IMessage> {

    private byte data;

    public MessageSample(){}

    public MessageSample(byte par1) {
        this.data= par1;
    }

    @Override//IMessageのメソッド。ByteBufからデータを読み取る。
    public void fromBytes(ByteBuf buf) {
        this.data= buf.readByte();
    }

    @Override//IMessageのメソッド。ByteBufにデータを書き込む。
    public void toBytes(ByteBuf buf) {
        buf.writeByte(this.data);
    }

    @Override//IMessageHandlerのメソッド
    public IMessage onMessage(MessageKeyPressed message, MessageContext ctx) {
        //クライアントへ送った際に、Worldインスタンスはこのように取れる。
        //FMLClientHandler.instance().getClient().theWorld
        //サーバーへ送った際に、EntityPlayerインスタンス(EntityPlayerMPインスタンス)はこのように取れる。
        //EntityPlayer entityPlayer = ctx.getServerHandler().playerEntity;
        //Do something.
        return null;//本来は返答用IMessageインスタンスを返すのだが、旧来のパケットの使い方をするなら必要ない。
    }
}

@Modクラス内の記述

package mods.samplepacketmod;

import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;

@Mod(modid="SamplePacketMod", name="SamplePacketMod", version="1.0.0",dependencies="required-after:Forge@[10.12.1.1090,)")
public class SamplePacketMod {
	@Mod.Instance("SamplePacketMod")
	public static SamplePacketMod instance;
	@Mod.EventHandler
	public void preInit(FMLPreInitializationEvent event)
	{
        PacketHandler.init();
	}
}

パケットを送る際の記述

PacketHandler.INSTANCE.sendToServer(new MessageSample(data));

解説

Nettyを利用していることには変わりないので、パケット(Message)の書き方が1.6とは大分違ったものとなっている。
ソース内に必要なコメントは載せているので、パケットの流れを解説する。

PacketHandler.INSTANCE.sendToServer

IMessageのtoBytesメソッド

サーバーorクライアントへ

IMessageのfromBytesメソッド

IMessageHandlerのonMessageメソッド
IMessageクラスに引数を指定したコンストラクタを用意する必要はないが、例のように、用意したほうが利便性は高い。