001    package net.minecraft.world;
002    
003    import cpw.mods.fml.common.Side;
004    import cpw.mods.fml.common.asm.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.shiftedIndex, 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.shiftedIndex, 0, 1, 1, 3), new WeightedRandomChestContent(Item.axeWood.shiftedIndex, 0, 1, 1, 5), new WeightedRandomChestContent(Item.pickaxeStone.shiftedIndex, 0, 1, 1, 3), new WeightedRandomChestContent(Item.pickaxeWood.shiftedIndex, 0, 1, 1, 5), new WeightedRandomChestContent(Item.appleRed.shiftedIndex, 0, 2, 3, 5), new WeightedRandomChestContent(Item.bread.shiftedIndex, 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 time = -System.currentTimeMillis();
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                if (System.currentTimeMillis() + time <= 4 && doneChunks.add(var4)) { //Limits and evenly distributes the lighting update time
335                    var7.updateSkylight();
336                }
337                this.theProfiler.endStartSection("thunder");
338                int var8;
339                int var9;
340                int var10;
341                int var11;
342    
343                if (provider.canDoLightning(var7) && this.rand.nextInt(100000) == 0 && this.isRaining() && this.isThundering())
344                {
345                    this.updateLCG = this.updateLCG * 3 + 1013904223;
346                    var8 = this.updateLCG >> 2;
347                    var9 = var5 + (var8 & 15);
348                    var10 = var6 + (var8 >> 8 & 15);
349                    var11 = this.getPrecipitationHeight(var9, var10);
350    
351                    if (this.canLightningStrikeAt(var9, var11, var10))
352                    {
353                        this.addWeatherEffect(new EntityLightningBolt(this, (double)var9, (double)var11, (double)var10));
354                        this.lastLightningBolt = 2;
355                    }
356                }
357    
358                this.theProfiler.endStartSection("iceandsnow");
359                int var13;
360    
361                if (provider.canDoRainSnowIce(var7) && this.rand.nextInt(16) == 0)
362                {
363                    this.updateLCG = this.updateLCG * 3 + 1013904223;
364                    var8 = this.updateLCG >> 2;
365                    var9 = var8 & 15;
366                    var10 = var8 >> 8 & 15;
367                    var11 = this.getPrecipitationHeight(var9 + var5, var10 + var6);
368    
369                    if (this.isBlockFreezableNaturally(var9 + var5, var11 - 1, var10 + var6))
370                    {
371                        this.setBlockWithNotify(var9 + var5, var11 - 1, var10 + var6, Block.ice.blockID);
372                    }
373    
374                    if (this.isRaining() && this.canSnowAt(var9 + var5, var11, var10 + var6))
375                    {
376                        this.setBlockWithNotify(var9 + var5, var11, var10 + var6, Block.snow.blockID);
377                    }
378    
379                    if (this.isRaining())
380                    {
381                        BiomeGenBase var12 = this.getBiomeGenForCoords(var9 + var5, var10 + var6);
382    
383                        if (var12.canSpawnLightningBolt())
384                        {
385                            var13 = this.getBlockId(var9 + var5, var11 - 1, var10 + var6);
386    
387                            if (var13 != 0)
388                            {
389                                Block.blocksList[var13].fillWithRain(this, var9 + var5, var11 - 1, var10 + var6);
390                            }
391                        }
392                    }
393                }
394    
395                this.theProfiler.endStartSection("tickTiles");
396                ExtendedBlockStorage[] var19 = var7.getBlockStorageArray();
397                var9 = var19.length;
398    
399                for (var10 = 0; var10 < var9; ++var10)
400                {
401                    ExtendedBlockStorage var21 = var19[var10];
402    
403                    if (var21 != null && var21.getNeedsRandomTick())
404                    {
405                        for (int var20 = 0; var20 < 3; ++var20)
406                        {
407                            this.updateLCG = this.updateLCG * 3 + 1013904223;
408                            var13 = this.updateLCG >> 2;
409                            int var14 = var13 & 15;
410                            int var15 = var13 >> 8 & 15;
411                            int var16 = var13 >> 16 & 15;
412                            int var17 = var21.getExtBlockID(var14, var16, var15);
413                            ++var2;
414                            Block var18 = Block.blocksList[var17];
415    
416                            if (var18 != null && var18.getTickRandomly())
417                            {
418                                ++var1;
419                                var18.updateTick(this, var14 + var5, var16 + var21.getYLocation(), var15 + var6, this.rand);
420                            }
421                        }
422                    }
423                }
424    
425                this.theProfiler.endSection();
426            }
427        }
428    
429        /**
430         * Schedules a tick to a block with a delay (Most commonly the tick rate)
431         */
432        public void scheduleBlockUpdate(int par1, int par2, int par3, int par4, int par5)
433        {
434            this.func_82740_a(par1, par2, par3, par4, par5, 0);
435        }
436    
437        public void func_82740_a(int par1, int par2, int par3, int par4, int par5, int par6)
438        {
439            NextTickListEntry var7 = new NextTickListEntry(par1, par2, par3, par4);
440            boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(var7.xCoord >> 4, var7.zCoord >> 4));
441            byte var8 = isForced ? (byte)0 : 8;
442    
443            if (this.scheduledUpdatesAreImmediate && par4 > 0)
444            {
445                if (Block.blocksList[par4].func_82506_l())
446                {
447                    if (this.checkChunksExist(var7.xCoord - var8, var7.yCoord - var8, var7.zCoord - var8, var7.xCoord + var8, var7.yCoord + var8, var7.zCoord + var8))
448                    {
449                        int var9 = this.getBlockId(var7.xCoord, var7.yCoord, var7.zCoord);
450    
451                        if (var9 == var7.blockID && var9 > 0)
452                        {
453                            Block.blocksList[var9].updateTick(this, var7.xCoord, var7.yCoord, var7.zCoord, this.rand);
454                        }
455                    }
456    
457                    return;
458                }
459    
460                par5 = 1;
461            }
462    
463            if (this.checkChunksExist(par1 - var8, par2 - var8, par3 - var8, par1 + var8, par2 + var8, par3 + var8))
464            {
465                if (par4 > 0)
466                {
467                    var7.setScheduledTime((long)par5 + this.worldInfo.getWorldTotalTime());
468                    var7.func_82753_a(par6);
469                }
470    
471                if (!this.field_73064_N.contains(var7))
472                {
473                    this.field_73064_N.add(var7);
474                    this.pendingTickListEntries.add(var7);
475                }
476            }
477        }
478    
479        /**
480         * Schedules a block update from the saved information in a chunk. Called when the chunk is loaded.
481         */
482        public void scheduleBlockUpdateFromLoad(int par1, int par2, int par3, int par4, int par5)
483        {
484            NextTickListEntry var6 = new NextTickListEntry(par1, par2, par3, par4);
485    
486            if (par4 > 0)
487            {
488                var6.setScheduledTime((long)par5 + this.worldInfo.getWorldTotalTime());
489            }
490    
491            if (!this.field_73064_N.contains(var6))
492            {
493                this.field_73064_N.add(var6);
494                this.pendingTickListEntries.add(var6);
495            }
496        }
497    
498        /**
499         * Updates (and cleans up) entities and tile entities
500         */
501        public void updateEntities()
502        {
503            if (this.playerEntities.isEmpty() && getPersistentChunks().isEmpty())
504            {
505                if (this.updateEntityTick++ >= 1200)
506                {
507                    return;
508                }
509            }
510            else
511            {
512                this.resetUpdateEntityTick();
513            }
514    
515            super.updateEntities();
516        }
517    
518        /**
519         * Resets the updateEntityTick field to 0
520         */
521        public void resetUpdateEntityTick()
522        {
523            this.updateEntityTick = 0;
524        }
525    
526        /**
527         * Runs through the list of updates to run and ticks them
528         */
529        public boolean tickUpdates(boolean par1)
530        {
531            int var2 = this.pendingTickListEntries.size();
532    
533            if (var2 != this.field_73064_N.size())
534            {
535                throw new IllegalStateException("TickNextTick list out of synch");
536            }
537            else
538            {
539                if (var2 > 1000)
540                {
541                    var2 = 1000;
542                }
543    
544                for (int var3 = 0; var3 < var2; ++var3)
545                {
546                    NextTickListEntry var4 = (NextTickListEntry)this.pendingTickListEntries.first();
547    
548                    if (!par1 && var4.scheduledTime > this.worldInfo.getWorldTotalTime())
549                    {
550                        break;
551                    }
552    
553                    this.pendingTickListEntries.remove(var4);
554                    this.field_73064_N.remove(var4);
555                    boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(var4.xCoord >> 4, var4.zCoord >> 4));
556                    byte var5 = isForced ? (byte)0 : 8;
557    
558                    if (this.checkChunksExist(var4.xCoord - var5, var4.yCoord - var5, var4.zCoord - var5, var4.xCoord + var5, var4.yCoord + var5, var4.zCoord + var5))
559                    {
560                        int var6 = this.getBlockId(var4.xCoord, var4.yCoord, var4.zCoord);
561    
562                        if (var6 == var4.blockID && var6 > 0)
563                        {
564                            try
565                            {
566                                Block.blocksList[var6].updateTick(this, var4.xCoord, var4.yCoord, var4.zCoord, this.rand);
567                            }
568                            catch (Throwable var13)
569                            {
570                                CrashReport var8 = CrashReport.func_85055_a(var13, "Exception while ticking a block");
571                                CrashReportCategory var9 = var8.func_85058_a("Block being ticked");
572                                int var10;
573    
574                                try
575                                {
576                                    var10 = this.getBlockMetadata(var4.xCoord, var4.yCoord, var4.zCoord);
577                                }
578                                catch (Throwable var12)
579                                {
580                                    var10 = -1;
581                                }
582    
583                                CrashReportCategory.func_85068_a(var9, var4.xCoord, var4.yCoord, var4.zCoord, var6, var10);
584                                throw new ReportedException(var8);
585                            }
586                        }
587                    }
588                }
589    
590                return !this.pendingTickListEntries.isEmpty();
591            }
592        }
593    
594        public List getPendingBlockUpdates(Chunk par1Chunk, boolean par2)
595        {
596            ArrayList var3 = null;
597            ChunkCoordIntPair var4 = par1Chunk.getChunkCoordIntPair();
598            int var5 = var4.chunkXPos << 4;
599            int var6 = var5 + 16;
600            int var7 = var4.chunkZPos << 4;
601            int var8 = var7 + 16;
602            Iterator var9 = this.pendingTickListEntries.iterator();
603    
604            while (var9.hasNext())
605            {
606                NextTickListEntry var10 = (NextTickListEntry)var9.next();
607    
608                if (var10.xCoord >= var5 && var10.xCoord < var6 && var10.zCoord >= var7 && var10.zCoord < var8)
609                {
610                    if (par2)
611                    {
612                        this.field_73064_N.remove(var10);
613                        var9.remove();
614                    }
615    
616                    if (var3 == null)
617                    {
618                        var3 = new ArrayList();
619                    }
620    
621                    var3.add(var10);
622                }
623            }
624    
625            return var3;
626        }
627    
628        /**
629         * Will update the entity in the world if the chunk the entity is in is currently loaded or its forced to update.
630         * Args: entity, forceUpdate
631         */
632        public void updateEntityWithOptionalForce(Entity par1Entity, boolean par2)
633        {
634            if (!this.mcServer.getCanSpawnAnimals() && (par1Entity instanceof EntityAnimal || par1Entity instanceof EntityWaterMob))
635            {
636                par1Entity.setDead();
637            }
638    
639            if (!this.mcServer.getCanSpawnNPCs() && par1Entity instanceof INpc)
640            {
641                par1Entity.setDead();
642            }
643    
644            if (!(par1Entity.riddenByEntity instanceof EntityPlayer))
645            {
646                super.updateEntityWithOptionalForce(par1Entity, par2);
647            }
648        }
649    
650        /**
651         * direct call to super.updateEntityWithOptionalForce
652         */
653        public void uncheckedUpdateEntity(Entity par1Entity, boolean par2)
654        {
655            super.updateEntityWithOptionalForce(par1Entity, par2);
656        }
657    
658        /**
659         * Creates the chunk provider for this world. Called in the constructor. Retrieves provider from worldProvider?
660         */
661        protected IChunkProvider createChunkProvider()
662        {
663            IChunkLoader var1 = this.saveHandler.getChunkLoader(this.provider);
664            this.theChunkProviderServer = new ChunkProviderServer(this, var1, this.provider.createChunkGenerator());
665            return this.theChunkProviderServer;
666        }
667    
668        /**
669         * pars: min x,y,z , max x,y,z
670         */
671        public List getAllTileEntityInBox(int par1, int par2, int par3, int par4, int par5, int par6)
672        {
673            ArrayList var7 = new ArrayList();
674    
675            for(int x = (par1 >> 4); x <= (par4 >> 4); x++)
676            {
677                for(int z = (par3 >> 4); z <= (par6 >> 4); z++)
678                {
679                    Chunk chunk = getChunkFromChunkCoords(x, z);
680                    if (chunk != null)
681                    {
682                        for(Object obj : chunk.chunkTileEntityMap.values())
683                        {
684                            TileEntity entity = (TileEntity)obj;
685                            if (!entity.isInvalid())
686                            {
687                                if (entity.xCoord >= par1 && entity.yCoord >= par2 && entity.zCoord >= par3 &&
688                                    entity.xCoord <= par4 && entity.yCoord <= par5 && entity.zCoord <= par6)
689                                {
690                                    var7.add(entity);
691                                }
692                            }
693                        }
694                    }
695                }
696            }
697            return var7;
698        }
699    
700        /**
701         * Called when checking if a certain block can be mined or not. The 'spawn safe zone' check is located here.
702         */
703        public boolean canMineBlock(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
704        {
705            return super.canMineBlock(par1EntityPlayer, par2, par3, par4);
706        }
707    
708        public boolean canMineBlockBody(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
709        {
710            int var5 = MathHelper.abs_int(par2 - this.worldInfo.getSpawnX());
711            int var6 = MathHelper.abs_int(par4 - this.worldInfo.getSpawnZ());
712    
713            if (var5 > var6)
714            {
715                var6 = var5;
716            }
717    
718            return var6 > mcServer.getSpawnProtectionSize() || this.mcServer.getConfigurationManager().areCommandsAllowed(par1EntityPlayer.username) || this.mcServer.isSinglePlayer();
719        }
720    
721        protected void initialize(WorldSettings par1WorldSettings)
722        {
723            if (this.entityIdMap == null)
724            {
725                this.entityIdMap = new IntHashMap();
726            }
727    
728            if (this.field_73064_N == null)
729            {
730                this.field_73064_N = new HashSet();
731            }
732    
733            if (this.pendingTickListEntries == null)
734            {
735                this.pendingTickListEntries = new TreeSet();
736            }
737    
738            this.createSpawnPosition(par1WorldSettings);
739            super.initialize(par1WorldSettings);
740        }
741    
742        /**
743         * creates a spawn position at random within 256 blocks of 0,0
744         */
745        protected void createSpawnPosition(WorldSettings par1WorldSettings)
746        {
747            if (!this.provider.canRespawnHere())
748            {
749                this.worldInfo.setSpawnPosition(0, this.provider.getAverageGroundLevel(), 0);
750            }
751            else
752            {
753                this.findingSpawnPoint = true;
754                WorldChunkManager var2 = this.provider.worldChunkMgr;
755                List var3 = var2.getBiomesToSpawnIn();
756                Random var4 = new Random(this.getSeed());
757                ChunkPosition var5 = var2.findBiomePosition(0, 0, 256, var3, var4);
758                int var6 = 0;
759                int var7 = this.provider.getAverageGroundLevel();
760                int var8 = 0;
761    
762                if (var5 != null)
763                {
764                    var6 = var5.x;
765                    var8 = var5.z;
766                }
767                else
768                {
769                    System.out.println("Unable to find spawn biome");
770                }
771    
772                int var9 = 0;
773    
774                while (!this.provider.canCoordinateBeSpawn(var6, var8))
775                {
776                    var6 += var4.nextInt(64) - var4.nextInt(64);
777                    var8 += var4.nextInt(64) - var4.nextInt(64);
778                    ++var9;
779    
780                    if (var9 == 1000)
781                    {
782                        break;
783                    }
784                }
785    
786                this.worldInfo.setSpawnPosition(var6, var7, var8);
787                this.findingSpawnPoint = false;
788    
789                if (par1WorldSettings.isBonusChestEnabled())
790                {
791                    this.createBonusChest();
792                }
793            }
794        }
795    
796        /**
797         * Creates the bonus chest in the world.
798         */
799        protected void createBonusChest()
800        {
801            WorldGeneratorBonusChest var1 = new WorldGeneratorBonusChest(ChestGenHooks.getItems(BONUS_CHEST), ChestGenHooks.getCount(BONUS_CHEST, rand));
802    
803            for (int var2 = 0; var2 < 10; ++var2)
804            {
805                int var3 = this.worldInfo.getSpawnX() + this.rand.nextInt(6) - this.rand.nextInt(6);
806                int var4 = this.worldInfo.getSpawnZ() + this.rand.nextInt(6) - this.rand.nextInt(6);
807                int var5 = this.getTopSolidOrLiquidBlock(var3, var4) + 1;
808    
809                if (var1.generate(this, this.rand, var3, var5, var4))
810                {
811                    break;
812                }
813            }
814        }
815    
816        /**
817         * Gets the hard-coded portal location to use when entering this dimension.
818         */
819        public ChunkCoordinates getEntrancePortalLocation()
820        {
821            return this.provider.getEntrancePortalLocation();
822        }
823    
824        /**
825         * Saves all chunks to disk while updating progress bar.
826         */
827        public void saveAllChunks(boolean par1, IProgressUpdate par2IProgressUpdate) throws MinecraftException
828        {
829            if (this.chunkProvider.canSave())
830            {
831                if (par2IProgressUpdate != null)
832                {
833                    par2IProgressUpdate.displayProgressMessage("Saving level");
834                }
835    
836                this.saveLevel();
837    
838                if (par2IProgressUpdate != null)
839                {
840                    par2IProgressUpdate.resetProgresAndWorkingMessage("Saving chunks");
841                }
842    
843                this.chunkProvider.saveChunks(par1, par2IProgressUpdate);
844                MinecraftForge.EVENT_BUS.post(new WorldEvent.Save(this));
845            }
846        }
847    
848        /**
849         * Saves the chunks to disk.
850         */
851        protected void saveLevel() throws MinecraftException
852        {
853            this.checkSessionLock();
854            this.saveHandler.saveWorldInfoWithPlayer(this.worldInfo, this.mcServer.getConfigurationManager().getTagsFromLastWrite());
855            this.mapStorage.saveAllData();
856            this.perWorldStorage.saveAllData();
857        }
858    
859        /**
860         * Start the skin for this entity downloading, if necessary, and increment its reference counter
861         */
862        protected void obtainEntitySkin(Entity par1Entity)
863        {
864            super.obtainEntitySkin(par1Entity);
865            this.entityIdMap.addKey(par1Entity.entityId, par1Entity);
866            Entity[] var2 = par1Entity.getParts();
867    
868            if (var2 != null)
869            {
870                for (int var3 = 0; var3 < var2.length; ++var3)
871                {
872                    this.entityIdMap.addKey(var2[var3].entityId, var2[var3]);
873                }
874            }
875        }
876    
877        /**
878         * Decrement the reference counter for this entity's skin image data
879         */
880        protected void releaseEntitySkin(Entity par1Entity)
881        {
882            super.releaseEntitySkin(par1Entity);
883            this.entityIdMap.removeObject(par1Entity.entityId);
884            Entity[] var2 = par1Entity.getParts();
885    
886            if (var2 != null)
887            {
888                for (int var3 = 0; var3 < var2.length; ++var3)
889                {
890                    this.entityIdMap.removeObject(var2[var3].entityId);
891                }
892            }
893        }
894    
895        /**
896         * Returns the Entity with the given ID, or null if it doesn't exist in this World.
897         */
898        public Entity getEntityByID(int par1)
899        {
900            return (Entity)this.entityIdMap.lookup(par1);
901        }
902    
903        /**
904         * adds a lightning bolt to the list of lightning bolts in this world.
905         */
906        public boolean addWeatherEffect(Entity par1Entity)
907        {
908            if (super.addWeatherEffect(par1Entity))
909            {
910                this.mcServer.getConfigurationManager().sendToAllNear(par1Entity.posX, par1Entity.posY, par1Entity.posZ, 512.0D, this.provider.dimensionId, new Packet71Weather(par1Entity));
911                return true;
912            }
913            else
914            {
915                return false;
916            }
917        }
918    
919        /**
920         * sends a Packet 38 (Entity Status) to all tracked players of that entity
921         */
922        public void setEntityState(Entity par1Entity, byte par2)
923        {
924            Packet38EntityStatus var3 = new Packet38EntityStatus(par1Entity.entityId, par2);
925            this.getEntityTracker().sendPacketToAllAssociatedPlayers(par1Entity, var3);
926        }
927    
928        /**
929         * returns a new explosion. Does initiation (at time of writing Explosion is not finished)
930         */
931        public Explosion newExplosion(Entity par1Entity, double par2, double par4, double par6, float par8, boolean par9, boolean par10)
932        {
933            Explosion var11 = new Explosion(this, par1Entity, par2, par4, par6, par8);
934            var11.isFlaming = par9;
935            var11.isSmoking = par10;
936            var11.doExplosionA();
937            var11.doExplosionB(false);
938    
939            if (!par10)
940            {
941                var11.affectedBlockPositions.clear();
942            }
943    
944            Iterator var12 = this.playerEntities.iterator();
945    
946            while (var12.hasNext())
947            {
948                EntityPlayer var13 = (EntityPlayer)var12.next();
949    
950                if (var13.getDistanceSq(par2, par4, par6) < 4096.0D)
951                {
952                    ((EntityPlayerMP)var13).playerNetServerHandler.sendPacketToPlayer(new Packet60Explosion(par2, par4, par6, par8, var11.affectedBlockPositions, (Vec3)var11.func_77277_b().get(var13)));
953                }
954            }
955    
956            return var11;
957        }
958    
959        /**
960         * Adds a block event with the given Args to the blockEventCache. During the next tick(), the block specified will
961         * have its onBlockEvent handler called with the given parameters. Args: X,Y,Z, BlockID, EventID, EventParameter
962         */
963        public void addBlockEvent(int par1, int par2, int par3, int par4, int par5, int par6)
964        {
965            BlockEventData var7 = new BlockEventData(par1, par2, par3, par4, par5, par6);
966            Iterator var8 = this.blockEventCache[this.blockEventCacheIndex].iterator();
967            BlockEventData var9;
968    
969            do
970            {
971                if (!var8.hasNext())
972                {
973                    this.blockEventCache[this.blockEventCacheIndex].add(var7);
974                    return;
975                }
976    
977                var9 = (BlockEventData)var8.next();
978            }
979            while (!var9.equals(var7));
980        }
981    
982        /**
983         * Send and apply locally all pending BlockEvents to each player with 64m radius of the event.
984         */
985        private void sendAndApplyBlockEvents()
986        {
987            while (!this.blockEventCache[this.blockEventCacheIndex].isEmpty())
988            {
989                int var1 = this.blockEventCacheIndex;
990                this.blockEventCacheIndex ^= 1;
991                Iterator var2 = this.blockEventCache[var1].iterator();
992    
993                while (var2.hasNext())
994                {
995                    BlockEventData var3 = (BlockEventData)var2.next();
996    
997                    if (this.onBlockEventReceived(var3))
998                    {
999                        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()));
1000                    }
1001                }
1002    
1003                this.blockEventCache[var1].clear();
1004            }
1005        }
1006    
1007        /**
1008         * Called to apply a pending BlockEvent to apply to the current world.
1009         */
1010        private boolean onBlockEventReceived(BlockEventData par1BlockEventData)
1011        {
1012            int var2 = this.getBlockId(par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ());
1013    
1014            if (var2 == par1BlockEventData.getBlockID())
1015            {
1016                Block.blocksList[var2].onBlockEventReceived(this, par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ(), par1BlockEventData.getEventID(), par1BlockEventData.getEventParameter());
1017                return true;
1018            }
1019            else
1020            {
1021                return false;
1022            }
1023        }
1024    
1025        /**
1026         * Syncs all changes to disk and wait for completion.
1027         */
1028        public void flush()
1029        {
1030            this.saveHandler.flush();
1031        }
1032    
1033        /**
1034         * Updates all weather states.
1035         */
1036        protected void updateWeather()
1037        {
1038            boolean var1 = this.isRaining();
1039            super.updateWeather();
1040    
1041            if (var1 != this.isRaining())
1042            {
1043                if (var1)
1044                {
1045                    this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(2, 0));
1046                }
1047                else
1048                {
1049                    this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(1, 0));
1050                }
1051            }
1052        }
1053    
1054        /**
1055         * Gets the MinecraftServer.
1056         */
1057        public MinecraftServer getMinecraftServer()
1058        {
1059            return this.mcServer;
1060        }
1061    
1062        /**
1063         * Gets the EntityTracker
1064         */
1065        public EntityTracker getEntityTracker()
1066        {
1067            return this.theEntityTracker;
1068        }
1069    
1070        public PlayerManager getPlayerManager()
1071        {
1072            return this.thePlayerManager;
1073        }
1074    
1075        public Teleporter func_85176_s()
1076        {
1077            return this.field_85177_Q;
1078        }
1079    
1080        public File getChunkSaveLocation()
1081        {
1082            return ((AnvilChunkLoader)theChunkProviderServer.currentChunkLoader).chunkSaveLocation;
1083        }
1084    }