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    }