    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        /**
    033         * this is set related to Manager.areCommandsAllowed, but is never used is is also set back to false at the end of
    034         * both functions which set it.
    035         */
    036        public boolean actionsAllowed = false;
    037
    038        /** set by CommandServerSave{all,Off,On} */
    039        public boolean canNotSave;
    040
    041        /** is false if there are no players */
    042        private boolean allPlayersSleeping;
    043        private int updateEntityTick = 0;
    044
    045        /**
    046         * Double buffer of ServerBlockEventList[] for holding pending BlockEventData's
    047         */
    048        private ServerBlockEventList[] blockEventCache = new ServerBlockEventList[] {new ServerBlockEventList((ServerBlockEvent)null), new ServerBlockEventList((ServerBlockEvent)null)};
    049
    050        /**
    051         * The index into the blockEventCache; either 0, or 1, toggled in sendBlockEventPackets  where all BlockEvent are
    052         * applied locally and send to clients.
    053         */
    054        private int blockEventCacheIndex = 0;
    055        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)};
    056
    057        /** An IntHashMap of entity IDs (integers) to their Entity objects. */
    058        private IntHashMap entityIdMap;
    059
    060        /** Stores the recently processed (lighting) chunks */
    061        protected Set<ChunkCoordIntPair> doneChunks = new HashSet<ChunkCoordIntPair>();
    062
    063        public WorldServer(MinecraftServer par1MinecraftServer, ISaveHandler par2ISaveHandler, String par3Str, int par4, WorldSettings par5WorldSettings, Profiler par6Profiler)
    064        {
    065            super(par2ISaveHandler, par3Str, par5WorldSettings, WorldProvider.getProviderForDimension(par4), par6Profiler);
    066            this.mcServer = par1MinecraftServer;
    067            this.theEntityTracker = new EntityTracker(this);
    068            this.thePlayerManager = new PlayerManager(this, par1MinecraftServer.getConfigurationManager().getViewDistance());
    069
    070            if (this.entityIdMap == null)
    071            {
    072                this.entityIdMap = new IntHashMap();
    073            }
    074
    075            if (this.field_73064_N == null)
    076            {
    077                this.field_73064_N = new HashSet();
    078            }
    079
    080            if (this.pendingTickListEntries == null)
    081            {
    082                this.pendingTickListEntries = new TreeSet();
    083            }
    084            DimensionManager.setWorld(par4, this);
    085        }
    086
    087        /**
    088         * Runs a single tick for the world
    089         */
    090        public void tick()
    091        {
    092            super.tick();
    093
    094            if (this.getWorldInfo().isHardcoreModeEnabled() && this.difficultySetting < 3)
    095            {
    096                this.difficultySetting = 3;
    097            }
    098
    099            this.provider.worldChunkMgr.cleanupCache();
    100
    101            if (this.areAllPlayersAsleep())
    102            {
    103                boolean var1 = false;
    104
    105                if (this.spawnHostileMobs && this.difficultySetting >= 1)
    106                {
    107                    ;
    108                }
    109
    110                if (!var1)
    111                {
    112                    long var2 = this.worldInfo.getWorldTime() + 24000L;
    113                    this.worldInfo.setWorldTime(var2 - var2 % 24000L);
    114                    this.wakeAllPlayers();
    115                }
    116            }
    117
    118            this.theProfiler.startSection("mobSpawner");
    119            SpawnerAnimals.findChunksForSpawning(this, this.spawnHostileMobs, this.spawnPeacefulMobs && this.worldInfo.getWorldTime() % 400L == 0L);
    120            this.theProfiler.endStartSection("chunkSource");
    121            this.chunkProvider.unload100OldestChunks();
    122            int var4 = this.calculateSkylightSubtracted(1.0F);
    123
    124            if (var4 != this.skylightSubtracted)
    125            {
    126                this.skylightSubtracted = var4;
    127            }
    128
    129            this.sendAndApplyBlockEvents();
    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            NextTickListEntry var6 = new NextTickListEntry(par1, par2, par3, par4);
    383            boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(var6.xCoord >> 4, var6.zCoord >> 4));
    384            byte var7 = isForced ? (byte)0 : 8;
    385
    386            if (this.scheduledUpdatesAreImmediate)
    387            {
    388                if (this.checkChunksExist(var6.xCoord - var7, var6.yCoord - var7, var6.zCoord - var7, var6.xCoord + var7, var6.yCoord + var7, var6.zCoord + var7))
    389                {
    390                    int var8 = this.getBlockId(var6.xCoord, var6.yCoord, var6.zCoord);
    391
    392                    if (var8 == var6.blockID && var8 > 0)
    393                    {
    394                        Block.blocksList[var8].updateTick(this, var6.xCoord, var6.yCoord, var6.zCoord, this.rand);
    395                    }
    396                }
    397            }
    398            else
    399            {
    400                if (this.checkChunksExist(par1 - var7, par2 - var7, par3 - var7, par1 + var7, par2 + var7, par3 + var7))
    401                {
    402                    if (par4 > 0)
    403                    {
    404                        var6.setScheduledTime((long)par5 + this.worldInfo.getWorldTime());
    405                    }
    406
    407                    if (!this.field_73064_N.contains(var6))
    408                    {
    409                        this.field_73064_N.add(var6);
    410                        this.pendingTickListEntries.add(var6);
    411                    }
    412                }
    413            }
    414        }
    415
    416        /**
    417         * Schedules a block update from the saved information in a chunk. Called when the chunk is loaded.
    418         */
    419        public void scheduleBlockUpdateFromLoad(int par1, int par2, int par3, int par4, int par5)
    420        {
    421            NextTickListEntry var6 = new NextTickListEntry(par1, par2, par3, par4);
    422
    423            if (par4 > 0)
    424            {
    425                var6.setScheduledTime((long)par5 + this.worldInfo.getWorldTime());
    426            }
    427
    428            if (!this.field_73064_N.contains(var6))
    429            {
    430                this.field_73064_N.add(var6);
    431                this.pendingTickListEntries.add(var6);
    432            }
    433        }
    434
    435        /**
    436         * Updates (and cleans up) entities and tile entities
    437         */
    438        public void updateEntities()
    439        {
    440            if (this.playerEntities.isEmpty() && getPersistentChunks().isEmpty())
    441            {
    442                if (this.updateEntityTick++ >= 60)
    443                {
    444                    return;
    445                }
    446            }
    447            else
    448            {
    449                this.updateEntityTick = 0;
    450            }
    451
    452            super.updateEntities();
    453        }
    454
    455        /**
    456         * Runs through the list of updates to run and ticks them
    457         */
    458        public boolean tickUpdates(boolean par1)
    459        {
    460            int var2 = this.pendingTickListEntries.size();
    461
    462            if (var2 != this.field_73064_N.size())
    463            {
    464                throw new IllegalStateException("TickNextTick list out of synch");
    465            }
    466            else
    467            {
    468                if (var2 > 1000)
    469                {
    470                    var2 = 1000;
    471                }
    472
    473                for (int var3 = 0; var3 < var2; ++var3)
    474                {
    475                    NextTickListEntry var4 = (NextTickListEntry)this.pendingTickListEntries.first();
    476
    477                    if (!par1 && var4.scheduledTime > this.worldInfo.getWorldTime())
    478                    {
    479                        break;
    480                    }
    481
    482                    this.pendingTickListEntries.remove(var4);
    483                    this.field_73064_N.remove(var4);
    484                    boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(var4.xCoord >> 4, var4.zCoord >> 4));
    485                    byte var5 = isForced ? (byte)0 : 8;
    486
    487                    if (this.checkChunksExist(var4.xCoord - var5, var4.yCoord - var5, var4.zCoord - var5, var4.xCoord + var5, var4.yCoord + var5, var4.zCoord + var5))
    488                    {
    489                        int var6 = this.getBlockId(var4.xCoord, var4.yCoord, var4.zCoord);
    490
    491                        if (var6 == var4.blockID && var6 > 0)
    492                        {
    493                            Block.blocksList[var6].updateTick(this, var4.xCoord, var4.yCoord, var4.zCoord, this.rand);
    494                        }
    495                    }
    496                }
    497
    498                return !this.pendingTickListEntries.isEmpty();
    499            }
    500        }
    501
    502        public List getPendingBlockUpdates(Chunk par1Chunk, boolean par2)
    503        {
    504            ArrayList var3 = null;
    505            ChunkCoordIntPair var4 = par1Chunk.getChunkCoordIntPair();
    506            int var5 = var4.chunkXPos << 4;
    507            int var6 = var5 + 16;
    508            int var7 = var4.chunkZPos << 4;
    509            int var8 = var7 + 16;
    510            Iterator var9 = this.pendingTickListEntries.iterator();
    511
    512            while (var9.hasNext())
    513            {
    514                NextTickListEntry var10 = (NextTickListEntry)var9.next();
    515
    516                if (var10.xCoord >= var5 && var10.xCoord < var6 && var10.zCoord >= var7 && var10.zCoord < var8)
    517                {
    518                    if (par2)
    519                    {
    520                        this.field_73064_N.remove(var10);
    521                        var9.remove();
    522                    }
    523
    524                    if (var3 == null)
    525                    {
    526                        var3 = new ArrayList();
    527                    }
    528
    529                    var3.add(var10);
    530                }
    531            }
    532
    533            return var3;
    534        }
    535
    536        /**
    537         * Will update the entity in the world if the chunk the entity is in is currently loaded or its forced to update.
    538         * Args: entity, forceUpdate
    539         */
    540        public void updateEntityWithOptionalForce(Entity par1Entity, boolean par2)
    541        {
    542            if (!this.mcServer.getCanSpawnAnimals() && (par1Entity instanceof EntityAnimal || par1Entity instanceof EntityWaterMob))
    543            {
    544                par1Entity.setDead();
    545            }
    546
    547            if (!this.mcServer.getCanSpawnNPCs() && par1Entity instanceof INpc)
    548            {
    549                par1Entity.setDead();
    550            }
    551
    552            if (!(par1Entity.riddenByEntity instanceof EntityPlayer))
    553            {
    554                super.updateEntityWithOptionalForce(par1Entity, par2);
    555            }
    556        }
    557
    558        /**
    559         * direct call to super.updateEntityWithOptionalForce
    560         */
    561        public void uncheckedUpdateEntity(Entity par1Entity, boolean par2)
    562        {
    563            super.updateEntityWithOptionalForce(par1Entity, par2);
    564        }
    565
    566        /**
    567         * Creates the chunk provider for this world. Called in the constructor. Retrieves provider from worldProvider?
    568         */
    569        protected IChunkProvider createChunkProvider()
    570        {
    571            IChunkLoader var1 = this.saveHandler.getChunkLoader(this.provider);
    572            this.theChunkProviderServer = new ChunkProviderServer(this, var1, this.provider.getChunkProvider());
    573            return this.theChunkProviderServer;
    574        }
    575
    576        /**
    577         * pars: min x,y,z , max x,y,z
    578         */
    579        public List getAllTileEntityInBox(int par1, int par2, int par3, int par4, int par5, int par6)
    580        {
    581            ArrayList var7 = new ArrayList();
    582
    583            for(int x = (par1 >> 4); x <= (par4 >> 4); x++)
    584            {
    585                for(int z = (par3 >> 4); z <= (par6 >> 4); z++)
    586                {
    587                    Chunk chunk = getChunkFromChunkCoords(x, z);
    588                    if (chunk != null)
    589                    {
    590                        for(Object obj : chunk.chunkTileEntityMap.values())
    591                        {
    592                            TileEntity entity = (TileEntity)obj;
    593                            if (!entity.isInvalid())
    594                            {
    595                                if (entity.xCoord >= par1 && entity.yCoord >= par2 && entity.zCoord >= par3 &&
    596                                    entity.xCoord <= par4 && entity.yCoord <= par5 && entity.zCoord <= par6)
    597                                {
    598                                    var7.add(entity);
    599                                }
    600                            }
    601                        }
    602                    }
    603                }
    604            }
    605
    606            return var7;
    607        }
    608
    609        /**
    610         * Called when checking if a certain block can be mined or not. The 'spawn safe zone' check is located here.
    611         */
    612        public boolean canMineBlock(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
    613        {
    614            return super.canMineBlock(par1EntityPlayer, par2, par3, par4);
    615        }
    616
    617        public boolean canMineBlockBody(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
    618        {
    619            int var5 = MathHelper.abs_int(par2 - this.worldInfo.getSpawnX());
    620            int var6 = MathHelper.abs_int(par4 - this.worldInfo.getSpawnZ());
    621
    622            if (var5 > var6)
    623            {
    624                var6 = var5;
    625            }
    626
    627            return var6 > mcServer.spawnProtectionSize || this.mcServer.getConfigurationManager().areCommandsAllowed(par1EntityPlayer.username) || this.mcServer.isSinglePlayer();
    628        }
    629
    630        protected void initialize(WorldSettings par1WorldSettings)
    631        {
    632            if (this.entityIdMap == null)
    633            {
    634                this.entityIdMap = new IntHashMap();
    635            }
    636
    637            if (this.field_73064_N == null)
    638            {
    639                this.field_73064_N = new HashSet();
    640            }
    641
    642            if (this.pendingTickListEntries == null)
    643            {
    644                this.pendingTickListEntries = new TreeSet();
    645            }
    646
    647            this.createSpawnPosition(par1WorldSettings);
    648            super.initialize(par1WorldSettings);
    649        }
    650
    651        /**
    652         * creates a spawn position at random within 256 blocks of 0,0
    653         */
    654        protected void createSpawnPosition(WorldSettings par1WorldSettings)
    655        {
    656            if (!this.provider.canRespawnHere())
    657            {
    658                this.worldInfo.setSpawnPosition(0, this.provider.getAverageGroundLevel(), 0);
    659            }
    660            else
    661            {
    662                this.findingSpawnPoint = true;
    663                WorldChunkManager var2 = this.provider.worldChunkMgr;
    664                List var3 = var2.getBiomesToSpawnIn();
    665                Random var4 = new Random(this.getSeed());
    666                ChunkPosition var5 = var2.findBiomePosition(0, 0, 256, var3, var4);
    667                int var6 = 0;
    668                int var7 = this.provider.getAverageGroundLevel();
    669                int var8 = 0;
    670
    671                if (var5 != null)
    672                {
    673                    var6 = var5.x;
    674                    var8 = var5.z;
    675                }
    676                else
    677                {
    678                    System.out.println("Unable to find spawn biome");
    679                }
    680
    681                int var9 = 0;
    682
    683                while (!this.provider.canCoordinateBeSpawn(var6, var8))
    684                {
    685                    var6 += var4.nextInt(64) - var4.nextInt(64);
    686                    var8 += var4.nextInt(64) - var4.nextInt(64);
    687                    ++var9;
    688
    689                    if (var9 == 1000)
    690                    {
    691                        break;
    692                    }
    693                }
    694
    695                this.worldInfo.setSpawnPosition(var6, var7, var8);
    696                this.findingSpawnPoint = false;
    697
    698                if (par1WorldSettings.isBonusChestEnabled())
    699                {
    700                    this.createBonusChest();
    701                }
    702            }
    703        }
    704
    705        /**
    706         * Creates the bonus chest in the world.
    707         */
    708        protected void createBonusChest()
    709        {
    710            WorldGeneratorBonusChest var1 = new WorldGeneratorBonusChest(ChestGenHooks.getItems(BONUS_CHEST), ChestGenHooks.getCount(BONUS_CHEST, rand));
    711
    712            for (int var2 = 0; var2 < 10; ++var2)
    713            {
    714                int var3 = this.worldInfo.getSpawnX() + this.rand.nextInt(6) - this.rand.nextInt(6);
    715                int var4 = this.worldInfo.getSpawnZ() + this.rand.nextInt(6) - this.rand.nextInt(6);
    716                int var5 = this.getTopSolidOrLiquidBlock(var3, var4) + 1;
    717
    718                if (var1.generate(this, this.rand, var3, var5, var4))
    719                {
    720                    break;
    721                }
    722            }
    723        }
    724
    725        /**
    726         * Gets the hard-coded portal location to use when entering this dimension.
    727         */
    728        public ChunkCoordinates getEntrancePortalLocation()
    729        {
    730            return this.provider.getEntrancePortalLocation();
    731        }
    732
    733        /**
    734         * Saves all chunks to disk while updating progress bar.
    735         */
    736        public void saveAllChunks(boolean par1, IProgressUpdate par2IProgressUpdate) throws MinecraftException
    737        {
    738            if (this.chunkProvider.canSave())
    739            {
    740                if (par2IProgressUpdate != null)
    741                {
    742                    par2IProgressUpdate.displayProgressMessage("Saving level");
    743                }
    744
    745                this.saveLevel();
    746
    747                if (par2IProgressUpdate != null)
    748                {
    749                    par2IProgressUpdate.resetProgresAndWorkingMessage("Saving chunks");
    750                }
    751
    752                this.chunkProvider.saveChunks(par1, par2IProgressUpdate);
    753                MinecraftForge.EVENT_BUS.post(new WorldEvent.Save(this));
    754            }
    755        }
    756
    757        /**
    758         * Saves the chunks to disk.
    759         */
    760        protected void saveLevel() throws MinecraftException
    761        {
    762            this.checkSessionLock();
    763            this.saveHandler.saveWorldInfoWithPlayer(this.worldInfo, this.mcServer.getConfigurationManager().getTagsFromLastWrite());
    764            this.mapStorage.saveAllData();
    765        }
    766
    767        /**
    768         * Start the skin for this entity downloading, if necessary, and increment its reference counter
    769         */
    770        protected void obtainEntitySkin(Entity par1Entity)
    771        {
    772            super.obtainEntitySkin(par1Entity);
    773            this.entityIdMap.addKey(par1Entity.entityId, par1Entity);
    774            Entity[] var2 = par1Entity.getParts();
    775
    776            if (var2 != null)
    777            {
    778                Entity[] var3 = var2;
    779                int var4 = var2.length;
    780
    781                for (int var5 = 0; var5 < var4; ++var5)
    782                {
    783                    Entity var6 = var3[var5];
    784                    this.entityIdMap.addKey(var6.entityId, var6);
    785                }
    786            }
    787        }
    788
    789        /**
    790         * Decrement the reference counter for this entity's skin image data
    791         */
    792        protected void releaseEntitySkin(Entity par1Entity)
    793        {
    794            super.releaseEntitySkin(par1Entity);
    795            this.entityIdMap.removeObject(par1Entity.entityId);
    796            Entity[] var2 = par1Entity.getParts();
    797
    798            if (var2 != null)
    799            {
    800                Entity[] var3 = var2;
    801                int var4 = var2.length;
    802
    803                for (int var5 = 0; var5 < var4; ++var5)
    804                {
    805                    Entity var6 = var3[var5];
    806                    this.entityIdMap.removeObject(var6.entityId);
    807                }
    808            }
    809        }
    810
    811        /**
    812         * Returns the Entity with the given ID, or null if it doesn't exist in this World.
    813         */
    814        public Entity getEntityByID(int par1)
    815        {
    816            return (Entity)this.entityIdMap.lookup(par1);
    817        }
    818
    819        /**
    820         * adds a lightning bolt to the list of lightning bolts in this world.
    821         */
    822        public boolean addWeatherEffect(Entity par1Entity)
    823        {
    824            if (super.addWeatherEffect(par1Entity))
    825            {
    826                this.mcServer.getConfigurationManager().sendToAllNear(par1Entity.posX, par1Entity.posY, par1Entity.posZ, 512.0D, this.provider.dimensionId, new Packet71Weather(par1Entity));
    827                return true;
    828            }
    829            else
    830            {
    831                return false;
    832            }
    833        }
    834
    835        /**
    836         * sends a Packet 38 (Entity Status) to all tracked players of that entity
    837         */
    838        public void setEntityState(Entity par1Entity, byte par2)
    839        {
    840            Packet38EntityStatus var3 = new Packet38EntityStatus(par1Entity.entityId, par2);
    841            this.getEntityTracker().sendPacketToAllAssociatedPlayers(par1Entity, var3);
    842        }
    843
    844        /**
    845         * returns a new explosion. Does initiation (at time of writing Explosion is not finished)
    846         */
    847        public Explosion newExplosion(Entity par1Entity, double par2, double par4, double par6, float par8, boolean par9)
    848        {
    849            Explosion var10 = new Explosion(this, par1Entity, par2, par4, par6, par8);
    850            var10.isFlaming = par9;
    851            var10.doExplosionA();
    852            var10.doExplosionB(false);
    853            Iterator var11 = this.playerEntities.iterator();
    854
    855            while (var11.hasNext())
    856            {
    857                EntityPlayer var12 = (EntityPlayer)var11.next();
    858
    859                if (var12.getDistanceSq(par2, par4, par6) < 4096.0D)
    860                {
    861                    ((EntityPlayerMP)var12).playerNetServerHandler.sendPacketToPlayer(new Packet60Explosion(par2, par4, par6, par8, var10.field_77281_g, (Vec3)var10.func_77277_b().get(var12)));
    862                }
    863            }
    864
    865            return var10;
    866        }
    867
    868        /**
    869         * Adds a block event with the given Args to the blockEventCache. During the next tick(), the block specified will
    870         * have its onBlockEvent handler called with the given parameters. Args: X,Y,Z, BlockID, EventID, EventParameter
    871         */
    872        public void addBlockEvent(int par1, int par2, int par3, int par4, int par5, int par6)
    873        {
    874            BlockEventData var7 = new BlockEventData(par1, par2, par3, par4, par5, par6);
    875            Iterator var8 = this.blockEventCache[this.blockEventCacheIndex].iterator();
    876            BlockEventData var9;
    877
    878            do
    879            {
    880                if (!var8.hasNext())
    881                {
    882                    this.blockEventCache[this.blockEventCacheIndex].add(var7);
    883                    return;
    884                }
    885
    886                var9 = (BlockEventData)var8.next();
    887            }
    888            while (!var9.equals(var7));
    889        }
    890
    891        /**
    892         * Send and apply locally all pending BlockEvents to each player with 64m radius of the event.
    893         */
    894        private void sendAndApplyBlockEvents()
    895        {
    896            while (!this.blockEventCache[this.blockEventCacheIndex].isEmpty())
    897            {
    898                int var1 = this.blockEventCacheIndex;
    899                this.blockEventCacheIndex ^= 1;
    900                Iterator var2 = this.blockEventCache[var1].iterator();
    901
    902                while (var2.hasNext())
    903                {
    904                    BlockEventData var3 = (BlockEventData)var2.next();
    905
    906                    if (this.onBlockEventReceived(var3))
    907                    {
    908                        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()));
    909                    }
    910                }
    911
    912                this.blockEventCache[var1].clear();
    913            }
    914        }
    915
    916        /**
    917         * Called to apply a pending BlockEvent to apply to the current world.
    918         */
    919        private boolean onBlockEventReceived(BlockEventData par1BlockEventData)
    920        {
    921            int var2 = this.getBlockId(par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ());
    922
    923            if (var2 == par1BlockEventData.getBlockID())
    924            {
    925                Block.blocksList[var2].onBlockEventReceived(this, par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ(), par1BlockEventData.getEventID(), par1BlockEventData.getEventParameter());
    926                return true;
    927            }
    928            else
    929            {
    930                return false;
    931            }
    932        }
    933
    934        /**
    935         * Syncs all changes to disk and wait for completion.
    936         */
    937        public void flush()
    938        {
    939            this.saveHandler.flush();
    940        }
    941
    942        /**
    943         * Updates all weather states.
    944         */
    945        protected void updateWeather()
    946        {
    947            boolean var1 = this.isRaining();
    948            super.updateWeather();
    949
    950            if (var1 != this.isRaining())
    951            {
    952                if (var1)
    953                {
    954                    this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(2, 0));
    955                }
    956                else
    957                {
    958                    this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(1, 0));
    959                }
    960            }
    961        }
    962
    963        /**
    964         * Gets the MinecraftServer.
    965         */
    966        public MinecraftServer getMinecraftServer()
    967        {
    968            return this.mcServer;
    969        }
    970
    971        /**
    972         * Gets the EntityTracker
    973         */
    974        public EntityTracker getEntityTracker()
    975        {
    976            return this.theEntityTracker;
    977        }
    978
    979        /**
    980         * Sets the time on the given WorldServer
    981         */
    982        public void setTime(long par1)
    983        {
    984            long var3 = par1 - this.worldInfo.getWorldTime();
    985            NextTickListEntry var6;
    986
    987            for (Iterator var5 = this.field_73064_N.iterator(); var5.hasNext(); var6.scheduledTime += var3)
    988            {
    989                var6 = (NextTickListEntry)var5.next();
    990            }
    991
    992            Block[] var9 = Block.blocksList;
    993            int var10 = var9.length;
    994
    995            for (int var7 = 0; var7 < var10; ++var7)
    996            {
    997                Block var8 = var9[var7];
    998
    999                if (var8 != null)
    1000                {
    1001                    var8.onTimeChanged(this, var3, par1);
    1002                }
    1003            }
    1004
    1005            this.setWorldTime(par1);
    1006        }
    1007
    1008        public PlayerManager getPlayerManager()
    1009        {
    1010            return this.thePlayerManager;
    1011        }
    1012
    1013        public File getChunkSaveLocation()
    1014        {
    1015            return ((AnvilChunkLoader)theChunkProviderServer.currentChunkLoader).chunkSaveLocation;
    1016        }
    1017    }