diff --git a/pandalib-core/pom.xml b/pandalib-core/pom.xml
index b822750..7b0dba0 100644
--- a/pandalib-core/pom.xml
+++ b/pandalib-core/pom.xml
@@ -85,6 +85,27 @@
+
+
+ com.googlecode.maven-download-plugin
+ download-maven-plugin
+ 1.7.0
+
+
+ mcversion-download
+ compile
+
+ wget
+
+
+
+
+ https://api.pandacube.fr/rest/mcversion
+ ${project.basedir}/src/main/resources/fr/pandacube/lib/core/mc_version
+ mcversion.json
+ true
+
+
diff --git a/pandalib-core/src/main/java/fr/pandacube/lib/core/mc_version/MinecraftVersionUtil.java b/pandalib-core/src/main/java/fr/pandacube/lib/core/mc_version/MinecraftVersionUtil.java
index 066ba20..8cb3e62 100644
--- a/pandalib-core/src/main/java/fr/pandacube/lib/core/mc_version/MinecraftVersionUtil.java
+++ b/pandalib-core/src/main/java/fr/pandacube/lib/core/mc_version/MinecraftVersionUtil.java
@@ -4,15 +4,23 @@ import fr.pandacube.lib.util.StringUtil;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
+/**
+ * Utility class to manipulate {@link String}s representing Minecraft versions.
+ */
public class MinecraftVersionUtil {
-
+ /**
+ * Compare two Minecraft version strings. It uses the rules of semantic
+ * versionning to compare the versions.
+ * @param v1 the first version to compare.
+ * @param v2 the second version to compare.
+ * @return 0 if they are equal, <0 if v1<v2 and vice-versa.
+ */
public static int compareVersions(String v1, String v2) {
int[] v1Int = decomposedVersion(v1);
int[] v2Int = decomposedVersion(v2);
@@ -27,7 +35,11 @@ public class MinecraftVersionUtil {
}
-
+ /**
+ * Decompose a version string into a series of integers.
+ * @param v a string representation of a version (eg. 1.19.1).
+ * @return an array of int representing the provided version (eg. [1, 19, 1]).
+ */
public static int[] decomposedVersion(String v) {
try {
return Arrays.stream(v.split("\\.")).mapToInt(Integer::parseInt).toArray();
@@ -36,7 +48,20 @@ public class MinecraftVersionUtil {
}
}
-
+ /**
+ * Tells if the two provided Minecraft versions are consecutives.
+ *
+ * Two versions are consecutives if (considering {@code 1.X[.Y]}):
+ *
versions, String finalWordSeparator) {
if (versions.isEmpty())
return "";
diff --git a/pandalib-core/src/main/java/fr/pandacube/lib/core/mc_version/ProtocolVersion.java b/pandalib-core/src/main/java/fr/pandacube/lib/core/mc_version/ProtocolVersion.java
index 888eced..7b3b351 100644
--- a/pandalib-core/src/main/java/fr/pandacube/lib/core/mc_version/ProtocolVersion.java
+++ b/pandalib-core/src/main/java/fr/pandacube/lib/core/mc_version/ProtocolVersion.java
@@ -2,7 +2,8 @@ package fr.pandacube.lib.core.mc_version;
import fr.pandacube.lib.core.json.Json;
import fr.pandacube.lib.util.Log;
-import org.jetbrains.annotations.ApiStatus.Internal;
+import fr.pandacube.lib.util.MinecraftVersion;
+import fr.pandacube.lib.util.StringUtil;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -11,9 +12,22 @@ import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
+import java.time.Duration;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
+/**
+ * Class handling a relationship table of known Minecraft version and their
+ * corresponding protocol version.
+ *
+ * The data if fetch updated data from an external API on startup. If it fails,
+ * it uses the data stored in the current package at build time.
+ *
+ * The public static methos are used to fetch an instance of {@link ProtocolVersion}
+ * based on the provided protocol version (eg. 763) or Minecraft version (eg. "1.20.1").
+ * An instance of this class provides information related to a protocol version
+ * (the protocol version number and all the corresponding Minecraft versions).
+ */
public class ProtocolVersion {
private static final String ONLINE_DATA_URL = "https://api.pandacube.fr/rest/mcversion";
@@ -28,10 +42,25 @@ public class ProtocolVersion {
}
}
-
+ /**
+ * Replace the currently used data cache by a new source.
+ *
+ * Note: this method is not meant to be used by the final user of
+ * this class. Use it only if you have a better data source.
+ * @param data the data to use instead of the provided (external API or packaged file)
+ */
public static void setRawData(MinecraftVersionList data) {
versionList.set(data);
}
+
+ /**
+ * Gets the raw data used internally by this class.
+ *
+ * Note: this method is not meant to be used by the final user of
+ * this class. Use it only if you know what you do.
+ * @return the current instance of {@link MinecraftVersionUtil} uses
+ * internally by this class.
+ */
public static MinecraftVersionList getRawData() {
initIfNecessary();
return versionList.get();
@@ -40,7 +69,9 @@ public class ProtocolVersion {
private static void init() {
// try online source first
try {
- HttpResponse response = HttpClient.newHttpClient()
+ HttpResponse response = HttpClient.newBuilder()
+ .connectTimeout(Duration.ofSeconds(5))
+ .build()
.send(HttpRequest.newBuilder(URI.create(ONLINE_DATA_URL)).build(),
BodyHandlers.ofString()
);
@@ -70,19 +101,96 @@ public class ProtocolVersion {
}
-
-
- public static int ofVersion(String versionId) {
+ private static int getPVNOfVersion(String version) {
initIfNecessary();
- Integer v = versionList.get().protocolOfVersion().get(versionId);
+ Integer v = versionList.get().protocolOfVersion().get(version);
return v == null ? -1 : v;
}
-
- public static List getVersionsOfProtocol(int protocolVersion) {
+ private static List getVersionsOfPVN(int pvn) {
initIfNecessary();
- return versionList.get().versionsOfProtocol().get(protocolVersion);
+ return versionList.get().versionsOfProtocol().get(pvn);
+ }
+
+ /**
+ * Gets the {@link ProtocolVersion} associated with the provided Minecraft version.
+ * @param version The Minecraft version, in the format "X.X[.X]" (eg. "1.17" or "1.8.8").
+ * @return an instance of {@link ProtocolVersion}.
+ */
+ public static ProtocolVersion ofVersion(String version) {
+ int pvn = getPVNOfVersion(version);
+ if (pvn == -1)
+ return null;
+ List versions = getVersionsOfPVN(pvn);
+ if (versions == null) {
+ versions = List.of(version);
+ }
+ return new ProtocolVersion(pvn, List.copyOf(versions));
+ }
+
+ /**
+ * Gets the {@link ProtocolVersion} associated with the provided protocol version number.
+ * @param pvn The protocol version number.
+ * @return an instance of {@link ProtocolVersion}.
+ */
+ public static ProtocolVersion ofProtocol(int pvn) {
+ List versions = getVersionsOfPVN(pvn);
+ if (versions == null) {
+ return null;
+ }
+ return new ProtocolVersion(pvn, List.copyOf(versions));
}
+
+
+ /**
+ * The protocol version number.
+ */
+ public final int protocolVersionNumber;
+ /**
+ * All Minecraft version supported by this protocol version number.
+ */
+ public final List versions;
+
+ private ProtocolVersion(int protocolVersionNumber, List versions) {
+ this.protocolVersionNumber = protocolVersionNumber;
+ this.versions = versions;
+ }
+
+ @Override
+ public String toString() {
+ return "ProtocolVersion{protocol=" + protocolVersionNumber + ", toString(\"and\")=" + toString("and") + "}";
+ }
+
+ /**
+ * Returns a string representation of all the Minecraft version of this enum value, using
+ * {@link StringUtil#joinGrammatically(CharSequence, CharSequence, List)}.
+ *
+ * @param finalWordSeparator the word separator between the two last versions in the returned string, like "and",
+ * "or" or any other word of any language. The spaces before and after are already
+ * concatenated.
+ * @return a string representation of this {@link ProtocolVersion}.
+ */
+ public String toString(String finalWordSeparator) {
+ return displayOptimizedListOfVersions(List.of(this), finalWordSeparator);
+ }
+
+
+ /**
+ * Generate a string representation of the provided list of version, using
+ * {@link StringUtil#joinGrammatically(CharSequence, CharSequence, List)}.
+ *
+ * @param versions the minecraft versions to list
+ * @param finalWordSeparator the word separator between the two last versions in the returned string, like "and",
+ * "or" or any other word of any language. The spaces before and after are already
+ * concatenated.
+ * @return a string representation of the provided list of version.
+ */
+ public static String displayOptimizedListOfVersions(List versions, String finalWordSeparator) {
+ return MinecraftVersionUtil.toString(versions.stream().flatMap(pv -> pv.versions.stream()).toList(), finalWordSeparator);
+ }
+
+
+
}
diff --git a/pandalib-core/src/main/resources/fr/pandacube/lib/core/mc_version/mcversion.json b/pandalib-core/src/main/resources/fr/pandacube/lib/core/mc_version/mcversion.json
new file mode 100644
index 0000000..b982dd5
--- /dev/null
+++ b/pandalib-core/src/main/resources/fr/pandacube/lib/core/mc_version/mcversion.json
@@ -0,0 +1,206 @@
+{
+ "protocolOfVersion": {
+ "1.20.1": 763,
+ "1.20": 763,
+ "1.19.4": 762,
+ "1.19.3": 761,
+ "1.19.2": 760,
+ "1.19.1": 760,
+ "1.19": 759,
+ "1.18.2": 758,
+ "1.18.1": 757,
+ "1.18": 757,
+ "1.17.1": 756,
+ "1.17": 755,
+ "1.16.5": 754,
+ "1.16.4": 754,
+ "1.16.3": 753,
+ "1.16.2": 751,
+ "1.16.1": 736,
+ "1.16": 735,
+ "1.15.2": 578,
+ "1.15.1": 575,
+ "1.15": 573,
+ "1.14.4": 498,
+ "1.14.3": 490,
+ "1.14.2": 485,
+ "1.14.1": 480,
+ "1.14": 477,
+ "1.13.2": 404,
+ "1.13.1": 401,
+ "1.13": 393,
+ "1.12.2": 340,
+ "1.12.1": 338,
+ "1.12": 335,
+ "1.11.2": 316,
+ "1.11.1": 316,
+ "1.11": 315,
+ "1.10.2": 210,
+ "1.10.1": 210,
+ "1.10": 210,
+ "1.9.4": 110,
+ "1.9.3": 110,
+ "1.9.2": 109,
+ "1.9.1": 108,
+ "1.9": 107,
+ "1.8.9": 47,
+ "1.8.8": 47,
+ "1.8.7": 47,
+ "1.8.6": 47,
+ "1.8.5": 47,
+ "1.8.4": 47,
+ "1.8.3": 47,
+ "1.8.2": 47,
+ "1.8.1": 47,
+ "1.8": 47,
+ "1.7.10": 5,
+ "1.7.9": 5,
+ "1.7.8": 5,
+ "1.7.7": 5,
+ "1.7.6": 5,
+ "1.7.5": 4,
+ "1.7.4": 4,
+ "1.7.3": 4,
+ "1.7.2": 4
+ },
+ "versionsOfProtocol": {
+ "763": [
+ "1.20",
+ "1.20.1"
+ ],
+ "762": [
+ "1.19.4"
+ ],
+ "761": [
+ "1.19.3"
+ ],
+ "760": [
+ "1.19.1",
+ "1.19.2"
+ ],
+ "759": [
+ "1.19"
+ ],
+ "758": [
+ "1.18.2"
+ ],
+ "757": [
+ "1.18",
+ "1.18.1"
+ ],
+ "756": [
+ "1.17.1"
+ ],
+ "755": [
+ "1.17"
+ ],
+ "754": [
+ "1.16.4",
+ "1.16.5"
+ ],
+ "753": [
+ "1.16.3"
+ ],
+ "751": [
+ "1.16.2"
+ ],
+ "736": [
+ "1.16.1"
+ ],
+ "735": [
+ "1.16"
+ ],
+ "578": [
+ "1.15.2"
+ ],
+ "575": [
+ "1.15.1"
+ ],
+ "573": [
+ "1.15"
+ ],
+ "498": [
+ "1.14.4"
+ ],
+ "490": [
+ "1.14.3"
+ ],
+ "485": [
+ "1.14.2"
+ ],
+ "480": [
+ "1.14.1"
+ ],
+ "477": [
+ "1.14"
+ ],
+ "404": [
+ "1.13.2"
+ ],
+ "401": [
+ "1.13.1"
+ ],
+ "393": [
+ "1.13"
+ ],
+ "340": [
+ "1.12.2"
+ ],
+ "338": [
+ "1.12.1"
+ ],
+ "335": [
+ "1.12"
+ ],
+ "316": [
+ "1.11.1",
+ "1.11.2"
+ ],
+ "315": [
+ "1.11"
+ ],
+ "210": [
+ "1.10",
+ "1.10.1",
+ "1.10.2"
+ ],
+ "110": [
+ "1.9.3",
+ "1.9.4"
+ ],
+ "109": [
+ "1.9.2"
+ ],
+ "108": [
+ "1.9.1"
+ ],
+ "107": [
+ "1.9"
+ ],
+ "47": [
+ "1.8",
+ "1.8.1",
+ "1.8.2",
+ "1.8.3",
+ "1.8.4",
+ "1.8.5",
+ "1.8.6",
+ "1.8.7",
+ "1.8.8",
+ "1.8.9"
+ ],
+ "5": [
+ "1.7.6",
+ "1.7.7",
+ "1.7.8",
+ "1.7.9",
+ "1.7.10"
+ ],
+ "4": [
+ "1.7.2",
+ "1.7.3",
+ "1.7.4",
+ "1.7.5"
+ ]
+ }
+}
\ No newline at end of file