    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
    653            return var7;
    654        }
    655
    656        /**
    657         * Called when checking if a certain block can be mined or not. The 'spawn safe zone' check is located here.
    658         */
    659        public boolean canMineBlock(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
    660        {
    661            return super.canMineBlock(par1EntityPlayer, par2, par3, par4);
    662        }
    663
    664        public boolean canMineBlockBody(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
    665        {
    666            int var5 = MathHelper.abs_int(par2 - this.worldInfo.getSpawnX());
    667            int var6 = MathHelper.abs_int(par4 - this.worldInfo.getSpawnZ());
    668
    669            if (var5 > var6)
    670            {
    671                var6 = var5;
    672            }
    673
    674            return var6 > mcServer.getSpawnProtectionSize() || this.mcServer.getConfigurationManager().areCommandsAllowed(par1EntityPlayer.username) || this.mcServer.isSinglePlayer();
    675        }
    676
    677        protected void initialize(WorldSettings par1WorldSettings)
    678        {
    679            if (this.entityIdMap == null)
    680            {
    681                this.entityIdMap = new IntHashMap();
    682            }
    683
    684            if (this.field_73064_N == null)
    685            {
    686                this.field_73064_N = new HashSet();
    687            }
    688
    689            if (this.pendingTickListEntries == null)
    690            {
    691                this.pendingTickListEntries = new TreeSet();
    692            }
    693
    694            this.createSpawnPosition(par1WorldSettings);
    695            super.initialize(par1WorldSettings);
    696        }
    697
    698        /**
    699         * creates a spawn position at random within 256 blocks of 0,0
    700         */
    701        protected void createSpawnPosition(WorldSettings par1WorldSettings)
    702        {
    703            if (!this.provider.canRespawnHere())
    704            {
    705                this.worldInfo.setSpawnPosition(0, this.provider.getAverageGroundLevel(), 0);
    706            }
    707            else
    708            {
    709                this.findingSpawnPoint = true;
    710                WorldChunkManager var2 = this.provider.worldChunkMgr;
    711                List var3 = var2.getBiomesToSpawnIn();
    712                Random var4 = new Random(this.getSeed());
    713                ChunkPosition var5 = var2.findBiomePosition(0, 0, 256, var3, var4);
    714                int var6 = 0;
    715                int var7 = this.provider.getAverageGroundLevel();
    716                int var8 = 0;
    717
    718                if (var5 != null)
    719                {
    720                    var6 = var5.x;
    721                    var8 = var5.z;
    722                }
    723                else
    724                {
    725                    System.out.println("Unable to find spawn biome");
    726                }
    727
    728                int var9 = 0;
    729
    730                while (!this.provider.canCoordinateBeSpawn(var6, var8))
    731                {
    732                    var6 += var4.nextInt(64) - var4.nextInt(64);
    733                    var8 += var4.nextInt(64) - var4.nextInt(64);
    734                    ++var9;
    735
    736                    if (var9 == 1000)
    737                    {
    738                        break;
    739                    }
    740                }
    741
    742                this.worldInfo.setSpawnPosition(var6, var7, var8);
    743                this.findingSpawnPoint = false;
    744
    745                if (par1WorldSettings.isBonusChestEnabled())
    746                {
    747                    this.createBonusChest();
    748                }
    749            }
    750        }
    751
    752        /**
    753         * Creates the bonus chest in the world.
    754         */
    755        protected void createBonusChest()
    756        {
    757            WorldGeneratorBonusChest var1 = new WorldGeneratorBonusChest(ChestGenHooks.getItems(BONUS_CHEST), ChestGenHooks.getCount(BONUS_CHEST, rand));
    758
    759            for (int var2 = 0; var2 < 10; ++var2)
    760            {
    761                int var3 = this.worldInfo.getSpawnX() + this.rand.nextInt(6) - this.rand.nextInt(6);
    762                int var4 = this.worldInfo.getSpawnZ() + this.rand.nextInt(6) - this.rand.nextInt(6);
    763                int var5 = this.getTopSolidOrLiquidBlock(var3, var4) + 1;
    764
    765                if (var1.generate(this, this.rand, var3, var5, var4))
    766                {
    767                    break;
    768                }
    769            }
    770        }
    771
    772        /**
    773         * Gets the hard-coded portal location to use when entering this dimension.
    774         */
    775        public ChunkCoordinates getEntrancePortalLocation()
    776        {
    777            return this.provider.getEntrancePortalLocation();
    778        }
    779
    780        /**
    781         * Saves all chunks to disk while updating progress bar.
    782         */
    783        public void saveAllChunks(boolean par1, IProgressUpdate par2IProgressUpdate) throws MinecraftException
    784        {
    785            if (this.chunkProvider.canSave())
    786            {
    787                if (par2IProgressUpdate != null)
    788                {
    789                    par2IProgressUpdate.displayProgressMessage("Saving level");
    790                }
    791
    792                this.saveLevel();
    793
    794                if (par2IProgressUpdate != null)
    795                {
    796                    par2IProgressUpdate.resetProgresAndWorkingMessage("Saving chunks");
    797                }
    798
    799                this.chunkProvider.saveChunks(par1, par2IProgressUpdate);
    800                MinecraftForge.EVENT_BUS.post(new WorldEvent.Save(this));
    801            }
    802        }
    803
    804        /**
    805         * Saves the chunks to disk.
    806         */
    807        protected void saveLevel() throws MinecraftException
    808        {
    809            this.checkSessionLock();
    810            this.saveHandler.saveWorldInfoWithPlayer(this.worldInfo, this.mcServer.getConfigurationManager().getTagsFromLastWrite());
    811            this.mapStorage.saveAllData();
    812            this.perWorldStorage.saveAllData();
    813        }
    814
    815        /**
    816         * Start the skin for this entity downloading, if necessary, and increment its reference counter
    817         */
    818        protected void obtainEntitySkin(Entity par1Entity)
    819        {
    820            super.obtainEntitySkin(par1Entity);
    821            this.entityIdMap.addKey(par1Entity.entityId, par1Entity);
    822            Entity[] var2 = par1Entity.getParts();
    823
    824            if (var2 != null)
    825            {
    826                Entity[] var3 = var2;
    827                int var4 = var2.length;
    828
    829                for (int var5 = 0; var5 < var4; ++var5)
    830                {
    831                    Entity var6 = var3[var5];
    832                    this.entityIdMap.addKey(var6.entityId, var6);
    833                }
    834            }
    835        }
    836
    837        /**
    838         * Decrement the reference counter for this entity's skin image data
    839         */
    840        protected void releaseEntitySkin(Entity par1Entity)
    841        {
    842            super.releaseEntitySkin(par1Entity);
    843            this.entityIdMap.removeObject(par1Entity.entityId);
    844            Entity[] var2 = par1Entity.getParts();
    845
    846            if (var2 != null)
    847            {
    848                Entity[] var3 = var2;
    849                int var4 = var2.length;
    850
    851                for (int var5 = 0; var5 < var4; ++var5)
    852                {
    853                    Entity var6 = var3[var5];
    854                    this.entityIdMap.removeObject(var6.entityId);
    855                }
    856            }
    857        }
    858
    859        /**
    860         * Returns the Entity with the given ID, or null if it doesn't exist in this World.
    861         */
    862        public Entity getEntityByID(int par1)
    863        {
    864            return (Entity)this.entityIdMap.lookup(par1);
    865        }
    866
    867        /**
    868         * adds a lightning bolt to the list of lightning bolts in this world.
    869         */
    870        public boolean addWeatherEffect(Entity par1Entity)
    871        {
    872            if (super.addWeatherEffect(par1Entity))
    873            {
    874                this.mcServer.getConfigurationManager().sendToAllNear(par1Entity.posX, par1Entity.posY, par1Entity.posZ, 512.0D, this.provider.dimensionId, new Packet71Weather(par1Entity));
    875                return true;
    876            }
    877            else
    878            {
    879                return false;
    880            }
    881        }
    882
    883        /**
    884         * sends a Packet 38 (Entity Status) to all tracked players of that entity
    885         */
    886        public void setEntityState(Entity par1Entity, byte par2)
    887        {
    888            Packet38EntityStatus var3 = new Packet38EntityStatus(par1Entity.entityId, par2);
    889            this.getEntityTracker().sendPacketToAllAssociatedPlayers(par1Entity, var3);
    890        }
    891
    892        /**
    893         * returns a new explosion. Does initiation (at time of writing Explosion is not finished)
    894         */
    895        public Explosion newExplosion(Entity par1Entity, double par2, double par4, double par6, float par8, boolean par9, boolean par10)
    896        {
    897            Explosion var11 = new Explosion(this, par1Entity, par2, par4, par6, par8);
    898            var11.isFlaming = par9;
    899            var11.isSmoking = par10;
    900            var11.doExplosionA();
    901            var11.doExplosionB(false);
    902
    903            if (!par10)
    904            {
    905                var11.affectedBlockPositions.clear();
    906            }
    907
    908            Iterator var12 = this.playerEntities.iterator();
    909
    910            while (var12.hasNext())
    911            {
    912                EntityPlayer var13 = (EntityPlayer)var12.next();
    913
    914                if (var13.getDistanceSq(par2, par4, par6) < 4096.0D)
    915                {
    916                    ((EntityPlayerMP)var13).playerNetServerHandler.sendPacketToPlayer(new Packet60Explosion(par2, par4, par6, par8, var11.affectedBlockPositions, (Vec3)var11.func_77277_b().get(var13)));
    917                }
    918            }
    919
    920            return var11;
    921        }
    922
    923        /**
    924         * Adds a block event with the given Args to the blockEventCache. During the next tick(), the block specified will
    925         * have its onBlockEvent handler called with the given parameters. Args: X,Y,Z, BlockID, EventID, EventParameter
    926         */
    927        public void addBlockEvent(int par1, int par2, int par3, int par4, int par5, int par6)
    928        {
    929            BlockEventData var7 = new BlockEventData(par1, par2, par3, par4, par5, par6);
    930            Iterator var8 = this.blockEventCache[this.blockEventCacheIndex].iterator();
    931            BlockEventData var9;
    932
    933            do
    934            {
    935                if (!var8.hasNext())
    936                {
    937                    this.blockEventCache[this.blockEventCacheIndex].add(var7);
    938                    return;
    939                }
    940
    941                var9 = (BlockEventData)var8.next();
    942            }
    943            while (!var9.equals(var7));
    944        }
    945
    946        /**
    947         * Send and apply locally all pending BlockEvents to each player with 64m radius of the event.
    948         */
    949        private void sendAndApplyBlockEvents()
    950        {
    951            while (!this.blockEventCache[this.blockEventCacheIndex].isEmpty())
    952            {
    953                int var1 = this.blockEventCacheIndex;
    954                this.blockEventCacheIndex ^= 1;
    955                Iterator var2 = this.blockEventCache[var1].iterator();
    956
    957                while (var2.hasNext())
    958                {
    959                    BlockEventData var3 = (BlockEventData)var2.next();
    960
    961                    if (this.onBlockEventReceived(var3))
    962                    {
    963                        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()));
    964                    }
    965                }
    966
    967                this.blockEventCache[var1].clear();
    968            }
    969        }
    970
    971        /**
    972         * Called to apply a pending BlockEvent to apply to the current world.
    973         */
    974        private boolean onBlockEventReceived(BlockEventData par1BlockEventData)
    975        {
    976            int var2 = this.getBlockId(par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ());
    977
    978            if (var2 == par1BlockEventData.getBlockID())
    979            {
    980                Block.blocksList[var2].onBlockEventReceived(this, par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ(), par1BlockEventData.getEventID(), par1BlockEventData.getEventParameter());
    981                return true;
    982            }
    983            else
    984            {
    985                return false;
    986            }
    987        }
    988
    989        /**
    990         * Syncs all changes to disk and wait for completion.
    991         */
    992        public void flush()
    993        {
    994            this.saveHandler.flush();
    995        }
    996
    997        /**
    998         * Updates all weather states.
    999         */
    1000        protected void updateWeather()
    1001        {
    1002            boolean var1 = this.isRaining();
    1003            super.updateWeather();
    1004
    1005            if (var1 != this.isRaining())
    1006            {
    1007                if (var1)
    1008                {
    1009                    this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(2, 0));
    1010                }
    1011                else
    1012                {
    1013                    this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(1, 0));
    1014                }
    1015            }
    1016        }
    1017
    1018        /**
    1019         * Gets the MinecraftServer.
    1020         */
    1021        public MinecraftServer getMinecraftServer()
    1022        {
    1023            return this.mcServer;
    1024        }
    1025
    1026        /**
    1027         * Gets the EntityTracker
    1028         */
    1029        public EntityTracker getEntityTracker()
    1030        {
    1031            return this.theEntityTracker;
    1032        }
    1033
    1034        public PlayerManager getPlayerManager()
    1035        {
    1036            return this.thePlayerManager;
    1037        }
    1038
    1039        public Teleporter func_85176_s()
    1040        {
    1041            return this.field_85177_Q;
    1042        }
    1043
    1044        public File getChunkSaveLocation()
    1045        {
    1046            return ((AnvilChunkLoader)theChunkProviderServer.currentChunkLoader).chunkSaveLocation;
    1047        }
    1048    }