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 }