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