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}