アットウィキロゴ
bbc_mc @ moddingメモ
掲示板 掲示板 ページ検索 ページ検索 メニュー メニュー

bbc_mc @ moddingメモ

Netty Packet Handling(和訳)

最終更新:

bbc_mc

- view
メンバー限定 登録/ログイン

編集

翻訳者コメント 以下のページの個人的和訳です。http://www.minecraftforge.net/wiki/Netty_Packet_Handling

現在、翻訳中です。(2014/07/09)

なるたけ原文(英文)を併記しますので、意味不明・曖昧な場合は参照下さい。

AKさん日本語訳 http://forum.minecraftuser.jp/viewtopic.php?f=21 =18255 リンク切れ?


編集

Netty Packet Handling

Warning
 This page is marked as Outdated.
 It was made for older content and may cause problems.
 Please improve this article if you can.

注意!! このページの内容は「古い」と申告されています。 古い情報に基づき判断すると、問題が生じる可能性があります。 もし可能であれば、このページの内容を改善して下さい。

This is a How-To guide or Tutorial detailing a practice or process for Minecraft Forge or related software.

このガイドは「How-to:どうやってやるか」のガイド、またはチュートリアルです。 Minecraft Forge やその関係ソフトウェアを使う方法を示したものです。

This page was made for Minecraft 1.7.2.
It might not work with other versions.
This is a poor example of using Netty. It can cause memory leaks. 
It doesn't separate handlers from codecs properly.
It reimplements functionality existant in FML for months.
If you are using this, consider switching to using FMLIndexedMessageToMessageCodec, or better yet,use the simpleimpl Message functionality.

このページの内容は Minecraft 1.7.2 に対応していますが、他のバージョンでは動作しない可能性があります。

本ページで紹介するコードは Netty を使用するための簡易なものであり、メモリーリークを生じる可能性もあります。

Handler 類を正しく切り離していません。

FML に何ヶ月も既に存在している機能を再実装しています。

もし FML を使用しているのであれば、FMLIndexedMessageToMessageCodec や simpleimpl Message 機能を使用する事を検討してみて下さい。

コンテンツ/

1 DO NOT USE THIS IN NEW CODE OR CONVERSIONS
2 Example Packet Structure
  2.1 AbstractPacket Class
3 The Packet Handler
  3.1 PacketPipeline Class
4 Registering the Pipeline
  4.1 Within your @Mod Class
5 Registering Packets
6 Implementation
7 Authors

DO NOT USE THIS IN NEW CODE OR CONVERSIONS / 本文書の内容を新しいバージョンで使用しないでください

Below is a short alternative to the SimpleChannelHandler now present within FML.
It allows for automatic discriminator generation and sided packet handling within the packets themselves.

以下の内容は、FML の SimpleChannelHandler に現在は含まれている内容と僅かに異なるものです。

以下では、パケット自体に自動的にサイド(Server/Client)を識別し、ハンドリングする機能を追加しています。

編集

Example Packet Structure / 例示するパケットの構造

Below is a common abstract packet that should be extended by any packet that you wish to send.
Any resultant behaviour from the packet can be described in the side specific *handle* methods.
NOTE: All children of this class *MUST* have an empty constructor (multiple constructors is fine!)

以下に示すのは、あなたが作成したいパケットの拡張元とする汎用的な抽象クラスです。

パケットの受け取り処理は、それぞれ処理サイド(Server/Client)を指定した handle 関数で処理します。

メモ:この抽象クラスを拡張する全てのクラスは、”必ず”空のコンストラクタを持たなくてはなりません。

AbstractPacket Class

package you.packethandling

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;

import net.minecraft.entity.player.EntityPlayer;

/**
 * AbstractPacket class. Should be the parent of all packets wishing to use the PacketPipeline.
 * パケットの汎用抽象クラス。
 * 以下で示す PacketPipeline を使用したい場合は、本クラスから拡張した packet クラスを使用する事。
 * 
 * @author sirgingalot
 */
public abstract class AbstractPacket {

   /**
    * Encode the packet data into the ByteBuf stream.
    *   Complex data sets may need specific data handlers
    *    (See @link{cpw.mods.fml.common.network.ByteBuffUtils})
    *
    * @param ctx    channel context
    * @param buffer the buffer to encode into
    */
  public abstract void encodeInto(ChannelHandlerContext ctx, ByteBuf buffer);

   /**
    * Decode the packet data from the ByteBuf stream.
    *   Complex data sets may need specific data handlers
    *    (See @link{cpw.mods.fml.common.network.ByteBuffUtils})
    *
    * @param ctx    channel context
    * @param buffer the buffer to decode from
    */
  public abstract void decodeInto(ChannelHandlerContext ctx, ByteBuf buffer);

   /**
    * Handle a packet on the client side.
    *   Note this occurs after decoding has completed.
    *
    * @param player the player reference
    */
  public abstract void handleClientSide(EntityPlayer player);

   /**
    * Handle a packet on the server side.
    *   Note this occurs after decoding has completed.
    *
    * @param player the player reference
    */
  public abstract void handleServerSide(EntityPlayer player);
}

編集

The Packet Handler

Core packet handling.
Essentially it automatically maps a packet to a discriminator, allowing in line encoding/decoding of packet specific data.
It also allows sided behaviour to be handled by the packets themselves.
NOTE: Remember to rename the channel as it is currently "TUT"
以下、パケット処理クラスの主要部を説明する。

PacketPipeline Class

package you.packethandling;

import java.util.*;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;

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.internal.FMLProxyPacket;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;

/**
 * Packet pipeline class. Directs all registered packet data to be handled by the packets themselves.
 * @author sirgingalot
 * some code from: cpw
 */
@ChannelHandler.Sharable
public class PacketPipeline extends MessageToMessageCodec<FMLProxyPacket, AbstractPacket> {

   private EnumMap<Side, FMLEmbeddedChannel>           channels;
   private LinkedList<Class<? extends AbstractPacket>> packets           = new LinkedList<Class<? extends AbstractPacket>>();
   private boolean                                     isPostInitialised = false;

   /**
    * Register your packet with the pipeline. Discriminators are automatically set.
    *
    * @param clazz the class to register
    *
    * @return whether registration was successful. Failure may occur if 256 packets have been registered or if the registry already contains this packet
    */
   public boolean registerPacket(Class<? extends AbstractPacket> clazz) {
       if (this.packets.size() > 256) {
           // You should log here!!
           return false;
       }

       if (this.packets.contains(clazz)) {
           // You should log here!!
           return false;
       }

       if (this.isPostInitialised) {
           // You should log here!!
           return false;
       }

       this.packets.add(clazz);
       return true;
   }

   // In line encoding of the packet, including discriminator setting
   @Override
   protected void encode(ChannelHandlerContext ctx, AbstractPacket msg, List<Object> out) throws Exception {
       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);
       buffer.writeByte(discriminator);
       msg.encodeInto(ctx, buffer);
       FMLProxyPacket proxyPacket = new FMLProxyPacket(buffer.copy(), ctx.channel().attr(NetworkRegistry.FML_CHANNEL).get());
       out.add(proxyPacket);
   }

   // In line decoding and handling of the 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);
   }

   // Method to call from FMLInitializationEvent
   public void initialise() {
       this.channels = NetworkRegistry.INSTANCE.newChannel("TUT", this);
   }

   // Method to call from FMLPostInitializationEvent
   // Ensures that packet discriminators are common between server and client by using logical sorting
   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 EntityPlayer getClientPlayer() {
       return Minecraft.getMinecraft().thePlayer;
   }

   /**
    * Send this message to everyone.
    * <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);
   }

   /**
    * Send this message to the specified player.
    * <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);
   }

   /**
    * Send this message to everyone within a certain range of a point.
    * <p/>
    * 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);
   }

   /**
    * Send this message to everyone within the supplied dimension.
    * <p/>
    * 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);
   }

   /**
    * Send this message to the server.
    * <p/>
    * Adapted from CPW's code in cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper
    *
    * @param message The message to send
    */
   public void sendToServer(AbstractPacket message) {
       this.channels.get(Side.CLIENT).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.TOSERVER);
       this.channels.get(Side.CLIENT).writeAndFlush(message);
   }
}

編集

Registering the Pipeline

Because of the self contained nature of the packet pipeline the following is all that needs to be done to register your handler within FML

Within your @Mod Class

public static final PacketPipeline packetPipeline = new PacketPipeline();

@EventHandler
public void initialise(FMLInitializationEvent evt) {
   packetPipeline.initialise();
}

@EventHandler
public void postInitialise(FMLPostInitializationEvent evt) {
   packetPipeline.postInitialise();
}

編集

Registering Packets

Packets can be registered up to the postInitialisation phase of the packet pipeline.
Packet registration is performed by calling the registerPacket(Class<? extends AbstractPacket> clazz) method in the Packet Pipeline.

編集

Implementation

Using the packet pipeline is as simple as writing a custom class extending AbstractPacket and registering it with the pipeline. For example implementations please look at the links below: 
Tinker's Construct Packets (Many thanks to fuj1n)
Authors
  • Sirgingalot 15:59 19 January 2014

編集

記事メニュー
最近更新されたスレッド
ウィキ募集バナー