    001package net.minecraft.world;
    002
    003import cpw.mods.fml.relauncher.Side;
    004import cpw.mods.fml.relauncher.SideOnly;
    005
    006import java.io.File;
    007import java.util.ArrayList;
    008import java.util.HashSet;
    009import java.util.Iterator;
    010import java.util.List;
    011import java.util.Random;
    012import java.util.Set;
    013import java.util.TreeSet;
    014import net.minecraft.block.Block;
    015import net.minecraft.block.BlockEventData;
    016import net.minecraft.crash.CrashReport;
    017import net.minecraft.crash.CrashReportCategory;
    018import net.minecraft.entity.Entity;
    019import net.minecraft.entity.EntityTracker;
    020import net.minecraft.entity.EnumCreatureType;
    021import net.minecraft.entity.INpc;
    022import net.minecraft.entity.effect.EntityLightningBolt;
    023import net.minecraft.entity.passive.EntityAnimal;
    024import net.minecraft.entity.passive.EntityWaterMob;
    025import net.minecraft.entity.player.EntityPlayer;
    026import net.minecraft.entity.player.EntityPlayerMP;
    027import net.minecraft.item.Item;
    028import net.minecraft.network.packet.Packet38EntityStatus;
    029import net.minecraft.network.packet.Packet54PlayNoteBlock;
    030import net.minecraft.network.packet.Packet60Explosion;
    031import net.minecraft.network.packet.Packet70GameEvent;
    032import net.minecraft.network.packet.Packet71Weather;
    033import net.minecraft.profiler.Profiler;
    034import net.minecraft.server.MinecraftServer;
    035import net.minecraft.server.management.PlayerManager;
    036import net.minecraft.tileentity.TileEntity;
    037import net.minecraft.util.ChunkCoordinates;
    038import net.minecraft.util.IProgressUpdate;
    039import net.minecraft.util.IntHashMap;
    040import net.minecraft.util.MathHelper;
    041import net.minecraft.util.ReportedException;
    042import net.minecraft.util.Vec3;
    043import net.minecraft.util.WeightedRandom;
    044import net.minecraft.util.WeightedRandomChestContent;
    045import net.minecraft.world.biome.BiomeGenBase;
    046import net.minecraft.world.biome.SpawnListEntry;
    047import net.minecraft.world.biome.WorldChunkManager;
    048import net.minecraft.world.chunk.Chunk;
    049import net.minecraft.world.chunk.IChunkProvider;
    050import net.minecraft.world.chunk.storage.AnvilChunkLoader;
    051import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
    052import net.minecraft.world.chunk.storage.IChunkLoader;
    053import net.minecraft.world.gen.ChunkProviderServer;
    054import net.minecraft.world.gen.feature.WorldGeneratorBonusChest;
    055import net.minecraft.world.storage.ISaveHandler;
    056
    057import net.minecraftforge.common.ChestGenHooks;
    058import static net.minecraftforge.common.ChestGenHooks.*;
    059import net.minecraftforge.common.DimensionManager;
    060import net.minecraftforge.common.MinecraftForge;
    061import net.minecraftforge.event.world.WorldEvent;
    062
    063public 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    public 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.itemID, 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.itemID, 0, 1, 1, 3), new WeightedRandomChestContent(Item.axeWood.itemID, 0, 1, 1, 5), new WeightedRandomChestContent(Item.pickaxeStone.itemID, 0, 1, 1, 3), new WeightedRandomChestContent(Item.pickaxeWood.itemID, 0, 1, 1, 5), new WeightedRandomChestContent(Item.appleRed.itemID, 0, 2, 3, 5), new WeightedRandomChestContent(Item.bread.itemID, 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 startTime = System.nanoTime();
    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            //Limits and evenly distributes the lighting update time
    335            if (System.nanoTime() - startTime <= 4000000 && doneChunks.add(var4))
    336            {
    337                var7.updateSkylight();
    338            }
    339            this.theProfiler.endStartSection("thunder");
    340            int var8;
    341            int var9;
    342            int var10;
    343            int var11;
    344
    345            if (provider.canDoLightning(var7) && this.rand.nextInt(100000) == 0 && this.isRaining() && this.isThundering())
    346            {
    347                this.updateLCG = this.updateLCG * 3 + 1013904223;
    348                var8 = this.updateLCG >> 2;
    349                var9 = var5 + (var8 & 15);
    350                var10 = var6 + (var8 >> 8 & 15);
    351                var11 = this.getPrecipitationHeight(var9, var10);
    352
    353                if (this.canLightningStrikeAt(var9, var11, var10))
    354                {
    355                    this.addWeatherEffect(new EntityLightningBolt(this, (double)var9, (double)var11, (double)var10));
    356                }
    357            }
    358
    359            this.theProfiler.endStartSection("iceandsnow");
    360            int var13;
    361
    362            if (provider.canDoRainSnowIce(var7) && this.rand.nextInt(16) == 0)
    363            {
    364                this.updateLCG = this.updateLCG * 3 + 1013904223;
    365                var8 = this.updateLCG >> 2;
    366                var9 = var8 & 15;
    367                var10 = var8 >> 8 & 15;
    368                var11 = this.getPrecipitationHeight(var9 + var5, var10 + var6);
    369
    370                if (this.isBlockFreezableNaturally(var9 + var5, var11 - 1, var10 + var6))
    371                {
    372                    this.setBlockWithNotify(var9 + var5, var11 - 1, var10 + var6, Block.ice.blockID);
    373                }
    374
    375                if (this.isRaining() && this.canSnowAt(var9 + var5, var11, var10 + var6))
    376                {
    377                    this.setBlockWithNotify(var9 + var5, var11, var10 + var6, Block.snow.blockID);
    378                }
    379
    380                if (this.isRaining())
    381                {
    382                    BiomeGenBase var12 = this.getBiomeGenForCoords(var9 + var5, var10 + var6);
    383
    384                    if (var12.canSpawnLightningBolt())
    385                    {
    386                        var13 = this.getBlockId(var9 + var5, var11 - 1, var10 + var6);
    387
    388                        if (var13 != 0)
    389                        {
    390                            Block.blocksList[var13].fillWithRain(this, var9 + var5, var11 - 1, var10 + var6);
    391                        }
    392                    }
    393                }
    394            }
    395
    396            this.theProfiler.endStartSection("tickTiles");
    397            ExtendedBlockStorage[] var19 = var7.getBlockStorageArray();
    398            var9 = var19.length;
    399
    400            for (var10 = 0; var10 < var9; ++var10)
    401            {
    402                ExtendedBlockStorage var21 = var19[var10];
    403
    404                if (var21 != null && var21.getNeedsRandomTick())
    405                {
    406                    for (int var20 = 0; var20 < 3; ++var20)
    407                    {
    408                        this.updateLCG = this.updateLCG * 3 + 1013904223;
    409                        var13 = this.updateLCG >> 2;
    410                        int var14 = var13 & 15;
    411                        int var15 = var13 >> 8 & 15;
    412                        int var16 = var13 >> 16 & 15;
    413                        int var17 = var21.getExtBlockID(var14, var16, var15);
    414                        ++var2;
    415                        Block var18 = Block.blocksList[var17];
    416
    417                        if (var18 != null && var18.getTickRandomly())
    418                        {
    419                            ++var1;
    420                            var18.updateTick(this, var14 + var5, var16 + var21.getYLocation(), var15 + var6, this.rand);
    421                        }
    422                    }
    423                }
    424            }
    425
    426            this.theProfiler.endSection();
    427        }
    428    }
    429
    430    /**
    431     * Schedules a tick to a block with a delay (Most commonly the tick rate)
    432     */
    433    public void scheduleBlockUpdate(int par1, int par2, int par3, int par4, int par5)
    434    {
    435        this.func_82740_a(par1, par2, par3, par4, par5, 0);
    436    }
    437
    438    public void func_82740_a(int par1, int par2, int par3, int par4, int par5, int par6)
    439    {
    440        NextTickListEntry var7 = new NextTickListEntry(par1, par2, par3, par4);
    441        boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(var7.xCoord >> 4, var7.zCoord >> 4));
    442        byte var8 = isForced ? (byte)0 : 8;
    443
    444        if (this.scheduledUpdatesAreImmediate && par4 > 0)
    445        {
    446            if (Block.blocksList[par4].func_82506_l())
    447            {
    448                if (this.checkChunksExist(var7.xCoord - var8, var7.yCoord - var8, var7.zCoord - var8, var7.xCoord + var8, var7.yCoord + var8, var7.zCoord + var8))
    449                {
    450                    int var9 = this.getBlockId(var7.xCoord, var7.yCoord, var7.zCoord);
    451
    452                    if (var9 == var7.blockID && var9 > 0)
    453                    {
    454                        Block.blocksList[var9].updateTick(this, var7.xCoord, var7.yCoord, var7.zCoord, this.rand);
    455                    }
    456                }
    457
    458                return;
    459            }
    460
    461            par5 = 1;
    462        }
    463
    464        if (this.checkChunksExist(par1 - var8, par2 - var8, par3 - var8, par1 + var8, par2 + var8, par3 + var8))
    465        {
    466            if (par4 > 0)
    467            {
    468                var7.setScheduledTime((long)par5 + this.worldInfo.getWorldTotalTime());
    469                var7.func_82753_a(par6);
    470            }
    471
    472            if (!this.field_73064_N.contains(var7))
    473            {
    474                this.field_73064_N.add(var7);
    475                this.pendingTickListEntries.add(var7);
    476            }
    477        }
    478    }
    479
    480    /**
    481     * Schedules a block update from the saved information in a chunk. Called when the chunk is loaded.
    482     */
    483    public void scheduleBlockUpdateFromLoad(int par1, int par2, int par3, int par4, int par5)
    484    {
    485        NextTickListEntry var6 = new NextTickListEntry(par1, par2, par3, par4);
    486
    487        if (par4 > 0)
    488        {
    489            var6.setScheduledTime((long)par5 + this.worldInfo.getWorldTotalTime());
    490        }
    491
    492        if (!this.field_73064_N.contains(var6))
    493        {
    494            this.field_73064_N.add(var6);
    495            this.pendingTickListEntries.add(var6);
    496        }
    497    }
    498
    499    /**
    500     * Updates (and cleans up) entities and tile entities
    501     */
    502    public void updateEntities()
    503    {
    504        if (this.playerEntities.isEmpty() && getPersistentChunks().isEmpty())
    505        {
    506            if (this.updateEntityTick++ >= 1200)
    507            {
    508                return;
    509            }
    510        }
    511        else
    512        {
    513            this.resetUpdateEntityTick();
    514        }
    515
    516        super.updateEntities();
    517    }
    518
    519    /**
    520     * Resets the updateEntityTick field to 0
    521     */
    522    public void resetUpdateEntityTick()
    523    {
    524        this.updateEntityTick = 0;
    525    }
    526
    527    /**
    528     * Runs through the list of updates to run and ticks them
    529     */
    530    public boolean tickUpdates(boolean par1)
    531    {
    532        int var2 = this.pendingTickListEntries.size();
    533
    534        if (var2 != this.field_73064_N.size())
    535        {
    536            throw new IllegalStateException("TickNextTick list out of synch");
    537        }
    538        else
    539        {
    540            if (var2 > 1000)
    541            {
    542                var2 = 1000;
    543            }
    544
    545            for (int var3 = 0; var3 < var2; ++var3)
    546            {
    547                NextTickListEntry var4 = (NextTickListEntry)this.pendingTickListEntries.first();
    548
    549                if (!par1 && var4.scheduledTime > this.worldInfo.getWorldTotalTime())
    550                {
    551                    break;
    552                }
    553
    554                this.pendingTickListEntries.remove(var4);
    555                this.field_73064_N.remove(var4);
    556                boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(var4.xCoord >> 4, var4.zCoord >> 4));
    557                byte var5 = isForced ? (byte)0 : 8;
    558
    559                if (this.checkChunksExist(var4.xCoord - var5, var4.yCoord - var5, var4.zCoord - var5, var4.xCoord + var5, var4.yCoord + var5, var4.zCoord + var5))
    560                {
    561                    int var6 = this.getBlockId(var4.xCoord, var4.yCoord, var4.zCoord);
    562
    563                    if (var6 == var4.blockID && var6 > 0)
    564                    {
    565                        try
    566                        {
    567                            Block.blocksList[var6].updateTick(this, var4.xCoord, var4.yCoord, var4.zCoord, this.rand);
    568                        }
    569                        catch (Throwable var13)
    570                        {
    571                            CrashReport var8 = CrashReport.makeCrashReport(var13, "Exception while ticking a block");
    572                            CrashReportCategory var9 = var8.makeCategory("Block being ticked");
    573                            int var10;
    574
    575                            try
    576                            {
    577                                var10 = this.getBlockMetadata(var4.xCoord, var4.yCoord, var4.zCoord);
    578                            }
    579                            catch (Throwable var12)
    580                            {
    581                                var10 = -1;
    582                            }
    583
    584                            CrashReportCategory.func_85068_a(var9, var4.xCoord, var4.yCoord, var4.zCoord, var6, var10);
    585                            throw new ReportedException(var8);
    586                        }
    587                    }
    588                }
    589            }
    590
    591            return !this.pendingTickListEntries.isEmpty();
    592        }
    593    }
    594
    595    public List getPendingBlockUpdates(Chunk par1Chunk, boolean par2)
    596    {
    597        ArrayList var3 = null;
    598        ChunkCoordIntPair var4 = par1Chunk.getChunkCoordIntPair();
    599        int var5 = var4.chunkXPos << 4;
    600        int var6 = var5 + 16;
    601        int var7 = var4.chunkZPos << 4;
    602        int var8 = var7 + 16;
    603        Iterator var9 = this.pendingTickListEntries.iterator();
    604
    605        while (var9.hasNext())
    606        {
    607            NextTickListEntry var10 = (NextTickListEntry)var9.next();
    608
    609            if (var10.xCoord >= var5 && var10.xCoord < var6 && var10.zCoord >= var7 && var10.zCoord < var8)
    610            {
    611                if (par2)
    612                {
    613                    this.field_73064_N.remove(var10);
    614                    var9.remove();
    615                }
    616
    617                if (var3 == null)
    618                {
    619                    var3 = new ArrayList();
    620                }
    621
    622                var3.add(var10);
    623            }
    624        }
    625
    626        return var3;
    627    }
    628
    629    /**
    630     * Will update the entity in the world if the chunk the entity is in is currently loaded or its forced to update.
    631     * Args: entity, forceUpdate
    632     */
    633    public void updateEntityWithOptionalForce(Entity par1Entity, boolean par2)
    634    {
    635        if (!this.mcServer.getCanSpawnAnimals() && (par1Entity instanceof EntityAnimal || par1Entity instanceof EntityWaterMob))
    636        {
    637            par1Entity.setDead();
    638        }
    639
    640        if (!this.mcServer.getCanSpawnNPCs() && par1Entity instanceof INpc)
    641        {
    642            par1Entity.setDead();
    643        }
    644
    645        if (!(par1Entity.riddenByEntity instanceof EntityPlayer))
    646        {
    647            super.updateEntityWithOptionalForce(par1Entity, par2);
    648        }
    649    }
    650
    651    /**
    652     * direct call to super.updateEntityWithOptionalForce
    653     */
    654    public void uncheckedUpdateEntity(Entity par1Entity, boolean par2)
    655    {
    656        super.updateEntityWithOptionalForce(par1Entity, par2);
    657    }
    658
    659    /**
    660     * Creates the chunk provider for this world. Called in the constructor. Retrieves provider from worldProvider?
    661     */
    662    protected IChunkProvider createChunkProvider()
    663    {
    664        IChunkLoader var1 = this.saveHandler.getChunkLoader(this.provider);
    665        this.theChunkProviderServer = new ChunkProviderServer(this, var1, this.provider.createChunkGenerator());
    666        return this.theChunkProviderServer;
    667    }
    668
    669    /**
    670     * pars: min x,y,z , max x,y,z
    671     */
    672    public List getAllTileEntityInBox(int par1, int par2, int par3, int par4, int par5, int par6)
    673    {
    674        ArrayList var7 = new ArrayList();
    675
    676        for(int x = (par1 >> 4); x <= (par4 >> 4); x++)
    677        {
    678            for(int z = (par3 >> 4); z <= (par6 >> 4); z++)
    679            {
    680                Chunk chunk = getChunkFromChunkCoords(x, z);
    681                if (chunk != null)
    682                {
    683                    for(Object obj : chunk.chunkTileEntityMap.values())
    684                    {
    685                        TileEntity entity = (TileEntity)obj;
    686                        if (!entity.isInvalid())
    687                        {
    688                            if (entity.xCoord >= par1 && entity.yCoord >= par2 && entity.zCoord >= par3 &&
    689                                entity.xCoord <= par4 && entity.yCoord <= par5 && entity.zCoord <= par6)
    690                            {
    691                                var7.add(entity);
    692                            }
    693                        }
    694                    }
    695                }
    696            }
    697        }
    698        return var7;
    699    }
    700
    701    /**
    702     * Called when checking if a certain block can be mined or not. The 'spawn safe zone' check is located here.
    703     */
    704    public boolean canMineBlock(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
    705    {
    706        return super.canMineBlock(par1EntityPlayer, par2, par3, par4);
    707    }
    708
    709    public boolean canMineBlockBody(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
    710    {
    711        int var5 = MathHelper.abs_int(par2 - this.worldInfo.getSpawnX());
    712        int var6 = MathHelper.abs_int(par4 - this.worldInfo.getSpawnZ());
    713
    714        if (var5 > var6)
    715        {
    716            var6 = var5;
    717        }
    718
    719        return var6 > mcServer.getSpawnProtectionSize() || this.mcServer.getConfigurationManager().areCommandsAllowed(par1EntityPlayer.username) || this.mcServer.isSinglePlayer();
    720    }
    721
    722    protected void initialize(WorldSettings par1WorldSettings)
    723    {
    724        if (this.entityIdMap == null)
    725        {
    726            this.entityIdMap = new IntHashMap();
    727        }
    728
    729        if (this.field_73064_N == null)
    730        {
    731            this.field_73064_N = new HashSet();
    732        }
    733
    734        if (this.pendingTickListEntries == null)
    735        {
    736            this.pendingTickListEntries = new TreeSet();
    737        }
    738
    739        this.createSpawnPosition(par1WorldSettings);
    740        super.initialize(par1WorldSettings);
    741    }
    742
    743    /**
    744     * creates a spawn position at random within 256 blocks of 0,0
    745     */
    746    protected void createSpawnPosition(WorldSettings par1WorldSettings)
    747    {
    748        if (!this.provider.canRespawnHere())
    749        {
    750            this.worldInfo.setSpawnPosition(0, this.provider.getAverageGroundLevel(), 0);
    751        }
    752        else
    753        {
    754            this.findingSpawnPoint = true;
    755            WorldChunkManager var2 = this.provider.worldChunkMgr;
    756            List var3 = var2.getBiomesToSpawnIn();
    757            Random var4 = new Random(this.getSeed());
    758            ChunkPosition var5 = var2.findBiomePosition(0, 0, 256, var3, var4);
    759            int var6 = 0;
    760            int var7 = this.provider.getAverageGroundLevel();
    761            int var8 = 0;
    762
    763            if (var5 != null)
    764            {
    765                var6 = var5.x;
    766                var8 = var5.z;
    767            }
    768            else
    769            {
    770                System.out.println("Unable to find spawn biome");
    771            }
    772
    773            int var9 = 0;
    774
    775            while (!this.provider.canCoordinateBeSpawn(var6, var8))
    776            {
    777                var6 += var4.nextInt(64) - var4.nextInt(64);
    778                var8 += var4.nextInt(64) - var4.nextInt(64);
    779                ++var9;
    780
    781                if (var9 == 1000)
    782                {
    783                    break;
    784                }
    785            }
    786
    787            this.worldInfo.setSpawnPosition(var6, var7, var8);
    788            this.findingSpawnPoint = false;
    789
    790            if (par1WorldSettings.isBonusChestEnabled())
    791            {
    792                this.createBonusChest();
    793            }
    794        }
    795    }
    796
    797    /**
    798     * Creates the bonus chest in the world.
    799     */
    800    protected void createBonusChest()
    801    {
    802        WorldGeneratorBonusChest var1 = new WorldGeneratorBonusChest(ChestGenHooks.getItems(BONUS_CHEST, rand), ChestGenHooks.getCount(BONUS_CHEST, rand));
    803
    804        for (int var2 = 0; var2 < 10; ++var2)
    805        {
    806            int var3 = this.worldInfo.getSpawnX() + this.rand.nextInt(6) - this.rand.nextInt(6);
    807            int var4 = this.worldInfo.getSpawnZ() + this.rand.nextInt(6) - this.rand.nextInt(6);
    808            int var5 = this.getTopSolidOrLiquidBlock(var3, var4) + 1;
    809
    810            if (var1.generate(this, this.rand, var3, var5, var4))
    811            {
    812                break;
    813            }
    814        }
    815    }
    816
    817    /**
    818     * Gets the hard-coded portal location to use when entering this dimension.
    819     */
    820    public ChunkCoordinates getEntrancePortalLocation()
    821    {
    822        return this.provider.getEntrancePortalLocation();
    823    }
    824
    825    /**
    826     * Saves all chunks to disk while updating progress bar.
    827     */
    828    public void saveAllChunks(boolean par1, IProgressUpdate par2IProgressUpdate) throws MinecraftException
    829    {
    830        if (this.chunkProvider.canSave())
    831        {
    832            if (par2IProgressUpdate != null)
    833            {
    834                par2IProgressUpdate.displayProgressMessage("Saving level");
    835            }
    836
    837            this.saveLevel();
    838
    839            if (par2IProgressUpdate != null)
    840            {
    841                par2IProgressUpdate.resetProgresAndWorkingMessage("Saving chunks");
    842            }
    843
    844            this.chunkProvider.saveChunks(par1, par2IProgressUpdate);
    845            MinecraftForge.EVENT_BUS.post(new WorldEvent.Save(this));
    846        }
    847    }
    848
    849    /**
    850     * Saves the chunks to disk.
    851     */
    852    protected void saveLevel() throws MinecraftException
    853    {
    854        this.checkSessionLock();
    855        this.saveHandler.saveWorldInfoWithPlayer(this.worldInfo, this.mcServer.getConfigurationManager().getTagsFromLastWrite());
    856        this.mapStorage.saveAllData();
    857        this.perWorldStorage.saveAllData();
    858    }
    859
    860    /**
    861     * Start the skin for this entity downloading, if necessary, and increment its reference counter
    862     */
    863    protected void obtainEntitySkin(Entity par1Entity)
    864    {
    865        super.obtainEntitySkin(par1Entity);
    866        this.entityIdMap.addKey(par1Entity.entityId, par1Entity);
    867        Entity[] var2 = par1Entity.getParts();
    868
    869        if (var2 != null)
    870        {
    871            for (int var3 = 0; var3 < var2.length; ++var3)
    872            {
    873                this.entityIdMap.addKey(var2[var3].entityId, var2[var3]);
    874            }
    875        }
    876    }
    877
    878    /**
    879     * Decrement the reference counter for this entity's skin image data
    880     */
    881    public void releaseEntitySkin(Entity par1Entity)
    882    {
    883        super.releaseEntitySkin(par1Entity);
    884        this.entityIdMap.removeObject(par1Entity.entityId);
    885        Entity[] var2 = par1Entity.getParts();
    886
    887        if (var2 != null)
    888        {
    889            for (int var3 = 0; var3 < var2.length; ++var3)
    890            {
    891                this.entityIdMap.removeObject(var2[var3].entityId);
    892            }
    893        }
    894    }
    895
    896    /**
    897     * Returns the Entity with the given ID, or null if it doesn't exist in this World.
    898     */
    899    public Entity getEntityByID(int par1)
    900    {
    901        return (Entity)this.entityIdMap.lookup(par1);
    902    }
    903
    904    /**
    905     * adds a lightning bolt to the list of lightning bolts in this world.
    906     */
    907    public boolean addWeatherEffect(Entity par1Entity)
    908    {
    909        if (super.addWeatherEffect(par1Entity))
    910        {
    911            this.mcServer.getConfigurationManager().sendToAllNear(par1Entity.posX, par1Entity.posY, par1Entity.posZ, 512.0D, this.provider.dimensionId, new Packet71Weather(par1Entity));
    912            return true;
    913        }
    914        else
    915        {
    916            return false;
    917        }
    918    }
    919
    920    /**
    921     * sends a Packet 38 (Entity Status) to all tracked players of that entity
    922     */
    923    public void setEntityState(Entity par1Entity, byte par2)
    924    {
    925        Packet38EntityStatus var3 = new Packet38EntityStatus(par1Entity.entityId, par2);
    926        this.getEntityTracker().sendPacketToAllAssociatedPlayers(par1Entity, var3);
    927    }
    928
    929    /**
    930     * returns a new explosion. Does initiation (at time of writing Explosion is not finished)
    931     */
    932    public Explosion newExplosion(Entity par1Entity, double par2, double par4, double par6, float par8, boolean par9, boolean par10)
    933    {
    934        Explosion var11 = new Explosion(this, par1Entity, par2, par4, par6, par8);
    935        var11.isFlaming = par9;
    936        var11.isSmoking = par10;
    937        var11.doExplosionA();
    938        var11.doExplosionB(false);
    939
    940        if (!par10)
    941        {
    942            var11.affectedBlockPositions.clear();
    943        }
    944
    945        Iterator var12 = this.playerEntities.iterator();
    946
    947        while (var12.hasNext())
    948        {
    949            EntityPlayer var13 = (EntityPlayer)var12.next();
    950
    951            if (var13.getDistanceSq(par2, par4, par6) < 4096.0D)
    952            {
    953                ((EntityPlayerMP)var13).playerNetServerHandler.sendPacketToPlayer(new Packet60Explosion(par2, par4, par6, par8, var11.affectedBlockPositions, (Vec3)var11.func_77277_b().get(var13)));
    954            }
    955        }
    956
    957        return var11;
    958    }
    959
    960    /**
    961     * Adds a block event with the given Args to the blockEventCache. During the next tick(), the block specified will
    962     * have its onBlockEvent handler called with the given parameters. Args: X,Y,Z, BlockID, EventID, EventParameter
    963     */
    964    public void addBlockEvent(int par1, int par2, int par3, int par4, int par5, int par6)
    965    {
    966        BlockEventData var7 = new BlockEventData(par1, par2, par3, par4, par5, par6);
    967        Iterator var8 = this.blockEventCache[this.blockEventCacheIndex].iterator();
    968        BlockEventData var9;
    969
    970        do
    971        {
    972            if (!var8.hasNext())
    973            {
    974                this.blockEventCache[this.blockEventCacheIndex].add(var7);
    975                return;
    976            }
    977
    978            var9 = (BlockEventData)var8.next();
    979        }
    980        while (!var9.equals(var7));
    981    }
    982
    983    /**
    984     * Send and apply locally all pending BlockEvents to each player with 64m radius of the event.
    985     */
    986    private void sendAndApplyBlockEvents()
    987    {
    988        while (!this.blockEventCache[this.blockEventCacheIndex].isEmpty())
    989        {
    990            int var1 = this.blockEventCacheIndex;
    991            this.blockEventCacheIndex ^= 1;
    992            Iterator var2 = this.blockEventCache[var1].iterator();
    993
    994            while (var2.hasNext())
    995            {
    996                BlockEventData var3 = (BlockEventData)var2.next();
    997
    998                if (this.onBlockEventReceived(var3))
    999                {
    1000                    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()));
    1001                }
    1002            }
    1003
    1004            this.blockEventCache[var1].clear();
    1005        }
    1006    }
    1007
    1008    /**
    1009     * Called to apply a pending BlockEvent to apply to the current world.
    1010     */
    1011    private boolean onBlockEventReceived(BlockEventData par1BlockEventData)
    1012    {
    1013        int var2 = this.getBlockId(par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ());
    1014
    1015        if (var2 == par1BlockEventData.getBlockID())
    1016        {
    1017            Block.blocksList[var2].onBlockEventReceived(this, par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ(), par1BlockEventData.getEventID(), par1BlockEventData.getEventParameter());
    1018            return true;
    1019        }
    1020        else
    1021        {
    1022            return false;
    1023        }
    1024    }
    1025
    1026    /**
    1027     * Syncs all changes to disk and wait for completion.
    1028     */
    1029    public void flush()
    1030    {
    1031        this.saveHandler.flush();
    1032    }
    1033
    1034    /**
    1035     * Updates all weather states.
    1036     */
    1037    protected void updateWeather()
    1038    {
    1039        boolean var1 = this.isRaining();
    1040        super.updateWeather();
    1041
    1042        if (var1 != this.isRaining())
    1043        {
    1044            if (var1)
    1045            {
    1046                this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(2, 0));
    1047            }
    1048            else
    1049            {
    1050                this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(1, 0));
    1051            }
    1052        }
    1053    }
    1054
    1055    /**
    1056     * Gets the MinecraftServer.
    1057     */
    1058    public MinecraftServer getMinecraftServer()
    1059    {
    1060        return this.mcServer;
    1061    }
    1062
    1063    /**
    1064     * Gets the EntityTracker
    1065     */
    1066    public EntityTracker getEntityTracker()
    1067    {
    1068        return this.theEntityTracker;
    1069    }
    1070
    1071    public PlayerManager getPlayerManager()
    1072    {
    1073        return this.thePlayerManager;
    1074    }
    1075
    1076    public Teleporter func_85176_s()
    1077    {
    1078        return this.field_85177_Q;
    1079    }
    1080
    1081    public File getChunkSaveLocation()
    1082    {
    1083        return ((AnvilChunkLoader)theChunkProviderServer.currentChunkLoader).chunkSaveLocation;
    1084    }
    1085}