001 package net.minecraft.src; 002 003 import java.io.IOException; 004 import java.util.ArrayList; 005 import java.util.HashSet; 006 import java.util.Iterator; 007 import java.util.List; 008 import java.util.Set; 009 010 import net.minecraftforge.common.DimensionManager; 011 import net.minecraftforge.common.ForgeChunkManager; 012 013 import cpw.mods.fml.common.registry.GameRegistry; 014 015 public class ChunkProviderServer implements IChunkProvider 016 { 017 /** 018 * used by unload100OldestChunks to iterate the loadedChunkHashMap for unload (underlying assumption, first in, 019 * first out) 020 */ 021 private Set chunksToUnload = new HashSet(); 022 private Chunk defaultEmptyChunk; 023 private IChunkProvider currentChunkProvider; 024 IChunkLoader currentChunkLoader; 025 026 /** 027 * if this is false, the defaultEmptyChunk will be returned by the provider 028 */ 029 public boolean loadChunkOnProvideRequest = true; 030 private LongHashMap loadedChunkHashMap = new LongHashMap(); 031 private List loadedChunks = new ArrayList(); 032 private WorldServer currentServer; 033 034 public ChunkProviderServer(WorldServer par1WorldServer, IChunkLoader par2IChunkLoader, IChunkProvider par3IChunkProvider) 035 { 036 this.defaultEmptyChunk = new EmptyChunk(par1WorldServer, 0, 0); 037 this.currentServer = par1WorldServer; 038 this.currentChunkLoader = par2IChunkLoader; 039 this.currentChunkProvider = par3IChunkProvider; 040 } 041 042 /** 043 * Checks to see if a chunk exists at x, y 044 */ 045 public boolean chunkExists(int par1, int par2) 046 { 047 return this.loadedChunkHashMap.containsItem(ChunkCoordIntPair.chunkXZ2Int(par1, par2)); 048 } 049 050 /** 051 * marks chunk for unload by "unload100OldestChunks" if there is no spawn point, or if the center of the chunk is 052 * outside 200 blocks (x or z) of the spawn 053 */ 054 public void unloadChunksIfNotNearSpawn(int par1, int par2) 055 { 056 if (this.currentServer.provider.canRespawnHere() && DimensionManager.shouldLoadSpawn(currentServer.provider.dimensionId)) 057 { 058 ChunkCoordinates var3 = this.currentServer.getSpawnPoint(); 059 int var4 = par1 * 16 + 8 - var3.posX; 060 int var5 = par2 * 16 + 8 - var3.posZ; 061 short var6 = 128; 062 063 if (var4 < -var6 || var4 > var6 || var5 < -var6 || var5 > var6) 064 { 065 this.chunksToUnload.add(Long.valueOf(ChunkCoordIntPair.chunkXZ2Int(par1, par2))); 066 } 067 } 068 else 069 { 070 this.chunksToUnload.add(Long.valueOf(ChunkCoordIntPair.chunkXZ2Int(par1, par2))); 071 } 072 } 073 074 /** 075 * marks all chunks for unload, ignoring those near the spawn 076 */ 077 public void unloadAllChunks() 078 { 079 Iterator var1 = this.loadedChunks.iterator(); 080 081 while (var1.hasNext()) 082 { 083 Chunk var2 = (Chunk)var1.next(); 084 this.unloadChunksIfNotNearSpawn(var2.xPosition, var2.zPosition); 085 } 086 } 087 088 /** 089 * loads or generates the chunk at the chunk location specified 090 */ 091 public Chunk loadChunk(int par1, int par2) 092 { 093 long var3 = ChunkCoordIntPair.chunkXZ2Int(par1, par2); 094 this.chunksToUnload.remove(Long.valueOf(var3)); 095 Chunk var5 = (Chunk)this.loadedChunkHashMap.getValueByKey(var3); 096 097 if (var5 == null) 098 { 099 var5 = ForgeChunkManager.fetchDormantChunk(var3, currentServer); 100 if (var5 == null) 101 { 102 var5 = this.safeLoadChunk(par1, par2); 103 } 104 105 if (var5 == null) 106 { 107 if (this.currentChunkProvider == null) 108 { 109 var5 = this.defaultEmptyChunk; 110 } 111 else 112 { 113 try 114 { 115 var5 = this.currentChunkProvider.provideChunk(par1, par2); 116 } 117 catch (Throwable var9) 118 { 119 CrashReport var7 = CrashReport.func_85055_a(var9, "Exception generating new chunk"); 120 CrashReportCategory var8 = var7.func_85058_a("Chunk to be generated"); 121 var8.addCrashSection("Location", String.format("%d,%d", new Object[] {Integer.valueOf(par1), Integer.valueOf(par2)})); 122 var8.addCrashSection("Position hash", Long.valueOf(var3)); 123 var8.addCrashSection("Generator", this.currentChunkProvider.makeString()); 124 throw new ReportedException(var7); 125 } 126 } 127 } 128 129 this.loadedChunkHashMap.add(var3, var5); 130 this.loadedChunks.add(var5); 131 132 if (var5 != null) 133 { 134 var5.onChunkLoad(); 135 } 136 137 var5.populateChunk(this, this, par1, par2); 138 } 139 140 return var5; 141 } 142 143 /** 144 * Will return back a chunk, if it doesn't exist and its not a MP client it will generates all the blocks for the 145 * specified chunk from the map seed and chunk seed 146 */ 147 public Chunk provideChunk(int par1, int par2) 148 { 149 Chunk var3 = (Chunk)this.loadedChunkHashMap.getValueByKey(ChunkCoordIntPair.chunkXZ2Int(par1, par2)); 150 return var3 == null ? (!this.currentServer.findingSpawnPoint && !this.loadChunkOnProvideRequest ? this.defaultEmptyChunk : this.loadChunk(par1, par2)) : var3; 151 } 152 153 /** 154 * used by loadChunk, but catches any exceptions if the load fails. 155 */ 156 private Chunk safeLoadChunk(int par1, int par2) 157 { 158 if (this.currentChunkLoader == null) 159 { 160 return null; 161 } 162 else 163 { 164 try 165 { 166 Chunk var3 = this.currentChunkLoader.loadChunk(this.currentServer, par1, par2); 167 168 if (var3 != null) 169 { 170 var3.lastSaveTime = this.currentServer.getTotalWorldTime(); 171 172 if (this.currentChunkProvider != null) 173 { 174 this.currentChunkProvider.func_82695_e(par1, par2); 175 } 176 } 177 178 return var3; 179 } 180 catch (Exception var4) 181 { 182 var4.printStackTrace(); 183 return null; 184 } 185 } 186 } 187 188 /** 189 * used by saveChunks, but catches any exceptions if the save fails. 190 */ 191 private void safeSaveExtraChunkData(Chunk par1Chunk) 192 { 193 if (this.currentChunkLoader != null) 194 { 195 try 196 { 197 this.currentChunkLoader.saveExtraChunkData(this.currentServer, par1Chunk); 198 } 199 catch (Exception var3) 200 { 201 var3.printStackTrace(); 202 } 203 } 204 } 205 206 /** 207 * used by saveChunks, but catches any exceptions if the save fails. 208 */ 209 private void safeSaveChunk(Chunk par1Chunk) 210 { 211 if (this.currentChunkLoader != null) 212 { 213 try 214 { 215 par1Chunk.lastSaveTime = this.currentServer.getTotalWorldTime(); 216 this.currentChunkLoader.saveChunk(this.currentServer, par1Chunk); 217 } 218 catch (IOException var3) 219 { 220 var3.printStackTrace(); 221 } 222 catch (MinecraftException var4) 223 { 224 var4.printStackTrace(); 225 } 226 } 227 } 228 229 /** 230 * Populates chunk with ores etc etc 231 */ 232 public void populate(IChunkProvider par1IChunkProvider, int par2, int par3) 233 { 234 Chunk var4 = this.provideChunk(par2, par3); 235 236 if (!var4.isTerrainPopulated) 237 { 238 var4.isTerrainPopulated = true; 239 240 if (this.currentChunkProvider != null) 241 { 242 this.currentChunkProvider.populate(par1IChunkProvider, par2, par3); 243 GameRegistry.generateWorld(par2, par3, currentServer, currentChunkProvider, par1IChunkProvider); 244 var4.setChunkModified(); 245 } 246 } 247 } 248 249 /** 250 * Two modes of operation: if passed true, save all Chunks in one go. If passed false, save up to two chunks. 251 * Return true if all chunks have been saved. 252 */ 253 public boolean saveChunks(boolean par1, IProgressUpdate par2IProgressUpdate) 254 { 255 int var3 = 0; 256 257 for (int var4 = 0; var4 < this.loadedChunks.size(); ++var4) 258 { 259 Chunk var5 = (Chunk)this.loadedChunks.get(var4); 260 261 if (par1) 262 { 263 this.safeSaveExtraChunkData(var5); 264 } 265 266 if (var5.needsSaving(par1)) 267 { 268 this.safeSaveChunk(var5); 269 var5.isModified = false; 270 ++var3; 271 272 if (var3 == 24 && !par1) 273 { 274 return false; 275 } 276 } 277 } 278 279 if (par1) 280 { 281 if (this.currentChunkLoader == null) 282 { 283 return true; 284 } 285 286 this.currentChunkLoader.saveExtraData(); 287 } 288 289 return true; 290 } 291 292 /** 293 * Unloads the 100 oldest chunks from memory, due to a bug with chunkSet.add() never being called it thinks the list 294 * is always empty and will not remove any chunks. 295 */ 296 public boolean unload100OldestChunks() 297 { 298 if (!this.currentServer.canNotSave) 299 { 300 for (ChunkCoordIntPair forced : currentServer.getPersistentChunks().keySet()) 301 { 302 this.chunksToUnload.remove(ChunkCoordIntPair.chunkXZ2Int(forced.chunkXPos, forced.chunkZPos)); 303 } 304 305 for (int var1 = 0; var1 < 100; ++var1) 306 { 307 if (!this.chunksToUnload.isEmpty()) 308 { 309 Long var2 = (Long)this.chunksToUnload.iterator().next(); 310 Chunk var3 = (Chunk)this.loadedChunkHashMap.getValueByKey(var2.longValue()); 311 var3.onChunkUnload(); 312 this.safeSaveChunk(var3); 313 this.safeSaveExtraChunkData(var3); 314 this.chunksToUnload.remove(var2); 315 this.loadedChunkHashMap.remove(var2.longValue()); 316 this.loadedChunks.remove(var3); 317 ForgeChunkManager.putDormantChunk(ChunkCoordIntPair.chunkXZ2Int(var3.xPosition, var3.zPosition), var3); 318 if(loadedChunks.size() == 0 && ForgeChunkManager.getPersistentChunksFor(currentServer).size() == 0 && !DimensionManager.shouldLoadSpawn(currentServer.provider.dimensionId)) { 319 DimensionManager.unloadWorld(currentServer.provider.dimensionId); 320 return currentChunkProvider.unload100OldestChunks(); 321 } 322 } 323 } 324 325 if (this.currentChunkLoader != null) 326 { 327 this.currentChunkLoader.chunkTick(); 328 } 329 } 330 331 return this.currentChunkProvider.unload100OldestChunks(); 332 } 333 334 /** 335 * Returns if the IChunkProvider supports saving. 336 */ 337 public boolean canSave() 338 { 339 return !this.currentServer.canNotSave; 340 } 341 342 /** 343 * Converts the instance data to a readable string. 344 */ 345 public String makeString() 346 { 347 return "ServerChunkCache: " + this.loadedChunkHashMap.getNumHashElements() + " Drop: " + this.chunksToUnload.size(); 348 } 349 350 /** 351 * Returns a list of creatures of the specified type that can spawn at the given location. 352 */ 353 public List getPossibleCreatures(EnumCreatureType par1EnumCreatureType, int par2, int par3, int par4) 354 { 355 return this.currentChunkProvider.getPossibleCreatures(par1EnumCreatureType, par2, par3, par4); 356 } 357 358 /** 359 * Returns the location of the closest structure of the specified type. If not found returns null. 360 */ 361 public ChunkPosition findClosestStructure(World par1World, String par2Str, int par3, int par4, int par5) 362 { 363 return this.currentChunkProvider.findClosestStructure(par1World, par2Str, par3, par4, par5); 364 } 365 366 public int getLoadedChunkCount() 367 { 368 return this.loadedChunkHashMap.getNumHashElements(); 369 } 370 371 public void func_82695_e(int par1, int par2) {} 372 }