- 771
- 5
Ниже приведен общий абстрактный пакет, который должен быть родителем любого пакета, который вы хотите отправить. Любой результат действия пакета могут быть описаны в специфических методах(handleClientSide и handleServerSide).
ПРИМЕЧАНИЕ: Все дочерные классы этого класса ДОЛЖНЫ иметь пустой конструктор (Можно использовать несколько конструкторов, но один должен быть пустой!).
Обработчик пакетов.
Ядро обработки пакетов. По сути, он автоматически сопоставляет пакет на дискриминатор, позволяющий в соответствии кодирования / декодирования конкретных данных пакетов.
ПРИМЕЧАНИЕ: Не забудьте переименовать канал на свой, а не как в туториале("TUT").
Регистрация PacketPipeline в главном классе мода.
Пример отправки пакета.
Давайте, например, сделаем пакет открытия гуи на сервере(это нужно, когда открываешь гуи на кнопку в KeyHandler'e).
Потом в KeyHandler'e просто пишем YourMod.packetPipeline.sendToServer(new PacketOpenGui(id));
Оригинальная статья: тык.
ПРИМЕЧАНИЕ: Все дочерные классы этого класса ДОЛЖНЫ иметь пустой конструктор (Можно использовать несколько конструкторов, но один должен быть пустой!).
Код:
package you.packethandling;
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. Сложные наборы данных, возможно, потребуют конкретных обработчиков данных(например, ItemStack) (См. {cpw.mods.fml.common.network.ByteBuffUtils})
*
* @param ctx контекст канала
* @param buffer буфер для кодирования в
*/
public abstract void encodeInto(ChannelHandlerContext ctx, ByteBuf buffer);
/**
* Декодировать пакет данных из потока ByteBuf. Сложные наборы данных, возможно, потребуют конкретных обработчиков данных(например, ItemStack) (См. {cpw.mods.fml.common.network.ByteBuffUtils})
*
* @param ctx контекст канала
* @param buffer буфер для кодирования из
*/
public abstract void decodeInto(ChannelHandlerContext ctx, ByteBuf buffer);
/**
* Действия пакета на стороне клиента. Обратите внимание, это происходит после завершения декодирования.
*
* @param player игрок
*/
public abstract void handleClientSide(EntityPlayer player);
/**
* Действия пакета на стороне сервера. Обратите внимание, это происходит после завершения декодирования.
*
* @param player игрок
*/
public abstract void handleServerSide(EntityPlayer player);
}
Обработчик пакетов.
Ядро обработки пакетов. По сути, он автоматически сопоставляет пакет на дискриминатор, позволяющий в соответствии кодирования / декодирования конкретных данных пакетов.
ПРИМЕЧАНИЕ: Не забудьте переименовать канал на свой, а не как в туториале("TUT").
Код:
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;
/**
* Класс PacketPipeline. Направляет все зарегистрированные пакетные данные на обработку.
* @author sirgingalot
* Некоторый код: 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;
/**
* Зарегистрировать свой пакет. Дискриминаторы устанавливаются автоматически.
*
* @param clazz класс пакета, который необходимо зарегистрировать.
*
* @return Вернуть тру, если регистрация была успешна. Отказ может произойти, если 256 пакетов было зарегистрировано или если в реестре уже содержится этот пакет.
*/
public boolean registerPacket(Class<? extends AbstractPacket> clazz) {
if(packets.size() > 256){
return false;
}
if(packets.contains(clazz)){
return false;
}
if(isPostInitialised){
return false;
}
packets.add(clazz);
return true;
}
//Кодирование пакета, в том числе настройки дискриминатора.
@Override
protected void encode(ChannelHandlerContext ctx, AbstractPacket msg, List<Object> out) throws Exception {
ByteBuf buffer = Unpooled.buffer();
Class<? extends AbstractPacket> clazz = msg.getClass();
if (!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);
}
//Декодирования и обработка пакета
@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() {
channels = NetworkRegistry.INSTANCE.newChannel("TUT", this); //Поменяйте "TUT" на свое имя канала
}
//Метод для создания нового канала в FMLPostInitializationEvent.
//Гарантирует, что пакетные дискриминаторы являются общими между сервером и клиентом с помощью логической сортировки.
public void postInitialise(){
if (isPostInitialised){
return;
}
isPostInitialised = true;
Collections.sort(packets, new Comparator<Class<? extends AbstractPacket>>(){
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;
}
/**
* Отправить пакет всем.
*
* По материалам когда cpw's из cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper.
*
* @param message Пакет, который нужно отправить.
*/
public void sendToAll(AbstractPacket message){
channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.ALL);
channels.get(Side.SERVER).writeAndFlush(message);
}
/**
* Отправить пакет определенному игроку
*
* По материалам когда cpw's из cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper
*
* @param message Пакет, который нужно отправить.
* @param player Игрок, которому нужно отправить пакет.
*/
public void sendTo(AbstractPacket message, EntityPlayerMP player){
channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.PLAYER);
channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(player);
channels.get(Side.SERVER).writeAndFlush(message);
}
/**
* Отправить пакет всем в определенном диапазоне точки
*
* По материалам когда cpw's из cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper
*
* @param message Пакет, который нужно отправить.
* @param point Точки, {cpw.mods.fml.common.network.NetworkRegistry.TargetPoint} в которых нужно отправить пакет
*/
public void sendToAllAround(AbstractPacket message, NetworkRegistry.TargetPoint point){
channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.ALLAROUNDPOINT);
channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(point);
channels.get(Side.SERVER).writeAndFlush(message);
}
/**
* Отправить пакет в определенном измерении
*
* По материалам когда cpw's из cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper
*
* @param message Пакет, который нужно отправить.
* @param dimensionId Айди измерения.
*/
public void sendToDimension(AbstractPacket message, int dimensionId){
channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.DIMENSION);
channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(dimensionId);
channels.get(Side.SERVER).writeAndFlush(message);
}
/**
* Отправить пакет на сервер.
*
* По материалам когда cpw's из cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper
*
* @param message Пакет, который нужно отправить.
*/
public void sendToServer(AbstractPacket message){
channels.get(Side.CLIENT).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.TOSERVER);
channels.get(Side.CLIENT).writeAndFlush(message);
}
}
Регистрация PacketPipeline в главном классе мода.
Код:
public static final PacketPipeline packetPipeline = new PacketPipeline();
@EventHandler
public void initialise(FMLInitializationEvent evt){
packetPipeline.initialise();
packetPipeline.registerPacket(YourPacket.class); //Ну и другие ваши пакеты аналогично.
}
@EventHandler
public void postInitialise(FMLPostInitializationEvent evt){
packetPipeline.postInitialise();
}
Пример отправки пакета.
Давайте, например, сделаем пакет открытия гуи на сервере(это нужно, когда открываешь гуи на кнопку в KeyHandler'e).
Код:
public class PacketOpenGui extends AbstractPacket{
int id;
public PacketOpenGui(){
//Как я и говорил, должен быть пустой конструктор.
}
public PacketOpenGui(int guiId){
id = guiId;
}
public void encodeInto(ChannelHandlerContext ctx, ByteBuf buffer){
buffer.writeInt(id);
}
public void decodeInto(ChannelHandlerContext ctx, ByteBuf buffer){
id = buffer.readInt();
}
public void handleClientSide(EntityPlayer player){
//На клиенте ничего не делаем.
}
public void handleServerSide(EntityPlayer player){
player.openGui(YourMod.instance, id, player.worldObj, (int)player.posX, (int)player.posY, (int)player.posZ); //id - айди нашего гуи.
}
}
Потом в KeyHandler'e просто пишем YourMod.packetPipeline.sendToServer(new PacketOpenGui(id));
Оригинальная статья: тык.