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