package net.minecraftforge.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 java.util.List;
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 net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.network.FMLNetworkException;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher;
import net.minecraftforge.fml.relauncher.Side;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.helpers.Integers;
import com.google.common.collect.ConcurrentHashMultiset;
import com.google.common.collect.Lists;
import com.google.common.collect.Multiset;
import com.google.common.collect.Multiset.Entry;
import com.google.common.collect.Multisets;
public class FMLProxyPacket implements Packet {
final String channel;
private Side target;
private final PacketBuffer payload;
private INetHandler netHandler;
private NetworkDispatcher dispatcher;
private static Multiset<String> badPackets = ConcurrentHashMultiset.create();
private static int packetCountWarning = Integers.parseInt(System.getProperty("fml.badPacketCounter", "100"), 100);
public FMLProxyPacket(S3FPacketCustomPayload original)
{
this(original.getBufferData(), original.getChannelName());
this.target = Side.CLIENT;
}
public FMLProxyPacket(C17PacketCustomPayload original)
{
this(original.getBufferData(), original.getChannelName());
this.target = Side.SERVER;
}
public FMLProxyPacket(PacketBuffer 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))
{
badPackets.add(this.channel);
if (badPackets.size() % packetCountWarning == 0)
{
FMLLog.severe("Detected ongoing potential memory leak. %d packets have leaked. Top offenders", badPackets.size());
int i = 0;
for (Entry<String> s : Multisets.copyHighestCountFirst(badPackets).entrySet())
{
if (i++ > 10) break;
FMLLog.severe("\t %s : %d", s.getElement(), s.getCount());
}
}
}
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);
}
static final int PART_SIZE = 0x1000000 - 0x50; // Make it a constant so that it gets inlined below.
public static final int MAX_LENGTH = PART_SIZE * 255;
public List<Packet> toS3FPackets() throws IOException
{
List<Packet> ret = Lists.newArrayList();
byte[] data = payload.array();
if (data.length < PART_SIZE)
{
ret.add(new S3FPacketCustomPayload(channel, payload));
}
else
{
int parts = (int)Math.ceil(data.length / (double)(PART_SIZE - 1)); //We add a byte header so -1
if (parts > 255)
{
throw new IllegalArgumentException("Payload may not be larger than " + MAX_LENGTH + " bytes");
}
PacketBuffer preamble = new PacketBuffer(Unpooled.buffer());
preamble.writeString(channel);
preamble.writeByte(parts);
preamble.writeInt(data.length);
ret.add(new S3FPacketCustomPayload("FML|MP", preamble));
int offset = 0;
for (int x = 0; x < parts; x++)
{
int length = Math.min(PART_SIZE, data.length - offset + 1);
byte[] tmp = new byte[length];
tmp[0] = (byte)(x & 0xFF);
System.arraycopy(data, offset, tmp, 1, tmp.length - 1);
offset += tmp.length - 1;
ret.add(new S3FPacketCustomPayload("FML|MP", new PacketBuffer(Unpooled.wrappedBuffer(tmp))));
}
}
return ret;
}
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;
}
}