package net.minecraft.client.server;

import org.apache.logging.log4j.LogManager;
import java.util.List;
import com.mojang.authlib.GameProfile;
import java.util.ArrayList;
import com.google.common.collect.Lists;
import java.util.Iterator;
import net.minecraft.server.level.ServerPlayer;
import java.net.InetAddress;
import net.minecraft.world.Snooper;
import net.minecraft.client.ClientBrandRetriever;
import java.util.Optional;
import net.minecraft.CrashReport;
import net.minecraft.world.Difficulty;
import net.minecraft.world.level.GameType;
import net.minecraft.util.profiling.GameProfiler;
import java.util.function.BooleanSupplier;
import java.io.IOException;
import net.minecraft.util.Crypt;
import net.minecraft.SharedConstants;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.world.level.storage.LevelStorage;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.storage.LevelData;
import com.google.gson.JsonElement;
import net.minecraft.world.level.LevelType;
import net.minecraft.server.players.PlayerList;
import net.minecraft.commands.Commands;
import java.io.File;
import net.minecraft.server.level.progress.ChunkProgressListenerFactory;
import net.minecraft.server.players.GameProfileCache;
import com.mojang.authlib.GameProfileRepository;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import java.util.UUID;
import net.minecraft.world.level.LevelSettings;
import net.minecraft.client.Minecraft;
import org.apache.logging.log4j.Logger;
import net.minecraft.server.MinecraftServer;

public class IntegratedServer extends MinecraftServer {
    private static final Logger LOGGER;
    private final Minecraft minecraft;
    private final LevelSettings settings;
    private boolean paused;
    private int publishedPort;
    private LanServerPinger lanPinger;
    private UUID uuid;
    
    public IntegratedServer(final Minecraft dbn, final String string2, final String string3, final LevelSettings bka, final YggdrasilAuthenticationService yggdrasilAuthenticationService, final MinecraftSessionService minecraftSessionService, final GameProfileRepository gameProfileRepository, final GameProfileCache zk, final ChunkProgressListenerFactory xn) {
        super(new File(dbn.gameDirectory, "saves"), dbn.getProxy(), dbn.getFixerUpper(), new Commands(false), yggdrasilAuthenticationService, minecraftSessionService, gameProfileRepository, zk, xn, string2);
        this.publishedPort = -1;
        this.setSingleplayerName(dbn.getUser().getName());
        this.setLevelName(string3);
        this.setDemo(dbn.isDemo());
        this.setBonusChest(bka.hasStartingBonusItems());
        this.setMaxBuildHeight(256);
        this.setPlayerList(new IntegratedPlayerList(this));
        this.minecraft = dbn;
        this.settings = (this.isDemo() ? MinecraftServer.DEMO_SETTINGS : bka);
    }
    
    public void loadLevel(final String string1, final String string2, final long long3, final LevelType bkd, final JsonElement jsonElement) {
        this.ensureLevelConversion(string1);
        final LevelStorage crn8 = this.getStorageSource().selectLevel(string1, this);
        this.detectBundledResources(this.getLevelIdName(), crn8);
        LevelData crm9 = crn8.prepareLevel();
        if (crm9 == null) {
            crm9 = new LevelData(this.settings, string2);
        }
        else {
            crm9.setLevelName(string2);
        }
        crm9.setModdedInfo(this.getServerModName(), this.getModdedStatus().isPresent());
        this.loadDataPacks(crn8.getFolder(), crm9);
        final ChunkProgressListener xm10 = this.progressListenerFactory.create(11);
        this.createLevels(crn8, crm9, this.settings, xm10);
        if (this.getLevel(DimensionType.OVERWORLD).getLevelData().getDifficulty() == null) {
            this.setDifficulty(this.minecraft.options.difficulty, true);
        }
        this.prepareLevels(xm10);
    }
    
    public boolean initServer() throws IOException {
        IntegratedServer.LOGGER.info("Starting integrated minecraft server version " + SharedConstants.getCurrentVersion().getName());
        this.setUsesAuthentication(true);
        this.setAnimals(true);
        this.setNpcsEnabled(true);
        this.setPvpAllowed(true);
        this.setFlightAllowed(true);
        IntegratedServer.LOGGER.info("Generating keypair");
        this.setKeyPair(Crypt.generateKeyPair());
        this.loadLevel(this.getLevelIdName(), this.getLevelName(), this.settings.getSeed(), this.settings.getLevelType(), this.settings.getLevelTypeOptions());
        this.setMotd(this.getSingleplayerName() + " - " + this.getLevel(DimensionType.OVERWORLD).getLevelData().getLevelName());
        return true;
    }
    
    public void tickServer(final BooleanSupplier booleanSupplier) {
        final boolean boolean3 = this.paused;
        this.paused = (Minecraft.getInstance().getConnection() != null && Minecraft.getInstance().isPaused());
        final GameProfiler aik4 = this.getProfiler();
        if (!boolean3 && this.paused) {
            aik4.push("autoSave");
            IntegratedServer.LOGGER.info("Saving and pausing game...");
            this.getPlayerList().saveAll();
            this.saveAllChunks(false, false, false);
            aik4.pop();
        }
        if (this.paused) {
            return;
        }
        super.tickServer(booleanSupplier);
        final int integer5 = Math.max(2, this.minecraft.options.renderDistance - 1);
        if (integer5 != this.getPlayerList().getViewDistance()) {
            IntegratedServer.LOGGER.info("Changing view distance to {}, from {}", integer5, this.getPlayerList().getViewDistance());
            this.getPlayerList().setViewDistance(integer5);
        }
    }
    
    @Override
    public boolean canGenerateStructures() {
        return false;
    }
    
    @Override
    public GameType getDefaultGameType() {
        return this.settings.getGameType();
    }
    
    @Override
    public Difficulty getDefaultDifficulty() {
        return this.minecraft.level.getLevelData().getDifficulty();
    }
    
    @Override
    public boolean isHardcore() {
        return this.settings.isHardcore();
    }
    
    @Override
    public boolean shouldRconBroadcast() {
        return true;
    }
    
    @Override
    public boolean shouldInformAdmins() {
        return true;
    }
    
    @Override
    public File getServerDirectory() {
        return this.minecraft.gameDirectory;
    }
    
    @Override
    public boolean isDedicatedServer() {
        return false;
    }
    
    @Override
    public boolean isEpollEnabled() {
        return false;
    }
    
    public void onServerCrash(final CrashReport h) {
        this.minecraft.delayCrash(h);
    }
    
    @Override
    public CrashReport fillReport(CrashReport h) {
        h = super.fillReport(h);
        h.getSystemDetails().setDetail("Type", "Integrated Server (map_client.txt)");
        h.getSystemDetails().setDetail("Is Modded", () -> this.getModdedStatus().orElse("Probably not. Jar signature remains and both client + server brands are untouched."));
        return h;
    }
    
    @Override
    public Optional<String> getModdedStatus() {
        String string2 = ClientBrandRetriever.getClientModName();
        if (!string2.equals("vanilla")) {
            return Optional.<String>of("Definitely; Client brand changed to '" + string2 + "'");
        }
        string2 = this.getServerModName();
        if (!"vanilla".equals(string2)) {
            return Optional.<String>of("Definitely; Server brand changed to '" + string2 + "'");
        }
        if (Minecraft.class.getSigners() == null) {
            return Optional.<String>of("Very likely; Jar signature invalidated");
        }
        return Optional.<String>empty();
    }
    
    @Override
    public void populateSnooper(final Snooper ajs) {
        super.populateSnooper(ajs);
        ajs.setDynamicData("snooper_partner", this.minecraft.getSnooper().getToken());
    }
    
    @Override
    public boolean publishServer(final GameType bjt, final boolean boolean2, final int integer) {
        try {
            this.getConnection().startTcpServerListener(null, integer);
            IntegratedServer.LOGGER.info("Started serving on {}", integer);
            this.publishedPort = integer;
            (this.lanPinger = new LanServerPinger(this.getMotd(), integer + "")).start();
            this.getPlayerList().setOverrideGameMode(bjt);
            this.getPlayerList().setAllowCheatsForAllPlayers(boolean2);
            final int integer2 = this.getProfilePermissions(this.minecraft.player.getGameProfile());
            this.minecraft.player.setPermissionLevel(integer2);
            for (final ServerPlayer xe7 : this.getPlayerList().getPlayers()) {
                this.getCommands().sendCommands(xe7);
            }
            return true;
        }
        catch (IOException ex) {
            return false;
        }
    }
    
    public void stopServer() {
        super.stopServer();
        if (this.lanPinger != null) {
            this.lanPinger.interrupt();
            this.lanPinger = null;
        }
    }
    
    @Override
    public void halt(final boolean boolean1) {
        final ArrayList<Object> list2;
        final Iterator<ServerPlayer> iterator;
        ServerPlayer xe4;
        this.executeBlocking(() -> {
            list2 = Lists.newArrayList(this.getPlayerList().getPlayers());
            list2.iterator();
            while (iterator.hasNext()) {
                xe4 = iterator.next();
                if (!xe4.getUUID().equals(this.uuid)) {
                    this.getPlayerList().remove(xe4);
                }
            }
            return;
        });
        super.halt(boolean1);
        if (this.lanPinger != null) {
            this.lanPinger.interrupt();
            this.lanPinger = null;
        }
    }
    
    @Override
    public boolean isPublished() {
        return this.publishedPort > -1;
    }
    
    @Override
    public int getPort() {
        return this.publishedPort;
    }
    
    @Override
    public void setDefaultGameMode(final GameType bjt) {
        super.setDefaultGameMode(bjt);
        this.getPlayerList().setOverrideGameMode(bjt);
    }
    
    @Override
    public boolean isCommandBlockEnabled() {
        return true;
    }
    
    @Override
    public int getOperatorUserPermissionLevel() {
        return 2;
    }
    
    @Override
    public int getFunctionCompilationLevel() {
        return 2;
    }
    
    public void setUUID(final UUID uUID) {
        this.uuid = uUID;
    }
    
    @Override
    public boolean isSingleplayerOwner(final GameProfile gameProfile) {
        return gameProfile.getName().equalsIgnoreCase(this.getSingleplayerName());
    }
    
    static {
        LOGGER = LogManager.getLogger();
    }
}
