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