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