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