233 lines
8.2 KiB
Java
233 lines
8.2 KiB
Java
package fr.pandacube.lib.core.mc_version;
|
|
|
|
import fr.pandacube.lib.core.json.Json;
|
|
import fr.pandacube.lib.util.Log;
|
|
import fr.pandacube.lib.util.StringUtil;
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
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.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.
|
|
* <p>
|
|
* 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.
|
|
* <p>
|
|
* The public static methods are used to fetch an instance of {@link ProtocolVersion}
|
|
* based on the provided protocol version (e.g. 763) or Minecraft version (e.g. "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 implements Comparable<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();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Replace the currently used data cache by a new source.
|
|
* <p>
|
|
* <b>Note: </b>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.
|
|
* <p>
|
|
* <b>Note: </b>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();
|
|
}
|
|
|
|
private static void init() {
|
|
// try online source first
|
|
try {
|
|
HttpResponse<String> response = HttpClient.newBuilder()
|
|
.connectTimeout(Duration.ofSeconds(5))
|
|
.build()
|
|
.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")) {
|
|
if (is != null) {
|
|
try (InputStreamReader isr = new InputStreamReader(is)) {
|
|
MinecraftVersionList data = Json.gson.fromJson(isr, MinecraftVersionList.class);
|
|
versionList.set(data);
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
Log.warning(e);
|
|
}
|
|
|
|
if (versionList.get() != null) {
|
|
return;
|
|
}
|
|
|
|
Log.severe("Unable to get Minecraft versions data from classpath. Using empty data instead.");
|
|
|
|
versionList.set(new MinecraftVersionList());
|
|
}
|
|
|
|
|
|
private static int getPVNOfVersion(String version) {
|
|
initIfNecessary();
|
|
Integer v = versionList.get().protocolOfVersion().get(version);
|
|
return v == null ? -1 : v;
|
|
}
|
|
|
|
private static List<String> getVersionsOfPVN(int pvn) {
|
|
initIfNecessary();
|
|
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<String> 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<String> versions = getVersionsOfPVN(pvn);
|
|
if (versions == null) {
|
|
return null;
|
|
}
|
|
return new ProtocolVersion(pvn, List.copyOf(versions));
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns all the {@link ProtocolVersion} currently known by this class.
|
|
* @return all the {@link ProtocolVersion} currently known by this class.
|
|
*/
|
|
public static List<ProtocolVersion> allKnownProtocolVersions() {
|
|
return versionList.get().versionsOfProtocol().keySet().stream()
|
|
.map(ProtocolVersion::ofProtocol)
|
|
.toList();
|
|
}
|
|
|
|
|
|
/**
|
|
* 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<ProtocolVersion> versions, String finalWordSeparator) {
|
|
return MinecraftVersionUtil.toString(versions.stream().flatMap(pv -> pv.versions.stream()).toList(), finalWordSeparator);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
* The protocol version number.
|
|
*/
|
|
public final int protocolVersionNumber;
|
|
/**
|
|
* All Minecraft version supported by this protocol version number.
|
|
*/
|
|
public final List<String> versions;
|
|
|
|
private ProtocolVersion(int protocolVersionNumber, List<String> 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);
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
return o instanceof ProtocolVersion pv && protocolVersionNumber == pv.protocolVersionNumber;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return protocolVersionNumber;
|
|
}
|
|
|
|
@Override
|
|
public int compareTo(@NotNull ProtocolVersion o) {
|
|
return Integer.compare(protocolVersionNumber, o.protocolVersionNumber);
|
|
}
|
|
}
|