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.func_82737_E()); 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.func_82737_E(); 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 }