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.field_96442_D = 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.field_96442_D); 138 ((ServerScoreboard)this.field_96442_D).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.func_94575_c(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.func_94575_c(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 public boolean func_94573_a(int par1, int par2, int par3, int par4) 446 { 447 NextTickListEntry nextticklistentry = new NextTickListEntry(par1, par2, par3, par4); 448 return this.field_94579_S.contains(nextticklistentry); 449 } 450 451 /** 452 * Schedules a tick to a block with a delay (Most commonly the tick rate) 453 */ 454 public void scheduleBlockUpdate(int par1, int par2, int par3, int par4, int par5) 455 { 456 this.func_82740_a(par1, par2, par3, par4, par5, 0); 457 } 458 459 public void func_82740_a(int par1, int par2, int par3, int par4, int par5, int par6) 460 { 461 NextTickListEntry nextticklistentry = new NextTickListEntry(par1, par2, par3, par4); 462 //Keeping here as a note for future when it may be restored. 463 //boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(nextticklistentry.xCoord >> 4, nextticklistentry.zCoord >> 4)); 464 //byte b0 = isForced ? 0 : 8; 465 byte b0 = 0; 466 467 if (this.scheduledUpdatesAreImmediate && par4 > 0) 468 { 469 if (Block.blocksList[par4].func_82506_l()) 470 { 471 if (this.checkChunksExist(nextticklistentry.xCoord - b0, nextticklistentry.yCoord - b0, nextticklistentry.zCoord - b0, nextticklistentry.xCoord + b0, nextticklistentry.yCoord + b0, nextticklistentry.zCoord + b0)) 472 { 473 int k1 = this.getBlockId(nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord); 474 475 if (k1 == nextticklistentry.blockID && k1 > 0) 476 { 477 Block.blocksList[k1].updateTick(this, nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord, this.rand); 478 } 479 } 480 481 return; 482 } 483 484 par5 = 1; 485 } 486 487 if (this.checkChunksExist(par1 - b0, par2 - b0, par3 - b0, par1 + b0, par2 + b0, par3 + b0)) 488 { 489 if (par4 > 0) 490 { 491 nextticklistentry.setScheduledTime((long)par5 + this.worldInfo.getWorldTotalTime()); 492 nextticklistentry.func_82753_a(par6); 493 } 494 495 if (!this.field_73064_N.contains(nextticklistentry)) 496 { 497 this.field_73064_N.add(nextticklistentry); 498 this.pendingTickListEntries.add(nextticklistentry); 499 } 500 } 501 } 502 503 /** 504 * Schedules a block update from the saved information in a chunk. Called when the chunk is loaded. 505 */ 506 public void scheduleBlockUpdateFromLoad(int par1, int par2, int par3, int par4, int par5, int par6) 507 { 508 NextTickListEntry nextticklistentry = new NextTickListEntry(par1, par2, par3, par4); 509 nextticklistentry.func_82753_a(par6); 510 511 if (par4 > 0) 512 { 513 nextticklistentry.setScheduledTime((long)par5 + this.worldInfo.getWorldTotalTime()); 514 } 515 516 if (!this.field_73064_N.contains(nextticklistentry)) 517 { 518 this.field_73064_N.add(nextticklistentry); 519 this.pendingTickListEntries.add(nextticklistentry); 520 } 521 } 522 523 /** 524 * Updates (and cleans up) entities and tile entities 525 */ 526 public void updateEntities() 527 { 528 if (this.playerEntities.isEmpty() && getPersistentChunks().isEmpty()) 529 { 530 if (this.updateEntityTick++ >= 1200) 531 { 532 return; 533 } 534 } 535 else 536 { 537 this.resetUpdateEntityTick(); 538 } 539 540 super.updateEntities(); 541 } 542 543 /** 544 * Resets the updateEntityTick field to 0 545 */ 546 public void resetUpdateEntityTick() 547 { 548 this.updateEntityTick = 0; 549 } 550 551 /** 552 * Runs through the list of updates to run and ticks them 553 */ 554 public boolean tickUpdates(boolean par1) 555 { 556 int i = this.pendingTickListEntries.size(); 557 558 if (i != this.field_73064_N.size()) 559 { 560 throw new IllegalStateException("TickNextTick list out of synch"); 561 } 562 else 563 { 564 if (i > 1000) 565 { 566 i = 1000; 567 } 568 569 this.theProfiler.startSection("cleaning"); 570 NextTickListEntry nextticklistentry; 571 572 for (int j = 0; j < i; ++j) 573 { 574 nextticklistentry = (NextTickListEntry)this.pendingTickListEntries.first(); 575 576 if (!par1 && nextticklistentry.scheduledTime > this.worldInfo.getWorldTotalTime()) 577 { 578 break; 579 } 580 581 this.pendingTickListEntries.remove(nextticklistentry); 582 this.field_73064_N.remove(nextticklistentry); 583 this.field_94579_S.add(nextticklistentry); 584 } 585 586 this.theProfiler.endSection(); 587 this.theProfiler.startSection("ticking"); 588 Iterator iterator = this.field_94579_S.iterator(); 589 590 while (iterator.hasNext()) 591 { 592 nextticklistentry = (NextTickListEntry)iterator.next(); 593 iterator.remove(); 594 //Keeping here as a note for future when it may be restored. 595 //boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(nextticklistentry.xCoord >> 4, nextticklistentry.zCoord >> 4)); 596 //byte b0 = isForced ? 0 : 8; 597 byte b0 = 0; 598 599 if (this.checkChunksExist(nextticklistentry.xCoord - b0, nextticklistentry.yCoord - b0, nextticklistentry.zCoord - b0, nextticklistentry.xCoord + b0, nextticklistentry.yCoord + b0, nextticklistentry.zCoord + b0)) 600 { 601 int k = this.getBlockId(nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord); 602 603 if (k > 0 && Block.func_94329_b(k, nextticklistentry.blockID)) 604 { 605 try 606 { 607 Block.blocksList[k].updateTick(this, nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord, this.rand); 608 } 609 catch (Throwable throwable) 610 { 611 CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Exception while ticking a block"); 612 CrashReportCategory crashreportcategory = crashreport.makeCategory("Block being ticked"); 613 int l; 614 615 try 616 { 617 l = this.getBlockMetadata(nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord); 618 } 619 catch (Throwable throwable1) 620 { 621 l = -1; 622 } 623 624 CrashReportCategory.func_85068_a(crashreportcategory, nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord, k, l); 625 throw new ReportedException(crashreport); 626 } 627 } 628 } 629 else 630 { 631 this.scheduleBlockUpdate(nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord, nextticklistentry.blockID, 0); 632 } 633 } 634 635 this.theProfiler.endSection(); 636 this.field_94579_S.clear(); 637 return !this.pendingTickListEntries.isEmpty(); 638 } 639 } 640 641 public List getPendingBlockUpdates(Chunk par1Chunk, boolean par2) 642 { 643 ArrayList arraylist = null; 644 ChunkCoordIntPair chunkcoordintpair = par1Chunk.getChunkCoordIntPair(); 645 int i = (chunkcoordintpair.chunkXPos << 4) - 2; 646 int j = i + 16 + 2; 647 int k = (chunkcoordintpair.chunkZPos << 4) - 2; 648 int l = k + 16 + 2; 649 650 for (int i1 = 0; i1 < 2; ++i1) 651 { 652 Iterator iterator; 653 654 if (i1 == 0) 655 { 656 iterator = this.pendingTickListEntries.iterator(); 657 } 658 else 659 { 660 iterator = this.field_94579_S.iterator(); 661 662 if (!this.field_94579_S.isEmpty()) 663 { 664 System.out.println(this.field_94579_S.size()); 665 } 666 } 667 668 while (iterator.hasNext()) 669 { 670 NextTickListEntry nextticklistentry = (NextTickListEntry)iterator.next(); 671 672 if (nextticklistentry.xCoord >= i && nextticklistentry.xCoord < j && nextticklistentry.zCoord >= k && nextticklistentry.zCoord < l) 673 { 674 if (par2) 675 { 676 this.field_73064_N.remove(nextticklistentry); 677 iterator.remove(); 678 } 679 680 if (arraylist == null) 681 { 682 arraylist = new ArrayList(); 683 } 684 685 arraylist.add(nextticklistentry); 686 } 687 } 688 } 689 690 return arraylist; 691 } 692 693 /** 694 * Will update the entity in the world if the chunk the entity is in is currently loaded or its forced to update. 695 * Args: entity, forceUpdate 696 */ 697 public void updateEntityWithOptionalForce(Entity par1Entity, boolean par2) 698 { 699 if (!this.mcServer.getCanSpawnAnimals() && (par1Entity instanceof EntityAnimal || par1Entity instanceof EntityWaterMob)) 700 { 701 par1Entity.setDead(); 702 } 703 704 if (!this.mcServer.getCanSpawnNPCs() && par1Entity instanceof INpc) 705 { 706 par1Entity.setDead(); 707 } 708 709 if (!(par1Entity.riddenByEntity instanceof EntityPlayer)) 710 { 711 super.updateEntityWithOptionalForce(par1Entity, par2); 712 } 713 } 714 715 /** 716 * direct call to super.updateEntityWithOptionalForce 717 */ 718 public void uncheckedUpdateEntity(Entity par1Entity, boolean par2) 719 { 720 super.updateEntityWithOptionalForce(par1Entity, par2); 721 } 722 723 /** 724 * Creates the chunk provider for this world. Called in the constructor. Retrieves provider from worldProvider? 725 */ 726 protected IChunkProvider createChunkProvider() 727 { 728 IChunkLoader ichunkloader = this.saveHandler.getChunkLoader(this.provider); 729 this.theChunkProviderServer = new ChunkProviderServer(this, ichunkloader, this.provider.createChunkGenerator()); 730 return this.theChunkProviderServer; 731 } 732 733 /** 734 * pars: min x,y,z , max x,y,z 735 */ 736 public List getAllTileEntityInBox(int par1, int par2, int par3, int par4, int par5, int par6) 737 { 738 ArrayList arraylist = new ArrayList(); 739 740 for(int x = (par1 >> 4); x <= (par4 >> 4); x++) 741 { 742 for(int z = (par3 >> 4); z <= (par6 >> 4); z++) 743 { 744 Chunk chunk = getChunkFromChunkCoords(x, z); 745 if (chunk != null) 746 { 747 for(Object obj : chunk.chunkTileEntityMap.values()) 748 { 749 TileEntity entity = (TileEntity)obj; 750 if (!entity.isInvalid()) 751 { 752 if (entity.xCoord >= par1 && entity.yCoord >= par2 && entity.zCoord >= par3 && 753 entity.xCoord <= par4 && entity.yCoord <= par5 && entity.zCoord <= par6) 754 { 755 arraylist.add(entity); 756 } 757 } 758 } 759 } 760 } 761 } 762 return arraylist; 763 } 764 765 /** 766 * Called when checking if a certain block can be mined or not. The 'spawn safe zone' check is located here. 767 */ 768 public boolean canMineBlock(EntityPlayer par1EntityPlayer, int par2, int par3, int par4) 769 { 770 return super.canMineBlock(par1EntityPlayer, par2, par3, par4); 771 } 772 773 public boolean canMineBlockBody(EntityPlayer par1EntityPlayer, int par2, int par3, int par4) 774 { 775 return !this.mcServer.func_96290_a(this, par2, par3, par4, par1EntityPlayer); 776 } 777 778 protected void initialize(WorldSettings par1WorldSettings) 779 { 780 if (this.entityIdMap == null) 781 { 782 this.entityIdMap = new IntHashMap(); 783 } 784 785 if (this.field_73064_N == null) 786 { 787 this.field_73064_N = new HashSet(); 788 } 789 790 if (this.pendingTickListEntries == null) 791 { 792 this.pendingTickListEntries = new TreeSet(); 793 } 794 795 this.createSpawnPosition(par1WorldSettings); 796 super.initialize(par1WorldSettings); 797 } 798 799 /** 800 * creates a spawn position at random within 256 blocks of 0,0 801 */ 802 protected void createSpawnPosition(WorldSettings par1WorldSettings) 803 { 804 if (!this.provider.canRespawnHere()) 805 { 806 this.worldInfo.setSpawnPosition(0, this.provider.getAverageGroundLevel(), 0); 807 } 808 else 809 { 810 this.findingSpawnPoint = true; 811 WorldChunkManager worldchunkmanager = this.provider.worldChunkMgr; 812 List list = worldchunkmanager.getBiomesToSpawnIn(); 813 Random random = new Random(this.getSeed()); 814 ChunkPosition chunkposition = worldchunkmanager.findBiomePosition(0, 0, 256, list, random); 815 int i = 0; 816 int j = this.provider.getAverageGroundLevel(); 817 int k = 0; 818 819 if (chunkposition != null) 820 { 821 i = chunkposition.x; 822 k = chunkposition.z; 823 } 824 else 825 { 826 this.func_98180_V().func_98236_b("Unable to find spawn biome"); 827 } 828 829 int l = 0; 830 831 while (!this.provider.canCoordinateBeSpawn(i, k)) 832 { 833 i += random.nextInt(64) - random.nextInt(64); 834 k += random.nextInt(64) - random.nextInt(64); 835 ++l; 836 837 if (l == 1000) 838 { 839 break; 840 } 841 } 842 843 this.worldInfo.setSpawnPosition(i, j, k); 844 this.findingSpawnPoint = false; 845 846 if (par1WorldSettings.isBonusChestEnabled()) 847 { 848 this.createBonusChest(); 849 } 850 } 851 } 852 853 /** 854 * Creates the bonus chest in the world. 855 */ 856 protected void createBonusChest() 857 { 858 WorldGeneratorBonusChest worldgeneratorbonuschest = new WorldGeneratorBonusChest(ChestGenHooks.getItems(BONUS_CHEST, rand), ChestGenHooks.getCount(BONUS_CHEST, rand)); 859 860 for (int i = 0; i < 10; ++i) 861 { 862 int j = this.worldInfo.getSpawnX() + this.rand.nextInt(6) - this.rand.nextInt(6); 863 int k = this.worldInfo.getSpawnZ() + this.rand.nextInt(6) - this.rand.nextInt(6); 864 int l = this.getTopSolidOrLiquidBlock(j, k) + 1; 865 866 if (worldgeneratorbonuschest.generate(this, this.rand, j, l, k)) 867 { 868 break; 869 } 870 } 871 } 872 873 /** 874 * Gets the hard-coded portal location to use when entering this dimension. 875 */ 876 public ChunkCoordinates getEntrancePortalLocation() 877 { 878 return this.provider.getEntrancePortalLocation(); 879 } 880 881 /** 882 * Saves all chunks to disk while updating progress bar. 883 */ 884 public void saveAllChunks(boolean par1, IProgressUpdate par2IProgressUpdate) throws MinecraftException 885 { 886 if (this.chunkProvider.canSave()) 887 { 888 if (par2IProgressUpdate != null) 889 { 890 par2IProgressUpdate.displayProgressMessage("Saving level"); 891 } 892 893 this.saveLevel(); 894 895 if (par2IProgressUpdate != null) 896 { 897 par2IProgressUpdate.resetProgresAndWorkingMessage("Saving chunks"); 898 } 899 900 this.chunkProvider.saveChunks(par1, par2IProgressUpdate); 901 MinecraftForge.EVENT_BUS.post(new WorldEvent.Save(this)); 902 } 903 } 904 905 /** 906 * Saves the chunks to disk. 907 */ 908 protected void saveLevel() throws MinecraftException 909 { 910 this.checkSessionLock(); 911 this.saveHandler.saveWorldInfoWithPlayer(this.worldInfo, this.mcServer.getConfigurationManager().getHostPlayerData()); 912 this.mapStorage.saveAllData(); 913 this.perWorldStorage.saveAllData(); 914 } 915 916 /** 917 * Start the skin for this entity downloading, if necessary, and increment its reference counter 918 */ 919 protected void obtainEntitySkin(Entity par1Entity) 920 { 921 super.obtainEntitySkin(par1Entity); 922 this.entityIdMap.addKey(par1Entity.entityId, par1Entity); 923 Entity[] aentity = par1Entity.getParts(); 924 925 if (aentity != null) 926 { 927 for (int i = 0; i < aentity.length; ++i) 928 { 929 this.entityIdMap.addKey(aentity[i].entityId, aentity[i]); 930 } 931 } 932 } 933 934 /** 935 * Decrement the reference counter for this entity's skin image data 936 */ 937 public void releaseEntitySkin(Entity par1Entity) 938 { 939 super.releaseEntitySkin(par1Entity); 940 this.entityIdMap.removeObject(par1Entity.entityId); 941 Entity[] aentity = par1Entity.getParts(); 942 943 if (aentity != null) 944 { 945 for (int i = 0; i < aentity.length; ++i) 946 { 947 this.entityIdMap.removeObject(aentity[i].entityId); 948 } 949 } 950 } 951 952 /** 953 * Returns the Entity with the given ID, or null if it doesn't exist in this World. 954 */ 955 public Entity getEntityByID(int par1) 956 { 957 return (Entity)this.entityIdMap.lookup(par1); 958 } 959 960 /** 961 * adds a lightning bolt to the list of lightning bolts in this world. 962 */ 963 public boolean addWeatherEffect(Entity par1Entity) 964 { 965 if (super.addWeatherEffect(par1Entity)) 966 { 967 this.mcServer.getConfigurationManager().sendToAllNear(par1Entity.posX, par1Entity.posY, par1Entity.posZ, 512.0D, this.provider.dimensionId, new Packet71Weather(par1Entity)); 968 return true; 969 } 970 else 971 { 972 return false; 973 } 974 } 975 976 /** 977 * sends a Packet 38 (Entity Status) to all tracked players of that entity 978 */ 979 public void setEntityState(Entity par1Entity, byte par2) 980 { 981 Packet38EntityStatus packet38entitystatus = new Packet38EntityStatus(par1Entity.entityId, par2); 982 this.getEntityTracker().sendPacketToAllAssociatedPlayers(par1Entity, packet38entitystatus); 983 } 984 985 /** 986 * returns a new explosion. Does initiation (at time of writing Explosion is not finished) 987 */ 988 public Explosion newExplosion(Entity par1Entity, double par2, double par4, double par6, float par8, boolean par9, boolean par10) 989 { 990 Explosion explosion = new Explosion(this, par1Entity, par2, par4, par6, par8); 991 explosion.isFlaming = par9; 992 explosion.isSmoking = par10; 993 explosion.doExplosionA(); 994 explosion.doExplosionB(false); 995 996 if (!par10) 997 { 998 explosion.affectedBlockPositions.clear(); 999 } 1000 1001 Iterator iterator = this.playerEntities.iterator(); 1002 1003 while (iterator.hasNext()) 1004 { 1005 EntityPlayer entityplayer = (EntityPlayer)iterator.next(); 1006 1007 if (entityplayer.getDistanceSq(par2, par4, par6) < 4096.0D) 1008 { 1009 ((EntityPlayerMP)entityplayer).playerNetServerHandler.sendPacketToPlayer(new Packet60Explosion(par2, par4, par6, par8, explosion.affectedBlockPositions, (Vec3)explosion.func_77277_b().get(entityplayer))); 1010 } 1011 } 1012 1013 return explosion; 1014 } 1015 1016 /** 1017 * Adds a block event with the given Args to the blockEventCache. During the next tick(), the block specified will 1018 * have its onBlockEvent handler called with the given parameters. Args: X,Y,Z, BlockID, EventID, EventParameter 1019 */ 1020 public void addBlockEvent(int par1, int par2, int par3, int par4, int par5, int par6) 1021 { 1022 BlockEventData blockeventdata = new BlockEventData(par1, par2, par3, par4, par5, par6); 1023 Iterator iterator = this.blockEventCache[this.blockEventCacheIndex].iterator(); 1024 BlockEventData blockeventdata1; 1025 1026 do 1027 { 1028 if (!iterator.hasNext()) 1029 { 1030 this.blockEventCache[this.blockEventCacheIndex].add(blockeventdata); 1031 return; 1032 } 1033 1034 blockeventdata1 = (BlockEventData)iterator.next(); 1035 } 1036 while (!blockeventdata1.equals(blockeventdata)); 1037 } 1038 1039 /** 1040 * Send and apply locally all pending BlockEvents to each player with 64m radius of the event. 1041 */ 1042 private void sendAndApplyBlockEvents() 1043 { 1044 while (!this.blockEventCache[this.blockEventCacheIndex].isEmpty()) 1045 { 1046 int i = this.blockEventCacheIndex; 1047 this.blockEventCacheIndex ^= 1; 1048 Iterator iterator = this.blockEventCache[i].iterator(); 1049 1050 while (iterator.hasNext()) 1051 { 1052 BlockEventData blockeventdata = (BlockEventData)iterator.next(); 1053 1054 if (this.onBlockEventReceived(blockeventdata)) 1055 { 1056 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())); 1057 } 1058 } 1059 1060 this.blockEventCache[i].clear(); 1061 } 1062 } 1063 1064 /** 1065 * Called to apply a pending BlockEvent to apply to the current world. 1066 */ 1067 private boolean onBlockEventReceived(BlockEventData par1BlockEventData) 1068 { 1069 int i = this.getBlockId(par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ()); 1070 return i == par1BlockEventData.getBlockID() ? Block.blocksList[i].onBlockEventReceived(this, par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ(), par1BlockEventData.getEventID(), par1BlockEventData.getEventParameter()) : false; 1071 } 1072 1073 /** 1074 * Syncs all changes to disk and wait for completion. 1075 */ 1076 public void flush() 1077 { 1078 this.saveHandler.flush(); 1079 } 1080 1081 /** 1082 * Updates all weather states. 1083 */ 1084 protected void updateWeather() 1085 { 1086 boolean flag = this.isRaining(); 1087 super.updateWeather(); 1088 1089 if (flag != this.isRaining()) 1090 { 1091 if (flag) 1092 { 1093 this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(2, 0)); 1094 } 1095 else 1096 { 1097 this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(1, 0)); 1098 } 1099 } 1100 } 1101 1102 /** 1103 * Gets the MinecraftServer. 1104 */ 1105 public MinecraftServer getMinecraftServer() 1106 { 1107 return this.mcServer; 1108 } 1109 1110 /** 1111 * Gets the EntityTracker 1112 */ 1113 public EntityTracker getEntityTracker() 1114 { 1115 return this.theEntityTracker; 1116 } 1117 1118 public PlayerManager getPlayerManager() 1119 { 1120 return this.thePlayerManager; 1121 } 1122 1123 public Teleporter getDefaultTeleporter() 1124 { 1125 return this.field_85177_Q; 1126 } 1127 1128 public File getChunkSaveLocation() 1129 { 1130 return ((AnvilChunkLoader)theChunkProviderServer.currentChunkLoader).chunkSaveLocation; 1131 } 1132}