package net.minecraft.server;

import org.apache.logging.log4j.LogManager;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.Block;
import java.lang.management.ThreadMXBean;
import java.util.function.Function;
import java.util.Comparator;
import java.lang.management.ThreadInfo;
import java.lang.management.ManagementFactory;
import com.google.common.base.Splitter;
import java.io.Writer;
import java.nio.file.OpenOption;
import net.minecraft.resources.ResourceLocation;
import java.nio.file.Files;
import java.nio.file.attribute.FileAttribute;
import net.minecraft.server.players.ServerOpListEntry;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec2;
import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.Vec3;
import net.minecraft.server.players.UserWhiteList;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.server.packs.Pack;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.network.protocol.game.ClientboundChangeDifficultyPacket;
import java.util.function.Consumer;
import java.nio.file.Path;
import joptsimple.OptionSet;
import net.minecraft.DefaultUncaughtExceptionHandler;
import java.awt.GraphicsEnvironment;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.progress.LoggerChunkProgressListener;
import net.minecraft.util.datafix.DataFixers;
import java.util.Optional;
import java.util.UUID;
import net.minecraft.server.dedicated.DedicatedServerSettings;
import java.nio.file.Paths;
import joptsimple.OptionSpec;
import joptsimple.OptionParser;
import net.minecraft.gametest.framework.GameTestTicker;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundSetTimePacket;
import net.minecraft.core.Registry;
import java.util.Collections;
import java.util.Arrays;
import net.minecraft.server.level.ServerPlayer;
import com.mojang.authlib.GameProfile;
import java.util.function.BooleanSupplier;
import java.nio.ByteBuffer;
import java.awt.image.BufferedImage;
import io.netty.buffer.ByteBuf;
import java.util.Base64;
import java.io.OutputStream;
import java.awt.image.RenderedImage;
import io.netty.buffer.ByteBufOutputStream;
import org.apache.commons.lang3.Validate;
import javax.imageio.ImageIO;
import io.netty.buffer.Unpooled;
import java.util.Date;
import java.text.SimpleDateFormat;
import net.minecraft.SharedConstants;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.world.level.LevelConflictException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import it.unimi.dsi.fastutil.longs.LongIterator;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.ForcedChunksSavedData;
import net.minecraft.server.level.TicketType;
import net.minecraft.world.level.ChunkPos;
import java.util.Collection;
import net.minecraft.server.packs.repository.RepositorySource;
import net.minecraft.server.packs.repository.ServerPacksSource;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.Difficulty;
import net.minecraft.world.level.GameType;
import java.util.Iterator;
import net.minecraft.server.level.DerivedServerLevel;
import net.minecraft.ReportedException;
import net.minecraft.CrashReport;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.world.level.storage.LevelStorage;
import com.google.gson.JsonElement;
import net.minecraft.world.level.LevelType;
import net.minecraft.world.level.storage.LevelData;
import net.minecraft.util.Mth;
import net.minecraft.util.worldupdate.WorldUpgrader;
import net.minecraft.util.ProgressListener;
import net.minecraft.network.chat.TranslatableComponent;
import java.io.IOException;
import net.minecraft.world.level.saveddata.SavedData;
import net.minecraft.world.level.saveddata.SaveDataDirtyRunnable;
import net.minecraft.world.scores.Scoreboard;
import net.minecraft.world.scores.ScoreboardSaveData;
import net.minecraft.world.level.storage.DimensionDataStorage;
import net.minecraft.server.packs.resources.PreparableReloadListener;
import net.minecraft.server.packs.resources.SimpleReloadableResourceManager;
import net.minecraft.server.packs.PackType;
import com.google.common.collect.Maps;
import com.google.common.collect.Lists;
import net.minecraft.Util;
import java.util.concurrent.Executor;
import net.minecraft.util.FrameTimer;
import net.minecraft.world.level.storage.loot.LootTables;
import net.minecraft.world.level.storage.loot.PredicateManager;
import net.minecraft.server.bossevents.CustomBossEvents;
import net.minecraft.world.level.storage.CommandStorage;
import net.minecraft.tags.TagManager;
import net.minecraft.world.item.crafting.RecipeManager;
import net.minecraft.commands.Commands;
import net.minecraft.server.packs.repository.FolderRepositorySource;
import net.minecraft.server.packs.repository.UnopenedPack;
import net.minecraft.server.packs.repository.PackRepository;
import net.minecraft.server.packs.resources.ReloadableResourceManager;
import net.minecraft.server.players.GameProfileCache;
import com.mojang.authlib.GameProfileRepository;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import net.minecraft.network.chat.Component;
import java.security.KeyPair;
import javax.annotation.Nullable;
import java.net.Proxy;
import net.minecraft.server.players.PlayerList;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.dimension.DimensionType;
import java.util.Map;
import com.mojang.datafixers.DataFixer;
import java.util.Random;
import net.minecraft.network.protocol.status.ServerStatus;
import net.minecraft.server.level.progress.ChunkProgressListenerFactory;
import net.minecraft.server.network.ServerConnectionListener;
import net.minecraft.util.profiling.GameProfiler;
import java.util.List;
import net.minecraft.world.Snooper;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.LevelSettings;
import net.minecraft.util.Unit;
import java.util.concurrent.CompletableFuture;
import java.io.File;
import org.apache.logging.log4j.Logger;
import net.minecraft.commands.CommandSource;
import net.minecraft.world.SnooperPopulator;
import net.minecraft.util.thread.ReentrantBlockableEventLoop;

public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTask> implements SnooperPopulator, CommandSource, AutoCloseable, Runnable {
    private static final Logger LOGGER;
    public static final File USERID_CACHE_FILE;
    private static final CompletableFuture<Unit> DATA_RELOAD_INITIAL_TASK;
    public static final LevelSettings DEMO_SETTINGS;
    private final LevelStorageSource storageSource;
    private final Snooper snooper;
    private final File universe;
    private final List<Runnable> tickables;
    private final GameProfiler profiler;
    private final ServerConnectionListener connection;
    protected final ChunkProgressListenerFactory progressListenerFactory;
    private final ServerStatus status;
    private final Random random;
    private final DataFixer fixerUpper;
    private String localIp;
    private int port;
    private final Map<DimensionType, ServerLevel> levels;
    private PlayerList playerList;
    private volatile boolean running;
    private boolean stopped;
    private int tickCount;
    protected final Proxy proxy;
    private boolean onlineMode;
    private boolean preventProxyConnections;
    private boolean animals;
    private boolean npcs;
    private boolean pvp;
    private boolean allowFlight;
    @Nullable
    private String motd;
    private int maxBuildHeight;
    private int playerIdleTimeout;
    public final long[] tickTimes;
    @Nullable
    private KeyPair keyPair;
    @Nullable
    private String singleplayerName;
    private final String levelIdName;
    @Nullable
    private String levelName;
    private boolean isDemo;
    private boolean levelHasStartingBonusChest;
    private String resourcePack;
    private String resourcePackHash;
    private volatile boolean isReady;
    private long lastOverloadWarning;
    @Nullable
    private Component startupState;
    private boolean delayProfilerStart;
    private boolean forceGameType;
    @Nullable
    private final YggdrasilAuthenticationService authenticationService;
    private final MinecraftSessionService sessionService;
    private final GameProfileRepository profileRepository;
    private final GameProfileCache profileCache;
    private long lastServerStatus;
    protected final Thread serverThread;
    private long nextTickTime;
    private long delayedTasksMaxNextTickTime;
    private boolean mayHaveDelayedTasks;
    private boolean hasWorldScreenshot;
    private final ReloadableResourceManager resources;
    private final PackRepository<UnopenedPack> packRepository;
    @Nullable
    private FolderRepositorySource folderPackSource;
    private final Commands commands;
    private final RecipeManager recipes;
    private final TagManager tags;
    private final ServerScoreboard scoreboard;
    @Nullable
    private CommandStorage commandStorage;
    private final CustomBossEvents customBossEvents;
    private final PredicateManager predicateManager;
    private final LootTables lootTables;
    private final ServerAdvancementManager advancements;
    private final ServerFunctionManager functions;
    private final FrameTimer frameTimer;
    private boolean enforceWhitelist;
    private boolean forceUpgrade;
    private boolean eraseCache;
    private float averageTickTime;
    private final Executor executor;
    @Nullable
    private String serverId;
    
    public MinecraftServer(final File file, final Proxy proxy, final DataFixer dataFixer, final Commands cr, final YggdrasilAuthenticationService yggdrasilAuthenticationService, final MinecraftSessionService minecraftSessionService, final GameProfileRepository gameProfileRepository, final GameProfileCache zk, final ChunkProgressListenerFactory xn, final String string) {
        super("Server");
        this.snooper = new Snooper("server", this, Util.getMillis());
        this.tickables = Lists.newArrayList();
        this.profiler = new GameProfiler(this::getTickCount);
        this.status = new ServerStatus();
        this.random = new Random();
        this.port = -1;
        this.levels = Maps.newIdentityHashMap();
        this.running = true;
        this.tickTimes = new long[100];
        this.resourcePack = "";
        this.resourcePackHash = "";
        this.serverThread = Util.<Thread>make(new Thread(this, "Server thread"), thread -> thread.setUncaughtExceptionHandler((thread, throwable) -> MinecraftServer.LOGGER.error(throwable)));
        this.nextTickTime = Util.getMillis();
        this.resources = new SimpleReloadableResourceManager(PackType.SERVER_DATA, this.serverThread);
        this.packRepository = new PackRepository<UnopenedPack>(UnopenedPack::new);
        this.recipes = new RecipeManager();
        this.tags = new TagManager();
        this.scoreboard = new ServerScoreboard(this);
        this.customBossEvents = new CustomBossEvents(this);
        this.predicateManager = new PredicateManager();
        this.lootTables = new LootTables(this.predicateManager);
        this.advancements = new ServerAdvancementManager();
        this.functions = new ServerFunctionManager(this);
        this.frameTimer = new FrameTimer();
        this.proxy = proxy;
        this.commands = cr;
        this.authenticationService = yggdrasilAuthenticationService;
        this.sessionService = minecraftSessionService;
        this.profileRepository = gameProfileRepository;
        this.profileCache = zk;
        this.universe = file;
        this.connection = new ServerConnectionListener(this);
        this.progressListenerFactory = xn;
        this.storageSource = new LevelStorageSource(file.toPath(), file.toPath().resolve("../backups"), dataFixer);
        this.fixerUpper = dataFixer;
        this.resources.registerReloadListener(this.tags);
        this.resources.registerReloadListener(this.predicateManager);
        this.resources.registerReloadListener(this.recipes);
        this.resources.registerReloadListener(this.lootTables);
        this.resources.registerReloadListener(this.functions);
        this.resources.registerReloadListener(this.advancements);
        this.executor = Util.backgroundExecutor();
        this.levelIdName = string;
    }
    
    private void readScoreboard(final DimensionDataStorage crl) {
        final ScoreboardSaveData cwm3 = crl.<ScoreboardSaveData>computeIfAbsent(ScoreboardSaveData::new, "scoreboard");
        cwm3.setScoreboard(this.getScoreboard());
        this.getScoreboard().addDirtyListener(new SaveDataDirtyRunnable(cwm3));
    }
    
    protected abstract boolean initServer() throws IOException;
    
    protected void ensureLevelConversion(final String string) {
        if (this.getStorageSource().requiresConversion(string)) {
            MinecraftServer.LOGGER.info("Converting map!");
            this.setServerStartupState(new TranslatableComponent("menu.convertingLevel", new Object[0]));
            this.getStorageSource().convertLevel(string, new ProgressListener() {
                private long timeStamp = Util.getMillis();
                
                @Override
                public void progressStartNoAbort(final Component lf) {
                }
                
                @Override
                public void progressStart(final Component lf) {
                }
                
                @Override
                public void progressStagePercentage(final int integer) {
                    if (Util.getMillis() - this.timeStamp >= 1000L) {
                        this.timeStamp = Util.getMillis();
                        MinecraftServer.LOGGER.info("Converting... {}%", integer);
                    }
                }
                
                @Override
                public void stop() {
                }
                
                @Override
                public void progressStage(final Component lf) {
                }
            });
        }
        if (this.forceUpgrade) {
            MinecraftServer.LOGGER.info("Forcing world upgrade!");
            final LevelData crm3 = this.getStorageSource().getDataTagFor(this.getLevelIdName());
            if (crm3 != null) {
                final WorldUpgrader aiz4 = new WorldUpgrader(this.getLevelIdName(), this.getStorageSource(), crm3, this.eraseCache);
                Component lf5 = null;
                while (!aiz4.isFinished()) {
                    final Component lf6 = aiz4.getStatus();
                    if (lf5 != lf6) {
                        lf5 = lf6;
                        MinecraftServer.LOGGER.info(aiz4.getStatus().getString());
                    }
                    final int integer7 = aiz4.getTotalChunks();
                    if (integer7 > 0) {
                        final int integer8 = aiz4.getConverted() + aiz4.getSkipped();
                        MinecraftServer.LOGGER.info("{}% completed ({} / {} chunks)...", Mth.floor(integer8 / (float)integer7 * 100.0f), integer8, integer7);
                    }
                    if (this.isStopped()) {
                        aiz4.cancel();
                    }
                    else {
                        try {
                            Thread.sleep(1000L);
                        }
                        catch (InterruptedException ex) {}
                    }
                }
            }
        }
    }
    
    protected synchronized void setServerStartupState(final Component lf) {
        this.startupState = lf;
    }
    
    protected void loadLevel(final String string1, final String string2, final long long3, final LevelType bkd, final JsonElement jsonElement) {
        this.ensureLevelConversion(string1);
        this.setServerStartupState(new TranslatableComponent("menu.loadingLevel", new Object[0]));
        final LevelStorage crn8 = this.getStorageSource().selectLevel(string1, this);
        this.detectBundledResources(this.getLevelIdName(), crn8);
        LevelData crm10 = crn8.prepareLevel();
        LevelSettings bka9;
        if (crm10 == null) {
            if (this.isDemo()) {
                bka9 = MinecraftServer.DEMO_SETTINGS;
            }
            else {
                bka9 = new LevelSettings(long3, this.getDefaultGameType(), this.canGenerateStructures(), this.isHardcore(), bkd);
                bka9.setLevelTypeOptions(jsonElement);
                if (this.levelHasStartingBonusChest) {
                    bka9.enableStartingBonusItems();
                }
            }
            crm10 = new LevelData(bka9, string2);
        }
        else {
            crm10.setLevelName(string2);
            bka9 = new LevelSettings(crm10);
        }
        crm10.setModdedInfo(this.getServerModName(), this.getModdedStatus().isPresent());
        this.loadDataPacks(crn8.getFolder(), crm10);
        final ChunkProgressListener xm11 = this.progressListenerFactory.create(11);
        this.createLevels(crn8, crm10, bka9, xm11);
        this.setDifficulty(this.getDefaultDifficulty(), true);
        this.prepareLevels(xm11);
    }
    
    protected void createLevels(final LevelStorage crn, final LevelData crm, final LevelSettings bka, final ChunkProgressListener xm) {
        if (this.isDemo()) {
            crm.setLevelSettings(MinecraftServer.DEMO_SETTINGS);
        }
        final ServerLevel xd6 = new ServerLevel(this, this.executor, crn, crm, DimensionType.OVERWORLD, this.profiler, xm);
        this.levels.put(DimensionType.OVERWORLD, xd6);
        final DimensionDataStorage crl7 = xd6.getDataStorage();
        this.readScoreboard(crl7);
        this.commandStorage = new CommandStorage(crl7);
        xd6.getWorldBorder().readBorderData(crm);
        final ServerLevel xd7 = this.getLevel(DimensionType.OVERWORLD);
        if (!crm.isInitialized()) {
            try {
                xd7.setInitialSpawn(bka);
                if (crm.getGeneratorType() == LevelType.DEBUG_ALL_BLOCK_STATES) {
                    this.setupDebugLevel(crm);
                }
                crm.setInitialized(true);
            }
            catch (Throwable throwable9) {
                final CrashReport h10 = CrashReport.forThrowable(throwable9, "Exception initializing level");
                try {
                    xd7.fillReportDetails(h10);
                }
                catch (Throwable t) {}
                throw new ReportedException(h10);
            }
            crm.setInitialized(true);
        }
        this.getPlayerList().setLevel(xd7);
        if (crm.getCustomBossEvents() != null) {
            this.getCustomBossEvents().load(crm.getCustomBossEvents());
        }
        for (final DimensionType cbi10 : DimensionType.getAllTypes()) {
            if (cbi10 == DimensionType.OVERWORLD) {
                continue;
            }
            this.levels.put(cbi10, new DerivedServerLevel(xd7, this, this.executor, crn, cbi10, this.profiler, xm));
        }
    }
    
    private void setupDebugLevel(final LevelData crm) {
        crm.setGenerateMapFeatures(false);
        crm.setAllowCommands(true);
        crm.setRaining(false);
        crm.setThundering(false);
        crm.setClearWeatherTime(1000000000);
        crm.setDayTime(6000L);
        crm.setGameType(GameType.SPECTATOR);
        crm.setHardcore(false);
        crm.setDifficulty(Difficulty.PEACEFUL);
        crm.setDifficultyLocked(true);
        crm.getGameRules().<GameRules.BooleanValue>getRule(GameRules.RULE_DAYLIGHT).set(false, this);
    }
    
    protected void loadDataPacks(final File file, final LevelData crm) {
        this.packRepository.addSource(new ServerPacksSource());
        this.folderPackSource = new FolderRepositorySource(new File(file, "datapacks"));
        this.packRepository.addSource(this.folderPackSource);
        this.packRepository.reload();
        final List<UnopenedPack> list4 = Lists.newArrayList();
        for (final String string6 : crm.getEnabledDataPacks()) {
            final UnopenedPack yt7 = this.packRepository.getPack(string6);
            if (yt7 != null) {
                list4.add(yt7);
            }
            else {
                MinecraftServer.LOGGER.warn("Missing data pack {}", string6);
            }
        }
        this.packRepository.setSelected(list4);
        this.updateSelectedPacks(crm);
        this.refreshRegistries();
    }
    
    protected void prepareLevels(final ChunkProgressListener xm) {
        this.setServerStartupState(new TranslatableComponent("menu.generatingTerrain", new Object[0]));
        final ServerLevel xd3 = this.getLevel(DimensionType.OVERWORLD);
        MinecraftServer.LOGGER.info("Preparing start region for dimension " + DimensionType.getName(xd3.dimension.getType()));
        final BlockPos fk4 = xd3.getSharedSpawnPos();
        xm.updateSpawnPos(new ChunkPos(fk4));
        final ServerChunkCache xb5 = xd3.getChunkSource();
        xb5.getLightEngine().setTaskPerBatch(500);
        this.nextTickTime = Util.getMillis();
        xb5.<Unit>addRegionTicket(TicketType.START, new ChunkPos(fk4), 11, Unit.INSTANCE);
        while (xb5.getTickingGenerated() != 441) {
            this.nextTickTime = Util.getMillis() + 10L;
            this.waitUntilNextTick();
        }
        this.nextTickTime = Util.getMillis() + 10L;
        this.waitUntilNextTick();
        for (final DimensionType cbi7 : DimensionType.getAllTypes()) {
            final ForcedChunksSavedData bjr8 = this.getLevel(cbi7).getDataStorage().<ForcedChunksSavedData>get(ForcedChunksSavedData::new, "chunks");
            if (bjr8 != null) {
                final ServerLevel xd4 = this.getLevel(cbi7);
                final LongIterator longIterator10 = bjr8.getChunks().iterator();
                while (longIterator10.hasNext()) {
                    final long long11 = longIterator10.nextLong();
                    final ChunkPos bjh13 = new ChunkPos(long11);
                    xd4.getChunkSource().updateChunkForced(bjh13, true);
                }
            }
        }
        this.nextTickTime = Util.getMillis() + 10L;
        this.waitUntilNextTick();
        xm.stop();
        xb5.getLightEngine().setTaskPerBatch(5);
    }
    
    protected void detectBundledResources(final String string, final LevelStorage crn) {
        final File file4 = new File(crn.getFolder(), "resources.zip");
        if (file4.isFile()) {
            try {
                this.setResourcePack("level://" + URLEncoder.encode(string, StandardCharsets.UTF_8.toString()) + "/" + "resources.zip", "");
            }
            catch (UnsupportedEncodingException unsupportedEncodingException5) {
                MinecraftServer.LOGGER.warn("Something went wrong url encoding {}", string);
            }
        }
    }
    
    public abstract boolean canGenerateStructures();
    
    public abstract GameType getDefaultGameType();
    
    public abstract Difficulty getDefaultDifficulty();
    
    public abstract boolean isHardcore();
    
    public abstract int getOperatorUserPermissionLevel();
    
    public abstract int getFunctionCompilationLevel();
    
    public abstract boolean shouldRconBroadcast();
    
    public boolean saveAllChunks(final boolean boolean1, final boolean boolean2, final boolean boolean3) {
        boolean boolean4 = false;
        for (final ServerLevel xd7 : this.getAllLevels()) {
            if (!boolean1) {
                MinecraftServer.LOGGER.info("Saving chunks for level '{}'/{}", xd7.getLevelData().getLevelName(), DimensionType.getName(xd7.dimension.getType()));
            }
            try {
                xd7.save(null, boolean2, xd7.noSave && !boolean3);
            }
            catch (LevelConflictException bjy8) {
                MinecraftServer.LOGGER.warn(bjy8.getMessage());
            }
            boolean4 = true;
        }
        final ServerLevel xd8 = this.getLevel(DimensionType.OVERWORLD);
        final LevelData crm7 = xd8.getLevelData();
        xd8.getWorldBorder().saveWorldBorderData(crm7);
        crm7.setCustomBossEvents(this.getCustomBossEvents().save());
        xd8.getLevelStorage().saveLevelData(crm7, this.getPlayerList().getSingleplayerData());
        return boolean4;
    }
    
    @Override
    public void close() {
        this.stopServer();
    }
    
    protected void stopServer() {
        MinecraftServer.LOGGER.info("Stopping server");
        if (this.getConnection() != null) {
            this.getConnection().stop();
        }
        if (this.playerList != null) {
            MinecraftServer.LOGGER.info("Saving players");
            this.playerList.saveAll();
            this.playerList.removeAll();
        }
        MinecraftServer.LOGGER.info("Saving worlds");
        for (final ServerLevel xd3 : this.getAllLevels()) {
            if (xd3 != null) {
                xd3.noSave = false;
            }
        }
        this.saveAllChunks(false, true, false);
        for (final ServerLevel xd3 : this.getAllLevels()) {
            if (xd3 != null) {
                try {
                    xd3.close();
                }
                catch (IOException iOException4) {
                    MinecraftServer.LOGGER.error("Exception closing the level", (Throwable)iOException4);
                }
            }
        }
        if (this.snooper.isStarted()) {
            this.snooper.interrupt();
        }
    }
    
    public String getLocalIp() {
        return this.localIp;
    }
    
    public void setLocalIp(final String string) {
        this.localIp = string;
    }
    
    public boolean isRunning() {
        return this.running;
    }
    
    public void halt(final boolean boolean1) {
        this.running = false;
        if (boolean1) {
            try {
                this.serverThread.join();
            }
            catch (InterruptedException interruptedException3) {
                MinecraftServer.LOGGER.error("Error while shutting down", (Throwable)interruptedException3);
            }
        }
    }
    
    @Override
    public void run() {
        try {
            if (this.initServer()) {
                this.nextTickTime = Util.getMillis();
                this.status.setDescription(new TextComponent(this.motd));
                this.status.setVersion(new ServerStatus.Version(SharedConstants.getCurrentVersion().getName(), SharedConstants.getCurrentVersion().getProtocolVersion()));
                this.updateStatusIcon(this.status);
                while (this.running) {
                    final long long2 = Util.getMillis() - this.nextTickTime;
                    if (long2 > 2000L && this.nextTickTime - this.lastOverloadWarning >= 15000L) {
                        final long long3 = long2 / 50L;
                        MinecraftServer.LOGGER.warn("Can't keep up! Is the server overloaded? Running {}ms or {} ticks behind", long2, long3);
                        this.nextTickTime += long3 * 50L;
                        this.lastOverloadWarning = this.nextTickTime;
                    }
                    this.nextTickTime += 50L;
                    if (this.delayProfilerStart) {
                        this.delayProfilerStart = false;
                        this.profiler.continuous().enable();
                    }
                    this.profiler.startTick();
                    this.profiler.push("tick");
                    this.tickServer(this::haveTime);
                    this.profiler.popPush("nextTickWait");
                    this.mayHaveDelayedTasks = true;
                    this.delayedTasksMaxNextTickTime = Math.max(Util.getMillis() + 50L, this.nextTickTime);
                    this.waitUntilNextTick();
                    this.profiler.pop();
                    this.profiler.endTick();
                    this.isReady = true;
                }
            }
            else {
                this.onServerCrash(null);
            }
        }
        catch (Throwable throwable2) {
            MinecraftServer.LOGGER.error("Encountered an unexpected exception", throwable2);
            CrashReport h3;
            if (throwable2 instanceof ReportedException) {
                h3 = this.fillReport(((ReportedException)throwable2).getReport());
            }
            else {
                h3 = this.fillReport(new CrashReport("Exception in server tick loop", throwable2));
            }
            final File file4 = new File(new File(this.getServerDirectory(), "crash-reports"), "crash-" + new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss").format(new Date()) + "-server.txt");
            if (h3.saveToFile(file4)) {
                MinecraftServer.LOGGER.error("This crash report has been saved to: {}", file4.getAbsolutePath());
            }
            else {
                MinecraftServer.LOGGER.error("We were unable to save this crash report to disk.");
            }
            this.onServerCrash(h3);
            try {
                this.stopped = true;
                this.stopServer();
            }
            catch (Throwable throwable2) {
                MinecraftServer.LOGGER.error("Exception stopping the server", throwable2);
            }
            finally {
                this.onServerExit();
            }
        }
        finally {
            try {
                this.stopped = true;
                this.stopServer();
            }
            catch (Throwable throwable3) {
                MinecraftServer.LOGGER.error("Exception stopping the server", throwable3);
                this.onServerExit();
            }
            finally {
                this.onServerExit();
            }
        }
    }
    
    private boolean haveTime() {
        return this.runningTask() || Util.getMillis() < (this.mayHaveDelayedTasks ? this.delayedTasksMaxNextTickTime : this.nextTickTime);
    }
    
    protected void waitUntilNextTick() {
        this.runAllTasks();
        this.managedBlock(() -> !this.haveTime());
    }
    
    @Override
    protected TickTask wrapRunnable(final Runnable runnable) {
        return new TickTask(this.tickCount, runnable);
    }
    
    @Override
    protected boolean shouldRun(final TickTask tb) {
        return tb.getTick() + 3 < this.tickCount || this.haveTime();
    }
    
    public boolean pollTask() {
        final boolean boolean2 = this.pollTaskInternal();
        return this.mayHaveDelayedTasks = boolean2;
    }
    
    private boolean pollTaskInternal() {
        if (super.pollTask()) {
            return true;
        }
        if (this.haveTime()) {
            for (final ServerLevel xd3 : this.getAllLevels()) {
                if (xd3.getChunkSource().pollTask()) {
                    return true;
                }
            }
        }
        return false;
    }
    
    @Override
    protected void doRunTask(final TickTask tb) {
        this.getProfiler().incrementCounter("runTask");
        super.doRunTask(tb);
    }
    
    public void updateStatusIcon(final ServerStatus rw) {
        File file3 = this.getFile("server-icon.png");
        if (!file3.exists()) {
            file3 = this.getStorageSource().getFile(this.getLevelIdName(), "icon.png");
        }
        if (file3.isFile()) {
            final ByteBuf byteBuf4 = Unpooled.buffer();
            try {
                final BufferedImage bufferedImage5 = ImageIO.read(file3);
                Validate.validState(bufferedImage5.getWidth() == 64, "Must be 64 pixels wide", new Object[0]);
                Validate.validState(bufferedImage5.getHeight() == 64, "Must be 64 pixels high", new Object[0]);
                ImageIO.write(bufferedImage5, "PNG", (OutputStream)new ByteBufOutputStream(byteBuf4));
                final ByteBuffer byteBuffer6 = Base64.getEncoder().encode(byteBuf4.nioBuffer());
                rw.setFavicon("data:image/png;base64," + StandardCharsets.UTF_8.decode(byteBuffer6));
            }
            catch (Exception exception5) {
                MinecraftServer.LOGGER.error("Couldn't load server icon", (Throwable)exception5);
            }
            finally {
                byteBuf4.release();
            }
        }
    }
    
    public boolean hasWorldScreenshot() {
        return this.hasWorldScreenshot = (this.hasWorldScreenshot || this.getWorldScreenshotFile().isFile());
    }
    
    public File getWorldScreenshotFile() {
        return this.getStorageSource().getFile(this.getLevelIdName(), "icon.png");
    }
    
    public File getServerDirectory() {
        return new File(".");
    }
    
    protected void onServerCrash(final CrashReport h) {
    }
    
    protected void onServerExit() {
    }
    
    protected void tickServer(final BooleanSupplier booleanSupplier) {
        final long long3 = Util.getNanos();
        ++this.tickCount;
        this.tickChildren(booleanSupplier);
        if (long3 - this.lastServerStatus >= 5000000000L) {
            this.lastServerStatus = long3;
            this.status.setPlayers(new ServerStatus.Players(this.getMaxPlayers(), this.getPlayerCount()));
            final GameProfile[] arr5 = new GameProfile[Math.min(this.getPlayerCount(), 12)];
            final int integer6 = Mth.nextInt(this.random, 0, this.getPlayerCount() - arr5.length);
            for (int integer7 = 0; integer7 < arr5.length; ++integer7) {
                arr5[integer7] = this.playerList.getPlayers().get(integer6 + integer7).getGameProfile();
            }
            Collections.shuffle(Arrays.<GameProfile>asList(arr5));
            this.status.getPlayers().setSample(arr5);
        }
        if (this.tickCount % 6000 == 0) {
            MinecraftServer.LOGGER.debug("Autosave started");
            this.profiler.push("save");
            this.playerList.saveAll();
            this.saveAllChunks(true, false, false);
            this.profiler.pop();
            MinecraftServer.LOGGER.debug("Autosave finished");
        }
        this.profiler.push("snooper");
        if (!this.snooper.isStarted() && this.tickCount > 100) {
            this.snooper.start();
        }
        if (this.tickCount % 6000 == 0) {
            this.snooper.prepare();
        }
        this.profiler.pop();
        this.profiler.push("tallying");
        final long[] tickTimes = this.tickTimes;
        final int n = this.tickCount % 100;
        final long n2 = Util.getNanos() - long3;
        tickTimes[n] = n2;
        final long long4 = n2;
        this.averageTickTime = this.averageTickTime * 0.8f + long4 / 1000000.0f * 0.19999999f;
        final long long5 = Util.getNanos();
        this.frameTimer.logFrameDuration(long5 - long3);
        this.profiler.pop();
    }
    
    protected void tickChildren(final BooleanSupplier booleanSupplier) {
        this.profiler.push("commandFunctions");
        this.getFunctions().tick();
        this.profiler.popPush("levels");
        for (final ServerLevel xd4 : this.getAllLevels()) {
            if (xd4.dimension.getType() == DimensionType.OVERWORLD || this.isNetherEnabled()) {
                final ServerLevel serverLevel;
                this.profiler.push(() -> serverLevel.getLevelData().getLevelName() + " " + Registry.DIMENSION_TYPE.getKey(serverLevel.dimension.getType()));
                if (this.tickCount % 20 == 0) {
                    this.profiler.push("timeSync");
                    this.playerList.broadcastAll(new ClientboundSetTimePacket(xd4.getGameTime(), xd4.getDayTime(), xd4.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)), xd4.dimension.getType());
                    this.profiler.pop();
                }
                this.profiler.push("tick");
                try {
                    xd4.tick(booleanSupplier);
                }
                catch (Throwable throwable5) {
                    final CrashReport h6 = CrashReport.forThrowable(throwable5, "Exception ticking world");
                    xd4.fillReportDetails(h6);
                    throw new ReportedException(h6);
                }
                this.profiler.pop();
                this.profiler.pop();
            }
        }
        this.profiler.popPush("connection");
        this.getConnection().tick();
        this.profiler.popPush("players");
        this.playerList.tick();
        if (SharedConstants.IS_RUNNING_IN_IDE) {
            GameTestTicker.singleton.tick();
        }
        this.profiler.popPush("server gui refresh");
        for (int integer3 = 0; integer3 < this.tickables.size(); ++integer3) {
            this.tickables.get(integer3).run();
        }
        this.profiler.pop();
    }
    
    public boolean isNetherEnabled() {
        return true;
    }
    
    public void addTickable(final Runnable runnable) {
        this.tickables.add(runnable);
    }
    
    public static void main(final String[] arr) {
        final OptionParser optionParser2 = new OptionParser();
        final OptionSpec<Void> optionSpec3 = optionParser2.accepts("nogui");
        final OptionSpec<Void> optionSpec4 = optionParser2.accepts("initSettings", "Initializes 'server.properties' and 'eula.txt', then quits");
        final OptionSpec<Void> optionSpec5 = optionParser2.accepts("demo");
        final OptionSpec<Void> optionSpec6 = optionParser2.accepts("bonusChest");
        final OptionSpec<Void> optionSpec7 = optionParser2.accepts("forceUpgrade");
        final OptionSpec<Void> optionSpec8 = optionParser2.accepts("eraseCache");
        final OptionSpec<Void> optionSpec9 = optionParser2.accepts("help").forHelp();
        final OptionSpec<String> optionSpec10 = optionParser2.accepts("singleplayer").withRequiredArg();
        final OptionSpec<String> optionSpec11 = optionParser2.accepts("universe").withRequiredArg().defaultsTo(".", new String[0]);
        final OptionSpec<String> optionSpec12 = optionParser2.accepts("world").withRequiredArg();
        final OptionSpec<Integer> optionSpec13 = optionParser2.accepts("port").withRequiredArg().<Integer>ofType(Integer.class).defaultsTo(-1, new Integer[0]);
        final OptionSpec<String> optionSpec14 = optionParser2.accepts("serverId").withRequiredArg();
        final OptionSpec<String> optionSpec15 = optionParser2.nonOptions();
        try {
            final OptionSet optionSet16 = optionParser2.parse(arr);
            if (optionSet16.has(optionSpec9)) {
                optionParser2.printHelpOn(System.err);
                return;
            }
            final Path path17 = Paths.get("server.properties");
            final DedicatedServerSettings wf18 = new DedicatedServerSettings(path17);
            wf18.forceSave();
            final Path path18 = Paths.get("eula.txt");
            final Eula st20 = new Eula(path18);
            if (optionSet16.has(optionSpec4)) {
                MinecraftServer.LOGGER.info("Initialized '" + path17.toAbsolutePath().toString() + "' and '" + path18.toAbsolutePath().toString() + "'");
                return;
            }
            if (!st20.hasAgreedToEULA()) {
                MinecraftServer.LOGGER.info("You need to agree to the EULA in order to run the server. Go to eula.txt for more info.");
                return;
            }
            CrashReport.preload();
            Bootstrap.bootStrap();
            Bootstrap.validate();
            final String string21 = optionSet16.<String>valueOf(optionSpec11);
            final YggdrasilAuthenticationService yggdrasilAuthenticationService22 = new YggdrasilAuthenticationService(Proxy.NO_PROXY, UUID.randomUUID().toString());
            final MinecraftSessionService minecraftSessionService23 = yggdrasilAuthenticationService22.createMinecraftSessionService();
            final GameProfileRepository gameProfileRepository24 = yggdrasilAuthenticationService22.createProfileRepository();
            final GameProfileCache zk25 = new GameProfileCache(gameProfileRepository24, new File(string21, MinecraftServer.USERID_CACHE_FILE.getName()));
            final String string22 = Optional.<String>ofNullable((String)optionSet16.<T>valueOf((OptionSpec<T>)optionSpec12)).orElse(wf18.getProperties().levelName);
            final DedicatedServer wd27 = new DedicatedServer(new File(string21), wf18, DataFixers.getDataFixer(), yggdrasilAuthenticationService22, minecraftSessionService23, gameProfileRepository24, zk25, LoggerChunkProgressListener::new, string22);
            wd27.setSingleplayerName(optionSet16.<String>valueOf(optionSpec10));
            wd27.setPort(optionSet16.<Integer>valueOf(optionSpec13));
            wd27.setDemo(optionSet16.has(optionSpec5));
            wd27.setBonusChest(optionSet16.has(optionSpec6));
            wd27.forceUpgrade(optionSet16.has(optionSpec7));
            wd27.eraseCache(optionSet16.has(optionSpec8));
            wd27.setId(optionSet16.<String>valueOf(optionSpec14));
            final boolean boolean28 = !optionSet16.has(optionSpec3) && !optionSet16.<String>valuesOf(optionSpec15).contains("nogui");
            if (boolean28 && !GraphicsEnvironment.isHeadless()) {
                wd27.showGui();
            }
            wd27.forkAndRun();
            final Thread thread29 = new Thread("Server Shutdown Thread") {
                @Override
                public void run() {
                    wd27.halt(true);
                }
            };
            thread29.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(MinecraftServer.LOGGER));
            Runtime.getRuntime().addShutdownHook(thread29);
        }
        catch (Exception exception16) {
            MinecraftServer.LOGGER.fatal("Failed to start the minecraft server", (Throwable)exception16);
        }
    }
    
    protected void setId(final String string) {
        this.serverId = string;
    }
    
    protected void forceUpgrade(final boolean boolean1) {
        this.forceUpgrade = boolean1;
    }
    
    protected void eraseCache(final boolean boolean1) {
        this.eraseCache = boolean1;
    }
    
    public void forkAndRun() {
        this.serverThread.start();
    }
    
    public boolean isShutdown() {
        return !this.serverThread.isAlive();
    }
    
    public File getFile(final String string) {
        return new File(this.getServerDirectory(), string);
    }
    
    public void info(final String string) {
        MinecraftServer.LOGGER.info(string);
    }
    
    public void warn(final String string) {
        MinecraftServer.LOGGER.warn(string);
    }
    
    public ServerLevel getLevel(final DimensionType cbi) {
        return this.levels.get(cbi);
    }
    
    public Iterable<ServerLevel> getAllLevels() {
        return this.levels.values();
    }
    
    public String getServerVersion() {
        return SharedConstants.getCurrentVersion().getName();
    }
    
    public int getPlayerCount() {
        return this.playerList.getPlayerCount();
    }
    
    public int getMaxPlayers() {
        return this.playerList.getMaxPlayers();
    }
    
    public String[] getPlayerNames() {
        return this.playerList.getPlayerNamesArray();
    }
    
    public boolean isDebugging() {
        return false;
    }
    
    public void error(final String string) {
        MinecraftServer.LOGGER.error(string);
    }
    
    public void debug(final String string) {
        if (this.isDebugging()) {
            MinecraftServer.LOGGER.info(string);
        }
    }
    
    public String getServerModName() {
        return "vanilla";
    }
    
    public CrashReport fillReport(final CrashReport h) {
        if (this.playerList != null) {
            h.getSystemDetails().setDetail("Player Count", () -> this.playerList.getPlayerCount() + " / " + this.playerList.getMaxPlayers() + "; " + this.playerList.getPlayers());
        }
        final StringBuilder stringBuilder2;
        final Iterator<UnopenedPack> iterator;
        UnopenedPack yt4;
        h.getSystemDetails().setDetail("Data Packs", () -> {
            stringBuilder2 = new StringBuilder();
            this.packRepository.getSelected().iterator();
            while (iterator.hasNext()) {
                yt4 = iterator.next();
                if (stringBuilder2.length() > 0) {
                    stringBuilder2.append(", ");
                }
                stringBuilder2.append(yt4.getId());
                if (!yt4.getCompatibility().isCompatible()) {
                    stringBuilder2.append(" (incompatible)");
                }
            }
            return stringBuilder2.toString();
        });
        if (this.serverId != null) {
            h.getSystemDetails().setDetail("Server Id", () -> this.serverId);
        }
        return h;
    }
    
    public abstract Optional<String> getModdedStatus();
    
    public boolean isInitialized() {
        return this.universe != null;
    }
    
    @Override
    public void sendMessage(final Component lf) {
        MinecraftServer.LOGGER.info(lf.getString());
    }
    
    public KeyPair getKeyPair() {
        return this.keyPair;
    }
    
    public int getPort() {
        return this.port;
    }
    
    public void setPort(final int integer) {
        this.port = integer;
    }
    
    public String getSingleplayerName() {
        return this.singleplayerName;
    }
    
    public void setSingleplayerName(final String string) {
        this.singleplayerName = string;
    }
    
    public boolean isSingleplayer() {
        return this.singleplayerName != null;
    }
    
    public String getLevelIdName() {
        return this.levelIdName;
    }
    
    public void setLevelName(final String string) {
        this.levelName = string;
    }
    
    public String getLevelName() {
        return this.levelName;
    }
    
    public void setKeyPair(final KeyPair keyPair) {
        this.keyPair = keyPair;
    }
    
    public void setDifficulty(final Difficulty aji, final boolean boolean2) {
        for (final ServerLevel xd5 : this.getAllLevels()) {
            final LevelData crm6 = xd5.getLevelData();
            if (!boolean2 && crm6.isDifficultyLocked()) {
                continue;
            }
            if (crm6.isHardcore()) {
                crm6.setDifficulty(Difficulty.HARD);
                xd5.setSpawnSettings(true, true);
            }
            else if (this.isSingleplayer()) {
                crm6.setDifficulty(aji);
                xd5.setSpawnSettings(xd5.getDifficulty() != Difficulty.PEACEFUL, true);
            }
            else {
                crm6.setDifficulty(aji);
                xd5.setSpawnSettings(this.getSpawnMonsters(), this.animals);
            }
        }
        this.getPlayerList().getPlayers().forEach(this::sendDifficultyUpdate);
    }
    
    public void setDifficultyLocked(final boolean boolean1) {
        for (final ServerLevel xd4 : this.getAllLevels()) {
            final LevelData crm5 = xd4.getLevelData();
            crm5.setDifficultyLocked(boolean1);
        }
        this.getPlayerList().getPlayers().forEach(this::sendDifficultyUpdate);
    }
    
    private void sendDifficultyUpdate(final ServerPlayer xe) {
        final LevelData crm3 = xe.getLevel().getLevelData();
        xe.connection.send(new ClientboundChangeDifficultyPacket(crm3.getDifficulty(), crm3.isDifficultyLocked()));
    }
    
    protected boolean getSpawnMonsters() {
        return true;
    }
    
    public boolean isDemo() {
        return this.isDemo;
    }
    
    public void setDemo(final boolean boolean1) {
        this.isDemo = boolean1;
    }
    
    public void setBonusChest(final boolean boolean1) {
        this.levelHasStartingBonusChest = boolean1;
    }
    
    public LevelStorageSource getStorageSource() {
        return this.storageSource;
    }
    
    public String getResourcePack() {
        return this.resourcePack;
    }
    
    public String getResourcePackHash() {
        return this.resourcePackHash;
    }
    
    public void setResourcePack(final String string1, final String string2) {
        this.resourcePack = string1;
        this.resourcePackHash = string2;
    }
    
    @Override
    public void populateSnooper(final Snooper ajs) {
        ajs.setDynamicData("whitelist_enabled", false);
        ajs.setDynamicData("whitelist_count", 0);
        if (this.playerList != null) {
            ajs.setDynamicData("players_current", this.getPlayerCount());
            ajs.setDynamicData("players_max", this.getMaxPlayers());
            ajs.setDynamicData("players_seen", this.getLevel(DimensionType.OVERWORLD).getLevelStorage().getSeenPlayers().length);
        }
        ajs.setDynamicData("uses_auth", this.onlineMode);
        ajs.setDynamicData("gui_state", this.hasGui() ? "enabled" : "disabled");
        ajs.setDynamicData("run_time", (Util.getMillis() - ajs.getStartupTime()) / 60L * 1000L);
        ajs.setDynamicData("avg_tick_ms", (int)(Mth.average(this.tickTimes) * 1.0E-6));
        int integer3 = 0;
        for (final ServerLevel xd5 : this.getAllLevels()) {
            if (xd5 != null) {
                final LevelData crm6 = xd5.getLevelData();
                ajs.setDynamicData("world[" + integer3 + "][dimension]", xd5.dimension.getType());
                ajs.setDynamicData("world[" + integer3 + "][mode]", crm6.getGameType());
                ajs.setDynamicData("world[" + integer3 + "][difficulty]", xd5.getDifficulty());
                ajs.setDynamicData("world[" + integer3 + "][hardcore]", crm6.isHardcore());
                ajs.setDynamicData("world[" + integer3 + "][generator_name]", crm6.getGeneratorType().getName());
                ajs.setDynamicData("world[" + integer3 + "][generator_version]", crm6.getGeneratorType().getVersion());
                ajs.setDynamicData("world[" + integer3 + "][height]", this.maxBuildHeight);
                ajs.setDynamicData("world[" + integer3 + "][chunks_loaded]", xd5.getChunkSource().getLoadedChunksCount());
                ++integer3;
            }
        }
        ajs.setDynamicData("worlds", integer3);
    }
    
    public abstract boolean isDedicatedServer();
    
    public boolean usesAuthentication() {
        return this.onlineMode;
    }
    
    public void setUsesAuthentication(final boolean boolean1) {
        this.onlineMode = boolean1;
    }
    
    public boolean getPreventProxyConnections() {
        return this.preventProxyConnections;
    }
    
    public void setPreventProxyConnections(final boolean boolean1) {
        this.preventProxyConnections = boolean1;
    }
    
    public boolean isAnimals() {
        return this.animals;
    }
    
    public void setAnimals(final boolean boolean1) {
        this.animals = boolean1;
    }
    
    public boolean isNpcsEnabled() {
        return this.npcs;
    }
    
    public abstract boolean isEpollEnabled();
    
    public void setNpcsEnabled(final boolean boolean1) {
        this.npcs = boolean1;
    }
    
    public boolean isPvpAllowed() {
        return this.pvp;
    }
    
    public void setPvpAllowed(final boolean boolean1) {
        this.pvp = boolean1;
    }
    
    public boolean isFlightAllowed() {
        return this.allowFlight;
    }
    
    public void setFlightAllowed(final boolean boolean1) {
        this.allowFlight = boolean1;
    }
    
    public abstract boolean isCommandBlockEnabled();
    
    public String getMotd() {
        return this.motd;
    }
    
    public void setMotd(final String string) {
        this.motd = string;
    }
    
    public int getMaxBuildHeight() {
        return this.maxBuildHeight;
    }
    
    public void setMaxBuildHeight(final int integer) {
        this.maxBuildHeight = integer;
    }
    
    public boolean isStopped() {
        return this.stopped;
    }
    
    public PlayerList getPlayerList() {
        return this.playerList;
    }
    
    public void setPlayerList(final PlayerList zo) {
        this.playerList = zo;
    }
    
    public abstract boolean isPublished();
    
    public void setDefaultGameMode(final GameType bjt) {
        for (final ServerLevel xd4 : this.getAllLevels()) {
            xd4.getLevelData().setGameType(bjt);
        }
    }
    
    @Nullable
    public ServerConnectionListener getConnection() {
        return this.connection;
    }
    
    public boolean isReady() {
        return this.isReady;
    }
    
    public boolean hasGui() {
        return false;
    }
    
    public abstract boolean publishServer(final GameType bjt, final boolean boolean2, final int integer);
    
    public int getTickCount() {
        return this.tickCount;
    }
    
    public void delayStartProfiler() {
        this.delayProfilerStart = true;
    }
    
    public Snooper getSnooper() {
        return this.snooper;
    }
    
    public int getSpawnProtectionRadius() {
        return 16;
    }
    
    public boolean isUnderSpawnProtection(final Level bjw, final BlockPos fk, final Player ayj) {
        return false;
    }
    
    public void setForceGameType(final boolean boolean1) {
        this.forceGameType = boolean1;
    }
    
    public boolean getForceGameType() {
        return this.forceGameType;
    }
    
    public int getPlayerIdleTimeout() {
        return this.playerIdleTimeout;
    }
    
    public void setPlayerIdleTimeout(final int integer) {
        this.playerIdleTimeout = integer;
    }
    
    public MinecraftSessionService getSessionService() {
        return this.sessionService;
    }
    
    public GameProfileRepository getProfileRepository() {
        return this.profileRepository;
    }
    
    public GameProfileCache getProfileCache() {
        return this.profileCache;
    }
    
    public ServerStatus getStatus() {
        return this.status;
    }
    
    public void invalidateStatus() {
        this.lastServerStatus = 0L;
    }
    
    public int getAbsoluteMaxWorldSize() {
        return 29999984;
    }
    
    public boolean scheduleExecutables() {
        return super.scheduleExecutables() && !this.isStopped();
    }
    
    public Thread getRunningThread() {
        return this.serverThread;
    }
    
    public int getCompressionThreshold() {
        return 256;
    }
    
    public long getNextTickTime() {
        return this.nextTickTime;
    }
    
    public DataFixer getFixerUpper() {
        return this.fixerUpper;
    }
    
    public int getSpawnRadius(@Nullable final ServerLevel xd) {
        if (xd != null) {
            return xd.getGameRules().getInt(GameRules.RULE_SPAWN_RADIUS);
        }
        return 10;
    }
    
    public ServerAdvancementManager getAdvancements() {
        return this.advancements;
    }
    
    public ServerFunctionManager getFunctions() {
        return this.functions;
    }
    
    public void reloadResources() {
        if (!this.isSameThread()) {
            this.execute(this::reloadResources);
            return;
        }
        this.getPlayerList().saveAll();
        this.packRepository.reload();
        this.updateSelectedPacks(this.getLevel(DimensionType.OVERWORLD).getLevelData());
        this.getPlayerList().reloadResources();
        this.refreshRegistries();
    }
    
    private void updateSelectedPacks(final LevelData crm) {
        final List<UnopenedPack> list3 = Lists.newArrayList(this.packRepository.getSelected());
        for (final UnopenedPack yt2 : this.packRepository.getAvailable()) {
            if (!crm.getDisabledDataPacks().contains(yt2.getId()) && !list3.contains(yt2)) {
                MinecraftServer.LOGGER.info("Found new data pack {}, loading it automatically", yt2.getId());
                yt2.getDefaultPosition().<UnopenedPack, UnopenedPack>insert(list3, yt2, yt -> yt, false);
            }
        }
        this.packRepository.setSelected(list3);
        final List<Pack> list4 = Lists.newArrayList();
        this.packRepository.getSelected().forEach(yt -> list4.add(yt.open()));
        final CompletableFuture<Unit> completableFuture5 = this.resources.reload(this.executor, this, list4, MinecraftServer.DATA_RELOAD_INITIAL_TASK);
        this.managedBlock(completableFuture5::isDone);
        try {
            completableFuture5.get();
        }
        catch (Exception exception6) {
            MinecraftServer.LOGGER.error("Failed to reload data packs", (Throwable)exception6);
        }
        crm.getEnabledDataPacks().clear();
        crm.getDisabledDataPacks().clear();
        this.packRepository.getSelected().forEach(yt -> crm.getEnabledDataPacks().add(yt.getId()));
        this.packRepository.getAvailable().forEach(yt -> {
            if (!this.packRepository.getSelected().contains(yt)) {
                crm.getDisabledDataPacks().add(yt.getId());
            }
        });
    }
    
    public void kickUnlistedPlayers(final CommandSourceStack cq) {
        if (!this.isEnforceWhitelist()) {
            return;
        }
        final PlayerList zo3 = cq.getServer().getPlayerList();
        final UserWhiteList zv4 = zo3.getWhiteList();
        if (!zv4.isEnabled()) {
            return;
        }
        final List<ServerPlayer> list5 = Lists.newArrayList(zo3.getPlayers());
        for (final ServerPlayer xe7 : list5) {
            if (!zv4.isWhiteListed(xe7.getGameProfile())) {
                xe7.connection.disconnect(new TranslatableComponent("multiplayer.disconnect.not_whitelisted", new Object[0]));
            }
        }
    }
    
    public ReloadableResourceManager getResources() {
        return this.resources;
    }
    
    public PackRepository<UnopenedPack> getPackRepository() {
        return this.packRepository;
    }
    
    public Commands getCommands() {
        return this.commands;
    }
    
    public CommandSourceStack createCommandSourceStack() {
        return new CommandSourceStack(this, (this.getLevel(DimensionType.OVERWORLD) == null) ? Vec3.ZERO : new Vec3(this.getLevel(DimensionType.OVERWORLD).getSharedSpawnPos()), Vec2.ZERO, this.getLevel(DimensionType.OVERWORLD), 4, "Server", new TextComponent("Server"), this, null);
    }
    
    @Override
    public boolean acceptsSuccess() {
        return true;
    }
    
    @Override
    public boolean acceptsFailure() {
        return true;
    }
    
    public RecipeManager getRecipeManager() {
        return this.recipes;
    }
    
    public TagManager getTags() {
        return this.tags;
    }
    
    public ServerScoreboard getScoreboard() {
        return this.scoreboard;
    }
    
    public CommandStorage getCommandStorage() {
        if (this.commandStorage == null) {
            throw new NullPointerException("Called before server init");
        }
        return this.commandStorage;
    }
    
    public LootTables getLootTables() {
        return this.lootTables;
    }
    
    public PredicateManager getPredicateManager() {
        return this.predicateManager;
    }
    
    public GameRules getGameRules() {
        return this.getLevel(DimensionType.OVERWORLD).getGameRules();
    }
    
    public CustomBossEvents getCustomBossEvents() {
        return this.customBossEvents;
    }
    
    public boolean isEnforceWhitelist() {
        return this.enforceWhitelist;
    }
    
    public void setEnforceWhitelist(final boolean boolean1) {
        this.enforceWhitelist = boolean1;
    }
    
    public float getAverageTickTime() {
        return this.averageTickTime;
    }
    
    public int getProfilePermissions(final GameProfile gameProfile) {
        if (!this.getPlayerList().isOp(gameProfile)) {
            return 0;
        }
        final ServerOpListEntry zq3 = this.getPlayerList().getOps().get(gameProfile);
        if (zq3 != null) {
            return zq3.getLevel();
        }
        if (this.isSingleplayerOwner(gameProfile)) {
            return 4;
        }
        if (this.isSingleplayer()) {
            return this.getPlayerList().isAllowCheatsForAllPlayers() ? 4 : 0;
        }
        return this.getOperatorUserPermissionLevel();
    }
    
    public FrameTimer getFrameTimer() {
        return this.frameTimer;
    }
    
    public GameProfiler getProfiler() {
        return this.profiler;
    }
    
    public Executor getBackgroundTaskExecutor() {
        return this.executor;
    }
    
    public abstract boolean isSingleplayerOwner(final GameProfile gameProfile);
    
    public void saveDebugReport(final Path path) throws IOException {
        final Path path2 = path.resolve("levels");
        for (final Map.Entry<DimensionType, ServerLevel> entry5 : this.levels.entrySet()) {
            final ResourceLocation sm6 = DimensionType.getName(entry5.getKey());
            final Path path3 = path2.resolve(sm6.getNamespace()).resolve(sm6.getPath());
            Files.createDirectories(path3, new FileAttribute[0]);
            entry5.getValue().saveDebugReport(path3);
        }
        this.dumpGameRules(path.resolve("gamerules.txt"));
        this.dumpClasspath(path.resolve("classpath.txt"));
        this.dumpCrashCategory(path.resolve("example_crash.txt"));
        this.dumpMiscStats(path.resolve("stats.txt"));
        this.dumpThreads(path.resolve("threads.txt"));
    }
    
    private void dumpMiscStats(final Path path) throws IOException {
        try (final Writer writer3 = Files.newBufferedWriter(path)) {
            writer3.write(String.format("pending_tasks: %d\n", this.getPendingTasksCount()));
            writer3.write(String.format("average_tick_time: %f\n", this.getAverageTickTime()));
            writer3.write(String.format("tick_times: %s\n", Arrays.toString(this.tickTimes)));
            writer3.write(String.format("queue: %s\n", Util.backgroundExecutor()));
        }
    }
    
    private void dumpCrashCategory(final Path path) throws IOException {
        final CrashReport h3 = new CrashReport("Server dump", new Exception("dummy"));
        this.fillReport(h3);
        try (final Writer writer4 = Files.newBufferedWriter(path)) {
            writer4.write(h3.getFriendlyReport());
        }
    }
    
    private void dumpGameRules(final Path path) throws IOException {
        try (final Writer writer3 = Files.newBufferedWriter(path)) {
            final List<String> list5 = Lists.newArrayList();
            final GameRules bjs6 = this.getGameRules();
            GameRules.visitGameRuleTypes(new GameRules.GameRuleTypeVisitor() {
                @Override
                public <T extends GameRules.Value<T>> void visit(final GameRules.Key<T> d, final GameRules.Type<T> e) {
                    list5.add(String.format("%s=%s\n", d.getId(), bjs6.<T>getRule(d).toString()));
                }
            });
            for (final String string8 : list5) {
                writer3.write(string8);
            }
        }
    }
    
    private void dumpClasspath(final Path path) throws IOException {
        try (final Writer writer3 = Files.newBufferedWriter(path)) {
            final String string5 = System.getProperty("java.class.path");
            final String string6 = System.getProperty("path.separator");
            for (final String string7 : Splitter.on(string6).split(string5)) {
                writer3.write(string7);
                writer3.write("\n");
            }
        }
    }
    
    private void dumpThreads(final Path path) throws IOException {
        final ThreadMXBean threadMXBean3 = ManagementFactory.getThreadMXBean();
        final ThreadInfo[] arr4 = threadMXBean3.dumpAllThreads(true, true);
        Arrays.<ThreadInfo>sort(arr4, Comparator.comparing(ThreadInfo::getThreadName));
        try (final Writer writer5 = Files.newBufferedWriter(path)) {
            for (final ThreadInfo threadInfo10 : arr4) {
                writer5.write(threadInfo10.toString());
                writer5.write(10);
            }
        }
    }
    
    private void refreshRegistries() {
        Block.BLOCK_STATE_REGISTRY.forEach(BlockState::initCache);
    }
    
    static {
        LOGGER = LogManager.getLogger();
        USERID_CACHE_FILE = new File("usercache.json");
        DATA_RELOAD_INITIAL_TASK = CompletableFuture.<Unit>completedFuture(Unit.INSTANCE);
        DEMO_SETTINGS = new LevelSettings("North Carolina".hashCode(), GameType.SURVIVAL, true, false, LevelType.NORMAL).enableStartingBonusItems();
    }
}
