001    package cpw.mods.fml.common.network;
002    
003    import java.util.Arrays;
004    import java.util.concurrent.ConcurrentMap;
005    import java.util.logging.Level;
006    
007    import net.minecraft.network.INetworkManager;
008    import net.minecraft.network.packet.NetHandler;
009    
010    import com.google.common.base.Throwables;
011    import com.google.common.collect.MapMaker;
012    import com.google.common.primitives.Bytes;
013    import com.google.common.primitives.Ints;
014    import com.google.common.primitives.UnsignedBytes;
015    
016    import cpw.mods.fml.common.FMLLog;
017    
018    public 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));
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    }