001 package net.minecraft.src;
002
003 import cpw.mods.fml.common.Side;
004 import cpw.mods.fml.common.asm.SideOnly;
005
006 import java.io.File;
007 import java.util.ArrayList;
008 import java.util.HashSet;
009 import java.util.Iterator;
010 import java.util.List;
011 import java.util.Random;
012 import java.util.Set;
013 import java.util.TreeSet;
014 import net.minecraft.server.MinecraftServer;
015 import net.minecraftforge.common.ChestGenHooks;
016 import static net.minecraftforge.common.ChestGenHooks.*;
017 import net.minecraftforge.common.DimensionManager;
018 import net.minecraftforge.common.MinecraftForge;
019 import net.minecraftforge.event.world.WorldEvent;
020
021 public class WorldServer extends World
022 {
023 private final MinecraftServer mcServer;
024 private final EntityTracker theEntityTracker;
025 private final PlayerManager thePlayerManager;
026 private Set field_73064_N;
027
028 /** All work to do in future ticks. */
029 private TreeSet pendingTickListEntries;
030 public ChunkProviderServer theChunkProviderServer;
031
032 /** set by CommandServerSave{all,Off,On} */
033 public boolean canNotSave;
034
035 /** is false if there are no players */
036 private boolean allPlayersSleeping;
037 private int updateEntityTick = 0;
038
039 /**
040 * Double buffer of ServerBlockEventList[] for holding pending BlockEventData's
041 */
042 private ServerBlockEventList[] blockEventCache = new ServerBlockEventList[] {new ServerBlockEventList((ServerBlockEvent)null), new ServerBlockEventList((ServerBlockEvent)null)};
043
044 /**
045 * The index into the blockEventCache; either 0, or 1, toggled in sendBlockEventPackets where all BlockEvent are
046 * applied locally and send to clients.
047 */
048 private int blockEventCacheIndex = 0;
049 public static final WeightedRandomChestContent[] bonusChestContent = new WeightedRandomChestContent[] {new WeightedRandomChestContent(Item.stick.shiftedIndex, 0, 1, 3, 10), new WeightedRandomChestContent(Block.planks.blockID, 0, 1, 3, 10), new WeightedRandomChestContent(Block.wood.blockID, 0, 1, 3, 10), new WeightedRandomChestContent(Item.axeStone.shiftedIndex, 0, 1, 1, 3), new WeightedRandomChestContent(Item.axeWood.shiftedIndex, 0, 1, 1, 5), new WeightedRandomChestContent(Item.pickaxeStone.shiftedIndex, 0, 1, 1, 3), new WeightedRandomChestContent(Item.pickaxeWood.shiftedIndex, 0, 1, 1, 5), new WeightedRandomChestContent(Item.appleRed.shiftedIndex, 0, 2, 3, 5), new WeightedRandomChestContent(Item.bread.shiftedIndex, 0, 2, 3, 3)};
050
051 /** An IntHashMap of entity IDs (integers) to their Entity objects. */
052 private IntHashMap entityIdMap;
053
054 /** Stores the recently processed (lighting) chunks */
055 protected Set<ChunkCoordIntPair> doneChunks = new HashSet<ChunkCoordIntPair>();
056
057 public WorldServer(MinecraftServer par1MinecraftServer, ISaveHandler par2ISaveHandler, String par3Str, int par4, WorldSettings par5WorldSettings, Profiler par6Profiler)
058 {
059 super(par2ISaveHandler, par3Str, par5WorldSettings, WorldProvider.getProviderForDimension(par4), par6Profiler);
060 this.mcServer = par1MinecraftServer;
061 this.theEntityTracker = new EntityTracker(this);
062 this.thePlayerManager = new PlayerManager(this, par1MinecraftServer.getConfigurationManager().getViewDistance());
063
064 if (this.entityIdMap == null)
065 {
066 this.entityIdMap = new IntHashMap();
067 }
068
069 if (this.field_73064_N == null)
070 {
071 this.field_73064_N = new HashSet();
072 }
073
074 if (this.pendingTickListEntries == null)
075 {
076 this.pendingTickListEntries = new TreeSet();
077 }
078 DimensionManager.setWorld(par4, this);
079 }
080
081 /**
082 * Runs a single tick for the world
083 */
084 public void tick()
085 {
086 super.tick();
087
088 if (this.getWorldInfo().isHardcoreModeEnabled() && this.difficultySetting < 3)
089 {
090 this.difficultySetting = 3;
091 }
092
093 this.provider.worldChunkMgr.cleanupCache();
094
095 if (this.areAllPlayersAsleep())
096 {
097 boolean var1 = false;
098
099 if (this.spawnHostileMobs && this.difficultySetting >= 1)
100 {
101 ;
102 }
103
104 if (!var1)
105 {
106 long var2 = this.worldInfo.getWorldTime() + 24000L;
107 this.worldInfo.setWorldTime(var2 - var2 % 24000L);
108 this.wakeAllPlayers();
109 }
110 }
111
112 this.theProfiler.startSection("mobSpawner");
113
114 if (this.func_82736_K().func_82766_b("doMobSpawning"))
115 {
116 SpawnerAnimals.findChunksForSpawning(this, this.spawnHostileMobs, this.spawnPeacefulMobs, this.worldInfo.func_82573_f() % 400L == 0L);
117 }
118
119 this.theProfiler.endStartSection("chunkSource");
120 this.chunkProvider.unload100OldestChunks();
121 int var4 = this.calculateSkylightSubtracted(1.0F);
122
123 if (var4 != this.skylightSubtracted)
124 {
125 this.skylightSubtracted = var4;
126 }
127
128 this.sendAndApplyBlockEvents();
129 this.worldInfo.func_82572_b(this.worldInfo.func_82573_f() + 1L);
130 this.worldInfo.setWorldTime(this.worldInfo.getWorldTime() + 1L);
131 this.theProfiler.endStartSection("tickPending");
132 this.tickUpdates(false);
133 this.theProfiler.endStartSection("tickTiles");
134 this.tickBlocksAndAmbiance();
135 this.theProfiler.endStartSection("chunkMap");
136 this.thePlayerManager.updatePlayerInstances();
137 this.theProfiler.endStartSection("village");
138 this.villageCollectionObj.tick();
139 this.villageSiegeObj.tick();
140 this.theProfiler.endSection();
141 this.sendAndApplyBlockEvents();
142 }
143
144 /**
145 * only spawns creatures allowed by the chunkProvider
146 */
147 public SpawnListEntry spawnRandomCreature(EnumCreatureType par1EnumCreatureType, int par2, int par3, int par4)
148 {
149 List var5 = this.getChunkProvider().getPossibleCreatures(par1EnumCreatureType, par2, par3, par4);
150 return var5 != null && !var5.isEmpty() ? (SpawnListEntry)WeightedRandom.getRandomItem(this.rand, var5) : null;
151 }
152
153 /**
154 * Updates the flag that indicates whether or not all players in the world are sleeping.
155 */
156 public void updateAllPlayersSleepingFlag()
157 {
158 this.allPlayersSleeping = !this.playerEntities.isEmpty();
159 Iterator var1 = this.playerEntities.iterator();
160
161 while (var1.hasNext())
162 {
163 EntityPlayer var2 = (EntityPlayer)var1.next();
164
165 if (!var2.isPlayerSleeping())
166 {
167 this.allPlayersSleeping = false;
168 break;
169 }
170 }
171 }
172
173 protected void wakeAllPlayers()
174 {
175 this.allPlayersSleeping = false;
176 Iterator var1 = this.playerEntities.iterator();
177
178 while (var1.hasNext())
179 {
180 EntityPlayer var2 = (EntityPlayer)var1.next();
181
182 if (var2.isPlayerSleeping())
183 {
184 var2.wakeUpPlayer(false, false, true);
185 }
186 }
187
188 this.resetRainAndThunder();
189 }
190
191 private void resetRainAndThunder()
192 {
193 provider.resetRainAndThunder();
194 }
195
196 public boolean areAllPlayersAsleep()
197 {
198 if (this.allPlayersSleeping && !this.isRemote)
199 {
200 Iterator var1 = this.playerEntities.iterator();
201 EntityPlayer var2;
202
203 do
204 {
205 if (!var1.hasNext())
206 {
207 return true;
208 }
209
210 var2 = (EntityPlayer)var1.next();
211 }
212 while (var2.isPlayerFullyAsleep());
213
214 return false;
215 }
216 else
217 {
218 return false;
219 }
220 }
221
222 @SideOnly(Side.CLIENT)
223
224 /**
225 * Sets a new spawn location by finding an uncovered block at a random (x,z) location in the chunk.
226 */
227 public void setSpawnLocation()
228 {
229 if (this.worldInfo.getSpawnY() <= 0)
230 {
231 this.worldInfo.setSpawnY(64);
232 }
233
234 int var1 = this.worldInfo.getSpawnX();
235 int var2 = this.worldInfo.getSpawnZ();
236 int var3 = 0;
237
238 while (this.getFirstUncoveredBlock(var1, var2) == 0)
239 {
240 var1 += this.rand.nextInt(8) - this.rand.nextInt(8);
241 var2 += this.rand.nextInt(8) - this.rand.nextInt(8);
242 ++var3;
243
244 if (var3 == 10000)
245 {
246 break;
247 }
248 }
249
250 this.worldInfo.setSpawnX(var1);
251 this.worldInfo.setSpawnZ(var2);
252 }
253
254 /**
255 * plays random cave ambient sounds and runs updateTick on random blocks within each chunk in the vacinity of a
256 * player
257 */
258 protected void tickBlocksAndAmbiance()
259 {
260 super.tickBlocksAndAmbiance();
261 int var1 = 0;
262 int var2 = 0;
263 Iterator var3 = this.activeChunkSet.iterator();
264
265 doneChunks.retainAll(activeChunkSet);
266 if (doneChunks.size() == activeChunkSet.size())
267 {
268 doneChunks.clear();
269 }
270
271 final long time = -System.currentTimeMillis();
272
273 while (var3.hasNext())
274 {
275 ChunkCoordIntPair var4 = (ChunkCoordIntPair)var3.next();
276 int var5 = var4.chunkXPos * 16;
277 int var6 = var4.chunkZPos * 16;
278 this.theProfiler.startSection("getChunk");
279 Chunk var7 = this.getChunkFromChunkCoords(var4.chunkXPos, var4.chunkZPos);
280 this.moodSoundAndLightCheck(var5, var6, var7);
281 this.theProfiler.endStartSection("tickChunk");
282 if (System.currentTimeMillis() + time <= 4 && doneChunks.add(var4)) { //Limits and evenly distributes the lighting update time
283 var7.updateSkylight();
284 }
285 this.theProfiler.endStartSection("thunder");
286 int var8;
287 int var9;
288 int var10;
289 int var11;
290
291 if (provider.canDoLightning(var7) && this.rand.nextInt(100000) == 0 && this.isRaining() && this.isThundering())
292 {
293 this.updateLCG = this.updateLCG * 3 + 1013904223;
294 var8 = this.updateLCG >> 2;
295 var9 = var5 + (var8 & 15);
296 var10 = var6 + (var8 >> 8 & 15);
297 var11 = this.getPrecipitationHeight(var9, var10);
298
299 if (this.canLightningStrikeAt(var9, var11, var10))
300 {
301 this.addWeatherEffect(new EntityLightningBolt(this, (double)var9, (double)var11, (double)var10));
302 this.lastLightningBolt = 2;
303 }
304 }
305
306 this.theProfiler.endStartSection("iceandsnow");
307 int var13;
308
309 if (provider.canDoRainSnowIce(var7) && this.rand.nextInt(16) == 0)
310 {
311 this.updateLCG = this.updateLCG * 3 + 1013904223;
312 var8 = this.updateLCG >> 2;
313 var9 = var8 & 15;
314 var10 = var8 >> 8 & 15;
315 var11 = this.getPrecipitationHeight(var9 + var5, var10 + var6);
316
317 if (this.isBlockFreezableNaturally(var9 + var5, var11 - 1, var10 + var6))
318 {
319 this.setBlockWithNotify(var9 + var5, var11 - 1, var10 + var6, Block.ice.blockID);
320 }
321
322 if (this.isRaining() && this.canSnowAt(var9 + var5, var11, var10 + var6))
323 {
324 this.setBlockWithNotify(var9 + var5, var11, var10 + var6, Block.snow.blockID);
325 }
326
327 if (this.isRaining())
328 {
329 BiomeGenBase var12 = this.getBiomeGenForCoords(var9 + var5, var10 + var6);
330
331 if (var12.canSpawnLightningBolt())
332 {
333 var13 = this.getBlockId(var9 + var5, var11 - 1, var10 + var6);
334
335 if (var13 != 0)
336 {
337 Block.blocksList[var13].fillWithRain(this, var9 + var5, var11 - 1, var10 + var6);
338 }
339 }
340 }
341 }
342
343 this.theProfiler.endStartSection("tickTiles");
344 ExtendedBlockStorage[] var19 = var7.getBlockStorageArray();
345 var9 = var19.length;
346
347 for (var10 = 0; var10 < var9; ++var10)
348 {
349 ExtendedBlockStorage var21 = var19[var10];
350
351 if (var21 != null && var21.getNeedsRandomTick())
352 {
353 for (int var20 = 0; var20 < 3; ++var20)
354 {
355 this.updateLCG = this.updateLCG * 3 + 1013904223;
356 var13 = this.updateLCG >> 2;
357 int var14 = var13 & 15;
358 int var15 = var13 >> 8 & 15;
359 int var16 = var13 >> 16 & 15;
360 int var17 = var21.getExtBlockID(var14, var16, var15);
361 ++var2;
362 Block var18 = Block.blocksList[var17];
363
364 if (var18 != null && var18.getTickRandomly())
365 {
366 ++var1;
367 var18.updateTick(this, var14 + var5, var16 + var21.getYLocation(), var15 + var6, this.rand);
368 }
369 }
370 }
371 }
372
373 this.theProfiler.endSection();
374 }
375 }
376
377 /**
378 * Schedules a tick to a block with a delay (Most commonly the tick rate)
379 */
380 public void scheduleBlockUpdate(int par1, int par2, int par3, int par4, int par5)
381 {
382 this.func_82740_a(par1, par2, par3, par4, par5, 0);
383 }
384
385 public void func_82740_a(int par1, int par2, int par3, int par4, int par5, int par6)
386 {
387 NextTickListEntry var7 = new NextTickListEntry(par1, par2, par3, par4);
388 boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(var7.xCoord >> 4, var7.zCoord >> 4));
389 byte var8 = isForced ? (byte)0 : 8;
390
391 if (this.scheduledUpdatesAreImmediate && par4 > 0)
392 {
393 if (Block.blocksList[par4].func_82506_l())
394 {
395 if (this.checkChunksExist(var7.xCoord - var8, var7.yCoord - var8, var7.zCoord - var8, var7.xCoord + var8, var7.yCoord + var8, var7.zCoord + var8))
396 {
397 int var9 = this.getBlockId(var7.xCoord, var7.yCoord, var7.zCoord);
398
399 if (var9 == var7.blockID && var9 > 0)
400 {
401 Block.blocksList[var9].updateTick(this, var7.xCoord, var7.yCoord, var7.zCoord, this.rand);
402 }
403 }
404
405 return;
406 }
407
408 par5 = 1;
409 }
410
411 if (this.checkChunksExist(par1 - var8, par2 - var8, par3 - var8, par1 + var8, par2 + var8, par3 + var8))
412 {
413 if (par4 > 0)
414 {
415 var7.setScheduledTime((long)par5 + this.worldInfo.func_82573_f());
416 var7.func_82753_a(par6);
417 }
418
419 if (!this.field_73064_N.contains(var7))
420 {
421 this.field_73064_N.add(var7);
422 this.pendingTickListEntries.add(var7);
423 }
424 }
425 }
426
427 /**
428 * Schedules a block update from the saved information in a chunk. Called when the chunk is loaded.
429 */
430 public void scheduleBlockUpdateFromLoad(int par1, int par2, int par3, int par4, int par5)
431 {
432 NextTickListEntry var6 = new NextTickListEntry(par1, par2, par3, par4);
433
434 if (par4 > 0)
435 {
436 var6.setScheduledTime((long)par5 + this.worldInfo.func_82573_f());
437 }
438
439 if (!this.field_73064_N.contains(var6))
440 {
441 this.field_73064_N.add(var6);
442 this.pendingTickListEntries.add(var6);
443 }
444 }
445
446 /**
447 * Updates (and cleans up) entities and tile entities
448 */
449 public void updateEntities()
450 {
451 if (this.playerEntities.isEmpty() && getPersistentChunks().isEmpty())
452 {
453 if (this.updateEntityTick++ >= 1200)
454 {
455 return;
456 }
457 }
458 else
459 {
460 this.func_82742_i();
461 }
462
463 super.updateEntities();
464 }
465
466 public void func_82742_i()
467 {
468 this.updateEntityTick = 0;
469 }
470
471 /**
472 * Runs through the list of updates to run and ticks them
473 */
474 public boolean tickUpdates(boolean par1)
475 {
476 int var2 = this.pendingTickListEntries.size();
477
478 if (var2 != this.field_73064_N.size())
479 {
480 throw new IllegalStateException("TickNextTick list out of synch");
481 }
482 else
483 {
484 if (var2 > 1000)
485 {
486 var2 = 1000;
487 }
488
489 for (int var3 = 0; var3 < var2; ++var3)
490 {
491 NextTickListEntry var4 = (NextTickListEntry)this.pendingTickListEntries.first();
492
493 if (!par1 && var4.scheduledTime > this.worldInfo.func_82573_f())
494 {
495 break;
496 }
497
498 this.pendingTickListEntries.remove(var4);
499 this.field_73064_N.remove(var4);
500 boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(var4.xCoord >> 4, var4.zCoord >> 4));
501 byte var5 = isForced ? (byte)0 : 8;
502
503 if (this.checkChunksExist(var4.xCoord - var5, var4.yCoord - var5, var4.zCoord - var5, var4.xCoord + var5, var4.yCoord + var5, var4.zCoord + var5))
504 {
505 int var6 = this.getBlockId(var4.xCoord, var4.yCoord, var4.zCoord);
506
507 if (var6 == var4.blockID && var6 > 0)
508 {
509 Block.blocksList[var6].updateTick(this, var4.xCoord, var4.yCoord, var4.zCoord, this.rand);
510 }
511 }
512 }
513
514 return !this.pendingTickListEntries.isEmpty();
515 }
516 }
517
518 public List getPendingBlockUpdates(Chunk par1Chunk, boolean par2)
519 {
520 ArrayList var3 = null;
521 ChunkCoordIntPair var4 = par1Chunk.getChunkCoordIntPair();
522 int var5 = var4.chunkXPos << 4;
523 int var6 = var5 + 16;
524 int var7 = var4.chunkZPos << 4;
525 int var8 = var7 + 16;
526 Iterator var9 = this.pendingTickListEntries.iterator();
527
528 while (var9.hasNext())
529 {
530 NextTickListEntry var10 = (NextTickListEntry)var9.next();
531
532 if (var10.xCoord >= var5 && var10.xCoord < var6 && var10.zCoord >= var7 && var10.zCoord < var8)
533 {
534 if (par2)
535 {
536 this.field_73064_N.remove(var10);
537 var9.remove();
538 }
539
540 if (var3 == null)
541 {
542 var3 = new ArrayList();
543 }
544
545 var3.add(var10);
546 }
547 }
548
549 return var3;
550 }
551
552 /**
553 * Will update the entity in the world if the chunk the entity is in is currently loaded or its forced to update.
554 * Args: entity, forceUpdate
555 */
556 public void updateEntityWithOptionalForce(Entity par1Entity, boolean par2)
557 {
558 if (!this.mcServer.getCanSpawnAnimals() && (par1Entity instanceof EntityAnimal || par1Entity instanceof EntityWaterMob))
559 {
560 par1Entity.setDead();
561 }
562
563 if (!this.mcServer.getCanSpawnNPCs() && par1Entity instanceof INpc)
564 {
565 par1Entity.setDead();
566 }
567
568 if (!(par1Entity.riddenByEntity instanceof EntityPlayer))
569 {
570 super.updateEntityWithOptionalForce(par1Entity, par2);
571 }
572 }
573
574 /**
575 * direct call to super.updateEntityWithOptionalForce
576 */
577 public void uncheckedUpdateEntity(Entity par1Entity, boolean par2)
578 {
579 super.updateEntityWithOptionalForce(par1Entity, par2);
580 }
581
582 /**
583 * Creates the chunk provider for this world. Called in the constructor. Retrieves provider from worldProvider?
584 */
585 protected IChunkProvider createChunkProvider()
586 {
587 IChunkLoader var1 = this.saveHandler.getChunkLoader(this.provider);
588 this.theChunkProviderServer = new ChunkProviderServer(this, var1, this.provider.getChunkProvider());
589 return this.theChunkProviderServer;
590 }
591
592 /**
593 * pars: min x,y,z , max x,y,z
594 */
595 public List getAllTileEntityInBox(int par1, int par2, int par3, int par4, int par5, int par6)
596 {
597 ArrayList var7 = new ArrayList();
598
599 for(int x = (par1 >> 4); x <= (par4 >> 4); x++)
600 {
601 for(int z = (par3 >> 4); z <= (par6 >> 4); z++)
602 {
603 Chunk chunk = getChunkFromChunkCoords(x, z);
604 if (chunk != null)
605 {
606 for(Object obj : chunk.chunkTileEntityMap.values())
607 {
608 TileEntity entity = (TileEntity)obj;
609 if (!entity.isInvalid())
610 {
611 if (entity.xCoord >= par1 && entity.yCoord >= par2 && entity.zCoord >= par3 &&
612 entity.xCoord <= par4 && entity.yCoord <= par5 && entity.zCoord <= par6)
613 {
614 var7.add(entity);
615 }
616 }
617 }
618 }
619 }
620 }
621
622 return var7;
623 }
624
625 /**
626 * Called when checking if a certain block can be mined or not. The 'spawn safe zone' check is located here.
627 */
628 public boolean canMineBlock(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
629 {
630 return super.canMineBlock(par1EntityPlayer, par2, par3, par4);
631 }
632
633 public boolean canMineBlockBody(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
634 {
635 int var5 = MathHelper.abs_int(par2 - this.worldInfo.getSpawnX());
636 int var6 = MathHelper.abs_int(par4 - this.worldInfo.getSpawnZ());
637
638 if (var5 > var6)
639 {
640 var6 = var5;
641 }
642
643 return var6 > mcServer.func_82357_ak() || this.mcServer.getConfigurationManager().areCommandsAllowed(par1EntityPlayer.username) || this.mcServer.isSinglePlayer();
644 }
645
646 protected void initialize(WorldSettings par1WorldSettings)
647 {
648 if (this.entityIdMap == null)
649 {
650 this.entityIdMap = new IntHashMap();
651 }
652
653 if (this.field_73064_N == null)
654 {
655 this.field_73064_N = new HashSet();
656 }
657
658 if (this.pendingTickListEntries == null)
659 {
660 this.pendingTickListEntries = new TreeSet();
661 }
662
663 this.createSpawnPosition(par1WorldSettings);
664 super.initialize(par1WorldSettings);
665 }
666
667 /**
668 * creates a spawn position at random within 256 blocks of 0,0
669 */
670 protected void createSpawnPosition(WorldSettings par1WorldSettings)
671 {
672 if (!this.provider.canRespawnHere())
673 {
674 this.worldInfo.setSpawnPosition(0, this.provider.getAverageGroundLevel(), 0);
675 }
676 else
677 {
678 this.findingSpawnPoint = true;
679 WorldChunkManager var2 = this.provider.worldChunkMgr;
680 List var3 = var2.getBiomesToSpawnIn();
681 Random var4 = new Random(this.getSeed());
682 ChunkPosition var5 = var2.findBiomePosition(0, 0, 256, var3, var4);
683 int var6 = 0;
684 int var7 = this.provider.getAverageGroundLevel();
685 int var8 = 0;
686
687 if (var5 != null)
688 {
689 var6 = var5.x;
690 var8 = var5.z;
691 }
692 else
693 {
694 System.out.println("Unable to find spawn biome");
695 }
696
697 int var9 = 0;
698
699 while (!this.provider.canCoordinateBeSpawn(var6, var8))
700 {
701 var6 += var4.nextInt(64) - var4.nextInt(64);
702 var8 += var4.nextInt(64) - var4.nextInt(64);
703 ++var9;
704
705 if (var9 == 1000)
706 {
707 break;
708 }
709 }
710
711 this.worldInfo.setSpawnPosition(var6, var7, var8);
712 this.findingSpawnPoint = false;
713
714 if (par1WorldSettings.isBonusChestEnabled())
715 {
716 this.createBonusChest();
717 }
718 }
719 }
720
721 /**
722 * Creates the bonus chest in the world.
723 */
724 protected void createBonusChest()
725 {
726 WorldGeneratorBonusChest var1 = new WorldGeneratorBonusChest(ChestGenHooks.getItems(BONUS_CHEST), ChestGenHooks.getCount(BONUS_CHEST, rand));
727
728 for (int var2 = 0; var2 < 10; ++var2)
729 {
730 int var3 = this.worldInfo.getSpawnX() + this.rand.nextInt(6) - this.rand.nextInt(6);
731 int var4 = this.worldInfo.getSpawnZ() + this.rand.nextInt(6) - this.rand.nextInt(6);
732 int var5 = this.getTopSolidOrLiquidBlock(var3, var4) + 1;
733
734 if (var1.generate(this, this.rand, var3, var5, var4))
735 {
736 break;
737 }
738 }
739 }
740
741 /**
742 * Gets the hard-coded portal location to use when entering this dimension.
743 */
744 public ChunkCoordinates getEntrancePortalLocation()
745 {
746 return this.provider.getEntrancePortalLocation();
747 }
748
749 /**
750 * Saves all chunks to disk while updating progress bar.
751 */
752 public void saveAllChunks(boolean par1, IProgressUpdate par2IProgressUpdate) throws MinecraftException
753 {
754 if (this.chunkProvider.canSave())
755 {
756 if (par2IProgressUpdate != null)
757 {
758 par2IProgressUpdate.displayProgressMessage("Saving level");
759 }
760
761 this.saveLevel();
762
763 if (par2IProgressUpdate != null)
764 {
765 par2IProgressUpdate.resetProgresAndWorkingMessage("Saving chunks");
766 }
767
768 this.chunkProvider.saveChunks(par1, par2IProgressUpdate);
769 MinecraftForge.EVENT_BUS.post(new WorldEvent.Save(this));
770 }
771 }
772
773 /**
774 * Saves the chunks to disk.
775 */
776 protected void saveLevel() throws MinecraftException
777 {
778 this.checkSessionLock();
779 this.saveHandler.saveWorldInfoWithPlayer(this.worldInfo, this.mcServer.getConfigurationManager().getTagsFromLastWrite());
780 this.mapStorage.saveAllData();
781 }
782
783 /**
784 * Start the skin for this entity downloading, if necessary, and increment its reference counter
785 */
786 protected void obtainEntitySkin(Entity par1Entity)
787 {
788 super.obtainEntitySkin(par1Entity);
789 this.entityIdMap.addKey(par1Entity.entityId, par1Entity);
790 Entity[] var2 = par1Entity.getParts();
791
792 if (var2 != null)
793 {
794 Entity[] var3 = var2;
795 int var4 = var2.length;
796
797 for (int var5 = 0; var5 < var4; ++var5)
798 {
799 Entity var6 = var3[var5];
800 this.entityIdMap.addKey(var6.entityId, var6);
801 }
802 }
803 }
804
805 /**
806 * Decrement the reference counter for this entity's skin image data
807 */
808 protected void releaseEntitySkin(Entity par1Entity)
809 {
810 super.releaseEntitySkin(par1Entity);
811 this.entityIdMap.removeObject(par1Entity.entityId);
812 Entity[] var2 = par1Entity.getParts();
813
814 if (var2 != null)
815 {
816 Entity[] var3 = var2;
817 int var4 = var2.length;
818
819 for (int var5 = 0; var5 < var4; ++var5)
820 {
821 Entity var6 = var3[var5];
822 this.entityIdMap.removeObject(var6.entityId);
823 }
824 }
825 }
826
827 /**
828 * Returns the Entity with the given ID, or null if it doesn't exist in this World.
829 */
830 public Entity getEntityByID(int par1)
831 {
832 return (Entity)this.entityIdMap.lookup(par1);
833 }
834
835 /**
836 * adds a lightning bolt to the list of lightning bolts in this world.
837 */
838 public boolean addWeatherEffect(Entity par1Entity)
839 {
840 if (super.addWeatherEffect(par1Entity))
841 {
842 this.mcServer.getConfigurationManager().sendToAllNear(par1Entity.posX, par1Entity.posY, par1Entity.posZ, 512.0D, this.provider.dimensionId, new Packet71Weather(par1Entity));
843 return true;
844 }
845 else
846 {
847 return false;
848 }
849 }
850
851 /**
852 * sends a Packet 38 (Entity Status) to all tracked players of that entity
853 */
854 public void setEntityState(Entity par1Entity, byte par2)
855 {
856 Packet38EntityStatus var3 = new Packet38EntityStatus(par1Entity.entityId, par2);
857 this.getEntityTracker().sendPacketToAllAssociatedPlayers(par1Entity, var3);
858 }
859
860 /**
861 * returns a new explosion. Does initiation (at time of writing Explosion is not finished)
862 */
863 public Explosion newExplosion(Entity par1Entity, double par2, double par4, double par6, float par8, boolean par9, boolean par10)
864 {
865 Explosion var11 = new Explosion(this, par1Entity, par2, par4, par6, par8);
866 var11.isFlaming = par9;
867 var11.field_82755_b = par10;
868 var11.doExplosionA();
869 var11.doExplosionB(false);
870
871 if (!par10)
872 {
873 var11.field_77281_g.clear();
874 }
875
876 Iterator var12 = this.playerEntities.iterator();
877
878 while (var12.hasNext())
879 {
880 EntityPlayer var13 = (EntityPlayer)var12.next();
881
882 if (var13.getDistanceSq(par2, par4, par6) < 4096.0D)
883 {
884 ((EntityPlayerMP)var13).playerNetServerHandler.sendPacketToPlayer(new Packet60Explosion(par2, par4, par6, par8, var11.field_77281_g, (Vec3)var11.func_77277_b().get(var13)));
885 }
886 }
887
888 return var11;
889 }
890
891 /**
892 * Adds a block event with the given Args to the blockEventCache. During the next tick(), the block specified will
893 * have its onBlockEvent handler called with the given parameters. Args: X,Y,Z, BlockID, EventID, EventParameter
894 */
895 public void addBlockEvent(int par1, int par2, int par3, int par4, int par5, int par6)
896 {
897 BlockEventData var7 = new BlockEventData(par1, par2, par3, par4, par5, par6);
898 Iterator var8 = this.blockEventCache[this.blockEventCacheIndex].iterator();
899 BlockEventData var9;
900
901 do
902 {
903 if (!var8.hasNext())
904 {
905 this.blockEventCache[this.blockEventCacheIndex].add(var7);
906 return;
907 }
908
909 var9 = (BlockEventData)var8.next();
910 }
911 while (!var9.equals(var7));
912 }
913
914 /**
915 * Send and apply locally all pending BlockEvents to each player with 64m radius of the event.
916 */
917 private void sendAndApplyBlockEvents()
918 {
919 while (!this.blockEventCache[this.blockEventCacheIndex].isEmpty())
920 {
921 int var1 = this.blockEventCacheIndex;
922 this.blockEventCacheIndex ^= 1;
923 Iterator var2 = this.blockEventCache[var1].iterator();
924
925 while (var2.hasNext())
926 {
927 BlockEventData var3 = (BlockEventData)var2.next();
928
929 if (this.onBlockEventReceived(var3))
930 {
931 this.mcServer.getConfigurationManager().sendToAllNear((double)var3.getX(), (double)var3.getY(), (double)var3.getZ(), 64.0D, this.provider.dimensionId, new Packet54PlayNoteBlock(var3.getX(), var3.getY(), var3.getZ(), var3.getBlockID(), var3.getEventID(), var3.getEventParameter()));
932 }
933 }
934
935 this.blockEventCache[var1].clear();
936 }
937 }
938
939 /**
940 * Called to apply a pending BlockEvent to apply to the current world.
941 */
942 private boolean onBlockEventReceived(BlockEventData par1BlockEventData)
943 {
944 int var2 = this.getBlockId(par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ());
945
946 if (var2 == par1BlockEventData.getBlockID())
947 {
948 Block.blocksList[var2].onBlockEventReceived(this, par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ(), par1BlockEventData.getEventID(), par1BlockEventData.getEventParameter());
949 return true;
950 }
951 else
952 {
953 return false;
954 }
955 }
956
957 /**
958 * Syncs all changes to disk and wait for completion.
959 */
960 public void flush()
961 {
962 this.saveHandler.flush();
963 }
964
965 /**
966 * Updates all weather states.
967 */
968 protected void updateWeather()
969 {
970 boolean var1 = this.isRaining();
971 super.updateWeather();
972
973 if (var1 != this.isRaining())
974 {
975 if (var1)
976 {
977 this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(2, 0));
978 }
979 else
980 {
981 this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(1, 0));
982 }
983 }
984 }
985
986 /**
987 * Gets the MinecraftServer.
988 */
989 public MinecraftServer getMinecraftServer()
990 {
991 return this.mcServer;
992 }
993
994 /**
995 * Gets the EntityTracker
996 */
997 public EntityTracker getEntityTracker()
998 {
999 return this.theEntityTracker;
1000 }
1001
1002 public PlayerManager getPlayerManager()
1003 {
1004 return this.thePlayerManager;
1005 }
1006
1007 public File getChunkSaveLocation()
1008 {
1009 return ((AnvilChunkLoader)theChunkProviderServer.currentChunkLoader).chunkSaveLocation;
1010 }
1011 }