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