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