package net.minecraft.world.chunk.storage; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; 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.util.BlockPos; import net.minecraft.util.ResourceLocation; 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 org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class AnvilChunkLoader implements IChunkLoader, IThreadedFileIO { private static final Logger logger = LogManager.getLogger(); private List chunksToRemove = Lists.newArrayList(); private Set pendingAnvilChunksCoordinates = Sets.newHashSet(); private Object syncLockObject = new Object(); /** Save directory for chunks using the Anvil format */ private final File chunkSaveLocation; private static final String __OBFID = "CL_00000384"; public AnvilChunkLoader(File p_i2003_1_) { this.chunkSaveLocation = p_i2003_1_; } /** * Loads the specified(XZ) chunk into the specified world. */ public Chunk loadChunk(World worldIn, int x, int z) throws IOException { NBTTagCompound var4 = null; ChunkCoordIntPair var5 = new ChunkCoordIntPair(x, z); Object var6 = this.syncLockObject; synchronized (this.syncLockObject) { if (this.pendingAnvilChunksCoordinates.contains(var5)) { for (int var7 = 0; var7 < this.chunksToRemove.size(); ++var7) { if (((AnvilChunkLoader.PendingChunk)this.chunksToRemove.get(var7)).chunkCoordinate.equals(var5)) { var4 = ((AnvilChunkLoader.PendingChunk)this.chunksToRemove.get(var7)).nbtTags; break; } } } } if (var4 == null) { DataInputStream var10 = RegionFileCache.getChunkInputStream(this.chunkSaveLocation, x, z); if (var10 == null) { return null; } var4 = CompressedStreamTools.read(var10); } return this.checkedReadChunkFromNBT(worldIn, x, z, var4); } /** * Wraps readChunkFromNBT. Checks the coordinates and several NBT tags. */ protected Chunk checkedReadChunkFromNBT(World worldIn, 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 var5 = this.readChunkFromNBT(worldIn, p_75822_4_.getCompoundTag("Level")); if (!var5.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 " + var5.xPosition + ", " + var5.zPosition + ")"); p_75822_4_.setInteger("xPos", p_75822_2_); p_75822_4_.setInteger("zPos", p_75822_3_); var5 = this.readChunkFromNBT(worldIn, p_75822_4_.getCompoundTag("Level")); } return var5; } } public void saveChunk(World worldIn, Chunk chunkIn) throws MinecraftException, IOException { worldIn.checkSessionLock(); try { NBTTagCompound var3 = new NBTTagCompound(); NBTTagCompound var4 = new NBTTagCompound(); var3.setTag("Level", var4); this.writeChunkToNBT(chunkIn, worldIn, var4); this.addChunkToPending(chunkIn.getChunkCoordIntPair(), var3); } catch (Exception var5) { var5.printStackTrace(); } } protected void addChunkToPending(ChunkCoordIntPair p_75824_1_, NBTTagCompound p_75824_2_) { Object var3 = this.syncLockObject; synchronized (this.syncLockObject) { if (this.pendingAnvilChunksCoordinates.contains(p_75824_1_)) { for (int var4 = 0; var4 < this.chunksToRemove.size(); ++var4) { if (((AnvilChunkLoader.PendingChunk)this.chunksToRemove.get(var4)).chunkCoordinate.equals(p_75824_1_)) { this.chunksToRemove.set(var4, 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.func_178779_a().queueIO(this); } } /** * Returns a boolean stating if the write was unsuccessful. */ public boolean writeNextIO() { AnvilChunkLoader.PendingChunk var1 = null; Object var2 = this.syncLockObject; synchronized (this.syncLockObject) { if (this.chunksToRemove.isEmpty()) { return false; } var1 = (AnvilChunkLoader.PendingChunk)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(AnvilChunkLoader.PendingChunk p_75821_1_) throws IOException { DataOutputStream var2 = RegionFileCache.getChunkOutputStream(this.chunkSaveLocation, p_75821_1_.chunkCoordinate.chunkXPos, p_75821_1_.chunkCoordinate.chunkZPos); CompressedStreamTools.write(p_75821_1_.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 worldIn, Chunk chunkIn) {} /** * 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 worldIn, 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", worldIn.getTotalWorldTime()); p_75820_3_.setIntArray("HeightMap", p_75820_1_.getHeightMap()); 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_.getInhabitedTime()); ExtendedBlockStorage[] var4 = p_75820_1_.getBlockStorageArray(); NBTTagList var5 = new NBTTagList(); boolean var6 = !worldIn.provider.getHasNoSky(); 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)); byte[] var12 = new byte[var10.getData().length]; NibbleArray var13 = new NibbleArray(); NibbleArray var14 = null; for (int var15 = 0; var15 < var10.getData().length; ++var15) { char var16 = var10.getData()[var15]; int var17 = var15 & 15; int var18 = var15 >> 8 & 15; int var19 = var15 >> 4 & 15; if (var16 >> 12 != 0) { if (var14 == null) { var14 = new NibbleArray(); } var14.set(var17, var18, var19, var16 >> 12); } var12[var15] = (byte)(var16 >> 4 & 255); var13.set(var17, var18, var19, var16 & 15); } var11.setByteArray("Blocks", var12); var11.setByteArray("Data", var13.getData()); if (var14 != null) { var11.setByteArray("Add", var14.getData()); } var11.setByteArray("BlockLight", var10.getBlocklightArray().getData()); if (var6) { var11.setByteArray("SkyLight", var10.getSkylightArray().getData()); } else { var11.setByteArray("SkyLight", new byte[var10.getBlocklightArray().getData().length]); } var5.appendTag(var11); } } p_75820_3_.setTag("Sections", var5); p_75820_3_.setByteArray("Biomes", p_75820_1_.getBiomeArray()); p_75820_1_.setHasEntities(false); NBTTagList var20 = new NBTTagList(); Iterator var22; for (var8 = 0; var8 < p_75820_1_.getEntityLists().length; ++var8) { var22 = p_75820_1_.getEntityLists()[var8].iterator(); while (var22.hasNext()) { Entity var24 = (Entity)var22.next(); var11 = new NBTTagCompound(); if (var24.writeToNBTOptional(var11)) { p_75820_1_.setHasEntities(true); var20.appendTag(var11); } } } p_75820_3_.setTag("Entities", var20); NBTTagList var21 = new NBTTagList(); var22 = p_75820_1_.getTileEntityMap().values().iterator(); while (var22.hasNext()) { TileEntity var25 = (TileEntity)var22.next(); var11 = new NBTTagCompound(); var25.writeToNBT(var11); var21.appendTag(var11); } p_75820_3_.setTag("TileEntities", var21); List var23 = worldIn.getPendingBlockUpdates(p_75820_1_, false); if (var23 != null) { long var26 = worldIn.getTotalWorldTime(); NBTTagList var27 = new NBTTagList(); Iterator var28 = var23.iterator(); while (var28.hasNext()) { NextTickListEntry var29 = (NextTickListEntry)var28.next(); NBTTagCompound var30 = new NBTTagCompound(); ResourceLocation var31 = (ResourceLocation)Block.blockRegistry.getNameForObject(var29.func_151351_a()); var30.setString("i", var31 == null ? "" : var31.toString()); var30.setInteger("x", var29.field_180282_a.getX()); var30.setInteger("y", var29.field_180282_a.getY()); var30.setInteger("z", var29.field_180282_a.getZ()); var30.setInteger("t", (int)(var29.scheduledTime - var26)); var30.setInteger("p", var29.priority); var27.appendTag(var30); } p_75820_3_.setTag("TileTicks", var27); } } /** * 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 worldIn, NBTTagCompound p_75823_2_) { int var3 = p_75823_2_.getInteger("xPos"); int var4 = p_75823_2_.getInteger("zPos"); Chunk var5 = new Chunk(worldIn, var3, var4); var5.setHeightMap(p_75823_2_.getIntArray("HeightMap")); var5.setTerrainPopulated(p_75823_2_.getBoolean("TerrainPopulated")); var5.setLightPopulated(p_75823_2_.getBoolean("LightPopulated")); var5.setInhabitedTime(p_75823_2_.getLong("InhabitedTime")); NBTTagList var6 = p_75823_2_.getTagList("Sections", 10); byte var7 = 16; ExtendedBlockStorage[] var8 = new ExtendedBlockStorage[var7]; boolean var9 = !worldIn.provider.getHasNoSky(); for (int var10 = 0; var10 < var6.tagCount(); ++var10) { NBTTagCompound var11 = var6.getCompoundTagAt(var10); byte var12 = var11.getByte("Y"); ExtendedBlockStorage var13 = new ExtendedBlockStorage(var12 << 4, var9); byte[] var14 = var11.getByteArray("Blocks"); NibbleArray var15 = new NibbleArray(var11.getByteArray("Data")); NibbleArray var16 = var11.hasKey("Add", 7) ? new NibbleArray(var11.getByteArray("Add")) : null; char[] var17 = new char[var14.length]; for (int var18 = 0; var18 < var17.length; ++var18) { int var19 = var18 & 15; int var20 = var18 >> 8 & 15; int var21 = var18 >> 4 & 15; int var22 = var16 != null ? var16.get(var19, var20, var21) : 0; var17[var18] = (char)(var22 << 12 | (var14[var18] & 255) << 4 | var15.get(var19, var20, var21)); } var13.setData(var17); var13.setBlocklightArray(new NibbleArray(var11.getByteArray("BlockLight"))); if (var9) { var13.setSkylightArray(new NibbleArray(var11.getByteArray("SkyLight"))); } var13.removeInvalidBlocks(); var8[var12] = var13; } var5.setStorageArrays(var8); if (p_75823_2_.hasKey("Biomes", 7)) { var5.setBiomeArray(p_75823_2_.getByteArray("Biomes")); } NBTTagList var23 = p_75823_2_.getTagList("Entities", 10); if (var23 != null) { for (int var24 = 0; var24 < var23.tagCount(); ++var24) { NBTTagCompound var26 = var23.getCompoundTagAt(var24); Entity var29 = EntityList.createEntityFromNBT(var26, worldIn); var5.setHasEntities(true); if (var29 != null) { var5.addEntity(var29); Entity var32 = var29; for (NBTTagCompound var35 = var26; var35.hasKey("Riding", 10); var35 = var35.getCompoundTag("Riding")) { Entity var37 = EntityList.createEntityFromNBT(var35.getCompoundTag("Riding"), worldIn); if (var37 != null) { var5.addEntity(var37); var32.mountEntity(var37); } var32 = var37; } } } } NBTTagList var25 = p_75823_2_.getTagList("TileEntities", 10); if (var25 != null) { for (int var27 = 0; var27 < var25.tagCount(); ++var27) { NBTTagCompound var30 = var25.getCompoundTagAt(var27); TileEntity var33 = TileEntity.createAndLoadEntity(var30); if (var33 != null) { var5.addTileEntity(var33); } } } if (p_75823_2_.hasKey("TileTicks", 9)) { NBTTagList var28 = p_75823_2_.getTagList("TileTicks", 10); if (var28 != null) { for (int var31 = 0; var31 < var28.tagCount(); ++var31) { NBTTagCompound var34 = var28.getCompoundTagAt(var31); Block var36; if (var34.hasKey("i", 8)) { var36 = Block.getBlockFromName(var34.getString("i")); } else { var36 = Block.getBlockById(var34.getInteger("i")); } worldIn.func_180497_b(new BlockPos(var34.getInteger("x"), var34.getInteger("y"), var34.getInteger("z")), var36, var34.getInteger("t"), var34.getInteger("p")); } } } return var5; } 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_; } } }