package net.minecraft.world.chunk.storage; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityList; import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.MinecraftException; import net.minecraft.world.NextTickListEntry; import net.minecraft.world.World; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.NibbleArray; import net.minecraft.world.storage.IThreadedFileIO; import net.minecraft.world.storage.ThreadedFileIOBase; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.world.ChunkDataEvent; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import cpw.mods.fml.common.FMLLog; public class AnvilChunkLoader implements IChunkLoader, IThreadedFileIO { private static final Logger logger = LogManager.getLogger(); private List chunksToRemove = new ArrayList(); private Set pendingAnvilChunksCoordinates = new HashSet(); private Object syncLockObject = new Object(); /** Save directory for chunks using the Anvil format */ public final File chunkSaveLocation; private static final String __OBFID = "CL_00000384"; public AnvilChunkLoader(File p_i2003_1_) { this.chunkSaveLocation = p_i2003_1_; } public boolean chunkExists(World world, int i, int j) { ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i, j); synchronized (this.syncLockObject) { if (this.pendingAnvilChunksCoordinates.contains(chunkcoordintpair)) { Iterator iter = this.chunksToRemove.iterator(); while (iter.hasNext()) { PendingChunk pendingChunk = (PendingChunk)iter.next(); if (pendingChunk.chunkCoordinate.equals(chunkcoordintpair)) { return true; } } } } return RegionFileCache.createOrLoadRegionFile(this.chunkSaveLocation, i, j).chunkExists(i & 31, j & 31); } /** * Loads the specified(XZ) chunk into the specified world. */ public Chunk loadChunk(World p_75815_1_, int p_75815_2_, int p_75815_3_) throws IOException { Object[] data = this.loadChunk__Async(p_75815_1_, p_75815_2_, p_75815_3_); if (data != null) { Chunk chunk = (Chunk) data[0]; NBTTagCompound nbttagcompound = (NBTTagCompound) data[1]; this.loadEntities(p_75815_1_, nbttagcompound.getCompoundTag("Level"), chunk); return chunk; } return null; } public Object[] loadChunk__Async(World p_75815_1_, int p_75815_2_, int p_75815_3_) throws IOException { NBTTagCompound nbttagcompound = null; ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(p_75815_2_, p_75815_3_); Object object = this.syncLockObject; synchronized (this.syncLockObject) { if (this.pendingAnvilChunksCoordinates.contains(chunkcoordintpair)) { Iterator iter = this.chunksToRemove.iterator(); while (iter.hasNext()) { PendingChunk pendingChunk = (PendingChunk)iter.next(); if (pendingChunk.chunkCoordinate.equals(chunkcoordintpair)) { nbttagcompound = pendingChunk.nbtTags; break; } } } } if (nbttagcompound == null) { DataInputStream datainputstream = RegionFileCache.getChunkInputStream(this.chunkSaveLocation, p_75815_2_, p_75815_3_); if (datainputstream == null) { return null; } nbttagcompound = CompressedStreamTools.read(datainputstream); } return this.checkedReadChunkFromNBT__Async(p_75815_1_, p_75815_2_, p_75815_3_, nbttagcompound); } /** * Wraps readChunkFromNBT. Checks the coordinates and several NBT tags. */ protected Chunk checkedReadChunkFromNBT(World p_75822_1_, int p_75822_2_, int p_75822_3_, NBTTagCompound p_75822_4_) { Object[] data = this.checkedReadChunkFromNBT__Async(p_75822_1_, p_75822_2_, p_75822_3_, p_75822_4_); if (data != null) { Chunk chunk = (Chunk) data[0]; return chunk; } return null; } protected Object[] checkedReadChunkFromNBT__Async(World p_75822_1_, int p_75822_2_, int p_75822_3_, NBTTagCompound p_75822_4_) { if (!p_75822_4_.hasKey("Level", 10)) { logger.error("Chunk file at " + p_75822_2_ + "," + p_75822_3_ + " is missing level data, skipping"); return null; } else if (!p_75822_4_.getCompoundTag("Level").hasKey("Sections", 9)) { logger.error("Chunk file at " + p_75822_2_ + "," + p_75822_3_ + " is missing block data, skipping"); return null; } else { Chunk chunk = this.readChunkFromNBT(p_75822_1_, p_75822_4_.getCompoundTag("Level")); if (!chunk.isAtLocation(p_75822_2_, p_75822_3_)) { logger.error("Chunk file at " + p_75822_2_ + "," + p_75822_3_ + " is in the wrong location; relocating. (Expected " + p_75822_2_ + ", " + p_75822_3_ + ", got " + chunk.xPosition + ", " + chunk.zPosition + ")"); p_75822_4_.setInteger("xPos", p_75822_2_); p_75822_4_.setInteger("zPos", p_75822_3_); // Have to move tile entities since we don't load them at this stage NBTTagList tileEntities = p_75822_4_.getCompoundTag("Level").getTagList("TileEntities", 10); if (tileEntities != null) { for (int te = 0; te < tileEntities.tagCount(); te++) { NBTTagCompound tileEntity = (NBTTagCompound) tileEntities.getCompoundTagAt(te); int x = tileEntity.getInteger("x") - chunk.xPosition * 16; int z = tileEntity.getInteger("z") - chunk.zPosition * 16; tileEntity.setInteger("x", p_75822_2_ * 16 + x); tileEntity.setInteger("z", p_75822_3_ * 16 + z); } } chunk = this.readChunkFromNBT(p_75822_1_, p_75822_4_.getCompoundTag("Level")); } Object[] data = new Object[2]; data[0] = chunk; data[1] = p_75822_4_; // event is fired in ChunkIOProvider.callStage2 since it must be fired after TE's load. // MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Load(chunk, par4NBTTagCompound)); return data; } } public void saveChunk(World p_75816_1_, Chunk p_75816_2_) throws MinecraftException, IOException { p_75816_1_.checkSessionLock(); try { NBTTagCompound nbttagcompound = new NBTTagCompound(); NBTTagCompound nbttagcompound1 = new NBTTagCompound(); nbttagcompound.setTag("Level", nbttagcompound1); this.writeChunkToNBT(p_75816_2_, p_75816_1_, nbttagcompound1); MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Save(p_75816_2_, nbttagcompound)); this.addChunkToPending(p_75816_2_.getChunkCoordIntPair(), nbttagcompound); } catch (Exception exception) { exception.printStackTrace(); } } protected void addChunkToPending(ChunkCoordIntPair p_75824_1_, NBTTagCompound p_75824_2_) { Object object = this.syncLockObject; synchronized (this.syncLockObject) { if (this.pendingAnvilChunksCoordinates.contains(p_75824_1_)) { for (int i = 0; i < this.chunksToRemove.size(); ++i) { if (((AnvilChunkLoader.PendingChunk)this.chunksToRemove.get(i)).chunkCoordinate.equals(p_75824_1_)) { this.chunksToRemove.set(i, new AnvilChunkLoader.PendingChunk(p_75824_1_, p_75824_2_)); return; } } } this.chunksToRemove.add(new AnvilChunkLoader.PendingChunk(p_75824_1_, p_75824_2_)); this.pendingAnvilChunksCoordinates.add(p_75824_1_); ThreadedFileIOBase.threadedIOInstance.queueIO(this); } } /** * Returns a boolean stating if the write was unsuccessful. */ public boolean writeNextIO() { AnvilChunkLoader.PendingChunk pendingchunk = null; Object object = this.syncLockObject; synchronized (this.syncLockObject) { if (this.chunksToRemove.isEmpty()) { return false; } pendingchunk = (AnvilChunkLoader.PendingChunk)this.chunksToRemove.remove(0); this.pendingAnvilChunksCoordinates.remove(pendingchunk.chunkCoordinate); } if (pendingchunk != null) { try { this.writeChunkNBTTags(pendingchunk); } catch (Exception exception) { exception.printStackTrace(); } } return true; } private void writeChunkNBTTags(AnvilChunkLoader.PendingChunk p_75821_1_) throws IOException { DataOutputStream dataoutputstream = RegionFileCache.getChunkOutputStream(this.chunkSaveLocation, p_75821_1_.chunkCoordinate.chunkXPos, p_75821_1_.chunkCoordinate.chunkZPos); CompressedStreamTools.write(p_75821_1_.nbtTags, dataoutputstream); dataoutputstream.close(); } /** * Save extra data associated with this Chunk not normally saved during autosave, only during chunk unload. * Currently unused. */ public void saveExtraChunkData(World p_75819_1_, Chunk p_75819_2_) {} /** * Called every World.tick() */ public void chunkTick() {} /** * Save extra data not associated with any Chunk. Not saved during autosave, only during world unload. Currently * unused. */ public void saveExtraData() { while (this.writeNextIO()) { ; } } /** * Writes the Chunk passed as an argument to the NBTTagCompound also passed, using the World argument to retrieve * the Chunk's last update time. */ private void writeChunkToNBT(Chunk p_75820_1_, World p_75820_2_, NBTTagCompound p_75820_3_) { p_75820_3_.setByte("V", (byte)1); p_75820_3_.setInteger("xPos", p_75820_1_.xPosition); p_75820_3_.setInteger("zPos", p_75820_1_.zPosition); p_75820_3_.setLong("LastUpdate", p_75820_2_.getTotalWorldTime()); p_75820_3_.setIntArray("HeightMap", p_75820_1_.heightMap); p_75820_3_.setBoolean("TerrainPopulated", p_75820_1_.isTerrainPopulated); p_75820_3_.setBoolean("LightPopulated", p_75820_1_.isLightPopulated); p_75820_3_.setLong("InhabitedTime", p_75820_1_.inhabitedTime); ExtendedBlockStorage[] aextendedblockstorage = p_75820_1_.getBlockStorageArray(); NBTTagList nbttaglist = new NBTTagList(); boolean flag = !p_75820_2_.provider.hasNoSky; ExtendedBlockStorage[] aextendedblockstorage1 = aextendedblockstorage; int i = aextendedblockstorage.length; NBTTagCompound nbttagcompound1; for (int j = 0; j < i; ++j) { ExtendedBlockStorage extendedblockstorage = aextendedblockstorage1[j]; if (extendedblockstorage != null) { nbttagcompound1 = new NBTTagCompound(); nbttagcompound1.setByte("Y", (byte)(extendedblockstorage.getYLocation() >> 4 & 255)); nbttagcompound1.setByteArray("Blocks", extendedblockstorage.getBlockLSBArray()); if (extendedblockstorage.getBlockMSBArray() != null) { nbttagcompound1.setByteArray("Add", extendedblockstorage.getBlockMSBArray().data); } nbttagcompound1.setByteArray("Data", extendedblockstorage.getMetadataArray().data); nbttagcompound1.setByteArray("BlockLight", extendedblockstorage.getBlocklightArray().data); if (flag) { nbttagcompound1.setByteArray("SkyLight", extendedblockstorage.getSkylightArray().data); } else { nbttagcompound1.setByteArray("SkyLight", new byte[extendedblockstorage.getBlocklightArray().data.length]); } nbttaglist.appendTag(nbttagcompound1); } } p_75820_3_.setTag("Sections", nbttaglist); p_75820_3_.setByteArray("Biomes", p_75820_1_.getBiomeArray()); p_75820_1_.hasEntities = false; NBTTagList nbttaglist2 = new NBTTagList(); Iterator iterator1; for (i = 0; i < p_75820_1_.entityLists.length; ++i) { iterator1 = p_75820_1_.entityLists[i].iterator(); while (iterator1.hasNext()) { Entity entity = (Entity)iterator1.next(); nbttagcompound1 = new NBTTagCompound(); try { if (entity.writeToNBTOptional(nbttagcompound1)) { p_75820_1_.hasEntities = true; nbttaglist2.appendTag(nbttagcompound1); } } catch (Exception e) { FMLLog.log(Level.ERROR, e, "An Entity type %s has thrown an exception trying to write state. It will not persist. Report this to the mod author", entity.getClass().getName()); } } } p_75820_3_.setTag("Entities", nbttaglist2); NBTTagList nbttaglist3 = new NBTTagList(); iterator1 = p_75820_1_.chunkTileEntityMap.values().iterator(); while (iterator1.hasNext()) { TileEntity tileentity = (TileEntity)iterator1.next(); nbttagcompound1 = new NBTTagCompound(); try { tileentity.writeToNBT(nbttagcompound1); nbttaglist3.appendTag(nbttagcompound1); } catch (Exception e) { FMLLog.log(Level.ERROR, e, "A TileEntity type %s has throw an exception trying to write state. It will not persist. Report this to the mod author", tileentity.getClass().getName()); } } p_75820_3_.setTag("TileEntities", nbttaglist3); List list = p_75820_2_.getPendingBlockUpdates(p_75820_1_, false); if (list != null) { long k = p_75820_2_.getTotalWorldTime(); NBTTagList nbttaglist1 = new NBTTagList(); Iterator iterator = list.iterator(); while (iterator.hasNext()) { NextTickListEntry nextticklistentry = (NextTickListEntry)iterator.next(); NBTTagCompound nbttagcompound2 = new NBTTagCompound(); nbttagcompound2.setInteger("i", Block.getIdFromBlock(nextticklistentry.func_151351_a())); nbttagcompound2.setInteger("x", nextticklistentry.xCoord); nbttagcompound2.setInteger("y", nextticklistentry.yCoord); nbttagcompound2.setInteger("z", nextticklistentry.zCoord); nbttagcompound2.setInteger("t", (int)(nextticklistentry.scheduledTime - k)); nbttagcompound2.setInteger("p", nextticklistentry.priority); nbttaglist1.appendTag(nbttagcompound2); } p_75820_3_.setTag("TileTicks", nbttaglist1); } } /** * Reads the data stored in the passed NBTTagCompound and creates a Chunk with that data in the passed World. * Returns the created Chunk. */ private Chunk readChunkFromNBT(World p_75823_1_, NBTTagCompound p_75823_2_) { int i = p_75823_2_.getInteger("xPos"); int j = p_75823_2_.getInteger("zPos"); Chunk chunk = new Chunk(p_75823_1_, i, j); chunk.heightMap = p_75823_2_.getIntArray("HeightMap"); chunk.isTerrainPopulated = p_75823_2_.getBoolean("TerrainPopulated"); chunk.isLightPopulated = p_75823_2_.getBoolean("LightPopulated"); chunk.inhabitedTime = p_75823_2_.getLong("InhabitedTime"); NBTTagList nbttaglist = p_75823_2_.getTagList("Sections", 10); byte b0 = 16; ExtendedBlockStorage[] aextendedblockstorage = new ExtendedBlockStorage[b0]; boolean flag = !p_75823_1_.provider.hasNoSky; for (int k = 0; k < nbttaglist.tagCount(); ++k) { NBTTagCompound nbttagcompound1 = nbttaglist.getCompoundTagAt(k); byte b1 = nbttagcompound1.getByte("Y"); ExtendedBlockStorage extendedblockstorage = new ExtendedBlockStorage(b1 << 4, flag); extendedblockstorage.setBlockLSBArray(nbttagcompound1.getByteArray("Blocks")); if (nbttagcompound1.hasKey("Add", 7)) { extendedblockstorage.setBlockMSBArray(new NibbleArray(nbttagcompound1.getByteArray("Add"), 4)); } extendedblockstorage.setBlockMetadataArray(new NibbleArray(nbttagcompound1.getByteArray("Data"), 4)); extendedblockstorage.setBlocklightArray(new NibbleArray(nbttagcompound1.getByteArray("BlockLight"), 4)); if (flag) { extendedblockstorage.setSkylightArray(new NibbleArray(nbttagcompound1.getByteArray("SkyLight"), 4)); } extendedblockstorage.removeInvalidBlocks(); aextendedblockstorage[b1] = extendedblockstorage; } chunk.setStorageArrays(aextendedblockstorage); if (p_75823_2_.hasKey("Biomes", 7)) { chunk.setBiomeArray(p_75823_2_.getByteArray("Biomes")); } // End this method here and split off entity loading to another method return chunk; } public void loadEntities(World p_75823_1_, NBTTagCompound p_75823_2_, Chunk chunk) { NBTTagList nbttaglist1 = p_75823_2_.getTagList("Entities", 10); if (nbttaglist1 != null) { for (int l = 0; l < nbttaglist1.tagCount(); ++l) { NBTTagCompound nbttagcompound3 = nbttaglist1.getCompoundTagAt(l); Entity entity2 = EntityList.createEntityFromNBT(nbttagcompound3, p_75823_1_); chunk.hasEntities = true; if (entity2 != null) { chunk.addEntity(entity2); Entity entity = entity2; for (NBTTagCompound nbttagcompound2 = nbttagcompound3; nbttagcompound2.hasKey("Riding", 10); nbttagcompound2 = nbttagcompound2.getCompoundTag("Riding")) { Entity entity1 = EntityList.createEntityFromNBT(nbttagcompound2.getCompoundTag("Riding"), p_75823_1_); if (entity1 != null) { chunk.addEntity(entity1); entity.mountEntity(entity1); } entity = entity1; } } } } NBTTagList nbttaglist2 = p_75823_2_.getTagList("TileEntities", 10); if (nbttaglist2 != null) { for (int i1 = 0; i1 < nbttaglist2.tagCount(); ++i1) { NBTTagCompound nbttagcompound4 = nbttaglist2.getCompoundTagAt(i1); TileEntity tileentity = TileEntity.createAndLoadEntity(nbttagcompound4); if (tileentity != null) { chunk.addTileEntity(tileentity); } } } if (p_75823_2_.hasKey("TileTicks", 9)) { NBTTagList nbttaglist3 = p_75823_2_.getTagList("TileTicks", 10); if (nbttaglist3 != null) { for (int j1 = 0; j1 < nbttaglist3.tagCount(); ++j1) { NBTTagCompound nbttagcompound5 = nbttaglist3.getCompoundTagAt(j1); p_75823_1_.func_147446_b(nbttagcompound5.getInteger("x"), nbttagcompound5.getInteger("y"), nbttagcompound5.getInteger("z"), Block.getBlockById(nbttagcompound5.getInteger("i")), nbttagcompound5.getInteger("t"), nbttagcompound5.getInteger("p")); } } } // return chunk; } static class PendingChunk { public final ChunkCoordIntPair chunkCoordinate; public final NBTTagCompound nbtTags; private static final String __OBFID = "CL_00000385"; public PendingChunk(ChunkCoordIntPair p_i2002_1_, NBTTagCompound p_i2002_2_) { this.chunkCoordinate = p_i2002_1_; this.nbtTags = p_i2002_2_; } } }