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