    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    }