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