001package net.minecraft.world;
002
003import cpw.mods.fml.common.FMLLog;
004import cpw.mods.fml.relauncher.Side;
005import cpw.mods.fml.relauncher.SideOnly;
006import java.util.ArrayList;
007import java.util.Calendar;
008import java.util.Collection;
009import java.util.HashSet;
010import java.util.Iterator;
011import java.util.List;
012import java.util.Random;
013import java.util.Set;
014import net.minecraft.block.Block;
015import net.minecraft.block.BlockFluid;
016import net.minecraft.block.BlockHalfSlab;
017import net.minecraft.block.BlockHopper;
018import net.minecraft.block.BlockSnow;
019import net.minecraft.block.BlockStairs;
020import net.minecraft.block.material.Material;
021import net.minecraft.command.IEntitySelector;
022import net.minecraft.crash.CrashReport;
023import net.minecraft.crash.CrashReportCategory;
024import net.minecraft.entity.Entity;
025import net.minecraft.entity.item.EntityMinecart;
026import net.minecraft.entity.player.EntityPlayer;
027import net.minecraft.item.ItemStack;
028import net.minecraft.logging.ILogAgent;
029import net.minecraft.nbt.NBTTagCompound;
030import net.minecraft.pathfinding.PathEntity;
031import net.minecraft.pathfinding.PathFinder;
032import net.minecraft.profiler.Profiler;
033import net.minecraft.scoreboard.Scoreboard;
034import net.minecraft.server.gui.IUpdatePlayerListBox;
035import net.minecraft.tileentity.TileEntity;
036import net.minecraft.util.AxisAlignedBB;
037import net.minecraft.util.ChunkCoordinates;
038import net.minecraft.util.Direction;
039import net.minecraft.util.Facing;
040import net.minecraft.util.MathHelper;
041import net.minecraft.util.MovingObjectPosition;
042import net.minecraft.util.ReportedException;
043import net.minecraft.util.Vec3;
044import net.minecraft.util.Vec3Pool;
045import net.minecraft.village.VillageCollection;
046import net.minecraft.village.VillageSiege;
047import net.minecraft.world.biome.BiomeGenBase;
048import net.minecraft.world.biome.WorldChunkManager;
049import net.minecraft.world.chunk.Chunk;
050import net.minecraft.world.chunk.IChunkProvider;
051import net.minecraft.world.storage.ISaveHandler;
052import net.minecraft.world.storage.MapStorage;
053import net.minecraft.world.storage.WorldInfo;
054
055import com.google.common.collect.ImmutableSetMultimap;
056
057import net.minecraftforge.common.ForgeChunkManager;
058import net.minecraftforge.common.ForgeChunkManager.Ticket;
059import net.minecraftforge.common.ForgeDummyContainer;
060import net.minecraftforge.common.ForgeHooks;
061import net.minecraftforge.common.MinecraftForge;
062import net.minecraftforge.common.ForgeDirection;
063import net.minecraftforge.common.WorldSpecificSaveHandler;
064import net.minecraftforge.event.entity.EntityEvent;
065import net.minecraftforge.event.entity.EntityJoinWorldEvent;
066import net.minecraftforge.event.world.WorldEvent;
067import net.minecraftforge.event.entity.PlaySoundAtEntityEvent;
068import net.minecraft.entity.EnumCreatureType;
069
070public abstract class World implements IBlockAccess
071{
072    /**
073     * Used in the getEntitiesWithinAABB functions to expand the search area for entities.
074     * Modders should change this variable to a higher value if it is less then the radius
075     * of one of there entities.
076     */
077    public static double MAX_ENTITY_RADIUS = 2.0D;
078
079    public final MapStorage perWorldStorage;
080
081    /**
082     * boolean; if true updates scheduled by scheduleBlockUpdate happen immediately
083     */
084    public boolean scheduledUpdatesAreImmediate = false;
085
086    /** A list of all Entities in all currently-loaded chunks */
087    public List loadedEntityList = new ArrayList();
088    protected List unloadedEntityList = new ArrayList();
089
090    /** A list of all TileEntities in all currently-loaded chunks */
091    public List loadedTileEntityList = new ArrayList();
092    private List addedTileEntityList = new ArrayList();
093
094    /** Entities marked for removal. */
095    private List entityRemoval = new ArrayList();
096
097    /** Array list of players in the world. */
098    public List playerEntities = new ArrayList();
099
100    /** a list of all the lightning entities */
101    public List weatherEffects = new ArrayList();
102    private long cloudColour = 16777215L;
103
104    /** How much light is subtracted from full daylight */
105    public int skylightSubtracted = 0;
106
107    /**
108     * Contains the current Linear Congruential Generator seed for block updates. Used with an A value of 3 and a C
109     * value of 0x3c6ef35f, producing a highly planar series of values ill-suited for choosing random blocks in a
110     * 16x128x16 field.
111     */
112    protected int updateLCG = (new Random()).nextInt();
113
114    /**
115     * magic number used to generate fast random numbers for 3d distribution within a chunk
116     */
117    protected final int DIST_HASH_MAGIC = 1013904223;
118    public float prevRainingStrength;
119    public float rainingStrength;
120    public float prevThunderingStrength;
121    public float thunderingStrength;
122
123    /**
124     * Set to 2 whenever a lightning bolt is generated in SSP. Decrements if > 0 in updateWeather(). Value appears to be
125     * unused.
126     */
127    public int lastLightningBolt = 0;
128
129    /** Option > Difficulty setting (0 - 3) */
130    public int difficultySetting;
131
132    /** RNG for World. */
133    public Random rand = new Random();
134
135    /** The WorldProvider instance that World uses. */
136    public final WorldProvider provider;
137    protected List worldAccesses = new ArrayList();
138
139    /** Handles chunk operations and caching */
140    protected IChunkProvider chunkProvider;
141    protected final ISaveHandler saveHandler;
142
143    /**
144     * holds information about a world (size on disk, time, spawn point, seed, ...)
145     */
146    protected WorldInfo worldInfo;
147
148    /** Boolean that is set to true when trying to find a spawn point */
149    public boolean findingSpawnPoint;
150    public MapStorage mapStorage;
151    public VillageCollection villageCollectionObj;
152    protected final VillageSiege villageSiegeObj = new VillageSiege(this);
153    public final Profiler theProfiler;
154
155    /** The world-local pool of vectors */
156    private final Vec3Pool vecPool = new Vec3Pool(300, 2000);
157    private final Calendar theCalendar = Calendar.getInstance();
158    protected Scoreboard worldScoreboard = new Scoreboard();
159    private final ILogAgent field_98181_L;
160    private ArrayList collidingBoundingBoxes = new ArrayList();
161    private boolean scanningTileEntities;
162
163    /** indicates if enemies are spawned or not */
164    protected boolean spawnHostileMobs = true;
165
166    /** A flag indicating whether we should spawn peaceful mobs. */
167    protected boolean spawnPeacefulMobs = true;
168
169    /** Positions to update */
170    public Set activeChunkSet = new HashSet();
171
172    /** number of ticks until the next random ambients play */
173    private int ambientTickCountdown;
174
175    /**
176     * is a temporary list of blocks and light values used when updating light levels. Holds up to 32x32x32 blocks (the
177     * maximum influence of a light source.) Every element is a packed bit value: 0000000000LLLLzzzzzzyyyyyyxxxxxx. The
178     * 4-bit L is a light level used when darkening blocks. 6-bit numbers x, y and z represent the block's offset from
179     * the original block, plus 32 (i.e. value of 31 would mean a -1 offset
180     */
181    int[] lightUpdateBlockList;
182
183    /** This is set to true for client worlds, and false for server worlds. */
184    public boolean isRemote;
185
186    /**
187     * Gets the biome for a given set of x/z coordinates
188     */
189    public BiomeGenBase getBiomeGenForCoords(int par1, int par2)
190    {
191        return provider.getBiomeGenForCoords(par1, par2);
192    }
193
194    public BiomeGenBase getBiomeGenForCoordsBody(int par1, int par2)
195    {
196        if (this.blockExists(par1, 0, par2))
197        {
198            Chunk chunk = this.getChunkFromBlockCoords(par1, par2);
199
200            if (chunk != null)
201            {
202                return chunk.getBiomeGenForWorldCoords(par1 & 15, par2 & 15, this.provider.worldChunkMgr);
203            }
204        }
205
206        return this.provider.worldChunkMgr.getBiomeGenAt(par1, par2);
207    }
208
209    public WorldChunkManager getWorldChunkManager()
210    {
211        return this.provider.worldChunkMgr;
212    }
213
214    @SideOnly(Side.CLIENT)
215    public World(ISaveHandler par1ISaveHandler, String par2Str, WorldProvider par3WorldProvider, WorldSettings par4WorldSettings, Profiler par5Profiler, ILogAgent par6ILogAgent)
216    {
217        this.ambientTickCountdown = this.rand.nextInt(12000);
218        this.lightUpdateBlockList = new int[32768];
219        this.isRemote = false;
220        this.saveHandler = par1ISaveHandler;
221        this.theProfiler = par5Profiler;
222        this.worldInfo = new WorldInfo(par4WorldSettings, par2Str);
223        this.provider = par3WorldProvider;
224        perWorldStorage = new MapStorage((ISaveHandler)null);
225        this.field_98181_L = par6ILogAgent;
226    }
227
228    // Broken up so that the WorldClient gets the chance to set the mapstorage object before the dimension initializes
229    @SideOnly(Side.CLIENT)
230    protected void finishSetup()
231    {
232        VillageCollection villagecollection = (VillageCollection)this.mapStorage.loadData(VillageCollection.class, "villages");
233
234        if (villagecollection == null)
235        {
236            this.villageCollectionObj = new VillageCollection(this);
237            this.mapStorage.setData("villages", this.villageCollectionObj);
238        }
239        else
240        {
241            this.villageCollectionObj = villagecollection;
242            this.villageCollectionObj.func_82566_a(this);
243        }
244
245        this.provider.registerWorld(this);
246        this.chunkProvider = this.createChunkProvider();
247        this.calculateInitialSkylight();
248        this.calculateInitialWeather();
249    }
250
251    public World(ISaveHandler par1ISaveHandler, String par2Str, WorldSettings par3WorldSettings, WorldProvider par4WorldProvider, Profiler par5Profiler, ILogAgent par6ILogAgent)
252    {
253        this.ambientTickCountdown = this.rand.nextInt(12000);
254        this.lightUpdateBlockList = new int[32768];
255        this.isRemote = false;
256        this.saveHandler = par1ISaveHandler;
257        this.theProfiler = par5Profiler;
258        this.mapStorage = getMapStorage(par1ISaveHandler);
259        this.field_98181_L = par6ILogAgent;
260        this.worldInfo = par1ISaveHandler.loadWorldInfo();
261
262        if (par4WorldProvider != null)
263        {
264            this.provider = par4WorldProvider;
265        }
266        else if (this.worldInfo != null && this.worldInfo.getDimension() != 0)
267        {
268            this.provider = WorldProvider.getProviderForDimension(this.worldInfo.getDimension());
269        }
270        else
271        {
272            this.provider = WorldProvider.getProviderForDimension(0);
273        }
274
275        if (this.worldInfo == null)
276        {
277            this.worldInfo = new WorldInfo(par3WorldSettings, par2Str);
278        }
279        else
280        {
281            this.worldInfo.setWorldName(par2Str);
282        }
283
284        this.provider.registerWorld(this);
285        this.chunkProvider = this.createChunkProvider();
286
287        if (!this.worldInfo.isInitialized())
288        {
289            try
290            {
291                this.initialize(par3WorldSettings);
292            }
293            catch (Throwable throwable)
294            {
295                CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Exception initializing level");
296
297                try
298                {
299                    this.addWorldInfoToCrashReport(crashreport);
300                }
301                catch (Throwable throwable1)
302                {
303                    ;
304                }
305
306                throw new ReportedException(crashreport);
307            }
308
309            this.worldInfo.setServerInitialized(true);
310        }
311
312        if (this instanceof WorldServer)
313        {
314            this.perWorldStorage = new MapStorage(new WorldSpecificSaveHandler((WorldServer)this, par1ISaveHandler));
315        }
316        else
317        {
318            this.perWorldStorage = new MapStorage((ISaveHandler)null);
319        }
320        VillageCollection villagecollection = (VillageCollection)perWorldStorage.loadData(VillageCollection.class, "villages");
321
322        if (villagecollection == null)
323        {
324            this.villageCollectionObj = new VillageCollection(this);
325            this.perWorldStorage.setData("villages", this.villageCollectionObj);
326        }
327        else
328        {
329            this.villageCollectionObj = villagecollection;
330            this.villageCollectionObj.func_82566_a(this);
331        }
332
333        this.calculateInitialSkylight();
334        this.calculateInitialWeather();
335    }
336
337    private static MapStorage s_mapStorage;
338    private static ISaveHandler s_savehandler;
339    //Provides a solution for different worlds getting different copies of the same data, potentially rewriting the data or causing race conditions/stale data
340    //Buildcraft has suffered from the issue this fixes.  If you load the same data from two different worlds they can get two different copies of the same object, thus the last saved gets final say.
341    private MapStorage getMapStorage(ISaveHandler savehandler)
342    {
343        if (s_savehandler != savehandler || s_mapStorage == null) {
344            s_mapStorage = new MapStorage(savehandler);
345            s_savehandler = savehandler;
346        }
347        return s_mapStorage;
348    }
349
350    /**
351     * Creates the chunk provider for this world. Called in the constructor. Retrieves provider from worldProvider?
352     */
353    protected abstract IChunkProvider createChunkProvider();
354
355    protected void initialize(WorldSettings par1WorldSettings)
356    {
357        this.worldInfo.setServerInitialized(true);
358    }
359
360    @SideOnly(Side.CLIENT)
361
362    /**
363     * Sets a new spawn location by finding an uncovered block at a random (x,z) location in the chunk.
364     */
365    public void setSpawnLocation()
366    {
367        this.setSpawnLocation(8, 64, 8);
368    }
369
370    /**
371     * Returns the block ID of the first block at this (x,z) location with air above it, searching from sea level
372     * upwards.
373     */
374    public int getFirstUncoveredBlock(int par1, int par2)
375    {
376        int k;
377
378        for (k = 63; !this.isAirBlock(par1, k + 1, par2); ++k)
379        {
380            ;
381        }
382
383        return this.getBlockId(par1, k, par2);
384    }
385
386    /**
387     * Returns the block ID at coords x,y,z
388     */
389    public int getBlockId(int par1, int par2, int par3)
390    {
391        if (par1 >= -30000000 && par3 >= -30000000 && par1 < 30000000 && par3 < 30000000)
392        {
393            if (par2 < 0)
394            {
395                return 0;
396            }
397            else if (par2 >= 256)
398            {
399                return 0;
400            }
401            else
402            {
403                Chunk chunk = null;
404
405                try
406                {
407                    chunk = this.getChunkFromChunkCoords(par1 >> 4, par3 >> 4);
408                    return chunk.getBlockID(par1 & 15, par2, par3 & 15);
409                }
410                catch (Throwable throwable)
411                {
412                    CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Exception getting block type in world");
413                    CrashReportCategory crashreportcategory = crashreport.makeCategory("Requested block coordinates");
414                    crashreportcategory.addCrashSection("Found chunk", Boolean.valueOf(chunk == null));
415                    crashreportcategory.addCrashSection("Location", CrashReportCategory.func_85071_a(par1, par2, par3));
416                    throw new ReportedException(crashreport);
417                }
418            }
419        }
420        else
421        {
422            return 0;
423        }
424    }
425
426    /**
427     * Returns true if the block at the specified coordinates is empty
428     */
429    public boolean isAirBlock(int par1, int par2, int par3)
430    {
431        int id = getBlockId(par1, par2, par3);
432        return id == 0 || Block.blocksList[id] == null || Block.blocksList[id].isAirBlock(this, par1, par2, par3);
433    }
434
435    /**
436     * Checks if a block at a given position should have a tile entity.
437     */
438    public boolean blockHasTileEntity(int par1, int par2, int par3)
439    {
440        int l = this.getBlockId(par1, par2, par3);
441        int meta = this.getBlockMetadata(par1, par2, par3);
442        return Block.blocksList[l] != null && Block.blocksList[l].hasTileEntity(meta);
443    }
444
445    /**
446     * Returns the render type of the block at the given coordinate.
447     */
448    public int blockGetRenderType(int par1, int par2, int par3)
449    {
450        int l = this.getBlockId(par1, par2, par3);
451        return Block.blocksList[l] != null ? Block.blocksList[l].getRenderType() : -1;
452    }
453
454    /**
455     * Returns whether a block exists at world coordinates x, y, z
456     */
457    public boolean blockExists(int par1, int par2, int par3)
458    {
459        return par2 >= 0 && par2 < 256 ? this.chunkExists(par1 >> 4, par3 >> 4) : false;
460    }
461
462    /**
463     * Checks if any of the chunks within distance (argument 4) blocks of the given block exist
464     */
465    public boolean doChunksNearChunkExist(int par1, int par2, int par3, int par4)
466    {
467        return this.checkChunksExist(par1 - par4, par2 - par4, par3 - par4, par1 + par4, par2 + par4, par3 + par4);
468    }
469
470    /**
471     * Checks between a min and max all the chunks inbetween actually exist. Args: minX, minY, minZ, maxX, maxY, maxZ
472     */
473    public boolean checkChunksExist(int par1, int par2, int par3, int par4, int par5, int par6)
474    {
475        if (par5 >= 0 && par2 < 256)
476        {
477            par1 >>= 4;
478            par3 >>= 4;
479            par4 >>= 4;
480            par6 >>= 4;
481
482            for (int k1 = par1; k1 <= par4; ++k1)
483            {
484                for (int l1 = par3; l1 <= par6; ++l1)
485                {
486                    if (!this.chunkExists(k1, l1))
487                    {
488                        return false;
489                    }
490                }
491            }
492
493            return true;
494        }
495        else
496        {
497            return false;
498        }
499    }
500
501    /**
502     * Returns whether a chunk exists at chunk coordinates x, y
503     */
504    protected boolean chunkExists(int par1, int par2)
505    {
506        return this.chunkProvider.chunkExists(par1, par2);
507    }
508
509    /**
510     * Returns a chunk looked up by block coordinates. Args: x, z
511     */
512    public Chunk getChunkFromBlockCoords(int par1, int par2)
513    {
514        return this.getChunkFromChunkCoords(par1 >> 4, par2 >> 4);
515    }
516
517    /**
518     * Returns back a chunk looked up by chunk coordinates Args: x, y
519     */
520    public Chunk getChunkFromChunkCoords(int par1, int par2)
521    {
522        return this.chunkProvider.provideChunk(par1, par2);
523    }
524
525    /**
526     * Sets the block ID and metadata at a given location. Args: X, Y, Z, new block ID, new metadata, flags. Flag 0x02
527     * will trigger a block update both on server and on client, flag 0x04, if used with 0x02, will prevent a block
528     * update on client worlds. Flag 0x01 will pass the original block ID when notifying adjacent blocks, otherwise it
529     * will pass 0.
530     */
531    public boolean setBlock(int par1, int par2, int par3, int par4, int par5, int par6)
532    {
533        if (par1 >= -30000000 && par3 >= -30000000 && par1 < 30000000 && par3 < 30000000)
534        {
535            if (par2 < 0)
536            {
537                return false;
538            }
539            else if (par2 >= 256)
540            {
541                return false;
542            }
543            else
544            {
545                Chunk chunk = this.getChunkFromChunkCoords(par1 >> 4, par3 >> 4);
546                int k1 = 0;
547
548                if ((par6 & 1) != 0)
549                {
550                    k1 = chunk.getBlockID(par1 & 15, par2, par3 & 15);
551                }
552
553                boolean flag = chunk.setBlockIDWithMetadata(par1 & 15, par2, par3 & 15, par4, par5);
554                this.theProfiler.startSection("checkLight");
555                this.updateAllLightTypes(par1, par2, par3);
556                this.theProfiler.endSection();
557
558                if (flag)
559                {
560                    if ((par6 & 2) != 0 && (!this.isRemote || (par6 & 4) == 0))
561                    {
562                        this.markBlockForUpdate(par1, par2, par3);
563                    }
564
565                    if (!this.isRemote && (par6 & 1) != 0)
566                    {
567                        this.notifyBlockChange(par1, par2, par3, k1);
568                        Block block = Block.blocksList[par4];
569
570                        if (block != null && block.hasComparatorInputOverride())
571                        {
572                            this.func_96440_m(par1, par2, par3, par4);
573                        }
574                    }
575                }
576
577                return flag;
578            }
579        }
580        else
581        {
582            return false;
583        }
584    }
585
586    /**
587     * Returns the block's material.
588     */
589    public Material getBlockMaterial(int par1, int par2, int par3)
590    {
591        int l = this.getBlockId(par1, par2, par3);
592        return l == 0 ? Material.air : Block.blocksList[l].blockMaterial;
593    }
594
595    /**
596     * Returns the block metadata at coords x,y,z
597     */
598    public int getBlockMetadata(int par1, int par2, int par3)
599    {
600        if (par1 >= -30000000 && par3 >= -30000000 && par1 < 30000000 && par3 < 30000000)
601        {
602            if (par2 < 0)
603            {
604                return 0;
605            }
606            else if (par2 >= 256)
607            {
608                return 0;
609            }
610            else
611            {
612                Chunk chunk = this.getChunkFromChunkCoords(par1 >> 4, par3 >> 4);
613                par1 &= 15;
614                par3 &= 15;
615                return chunk.getBlockMetadata(par1, par2, par3);
616            }
617        }
618        else
619        {
620            return 0;
621        }
622    }
623
624    /**
625     * Sets the blocks metadata and if set will then notify blocks that this block changed, depending on the flag. Args:
626     * x, y, z, metadata, flag. See setBlock for flag description
627     */
628    public boolean setBlockMetadataWithNotify(int par1, int par2, int par3, int par4, int par5)
629    {
630        if (par1 >= -30000000 && par3 >= -30000000 && par1 < 30000000 && par3 < 30000000)
631        {
632            if (par2 < 0)
633            {
634                return false;
635            }
636            else if (par2 >= 256)
637            {
638                return false;
639            }
640            else
641            {
642                Chunk chunk = this.getChunkFromChunkCoords(par1 >> 4, par3 >> 4);
643                int j1 = par1 & 15;
644                int k1 = par3 & 15;
645                boolean flag = chunk.setBlockMetadata(j1, par2, k1, par4);
646
647                if (flag)
648                {
649                    int l1 = chunk.getBlockID(j1, par2, k1);
650
651                    if ((par5 & 2) != 0 && (!this.isRemote || (par5 & 4) == 0))
652                    {
653                        this.markBlockForUpdate(par1, par2, par3);
654                    }
655
656                    if (!this.isRemote && (par5 & 1) != 0)
657                    {
658                        this.notifyBlockChange(par1, par2, par3, l1);
659                        Block block = Block.blocksList[l1];
660
661                        if (block != null && block.hasComparatorInputOverride())
662                        {
663                            this.func_96440_m(par1, par2, par3, l1);
664                        }
665                    }
666                }
667
668                return flag;
669            }
670        }
671        else
672        {
673            return false;
674        }
675    }
676
677    /**
678     * Sets a block to 0 and notifies relevant systems with the block change  Args: x, y, z
679     */
680    public boolean setBlockToAir(int par1, int par2, int par3)
681    {
682        return this.setBlock(par1, par2, par3, 0, 0, 3);
683    }
684
685    /**
686     * Destroys a block and optionally drops items. Args: X, Y, Z, dropItems
687     */
688    public boolean destroyBlock(int par1, int par2, int par3, boolean par4)
689    {
690        int l = this.getBlockId(par1, par2, par3);
691
692        if (l > 0)
693        {
694            int i1 = this.getBlockMetadata(par1, par2, par3);
695            this.playAuxSFX(2001, par1, par2, par3, l + (i1 << 12));
696
697            if (par4)
698            {
699                Block.blocksList[l].dropBlockAsItem(this, par1, par2, par3, i1, 0);
700            }
701
702            return this.setBlock(par1, par2, par3, 0, 0, 3);
703        }
704        else
705        {
706            return false;
707        }
708    }
709
710    /**
711     * Sets a block and notifies relevant systems with the block change  Args: x, y, z, blockID
712     */
713    public boolean setBlock(int par1, int par2, int par3, int par4)
714    {
715        return this.setBlock(par1, par2, par3, par4, 0, 3);
716    }
717
718    /**
719     * On the client, re-renders the block. On the server, sends the block to the client (which will re-render it),
720     * including the tile entity description packet if applicable. Args: x, y, z
721     */
722    public void markBlockForUpdate(int par1, int par2, int par3)
723    {
724        for (int l = 0; l < this.worldAccesses.size(); ++l)
725        {
726            ((IWorldAccess)this.worldAccesses.get(l)).markBlockForUpdate(par1, par2, par3);
727        }
728    }
729
730    /**
731     * The block type change and need to notify other systems  Args: x, y, z, blockID
732     */
733    public void notifyBlockChange(int par1, int par2, int par3, int par4)
734    {
735        this.notifyBlocksOfNeighborChange(par1, par2, par3, par4);
736    }
737
738    /**
739     * marks a vertical line of blocks as dirty
740     */
741    public void markBlocksDirtyVertical(int par1, int par2, int par3, int par4)
742    {
743        int i1;
744
745        if (par3 > par4)
746        {
747            i1 = par4;
748            par4 = par3;
749            par3 = i1;
750        }
751
752        if (!this.provider.hasNoSky)
753        {
754            for (i1 = par3; i1 <= par4; ++i1)
755            {
756                this.updateLightByType(EnumSkyBlock.Sky, par1, i1, par2);
757            }
758        }
759
760        this.markBlockRangeForRenderUpdate(par1, par3, par2, par1, par4, par2);
761    }
762
763    /**
764     * On the client, re-renders all blocks in this range, inclusive. On the server, does nothing. Args: min x, min y,
765     * min z, max x, max y, max z
766     */
767    public void markBlockRangeForRenderUpdate(int par1, int par2, int par3, int par4, int par5, int par6)
768    {
769        for (int k1 = 0; k1 < this.worldAccesses.size(); ++k1)
770        {
771            ((IWorldAccess)this.worldAccesses.get(k1)).markBlockRangeForRenderUpdate(par1, par2, par3, par4, par5, par6);
772        }
773    }
774
775    /**
776     * Notifies neighboring blocks that this specified block changed  Args: x, y, z, blockID
777     */
778    public void notifyBlocksOfNeighborChange(int par1, int par2, int par3, int par4)
779    {
780        this.notifyBlockOfNeighborChange(par1 - 1, par2, par3, par4);
781        this.notifyBlockOfNeighborChange(par1 + 1, par2, par3, par4);
782        this.notifyBlockOfNeighborChange(par1, par2 - 1, par3, par4);
783        this.notifyBlockOfNeighborChange(par1, par2 + 1, par3, par4);
784        this.notifyBlockOfNeighborChange(par1, par2, par3 - 1, par4);
785        this.notifyBlockOfNeighborChange(par1, par2, par3 + 1, par4);
786    }
787
788    /**
789     * Calls notifyBlockOfNeighborChange on adjacent blocks, except the one on the given side. Args: X, Y, Z,
790     * changingBlockID, side
791     */
792    public void notifyBlocksOfNeighborChange(int par1, int par2, int par3, int par4, int par5)
793    {
794        if (par5 != 4)
795        {
796            this.notifyBlockOfNeighborChange(par1 - 1, par2, par3, par4);
797        }
798
799        if (par5 != 5)
800        {
801            this.notifyBlockOfNeighborChange(par1 + 1, par2, par3, par4);
802        }
803
804        if (par5 != 0)
805        {
806            this.notifyBlockOfNeighborChange(par1, par2 - 1, par3, par4);
807        }
808
809        if (par5 != 1)
810        {
811            this.notifyBlockOfNeighborChange(par1, par2 + 1, par3, par4);
812        }
813
814        if (par5 != 2)
815        {
816            this.notifyBlockOfNeighborChange(par1, par2, par3 - 1, par4);
817        }
818
819        if (par5 != 3)
820        {
821            this.notifyBlockOfNeighborChange(par1, par2, par3 + 1, par4);
822        }
823    }
824
825    /**
826     * Notifies a block that one of its neighbor change to the specified type Args: x, y, z, blockID
827     */
828    public void notifyBlockOfNeighborChange(int par1, int par2, int par3, int par4)
829    {
830        if (!this.isRemote)
831        {
832            int i1 = this.getBlockId(par1, par2, par3);
833            Block block = Block.blocksList[i1];
834
835            if (block != null)
836            {
837                try
838                {
839                    block.onNeighborBlockChange(this, par1, par2, par3, par4);
840                }
841                catch (Throwable throwable)
842                {
843                    CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Exception while updating neighbours");
844                    CrashReportCategory crashreportcategory = crashreport.makeCategory("Block being updated");
845                    int j1;
846
847                    try
848                    {
849                        j1 = this.getBlockMetadata(par1, par2, par3);
850                    }
851                    catch (Throwable throwable1)
852                    {
853                        j1 = -1;
854                    }
855
856                    crashreportcategory.addCrashSectionCallable("Source block type", new CallableLvl1(this, par4));
857                    CrashReportCategory.func_85068_a(crashreportcategory, par1, par2, par3, i1, j1);
858                    throw new ReportedException(crashreport);
859                }
860            }
861        }
862    }
863
864    /**
865     * Returns true if the given block will receive a scheduled tick in the future. Args: X, Y, Z, blockID
866     */
867    public boolean isBlockTickScheduled(int par1, int par2, int par3, int par4)
868    {
869        return false;
870    }
871
872    /**
873     * Checks if the specified block is able to see the sky
874     */
875    public boolean canBlockSeeTheSky(int par1, int par2, int par3)
876    {
877        return this.getChunkFromChunkCoords(par1 >> 4, par3 >> 4).canBlockSeeTheSky(par1 & 15, par2, par3 & 15);
878    }
879
880    /**
881     * Does the same as getBlockLightValue_do but without checking if its not a normal block
882     */
883    public int getFullBlockLightValue(int par1, int par2, int par3)
884    {
885        if (par2 < 0)
886        {
887            return 0;
888        }
889        else
890        {
891            if (par2 >= 256)
892            {
893                par2 = 255;
894            }
895
896            return this.getChunkFromChunkCoords(par1 >> 4, par3 >> 4).getBlockLightValue(par1 & 15, par2, par3 & 15, 0);
897        }
898    }
899
900    /**
901     * Gets the light value of a block location
902     */
903    public int getBlockLightValue(int par1, int par2, int par3)
904    {
905        return this.getBlockLightValue_do(par1, par2, par3, true);
906    }
907
908    /**
909     * Gets the light value of a block location. This is the actual function that gets the value and has a bool flag
910     * that indicates if its a half step block to get the maximum light value of a direct neighboring block (left,
911     * right, forward, back, and up)
912     */
913    public int getBlockLightValue_do(int par1, int par2, int par3, boolean par4)
914    {
915        if (par1 >= -30000000 && par3 >= -30000000 && par1 < 30000000 && par3 < 30000000)
916        {
917            if (par4)
918            {
919                int l = this.getBlockId(par1, par2, par3);
920
921                if (Block.useNeighborBrightness[l])
922                {
923                    int i1 = this.getBlockLightValue_do(par1, par2 + 1, par3, false);
924                    int j1 = this.getBlockLightValue_do(par1 + 1, par2, par3, false);
925                    int k1 = this.getBlockLightValue_do(par1 - 1, par2, par3, false);
926                    int l1 = this.getBlockLightValue_do(par1, par2, par3 + 1, false);
927                    int i2 = this.getBlockLightValue_do(par1, par2, par3 - 1, false);
928
929                    if (j1 > i1)
930                    {
931                        i1 = j1;
932                    }
933
934                    if (k1 > i1)
935                    {
936                        i1 = k1;
937                    }
938
939                    if (l1 > i1)
940                    {
941                        i1 = l1;
942                    }
943
944                    if (i2 > i1)
945                    {
946                        i1 = i2;
947                    }
948
949                    return i1;
950                }
951            }
952
953            if (par2 < 0)
954            {
955                return 0;
956            }
957            else
958            {
959                if (par2 >= 256)
960                {
961                    par2 = 255;
962                }
963
964                Chunk chunk = this.getChunkFromChunkCoords(par1 >> 4, par3 >> 4);
965                par1 &= 15;
966                par3 &= 15;
967                return chunk.getBlockLightValue(par1, par2, par3, this.skylightSubtracted);
968            }
969        }
970        else
971        {
972            return 15;
973        }
974    }
975
976    /**
977     * Returns the y coordinate with a block in it at this x, z coordinate
978     */
979    public int getHeightValue(int par1, int par2)
980    {
981        if (par1 >= -30000000 && par2 >= -30000000 && par1 < 30000000 && par2 < 30000000)
982        {
983            if (!this.chunkExists(par1 >> 4, par2 >> 4))
984            {
985                return 0;
986            }
987            else
988            {
989                Chunk chunk = this.getChunkFromChunkCoords(par1 >> 4, par2 >> 4);
990                return chunk.getHeightValue(par1 & 15, par2 & 15);
991            }
992        }
993        else
994        {
995            return 0;
996        }
997    }
998
999    /**
1000     * Gets the heightMapMinimum field of the given chunk, or 0 if the chunk is not loaded. Coords are in blocks. Args:
1001     * X, Z
1002     */
1003    public int getChunkHeightMapMinimum(int par1, int par2)
1004    {
1005        if (par1 >= -30000000 && par2 >= -30000000 && par1 < 30000000 && par2 < 30000000)
1006        {
1007            if (!this.chunkExists(par1 >> 4, par2 >> 4))
1008            {
1009                return 0;
1010            }
1011            else
1012            {
1013                Chunk chunk = this.getChunkFromChunkCoords(par1 >> 4, par2 >> 4);
1014                return chunk.heightMapMinimum;
1015            }
1016        }
1017        else
1018        {
1019            return 0;
1020        }
1021    }
1022
1023    @SideOnly(Side.CLIENT)
1024
1025    /**
1026     * Brightness for SkyBlock.Sky is clear white and (through color computing it is assumed) DEPENDENT ON DAYTIME.
1027     * Brightness for SkyBlock.Block is yellowish and independent.
1028     */
1029    public int getSkyBlockTypeBrightness(EnumSkyBlock par1EnumSkyBlock, int par2, int par3, int par4)
1030    {
1031        if (this.provider.hasNoSky && par1EnumSkyBlock == EnumSkyBlock.Sky)
1032        {
1033            return 0;
1034        }
1035        else
1036        {
1037            if (par3 < 0)
1038            {
1039                par3 = 0;
1040            }
1041
1042            if (par3 >= 256)
1043            {
1044                return par1EnumSkyBlock.defaultLightValue;
1045            }
1046            else if (par2 >= -30000000 && par4 >= -30000000 && par2 < 30000000 && par4 < 30000000)
1047            {
1048                int l = par2 >> 4;
1049                int i1 = par4 >> 4;
1050
1051                if (!this.chunkExists(l, i1))
1052                {
1053                    return par1EnumSkyBlock.defaultLightValue;
1054                }
1055                else if (Block.useNeighborBrightness[this.getBlockId(par2, par3, par4)])
1056                {
1057                    int j1 = this.getSavedLightValue(par1EnumSkyBlock, par2, par3 + 1, par4);
1058                    int k1 = this.getSavedLightValue(par1EnumSkyBlock, par2 + 1, par3, par4);
1059                    int l1 = this.getSavedLightValue(par1EnumSkyBlock, par2 - 1, par3, par4);
1060                    int i2 = this.getSavedLightValue(par1EnumSkyBlock, par2, par3, par4 + 1);
1061                    int j2 = this.getSavedLightValue(par1EnumSkyBlock, par2, par3, par4 - 1);
1062
1063                    if (k1 > j1)
1064                    {
1065                        j1 = k1;
1066                    }
1067
1068                    if (l1 > j1)
1069                    {
1070                        j1 = l1;
1071                    }
1072
1073                    if (i2 > j1)
1074                    {
1075                        j1 = i2;
1076                    }
1077
1078                    if (j2 > j1)
1079                    {
1080                        j1 = j2;
1081                    }
1082
1083                    return j1;
1084                }
1085                else
1086                {
1087                    Chunk chunk = this.getChunkFromChunkCoords(l, i1);
1088                    return chunk.getSavedLightValue(par1EnumSkyBlock, par2 & 15, par3, par4 & 15);
1089                }
1090            }
1091            else
1092            {
1093                return par1EnumSkyBlock.defaultLightValue;
1094            }
1095        }
1096    }
1097
1098    /**
1099     * Returns saved light value without taking into account the time of day.  Either looks in the sky light map or
1100     * block light map based on the enumSkyBlock arg.
1101     */
1102    public int getSavedLightValue(EnumSkyBlock par1EnumSkyBlock, int par2, int par3, int par4)
1103    {
1104        if (par3 < 0)
1105        {
1106            par3 = 0;
1107        }
1108
1109        if (par3 >= 256)
1110        {
1111            par3 = 255;
1112        }
1113
1114        if (par2 >= -30000000 && par4 >= -30000000 && par2 < 30000000 && par4 < 30000000)
1115        {
1116            int l = par2 >> 4;
1117            int i1 = par4 >> 4;
1118
1119            if (!this.chunkExists(l, i1))
1120            {
1121                return par1EnumSkyBlock.defaultLightValue;
1122            }
1123            else
1124            {
1125                Chunk chunk = this.getChunkFromChunkCoords(l, i1);
1126                return chunk.getSavedLightValue(par1EnumSkyBlock, par2 & 15, par3, par4 & 15);
1127            }
1128        }
1129        else
1130        {
1131            return par1EnumSkyBlock.defaultLightValue;
1132        }
1133    }
1134
1135    /**
1136     * Sets the light value either into the sky map or block map depending on if enumSkyBlock is set to sky or block.
1137     * Args: enumSkyBlock, x, y, z, lightValue
1138     */
1139    public void setLightValue(EnumSkyBlock par1EnumSkyBlock, int par2, int par3, int par4, int par5)
1140    {
1141        if (par2 >= -30000000 && par4 >= -30000000 && par2 < 30000000 && par4 < 30000000)
1142        {
1143            if (par3 >= 0)
1144            {
1145                if (par3 < 256)
1146                {
1147                    if (this.chunkExists(par2 >> 4, par4 >> 4))
1148                    {
1149                        Chunk chunk = this.getChunkFromChunkCoords(par2 >> 4, par4 >> 4);
1150                        chunk.setLightValue(par1EnumSkyBlock, par2 & 15, par3, par4 & 15, par5);
1151
1152                        for (int i1 = 0; i1 < this.worldAccesses.size(); ++i1)
1153                        {
1154                            ((IWorldAccess)this.worldAccesses.get(i1)).markBlockForRenderUpdate(par2, par3, par4);
1155                        }
1156                    }
1157                }
1158            }
1159        }
1160    }
1161
1162    /**
1163     * On the client, re-renders this block. On the server, does nothing. Used for lighting updates.
1164     */
1165    public void markBlockForRenderUpdate(int par1, int par2, int par3)
1166    {
1167        for (int l = 0; l < this.worldAccesses.size(); ++l)
1168        {
1169            ((IWorldAccess)this.worldAccesses.get(l)).markBlockForRenderUpdate(par1, par2, par3);
1170        }
1171    }
1172
1173    @SideOnly(Side.CLIENT)
1174
1175    /**
1176     * Any Light rendered on a 1.8 Block goes through here
1177     */
1178    public int getLightBrightnessForSkyBlocks(int par1, int par2, int par3, int par4)
1179    {
1180        int i1 = this.getSkyBlockTypeBrightness(EnumSkyBlock.Sky, par1, par2, par3);
1181        int j1 = this.getSkyBlockTypeBrightness(EnumSkyBlock.Block, par1, par2, par3);
1182
1183        if (j1 < par4)
1184        {
1185            j1 = par4;
1186        }
1187
1188        return i1 << 20 | j1 << 4;
1189    }
1190
1191    @SideOnly(Side.CLIENT)
1192    public float getBrightness(int par1, int par2, int par3, int par4)
1193    {
1194        int i1 = this.getBlockLightValue(par1, par2, par3);
1195
1196        if (i1 < par4)
1197        {
1198            i1 = par4;
1199        }
1200
1201        return this.provider.lightBrightnessTable[i1];
1202    }
1203
1204    /**
1205     * Returns how bright the block is shown as which is the block's light value looked up in a lookup table (light
1206     * values aren't linear for brightness). Args: x, y, z
1207     */
1208    public float getLightBrightness(int par1, int par2, int par3)
1209    {
1210        return this.provider.lightBrightnessTable[this.getBlockLightValue(par1, par2, par3)];
1211    }
1212
1213    /**
1214     * Checks whether its daytime by seeing if the light subtracted from the skylight is less than 4
1215     */
1216    public boolean isDaytime()
1217    {
1218        return provider.isDaytime();
1219    }
1220
1221    /**
1222     * ray traces all blocks, including non-collideable ones
1223     */
1224    public MovingObjectPosition rayTraceBlocks(Vec3 par1Vec3, Vec3 par2Vec3)
1225    {
1226        return this.rayTraceBlocks_do_do(par1Vec3, par2Vec3, false, false);
1227    }
1228
1229    public MovingObjectPosition rayTraceBlocks_do(Vec3 par1Vec3, Vec3 par2Vec3, boolean par3)
1230    {
1231        return this.rayTraceBlocks_do_do(par1Vec3, par2Vec3, par3, false);
1232    }
1233
1234    public MovingObjectPosition rayTraceBlocks_do_do(Vec3 par1Vec3, Vec3 par2Vec3, boolean par3, boolean par4)
1235    {
1236        if (!Double.isNaN(par1Vec3.xCoord) && !Double.isNaN(par1Vec3.yCoord) && !Double.isNaN(par1Vec3.zCoord))
1237        {
1238            if (!Double.isNaN(par2Vec3.xCoord) && !Double.isNaN(par2Vec3.yCoord) && !Double.isNaN(par2Vec3.zCoord))
1239            {
1240                int i = MathHelper.floor_double(par2Vec3.xCoord);
1241                int j = MathHelper.floor_double(par2Vec3.yCoord);
1242                int k = MathHelper.floor_double(par2Vec3.zCoord);
1243                int l = MathHelper.floor_double(par1Vec3.xCoord);
1244                int i1 = MathHelper.floor_double(par1Vec3.yCoord);
1245                int j1 = MathHelper.floor_double(par1Vec3.zCoord);
1246                int k1 = this.getBlockId(l, i1, j1);
1247                int l1 = this.getBlockMetadata(l, i1, j1);
1248                Block block = Block.blocksList[k1];
1249
1250                if (block != null && (!par4 || block == null || block.getCollisionBoundingBoxFromPool(this, l, i1, j1) != null) && k1 > 0 && block.canCollideCheck(l1, par3))
1251                {
1252                    MovingObjectPosition movingobjectposition = block.collisionRayTrace(this, l, i1, j1, par1Vec3, par2Vec3);
1253
1254                    if (movingobjectposition != null)
1255                    {
1256                        return movingobjectposition;
1257                    }
1258                }
1259
1260                k1 = 200;
1261
1262                while (k1-- >= 0)
1263                {
1264                    if (Double.isNaN(par1Vec3.xCoord) || Double.isNaN(par1Vec3.yCoord) || Double.isNaN(par1Vec3.zCoord))
1265                    {
1266                        return null;
1267                    }
1268
1269                    if (l == i && i1 == j && j1 == k)
1270                    {
1271                        return null;
1272                    }
1273
1274                    boolean flag2 = true;
1275                    boolean flag3 = true;
1276                    boolean flag4 = true;
1277                    double d0 = 999.0D;
1278                    double d1 = 999.0D;
1279                    double d2 = 999.0D;
1280
1281                    if (i > l)
1282                    {
1283                        d0 = (double)l + 1.0D;
1284                    }
1285                    else if (i < l)
1286                    {
1287                        d0 = (double)l + 0.0D;
1288                    }
1289                    else
1290                    {
1291                        flag2 = false;
1292                    }
1293
1294                    if (j > i1)
1295                    {
1296                        d1 = (double)i1 + 1.0D;
1297                    }
1298                    else if (j < i1)
1299                    {
1300                        d1 = (double)i1 + 0.0D;
1301                    }
1302                    else
1303                    {
1304                        flag3 = false;
1305                    }
1306
1307                    if (k > j1)
1308                    {
1309                        d2 = (double)j1 + 1.0D;
1310                    }
1311                    else if (k < j1)
1312                    {
1313                        d2 = (double)j1 + 0.0D;
1314                    }
1315                    else
1316                    {
1317                        flag4 = false;
1318                    }
1319
1320                    double d3 = 999.0D;
1321                    double d4 = 999.0D;
1322                    double d5 = 999.0D;
1323                    double d6 = par2Vec3.xCoord - par1Vec3.xCoord;
1324                    double d7 = par2Vec3.yCoord - par1Vec3.yCoord;
1325                    double d8 = par2Vec3.zCoord - par1Vec3.zCoord;
1326
1327                    if (flag2)
1328                    {
1329                        d3 = (d0 - par1Vec3.xCoord) / d6;
1330                    }
1331
1332                    if (flag3)
1333                    {
1334                        d4 = (d1 - par1Vec3.yCoord) / d7;
1335                    }
1336
1337                    if (flag4)
1338                    {
1339                        d5 = (d2 - par1Vec3.zCoord) / d8;
1340                    }
1341
1342                    boolean flag5 = false;
1343                    byte b0;
1344
1345                    if (d3 < d4 && d3 < d5)
1346                    {
1347                        if (i > l)
1348                        {
1349                            b0 = 4;
1350                        }
1351                        else
1352                        {
1353                            b0 = 5;
1354                        }
1355
1356                        par1Vec3.xCoord = d0;
1357                        par1Vec3.yCoord += d7 * d3;
1358                        par1Vec3.zCoord += d8 * d3;
1359                    }
1360                    else if (d4 < d5)
1361                    {
1362                        if (j > i1)
1363                        {
1364                            b0 = 0;
1365                        }
1366                        else
1367                        {
1368                            b0 = 1;
1369                        }
1370
1371                        par1Vec3.xCoord += d6 * d4;
1372                        par1Vec3.yCoord = d1;
1373                        par1Vec3.zCoord += d8 * d4;
1374                    }
1375                    else
1376                    {
1377                        if (k > j1)
1378                        {
1379                            b0 = 2;
1380                        }
1381                        else
1382                        {
1383                            b0 = 3;
1384                        }
1385
1386                        par1Vec3.xCoord += d6 * d5;
1387                        par1Vec3.yCoord += d7 * d5;
1388                        par1Vec3.zCoord = d2;
1389                    }
1390
1391                    Vec3 vec32 = this.getWorldVec3Pool().getVecFromPool(par1Vec3.xCoord, par1Vec3.yCoord, par1Vec3.zCoord);
1392                    l = (int)(vec32.xCoord = (double)MathHelper.floor_double(par1Vec3.xCoord));
1393
1394                    if (b0 == 5)
1395                    {
1396                        --l;
1397                        ++vec32.xCoord;
1398                    }
1399
1400                    i1 = (int)(vec32.yCoord = (double)MathHelper.floor_double(par1Vec3.yCoord));
1401
1402                    if (b0 == 1)
1403                    {
1404                        --i1;
1405                        ++vec32.yCoord;
1406                    }
1407
1408                    j1 = (int)(vec32.zCoord = (double)MathHelper.floor_double(par1Vec3.zCoord));
1409
1410                    if (b0 == 3)
1411                    {
1412                        --j1;
1413                        ++vec32.zCoord;
1414                    }
1415
1416                    int i2 = this.getBlockId(l, i1, j1);
1417                    int j2 = this.getBlockMetadata(l, i1, j1);
1418                    Block block1 = Block.blocksList[i2];
1419
1420                    if ((!par4 || block1 == null || block1.getCollisionBoundingBoxFromPool(this, l, i1, j1) != null) && i2 > 0 && block1.canCollideCheck(j2, par3))
1421                    {
1422                        MovingObjectPosition movingobjectposition1 = block1.collisionRayTrace(this, l, i1, j1, par1Vec3, par2Vec3);
1423
1424                        if (movingobjectposition1 != null)
1425                        {
1426                            return movingobjectposition1;
1427                        }
1428                    }
1429                }
1430
1431                return null;
1432            }
1433            else
1434            {
1435                return null;
1436            }
1437        }
1438        else
1439        {
1440            return null;
1441        }
1442    }
1443
1444    /**
1445     * Plays a sound at the entity's position. Args: entity, sound, volume (relative to 1.0), and frequency (or pitch,
1446     * also relative to 1.0).
1447     */
1448    public void playSoundAtEntity(Entity par1Entity, String par2Str, float par3, float par4)
1449    {
1450        PlaySoundAtEntityEvent event = new PlaySoundAtEntityEvent(par1Entity, par2Str, par3, par4);
1451        if (MinecraftForge.EVENT_BUS.post(event))
1452        {
1453            return;
1454        }
1455        par2Str = event.name;
1456        if (par1Entity != null && par2Str != null)
1457        {
1458            for (int i = 0; i < this.worldAccesses.size(); ++i)
1459            {
1460                ((IWorldAccess)this.worldAccesses.get(i)).playSound(par2Str, par1Entity.posX, par1Entity.posY - (double)par1Entity.yOffset, par1Entity.posZ, par3, par4);
1461            }
1462        }
1463    }
1464
1465    /**
1466     * Plays sound to all near players except the player reference given
1467     */
1468    public void playSoundToNearExcept(EntityPlayer par1EntityPlayer, String par2Str, float par3, float par4)
1469    {
1470        PlaySoundAtEntityEvent event = new PlaySoundAtEntityEvent(par1EntityPlayer, par2Str, par3, par4);
1471        if (MinecraftForge.EVENT_BUS.post(event))
1472        {
1473            return;
1474        }
1475        par2Str = event.name;
1476        if (par1EntityPlayer != null && par2Str != null)
1477        {
1478            for (int i = 0; i < this.worldAccesses.size(); ++i)
1479            {
1480                ((IWorldAccess)this.worldAccesses.get(i)).playSoundToNearExcept(par1EntityPlayer, par2Str, par1EntityPlayer.posX, par1EntityPlayer.posY - (double)par1EntityPlayer.yOffset, par1EntityPlayer.posZ, par3, par4);
1481            }
1482        }
1483    }
1484
1485    /**
1486     * Play a sound effect. Many many parameters for this function. Not sure what they do, but a classic call is :
1487     * (double)i + 0.5D, (double)j + 0.5D, (double)k + 0.5D, 'random.door_open', 1.0F, world.rand.nextFloat() * 0.1F +
1488     * 0.9F with i,j,k position of the block.
1489     */
1490    public void playSoundEffect(double par1, double par3, double par5, String par7Str, float par8, float par9)
1491    {
1492        if (par7Str != null)
1493        {
1494            for (int i = 0; i < this.worldAccesses.size(); ++i)
1495            {
1496                ((IWorldAccess)this.worldAccesses.get(i)).playSound(par7Str, par1, par3, par5, par8, par9);
1497            }
1498        }
1499    }
1500
1501    /**
1502     * par8 is loudness, all pars passed to minecraftInstance.sndManager.playSound
1503     */
1504    public void playSound(double par1, double par3, double par5, String par7Str, float par8, float par9, boolean par10) {}
1505
1506    /**
1507     * Plays a record at the specified coordinates of the specified name. Args: recordName, x, y, z
1508     */
1509    public void playRecord(String par1Str, int par2, int par3, int par4)
1510    {
1511        for (int l = 0; l < this.worldAccesses.size(); ++l)
1512        {
1513            ((IWorldAccess)this.worldAccesses.get(l)).playRecord(par1Str, par2, par3, par4);
1514        }
1515    }
1516
1517    /**
1518     * Spawns a particle.  Args particleName, x, y, z, velX, velY, velZ
1519     */
1520    public void spawnParticle(String par1Str, double par2, double par4, double par6, double par8, double par10, double par12)
1521    {
1522        for (int i = 0; i < this.worldAccesses.size(); ++i)
1523        {
1524            ((IWorldAccess)this.worldAccesses.get(i)).spawnParticle(par1Str, par2, par4, par6, par8, par10, par12);
1525        }
1526    }
1527
1528    /**
1529     * adds a lightning bolt to the list of lightning bolts in this world.
1530     */
1531    public boolean addWeatherEffect(Entity par1Entity)
1532    {
1533        this.weatherEffects.add(par1Entity);
1534        return true;
1535    }
1536
1537    /**
1538     * Called to place all entities as part of a world
1539     */
1540    public boolean spawnEntityInWorld(Entity par1Entity)
1541    {
1542        int i = MathHelper.floor_double(par1Entity.posX / 16.0D);
1543        int j = MathHelper.floor_double(par1Entity.posZ / 16.0D);
1544        boolean flag = par1Entity.field_98038_p;
1545
1546        if (par1Entity instanceof EntityPlayer)
1547        {
1548            flag = true;
1549        }
1550
1551        if (!flag && !this.chunkExists(i, j))
1552        {
1553            return false;
1554        }
1555        else
1556        {
1557            if (par1Entity instanceof EntityPlayer)
1558            {
1559                EntityPlayer entityplayer = (EntityPlayer)par1Entity;
1560                this.playerEntities.add(entityplayer);
1561                this.updateAllPlayersSleepingFlag();
1562            }
1563
1564            if (!flag && MinecraftForge.EVENT_BUS.post(new EntityJoinWorldEvent(par1Entity, this)))
1565            {
1566                return false;
1567            }
1568
1569            this.getChunkFromChunkCoords(i, j).addEntity(par1Entity);
1570            this.loadedEntityList.add(par1Entity);
1571            this.obtainEntitySkin(par1Entity);
1572            return true;
1573        }
1574    }
1575
1576    /**
1577     * Start the skin for this entity downloading, if necessary, and increment its reference counter
1578     */
1579    protected void obtainEntitySkin(Entity par1Entity)
1580    {
1581        for (int i = 0; i < this.worldAccesses.size(); ++i)
1582        {
1583            ((IWorldAccess)this.worldAccesses.get(i)).onEntityCreate(par1Entity);
1584        }
1585    }
1586
1587    /**
1588     * Decrement the reference counter for this entity's skin image data
1589     */
1590    public void releaseEntitySkin(Entity par1Entity)
1591    {
1592        for (int i = 0; i < this.worldAccesses.size(); ++i)
1593        {
1594            ((IWorldAccess)this.worldAccesses.get(i)).onEntityDestroy(par1Entity);
1595        }
1596    }
1597
1598    /**
1599     * Schedule the entity for removal during the next tick. Marks the entity dead in anticipation.
1600     */
1601    public void removeEntity(Entity par1Entity)
1602    {
1603        if (par1Entity.riddenByEntity != null)
1604        {
1605            par1Entity.riddenByEntity.mountEntity((Entity)null);
1606        }
1607
1608        if (par1Entity.ridingEntity != null)
1609        {
1610            par1Entity.mountEntity((Entity)null);
1611        }
1612
1613        par1Entity.setDead();
1614
1615        if (par1Entity instanceof EntityPlayer)
1616        {
1617            this.playerEntities.remove(par1Entity);
1618            this.updateAllPlayersSleepingFlag();
1619        }
1620    }
1621
1622    /**
1623     * Do NOT use this method to remove normal entities- use normal removeEntity
1624     */
1625    public void removePlayerEntityDangerously(Entity par1Entity)
1626    {
1627        par1Entity.setDead();
1628
1629        if (par1Entity instanceof EntityPlayer)
1630        {
1631            this.playerEntities.remove(par1Entity);
1632            this.updateAllPlayersSleepingFlag();
1633        }
1634
1635        int i = par1Entity.chunkCoordX;
1636        int j = par1Entity.chunkCoordZ;
1637
1638        if (par1Entity.addedToChunk && this.chunkExists(i, j))
1639        {
1640            this.getChunkFromChunkCoords(i, j).removeEntity(par1Entity);
1641        }
1642
1643        this.loadedEntityList.remove(par1Entity);
1644        this.releaseEntitySkin(par1Entity);
1645    }
1646
1647    /**
1648     * Adds a IWorldAccess to the list of worldAccesses
1649     */
1650    public void addWorldAccess(IWorldAccess par1IWorldAccess)
1651    {
1652        this.worldAccesses.add(par1IWorldAccess);
1653    }
1654
1655    /**
1656     * Returns a list of bounding boxes that collide with aabb excluding the passed in entity's collision. Args: entity,
1657     * aabb
1658     */
1659    public List getCollidingBoundingBoxes(Entity par1Entity, AxisAlignedBB par2AxisAlignedBB)
1660    {
1661        this.collidingBoundingBoxes.clear();
1662        int i = MathHelper.floor_double(par2AxisAlignedBB.minX);
1663        int j = MathHelper.floor_double(par2AxisAlignedBB.maxX + 1.0D);
1664        int k = MathHelper.floor_double(par2AxisAlignedBB.minY);
1665        int l = MathHelper.floor_double(par2AxisAlignedBB.maxY + 1.0D);
1666        int i1 = MathHelper.floor_double(par2AxisAlignedBB.minZ);
1667        int j1 = MathHelper.floor_double(par2AxisAlignedBB.maxZ + 1.0D);
1668
1669        for (int k1 = i; k1 < j; ++k1)
1670        {
1671            for (int l1 = i1; l1 < j1; ++l1)
1672            {
1673                if (this.blockExists(k1, 64, l1))
1674                {
1675                    for (int i2 = k - 1; i2 < l; ++i2)
1676                    {
1677                        Block block = Block.blocksList[this.getBlockId(k1, i2, l1)];
1678
1679                        if (block != null)
1680                        {
1681                            block.addCollisionBoxesToList(this, k1, i2, l1, par2AxisAlignedBB, this.collidingBoundingBoxes, par1Entity);
1682                        }
1683                    }
1684                }
1685            }
1686        }
1687
1688        double d0 = 0.25D;
1689        List list = this.getEntitiesWithinAABBExcludingEntity(par1Entity, par2AxisAlignedBB.expand(d0, d0, d0));
1690
1691        for (int j2 = 0; j2 < list.size(); ++j2)
1692        {
1693            AxisAlignedBB axisalignedbb1 = ((Entity)list.get(j2)).getBoundingBox();
1694
1695            if (axisalignedbb1 != null && axisalignedbb1.intersectsWith(par2AxisAlignedBB))
1696            {
1697                this.collidingBoundingBoxes.add(axisalignedbb1);
1698            }
1699
1700            axisalignedbb1 = par1Entity.getCollisionBox((Entity)list.get(j2));
1701
1702            if (axisalignedbb1 != null && axisalignedbb1.intersectsWith(par2AxisAlignedBB))
1703            {
1704                this.collidingBoundingBoxes.add(axisalignedbb1);
1705            }
1706        }
1707
1708        return this.collidingBoundingBoxes;
1709    }
1710
1711    /**
1712     * calculates and returns a list of colliding bounding boxes within a given AABB
1713     */
1714    public List getAllCollidingBoundingBoxes(AxisAlignedBB par1AxisAlignedBB)
1715    {
1716        this.collidingBoundingBoxes.clear();
1717        int i = MathHelper.floor_double(par1AxisAlignedBB.minX);
1718        int j = MathHelper.floor_double(par1AxisAlignedBB.maxX + 1.0D);
1719        int k = MathHelper.floor_double(par1AxisAlignedBB.minY);
1720        int l = MathHelper.floor_double(par1AxisAlignedBB.maxY + 1.0D);
1721        int i1 = MathHelper.floor_double(par1AxisAlignedBB.minZ);
1722        int j1 = MathHelper.floor_double(par1AxisAlignedBB.maxZ + 1.0D);
1723
1724        for (int k1 = i; k1 < j; ++k1)
1725        {
1726            for (int l1 = i1; l1 < j1; ++l1)
1727            {
1728                if (this.blockExists(k1, 64, l1))
1729                {
1730                    for (int i2 = k - 1; i2 < l; ++i2)
1731                    {
1732                        Block block = Block.blocksList[this.getBlockId(k1, i2, l1)];
1733
1734                        if (block != null)
1735                        {
1736                            block.addCollisionBoxesToList(this, k1, i2, l1, par1AxisAlignedBB, this.collidingBoundingBoxes, (Entity)null);
1737                        }
1738                    }
1739                }
1740            }
1741        }
1742
1743        return this.collidingBoundingBoxes;
1744    }
1745
1746    /**
1747     * Returns the amount of skylight subtracted for the current time
1748     */
1749    public int calculateSkylightSubtracted(float par1)
1750    {
1751        float f1 = this.getCelestialAngle(par1);
1752        float f2 = 1.0F - (MathHelper.cos(f1 * (float)Math.PI * 2.0F) * 2.0F + 0.5F);
1753
1754        if (f2 < 0.0F)
1755        {
1756            f2 = 0.0F;
1757        }
1758
1759        if (f2 > 1.0F)
1760        {
1761            f2 = 1.0F;
1762        }
1763
1764        f2 = 1.0F - f2;
1765        f2 = (float)((double)f2 * (1.0D - (double)(this.getRainStrength(par1) * 5.0F) / 16.0D));
1766        f2 = (float)((double)f2 * (1.0D - (double)(this.getWeightedThunderStrength(par1) * 5.0F) / 16.0D));
1767        f2 = 1.0F - f2;
1768        return (int)(f2 * 11.0F);
1769    }
1770
1771    @SideOnly(Side.CLIENT)
1772
1773    /**
1774     * Removes a worldAccess from the worldAccesses object
1775     */
1776    public void removeWorldAccess(IWorldAccess par1IWorldAccess)
1777    {
1778        this.worldAccesses.remove(par1IWorldAccess);
1779    }
1780
1781    @SideOnly(Side.CLIENT)
1782
1783    /**
1784     * Returns the sun brightness - checks time of day, rain and thunder
1785     */
1786    public float getSunBrightness(float par1)
1787    {
1788        float f1 = this.getCelestialAngle(par1);
1789        float f2 = 1.0F - (MathHelper.cos(f1 * (float)Math.PI * 2.0F) * 2.0F + 0.2F);
1790
1791        if (f2 < 0.0F)
1792        {
1793            f2 = 0.0F;
1794        }
1795
1796        if (f2 > 1.0F)
1797        {
1798            f2 = 1.0F;
1799        }
1800
1801        f2 = 1.0F - f2;
1802        f2 = (float)((double)f2 * (1.0D - (double)(this.getRainStrength(par1) * 5.0F) / 16.0D));
1803        f2 = (float)((double)f2 * (1.0D - (double)(this.getWeightedThunderStrength(par1) * 5.0F) / 16.0D));
1804        return f2 * 0.8F + 0.2F;
1805    }
1806
1807    @SideOnly(Side.CLIENT)
1808
1809    /**
1810     * Calculates the color for the skybox
1811     */
1812    public Vec3 getSkyColor(Entity par1Entity, float par2)
1813    {
1814        return provider.getSkyColor(par1Entity, par2);
1815    }
1816
1817    @SideOnly(Side.CLIENT)
1818    public Vec3 getSkyColorBody(Entity par1Entity, float par2)
1819    {
1820        float f1 = this.getCelestialAngle(par2);
1821        float f2 = MathHelper.cos(f1 * (float)Math.PI * 2.0F) * 2.0F + 0.5F;
1822
1823        if (f2 < 0.0F)
1824        {
1825            f2 = 0.0F;
1826        }
1827
1828        if (f2 > 1.0F)
1829        {
1830            f2 = 1.0F;
1831        }
1832
1833        int i = MathHelper.floor_double(par1Entity.posX);
1834        int j = MathHelper.floor_double(par1Entity.posZ);
1835        BiomeGenBase biomegenbase = this.getBiomeGenForCoords(i, j);
1836        float f3 = biomegenbase.getFloatTemperature();
1837        int k = biomegenbase.getSkyColorByTemp(f3);
1838        float f4 = (float)(k >> 16 & 255) / 255.0F;
1839        float f5 = (float)(k >> 8 & 255) / 255.0F;
1840        float f6 = (float)(k & 255) / 255.0F;
1841        f4 *= f2;
1842        f5 *= f2;
1843        f6 *= f2;
1844        float f7 = this.getRainStrength(par2);
1845        float f8;
1846        float f9;
1847
1848        if (f7 > 0.0F)
1849        {
1850            f8 = (f4 * 0.3F + f5 * 0.59F + f6 * 0.11F) * 0.6F;
1851            f9 = 1.0F - f7 * 0.75F;
1852            f4 = f4 * f9 + f8 * (1.0F - f9);
1853            f5 = f5 * f9 + f8 * (1.0F - f9);
1854            f6 = f6 * f9 + f8 * (1.0F - f9);
1855        }
1856
1857        f8 = this.getWeightedThunderStrength(par2);
1858
1859        if (f8 > 0.0F)
1860        {
1861            f9 = (f4 * 0.3F + f5 * 0.59F + f6 * 0.11F) * 0.2F;
1862            float f10 = 1.0F - f8 * 0.75F;
1863            f4 = f4 * f10 + f9 * (1.0F - f10);
1864            f5 = f5 * f10 + f9 * (1.0F - f10);
1865            f6 = f6 * f10 + f9 * (1.0F - f10);
1866        }
1867
1868        if (this.lastLightningBolt > 0)
1869        {
1870            f9 = (float)this.lastLightningBolt - par2;
1871
1872            if (f9 > 1.0F)
1873            {
1874                f9 = 1.0F;
1875            }
1876
1877            f9 *= 0.45F;
1878            f4 = f4 * (1.0F - f9) + 0.8F * f9;
1879            f5 = f5 * (1.0F - f9) + 0.8F * f9;
1880            f6 = f6 * (1.0F - f9) + 1.0F * f9;
1881        }
1882
1883        return this.getWorldVec3Pool().getVecFromPool((double)f4, (double)f5, (double)f6);
1884    }
1885
1886    /**
1887     * calls calculateCelestialAngle
1888     */
1889    public float getCelestialAngle(float par1)
1890    {
1891        return this.provider.calculateCelestialAngle(this.worldInfo.getWorldTime(), par1);
1892    }
1893
1894    public int getMoonPhase()
1895    {
1896        return this.provider.getMoonPhase(this.worldInfo.getWorldTime());
1897    }
1898
1899    /**
1900     * Return getCelestialAngle()*2*PI
1901     */
1902    public float getCelestialAngleRadians(float par1)
1903    {
1904        float f1 = this.getCelestialAngle(par1);
1905        return f1 * (float)Math.PI * 2.0F;
1906    }
1907
1908    @SideOnly(Side.CLIENT)
1909    public Vec3 getCloudColour(float par1)
1910    {
1911        return provider.drawClouds(par1);
1912    }
1913
1914    @SideOnly(Side.CLIENT)
1915    public Vec3 drawCloudsBody(float par1)
1916    {
1917        float f1 = this.getCelestialAngle(par1);
1918        float f2 = MathHelper.cos(f1 * (float)Math.PI * 2.0F) * 2.0F + 0.5F;
1919
1920        if (f2 < 0.0F)
1921        {
1922            f2 = 0.0F;
1923        }
1924
1925        if (f2 > 1.0F)
1926        {
1927            f2 = 1.0F;
1928        }
1929
1930        float f3 = (float)(this.cloudColour >> 16 & 255L) / 255.0F;
1931        float f4 = (float)(this.cloudColour >> 8 & 255L) / 255.0F;
1932        float f5 = (float)(this.cloudColour & 255L) / 255.0F;
1933        float f6 = this.getRainStrength(par1);
1934        float f7;
1935        float f8;
1936
1937        if (f6 > 0.0F)
1938        {
1939            f7 = (f3 * 0.3F + f4 * 0.59F + f5 * 0.11F) * 0.6F;
1940            f8 = 1.0F - f6 * 0.95F;
1941            f3 = f3 * f8 + f7 * (1.0F - f8);
1942            f4 = f4 * f8 + f7 * (1.0F - f8);
1943            f5 = f5 * f8 + f7 * (1.0F - f8);
1944        }
1945
1946        f3 *= f2 * 0.9F + 0.1F;
1947        f4 *= f2 * 0.9F + 0.1F;
1948        f5 *= f2 * 0.85F + 0.15F;
1949        f7 = this.getWeightedThunderStrength(par1);
1950
1951        if (f7 > 0.0F)
1952        {
1953            f8 = (f3 * 0.3F + f4 * 0.59F + f5 * 0.11F) * 0.2F;
1954            float f9 = 1.0F - f7 * 0.95F;
1955            f3 = f3 * f9 + f8 * (1.0F - f9);
1956            f4 = f4 * f9 + f8 * (1.0F - f9);
1957            f5 = f5 * f9 + f8 * (1.0F - f9);
1958        }
1959
1960        return this.getWorldVec3Pool().getVecFromPool((double)f3, (double)f4, (double)f5);
1961    }
1962
1963    @SideOnly(Side.CLIENT)
1964
1965    /**
1966     * Returns vector(ish) with R/G/B for fog
1967     */
1968    public Vec3 getFogColor(float par1)
1969    {
1970        float f1 = this.getCelestialAngle(par1);
1971        return this.provider.getFogColor(f1, par1);
1972    }
1973
1974    /**
1975     * Gets the height to which rain/snow will fall. Calculates it if not already stored.
1976     */
1977    public int getPrecipitationHeight(int par1, int par2)
1978    {
1979        return this.getChunkFromBlockCoords(par1, par2).getPrecipitationHeight(par1 & 15, par2 & 15);
1980    }
1981
1982    /**
1983     * Finds the highest block on the x, z coordinate that is solid and returns its y coord. Args x, z
1984     */
1985    public int getTopSolidOrLiquidBlock(int par1, int par2)
1986    {
1987        Chunk chunk = this.getChunkFromBlockCoords(par1, par2);
1988        int k = chunk.getTopFilledSegment() + 15;
1989        par1 &= 15;
1990
1991        for (par2 &= 15; k > 0; --k)
1992        {
1993            int l = chunk.getBlockID(par1, k, par2);
1994
1995            if (l != 0 && Block.blocksList[l].blockMaterial.blocksMovement() && Block.blocksList[l].blockMaterial != Material.leaves && !Block.blocksList[l].isBlockFoliage(this, par1, k, par2))
1996            {
1997                return k + 1;
1998            }
1999        }
2000
2001        return -1;
2002    }
2003
2004    @SideOnly(Side.CLIENT)
2005
2006    /**
2007     * How bright are stars in the sky
2008     */
2009    public float getStarBrightness(float par1)
2010    {
2011        return provider.getStarBrightness(par1);
2012    }
2013
2014    @SideOnly(Side.CLIENT)
2015    public float getStarBrightnessBody(float par1)
2016    {
2017        float f1 = this.getCelestialAngle(par1);
2018        float f2 = 1.0F - (MathHelper.cos(f1 * (float)Math.PI * 2.0F) * 2.0F + 0.25F);
2019
2020        if (f2 < 0.0F)
2021        {
2022            f2 = 0.0F;
2023        }
2024
2025        if (f2 > 1.0F)
2026        {
2027            f2 = 1.0F;
2028        }
2029
2030        return f2 * f2 * 0.5F;
2031    }
2032
2033    /**
2034     * Schedules a tick to a block with a delay (Most commonly the tick rate)
2035     */
2036    public void scheduleBlockUpdate(int par1, int par2, int par3, int par4, int par5) {}
2037
2038    public void func_82740_a(int par1, int par2, int par3, int par4, int par5, int par6) {}
2039
2040    /**
2041     * Schedules a block update from the saved information in a chunk. Called when the chunk is loaded.
2042     */
2043    public void scheduleBlockUpdateFromLoad(int par1, int par2, int par3, int par4, int par5, int par6) {}
2044
2045    /**
2046     * Updates (and cleans up) entities and tile entities
2047     */
2048    public void updateEntities()
2049    {
2050        this.theProfiler.startSection("entities");
2051        this.theProfiler.startSection("global");
2052        int i;
2053        Entity entity;
2054        CrashReport crashreport;
2055        CrashReportCategory crashreportcategory;
2056
2057        for (i = 0; i < this.weatherEffects.size(); ++i)
2058        {
2059            entity = (Entity)this.weatherEffects.get(i);
2060
2061            try
2062            {
2063                ++entity.ticksExisted;
2064                entity.onUpdate();
2065            }
2066            catch (Throwable throwable)
2067            {
2068                crashreport = CrashReport.makeCrashReport(throwable, "Ticking entity");
2069                crashreportcategory = crashreport.makeCategory("Entity being ticked");
2070
2071                if (entity == null)
2072                {
2073                    crashreportcategory.addCrashSection("Entity", "~~NULL~~");
2074                }
2075                else
2076                {
2077                    entity.func_85029_a(crashreportcategory);
2078                }
2079
2080                if (ForgeDummyContainer.removeErroringEntities)
2081                {
2082                    FMLLog.severe(crashreport.getCompleteReport());
2083                    removeEntity(entity);
2084                }
2085                else
2086                {
2087                    throw new ReportedException(crashreport);
2088                }
2089            }
2090
2091            if (entity.isDead)
2092            {
2093                this.weatherEffects.remove(i--);
2094            }
2095        }
2096
2097        this.theProfiler.endStartSection("remove");
2098        this.loadedEntityList.removeAll(this.unloadedEntityList);
2099        int j;
2100        int k;
2101
2102        for (i = 0; i < this.unloadedEntityList.size(); ++i)
2103        {
2104            entity = (Entity)this.unloadedEntityList.get(i);
2105            j = entity.chunkCoordX;
2106            k = entity.chunkCoordZ;
2107
2108            if (entity.addedToChunk && this.chunkExists(j, k))
2109            {
2110                this.getChunkFromChunkCoords(j, k).removeEntity(entity);
2111            }
2112        }
2113
2114        for (i = 0; i < this.unloadedEntityList.size(); ++i)
2115        {
2116            this.releaseEntitySkin((Entity)this.unloadedEntityList.get(i));
2117        }
2118
2119        this.unloadedEntityList.clear();
2120        this.theProfiler.endStartSection("regular");
2121
2122        for (i = 0; i < this.loadedEntityList.size(); ++i)
2123        {
2124            entity = (Entity)this.loadedEntityList.get(i);
2125
2126            if (entity.ridingEntity != null)
2127            {
2128                if (!entity.ridingEntity.isDead && entity.ridingEntity.riddenByEntity == entity)
2129                {
2130                    continue;
2131                }
2132
2133                entity.ridingEntity.riddenByEntity = null;
2134                entity.ridingEntity = null;
2135            }
2136
2137            this.theProfiler.startSection("tick");
2138
2139            if (!entity.isDead)
2140            {
2141                try
2142                {
2143                    this.updateEntity(entity);
2144                }
2145                catch (Throwable throwable1)
2146                {
2147                    crashreport = CrashReport.makeCrashReport(throwable1, "Ticking entity");
2148                    crashreportcategory = crashreport.makeCategory("Entity being ticked");
2149                    entity.func_85029_a(crashreportcategory);
2150
2151                    if (ForgeDummyContainer.removeErroringEntities)
2152                    {
2153                        FMLLog.severe(crashreport.getCompleteReport());
2154                        removeEntity(entity);
2155                    }
2156                    else
2157                    {
2158                        throw new ReportedException(crashreport);
2159                    }
2160                }
2161            }
2162
2163            this.theProfiler.endSection();
2164            this.theProfiler.startSection("remove");
2165
2166            if (entity.isDead)
2167            {
2168                // If it's dead, move it to the unloaded list for removal on the next tick
2169                unloadedEntityList.add(entity);
2170            }
2171
2172            this.theProfiler.endSection();
2173        }
2174
2175        this.theProfiler.endStartSection("tileEntities");
2176        this.scanningTileEntities = true;
2177        Iterator iterator = this.loadedTileEntityList.iterator();
2178
2179        while (iterator.hasNext())
2180        {
2181            TileEntity tileentity = (TileEntity)iterator.next();
2182
2183            if (!tileentity.isInvalid() && tileentity.func_70309_m() && this.blockExists(tileentity.xCoord, tileentity.yCoord, tileentity.zCoord))
2184            {
2185                try
2186                {
2187                    tileentity.updateEntity();
2188                }
2189                catch (Throwable throwable2)
2190                {
2191                    crashreport = CrashReport.makeCrashReport(throwable2, "Ticking tile entity");
2192                    crashreportcategory = crashreport.makeCategory("Tile entity being ticked");
2193                    tileentity.func_85027_a(crashreportcategory);
2194                    if (ForgeDummyContainer.removeErroringTileEntities)
2195                    {
2196                        FMLLog.severe(crashreport.getCompleteReport());
2197                        tileentity.invalidate();
2198                        setBlockToAir(tileentity.xCoord, tileentity.yCoord, tileentity.zCoord);
2199                    }
2200                    else
2201                    {
2202                        throw new ReportedException(crashreport);
2203                    }
2204                }
2205            }
2206
2207            if (tileentity.isInvalid())
2208            {
2209                iterator.remove();
2210
2211                if (this.chunkExists(tileentity.xCoord >> 4, tileentity.zCoord >> 4))
2212                {
2213                    Chunk chunk = this.getChunkFromChunkCoords(tileentity.xCoord >> 4, tileentity.zCoord >> 4);
2214
2215                    if (chunk != null)
2216                    {
2217                        chunk.cleanChunkBlockTileEntity(tileentity.xCoord & 15, tileentity.yCoord, tileentity.zCoord & 15);
2218                    }
2219                }
2220            }
2221        }
2222
2223
2224        if (!this.entityRemoval.isEmpty())
2225        {
2226            for (Object tile : entityRemoval)
2227            {
2228               ((TileEntity)tile).onChunkUnload();
2229            }
2230            this.loadedTileEntityList.removeAll(this.entityRemoval);
2231            this.entityRemoval.clear();
2232        }
2233
2234        this.scanningTileEntities = false;
2235
2236        this.theProfiler.endStartSection("pendingTileEntities");
2237
2238        if (!this.addedTileEntityList.isEmpty())
2239        {
2240            for (int l = 0; l < this.addedTileEntityList.size(); ++l)
2241            {
2242                TileEntity tileentity1 = (TileEntity)this.addedTileEntityList.get(l);
2243
2244                if (!tileentity1.isInvalid())
2245                {
2246                    if (!this.loadedTileEntityList.contains(tileentity1))
2247                    {
2248                        this.loadedTileEntityList.add(tileentity1);
2249                    }
2250                }
2251                else
2252                {
2253                    if (this.chunkExists(tileentity1.xCoord >> 4, tileentity1.zCoord >> 4))
2254                    {
2255                        Chunk chunk1 = this.getChunkFromChunkCoords(tileentity1.xCoord >> 4, tileentity1.zCoord >> 4);
2256
2257                        if (chunk1 != null)
2258                        {
2259                            chunk1.cleanChunkBlockTileEntity(tileentity1.xCoord & 15, tileentity1.yCoord, tileentity1.zCoord & 15);
2260                        }
2261                    }
2262                }
2263            }
2264
2265            this.addedTileEntityList.clear();
2266        }
2267
2268        this.theProfiler.endSection();
2269        this.theProfiler.endSection();
2270    }
2271
2272    public void addTileEntity(Collection par1Collection)
2273    {
2274        List dest = scanningTileEntities ? addedTileEntityList : loadedTileEntityList;
2275        for(Object entity : par1Collection)
2276        {
2277            if(((TileEntity)entity).canUpdate())
2278            {
2279                dest.add(entity);
2280            }
2281        }
2282    }
2283
2284    /**
2285     * Will update the entity in the world if the chunk the entity is in is currently loaded. Args: entity
2286     */
2287    public void updateEntity(Entity par1Entity)
2288    {
2289        this.updateEntityWithOptionalForce(par1Entity, true);
2290    }
2291
2292    /**
2293     * Will update the entity in the world if the chunk the entity is in is currently loaded or its forced to update.
2294     * Args: entity, forceUpdate
2295     */
2296    public void updateEntityWithOptionalForce(Entity par1Entity, boolean par2)
2297    {
2298        int i = MathHelper.floor_double(par1Entity.posX);
2299        int j = MathHelper.floor_double(par1Entity.posZ);
2300
2301        boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(i >> 4, j >> 4));
2302        byte b0 = isForced ? (byte)0 : 32;
2303        boolean canUpdate = !par2 || this.checkChunksExist(i - b0, 0, j - b0, i + b0, 0, j + b0);
2304        if (!canUpdate)
2305        {
2306            EntityEvent.CanUpdate event = new EntityEvent.CanUpdate(par1Entity);
2307            MinecraftForge.EVENT_BUS.post(event);
2308            canUpdate = event.canUpdate;
2309        }
2310        if (canUpdate)
2311        {
2312            par1Entity.lastTickPosX = par1Entity.posX;
2313            par1Entity.lastTickPosY = par1Entity.posY;
2314            par1Entity.lastTickPosZ = par1Entity.posZ;
2315            par1Entity.prevRotationYaw = par1Entity.rotationYaw;
2316            par1Entity.prevRotationPitch = par1Entity.rotationPitch;
2317
2318            if (par2 && par1Entity.addedToChunk)
2319            {
2320                if (par1Entity.ridingEntity != null)
2321                {
2322                    par1Entity.updateRidden();
2323                }
2324                else
2325                {
2326                    ++par1Entity.ticksExisted;
2327                    par1Entity.onUpdate();
2328                }
2329            }
2330
2331            this.theProfiler.startSection("chunkCheck");
2332
2333            if (Double.isNaN(par1Entity.posX) || Double.isInfinite(par1Entity.posX))
2334            {
2335                par1Entity.posX = par1Entity.lastTickPosX;
2336            }
2337
2338            if (Double.isNaN(par1Entity.posY) || Double.isInfinite(par1Entity.posY))
2339            {
2340                par1Entity.posY = par1Entity.lastTickPosY;
2341            }
2342
2343            if (Double.isNaN(par1Entity.posZ) || Double.isInfinite(par1Entity.posZ))
2344            {
2345                par1Entity.posZ = par1Entity.lastTickPosZ;
2346            }
2347
2348            if (Double.isNaN((double)par1Entity.rotationPitch) || Double.isInfinite((double)par1Entity.rotationPitch))
2349            {
2350                par1Entity.rotationPitch = par1Entity.prevRotationPitch;
2351            }
2352
2353            if (Double.isNaN((double)par1Entity.rotationYaw) || Double.isInfinite((double)par1Entity.rotationYaw))
2354            {
2355                par1Entity.rotationYaw = par1Entity.prevRotationYaw;
2356            }
2357
2358            int k = MathHelper.floor_double(par1Entity.posX / 16.0D);
2359            int l = MathHelper.floor_double(par1Entity.posY / 16.0D);
2360            int i1 = MathHelper.floor_double(par1Entity.posZ / 16.0D);
2361
2362            if (!par1Entity.addedToChunk || par1Entity.chunkCoordX != k || par1Entity.chunkCoordY != l || par1Entity.chunkCoordZ != i1)
2363            {
2364                if (par1Entity.addedToChunk && this.chunkExists(par1Entity.chunkCoordX, par1Entity.chunkCoordZ))
2365                {
2366                    this.getChunkFromChunkCoords(par1Entity.chunkCoordX, par1Entity.chunkCoordZ).removeEntityAtIndex(par1Entity, par1Entity.chunkCoordY);
2367                }
2368
2369                if (this.chunkExists(k, i1))
2370                {
2371                    par1Entity.addedToChunk = true;
2372                    this.getChunkFromChunkCoords(k, i1).addEntity(par1Entity);
2373                }
2374                else
2375                {
2376                    par1Entity.addedToChunk = false;
2377                }
2378            }
2379
2380            this.theProfiler.endSection();
2381
2382            if (par2 && par1Entity.addedToChunk && par1Entity.riddenByEntity != null)
2383            {
2384                if (!par1Entity.riddenByEntity.isDead && par1Entity.riddenByEntity.ridingEntity == par1Entity)
2385                {
2386                    this.updateEntity(par1Entity.riddenByEntity);
2387                }
2388                else
2389                {
2390                    par1Entity.riddenByEntity.ridingEntity = null;
2391                    par1Entity.riddenByEntity = null;
2392                }
2393            }
2394        }
2395    }
2396
2397    /**
2398     * Returns true if there are no solid, live entities in the specified AxisAlignedBB
2399     */
2400    public boolean checkIfAABBIsClear(AxisAlignedBB par1AxisAlignedBB)
2401    {
2402        return this.checkIfAABBIsClearExcludingEntity(par1AxisAlignedBB, (Entity)null);
2403    }
2404
2405    /**
2406     * Returns true if there are no solid, live entities in the specified AxisAlignedBB, excluding the given entity
2407     */
2408    public boolean checkIfAABBIsClearExcludingEntity(AxisAlignedBB par1AxisAlignedBB, Entity par2Entity)
2409    {
2410        List list = this.getEntitiesWithinAABBExcludingEntity((Entity)null, par1AxisAlignedBB);
2411
2412        for (int i = 0; i < list.size(); ++i)
2413        {
2414            Entity entity1 = (Entity)list.get(i);
2415
2416            if (!entity1.isDead && entity1.preventEntitySpawning && entity1 != par2Entity)
2417            {
2418                return false;
2419            }
2420        }
2421
2422        return true;
2423    }
2424
2425    /**
2426     * Returns true if there are any blocks in the region constrained by an AxisAlignedBB
2427     */
2428    public boolean isAABBNonEmpty(AxisAlignedBB par1AxisAlignedBB)
2429    {
2430        int i = MathHelper.floor_double(par1AxisAlignedBB.minX);
2431        int j = MathHelper.floor_double(par1AxisAlignedBB.maxX + 1.0D);
2432        int k = MathHelper.floor_double(par1AxisAlignedBB.minY);
2433        int l = MathHelper.floor_double(par1AxisAlignedBB.maxY + 1.0D);
2434        int i1 = MathHelper.floor_double(par1AxisAlignedBB.minZ);
2435        int j1 = MathHelper.floor_double(par1AxisAlignedBB.maxZ + 1.0D);
2436
2437        if (par1AxisAlignedBB.minX < 0.0D)
2438        {
2439            --i;
2440        }
2441
2442        if (par1AxisAlignedBB.minY < 0.0D)
2443        {
2444            --k;
2445        }
2446
2447        if (par1AxisAlignedBB.minZ < 0.0D)
2448        {
2449            --i1;
2450        }
2451
2452        for (int k1 = i; k1 < j; ++k1)
2453        {
2454            for (int l1 = k; l1 < l; ++l1)
2455            {
2456                for (int i2 = i1; i2 < j1; ++i2)
2457                {
2458                    Block block = Block.blocksList[this.getBlockId(k1, l1, i2)];
2459
2460                    if (block != null)
2461                    {
2462                        return true;
2463                    }
2464                }
2465            }
2466        }
2467
2468        return false;
2469    }
2470
2471    /**
2472     * Returns if any of the blocks within the aabb are liquids. Args: aabb
2473     */
2474    public boolean isAnyLiquid(AxisAlignedBB par1AxisAlignedBB)
2475    {
2476        int i = MathHelper.floor_double(par1AxisAlignedBB.minX);
2477        int j = MathHelper.floor_double(par1AxisAlignedBB.maxX + 1.0D);
2478        int k = MathHelper.floor_double(par1AxisAlignedBB.minY);
2479        int l = MathHelper.floor_double(par1AxisAlignedBB.maxY + 1.0D);
2480        int i1 = MathHelper.floor_double(par1AxisAlignedBB.minZ);
2481        int j1 = MathHelper.floor_double(par1AxisAlignedBB.maxZ + 1.0D);
2482
2483        if (par1AxisAlignedBB.minX < 0.0D)
2484        {
2485            --i;
2486        }
2487
2488        if (par1AxisAlignedBB.minY < 0.0D)
2489        {
2490            --k;
2491        }
2492
2493        if (par1AxisAlignedBB.minZ < 0.0D)
2494        {
2495            --i1;
2496        }
2497
2498        for (int k1 = i; k1 < j; ++k1)
2499        {
2500            for (int l1 = k; l1 < l; ++l1)
2501            {
2502                for (int i2 = i1; i2 < j1; ++i2)
2503                {
2504                    Block block = Block.blocksList[this.getBlockId(k1, l1, i2)];
2505
2506                    if (block != null && block.blockMaterial.isLiquid())
2507                    {
2508                        return true;
2509                    }
2510                }
2511            }
2512        }
2513
2514        return false;
2515    }
2516
2517    /**
2518     * Returns whether or not the given bounding box is on fire or not
2519     */
2520    public boolean isBoundingBoxBurning(AxisAlignedBB par1AxisAlignedBB)
2521    {
2522        int i = MathHelper.floor_double(par1AxisAlignedBB.minX);
2523        int j = MathHelper.floor_double(par1AxisAlignedBB.maxX + 1.0D);
2524        int k = MathHelper.floor_double(par1AxisAlignedBB.minY);
2525        int l = MathHelper.floor_double(par1AxisAlignedBB.maxY + 1.0D);
2526        int i1 = MathHelper.floor_double(par1AxisAlignedBB.minZ);
2527        int j1 = MathHelper.floor_double(par1AxisAlignedBB.maxZ + 1.0D);
2528
2529        if (this.checkChunksExist(i, k, i1, j, l, j1))
2530        {
2531            for (int k1 = i; k1 < j; ++k1)
2532            {
2533                for (int l1 = k; l1 < l; ++l1)
2534                {
2535                    for (int i2 = i1; i2 < j1; ++i2)
2536                    {
2537                        int j2 = this.getBlockId(k1, l1, i2);
2538
2539                        if (j2 == Block.fire.blockID || j2 == Block.lavaMoving.blockID || j2 == Block.lavaStill.blockID)
2540                        {
2541                            return true;
2542                        }
2543                        else
2544                        {
2545                            Block block = Block.blocksList[j2];
2546                            if (block != null && block.isBlockBurning(this, k1, l1, i2))
2547                            {
2548                                return true;
2549                            }
2550                        }
2551                    }
2552                }
2553            }
2554        }
2555
2556        return false;
2557    }
2558
2559    /**
2560     * handles the acceleration of an object whilst in water. Not sure if it is used elsewhere.
2561     */
2562    public boolean handleMaterialAcceleration(AxisAlignedBB par1AxisAlignedBB, Material par2Material, Entity par3Entity)
2563    {
2564        int i = MathHelper.floor_double(par1AxisAlignedBB.minX);
2565        int j = MathHelper.floor_double(par1AxisAlignedBB.maxX + 1.0D);
2566        int k = MathHelper.floor_double(par1AxisAlignedBB.minY);
2567        int l = MathHelper.floor_double(par1AxisAlignedBB.maxY + 1.0D);
2568        int i1 = MathHelper.floor_double(par1AxisAlignedBB.minZ);
2569        int j1 = MathHelper.floor_double(par1AxisAlignedBB.maxZ + 1.0D);
2570
2571        if (!this.checkChunksExist(i, k, i1, j, l, j1))
2572        {
2573            return false;
2574        }
2575        else
2576        {
2577            boolean flag = false;
2578            Vec3 vec3 = this.getWorldVec3Pool().getVecFromPool(0.0D, 0.0D, 0.0D);
2579
2580            for (int k1 = i; k1 < j; ++k1)
2581            {
2582                for (int l1 = k; l1 < l; ++l1)
2583                {
2584                    for (int i2 = i1; i2 < j1; ++i2)
2585                    {
2586                        Block block = Block.blocksList[this.getBlockId(k1, l1, i2)];
2587
2588                        if (block != null && block.blockMaterial == par2Material)
2589                        {
2590                            double d0 = (double)((float)(l1 + 1) - BlockFluid.getFluidHeightPercent(this.getBlockMetadata(k1, l1, i2)));
2591
2592                            if ((double)l >= d0)
2593                            {
2594                                flag = true;
2595                                block.velocityToAddToEntity(this, k1, l1, i2, par3Entity, vec3);
2596                            }
2597                        }
2598                    }
2599                }
2600            }
2601
2602            if (vec3.lengthVector() > 0.0D && par3Entity.func_96092_aw())
2603            {
2604                vec3 = vec3.normalize();
2605                double d1 = 0.014D;
2606                par3Entity.motionX += vec3.xCoord * d1;
2607                par3Entity.motionY += vec3.yCoord * d1;
2608                par3Entity.motionZ += vec3.zCoord * d1;
2609            }
2610
2611            return flag;
2612        }
2613    }
2614
2615    /**
2616     * Returns true if the given bounding box contains the given material
2617     */
2618    public boolean isMaterialInBB(AxisAlignedBB par1AxisAlignedBB, Material par2Material)
2619    {
2620        int i = MathHelper.floor_double(par1AxisAlignedBB.minX);
2621        int j = MathHelper.floor_double(par1AxisAlignedBB.maxX + 1.0D);
2622        int k = MathHelper.floor_double(par1AxisAlignedBB.minY);
2623        int l = MathHelper.floor_double(par1AxisAlignedBB.maxY + 1.0D);
2624        int i1 = MathHelper.floor_double(par1AxisAlignedBB.minZ);
2625        int j1 = MathHelper.floor_double(par1AxisAlignedBB.maxZ + 1.0D);
2626
2627        for (int k1 = i; k1 < j; ++k1)
2628        {
2629            for (int l1 = k; l1 < l; ++l1)
2630            {
2631                for (int i2 = i1; i2 < j1; ++i2)
2632                {
2633                    Block block = Block.blocksList[this.getBlockId(k1, l1, i2)];
2634
2635                    if (block != null && block.blockMaterial == par2Material)
2636                    {
2637                        return true;
2638                    }
2639                }
2640            }
2641        }
2642
2643        return false;
2644    }
2645
2646    /**
2647     * checks if the given AABB is in the material given. Used while swimming.
2648     */
2649    public boolean isAABBInMaterial(AxisAlignedBB par1AxisAlignedBB, Material par2Material)
2650    {
2651        int i = MathHelper.floor_double(par1AxisAlignedBB.minX);
2652        int j = MathHelper.floor_double(par1AxisAlignedBB.maxX + 1.0D);
2653        int k = MathHelper.floor_double(par1AxisAlignedBB.minY);
2654        int l = MathHelper.floor_double(par1AxisAlignedBB.maxY + 1.0D);
2655        int i1 = MathHelper.floor_double(par1AxisAlignedBB.minZ);
2656        int j1 = MathHelper.floor_double(par1AxisAlignedBB.maxZ + 1.0D);
2657
2658        for (int k1 = i; k1 < j; ++k1)
2659        {
2660            for (int l1 = k; l1 < l; ++l1)
2661            {
2662                for (int i2 = i1; i2 < j1; ++i2)
2663                {
2664                    Block block = Block.blocksList[this.getBlockId(k1, l1, i2)];
2665
2666                    if (block != null && block.blockMaterial == par2Material)
2667                    {
2668                        int j2 = this.getBlockMetadata(k1, l1, i2);
2669                        double d0 = (double)(l1 + 1);
2670
2671                        if (j2 < 8)
2672                        {
2673                            d0 = (double)(l1 + 1) - (double)j2 / 8.0D;
2674                        }
2675
2676                        if (d0 >= par1AxisAlignedBB.minY)
2677                        {
2678                            return true;
2679                        }
2680                    }
2681                }
2682            }
2683        }
2684
2685        return false;
2686    }
2687
2688    /**
2689     * Creates an explosion. Args: entity, x, y, z, strength
2690     */
2691    public Explosion createExplosion(Entity par1Entity, double par2, double par4, double par6, float par8, boolean par9)
2692    {
2693        return this.newExplosion(par1Entity, par2, par4, par6, par8, false, par9);
2694    }
2695
2696    /**
2697     * returns a new explosion. Does initiation (at time of writing Explosion is not finished)
2698     */
2699    public Explosion newExplosion(Entity par1Entity, double par2, double par4, double par6, float par8, boolean par9, boolean par10)
2700    {
2701        Explosion explosion = new Explosion(this, par1Entity, par2, par4, par6, par8);
2702        explosion.isFlaming = par9;
2703        explosion.isSmoking = par10;
2704        explosion.doExplosionA();
2705        explosion.doExplosionB(true);
2706        return explosion;
2707    }
2708
2709    /**
2710     * Gets the percentage of real blocks within within a bounding box, along a specified vector.
2711     */
2712    public float getBlockDensity(Vec3 par1Vec3, AxisAlignedBB par2AxisAlignedBB)
2713    {
2714        double d0 = 1.0D / ((par2AxisAlignedBB.maxX - par2AxisAlignedBB.minX) * 2.0D + 1.0D);
2715        double d1 = 1.0D / ((par2AxisAlignedBB.maxY - par2AxisAlignedBB.minY) * 2.0D + 1.0D);
2716        double d2 = 1.0D / ((par2AxisAlignedBB.maxZ - par2AxisAlignedBB.minZ) * 2.0D + 1.0D);
2717        int i = 0;
2718        int j = 0;
2719
2720        for (float f = 0.0F; f <= 1.0F; f = (float)((double)f + d0))
2721        {
2722            for (float f1 = 0.0F; f1 <= 1.0F; f1 = (float)((double)f1 + d1))
2723            {
2724                for (float f2 = 0.0F; f2 <= 1.0F; f2 = (float)((double)f2 + d2))
2725                {
2726                    double d3 = par2AxisAlignedBB.minX + (par2AxisAlignedBB.maxX - par2AxisAlignedBB.minX) * (double)f;
2727                    double d4 = par2AxisAlignedBB.minY + (par2AxisAlignedBB.maxY - par2AxisAlignedBB.minY) * (double)f1;
2728                    double d5 = par2AxisAlignedBB.minZ + (par2AxisAlignedBB.maxZ - par2AxisAlignedBB.minZ) * (double)f2;
2729
2730                    if (this.rayTraceBlocks(this.getWorldVec3Pool().getVecFromPool(d3, d4, d5), par1Vec3) == null)
2731                    {
2732                        ++i;
2733                    }
2734
2735                    ++j;
2736                }
2737            }
2738        }
2739
2740        return (float)i / (float)j;
2741    }
2742
2743    /**
2744     * If the block in the given direction of the given coordinate is fire, extinguish it. Args: Player, X,Y,Z,
2745     * blockDirection
2746     */
2747    public boolean extinguishFire(EntityPlayer par1EntityPlayer, int par2, int par3, int par4, int par5)
2748    {
2749        if (par5 == 0)
2750        {
2751            --par3;
2752        }
2753
2754        if (par5 == 1)
2755        {
2756            ++par3;
2757        }
2758
2759        if (par5 == 2)
2760        {
2761            --par4;
2762        }
2763
2764        if (par5 == 3)
2765        {
2766            ++par4;
2767        }
2768
2769        if (par5 == 4)
2770        {
2771            --par2;
2772        }
2773
2774        if (par5 == 5)
2775        {
2776            ++par2;
2777        }
2778
2779        if (this.getBlockId(par2, par3, par4) == Block.fire.blockID)
2780        {
2781            this.playAuxSFXAtEntity(par1EntityPlayer, 1004, par2, par3, par4, 0);
2782            this.setBlockToAir(par2, par3, par4);
2783            return true;
2784        }
2785        else
2786        {
2787            return false;
2788        }
2789    }
2790
2791    @SideOnly(Side.CLIENT)
2792
2793    /**
2794     * This string is 'All: (number of loaded entities)' Viewable by press ing F3
2795     */
2796    public String getDebugLoadedEntities()
2797    {
2798        return "All: " + this.loadedEntityList.size();
2799    }
2800
2801    @SideOnly(Side.CLIENT)
2802
2803    /**
2804     * Returns the name of the current chunk provider, by calling chunkprovider.makeString()
2805     */
2806    public String getProviderName()
2807    {
2808        return this.chunkProvider.makeString();
2809    }
2810
2811    /**
2812     * Returns the TileEntity associated with a given block in X,Y,Z coordinates, or null if no TileEntity exists
2813     */
2814    public TileEntity getBlockTileEntity(int par1, int par2, int par3)
2815    {
2816        if (par2 >= 0 && par2 < 256)
2817        {
2818            TileEntity tileentity = null;
2819            int l;
2820            TileEntity tileentity1;
2821
2822            if (this.scanningTileEntities)
2823            {
2824                for (l = 0; l < this.addedTileEntityList.size(); ++l)
2825                {
2826                    tileentity1 = (TileEntity)this.addedTileEntityList.get(l);
2827
2828                    if (!tileentity1.isInvalid() && tileentity1.xCoord == par1 && tileentity1.yCoord == par2 && tileentity1.zCoord == par3)
2829                    {
2830                        tileentity = tileentity1;
2831                        break;
2832                    }
2833                }
2834            }
2835
2836            if (tileentity == null)
2837            {
2838                Chunk chunk = this.getChunkFromChunkCoords(par1 >> 4, par3 >> 4);
2839
2840                if (chunk != null)
2841                {
2842                    tileentity = chunk.getChunkBlockTileEntity(par1 & 15, par2, par3 & 15);
2843                }
2844            }
2845
2846            if (tileentity == null)
2847            {
2848                for (l = 0; l < this.addedTileEntityList.size(); ++l)
2849                {
2850                    tileentity1 = (TileEntity)this.addedTileEntityList.get(l);
2851
2852                    if (!tileentity1.isInvalid() && tileentity1.xCoord == par1 && tileentity1.yCoord == par2 && tileentity1.zCoord == par3)
2853                    {
2854                        tileentity = tileentity1;
2855                        break;
2856                    }
2857                }
2858            }
2859
2860            return tileentity;
2861        }
2862        else
2863        {
2864            return null;
2865        }
2866    }
2867
2868    /**
2869     * Sets the TileEntity for a given block in X, Y, Z coordinates
2870     */
2871    public void setBlockTileEntity(int par1, int par2, int par3, TileEntity par4TileEntity)
2872    {
2873        if (par4TileEntity == null || par4TileEntity.isInvalid())
2874        {
2875            return;
2876        }
2877
2878        if (par4TileEntity.canUpdate())
2879        {
2880            if (scanningTileEntities)
2881            {
2882                Iterator iterator = addedTileEntityList.iterator();
2883                while (iterator.hasNext())
2884                {
2885                    TileEntity tileentity1 = (TileEntity)iterator.next();
2886    
2887                    if (tileentity1.xCoord == par1 && tileentity1.yCoord == par2 && tileentity1.zCoord == par3)
2888                    {
2889                        tileentity1.invalidate();
2890                        iterator.remove();
2891                    }
2892                }
2893                addedTileEntityList.add(par4TileEntity);
2894            }
2895            else
2896            {
2897                loadedTileEntityList.add(par4TileEntity);
2898            }
2899        }
2900
2901        Chunk chunk = this.getChunkFromChunkCoords(par1 >> 4, par3 >> 4);
2902        if (chunk != null)
2903        {
2904            chunk.setChunkBlockTileEntity(par1 & 15, par2, par3 & 15, par4TileEntity);
2905        }
2906    }
2907
2908    /**
2909     * Removes the TileEntity for a given block in X,Y,Z coordinates
2910     */
2911    public void removeBlockTileEntity(int par1, int par2, int par3)
2912    {
2913        Chunk chunk = getChunkFromChunkCoords(par1 >> 4, par3 >> 4);
2914        if (chunk != null)
2915        {
2916            chunk.removeChunkBlockTileEntity(par1 & 15, par2, par3 & 15);
2917        }
2918    }
2919
2920    /**
2921     * adds tile entity to despawn list (renamed from markEntityForDespawn)
2922     */
2923    public void markTileEntityForDespawn(TileEntity par1TileEntity)
2924    {
2925        this.entityRemoval.add(par1TileEntity);
2926    }
2927
2928    /**
2929     * Returns true if the block at the specified coordinates is an opaque cube. Args: x, y, z
2930     */
2931    public boolean isBlockOpaqueCube(int par1, int par2, int par3)
2932    {
2933        Block block = Block.blocksList[this.getBlockId(par1, par2, par3)];
2934        return block == null ? false : block.isOpaqueCube();
2935    }
2936
2937    /**
2938     * Indicate if a material is a normal solid opaque cube.
2939     */
2940    public boolean isBlockNormalCube(int par1, int par2, int par3)
2941    {
2942        Block block = Block.blocksList[getBlockId(par1, par2, par3)];
2943        return block != null && block.isBlockNormalCube(this, par1, par2, par3);
2944    }
2945
2946    public boolean func_85174_u(int par1, int par2, int par3)
2947    {
2948        int l = this.getBlockId(par1, par2, par3);
2949
2950        if (l != 0 && Block.blocksList[l] != null)
2951        {
2952            AxisAlignedBB axisalignedbb = Block.blocksList[l].getCollisionBoundingBoxFromPool(this, par1, par2, par3);
2953            return axisalignedbb != null && axisalignedbb.getAverageEdgeLength() >= 1.0D;
2954        }
2955        else
2956        {
2957            return false;
2958        }
2959    }
2960
2961    /**
2962     * Returns true if the block at the given coordinate has a solid (buildable) top surface.
2963     */
2964    public boolean doesBlockHaveSolidTopSurface(int par1, int par2, int par3)
2965    {
2966        return isBlockSolidOnSide(par1, par2, par3, ForgeDirection.UP);
2967    }
2968
2969    @Deprecated //DO NOT USE THIS!!! USE doesBlockHaveSolidTopSurface
2970    public boolean func_102026_a(Block par1Block, int par2)
2971    {
2972        // -.-  Mojang PLEASE make this location sensitive, you have no reason not to.
2973        return par1Block == null ? false : (par1Block.blockMaterial.isOpaque() && par1Block.renderAsNormalBlock() ? true : (par1Block instanceof BlockStairs ? (par2 & 4) == 4 : (par1Block instanceof BlockHalfSlab ? (par2 & 8) == 8 : (par1Block instanceof BlockHopper ? true : (par1Block instanceof BlockSnow ? (par2 & 7) == 7 : false)))));
2974    }
2975
2976    /**
2977     * Checks if the block is a solid, normal cube. If the chunk does not exist, or is not loaded, it returns the
2978     * boolean parameter.
2979     */
2980    public boolean isBlockNormalCubeDefault(int par1, int par2, int par3, boolean par4)
2981    {
2982        if (par1 >= -30000000 && par3 >= -30000000 && par1 < 30000000 && par3 < 30000000)
2983        {
2984            Chunk chunk = this.chunkProvider.provideChunk(par1 >> 4, par3 >> 4);
2985
2986            if (chunk != null && !chunk.isEmpty())
2987            {
2988                Block block = Block.blocksList[this.getBlockId(par1, par2, par3)];
2989                return block == null ? false : isBlockNormalCube(par1, par2, par3);
2990            }
2991            else
2992            {
2993                return par4;
2994            }
2995        }
2996        else
2997        {
2998            return par4;
2999        }
3000    }
3001
3002    /**
3003     * Called on construction of the World class to setup the initial skylight values
3004     */
3005    public void calculateInitialSkylight()
3006    {
3007        int i = this.calculateSkylightSubtracted(1.0F);
3008
3009        if (i != this.skylightSubtracted)
3010        {
3011            this.skylightSubtracted = i;
3012        }
3013    }
3014
3015    /**
3016     * Set which types of mobs are allowed to spawn (peaceful vs hostile).
3017     */
3018    public void setAllowedSpawnTypes(boolean par1, boolean par2)
3019    {
3020        provider.setAllowedSpawnTypes(par1, par2);
3021    }
3022
3023    /**
3024     * Runs a single tick for the world
3025     */
3026    public void tick()
3027    {
3028        this.updateWeather();
3029    }
3030
3031    /**
3032     * Called from World constructor to set rainingStrength and thunderingStrength
3033     */
3034    private void calculateInitialWeather()
3035    {
3036        provider.calculateInitialWeather();
3037    }
3038
3039    public void calculateInitialWeatherBody()
3040    {
3041        if (this.worldInfo.isRaining())
3042        {
3043            this.rainingStrength = 1.0F;
3044
3045            if (this.worldInfo.isThundering())
3046            {
3047                this.thunderingStrength = 1.0F;
3048            }
3049        }
3050    }
3051
3052    /**
3053     * Updates all weather states.
3054     */
3055    protected void updateWeather()
3056    {
3057        provider.updateWeather();
3058    }
3059
3060    public void updateWeatherBody()
3061    {
3062        if (!this.provider.hasNoSky)
3063        {
3064            int i = this.worldInfo.getThunderTime();
3065
3066            if (i <= 0)
3067            {
3068                if (this.worldInfo.isThundering())
3069                {
3070                    this.worldInfo.setThunderTime(this.rand.nextInt(12000) + 3600);
3071                }
3072                else
3073                {
3074                    this.worldInfo.setThunderTime(this.rand.nextInt(168000) + 12000);
3075                }
3076            }
3077            else
3078            {
3079                --i;
3080                this.worldInfo.setThunderTime(i);
3081
3082                if (i <= 0)
3083                {
3084                    this.worldInfo.setThundering(!this.worldInfo.isThundering());
3085                }
3086            }
3087
3088            int j = this.worldInfo.getRainTime();
3089
3090            if (j <= 0)
3091            {
3092                if (this.worldInfo.isRaining())
3093                {
3094                    this.worldInfo.setRainTime(this.rand.nextInt(12000) + 12000);
3095                }
3096                else
3097                {
3098                    this.worldInfo.setRainTime(this.rand.nextInt(168000) + 12000);
3099                }
3100            }
3101            else
3102            {
3103                --j;
3104                this.worldInfo.setRainTime(j);
3105
3106                if (j <= 0)
3107                {
3108                    this.worldInfo.setRaining(!this.worldInfo.isRaining());
3109                }
3110            }
3111
3112            this.prevRainingStrength = this.rainingStrength;
3113
3114            if (this.worldInfo.isRaining())
3115            {
3116                this.rainingStrength = (float)((double)this.rainingStrength + 0.01D);
3117            }
3118            else
3119            {
3120                this.rainingStrength = (float)((double)this.rainingStrength - 0.01D);
3121            }
3122
3123            if (this.rainingStrength < 0.0F)
3124            {
3125                this.rainingStrength = 0.0F;
3126            }
3127
3128            if (this.rainingStrength > 1.0F)
3129            {
3130                this.rainingStrength = 1.0F;
3131            }
3132
3133            this.prevThunderingStrength = this.thunderingStrength;
3134
3135            if (this.worldInfo.isThundering())
3136            {
3137                this.thunderingStrength = (float)((double)this.thunderingStrength + 0.01D);
3138            }
3139            else
3140            {
3141                this.thunderingStrength = (float)((double)this.thunderingStrength - 0.01D);
3142            }
3143
3144            if (this.thunderingStrength < 0.0F)
3145            {
3146                this.thunderingStrength = 0.0F;
3147            }
3148
3149            if (this.thunderingStrength > 1.0F)
3150            {
3151                this.thunderingStrength = 1.0F;
3152            }
3153        }
3154    }
3155
3156    public void toggleRain()
3157    {
3158        provider.toggleRain();
3159    }
3160
3161    protected void setActivePlayerChunksAndCheckLight()
3162    {
3163        this.activeChunkSet.clear();
3164        this.activeChunkSet.addAll(getPersistentChunks().keySet());
3165
3166        this.theProfiler.startSection("buildList");
3167        int i;
3168        EntityPlayer entityplayer;
3169        int j;
3170        int k;
3171
3172        for (i = 0; i < this.playerEntities.size(); ++i)
3173        {
3174            entityplayer = (EntityPlayer)this.playerEntities.get(i);
3175            j = MathHelper.floor_double(entityplayer.posX / 16.0D);
3176            k = MathHelper.floor_double(entityplayer.posZ / 16.0D);
3177            byte b0 = 7;
3178
3179            for (int l = -b0; l <= b0; ++l)
3180            {
3181                for (int i1 = -b0; i1 <= b0; ++i1)
3182                {
3183                    this.activeChunkSet.add(new ChunkCoordIntPair(l + j, i1 + k));
3184                }
3185            }
3186        }
3187
3188        this.theProfiler.endSection();
3189
3190        if (this.ambientTickCountdown > 0)
3191        {
3192            --this.ambientTickCountdown;
3193        }
3194
3195        this.theProfiler.startSection("playerCheckLight");
3196
3197        if (!this.playerEntities.isEmpty())
3198        {
3199            i = this.rand.nextInt(this.playerEntities.size());
3200            entityplayer = (EntityPlayer)this.playerEntities.get(i);
3201            j = MathHelper.floor_double(entityplayer.posX) + this.rand.nextInt(11) - 5;
3202            k = MathHelper.floor_double(entityplayer.posY) + this.rand.nextInt(11) - 5;
3203            int j1 = MathHelper.floor_double(entityplayer.posZ) + this.rand.nextInt(11) - 5;
3204            this.updateAllLightTypes(j, k, j1);
3205        }
3206
3207        this.theProfiler.endSection();
3208    }
3209
3210    protected void moodSoundAndLightCheck(int par1, int par2, Chunk par3Chunk)
3211    {
3212        this.theProfiler.endStartSection("moodSound");
3213
3214        if (this.ambientTickCountdown == 0 && !this.isRemote)
3215        {
3216            this.updateLCG = this.updateLCG * 3 + 1013904223;
3217            int k = this.updateLCG >> 2;
3218            int l = k & 15;
3219            int i1 = k >> 8 & 15;
3220            int j1 = k >> 16 & 127;
3221            int k1 = par3Chunk.getBlockID(l, j1, i1);
3222            l += par1;
3223            i1 += par2;
3224
3225            if (k1 == 0 && this.getFullBlockLightValue(l, j1, i1) <= this.rand.nextInt(8) && this.getSavedLightValue(EnumSkyBlock.Sky, l, j1, i1) <= 0)
3226            {
3227                EntityPlayer entityplayer = this.getClosestPlayer((double)l + 0.5D, (double)j1 + 0.5D, (double)i1 + 0.5D, 8.0D);
3228
3229                if (entityplayer != null && entityplayer.getDistanceSq((double)l + 0.5D, (double)j1 + 0.5D, (double)i1 + 0.5D) > 4.0D)
3230                {
3231                    this.playSoundEffect((double)l + 0.5D, (double)j1 + 0.5D, (double)i1 + 0.5D, "ambient.cave.cave", 0.7F, 0.8F + this.rand.nextFloat() * 0.2F);
3232                    this.ambientTickCountdown = this.rand.nextInt(12000) + 6000;
3233                }
3234            }
3235        }
3236
3237        this.theProfiler.endStartSection("checkLight");
3238        par3Chunk.enqueueRelightChecks();
3239    }
3240
3241    /**
3242     * plays random cave ambient sounds and runs updateTick on random blocks within each chunk in the vacinity of a
3243     * player
3244     */
3245    protected void tickBlocksAndAmbiance()
3246    {
3247        this.setActivePlayerChunksAndCheckLight();
3248    }
3249
3250    /**
3251     * checks to see if a given block is both water and is cold enough to freeze
3252     */
3253    public boolean isBlockFreezable(int par1, int par2, int par3)
3254    {
3255        return this.canBlockFreeze(par1, par2, par3, false);
3256    }
3257
3258    /**
3259     * checks to see if a given block is both water and has at least one immediately adjacent non-water block
3260     */
3261    public boolean isBlockFreezableNaturally(int par1, int par2, int par3)
3262    {
3263        return this.canBlockFreeze(par1, par2, par3, true);
3264    }
3265
3266    /**
3267     * checks to see if a given block is both water, and cold enough to freeze - if the par4 boolean is set, this will
3268     * only return true if there is a non-water block immediately adjacent to the specified block
3269     */
3270    public boolean canBlockFreeze(int par1, int par2, int par3, boolean par4)
3271    {
3272        return provider.canBlockFreeze(par1, par2, par3, par4);
3273    }
3274
3275    public boolean canBlockFreezeBody(int par1, int par2, int par3, boolean par4)
3276    {
3277        BiomeGenBase biomegenbase = this.getBiomeGenForCoords(par1, par3);
3278        float f = biomegenbase.getFloatTemperature();
3279
3280        if (f > 0.15F)
3281        {
3282            return false;
3283        }
3284        else
3285        {
3286            if (par2 >= 0 && par2 < 256 && this.getSavedLightValue(EnumSkyBlock.Block, par1, par2, par3) < 10)
3287            {
3288                int l = this.getBlockId(par1, par2, par3);
3289
3290                if ((l == Block.waterStill.blockID || l == Block.waterMoving.blockID) && this.getBlockMetadata(par1, par2, par3) == 0)
3291                {
3292                    if (!par4)
3293                    {
3294                        return true;
3295                    }
3296
3297                    boolean flag1 = true;
3298
3299                    if (flag1 && this.getBlockMaterial(par1 - 1, par2, par3) != Material.water)
3300                    {
3301                        flag1 = false;
3302                    }
3303
3304                    if (flag1 && this.getBlockMaterial(par1 + 1, par2, par3) != Material.water)
3305                    {
3306                        flag1 = false;
3307                    }
3308
3309                    if (flag1 && this.getBlockMaterial(par1, par2, par3 - 1) != Material.water)
3310                    {
3311                        flag1 = false;
3312                    }
3313
3314                    if (flag1 && this.getBlockMaterial(par1, par2, par3 + 1) != Material.water)
3315                    {
3316                        flag1 = false;
3317                    }
3318
3319                    if (!flag1)
3320                    {
3321                        return true;
3322                    }
3323                }
3324            }
3325
3326            return false;
3327        }
3328    }
3329
3330    /**
3331     * Tests whether or not snow can be placed at a given location
3332     */
3333    public boolean canSnowAt(int par1, int par2, int par3)
3334    {
3335        return provider.canSnowAt(par1, par2, par3);
3336    }
3337
3338    public boolean canSnowAtBody(int par1, int par2, int par3)
3339    {
3340        BiomeGenBase biomegenbase = this.getBiomeGenForCoords(par1, par3);
3341        float f = biomegenbase.getFloatTemperature();
3342
3343        if (f > 0.15F)
3344        {
3345            return false;
3346        }
3347        else
3348        {
3349            if (par2 >= 0 && par2 < 256 && this.getSavedLightValue(EnumSkyBlock.Block, par1, par2, par3) < 10)
3350            {
3351                int l = this.getBlockId(par1, par2 - 1, par3);
3352                int i1 = this.getBlockId(par1, par2, par3);
3353
3354                if (i1 == 0 && Block.snow.canPlaceBlockAt(this, par1, par2, par3) && l != 0 && l != Block.ice.blockID && Block.blocksList[l].blockMaterial.blocksMovement())
3355                {
3356                    return true;
3357                }
3358            }
3359
3360            return false;
3361        }
3362    }
3363
3364    public void updateAllLightTypes(int par1, int par2, int par3)
3365    {
3366        if (!this.provider.hasNoSky)
3367        {
3368            this.updateLightByType(EnumSkyBlock.Sky, par1, par2, par3);
3369        }
3370
3371        this.updateLightByType(EnumSkyBlock.Block, par1, par2, par3);
3372    }
3373
3374    private int func_98179_a(int par1, int par2, int par3, EnumSkyBlock par4EnumSkyBlock)
3375    {
3376        if (par4EnumSkyBlock == EnumSkyBlock.Sky && this.canBlockSeeTheSky(par1, par2, par3))
3377        {
3378            return 15;
3379        }
3380        else
3381        {
3382            int l = this.getBlockId(par1, par2, par3);
3383            Block block = Block.blocksList[l];
3384            int blockLight = (block == null ? 0 : block.getLightValue(this, par1, par2, par3));
3385            int i1 = par4EnumSkyBlock == EnumSkyBlock.Sky ? 0 : blockLight;
3386            int j1 = (block == null ? 0 : block.getLightOpacity(this, par1, par2, par3));
3387
3388            if (j1 >= 15 && blockLight > 0)
3389            {
3390                j1 = 1;
3391            }
3392
3393            if (j1 < 1)
3394            {
3395                j1 = 1;
3396            }
3397
3398            if (j1 >= 15)
3399            {
3400                return 0;
3401            }
3402            else if (i1 >= 14)
3403            {
3404                return i1;
3405            }
3406            else
3407            {
3408                for (int k1 = 0; k1 < 6; ++k1)
3409                {
3410                    int l1 = par1 + Facing.offsetsXForSide[k1];
3411                    int i2 = par2 + Facing.offsetsYForSide[k1];
3412                    int j2 = par3 + Facing.offsetsZForSide[k1];
3413                    int k2 = this.getSavedLightValue(par4EnumSkyBlock, l1, i2, j2) - j1;
3414
3415                    if (k2 > i1)
3416                    {
3417                        i1 = k2;
3418                    }
3419
3420                    if (i1 >= 14)
3421                    {
3422                        return i1;
3423                    }
3424                }
3425
3426                return i1;
3427            }
3428        }
3429    }
3430
3431    public void updateLightByType(EnumSkyBlock par1EnumSkyBlock, int par2, int par3, int par4)
3432    {
3433        if (this.doChunksNearChunkExist(par2, par3, par4, 17))
3434        {
3435            int l = 0;
3436            int i1 = 0;
3437            this.theProfiler.startSection("getBrightness");
3438            int j1 = this.getSavedLightValue(par1EnumSkyBlock, par2, par3, par4);
3439            int k1 = this.func_98179_a(par2, par3, par4, par1EnumSkyBlock);
3440            int l1;
3441            int i2;
3442            int j2;
3443            int k2;
3444            int l2;
3445            int i3;
3446            int j3;
3447            int k3;
3448            int l3;
3449
3450            if (k1 > j1)
3451            {
3452                this.lightUpdateBlockList[i1++] = 133152;
3453            }
3454            else if (k1 < j1)
3455            {
3456                this.lightUpdateBlockList[i1++] = 133152 | j1 << 18;
3457
3458                while (l < i1)
3459                {
3460                    l1 = this.lightUpdateBlockList[l++];
3461                    i2 = (l1 & 63) - 32 + par2;
3462                    j2 = (l1 >> 6 & 63) - 32 + par3;
3463                    k2 = (l1 >> 12 & 63) - 32 + par4;
3464                    l2 = l1 >> 18 & 15;
3465                    i3 = this.getSavedLightValue(par1EnumSkyBlock, i2, j2, k2);
3466
3467                    if (i3 == l2)
3468                    {
3469                        this.setLightValue(par1EnumSkyBlock, i2, j2, k2, 0);
3470
3471                        if (l2 > 0)
3472                        {
3473                            j3 = MathHelper.abs_int(i2 - par2);
3474                            l3 = MathHelper.abs_int(j2 - par3);
3475                            k3 = MathHelper.abs_int(k2 - par4);
3476
3477                            if (j3 + l3 + k3 < 17)
3478                            {
3479                                for (int i4 = 0; i4 < 6; ++i4)
3480                                {
3481                                    int j4 = i2 + Facing.offsetsXForSide[i4];
3482                                    int k4 = j2 + Facing.offsetsYForSide[i4];
3483                                    int l4 = k2 + Facing.offsetsZForSide[i4];
3484                                    Block block = Block.blocksList[getBlockId(j4, k4, l4)];
3485                                    int blockOpacity = (block == null ? 0 : block.getLightOpacity(this, j4, k4, l4));
3486                                    int i5 = Math.max(1, blockOpacity);
3487                                    i3 = this.getSavedLightValue(par1EnumSkyBlock, j4, k4, l4);
3488
3489                                    if (i3 == l2 - i5 && i1 < this.lightUpdateBlockList.length)
3490                                    {
3491                                        this.lightUpdateBlockList[i1++] = j4 - par2 + 32 | k4 - par3 + 32 << 6 | l4 - par4 + 32 << 12 | l2 - i5 << 18;
3492                                    }
3493                                }
3494                            }
3495                        }
3496                    }
3497                }
3498
3499                l = 0;
3500            }
3501
3502            this.theProfiler.endSection();
3503            this.theProfiler.startSection("checkedPosition < toCheckCount");
3504
3505            while (l < i1)
3506            {
3507                l1 = this.lightUpdateBlockList[l++];
3508                i2 = (l1 & 63) - 32 + par2;
3509                j2 = (l1 >> 6 & 63) - 32 + par3;
3510                k2 = (l1 >> 12 & 63) - 32 + par4;
3511                l2 = this.getSavedLightValue(par1EnumSkyBlock, i2, j2, k2);
3512                i3 = this.func_98179_a(i2, j2, k2, par1EnumSkyBlock);
3513
3514                if (i3 != l2)
3515                {
3516                    this.setLightValue(par1EnumSkyBlock, i2, j2, k2, i3);
3517
3518                    if (i3 > l2)
3519                    {
3520                        j3 = Math.abs(i2 - par2);
3521                        l3 = Math.abs(j2 - par3);
3522                        k3 = Math.abs(k2 - par4);
3523                        boolean flag = i1 < this.lightUpdateBlockList.length - 6;
3524
3525                        if (j3 + l3 + k3 < 17 && flag)
3526                        {
3527                            if (this.getSavedLightValue(par1EnumSkyBlock, i2 - 1, j2, k2) < i3)
3528                            {
3529                                this.lightUpdateBlockList[i1++] = i2 - 1 - par2 + 32 + (j2 - par3 + 32 << 6) + (k2 - par4 + 32 << 12);
3530                            }
3531
3532                            if (this.getSavedLightValue(par1EnumSkyBlock, i2 + 1, j2, k2) < i3)
3533                            {
3534                                this.lightUpdateBlockList[i1++] = i2 + 1 - par2 + 32 + (j2 - par3 + 32 << 6) + (k2 - par4 + 32 << 12);
3535                            }
3536
3537                            if (this.getSavedLightValue(par1EnumSkyBlock, i2, j2 - 1, k2) < i3)
3538                            {
3539                                this.lightUpdateBlockList[i1++] = i2 - par2 + 32 + (j2 - 1 - par3 + 32 << 6) + (k2 - par4 + 32 << 12);
3540                            }
3541
3542                            if (this.getSavedLightValue(par1EnumSkyBlock, i2, j2 + 1, k2) < i3)
3543                            {
3544                                this.lightUpdateBlockList[i1++] = i2 - par2 + 32 + (j2 + 1 - par3 + 32 << 6) + (k2 - par4 + 32 << 12);
3545                            }
3546
3547                            if (this.getSavedLightValue(par1EnumSkyBlock, i2, j2, k2 - 1) < i3)
3548                            {
3549                                this.lightUpdateBlockList[i1++] = i2 - par2 + 32 + (j2 - par3 + 32 << 6) + (k2 - 1 - par4 + 32 << 12);
3550                            }
3551
3552                            if (this.getSavedLightValue(par1EnumSkyBlock, i2, j2, k2 + 1) < i3)
3553                            {
3554                                this.lightUpdateBlockList[i1++] = i2 - par2 + 32 + (j2 - par3 + 32 << 6) + (k2 + 1 - par4 + 32 << 12);
3555                            }
3556                        }
3557                    }
3558                }
3559            }
3560
3561            this.theProfiler.endSection();
3562        }
3563    }
3564
3565    /**
3566     * Runs through the list of updates to run and ticks them
3567     */
3568    public boolean tickUpdates(boolean par1)
3569    {
3570        return false;
3571    }
3572
3573    public List getPendingBlockUpdates(Chunk par1Chunk, boolean par2)
3574    {
3575        return null;
3576    }
3577
3578    /**
3579     * Will get all entities within the specified AABB excluding the one passed into it. Args: entityToExclude, aabb
3580     */
3581    public List getEntitiesWithinAABBExcludingEntity(Entity par1Entity, AxisAlignedBB par2AxisAlignedBB)
3582    {
3583        return this.func_94576_a(par1Entity, par2AxisAlignedBB, (IEntitySelector)null);
3584    }
3585
3586    public List func_94576_a(Entity par1Entity, AxisAlignedBB par2AxisAlignedBB, IEntitySelector par3IEntitySelector)
3587    {
3588        ArrayList arraylist = new ArrayList();
3589        int i = MathHelper.floor_double((par2AxisAlignedBB.minX - MAX_ENTITY_RADIUS) / 16.0D);
3590        int j = MathHelper.floor_double((par2AxisAlignedBB.maxX + MAX_ENTITY_RADIUS) / 16.0D);
3591        int k = MathHelper.floor_double((par2AxisAlignedBB.minZ - MAX_ENTITY_RADIUS) / 16.0D);
3592        int l = MathHelper.floor_double((par2AxisAlignedBB.maxZ + MAX_ENTITY_RADIUS) / 16.0D);
3593
3594        for (int i1 = i; i1 <= j; ++i1)
3595        {
3596            for (int j1 = k; j1 <= l; ++j1)
3597            {
3598                if (this.chunkExists(i1, j1))
3599                {
3600                    this.getChunkFromChunkCoords(i1, j1).getEntitiesWithinAABBForEntity(par1Entity, par2AxisAlignedBB, arraylist, par3IEntitySelector);
3601                }
3602            }
3603        }
3604
3605        return arraylist;
3606    }
3607
3608    /**
3609     * Returns all entities of the specified class type which intersect with the AABB. Args: entityClass, aabb
3610     */
3611    public List getEntitiesWithinAABB(Class par1Class, AxisAlignedBB par2AxisAlignedBB)
3612    {
3613        return this.selectEntitiesWithinAABB(par1Class, par2AxisAlignedBB, (IEntitySelector)null);
3614    }
3615
3616    public List selectEntitiesWithinAABB(Class par1Class, AxisAlignedBB par2AxisAlignedBB, IEntitySelector par3IEntitySelector)
3617    {
3618        int i = MathHelper.floor_double((par2AxisAlignedBB.minX - MAX_ENTITY_RADIUS) / 16.0D);
3619        int j = MathHelper.floor_double((par2AxisAlignedBB.maxX + MAX_ENTITY_RADIUS) / 16.0D);
3620        int k = MathHelper.floor_double((par2AxisAlignedBB.minZ - MAX_ENTITY_RADIUS) / 16.0D);
3621        int l = MathHelper.floor_double((par2AxisAlignedBB.maxZ + MAX_ENTITY_RADIUS) / 16.0D);
3622        ArrayList arraylist = new ArrayList();
3623
3624        for (int i1 = i; i1 <= j; ++i1)
3625        {
3626            for (int j1 = k; j1 <= l; ++j1)
3627            {
3628                if (this.chunkExists(i1, j1))
3629                {
3630                    this.getChunkFromChunkCoords(i1, j1).getEntitiesOfTypeWithinAAAB(par1Class, par2AxisAlignedBB, arraylist, par3IEntitySelector);
3631                }
3632            }
3633        }
3634
3635        return arraylist;
3636    }
3637
3638    public Entity findNearestEntityWithinAABB(Class par1Class, AxisAlignedBB par2AxisAlignedBB, Entity par3Entity)
3639    {
3640        List list = this.getEntitiesWithinAABB(par1Class, par2AxisAlignedBB);
3641        Entity entity1 = null;
3642        double d0 = Double.MAX_VALUE;
3643
3644        for (int i = 0; i < list.size(); ++i)
3645        {
3646            Entity entity2 = (Entity)list.get(i);
3647
3648            if (entity2 != par3Entity)
3649            {
3650                double d1 = par3Entity.getDistanceSqToEntity(entity2);
3651
3652                if (d1 <= d0)
3653                {
3654                    entity1 = entity2;
3655                    d0 = d1;
3656                }
3657            }
3658        }
3659
3660        return entity1;
3661    }
3662
3663    /**
3664     * Returns the Entity with the given ID, or null if it doesn't exist in this World.
3665     */
3666    public abstract Entity getEntityByID(int i);
3667
3668    @SideOnly(Side.CLIENT)
3669
3670    /**
3671     * Accessor for world Loaded Entity List
3672     */
3673    public List getLoadedEntityList()
3674    {
3675        return this.loadedEntityList;
3676    }
3677
3678    /**
3679     * marks the chunk that contains this tilentity as modified and then calls worldAccesses.doNothingWithTileEntity
3680     */
3681    public void updateTileEntityChunkAndDoNothing(int par1, int par2, int par3, TileEntity par4TileEntity)
3682    {
3683        if (this.blockExists(par1, par2, par3))
3684        {
3685            this.getChunkFromBlockCoords(par1, par3).setChunkModified();
3686        }
3687    }
3688
3689    /**
3690     * Counts how many entities of an entity class exist in the world. Args: entityClass
3691     */
3692    public int countEntities(Class par1Class)
3693    {
3694        int i = 0;
3695
3696        for (int j = 0; j < this.loadedEntityList.size(); ++j)
3697        {
3698            Entity entity = (Entity)this.loadedEntityList.get(j);
3699
3700            if (par1Class.isAssignableFrom(entity.getClass()))
3701            {
3702                ++i;
3703            }
3704        }
3705
3706        return i;
3707    }
3708
3709    /**
3710     * adds entities to the loaded entities list, and loads thier skins.
3711     */
3712    public void addLoadedEntities(List par1List)
3713    {
3714        for (int i = 0; i < par1List.size(); ++i)
3715        {
3716            Entity entity = (Entity)par1List.get(i);
3717            if (!MinecraftForge.EVENT_BUS.post(new EntityJoinWorldEvent(entity, this)))
3718            {
3719                loadedEntityList.add(entity);
3720                this.obtainEntitySkin(entity);
3721            }
3722        }
3723    }
3724
3725    /**
3726     * Adds a list of entities to be unloaded on the next pass of World.updateEntities()
3727     */
3728    public void unloadEntities(List par1List)
3729    {
3730        this.unloadedEntityList.addAll(par1List);
3731    }
3732
3733    /**
3734     * Returns true if the given Entity can be placed on the given side of the given block position.
3735     */
3736    public boolean canPlaceEntityOnSide(int par1, int par2, int par3, int par4, boolean par5, int par6, Entity par7Entity, ItemStack par8ItemStack)
3737    {
3738        int j1 = this.getBlockId(par2, par3, par4);
3739        Block block = Block.blocksList[j1];
3740        Block block1 = Block.blocksList[par1];
3741        AxisAlignedBB axisalignedbb = block1.getCollisionBoundingBoxFromPool(this, par2, par3, par4);
3742
3743        if (par5)
3744        {
3745            axisalignedbb = null;
3746        }
3747
3748        if (axisalignedbb != null && !this.checkIfAABBIsClearExcludingEntity(axisalignedbb, par7Entity))
3749        {
3750            return false;
3751        }
3752        else
3753        {
3754            if (block != null && (block == Block.waterMoving || block == Block.waterStill || block == Block.lavaMoving || block == Block.lavaStill || block == Block.fire || block.blockMaterial.isReplaceable()))
3755            {
3756                block = null;
3757            }
3758
3759            if (block != null && block.isBlockReplaceable(this, par2, par3, par4))
3760            {
3761                block = null;
3762            }
3763
3764            return block != null && block.blockMaterial == Material.circuits && block1 == Block.anvil ? true : par1 > 0 && block == null && block1.canPlaceBlockOnSide(this, par2, par3, par4, par6, par8ItemStack);
3765        }
3766    }
3767
3768    public PathEntity getPathEntityToEntity(Entity par1Entity, Entity par2Entity, float par3, boolean par4, boolean par5, boolean par6, boolean par7)
3769    {
3770        this.theProfiler.startSection("pathfind");
3771        int i = MathHelper.floor_double(par1Entity.posX);
3772        int j = MathHelper.floor_double(par1Entity.posY + 1.0D);
3773        int k = MathHelper.floor_double(par1Entity.posZ);
3774        int l = (int)(par3 + 16.0F);
3775        int i1 = i - l;
3776        int j1 = j - l;
3777        int k1 = k - l;
3778        int l1 = i + l;
3779        int i2 = j + l;
3780        int j2 = k + l;
3781        ChunkCache chunkcache = new ChunkCache(this, i1, j1, k1, l1, i2, j2, 0);
3782        PathEntity pathentity = (new PathFinder(chunkcache, par4, par5, par6, par7)).createEntityPathTo(par1Entity, par2Entity, par3);
3783        this.theProfiler.endSection();
3784        return pathentity;
3785    }
3786
3787    public PathEntity getEntityPathToXYZ(Entity par1Entity, int par2, int par3, int par4, float par5, boolean par6, boolean par7, boolean par8, boolean par9)
3788    {
3789        this.theProfiler.startSection("pathfind");
3790        int l = MathHelper.floor_double(par1Entity.posX);
3791        int i1 = MathHelper.floor_double(par1Entity.posY);
3792        int j1 = MathHelper.floor_double(par1Entity.posZ);
3793        int k1 = (int)(par5 + 8.0F);
3794        int l1 = l - k1;
3795        int i2 = i1 - k1;
3796        int j2 = j1 - k1;
3797        int k2 = l + k1;
3798        int l2 = i1 + k1;
3799        int i3 = j1 + k1;
3800        ChunkCache chunkcache = new ChunkCache(this, l1, i2, j2, k2, l2, i3, 0);
3801        PathEntity pathentity = (new PathFinder(chunkcache, par6, par7, par8, par9)).createEntityPathTo(par1Entity, par2, par3, par4, par5);
3802        this.theProfiler.endSection();
3803        return pathentity;
3804    }
3805
3806    /**
3807     * Is this block powering in the specified direction Args: x, y, z, direction
3808     */
3809    public int isBlockProvidingPowerTo(int par1, int par2, int par3, int par4)
3810    {
3811        int i1 = this.getBlockId(par1, par2, par3);
3812        return i1 == 0 ? 0 : Block.blocksList[i1].isProvidingStrongPower(this, par1, par2, par3, par4);
3813    }
3814
3815    /**
3816     * Returns the highest redstone signal strength powering the given block. Args: X, Y, Z.
3817     */
3818    public int getBlockPowerInput(int par1, int par2, int par3)
3819    {
3820        byte b0 = 0;
3821        int l = Math.max(b0, this.isBlockProvidingPowerTo(par1, par2 - 1, par3, 0));
3822
3823        if (l >= 15)
3824        {
3825            return l;
3826        }
3827        else
3828        {
3829            l = Math.max(l, this.isBlockProvidingPowerTo(par1, par2 + 1, par3, 1));
3830
3831            if (l >= 15)
3832            {
3833                return l;
3834            }
3835            else
3836            {
3837                l = Math.max(l, this.isBlockProvidingPowerTo(par1, par2, par3 - 1, 2));
3838
3839                if (l >= 15)
3840                {
3841                    return l;
3842                }
3843                else
3844                {
3845                    l = Math.max(l, this.isBlockProvidingPowerTo(par1, par2, par3 + 1, 3));
3846
3847                    if (l >= 15)
3848                    {
3849                        return l;
3850                    }
3851                    else
3852                    {
3853                        l = Math.max(l, this.isBlockProvidingPowerTo(par1 - 1, par2, par3, 4));
3854
3855                        if (l >= 15)
3856                        {
3857                            return l;
3858                        }
3859                        else
3860                        {
3861                            l = Math.max(l, this.isBlockProvidingPowerTo(par1 + 1, par2, par3, 5));
3862                            return l >= 15 ? l : l;
3863                        }
3864                    }
3865                }
3866            }
3867        }
3868    }
3869
3870    /**
3871     * Returns the indirect signal strength being outputted by the given block in the *opposite* of the given direction.
3872     * Args: X, Y, Z, direction
3873     */
3874    public boolean getIndirectPowerOutput(int par1, int par2, int par3, int par4)
3875    {
3876        return this.getIndirectPowerLevelTo(par1, par2, par3, par4) > 0;
3877    }
3878
3879    /**
3880     * Gets the power level from a certain block face.  Args: x, y, z, direction
3881     */
3882    public int getIndirectPowerLevelTo(int par1, int par2, int par3, int par4)
3883    {
3884        if (this.isBlockNormalCube(par1, par2, par3))
3885        {
3886            return this.getBlockPowerInput(par1, par2, par3);
3887        }
3888        else
3889        {
3890            int i1 = this.getBlockId(par1, par2, par3);
3891            return i1 == 0 ? 0 : Block.blocksList[i1].isProvidingWeakPower(this, par1, par2, par3, par4);
3892        }
3893    }
3894
3895    /**
3896     * Used to see if one of the blocks next to you or your block is getting power from a neighboring block. Used by
3897     * items like TNT or Doors so they don't have redstone going straight into them.  Args: x, y, z
3898     */
3899    public boolean isBlockIndirectlyGettingPowered(int par1, int par2, int par3)
3900    {
3901        return this.getIndirectPowerLevelTo(par1, par2 - 1, par3, 0) > 0 ? true : (this.getIndirectPowerLevelTo(par1, par2 + 1, par3, 1) > 0 ? true : (this.getIndirectPowerLevelTo(par1, par2, par3 - 1, 2) > 0 ? true : (this.getIndirectPowerLevelTo(par1, par2, par3 + 1, 3) > 0 ? true : (this.getIndirectPowerLevelTo(par1 - 1, par2, par3, 4) > 0 ? true : this.getIndirectPowerLevelTo(par1 + 1, par2, par3, 5) > 0))));
3902    }
3903
3904    public int getStrongestIndirectPower(int par1, int par2, int par3)
3905    {
3906        int l = 0;
3907
3908        for (int i1 = 0; i1 < 6; ++i1)
3909        {
3910            int j1 = this.getIndirectPowerLevelTo(par1 + Facing.offsetsXForSide[i1], par2 + Facing.offsetsYForSide[i1], par3 + Facing.offsetsZForSide[i1], i1);
3911
3912            if (j1 >= 15)
3913            {
3914                return 15;
3915            }
3916
3917            if (j1 > l)
3918            {
3919                l = j1;
3920            }
3921        }
3922
3923        return l;
3924    }
3925
3926    /**
3927     * Gets the closest player to the entity within the specified distance (if distance is less than 0 then ignored).
3928     * Args: entity, dist
3929     */
3930    public EntityPlayer getClosestPlayerToEntity(Entity par1Entity, double par2)
3931    {
3932        return this.getClosestPlayer(par1Entity.posX, par1Entity.posY, par1Entity.posZ, par2);
3933    }
3934
3935    /**
3936     * Gets the closest player to the point within the specified distance (distance can be set to less than 0 to not
3937     * limit the distance). Args: x, y, z, dist
3938     */
3939    public EntityPlayer getClosestPlayer(double par1, double par3, double par5, double par7)
3940    {
3941        double d4 = -1.0D;
3942        EntityPlayer entityplayer = null;
3943
3944        for (int i = 0; i < this.playerEntities.size(); ++i)
3945        {
3946            EntityPlayer entityplayer1 = (EntityPlayer)this.playerEntities.get(i);
3947            double d5 = entityplayer1.getDistanceSq(par1, par3, par5);
3948
3949            if ((par7 < 0.0D || d5 < par7 * par7) && (d4 == -1.0D || d5 < d4))
3950            {
3951                d4 = d5;
3952                entityplayer = entityplayer1;
3953            }
3954        }
3955
3956        return entityplayer;
3957    }
3958
3959    /**
3960     * Returns the closest vulnerable player to this entity within the given radius, or null if none is found
3961     */
3962    public EntityPlayer getClosestVulnerablePlayerToEntity(Entity par1Entity, double par2)
3963    {
3964        return this.getClosestVulnerablePlayer(par1Entity.posX, par1Entity.posY, par1Entity.posZ, par2);
3965    }
3966
3967    /**
3968     * Returns the closest vulnerable player within the given radius, or null if none is found.
3969     */
3970    public EntityPlayer getClosestVulnerablePlayer(double par1, double par3, double par5, double par7)
3971    {
3972        double d4 = -1.0D;
3973        EntityPlayer entityplayer = null;
3974
3975        for (int i = 0; i < this.playerEntities.size(); ++i)
3976        {
3977            EntityPlayer entityplayer1 = (EntityPlayer)this.playerEntities.get(i);
3978
3979            if (!entityplayer1.capabilities.disableDamage && entityplayer1.isEntityAlive())
3980            {
3981                double d5 = entityplayer1.getDistanceSq(par1, par3, par5);
3982                double d6 = par7;
3983
3984                if (entityplayer1.isSneaking())
3985                {
3986                    d6 = par7 * 0.800000011920929D;
3987                }
3988
3989                if (entityplayer1.getHasActivePotion())
3990                {
3991                    float f = entityplayer1.func_82243_bO();
3992
3993                    if (f < 0.1F)
3994                    {
3995                        f = 0.1F;
3996                    }
3997
3998                    d6 *= (double)(0.7F * f);
3999                }
4000
4001                if ((par7 < 0.0D || d5 < d6 * d6) && (d4 == -1.0D || d5 < d4))
4002                {
4003                    d4 = d5;
4004                    entityplayer = entityplayer1;
4005                }
4006            }
4007        }
4008
4009        return entityplayer;
4010    }
4011
4012    /**
4013     * Find a player by name in this world.
4014     */
4015    public EntityPlayer getPlayerEntityByName(String par1Str)
4016    {
4017        for (int i = 0; i < this.playerEntities.size(); ++i)
4018        {
4019            if (par1Str.equals(((EntityPlayer)this.playerEntities.get(i)).username))
4020            {
4021                return (EntityPlayer)this.playerEntities.get(i);
4022            }
4023        }
4024
4025        return null;
4026    }
4027
4028    @SideOnly(Side.CLIENT)
4029
4030    /**
4031     * If on MP, sends a quitting packet.
4032     */
4033    public void sendQuittingDisconnectingPacket() {}
4034
4035    /**
4036     * Checks whether the session lock file was modified by another process
4037     */
4038    public void checkSessionLock() throws MinecraftException
4039    {
4040        this.saveHandler.checkSessionLock();
4041    }
4042
4043    @SideOnly(Side.CLIENT)
4044    public void func_82738_a(long par1)
4045    {
4046        this.worldInfo.incrementTotalWorldTime(par1);
4047    }
4048
4049    /**
4050     * Retrieve the world seed from level.dat
4051     */
4052    public long getSeed()
4053    {
4054        return provider.getSeed();
4055    }
4056
4057    public long getTotalWorldTime()
4058    {
4059        return this.worldInfo.getWorldTotalTime();
4060    }
4061
4062    public long getWorldTime()
4063    {
4064        return provider.getWorldTime();
4065    }
4066
4067    /**
4068     * Sets the world time.
4069     */
4070    public void setWorldTime(long par1)
4071    {
4072        provider.setWorldTime(par1);
4073    }
4074
4075    /**
4076     * Returns the coordinates of the spawn point
4077     */
4078    public ChunkCoordinates getSpawnPoint()
4079    {
4080        return provider.getSpawnPoint();
4081    }
4082
4083    @SideOnly(Side.CLIENT)
4084    public void setSpawnLocation(int par1, int par2, int par3)
4085    {
4086        provider.setSpawnPoint(par1, par2, par3);
4087    }
4088
4089    @SideOnly(Side.CLIENT)
4090
4091    /**
4092     * spwans an entity and loads surrounding chunks
4093     */
4094    public void joinEntityInSurroundings(Entity par1Entity)
4095    {
4096        int i = MathHelper.floor_double(par1Entity.posX / 16.0D);
4097        int j = MathHelper.floor_double(par1Entity.posZ / 16.0D);
4098        byte b0 = 2;
4099
4100        for (int k = i - b0; k <= i + b0; ++k)
4101        {
4102            for (int l = j - b0; l <= j + b0; ++l)
4103            {
4104                this.getChunkFromChunkCoords(k, l);
4105            }
4106        }
4107
4108        if (!this.loadedEntityList.contains(par1Entity))
4109        {
4110            if (!MinecraftForge.EVENT_BUS.post(new EntityJoinWorldEvent(par1Entity, this)))
4111            {
4112                loadedEntityList.add(par1Entity);
4113            }
4114        }
4115    }
4116
4117    /**
4118     * Called when checking if a certain block can be mined or not. The 'spawn safe zone' check is located here.
4119     */
4120    public boolean canMineBlock(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
4121    {
4122        return provider.canMineBlock(par1EntityPlayer, par2, par3, par4);
4123    }
4124
4125    public boolean canMineBlockBody(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
4126    {
4127        return true;
4128    }
4129
4130    /**
4131     * sends a Packet 38 (Entity Status) to all tracked players of that entity
4132     */
4133    public void setEntityState(Entity par1Entity, byte par2) {}
4134
4135    /**
4136     * gets the IChunkProvider this world uses.
4137     */
4138    public IChunkProvider getChunkProvider()
4139    {
4140        return this.chunkProvider;
4141    }
4142
4143    /**
4144     * Adds a block event with the given Args to the blockEventCache. During the next tick(), the block specified will
4145     * have its onBlockEvent handler called with the given parameters. Args: X,Y,Z, BlockID, EventID, EventParameter
4146     */
4147    public void addBlockEvent(int par1, int par2, int par3, int par4, int par5, int par6)
4148    {
4149        if (par4 > 0)
4150        {
4151            Block.blocksList[par4].onBlockEventReceived(this, par1, par2, par3, par5, par6);
4152        }
4153    }
4154
4155    /**
4156     * Returns this world's current save handler
4157     */
4158    public ISaveHandler getSaveHandler()
4159    {
4160        return this.saveHandler;
4161    }
4162
4163    /**
4164     * Gets the World's WorldInfo instance
4165     */
4166    public WorldInfo getWorldInfo()
4167    {
4168        return this.worldInfo;
4169    }
4170
4171    /**
4172     * Gets the GameRules instance.
4173     */
4174    public GameRules getGameRules()
4175    {
4176        return this.worldInfo.getGameRulesInstance();
4177    }
4178
4179    /**
4180     * Updates the flag that indicates whether or not all players in the world are sleeping.
4181     */
4182    public void updateAllPlayersSleepingFlag() {}
4183
4184    public float getWeightedThunderStrength(float par1)
4185    {
4186        return (this.prevThunderingStrength + (this.thunderingStrength - this.prevThunderingStrength) * par1) * this.getRainStrength(par1);
4187    }
4188
4189    /**
4190     * Not sure about this actually. Reverting this one myself.
4191     */
4192    public float getRainStrength(float par1)
4193    {
4194        return this.prevRainingStrength + (this.rainingStrength - this.prevRainingStrength) * par1;
4195    }
4196
4197    @SideOnly(Side.CLIENT)
4198    public void setRainStrength(float par1)
4199    {
4200        this.prevRainingStrength = par1;
4201        this.rainingStrength = par1;
4202    }
4203
4204    /**
4205     * Returns true if the current thunder strength (weighted with the rain strength) is greater than 0.9
4206     */
4207    public boolean isThundering()
4208    {
4209        return (double)this.getWeightedThunderStrength(1.0F) > 0.9D;
4210    }
4211
4212    /**
4213     * Returns true if the current rain strength is greater than 0.2
4214     */
4215    public boolean isRaining()
4216    {
4217        return (double)this.getRainStrength(1.0F) > 0.2D;
4218    }
4219
4220    public boolean canLightningStrikeAt(int par1, int par2, int par3)
4221    {
4222        if (!this.isRaining())
4223        {
4224            return false;
4225        }
4226        else if (!this.canBlockSeeTheSky(par1, par2, par3))
4227        {
4228            return false;
4229        }
4230        else if (this.getPrecipitationHeight(par1, par3) > par2)
4231        {
4232            return false;
4233        }
4234        else
4235        {
4236            BiomeGenBase biomegenbase = this.getBiomeGenForCoords(par1, par3);
4237            return biomegenbase.getEnableSnow() ? false : biomegenbase.canSpawnLightningBolt();
4238        }
4239    }
4240
4241    /**
4242     * Checks to see if the biome rainfall values for a given x,y,z coordinate set are extremely high
4243     */
4244    public boolean isBlockHighHumidity(int par1, int par2, int par3)
4245    {
4246        return provider.isBlockHighHumidity(par1, par2, par3);
4247    }
4248
4249    /**
4250     * Assigns the given String id to the given MapDataBase using the MapStorage, removing any existing ones of the same
4251     * id.
4252     */
4253    public void setItemData(String par1Str, WorldSavedData par2WorldSavedData)
4254    {
4255        this.mapStorage.setData(par1Str, par2WorldSavedData);
4256    }
4257
4258    /**
4259     * Loads an existing MapDataBase corresponding to the given String id from disk using the MapStorage, instantiating
4260     * the given Class, or returns null if none such file exists. args: Class to instantiate, String dataid
4261     */
4262    public WorldSavedData loadItemData(Class par1Class, String par2Str)
4263    {
4264        return this.mapStorage.loadData(par1Class, par2Str);
4265    }
4266
4267    /**
4268     * Returns an unique new data id from the MapStorage for the given prefix and saves the idCounts map to the
4269     * 'idcounts' file.
4270     */
4271    public int getUniqueDataId(String par1Str)
4272    {
4273        return this.mapStorage.getUniqueDataId(par1Str);
4274    }
4275
4276    public void func_82739_e(int par1, int par2, int par3, int par4, int par5)
4277    {
4278        for (int j1 = 0; j1 < this.worldAccesses.size(); ++j1)
4279        {
4280            ((IWorldAccess)this.worldAccesses.get(j1)).broadcastSound(par1, par2, par3, par4, par5);
4281        }
4282    }
4283
4284    /**
4285     * See description for playAuxSFX.
4286     */
4287    public void playAuxSFX(int par1, int par2, int par3, int par4, int par5)
4288    {
4289        this.playAuxSFXAtEntity((EntityPlayer)null, par1, par2, par3, par4, par5);
4290    }
4291
4292    /**
4293     * See description for playAuxSFX.
4294     */
4295    public void playAuxSFXAtEntity(EntityPlayer par1EntityPlayer, int par2, int par3, int par4, int par5, int par6)
4296    {
4297        try
4298        {
4299            for (int j1 = 0; j1 < this.worldAccesses.size(); ++j1)
4300            {
4301                ((IWorldAccess)this.worldAccesses.get(j1)).playAuxSFX(par1EntityPlayer, par2, par3, par4, par5, par6);
4302            }
4303        }
4304        catch (Throwable throwable)
4305        {
4306            CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Playing level event");
4307            CrashReportCategory crashreportcategory = crashreport.makeCategory("Level event being played");
4308            crashreportcategory.addCrashSection("Block coordinates", CrashReportCategory.func_85071_a(par3, par4, par5));
4309            crashreportcategory.addCrashSection("Event source", par1EntityPlayer);
4310            crashreportcategory.addCrashSection("Event type", Integer.valueOf(par2));
4311            crashreportcategory.addCrashSection("Event data", Integer.valueOf(par6));
4312            throw new ReportedException(crashreport);
4313        }
4314    }
4315
4316    /**
4317     * Returns current world height.
4318     */
4319    public int getHeight()
4320    {
4321        return provider.getHeight();
4322    }
4323
4324    /**
4325     * Returns current world height.
4326     */
4327    public int getActualHeight()
4328    {
4329        return provider.getActualHeight();
4330    }
4331
4332    public IUpdatePlayerListBox func_82735_a(EntityMinecart par1EntityMinecart)
4333    {
4334        return null;
4335    }
4336
4337    /**
4338     * puts the World Random seed to a specific state dependant on the inputs
4339     */
4340    public Random setRandomSeed(int par1, int par2, int par3)
4341    {
4342        long l = (long)par1 * 341873128712L + (long)par2 * 132897987541L + this.getWorldInfo().getSeed() + (long)par3;
4343        this.rand.setSeed(l);
4344        return this.rand;
4345    }
4346
4347    /**
4348     * Returns the location of the closest structure of the specified type. If not found returns null.
4349     */
4350    public ChunkPosition findClosestStructure(String par1Str, int par2, int par3, int par4)
4351    {
4352        return this.getChunkProvider().findClosestStructure(this, par1Str, par2, par3, par4);
4353    }
4354
4355    @SideOnly(Side.CLIENT)
4356
4357    /**
4358     * set by !chunk.getAreLevelsEmpty
4359     */
4360    public boolean extendedLevelsInChunkCache()
4361    {
4362        return false;
4363    }
4364
4365    @SideOnly(Side.CLIENT)
4366
4367    /**
4368     * Returns horizon height for use in rendering the sky.
4369     */
4370    public double getHorizon()
4371    {
4372        return provider.getHorizon();
4373    }
4374
4375    /**
4376     * Adds some basic stats of the world to the given crash report.
4377     */
4378    public CrashReportCategory addWorldInfoToCrashReport(CrashReport par1CrashReport)
4379    {
4380        CrashReportCategory crashreportcategory = par1CrashReport.makeCategoryDepth("Affected level", 1);
4381        crashreportcategory.addCrashSection("Level name", this.worldInfo == null ? "????" : this.worldInfo.getWorldName());
4382        crashreportcategory.addCrashSectionCallable("All players", new CallableLvl2(this));
4383        crashreportcategory.addCrashSectionCallable("Chunk stats", new CallableLvl3(this));
4384
4385        try
4386        {
4387            this.worldInfo.addToCrashReport(crashreportcategory);
4388        }
4389        catch (Throwable throwable)
4390        {
4391            crashreportcategory.addCrashSectionThrowable("Level Data Unobtainable", throwable);
4392        }
4393
4394        return crashreportcategory;
4395    }
4396
4397    /**
4398     * Starts (or continues) destroying a block with given ID at the given coordinates for the given partially destroyed
4399     * value
4400     */
4401    public void destroyBlockInWorldPartially(int par1, int par2, int par3, int par4, int par5)
4402    {
4403        for (int j1 = 0; j1 < this.worldAccesses.size(); ++j1)
4404        {
4405            IWorldAccess iworldaccess = (IWorldAccess)this.worldAccesses.get(j1);
4406            iworldaccess.destroyBlockPartially(par1, par2, par3, par4, par5);
4407        }
4408    }
4409
4410    /**
4411     * Return the Vec3Pool object for this world.
4412     */
4413    public Vec3Pool getWorldVec3Pool()
4414    {
4415        return this.vecPool;
4416    }
4417
4418    /**
4419     * returns a calendar object containing the current date
4420     */
4421    public Calendar getCurrentDate()
4422    {
4423        if (this.getTotalWorldTime() % 600L == 0L)
4424        {
4425            this.theCalendar.setTimeInMillis(System.currentTimeMillis());
4426        }
4427
4428        return this.theCalendar;
4429    }
4430
4431    @SideOnly(Side.CLIENT)
4432    public void func_92088_a(double par1, double par3, double par5, double par7, double par9, double par11, NBTTagCompound par13NBTTagCompound) {}
4433
4434    public Scoreboard getScoreboard()
4435    {
4436        return this.worldScoreboard;
4437    }
4438
4439    public void func_96440_m(int par1, int par2, int par3, int par4)
4440    {
4441        for (int i1 = 0; i1 < 4; ++i1)
4442        {
4443            int j1 = par1 + Direction.offsetX[i1];
4444            int k1 = par3 + Direction.offsetZ[i1];
4445            int l1 = this.getBlockId(j1, par2, k1);
4446
4447            if (l1 != 0)
4448            {
4449                Block block = Block.blocksList[l1];
4450
4451                if (Block.redstoneComparatorIdle.func_94487_f(l1))
4452                {
4453                    block.onNeighborBlockChange(this, j1, par2, k1, par4);
4454                }
4455                else if (Block.isNormalCube(l1))
4456                {
4457                    j1 += Direction.offsetX[i1];
4458                    k1 += Direction.offsetZ[i1];
4459                    l1 = this.getBlockId(j1, par2, k1);
4460                    block = Block.blocksList[l1];
4461
4462                    if (Block.redstoneComparatorIdle.func_94487_f(l1))
4463                    {
4464                        block.onNeighborBlockChange(this, j1, par2, k1, par4);
4465                    }
4466                }
4467            }
4468        }
4469    }
4470
4471    public ILogAgent getWorldLogAgent()
4472    {
4473        return this.field_98181_L;
4474    }
4475
4476    /**
4477     * Adds a single TileEntity to the world.
4478     * @param entity The TileEntity to be added.
4479     */
4480    public void addTileEntity(TileEntity entity)
4481    {
4482        List dest = scanningTileEntities ? addedTileEntityList : loadedTileEntityList;
4483        if(entity.canUpdate())
4484        {
4485            dest.add(entity);
4486        }
4487    }
4488
4489    /**
4490     * Determine if the given block is considered solid on the
4491     * specified side.  Used by placement logic.
4492     *
4493     * @param x Block X Position
4494     * @param y Block Y Position
4495     * @param z Block Z Position
4496     * @param side The Side in question
4497     * @return True if the side is solid
4498     */
4499    public boolean isBlockSolidOnSide(int x, int y, int z, ForgeDirection side)
4500    {
4501        return isBlockSolidOnSide(x, y, z, side, false);
4502    }
4503
4504    /**
4505     * Determine if the given block is considered solid on the
4506     * specified side.  Used by placement logic.
4507     *
4508     * @param x Block X Position
4509     * @param y Block Y Position
4510     * @param z Block Z Position
4511     * @param side The Side in question
4512     * @param _default The defult to return if the block doesn't exist.
4513     * @return True if the side is solid
4514     */
4515    public boolean isBlockSolidOnSide(int x, int y, int z, ForgeDirection side, boolean _default)
4516    {
4517        if (x < -30000000 || z < -30000000 || x >= 30000000 || z >= 30000000)
4518        {
4519            return _default;
4520        }
4521
4522        Chunk chunk = this.chunkProvider.provideChunk(x >> 4, z >> 4);
4523        if (chunk == null || chunk.isEmpty())
4524        {
4525            return _default;
4526        }
4527
4528        Block block = Block.blocksList[getBlockId(x, y, z)];
4529        if(block == null)
4530        {
4531            return false;
4532        }
4533
4534        return block.isBlockSolidOnSide(this, x, y, z, side);
4535    }
4536
4537    /**
4538     * Get the persistent chunks for this world
4539     *
4540     * @return
4541     */
4542    public ImmutableSetMultimap<ChunkCoordIntPair, Ticket> getPersistentChunks()
4543    {
4544        return ForgeChunkManager.getPersistentChunksFor(this);
4545    }
4546
4547    /**
4548     * Readded as it was removed, very useful helper function
4549     * 
4550     * @param x X position
4551     * @param y Y Position
4552     * @param z Z Position
4553     * @return The blocks light opacity
4554     */
4555    public int getBlockLightOpacity(int x, int y, int z)
4556    {
4557        if (x < -30000000 || z < -30000000 || x >= 30000000 || z >= 30000000)
4558        {
4559            return 0;
4560        }
4561
4562        if (y < 0 || y >= 256)
4563        {
4564            return 0;
4565        }
4566
4567        return getChunkFromChunkCoords(x >> 4, z >> 4).getBlockLightOpacity(x & 15, y, z & 15);
4568    }
4569
4570    /**
4571     * Returns a count of entities that classify themselves as the specified creature type.
4572     */
4573    public int countEntities(EnumCreatureType type, boolean forSpawnCount)
4574    {
4575        int count = 0;
4576        for (int x = 0; x < loadedEntityList.size(); x++)
4577        {
4578            if (((Entity)loadedEntityList.get(x)).isCreatureType(type, forSpawnCount))
4579            {
4580                count++;
4581            }
4582        }
4583        return count;
4584    }
4585}