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        /** set by CommandServerSave{all,Off,On} */
033        public boolean canNotSave;
034    
035        /** is false if there are no players */
036        private boolean allPlayersSleeping;
037        private int updateEntityTick = 0;
038    
039        /**
040         * Double buffer of ServerBlockEventList[] for holding pending BlockEventData's
041         */
042        private ServerBlockEventList[] blockEventCache = new ServerBlockEventList[] {new ServerBlockEventList((ServerBlockEvent)null), new ServerBlockEventList((ServerBlockEvent)null)};
043    
044        /**
045         * The index into the blockEventCache; either 0, or 1, toggled in sendBlockEventPackets  where all BlockEvent are
046         * applied locally and send to clients.
047         */
048        private int blockEventCacheIndex = 0;
049        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)};
050    
051        /** An IntHashMap of entity IDs (integers) to their Entity objects. */
052        private IntHashMap entityIdMap;
053    
054        /** Stores the recently processed (lighting) chunks */
055        protected Set<ChunkCoordIntPair> doneChunks = new HashSet<ChunkCoordIntPair>();
056    
057        public WorldServer(MinecraftServer par1MinecraftServer, ISaveHandler par2ISaveHandler, String par3Str, int par4, WorldSettings par5WorldSettings, Profiler par6Profiler)
058        {
059            super(par2ISaveHandler, par3Str, par5WorldSettings, WorldProvider.getProviderForDimension(par4), par6Profiler);
060            this.mcServer = par1MinecraftServer;
061            this.theEntityTracker = new EntityTracker(this);
062            this.thePlayerManager = new PlayerManager(this, par1MinecraftServer.getConfigurationManager().getViewDistance());
063    
064            if (this.entityIdMap == null)
065            {
066                this.entityIdMap = new IntHashMap();
067            }
068    
069            if (this.field_73064_N == null)
070            {
071                this.field_73064_N = new HashSet();
072            }
073    
074            if (this.pendingTickListEntries == null)
075            {
076                this.pendingTickListEntries = new TreeSet();
077            }
078            DimensionManager.setWorld(par4, this);
079        }
080    
081        /**
082         * Runs a single tick for the world
083         */
084        public void tick()
085        {
086            super.tick();
087    
088            if (this.getWorldInfo().isHardcoreModeEnabled() && this.difficultySetting < 3)
089            {
090                this.difficultySetting = 3;
091            }
092    
093            this.provider.worldChunkMgr.cleanupCache();
094    
095            if (this.areAllPlayersAsleep())
096            {
097                boolean var1 = false;
098    
099                if (this.spawnHostileMobs && this.difficultySetting >= 1)
100                {
101                    ;
102                }
103    
104                if (!var1)
105                {
106                    long var2 = this.worldInfo.getWorldTime() + 24000L;
107                    this.worldInfo.setWorldTime(var2 - var2 % 24000L);
108                    this.wakeAllPlayers();
109                }
110            }
111    
112            this.theProfiler.startSection("mobSpawner");
113    
114            if (this.func_82736_K().func_82766_b("doMobSpawning"))
115            {
116                SpawnerAnimals.findChunksForSpawning(this, this.spawnHostileMobs, this.spawnPeacefulMobs, this.worldInfo.func_82573_f() % 400L == 0L);
117            }
118    
119            this.theProfiler.endStartSection("chunkSource");
120            this.chunkProvider.unload100OldestChunks();
121            int var4 = this.calculateSkylightSubtracted(1.0F);
122    
123            if (var4 != this.skylightSubtracted)
124            {
125                this.skylightSubtracted = var4;
126            }
127    
128            this.sendAndApplyBlockEvents();
129            this.worldInfo.func_82572_b(this.worldInfo.func_82573_f() + 1L);
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            this.func_82740_a(par1, par2, par3, par4, par5, 0);
383        }
384    
385        public void func_82740_a(int par1, int par2, int par3, int par4, int par5, int par6)
386        {
387            NextTickListEntry var7 = new NextTickListEntry(par1, par2, par3, par4);
388            boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(var7.xCoord >> 4, var7.zCoord >> 4));
389            byte var8 = isForced ? (byte)0 : 8;
390    
391            if (this.scheduledUpdatesAreImmediate && par4 > 0)
392            {
393                if (Block.blocksList[par4].func_82506_l())
394                {
395                    if (this.checkChunksExist(var7.xCoord - var8, var7.yCoord - var8, var7.zCoord - var8, var7.xCoord + var8, var7.yCoord + var8, var7.zCoord + var8))
396                    {
397                        int var9 = this.getBlockId(var7.xCoord, var7.yCoord, var7.zCoord);
398    
399                        if (var9 == var7.blockID && var9 > 0)
400                        {
401                            Block.blocksList[var9].updateTick(this, var7.xCoord, var7.yCoord, var7.zCoord, this.rand);
402                        }
403                    }
404    
405                    return;
406                }
407    
408                par5 = 1;
409            }
410    
411            if (this.checkChunksExist(par1 - var8, par2 - var8, par3 - var8, par1 + var8, par2 + var8, par3 + var8))
412            {
413                if (par4 > 0)
414                {
415                    var7.setScheduledTime((long)par5 + this.worldInfo.func_82573_f());
416                    var7.func_82753_a(par6);
417                }
418    
419                if (!this.field_73064_N.contains(var7))
420                {
421                    this.field_73064_N.add(var7);
422                    this.pendingTickListEntries.add(var7);
423                }
424            }
425        }
426    
427        /**
428         * Schedules a block update from the saved information in a chunk. Called when the chunk is loaded.
429         */
430        public void scheduleBlockUpdateFromLoad(int par1, int par2, int par3, int par4, int par5)
431        {
432            NextTickListEntry var6 = new NextTickListEntry(par1, par2, par3, par4);
433    
434            if (par4 > 0)
435            {
436                var6.setScheduledTime((long)par5 + this.worldInfo.func_82573_f());
437            }
438    
439            if (!this.field_73064_N.contains(var6))
440            {
441                this.field_73064_N.add(var6);
442                this.pendingTickListEntries.add(var6);
443            }
444        }
445    
446        /**
447         * Updates (and cleans up) entities and tile entities
448         */
449        public void updateEntities()
450        {
451            if (this.playerEntities.isEmpty() && getPersistentChunks().isEmpty())
452            {
453                if (this.updateEntityTick++ >= 1200)
454                {
455                    return;
456                }
457            }
458            else
459            {
460                this.func_82742_i();
461            }
462    
463            super.updateEntities();
464        }
465    
466        public void func_82742_i()
467        {
468            this.updateEntityTick = 0;
469        }
470    
471        /**
472         * Runs through the list of updates to run and ticks them
473         */
474        public boolean tickUpdates(boolean par1)
475        {
476            int var2 = this.pendingTickListEntries.size();
477    
478            if (var2 != this.field_73064_N.size())
479            {
480                throw new IllegalStateException("TickNextTick list out of synch");
481            }
482            else
483            {
484                if (var2 > 1000)
485                {
486                    var2 = 1000;
487                }
488    
489                for (int var3 = 0; var3 < var2; ++var3)
490                {
491                    NextTickListEntry var4 = (NextTickListEntry)this.pendingTickListEntries.first();
492    
493                    if (!par1 && var4.scheduledTime > this.worldInfo.func_82573_f())
494                    {
495                        break;
496                    }
497    
498                    this.pendingTickListEntries.remove(var4);
499                    this.field_73064_N.remove(var4);
500                    boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(var4.xCoord >> 4, var4.zCoord >> 4));
501                    byte var5 = isForced ? (byte)0 : 8;
502    
503                    if (this.checkChunksExist(var4.xCoord - var5, var4.yCoord - var5, var4.zCoord - var5, var4.xCoord + var5, var4.yCoord + var5, var4.zCoord + var5))
504                    {
505                        int var6 = this.getBlockId(var4.xCoord, var4.yCoord, var4.zCoord);
506    
507                        if (var6 == var4.blockID && var6 > 0)
508                        {
509                            Block.blocksList[var6].updateTick(this, var4.xCoord, var4.yCoord, var4.zCoord, this.rand);
510                        }
511                    }
512                }
513    
514                return !this.pendingTickListEntries.isEmpty();
515            }
516        }
517    
518        public List getPendingBlockUpdates(Chunk par1Chunk, boolean par2)
519        {
520            ArrayList var3 = null;
521            ChunkCoordIntPair var4 = par1Chunk.getChunkCoordIntPair();
522            int var5 = var4.chunkXPos << 4;
523            int var6 = var5 + 16;
524            int var7 = var4.chunkZPos << 4;
525            int var8 = var7 + 16;
526            Iterator var9 = this.pendingTickListEntries.iterator();
527    
528            while (var9.hasNext())
529            {
530                NextTickListEntry var10 = (NextTickListEntry)var9.next();
531    
532                if (var10.xCoord >= var5 && var10.xCoord < var6 && var10.zCoord >= var7 && var10.zCoord < var8)
533                {
534                    if (par2)
535                    {
536                        this.field_73064_N.remove(var10);
537                        var9.remove();
538                    }
539    
540                    if (var3 == null)
541                    {
542                        var3 = new ArrayList();
543                    }
544    
545                    var3.add(var10);
546                }
547            }
548    
549            return var3;
550        }
551    
552        /**
553         * Will update the entity in the world if the chunk the entity is in is currently loaded or its forced to update.
554         * Args: entity, forceUpdate
555         */
556        public void updateEntityWithOptionalForce(Entity par1Entity, boolean par2)
557        {
558            if (!this.mcServer.getCanSpawnAnimals() && (par1Entity instanceof EntityAnimal || par1Entity instanceof EntityWaterMob))
559            {
560                par1Entity.setDead();
561            }
562    
563            if (!this.mcServer.getCanSpawnNPCs() && par1Entity instanceof INpc)
564            {
565                par1Entity.setDead();
566            }
567    
568            if (!(par1Entity.riddenByEntity instanceof EntityPlayer))
569            {
570                super.updateEntityWithOptionalForce(par1Entity, par2);
571            }
572        }
573    
574        /**
575         * direct call to super.updateEntityWithOptionalForce
576         */
577        public void uncheckedUpdateEntity(Entity par1Entity, boolean par2)
578        {
579            super.updateEntityWithOptionalForce(par1Entity, par2);
580        }
581    
582        /**
583         * Creates the chunk provider for this world. Called in the constructor. Retrieves provider from worldProvider?
584         */
585        protected IChunkProvider createChunkProvider()
586        {
587            IChunkLoader var1 = this.saveHandler.getChunkLoader(this.provider);
588            this.theChunkProviderServer = new ChunkProviderServer(this, var1, this.provider.getChunkProvider());
589            return this.theChunkProviderServer;
590        }
591    
592        /**
593         * pars: min x,y,z , max x,y,z
594         */
595        public List getAllTileEntityInBox(int par1, int par2, int par3, int par4, int par5, int par6)
596        {
597            ArrayList var7 = new ArrayList();
598    
599            for(int x = (par1 >> 4); x <= (par4 >> 4); x++)
600            {
601                for(int z = (par3 >> 4); z <= (par6 >> 4); z++)
602                {
603                    Chunk chunk = getChunkFromChunkCoords(x, z);
604                    if (chunk != null)
605                    {
606                        for(Object obj : chunk.chunkTileEntityMap.values())
607                        {
608                            TileEntity entity = (TileEntity)obj;
609                            if (!entity.isInvalid())
610                            {
611                                if (entity.xCoord >= par1 && entity.yCoord >= par2 && entity.zCoord >= par3 &&
612                                    entity.xCoord <= par4 && entity.yCoord <= par5 && entity.zCoord <= par6)
613                                {
614                                    var7.add(entity);
615                                }
616                            }
617                        }
618                    }
619                }
620            }
621    
622            return var7;
623        }
624    
625        /**
626         * Called when checking if a certain block can be mined or not. The 'spawn safe zone' check is located here.
627         */
628        public boolean canMineBlock(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
629        {
630            return super.canMineBlock(par1EntityPlayer, par2, par3, par4);
631        }
632    
633        public boolean canMineBlockBody(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
634        {
635            int var5 = MathHelper.abs_int(par2 - this.worldInfo.getSpawnX());
636            int var6 = MathHelper.abs_int(par4 - this.worldInfo.getSpawnZ());
637    
638            if (var5 > var6)
639            {
640                var6 = var5;
641            }
642    
643            return var6 > mcServer.func_82357_ak() || this.mcServer.getConfigurationManager().areCommandsAllowed(par1EntityPlayer.username) || this.mcServer.isSinglePlayer();
644        }
645    
646        protected void initialize(WorldSettings par1WorldSettings)
647        {
648            if (this.entityIdMap == null)
649            {
650                this.entityIdMap = new IntHashMap();
651            }
652    
653            if (this.field_73064_N == null)
654            {
655                this.field_73064_N = new HashSet();
656            }
657    
658            if (this.pendingTickListEntries == null)
659            {
660                this.pendingTickListEntries = new TreeSet();
661            }
662    
663            this.createSpawnPosition(par1WorldSettings);
664            super.initialize(par1WorldSettings);
665        }
666    
667        /**
668         * creates a spawn position at random within 256 blocks of 0,0
669         */
670        protected void createSpawnPosition(WorldSettings par1WorldSettings)
671        {
672            if (!this.provider.canRespawnHere())
673            {
674                this.worldInfo.setSpawnPosition(0, this.provider.getAverageGroundLevel(), 0);
675            }
676            else
677            {
678                this.findingSpawnPoint = true;
679                WorldChunkManager var2 = this.provider.worldChunkMgr;
680                List var3 = var2.getBiomesToSpawnIn();
681                Random var4 = new Random(this.getSeed());
682                ChunkPosition var5 = var2.findBiomePosition(0, 0, 256, var3, var4);
683                int var6 = 0;
684                int var7 = this.provider.getAverageGroundLevel();
685                int var8 = 0;
686    
687                if (var5 != null)
688                {
689                    var6 = var5.x;
690                    var8 = var5.z;
691                }
692                else
693                {
694                    System.out.println("Unable to find spawn biome");
695                }
696    
697                int var9 = 0;
698    
699                while (!this.provider.canCoordinateBeSpawn(var6, var8))
700                {
701                    var6 += var4.nextInt(64) - var4.nextInt(64);
702                    var8 += var4.nextInt(64) - var4.nextInt(64);
703                    ++var9;
704    
705                    if (var9 == 1000)
706                    {
707                        break;
708                    }
709                }
710    
711                this.worldInfo.setSpawnPosition(var6, var7, var8);
712                this.findingSpawnPoint = false;
713    
714                if (par1WorldSettings.isBonusChestEnabled())
715                {
716                    this.createBonusChest();
717                }
718            }
719        }
720    
721        /**
722         * Creates the bonus chest in the world.
723         */
724        protected void createBonusChest()
725        {
726            WorldGeneratorBonusChest var1 = new WorldGeneratorBonusChest(ChestGenHooks.getItems(BONUS_CHEST), ChestGenHooks.getCount(BONUS_CHEST, rand));
727    
728            for (int var2 = 0; var2 < 10; ++var2)
729            {
730                int var3 = this.worldInfo.getSpawnX() + this.rand.nextInt(6) - this.rand.nextInt(6);
731                int var4 = this.worldInfo.getSpawnZ() + this.rand.nextInt(6) - this.rand.nextInt(6);
732                int var5 = this.getTopSolidOrLiquidBlock(var3, var4) + 1;
733    
734                if (var1.generate(this, this.rand, var3, var5, var4))
735                {
736                    break;
737                }
738            }
739        }
740    
741        /**
742         * Gets the hard-coded portal location to use when entering this dimension.
743         */
744        public ChunkCoordinates getEntrancePortalLocation()
745        {
746            return this.provider.getEntrancePortalLocation();
747        }
748    
749        /**
750         * Saves all chunks to disk while updating progress bar.
751         */
752        public void saveAllChunks(boolean par1, IProgressUpdate par2IProgressUpdate) throws MinecraftException
753        {
754            if (this.chunkProvider.canSave())
755            {
756                if (par2IProgressUpdate != null)
757                {
758                    par2IProgressUpdate.displayProgressMessage("Saving level");
759                }
760    
761                this.saveLevel();
762    
763                if (par2IProgressUpdate != null)
764                {
765                    par2IProgressUpdate.resetProgresAndWorkingMessage("Saving chunks");
766                }
767    
768                this.chunkProvider.saveChunks(par1, par2IProgressUpdate);
769                MinecraftForge.EVENT_BUS.post(new WorldEvent.Save(this));
770            }
771        }
772    
773        /**
774         * Saves the chunks to disk.
775         */
776        protected void saveLevel() throws MinecraftException
777        {
778            this.checkSessionLock();
779            this.saveHandler.saveWorldInfoWithPlayer(this.worldInfo, this.mcServer.getConfigurationManager().getTagsFromLastWrite());
780            this.mapStorage.saveAllData();
781            this.perWorldStorage.saveAllData();
782        }
783    
784        /**
785         * Start the skin for this entity downloading, if necessary, and increment its reference counter
786         */
787        protected void obtainEntitySkin(Entity par1Entity)
788        {
789            super.obtainEntitySkin(par1Entity);
790            this.entityIdMap.addKey(par1Entity.entityId, par1Entity);
791            Entity[] var2 = par1Entity.getParts();
792    
793            if (var2 != null)
794            {
795                Entity[] var3 = var2;
796                int var4 = var2.length;
797    
798                for (int var5 = 0; var5 < var4; ++var5)
799                {
800                    Entity var6 = var3[var5];
801                    this.entityIdMap.addKey(var6.entityId, var6);
802                }
803            }
804        }
805    
806        /**
807         * Decrement the reference counter for this entity's skin image data
808         */
809        protected void releaseEntitySkin(Entity par1Entity)
810        {
811            super.releaseEntitySkin(par1Entity);
812            this.entityIdMap.removeObject(par1Entity.entityId);
813            Entity[] var2 = par1Entity.getParts();
814    
815            if (var2 != null)
816            {
817                Entity[] var3 = var2;
818                int var4 = var2.length;
819    
820                for (int var5 = 0; var5 < var4; ++var5)
821                {
822                    Entity var6 = var3[var5];
823                    this.entityIdMap.removeObject(var6.entityId);
824                }
825            }
826        }
827    
828        /**
829         * Returns the Entity with the given ID, or null if it doesn't exist in this World.
830         */
831        public Entity getEntityByID(int par1)
832        {
833            return (Entity)this.entityIdMap.lookup(par1);
834        }
835    
836        /**
837         * adds a lightning bolt to the list of lightning bolts in this world.
838         */
839        public boolean addWeatherEffect(Entity par1Entity)
840        {
841            if (super.addWeatherEffect(par1Entity))
842            {
843                this.mcServer.getConfigurationManager().sendToAllNear(par1Entity.posX, par1Entity.posY, par1Entity.posZ, 512.0D, this.provider.dimensionId, new Packet71Weather(par1Entity));
844                return true;
845            }
846            else
847            {
848                return false;
849            }
850        }
851    
852        /**
853         * sends a Packet 38 (Entity Status) to all tracked players of that entity
854         */
855        public void setEntityState(Entity par1Entity, byte par2)
856        {
857            Packet38EntityStatus var3 = new Packet38EntityStatus(par1Entity.entityId, par2);
858            this.getEntityTracker().sendPacketToAllAssociatedPlayers(par1Entity, var3);
859        }
860    
861        /**
862         * returns a new explosion. Does initiation (at time of writing Explosion is not finished)
863         */
864        public Explosion newExplosion(Entity par1Entity, double par2, double par4, double par6, float par8, boolean par9, boolean par10)
865        {
866            Explosion var11 = new Explosion(this, par1Entity, par2, par4, par6, par8);
867            var11.isFlaming = par9;
868            var11.field_82755_b = par10;
869            var11.doExplosionA();
870            var11.doExplosionB(false);
871    
872            if (!par10)
873            {
874                var11.field_77281_g.clear();
875            }
876    
877            Iterator var12 = this.playerEntities.iterator();
878    
879            while (var12.hasNext())
880            {
881                EntityPlayer var13 = (EntityPlayer)var12.next();
882    
883                if (var13.getDistanceSq(par2, par4, par6) < 4096.0D)
884                {
885                    ((EntityPlayerMP)var13).playerNetServerHandler.sendPacketToPlayer(new Packet60Explosion(par2, par4, par6, par8, var11.field_77281_g, (Vec3)var11.func_77277_b().get(var13)));
886                }
887            }
888    
889            return var11;
890        }
891    
892        /**
893         * Adds a block event with the given Args to the blockEventCache. During the next tick(), the block specified will
894         * have its onBlockEvent handler called with the given parameters. Args: X,Y,Z, BlockID, EventID, EventParameter
895         */
896        public void addBlockEvent(int par1, int par2, int par3, int par4, int par5, int par6)
897        {
898            BlockEventData var7 = new BlockEventData(par1, par2, par3, par4, par5, par6);
899            Iterator var8 = this.blockEventCache[this.blockEventCacheIndex].iterator();
900            BlockEventData var9;
901    
902            do
903            {
904                if (!var8.hasNext())
905                {
906                    this.blockEventCache[this.blockEventCacheIndex].add(var7);
907                    return;
908                }
909    
910                var9 = (BlockEventData)var8.next();
911            }
912            while (!var9.equals(var7));
913        }
914    
915        /**
916         * Send and apply locally all pending BlockEvents to each player with 64m radius of the event.
917         */
918        private void sendAndApplyBlockEvents()
919        {
920            while (!this.blockEventCache[this.blockEventCacheIndex].isEmpty())
921            {
922                int var1 = this.blockEventCacheIndex;
923                this.blockEventCacheIndex ^= 1;
924                Iterator var2 = this.blockEventCache[var1].iterator();
925    
926                while (var2.hasNext())
927                {
928                    BlockEventData var3 = (BlockEventData)var2.next();
929    
930                    if (this.onBlockEventReceived(var3))
931                    {
932                        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()));
933                    }
934                }
935    
936                this.blockEventCache[var1].clear();
937            }
938        }
939    
940        /**
941         * Called to apply a pending BlockEvent to apply to the current world.
942         */
943        private boolean onBlockEventReceived(BlockEventData par1BlockEventData)
944        {
945            int var2 = this.getBlockId(par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ());
946    
947            if (var2 == par1BlockEventData.getBlockID())
948            {
949                Block.blocksList[var2].onBlockEventReceived(this, par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ(), par1BlockEventData.getEventID(), par1BlockEventData.getEventParameter());
950                return true;
951            }
952            else
953            {
954                return false;
955            }
956        }
957    
958        /**
959         * Syncs all changes to disk and wait for completion.
960         */
961        public void flush()
962        {
963            this.saveHandler.flush();
964        }
965    
966        /**
967         * Updates all weather states.
968         */
969        protected void updateWeather()
970        {
971            boolean var1 = this.isRaining();
972            super.updateWeather();
973    
974            if (var1 != this.isRaining())
975            {
976                if (var1)
977                {
978                    this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(2, 0));
979                }
980                else
981                {
982                    this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(1, 0));
983                }
984            }
985        }
986    
987        /**
988         * Gets the MinecraftServer.
989         */
990        public MinecraftServer getMinecraftServer()
991        {
992            return this.mcServer;
993        }
994    
995        /**
996         * Gets the EntityTracker
997         */
998        public EntityTracker getEntityTracker()
999        {
1000            return this.theEntityTracker;
1001        }
1002    
1003        public PlayerManager getPlayerManager()
1004        {
1005            return this.thePlayerManager;
1006        }
1007    
1008        public File getChunkSaveLocation()
1009        {
1010            return ((AnvilChunkLoader)theChunkProviderServer.currentChunkLoader).chunkSaveLocation;
1011        }
1012    }