diff --git a/src/main/java/net/ornithemc/meta/data/VersionDatabase.java b/src/main/java/net/ornithemc/meta/data/VersionDatabase.java index 1acf83b..101e707 100644 --- a/src/main/java/net/ornithemc/meta/data/VersionDatabase.java +++ b/src/main/java/net/ornithemc/meta/data/VersionDatabase.java @@ -18,14 +18,12 @@ package net.ornithemc.meta.data; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.ornithemc.meta.OrnitheMeta; -import net.ornithemc.meta.utils.MinecraftLauncherMeta; import net.ornithemc.meta.utils.MavenPomParser; import net.ornithemc.meta.utils.MavenMetadataParser; import net.ornithemc.meta.utils.MavenMetadataParser.StableVersionIdentifier; @@ -43,7 +41,6 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; -@JsonIgnoreProperties({"manifest"}) public class VersionDatabase { public static final String FABRIC_MAVEN_URL = "https://maven.fabricmc.net/"; @@ -162,7 +159,7 @@ private static List oslModules(int generation) { return modules; } - public final VersionManifest manifest; + private final Int2ObjectMap manifests; private final Int2ObjectMap> game; private final Int2ObjectMap> intermediary; private final Int2ObjectMap> feather; @@ -178,7 +175,7 @@ private static List oslModules(int generation) { public List libraryUpgrades; private VersionDatabase() { - this.manifest = new VersionManifest(); + this.manifests = new Int2ObjectOpenHashMap<>(); this.game = new Int2ObjectOpenHashMap<>(); this.intermediary = new Int2ObjectOpenHashMap<>(); this.feather = new Int2ObjectOpenHashMap<>(); @@ -193,6 +190,7 @@ public static VersionDatabase generate() throws Exception { VersionDatabase database = new VersionDatabase(); config = ConfigV3.load(); for (int generation = 1; generation <= config.latestIntermediaryGeneration; generation++) { + database.manifests.put(generation, VersionManifest.forGenSorted(generation)); database.intermediary.put(generation, intermediaryMetadataParser(generation).getVersions(MavenVersion::new)); database.feather.put(generation, featherMetadataParser(generation).getVersions(MavenBuildGameVersion::new)); database.osl.put(generation, oslMetadataParser(generation).getVersions(MavenVersion::new)); @@ -212,7 +210,7 @@ public static VersionDatabase generate() throws Exception { database.sparrow = SPARROW_METADATA_PARSER.getVersions(MavenBuildGameVersion::new); database.nests = NESTS_METADATA_PARSER.getVersions(MavenBuildGameVersion::new); database.installer = INSTALLER_METADATA_PARSER.getVersions(MavenUrlVersion::new); - database.libraryUpgrades = LibraryUpgradesV3.get(); + database.libraryUpgrades = LibraryUpgradesV3.reload(); database.loadMcData(); OrnitheMeta.LOGGER.info("DB update took {}ms", System.currentTimeMillis() - start); return database; @@ -228,21 +226,18 @@ private void loadMcData() throws IOException { throw new RuntimeException("Mappings are empty"); } - Int2ObjectMap launcherMetas = new Int2ObjectOpenHashMap<>(); - for (int generation = 1; generation <= config.latestIntermediaryGeneration; generation++) { final int gen = generation; - final MinecraftLauncherMeta launcherMeta = MinecraftLauncherMeta.getSortedMeta(gen); - launcherMetas.put(generation, launcherMeta); + final VersionManifest manifest = manifests.get(generation); intermediary.compute(generation, (key, value) -> { // Sorts in the order of minecraft release dates value = new ArrayList<>(value); - value.sort(Comparator.comparingInt(o -> launcherMeta.getIndex(o.getVersionNoSide()))); + value.sort(Comparator.comparingInt(o -> manifest.indexOf(o.getVersionNoSide()))); value.forEach(version -> version.setStable(true)); // Remove entries that do not match a valid mc version. value.removeIf(o -> { - if (launcherMeta.getVersions().stream().noneMatch(metaVersion -> metaVersion.getId().equals(o.getVersionNoSide()))) { + if (!manifest.contains(o.getVersionNoSide())) { OrnitheMeta.LOGGER.info("Removing {} from intermediary v3{} as it does not match a mc version", o.getVersion(), (gen < 1 ? "" : " gen" + gen)); return true; } @@ -254,12 +249,12 @@ private void loadMcData() throws IOException { feather.compute(generation, (key, value) -> { // Sorts in the order of minecraft release dates value = new ArrayList<>(value); - value.sort(Comparator.comparingInt(o -> launcherMeta.getIndex(o.getVersionNoSide()))); + value.sort(Comparator.comparingInt(o -> manifest.indexOf(o.getVersionNoSide()))); value.forEach(version -> version.setStable(true)); // Remove entries that do not match a valid mc version. value.removeIf(o -> { - if (launcherMeta.getVersions().stream().noneMatch(metaVersion -> metaVersion.getId().equals(o.getVersionNoSide()))) { + if (!manifest.contains(o.getVersionNoSide())) { OrnitheMeta.LOGGER.info("Removing {} from v3 feather gen{} as it does not match a mc version", o.getGameVersion(), gen); return true; } @@ -276,13 +271,13 @@ private void loadMcData() throws IOException { } } - game.put(generation, minecraftVersions.stream().map(s -> new BaseVersion(s, launcherMeta.isStable(s))).collect(Collectors.toList())); + game.put(generation, minecraftVersions.stream().map(s -> new BaseVersion(s, manifest.isStable(s))).collect(Collectors.toList())); } Function> p = src -> { return o -> { for (int generation = 1; generation <= config.latestIntermediaryGeneration; generation++) { - if (launcherMetas.get(generation).getVersions().stream().anyMatch(metaVersion -> metaVersion.getId().equals(o.getVersionNoSide()))) { + if (manifests.get(generation).contains(o.getVersionNoSide())) { return false; } } @@ -294,9 +289,9 @@ private void loadMcData() throws IOException { int i1 = Integer.MAX_VALUE; int i2 = Integer.MAX_VALUE; for (int generation = 1; generation <= config.latestIntermediaryGeneration; generation++) { - MinecraftLauncherMeta launcherMeta = launcherMetas.get(generation); - i1 = Math.min(i1, launcherMeta.getIndex(v1.getVersionNoSide())); - i2 = Math.min(i2, launcherMeta.getIndex(v2.getVersionNoSide())); + VersionManifest manifest = manifests.get(generation); + i1 = Math.min(i1, manifest.indexOf(v1.getVersionNoSide())); + i2 = Math.min(i2, manifest.indexOf(v2.getVersionNoSide())); } return Integer.compare(i1, i2); }; @@ -316,6 +311,10 @@ private void loadMcData() throws IOException { nests.removeIf(p.apply("v3 nests")); } + public VersionManifest getManifest(int generation) { + return manifests.get(generation); + } + public List getGame(int generation) { return game.get(generation); } diff --git a/src/main/java/net/ornithemc/meta/data/VersionDatabaseOld.java b/src/main/java/net/ornithemc/meta/data/VersionDatabaseOld.java index 18b058c..dcbbb37 100644 --- a/src/main/java/net/ornithemc/meta/data/VersionDatabaseOld.java +++ b/src/main/java/net/ornithemc/meta/data/VersionDatabaseOld.java @@ -19,8 +19,8 @@ package net.ornithemc.meta.data; import net.ornithemc.meta.OrnitheMeta; -import net.ornithemc.meta.utils.MinecraftLauncherMeta; import net.ornithemc.meta.utils.MavenMetadataParser; +import net.ornithemc.meta.utils.VersionManifest; import net.ornithemc.meta.web.models.BaseVersion; import net.ornithemc.meta.web.models.MavenBuildVersion; import net.ornithemc.meta.web.models.MavenUrlVersion; @@ -72,11 +72,11 @@ private void loadMcData() throws IOException { if (calamus == null) { throw new RuntimeException("Mappings are null"); } - MinecraftLauncherMeta launcherMeta = MinecraftLauncherMeta.getSortedMeta(1); + VersionManifest manifest = VersionManifest.forGenSorted(1); //Sorts in the order of minecraft release dates calamus = new ArrayList<>(calamus); - calamus.sort(Comparator.comparingInt(o -> launcherMeta.getIndex(o.getVersion()))); + calamus.sort(Comparator.comparingInt(o -> manifest.indexOf(o.getVersion()))); calamus.forEach(version -> version.setStable(true)); // Remove entries that do not match a valid mc version. @@ -88,7 +88,7 @@ private void loadMcData() throws IOException { iVersion = o.getVersion(); } - if (launcherMeta.getVersions().stream().noneMatch(metaVersion -> metaVersion.getId().equals(iVersion))) { + if (!manifest.contains(iVersion)) { OrnitheMeta.LOGGER.info("Removing {} as it is not match an mc version (v2)", o.getVersion()); return true; } @@ -102,7 +102,7 @@ private void loadMcData() throws IOException { } } - game = minecraftVersions.stream().map(s -> new BaseVersion(s, launcherMeta.isStable(s))).collect(Collectors.toList()); + game = minecraftVersions.stream().map(s -> new BaseVersion(s, manifest.isStable(s))).collect(Collectors.toList()); } public List getLoader() { diff --git a/src/main/java/net/ornithemc/meta/utils/MinecraftLauncherMeta.java b/src/main/java/net/ornithemc/meta/utils/MinecraftLauncherMeta.java deleted file mode 100644 index 94514a9..0000000 --- a/src/main/java/net/ornithemc/meta/utils/MinecraftLauncherMeta.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2019 FabricMC - * - * Modifications copyright (c) 2022 OrnitheMC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.ornithemc.meta.utils; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import net.ornithemc.meta.OrnitheMeta; -import org.apache.commons.io.IOUtils; - -import java.io.IOException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -@JsonIgnoreProperties({"$schema", "latest"}) -public class MinecraftLauncherMeta { - - List versions; - - public MinecraftLauncherMeta(@JsonProperty("versions") List versions) { - this.versions = versions; - } - - public static MinecraftLauncherMeta getMeta(int generation) throws IOException { - String url; - if (generation < 1) { - throw new IllegalArgumentException("invalid generation " + generation); - } else if (generation == 1) { - url = "https://skyrising.github.io/mc-versions/version_manifest.json"; - } else { - url = "https://ornithemc.net/mc-versions/version_manifest.json"; - } - String json = IOUtils.toString(new URL(url), StandardCharsets.UTF_8); - return OrnitheMeta.MAPPER.readValue(json, MinecraftLauncherMeta.class); - } - - public static MinecraftLauncherMeta getSortedMeta(int generation) throws IOException { - List versions = new ArrayList<>(getMeta(generation).versions); - - // Order by release time - versions.sort(Comparator.comparing(Version::getReleaseTime).reversed()); - - return new MinecraftLauncherMeta(versions); - } - - public boolean isStable(String id) { - return versions.stream().anyMatch(version -> version.id.equals(id) && version.type.equals("release")); - } - - public int getIndex(String version) { - for (int i = 0; i < versions.size(); i++) { - if (versions.get(i).id.equals(version)) { - return i; - } - } - return 0; - } - - public List getVersions() { - return Collections.unmodifiableList(versions); - } - - public static class Version { - - String id; - String type; - String url; - String time; - String releaseTime; - String details; - - public String getId() { - return id; - } - - public String getType() { - return type; - } - - public String getUrl() { - return url; - } - - public String getTime() { - return time; - } - - public String getDetails() { - return details; - } - - public String getReleaseTime() { - return releaseTime; - } - } - -} diff --git a/src/main/java/net/ornithemc/meta/utils/VersionManifest.java b/src/main/java/net/ornithemc/meta/utils/VersionManifest.java index 4ade280..b663393 100644 --- a/src/main/java/net/ornithemc/meta/utils/VersionManifest.java +++ b/src/main/java/net/ornithemc/meta/utils/VersionManifest.java @@ -18,36 +18,134 @@ package net.ornithemc.meta.utils; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; import com.vdurmont.semver4j.Semver; + import net.ornithemc.meta.OrnitheMeta; +import org.apache.commons.io.IOUtils; import java.io.IOException; -import java.io.InputStreamReader; import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; +import java.util.List; import java.util.Map; +@JsonIgnoreProperties({"$schema", "latest"}) public class VersionManifest { - private static final String DETAILS_URL = "https://skyrising.github.io/mc-versions/version/%s.json"; + private final List versions; + private final Map details; + + public VersionManifest(@JsonProperty("versions") List versions) { + this.versions = versions; + this.details = new HashMap<>(); + } + + public static VersionManifest forGen(int generation) throws IOException { + String url; + if (generation < 1) { + throw new IllegalArgumentException("invalid generation " + generation); + } else { + url = String.format("https://ornithemc.net/mc-versions/gen%d/version_manifest.json", generation); + } + String json = IOUtils.toString(new URL(url), StandardCharsets.UTF_8); + return OrnitheMeta.MAPPER.readValue(json, VersionManifest.class); + } + + public static VersionManifest forGenSorted(int generation) throws IOException { + List versions = new ArrayList<>(forGen(generation).versions); - private final Map versions; + // Order by release time + versions.sort(Comparator.comparing(Version::releaseTime).reversed()); - public VersionManifest() { - this.versions = new HashMap<>(); + return new VersionManifest(versions); } - public Semver getVersion(String id) { - return versions.computeIfAbsent(id, key -> { - try (InputStreamReader input = new InputStreamReader(new URL(String.format(DETAILS_URL, id)).openStream())) { - JsonNode details = OrnitheMeta.MAPPER.readTree(input); - String normalized = details.get("normalizedVersion").asText(); + private VersionDetails versionDetails(String id) { + return details.computeIfAbsent(id, (key) -> { + int index = indexOf(id); + Version version = versions.get(index); + + if (version == null) { + return null; + } - return new Semver(normalized); + try { + String json = IOUtils.toString(new URL(version.details), StandardCharsets.UTF_8); + return OrnitheMeta.MAPPER.readValue(json, VersionDetails.class); } catch (IOException e) { return null; } }); } + + public boolean contains(String id) { + return versions.stream().anyMatch(version -> version.id.equals(id)); + } + + public int indexOf(String id) { + for (int i = 0; i < versions.size(); i++) { + if (versions.get(i).id.equals(id)) { + return i; + } + } + + return 0; + } + + public boolean isStable(String id) { + return versions.stream().anyMatch(version -> version.id.equals(id) && version.type.equals("release")); + } + + public Semver normalize(String id) { + VersionDetails details = versionDetails(id); + return details == null ? null : new Semver(details.normalizedVersion); + } + + public static class Version { + + public String id; + public String type; + public String url; + public String sha1; + public String time; + public String releaseTime; + public String details; + public String detailsSha1; + + public String id() { + return id; + } + + public String type() { + return type; + } + + public String url() { + return url; + } + + public String time() { + return time; + } + + public String details() { + return details; + } + + public String releaseTime() { + return releaseTime; + } + } + + public static class VersionDetails { + + public String id; + public String normalizedVersion; + + } } diff --git a/src/main/java/net/ornithemc/meta/web/EndpointsV3.java b/src/main/java/net/ornithemc/meta/web/EndpointsV3.java index 0f29528..bc2c95d 100644 --- a/src/main/java/net/ornithemc/meta/web/EndpointsV3.java +++ b/src/main/java/net/ornithemc/meta/web/EndpointsV3.java @@ -23,6 +23,7 @@ import io.javalin.http.Handler; import net.ornithemc.meta.OrnitheMeta; import net.ornithemc.meta.data.VersionDatabase; +import net.ornithemc.meta.utils.VersionManifest; import net.ornithemc.meta.web.LibraryUpgradesV3.LibraryUpgrade; import net.ornithemc.meta.web.models.BaseVersion; import net.ornithemc.meta.web.models.Library; @@ -161,7 +162,7 @@ private static List getLibraries(Context context, int generation) { } String gameVersion = context.pathParam("game_version"); - Semver version = OrnitheMeta.database.manifest.getVersion(gameVersion); + Semver version = OrnitheMeta.database.getManifest(generation).normalize(gameVersion); if (version == null) { return null; @@ -257,9 +258,11 @@ private static List getOslModuleInfo(Context context, int generation) { return null; } + VersionManifest manifest = OrnitheMeta.database.getManifest(generation); + String module = context.pathParam("module"); String gameVersion = context.pathParam("game_version"); - Semver version = OrnitheMeta.database.manifest.getVersion(gameVersion); + Semver version = manifest.normalize(gameVersion); if (version == null) { return null; @@ -298,8 +301,8 @@ private static List getOslModuleInfo(Context context, int generation) { } try { - Semver vmin = OrnitheMeta.database.manifest.getVersion(minGameVersion); - Semver vmax = OrnitheMeta.database.manifest.getVersion(maxGameVersion); + Semver vmin = manifest.normalize(minGameVersion); + Semver vmax = manifest.normalize(maxGameVersion); return version.compareTo(vmin) >= 0 && version.compareTo(vmax) <= 0; } catch (NoSuchElementException e) { diff --git a/src/main/java/net/ornithemc/meta/web/LibraryUpgradesV3.java b/src/main/java/net/ornithemc/meta/web/LibraryUpgradesV3.java index 37d18d0..4d05be4 100644 --- a/src/main/java/net/ornithemc/meta/web/LibraryUpgradesV3.java +++ b/src/main/java/net/ornithemc/meta/web/LibraryUpgradesV3.java @@ -26,10 +26,12 @@ import java.util.List; import com.fasterxml.jackson.core.type.TypeReference; + import com.vdurmont.semver4j.Semver; import net.ornithemc.meta.OrnitheMeta; import net.ornithemc.meta.data.VersionDatabase; +import net.ornithemc.meta.utils.VersionManifest; import net.ornithemc.meta.web.models.Library; public class LibraryUpgradesV3 { @@ -38,14 +40,7 @@ public class LibraryUpgradesV3 { private static List cache; - public static List get() { - reload(); - validate(); - - return cache; - } - - private static void reload() { + public static List reload() { if (Files.exists(FILE_PATH)) { try (InputStream is = Files.newInputStream(FILE_PATH);) { cache = OrnitheMeta.MAPPER.readValue(is, new TypeReference>() { }); @@ -53,60 +48,69 @@ private static void reload() { OrnitheMeta.LOGGER.warn("unable to load library upgrades from file", e); } } + + return cache; } - private static void validate() { - if (cache == null) { - throw new RuntimeException("library upgrades v3 could not be read from file: file does not exist or is badly formatted"); - } + public static class LibraryUpgrade { + + public String name; + public String url = VersionDatabase.MINECRAFT_LIBRARIES_URL; + + public Integer minIntermediaryGeneration; + public Integer maxIntermediaryGeneration; + public String minGameVersion; + public String maxGameVersion; + + private boolean validated; + + private void validate() { + if (this.validated) { + return; + } - for (LibraryUpgrade lib : cache) { - String name = lib.name; - String[] parts = name.split("[:]"); + String[] parts = this.name.split("[:]"); if (parts.length < 3 || parts.length > 4) { throw new RuntimeException("invalid maven notation for library upgrade: " + name); } - Integer minGeneration = lib.minIntermediaryGeneration; - Integer maxGeneration = lib.maxIntermediaryGeneration; - - if (minGeneration != null && maxGeneration != null && minGeneration > maxGeneration) { - throw new RuntimeException("invalid intermediary generation bounds for library upgrade: " + name + " (" + minGeneration + " > " + maxGeneration + ")"); + if (minIntermediaryGeneration != null && maxIntermediaryGeneration != null && minIntermediaryGeneration > maxIntermediaryGeneration) { + throw new RuntimeException("invalid intermediary generation bounds for library upgrade: " + name + " (" + minIntermediaryGeneration + " > " + maxIntermediaryGeneration + ")"); } - String minGameVersion = lib.minGameVersion; - String maxGameVersion = lib.maxGameVersion; + // generation bounds for checking version bounds + int minGen = (minIntermediaryGeneration == null) ? 1 : minIntermediaryGeneration; + int maxGen = (maxIntermediaryGeneration == null) ? VersionDatabase.config.latestIntermediaryGeneration : maxIntermediaryGeneration; - if (minGameVersion != null && maxGameVersion != null) { - Semver min = OrnitheMeta.database.manifest.getVersion(minGameVersion); - Semver max = OrnitheMeta.database.manifest.getVersion(maxGameVersion); - if (min == null) { - throw new RuntimeException("unknown minimum game version for library upgrade: " + name + " (" + minGameVersion + ")"); - } - if (max == null) { - throw new RuntimeException("unknown maximum game version for library upgrade: " + name + " (" + maxGameVersion + ")"); - } + if (minGameVersion != null || maxGameVersion != null) { + for (int generation = minGen; generation <= maxGen; generation++) { + VersionManifest manifest = OrnitheMeta.database.getManifest(generation); - if (min.compareTo(max) > 0) { - throw new RuntimeException("invalid game version bounds for library upgrade: " + name + " (" + minGameVersion + " > " + maxGameVersion + ")"); - } - } - } - } + Semver minVersion = (minGameVersion == null) ? null : manifest.normalize(minGameVersion); + Semver maxVersion = (maxGameVersion == null) ? null : manifest.normalize(maxGameVersion); - public static class LibraryUpgrade { + if (minGameVersion != null && minVersion == null) { + throw new RuntimeException("unknown minimum game version for library upgrade (gen" + generation + ": " + name + " (" + minGameVersion + ")"); + } + if (maxGameVersion != null && maxVersion == null) { + throw new RuntimeException("unknown maximum game version for library upgrade (gen" + generation + ": " + name + " (" + maxGameVersion + ")"); + } + + if (minVersion != null && maxVersion != null && minVersion.compareTo(maxVersion) > 0) { + throw new RuntimeException("invalid game version bounds for library upgrade (gen" + generation + "): " + name + " (" + minGameVersion + " > " + maxGameVersion + ")"); + } + } - public String name; - public String url = VersionDatabase.MINECRAFT_LIBRARIES_URL; + } - public Integer minIntermediaryGeneration; - public Integer maxIntermediaryGeneration; - public String minGameVersion; - public String maxGameVersion; + this.validated = true; + } public boolean test(int generation, String gameVersion) { + validate(); + if (this.minIntermediaryGeneration != null && generation < this.minIntermediaryGeneration) { return false; } @@ -114,19 +118,20 @@ public boolean test(int generation, String gameVersion) { return false; } - Semver v = OrnitheMeta.database.manifest.getVersion(gameVersion); + VersionManifest manifest = OrnitheMeta.database.getManifest(generation); + Semver version = manifest.normalize(gameVersion); if (this.minGameVersion != null) { - Semver vmin = OrnitheMeta.database.manifest.getVersion(this.minGameVersion); + Semver minVersion = manifest.normalize(this.minGameVersion); - if (v.compareTo(vmin) < 0) { + if (version.compareTo(minVersion) < 0) { return false; } } if (this.maxGameVersion != null) { - Semver vmax = OrnitheMeta.database.manifest.getVersion(this.maxGameVersion); + Semver maxVersion = manifest.normalize(this.maxGameVersion); - if (v.compareTo(vmax) > 0) { + if (version.compareTo(maxVersion) > 0) { return false; } }