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