--- a/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
@@ -5,9 +5,9 @@
 import com.mojang.datafixers.DataFixer;
 import com.mojang.logging.LogUtils;
 import java.io.BufferedReader;
+import java.io.BufferedWriter;
 import java.io.IOException;
 import java.io.InputStreamReader;
-import java.io.Writer;
 import java.net.InetAddress;
 import java.net.Proxy;
 import java.nio.charset.StandardCharsets;
@@ -48,16 +48,28 @@
 import net.minecraft.world.entity.player.Player;
 import net.minecraft.world.level.GameRules;
 import net.minecraft.world.level.GameType;
-import net.minecraft.world.level.Level;
 import net.minecraft.world.level.block.entity.SkullBlockEntity;
 import net.minecraft.world.level.storage.LevelStorageSource;
 import org.slf4j.Logger;
 
+// CraftBukkit start
+import net.minecraft.server.WorldLoader;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.io.IoBuilder;
+import org.bukkit.command.CommandSender;
+import co.aikar.timings.MinecraftTimings; // Paper
+import org.bukkit.event.server.ServerCommandEvent;
+import org.bukkit.craftbukkit.v1_19_R2.util.Waitable;
+import org.bukkit.event.server.RemoteServerCommandEvent;
+// CraftBukkit end
+
 public class DedicatedServer extends MinecraftServer implements ServerInterface {
+
     static final Logger LOGGER = LogUtils.getLogger();
     private static final int CONVERSION_RETRY_DELAY_MS = 5000;
     private static final int CONVERSION_RETRIES = 2;
-    private final List<ConsoleInput> consoleInput = Collections.synchronizedList(Lists.newArrayList());
+    private final java.util.Queue<ConsoleInput> serverCommandQueue = new java.util.concurrent.ConcurrentLinkedQueue<>(); // Paper - use a proper queuemmands
     @Nullable
     private QueryThreadGs4 queryThreadGs4;
     public final RconConsoleSource rconConsoleSource;
@@ -69,125 +81,255 @@
     @Nullable
     private final TextFilterClient textFilterClient;
 
-    public DedicatedServer(Thread serverThread, LevelStorageSource.LevelStorageAccess session, PackRepository dataPackManager, WorldStem saveLoader, DedicatedServerSettings propertiesLoader, DataFixer dataFixer, Services apiServices, ChunkProgressListenerFactory worldGenerationProgressListenerFactory) {
-        super(serverThread, session, dataPackManager, saveLoader, Proxy.NO_PROXY, dataFixer, apiServices, worldGenerationProgressListenerFactory);
-        this.settings = propertiesLoader;
+    // CraftBukkit start - Signature changed
+    public DedicatedServer(joptsimple.OptionSet options, WorldLoader.DataLoadContext worldLoader, Thread thread, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PackRepository resourcepackrepository, WorldStem worldstem, DedicatedServerSettings dedicatedserversettings, DataFixer datafixer, Services services, ChunkProgressListenerFactory worldloadlistenerfactory) {
+        super(options, worldLoader, thread, convertable_conversionsession, resourcepackrepository, worldstem, Proxy.NO_PROXY, datafixer, services, worldloadlistenerfactory);
+        // CraftBukkit end
+        this.settings = dedicatedserversettings;
         this.rconConsoleSource = new RconConsoleSource(this);
-        this.textFilterClient = TextFilterClient.createFromConfig(propertiesLoader.getProperties().textFilteringConfig);
+        this.textFilterClient = TextFilterClient.createFromConfig(dedicatedserversettings.getProperties().textFilteringConfig);
     }
 
     @Override
     public boolean initServer() throws IOException {
         Thread thread = new Thread("Server console handler") {
-            @Override
             public void run() {
-                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8));
+                // CraftBukkit start
+                if (!org.bukkit.craftbukkit.v1_19_R2.Main.useConsole) {
+                    return;
+                }
+                // Paper start - Use TerminalConsoleAppender
+                new com.destroystokyo.paper.console.PaperConsole(DedicatedServer.this).start();
+                /*
+                jline.console.ConsoleReader bufferedreader = reader;
 
-                String string;
+                // MC-33041, SPIGOT-5538: if System.in is not valid due to javaw, then return
                 try {
-                    while(!DedicatedServer.this.isStopped() && DedicatedServer.this.isRunning() && (string = bufferedReader.readLine()) != null) {
-                        DedicatedServer.this.handleConsoleInput(string, DedicatedServer.this.createCommandSourceStack());
+                    System.in.available();
+                } catch (IOException ex) {
+                    return;
+                }
+                // CraftBukkit end
+
+                String s;
+
+                try {
+                    // CraftBukkit start - JLine disabling compatibility
+                    while (!DedicatedServer.this.isStopped() && DedicatedServer.this.isRunning()) {
+                        if (org.bukkit.craftbukkit.v1_19_R2.Main.useJline) {
+                            s = bufferedreader.readLine(">", null);
+                        } else {
+                            s = bufferedreader.readLine();
+                        }
+
+                        // SPIGOT-5220: Throttle if EOF (ctrl^d) or stdin is /dev/null
+                        if (s == null) {
+                            try {
+                                Thread.sleep(50L);
+                            } catch (InterruptedException ex) {
+                                Thread.currentThread().interrupt();
+                            }
+                            continue;
+                        }
+                        if (s.trim().length() > 0) { // Trim to filter lines which are just spaces
+                            DedicatedServer.this.issueCommand(s, DedicatedServer.this.getServerCommandListener());
+                        }
+                        // CraftBukkit end
                     }
-                } catch (IOException var4) {
-                    DedicatedServer.LOGGER.error("Exception handling console input", (Throwable)var4);
+                } catch (IOException ioexception) {
+                    DedicatedServer.LOGGER.error("Exception handling console input", ioexception);
                 }
 
+                */
+                // Paper end
             }
         };
+
+        // CraftBukkit start - TODO: handle command-line logging arguments
+        java.util.logging.Logger global = java.util.logging.Logger.getLogger("");
+        global.setUseParentHandlers(false);
+        for (java.util.logging.Handler handler : global.getHandlers()) {
+            global.removeHandler(handler);
+        }
+        global.addHandler(new org.bukkit.craftbukkit.v1_19_R2.util.ForwardLogHandler());
+
+        // Paper start - Not needed with TerminalConsoleAppender
+        final org.apache.logging.log4j.Logger logger = LogManager.getRootLogger();
+        /*
+        final org.apache.logging.log4j.core.Logger logger = ((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger());
+        for (org.apache.logging.log4j.core.Appender appender : logger.getAppenders().values()) {
+            if (appender instanceof org.apache.logging.log4j.core.appender.ConsoleAppender) {
+                logger.removeAppender(appender);
+            }
+        }
+
+        new org.bukkit.craftbukkit.v1_19_R2.util.TerminalConsoleWriterThread(System.out, this.reader).start();
+        */
+        // Paper end
+
+        System.setOut(IoBuilder.forLogger(logger).setLevel(Level.INFO).buildPrintStream());
+        System.setErr(IoBuilder.forLogger(logger).setLevel(Level.WARN).buildPrintStream());
+        // CraftBukkit end
+
         thread.setDaemon(true);
-        thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER));
-        thread.start();
-        LOGGER.info("Starting minecraft server version {}", (Object)SharedConstants.getCurrentVersion().getName());
+        thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(DedicatedServer.LOGGER));
+        // thread.start(); // Paper - moved down
+        DedicatedServer.LOGGER.info("Starting minecraft server version {}", SharedConstants.getCurrentVersion().getName());
         if (Runtime.getRuntime().maxMemory() / 1024L / 1024L < 512L) {
-            LOGGER.warn("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\"");
+            DedicatedServer.LOGGER.warn("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\"");
         }
 
-        LOGGER.info("Loading properties");
-        DedicatedServerProperties dedicatedServerProperties = this.settings.getProperties();
+        // Paper start - detect running as root
+        if (io.papermc.paper.util.ServerEnvironment.userIsRootOrAdmin()) {
+            DedicatedServer.LOGGER.warn("****************************");
+            DedicatedServer.LOGGER.warn("YOU ARE RUNNING THIS SERVER AS AN ADMINISTRATIVE OR ROOT USER. THIS IS NOT ADVISED.");
+            DedicatedServer.LOGGER.warn("YOU ARE OPENING YOURSELF UP TO POTENTIAL RISKS WHEN DOING THIS.");
+            DedicatedServer.LOGGER.warn("FOR MORE INFORMATION, SEE https://madelinemiller.dev/blog/root-minecraft-server/");
+            DedicatedServer.LOGGER.warn("****************************");
+        }
+        // Paper end
+
+        DedicatedServer.LOGGER.info("Loading properties");
+        DedicatedServerProperties dedicatedserverproperties = this.settings.getProperties();
+
         if (this.isSingleplayer()) {
             this.setLocalIp("127.0.0.1");
         } else {
-            this.setUsesAuthentication(dedicatedServerProperties.onlineMode);
-            this.setPreventProxyConnections(dedicatedServerProperties.preventProxyConnections);
-            this.setLocalIp(dedicatedServerProperties.serverIp);
+            this.setUsesAuthentication(dedicatedserverproperties.onlineMode);
+            this.setPreventProxyConnections(dedicatedserverproperties.preventProxyConnections);
+            this.setLocalIp(dedicatedserverproperties.serverIp);
         }
+        // Spigot start
+        this.setPlayerList(new DedicatedPlayerList(this, this.registries(), this.playerDataStorage));
+        org.spigotmc.SpigotConfig.init((java.io.File) options.valueOf("spigot-settings"));
+        org.spigotmc.SpigotConfig.registerCommands();
+        // Spigot end
+        // Paper start
+        io.papermc.paper.util.ObfHelper.INSTANCE.getClass(); // Paper - load mappings for stacktrace deobf and etc.
+        paperConfigurations.initializeGlobalConfiguration();
+        paperConfigurations.initializeWorldDefaultsConfiguration();
+        // Paper start - moved up to right after PlayerList creation but before file load/save
+        if (this.convertOldUsers()) {
+            this.getProfileCache().save(false); // Paper
+        }
+        this.getPlayerList().loadAndSaveFiles(); // Must be after convertNames
+        // Paper end - moved up
+        org.spigotmc.WatchdogThread.doStart(org.spigotmc.SpigotConfig.timeoutTime, org.spigotmc.SpigotConfig.restartOnCrash);
+        thread.start(); // Paper - start console thread after MinecraftServer.console & PaperConfig are initialized
+        io.papermc.paper.command.PaperCommands.registerCommands(this);
+        com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics();
+        com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // load version history now
+        io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // init PaperBrigadierProvider
+        // Paper end
+
+        this.setPvpAllowed(dedicatedserverproperties.pvp);
+        this.setFlightAllowed(dedicatedserverproperties.allowFlight);
+        this.setMotd(dedicatedserverproperties.motd);
+        super.setPlayerIdleTimeout((Integer) dedicatedserverproperties.playerIdleTimeout.get());
+        this.setEnforceWhitelist(dedicatedserverproperties.enforceWhitelist);
+        // this.worldData.setGameType(dedicatedserverproperties.gamemode); // CraftBukkit - moved to world loading
+        DedicatedServer.LOGGER.info("Default game type: {}", dedicatedserverproperties.gamemode);
+        // Paper start - Unix domain socket support
+        java.net.SocketAddress bindAddress;
+        if (this.getLocalIp().startsWith("unix:")) {
+            if (!io.netty.channel.epoll.Epoll.isAvailable()) {
+                DedicatedServer.LOGGER.error("**** INVALID CONFIGURATION!");
+                DedicatedServer.LOGGER.error("You are trying to use a Unix domain socket but you're not on a supported OS.");
+                return false;
+            } else if (!io.papermc.paper.configuration.GlobalConfiguration.get().proxies.velocity.enabled && !org.spigotmc.SpigotConfig.bungee) {
+                DedicatedServer.LOGGER.error("**** INVALID CONFIGURATION!");
+                DedicatedServer.LOGGER.error("Unix domain sockets require IPs to be forwarded from a proxy.");
+                return false;
+            }
+            bindAddress = new io.netty.channel.unix.DomainSocketAddress(this.getLocalIp().substring("unix:".length()));
+        } else {
+        InetAddress inetaddress = null;
 
-        this.setPvpAllowed(dedicatedServerProperties.pvp);
-        this.setFlightAllowed(dedicatedServerProperties.allowFlight);
-        this.setMotd(dedicatedServerProperties.motd);
-        super.setPlayerIdleTimeout(dedicatedServerProperties.playerIdleTimeout.get());
-        this.setEnforceWhitelist(dedicatedServerProperties.enforceWhitelist);
-        this.worldData.setGameType(dedicatedServerProperties.gamemode);
-        LOGGER.info("Default game type: {}", (Object)dedicatedServerProperties.gamemode);
-        InetAddress inetAddress = null;
         if (!this.getLocalIp().isEmpty()) {
-            inetAddress = InetAddress.getByName(this.getLocalIp());
+            inetaddress = InetAddress.getByName(this.getLocalIp());
         }
 
         if (this.getPort() < 0) {
-            this.setPort(dedicatedServerProperties.serverPort);
+            this.setPort(dedicatedserverproperties.serverPort);
+        }
+        bindAddress = new java.net.InetSocketAddress(inetaddress, this.getPort());
         }
+        // Paper end
 
         this.initializeKeyPair();
-        LOGGER.info("Starting Minecraft server on {}:{}", this.getLocalIp().isEmpty() ? "*" : this.getLocalIp(), this.getPort());
+        DedicatedServer.LOGGER.info("Starting Minecraft server on {}:{}", this.getLocalIp().isEmpty() ? "*" : this.getLocalIp(), this.getPort());
 
         try {
-            this.getConnection().startTcpServerListener(inetAddress, this.getPort());
-        } catch (IOException var10) {
-            LOGGER.warn("**** FAILED TO BIND TO PORT!");
-            LOGGER.warn("The exception was: {}", (Object)var10.toString());
-            LOGGER.warn("Perhaps a server is already running on that port?");
+            this.getConnection().bind(bindAddress); // Paper - Unix domain socket support
+        } catch (IOException ioexception) {
+            DedicatedServer.LOGGER.warn("**** FAILED TO BIND TO PORT!");
+            DedicatedServer.LOGGER.warn("The exception was: {}", ioexception.toString());
+            DedicatedServer.LOGGER.warn("Perhaps a server is already running on that port?");
             return false;
         }
 
+        // CraftBukkit start
+        // this.setPlayerList(new DedicatedPlayerList(this, this.registries(), this.playerDataStorage)); // Spigot - moved up
+        server.loadPlugins();
+        server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.STARTUP);
+        // CraftBukkit end
+
         if (!this.usesAuthentication()) {
-            LOGGER.warn("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!");
-            LOGGER.warn("The server will make no attempt to authenticate usernames. Beware.");
-            LOGGER.warn("While this makes the game possible to play without internet access, it also opens up the ability for hackers to connect with any username they choose.");
-            LOGGER.warn("To change this, set \"online-mode\" to \"true\" in the server.properties file.");
+            DedicatedServer.LOGGER.warn("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!");
+            DedicatedServer.LOGGER.warn("The server will make no attempt to authenticate usernames. Beware.");
+            // Spigot start
+            if (org.spigotmc.SpigotConfig.bungee) {
+                DedicatedServer.LOGGER.warn("Whilst this makes it possible to use BungeeCord, unless access to your server is properly restricted, it also opens up the ability for hackers to connect with any username they choose.");
+                DedicatedServer.LOGGER.warn("Please see http://www.spigotmc.org/wiki/firewall-guide/ for further information.");
+            } else {
+                DedicatedServer.LOGGER.warn("While this makes the game possible to play without internet access, it also opens up the ability for hackers to connect with any username they choose.");
+            }
+            // Spigot end
+            DedicatedServer.LOGGER.warn("To change this, set \"online-mode\" to \"true\" in the server.properties file.");
         }
 
-        if (this.convertOldUsers()) {
-            this.getProfileCache().save();
-        }
 
         if (!OldUsersConverter.serverReadyAfterUserconversion(this)) {
             return false;
         } else {
-            this.setPlayerList(new DedicatedPlayerList(this, this.registries(), this.playerDataStorage));
-            long l = Util.getNanos();
+            // this.setPlayerList(new DedicatedPlayerList(this, this.registries(), this.playerDataStorage)); // CraftBukkit - moved up
+            long i = Util.getNanos();
+
             SkullBlockEntity.setup(this.services, this);
             GameProfileCache.setUsesAuthentication(this.usesAuthentication());
-            LOGGER.info("Preparing level \"{}\"", (Object)this.getLevelIdName());
-            this.loadLevel();
-            long m = Util.getNanos() - l;
-            String string = String.format(Locale.ROOT, "%.3fs", (double)m / 1.0E9D);
-            LOGGER.info("Done ({})! For help, type \"help\"", (Object)string);
-            if (dedicatedServerProperties.announcePlayerAchievements != null) {
-                this.getGameRules().getRule(GameRules.RULE_ANNOUNCE_ADVANCEMENTS).set(dedicatedServerProperties.announcePlayerAchievements, this);
+            DedicatedServer.LOGGER.info("Preparing level \"{}\"", this.getLevelIdName());
+            this.loadLevel(storageSource.getLevelId()); // CraftBukkit
+            long j = Util.getNanos() - i;
+            String s = String.format(Locale.ROOT, "%.3fs", (double) j / 1.0E9D);
+
+            //DedicatedServer.LOGGER.info("Done ({})! For help, type \"help\"", s); // Paper moved to after init
+            if (dedicatedserverproperties.announcePlayerAchievements != null) {
+                ((GameRules.BooleanValue) this.getGameRules().getRule(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)).set(dedicatedserverproperties.announcePlayerAchievements, null); // Paper
             }
 
-            if (dedicatedServerProperties.enableQuery) {
-                LOGGER.info("Starting GS4 status listener");
+            if (dedicatedserverproperties.enableQuery) {
+                DedicatedServer.LOGGER.info("Starting GS4 status listener");
                 this.queryThreadGs4 = QueryThreadGs4.create(this);
             }
 
-            if (dedicatedServerProperties.enableRcon) {
-                LOGGER.info("Starting remote control listener");
+            if (dedicatedserverproperties.enableRcon) {
+                DedicatedServer.LOGGER.info("Starting remote control listener");
                 this.rconThread = RconThread.create(this);
+                this.remoteConsole = new org.bukkit.craftbukkit.v1_19_R2.command.CraftRemoteConsoleCommandSender(this.rconConsoleSource); // CraftBukkit
             }
 
-            if (this.getMaxTickLength() > 0L) {
-                Thread thread2 = new Thread(new ServerWatchdog(this));
-                thread2.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandlerWithName(LOGGER));
-                thread2.setName("Server Watchdog");
-                thread2.setDaemon(true);
-                thread2.start();
+            if (false && this.getMaxTickLength() > 0L) {  // Spigot - disable
+                Thread thread1 = new Thread(new ServerWatchdog(this));
+
+                thread1.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandlerWithName(DedicatedServer.LOGGER));
+                thread1.setName("Server Watchdog");
+                thread1.setDaemon(true);
+                thread1.start();
             }
 
-            if (dedicatedServerProperties.enableJmxMonitoring) {
+            if (dedicatedserverproperties.enableJmxMonitoring) {
                 MinecraftServerStatistics.registerJmxMonitoring(this);
-                LOGGER.info("JMX monitoring enabled");
+                DedicatedServer.LOGGER.info("JMX monitoring enabled");
             }
 
             return true;
@@ -216,7 +358,7 @@
 
     @Override
     public void forceDifficulty() {
-        this.setDifficulty(this.getProperties().difficulty, true);
+        // this.setDifficulty(this.getProperties().difficulty, true); // Paper - Don't overwrite level.dat's difficulty, keep current
     }
 
     @Override
@@ -237,21 +379,36 @@
 
     @Override
     public void dumpServerProperties(Path file) throws IOException {
-        DedicatedServerProperties dedicatedServerProperties = this.getProperties();
+        DedicatedServerProperties dedicatedserverproperties = this.getProperties();
+        BufferedWriter bufferedwriter = Files.newBufferedWriter(file);
 
-        try (Writer writer = Files.newBufferedWriter(file)) {
-            writer.write(String.format(Locale.ROOT, "sync-chunk-writes=%s%n", dedicatedServerProperties.syncChunkWrites));
-            writer.write(String.format(Locale.ROOT, "gamemode=%s%n", dedicatedServerProperties.gamemode));
-            writer.write(String.format(Locale.ROOT, "spawn-monsters=%s%n", dedicatedServerProperties.spawnMonsters));
-            writer.write(String.format(Locale.ROOT, "entity-broadcast-range-percentage=%d%n", dedicatedServerProperties.entityBroadcastRangePercentage));
-            writer.write(String.format(Locale.ROOT, "max-world-size=%d%n", dedicatedServerProperties.maxWorldSize));
-            writer.write(String.format(Locale.ROOT, "spawn-npcs=%s%n", dedicatedServerProperties.spawnNpcs));
-            writer.write(String.format(Locale.ROOT, "view-distance=%d%n", dedicatedServerProperties.viewDistance));
-            writer.write(String.format(Locale.ROOT, "simulation-distance=%d%n", dedicatedServerProperties.simulationDistance));
-            writer.write(String.format(Locale.ROOT, "spawn-animals=%s%n", dedicatedServerProperties.spawnAnimals));
-            writer.write(String.format(Locale.ROOT, "generate-structures=%s%n", dedicatedServerProperties.worldOptions.generateStructures()));
-            writer.write(String.format(Locale.ROOT, "use-native=%s%n", dedicatedServerProperties.useNativeTransport));
-            writer.write(String.format(Locale.ROOT, "rate-limit=%d%n", dedicatedServerProperties.rateLimitPacketsPerSecond));
+        try {
+            bufferedwriter.write(String.format(Locale.ROOT, "sync-chunk-writes=%s%n", dedicatedserverproperties.syncChunkWrites));
+            bufferedwriter.write(String.format(Locale.ROOT, "gamemode=%s%n", dedicatedserverproperties.gamemode));
+            bufferedwriter.write(String.format(Locale.ROOT, "spawn-monsters=%s%n", dedicatedserverproperties.spawnMonsters));
+            bufferedwriter.write(String.format(Locale.ROOT, "entity-broadcast-range-percentage=%d%n", dedicatedserverproperties.entityBroadcastRangePercentage));
+            bufferedwriter.write(String.format(Locale.ROOT, "max-world-size=%d%n", dedicatedserverproperties.maxWorldSize));
+            bufferedwriter.write(String.format(Locale.ROOT, "spawn-npcs=%s%n", dedicatedserverproperties.spawnNpcs));
+            bufferedwriter.write(String.format(Locale.ROOT, "view-distance=%d%n", dedicatedserverproperties.viewDistance));
+            bufferedwriter.write(String.format(Locale.ROOT, "simulation-distance=%d%n", dedicatedserverproperties.simulationDistance));
+            bufferedwriter.write(String.format(Locale.ROOT, "spawn-animals=%s%n", dedicatedserverproperties.spawnAnimals));
+            bufferedwriter.write(String.format(Locale.ROOT, "generate-structures=%s%n", dedicatedserverproperties.worldOptions.generateStructures()));
+            bufferedwriter.write(String.format(Locale.ROOT, "use-native=%s%n", dedicatedserverproperties.useNativeTransport));
+            bufferedwriter.write(String.format(Locale.ROOT, "rate-limit=%d%n", dedicatedserverproperties.rateLimitPacketsPerSecond));
+        } catch (Throwable throwable) {
+            if (bufferedwriter != null) {
+                try {
+                    bufferedwriter.close();
+                } catch (Throwable throwable1) {
+                    throwable.addSuppressed(throwable1);
+                }
+            }
+
+            throw throwable;
+        }
+
+        if (bufferedwriter != null) {
+            bufferedwriter.close();
         }
 
     }
@@ -267,13 +424,15 @@
         }
 
         if (this.rconThread != null) {
-            this.rconThread.stop();
+            //this.remoteControlListener.b(); // Paper - don't wait for remote connections
         }
 
         if (this.queryThreadGs4 != null) {
-            this.queryThreadGs4.stop();
+            //this.remoteStatusListener.b(); // Paper - don't wait for remote connections
         }
 
+        hasFullyShutdown = true; // Paper
+        System.exit(this.abnormalExit ? 70 : 0); // CraftBukkit // Paper
     }
 
     @Override
@@ -287,16 +446,56 @@
         return this.getProperties().allowNether;
     }
 
+    static final java.util.concurrent.atomic.AtomicInteger ASYNC_DEBUG_CHUNKS_COUNT = new java.util.concurrent.atomic.AtomicInteger(); // Paper - rewrite chunk system
+
     public void handleConsoleInput(String command, CommandSourceStack commandSource) {
-        this.consoleInput.add(new ConsoleInput(command, commandSource));
+        // Paper start - rewrite chunk system
+        if (command.equalsIgnoreCase("paper debug chunks --async")) {
+            LOGGER.info("Scheduling async debug chunks");
+            Runnable run = () -> {
+                LOGGER.info("Async debug chunks executing");
+                io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.dumpAllChunkLoadInfo(false);
+                CommandSender sender = MinecraftServer.getServer().console;
+                java.io.File file = new java.io.File(new java.io.File(new java.io.File("."), "debug"),
+                    "chunks-" + java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(java.time.LocalDateTime.now()) + ".txt");
+                sender.sendMessage(net.kyori.adventure.text.Component.text("Writing chunk information dump to " + file, net.kyori.adventure.text.format.NamedTextColor.GREEN));
+                try {
+                    io.papermc.paper.util.MCUtil.dumpChunks(file, true);
+                    sender.sendMessage(net.kyori.adventure.text.Component.text("Successfully written chunk information!", net.kyori.adventure.text.format.NamedTextColor.GREEN));
+                } catch (Throwable thr) {
+                    MinecraftServer.LOGGER.warn("Failed to dump chunk information to file " + file.toString(), thr);
+                    sender.sendMessage(net.kyori.adventure.text.Component.text("Failed to dump chunk information, see console", net.kyori.adventure.text.format.NamedTextColor.RED));
+                }
+            };
+            Thread t = new Thread(run);
+            t.setName("Async debug thread #" + ASYNC_DEBUG_CHUNKS_COUNT.getAndIncrement());
+            t.setDaemon(true);
+            t.start();
+            return;
+        }
+        // Paper end - rewrite chunk system
+        this.serverCommandQueue.add(new ConsoleInput(command, commandSource)); // Paper - use proper queue
     }
 
     public void handleConsoleInputs() {
-        while(!this.consoleInput.isEmpty()) {
-            ConsoleInput consoleInput = this.consoleInput.remove(0);
-            this.getCommands().performPrefixedCommand(consoleInput.source, consoleInput.msg);
+        MinecraftTimings.serverCommandTimer.startTiming(); // Spigot
+        // Paper start - use proper queue
+        ConsoleInput servercommand;
+        while ((servercommand = this.serverCommandQueue.poll()) != null) {
+            // Paper end
+
+            // CraftBukkit start - ServerCommand for preprocessing
+            ServerCommandEvent event = new ServerCommandEvent(console, servercommand.msg);
+            server.getPluginManager().callEvent(event);
+            if (event.isCancelled()) continue;
+            servercommand = new ConsoleInput(event.getCommand(), servercommand.source);
+
+            // this.getCommands().performPrefixedCommand(servercommand.source, servercommand.msg); // Called in dispatchServerCommand
+            server.dispatchServerCommand(console, servercommand);
+            // CraftBukkit end
         }
 
+        MinecraftTimings.serverCommandTimer.stopTiming(); // Spigot
     }
 
     @Override
@@ -316,7 +515,7 @@
 
     @Override
     public DedicatedPlayerList getPlayerList() {
-        return (DedicatedPlayerList)super.getPlayerList();
+        return (DedicatedPlayerList) super.getPlayerList();
     }
 
     @Override
@@ -363,7 +562,7 @@
 
     @Override
     public boolean isUnderSpawnProtection(ServerLevel world, BlockPos pos, Player player) {
-        if (world.dimension() != Level.OVERWORLD) {
+        if (world.dimension() != net.minecraft.world.level.Level.OVERWORLD) {
             return false;
         } else if (this.getPlayerList().getOps().isEmpty()) {
             return false;
@@ -372,10 +571,11 @@
         } else if (this.getSpawnProtectionRadius() <= 0) {
             return false;
         } else {
-            BlockPos blockPos = world.getSharedSpawnPos();
-            int i = Mth.abs(pos.getX() - blockPos.getX());
-            int j = Mth.abs(pos.getZ() - blockPos.getZ());
+            BlockPos blockposition1 = world.getSharedSpawnPos();
+            int i = Mth.abs(pos.getX() - blockposition1.getX());
+            int j = Mth.abs(pos.getZ() - blockposition1.getZ());
             int k = Math.max(i, j);
+
             return k <= this.getSpawnProtectionRadius();
         }
     }
@@ -403,8 +603,8 @@
     @Override
     public void setPlayerIdleTimeout(int playerIdleTimeout) {
         super.setPlayerIdleTimeout(playerIdleTimeout);
-        this.settings.update((serverPropertiesHandler) -> {
-            return serverPropertiesHandler.playerIdleTimeout.update(this.registryAccess(), playerIdleTimeout);
+        this.settings.update((dedicatedserverproperties) -> {
+            return (DedicatedServerProperties) dedicatedserverproperties.playerIdleTimeout.update(this.registryAccess(), playerIdleTimeout);
         });
     }
 
@@ -434,68 +634,71 @@
     }
 
     protected boolean convertOldUsers() {
-        boolean bl = false;
+        boolean flag = false;
 
-        for(int i = 0; !bl && i <= 2; ++i) {
+        int i;
+
+        for (i = 0; !flag && i <= 2; ++i) {
             if (i > 0) {
-                LOGGER.warn("Encountered a problem while converting the user banlist, retrying in a few seconds");
+                DedicatedServer.LOGGER.warn("Encountered a problem while converting the user banlist, retrying in a few seconds");
                 this.waitForRetry();
             }
 
-            bl = OldUsersConverter.convertUserBanlist(this);
+            flag = OldUsersConverter.convertUserBanlist(this);
         }
 
-        boolean bl2 = false;
+        boolean flag1 = false;
 
-        for(int var7 = 0; !bl2 && var7 <= 2; ++var7) {
-            if (var7 > 0) {
-                LOGGER.warn("Encountered a problem while converting the ip banlist, retrying in a few seconds");
+        for (i = 0; !flag1 && i <= 2; ++i) {
+            if (i > 0) {
+                DedicatedServer.LOGGER.warn("Encountered a problem while converting the ip banlist, retrying in a few seconds");
                 this.waitForRetry();
             }
 
-            bl2 = OldUsersConverter.convertIpBanlist(this);
+            flag1 = OldUsersConverter.convertIpBanlist(this);
         }
 
-        boolean bl3 = false;
+        boolean flag2 = false;
 
-        for(int var8 = 0; !bl3 && var8 <= 2; ++var8) {
-            if (var8 > 0) {
-                LOGGER.warn("Encountered a problem while converting the op list, retrying in a few seconds");
+        for (i = 0; !flag2 && i <= 2; ++i) {
+            if (i > 0) {
+                DedicatedServer.LOGGER.warn("Encountered a problem while converting the op list, retrying in a few seconds");
                 this.waitForRetry();
             }
 
-            bl3 = OldUsersConverter.convertOpsList(this);
+            flag2 = OldUsersConverter.convertOpsList(this);
         }
 
-        boolean bl4 = false;
+        boolean flag3 = false;
 
-        for(int var9 = 0; !bl4 && var9 <= 2; ++var9) {
-            if (var9 > 0) {
-                LOGGER.warn("Encountered a problem while converting the whitelist, retrying in a few seconds");
+        for (i = 0; !flag3 && i <= 2; ++i) {
+            if (i > 0) {
+                DedicatedServer.LOGGER.warn("Encountered a problem while converting the whitelist, retrying in a few seconds");
                 this.waitForRetry();
             }
 
-            bl4 = OldUsersConverter.convertWhiteList(this);
+            flag3 = OldUsersConverter.convertWhiteList(this);
         }
 
-        boolean bl5 = false;
+        boolean flag4 = false;
 
-        for(int var10 = 0; !bl5 && var10 <= 2; ++var10) {
-            if (var10 > 0) {
-                LOGGER.warn("Encountered a problem while converting the player save files, retrying in a few seconds");
+        for (i = 0; !flag4 && i <= 2; ++i) {
+            if (i > 0) {
+                DedicatedServer.LOGGER.warn("Encountered a problem while converting the player save files, retrying in a few seconds");
                 this.waitForRetry();
             }
 
-            bl5 = OldUsersConverter.convertPlayers(this);
+            flag4 = OldUsersConverter.convertPlayers(this);
         }
 
-        return bl || bl2 || bl3 || bl4 || bl5;
+        return flag || flag1 || flag2 || flag3 || flag4;
     }
 
     private void waitForRetry() {
         try {
             Thread.sleep(5000L);
-        } catch (InterruptedException var2) {
+        } catch (InterruptedException interruptedexception) {
+            ;
         }
     }
 
@@ -510,28 +713,89 @@
 
     @Override
     public String getPluginNames() {
-        return "";
+        // CraftBukkit start - Whole method
+        StringBuilder result = new StringBuilder();
+        org.bukkit.plugin.Plugin[] plugins = server.getPluginManager().getPlugins();
+
+        result.append(server.getName());
+        result.append(" on Bukkit ");
+        result.append(server.getBukkitVersion());
+
+        if (plugins.length > 0 && server.getQueryPlugins()) {
+            result.append(": ");
+
+            for (int i = 0; i < plugins.length; i++) {
+                if (i > 0) {
+                    result.append("; ");
+                }
+
+                result.append(plugins[i].getDescription().getName());
+                result.append(" ");
+                result.append(plugins[i].getDescription().getVersion().replaceAll(";", ","));
+            }
+        }
+
+        return result.toString();
+        // CraftBukkit end
     }
 
     @Override
     public String runCommand(String command) {
+        Waitable[] waitableArray = new Waitable[1];
         this.rconConsoleSource.prepareForCommand();
         this.executeBlocking(() -> {
-            this.getCommands().performPrefixedCommand(this.rconConsoleSource.createCommandSourceStack(), command);
+            // CraftBukkit start - fire RemoteServerCommandEvent
+            RemoteServerCommandEvent event = new RemoteServerCommandEvent(remoteConsole, command);
+            server.getPluginManager().callEvent(event);
+            if (event.isCancelled()) {
+                return;
+            }
+            // Paper start
+            if (command.toLowerCase().startsWith("timings") && command.toLowerCase().matches("timings (report|paste|get|merged|seperate)")) {
+                org.bukkit.command.BufferedCommandSender sender = new org.bukkit.command.BufferedCommandSender();
+                Waitable<String> waitable = new Waitable<String>() {
+                    @Override
+                    protected String evaluate() {
+                        return sender.getBuffer();
+                    }
+                };
+                waitableArray[0] = waitable;
+                co.aikar.timings.Timings.generateReport(new co.aikar.timings.TimingsReportListener(sender, waitable));
+            } else {
+            // Paper end
+            ConsoleInput serverCommand = new ConsoleInput(event.getCommand(), this.rconConsoleSource.createCommandSourceStack());
+            server.dispatchServerCommand(remoteConsole, serverCommand);
+            } // Paper
+            // CraftBukkit end
         });
+        // Paper start
+        if (waitableArray[0] != null) {
+            //noinspection unchecked
+            Waitable<String> waitable = waitableArray[0];
+            try {
+                return waitable.get();
+            } catch (java.util.concurrent.ExecutionException e) {
+                throw new RuntimeException("Exception processing rcon command " + command, e.getCause());
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt(); // Maintain interrupted state
+                throw new RuntimeException("Interrupted processing rcon command " + command, e);
+            }
+
+        }
+        // Paper end
         return this.rconConsoleSource.getCommandResponse();
     }
 
     public void storeUsingWhiteList(boolean useWhitelist) {
-        this.settings.update((serverPropertiesHandler) -> {
-            return serverPropertiesHandler.whiteList.update(this.registryAccess(), useWhitelist);
+        this.settings.update((dedicatedserverproperties) -> {
+            return (DedicatedServerProperties) dedicatedserverproperties.whiteList.update(this.registryAccess(), useWhitelist);
         });
     }
 
     @Override
     public void stopServer() {
         super.stopServer();
-        Util.shutdownExecutors();
+        //Util.shutdownExecutors(); // Paper - moved into super
         SkullBlockEntity.clear();
     }
 
@@ -570,4 +834,15 @@
     public Optional<MinecraftServer.ServerResourcePackInfo> getServerResourcePack() {
         return this.settings.getProperties().serverResourcePackInfo;
     }
+
+    // CraftBukkit start
+    public boolean isDebugging() {
+        return this.getProperties().debug;
+    }
+
+    @Override
+    public CommandSender getBukkitSender(CommandSourceStack wrapper) {
+        return console;
+    }
+    // CraftBukkit end
 }
