package net.minecraft.server.level;

import org.apache.logging.log4j.LogManager;
import net.minecraft.world.level.dimension.Dimension;
import net.minecraft.world.level.TickList;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.scores.Scoreboard;
import com.google.common.annotations.VisibleForTesting;
import net.minecraft.core.Vec3i;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.util.CsvOutput;
import java.io.IOException;
import java.io.Writer;
import net.minecraft.CrashReport;
import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import net.minecraft.world.entity.ReputationEventHandler;
import net.minecraft.world.entity.ai.village.ReputationEventType;
import net.minecraft.world.entity.raid.Raid;
import net.minecraft.core.SectionPos;
import net.minecraft.world.entity.ai.village.poi.PoiManager;
import java.util.Optional;
import net.minecraft.network.protocol.game.DebugPackets;
import java.util.Objects;
import net.minecraft.world.entity.ai.village.poi.PoiType;
import it.unimi.dsi.fastutil.longs.LongSets;
import net.minecraft.world.level.ForcedChunksSavedData;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.util.Unit;
import net.minecraft.world.level.saveddata.maps.MapIndex;
import net.minecraft.world.level.saveddata.SavedData;
import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
import net.minecraft.world.level.storage.DimensionDataStorage;
import net.minecraft.tags.TagManager;
import net.minecraft.world.item.crafting.RecipeManager;
import net.minecraft.core.Position;
import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
import javax.annotation.Nonnull;
import net.minecraft.network.protocol.game.ClientboundBlockEventPacket;
import net.minecraft.network.protocol.game.ClientboundExplodePacket;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.level.Explosion;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.network.protocol.game.ClientboundEntityEventPacket;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.BooleanOp;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.network.protocol.game.ClientboundLevelEventPacket;
import net.minecraft.network.protocol.game.ClientboundSoundEntityPacket;
import net.minecraft.network.protocol.game.ClientboundSoundPacket;
import net.minecraft.sounds.SoundSource;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket;
import net.minecraft.network.protocol.game.ClientboundAddGlobalEntityPacket;
import net.minecraft.util.ClassInstanceMultiMap;
import net.minecraft.Util;
import net.minecraft.world.level.block.entity.BlockEntity;
import java.util.Collection;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import java.util.function.BiFunction;
import net.minecraft.world.entity.Mob;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.minecraft.world.entity.MobCategory;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
import java.util.function.Predicate;
import net.minecraft.world.level.LevelConflictException;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.util.ProgressListener;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.level.levelgen.ChunkGeneratorSettings;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.tags.BlockTags;
import java.util.Random;
import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.TickNextTickData;
import net.minecraft.server.ServerScoreboard;
import java.util.Iterator;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.entity.global.LightningBolt;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.animal.horse.SkeletonHorse;
import net.minecraft.world.level.chunk.LevelChunk;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import net.minecraft.world.entity.LivingEntity;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import net.minecraft.world.entity.boss.EnderDragonPart;
import net.minecraft.world.entity.npc.Npc;
import net.minecraft.world.entity.animal.WaterAnimal;
import net.minecraft.world.entity.animal.Animal;
import net.minecraft.world.level.LevelType;
import net.minecraft.world.Difficulty;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
import net.minecraft.util.Mth;
import net.minecraft.world.level.GameRules;
import java.util.function.BooleanSupplier;
import net.minecraft.world.level.biome.Biome;
import com.google.common.collect.Sets;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.core.Registry;
import com.google.common.collect.Queues;
import com.google.common.collect.Maps;
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
import com.google.common.collect.Lists;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.storage.LevelData;
import java.util.concurrent.Executor;
import javax.annotation.Nullable;
import net.minecraft.world.entity.npc.WanderingTraderSpawner;
import net.minecraft.world.level.BlockEventData;
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
import net.minecraft.world.entity.raid.Raids;
import net.minecraft.world.entity.ai.navigation.PathNavigation;
import java.util.Set;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.ServerTickList;
import net.minecraft.world.level.PortalForcer;
import net.minecraft.world.level.storage.LevelStorage;
import net.minecraft.server.MinecraftServer;
import java.util.Queue;
import java.util.UUID;
import java.util.Map;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minecraft.world.entity.Entity;
import java.util.List;
import org.apache.logging.log4j.Logger;
import net.minecraft.world.level.Level;

public class ServerLevel extends Level {
    private static final Logger LOGGER;
    private final List<Entity> globalEntities;
    private final Int2ObjectMap<Entity> entitiesById;
    private final Map<UUID, Entity> entitiesByUuid;
    private final Queue<Entity> toAddAfterTick;
    private final List<ServerPlayer> players;
    boolean tickingEntities;
    private final MinecraftServer server;
    private final LevelStorage levelStorage;
    public boolean noSave;
    private boolean allPlayersSleeping;
    private int emptyTime;
    private final PortalForcer portalForcer;
    private final ServerTickList<Block> blockTicks;
    private final ServerTickList<Fluid> liquidTicks;
    private final Set<PathNavigation> navigations;
    protected final Raids raids;
    private final ObjectLinkedOpenHashSet<BlockEventData> blockEvents;
    private boolean handlingTick;
    @Nullable
    private final WanderingTraderSpawner wanderingTraderSpawner;
    
    public ServerLevel(final MinecraftServer minecraftServer, final Executor executor, final LevelStorage crn, final LevelData crm, final DimensionType cbi, final ProfilerFiller aio, final ChunkProgressListener xm) {
        super(crm, cbi, (bjw, cbh) -> new ServerChunkCache(bjw, crn.getFolder(), crn.getFixerUpper(), crn.getStructureManager(), executor, cbh.createRandomLevelGenerator(), minecraftServer.getPlayerList().getViewDistance(), xm, () -> minecraftServer.getLevel(DimensionType.OVERWORLD).getDataStorage()), aio, false);
        this.globalEntities = Lists.newArrayList();
        this.entitiesById = (Int2ObjectMap<Entity>)new Int2ObjectLinkedOpenHashMap();
        this.entitiesByUuid = Maps.newHashMap();
        this.toAddAfterTick = Queues.newArrayDeque();
        this.players = Lists.newArrayList();
        this.blockTicks = new ServerTickList<Block>(this, bph -> bph == null || bph.defaultBlockState().isAir(), Registry.BLOCK::getKey, Registry.BLOCK::get, this::tickBlock);
        this.liquidTicks = new ServerTickList<Fluid>(this, coi -> coi == null || coi == Fluids.EMPTY, Registry.FLUID::getKey, Registry.FLUID::get, this::tickLiquid);
        this.navigations = Sets.newHashSet();
        this.blockEvents = (ObjectLinkedOpenHashSet<BlockEventData>)new ObjectLinkedOpenHashSet();
        this.levelStorage = crn;
        this.server = minecraftServer;
        this.portalForcer = new PortalForcer(this);
        this.updateSkyBrightness();
        this.prepareWeather();
        this.getWorldBorder().setAbsoluteMaxSize(minecraftServer.getAbsoluteMaxWorldSize());
        this.raids = this.getDataStorage().<Raids>computeIfAbsent(() -> new Raids(this), Raids.getFileId(this.dimension));
        if (!minecraftServer.isSingleplayer()) {
            this.getLevelData().setGameType(minecraftServer.getDefaultGameType());
        }
        this.wanderingTraderSpawner = ((this.dimension.getType() == DimensionType.OVERWORLD) ? new WanderingTraderSpawner(this) : null);
    }
    
    @Override
    public Biome getUncachedNoiseBiome(final int integer1, final int integer2, final int integer3) {
        return this.getChunkSource().getGenerator().getBiomeSource().getNoiseBiome(integer1, integer2, integer3);
    }
    
    public void tick(final BooleanSupplier booleanSupplier) {
        final ProfilerFiller aio3 = this.getProfiler();
        this.handlingTick = true;
        aio3.push("world border");
        this.getWorldBorder().tick();
        aio3.popPush("weather");
        final boolean boolean4 = this.isRaining();
        if (this.dimension.isHasSkyLight()) {
            if (this.getGameRules().getBoolean(GameRules.RULE_WEATHER_CYCLE)) {
                int integer5 = this.levelData.getClearWeatherTime();
                int integer6 = this.levelData.getThunderTime();
                int integer7 = this.levelData.getRainTime();
                boolean boolean5 = this.levelData.isThundering();
                boolean boolean6 = this.levelData.isRaining();
                if (integer5 > 0) {
                    --integer5;
                    integer6 = (boolean5 ? 0 : 1);
                    integer7 = (boolean6 ? 0 : 1);
                    boolean5 = false;
                    boolean6 = false;
                }
                else {
                    if (integer6 > 0) {
                        if (--integer6 == 0) {
                            boolean5 = !boolean5;
                        }
                    }
                    else if (boolean5) {
                        integer6 = this.random.nextInt(12000) + 3600;
                    }
                    else {
                        integer6 = this.random.nextInt(168000) + 12000;
                    }
                    if (integer7 > 0) {
                        if (--integer7 == 0) {
                            boolean6 = !boolean6;
                        }
                    }
                    else if (boolean6) {
                        integer7 = this.random.nextInt(12000) + 12000;
                    }
                    else {
                        integer7 = this.random.nextInt(168000) + 12000;
                    }
                }
                this.levelData.setThunderTime(integer6);
                this.levelData.setRainTime(integer7);
                this.levelData.setClearWeatherTime(integer5);
                this.levelData.setThundering(boolean5);
                this.levelData.setRaining(boolean6);
            }
            this.oThunderLevel = this.thunderLevel;
            if (this.levelData.isThundering()) {
                this.thunderLevel += (float)0.01;
            }
            else {
                this.thunderLevel -= (float)0.01;
            }
            this.thunderLevel = Mth.clamp(this.thunderLevel, 0.0f, 1.0f);
            this.oRainLevel = this.rainLevel;
            if (this.levelData.isRaining()) {
                this.rainLevel += (float)0.01;
            }
            else {
                this.rainLevel -= (float)0.01;
            }
            this.rainLevel = Mth.clamp(this.rainLevel, 0.0f, 1.0f);
        }
        if (this.oRainLevel != this.rainLevel) {
            this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(7, this.rainLevel), this.dimension.getType());
        }
        if (this.oThunderLevel != this.thunderLevel) {
            this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(8, this.thunderLevel), this.dimension.getType());
        }
        if (boolean4 != this.isRaining()) {
            if (boolean4) {
                this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(2, 0.0f));
            }
            else {
                this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(1, 0.0f));
            }
            this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(7, this.rainLevel));
            this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(8, this.thunderLevel));
        }
        if (this.getLevelData().isHardcore() && this.getDifficulty() != Difficulty.HARD) {
            this.getLevelData().setDifficulty(Difficulty.HARD);
        }
        if (this.allPlayersSleeping && this.players.stream().noneMatch(xe -> !xe.isSpectator() && !xe.isSleepingLongEnough())) {
            this.allPlayersSleeping = false;
            if (this.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)) {
                final long long5 = this.levelData.getDayTime() + 24000L;
                this.setDayTime(long5 - long5 % 24000L);
            }
            this.wakeUpAllPlayers();
            if (this.getGameRules().getBoolean(GameRules.RULE_WEATHER_CYCLE)) {
                this.stopWeather();
            }
        }
        this.updateSkyBrightness();
        this.tickTime();
        aio3.popPush("chunkSource");
        this.getChunkSource().tick(booleanSupplier);
        aio3.popPush("tickPending");
        if (this.levelData.getGeneratorType() != LevelType.DEBUG_ALL_BLOCK_STATES) {
            this.blockTicks.tick();
            this.liquidTicks.tick();
        }
        aio3.popPush("raid");
        this.raids.tick();
        if (this.wanderingTraderSpawner != null) {
            this.wanderingTraderSpawner.tick();
        }
        aio3.popPush("blockEvents");
        this.runBlockEvents();
        this.handlingTick = false;
        aio3.popPush("entities");
        final boolean boolean7 = !this.players.isEmpty() || !this.getForcedChunks().isEmpty();
        if (boolean7) {
            this.resetEmptyTime();
        }
        if (boolean7 || this.emptyTime++ < 300) {
            this.dimension.tick();
            aio3.push("global");
            for (int integer6 = 0; integer6 < this.globalEntities.size(); ++integer6) {
                final Entity akq2 = this.globalEntities.get(integer6);
                this.guardEntityTick(akq -> {
                    ++akq.tickCount;
                    akq.tick();
                    return;
                }, akq2);
                if (akq2.removed) {
                    this.globalEntities.remove(integer6--);
                }
            }
            aio3.popPush("regular");
            this.tickingEntities = true;
            final ObjectIterator<Int2ObjectMap.Entry<Entity>> objectIterator6 = (ObjectIterator<Int2ObjectMap.Entry<Entity>>)this.entitiesById.int2ObjectEntrySet().iterator();
            while (objectIterator6.hasNext()) {
                final Int2ObjectMap.Entry<Entity> entry7 = (Int2ObjectMap.Entry<Entity>)objectIterator6.next();
                final Entity akq3 = (Entity)entry7.getValue();
                final Entity akq4 = akq3.getVehicle();
                if (!this.server.isAnimals() && (akq3 instanceof Animal || akq3 instanceof WaterAnimal)) {
                    akq3.remove();
                }
                if (!this.server.isNpcsEnabled() && akq3 instanceof Npc) {
                    akq3.remove();
                }
                aio3.push("checkDespawn");
                if (!akq3.removed) {
                    akq3.checkDespawn();
                }
                aio3.pop();
                if (akq4 != null) {
                    if (!akq4.removed && akq4.hasPassenger(akq3)) {
                        continue;
                    }
                    akq3.stopRiding();
                }
                aio3.push("tick");
                if (!akq3.removed && !(akq3 instanceof EnderDragonPart)) {
                    this.guardEntityTick(this::tickNonPassenger, akq3);
                }
                aio3.pop();
                aio3.push("remove");
                if (akq3.removed) {
                    this.removeFromChunk(akq3);
                    objectIterator6.remove();
                    this.onEntityRemoved(akq3);
                }
                aio3.pop();
            }
            this.tickingEntities = false;
            Entity akq2;
            while ((akq2 = this.toAddAfterTick.poll()) != null) {
                this.add(akq2);
            }
            aio3.pop();
            this.tickBlockEntities();
        }
        aio3.pop();
    }
    
    private void wakeUpAllPlayers() {
        this.players.stream().filter(LivingEntity::isSleeping).collect(Collectors.toList()).forEach(xe -> xe.stopSleepInBed(false, false));
    }
    
    public void tickChunk(final LevelChunk cal, final int integer) {
        final ChunkPos bjh4 = cal.getPos();
        final boolean boolean5 = this.isRaining();
        final int integer2 = bjh4.getMinBlockX();
        final int integer3 = bjh4.getMinBlockZ();
        final ProfilerFiller aio8 = this.getProfiler();
        aio8.push("thunder");
        if (boolean5 && this.isThundering() && this.random.nextInt(100000) == 0) {
            final BlockPos fk9 = this.findLightingTargetAround(this.getBlockRandomPos(integer2, 0, integer3, 15));
            if (this.isRainingAt(fk9)) {
                final DifficultyInstance ajj10 = this.getCurrentDifficultyAt(fk9);
                final boolean boolean6 = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && this.random.nextDouble() < ajj10.getEffectiveDifficulty() * 0.01;
                if (boolean6) {
                    final SkeletonHorse auj12 = EntityType.SKELETON_HORSE.create(this);
                    auj12.setTrap(true);
                    auj12.setAge(0);
                    auj12.setPos(fk9.getX(), fk9.getY(), fk9.getZ());
                    this.addFreshEntity(auj12);
                }
                this.addGlobalEntity(new LightningBolt(this, fk9.getX() + 0.5, fk9.getY(), fk9.getZ() + 0.5, boolean6));
            }
        }
        aio8.popPush("iceandsnow");
        if (this.random.nextInt(16) == 0) {
            final BlockPos fk9 = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, this.getBlockRandomPos(integer2, 0, integer3, 15));
            final BlockPos fk10 = fk9.below();
            final Biome bkt11 = this.getBiome(fk9);
            if (bkt11.shouldFreeze(this, fk10)) {
                this.setBlockAndUpdate(fk10, Blocks.ICE.defaultBlockState());
            }
            if (boolean5 && bkt11.shouldSnow(this, fk9)) {
                this.setBlockAndUpdate(fk9, Blocks.SNOW.defaultBlockState());
            }
            if (boolean5 && this.getBiome(fk10).getPrecipitation() == Biome.Precipitation.RAIN) {
                this.getBlockState(fk10).getBlock().handleRain(this, fk10);
            }
        }
        aio8.popPush("tickBlocks");
        if (integer > 0) {
            for (final LevelChunkSection cam12 : cal.getSections()) {
                if (cam12 != LevelChunk.EMPTY_SECTION && cam12.isRandomlyTicking()) {
                    final int integer4 = cam12.bottomBlockY();
                    for (int integer5 = 0; integer5 < integer; ++integer5) {
                        final BlockPos fk11 = this.getBlockRandomPos(integer2, integer4, integer3, 15);
                        aio8.push("randomTick");
                        final BlockState byj16 = cam12.getBlockState(fk11.getX() - integer2, fk11.getY() - integer4, fk11.getZ() - integer3);
                        if (byj16.isRandomlyTicking()) {
                            byj16.randomTick(this, fk11, this.random);
                        }
                        final FluidState coj17 = byj16.getFluidState();
                        if (coj17.isRandomlyTicking()) {
                            coj17.randomTick(this, fk11, this.random);
                        }
                        aio8.pop();
                    }
                }
            }
        }
        aio8.pop();
    }
    
    protected BlockPos findLightingTargetAround(final BlockPos fk) {
        BlockPos fk2 = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, fk);
        final AABB cvf4 = new AABB(fk2, new BlockPos(fk2.getX(), this.getMaxBuildHeight(), fk2.getZ())).inflate(3.0);
        final List<LivingEntity> list5 = this.<LivingEntity>getEntitiesOfClass(LivingEntity.class, cvf4, akz -> akz != null && akz.isAlive() && this.canSeeSky(akz.getCommandSenderBlockPosition()));
        if (!list5.isEmpty()) {
            return list5.get(this.random.nextInt(list5.size())).getCommandSenderBlockPosition();
        }
        if (fk2.getY() == -1) {
            fk2 = fk2.above(2);
        }
        return fk2;
    }
    
    public boolean isHandlingTick() {
        return this.handlingTick;
    }
    
    public void updateSleepingPlayerList() {
        this.allPlayersSleeping = false;
        if (!this.players.isEmpty()) {
            int integer2 = 0;
            int integer3 = 0;
            for (final ServerPlayer xe5 : this.players) {
                if (xe5.isSpectator()) {
                    ++integer2;
                }
                else {
                    if (!xe5.isSleeping()) {
                        continue;
                    }
                    ++integer3;
                }
            }
            this.allPlayersSleeping = (integer3 > 0 && integer3 >= this.players.size() - integer2);
        }
    }
    
    @Override
    public ServerScoreboard getScoreboard() {
        return this.server.getScoreboard();
    }
    
    private void stopWeather() {
        this.levelData.setRainTime(0);
        this.levelData.setRaining(false);
        this.levelData.setThunderTime(0);
        this.levelData.setThundering(false);
    }
    
    @Override
    public void validateSpawn() {
        if (this.levelData.getYSpawn() <= 0) {
            this.levelData.setYSpawn(this.getSeaLevel() + 1);
        }
        int integer2 = this.levelData.getXSpawn();
        int integer3 = this.levelData.getZSpawn();
        int integer4 = 0;
        while (this.getTopBlockState(new BlockPos(integer2, 0, integer3)).isAir()) {
            integer2 += this.random.nextInt(8) - this.random.nextInt(8);
            integer3 += this.random.nextInt(8) - this.random.nextInt(8);
            if (++integer4 == 10000) {
                break;
            }
        }
        this.levelData.setXSpawn(integer2);
        this.levelData.setZSpawn(integer3);
    }
    
    public void resetEmptyTime() {
        this.emptyTime = 0;
    }
    
    private void tickLiquid(final TickNextTickData<Fluid> bkm) {
        final FluidState coj3 = this.getFluidState(bkm.pos);
        if (coj3.getType() == bkm.getType()) {
            coj3.tick(this, bkm.pos);
        }
    }
    
    private void tickBlock(final TickNextTickData<Block> bkm) {
        final BlockState byj3 = this.getBlockState(bkm.pos);
        if (byj3.getBlock() == bkm.getType()) {
            byj3.tick(this, bkm.pos, this.random);
        }
    }
    
    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.tickCount;
            final ProfilerFiller aio3 = this.getProfiler();
            aio3.push(() -> Registry.ENTITY_TYPE.getKey(akq.getType()).toString());
            aio3.incrementCounter("tickNonPassenger");
            akq.tick();
            aio3.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;
            final ProfilerFiller aio4 = this.getProfiler();
            aio4.push(() -> Registry.ENTITY_TYPE.getKey(akq2.getType()).toString());
            aio4.incrementCounter("tickPassenger");
            akq2.rideTick();
            aio4.pop();
        }
        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();
    }
    
    @Override
    public boolean mayInteract(final Player ayj, final BlockPos fk) {
        return !this.server.isUnderSpawnProtection(this, fk, ayj) && this.getWorldBorder().isWithinBounds(fk);
    }
    
    public void setInitialSpawn(final LevelSettings bka) {
        if (!this.dimension.mayRespawn()) {
            this.levelData.setSpawn(BlockPos.ZERO.above(this.getChunkSource().getGenerator().getSpawnHeight()));
            return;
        }
        if (this.levelData.getGeneratorType() == LevelType.DEBUG_ALL_BLOCK_STATES) {
            this.levelData.setSpawn(BlockPos.ZERO.above());
            return;
        }
        final BiomeSource bkw3 = this.getChunkSource().getGenerator().getBiomeSource();
        final List<Biome> list4 = bkw3.getPlayerSpawnBiomes();
        final Random random5 = new Random(this.getSeed());
        final BlockPos fk6 = bkw3.findBiomeHorizontal(0, this.getSeaLevel(), 0, 256, list4, random5);
        final ChunkPos bjh7 = (fk6 == null) ? new ChunkPos(0, 0) : new ChunkPos(fk6);
        if (fk6 == null) {
            ServerLevel.LOGGER.warn("Unable to find spawn biome");
        }
        boolean boolean8 = false;
        for (final Block bph10 : BlockTags.VALID_SPAWN.getValues()) {
            if (bkw3.getSurfaceBlocks().contains(bph10.defaultBlockState())) {
                boolean8 = true;
                break;
            }
        }
        this.levelData.setSpawn(bjh7.getWorldPosition().offset(8, this.getChunkSource().getGenerator().getSpawnHeight(), 8));
        int integer9 = 0;
        int integer10 = 0;
        int integer11 = 0;
        int integer12 = -1;
        final int integer13 = 32;
        for (int integer14 = 0; integer14 < 1024; ++integer14) {
            if (integer9 > -16 && integer9 <= 16 && integer10 > -16 && integer10 <= 16) {
                final BlockPos fk7 = this.dimension.getSpawnPosInChunk(new ChunkPos(bjh7.x + integer9, bjh7.z + integer10), boolean8);
                if (fk7 != null) {
                    this.levelData.setSpawn(fk7);
                    break;
                }
            }
            if (integer9 == integer10 || (integer9 < 0 && integer9 == -integer10) || (integer9 > 0 && integer9 == 1 - integer10)) {
                final int integer15 = integer11;
                integer11 = -integer12;
                integer12 = integer15;
            }
            integer9 += integer11;
            integer10 += integer12;
        }
        if (bka.hasStartingBonusItems()) {
            this.generateBonusItemsNearSpawn();
        }
    }
    
    protected void generateBonusItemsNearSpawn() {
        final ConfiguredFeature<?, ?> cdc2 = Feature.BONUS_CHEST.configured(FeatureConfiguration.NONE);
        cdc2.place(this, this.getChunkSource().getGenerator(), this.random, new BlockPos(this.levelData.getXSpawn(), this.levelData.getYSpawn(), this.levelData.getZSpawn()));
    }
    
    @Nullable
    public BlockPos getDimensionSpecificSpawn() {
        return this.dimension.getDimensionSpecificSpawn();
    }
    
    public void save(@Nullable final ProgressListener abs, final boolean boolean2, final boolean boolean3) throws LevelConflictException {
        final ServerChunkCache xb5 = this.getChunkSource();
        if (boolean3) {
            return;
        }
        if (abs != null) {
            abs.progressStartNoAbort(new TranslatableComponent("menu.savingLevel", new Object[0]));
        }
        this.saveLevelData();
        if (abs != null) {
            abs.progressStage(new TranslatableComponent("menu.savingChunks", new Object[0]));
        }
        xb5.save(boolean2);
    }
    
    protected void saveLevelData() throws LevelConflictException {
        this.checkSession();
        this.dimension.saveData();
        this.getChunkSource().getDataStorage().save();
    }
    
    public List<Entity> getEntities(@Nullable final EntityType<?> aku, final Predicate<? super Entity> predicate) {
        final List<Entity> list4 = Lists.newArrayList();
        final ServerChunkCache xb5 = this.getChunkSource();
        for (final Entity akq7 : this.entitiesById.values()) {
            if ((aku == null || akq7.getType() == aku) && xb5.hasChunk(Mth.floor(akq7.getX()) >> 4, Mth.floor(akq7.getZ()) >> 4) && predicate.test(akq7)) {
                list4.add(akq7);
            }
        }
        return list4;
    }
    
    public List<EnderDragon> getDragons() {
        final List<EnderDragon> list2 = Lists.newArrayList();
        for (final Entity akq4 : this.entitiesById.values()) {
            if (akq4 instanceof EnderDragon && akq4.isAlive()) {
                list2.add((EnderDragon)akq4);
            }
        }
        return list2;
    }
    
    public List<ServerPlayer> getPlayers(final Predicate<? super ServerPlayer> predicate) {
        final List<ServerPlayer> list3 = Lists.newArrayList();
        for (final ServerPlayer xe5 : this.players) {
            if (predicate.test(xe5)) {
                list3.add(xe5);
            }
        }
        return list3;
    }
    
    @Nullable
    public ServerPlayer getRandomPlayer() {
        final List<ServerPlayer> list2 = this.getPlayers(LivingEntity::isAlive);
        if (list2.isEmpty()) {
            return null;
        }
        return list2.get(this.random.nextInt(list2.size()));
    }
    
    public Object2IntMap<MobCategory> getMobCategoryCounts() {
        final Object2IntMap<MobCategory> object2IntMap2 = (Object2IntMap<MobCategory>)new Object2IntOpenHashMap();
        for (final Entity akq4 : this.entitiesById.values()) {
            if (akq4 instanceof Mob) {
                final Mob ala5 = (Mob)akq4;
                if (ala5.isPersistenceRequired()) {
                    continue;
                }
                if (ala5.requiresCustomPersistence()) {
                    continue;
                }
            }
            final MobCategory alb5 = akq4.getType().getCategory();
            if (alb5 == MobCategory.MISC) {
                continue;
            }
            if (!this.getChunkSource().isInAccessibleChunk(akq4)) {
                continue;
            }
            object2IntMap2.mergeInt(alb5, 1, (BiFunction)Integer::sum);
        }
        return object2IntMap2;
    }
    
    @Override
    public boolean addFreshEntity(final Entity akq) {
        return this.addEntity(akq);
    }
    
    public boolean addWithUUID(final Entity akq) {
        return this.addEntity(akq);
    }
    
    public void addFromAnotherDimension(final Entity akq) {
        final boolean boolean3 = akq.forcedLoading;
        akq.forcedLoading = true;
        this.addWithUUID(akq);
        akq.forcedLoading = boolean3;
        this.updateChunkPos(akq);
    }
    
    public void addDuringCommandTeleport(final ServerPlayer xe) {
        this.addPlayer(xe);
        this.updateChunkPos(xe);
    }
    
    public void addDuringPortalTeleport(final ServerPlayer xe) {
        this.addPlayer(xe);
        this.updateChunkPos(xe);
    }
    
    public void addNewPlayer(final ServerPlayer xe) {
        this.addPlayer(xe);
    }
    
    public void addRespawnedPlayer(final ServerPlayer xe) {
        this.addPlayer(xe);
    }
    
    private void addPlayer(final ServerPlayer xe) {
        final Entity akq3 = this.entitiesByUuid.get(xe.getUUID());
        if (akq3 != null) {
            ServerLevel.LOGGER.warn("Force-added player with duplicate UUID {}", xe.getUUID().toString());
            akq3.unRide();
            this.removePlayerImmediately((ServerPlayer)akq3);
        }
        this.players.add(xe);
        this.updateSleepingPlayerList();
        final ChunkAccess bzy4 = this.getChunk(Mth.floor(xe.getX() / 16.0), Mth.floor(xe.getZ() / 16.0), ChunkStatus.FULL, true);
        if (bzy4 instanceof LevelChunk) {
            bzy4.addEntity(xe);
        }
        this.add(xe);
    }
    
    private boolean addEntity(final Entity akq) {
        if (akq.removed) {
            ServerLevel.LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityType.getKey(akq.getType()));
            return false;
        }
        if (this.isUUIDUsed(akq)) {
            return false;
        }
        final ChunkAccess bzy3 = this.getChunk(Mth.floor(akq.getX() / 16.0), Mth.floor(akq.getZ() / 16.0), ChunkStatus.FULL, akq.forcedLoading);
        if (!(bzy3 instanceof LevelChunk)) {
            return false;
        }
        bzy3.addEntity(akq);
        this.add(akq);
        return true;
    }
    
    public boolean loadFromChunk(final Entity akq) {
        if (this.isUUIDUsed(akq)) {
            return false;
        }
        this.add(akq);
        return true;
    }
    
    private boolean isUUIDUsed(final Entity akq) {
        final Entity akq2 = this.entitiesByUuid.get(akq.getUUID());
        if (akq2 == null) {
            return false;
        }
        ServerLevel.LOGGER.warn("Keeping entity {} that already exists with UUID {}", EntityType.getKey(akq2.getType()), akq.getUUID().toString());
        return true;
    }
    
    public void unload(final LevelChunk cal) {
        this.blockEntitiesToUnload.addAll(cal.getBlockEntities().values());
        for (final ClassInstanceMultiMap<Entity> abe6 : cal.getEntitySections()) {
            for (final Entity akq8 : abe6) {
                if (akq8 instanceof ServerPlayer) {
                    continue;
                }
                if (this.tickingEntities) {
                    throw Util.<IllegalStateException>pauseInIde(new IllegalStateException("Removing entity while ticking!"));
                }
                this.entitiesById.remove(akq8.getId());
                this.onEntityRemoved(akq8);
            }
        }
    }
    
    public void onEntityRemoved(final Entity akq) {
        if (akq instanceof EnderDragon) {
            for (final EnderDragonPart auq6 : ((EnderDragon)akq).getSubEntities()) {
                auq6.remove();
            }
        }
        this.entitiesByUuid.remove(akq.getUUID());
        this.getChunkSource().removeEntity(akq);
        if (akq instanceof ServerPlayer) {
            final ServerPlayer xe3 = (ServerPlayer)akq;
            this.players.remove(xe3);
        }
        this.getScoreboard().entityRemoved(akq);
        if (akq instanceof Mob) {
            this.navigations.remove(((Mob)akq).getNavigation());
        }
    }
    
    private void add(final Entity akq) {
        if (this.tickingEntities) {
            this.toAddAfterTick.add(akq);
        }
        else {
            this.entitiesById.put(akq.getId(), akq);
            if (akq instanceof EnderDragon) {
                for (final EnderDragonPart auq6 : ((EnderDragon)akq).getSubEntities()) {
                    this.entitiesById.put(auq6.getId(), auq6);
                }
            }
            this.entitiesByUuid.put(akq.getUUID(), akq);
            this.getChunkSource().addEntity(akq);
            if (akq instanceof Mob) {
                this.navigations.add(((Mob)akq).getNavigation());
            }
        }
    }
    
    public void despawn(final Entity akq) {
        if (this.tickingEntities) {
            throw Util.<IllegalStateException>pauseInIde(new IllegalStateException("Removing entity while ticking!"));
        }
        this.removeFromChunk(akq);
        this.entitiesById.remove(akq.getId());
        this.onEntityRemoved(akq);
    }
    
    private void removeFromChunk(final Entity akq) {
        final ChunkAccess bzy3 = this.getChunk(akq.xChunk, akq.zChunk, ChunkStatus.FULL, false);
        if (bzy3 instanceof LevelChunk) {
            ((LevelChunk)bzy3).removeEntity(akq);
        }
    }
    
    public void removePlayerImmediately(final ServerPlayer xe) {
        xe.remove();
        this.despawn(xe);
        this.updateSleepingPlayerList();
    }
    
    public void addGlobalEntity(final LightningBolt avy) {
        this.globalEntities.add(avy);
        this.server.getPlayerList().broadcast(null, avy.getX(), avy.getY(), avy.getZ(), 512.0, this.dimension.getType(), new ClientboundAddGlobalEntityPacket(avy));
    }
    
    @Override
    public void destroyBlockProgress(final int integer1, final BlockPos fk, final int integer3) {
        for (final ServerPlayer xe6 : this.server.getPlayerList().getPlayers()) {
            if (xe6 != null && xe6.level == this) {
                if (xe6.getId() == integer1) {
                    continue;
                }
                final double double7 = fk.getX() - xe6.getX();
                final double double8 = fk.getY() - xe6.getY();
                final double double9 = fk.getZ() - xe6.getZ();
                if (double7 * double7 + double8 * double8 + double9 * double9 >= 1024.0) {
                    continue;
                }
                xe6.connection.send(new ClientboundBlockDestructionPacket(integer1, fk, integer3));
            }
        }
    }
    
    @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) {
        this.server.getPlayerList().broadcast(ayj, double2, double3, double4, (float7 > 1.0f) ? ((double)(16.0f * float7)) : 16.0, this.dimension.getType(), new ClientboundSoundPacket(aah, aaj, double2, double3, double4, float7, float8));
    }
    
    @Override
    public void playSound(@Nullable final Player ayj, final Entity akq, final SoundEvent aah, final SoundSource aaj, final float float5, final float float6) {
        this.server.getPlayerList().broadcast(ayj, akq.getX(), akq.getY(), akq.getZ(), (float5 > 1.0f) ? ((double)(16.0f * float5)) : 16.0, this.dimension.getType(), new ClientboundSoundEntityPacket(aah, aaj, akq, float5, float6));
    }
    
    @Override
    public void globalLevelEvent(final int integer1, final BlockPos fk, final int integer3) {
        this.server.getPlayerList().broadcastAll(new ClientboundLevelEventPacket(integer1, fk, integer3, true));
    }
    
    @Override
    public void levelEvent(@Nullable final Player ayj, final int integer2, final BlockPos fk, final int integer4) {
        this.server.getPlayerList().broadcast(ayj, fk.getX(), fk.getY(), fk.getZ(), 64.0, this.dimension.getType(), new ClientboundLevelEventPacket(integer2, fk, integer4, false));
    }
    
    @Override
    public void sendBlockUpdated(final BlockPos fk, final BlockState byj2, final BlockState byj3, final int integer) {
        this.getChunkSource().blockChanged(fk);
        final VoxelShape cwf6 = byj2.getCollisionShape(this, fk);
        final VoxelShape cwf7 = byj3.getCollisionShape(this, fk);
        if (!Shapes.joinIsNotEmpty(cwf6, cwf7, BooleanOp.NOT_SAME)) {
            return;
        }
        for (final PathNavigation arr9 : this.navigations) {
            if (arr9.hasDelayedRecomputation()) {
                continue;
            }
            arr9.recomputePath(fk);
        }
    }
    
    @Override
    public void broadcastEntityEvent(final Entity akq, final byte byte2) {
        this.getChunkSource().broadcastAndSend(akq, new ClientboundEntityEventPacket(akq, byte2));
    }
    
    @Override
    public ServerChunkCache getChunkSource() {
        return (ServerChunkCache)super.getChunkSource();
    }
    
    @Override
    public Explosion explode(@Nullable final Entity akq, @Nullable final DamageSource ajz, final double double3, final double double4, final double double5, final float float6, final boolean boolean7, final Explosion.BlockInteraction a) {
        final Explosion bjp13 = new Explosion(this, akq, double3, double4, double5, float6, boolean7, a);
        if (ajz != null) {
            bjp13.setDamageSource(ajz);
        }
        bjp13.explode();
        bjp13.finalizeExplosion(false);
        if (a == Explosion.BlockInteraction.NONE) {
            bjp13.clearToBlow();
        }
        for (final ServerPlayer xe15 : this.players) {
            if (xe15.distanceToSqr(double3, double4, double5) < 4096.0) {
                xe15.connection.send(new ClientboundExplodePacket(double3, double4, double5, float6, bjp13.getToBlow(), bjp13.getHitPlayers().get(xe15)));
            }
        }
        return bjp13;
    }
    
    @Override
    public void blockEvent(final BlockPos fk, final Block bph, final int integer3, final int integer4) {
        this.blockEvents.add(new BlockEventData(fk, bph, integer3, integer4));
    }
    
    private void runBlockEvents() {
        while (!this.blockEvents.isEmpty()) {
            final BlockEventData bjf2 = (BlockEventData)this.blockEvents.removeFirst();
            if (this.doBlockEvent(bjf2)) {
                this.server.getPlayerList().broadcast(null, bjf2.getPos().getX(), bjf2.getPos().getY(), bjf2.getPos().getZ(), 64.0, this.dimension.getType(), new ClientboundBlockEventPacket(bjf2.getPos(), bjf2.getBlock(), bjf2.getParamA(), bjf2.getParamB()));
            }
        }
    }
    
    private boolean doBlockEvent(final BlockEventData bjf) {
        final BlockState byj3 = this.getBlockState(bjf.getPos());
        return byj3.getBlock() == bjf.getBlock() && byj3.triggerEvent(this, bjf.getPos(), bjf.getParamA(), bjf.getParamB());
    }
    
    @Override
    public ServerTickList<Block> getBlockTicks() {
        return this.blockTicks;
    }
    
    @Override
    public ServerTickList<Fluid> getLiquidTicks() {
        return this.liquidTicks;
    }
    
    @Nonnull
    @Override
    public MinecraftServer getServer() {
        return this.server;
    }
    
    public PortalForcer getPortalForcer() {
        return this.portalForcer;
    }
    
    public StructureManager getStructureManager() {
        return this.levelStorage.getStructureManager();
    }
    
    public <T extends ParticleOptions> int sendParticles(final T gt, final double double2, final double double3, final double double4, final int integer, final double double6, final double double7, final double double8, final double double9) {
        final ClientboundLevelParticlesPacket nh18 = new ClientboundLevelParticlesPacket((T)gt, false, double2, double3, double4, (float)double6, (float)double7, (float)double8, (float)double9, integer);
        int integer2 = 0;
        for (int integer3 = 0; integer3 < this.players.size(); ++integer3) {
            final ServerPlayer xe21 = this.players.get(integer3);
            if (this.sendParticles(xe21, false, double2, double3, double4, nh18)) {
                ++integer2;
            }
        }
        return integer2;
    }
    
    public <T extends ParticleOptions> boolean sendParticles(final ServerPlayer xe, final T gt, final boolean boolean3, final double double4, final double double5, final double double6, final int integer, final double double8, final double double9, final double double10, final double double11) {
        final Packet<?> lt20 = new ClientboundLevelParticlesPacket((T)gt, boolean3, double4, double5, double6, (float)double8, (float)double9, (float)double10, (float)double11, integer);
        return this.sendParticles(xe, boolean3, double4, double5, double6, lt20);
    }
    
    private boolean sendParticles(final ServerPlayer xe, final boolean boolean2, final double double3, final double double4, final double double5, final Packet<?> lt) {
        if (xe.getLevel() != this) {
            return false;
        }
        final BlockPos fk11 = xe.getCommandSenderBlockPosition();
        if (fk11.closerThan(new Vec3(double3, double4, double5), boolean2 ? 512.0 : 32.0)) {
            xe.connection.send(lt);
            return true;
        }
        return false;
    }
    
    @Nullable
    @Override
    public Entity getEntity(final int integer) {
        return (Entity)this.entitiesById.get(integer);
    }
    
    @Nullable
    public Entity getEntity(final UUID uUID) {
        return this.entitiesByUuid.get(uUID);
    }
    
    @Nullable
    public BlockPos findNearestMapFeature(final String string, final BlockPos fk, final int integer, final boolean boolean4) {
        return this.getChunkSource().getGenerator().findNearestMapFeature(this, string, fk, integer, boolean4);
    }
    
    @Override
    public RecipeManager getRecipeManager() {
        return this.server.getRecipeManager();
    }
    
    @Override
    public TagManager getTagManager() {
        return this.server.getTags();
    }
    
    @Override
    public void setGameTime(final long long1) {
        super.setGameTime(long1);
        this.levelData.getScheduledEvents().tick(this.server, long1);
    }
    
    @Override
    public boolean noSave() {
        return this.noSave;
    }
    
    public void checkSession() throws LevelConflictException {
        this.levelStorage.checkSession();
    }
    
    public LevelStorage getLevelStorage() {
        return this.levelStorage;
    }
    
    public DimensionDataStorage getDataStorage() {
        return this.getChunkSource().getDataStorage();
    }
    
    @Nullable
    @Override
    public MapItemSavedData getMapData(final String string) {
        return this.getServer().getLevel(DimensionType.OVERWORLD).getDataStorage().<MapItemSavedData>get(() -> new MapItemSavedData(string), string);
    }
    
    @Override
    public void setMapData(final MapItemSavedData crg) {
        this.getServer().getLevel(DimensionType.OVERWORLD).getDataStorage().set(crg);
    }
    
    @Override
    public int getFreeMapId() {
        return this.getServer().getLevel(DimensionType.OVERWORLD).getDataStorage().<MapIndex>computeIfAbsent(MapIndex::new, "idcounts").getFreeAuxValueForMap();
    }
    
    @Override
    public void setSpawnPos(final BlockPos fk) {
        final ChunkPos bjh3 = new ChunkPos(new BlockPos(this.levelData.getXSpawn(), 0, this.levelData.getZSpawn()));
        super.setSpawnPos(fk);
        this.getChunkSource().<Unit>removeRegionTicket(TicketType.START, bjh3, 11, Unit.INSTANCE);
        this.getChunkSource().<Unit>addRegionTicket(TicketType.START, new ChunkPos(fk), 11, Unit.INSTANCE);
    }
    
    public LongSet getForcedChunks() {
        final ForcedChunksSavedData bjr2 = this.getDataStorage().<ForcedChunksSavedData>get(ForcedChunksSavedData::new, "chunks");
        return (LongSet)((bjr2 != null) ? LongSets.unmodifiable(bjr2.getChunks()) : LongSets.EMPTY_SET);
    }
    
    public boolean setChunkForced(final int integer1, final int integer2, final boolean boolean3) {
        final ForcedChunksSavedData bjr5 = this.getDataStorage().<ForcedChunksSavedData>computeIfAbsent(ForcedChunksSavedData::new, "chunks");
        final ChunkPos bjh6 = new ChunkPos(integer1, integer2);
        final long long7 = bjh6.toLong();
        boolean boolean4;
        if (boolean3) {
            boolean4 = bjr5.getChunks().add(long7);
            if (boolean4) {
                this.getChunk(integer1, integer2);
            }
        }
        else {
            boolean4 = bjr5.getChunks().remove(long7);
        }
        bjr5.setDirty(boolean4);
        if (boolean4) {
            this.getChunkSource().updateChunkForced(bjh6, boolean3);
        }
        return boolean4;
    }
    
    @Override
    public List<ServerPlayer> players() {
        return this.players;
    }
    
    @Override
    public void onBlockStateChange(final BlockPos fk, final BlockState byj2, final BlockState byj3) {
        final Optional<PoiType> optional5 = PoiType.forState(byj2);
        final Optional<PoiType> optional6 = PoiType.forState(byj3);
        if (Objects.equals(optional5, optional6)) {
            return;
        }
        final BlockPos fk2 = fk.immutable();
        final BlockPos blockPos;
        optional5.ifPresent(asu -> this.getServer().execute(() -> {
            this.getPoiManager().remove(blockPos);
            DebugPackets.sendPoiRemovedPacket(this, blockPos);
        }));
        final BlockPos blockPos2;
        optional6.ifPresent(asu -> this.getServer().execute(() -> {
            this.getPoiManager().add(blockPos2, asu);
            DebugPackets.sendPoiAddedPacket(this, blockPos2);
        }));
    }
    
    public PoiManager getPoiManager() {
        return this.getChunkSource().getPoiManager();
    }
    
    public boolean isVillage(final BlockPos fk) {
        return this.closeToVillage(fk, 1);
    }
    
    public boolean isVillage(final SectionPos gd) {
        return this.isVillage(gd.center());
    }
    
    public boolean closeToVillage(final BlockPos fk, final int integer) {
        return integer <= 6 && this.sectionsToVillage(SectionPos.of(fk)) <= integer;
    }
    
    public int sectionsToVillage(final SectionPos gd) {
        return this.getPoiManager().sectionsToVillage(gd);
    }
    
    public Raids getRaids() {
        return this.raids;
    }
    
    @Nullable
    public Raid getRaidAt(final BlockPos fk) {
        return this.raids.getNearbyRaid(fk, 9216);
    }
    
    public boolean isRaided(final BlockPos fk) {
        return this.getRaidAt(fk) != null;
    }
    
    public void onReputationEvent(final ReputationEventType aso, final Entity akq, final ReputationEventHandler all) {
        all.onReputationEventFrom(aso, akq);
    }
    
    public void saveDebugReport(final Path path) throws IOException {
        final ChunkMap wp3 = this.getChunkSource().chunkMap;
        try (final Writer writer4 = Files.newBufferedWriter(path.resolve("stats.txt"))) {
            writer4.write(String.format("spawning_chunks: %d\n", wp3.getDistanceManager().getNaturalSpawnChunkCount()));
            for (final Object2IntMap.Entry<MobCategory> entry7 : this.getMobCategoryCounts().object2IntEntrySet()) {
                writer4.write(String.format("spawn_count.%s: %d\n", ((MobCategory)entry7.getKey()).getName(), entry7.getIntValue()));
            }
            writer4.write(String.format("entities: %d\n", this.entitiesById.size()));
            writer4.write(String.format("block_entities: %d\n", this.blockEntityList.size()));
            writer4.write(String.format("block_ticks: %d\n", this.getBlockTicks().size()));
            writer4.write(String.format("fluid_ticks: %d\n", this.getLiquidTicks().size()));
            writer4.write("distance_manager: " + wp3.getDistanceManager().getDebugStatus() + "\n");
            writer4.write(String.format("pending_tasks: %d\n", this.getChunkSource().getPendingTasksCount()));
        }
        final CrashReport h4 = new CrashReport("Level dump", new Exception("dummy"));
        this.fillReportDetails(h4);
        try (final Writer writer5 = Files.newBufferedWriter(path.resolve("example_crash.txt"))) {
            writer5.write(h4.getFriendlyReport());
        }
        final Path path2 = path.resolve("chunks.csv");
        try (final Writer writer6 = Files.newBufferedWriter(path2)) {
            wp3.dumpChunks(writer6);
        }
        final Path path3 = path.resolve("entities.csv");
        try (final Writer writer7 = Files.newBufferedWriter(path3)) {
            dumpEntities(writer7, (Iterable<Entity>)this.entitiesById.values());
        }
        final Path path4 = path.resolve("global_entities.csv");
        try (final Writer writer8 = Files.newBufferedWriter(path4)) {
            dumpEntities(writer8, this.globalEntities);
        }
        final Path path5 = path.resolve("block_entities.csv");
        try (final Writer writer9 = Files.newBufferedWriter(path5)) {
            this.dumpBlockEntities(writer9);
        }
    }
    
    private static void dumpEntities(final Writer writer, final Iterable<Entity> iterable) throws IOException {
        final CsvOutput abh3 = CsvOutput.builder().addColumn("x").addColumn("y").addColumn("z").addColumn("uuid").addColumn("type").addColumn("alive").addColumn("display_name").addColumn("custom_name").build(writer);
        for (final Entity akq5 : iterable) {
            final Component lf6 = akq5.getCustomName();
            final Component lf7 = akq5.getDisplayName();
            abh3.writeRow(akq5.getX(), akq5.getY(), akq5.getZ(), akq5.getUUID(), Registry.ENTITY_TYPE.getKey(akq5.getType()), akq5.isAlive(), lf7.getString(), (lf6 != null) ? lf6.getString() : null);
        }
    }
    
    private void dumpBlockEntities(final Writer writer) throws IOException {
        final CsvOutput abh3 = CsvOutput.builder().addColumn("x").addColumn("y").addColumn("z").addColumn("type").build(writer);
        for (final BlockEntity bwl5 : this.blockEntityList) {
            final BlockPos fk6 = bwl5.getBlockPos();
            abh3.writeRow(fk6.getX(), fk6.getY(), fk6.getZ(), Registry.BLOCK_ENTITY_TYPE.getKey(bwl5.getType()));
        }
    }
    
    @VisibleForTesting
    public void clearBlockEvents(final BoundingBox clb) {
        this.blockEvents.removeIf(bjf -> clb.isInside(bjf.getPos()));
    }
    
    static {
        LOGGER = LogManager.getLogger();
    }
}
