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 /**
033 * this is set related to Manager.areCommandsAllowed, but is never used is is also set back to false at the end of
034 * both functions which set it.
035 */
036 public boolean actionsAllowed = false;
037
038 /** set by CommandServerSave{all,Off,On} */
039 public boolean canNotSave;
040
041 /** is false if there are no players */
042 private boolean allPlayersSleeping;
043 private int updateEntityTick = 0;
044
045 /**
046 * Double buffer of ServerBlockEventList[] for holding pending BlockEventData's
047 */
048 private ServerBlockEventList[] blockEventCache = new ServerBlockEventList[] {new ServerBlockEventList((ServerBlockEvent)null), new ServerBlockEventList((ServerBlockEvent)null)};
049
050 /**
051 * The index into the blockEventCache; either 0, or 1, toggled in sendBlockEventPackets where all BlockEvent are
052 * applied locally and send to clients.
053 */
054 private int blockEventCacheIndex = 0;
055 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)};
056
057 /** An IntHashMap of entity IDs (integers) to their Entity objects. */
058 private IntHashMap entityIdMap;
059
060 /** Stores the recently processed (lighting) chunks */
061 protected Set<ChunkCoordIntPair> doneChunks = new HashSet<ChunkCoordIntPair>();
062
063 public WorldServer(MinecraftServer par1MinecraftServer, ISaveHandler par2ISaveHandler, String par3Str, int par4, WorldSettings par5WorldSettings, Profiler par6Profiler)
064 {
065 super(par2ISaveHandler, par3Str, par5WorldSettings, WorldProvider.getProviderForDimension(par4), par6Profiler);
066 this.mcServer = par1MinecraftServer;
067 this.theEntityTracker = new EntityTracker(this);
068 this.thePlayerManager = new PlayerManager(this, par1MinecraftServer.getConfigurationManager().getViewDistance());
069
070 if (this.entityIdMap == null)
071 {
072 this.entityIdMap = new IntHashMap();
073 }
074
075 if (this.field_73064_N == null)
076 {
077 this.field_73064_N = new HashSet();
078 }
079
080 if (this.pendingTickListEntries == null)
081 {
082 this.pendingTickListEntries = new TreeSet();
083 }
084 DimensionManager.setWorld(par4, this);
085 }
086
087 /**
088 * Runs a single tick for the world
089 */
090 public void tick()
091 {
092 super.tick();
093
094 if (this.getWorldInfo().isHardcoreModeEnabled() && this.difficultySetting < 3)
095 {
096 this.difficultySetting = 3;
097 }
098
099 this.provider.worldChunkMgr.cleanupCache();
100
101 if (this.areAllPlayersAsleep())
102 {
103 boolean var1 = false;
104
105 if (this.spawnHostileMobs && this.difficultySetting >= 1)
106 {
107 ;
108 }
109
110 if (!var1)
111 {
112 long var2 = this.worldInfo.getWorldTime() + 24000L;
113 this.worldInfo.setWorldTime(var2 - var2 % 24000L);
114 this.wakeAllPlayers();
115 }
116 }
117
118 this.theProfiler.startSection("mobSpawner");
119 SpawnerAnimals.findChunksForSpawning(this, this.spawnHostileMobs, this.spawnPeacefulMobs && this.worldInfo.getWorldTime() % 400L == 0L);
120 this.theProfiler.endStartSection("chunkSource");
121 this.chunkProvider.unload100OldestChunks();
122 int var4 = this.calculateSkylightSubtracted(1.0F);
123
124 if (var4 != this.skylightSubtracted)
125 {
126 this.skylightSubtracted = var4;
127 }
128
129 this.sendAndApplyBlockEvents();
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 NextTickListEntry var6 = new NextTickListEntry(par1, par2, par3, par4);
383 boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(var6.xCoord >> 4, var6.zCoord >> 4));
384 byte var7 = isForced ? (byte)0 : 8;
385
386 if (this.scheduledUpdatesAreImmediate)
387 {
388 if (this.checkChunksExist(var6.xCoord - var7, var6.yCoord - var7, var6.zCoord - var7, var6.xCoord + var7, var6.yCoord + var7, var6.zCoord + var7))
389 {
390 int var8 = this.getBlockId(var6.xCoord, var6.yCoord, var6.zCoord);
391
392 if (var8 == var6.blockID && var8 > 0)
393 {
394 Block.blocksList[var8].updateTick(this, var6.xCoord, var6.yCoord, var6.zCoord, this.rand);
395 }
396 }
397 }
398 else
399 {
400 if (this.checkChunksExist(par1 - var7, par2 - var7, par3 - var7, par1 + var7, par2 + var7, par3 + var7))
401 {
402 if (par4 > 0)
403 {
404 var6.setScheduledTime((long)par5 + this.worldInfo.getWorldTime());
405 }
406
407 if (!this.field_73064_N.contains(var6))
408 {
409 this.field_73064_N.add(var6);
410 this.pendingTickListEntries.add(var6);
411 }
412 }
413 }
414 }
415
416 /**
417 * Schedules a block update from the saved information in a chunk. Called when the chunk is loaded.
418 */
419 public void scheduleBlockUpdateFromLoad(int par1, int par2, int par3, int par4, int par5)
420 {
421 NextTickListEntry var6 = new NextTickListEntry(par1, par2, par3, par4);
422
423 if (par4 > 0)
424 {
425 var6.setScheduledTime((long)par5 + this.worldInfo.getWorldTime());
426 }
427
428 if (!this.field_73064_N.contains(var6))
429 {
430 this.field_73064_N.add(var6);
431 this.pendingTickListEntries.add(var6);
432 }
433 }
434
435 /**
436 * Updates (and cleans up) entities and tile entities
437 */
438 public void updateEntities()
439 {
440 if (this.playerEntities.isEmpty() && getPersistentChunks().isEmpty())
441 {
442 if (this.updateEntityTick++ >= 60)
443 {
444 return;
445 }
446 }
447 else
448 {
449 this.updateEntityTick = 0;
450 }
451
452 super.updateEntities();
453 }
454
455 /**
456 * Runs through the list of updates to run and ticks them
457 */
458 public boolean tickUpdates(boolean par1)
459 {
460 int var2 = this.pendingTickListEntries.size();
461
462 if (var2 != this.field_73064_N.size())
463 {
464 throw new IllegalStateException("TickNextTick list out of synch");
465 }
466 else
467 {
468 if (var2 > 1000)
469 {
470 var2 = 1000;
471 }
472
473 for (int var3 = 0; var3 < var2; ++var3)
474 {
475 NextTickListEntry var4 = (NextTickListEntry)this.pendingTickListEntries.first();
476
477 if (!par1 && var4.scheduledTime > this.worldInfo.getWorldTime())
478 {
479 break;
480 }
481
482 this.pendingTickListEntries.remove(var4);
483 this.field_73064_N.remove(var4);
484 boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(var4.xCoord >> 4, var4.zCoord >> 4));
485 byte var5 = isForced ? (byte)0 : 8;
486
487 if (this.checkChunksExist(var4.xCoord - var5, var4.yCoord - var5, var4.zCoord - var5, var4.xCoord + var5, var4.yCoord + var5, var4.zCoord + var5))
488 {
489 int var6 = this.getBlockId(var4.xCoord, var4.yCoord, var4.zCoord);
490
491 if (var6 == var4.blockID && var6 > 0)
492 {
493 Block.blocksList[var6].updateTick(this, var4.xCoord, var4.yCoord, var4.zCoord, this.rand);
494 }
495 }
496 }
497
498 return !this.pendingTickListEntries.isEmpty();
499 }
500 }
501
502 public List getPendingBlockUpdates(Chunk par1Chunk, boolean par2)
503 {
504 ArrayList var3 = null;
505 ChunkCoordIntPair var4 = par1Chunk.getChunkCoordIntPair();
506 int var5 = var4.chunkXPos << 4;
507 int var6 = var5 + 16;
508 int var7 = var4.chunkZPos << 4;
509 int var8 = var7 + 16;
510 Iterator var9 = this.pendingTickListEntries.iterator();
511
512 while (var9.hasNext())
513 {
514 NextTickListEntry var10 = (NextTickListEntry)var9.next();
515
516 if (var10.xCoord >= var5 && var10.xCoord < var6 && var10.zCoord >= var7 && var10.zCoord < var8)
517 {
518 if (par2)
519 {
520 this.field_73064_N.remove(var10);
521 var9.remove();
522 }
523
524 if (var3 == null)
525 {
526 var3 = new ArrayList();
527 }
528
529 var3.add(var10);
530 }
531 }
532
533 return var3;
534 }
535
536 /**
537 * Will update the entity in the world if the chunk the entity is in is currently loaded or its forced to update.
538 * Args: entity, forceUpdate
539 */
540 public void updateEntityWithOptionalForce(Entity par1Entity, boolean par2)
541 {
542 if (!this.mcServer.getCanSpawnAnimals() && (par1Entity instanceof EntityAnimal || par1Entity instanceof EntityWaterMob))
543 {
544 par1Entity.setDead();
545 }
546
547 if (!this.mcServer.getCanSpawnNPCs() && par1Entity instanceof INpc)
548 {
549 par1Entity.setDead();
550 }
551
552 if (!(par1Entity.riddenByEntity instanceof EntityPlayer))
553 {
554 super.updateEntityWithOptionalForce(par1Entity, par2);
555 }
556 }
557
558 /**
559 * direct call to super.updateEntityWithOptionalForce
560 */
561 public void uncheckedUpdateEntity(Entity par1Entity, boolean par2)
562 {
563 super.updateEntityWithOptionalForce(par1Entity, par2);
564 }
565
566 /**
567 * Creates the chunk provider for this world. Called in the constructor. Retrieves provider from worldProvider?
568 */
569 protected IChunkProvider createChunkProvider()
570 {
571 IChunkLoader var1 = this.saveHandler.getChunkLoader(this.provider);
572 this.theChunkProviderServer = new ChunkProviderServer(this, var1, this.provider.getChunkProvider());
573 return this.theChunkProviderServer;
574 }
575
576 /**
577 * pars: min x,y,z , max x,y,z
578 */
579 public List getAllTileEntityInBox(int par1, int par2, int par3, int par4, int par5, int par6)
580 {
581 ArrayList var7 = new ArrayList();
582
583 for(int x = (par1 >> 4); x <= (par4 >> 4); x++)
584 {
585 for(int z = (par3 >> 4); z <= (par6 >> 4); z++)
586 {
587 Chunk chunk = getChunkFromChunkCoords(x, z);
588 if (chunk != null)
589 {
590 for(Object obj : chunk.chunkTileEntityMap.values())
591 {
592 TileEntity entity = (TileEntity)obj;
593 if (!entity.isInvalid())
594 {
595 if (entity.xCoord >= par1 && entity.yCoord >= par2 && entity.zCoord >= par3 &&
596 entity.xCoord <= par4 && entity.yCoord <= par5 && entity.zCoord <= par6)
597 {
598 var7.add(entity);
599 }
600 }
601 }
602 }
603 }
604 }
605
606 return var7;
607 }
608
609 /**
610 * Called when checking if a certain block can be mined or not. The 'spawn safe zone' check is located here.
611 */
612 public boolean canMineBlock(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
613 {
614 return super.canMineBlock(par1EntityPlayer, par2, par3, par4);
615 }
616
617 public boolean canMineBlockBody(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
618 {
619 int var5 = MathHelper.abs_int(par2 - this.worldInfo.getSpawnX());
620 int var6 = MathHelper.abs_int(par4 - this.worldInfo.getSpawnZ());
621
622 if (var5 > var6)
623 {
624 var6 = var5;
625 }
626
627 return var6 > mcServer.spawnProtectionSize || this.mcServer.getConfigurationManager().areCommandsAllowed(par1EntityPlayer.username) || this.mcServer.isSinglePlayer();
628 }
629
630 protected void initialize(WorldSettings par1WorldSettings)
631 {
632 if (this.entityIdMap == null)
633 {
634 this.entityIdMap = new IntHashMap();
635 }
636
637 if (this.field_73064_N == null)
638 {
639 this.field_73064_N = new HashSet();
640 }
641
642 if (this.pendingTickListEntries == null)
643 {
644 this.pendingTickListEntries = new TreeSet();
645 }
646
647 this.createSpawnPosition(par1WorldSettings);
648 super.initialize(par1WorldSettings);
649 }
650
651 /**
652 * creates a spawn position at random within 256 blocks of 0,0
653 */
654 protected void createSpawnPosition(WorldSettings par1WorldSettings)
655 {
656 if (!this.provider.canRespawnHere())
657 {
658 this.worldInfo.setSpawnPosition(0, this.provider.getAverageGroundLevel(), 0);
659 }
660 else
661 {
662 this.findingSpawnPoint = true;
663 WorldChunkManager var2 = this.provider.worldChunkMgr;
664 List var3 = var2.getBiomesToSpawnIn();
665 Random var4 = new Random(this.getSeed());
666 ChunkPosition var5 = var2.findBiomePosition(0, 0, 256, var3, var4);
667 int var6 = 0;
668 int var7 = this.provider.getAverageGroundLevel();
669 int var8 = 0;
670
671 if (var5 != null)
672 {
673 var6 = var5.x;
674 var8 = var5.z;
675 }
676 else
677 {
678 System.out.println("Unable to find spawn biome");
679 }
680
681 int var9 = 0;
682
683 while (!this.provider.canCoordinateBeSpawn(var6, var8))
684 {
685 var6 += var4.nextInt(64) - var4.nextInt(64);
686 var8 += var4.nextInt(64) - var4.nextInt(64);
687 ++var9;
688
689 if (var9 == 1000)
690 {
691 break;
692 }
693 }
694
695 this.worldInfo.setSpawnPosition(var6, var7, var8);
696 this.findingSpawnPoint = false;
697
698 if (par1WorldSettings.isBonusChestEnabled())
699 {
700 this.createBonusChest();
701 }
702 }
703 }
704
705 /**
706 * Creates the bonus chest in the world.
707 */
708 protected void createBonusChest()
709 {
710 WorldGeneratorBonusChest var1 = new WorldGeneratorBonusChest(ChestGenHooks.getItems(BONUS_CHEST), ChestGenHooks.getCount(BONUS_CHEST, rand));
711
712 for (int var2 = 0; var2 < 10; ++var2)
713 {
714 int var3 = this.worldInfo.getSpawnX() + this.rand.nextInt(6) - this.rand.nextInt(6);
715 int var4 = this.worldInfo.getSpawnZ() + this.rand.nextInt(6) - this.rand.nextInt(6);
716 int var5 = this.getTopSolidOrLiquidBlock(var3, var4) + 1;
717
718 if (var1.generate(this, this.rand, var3, var5, var4))
719 {
720 break;
721 }
722 }
723 }
724
725 /**
726 * Gets the hard-coded portal location to use when entering this dimension.
727 */
728 public ChunkCoordinates getEntrancePortalLocation()
729 {
730 return this.provider.getEntrancePortalLocation();
731 }
732
733 /**
734 * Saves all chunks to disk while updating progress bar.
735 */
736 public void saveAllChunks(boolean par1, IProgressUpdate par2IProgressUpdate) throws MinecraftException
737 {
738 if (this.chunkProvider.canSave())
739 {
740 if (par2IProgressUpdate != null)
741 {
742 par2IProgressUpdate.displayProgressMessage("Saving level");
743 }
744
745 this.saveLevel();
746
747 if (par2IProgressUpdate != null)
748 {
749 par2IProgressUpdate.resetProgresAndWorkingMessage("Saving chunks");
750 }
751
752 this.chunkProvider.saveChunks(par1, par2IProgressUpdate);
753 MinecraftForge.EVENT_BUS.post(new WorldEvent.Save(this));
754 }
755 }
756
757 /**
758 * Saves the chunks to disk.
759 */
760 protected void saveLevel() throws MinecraftException
761 {
762 this.checkSessionLock();
763 this.saveHandler.saveWorldInfoWithPlayer(this.worldInfo, this.mcServer.getConfigurationManager().getTagsFromLastWrite());
764 this.mapStorage.saveAllData();
765 }
766
767 /**
768 * Start the skin for this entity downloading, if necessary, and increment its reference counter
769 */
770 protected void obtainEntitySkin(Entity par1Entity)
771 {
772 super.obtainEntitySkin(par1Entity);
773 this.entityIdMap.addKey(par1Entity.entityId, par1Entity);
774 Entity[] var2 = par1Entity.getParts();
775
776 if (var2 != null)
777 {
778 Entity[] var3 = var2;
779 int var4 = var2.length;
780
781 for (int var5 = 0; var5 < var4; ++var5)
782 {
783 Entity var6 = var3[var5];
784 this.entityIdMap.addKey(var6.entityId, var6);
785 }
786 }
787 }
788
789 /**
790 * Decrement the reference counter for this entity's skin image data
791 */
792 protected void releaseEntitySkin(Entity par1Entity)
793 {
794 super.releaseEntitySkin(par1Entity);
795 this.entityIdMap.removeObject(par1Entity.entityId);
796 Entity[] var2 = par1Entity.getParts();
797
798 if (var2 != null)
799 {
800 Entity[] var3 = var2;
801 int var4 = var2.length;
802
803 for (int var5 = 0; var5 < var4; ++var5)
804 {
805 Entity var6 = var3[var5];
806 this.entityIdMap.removeObject(var6.entityId);
807 }
808 }
809 }
810
811 /**
812 * Returns the Entity with the given ID, or null if it doesn't exist in this World.
813 */
814 public Entity getEntityByID(int par1)
815 {
816 return (Entity)this.entityIdMap.lookup(par1);
817 }
818
819 /**
820 * adds a lightning bolt to the list of lightning bolts in this world.
821 */
822 public boolean addWeatherEffect(Entity par1Entity)
823 {
824 if (super.addWeatherEffect(par1Entity))
825 {
826 this.mcServer.getConfigurationManager().sendToAllNear(par1Entity.posX, par1Entity.posY, par1Entity.posZ, 512.0D, this.provider.dimensionId, new Packet71Weather(par1Entity));
827 return true;
828 }
829 else
830 {
831 return false;
832 }
833 }
834
835 /**
836 * sends a Packet 38 (Entity Status) to all tracked players of that entity
837 */
838 public void setEntityState(Entity par1Entity, byte par2)
839 {
840 Packet38EntityStatus var3 = new Packet38EntityStatus(par1Entity.entityId, par2);
841 this.getEntityTracker().sendPacketToAllAssociatedPlayers(par1Entity, var3);
842 }
843
844 /**
845 * returns a new explosion. Does initiation (at time of writing Explosion is not finished)
846 */
847 public Explosion newExplosion(Entity par1Entity, double par2, double par4, double par6, float par8, boolean par9)
848 {
849 Explosion var10 = new Explosion(this, par1Entity, par2, par4, par6, par8);
850 var10.isFlaming = par9;
851 var10.doExplosionA();
852 var10.doExplosionB(false);
853 Iterator var11 = this.playerEntities.iterator();
854
855 while (var11.hasNext())
856 {
857 EntityPlayer var12 = (EntityPlayer)var11.next();
858
859 if (var12.getDistanceSq(par2, par4, par6) < 4096.0D)
860 {
861 ((EntityPlayerMP)var12).playerNetServerHandler.sendPacketToPlayer(new Packet60Explosion(par2, par4, par6, par8, var10.field_77281_g, (Vec3)var10.func_77277_b().get(var12)));
862 }
863 }
864
865 return var10;
866 }
867
868 /**
869 * Adds a block event with the given Args to the blockEventCache. During the next tick(), the block specified will
870 * have its onBlockEvent handler called with the given parameters. Args: X,Y,Z, BlockID, EventID, EventParameter
871 */
872 public void addBlockEvent(int par1, int par2, int par3, int par4, int par5, int par6)
873 {
874 BlockEventData var7 = new BlockEventData(par1, par2, par3, par4, par5, par6);
875 Iterator var8 = this.blockEventCache[this.blockEventCacheIndex].iterator();
876 BlockEventData var9;
877
878 do
879 {
880 if (!var8.hasNext())
881 {
882 this.blockEventCache[this.blockEventCacheIndex].add(var7);
883 return;
884 }
885
886 var9 = (BlockEventData)var8.next();
887 }
888 while (!var9.equals(var7));
889 }
890
891 /**
892 * Send and apply locally all pending BlockEvents to each player with 64m radius of the event.
893 */
894 private void sendAndApplyBlockEvents()
895 {
896 while (!this.blockEventCache[this.blockEventCacheIndex].isEmpty())
897 {
898 int var1 = this.blockEventCacheIndex;
899 this.blockEventCacheIndex ^= 1;
900 Iterator var2 = this.blockEventCache[var1].iterator();
901
902 while (var2.hasNext())
903 {
904 BlockEventData var3 = (BlockEventData)var2.next();
905
906 if (this.onBlockEventReceived(var3))
907 {
908 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()));
909 }
910 }
911
912 this.blockEventCache[var1].clear();
913 }
914 }
915
916 /**
917 * Called to apply a pending BlockEvent to apply to the current world.
918 */
919 private boolean onBlockEventReceived(BlockEventData par1BlockEventData)
920 {
921 int var2 = this.getBlockId(par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ());
922
923 if (var2 == par1BlockEventData.getBlockID())
924 {
925 Block.blocksList[var2].onBlockEventReceived(this, par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ(), par1BlockEventData.getEventID(), par1BlockEventData.getEventParameter());
926 return true;
927 }
928 else
929 {
930 return false;
931 }
932 }
933
934 /**
935 * Syncs all changes to disk and wait for completion.
936 */
937 public void flush()
938 {
939 this.saveHandler.flush();
940 }
941
942 /**
943 * Updates all weather states.
944 */
945 protected void updateWeather()
946 {
947 boolean var1 = this.isRaining();
948 super.updateWeather();
949
950 if (var1 != this.isRaining())
951 {
952 if (var1)
953 {
954 this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(2, 0));
955 }
956 else
957 {
958 this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(1, 0));
959 }
960 }
961 }
962
963 /**
964 * Gets the MinecraftServer.
965 */
966 public MinecraftServer getMinecraftServer()
967 {
968 return this.mcServer;
969 }
970
971 /**
972 * Gets the EntityTracker
973 */
974 public EntityTracker getEntityTracker()
975 {
976 return this.theEntityTracker;
977 }
978
979 /**
980 * Sets the time on the given WorldServer
981 */
982 public void setTime(long par1)
983 {
984 long var3 = par1 - this.worldInfo.getWorldTime();
985 NextTickListEntry var6;
986
987 for (Iterator var5 = this.field_73064_N.iterator(); var5.hasNext(); var6.scheduledTime += var3)
988 {
989 var6 = (NextTickListEntry)var5.next();
990 }
991
992 Block[] var9 = Block.blocksList;
993 int var10 = var9.length;
994
995 for (int var7 = 0; var7 < var10; ++var7)
996 {
997 Block var8 = var9[var7];
998
999 if (var8 != null)
1000 {
1001 var8.onTimeChanged(this, var3, par1);
1002 }
1003 }
1004
1005 this.setWorldTime(par1);
1006 }
1007
1008 public PlayerManager getPlayerManager()
1009 {
1010 return this.thePlayerManager;
1011 }
1012
1013 public File getChunkSaveLocation()
1014 {
1015 return ((AnvilChunkLoader)theChunkProviderServer.currentChunkLoader).chunkSaveLocation;
1016 }
1017 }