package net.minecraftplus._api.minecraft; 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 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 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; /** * 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 PacketChannel extends MessageToMessageCodec<FMLProxyPacket, Packet> { private String modChannel; private EnumMap<Side, FMLEmbeddedChannel> channels; private LinkedList<Class<? extends Packet>> packets = new LinkedList<Class<? extends Packet>>(); private boolean hasPostInit = false; public PacketChannel(String parChannel) { this.modChannel = parChannel; } /** * Register your packet with the pipeline. Discriminators are automatically set. * * @param parClass 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 Packet> parClass) { if (this.packets.size() > 256) { // You should log here!! return false; } if (this.packets.contains(parClass)) { // You should log here!! return false; } if (this.hasPostInit) { // You should log here!! return false; } this.packets.add(parClass); return true; } // In line encoding of the packet, including discriminator setting @Override protected void encode(ChannelHandlerContext ctx, Packet msg, List<Object> out) throws Exception { ByteBuf buffer = Unpooled.buffer(); Class<? extends Packet> msgClass = msg.getClass(); if (!this.packets.contains(msg.getClass())) { throw new NullPointerException("No Packet Registered for: " + msg.getClass().getCanonicalName()); } byte discriminator = (byte) this.packets.indexOf(msgClass); buffer.writeByte(discriminator); msg.encode(ctx, buffer); FMLProxyPacket proxyPacket = new FMLProxyPacket((PacketBuffer) 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 Packet> packetClass = this.packets.get(discriminator); if (packetClass == null) { throw new NullPointerException("No packet registered for discriminator: " + discriminator); } Packet pkt = packetClass.newInstance(); pkt.decode(ctx, payload.slice()); EntityPlayer player; switch (FMLCommonHandler.instance().getEffectiveSide()) { case CLIENT: player = this.getClientPlayer(); pkt.onClientSide(player); break; case SERVER: INetHandler netHandler = ctx.channel().attr(NetworkRegistry.NET_HANDLER).get(); player = ((NetHandlerPlayServer) netHandler).playerEntity; pkt.onServerSide(player); break; default: } out.add(pkt); } // Method to call from FMLInitializationEvent public void initialize() { this.channels = NetworkRegistry.INSTANCE.newChannel(this.modChannel, this); } // Method to call from FMLPostInitializationEvent // Ensures that packet discriminators are common between server and client by using logical sorting public void postInitialize() { if (this.hasPostInit) { return; } this.hasPostInit = true; Collections.sort(this.packets, new Comparator<Class<? extends Packet>>() { @Override public int compare(Class<? extends Packet> parClass1, Class<? extends Packet> parClass2) { int com = String.CASE_INSENSITIVE_ORDER.compare(parClass1.getCanonicalName(), parClass2.getCanonicalName()); if (com == 0) { com = parClass1.getCanonicalName().compareTo(parClass2.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(Packet 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(Packet 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(Packet 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(Packet 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(Packet message) { this.channels.get(Side.CLIENT).attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.TOSERVER); this.channels.get(Side.CLIENT).writeAndFlush(message); } public boolean isInitialized() { return this.hasPostInit; } }