--- 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 } } }