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