    001package net.minecraft.network.packet;
    002
    003import java.io.DataInputStream;
    004import java.io.DataOutputStream;
    005import java.io.EOFException;
    006import java.io.IOException;
    007import java.net.Socket;
    008import java.util.HashMap;
    009import java.util.HashSet;
    010import java.util.Map;
    011import java.util.Set;
    012import net.minecraft.item.ItemStack;
    013import net.minecraft.logging.ILogAgent;
    014import net.minecraft.nbt.CompressedStreamTools;
    015import net.minecraft.nbt.NBTTagCompound;
    016import net.minecraft.util.IntHashMap;
    017
    018public abstract class Packet
    019{
    020    /** Maps packet id to packet class */
    021    public static IntHashMap packetIdToClassMap = new IntHashMap();
    022
    023    /** Maps packet class to packet id */
    024    private static Map packetClassToIdMap = new HashMap();
    025
    026    /** List of the client's packet IDs. */
    027    private static Set clientPacketIdList = new HashSet();
    028
    029    /** List of the server's packet IDs. */
    030    private static Set serverPacketIdList = new HashSet();
    031    protected ILogAgent field_98193_m;
    032
    033    /** the system time in milliseconds when this packet was created. */
    034    public final long creationTimeMillis = System.currentTimeMillis();
    035    public static long receivedID;
    036    public static long receivedSize;
    037
    038    /** Assumed to be sequential by the profiler. */
    039    public static long sentID;
    040    public static long sentSize;
    041
    042    /**
    043     * Only true for Packet51MapChunk, Packet52MultiBlockChange, Packet53BlockChange and Packet59ComplexEntity. Used to
    044     * separate them into a different send queue.
    045     */
    046    public boolean isChunkDataPacket = false;
    047
    048    /**
    049     * Adds a two way mapping between the packet ID and packet class.
    050     */
    051    public static void addIdClassMapping(int par0, boolean par1, boolean par2, Class par3Class)
    052    {
    053        if (packetIdToClassMap.containsItem(par0))
    054        {
    055            throw new IllegalArgumentException("Duplicate packet id:" + par0);
    056        }
    057        else if (packetClassToIdMap.containsKey(par3Class))
    058        {
    059            throw new IllegalArgumentException("Duplicate packet class:" + par3Class);
    060        }
    061        else
    062        {
    063            packetIdToClassMap.addKey(par0, par3Class);
    064            packetClassToIdMap.put(par3Class, Integer.valueOf(par0));
    065
    066            if (par1)
    067            {
    068                clientPacketIdList.add(Integer.valueOf(par0));
    069            }
    070
    071            if (par2)
    072            {
    073                serverPacketIdList.add(Integer.valueOf(par0));
    074            }
    075        }
    076    }
    077
    078    /**
    079     * Returns a new instance of the specified Packet class.
    080     */
    081    public static Packet getNewPacket(ILogAgent par0ILogAgent, int par1)
    082    {
    083        try
    084        {
    085            Class oclass = (Class)packetIdToClassMap.lookup(par1);
    086            return oclass == null ? null : (Packet)oclass.newInstance();
    087        }
    088        catch (Exception exception)
    089        {
    090            exception.printStackTrace();
    091            par0ILogAgent.logSevere("Skipping packet with id " + par1);
    092            return null;
    093        }
    094    }
    095
    096    /**
    097     * Writes a byte array to the DataOutputStream
    098     */
    099    public static void writeByteArray(DataOutputStream par0DataOutputStream, byte[] par1ArrayOfByte) throws IOException
    100    {
    101        par0DataOutputStream.writeShort(par1ArrayOfByte.length);
    102        par0DataOutputStream.write(par1ArrayOfByte);
    103    }
    104
    105    /**
    106     * the first short in the stream indicates the number of bytes to read
    107     */
    108    public static byte[] readBytesFromStream(DataInputStream par0DataInputStream) throws IOException
    109    {
    110        short short1 = par0DataInputStream.readShort();
    111
    112        if (short1 < 0)
    113        {
    114            throw new IOException("Key was smaller than nothing!  Weird key!");
    115        }
    116        else
    117        {
    118            byte[] abyte = new byte[short1];
    119            par0DataInputStream.readFully(abyte);
    120            return abyte;
    121        }
    122    }
    123
    124    /**
    125     * Returns the ID of this packet.
    126     */
    127    public final int getPacketId()
    128    {
    129        return ((Integer)packetClassToIdMap.get(this.getClass())).intValue();
    130    }
    131
    132    /**
    133     * Read a packet, prefixed by its ID, from the data stream.
    134     */
    135    public static Packet readPacket(ILogAgent par0ILogAgent, DataInputStream par1DataInputStream, boolean par2, Socket par3Socket) throws IOException
    136    {
    137        boolean flag1 = false;
    138        Packet packet = null;
    139        int i = par3Socket.getSoTimeout();
    140        int j;
    141
    142        try
    143        {
    144            j = par1DataInputStream.read();
    145
    146            if (j == -1)
    147            {
    148                return null;
    149            }
    150
    151            if (par2 && !serverPacketIdList.contains(Integer.valueOf(j)) || !par2 && !clientPacketIdList.contains(Integer.valueOf(j)))
    152            {
    153                throw new IOException("Bad packet id " + j);
    154            }
    155
    156            packet = getNewPacket(par0ILogAgent, j);
    157
    158            if (packet == null)
    159            {
    160                throw new IOException("Bad packet id " + j);
    161            }
    162
    163            packet.field_98193_m = par0ILogAgent;
    164
    165            if (packet instanceof Packet254ServerPing)
    166            {
    167                par3Socket.setSoTimeout(1500);
    168            }
    169
    170            packet.readPacketData(par1DataInputStream);
    171            ++receivedID;
    172            receivedSize += (long)packet.getPacketSize();
    173        }
    174        catch (EOFException eofexception)
    175        {
    176            par0ILogAgent.logSevere("Reached end of stream");
    177            return null;
    178        }
    179
    180        PacketCount.countPacket(j, (long)packet.getPacketSize());
    181        ++receivedID;
    182        receivedSize += (long)packet.getPacketSize();
    183        par3Socket.setSoTimeout(i);
    184        return packet;
    185    }
    186
    187    /**
    188     * Writes a packet, prefixed by its ID, to the data stream.
    189     */
    190    public static void writePacket(Packet par0Packet, DataOutputStream par1DataOutputStream) throws IOException
    191    {
    192        par1DataOutputStream.write(par0Packet.getPacketId());
    193        par0Packet.writePacketData(par1DataOutputStream);
    194        ++sentID;
    195        sentSize += (long)par0Packet.getPacketSize();
    196    }
    197
    198    /**
    199     * Writes a String to the DataOutputStream
    200     */
    201    public static void writeString(String par0Str, DataOutputStream par1DataOutputStream) throws IOException
    202    {
    203        if (par0Str.length() > 32767)
    204        {
    205            throw new IOException("String too big");
    206        }
    207        else
    208        {
    209            par1DataOutputStream.writeShort(par0Str.length());
    210            par1DataOutputStream.writeChars(par0Str);
    211        }
    212    }
    213
    214    /**
    215     * Reads a string from a packet
    216     */
    217    public static String readString(DataInputStream par0DataInputStream, int par1) throws IOException
    218    {
    219        short short1 = par0DataInputStream.readShort();
    220
    221        if (short1 > par1)
    222        {
    223            throw new IOException("Received string length longer than maximum allowed (" + short1 + " > " + par1 + ")");
    224        }
    225        else if (short1 < 0)
    226        {
    227            throw new IOException("Received string length is less than zero! Weird string!");
    228        }
    229        else
    230        {
    231            StringBuilder stringbuilder = new StringBuilder();
    232
    233            for (int j = 0; j < short1; ++j)
    234            {
    235                stringbuilder.append(par0DataInputStream.readChar());
    236            }
    237
    238            return stringbuilder.toString();
    239        }
    240    }
    241
    242    /**
    243     * Abstract. Reads the raw packet data from the data stream.
    244     */
    245    public abstract void readPacketData(DataInputStream datainputstream) throws IOException;
    246
    247    /**
    248     * Abstract. Writes the raw packet data to the data stream.
    249     */
    250    public abstract void writePacketData(DataOutputStream dataoutputstream) throws IOException;
    251
    252    /**
    253     * Passes this Packet on to the NetHandler for processing.
    254     */
    255    public abstract void processPacket(NetHandler nethandler);
    256
    257    /**
    258     * Abstract. Return the size of the packet (not counting the header).
    259     */
    260    public abstract int getPacketSize();
    261
    262    /**
    263     * only false for the abstract Packet class, all real packets return true
    264     */
    265    public boolean isRealPacket()
    266    {
    267        return false;
    268    }
    269
    270    /**
    271     * eg return packet30entity.entityId == entityId; WARNING : will throw if you compare a packet to a different packet
    272     * class
    273     */
    274    public boolean containsSameEntityIDAs(Packet par1Packet)
    275    {
    276        return false;
    277    }
    278
    279    /**
    280     * If this returns true, the packet may be processed on any thread; otherwise it is queued for the main thread to
    281     * handle.
    282     */
    283    public boolean canProcessAsync()
    284    {
    285        return false;
    286    }
    287
    288    public String toString()
    289    {
    290        String s = this.getClass().getSimpleName();
    291        return s;
    292    }
    293
    294    /**
    295     * Reads a ItemStack from the InputStream
    296     */
    297    public static ItemStack readItemStack(DataInputStream par0DataInputStream) throws IOException
    298    {
    299        ItemStack itemstack = null;
    300        short short1 = par0DataInputStream.readShort();
    301
    302        if (short1 >= 0)
    303        {
    304            byte b0 = par0DataInputStream.readByte();
    305            short short2 = par0DataInputStream.readShort();
    306            itemstack = new ItemStack(short1, b0, short2);
    307            itemstack.stackTagCompound = readNBTTagCompound(par0DataInputStream);
    308        }
    309
    310        return itemstack;
    311    }
    312
    313    /**
    314     * Writes the ItemStack's ID (short), then size (byte), then damage. (short)
    315     */
    316    public static void writeItemStack(ItemStack par0ItemStack, DataOutputStream par1DataOutputStream) throws IOException
    317    {
    318        if (par0ItemStack == null)
    319        {
    320            par1DataOutputStream.writeShort(-1);
    321        }
    322        else
    323        {
    324            par1DataOutputStream.writeShort(par0ItemStack.itemID);
    325            par1DataOutputStream.writeByte(par0ItemStack.stackSize);
    326            par1DataOutputStream.writeShort(par0ItemStack.getItemDamage());
    327            NBTTagCompound nbttagcompound = null;
    328
    329            if (par0ItemStack.getItem().isDamageable() || par0ItemStack.getItem().getShareTag())
    330            {
    331                nbttagcompound = par0ItemStack.stackTagCompound;
    332            }
    333
    334            writeNBTTagCompound(nbttagcompound, par1DataOutputStream);
    335        }
    336    }
    337
    338    /**
    339     * Reads a compressed NBTTagCompound from the InputStream
    340     */
    341    public static NBTTagCompound readNBTTagCompound(DataInputStream par0DataInputStream) throws IOException
    342    {
    343        short short1 = par0DataInputStream.readShort();
    344
    345        if (short1 < 0)
    346        {
    347            return null;
    348        }
    349        else
    350        {
    351            byte[] abyte = new byte[short1];
    352            par0DataInputStream.readFully(abyte);
    353            return CompressedStreamTools.decompress(abyte);
    354        }
    355    }
    356
    357    /**
    358     * Writes a compressed NBTTagCompound to the OutputStream
    359     */
    360    protected static void writeNBTTagCompound(NBTTagCompound par0NBTTagCompound, DataOutputStream par1DataOutputStream) throws IOException
    361    {
    362        if (par0NBTTagCompound == null)
    363        {
    364            par1DataOutputStream.writeShort(-1);
    365        }
    366        else
    367        {
    368            byte[] abyte = CompressedStreamTools.compress(par0NBTTagCompound);
    369            par1DataOutputStream.writeShort((short)abyte.length);
    370            par1DataOutputStream.write(abyte);
    371        }
    372    }
    373
    374    static
    375    {
    376        addIdClassMapping(0, true, true, Packet0KeepAlive.class);
    377        addIdClassMapping(1, true, true, Packet1Login.class);
    378        addIdClassMapping(2, false, true, Packet2ClientProtocol.class);
    379        addIdClassMapping(3, true, true, Packet3Chat.class);
    380        addIdClassMapping(4, true, false, Packet4UpdateTime.class);
    381        addIdClassMapping(5, true, false, Packet5PlayerInventory.class);
    382        addIdClassMapping(6, true, false, Packet6SpawnPosition.class);
    383        addIdClassMapping(7, false, true, Packet7UseEntity.class);
    384        addIdClassMapping(8, true, false, Packet8UpdateHealth.class);
    385        addIdClassMapping(9, true, true, Packet9Respawn.class);
    386        addIdClassMapping(10, true, true, Packet10Flying.class);
    387        addIdClassMapping(11, true, true, Packet11PlayerPosition.class);
    388        addIdClassMapping(12, true, true, Packet12PlayerLook.class);
    389        addIdClassMapping(13, true, true, Packet13PlayerLookMove.class);
    390        addIdClassMapping(14, false, true, Packet14BlockDig.class);
    391        addIdClassMapping(15, false, true, Packet15Place.class);
    392        addIdClassMapping(16, true, true, Packet16BlockItemSwitch.class);
    393        addIdClassMapping(17, true, false, Packet17Sleep.class);
    394        addIdClassMapping(18, true, true, Packet18Animation.class);
    395        addIdClassMapping(19, false, true, Packet19EntityAction.class);
    396        addIdClassMapping(20, true, false, Packet20NamedEntitySpawn.class);
    397        addIdClassMapping(22, true, false, Packet22Collect.class);
    398        addIdClassMapping(23, true, false, Packet23VehicleSpawn.class);
    399        addIdClassMapping(24, true, false, Packet24MobSpawn.class);
    400        addIdClassMapping(25, true, false, Packet25EntityPainting.class);
    401        addIdClassMapping(26, true, false, Packet26EntityExpOrb.class);
    402        addIdClassMapping(28, true, false, Packet28EntityVelocity.class);
    403        addIdClassMapping(29, true, false, Packet29DestroyEntity.class);
    404        addIdClassMapping(30, true, false, Packet30Entity.class);
    405        addIdClassMapping(31, true, false, Packet31RelEntityMove.class);
    406        addIdClassMapping(32, true, false, Packet32EntityLook.class);
    407        addIdClassMapping(33, true, false, Packet33RelEntityMoveLook.class);
    408        addIdClassMapping(34, true, false, Packet34EntityTeleport.class);
    409        addIdClassMapping(35, true, false, Packet35EntityHeadRotation.class);
    410        addIdClassMapping(38, true, false, Packet38EntityStatus.class);
    411        addIdClassMapping(39, true, false, Packet39AttachEntity.class);
    412        addIdClassMapping(40, true, false, Packet40EntityMetadata.class);
    413        addIdClassMapping(41, true, false, Packet41EntityEffect.class);
    414        addIdClassMapping(42, true, false, Packet42RemoveEntityEffect.class);
    415        addIdClassMapping(43, true, false, Packet43Experience.class);
    416        addIdClassMapping(51, true, false, Packet51MapChunk.class);
    417        addIdClassMapping(52, true, false, Packet52MultiBlockChange.class);
    418        addIdClassMapping(53, true, false, Packet53BlockChange.class);
    419        addIdClassMapping(54, true, false, Packet54PlayNoteBlock.class);
    420        addIdClassMapping(55, true, false, Packet55BlockDestroy.class);
    421        addIdClassMapping(56, true, false, Packet56MapChunks.class);
    422        addIdClassMapping(60, true, false, Packet60Explosion.class);
    423        addIdClassMapping(61, true, false, Packet61DoorChange.class);
    424        addIdClassMapping(62, true, false, Packet62LevelSound.class);
    425        addIdClassMapping(63, true, false, Packet63WorldParticles.class);
    426        addIdClassMapping(70, true, false, Packet70GameEvent.class);
    427        addIdClassMapping(71, true, false, Packet71Weather.class);
    428        addIdClassMapping(100, true, false, Packet100OpenWindow.class);
    429        addIdClassMapping(101, true, true, Packet101CloseWindow.class);
    430        addIdClassMapping(102, false, true, Packet102WindowClick.class);
    431        addIdClassMapping(103, true, false, Packet103SetSlot.class);
    432        addIdClassMapping(104, true, false, Packet104WindowItems.class);
    433        addIdClassMapping(105, true, false, Packet105UpdateProgressbar.class);
    434        addIdClassMapping(106, true, true, Packet106Transaction.class);
    435        addIdClassMapping(107, true, true, Packet107CreativeSetSlot.class);
    436        addIdClassMapping(108, false, true, Packet108EnchantItem.class);
    437        addIdClassMapping(130, true, true, Packet130UpdateSign.class);
    438        addIdClassMapping(131, true, true, Packet131MapData.class);
    439        addIdClassMapping(132, true, false, Packet132TileEntityData.class);
    440        addIdClassMapping(200, true, false, Packet200Statistic.class);
    441        addIdClassMapping(201, true, false, Packet201PlayerInfo.class);
    442        addIdClassMapping(202, true, true, Packet202PlayerAbilities.class);
    443        addIdClassMapping(203, true, true, Packet203AutoComplete.class);
    444        addIdClassMapping(204, false, true, Packet204ClientInfo.class);
    445        addIdClassMapping(205, false, true, Packet205ClientCommand.class);
    446        addIdClassMapping(206, true, false, Packet206SetObjective.class);
    447        addIdClassMapping(207, true, false, Packet207SetScore.class);
    448        addIdClassMapping(208, true, false, Packet208SetDisplayObjective.class);
    449        addIdClassMapping(209, true, false, Packet209SetPlayerTeam.class);
    450        addIdClassMapping(250, true, true, Packet250CustomPayload.class);
    451        addIdClassMapping(252, true, true, Packet252SharedKey.class);
    452        addIdClassMapping(253, true, false, Packet253ServerAuthData.class);
    453        addIdClassMapping(254, false, true, Packet254ServerPing.class);
    454        addIdClassMapping(255, true, true, Packet255KickDisconnect.class);
    455    }
    456}