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