    001package net.minecraft.entity;
    002
    003import cpw.mods.fml.relauncher.Side;
    004import cpw.mods.fml.relauncher.SideOnly;
    005import java.io.DataInputStream;
    006import java.io.DataOutputStream;
    007import java.io.IOException;
    008import java.util.ArrayList;
    009import java.util.HashMap;
    010import java.util.Iterator;
    011import java.util.List;
    012import java.util.Map;
    013import java.util.concurrent.locks.ReadWriteLock;
    014import java.util.concurrent.locks.ReentrantReadWriteLock;
    015import net.minecraft.crash.CrashReport;
    016import net.minecraft.crash.CrashReportCategory;
    017import net.minecraft.item.ItemStack;
    018import net.minecraft.network.packet.Packet;
    019import net.minecraft.util.ChunkCoordinates;
    020import net.minecraft.util.ReportedException;
    021
    022public class DataWatcher
    023{
    024    /** When isBlank is true the DataWatcher is not watching any objects */
    025    private boolean isBlank = true;
    026    private static final HashMap dataTypes = new HashMap();
    027    private final Map watchedObjects = new HashMap();
    028
    029    /** true if one or more object was changed */
    030    private boolean objectChanged;
    031    private ReadWriteLock lock = new ReentrantReadWriteLock();
    032
    033    /**
    034     * adds a new object to dataWatcher to watch, to update an already existing object see updateObject. Arguments: data
    035     * Value Id, Object to add
    036     */
    037    public void addObject(int par1, Object par2Obj)
    038    {
    039        Integer integer = (Integer)dataTypes.get(par2Obj.getClass());
    040
    041        if (integer == null)
    042        {
    043            throw new IllegalArgumentException("Unknown data type: " + par2Obj.getClass());
    044        }
    045        else if (par1 > 31)
    046        {
    047            throw new IllegalArgumentException("Data value id is too big with " + par1 + "! (Max is " + 31 + ")");
    048        }
    049        else if (this.watchedObjects.containsKey(Integer.valueOf(par1)))
    050        {
    051            throw new IllegalArgumentException("Duplicate id value for " + par1 + "!");
    052        }
    053        else
    054        {
    055            WatchableObject watchableobject = new WatchableObject(integer.intValue(), par1, par2Obj);
    056            this.lock.writeLock().lock();
    057            this.watchedObjects.put(Integer.valueOf(par1), watchableobject);
    058            this.lock.writeLock().unlock();
    059            this.isBlank = false;
    060        }
    061    }
    062
    063    /**
    064     * Add a new object for the DataWatcher to watch, using the specified data type.
    065     */
    066    public void addObjectByDataType(int par1, int par2)
    067    {
    068        WatchableObject watchableobject = new WatchableObject(par2, par1, (Object)null);
    069        this.lock.writeLock().lock();
    070        this.watchedObjects.put(Integer.valueOf(par1), watchableobject);
    071        this.lock.writeLock().unlock();
    072        this.isBlank = false;
    073    }
    074
    075    /**
    076     * gets the bytevalue of a watchable object
    077     */
    078    public byte getWatchableObjectByte(int par1)
    079    {
    080        return ((Byte)this.getWatchedObject(par1).getObject()).byteValue();
    081    }
    082
    083    public short getWatchableObjectShort(int par1)
    084    {
    085        return ((Short)this.getWatchedObject(par1).getObject()).shortValue();
    086    }
    087
    088    /**
    089     * gets a watchable object and returns it as a Integer
    090     */
    091    public int getWatchableObjectInt(int par1)
    092    {
    093        return ((Integer)this.getWatchedObject(par1).getObject()).intValue();
    094    }
    095
    096    /**
    097     * gets a watchable object and returns it as a String
    098     */
    099    public String getWatchableObjectString(int par1)
    100    {
    101        return (String)this.getWatchedObject(par1).getObject();
    102    }
    103
    104    /**
    105     * Get a watchable object as an ItemStack.
    106     */
    107    public ItemStack getWatchableObjectItemStack(int par1)
    108    {
    109        return (ItemStack)this.getWatchedObject(par1).getObject();
    110    }
    111
    112    /**
    113     * is threadsafe, unless it throws an exception, then
    114     */
    115    private WatchableObject getWatchedObject(int par1)
    116    {
    117        this.lock.readLock().lock();
    118        WatchableObject watchableobject;
    119
    120        try
    121        {
    122            watchableobject = (WatchableObject)this.watchedObjects.get(Integer.valueOf(par1));
    123        }
    124        catch (Throwable throwable)
    125        {
    126            CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Getting synched entity data");
    127            CrashReportCategory crashreportcategory = crashreport.makeCategory("Synched entity data");
    128            crashreportcategory.addCrashSection("Data ID", Integer.valueOf(par1));
    129            throw new ReportedException(crashreport);
    130        }
    131
    132        this.lock.readLock().unlock();
    133        return watchableobject;
    134    }
    135
    136    /**
    137     * updates an already existing object
    138     */
    139    public void updateObject(int par1, Object par2Obj)
    140    {
    141        WatchableObject watchableobject = this.getWatchedObject(par1);
    142
    143        if (!par2Obj.equals(watchableobject.getObject()))
    144        {
    145            watchableobject.setObject(par2Obj);
    146            watchableobject.setWatched(true);
    147            this.objectChanged = true;
    148        }
    149    }
    150
    151    public void setObjectWatched(int par1)
    152    {
    153        WatchableObject.setWatchableObjectWatched(this.getWatchedObject(par1), true);
    154        this.objectChanged = true;
    155    }
    156
    157    public boolean hasChanges()
    158    {
    159        return this.objectChanged;
    160    }
    161
    162    /**
    163     * writes every object in passed list to dataoutputstream, terminated by 0x7F
    164     */
    165    public static void writeObjectsInListToStream(List par0List, DataOutputStream par1DataOutputStream) throws IOException
    166    {
    167        if (par0List != null)
    168        {
    169            Iterator iterator = par0List.iterator();
    170
    171            while (iterator.hasNext())
    172            {
    173                WatchableObject watchableobject = (WatchableObject)iterator.next();
    174                writeWatchableObject(par1DataOutputStream, watchableobject);
    175            }
    176        }
    177
    178        par1DataOutputStream.writeByte(127);
    179    }
    180
    181    public List unwatchAndReturnAllWatched()
    182    {
    183        ArrayList arraylist = null;
    184
    185        if (this.objectChanged)
    186        {
    187            this.lock.readLock().lock();
    188            Iterator iterator = this.watchedObjects.values().iterator();
    189
    190            while (iterator.hasNext())
    191            {
    192                WatchableObject watchableobject = (WatchableObject)iterator.next();
    193
    194                if (watchableobject.isWatched())
    195                {
    196                    watchableobject.setWatched(false);
    197
    198                    if (arraylist == null)
    199                    {
    200                        arraylist = new ArrayList();
    201                    }
    202
    203                    arraylist.add(watchableobject);
    204                }
    205            }
    206
    207            this.lock.readLock().unlock();
    208        }
    209
    210        this.objectChanged = false;
    211        return arraylist;
    212    }
    213
    214    public void writeWatchableObjects(DataOutputStream par1DataOutputStream) throws IOException
    215    {
    216        this.lock.readLock().lock();
    217        Iterator iterator = this.watchedObjects.values().iterator();
    218
    219        while (iterator.hasNext())
    220        {
    221            WatchableObject watchableobject = (WatchableObject)iterator.next();
    222            writeWatchableObject(par1DataOutputStream, watchableobject);
    223        }
    224
    225        this.lock.readLock().unlock();
    226        par1DataOutputStream.writeByte(127);
    227    }
    228
    229    public List getAllWatched()
    230    {
    231        ArrayList arraylist = null;
    232        this.lock.readLock().lock();
    233        WatchableObject watchableobject;
    234
    235        for (Iterator iterator = this.watchedObjects.values().iterator(); iterator.hasNext(); arraylist.add(watchableobject))
    236        {
    237            watchableobject = (WatchableObject)iterator.next();
    238
    239            if (arraylist == null)
    240            {
    241                arraylist = new ArrayList();
    242            }
    243        }
    244
    245        this.lock.readLock().unlock();
    246        return arraylist;
    247    }
    248
    249    private static void writeWatchableObject(DataOutputStream par0DataOutputStream, WatchableObject par1WatchableObject) throws IOException
    250    {
    251        int i = (par1WatchableObject.getObjectType() << 5 | par1WatchableObject.getDataValueId() & 31) & 255;
    252        par0DataOutputStream.writeByte(i);
    253
    254        switch (par1WatchableObject.getObjectType())
    255        {
    256            case 0:
    257                par0DataOutputStream.writeByte(((Byte)par1WatchableObject.getObject()).byteValue());
    258                break;
    259            case 1:
    260                par0DataOutputStream.writeShort(((Short)par1WatchableObject.getObject()).shortValue());
    261                break;
    262            case 2:
    263                par0DataOutputStream.writeInt(((Integer)par1WatchableObject.getObject()).intValue());
    264                break;
    265            case 3:
    266                par0DataOutputStream.writeFloat(((Float)par1WatchableObject.getObject()).floatValue());
    267                break;
    268            case 4:
    269                Packet.writeString((String)par1WatchableObject.getObject(), par0DataOutputStream);
    270                break;
    271            case 5:
    272                ItemStack itemstack = (ItemStack)par1WatchableObject.getObject();
    273                Packet.writeItemStack(itemstack, par0DataOutputStream);
    274                break;
    275            case 6:
    276                ChunkCoordinates chunkcoordinates = (ChunkCoordinates)par1WatchableObject.getObject();
    277                par0DataOutputStream.writeInt(chunkcoordinates.posX);
    278                par0DataOutputStream.writeInt(chunkcoordinates.posY);
    279                par0DataOutputStream.writeInt(chunkcoordinates.posZ);
    280        }
    281    }
    282
    283    public static List readWatchableObjects(DataInputStream par0DataInputStream) throws IOException
    284    {
    285        ArrayList arraylist = null;
    286
    287        for (byte b0 = par0DataInputStream.readByte(); b0 != 127; b0 = par0DataInputStream.readByte())
    288        {
    289            if (arraylist == null)
    290            {
    291                arraylist = new ArrayList();
    292            }
    293
    294            int i = (b0 & 224) >> 5;
    295            int j = b0 & 31;
    296            WatchableObject watchableobject = null;
    297
    298            switch (i)
    299            {
    300                case 0:
    301                    watchableobject = new WatchableObject(i, j, Byte.valueOf(par0DataInputStream.readByte()));
    302                    break;
    303                case 1:
    304                    watchableobject = new WatchableObject(i, j, Short.valueOf(par0DataInputStream.readShort()));
    305                    break;
    306                case 2:
    307                    watchableobject = new WatchableObject(i, j, Integer.valueOf(par0DataInputStream.readInt()));
    308                    break;
    309                case 3:
    310                    watchableobject = new WatchableObject(i, j, Float.valueOf(par0DataInputStream.readFloat()));
    311                    break;
    312                case 4:
    313                    watchableobject = new WatchableObject(i, j, Packet.readString(par0DataInputStream, 64));
    314                    break;
    315                case 5:
    316                    watchableobject = new WatchableObject(i, j, Packet.readItemStack(par0DataInputStream));
    317                    break;
    318                case 6:
    319                    int k = par0DataInputStream.readInt();
    320                    int l = par0DataInputStream.readInt();
    321                    int i1 = par0DataInputStream.readInt();
    322                    watchableobject = new WatchableObject(i, j, new ChunkCoordinates(k, l, i1));
    323            }
    324
    325            arraylist.add(watchableobject);
    326        }
    327
    328        return arraylist;
    329    }
    330
    331    @SideOnly(Side.CLIENT)
    332    public void updateWatchedObjectsFromList(List par1List)
    333    {
    334        this.lock.writeLock().lock();
    335        Iterator iterator = par1List.iterator();
    336
    337        while (iterator.hasNext())
    338        {
    339            WatchableObject watchableobject = (WatchableObject)iterator.next();
    340            WatchableObject watchableobject1 = (WatchableObject)this.watchedObjects.get(Integer.valueOf(watchableobject.getDataValueId()));
    341
    342            if (watchableobject1 != null)
    343            {
    344                watchableobject1.setObject(watchableobject.getObject());
    345            }
    346        }
    347
    348        this.lock.writeLock().unlock();
    349    }
    350
    351    public boolean getIsBlank()
    352    {
    353        return this.isBlank;
    354    }
    355
    356    static
    357    {
    358        dataTypes.put(Byte.class, Integer.valueOf(0));
    359        dataTypes.put(Short.class, Integer.valueOf(1));
    360        dataTypes.put(Integer.class, Integer.valueOf(2));
    361        dataTypes.put(Float.class, Integer.valueOf(3));
    362        dataTypes.put(String.class, Integer.valueOf(4));
    363        dataTypes.put(ItemStack.class, Integer.valueOf(5));
    364        dataTypes.put(ChunkCoordinates.class, Integer.valueOf(6));
    365    }
    366}