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