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    }