package ru.nord.common.network; 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 net.minecraft.network.PacketBuffer; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.network.FMLEmbeddedChannel; import net.minecraftforge.fml.common.network.FMLOutboundHandler; import net.minecraftforge.fml.common.network.NetworkRegistry; import net.minecraftforge.fml.common.network.internal.FMLProxyPacket; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import ru.nord.common.utils.Version; import java.util.*; /** * Класс PacketPipeline. Направляет все зарегистрированные пакетные данные на * обработку. * * @author sirgingalot Некоторый код: cpw */ @ChannelHandler.Sharable public class PacketPipeline extends MessageToMessageCodec<FMLProxyPacket, PacketAbstract> { private EnumMap<Side, FMLEmbeddedChannel> channels; private final LinkedList<Class<? extends PacketAbstract>> packets = new LinkedList<Class<? extends PacketAbstract>>(); private boolean isPostInitialised = false; /** * Зарегистрировать свой пакет. Дискриминаторы устанавливаются * автоматически. * * @param clazz класс пакета, который необходимо зарегистрировать. * @return Вернуть тру, если регистрация была успешна. Отказ может * произойти, если 256 пакетов было зарегистрировано или если в * реестре уже содержится этот пакет. */ public boolean registerPacket(Class<? extends PacketAbstract> 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, PacketAbstract msg, List<Object> out) throws Exception { ByteBuf buffer = Unpooled.buffer(); Class<? extends PacketAbstract> 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((PacketBuffer) 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 PacketAbstract> clazz = this.packets.get(discriminator); if (clazz == null) { throw new NullPointerException( "No packet registered for discriminator: " + discriminator); } PacketAbstract 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(Version.MODID, this); } // Метод для создания нового канала в FMLPostInitializationEvent. // Гарантирует, что пакетные дискриминаторы являются общими между сервером и // клиентом с помощью логической сортировки. public void postInitialise() { if (isPostInitialised) { return; } isPostInitialised = true; Collections.sort(packets, new Comparator<Class<? extends PacketAbstract>>() { public int compare(Class<? extends PacketAbstract> clazz1, Class<? extends PacketAbstract> 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; } /** * Отправить пакет всем. * <p/> * По материалам когда cpw's из * cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper. * * @param message Пакет, который нужно отправить. */ public void sendToAll(PacketAbstract message) { channels.get(Side.SERVER).attr(FMLOutboundHandler.FML_MESSAGETARGET) .set(FMLOutboundHandler.OutboundTarget.ALL); channels.get(Side.SERVER).writeAndFlush(message); } /** * Отправить пакет определенному игроку * <p/> * По материалам когда cpw's из * cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper * * @param message Пакет, который нужно отправить. * @param player Игрок, которому нужно отправить пакет. */ public void sendTo(PacketAbstract 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); } /** * Отправить пакет всем в определенном диапазоне точки * <p/> * По материалам когда cpw's из * cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper * * @param message Пакет, который нужно отправить. * @param point Точки, * {cpw.mods.fml.common.network.NetworkRegistry.TargetPoint} в * которых нужно отправить пакет */ public void sendToAllAround(PacketAbstract 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); } /** * Отправить пакет в определенном измерении * <p/> * По материалам когда cpw's из * cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper * * @param message Пакет, который нужно отправить. * @param dimensionId Айди измерения. */ public void sendToDimension(PacketAbstract 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); } /** * Отправить пакет на сервер. * <p/> * По материалам когда cpw's из * cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper * * @param message Пакет, который нужно отправить. */ public void sendToServer(PacketAbstract message) { channels.get(Side.CLIENT).attr(FMLOutboundHandler.FML_MESSAGETARGET) .set(FMLOutboundHandler.OutboundTarget.TOSERVER); channels.get(Side.CLIENT).writeAndFlush(message); } }