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