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