diff --git a/build-data/paper.at b/build-data/paper.at index 91c5e8792f..9b69a12479 100644 --- a/build-data/paper.at +++ b/build-data/paper.at @@ -680,6 +680,7 @@ public-f net.minecraft.server.MinecraftServer storageSource public-f net.minecraft.server.ReloadableServerResources commands public-f net.minecraft.server.dedicated.DedicatedServer serverLinks public-f net.minecraft.server.dedicated.DedicatedServer settings +public-f net.minecraft.server.dedicated.DedicatedServerProperties pauseWhenEmptySeconds public-f net.minecraft.server.level.TicketType timeout public-f net.minecraft.server.players.PlayerList maxPlayers public-f net.minecraft.world.entity.LivingEntity combatTracker diff --git a/paper-server/patches/unapplied/net/minecraft/server/dedicated/DedicatedPlayerList.java.patch b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedPlayerList.java.patch similarity index 62% rename from paper-server/patches/unapplied/net/minecraft/server/dedicated/DedicatedPlayerList.java.patch rename to paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedPlayerList.java.patch index 17d45a61ee..758bc8ca8d 100644 --- a/paper-server/patches/unapplied/net/minecraft/server/dedicated/DedicatedPlayerList.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedPlayerList.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/server/dedicated/DedicatedPlayerList.java +++ b/net/minecraft/server/dedicated/DedicatedPlayerList.java -@@ -18,6 +18,11 @@ - this.setViewDistance(dedicatedServerProperties.viewDistance); - this.setSimulationDistance(dedicatedServerProperties.simulationDistance); - super.setUsingWhiteList(dedicatedServerProperties.whiteList.get()); +@@ -18,6 +_,11 @@ + this.setViewDistance(properties.viewDistance); + this.setSimulationDistance(properties.simulationDistance); + super.setUsingWhiteList(properties.whiteList.get()); + // Paper start - fix converting txt to json file; moved from constructor + } + @Override diff --git a/paper-server/patches/unapplied/net/minecraft/server/dedicated/DedicatedServer.java.patch b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch similarity index 63% rename from paper-server/patches/unapplied/net/minecraft/server/dedicated/DedicatedServer.java.patch rename to paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch index 0780484bbb..2deca3f0de 100644 --- a/paper-server/patches/unapplied/net/minecraft/server/dedicated/DedicatedServer.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch @@ -1,28 +1,6 @@ --- a/net/minecraft/server/dedicated/DedicatedServer.java +++ b/net/minecraft/server/dedicated/DedicatedServer.java -@@ -54,20 +54,31 @@ - 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 org.bukkit.craftbukkit.util.TerminalCompletionHandler; -+import org.bukkit.craftbukkit.util.TerminalConsoleWriterThread; -+import org.bukkit.event.server.ServerCommandEvent; -+import org.bukkit.event.server.RemoteServerCommandEvent; -+// CraftBukkit end -+ - public class DedicatedServer extends MinecraftServer implements ServerInterface { - +@@ -63,10 +_,10 @@ static final Logger LOGGER = LogUtils.getLogger(); private static final int CONVERSION_RETRY_DELAY_MS = 5000; private static final int CONVERSION_RETRIES = 2; @@ -31,44 +9,47 @@ @Nullable private QueryThreadGs4 queryThreadGs4; - private final RconConsoleSource rconConsoleSource; -+ // private final RemoteControlCommandListener rconConsoleSource; // CraftBukkit - remove field ++ // private final RconConsoleSource rconConsoleSource; // CraftBukkit - remove field @Nullable private RconThread rconThread; public DedicatedServerSettings settings; -@@ -81,41 +92,117 @@ +@@ -80,19 +_,12 @@ private DebugSampleSubscriptionTracker debugSampleSubscriptionTracker; public ServerLinks serverLinks; -- 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; -- this.rconConsoleSource = new RconConsoleSource(this); -- this.serverTextFilter = ServerTextFilter.createFromConfig(propertiesLoader.getProperties()); -- this.serverLinks = DedicatedServer.createServerLinks(propertiesLoader); +- public DedicatedServer( +- Thread serverThread, +- LevelStorageSource.LevelStorageAccess storageSource, +- PackRepository packRepository, +- WorldStem worldStem, +- DedicatedServerSettings settings, +- DataFixer fixerUpper, +- Services services, +- ChunkProgressListenerFactory progressListenerFactory +- ) { +- super(serverThread, storageSource, packRepository, worldStem, Proxy.NO_PROXY, fixerUpper, services, progressListenerFactory); + // 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) { ++ public DedicatedServer(joptsimple.OptionSet options, WorldLoader.DataLoadContext worldLoader, Thread thread, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PackRepository resourcepackrepository, WorldStem worldstem, DedicatedServerSettings settings, 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 RemoteControlCommandListener(this); // CraftBukkit - remove field -+ this.serverTextFilter = ServerTextFilter.createFromConfig(dedicatedserversettings.getProperties()); -+ this.serverLinks = DedicatedServer.createServerLinks(dedicatedserversettings); + this.settings = settings; +- this.rconConsoleSource = new RconConsoleSource(this); ++ //this.rconConsoleSource = new RconConsoleSource(this); // CraftBukkit - remove field + this.serverTextFilter = ServerTextFilter.createFromConfig(settings.getProperties()); + this.serverLinks = createServerLinks(settings); } - - @Override - public boolean initServer() throws IOException { +@@ -102,26 +_,95 @@ Thread thread = new Thread("Server console handler") { + @Override public void run() { -- BufferedReader bufferedreader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8)); +- BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8)); +- + // CraftBukkit start -+ if (!org.bukkit.craftbukkit.Main.useConsole) { -+ return; -+ } ++ if (!org.bukkit.craftbukkit.Main.useConsole) return; + // Paper start - Use TerminalConsoleAppender + new com.destroystokyo.paper.console.PaperConsole(DedicatedServer.this).start(); + /* + jline.console.ConsoleReader bufferedreader = DedicatedServer.this.reader; - + // MC-33041, SPIGOT-5538: if System.in is not valid due to javaw, then return + try { + System.in.available(); @@ -76,22 +57,20 @@ + return; + } + // CraftBukkit end -+ - String s; - + String string1; try { -- while (!DedicatedServer.this.isStopped() && DedicatedServer.this.isRunning() && (s = bufferedreader.readLine()) != null) { -- DedicatedServer.this.handleConsoleInput(s, DedicatedServer.this.createCommandSourceStack()); +- while (!DedicatedServer.this.isStopped() && DedicatedServer.this.isRunning() && (string1 = bufferedReader.readLine()) != null) { +- DedicatedServer.this.handleConsoleInput(string1, DedicatedServer.this.createCommandSourceStack()); + // CraftBukkit start - JLine disabling compatibility + while (!DedicatedServer.this.isStopped() && DedicatedServer.this.isRunning()) { + if (org.bukkit.craftbukkit.Main.useJline) { -+ s = bufferedreader.readLine(">", null); ++ string1 = bufferedreader.readLine(">", null); + } else { -+ s = bufferedreader.readLine(); ++ string1 = bufferedreader.readLine(); + } + + // SPIGOT-5220: Throttle if EOF (ctrl^d) or stdin is /dev/null -+ if (s == null) { ++ if (string1 == null) { + try { + Thread.sleep(50L); + } catch (InterruptedException ex) { @@ -99,20 +78,18 @@ + } + continue; + } -+ if (s.trim().length() > 0) { // Trim to filter lines which are just spaces ++ if (string1.trim().length() > 0) { // Trim to filter lines which are just spaces + DedicatedServer.this.issueCommand(s, DedicatedServer.this.getServerCommandListener()); + } + // CraftBukkit end } - } catch (IOException ioexception) { - DedicatedServer.LOGGER.error("Exception handling console input", ioexception); + } catch (IOException var4) { + DedicatedServer.LOGGER.error("Exception handling console input", (Throwable)var4); } - + */ + // Paper end } }; - + // CraftBukkit start - TODO: handle command-line logging arguments + java.util.logging.Logger global = java.util.logging.Logger.getLogger(""); + global.setUseParentHandlers(false); @@ -122,7 +99,7 @@ + global.addHandler(new org.bukkit.craftbukkit.util.ForwardLogHandler()); + + // Paper start - Not needed with TerminalConsoleAppender -+ final org.apache.logging.log4j.Logger logger = LogManager.getRootLogger(); ++ final org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.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()) { @@ -137,17 +114,16 @@ + */ + // Paper end - Not needed with TerminalConsoleAppender + -+ System.setOut(IoBuilder.forLogger(logger).setLevel(Level.INFO).buildPrintStream()); -+ System.setErr(IoBuilder.forLogger(logger).setLevel(Level.WARN).buildPrintStream()); ++ System.setOut(org.apache.logging.log4j.io.IoBuilder.forLogger(logger).setLevel(org.apache.logging.log4j.Level.INFO).buildPrintStream()); ++ System.setErr(org.apache.logging.log4j.io.IoBuilder.forLogger(logger).setLevel(org.apache.logging.log4j.Level.WARN).buildPrintStream()); + // CraftBukkit end -+ thread.setDaemon(true); - thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(DedicatedServer.LOGGER)); + thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER)); - thread.start(); + // thread.start(); // Paper - Enhance console tab completions for brigadier commands; moved down - DedicatedServer.LOGGER.info("Starting minecraft server version {}", SharedConstants.getCurrentVersion().getName()); + LOGGER.info("Starting minecraft server version {}", SharedConstants.getCurrentVersion().getName()); if (Runtime.getRuntime().maxMemory() / 1024L / 1024L < 512L) { - DedicatedServer.LOGGER.warn("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\""); + LOGGER.warn("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\""); } + // Paper start - detect running as root @@ -160,13 +136,13 @@ + } + // Paper end - detect running as root + - DedicatedServer.LOGGER.info("Loading properties"); - DedicatedServerProperties dedicatedserverproperties = this.settings.getProperties(); - -@@ -126,14 +213,51 @@ - this.setPreventProxyConnections(dedicatedserverproperties.preventProxyConnections); - this.setLocalIp(dedicatedserverproperties.serverIp); + LOGGER.info("Loading properties"); + DedicatedServerProperties properties = this.settings.getProperties(); + if (this.isSingleplayer()) { +@@ -132,13 +_,51 @@ + this.setLocalIp(properties.serverIp); } + + // Spigot start + this.setPlayerList(new DedicatedPlayerList(this, this.registries(), this.playerDataStorage)); + org.spigotmc.SpigotConfig.init((java.io.File) this.options.valueOf("spigot-settings")); @@ -190,15 +166,15 @@ + this.server.spark.registerCommandBeforePlugins(this.server); // Paper - spark + com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics + com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now - - 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); -+ // this.worldData.setGameType(dedicatedserverproperties.gamemode); // CraftBukkit - moved to world loading - DedicatedServer.LOGGER.info("Default game type: {}", dedicatedserverproperties.gamemode); ++ + this.setPvpAllowed(properties.pvp); + this.setFlightAllowed(properties.allowFlight); + this.setMotd(properties.motd); + super.setPlayerIdleTimeout(properties.playerIdleTimeout.get()); + this.setEnforceWhitelist(properties.enforceWhitelist); +- this.worldData.setGameType(properties.gamemode); ++ // this.worldData.setGameType(properties.gamemode); // CraftBukkit - moved to world loading + LOGGER.info("Default game type: {}", properties.gamemode); + // Paper start - Unix domain socket support + java.net.SocketAddress bindAddress; + if (this.getLocalIp().startsWith("unix:")) { @@ -213,28 +189,28 @@ + } + bindAddress = new io.netty.channel.unix.DomainSocketAddress(this.getLocalIp().substring("unix:".length())); + } else { - InetAddress inetaddress = null; - + InetAddress inetAddress = null; if (!this.getLocalIp().isEmpty()) { -@@ -143,34 +267,55 @@ + inetAddress = InetAddress.getByName(this.getLocalIp()); +@@ -147,36 +_,62 @@ if (this.getPort() < 0) { - this.setPort(dedicatedserverproperties.serverPort); + this.setPort(properties.serverPort); } -+ bindAddress = new java.net.InetSocketAddress(inetaddress, this.getPort()); ++ bindAddress = new java.net.InetSocketAddress(inetAddress, this.getPort()); + } + // Paper end - Unix domain socket support this.initializeKeyPair(); - DedicatedServer.LOGGER.info("Starting Minecraft server on {}:{}", this.getLocalIp().isEmpty() ? "*" : this.getLocalIp(), this.getPort()); + LOGGER.info("Starting Minecraft server on {}:{}", this.getLocalIp().isEmpty() ? "*" : this.getLocalIp(), this.getPort()); try { -- this.getConnection().startTcpServerListener(inetaddress, this.getPort()); +- this.getConnection().startTcpServerListener(inetAddress, this.getPort()); + 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?"); -+ if (true) throw new IllegalStateException("Failed to bind to port", ioexception); // Paper - Propagate failed to bind to port error + } catch (IOException var10) { + LOGGER.warn("**** FAILED TO BIND TO PORT!"); + LOGGER.warn("The exception was: {}", var10.toString()); + LOGGER.warn("Perhaps a server is already running on that port?"); ++ if (true) throw new IllegalStateException("Failed to bind to port", var10); // Paper - Propagate failed to bind to port error return false; } @@ -250,25 +226,31 @@ + String proxyLink = (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.velocity.enabled) ? "https://docs.papermc.io/velocity/security" : "http://www.spigotmc.org/wiki/firewall-guide/"; + // Paper end - Add Velocity IP Forwarding Support if (!this.usesAuthentication()) { - DedicatedServer.LOGGER.warn("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!"); - DedicatedServer.LOGGER.warn("The server will make no attempt to authenticate usernames. Beware."); -- 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."); + 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." +- ); + // Spigot start + // Paper start - Add Velocity IP Forwarding Support + if (usingProxy) { + DedicatedServer.LOGGER.warn("Whilst this makes it possible to use " + proxyFlavor + ", 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 " + proxyLink + " for further information."); -+ // Paper end - Add Velocity IP Forwarding Support ++ // Paper end - Add Velocity IP Forwarding Support + } 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."); + LOGGER.warn("To change this, set \"online-mode\" to \"true\" in the server.properties file."); } -- if (this.convertOldUsers()) { -- this.getProfileCache().save(); -- } ++ // CraftBukkit start ++ /* + if (this.convertOldUsers()) { + this.getProfileCache().save(); + } ++ */ ++ // CraftBukkit end if (!OldUsersConverter.serverReadyAfterUserconversion(this)) { return false; @@ -276,34 +258,33 @@ - this.setPlayerList(new DedicatedPlayerList(this, this.registries(), this.playerDataStorage)); + // this.setPlayerList(new DedicatedPlayerList(this, this.registries(), this.playerDataStorage)); // CraftBukkit - moved up this.debugSampleSubscriptionTracker = new DebugSampleSubscriptionTracker(this.getPlayerList()); - this.tickTimeLogger = new RemoteSampleLogger(TpsDebugDimensions.values().length, this.debugSampleSubscriptionTracker, RemoteDebugSampleType.TICK_TIME); - long i = Util.getNanos(); -@@ -178,13 +323,13 @@ + this.tickTimeLogger = new RemoteSampleLogger( + TpsDebugDimensions.values().length, this.debugSampleSubscriptionTracker, RemoteDebugSampleType.TICK_TIME +@@ -185,12 +_,12 @@ SkullBlockEntity.setup(this.services, this); GameProfileCache.setUsesAuthentication(this.usesAuthentication()); - DedicatedServer.LOGGER.info("Preparing level \"{}\"", this.getLevelIdName()); + LOGGER.info("Preparing level \"{}\"", this.getLevelIdName()); - this.loadLevel(); + this.loadLevel(this.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); - if (dedicatedserverproperties.announcePlayerAchievements != null) { -- ((GameRules.BooleanValue) this.getGameRules().getRule(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)).set(dedicatedserverproperties.announcePlayerAchievements, this); -+ ((GameRules.BooleanValue) this.getGameRules().getRule(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)).set(dedicatedserverproperties.announcePlayerAchievements, this.overworld()); // CraftBukkit - per-world + long l = Util.getNanos() - nanos; + String string = String.format(Locale.ROOT, "%.3fs", l / 1.0E9); + LOGGER.info("Done ({})! For help, type \"help\"", string); + if (properties.announcePlayerAchievements != null) { +- this.getGameRules().getRule(GameRules.RULE_ANNOUNCE_ADVANCEMENTS).set(properties.announcePlayerAchievements, this); ++ this.getGameRules().getRule(GameRules.RULE_ANNOUNCE_ADVANCEMENTS).set(properties.announcePlayerAchievements, this.overworld()); // CraftBukkit - per-world } - if (dedicatedserverproperties.enableQuery) { -@@ -197,7 +342,7 @@ + if (properties.enableQuery) { +@@ -203,7 +_,7 @@ this.rconThread = RconThread.create(this); } - if (this.getMaxTickLength() > 0L) { -+ if (false && this.getMaxTickLength() > 0L) { // Spigot - disable ++ if (false && this.getMaxTickLength() > 0L) { // Spigot - disable Thread thread1 = new Thread(new ServerWatchdog(this)); - - thread1.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandlerWithName(DedicatedServer.LOGGER)); -@@ -215,6 +360,12 @@ + thread1.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandlerWithName(LOGGER)); + thread1.setName("Server Watchdog"); +@@ -220,6 +_,12 @@ } } @@ -316,7 +297,7 @@ @Override public boolean isSpawningMonsters() { return this.settings.getProperties().spawnMonsters && super.isSpawningMonsters(); -@@ -227,7 +378,7 @@ +@@ -232,7 +_,7 @@ @Override public void forceDifficulty() { @@ -325,7 +306,7 @@ } @Override -@@ -286,13 +437,14 @@ +@@ -271,12 +_,14 @@ } if (this.rconThread != null) { @@ -337,37 +318,29 @@ - this.queryThreadGs4.stop(); + // this.remoteStatusListener.stop(); // Paper - don't wait for remote connections } - ++ + System.exit(0); // CraftBukkit } @Override -@@ -302,19 +454,29 @@ +@@ -291,13 +_,23 @@ } - @Override -- public boolean isLevelEnabled(Level world) { -- return world.dimension() == Level.NETHER ? this.getProperties().allowNether : true; -+ public boolean isLevelEnabled(net.minecraft.world.level.Level world) { -+ return world.dimension() == net.minecraft.world.level.Level.NETHER ? this.getProperties().allowNether : true; - } - - public void handleConsoleInput(String command, CommandSourceStack commandSource) { -- this.consoleInput.add(new ConsoleInput(command, commandSource)); -+ this.serverCommandQueue.add(new ConsoleInput(command, commandSource)); // Paper - Perf: use proper queue + public void handleConsoleInput(String msg, CommandSourceStack source) { +- this.consoleInput.add(new ConsoleInput(msg, source)); ++ this.serverCommandQueue.add(new ConsoleInput(msg, source)); // Paper - Perf: use proper queue } public void handleConsoleInputs() { - while (!this.consoleInput.isEmpty()) { -- ConsoleInput servercommand = (ConsoleInput) this.consoleInput.remove(0); +- ConsoleInput consoleInput = this.consoleInput.remove(0); +- this.getCommands().performPrefixedCommand(consoleInput.source, consoleInput.msg); + // Paper start - Perf: use proper queue + ConsoleInput servercommand; + while ((servercommand = this.serverCommandQueue.poll()) != null) { + // Paper end - Perf: use proper queue - -- this.getCommands().performPrefixedCommand(servercommand.source, servercommand.msg); + // CraftBukkit start - ServerCommand for preprocessing -+ ServerCommandEvent event = new ServerCommandEvent(this.console, servercommand.msg); ++ org.bukkit.event.server.ServerCommandEvent event = new org.bukkit.event.server.ServerCommandEvent(this.console, servercommand.msg); + this.server.getPluginManager().callEvent(event); + if (event.isCancelled()) continue; + servercommand = new ConsoleInput(event.getCommand(), servercommand.source); @@ -376,31 +349,22 @@ + this.server.dispatchServerCommand(this.console, servercommand); + // CraftBukkit end } - } -@@ -383,7 +545,7 @@ +@@ -430,7 +_,11 @@ @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; -@@ -453,7 +615,11 @@ public boolean enforceSecureProfile() { - DedicatedServerProperties dedicatedserverproperties = this.getProperties(); - -- return dedicatedserverproperties.enforceSecureProfile && dedicatedserverproperties.onlineMode && this.services.canValidateProfileKeys(); + DedicatedServerProperties properties = this.getProperties(); +- return properties.enforceSecureProfile && properties.onlineMode && this.services.canValidateProfileKeys(); + // Paper start - Add setting for proxy online mode status -+ return dedicatedserverproperties.enforceSecureProfile ++ return properties.enforceSecureProfile + && io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() + && this.services.canValidateProfileKeys(); + // Paper end - Add setting for proxy online mode status } @Override -@@ -541,16 +707,52 @@ +@@ -515,14 +_,52 @@ @Override public String getPluginNames() { @@ -434,30 +398,30 @@ @Override public String runCommand(String command) { - this.rconConsoleSource.prepareForCommand(); +- this.executeBlocking(() -> this.getCommands().performPrefixedCommand(this.rconConsoleSource.createCommandSourceStack(), command)); +- return this.rconConsoleSource.getCommandResponse(); + // CraftBukkit start - fire RemoteServerCommandEvent + throw new UnsupportedOperationException("Not supported - remote source required."); + } + + public String runCommand(RconConsoleSource rconConsoleSource, String s) { + rconConsoleSource.prepareForCommand(); - this.executeBlocking(() -> { -- this.getCommands().performPrefixedCommand(this.rconConsoleSource.createCommandSourceStack(), command); ++ this.executeBlocking(() -> { + CommandSourceStack wrapper = rconConsoleSource.createCommandSourceStack(); -+ RemoteServerCommandEvent event = new RemoteServerCommandEvent(rconConsoleSource.getBukkitSender(wrapper), s); ++ org.bukkit.event.server.RemoteServerCommandEvent event = new org.bukkit.event.server.RemoteServerCommandEvent(rconConsoleSource.getBukkitSender(wrapper), s); + this.server.getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + ConsoleInput serverCommand = new ConsoleInput(event.getCommand(), wrapper); + this.server.dispatchServerCommand(event.getSender(), serverCommand); - }); -- return this.rconConsoleSource.getCommandResponse(); ++ }); + return rconConsoleSource.getCommandResponse(); + // CraftBukkit end } - public void storeUsingWhiteList(boolean useWhitelist) { -@@ -660,4 +862,15 @@ + public void storeUsingWhiteList(boolean isStoreUsingWhiteList) { +@@ -626,4 +_,15 @@ } } } diff --git a/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServerProperties.java.patch b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServerProperties.java.patch new file mode 100644 index 0000000000..9e44f8227b --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServerProperties.java.patch @@ -0,0 +1,65 @@ +--- a/net/minecraft/server/dedicated/DedicatedServerProperties.java ++++ b/net/minecraft/server/dedicated/DedicatedServerProperties.java +@@ -45,6 +_,7 @@ + static final Logger LOGGER = LogUtils.getLogger(); + private static final Pattern SHA1 = Pattern.compile("^[a-fA-F0-9]{40}$"); + private static final Splitter COMMA_SPLITTER = Splitter.on(',').trimResults(); ++ public final boolean debug = this.get("debug", false); // CraftBukkit + public final boolean onlineMode = this.get("online-mode", true); + public final boolean preventProxyConnections = this.get("prevent-proxy-connections", false); + public final String serverIp = this.get("server-ip", ""); +@@ -85,7 +_,7 @@ + public final boolean broadcastRconToOps = this.get("broadcast-rcon-to-ops", true); + public final boolean broadcastConsoleToOps = this.get("broadcast-console-to-ops", true); + public final int maxWorldSize = this.get("max-world-size", property -> Mth.clamp(property, 1, 29999984), 29999984); +- public final boolean syncChunkWrites = this.get("sync-chunk-writes", true); ++ public final boolean syncChunkWrites = this.get("sync-chunk-writes", true) && Boolean.getBoolean("Paper.enable-sync-chunk-writes"); // Paper - Hide sync chunk writes behind flag + public final String regionFileComression = this.get("region-file-compression", "deflate"); + public final boolean enableJmxMonitoring = this.get("enable-jmx-monitoring", false); + public final boolean enableStatus = this.get("enable-status", true); +@@ -99,13 +_,16 @@ + public final Settings.MutableValue whiteList = this.getMutable("white-list", false); + public final boolean enforceSecureProfile = this.get("enforce-secure-profile", true); + public final boolean logIPs = this.get("log-ips", true); +- public int pauseWhenEmptySeconds = this.get("pause-when-empty-seconds", 60); ++ public int pauseWhenEmptySeconds = this.get("pause-when-empty-seconds", -1); // Paper - disable tick sleeping by default + private final DedicatedServerProperties.WorldDimensionData worldDimensionData; + public final WorldOptions worldOptions; + public boolean acceptsTransfers = this.get("accepts-transfers", false); ++ public final String rconIp; // Paper - Configurable rcon ip + +- public DedicatedServerProperties(Properties properties) { +- super(properties); ++ // CraftBukkit start ++ public DedicatedServerProperties(Properties properties, joptsimple.OptionSet optionset) { ++ super(properties, optionset); ++ // CraftBukkit end + String string = this.get("level-seed", ""); + boolean flag = this.get("generate-structures", true); + long l = WorldOptions.parseSeed(string).orElse(WorldOptions.randomSeed()); +@@ -126,15 +_,21 @@ + this.get("initial-enabled-packs", String.join(",", WorldDataConfiguration.DEFAULT.dataPacks().getEnabled())), + this.get("initial-disabled-packs", String.join(",", WorldDataConfiguration.DEFAULT.dataPacks().getDisabled())) + ); ++ // Paper start - Configurable rcon ip ++ final String rconIp = this.getStringRaw("rcon.ip"); ++ this.rconIp = rconIp == null ? this.serverIp : rconIp; ++ // Paper end - Configurable rcon ip + } + +- public static DedicatedServerProperties fromFile(Path path) { +- return new DedicatedServerProperties(loadFromFile(path)); ++ // CraftBukkit start ++ public static DedicatedServerProperties fromFile(Path path, joptsimple.OptionSet optionset) { ++ return new DedicatedServerProperties(loadFromFile(path), optionset); + } + + @Override +- protected DedicatedServerProperties reload(RegistryAccess registryAccess, Properties properties) { +- return new DedicatedServerProperties(properties); ++ public DedicatedServerProperties reload(RegistryAccess registryAccess, Properties properties, joptsimple.OptionSet options) { ++ return new DedicatedServerProperties(properties, options); ++ // CraftBukkit end + } + + @Nullable diff --git a/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServerSettings.java.patch b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServerSettings.java.patch new file mode 100644 index 0000000000..acd4a0443e --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServerSettings.java.patch @@ -0,0 +1,17 @@ +--- a/net/minecraft/server/dedicated/DedicatedServerSettings.java ++++ b/net/minecraft/server/dedicated/DedicatedServerSettings.java +@@ -7,9 +_,11 @@ + private final Path source; + private DedicatedServerProperties properties; + +- public DedicatedServerSettings(Path source) { +- this.source = source; +- this.properties = DedicatedServerProperties.fromFile(source); ++ // CraftBukkit start ++ public DedicatedServerSettings(joptsimple.OptionSet optionset) { ++ this.source = ((java.io.File) optionset.valueOf("config")).toPath(); ++ this.properties = DedicatedServerProperties.fromFile(this.source, optionset); ++ // CraftBukkit end + } + + public DedicatedServerProperties getProperties() { diff --git a/paper-server/patches/sources/net/minecraft/server/dedicated/Settings.java.patch b/paper-server/patches/sources/net/minecraft/server/dedicated/Settings.java.patch new file mode 100644 index 0000000000..33fd250afb --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/server/dedicated/Settings.java.patch @@ -0,0 +1,128 @@ +--- a/net/minecraft/server/dedicated/Settings.java ++++ b/net/minecraft/server/dedicated/Settings.java +@@ -26,12 +_,26 @@ + public abstract class Settings> { + private static final Logger LOGGER = LogUtils.getLogger(); + public final Properties properties; ++ private static final boolean skipComments = Boolean.getBoolean("Paper.skipServerPropertiesComments"); // Paper - allow skipping server.properties comments ++ // CraftBukkit start ++ private joptsimple.OptionSet options = null; + +- public Settings(Properties properties) { ++ public Settings(Properties properties, final joptsimple.OptionSet options) { + this.properties = properties; ++ this.options = options; ++ } ++ ++ private String getOverride(String name, String value) { ++ if ((this.options != null) && (this.options.has(name))) { ++ return String.valueOf(this.options.valueOf(name)); ++ } ++ ++ return value; ++ // CraftBukkit end + } + + public static Properties loadFromFile(Path path) { ++ if (!path.toFile().exists()) return new Properties(); // CraftBukkit - SPIGOT-7465, MC-264979: Don't load if file doesn't exist + try { + try { + Properties var13; +@@ -65,7 +_,53 @@ + } + + public void store(Path path) { +- try (Writer bufferedWriter = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) { ++ try /*(Writer bufferedWriter = Files.newBufferedWriter(path, StandardCharsets.UTF_8))*/ { // Paper ++ // CraftBukkit start - Don't attempt writing to file if it's read only ++ if (path.toFile().exists() && !path.toFile().canWrite()) { ++ Settings.LOGGER.warn("Can not write to file {}, skipping.", path); // Paper - log message file is read-only ++ return; ++ } ++ // CraftBukkit end ++ // Paper start - allow skipping server.properties comments ++ java.io.OutputStream outputstream = Files.newOutputStream(path); ++ java.io.BufferedOutputStream bufferedOutputStream = !skipComments ? new java.io.BufferedOutputStream(outputstream) : new java.io.BufferedOutputStream(outputstream) { ++ private boolean isRightAfterNewline = true; // If last written char was newline ++ private boolean isComment = false; // Are we writing comment currently? ++ ++ @Override ++ public void write(@org.jetbrains.annotations.NotNull byte[] b) throws IOException { ++ this.write(b, 0, b.length); ++ } ++ ++ @Override ++ public void write(@org.jetbrains.annotations.NotNull byte[] bbuf, int off, int len) throws IOException { ++ int latest_offset = off; // The latest offset, updated when comment ends ++ for (int index = off; index < off + len; ++index ) { ++ byte c = bbuf[index]; ++ boolean isNewline = (c == '\n' || c == '\r'); ++ if (isNewline && this.isComment) { ++ // Comment has ended ++ this.isComment = false; ++ latest_offset = index+1; ++ } ++ if (c == '#' && this.isRightAfterNewline) { ++ this.isComment = true; ++ if (index != latest_offset) { ++ // We got some non-comment data earlier ++ super.write(bbuf, latest_offset, index-latest_offset); ++ } ++ } ++ this.isRightAfterNewline = isNewline; // Store for next iteration ++ ++ } ++ if (latest_offset < off+len && !this.isComment) { ++ // We have some unwritten data, that isn't part of a comment ++ super.write(bbuf, latest_offset, (off + len) - latest_offset); ++ } ++ } ++ }; ++ java.io.BufferedWriter bufferedWriter = new java.io.BufferedWriter(new java.io.OutputStreamWriter(bufferedOutputStream, java.nio.charset.StandardCharsets.UTF_8.newEncoder())); ++ // Paper end - allow skipping server.properties comments + this.properties.store(bufferedWriter, "Minecraft server properties"); + } catch (IOException var7) { + LOGGER.error("Failed to store properties to file: {}", path); +@@ -94,7 +_,7 @@ + + @Nullable + public String getStringRaw(String key) { +- return (String)this.properties.get(key); ++ return (String)this.getOverride(key, this.properties.getProperty(key)); // CraftBukkit + } + + @Nullable +@@ -109,6 +_,15 @@ + } + + protected V get(String key, Function serializer, Function deserializer, V defaultValue) { ++ // CraftBukkit start ++ try { ++ return this.get0(key, serializer, deserializer, defaultValue); ++ } catch (Exception ex) { ++ throw new RuntimeException("Could not load invalidly configured property '" + key + "'", ex); ++ } ++ } ++ private V get0(String key, Function serializer, Function deserializer, V defaultValue) { ++ // CraftBukkit end + String stringRaw = this.getStringRaw(key); + V object = MoreObjects.firstNonNull(stringRaw != null ? serializer.apply(stringRaw) : null, defaultValue); + this.properties.put(key, deserializer.apply(object)); +@@ -181,7 +_,7 @@ + return map; + } + +- protected abstract T reload(RegistryAccess registryAccess, Properties properties); ++ protected abstract T reload(RegistryAccess registryAccess, Properties properties, joptsimple.OptionSet optionset); // CraftBukkit + + public class MutableValue implements Supplier { + private final String key; +@@ -202,7 +_,7 @@ + public T update(RegistryAccess registryAccess, V newValue) { + Properties map = Settings.this.cloneProperties(); + map.put(this.key, this.serializer.apply(newValue)); +- return Settings.this.reload(registryAccess, map); ++ return Settings.this.reload(registryAccess, properties, Settings.this.options); // CraftBukkit + } + } + } diff --git a/paper-server/patches/unapplied/net/minecraft/server/dedicated/DedicatedServerProperties.java.patch b/paper-server/patches/unapplied/net/minecraft/server/dedicated/DedicatedServerProperties.java.patch deleted file mode 100644 index 2020814765..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/server/dedicated/DedicatedServerProperties.java.patch +++ /dev/null @@ -1,106 +0,0 @@ ---- a/net/minecraft/server/dedicated/DedicatedServerProperties.java -+++ b/net/minecraft/server/dedicated/DedicatedServerProperties.java -@@ -43,11 +43,16 @@ - import net.minecraft.world.level.levelgen.presets.WorldPresets; - import org.slf4j.Logger; - -+// CraftBukkit start -+import joptsimple.OptionSet; -+// CraftBukkit end -+ - public class DedicatedServerProperties extends Settings { - - static final Logger LOGGER = LogUtils.getLogger(); - private static final Pattern SHA1 = Pattern.compile("^[a-fA-F0-9]{40}$"); - private static final Splitter COMMA_SPLITTER = Splitter.on(',').trimResults(); -+ public final boolean debug = this.get("debug", false); // CraftBukkit - public final boolean onlineMode = this.get("online-mode", true); - public final boolean preventProxyConnections = this.get("prevent-proxy-connections", false); - public final String serverIp = this.get("server-ip", ""); -@@ -100,13 +105,17 @@ - public final Settings.MutableValue whiteList; - public final boolean enforceSecureProfile; - public final boolean logIPs; -- public final int pauseWhenEmptySeconds; -+ public int pauseWhenEmptySeconds; - private final DedicatedServerProperties.WorldDimensionData worldDimensionData; - public final WorldOptions worldOptions; - public boolean acceptsTransfers; - -- public DedicatedServerProperties(Properties properties) { -- super(properties); -+ public final String rconIp; // Paper - Configurable rcon ip -+ -+ // CraftBukkit start -+ public DedicatedServerProperties(Properties properties, OptionSet optionset) { -+ super(properties, optionset); -+ // CraftBukkit end - this.difficulty = (Difficulty) this.get("difficulty", dispatchNumberOrString(Difficulty::byId, Difficulty::byName), Difficulty::getKey, Difficulty.EASY); - this.gamemode = (GameType) this.get("gamemode", dispatchNumberOrString(GameType::byId, GameType::byName), GameType::getName, GameType.SURVIVAL); - this.levelName = this.get("level-name", "world"); -@@ -137,7 +146,7 @@ - this.maxWorldSize = this.get("max-world-size", (integer) -> { - return Mth.clamp(integer, 1, 29999984); - }, 29999984); -- this.syncChunkWrites = this.get("sync-chunk-writes", true); -+ this.syncChunkWrites = this.get("sync-chunk-writes", true) && Boolean.getBoolean("Paper.enable-sync-chunk-writes"); // Paper - Hide sync chunk writes behind flag - this.regionFileComression = this.get("region-file-compression", "deflate"); - this.enableJmxMonitoring = this.get("enable-jmx-monitoring", false); - this.enableStatus = this.get("enable-status", true); -@@ -151,7 +160,7 @@ - this.whiteList = this.getMutable("white-list", false); - this.enforceSecureProfile = this.get("enforce-secure-profile", true); - this.logIPs = this.get("log-ips", true); -- this.pauseWhenEmptySeconds = this.get("pause-when-empty-seconds", 60); -+ this.pauseWhenEmptySeconds = this.get("pause-when-empty-seconds", -1); // Paper - disable tick sleeping by default - this.acceptsTransfers = this.get("accepts-transfers", false); - String s = this.get("level-seed", ""); - boolean flag = this.get("generate-structures", true); -@@ -165,15 +174,21 @@ - }, WorldPresets.NORMAL.location().toString())); - this.serverResourcePackInfo = DedicatedServerProperties.getServerPackInfo(this.get("resource-pack-id", ""), this.get("resource-pack", ""), this.get("resource-pack-sha1", ""), this.getLegacyString("resource-pack-hash"), this.get("require-resource-pack", false), this.get("resource-pack-prompt", "")); - this.initialDataPackConfiguration = DedicatedServerProperties.getDatapackConfig(this.get("initial-enabled-packs", String.join(",", WorldDataConfiguration.DEFAULT.dataPacks().getEnabled())), this.get("initial-disabled-packs", String.join(",", WorldDataConfiguration.DEFAULT.dataPacks().getDisabled()))); -+ // Paper start - Configurable rcon ip -+ final String rconIp = this.getStringRaw("rcon.ip"); -+ this.rconIp = rconIp == null ? this.serverIp : rconIp; -+ // Paper end - Configurable rcon ip - } - -- public static DedicatedServerProperties fromFile(Path path) { -- return new DedicatedServerProperties(loadFromFile(path)); -+ // CraftBukkit start -+ public static DedicatedServerProperties fromFile(Path path, OptionSet optionset) { -+ return new DedicatedServerProperties(loadFromFile(path), optionset); - } - - @Override -- protected DedicatedServerProperties reload(RegistryAccess registryManager, Properties properties) { -- return new DedicatedServerProperties(properties); -+ public DedicatedServerProperties reload(RegistryAccess iregistrycustom, Properties properties, OptionSet optionset) { -+ return new DedicatedServerProperties(properties, optionset); -+ // CraftBukkit end - } - - @Nullable -@@ -254,10 +269,10 @@ - }).orElseThrow(() -> { - return new IllegalStateException("Invalid datapack contents: can't find default preset"); - }); -- Optional optional = Optional.ofNullable(ResourceLocation.tryParse(this.levelType)).map((minecraftkey) -> { -+ Optional> optional = Optional.ofNullable(ResourceLocation.tryParse(this.levelType)).map((minecraftkey) -> { // CraftBukkit - decompile error - return ResourceKey.create(Registries.WORLD_PRESET, minecraftkey); - }).or(() -> { -- return Optional.ofNullable((ResourceKey) DedicatedServerProperties.WorldDimensionData.LEGACY_PRESET_NAMES.get(this.levelType)); -+ return Optional.ofNullable(DedicatedServerProperties.WorldDimensionData.LEGACY_PRESET_NAMES.get(this.levelType)); // CraftBukkit - decompile error - }); - - Objects.requireNonNull(holderlookup); -@@ -269,7 +284,7 @@ - - if (holder.is(WorldPresets.FLAT)) { - RegistryOps registryops = registries.createSerializationContext(JsonOps.INSTANCE); -- DataResult dataresult = FlatLevelGeneratorSettings.CODEC.parse(new Dynamic(registryops, this.generatorSettings())); -+ DataResult dataresult = FlatLevelGeneratorSettings.CODEC.parse(new Dynamic(registryops, this.generatorSettings())); // CraftBukkit - decompile error - Logger logger = DedicatedServerProperties.LOGGER; - - Objects.requireNonNull(logger); diff --git a/paper-server/patches/unapplied/net/minecraft/server/dedicated/DedicatedServerSettings.java.patch b/paper-server/patches/unapplied/net/minecraft/server/dedicated/DedicatedServerSettings.java.patch deleted file mode 100644 index 0aaaffd34b..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/server/dedicated/DedicatedServerSettings.java.patch +++ /dev/null @@ -1,27 +0,0 @@ ---- a/net/minecraft/server/dedicated/DedicatedServerSettings.java -+++ b/net/minecraft/server/dedicated/DedicatedServerSettings.java -@@ -3,14 +3,21 @@ - import java.nio.file.Path; - import java.util.function.UnaryOperator; - -+// CraftBukkit start -+import java.io.File; -+import joptsimple.OptionSet; -+// CraftBukkit end -+ - public class DedicatedServerSettings { - - private final Path source; - private DedicatedServerProperties properties; - -- public DedicatedServerSettings(Path path) { -- this.source = path; -- this.properties = DedicatedServerProperties.fromFile(path); -+ // CraftBukkit start -+ public DedicatedServerSettings(OptionSet optionset) { -+ this.source = ((File) optionset.valueOf("config")).toPath(); -+ this.properties = DedicatedServerProperties.fromFile(this.source, optionset); -+ // CraftBukkit end - } - - public DedicatedServerProperties getProperties() { diff --git a/paper-server/patches/unapplied/net/minecraft/server/dedicated/Settings.java.patch b/paper-server/patches/unapplied/net/minecraft/server/dedicated/Settings.java.patch deleted file mode 100644 index 300eeaa029..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/server/dedicated/Settings.java.patch +++ /dev/null @@ -1,179 +0,0 @@ ---- a/net/minecraft/server/dedicated/Settings.java -+++ b/net/minecraft/server/dedicated/Settings.java -@@ -20,20 +20,41 @@ - import java.util.function.Supplier; - import java.util.function.UnaryOperator; - import javax.annotation.Nullable; --import net.minecraft.core.RegistryAccess; - import org.slf4j.Logger; - -+import joptsimple.OptionSet; // CraftBukkit -+import net.minecraft.core.RegistryAccess; -+ - public abstract class Settings> { - - private static final Logger LOGGER = LogUtils.getLogger(); - public final Properties properties; -+ private static final boolean skipComments = Boolean.getBoolean("Paper.skipServerPropertiesComments"); // Paper - allow skipping server.properties comments -+ // CraftBukkit start -+ private OptionSet options = null; - -- public Settings(Properties properties) { -+ public Settings(Properties properties, final OptionSet options) { - this.properties = properties; -+ -+ this.options = options; - } - -+ private String getOverride(String name, String value) { -+ if ((this.options != null) && (this.options.has(name))) { -+ return String.valueOf(this.options.valueOf(name)); -+ } -+ -+ return value; -+ // CraftBukkit end -+ } -+ - public static Properties loadFromFile(Path path) { - try { -+ // CraftBukkit start - SPIGOT-7465, MC-264979: Don't load if file doesn't exist -+ if (!path.toFile().exists()) { -+ return new Properties(); -+ } -+ // CraftBukkit end - Properties properties; - Properties properties1; - -@@ -97,8 +118,53 @@ - - public void store(Path path) { - try { -- BufferedWriter bufferedwriter = Files.newBufferedWriter(path, StandardCharsets.UTF_8); -+ // CraftBukkit start - Don't attempt writing to file if it's read only -+ if (path.toFile().exists() && !path.toFile().canWrite()) { -+ Settings.LOGGER.warn("Can not write to file {}, skipping.", path); // Paper - log message file is read-only -+ return; -+ } -+ // CraftBukkit end -+ // Paper start - allow skipping server.properties comments -+ java.io.OutputStream outputstream = Files.newOutputStream(path); -+ java.io.BufferedOutputStream bufferedOutputStream = !skipComments ? new java.io.BufferedOutputStream(outputstream) : new java.io.BufferedOutputStream(outputstream) { -+ private boolean isRightAfterNewline = true; // If last written char was newline -+ private boolean isComment = false; // Are we writing comment currently? -+ -+ @Override -+ public void write(@org.jetbrains.annotations.NotNull byte[] b) throws IOException { -+ this.write(b, 0, b.length); -+ } -+ -+ @Override -+ public void write(@org.jetbrains.annotations.NotNull byte[] bbuf, int off, int len) throws IOException { -+ int latest_offset = off; // The latest offset, updated when comment ends -+ for (int index = off; index < off + len; ++index ) { -+ byte c = bbuf[index]; -+ boolean isNewline = (c == '\n' || c == '\r'); -+ if (isNewline && this.isComment) { -+ // Comment has ended -+ this.isComment = false; -+ latest_offset = index+1; -+ } -+ if (c == '#' && this.isRightAfterNewline) { -+ this.isComment = true; -+ if (index != latest_offset) { -+ // We got some non-comment data earlier -+ super.write(bbuf, latest_offset, index-latest_offset); -+ } -+ } -+ this.isRightAfterNewline = isNewline; // Store for next iteration - -+ } -+ if (latest_offset < off+len && !this.isComment) { -+ // We have some unwritten data, that isn't part of a comment -+ super.write(bbuf, latest_offset, (off + len) - latest_offset); -+ } -+ } -+ }; -+ BufferedWriter bufferedwriter = new BufferedWriter(new java.io.OutputStreamWriter(bufferedOutputStream, java.nio.charset.StandardCharsets.UTF_8.newEncoder())); -+ // Paper end - allow skipping server.properties comments -+ - try { - this.properties.store(bufferedwriter, "Minecraft server properties"); - } catch (Throwable throwable) { -@@ -125,7 +191,7 @@ - private static Function wrapNumberDeserializer(Function parser) { - return (s) -> { - try { -- return (Number) parser.apply(s); -+ return (V) parser.apply(s); // CraftBukkit - decompile error - } catch (NumberFormatException numberformatexception) { - return null; - } -@@ -144,7 +210,7 @@ - - @Nullable - public String getStringRaw(String key) { -- return (String) this.properties.get(key); -+ return (String) this.getOverride(key, this.properties.getProperty(key)); // CraftBukkit - } - - @Nullable -@@ -160,10 +226,20 @@ - } - - protected V get(String key, Function parser, Function stringifier, V fallback) { -- String s1 = this.getStringRaw(key); -- V v1 = MoreObjects.firstNonNull(s1 != null ? parser.apply(s1) : null, fallback); -+ // CraftBukkit start -+ try { -+ return this.get0(key, parser, stringifier, fallback); -+ } catch (Exception ex) { -+ throw new RuntimeException("Could not load invalidly configured property '" + key + "'", ex); -+ } -+ } - -- this.properties.put(key, stringifier.apply(v1)); -+ private V get0(String s, Function function, Function function1, V v0) { -+ // CraftBukkit end -+ String s1 = this.getStringRaw(s); -+ V v1 = MoreObjects.firstNonNull(s1 != null ? function.apply(s1) : null, v0); -+ -+ this.properties.put(s, function1.apply(v1)); - return v1; - } - -@@ -172,7 +248,7 @@ - V v1 = MoreObjects.firstNonNull(s1 != null ? parser.apply(s1) : null, fallback); - - this.properties.put(key, stringifier.apply(v1)); -- return new Settings.MutableValue<>(key, v1, stringifier); -+ return new Settings.MutableValue(key, v1, stringifier); // CraftBukkit - decompile error - } - - protected V get(String key, Function parser, UnaryOperator parsedTransformer, Function stringifier, V fallback) { -@@ -236,7 +312,7 @@ - return properties; - } - -- protected abstract T reload(RegistryAccess registryManager, Properties properties); -+ protected abstract T reload(RegistryAccess iregistrycustom, Properties properties, OptionSet optionset); // CraftBukkit - - public class MutableValue implements Supplier { - -@@ -244,7 +320,7 @@ - private final V value; - private final Function serializer; - -- MutableValue(final String s, final Object object, final Function function) { -+ MutableValue(final String s, final V object, final Function function) { // CraftBukkit - decompile error - this.key = s; - this.value = object; - this.serializer = function; -@@ -258,7 +334,7 @@ - Properties properties = Settings.this.cloneProperties(); - - properties.put(this.key, this.serializer.apply(value)); -- return Settings.this.reload(registryManager, properties); -+ return Settings.this.reload(registryManager, properties, Settings.this.options); // CraftBukkit - } - } - }