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}