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