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