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 }