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