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 java.util.logging.Level; import cpw.mods.fml.common.FMLLog; 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; public class AnvilChunkLoader implements IChunkLoader, IThreadedFileIO { 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; public AnvilChunkLoader(File par1File) { this.chunkSaveLocation = par1File; } /** * Loads the specified(XZ) chunk into the specified world. */ public Chunk loadChunk(World par1World, int par2, int par3) throws IOException { NBTTagCompound var4 = null; ChunkCoordIntPair var5 = new ChunkCoordIntPair(par2, par3); Object var6 = this.syncLockObject; synchronized (this.syncLockObject) { if (this.pendingAnvilChunksCoordinates.contains(var5)) { for (int var7 = 0; var7 < this.chunksToRemove.size(); ++var7) { if (((AnvilChunkLoaderPending)this.chunksToRemove.get(var7)).chunkCoordinate.equals(var5)) { var4 = ((AnvilChunkLoaderPending)this.chunksToRemove.get(var7)).nbtTags; break; } } } } if (var4 == null) { DataInputStream var10 = RegionFileCache.getChunkInputStream(this.chunkSaveLocation, par2, par3); if (var10 == null) { return null; } var4 = CompressedStreamTools.read(var10); } return this.checkedReadChunkFromNBT(par1World, par2, par3, var4); } /** * Wraps readChunkFromNBT. Checks the coordinates and several NBT tags. */ protected Chunk checkedReadChunkFromNBT(World par1World, int par2, int par3, NBTTagCompound par4NBTTagCompound) { if (!par4NBTTagCompound.hasKey("Level")) { System.out.println("Chunk file at " + par2 + "," + par3 + " is missing level data, skipping"); return null; } else if (!par4NBTTagCompound.getCompoundTag("Level").hasKey("Sections")) { System.out.println("Chunk file at " + par2 + "," + par3 + " is missing block data, skipping"); return null; } else { Chunk var5 = this.readChunkFromNBT(par1World, par4NBTTagCompound.getCompoundTag("Level")); if (!var5.isAtLocation(par2, par3)) { System.out.println("Chunk file at " + par2 + "," + par3 + " is in the wrong location; relocating. (Expected " + par2 + ", " + par3 + ", got " + var5.xPosition + ", " + var5.zPosition + ")"); par4NBTTagCompound.setInteger("xPos", par2); par4NBTTagCompound.setInteger("zPos", par3); var5 = this.readChunkFromNBT(par1World, par4NBTTagCompound.getCompoundTag("Level")); } MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Load(var5, par4NBTTagCompound)); return var5; } } public void saveChunk(World par1World, Chunk par2Chunk) throws MinecraftException, IOException { par1World.checkSessionLock(); try { NBTTagCompound var3 = new NBTTagCompound(); NBTTagCompound var4 = new NBTTagCompound(); var3.setTag("Level", var4); this.writeChunkToNBT(par2Chunk, par1World, var4); this.func_75824_a(par2Chunk.getChunkCoordIntPair(), var3); MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Save(par2Chunk, var3)); } catch (Exception var5) { var5.printStackTrace(); } } protected void func_75824_a(ChunkCoordIntPair par1ChunkCoordIntPair, NBTTagCompound par2NBTTagCompound) { Object var3 = this.syncLockObject; synchronized (this.syncLockObject) { if (this.pendingAnvilChunksCoordinates.contains(par1ChunkCoordIntPair)) { for (int var4 = 0; var4 < this.chunksToRemove.size(); ++var4) { if (((AnvilChunkLoaderPending)this.chunksToRemove.get(var4)).chunkCoordinate.equals(par1ChunkCoordIntPair)) { this.chunksToRemove.set(var4, new AnvilChunkLoaderPending(par1ChunkCoordIntPair, par2NBTTagCompound)); return; } } } this.chunksToRemove.add(new AnvilChunkLoaderPending(par1ChunkCoordIntPair, par2NBTTagCompound)); this.pendingAnvilChunksCoordinates.add(par1ChunkCoordIntPair); ThreadedFileIOBase.threadedIOInstance.queueIO(this); } } /** * Returns a boolean stating if the write was unsuccessful. */ public boolean writeNextIO() { AnvilChunkLoaderPending var1 = null; Object var2 = this.syncLockObject; synchronized (this.syncLockObject) { if (this.chunksToRemove.isEmpty()) { return false; } var1 = (AnvilChunkLoaderPending)this.chunksToRemove.remove(0); this.pendingAnvilChunksCoordinates.remove(var1.chunkCoordinate); } if (var1 != null) { try { this.writeChunkNBTTags(var1); } catch (Exception var4) { var4.printStackTrace(); } } return true; } private void writeChunkNBTTags(AnvilChunkLoaderPending par1AnvilChunkLoaderPending) throws IOException { DataOutputStream var2 = RegionFileCache.getChunkOutputStream(this.chunkSaveLocation, par1AnvilChunkLoaderPending.chunkCoordinate.chunkXPos, par1AnvilChunkLoaderPending.chunkCoordinate.chunkZPos); CompressedStreamTools.write(par1AnvilChunkLoaderPending.nbtTags, var2); var2.close(); } /** * Save extra data associated with this Chunk not normally saved during autosave, only during chunk unload. * Currently unused. */ public void saveExtraChunkData(World par1World, Chunk par2Chunk) {} /** * 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() {} /** * 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 par1Chunk, World par2World, NBTTagCompound par3NBTTagCompound) { par3NBTTagCompound.setInteger("xPos", par1Chunk.xPosition); par3NBTTagCompound.setInteger("zPos", par1Chunk.zPosition); par3NBTTagCompound.setLong("LastUpdate", par2World.getTotalWorldTime()); par3NBTTagCompound.setIntArray("HeightMap", par1Chunk.heightMap); par3NBTTagCompound.setBoolean("TerrainPopulated", par1Chunk.isTerrainPopulated); ExtendedBlockStorage[] var4 = par1Chunk.getBlockStorageArray(); NBTTagList var5 = new NBTTagList("Sections"); boolean var6 = !par2World.provider.hasNoSky; ExtendedBlockStorage[] var7 = var4; int var8 = var4.length; NBTTagCompound var11; for (int var9 = 0; var9 < var8; ++var9) { ExtendedBlockStorage var10 = var7[var9]; if (var10 != null) { var11 = new NBTTagCompound(); var11.setByte("Y", (byte)(var10.getYLocation() >> 4 & 255)); var11.setByteArray("Blocks", var10.getBlockLSBArray()); if (var10.getBlockMSBArray() != null) { var11.setByteArray("Add", var10.getBlockMSBArray().data); } var11.setByteArray("Data", var10.getMetadataArray().data); var11.setByteArray("BlockLight", var10.getBlocklightArray().data); if (var6) { var11.setByteArray("SkyLight", var10.getSkylightArray().data); } else { var11.setByteArray("SkyLight", new byte[var10.getBlocklightArray().data.length]); } var5.appendTag(var11); } } par3NBTTagCompound.setTag("Sections", var5); par3NBTTagCompound.setByteArray("Biomes", par1Chunk.getBiomeArray()); par1Chunk.hasEntities = false; NBTTagList var16 = new NBTTagList(); Iterator var18; for (var8 = 0; var8 < par1Chunk.entityLists.length; ++var8) { var18 = par1Chunk.entityLists[var8].iterator(); while (var18.hasNext()) { Entity var21 = (Entity)var18.next(); par1Chunk.hasEntities = true; var11 = new NBTTagCompound(); try { if (var21.addEntityID(var11)) { var16.appendTag(var11); } } catch (Exception e) { FMLLog.log(Level.SEVERE, e, "An Entity type %s has thrown an exception trying to write state. It will not persist. Report this to the mod author", var21.getClass().getName()); } } } par3NBTTagCompound.setTag("Entities", var16); NBTTagList var17 = new NBTTagList(); var18 = par1Chunk.chunkTileEntityMap.values().iterator(); while (var18.hasNext()) { TileEntity var22 = (TileEntity)var18.next(); var11 = new NBTTagCompound(); try { var22.writeToNBT(var11); var17.appendTag(var11); } catch (Exception e) { FMLLog.log(Level.SEVERE, e, "A TileEntity type %s has throw an exception trying to write state. It will not persist. Report this to the mod author", var22.getClass().getName()); } } par3NBTTagCompound.setTag("TileEntities", var17); List var20 = par2World.getPendingBlockUpdates(par1Chunk, false); if (var20 != null) { long var19 = par2World.getTotalWorldTime(); NBTTagList var12 = new NBTTagList(); Iterator var13 = var20.iterator(); while (var13.hasNext()) { NextTickListEntry var14 = (NextTickListEntry)var13.next(); NBTTagCompound var15 = new NBTTagCompound(); var15.setInteger("i", var14.blockID); var15.setInteger("x", var14.xCoord); var15.setInteger("y", var14.yCoord); var15.setInteger("z", var14.zCoord); var15.setInteger("t", (int)(var14.scheduledTime - var19)); var12.appendTag(var15); } par3NBTTagCompound.setTag("TileTicks", var12); } } /** * 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 par1World, NBTTagCompound par2NBTTagCompound) { int var3 = par2NBTTagCompound.getInteger("xPos"); int var4 = par2NBTTagCompound.getInteger("zPos"); Chunk var5 = new Chunk(par1World, var3, var4); var5.heightMap = par2NBTTagCompound.getIntArray("HeightMap"); var5.isTerrainPopulated = par2NBTTagCompound.getBoolean("TerrainPopulated"); NBTTagList var6 = par2NBTTagCompound.getTagList("Sections"); byte var7 = 16; ExtendedBlockStorage[] var8 = new ExtendedBlockStorage[var7]; boolean var9 = !par1World.provider.hasNoSky; for (int var10 = 0; var10 < var6.tagCount(); ++var10) { NBTTagCompound var11 = (NBTTagCompound)var6.tagAt(var10); byte var12 = var11.getByte("Y"); ExtendedBlockStorage var13 = new ExtendedBlockStorage(var12 << 4, var9); var13.setBlockLSBArray(var11.getByteArray("Blocks")); if (var11.hasKey("Add")) { var13.setBlockMSBArray(new NibbleArray(var11.getByteArray("Add"), 4)); } var13.setBlockMetadataArray(new NibbleArray(var11.getByteArray("Data"), 4)); var13.setBlocklightArray(new NibbleArray(var11.getByteArray("BlockLight"), 4)); if (var9) { var13.setSkylightArray(new NibbleArray(var11.getByteArray("SkyLight"), 4)); } var13.removeInvalidBlocks(); var8[var12] = var13; } var5.setStorageArrays(var8); if (par2NBTTagCompound.hasKey("Biomes")) { var5.setBiomeArray(par2NBTTagCompound.getByteArray("Biomes")); } NBTTagList var16 = par2NBTTagCompound.getTagList("Entities"); if (var16 != null) { for (int var15 = 0; var15 < var16.tagCount(); ++var15) { NBTTagCompound var17 = (NBTTagCompound)var16.tagAt(var15); Entity var22 = EntityList.createEntityFromNBT(var17, par1World); var5.hasEntities = true; if (var22 != null) { var5.addEntity(var22); } } } NBTTagList var19 = par2NBTTagCompound.getTagList("TileEntities"); if (var19 != null) { for (int var18 = 0; var18 < var19.tagCount(); ++var18) { NBTTagCompound var20 = (NBTTagCompound)var19.tagAt(var18); TileEntity var14 = TileEntity.createAndLoadEntity(var20); if (var14 != null) { var5.addTileEntity(var14); } } } if (par2NBTTagCompound.hasKey("TileTicks")) { NBTTagList var23 = par2NBTTagCompound.getTagList("TileTicks"); if (var23 != null) { for (int var21 = 0; var21 < var23.tagCount(); ++var21) { NBTTagCompound var24 = (NBTTagCompound)var23.tagAt(var21); par1World.scheduleBlockUpdateFromLoad(var24.getInteger("x"), var24.getInteger("y"), var24.getInteger("z"), var24.getInteger("i"), var24.getInteger("t")); } } } return var5; } }