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        scoreboardsavedata.func_96499_a(this.worldScoreboard);
138        ((ServerScoreboard)this.worldScoreboard).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.setBlock(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.setBlock(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    /**
446     * Returns true if the given block will receive a scheduled tick in the future. Args: X, Y, Z, blockID
447     */
448    public boolean isBlockTickScheduled(int par1, int par2, int par3, int par4)
449    {
450        NextTickListEntry nextticklistentry = new NextTickListEntry(par1, par2, par3, par4);
451        return this.field_94579_S.contains(nextticklistentry);
452    }
453
454    /**
455     * Schedules a tick to a block with a delay (Most commonly the tick rate)
456     */
457    public void scheduleBlockUpdate(int par1, int par2, int par3, int par4, int par5)
458    {
459        this.func_82740_a(par1, par2, par3, par4, par5, 0);
460    }
461
462    public void func_82740_a(int par1, int par2, int par3, int par4, int par5, int par6)
463    {
464        NextTickListEntry nextticklistentry = new NextTickListEntry(par1, par2, par3, par4);
465        //Keeping here as a note for future when it may be restored.
466        //boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(nextticklistentry.xCoord >> 4, nextticklistentry.zCoord >> 4));
467        //byte b0 = isForced ? 0 : 8;
468        byte b0 = 0;
469
470        if (this.scheduledUpdatesAreImmediate && par4 > 0)
471        {
472            if (Block.blocksList[par4].func_82506_l())
473            {
474                if (this.checkChunksExist(nextticklistentry.xCoord - b0, nextticklistentry.yCoord - b0, nextticklistentry.zCoord - b0, nextticklistentry.xCoord + b0, nextticklistentry.yCoord + b0, nextticklistentry.zCoord + b0))
475                {
476                    int k1 = this.getBlockId(nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord);
477
478                    if (k1 == nextticklistentry.blockID && k1 > 0)
479                    {
480                        Block.blocksList[k1].updateTick(this, nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord, this.rand);
481                    }
482                }
483
484                return;
485            }
486
487            par5 = 1;
488        }
489
490        if (this.checkChunksExist(par1 - b0, par2 - b0, par3 - b0, par1 + b0, par2 + b0, par3 + b0))
491        {
492            if (par4 > 0)
493            {
494                nextticklistentry.setScheduledTime((long)par5 + this.worldInfo.getWorldTotalTime());
495                nextticklistentry.func_82753_a(par6);
496            }
497
498            if (!this.field_73064_N.contains(nextticklistentry))
499            {
500                this.field_73064_N.add(nextticklistentry);
501                this.pendingTickListEntries.add(nextticklistentry);
502            }
503        }
504    }
505
506    /**
507     * Schedules a block update from the saved information in a chunk. Called when the chunk is loaded.
508     */
509    public void scheduleBlockUpdateFromLoad(int par1, int par2, int par3, int par4, int par5, int par6)
510    {
511        NextTickListEntry nextticklistentry = new NextTickListEntry(par1, par2, par3, par4);
512        nextticklistentry.func_82753_a(par6);
513
514        if (par4 > 0)
515        {
516            nextticklistentry.setScheduledTime((long)par5 + this.worldInfo.getWorldTotalTime());
517        }
518
519        if (!this.field_73064_N.contains(nextticklistentry))
520        {
521            this.field_73064_N.add(nextticklistentry);
522            this.pendingTickListEntries.add(nextticklistentry);
523        }
524    }
525
526    /**
527     * Updates (and cleans up) entities and tile entities
528     */
529    public void updateEntities()
530    {
531        if (this.playerEntities.isEmpty() && getPersistentChunks().isEmpty())
532        {
533            if (this.updateEntityTick++ >= 1200)
534            {
535                return;
536            }
537        }
538        else
539        {
540            this.resetUpdateEntityTick();
541        }
542
543        super.updateEntities();
544    }
545
546    /**
547     * Resets the updateEntityTick field to 0
548     */
549    public void resetUpdateEntityTick()
550    {
551        this.updateEntityTick = 0;
552    }
553
554    /**
555     * Runs through the list of updates to run and ticks them
556     */
557    public boolean tickUpdates(boolean par1)
558    {
559        int i = this.pendingTickListEntries.size();
560
561        if (i != this.field_73064_N.size())
562        {
563            throw new IllegalStateException("TickNextTick list out of synch");
564        }
565        else
566        {
567            if (i > 1000)
568            {
569                i = 1000;
570            }
571
572            this.theProfiler.startSection("cleaning");
573            NextTickListEntry nextticklistentry;
574
575            for (int j = 0; j < i; ++j)
576            {
577                nextticklistentry = (NextTickListEntry)this.pendingTickListEntries.first();
578
579                if (!par1 && nextticklistentry.scheduledTime > this.worldInfo.getWorldTotalTime())
580                {
581                    break;
582                }
583
584                this.pendingTickListEntries.remove(nextticklistentry);
585                this.field_73064_N.remove(nextticklistentry);
586                this.field_94579_S.add(nextticklistentry);
587            }
588
589            this.theProfiler.endSection();
590            this.theProfiler.startSection("ticking");
591            Iterator iterator = this.field_94579_S.iterator();
592
593            while (iterator.hasNext())
594            {
595                nextticklistentry = (NextTickListEntry)iterator.next();
596                iterator.remove();
597                //Keeping here as a note for future when it may be restored.
598                //boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(nextticklistentry.xCoord >> 4, nextticklistentry.zCoord >> 4));
599                //byte b0 = isForced ? 0 : 8;
600                byte b0 = 0;
601
602                if (this.checkChunksExist(nextticklistentry.xCoord - b0, nextticklistentry.yCoord - b0, nextticklistentry.zCoord - b0, nextticklistentry.xCoord + b0, nextticklistentry.yCoord + b0, nextticklistentry.zCoord + b0))
603                {
604                    int k = this.getBlockId(nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord);
605
606                    if (k > 0 && Block.isAssociatedBlockID(k, nextticklistentry.blockID))
607                    {
608                        try
609                        {
610                            Block.blocksList[k].updateTick(this, nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord, this.rand);
611                        }
612                        catch (Throwable throwable)
613                        {
614                            CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Exception while ticking a block");
615                            CrashReportCategory crashreportcategory = crashreport.makeCategory("Block being ticked");
616                            int l;
617
618                            try
619                            {
620                                l = this.getBlockMetadata(nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord);
621                            }
622                            catch (Throwable throwable1)
623                            {
624                                l = -1;
625                            }
626
627                            CrashReportCategory.func_85068_a(crashreportcategory, nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord, k, l);
628                            throw new ReportedException(crashreport);
629                        }
630                    }
631                }
632                else
633                {
634                    this.scheduleBlockUpdate(nextticklistentry.xCoord, nextticklistentry.yCoord, nextticklistentry.zCoord, nextticklistentry.blockID, 0);
635                }
636            }
637
638            this.theProfiler.endSection();
639            this.field_94579_S.clear();
640            return !this.pendingTickListEntries.isEmpty();
641        }
642    }
643
644    public List getPendingBlockUpdates(Chunk par1Chunk, boolean par2)
645    {
646        ArrayList arraylist = null;
647        ChunkCoordIntPair chunkcoordintpair = par1Chunk.getChunkCoordIntPair();
648        int i = (chunkcoordintpair.chunkXPos << 4) - 2;
649        int j = i + 16 + 2;
650        int k = (chunkcoordintpair.chunkZPos << 4) - 2;
651        int l = k + 16 + 2;
652
653        for (int i1 = 0; i1 < 2; ++i1)
654        {
655            Iterator iterator;
656
657            if (i1 == 0)
658            {
659                iterator = this.pendingTickListEntries.iterator();
660            }
661            else
662            {
663                iterator = this.field_94579_S.iterator();
664
665                if (!this.field_94579_S.isEmpty())
666                {
667                    System.out.println(this.field_94579_S.size());
668                }
669            }
670
671            while (iterator.hasNext())
672            {
673                NextTickListEntry nextticklistentry = (NextTickListEntry)iterator.next();
674
675                if (nextticklistentry.xCoord >= i && nextticklistentry.xCoord < j && nextticklistentry.zCoord >= k && nextticklistentry.zCoord < l)
676                {
677                    if (par2)
678                    {
679                        this.field_73064_N.remove(nextticklistentry);
680                        iterator.remove();
681                    }
682
683                    if (arraylist == null)
684                    {
685                        arraylist = new ArrayList();
686                    }
687
688                    arraylist.add(nextticklistentry);
689                }
690            }
691        }
692
693        return arraylist;
694    }
695
696    /**
697     * Will update the entity in the world if the chunk the entity is in is currently loaded or its forced to update.
698     * Args: entity, forceUpdate
699     */
700    public void updateEntityWithOptionalForce(Entity par1Entity, boolean par2)
701    {
702        if (!this.mcServer.getCanSpawnAnimals() && (par1Entity instanceof EntityAnimal || par1Entity instanceof EntityWaterMob))
703        {
704            par1Entity.setDead();
705        }
706
707        if (!this.mcServer.getCanSpawnNPCs() && par1Entity instanceof INpc)
708        {
709            par1Entity.setDead();
710        }
711
712        if (!(par1Entity.riddenByEntity instanceof EntityPlayer))
713        {
714            super.updateEntityWithOptionalForce(par1Entity, par2);
715        }
716    }
717
718    /**
719     * direct call to super.updateEntityWithOptionalForce
720     */
721    public void uncheckedUpdateEntity(Entity par1Entity, boolean par2)
722    {
723        super.updateEntityWithOptionalForce(par1Entity, par2);
724    }
725
726    /**
727     * Creates the chunk provider for this world. Called in the constructor. Retrieves provider from worldProvider?
728     */
729    protected IChunkProvider createChunkProvider()
730    {
731        IChunkLoader ichunkloader = this.saveHandler.getChunkLoader(this.provider);
732        this.theChunkProviderServer = new ChunkProviderServer(this, ichunkloader, this.provider.createChunkGenerator());
733        return this.theChunkProviderServer;
734    }
735
736    /**
737     * pars: min x,y,z , max x,y,z
738     */
739    public List getAllTileEntityInBox(int par1, int par2, int par3, int par4, int par5, int par6)
740    {
741        ArrayList arraylist = new ArrayList();
742
743        for(int x = (par1 >> 4); x <= (par4 >> 4); x++)
744        {
745            for(int z = (par3 >> 4); z <= (par6 >> 4); z++)
746            {
747                Chunk chunk = getChunkFromChunkCoords(x, z);
748                if (chunk != null)
749                {
750                    for(Object obj : chunk.chunkTileEntityMap.values())
751                    {
752                        TileEntity entity = (TileEntity)obj;
753                        if (!entity.isInvalid())
754                        {
755                            if (entity.xCoord >= par1 && entity.yCoord >= par2 && entity.zCoord >= par3 &&
756                                entity.xCoord <= par4 && entity.yCoord <= par5 && entity.zCoord <= par6)
757                            {
758                                arraylist.add(entity);
759                            }
760                        }
761                    }
762                }
763            }
764        }
765        return arraylist;
766    }
767
768    /**
769     * Called when checking if a certain block can be mined or not. The 'spawn safe zone' check is located here.
770     */
771    public boolean canMineBlock(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
772    {
773        return super.canMineBlock(par1EntityPlayer, par2, par3, par4);
774    }
775
776    public boolean canMineBlockBody(EntityPlayer par1EntityPlayer, int par2, int par3, int par4)
777    {
778        return !this.mcServer.func_96290_a(this, par2, par3, par4, par1EntityPlayer);
779    }
780
781    protected void initialize(WorldSettings par1WorldSettings)
782    {
783        if (this.entityIdMap == null)
784        {
785            this.entityIdMap = new IntHashMap();
786        }
787
788        if (this.field_73064_N == null)
789        {
790            this.field_73064_N = new HashSet();
791        }
792
793        if (this.pendingTickListEntries == null)
794        {
795            this.pendingTickListEntries = new TreeSet();
796        }
797
798        this.createSpawnPosition(par1WorldSettings);
799        super.initialize(par1WorldSettings);
800    }
801
802    /**
803     * creates a spawn position at random within 256 blocks of 0,0
804     */
805    protected void createSpawnPosition(WorldSettings par1WorldSettings)
806    {
807        if (!this.provider.canRespawnHere())
808        {
809            this.worldInfo.setSpawnPosition(0, this.provider.getAverageGroundLevel(), 0);
810        }
811        else
812        {
813            this.findingSpawnPoint = true;
814            WorldChunkManager worldchunkmanager = this.provider.worldChunkMgr;
815            List list = worldchunkmanager.getBiomesToSpawnIn();
816            Random random = new Random(this.getSeed());
817            ChunkPosition chunkposition = worldchunkmanager.findBiomePosition(0, 0, 256, list, random);
818            int i = 0;
819            int j = this.provider.getAverageGroundLevel();
820            int k = 0;
821
822            if (chunkposition != null)
823            {
824                i = chunkposition.x;
825                k = chunkposition.z;
826            }
827            else
828            {
829                this.getWorldLogAgent().logWarning("Unable to find spawn biome");
830            }
831
832            int l = 0;
833
834            while (!this.provider.canCoordinateBeSpawn(i, k))
835            {
836                i += random.nextInt(64) - random.nextInt(64);
837                k += random.nextInt(64) - random.nextInt(64);
838                ++l;
839
840                if (l == 1000)
841                {
842                    break;
843                }
844            }
845
846            this.worldInfo.setSpawnPosition(i, j, k);
847            this.findingSpawnPoint = false;
848
849            if (par1WorldSettings.isBonusChestEnabled())
850            {
851                this.createBonusChest();
852            }
853        }
854    }
855
856    /**
857     * Creates the bonus chest in the world.
858     */
859    protected void createBonusChest()
860    {
861        WorldGeneratorBonusChest worldgeneratorbonuschest = new WorldGeneratorBonusChest(ChestGenHooks.getItems(BONUS_CHEST, rand), ChestGenHooks.getCount(BONUS_CHEST, rand));
862
863        for (int i = 0; i < 10; ++i)
864        {
865            int j = this.worldInfo.getSpawnX() + this.rand.nextInt(6) - this.rand.nextInt(6);
866            int k = this.worldInfo.getSpawnZ() + this.rand.nextInt(6) - this.rand.nextInt(6);
867            int l = this.getTopSolidOrLiquidBlock(j, k) + 1;
868
869            if (worldgeneratorbonuschest.generate(this, this.rand, j, l, k))
870            {
871                break;
872            }
873        }
874    }
875
876    /**
877     * Gets the hard-coded portal location to use when entering this dimension.
878     */
879    public ChunkCoordinates getEntrancePortalLocation()
880    {
881        return this.provider.getEntrancePortalLocation();
882    }
883
884    /**
885     * Saves all chunks to disk while updating progress bar.
886     */
887    public void saveAllChunks(boolean par1, IProgressUpdate par2IProgressUpdate) throws MinecraftException
888    {
889        if (this.chunkProvider.canSave())
890        {
891            if (par2IProgressUpdate != null)
892            {
893                par2IProgressUpdate.displayProgressMessage("Saving level");
894            }
895
896            this.saveLevel();
897
898            if (par2IProgressUpdate != null)
899            {
900                par2IProgressUpdate.resetProgresAndWorkingMessage("Saving chunks");
901            }
902
903            this.chunkProvider.saveChunks(par1, par2IProgressUpdate);
904            MinecraftForge.EVENT_BUS.post(new WorldEvent.Save(this));
905        }
906    }
907
908    /**
909     * Saves the chunks to disk.
910     */
911    protected void saveLevel() throws MinecraftException
912    {
913        this.checkSessionLock();
914        this.saveHandler.saveWorldInfoWithPlayer(this.worldInfo, this.mcServer.getConfigurationManager().getHostPlayerData());
915        this.mapStorage.saveAllData();
916        this.perWorldStorage.saveAllData();
917    }
918
919    /**
920     * Start the skin for this entity downloading, if necessary, and increment its reference counter
921     */
922    protected void obtainEntitySkin(Entity par1Entity)
923    {
924        super.obtainEntitySkin(par1Entity);
925        this.entityIdMap.addKey(par1Entity.entityId, par1Entity);
926        Entity[] aentity = par1Entity.getParts();
927
928        if (aentity != null)
929        {
930            for (int i = 0; i < aentity.length; ++i)
931            {
932                this.entityIdMap.addKey(aentity[i].entityId, aentity[i]);
933            }
934        }
935    }
936
937    /**
938     * Decrement the reference counter for this entity's skin image data
939     */
940    public void releaseEntitySkin(Entity par1Entity)
941    {
942        super.releaseEntitySkin(par1Entity);
943        this.entityIdMap.removeObject(par1Entity.entityId);
944        Entity[] aentity = par1Entity.getParts();
945
946        if (aentity != null)
947        {
948            for (int i = 0; i < aentity.length; ++i)
949            {
950                this.entityIdMap.removeObject(aentity[i].entityId);
951            }
952        }
953    }
954
955    /**
956     * Returns the Entity with the given ID, or null if it doesn't exist in this World.
957     */
958    public Entity getEntityByID(int par1)
959    {
960        return (Entity)this.entityIdMap.lookup(par1);
961    }
962
963    /**
964     * adds a lightning bolt to the list of lightning bolts in this world.
965     */
966    public boolean addWeatherEffect(Entity par1Entity)
967    {
968        if (super.addWeatherEffect(par1Entity))
969        {
970            this.mcServer.getConfigurationManager().sendToAllNear(par1Entity.posX, par1Entity.posY, par1Entity.posZ, 512.0D, this.provider.dimensionId, new Packet71Weather(par1Entity));
971            return true;
972        }
973        else
974        {
975            return false;
976        }
977    }
978
979    /**
980     * sends a Packet 38 (Entity Status) to all tracked players of that entity
981     */
982    public void setEntityState(Entity par1Entity, byte par2)
983    {
984        Packet38EntityStatus packet38entitystatus = new Packet38EntityStatus(par1Entity.entityId, par2);
985        this.getEntityTracker().sendPacketToAllAssociatedPlayers(par1Entity, packet38entitystatus);
986    }
987
988    /**
989     * returns a new explosion. Does initiation (at time of writing Explosion is not finished)
990     */
991    public Explosion newExplosion(Entity par1Entity, double par2, double par4, double par6, float par8, boolean par9, boolean par10)
992    {
993        Explosion explosion = new Explosion(this, par1Entity, par2, par4, par6, par8);
994        explosion.isFlaming = par9;
995        explosion.isSmoking = par10;
996        explosion.doExplosionA();
997        explosion.doExplosionB(false);
998
999        if (!par10)
1000        {
1001            explosion.affectedBlockPositions.clear();
1002        }
1003
1004        Iterator iterator = this.playerEntities.iterator();
1005
1006        while (iterator.hasNext())
1007        {
1008            EntityPlayer entityplayer = (EntityPlayer)iterator.next();
1009
1010            if (entityplayer.getDistanceSq(par2, par4, par6) < 4096.0D)
1011            {
1012                ((EntityPlayerMP)entityplayer).playerNetServerHandler.sendPacketToPlayer(new Packet60Explosion(par2, par4, par6, par8, explosion.affectedBlockPositions, (Vec3)explosion.func_77277_b().get(entityplayer)));
1013            }
1014        }
1015
1016        return explosion;
1017    }
1018
1019    /**
1020     * Adds a block event with the given Args to the blockEventCache. During the next tick(), the block specified will
1021     * have its onBlockEvent handler called with the given parameters. Args: X,Y,Z, BlockID, EventID, EventParameter
1022     */
1023    public void addBlockEvent(int par1, int par2, int par3, int par4, int par5, int par6)
1024    {
1025        BlockEventData blockeventdata = new BlockEventData(par1, par2, par3, par4, par5, par6);
1026        Iterator iterator = this.blockEventCache[this.blockEventCacheIndex].iterator();
1027        BlockEventData blockeventdata1;
1028
1029        do
1030        {
1031            if (!iterator.hasNext())
1032            {
1033                this.blockEventCache[this.blockEventCacheIndex].add(blockeventdata);
1034                return;
1035            }
1036
1037            blockeventdata1 = (BlockEventData)iterator.next();
1038        }
1039        while (!blockeventdata1.equals(blockeventdata));
1040    }
1041
1042    /**
1043     * Send and apply locally all pending BlockEvents to each player with 64m radius of the event.
1044     */
1045    private void sendAndApplyBlockEvents()
1046    {
1047        while (!this.blockEventCache[this.blockEventCacheIndex].isEmpty())
1048        {
1049            int i = this.blockEventCacheIndex;
1050            this.blockEventCacheIndex ^= 1;
1051            Iterator iterator = this.blockEventCache[i].iterator();
1052
1053            while (iterator.hasNext())
1054            {
1055                BlockEventData blockeventdata = (BlockEventData)iterator.next();
1056
1057                if (this.onBlockEventReceived(blockeventdata))
1058                {
1059                    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()));
1060                }
1061            }
1062
1063            this.blockEventCache[i].clear();
1064        }
1065    }
1066
1067    /**
1068     * Called to apply a pending BlockEvent to apply to the current world.
1069     */
1070    private boolean onBlockEventReceived(BlockEventData par1BlockEventData)
1071    {
1072        int i = this.getBlockId(par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ());
1073        return i == par1BlockEventData.getBlockID() ? Block.blocksList[i].onBlockEventReceived(this, par1BlockEventData.getX(), par1BlockEventData.getY(), par1BlockEventData.getZ(), par1BlockEventData.getEventID(), par1BlockEventData.getEventParameter()) : false;
1074    }
1075
1076    /**
1077     * Syncs all changes to disk and wait for completion.
1078     */
1079    public void flush()
1080    {
1081        this.saveHandler.flush();
1082    }
1083
1084    /**
1085     * Updates all weather states.
1086     */
1087    protected void updateWeather()
1088    {
1089        boolean flag = this.isRaining();
1090        super.updateWeather();
1091
1092        if (flag != this.isRaining())
1093        {
1094            if (flag)
1095            {
1096                this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(2, 0));
1097            }
1098            else
1099            {
1100                this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new Packet70GameEvent(1, 0));
1101            }
1102        }
1103    }
1104
1105    /**
1106     * Gets the MinecraftServer.
1107     */
1108    public MinecraftServer getMinecraftServer()
1109    {
1110        return this.mcServer;
1111    }
1112
1113    /**
1114     * Gets the EntityTracker
1115     */
1116    public EntityTracker getEntityTracker()
1117    {
1118        return this.theEntityTracker;
1119    }
1120
1121    public PlayerManager getPlayerManager()
1122    {
1123        return this.thePlayerManager;
1124    }
1125
1126    public Teleporter getDefaultTeleporter()
1127    {
1128        return this.field_85177_Q;
1129    }
1130
1131    public File getChunkSaveLocation()
1132    {
1133        return ((AnvilChunkLoader)theChunkProviderServer.currentChunkLoader).chunkSaveLocation;
1134    }
1135}