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