更新 GameVersionNumber 解析规则 (#4917)
1. 支持 [Minecraft 新版本号方案](https://www.minecraft.net/en-us/article/minecraft-new-version-numbering-system) 2. 支持对版本号进行归一化处理。
This commit is contained in:
@@ -24,6 +24,7 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||
import javafx.scene.image.Image;
|
||||
import org.jackhuang.hmcl.util.io.*;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -48,7 +49,7 @@ public final class World {
|
||||
private final Path file;
|
||||
private String fileName;
|
||||
private String worldName;
|
||||
private String gameVersion;
|
||||
private GameVersionNumber gameVersion;
|
||||
private long lastPlayed;
|
||||
private Image icon;
|
||||
private Long seed;
|
||||
@@ -108,7 +109,7 @@ public final class World {
|
||||
return lastPlayed;
|
||||
}
|
||||
|
||||
public String getGameVersion() {
|
||||
public @Nullable GameVersionNumber getGameVersion() {
|
||||
return gameVersion;
|
||||
}
|
||||
|
||||
@@ -188,7 +189,7 @@ public final class World {
|
||||
CompoundTag version = data.get("Version");
|
||||
|
||||
if (version.get("Name") instanceof StringTag)
|
||||
gameVersion = version.<StringTag>get("Name").getValue();
|
||||
gameVersion = GameVersionNumber.asGameVersion(version.<StringTag>get("Name").getValue());
|
||||
}
|
||||
|
||||
Tag worldGenSettings = data.get("WorldGenSettings");
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.versioning;
|
||||
|
||||
import org.intellij.lang.annotations.MagicConstant;
|
||||
import org.jackhuang.hmcl.util.ToStringBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
@@ -28,6 +28,8 @@ import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
/**
|
||||
* @author Glavo
|
||||
*/
|
||||
@@ -38,6 +40,10 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
}
|
||||
|
||||
public static GameVersionNumber asGameVersion(String version) {
|
||||
GameVersionNumber versionNumber = Versions.SPECIALS.get(version);
|
||||
if (versionNumber != null)
|
||||
return versionNumber;
|
||||
|
||||
try {
|
||||
if (!version.isEmpty()) {
|
||||
char ch = version.charAt(0);
|
||||
@@ -53,20 +59,15 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
if (version.equals("0.0"))
|
||||
return Release.ZERO;
|
||||
|
||||
if (version.startsWith("1."))
|
||||
return Release.parse(version);
|
||||
if (version.length() >= 6 && version.charAt(2) == 'w')
|
||||
return LegacySnapshot.parse(version);
|
||||
|
||||
if (version.length() == 6 && version.charAt(2) == 'w')
|
||||
return Snapshot.parse(version);
|
||||
return Release.parse(version);
|
||||
}
|
||||
} catch (IllegalArgumentException ignore) {
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
|
||||
Special special = Versions.SPECIALS.get(version);
|
||||
if (special == null) {
|
||||
special = new Special(version);
|
||||
}
|
||||
return special;
|
||||
return new Special(version, version);
|
||||
}
|
||||
|
||||
public static GameVersionNumber asGameVersion(Optional<String> version) {
|
||||
@@ -94,17 +95,22 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
}
|
||||
|
||||
final String value;
|
||||
final String normalized;
|
||||
|
||||
GameVersionNumber(String value) {
|
||||
GameVersionNumber(String value, String normalized) {
|
||||
this.value = value;
|
||||
this.normalized = normalized;
|
||||
}
|
||||
|
||||
public boolean isAprilFools() {
|
||||
if (this instanceof Special && !value.endsWith("_unobfuscated"))
|
||||
return true;
|
||||
if (this instanceof Special) {
|
||||
String normalizedVersion = this.toNormalizedString();
|
||||
return !normalizedVersion.startsWith("1.") && !normalizedVersion.equals("13w12~")
|
||||
|| normalizedVersion.equals("1.RV-Pre1");
|
||||
}
|
||||
|
||||
if (this instanceof Snapshot snapshot) {
|
||||
return snapshot.intValue == Snapshot.toInt(15, 14, 'a');
|
||||
if (this instanceof LegacySnapshot snapshot) {
|
||||
return snapshot.intValue == LegacySnapshot.toInt(15, 14, 'a', false);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -144,10 +150,10 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
///
|
||||
/// ```java
|
||||
/// GameVersionNumber.asVersion("...").isAtLeast("1.13", "17w43a");
|
||||
/// ```
|
||||
///```
|
||||
///
|
||||
/// @param strictReleaseVersion When `strictReleaseVersion` is `false`, `releaseVersion` is considered less than
|
||||
/// its corresponding pre/rc versions.
|
||||
/// its corresponding pre/rc versions.
|
||||
public boolean isAtLeast(@NotNull String releaseVersion, @NotNull String snapshotVersion, boolean strictReleaseVersion) {
|
||||
if (this instanceof Release self) {
|
||||
Release other;
|
||||
@@ -159,17 +165,35 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
|
||||
return self.compareToRelease(other) >= 0;
|
||||
} else {
|
||||
return this.compareTo(Snapshot.parse(snapshotVersion)) >= 0;
|
||||
return this.compareTo(LegacySnapshot.parse(snapshotVersion)) >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
public String toNormalizedString() {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
protected ToStringBuilder buildDebugString() {
|
||||
return new ToStringBuilder(this)
|
||||
.append("value", value)
|
||||
.append("normalized", normalized)
|
||||
.append("type", getType());
|
||||
}
|
||||
|
||||
public final String toDebugString() {
|
||||
return buildDebugString().toString();
|
||||
}
|
||||
|
||||
public static final class Old extends GameVersionNumber {
|
||||
static Old parse(String value) {
|
||||
if (value.isEmpty())
|
||||
throw new IllegalArgumentException("Empty old version number");
|
||||
|
||||
Type type;
|
||||
int prefixLength = 1;
|
||||
switch (value.charAt(0)) {
|
||||
@@ -215,7 +239,7 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
final VersionNumber versionNumber;
|
||||
|
||||
private Old(String value, Type type, VersionNumber versionNumber) {
|
||||
super(value);
|
||||
super(value, value);
|
||||
this.type = type;
|
||||
this.versionNumber = versionNumber;
|
||||
}
|
||||
@@ -231,83 +255,137 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
return o instanceof Old other && type == other.type && this.versionNumber.compareTo(other.versionNumber) == 0;
|
||||
public int hashCode() {
|
||||
return Objects.hash(type, versionNumber);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(type, versionNumber.hashCode());
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof Old that
|
||||
&& this.type == that.type
|
||||
&& this.versionNumber.equals(that.versionNumber);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Release extends GameVersionNumber {
|
||||
private static final int MINIMUM_YEAR_MAJOR_VERSION = 25;
|
||||
|
||||
private static final Pattern PATTERN = Pattern.compile("1\\.(?<minor>[0-9]+)(\\.(?<patch>[0-9]+))?((?<eaType>(-[a-zA-Z]+| Pre-Release ))(?<eaVersion>.+))?");
|
||||
public enum ReleaseType {
|
||||
UNKNOWN(""),
|
||||
SNAPSHOT("-snapshot-"),
|
||||
PRE_RELEASE("-pre"),
|
||||
RELEASE_CANDIDATE("-rc"),
|
||||
GA("");
|
||||
private final String infix;
|
||||
|
||||
public static final int TYPE_GA = Integer.MAX_VALUE;
|
||||
ReleaseType(String infix) {
|
||||
this.infix = infix;
|
||||
}
|
||||
}
|
||||
|
||||
public static final int TYPE_UNKNOWN = 0;
|
||||
public static final int TYPE_EXP = 1;
|
||||
public static final int TYPE_PRE = 2;
|
||||
public static final int TYPE_RC = 3;
|
||||
public enum Additional {
|
||||
NONE(""), UNOBFUSCATED("_unobfuscated");
|
||||
private final String suffix;
|
||||
|
||||
static final Release ZERO = new Release("0.0", 0, 0, 0, TYPE_GA, VersionNumber.ZERO);
|
||||
Additional(String suffix) {
|
||||
this.suffix = suffix;
|
||||
}
|
||||
}
|
||||
|
||||
static final Release ZERO = new Release(
|
||||
"0.0", "0.0",
|
||||
0, 0, 0,
|
||||
ReleaseType.UNKNOWN, VersionNumber.ZERO, Additional.NONE
|
||||
);
|
||||
|
||||
private static final Pattern VERSION_PATTERN = Pattern.compile("(?<prefix>(?<major>1|[1-9]\\d+)\\.(?<minor>\\d+)(\\.(?<patch>[0-9]+))?)(?<suffix>.*)");
|
||||
|
||||
static Release parse(String value) {
|
||||
Matcher matcher = PATTERN.matcher(value);
|
||||
Matcher matcher = VERSION_PATTERN.matcher(value);
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalArgumentException(value);
|
||||
}
|
||||
|
||||
int major = Integer.parseInt(matcher.group("major"));
|
||||
if (major != 1 && major < MINIMUM_YEAR_MAJOR_VERSION)
|
||||
throw new IllegalArgumentException(value);
|
||||
|
||||
int minor = Integer.parseInt(matcher.group("minor"));
|
||||
|
||||
String patchString = matcher.group("patch");
|
||||
int patch = patchString != null ? Integer.parseInt(patchString) : 0;
|
||||
|
||||
String eaTypeString = matcher.group("eaType");
|
||||
int eaType;
|
||||
if (eaTypeString == null) {
|
||||
eaType = TYPE_GA;
|
||||
} else if ("-pre".equals(eaTypeString) || " Pre-Release ".equals(eaTypeString)) {
|
||||
eaType = TYPE_PRE;
|
||||
} else if ("-rc".equals(eaTypeString)) {
|
||||
eaType = TYPE_RC;
|
||||
} else if ("-exp".equals(eaTypeString)) {
|
||||
eaType = TYPE_EXP;
|
||||
String suffix = matcher.group("suffix");
|
||||
|
||||
ReleaseType releaseType;
|
||||
VersionNumber eaVersion;
|
||||
Additional additional = Additional.NONE;
|
||||
boolean needNormalize = false;
|
||||
|
||||
if (suffix.endsWith("_unobfuscated")) {
|
||||
suffix = suffix.substring(0, suffix.length() - "_unobfuscated".length());
|
||||
additional = Additional.UNOBFUSCATED;
|
||||
} else if (suffix.endsWith(" Unobfuscated")) {
|
||||
needNormalize = true;
|
||||
suffix = suffix.substring(0, suffix.length() - " Unobfuscated".length());
|
||||
additional = Additional.UNOBFUSCATED;
|
||||
}
|
||||
|
||||
if (suffix.isEmpty()) {
|
||||
releaseType = ReleaseType.GA;
|
||||
eaVersion = VersionNumber.ZERO;
|
||||
} else if (suffix.startsWith("-snapshot-")) {
|
||||
releaseType = ReleaseType.SNAPSHOT;
|
||||
eaVersion = VersionNumber.asVersion(suffix.substring("-snapshot-".length()));
|
||||
} else if (suffix.startsWith("-pre")) {
|
||||
releaseType = ReleaseType.PRE_RELEASE;
|
||||
eaVersion = VersionNumber.asVersion(suffix.substring("-pre".length()));
|
||||
} else if (suffix.startsWith(" Pre-Release ")) {
|
||||
needNormalize = true;
|
||||
releaseType = ReleaseType.PRE_RELEASE;
|
||||
eaVersion = VersionNumber.asVersion(suffix.substring(" Pre-Release ".length()));
|
||||
} else if (suffix.startsWith("-rc")) {
|
||||
releaseType = ReleaseType.RELEASE_CANDIDATE;
|
||||
eaVersion = VersionNumber.asVersion(suffix.substring("-rc".length()));
|
||||
} else if (suffix.startsWith(" Release Candidate ")) {
|
||||
needNormalize = true;
|
||||
releaseType = ReleaseType.RELEASE_CANDIDATE;
|
||||
eaVersion = VersionNumber.asVersion(suffix.substring(" Release Candidate ".length()));
|
||||
} else {
|
||||
eaType = TYPE_UNKNOWN;
|
||||
throw new IllegalArgumentException(value);
|
||||
}
|
||||
|
||||
String eaVersionString = matcher.group("eaVersion");
|
||||
VersionNumber eaVersion = eaVersionString != null ? VersionNumber.asVersion(eaVersionString) : VersionNumber.ZERO;
|
||||
|
||||
return new Release(value, 1, minor, patch, eaType, eaVersion);
|
||||
}
|
||||
|
||||
private static int getNumberLength(String value, int offset) {
|
||||
int current = offset;
|
||||
while (current < value.length()) {
|
||||
char ch = value.charAt(current);
|
||||
if (ch < '0' || ch > '9')
|
||||
break;
|
||||
|
||||
current++;
|
||||
String normalized;
|
||||
if (needNormalize) {
|
||||
StringBuilder builder = new StringBuilder(value.length());
|
||||
builder.append(matcher.group("prefix"));
|
||||
if (releaseType != ReleaseType.GA) {
|
||||
builder.append(releaseType.infix);
|
||||
builder.append(eaVersion);
|
||||
}
|
||||
builder.append(additional.suffix);
|
||||
normalized = builder.toString();
|
||||
} else {
|
||||
normalized = value;
|
||||
}
|
||||
|
||||
return current - offset;
|
||||
return new Release(value, normalized, major, minor, patch, releaseType, eaVersion, additional);
|
||||
}
|
||||
|
||||
/// Quickly parses a simple format (`1\.[0-9]+(\.[0-9]+)?`) release version.
|
||||
/// The returned [#eaType] will be set to [#TYPE_UNKNOWN], meaning it will be less than all pre/rc and official versions of this version.
|
||||
/// Quickly parses a simple format (`[1-9][0-9]+\.[0-9]+(\.[0-9]+)?`) release version.
|
||||
/// The returned [#eaType] will be set to [ReleaseType#UNKNOWN], meaning it will be less than all pre/rc and official versions of this version.
|
||||
///
|
||||
/// @see GameVersionNumber#isAtLeast(String, String)
|
||||
static Release parseSimple(String value) {
|
||||
if (!value.startsWith("1."))
|
||||
int majorLength = getNumberLength(value, 0);
|
||||
if (majorLength == 0 || value.length() < majorLength + 2 || value.charAt(majorLength) != '.')
|
||||
throw new IllegalArgumentException(value);
|
||||
|
||||
final int minorOffset = 2;
|
||||
int major = Integer.parseInt(value.substring(0, majorLength));
|
||||
if (major != 1 && major < MINIMUM_YEAR_MAJOR_VERSION)
|
||||
throw new IllegalArgumentException(value);
|
||||
|
||||
final int minorOffset = majorLength + 1;
|
||||
|
||||
int minorLength = getNumberLength(value, minorOffset);
|
||||
if (minorLength == 0)
|
||||
@@ -326,27 +404,41 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
patch = Integer.parseInt(value.substring(patchOffset));
|
||||
}
|
||||
|
||||
return new Release(value, 1, minor, patch, TYPE_UNKNOWN, VersionNumber.ZERO);
|
||||
return new Release(value, value, major, minor, patch, ReleaseType.UNKNOWN, VersionNumber.ZERO, Additional.NONE);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException(value);
|
||||
}
|
||||
}
|
||||
|
||||
private static int getNumberLength(String value, int offset) {
|
||||
int current = offset;
|
||||
while (current < value.length()) {
|
||||
char ch = value.charAt(current);
|
||||
if (ch < '0' || ch > '9')
|
||||
break;
|
||||
|
||||
current++;
|
||||
}
|
||||
|
||||
return current - offset;
|
||||
}
|
||||
|
||||
private final int major;
|
||||
private final int minor;
|
||||
private final int patch;
|
||||
|
||||
@MagicConstant(intValues = {TYPE_GA, TYPE_UNKNOWN, TYPE_EXP, TYPE_PRE, TYPE_RC})
|
||||
private final int eaType;
|
||||
private final ReleaseType eaType;
|
||||
private final VersionNumber eaVersion;
|
||||
private final Additional additional;
|
||||
|
||||
Release(String value, int major, int minor, int patch, int eaType, VersionNumber eaVersion) {
|
||||
super(value);
|
||||
Release(String value, String normalized, int major, int minor, int patch, ReleaseType eaType, VersionNumber eaVersion, Additional additional) {
|
||||
super(value, normalized);
|
||||
this.major = major;
|
||||
this.minor = minor;
|
||||
this.patch = patch;
|
||||
this.eaType = eaType;
|
||||
this.eaVersion = eaVersion;
|
||||
this.additional = additional;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -367,35 +459,45 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
if (c != 0)
|
||||
return c;
|
||||
|
||||
c = Integer.compare(this.eaType, other.eaType);
|
||||
c = this.eaType.compareTo(other.eaType);
|
||||
if (c != 0)
|
||||
return c;
|
||||
|
||||
return this.eaVersion.compareTo(other.eaVersion);
|
||||
c = this.eaVersion.compareTo(other.eaVersion);
|
||||
if (c != 0)
|
||||
return c;
|
||||
|
||||
return this.additional.compareTo(other.additional);
|
||||
}
|
||||
|
||||
int compareToSnapshot(Snapshot other) {
|
||||
int idx = Arrays.binarySearch(Versions.SNAPSHOT_INTS, other.intValue);
|
||||
if (idx >= 0)
|
||||
return this.compareToRelease(Versions.SNAPSHOT_PREV[idx]) <= 0 ? -1 : 1;
|
||||
|
||||
idx = -(idx + 1);
|
||||
if (idx == Versions.SNAPSHOT_INTS.length)
|
||||
int compareToSnapshot(LegacySnapshot other) {
|
||||
if (major == 0) {
|
||||
return -1;
|
||||
} else if (major == 1) {
|
||||
int idx = Arrays.binarySearch(Versions.SNAPSHOT_INTS, other.intValue);
|
||||
if (idx >= 0)
|
||||
return this.compareToRelease(Versions.SNAPSHOT_PREV[idx]) <= 0 ? -1 : 1;
|
||||
|
||||
return this.compareToRelease(Versions.SNAPSHOT_PREV[idx]) <= 0 ? -1 : 1;
|
||||
idx = -(idx + 1);
|
||||
if (idx == Versions.SNAPSHOT_INTS.length)
|
||||
return -1;
|
||||
|
||||
return this.compareToRelease(Versions.SNAPSHOT_PREV[idx]) <= 0 ? -1 : 1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
int compareToImpl(@NotNull GameVersionNumber other) {
|
||||
if (other instanceof Release)
|
||||
return compareToRelease((Release) other);
|
||||
if (other instanceof Release release)
|
||||
return compareToRelease(release);
|
||||
|
||||
if (other instanceof Snapshot)
|
||||
return compareToSnapshot((Snapshot) other);
|
||||
if (other instanceof LegacySnapshot snapshot)
|
||||
return compareToSnapshot(snapshot);
|
||||
|
||||
if (other instanceof Special)
|
||||
return -((Special) other).compareToReleaseOrSnapshot(this);
|
||||
if (other instanceof Special special)
|
||||
return -special.compareToReleaseOrSnapshot(this);
|
||||
|
||||
throw new AssertionError(other.getClass());
|
||||
}
|
||||
@@ -412,7 +514,7 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
return patch;
|
||||
}
|
||||
|
||||
public int getEaType() {
|
||||
public ReleaseType getEaType() {
|
||||
return eaType;
|
||||
}
|
||||
|
||||
@@ -420,28 +522,65 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
return eaVersion;
|
||||
}
|
||||
|
||||
public Additional getAdditional() {
|
||||
return additional;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(major, minor, patch, eaType, eaVersion);
|
||||
return Objects.hash(major, minor, patch, eaType, eaVersion, additional);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
return o instanceof Release other
|
||||
&& major == other.major
|
||||
&& minor == other.minor
|
||||
&& patch == other.patch
|
||||
&& eaType == other.eaType
|
||||
&& eaVersion.equals(other.eaVersion);
|
||||
return o instanceof Release that
|
||||
&& this.major == that.major
|
||||
&& this.minor == that.minor
|
||||
&& this.patch == that.patch
|
||||
&& this.eaType == that.eaType
|
||||
&& this.eaVersion.equals(that.eaVersion)
|
||||
&& this.additional == that.additional;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ToStringBuilder buildDebugString() {
|
||||
return super.buildDebugString()
|
||||
.append("major", major)
|
||||
.append("minor", minor)
|
||||
.append("patch", patch)
|
||||
.append("eaType", eaType)
|
||||
.append("eaVersion", eaVersion)
|
||||
.append("additional", additional);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Snapshot extends GameVersionNumber {
|
||||
static Snapshot parse(String value) {
|
||||
if (value.length() != 6 || value.charAt(2) != 'w')
|
||||
/// Legacy snapshot version numbers like `25w46a`.
|
||||
public static final class LegacySnapshot extends GameVersionNumber {
|
||||
static LegacySnapshot parse(String value) {
|
||||
if (value.length() < 6 || value.charAt(2) != 'w')
|
||||
throw new IllegalArgumentException(value);
|
||||
|
||||
int prefixLength;
|
||||
boolean unobfuscated;
|
||||
String normalized;
|
||||
if (value.endsWith("_unobfuscated")) {
|
||||
prefixLength = value.length() - "_unobfuscated".length();
|
||||
unobfuscated = true;
|
||||
normalized = value;
|
||||
} else if (value.endsWith(" Unobfuscated")) {
|
||||
prefixLength = value.length() - " Unobfuscated".length();
|
||||
unobfuscated = true;
|
||||
normalized = value.substring(0, prefixLength) + "_unobfuscated";
|
||||
} else {
|
||||
prefixLength = value.length();
|
||||
unobfuscated = false;
|
||||
normalized = value;
|
||||
}
|
||||
|
||||
if (prefixLength != 6) {
|
||||
throw new IllegalArgumentException(value);
|
||||
}
|
||||
|
||||
int year;
|
||||
int week;
|
||||
try {
|
||||
@@ -452,21 +591,21 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
}
|
||||
|
||||
char suffix = value.charAt(5);
|
||||
if ((suffix < 'a' || suffix > 'z') && suffix != '~')
|
||||
if (suffix < 'a' || suffix > 'z')
|
||||
throw new IllegalArgumentException(value);
|
||||
|
||||
return new Snapshot(value, year, week, suffix);
|
||||
return new LegacySnapshot(value, normalized, year, week, suffix, unobfuscated);
|
||||
}
|
||||
|
||||
static int toInt(int year, int week, char suffix) {
|
||||
return (year << 16) | (week << 8) | suffix;
|
||||
static int toInt(int year, int week, char suffix, boolean unobfuscated) {
|
||||
return (year << 24) | (week << 16) | (suffix << 8) | (unobfuscated ? 1 : 0);
|
||||
}
|
||||
|
||||
final int intValue;
|
||||
|
||||
Snapshot(String value, int year, int week, char suffix) {
|
||||
super(value);
|
||||
this.intValue = toInt(year, week, suffix);
|
||||
LegacySnapshot(String value, String normalized, int year, int week, char suffix, boolean unobfuscated) {
|
||||
super(value, normalized);
|
||||
this.intValue = toInt(year, week, suffix, unobfuscated);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -476,40 +615,52 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
|
||||
@Override
|
||||
int compareToImpl(@NotNull GameVersionNumber other) {
|
||||
if (other instanceof Release)
|
||||
return -((Release) other).compareToSnapshot(this);
|
||||
if (other instanceof Release otherRelease)
|
||||
return -otherRelease.compareToSnapshot(this);
|
||||
|
||||
if (other instanceof Snapshot)
|
||||
return Integer.compare(this.intValue, ((Snapshot) other).intValue);
|
||||
if (other instanceof LegacySnapshot otherSnapshot)
|
||||
return Integer.compare(this.intValue, otherSnapshot.intValue);
|
||||
|
||||
if (other instanceof Special)
|
||||
return -((Special) other).compareToReleaseOrSnapshot(this);
|
||||
if (other instanceof Special otherSpecial)
|
||||
return -otherSpecial.compareToReleaseOrSnapshot(this);
|
||||
|
||||
throw new AssertionError(other.getClass());
|
||||
}
|
||||
|
||||
public int getYear() {
|
||||
return (intValue >> 16) & 0xff;
|
||||
return (intValue >> 24) & 0xff;
|
||||
}
|
||||
|
||||
public int getWeek() {
|
||||
return (intValue >> 8) & 0xff;
|
||||
return (intValue >> 16) & 0xff;
|
||||
}
|
||||
|
||||
public char getSuffix() {
|
||||
return (char) (intValue & 0xff);
|
||||
return (char) ((intValue >> 8) & 0xff);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
return o instanceof Snapshot other && this.intValue == other.intValue;
|
||||
public boolean isUnobfuscated() {
|
||||
return (intValue & 0b00000001) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return intValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof LegacySnapshot that && this.intValue == that.intValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ToStringBuilder buildDebugString() {
|
||||
return super.buildDebugString()
|
||||
.append("year", getYear())
|
||||
.append("week", getWeek())
|
||||
.append("suffix", getSuffix())
|
||||
.append("unobfuscated", isUnobfuscated());
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Special extends GameVersionNumber {
|
||||
@@ -517,8 +668,8 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
|
||||
private GameVersionNumber prev;
|
||||
|
||||
Special(String value) {
|
||||
super(value);
|
||||
Special(String value, String normalized) {
|
||||
super(value, normalized);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -534,13 +685,13 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
if (versionNumber != null)
|
||||
return versionNumber;
|
||||
|
||||
return versionNumber = VersionNumber.asVersion(value);
|
||||
return versionNumber = VersionNumber.asVersion(normalized);
|
||||
}
|
||||
|
||||
GameVersionNumber getPrevNormalVersion() {
|
||||
GameVersionNumber v = prev;
|
||||
while (v instanceof Special) {
|
||||
v = ((Special) v).prev;
|
||||
while (v instanceof Special special) {
|
||||
v = special.prev;
|
||||
}
|
||||
|
||||
if (v == null) throw new AssertionError("version: " + value);
|
||||
@@ -567,7 +718,7 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
if (other.isUnknown())
|
||||
return -1;
|
||||
|
||||
if (this.value.equals(other.value))
|
||||
if (this.normalized.equals(other.normalized))
|
||||
return 0;
|
||||
|
||||
int c = this.getPrevNormalVersion().compareTo(other.getPrevNormalVersion());
|
||||
@@ -575,11 +726,11 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
return c;
|
||||
|
||||
GameVersionNumber v = prev;
|
||||
while (v instanceof Special) {
|
||||
while (v instanceof Special special) {
|
||||
if (v == other)
|
||||
return 1;
|
||||
|
||||
v = ((Special) v).prev;
|
||||
v = special.prev;
|
||||
}
|
||||
|
||||
return -1;
|
||||
@@ -587,27 +738,23 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
|
||||
@Override
|
||||
int compareToImpl(@NotNull GameVersionNumber o) {
|
||||
if (o instanceof Release)
|
||||
if (o instanceof Release || o instanceof LegacySnapshot)
|
||||
return compareToReleaseOrSnapshot(o);
|
||||
|
||||
if (o instanceof Snapshot)
|
||||
return compareToReleaseOrSnapshot(o);
|
||||
|
||||
if (o instanceof Special)
|
||||
return compareToSpecial((Special) o);
|
||||
if (o instanceof Special special)
|
||||
return compareToSpecial(special);
|
||||
|
||||
throw new AssertionError(o.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return value.hashCode();
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof Special that && this.normalized.equals(that.normalized);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
return o instanceof Special other && this.value.equals(other.value);
|
||||
public int hashCode() {
|
||||
return normalized.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -621,10 +768,11 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
static {
|
||||
ArrayDeque<String> defaultGameVersions = new ArrayDeque<>(64);
|
||||
|
||||
List<Snapshot> snapshots = new ArrayList<>(1024);
|
||||
List<LegacySnapshot> snapshots = new ArrayList<>(1024);
|
||||
List<Release> snapshotPrev = new ArrayList<>(1024);
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(GameVersionNumber.class.getResourceAsStream("/assets/game/versions.txt"), StandardCharsets.US_ASCII))) {
|
||||
//noinspection DataFlowIssue
|
||||
try (var reader = new BufferedReader(new InputStreamReader(GameVersionNumber.class.getResourceAsStream("/assets/game/versions.txt"), StandardCharsets.US_ASCII))) {
|
||||
Release currentRelease = null;
|
||||
GameVersionNumber prev = null;
|
||||
|
||||
@@ -637,13 +785,13 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
if (currentRelease == null)
|
||||
currentRelease = (Release) version;
|
||||
|
||||
if (version instanceof Snapshot snapshot) {
|
||||
if (version instanceof LegacySnapshot snapshot) {
|
||||
snapshots.add(snapshot);
|
||||
snapshotPrev.add(currentRelease);
|
||||
} else if (version instanceof Release) {
|
||||
currentRelease = (Release) version;
|
||||
} else if (version instanceof Release release) {
|
||||
currentRelease = release;
|
||||
|
||||
if (currentRelease.eaType == Release.TYPE_GA) {
|
||||
if (currentRelease.eaType == Release.ReleaseType.GA) {
|
||||
defaultGameVersions.addFirst(currentRelease.value);
|
||||
}
|
||||
} else if (version instanceof Special special) {
|
||||
@@ -658,6 +806,36 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
//noinspection DataFlowIssue
|
||||
try (var reader = new BufferedReader(new InputStreamReader(GameVersionNumber.class.getResourceAsStream("/assets/game/version-alias.csv"), StandardCharsets.US_ASCII))) {
|
||||
for (String line; (line = reader.readLine()) != null; ) {
|
||||
if (line.isEmpty())
|
||||
continue;
|
||||
|
||||
String[] parts = line.split(",");
|
||||
if (parts.length < 2) {
|
||||
LOG.warning("Invalid line: " + line);
|
||||
continue;
|
||||
}
|
||||
|
||||
String normalized = parts[0];
|
||||
Special normalizedVersion = SPECIALS.get(normalized);
|
||||
if (normalizedVersion == null) {
|
||||
LOG.warning("Unknown special version: " + normalized);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 1; i < parts.length; i++) {
|
||||
String version = parts[i];
|
||||
Special versionNumber = new Special(version, normalized);
|
||||
versionNumber.prev = normalizedVersion.prev;
|
||||
SPECIALS.put(version, versionNumber);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
DEFAULT_GAME_VERSIONS = defaultGameVersions.toArray(new String[0]);
|
||||
|
||||
SNAPSHOT_INTS = new int[snapshots.size()];
|
||||
|
||||
@@ -92,63 +92,63 @@
|
||||
"releaseTime": "2021-07-13T12:54:19+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_16_combat-6",
|
||||
"id": "1.16_combat-6",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_16_combat-6/1_16_combat-6.json",
|
||||
"time": "2020-08-26T06:24:28+00:00",
|
||||
"releaseTime": "2020-08-26T06:24:28+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_16_combat-5",
|
||||
"id": "1.16_combat-5",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_16_combat-5/1_16_combat-5.json",
|
||||
"time": "2020-08-21T09:23:13+00:00",
|
||||
"releaseTime": "2020-08-21T09:23:13+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_16_combat-4",
|
||||
"id": "1.16_combat-4",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_16_combat-4/1_16_combat-4.json",
|
||||
"time": "2020-08-19T11:14:58+00:00",
|
||||
"releaseTime": "2020-08-19T11:14:58+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_16_combat-3",
|
||||
"id": "1.16_combat-3",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_16_combat-3/1_16_combat-3.json",
|
||||
"time": "2020-08-14T09:02:15+00:00",
|
||||
"releaseTime": "2020-08-14T09:02:15+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_16_combat-2",
|
||||
"id": "1.16_combat-2",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_16_combat-2/1_16_combat-2.json",
|
||||
"time": "2020-08-13T11:56:30+00:00",
|
||||
"releaseTime": "2020-08-13T11:56:30+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_16_combat-1",
|
||||
"id": "1.16_combat-1",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_16_combat-1/1_16_combat-1.json",
|
||||
"time": "2020-08-12T14:07:25+00:00",
|
||||
"releaseTime": "2020-08-12T14:07:25+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_16_combat-0",
|
||||
"id": "1.16_combat-0",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_16_combat-0/1_16_combat-0.json",
|
||||
"time": "2020-08-07T10:44:47+00:00",
|
||||
"releaseTime": "2020-08-07T10:44:47+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_15_combat-6",
|
||||
"id": "1.15_combat-6",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_15_combat-6/1_15_combat-6.json",
|
||||
"time": "2020-01-15T09:46:35+00:00",
|
||||
"releaseTime": "2020-01-15T09:46:35+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_15_combat-1",
|
||||
"id": "1.15_combat-1",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_15_combat-1/1_15_combat-1.json",
|
||||
"time": "2019-11-29T15:41:39+00:00",
|
||||
|
||||
20
HMCLCore/src/main/resources/assets/game/version-alias.csv
Normal file
20
HMCLCore/src/main/resources/assets/game/version-alias.csv
Normal file
@@ -0,0 +1,20 @@
|
||||
1.14_combat-212796,1.14.3 - Combat Test
|
||||
1.14_combat-0,Combat Test 2
|
||||
1.14_combat-3,Combat Test 3
|
||||
1.15_combat-1,Combat Test 4
|
||||
1.15_combat-6,Combat Test 5
|
||||
1.16_combat-0,Combat Test 6
|
||||
1.16_combat-1,Combat Test 7
|
||||
1.16_combat-2,Combat Test 7b
|
||||
1.16_combat-3,Combat Test 7c
|
||||
1.16_combat-4,Combat Test 8
|
||||
1.16_combat-5,Combat Test 8b
|
||||
1.16_combat-6,Combat Test 8c
|
||||
1.18_experimental-snapshot-1,1.18 Experimental Snapshot 1
|
||||
1.18_experimental-snapshot-2,1.18 Experimental Snapshot 2
|
||||
1.18_experimental-snapshot-3,1.18 Experimental Snapshot 3
|
||||
1.18_experimental-snapshot-4,1.18 Experimental Snapshot 4
|
||||
1.18_experimental-snapshot-5,1.18 Experimental Snapshot 5
|
||||
1.18_experimental-snapshot-6,1.18 Experimental Snapshot 6
|
||||
1.19_deep_dark_experimental_snapshot-1,Deep Dark Experimental Snapshot 1
|
||||
20w14infinite,20w14~
|
||||
|
@@ -452,24 +452,25 @@
|
||||
3D Shareware v1.34
|
||||
19w14a
|
||||
19w14b
|
||||
1.14 Pre-Release 1
|
||||
1.14 Pre-Release 2
|
||||
1.14 Pre-Release 3
|
||||
1.14 Pre-Release 4
|
||||
1.14 Pre-Release 5
|
||||
1.14-pre1
|
||||
1.14-pre2
|
||||
1.14-pre3
|
||||
1.14-pre4
|
||||
1.14-pre5
|
||||
1.14
|
||||
1.14.1 Pre-Release 1
|
||||
1.14.1 Pre-Release 2
|
||||
1.14.1-pre1
|
||||
1.14.1-pre2
|
||||
1.14.1
|
||||
1.14.2 Pre-Release 1
|
||||
1.14.2 Pre-Release 2
|
||||
1.14.2 Pre-Release 3
|
||||
1.14.2 Pre-Release 4
|
||||
1.14.2-pre1
|
||||
1.14.2-pre2
|
||||
1.14.2-pre3
|
||||
1.14.2-pre4
|
||||
1.14.2
|
||||
1.14.3-pre1
|
||||
1.14.3-pre2
|
||||
1.14.3-pre3
|
||||
1.14.3-pre4
|
||||
1.14_combat-212796
|
||||
1.14.3
|
||||
1.14.4-pre1
|
||||
1.14.4-pre2
|
||||
@@ -479,6 +480,8 @@
|
||||
1.14.4-pre6
|
||||
1.14.4-pre7
|
||||
1.14.4
|
||||
1.14_combat-0
|
||||
1.14_combat-3
|
||||
19w34a
|
||||
19w35a
|
||||
19w36a
|
||||
@@ -497,6 +500,7 @@
|
||||
1.15-pre1
|
||||
1.15-pre2
|
||||
1.15-pre3
|
||||
1.15_combat-1
|
||||
1.15-pre4
|
||||
1.15-pre5
|
||||
1.15-pre6
|
||||
@@ -506,6 +510,7 @@
|
||||
1.15.1
|
||||
1.15.2-pre1
|
||||
1.15.2-pre2
|
||||
1.15_combat-6
|
||||
1.15.2
|
||||
20w06a
|
||||
20w07a
|
||||
@@ -545,9 +550,16 @@
|
||||
1.16.2-pre1
|
||||
1.16.2-pre2
|
||||
1.16.2-pre3
|
||||
1.16_combat-0
|
||||
1.16.2-rc1
|
||||
1.16.2-rc2
|
||||
1.16.2
|
||||
1.16_combat-1
|
||||
1.16_combat-2
|
||||
1.16_combat-3
|
||||
1.16_combat-4
|
||||
1.16_combat-5
|
||||
1.16_combat-6
|
||||
1.16.3-rc1
|
||||
1.16.3
|
||||
1.16.4-pre1
|
||||
@@ -592,6 +604,13 @@
|
||||
1.17.1-rc1
|
||||
1.17.1-rc2
|
||||
1.17.1
|
||||
1.18_experimental-snapshot-1
|
||||
1.18_experimental-snapshot-2
|
||||
1.18_experimental-snapshot-3
|
||||
1.18_experimental-snapshot-4
|
||||
1.18_experimental-snapshot-5
|
||||
1.18_experimental-snapshot-6
|
||||
1.18_experimental-snapshot-7
|
||||
21w37a
|
||||
21w38a
|
||||
21w39a
|
||||
@@ -622,6 +641,7 @@
|
||||
22w05a
|
||||
22w06a
|
||||
22w07a
|
||||
1.19_deep_dark_experimental_snapshot-1
|
||||
1.18.2-pre1
|
||||
1.18.2-pre2
|
||||
1.18.2-pre3
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.io.InputStreamReader;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.jackhuang.hmcl.util.versioning.GameVersionNumber.asGameVersion;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
@@ -34,6 +35,8 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
*/
|
||||
public final class GameVersionNumberTest {
|
||||
|
||||
//region Helpers
|
||||
|
||||
private static List<String> readVersions() {
|
||||
List<String> versions = new ArrayList<>();
|
||||
|
||||
@@ -48,18 +51,8 @@ public final class GameVersionNumberTest {
|
||||
return versions;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortVersions() {
|
||||
List<String> versions = readVersions();
|
||||
List<String> copied = new ArrayList<>(versions);
|
||||
Collections.shuffle(copied, new Random(0));
|
||||
copied.sort(Comparator.comparing(GameVersionNumber::asGameVersion));
|
||||
|
||||
assertIterableEquals(versions, copied);
|
||||
}
|
||||
|
||||
private static String errorMessage(String version1, String version2) {
|
||||
return String.format("version1=%s, version2=%s", version1, version2);
|
||||
private static Supplier<String> errorMessage(GameVersionNumber version1, GameVersionNumber version2) {
|
||||
return () -> "version1=%s, version2=%s".formatted(version1.toDebugString(), version2.toDebugString());
|
||||
}
|
||||
|
||||
private static void assertGameVersionEquals(String version) {
|
||||
@@ -67,25 +60,42 @@ public final class GameVersionNumberTest {
|
||||
}
|
||||
|
||||
private static void assertGameVersionEquals(String version1, String version2) {
|
||||
assertEquals(0, asGameVersion(version1).compareTo(version2), errorMessage(version1, version2));
|
||||
assertEquals(asGameVersion(version1), asGameVersion(version2), errorMessage(version1, version2));
|
||||
}
|
||||
|
||||
private static String toString(GameVersionNumber gameVersionNumber) {
|
||||
return gameVersionNumber.getClass().getSimpleName();
|
||||
GameVersionNumber gameVersion1 = asGameVersion(version1);
|
||||
GameVersionNumber gameVersion2 = asGameVersion(version2);
|
||||
assertEquals(0, gameVersion1.compareTo(gameVersion2), errorMessage(gameVersion1, gameVersion2));
|
||||
assertEquals(0, gameVersion2.compareTo(gameVersion1), errorMessage(gameVersion1, gameVersion2));
|
||||
assertEquals(gameVersion1, gameVersion2, errorMessage(gameVersion1, gameVersion2));
|
||||
assertEquals(gameVersion2, gameVersion1, errorMessage(gameVersion1, gameVersion2));
|
||||
assertEquals(gameVersion1.hashCode(), gameVersion2.hashCode(), errorMessage(gameVersion1, gameVersion2));
|
||||
}
|
||||
|
||||
private static void assertOrder(String... versions) {
|
||||
var gameVersionNumbers = new GameVersionNumber[versions.length];
|
||||
for (int i = 0; i < versions.length; i++) {
|
||||
gameVersionNumbers[i] = asGameVersion(versions[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < versions.length - 1; i++) {
|
||||
GameVersionNumber version1 = asGameVersion(versions[i]);
|
||||
GameVersionNumber version1 = gameVersionNumbers[i];
|
||||
|
||||
for (int j = 0; j < i; j++) {
|
||||
GameVersionNumber version2 = gameVersionNumbers[j];
|
||||
|
||||
assertTrue(version1.compareTo(version2) > 0, errorMessage(version1, version2));
|
||||
assertTrue(version2.compareTo(version1) < 0, errorMessage(version1, version2));
|
||||
assertNotEquals(version1, version2, errorMessage(version1, version2));
|
||||
assertNotEquals(version2, version1, errorMessage(version1, version2));
|
||||
}
|
||||
|
||||
assertGameVersionEquals(versions[i]);
|
||||
|
||||
for (int j = i + 1; j < versions.length; j++) {
|
||||
GameVersionNumber version2 = asGameVersion(versions[j]);
|
||||
GameVersionNumber version2 = gameVersionNumbers[j];
|
||||
|
||||
assertEquals(-1, version1.compareTo(version2), String.format("version1=%s (%s), version2=%s (%s)", versions[i], toString(version1), versions[j], toString(version2)));
|
||||
assertEquals(1, version2.compareTo(version1), String.format("version1=%s (%s), version2=%s (%s)", versions[i], toString(version1), versions[j], toString(version2)));
|
||||
assertTrue(version1.compareTo(version2) < 0, errorMessage(version1, version2));
|
||||
assertTrue(version2.compareTo(version1) > 0, errorMessage(version1, version2));
|
||||
assertNotEquals(version1, version2, errorMessage(version1, version2));
|
||||
assertNotEquals(version2, version1, errorMessage(version1, version2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +110,8 @@ public final class GameVersionNumberTest {
|
||||
assertEquals(VersionNumber.asVersion(versionNumber), old.versionNumber);
|
||||
}
|
||||
|
||||
//endregion Helpers
|
||||
|
||||
private static boolean isAprilFools(String version) {
|
||||
return asGameVersion(version).isAprilFools();
|
||||
}
|
||||
@@ -124,6 +136,31 @@ public final class GameVersionNumberTest {
|
||||
assertFalse(isAprilFools("25w45a_unobfuscated"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortVersions() {
|
||||
List<String> versions = readVersions();
|
||||
|
||||
{
|
||||
List<String> copied = new ArrayList<>(versions);
|
||||
copied.sort(Comparator.comparing(GameVersionNumber::asGameVersion));
|
||||
assertIterableEquals(versions, copied);
|
||||
}
|
||||
|
||||
{
|
||||
List<String> copied = new ArrayList<>(versions);
|
||||
Collections.reverse(copied);
|
||||
copied.sort(Comparator.comparing(GameVersionNumber::asGameVersion));
|
||||
assertIterableEquals(versions, copied);
|
||||
}
|
||||
|
||||
for (int randomSeed = 0; randomSeed < 5; randomSeed++) {
|
||||
List<String> copied = new ArrayList<>(versions);
|
||||
Collections.shuffle(copied, new Random(randomSeed));
|
||||
copied.sort(Comparator.comparing(GameVersionNumber::asGameVersion));
|
||||
assertIterableEquals(versions, copied);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseOld() {
|
||||
assertOldVersion("rd-132211", GameVersionNumber.Type.PRE_CLASSIC, "132211");
|
||||
@@ -136,36 +173,96 @@ public final class GameVersionNumberTest {
|
||||
assertOldVersion("a1.0.13_01-1", GameVersionNumber.Type.ALPHA, "1.0.13_01-1");
|
||||
assertOldVersion("b1.0", GameVersionNumber.Type.BETA, "1.0");
|
||||
assertOldVersion("b1.0_01", GameVersionNumber.Type.BETA, "1.0_01");
|
||||
assertOldVersion("b1.6-tb3", GameVersionNumber.Type.BETA, "1.6-tb3");
|
||||
assertOldVersion("b1.8-pre1-2", GameVersionNumber.Type.BETA, "1.8-pre1-2");
|
||||
assertOldVersion("b1.9-pre1", GameVersionNumber.Type.BETA, "1.9-pre1");
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.Old.parse(""));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.Old.parse("1.21"));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.Old.parse("r-132211"));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.Old.parse("rd-"));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.Old.parse("rd-a"));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.Old.parse("i-20100223"));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.Old.parse("in-"));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.Old.parse("in-a"));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.Old.parse("inf-"));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.Old.parse("inf-a"));
|
||||
}
|
||||
|
||||
private static void testParseLegacySnapshot(int year, int week, char suffix) {
|
||||
String raw = "%02dw%02d%s".formatted(year, week, suffix);
|
||||
var rawVersion = (GameVersionNumber.LegacySnapshot) asGameVersion(raw);
|
||||
assertInstanceOf(GameVersionNumber.LegacySnapshot.class, rawVersion);
|
||||
assertEquals(raw, rawVersion.toString());
|
||||
assertEquals(raw, rawVersion.toNormalizedString());
|
||||
assertEquals(year, rawVersion.getYear());
|
||||
assertEquals(week, rawVersion.getWeek());
|
||||
assertEquals(suffix, rawVersion.getSuffix());
|
||||
assertFalse(rawVersion.isUnobfuscated());
|
||||
|
||||
var unobfuscated = raw + "_unobfuscated";
|
||||
var unobfuscatedVersion = (GameVersionNumber.LegacySnapshot) asGameVersion(unobfuscated);
|
||||
assertInstanceOf(GameVersionNumber.LegacySnapshot.class, rawVersion);
|
||||
assertEquals(unobfuscated, unobfuscatedVersion.toString());
|
||||
assertEquals(unobfuscated, unobfuscatedVersion.toNormalizedString());
|
||||
assertEquals(year, unobfuscatedVersion.getYear());
|
||||
assertEquals(week, unobfuscatedVersion.getWeek());
|
||||
assertEquals(suffix, unobfuscatedVersion.getSuffix());
|
||||
assertTrue(unobfuscatedVersion.isUnobfuscated());
|
||||
|
||||
var unobfuscated2 = raw + " Unobfuscated";
|
||||
var unobfuscatedVersion2 = (GameVersionNumber.LegacySnapshot) asGameVersion(unobfuscated2);
|
||||
assertInstanceOf(GameVersionNumber.LegacySnapshot.class, rawVersion);
|
||||
assertEquals(unobfuscated2, unobfuscatedVersion2.toString());
|
||||
assertEquals(unobfuscated, unobfuscatedVersion2.toNormalizedString());
|
||||
assertEquals(year, unobfuscatedVersion2.getYear());
|
||||
assertEquals(week, unobfuscatedVersion2.getWeek());
|
||||
assertEquals(suffix, unobfuscatedVersion2.getSuffix());
|
||||
assertTrue(unobfuscatedVersion2.isUnobfuscated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseNew() {
|
||||
List<String> versions = readVersions();
|
||||
for (String version : versions) {
|
||||
assertFalse(asGameVersion(version) instanceof GameVersionNumber.Old, "version=" + version);
|
||||
GameVersionNumber gameVersion = asGameVersion(version);
|
||||
assertFalse(gameVersion instanceof GameVersionNumber.Old, "version=" + gameVersion.toDebugString());
|
||||
}
|
||||
|
||||
testParseLegacySnapshot(25, 46, 'a');
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.Release.parse("2.1"));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.LegacySnapshot.parse("1.0"));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.LegacySnapshot.parse("1.100.1"));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.LegacySnapshot.parse("aawbba"));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.LegacySnapshot.parse("13w12A"));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.LegacySnapshot.parse("13w12~"));
|
||||
}
|
||||
|
||||
private static void assertSimpleReleaseVersion(String simpleReleaseVersion, int minor, int patch) {
|
||||
private static void assertSimpleReleaseVersion(String simpleReleaseVersion, int major, int minor, int patch) {
|
||||
GameVersionNumber.Release release = GameVersionNumber.Release.parseSimple(simpleReleaseVersion);
|
||||
assertAll("Assert Simple Release Version " + simpleReleaseVersion,
|
||||
() -> assertEquals(1, release.getMajor()),
|
||||
() -> assertEquals(major, release.getMajor()),
|
||||
() -> assertEquals(minor, release.getMinor()),
|
||||
() -> assertEquals(patch, release.getPatch()),
|
||||
() -> assertEquals(GameVersionNumber.Release.TYPE_UNKNOWN, release.getEaType()),
|
||||
() -> assertEquals(GameVersionNumber.Release.ReleaseType.UNKNOWN, release.getEaType()),
|
||||
() -> assertEquals(VersionNumber.ZERO, release.getEaVersion())
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseSimpleRelease() {
|
||||
assertSimpleReleaseVersion("1.0", 0, 0);
|
||||
assertSimpleReleaseVersion("1.13", 13, 0);
|
||||
assertSimpleReleaseVersion("1.21.8", 21, 8);
|
||||
assertSimpleReleaseVersion("1.0", 1, 0, 0);
|
||||
assertSimpleReleaseVersion("1.13", 1, 13, 0);
|
||||
assertSimpleReleaseVersion("1.21.8", 1, 21, 8);
|
||||
assertSimpleReleaseVersion("26.1", 26, 1, 0);
|
||||
assertSimpleReleaseVersion("26.1.1", 26, 1, 1);
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.Release.parseSimple("26"));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.Release.parseSimple("24.0.0"));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.Release.parseSimple("24.0"));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.Release.parseSimple("2.0"));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.Release.parseSimple("1"));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.Release.parseSimple("1..0"));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.Release.parseSimple("1.0."));
|
||||
assertThrows(IllegalArgumentException.class, () -> GameVersionNumber.Release.parseSimple("1.a"));
|
||||
@@ -186,13 +283,13 @@ public final class GameVersionNumberTest {
|
||||
"0.0",
|
||||
"1.0",
|
||||
"1.99",
|
||||
"1.99.1-unknown1",
|
||||
"1.99.1-pre1",
|
||||
"1.99.1 Pre-Release 2",
|
||||
"1.99.1-rc1",
|
||||
"1.99.1",
|
||||
"1.100",
|
||||
"1.100.1"
|
||||
"1.100.1",
|
||||
"26.1"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -202,7 +299,6 @@ public final class GameVersionNumberTest {
|
||||
"90w01a",
|
||||
"90w01b",
|
||||
"90w01e",
|
||||
"90w01~",
|
||||
"90w02a"
|
||||
);
|
||||
}
|
||||
@@ -257,6 +353,7 @@ public final class GameVersionNumberTest {
|
||||
"1.14",
|
||||
"1.15.2",
|
||||
"20w06a",
|
||||
"20w13b",
|
||||
"20w14infinite",
|
||||
"20w22a",
|
||||
"1.16-pre1",
|
||||
@@ -273,10 +370,21 @@ public final class GameVersionNumberTest {
|
||||
"24w13a",
|
||||
"24w14potato",
|
||||
"24w14a",
|
||||
"25w45a",
|
||||
"25w45a_unobfuscated",
|
||||
"Unknown",
|
||||
"100.0"
|
||||
"25w46a",
|
||||
"25w46a_unobfuscated",
|
||||
"1.21.11-pre1",
|
||||
"1.21.11-pre1_unobfuscated",
|
||||
"1.21.11-pre2",
|
||||
"1.21.11-pre2_unobfuscated",
|
||||
"99w99a",
|
||||
"26.1-snapshot-1",
|
||||
"26.1-snapshot-2",
|
||||
"26.1",
|
||||
"26.2-snapshot-1",
|
||||
"26.2-snapshot-2",
|
||||
"26.2",
|
||||
"100.0",
|
||||
"Unknown"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -312,26 +420,83 @@ public final class GameVersionNumberTest {
|
||||
);
|
||||
}
|
||||
|
||||
private static void assertNormalized(String normalized, String version) {
|
||||
assertGameVersionEquals(version);
|
||||
assertGameVersionEquals(normalized, version);
|
||||
assertEquals(normalized, asGameVersion(version).toNormalizedString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToNormalizedString() {
|
||||
for (String version : readVersions()) {
|
||||
assertNormalized(version, version);
|
||||
}
|
||||
|
||||
assertNormalized("1.21.11-pre3", "1.21.11 Pre-Release 3");
|
||||
assertNormalized("1.21.11-pre3_unobfuscated", "1.21.11 Pre-Release 3 Unobfuscated");
|
||||
assertNormalized("1.21.11-pre3_unobfuscated", "1.21.11-pre3 Unobfuscated");
|
||||
assertNormalized("1.21.11-rc1", "1.21.11 Release Candidate 1");
|
||||
assertNormalized("1.21.11-rc1_unobfuscated", "1.21.11 Release Candidate 1 Unobfuscated");
|
||||
assertNormalized("1.14_combat-212796", "1.14.3 - Combat Test");
|
||||
assertNormalized("1.14_combat-0", "Combat Test 2");
|
||||
assertNormalized("1.14_combat-3", "Combat Test 3");
|
||||
assertNormalized("1.15_combat-1", "Combat Test 4");
|
||||
assertNormalized("1.15_combat-6", "Combat Test 5");
|
||||
assertNormalized("1.16_combat-0", "Combat Test 6");
|
||||
assertNormalized("1.16_combat-1", "Combat Test 7");
|
||||
assertNormalized("1.16_combat-2", "Combat Test 7b");
|
||||
assertNormalized("1.16_combat-3", "Combat Test 7c");
|
||||
assertNormalized("1.16_combat-4", "Combat Test 8");
|
||||
assertNormalized("1.16_combat-5", "Combat Test 8b");
|
||||
assertNormalized("1.16_combat-6", "Combat Test 8c");
|
||||
assertNormalized("1.18_experimental-snapshot-1", "1.18 Experimental Snapshot 1");
|
||||
assertNormalized("1.18_experimental-snapshot-2", "1.18 Experimental Snapshot 2");
|
||||
assertNormalized("1.18_experimental-snapshot-3", "1.18 Experimental Snapshot 3");
|
||||
assertNormalized("1.18_experimental-snapshot-4", "1.18 Experimental Snapshot 4");
|
||||
assertNormalized("1.18_experimental-snapshot-5", "1.18 Experimental Snapshot 5");
|
||||
assertNormalized("1.18_experimental-snapshot-6", "1.18 Experimental Snapshot 6");
|
||||
assertNormalized("1.19_deep_dark_experimental_snapshot-1", "Deep Dark Experimental Snapshot 1");
|
||||
assertNormalized("20w14infinite", "20w14~");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAtLeast() {
|
||||
assertTrue(asGameVersion("1.13").isAtLeast("1.13", "17w43a"));
|
||||
assertTrue(asGameVersion("1.13.1").isAtLeast("1.13", "17w43a"));
|
||||
assertTrue(asGameVersion("1.14").isAtLeast("1.13", "17w43a"));
|
||||
assertTrue(asGameVersion("1.13-rc1").isAtLeast("1.13", "17w43a"));
|
||||
assertTrue(asGameVersion("1.13-pre1").isAtLeast("1.13", "17w43a"));
|
||||
assertTrue(asGameVersion("17w43a").isAtLeast("1.13", "17w43a"));
|
||||
assertTrue(asGameVersion("17w43b").isAtLeast("1.13", "17w43a"));
|
||||
assertTrue(asGameVersion("17w45a").isAtLeast("1.13", "17w43a"));
|
||||
assertTrue(asGameVersion("1.13").isAtLeast("1.13", "17w43a", true));
|
||||
assertTrue(asGameVersion("1.13").isAtLeast("1.13", "17w43a", false));
|
||||
assertTrue(asGameVersion("1.13.1").isAtLeast("1.13", "17w43a", true));
|
||||
assertTrue(asGameVersion("1.13.1").isAtLeast("1.13", "17w43a", false));
|
||||
assertTrue(asGameVersion("1.14").isAtLeast("1.13", "17w43a", true));
|
||||
assertTrue(asGameVersion("1.14").isAtLeast("1.13", "17w43a", false));
|
||||
assertTrue(asGameVersion("1.13-rc1").isAtLeast("1.13", "17w43a", false));
|
||||
assertTrue(asGameVersion("1.13-pre1").isAtLeast("1.13", "17w43a", false));
|
||||
assertTrue(asGameVersion("17w43a").isAtLeast("1.13", "17w43a", true));
|
||||
assertTrue(asGameVersion("17w43a").isAtLeast("1.13", "17w43a", false));
|
||||
assertTrue(asGameVersion("17w43b").isAtLeast("1.13", "17w43a", true));
|
||||
assertTrue(asGameVersion("17w43b").isAtLeast("1.13", "17w43a", false));
|
||||
assertTrue(asGameVersion("17w45a").isAtLeast("1.13", "17w43a", true));
|
||||
assertTrue(asGameVersion("17w45a").isAtLeast("1.13", "17w43a", false));
|
||||
|
||||
assertFalse(asGameVersion("17w31a").isAtLeast("1.13", "17w43a"));
|
||||
assertFalse(asGameVersion("1.12").isAtLeast("1.13", "17w43a"));
|
||||
assertFalse(asGameVersion("1.12.2").isAtLeast("1.13", "17w43a"));
|
||||
assertFalse(asGameVersion("1.12.2-pre1").isAtLeast("1.13", "17w43a"));
|
||||
assertFalse(asGameVersion("rd-132211").isAtLeast("1.13", "17w43a"));
|
||||
assertFalse(asGameVersion("a1.0.6").isAtLeast("1.13", "17w43a"));
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> asGameVersion("1.13").isAtLeast("17w43a", "17w43a"));
|
||||
assertThrows(IllegalArgumentException.class, () -> asGameVersion("17w43a").isAtLeast("1.13", "1.13"));
|
||||
assertThrows(IllegalArgumentException.class, () -> asGameVersion("17w43a").isAtLeast("1.13", "22w13oneblockatatime"));
|
||||
assertFalse(asGameVersion("1.13-rc1").isAtLeast("1.13", "17w43a", true));
|
||||
assertFalse(asGameVersion("1.13-pre1").isAtLeast("1.13", "17w43a", true));
|
||||
assertFalse(asGameVersion("17w31a").isAtLeast("1.13", "17w43a", true));
|
||||
assertFalse(asGameVersion("17w31a").isAtLeast("1.13", "17w43a", false));
|
||||
assertFalse(asGameVersion("1.12").isAtLeast("1.13", "17w43a", true));
|
||||
assertFalse(asGameVersion("1.12").isAtLeast("1.13", "17w43a", false));
|
||||
assertFalse(asGameVersion("1.12.2").isAtLeast("1.13", "17w43a", true));
|
||||
assertFalse(asGameVersion("1.12.2").isAtLeast("1.13", "17w43a", false));
|
||||
assertFalse(asGameVersion("1.12.2-pre1").isAtLeast("1.13", "17w43a", true));
|
||||
assertFalse(asGameVersion("1.12.2-pre1").isAtLeast("1.13", "17w43a", false));
|
||||
assertFalse(asGameVersion("rd-132211").isAtLeast("1.13", "17w43a", true));
|
||||
assertFalse(asGameVersion("rd-132211").isAtLeast("1.13", "17w43a", false));
|
||||
assertFalse(asGameVersion("a1.0.6").isAtLeast("1.13", "17w43a", true));
|
||||
assertFalse(asGameVersion("a1.0.6").isAtLeast("1.13", "17w43a", false));
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> asGameVersion("1.13").isAtLeast("17w43a", "17w43a", true));
|
||||
assertThrows(IllegalArgumentException.class, () -> asGameVersion("1.13").isAtLeast("17w43a", "17w43a", false));
|
||||
assertThrows(IllegalArgumentException.class, () -> asGameVersion("17w43a").isAtLeast("1.13", "1.13", true));
|
||||
assertThrows(IllegalArgumentException.class, () -> asGameVersion("17w43a").isAtLeast("1.13", "1.13", false));
|
||||
assertThrows(IllegalArgumentException.class, () -> asGameVersion("17w43a").isAtLeast("1.13", "22w13oneblockatatime", true));
|
||||
assertThrows(IllegalArgumentException.class, () -> asGameVersion("17w43a").isAtLeast("1.13", "22w13oneblockatatime", false));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user