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 return var7;
653 }
654
655 /**
656 * Called when checking if a certain block can be mined or not. The 'spawn safe zone' check is located here.
657 */
658 public boolean canMineBlock(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
659 {
660 return super.canMineBlock(par1EntityPlayer, par2, par3, par4);
661 }
662
663 public boolean canMineBlockBody(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
664 {
665 int var5 = MathHelper.abs_int(par2 - this.worldInfo.getSpawnX());
666 int var6 = MathHelper.abs_int(par4 - this.worldInfo.getSpawnZ());
667
668 if (var5 > var6)
669 {
670 var6 = var5;
671 }
672
673 return var6 > mcServer.getSpawnProtectionSize() || this.mcServer.getConfigurationManager().areCommandsAllowed(par1EntityPlayer.username) || this.mcServer.isSinglePlayer();
674 }
675
676 protected void initialize(WorldSettings par1WorldSettings)
677 {
678 if (this.entityIdMap == null)
679 {
680 this.entityIdMap = new IntHashMap();
681 }
682
683 if (this.field_73064_N == null)
684 {
685 this.field_73064_N = new HashSet();
686 }
687
688 if (this.pendingTickListEntries == null)
689 {
690 this.pendingTickListEntries = new TreeSet();
691 }
692
693 this.createSpawnPosition(par1WorldSettings);
694 super.initialize(par1WorldSettings);
695 }
696
697 /**
698 * creates a spawn position at random within 256 blocks of 0,0
699 */
700 protected void createSpawnPosition(WorldSettings par1WorldSettings)
701 {
702 if (!this.provider.canRespawnHere())
703 {
704 this.worldInfo.setSpawnPosition(0, this.provider.getAverageGroundLevel(), 0);
705 }
706 else
707 {
708 this.findingSpawnPoint = true;
709 WorldChunkManager var2 = this.provider.worldChunkMgr;
710 List var3 = var2.getBiomesToSpawnIn();
711 Random var4 = new Random(this.getSeed());
712 ChunkPosition var5 = var2.findBiomePosition(0, 0, 256, var3, var4);
713 int var6 = 0;
714 int var7 = this.provider.getAverageGroundLevel();
715 int var8 = 0;
716
717 if (var5 != null)
718 {
719 var6 = var5.x;
720 var8 = var5.z;
721 }
722 else
723 {
724 System.out.println("Unable to find spawn biome");
725 }
726
727 int var9 = 0;
728
729 while (!this.provider.canCoordinateBeSpawn(var6, var8))
730 {
731 var6 += var4.nextInt(64) - var4.nextInt(64);
732 var8 += var4.nextInt(64) - var4.nextInt(64);
733 ++var9;
734
735 if (var9 == 1000)
736 {
737 break;
738 }
739 }
740
741 this.worldInfo.setSpawnPosition(var6, var7, var8);
742 this.findingSpawnPoint = false;
743
744 if (par1WorldSettings.isBonusChestEnabled())
745 {
746 this.createBonusChest();
747 }
748 }
749 }
750
751 /**
752 * Creates the bonus chest in the world.
753 */
754 protected void createBonusChest()
755 {
756 WorldGeneratorBonusChest var1 = new WorldGeneratorBonusChest(ChestGenHooks.getItems(BONUS_CHEST), ChestGenHooks.getCount(BONUS_CHEST, rand));
757
758 for (int var2 = 0; var2 < 10; ++var2)
759 {
760 int var3 = this.worldInfo.getSpawnX() + this.rand.nextInt(6) - this.rand.nextInt(6);
761 int var4 = this.worldInfo.getSpawnZ() + this.rand.nextInt(6) - this.rand.nextInt(6);
762 int var5 = this.getTopSolidOrLiquidBlock(var3, var4) + 1;
763
764 if (var1.generate(this, this.rand, var3, var5, var4))
765 {
766 break;
767 }
768 }
769 }
770
771 /**
772 * Gets the hard-coded portal location to use when entering this dimension.
773 */
774 public ChunkCoordinates getEntrancePortalLocation()
775 {
776 return this.provider.getEntrancePortalLocation();
777 }
778
779 /**
780 * Saves all chunks to disk while updating progress bar.
781 */
782 public void saveAllChunks(boolean par1, IProgressUpdate par2IProgressUpdate) throws MinecraftException
783 {
784 if (this.chunkProvider.canSave())
785 {
786 if (par2IProgressUpdate != null)
787 {
788 par2IProgressUpdate.displayProgressMessage("Saving level");
789 }
790
791 this.saveLevel();
792
793 if (par2IProgressUpdate != null)
794 {
795 par2IProgressUpdate.resetProgresAndWorkingMessage("Saving chunks");
796 }
797
798 this.chunkProvider.saveChunks(par1, par2IProgressUpdate);
799 MinecraftForge.EVENT_BUS.post(new WorldEvent.Save(this));
800 }
801 }
802
803 /**
804 * Saves the chunks to disk.
805 */
806 protected void saveLevel() throws MinecraftException
807 {
808 this.checkSessionLock();
809 this.saveHandler.saveWorldInfoWithPlayer(this.worldInfo, this.mcServer.getConfigurationManager().getTagsFromLastWrite());
810 this.mapStorage.saveAllData();
811 this.perWorldStorage.saveAllData();
812 }
813
814 /**
815 * Start the skin for this entity downloading, if necessary, and increment its reference counter
816 */
817 protected void obtainEntitySkin(Entity par1Entity)
818 {
819 super.obtainEntitySkin(par1Entity);
820 this.entityIdMap.addKey(par1Entity.entityId, par1Entity);
821 Entity[] var2 = par1Entity.getParts();
822
823 if (var2 != null)
824 {
825 for (int var3 = 0; var3 < var2.length; ++var3)
826 {
827 this.entityIdMap.addKey(var2[var3].entityId, var2[var3]);
828 }
829 }
830 }
831
832 /**
833 * Decrement the reference counter for this entity's skin image data
834 */
835 protected void releaseEntitySkin(Entity par1Entity)
836 {
837 super.releaseEntitySkin(par1Entity);
838 this.entityIdMap.removeObject(par1Entity.entityId);
839 Entity[] var2 = par1Entity.getParts();
840
841 if (var2 != null)
842 {
843 for (int var3 = 0; var3 < var2.length; ++var3)
844 {
845 this.entityIdMap.removeObject(var2[var3].entityId);
846 }
847 }
848 }
849
850 /**
851 * Returns the Entity with the given ID, or null if it doesn't exist in this World.
852 */
853 public Entity getEntityByID(int par1)
854 {
855 return (Entity)this.entityIdMap.lookup(par1);
856 }
857
858 /**
859 * adds a lightning bolt to the list of lightning bolts in this world.
860 */
861 public boolean addWeatherEffect(Entity par1Entity)
862 {
863 if (super.addWeatherEffect(par1Entity))
864 {
865 this.mcServer.getConfigurationManager().sendToAllNear(par1Entity.posX, par1Entity.posY, par1Entity.posZ, 512.0D, this.provider.dimensionId, new Packet71Weather(par1Entity));
866 return true;
867 }
868 else
869 {
870 return false;
871 }
872 }
873
874 /**
875 * sends a Packet 38 (Entity Status) to all tracked players of that entity
876 */
877 public void setEntityState(Entity par1Entity, byte par2)
878 {
879 Packet38EntityStatus var3 = new Packet38EntityStatus(par1Entity.entityId, par2);
880 this.getEntityTracker().sendPacketToAllAssociatedPlayers(par1Entity, var3);
881 }
882
883 /**
884 * returns a new explosion. Does initiation (at time of writing Explosion is not finished)
885 */
886 public Explosion newExplosion(Entity par1Entity, double par2, double par4, double par6, float par8, boolean par9, boolean par10)
887 {
888 Explosion var11 = new Explosion(this, par1Entity, par2, par4, par6, par8);
889 var11.isFlaming = par9;
890 var11.isSmoking = par10;
891 var11.doExplosionA();
892 var11.doExplosionB(false);
893
894 if (!par10)
895 {
896 var11.affectedBlockPositions.clear();
897 }
898
899 Iterator var12 = this.playerEntities.iterator();
900
901 while (var12.hasNext())
902 {
903 EntityPlayer var13 = (EntityPlayer)var12.next();
904
905 if (var13.getDistanceSq(par2, par4, par6) < 4096.0D)
906 {
907 ((EntityPlayerMP)var13).playerNetServerHandler.sendPacketToPlayer(new Packet60Explosion(par2, par4, par6, par8, var11.affectedBlockPositions, (Vec3)var11.func_77277_b().get(var13)));
908 }
909 }
910
911 return var11;
912 }
913
914 /**
915 * Adds a block event with the given Args to the blockEventCache. During the next tick(), the block specified will
916 * have its onBlockEvent handler called with the given parameters. Args: X,Y,Z, BlockID, EventID, EventParameter
917 */
918 public void addBlockEvent(int par1, int par2, int par3, int par4, int par5, int par6)
919 {
920 BlockEventData var7 = new BlockEventData(par1, par2, par3, par4, par5, par6);
921 Iterator var8 = this.blockEventCache[this.blockEventCacheIndex].iterator();
922 BlockEventData var9;
923
924 do
925 {
926 if (!var8.hasNext())
927 {
928 this.blockEventCache[this.blockEventCacheIndex].add(var7);
929 return;
930 }
931
932 var9 = (BlockEventData)var8.next();
933 }
934 while (!var9.equals(var7));
935 }
936
937 /**
938 * Send and apply locally all pending BlockEvents to each player with 64m radius of the event.
939 */
940 private void sendAndApplyBlockEvents()
941 {
942 while (!this.blockEventCache[this.blockEventCacheIndex].isEmpty())
943 {
944 int var1 = this.blockEventCacheIndex;
945 this.blockEventCacheIndex ^= 1;
946 Iterator var2 = this.blockEventCache[var1].iterator();
947
948 while (var2.hasNext())
949 {
950 BlockEventData var3 = (BlockEventData)var2.next();
951
952 if (this.onBlockEventReceived(var3))
953 {
954 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()));
955 }
956 }
957
958 this.blockEventCache[var1].clear();
959 }
960 }
961
962 /**
963 * Called to apply a pending BlockEvent to apply to the current world.
964 */
965 private boolean onBlockEventReceived(BlockEventData par1BlockEventData)
966 {
967 int var2 = this.getBlockId(par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ());
968
969 if (var2 == par1BlockEventData.getBlockID())
970 {
971 Block.blocksList[var2].onBlockEventReceived(this, par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ(), par1BlockEventData.getEventID(), par1BlockEventData.getEventParameter());
972 return true;
973 }
974 else
975 {
976 return false;
977 }
978 }
979
980 /**
981 * Syncs all changes to disk and wait for completion.
982 */
983 public void flush()
984 {
985 this.saveHandler.flush();
986 }
987
988 /**
989 * Updates all weather states.
990 */
991 protected void updateWeather()
992 {
993 boolean var1 = this.isRaining();
994 super.updateWeather();
995
996 if (var1 != this.isRaining())
997 {
998 if (var1)
999 {
1000 this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(2, 0));
1001 }
1002 else
1003 {
1004 this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(1, 0));
1005 }
1006 }
1007 }
1008
1009 /**
1010 * Gets the MinecraftServer.
1011 */
1012 public MinecraftServer getMinecraftServer()
1013 {
1014 return this.mcServer;
1015 }
1016
1017 /**
1018 * Gets the EntityTracker
1019 */
1020 public EntityTracker getEntityTracker()
1021 {
1022 return this.theEntityTracker;
1023 }
1024
1025 public PlayerManager getPlayerManager()
1026 {
1027 return this.thePlayerManager;
1028 }
1029
1030 public Teleporter func_85176_s()
1031 {
1032 return this.field_85177_Q;
1033 }
1034
1035 public File getChunkSaveLocation()
1036 {
1037 return ((AnvilChunkLoader)theChunkProviderServer.currentChunkLoader).chunkSaveLocation;
1038 }
1039 }