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