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