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