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