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