Deprecate old MinecraftVersion enum and create new ProtocolVersion class which automatically update the version list on startup

This commit is contained in:
Marc Baloup 2023-06-15 15:08:18 +02:00
parent 436c9b9381
commit edd5b06a46
4 changed files with 220 additions and 4 deletions

View File

@ -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<String, Integer> protocolOfVersion,
Map<Integer, List<String>> versionsOfProtocol
) {
public MinecraftVersionList() {
this(new TreeMap<>(MinecraftVersionUtil::compareVersions), new TreeMap<>());
}
public void add(String versionId, int protocolVersion) {
protocolOfVersion.put(versionId, protocolVersion);
List<String> versions = versionsOfProtocol.computeIfAbsent(protocolVersion, p -> new ArrayList<>());
versions.add(versionId);
versions.sort(MinecraftVersionUtil::compareVersions);
}
}

View File

@ -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<String> versions, String finalWordSeparator) {
if (versions.isEmpty())
return "";
// put them in order and remove duplicates
versions = new ArrayList<>(toOrderedSet(versions));
List<String> 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<String> toOrderedSet(List<String> versions) {
Set<String> set = new TreeSet<>(MinecraftVersionUtil::compareVersions);
set.addAll(versions);
return set;
}
}

View File

@ -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<MinecraftVersionList> 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<String> 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<String> getVersionsOfProtocol(int protocolVersion) {
initIfNecessary();
return versionList.get().versionsOfProtocol().get(protocolVersion);
}
}

View File

@ -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") * "1.16-1.16.3", "1.8.x and 1.9", "1.18.1 or 1.18.2")
* <p> * <p>
* Note that this enum uses one value to represent every Minecraft version using the same protocol version number. * 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 { public enum MinecraftVersion {
/** Minecraft versions 1.7.2 to 1.7.5, protocol version 4. */ /** 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. */ /** 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. */ /** 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. */ /** Minecraft version 1.9, protocol version 107. */
v1_9(107, "1.9"), 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"), v1_9_3_to_1_9_4(110, "1.9.3", "1.9.4"),
/** Minecraft versions 1.10.x, protocol version 210. */ /** 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. */ /** Minecraft version 1.11, protocol version 315. */
v1_11(315, "1.11"), v1_11(315, "1.11"),