    001    package net.minecraft.client.multiplayer;
    002
    003    import cpw.mods.fml.relauncher.Side;
    004    import cpw.mods.fml.relauncher.SideOnly;
    005    import java.util.HashSet;
    006    import java.util.Iterator;
    007    import java.util.Random;
    008    import java.util.Set;
    009    import net.minecraft.block.Block;
    010    import net.minecraft.client.Minecraft;
    011    import net.minecraft.client.particle.EntityFireworkStarterFX;
    012    import net.minecraft.crash.CrashReport;
    013    import net.minecraft.crash.CrashReportCategory;
    014    import net.minecraft.entity.Entity;
    015    import net.minecraft.entity.item.EntityMinecart;
    016    import net.minecraft.entity.item.SoundUpdaterMinecart;
    017    import net.minecraft.nbt.NBTTagCompound;
    018    import net.minecraft.network.packet.Packet255KickDisconnect;
    019    import net.minecraft.profiler.Profiler;
    020    import net.minecraft.server.gui.IUpdatePlayerListBox;
    021    import net.minecraft.util.IntHashMap;
    022    import net.minecraft.world.ChunkCoordIntPair;
    023    import net.minecraft.world.World;
    024    import net.minecraft.world.WorldProvider;
    025    import net.minecraft.world.WorldSettings;
    026    import net.minecraft.world.chunk.Chunk;
    027    import net.minecraft.world.chunk.IChunkProvider;
    028    import net.minecraft.world.storage.SaveHandlerMP;
    029
    030    import net.minecraftforge.common.MinecraftForge;
    031    import net.minecraftforge.event.world.WorldEvent;
    032
    033    @SideOnly(Side.CLIENT)
    034    public class WorldClient extends World
    035    {
    036        /** The packets that need to be sent to the server. */
    037        private NetClientHandler sendQueue;
    038
    039        /** The ChunkProviderClient instance */
    040        private ChunkProviderClient clientChunkProvider;
    041
    042        /**
    043         * The hash set of entities handled by this client. Uses the entity's ID as the hash set's key.
    044         */
    045        private IntHashMap entityHashSet = new IntHashMap();
    046
    047        /** Contains all entities for this client, both spawned and non-spawned. */
    048        private Set entityList = new HashSet();
    049
    050        /**
    051         * Contains all entities for this client that were not spawned due to a non-present chunk. The game will attempt to
    052         * spawn up to 10 pending entities with each subsequent tick until the spawn queue is empty.
    053         */
    054        private Set entitySpawnQueue = new HashSet();
    055        private final Minecraft mc = Minecraft.getMinecraft();
    056        private final Set previousActiveChunkSet = new HashSet();
    057
    058        public WorldClient(NetClientHandler par1NetClientHandler, WorldSettings par2WorldSettings, int par3, int par4, Profiler par5Profiler)
    059        {
    060            super(new SaveHandlerMP(), "MpServer", WorldProvider.getProviderForDimension(par3), par2WorldSettings, par5Profiler);
    061            this.sendQueue = par1NetClientHandler;
    062            this.difficultySetting = par4;
    063            this.mapStorage = par1NetClientHandler.mapStorage;
    064            this.isRemote = true;
    065            finishSetup();
    066            this.setSpawnLocation(8, 64, 8);
    067            MinecraftForge.EVENT_BUS.post(new WorldEvent.Load(this));
    068        }
    069
    070        /**
    071         * Runs a single tick for the world
    072         */
    073        public void tick()
    074        {
    075            super.tick();
    076            this.func_82738_a(this.getTotalWorldTime() + 1L);
    077            this.setWorldTime(this.getWorldTime() + 1L);
    078            this.theProfiler.startSection("reEntryProcessing");
    079
    080            for (int var1 = 0; var1 < 10 && !this.entitySpawnQueue.isEmpty(); ++var1)
    081            {
    082                Entity var2 = (Entity)this.entitySpawnQueue.iterator().next();
    083                this.entitySpawnQueue.remove(var2);
    084
    085                if (!this.loadedEntityList.contains(var2))
    086                {
    087                    this.spawnEntityInWorld(var2);
    088                }
    089            }
    090
    091            this.theProfiler.endStartSection("connection");
    092            this.sendQueue.processReadPackets();
    093            this.theProfiler.endStartSection("chunkCache");
    094            this.clientChunkProvider.unload100OldestChunks();
    095            this.theProfiler.endStartSection("tiles");
    096            this.tickBlocksAndAmbiance();
    097            this.theProfiler.endSection();
    098        }
    099
    100        /**
    101         * Invalidates an AABB region of blocks from the receive queue, in the event that the block has been modified
    102         * client-side in the intervening 80 receive ticks.
    103         */
    104        public void invalidateBlockReceiveRegion(int par1, int par2, int par3, int par4, int par5, int par6) {}
    105
    106        /**
    107         * Creates the chunk provider for this world. Called in the constructor. Retrieves provider from worldProvider?
    108         */
    109        protected IChunkProvider createChunkProvider()
    110        {
    111            this.clientChunkProvider = new ChunkProviderClient(this);
    112            return this.clientChunkProvider;
    113        }
    114
    115        /**
    116         * plays random cave ambient sounds and runs updateTick on random blocks within each chunk in the vacinity of a
    117         * player
    118         */
    119        protected void tickBlocksAndAmbiance()
    120        {
    121            super.tickBlocksAndAmbiance();
    122            this.previousActiveChunkSet.retainAll(this.activeChunkSet);
    123
    124            if (this.previousActiveChunkSet.size() == this.activeChunkSet.size())
    125            {
    126                this.previousActiveChunkSet.clear();
    127            }
    128
    129            int var1 = 0;
    130            Iterator var2 = this.activeChunkSet.iterator();
    131
    132            while (var2.hasNext())
    133            {
    134                ChunkCoordIntPair var3 = (ChunkCoordIntPair)var2.next();
    135
    136                if (!this.previousActiveChunkSet.contains(var3))
    137                {
    138                    int var4 = var3.chunkXPos * 16;
    139                    int var5 = var3.chunkZPos * 16;
    140                    this.theProfiler.startSection("getChunk");
    141                    Chunk var6 = this.getChunkFromChunkCoords(var3.chunkXPos, var3.chunkZPos);
    142                    this.moodSoundAndLightCheck(var4, var5, var6);
    143                    this.theProfiler.endSection();
    144                    this.previousActiveChunkSet.add(var3);
    145                    ++var1;
    146
    147                    if (var1 >= 10)
    148                    {
    149                        return;
    150                    }
    151                }
    152            }
    153        }
    154
    155        public void doPreChunk(int par1, int par2, boolean par3)
    156        {
    157            if (par3)
    158            {
    159                this.clientChunkProvider.loadChunk(par1, par2);
    160            }
    161            else
    162            {
    163                this.clientChunkProvider.unloadChunk(par1, par2);
    164            }
    165
    166            if (!par3)
    167            {
    168                this.markBlockRangeForRenderUpdate(par1 * 16, 0, par2 * 16, par1 * 16 + 15, 256, par2 * 16 + 15);
    169            }
    170        }
    171
    172        /**
    173         * Called to place all entities as part of a world
    174         */
    175        public boolean spawnEntityInWorld(Entity par1Entity)
    176        {
    177            boolean var2 = super.spawnEntityInWorld(par1Entity);
    178            this.entityList.add(par1Entity);
    179
    180            if (!var2)
    181            {
    182                this.entitySpawnQueue.add(par1Entity);
    183            }
    184
    185            return var2;
    186        }
    187
    188        /**
    189         * Dismounts the entity (and anything riding the entity), sets the dead flag, and removes the player entity from the
    190         * player entity list. Called by the playerLoggedOut function.
    191         */
    192        public void setEntityDead(Entity par1Entity)
    193        {
    194            super.setEntityDead(par1Entity);
    195            this.entityList.remove(par1Entity);
    196        }
    197
    198        /**
    199         * Start the skin for this entity downloading, if necessary, and increment its reference counter
    200         */
    201        protected void obtainEntitySkin(Entity par1Entity)
    202        {
    203            super.obtainEntitySkin(par1Entity);
    204
    205            if (this.entitySpawnQueue.contains(par1Entity))
    206            {
    207                this.entitySpawnQueue.remove(par1Entity);
    208            }
    209        }
    210
    211        /**
    212         * Decrement the reference counter for this entity's skin image data
    213         */
    214        public void releaseEntitySkin(Entity par1Entity)
    215        {
    216            super.releaseEntitySkin(par1Entity);
    217
    218            if (this.entityList.contains(par1Entity))
    219            {
    220                if (par1Entity.isEntityAlive())
    221                {
    222                    this.entitySpawnQueue.add(par1Entity);
    223                }
    224                else
    225                {
    226                    this.entityList.remove(par1Entity);
    227                }
    228            }
    229        }
    230
    231        /**
    232         * Add an ID to Entity mapping to entityHashSet
    233         */
    234        public void addEntityToWorld(int par1, Entity par2Entity)
    235        {
    236            Entity var3 = this.getEntityByID(par1);
    237
    238            if (var3 != null)
    239            {
    240                this.setEntityDead(var3);
    241            }
    242
    243            this.entityList.add(par2Entity);
    244            par2Entity.entityId = par1;
    245
    246            if (!this.spawnEntityInWorld(par2Entity))
    247            {
    248                this.entitySpawnQueue.add(par2Entity);
    249            }
    250
    251            this.entityHashSet.addKey(par1, par2Entity);
    252        }
    253
    254        /**
    255         * Returns the Entity with the given ID, or null if it doesn't exist in this World.
    256         */
    257        public Entity getEntityByID(int par1)
    258        {
    259            return (Entity)(par1 == this.mc.thePlayer.entityId ? this.mc.thePlayer : (Entity)this.entityHashSet.lookup(par1));
    260        }
    261
    262        public Entity removeEntityFromWorld(int par1)
    263        {
    264            Entity var2 = (Entity)this.entityHashSet.removeObject(par1);
    265
    266            if (var2 != null)
    267            {
    268                this.entityList.remove(var2);
    269                this.setEntityDead(var2);
    270            }
    271
    272            return var2;
    273        }
    274
    275        public boolean setBlockAndMetadataAndInvalidate(int par1, int par2, int par3, int par4, int par5)
    276        {
    277            this.invalidateBlockReceiveRegion(par1, par2, par3, par1, par2, par3);
    278            return super.setBlockAndMetadataWithNotify(par1, par2, par3, par4, par5);
    279        }
    280
    281        /**
    282         * If on MP, sends a quitting packet.
    283         */
    284        public void sendQuittingDisconnectingPacket()
    285        {
    286            this.sendQueue.quitWithPacket(new Packet255KickDisconnect("Quitting"));
    287        }
    288
    289        public IUpdatePlayerListBox func_82735_a(EntityMinecart par1EntityMinecart)
    290        {
    291            return new SoundUpdaterMinecart(this.mc.sndManager, par1EntityMinecart, this.mc.thePlayer);
    292        }
    293
    294        /**
    295         * Updates all weather states.
    296         */
    297        protected void updateWeather()
    298        {
    299            super.updateWeather();
    300        }
    301
    302        @Override
    303        public void updateWeatherBody()
    304        {
    305            if (!this.provider.hasNoSky)
    306            {
    307                this.prevRainingStrength = this.rainingStrength;
    308
    309                if (this.worldInfo.isRaining())
    310                {
    311                    this.rainingStrength = (float)((double)this.rainingStrength + 0.01D);
    312                }
    313                else
    314                {
    315                    this.rainingStrength = (float)((double)this.rainingStrength - 0.01D);
    316                }
    317
    318                if (this.rainingStrength < 0.0F)
    319                {
    320                    this.rainingStrength = 0.0F;
    321                }
    322
    323                if (this.rainingStrength > 1.0F)
    324                {
    325                    this.rainingStrength = 1.0F;
    326                }
    327
    328                this.prevThunderingStrength = this.thunderingStrength;
    329
    330                if (this.worldInfo.isThundering())
    331                {
    332                    this.thunderingStrength = (float)((double)this.thunderingStrength + 0.01D);
    333                }
    334                else
    335                {
    336                    this.thunderingStrength = (float)((double)this.thunderingStrength - 0.01D);
    337                }
    338
    339                if (this.thunderingStrength < 0.0F)
    340                {
    341                    this.thunderingStrength = 0.0F;
    342                }
    343
    344                if (this.thunderingStrength > 1.0F)
    345                {
    346                    this.thunderingStrength = 1.0F;
    347                }
    348            }
    349        }
    350
    351        public void func_73029_E(int par1, int par2, int par3)
    352        {
    353            byte var4 = 16;
    354            Random var5 = new Random();
    355
    356            for (int var6 = 0; var6 < 1000; ++var6)
    357            {
    358                int var7 = par1 + this.rand.nextInt(var4) - this.rand.nextInt(var4);
    359                int var8 = par2 + this.rand.nextInt(var4) - this.rand.nextInt(var4);
    360                int var9 = par3 + this.rand.nextInt(var4) - this.rand.nextInt(var4);
    361                int var10 = this.getBlockId(var7, var8, var9);
    362
    363                if (var10 == 0 && this.rand.nextInt(8) > var8 && this.provider.getWorldHasVoidParticles())
    364                {
    365                    this.spawnParticle("depthsuspend", (double)((float)var7 + this.rand.nextFloat()), (double)((float)var8 + this.rand.nextFloat()), (double)((float)var9 + this.rand.nextFloat()), 0.0D, 0.0D, 0.0D);
    366                }
    367                else if (var10 > 0)
    368                {
    369                    Block.blocksList[var10].randomDisplayTick(this, var7, var8, var9, var5);
    370                }
    371            }
    372        }
    373
    374        /**
    375         * also releases skins.
    376         */
    377        public void removeAllEntities()
    378        {
    379            this.loadedEntityList.removeAll(this.unloadedEntityList);
    380            int var1;
    381            Entity var2;
    382            int var3;
    383            int var4;
    384
    385            for (var1 = 0; var1 < this.unloadedEntityList.size(); ++var1)
    386            {
    387                var2 = (Entity)this.unloadedEntityList.get(var1);
    388                var3 = var2.chunkCoordX;
    389                var4 = var2.chunkCoordZ;
    390
    391                if (var2.addedToChunk && this.chunkExists(var3, var4))
    392                {
    393                    this.getChunkFromChunkCoords(var3, var4).removeEntity(var2);
    394                }
    395            }
    396
    397            for (var1 = 0; var1 < this.unloadedEntityList.size(); ++var1)
    398            {
    399                this.releaseEntitySkin((Entity)this.unloadedEntityList.get(var1));
    400            }
    401
    402            this.unloadedEntityList.clear();
    403
    404            for (var1 = 0; var1 < this.loadedEntityList.size(); ++var1)
    405            {
    406                var2 = (Entity)this.loadedEntityList.get(var1);
    407
    408                if (var2.ridingEntity != null)
    409                {
    410                    if (!var2.ridingEntity.isDead && var2.ridingEntity.riddenByEntity == var2)
    411                    {
    412                        continue;
    413                    }
    414
    415                    var2.ridingEntity.riddenByEntity = null;
    416                    var2.ridingEntity = null;
    417                }
    418
    419                if (var2.isDead)
    420                {
    421                    var3 = var2.chunkCoordX;
    422                    var4 = var2.chunkCoordZ;
    423
    424                    if (var2.addedToChunk && this.chunkExists(var3, var4))
    425                    {
    426                        this.getChunkFromChunkCoords(var3, var4).removeEntity(var2);
    427                    }
    428
    429                    this.loadedEntityList.remove(var1--);
    430                    this.releaseEntitySkin(var2);
    431                }
    432            }
    433        }
    434
    435        /**
    436         * Adds some basic stats of the world to the given crash report.
    437         */
    438        public CrashReportCategory addWorldInfoToCrashReport(CrashReport par1CrashReport)
    439        {
    440            CrashReportCategory var2 = super.addWorldInfoToCrashReport(par1CrashReport);
    441            var2.addCrashSectionCallable("Forced entities", new CallableMPL1(this));
    442            var2.addCrashSectionCallable("Retry entities", new CallableMPL2(this));
    443            return var2;
    444        }
    445
    446        /**
    447         * par8 is loudness, all pars passed to minecraftInstance.sndManager.playSound
    448         */
    449        public void playSound(double par1, double par3, double par5, String par7Str, float par8, float par9, boolean par10)
    450        {
    451            float var11 = 16.0F;
    452
    453            if (par8 > 1.0F)
    454            {
    455                var11 *= par8;
    456            }
    457
    458            double var12 = this.mc.renderViewEntity.getDistanceSq(par1, par3, par5);
    459
    460            if (var12 < (double)(var11 * var11))
    461            {
    462                if (par10 && var12 > 100.0D)
    463                {
    464                    double var14 = Math.sqrt(var12) / 40.0D;
    465                    this.mc.sndManager.func_92070_a(par7Str, (float)par1, (float)par3, (float)par5, par8, par9, (int)Math.round(var14 * 20.0D));
    466                }
    467                else
    468                {
    469                    this.mc.sndManager.playSound(par7Str, (float)par1, (float)par3, (float)par5, par8, par9);
    470                }
    471            }
    472        }
    473
    474        public void func_92088_a(double par1, double par3, double par5, double par7, double par9, double par11, NBTTagCompound par13NBTTagCompound)
    475        {
    476            this.mc.effectRenderer.addEffect(new EntityFireworkStarterFX(this, par1, par3, par5, par7, par9, par11, this.mc.effectRenderer, par13NBTTagCompound));
    477        }
    478
    479        static Set getEntityList(WorldClient par0WorldClient)
    480        {
    481            return par0WorldClient.entityList;
    482        }
    483
    484        static Set getEntitySpawnQueue(WorldClient par0WorldClient)
    485        {
    486            return par0WorldClient.entitySpawnQueue;
    487        }
    488    }