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