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