    001    package net.minecraft.src;
    002
    003    import java.io.DataInputStream;
    004    import java.io.DataOutputStream;
    005    import java.io.File;
    006    import java.io.IOException;
    007    import java.util.ArrayList;
    008    import java.util.HashSet;
    009    import java.util.Iterator;
    010    import java.util.List;
    011    import java.util.Set;
    012    import java.util.logging.Level;
    013
    014    import cpw.mods.fml.common.FMLLog;
    015
    016    import net.minecraftforge.common.MinecraftForge;
    017    import net.minecraftforge.event.world.ChunkDataEvent;
    018
    019    public class AnvilChunkLoader implements IThreadedFileIO, IChunkLoader
    020    {
    021        private List chunksToRemove = new ArrayList();
    022        private Set pendingAnvilChunksCoordinates = new HashSet();
    023        private Object syncLockObject = new Object();
    024
    025        /** Save directory for chunks using the Anvil format */
    026        final File chunkSaveLocation;
    027
    028        public AnvilChunkLoader(File par1File)
    029        {
    030            this.chunkSaveLocation = par1File;
    031        }
    032
    033        /**
    034         * Loads the specified(XZ) chunk into the specified world.
    035         */
    036        public Chunk loadChunk(World par1World, int par2, int par3) throws IOException
    037        {
    038            NBTTagCompound var4 = null;
    039            ChunkCoordIntPair var5 = new ChunkCoordIntPair(par2, par3);
    040            Object var6 = this.syncLockObject;
    041
    042            synchronized (this.syncLockObject)
    043            {
    044                if (this.pendingAnvilChunksCoordinates.contains(var5))
    045                {
    046                    Iterator var7 = this.chunksToRemove.iterator();
    047
    048                    while (var7.hasNext())
    049                    {
    050                        AnvilChunkLoaderPending var8 = (AnvilChunkLoaderPending)var7.next();
    051
    052                        if (var8.chunkCoordinate.equals(var5))
    053                        {
    054                            var4 = var8.nbtTags;
    055                            break;
    056                        }
    057                    }
    058                }
    059            }
    060
    061            if (var4 == null)
    062            {
    063                DataInputStream var11 = RegionFileCache.getChunkInputStream(this.chunkSaveLocation, par2, par3);
    064
    065                if (var11 == null)
    066                {
    067                    return null;
    068                }
    069
    070                var4 = CompressedStreamTools.read(var11);
    071            }
    072
    073            return this.checkedReadChunkFromNBT(par1World, par2, par3, var4);
    074        }
    075
    076        /**
    077         * Wraps readChunkFromNBT. Checks the coordinates and several NBT tags.
    078         */
    079        protected Chunk checkedReadChunkFromNBT(World par1World, int par2, int par3, NBTTagCompound par4NBTTagCompound)
    080        {
    081            if (!par4NBTTagCompound.hasKey("Level"))
    082            {
    083                System.out.println("Chunk file at " + par2 + "," + par3 + " is missing level data, skipping");
    084                return null;
    085            }
    086            else if (!par4NBTTagCompound.getCompoundTag("Level").hasKey("Sections"))
    087            {
    088                System.out.println("Chunk file at " + par2 + "," + par3 + " is missing block data, skipping");
    089                return null;
    090            }
    091            else
    092            {
    093                Chunk var5 = this.readChunkFromNBT(par1World, par4NBTTagCompound.getCompoundTag("Level"));
    094
    095                if (!var5.isAtLocation(par2, par3))
    096                {
    097                    System.out.println("Chunk file at " + par2 + "," + par3 + " is in the wrong location; relocating. (Expected " + par2 + ", " + par3 + ", got " + var5.xPosition + ", " + var5.zPosition + ")");
    098                    par4NBTTagCompound.setInteger("xPos", par2);
    099                    par4NBTTagCompound.setInteger("zPos", par3);
    100                    var5 = this.readChunkFromNBT(par1World, par4NBTTagCompound.getCompoundTag("Level"));
    101                }
    102
    103                MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Load(var5, par4NBTTagCompound));
    104                return var5;
    105            }
    106        }
    107
    108        public void saveChunk(World par1World, Chunk par2Chunk) throws MinecraftException, IOException
    109        {
    110            par1World.checkSessionLock();
    111
    112            try
    113            {
    114                NBTTagCompound var3 = new NBTTagCompound();
    115                NBTTagCompound var4 = new NBTTagCompound();
    116                var3.setTag("Level", var4);
    117                this.writeChunkToNBT(par2Chunk, par1World, var4);
    118                this.func_75824_a(par2Chunk.getChunkCoordIntPair(), var3);
    119                MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Save(par2Chunk, var3));
    120            }
    121            catch (Exception var5)
    122            {
    123                var5.printStackTrace();
    124            }
    125        }
    126
    127        protected void func_75824_a(ChunkCoordIntPair par1ChunkCoordIntPair, NBTTagCompound par2NBTTagCompound)
    128        {
    129            Object var3 = this.syncLockObject;
    130
    131            synchronized (this.syncLockObject)
    132            {
    133                if (this.pendingAnvilChunksCoordinates.contains(par1ChunkCoordIntPair))
    134                {
    135                    for (int var4 = 0; var4 < this.chunksToRemove.size(); ++var4)
    136                    {
    137                        if (((AnvilChunkLoaderPending)this.chunksToRemove.get(var4)).chunkCoordinate.equals(par1ChunkCoordIntPair))
    138                        {
    139                            this.chunksToRemove.set(var4, new AnvilChunkLoaderPending(par1ChunkCoordIntPair, par2NBTTagCompound));
    140                            return;
    141                        }
    142                    }
    143                }
    144
    145                this.chunksToRemove.add(new AnvilChunkLoaderPending(par1ChunkCoordIntPair, par2NBTTagCompound));
    146                this.pendingAnvilChunksCoordinates.add(par1ChunkCoordIntPair);
    147                ThreadedFileIOBase.threadedIOInstance.queueIO(this);
    148            }
    149        }
    150
    151        /**
    152         * Returns a boolean stating if the write was unsuccessful.
    153         */
    154        public boolean writeNextIO()
    155        {
    156            AnvilChunkLoaderPending var1 = null;
    157            Object var2 = this.syncLockObject;
    158
    159            synchronized (this.syncLockObject)
    160            {
    161                if (this.chunksToRemove.isEmpty())
    162                {
    163                    return false;
    164                }
    165
    166                var1 = (AnvilChunkLoaderPending)this.chunksToRemove.remove(0);
    167                this.pendingAnvilChunksCoordinates.remove(var1.chunkCoordinate);
    168            }
    169
    170            if (var1 != null)
    171            {
    172                try
    173                {
    174                    this.writeChunkNBTTags(var1);
    175                }
    176                catch (Exception var4)
    177                {
    178                    var4.printStackTrace();
    179                }
    180            }
    181
    182            return true;
    183        }
    184
    185        private void writeChunkNBTTags(AnvilChunkLoaderPending par1AnvilChunkLoaderPending) throws IOException
    186        {
    187            DataOutputStream var2 = RegionFileCache.getChunkOutputStream(this.chunkSaveLocation, par1AnvilChunkLoaderPending.chunkCoordinate.chunkXPos, par1AnvilChunkLoaderPending.chunkCoordinate.chunkZPos);
    188            CompressedStreamTools.write(par1AnvilChunkLoaderPending.nbtTags, var2);
    189            var2.close();
    190        }
    191
    192        /**
    193         * Save extra data associated with this Chunk not normally saved during autosave, only during chunk unload.
    194         * Currently unused.
    195         */
    196        public void saveExtraChunkData(World par1World, Chunk par2Chunk) {}
    197
    198        /**
    199         * Called every World.tick()
    200         */
    201        public void chunkTick() {}
    202
    203        /**
    204         * Save extra data not associated with any Chunk.  Not saved during autosave, only during world unload.  Currently
    205         * unused.
    206         */
    207        public void saveExtraData() {}
    208
    209        /**
    210         * Writes the Chunk passed as an argument to the NBTTagCompound also passed, using the World argument to retrieve
    211         * the Chunk's last update time.
    212         */
    213        private void writeChunkToNBT(Chunk par1Chunk, World par2World, NBTTagCompound par3NBTTagCompound)
    214        {
    215            par3NBTTagCompound.setInteger("xPos", par1Chunk.xPosition);
    216            par3NBTTagCompound.setInteger("zPos", par1Chunk.zPosition);
    217            par3NBTTagCompound.setLong("LastUpdate", par2World.getTotalWorldTime());
    218            par3NBTTagCompound.setIntArray("HeightMap", par1Chunk.heightMap);
    219            par3NBTTagCompound.setBoolean("TerrainPopulated", par1Chunk.isTerrainPopulated);
    220            ExtendedBlockStorage[] var4 = par1Chunk.getBlockStorageArray();
    221            NBTTagList var5 = new NBTTagList("Sections");
    222            ExtendedBlockStorage[] var6 = var4;
    223            int var7 = var4.length;
    224            NBTTagCompound var10;
    225
    226            for (int var8 = 0; var8 < var7; ++var8)
    227            {
    228                ExtendedBlockStorage var9 = var6[var8];
    229
    230                if (var9 != null)
    231                {
    232                    var10 = new NBTTagCompound();
    233                    var10.setByte("Y", (byte)(var9.getYLocation() >> 4 & 255));
    234                    var10.setByteArray("Blocks", var9.getBlockLSBArray());
    235
    236                    if (var9.getBlockMSBArray() != null)
    237                    {
    238                        var10.setByteArray("Add", var9.getBlockMSBArray().data);
    239                    }
    240
    241                    var10.setByteArray("Data", var9.getMetadataArray().data);
    242                    var10.setByteArray("SkyLight", var9.getSkylightArray().data);
    243                    var10.setByteArray("BlockLight", var9.getBlocklightArray().data);
    244                    var5.appendTag(var10);
    245                }
    246            }
    247
    248            par3NBTTagCompound.setTag("Sections", var5);
    249            par3NBTTagCompound.setByteArray("Biomes", par1Chunk.getBiomeArray());
    250            par1Chunk.hasEntities = false;
    251            NBTTagList var15 = new NBTTagList();
    252            Iterator var17;
    253
    254            for (var7 = 0; var7 < par1Chunk.entityLists.length; ++var7)
    255            {
    256                var17 = par1Chunk.entityLists[var7].iterator();
    257
    258                while (var17.hasNext())
    259                {
    260                    Entity var19 = (Entity)var17.next();
    261                    par1Chunk.hasEntities = true;
    262                    var10 = new NBTTagCompound();
    263
    264                    try
    265                    {
    266                        if (var19.addEntityID(var10))
    267                        {
    268                            var15.appendTag(var10);
    269                        }
    270                    }
    271                    catch (Exception e)
    272                    {
    273                        FMLLog.log(Level.SEVERE, e,
    274                                "An Entity type %s has thrown an exception trying to write state. It will not persist. Report this to the mod author",
    275                                var19.getClass().getName());
    276                    }
    277                }
    278            }
    279
    280            par3NBTTagCompound.setTag("Entities", var15);
    281            NBTTagList var16 = new NBTTagList();
    282            var17 = par1Chunk.chunkTileEntityMap.values().iterator();
    283
    284            while (var17.hasNext())
    285            {
    286                TileEntity var21 = (TileEntity)var17.next();
    287                var10 = new NBTTagCompound();
    288                try
    289                {
    290                    var21.writeToNBT(var10);
    291                    var16.appendTag(var10);
    292                }
    293                catch (Exception e)
    294                {
    295                    FMLLog.log(Level.SEVERE, e,
    296                            "A TileEntity type %s has throw an exception trying to write state. It will not persist. Report this to the mod author",
    297                            var21.getClass().getName());
    298                }
    299            }
    300
    301            par3NBTTagCompound.setTag("TileEntities", var16);
    302            List var18 = par2World.getPendingBlockUpdates(par1Chunk, false);
    303
    304            if (var18 != null)
    305            {
    306                long var20 = par2World.getTotalWorldTime();
    307                NBTTagList var11 = new NBTTagList();
    308                Iterator var12 = var18.iterator();
    309
    310                while (var12.hasNext())
    311                {
    312                    NextTickListEntry var13 = (NextTickListEntry)var12.next();
    313                    NBTTagCompound var14 = new NBTTagCompound();
    314                    var14.setInteger("i", var13.blockID);
    315                    var14.setInteger("x", var13.xCoord);
    316                    var14.setInteger("y", var13.yCoord);
    317                    var14.setInteger("z", var13.zCoord);
    318                    var14.setInteger("t", (int)(var13.scheduledTime - var20));
    319                    var11.appendTag(var14);
    320                }
    321
    322                par3NBTTagCompound.setTag("TileTicks", var11);
    323            }
    324        }
    325
    326        /**
    327         * Reads the data stored in the passed NBTTagCompound and creates a Chunk with that data in the passed World.
    328         * Returns the created Chunk.
    329         */
    330        private Chunk readChunkFromNBT(World par1World, NBTTagCompound par2NBTTagCompound)
    331        {
    332            int var3 = par2NBTTagCompound.getInteger("xPos");
    333            int var4 = par2NBTTagCompound.getInteger("zPos");
    334            Chunk var5 = new Chunk(par1World, var3, var4);
    335            var5.heightMap = par2NBTTagCompound.getIntArray("HeightMap");
    336            var5.isTerrainPopulated = par2NBTTagCompound.getBoolean("TerrainPopulated");
    337            NBTTagList var6 = par2NBTTagCompound.getTagList("Sections");
    338            byte var7 = 16;
    339            ExtendedBlockStorage[] var8 = new ExtendedBlockStorage[var7];
    340
    341            for (int var9 = 0; var9 < var6.tagCount(); ++var9)
    342            {
    343                NBTTagCompound var10 = (NBTTagCompound)var6.tagAt(var9);
    344                byte var11 = var10.getByte("Y");
    345                ExtendedBlockStorage var12 = new ExtendedBlockStorage(var11 << 4);
    346                var12.setBlockLSBArray(var10.getByteArray("Blocks"));
    347
    348                if (var10.hasKey("Add"))
    349                {
    350                    var12.setBlockMSBArray(new NibbleArray(var10.getByteArray("Add"), 4));
    351                }
    352
    353                var12.setBlockMetadataArray(new NibbleArray(var10.getByteArray("Data"), 4));
    354                var12.setSkylightArray(new NibbleArray(var10.getByteArray("SkyLight"), 4));
    355                var12.setBlocklightArray(new NibbleArray(var10.getByteArray("BlockLight"), 4));
    356                var12.removeInvalidBlocks();
    357                var8[var11] = var12;
    358            }
    359
    360            var5.setStorageArrays(var8);
    361
    362            if (par2NBTTagCompound.hasKey("Biomes"))
    363            {
    364                var5.setBiomeArray(par2NBTTagCompound.getByteArray("Biomes"));
    365            }
    366
    367            NBTTagList var14 = par2NBTTagCompound.getTagList("Entities");
    368
    369            if (var14 != null)
    370            {
    371                for (int var17 = 0; var17 < var14.tagCount(); ++var17)
    372                {
    373                    NBTTagCompound var16 = (NBTTagCompound)var14.tagAt(var17);
    374                    Entity var18 = EntityList.createEntityFromNBT(var16, par1World);
    375                    var5.hasEntities = true;
    376
    377                    if (var18 != null)
    378                    {
    379                        var5.addEntity(var18);
    380                    }
    381                }
    382            }
    383
    384            NBTTagList var15 = par2NBTTagCompound.getTagList("TileEntities");
    385
    386            if (var15 != null)
    387            {
    388                for (int var21 = 0; var21 < var15.tagCount(); ++var21)
    389                {
    390                    NBTTagCompound var20 = (NBTTagCompound)var15.tagAt(var21);
    391                    TileEntity var13 = TileEntity.createAndLoadEntity(var20);
    392
    393                    if (var13 != null)
    394                    {
    395                        var5.addTileEntity(var13);
    396                    }
    397                }
    398            }
    399
    400            if (par2NBTTagCompound.hasKey("TileTicks"))
    401            {
    402                NBTTagList var19 = par2NBTTagCompound.getTagList("TileTicks");
    403
    404                if (var19 != null)
    405                {
    406                    for (int var22 = 0; var22 < var19.tagCount(); ++var22)
    407                    {
    408                        NBTTagCompound var23 = (NBTTagCompound)var19.tagAt(var22);
    409                        par1World.scheduleBlockUpdateFromLoad(var23.getInteger("x"), var23.getInteger("y"), var23.getInteger("z"), var23.getInteger("i"), var23.getInteger("t"));
    410                    }
    411                }
    412            }
    413
    414            return var5;
    415        }
    416    }