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