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