package net.minecraft.client.multiplayer;

import net.minecraft.world.level.dimension.Dimension;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.core.Cursor3D;
import net.minecraft.world.level.LevelType;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.ReportedException;
import net.minecraft.tags.TagManager;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.EmptyTickList;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.TickList;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.item.crafting.RecipeManager;
import net.minecraft.network.protocol.Packet;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.particle.FireworkParticles;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.client.resources.sounds.SimpleSoundInstance;
import net.minecraft.client.resources.sounds.SoundInstance;
import net.minecraft.client.resources.sounds.EntityBoundSoundInstance;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.CrashReportCategory;
import net.minecraft.CrashReport;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.GameType;
import java.util.Random;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import javax.annotation.Nullable;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.entity.global.LightningBolt;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.sounds.SoundSource;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.level.LightLayer;
import net.minecraft.core.Vec3i;
import net.minecraft.world.level.block.entity.BlockEntity;
import java.util.Collection;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.util.Mth;
import java.util.Iterator;
import net.minecraft.core.Registry;
import net.minecraft.world.entity.player.Player;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import com.google.common.collect.Iterables;
import java.util.function.BooleanSupplier;
import net.minecraft.core.BlockPos;
import net.minecraft.Util;
import net.minecraft.client.renderer.BiomeColors;
import com.google.common.collect.Maps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import com.google.common.collect.Lists;
import net.minecraft.world.level.storage.LevelData;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.LevelSettings;
import net.minecraft.client.color.block.BlockTintCache;
import net.minecraft.world.level.ColorResolver;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
import java.util.Map;
import net.minecraft.world.scores.Scoreboard;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.LevelRenderer;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minecraft.world.entity.Entity;
import java.util.List;
import net.minecraft.world.level.Level;

public class ClientLevel extends Level {
    private final List<Entity> globalEntities;
    private final Int2ObjectMap<Entity> entitiesById;
    private final ClientPacketListener connection;
    private final LevelRenderer levelRenderer;
    private final Minecraft minecraft;
    private final List<AbstractClientPlayer> players;
    private int delayUntilNextMoodSound;
    private Scoreboard scoreboard;
    private final Map<String, MapItemSavedData> mapData;
    private int skyFlashTime;
    private final Object2ObjectArrayMap<ColorResolver, BlockTintCache> tintCaches;
    
    public ClientLevel(final ClientPacketListener dnp, final LevelSettings bka, final DimensionType cbi, final int integer, final ProfilerFiller aio, final LevelRenderer dqr) {
        super(new LevelData(bka, "MpServer"), cbi, (bjw, cbh) -> new ClientChunkCache(bjw, integer), aio, true);
        this.globalEntities = Lists.newArrayList();
        this.entitiesById = (Int2ObjectMap<Entity>)new Int2ObjectOpenHashMap();
        this.minecraft = Minecraft.getInstance();
        this.players = Lists.newArrayList();
        this.delayUntilNextMoodSound = this.random.nextInt(12000);
        this.scoreboard = new Scoreboard();
        this.mapData = Maps.newHashMap();
        this.tintCaches = Util.make(new Object2ObjectArrayMap(3), object2ObjectArrayMap -> {
            object2ObjectArrayMap.put(BiomeColors.GRASS_COLOR_RESOLVER, new BlockTintCache());
            object2ObjectArrayMap.put(BiomeColors.FOLIAGE_COLOR_RESOLVER, new BlockTintCache());
            object2ObjectArrayMap.put(BiomeColors.WATER_COLOR_RESOLVER, new BlockTintCache());
            return;
        });
        this.connection = dnp;
        this.levelRenderer = dqr;
        this.setSpawnPos(new BlockPos(8, 64, 8));
        this.updateSkyBrightness();
        this.prepareWeather();
    }
    
    public void tick(final BooleanSupplier booleanSupplier) {
        this.getWorldBorder().tick();
        this.tickTime();
        this.getProfiler().push("blocks");
        this.chunkSource.tick(booleanSupplier);
        this.playMoodSounds();
        this.getProfiler().pop();
    }
    
    public Iterable<Entity> entitiesForRendering() {
        return Iterables.<Entity>concat(this.entitiesById.values(), this.globalEntities);
    }
    
    public void tickEntities() {
        final ProfilerFiller aio2 = this.getProfiler();
        aio2.push("entities");
        aio2.push("global");
        for (int integer3 = 0; integer3 < this.globalEntities.size(); ++integer3) {
            final Entity akq2 = this.globalEntities.get(integer3);
            this.guardEntityTick(akq -> {
                ++akq.tickCount;
                akq.tick();
                return;
            }, akq2);
            if (akq2.removed) {
                this.globalEntities.remove(integer3--);
            }
        }
        aio2.popPush("regular");
        final ObjectIterator<Int2ObjectMap.Entry<Entity>> objectIterator3 = (ObjectIterator<Int2ObjectMap.Entry<Entity>>)this.entitiesById.int2ObjectEntrySet().iterator();
        while (objectIterator3.hasNext()) {
            final Int2ObjectMap.Entry<Entity> entry4 = (Int2ObjectMap.Entry<Entity>)objectIterator3.next();
            final Entity akq3 = (Entity)entry4.getValue();
            if (akq3.isPassenger()) {
                continue;
            }
            aio2.push("tick");
            if (!akq3.removed) {
                this.guardEntityTick(this::tickNonPassenger, akq3);
            }
            aio2.pop();
            aio2.push("remove");
            if (akq3.removed) {
                objectIterator3.remove();
                this.onEntityRemoved(akq3);
            }
            aio2.pop();
        }
        aio2.pop();
        this.tickBlockEntities();
        aio2.pop();
    }
    
    public void tickNonPassenger(final Entity akq) {
        if (!(akq instanceof Player) && !this.getChunkSource().isEntityTickingChunk(akq)) {
            return;
        }
        akq.setPosAndOldPos(akq.getX(), akq.getY(), akq.getZ());
        akq.yRotO = akq.yRot;
        akq.xRotO = akq.xRot;
        if (akq.inChunk || akq.isSpectator()) {
            ++akq.tickCount;
            this.getProfiler().push(() -> Registry.ENTITY_TYPE.getKey(akq.getType()).toString());
            akq.tick();
            this.getProfiler().pop();
        }
        this.updateChunkPos(akq);
        if (akq.inChunk) {
            for (final Entity akq2 : akq.getPassengers()) {
                this.tickPassenger(akq, akq2);
            }
        }
    }
    
    public void tickPassenger(final Entity akq1, final Entity akq2) {
        if (akq2.removed || akq2.getVehicle() != akq1) {
            akq2.stopRiding();
            return;
        }
        if (!(akq2 instanceof Player) && !this.getChunkSource().isEntityTickingChunk(akq2)) {
            return;
        }
        akq2.setPosAndOldPos(akq2.getX(), akq2.getY(), akq2.getZ());
        akq2.yRotO = akq2.yRot;
        akq2.xRotO = akq2.xRot;
        if (akq2.inChunk) {
            ++akq2.tickCount;
            akq2.rideTick();
        }
        this.updateChunkPos(akq2);
        if (akq2.inChunk) {
            for (final Entity akq3 : akq2.getPassengers()) {
                this.tickPassenger(akq2, akq3);
            }
        }
    }
    
    public void updateChunkPos(final Entity akq) {
        this.getProfiler().push("chunkCheck");
        final int integer3 = Mth.floor(akq.getX() / 16.0);
        final int integer4 = Mth.floor(akq.getY() / 16.0);
        final int integer5 = Mth.floor(akq.getZ() / 16.0);
        if (!akq.inChunk || akq.xChunk != integer3 || akq.yChunk != integer4 || akq.zChunk != integer5) {
            if (akq.inChunk && this.hasChunk(akq.xChunk, akq.zChunk)) {
                this.getChunk(akq.xChunk, akq.zChunk).removeEntity(akq, akq.yChunk);
            }
            if (akq.checkAndResetTeleportedFlag() || this.hasChunk(integer3, integer5)) {
                this.getChunk(integer3, integer5).addEntity(akq);
            }
            else {
                akq.inChunk = false;
            }
        }
        this.getProfiler().pop();
    }
    
    public void unload(final LevelChunk cal) {
        this.blockEntitiesToUnload.addAll(cal.getBlockEntities().values());
        this.chunkSource.getLightEngine().enableLightSources(cal.getPos(), false);
    }
    
    public void onChunkLoaded(final int integer1, final int integer2) {
        this.tintCaches.forEach((colorResolver, dcc) -> dcc.invalidateForChunk(integer1, integer2));
    }
    
    public void clearTintCaches() {
        this.tintCaches.forEach((colorResolver, dcc) -> dcc.invalidateAll());
    }
    
    @Override
    public boolean hasChunk(final int integer1, final int integer2) {
        return true;
    }
    
    private void playMoodSounds() {
        if (this.minecraft.player == null) {
            return;
        }
        if (this.delayUntilNextMoodSound > 0) {
            --this.delayUntilNextMoodSound;
            return;
        }
        final BlockPos fk2 = new BlockPos(this.minecraft.player);
        final BlockPos fk3 = fk2.offset(4 * (this.random.nextInt(3) - 1), 4 * (this.random.nextInt(3) - 1), 4 * (this.random.nextInt(3) - 1));
        final double double4 = fk2.distSqr(fk3);
        if (double4 >= 4.0 && double4 <= 256.0) {
            final BlockState byj6 = this.getBlockState(fk3);
            if (byj6.isAir() && this.getRawBrightness(fk3, 0) <= this.random.nextInt(8) && this.getBrightness(LightLayer.SKY, fk3) <= 0) {
                this.playLocalSound(fk3.getX() + 0.5, fk3.getY() + 0.5, fk3.getZ() + 0.5, SoundEvents.AMBIENT_CAVE, SoundSource.AMBIENT, 0.7f, 0.8f + this.random.nextFloat() * 0.2f, false);
                this.delayUntilNextMoodSound = this.random.nextInt(12000) + 6000;
            }
        }
    }
    
    public int getEntityCount() {
        return this.entitiesById.size();
    }
    
    public void addLightning(final LightningBolt avy) {
        this.globalEntities.add(avy);
    }
    
    public void addPlayer(final int integer, final AbstractClientPlayer dpy) {
        this.addEntity(integer, dpy);
        this.players.add(dpy);
    }
    
    public void putNonPlayerEntity(final int integer, final Entity akq) {
        this.addEntity(integer, akq);
    }
    
    private void addEntity(final int integer, final Entity akq) {
        this.removeEntity(integer);
        this.entitiesById.put(integer, akq);
        this.getChunkSource().getChunk(Mth.floor(akq.getX() / 16.0), Mth.floor(akq.getZ() / 16.0), ChunkStatus.FULL, true).addEntity(akq);
    }
    
    public void removeEntity(final int integer) {
        final Entity akq3 = (Entity)this.entitiesById.remove(integer);
        if (akq3 != null) {
            akq3.remove();
            this.onEntityRemoved(akq3);
        }
    }
    
    private void onEntityRemoved(final Entity akq) {
        akq.unRide();
        if (akq.inChunk) {
            this.getChunk(akq.xChunk, akq.zChunk).removeEntity(akq);
        }
        this.players.remove(akq);
    }
    
    public void reAddEntitiesToChunk(final LevelChunk cal) {
        for (final Int2ObjectMap.Entry<Entity> entry4 : this.entitiesById.int2ObjectEntrySet()) {
            final Entity akq5 = (Entity)entry4.getValue();
            final int integer6 = Mth.floor(akq5.getX() / 16.0);
            final int integer7 = Mth.floor(akq5.getZ() / 16.0);
            if (integer6 == cal.getPos().x && integer7 == cal.getPos().z) {
                cal.addEntity(akq5);
            }
        }
    }
    
    @Nullable
    @Override
    public Entity getEntity(final int integer) {
        return (Entity)this.entitiesById.get(integer);
    }
    
    public void setKnownState(final BlockPos fk, final BlockState byj) {
        this.setBlock(fk, byj, 19);
    }
    
    @Override
    public void disconnect() {
        this.connection.getConnection().disconnect(new TranslatableComponent("multiplayer.status.quitting", new Object[0]));
    }
    
    public void animateTick(final int integer1, final int integer2, final int integer3) {
        final int integer4 = 32;
        final Random random6 = new Random();
        boolean boolean7 = false;
        if (this.minecraft.gameMode.getPlayerMode() == GameType.CREATIVE) {
            for (final ItemStack ben9 : this.minecraft.player.getHandSlots()) {
                if (ben9.getItem() == Blocks.BARRIER.asItem()) {
                    boolean7 = true;
                    break;
                }
            }
        }
        final BlockPos.MutableBlockPos a8 = new BlockPos.MutableBlockPos();
        for (int integer5 = 0; integer5 < 667; ++integer5) {
            this.doAnimateTick(integer1, integer2, integer3, 16, random6, boolean7, a8);
            this.doAnimateTick(integer1, integer2, integer3, 32, random6, boolean7, a8);
        }
    }
    
    public void doAnimateTick(final int integer1, final int integer2, final int integer3, final int integer4, final Random random, final boolean boolean6, final BlockPos.MutableBlockPos a) {
        final int integer5 = integer1 + this.random.nextInt(integer4) - this.random.nextInt(integer4);
        final int integer6 = integer2 + this.random.nextInt(integer4) - this.random.nextInt(integer4);
        final int integer7 = integer3 + this.random.nextInt(integer4) - this.random.nextInt(integer4);
        a.set(integer5, integer6, integer7);
        final BlockState byj12 = this.getBlockState(a);
        byj12.getBlock().animateTick(byj12, this, a, random);
        final FluidState coj13 = this.getFluidState(a);
        if (!coj13.isEmpty()) {
            coj13.animateTick(this, a, random);
            final ParticleOptions gt14 = coj13.getDripParticle();
            if (gt14 != null && this.random.nextInt(10) == 0) {
                final boolean boolean7 = byj12.isFaceSturdy(this, a, Direction.DOWN);
                final BlockPos fk16 = a.below();
                this.trySpawnDripParticles(fk16, this.getBlockState(fk16), gt14, boolean7);
            }
        }
        if (boolean6 && byj12.getBlock() == Blocks.BARRIER) {
            this.addParticle(ParticleTypes.BARRIER, integer5 + 0.5, integer6 + 0.5, integer7 + 0.5, 0.0, 0.0, 0.0);
        }
    }
    
    private void trySpawnDripParticles(final BlockPos fk, final BlockState byj, final ParticleOptions gt, final boolean boolean4) {
        if (!byj.getFluidState().isEmpty()) {
            return;
        }
        final VoxelShape cwf6 = byj.getCollisionShape(this, fk);
        final double double7 = cwf6.max(Direction.Axis.Y);
        if (double7 < 1.0) {
            if (boolean4) {
                this.spawnFluidParticle(fk.getX(), fk.getX() + 1, fk.getZ(), fk.getZ() + 1, fk.getY() + 1 - 0.05, gt);
            }
        }
        else if (!byj.is(BlockTags.IMPERMEABLE)) {
            final double double8 = cwf6.min(Direction.Axis.Y);
            if (double8 > 0.0) {
                this.spawnParticle(fk, gt, cwf6, fk.getY() + double8 - 0.05);
            }
            else {
                final BlockPos fk2 = fk.below();
                final BlockState byj2 = this.getBlockState(fk2);
                final VoxelShape cwf7 = byj2.getCollisionShape(this, fk2);
                final double double9 = cwf7.max(Direction.Axis.Y);
                if (double9 < 1.0 && byj2.getFluidState().isEmpty()) {
                    this.spawnParticle(fk, gt, cwf6, fk.getY() - 0.05);
                }
            }
        }
    }
    
    private void spawnParticle(final BlockPos fk, final ParticleOptions gt, final VoxelShape cwf, final double double4) {
        this.spawnFluidParticle(fk.getX() + cwf.min(Direction.Axis.X), fk.getX() + cwf.max(Direction.Axis.X), fk.getZ() + cwf.min(Direction.Axis.Z), fk.getZ() + cwf.max(Direction.Axis.Z), double4, gt);
    }
    
    private void spawnFluidParticle(final double double1, final double double2, final double double3, final double double4, final double double5, final ParticleOptions gt) {
        this.addParticle(gt, Mth.lerp(this.random.nextDouble(), double1, double2), double5, Mth.lerp(this.random.nextDouble(), double3, double4), 0.0, 0.0, 0.0);
    }
    
    public void removeAllPendingEntityRemovals() {
        final ObjectIterator<Int2ObjectMap.Entry<Entity>> objectIterator2 = (ObjectIterator<Int2ObjectMap.Entry<Entity>>)this.entitiesById.int2ObjectEntrySet().iterator();
        while (objectIterator2.hasNext()) {
            final Int2ObjectMap.Entry<Entity> entry3 = (Int2ObjectMap.Entry<Entity>)objectIterator2.next();
            final Entity akq4 = (Entity)entry3.getValue();
            if (akq4.removed) {
                objectIterator2.remove();
                this.onEntityRemoved(akq4);
            }
        }
    }
    
    @Override
    public CrashReportCategory fillReportDetails(final CrashReport h) {
        final CrashReportCategory i3 = super.fillReportDetails(h);
        i3.setDetail("Server brand", () -> this.minecraft.player.getServerBrand());
        i3.setDetail("Server type", () -> (this.minecraft.getSingleplayerServer() == null) ? "Non-integrated multiplayer server" : "Integrated singleplayer server");
        return i3;
    }
    
    @Override
    public void playSound(@Nullable final Player ayj, final double double2, final double double3, final double double4, final SoundEvent aah, final SoundSource aaj, final float float7, final float float8) {
        if (ayj == this.minecraft.player) {
            this.playLocalSound(double2, double3, double4, aah, aaj, float7, float8, false);
        }
    }
    
    @Override
    public void playSound(@Nullable final Player ayj, final Entity akq, final SoundEvent aah, final SoundSource aaj, final float float5, final float float6) {
        if (ayj == this.minecraft.player) {
            this.minecraft.getSoundManager().play(new EntityBoundSoundInstance(aah, aaj, akq));
        }
    }
    
    public void playLocalSound(final BlockPos fk, final SoundEvent aah, final SoundSource aaj, final float float4, final float float5, final boolean boolean6) {
        this.playLocalSound(fk.getX() + 0.5, fk.getY() + 0.5, fk.getZ() + 0.5, aah, aaj, float4, float5, boolean6);
    }
    
    @Override
    public void playLocalSound(final double double1, final double double2, final double double3, final SoundEvent aah, final SoundSource aaj, final float float6, final float float7, final boolean boolean8) {
        final double double4 = this.minecraft.gameRenderer.getMainCamera().getPosition().distanceToSqr(double1, double2, double3);
        final SimpleSoundInstance ecv15 = new SimpleSoundInstance(aah, aaj, float6, float7, (float)double1, (float)double2, (float)double3);
        if (boolean8 && double4 > 100.0) {
            final double double5 = Math.sqrt(double4) / 40.0;
            this.minecraft.getSoundManager().playDelayed(ecv15, (int)(double5 * 20.0));
        }
        else {
            this.minecraft.getSoundManager().play(ecv15);
        }
    }
    
    @Override
    public void createFireworks(final double double1, final double double2, final double double3, final double double4, final double double5, final double double6, @Nullable final CompoundTag jt) {
        this.minecraft.particleEngine.add(new FireworkParticles.Starter(this, double1, double2, double3, double4, double5, double6, this.minecraft.particleEngine, jt));
    }
    
    @Override
    public void sendPacketToServer(final Packet<?> lt) {
        this.connection.send(lt);
    }
    
    @Override
    public RecipeManager getRecipeManager() {
        return this.connection.getRecipeManager();
    }
    
    public void setScoreboard(final Scoreboard cwl) {
        this.scoreboard = cwl;
    }
    
    @Override
    public void setDayTime(long long1) {
        if (long1 < 0L) {
            long1 = -long1;
            this.getGameRules().<GameRules.BooleanValue>getRule(GameRules.RULE_DAYLIGHT).set(false, null);
        }
        else {
            this.getGameRules().<GameRules.BooleanValue>getRule(GameRules.RULE_DAYLIGHT).set(true, null);
        }
        super.setDayTime(long1);
    }
    
    @Override
    public TickList<Block> getBlockTicks() {
        return EmptyTickList.empty();
    }
    
    @Override
    public TickList<Fluid> getLiquidTicks() {
        return EmptyTickList.empty();
    }
    
    @Override
    public ClientChunkCache getChunkSource() {
        return (ClientChunkCache)super.getChunkSource();
    }
    
    @Nullable
    @Override
    public MapItemSavedData getMapData(final String string) {
        return this.mapData.get(string);
    }
    
    @Override
    public void setMapData(final MapItemSavedData crg) {
        this.mapData.put(crg.getId(), crg);
    }
    
    @Override
    public int getFreeMapId() {
        return 0;
    }
    
    @Override
    public Scoreboard getScoreboard() {
        return this.scoreboard;
    }
    
    @Override
    public TagManager getTagManager() {
        return this.connection.getTags();
    }
    
    @Override
    public void sendBlockUpdated(final BlockPos fk, final BlockState byj2, final BlockState byj3, final int integer) {
        this.levelRenderer.blockChanged(this, fk, byj2, byj3, integer);
    }
    
    @Override
    public void setBlocksDirty(final BlockPos fk, final BlockState byj2, final BlockState byj3) {
        this.levelRenderer.setBlockDirty(fk, byj2, byj3);
    }
    
    public void setSectionDirtyWithNeighbors(final int integer1, final int integer2, final int integer3) {
        this.levelRenderer.setSectionDirtyWithNeighbors(integer1, integer2, integer3);
    }
    
    @Override
    public void destroyBlockProgress(final int integer1, final BlockPos fk, final int integer3) {
        this.levelRenderer.destroyBlockProgress(integer1, fk, integer3);
    }
    
    @Override
    public void globalLevelEvent(final int integer1, final BlockPos fk, final int integer3) {
        this.levelRenderer.globalLevelEvent(integer1, fk, integer3);
    }
    
    @Override
    public void levelEvent(@Nullable final Player ayj, final int integer2, final BlockPos fk, final int integer4) {
        try {
            this.levelRenderer.levelEvent(ayj, integer2, fk, integer4);
        }
        catch (Throwable throwable6) {
            final CrashReport h7 = CrashReport.forThrowable(throwable6, "Playing level event");
            final CrashReportCategory i8 = h7.addCategory("Level event being played");
            i8.setDetail("Block coordinates", CrashReportCategory.formatLocation(fk));
            i8.setDetail("Event source", ayj);
            i8.setDetail("Event type", integer2);
            i8.setDetail("Event data", integer4);
            throw new ReportedException(h7);
        }
    }
    
    @Override
    public void addParticle(final ParticleOptions gt, final double double2, final double double3, final double double4, final double double5, final double double6, final double double7) {
        this.levelRenderer.addParticle(gt, gt.getType().getOverrideLimiter(), double2, double3, double4, double5, double6, double7);
    }
    
    @Override
    public void addParticle(final ParticleOptions gt, final boolean boolean2, final double double3, final double double4, final double double5, final double double6, final double double7, final double double8) {
        this.levelRenderer.addParticle(gt, gt.getType().getOverrideLimiter() || boolean2, double3, double4, double5, double6, double7, double8);
    }
    
    @Override
    public void addAlwaysVisibleParticle(final ParticleOptions gt, final double double2, final double double3, final double double4, final double double5, final double double6, final double double7) {
        this.levelRenderer.addParticle(gt, false, true, double2, double3, double4, double5, double6, double7);
    }
    
    @Override
    public void addAlwaysVisibleParticle(final ParticleOptions gt, final boolean boolean2, final double double3, final double double4, final double double5, final double double6, final double double7, final double double8) {
        this.levelRenderer.addParticle(gt, gt.getType().getOverrideLimiter() || boolean2, true, double3, double4, double5, double6, double7, double8);
    }
    
    @Override
    public List<AbstractClientPlayer> players() {
        return this.players;
    }
    
    @Override
    public Biome getUncachedNoiseBiome(final int integer1, final int integer2, final int integer3) {
        return Biomes.PLAINS;
    }
    
    public float getSkyDarken(final float float1) {
        final float float2 = this.getTimeOfDay(float1);
        float float3 = 1.0f - (Mth.cos(float2 * 6.2831855f) * 2.0f + 0.2f);
        float3 = Mth.clamp(float3, 0.0f, 1.0f);
        float3 = 1.0f - float3;
        float3 *= (float)(1.0 - this.getRainLevel(float1) * 5.0f / 16.0);
        float3 *= (float)(1.0 - this.getThunderLevel(float1) * 5.0f / 16.0);
        return float3 * 0.8f + 0.2f;
    }
    
    public Vec3 getSkyColor(final BlockPos fk, final float float2) {
        final float float3 = this.getTimeOfDay(float2);
        float float4 = Mth.cos(float3 * 6.2831855f) * 2.0f + 0.5f;
        float4 = Mth.clamp(float4, 0.0f, 1.0f);
        final Biome bkt6 = this.getBiome(fk);
        final int integer7 = bkt6.getSkyColor();
        float float5 = (integer7 >> 16 & 0xFF) / 255.0f;
        float float6 = (integer7 >> 8 & 0xFF) / 255.0f;
        float float7 = (integer7 & 0xFF) / 255.0f;
        float5 *= float4;
        float6 *= float4;
        float7 *= float4;
        final float float8 = this.getRainLevel(float2);
        if (float8 > 0.0f) {
            final float float9 = (float5 * 0.3f + float6 * 0.59f + float7 * 0.11f) * 0.6f;
            final float float10 = 1.0f - float8 * 0.75f;
            float5 = float5 * float10 + float9 * (1.0f - float10);
            float6 = float6 * float10 + float9 * (1.0f - float10);
            float7 = float7 * float10 + float9 * (1.0f - float10);
        }
        final float float9 = this.getThunderLevel(float2);
        if (float9 > 0.0f) {
            final float float10 = (float5 * 0.3f + float6 * 0.59f + float7 * 0.11f) * 0.2f;
            final float float11 = 1.0f - float9 * 0.75f;
            float5 = float5 * float11 + float10 * (1.0f - float11);
            float6 = float6 * float11 + float10 * (1.0f - float11);
            float7 = float7 * float11 + float10 * (1.0f - float11);
        }
        if (this.skyFlashTime > 0) {
            float float10 = this.skyFlashTime - float2;
            if (float10 > 1.0f) {
                float10 = 1.0f;
            }
            float10 *= 0.45f;
            float5 = float5 * (1.0f - float10) + 0.8f * float10;
            float6 = float6 * (1.0f - float10) + 0.8f * float10;
            float7 = float7 * (1.0f - float10) + 1.0f * float10;
        }
        return new Vec3(float5, float6, float7);
    }
    
    public Vec3 getCloudColor(final float float1) {
        final float float2 = this.getTimeOfDay(float1);
        float float3 = Mth.cos(float2 * 6.2831855f) * 2.0f + 0.5f;
        float3 = Mth.clamp(float3, 0.0f, 1.0f);
        float float4 = 1.0f;
        float float5 = 1.0f;
        float float6 = 1.0f;
        final float float7 = this.getRainLevel(float1);
        if (float7 > 0.0f) {
            final float float8 = (float4 * 0.3f + float5 * 0.59f + float6 * 0.11f) * 0.6f;
            final float float9 = 1.0f - float7 * 0.95f;
            float4 = float4 * float9 + float8 * (1.0f - float9);
            float5 = float5 * float9 + float8 * (1.0f - float9);
            float6 = float6 * float9 + float8 * (1.0f - float9);
        }
        float4 *= float3 * 0.9f + 0.1f;
        float5 *= float3 * 0.9f + 0.1f;
        float6 *= float3 * 0.85f + 0.15f;
        final float float8 = this.getThunderLevel(float1);
        if (float8 > 0.0f) {
            final float float9 = (float4 * 0.3f + float5 * 0.59f + float6 * 0.11f) * 0.2f;
            final float float10 = 1.0f - float8 * 0.95f;
            float4 = float4 * float10 + float9 * (1.0f - float10);
            float5 = float5 * float10 + float9 * (1.0f - float10);
            float6 = float6 * float10 + float9 * (1.0f - float10);
        }
        return new Vec3(float4, float5, float6);
    }
    
    public Vec3 getFogColor(final float float1) {
        final float float2 = this.getTimeOfDay(float1);
        return this.dimension.getFogColor(float2, float1);
    }
    
    public float getStarBrightness(final float float1) {
        final float float2 = this.getTimeOfDay(float1);
        float float3 = 1.0f - (Mth.cos(float2 * 6.2831855f) * 2.0f + 0.25f);
        float3 = Mth.clamp(float3, 0.0f, 1.0f);
        return float3 * float3 * 0.5f;
    }
    
    public double getHorizonHeight() {
        if (this.levelData.getGeneratorType() == LevelType.FLAT) {
            return 0.0;
        }
        return 63.0;
    }
    
    public int getSkyFlashTime() {
        return this.skyFlashTime;
    }
    
    @Override
    public void setSkyFlashTime(final int integer) {
        this.skyFlashTime = integer;
    }
    
    @Override
    public int getBlockTint(final BlockPos fk, final ColorResolver colorResolver) {
        final BlockTintCache dcc4 = (BlockTintCache)this.tintCaches.get(colorResolver);
        return dcc4.getColor(fk, () -> this.calculateBlockTint(fk, colorResolver));
    }
    
    public int calculateBlockTint(final BlockPos fk, final ColorResolver colorResolver) {
        final int integer4 = Minecraft.getInstance().options.biomeBlendRadius;
        if (integer4 == 0) {
            return colorResolver.getColor(this.getBiome(fk), fk.getX(), fk.getZ());
        }
        final int integer5 = (integer4 * 2 + 1) * (integer4 * 2 + 1);
        int integer6 = 0;
        int integer7 = 0;
        int integer8 = 0;
        final Cursor3D fn9 = new Cursor3D(fk.getX() - integer4, fk.getY(), fk.getZ() - integer4, fk.getX() + integer4, fk.getY(), fk.getZ() + integer4);
        final BlockPos.MutableBlockPos a10 = new BlockPos.MutableBlockPos();
        while (fn9.advance()) {
            a10.set(fn9.nextX(), fn9.nextY(), fn9.nextZ());
            final int integer9 = colorResolver.getColor(this.getBiome(a10), a10.getX(), a10.getZ());
            integer6 += (integer9 & 0xFF0000) >> 16;
            integer7 += (integer9 & 0xFF00) >> 8;
            integer8 += (integer9 & 0xFF);
        }
        return (integer6 / integer5 & 0xFF) << 16 | (integer7 / integer5 & 0xFF) << 8 | (integer8 / integer5 & 0xFF);
    }
}
