001 package net.minecraft.src; 002 003 import cpw.mods.fml.common.Side; 004 import cpw.mods.fml.common.asm.SideOnly; 005 import java.util.HashSet; 006 import java.util.Iterator; 007 import java.util.Random; 008 import java.util.Set; 009 import net.minecraft.client.Minecraft; 010 import net.minecraftforge.common.MinecraftForge; 011 import net.minecraftforge.event.world.WorldEvent; 012 013 @SideOnly(Side.CLIENT) 014 public class WorldClient extends World 015 { 016 /** The packets that need to be sent to the server. */ 017 private NetClientHandler sendQueue; 018 019 /** The ChunkProviderClient instance */ 020 private ChunkProviderClient clientChunkProvider; 021 022 /** 023 * The hash set of entities handled by this client. Uses the entity's ID as the hash set's key. 024 */ 025 private IntHashMap entityHashSet = new IntHashMap(); 026 027 /** Contains all entities for this client, both spawned and non-spawned. */ 028 private Set entityList = new HashSet(); 029 030 /** 031 * Contains all entities for this client that were not spawned due to a non-present chunk. The game will attempt to 032 * spawn up to 10 pending entities with each subsequent tick until the spawn queue is empty. 033 */ 034 private Set entitySpawnQueue = new HashSet(); 035 private final Minecraft mc = Minecraft.getMinecraft(); 036 private final Set previousActiveChunkSet = new HashSet(); 037 038 public WorldClient(NetClientHandler par1NetClientHandler, WorldSettings par2WorldSettings, int par3, int par4, Profiler par5Profiler) 039 { 040 super(new SaveHandlerMP(), "MpServer", WorldProvider.getProviderForDimension(par3), par2WorldSettings, par5Profiler); 041 this.sendQueue = par1NetClientHandler; 042 this.difficultySetting = par4; 043 this.mapStorage = par1NetClientHandler.mapStorage; 044 this.isRemote = true; 045 finishSetup(); 046 this.setSpawnLocation(8, 64, 8); 047 MinecraftForge.EVENT_BUS.post(new WorldEvent.Load(this)); 048 } 049 050 /** 051 * Runs a single tick for the world 052 */ 053 public void tick() 054 { 055 super.tick(); 056 this.setWorldTime(this.getWorldTime() + 1L); 057 this.theProfiler.startSection("reEntryProcessing"); 058 059 for (int var1 = 0; var1 < 10 && !this.entitySpawnQueue.isEmpty(); ++var1) 060 { 061 Entity var2 = (Entity)this.entitySpawnQueue.iterator().next(); 062 this.entitySpawnQueue.remove(var2); 063 064 if (!this.loadedEntityList.contains(var2)) 065 { 066 this.spawnEntityInWorld(var2); 067 } 068 } 069 070 this.theProfiler.endStartSection("connection"); 071 this.sendQueue.processReadPackets(); 072 this.theProfiler.endStartSection("chunkCache"); 073 this.clientChunkProvider.unload100OldestChunks(); 074 this.theProfiler.endStartSection("tiles"); 075 this.tickBlocksAndAmbiance(); 076 this.theProfiler.endSection(); 077 } 078 079 /** 080 * Invalidates an AABB region of blocks from the receive queue, in the event that the block has been modified 081 * client-side in the intervening 80 receive ticks. 082 */ 083 public void invalidateBlockReceiveRegion(int par1, int par2, int par3, int par4, int par5, int par6) {} 084 085 /** 086 * Creates the chunk provider for this world. Called in the constructor. Retrieves provider from worldProvider? 087 */ 088 protected IChunkProvider createChunkProvider() 089 { 090 this.clientChunkProvider = new ChunkProviderClient(this); 091 return this.clientChunkProvider; 092 } 093 094 /** 095 * plays random cave ambient sounds and runs updateTick on random blocks within each chunk in the vacinity of a 096 * player 097 */ 098 protected void tickBlocksAndAmbiance() 099 { 100 super.tickBlocksAndAmbiance(); 101 this.previousActiveChunkSet.retainAll(this.activeChunkSet); 102 103 if (this.previousActiveChunkSet.size() == this.activeChunkSet.size()) 104 { 105 this.previousActiveChunkSet.clear(); 106 } 107 108 int var1 = 0; 109 Iterator var2 = this.activeChunkSet.iterator(); 110 111 while (var2.hasNext()) 112 { 113 ChunkCoordIntPair var3 = (ChunkCoordIntPair)var2.next(); 114 115 if (!this.previousActiveChunkSet.contains(var3)) 116 { 117 int var4 = var3.chunkXPos * 16; 118 int var5 = var3.chunkZPos * 16; 119 this.theProfiler.startSection("getChunk"); 120 Chunk var6 = this.getChunkFromChunkCoords(var3.chunkXPos, var3.chunkZPos); 121 this.moodSoundAndLightCheck(var4, var5, var6); 122 this.theProfiler.endSection(); 123 this.previousActiveChunkSet.add(var3); 124 ++var1; 125 126 if (var1 >= 10) 127 { 128 return; 129 } 130 } 131 } 132 } 133 134 public void doPreChunk(int par1, int par2, boolean par3) 135 { 136 if (par3) 137 { 138 this.clientChunkProvider.loadChunk(par1, par2); 139 } 140 else 141 { 142 this.clientChunkProvider.unloadChunk(par1, par2); 143 } 144 145 if (!par3) 146 { 147 this.markBlocksDirty(par1 * 16, 0, par2 * 16, par1 * 16 + 15, 256, par2 * 16 + 15); 148 } 149 } 150 151 /** 152 * Called to place all entities as part of a world 153 */ 154 public boolean spawnEntityInWorld(Entity par1Entity) 155 { 156 boolean var2 = super.spawnEntityInWorld(par1Entity); 157 this.entityList.add(par1Entity); 158 159 if (!var2) 160 { 161 this.entitySpawnQueue.add(par1Entity); 162 } 163 164 return var2; 165 } 166 167 /** 168 * Dismounts the entity (and anything riding the entity), sets the dead flag, and removes the player entity from the 169 * player entity list. Called by the playerLoggedOut function. 170 */ 171 public void setEntityDead(Entity par1Entity) 172 { 173 super.setEntityDead(par1Entity); 174 this.entityList.remove(par1Entity); 175 } 176 177 /** 178 * Start the skin for this entity downloading, if necessary, and increment its reference counter 179 */ 180 protected void obtainEntitySkin(Entity par1Entity) 181 { 182 super.obtainEntitySkin(par1Entity); 183 184 if (this.entitySpawnQueue.contains(par1Entity)) 185 { 186 this.entitySpawnQueue.remove(par1Entity); 187 } 188 } 189 190 /** 191 * Decrement the reference counter for this entity's skin image data 192 */ 193 protected void releaseEntitySkin(Entity par1Entity) 194 { 195 super.releaseEntitySkin(par1Entity); 196 197 if (this.entityList.contains(par1Entity)) 198 { 199 if (par1Entity.isEntityAlive()) 200 { 201 this.entitySpawnQueue.add(par1Entity); 202 } 203 else 204 { 205 this.entityList.remove(par1Entity); 206 } 207 } 208 } 209 210 /** 211 * Add an ID to Entity mapping to entityHashSet 212 */ 213 public void addEntityToWorld(int par1, Entity par2Entity) 214 { 215 Entity var3 = this.getEntityByID(par1); 216 217 if (var3 != null) 218 { 219 this.setEntityDead(var3); 220 } 221 222 this.entityList.add(par2Entity); 223 par2Entity.entityId = par1; 224 225 if (!this.spawnEntityInWorld(par2Entity)) 226 { 227 this.entitySpawnQueue.add(par2Entity); 228 } 229 230 this.entityHashSet.addKey(par1, par2Entity); 231 } 232 233 /** 234 * Lookup and return an Entity based on its ID 235 */ 236 public Entity getEntityByID(int par1) 237 { 238 return (Entity)this.entityHashSet.lookup(par1); 239 } 240 241 public Entity removeEntityFromWorld(int par1) 242 { 243 Entity var2 = (Entity)this.entityHashSet.removeObject(par1); 244 245 if (var2 != null) 246 { 247 this.entityList.remove(var2); 248 this.setEntityDead(var2); 249 } 250 251 return var2; 252 } 253 254 public boolean setBlockAndMetadataAndInvalidate(int par1, int par2, int par3, int par4, int par5) 255 { 256 this.invalidateBlockReceiveRegion(par1, par2, par3, par1, par2, par3); 257 return super.setBlockAndMetadataWithNotify(par1, par2, par3, par4, par5); 258 } 259 260 /** 261 * If on MP, sends a quitting packet. 262 */ 263 public void sendQuittingDisconnectingPacket() 264 { 265 this.sendQueue.quitWithPacket(new Packet255KickDisconnect("Quitting")); 266 } 267 268 /** 269 * Updates all weather states. 270 */ 271 protected void updateWeather() 272 { 273 super.updateWeather(); 274 } 275 276 @Override 277 public void updateWeatherBody() 278 { 279 if (!this.provider.hasNoSky) 280 { 281 if (this.lastLightningBolt > 0) 282 { 283 --this.lastLightningBolt; 284 } 285 286 this.prevRainingStrength = this.rainingStrength; 287 288 if (this.worldInfo.isRaining()) 289 { 290 this.rainingStrength = (float)((double)this.rainingStrength + 0.01D); 291 } 292 else 293 { 294 this.rainingStrength = (float)((double)this.rainingStrength - 0.01D); 295 } 296 297 if (this.rainingStrength < 0.0F) 298 { 299 this.rainingStrength = 0.0F; 300 } 301 302 if (this.rainingStrength > 1.0F) 303 { 304 this.rainingStrength = 1.0F; 305 } 306 307 this.prevThunderingStrength = this.thunderingStrength; 308 309 if (this.worldInfo.isThundering()) 310 { 311 this.thunderingStrength = (float)((double)this.thunderingStrength + 0.01D); 312 } 313 else 314 { 315 this.thunderingStrength = (float)((double)this.thunderingStrength - 0.01D); 316 } 317 318 if (this.thunderingStrength < 0.0F) 319 { 320 this.thunderingStrength = 0.0F; 321 } 322 323 if (this.thunderingStrength > 1.0F) 324 { 325 this.thunderingStrength = 1.0F; 326 } 327 } 328 } 329 330 public void func_73029_E(int par1, int par2, int par3) 331 { 332 byte var4 = 16; 333 Random var5 = new Random(); 334 335 for (int var6 = 0; var6 < 1000; ++var6) 336 { 337 int var7 = par1 + this.rand.nextInt(var4) - this.rand.nextInt(var4); 338 int var8 = par2 + this.rand.nextInt(var4) - this.rand.nextInt(var4); 339 int var9 = par3 + this.rand.nextInt(var4) - this.rand.nextInt(var4); 340 int var10 = this.getBlockId(var7, var8, var9); 341 342 if (var10 == 0 && this.rand.nextInt(8) > var8 && this.provider.getWorldHasVoidParticles()) 343 { 344 this.spawnParticle("depthsuspend", (double)((float)var7 + this.rand.nextFloat()), (double)((float)var8 + this.rand.nextFloat()), (double)((float)var9 + this.rand.nextFloat()), 0.0D, 0.0D, 0.0D); 345 } 346 else if (var10 > 0) 347 { 348 Block.blocksList[var10].randomDisplayTick(this, var7, var8, var9, var5); 349 } 350 } 351 } 352 353 /** 354 * also releases skins. 355 */ 356 public void removeAllEntities() 357 { 358 this.loadedEntityList.removeAll(this.unloadedEntityList); 359 int var1; 360 Entity var2; 361 int var3; 362 int var4; 363 364 for (var1 = 0; var1 < this.unloadedEntityList.size(); ++var1) 365 { 366 var2 = (Entity)this.unloadedEntityList.get(var1); 367 var3 = var2.chunkCoordX; 368 var4 = var2.chunkCoordZ; 369 370 if (var2.addedToChunk && this.chunkExists(var3, var4)) 371 { 372 this.getChunkFromChunkCoords(var3, var4).removeEntity(var2); 373 } 374 } 375 376 for (var1 = 0; var1 < this.unloadedEntityList.size(); ++var1) 377 { 378 this.releaseEntitySkin((Entity)this.unloadedEntityList.get(var1)); 379 } 380 381 this.unloadedEntityList.clear(); 382 383 for (var1 = 0; var1 < this.loadedEntityList.size(); ++var1) 384 { 385 var2 = (Entity)this.loadedEntityList.get(var1); 386 387 if (var2.ridingEntity != null) 388 { 389 if (!var2.ridingEntity.isDead && var2.ridingEntity.riddenByEntity == var2) 390 { 391 continue; 392 } 393 394 var2.ridingEntity.riddenByEntity = null; 395 var2.ridingEntity = null; 396 } 397 398 if (var2.isDead) 399 { 400 var3 = var2.chunkCoordX; 401 var4 = var2.chunkCoordZ; 402 403 if (var2.addedToChunk && this.chunkExists(var3, var4)) 404 { 405 this.getChunkFromChunkCoords(var3, var4).removeEntity(var2); 406 } 407 408 this.loadedEntityList.remove(var1--); 409 this.releaseEntitySkin(var2); 410 } 411 } 412 } 413 414 /** 415 * Adds some basic stats of the world to the given crash report. 416 */ 417 public CrashReport addWorldInfoToCrashReport(CrashReport par1CrashReport) 418 { 419 par1CrashReport = super.addWorldInfoToCrashReport(par1CrashReport); 420 par1CrashReport.addCrashSectionCallable("Forced Entities", new CallableMPL1(this)); 421 par1CrashReport.addCrashSectionCallable("Retry Entities", new CallableMPL2(this)); 422 return par1CrashReport; 423 } 424 425 /** 426 * par8 is loudness, all pars passed to minecraftInstance.sndManager.playSound 427 */ 428 public void playSound(double par1, double par3, double par5, String par7Str, float par8, float par9) 429 { 430 float var10 = 16.0F; 431 432 if (par8 > 1.0F) 433 { 434 var10 *= par8; 435 } 436 437 if (this.mc.renderViewEntity.getDistanceSq(par1, par3, par5) < (double)(var10 * var10)) 438 { 439 this.mc.sndManager.playSound(par7Str, (float)par1, (float)par3, (float)par5, par8, par9); 440 } 441 } 442 443 static Set getEntityList(WorldClient par0WorldClient) 444 { 445 return par0WorldClient.entityList; 446 } 447 448 static Set getEntitySpawnQueue(WorldClient par0WorldClient) 449 { 450 return par0WorldClient.entitySpawnQueue; 451 } 452 }