001 package net.minecraft.src; 002 003 import cpw.mods.fml.common.Side; 004 import cpw.mods.fml.common.asm.SideOnly; 005 006 import java.io.File; 007 import java.util.ArrayList; 008 import java.util.HashSet; 009 import java.util.Iterator; 010 import java.util.List; 011 import java.util.Random; 012 import java.util.Set; 013 import java.util.TreeSet; 014 import net.minecraft.server.MinecraftServer; 015 import net.minecraftforge.common.ChestGenHooks; 016 import static net.minecraftforge.common.ChestGenHooks.*; 017 import net.minecraftforge.common.DimensionManager; 018 import net.minecraftforge.common.MinecraftForge; 019 import net.minecraftforge.event.world.WorldEvent; 020 021 public class WorldServer extends World 022 { 023 private final MinecraftServer mcServer; 024 private final EntityTracker theEntityTracker; 025 private final PlayerManager thePlayerManager; 026 private Set field_73064_N; 027 028 /** All work to do in future ticks. */ 029 private TreeSet pendingTickListEntries; 030 public ChunkProviderServer theChunkProviderServer; 031 032 /** 033 * this is set related to Manager.areCommandsAllowed, but is never used is is also set back to false at the end of 034 * both functions which set it. 035 */ 036 public boolean actionsAllowed = false; 037 038 /** set by CommandServerSave{all,Off,On} */ 039 public boolean canNotSave; 040 041 /** is false if there are no players */ 042 private boolean allPlayersSleeping; 043 private int updateEntityTick = 0; 044 045 /** 046 * Double buffer of ServerBlockEventList[] for holding pending BlockEventData's 047 */ 048 private ServerBlockEventList[] blockEventCache = new ServerBlockEventList[] {new ServerBlockEventList((ServerBlockEvent)null), new ServerBlockEventList((ServerBlockEvent)null)}; 049 050 /** 051 * The index into the blockEventCache; either 0, or 1, toggled in sendBlockEventPackets where all BlockEvent are 052 * applied locally and send to clients. 053 */ 054 private int blockEventCacheIndex = 0; 055 public static final WeightedRandomChestContent[] bonusChestContent = new WeightedRandomChestContent[] {new WeightedRandomChestContent(Item.stick.shiftedIndex, 0, 1, 3, 10), new WeightedRandomChestContent(Block.planks.blockID, 0, 1, 3, 10), new WeightedRandomChestContent(Block.wood.blockID, 0, 1, 3, 10), new WeightedRandomChestContent(Item.axeStone.shiftedIndex, 0, 1, 1, 3), new WeightedRandomChestContent(Item.axeWood.shiftedIndex, 0, 1, 1, 5), new WeightedRandomChestContent(Item.pickaxeStone.shiftedIndex, 0, 1, 1, 3), new WeightedRandomChestContent(Item.pickaxeWood.shiftedIndex, 0, 1, 1, 5), new WeightedRandomChestContent(Item.appleRed.shiftedIndex, 0, 2, 3, 5), new WeightedRandomChestContent(Item.bread.shiftedIndex, 0, 2, 3, 3)}; 056 057 /** An IntHashMap of entity IDs (integers) to their Entity objects. */ 058 private IntHashMap entityIdMap; 059 060 public WorldServer(MinecraftServer par1MinecraftServer, ISaveHandler par2ISaveHandler, String par3Str, int par4, WorldSettings par5WorldSettings, Profiler par6Profiler) 061 { 062 super(par2ISaveHandler, par3Str, par5WorldSettings, WorldProvider.getProviderForDimension(par4), par6Profiler); 063 this.mcServer = par1MinecraftServer; 064 this.theEntityTracker = new EntityTracker(this); 065 this.thePlayerManager = new PlayerManager(this, par1MinecraftServer.getConfigurationManager().getViewDistance()); 066 067 if (this.entityIdMap == null) 068 { 069 this.entityIdMap = new IntHashMap(); 070 } 071 072 if (this.field_73064_N == null) 073 { 074 this.field_73064_N = new HashSet(); 075 } 076 077 if (this.pendingTickListEntries == null) 078 { 079 this.pendingTickListEntries = new TreeSet(); 080 } 081 DimensionManager.setWorld(par4, this); 082 } 083 084 /** 085 * Runs a single tick for the world 086 */ 087 public void tick() 088 { 089 super.tick(); 090 091 if (this.getWorldInfo().isHardcoreModeEnabled() && this.difficultySetting < 3) 092 { 093 this.difficultySetting = 3; 094 } 095 096 this.provider.worldChunkMgr.cleanupCache(); 097 098 if (this.areAllPlayersAsleep()) 099 { 100 boolean var1 = false; 101 102 if (this.spawnHostileMobs && this.difficultySetting >= 1) 103 { 104 ; 105 } 106 107 if (!var1) 108 { 109 long var2 = this.worldInfo.getWorldTime() + 24000L; 110 this.worldInfo.setWorldTime(var2 - var2 % 24000L); 111 this.wakeAllPlayers(); 112 } 113 } 114 115 this.theProfiler.startSection("mobSpawner"); 116 SpawnerAnimals.findChunksForSpawning(this, this.spawnHostileMobs, this.spawnPeacefulMobs && this.worldInfo.getWorldTime() % 400L == 0L); 117 this.theProfiler.endStartSection("chunkSource"); 118 this.chunkProvider.unload100OldestChunks(); 119 int var4 = this.calculateSkylightSubtracted(1.0F); 120 121 if (var4 != this.skylightSubtracted) 122 { 123 this.skylightSubtracted = var4; 124 } 125 126 this.sendAndApplyBlockEvents(); 127 this.worldInfo.setWorldTime(this.worldInfo.getWorldTime() + 1L); 128 this.theProfiler.endStartSection("tickPending"); 129 this.tickUpdates(false); 130 this.theProfiler.endStartSection("tickTiles"); 131 this.tickBlocksAndAmbiance(); 132 this.theProfiler.endStartSection("chunkMap"); 133 this.thePlayerManager.updatePlayerInstances(); 134 this.theProfiler.endStartSection("village"); 135 this.villageCollectionObj.tick(); 136 this.villageSiegeObj.tick(); 137 this.theProfiler.endSection(); 138 this.sendAndApplyBlockEvents(); 139 } 140 141 /** 142 * only spawns creatures allowed by the chunkProvider 143 */ 144 public SpawnListEntry spawnRandomCreature(EnumCreatureType par1EnumCreatureType, int par2, int par3, int par4) 145 { 146 List var5 = this.getChunkProvider().getPossibleCreatures(par1EnumCreatureType, par2, par3, par4); 147 return var5 != null && !var5.isEmpty() ? (SpawnListEntry)WeightedRandom.getRandomItem(this.rand, var5) : null; 148 } 149 150 /** 151 * Updates the flag that indicates whether or not all players in the world are sleeping. 152 */ 153 public void updateAllPlayersSleepingFlag() 154 { 155 this.allPlayersSleeping = !this.playerEntities.isEmpty(); 156 Iterator var1 = this.playerEntities.iterator(); 157 158 while (var1.hasNext()) 159 { 160 EntityPlayer var2 = (EntityPlayer)var1.next(); 161 162 if (!var2.isPlayerSleeping()) 163 { 164 this.allPlayersSleeping = false; 165 break; 166 } 167 } 168 } 169 170 protected void wakeAllPlayers() 171 { 172 this.allPlayersSleeping = false; 173 Iterator var1 = this.playerEntities.iterator(); 174 175 while (var1.hasNext()) 176 { 177 EntityPlayer var2 = (EntityPlayer)var1.next(); 178 179 if (var2.isPlayerSleeping()) 180 { 181 var2.wakeUpPlayer(false, false, true); 182 } 183 } 184 185 this.resetRainAndThunder(); 186 } 187 188 private void resetRainAndThunder() 189 { 190 provider.resetRainAndThunder(); 191 } 192 193 public boolean areAllPlayersAsleep() 194 { 195 if (this.allPlayersSleeping && !this.isRemote) 196 { 197 Iterator var1 = this.playerEntities.iterator(); 198 EntityPlayer var2; 199 200 do 201 { 202 if (!var1.hasNext()) 203 { 204 return true; 205 } 206 207 var2 = (EntityPlayer)var1.next(); 208 } 209 while (var2.isPlayerFullyAsleep()); 210 211 return false; 212 } 213 else 214 { 215 return false; 216 } 217 } 218 219 @SideOnly(Side.CLIENT) 220 221 /** 222 * Sets a new spawn location by finding an uncovered block at a random (x,z) location in the chunk. 223 */ 224 public void setSpawnLocation() 225 { 226 if (this.worldInfo.getSpawnY() <= 0) 227 { 228 this.worldInfo.setSpawnY(64); 229 } 230 231 int var1 = this.worldInfo.getSpawnX(); 232 int var2 = this.worldInfo.getSpawnZ(); 233 int var3 = 0; 234 235 while (this.getFirstUncoveredBlock(var1, var2) == 0) 236 { 237 var1 += this.rand.nextInt(8) - this.rand.nextInt(8); 238 var2 += this.rand.nextInt(8) - this.rand.nextInt(8); 239 ++var3; 240 241 if (var3 == 10000) 242 { 243 break; 244 } 245 } 246 247 this.worldInfo.setSpawnX(var1); 248 this.worldInfo.setSpawnZ(var2); 249 } 250 251 /** 252 * plays random cave ambient sounds and runs updateTick on random blocks within each chunk in the vacinity of a 253 * player 254 */ 255 protected void tickBlocksAndAmbiance() 256 { 257 super.tickBlocksAndAmbiance(); 258 int var1 = 0; 259 int var2 = 0; 260 Iterator var3 = this.activeChunkSet.iterator(); 261 262 while (var3.hasNext()) 263 { 264 ChunkCoordIntPair var4 = (ChunkCoordIntPair)var3.next(); 265 int var5 = var4.chunkXPos * 16; 266 int var6 = var4.chunkZPos * 16; 267 this.theProfiler.startSection("getChunk"); 268 Chunk var7 = this.getChunkFromChunkCoords(var4.chunkXPos, var4.chunkZPos); 269 this.moodSoundAndLightCheck(var5, var6, var7); 270 this.theProfiler.endStartSection("tickChunk"); 271 var7.updateSkylight(); 272 this.theProfiler.endStartSection("thunder"); 273 int var8; 274 int var9; 275 int var10; 276 int var11; 277 278 if (provider.canDoLightning(var7) && this.rand.nextInt(100000) == 0 && this.isRaining() && this.isThundering()) 279 { 280 this.updateLCG = this.updateLCG * 3 + 1013904223; 281 var8 = this.updateLCG >> 2; 282 var9 = var5 + (var8 & 15); 283 var10 = var6 + (var8 >> 8 & 15); 284 var11 = this.getPrecipitationHeight(var9, var10); 285 286 if (this.canLightningStrikeAt(var9, var11, var10)) 287 { 288 this.addWeatherEffect(new EntityLightningBolt(this, (double)var9, (double)var11, (double)var10)); 289 this.lastLightningBolt = 2; 290 } 291 } 292 293 this.theProfiler.endStartSection("iceandsnow"); 294 int var13; 295 296 if (provider.canDoRainSnowIce(var7) && this.rand.nextInt(16) == 0) 297 { 298 this.updateLCG = this.updateLCG * 3 + 1013904223; 299 var8 = this.updateLCG >> 2; 300 var9 = var8 & 15; 301 var10 = var8 >> 8 & 15; 302 var11 = this.getPrecipitationHeight(var9 + var5, var10 + var6); 303 304 if (this.isBlockFreezableNaturally(var9 + var5, var11 - 1, var10 + var6)) 305 { 306 this.setBlockWithNotify(var9 + var5, var11 - 1, var10 + var6, Block.ice.blockID); 307 } 308 309 if (this.isRaining() && this.canSnowAt(var9 + var5, var11, var10 + var6)) 310 { 311 this.setBlockWithNotify(var9 + var5, var11, var10 + var6, Block.snow.blockID); 312 } 313 314 if (this.isRaining()) 315 { 316 BiomeGenBase var12 = this.getBiomeGenForCoords(var9 + var5, var10 + var6); 317 318 if (var12.canSpawnLightningBolt()) 319 { 320 var13 = this.getBlockId(var9 + var5, var11 - 1, var10 + var6); 321 322 if (var13 != 0) 323 { 324 Block.blocksList[var13].fillWithRain(this, var9 + var5, var11 - 1, var10 + var6); 325 } 326 } 327 } 328 } 329 330 this.theProfiler.endStartSection("tickTiles"); 331 ExtendedBlockStorage[] var19 = var7.getBlockStorageArray(); 332 var9 = var19.length; 333 334 for (var10 = 0; var10 < var9; ++var10) 335 { 336 ExtendedBlockStorage var21 = var19[var10]; 337 338 if (var21 != null && var21.getNeedsRandomTick()) 339 { 340 for (int var20 = 0; var20 < 3; ++var20) 341 { 342 this.updateLCG = this.updateLCG * 3 + 1013904223; 343 var13 = this.updateLCG >> 2; 344 int var14 = var13 & 15; 345 int var15 = var13 >> 8 & 15; 346 int var16 = var13 >> 16 & 15; 347 int var17 = var21.getExtBlockID(var14, var16, var15); 348 ++var2; 349 Block var18 = Block.blocksList[var17]; 350 351 if (var18 != null && var18.getTickRandomly()) 352 { 353 ++var1; 354 var18.updateTick(this, var14 + var5, var16 + var21.getYLocation(), var15 + var6, this.rand); 355 } 356 } 357 } 358 } 359 360 this.theProfiler.endSection(); 361 } 362 } 363 364 /** 365 * Schedules a tick to a block with a delay (Most commonly the tick rate) 366 */ 367 public void scheduleBlockUpdate(int par1, int par2, int par3, int par4, int par5) 368 { 369 NextTickListEntry var6 = new NextTickListEntry(par1, par2, par3, par4); 370 boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(var6.xCoord >> 4, var6.zCoord >> 4)); 371 byte var7 = isForced ? (byte)0 : 8; 372 373 if (this.scheduledUpdatesAreImmediate) 374 { 375 if (this.checkChunksExist(var6.xCoord - var7, var6.yCoord - var7, var6.zCoord - var7, var6.xCoord + var7, var6.yCoord + var7, var6.zCoord + var7)) 376 { 377 int var8 = this.getBlockId(var6.xCoord, var6.yCoord, var6.zCoord); 378 379 if (var8 == var6.blockID && var8 > 0) 380 { 381 Block.blocksList[var8].updateTick(this, var6.xCoord, var6.yCoord, var6.zCoord, this.rand); 382 } 383 } 384 } 385 else 386 { 387 if (this.checkChunksExist(par1 - var7, par2 - var7, par3 - var7, par1 + var7, par2 + var7, par3 + var7)) 388 { 389 if (par4 > 0) 390 { 391 var6.setScheduledTime((long)par5 + this.worldInfo.getWorldTime()); 392 } 393 394 if (!this.field_73064_N.contains(var6)) 395 { 396 this.field_73064_N.add(var6); 397 this.pendingTickListEntries.add(var6); 398 } 399 } 400 } 401 } 402 403 /** 404 * Schedules a block update from the saved information in a chunk. Called when the chunk is loaded. 405 */ 406 public void scheduleBlockUpdateFromLoad(int par1, int par2, int par3, int par4, int par5) 407 { 408 NextTickListEntry var6 = new NextTickListEntry(par1, par2, par3, par4); 409 410 if (par4 > 0) 411 { 412 var6.setScheduledTime((long)par5 + this.worldInfo.getWorldTime()); 413 } 414 415 if (!this.field_73064_N.contains(var6)) 416 { 417 this.field_73064_N.add(var6); 418 this.pendingTickListEntries.add(var6); 419 } 420 } 421 422 /** 423 * Updates (and cleans up) entities and tile entities 424 */ 425 public void updateEntities() 426 { 427 if (this.playerEntities.isEmpty() && getPersistentChunks().isEmpty()) 428 { 429 if (this.updateEntityTick++ >= 60) 430 { 431 return; 432 } 433 } 434 else 435 { 436 this.updateEntityTick = 0; 437 } 438 439 super.updateEntities(); 440 } 441 442 /** 443 * Runs through the list of updates to run and ticks them 444 */ 445 public boolean tickUpdates(boolean par1) 446 { 447 int var2 = this.pendingTickListEntries.size(); 448 449 if (var2 != this.field_73064_N.size()) 450 { 451 throw new IllegalStateException("TickNextTick list out of synch"); 452 } 453 else 454 { 455 if (var2 > 1000) 456 { 457 var2 = 1000; 458 } 459 460 for (int var3 = 0; var3 < var2; ++var3) 461 { 462 NextTickListEntry var4 = (NextTickListEntry)this.pendingTickListEntries.first(); 463 464 if (!par1 && var4.scheduledTime > this.worldInfo.getWorldTime()) 465 { 466 break; 467 } 468 469 this.pendingTickListEntries.remove(var4); 470 this.field_73064_N.remove(var4); 471 boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(var4.xCoord >> 4, var4.zCoord >> 4)); 472 byte var5 = isForced ? (byte)0 : 8; 473 474 if (this.checkChunksExist(var4.xCoord - var5, var4.yCoord - var5, var4.zCoord - var5, var4.xCoord + var5, var4.yCoord + var5, var4.zCoord + var5)) 475 { 476 int var6 = this.getBlockId(var4.xCoord, var4.yCoord, var4.zCoord); 477 478 if (var6 == var4.blockID && var6 > 0) 479 { 480 Block.blocksList[var6].updateTick(this, var4.xCoord, var4.yCoord, var4.zCoord, this.rand); 481 } 482 } 483 } 484 485 return !this.pendingTickListEntries.isEmpty(); 486 } 487 } 488 489 public List getPendingBlockUpdates(Chunk par1Chunk, boolean par2) 490 { 491 ArrayList var3 = null; 492 ChunkCoordIntPair var4 = par1Chunk.getChunkCoordIntPair(); 493 int var5 = var4.chunkXPos << 4; 494 int var6 = var5 + 16; 495 int var7 = var4.chunkZPos << 4; 496 int var8 = var7 + 16; 497 Iterator var9 = this.pendingTickListEntries.iterator(); 498 499 while (var9.hasNext()) 500 { 501 NextTickListEntry var10 = (NextTickListEntry)var9.next(); 502 503 if (var10.xCoord >= var5 && var10.xCoord < var6 && var10.zCoord >= var7 && var10.zCoord < var8) 504 { 505 if (par2) 506 { 507 this.field_73064_N.remove(var10); 508 var9.remove(); 509 } 510 511 if (var3 == null) 512 { 513 var3 = new ArrayList(); 514 } 515 516 var3.add(var10); 517 } 518 } 519 520 return var3; 521 } 522 523 /** 524 * Will update the entity in the world if the chunk the entity is in is currently loaded or its forced to update. 525 * Args: entity, forceUpdate 526 */ 527 public void updateEntityWithOptionalForce(Entity par1Entity, boolean par2) 528 { 529 if (!this.mcServer.getCanSpawnAnimals() && (par1Entity instanceof EntityAnimal || par1Entity instanceof EntityWaterMob)) 530 { 531 par1Entity.setDead(); 532 } 533 534 if (!this.mcServer.getCanSpawnNPCs() && par1Entity instanceof INpc) 535 { 536 par1Entity.setDead(); 537 } 538 539 if (!(par1Entity.riddenByEntity instanceof EntityPlayer)) 540 { 541 super.updateEntityWithOptionalForce(par1Entity, par2); 542 } 543 } 544 545 /** 546 * direct call to super.updateEntityWithOptionalForce 547 */ 548 public void uncheckedUpdateEntity(Entity par1Entity, boolean par2) 549 { 550 super.updateEntityWithOptionalForce(par1Entity, par2); 551 } 552 553 /** 554 * Creates the chunk provider for this world. Called in the constructor. Retrieves provider from worldProvider? 555 */ 556 protected IChunkProvider createChunkProvider() 557 { 558 IChunkLoader var1 = this.saveHandler.getChunkLoader(this.provider); 559 this.theChunkProviderServer = new ChunkProviderServer(this, var1, this.provider.getChunkProvider()); 560 return this.theChunkProviderServer; 561 } 562 563 /** 564 * pars: min x,y,z , max x,y,z 565 */ 566 public List getAllTileEntityInBox(int par1, int par2, int par3, int par4, int par5, int par6) 567 { 568 ArrayList var7 = new ArrayList(); 569 570 for(int x = (par1 >> 4); x <= (par4 >> 4); x++) 571 { 572 for(int z = (par3 >> 4); z <= (par6 >> 4); z++) 573 { 574 Chunk chunk = getChunkFromChunkCoords(x, z); 575 if (chunk != null) 576 { 577 for(Object obj : chunk.chunkTileEntityMap.values()) 578 { 579 TileEntity entity = (TileEntity)obj; 580 if (!entity.isInvalid()) 581 { 582 if (entity.xCoord >= par1 && entity.yCoord >= par2 && entity.zCoord >= par3 && 583 entity.xCoord <= par4 && entity.yCoord <= par5 && entity.zCoord <= par6) 584 { 585 var7.add(entity); 586 } 587 } 588 } 589 } 590 } 591 } 592 593 return var7; 594 } 595 596 /** 597 * Called when checking if a certain block can be mined or not. The 'spawn safe zone' check is located here. 598 */ 599 public boolean canMineBlock(EntityPlayer par1EntityPlayer, int par2, int par3, int par4) 600 { 601 return super.canMineBlock(par1EntityPlayer, par2, par3, par4); 602 } 603 604 public boolean canMineBlockBody(EntityPlayer par1EntityPlayer, int par2, int par3, int par4) 605 { 606 int var5 = MathHelper.abs_int(par2 - this.worldInfo.getSpawnX()); 607 int var6 = MathHelper.abs_int(par4 - this.worldInfo.getSpawnZ()); 608 609 if (var5 > var6) 610 { 611 var6 = var5; 612 } 613 614 return var6 > mcServer.spawnProtectionSize || this.mcServer.getConfigurationManager().areCommandsAllowed(par1EntityPlayer.username) || this.mcServer.isSinglePlayer(); 615 } 616 617 protected void initialize(WorldSettings par1WorldSettings) 618 { 619 if (this.entityIdMap == null) 620 { 621 this.entityIdMap = new IntHashMap(); 622 } 623 624 if (this.field_73064_N == null) 625 { 626 this.field_73064_N = new HashSet(); 627 } 628 629 if (this.pendingTickListEntries == null) 630 { 631 this.pendingTickListEntries = new TreeSet(); 632 } 633 634 this.createSpawnPosition(par1WorldSettings); 635 super.initialize(par1WorldSettings); 636 } 637 638 /** 639 * creates a spawn position at random within 256 blocks of 0,0 640 */ 641 protected void createSpawnPosition(WorldSettings par1WorldSettings) 642 { 643 if (!this.provider.canRespawnHere()) 644 { 645 this.worldInfo.setSpawnPosition(0, this.provider.getAverageGroundLevel(), 0); 646 } 647 else 648 { 649 this.findingSpawnPoint = true; 650 WorldChunkManager var2 = this.provider.worldChunkMgr; 651 List var3 = var2.getBiomesToSpawnIn(); 652 Random var4 = new Random(this.getSeed()); 653 ChunkPosition var5 = var2.findBiomePosition(0, 0, 256, var3, var4); 654 int var6 = 0; 655 int var7 = this.provider.getAverageGroundLevel(); 656 int var8 = 0; 657 658 if (var5 != null) 659 { 660 var6 = var5.x; 661 var8 = var5.z; 662 } 663 else 664 { 665 System.out.println("Unable to find spawn biome"); 666 } 667 668 int var9 = 0; 669 670 while (!this.provider.canCoordinateBeSpawn(var6, var8)) 671 { 672 var6 += var4.nextInt(64) - var4.nextInt(64); 673 var8 += var4.nextInt(64) - var4.nextInt(64); 674 ++var9; 675 676 if (var9 == 1000) 677 { 678 break; 679 } 680 } 681 682 this.worldInfo.setSpawnPosition(var6, var7, var8); 683 this.findingSpawnPoint = false; 684 685 if (par1WorldSettings.isBonusChestEnabled()) 686 { 687 this.createBonusChest(); 688 } 689 } 690 } 691 692 /** 693 * Creates the bonus chest in the world. 694 */ 695 protected void createBonusChest() 696 { 697 WorldGeneratorBonusChest var1 = new WorldGeneratorBonusChest(ChestGenHooks.getItems(BONUS_CHEST), ChestGenHooks.getCount(BONUS_CHEST, rand)); 698 699 for (int var2 = 0; var2 < 10; ++var2) 700 { 701 int var3 = this.worldInfo.getSpawnX() + this.rand.nextInt(6) - this.rand.nextInt(6); 702 int var4 = this.worldInfo.getSpawnZ() + this.rand.nextInt(6) - this.rand.nextInt(6); 703 int var5 = this.getTopSolidOrLiquidBlock(var3, var4) + 1; 704 705 if (var1.generate(this, this.rand, var3, var5, var4)) 706 { 707 break; 708 } 709 } 710 } 711 712 /** 713 * Gets the hard-coded portal location to use when entering this dimension. 714 */ 715 public ChunkCoordinates getEntrancePortalLocation() 716 { 717 return this.provider.getEntrancePortalLocation(); 718 } 719 720 /** 721 * Saves all chunks to disk while updating progress bar. 722 */ 723 public void saveAllChunks(boolean par1, IProgressUpdate par2IProgressUpdate) throws MinecraftException 724 { 725 if (this.chunkProvider.canSave()) 726 { 727 if (par2IProgressUpdate != null) 728 { 729 par2IProgressUpdate.displayProgressMessage("Saving level"); 730 } 731 732 this.saveLevel(); 733 734 if (par2IProgressUpdate != null) 735 { 736 par2IProgressUpdate.resetProgresAndWorkingMessage("Saving chunks"); 737 } 738 739 this.chunkProvider.saveChunks(par1, par2IProgressUpdate); 740 MinecraftForge.EVENT_BUS.post(new WorldEvent.Save(this)); 741 } 742 } 743 744 /** 745 * Saves the chunks to disk. 746 */ 747 protected void saveLevel() throws MinecraftException 748 { 749 this.checkSessionLock(); 750 this.saveHandler.saveWorldInfoWithPlayer(this.worldInfo, this.mcServer.getConfigurationManager().getTagsFromLastWrite()); 751 this.mapStorage.saveAllData(); 752 } 753 754 /** 755 * Start the skin for this entity downloading, if necessary, and increment its reference counter 756 */ 757 protected void obtainEntitySkin(Entity par1Entity) 758 { 759 super.obtainEntitySkin(par1Entity); 760 this.entityIdMap.addKey(par1Entity.entityId, par1Entity); 761 Entity[] var2 = par1Entity.getParts(); 762 763 if (var2 != null) 764 { 765 Entity[] var3 = var2; 766 int var4 = var2.length; 767 768 for (int var5 = 0; var5 < var4; ++var5) 769 { 770 Entity var6 = var3[var5]; 771 this.entityIdMap.addKey(var6.entityId, var6); 772 } 773 } 774 } 775 776 /** 777 * Decrement the reference counter for this entity's skin image data 778 */ 779 protected void releaseEntitySkin(Entity par1Entity) 780 { 781 super.releaseEntitySkin(par1Entity); 782 this.entityIdMap.removeObject(par1Entity.entityId); 783 Entity[] var2 = par1Entity.getParts(); 784 785 if (var2 != null) 786 { 787 Entity[] var3 = var2; 788 int var4 = var2.length; 789 790 for (int var5 = 0; var5 < var4; ++var5) 791 { 792 Entity var6 = var3[var5]; 793 this.entityIdMap.removeObject(var6.entityId); 794 } 795 } 796 } 797 798 /** 799 * Returns the Entity with the given ID, or null if it doesn't exist in this World. 800 */ 801 public Entity getEntityByID(int par1) 802 { 803 return (Entity)this.entityIdMap.lookup(par1); 804 } 805 806 /** 807 * adds a lightning bolt to the list of lightning bolts in this world. 808 */ 809 public boolean addWeatherEffect(Entity par1Entity) 810 { 811 if (super.addWeatherEffect(par1Entity)) 812 { 813 this.mcServer.getConfigurationManager().sendToAllNear(par1Entity.posX, par1Entity.posY, par1Entity.posZ, 512.0D, this.provider.dimensionId, new Packet71Weather(par1Entity)); 814 return true; 815 } 816 else 817 { 818 return false; 819 } 820 } 821 822 /** 823 * sends a Packet 38 (Entity Status) to all tracked players of that entity 824 */ 825 public void setEntityState(Entity par1Entity, byte par2) 826 { 827 Packet38EntityStatus var3 = new Packet38EntityStatus(par1Entity.entityId, par2); 828 this.getEntityTracker().sendPacketToAllAssociatedPlayers(par1Entity, var3); 829 } 830 831 /** 832 * returns a new explosion. Does initiation (at time of writing Explosion is not finished) 833 */ 834 public Explosion newExplosion(Entity par1Entity, double par2, double par4, double par6, float par8, boolean par9) 835 { 836 Explosion var10 = new Explosion(this, par1Entity, par2, par4, par6, par8); 837 var10.isFlaming = par9; 838 var10.doExplosionA(); 839 var10.doExplosionB(false); 840 Iterator var11 = this.playerEntities.iterator(); 841 842 while (var11.hasNext()) 843 { 844 EntityPlayer var12 = (EntityPlayer)var11.next(); 845 846 if (var12.getDistanceSq(par2, par4, par6) < 4096.0D) 847 { 848 ((EntityPlayerMP)var12).playerNetServerHandler.sendPacketToPlayer(new Packet60Explosion(par2, par4, par6, par8, var10.field_77281_g, (Vec3)var10.func_77277_b().get(var12))); 849 } 850 } 851 852 return var10; 853 } 854 855 /** 856 * Adds a block event with the given Args to the blockEventCache. During the next tick(), the block specified will 857 * have its onBlockEvent handler called with the given parameters. Args: X,Y,Z, BlockID, EventID, EventParameter 858 */ 859 public void addBlockEvent(int par1, int par2, int par3, int par4, int par5, int par6) 860 { 861 BlockEventData var7 = new BlockEventData(par1, par2, par3, par4, par5, par6); 862 Iterator var8 = this.blockEventCache[this.blockEventCacheIndex].iterator(); 863 BlockEventData var9; 864 865 do 866 { 867 if (!var8.hasNext()) 868 { 869 this.blockEventCache[this.blockEventCacheIndex].add(var7); 870 return; 871 } 872 873 var9 = (BlockEventData)var8.next(); 874 } 875 while (!var9.equals(var7)); 876 } 877 878 /** 879 * Send and apply locally all pending BlockEvents to each player with 64m radius of the event. 880 */ 881 private void sendAndApplyBlockEvents() 882 { 883 while (!this.blockEventCache[this.blockEventCacheIndex].isEmpty()) 884 { 885 int var1 = this.blockEventCacheIndex; 886 this.blockEventCacheIndex ^= 1; 887 Iterator var2 = this.blockEventCache[var1].iterator(); 888 889 while (var2.hasNext()) 890 { 891 BlockEventData var3 = (BlockEventData)var2.next(); 892 893 if (this.onBlockEventReceived(var3)) 894 { 895 this.mcServer.getConfigurationManager().sendToAllNear((double)var3.getX(), (double)var3.getY(), (double)var3.getZ(), 64.0D, this.provider.dimensionId, new Packet54PlayNoteBlock(var3.getX(), var3.getY(), var3.getZ(), var3.getBlockID(), var3.getEventID(), var3.getEventParameter())); 896 } 897 } 898 899 this.blockEventCache[var1].clear(); 900 } 901 } 902 903 /** 904 * Called to apply a pending BlockEvent to apply to the current world. 905 */ 906 private boolean onBlockEventReceived(BlockEventData par1BlockEventData) 907 { 908 int var2 = this.getBlockId(par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ()); 909 910 if (var2 == par1BlockEventData.getBlockID()) 911 { 912 Block.blocksList[var2].onBlockEventReceived(this, par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ(), par1BlockEventData.getEventID(), par1BlockEventData.getEventParameter()); 913 return true; 914 } 915 else 916 { 917 return false; 918 } 919 } 920 921 /** 922 * Syncs all changes to disk and wait for completion. 923 */ 924 public void flush() 925 { 926 this.saveHandler.flush(); 927 } 928 929 /** 930 * Updates all weather states. 931 */ 932 protected void updateWeather() 933 { 934 boolean var1 = this.isRaining(); 935 super.updateWeather(); 936 937 if (var1 != this.isRaining()) 938 { 939 if (var1) 940 { 941 this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(2, 0)); 942 } 943 else 944 { 945 this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(1, 0)); 946 } 947 } 948 } 949 950 /** 951 * Gets the MinecraftServer. 952 */ 953 public MinecraftServer getMinecraftServer() 954 { 955 return this.mcServer; 956 } 957 958 /** 959 * Gets the EntityTracker 960 */ 961 public EntityTracker getEntityTracker() 962 { 963 return this.theEntityTracker; 964 } 965 966 /** 967 * Sets the time on the given WorldServer 968 */ 969 public void setTime(long par1) 970 { 971 long var3 = par1 - this.worldInfo.getWorldTime(); 972 NextTickListEntry var6; 973 974 for (Iterator var5 = this.field_73064_N.iterator(); var5.hasNext(); var6.scheduledTime += var3) 975 { 976 var6 = (NextTickListEntry)var5.next(); 977 } 978 979 Block[] var9 = Block.blocksList; 980 int var10 = var9.length; 981 982 for (int var7 = 0; var7 < var10; ++var7) 983 { 984 Block var8 = var9[var7]; 985 986 if (var8 != null) 987 { 988 var8.onTimeChanged(this, var3, par1); 989 } 990 } 991 992 this.setWorldTime(par1); 993 } 994 995 public PlayerManager getPlayerManager() 996 { 997 return this.thePlayerManager; 998 } 999 1000 public File getChunkSaveLocation() 1001 { 1002 return ((AnvilChunkLoader)theChunkProviderServer.currentChunkLoader).chunkSaveLocation; 1003 } 1004 }