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