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