From 1129c174895dd2657aef07df5011ae37ea48c898 Mon Sep 17 00:00:00 2001 From: KennyTV Date: Wed, 21 Oct 2020 13:28:20 +0200 Subject: [PATCH] Improve ProtocolVersion subversion/range handling Closes #2041 --- .../api/protocol/ProtocolVersion.java | 95 +++++++++++++++---- .../ViaVersion/api/protocol/VersionRange.java | 37 ++++++++ .../common/protocol/ProtocolVersionTest.java | 28 ++++++ 3 files changed, 140 insertions(+), 20 deletions(-) create mode 100644 common/src/main/java/us/myles/ViaVersion/api/protocol/VersionRange.java create mode 100644 common/src/test/java/us/myles/ViaVersion/common/protocol/ProtocolVersionTest.java diff --git a/common/src/main/java/us/myles/ViaVersion/api/protocol/ProtocolVersion.java b/common/src/main/java/us/myles/ViaVersion/api/protocol/ProtocolVersion.java index ad79b4b34..4ef388215 100644 --- a/common/src/main/java/us/myles/ViaVersion/api/protocol/ProtocolVersion.java +++ b/common/src/main/java/us/myles/ViaVersion/api/protocol/ProtocolVersion.java @@ -4,10 +4,13 @@ import com.google.common.base.Preconditions; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; public class ProtocolVersion { private static final Int2ObjectMap versions = new Int2ObjectOpenHashMap<>(); @@ -22,13 +25,13 @@ public class ProtocolVersion { public static final ProtocolVersion v_1_6_3 = register(77, "1.6.3"); public static final ProtocolVersion v_1_6_4 = register(78, "1.6.4"); // After the Netty rewrite - public static final ProtocolVersion v1_7_1 = register(4, "1.7-1.7.5"); - public static final ProtocolVersion v1_7_6 = register(5, "1.7.6-1.7.10"); + public static final ProtocolVersion v1_7_1 = register(4, "1.7-1.7.5", new VersionRange("1.7", 0, 5)); + public static final ProtocolVersion v1_7_6 = register(5, "1.7.6-1.7.10", new VersionRange("1.7", 6, 10)); public static final ProtocolVersion v1_8 = register(47, "1.8.x"); public static final ProtocolVersion v1_9 = register(107, "1.9"); public static final ProtocolVersion v1_9_1 = register(108, "1.9.1"); public static final ProtocolVersion v1_9_2 = register(109, "1.9.2"); - public static final ProtocolVersion v1_9_3 = register(110, "1.9.3/4"); + public static final ProtocolVersion v1_9_3 = register(110, "1.9.3/4", new VersionRange("1.9", 3, 4)); public static final ProtocolVersion v1_10 = register(210, "1.10"); public static final ProtocolVersion v1_11 = register(315, "1.11"); public static final ProtocolVersion v1_11_1 = register(316, "1.11.1"); @@ -58,7 +61,15 @@ public class ProtocolVersion { } public static ProtocolVersion register(int version, int snapshotVersion, String name) { - ProtocolVersion protocol = new ProtocolVersion(version, snapshotVersion, name); + return register(version, snapshotVersion, name, null); + } + + public static ProtocolVersion register(int version, String name, @Nullable VersionRange versionRange) { + return register(version, -1, name, versionRange); + } + + public static ProtocolVersion register(int version, int snapshotVersion, String name, @Nullable VersionRange versionRange) { + ProtocolVersion protocol = new ProtocolVersion(version, snapshotVersion, name, versionRange); versionList.add(protocol); versions.put(protocol.version, protocol); if (protocol.isSnapshot()) { @@ -89,19 +100,23 @@ public class ProtocolVersion { return Collections.unmodifiableList(new ArrayList<>(versions.values())); } + @Nullable public static ProtocolVersion getClosest(String protocol) { for (ProtocolVersion version : versions.values()) { String name = version.name; - if (name.equals(protocol) || name.equals(protocol + ".x")) { + if (name.equals(protocol)) { return version; } - if (version.isRange()) { - String[] parts = name.split("-"); - for (String part : parts) { - if (part.equalsIgnoreCase(protocol) || part.equals(protocol + ".x")) { - return version; - } + if (version.versionWildcard) { + // Test against the major version as well as with the ".x" prefix + String majorVersion = name.substring(0, name.length() - 2); + if (majorVersion.equals(protocol) || (protocol.startsWith(name.substring(0, name.length() - 1)))) { + return version; + } + } else if (version.isRange()) { + if (version.includedVersions.contains(protocol)) { + return version; } } } @@ -111,15 +126,32 @@ public class ProtocolVersion { private final int version; private final int snapshotVersion; private final String name; + private final boolean versionWildcard; + private final Set includedVersions; public ProtocolVersion(int version, String name) { - this(version, -1, name); + this(version, -1, name, null); } - public ProtocolVersion(int version, int snapshotVersion, String name) { + public ProtocolVersion(int version, int snapshotVersion, String name, @Nullable VersionRange versionRange) { this.version = version; this.snapshotVersion = snapshotVersion; this.name = name; + this.versionWildcard = name.endsWith(".x"); + + Preconditions.checkArgument(!versionWildcard || versionRange == null, "A version cannot be a wildcard and a range at the same time!"); + if (versionRange != null) { + includedVersions = new HashSet<>(); + for (int i = versionRange.getRangeFrom(); i <= versionRange.getRangeTo(); i++) { + if (i == 0) { + includedVersions.add(versionRange.getBaseVersion()); // Keep both the base version and with ".0" appended + } + + includedVersions.add(versionRange.getBaseVersion() + "." + i); + } + } else { + includedVersions = Collections.singleton(name); + } } /** @@ -157,12 +189,30 @@ public class ProtocolVersion { } /** - * @return release version - * @deprecated ambiguous, see {@link #getOriginalVersion()} ()}, {@link #getVersion()} ()}, and {@link #getSnapshotVersion()} + * @return true if the protocol includes a range of versions (but not an entire major version range), for example 1.7-1.7.5 + * @see #getIncludedVersions() */ - @Deprecated - public int getId() { - return version; + public boolean isRange() { + return includedVersions.size() != 1; + } + + /** + * Returns an immutable set of all included versions if the protocol is a version range. + * If the protocol only includes a single Minecraft version or the entire major version as a wildcard ({@link #isVersionWildcard()}), + * the set will only contain the string given in {@link #getName()}. + * + * @return immutable set of all included versions if the protocol is a version range + * @see #isRange() + */ + public Set getIncludedVersions() { + return Collections.unmodifiableSet(includedVersions); + } + + /** + * @return true if the protocol includes an entire major version range (for example 1.8.x) + */ + public boolean isVersionWildcard() { + return versionWildcard; } public String getName() { @@ -173,8 +223,13 @@ public class ProtocolVersion { return snapshotVersion != -1; } - public boolean isRange() { - return name.indexOf('-') != -1; + /** + * @return release version + * @deprecated ambiguous, see {@link #getOriginalVersion()}, {@link #getVersion()}, and {@link #getSnapshotVersion()} + */ + @Deprecated + public int getId() { + return version; } @Override diff --git a/common/src/main/java/us/myles/ViaVersion/api/protocol/VersionRange.java b/common/src/main/java/us/myles/ViaVersion/api/protocol/VersionRange.java new file mode 100644 index 000000000..2cd7eb41e --- /dev/null +++ b/common/src/main/java/us/myles/ViaVersion/api/protocol/VersionRange.java @@ -0,0 +1,37 @@ +package us.myles.ViaVersion.api.protocol; + +import com.google.common.base.Preconditions; + +public class VersionRange { + private final String baseVersion; + private final int rangeFrom; + private final int rangeTo; + + /** + * Creates a new version range. Giving "1.7", 0, and 5 for example would represent the range from 1.7-1.7.5. + * + * @param baseVersion base version + * @param rangeFrom minor version the range begins at, must be greater than or equal to 0 + * @param rangeTo minor version the range ends at, must be greater than 0 and {@link #rangeFrom} + */ + public VersionRange(String baseVersion, int rangeFrom, int rangeTo) { + Preconditions.checkNotNull(baseVersion); + Preconditions.checkArgument(rangeFrom >= 0); + Preconditions.checkArgument(rangeTo > rangeFrom && rangeTo > 0); + this.baseVersion = baseVersion; + this.rangeFrom = rangeFrom; + this.rangeTo = rangeTo; + } + + public String getBaseVersion() { + return baseVersion; + } + + public int getRangeFrom() { + return rangeFrom; + } + + public int getRangeTo() { + return rangeTo; + } +} diff --git a/common/src/test/java/us/myles/ViaVersion/common/protocol/ProtocolVersionTest.java b/common/src/test/java/us/myles/ViaVersion/common/protocol/ProtocolVersionTest.java new file mode 100644 index 000000000..facc0efab --- /dev/null +++ b/common/src/test/java/us/myles/ViaVersion/common/protocol/ProtocolVersionTest.java @@ -0,0 +1,28 @@ +package us.myles.ViaVersion.common.protocol; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import us.myles.ViaVersion.api.protocol.ProtocolVersion; + +public class ProtocolVersionTest { + + @Test + void testVersionWildcard() { + Assertions.assertEquals(ProtocolVersion.v1_8, ProtocolVersion.getClosest("1.8.3")); + Assertions.assertEquals(ProtocolVersion.v1_8, ProtocolVersion.getClosest("1.8")); + Assertions.assertEquals(ProtocolVersion.v1_8, ProtocolVersion.getClosest("1.8.x")); + } + + @Test + void testVersionRange() { + Assertions.assertEquals(ProtocolVersion.v1_7_1, ProtocolVersion.getClosest("1.7")); + Assertions.assertEquals(ProtocolVersion.v1_7_1, ProtocolVersion.getClosest("1.7.0")); + Assertions.assertEquals(ProtocolVersion.v1_7_1, ProtocolVersion.getClosest("1.7.1")); + Assertions.assertEquals(ProtocolVersion.v1_7_1, ProtocolVersion.getClosest("1.7.5")); + } + + @Test + void testGet() { + Assertions.assertEquals(ProtocolVersion.v1_16_3, ProtocolVersion.getProtocol(753)); + } +}