    001package net.minecraft.client.multiplayer;
    002
    003import cpw.mods.fml.relauncher.Side;
    004import cpw.mods.fml.relauncher.SideOnly;
    005import java.util.HashSet;
    006import java.util.Iterator;
    007import java.util.Random;
    008import java.util.Set;
    009import net.minecraft.block.Block;
    010import net.minecraft.client.Minecraft;
    011import net.minecraft.client.particle.EntityFireworkStarterFX;
    012import net.minecraft.crash.CrashReport;
    013import net.minecraft.crash.CrashReportCategory;
    014import net.minecraft.entity.Entity;
    015import net.minecraft.entity.item.EntityMinecart;
    016import net.minecraft.entity.item.SoundUpdaterMinecart;
    017import net.minecraft.nbt.NBTTagCompound;
    018import net.minecraft.network.packet.Packet255KickDisconnect;
    019import net.minecraft.profiler.Profiler;
    020import net.minecraft.server.gui.IUpdatePlayerListBox;
    021import net.minecraft.util.IntHashMap;
    022import net.minecraft.world.ChunkCoordIntPair;
    023import net.minecraft.world.World;
    024import net.minecraft.world.WorldProvider;
    025import net.minecraft.world.WorldSettings;
    026import net.minecraft.world.chunk.Chunk;
    027import net.minecraft.world.chunk.IChunkProvider;
    028import net.minecraft.world.storage.SaveHandlerMP;
    029
    030import net.minecraftforge.common.MinecraftForge;
    031import net.minecraftforge.event.world.WorldEvent;
    032
    033@SideOnly(Side.CLIENT)
    034public class WorldClient extends World
    035{
    036    /** The packets that need to be sent to the server. */
    037    private NetClientHandler sendQueue;
    038
    039    /** The ChunkProviderClient instance */
    040    private ChunkProviderClient clientChunkProvider;
    041
    042    /**
    043     * The hash set of entities handled by this client. Uses the entity's ID as the hash set's key.
    044     */
    045    private IntHashMap entityHashSet = new IntHashMap();
    046
    047    /** Contains all entities for this client, both spawned and non-spawned. */
    048    private Set entityList = new HashSet();
    049
    050    /**
    051     * Contains all entities for this client that were not spawned due to a non-present chunk. The game will attempt to
    052     * spawn up to 10 pending entities with each subsequent tick until the spawn queue is empty.
    053     */
    054    private Set entitySpawnQueue = new HashSet();
    055    private final Minecraft mc = Minecraft.getMinecraft();
    056    private final Set previousActiveChunkSet = new HashSet();
    057
    058    public WorldClient(NetClientHandler par1NetClientHandler, WorldSettings par2WorldSettings, int par3, int par4, Profiler par5Profiler)
    059    {
    060        super(new SaveHandlerMP(), "MpServer", WorldProvider.getProviderForDimension(par3), par2WorldSettings, par5Profiler);
    061        this.sendQueue = par1NetClientHandler;
    062        this.difficultySetting = par4;
    063        this.mapStorage = par1NetClientHandler.mapStorage;
    064        this.isRemote = true;
    065        finishSetup();
    066        this.setSpawnLocation(8, 64, 8);
    067        MinecraftForge.EVENT_BUS.post(new WorldEvent.Load(this));
    068    }
    069
    070    /**
    071     * Runs a single tick for the world
    072     */
    073    public void tick()
    074    {
    075        super.tick();
    076        this.func_82738_a(this.getTotalWorldTime() + 1L);
    077        this.setWorldTime(this.getWorldTime() + 1L);
    078        this.theProfiler.startSection("reEntryProcessing");
    079
    080        for (int var1 = 0; var1 < 10 && !this.entitySpawnQueue.isEmpty(); ++var1)
    081        {
    082            Entity var2 = (Entity)this.entitySpawnQueue.iterator().next();
    083            this.entitySpawnQueue.remove(var2);
    084
    085            if (!this.loadedEntityList.contains(var2))
    086            {
    087                this.spawnEntityInWorld(var2);
    088            }
    089        }
    090
    091        this.theProfiler.endStartSection("connection");
    092        this.sendQueue.processReadPackets();
    093        this.theProfiler.endStartSection("chunkCache");
    094        this.clientChunkProvider.unload100OldestChunks();
    095        this.theProfiler.endStartSection("tiles");
    096        this.tickBlocksAndAmbiance();
    097        this.theProfiler.endSection();
    098    }
    099
    100    /**
    101     * Invalidates an AABB region of blocks from the receive queue, in the event that the block has been modified
    102     * client-side in the intervening 80 receive ticks.
    103     */
    104    public void invalidateBlockReceiveRegion(int par1, int par2, int par3, int par4, int par5, int par6) {}
    105
    106    /**
    107     * Creates the chunk provider for this world. Called in the constructor. Retrieves provider from worldProvider?
    108     */
    109    protected IChunkProvider createChunkProvider()
    110    {
    111        this.clientChunkProvider = new ChunkProviderClient(this);
    112        return this.clientChunkProvider;
    113    }
    114
    115    /**
    116     * plays random cave ambient sounds and runs updateTick on random blocks within each chunk in the vacinity of a
    117     * player
    118     */
    119    protected void tickBlocksAndAmbiance()
    120    {
    121        super.tickBlocksAndAmbiance();
    122        this.previousActiveChunkSet.retainAll(this.activeChunkSet);
    123
    124        if (this.previousActiveChunkSet.size() == this.activeChunkSet.size())
    125        {
    126            this.previousActiveChunkSet.clear();
    127        }
    128
    129        int var1 = 0;
    130        Iterator var2 = this.activeChunkSet.iterator();
    131
    132        while (var2.hasNext())
    133        {
    134            ChunkCoordIntPair var3 = (ChunkCoordIntPair)var2.next();
    135
    136            if (!this.previousActiveChunkSet.contains(var3))
    137            {
    138                int var4 = var3.chunkXPos * 16;
    139                int var5 = var3.chunkZPos * 16;
    140                this.theProfiler.startSection("getChunk");
    141                Chunk var6 = this.getChunkFromChunkCoords(var3.chunkXPos, var3.chunkZPos);
    142                this.moodSoundAndLightCheck(var4, var5, var6);
    143                this.theProfiler.endSection();
    144                this.previousActiveChunkSet.add(var3);
    145                ++var1;
    146
    147                if (var1 >= 10)
    148                {
    149                    return;
    150                }
    151            }
    152        }
    153    }
    154
    155    public void doPreChunk(int par1, int par2, boolean par3)
    156    {
    157        if (par3)
    158        {
    159            this.clientChunkProvider.loadChunk(par1, par2);
    160        }
    161        else
    162        {
    163            this.clientChunkProvider.unloadChunk(par1, par2);
    164        }
    165
    166        if (!par3)
    167        {
    168            this.markBlockRangeForRenderUpdate(par1 * 16, 0, par2 * 16, par1 * 16 + 15, 256, par2 * 16 + 15);
    169        }
    170    }
    171
    172    /**
    173     * Called to place all entities as part of a world
    174     */
    175    public boolean spawnEntityInWorld(Entity par1Entity)
    176    {
    177        boolean var2 = super.spawnEntityInWorld(par1Entity);
    178        this.entityList.add(par1Entity);
    179
    180        if (!var2)
    181        {
    182            this.entitySpawnQueue.add(par1Entity);
    183        }
    184
    185        return var2;
    186    }
    187
    188    /**
    189     * Schedule the entity for removal during the next tick. Marks the entity dead in anticipation.
    190     */
    191    public void removeEntity(Entity par1Entity)
    192    {
    193        super.removeEntity(par1Entity);
    194        this.entityList.remove(par1Entity);
    195    }
    196
    197    /**
    198     * Start the skin for this entity downloading, if necessary, and increment its reference counter
    199     */
    200    protected void obtainEntitySkin(Entity par1Entity)
    201    {
    202        super.obtainEntitySkin(par1Entity);
    203
    204        if (this.entitySpawnQueue.contains(par1Entity))
    205        {
    206            this.entitySpawnQueue.remove(par1Entity);
    207        }
    208    }
    209
    210    /**
    211     * Decrement the reference counter for this entity's skin image data
    212     */
    213    public void releaseEntitySkin(Entity par1Entity)
    214    {
    215        super.releaseEntitySkin(par1Entity);
    216
    217        if (this.entityList.contains(par1Entity))
    218        {
    219            if (par1Entity.isEntityAlive())
    220            {
    221                this.entitySpawnQueue.add(par1Entity);
    222            }
    223            else
    224            {
    225                this.entityList.remove(par1Entity);
    226            }
    227        }
    228    }
    229
    230    /**
    231     * Add an ID to Entity mapping to entityHashSet
    232     */
    233    public void addEntityToWorld(int par1, Entity par2Entity)
    234    {
    235        Entity var3 = this.getEntityByID(par1);
    236
    237        if (var3 != null)
    238        {
    239            this.removeEntity(var3);
    240        }
    241
    242        this.entityList.add(par2Entity);
    243        par2Entity.entityId = par1;
    244
    245        if (!this.spawnEntityInWorld(par2Entity))
    246        {
    247            this.entitySpawnQueue.add(par2Entity);
    248        }
    249
    250        this.entityHashSet.addKey(par1, par2Entity);
    251    }
    252
    253    /**
    254     * Returns the Entity with the given ID, or null if it doesn't exist in this World.
    255     */
    256    public Entity getEntityByID(int par1)
    257    {
    258        return (Entity)(par1 == this.mc.thePlayer.entityId ? this.mc.thePlayer : (Entity)this.entityHashSet.lookup(par1));
    259    }
    260
    261    public Entity removeEntityFromWorld(int par1)
    262    {
    263        Entity var2 = (Entity)this.entityHashSet.removeObject(par1);
    264
    265        if (var2 != null)
    266        {
    267            this.entityList.remove(var2);
    268            this.removeEntity(var2);
    269        }
    270
    271        return var2;
    272    }
    273
    274    public boolean setBlockAndMetadataAndInvalidate(int par1, int par2, int par3, int par4, int par5)
    275    {
    276        this.invalidateBlockReceiveRegion(par1, par2, par3, par1, par2, par3);
    277        return super.setBlockAndMetadataWithNotify(par1, par2, par3, par4, par5);
    278    }
    279
    280    /**
    281     * If on MP, sends a quitting packet.
    282     */
    283    public void sendQuittingDisconnectingPacket()
    284    {
    285        this.sendQueue.quitWithPacket(new Packet255KickDisconnect("Quitting"));
    286    }
    287
    288    public IUpdatePlayerListBox func_82735_a(EntityMinecart par1EntityMinecart)
    289    {
    290        return new SoundUpdaterMinecart(this.mc.sndManager, par1EntityMinecart, this.mc.thePlayer);
    291    }
    292
    293    /**
    294     * Updates all weather states.
    295     */
    296    protected void updateWeather()
    297    {
    298        super.updateWeather();
    299    }
    300
    301    @Override
    302    public void updateWeatherBody()
    303    {
    304        if (!this.provider.hasNoSky)
    305        {
    306            this.prevRainingStrength = this.rainingStrength;
    307
    308            if (this.worldInfo.isRaining())
    309            {
    310                this.rainingStrength = (float)((double)this.rainingStrength + 0.01D);
    311            }
    312            else
    313            {
    314                this.rainingStrength = (float)((double)this.rainingStrength - 0.01D);
    315            }
    316
    317            if (this.rainingStrength < 0.0F)
    318            {
    319                this.rainingStrength = 0.0F;
    320            }
    321
    322            if (this.rainingStrength > 1.0F)
    323            {
    324                this.rainingStrength = 1.0F;
    325            }
    326
    327            this.prevThunderingStrength = this.thunderingStrength;
    328
    329            if (this.worldInfo.isThundering())
    330            {
    331                this.thunderingStrength = (float)((double)this.thunderingStrength + 0.01D);
    332            }
    333            else
    334            {
    335                this.thunderingStrength = (float)((double)this.thunderingStrength - 0.01D);
    336            }
    337
    338            if (this.thunderingStrength < 0.0F)
    339            {
    340                this.thunderingStrength = 0.0F;
    341            }
    342
    343            if (this.thunderingStrength > 1.0F)
    344            {
    345                this.thunderingStrength = 1.0F;
    346            }
    347        }
    348    }
    349
    350    public void func_73029_E(int par1, int par2, int par3)
    351    {
    352        byte var4 = 16;
    353        Random var5 = new Random();
    354
    355        for (int var6 = 0; var6 < 1000; ++var6)
    356        {
    357            int var7 = par1 + this.rand.nextInt(var4) - this.rand.nextInt(var4);
    358            int var8 = par2 + this.rand.nextInt(var4) - this.rand.nextInt(var4);
    359            int var9 = par3 + this.rand.nextInt(var4) - this.rand.nextInt(var4);
    360            int var10 = this.getBlockId(var7, var8, var9);
    361
    362            if (var10 == 0 && this.rand.nextInt(8) > var8 && this.provider.getWorldHasVoidParticles())
    363            {
    364                this.spawnParticle("depthsuspend", (double)((float)var7 + this.rand.nextFloat()), (double)((float)var8 + this.rand.nextFloat()), (double)((float)var9 + this.rand.nextFloat()), 0.0D, 0.0D, 0.0D);
    365            }
    366            else if (var10 > 0)
    367            {
    368                Block.blocksList[var10].randomDisplayTick(this, var7, var8, var9, var5);
    369            }
    370        }
    371    }
    372
    373    /**
    374     * also releases skins.
    375     */
    376    public void removeAllEntities()
    377    {
    378        this.loadedEntityList.removeAll(this.unloadedEntityList);
    379        int var1;
    380        Entity var2;
    381        int var3;
    382        int var4;
    383
    384        for (var1 = 0; var1 < this.unloadedEntityList.size(); ++var1)
    385        {
    386            var2 = (Entity)this.unloadedEntityList.get(var1);
    387            var3 = var2.chunkCoordX;
    388            var4 = var2.chunkCoordZ;
    389
    390            if (var2.addedToChunk && this.chunkExists(var3, var4))
    391            {
    392                this.getChunkFromChunkCoords(var3, var4).removeEntity(var2);
    393            }
    394        }
    395
    396        for (var1 = 0; var1 < this.unloadedEntityList.size(); ++var1)
    397        {
    398            this.releaseEntitySkin((Entity)this.unloadedEntityList.get(var1));
    399        }
    400
    401        this.unloadedEntityList.clear();
    402
    403        for (var1 = 0; var1 < this.loadedEntityList.size(); ++var1)
    404        {
    405            var2 = (Entity)this.loadedEntityList.get(var1);
    406
    407            if (var2.ridingEntity != null)
    408            {
    409                if (!var2.ridingEntity.isDead && var2.ridingEntity.riddenByEntity == var2)
    410                {
    411                    continue;
    412                }
    413
    414                var2.ridingEntity.riddenByEntity = null;
    415                var2.ridingEntity = null;
    416            }
    417
    418            if (var2.isDead)
    419            {
    420                var3 = var2.chunkCoordX;
    421                var4 = var2.chunkCoordZ;
    422
    423                if (var2.addedToChunk && this.chunkExists(var3, var4))
    424                {
    425                    this.getChunkFromChunkCoords(var3, var4).removeEntity(var2);
    426                }
    427
    428                this.loadedEntityList.remove(var1--);
    429                this.releaseEntitySkin(var2);
    430            }
    431        }
    432    }
    433
    434    /**
    435     * Adds some basic stats of the world to the given crash report.
    436     */
    437    public CrashReportCategory addWorldInfoToCrashReport(CrashReport par1CrashReport)
    438    {
    439        CrashReportCategory var2 = super.addWorldInfoToCrashReport(par1CrashReport);
    440        var2.addCrashSectionCallable("Forced entities", new CallableMPL1(this));
    441        var2.addCrashSectionCallable("Retry entities", new CallableMPL2(this));
    442        return var2;
    443    }
    444
    445    /**
    446     * par8 is loudness, all pars passed to minecraftInstance.sndManager.playSound
    447     */
    448    public void playSound(double par1, double par3, double par5, String par7Str, float par8, float par9, boolean par10)
    449    {
    450        float var11 = 16.0F;
    451
    452        if (par8 > 1.0F)
    453        {
    454            var11 *= par8;
    455        }
    456
    457        double var12 = this.mc.renderViewEntity.getDistanceSq(par1, par3, par5);
    458
    459        if (var12 < (double)(var11 * var11))
    460        {
    461            if (par10 && var12 > 100.0D)
    462            {
    463                double var14 = Math.sqrt(var12) / 40.0D;
    464                this.mc.sndManager.func_92070_a(par7Str, (float)par1, (float)par3, (float)par5, par8, par9, (int)Math.round(var14 * 20.0D));
    465            }
    466            else
    467            {
    468                this.mc.sndManager.playSound(par7Str, (float)par1, (float)par3, (float)par5, par8, par9);
    469            }
    470        }
    471    }
    472
    473    public void func_92088_a(double par1, double par3, double par5, double par7, double par9, double par11, NBTTagCompound par13NBTTagCompound)
    474    {
    475        this.mc.effectRenderer.addEffect(new EntityFireworkStarterFX(this, par1, par3, par5, par7, par9, par11, this.mc.effectRenderer, par13NBTTagCompound));
    476    }
    477
    478    static Set getEntityList(WorldClient par0WorldClient)
    479    {
    480        return par0WorldClient.entityList;
    481    }
    482
    483    static Set getEntitySpawnQueue(WorldClient par0WorldClient)
    484    {
    485        return par0WorldClient.entitySpawnQueue;
    486    }
    487}