From edd5b06a469f39cba9124940732f38a8e92b21b9 Mon Sep 17 00:00:00 2001 From: Marc Baloup Date: Thu, 15 Jun 2023 15:08:18 +0200 Subject: [PATCH] Deprecate old MinecraftVersion enum and create new ProtocolVersion class which automatically update the version list on startup --- .../core/mc_version/MinecraftVersionList.java | 24 +++++ .../core/mc_version/MinecraftVersionUtil.java | 102 ++++++++++++++++++ .../lib/core/mc_version/ProtocolVersion.java | 88 +++++++++++++++ .../pandacube/lib/util/MinecraftVersion.java | 10 +- 4 files changed, 220 insertions(+), 4 deletions(-) create mode 100644 pandalib-core/src/main/java/fr/pandacube/lib/core/mc_version/MinecraftVersionList.java create mode 100644 pandalib-core/src/main/java/fr/pandacube/lib/core/mc_version/MinecraftVersionUtil.java create mode 100644 pandalib-core/src/main/java/fr/pandacube/lib/core/mc_version/ProtocolVersion.java diff --git a/pandalib-core/src/main/java/fr/pandacube/lib/core/mc_version/MinecraftVersionList.java b/pandalib-core/src/main/java/fr/pandacube/lib/core/mc_version/MinecraftVersionList.java new file mode 100644 index 0000000..1f7e442 --- /dev/null +++ b/pandalib-core/src/main/java/fr/pandacube/lib/core/mc_version/MinecraftVersionList.java @@ -0,0 +1,24 @@ +package fr.pandacube.lib.core.mc_version; + +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public record MinecraftVersionList( + Map protocolOfVersion, + Map> versionsOfProtocol +) { + public MinecraftVersionList() { + this(new TreeMap<>(MinecraftVersionUtil::compareVersions), new TreeMap<>()); + } + + public void add(String versionId, int protocolVersion) { + protocolOfVersion.put(versionId, protocolVersion); + List versions = versionsOfProtocol.computeIfAbsent(protocolVersion, p -> new ArrayList<>()); + versions.add(versionId); + versions.sort(MinecraftVersionUtil::compareVersions); + } +} 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 new file mode 100644 index 0000000..066ba20 --- /dev/null +++ b/pandalib-core/src/main/java/fr/pandacube/lib/core/mc_version/MinecraftVersionUtil.java @@ -0,0 +1,102 @@ +package fr.pandacube.lib.core.mc_version; + +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; + +public class MinecraftVersionUtil { + + + + public static int compareVersions(String v1, String v2) { + int[] v1Int = decomposedVersion(v1); + int[] v2Int = decomposedVersion(v2); + + for (int i = 0; i < Math.min(v1Int.length, v2Int.length); i++) { + int cmp = Integer.compare(v1Int[i], v2Int[i]); + if (cmp != 0) + return cmp; + } + + return Integer.compare(v1Int.length, v2Int.length); + } + + + + public static int[] decomposedVersion(String v) { + try { + return Arrays.stream(v.split("\\.")).mapToInt(Integer::parseInt).toArray(); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid version format: '" + v + "'.", e); + } + } + + + public static boolean areConsecutives(String v1, String v2) { + int[] v1Int = decomposedVersion(v1); + int[] v2Int = decomposedVersion(v2); + + if (v1Int.length == v2Int.length) { + for (int i = 0; i < v1Int.length - 1; i++) { + if (v1Int[i] != v2Int[i]) + return false; + } + return v1Int[v1Int.length - 1] + 1 == v2Int[v2Int.length - 1]; + } + else if (v1Int.length == v2Int.length - 1) { + for (int i = 0; i < v1Int.length; i++) { + if (v1Int[i] != v2Int[i]) + return false; + } + return v2Int[v2Int.length - 1] == 1; + } + + return false; + } + + + + + public static String toString(List versions, String finalWordSeparator) { + if (versions.isEmpty()) + return ""; + // put them in order and remove duplicates + versions = new ArrayList<>(toOrderedSet(versions)); + List keptVersions = new ArrayList<>(versions.size()); + + for (int i = 0, firstConsec = 0; i < versions.size(); i++) { + if (i == versions.size() - 1 || !areConsecutives(versions.get(i), versions.get(i + 1))) { + if (firstConsec == i) { + keptVersions.add(versions.get(i)); + firstConsec++; + } + else { + // merge + if (i - firstConsec > 1) + keptVersions.add(versions.get(firstConsec) + "-" + versions.get(i)); + else { + keptVersions.add(versions.get(firstConsec)); + keptVersions.add(versions.get(i)); + } + firstConsec = i + 1; + } + } + } + + return StringUtil.joinGrammatically(", ", " " + finalWordSeparator + " ", keptVersions); + } + + + + private static Set toOrderedSet(List versions) { + Set set = new TreeSet<>(MinecraftVersionUtil::compareVersions); + set.addAll(versions); + return set; + } + +} 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 new file mode 100644 index 0000000..888eced --- /dev/null +++ b/pandalib-core/src/main/java/fr/pandacube/lib/core/mc_version/ProtocolVersion.java @@ -0,0 +1,88 @@ +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 java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +public class ProtocolVersion { + + private static final String ONLINE_DATA_URL = "https://api.pandacube.fr/rest/mcversion"; + + private static final AtomicReference versionList = new AtomicReference<>(); + + private static void initIfNecessary() { + synchronized (versionList) { + if (versionList.get() == null) { + init(); + } + } + } + + + public static void setRawData(MinecraftVersionList data) { + versionList.set(data); + } + public static MinecraftVersionList getRawData() { + initIfNecessary(); + return versionList.get(); + } + + private static void init() { + // try online source first + try { + HttpResponse response = HttpClient.newHttpClient() + .send(HttpRequest.newBuilder(URI.create(ONLINE_DATA_URL)).build(), + BodyHandlers.ofString() + ); + if (response.statusCode() == 200) { + MinecraftVersionList data = Json.gson.fromJson(response.body(), MinecraftVersionList.class); + versionList.set(data); + } + } catch (Exception e) { + Log.warning(e); + } + + if (versionList.get() != null) { + return; + } + + Log.warning("Unable to get minecraft version data from API. Using local data instead."); + // try local source + try (InputStream is = ProtocolVersion.class.getResourceAsStream("mcversion.json"); + InputStreamReader isr = new InputStreamReader(is)) { + MinecraftVersionList data = Json.gson.fromJson(isr, MinecraftVersionList.class); + versionList.set(data); + } catch (Exception e) { + Log.severe("Unable to get Minecraft versions data from classpath. Using empty data instead."); + versionList.set(new MinecraftVersionList()); + } + + } + + + + + public static int ofVersion(String versionId) { + initIfNecessary(); + Integer v = versionList.get().protocolOfVersion().get(versionId); + return v == null ? -1 : v; + } + + + public static List getVersionsOfProtocol(int protocolVersion) { + initIfNecessary(); + return versionList.get().versionsOfProtocol().get(protocolVersion); + } + + +} diff --git a/pandalib-util/src/main/java/fr/pandacube/lib/util/MinecraftVersion.java b/pandalib-util/src/main/java/fr/pandacube/lib/util/MinecraftVersion.java index 3883d53..5065e2a 100644 --- a/pandalib-util/src/main/java/fr/pandacube/lib/util/MinecraftVersion.java +++ b/pandalib-util/src/main/java/fr/pandacube/lib/util/MinecraftVersion.java @@ -15,15 +15,17 @@ import java.util.Set; * "1.16-1.16.3", "1.8.x and 1.9", "1.18.1 or 1.18.2") *

* Note that this enum uses one value to represent every Minecraft version using the same protocol version number. + * @deprecated This class may not be updated. Use the class ProtocolVersion in pandalib-core module instead. */ +@Deprecated public enum MinecraftVersion { /** Minecraft versions 1.7.2 to 1.7.5, protocol version 4. */ - v1_7_2_to_1_7_5(4, "1.7.2-1.7.5"), + v1_7_2_to_1_7_5(4, "1.7.2", "1.7.3", "1.7.4", "1.7.5"), /** Minecraft versions 1.7.6 to 1.7.10, protocol version 5. */ - v1_7_6_to_1_7_10(5, "1.7.6-1.7.10"), + v1_7_6_to_1_7_10(5, "1.7.6", "1.7.7", "1.7.8", "1.7.9", "1.7.10"), /** Minecraft versions 1.8.x, protocol version 47. */ - v1_8(47, "1.8.x"), + v1_8(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"), /** Minecraft version 1.9, protocol version 107. */ v1_9(107, "1.9"), @@ -35,7 +37,7 @@ public enum MinecraftVersion { v1_9_3_to_1_9_4(110, "1.9.3", "1.9.4"), /** Minecraft versions 1.10.x, protocol version 210. */ - v1_10(210, "1.10.x"), + v1_10(210, "1.10", "1.10.1", "1.10.2"), /** Minecraft version 1.11, protocol version 315. */ v1_11(315, "1.11"),