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.network.packet.Packet38EntityStatus;
029import net.minecraft.network.packet.Packet54PlayNoteBlock;
030import net.minecraft.network.packet.Packet60Explosion;
031import net.minecraft.network.packet.Packet70GameEvent;
032import net.minecraft.network.packet.Packet71Weather;
033import net.minecraft.profiler.Profiler;
034import net.minecraft.server.MinecraftServer;
035import net.minecraft.server.management.PlayerManager;
036import net.minecraft.tileentity.TileEntity;
037import net.minecraft.util.ChunkCoordinates;
038import net.minecraft.util.IProgressUpdate;
039import net.minecraft.util.IntHashMap;
040import net.minecraft.util.MathHelper;
041import net.minecraft.util.ReportedException;
042import net.minecraft.util.Vec3;
043import net.minecraft.util.WeightedRandom;
044import net.minecraft.util.WeightedRandomChestContent;
045import net.minecraft.world.biome.BiomeGenBase;
046import net.minecraft.world.biome.SpawnListEntry;
047import net.minecraft.world.biome.WorldChunkManager;
048import net.minecraft.world.chunk.Chunk;
049import net.minecraft.world.chunk.IChunkProvider;
050import net.minecraft.world.chunk.storage.AnvilChunkLoader;
051import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
052import net.minecraft.world.chunk.storage.IChunkLoader;
053import net.minecraft.world.gen.ChunkProviderServer;
054import net.minecraft.world.gen.feature.WorldGeneratorBonusChest;
055import net.minecraft.world.storage.ISaveHandler;
056
057import net.minecraftforge.common.ChestGenHooks;
058import static net.minecraftforge.common.ChestGenHooks.*;
059import net.minecraftforge.common.DimensionManager;
060import net.minecraftforge.common.MinecraftForge;
061import net.minecraftforge.event.world.WorldEvent;
062
063public 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}