diff --git a/api/src/main/java/com/viaversion/viaversion/api/ViaAPI.java b/api/src/main/java/com/viaversion/viaversion/api/ViaAPI.java index 22b059228..580d4f07f 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/ViaAPI.java +++ b/api/src/main/java/com/viaversion/viaversion/api/ViaAPI.java @@ -64,7 +64,7 @@ public interface ViaAPI { * @return API version incremented with meaningful API changes */ default int apiVersion() { - return 7; + return 8; } /** diff --git a/api/src/main/java/com/viaversion/viaversion/api/configuration/ViaVersionConfig.java b/api/src/main/java/com/viaversion/viaversion/api/configuration/ViaVersionConfig.java index 59266be11..ba8532fa8 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/configuration/ViaVersionConfig.java +++ b/api/src/main/java/com/viaversion/viaversion/api/configuration/ViaVersionConfig.java @@ -23,8 +23,11 @@ package com.viaversion.viaversion.api.configuration; import com.google.gson.JsonElement; -import it.unimi.dsi.fastutil.ints.IntSet; +import com.viaversion.viaversion.api.connection.StorableObject; +import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.minecraft.WorldIdentifiers; +import com.viaversion.viaversion.api.protocol.version.BlockedProtocolVersions; +import it.unimi.dsi.fastutil.ints.IntSet; public interface ViaVersionConfig { @@ -266,12 +269,23 @@ public interface ViaVersionConfig { */ boolean is1_12QuickMoveActionFix(); + /** + * API to check for blocked protocol versions. + * + * @return blocked protocol versions + */ + BlockedProtocolVersions blockedProtocolVersions(); + /** * Get the blocked protocols * * @return An Integer list + * @deprecated use {@link #blockedProtocolVersions()} */ - IntSet getBlockedProtocols(); + @Deprecated/*(forRemoval = true)*/ + default IntSet getBlockedProtocols() { + return blockedProtocolVersions().singleBlockedVersions(); + } /** * Get the custom disconnect message @@ -432,11 +446,11 @@ public interface ViaVersionConfig { JsonElement get1_17ResourcePackPrompt(); /*** - * Get the world names which should be returned for each vanilla dimension - * + * Get the world names that should be returned for each Vanilla dimension. + * Note that this can be overriden per-user by using {@link UserConnection#put(StorableObject)} with + * a custom instance of {@link WorldIdentifiers} for the user's {@link UserConnection}. + * * @return the global map from vanilla dimensions to world name - * Note that this can be overriden per-user by using {@link com.viaversion.viaversion.api.connection.UserConnection#put} with - * a custom instance of {@link WorldIdentifiers} for the user's {@link UserConnection} */ WorldIdentifiers get1_16WorldNamesMap(); } diff --git a/api/src/main/java/com/viaversion/viaversion/api/connection/UserConnection.java b/api/src/main/java/com/viaversion/viaversion/api/connection/UserConnection.java index 7b97a0a9f..b6d8ea2d6 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/connection/UserConnection.java +++ b/api/src/main/java/com/viaversion/viaversion/api/connection/UserConnection.java @@ -291,7 +291,7 @@ public interface UserConnection { boolean isClientSide(); /** - * Returns whether {@link ViaVersionConfig#getBlockedProtocols()} should be checked for this connection. + * Returns whether {@link ViaVersionConfig#blockedProtocolVersions()} should be checked for this connection. * * @return whether blocked protocols should be applied */ diff --git a/api/src/main/java/com/viaversion/viaversion/api/protocol/version/BlockedProtocolVersions.java b/api/src/main/java/com/viaversion/viaversion/api/protocol/version/BlockedProtocolVersions.java new file mode 100644 index 000000000..b50aa03c8 --- /dev/null +++ b/api/src/main/java/com/viaversion/viaversion/api/protocol/version/BlockedProtocolVersions.java @@ -0,0 +1,57 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2016-2021 ViaVersion and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.viaversion.viaversion.api.protocol.version; + +import it.unimi.dsi.fastutil.ints.IntSet; + +public interface BlockedProtocolVersions { + + /** + * Returns whether the given protocol version is blocked per boundary ranges or individually blocked versions. + * + * @param protocolVersion protocol version + * @return whether the given protocol version is blocked + */ + boolean contains(int protocolVersion); + + /** + * Returns the boundary below which protocol versions are blocked, or -1 if none is set. + * + * @return exclusive boundary below which protocol versions are blocked, or -1 if none + */ + int blocksBelow(); + + /** + * Returns the boundary above which protocol versions are blocked, or -1 if none is set. + * + * @return exclusive boundary above which protocol versions are blocked, or -1 if none + */ + int blocksAbove(); + + /** + * Returns a set of blocked protocol versions between the outer block ranges. + * + * @return set of blocked protocol versions between the outer block ranges + */ + IntSet singleBlockedVersions(); +} diff --git a/common/src/main/java/com/viaversion/viaversion/ViaAPIBase.java b/common/src/main/java/com/viaversion/viaversion/ViaAPIBase.java index 243f90485..6980de335 100644 --- a/common/src/main/java/com/viaversion/viaversion/ViaAPIBase.java +++ b/common/src/main/java/com/viaversion/viaversion/ViaAPIBase.java @@ -21,6 +21,7 @@ import com.viaversion.viaversion.api.Via; import com.viaversion.viaversion.api.ViaAPI; import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.legacy.LegacyViaAPI; +import com.viaversion.viaversion.api.protocol.version.BlockedProtocolVersions; import com.viaversion.viaversion.api.protocol.version.ServerProtocolVersion; import com.viaversion.viaversion.legacy.LegacyAPI; import io.netty.buffer.ByteBuf; @@ -73,7 +74,8 @@ public abstract class ViaAPIBase implements ViaAPI { @Override public SortedSet getSupportedVersions() { SortedSet outputSet = new TreeSet<>(Via.getManager().getProtocolManager().getSupportedVersions()); - outputSet.removeAll(Via.getPlatform().getConf().getBlockedProtocols()); + BlockedProtocolVersions blockedVersions = Via.getPlatform().getConf().blockedProtocolVersions(); + outputSet.removeIf(blockedVersions::contains); return outputSet; } diff --git a/common/src/main/java/com/viaversion/viaversion/configuration/AbstractViaConfig.java b/common/src/main/java/com/viaversion/viaversion/configuration/AbstractViaConfig.java index 55b1b7376..e809b0577 100644 --- a/common/src/main/java/com/viaversion/viaversion/configuration/AbstractViaConfig.java +++ b/common/src/main/java/com/viaversion/viaversion/configuration/AbstractViaConfig.java @@ -18,16 +18,21 @@ package com.viaversion.viaversion.configuration; import com.google.gson.JsonElement; -import com.google.gson.JsonObject; +import com.viaversion.viaversion.api.Via; import com.viaversion.viaversion.api.configuration.ViaVersionConfig; -import com.viaversion.viaversion.util.Config; import com.viaversion.viaversion.api.minecraft.WorldIdentifiers; +import com.viaversion.viaversion.api.protocol.version.BlockedProtocolVersions; +import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; +import com.viaversion.viaversion.protocol.BlockedProtocolVersionsImpl; +import com.viaversion.viaversion.util.Config; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; +import org.checkerframework.checker.nullness.qual.Nullable; import java.io.File; import java.util.HashMap; import java.util.Map; +import java.util.function.IntPredicate; public abstract class AbstractViaConfig extends Config implements ViaVersionConfig { @@ -60,7 +65,7 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf private boolean autoTeam; private boolean forceJsonTransform; private boolean nbtArrayFix; - private IntSet blockedProtocols; + private BlockedProtocolVersions blockedProtocolVersions; private String blockedDisconnectMessage; private String reloadDisconnectMessage; private boolean suppressConversionWarnings; @@ -124,7 +129,7 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf autoTeam = getBoolean("auto-team", true); forceJsonTransform = getBoolean("force-json-transform", false); nbtArrayFix = getBoolean("chat-nbt-fix", true); - blockedProtocols = new IntOpenHashSet(getIntegerList("block-protocols")); + blockedProtocolVersions = loadBlockedProtocolVersions(); blockedDisconnectMessage = getString("block-disconnect-msg", "You are using an unsupported Minecraft version!"); reloadDisconnectMessage = getString("reload-disconnect-msg", "Server reload, please rejoin!"); minimizeCooldown = getBoolean("minimize-cooldown", true); @@ -152,6 +157,74 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf worlds.getOrDefault("end", WorldIdentifiers.END_DEFAULT)); } + private BlockedProtocolVersions loadBlockedProtocolVersions() { + IntSet blockedProtocols = new IntOpenHashSet(getIntegerList("block-protocols")); + int lowerBound = -1; + int upperBound = -1; + for (String s : getStringList("block-versions")) { + if (s.isEmpty()) { + continue; + } + + char c = s.charAt(0); + if (c == '<' || c == '>') { + // Set lower/upper bound + ProtocolVersion protocolVersion = protocolVersion(s.substring(1)); + if (protocolVersion == null) { + continue; + } + + if (c == '<') { + if (lowerBound != -1) { + Via.getPlatform().getLogger().warning("Already set lower bound " + lowerBound + " overridden by " + protocolVersion.getName()); + } + lowerBound = protocolVersion.getVersion(); + } else { + if (upperBound != -1) { + Via.getPlatform().getLogger().warning("Already set upper bound " + upperBound + " overridden by " + protocolVersion.getName()); + } + upperBound = protocolVersion.getVersion(); + } + continue; + } + + ProtocolVersion protocolVersion = protocolVersion(s); + if (protocolVersion == null) { + continue; + } + + // Add single protocol version and check for duplication + if (!blockedProtocols.add(protocolVersion.getVersion())) { + Via.getPlatform().getLogger().warning("Duplicated blocked protocol version " + protocolVersion.getName() + "/" + protocolVersion.getVersion()); + } + } + + // Check for duplicated entries + if (lowerBound != -1 || upperBound != -1) { + final int finalLowerBound = lowerBound; + final int finalUpperBound = upperBound; + blockedProtocols.removeIf((IntPredicate) version -> { + if (finalLowerBound != -1 && version < finalLowerBound || finalUpperBound != -1 && version > finalUpperBound) { + ProtocolVersion protocolVersion = ProtocolVersion.getProtocol(version); + Via.getPlatform().getLogger().warning("Blocked protocol version " + + protocolVersion.getName() + "/" + protocolVersion.getVersion() + " already covered by upper or lower bound"); + return true; + } + return false; + }); + } + return new BlockedProtocolVersionsImpl(blockedProtocols, lowerBound, upperBound); + } + + private @Nullable ProtocolVersion protocolVersion(String s) { + ProtocolVersion protocolVersion = ProtocolVersion.getClosest(s); + if (protocolVersion == null) { + Via.getPlatform().getLogger().warning("Unknown protocol version in block-versions: " + s); + return null; + } + return protocolVersion; + } + @Override public boolean isCheckForUpdates() { return checkForUpdates; @@ -315,8 +388,8 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf } @Override - public IntSet getBlockedProtocols() { - return blockedProtocols; + public BlockedProtocolVersions blockedProtocolVersions() { + return blockedProtocolVersions; } @Override @@ -442,5 +515,5 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf @Override public WorldIdentifiers get1_16WorldNamesMap() { return map1_16WorldNames; - }; + } } diff --git a/common/src/main/java/com/viaversion/viaversion/protocol/BlockedProtocolVersionsImpl.java b/common/src/main/java/com/viaversion/viaversion/protocol/BlockedProtocolVersionsImpl.java new file mode 100644 index 000000000..f8be5ef78 --- /dev/null +++ b/common/src/main/java/com/viaversion/viaversion/protocol/BlockedProtocolVersionsImpl.java @@ -0,0 +1,55 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2016-2021 ViaVersion and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.viaversion.viaversion.protocol; + +import com.viaversion.viaversion.api.protocol.version.BlockedProtocolVersions; +import it.unimi.dsi.fastutil.ints.IntSet; + +public class BlockedProtocolVersionsImpl implements BlockedProtocolVersions { + private final IntSet singleBlockedVersions; + private final int blocksBelow; + private final int blocksAbove; + + public BlockedProtocolVersionsImpl(final IntSet singleBlockedVersions, final int blocksBelow, final int blocksAbove) { + this.singleBlockedVersions = singleBlockedVersions; + this.blocksBelow = blocksBelow; + this.blocksAbove = blocksAbove; + } + + @Override + public boolean contains(final int protocolVersion) { + return blocksBelow != -1 && protocolVersion < blocksBelow + || blocksAbove != -1 && protocolVersion > blocksAbove + || singleBlockedVersions.contains(protocolVersion); + } + + @Override + public int blocksBelow() { + return blocksBelow; + } + + @Override + public int blocksAbove() { + return blocksAbove; + } + + @Override + public IntSet singleBlockedVersions() { + return singleBlockedVersions; + } +} diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/base/BaseProtocol1_7.java b/common/src/main/java/com/viaversion/viaversion/protocols/base/BaseProtocol1_7.java index fe4dc9be7..6e5f1ccf8 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/base/BaseProtocol1_7.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/base/BaseProtocol1_7.java @@ -111,7 +111,7 @@ public class BaseProtocol1_7 extends AbstractProtocol { wrapper.user().setActive(false); } - if (Via.getConfig().getBlockedProtocols().contains(info.getProtocolVersion())) { + if (Via.getConfig().blockedProtocolVersions().contains(info.getProtocolVersion())) { version.addProperty("protocol", -1); // Show blocked versions as outdated } @@ -169,7 +169,7 @@ public class BaseProtocol1_7 extends AbstractProtocol { @Override public void handle(final PacketWrapper wrapper) throws Exception { int protocol = wrapper.user().getProtocolInfo().getProtocolVersion(); - if (Via.getConfig().getBlockedProtocols().contains(protocol)) { + if (Via.getConfig().blockedProtocolVersions().contains(protocol)) { if (!wrapper.user().getChannel().isOpen()) return; if (!wrapper.user().shouldApplyBlockProtocol()) return; diff --git a/common/src/main/java/com/viaversion/viaversion/util/CommentStore.java b/common/src/main/java/com/viaversion/viaversion/util/CommentStore.java index 026d78ff0..e47b71647 100644 --- a/common/src/main/java/com/viaversion/viaversion/util/CommentStore.java +++ b/common/src/main/java/com/viaversion/viaversion/util/CommentStore.java @@ -150,7 +150,11 @@ public class CommentStore { } for (String line : yaml.split("\n")) { - if (line.isEmpty()) continue; // Skip empty lines + if (line.isEmpty() || line.trim().charAt(0) == '-') { + fileData.append(line).append('\n'); + continue; + } + int indent = getSuccessiveCharCount(line, ' '); int indents = indent / indentLength; String indentText = indent > 0 ? line.substring(0, indent) : ""; @@ -162,7 +166,7 @@ public class CommentStore { } // Add new section to key - String separator = key.length() > 0 ? pathSeparator : ""; + String separator = !key.isEmpty() ? pathSeparator : ""; String lineKey = line.contains(":") ? line.split(Pattern.quote(":"))[0] : line; key += separator + lineKey.substring(indent); diff --git a/common/src/main/java/com/viaversion/viaversion/util/Config.java b/common/src/main/java/com/viaversion/viaversion/util/Config.java index 46f60609a..08882c189 100644 --- a/common/src/main/java/com/viaversion/viaversion/util/Config.java +++ b/common/src/main/java/com/viaversion/viaversion/util/Config.java @@ -204,11 +204,12 @@ public abstract class Config implements ConfigurationProvider { public List getIntegerList(String key) { Object o = this.config.get(key); - if (o != null) { - return (List) o; - } else { - return new ArrayList<>(); - } + return o != null ? (List) o : new ArrayList<>(); + } + + public List getStringList(String key) { + Object o = this.config.get(key); + return o != null ? (List) o : new ArrayList<>(); } public @Nullable JsonElement getSerializedComponent(String key) { diff --git a/common/src/main/resources/assets/viaversion/config.yml b/common/src/main/resources/assets/viaversion/config.yml index 1801261ca..288ddbfd7 100644 --- a/common/src/main/resources/assets/viaversion/config.yml +++ b/common/src/main/resources/assets/viaversion/config.yml @@ -13,8 +13,12 @@ checkforupdates: true # Send the supported versions with the Status (Ping) response packet send-supported-versions: false -# Block specific Minecraft protocols that ViaVersion allows -# List of all Minecraft protocol versions: http://wiki.vg/Protocol_version_numbers or use a generator: https://via.krusic22.com +# Easier to configure alternative to 'block-protocols'. Uses readable version strings with possible '<' and '>' prefixes. +# An example to block 1.16.4, everything below 1.16, as well as everything above 1.17.1 would be: ["<1.16", "1.16.4", ">1.17.1"] +# You can use both this and the block-protocols option at the same time as well. +block-versions: [] +# Block specific Minecraft protocol version numbers. +# List of all Minecraft protocol versions: http://wiki.vg/Protocol_version_numbers, or use a generator: https://via.krusic22.com block-protocols: [] # Change the blocked disconnect message block-disconnect-msg: "You are using an unsupported Minecraft version!"