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