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