package cpw.mods.fml.common.network.internal;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import java.io.IOException;
import net.minecraft.network.INetHandler;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.Packet;
import net.minecraft.network.PacketBuffer;
import net.minecraft.network.play.client.C17PacketCustomPayload;
import net.minecraft.network.play.server.S3FPacketCustomPayload;
import org.apache.logging.log4j.Level;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.network.ByteBufUtils;
import cpw.mods.fml.common.network.FMLNetworkException;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.network.handshake.NetworkDispatcher;
import cpw.mods.fml.relauncher.Side;

public class FMLProxyPacket extends Packet {
    final String channel;
    private Side target;
    private final ByteBuf payload;
    private INetHandler netHandler;
    private NetworkDispatcher dispatcher;

    private FMLProxyPacket(byte[] payload, String channel)
    {
        this(Unpooled.wrappedBuffer(payload), channel);
    }

    public FMLProxyPacket(S3FPacketCustomPayload original)
    {
        this(original.func_149168_d(), original.func_149169_c());
        this.target = Side.CLIENT;
    }

    public FMLProxyPacket(C17PacketCustomPayload original)
    {
        this(original.func_149558_e(), original.func_149559_c());
        this.target = Side.SERVER;
    }

    public FMLProxyPacket(ByteBuf payload, String channel)
    {
        this.channel = channel;
        this.payload = payload;
    }
    @Override
    public void readPacketData(PacketBuffer packetbuffer) throws IOException
    {
        // NOOP - we are not built this way
    }

    @Override
    public void writePacketData(PacketBuffer packetbuffer) throws IOException
    {
        // NOOP - we are not built this way
    }

    @Override
    public void processPacket(INetHandler inethandler)
    {
        this.netHandler = inethandler;
        EmbeddedChannel internalChannel = NetworkRegistry.INSTANCE.getChannel(this.channel, this.target);
        if (internalChannel != null)
        {
            internalChannel.attr(NetworkRegistry.NET_HANDLER).set(this.netHandler);
            try
            {
                if (internalChannel.writeInbound(this))
                {
                    FMLLog.severe("Messages for channel %s for side %s were not processed by the embedded channel %s.\n They have been dropped.\n%s", this.channel, this.target, internalChannel.inboundMessages(), ByteBufUtils.getContentDump(this.payload));
                }
                internalChannel.inboundMessages().clear();
            }
            catch (FMLNetworkException ne)
            {
                FMLLog.log(Level.ERROR, ne, "There was a network exception handling a packet on channel %s", channel);
                dispatcher.rejectHandshake(ne.getMessage());
            }
            catch (Throwable t)
            {
                FMLLog.log(Level.ERROR, t, "There was a critical exception handling a packet on channel %s", channel);
                dispatcher.rejectHandshake("A fatal error has occured, this connection is terminated");
            }
        }
    }

    public String channel()
    {
        return channel;
    }
    public ByteBuf payload()
    {
        return payload;
    }
    public INetHandler handler()
    {
        return netHandler;
    }
    public Packet toC17Packet()
    {
        return new C17PacketCustomPayload(channel, payload.array());
    }

    public Packet toS3FPacket()
    {
        return new S3FPacketCustomPayload(channel, payload.array());
    }

    public void setTarget(Side target)
    {
        this.target = target;
    }

    public void setDispatcher(NetworkDispatcher networkDispatcher)
    {
        this.dispatcher = networkDispatcher;
    }

    public NetworkManager getOrigin()
    {
        return this.dispatcher != null ? this.dispatcher.manager : null;
    }

    public NetworkDispatcher getDispatcher()
    {
        return this.dispatcher;
    }

    public Side getTarget()
    {
        return target;
    }
}

@@ -530,83 +530,282 @@
 ort 
-cpw.mods.fml.common.FMLLog;%0Aimport cpw.mods.fml.common.network.ByteBufUtils
+org.apache.logging.log4j.core.helpers.Integers;%0Aimport com.google.common.collect.ConcurrentHashMultiset;%0Aimport com.google.common.collect.Multiset;%0Aimport com.google.common.collect.Multiset.Entry;%0Aimport com.google.common.collect.Multisets;%0Aimport cpw.mods.fml.common.FMLLog
 ;%0Aim
@@ -1221,17 +1221,217 @@
 atcher;%0A
+    private static Multiset%3CString%3E badPackets = ConcurrentHashMultiset.create();%0A    private static int packetCountWarning = Integers.parseInt(System.getProperty(%22fml.badPacketCounter%22, %22100%22), 100);
 %0A
-
     priv
@@ -2807,243 +2807,616 @@
     
-FMLLog.severe(%22Messages for channel %25s for side %25s were not processed by the embedded channel %25s.%5Cn They have been dropped.%5Cn%25s%22, this.channel, this.target, internalChannel.inboundMessages(), ByteBufUtils.getContentDump(this.payload));
+badPackets.add(this.channel);%0A                    if (badPackets.size() %25 packetCountWarning == 0)%0A                    %7B%0A                        FMLLog.severe(%22Detected ongoing potential memory leak. %25d packets have leaked. Top offenders%22, badPackets.size());%0A                        int i = 0;%0A                        for (Entry%3CString%3E s  : Multisets.copyHighestCountFirst(badPackets).entrySet())%0A                        %7B%0A                            if (i++ %3E 10) break;%0A                            FMLLog.severe(%22%5Ct %25s : %25d%22, s.getElement(), s.getCount());%0A                        %7D%0A                    %7D
 %0A   

Tweak bad packet warning some. Makes it a bit more log friendly.