package net.minecraft.server.level;

import org.apache.logging.log4j.LogManager;
import net.minecraft.world.level.TickList;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.scores.Scoreboard;
import com.google.common.collect.Iterables;
import net.minecraft.core.Direction;
import com.google.common.annotations.VisibleForTesting;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.util.CsvOutput;
import java.io.IOException;
import net.minecraft.world.level.NaturalSpawner;
import java.io.Writer;
import net.minecraft.CrashReport;
import net.minecraft.world.entity.MobCategory;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
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.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket;
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 com.google.common.collect.ImmutableList;
import net.minecraft.world.level.levelgen.feature.StructureFeature;
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.level.Explosion;
import net.minecraft.world.level.ExplosionDamageCalculator;
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.world.entity.Mob;
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 net.minecraft.world.entity.boss.enderdragon.EnderDragon;
import java.util.function.Predicate;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.util.ProgressListener;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.TickNextTickData;
import net.minecraft.server.ServerScoreboard;
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.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.core.Vec3i;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.entity.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 java.util.Iterator;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import net.minecraft.util.profiling.ProfilerFiller;
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.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 net.minecraft.world.level.LevelAccessor;
import com.google.common.collect.Sets;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.core.Registry;
import com.google.common.collect.Lists;
import com.google.common.collect.Queues;
import com.google.common.collect.Maps;
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
import net.minecraft.world.level.storage.WritableLevelData;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.storage.LevelStorageSource;
import java.util.concurrent.Executor;
import net.minecraft.world.level.StructureFeatureManager;
import javax.annotation.Nullable;
import net.minecraft.world.level.dimension.end.EndDragonFight;
import net.minecraft.world.level.CustomSpawner;
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.ServerLevelData;
import net.minecraft.server.MinecraftServer;
import java.util.List;
import java.util.Queue;
import java.util.UUID;
import java.util.Map;
import net.minecraft.world.entity.Entity;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import org.apache.logging.log4j.Logger;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.Level;

public class ServerLevel extends Level implements WorldGenLevel {
    public static final BlockPos END_SPAWN_POINT;
    private static final Logger LOGGER;
    private final Int2ObjectMap<Entity> entitiesById;
    private final Map<UUID, Entity> entitiesByUuid;
    private final Queue<Entity> toAddAfterTick;
    private final List<ServerPlayer> players;
    private final ServerChunkCache chunkSource;
    boolean tickingEntities;
    private final MinecraftServer server;
    private final ServerLevelData serverLevelData;
    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;
    private final List<CustomSpawner> customSpawners;
    @Nullable
    private final EndDragonFight dragonFight;
    private final StructureFeatureManager structureFeatureManager;
    private final boolean tickTime;
    
    public ServerLevel(final MinecraftServer minecraftServer, final Executor executor, final LevelStorageSource.LevelStorageAccess a, final ServerLevelData dak, final ResourceKey<Level> ug5, final ResourceKey<DimensionType> ug6, final DimensionType cif, final ChunkProgressListener zm, final ChunkGenerator cha, final boolean boolean10, final long long11, final List<CustomSpawner> list, final boolean boolean13) {
        super(dak, ug5, ug6, cif, minecraftServer::getProfiler, false, boolean10, long11);
        this.entitiesById = (Int2ObjectMap<Entity>)new Int2ObjectLinkedOpenHashMap();
        this.entitiesByUuid = Maps.newHashMap();
        this.toAddAfterTick = Queues.newArrayDeque();
        this.players = Lists.newArrayList();
        this.blockTicks = new ServerTickList<Block>(this, bvr -> bvr == null || bvr.defaultBlockState().isAir(), Registry.BLOCK::getKey, this::tickBlock);
        this.liquidTicks = new ServerTickList<Fluid>(this, cwz -> cwz == null || cwz == Fluids.EMPTY, Registry.FLUID::getKey, this::tickLiquid);
        this.navigations = Sets.newHashSet();
        this.blockEvents = (ObjectLinkedOpenHashSet<BlockEventData>)new ObjectLinkedOpenHashSet();
        this.tickTime = boolean13;
        this.server = minecraftServer;
        this.customSpawners = list;
        this.serverLevelData = dak;
        this.chunkSource = new ServerChunkCache(this, a, minecraftServer.getFixerUpper(), minecraftServer.getStructureManager(), executor, cha, minecraftServer.getPlayerList().getViewDistance(), minecraftServer.forceSynchronousWrites(), zm, () -> minecraftServer.overworld().getDataStorage());
        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.dimensionType()));
        if (!minecraftServer.isSingleplayer()) {
            dak.setGameType(minecraftServer.getDefaultGameType());
        }
        this.structureFeatureManager = new StructureFeatureManager(this, minecraftServer.getWorldData().worldGenSettings());
        if (this.dimensionType().createDragonFight()) {
            this.dragonFight = new EndDragonFight(this, minecraftServer.getWorldData().worldGenSettings().seed(), minecraftServer.getWorldData().endDragonFightData());
        }
        else {
            this.dragonFight = null;
        }
    }
    
    public void setWeatherParameters(final int integer1, final int integer2, final boolean boolean3, final boolean boolean4) {
        this.serverLevelData.setClearWeatherTime(integer1);
        this.serverLevelData.setRainTime(integer2);
        this.serverLevelData.setThunderTime(integer2);
        this.serverLevelData.setRaining(boolean3);
        this.serverLevelData.setThundering(boolean4);
    }
    
    @Override
    public Biome getUncachedNoiseBiome(final int integer1, final int integer2, final int integer3) {
        return this.getChunkSource().getGenerator().getBiomeSource().getNoiseBiome(integer1, integer2, integer3);
    }
    
    public StructureFeatureManager structureFeatureManager() {
        return this.structureFeatureManager;
    }
    
    public void tick(final BooleanSupplier booleanSupplier) {
        final ProfilerFiller ami3 = this.getProfiler();
        this.handlingTick = true;
        ami3.push("world border");
        this.getWorldBorder().tick();
        ami3.popPush("weather");
        final boolean boolean4 = this.isRaining();
        if (this.dimensionType().hasSkyLight()) {
            if (this.getGameRules().getBoolean(GameRules.RULE_WEATHER_CYCLE)) {
                int integer5 = this.serverLevelData.getClearWeatherTime();
                int integer6 = this.serverLevelData.getThunderTime();
                int integer7 = this.serverLevelData.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.serverLevelData.setThunderTime(integer6);
                this.serverLevelData.setRainTime(integer7);
                this.serverLevelData.setClearWeatherTime(integer5);
                this.serverLevelData.setThundering(boolean5);
                this.serverLevelData.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(ClientboundGameEventPacket.RAIN_LEVEL_CHANGE, this.rainLevel), this.dimension());
        }
        if (this.oThunderLevel != this.thunderLevel) {
            this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE, this.thunderLevel), this.dimension());
        }
        if (boolean4 != this.isRaining()) {
            if (boolean4) {
                this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.STOP_RAINING, 0.0f));
            }
            else {
                this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.START_RAINING, 0.0f));
            }
            this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.RAIN_LEVEL_CHANGE, this.rainLevel));
            this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE, this.thunderLevel));
        }
        if (this.allPlayersSleeping && this.players.stream().noneMatch(ze -> !ze.isSpectator() && !ze.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();
        ami3.popPush("chunkSource");
        this.getChunkSource().tick(booleanSupplier);
        ami3.popPush("tickPending");
        if (!this.isDebug()) {
            this.blockTicks.tick();
            this.liquidTicks.tick();
        }
        ami3.popPush("raid");
        this.raids.tick();
        ami3.popPush("blockEvents");
        this.runBlockEvents();
        this.handlingTick = false;
        ami3.popPush("entities");
        final boolean boolean7 = !this.players.isEmpty() || !this.getForcedChunks().isEmpty();
        if (boolean7) {
            this.resetEmptyTime();
        }
        if (boolean7 || this.emptyTime++ < 300) {
            if (this.dragonFight != null) {
                this.dragonFight.tick();
            }
            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 aom8 = (Entity)entry7.getValue();
                final Entity aom9 = aom8.getVehicle();
                if (!this.server.isSpawningAnimals() && (aom8 instanceof Animal || aom8 instanceof WaterAnimal)) {
                    aom8.remove();
                }
                if (!this.server.areNpcsEnabled() && aom8 instanceof Npc) {
                    aom8.remove();
                }
                ami3.push("checkDespawn");
                if (!aom8.removed) {
                    aom8.checkDespawn();
                }
                ami3.pop();
                if (aom9 != null) {
                    if (!aom9.removed && aom9.hasPassenger(aom8)) {
                        continue;
                    }
                    aom8.stopRiding();
                }
                ami3.push("tick");
                if (!aom8.removed && !(aom8 instanceof EnderDragonPart)) {
                    this.guardEntityTick(this::tickNonPassenger, aom8);
                }
                ami3.pop();
                ami3.push("remove");
                if (aom8.removed) {
                    this.removeFromChunk(aom8);
                    objectIterator6.remove();
                    this.onEntityRemoved(aom8);
                }
                ami3.pop();
            }
            this.tickingEntities = false;
            Entity aom10;
            while ((aom10 = this.toAddAfterTick.poll()) != null) {
                this.add(aom10);
            }
            this.tickBlockEntities();
        }
        ami3.pop();
    }
    
    protected void tickTime() {
        if (!this.tickTime) {
            return;
        }
        final long long2 = this.levelData.getGameTime() + 1L;
        this.serverLevelData.setGameTime(long2);
        this.serverLevelData.getScheduledEvents().tick(this.server, long2);
        if (this.levelData.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)) {
            this.setDayTime(this.levelData.getDayTime() + 1L);
        }
    }
    
    public void setDayTime(final long long1) {
        this.serverLevelData.setDayTime(long1);
    }
    
    public void tickCustomSpawners(final boolean boolean1, final boolean boolean2) {
        for (final CustomSpawner bpm5 : this.customSpawners) {
            bpm5.tick(this, boolean1, boolean2);
        }
    }
    
    private void wakeUpAllPlayers() {
        this.players.stream().filter(LivingEntity::isSleeping).collect(Collectors.toList()).forEach(ze -> ze.stopSleepInBed(false, false));
    }
    
    public void tickChunk(final LevelChunk chj, final int integer) {
        final ChunkPos bph4 = chj.getPos();
        final boolean boolean5 = this.isRaining();
        final int integer2 = bph4.getMinBlockX();
        final int integer3 = bph4.getMinBlockZ();
        final ProfilerFiller ami8 = this.getProfiler();
        ami8.push("thunder");
        if (boolean5 && this.isThundering() && this.random.nextInt(100000) == 0) {
            final BlockPos fu9 = this.findLightingTargetAround(this.getBlockRandomPos(integer2, 0, integer3, 15));
            if (this.isRainingAt(fu9)) {
                final DifficultyInstance ane10 = this.getCurrentDifficultyAt(fu9);
                final boolean boolean6 = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && this.random.nextDouble() < ane10.getEffectiveDifficulty() * 0.01;
                if (boolean6) {
                    final SkeletonHorse azs12 = EntityType.SKELETON_HORSE.create(this);
                    azs12.setTrap(true);
                    azs12.setAge(0);
                    azs12.setPos(fu9.getX(), fu9.getY(), fu9.getZ());
                    this.addFreshEntity(azs12);
                }
                final LightningBolt aox12 = EntityType.LIGHTNING_BOLT.create(this);
                aox12.moveTo(Vec3.atBottomCenterOf(fu9));
                aox12.setVisualOnly(boolean6);
                this.addFreshEntity(aox12);
            }
        }
        ami8.popPush("iceandsnow");
        if (this.random.nextInt(16) == 0) {
            final BlockPos fu9 = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, this.getBlockRandomPos(integer2, 0, integer3, 15));
            final BlockPos fu10 = fu9.below();
            final Biome bre11 = this.getBiome(fu9);
            if (bre11.shouldFreeze(this, fu10)) {
                this.setBlockAndUpdate(fu10, Blocks.ICE.defaultBlockState());
            }
            if (boolean5 && bre11.shouldSnow(this, fu9)) {
                this.setBlockAndUpdate(fu9, Blocks.SNOW.defaultBlockState());
            }
            if (boolean5 && this.getBiome(fu10).getPrecipitation() == Biome.Precipitation.RAIN) {
                this.getBlockState(fu10).getBlock().handleRain(this, fu10);
            }
        }
        ami8.popPush("tickBlocks");
        if (integer > 0) {
            for (final LevelChunkSection chk12 : chj.getSections()) {
                if (chk12 != LevelChunk.EMPTY_SECTION && chk12.isRandomlyTicking()) {
                    final int integer4 = chk12.bottomBlockY();
                    for (int integer5 = 0; integer5 < integer; ++integer5) {
                        final BlockPos fu11 = this.getBlockRandomPos(integer2, integer4, integer3, 15);
                        ami8.push("randomTick");
                        final BlockState cfj16 = chk12.getBlockState(fu11.getX() - integer2, fu11.getY() - integer4, fu11.getZ() - integer3);
                        if (cfj16.isRandomlyTicking()) {
                            cfj16.randomTick(this, fu11, this.random);
                        }
                        final FluidState cxa17 = cfj16.getFluidState();
                        if (cxa17.isRandomlyTicking()) {
                            cxa17.randomTick(this, fu11, this.random);
                        }
                        ami8.pop();
                    }
                }
            }
        }
        ami8.pop();
    }
    
    protected BlockPos findLightingTargetAround(final BlockPos fu) {
        BlockPos fu2 = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, fu);
        final AABB deg4 = new AABB(fu2, new BlockPos(fu2.getX(), this.getMaxBuildHeight(), fu2.getZ())).inflate(3.0);
        final List<LivingEntity> list5 = this.<LivingEntity>getEntitiesOfClass(LivingEntity.class, deg4, aoy -> aoy != null && aoy.isAlive() && this.canSeeSky(aoy.blockPosition()));
        if (!list5.isEmpty()) {
            return list5.get(this.random.nextInt(list5.size())).blockPosition();
        }
        if (fu2.getY() == -1) {
            fu2 = fu2.above(2);
        }
        return fu2;
    }
    
    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 ze5 : this.players) {
                if (ze5.isSpectator()) {
                    ++integer2;
                }
                else {
                    if (!ze5.isSleeping()) {
                        continue;
                    }
                    ++integer3;
                }
            }
            this.allPlayersSleeping = (integer3 > 0 && integer3 >= this.players.size() - integer2);
        }
    }
    
    @Override
    public ServerScoreboard getScoreboard() {
        return this.server.getScoreboard();
    }
    
    private void stopWeather() {
        this.serverLevelData.setRainTime(0);
        this.serverLevelData.setRaining(false);
        this.serverLevelData.setThunderTime(0);
        this.serverLevelData.setThundering(false);
    }
    
    public void resetEmptyTime() {
        this.emptyTime = 0;
    }
    
    private void tickLiquid(final TickNextTickData<Fluid> bqs) {
        final FluidState cxa3 = this.getFluidState(bqs.pos);
        if (cxa3.getType() == bqs.getType()) {
            cxa3.tick(this, bqs.pos);
        }
    }
    
    private void tickBlock(final TickNextTickData<Block> bqs) {
        final BlockState cfj3 = this.getBlockState(bqs.pos);
        if (cfj3.is(bqs.getType())) {
            cfj3.tick(this, bqs.pos, this.random);
        }
    }
    
    public void tickNonPassenger(final Entity aom) {
        if (!(aom instanceof Player) && !this.getChunkSource().isEntityTickingChunk(aom)) {
            this.updateChunkPos(aom);
            return;
        }
        aom.setPosAndOldPos(aom.getX(), aom.getY(), aom.getZ());
        aom.yRotO = aom.yRot;
        aom.xRotO = aom.xRot;
        if (aom.inChunk) {
            ++aom.tickCount;
            final ProfilerFiller ami3 = this.getProfiler();
            ami3.push(() -> Registry.ENTITY_TYPE.getKey(aom.getType()).toString());
            ami3.incrementCounter("tickNonPassenger");
            aom.tick();
            ami3.pop();
        }
        this.updateChunkPos(aom);
        if (aom.inChunk) {
            for (final Entity aom2 : aom.getPassengers()) {
                this.tickPassenger(aom, aom2);
            }
        }
    }
    
    public void tickPassenger(final Entity aom1, final Entity aom2) {
        if (aom2.removed || aom2.getVehicle() != aom1) {
            aom2.stopRiding();
            return;
        }
        if (!(aom2 instanceof Player) && !this.getChunkSource().isEntityTickingChunk(aom2)) {
            return;
        }
        aom2.setPosAndOldPos(aom2.getX(), aom2.getY(), aom2.getZ());
        aom2.yRotO = aom2.yRot;
        aom2.xRotO = aom2.xRot;
        if (aom2.inChunk) {
            ++aom2.tickCount;
            final ProfilerFiller ami4 = this.getProfiler();
            ami4.push(() -> Registry.ENTITY_TYPE.getKey(aom2.getType()).toString());
            ami4.incrementCounter("tickPassenger");
            aom2.rideTick();
            ami4.pop();
        }
        this.updateChunkPos(aom2);
        if (aom2.inChunk) {
            for (final Entity aom3 : aom2.getPassengers()) {
                this.tickPassenger(aom2, aom3);
            }
        }
    }
    
    public void updateChunkPos(final Entity aom) {
        if (!aom.checkAndResetUpdateChunkPos()) {
            return;
        }
        this.getProfiler().push("chunkCheck");
        final int integer3 = Mth.floor(aom.getX() / 16.0);
        final int integer4 = Mth.floor(aom.getY() / 16.0);
        final int integer5 = Mth.floor(aom.getZ() / 16.0);
        if (!aom.inChunk || aom.xChunk != integer3 || aom.yChunk != integer4 || aom.zChunk != integer5) {
            if (aom.inChunk && this.hasChunk(aom.xChunk, aom.zChunk)) {
                this.getChunk(aom.xChunk, aom.zChunk).removeEntity(aom, aom.yChunk);
            }
            if (aom.checkAndResetForcedChunkAdditionFlag() || this.hasChunk(integer3, integer5)) {
                this.getChunk(integer3, integer5).addEntity(aom);
            }
            else {
                if (aom.inChunk) {
                    ServerLevel.LOGGER.warn("Entity {} left loaded chunk area", aom);
                }
                aom.inChunk = false;
            }
        }
        this.getProfiler().pop();
    }
    
    @Override
    public boolean mayInteract(final Player bec, final BlockPos fu) {
        return !this.server.isUnderSpawnProtection(this, fu, bec) && this.getWorldBorder().isWithinBounds(fu);
    }
    
    public void save(@Nullable final ProgressListener aed, final boolean boolean2, final boolean boolean3) {
        final ServerChunkCache zb5 = this.getChunkSource();
        if (boolean3) {
            return;
        }
        if (aed != null) {
            aed.progressStartNoAbort(new TranslatableComponent("menu.savingLevel"));
        }
        this.saveLevelData();
        if (aed != null) {
            aed.progressStage(new TranslatableComponent("menu.savingChunks"));
        }
        zb5.save(boolean2);
    }
    
    private void saveLevelData() {
        if (this.dragonFight != null) {
            this.server.getWorldData().setEndDragonFightData(this.dragonFight.saveData());
        }
        this.getChunkSource().getDataStorage().save();
    }
    
    public List<Entity> getEntities(@Nullable final EntityType<?> aoq, final Predicate<? super Entity> predicate) {
        final List<Entity> list4 = Lists.newArrayList();
        final ServerChunkCache zb5 = this.getChunkSource();
        for (final Entity aom7 : this.entitiesById.values()) {
            if ((aoq == null || aom7.getType() == aoq) && zb5.hasChunk(Mth.floor(aom7.getX()) >> 4, Mth.floor(aom7.getZ()) >> 4) && predicate.test(aom7)) {
                list4.add(aom7);
            }
        }
        return list4;
    }
    
    public List<EnderDragon> getDragons() {
        final List<EnderDragon> list2 = Lists.newArrayList();
        for (final Entity aom4 : this.entitiesById.values()) {
            if (aom4 instanceof EnderDragon && aom4.isAlive()) {
                list2.add((EnderDragon)aom4);
            }
        }
        return list2;
    }
    
    public List<ServerPlayer> getPlayers(final Predicate<? super ServerPlayer> predicate) {
        final List<ServerPlayer> list3 = Lists.newArrayList();
        for (final ServerPlayer ze5 : this.players) {
            if (predicate.test(ze5)) {
                list3.add(ze5);
            }
        }
        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()));
    }
    
    @Override
    public boolean addFreshEntity(final Entity aom) {
        return this.addEntity(aom);
    }
    
    public boolean addWithUUID(final Entity aom) {
        return this.addEntity(aom);
    }
    
    public void addFromAnotherDimension(final Entity aom) {
        final boolean boolean3 = aom.forcedLoading;
        aom.forcedLoading = true;
        this.addWithUUID(aom);
        aom.forcedLoading = boolean3;
        this.updateChunkPos(aom);
    }
    
    public void addDuringCommandTeleport(final ServerPlayer ze) {
        this.addPlayer(ze);
        this.updateChunkPos(ze);
    }
    
    public void addDuringPortalTeleport(final ServerPlayer ze) {
        this.addPlayer(ze);
        this.updateChunkPos(ze);
    }
    
    public void addNewPlayer(final ServerPlayer ze) {
        this.addPlayer(ze);
    }
    
    public void addRespawnedPlayer(final ServerPlayer ze) {
        this.addPlayer(ze);
    }
    
    private void addPlayer(final ServerPlayer ze) {
        final Entity aom3 = this.entitiesByUuid.get(ze.getUUID());
        if (aom3 != null) {
            ServerLevel.LOGGER.warn("Force-added player with duplicate UUID {}", ze.getUUID().toString());
            aom3.unRide();
            this.removePlayerImmediately((ServerPlayer)aom3);
        }
        this.players.add(ze);
        this.updateSleepingPlayerList();
        final ChunkAccess cgy4 = this.getChunk(Mth.floor(ze.getX() / 16.0), Mth.floor(ze.getZ() / 16.0), ChunkStatus.FULL, true);
        if (cgy4 instanceof LevelChunk) {
            cgy4.addEntity(ze);
        }
        this.add(ze);
    }
    
    private boolean addEntity(final Entity aom) {
        if (aom.removed) {
            ServerLevel.LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityType.getKey(aom.getType()));
            return false;
        }
        if (this.isUUIDUsed(aom)) {
            return false;
        }
        final ChunkAccess cgy3 = this.getChunk(Mth.floor(aom.getX() / 16.0), Mth.floor(aom.getZ() / 16.0), ChunkStatus.FULL, aom.forcedLoading);
        if (!(cgy3 instanceof LevelChunk)) {
            return false;
        }
        cgy3.addEntity(aom);
        this.add(aom);
        return true;
    }
    
    public boolean loadFromChunk(final Entity aom) {
        if (this.isUUIDUsed(aom)) {
            return false;
        }
        this.add(aom);
        return true;
    }
    
    private boolean isUUIDUsed(final Entity aom) {
        final Entity aom2 = this.entitiesByUuid.get(aom.getUUID());
        if (aom2 == null) {
            return false;
        }
        ServerLevel.LOGGER.warn("Keeping entity {} that already exists with UUID {}", EntityType.getKey(aom2.getType()), aom.getUUID().toString());
        return true;
    }
    
    public void unload(final LevelChunk chj) {
        this.blockEntitiesToUnload.addAll(chj.getBlockEntities().values());
        for (final ClassInstanceMultiMap<Entity> adk6 : chj.getEntitySections()) {
            for (final Entity aom8 : adk6) {
                if (aom8 instanceof ServerPlayer) {
                    continue;
                }
                if (this.tickingEntities) {
                    throw Util.<IllegalStateException>pauseInIde(new IllegalStateException("Removing entity while ticking!"));
                }
                this.entitiesById.remove(aom8.getId());
                this.onEntityRemoved(aom8);
            }
        }
    }
    
    public void onEntityRemoved(final Entity aom) {
        if (aom instanceof EnderDragon) {
            for (final EnderDragonPart baa6 : ((EnderDragon)aom).getSubEntities()) {
                baa6.remove();
            }
        }
        this.entitiesByUuid.remove(aom.getUUID());
        this.getChunkSource().removeEntity(aom);
        if (aom instanceof ServerPlayer) {
            final ServerPlayer ze3 = (ServerPlayer)aom;
            this.players.remove(ze3);
        }
        this.getScoreboard().entityRemoved(aom);
        if (aom instanceof Mob) {
            this.navigations.remove(((Mob)aom).getNavigation());
        }
    }
    
    private void add(final Entity aom) {
        if (this.tickingEntities) {
            this.toAddAfterTick.add(aom);
        }
        else {
            this.entitiesById.put(aom.getId(), aom);
            if (aom instanceof EnderDragon) {
                for (final EnderDragonPart baa6 : ((EnderDragon)aom).getSubEntities()) {
                    this.entitiesById.put(baa6.getId(), baa6);
                }
            }
            this.entitiesByUuid.put(aom.getUUID(), aom);
            this.getChunkSource().addEntity(aom);
            if (aom instanceof Mob) {
                this.navigations.add(((Mob)aom).getNavigation());
            }
        }
    }
    
    public void despawn(final Entity aom) {
        if (this.tickingEntities) {
            throw Util.<IllegalStateException>pauseInIde(new IllegalStateException("Removing entity while ticking!"));
        }
        this.removeFromChunk(aom);
        this.entitiesById.remove(aom.getId());
        this.onEntityRemoved(aom);
    }
    
    private void removeFromChunk(final Entity aom) {
        final ChunkAccess cgy3 = this.getChunk(aom.xChunk, aom.zChunk, ChunkStatus.FULL, false);
        if (cgy3 instanceof LevelChunk) {
            ((LevelChunk)cgy3).removeEntity(aom);
        }
    }
    
    public void removePlayerImmediately(final ServerPlayer ze) {
        ze.remove();
        this.despawn(ze);
        this.updateSleepingPlayerList();
    }
    
    @Override
    public void destroyBlockProgress(final int integer1, final BlockPos fu, final int integer3) {
        for (final ServerPlayer ze6 : this.server.getPlayerList().getPlayers()) {
            if (ze6 != null && ze6.level == this) {
                if (ze6.getId() == integer1) {
                    continue;
                }
                final double double7 = fu.getX() - ze6.getX();
                final double double8 = fu.getY() - ze6.getY();
                final double double9 = fu.getZ() - ze6.getZ();
                if (double7 * double7 + double8 * double8 + double9 * double9 >= 1024.0) {
                    continue;
                }
                ze6.connection.send(new ClientboundBlockDestructionPacket(integer1, fu, integer3));
            }
        }
    }
    
    @Override
    public void playSound(@Nullable final Player bec, final double double2, final double double3, final double double4, final SoundEvent ack, final SoundSource acm, final float float7, final float float8) {
        this.server.getPlayerList().broadcast(bec, double2, double3, double4, (float7 > 1.0f) ? ((double)(16.0f * float7)) : 16.0, this.dimension(), new ClientboundSoundPacket(ack, acm, double2, double3, double4, float7, float8));
    }
    
    @Override
    public void playSound(@Nullable final Player bec, final Entity aom, final SoundEvent ack, final SoundSource acm, final float float5, final float float6) {
        this.server.getPlayerList().broadcast(bec, aom.getX(), aom.getY(), aom.getZ(), (float5 > 1.0f) ? ((double)(16.0f * float5)) : 16.0, this.dimension(), new ClientboundSoundEntityPacket(ack, acm, aom, float5, float6));
    }
    
    @Override
    public void globalLevelEvent(final int integer1, final BlockPos fu, final int integer3) {
        this.server.getPlayerList().broadcastAll(new ClientboundLevelEventPacket(integer1, fu, integer3, true));
    }
    
    @Override
    public void levelEvent(@Nullable final Player bec, final int integer2, final BlockPos fu, final int integer4) {
        this.server.getPlayerList().broadcast(bec, fu.getX(), fu.getY(), fu.getZ(), 64.0, this.dimension(), new ClientboundLevelEventPacket(integer2, fu, integer4, false));
    }
    
    @Override
    public void sendBlockUpdated(final BlockPos fu, final BlockState cfj2, final BlockState cfj3, final int integer) {
        this.getChunkSource().blockChanged(fu);
        final VoxelShape dfg6 = cfj2.getCollisionShape(this, fu);
        final VoxelShape dfg7 = cfj3.getCollisionShape(this, fu);
        if (!Shapes.joinIsNotEmpty(dfg6, dfg7, BooleanOp.NOT_SAME)) {
            return;
        }
        for (final PathNavigation awv9 : this.navigations) {
            if (awv9.hasDelayedRecomputation()) {
                continue;
            }
            awv9.recomputePath(fu);
        }
    }
    
    @Override
    public void broadcastEntityEvent(final Entity aom, final byte byte2) {
        this.getChunkSource().broadcastAndSend(aom, new ClientboundEntityEventPacket(aom, byte2));
    }
    
    @Override
    public ServerChunkCache getChunkSource() {
        return this.chunkSource;
    }
    
    @Override
    public Explosion explode(@Nullable final Entity aom, @Nullable final DamageSource anw, @Nullable final ExplosionDamageCalculator bpu, final double double4, final double double5, final double double6, final float float7, final boolean boolean8, final Explosion.BlockInteraction a) {
        final Explosion bpt14 = new Explosion(this, aom, anw, bpu, double4, double5, double6, float7, boolean8, a);
        bpt14.explode();
        bpt14.finalizeExplosion(false);
        if (a == Explosion.BlockInteraction.NONE) {
            bpt14.clearToBlow();
        }
        for (final ServerPlayer ze16 : this.players) {
            if (ze16.distanceToSqr(double4, double5, double6) < 4096.0) {
                ze16.connection.send(new ClientboundExplodePacket(double4, double5, double6, float7, bpt14.getToBlow(), bpt14.getHitPlayers().get(ze16)));
            }
        }
        return bpt14;
    }
    
    @Override
    public void blockEvent(final BlockPos fu, final Block bvr, final int integer3, final int integer4) {
        this.blockEvents.add(new BlockEventData(fu, bvr, integer3, integer4));
    }
    
    private void runBlockEvents() {
        while (!this.blockEvents.isEmpty()) {
            final BlockEventData bpf2 = (BlockEventData)this.blockEvents.removeFirst();
            if (this.doBlockEvent(bpf2)) {
                this.server.getPlayerList().broadcast(null, bpf2.getPos().getX(), bpf2.getPos().getY(), bpf2.getPos().getZ(), 64.0, this.dimension(), new ClientboundBlockEventPacket(bpf2.getPos(), bpf2.getBlock(), bpf2.getParamA(), bpf2.getParamB()));
            }
        }
    }
    
    private boolean doBlockEvent(final BlockEventData bpf) {
        final BlockState cfj3 = this.getBlockState(bpf.getPos());
        return cfj3.is(bpf.getBlock()) && cfj3.triggerEvent(this, bpf.getPos(), bpf.getParamA(), bpf.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.server.getStructureManager();
    }
    
    public <T extends ParticleOptions> int sendParticles(final T hf, 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 ov18 = new ClientboundLevelParticlesPacket((T)hf, 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 ze21 = this.players.get(integer3);
            if (this.sendParticles(ze21, false, double2, double3, double4, ov18)) {
                ++integer2;
            }
        }
        return integer2;
    }
    
    public <T extends ParticleOptions> boolean sendParticles(final ServerPlayer ze, final T hf, 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<?> ni20 = new ClientboundLevelParticlesPacket((T)hf, boolean3, double4, double5, double6, (float)double8, (float)double9, (float)double10, (float)double11, integer);
        return this.sendParticles(ze, boolean3, double4, double5, double6, ni20);
    }
    
    private boolean sendParticles(final ServerPlayer ze, final boolean boolean2, final double double3, final double double4, final double double5, final Packet<?> ni) {
        if (ze.getLevel() != this) {
            return false;
        }
        final BlockPos fu11 = ze.blockPosition();
        if (fu11.closerThan(new Vec3(double3, double4, double5), boolean2 ? 512.0 : 32.0)) {
            ze.connection.send(ni);
            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 StructureFeature<?> cml, final BlockPos fu, final int integer, final boolean boolean4) {
        if (!this.server.getWorldData().worldGenSettings().generateFeatures()) {
            return null;
        }
        return this.getChunkSource().getGenerator().findNearestMapFeature(this, cml, fu, integer, boolean4);
    }
    
    @Nullable
    public BlockPos findNearestBiome(final Biome bre, final BlockPos fu, final int integer3, final int integer4) {
        return this.getChunkSource().getGenerator().getBiomeSource().findBiomeHorizontal(fu.getX(), fu.getY(), fu.getZ(), integer3, integer4, ImmutableList.<Biome>of(bre), this.random, true);
    }
    
    @Override
    public RecipeManager getRecipeManager() {
        return this.server.getRecipeManager();
    }
    
    @Override
    public TagManager getTagManager() {
        return this.server.getTags();
    }
    
    @Override
    public boolean noSave() {
        return this.noSave;
    }
    
    public DimensionDataStorage getDataStorage() {
        return this.getChunkSource().getDataStorage();
    }
    
    @Nullable
    @Override
    public MapItemSavedData getMapData(final String string) {
        return this.getServer().overworld().getDataStorage().<MapItemSavedData>get(() -> new MapItemSavedData(string), string);
    }
    
    @Override
    public void setMapData(final MapItemSavedData czv) {
        this.getServer().overworld().getDataStorage().set(czv);
    }
    
    @Override
    public int getFreeMapId() {
        return this.getServer().overworld().getDataStorage().<MapIndex>computeIfAbsent(MapIndex::new, "idcounts").getFreeAuxValueForMap();
    }
    
    public void setDefaultSpawnPos(final BlockPos fu) {
        final ChunkPos bph3 = new ChunkPos(new BlockPos(this.levelData.getXSpawn(), 0, this.levelData.getZSpawn()));
        this.levelData.setSpawn(fu);
        this.getChunkSource().<Unit>removeRegionTicket(TicketType.START, bph3, 11, Unit.INSTANCE);
        this.getChunkSource().<Unit>addRegionTicket(TicketType.START, new ChunkPos(fu), 11, Unit.INSTANCE);
        this.getServer().getPlayerList().broadcastAll(new ClientboundSetDefaultSpawnPositionPacket(fu));
    }
    
    public BlockPos getSharedSpawnPos() {
        BlockPos fu2 = new BlockPos(this.levelData.getXSpawn(), this.levelData.getYSpawn(), this.levelData.getZSpawn());
        if (!this.getWorldBorder().isWithinBounds(fu2)) {
            fu2 = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, new BlockPos(this.getWorldBorder().getCenterX(), 0.0, this.getWorldBorder().getCenterZ()));
        }
        return fu2;
    }
    
    public LongSet getForcedChunks() {
        final ForcedChunksSavedData bpw2 = this.getDataStorage().<ForcedChunksSavedData>get(ForcedChunksSavedData::new, "chunks");
        return (LongSet)((bpw2 != null) ? LongSets.unmodifiable(bpw2.getChunks()) : LongSets.EMPTY_SET);
    }
    
    public boolean setChunkForced(final int integer1, final int integer2, final boolean boolean3) {
        final ForcedChunksSavedData bpw5 = this.getDataStorage().<ForcedChunksSavedData>computeIfAbsent(ForcedChunksSavedData::new, "chunks");
        final ChunkPos bph6 = new ChunkPos(integer1, integer2);
        final long long7 = bph6.toLong();
        boolean boolean4;
        if (boolean3) {
            boolean4 = bpw5.getChunks().add(long7);
            if (boolean4) {
                this.getChunk(integer1, integer2);
            }
        }
        else {
            boolean4 = bpw5.getChunks().remove(long7);
        }
        bpw5.setDirty(boolean4);
        if (boolean4) {
            this.getChunkSource().updateChunkForced(bph6, boolean3);
        }
        return boolean4;
    }
    
    @Override
    public List<ServerPlayer> players() {
        return this.players;
    }
    
    @Override
    public void onBlockStateChange(final BlockPos fu, final BlockState cfj2, final BlockState cfj3) {
        final Optional<PoiType> optional5 = PoiType.forState(cfj2);
        final Optional<PoiType> optional6 = PoiType.forState(cfj3);
        if (Objects.equals(optional5, optional6)) {
            return;
        }
        final BlockPos fu2 = fu.immutable();
        final BlockPos blockPos;
        optional5.ifPresent(ayc -> this.getServer().execute(() -> {
            this.getPoiManager().remove(blockPos);
            DebugPackets.sendPoiRemovedPacket(this, blockPos);
        }));
        final BlockPos blockPos2;
        optional6.ifPresent(ayc -> this.getServer().execute(() -> {
            this.getPoiManager().add(blockPos2, ayc);
            DebugPackets.sendPoiAddedPacket(this, blockPos2);
        }));
    }
    
    public PoiManager getPoiManager() {
        return this.getChunkSource().getPoiManager();
    }
    
    public boolean isVillage(final BlockPos fu) {
        return this.isCloseToVillage(fu, 1);
    }
    
    public boolean isVillage(final SectionPos go) {
        return this.isVillage(go.center());
    }
    
    public boolean isCloseToVillage(final BlockPos fu, final int integer) {
        return integer <= 6 && this.sectionsToVillage(SectionPos.of(fu)) <= integer;
    }
    
    public int sectionsToVillage(final SectionPos go) {
        return this.getPoiManager().sectionsToVillage(go);
    }
    
    public Raids getRaids() {
        return this.raids;
    }
    
    @Nullable
    public Raid getRaidAt(final BlockPos fu) {
        return this.raids.getNearbyRaid(fu, 9216);
    }
    
    public boolean isRaided(final BlockPos fu) {
        return this.getRaidAt(fu) != null;
    }
    
    public void onReputationEvent(final ReputationEventType axw, final Entity aom, final ReputationEventHandler apl) {
        apl.onReputationEventFrom(axw, aom);
    }
    
    public void saveDebugReport(final Path path) throws IOException {
        final ChunkMap yp3 = this.getChunkSource().chunkMap;
        try (final Writer writer4 = Files.newBufferedWriter(path.resolve("stats.txt"))) {
            writer4.write(String.format("spawning_chunks: %d\n", yp3.getDistanceManager().getNaturalSpawnChunkCount()));
            final NaturalSpawner.SpawnState d6 = this.getChunkSource().getLastSpawnState();
            if (d6 != null) {
                for (final Object2IntMap.Entry<MobCategory> entry8 : d6.getMobCategoryCounts().object2IntEntrySet()) {
                    writer4.write(String.format("spawn_count.%s: %d\n", ((MobCategory)entry8.getKey()).getName(), entry8.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: " + yp3.getDistanceManager().getDebugStatus() + "\n");
            writer4.write(String.format("pending_tasks: %d\n", this.getChunkSource().getPendingTasksCount()));
        }
        final CrashReport j4 = new CrashReport("Level dump", new Exception("dummy"));
        this.fillReportDetails(j4);
        try (final Writer writer5 = Files.newBufferedWriter(path.resolve("example_crash.txt"))) {
            writer5.write(j4.getFriendlyReport());
        }
        final Path path2 = path.resolve("chunks.csv");
        try (final Writer writer6 = Files.newBufferedWriter(path2)) {
            yp3.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("block_entities.csv");
        try (final Writer writer8 = Files.newBufferedWriter(path4)) {
            this.dumpBlockEntities(writer8);
        }
    }
    
    private static void dumpEntities(final Writer writer, final Iterable<Entity> iterable) throws IOException {
        final CsvOutput ado3 = 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 aom5 : iterable) {
            final Component mr6 = aom5.getCustomName();
            final Component mr7 = aom5.getDisplayName();
            ado3.writeRow(aom5.getX(), aom5.getY(), aom5.getZ(), aom5.getUUID(), Registry.ENTITY_TYPE.getKey(aom5.getType()), aom5.isAlive(), mr7.getString(), (mr6 != null) ? mr6.getString() : null);
        }
    }
    
    private void dumpBlockEntities(final Writer writer) throws IOException {
        final CsvOutput ado3 = CsvOutput.builder().addColumn("x").addColumn("y").addColumn("z").addColumn("type").build(writer);
        for (final BlockEntity cdl5 : this.blockEntityList) {
            final BlockPos fu6 = cdl5.getBlockPos();
            ado3.writeRow(fu6.getX(), fu6.getY(), fu6.getZ(), Registry.BLOCK_ENTITY_TYPE.getKey(cdl5.getType()));
        }
    }
    
    @VisibleForTesting
    public void clearBlockEvents(final BoundingBox ctd) {
        this.blockEvents.removeIf(bpf -> ctd.isInside(bpf.getPos()));
    }
    
    @Override
    public void blockUpdated(final BlockPos fu, final Block bvr) {
        if (!this.isDebug()) {
            this.updateNeighborsAt(fu, bvr);
        }
    }
    
    @Override
    public float getShade(final Direction fz, final boolean boolean2) {
        return 1.0f;
    }
    
    public Iterable<Entity> getAllEntities() {
        return Iterables.<Entity>unmodifiableIterable(this.entitiesById.values());
    }
    
    @Override
    public String toString() {
        return "ServerLevel[" + this.serverLevelData.getLevelName() + "]";
    }
    
    public boolean isFlat() {
        return this.server.getWorldData().worldGenSettings().isFlatWorld();
    }
    
    @Override
    public long getSeed() {
        return this.server.getWorldData().worldGenSettings().seed();
    }
    
    @Nullable
    public EndDragonFight dragonFight() {
        return this.dragonFight;
    }
    
    public static void makeObsidianPlatform(final ServerLevel zd) {
        final BlockPos fu2 = ServerLevel.END_SPAWN_POINT;
        final int integer3 = fu2.getX();
        final int integer4 = fu2.getY() - 2;
        final int integer5 = fu2.getZ();
        BlockPos.betweenClosed(integer3 - 2, integer4 + 1, integer5 - 2, integer3 + 2, integer4 + 3, integer5 + 2).forEach(fu -> zd.setBlockAndUpdate(fu, Blocks.AIR.defaultBlockState()));
        BlockPos.betweenClosed(integer3 - 2, integer4, integer5 - 2, integer3 + 2, integer4, integer5 + 2).forEach(fu -> zd.setBlockAndUpdate(fu, Blocks.OBSIDIAN.defaultBlockState()));
    }
    
    static {
        END_SPAWN_POINT = new BlockPos(100, 50, 0);
        LOGGER = LogManager.getLogger();
    }
}
