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