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 }