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 CrashReportCategory addWorldInfoToCrashReport(CrashReport par1CrashReport)
424        {
425            CrashReportCategory var2 = super.addWorldInfoToCrashReport(par1CrashReport);
426            var2.addCrashSectionCallable("Forced entities", new CallableMPL1(this));
427            var2.addCrashSectionCallable("Retry entities", new CallableMPL2(this));
428            return var2;
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    }