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