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