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