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 Iterator var7 = this.chunksToRemove.iterator(); 047 048 while (var7.hasNext()) 049 { 050 AnvilChunkLoaderPending var8 = (AnvilChunkLoaderPending)var7.next(); 051 052 if (var8.chunkCoordinate.equals(var5)) 053 { 054 var4 = var8.nbtTags; 055 break; 056 } 057 } 058 } 059 } 060 061 if (var4 == null) 062 { 063 DataInputStream var11 = RegionFileCache.getChunkInputStream(this.chunkSaveLocation, par2, par3); 064 065 if (var11 == null) 066 { 067 return null; 068 } 069 070 var4 = CompressedStreamTools.read(var11); 071 } 072 073 return this.checkedReadChunkFromNBT(par1World, par2, par3, var4); 074 } 075 076 /** 077 * Wraps readChunkFromNBT. Checks the coordinates and several NBT tags. 078 */ 079 protected Chunk checkedReadChunkFromNBT(World par1World, int par2, int par3, NBTTagCompound par4NBTTagCompound) 080 { 081 if (!par4NBTTagCompound.hasKey("Level")) 082 { 083 System.out.println("Chunk file at " + par2 + "," + par3 + " is missing level data, skipping"); 084 return null; 085 } 086 else if (!par4NBTTagCompound.getCompoundTag("Level").hasKey("Sections")) 087 { 088 System.out.println("Chunk file at " + par2 + "," + par3 + " is missing block data, skipping"); 089 return null; 090 } 091 else 092 { 093 Chunk var5 = this.readChunkFromNBT(par1World, par4NBTTagCompound.getCompoundTag("Level")); 094 095 if (!var5.isAtLocation(par2, par3)) 096 { 097 System.out.println("Chunk file at " + par2 + "," + par3 + " is in the wrong location; relocating. (Expected " + par2 + ", " + par3 + ", got " + var5.xPosition + ", " + var5.zPosition + ")"); 098 par4NBTTagCompound.setInteger("xPos", par2); 099 par4NBTTagCompound.setInteger("zPos", par3); 100 var5 = this.readChunkFromNBT(par1World, par4NBTTagCompound.getCompoundTag("Level")); 101 } 102 103 MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Load(var5, par4NBTTagCompound)); 104 return var5; 105 } 106 } 107 108 public void saveChunk(World par1World, Chunk par2Chunk) throws MinecraftException, IOException 109 { 110 par1World.checkSessionLock(); 111 112 try 113 { 114 NBTTagCompound var3 = new NBTTagCompound(); 115 NBTTagCompound var4 = new NBTTagCompound(); 116 var3.setTag("Level", var4); 117 this.writeChunkToNBT(par2Chunk, par1World, var4); 118 this.func_75824_a(par2Chunk.getChunkCoordIntPair(), var3); 119 MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Save(par2Chunk, var3)); 120 } 121 catch (Exception var5) 122 { 123 var5.printStackTrace(); 124 } 125 } 126 127 protected void func_75824_a(ChunkCoordIntPair par1ChunkCoordIntPair, NBTTagCompound par2NBTTagCompound) 128 { 129 Object var3 = this.syncLockObject; 130 131 synchronized (this.syncLockObject) 132 { 133 if (this.pendingAnvilChunksCoordinates.contains(par1ChunkCoordIntPair)) 134 { 135 for (int var4 = 0; var4 < this.chunksToRemove.size(); ++var4) 136 { 137 if (((AnvilChunkLoaderPending)this.chunksToRemove.get(var4)).chunkCoordinate.equals(par1ChunkCoordIntPair)) 138 { 139 this.chunksToRemove.set(var4, new AnvilChunkLoaderPending(par1ChunkCoordIntPair, par2NBTTagCompound)); 140 return; 141 } 142 } 143 } 144 145 this.chunksToRemove.add(new AnvilChunkLoaderPending(par1ChunkCoordIntPair, par2NBTTagCompound)); 146 this.pendingAnvilChunksCoordinates.add(par1ChunkCoordIntPair); 147 ThreadedFileIOBase.threadedIOInstance.queueIO(this); 148 } 149 } 150 151 /** 152 * Returns a boolean stating if the write was unsuccessful. 153 */ 154 public boolean writeNextIO() 155 { 156 AnvilChunkLoaderPending var1 = null; 157 Object var2 = this.syncLockObject; 158 159 synchronized (this.syncLockObject) 160 { 161 if (this.chunksToRemove.isEmpty()) 162 { 163 return false; 164 } 165 166 var1 = (AnvilChunkLoaderPending)this.chunksToRemove.remove(0); 167 this.pendingAnvilChunksCoordinates.remove(var1.chunkCoordinate); 168 } 169 170 if (var1 != null) 171 { 172 try 173 { 174 this.writeChunkNBTTags(var1); 175 } 176 catch (Exception var4) 177 { 178 var4.printStackTrace(); 179 } 180 } 181 182 return true; 183 } 184 185 private void writeChunkNBTTags(AnvilChunkLoaderPending par1AnvilChunkLoaderPending) throws IOException 186 { 187 DataOutputStream var2 = RegionFileCache.getChunkOutputStream(this.chunkSaveLocation, par1AnvilChunkLoaderPending.chunkCoordinate.chunkXPos, par1AnvilChunkLoaderPending.chunkCoordinate.chunkZPos); 188 CompressedStreamTools.write(par1AnvilChunkLoaderPending.nbtTags, var2); 189 var2.close(); 190 } 191 192 /** 193 * Save extra data associated with this Chunk not normally saved during autosave, only during chunk unload. 194 * Currently unused. 195 */ 196 public void saveExtraChunkData(World par1World, Chunk par2Chunk) {} 197 198 /** 199 * Called every World.tick() 200 */ 201 public void chunkTick() {} 202 203 /** 204 * Save extra data not associated with any Chunk. Not saved during autosave, only during world unload. Currently 205 * unused. 206 */ 207 public void saveExtraData() {} 208 209 /** 210 * Writes the Chunk passed as an argument to the NBTTagCompound also passed, using the World argument to retrieve 211 * the Chunk's last update time. 212 */ 213 private void writeChunkToNBT(Chunk par1Chunk, World par2World, NBTTagCompound par3NBTTagCompound) 214 { 215 par3NBTTagCompound.setInteger("xPos", par1Chunk.xPosition); 216 par3NBTTagCompound.setInteger("zPos", par1Chunk.zPosition); 217 par3NBTTagCompound.setLong("LastUpdate", par2World.getTotalWorldTime()); 218 par3NBTTagCompound.setIntArray("HeightMap", par1Chunk.heightMap); 219 par3NBTTagCompound.setBoolean("TerrainPopulated", par1Chunk.isTerrainPopulated); 220 ExtendedBlockStorage[] var4 = par1Chunk.getBlockStorageArray(); 221 NBTTagList var5 = new NBTTagList("Sections"); 222 ExtendedBlockStorage[] var6 = var4; 223 int var7 = var4.length; 224 NBTTagCompound var10; 225 226 for (int var8 = 0; var8 < var7; ++var8) 227 { 228 ExtendedBlockStorage var9 = var6[var8]; 229 230 if (var9 != null) 231 { 232 var10 = new NBTTagCompound(); 233 var10.setByte("Y", (byte)(var9.getYLocation() >> 4 & 255)); 234 var10.setByteArray("Blocks", var9.getBlockLSBArray()); 235 236 if (var9.getBlockMSBArray() != null) 237 { 238 var10.setByteArray("Add", var9.getBlockMSBArray().data); 239 } 240 241 var10.setByteArray("Data", var9.getMetadataArray().data); 242 var10.setByteArray("SkyLight", var9.getSkylightArray().data); 243 var10.setByteArray("BlockLight", var9.getBlocklightArray().data); 244 var5.appendTag(var10); 245 } 246 } 247 248 par3NBTTagCompound.setTag("Sections", var5); 249 par3NBTTagCompound.setByteArray("Biomes", par1Chunk.getBiomeArray()); 250 par1Chunk.hasEntities = false; 251 NBTTagList var15 = new NBTTagList(); 252 Iterator var17; 253 254 for (var7 = 0; var7 < par1Chunk.entityLists.length; ++var7) 255 { 256 var17 = par1Chunk.entityLists[var7].iterator(); 257 258 while (var17.hasNext()) 259 { 260 Entity var19 = (Entity)var17.next(); 261 par1Chunk.hasEntities = true; 262 var10 = new NBTTagCompound(); 263 264 try 265 { 266 if (var19.addEntityID(var10)) 267 { 268 var15.appendTag(var10); 269 } 270 } 271 catch (Exception e) 272 { 273 FMLLog.log(Level.SEVERE, e, 274 "An Entity type %s has thrown an exception trying to write state. It will not persist. Report this to the mod author", 275 var19.getClass().getName()); 276 } 277 } 278 } 279 280 par3NBTTagCompound.setTag("Entities", var15); 281 NBTTagList var16 = new NBTTagList(); 282 var17 = par1Chunk.chunkTileEntityMap.values().iterator(); 283 284 while (var17.hasNext()) 285 { 286 TileEntity var21 = (TileEntity)var17.next(); 287 var10 = new NBTTagCompound(); 288 try 289 { 290 var21.writeToNBT(var10); 291 var16.appendTag(var10); 292 } 293 catch (Exception e) 294 { 295 FMLLog.log(Level.SEVERE, e, 296 "A TileEntity type %s has throw an exception trying to write state. It will not persist. Report this to the mod author", 297 var21.getClass().getName()); 298 } 299 } 300 301 par3NBTTagCompound.setTag("TileEntities", var16); 302 List var18 = par2World.getPendingBlockUpdates(par1Chunk, false); 303 304 if (var18 != null) 305 { 306 long var20 = par2World.getTotalWorldTime(); 307 NBTTagList var11 = new NBTTagList(); 308 Iterator var12 = var18.iterator(); 309 310 while (var12.hasNext()) 311 { 312 NextTickListEntry var13 = (NextTickListEntry)var12.next(); 313 NBTTagCompound var14 = new NBTTagCompound(); 314 var14.setInteger("i", var13.blockID); 315 var14.setInteger("x", var13.xCoord); 316 var14.setInteger("y", var13.yCoord); 317 var14.setInteger("z", var13.zCoord); 318 var14.setInteger("t", (int)(var13.scheduledTime - var20)); 319 var11.appendTag(var14); 320 } 321 322 par3NBTTagCompound.setTag("TileTicks", var11); 323 } 324 } 325 326 /** 327 * Reads the data stored in the passed NBTTagCompound and creates a Chunk with that data in the passed World. 328 * Returns the created Chunk. 329 */ 330 private Chunk readChunkFromNBT(World par1World, NBTTagCompound par2NBTTagCompound) 331 { 332 int var3 = par2NBTTagCompound.getInteger("xPos"); 333 int var4 = par2NBTTagCompound.getInteger("zPos"); 334 Chunk var5 = new Chunk(par1World, var3, var4); 335 var5.heightMap = par2NBTTagCompound.getIntArray("HeightMap"); 336 var5.isTerrainPopulated = par2NBTTagCompound.getBoolean("TerrainPopulated"); 337 NBTTagList var6 = par2NBTTagCompound.getTagList("Sections"); 338 byte var7 = 16; 339 ExtendedBlockStorage[] var8 = new ExtendedBlockStorage[var7]; 340 341 for (int var9 = 0; var9 < var6.tagCount(); ++var9) 342 { 343 NBTTagCompound var10 = (NBTTagCompound)var6.tagAt(var9); 344 byte var11 = var10.getByte("Y"); 345 ExtendedBlockStorage var12 = new ExtendedBlockStorage(var11 << 4); 346 var12.setBlockLSBArray(var10.getByteArray("Blocks")); 347 348 if (var10.hasKey("Add")) 349 { 350 var12.setBlockMSBArray(new NibbleArray(var10.getByteArray("Add"), 4)); 351 } 352 353 var12.setBlockMetadataArray(new NibbleArray(var10.getByteArray("Data"), 4)); 354 var12.setSkylightArray(new NibbleArray(var10.getByteArray("SkyLight"), 4)); 355 var12.setBlocklightArray(new NibbleArray(var10.getByteArray("BlockLight"), 4)); 356 var12.removeInvalidBlocks(); 357 var8[var11] = var12; 358 } 359 360 var5.setStorageArrays(var8); 361 362 if (par2NBTTagCompound.hasKey("Biomes")) 363 { 364 var5.setBiomeArray(par2NBTTagCompound.getByteArray("Biomes")); 365 } 366 367 NBTTagList var14 = par2NBTTagCompound.getTagList("Entities"); 368 369 if (var14 != null) 370 { 371 for (int var17 = 0; var17 < var14.tagCount(); ++var17) 372 { 373 NBTTagCompound var16 = (NBTTagCompound)var14.tagAt(var17); 374 Entity var18 = EntityList.createEntityFromNBT(var16, par1World); 375 var5.hasEntities = true; 376 377 if (var18 != null) 378 { 379 var5.addEntity(var18); 380 } 381 } 382 } 383 384 NBTTagList var15 = par2NBTTagCompound.getTagList("TileEntities"); 385 386 if (var15 != null) 387 { 388 for (int var21 = 0; var21 < var15.tagCount(); ++var21) 389 { 390 NBTTagCompound var20 = (NBTTagCompound)var15.tagAt(var21); 391 TileEntity var13 = TileEntity.createAndLoadEntity(var20); 392 393 if (var13 != null) 394 { 395 var5.addTileEntity(var13); 396 } 397 } 398 } 399 400 if (par2NBTTagCompound.hasKey("TileTicks")) 401 { 402 NBTTagList var19 = par2NBTTagCompound.getTagList("TileTicks"); 403 404 if (var19 != null) 405 { 406 for (int var22 = 0; var22 < var19.tagCount(); ++var22) 407 { 408 NBTTagCompound var23 = (NBTTagCompound)var19.tagAt(var22); 409 par1World.scheduleBlockUpdateFromLoad(var23.getInteger("x"), var23.getInteger("y"), var23.getInteger("z"), var23.getInteger("i"), var23.getInteger("t")); 410 } 411 } 412 } 413 414 return var5; 415 } 416 }