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.func_82572_b(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.func_82742_i();
471            }
472    
473            super.updateEntities();
474        }
475    
476        public void func_82742_i()
477        {
478            this.updateEntityTick = 0;
479        }
480    
481        /**
482         * Runs through the list of updates to run and ticks them
483         */
484        public boolean tickUpdates(boolean par1)
485        {
486            int var2 = this.pendingTickListEntries.size();
487    
488            if (var2 != this.field_73064_N.size())
489            {
490                throw new IllegalStateException("TickNextTick list out of synch");
491            }
492            else
493            {
494                if (var2 > 1000)
495                {
496                    var2 = 1000;
497                }
498    
499                for (int var3 = 0; var3 < var2; ++var3)
500                {
501                    NextTickListEntry var4 = (NextTickListEntry)this.pendingTickListEntries.first();
502    
503                    if (!par1 && var4.scheduledTime > this.worldInfo.getWorldTotalTime())
504                    {
505                        break;
506                    }
507    
508                    this.pendingTickListEntries.remove(var4);
509                    this.field_73064_N.remove(var4);
510                    boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(var4.xCoord >> 4, var4.zCoord >> 4));
511                    byte var5 = isForced ? (byte)0 : 8;
512    
513                    if (this.checkChunksExist(var4.xCoord - var5, var4.yCoord - var5, var4.zCoord - var5, var4.xCoord + var5, var4.yCoord + var5, var4.zCoord + var5))
514                    {
515                        int var6 = this.getBlockId(var4.xCoord, var4.yCoord, var4.zCoord);
516    
517                        if (var6 == var4.blockID && var6 > 0)
518                        {
519                            try
520                            {
521                                Block.blocksList[var6].updateTick(this, var4.xCoord, var4.yCoord, var4.zCoord, this.rand);
522                            }
523                            catch (Throwable var13)
524                            {
525                                CrashReport var8 = CrashReport.func_85055_a(var13, "Exception while ticking a block");
526                                CrashReportCategory var9 = var8.func_85058_a("Block being ticked");
527                                int var10;
528    
529                                try
530                                {
531                                    var10 = this.getBlockMetadata(var4.xCoord, var4.yCoord, var4.zCoord);
532                                }
533                                catch (Throwable var12)
534                                {
535                                    var10 = -1;
536                                }
537    
538                                CrashReportCategory.func_85068_a(var9, var4.xCoord, var4.yCoord, var4.zCoord, var6, var10);
539                                throw new ReportedException(var8);
540                            }
541                        }
542                    }
543                }
544    
545                return !this.pendingTickListEntries.isEmpty();
546            }
547        }
548    
549        public List getPendingBlockUpdates(Chunk par1Chunk, boolean par2)
550        {
551            ArrayList var3 = null;
552            ChunkCoordIntPair var4 = par1Chunk.getChunkCoordIntPair();
553            int var5 = var4.chunkXPos << 4;
554            int var6 = var5 + 16;
555            int var7 = var4.chunkZPos << 4;
556            int var8 = var7 + 16;
557            Iterator var9 = this.pendingTickListEntries.iterator();
558    
559            while (var9.hasNext())
560            {
561                NextTickListEntry var10 = (NextTickListEntry)var9.next();
562    
563                if (var10.xCoord >= var5 && var10.xCoord < var6 && var10.zCoord >= var7 && var10.zCoord < var8)
564                {
565                    if (par2)
566                    {
567                        this.field_73064_N.remove(var10);
568                        var9.remove();
569                    }
570    
571                    if (var3 == null)
572                    {
573                        var3 = new ArrayList();
574                    }
575    
576                    var3.add(var10);
577                }
578            }
579    
580            return var3;
581        }
582    
583        /**
584         * Will update the entity in the world if the chunk the entity is in is currently loaded or its forced to update.
585         * Args: entity, forceUpdate
586         */
587        public void updateEntityWithOptionalForce(Entity par1Entity, boolean par2)
588        {
589            if (!this.mcServer.getCanSpawnAnimals() && (par1Entity instanceof EntityAnimal || par1Entity instanceof EntityWaterMob))
590            {
591                par1Entity.setDead();
592            }
593    
594            if (!this.mcServer.getCanSpawnNPCs() && par1Entity instanceof INpc)
595            {
596                par1Entity.setDead();
597            }
598    
599            if (!(par1Entity.riddenByEntity instanceof EntityPlayer))
600            {
601                super.updateEntityWithOptionalForce(par1Entity, par2);
602            }
603        }
604    
605        /**
606         * direct call to super.updateEntityWithOptionalForce
607         */
608        public void uncheckedUpdateEntity(Entity par1Entity, boolean par2)
609        {
610            super.updateEntityWithOptionalForce(par1Entity, par2);
611        }
612    
613        /**
614         * Creates the chunk provider for this world. Called in the constructor. Retrieves provider from worldProvider?
615         */
616        protected IChunkProvider createChunkProvider()
617        {
618            IChunkLoader var1 = this.saveHandler.getChunkLoader(this.provider);
619            this.theChunkProviderServer = new ChunkProviderServer(this, var1, this.provider.getChunkProvider());
620            return this.theChunkProviderServer;
621        }
622    
623        /**
624         * pars: min x,y,z , max x,y,z
625         */
626        public List getAllTileEntityInBox(int par1, int par2, int par3, int par4, int par5, int par6)
627        {
628            ArrayList var7 = new ArrayList();
629    
630            for(int x = (par1 >> 4); x <= (par4 >> 4); x++)
631            {
632                for(int z = (par3 >> 4); z <= (par6 >> 4); z++)
633                {
634                    Chunk chunk = getChunkFromChunkCoords(x, z);
635                    if (chunk != null)
636                    {
637                        for(Object obj : chunk.chunkTileEntityMap.values())
638                        {
639                            TileEntity entity = (TileEntity)obj;
640                            if (!entity.isInvalid())
641                            {
642                                if (entity.xCoord >= par1 && entity.yCoord >= par2 && entity.zCoord >= par3 &&
643                                    entity.xCoord <= par4 && entity.yCoord <= par5 && entity.zCoord <= par6)
644                                {
645                                    var7.add(entity);
646                                }
647                            }
648                        }
649                    }
650                }
651            }
652            return var7;
653        }
654    
655        /**
656         * Called when checking if a certain block can be mined or not. The 'spawn safe zone' check is located here.
657         */
658        public boolean canMineBlock(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
659        {
660            return super.canMineBlock(par1EntityPlayer, par2, par3, par4);
661        }
662    
663        public boolean canMineBlockBody(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
664        {
665            int var5 = MathHelper.abs_int(par2 - this.worldInfo.getSpawnX());
666            int var6 = MathHelper.abs_int(par4 - this.worldInfo.getSpawnZ());
667    
668            if (var5 > var6)
669            {
670                var6 = var5;
671            }
672    
673            return var6 > mcServer.getSpawnProtectionSize() || this.mcServer.getConfigurationManager().areCommandsAllowed(par1EntityPlayer.username) || this.mcServer.isSinglePlayer();
674        }
675    
676        protected void initialize(WorldSettings par1WorldSettings)
677        {
678            if (this.entityIdMap == null)
679            {
680                this.entityIdMap = new IntHashMap();
681            }
682    
683            if (this.field_73064_N == null)
684            {
685                this.field_73064_N = new HashSet();
686            }
687    
688            if (this.pendingTickListEntries == null)
689            {
690                this.pendingTickListEntries = new TreeSet();
691            }
692    
693            this.createSpawnPosition(par1WorldSettings);
694            super.initialize(par1WorldSettings);
695        }
696    
697        /**
698         * creates a spawn position at random within 256 blocks of 0,0
699         */
700        protected void createSpawnPosition(WorldSettings par1WorldSettings)
701        {
702            if (!this.provider.canRespawnHere())
703            {
704                this.worldInfo.setSpawnPosition(0, this.provider.getAverageGroundLevel(), 0);
705            }
706            else
707            {
708                this.findingSpawnPoint = true;
709                WorldChunkManager var2 = this.provider.worldChunkMgr;
710                List var3 = var2.getBiomesToSpawnIn();
711                Random var4 = new Random(this.getSeed());
712                ChunkPosition var5 = var2.findBiomePosition(0, 0, 256, var3, var4);
713                int var6 = 0;
714                int var7 = this.provider.getAverageGroundLevel();
715                int var8 = 0;
716    
717                if (var5 != null)
718                {
719                    var6 = var5.x;
720                    var8 = var5.z;
721                }
722                else
723                {
724                    System.out.println("Unable to find spawn biome");
725                }
726    
727                int var9 = 0;
728    
729                while (!this.provider.canCoordinateBeSpawn(var6, var8))
730                {
731                    var6 += var4.nextInt(64) - var4.nextInt(64);
732                    var8 += var4.nextInt(64) - var4.nextInt(64);
733                    ++var9;
734    
735                    if (var9 == 1000)
736                    {
737                        break;
738                    }
739                }
740    
741                this.worldInfo.setSpawnPosition(var6, var7, var8);
742                this.findingSpawnPoint = false;
743    
744                if (par1WorldSettings.isBonusChestEnabled())
745                {
746                    this.createBonusChest();
747                }
748            }
749        }
750    
751        /**
752         * Creates the bonus chest in the world.
753         */
754        protected void createBonusChest()
755        {
756            WorldGeneratorBonusChest var1 = new WorldGeneratorBonusChest(ChestGenHooks.getItems(BONUS_CHEST), ChestGenHooks.getCount(BONUS_CHEST, rand));
757    
758            for (int var2 = 0; var2 < 10; ++var2)
759            {
760                int var3 = this.worldInfo.getSpawnX() + this.rand.nextInt(6) - this.rand.nextInt(6);
761                int var4 = this.worldInfo.getSpawnZ() + this.rand.nextInt(6) - this.rand.nextInt(6);
762                int var5 = this.getTopSolidOrLiquidBlock(var3, var4) + 1;
763    
764                if (var1.generate(this, this.rand, var3, var5, var4))
765                {
766                    break;
767                }
768            }
769        }
770    
771        /**
772         * Gets the hard-coded portal location to use when entering this dimension.
773         */
774        public ChunkCoordinates getEntrancePortalLocation()
775        {
776            return this.provider.getEntrancePortalLocation();
777        }
778    
779        /**
780         * Saves all chunks to disk while updating progress bar.
781         */
782        public void saveAllChunks(boolean par1, IProgressUpdate par2IProgressUpdate) throws MinecraftException
783        {
784            if (this.chunkProvider.canSave())
785            {
786                if (par2IProgressUpdate != null)
787                {
788                    par2IProgressUpdate.displayProgressMessage("Saving level");
789                }
790    
791                this.saveLevel();
792    
793                if (par2IProgressUpdate != null)
794                {
795                    par2IProgressUpdate.resetProgresAndWorkingMessage("Saving chunks");
796                }
797    
798                this.chunkProvider.saveChunks(par1, par2IProgressUpdate);
799                MinecraftForge.EVENT_BUS.post(new WorldEvent.Save(this));
800            }
801        }
802    
803        /**
804         * Saves the chunks to disk.
805         */
806        protected void saveLevel() throws MinecraftException
807        {
808            this.checkSessionLock();
809            this.saveHandler.saveWorldInfoWithPlayer(this.worldInfo, this.mcServer.getConfigurationManager().getTagsFromLastWrite());
810            this.mapStorage.saveAllData();
811            this.perWorldStorage.saveAllData();
812        }
813    
814        /**
815         * Start the skin for this entity downloading, if necessary, and increment its reference counter
816         */
817        protected void obtainEntitySkin(Entity par1Entity)
818        {
819            super.obtainEntitySkin(par1Entity);
820            this.entityIdMap.addKey(par1Entity.entityId, par1Entity);
821            Entity[] var2 = par1Entity.getParts();
822    
823            if (var2 != null)
824            {
825                for (int var3 = 0; var3 < var2.length; ++var3)
826                {
827                    this.entityIdMap.addKey(var2[var3].entityId, var2[var3]);
828                }
829            }
830        }
831    
832        /**
833         * Decrement the reference counter for this entity's skin image data
834         */
835        protected void releaseEntitySkin(Entity par1Entity)
836        {
837            super.releaseEntitySkin(par1Entity);
838            this.entityIdMap.removeObject(par1Entity.entityId);
839            Entity[] var2 = par1Entity.getParts();
840    
841            if (var2 != null)
842            {
843                for (int var3 = 0; var3 < var2.length; ++var3)
844                {
845                    this.entityIdMap.removeObject(var2[var3].entityId);
846                }
847            }
848        }
849    
850        /**
851         * Returns the Entity with the given ID, or null if it doesn't exist in this World.
852         */
853        public Entity getEntityByID(int par1)
854        {
855            return (Entity)this.entityIdMap.lookup(par1);
856        }
857    
858        /**
859         * adds a lightning bolt to the list of lightning bolts in this world.
860         */
861        public boolean addWeatherEffect(Entity par1Entity)
862        {
863            if (super.addWeatherEffect(par1Entity))
864            {
865                this.mcServer.getConfigurationManager().sendToAllNear(par1Entity.posX, par1Entity.posY, par1Entity.posZ, 512.0D, this.provider.dimensionId, new Packet71Weather(par1Entity));
866                return true;
867            }
868            else
869            {
870                return false;
871            }
872        }
873    
874        /**
875         * sends a Packet 38 (Entity Status) to all tracked players of that entity
876         */
877        public void setEntityState(Entity par1Entity, byte par2)
878        {
879            Packet38EntityStatus var3 = new Packet38EntityStatus(par1Entity.entityId, par2);
880            this.getEntityTracker().sendPacketToAllAssociatedPlayers(par1Entity, var3);
881        }
882    
883        /**
884         * returns a new explosion. Does initiation (at time of writing Explosion is not finished)
885         */
886        public Explosion newExplosion(Entity par1Entity, double par2, double par4, double par6, float par8, boolean par9, boolean par10)
887        {
888            Explosion var11 = new Explosion(this, par1Entity, par2, par4, par6, par8);
889            var11.isFlaming = par9;
890            var11.isSmoking = par10;
891            var11.doExplosionA();
892            var11.doExplosionB(false);
893    
894            if (!par10)
895            {
896                var11.affectedBlockPositions.clear();
897            }
898    
899            Iterator var12 = this.playerEntities.iterator();
900    
901            while (var12.hasNext())
902            {
903                EntityPlayer var13 = (EntityPlayer)var12.next();
904    
905                if (var13.getDistanceSq(par2, par4, par6) < 4096.0D)
906                {
907                    ((EntityPlayerMP)var13).playerNetServerHandler.sendPacketToPlayer(new Packet60Explosion(par2, par4, par6, par8, var11.affectedBlockPositions, (Vec3)var11.func_77277_b().get(var13)));
908                }
909            }
910    
911            return var11;
912        }
913    
914        /**
915         * Adds a block event with the given Args to the blockEventCache. During the next tick(), the block specified will
916         * have its onBlockEvent handler called with the given parameters. Args: X,Y,Z, BlockID, EventID, EventParameter
917         */
918        public void addBlockEvent(int par1, int par2, int par3, int par4, int par5, int par6)
919        {
920            BlockEventData var7 = new BlockEventData(par1, par2, par3, par4, par5, par6);
921            Iterator var8 = this.blockEventCache[this.blockEventCacheIndex].iterator();
922            BlockEventData var9;
923    
924            do
925            {
926                if (!var8.hasNext())
927                {
928                    this.blockEventCache[this.blockEventCacheIndex].add(var7);
929                    return;
930                }
931    
932                var9 = (BlockEventData)var8.next();
933            }
934            while (!var9.equals(var7));
935        }
936    
937        /**
938         * Send and apply locally all pending BlockEvents to each player with 64m radius of the event.
939         */
940        private void sendAndApplyBlockEvents()
941        {
942            while (!this.blockEventCache[this.blockEventCacheIndex].isEmpty())
943            {
944                int var1 = this.blockEventCacheIndex;
945                this.blockEventCacheIndex ^= 1;
946                Iterator var2 = this.blockEventCache[var1].iterator();
947    
948                while (var2.hasNext())
949                {
950                    BlockEventData var3 = (BlockEventData)var2.next();
951    
952                    if (this.onBlockEventReceived(var3))
953                    {
954                        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()));
955                    }
956                }
957    
958                this.blockEventCache[var1].clear();
959            }
960        }
961    
962        /**
963         * Called to apply a pending BlockEvent to apply to the current world.
964         */
965        private boolean onBlockEventReceived(BlockEventData par1BlockEventData)
966        {
967            int var2 = this.getBlockId(par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ());
968    
969            if (var2 == par1BlockEventData.getBlockID())
970            {
971                Block.blocksList[var2].onBlockEventReceived(this, par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ(), par1BlockEventData.getEventID(), par1BlockEventData.getEventParameter());
972                return true;
973            }
974            else
975            {
976                return false;
977            }
978        }
979    
980        /**
981         * Syncs all changes to disk and wait for completion.
982         */
983        public void flush()
984        {
985            this.saveHandler.flush();
986        }
987    
988        /**
989         * Updates all weather states.
990         */
991        protected void updateWeather()
992        {
993            boolean var1 = this.isRaining();
994            super.updateWeather();
995    
996            if (var1 != this.isRaining())
997            {
998                if (var1)
999                {
1000                    this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(2, 0));
1001                }
1002                else
1003                {
1004                    this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(1, 0));
1005                }
1006            }
1007        }
1008    
1009        /**
1010         * Gets the MinecraftServer.
1011         */
1012        public MinecraftServer getMinecraftServer()
1013        {
1014            return this.mcServer;
1015        }
1016    
1017        /**
1018         * Gets the EntityTracker
1019         */
1020        public EntityTracker getEntityTracker()
1021        {
1022            return this.theEntityTracker;
1023        }
1024    
1025        public PlayerManager getPlayerManager()
1026        {
1027            return this.thePlayerManager;
1028        }
1029    
1030        public Teleporter func_85176_s()
1031        {
1032            return this.field_85177_Q;
1033        }
1034    
1035        public File getChunkSaveLocation()
1036        {
1037            return ((AnvilChunkLoader)theChunkProviderServer.currentChunkLoader).chunkSaveLocation;
1038        }
1039    }