    001package cpw.mods.fml.common.network;
    002
    003import java.util.Arrays;
    004import java.util.concurrent.ConcurrentMap;
    005import java.util.logging.Level;
    006
    007import net.minecraft.network.INetworkManager;
    008import net.minecraft.network.packet.NetHandler;
    009
    010import com.google.common.base.Throwables;
    011import com.google.common.collect.MapMaker;
    012import com.google.common.primitives.Bytes;
    013import com.google.common.primitives.Ints;
    014import com.google.common.primitives.UnsignedBytes;
    015
    016import cpw.mods.fml.common.FMLLog;
    017
    018public abstract class FMLPacket
    019{
    020    enum Type
    021    {
    022        /**
    023         * Opening salutation from the server to the client -> request all mods from the client
    024         */
    025        MOD_LIST_REQUEST(ModListRequestPacket.class, false),
    026        /**
    027         * The client responds with the list of mods and versions it has. This is verified by the server.
    028         */
    029        MOD_LIST_RESPONSE(ModListResponsePacket.class, false),
    030        /**
    031         * At which point the server tells the client the mod identifiers for this session.
    032         */
    033        MOD_IDENTIFIERS(ModIdentifiersPacket.class, false),
    034        /**
    035         * Or, if there is missing stuff, the server tells the client what's missing and drops the connection.
    036         */
    037        MOD_MISSING(ModMissingPacket.class, false),
    038        /**
    039         * Open a GUI on the client from the server
    040         */
    041        GUIOPEN(OpenGuiPacket.class, false),
    042        /**
    043         * Spawn an entity on the client from the server
    044         */
    045        ENTITYSPAWN(EntitySpawnPacket.class, false),
    046        /**
    047         * Fixes entity location data after spawning
    048         */
    049        ENTITYSPAWNADJUSTMENT(EntitySpawnAdjustmentPacket.class, false),
    050        /**
    051         * The ID map to send to the client
    052         */
    053        MOD_IDMAP(ModIdMapPacket.class, true);
    054
    055
    056        private Class<? extends FMLPacket> packetType;
    057        private boolean isMultipart;
    058        private ConcurrentMap<INetworkManager, FMLPacket> partTracker;
    059
    060        private Type(Class<? extends FMLPacket> clazz, boolean isMultipart)
    061        {
    062            this.packetType = clazz;
    063            this.isMultipart = isMultipart;
    064        }
    065
    066        FMLPacket make()
    067        {
    068            try
    069            {
    070                return this.packetType.newInstance();
    071            }
    072            catch (Exception e)
    073            {
    074                Throwables.propagateIfPossible(e);
    075                FMLLog.log(Level.SEVERE, e, "A bizarre critical error occured during packet encoding");
    076                throw new FMLNetworkException(e);
    077            }
    078        }
    079
    080        public boolean isMultipart()
    081        {
    082            return isMultipart;
    083        }
    084
    085        private FMLPacket findCurrentPart(INetworkManager network)
    086        {
    087            if (partTracker == null)
    088            {
    089                partTracker = new MapMaker().weakKeys().weakValues().makeMap();
    090            }
    091            if (!partTracker.containsKey(network))
    092            {
    093                partTracker.put(network, make());
    094            }
    095            return partTracker.get(network);
    096        }
    097    }
    098
    099    private Type type;
    100
    101    public static byte[][] makePacketSet(Type type, Object... data)
    102    {
    103        if (!type.isMultipart())
    104        {
    105            return new byte[0][];
    106        }
    107        byte[] packetData = type.make().generatePacket(data);
    108
    109        byte[][] chunks = new byte[packetData.length / 32000 + 1][];
    110        for (int i = 0; i < packetData.length / 32000 + 1; i++)
    111        {
    112            int len = Math.min(32000, packetData.length - i* 32000);
    113            chunks[i] = Bytes.concat(new byte[] { UnsignedBytes.checkedCast(type.ordinal()), UnsignedBytes.checkedCast(i), UnsignedBytes.checkedCast(chunks.length)}, Ints.toByteArray(len), Arrays.copyOfRange(packetData, i * 32000, len + i * 32000));
    114        }
    115        return chunks;
    116    }
    117    public static byte[] makePacket(Type type, Object... data)
    118    {
    119        byte[] packetData = type.make().generatePacket(data);
    120        return Bytes.concat(new byte[] { UnsignedBytes.checkedCast(type.ordinal()) }, packetData );
    121    }
    122
    123    public static FMLPacket readPacket(INetworkManager network, byte[] payload)
    124    {
    125        int type = UnsignedBytes.toInt(payload[0]);
    126        Type eType = Type.values()[type];
    127        FMLPacket pkt;
    128        if (eType.isMultipart())
    129        {
    130            pkt = eType.findCurrentPart(network);
    131        }
    132        else
    133        {
    134            pkt = eType.make();
    135        }
    136        return pkt.consumePacket(Arrays.copyOfRange(payload, 1, payload.length));
    137    }
    138
    139    public FMLPacket(Type type)
    140    {
    141        this.type = type;
    142    }
    143
    144    public abstract byte[] generatePacket(Object... data);
    145
    146    public abstract FMLPacket consumePacket(byte[] data);
    147
    148    public abstract void execute(INetworkManager network, FMLNetworkHandler handler, NetHandler netHandler, String userName);
    149}